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.40 2003/04/30 02:35:20 gerald 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;
75 static gint ett_xauth_subopt = -1;
76 static gint ett_charset_subopt = -1;
77 static gint ett_rsp_subopt = -1;
78 static gint ett_comport_subopt = -1;
81 /* Some defines for Telnet */
83 #define TCP_PORT_TELNET 23
108 NO_LENGTH, /* option has no data, hence no length */
109 FIXED_LENGTH, /* option always has the same length */
110 VARIABLE_LENGTH /* option is variable-length - optlen is minimum */
113 /* Member of table of IP or TCP options. */
114 typedef struct tn_opt {
115 char *name; /* name of option */
116 gint *subtree_index; /* pointer to subtree index for option */
117 tn_opt_len_type len_type; /* type of option length field */
118 int optlen; /* value length should be (minimum if VARIABLE) */
119 void (*dissect)(const char *, tvbuff_t *, int, int, proto_tree *);
120 /* routine to dissect option */
124 dissect_string_subopt(const char *optname, tvbuff_t *tvb, int offset, int len,
129 cmd = tvb_get_guint8(tvb, offset);
133 proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
137 proto_tree_add_text(tree, tvb, offset, len, "Value: %s",
138 tvb_format_text(tvb, offset, len));
143 proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
147 proto_tree_add_text(tree, tvb, offset, len, "Extra data");
151 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
156 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
162 dissect_outmark_subopt(const char *optname _U_, tvbuff_t *tvb, int offset,
163 int len, proto_tree *tree)
166 int gs_offset, datalen;
169 cmd = tvb_get_guint8(tvb, offset);
173 proto_tree_add_text(tree, tvb, offset, 1, "ACK");
177 proto_tree_add_text(tree, tvb, offset, 1, "NAK");
181 proto_tree_add_text(tree, tvb, offset, 1, "Default");
185 proto_tree_add_text(tree, tvb, offset, 1, "Top");
189 proto_tree_add_text(tree, tvb, offset, 1, "Bottom");
193 proto_tree_add_text(tree, tvb, offset, 1, "Left");
197 proto_tree_add_text(tree, tvb, offset, 1, "Right");
201 proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", cmd);
208 gs_offset = tvb_find_guint8(tvb, offset, len, 29);
209 if (gs_offset == -1) {
210 /* None found - run to the end of the packet. */
211 gs_offset = offset + len;
213 datalen = gs_offset - offset;
215 proto_tree_add_text(tree, tvb, offset, datalen, "Banner: %s",
216 tvb_format_text(tvb, offset, datalen));
224 dissect_htstops_subopt(const char *optname, tvbuff_t *tvb, int offset, int len,
230 cmd = tvb_get_guint8(tvb, offset);
234 proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
240 proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
246 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
251 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
256 tabval = tvb_get_guint8(tvb, offset);
260 proto_tree_add_text(tree, tvb, offset, 1,
261 "Sender wants to handle tab stops");
265 proto_tree_add_text(tree, tvb, offset, 1,
266 "Sender wants receiver to handle tab stop at %u",
274 proto_tree_add_text(tree, tvb, offset, 1,
275 "Invalid value: %u", tabval);
279 proto_tree_add_text(tree, tvb, offset, 1,
280 "Sender wants receiver to handle tab stops");
289 dissect_naws_subopt(const char *optname _U_, tvbuff_t *tvb, int offset,
290 int len _U_, proto_tree *tree)
292 proto_tree_add_text(tree, tvb, offset, 2, "Width: %u",
293 tvb_get_ntohs(tvb, offset));
295 proto_tree_add_text(tree, tvb, offset, 2, "Height: %u",
296 tvb_get_ntohs(tvb, offset));
299 /* BEGIN RFC-2217 (COM Port Control) Definitions */
301 #define TNCOMPORT_SIGNATURE 0
302 #define TNCOMPORT_SETBAUDRATE 1
303 #define TNCOMPORT_SETDATASIZE 2
304 #define TNCOMPORT_SETPARITY 3
305 #define TNCOMPORT_SETSTOPSIZE 4
306 #define TNCOMPORT_SETCONTROL 5
307 #define TNCOMPORT_NOTIFYLINESTATE 6
308 #define TNCOMPORT_NOTIFYMODEMSTATE 7
309 #define TNCOMPORT_FLOWCONTROLSUSPEND 8
310 #define TNCOMPORT_FLOWCONTROLRESUME 9
311 #define TNCOMPORT_SETLINESTATEMASK 10
312 #define TNCOMPORT_SETMODEMSTATEMASK 11
313 #define TNCOMPORT_PURGEDATA 12
315 /* END RFC-2217 (COM Port Control) Definitions */
318 dissect_comport_subopt(const char *optname, tvbuff_t *tvb, int offset, int len,
320 {static const char *datasizes[] = {
331 static const char *parities[] = {
339 static const char *stops[] = {
345 static const char *control[] = {
346 "Output Flow Control Request",
348 "Output Flow: XON/XOFF",
349 "Output Flow: CTS/RTS",
359 "Input Flow Control Request",
361 "Input Flow: XON/XOFF",
362 "Input Flow: CTS/RTS",
367 static const char *linestate_bits[] = {
373 "Transfer Holding Register Empty",
374 "Transfer Shift Register Empty",
377 static const char *modemstate_bits[] = {
387 static const char *purges[] = {
398 cmd = tvb_get_guint8(tvb, offset);
399 isservercmd = cmd > 99;
400 cmd = (isservercmd) ? (cmd - 100) : cmd;
401 source = (isservercmd) ? "Server" : "Client";
404 case TNCOMPORT_SIGNATURE:
407 proto_tree_add_text(tree, tvb, offset, 1, "%s Requests Signature",source);
409 guint8 *sig = g_malloc(len);
410 gint siglen = tvb_get_nstringz0(tvb, offset+1, len, sig);
411 proto_tree_add_text(tree, tvb, offset, 1 + siglen, "%s Signature: %s",source, sig);
416 case TNCOMPORT_SETBAUDRATE:
419 guint32 baud = tvb_get_ntohl(tvb, offset+1);
421 proto_tree_add_text(tree, tvb, offset, 5, "%s Requests Baud Rate",source);
423 proto_tree_add_text(tree, tvb, offset, 5, "%s Baud Rate: %d",source,baud);
426 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Baud Rate Packet>",source);
430 case TNCOMPORT_SETDATASIZE:
433 guint8 datasize = tvb_get_guint8(tvb, offset+1);
434 const char *ds = (datasize > 8) ? "<invalid>" : datasizes[datasize];
435 proto_tree_add_text(tree, tvb, offset, 2, "%s Data Size: %s",source,ds);
437 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Data Size Packet>",source);
441 case TNCOMPORT_SETPARITY:
444 guint8 parity = tvb_get_guint8(tvb, offset+1);
445 const char *pr = (parity > 5) ? "<invalid>" : parities[parity];
446 proto_tree_add_text(tree, tvb, offset, 2, "%s Parity: %s",source,pr);
448 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Parity Packet>",source);
452 case TNCOMPORT_SETSTOPSIZE:
455 guint8 stop = tvb_get_guint8(tvb, offset+1);
456 const char *st = (stop > 3) ? "<invalid>" : stops[stop];
457 proto_tree_add_text(tree, tvb, offset, 2, "%s Stop: %s",source,st);
459 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Stop Packet>",source);
463 case TNCOMPORT_SETCONTROL:
466 guint8 crt = tvb_get_guint8(tvb, offset+1);
467 const char *c = (crt > 19) ? "Control: <invalid>" : control[crt];
468 proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,c);
470 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Control Packet>",source);
474 case TNCOMPORT_SETLINESTATEMASK:
475 case TNCOMPORT_NOTIFYLINESTATE:
478 const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
479 "%s Set Linestate Mask: %s" : "%s Linestate: %s";
481 guint8 ls = tvb_get_guint8(tvb, offset+1);
485 for (idx = 0; idx < 8; idx++) {
488 if (print_count != 0) {
489 strcat(ls_buffer,", ");
491 strcat(ls_buffer,linestate_bits[idx]);
496 proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ls_buffer);
498 const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
499 "%s <Invalid Linestate Mask>" : "%s <Invalid Linestate Packet>";
500 proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
504 case TNCOMPORT_SETMODEMSTATEMASK:
505 case TNCOMPORT_NOTIFYMODEMSTATE:
508 const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
509 "%s Set Modemstate Mask: %s" : "%s Modemstate: %s";
511 guint8 ms = tvb_get_guint8(tvb, offset+1);
515 for (idx = 0; idx < 8; idx++) {
518 if (print_count != 0) {
519 strcat(ms_buffer,", ");
521 strcat(ms_buffer,modemstate_bits[idx]);
526 proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ms_buffer);
528 const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
529 "%s <Invalid Modemstate Mask>" : "%s <Invalid Modemstate Packet>";
530 proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
534 case TNCOMPORT_FLOWCONTROLSUSPEND:
536 proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Suspend",source);
539 case TNCOMPORT_FLOWCONTROLRESUME:
541 proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Resume",source);
544 case TNCOMPORT_PURGEDATA:
547 guint8 purge = tvb_get_guint8(tvb, offset+1);
548 const char *p = (purge > 3) ? "<Purge invalid>" : purges[purge];
549 proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,p);
551 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Purge Packet>",source);
556 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
561 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
567 static const value_string rfc_opt_vals[] = {
570 { 2, "RESTART-ANY" },
571 { 3, "RESTART-XON" },
576 dissect_rfc_subopt(const char *optname _U_, tvbuff_t *tvb, int offset,
577 int len _U_, proto_tree *tree)
581 cmd = tvb_get_guint8(tvb, offset);
582 proto_tree_add_text(tree, tvb, offset, 2, "%s",
583 val_to_str(cmd, rfc_opt_vals, "Unknown (%u)"));
586 static tn_opt options[] = {
588 "Binary Transmission", /* RFC 856 */
589 NULL, /* no suboption negotiation */
595 "Echo", /* RFC 857 */
596 NULL, /* no suboption negotiation */
602 "Reconnection", /* DOD Protocol Handbook */
609 "Suppress Go Ahead", /* RFC 858 */
610 NULL, /* no suboption negotiation */
616 "Approx Message Size Negotiation", /* Ethernet spec(!) */
623 "Status", /* RFC 859 */
627 NULL /* XXX - fill me in */
630 "Timing Mark", /* RFC 860 */
631 NULL, /* no suboption negotiation */
637 "Remote Controlled Trans and Echo", /* RFC 726 */
641 NULL /* XXX - fill me in */
644 "Output Line Width", /* DOD Protocol Handbook */
646 VARIABLE_LENGTH, /* XXX - fill me in */
647 0, /* XXX - fill me in */
648 NULL /* XXX - fill me in */
651 "Output Page Size", /* DOD Protocol Handbook */
653 VARIABLE_LENGTH, /* XXX - fill me in */
654 0, /* XXX - fill me in */
655 NULL /* XXX - fill me in */
658 "Output Carriage-Return Disposition", /* RFC 652 */
662 NULL /* XXX - fill me in */
665 "Output Horizontal Tab Stops", /* RFC 653 */
669 dissect_htstops_subopt
672 "Output Horizontal Tab Disposition", /* RFC 654 */
676 NULL /* XXX - fill me in */
679 "Output Formfeed Disposition", /* RFC 655 */
683 NULL /* XXX - fill me in */
686 "Output Vertical Tabstops", /* RFC 656 */
690 NULL /* XXX - fill me in */
693 "Output Vertical Tab Disposition", /* RFC 657 */
697 NULL /* XXX - fill me in */
700 "Output Linefeed Disposition", /* RFC 658 */
704 NULL /* XXX - fill me in */
707 "Extended ASCII", /* RFC 698 */
711 NULL /* XXX - fill me in */
714 "Logout", /* RFC 727 */
715 NULL, /* no suboption negotiation */
721 "Byte Macro", /* RFC 735 */
722 &ett_bytemacro_subopt,
725 NULL /* XXX - fill me in */
728 "Data Entry Terminal", /* RFC 732, RFC 1043 */
732 NULL /* XXX - fill me in */
735 "SUPDUP", /* RFC 734, RFC 736 */
736 NULL, /* no suboption negotiation */
742 "SUPDUP Output", /* RFC 749 */
743 &ett_supdupout_subopt,
746 NULL /* XXX - fill me in */
749 "Send Location", /* RFC 779 */
753 NULL /* XXX - fill me in */
756 "Terminal Type", /* RFC 1091 */
757 &ett_termtype_subopt,
760 dissect_string_subopt
763 "End of Record", /* RFC 885 */
764 NULL, /* no suboption negotiation */
770 "TACACS User Identification", /* RFC 927 */
771 &ett_tacacsui_subopt,
774 NULL /* XXX - fill me in */
777 "Output Marking", /* RFC 933 */
781 dissect_outmark_subopt,
784 "Terminal Location Number", /* RFC 946 */
788 NULL /* XXX - fill me in */
791 "Telnet 3270 Regime", /* RFC 1041 */
792 &ett_tn3270reg_subopt,
795 NULL /* XXX - fill me in */
798 "X.3 PAD", /* RFC 1053 */
802 NULL /* XXX - fill me in */
805 "Negotiate About Window Size", /* RFC 1073, DW183 */
812 "Terminal Speed", /* RFC 1079 */
816 NULL /* XXX - fill me in */
819 "Remote Flow Control", /* RFC 1372 */
826 "Linemode", /* RFC 1184 */
827 &ett_linemode_subopt,
830 NULL /* XXX - fill me in */
833 "X Display Location", /* RFC 1096 */
837 dissect_string_subopt
840 "Environment Option", /* RFC 1408, RFC 1571 */
844 NULL /* XXX - fill me in */
847 "Authentication Option", /* RFC 2941 */
851 NULL /* XXX - fill me in */
854 "Encryption Option", /* RFC 2946 */
858 NULL /* XXX - fill me in */
861 "New Environment Option", /* RFC 1572 */
865 NULL /* XXX - fill me in */
868 "TN3270E", /* RFC 1647 */
872 NULL /* XXX - fill me in */
879 NULL /* XXX - fill me in */
882 "CHARSET", /* CHARSET */
886 NULL /* XXX - fill me in */
889 "Remote Serial Port", /* Remote Serial Port */
893 NULL /* XXX - fill me in */
896 "COM Port Control", /* RFC 2217 */
900 dissect_comport_subopt
905 #define NOPTIONS (sizeof options / sizeof options[0])
908 telnet_sub_option(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
910 proto_tree *ti, *option_tree;
911 int offset = start_offset;
918 void (*dissect)(const char *, tvbuff_t *, int, int, proto_tree *);
922 offset += 2; /* skip IAC and SB */
924 /* Get the option code */
925 opt_byte = tvb_get_guint8(tvb, offset);
926 if (opt_byte > NOPTIONS) {
927 opt = "<unknown option>";
928 ett = ett_telnet_subopt;
931 opt = options[opt_byte].name;
932 if (options[opt_byte].subtree_index != NULL)
933 ett = *(options[opt_byte].subtree_index);
935 ett = ett_telnet_subopt;
936 dissect = options[opt_byte].dissect;
940 /* Search for an unescaped IAC. */
943 len = tvb_length_remaining(tvb, offset);
945 iac_offset = tvb_find_guint8(tvb, cur_offset, len, TN_IAC);
947 if (iac_offset == -1) {
948 /* None found - run to the end of the packet. */
951 if (((guint)(iac_offset + 1) >= len) ||
952 (tvb_get_guint8(tvb, iac_offset + 1) != TN_IAC)) {
953 /* We really found a single IAC, so we're done */
957 * We saw an escaped IAC, so we have to move ahead to the
961 cur_offset = iac_offset + 2;
965 } while (!iac_found);
967 subneg_len = offset - start_offset;
969 ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
970 "Suboption Begin: %s", opt);
971 option_tree = proto_item_add_subtree(ti, ett);
972 start_offset += 3; /* skip IAC, SB, and option code */
975 if (subneg_len > 0) {
976 switch (options[opt_byte].len_type) {
979 /* There isn't supposed to *be* sub-option negotiation for this. */
980 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
981 "Bogus suboption data");
985 /* Make sure the length is what it's supposed to be. */
986 if (subneg_len != options[opt_byte].optlen) {
987 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
988 "Suboption parameter length is %d, should be %d",
989 subneg_len, options[opt_byte].optlen);
994 case VARIABLE_LENGTH:
995 /* Make sure the length is greater than the minimum. */
996 if (subneg_len < options[opt_byte].optlen) {
997 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
998 "Suboption parameter length is %d, should be at least %d",
999 subneg_len, options[opt_byte].optlen);
1005 /* Now dissect the suboption parameters. */
1006 if (dissect != NULL) {
1007 /* We have a dissector for this suboption's parameters; call it. */
1008 (*dissect)(opt, tvb, start_offset, subneg_len, option_tree);
1010 /* We don't have a dissector for them; just show them as data. */
1011 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1019 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
1020 int start_offset, char *type)
1022 int offset = start_offset;
1026 offset += 2; /* skip IAC and WILL,WONT,DO,DONT} */
1027 opt_byte = tvb_get_guint8(tvb, offset);
1028 if (opt_byte > NOPTIONS)
1029 opt = "<unknown option>";
1031 opt = options[opt_byte].name;
1034 proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
1035 "Command: %s %s", type, opt);
1040 telnet_command(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
1042 int offset = start_offset;
1045 offset += 1; /* skip IAC */
1046 optcode = tvb_get_guint8(tvb, offset);
1051 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1052 "Command: End of File");
1056 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1057 "Command: Suspend Current Process");
1061 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1062 "Command: Abort Process");
1066 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1067 "Command: End of Record");
1071 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1072 "Command: Suboption End");
1076 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1077 "Command: No Operation");
1081 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1082 "Command: Data Mark");
1086 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1091 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1092 "Command: Interrupt Process");
1096 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1097 "Command: Abort Output");
1101 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1102 "Command: Are You There?");
1106 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1107 "Command: Escape Character");
1111 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1112 "Command: Erase Line");
1116 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1117 "Command: Go Ahead");
1121 offset = telnet_sub_option(telnet_tree, tvb, start_offset);
1125 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1130 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1135 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1140 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1145 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1146 "Command: Unknown (0x%02x)", optcode);
1154 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
1159 gboolean last_char_was_cr;
1161 while (len != 0 && tvb_offset_exists(tvb, offset)) {
1163 * Find the end of the line.
1165 linelen = tvb_find_line_end(tvb, offset, len, &next_offset, FALSE);
1166 len -= next_offset - offset; /* subtract out the line's characters */
1169 * In Telnet, CR NUL is the way you send a CR by itself in the
1170 * default ASCII mode; don't treat CR by itself as a line ending,
1171 * treat only CR NUL, CR LF, or LF by itself as a line ending.
1173 if (next_offset == offset + linelen + 1 && len >= 1) {
1175 * Well, we saw a one-character line ending, so either it's a CR
1176 * or an LF; we have at least two characters left, including the
1179 * If the line ending is a CR, skip all subsequent CRs; at
1180 * least one capture appeared to have multiple CRs at the end of
1183 if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
1184 last_char_was_cr = TRUE;
1185 while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
1186 c = tvb_get_guint8(tvb, next_offset);
1187 next_offset++; /* skip over that character */
1189 if (c == '\n' || (c == '\0' && last_char_was_cr)) {
1191 * LF is a line ending, whether preceded by CR or not.
1192 * NUL is a line ending if preceded by CR.
1196 last_char_was_cr = (c == '\r');
1202 * Now compute the length of the line *including* the end-of-line
1203 * indication, if any; we display it all.
1205 linelen = next_offset - offset;
1207 proto_tree_add_text(tree, tvb, offset, linelen,
1209 tvb_format_text(tvb, offset, linelen));
1210 offset = next_offset;
1215 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1217 proto_tree *telnet_tree, *ti;
1219 if (check_col(pinfo->cinfo, COL_PROTOCOL))
1220 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
1222 if (check_col(pinfo->cinfo, COL_INFO))
1223 col_add_fstr(pinfo->cinfo, COL_INFO, "Telnet Data ...");
1231 ti = proto_tree_add_item(tree, proto_telnet, tvb, offset, -1, FALSE);
1232 telnet_tree = proto_item_add_subtree(ti, ett_telnet);
1235 * Scan through the buffer looking for an IAC byte.
1237 while ((len = tvb_length_remaining(tvb, offset)) > 0) {
1238 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
1239 if (iac_offset != -1) {
1241 * We found an IAC byte.
1242 * If there's any data before it, add that data to the
1243 * tree, a line at a time.
1245 data_len = iac_offset - offset;
1247 telnet_add_text(telnet_tree, tvb, offset, data_len);
1250 * Now interpret the command.
1252 offset = telnet_command(telnet_tree, tvb, iac_offset);
1256 * We found no IAC byte, so what remains in the buffer
1257 * is the last of the data in the packet.
1258 * Add it to the tree, a line at a time, and then quit.
1260 telnet_add_text(telnet_tree, tvb, offset, len);
1268 proto_register_telnet(void)
1270 /* static hf_register_info hf[] = {
1272 { "Name", "telnet.abbreviation", TYPE, VALS_POINTER }},
1274 static gint *ett[] = {
1282 &ett_htstops_subopt,
1285 &ett_vtstops_subopt,
1289 &ett_bytemacro_subopt,
1291 &ett_supdupout_subopt,
1292 &ett_sendloc_subopt,
1293 &ett_termtype_subopt,
1294 &ett_tacacsui_subopt,
1295 &ett_outmark_subopt,
1296 &ett_tlocnum_subopt,
1297 &ett_tn3270reg_subopt,
1302 &ett_linemode_subopt,
1303 &ett_xdpyloc_subopt,
1308 &ett_tn3270e_subopt,
1310 &ett_charset_subopt,
1312 &ett_comport_subopt,
1315 proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
1316 /* proto_register_field_array(proto_telnet, hf, array_length(hf));*/
1317 proto_register_subtree_array(ett, array_length(ett));
1321 proto_reg_handoff_telnet(void)
1323 dissector_handle_t telnet_handle;
1325 telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
1326 dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);