Login commands
#login regd <username> <password>
to login as a registered user
#login anon <username>
to login as a anonymous user
return:
@status <status no>
Refer to error_def.h for meanings of status no. 0 is NO_ERROR.
registered users, currently validation is done from file regdusers. Each line in
this file contains username and password for each user. Change cserv_command.cpp
to change regd users code. On login you are part of room 'Home' by default. The
mention of the password in this case is that actual text based password. Later
it shall be changed to mean a encrypted password string.
None of the following commands will work unless the user has logged in successfully.
#rmlogin
returns:
@status <no>
To delete your current login. Only for registered users.
#changepass <new password> <old password>
returns:
@status <no>
Change your current login’s password. Only for registered users.
#createlogin <name> <password> <real name>
<email id>
returns:
@status <no>
Create a registered login.
#whoami
returns:
@ret #whoami <username> <current room name> <users in current room>
<current group name> <group id>
Retrieve information about your current position, this entire message is
returned as one line to the user. Every user joins the ‘Home’ room by default.
If no group has been joined (message group), this is returned as name = “-“ and
id = 0.
Room related commands
#listrooms
returns:
@ret #listrooms <no of rooms>
<name of room> <owner of room> <no of users> <secs for room to expire> <pub/priv>
<anon/regd>
... (above data is repeated for each room)
retrieves information about each room. If there are 3 rooms, there will be
three lines of text following the @ret line. If the room is occupied, secs for
expiry set as 0. Room 'Home' never expires. Type is priv or pub, for private or
public rooms.
#listusers
return:
@ret #listusers <no of users>
<user name> <type>
... (above data is repeated for each user)
The above command retrieves information about every user in the room. type
maybe anon or regd or none (none for users yet to login).
#createroom <new room name> [room password]
return:
@status <status no>
creates a new room, you will be the owner of the room. The room password
is to be given if you want to make a private room.
#rmroom <room name> [room password]
return:
@status <status no>
Deletes a room. Will succeed only if you are the owner of the room.
#changeroom <room name> [room password] [anon/regd]
return:
@status <status no>
will change you to an existing room. if it is a private room a password
is to be given. Default is anon
#broadcast "<text>"
return to all users in users current room
@mess #broadcast <uname> "<text>"
the broadcast message is causes the text to be send to all the users in
the users room. they all receive the same message. The message contains the name
of the broadcasting user
#sendto <uname> "<text>"
return:
@status <status no>
return to user uname:
@mess #sendto <sending uname> "<text>"
use this to send private messages to any user in your room. You get a
return status and the other user (if exists) gets the message.
#peer2peer <uname> <my listening port> <my ip
string>
return:
@status <no>
return to user uname
@mess #peer2peer <sending uname> " <port name> <ip address>"
use this to enable peer to peer connectivity. If you have a listening
port, you may transmit this and your ip to any user. The server sends you a no
depending on if the message was send to the user. How he responds is entirely up
to him. If he wishes, he can then connect to you at the port and ip. The server
assumes no responsibility once the data has been transmitted - this is entirely
up to the clients.
Status commands
The server maintains 3 status numbers that a client can query. These
maybe queried according the clients wishes and is can be used to check if any
changes on the server need be reflected for the client.
#archstatus
return:
@ret #archstatus <no>
the number changes every time a room is added or deleted. if the client
maintains a display of the rooms names, he may execute a #listrooms, if the no
returned is different from the one he got when last queried #archstatus.
#roomstatus
return:
@ret #roomstatus <no>
no changes every time any user goes in or out of any room in the server.
#userstatus
return:
@ret #userstatus <no>
the number changes every time a user enters or leaves the room that the
user is a part of. Thus the client can query the room for the new list using #listusers
.
Message Posting Related Commands
#listgroups
return:
@ret #listgroups <no of groups>
<name of group> <owner name> <group no> <no of messages> <no of users>
<terminate time> <pub/priv> <anon/regd>
...
use this to see the available message groups. A message group is place where
you can post and read messages. Users can create group and it is recommended
that groups are made for specific purposes. Groups expire after a duration which
can be user specified. When a group expires all messages in it are deleted.
Group expiry will be suspended till all users leave the group.
#creategroup <group name> [password] [anon/regd]
[terminate time]
return
@status <no>
will create a new group. Anon if specified will allow access for anon
users also. Regd allows only regd users, no anon. if password is provided list
groups will return priv, or else pub. Default is public and regd. Ffor no
password and anon, specify #creategroup <gname> "" anon
#joingroup <gname> [passwd]
or
#changegroup <gname> [passwd]
return:
@status <no>
join a group. You can then post and read messages there. This will
automatically make you leave any other group you were a part of.
#leavegroup
return:
@status 0
leave any group you had joined. (possible bug in code here)
#listmessages [parent no]
return:
@status <no>
if there is an error else
@ret #listmessages <message no>
<message name> <owner> <id> <parent id> <no of replies> <terminate time>
...
a useful command. If no parent no is specified it returns all the messages
in the group. If specified it will return all messages with parent no as
specified. All top level messages have parent no as 0. All messages that are
replies have parent no as the no of the message they are a reply to. If a
message has any replies it is accounted for. All top level messages have non
zero terminate times. This is the no of seconds before the server deletes the
message. All reply messages are deleted recursively when the parent message
terminates.
#getmessage <mess no>
return on error:
@status <no>
else
@ret #getmessage <mess name> <parts> <no> <parent no> <replies>
<terminate time>
<text of message>
Retrieves a message when its number is given, the parts parameter
denotes how many lines of text the server is going to return.
#postmessage <name> [parent] [parts] [terminate
time]
<text>
return:
@status <no>
post a message to the current group. If parent is non zero, terminate
time is 0.If any parameters are not specified, default values are taken. Default
parent id is 0, parts is 1. The parts parameter denotes how many lines of text
will be posted to the server.
Status commands
#messagestatus
return
@ret #messagestatus <status no>
changes every time no of messages on the current group changes
#groupstatus
return
@ret #groupstatus <no>
Changes every time a group is added or removed.
Description of the error codes returned by @status
This is a copy of the error_def.h file that is part of the server source. It has been annotated.
#define NO_ERROR 0
#define UNKNOWN_ERROR (-1)
#define NOMEMORY_ERROR (-2)
The server ran out of memory. This is a rare case.
#define NOTFOUND_ERROR (-3)
Some item that was expected to have been found was not. For example you tried to send a private message to a user name that did not exist.
#define INVALIDNAME_ERROR (-4)
The name you have used if not valid. This maybe due to invalid characters or invalid length. Try stick to text and length <30 bytes.
#define NOTEMPTY_ERROR (-5)
You attempted to delete or cause some other change to at item that was not empty. Example a room where there are users.
//Access Errors
#define ACCESS_NO_LOGIN 3500
You are issuing commands without logging in.
#define ACCESS_NO_ANON 3501
This item is not accessible by anonymous users
#define ACCESS_WRONGCMD 3502
No such command, the server did not recognize the command you sent.
#define ACCESS_DELETIONSET 3503
You tried to access at item that is invalid because it has been marked for deletion; such an item will delete at the next server refresh and cannot be used.
//Message Errors
#define MESSAGE_NO_GROUP 3200
You issued some message command without joining any group.
#define MESSAGE_WRONGPASSWD 3201
The password supplied for joining the message group is wrong.
//ChatRoom related errors
#define ROOM_WRONGNAME 3100
You specified an invalid room name or one that does not exist.
#define ROOM_WRONGPASSWD 3101
Wrong password for the room you chose to join.
#define ROOM_NAME_USED 3102
You attempted to create a room with a name that is already used by another room.
#define ROOM_NOTOWNER 3103
You attempted an operation on a room that only the room’s owner has access to.
//LOGIN errors
#define LOGIN_NO_USER_TYPE 3000
Your login command did not specify a login type regd or anon.
#define LOGIN_REGD_WRONGNAME 3001
No such registered user.
#define LOGIN_REGD_WRONGPASSWD 3002
Wrong password.
#define LOGIN_ANON_USED 3003
The name you requested is already being used by another anonymous user.
#define LOGIN_REGD_USED 3004
The name you requested is reserved for a registered user.
CHATTER CLIENT
Chatter client encompasses all the basic features of a regular chat service and instant messaging service incorporating the concept of groups, rooms, direct chatting and posting messages. It provides with an ‘easy to use’ and efficient means of communication with a group of people as desired. Chatting can be directed to a set of friends or to everybody in a room. Also a private chat is supported, where messaging is with one user only. Multiple direct chats are allowed. Rooms and groups can be created by any user (anon/regd). They can be marked as public/private and like wise permit entries to regd/anon users.
Implementation
Chatter client has the heart of its implementation in an event queue which holds all the events that have occurred and are awaiting completion. On logging in an EVENT_LOGIN is pushed into the queue. Everytime a packet arrives in the OnReceive() function , the queue is peeked to find the event and the corresponding handle is called .In the above case, an OnHandleLogin() is called.
Similarly, there are events pre-defined for all packets requested. Examples are EVENT_CREATEROOM, EVENT_CHANGEROOM, EVENT_GETMESSAGE, EVENT_SENDTO etc. The queue prevents the clash and undesired jumbling of packets especially in cases where the next packet arrives before the previous is handled, which may be because of varying network speeds and excessive load.
The OnReceive() function sets a flag on entering it and resets it before leaving it. Next time OnReceive() is entered , if the flag is set then the function waits until it is reset. This waiting is nothing but waiting for the completion of the handling of the previous packet received. Once the handling of a particular event is handled, the corresponding event is popped from the queue.
Timers are used to monitor and update the room users, group users and group messages periodically.
More About Rooms
By default, every user logs into the Home room. Depending on his privileges he may/may not be allowed entry into a room.
On entering a room, the user is listed along with the other users as anon/regd depending on his login status. Message is sent by default to all users in the room. A user can mark out the desired users as friends, and then direct messages to friends only. He can choose to ignore a particular user, in which case, he doesn’t receive any messages sent from that particular user.
A user can also start a private chat (direct messaging) with one user. In this case, he receives all messages from that particular user in a private chat dialog box. If the other user also starts a private chat with this user, then he too receives messages from this user in a private chat dialog box which is apart from the common room chat. On a broader aspect, this form of private chatting is indeed an instant messaging scheme.
More About Groups
By default, a user is not in any group. If he chooses to join a group he may/may not be permitted, just like the case with rooms. On entering a group, the user sees all the messages posted to this group. Message groups thus, form another important aspect of the chatter client. It facilitates easy communication between a pre decided group of people, handling privacy and security.
In a group, the user can view the messages (their content), reply to a message or post new messages.
In case where a trust group is formed, this form of posting and reading messages becomes very useful.
If not used for some period of time, the rooms and groups are timed out by the server and the client keeps track of all such updates.
Limitations
Peer to peer connectivity supported by the chatter server is not implemented in the current version of chatter client.
CONCLUSIONS
This project can is best described in our opinions as a strangle with programming. In some senses we were not able to dedicate too much time to this project or have it as complete as we would have liked it to have been. On the other hand much has been achieved. We now have an understanding of the MSN protocol and how commercial quality messaging services work.
Though we did not manage to complete work on these, rather extensive ground work has been done on the Yahoo Messenger protocol, Rediff Bol and ICQ. This should serve as an excellent base to others who wish to pursue work in this line. Anyone wishing to continue work in this line should feel free to contact us.
The chat server we developed and its corresponding client is envisaged to be used in the future as Model Engineering Colleges official chat/communications service. This in many ways is a welcome change and a base for new standards and new concepts to take root which can be developed on by students of this college.
The source for the various project archives especially the server and its client should be available from the MEC server called www.meconline.org atleast for the near future.
REFERENCES
Here are lists of sources that we feel useful.
Firstly there is nothing better than having an intelligent person you can run to with those killer problems. Further, it really helps to have staff who help you and a great Head of department.
To the more conventional sources, firstly we would like to mention WPE written by Olivier Pasqualini. WPE is the Windows Packet editor, which turned out to be a great packet sniffing tool. Also in this category is Socket Spy by WinTECH software which provided a great API level debugger for windows socket calls to winsock.dll.
On the development side, nothing greater that Visual C++. Those guys have really got it going. Also a special mention of Rhide. Rhide is a Turbo C++ style IDE originally developed for DJGPP under dos. It has now been ported to linux. Rhide is great development environment under linux.
Also thanks to the endeavors of Richard Stallman who gave us gcc and the Open Source community for a practical free unix.
Thanks to George Anescu for his implementation of AES encryption and to the maintainers of CodeProject (www.codeproject.com) for the great source archives they maintain.
Special thanks to a Venkat for his curious site about protocol specifications at www.venkydude.com .
=======================================================================
APPENDIX
CHAT SERVER
Calling this a chat server is actually a misnomer, because in effect it does a few things more than just providing the chat facilities. We felt this appendix to be necessary to fully understand the scope and expanse of this project and also to give an insight into the intricacies of writing such an application. The server is relatively small application of around four thousand lines of c++ code, but is virtually an exercise into concurrency management and resource allocation.
In this documentation we will only be covering some aspects of the server that we feel might prove interesting to anyone working on a similar project and do not plan to elaborate on the whole application.
Functionality
The server (in its current version) provides support for the following:
Users
Users for the service are of two kinds
- Registered
- Anonymous
Registered are those have a login and password on the server, these are identifiable people who have specific rights on the server. Anonymous users can choose any free name, they have access to only certain services.
Chat
Chat Rooms
These maybe public (for everyone) or private (password protected). Users also have the option of creating rooms only registered users. Users in a room can send messages to each other or to all users.
There are presently 3 kinds of messages supported
- Public messages that go to everyone in that room
- Private messages that go only to a specific user
- Peer to Peer connectivity where 2 conforming clients can contact each other through the server
Rooms self delete themselves if they are not used for by anyone for a certain period.
Messages
The server lets you post messages (like email) to the server (unlike email which is maintained on a per user basis). These are maintained by the server and can be read by other users that log onto the server. You may post replies to existing messages and thus the gives the functionality of message/discussion threads.
Messages can be organized into Message Groups. Users are free to create their own message groups. Groups maybe only for registered users or for everyone. Also you may create private message groups.
Self deletion also applies to messages, in which case they delete all their reply messages recursively. Reply messages get deleted when their parent message deletes and have no deletion of their own. Message Groups also self delete in which case they destroy all the messages within them also.
Error Tolerance
The most difficult aspect of writing applications that are meant to be reliable is when their environments are often unreliable. To safe guard against unexpected power failures, the server runs multiple threads that monitor the status of the current room architecture, message groups and the messages of each group. These threads are sleeping most of the time and awaken periodically and save their relative parts if any changes are detected.
Security
This is at best a difficult concept to describe so let us tread carefully. There are two sides of security that are considered.
- The data maintained by the server on its disks (passwords and such)
- The sensitive data transferred across the network.
The data on the server is currently being saved in encrypted format. This presently uses AES encryption. The Rijndael encryption (a.k.a. AES) is a symmetric key recoverable encryption. Encryption on the server is maintained at 128 bit at the time of this writing.
The sensitive data transferred across the network, passwords etc, have rather difficult situation. This will be described later but for now it will suffice to mention that it is using non-recoverable Unix MD5 encryption.
Implementation Quirks
A brief mention of some aspects of the implementation that deserve some thought. Some of the features described here are not yet fully enabled at the time of this writing, but will be by the time this document is available for public access.
Encryption
Data on the Server
The data on the server is in Rijndael 128 bit encrypted. The problem is where should the key be maintained? The key is maintained within the server executable. This might come as a surprise to most who believe that having source code gives you absolute predictability of the behavior of the application.
The key is ‘injected’ into the server executable. The injection code looks for certain character patterns in the executable and then inserts the encryption key into it. The server on next startup notices a fresh key and accordingly patches its executable code to move the key to other places after modifying it a bit. Thus standard file compares on different the server executables will gives mismatches but none of those will directly disclose the key.
Data across the network
Everything across TCP/IP is track-able, no security there. Any recoverable encryption can be broken in time by a persistent hacker. The alternative is to use a non recoverable encryption.
In our scheme we found that the best is to send the client a random string. The client concatenates the string and the password and MD5 encrypts it. This is transferred across the network. The server does the same process at his end and compares the results. The concatenation was required so that the same password looks different on every login, else a hacker just needs to replicate the bytes even if he doesn’t understand what they are. This is similar to the process followed by Microsoft for its msn login.
Application Architecture and Threads
The application is our first attempt at writing UNIX based code, so there are some unfinished loop holes. It relies on the Posix -thread support provided by its host operating system. The current version was developed on a Redhat Linux 7.1 system. The server uses a plain text based protocol and can be connected and interfaced to by a regular telnet application.
The application has a main thread that listens on port 6500 for connections. At the time of this writing the server is up and working on the MEC server which is currently 203.197.150.179. When a connection is received two threads branch off (per user) from the main thread. One of these is the receive thread that receives data sent by the user and the other is the send thread. The receive thread of the user connection has the task of ‘talking’ to the other parts of the server to get its tasks done. The send thread is contacted by other threads of the server as well the users receive thread to send information to the user.
The application is has two major objects (c++ objects) that start on startup. These are ChatRoomManager and the MessageGroupManager. Another not so important object is the UserManager .
The ChatRoomManager is responsible for the creating a maintaining Rooms and to manage the flow of users between the rooms. This object runs threads for saving the rooms. This object also loads saved rooms on startup. Threads also run for each room which checks if the particular room is to self delete.
The MessageGroupManager loads saved message groups. It maintains the save-thread that saves of the list of active message groups. Each message group has its own thread that checks for its time out (for self deletion) as well as for saving the current messages.
Concurrency
The extensive use of threading in the application gives rise to many concurrency related issues in the server such as resource sharing and deadlocks. These are similar to the discussions by Andrew Tanenbaum in his OS text. The only one we chosen to discuss here is the SetSendBuff issue.
The SetSendBuff
Every client connected to the server has 2 dedicated threads. Of these the send thread sends the data to the user. This thread simply a function that sits in a loop that exits when the connection terminates. The loop’s purpose is to send data to the user whenever it is available.
This gives rise to the classical producer consumer problem, with the additional quirk that there are many producers here and any of these or the consumer (the send thread) may go invalid at any point.
The send thread checks a flag to see if data is set in a buffer. If the flag is found to be true, it sends the data. The issue comes in to maintain integral data in this buffer when many threads are trying to write into it and one is trying to read from it.
The solution is by the use of a mutex to lock access to the flag and the buffer. That does not solve the problem, if multiple locks are set on the mutex, 2 threads consecutively locked to send data will not be able to so as the second one will find the flag set and the send thread is unable to access the data as the mutex has locked it out.
What has been done in our implementation is a push-ahead-in-the-queue case. Assume that multiple locks on a mutex act as a queue of threads waiting for processing. Each producer writes locks the mutex, if the flag is not set, sets it and writes the data into buffer and unlocks the mutex. Every thread that finds the flag set knows that it cannot ever send the data unless the send thread sends the data. It also knows that the send thread is waiting behind in the queue to accomplish this.
The solution is that every producer thread that finds the flag set merely unlocks the mutex and relocks it, repeatedly till the flag is unset. This has a an interesting effect on the mutex queue, the thread has effectively placed itself at the end of the mutex queue thus letting the send thread’s lock ahead of it. As each one does the same thing, the send thread will eventually have access to the buffer. It will send the data and then unset the flag. The next thread line will then have access to the buffer. And so on.
Notes
The server had been, in its original implementation, done in about 5 or 6 days. The development still continues with little tweaks everyday and is slowly maturing. Some of these are recommended as worth taking a look at.
Among the acknowledgements for this development, I would like to mention George Anescu for his post of visual c++ code on Rijndael encryption at www.codeproject.com
Also thanks to the official maintainers of the MD5 encryption algorithm and its code, Mordechai T. Abzug.
Development tools used were gcc as the compiler and rhide as the IDE. A special mention and thanks to Robert Hohne for RH-IDE, which was a blast of fresh air compared other cryptic standard IDEs provided. Rhide is a tc (under dos) like IDE that gives very good project options and file navigation and session maintenance features that seamlessly integrates with gcc.