change a whole bunch of ethereal into wireshark
[obnox/wireshark/wip.git] / epan / dissectors / packet-smtp.c
1 /* packet-smtp.c
2  * Routines for SMTP packet disassembly
3  *
4  * $Id$
5  *
6  * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1999 Gerald Combs
11  *
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.
16  *
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.
21  *
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.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <time.h>
35 #include <glib.h>
36 #include <string.h>
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>
43
44 #define TCP_PORT_SMTP 25
45
46 static int proto_smtp = -1;
47
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;
54
55 static int ett_smtp = -1;
56 static int ett_smtp_cmdresp = -1;
57
58 /* desegmentation of SMTP command and response lines */
59 static gboolean smtp_desegment = TRUE;
60
61 /*
62  * A CMD is an SMTP command, MESSAGE is the message portion, and EOM is the
63  * last part of a message
64  */
65
66 #define SMTP_PDU_CMD     0
67 #define SMTP_PDU_MESSAGE 1
68 #define SMTP_PDU_EOM     2
69
70 struct smtp_proto_data {
71   guint16 pdu_type;
72 };
73
74 /*
75  * State information stored with a conversation.
76  */
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 */
80 };
81
82 static void
83 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
84 {
85     struct smtp_proto_data  *frame_data;
86     proto_tree              *smtp_tree;
87     proto_tree              *cmdresp_tree;
88     proto_item              *ti;
89     int                     offset = 0;
90     int                     request = 0;
91     conversation_t          *conversation;
92     struct smtp_request_val *request_val;
93     const guchar            *line;
94     guint32                 code;
95     int                     linelen;
96     gint                    length_remaining;
97     gboolean                eom_seen = FALSE;
98     gint                    next_offset;
99     gboolean                is_continuation_line;
100     int                     cmdlen;
101
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.
108      *
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.
113      *
114      * We only need that for the client->server stream; responses
115      * are easy to manage.
116      *
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.
119      */
120
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
123      * info first.
124      */
125
126     /* SMTP messages have a simple format ... */
127
128     request = pinfo -> destport == pinfo -> match_port;
129
130     /*
131      * Get the first line from the buffer.
132      *
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.
138      */
139     linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
140       smtp_desegment && pinfo->can_desegment);
141     if (linelen == -1) {
142       /*
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.
148        */
149       pinfo->desegment_offset = offset;
150       pinfo->desegment_len = 1;
151       return;
152     }
153     line = tvb_get_ptr(tvb, offset, linelen);
154
155     frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
156
157     if (!frame_data) {
158
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);
164
165       }
166
167       /*
168        * Is there a request structure attached to this conversation?
169        */
170       request_val = conversation_get_proto_data(conversation, proto_smtp);
171
172       if (!request_val) {
173
174         /*
175          * No - create one and attach it.
176          */
177         request_val = se_alloc(sizeof(struct smtp_request_val));
178         request_val->reading_data = FALSE;
179         request_val->crlf_seen = 0;
180
181         conversation_add_proto_data(conversation, proto_smtp, request_val);
182
183       }
184
185       /*
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 ...
190        */
191
192       if (request_val->reading_data) {
193
194         /*
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.
198          */
199
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) {
202
203           eom_seen = TRUE;
204
205         }
206
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) {
210
211           request_val->crlf_seen = 1;
212
213         }
214         else {
215
216           request_val->crlf_seen = 0;
217
218         }
219       }
220
221     /*
222      * OK, Check if we have seen a DATA request. We do it here for
223      * simplicity, but we have to be careful below.
224      */
225
226       if (request) {
227
228         frame_data = se_alloc(sizeof(struct smtp_proto_data));
229
230         if (request_val->reading_data) {
231           /*
232            * This is message data.
233            */
234           if (eom_seen) { /* Seen the EOM */
235             /*
236              * EOM.
237              * Everything that comes after it is commands.
238              *
239              * XXX - what if the EOM isn't at the beginning of
240              * the TCP segment?  It can occur anywhere....
241              */
242             frame_data->pdu_type = SMTP_PDU_EOM;
243             request_val->reading_data = FALSE;
244           } else {
245             /*
246              * Message data with no EOM.
247              */
248             frame_data->pdu_type = SMTP_PDU_MESSAGE;
249           }
250         } else {
251           /*
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.)
259            */
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) {
266
267               /*
268                * DATA command.
269                * This is a command, but everything that comes after it,
270                * until an EOM, is data.
271                */
272               frame_data->pdu_type = SMTP_PDU_CMD;
273               request_val->reading_data = TRUE;
274
275             } else {
276
277               /*
278                * Regular command.
279                */
280               frame_data->pdu_type = SMTP_PDU_CMD;
281
282             }
283           } else {
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;
288                 else
289             /*
290              * Assume it's message data.
291              */
292
293             frame_data->pdu_type = SMTP_PDU_MESSAGE;
294
295           }
296
297         }
298
299         p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
300
301       }
302     }
303
304     /*
305      * From here, we simply add items to the tree and info to the info
306      * fields ...
307      */
308
309     if (check_col(pinfo->cinfo, COL_PROTOCOL))
310       col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMTP");
311
312     if (check_col(pinfo->cinfo, COL_INFO)) {  /* Add the appropriate type here */
313
314       /*
315        * If it is a request, we have to look things up, otherwise, just
316        * display the right things
317        */
318
319       if (request) {
320
321         /* We must have frame_data here ... */
322
323         switch (frame_data->pdu_type) {
324         case SMTP_PDU_MESSAGE:
325
326           col_set_str(pinfo->cinfo, COL_INFO, "Message Body");
327           break;
328
329         case SMTP_PDU_EOM:
330
331           col_add_fstr(pinfo->cinfo, COL_INFO, "EOM: %s",
332               format_text(line, linelen));
333           break;
334
335         case SMTP_PDU_CMD:
336
337           col_add_fstr(pinfo->cinfo, COL_INFO, "Command: %s",
338               format_text(line, linelen));
339           break;
340
341         }
342
343       }
344       else {
345
346         col_add_fstr(pinfo->cinfo, COL_INFO, "Response: %s",
347             format_text(line, linelen));
348
349       }
350     }
351
352     if (tree) { /* Build the tree info ... */
353
354       ti = proto_tree_add_item(tree, proto_smtp, tvb, offset, -1, FALSE);
355       smtp_tree = proto_item_add_subtree(ti, ett_smtp);
356       if (request) {
357
358         /*
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
361          * and not eom_seen.
362          *
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.
366          *
367          * On the first pass, we will not have any info on the packets
368          * On second and subsequent passes, we will.
369          */
370
371         switch (frame_data->pdu_type) {
372
373         case SMTP_PDU_MESSAGE:
374
375           /*
376            * Message body.
377            * Put its lines into the protocol tree, a line at a time.
378            */
379           while (tvb_offset_exists(tvb, offset)) {
380
381             /*
382              * Find the end of the line.
383              */
384             tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
385
386             /*
387              * Put this line.
388              */
389             proto_tree_add_text(smtp_tree, tvb, offset, next_offset - offset,
390                 "Message: %s",
391                 tvb_format_text(tvb, offset, next_offset - offset));
392
393             /*
394              * Step to the next line.
395              */
396             offset = next_offset;
397
398           }
399
400           break;
401
402         case SMTP_PDU_EOM:
403
404           /*
405            * End-of-message-body indicator.
406            *
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.
411            */
412           proto_tree_add_text(smtp_tree, tvb, offset, linelen,
413               "EOM: %s", format_text(line, linelen));
414
415           break;
416
417         case SMTP_PDU_CMD:
418
419           /*
420            * Command.
421            *
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.
426            */
427           if (linelen >= 4)
428             cmdlen = 4;
429           else
430             cmdlen = linelen;
431           proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_req, tvb,
432                                         0, 0, TRUE);
433           /*
434            * Put the command line into the protocol tree.
435            */
436           ti = proto_tree_add_text(smtp_tree, tvb, offset, next_offset - offset,
437                 "Command: %s",
438                 tvb_format_text(tvb, offset, next_offset - offset));
439           cmdresp_tree = proto_item_add_subtree(ti, ett_smtp_cmdresp);
440
441           proto_tree_add_item(cmdresp_tree, hf_smtp_req_command, tvb,
442                               offset, cmdlen, FALSE);
443           if (linelen > 5) {
444             proto_tree_add_item(cmdresp_tree, hf_smtp_req_parameter, tvb,
445                                 offset + 5, linelen - 5, FALSE);
446           }
447
448         }
449
450       }
451       else {
452
453         /*
454          * Process the response, a line at a time, until we hit a line
455          * that doesn't have a continuation indication on it.
456          */
457         proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_rsp, tvb,
458                                         0, 0, TRUE);
459
460         while (tvb_offset_exists(tvb, offset)) {
461
462           /*
463            * Find the end of the line.
464            */
465           linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
466
467           /*
468            * Put it into the protocol tree.
469            */
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);
475
476           /*
477            * Is it a continuation line?
478            */
479           is_continuation_line =
480               (linelen >= 4 && tvb_get_guint8(tvb, offset + 3) == '-');
481
482           /*
483            * Put the response code and parameters into the protocol tree.
484            */
485           line = tvb_get_ptr(tvb, offset, linelen);
486           if (linelen >= 3 && isdigit(line[0]) && isdigit(line[1])
487                            && isdigit(line[2])) {
488             /*
489              * We have a 3-digit response code.
490              */
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,
493                                 code);
494
495             if (linelen >= 4) {
496               proto_tree_add_item(cmdresp_tree, hf_smtp_rsp_parameter, tvb,
497                                   offset + 4, linelen - 4, FALSE);
498             }
499           }
500
501           /*
502            * Step past this line.
503            */
504           offset = next_offset;
505
506           /*
507            * If it's not a continuation line, quit.
508            */
509           if (!is_continuation_line)
510             break;
511
512         }
513
514       }
515     }
516 }
517
518 /* Register all the bits needed by the filtering engine */
519
520 void
521 proto_register_smtp(void)
522 {
523   static hf_register_info hf[] = {
524     { &hf_smtp_req,
525       { "Request", "smtp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
526
527     { &hf_smtp_rsp,
528       { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
529
530     { &hf_smtp_req_command,
531       { "Command", "smtp.req.command", FT_STRING,  BASE_NONE, NULL, 0x0,
532         "", HFILL }},
533
534     { &hf_smtp_req_parameter,
535       { "Request parameter", "smtp.req.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
536         "", HFILL }},
537
538     { &hf_smtp_rsp_code,
539       { "Response code", "smtp.response.code", FT_UINT32, BASE_DEC, NULL, 0x0,
540         "", HFILL }},
541
542     { &hf_smtp_rsp_parameter,
543       { "Response parameter", "smtp.rsp.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
544         "", HFILL }}
545   };
546   static gint *ett[] = {
547     &ett_smtp,
548     &ett_smtp_cmdresp,
549   };
550   module_t *smtp_module;
551
552   /* No Configuration options to register? */
553
554   proto_smtp = proto_register_protocol("Simple Mail Transfer Protocol",
555                                        "SMTP", "smtp");
556
557   proto_register_field_array(proto_smtp, hf, array_length(hf));
558   proto_register_subtree_array(ett, array_length(ett));
559
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.",
565     &smtp_desegment);
566 }
567
568 /* The registration hand-off routine */
569 void
570 proto_reg_handoff_smtp(void)
571 {
572   dissector_handle_t smtp_handle;
573
574   smtp_handle = create_dissector_handle(dissect_smtp, proto_smtp);
575   dissector_add("tcp.port", TCP_PORT_SMTP, smtp_handle);
576 }