Don't guard col_set_str (COL_INFO) with col_check
[metze/wireshark/wip.git] / epan / dissectors / packet-telnet.c
1 /* packet-telnet.c
2  * Routines for Telnet packet dissection; see RFC 854 and RFC 855
3  * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * Copied from packet-pop.c
12  *
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.
17  *
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.
22  *
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.
26  */
27 /* Telnet authentication options as per     RFC2941
28  * Kerberos v5 telnet authentication as per RFC2942
29  */
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdio.h>
35
36 #include <string.h>
37 #include <glib.h>
38 #include <epan/packet.h>
39 #include <epan/strutil.h>
40 #include <epan/emem.h>
41 #include <epan/asn1.h>
42 #include "packet-kerberos.h"
43 #include "packet-tn3270.h"
44
45 static int proto_telnet = -1;
46 static int hf_telnet_auth_cmd = -1;
47 static int hf_telnet_auth_name = -1;
48 static int hf_telnet_auth_type = -1;
49 static int hf_telnet_auth_mod_who = -1;
50 static int hf_telnet_auth_mod_how = -1;
51 static int hf_telnet_auth_mod_cred_fwd = -1;
52 static int hf_telnet_auth_mod_enc = -1;
53 static int hf_telnet_auth_krb5_type = -1;
54
55 static int hf_telnet_enc_cmd = -1;
56 static int hf_telnet_enc_type = -1;
57
58 static gint ett_telnet = -1;
59 static gint ett_telnet_subopt = -1;
60 static gint ett_status_subopt = -1;
61 static gint ett_rcte_subopt = -1;
62 static gint ett_olw_subopt = -1;
63 static gint ett_ops_subopt = -1;
64 static gint ett_crdisp_subopt = -1;
65 static gint ett_htstops_subopt = -1;
66 static gint ett_htdisp_subopt = -1;
67 static gint ett_ffdisp_subopt = -1;
68 static gint ett_vtstops_subopt = -1;
69 static gint ett_vtdisp_subopt = -1;
70 static gint ett_lfdisp_subopt = -1;
71 static gint ett_extasc_subopt = -1;
72 static gint ett_bytemacro_subopt = -1;
73 static gint ett_det_subopt = -1;
74 static gint ett_supdupout_subopt = -1;
75 static gint ett_sendloc_subopt = -1;
76 static gint ett_termtype_subopt = -1;
77 static gint ett_tacacsui_subopt = -1;
78 static gint ett_outmark_subopt = -1;
79 static gint ett_tlocnum_subopt = -1;
80 static gint ett_tn3270reg_subopt = -1;
81 static gint ett_x3pad_subopt = -1;
82 static gint ett_naws_subopt = -1;
83 static gint ett_tspeed_subopt = -1;
84 static gint ett_rfc_subopt = -1;
85 static gint ett_linemode_subopt = -1;
86 static gint ett_xdpyloc_subopt = -1;
87 static gint ett_env_subopt = -1;
88 static gint ett_auth_subopt = -1;
89 static gint ett_enc_subopt = -1;
90 static gint ett_newenv_subopt = -1;
91 static gint ett_tn3270e_subopt = -1;
92 static gint ett_xauth_subopt = -1;
93 static gint ett_charset_subopt = -1;
94 static gint ett_rsp_subopt = -1;
95 static gint ett_comport_subopt = -1;
96
97 static dissector_handle_t tn3270_handle;
98
99 /* Some defines for Telnet */
100
101 #define TCP_PORT_TELNET                 23
102
103 #define TN_IAC   255
104 #define TN_DONT  254
105 #define TN_DO    253
106 #define TN_WONT  252
107 #define TN_WILL  251
108 #define TN_SB    250
109 #define TN_GA    249
110 #define TN_EL    248
111 #define TN_EC    247
112 #define TN_AYT   246
113 #define TN_AO    245
114 #define TN_IP    244
115 #define TN_BRK   243
116 #define TN_DM    242
117 #define TN_NOP   241
118 #define TN_SE    240
119 #define TN_EOR   239
120 #define TN_ABORT 238
121 #define TN_SUSP  237
122 #define TN_EOF   236
123 #define TN_ARE   1
124
125
126 typedef enum {
127   NO_LENGTH,            /* option has no data, hence no length */
128   FIXED_LENGTH,         /* option always has the same length */
129   VARIABLE_LENGTH       /* option is variable-length - optlen is minimum */
130 } tn_opt_len_type;
131
132 /* Member of table of IP or TCP options. */
133 typedef struct tn_opt {
134   const char  *name;            /* name of option */
135   gint  *subtree_index;         /* pointer to subtree index for option */
136   tn_opt_len_type len_type;     /* type of option length field */
137   int   optlen;                 /* value length should be (minimum if VARIABLE) */
138   void  (*dissect)(packet_info *pinfo, const char *, tvbuff_t *, int, int, proto_tree *);
139                                 /* routine to dissect option */
140 } tn_opt;
141
142 static void
143 check_for_tn3270(packet_info *pinfo _U_, const char *optname, const char *terminaltype)
144 {
145
146   if (strcmp(optname,"Terminal Type") != 0) {
147       return;
148   }
149
150   if ((strcmp(terminaltype,"IBM-3278-2-E") == 0) || (strcmp(terminaltype,"IBM-3278-2") == 0) ||
151       (strcmp(terminaltype,"IBM-3278-3") == 0) || (strcmp(terminaltype,"IBM-3278-4") == 0) ||
152       (strcmp(terminaltype,"IBM-3278-5") == 0) || (strcmp(terminaltype,"IBM-3277-2") == 0) ||
153       (strcmp(terminaltype,"IBM-3279-3") == 0) || (strcmp(terminaltype,"IBM-3279-4") == 0) ||
154       (strcmp(terminaltype,"IBM-3279-2-E") == 0) || (strcmp(terminaltype,"IBM-3279-2") == 0) ||
155       (strcmp(terminaltype,"IBM-3279-4-E") == 0))
156       add_tn3270_conversation(pinfo, 0);
157
158 }
159
160 static void
161 dissect_string_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
162                       proto_tree *tree)
163 {
164   guint8 cmd;
165
166   cmd = tvb_get_guint8(tvb, offset);
167   switch (cmd) {
168
169   case 0:       /* IS */
170     proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
171     offset++;
172     len--;
173     if (len > 0) {
174       proto_tree_add_text(tree, tvb, offset, len, "Value: %s",
175                           tvb_format_text(tvb, offset, len));
176     }
177     check_for_tn3270(pinfo, optname, tvb_format_text(tvb, offset, len));
178     break;
179
180   case 1:       /* SEND */
181     proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
182     offset++;
183     len--;
184     if (len > 0)
185       proto_tree_add_text(tree, tvb, offset, len, "Extra data");
186     break;
187
188   default:
189     proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
190                         optname, cmd);
191     offset++;
192     len--;
193     if (len > 0)
194       proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
195     break;
196   }
197 }
198
199 static void
200 dissect_tn3270_regime_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
201                        int len, proto_tree *tree)
202 {
203 #define TN3270_REGIME_ARE          0x01
204 #define TN3270_REGIME_IS           0x00
205
206
207   guint8 cmd;
208   while (len > 0) {
209     cmd = tvb_get_guint8(tvb, offset);
210     switch (cmd) {
211       case TN3270_REGIME_ARE:
212       case TN3270_REGIME_IS:
213         if (cmd == TN3270_REGIME_ARE) {
214             proto_tree_add_text(tree, tvb, offset, 1, "ARE");
215             add_tn3270_conversation(pinfo, 0);
216         } else {
217             proto_tree_add_text(tree, tvb, offset, 1, "IS");
218         }
219         proto_tree_add_text(tree, tvb, offset + 1, len - 1, "%s",
220                 tvb_format_text(tvb, offset + 1, len - 1));
221         offset += len;
222         len -= len;
223         return;
224       default:
225         proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", cmd);
226         break;
227     }
228     offset++;
229     len --;
230   }
231
232 }
233
234 #define TN3270_ASSOCIATE          0x00
235 #define TN3270_CONNECT            0x01
236 #define TN3270_DEVICE_TYPE        0x02
237 #define TN3270_FUNCTIONS          0x03
238 #define TN3270_IS                 0x04
239 #define TN3270_REASON             0x05
240 #define TN3270_REJECT             0x06
241 #define TN3270_REQUEST            0x07
242 #define TN3270_SEND               0x08
243 /*       Reason_codes*/
244 #define TN3270_CONN_PARTNER       0x00
245 #define TN3270_DEVICE_IN_USE      0x01
246 #define TN3270_INV_ASSOCIATE      0x02
247 #define TN3270_INV_DEVICE_NAME    0x03
248 #define TN3270_INV_DEVICE_TYPE    0x04
249 #define TN3270_TYPE_NAME_ERROR    0x05
250 #define TN3270_UNKNOWN_ERROR      0x06
251 #define TN3270_UNSUPPORTED_REQ    0x07
252 /*       Function Names*/
253 #define TN3270_BIND_IMAGE         0x00
254 #define TN3270_DATA_STREAM_CTL    0x01
255 #define TN3270_RESPONSES          0x02
256 #define TN3270_SCS_CTL_CODES      0x03
257 #define TN3270_SYSREQ             0x04
258
259 static void
260 dissect_tn3270e_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
261                        int len, proto_tree *tree)
262 {
263
264   guint8 cmd;
265   int datalen;
266   int connect_offset = 0;
267   int device_type = 0;
268   int rsn = 0;
269   while (len > 0) {
270     cmd = tvb_get_guint8(tvb, offset);
271     switch (cmd) {
272       case TN3270_ASSOCIATE:
273             proto_tree_add_text(tree, tvb, offset, 1, "ASSOCIATE");
274             break;
275       case TN3270_CONNECT:
276             proto_tree_add_text(tree, tvb, offset, 1, "CONNECT");
277             proto_tree_add_text(tree, tvb, offset + 1, len, "%s",
278                                 tvb_format_text(tvb, offset + 1, len - 1));
279             offset += (len - 1);
280             len -= (len - 1);
281             break;
282       case TN3270_DEVICE_TYPE:
283             proto_tree_add_text(tree, tvb, offset, 1, "DEVICE-TYPE");
284             break;
285       case TN3270_FUNCTIONS:
286             proto_tree_add_text(tree, tvb, offset, 1, "FUNCTIONS");
287             break;
288       case TN3270_IS:
289             proto_tree_add_text(tree, tvb, offset, 1, "IS");
290             device_type = tvb_get_guint8(tvb, offset-1);
291             if (device_type == TN3270_DEVICE_TYPE) {
292                 /* If there is a terminal type to display, then it will be followed by CONNECT */
293                 connect_offset = tvb_find_guint8(tvb, offset + 1, len, TN3270_CONNECT);
294                 if (connect_offset != -1) {
295                   datalen = connect_offset - (offset + 1);
296                   if (datalen > 0) {
297                     proto_tree_add_text(tree, tvb, offset + 1, datalen, "%s",
298                                         tvb_format_text(tvb, offset + 1, datalen));
299                     offset += datalen;
300                     len -= datalen;
301                   }
302                 }
303             }
304             break;
305       case TN3270_REASON:
306             proto_tree_add_text(tree, tvb, offset, 1, "REASON");
307             offset++;
308             len--;
309             rsn = tvb_get_guint8(tvb, offset);
310             switch (rsn) {
311               case TN3270_CONN_PARTNER:
312                     proto_tree_add_text(tree, tvb, offset, 1, "CONN-PARTNER");
313                     break;
314               case TN3270_DEVICE_IN_USE:
315                     proto_tree_add_text(tree, tvb, offset, 1, "DEVICE-IN-USE");
316                     break;
317               case TN3270_INV_ASSOCIATE:
318                     proto_tree_add_text(tree, tvb, offset, 1, "INV-ASSOCIATE");
319                     break;
320               case TN3270_INV_DEVICE_NAME:
321                     proto_tree_add_text(tree, tvb, offset, 1, "INV-DEVICE-NAME");
322                     break;
323               case TN3270_INV_DEVICE_TYPE:
324                     proto_tree_add_text(tree, tvb, offset, 1, "INV-DEVICE-TYPE");
325                     break;
326               case TN3270_TYPE_NAME_ERROR:
327                     proto_tree_add_text(tree, tvb, offset, 1, "TYPE-NAME-ERROR");
328                     break;
329               case TN3270_UNKNOWN_ERROR:
330                     proto_tree_add_text(tree, tvb, offset, 1, "UNKNOWN-ERROR");
331                     break;
332               case TN3270_UNSUPPORTED_REQ:
333                     proto_tree_add_text(tree, tvb, offset, 1, "UNSUPPORTED-REQ");
334                     break;
335               default:
336                     proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", rsn);
337                     break;
338             }
339             break;
340       case TN3270_REJECT:
341             proto_tree_add_text(tree, tvb, offset, 1, "REJECT");
342             break;
343       case TN3270_REQUEST:
344             add_tn3270_conversation(pinfo, 1);
345             proto_tree_add_text(tree, tvb, offset, 1, "REQUEST");
346             device_type = tvb_get_guint8(tvb, offset-1);
347             if (device_type == TN3270_DEVICE_TYPE) {
348               proto_tree_add_text(tree, tvb, offset + 1, len, "%s",
349                                   tvb_format_text(tvb, offset + 1, len - 1));
350               offset += (len - 1);
351               len -= (len - 1);
352             }else if (device_type == TN3270_FUNCTIONS) {
353               int fn = 0;
354               while (len > 0 && fn < 5) {
355                 rsn = tvb_get_guint8(tvb, offset);
356                 switch (rsn) {
357                   case TN3270_BIND_IMAGE:
358                         proto_tree_add_text(tree, tvb, offset, 1, "BIND-IMAGE");
359                         break;
360                   case TN3270_DATA_STREAM_CTL:
361                         proto_tree_add_text(tree, tvb, offset, 1, "DATA-STREAM-CTL");
362                         break;
363                   case TN3270_RESPONSES:
364                         proto_tree_add_text(tree, tvb, offset, 1, "RESPONSES");
365                         break;
366                   case TN3270_SCS_CTL_CODES:
367                         proto_tree_add_text(tree, tvb, offset, 1, "SCS-CTL-CODES");
368                         break;
369                   case TN3270_SYSREQ:
370                         proto_tree_add_text(tree, tvb, offset, 1, "SYSREQ");
371                         break;
372                   default:
373                         fn = 5;
374                         break;
375                 }
376                 offset++;
377                 len--;
378               }
379             }
380             break;
381       case TN3270_SEND:
382             proto_tree_add_text(tree, tvb, offset, 1, "SEND");
383             break;
384       default:
385             proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", cmd);
386             break;
387     }
388     offset++;
389     len--;
390   }
391
392 }
393
394 static void
395 dissect_outmark_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
396                        int len, proto_tree *tree)
397 {
398   guint8 cmd;
399   int gs_offset, datalen;
400
401   while (len > 0) {
402     cmd = tvb_get_guint8(tvb, offset);
403     switch (cmd) {
404
405     case 6:     /* ACK */
406       proto_tree_add_text(tree, tvb, offset, 1, "ACK");
407       break;
408
409     case 21:    /* NAK */
410       proto_tree_add_text(tree, tvb, offset, 1, "NAK");
411       break;
412
413     case 'D':
414       proto_tree_add_text(tree, tvb, offset, 1, "Default");
415       break;
416
417     case 'T':
418       proto_tree_add_text(tree, tvb, offset, 1, "Top");
419       break;
420
421     case 'B':
422       proto_tree_add_text(tree, tvb, offset, 1, "Bottom");
423       break;
424
425     case 'L':
426       proto_tree_add_text(tree, tvb, offset, 1, "Left");
427       break;
428
429     case 'R':
430       proto_tree_add_text(tree, tvb, offset, 1, "Right");
431       break;
432
433     default:
434       proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", cmd);
435       break;
436     }
437     offset++;
438     len--;
439
440     /* Look for a GS */
441     gs_offset = tvb_find_guint8(tvb, offset, len, 29);
442     if (gs_offset == -1) {
443       /* None found - run to the end of the packet. */
444       gs_offset = offset + len;
445     }
446     datalen = gs_offset - offset;
447     if (datalen > 0) {
448       proto_tree_add_text(tree, tvb, offset, datalen, "Banner: %s",
449                           tvb_format_text(tvb, offset, datalen));
450       offset += datalen;
451       len -= datalen;
452     }
453   }
454 }
455
456 static void
457 dissect_htstops_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
458                        proto_tree *tree)
459 {
460   guint8 cmd;
461   guint8 tabval;
462
463   cmd = tvb_get_guint8(tvb, offset);
464   switch (cmd) {
465
466   case 0:       /* IS */
467     proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
468     offset++;
469     len--;
470     break;
471
472   case 1:       /* SEND */
473     proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
474     offset++;
475     len--;
476     break;
477
478   default:
479     proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
480                         optname, cmd);
481     offset++;
482     len--;
483     if (len > 0)
484       proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
485     return;
486   }
487
488   while (len > 0) {
489     tabval = tvb_get_guint8(tvb, offset);
490     switch (tabval) {
491
492     case 0:
493       proto_tree_add_text(tree, tvb, offset, 1,
494                           "Sender wants to handle tab stops");
495       break;
496
497     default:
498       proto_tree_add_text(tree, tvb, offset, 1,
499                           "Sender wants receiver to handle tab stop at %u",
500                           tabval);
501       break;
502
503     case 251:
504     case 252:
505     case 253:
506     case 254:
507       proto_tree_add_text(tree, tvb, offset, 1,
508                           "Invalid value: %u", tabval);
509       break;
510
511     case 255:
512       proto_tree_add_text(tree, tvb, offset, 1,
513                           "Sender wants receiver to handle tab stops");
514       break;
515     }
516     offset++;
517     len--;
518   }
519 }
520
521 static void
522 dissect_naws_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
523                     int len _U_, proto_tree *tree)
524 {
525   proto_tree_add_text(tree, tvb, offset, 2, "Width: %u",
526                       tvb_get_ntohs(tvb, offset));
527   offset += 2;
528   proto_tree_add_text(tree, tvb, offset, 2, "Height: %u",
529                       tvb_get_ntohs(tvb, offset));
530 }
531
532 /* BEGIN RFC-2217 (COM Port Control) Definitions */
533
534 #define TNCOMPORT_SIGNATURE             0
535 #define TNCOMPORT_SETBAUDRATE           1
536 #define TNCOMPORT_SETDATASIZE           2
537 #define TNCOMPORT_SETPARITY             3
538 #define TNCOMPORT_SETSTOPSIZE           4
539 #define TNCOMPORT_SETCONTROL            5
540 #define TNCOMPORT_NOTIFYLINESTATE       6
541 #define TNCOMPORT_NOTIFYMODEMSTATE      7
542 #define TNCOMPORT_FLOWCONTROLSUSPEND    8
543 #define TNCOMPORT_FLOWCONTROLRESUME      9
544 #define TNCOMPORT_SETLINESTATEMASK      10
545 #define TNCOMPORT_SETMODEMSTATEMASK     11
546 #define TNCOMPORT_PURGEDATA             12
547
548 /* END RFC-2217 (COM Port Control) Definitions */
549
550 static void
551 dissect_comport_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
552                        proto_tree *tree)
553 {static const char *datasizes[] = {
554     "Request",
555     "<invalid>",
556     "<invalid>",
557     "<invalid>",
558     "<invalid>",
559     "5",
560     "6",
561     "7",
562     "8"
563  };
564  static const char *parities[] = {
565     "Request",
566     "None",
567     "Odd",
568     "Even",
569     "Mark",
570     "Space"
571  };
572  static const char *stops[] = {
573     "Request",
574     "1",
575     "2",
576     "1.5"
577  };
578  static const char *control[] = {
579     "Output Flow Control Request",
580     "Output Flow: None",
581     "Output Flow: XON/XOFF",
582     "Output Flow: CTS/RTS",
583     "Break Request",
584     "Break: ON",
585     "Break: OFF",
586     "DTR Request",
587     "DTR: ON",
588     "DTR: OFF",
589     "RTS Request",
590     "RTS: ON",
591     "RTS: OFF",
592     "Input Flow Control Request",
593     "Input Flow: None",
594     "Input Flow: XON/XOFF",
595     "Input Flow: CTS/RTS",
596     "Output Flow: DCD",
597     "Input Flow: DTR",
598     "Output Flow: DSR"
599  };
600  static const char *linestate_bits[] = {
601     "Data Ready",
602     "Overrun Error",
603     "Parity Error",
604     "Framing Error",
605     "Break Detected",
606     "Transfer Holding Register Empty",
607     "Transfer Shift Register Empty",
608     "Timeout Error"
609  };
610  static const char *modemstate_bits[] = {
611      "DCTS",
612      "DDSR",
613      "TERI",
614      "DDCD",
615      "CTS",
616      "DSR",
617      "RI",
618      "DCD"
619  };
620  static const char *purges[] = {
621      "Purge None",
622      "Purge RX",
623      "Purge TX",
624      "Purge RX/TX"
625  };
626   
627   guint8 cmd;
628   guint8 isservercmd;
629   const char *source;
630   
631   cmd = tvb_get_guint8(tvb, offset);
632   isservercmd = cmd > 99;
633   cmd = (isservercmd) ? (cmd - 100) : cmd;
634   source = (isservercmd) ? "Server" : "Client";
635   switch (cmd) {
636
637   case TNCOMPORT_SIGNATURE:
638     len--;
639     if (len == 0) {
640         proto_tree_add_text(tree, tvb, offset, 1, "%s Requests Signature",source);
641     } else {
642         guint8 *sig = tvb_get_ephemeral_string(tvb, offset + 1, len);
643         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s Signature: %s",source, sig);
644     }
645     break;
646
647   case TNCOMPORT_SETBAUDRATE:
648     len--;
649     if (len >= 4) {
650         guint32 baud = tvb_get_ntohl(tvb, offset+1);
651         if (baud == 0) {
652             proto_tree_add_text(tree, tvb, offset, 5, "%s Requests Baud Rate",source);            
653         } else {
654             proto_tree_add_text(tree, tvb, offset, 5, "%s Baud Rate: %d",source,baud);
655         }
656     } else {
657         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Baud Rate Packet>",source);
658     }
659     break;
660     
661   case TNCOMPORT_SETDATASIZE:
662     len--;
663     if (len >= 1) {
664         guint8 datasize = tvb_get_guint8(tvb, offset+1);
665         const char *ds = (datasize > 8) ? "<invalid>" : datasizes[datasize];
666         proto_tree_add_text(tree, tvb, offset, 2, "%s Data Size: %s",source,ds);
667     } else {
668         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Data Size Packet>",source);
669     }
670     break;
671
672   case TNCOMPORT_SETPARITY:
673     len--;
674     if (len >= 1) {
675         guint8 parity = tvb_get_guint8(tvb, offset+1);
676         const char *pr = (parity > 5) ? "<invalid>" : parities[parity];
677         proto_tree_add_text(tree, tvb, offset, 2, "%s Parity: %s",source,pr);
678     } else {
679         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Parity Packet>",source);
680     }
681     break;
682     
683   case TNCOMPORT_SETSTOPSIZE:
684     len--;
685     if (len >= 1) {
686         guint8 stop = tvb_get_guint8(tvb, offset+1);
687         const char *st = (stop > 3) ? "<invalid>" : stops[stop];
688         proto_tree_add_text(tree, tvb, offset, 2, "%s Stop: %s",source,st);
689     } else {
690         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Stop Packet>",source);
691     }
692     break;
693
694   case TNCOMPORT_SETCONTROL:
695     len--;
696     if (len >= 1) {
697         guint8 crt = tvb_get_guint8(tvb, offset+1);
698         const char *c = (crt > 19) ? "Control: <invalid>" : control[crt];
699         proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,c);
700     } else {
701         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Control Packet>",source);
702     }
703     break;
704     
705   case TNCOMPORT_SETLINESTATEMASK:
706   case TNCOMPORT_NOTIFYLINESTATE:
707     len--;
708     if (len >= 1) {
709         const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
710                                         "%s Set Linestate Mask: %s" : "%s Linestate: %s";
711         char ls_buffer[512];
712         guint8 ls = tvb_get_guint8(tvb, offset+1);
713         int print_count = 0;
714         int idx;
715         ls_buffer[0] = '\0';
716         for (idx = 0; idx < 8; idx++) {
717             int bit = ls & 1;
718             if (bit) {
719                 if (print_count != 0) {
720                     g_strlcat(ls_buffer,", ",512);
721                 }
722                 g_strlcat(ls_buffer,linestate_bits[idx], 512);
723                 print_count++;
724             }
725             ls = ls >> 1;
726         }
727         proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ls_buffer);
728     } else {
729         const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
730                                         "%s <Invalid Linestate Mask>" : "%s <Invalid Linestate Packet>";
731         proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
732     }
733     break;
734     
735   case TNCOMPORT_SETMODEMSTATEMASK:
736   case TNCOMPORT_NOTIFYMODEMSTATE:
737     len--;
738     if (len >= 1) {
739         const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
740                                         "%s Set Modemstate Mask: %s" : "%s Modemstate: %s";
741         char ms_buffer[256];
742             guint8 ms = tvb_get_guint8(tvb, offset+1);
743         int print_count = 0;
744         int idx;
745         ms_buffer[0] = '\0';
746         for (idx = 0; idx < 8; idx++) {
747             int bit = ms & 1;
748             if (bit) {
749                 if (print_count != 0) {
750                     g_strlcat(ms_buffer,", ",256);
751                 }
752                 g_strlcat(ms_buffer,modemstate_bits[idx],256);
753                 print_count++;
754             }
755             ms = ms >> 1;
756         }
757         proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ms_buffer);
758     } else {
759         const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
760                                          "%s <Invalid Modemstate Mask>" : "%s <Invalid Modemstate Packet>";
761         proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
762     }
763     break;
764
765   case TNCOMPORT_FLOWCONTROLSUSPEND:
766     len--;
767     proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Suspend",source);
768     break;
769   
770   case TNCOMPORT_FLOWCONTROLRESUME:
771     len--;
772     proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Resume",source);
773     break;
774   
775   case TNCOMPORT_PURGEDATA:
776     len--;
777     if (len >= 1) {
778             guint8 purge = tvb_get_guint8(tvb, offset+1);
779         const char *p = (purge > 3) ? "<Purge invalid>" : purges[purge];
780         proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,p);
781     } else {
782         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Purge Packet>",source);
783     }
784     break;
785   
786   default:
787     proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
788                         optname, cmd);
789     offset++;
790     len--;
791     if (len > 0)
792       proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
793     return;
794   }
795
796 }
797
798 static const value_string rfc_opt_vals[] = {
799         { 0, "OFF" },
800         { 1, "ON" },
801         { 2, "RESTART-ANY" },
802         { 3, "RESTART-XON" },
803         { 0, NULL }
804 };
805
806 static void
807 dissect_rfc_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
808                    int len _U_, proto_tree *tree)
809 {
810   guint8 cmd;
811
812   cmd = tvb_get_guint8(tvb, offset);
813   proto_tree_add_text(tree, tvb, offset, 2, "%s",
814                       val_to_str(cmd, rfc_opt_vals, "Unknown (%u)"));
815 }
816
817 #define TN_ENC_IS               0
818 #define TN_ENC_SUPPORT          1
819 #define TN_ENC_REPLY            2
820 #define TN_ENC_START            3
821 #define TN_ENC_END              4
822 #define TN_ENC_REQUEST_START    5
823 #define TN_ENC_REQUEST_END      6
824 #define TN_ENC_ENC_KEYID        7
825 #define TN_ENC_DEC_KEYID        8
826 static const value_string enc_cmd_vals[] = {
827         { TN_ENC_IS,            "IS" },
828         { TN_ENC_SUPPORT,       "SUPPORT" },
829         { TN_ENC_REPLY,         "REPLY" },
830         { TN_ENC_START,         "START" },
831         { TN_ENC_END,           "END" },
832         { TN_ENC_REQUEST_START, "REQUEST-START" },
833         { TN_ENC_REQUEST_END,   "REQUEST-END" },
834         { TN_ENC_ENC_KEYID,     "ENC_KEYID" },
835         { TN_ENC_DEC_KEYID,     "DEC_KEYID" },
836         { 0, NULL }
837 };
838
839 #define TN_ENCTYPE_NULL                 0
840 #define TN_ENCTYPE_DES_CFB64            1
841 #define TN_ENCTYPE_DES_OFB64            2
842 #define TN_ENCTYPE_DES3_CFB64           3
843 #define TN_ENCTYPE_DES3_OFB64           4
844 #define TN_ENCTYPE_CAST5_40_CFB64       8
845 #define TN_ENCTYPE_CAST5_40_OFB64       9
846 #define TN_ENCTYPE_CAST128_CFB64        10
847 #define TN_ENCTYPE_CAST128_OFB64        11
848 static const value_string enc_type_vals[] = {
849     { TN_ENCTYPE_NULL,                  "NULL" },
850     { TN_ENCTYPE_DES_CFB64,             "DES_CFB64" },
851     { TN_ENCTYPE_DES_OFB64,             "DES_OFB64" },
852     { TN_ENCTYPE_DES3_CFB64,            "DES3_CFB64" },
853     { TN_ENCTYPE_DES3_OFB64,            "DES3_OFB64" },
854     { TN_ENCTYPE_CAST5_40_CFB64,        "CAST5_40_CFB64" },
855     { TN_ENCTYPE_CAST5_40_OFB64,        "CAST5_40_OFB64" },
856     { TN_ENCTYPE_CAST128_CFB64,         "CAST128_CFB64" },
857     { TN_ENCTYPE_CAST128_OFB64,         "CAST128_OFB64" },
858     { 0, NULL }
859 };
860
861
862 #define TN_AC_IS        0
863 #define TN_AC_SEND      1
864 #define TN_AC_REPLY     2
865 #define TN_AC_NAME      3
866 static const value_string auth_cmd_vals[] = {
867         { TN_AC_IS,     "IS" },
868         { TN_AC_SEND,   "SEND" },
869         { TN_AC_REPLY,  "REPLY" },
870         { TN_AC_NAME,   "NAME" },
871         { 0, NULL }
872 };
873
874 #define TN_AT_NULL      0
875 #define TN_AT_KRB4      1
876 #define TN_AT_KRB5      2
877 #define TN_AT_SPX       3
878 #define TN_AT_MINK      4
879 #define TN_AT_SRP       5
880 #define TN_AT_RSA       6
881 #define TN_AT_SSL       7
882 #define TN_AT_LOKI      10
883 #define TN_AT_SSA       11
884 #define TN_AT_KEA_SJ    12
885 #define TN_AT_KEA_SJ_INTEG      13
886 #define TN_AT_DSS       14
887 #define TN_AT_NTLM      15
888 static const value_string auth_type_vals[] = {
889         { TN_AT_NULL,   "NULL" },
890         { TN_AT_KRB4,   "Kerberos v4" },
891         { TN_AT_KRB5,   "Kerberos v5" },
892         { TN_AT_SPX,    "SPX" },
893         { TN_AT_MINK,   "MINK" },
894         { TN_AT_SRP,    "SRP" },
895         { TN_AT_RSA,    "RSA" },
896         { TN_AT_SSL,    "SSL" },
897         { TN_AT_LOKI,   "LOKI" },
898         { TN_AT_SSA,    "SSA" },
899         { TN_AT_KEA_SJ, "KEA_SJ" },
900         { TN_AT_KEA_SJ_INTEG, "KEA_SJ_INTEG" },
901         { TN_AT_DSS,    "DSS" },
902         { TN_AT_NTLM,   "NTLM" },
903         { 0, NULL }
904 };
905 static const true_false_string auth_mod_cred_fwd = {
906         "Client WILL forward auth creds",
907         "Client will NOT forward auth creds"
908 };
909 static const true_false_string auth_mod_who = {
910         "Mask server to client",
911         "Mask client to server"
912 };
913 static const true_false_string auth_mod_how = {
914         "MUTUAL authentication",
915         "One Way authentication"
916 };
917 #define TN_AM_OFF               0x00
918 #define TN_AM_USING_TELOPT      0x01
919 #define TN_AM_AFTER_EXCHANGE    0x02
920 #define TN_AM_RESERVED          0x04
921 static const value_string auth_mod_enc[] = {
922         { TN_AM_OFF,            "Off" },
923         { TN_AM_USING_TELOPT,   "Telnet Options" },
924         { TN_AM_AFTER_EXCHANGE, "After Exchange" },
925         { TN_AM_RESERVED,       "Reserved" },
926         { 0, NULL }
927 };
928 #define TN_KRB5_TYPE_AUTH               0
929 #define TN_KRB5_TYPE_REJECT             1
930 #define TN_KRB5_TYPE_ACCEPT             2
931 #define TN_KRB5_TYPE_RESPONSE           3
932 #define TN_KRB5_TYPE_FORWARD            4
933 #define TN_KRB5_TYPE_FORWARD_ACCEPT     5
934 #define TN_KRB5_TYPE_FORWARD_REJECT     6
935 static const value_string auth_krb5_types[] = {
936         { TN_KRB5_TYPE_AUTH,            "Auth" },
937         { TN_KRB5_TYPE_REJECT,          "Reject" },
938         { TN_KRB5_TYPE_ACCEPT,          "Accept" },
939         { TN_KRB5_TYPE_RESPONSE,        "Response" },
940         { TN_KRB5_TYPE_FORWARD,         "Forward" },
941         { TN_KRB5_TYPE_FORWARD_ACCEPT,  "Forward Accept" },
942         { TN_KRB5_TYPE_FORWARD_REJECT,  "Forward Reject" },
943         { 0, NULL }
944 };
945 static void
946 dissect_authentication_type_pair(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, proto_tree *tree)
947 {
948         guint8 type, mod;
949
950         type=tvb_get_guint8(tvb, offset);
951         proto_tree_add_uint(tree, hf_telnet_auth_type, tvb, offset, 1, type);
952
953         mod=tvb_get_guint8(tvb, offset+1);
954         proto_tree_add_uint(tree, hf_telnet_auth_mod_enc, tvb, offset+1, 1, mod);
955         proto_tree_add_boolean(tree, hf_telnet_auth_mod_cred_fwd, tvb, offset+1, 1, mod);
956         proto_tree_add_boolean(tree, hf_telnet_auth_mod_how, tvb, offset+1, 1, mod);
957         proto_tree_add_boolean(tree, hf_telnet_auth_mod_who, tvb, offset+1, 1, mod);
958 }
959
960 /* no kerberos blobs are ever >10kb ? (arbitrary limit) */
961 #define MAX_KRB5_BLOB_LEN       10240
962
963 static tvbuff_t *
964 unescape_and_tvbuffify_telnet_option(packet_info *pinfo, tvbuff_t *tvb, int offset, int len)
965 {
966         tvbuff_t *krb5_tvb;
967         guint8 *buf;
968         const guint8 *spos;
969         guint8 *dpos;
970         int skip, l;
971
972         if(len>=MAX_KRB5_BLOB_LEN)
973                 return NULL;
974
975         spos=tvb_get_ptr(tvb, offset, len);
976         buf=g_malloc(len);
977         dpos=buf;
978         skip=0;
979         l=len;
980         while(l>0){
981                 if((spos[0]==0xff) && (spos[1]==0xff)){
982                         skip++;
983                         l-=2;
984                         *(dpos++)=0xff;
985                         spos+=2;
986                         continue;
987                 }
988                 *(dpos++)=*(spos++);
989                 l--;
990         }
991         krb5_tvb = tvb_new_child_real_data(tvb, buf, len-skip, len-skip); 
992         tvb_set_free_cb(krb5_tvb, g_free);
993         add_new_data_source(pinfo, krb5_tvb, "Unpacked Telnet Option");
994
995         return krb5_tvb;
996 }
997
998
999 /* as per RFC2942 */
1000 static void
1001 dissect_krb5_authentication_data(packet_info *pinfo, tvbuff_t *tvb, int offset, int len, proto_tree *tree, guint8 acmd)
1002 {
1003         tvbuff_t *krb5_tvb;
1004         guint8 krb5_cmd;
1005
1006         dissect_authentication_type_pair(pinfo, tvb, offset, tree);
1007         offset+=2;
1008         len-=2;
1009
1010
1011         krb5_cmd=tvb_get_guint8(tvb, offset);
1012         proto_tree_add_uint(tree, hf_telnet_auth_krb5_type, tvb, offset, 1, krb5_cmd);
1013         offset++;
1014         len--;
1015
1016
1017         /* IAC SB AUTHENTICATION IS <authentication-type-pair> AUTH <Kerberos V5 KRB_AP_REQ message> IAC SE */
1018         if((acmd==TN_AC_IS)&&(krb5_cmd==TN_KRB5_TYPE_AUTH)){
1019                 if(len){
1020                         krb5_tvb=unescape_and_tvbuffify_telnet_option(pinfo, tvb, offset, len);
1021                         if(krb5_tvb)
1022                                 dissect_kerberos_main(krb5_tvb, pinfo, tree, FALSE, NULL);
1023                         else
1024                                 proto_tree_add_text(tree, tvb, offset, len, "Kerberos blob (too long to dissect - length %u > %u",
1025                                     len, MAX_KRB5_BLOB_LEN);
1026                 }
1027         }
1028
1029
1030
1031         /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> ACCEPT IAC SE */
1032         /* nothing more to dissect */
1033
1034
1035
1036         /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> REJECT <optional reason for rejection> IAC SE*/
1037 /*qqq*/
1038
1039
1040         /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> RESPONSE <KRB_AP_REP message> IAC SE */
1041         if((acmd==TN_AC_REPLY)&&(krb5_cmd==TN_KRB5_TYPE_RESPONSE)){
1042                 if(len){
1043                         krb5_tvb=unescape_and_tvbuffify_telnet_option(pinfo, tvb, offset, len);
1044                         dissect_kerberos_main(krb5_tvb, pinfo, tree, FALSE, NULL);
1045                 }
1046         }
1047
1048
1049         /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD <KRB_CRED message> IAC SE */
1050         /* XXX unclear what this one looks like */
1051
1052
1053         /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD_ACCEPT IAC SE */
1054         /* nothing more to dissect */
1055
1056
1057
1058         /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD_REJECT */
1059         /* nothing more to dissect */
1060 }
1061
1062 static void
1063 dissect_authentication_subopt(packet_info *pinfo, const char *optname _U_, tvbuff_t *tvb, int offset, int len, proto_tree *tree)
1064 {
1065         guint8 acmd;
1066         char *name;
1067
1068 /* XXX here we should really split it up in a conversation struct keeping
1069        track of what method we actually use and not just assume it is always
1070        kerberos v5
1071 */
1072         acmd=tvb_get_guint8(tvb, offset);
1073         proto_tree_add_uint(tree, hf_telnet_auth_cmd, tvb, offset, 1, acmd);
1074         offset++;
1075         len--;
1076
1077         switch(acmd){
1078         case TN_AC_REPLY:
1079         case TN_AC_IS:
1080                 /* XXX here we shouldnt just assume it is krb5 */
1081                 dissect_krb5_authentication_data(pinfo, tvb, offset, len, tree, acmd);
1082                 break;
1083         case TN_AC_SEND:
1084                 while(len>0){
1085                         dissect_authentication_type_pair(pinfo, tvb, offset, tree);
1086                         offset+=2;
1087                         len-=2;
1088                 }
1089                 break;
1090         case TN_AC_NAME:
1091                 if(len<255){
1092                         name=ep_alloc(256);
1093                         tvb_memcpy(tvb, (guint8*)name, offset, len);
1094                         name[len]=0;
1095                 } else {
1096                         name="<...name too long...>";
1097                 }
1098                 proto_tree_add_string(tree, hf_telnet_auth_name, tvb, offset, len, name);
1099                 break;
1100         }
1101 }
1102
1103 /* This function only uses the octet in the buffer at 'offset' */
1104 static void dissect_encryption_type(tvbuff_t *tvb, int offset, proto_tree *tree) {
1105         guint8 etype;
1106         etype = tvb_get_guint8(tvb, offset);
1107         proto_tree_add_uint(tree, hf_telnet_enc_type, tvb, offset, 1, etype);
1108 }
1109
1110 static void
1111 dissect_encryption_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset, int len, proto_tree *tree)
1112 {
1113         guint8 ecmd, key_first_octet;
1114
1115         ecmd = tvb_get_guint8(tvb, offset);
1116         proto_tree_add_uint(tree, hf_telnet_enc_cmd, tvb, offset, 1, ecmd);
1117         
1118         offset++;
1119         len--;
1120
1121         switch(ecmd) {
1122         case TN_ENC_IS:
1123         case TN_ENC_REPLY:
1124                 /* encryption type, type-specific data ... */
1125                 if (len > 0) {
1126                         dissect_encryption_type(tvb, offset, tree);
1127                         offset++;
1128                         len--;
1129                         proto_tree_add_text(tree, tvb, offset, len, "Type-specific data");
1130                 }
1131                 break;
1132
1133         case TN_ENC_SUPPORT:
1134                 /* list of encryption types ... */
1135                 while (len > 0) {
1136                         dissect_encryption_type(tvb, offset, tree);
1137                         offset++;
1138                         len--;
1139                 }
1140                 break;
1141                 
1142         case TN_ENC_START:
1143                 /* keyid ... */
1144                 if (len > 0) {
1145                         key_first_octet = tvb_get_guint8(tvb, offset);
1146                         proto_tree_add_text(tree, tvb, offset, len, (key_first_octet == 0) ? "Default key" : "Key ID");
1147                 }
1148                 break;
1149
1150         case TN_ENC_END:
1151                 /* no data */
1152                 break;
1153
1154         case TN_ENC_REQUEST_START:
1155                 /* (optional) keyid */
1156                 if (len > 0)
1157                         proto_tree_add_text(tree, tvb, offset, len, "Key ID (advisory)");
1158                 break;
1159
1160         case TN_ENC_REQUEST_END:
1161                 /* no data */
1162                 break;
1163                 
1164         case TN_ENC_ENC_KEYID:
1165         case TN_ENC_DEC_KEYID:
1166                 /* (optional) keyid - if not supplied, there are no more known keys */
1167                 if (len > 0)
1168                         proto_tree_add_text(tree, tvb, offset, len, "Key ID");
1169                 break;
1170
1171         default:
1172                 proto_tree_add_text(tree, tvb, offset, len, "Unknown command");
1173         }
1174 }
1175
1176 static tn_opt options[] = {
1177   {
1178     "Binary Transmission",                      /* RFC 856 */
1179     NULL,                                       /* no suboption negotiation */
1180     NO_LENGTH,
1181     0,
1182     NULL
1183   },
1184   {
1185     "Echo",                                     /* RFC 857 */
1186     NULL,                                       /* no suboption negotiation */
1187     NO_LENGTH,
1188     0,
1189     NULL
1190   },
1191   {    
1192     "Reconnection",                             /* DOD Protocol Handbook */
1193     NULL,
1194     NO_LENGTH,
1195     0,
1196     NULL
1197   },
1198   {
1199     "Suppress Go Ahead",                        /* RFC 858 */
1200     NULL,                                       /* no suboption negotiation */
1201     NO_LENGTH,
1202     0,
1203     NULL
1204   },
1205   {
1206     "Approx Message Size Negotiation",          /* Ethernet spec(!) */
1207     NULL,
1208     NO_LENGTH,
1209     0,
1210     NULL
1211   },
1212   {
1213     "Status",                                   /* RFC 859 */
1214     &ett_status_subopt,
1215     VARIABLE_LENGTH,
1216     1,
1217     NULL                                        /* XXX - fill me in */
1218   },
1219   {
1220     "Timing Mark",                              /* RFC 860 */
1221     NULL,                                       /* no suboption negotiation */
1222     NO_LENGTH,
1223     0,
1224     NULL
1225   },
1226   {
1227     "Remote Controlled Trans and Echo",         /* RFC 726 */
1228     &ett_rcte_subopt,
1229     VARIABLE_LENGTH,
1230     1,
1231     NULL                                        /* XXX - fill me in */
1232   },
1233   {
1234     "Output Line Width",                        /* DOD Protocol Handbook */
1235     &ett_olw_subopt,
1236     VARIABLE_LENGTH,                            /* XXX - fill me in */
1237     0,                                          /* XXX - fill me in */
1238     NULL                                        /* XXX - fill me in */
1239   },
1240   {
1241     "Output Page Size",                         /* DOD Protocol Handbook */
1242     &ett_ops_subopt,
1243     VARIABLE_LENGTH,                            /* XXX - fill me in */
1244     0,                                          /* XXX - fill me in */
1245     NULL                                        /* XXX - fill me in */
1246   },
1247   {
1248     "Output Carriage-Return Disposition",       /* RFC 652 */
1249     &ett_crdisp_subopt,
1250     FIXED_LENGTH,
1251     2,
1252     NULL                                        /* XXX - fill me in */
1253   },
1254   {
1255     "Output Horizontal Tab Stops",              /* RFC 653 */
1256     &ett_htstops_subopt,
1257     VARIABLE_LENGTH,
1258     1,
1259     dissect_htstops_subopt
1260   },
1261   {
1262     "Output Horizontal Tab Disposition",        /* RFC 654 */
1263     &ett_htdisp_subopt,
1264     FIXED_LENGTH,
1265     2,
1266     NULL                                        /* XXX - fill me in */
1267   },
1268   {
1269     "Output Formfeed Disposition",              /* RFC 655 */
1270     &ett_ffdisp_subopt,
1271     FIXED_LENGTH,
1272     2,
1273     NULL                                        /* XXX - fill me in */
1274   },
1275   {
1276     "Output Vertical Tabstops",                 /* RFC 656 */
1277     &ett_vtstops_subopt,
1278     VARIABLE_LENGTH,
1279     1,
1280     NULL                                        /* XXX - fill me in */
1281   },
1282   {
1283     "Output Vertical Tab Disposition",          /* RFC 657 */
1284     &ett_vtdisp_subopt,
1285     FIXED_LENGTH,
1286     2,
1287     NULL                                        /* XXX - fill me in */
1288   },
1289   {
1290     "Output Linefeed Disposition",              /* RFC 658 */
1291     &ett_lfdisp_subopt,
1292     FIXED_LENGTH,
1293     2,
1294     NULL                                        /* XXX - fill me in */
1295   },
1296   {
1297     "Extended ASCII",                           /* RFC 698 */
1298     &ett_extasc_subopt,
1299     FIXED_LENGTH,
1300     2,
1301     NULL                                        /* XXX - fill me in */
1302   },
1303   {
1304     "Logout",                                   /* RFC 727 */
1305     NULL,                                       /* no suboption negotiation */
1306     NO_LENGTH,
1307     0,
1308     NULL
1309   },
1310   {
1311     "Byte Macro",                               /* RFC 735 */
1312     &ett_bytemacro_subopt,
1313     VARIABLE_LENGTH,
1314     2,
1315     NULL                                        /* XXX - fill me in */
1316   },
1317   {
1318     "Data Entry Terminal",                      /* RFC 732, RFC 1043 */
1319     &ett_det_subopt,
1320     VARIABLE_LENGTH,
1321     2,
1322     NULL                                        /* XXX - fill me in */
1323   },
1324   {
1325     "SUPDUP",                                   /* RFC 734, RFC 736 */
1326     NULL,                                       /* no suboption negotiation */
1327     NO_LENGTH,
1328     0,
1329     NULL
1330   },
1331   {
1332     "SUPDUP Output",                            /* RFC 749 */
1333     &ett_supdupout_subopt,
1334     VARIABLE_LENGTH,
1335     1,
1336     NULL                                        /* XXX - fill me in */
1337   },
1338   {
1339     "Send Location",                            /* RFC 779 */
1340     &ett_sendloc_subopt,
1341     VARIABLE_LENGTH,
1342     0,
1343     NULL                                        /* XXX - fill me in */
1344   },
1345   {
1346     "Terminal Type",                            /* RFC 1091 */
1347     &ett_termtype_subopt,
1348     VARIABLE_LENGTH,
1349     1,
1350     dissect_string_subopt
1351   },
1352   {
1353     "End of Record",                            /* RFC 885 */
1354     NULL,                                       /* no suboption negotiation */
1355     NO_LENGTH,
1356     0,
1357     NULL
1358   },
1359   {
1360     "TACACS User Identification",               /* RFC 927 */
1361     &ett_tacacsui_subopt,
1362     FIXED_LENGTH,
1363     4,
1364     NULL                                        /* XXX - fill me in */
1365   },
1366   {
1367     "Output Marking",                           /* RFC 933 */
1368     &ett_outmark_subopt,
1369     VARIABLE_LENGTH,
1370     1,
1371     dissect_outmark_subopt,
1372   },
1373   {
1374     "Terminal Location Number",                 /* RFC 946 */
1375     &ett_tlocnum_subopt,
1376     VARIABLE_LENGTH,
1377     1,
1378     NULL                                        /* XXX - fill me in */
1379   },
1380   {
1381     "Telnet 3270 Regime",                       /* RFC 1041 */
1382     &ett_tn3270reg_subopt,
1383     VARIABLE_LENGTH,
1384     1,
1385     dissect_tn3270_regime_subopt
1386   },
1387   {
1388     "X.3 PAD",                                  /* RFC 1053 */
1389     &ett_x3pad_subopt,
1390     VARIABLE_LENGTH,
1391     1,
1392     NULL                                        /* XXX - fill me in */
1393   },
1394   {
1395     "Negotiate About Window Size",              /* RFC 1073, DW183 */
1396     &ett_naws_subopt,
1397     FIXED_LENGTH,
1398     4,
1399     dissect_naws_subopt
1400   },
1401   {
1402     "Terminal Speed",                           /* RFC 1079 */
1403     &ett_tspeed_subopt,
1404     VARIABLE_LENGTH,
1405     1,
1406     NULL                                        /* XXX - fill me in */
1407   },
1408   {
1409     "Remote Flow Control",                      /* RFC 1372 */
1410     &ett_rfc_subopt,
1411     FIXED_LENGTH,
1412     1,
1413     dissect_rfc_subopt
1414   },
1415   {
1416     "Linemode",                                 /* RFC 1184 */
1417     &ett_linemode_subopt,
1418     VARIABLE_LENGTH,
1419     1,
1420     NULL                                        /* XXX - fill me in */
1421   },
1422   {
1423     "X Display Location",                       /* RFC 1096 */
1424     &ett_xdpyloc_subopt,
1425     VARIABLE_LENGTH,
1426     1,
1427     dissect_string_subopt
1428   },
1429   {
1430     "Environment Option",                       /* RFC 1408, RFC 1571 */
1431     &ett_env_subopt,
1432     VARIABLE_LENGTH,
1433     1,
1434     NULL                                        /* XXX - fill me in */
1435   },
1436   {
1437     "Authentication Option",                    /* RFC 2941 */
1438     &ett_auth_subopt,
1439     VARIABLE_LENGTH,
1440     1,
1441     dissect_authentication_subopt               
1442   },
1443   {
1444     "Encryption Option",                        /* RFC 2946 */
1445     &ett_enc_subopt,
1446     VARIABLE_LENGTH,
1447     1,
1448     dissect_encryption_subopt
1449   },
1450   {
1451     "New Environment Option",                   /* RFC 1572 */
1452     &ett_newenv_subopt,
1453     VARIABLE_LENGTH,
1454     1,
1455     NULL                                        /* XXX - fill me in */
1456   },
1457   {
1458     "TN3270E",                                  /* RFC 1647 */
1459     &ett_tn3270e_subopt,
1460     VARIABLE_LENGTH,
1461     1,
1462     dissect_tn3270e_subopt
1463   },
1464   {
1465     "XAUTH",                                    /* XAUTH  */
1466     &ett_xauth_subopt,
1467     VARIABLE_LENGTH,
1468     1,
1469     NULL                                        /* XXX - fill me in */
1470   },
1471   {
1472     "CHARSET",                                  /* CHARSET  */
1473     &ett_charset_subopt,
1474     VARIABLE_LENGTH,
1475     1,
1476     NULL                                        /* XXX - fill me in */
1477   },
1478   {
1479     "Remote Serial Port",                               /* Remote Serial Port */
1480     &ett_rsp_subopt,
1481     VARIABLE_LENGTH,
1482     1,
1483     NULL                                        /* XXX - fill me in */
1484   },
1485   {
1486     "COM Port Control",                                 /* RFC 2217 */
1487     &ett_comport_subopt,
1488     VARIABLE_LENGTH,
1489     1,
1490     dissect_comport_subopt
1491   }
1492   
1493 };
1494
1495 #define NOPTIONS        (sizeof options / sizeof options[0])
1496
1497 static int
1498 telnet_sub_option(packet_info *pinfo, proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
1499 {
1500   proto_tree *ti, *option_tree;
1501   int offset = start_offset;
1502   guint8 opt_byte;
1503   int subneg_len;
1504   const char *opt;
1505   gint ett = ett_telnet_subopt;
1506   int iac_offset;
1507   guint len;
1508   tvbuff_t * unescaped_tvb; 
1509   void (*dissect)(packet_info *, const char *, tvbuff_t *, int, int, proto_tree *);
1510   gint cur_offset;
1511   gboolean iac_found;
1512
1513   /*
1514    * As data with value iac (0xff) is possible, this value must be escaped 
1515    * with iac (rfc 854). 
1516    */
1517   int  iac_data = 0;
1518
1519   offset += 2;  /* skip IAC and SB */
1520
1521   /* Get the option code */
1522   opt_byte = tvb_get_guint8(tvb, offset);
1523   if (opt_byte >= NOPTIONS) {
1524     opt = "<unknown option>";
1525     dissect = NULL;
1526   } else {
1527     opt = options[opt_byte].name;
1528     if (options[opt_byte].subtree_index != NULL)
1529       ett = *(options[opt_byte].subtree_index);
1530     dissect = options[opt_byte].dissect;
1531   }
1532   offset++;
1533
1534   /* Search for an unescaped IAC. */
1535   cur_offset = offset;
1536   iac_found = FALSE;
1537   len = tvb_length_remaining(tvb, offset);
1538   do {
1539       iac_offset = tvb_find_guint8(tvb, cur_offset, len, TN_IAC);
1540       iac_found = TRUE;
1541       if (iac_offset == -1) {
1542         /* None found - run to the end of the packet. */
1543         offset += len;
1544       } else {
1545         if (((guint)(iac_offset + 1) >= len) ||
1546              (tvb_get_guint8(tvb, iac_offset + 1) != TN_IAC)) {
1547             /* We really found a single IAC, so we're done */
1548             offset = iac_offset;
1549         } else {
1550             /*
1551              * We saw an escaped IAC, so we have to move ahead to the
1552              * next section
1553              */
1554             iac_found = FALSE;
1555             cur_offset = iac_offset + 2;
1556             iac_data += 1;
1557         }
1558       }
1559
1560   } while (!iac_found);
1561   
1562   subneg_len = offset - start_offset;
1563
1564   ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
1565                            "Suboption Begin: %s", opt);
1566   option_tree = proto_item_add_subtree(ti, ett);
1567   start_offset += 3;    /* skip IAC, SB, and option code */
1568   subneg_len -= 3;
1569
1570   if (subneg_len > 0) {
1571
1572     /* Now dissect the suboption parameters. */
1573     if (dissect != NULL) {
1574
1575       switch (options[opt_byte].len_type) {
1576
1577       case NO_LENGTH:
1578         /* There isn't supposed to *be* sub-option negotiation for this. */
1579         proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1580                             "Bogus suboption data");
1581         return offset;
1582
1583       case FIXED_LENGTH:
1584         /* Make sure the length is what it's supposed to be. */
1585         if (subneg_len - iac_data != options[opt_byte].optlen) {
1586           proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1587                             "Suboption parameter length is %d, should be %d",
1588                             subneg_len, options[opt_byte].optlen);
1589           return offset;
1590         }
1591         break;
1592
1593       case VARIABLE_LENGTH:
1594         /* Make sure the length is greater than the minimum. */
1595         if (subneg_len - iac_data < options[opt_byte].optlen) {
1596           proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1597                               "Suboption parameter length is %d, should be at least %d",
1598                               subneg_len, options[opt_byte].optlen);
1599           return offset;
1600         }
1601         break;
1602       }
1603
1604       /* We have a dissector for this suboption's parameters; call it. */
1605       if (iac_data > 0) { 
1606         /* Data is escaped, we have to unescape it. */
1607         unescaped_tvb = unescape_and_tvbuffify_telnet_option(pinfo, tvb, start_offset, subneg_len);
1608         (*dissect)(pinfo, opt, unescaped_tvb, 0, subneg_len - iac_data, option_tree);
1609       } else {
1610         (*dissect)(pinfo, opt, tvb, start_offset, subneg_len, option_tree);
1611       }
1612     } else {
1613       /* We don't have a dissector for them; just show them as data. */
1614       if (iac_data > 0) { 
1615         /* Data is escaped, we have to unescape it. */
1616         unescaped_tvb = unescape_and_tvbuffify_telnet_option(pinfo, tvb, start_offset, subneg_len);
1617         proto_tree_add_text(option_tree, unescaped_tvb, 0, subneg_len - iac_data,
1618                           "Option data");
1619       } else {
1620         proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1621                           "Option data");
1622       }
1623     }
1624   }
1625   return offset;
1626 }
1627
1628 static int
1629 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
1630                         int start_offset, const char *type)
1631 {
1632   int offset = start_offset;
1633   guint8 opt_byte;
1634   const char *opt;
1635
1636   offset += 2;  /* skip IAC and WILL,WONT,DO,DONT} */
1637   opt_byte = tvb_get_guint8(tvb, offset);
1638   if (opt_byte >= NOPTIONS)
1639     opt = "<unknown option>";
1640   else
1641     opt = options[opt_byte].name;
1642   offset++;
1643
1644   proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
1645                         "Command: %s %s", type, opt);
1646   return offset;
1647 }
1648
1649 static int
1650 telnet_command(packet_info *pinfo, proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
1651 {
1652   int offset = start_offset;
1653   guchar optcode;
1654
1655   offset += 1;  /* skip IAC */
1656   optcode = tvb_get_guint8(tvb, offset);
1657   offset++;
1658   switch(optcode) {
1659
1660   case TN_EOF:
1661     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1662                         "Command: End of File");
1663     break;
1664
1665   case TN_SUSP:
1666     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1667                         "Command: Suspend Current Process");
1668     break;
1669
1670   case TN_ABORT:
1671     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1672                         "Command: Abort Process");
1673     break;
1674
1675   case TN_EOR:
1676     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1677                         "Command: End of Record");
1678     break;
1679
1680   case TN_SE:
1681     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1682                         "Command: Suboption End");
1683     break;
1684
1685   case TN_NOP:
1686     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1687                         "Command: No Operation");
1688     break;
1689
1690   case TN_DM:
1691     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1692                         "Command: Data Mark");
1693     break;
1694
1695   case TN_BRK:
1696     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1697                         "Command: Break");
1698     break;
1699
1700   case TN_IP:
1701     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1702                         "Command: Interrupt Process");
1703     break;
1704
1705   case TN_AO:
1706     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1707                         "Command: Abort Output");
1708     break;
1709
1710   case TN_AYT:
1711     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1712                         "Command: Are You There?");
1713     break;
1714
1715   case TN_EC:
1716     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1717                         "Command: Escape Character");
1718     break;
1719
1720   case TN_EL:
1721     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1722                         "Command: Erase Line");
1723     break;
1724
1725   case TN_GA:
1726     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1727                         "Command: Go Ahead");
1728     break;
1729
1730   case TN_SB:
1731     offset = telnet_sub_option(pinfo, telnet_tree, tvb, start_offset);
1732     break;
1733
1734   case TN_WILL:
1735     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1736                                         "Will");
1737     break;
1738
1739   case TN_WONT:
1740     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1741                                         "Won't");
1742     break;
1743
1744   case TN_DO:
1745     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1746                                         "Do");
1747     break;
1748
1749   case TN_DONT:
1750     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1751                                         "Don't");
1752     break;
1753
1754   default:
1755     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1756                         "Command: Unknown (0x%02x)", optcode);
1757     break;
1758   }
1759
1760   return offset;
1761 }
1762
1763 static void
1764 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
1765 {
1766   gint next_offset;
1767   int linelen;
1768   guint8 c;
1769   gboolean last_char_was_cr;
1770
1771   while (len != 0 && tvb_offset_exists(tvb, offset)) {
1772     /*
1773      * Find the end of the line.
1774      */
1775     linelen = tvb_find_line_end(tvb, offset, len, &next_offset, FALSE);
1776     len -= next_offset - offset;        /* subtract out the line's characters */
1777
1778     /*
1779      * In Telnet, CR NUL is the way you send a CR by itself in the
1780      * default ASCII mode; don't treat CR by itself as a line ending,
1781      * treat only CR NUL, CR LF, or LF by itself as a line ending.
1782      */
1783     if (next_offset == offset + linelen + 1 && len >= 1) {
1784       /*
1785        * Well, we saw a one-character line ending, so either it's a CR
1786        * or an LF; we have at least two characters left, including the
1787        * CR.
1788        *
1789        * If the line ending is a CR, skip all subsequent CRs; at
1790        * least one capture appeared to have multiple CRs at the end of
1791        * a line.
1792        */
1793       if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
1794         last_char_was_cr = TRUE;
1795         while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
1796           c = tvb_get_guint8(tvb, next_offset);
1797           next_offset++;        /* skip over that character */
1798           len--;
1799           if (c == '\n' || (c == '\0' && last_char_was_cr)) {
1800             /*
1801              * LF is a line ending, whether preceded by CR or not.
1802              * NUL is a line ending if preceded by CR.
1803              */
1804             break;
1805           }
1806           last_char_was_cr = (c == '\r');
1807         }
1808       }
1809     }
1810
1811     /*
1812      * Now compute the length of the line *including* the end-of-line
1813      * indication, if any; we display it all.
1814      */
1815     linelen = next_offset - offset;
1816
1817     proto_tree_add_text(tree, tvb, offset, linelen,
1818                         "Data: %s",
1819                         tvb_format_text(tvb, offset, linelen));
1820     offset = next_offset;
1821   }
1822 }
1823
1824 static int find_unescaped_iac(tvbuff_t *tvb, int offset, int len)
1825 {
1826     int iac_offset = offset;
1827
1828     /* If we find an IAC (0XFF), make sure it is not followed by another 0XFF.
1829        Such cases indicate that it is not an IAC at all */
1830     while ((iac_offset = tvb_find_guint8(tvb, iac_offset, len, TN_IAC)) != -1 &&
1831            (tvb_get_guint8(tvb, iac_offset + 1) == TN_IAC))
1832     {
1833         iac_offset+=2;
1834         len = tvb_length_remaining(tvb, iac_offset);
1835     }
1836     return iac_offset;
1837 }
1838
1839 static void
1840 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1841 {
1842         proto_tree      *telnet_tree, *ti;
1843         tvbuff_t *next_tvb;
1844         gint offset = 0;
1845         guint len = 0;
1846         guint is_tn3270 = 0;
1847
1848         col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
1849
1850         col_set_str(pinfo->cinfo, COL_INFO, "Telnet Data ...");
1851
1852         is_tn3270 = find_tn3270_conversation(pinfo);
1853
1854         if (tree) {
1855           int data_len;
1856           gint iac_offset;
1857
1858           ti = proto_tree_add_item(tree, proto_telnet, tvb, offset, -1, FALSE);
1859           telnet_tree = proto_item_add_subtree(ti, ett_telnet);
1860
1861           /*
1862            * Scan through the buffer looking for an IAC byte.
1863            */
1864           while ((len = tvb_length_remaining(tvb, offset)) > 0) {
1865             iac_offset = find_unescaped_iac(tvb, offset, len);
1866             if (iac_offset != -1) {
1867               /*
1868                * We found an IAC byte.
1869                * If there's any data before it, add that data to the
1870                * tree, a line at a time.
1871                */
1872               data_len = iac_offset - offset;
1873               if (data_len > 0) {
1874                 if (is_tn3270) {
1875                   next_tvb = tvb_new_subset(tvb, offset, data_len, data_len);
1876                   call_dissector(tn3270_handle, next_tvb, pinfo, telnet_tree);
1877                 } else
1878                   telnet_add_text(telnet_tree, tvb, offset, data_len);
1879               }
1880               /*
1881                * Now interpret the command.
1882                */
1883               offset = telnet_command(pinfo, telnet_tree, tvb, iac_offset);
1884             } else {
1885               /* get more data if tn3270 */
1886               if (is_tn3270) {
1887                 pinfo->desegment_offset = offset;
1888                 pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
1889                 return;
1890               }
1891               /*
1892                * We found no IAC byte, so what remains in the buffer
1893                * is the last of the data in the packet.
1894                * Add it to the tree, a line at a time, and then quit.
1895                */
1896               telnet_add_text(telnet_tree, tvb, offset, len);
1897               break;
1898             }
1899           }
1900         }
1901 }
1902
1903 void
1904 proto_register_telnet(void)
1905 {
1906         static hf_register_info hf[] = {  
1907         { &hf_telnet_auth_name,
1908                 { "Name", "telnet.auth.name", FT_STRING, BASE_NONE,
1909                   NULL, 0, "Name of user being authenticated", HFILL }},
1910         { &hf_telnet_auth_cmd,
1911                 { "Auth Cmd", "telnet.auth.cmd", FT_UINT8, BASE_DEC,
1912                   VALS(auth_cmd_vals), 0, "Authentication Command", HFILL }},
1913         { &hf_telnet_auth_type,
1914                 { "Auth Type", "telnet.auth.type", FT_UINT8, BASE_DEC,
1915                   VALS(auth_type_vals), 0, "Authentication Type", HFILL }},
1916         { &hf_telnet_auth_mod_cred_fwd,
1917                 { "Cred Fwd", "telnet.auth.mod.cred_fwd", FT_BOOLEAN, 8,
1918                   TFS(&auth_mod_cred_fwd), 0x08, "Modifier: Whether client will forward creds or not", HFILL }},
1919         { &hf_telnet_auth_mod_who,
1920                 { "Who", "telnet.auth.mod.who", FT_BOOLEAN, 8,
1921                   TFS(&auth_mod_who), 0x01, "Modifier: Who to mask", HFILL }},
1922         { &hf_telnet_auth_mod_how,
1923                 { "How", "telnet.auth.mod.how", FT_BOOLEAN, 8,
1924                   TFS(&auth_mod_how), 0x02, "Modifier: How to mask", HFILL }},
1925         { &hf_telnet_auth_mod_enc,
1926                 { "Encrypt", "telnet.auth.mod.enc", FT_UINT8, BASE_DEC,
1927                   VALS(auth_mod_enc), 0x14, "Modifier: How to enable Encryption", HFILL }},
1928         { &hf_telnet_auth_krb5_type,
1929                 { "Command", "telnet.auth.krb5.cmd", FT_UINT8, BASE_DEC,
1930                   VALS(auth_krb5_types), 0, "Krb5 Authentication sub-command", HFILL }},
1931         { &hf_telnet_enc_cmd,
1932                 { "Enc Cmd", "telnet.enc.cmd", FT_UINT8, BASE_DEC,
1933                   VALS(enc_cmd_vals), 0, "Encryption command", HFILL }},
1934         { &hf_telnet_enc_type,
1935                 { "Enc Type", "telnet.enc.type", FT_UINT8, BASE_DEC,
1936                   VALS(enc_type_vals), 0, "Encryption type", HFILL }},
1937         };
1938         static gint *ett[] = {
1939                 &ett_telnet,
1940                 &ett_telnet_subopt,
1941                 &ett_status_subopt,
1942                 &ett_rcte_subopt,
1943                 &ett_olw_subopt,
1944                 &ett_ops_subopt,
1945                 &ett_crdisp_subopt,
1946                 &ett_htstops_subopt,
1947                 &ett_htdisp_subopt,
1948                 &ett_ffdisp_subopt,
1949                 &ett_vtstops_subopt,
1950                 &ett_vtdisp_subopt,
1951                 &ett_lfdisp_subopt,
1952                 &ett_extasc_subopt,
1953                 &ett_bytemacro_subopt,
1954                 &ett_det_subopt,
1955                 &ett_supdupout_subopt,
1956                 &ett_sendloc_subopt,
1957                 &ett_termtype_subopt,
1958                 &ett_tacacsui_subopt,
1959                 &ett_outmark_subopt,
1960                 &ett_tlocnum_subopt,
1961                 &ett_tn3270reg_subopt,
1962                 &ett_x3pad_subopt,
1963                 &ett_naws_subopt,
1964                 &ett_tspeed_subopt,
1965                 &ett_rfc_subopt,
1966                 &ett_linemode_subopt,
1967                 &ett_xdpyloc_subopt,
1968                 &ett_env_subopt,
1969                 &ett_auth_subopt,
1970                 &ett_enc_subopt,
1971                 &ett_newenv_subopt,
1972                 &ett_tn3270e_subopt,
1973                 &ett_xauth_subopt,
1974                 &ett_charset_subopt,
1975                 &ett_rsp_subopt,
1976                 &ett_comport_subopt
1977         };
1978
1979         proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
1980         proto_register_field_array(proto_telnet, hf, array_length(hf));
1981         proto_register_subtree_array(ett, array_length(ett));
1982 }
1983
1984 void
1985 proto_reg_handoff_telnet(void)
1986 {
1987         dissector_handle_t telnet_handle;
1988
1989         telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
1990         dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);
1991         tn3270_handle = find_dissector("tn3270");
1992 }