2 * Routines for telnet packet dissection
3 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
5 * $Id: packet-telnet.c,v 1.23 2001/01/09 06:31:44 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@zing.org>
9 * Copyright 1998 Gerald Combs
11 * Copied from packet-pop.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
47 static int proto_telnet = -1;
49 static gint ett_telnet = -1;
50 static gint ett_telnet_subopt = -1;
52 /* Some defines for Telnet */
54 #define TCP_PORT_TELNET 23
77 static const char *options[] = {
78 "Binary Transmission",
82 "Approx Message Size Negotiation",
85 "Remote Controlled Trans and Echo",
88 "Output Carriage-Return Disposition",
89 "Output Horizontal Tab Stops",
90 "Output Horizontal Tab Disposition",
91 "Output Formfeed Disposition",
92 "Output Vertical Tabstops",
93 "Output Vertical Tab Disposition",
94 "Output Linefeed Disposition",
98 "Data Entry Terminal",
104 "TACACS User Identification",
106 "Terminal Location Number",
107 "Telnet 3270 Regime",
109 "Negotiate About Window Size",
111 "Remote Flow Control",
113 "X Display Location",
114 "Environment Option",
115 "Authentication Option",
117 "New Environment Option",
121 #define NOPTIONS (sizeof options / sizeof options[0])
124 telnet_sub_option(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
126 proto_tree *ti, *option_tree;
127 int offset = start_offset;
133 offset += 2; /* skip IAC and SB */
135 /* Figure out the option and type */
136 opt_byte = tvb_get_guint8(tvb, offset);
137 if (opt_byte > NOPTIONS)
138 opt = "<unknown option>";
140 opt = options[opt_byte];
142 req = tvb_get_guint8(tvb, offset);
145 /* Search for an IAC. */
146 len = tvb_length_remaining(tvb, offset);
147 offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
149 /* None found - run to the end of the packet. */
153 subneg_len = offset - start_offset;
155 ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
156 "Suboption Begin: %s", opt);
158 option_tree = proto_item_add_subtree(ti, ett_telnet_subopt);
160 proto_tree_add_text(option_tree, tvb, start_offset + 2, 2,
161 "%s %s", (req ? "Send your" : "Here's my"), opt);
163 if (req == 0) { /* Add the value */
164 proto_tree_add_text(option_tree, tvb, start_offset + 4, subneg_len - 4,
165 "Value: %s", tvb_format_text(tvb, start_offset + 4, subneg_len - 4));
171 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
172 int start_offset, char *type)
174 int offset = start_offset;
178 offset += 2; /* skip IAC and WILL,WONT,DO,DONT} */
179 opt_byte = tvb_get_guint8(tvb, offset);
180 if (opt_byte > NOPTIONS)
181 opt = "<unknown option>";
183 opt = options[opt_byte];
186 proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
187 "Command: %s %s", type, opt);
192 telnet_command(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
194 int offset = start_offset;
197 offset += 1; /* skip IAC */
198 optcode = tvb_get_guint8(tvb, offset);
203 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
204 "Command: End of File");
208 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
209 "Command: Suspend Current Process");
213 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
214 "Command: Abort Process");
218 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
219 "Command: End of Record");
223 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
224 "Command: Suboption End");
228 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
229 "Command: No Operation");
233 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
234 "Command: Data Mark");
238 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
243 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
244 "Command: Interrupt Process");
248 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
249 "Command: Abort Output");
253 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
254 "Command: Are You There?");
258 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
259 "Command: Escape Character");
263 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
264 "Command: Erase Line");
268 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
269 "Command: Go Ahead");
273 offset = telnet_sub_option(telnet_tree, tvb, start_offset);
277 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
282 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
287 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
292 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
301 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
306 gboolean last_char_was_cr;
308 while (len != 0 && tvb_offset_exists(tvb, offset)) {
310 * Find the end of the line.
312 linelen = tvb_find_line_end(tvb, offset, len, &next_offset);
313 len -= next_offset - offset; /* subtract out the line's characters */
316 * In Telnet, CR NUL is the way you send a CR by itself in the
317 * default ASCII mode; don't treat CR by itself as a line ending,
318 * treat only CR NUL, CR LF, or LF by itself as a line ending.
320 if (next_offset == offset + linelen + 1 && len >= 1) {
322 * Well, we saw a one-character line ending, so either it's a CR
323 * or an LF; we have at least two characters left, including the
326 * If the line ending is a CR, skip all subsequent CRs; at
327 * least one capture appeared to have multiple CRs at the end of
330 if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
331 last_char_was_cr = TRUE;
332 while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
333 c = tvb_get_guint8(tvb, next_offset);
334 next_offset++; /* skip over that character */
336 if (c == '\n' || (c == '\0' && last_char_was_cr)) {
338 * LF is a line ending, whether preceded by CR or not.
339 * NUL is a line ending if preceded by CR.
343 last_char_was_cr = (c == '\r');
349 * Now compute the length of the line *including* the end-of-line
350 * indication, if any; we display it all.
352 linelen = next_offset - offset;
354 proto_tree_add_text(tree, tvb, offset, linelen,
356 tvb_format_text(tvb, offset, linelen));
357 offset = next_offset;
362 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
364 proto_tree *telnet_tree, *ti;
366 CHECK_DISPLAY_AS_DATA(proto_telnet, tvb, pinfo, tree);
368 pinfo->current_proto = "TELNET";
370 if (check_col(pinfo->fd, COL_PROTOCOL))
371 col_set_str(pinfo->fd, COL_PROTOCOL, "TELNET");
373 if (check_col(pinfo->fd, COL_INFO))
374 col_add_fstr(pinfo->fd, COL_INFO, "Telnet Data ...");
382 ti = proto_tree_add_item(tree, proto_telnet, tvb, offset,
383 tvb_length_remaining(tvb, offset), FALSE);
384 telnet_tree = proto_item_add_subtree(ti, ett_telnet);
387 * Scan through the buffer looking for an IAC byte.
389 while ((len = tvb_length_remaining(tvb, offset)) > 0) {
390 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
391 if (iac_offset != -1) {
393 * We found an IAC byte.
394 * If there's any data before it, add that data to the
395 * tree, a line at a time.
397 data_len = iac_offset - offset;
399 telnet_add_text(telnet_tree, tvb, offset, data_len);
402 * Now interpret the command.
404 offset = telnet_command(telnet_tree, tvb, iac_offset);
408 * We found no IAC byte, so what remains in the buffer
409 * is the last of the data in the packet.
410 * Add it to the tree, a line at a time, and then quit.
412 telnet_add_text(telnet_tree, tvb, offset, len);
420 proto_register_telnet(void)
422 /* static hf_register_info hf[] = {
424 { "Name", "telnet.abbreviation", TYPE, VALS_POINTER }},
426 static gint *ett[] = {
431 proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
432 /* proto_register_field_array(proto_telnet, hf, array_length(hf));*/
433 proto_register_subtree_array(ett, array_length(ett));
437 proto_reg_handoff_telnet(void)
439 dissector_add("tcp.port", TCP_PORT_TELNET, dissect_telnet,