Support for conversations with "wildcard" destination addresses, from
[obnox/wireshark/wip.git] / packet-smtp.c
1 /* packet-smtp.c
2  * Routines for SMTP packet disassembly
3  *
4  * $Id: packet-smtp.c,v 1.7 2000/10/21 05:52:23 guy Exp $
5  *
6  * Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs
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.
15  *
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.
20  *
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif
33
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <time.h>
42 #include <glib.h>
43 #include <string.h>
44 #include "packet.h"
45 #include "conversation.h"
46 #include "resolv.h"
47 #include "prefs.h"
48 #include "strutil.h"
49
50 #define TCP_PORT_SMTP 25
51
52 void proto_reg_handoff_smtp(void);
53
54 static int proto_smtp = -1;
55
56 static int hf_smtp_req = -1;
57 static int hf_smtp_rsp = -1;
58
59 static int ett_smtp = -1;
60
61 static int global_smtp_tcp_port = TCP_PORT_SMTP;
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 struct smtp_request_key {
79   guint32 conversation;
80 };
81
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 */
87 };
88
89 GHashTable *smtp_request_hash = NULL;
90 GMemChunk  *smtp_request_keys = NULL;
91 GMemChunk  *smtp_request_vals = NULL;
92 GMemChunk  *smtp_packet_infos = NULL;
93
94 /* Hash Functions */
95 gint
96 smtp_equal(gconstpointer v, gconstpointer w)
97 {
98   struct smtp_request_key *v1 = (struct smtp_request_key *)v;
99   struct smtp_request_key *v2 = (struct smtp_request_key *)w;
100
101 #if defined(DEBUG_SMTP_HASH)
102   printf("Comparing %08X\n      and %08X\n",
103          v1->conversation, v2->conversation);
104 #endif
105
106   if (v1->conversation == v2->conversation)
107     return 1;
108
109   return 0;
110
111 }
112
113 static guint
114 smtp_hash(gconstpointer v)
115 {
116   struct smtp_request_key *key = (struct smtp_request_key *)v;
117   guint val;
118
119   val = key->conversation;
120
121 #if defined(DEBUG_SMTP_HASH)
122   printf("SMTP Hash calculated as %u\n", val);
123 #endif
124
125   return val;
126
127 }
128
129 static void
130 smtp_init_protocol(void)
131 {
132 #if defined(DEBUG_SMTP_HASH)
133   printf("Initializing SMTP hashtable area\n");
134 #endif
135
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);
144
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);
155
156 }
157
158 static
159 int find_smtp_resp_end(const u_char *pd, int offset)
160 {
161   int cntr = 0;
162
163   /* Look for the CRLF ... but keep in mind the END_OF_FRAME */
164
165   while (END_OF_FRAME >= cntr) {
166
167     if (pd[offset + cntr] == 0x0A) { /* Found it */
168
169       if (END_OF_FRAME >= cntr + 1) cntr++;
170
171       return cntr;
172
173     }
174
175     cntr++;
176
177   }
178
179   return cntr;
180
181 }
182
183 #if 0
184 static void
185 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
186 {
187 #else
188 static void
189 dissect_smtp(const u_char *pd, int offset, frame_data *fd,
190              proto_tree *tree)
191 {
192   /*    tvbuff_t *tvb = tvb_create_from_top(offset);*/
193     packet_info *pinfo = &pi;
194 #endif
195     struct smtp_proto_data  *frame_data;
196     proto_tree              *smtp_tree, *ti;
197     int                     request = 0;
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;
202
203 #if 0
204     CHECK_DISPLAY_AS_DATA(proto_smtp, tvb, pinfo, tree);
205 #else
206     OLD_CHECK_DISPLAY_AS_DATA(proto_smtp, pd, offset, fd, tree);
207 #endif
208
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.
211      * 
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.
217      */
218
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 
221      * info first.
222      */
223
224     /* SMTP messages have a simple format ... */
225
226     request = pinfo -> destport == TCP_PORT_SMTP;
227     cmd = pd + offset;   /* FIXME: What about tvb */
228
229     frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
230
231     if (!frame_data) {
232
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,
238                                         0);
239
240       }
241
242       /* 
243        * Check for and insert an entry in the request table if does not exist
244        */
245       request_key.conversation = conversation->index;
246
247       request_val = (struct smtp_request_val *)g_hash_table_lookup(smtp_request_hash, &request_key);
248       
249       if (!request_val) { /* Create one */
250
251         new_request_key = g_mem_chunk_alloc(smtp_request_keys);
252         new_request_key->conversation = conversation->index;
253
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;
259
260         g_hash_table_insert(smtp_request_hash, new_request_key, request_val);
261
262       }
263
264       /* 
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 ...
269        */
270
271       if (request_val->data_seen && !request_val->processed) {
272
273         /*
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.
277          */
278
279         if ((request_val->crlf_seen && strncmp(pd + offset, ".\r\n", 3) == 0) ||
280             (strncmp(pd + offset, "\r\n.\r\n", 5) == 0)) {
281
282           request_val->eom_seen = 1;
283
284         }
285
286         if (strncmp(pd + offset + END_OF_FRAME - 2, "\r\n", 2) == 0) {
287
288           request_val->crlf_seen = 1;
289
290         }
291         else {
292
293           request_val->crlf_seen = 0;
294
295         }
296       }
297
298     /*
299      * OK, Check if we have seen a DATA request. We do it here for 
300      * simplicity, but we have to be careful below.
301      */
302
303       if (request) {
304
305         frame_data = g_mem_chunk_alloc(smtp_packet_infos);
306
307         if (!request_val->processed) { 
308           if (strncmp(pd + offset, "DATA", 4)==0) {
309
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);
313
314           } else if ((!request_val->eom_seen) &&
315                      (request_val->data_seen)) {
316          
317             /* Now, create the frame data for this frame ... */
318
319             frame_data->pdu_type = SMTP_PDU_MESSAGE;
320             p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
321
322           } else if (request_val->eom_seen) { /* Seen the EOM */
323
324             /* Now, we clear the eom_seen and data_seen bits */
325
326             request_val->eom_seen = request_val->data_seen = 0;
327             request_val->processed = 1;   /* We have seen all the packets */
328
329             /* And add the packet data */
330
331             frame_data->pdu_type = SMTP_PDU_EOM;
332             p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
333
334           } else {
335
336             frame_data->pdu_type = SMTP_PDU_CMD;
337             p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
338
339           }
340         }
341       }
342     }
343
344     /* 
345      * From here, we simply add items to the tree and info to the info 
346      * fields ...
347      */
348
349     if (check_col(fd, COL_PROTOCOL))
350       col_add_str(fd, COL_PROTOCOL, "SMTP");
351
352     if (check_col(fd, COL_INFO)) {  /* Add the appropriate type here */
353
354       /*
355        * If it is a request, we have to look things up, otherwise, just
356        * display the right things 
357        */
358
359       if (request) {
360
361         /* We must have frame_data here ... */
362
363         switch (frame_data->pdu_type) {
364         case SMTP_PDU_MESSAGE:
365
366           col_add_fstr(pinfo->fd, COL_INFO, "Message: %s", format_text(cmd, END_OF_FRAME));
367           break;
368
369         case SMTP_PDU_EOM:
370
371           col_add_fstr(pinfo->fd, COL_INFO, "EOM: %s", format_text(cmd, END_OF_FRAME));
372           break;
373
374         case SMTP_PDU_CMD:
375
376           col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
377
378         }
379
380       }
381       else {
382
383         col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
384
385       }
386     }
387
388     if (tree) { /* Build the tree info ... */
389
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);
394       if (request) {
395
396         /* 
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
399          * and not eom_seen.
400          *
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.
404          *
405          * On the first pass, we will not have any info on the packets
406          * On second and subsequent passes, we will.
407          */
408
409         switch (frame_data->pdu_type) {
410
411         case SMTP_PDU_MESSAGE:
412
413           proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "Message: %s", format_text(cmd, END_OF_FRAME));
414
415           break;
416
417         case SMTP_PDU_EOM:
418
419           proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "EOM: %s", format_text(cmd, END_OF_FRAME));
420
421           break;
422
423         case SMTP_PDU_CMD:
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));
426
427         }
428
429       }
430       else {
431
432         /* Must consider a multi-line response here ... */
433
434         while (END_OF_FRAME >= 4 && pd[offset + 3] == '-') {
435           int resp_len = find_smtp_resp_end(pd, offset);
436
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));
439
440           offset += resp_len;
441         }
442
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));
445         
446       }
447     }
448 }
449
450 /* Register all the bits needed by the filtering engine */
451
452 void
453 proto_register_smtp(void)
454 {
455   static hf_register_info hf[] = {
456     { &hf_smtp_req,
457       { "Request", "smtp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
458
459     { &hf_smtp_rsp,
460       { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
461   };
462   static gint *ett[] = {
463     &ett_smtp
464   };
465   /*module_t *smtp_module = NULL; */  /* Not yet used */
466
467   /* No Configuration options to register? */
468
469   proto_smtp = proto_register_protocol("Simple Mail Transfer Protocol", "smtp");
470
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);
474
475 }
476
477 /* The registration hand-off routine */
478 void
479 proto_reg_handoff_smtp(void)
480 {
481   static int smtp_prefs_initialized = FALSE;
482   static int tcp_port = 0;
483
484   if (smtp_prefs_initialized) {
485
486     old_dissector_delete("tcp.port", tcp_port, dissect_smtp);
487
488   }
489   else {
490
491     smtp_prefs_initialized = TRUE;
492
493   }
494
495   tcp_port = global_smtp_tcp_port;
496
497   old_dissector_add("tcp.port", global_smtp_tcp_port, dissect_smtp);
498
499 }