2 * Routines for telnet packet dissection
3 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
5 * $Id: packet-telnet.c,v 1.27 2001/12/10 00:25:40 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
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 if (subneg_len > 0) {
156 ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
157 "Suboption Begin: %s", opt);
159 option_tree = proto_item_add_subtree(ti, ett_telnet_subopt);
161 proto_tree_add_text(option_tree, tvb, start_offset + 2, 2,
162 "%s %s", (req ? "Send your" : "Here's my"), opt);
164 if (req == 0) { /* Add the value */
165 proto_tree_add_text(option_tree, tvb, start_offset + 4, subneg_len - 4,
166 "Value: %s", tvb_format_text(tvb, start_offset + 4, subneg_len - 4));
173 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
174 int start_offset, char *type)
176 int offset = start_offset;
180 offset += 2; /* skip IAC and WILL,WONT,DO,DONT} */
181 opt_byte = tvb_get_guint8(tvb, offset);
182 if (opt_byte > NOPTIONS)
183 opt = "<unknown option>";
185 opt = options[opt_byte];
188 proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
189 "Command: %s %s", type, opt);
194 telnet_command(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
196 int offset = start_offset;
199 offset += 1; /* skip IAC */
200 optcode = tvb_get_guint8(tvb, offset);
205 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
206 "Command: End of File");
210 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
211 "Command: Suspend Current Process");
215 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
216 "Command: Abort Process");
220 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
221 "Command: End of Record");
225 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
226 "Command: Suboption End");
230 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
231 "Command: No Operation");
235 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
236 "Command: Data Mark");
240 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
245 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
246 "Command: Interrupt Process");
250 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
251 "Command: Abort Output");
255 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
256 "Command: Are You There?");
260 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
261 "Command: Escape Character");
265 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
266 "Command: Erase Line");
270 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
271 "Command: Go Ahead");
275 offset = telnet_sub_option(telnet_tree, tvb, start_offset);
279 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
284 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
289 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
294 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
303 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
308 gboolean last_char_was_cr;
310 while (len != 0 && tvb_offset_exists(tvb, offset)) {
312 * Find the end of the line.
314 linelen = tvb_find_line_end(tvb, offset, len, &next_offset);
315 len -= next_offset - offset; /* subtract out the line's characters */
318 * In Telnet, CR NUL is the way you send a CR by itself in the
319 * default ASCII mode; don't treat CR by itself as a line ending,
320 * treat only CR NUL, CR LF, or LF by itself as a line ending.
322 if (next_offset == offset + linelen + 1 && len >= 1) {
324 * Well, we saw a one-character line ending, so either it's a CR
325 * or an LF; we have at least two characters left, including the
328 * If the line ending is a CR, skip all subsequent CRs; at
329 * least one capture appeared to have multiple CRs at the end of
332 if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
333 last_char_was_cr = TRUE;
334 while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
335 c = tvb_get_guint8(tvb, next_offset);
336 next_offset++; /* skip over that character */
338 if (c == '\n' || (c == '\0' && last_char_was_cr)) {
340 * LF is a line ending, whether preceded by CR or not.
341 * NUL is a line ending if preceded by CR.
345 last_char_was_cr = (c == '\r');
351 * Now compute the length of the line *including* the end-of-line
352 * indication, if any; we display it all.
354 linelen = next_offset - offset;
356 proto_tree_add_text(tree, tvb, offset, linelen,
358 tvb_format_text(tvb, offset, linelen));
359 offset = next_offset;
364 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
366 proto_tree *telnet_tree, *ti;
368 if (check_col(pinfo->cinfo, COL_PROTOCOL))
369 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
371 if (check_col(pinfo->cinfo, COL_INFO))
372 col_add_fstr(pinfo->cinfo, COL_INFO, "Telnet Data ...");
380 ti = proto_tree_add_item(tree, proto_telnet, tvb, offset,
381 tvb_length_remaining(tvb, offset), FALSE);
382 telnet_tree = proto_item_add_subtree(ti, ett_telnet);
385 * Scan through the buffer looking for an IAC byte.
387 while ((len = tvb_length_remaining(tvb, offset)) > 0) {
388 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
389 if (iac_offset != -1) {
391 * We found an IAC byte.
392 * If there's any data before it, add that data to the
393 * tree, a line at a time.
395 data_len = iac_offset - offset;
397 telnet_add_text(telnet_tree, tvb, offset, data_len);
400 * Now interpret the command.
402 offset = telnet_command(telnet_tree, tvb, iac_offset);
406 * We found no IAC byte, so what remains in the buffer
407 * is the last of the data in the packet.
408 * Add it to the tree, a line at a time, and then quit.
410 telnet_add_text(telnet_tree, tvb, offset, len);
418 proto_register_telnet(void)
420 /* static hf_register_info hf[] = {
422 { "Name", "telnet.abbreviation", TYPE, VALS_POINTER }},
424 static gint *ett[] = {
429 proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
430 /* proto_register_field_array(proto_telnet, hf, array_length(hf));*/
431 proto_register_subtree_array(ett, array_length(ett));
435 proto_reg_handoff_telnet(void)
437 dissector_handle_t telnet_handle;
439 telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
440 dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);