2 * Routines for RFC 1436 Gopher protocol dissection
3 * Copyright 2010, Gerald Combs <gerald@wireshark.org>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * Copied from packet-banana.c
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 * RFC 1436: http://tools.ietf.org/html/rfc1436
30 * http://en.wikipedia.org/wiki/Gopher_%28protocol%29
37 #include <epan/packet.h>
38 #include <epan/prefs.h>
40 /* Initialize the protocol and registered fields */
41 static int proto_gopher
= -1;
42 static int hf_gopher_request
= -1;
43 static int hf_gopher_dir_item
= -1;
44 static int hf_gopher_di_type
= -1;
45 static int hf_gopher_di_name
= -1;
46 static int hf_gopher_di_selector
= -1;
47 static int hf_gopher_di_host
= -1;
48 static int hf_gopher_di_port
= -1;
49 static int hf_gopher_unknown
= -1;
51 /* Initialize the subtree pointers */
52 static gint ett_gopher
= -1;
53 static gint ett_dir_item
= -1;
55 static dissector_handle_t gopher_handle
;
57 /* RFC 1436 section 3.8 */
58 static const value_string item_types
[] = {
59 { '+', "Redundant server" },
62 { '2', "CSO phone book entity" },
64 { '4', "BinHexed Macintosh file" },
65 { '5', "DOS binary file" },
66 { '6', "Uuencoded file" },
67 { '7', "Index server" },
68 { '8', "Telnet session" },
69 { '9', "Binary file" },
71 { 'h', "HTML file" }, /* Not in RFC 1436 */
72 { 'i', "Informational message"}, /* Not in RFC 1436 */
73 { 'I', "Image file" },
74 { 's', "Audio file" }, /* Not in RFC 1436 */
75 { 'T', "Tn3270 session" },
79 #define TCP_DEFAULT_RANGE "70"
81 static range_t
*global_gopher_tcp_range
= NULL
;
82 static range_t
*gopher_tcp_range
= NULL
;
84 /* Returns TRUE if the packet is from a client */
86 is_client(packet_info
*pinfo
) {
87 if (value_is_in_range(gopher_tcp_range
, pinfo
->destport
)) {
93 /* Name + Tab + Selector + Tab + Host + Tab + Port */
94 #define MAX_DIR_LINE_LEN (70 + 1 + 255 + 1 + 255 + 1 + 5)
95 #define MIN_DIR_LINE_LEN (0 + 1 + 0 + 1 + 1 + 1 + 1)
97 find_dir_tokens(tvbuff_t
*tvb
, gint name_start
, gint
*sel_start
, gint
*host_start
, gint
*port_start
, gint
*line_len
, gint
*next_offset
) {
100 if (tvb_length_remaining(tvb
, name_start
) < MIN_DIR_LINE_LEN
)
103 if (! (sel_start
&& host_start
&& port_start
&& line_len
&& next_offset
) )
106 *line_len
= tvb_find_line_end(tvb
, name_start
, MAX_DIR_LINE_LEN
, next_offset
, FALSE
);
107 if (*line_len
< MIN_DIR_LINE_LEN
)
111 *sel_start
= tvb_find_guint8(tvb
, name_start
, remain
, '\t') + 1;
112 if (*sel_start
< name_start
+ 1)
115 remain
-= *sel_start
- name_start
;
116 *host_start
= tvb_find_guint8(tvb
, *sel_start
, remain
, '\t') + 1;
117 if (*host_start
< *sel_start
+ 1)
120 remain
-= *host_start
- *sel_start
;
121 *port_start
= tvb_find_guint8(tvb
, *host_start
, remain
, '\t') + 1;
122 if (*port_start
< *host_start
+ 1)
128 /* Dissect the packets */
131 dissect_gopher(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
133 proto_tree
*gopher_tree
, *dir_tree
= NULL
;
134 gboolean client
= is_client(pinfo
);
136 const gchar
*request
= "[Invalid request]";
137 gboolean is_dir
= FALSE
;
138 gint offset
= 0, next_offset
;
139 gint sel_start
, host_start
, port_start
;
142 /* Fill in our protocol and info columns */
143 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Gopher");
146 line_len
= tvb_find_line_end(tvb
, 0, -1, NULL
, FALSE
);
148 request
= "[Directory list]";
149 } else if (line_len
> 0) {
150 request
= tvb_get_string(wmem_packet_scope(), tvb
, 0, line_len
);
152 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Request: %s", request
);
154 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Response");
158 /* Create display subtree for the protocol */
159 ti
= proto_tree_add_item(tree
, proto_gopher
, tvb
, 0, -1, ENC_NA
);
160 gopher_tree
= proto_item_add_subtree(ti
, ett_gopher
);
163 proto_item_append_text(ti
, " request: %s", request
);
164 proto_tree_add_string(gopher_tree
, hf_gopher_request
, tvb
,
167 proto_item_append_text(ti
, " response: ");
169 while (find_dir_tokens(tvb
, offset
+ 1, &sel_start
, &host_start
, &port_start
, &line_len
, &next_offset
)) {
170 if (!is_dir
) { /* First time */
171 proto_item_append_text(ti
, "[Directory list]");
172 col_append_str(pinfo
->cinfo
, COL_INFO
, ": [Directory list]");
175 name
= tvb_get_string(wmem_packet_scope(), tvb
, offset
+ 1, sel_start
- offset
- 2);
176 ti
= proto_tree_add_string(gopher_tree
, hf_gopher_dir_item
, tvb
,
177 offset
, line_len
+ 1, name
);
178 dir_tree
= proto_item_add_subtree(ti
, ett_dir_item
);
179 proto_tree_add_item(dir_tree
, hf_gopher_di_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
180 proto_tree_add_item(dir_tree
, hf_gopher_di_name
, tvb
, offset
+ 1,
181 sel_start
- offset
- 2, ENC_ASCII
|ENC_NA
);
182 proto_tree_add_item(dir_tree
, hf_gopher_di_selector
, tvb
, sel_start
,
183 host_start
- sel_start
- 1, ENC_ASCII
|ENC_NA
);
184 proto_tree_add_item(dir_tree
, hf_gopher_di_host
, tvb
, host_start
,
185 port_start
- host_start
- 1, ENC_ASCII
|ENC_NA
);
186 proto_tree_add_item(dir_tree
, hf_gopher_di_port
, tvb
, port_start
,
187 line_len
- (port_start
- offset
- 1), ENC_ASCII
|ENC_NA
);
189 offset
= next_offset
;
193 proto_item_append_text(ti
, "[Unknown]");
194 proto_tree_add_item(gopher_tree
, hf_gopher_unknown
, tvb
, 0, -1, ENC_ASCII
|ENC_NA
);
200 /* Return the amount of data this dissector was able to dissect */
201 return tvb_length(tvb
);
204 /* Preference callbacks */
206 range_delete_gopher_tcp_callback(guint32 port
) {
207 dissector_delete_uint("tcp.port", port
, gopher_handle
);
211 range_add_gopher_tcp_callback(guint32 port
) {
212 dissector_add_uint("tcp.port", port
, gopher_handle
);
216 gopher_prefs_apply(void) {
217 range_foreach(gopher_tcp_range
, range_delete_gopher_tcp_callback
);
218 g_free(gopher_tcp_range
);
219 gopher_tcp_range
= range_copy(global_gopher_tcp_range
);
220 range_foreach(gopher_tcp_range
, range_add_gopher_tcp_callback
);
223 /* Register the protocol with Wireshark */
226 proto_register_gopher(void)
228 static hf_register_info hf
[] = {
229 { &hf_gopher_request
,
230 { "Gopher client request", "gopher.request",
231 FT_STRING
, BASE_NONE
, NULL
, 0,
235 { &hf_gopher_dir_item
,
236 { "Directory item", "gopher.directory",
237 FT_STRING
, BASE_NONE
, NULL
, 0,
240 { &hf_gopher_di_type
,
241 { "Type", "gopher.directory.type",
242 FT_UINT8
, BASE_HEX
, VALS(item_types
), 0,
245 { &hf_gopher_di_name
,
246 { "Name", "gopher.directory.name",
247 FT_STRING
, BASE_NONE
, NULL
, 0,
250 { &hf_gopher_di_selector
,
251 { "Selector", "gopher.directory.selector",
252 FT_STRING
, BASE_NONE
, NULL
, 0,
255 { &hf_gopher_di_host
,
256 { "Host", "gopher.directory.host",
257 FT_STRING
, BASE_NONE
, NULL
, 0,
260 { &hf_gopher_di_port
,
261 { "Port", "gopher.directory.port",
262 FT_STRING
, BASE_NONE
, NULL
, 0,
266 { &hf_gopher_unknown
,
267 { "Unknown Gopher transaction data", "gopher.unknown",
268 FT_STRING
, BASE_NONE
, NULL
, 0,
273 module_t
*gopher_module
;
275 /* Setup protocol subtree array */
276 static gint
*ett
[] = {
281 /* Register the protocol name and description */
282 proto_gopher
= proto_register_protocol("Gopher",
285 /* Required function calls to register the header fields and subtrees used */
286 proto_register_field_array(proto_gopher
, hf
, array_length(hf
));
287 proto_register_subtree_array(ett
, array_length(ett
));
289 /* Initialize dissector preferences */
290 gopher_module
= prefs_register_protocol(proto_gopher
, gopher_prefs_apply
);
292 range_convert_str(&global_gopher_tcp_range
, TCP_DEFAULT_RANGE
, 65535);
293 gopher_tcp_range
= range_empty();
294 prefs_register_range_preference(gopher_module
, "tcp.port", "TCP Ports",
296 &global_gopher_tcp_range
, 65535);
300 proto_reg_handoff_gopher(void)
302 gopher_handle
= new_create_dissector_handle(dissect_gopher
, proto_gopher
);
303 gopher_prefs_apply();
307 * Editor modelines - http://www.wireshark.org/tools/modelines.html
312 * indent-tabs-mode: nil
315 * vi: set shiftwidth=4 tabstop=8 expandtab:
316 * :indentSize=4:tabSize=8:noTabs=true: