Content from 2002-07

IMUnified - Protokoll

posted on

IMUnified hat vor etwa zwei Jahren angekündigt, ein Protokoll zu schaffen, das es ermöglichen soll, verschiedene Instant Messaging Systeme mit einem Client zu benutzen. Gründungsmitglieder waren unter anderem MSN, Odigo und Yahoo!.

Inzwischen ist es wieder ruhig geworden, ob IMU jemals ernsthaft eingesetzt wird, wird wage ich fast zu bezweifeln. Aber immerhin kann beispielsweise der Yahoo!-Messenger das Protokoll, man muss es nur mit ein paar Registry-Änderungen unter Windows einschalten. Danach hat man die Möglichkeit, sich zusätzlich zu Yahoo! auch in andere IM Dienste anzumelden.

Dies habe ich zum Anlass genommen, mir IM Unified doch einmal anzuschauen und habe ein paar Sessions mit einem Network-Sniffer protokolliert. Das Protokoll ist an sich sehr einfach aufgebaut und erinnert etwas an Protokolle wie SMTP oder HTTP.

Alle Daten werden über eine TCP/IP-Verbindung ausgetauscht. Der Client konnektiert hierzu auf den Port 11319 des Servers. Über diese Verbindung macht er zunächst ein Login-Handshake und tauscht danach Daten aus. Der Austausch erfolgt in Blöcken.

Diese Blöcke haben dabei eine Zeilenstruktur, jede Zeile wird durch \r\n (carriage return, line feed) abgeschlosssen. Die erste Zeile enthält den Typ des Blockes, er gibt die Art des Befehles an. In der zweiten Zeile steht eine ASCII-Dezimalzahl, die angibt, wie viele Bytes der Block (gerechnet ab der dritten Zeile) enthält. Ab der dritten Zeile stehen Parameter-Zeilen, optional gefolgt von einer leeren Zeile zum Abschluss der Headerzeilen, optional gefolgt von einem Body des Blockes (z.B. in Nachrichten-Blöcken). Der Yahoo!-Messenger scheint die leere Zeile grundsätzlich zu übertragen, auch wenn kein Body benötigt wird, der Odigo-Server scheint die Leerzeile nur zu übermitteln, wenn der Body nicht leer ist.

Blocktypen (also Inhalt der ersten Zeile) habe ich bei der Nutzung von Odigo mit dem Yahoo!-Client dabei bisher folgende erhalten: "HELO" (vor Anmeldung, Versionsabsprache, Absprache über Authorisierungstyp), "LOGN Odigo-Nummer" (Austausch der eigentlichen Logininfo), "STAT status" (Setzen und empfangen eines Anwesenheitsstatus, z.B. STAT ONLINE), "ACK 600" (Bestätigung, keine Ahnung was die 600 bedeutet), "LIST ADD Odigo-Nummer" (Hinzufügen eines Nutzers), "PING" (Keep-Alive-Packets, Intervall bestimmt der Server in seinem HELO-Block), "MESG" (Nachricht), "LIST ACCEPT Odigo-Nummer" (Hinzufügen von sich selbst für einen Nutzer erlauben), "DISC" (Abmelden).

Die Kopfzeilen in einem Block haben immer das Format "Bezeichnung: Inhalt". Eine dabei immer enthaltene Kopfzeile ist die ID-Zeile. Sie identifiziert jeden Block eindeutig, der Server spiegelt die enthaltene ID in einer Reference-Kopfzeile, wenn sich ein Block direkt auf einen Block des Clients bezieht (z.B. bei "ACK 600"-Blöcken). Im Falle es Yahoo!-Messengers werden die IDs anscheinend immer als erste Kopfzeile gesendet und haben als Inhalt eine fortlaufende ASCII-Dezimalzahl ab 1001. Auch übermittelt der Yahoo!-Messenger die ID-Zeile immer als erste Kopfzeile. Dies ist vermutlich nicht notwendig, jedenfalls fügt der Odigo-Server seine ID-Zeile nicht immer als erste Kopfzeile ein.

Der erste vom Client zum Server übertragene Block ist ein "HELO"-Block, neben der ID-Zeile enthält dieser Block noch eine Protocol-Zeile ("Protocol: IMIP/1.0") sowie eine leere Zeile. Der Odigo-Server antwortet mit "Auth-Type: imip-md5", "Capabilities: server-lists", ID-Zeile, "Keep-Alive: 60", "Protocol: IMIP/1.0", "Registration: http://www.odigo.com/user", "Service: odigo.com", "ServiceDisplayName: Odigo", Leerzeile, ASCII-String (bei Odigo eine dezimale 32-Bit-ASCII-Zahl, als Salt für das Passwort). Die Zeilen scheinen recht eindeutig zu sein, die Keep-Alive-Zeile scheint zu steuern wie oft der Client einen PING-Block schickt. Der HELO-Block des Servers besitzt interessanterweise keine Reference-Zeile.

Danach sendet der Client einen "LOGN Odigo-Nummer"-Block. Die enthaltenen Zeilen sind: ID-Zeile, "Client: Yahoo! IMIP Client", "Auth-Type: imip-md5" und eine Leerzeile, danach kommt als Body eine 128-Bit-Zahl hexadezimal mit Kleinbuchstaben in ASCII-Darstellung. Diese Zahl ist der md5-Hash des Strings, der durch direktes aneinanderhängen des Salt-Strings aus dem HELO-Block des Servers und dem Passwort gebildet wird. An der UNIX-Kommandozeile kann dies mit dem m5sum-Befehl nachvollzogen werden. Der Wert für das Salt "1919833824" und das Passwort "Passwort" wird wie folgt berechnet: printf "1919833824Passwort" | md5sum. Der Server antwortet darauf mit einem "LOGN Odigo-Nummer"-Block. Kopfzeilen sind nur ID und Reference enthalten.

Anschließend setzt der Client sich online mit einem "STAT ONLINE"-Block, enthaltene Kopfzeilen: ID-Zeile und Leerzeile. Der Server antwortet mit einem "ACK 600"-Block, enthaltene Zeilen: ID und Reference. Andere Online-Stati sind "STAT BUSY", "STAT AWAY" und "STAT OFFLINE" (invisible). Dem Online-Status kann ein erklärender Text als Body des Blockes hinzugefügt werden.

Der Server informiert den Client über die Anwesenheit der Kontakte ebenfalls mit "STAT Status"-Blöcken. Enthaltene Kopfzeilen sind ID und "From: Odigo-Nummer". Es ist keine Leerzeile und kein Body enthalten. Auch bestätigt der Client den Empfang nicht.

Der Server setzt in seinem HELO-Block ein Intervall mit dem der Client PING-Blöcke sendet. Ein solcher Block enthält zwei Kopfzeilen: ID und eine leere. Der Server antwortet mit dem oben beschriebenen "ACK 600"-Block.

Eine abgehende Nachricht verschickt der Yahoo!-Messenger mit einem "MESG"-Block, der die folgenden Zeilen enthält: ID, "To: Odigo-Nummer", "ACK-Type: errors-only", "From: eigene Odigo-Nummer", Leerzeile gefolgt vom Nachrichtenbody. Vom Server kam keine Bestätigung, dies ist vermutlich über die ACK-Type-Zeile gesteuert.

Eine eingehende Nachricht wird ebenfalls in einem "MESG"-Block übertragen. Der Odigo-Server übermittelt dabei jedoch ein paar mehr Kopfzeilen als oben im "MESG"-Block des Clients. Enthalten sind: "Content-Type: text/plain;charset=utf-8", "From: Odigo-Nummer "Nickname"", ID-Zeile, "Time: yyyy-mm-ddThh:mm:ssZ" (Kleinbuchstaben im Datumsstring stehen für entsprechende Zahlen, Angabe in UTC), "To: eigene Odigo-Nummer", Leerzeile und der Nachricht im Body. Der Client bestätigt diesen Block nicht.

Die Abmeldung leitet der Client mit einem "DISC"-Block ein. Enthalten ist eine ID-Zeile und eine leere Zeile. Der Server bestätigt dies mit einem üblichen "ACK 600"-Block. Danach wird die Verbindung getrennt.

Auf die Kontaktliste wird ein Kontakt mit einem "LIST ADD Odigo-Nummer"-Block hinzugefügt. Enthaltene Kopfzeilen sind dabei: ID, "List: Buddy" (vermutlich Auswahl einer Gruppe), "From: Odigo-Nummer" und eine Leerzeile. Der Server antwortet sofort mit einem "ACK 600"-Block. Dass das Gegenüber die Sichtbarkeit erlaubt hat, erfährt man, indem man die Anwesenheit mit einem "STAT Online-Status"-Block übermittelt bekommt.

Wenn jemand einen selbst auf seine Kontaktliste hinzufügen möchte, so bekommt man einen "LIST ADD"-Block ohne Odigo-Nummer in der ersten Zeile. Enthaltene Kopfzeilen sind: "From: Odigo-Nummer "Nick-Name"", ID, "List: Buddy" (ich hatte den entsprechenden Kontakt in dieser Gruppe stehen), Leerzeile und der mit der Authorisierungsanforderung angegebene Text. Die Authorisierung wird mit einem "LIST ACCEPT Odigo-Nummer "Nick-Name""-Block angenommen. Enthaltene Kopfzeilen darin sind ID, From und eine Leerzeile. Der Server bestätigt dies mit einem "ACK 600"-Block.

Update: Ich vermute inzwischen, dass "List: Buddy" keine Gruppe ist, sondern es sich dabei um die Contact-List an sich handelt und andere Listen evtl. Invisible-List und dergleichen sein könnten. Ich habe hierzu jedoch noch keine weiteren Nachforschungen angestellt. Neu habe ich jedoch inzwischen herausgefunden, wie ein Kontakt von der Contact-List entfernt wird (Block des Typs "LIST REMOVE Odigo-Nummer" mit den Headern ID, "List: Buddy" und "From: Odigo-Nummer"; Bestätigung durch den Server mit einem "ACK 600"-Paket) und wie das Hinzufügen zur Contact-List abgelehnt wird (Block des Typs "LIST REJECT Odigo-Nummer" mit den Headern ID und "From: Odigo-Nummer").

Im LOGN-Block des Servers wird die Contact-List des Users gemeldet. Dies geschieht in der Header-Zeile Buddy, die eine Liste von Nutzern (und deren Nick-Name in Anführungszeichen) durch Kommata getrennt enthält.

Folgende weitere ACK-Statusnummern habe ich neben "ACK 600" bisher erhalten: 800 als ich einen STAT-Block ohne Status gesendet habe, 801 als ich einen STAT-Block mit ungültigem Status gesendet habe, 810 wenn ich beim Login ein falsches Passwort verwende, 811 wenn ich beim Login eine ungültige User-ID nutze oder an eine solche User-ID eine Nachricht schicke (die UserID war nicht numerisch, wie bei Odigo immer de Fall).

Einfache IMIP-Session als Beispiel, Beispiel mit Nachrichtentausch und Subscription, Testimplementierung eines einfachen Java-Odigo-Client auf IMIP-Basis.

IMUnified - protocol

posted on

In 2000, IM Unified announced to create a protocol that enables an instant messaging client to join different IM services. Founding members were MSN, Odigo, Yahoo! and others.

Since then I have not heard much of IMU. I don't even think that their IMIP protocol will ever be used really. But it does exist and e.g. the Yahoo! messenger client supports it. You only have to change some values in the Windows registry. Afterwards, you're client is able to join other IM services like Odigo with the Y! messenger.

For no other reason than to see how IMIP works I've dumped some IMIP sessions with a network sniffer. The IMIP protocol is very simple structured and looks a bit like SMTP or http.

All data is exchanged over a TCP/IP connection. The clients connects to port 11319 at the server. Firstly, it performs the login handshake and afterswards, it is exchanging messages, presences and other data. All data is exchanged in blocks.

All blocks have a structure of lines. Every line is finished by \r\n (carriage return, line feed). The first line contains the type of a block, the second line contains the size of the block (first two lines not counted). It is a decimal ASCII number containing the size in bytes. Starting at the third line, there are head lines, optionally followed by an empty line, optionally followed by a block body. If the body is empty, the empty line can be absent. (The Y! messenger is allways sending the empty line, the Odigo server isn't.)

As block types (content of the first line) I have noticed the following so far: "HELO" (before login, version check, negotiation of auth type), "LOGN odigo-number" (exchange of login data), "STAT presence" (exchange of presence information, presence is one of ONLINE, AWAY, BUSY, OFFLINE (other side going offline or this side going invisible), "ACK status" acknowledge (status 600 is OK, I got 811 after sending wrong login data), "LIST ADD odigo-number" (adding user to list), "PING" (keep-alive packet, interval set by server in HELO block), "MESG" (message), "LIST ACCEPT odigo-number" (accept being added by other side), "DISC" (logout).

The header lines always have the form "identifier: content\r\n". Every block has an ID line ("ID: idvalue"). This ID is mirrored in a reply by the "Reference: idvalue" header line, sent by the server. The Y! messenger starts to generate the IDs at "1001" and increments this value for every block. The Y! messenger is always sending the ID lines as the first header line. I don't think this is neccessary because the Odigo server is not always sending this line as first header line.

The first block that is transmitted from the client to the server is a "HELO" block. Besides the ID line it contains a "Protocol: IMIP/1.0" line. The Odigo server replies with another "HELO" block containing the following lines: "Auth-Type: imip-md5", "Capabilities: server-lists", the ID line, "Keep-Alive: 60", "Protocol: IMIP/1.0", "Registration http://www.odigo.com/user", "Service: odigo.com", "ServiceDisplayName: Odigo", an empty line and an ASCII string (Odigo uses the decimal ASCII representation of a 32 bit value), that is used as salt for the MD5 password exchange. The Keep-Alive line seems to control the interval of the client sending "PING" blocks. The HELO block does not contain a Reference line.

Afterwards, the client sends a "LOGIN odigo-number" block containing the following header lines: ID line, "Client: Yahoo! IMIP Client", "Auth-Type: imip-md5", an empty line and the MD5 hash of a string containing the salt concatenated with the user's password. The hash is represented as a hexadecimal ASCII value with small letters. At the UNIX command line you can reproduce the hash value with md5sum. e.g. if the salt in the HELO block was "1919833824" and your password is "password" you get the hash with: printf "1919833824password" | md5sum (the result should be c35900c24a82592dd2d0db27c647ff17). The server replies with a "LOGN odigo-number" block containing only ID and Reference header lines.

After login, the client sets its presence with a "STAT status" block (status being ONLINE, BUSY, AWAY or OFFLINE). This block only contains an ID header line. The server replies with an "ACK 600" block containing ID and reference header lines. The presence can be explained additionally by a message in the body of the block.

The server informs the client about the presence of the user's contacts with "STAT status" blocks as well. These blocks contain an ID header line and a "From: odigo-number" header line. The client doesn't reply these blocks.

Every time the interval set by the HELO block expires the client sends a "PING" block to the server. This block only contains the ID header line. The server replies with "ACK 600".

A message is sent by a "MESG" block that contains the following header lines: ID, "To: odigo-number", "ACK-Type: errors-only", "From: own-odigo-number". After an empty line, there is the body of the message. The server doesn't react on this, probably controlled by the ACK-Type line.

A message is also received by a "MESG" block. The server sends some more header lines: "Content-Type: text/plain;charset=utf-8", "From: odigo-number \"nickname\"", ID line, "Timo: yyyy-mm-ddThh:mm:ssZ" (time in UTC), "To: own-odigo-number", an empty line and afterwards the body with the message in it. The client doesn't react on this block.

The clients starts a disconnect with a "DISC" block containing only an ID header line. The server replies with an "ACK 600" block. Afterwards the connection is closed.

To add a contact to the list, the client sends a "LIST ADD odigo-number" block. The contained header lines are: ID, "List: Buddy", "From: odigo-number". The body can contain a message. The server replies immediately with an "ACK 600" block containing ID and Reference header lines. After the contact has accepted the subscription, the server sends a "STAT status" block.

If somebody else wants to add you to his list, the server sends a "LIST ADD" block without an Odigo number in the first line. This block contains the following header lines: "From: odigo-number \"nick\"", ID, "List: Buddy" (I had this user in the Buddy group). This block can contain a message in the body. The subscription is accepted by a "LIST ACCEPT odigo-number \"nick\"" block, that contains an ID and a From header line. It does not contain a Reference header line. The server replies with an "ACK 600" block.

Update: In the meantime I beleave that "List: Buddy" is no contact list group but just the contact list and that there could be other lists like a invisible list. I havn't done more research on this topic yet. New is what I found out how a contact is removed (block of type "LIST REMOVE odigo-number" with the headers ID, "List: Buddy" and "From: odigo-number"; acknowledged with A "ACK 600" block by the server) and how to reject a subscription (block of type "LIST REJECT odigo-number" with the headers ID and "From: odigo-number").

In the LOGN block of the server there is also the list of buddies on the contact-list. This in the Buddy header. The content of this header is a list of comma separated contacts (with their nick names appended in quotes).

I got the following ACK status numbers besides "ACK 600": 800 after I sent a STAT block without a status, 801 after I sent a STAT block with a non existant status, 810 if I try to log in with a wrong password, 811 if I try to log in with an illegal user id or send a message to an illegal user id (the id was non-numerical, Odigo uses numerical IDs).

Simple IMIP session as an example, IMIP session with message exchange and subscription, simple Java-Odigo-Client implementing the IMIP protocol.


Unless otherwise credited all material Creative Commons License by Matthias Wimmer