2 * Routines for SMTP packet disassembly
4 * $Id: packet-smtp.c,v 1.7 2000/10/21 05:52:23 guy Exp $
6 * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
8 * Ethereal - Network traffic analyzer
10 * Copyright 1999 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
45 #include "conversation.h"
50 #define TCP_PORT_SMTP 25
52 void proto_reg_handoff_smtp(void);
54 static int proto_smtp = -1;
56 static int hf_smtp_req = -1;
57 static int hf_smtp_rsp = -1;
59 static int ett_smtp = -1;
61 static int global_smtp_tcp_port = TCP_PORT_SMTP;
64 * A CMD is an SMTP command, MESSAGE is the message portion, and EOM is the
65 * last part of a message
68 #define SMTP_PDU_CMD 0
69 #define SMTP_PDU_MESSAGE 1
70 #define SMTP_PDU_EOM 2
72 struct smtp_proto_data {
76 static int smtp_packet_init_count = 100;
78 struct smtp_request_key {
82 struct smtp_request_val {
83 guint16 processed; /* Have we processed this conversation? */
84 guint16 data_seen; /* Have we seen the data packet */
85 guint16 eom_seen; /* Have we seen the end of message */
86 guint16 crlf_seen; /* Have we seen a CRLF on the end of a packet */
89 GHashTable *smtp_request_hash = NULL;
90 GMemChunk *smtp_request_keys = NULL;
91 GMemChunk *smtp_request_vals = NULL;
92 GMemChunk *smtp_packet_infos = NULL;
96 smtp_equal(gconstpointer v, gconstpointer w)
98 struct smtp_request_key *v1 = (struct smtp_request_key *)v;
99 struct smtp_request_key *v2 = (struct smtp_request_key *)w;
101 #if defined(DEBUG_SMTP_HASH)
102 printf("Comparing %08X\n and %08X\n",
103 v1->conversation, v2->conversation);
106 if (v1->conversation == v2->conversation)
114 smtp_hash(gconstpointer v)
116 struct smtp_request_key *key = (struct smtp_request_key *)v;
119 val = key->conversation;
121 #if defined(DEBUG_SMTP_HASH)
122 printf("SMTP Hash calculated as %u\n", val);
130 smtp_init_protocol(void)
132 #if defined(DEBUG_SMTP_HASH)
133 printf("Initializing SMTP hashtable area\n");
136 if (smtp_request_hash)
137 g_hash_table_destroy(smtp_request_hash);
138 if (smtp_request_keys)
139 g_mem_chunk_destroy(smtp_request_keys);
140 if (smtp_request_vals)
141 g_mem_chunk_destroy(smtp_request_vals);
142 if (smtp_packet_infos)
143 g_mem_chunk_destroy(smtp_packet_infos);
145 smtp_request_hash = g_hash_table_new(smtp_hash, smtp_equal);
146 smtp_request_keys = g_mem_chunk_new("smtp_request_keys",
147 sizeof(struct smtp_request_key),
148 smtp_packet_init_count * sizeof(struct smtp_request_key), G_ALLOC_AND_FREE);
149 smtp_request_vals = g_mem_chunk_new("smtp_request_vals",
150 sizeof(struct smtp_request_val),
151 smtp_packet_init_count * sizeof(struct smtp_request_val), G_ALLOC_AND_FREE);
152 smtp_packet_infos = g_mem_chunk_new("smtp_packet_infos",
153 sizeof(struct smtp_proto_data),
154 smtp_packet_init_count * sizeof(struct smtp_proto_data), G_ALLOC_AND_FREE);
159 int find_smtp_resp_end(const u_char *pd, int offset)
163 /* Look for the CRLF ... but keep in mind the END_OF_FRAME */
165 while (END_OF_FRAME >= cntr) {
167 if (pd[offset + cntr] == 0x0A) { /* Found it */
169 if (END_OF_FRAME >= cntr + 1) cntr++;
185 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
189 dissect_smtp(const u_char *pd, int offset, frame_data *fd,
192 /* tvbuff_t *tvb = tvb_create_from_top(offset);*/
193 packet_info *pinfo = π
195 struct smtp_proto_data *frame_data;
196 proto_tree *smtp_tree, *ti;
198 const u_char *cmd = NULL;
199 conversation_t *conversation;
200 struct smtp_request_key request_key, *new_request_key;
201 struct smtp_request_val *request_val;
204 CHECK_DISPLAY_AS_DATA(proto_smtp, tvb, pinfo, tree);
206 OLD_CHECK_DISPLAY_AS_DATA(proto_smtp, pd, offset, fd, tree);
209 /* If we have per frame data, use that, else, we must be on the first
210 * pass, so we figure it out on the first pass.
212 * Since we can't stash info away in a conversation (as they are
213 * removed during a filter operation, and we can't rely on the visited
214 * flag, as that is set to 0 during a filter, we must save per-frame
215 * data for each frame. However, we only need it for requests. Responses
216 * are easy to manage.
219 /* Find out what conversation this packet is part of ... but only
220 * if we have no information on this packet, so find the per-frame
224 /* SMTP messages have a simple format ... */
226 request = pinfo -> destport == TCP_PORT_SMTP;
227 cmd = pd + offset; /* FIXME: What about tvb */
229 frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
233 conversation = find_conversation(&pinfo->src, &pinfo->dst, pi.ptype,
234 pinfo->srcport, pinfo->destport, 0);
235 if (conversation == NULL) { /* No conversation, create one */
236 conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
237 pinfo->srcport, pinfo->destport, NULL,
243 * Check for and insert an entry in the request table if does not exist
245 request_key.conversation = conversation->index;
247 request_val = (struct smtp_request_val *)g_hash_table_lookup(smtp_request_hash, &request_key);
249 if (!request_val) { /* Create one */
251 new_request_key = g_mem_chunk_alloc(smtp_request_keys);
252 new_request_key->conversation = conversation->index;
254 request_val = g_mem_chunk_alloc(smtp_request_vals);
255 request_val->processed = 0;
256 request_val->data_seen = 0;
257 request_val->eom_seen = 0;
258 request_val->crlf_seen = 0;
260 g_hash_table_insert(smtp_request_hash, new_request_key, request_val);
265 * Check whether or not this packet is an end of message packet
266 * We should look for CRLF.CRLF and they may be split.
267 * We have to keep in mind that we may see what we want on
268 * two passes through here ...
271 if (request_val->data_seen && !request_val->processed) {
274 * The order of these is important ... We want to avoid
275 * cases where there is a CRLF at the end of a packet and a
276 * .CRLF at the begining of the same packet.
279 if ((request_val->crlf_seen && strncmp(pd + offset, ".\r\n", 3) == 0) ||
280 (strncmp(pd + offset, "\r\n.\r\n", 5) == 0)) {
282 request_val->eom_seen = 1;
286 if (strncmp(pd + offset + END_OF_FRAME - 2, "\r\n", 2) == 0) {
288 request_val->crlf_seen = 1;
293 request_val->crlf_seen = 0;
299 * OK, Check if we have seen a DATA request. We do it here for
300 * simplicity, but we have to be careful below.
305 frame_data = g_mem_chunk_alloc(smtp_packet_infos);
307 if (!request_val->processed) {
308 if (strncmp(pd + offset, "DATA", 4)==0) {
310 request_val->data_seen = 1;
311 frame_data->pdu_type = SMTP_PDU_CMD;
312 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
314 } else if ((!request_val->eom_seen) &&
315 (request_val->data_seen)) {
317 /* Now, create the frame data for this frame ... */
319 frame_data->pdu_type = SMTP_PDU_MESSAGE;
320 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
322 } else if (request_val->eom_seen) { /* Seen the EOM */
324 /* Now, we clear the eom_seen and data_seen bits */
326 request_val->eom_seen = request_val->data_seen = 0;
327 request_val->processed = 1; /* We have seen all the packets */
329 /* And add the packet data */
331 frame_data->pdu_type = SMTP_PDU_EOM;
332 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
336 frame_data->pdu_type = SMTP_PDU_CMD;
337 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
345 * From here, we simply add items to the tree and info to the info
349 if (check_col(fd, COL_PROTOCOL))
350 col_add_str(fd, COL_PROTOCOL, "SMTP");
352 if (check_col(fd, COL_INFO)) { /* Add the appropriate type here */
355 * If it is a request, we have to look things up, otherwise, just
356 * display the right things
361 /* We must have frame_data here ... */
363 switch (frame_data->pdu_type) {
364 case SMTP_PDU_MESSAGE:
366 col_add_fstr(pinfo->fd, COL_INFO, "Message: %s", format_text(cmd, END_OF_FRAME));
371 col_add_fstr(pinfo->fd, COL_INFO, "EOM: %s", format_text(cmd, END_OF_FRAME));
376 col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
383 col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
388 if (tree) { /* Build the tree info ... */
390 ti = proto_tree_add_item(tree, proto_smtp, NullTVB, offset, END_OF_FRAME, FALSE);
391 smtp_tree = proto_item_add_subtree(ti, ett_smtp);
392 proto_tree_add_boolean_hidden(smtp_tree, (request ? hf_smtp_req : hf_smtp_rsp),
393 NullTVB, offset, 4, TRUE);
397 * Check out whether or not we can see a command in there ...
398 * What we are looking for is not data_seen and the word DATA
401 * We will see DATA and request_val->data_seen when we process the
402 * tree view after we have seen a DATA packet when processing
403 * the packet list pane.
405 * On the first pass, we will not have any info on the packets
406 * On second and subsequent passes, we will.
409 switch (frame_data->pdu_type) {
411 case SMTP_PDU_MESSAGE:
413 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "Message: %s", format_text(cmd, END_OF_FRAME));
419 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "EOM: %s", format_text(cmd, END_OF_FRAME));
424 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 4, "Command: %s", format_text(cmd, 4));
425 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));
432 /* Must consider a multi-line response here ... */
434 while (END_OF_FRAME >= 4 && pd[offset + 3] == '-') {
435 int resp_len = find_smtp_resp_end(pd, offset);
437 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 3, "Response: %s", format_text(pd + offset, 3));
438 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset + 4, resp_len, "Parameter: %s", format_text(pd + offset + 4, resp_len - 4));
443 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 3, "Response: %s", format_text(pd + offset, 3));
444 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));
450 /* Register all the bits needed by the filtering engine */
453 proto_register_smtp(void)
455 static hf_register_info hf[] = {
457 { "Request", "smtp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
460 { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
462 static gint *ett[] = {
465 /*module_t *smtp_module = NULL; */ /* Not yet used */
467 /* No Configuration options to register? */
469 proto_smtp = proto_register_protocol("Simple Mail Transfer Protocol", "smtp");
471 proto_register_field_array(proto_smtp, hf, array_length(hf));
472 proto_register_subtree_array(ett, array_length(ett));
473 register_init_routine(&smtp_init_protocol);
477 /* The registration hand-off routine */
479 proto_reg_handoff_smtp(void)
481 static int smtp_prefs_initialized = FALSE;
482 static int tcp_port = 0;
484 if (smtp_prefs_initialized) {
486 old_dissector_delete("tcp.port", tcp_port, dissect_smtp);
491 smtp_prefs_initialized = TRUE;
495 tcp_port = global_smtp_tcp_port;
497 old_dissector_add("tcp.port", global_smtp_tcp_port, dissect_smtp);