2 * Routines for SMTP packet disassembly
6 * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1999 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #include <epan/packet.h>
38 #include <epan/conversation.h>
39 #include <epan/addr_resolv.h>
40 #include <epan/prefs.h>
41 #include <epan/strutil.h>
42 #include <epan/emem.h>
44 #define TCP_PORT_SMTP 25
46 static int proto_smtp = -1;
48 static int hf_smtp_req = -1;
49 static int hf_smtp_rsp = -1;
50 static int hf_smtp_req_command = -1;
51 static int hf_smtp_req_parameter = -1;
52 static int hf_smtp_rsp_code = -1;
53 static int hf_smtp_rsp_parameter = -1;
55 static int ett_smtp = -1;
56 static int ett_smtp_cmdresp = -1;
58 /* desegmentation of SMTP command and response lines */
59 static gboolean smtp_desegment = TRUE;
62 * A CMD is an SMTP command, MESSAGE is the message portion, and EOM is the
63 * last part of a message
66 #define SMTP_PDU_CMD 0
67 #define SMTP_PDU_MESSAGE 1
68 #define SMTP_PDU_EOM 2
70 struct smtp_proto_data {
75 * State information stored with a conversation.
77 struct smtp_request_val {
78 gboolean reading_data; /* Reading message data, not commands */
79 guint16 crlf_seen; /* Have we seen a CRLF on the end of a packet */
83 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
85 struct smtp_proto_data *frame_data;
86 proto_tree *smtp_tree;
87 proto_tree *cmdresp_tree;
91 conversation_t *conversation;
92 struct smtp_request_val *request_val;
96 gint length_remaining;
97 gboolean eom_seen = FALSE;
99 gboolean is_continuation_line;
102 /* As there is no guarantee that we will only see frames in the
103 * the SMTP conversation once, and that we will see them in
104 * order - in Wireshark, the user could randomly click on frames
105 * in the conversation in any order in which they choose - we
106 * have to store information with each frame indicating whether
107 * it contains commands or data or an EOM indication.
109 * XXX - what about frames that contain *both*? TCP is a
110 * byte-stream protocol, and there are no guarantees that
111 * TCP segment boundaries will correspond to SMTP commands
112 * or EOM indications.
114 * We only need that for the client->server stream; responses
115 * are easy to manage.
117 * If we have per frame data, use that, else, we must be on the first
118 * pass, so we figure it out on the first pass.
121 /* Find out what conversation this packet is part of ... but only
122 * if we have no information on this packet, so find the per-frame
126 /* SMTP messages have a simple format ... */
128 request = pinfo -> destport == pinfo -> match_port;
131 * Get the first line from the buffer.
133 * Note that "tvb_find_line_end()" will, if it doesn't return
134 * -1, return a value that is not longer than what's in the buffer,
135 * and "tvb_find_line_end()" will always return a value that is not
136 * longer than what's in the buffer, so the "tvb_get_ptr()" call
137 * won't throw an exception.
139 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
140 smtp_desegment && pinfo->can_desegment);
143 * We didn't find a line ending, and we're doing desegmentation;
144 * tell the TCP dissector where the data for this message starts
145 * in the data it handed us, and tell it we need one more byte
146 * (we may need more, but we'll try again if what we get next
147 * isn't enough), and return.
149 pinfo->desegment_offset = offset;
150 pinfo->desegment_len = 1;
153 line = tvb_get_ptr(tvb, offset, linelen);
155 frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
159 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
160 pinfo->srcport, pinfo->destport, 0);
161 if (conversation == NULL) { /* No conversation, create one */
162 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
163 pinfo->srcport, pinfo->destport, 0);
168 * Is there a request structure attached to this conversation?
170 request_val = conversation_get_proto_data(conversation, proto_smtp);
175 * No - create one and attach it.
177 request_val = se_alloc(sizeof(struct smtp_request_val));
178 request_val->reading_data = FALSE;
179 request_val->crlf_seen = 0;
181 conversation_add_proto_data(conversation, proto_smtp, request_val);
186 * Check whether or not this packet is an end of message packet
187 * We should look for CRLF.CRLF and they may be split.
188 * We have to keep in mind that we may see what we want on
189 * two passes through here ...
192 if (request_val->reading_data) {
195 * The order of these is important ... We want to avoid
196 * cases where there is a CRLF at the end of a packet and a
197 * .CRLF at the begining of the same packet.
200 if ((request_val->crlf_seen && tvb_strneql(tvb, offset, ".\r\n", 3) == 0) ||
201 tvb_strneql(tvb, offset, "\r\n.\r\n", 5) == 0) {
207 length_remaining = tvb_length_remaining(tvb, offset);
208 if (length_remaining == tvb_reported_length_remaining(tvb, offset) &&
209 tvb_strneql(tvb, offset + length_remaining - 2, "\r\n", 2) == 0) {
211 request_val->crlf_seen = 1;
216 request_val->crlf_seen = 0;
222 * OK, Check if we have seen a DATA request. We do it here for
223 * simplicity, but we have to be careful below.
228 frame_data = se_alloc(sizeof(struct smtp_proto_data));
230 if (request_val->reading_data) {
232 * This is message data.
234 if (eom_seen) { /* Seen the EOM */
237 * Everything that comes after it is commands.
239 * XXX - what if the EOM isn't at the beginning of
240 * the TCP segment? It can occur anywhere....
242 frame_data->pdu_type = SMTP_PDU_EOM;
243 request_val->reading_data = FALSE;
246 * Message data with no EOM.
248 frame_data->pdu_type = SMTP_PDU_MESSAGE;
252 * This is commands - unless the capture started in the
253 * middle of a session, and we're in the middle of data.
254 * To quote RFC 821, "Command codes are four alphabetic
255 * characters"; if we don't see four alphabetic characters
256 * and, if there's anything else in the line, a space, we
257 * assume it's not a command.
258 * (We treat only A-Z and a-z as alphabetic.)
260 #define ISALPHA(c) (((c) >= 'A' && (c) <= 'Z') || \
261 ((c) >= 'a' && (c) <= 'z'))
262 if (linelen >= 4 && ISALPHA(line[0]) && ISALPHA(line[1]) &&
263 ISALPHA(line[2]) && ISALPHA(line[3]) &&
264 (linelen == 4 || line[4] == ' ')) {
265 if (strncasecmp(line, "DATA", 4) == 0) {
269 * This is a command, but everything that comes after it,
270 * until an EOM, is data.
272 frame_data->pdu_type = SMTP_PDU_CMD;
273 request_val->reading_data = TRUE;
280 frame_data->pdu_type = SMTP_PDU_CMD;
284 if ((linelen >= 7) && line[0] == 'X' && ( (strncasecmp(line, "X-EXPS ", 7) == 0) ||
285 ((linelen >=13) && (strncasecmp(line, "X-LINK2STATE ", 13) == 0)) ||
286 ((linelen >= 8) && (strncasecmp(line, "XEXCH50 ", 8) == 0)) ))
287 frame_data->pdu_type = SMTP_PDU_CMD;
290 * Assume it's message data.
293 frame_data->pdu_type = SMTP_PDU_MESSAGE;
299 p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
305 * From here, we simply add items to the tree and info to the info
309 if (check_col(pinfo->cinfo, COL_PROTOCOL))
310 col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMTP");
312 if (check_col(pinfo->cinfo, COL_INFO)) { /* Add the appropriate type here */
315 * If it is a request, we have to look things up, otherwise, just
316 * display the right things
321 /* We must have frame_data here ... */
323 switch (frame_data->pdu_type) {
324 case SMTP_PDU_MESSAGE:
326 col_set_str(pinfo->cinfo, COL_INFO, "Message Body");
331 col_add_fstr(pinfo->cinfo, COL_INFO, "EOM: %s",
332 format_text(line, linelen));
337 col_add_fstr(pinfo->cinfo, COL_INFO, "Command: %s",
338 format_text(line, linelen));
346 col_add_fstr(pinfo->cinfo, COL_INFO, "Response: %s",
347 format_text(line, linelen));
352 if (tree) { /* Build the tree info ... */
354 ti = proto_tree_add_item(tree, proto_smtp, tvb, offset, -1, FALSE);
355 smtp_tree = proto_item_add_subtree(ti, ett_smtp);
359 * Check out whether or not we can see a command in there ...
360 * What we are looking for is not data_seen and the word DATA
363 * We will see DATA and request_val->data_seen when we process the
364 * tree view after we have seen a DATA packet when processing
365 * the packet list pane.
367 * On the first pass, we will not have any info on the packets
368 * On second and subsequent passes, we will.
371 switch (frame_data->pdu_type) {
373 case SMTP_PDU_MESSAGE:
377 * Put its lines into the protocol tree, a line at a time.
379 while (tvb_offset_exists(tvb, offset)) {
382 * Find the end of the line.
384 tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
389 proto_tree_add_text(smtp_tree, tvb, offset, next_offset - offset,
391 tvb_format_text(tvb, offset, next_offset - offset));
394 * Step to the next line.
396 offset = next_offset;
405 * End-of-message-body indicator.
407 * XXX - what about stuff after the first line?
408 * Unlikely, as the client should wait for a response to the
409 * DATA command this terminates before sending another
410 * request, but we should probably handle it.
412 proto_tree_add_text(smtp_tree, tvb, offset, linelen,
413 "EOM: %s", format_text(line, linelen));
422 * XXX - what about stuff after the first line?
423 * Unlikely, as the client should wait for a response to the
424 * previous command before sending another request, but we
425 * should probably handle it.
431 proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_req, tvb,
434 * Put the command line into the protocol tree.
436 ti = proto_tree_add_text(smtp_tree, tvb, offset, next_offset - offset,
438 tvb_format_text(tvb, offset, next_offset - offset));
439 cmdresp_tree = proto_item_add_subtree(ti, ett_smtp_cmdresp);
441 proto_tree_add_item(cmdresp_tree, hf_smtp_req_command, tvb,
442 offset, cmdlen, FALSE);
444 proto_tree_add_item(cmdresp_tree, hf_smtp_req_parameter, tvb,
445 offset + 5, linelen - 5, FALSE);
454 * Process the response, a line at a time, until we hit a line
455 * that doesn't have a continuation indication on it.
457 proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_rsp, tvb,
460 while (tvb_offset_exists(tvb, offset)) {
463 * Find the end of the line.
465 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
468 * Put it into the protocol tree.
470 ti = proto_tree_add_text(smtp_tree, tvb, offset,
471 next_offset - offset, "Response: %s",
472 tvb_format_text(tvb, offset,
473 next_offset - offset));
474 cmdresp_tree = proto_item_add_subtree(ti, ett_smtp_cmdresp);
477 * Is it a continuation line?
479 is_continuation_line =
480 (linelen >= 4 && tvb_get_guint8(tvb, offset + 3) == '-');
483 * Put the response code and parameters into the protocol tree.
485 line = tvb_get_ptr(tvb, offset, linelen);
486 if (linelen >= 3 && isdigit(line[0]) && isdigit(line[1])
487 && isdigit(line[2])) {
489 * We have a 3-digit response code.
491 code = (line[0] - '0')*100 + (line[1] - '0')*10 + (line[2] - '0');
492 proto_tree_add_uint(cmdresp_tree, hf_smtp_rsp_code, tvb, offset, 3,
496 proto_tree_add_item(cmdresp_tree, hf_smtp_rsp_parameter, tvb,
497 offset + 4, linelen - 4, FALSE);
502 * Step past this line.
504 offset = next_offset;
507 * If it's not a continuation line, quit.
509 if (!is_continuation_line)
518 /* Register all the bits needed by the filtering engine */
521 proto_register_smtp(void)
523 static hf_register_info hf[] = {
525 { "Request", "smtp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
528 { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
530 { &hf_smtp_req_command,
531 { "Command", "smtp.req.command", FT_STRING, BASE_NONE, NULL, 0x0,
534 { &hf_smtp_req_parameter,
535 { "Request parameter", "smtp.req.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
539 { "Response code", "smtp.response.code", FT_UINT32, BASE_DEC, NULL, 0x0,
542 { &hf_smtp_rsp_parameter,
543 { "Response parameter", "smtp.rsp.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
546 static gint *ett[] = {
550 module_t *smtp_module;
552 /* No Configuration options to register? */
554 proto_smtp = proto_register_protocol("Simple Mail Transfer Protocol",
557 proto_register_field_array(proto_smtp, hf, array_length(hf));
558 proto_register_subtree_array(ett, array_length(ett));
560 smtp_module = prefs_register_protocol(proto_smtp, NULL);
561 prefs_register_bool_preference(smtp_module, "desegment_lines",
562 "Reassemble SMTP command and response lines\nspanning multiple TCP segments",
563 "Whether the SMTP dissector should reassemble command and response lines spanning multiple TCP segments."
564 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
568 /* The registration hand-off routine */
570 proto_reg_handoff_smtp(void)
572 dissector_handle_t smtp_handle;
574 smtp_handle = create_dissector_handle(dissect_smtp, proto_smtp);
575 dissector_add("tcp.port", TCP_PORT_SMTP, smtp_handle);