From Didier Gautheron:
[obnox/wireshark/wip.git] / epan / dissectors / packet-pop.c
1 /* packet-pop.c
2  * Routines for pop packet dissection
3  * RFC 1939
4  * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * Copied from packet-tftp.c
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <stdlib.h>
34
35 #include <string.h>
36 #include <glib.h>
37 #include <epan/packet.h>
38 #include <epan/strutil.h>
39 #include <epan/conversation.h>
40 #include <epan/prefs.h>
41 #include <epan/reassemble.h>
42 #include "packet-ssl.h"
43
44 static int proto_pop = -1;
45
46 static int hf_pop_response = -1;
47 static int hf_pop_response_indicator = -1;
48 static int hf_pop_response_description = -1;
49 static int hf_pop_response_data = -1;
50
51 static int hf_pop_request = -1;
52 static int hf_pop_request_command = -1;
53 static int hf_pop_request_parameter = -1;
54 static int hf_pop_request_data = -1;
55
56 static int hf_pop_data_fragments = -1;
57 static int hf_pop_data_fragment = -1;
58 static int hf_pop_data_fragment_overlap = -1;
59 static int hf_pop_data_fragment_overlap_conflicts = -1;
60 static int hf_pop_data_fragment_multiple_tails = -1;
61 static int hf_pop_data_fragment_too_long_fragment = -1;
62 static int hf_pop_data_fragment_error = -1;
63 static int hf_pop_data_reassembled_in = -1;
64
65 static gint ett_pop = -1;
66 static gint ett_pop_reqresp = -1;
67
68 static gint ett_pop_data_fragment = -1;
69 static gint ett_pop_data_fragments = -1;
70
71 static dissector_handle_t data_handle;
72 static dissector_handle_t imf_handle = NULL;
73
74 #define TCP_PORT_POP                    110
75 #define TCP_PORT_SSL_POP                995
76
77 /* desegmentation of POP command and response lines */
78 static gboolean pop_data_desegment = TRUE;
79
80 static GHashTable *pop_data_segment_table = NULL;
81 static GHashTable *pop_data_reassembled_table = NULL;
82
83 static const fragment_items pop_data_frag_items = {
84   /* Fragment subtrees */
85   &ett_pop_data_fragment,
86   &ett_pop_data_fragments,
87   /* Fragment fields */
88   &hf_pop_data_fragments,
89   &hf_pop_data_fragment,
90   &hf_pop_data_fragment_overlap,
91   &hf_pop_data_fragment_overlap_conflicts,
92   &hf_pop_data_fragment_multiple_tails,
93   &hf_pop_data_fragment_too_long_fragment,
94   &hf_pop_data_fragment_error,
95   /* Reassembled in field */
96   &hf_pop_data_reassembled_in,
97   /* Tag */
98   "DATA fragments"
99 };
100
101 struct pop_proto_data {
102   guint16 conversation_id;
103   gboolean more_frags;
104 };
105
106 struct pop_data_val {
107   gboolean msg_request;
108   guint32 msg_read_len;  /* Length of RETR message read so far */
109   guint32 msg_tot_len;   /* Total length of RETR message */
110 };
111
112
113
114 static gboolean response_is_continuation(const guchar *data);
115
116 static void
117 dissect_pop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
118 {
119   struct pop_proto_data  *frame_data;
120   gboolean               is_request;
121   gboolean               is_continuation;
122   proto_tree             *pop_tree, *reqresp_tree;
123   proto_item             *ti;
124   gint                   offset = 0;
125   const guchar           *line;
126   gint                   next_offset;
127   int                    linelen;
128   int                    tokenlen;
129   const guchar           *next_token;
130   fragment_data          *frag_msg = NULL;
131   tvbuff_t               *next_tvb = NULL;
132   conversation_t         *conversation = NULL;
133   struct pop_data_val    *data_val = NULL;
134   gint                   length_remaining;
135
136   col_set_str(pinfo->cinfo, COL_PROTOCOL, "POP");
137
138   /*
139    * Find the end of the first line.
140    *
141    * Note that "tvb_find_line_end()" will return a value that is
142    * not longer than what's in the buffer, so the "tvb_get_ptr()"
143    * call won't throw an exception.
144    */
145   linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
146   line = tvb_get_ptr(tvb, offset, linelen);
147
148   if (pinfo->match_port == pinfo->destport) {
149     is_request = TRUE;
150     is_continuation = FALSE;
151   } else {
152     is_request = FALSE;
153     is_continuation = response_is_continuation(line);
154   }
155
156   frame_data = p_get_proto_data(pinfo->fd, proto_pop);
157
158   if (!frame_data) {
159
160     conversation = find_conversation(pinfo->fd->num, 
161              &pinfo->src, &pinfo->dst, 
162              pinfo->ptype,
163              pinfo->srcport, pinfo->destport, 0);
164
165     if (conversation == NULL) { /* No conversation, create one */
166       conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
167                                       pinfo->ptype, pinfo->srcport,
168                                       pinfo->destport, 0);
169     }
170
171     data_val = conversation_get_proto_data(conversation, proto_pop);
172
173     if (!data_val) {
174
175       /*
176        * No - create one and attach it.
177        */
178       data_val = se_alloc(sizeof(struct pop_data_val));
179       data_val->msg_request = FALSE;
180       data_val->msg_read_len = 0;
181       data_val->msg_tot_len = 0;
182       
183       conversation_add_proto_data(conversation, proto_pop, data_val);
184     }
185   }
186
187   if (check_col(pinfo->cinfo, COL_INFO)) {
188     /*
189      * Put the first line from the buffer into the summary
190      * if it's a POP request or reply (but leave out the
191      * line terminator).
192      * Otherwise, just call it a continuation.
193      */
194     if (is_continuation) {
195       length_remaining = tvb_length_remaining(tvb, offset);
196       col_add_fstr(pinfo->cinfo, COL_INFO, "S: DATA fragment, %d byte%s", 
197                    length_remaining, plurality (length_remaining, "", "s"));
198     }
199     else
200       col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", is_request ? "C" : "S",
201                    format_text(line, linelen));
202   }
203
204   ti = proto_tree_add_item(tree, proto_pop, tvb, offset, -1, FALSE);
205   pop_tree = proto_item_add_subtree(ti, ett_pop);
206
207   if (is_continuation) {
208
209     if (pop_data_desegment) {
210
211       if (!frame_data) {
212
213         data_val->msg_read_len += tvb_length(tvb);
214
215         frame_data = se_alloc(sizeof(struct pop_proto_data));
216
217         frame_data->conversation_id = conversation->index;
218         frame_data->more_frags = data_val->msg_read_len < data_val->msg_tot_len;
219
220         p_add_proto_data(pinfo->fd, proto_pop, frame_data);  
221       }
222
223       frag_msg = fragment_add_seq_next(tvb, 0, pinfo, 
224                                        frame_data->conversation_id, 
225                                        pop_data_segment_table, 
226                                        pop_data_reassembled_table, 
227                                        tvb_length(tvb), 
228                                        frame_data->more_frags);
229
230       next_tvb = process_reassembled_data(tvb, offset, pinfo, 
231                                           "Reassembled DATA",
232                                           frag_msg, &pop_data_frag_items, 
233                                           NULL, pop_tree);
234
235       if (next_tvb) {
236
237         if (imf_handle)
238           call_dissector(imf_handle, next_tvb, pinfo, tree);
239
240         if (data_val) {
241           /* we have read everything - reset */
242           
243           data_val->msg_read_len = 0;
244           data_val->msg_tot_len = 0; 
245         }
246         pinfo->fragmented = FALSE;
247       } else {
248         pinfo->fragmented = TRUE;
249       }
250
251     } else {
252
253       /*
254        * Put the whole packet into the tree as data.
255        */
256       call_dissector(data_handle,tvb, pinfo, pop_tree);
257     
258     }
259     return;
260   }
261
262   /*
263    * Put the line into the protocol tree.
264    */
265   ti = proto_tree_add_string_format(pop_tree,
266                                     (is_request) ?
267                                         hf_pop_request :
268                                         hf_pop_response,
269                                     tvb, offset,
270                                     next_offset - offset,
271                                     "", "%s",
272                                     tvb_format_text(tvb, offset, next_offset - offset));
273   reqresp_tree = proto_item_add_subtree(ti, ett_pop_reqresp);
274
275   /*
276    * Extract the first token, and, if there is a first
277    * token, add it as the request or reply code.
278    */
279   tokenlen = get_token_len(line, line + linelen, &next_token);
280   if (tokenlen != 0) {
281     proto_tree_add_item(reqresp_tree,
282                         (is_request) ?
283                             hf_pop_request_command :
284                             hf_pop_response_indicator,
285                         tvb, offset, tokenlen, FALSE);
286
287     if (data_val) {
288       if (is_request) {
289         /* see if this is RETR or TOP command */
290         if (g_ascii_strncasecmp(line, "RETR", 4) == 0 ||
291            g_ascii_strncasecmp(line, "TOP", 3) == 0)
292           /* the next response will tell us how many bytes */
293           data_val->msg_request = TRUE;
294       } else {
295         if (data_val->msg_request) {
296           /* this is a response to a RETR or TOP command */
297
298           if (g_ascii_strncasecmp(line, "+OK ", 4) == 0) {
299             /* the message will be sent - work out how many bytes */
300             data_val->msg_read_len = 0;
301             data_val->msg_tot_len = atoi(line + 4);
302           }
303           data_val->msg_request = FALSE;
304         }
305       }
306     }
307
308     offset += (gint) (next_token - line);
309     linelen -= (int) (next_token - line);
310   }
311
312
313   if (tree) { 
314     /*
315      * Add the rest of the first line as request or
316      * reply param/description.
317      */
318     if (linelen != 0) {
319       proto_tree_add_item(reqresp_tree,
320                           (is_request) ?
321                               hf_pop_request_parameter :
322                               hf_pop_response_description,
323                           tvb, offset, linelen, FALSE);
324     }
325     offset = next_offset;
326
327     /*
328      * Show the rest of the request or response as text,
329      * a line at a time.
330      */
331     while (tvb_offset_exists(tvb, offset)) {
332       /*
333        * Find the end of the line.
334        */
335       tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
336
337       /*
338        * Put this line.
339        */
340       proto_tree_add_string_format(pop_tree,
341                                    (is_request) ?
342                                        hf_pop_request_data :
343                                        hf_pop_response_data,
344                                    tvb, offset,
345                                    next_offset - offset,
346                                    "", "%s",
347                                    tvb_format_text(tvb, offset, next_offset - offset));
348       offset = next_offset;
349     }
350   } 
351 }
352
353 static gboolean response_is_continuation(const guchar *data)
354 {
355   if (strncmp(data, "+OK", strlen("+OK")) == 0)
356     return FALSE;
357
358   if (strncmp(data, "-ERR", strlen("-ERR")) == 0)
359     return FALSE;
360
361   return TRUE;
362 }
363
364 static void pop_data_reassemble_init (void)
365 {
366   fragment_table_init (&pop_data_segment_table);
367   reassembled_table_init (&pop_data_reassembled_table);
368 }
369
370 void
371 proto_register_pop(void)
372 {
373   static hf_register_info hf[] = {
374     { &hf_pop_response,
375       { "Response",           "pop.response",
376         FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
377     { &hf_pop_response_indicator,
378       { "Response indicator",           "pop.response.indicator",
379          FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
380     { &hf_pop_response_description,
381       { "Response description",           "pop.response.description",
382          FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
383     { &hf_pop_response_data,
384       { "Data",           "pop.response.data",
385         FT_STRING, BASE_NONE, NULL, 0x0, "Response Data", HFILL }},
386     { &hf_pop_request,
387       { "Request",           "pop.request",
388          FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
389     { &hf_pop_request_command,
390       { "Request command",            "pop.request.command",
391         FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
392     { &hf_pop_request_parameter,
393       { "Request parameter",            "pop.request.parameter",
394         FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
395     { &hf_pop_request_data,
396       { "Data",           "pop.request.data",
397          FT_STRING, BASE_NONE, NULL, 0x0, "Request data", HFILL }},
398     /* Fragment entries */
399     { &hf_pop_data_fragments,
400       { "DATA fragments", "pop.data.fragments", FT_NONE, BASE_NONE,
401         NULL, 0x00, "Message fragments", HFILL } },
402     { &hf_pop_data_fragment,
403       { "DATA fragment", "pop.data.fragment", FT_FRAMENUM, BASE_NONE,
404         NULL, 0x00, "Message fragment", HFILL } },
405     { &hf_pop_data_fragment_overlap,
406       { "DATA fragment overlap", "pop.data.fragment.overlap", FT_BOOLEAN,
407         BASE_NONE, NULL, 0x0, "Message fragment overlap", HFILL } },
408     { &hf_pop_data_fragment_overlap_conflicts,
409       { "DATA fragment overlapping with conflicting data",
410         "pop.data.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE, NULL,
411         0x0, "Message fragment overlapping with conflicting data", HFILL } },
412     { &hf_pop_data_fragment_multiple_tails,
413       { "DATA has multiple tail fragments",
414         "pop.data.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE,
415         NULL, 0x0, "Message has multiple tail fragments", HFILL } },
416     { &hf_pop_data_fragment_too_long_fragment,
417       { "DATA fragment too long", "pop.data.fragment.too_long_fragment",
418         FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Message fragment too long",
419         HFILL } },
420     { &hf_pop_data_fragment_error,
421       { "DATA defragmentation error", "pop.data.fragment.error", FT_FRAMENUM,
422         BASE_NONE, NULL, 0x00, "Message defragmentation error", HFILL } },
423     { &hf_pop_data_reassembled_in,
424       { "Reassembled DATA in frame", "pop.data.reassembled.in", FT_FRAMENUM, BASE_NONE,
425         NULL, 0x00, "This DATA fragment is reassembled in this frame", HFILL } },
426   };
427
428   static gint *ett[] = {
429     &ett_pop,
430     &ett_pop_reqresp,
431     &ett_pop_data_fragment,
432     &ett_pop_data_fragments
433   };
434   module_t *pop_module;
435
436
437   proto_pop = proto_register_protocol("Post Office Protocol", "POP", "pop");
438   register_dissector("pop", dissect_pop, proto_pop);
439   proto_register_field_array(proto_pop, hf, array_length(hf));
440   proto_register_subtree_array(ett, array_length(ett));
441   register_init_routine (&pop_data_reassemble_init);
442
443   /* Preferences */
444   pop_module = prefs_register_protocol(proto_pop, NULL);
445
446   prefs_register_bool_preference(pop_module, "desegment_data",
447     "Reassemble POP RETR and TOP responses spanning multiple TCP segments",
448     "Whether the POP dissector should reassemble RETR and TOP responses and spanning multiple TCP segments."
449     " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
450     &pop_data_desegment);
451 }
452
453 void
454 proto_reg_handoff_pop(void)
455 {
456   dissector_handle_t pop_handle;
457
458   pop_handle = find_dissector("pop");
459   dissector_add("tcp.port", TCP_PORT_POP, pop_handle);
460   ssl_dissector_add(TCP_PORT_SSL_POP, "pop", TRUE);
461   data_handle = find_dissector("data");
462
463   /* find the IMF dissector */
464   imf_handle = find_dissector("imf");
465
466 }