Fix a minor spelling mistake ...
[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.5 2000/08/26 11:25:28 sharpe 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
49 #define TCP_PORT_SMTP 25
50
51 void proto_reg_handoff_smtp(void);
52
53 static int proto_smtp = -1;
54
55 static int hf_smtp_req = -1;
56 static int hf_smtp_rsp = -1;
57
58 static int ett_smtp = -1;
59
60 static int global_smtp_tcp_port = TCP_PORT_SMTP;
61
62 /*
63  * A CMD is an SMTP command, MESSAGE is the message portion, and EOM is the
64  * last part of a message
65  */
66
67 #define SMTP_PDU_CMD     0   
68 #define SMTP_PDU_MESSAGE 1
69 #define SMTP_PDU_EOM     2
70
71 struct smtp_proto_data {
72   guint16 pdu_type;
73 };
74
75 static int smtp_packet_init_count = 100;
76
77 struct smtp_request_key {
78   guint32 conversation;
79 };
80
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 */
86 };
87
88 GHashTable *smtp_request_hash = NULL;
89 GMemChunk  *smtp_request_keys = NULL;
90 GMemChunk  *smtp_request_vals = NULL;
91 GMemChunk  *smtp_packet_infos = NULL;
92
93 /* Hash Functions */
94 gint
95 smtp_equal(gconstpointer v, gconstpointer w)
96 {
97   struct smtp_request_key *v1 = (struct smtp_request_key *)v;
98   struct smtp_request_key *v2 = (struct smtp_request_key *)w;
99
100 #if defined(DEBUG_SMTP_HASH)
101   printf("Comparing %08X\n      and %08X\n",
102          v1->conversation, v2->conversation);
103 #endif
104
105   if (v1->conversation == v2->conversation)
106     return 1;
107
108   return 0;
109
110 }
111
112 static guint
113 smtp_hash(gconstpointer v)
114 {
115   struct smtp_request_key *key = (struct smtp_request_key *)v;
116   guint val;
117
118   val = key->conversation;
119
120 #if defined(DEBUG_SMTP_HASH)
121   printf("SMTP Hash calculated as %u\n", val);
122 #endif
123
124   return val;
125
126 }
127
128 static void
129 smtp_init_protocol(void)
130 {
131 #if defined(DEBUG_SMTP_HASH)
132   printf("Initializing SMTP hashtable area\n");
133 #endif
134
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);
143
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);
154
155 }
156
157 static
158 int find_smtp_resp_end(const u_char *pd, int offset)
159 {
160   int cntr = 0;
161
162   /* Look for the CRLF ... but keep in mind the END_OF_FRAME */
163
164   while (END_OF_FRAME >= cntr) {
165
166     if (pd[offset + cntr] == 0x0A) { /* Found it */
167
168       if (END_OF_FRAME >= cntr + 1) cntr++;
169
170       return cntr;
171
172     }
173
174     cntr++;
175
176   }
177
178   return cntr;
179
180 }
181
182 #if 0
183 static void
184 dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
185 {
186 #else
187 static void
188 dissect_smtp(const u_char *pd, int offset, frame_data *fd,
189              proto_tree *tree)
190 {
191   /*    tvbuff_t *tvb = tvb_create_from_top(offset);*/
192     packet_info *pinfo = &pi;
193 #endif
194     struct smtp_proto_data  *frame_data;
195     proto_tree              *smtp_tree, *ti;
196     int                     request = 0;
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;
201
202 #if 0
203     CHECK_DISPLAY_AS_DATA(proto_smtp, tvb, pinfo, tree);
204 #else
205     OLD_CHECK_DISPLAY_AS_DATA(proto_smtp, pd, offset, fd, tree);
206 #endif
207
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.
210      * 
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.
216      */
217
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 
220      * info first.
221      */
222
223     /* SMTP messages have a simple format ... */
224
225     request = pinfo -> destport == TCP_PORT_SMTP;
226     cmd = pd + offset;   /* FIXME: What about tvb */
227
228     frame_data = p_get_proto_data(pinfo->fd, proto_smtp);
229
230     if (!frame_data) {
231
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);
237
238       }
239
240       /* 
241        * Check for and insert an entry in the request table if does not exist
242        */
243       request_key.conversation = conversation->index;
244
245       request_val = (struct smtp_request_val *)g_hash_table_lookup(smtp_request_hash, &request_key);
246       
247       if (!request_val) { /* Create one */
248
249         new_request_key = g_mem_chunk_alloc(smtp_request_keys);
250         new_request_key->conversation = conversation->index;
251
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;
257
258         g_hash_table_insert(smtp_request_hash, new_request_key, request_val);
259
260       }
261
262       /* 
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 ...
267        */
268
269       if (request_val->data_seen && !request_val->processed) {
270
271         /*
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.
275          */
276
277         if ((request_val->crlf_seen && strncmp(pd + offset, ".\r\n", 3) == 0) ||
278             (strncmp(pd + offset, "\r\n.\r\n", 5) == 0)) {
279
280           request_val->eom_seen = 1;
281
282         }
283
284         if (strncmp(pd + offset + END_OF_FRAME - 2, "\r\n", 2) == 0) {
285
286           request_val->crlf_seen = 1;
287
288         }
289         else {
290
291           request_val->crlf_seen = 0;
292
293         }
294       }
295
296     /*
297      * OK, Check if we have seen a DATA request. We do it here for 
298      * simplicity, but we have to be careful below.
299      */
300
301       if (request) {
302
303         frame_data = g_mem_chunk_alloc(smtp_packet_infos);
304
305         if (!request_val->processed) { 
306           if (strncmp(pd + offset, "DATA", 4)==0) {
307
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);
311
312           } else if ((!request_val->eom_seen) &&
313                      (request_val->data_seen)) {
314          
315             /* Now, create the frame data for this frame ... */
316
317             frame_data->pdu_type = SMTP_PDU_MESSAGE;
318             p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
319
320           } else if (request_val->eom_seen) { /* Seen the EOM */
321
322             /* Now, we clear the eom_seen and data_seen bits */
323
324             request_val->eom_seen = request_val->data_seen = 0;
325             request_val->processed = 1;   /* We have seen all the packets */
326
327             /* And add the packet data */
328
329             frame_data->pdu_type = SMTP_PDU_EOM;
330             p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
331
332           } else {
333
334             frame_data->pdu_type = SMTP_PDU_CMD;
335             p_add_proto_data(pinfo->fd, proto_smtp, frame_data);
336
337           }
338         }
339       }
340     }
341
342     /* 
343      * From here, we simply add items to the tree and info to the info 
344      * fields ...
345      */
346
347     if (check_col(fd, COL_PROTOCOL))
348       col_add_str(fd, COL_PROTOCOL, "SMTP");
349
350     if (check_col(fd, COL_INFO)) {  /* Add the appropriate type here */
351
352       /*
353        * If it is a request, we have to look things up, otherwise, just
354        * display the right things 
355        */
356
357       if (request) {
358
359         /* We must have frame_data here ... */
360
361         switch (frame_data->pdu_type) {
362         case SMTP_PDU_MESSAGE:
363
364           col_add_fstr(pinfo->fd, COL_INFO, "Message: %s", format_text(cmd, END_OF_FRAME));
365           break;
366
367         case SMTP_PDU_EOM:
368
369           col_add_fstr(pinfo->fd, COL_INFO, "EOM: %s", format_text(cmd, END_OF_FRAME));
370           break;
371
372         case SMTP_PDU_CMD:
373
374           col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
375
376         }
377
378       }
379       else {
380
381         col_add_fstr(pinfo->fd, COL_INFO, "%s", format_text(cmd, END_OF_FRAME));
382
383       }
384     }
385
386     if (tree) { /* Build the tree info ... */
387
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);
392       if (request) {
393
394         /* 
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
397          * and not eom_seen.
398          *
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.
402          *
403          * On the first pass, we will not have any info on the packets
404          * On second and subsequent passes, we will.
405          */
406
407         switch (frame_data->pdu_type) {
408
409         case SMTP_PDU_MESSAGE:
410
411           proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "Message: %s", format_text(cmd, END_OF_FRAME));
412
413           break;
414
415         case SMTP_PDU_EOM:
416
417           proto_tree_add_protocol_format(smtp_tree, proto_smtp, NullTVB, offset, END_OF_FRAME, "EOM: %s", format_text(cmd, END_OF_FRAME));
418
419           break;
420
421         case SMTP_PDU_CMD:
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));
424
425         }
426
427       }
428       else {
429
430         /* Must consider a multi-line response here ... */
431
432         while (END_OF_FRAME >= 4 && pd[offset + 3] == '-') {
433           int resp_len = find_smtp_resp_end(pd, offset);
434
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));
437
438           offset += resp_len;
439         }
440
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));
443         
444       }
445     }
446 }
447
448 /* Register all the bits needed by the filtering engine */
449
450 void
451 proto_register_smtp(void)
452 {
453   static hf_register_info hf[] = {
454     { &hf_smtp_req,
455       { "Request", "smtp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
456
457     { &hf_smtp_rsp,
458       { "Response", "smtp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, ""}},
459   };
460   static gint *ett[] = {
461     &ett_smtp
462   };
463   /*module_t *smtp_module = NULL; */  /* Not yet used */
464
465   /* No Configuration options to register? */
466
467   proto_smtp = proto_register_protocol("Simple Mail Transfer Protocol", "smtp");
468
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);
472
473 }
474
475 /* The registration hand-off routine */
476 void
477 proto_reg_handoff_smtp(void)
478 {
479   static int smtp_prefs_initialized = FALSE;
480   static int tcp_port = 0;
481
482   if (smtp_prefs_initialized) {
483
484     old_dissector_delete("tcp.port", tcp_port, dissect_smtp);
485
486   }
487   else {
488
489     smtp_prefs_initialized = TRUE;
490
491   }
492
493   tcp_port = global_smtp_tcp_port;
494
495   old_dissector_add("tcp.port", global_smtp_tcp_port, dissect_smtp);
496
497 }