Group 5 : MIA

Jason Rosenthal and Basil Hayek

Task 4 : Directory Services

 

 

Goals

 

In an Internet Telephony system, Directory Services plays a vital role in the exchange of data between applications using the system. Directory Services allows data to be correctly delivered to a user, regardless of where he may be physically located. This is accomplished by querying a database that is constantly updated as users join and leave the Internet Telephony system.

 

We had several goals in this project. One of the most important goals was to get our Directory Service up and running by the end of the project. Since we realized that we are a core component to the system, we did not want to be the weak link. Another important goal was to write good code that was relatively free of bugs. There were feature specific goals we wanted to meet, which involved including specific features in our Directory Server. Providing security was one of these, because in an Internet Telephony system this is crucial for the system to perform as expected. We also wanted to provide robustness and design an application that was tolerant to failure in a component. Finally, we wanted to provide a way of permitting mobile users to be accessed through our system.

 

Accomplishments

 

We were able to accomplish the majority of our goals. In fact, we worked on every feature of the Directory Service that we had planned on doing. Most of these features were finished to completion, while some ended up more simplified than they would have been had there been more time. Our functionality was achieved through three components – the Directory Server, the Directory Client, and the Initialization Server.

 

Directory Server

 

The Directory Server is the heart and soul of Directory Services. It maintains a Microsoft Access database which tracks users, their locations, and the services available at each of their locations. The first step in a user using our system is for him to register through White Pages. This places an entry in our XReference table, which allows the user to have phone and IP address information stored for him.

 

At the time of registration with White Pages, a new user may choose to add a phone number. However, he is free to do so at any point afterwards, and can also delete a phone number and add services for a phone number as needed. An IP address is added a bit differently. Although originally we had planned on supporting a profile system, where a user could prioritize certain addresses, specify his location by time of day, after discussion with other groups, this didn’t really seem necessary for our system. The reason for this is that in order for a user to connect to a remote user via an IP address, an application that would handle the incoming connection request would need to be running on the remote. But if this application is running, that means that the user must have logged onto the system from that remote application. Thus, we could have the application notify Directory Services of the user coming on-line at that address. We could also have the application notify us when it was closing down, so we could remove that address from our database (in the case of a crash, management notifies us). This allows us to have a directory that is relatively clean, in that it contains addresses of where the user can actually be reached. Furthermore, it simplifies the problem of mobile users, since all users in our system can be considered to be mobile.

 

The Directory Server uses the Signaling team’s Message Center to listen to messages it is interested based on the type of message that has been received. These messages indicate the user, address, and services of interest, and it is the Directory Server that generates and executes the SQL code to provide the database update or query. The Directory Server is also responsible for enforcing referential integrity within the database. It does so according to the following rules:

 

    1. XReference contains the fields (UserName, PhoneNumber)
    2. PhoneInfo contains the fields (PhoneNumber, Services)
    3. IPInfo contains the fields (UserName, IPAddress, Port, Services)
    4. LKUPServices contains the fields (ServiceID, ServiceName)
    5. PhoneInfo.PhoneNumber exists if and only if XReference.PhoneNumber exists
    6. If IPInfo.UserName exists then so does XReference.UserName
(Fields in bold are keys.)

 

 

Directory Client

 

The Directory Client provides other components of the system with an interface to use to access our database. Signaling creates an instance of the client for any application that needs to access our service. This is how we provide interfaces to everyone, as they can call public functions of the client. The client handles the sending and receiving of messages to the Directory Server and Initialization Server necessary to satisfy the function requests.

 

Upon startup, the Directory Client listens for an initialization packet message to be sent to it from the Initialization Server. This initialization packet tells it where the Directory Server is located. Once it has received this packet, it is able to carry out all database requests. The Directory Client is also responsible for detecting and reporting Directory Server failures. When a client detects a failure, this is reported to the Initialization Server, which then handles the failure. Please refer to "Replication & Robustness to Failure" for the details of this process.

 

 

Initialization Server

 

The Initialization Server runs on the Signaling Server, and is primarily responsible for notifying Directory Clients of where to find the PRIMARY Directory Server. It also handles fail-overs in the case of a Directory Server failure. In order to give credit where it’s due, the Initialization Server was the brainchild of our Signaling team. They suggested it since it would allow for movement of the Directory Server, and would rely on less hard-coded or static initialization files (we pass in the location of the Signaling Server as command-line parameters).

 

When the Initialization Server starts, it waits for Directory Servers to start and notify it of their successful execution. Once it has received notification from two servers, it will designate one as PRIMARY and the other as BACKUP. At this point it will begin handling HelloMessages from clients. Note that it will not respond to HelloMessages unless at least two servers are running. This was done to try to maximize robustness from the start of execution. The fail-over handling of the Initialization Server is discussed below, under "Replication & Robustness to Failure".

 

To clarify the interaction of these components, please refer to Figure 1. Figure 1 illustrates the beginning of a typical Directory Services execution. It begins with the servers coming on-line, and ends after the first directory access (all directory accesses after the first are the same).

 

In addition to these components, the following features were also implemented.

 

Replication & Robustness to Failure

 

As was discussed, the Initialization Server waits for both a PRIMARY and a BACKUP server to be established before it allows clients to connect to the database. This is done to facilitate replication and robustness to failure. When the Initialization Server designates one server as a PRIMARY, all directory requests are directed to this server. However, in order to facilitate replication, the PRIMARY server passes on those messages that alter the database to the BACKUP server. The BACKUP server, upon receiving the message, executes the instructions just as if they had been received from a client. In this way, we know that any action that affects one database also affects the other.

 

Directory Services is highly available, since we can provide continuous service in the event of a single failure, although we cannot recover from a double failure. There are two types of single failures that could occur. The first is a PRIMARY server failure, which will be detected by the Directory Client (as a failure to connect). The second is a BACKUP server failure, which will be detected by the PRIMARY server address. In the event of a PRIMARY server failure, the Directory Client informs the Initialization Server of the failure, and requests a new PRIMARY server address. The Initialization Server notifies the BACKUP that it will now be acting as PRIMARY, and upon acknowledgment of this message, gives the Directory Client the address of the new PRIMARY. Please refer to Figure 2 for a visual clarification of this process. When another Directory Client reports a failure, the Initialization Server checks to see which server address is reported as failed, and will only perform a fail-over if the reported address is the same as the current PRIMARY address. This prevents several Directory Client messages reporting the same failure from being regarded as reports of several different failures. Management is notified of a PRIMARY server failure, so steps can be taken to correct this.

 

Note that new PRIMARY does not pass on replication messages. This is primarily because there is no new BACKUP to pass them on to. We contemplated the idea of providing several levels of fail-over, but this would have complicated replication issues, and is an idea that does not seem to be practiced in industry, at least from our experience. However, we did design our architecture so that multiple backup servers is a possibility, and implementation would require addition of new functionality but no changes to our design. In our system, to return to a dual server system, we would need to manually re-synch data. This is again due to time constraints. To make this process more automated, we were planning on creating a replication log on the new PRIMARY that would track all changes that would need to be made to the failed server. Once the server was restarted, a re-synch message would be sent to the PRIMARY server by the Initialization Server, who would pass the replication log to the restarted server. The restarted server would process the log, and once re-synched would become the new BACKUP server.

 

As was mentioned, a BACKUP server can fail. This is detected by the PRIMARY server when a replication message is not acknowledged by the BACKUP. When this occurs, the Initialization Server is notified that there is now no BACKUP to fail-over to. Management is also notified, so that appropriate steps can be take to remedy the situation.

 

 

Authentication & Security

 

All messages passed between different components of the Directory Services system are authenticated. This is done by the use of hashing (MD5) and a password shared amongst all components. An intra-Directory Service message is wrapped in a special message called a HashMessage. The HashMessage contains the original message, the destination, and the hash of the password concatenated with the serialization (byte representation) of the original message. This message is then sent out to the destination component. Upon receipt of the HashMessage, the component first checks to see if the HashMessage is valid. It does this by computing its own hash value, using its local copy of the password and the message contained in the HashMessage. It compares this hash value to the one contained in the message. If they match, then the message is valid (the probability of generating the same hash value without the same password or message is approximately 1 in 3.4 x 1038). Management is notified of all messages that have bad hashes, to provide a means of audit, and possibly apprehension of a threat.

 

 

This scheme is immune to a middle-man attack, since intercepting the HashMessage and altering the message it contains would drastically change the hash value, invalidating the message. This scheme is immune to a denial-of-service attack, since without the correct password (which is never sent out in the clear or any other usable form) a threat could never create the correct hash value. Finally, the scheme is also immune to spoofing, since when relying on a valid hash rather than a source address for authentication.

 

Note that this scheme provides authentication, but does not provide confidentiality of messages. This is due to a combination of factors. First we weren’t sure that the information being passed was intended to be confidential. Second, hashing is a fast process, whereas encryption and decryption is not, and would add significantly to the overhead of the system. Finally, time was a factor, since if we had more time for the project we would probably have added this feature just so it was available. We have implemented security and robustness. And finally, we have also provided for mobile users to use our system (although truthfully, this was almost a side-effect of our design).

This scheme does have a weakness in that we trust any application that has access to our Directory Client. But assuming that our server is secure, threats should not have access to our Java classes, and the greatest threat of security is through transmitted messages. Finally, we originally wanted to implement a policy of least privilege (which would require applications using our client to authenticate themselves) so that applications would be able to do only what they needed, and no more. But again, time was a limiting factor in the implementation of this.

 

Although we did not achieve all the goals that we first developed during the early stages of the project, we did accomplish all the goals that we developed later as we became more focused and knowledgeable about not only Directory Services, but the entire project and our role within it. Our Directory Service works, and no bugs have been encountered or reported to us that we could not track down and fix.

 

We did end up doing something we had never planned on doing, which was implementing our own method of replication. When we switched to Microsoft Access from DNS, we had planned on using the replication provided by Microsoft Access. However, this was intolerably slow and quite restrictive, so we instead implemented our own simple but effective replication scheme.

 

 

Problems

 

Admittedly, we ran into quite a few problems while developing our system. One of our recurring problems was understanding exactly what was expected of us from other groups, and knowing exactly how we fit in to the project. Even after many discussions with our team members and the reading of documentation, we still did not have the ability to piece things together because, quite simply, we lacked the experience. We became more and more knowledgeable about how things would and should work only after coding and allowing ourselves to write code to fit our specifications.

 

Another coordination problem was related to inter-team communication. Some teams were difficult to get a hold of, and brought several last minute issues up that would have been easier dealt with sooner. Still, this is something that we expected, especially in a project of this size. We are also happy to report that the groups we dealt with most directly were all excellent at communication and all very knowledgeable about their part of the project.

 

A technical problem we had was trying to run JDBC after we decided to switch our server to Java. Quite a bit of time was spent writing code, testing, attempting to debug, and reading on-line documentation from the Java site before we figured out the cause of the problem. The problem was that although the classes we needed were available, we could not run them because a DLL we needed was not installed in the appropriate directory. In addition, this would not have been a problem if we had been using the javac compiler and the java interpreter instead of the J++ compiler and the jview interpreter. Then, after we figured out the problem, we still had to wait to get access to the cs519 computers. So this was a bit of a time setback.

 

 

 

What We Learnt

 

Working on the project exposed us to many new areas in regards to network computing, database management, and general programming practices.

 

Networking / Database

 

Working with the MessageCenter taught us a new way of communicating between computers and applications in a network. We found using it was fairly easy and straightforward, and once mastered made our program easier to write. For the first time we had to write network communication code without the direct use of sockets. It was also how we learned how we would integrate ourselves with the other aspects of the MIA network.

 

Working on the database for the server exposed us to Java SQL for the first time. This forced us to learn a lot of the Java SQL functionality.

 

Microsoft Access replication proved to be too slow for our needs, and as a result we had to write our own replication code. This was an invaluable learning experience because it forced us to deal with many of the issues encountered during replication. Although we implemented only one back up server, we found the replication code to be much more complicated than we initially imagined. We had to focus on such issues of how to detect a crash, how to notify clients of a new server, and how to best mirror the database work on the primary server on to the backup server. We had to figure out how to write the server code so the same code could work for both the primary and backup servers. As we wrote the replication code, we discovered more and more hidden issues about replication we did not initially foresee. Although our final code did not handle everything we thought about, we understood better all the difficulties in creating a complete replication service.

 

For one of us this was the first time actually implementing security in network communication. This was very cool in seeing an approach to security that was actually fully integrated and successfully working. Although uses of security had been discussed in classes such as 414, it was the first time the one member wrote actual code dealing with security. As with replication, it made us think about the many questions a programmer must deal with when trying to incorporate security. We had to deal with issues such as effective uses of passwords, serialization and in general how to handle how security can complement your code. Again similar to replication, we were not able to code all the aspects of security we would have liked, but it did give us a chance to think about more security we could have implemented had we more time (for example, not just tamper-proof messages but confidential ones as well). Writing the code also required us to learn and use security function provided by Java.

 

 

Java

 

Programming exclusively in Java (for one of us the first time), helped teach us advanced functionality of the language.

 

This included Java’s use of polymorphism. We successfully used abstract classes to allow multiple implementations of our Directory Client. Through a client factory, we found an easy way to allow other users to create an instance of our client. We also used extension of classes to provide a hierarchical group of Directory Exceptions so users only had to catch one particular type of exception. The messages we created for our use also had to function as extensions of the general message class, including implementing its abstract classes.

 

We also gained experience with Java’s exception handling. We were able to use exceptions very effectively to help logically structure our code. We were also forced to learn how to deal with other users’ exceptions.

 

This project was also our first attempt at writing client and server code in Java. Through the demands of the project, we know have important experience writing fairly advanced client and server code in the Java environment. We learned hoe to effectively communicate between clients and servers so as to run efficiently and run replication smoothly. This coordination was one of the more difficult coding to get completely working.

 

 

Teamwork

 

Most importantly perhaps was the experience gained working with other programmers, both in using their code and writing ours so it could successfully integrate. It taught us how to write code so it could easily be understand and implemented by the other teams in our group.

 

The project taught us coordination skills in creating general team strategies and writing protocols we could all agree on. It also taught us how to work with other people who have extremely busy schedules like we do. While this caused many of the problems we had during the project, by the end we learned how to better manage our time with them.

 

To successfully finish a project of this magnitude that involved a team of more than 20 people is an incredible accomplishment and a personal achievement for all of us. It has been an invaluable experience especially in preparing for entering the "real world," where we will be expected to work on a good deal of team projects. It has already helped us during the interview process, as companies have been very interested in this project and how we worked in a team.

 

 

What We Would Do Differently

 

Many things we would do differently with the chief goal of minimizing the amount of unnecessary code written. So much of the code we wrote was deleted, changed, rewritten, etc. because our outlook on the problem was constantly changing and being refined. Since we were often working under assumptions, we found in this situation of changing our code.

 

First thing that would have helped is we involve better coordination with the other teams. It would have been much better if we spent more time early on working out the exact details of how data flowed through our system. Unnecessary work resulted, for example White Pages implemented some functions because we didn’t think we would be able to handle them, only to find out later that we easily could. We should have spent more time finalizing our interfaces that we provide to the other teams, as we would changes even up to the days before the demo. This complicated things because other teams had to update our code repeatedly to ensure that everything still worked. Part of the problem was that some groups did not approach us with problems they had with the interface until close to the demo date. In the future we will not assume "no news is good news" and make sure we sit down with all the groups using our interface and making sure it is exactly what they needed.

 

If we had finalized our strategy before we started writing code we could have saved time. Unfortunately it took us a while to figure out how we would set up our database, as in if we should use DNS or if we should create it ourselves, and as a result we wasted time writing C++ code and socket code that was never actually used. Had we decided early that we would be writing only in Java and using Java SQL, less unnecessary would have been written.

 

These design problems would have been avoided had we sat down and written a design specification before we coded. Although we have never tried doing this, we think in the future this would be a very helpful tool. The design specification would have consisted of pseudo code, which would allow us to accomplish something that would not need to be rewritten and would allow flexibility no matter what language we used.

 

It also would have helped if we had worked out a schedule for integration with the teams we needed to integrate with. This way we would have had deadlines to make sure certain functions worked together, and would helped ensure a full integration by the time of the demo.

 

 

Interfaces

 

The following interfaces are provided through the Directory Client. An application can use the interfaces by calling these functions through an instance of DirClient. DirClient is an abstract class with abstract functions that are the interfaces. Instances are created by the Signaling Server using a client factory and given to all the message centers.

 

//MOST OF THE INTERFACES MUST CATCH DIRECTORY EXCEPTION

//There are three types of exceptions that extend DirectoryException

// TimedOutException - Timed out before response from directory server received

// NackException - Got an error message from the directory server

// AllServersDownException - Failed to connect to either the primary or secondary server

 

//lookup(user,services)

// user - user name or phone address to look up

// services - services to look up

// returns MiaAddress array of successful lookups

public abstract MiaAddress[] lookup(String user, Vector services) throws DirectoryException;

 

//newAddress(phoneNumber, services)

//<USED TO ADD PHONE NUMBER AND USER NAME>

// phoneNumber - phoneNumber for new entry

// services - services phoneNumber supports

public abstract void newAddress(String user, String phoneNumber, Vector services) throws DirectoryException;

 

//newAddress(userName)

//<USED TO ADD USER NAME (IN THE FORM OF AN E-MAIL ADDRESS)>

// userName - username for new entry

public abstract void newAddress(String userName) throws DirectoryException;

 

//addPhoneNumber(userName, phoneNumber, services)

//<USED TO ADD PHONE NUMBER TO ALREADY ENTERED USER NAME>

// userName - username of entry to add phoneNumber

// phoneNumber - phoneNumber to add

// services - services phoneNumber supports

public abstract void addPhoneNumber(String userName, String phoneNumber, Vector services) throws DirectoryException;

 

//updateService(miaAddress,services)

// user - user name or phone number to add services to

// services - services to add

public abstract void updateServices(String user, Vector sevices) throws DirectoryException;

 

//registerIP(user,ipAddress,port,services)

// userName - user name of entry to add IP Address

// ipAddress - IP address to add

// port - port for new IP Address

// services - services new IP Address supports

public abstract void registerIP(String userName, String ipAddress, int port, Vector services) throws DirectoryException;

 

//killIP(addr)

// addr - the address to remove from the IPInfo Table

public abstract void killIP(IPAddress addr) throws DirectoryException;

 

//removeUser(userName)

// userName - the user to remove from the XReference table (referential integrity will be

// maintained as necessary).

public abstract void removeUser(String userName) throws DirectoryException;

 

//removeUser(userName, phoneNumber)

// userName - the user to remove from the XReference table (referential integrity will be

// maintained s necessary).

// phoneNumber

public abstract void removeUser(String userName, String phoneNumber) throws DirectoryException;

 

//close()

// kills the listening thread of the client in the message center

public abstract void close();

 

Furthermore two types of messages were created to communicate with management:

 

public class SecurityMessage implements Message{

public String warning; //earning message

public int severity; //value of severity

public Message message; //the actual illegal address

public MiaAddress badSender; //address where the illegal message came from

}

 

public class StatusMessage implements Message{

public String warning; //earning message

public int severity; //value of severity

public Message message; //the actual illegal address

}

 

There are two extensions of SecurityMessage: BadHashMessage, which is sent out if a bad hash code is found, and IllegalRequestMessage, sent out if an application tried to do something it is not allowed to do.

 

The second type is the StatusMessage, which instances of ServerDownMessage and BackupDownMessage. These notify Management the status of a particular server.

 

 

Advice

 

One recommendation we definitely make is the availability of more computers, or if this is not possible, the installation of necessary hardware and software on the machines that are available. We needed a dedicated server to run our database from, and this was complicated by the fact that we needed jdcbodbc.dll installed in the WINNT\SYSTEM32 directory in order for us to access our database. Since administrative privileges are required to write to this directory, the only machines we could do this on were the cs519 machines. This took us quite a while, since the machines did not come in until Complications arose here because quite a few of our groups needed access to these machines (we received access to the Keshav’s Kids machine) because they needed to use the sound cards. This became especially hard to test and integrate our code, since the machines were constantly occupied.

 

Other than that, more specific comments (pointing out early on weaknesses, or issues we may not have addressed that tend to be difficult for students) on our reports would have been helpful.

 

 

References

 

N. Anerousis, R. Gopalakrishnan, C.R. Kalmanek, A.E. Kaplan, W.T. Marshall, P.P. Mishra, P.Z. Onufryk, K.K. Ramakrishnan, C.J. Sreenan, "The TOPS Architecture for Signaling, Directory Services and Transport for Packet Telephony", Proceedings of IEEE International Workshop on Network and Operating System Support for Digital Audio and Video (NOSSDAV), pp. 41-53, July 1998.

 

Konstantinou, Alexander V., "Internet Telephony Directory Services", December 6th, 1996.

 

Richmond, Craig, "Setting up a basic DNS server for a domain", August 8th, 1996.

 

H. Schulzrinne, J. Rosenberg, "Internet Telephony: Architecture and Protocols an IETF Perspective", July 2nd, 1998.

 

JDBC overview:

http://www.javasoft.com/products/jdk/1.1/docs/guide/jdbc/index.html

 

JAVA SQL:

http://www.javasoft.com/products/jdk/1.1/docs/api/Package-java.sql.html

 

Security overview:

http://java.sun.com/products/jdk/1.2/docs/guide/security/index.html

 

Code using JDBC/ODBC provided by Dan Spoonhower

 

Microsoft J++ Online Documentation