Ethereal-dev: Re: [ethereal-dev] ICQ Crash - vprintf()

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Kojak <kojak@xxxxxxxxxx>
Date: Sun, 05 Dec 1999 22:19:02 +0100
> Program received signal SIGSEGV, Segmentation fault.
> 0x4040f466 in vfprintf ()
> (gdb) Terminated
> 
> >From the backtrace made in a core you wont see any symbols resolving
> so i guess it triggers a bug in an external lib (gtk ?) 
> 
Nope it was a bug in my code. There was already a small fix, this patch
fixes other bugs in the code, as well as adds a few more packets.

The code also outputs which types it cannot decode. I want to get rid of
them before the next formal release, but I have work to finish first, sorry.

Have fun.

Kojak

Index: packet-icq.c
===================================================================
RCS file: /cvsroot/ethereal/packet-icq.c,v
retrieving revision 1.8
diff -u -r1.8 packet-icq.c
--- packet-icq.c	1999/12/01 23:58:44	1.8
+++ packet-icq.c	1999/12/05 21:07:34
@@ -145,7 +145,7 @@
 #define SRV_USER_ONL_PORT	0x0008
 #define SRV_USER_ONL_REALIP	0x000c
 #define SRV_USER_ONL_X1		0x0010
-#define SRV_USER_ONL_STATUS	0x0011
+#define SRV_USER_ONL_STATUS	0x0013
 #define SRV_USER_ONL_X2		0x0015
 
 #define SRV_USER_OFFLINE	0x0078
@@ -166,6 +166,7 @@
 /*
  * ICQv5 SRV_META_USER subcommands
  */
+#define META_EX_USER_FOUND	0x0190
 #define META_USER_FOUND		0x019a
 #define META_ABOUT		0x00e6
 #define META_USER_INFO		0x00c8
@@ -179,8 +180,22 @@
 #define SRV_RECV_MSG_MINUTE	0x0009
 #define SRV_RECV_MSG_MSG_TYPE	0x000a
 
+#define SRV_RAND_USER		0x024e
+#define SRV_RAND_USER_UIN	0x0000
+#define SRV_RAND_USER_IP	0x0004
+#define SRV_RAND_USER_PORT	0x0008
+#define SRV_RAND_USER_REAL_IP	0x000c
+#define SRV_RAND_USER_CLASS	0x0010
+#define SRV_RAND_USER_X1	0x0011
+#define SRV_RAND_USER_STATUS	0x0015
+#define SRV_RAND_USER_TCP_VER	0x0019
+
+/* This message has the same structure as cmd_send_msg */
+#define SRV_SYS_DELIVERED_MESS	0x0104
+
 cmdcode serverMetaSubCmdCode[] = {
     { "META_USER_FOUND", META_USER_FOUND },
+    { "META_EX_USER_FOUND", META_EX_USER_FOUND },
     { "META_ABOUT", META_ABOUT },
     { "META_USER_INFO", META_USER_INFO },
     { NULL, -1 }
@@ -203,7 +218,7 @@
     { "SRV_X2", 230 },
     { "SRV_NOT_CONNECTED", 240 },
     { "SRV_TRY_AGAIN", 250 },
-    { "SRV_SYS_DELIVERED_MESS", 260 },
+    { "SRV_SYS_DELIVERED_MESS", SRV_SYS_DELIVERED_MESS },
     { "SRV_INFO_REPLY", 280 },
     { "SRV_EXT_INFO_REPLY", 290 },
     { "SRV_STATUS_UPDATE", 420 },
@@ -213,7 +228,7 @@
     { "SRV_AUTH_UPDATE", 500 },
     { "SRV_MULTI_PACKET", SRV_MULTI },
     { "SRV_X1", 540 },
-    { "SRV_RAND_USER", 590 },
+    { "SRV_RAND_USER", SRV_RAND_USER },
     { "SRV_META_USER", SRV_META_USER },
     { NULL, -1 }
 };
@@ -275,6 +290,8 @@
 #define CMD_SEND_TEXT_CODE_LEN	0x0000
 #define CMD_SEND_TEXT_CODE_TEXT	0x0002
 
+#define CMD_MSG_TO_NEW_USER     0x0456
+
 #define CMD_QUERY_SERVERS	0x04ba
 
 #define CMD_QUERY_ADDONS	0x04c4
@@ -324,7 +341,7 @@
     { "CMD_SEND_TEXT_CODE", CMD_SEND_TEXT_CODE },
     { "CMD_ACK_MESSAGES", CMD_ACK_MESSAGES },
     { "CMD_LOGIN_1", 1100 },
-    { "CMD_MSG_TO_NEW_USER", 1110 },
+    { "CMD_MSG_TO_NEW_USER", CMD_MSG_TO_NEW_USER },
     { "CMD_INFO_REQ", 1120 },
     { "CMD_EXT_INFO_REQ", 1130 },
     { "CMD_CHANGE_PW", 1180 },
@@ -567,7 +584,51 @@
     return p;
 }
 
+/*
+ * The packet at pd has a (len, string) pair.
+ * Copy the string to a buffer, and display it in the tree.
+ * Observe any limits you might cross.
+ *
+ * If anything is wrong, return -1, since -1 is not a valid string
+ * length. Else, return the number of chars processed.
+ */
+static guint16
+proto_add_icq_attr(proto_tree* tree, /* The tree to add to */
+		   const char* pd, /* Pointer to the field */
+		   const int offset, /* Offset from the start of packet */
+		   const int size, /* The number of bytes left in pd */
+		   char* descr)	/* The description to use in the tree */
+{
+    guint16 len;
+    char* data;
+    int left = size;
+    
+    if (size<sizeof(guint16))
+	return -1;
+    len = pletohs(pd);
+    left -= sizeof(guint16);
+    if (left<len) {
+	proto_tree_add_text(tree,
+			    offset,
+			    sizeof(guint16),
+			    "Length: %d", len);
+	return -1;
+    }
+			    
+    data = g_malloc(len);
+
+    strncpy(data, pd + sizeof(guint16), len);
+    data[len - 1] = '\0';
+
+    proto_tree_add_text(tree,
+			offset,
+			sizeof(guint16) + len,
+			"%s[%d]: %s", descr, len, data);
+    g_free(data);
 
+    return len + sizeof(guint16);
+}
+
 static void
 icqv5_decode_msgType(proto_tree* tree,
 		     const unsigned char* pd, /* From start of messageType */
@@ -586,6 +647,7 @@
 	"First name",
 	"Last name",
 	"Email address",
+	"Unknown",
 	"Reason"};
     static char* emain_field_descr[] = {
 	"Nickname",
@@ -706,24 +768,35 @@
 	break;
     }
     case MSG_AUTH_REQ:
-	/* Five parts, separated by FE */
+	/* Six parts, separated by FE */
 	i = 0;
 	j = 0;
-	msgText = NULL;
-	for (n = 0; n < 5; n++) {
-	    for (;
-		 (i<left) && (pd[OFF_MSG_TEXT+i]!=0xfe);
-		 i++)
-		;
-	    msgText = g_realloc(msgText, i-j);
-	    strncpy(msgText, pd + OFF_MSG_TEXT + j, i - j - 1);
-	    msgText[i-j-1] = '\0';
-	    proto_tree_add_text(subtree,
-				offset + OFF_MSG_TEXT + j,
-				i - j - 1,
-				"%s: %s", auth_req_field_descr[n], msgText);
-	    j = ++i;
-	}
+	msgText = g_malloc(64);
+	for (n = 0; n < 6 && i<left; n++) {
+            while (i<left && pd[OFF_MSG_TEXT+i]!=0xfe)
+                i++;
+            if (i<=left) {
+                /* pd[OFF_MSG_TEXT+i] == 0xfe */
+                if (i!=j) {   
+                    /* Otherwise, it'd be a null string */
+                    msgText = g_realloc(msgText, i - j);
+                    strncpy(msgText, pd + OFF_MSG_TEXT + j, i-j);
+		    msgText[i-j] = '\0';
+                    proto_tree_add_text(subtree,
+                                        offset + OFF_MSG_TEXT + j,
+                                        i - j,
+                                        "%s: %s", auth_req_field_descr[n], msgText);
+                } else {
+                    proto_tree_add_text(subtree,
+                                        offset + OFF_MSG_TEXT + j,
+                                        i - j,
+                                        "%s: %s", auth_req_field_descr[n], "(null)");
+                }
+                j = ++i;
+                /* i and j point after the 0xfe character */
+            }
+        }    
+
 	if (msgText != NULL)
 	    g_free(msgText);
 	break;
@@ -734,20 +807,30 @@
 	/* This is necessary, because g_realloc does not behave like
 	     * g_malloc if the first parameter == NULL */
 	msgText = g_malloc(64);
-	for (n = 0; n < 4; n++) {
-	    for (;
-		 (i<left) && (pd[OFF_MSG_TEXT+i]!=0xfe);
-		 i++)
-		;
-	    msgText = g_realloc(msgText, i-j+1);
-	    strncpy(msgText, pd + OFF_MSG_TEXT + j, i - j);
-	    msgText[i-j] = '\0';
-	    proto_tree_add_text(subtree,
-				offset + OFF_MSG_TEXT + j,
-				i - j,
-				"%s: %s", auth_req_field_descr[n], msgText);
-	    j = ++i;
-	}
+        for (n = 0; n < 4 && i<left; n++) {
+            while (i<left && pd[OFF_MSG_TEXT+i]!=0xfe)
+                i++;
+            if (i<=left) {
+                /* pd[OFF_MSG_TEXT+i] == 0xfe */
+                if (i!=j) {   
+                    /* Otherwise, it'd be a null string */
+                    msgText = g_realloc(msgText, i - j);
+                    strncpy(msgText, pd + OFF_MSG_TEXT + j, i-j);
+		    msgText[i-j] = '\0';
+                    proto_tree_add_text(subtree,
+                                        offset + OFF_MSG_TEXT + j,
+                                        i - j,
+                                        "%s: %s", auth_req_field_descr[n], msgText);
+                } else {
+                    proto_tree_add_text(subtree,
+                                        offset + OFF_MSG_TEXT + j,
+                                        i - j,
+                                        "%s: %s", auth_req_field_descr[n], "(null)");
+                }
+                j = ++i;
+                /* i and j point after the 0xfe character */
+            }
+        }    
 	if (msgText != NULL)
 	    g_free(msgText);
 	break;
@@ -1046,7 +1129,8 @@
 icqv5_cmd_send_msg(proto_tree* tree,
 		   const u_char* pd,
 		   int offset,
-		   int size)
+		   int size,
+		   int cmd)
 {
     proto_tree* subtree;
     proto_item* ti;
@@ -1055,24 +1139,25 @@
     guint16 msgLen = 0xffff;
     int left = size;		/* left chars to do */
     
-    if (left >= 4) {
-	receiverUIN = pletohl(pd + CMD_SEND_MSG_RECV_UIN);
-	left -= 4;
-    }
-    if (left >= 2) {
-	msgType = pletohs(pd + CMD_SEND_MSG_MSG_TYPE);
-	left -= 2;
-    }
-    if (left >= 2) {
-	msgLen = pletohs(pd + CMD_SEND_MSG_MSG_LEN);
-	left -= 2;
-    }
+    if (left < 4)
+	return;
+    receiverUIN = pletohl(pd + CMD_SEND_MSG_RECV_UIN);
+    left -= 4;
+    if (left < 2) 
+	return;
+    msgType = pletohs(pd + CMD_SEND_MSG_MSG_TYPE);
+    left -= 2;
+    if (left < 2) 
+	return;
+    msgLen = pletohs(pd + CMD_SEND_MSG_MSG_LEN);
+    left -= 2;
+
     if (tree) {
 	ti = proto_tree_add_item_format(tree,
 					hf_icq_cmd,
 					offset,
 					size,
-					CMD_SEND_MSG,
+					cmd,
 					"Body");
 	subtree = proto_item_add_subtree(ti, ett_icq_body);
 	proto_tree_add_text(subtree,
@@ -1326,8 +1411,8 @@
     if (size >= SRV_USER_ONL_REALIP + 4)
 	realipAddrp = &pd[SRV_USER_ONL_REALIP];
 
-    if (size >= SRV_USER_ONL_STATUS + 4)
-	status = pletohl(pd + SRV_USER_ONL_STATUS);
+    if (size >= SRV_USER_ONL_STATUS + 2)
+	status = pletohs(pd + SRV_USER_ONL_STATUS);
 
     /*
      * Kojak: Hypothesis is that this field might be an encoding for the
@@ -1363,7 +1448,7 @@
 			    "RealIP: %s", ip_to_str(realipAddrp));
 	proto_tree_add_text(subtree,
 			    offset + SRV_USER_ONL_STATUS,
-			    4,
+			    2,
 			    "Status: %s", findStatus(status));
 	proto_tree_add_text(subtree,
 			    offset + SRV_USER_ONL_X2,
@@ -1459,6 +1544,7 @@
 		    int size)              /* Number of chars left to do */
 {
     proto_tree* subtree = NULL;
+    proto_tree* sstree = NULL;
     proto_item* ti = NULL;
     int left = size;
     const char* p = pd;
@@ -1472,6 +1558,7 @@
 	result = pd[SRV_META_USER_RESULT];
 
     if (tree) {
+#if 0
 	ti = proto_tree_add_item_format(tree,
 					hf_icq_cmd,
 					offset,
@@ -1487,7 +1574,40 @@
 			    offset + SRV_META_USER_RESULT,
 			    1,
 			    "%s", (result==0x0a)?"Success":"Failure");
+	sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
+#else
+	ti = proto_tree_add_text(tree,
+				 offset + SRV_META_USER_SUBCMD,
+				 2,
+				 "%s", findSubCmd(subcmd));
+	sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
+	proto_tree_add_text(sstree,
+			    offset + SRV_META_USER_RESULT,
+			    1,
+			    "%s", (result==0x0a)?"Success":"Failure");
+#endif
+
+	/* Skip the META_USER header */
+	left -= 3;
+	p += 3;
+
 	switch(subcmd) {
+	case META_EX_USER_FOUND:
+	{
+	    /* This is almost the same as META_USER_FOUND,
+	     * however, there's an extra length field
+	     */
+	    guint16 pktLen = -1;
+
+	    /* Read the lenght field */
+	    pktLen = pletohs(p);
+	    proto_tree_add_text(sstree,
+				offset + size - left,
+				sizeof(guint16),
+				"Length: %d", pktLen);
+	    
+	    p += sizeof(guint16); left -= sizeof(guint16);
+	}
 	case META_USER_FOUND:
 	{
 	    /* The goto mentioned in this block should be local to this
@@ -1496,92 +1616,51 @@
 	     * They are used to "implement" a poorman's exception handling
 	     */
 	    guint32 uin = -1;
-	    char* nick = NULL;	/* Nick */
-	    char* first = NULL;	/* First name */
-	    char* last = NULL;	/* Last name */
-	    char* email = NULL;
-	    unsigned char auth = -1;
 	    int len = 0;
+	    char *descr[] = {
+		"Nick",
+		"First name",
+		"Last name",
+		"Email",
+		NULL};
+	    char** d = descr;
 	    guint16 x2 = -1;
 	    guint32 x3 = -1;
-	    proto_tree* sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
-
-	    /* Skip over META_USER header */
-	    left -= 3;
-	    p += 3;
-	    /* Get the uin */
+	    unsigned char auth;
+	    /*
+	     * Read UIN
+	     */
 	    if (left<sizeof(guint32))
-		goto stopMetaUser;
+		break;
 	    uin = pletohl(p);
 	    proto_tree_add_text(sstree,
 				offset + size - left,
 				sizeof(guint32),
 				"UIN: %ld", uin);
 	    p+=sizeof(guint32);left-=sizeof(guint32);
-	    /* Get the nickname */
-	    if (left<sizeof(guint16))
-		goto stopMetaUser;
-	    len = pletohs(p);
-	    p+=sizeof(guint16);left-=sizeof(guint16);
-	    if ((len<=0) || (left<len))
-		goto stopMetaUser;
-	    nick = g_malloc(len);
-	    strncpy(nick, p, len);
-	    proto_tree_add_text(sstree,
-				offset + size - left,
-				sizeof(guint16)+len,
-				"Nick(%d): %s", len, nick);
-	    p+=len;left-=len;
-	    /* Get the first name */
-	    if (left<sizeof(guint16))
-		goto stopMetaUser;
-	    len = pletohs(p);
-	    p+=sizeof(guint16);left-=sizeof(guint16);
-	    if ((len<=0) || (left<len))
-		goto stopMetaUser;
-	    first = g_malloc(len);
-	    strncpy(first, p, len);
-	    proto_tree_add_text(sstree,
-				offset + size - left,
-				sizeof(guint16)+len,
-				"First(%d): %s", len, first);
-	    p+=len;left-=len;
-	    /* Get last name */
-	    if (left<sizeof(guint16))
-		goto stopMetaUser;
-	    len = pletohs(p);
-	    p+=sizeof(guint16);left-=sizeof(guint16);
-	    if ((len<=0) || (left<len))
-		goto stopMetaUser;
-	    last = g_malloc(len);
-	    strncpy(last, p, len);
-	    proto_tree_add_text(sstree,
-				offset + size - left,
-				sizeof(guint16)+len,
-				"Last(%d): %s", len, last);
-	    p+=len;left-=len;
-	    /* Get email address */
-	    if (left<sizeof(guint16))
-		goto stopMetaUser;
-	    len = pletohs(p);
-	    p+=sizeof(guint16);left-=sizeof(guint16);
-	    if ((len<=0) || (left<len))
-		goto stopMetaUser;
-	    email = g_malloc(len);
-	    strncpy(email, p, len);
-	    proto_tree_add_text(sstree,
-				offset + size - left,
-				sizeof(guint16)+len,
-				"Email(%d): %s", len, email);
-	    p+=len;left-=len;
+
+	    for ( ; *d!=NULL; d++) {
+		len = proto_add_icq_attr(sstree,
+					 p,
+					 offset + size - left,
+					 left,
+					 *d);
+		if (len == -1)
+		    return;
+		p += len; left -= len;
+	    }
 	    /* Get the authorize setting */
 	    if (left<sizeof(unsigned char))
-		goto stopMetaUser;
+		break;
 	    auth = *p;
+	    proto_tree_add_text(sstree,
+				offset + size - left,
+				sizeof(guint16),
+				"authorization: %s", (auth==0x01)?"Neccessary":"Who needs it");
 	    p++; left--;
 	    /* Get x2 */
 	    if (left<sizeof(guint16))
-		goto stopMetaUser;
+		break;
 	    x2 = pletohs(p);
 	    proto_tree_add_text(sstree,
 				offset + size - left,
@@ -1590,34 +1669,19 @@
 	    p+=sizeof(guint16);left-=sizeof(guint16);
 	    /* Get x3 */
 	    if (left<sizeof(guint32))
-		goto stopMetaUser;
+		break;
 	    x3 = pletohl(p);
 	    proto_tree_add_text(sstree,
 				offset + size - left,
 				sizeof(guint32),
 				"x3: %08x", x3);
 	    p+=sizeof(guint32);left-=sizeof(guint32);
-
-	stopMetaUser:
-	    if (nick!=NULL)
-		g_free(nick);
-	    if (first!=NULL)
-		g_free(first);
-	    if (last!=NULL)
-		g_free(last);
-	    if (email!=NULL)
-		g_free(last);
 	    break;
 	}
 	case META_ABOUT:
 	{
 	    int len;
 	    char* about = NULL;
-	    proto_tree* sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
-
-	    /* Skip over META_USER header */
-	    left -= 3;
-	    p += 3;
 	    /* Get the about information */
 	    if (left<sizeof(guint16))
 		break;
@@ -1664,11 +1728,6 @@
 	    unsigned char user_timezone = -1;
 	    unsigned char auth = -1;
 	    int len = 0;
-	    proto_tree* sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
-
-	    /* Skip over META_USER header */
-	    left -= 3;
-	    p += 3;
 #if 0
 	    /* Get the uin */
 	    if (left<sizeof(guint32))
@@ -1819,6 +1878,96 @@
     }
 }
 
+static void
+icqv5_srv_rand_user(proto_tree* tree,      /* Tree to put the data in */
+		       const u_char* pd,      /* Packet content */
+		       int offset,            /* Offset from the start of the packet to the content */
+		       int size)              /* Number of chars left to do */
+{
+    proto_tree* subtree = NULL;
+    proto_item* ti = NULL;
+    guint32 uin = -1;
+    const unsigned char* IP = NULL;
+    guint32 port = -1;
+    const unsigned char* realIP = NULL;
+    unsigned char commClass = -1;
+    guint32 status;
+    guint16 tcpVer;
+    int left = size;
+    
+    if (tree) {
+	ti = proto_tree_add_item_format(tree,
+					hf_icq_cmd,
+					offset,
+					SRV_RAND_USER_TCP_VER + 2,
+					SRV_RAND_USER,
+					"Body");
+	subtree = proto_item_add_subtree(ti, ett_icq_body);
+	/* guint32 UIN */
+	if (left<sizeof(guint32))
+	    return;
+	uin = pletohl(pd + SRV_RAND_USER_UIN);
+	proto_tree_add_text(subtree,
+			    offset + SRV_RAND_USER_UIN,
+			    sizeof(guint32),
+			    "UIN: %ld", uin);
+	left -= sizeof(guint32);
+	/* guint32 IP */
+	if (left<sizeof(guint32))
+	    return;
+	IP = pd + SRV_RAND_USER_IP;
+	proto_tree_add_text(subtree,
+			    offset + SRV_RAND_USER_IP,
+			    sizeof(guint32),
+			    "IP: %s", ip_to_str(IP));
+	left -= sizeof(guint32);
+	/* guint32 portNum */
+	if (left<sizeof(guint32))
+	    return;
+	port = pletohs(pd + SRV_RAND_USER_PORT);
+	proto_tree_add_text(subtree,
+			    offset + SRV_RAND_USER_UIN,
+			    sizeof(guint32),
+			    "Port: %ld", port);
+	left -= sizeof(guint32);
+	/* guint32 realIP */			    
+	if (left<sizeof(guint32))
+	    return;
+	realIP = pd + SRV_RAND_USER_REAL_IP;
+	proto_tree_add_text(subtree,
+			    offset + SRV_RAND_USER_REAL_IP,
+			    sizeof(guint32),
+			    "RealIP: %s", ip_to_str(realIP));
+	left -= sizeof(guint32);
+	/* guit16 Communication Class */
+	if (left<sizeof(unsigned char))
+	    return;
+	commClass = pd[SRV_RAND_USER_CLASS];
+	proto_tree_add_text(subtree,
+			    offset + SRV_RAND_USER_CLASS,
+			    sizeof(unsigned char),
+			    "Class: %s", (commClass!=4)?"User to User":"Through Server");
+	left -= sizeof(unsigned char);
+	/* guint32 status */
+	if (left<sizeof(guint32))
+	    return;
+	status = pletohs(pd + SRV_RAND_USER_STATUS);
+	proto_tree_add_text(subtree,
+			    offset + SRV_RAND_USER_STATUS,
+			    sizeof(guint32),
+			    "Status: (%ld) %s", status, findStatus(status));
+	/* guint16 tcpVersion */
+	if (left<sizeof(guint16))
+	    return;
+	tcpVer = pletohs(pd + SRV_RAND_USER_TCP_VER);
+	proto_tree_add_text(subtree,
+			    offset + SRV_RAND_USER_TCP_VER,
+			    sizeof(guint16),
+			    "TCPVersion: %d", tcpVer);
+	left -= sizeof(guint16);
+    }
+}
+
 /*
  * Dissect all the v5 client traffic. This is encrypted, so be careful.
  */
@@ -1916,10 +2065,12 @@
 			  pktsize - ICQ5_CL_HDRSIZE);
 	    break;
 	case CMD_SEND_MSG:
+	case CMD_MSG_TO_NEW_USER:
 	    icqv5_cmd_send_msg(icq_tree,
 			       decr_pd + ICQ5_CL_HDRSIZE,
 			       offset + ICQ5_CL_HDRSIZE,
-			       pktsize - ICQ5_CL_HDRSIZE);
+			       pktsize - ICQ5_CL_HDRSIZE,
+			       cmd);
 	    break;
 	case CMD_RAND_SEARCH:
 	    icqv5_cmd_rand_search(icq_tree,
@@ -2086,6 +2237,21 @@
 				   "Checkcode: 0x%08x",
 				   checkcode);
 	switch (cmd) {
+	case SRV_RAND_USER:
+	    icqv5_srv_rand_user(icq_tree,
+			       decr_pd + ICQ5_SRV_HDRSIZE,
+			       offset + ICQ5_SRV_HDRSIZE,
+			       pktsize - ICQ5_SRV_HDRSIZE);
+	    break;
+	case SRV_SYS_DELIVERED_MESS:
+	    /* The message structures are all the same. Why not run
+	     * the same routine? */
+	    icqv5_cmd_send_msg(icq_tree,
+			       decr_pd + ICQ5_SRV_HDRSIZE,
+			       offset + ICQ5_SRV_HDRSIZE,
+			       pktsize - ICQ5_SRV_HDRSIZE,
+			       cmd);
+	    break;
 	case SRV_USER_ONLINE:
 	    icqv5_srv_user_online(icq_tree,
 			       decr_pd + ICQ5_SRV_HDRSIZE,