Have the Etherenet and PPP dissectors register themselves, and have
[obnox/wireshark/wip.git] / packet-smtp.c
index 7e2c0152e67c6d8cc6694b614da288972622f9b6..45a5f6d87ae1ce3be47f25d86245ddf6b5654229 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-smtp.c
  * Routines for SMTP packet disassembly
  *
- * $Id: packet-smtp.c,v 1.6 2000/09/11 16:16:07 gram Exp $
+ * $Id: packet-smtp.c,v 1.11 2000/11/13 08:58:13 guy Exp $
  *
  * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
  *
@@ -79,10 +79,11 @@ struct smtp_request_key {
   guint32 conversation;
 };
 
+/*
+ * State information stored with a conversation.
+ */
 struct smtp_request_val {
-  guint16 processed;     /* Have we processed this conversation? */
-  guint16 data_seen;     /* Have we seen the data packet */
-  guint16 eom_seen;      /* Have we seen the end of message */
+  gboolean reading_data; /* Reading message data, not commands */
   guint16 crlf_seen;     /* Have we seen a CRLF on the end of a packet */
 };
 
@@ -155,65 +156,45 @@ smtp_init_protocol(void)
 
 }
 
-static
-int find_smtp_resp_end(const u_char *pd, int offset)
-{
-  int cntr = 0;
-
-  /* Look for the CRLF ... but keep in mind the END_OF_FRAME */
-
-  while (END_OF_FRAME >= cntr) {
-
-    if (pd[offset + cntr] == 0x0A) { /* Found it */
-
-      if (END_OF_FRAME >= cntr + 1) cntr++;
-
-      return cntr;
-
-    }
-
-    cntr++;
-
-  }
-
-  return cntr;
-
-}
-
-#if 0
 static void
 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-#else
-static void
-dissect_smtp(const u_char *pd, int offset, frame_data *fd,
-            proto_tree *tree)
-{
-  /*    tvbuff_t *tvb = tvb_create_from_top(offset);*/
-    packet_info *pinfo = &pi;
-#endif
     struct smtp_proto_data  *frame_data;
-    proto_tree              *smtp_tree, *ti;
+    proto_tree              *smtp_tree;
+    proto_item              *ti;
+    int                     offset = 0;
     int                     request = 0;
-    const u_char            *cmd = NULL;
     conversation_t          *conversation;
     struct smtp_request_key request_key, *new_request_key;
     struct smtp_request_val *request_val;
+    char                    *line;
+    int                     linelen;
+    gboolean                eom_seen = FALSE;
+    gint                    next_offset;
+    gboolean                is_continuation_line;
+    int                     cmdlen;
 
-#if 0
     CHECK_DISPLAY_AS_DATA(proto_smtp, tvb, pinfo, tree);
-#else
-    OLD_CHECK_DISPLAY_AS_DATA(proto_smtp, pd, offset, fd, tree);
-#endif
 
-    /* If we have per frame data, use that, else, we must be on the first 
-     * pass, so we figure it out on the first pass.
-     * 
-     * Since we can't stash info away in a conversation (as they are 
-     * removed during a filter operation, and we can't rely on the visited
-     * flag, as that is set to 0 during a filter, we must save per-frame
-     * data for each frame. However, we only need it for requests. Responses
+    pinfo->current_proto = "SMTP";
+
+    /* As there is no guarantee that we will only see frames in the
+     * the SMTP conversation once, and that we will see them in
+     * order - in Ethereal, the user could randomly click on frames
+     * in the conversation in any order in which they choose - we
+     * have to store information with each frame indicating whether
+     * it contains commands or data or an EOM indication.
+     *
+     * XXX - what about frames that contain *both*?  TCP is a
+     * byte-stream protocol, and there are no guarantees that
+     * TCP segment boundaries will correspond to SMTP commands
+     * or EOM indications.
+     *
+     * We only need that for the client->server stream; responses
      * are easy to manage.
+     *
+     * If we have per frame data, use that, else, we must be on the first 
+     * pass, so we figure it out on the first pass.
      */
 
     /* Find out what conversation this packet is part of ... but only
@@ -224,17 +205,23 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
     /* SMTP messages have a simple format ... */
 
     request = pinfo -> destport == TCP_PORT_SMTP;
-    cmd = pd + offset;   /* FIXME: What about tvb */
+
+    /*
+     * Get the first line from the buffer.
+     */
+    linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
+    line = tvb_get_ptr(tvb, offset, linelen);
 
     frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
 
     if (!frame_data) {
 
       conversation = find_conversation(&pinfo->src, &pinfo->dst, pi.ptype,
-                                      pinfo->srcport, pinfo->destport);
+                                      pinfo->srcport, pinfo->destport, 0);
       if (conversation == NULL) { /* No conversation, create one */
        conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
-                                       pinfo->srcport, pinfo->destport, NULL);
+                                       pinfo->srcport, pinfo->destport, NULL,
+                                       0);
 
       }
 
@@ -251,9 +238,7 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
        new_request_key->conversation = conversation->index;
 
        request_val = g_mem_chunk_alloc(smtp_request_vals);
-       request_val->processed = 0;
-       request_val->data_seen = 0;
-       request_val->eom_seen = 0;
+       request_val->reading_data = FALSE;
        request_val->crlf_seen = 0;
 
        g_hash_table_insert(smtp_request_hash, new_request_key, request_val);
@@ -267,7 +252,7 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
        * two passes through here ...
        */
 
-      if (request_val->data_seen && !request_val->processed) {
+      if (request_val->reading_data) {
 
        /*
         * The order of these is important ... We want to avoid
@@ -275,14 +260,14 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
         * .CRLF at the begining of the same packet.
         */
 
-       if ((request_val->crlf_seen && strncmp(pd + offset, ".\r\n", 3) == 0) ||
-           (strncmp(pd + offset, "\r\n.\r\n", 5) == 0)) {
+       if ((request_val->crlf_seen && tvb_strneql(tvb, offset, ".\r\n", 3) == 0) ||
+           tvb_strneql(tvb, offset, "\r\n.\r\n", 5) == 0) {
 
-         request_val->eom_seen = 1;
+         eom_seen = TRUE;
 
        }
 
-       if (strncmp(pd + offset + END_OF_FRAME - 2, "\r\n", 2) == 0) {
+       if (tvb_strneql(tvb, offset + tvb_length_remaining(tvb, offset) - 2, "\r\n", 2) == 0) {
 
          request_val->crlf_seen = 1;
 
@@ -303,40 +288,73 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
 
        frame_data = g_mem_chunk_alloc(smtp_packet_infos);
 
-       if (!request_val->processed) { 
-         if (strncmp(pd + offset, "DATA", 4)==0) {
-
-         request_val->data_seen = 1;
-         frame_data->pdu_type = SMTP_PDU_CMD;
-         p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
-
-         } else if ((!request_val->eom_seen) &&
-                    (request_val->data_seen)) {
-        
-           /* Now, create the frame data for this frame ... */
-
+       if (request_val->reading_data) {
+         /*
+          * This is message data.
+          */
+         if (eom_seen) { /* Seen the EOM */
+           /*
+            * EOM.
+            * Everything that comes after it is commands.
+            *
+            * XXX - what if the EOM isn't at the beginning of
+            * the TCP segment?  It can occur anywhere....
+            */
+           frame_data->pdu_type = SMTP_PDU_EOM;
+           request_val->reading_data = FALSE;
+         } else {
+           /*
+            * Message data with no EOM.
+            */
            frame_data->pdu_type = SMTP_PDU_MESSAGE;
-           p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
-
-         } else if (request_val->eom_seen) { /* Seen the EOM */
-
-           /* Now, we clear the eom_seen and data_seen bits */
+         }
+       } else {
+         /*
+          * This is commands - unless the capture started in the
+          * middle of a session, and we're in the middle of data.
+          * To quote RFC 821, "Command codes are four alphabetic
+          * characters"; if we don't see four alphabetic characters
+          * and, if there's anything else in the line, a space, we
+          * assume it's not a command.
+          * (We treat only A-Z and a-z as alphabetic.)
+          */
+#define        ISALPHA(c)      (((c) >= 'A' && (c) <= 'Z') || \
+                        ((c) >= 'a' && (c) <= 'z'))
+         if (linelen >= 4 && ISALPHA(line[0]) && ISALPHA(line[1]) &&
+             ISALPHA(line[2]) && ISALPHA(line[3]) &&
+             (linelen == 4 || line[4] == ' ')) {
+           if (strncasecmp(line, "DATA", 4) == 0) {
+
+             /*
+              * DATA command.
+              * This is a command, but everything that comes after it,
+              * until an EOM, is data.
+              */
+             frame_data->pdu_type = SMTP_PDU_CMD;
+             request_val->reading_data = TRUE;
+
+           } else {
+
+             /*
+              * Regular command.
+              */
+             frame_data->pdu_type = SMTP_PDU_CMD;
+
+           }
+         } else {
 
-           request_val->eom_seen = request_val->data_seen = 0;
-           request_val->processed = 1;   /* We have seen all the packets */
+           /*
+            * Assume it's message data.
+            */
 
-           /* And add the packet data */
+           frame_data->pdu_type = SMTP_PDU_MESSAGE;
 
-           frame_data->pdu_type = SMTP_PDU_EOM;
-           p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
+         }
 
-         } else {
+       }
 
-           frame_data->pdu_type = SMTP_PDU_CMD;
-           p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
+       p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
 
-         }
-       }
       }
     }
 
@@ -345,10 +363,10 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
      * fields ...
      */
 
-    if (check_col(fd, COL_PROTOCOL))
-      col_add_str(fd, COL_PROTOCOL, "SMTP");
+    if (check_col(pinfo->fd, COL_PROTOCOL))
+      col_add_str(pinfo->fd, COL_PROTOCOL, "SMTP");
 
-    if (check_col(fd, COL_INFO)) {  /* Add the appropriate type here */
+    if (check_col(pinfo->fd, COL_INFO)) {  /* Add the appropriate type here */
 
       /*
        * If it is a request, we have to look things up, otherwise, just
@@ -362,34 +380,38 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
        switch (frame_data->pdu_type) {
        case SMTP_PDU_MESSAGE:
 
-         col_add_fstr(pinfo->fd, COL_INFO, "Message: %s", format_text(cmd, END_OF_FRAME));
+         col_add_str(pinfo->fd, COL_INFO, "Message Body");
          break;
 
        case SMTP_PDU_EOM:
 
-         col_add_fstr(pinfo->fd, COL_INFO, "EOM: %s", format_text(cmd, END_OF_FRAME));
+         col_add_fstr(pinfo->fd, COL_INFO, "EOM: %s",
+             format_text(line, linelen));
          break;
 
        case SMTP_PDU_CMD:
 
-         col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
+         col_add_fstr(pinfo->fd, COL_INFO, "Command: %s",
+             format_text(line, linelen));
+         break;
 
        }
 
       }
       else {
 
-       col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
+       col_add_fstr(pinfo->fd, COL_INFO, "Response: %s",
+           format_text(line, linelen));
 
       }
     }
 
     if (tree) { /* Build the tree info ... */
 
-      ti = proto_tree_add_item(tree, proto_smtp, NullTVB, offset, END_OF_FRAME, FALSE);
+      ti = proto_tree_add_item(tree, proto_smtp, tvb, offset, END_OF_FRAME, FALSE);
       smtp_tree = proto_item_add_subtree(ti, ett_smtp);
       proto_tree_add_boolean_hidden(smtp_tree, (request ? hf_smtp_req : hf_smtp_rsp),
-                                   NullTVB, offset, 4, TRUE);
+                                   tvb, offset, 4, TRUE);
       if (request) {
 
        /* 
@@ -409,38 +431,114 @@ dissect_smtp(const u_char *pd, int offset, frame_data *fd,
 
        case SMTP_PDU_MESSAGE:
 
-         proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "Message: %s", format_text(cmd, END_OF_FRAME));
+         /*
+          * Message body.
+          * Put its lines into the protocol tree, a line at a time.
+          */
+         while (tvb_offset_exists(tvb, offset)) {
+
+           /*
+            * Find the end of the line.
+            */
+           tvb_find_line_end(tvb, offset, -1, &next_offset);
+
+           /*
+            * Put this line.
+            */
+           proto_tree_add_text(smtp_tree, tvb, offset, next_offset - offset,
+               "Message: %s",
+               tvb_format_text(tvb, offset, next_offset - offset));
+
+           /*
+            * Step to the next line.
+            */
+           offset = next_offset;
+
+         }
 
          break;
 
        case SMTP_PDU_EOM:
 
-         proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "EOM: %s", format_text(cmd, END_OF_FRAME));
+         /*
+          * End-of-message-body indicator.
+          *
+          * XXX - what about stuff after the first line?
+          * Unlikely, as the client should wait for a response to the
+          * DATA command this terminates before sending another
+          * request, but we should probably handle it.
+          */
+         proto_tree_add_text(smtp_tree, tvb, offset, linelen,
+             "EOM: %s", format_text(line, linelen));
 
          break;
 
        case SMTP_PDU_CMD:
-         proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 4, "Command: %s", format_text(cmd, 4));
-         proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset + 5, END_OF_FRAME, "Parameter: %s", format_text(cmd + 5, END_OF_FRAME - 5));
+
+         /*
+          * Command.
+          *
+          * XXX - what about stuff after the first line?
+          * Unlikely, as the client should wait for a response to the
+          * previous command before sending another request, but we
+          * should probably handle it.
+          */
+         if (linelen >= 4)
+           cmdlen = 4;
+         else
+           cmdlen = linelen;
+         proto_tree_add_text(smtp_tree, tvb, offset, cmdlen,
+             "Command: %s", format_text(line, cmdlen));
+         if (linelen > 5) {
+           proto_tree_add_text(smtp_tree, tvb, offset + 5, linelen - 5,
+               "Parameter: %s", format_text(line + 5, linelen - 5));
+         }
 
        }
 
       }
       else {
 
-       /* Must consider a multi-line response here ... */
+        /*
+        * Process the response, a line at a time, until we hit a line
+        * that doesn't have a continuation indication on it.
+        */
+
+       while (tvb_offset_exists(tvb, offset)) {
+
+         /*
+          * Find the end of the line.
+          */
+         linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
+
+         /*
+          * Is it a continuation line?
+          */
+         is_continuation_line =
+             (linelen >= 4 && tvb_get_guint8(tvb, offset + 3) == '-');
+
+         /*
+          * Put it into the protocol tree.
+          */
+         proto_tree_add_text(smtp_tree, tvb, offset, 3,
+             "Response: %s", tvb_format_text(tvb, offset, 3));
+         if (linelen >= 4) {
+           proto_tree_add_text(smtp_tree, tvb, offset + 4, linelen - 4,
+               "Parameter: %s", tvb_format_text(tvb, offset + 4, linelen - 4));
+         }
 
-       while (END_OF_FRAME >= 4 && pd[offset + 3] == '-') {
-         int resp_len = find_smtp_resp_end(pd, offset);
+         /*
+          * Step past this line.
+          */
+         offset = next_offset;
 
-         proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 3, "Response: %s", format_text(pd + offset, 3));
-         proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset + 4, resp_len, "Parameter: %s", format_text(pd + offset + 4, resp_len - 4));
+         /*
+          * If it's not a continuation line, quit.
+          */
+         if (!is_continuation_line)
+           break;
 
-         offset += resp_len;
        }
-
-       proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 3, "Response: %s", format_text(pd + offset, 3));
-       proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset + 4, END_OF_FRAME, "Parameter: %s", format_text(pd + offset + 4, END_OF_FRAME - 4));
        
       }
     }
@@ -482,7 +580,7 @@ proto_reg_handoff_smtp(void)
 
   if (smtp_prefs_initialized) {
 
-    old_dissector_delete("tcp.port", tcp_port, dissect_smtp);
+    dissector_delete("tcp.port", tcp_port, dissect_smtp);
 
   }
   else {
@@ -493,6 +591,6 @@ proto_reg_handoff_smtp(void)
 
   tcp_port = global_smtp_tcp_port;
 
-  old_dissector_add("tcp.port", global_smtp_tcp_port, dissect_smtp);
+  dissector_add("tcp.port", global_smtp_tcp_port, dissect_smtp);
 
 }