- Make options filterable
[obnox/wireshark/wip.git] / epan / dissectors / packet-tftp.c
1 /* packet-tftp.c
2  * Routines for tftp packet dissection
3  *
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
9  *
10  * $Id$
11  *
12  * Wireshark - Network traffic analyzer
13  * By Gerald Combs <gerald@wireshark.org>
14  * Copyright 1998 Gerald Combs
15  *
16  * Copied from packet-bootp.c
17  *
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.
22  *
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.
27  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31  */
32
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)
41  */
42
43 #ifdef HAVE_CONFIG_H
44 # include "config.h"
45 #endif
46
47 #include <glib.h>
48 #include <string.h>
49 #include <epan/packet.h>
50 #include <epan/conversation.h>
51 #include <epan/emem.h>
52 #include <epan/expert.h>
53
54 /* Things we may want to remember for a whole conversation */
55 typedef struct _tftp_conv_info_t {
56         guint16 blocksize;
57 } tftp_conv_info_t;
58
59
60 static int proto_tftp = -1;
61 static int hf_tftp_opcode = -1;
62 static int hf_tftp_source_file = -1;
63 static int hf_tftp_destination_file = -1;
64 static int hf_tftp_transfer_type = -1;
65 static int hf_tftp_blocknum = -1;
66 static int hf_tftp_error_code = -1;
67 static int hf_tftp_error_string = -1;
68 static int hf_tftp_option_name = -1;
69 static int hf_tftp_option_value = -1;
70
71 static gint ett_tftp = -1;
72 static gint ett_tftp_option = -1;
73
74 static dissector_handle_t tftp_handle;
75
76 #define UDP_PORT_TFTP    69
77
78 #define TFTP_RRQ        1
79 #define TFTP_WRQ        2
80 #define TFTP_DATA       3
81 #define TFTP_ACK        4
82 #define TFTP_ERROR      5
83 #define TFTP_OACK       6
84
85 static const value_string tftp_opcode_vals[] = {
86   { TFTP_RRQ,   "Read Request" },
87   { TFTP_WRQ,   "Write Request" },
88   { TFTP_DATA,  "Data Packet" },
89   { TFTP_ACK,   "Acknowledgement" },
90   { TFTP_ERROR, "Error Code" },
91   { TFTP_OACK,  "Option Acknowledgement" },
92   { 0,          NULL }
93 };
94
95 static const value_string tftp_error_code_vals[] = {
96   { 0, "Not defined" },
97   { 1, "File not found" },
98   { 2, "Access violation" },
99   { 3, "Disk full or allocation exceeded" },
100   { 4, "Illegal TFTP Operation" },
101   { 5, "Unknown transfer ID" },         /* Does not cause termination */
102   { 6, "File already exists" },
103   { 7, "No such user" },
104   { 8, "Option negotiation failed" },
105   { 0, NULL }
106 };
107
108 static void
109 tftp_dissect_options(tvbuff_t *tvb, packet_info *pinfo, int offset,
110         proto_tree *tree, guint16 opcode, tftp_conv_info_t *tftp_info)
111 {
112         int option_len, value_len;
113         int value_offset;
114         const guint8 *optionname;
115         const guint8 *optionvalue;
116         proto_item *opt_item;
117         proto_tree *opt_tree;
118
119         while (tvb_offset_exists(tvb, offset)) {
120           option_len = tvb_strsize(tvb, offset);        /* length of option */
121           value_offset = offset + option_len;
122           value_len = tvb_strsize(tvb, value_offset);   /* length of value */
123           optionname = tvb_get_ptr(tvb, offset, option_len);
124           optionvalue = tvb_get_ptr(tvb, value_offset, value_len);
125           opt_item = proto_tree_add_text(tree, tvb, offset, option_len+value_len,
126                   "Option: %s = %s", optionname, optionvalue);
127
128           opt_tree = proto_item_add_subtree(opt_item, ett_tftp_option);
129           proto_tree_add_item(opt_tree, hf_tftp_option_name, tvb, offset,
130                 option_len, FALSE);
131           proto_tree_add_item(opt_tree, hf_tftp_option_value, tvb, value_offset,
132                 value_len, FALSE);
133
134           offset += option_len + value_len;
135
136           if (check_col(pinfo->cinfo, COL_INFO)) {
137             col_append_fstr(pinfo->cinfo, COL_INFO, ", %s=%s",
138                             optionname, optionvalue);
139           }
140
141           /* Special code to handle individual options */
142           if (!strcasecmp((const char *)optionname, "blksize") &&
143               opcode == TFTP_OACK) {
144                 gint blocksize = strtol((const char *)optionvalue, NULL, 10);
145                 if (blocksize < 8 || blocksize > 65464) {
146                         expert_add_info_format(pinfo, NULL, PI_RESPONSE_CODE,
147                                 PI_WARN, "TFTP blocksize out of range");
148
149                 } else {
150                         tftp_info->blocksize = blocksize;
151                 }
152           }
153         }
154 }
155
156 static void
157 dissect_tftp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
158 {
159         proto_tree       *tftp_tree = NULL;
160         proto_item       *ti;
161         conversation_t   *conversation;
162         gint             offset = 0;
163         guint16          opcode;
164         guint16          bytes;
165         guint16          blocknum;
166         guint            i1;
167         guint16          error;
168         tftp_conv_info_t *tftp_info;
169
170         /*
171          * The first TFTP packet goes to the TFTP port; the second one
172          * comes from some *other* port, but goes back to the same
173          * IP address and port as the ones from which the first packet
174          * came; all subsequent packets go between those two IP addresses
175          * and ports.
176          *
177          * If this packet went to the TFTP port, we check to see if
178          * there's already a conversation with one address/port pair
179          * matching the source IP address and port of this packet,
180          * the other address matching the destination IP address of this
181          * packet, and any destination port.
182          *
183          * If not, we create one, with its address 1/port 1 pair being
184          * the source address/port of this packet, its address 2 being
185          * the destination address of this packet, and its port 2 being
186          * wildcarded, and give it the TFTP dissector as a dissector.
187          */
188         if (pinfo->destport == UDP_PORT_TFTP) {
189           conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
190                                            pinfo->srcport, 0, NO_PORT_B);
191           if( (conversation == NULL) || (conversation->dissector_handle!=tftp_handle) ){
192             conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
193                                             pinfo->srcport, 0, NO_PORT2);
194             conversation_set_dissector(conversation, tftp_handle);
195           }
196         } else {
197           conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
198                 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
199           DISSECTOR_ASSERT(conversation);
200         }
201         tftp_info = conversation_get_proto_data(conversation, proto_tftp);
202         if (!tftp_info) {
203                 tftp_info = se_alloc(sizeof(tftp_conv_info_t));
204                 tftp_info->blocksize = 512; /* TFTP default block size */
205                 conversation_add_proto_data(conversation, proto_tftp, tftp_info);
206         }
207
208         if (check_col(pinfo->cinfo, COL_PROTOCOL))
209                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TFTP");
210
211         opcode = tvb_get_ntohs(tvb, offset);
212
213         if (check_col(pinfo->cinfo, COL_INFO)) {
214
215           col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
216             val_to_str(opcode, tftp_opcode_vals, "Unknown (0x%04x)"));
217
218         }
219
220         if (tree) {
221
222           ti = proto_tree_add_item(tree, proto_tftp, tvb, offset, -1, FALSE);
223           tftp_tree = proto_item_add_subtree(ti, ett_tftp);
224
225           proto_tree_add_uint(tftp_tree, hf_tftp_opcode, tvb,
226                             offset, 2, opcode);
227         }
228         offset += 2;
229
230         switch (opcode) {
231
232         case TFTP_RRQ:
233           i1 = tvb_strsize(tvb, offset);
234           if (tree) {
235             proto_tree_add_item(tftp_tree, hf_tftp_source_file,
236                             tvb, offset, i1, FALSE);
237           }
238           if (check_col(pinfo->cinfo, COL_INFO)) {
239             col_append_fstr(pinfo->cinfo, COL_INFO, ", File: %s",
240                             tvb_get_ptr(tvb, offset, i1));
241           }
242           offset += i1;
243
244           i1 = tvb_strsize(tvb, offset);
245           if (tree) {
246             ti = proto_tree_add_item(tftp_tree, hf_tftp_transfer_type,
247                             tvb, offset, i1, FALSE);
248           }
249           if (check_col(pinfo->cinfo, COL_INFO)) {
250             col_append_fstr(pinfo->cinfo, COL_INFO, ", Transfer type: %s",
251                             tvb_get_ptr(tvb, offset, i1));
252           }
253           offset += i1;
254
255           if (tree)
256             tftp_dissect_options(tvb, pinfo,  offset, tftp_tree,
257                 opcode, tftp_info);
258           break;
259
260         case TFTP_WRQ:
261           i1 = tvb_strsize(tvb, offset);
262           if (tree) {
263             proto_tree_add_item(tftp_tree, hf_tftp_destination_file,
264                             tvb, offset, i1, FALSE);
265           }
266           if (check_col(pinfo->cinfo, COL_INFO)) {
267             col_append_fstr(pinfo->cinfo, COL_INFO, ", File: %s",
268                             tvb_get_ptr(tvb, offset, i1));
269           }
270           offset += i1;
271
272           i1 = tvb_strsize(tvb, offset);
273           if (tree) {
274             ti = proto_tree_add_item(tftp_tree, hf_tftp_transfer_type,
275                             tvb, offset, i1, FALSE);
276           }
277           if (check_col(pinfo->cinfo, COL_INFO)) {
278             col_append_fstr(pinfo->cinfo, COL_INFO, ", Transfer type: %s",
279                             tvb_get_ptr(tvb, offset, i1));
280           }
281           offset += i1;
282
283           if (tree)
284             tftp_dissect_options(tvb, pinfo, offset, tftp_tree,
285                 opcode,  tftp_info);
286           break;
287
288         case TFTP_DATA:
289           blocknum = tvb_get_ntohs(tvb, offset);
290           if (tree) {
291             proto_tree_add_uint(tftp_tree, hf_tftp_blocknum, tvb, offset, 2,
292                             blocknum);
293           }
294           offset += 2;
295
296           bytes = tvb_reported_length_remaining(tvb, offset);
297
298           if (check_col(pinfo->cinfo, COL_INFO)) {
299             col_append_fstr(pinfo->cinfo, COL_INFO, ", Block: %i%s",
300                     blocknum,
301                     (bytes < tftp_info->blocksize)?" (last)":"" );
302           }
303
304           if (bytes != 0) {
305             if (tree) {
306               proto_tree_add_text(tftp_tree, tvb, offset, -1,
307                 "Data (%d bytes)", bytes);
308             }
309           }
310           break;
311
312         case TFTP_ACK:
313           blocknum = tvb_get_ntohs(tvb, offset);
314           if (tree) {
315             proto_tree_add_uint(tftp_tree, hf_tftp_blocknum, tvb, offset, 2,
316                             blocknum);
317           }
318           if (check_col(pinfo->cinfo, COL_INFO)) {
319             col_append_fstr(pinfo->cinfo, COL_INFO, ", Block: %i",
320                             blocknum);
321           }
322           break;
323
324         case TFTP_ERROR:
325           error = tvb_get_ntohs(tvb, offset);
326           if (tree) {
327             proto_tree_add_uint(tftp_tree, hf_tftp_error_code, tvb, offset, 2,
328                             error);
329           }
330           if (check_col(pinfo->cinfo, COL_INFO)) {
331             col_append_fstr(pinfo->cinfo, COL_INFO, ", Code: %s",
332                             val_to_str(error, tftp_error_code_vals, "Unknown (%u)"));
333           }
334           offset += 2;
335
336           i1 = tvb_strsize(tvb, offset);
337           if (tree) {
338             proto_tree_add_item(tftp_tree, hf_tftp_error_string, tvb, offset,
339                 i1, FALSE);
340           }
341           if (check_col(pinfo->cinfo, COL_INFO)) {
342             col_append_fstr(pinfo->cinfo, COL_INFO, ", Message: %s",
343                             tvb_get_ptr(tvb, offset, i1));
344           }
345           expert_add_info_format(pinfo, NULL, PI_RESPONSE_CODE,
346                 PI_NOTE, "TFTP blocksize out of range");
347           break;
348
349         case TFTP_OACK:
350           if (tree)
351             tftp_dissect_options(tvb, pinfo, offset, tftp_tree,
352                 opcode, tftp_info);
353           break;
354
355         default:
356           if (tree) {
357             proto_tree_add_text(tftp_tree, tvb, offset, -1,
358                 "Data (%d bytes)", tvb_reported_length_remaining(tvb, offset));
359           }
360           break;
361
362         }
363 }
364
365 void
366 proto_register_tftp(void)
367 {
368   static hf_register_info hf[] = {
369     { &hf_tftp_opcode,
370       { "Opcode",             "tftp.opcode",
371         FT_UINT16, BASE_DEC, VALS(tftp_opcode_vals), 0x0,
372         "TFTP message type", HFILL }},
373
374     { &hf_tftp_source_file,
375       { "Source File",        "tftp.source_file",
376         FT_STRINGZ, BASE_DEC, NULL, 0x0,
377         "TFTP source file name", HFILL }},
378
379     { &hf_tftp_destination_file,
380       { "DESTINATION File",   "tftp.destination_file",
381         FT_STRINGZ, BASE_DEC, NULL, 0x0,
382         "TFTP source file name", HFILL }},
383
384     { &hf_tftp_transfer_type,
385       { "Type",               "tftp.type",
386         FT_STRINGZ, BASE_DEC, NULL, 0x0,
387         "TFTP transfer type", HFILL }},
388
389     { &hf_tftp_blocknum,
390       { "Block",              "tftp.block",
391         FT_UINT16, BASE_DEC, NULL, 0x0,
392         "Block number", HFILL }},
393
394     { &hf_tftp_error_code,
395       { "Error code",         "tftp.error.code",
396         FT_UINT16, BASE_DEC, VALS(tftp_error_code_vals), 0x0,
397         "Error code in case of TFTP error message", HFILL }},
398
399     { &hf_tftp_error_string,
400       { "Error message",      "tftp.error.message",
401         FT_STRINGZ, BASE_DEC, NULL, 0x0,
402         "Error string in case of TFTP error message", HFILL }},
403
404     { &hf_tftp_option_name,
405       { "Option name",              "tftp.option.name",
406         FT_STRINGZ, BASE_DEC, NULL, 0x0,
407         "", HFILL }},
408
409     { &hf_tftp_option_value,
410       { "Option value",              "tftp.option.value",
411         FT_STRINGZ, BASE_DEC, NULL, 0x0,
412         "", HFILL }},
413
414   };
415   static gint *ett[] = {
416     &ett_tftp,
417     &ett_tftp_option,
418   };
419
420   proto_tftp = proto_register_protocol("Trivial File Transfer Protocol",
421                                        "TFTP", "tftp");
422   proto_register_field_array(proto_tftp, hf, array_length(hf));
423   proto_register_subtree_array(ett, array_length(ett));
424
425   register_dissector("tftp", dissect_tftp, proto_tftp);
426 }
427
428 void
429 proto_reg_handoff_tftp(void)
430 {
431   tftp_handle = find_dissector("tftp");
432
433   dissector_add("udp.port", UDP_PORT_TFTP, tftp_handle);
434 }