Group 1, G1

Team members: Haye Chan, haye@cs.cornell.edu

Team 4 – Directory Service

What did I set out to do?

My part of work is Directory Service. The project is about building a telephony network on Internet to enable computers to talk to the phones. Among all other key components, directory service sits at the central piece of the whole project. It is the centralized database that stores information about users and provides information retrieval as well as various lookups.

Users of directory service fall into a wide range. Those include the signaling component that sits on the Gateway, the signaling component that sits on each workstation, the fax gateway, the voice/email gateway, the multi-party conferencing server and client. See the following diagram for its user distribution.

Since this is such a central piece of the network system, it has to have the following features:

Features

Things to note

Since I am providing interface for people to use, there are several things that I need to pay attention to.

The ideal case is we formulate a set of interface at the beginning and don’t ever change it. This is important considering the understanding of the system we have at the beginning. I know the interface has to be changed some time later. My goal would be to minimize the troubles caused to other teams. This could only be achieved by doing work earlier than other people do. We have to be able to somewhat finalize the interface before anybody starting to figure out how to use directory service. We have to finish at least the core features before anybody starts testing their code. Furthermore, upgrading of directory service versions has to be treated with great caution in order not to cause tremendous inconvenience and confusion to other teams.

In addition, we would be the best team to implement the data structures (classes) that are used across the system. We would need to implement abstract data types such as Name, Address, Digits, NameList and FullName.

Due to the nature of directory service, we have to deal with many teams. We have to talk to them as early as possible and collect the features that they would need.

Schedule

Date

Checkpoint

10/19

Get BIND to work

10/26

Revise the interface

11/2

Provide dummy testing code for interface to let other groups begin testing

11/16

Get our server to work

11/30

Finish the basic features and add additional features

12/7

Declare bug-free

I am very proud to report that, since I have carefully drafted the initial schedule, I have always been able to meet the deadlines, while the above is exactly the schedule that I submitted in Report 1. Although there are numerous difficulties arising in the middle of development, I have been able to cope with them and managed to keep in line with the schedule.

 

What did I actually accomplish?

I provided a version of local directory service for people to test their code individually without having the trouble of sharing a centralized database. After I finished implementing the centralized version of database, I carefully upgraded it, while retaining the old version as a package with a slightly different name.

I chose to integrate BIND into our project as our directory service. I made this decision based on the following reasons:

  1. The BIND architecture is very flexible and it is relatively easily to store arbitrary records with it.
  2. The replication feature is considered necessary and implementing it from scratch is almost impossible.
  3. The BIND is known to be fast. This is a very desirable feature of our directory service.
  4. Other features such as scalability (the ability to spawn off sub-domains when network grows) and caching also comes for free.

The decision turns out to be fairly correct, though not without pain. A fair amount of time was spent on hacking into bind query and update code since the query and especially update message format is not well specified.

Integrating BIND

I looked at all the resource record formats and picked the TXT record. This is a very flexible record format that allows me to store arbitrary string. I thus store the user record in this format:

<name> IN TXT "Ext=00001;Address=cs519g1.csuglab.cornell.edu:50444;....."

This makes the resource record well extensible and could store any fields.

After several hours of fiddling with BIND, I finally got it up and running.

Writing the Stub code

I wrote the core part of the stub in C. The C-stub contains functions for doing query and update. I then use Java Native Interface to call the C functions from Java. Since a large portion of the core C code is ported from BIND directly, the performance is very satisfactory, even after the Java and record parsing overhead. The caching effect is obvious, as the second lookup is significant faster than the first one. I’m not running hierarchical server, and I don’t quite understand where the caching is working at. My guess is that BIND on the server side caches the query results so that next time it doesn’t traverse through the database and hence it becomes much faster in the second time.

Running Slave Server

It doesn’t take long at all the get the slave server up and running. So when the master server goes down or is too busy to serve request, people can still access directory service through the slave server. The slave server works exactly in the same way as normal DNS slave server. It automatically deals with the coherency issues and provides an illusion of a centralized database. I’m proud to say that this makes our directory service a real robust and reliable one.

Accommodating various teams feature requests

During the development process, I collected features request from all teams. For example, voice email team wants to store user id on the cs519g1 machine, fax team wants to store the full name (including first name and last name) of a user as well as his email address. Signaling team decides that having a field called Dialup would significantly simplify their dial-out process and I accommodate their needs in this. The most demanding requests come from the conferencing team. They want to have a separate set of records storing conference record, which each one contains list of current users. Besides various kinds of lookup features, they also want to have various ways to dynamically update the records.

Security Features

Then I started to implement some basic security features that were included in the initial API. I keep another set of separate resource record on directory server which contains the user’s password hash and his privilege level. Everyone who wants to do query through DirectoryStub class has to call the instance method DeclareIdentity(name, password) before he can call any other functions in this class. This declare identity will query the directory service for the password hash corresponding to this user name and sets his privilege level according to the record. Then certain function calls will be enabled depending on what privilege that user has. Only password hash is stored in the database and only hash will be passed out. The password given by user is internally hashed inside DeclareIdentity() and compared against the one from directory service. In this case, as long as inverse hashing is hard enough, there will be a resonable security protection. Here I just use the Java static method String.hashCode(). More sophisticated hashing algorithm can be replaced easily in the future.

Four access levels are available at this moment.

  1. Normal Query Level (which can do various kind of querying. This is suitable for billing and signaling at Gateway)
  2. Normal Update Level (which can do update of records. This is suitable for desktop signaling component)
  3. Conference Query Level (which can query conference records. This is specifically designed for conference server)
  4. Conference Update Level (which can update conference records. This is for both conference server and conference desktop component)

Any user can have a privilege as a combination of the four levels above. Each function call will throw an Exception if the user does not have the appropriate access level after authentication.

Roaming Users

Whenever a user logon, the signaling component update the directory service to reflect his new machine address by calling ChangeLocation() in DirectoryStub. When a user logon, signaling component needs to call ChangeLocation(DirectoryStub.absent) to update his offline status.

Dealing with DHCP

Dealing with DHCP protocol is potentially hard, especially because we can’t find an existing DHCP network around to test it. However, I think the solution could be very simple. I decided not to store machine name instead of the IP addresses in directory service for each user. For example, only cs519g1.csuglab.cornell.edu is stored in the directory service but not the actual IP address 132.236.227.85. In that case change of IP address would not have effect on the system since machine name is relatively static. So directory service always only give out machine name, while people will do the lookup when needed. This will fit well into the DHCP protocol.

Scalability

User names are hierarchical. For example, there are currently 21 registered users in our network. They are the 21 people in our group, with names in the format <name>.g1. For instance, my user name is hayechan.g1. In the future, when the number of users grow, we could put them into sub-domains in a similar way as DNS. There’ll be people named christ.cs.cornell.us.tel, ... Name servers can be hierarchically structured in the same way as DNS. All the successful features in DNS will be inherited in our project.

 

Porting to NT platform



An unanticipated problem was that at first I thought everybody is going to user directory service on Unix. But later I found out that there are teams who will call it on NT. This should not be a problem if the whole DirectoryStub is written entirely in Java. However, the core part is actually written in C. And since I ported a large amount of code from BIND, it is impossible to rewrite it in Java. Also due to time constraints, I was unable to port the C code to NT platform. (I actually tried for a while but soon found out that it is a very painful process) Therefore I decided to go around it by running a Java RMI server called DirectoryServerForNT. This server runs on UNIX and listens to requests from NT machines, then carry out the request by using DirectoryStub internally.

The users would then use a class called DirectoryStubForNT which has exactly the same interface as DirectoryStub. The difference is, internally, DirectoryStubForNT talks to DirectoryServerForNT.

The problem with that is this intermediate server adds overhead to the directory service for the NT users. The difference in performance between using DirectoryStub directly on UNIX and using DirectoryStubForNT is pretty obvious. If we could have anticipated this accident, I would try to port the C code to NT so that the DirectoryStub works on both NT and UNIX platform.

To my surprise, this is a total of over 4000 lines of Java and C code, not including the BIND libraries that I use.

 

 

Problems

I think most problems I ran into are minor technical problems. The single most devastating problem was the problem in getting my partner to work. I have trouble in getting my partner in replying my email. Finally I end up doing almost the whole project myself. This has put extraordinary stress on my already heavy academic schedule. I had a very hard time in getting the project finished with high quality while preparing for six final exams at the same time.

Apart from that, the major technical problems are:

  1. I don’t have enough time to port the DirectoryStub to NT. So as I mentioned before, I run a server on UNIX which listens to all NT requests. The server carry out the requests by using DirectoryStub and returns the results back to the NT clients. This is a slower than directly using DirectoryStub on UNIX. However I was left without alternatives due to time contraints.
  2. The platform of development is UNIX, which is one that I’m not used to. After working on it for one semester, I still think that software development in Visual Studio is more productive. Also, the platform issues have caused me a fair amount of trouble. I believe this could have been avoided if we have chosen NT as our platform.
  3. It was really hard to get a machine outside firewall to run the slave server. I spent a lot of time installing Linux on a machine in Syslab. But the efforts turned out to be futile because of complicated hardware issues. Eventually I asked the management team to do me a favor and let me run it on his dialup machine under his root account.
  4. I think one major problem we have is our team lacks a sense of unity and urgency. This is probably due to insufficient meetings. A lot of teams did not figure out what is going on until very late, and those include some of the core teams. It would probably be better if we could have more meetings in the initial stage. This creates some problems to me because a lot of people give me the features request very late. Fortunately I have designed the Directory Service to be extensible so that features are easily added without too much modification.

 

 

What I learnt

  1. I know quite a lot about DNS and BIND right now. I would be able to set up a DNS server in a fairly short time after this.
  2. I gained extensive experience in UNIX platform, as well as other tools such as emacs and cvs.
  3. I have learned about how to integrate things into our project. The integration work is quite fun actually. I tried to figure out the features that fit into our project and figure out the way to integrate it with least effort.
  4. The experience of providing interface is pretty interesting to me. There are a lot of issues to consider, such as how to maintain a stable interface and avoid future changes, how to upgrade interfaces or implementations with minimal changes. My ex-partner’s idea of providing an initial directory service that maintains only a local database is actually a very neat one.
  5. To integrate the core C code into the Java code, I had to learn Java Native Interface.
  6. The futile effort of porting code from UNIX to NT explained to me why a unified platform in the future is desirable. It takes too much effort to port code from a platform to another
  7. In order to implement the RMI server DirectoryServerForNT, I also had to learn about Java RMI package.
  8. Since my role is to provide interface and features, I have to deal with a lot of groups. By doing that, I had obtained a better understanding of the whole telephony network system, and how each component interacts with each other. Also the interacting experience is both pleasant and educational.
  9. The key to interacting with busy people is to talk to them early, as early as possible.

 

What would I do differently next time?

First, one major mistake I made was not to make sure everybody is on UNIX at the beginning. Next time I will pay more attention to the platform issues before doing a lot of platform dependent things. This is the only part that I’m not happy with my directory service. Although the UNIX users can still get a very outstanding performance, the NT users do not have the same feeling.

Second, I would have requested a temporary machine from course staff in the very early stage so that I could run the slave directory server on it.

Third, of course we should have more often meetings.

 

Interface

DirectoryStub/DirectoryStubForNT - this is the stub placed on the client who needs directory services

Name - this is the class for user name

SocketAddr - this is the class for storing Machine Addresses with port number. e.g. babbage.csuglab.cornell.edu:30

Digits - this class is designed for storing digit-like data, e.g. Extension, PIN

FullName - this class is designed for fax and voice/e-mail gateway team to store a normal pronounciable full name. It provides abstraction for storing first name and last name

NameList - this class is designed for multi-party conference team to store a list of participants in a conference.

DirectoryStub

public class DirectoryStub

{

// constructor

public DirectoryStub();

// authenticate this stub with the id and corresponding password

public int DeclareIdentity(Name name, String password)

throws AuthenticationException;

// change the password to newPassword

public void ChangePassword(String newPassword)

throws AccessDeniedException, RecordNotFoundException;

// delete the old SocketAddr from database, add a new SocketAddr in

public void ChangeLocation(SocketAddr newAddr)

throws AccessDeniedException, RecordNotFoundException, InvalidSocketAddrException;

// change the dialout phonenumber for a user

public void ChangeDialout(Digits phoneNum)

throws AccessDeniedException, RecordNotFoundException, InvalidSocketAddrException;

// QUERY methods

// obtain the location of a user

public SocketAddr GetLocation(Name name)

throws AccessDeniedException, RecordNotFoundException, InvalidSocketAddrException;

public Digits GetExtension(Name name)

throws AccessDeniedException, RecordNotFoundException, InvalidSocketAddrException;

public String GetEmail(Name name)

throws AccessDeniedException, RecordNotFoundException, InvalidSocketAddrException;

public Digits GetPIN(Name name)

throws AccessDeniedException, RecordNotFoundException, InvalidSocketAddrException;

public String GetUserID(Name name)

throws AccessDeniedException, RecordNotFoundException, InvalidSocketAddrException;

public FullName GetFullName(Name name)

throws AccessDeniedException, RecordNotFoundException,

InvalidSocketAddrException;

// obtain the location of a user

public Digits GetDialout(Name name)

throws AccessDeniedException, RecordNotFoundException,

InvalidSocketAddrException;

// map address to name

public Name GetNameByAddr(SocketAddr addr)

throws AccessDeniedException, RecordNotFoundException;

// map extension to name

public Name GetNameByExt(Digits ext)

throws AccessDeniedException, RecordNotFoundException;

// following are functions for multi-party conference call

// add a conference

// return true if sucessful

// return false if conference exist already

public boolean AddConference(Digits confNum)

throws AccessDeniedException;

// add a user to a conference

public void AddUserToConference(Digits confNum, Name newUser)

throws AccessDeniedException, RecordNotFoundException;

// delete a user from a conference record

public void DeleteUserFromConference(Digits confNum, Name diedUser)

throws AccessDeniedException, RecordNotFoundException;

// delete a conference record

public void DeleteConference(Digits confNum)

throws AccessDeniedException;

public NameList GetUsersInConference(Digits confNum)

throws AccessDeniedException, RecordNotFoundException;

}

Name

public class Name {

public Name (String s):

// not implemented yet

public Enumeration parts ();

public String toString ();

}

SocketAddr

public class SocketAddr extends Address {

// hostname : e.g. babbage.csuglab.cornell.edu

public String hostname;

// port number

public int port;

// the string meaning absent when stored as SocketAddr

public static final String absent;

// constructor taking in string of the format "hostname:port"

public SocketAddr(String addr);

// return true if the this Hostname is one for a user who is not present

public boolean IsAbsent();

public String toString();

}

Digits

public class Digits {

// constructor for string

public Digits (String num);

// constructor for integer array

public Digits (int[] n);

// return an array representing the digits one by one

public int [] GetArray ();

public String toString();

public int equals(Digits digits);

}

FullName

public class FullName {

// has to be in format " "

// e.g. "Haye Chan"

public FullName (String s);

// return the First Name

public String FirstName();

// return the Last Name

public String LastName();

public String toString ();

}

NameList

public class NameList

{

// constructor which takes in string in the format

// "user1,user2,user3,..."

public NameList(String strNameList);

// return whether the name list is empty or not

// this function is independent of the token evaluation

public boolean isEmpty();

// any more names in the name list to get?

// this should always be called and checked before GetNextName

public boolean hasMoreNames();

// return the next Name in the NameList

public Name GetNextName();

// reset the namelist so that next call to getNextName()

// will return the first Name

public void Restart();

// add a Name to the current NameList

// the NameList will be Restart()'ed

public void AddName(Name newName);

// delete a Name from the NameList after the current pointer

// this function assume namelist is not empty

// the NameList will be Restart()'ed

public void DeleteName(Name delName);

public String toString();

}

 

Advice for course staff

I think this class is too demanding as a 4-credit class. I suggest that in later years, it should be split into two portion. The 3 credits one will include lectures, exams, and homework. While the other one will be mainly project. Or some other better combinations whichever is more appropriate.

I think one major problem students had at first was visualizing how the various components in the system comes together and function. We of course eventually figured it out, but it would have been a lot more effective if there could be more covering of the project in the lecture or sections (which we don’t have right now). I think having TAs to hold some sections for students to talk about the project might be helpful. If we could have figured out things earlier, the time would be much more sufficient.

Hardware is basically readily available quite at the right time. Course staffs are all very helpful. The project idea is excellent. A little more improvements should make it perfect.

 

References

In the implementation of our stub, I made use of the following Java Spec web pages:

http://java.sun.com/docs/

Also I extensively used the book DNS and BIND, by Paul Albitz & Cricket Liu.