2 * Routines for Telnet packet dissection; see RFC 854 and RFC 855
3 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
5 * $Id: packet-telnet.c,v 1.36 2003/02/24 19:25:00 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;
43 static gint ett_status_subopt = -1;
44 static gint ett_rcte_subopt = -1;
45 static gint ett_olw_subopt = -1;
46 static gint ett_ops_subopt = -1;
47 static gint ett_crdisp_subopt = -1;
48 static gint ett_htstops_subopt = -1;
49 static gint ett_htdisp_subopt = -1;
50 static gint ett_ffdisp_subopt = -1;
51 static gint ett_vtstops_subopt = -1;
52 static gint ett_vtdisp_subopt = -1;
53 static gint ett_lfdisp_subopt = -1;
54 static gint ett_extasc_subopt = -1;
55 static gint ett_bytemacro_subopt = -1;
56 static gint ett_det_subopt = -1;
57 static gint ett_supdupout_subopt = -1;
58 static gint ett_sendloc_subopt = -1;
59 static gint ett_termtype_subopt = -1;
60 static gint ett_tacacsui_subopt = -1;
61 static gint ett_outmark_subopt = -1;
62 static gint ett_tlocnum_subopt = -1;
63 static gint ett_tn3270reg_subopt = -1;
64 static gint ett_x3pad_subopt = -1;
65 static gint ett_naws_subopt = -1;
66 static gint ett_tspeed_subopt = -1;
67 static gint ett_rfc_subopt = -1;
68 static gint ett_linemode_subopt = -1;
69 static gint ett_xdpyloc_subopt = -1;
70 static gint ett_env_subopt = -1;
71 static gint ett_auth_subopt = -1;
72 static gint ett_enc_subopt = -1;
73 static gint ett_newenv_subopt = -1;
74 static gint ett_tn3270e_subopt = -1;
76 /* Some defines for Telnet */
78 #define TCP_PORT_TELNET 23
102 NO_LENGTH, /* option has no data, hence no length */
103 FIXED_LENGTH, /* option always has the same length */
104 VARIABLE_LENGTH /* option is variable-length - optlen is minimum */
107 /* Member of table of IP or TCP options. */
108 typedef struct tn_opt {
109 char *name; /* name of option */
110 gint *subtree_index; /* pointer to subtree index for option */
111 tn_opt_len_type len_type; /* type of option length field */
112 int optlen; /* value length should be (minimum if VARIABLE) */
113 void (*dissect)(const char *, tvbuff_t *, int, int, proto_tree *);
114 /* routine to dissect option */
118 dissect_string_subopt(const char *optname, tvbuff_t *tvb, int offset, int len,
123 cmd = tvb_get_guint8(tvb, offset);
127 proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
131 proto_tree_add_text(tree, tvb, offset, len, "Value: %s",
132 tvb_format_text(tvb, offset, len));
137 proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
141 proto_tree_add_text(tree, tvb, offset, len, "Extra data");
145 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
150 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
156 dissect_outmark_subopt(const char *optname _U_, tvbuff_t *tvb, int offset,
157 int len, proto_tree *tree)
160 int gs_offset, datalen;
163 cmd = tvb_get_guint8(tvb, offset);
167 proto_tree_add_text(tree, tvb, offset, 1, "ACK");
171 proto_tree_add_text(tree, tvb, offset, 1, "NAK");
175 proto_tree_add_text(tree, tvb, offset, 1, "Default");
179 proto_tree_add_text(tree, tvb, offset, 1, "Top");
183 proto_tree_add_text(tree, tvb, offset, 1, "Bottom");
187 proto_tree_add_text(tree, tvb, offset, 1, "Left");
191 proto_tree_add_text(tree, tvb, offset, 1, "Right");
195 proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", cmd);
202 gs_offset = tvb_find_guint8(tvb, offset, len, 29);
203 if (gs_offset == -1) {
204 /* None found - run to the end of the packet. */
205 gs_offset = offset + len;
207 datalen = gs_offset - offset;
209 proto_tree_add_text(tree, tvb, offset, datalen, "Banner: %s",
210 tvb_format_text(tvb, offset, datalen));
218 dissect_htstops_subopt(const char *optname, tvbuff_t *tvb, int offset, int len,
224 cmd = tvb_get_guint8(tvb, offset);
228 proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
234 proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
240 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
245 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
250 tabval = tvb_get_guint8(tvb, offset);
254 proto_tree_add_text(tree, tvb, offset, 1,
255 "Sender wants to handle tab stops");
259 proto_tree_add_text(tree, tvb, offset, 1,
260 "Sender wants receiver to handle tab stop at %u",
268 proto_tree_add_text(tree, tvb, offset, 1,
269 "Invalid value: %u", tabval);
273 proto_tree_add_text(tree, tvb, offset, 1,
274 "Sender wants receiver to handle tab stops");
283 dissect_naws_subopt(const char *optname _U_, tvbuff_t *tvb, int offset,
284 int len _U_, proto_tree *tree)
286 proto_tree_add_text(tree, tvb, offset, 2, "Width: %u",
287 tvb_get_ntohs(tvb, offset));
289 proto_tree_add_text(tree, tvb, offset, 2, "Height: %u",
290 tvb_get_ntohs(tvb, offset));
293 static const value_string rfc_opt_vals[] = {
296 { 2, "RESTART-ANY" },
297 { 3, "RESTART-XON" },
302 dissect_rfc_subopt(const char *optname _U_, tvbuff_t *tvb, int offset,
303 int len _U_, proto_tree *tree)
307 cmd = tvb_get_guint8(tvb, offset);
308 proto_tree_add_text(tree, tvb, offset, 2, "%s",
309 val_to_str(cmd, rfc_opt_vals, "Unknown (%u)"));
312 static tn_opt options[] = {
314 "Binary Transmission", /* RFC 856 */
315 NULL, /* no suboption negotiation */
321 "Echo", /* RFC 857 */
322 NULL, /* no suboption negotiation */
328 "Reconnection", /* DOD Protocol Handbook */
335 "Suppress Go Ahead", /* RFC 858 */
336 NULL, /* no suboption negotiation */
342 "Approx Message Size Negotiation", /* Ethernet spec(!) */
349 "Status", /* RFC 859 */
353 NULL /* XXX - fill me in */
356 "Timing Mark", /* RFC 860 */
357 NULL, /* no suboption negotiation */
363 "Remote Controlled Trans and Echo", /* RFC 726 */
367 NULL /* XXX - fill me in */
370 "Output Line Width", /* DOD Protocol Handbook */
372 VARIABLE_LENGTH, /* XXX - fill me in */
373 0, /* XXX - fill me in */
374 NULL /* XXX - fill me in */
377 "Output Page Size", /* DOD Protocol Handbook */
379 VARIABLE_LENGTH, /* XXX - fill me in */
380 0, /* XXX - fill me in */
381 NULL /* XXX - fill me in */
384 "Output Carriage-Return Disposition", /* RFC 652 */
388 NULL /* XXX - fill me in */
391 "Output Horizontal Tab Stops", /* RFC 653 */
395 dissect_htstops_subopt
398 "Output Horizontal Tab Disposition", /* RFC 654 */
402 NULL /* XXX - fill me in */
405 "Output Formfeed Disposition", /* RFC 655 */
409 NULL /* XXX - fill me in */
412 "Output Vertical Tabstops", /* RFC 656 */
416 NULL /* XXX - fill me in */
419 "Output Vertical Tab Disposition", /* RFC 657 */
423 NULL /* XXX - fill me in */
426 "Output Linefeed Disposition", /* RFC 658 */
430 NULL /* XXX - fill me in */
433 "Extended ASCII", /* RFC 698 */
437 NULL /* XXX - fill me in */
440 "Logout", /* RFC 727 */
441 NULL, /* no suboption negotiation */
447 "Byte Macro", /* RFC 735 */
448 &ett_bytemacro_subopt,
451 NULL /* XXX - fill me in */
454 "Data Entry Terminal", /* RFC 732, RFC 1043 */
458 NULL /* XXX - fill me in */
461 "SUPDUP", /* RFC 734, RFC 736 */
462 NULL, /* no suboption negotiation */
468 "SUPDUP Output", /* RFC 749 */
469 &ett_supdupout_subopt,
472 NULL /* XXX - fill me in */
475 "Send Location", /* RFC 779 */
479 NULL /* XXX - fill me in */
482 "Terminal Type", /* RFC 1091 */
483 &ett_termtype_subopt,
486 dissect_string_subopt
489 "End of Record", /* RFC 885 */
490 NULL, /* no suboption negotiation */
496 "TACACS User Identification", /* RFC 927 */
497 &ett_tacacsui_subopt,
500 NULL /* XXX - fill me in */
503 "Output Marking", /* RFC 933 */
507 dissect_outmark_subopt,
510 "Terminal Location Number", /* RFC 946 */
514 NULL /* XXX - fill me in */
517 "Telnet 3270 Regime", /* RFC 1041 */
518 &ett_tn3270reg_subopt,
521 NULL /* XXX - fill me in */
524 "X.3 PAD", /* RFC 1053 */
528 NULL /* XXX - fill me in */
531 "Negotiate About Window Size", /* RFC 1073, DW183 */
538 "Terminal Speed", /* RFC 1079 */
542 NULL /* XXX - fill me in */
545 "Remote Flow Control", /* RFC 1372 */
552 "Linemode", /* RFC 1184 */
553 &ett_linemode_subopt,
556 NULL /* XXX - fill me in */
559 "X Display Location", /* RFC 1096 */
563 dissect_string_subopt
566 "Environment Option", /* RFC 1408, RFC 1571 */
570 NULL /* XXX - fill me in */
573 "Authentication Option", /* RFC 2941 */
577 NULL /* XXX - fill me in */
580 "Encryption Option", /* RFC 2946 */
584 NULL /* XXX - fill me in */
587 "New Environment Option", /* RFC 1572 */
591 NULL /* XXX - fill me in */
594 "TN3270E", /* RFC 1647 */
598 NULL /* XXX - fill me in */
602 #define NOPTIONS (sizeof options / sizeof options[0])
605 telnet_sub_option(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
607 proto_tree *ti, *option_tree;
608 int offset = start_offset;
615 void (*dissect)(const char *, tvbuff_t *, int, int, proto_tree *);
617 offset += 2; /* skip IAC and SB */
619 /* Get the option code */
620 opt_byte = tvb_get_guint8(tvb, offset);
621 if (opt_byte > NOPTIONS) {
622 opt = "<unknown option>";
623 ett = ett_telnet_subopt;
626 opt = options[opt_byte].name;
627 if (options[opt_byte].subtree_index != NULL)
628 ett = *(options[opt_byte].subtree_index);
630 ett = ett_telnet_subopt;
631 dissect = options[opt_byte].dissect;
635 /* Search for an IAC. */
636 len = tvb_length_remaining(tvb, offset);
637 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
638 if (iac_offset == -1) {
639 /* None found - run to the end of the packet. */
644 subneg_len = offset - start_offset;
646 ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
647 "Suboption Begin: %s", opt);
648 option_tree = proto_item_add_subtree(ti, ett);
649 start_offset += 3; /* skip IAC, SB, and option code */
652 if (subneg_len > 0) {
653 switch (options[opt_byte].len_type) {
656 /* There isn't supposed to *be* sub-option negotiation for this. */
657 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
658 "Bogus suboption data");
662 /* Make sure the length is what it's supposed to be. */
663 if (subneg_len != options[opt_byte].optlen) {
664 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
665 "Suboption parameter length is %d, should be %d",
666 subneg_len, options[opt_byte].optlen);
671 case VARIABLE_LENGTH:
672 /* Make sure the length is greater than the minimum. */
673 if (subneg_len < options[opt_byte].optlen) {
674 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
675 "Suboption parameter length is %d, should be at least %d",
676 subneg_len, options[opt_byte].optlen);
682 /* Now dissect the suboption parameters. */
683 if (dissect != NULL) {
684 /* We have a dissector for this suboption's parameters; call it. */
685 (*dissect)(opt, tvb, start_offset, subneg_len, option_tree);
687 /* We don't have a dissector for them; just show them as data. */
688 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
696 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
697 int start_offset, char *type)
699 int offset = start_offset;
703 offset += 2; /* skip IAC and WILL,WONT,DO,DONT} */
704 opt_byte = tvb_get_guint8(tvb, offset);
705 if (opt_byte > NOPTIONS)
706 opt = "<unknown option>";
708 opt = options[opt_byte].name;
711 proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
712 "Command: %s %s", type, opt);
717 telnet_command(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
719 int offset = start_offset;
722 offset += 1; /* skip IAC */
723 optcode = tvb_get_guint8(tvb, offset);
728 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
729 "Command: End of File");
733 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
734 "Command: Suspend Current Process");
738 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
739 "Command: Abort Process");
743 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
744 "Command: End of Record");
748 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
749 "Command: Suboption End");
753 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
754 "Command: No Operation");
758 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
759 "Command: Data Mark");
763 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
768 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
769 "Command: Interrupt Process");
773 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
774 "Command: Abort Output");
778 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
779 "Command: Are You There?");
783 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
784 "Command: Escape Character");
788 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
789 "Command: Erase Line");
793 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
794 "Command: Go Ahead");
798 offset = telnet_sub_option(telnet_tree, tvb, start_offset);
802 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
807 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
812 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
817 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
822 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
823 "Command: Unknown (0x%02x)", optcode);
831 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
836 gboolean last_char_was_cr;
838 while (len != 0 && tvb_offset_exists(tvb, offset)) {
840 * Find the end of the line.
842 linelen = tvb_find_line_end(tvb, offset, len, &next_offset, FALSE);
843 len -= next_offset - offset; /* subtract out the line's characters */
846 * In Telnet, CR NUL is the way you send a CR by itself in the
847 * default ASCII mode; don't treat CR by itself as a line ending,
848 * treat only CR NUL, CR LF, or LF by itself as a line ending.
850 if (next_offset == offset + linelen + 1 && len >= 1) {
852 * Well, we saw a one-character line ending, so either it's a CR
853 * or an LF; we have at least two characters left, including the
856 * If the line ending is a CR, skip all subsequent CRs; at
857 * least one capture appeared to have multiple CRs at the end of
860 if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
861 last_char_was_cr = TRUE;
862 while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
863 c = tvb_get_guint8(tvb, next_offset);
864 next_offset++; /* skip over that character */
866 if (c == '\n' || (c == '\0' && last_char_was_cr)) {
868 * LF is a line ending, whether preceded by CR or not.
869 * NUL is a line ending if preceded by CR.
873 last_char_was_cr = (c == '\r');
879 * Now compute the length of the line *including* the end-of-line
880 * indication, if any; we display it all.
882 linelen = next_offset - offset;
884 proto_tree_add_text(tree, tvb, offset, linelen,
886 tvb_format_text(tvb, offset, linelen));
887 offset = next_offset;
892 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
894 proto_tree *telnet_tree, *ti;
896 if (check_col(pinfo->cinfo, COL_PROTOCOL))
897 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
899 if (check_col(pinfo->cinfo, COL_INFO))
900 col_add_fstr(pinfo->cinfo, COL_INFO, "Telnet Data ...");
908 ti = proto_tree_add_item(tree, proto_telnet, tvb, offset, -1, FALSE);
909 telnet_tree = proto_item_add_subtree(ti, ett_telnet);
912 * Scan through the buffer looking for an IAC byte.
914 while ((len = tvb_length_remaining(tvb, offset)) > 0) {
915 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
916 if (iac_offset != -1) {
918 * We found an IAC byte.
919 * If there's any data before it, add that data to the
920 * tree, a line at a time.
922 data_len = iac_offset - offset;
924 telnet_add_text(telnet_tree, tvb, offset, data_len);
927 * Now interpret the command.
929 offset = telnet_command(telnet_tree, tvb, iac_offset);
933 * We found no IAC byte, so what remains in the buffer
934 * is the last of the data in the packet.
935 * Add it to the tree, a line at a time, and then quit.
937 telnet_add_text(telnet_tree, tvb, offset, len);
945 proto_register_telnet(void)
947 /* static hf_register_info hf[] = {
949 { "Name", "telnet.abbreviation", TYPE, VALS_POINTER }},
951 static gint *ett[] = {
966 &ett_bytemacro_subopt,
968 &ett_supdupout_subopt,
970 &ett_termtype_subopt,
971 &ett_tacacsui_subopt,
974 &ett_tn3270reg_subopt,
979 &ett_linemode_subopt,
988 proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
989 /* proto_register_field_array(proto_telnet, hf, array_length(hf));*/
990 proto_register_subtree_array(ett, array_length(ett));
994 proto_reg_handoff_telnet(void)
996 dissector_handle_t telnet_handle;
998 telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
999 dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);