From Richard Urwin a great enhancement to the color filter dialogue to
[obnox/wireshark/wip.git] / 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  *
8  * $Id: packet-tftp.c,v 1.40 2002/08/28 21:00:36 jmayer Exp $
9  *
10  * Ethereal - Network traffic analyzer
11  * By Gerald Combs <gerald@ethereal.com>
12  * Copyright 1998 Gerald Combs
13  *
14  * Copied from packet-bootp.c
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <glib.h>
36 #include <epan/packet.h>
37 #include <epan/conversation.h>
38
39 static int proto_tftp = -1;
40 static int hf_tftp_opcode = -1;
41 static int hf_tftp_source_file = -1;
42 static int hf_tftp_destination_file = -1;
43 static int hf_tftp_transfer_type = -1;
44 static int hf_tftp_blocknum = -1;
45 static int hf_tftp_error_code = -1;
46 static int hf_tftp_error_string = -1;
47
48 static gint ett_tftp = -1;
49
50 static dissector_handle_t tftp_handle;
51
52 #define UDP_PORT_TFTP    69
53
54 #define TFTP_RRQ        1
55 #define TFTP_WRQ        2
56 #define TFTP_DATA       3
57 #define TFTP_ACK        4
58 #define TFTP_ERROR      5
59 #define TFTP_OACK       6
60
61 static const value_string tftp_opcode_vals[] = {
62   { TFTP_RRQ,   "Read Request" },
63   { TFTP_WRQ,   "Write Request" },
64   { TFTP_DATA,  "Data Packet" },
65   { TFTP_ACK,   "Acknowledgement" },
66   { TFTP_ERROR, "Error Code" },
67   { TFTP_OACK,  "Option Acknowledgement" },
68   { 0,          NULL }
69 };
70
71 static const value_string tftp_error_code_vals[] = {
72   { 0, "Not defined" },
73   { 1, "File not found" },
74   { 2, "Access violation" },
75   { 3, "Disk full or allocation exceeded" },
76   { 4, "Illegal TFTP Operation" },
77   { 5, "Unknown transfer ID" },
78   { 6, "File already exists" },
79   { 7, "No such user" },
80   { 8, "Option negotiation failed" },
81   { 0, NULL }
82 };
83
84 static void tftp_dissect_options(tvbuff_t *tvb, int offset, proto_tree *tree);
85
86 static void
87 dissect_tftp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
88 {
89         proto_tree      *tftp_tree = NULL;
90         proto_item      *ti;
91         conversation_t  *conversation;
92         gint            offset = 0;
93         guint16         opcode;
94         guint16         bytes;
95         guint16         blocknum;
96         guint           i1;
97         guint16         error;
98
99         /*
100          * The first TFTP packet goes to the TFTP port; the second one
101          * comes from some *other* port, but goes back to the same
102          * IP address and port as the ones from which the first packet
103          * came; all subsequent packets go between those two IP addresses
104          * and ports.
105          *
106          * If this packet went to the TFTP port, we check to see if
107          * there's already a conversation with one address/port pair
108          * matching the source IP address and port of this packet,
109          * the other address matching the destination IP address of this
110          * packet, and any destination port.
111          *
112          * If not, we create one, with its address 1/port 1 pair being
113          * the source address/port of this packet, its address 2 being
114          * the destination address of this packet, and its port 2 being
115          * wildcarded, and give it the TFTP dissector as a dissector.
116          */
117         if (pinfo->destport == UDP_PORT_TFTP) {
118           conversation = find_conversation(&pinfo->src, &pinfo->dst, PT_UDP,
119                                            pinfo->srcport, 0, NO_PORT_B);
120           if (conversation == NULL) {
121             conversation = conversation_new(&pinfo->src, &pinfo->dst, PT_UDP,
122                                             pinfo->srcport, 0, NO_PORT2);
123             conversation_set_dissector(conversation, tftp_handle);
124           }
125         }
126
127         if (check_col(pinfo->cinfo, COL_PROTOCOL))
128                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TFTP");
129
130         opcode = tvb_get_ntohs(tvb, offset);
131
132         if (check_col(pinfo->cinfo, COL_INFO)) {
133
134           col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
135             val_to_str(opcode, tftp_opcode_vals, "Unknown (0x%04x)"));
136
137         }
138
139         if (tree) {
140
141           ti = proto_tree_add_item(tree, proto_tftp, tvb, offset, -1, FALSE);
142           tftp_tree = proto_item_add_subtree(ti, ett_tftp);
143
144           proto_tree_add_uint(tftp_tree, hf_tftp_opcode, tvb,
145                             offset, 2, opcode);
146         }
147         offset += 2;
148
149         switch (opcode) {
150
151         case TFTP_RRQ:
152           i1 = tvb_strsize(tvb, offset);
153           if (tree) {
154             proto_tree_add_item(tftp_tree, hf_tftp_source_file,
155                             tvb, offset, i1, FALSE);
156           }
157           if (check_col(pinfo->cinfo, COL_INFO)) {
158             col_append_fstr(pinfo->cinfo, COL_INFO, ", File: %s",
159                             tvb_get_ptr(tvb, offset, i1));
160           }
161           offset += i1;
162
163           i1 = tvb_strsize(tvb, offset);
164           if (tree) {
165             ti = proto_tree_add_item(tftp_tree, hf_tftp_transfer_type,
166                             tvb, offset, i1, FALSE);
167           }
168           if (check_col(pinfo->cinfo, COL_INFO)) {
169             col_append_fstr(pinfo->cinfo, COL_INFO, ", Transfer type: %s",
170                             tvb_get_ptr(tvb, offset, i1));
171           }
172           offset += i1;
173
174           if (tree)
175             tftp_dissect_options(tvb, offset, tftp_tree);
176           break;
177
178         case TFTP_WRQ:
179           i1 = tvb_strsize(tvb, offset);
180           if (tree) {
181             proto_tree_add_item(tftp_tree, hf_tftp_destination_file,
182                             tvb, offset, i1, FALSE);
183           }
184           if (check_col(pinfo->cinfo, COL_INFO)) {
185             col_append_fstr(pinfo->cinfo, COL_INFO, ", File: %s",
186                             tvb_get_ptr(tvb, offset, i1));
187           }
188           offset += i1;
189
190           i1 = tvb_strsize(tvb, offset);
191           if (tree) {
192             ti = proto_tree_add_item(tftp_tree, hf_tftp_transfer_type,
193                             tvb, offset, i1, FALSE);
194           }
195           if (check_col(pinfo->cinfo, COL_INFO)) {
196             col_append_fstr(pinfo->cinfo, COL_INFO, ", Transfer type: %s",
197                             tvb_get_ptr(tvb, offset, i1));
198           }
199           offset += i1;
200
201           if (tree)
202             tftp_dissect_options(tvb, offset, tftp_tree);
203           break;
204
205         case TFTP_DATA:
206           blocknum = tvb_get_ntohs(tvb, offset);
207           if (tree) {
208             proto_tree_add_uint(tftp_tree, hf_tftp_blocknum, tvb, offset, 2,
209                             blocknum);
210           }
211           offset += 2;
212
213           bytes = tvb_reported_length_remaining(tvb, offset);
214
215           if (check_col(pinfo->cinfo, COL_INFO)) {
216             col_append_fstr(pinfo->cinfo, COL_INFO, ", Block: %i%s",
217                     blocknum,
218                     (bytes < 512)?" (last)":"" );
219           }
220
221           if (tree) {
222             proto_tree_add_text(tftp_tree, tvb, offset, -1,
223                 "Data (%d bytes)", bytes);
224           }
225           break;
226
227         case TFTP_ACK:
228           blocknum = tvb_get_ntohs(tvb, offset);
229           if (tree) {
230             proto_tree_add_uint(tftp_tree, hf_tftp_blocknum, tvb, offset, 2,
231                             blocknum);
232           }
233           if (check_col(pinfo->cinfo, COL_INFO)) {
234             col_append_fstr(pinfo->cinfo, COL_INFO, ", Block: %i",
235                             blocknum);
236           }
237           break;
238
239         case TFTP_ERROR:
240           error = tvb_get_ntohs(tvb, offset);
241           if (tree) {
242             proto_tree_add_uint(tftp_tree, hf_tftp_error_code, tvb, offset, 2,
243                             error);
244           }
245           if (check_col(pinfo->cinfo, COL_INFO)) {
246             col_append_fstr(pinfo->cinfo, COL_INFO, ", Code: %s",
247                             val_to_str(error, tftp_error_code_vals, "Unknown (%u)"));
248           }
249           offset += 2;
250
251           i1 = tvb_strsize(tvb, offset);
252           if (tree) {
253             proto_tree_add_item(tftp_tree, hf_tftp_error_string, tvb, offset,
254                 i1, FALSE);
255           }
256           if (check_col(pinfo->cinfo, COL_INFO)) {
257             col_append_fstr(pinfo->cinfo, COL_INFO, ", Message: %s",
258                             tvb_get_ptr(tvb, offset, i1));
259           }
260           break;
261
262         case TFTP_OACK:
263           if (tree)
264             tftp_dissect_options(tvb, offset, tftp_tree);
265           break;
266
267         default:
268           if (tree) {
269             proto_tree_add_text(tftp_tree, tvb, offset, -1,
270                 "Data (%d bytes)", tvb_reported_length_remaining(tvb, offset));
271           }
272           break;
273
274         }
275 }
276
277 static void
278 tftp_dissect_options(tvbuff_t *tvb, int offset, proto_tree *tree)
279 {
280         int option_len, value_len;
281         int value_offset;
282
283         while (tvb_offset_exists(tvb, offset)) {
284           option_len = tvb_strsize(tvb, offset);        /* length of option */
285           value_offset = offset + option_len;
286           value_len = tvb_strsize(tvb, value_offset);   /* length of value */
287           proto_tree_add_text(tree, tvb, offset, option_len+value_len,
288                   "Option: %s = %s",
289                   tvb_get_ptr(tvb, offset, option_len),
290                   tvb_get_ptr(tvb, value_offset, value_len));
291           offset += option_len + value_len;
292         }
293 }
294
295 void
296 proto_register_tftp(void)
297 {
298   static hf_register_info hf[] = {
299     { &hf_tftp_opcode,
300       { "Opcode",             "tftp.opcode",
301         FT_UINT16, BASE_DEC, VALS(tftp_opcode_vals), 0x0,
302         "TFTP message type", HFILL }},
303
304     { &hf_tftp_source_file,
305       { "Source File",        "tftp.source_file",
306         FT_STRINGZ, BASE_DEC, NULL, 0x0,
307         "TFTP source file name", HFILL }},
308
309     { &hf_tftp_destination_file,
310       { "DESTINATION File",   "tftp.destination_file",
311         FT_STRINGZ, BASE_DEC, NULL, 0x0,
312         "TFTP source file name", HFILL }},
313
314     { &hf_tftp_transfer_type,
315       { "Type",               "tftp.type",
316         FT_STRINGZ, BASE_DEC, NULL, 0x0,
317         "TFTP transfer type", HFILL }},
318
319     { &hf_tftp_blocknum,
320       { "Block",              "tftp.block",
321         FT_UINT16, BASE_DEC, NULL, 0x0,
322         "Block number", HFILL }},
323
324     { &hf_tftp_error_code,
325       { "Error code",         "tftp.error.code",
326         FT_UINT16, BASE_DEC, VALS(tftp_error_code_vals), 0x0,
327         "Error code in case of TFTP error message", HFILL }},
328
329     { &hf_tftp_error_string,
330       { "Error message",      "tftp.error.message",
331         FT_STRINGZ, BASE_DEC, NULL, 0x0,
332         "Error string in case of TFTP error message", HFILL }}
333   };
334   static gint *ett[] = {
335     &ett_tftp,
336   };
337
338   proto_tftp = proto_register_protocol("Trivial File Transfer Protocol",
339                                        "TFTP", "tftp");
340   proto_register_field_array(proto_tftp, hf, array_length(hf));
341   proto_register_subtree_array(ett, array_length(ett));
342
343   tftp_handle = create_dissector_handle(dissect_tftp, proto_tftp);
344 }
345
346 void
347 proto_reg_handoff_tftp(void)
348 {
349   dissector_add("udp.port", UDP_PORT_TFTP, tftp_handle);
350 }