2 * Routines for pop packet dissection
4 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * Copied from packet-tftp.c
12 * SPDX-License-Identifier: GPL-2.0-or-later
19 #include <epan/packet.h>
20 #include <epan/strutil.h>
21 #include <epan/conversation.h>
22 #include <epan/prefs.h>
23 #include <epan/reassemble.h>
24 #include <epan/proto_data.h>
25 #include <epan/expert.h>
27 #include <wsutil/str_util.h>
28 #include <wsutil/strtoi.h>
30 #include <ui/tap-credentials.h>
33 #include "packet-tls.h"
34 #include "packet-tls-utils.h"
36 void proto_register_pop(void);
37 void proto_reg_handoff_pop(void);
41 static int credentials_tap
;
43 static int hf_pop_response
;
44 static int hf_pop_response_indicator
;
45 static int hf_pop_response_description
;
46 static int hf_pop_response_data
;
48 static int hf_pop_request
;
49 static int hf_pop_request_command
;
50 static int hf_pop_request_parameter
;
51 static int hf_pop_request_data
;
53 static int hf_pop_data_fragments
;
54 static int hf_pop_data_fragment
;
55 static int hf_pop_data_fragment_overlap
;
56 static int hf_pop_data_fragment_overlap_conflicts
;
57 static int hf_pop_data_fragment_multiple_tails
;
58 static int hf_pop_data_fragment_too_long_fragment
;
59 static int hf_pop_data_fragment_error
;
60 static int hf_pop_data_fragment_count
;
61 static int hf_pop_data_reassembled_in
;
62 static int hf_pop_data_reassembled_length
;
64 static expert_field ei_pop_resp_tot_len_invalid
;
67 static int ett_pop_reqresp
;
69 static int ett_pop_data_fragment
;
70 static int ett_pop_data_fragments
;
72 static dissector_handle_t pop_handle
;
73 static dissector_handle_t imf_handle
;
74 static dissector_handle_t tls_handle
;
76 #define TCP_PORT_POP 110
77 #define TCP_PORT_SSL_POP 995
79 /* desegmentation of POP command and response lines */
80 static bool pop_data_desegment
= true;
82 static reassembly_table pop_data_reassembly_table
;
84 static const fragment_items pop_data_frag_items
= {
85 /* Fragment subtrees */
86 &ett_pop_data_fragment
,
87 &ett_pop_data_fragments
,
89 &hf_pop_data_fragments
,
90 &hf_pop_data_fragment
,
91 &hf_pop_data_fragment_overlap
,
92 &hf_pop_data_fragment_overlap_conflicts
,
93 &hf_pop_data_fragment_multiple_tails
,
94 &hf_pop_data_fragment_too_long_fragment
,
95 &hf_pop_data_fragment_error
,
96 &hf_pop_data_fragment_count
,
97 /* Reassembled in field */
98 &hf_pop_data_reassembled_in
,
99 /* Reassembled length field */
100 &hf_pop_data_reassembled_length
,
101 /* Reassembled data field */
107 struct pop_proto_data
{
108 uint16_t conversation_id
;
112 struct pop_data_val
{
114 uint32_t msg_read_len
; /* Length of RETR message read so far */
115 uint32_t msg_tot_len
; /* Total length of RETR message */
116 bool stls_request
; /* Received STLS request */
118 unsigned username_num
;
122 pop_arg_type_unknown
,
123 pop_arg_type_username
,
124 pop_arg_type_password
127 static bool response_is_continuation(const unsigned char *data
);
130 dissect_pop(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
132 struct pop_proto_data
*frame_data_p
;
134 bool is_continuation
;
135 proto_tree
*pop_tree
, *reqresp_tree
;
142 const unsigned char *next_token
;
143 fragment_head
*frag_msg
= NULL
;
144 tvbuff_t
*next_tvb
= NULL
;
145 conversation_t
*conversation
= NULL
;
146 struct pop_data_val
*data_val
= NULL
;
147 int length_remaining
;
148 pop_arg_type_t pop_arg_type
= pop_arg_type_unknown
;
150 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "POP");
152 frame_data_p
= (struct pop_proto_data
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_pop
, 0);
154 conversation
= find_or_create_conversation(pinfo
);
155 data_val
= (struct pop_data_val
*)conversation_get_proto_data(conversation
, proto_pop
);
159 * No conversation - create one and attach it.
161 data_val
= wmem_new0(wmem_file_scope(), struct pop_data_val
);
163 conversation_add_proto_data(conversation
, proto_pop
, data_val
);
167 * Find the end of the first line.
169 linelen
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
170 line
= (unsigned char*)wmem_alloc(pinfo
->pool
, linelen
+1);
171 tvb_memcpy(tvb
, line
, offset
, linelen
);
172 line
[linelen
] = '\0';
174 if (pinfo
->match_uint
== pinfo
->destport
) {
176 is_continuation
= false;
179 is_continuation
= response_is_continuation(line
);
183 * Put the first line from the buffer into the summary
184 * if it's a POP request or reply (but leave out the
186 * Otherwise, just call it a continuation.
188 if (is_continuation
) {
189 length_remaining
= tvb_reported_length_remaining(tvb
, offset
);
190 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "S: DATA fragment, %d byte%s",
191 length_remaining
, plurality (length_remaining
, "", "s"));
194 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s: %s", is_request
? "C" : "S",
195 format_text(pinfo
->pool
, line
, linelen
));
197 ti
= proto_tree_add_item(tree
, proto_pop
, tvb
, offset
, -1, ENC_NA
);
198 pop_tree
= proto_item_add_subtree(ti
, ett_pop
);
200 if (is_continuation
) {
202 if (pop_data_desegment
) {
206 data_val
->msg_read_len
+= tvb_reported_length(tvb
);
208 frame_data_p
= wmem_new(wmem_file_scope(), struct pop_proto_data
);
210 frame_data_p
->conversation_id
= conversation
->conv_index
;
211 frame_data_p
->more_frags
= data_val
->msg_read_len
< data_val
->msg_tot_len
;
213 p_add_proto_data(wmem_file_scope(), pinfo
, proto_pop
, 0, frame_data_p
);
216 frag_msg
= fragment_add_seq_next(&pop_data_reassembly_table
, tvb
, 0,
218 frame_data_p
->conversation_id
,
220 tvb_reported_length(tvb
),
221 frame_data_p
->more_frags
);
223 next_tvb
= process_reassembled_data(tvb
, offset
, pinfo
,
225 frag_msg
, &pop_data_frag_items
,
231 call_dissector(imf_handle
, next_tvb
, pinfo
, tree
);
234 /* we have read everything - reset */
236 data_val
->msg_read_len
= 0;
237 data_val
->msg_tot_len
= 0;
239 pinfo
->fragmented
= false;
241 pinfo
->fragmented
= true;
247 * Put the whole packet into the tree as data.
249 call_data_dissector(tvb
, pinfo
, pop_tree
);
252 return tvb_captured_length(tvb
);
256 * Put the line into the protocol tree.
258 ti
= proto_tree_add_string_format(pop_tree
,
263 next_offset
- offset
,
265 tvb_format_text(pinfo
->pool
, tvb
, offset
, next_offset
- offset
));
266 reqresp_tree
= proto_item_add_subtree(ti
, ett_pop_reqresp
);
269 * Extract the first token, and, if there is a first
270 * token, add it as the request or reply code.
272 tokenlen
= get_token_len(line
, line
+ linelen
, &next_token
);
274 proto_tree_add_item(reqresp_tree
,
276 hf_pop_request_command
:
277 hf_pop_response_indicator
,
278 tvb
, offset
, tokenlen
, ENC_ASCII
|ENC_NA
);
282 /* see if this is RETR or TOP command */
283 if (g_ascii_strncasecmp(line
, "RETR", 4) == 0 ||
284 g_ascii_strncasecmp(line
, "TOP", 3) == 0)
285 /* the next response will tell us how many bytes */
286 data_val
->msg_request
= true;
288 if (g_ascii_strncasecmp(line
, "STLS", 4) == 0) {
289 data_val
->stls_request
= true;
292 if (g_ascii_strncasecmp(line
, "USER", 4) == 0) {
293 pop_arg_type
= pop_arg_type_username
;
296 if (g_ascii_strncasecmp(line
, "PASS", 4) == 0) {
297 pop_arg_type
= pop_arg_type_password
;
300 if (data_val
->msg_request
) {
301 /* this is a response to a RETR or TOP command */
303 if (g_ascii_strncasecmp(line
, "+OK ", 4) == 0 && linelen
> 4) {
304 /* the message will be sent - work out how many bytes */
305 data_val
->msg_read_len
= 0;
306 data_val
->msg_tot_len
= 0;
307 if (sscanf(line
, "%*s %u %*s", &data_val
->msg_tot_len
) != 1)
308 expert_add_info(pinfo
, ti
, &ei_pop_resp_tot_len_invalid
);
310 data_val
->msg_request
= false;
313 if (data_val
->stls_request
) {
314 if (g_ascii_strncasecmp(line
, "+OK ", 4) == 0) {
315 /* This is the last non-TLS frame. */
316 ssl_starttls_ack(tls_handle
, pinfo
, pop_handle
);
318 data_val
->stls_request
= false;
323 offset
+= (int) (next_token
- line
);
324 linelen
-= (int) (next_token
- line
);
329 * Add the rest of the first line as request or
330 * reply param/description.
333 tap_credential_t
* auth
;
334 proto_tree_add_item(reqresp_tree
,
336 hf_pop_request_parameter
:
337 hf_pop_response_description
,
338 tvb
, offset
, linelen
, ENC_ASCII
|ENC_NA
);
339 switch (pop_arg_type
) {
340 case pop_arg_type_username
:
341 if (!data_val
->username
&& linelen
> 0) {
342 data_val
->username
= tvb_get_string_enc(wmem_file_scope(), tvb
, offset
, linelen
, ENC_NA
|ENC_ASCII
);
343 data_val
->username_num
= pinfo
->num
;
346 case pop_arg_type_password
:
347 auth
= wmem_new0(pinfo
->pool
, tap_credential_t
);
348 auth
->num
= pinfo
->num
;
349 auth
->username_num
= data_val
->username_num
;
350 auth
->password_hf_id
= hf_pop_request_parameter
;
351 auth
->username
= data_val
->username
;
352 auth
->proto
= "POP3";
353 auth
->info
= wmem_strdup_printf(pinfo
->pool
, "Username in packet %u", data_val
->username_num
);
354 tap_queue_packet(credentials_tap
, pinfo
, auth
);
360 offset
= next_offset
;
363 * Show the rest of the request or response as text,
366 while (tvb_offset_exists(tvb
, offset
)) {
368 * Find the end of the line.
370 tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
375 proto_tree_add_string_format(pop_tree
,
377 hf_pop_request_data
:
378 hf_pop_response_data
,
380 next_offset
- offset
,
382 tvb_format_text(pinfo
->pool
, tvb
, offset
, next_offset
- offset
));
383 offset
= next_offset
;
385 return tvb_captured_length(tvb
);
388 static bool response_is_continuation(const unsigned char *data
)
390 if (strncmp(data
, "+OK", strlen("+OK")) == 0)
393 if (strncmp(data
, "-ERR", strlen("-ERR")) == 0)
400 proto_register_pop(void)
402 expert_module_t
* expert_pop
;
404 static hf_register_info hf
[] = {
406 { "Response", "pop.response",
407 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
408 { &hf_pop_response_indicator
,
409 { "Response indicator", "pop.response.indicator",
410 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
411 { &hf_pop_response_description
,
412 { "Response description", "pop.response.description",
413 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
414 { &hf_pop_response_data
,
415 { "Data", "pop.response.data",
416 FT_STRING
, BASE_NONE
, NULL
, 0x0, "Response Data", HFILL
}},
418 { "Request", "pop.request",
419 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
420 { &hf_pop_request_command
,
421 { "Request command", "pop.request.command",
422 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
423 { &hf_pop_request_parameter
,
424 { "Request parameter", "pop.request.parameter",
425 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
426 { &hf_pop_request_data
,
427 { "Data", "pop.request.data",
428 FT_STRING
, BASE_NONE
, NULL
, 0x0, "Request data", HFILL
}},
429 /* Fragment entries */
430 { &hf_pop_data_fragments
,
431 { "DATA fragments", "pop.data.fragments", FT_NONE
, BASE_NONE
,
432 NULL
, 0x00, "Message fragments", HFILL
} },
433 { &hf_pop_data_fragment
,
434 { "DATA fragment", "pop.data.fragment", FT_FRAMENUM
, BASE_NONE
,
435 NULL
, 0x00, "Message fragment", HFILL
} },
436 { &hf_pop_data_fragment_overlap
,
437 { "DATA fragment overlap", "pop.data.fragment.overlap", FT_BOOLEAN
,
438 BASE_NONE
, NULL
, 0x0, "Message fragment overlap", HFILL
} },
439 { &hf_pop_data_fragment_overlap_conflicts
,
440 { "DATA fragment overlapping with conflicting data",
441 "pop.data.fragment.overlap.conflicts", FT_BOOLEAN
, BASE_NONE
, NULL
,
442 0x0, "Message fragment overlapping with conflicting data", HFILL
} },
443 { &hf_pop_data_fragment_multiple_tails
,
444 { "DATA has multiple tail fragments",
445 "pop.data.fragment.multiple_tails", FT_BOOLEAN
, BASE_NONE
,
446 NULL
, 0x0, "Message has multiple tail fragments", HFILL
} },
447 { &hf_pop_data_fragment_too_long_fragment
,
448 { "DATA fragment too long", "pop.data.fragment.too_long_fragment",
449 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0, "Message fragment too long",
451 { &hf_pop_data_fragment_error
,
452 { "DATA defragmentation error", "pop.data.fragment.error", FT_FRAMENUM
,
453 BASE_NONE
, NULL
, 0x00, "Message defragmentation error", HFILL
} },
454 { &hf_pop_data_fragment_count
,
455 { "DATA fragment count", "pop.data.fragment.count", FT_UINT32
, BASE_DEC
,
456 NULL
, 0x00, NULL
, HFILL
} },
457 { &hf_pop_data_reassembled_in
,
458 { "Reassembled DATA in frame", "pop.data.reassembled.in", FT_FRAMENUM
, BASE_NONE
,
459 NULL
, 0x00, "This DATA fragment is reassembled in this frame", HFILL
} },
460 { &hf_pop_data_reassembled_length
,
461 { "Reassembled DATA length", "pop.data.reassembled.length", FT_UINT32
, BASE_DEC
,
462 NULL
, 0x00, "The total length of the reassembled payload", HFILL
} },
465 static ei_register_info ei
[] = {
466 { &ei_pop_resp_tot_len_invalid
, { "pop.response.tot_len.invalid", PI_MALFORMED
, PI_ERROR
,
467 "Length must be a string containing an integer", EXPFILL
}}
470 static int *ett
[] = {
473 &ett_pop_data_fragment
,
474 &ett_pop_data_fragments
476 module_t
*pop_module
;
479 proto_pop
= proto_register_protocol("Post Office Protocol", "POP", "pop");
480 pop_handle
= register_dissector("pop", dissect_pop
, proto_pop
);
481 proto_register_field_array(proto_pop
, hf
, array_length(hf
));
482 proto_register_subtree_array(ett
, array_length(ett
));
484 reassembly_table_register (&pop_data_reassembly_table
,
485 &addresses_ports_reassembly_table_functions
);
488 pop_module
= prefs_register_protocol(proto_pop
, NULL
);
490 prefs_register_bool_preference(pop_module
, "desegment_data",
491 "Reassemble POP RETR and TOP responses spanning multiple TCP segments",
492 "Whether the POP dissector should reassemble RETR and TOP responses and spanning multiple TCP segments."
493 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
494 &pop_data_desegment
);
496 expert_pop
= expert_register_protocol(proto_pop
);
497 expert_register_field_array(expert_pop
, ei
, array_length(ei
));
499 credentials_tap
= register_tap("credentials");
503 proto_reg_handoff_pop(void)
505 dissector_add_uint_with_preference("tcp.port", TCP_PORT_POP
, pop_handle
);
506 ssl_dissector_add(TCP_PORT_SSL_POP
, pop_handle
);
508 /* find the IMF dissector */
509 imf_handle
= find_dissector_add_dependency("imf", proto_pop
);
511 /* find the TLS dissector */
512 tls_handle
= find_dissector_add_dependency("tls", proto_pop
);
516 * Editor modelines - https://www.wireshark.org/tools/modelines.html
521 * indent-tabs-mode: nil
524 * ex: set shiftwidth=2 tabstop=8 expandtab:
525 * :indentSize=2:tabSize=8:noTabs=true: