Removed trailing whitespaces from .h and .c files using the
[obnox/wireshark/wip.git] / packet-smtp.c
index cd5740b8ef6de65f482aed53a4f14ce727b24b06..5a1da4476ae015f712d85b8fd748f456e670e274 100644 (file)
@@ -1,13 +1,14 @@
 /* packet-smtp.c
  * Routines for SMTP packet disassembly
  *
- * $Id: packet-smtp.c,v 1.18 2001/06/18 02:17:52 guy Exp $
+ * $Id: packet-smtp.c,v 1.31 2002/08/02 23:36:02 jmayer Exp $
  *
  * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1999 Gerald Combs
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
 #include "config.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <time.h>
 #include <glib.h>
 #include <string.h>
-#include "packet.h"
-#include "conversation.h"
-#include "resolv.h"
+#include <epan/packet.h>
+#include <epan/conversation.h>
+#include <epan/resolv.h>
 #include "prefs.h"
-#include "strutil.h"
+#include <epan/strutil.h>
 
 #define TCP_PORT_SMTP 25
 
@@ -55,11 +48,18 @@ static int proto_smtp = -1;
 
 static int hf_smtp_req = -1;
 static int hf_smtp_rsp = -1;
+static int hf_smtp_req_command = -1;
+static int hf_smtp_req_parameter = -1;
+static int hf_smtp_rsp_code = -1;
+static int hf_smtp_rsp_parameter = -1;
 
 static int ett_smtp = -1;
 
 static int global_smtp_tcp_port = TCP_PORT_SMTP;
 
+/* desegmentation of SMTP command and response lines */
+static gboolean smtp_desegment = TRUE;
+
 /*
  * A CMD is an SMTP command, MESSAGE is the message portion, and EOM is the
  * last part of a message
@@ -75,10 +75,6 @@ struct smtp_proto_data {
 
 static int smtp_packet_init_count = 100;
 
-struct smtp_request_key {
-  guint32 conversation;
-};
-
 /*
  * State information stored with a conversation.
  */
@@ -87,66 +83,17 @@ struct smtp_request_val {
   guint16 crlf_seen;     /* Have we seen a CRLF on the end of a packet */
 };
 
-GHashTable *smtp_request_hash = NULL;
-GMemChunk  *smtp_request_keys = NULL;
-GMemChunk  *smtp_request_vals = NULL;
-GMemChunk  *smtp_packet_infos = NULL;
-
-/* Hash Functions */
-gint
-smtp_equal(gconstpointer v, gconstpointer w)
-{
-  struct smtp_request_key *v1 = (struct smtp_request_key *)v;
-  struct smtp_request_key *v2 = (struct smtp_request_key *)w;
-
-#if defined(DEBUG_SMTP_HASH)
-  printf("Comparing %08X\n      and %08X\n",
-        v1->conversation, v2->conversation);
-#endif
-
-  if (v1->conversation == v2->conversation)
-    return 1;
-
-  return 0;
-
-}
-
-static guint
-smtp_hash(gconstpointer v)
-{
-  struct smtp_request_key *key = (struct smtp_request_key *)v;
-  guint val;
-
-  val = key->conversation;
-
-#if defined(DEBUG_SMTP_HASH)
-  printf("SMTP Hash calculated as %u\n", val);
-#endif
-
-  return val;
-
-}
+static GMemChunk  *smtp_request_vals = NULL;
+static GMemChunk  *smtp_packet_infos = NULL;
 
 static void
 smtp_init_protocol(void)
 {
-#if defined(DEBUG_SMTP_HASH)
-  printf("Initializing SMTP hashtable area\n");
-#endif
-
-  if (smtp_request_hash)
-    g_hash_table_destroy(smtp_request_hash);
-  if (smtp_request_keys)
-    g_mem_chunk_destroy(smtp_request_keys);
   if (smtp_request_vals)
     g_mem_chunk_destroy(smtp_request_vals);
   if (smtp_packet_infos)
     g_mem_chunk_destroy(smtp_packet_infos);
 
-  smtp_request_hash = g_hash_table_new(smtp_hash, smtp_equal);
-  smtp_request_keys = g_mem_chunk_new("smtp_request_keys",
-                                      sizeof(struct smtp_request_key),
-                                      smtp_packet_init_count * sizeof(struct smtp_request_key), G_ALLOC_AND_FREE);
   smtp_request_vals = g_mem_chunk_new("smtp_request_vals", 
                                      sizeof(struct smtp_request_val),
                                      smtp_packet_init_count * sizeof(struct smtp_request_val), G_ALLOC_AND_FREE);
@@ -165,9 +112,9 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     int                     offset = 0;
     int                     request = 0;
     conversation_t          *conversation;
-    struct smtp_request_key request_key, *new_request_key;
     struct smtp_request_val *request_val;
-    const char              *line;
+    const guchar            *line;
+    guint32                 code;
     int                     linelen;
     gboolean                eom_seen = FALSE;
     gint                    next_offset;
@@ -205,43 +152,55 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     /*
      * Get the first line from the buffer.
      *
-     * Note that "tvb_find_line_end()" will return a value that
-     * is not longer than what's in the buffer, so the
-     * "tvb_get_ptr()" call won't throw an exception.
+     * Note that "tvb_find_line_end()" will, if it doesn't return
+     * -1, return a value that is not longer than what's in the buffer,
+     * and "tvb_find_line_end()" will always return a value that is not
+     * longer than what's in the buffer, so the "tvb_get_ptr()" call
+     * won't throw an exception.
      */
-    linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
+    linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
+      smtp_desegment && pinfo->can_desegment);
+    if (linelen == -1) {
+      /*
+       * We didn't find a line ending, and we're doing desegmentation;
+       * tell the TCP dissector where the data for this message starts
+       * in the data it handed us, and tell it we need one more byte
+       * (we may need more, but we'll try again if what we get next
+       * isn't enough), and return.
+       */
+      pinfo->desegment_offset = offset;
+      pinfo->desegment_len = 1;
+      return;
+    }
     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,
+      conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
                                       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,
-                                       0);
+                                       pinfo->srcport, pinfo->destport, 0);
 
       }
 
-      /* 
-       * Check for and insert an entry in the request table if does not exist
+      /*
+       * Is there a request structure attached to this conversation?
        */
-      request_key.conversation = conversation->index;
-
-      request_val = (struct smtp_request_val *)g_hash_table_lookup(smtp_request_hash, &request_key);
-      
-      if (!request_val) { /* Create one */
+      request_val = conversation_get_proto_data(conversation, proto_smtp);
 
-       new_request_key = g_mem_chunk_alloc(smtp_request_keys);
-       new_request_key->conversation = conversation->index;
+      if (!request_val) {
 
+        /*
+         * No - create one and attach it.
+         */
        request_val = g_mem_chunk_alloc(smtp_request_vals);
        request_val->reading_data = FALSE;
        request_val->crlf_seen = 0;
 
-       g_hash_table_insert(smtp_request_hash, new_request_key, request_val);
+       conversation_add_proto_data(conversation, proto_smtp, request_val);
 
       }
 
@@ -363,10 +322,10 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      * fields ...
      */
 
-    if (check_col(pinfo->fd, COL_PROTOCOL))
-      col_set_str(pinfo->fd, COL_PROTOCOL, "SMTP");
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+      col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMTP");
 
-    if (check_col(pinfo->fd, COL_INFO)) {  /* Add the appropriate type here */
+    if (check_col(pinfo->cinfo, COL_INFO)) {  /* Add the appropriate type here */
 
       /*
        * If it is a request, we have to look things up, otherwise, just
@@ -380,18 +339,18 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        switch (frame_data->pdu_type) {
        case SMTP_PDU_MESSAGE:
 
-         col_set_str(pinfo->fd, COL_INFO, "Message Body");
+         col_set_str(pinfo->cinfo, COL_INFO, "Message Body");
          break;
 
        case SMTP_PDU_EOM:
 
-         col_add_fstr(pinfo->fd, COL_INFO, "EOM: %s",
+         col_add_fstr(pinfo->cinfo, COL_INFO, "EOM: %s",
              format_text(line, linelen));
          break;
 
        case SMTP_PDU_CMD:
 
-         col_add_fstr(pinfo->fd, COL_INFO, "Command: %s",
+         col_add_fstr(pinfo->cinfo, COL_INFO, "Command: %s",
              format_text(line, linelen));
          break;
 
@@ -400,7 +359,7 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
       }
       else {
 
-       col_add_fstr(pinfo->fd, COL_INFO, "Response: %s",
+       col_add_fstr(pinfo->cinfo, COL_INFO, "Response: %s",
            format_text(line, linelen));
 
       }
@@ -408,11 +367,8 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
     if (tree) { /* Build the tree info ... */
 
-      ti = proto_tree_add_item(tree, proto_smtp, tvb, offset,
-       tvb_length_remaining(tvb, offset), FALSE);
+      ti = proto_tree_add_item(tree, proto_smtp, tvb, offset, -1, FALSE);
       smtp_tree = proto_item_add_subtree(ti, ett_smtp);
-      proto_tree_add_boolean_hidden(smtp_tree, (request ? hf_smtp_req : hf_smtp_rsp),
-                                   tvb, offset, 4, TRUE);
       if (request) {
 
        /* 
@@ -441,7 +397,7 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            /*
             * Find the end of the line.
             */
-           tvb_find_line_end(tvb, offset, -1, &next_offset);
+           tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
 
            /*
             * Put this line.
@@ -488,11 +444,13 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            cmdlen = 4;
          else
            cmdlen = linelen;
-         proto_tree_add_text(smtp_tree, tvb, offset, cmdlen,
-             "Command: %s", format_text(line, cmdlen));
+         proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_req, tvb,
+                                       0, 0, TRUE);
+         proto_tree_add_item(smtp_tree, hf_smtp_req_command, tvb,
+                             offset, cmdlen, FALSE);
          if (linelen > 5) {
-           proto_tree_add_text(smtp_tree, tvb, offset + 5, linelen - 5,
-               "Parameter: %s", format_text(line + 5, linelen - 5));
+           proto_tree_add_item(smtp_tree, hf_smtp_req_parameter, tvb,
+                               offset + 5, linelen - 5, FALSE);
          }
 
        }
@@ -504,13 +462,15 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
         * Process the response, a line at a time, until we hit a line
         * that doesn't have a continuation indication on it.
         */
+       proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_rsp, tvb,
+                                       0, 0, TRUE);
 
        while (tvb_offset_exists(tvb, offset)) {
 
          /*
           * Find the end of the line.
           */
-         linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
+         linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
 
          /*
           * Is it a continuation line?
@@ -521,11 +481,27 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
          /*
           * 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));
+         line = tvb_get_ptr(tvb, offset, linelen);
+         if (linelen >= 3 && isdigit(line[0]) && isdigit(line[1])
+                          && isdigit(line[2])) {
+           /*
+            * We have a 3-digit response code.
+            */
+           code = (line[0] - '0')*100 + (line[1] - '0')*10 + (line[2] - '0');
+           proto_tree_add_uint(smtp_tree, hf_smtp_rsp_code, tvb, offset, 3,
+                               code);
+
+           if (linelen >= 4) {
+             proto_tree_add_item(smtp_tree, hf_smtp_rsp_parameter, tvb,
+                                 offset + 4, linelen - 4, FALSE);
+           }
+         } else {
+           /*
+            * No 3-digit response code.
+            */
+           proto_tree_add_text(smtp_tree, tvb, offset, linelen,
+                               "Bogus reply line (no response code): %s",
+                               tvb_format_text(tvb, offset, linelen));
          }
 
          /*
@@ -556,11 +532,27 @@ proto_register_smtp(void)
 
     { &hf_smtp_rsp,
       { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
+
+    { &hf_smtp_req_command,
+      { "Command", "smtp.req.command", FT_STRING,  BASE_NONE, NULL, 0x0,
+       "", HFILL }},
+
+    { &hf_smtp_req_parameter,
+      { "Request parameter", "smtp.req.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
+       "", HFILL }},
+
+    { &hf_smtp_rsp_code,
+      { "Response code", "smtp.response.code", FT_UINT32, BASE_DEC, NULL, 0x0,
+       "", HFILL }},
+
+    { &hf_smtp_rsp_parameter,
+      { "Response parameter", "smtp.rsp.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
+       "", HFILL }}
   };
   static gint *ett[] = {
     &ett_smtp
   };
-  /*module_t *smtp_module = NULL; */  /* Not yet used */
+  module_t *smtp_module;
 
   /* No Configuration options to register? */
 
@@ -571,6 +563,11 @@ proto_register_smtp(void)
   proto_register_subtree_array(ett, array_length(ett));
   register_init_routine(&smtp_init_protocol);
 
+  smtp_module = prefs_register_protocol(proto_smtp, NULL);
+  prefs_register_bool_preference(smtp_module, "desegment_lines",
+    "Desegment all SMTP command and response lines spanning multiple TCP segments",
+    "Whether the SMTP dissector should desegment all command and response lines spanning multiple TCP segments",
+    &smtp_desegment);
 }
 
 /* The registration hand-off routine */
@@ -578,21 +575,24 @@ void
 proto_reg_handoff_smtp(void)
 {
   static int smtp_prefs_initialized = FALSE;
+  static dissector_handle_t smtp_handle;
   static int tcp_port = 0;
 
-  if (smtp_prefs_initialized) {
+  if (!smtp_prefs_initialized) {
 
-    dissector_delete("tcp.port", tcp_port, dissect_smtp);
+    smtp_handle = create_dissector_handle(dissect_smtp, proto_smtp);
+
+    smtp_prefs_initialized = TRUE;
 
   }
   else {
 
-    smtp_prefs_initialized = TRUE;
+    dissector_delete("tcp.port", tcp_port, smtp_handle);
 
   }
 
   tcp_port = global_smtp_tcp_port;
 
-  dissector_add("tcp.port", global_smtp_tcp_port, dissect_smtp, proto_smtp);
+  dissector_add("tcp.port", global_smtp_tcp_port, smtp_handle);
 
 }