2 * Routines for RFC 1436 Gopher protocol dissection
3 * Copyright 2010, Gerald Combs <gerald@wireshark.org>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * Copied from packet-banana.c
11 * SPDX-License-Identifier: GPL-2.0-or-later
15 * RFC 1436: https://tools.ietf.org/html/rfc1436
16 * http://en.wikipedia.org/wiki/Gopher_%28protocol%29
21 #include <epan/packet.h>
22 #include <epan/prefs.h>
24 void proto_register_gopher(void);
25 void proto_reg_handoff_gopher(void);
27 /* Initialize the protocol and registered fields */
28 static int proto_gopher
;
29 static int hf_gopher_request
;
30 static int hf_gopher_dir_item
;
31 static int hf_gopher_di_type
;
32 static int hf_gopher_di_name
;
33 static int hf_gopher_di_selector
;
34 static int hf_gopher_di_host
;
35 static int hf_gopher_di_port
;
36 static int hf_gopher_unknown
;
38 /* Initialize the subtree pointers */
39 static int ett_gopher
;
40 static int ett_dir_item
;
42 static dissector_handle_t gopher_handle
;
44 /* RFC 1436 section 3.8 */
45 static const value_string item_types
[] = {
46 { '+', "Redundant server" },
49 { '2', "CSO phone book entity" },
51 { '4', "BinHexed Macintosh file" },
52 { '5', "DOS binary file" },
53 { '6', "Uuencoded file" },
54 { '7', "Index server" },
55 { '8', "Telnet session" },
56 { '9', "Binary file" },
58 { 'h', "HTML file" }, /* Not in RFC 1436 */
59 { 'i', "Informational message"}, /* Not in RFC 1436 */
60 { 'I', "Image file" },
61 { 's', "Audio file" }, /* Not in RFC 1436 */
62 { 'T', "Tn3270 session" },
66 #define TCP_DEFAULT_RANGE "70"
68 static range_t
*gopher_tcp_range
;
70 /* Returns true if the packet is from a client */
72 is_client(packet_info
*pinfo
) {
73 return value_is_in_range(gopher_tcp_range
, pinfo
->destport
);
76 /* Name + Tab + Selector + Tab + Host + Tab + Port */
77 #define MAX_DIR_LINE_LEN (70 + 1 + 255 + 1 + 255 + 1 + 5)
78 #define MIN_DIR_LINE_LEN (0 + 1 + 0 + 1 + 1 + 1 + 1)
80 find_dir_tokens(tvbuff_t
*tvb
, int name_start
, int *sel_start
, int *host_start
, int *port_start
, int *line_len
, int *next_offset
) {
83 if (tvb_captured_length_remaining(tvb
, name_start
) < MIN_DIR_LINE_LEN
)
86 if (! (sel_start
&& host_start
&& port_start
&& line_len
&& next_offset
) )
89 *line_len
= tvb_find_line_end(tvb
, name_start
, MAX_DIR_LINE_LEN
, next_offset
, false);
90 if (*line_len
< MIN_DIR_LINE_LEN
)
94 *sel_start
= tvb_find_uint8(tvb
, name_start
, remain
, '\t') + 1;
95 if (*sel_start
< name_start
+ 1)
98 remain
-= *sel_start
- name_start
;
99 *host_start
= tvb_find_uint8(tvb
, *sel_start
, remain
, '\t') + 1;
100 if (*host_start
< *sel_start
+ 1)
103 remain
-= *host_start
- *sel_start
;
104 *port_start
= tvb_find_uint8(tvb
, *host_start
, remain
, '\t') + 1;
105 if (*port_start
< *host_start
+ 1)
111 /* Dissect the packets */
114 dissect_gopher(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
116 proto_tree
*gopher_tree
, *dir_tree
= NULL
;
117 bool client
= is_client(pinfo
);
119 const char *request
= "[Invalid request]";
121 int offset
= 0, next_offset
;
122 int sel_start
, host_start
, port_start
;
125 /* Fill in our protocol and info columns */
126 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Gopher");
129 line_len
= tvb_find_line_end(tvb
, 0, -1, NULL
, false);
131 request
= "[Directory list]";
132 } else if (line_len
> 0) {
133 request
= tvb_get_string_enc(pinfo
->pool
, tvb
, 0, line_len
, ENC_ASCII
);
135 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Request: %s", request
);
137 col_set_str(pinfo
->cinfo
, COL_INFO
, "Response");
141 /* Create display subtree for the protocol */
142 ti
= proto_tree_add_item(tree
, proto_gopher
, tvb
, 0, -1, ENC_NA
);
143 gopher_tree
= proto_item_add_subtree(ti
, ett_gopher
);
146 proto_item_append_text(ti
, " request: %s", request
);
147 proto_tree_add_string(gopher_tree
, hf_gopher_request
, tvb
,
150 proto_item_append_text(ti
, " response: ");
152 while (find_dir_tokens(tvb
, offset
+ 1, &sel_start
, &host_start
, &port_start
, &line_len
, &next_offset
)) {
153 if (!is_dir
) { /* First time */
154 proto_item_append_text(ti
, "[Directory list]");
155 col_append_str(pinfo
->cinfo
, COL_INFO
, ": [Directory list]");
158 name
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
+ 1, sel_start
- offset
- 2, ENC_ASCII
);
159 ti
= proto_tree_add_string(gopher_tree
, hf_gopher_dir_item
, tvb
,
160 offset
, line_len
+ 1, name
);
161 dir_tree
= proto_item_add_subtree(ti
, ett_dir_item
);
162 proto_tree_add_item(dir_tree
, hf_gopher_di_type
, tvb
, offset
, 1, ENC_ASCII
|ENC_NA
);
163 proto_tree_add_item(dir_tree
, hf_gopher_di_name
, tvb
, offset
+ 1,
164 sel_start
- offset
- 2, ENC_ASCII
);
165 proto_tree_add_item(dir_tree
, hf_gopher_di_selector
, tvb
, sel_start
,
166 host_start
- sel_start
- 1, ENC_ASCII
);
167 proto_tree_add_item(dir_tree
, hf_gopher_di_host
, tvb
, host_start
,
168 port_start
- host_start
- 1, ENC_ASCII
);
169 proto_tree_add_item(dir_tree
, hf_gopher_di_port
, tvb
, port_start
,
170 line_len
- (port_start
- offset
- 1), ENC_ASCII
);
172 offset
= next_offset
;
176 proto_item_append_text(ti
, "[Unknown]");
177 proto_tree_add_item(gopher_tree
, hf_gopher_unknown
, tvb
, 0, -1, ENC_ASCII
);
183 /* Return the amount of data this dissector was able to dissect */
184 return tvb_captured_length(tvb
);
187 /* Preference callbacks */
189 gopher_prefs_apply(void) {
191 gopher_tcp_range
= prefs_get_range_value("gopher", "tcp.port");
194 /* Register the protocol with Wireshark */
197 proto_register_gopher(void)
199 static hf_register_info hf
[] = {
200 { &hf_gopher_request
,
201 { "Gopher client request", "gopher.request",
202 FT_STRING
, BASE_NONE
, NULL
, 0,
206 { &hf_gopher_dir_item
,
207 { "Directory item", "gopher.directory",
208 FT_STRING
, BASE_NONE
, NULL
, 0,
211 { &hf_gopher_di_type
,
212 { "Type", "gopher.directory.type",
213 FT_CHAR
, BASE_HEX
, VALS(item_types
), 0,
216 { &hf_gopher_di_name
,
217 { "Name", "gopher.directory.name",
218 FT_STRING
, BASE_NONE
, NULL
, 0,
221 { &hf_gopher_di_selector
,
222 { "Selector", "gopher.directory.selector",
223 FT_STRING
, BASE_NONE
, NULL
, 0,
226 { &hf_gopher_di_host
,
227 { "Host", "gopher.directory.host",
228 FT_STRING
, BASE_NONE
, NULL
, 0,
231 { &hf_gopher_di_port
,
232 { "Port", "gopher.directory.port",
233 FT_STRING
, BASE_NONE
, NULL
, 0,
237 { &hf_gopher_unknown
,
238 { "Unknown Gopher transaction data", "gopher.unknown",
239 FT_STRING
, BASE_NONE
, NULL
, 0,
244 /* Setup protocol subtree array */
245 static int *ett
[] = {
250 /* Register the protocol name and description */
251 proto_gopher
= proto_register_protocol("Gopher", "Gopher", "gopher");
253 /* Register the dissector handle */
254 gopher_handle
= register_dissector("gopher", dissect_gopher
, proto_gopher
);
256 /* Required function calls to register the header fields and subtrees used */
257 proto_register_field_array(proto_gopher
, hf
, array_length(hf
));
258 proto_register_subtree_array(ett
, array_length(ett
));
260 /* Preferences for this module are generated when registering with
261 dissector tables and need the callback function to get the
264 prefs_register_protocol(proto_gopher
, gopher_prefs_apply
);
268 proto_reg_handoff_gopher(void)
270 dissector_add_uint_range_with_preference("tcp.port", TCP_DEFAULT_RANGE
, gopher_handle
);
271 gopher_prefs_apply();
275 * Editor modelines - https://www.wireshark.org/tools/modelines.html
280 * indent-tabs-mode: nil
283 * vi: set shiftwidth=4 tabstop=8 expandtab:
284 * :indentSize=4:tabSize=8:noTabs=true: