HACK: 2nd try to match RowsetProperties
[wireshark-wip.git] / epan / dissectors / packet-tftp.c
blob3c98eb5f4934a678c07d745e4e0b18ed40098592
1 /* packet-tftp.c
2 * Routines for tftp packet dissection
4 * Richard Sharpe <rsharpe@ns.aus.com>
5 * Craig Newell <CraigN@cheque.uq.edu.au>
6 * RFC2347 TFTP Option Extension
7 * Joerg Mayer (see AUTHORS file)
8 * RFC2348 TFTP Blocksize Option
10 * $Id$
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 1998 Gerald Combs
16 * Copied from packet-bootp.c
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 /* Documentation:
34 * RFC 1350: THE TFTP PROTOCOL (REVISION 2)
35 * RFC 2090: TFTP Multicast Option
36 * (not yet implemented)
37 * RFC 2347: TFTP Option Extension
38 * RFC 2348: TFTP Blocksize Option
39 * RFC 2349: TFTP Timeout Interval and Transfer Size Options
40 * (not yet implemented)
43 #include "config.h"
45 #include <glib.h>
46 #include <stdlib.h>
47 #include <epan/packet.h>
48 #include <epan/conversation.h>
49 #include <epan/wmem/wmem.h>
50 #include <epan/expert.h>
51 #include <epan/range.h>
52 #include <epan/prefs.h>
54 /* Things we may want to remember for a whole conversation */
55 typedef struct _tftp_conv_info_t {
56 guint16 blocksize;
57 gchar *source_file, *destination_file;
58 } tftp_conv_info_t;
61 static int proto_tftp = -1;
62 static int hf_tftp_opcode = -1;
63 static int hf_tftp_source_file = -1;
64 static int hf_tftp_destination_file = -1;
65 static int hf_tftp_transfer_type = -1;
66 static int hf_tftp_blocknum = -1;
67 static int hf_tftp_error_code = -1;
68 static int hf_tftp_error_string = -1;
69 static int hf_tftp_option_name = -1;
70 static int hf_tftp_option_value = -1;
72 static gint ett_tftp = -1;
73 static gint ett_tftp_option = -1;
75 static expert_field ei_tftp_blocksize_range = EI_INIT;
77 static dissector_handle_t tftp_handle;
78 static dissector_handle_t data_handle;
80 #define UDP_PORT_TFTP_RANGE "69"
82 void proto_reg_handoff_tftp (void);
84 /* User definable values */
85 static range_t *global_tftp_port_range;
87 #define TFTP_RRQ 1
88 #define TFTP_WRQ 2
89 #define TFTP_DATA 3
90 #define TFTP_ACK 4
91 #define TFTP_ERROR 5
92 #define TFTP_OACK 6
93 #define TFTP_INFO 255
95 static const value_string tftp_opcode_vals[] = {
96 { TFTP_RRQ, "Read Request" },
97 { TFTP_WRQ, "Write Request" },
98 { TFTP_DATA, "Data Packet" },
99 { TFTP_ACK, "Acknowledgement" },
100 { TFTP_ERROR, "Error Code" },
101 { TFTP_OACK, "Option Acknowledgement" },
102 { TFTP_INFO, "Information (MSDP)" },
103 { 0, NULL }
106 static const value_string tftp_error_code_vals[] = {
107 { 0, "Not defined" },
108 { 1, "File not found" },
109 { 2, "Access violation" },
110 { 3, "Disk full or allocation exceeded" },
111 { 4, "Illegal TFTP Operation" },
112 { 5, "Unknown transfer ID" }, /* Does not cause termination */
113 { 6, "File already exists" },
114 { 7, "No such user" },
115 { 8, "Option negotiation failed" },
116 { 0, NULL }
119 static void
120 tftp_dissect_options(tvbuff_t *tvb, packet_info *pinfo, int offset,
121 proto_tree *tree, guint16 opcode, tftp_conv_info_t *tftp_info)
123 int option_len, value_len;
124 int value_offset;
125 const char *optionname;
126 const char *optionvalue;
127 proto_item *opt_item;
128 proto_tree *opt_tree;
130 while (tvb_offset_exists(tvb, offset)) {
131 option_len = tvb_strsize(tvb, offset); /* length of option */
132 value_offset = offset + option_len;
133 value_len = tvb_strsize(tvb, value_offset); /* length of value */
134 optionname = tvb_format_text(tvb, offset, option_len);
135 optionvalue = tvb_format_text(tvb, value_offset, value_len);
136 opt_item = proto_tree_add_text(tree, tvb, offset, option_len+value_len,
137 "Option: %s = %s", optionname, optionvalue);
139 opt_tree = proto_item_add_subtree(opt_item, ett_tftp_option);
140 proto_tree_add_item(opt_tree, hf_tftp_option_name, tvb, offset,
141 option_len, ENC_ASCII|ENC_NA);
142 proto_tree_add_item(opt_tree, hf_tftp_option_value, tvb, value_offset,
143 value_len, ENC_ASCII|ENC_NA);
145 offset += option_len + value_len;
147 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s=%s",
148 optionname, optionvalue);
150 /* Special code to handle individual options */
151 if (!g_ascii_strcasecmp((const char *)optionname, "blksize") &&
152 opcode == TFTP_OACK) {
153 gint blocksize = (gint)strtol((const char *)optionvalue, NULL, 10);
154 if (blocksize < 8 || blocksize > 65464) {
155 expert_add_info(pinfo, NULL, &ei_tftp_blocksize_range);
156 } else {
157 tftp_info->blocksize = blocksize;
163 static void dissect_tftp_message(tftp_conv_info_t *tftp_info,
164 tvbuff_t *tvb, packet_info *pinfo,
165 proto_tree *tree)
167 proto_tree *tftp_tree = NULL;
168 proto_item *ti;
169 gint offset = 0;
170 guint16 opcode;
171 guint16 bytes;
172 guint16 blocknum;
173 guint i1;
174 guint16 error;
176 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TFTP");
178 opcode = tvb_get_ntohs(tvb, offset);
180 col_add_str(pinfo->cinfo, COL_INFO,
181 val_to_str(opcode, tftp_opcode_vals, "Unknown (0x%04x)"));
183 if (tree) {
184 ti = proto_tree_add_item(tree, proto_tftp, tvb, offset, -1, ENC_NA);
185 tftp_tree = proto_item_add_subtree(ti, ett_tftp);
187 if (tftp_info->source_file) {
188 ti = proto_tree_add_string(tftp_tree, hf_tftp_source_file, tvb,
189 0, 0, tftp_info->source_file);
190 PROTO_ITEM_SET_GENERATED(ti);
193 if (tftp_info->destination_file) {
194 ti = proto_tree_add_string(tftp_tree, hf_tftp_destination_file, tvb,
195 0, 0, tftp_info->destination_file);
196 PROTO_ITEM_SET_GENERATED(ti);
199 proto_tree_add_uint(tftp_tree, hf_tftp_opcode, tvb,
200 offset, 2, opcode);
202 offset += 2;
204 switch (opcode) {
206 case TFTP_RRQ:
207 i1 = tvb_strsize(tvb, offset);
208 proto_tree_add_item(tftp_tree, hf_tftp_source_file,
209 tvb, offset, i1, ENC_ASCII|ENC_NA);
211 tftp_info->source_file = tvb_get_string(wmem_file_scope(), tvb, offset, i1);
213 col_append_fstr(pinfo->cinfo, COL_INFO, ", File: %s",
214 tvb_format_stringzpad(tvb, offset, i1));
216 offset += i1;
218 i1 = tvb_strsize(tvb, offset);
219 proto_tree_add_item(tftp_tree, hf_tftp_transfer_type,
220 tvb, offset, i1, ENC_ASCII|ENC_NA);
222 col_append_fstr(pinfo->cinfo, COL_INFO, ", Transfer type: %s",
223 tvb_format_stringzpad(tvb, offset, i1));
225 offset += i1;
227 tftp_dissect_options(tvb, pinfo, offset, tftp_tree,
228 opcode, tftp_info);
229 break;
231 case TFTP_WRQ:
232 i1 = tvb_strsize(tvb, offset);
233 proto_tree_add_item(tftp_tree, hf_tftp_destination_file,
234 tvb, offset, i1, ENC_ASCII|ENC_NA);
236 tftp_info->destination_file =
237 tvb_get_string(wmem_file_scope(), tvb, offset, i1);
239 col_append_fstr(pinfo->cinfo, COL_INFO, ", File: %s",
240 tvb_format_stringzpad(tvb, offset, i1));
242 offset += i1;
244 i1 = tvb_strsize(tvb, offset);
245 proto_tree_add_item(tftp_tree, hf_tftp_transfer_type,
246 tvb, offset, i1, ENC_ASCII|ENC_NA);
248 col_append_fstr(pinfo->cinfo, COL_INFO, ", Transfer type: %s",
249 tvb_format_stringzpad(tvb, offset, i1));
251 offset += i1;
253 tftp_dissect_options(tvb, pinfo, offset, tftp_tree,
254 opcode, tftp_info);
255 break;
257 case TFTP_INFO:
258 tftp_dissect_options(tvb, pinfo, offset, tftp_tree,
259 opcode, tftp_info);
260 break;
262 case TFTP_DATA:
263 blocknum = tvb_get_ntohs(tvb, offset);
264 proto_tree_add_uint(tftp_tree, hf_tftp_blocknum, tvb, offset, 2,
265 blocknum);
267 offset += 2;
269 bytes = tvb_reported_length_remaining(tvb, offset);
271 col_append_fstr(pinfo->cinfo, COL_INFO, ", Block: %i%s",
272 blocknum,
273 (bytes < tftp_info->blocksize)?" (last)":"" );
275 if (bytes != 0) {
276 tvbuff_t *data_tvb = tvb_new_subset(tvb, offset, -1, bytes);
277 call_dissector(data_handle, data_tvb, pinfo, tree);
279 break;
281 case TFTP_ACK:
282 blocknum = tvb_get_ntohs(tvb, offset);
283 proto_tree_add_uint(tftp_tree, hf_tftp_blocknum, tvb, offset, 2,
284 blocknum);
286 col_append_fstr(pinfo->cinfo, COL_INFO, ", Block: %i",
287 blocknum);
288 break;
290 case TFTP_ERROR:
291 error = tvb_get_ntohs(tvb, offset);
292 proto_tree_add_uint(tftp_tree, hf_tftp_error_code, tvb, offset, 2,
293 error);
295 col_append_fstr(pinfo->cinfo, COL_INFO, ", Code: %s",
296 val_to_str(error, tftp_error_code_vals, "Unknown (%u)"));
298 offset += 2;
300 i1 = tvb_strsize(tvb, offset);
301 proto_tree_add_item(tftp_tree, hf_tftp_error_string, tvb, offset,
302 i1, ENC_ASCII|ENC_NA);
304 col_append_fstr(pinfo->cinfo, COL_INFO, ", Message: %s",
305 tvb_format_stringzpad(tvb, offset, i1));
307 expert_add_info(pinfo, NULL, &ei_tftp_blocksize_range);
308 break;
310 case TFTP_OACK:
311 tftp_dissect_options(tvb, pinfo, offset, tftp_tree,
312 opcode, tftp_info);
313 break;
315 default:
316 proto_tree_add_text(tftp_tree, tvb, offset, -1,
317 "Data (%d bytes)", tvb_reported_length_remaining(tvb, offset));
318 break;
322 return;
325 static gboolean
326 dissect_embeddedtftp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
328 /* Used to dissect TFTP packets where one can not assume
329 that the TFTP is the only protocol used by that port, and
330 that TFTP may not be carried by UDP */
331 conversation_t *conversation = NULL;
332 guint16 opcode;
333 tftp_conv_info_t *tftp_info;
335 conversation = find_or_create_conversation(pinfo);
337 tftp_info = (tftp_conv_info_t *)conversation_get_proto_data(conversation, proto_tftp);
338 if (!tftp_info) {
339 tftp_info = wmem_new(wmem_file_scope(), tftp_conv_info_t);
340 tftp_info->blocksize = 512; /* TFTP default block size */
341 tftp_info->source_file = NULL;
342 tftp_info->destination_file = NULL;
343 conversation_add_proto_data(conversation, proto_tftp, tftp_info);
346 opcode = tvb_get_ntohs(tvb, 0);
348 if ((opcode == TFTP_RRQ) ||
349 (opcode == TFTP_WRQ) ||
350 (opcode == TFTP_DATA) ||
351 (opcode == TFTP_ACK) ||
352 (opcode == TFTP_ERROR) ||
353 (opcode == TFTP_INFO) ||
354 (opcode == TFTP_OACK)) {
355 dissect_tftp_message(tftp_info, tvb, pinfo, tree);
356 return TRUE;
358 else {
359 return FALSE;
363 static void
364 dissect_tftp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
366 conversation_t *conversation = NULL;
367 tftp_conv_info_t *tftp_info;
370 * The first TFTP packet goes to the TFTP port; the second one
371 * comes from some *other* port, but goes back to the same
372 * IP address and port as the ones from which the first packet
373 * came; all subsequent packets go between those two IP addresses
374 * and ports.
376 * If this packet went to the TFTP port, we check to see if
377 * there's already a conversation with one address/port pair
378 * matching the source IP address and port of this packet,
379 * the other address matching the destination IP address of this
380 * packet, and any destination port.
382 * If not, we create one, with its address 1/port 1 pair being
383 * the source address/port of this packet, its address 2 being
384 * the destination address of this packet, and its port 2 being
385 * wildcarded, and give it the TFTP dissector as a dissector.
387 if (value_is_in_range(global_tftp_port_range, pinfo->destport)) {
388 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
389 pinfo->srcport, 0, NO_PORT_B);
390 if( (conversation == NULL) || (conversation->dissector_handle != tftp_handle) ){
391 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
392 pinfo->srcport, 0, NO_PORT2);
393 conversation_set_dissector(conversation, tftp_handle);
395 } else {
396 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
397 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
398 if( (conversation == NULL) || (conversation->dissector_handle != tftp_handle) ){
399 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
400 pinfo->destport, pinfo->srcport, 0);
401 conversation_set_dissector(conversation, tftp_handle);
402 } else if (conversation->options & NO_PORT_B) {
403 if (pinfo->destport == conversation->key_ptr->port1)
404 conversation_set_port2(conversation, pinfo->srcport);
405 else
406 return;
409 tftp_info = (tftp_conv_info_t *)conversation_get_proto_data(conversation, proto_tftp);
410 if (!tftp_info) {
411 tftp_info = wmem_new(wmem_file_scope(), tftp_conv_info_t);
412 tftp_info->blocksize = 512; /* TFTP default block size */
413 tftp_info->source_file = NULL;
414 tftp_info->destination_file = NULL;
415 conversation_add_proto_data(conversation, proto_tftp, tftp_info);
418 dissect_tftp_message(tftp_info, tvb, pinfo, tree);
420 return;
424 void
425 proto_register_tftp(void)
427 static hf_register_info hf[] = {
428 { &hf_tftp_opcode,
429 { "Opcode", "tftp.opcode",
430 FT_UINT16, BASE_DEC, VALS(tftp_opcode_vals), 0x0,
431 "TFTP message type", HFILL }},
433 { &hf_tftp_source_file,
434 { "Source File", "tftp.source_file",
435 FT_STRINGZ, BASE_NONE, NULL, 0x0,
436 "TFTP source file name", HFILL }},
438 { &hf_tftp_destination_file,
439 { "DESTINATION File", "tftp.destination_file",
440 FT_STRINGZ, BASE_NONE, NULL, 0x0,
441 "TFTP source file name", HFILL }},
443 { &hf_tftp_transfer_type,
444 { "Type", "tftp.type",
445 FT_STRINGZ, BASE_NONE, NULL, 0x0,
446 "TFTP transfer type", HFILL }},
448 { &hf_tftp_blocknum,
449 { "Block", "tftp.block",
450 FT_UINT16, BASE_DEC, NULL, 0x0,
451 "Block number", HFILL }},
453 { &hf_tftp_error_code,
454 { "Error code", "tftp.error.code",
455 FT_UINT16, BASE_DEC, VALS(tftp_error_code_vals), 0x0,
456 "Error code in case of TFTP error message", HFILL }},
458 { &hf_tftp_error_string,
459 { "Error message", "tftp.error.message",
460 FT_STRINGZ, BASE_NONE, NULL, 0x0,
461 "Error string in case of TFTP error message", HFILL }},
463 { &hf_tftp_option_name,
464 { "Option name", "tftp.option.name",
465 FT_STRINGZ, BASE_NONE, NULL, 0x0,
466 NULL, HFILL }},
468 { &hf_tftp_option_value,
469 { "Option value", "tftp.option.value",
470 FT_STRINGZ, BASE_NONE, NULL, 0x0,
471 NULL, HFILL }},
474 static gint *ett[] = {
475 &ett_tftp,
476 &ett_tftp_option,
479 static ei_register_info ei[] = {
480 { &ei_tftp_blocksize_range, { "tftp.blocksize_range", PI_RESPONSE_CODE, PI_WARN, "TFTP blocksize out of range", EXPFILL }},
483 module_t *tftp_module;
484 expert_module_t* expert_tftp;
486 proto_tftp = proto_register_protocol("Trivial File Transfer Protocol",
487 "TFTP", "tftp");
488 proto_register_field_array(proto_tftp, hf, array_length(hf));
489 proto_register_subtree_array(ett, array_length(ett));
490 expert_tftp = expert_register_protocol(proto_tftp);
491 expert_register_field_array(expert_tftp, ei, array_length(ei));
493 register_dissector("tftp", dissect_tftp, proto_tftp);
495 /* Set default UDP ports */
496 range_convert_str(&global_tftp_port_range, UDP_PORT_TFTP_RANGE, MAX_UDP_PORT);
498 tftp_module = prefs_register_protocol(proto_tftp, proto_reg_handoff_tftp);
499 prefs_register_range_preference(tftp_module, "udp_ports",
500 "TFTP port numbers",
501 "Port numbers used for TFTP traffic "
502 "(default " UDP_PORT_TFTP_RANGE ")",
503 &global_tftp_port_range, MAX_UDP_PORT);
506 void
507 proto_reg_handoff_tftp(void)
509 static range_t *tftp_port_range;
510 static gboolean tftp_initialized = FALSE;
512 if (!tftp_initialized) {
513 tftp_handle = find_dissector("tftp");
514 data_handle = find_dissector("data");
515 heur_dissector_add("stun", dissect_embeddedtftp_heur, proto_tftp);
516 tftp_initialized = TRUE;
517 } else {
518 dissector_add_uint_range("udp.port", tftp_port_range, tftp_handle);
519 g_free(tftp_port_range);
522 tftp_port_range = range_copy(global_tftp_port_range);
523 dissector_add_uint_range("udp.port", tftp_port_range, tftp_handle);
527 * Editor modelines - http://www.wireshark.org/tools/modelines.html
529 * Local variables:
530 * c-basic-offset: 2
531 * tab-width: 8
532 * indent-tabs-mode: nil
533 * End:
535 * vi: set shiftwidth=2 tabstop=8 expandtab:
536 * :indentSize=2:tabSize=8:noTabs=true: