2 * Routines for SMTP packet disassembly
4 * $Id: packet-smtp.c,v 1.5 2000/08/26 11:25:28 sharpe 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"
49 #define TCP_PORT_SMTP 25
51 void proto_reg_handoff_smtp(void);
53 static int proto_smtp = -1;
55 static int hf_smtp_req = -1;
56 static int hf_smtp_rsp = -1;
58 static int ett_smtp = -1;
60 static int global_smtp_tcp_port = TCP_PORT_SMTP;
63 * A CMD is an SMTP command, MESSAGE is the message portion, and EOM is the
64 * last part of a message
67 #define SMTP_PDU_CMD 0
68 #define SMTP_PDU_MESSAGE 1
69 #define SMTP_PDU_EOM 2
71 struct smtp_proto_data {
75 static int smtp_packet_init_count = 100;
77 struct smtp_request_key {
81 struct smtp_request_val {
82 guint16 processed; /* Have we processed this conversation? */
83 guint16 data_seen; /* Have we seen the data packet */
84 guint16 eom_seen; /* Have we seen the end of message */
85 guint16 crlf_seen; /* Have we seen a CRLF on the end of a packet */
88 GHashTable *smtp_request_hash = NULL;
89 GMemChunk *smtp_request_keys = NULL;
90 GMemChunk *smtp_request_vals = NULL;
91 GMemChunk *smtp_packet_infos = NULL;
95 smtp_equal(gconstpointer v, gconstpointer w)
97 struct smtp_request_key *v1 = (struct smtp_request_key *)v;
98 struct smtp_request_key *v2 = (struct smtp_request_key *)w;
100 #if defined(DEBUG_SMTP_HASH)
101 printf("Comparing %08X\n and %08X\n",
102 v1->conversation, v2->conversation);
105 if (v1->conversation == v2->conversation)
113 smtp_hash(gconstpointer v)
115 struct smtp_request_key *key = (struct smtp_request_key *)v;
118 val = key->conversation;
120 #if defined(DEBUG_SMTP_HASH)
121 printf("SMTP Hash calculated as %u\n", val);
129 smtp_init_protocol(void)
131 #if defined(DEBUG_SMTP_HASH)
132 printf("Initializing SMTP hashtable area\n");
135 if (smtp_request_hash)
136 g_hash_table_destroy(smtp_request_hash);
137 if (smtp_request_keys)
138 g_mem_chunk_destroy(smtp_request_keys);
139 if (smtp_request_vals)
140 g_mem_chunk_destroy(smtp_request_vals);
141 if (smtp_packet_infos)
142 g_mem_chunk_destroy(smtp_packet_infos);
144 smtp_request_hash = g_hash_table_new(smtp_hash, smtp_equal);
145 smtp_request_keys = g_mem_chunk_new("smtp_request_keys",
146 sizeof(struct smtp_request_key),
147 smtp_packet_init_count * sizeof(struct smtp_request_key), G_ALLOC_AND_FREE);
148 smtp_request_vals = g_mem_chunk_new("smtp_request_vals",
149 sizeof(struct smtp_request_val),
150 smtp_packet_init_count * sizeof(struct smtp_request_val), G_ALLOC_AND_FREE);
151 smtp_packet_infos = g_mem_chunk_new("smtp_packet_infos",
152 sizeof(struct smtp_proto_data),
153 smtp_packet_init_count * sizeof(struct smtp_proto_data), G_ALLOC_AND_FREE);
158 int find_smtp_resp_end(const u_char *pd, int offset)
162 /* Look for the CRLF ... but keep in mind the END_OF_FRAME */
164 while (END_OF_FRAME >= cntr) {
166 if (pd[offset + cntr] == 0x0A) { /* Found it */
168 if (END_OF_FRAME >= cntr + 1) cntr++;
184 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
188 dissect_smtp(const u_char *pd, int offset, frame_data *fd,
191 /* tvbuff_t *tvb = tvb_create_from_top(offset);*/
192 packet_info *pinfo = π
194 struct smtp_proto_data *frame_data;
195 proto_tree *smtp_tree, *ti;
197 const u_char *cmd = NULL;
198 conversation_t *conversation;
199 struct smtp_request_key request_key, *new_request_key;
200 struct smtp_request_val *request_val;
203 CHECK_DISPLAY_AS_DATA(proto_smtp, tvb, pinfo, tree);
205 OLD_CHECK_DISPLAY_AS_DATA(proto_smtp, pd, offset, fd, tree);
208 /* If we have per frame data, use that, else, we must be on the first
209 * pass, so we figure it out on the first pass.
211 * Since we can't stash info away in a conversation (as they are
212 * removed during a filter operation, and we can't rely on the visited
213 * flag, as that is set to 0 during a filter, we must save per-frame
214 * data for each frame. However, we only need it for requests. Responses
215 * are easy to manage.
218 /* Find out what conversation this packet is part of ... but only
219 * if we have no information on this packet, so find the per-frame
223 /* SMTP messages have a simple format ... */
225 request = pinfo -> destport == TCP_PORT_SMTP;
226 cmd = pd + offset; /* FIXME: What about tvb */
228 frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
232 conversation = find_conversation(&pinfo->src, &pinfo->dst, pi.ptype,
233 pinfo->srcport, pinfo->destport);
234 if (conversation == NULL) { /* No conversation, create one */
235 conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
236 pinfo->srcport, pinfo->destport, NULL);
241 * Check for and insert an entry in the request table if does not exist
243 request_key.conversation = conversation->index;
245 request_val = (struct smtp_request_val *)g_hash_table_lookup(smtp_request_hash, &request_key);
247 if (!request_val) { /* Create one */
249 new_request_key = g_mem_chunk_alloc(smtp_request_keys);
250 new_request_key->conversation = conversation->index;
252 request_val = g_mem_chunk_alloc(smtp_request_vals);
253 request_val->processed = 0;
254 request_val->data_seen = 0;
255 request_val->eom_seen = 0;
256 request_val->crlf_seen = 0;
258 g_hash_table_insert(smtp_request_hash, new_request_key, request_val);
263 * Check whether or not this packet is an end of message packet
264 * We should look for CRLF.CRLF and they may be split.
265 * We have to keep in mind that we may see what we want on
266 * two passes through here ...
269 if (request_val->data_seen && !request_val->processed) {
272 * The order of these is important ... We want to avoid
273 * cases where there is a CRLF at the end of a packet and a
274 * .CRLF at the begining of the same packet.
277 if ((request_val->crlf_seen && strncmp(pd + offset, ".\r\n", 3) == 0) ||
278 (strncmp(pd + offset, "\r\n.\r\n", 5) == 0)) {
280 request_val->eom_seen = 1;
284 if (strncmp(pd + offset + END_OF_FRAME - 2, "\r\n", 2) == 0) {
286 request_val->crlf_seen = 1;
291 request_val->crlf_seen = 0;
297 * OK, Check if we have seen a DATA request. We do it here for
298 * simplicity, but we have to be careful below.
303 frame_data = g_mem_chunk_alloc(smtp_packet_infos);
305 if (!request_val->processed) {
306 if (strncmp(pd + offset, "DATA", 4)==0) {
308 request_val->data_seen = 1;
309 frame_data->pdu_type = SMTP_PDU_CMD;
310 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
312 } else if ((!request_val->eom_seen) &&
313 (request_val->data_seen)) {
315 /* Now, create the frame data for this frame ... */
317 frame_data->pdu_type = SMTP_PDU_MESSAGE;
318 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
320 } else if (request_val->eom_seen) { /* Seen the EOM */
322 /* Now, we clear the eom_seen and data_seen bits */
324 request_val->eom_seen = request_val->data_seen = 0;
325 request_val->processed = 1; /* We have seen all the packets */
327 /* And add the packet data */
329 frame_data->pdu_type = SMTP_PDU_EOM;
330 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
334 frame_data->pdu_type = SMTP_PDU_CMD;
335 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
343 * From here, we simply add items to the tree and info to the info
347 if (check_col(fd, COL_PROTOCOL))
348 col_add_str(fd, COL_PROTOCOL, "SMTP");
350 if (check_col(fd, COL_INFO)) { /* Add the appropriate type here */
353 * If it is a request, we have to look things up, otherwise, just
354 * display the right things
359 /* We must have frame_data here ... */
361 switch (frame_data->pdu_type) {
362 case SMTP_PDU_MESSAGE:
364 col_add_fstr(pinfo->fd, COL_INFO, "Message: %s", format_text(cmd, END_OF_FRAME));
369 col_add_fstr(pinfo->fd, COL_INFO, "EOM: %s", format_text(cmd, END_OF_FRAME));
374 col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
381 col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
386 if (tree) { /* Build the tree info ... */
388 ti = proto_tree_add_item(tree, proto_smtp, NullTVB, offset, END_OF_FRAME, FALSE);
389 smtp_tree = proto_item_add_subtree(ti, ett_smtp);
390 proto_tree_add_boolean_hidden(smtp_tree, (request ? hf_smtp_req : hf_smtp_rsp),
391 NullTVB, offset, 4, TRUE);
395 * Check out whether or not we can see a command in there ...
396 * What we are looking for is not data_seen and the word DATA
399 * We will see DATA and request_val->data_seen when we process the
400 * tree view after we have seen a DATA packet when processing
401 * the packet list pane.
403 * On the first pass, we will not have any info on the packets
404 * On second and subsequent passes, we will.
407 switch (frame_data->pdu_type) {
409 case SMTP_PDU_MESSAGE:
411 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "Message: %s", format_text(cmd, END_OF_FRAME));
417 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "EOM: %s", format_text(cmd, END_OF_FRAME));
422 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 4, "Command: %s", format_text(cmd, 4));
423 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));
430 /* Must consider a multi-line response here ... */
432 while (END_OF_FRAME >= 4 && pd[offset + 3] == '-') {
433 int resp_len = find_smtp_resp_end(pd, offset);
435 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 3, "Response: %s", format_text(pd + offset, 3));
436 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset + 4, resp_len, "Parameter: %s", format_text(pd + offset + 4, resp_len - 4));
441 proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, 3, "Response: %s", format_text(pd + offset, 3));
442 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));
448 /* Register all the bits needed by the filtering engine */
451 proto_register_smtp(void)
453 static hf_register_info hf[] = {
455 { "Request", "smtp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
458 { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
460 static gint *ett[] = {
463 /*module_t *smtp_module = NULL; */ /* Not yet used */
465 /* No Configuration options to register? */
467 proto_smtp = proto_register_protocol("Simple Mail Transfer Protocol", "smtp");
469 proto_register_field_array(proto_smtp, hf, array_length(hf));
470 proto_register_subtree_array(ett, array_length(ett));
471 register_init_routine(&smtp_init_protocol);
475 /* The registration hand-off routine */
477 proto_reg_handoff_smtp(void)
479 static int smtp_prefs_initialized = FALSE;
480 static int tcp_port = 0;
482 if (smtp_prefs_initialized) {
484 old_dissector_delete("tcp.port", tcp_port, dissect_smtp);
489 smtp_prefs_initialized = TRUE;
493 tcp_port = global_smtp_tcp_port;
495 old_dissector_add("tcp.port", global_smtp_tcp_port, dissect_smtp);