2 * Routines for telnet packet dissection
3 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
5 * $Id: packet-telnet.c,v 1.34 2002/10/25 21:13:38 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.
36 #include <epan/packet.h>
37 #include <epan/strutil.h>
39 static int proto_telnet = -1;
41 static gint ett_telnet = -1;
42 static gint ett_telnet_subopt = -1;
44 /* Some defines for Telnet */
46 #define TCP_PORT_TELNET 23
69 static const char *options[] = {
70 "Binary Transmission",
74 "Approx Message Size Negotiation",
77 "Remote Controlled Trans and Echo",
80 "Output Carriage-Return Disposition",
81 "Output Horizontal Tab Stops",
82 "Output Horizontal Tab Disposition",
83 "Output Formfeed Disposition",
84 "Output Vertical Tabstops",
85 "Output Vertical Tab Disposition",
86 "Output Linefeed Disposition",
90 "Data Entry Terminal",
96 "TACACS User Identification",
98 "Terminal Location Number",
101 "Negotiate About Window Size",
103 "Remote Flow Control",
105 "X Display Location",
106 "Environment Option",
107 "Authentication Option",
109 "New Environment Option",
113 #define NOPTIONS (sizeof options / sizeof options[0])
116 telnet_sub_option(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
118 proto_tree *ti, *option_tree;
119 int offset = start_offset;
126 offset += 2; /* skip IAC and SB */
128 /* Figure out the option and type */
129 opt_byte = tvb_get_guint8(tvb, offset);
130 if (opt_byte > NOPTIONS)
131 opt = "<unknown option>";
133 opt = options[opt_byte];
135 req = tvb_get_guint8(tvb, offset);
138 /* Search for an IAC. */
139 len = tvb_length_remaining(tvb, offset);
140 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
141 if (iac_offset == -1) {
142 /* None found - run to the end of the packet. */
147 subneg_len = offset - start_offset;
149 if (subneg_len > 0) {
150 ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
151 "Suboption Begin: %s", opt);
153 option_tree = proto_item_add_subtree(ti, ett_telnet_subopt);
155 proto_tree_add_text(option_tree, tvb, start_offset + 2, 2,
156 "%s %s", (req ? "Send your" : "Here's my"), opt);
158 if (req == 0) { /* Add the value */
159 proto_tree_add_text(option_tree, tvb, start_offset + 4, subneg_len - 4,
160 "Value: %s", tvb_format_text(tvb, start_offset + 4, subneg_len - 4));
167 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
168 int start_offset, char *type)
170 int offset = start_offset;
174 offset += 2; /* skip IAC and WILL,WONT,DO,DONT} */
175 opt_byte = tvb_get_guint8(tvb, offset);
176 if (opt_byte > NOPTIONS)
177 opt = "<unknown option>";
179 opt = options[opt_byte];
182 proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
183 "Command: %s %s", type, opt);
188 telnet_command(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
190 int offset = start_offset;
193 offset += 1; /* skip IAC */
194 optcode = tvb_get_guint8(tvb, offset);
199 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
200 "Command: End of File");
204 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
205 "Command: Suspend Current Process");
209 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
210 "Command: Abort Process");
214 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
215 "Command: End of Record");
219 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
220 "Command: Suboption End");
224 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
225 "Command: No Operation");
229 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
230 "Command: Data Mark");
234 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
239 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
240 "Command: Interrupt Process");
244 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
245 "Command: Abort Output");
249 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
250 "Command: Are You There?");
254 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
255 "Command: Escape Character");
259 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
260 "Command: Erase Line");
264 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
265 "Command: Go Ahead");
269 offset = telnet_sub_option(telnet_tree, tvb, start_offset);
273 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
278 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
283 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
288 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
293 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
294 "Command: Unknown (0x%02x)", optcode);
302 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
307 gboolean last_char_was_cr;
309 while (len != 0 && tvb_offset_exists(tvb, offset)) {
311 * Find the end of the line.
313 linelen = tvb_find_line_end(tvb, offset, len, &next_offset, FALSE);
314 len -= next_offset - offset; /* subtract out the line's characters */
317 * In Telnet, CR NUL is the way you send a CR by itself in the
318 * default ASCII mode; don't treat CR by itself as a line ending,
319 * treat only CR NUL, CR LF, or LF by itself as a line ending.
321 if (next_offset == offset + linelen + 1 && len >= 1) {
323 * Well, we saw a one-character line ending, so either it's a CR
324 * or an LF; we have at least two characters left, including the
327 * If the line ending is a CR, skip all subsequent CRs; at
328 * least one capture appeared to have multiple CRs at the end of
331 if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
332 last_char_was_cr = TRUE;
333 while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
334 c = tvb_get_guint8(tvb, next_offset);
335 next_offset++; /* skip over that character */
337 if (c == '\n' || (c == '\0' && last_char_was_cr)) {
339 * LF is a line ending, whether preceded by CR or not.
340 * NUL is a line ending if preceded by CR.
344 last_char_was_cr = (c == '\r');
350 * Now compute the length of the line *including* the end-of-line
351 * indication, if any; we display it all.
353 linelen = next_offset - offset;
355 proto_tree_add_text(tree, tvb, offset, linelen,
357 tvb_format_text(tvb, offset, linelen));
358 offset = next_offset;
363 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
365 proto_tree *telnet_tree, *ti;
367 if (check_col(pinfo->cinfo, COL_PROTOCOL))
368 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
370 if (check_col(pinfo->cinfo, COL_INFO))
371 col_add_fstr(pinfo->cinfo, COL_INFO, "Telnet Data ...");
379 ti = proto_tree_add_item(tree, proto_telnet, tvb, offset, -1, FALSE);
380 telnet_tree = proto_item_add_subtree(ti, ett_telnet);
383 * Scan through the buffer looking for an IAC byte.
385 while ((len = tvb_length_remaining(tvb, offset)) > 0) {
386 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
387 if (iac_offset != -1) {
389 * We found an IAC byte.
390 * If there's any data before it, add that data to the
391 * tree, a line at a time.
393 data_len = iac_offset - offset;
395 telnet_add_text(telnet_tree, tvb, offset, data_len);
398 * Now interpret the command.
400 offset = telnet_command(telnet_tree, tvb, iac_offset);
404 * We found no IAC byte, so what remains in the buffer
405 * is the last of the data in the packet.
406 * Add it to the tree, a line at a time, and then quit.
408 telnet_add_text(telnet_tree, tvb, offset, len);
416 proto_register_telnet(void)
418 /* static hf_register_info hf[] = {
420 { "Name", "telnet.abbreviation", TYPE, VALS_POINTER }},
422 static gint *ett[] = {
427 proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
428 /* proto_register_field_array(proto_telnet, hf, array_length(hf));*/
429 proto_register_subtree_array(ett, array_length(ett));
433 proto_reg_handoff_telnet(void)
435 dissector_handle_t telnet_handle;
437 telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
438 dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);