Apparently bit 0x80 of a SAMR domain handle is the permission to look
[metze/wireshark/wip.git] / packet-smtp.c
1 /* packet-smtp.c
2  * Routines for SMTP packet disassembly
3  *
4  * $Id: packet-smtp.c,v 1.32 2002/08/28 21:00:34 jmayer Exp $
5  *
6  * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
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/resolv.h>
40 #include "prefs.h"
41 #include <epan/strutil.h>
42
43 #define TCP_PORT_SMTP 25
44
45 void proto_reg_handoff_smtp(void);
46
47 static int proto_smtp = -1;
48
49 static int hf_smtp_req = -1;
50 static int hf_smtp_rsp = -1;
51 static int hf_smtp_req_command = -1;
52 static int hf_smtp_req_parameter = -1;
53 static int hf_smtp_rsp_code = -1;
54 static int hf_smtp_rsp_parameter = -1;
55
56 static int ett_smtp = -1;
57
58 static int global_smtp_tcp_port = TCP_PORT_SMTP;
59
60 /* desegmentation of SMTP command and response lines */
61 static gboolean smtp_desegment = TRUE;
62
63 /*
64  * A CMD is an SMTP command, MESSAGE is the message portion, and EOM is the
65  * last part of a message
66  */
67
68 #define SMTP_PDU_CMD     0
69 #define SMTP_PDU_MESSAGE 1
70 #define SMTP_PDU_EOM     2
71
72 struct smtp_proto_data {
73   guint16 pdu_type;
74 };
75
76 static int smtp_packet_init_count = 100;
77
78 /*
79  * State information stored with a conversation.
80  */
81 struct smtp_request_val {
82   gboolean reading_data; /* Reading message data, not commands */
83   guint16 crlf_seen;     /* Have we seen a CRLF on the end of a packet */
84 };
85
86 static GMemChunk  *smtp_request_vals = NULL;
87 static GMemChunk  *smtp_packet_infos = NULL;
88
89 static void
90 smtp_init_protocol(void)
91 {
92   if (smtp_request_vals)
93     g_mem_chunk_destroy(smtp_request_vals);
94   if (smtp_packet_infos)
95     g_mem_chunk_destroy(smtp_packet_infos);
96
97   smtp_request_vals = g_mem_chunk_new("smtp_request_vals",
98                                       sizeof(struct smtp_request_val),
99                                       smtp_packet_init_count * sizeof(struct smtp_request_val), G_ALLOC_AND_FREE);
100   smtp_packet_infos = g_mem_chunk_new("smtp_packet_infos",
101                                       sizeof(struct smtp_proto_data),
102                                       smtp_packet_init_count * sizeof(struct smtp_proto_data), G_ALLOC_AND_FREE);
103
104 }
105
106 static void
107 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
108 {
109     struct smtp_proto_data  *frame_data;
110     proto_tree              *smtp_tree;
111     proto_item              *ti;
112     int                     offset = 0;
113     int                     request = 0;
114     conversation_t          *conversation;
115     struct smtp_request_val *request_val;
116     const guchar            *line;
117     guint32                 code;
118     int                     linelen;
119     gboolean                eom_seen = FALSE;
120     gint                    next_offset;
121     gboolean                is_continuation_line;
122     int                     cmdlen;
123
124     /* As there is no guarantee that we will only see frames in the
125      * the SMTP conversation once, and that we will see them in
126      * order - in Ethereal, the user could randomly click on frames
127      * in the conversation in any order in which they choose - we
128      * have to store information with each frame indicating whether
129      * it contains commands or data or an EOM indication.
130      *
131      * XXX - what about frames that contain *both*?  TCP is a
132      * byte-stream protocol, and there are no guarantees that
133      * TCP segment boundaries will correspond to SMTP commands
134      * or EOM indications.
135      *
136      * We only need that for the client->server stream; responses
137      * are easy to manage.
138      *
139      * If we have per frame data, use that, else, we must be on the first
140      * pass, so we figure it out on the first pass.
141      */
142
143     /* Find out what conversation this packet is part of ... but only
144      * if we have no information on this packet, so find the per-frame
145      * info first.
146      */
147
148     /* SMTP messages have a simple format ... */
149
150     request = pinfo -> destport == pinfo -> match_port;
151
152     /*
153      * Get the first line from the buffer.
154      *
155      * Note that "tvb_find_line_end()" will, if it doesn't return
156      * -1, return a value that is not longer than what's in the buffer,
157      * and "tvb_find_line_end()" will always return a value that is not
158      * longer than what's in the buffer, so the "tvb_get_ptr()" call
159      * won't throw an exception.
160      */
161     linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
162       smtp_desegment && pinfo->can_desegment);
163     if (linelen == -1) {
164       /*
165        * We didn't find a line ending, and we're doing desegmentation;
166        * tell the TCP dissector where the data for this message starts
167        * in the data it handed us, and tell it we need one more byte
168        * (we may need more, but we'll try again if what we get next
169        * isn't enough), and return.
170        */
171       pinfo->desegment_offset = offset;
172       pinfo->desegment_len = 1;
173       return;
174     }
175     line = tvb_get_ptr(tvb, offset, linelen);
176
177     frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
178
179     if (!frame_data) {
180
181       conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
182                                        pinfo->srcport, pinfo->destport, 0);
183       if (conversation == NULL) { /* No conversation, create one */
184         conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
185                                         pinfo->srcport, pinfo->destport, 0);
186
187       }
188
189       /*
190        * Is there a request structure attached to this conversation?
191        */
192       request_val = conversation_get_proto_data(conversation, proto_smtp);
193
194       if (!request_val) {
195
196         /*
197          * No - create one and attach it.
198          */
199         request_val = g_mem_chunk_alloc(smtp_request_vals);
200         request_val->reading_data = FALSE;
201         request_val->crlf_seen = 0;
202
203         conversation_add_proto_data(conversation, proto_smtp, request_val);
204
205       }
206
207       /*
208        * Check whether or not this packet is an end of message packet
209        * We should look for CRLF.CRLF and they may be split.
210        * We have to keep in mind that we may see what we want on
211        * two passes through here ...
212        */
213
214       if (request_val->reading_data) {
215
216         /*
217          * The order of these is important ... We want to avoid
218          * cases where there is a CRLF at the end of a packet and a
219          * .CRLF at the begining of the same packet.
220          */
221
222         if ((request_val->crlf_seen && tvb_strneql(tvb, offset, ".\r\n", 3) == 0) ||
223             tvb_strneql(tvb, offset, "\r\n.\r\n", 5) == 0) {
224
225           eom_seen = TRUE;
226
227         }
228
229         if (tvb_strneql(tvb, offset + tvb_length_remaining(tvb, offset) - 2, "\r\n", 2) == 0) {
230
231           request_val->crlf_seen = 1;
232
233         }
234         else {
235
236           request_val->crlf_seen = 0;
237
238         }
239       }
240
241     /*
242      * OK, Check if we have seen a DATA request. We do it here for
243      * simplicity, but we have to be careful below.
244      */
245
246       if (request) {
247
248         frame_data = g_mem_chunk_alloc(smtp_packet_infos);
249
250         if (request_val->reading_data) {
251           /*
252            * This is message data.
253            */
254           if (eom_seen) { /* Seen the EOM */
255             /*
256              * EOM.
257              * Everything that comes after it is commands.
258              *
259              * XXX - what if the EOM isn't at the beginning of
260              * the TCP segment?  It can occur anywhere....
261              */
262             frame_data->pdu_type = SMTP_PDU_EOM;
263             request_val->reading_data = FALSE;
264           } else {
265             /*
266              * Message data with no EOM.
267              */
268             frame_data->pdu_type = SMTP_PDU_MESSAGE;
269           }
270         } else {
271           /*
272            * This is commands - unless the capture started in the
273            * middle of a session, and we're in the middle of data.
274            * To quote RFC 821, "Command codes are four alphabetic
275            * characters"; if we don't see four alphabetic characters
276            * and, if there's anything else in the line, a space, we
277            * assume it's not a command.
278            * (We treat only A-Z and a-z as alphabetic.)
279            */
280 #define ISALPHA(c)      (((c) >= 'A' && (c) <= 'Z') || \
281                          ((c) >= 'a' && (c) <= 'z'))
282           if (linelen >= 4 && ISALPHA(line[0]) && ISALPHA(line[1]) &&
283               ISALPHA(line[2]) && ISALPHA(line[3]) &&
284               (linelen == 4 || line[4] == ' ')) {
285             if (strncasecmp(line, "DATA", 4) == 0) {
286
287               /*
288                * DATA command.
289                * This is a command, but everything that comes after it,
290                * until an EOM, is data.
291                */
292               frame_data->pdu_type = SMTP_PDU_CMD;
293               request_val->reading_data = TRUE;
294
295             } else {
296
297               /*
298                * Regular command.
299                */
300               frame_data->pdu_type = SMTP_PDU_CMD;
301
302             }
303           } else {
304
305             /*
306              * Assume it's message data.
307              */
308
309             frame_data->pdu_type = SMTP_PDU_MESSAGE;
310
311           }
312
313         }
314
315         p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
316
317       }
318     }
319
320     /*
321      * From here, we simply add items to the tree and info to the info
322      * fields ...
323      */
324
325     if (check_col(pinfo->cinfo, COL_PROTOCOL))
326       col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMTP");
327
328     if (check_col(pinfo->cinfo, COL_INFO)) {  /* Add the appropriate type here */
329
330       /*
331        * If it is a request, we have to look things up, otherwise, just
332        * display the right things
333        */
334
335       if (request) {
336
337         /* We must have frame_data here ... */
338
339         switch (frame_data->pdu_type) {
340         case SMTP_PDU_MESSAGE:
341
342           col_set_str(pinfo->cinfo, COL_INFO, "Message Body");
343           break;
344
345         case SMTP_PDU_EOM:
346
347           col_add_fstr(pinfo->cinfo, COL_INFO, "EOM: %s",
348               format_text(line, linelen));
349           break;
350
351         case SMTP_PDU_CMD:
352
353           col_add_fstr(pinfo->cinfo, COL_INFO, "Command: %s",
354               format_text(line, linelen));
355           break;
356
357         }
358
359       }
360       else {
361
362         col_add_fstr(pinfo->cinfo, COL_INFO, "Response: %s",
363             format_text(line, linelen));
364
365       }
366     }
367
368     if (tree) { /* Build the tree info ... */
369
370       ti = proto_tree_add_item(tree, proto_smtp, tvb, offset, -1, FALSE);
371       smtp_tree = proto_item_add_subtree(ti, ett_smtp);
372       if (request) {
373
374         /*
375          * Check out whether or not we can see a command in there ...
376          * What we are looking for is not data_seen and the word DATA
377          * and not eom_seen.
378          *
379          * We will see DATA and request_val->data_seen when we process the
380          * tree view after we have seen a DATA packet when processing
381          * the packet list pane.
382          *
383          * On the first pass, we will not have any info on the packets
384          * On second and subsequent passes, we will.
385          */
386
387         switch (frame_data->pdu_type) {
388
389         case SMTP_PDU_MESSAGE:
390
391           /*
392            * Message body.
393            * Put its lines into the protocol tree, a line at a time.
394            */
395           while (tvb_offset_exists(tvb, offset)) {
396
397             /*
398              * Find the end of the line.
399              */
400             tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
401
402             /*
403              * Put this line.
404              */
405             proto_tree_add_text(smtp_tree, tvb, offset, next_offset - offset,
406                 "Message: %s",
407                 tvb_format_text(tvb, offset, next_offset - offset));
408
409             /*
410              * Step to the next line.
411              */
412             offset = next_offset;
413
414           }
415
416           break;
417
418         case SMTP_PDU_EOM:
419
420           /*
421            * End-of-message-body indicator.
422            *
423            * XXX - what about stuff after the first line?
424            * Unlikely, as the client should wait for a response to the
425            * DATA command this terminates before sending another
426            * request, but we should probably handle it.
427            */
428           proto_tree_add_text(smtp_tree, tvb, offset, linelen,
429               "EOM: %s", format_text(line, linelen));
430
431           break;
432
433         case SMTP_PDU_CMD:
434
435           /*
436            * Command.
437            *
438            * XXX - what about stuff after the first line?
439            * Unlikely, as the client should wait for a response to the
440            * previous command before sending another request, but we
441            * should probably handle it.
442            */
443           if (linelen >= 4)
444             cmdlen = 4;
445           else
446             cmdlen = linelen;
447           proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_req, tvb,
448                                         0, 0, TRUE);
449           proto_tree_add_item(smtp_tree, hf_smtp_req_command, tvb,
450                               offset, cmdlen, FALSE);
451           if (linelen > 5) {
452             proto_tree_add_item(smtp_tree, hf_smtp_req_parameter, tvb,
453                                 offset + 5, linelen - 5, FALSE);
454           }
455
456         }
457
458       }
459       else {
460
461         /*
462          * Process the response, a line at a time, until we hit a line
463          * that doesn't have a continuation indication on it.
464          */
465         proto_tree_add_boolean_hidden(smtp_tree, hf_smtp_rsp, tvb,
466                                         0, 0, TRUE);
467
468         while (tvb_offset_exists(tvb, offset)) {
469
470           /*
471            * Find the end of the line.
472            */
473           linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
474
475           /*
476            * Is it a continuation line?
477            */
478           is_continuation_line =
479               (linelen >= 4 && tvb_get_guint8(tvb, offset + 3) == '-');
480
481           /*
482            * Put it into the protocol tree.
483            */
484           line = tvb_get_ptr(tvb, offset, linelen);
485           if (linelen >= 3 && isdigit(line[0]) && isdigit(line[1])
486                            && isdigit(line[2])) {
487             /*
488              * We have a 3-digit response code.
489              */
490             code = (line[0] - '0')*100 + (line[1] - '0')*10 + (line[2] - '0');
491             proto_tree_add_uint(smtp_tree, hf_smtp_rsp_code, tvb, offset, 3,
492                                 code);
493
494             if (linelen >= 4) {
495               proto_tree_add_item(smtp_tree, hf_smtp_rsp_parameter, tvb,
496                                   offset + 4, linelen - 4, FALSE);
497             }
498           } else {
499             /*
500              * No 3-digit response code.
501              */
502             proto_tree_add_text(smtp_tree, tvb, offset, linelen,
503                                 "Bogus reply line (no response code): %s",
504                                 tvb_format_text(tvb, offset, linelen));
505           }
506
507           /*
508            * Step past this line.
509            */
510           offset = next_offset;
511
512           /*
513            * If it's not a continuation line, quit.
514            */
515           if (!is_continuation_line)
516             break;
517
518         }
519
520       }
521     }
522 }
523
524 /* Register all the bits needed by the filtering engine */
525
526 void
527 proto_register_smtp(void)
528 {
529   static hf_register_info hf[] = {
530     { &hf_smtp_req,
531       { "Request", "smtp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
532
533     { &hf_smtp_rsp,
534       { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
535
536     { &hf_smtp_req_command,
537       { "Command", "smtp.req.command", FT_STRING,  BASE_NONE, NULL, 0x0,
538         "", HFILL }},
539
540     { &hf_smtp_req_parameter,
541       { "Request parameter", "smtp.req.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
542         "", HFILL }},
543
544     { &hf_smtp_rsp_code,
545       { "Response code", "smtp.response.code", FT_UINT32, BASE_DEC, NULL, 0x0,
546         "", HFILL }},
547
548     { &hf_smtp_rsp_parameter,
549       { "Response parameter", "smtp.rsp.parameter", FT_STRING, BASE_NONE, NULL, 0x0,
550         "", HFILL }}
551   };
552   static gint *ett[] = {
553     &ett_smtp
554   };
555   module_t *smtp_module;
556
557   /* No Configuration options to register? */
558
559   proto_smtp = proto_register_protocol("Simple Mail Transfer Protocol",
560                                        "SMTP", "smtp");
561
562   proto_register_field_array(proto_smtp, hf, array_length(hf));
563   proto_register_subtree_array(ett, array_length(ett));
564   register_init_routine(&smtp_init_protocol);
565
566   smtp_module = prefs_register_protocol(proto_smtp, NULL);
567   prefs_register_bool_preference(smtp_module, "desegment_lines",
568     "Desegment all SMTP command and response lines spanning multiple TCP segments",
569     "Whether the SMTP dissector should desegment all command and response lines spanning multiple TCP segments",
570     &smtp_desegment);
571 }
572
573 /* The registration hand-off routine */
574 void
575 proto_reg_handoff_smtp(void)
576 {
577   static int smtp_prefs_initialized = FALSE;
578   static dissector_handle_t smtp_handle;
579   static int tcp_port = 0;
580
581   if (!smtp_prefs_initialized) {
582
583     smtp_handle = create_dissector_handle(dissect_smtp, proto_smtp);
584
585     smtp_prefs_initialized = TRUE;
586
587   }
588   else {
589
590     dissector_delete("tcp.port", tcp_port, smtp_handle);
591
592   }
593
594   tcp_port = global_smtp_tcp_port;
595
596   dissector_add("tcp.port", global_smtp_tcp_port, smtp_handle);
597
598 }