added some options and enhancements to the print output:
[obnox/wireshark/wip.git] / 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: packet-telnet.c,v 1.45 2004/02/25 09:31:07 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
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 "packet-kerberos.h"
41
42 static int proto_telnet = -1;
43 static int hf_telnet_auth_cmd = -1;
44 static int hf_telnet_auth_name = -1;
45 static int hf_telnet_auth_type = -1;
46 static int hf_telnet_auth_mod_who = -1;
47 static int hf_telnet_auth_mod_how = -1;
48 static int hf_telnet_auth_mod_cred_fwd = -1;
49 static int hf_telnet_auth_mod_enc = -1;
50 static int hf_telnet_auth_krb5_type = -1;
51
52 static gint ett_telnet = -1;
53 static gint ett_telnet_subopt = -1;
54 static gint ett_status_subopt = -1;
55 static gint ett_rcte_subopt = -1;
56 static gint ett_olw_subopt = -1;
57 static gint ett_ops_subopt = -1;
58 static gint ett_crdisp_subopt = -1;
59 static gint ett_htstops_subopt = -1;
60 static gint ett_htdisp_subopt = -1;
61 static gint ett_ffdisp_subopt = -1;
62 static gint ett_vtstops_subopt = -1;
63 static gint ett_vtdisp_subopt = -1;
64 static gint ett_lfdisp_subopt = -1;
65 static gint ett_extasc_subopt = -1;
66 static gint ett_bytemacro_subopt = -1;
67 static gint ett_det_subopt = -1;
68 static gint ett_supdupout_subopt = -1;
69 static gint ett_sendloc_subopt = -1;
70 static gint ett_termtype_subopt = -1;
71 static gint ett_tacacsui_subopt = -1;
72 static gint ett_outmark_subopt = -1;
73 static gint ett_tlocnum_subopt = -1;
74 static gint ett_tn3270reg_subopt = -1;
75 static gint ett_x3pad_subopt = -1;
76 static gint ett_naws_subopt = -1;
77 static gint ett_tspeed_subopt = -1;
78 static gint ett_rfc_subopt = -1;
79 static gint ett_linemode_subopt = -1;
80 static gint ett_xdpyloc_subopt = -1;
81 static gint ett_env_subopt = -1;
82 static gint ett_auth_subopt = -1;
83 static gint ett_enc_subopt = -1;
84 static gint ett_newenv_subopt = -1;
85 static gint ett_tn3270e_subopt = -1;
86 static gint ett_xauth_subopt = -1;
87 static gint ett_charset_subopt = -1;
88 static gint ett_rsp_subopt = -1;
89 static gint ett_comport_subopt = -1;
90
91
92 /* Some defines for Telnet */
93
94 #define TCP_PORT_TELNET                 23
95
96 #define TN_IAC   255
97 #define TN_DONT  254
98 #define TN_DO    253
99 #define TN_WONT  252
100 #define TN_WILL  251
101 #define TN_SB    250
102 #define TN_GA    249
103 #define TN_EL    248
104 #define TN_EC    247
105 #define TN_AYT   246
106 #define TN_AO    245
107 #define TN_IP    244
108 #define TN_BRK   243
109 #define TN_DM    242
110 #define TN_NOP   241
111 #define TN_SE    240
112 #define TN_EOR   239
113 #define TN_ABORT 238
114 #define TN_SUSP  237
115 #define TN_EOF   236
116
117
118 typedef enum {
119   NO_LENGTH,            /* option has no data, hence no length */
120   FIXED_LENGTH,         /* option always has the same length */
121   VARIABLE_LENGTH       /* option is variable-length - optlen is minimum */
122 } tn_opt_len_type;
123
124 /* Member of table of IP or TCP options. */
125 typedef struct tn_opt {
126   char  *name;                  /* name of option */
127   gint  *subtree_index;         /* pointer to subtree index for option */
128   tn_opt_len_type len_type;     /* type of option length field */
129   int   optlen;                 /* value length should be (minimum if VARIABLE) */
130   void  (*dissect)(packet_info *pinfo, const char *, tvbuff_t *, int, int, proto_tree *);
131                                 /* routine to dissect option */
132 } tn_opt;
133
134 static void
135 dissect_string_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
136                       proto_tree *tree)
137 {
138   guint8 cmd;
139
140   cmd = tvb_get_guint8(tvb, offset);
141   switch (cmd) {
142
143   case 0:       /* IS */
144     proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
145     offset++;
146     len--;
147     if (len > 0) {
148       proto_tree_add_text(tree, tvb, offset, len, "Value: %s",
149                           tvb_format_text(tvb, offset, len));
150     }
151     break;
152
153   case 1:       /* SEND */
154     proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
155     offset++;
156     len--;
157     if (len > 0)
158       proto_tree_add_text(tree, tvb, offset, len, "Extra data");
159     break;
160
161   default:
162     proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
163                         optname, cmd);
164     offset++;
165     len--;
166     if (len > 0)
167       proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
168     break;
169   }
170 }
171
172 static void
173 dissect_outmark_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
174                        int len, proto_tree *tree)
175 {
176   guint8 cmd;
177   int gs_offset, datalen;
178
179   while (len > 0) {
180     cmd = tvb_get_guint8(tvb, offset);
181     switch (cmd) {
182
183     case 6:     /* ACK */
184       proto_tree_add_text(tree, tvb, offset, 1, "ACK");
185       break;
186
187     case 21:    /* NAK */
188       proto_tree_add_text(tree, tvb, offset, 1, "NAK");
189       break;
190
191     case 'D':
192       proto_tree_add_text(tree, tvb, offset, 1, "Default");
193       break;
194
195     case 'T':
196       proto_tree_add_text(tree, tvb, offset, 1, "Top");
197       break;
198
199     case 'B':
200       proto_tree_add_text(tree, tvb, offset, 1, "Bottom");
201       break;
202
203     case 'L':
204       proto_tree_add_text(tree, tvb, offset, 1, "Left");
205       break;
206
207     case 'R':
208       proto_tree_add_text(tree, tvb, offset, 1, "Right");
209       break;
210
211     default:
212       proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", cmd);
213       break;
214     }
215     offset++;
216     len--;
217
218     /* Look for a GS */
219     gs_offset = tvb_find_guint8(tvb, offset, len, 29);
220     if (gs_offset == -1) {
221       /* None found - run to the end of the packet. */
222       gs_offset = offset + len;
223     }
224     datalen = gs_offset - offset;
225     if (datalen > 0) {
226       proto_tree_add_text(tree, tvb, offset, datalen, "Banner: %s",
227                           tvb_format_text(tvb, offset, datalen));
228       offset += datalen;
229       len -= datalen;
230     }
231   }
232 }
233
234 static void
235 dissect_htstops_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
236                        proto_tree *tree)
237 {
238   guint8 cmd;
239   guint8 tabval;
240
241   cmd = tvb_get_guint8(tvb, offset);
242   switch (cmd) {
243
244   case 0:       /* IS */
245     proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
246     offset++;
247     len--;
248     break;
249
250   case 1:       /* SEND */
251     proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
252     offset++;
253     len--;
254     break;
255
256   default:
257     proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
258                         optname, cmd);
259     offset++;
260     len--;
261     if (len > 0)
262       proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
263     return;
264   }
265
266   while (len > 0) {
267     tabval = tvb_get_guint8(tvb, offset);
268     switch (tabval) {
269
270     case 0:
271       proto_tree_add_text(tree, tvb, offset, 1,
272                           "Sender wants to handle tab stops");
273       break;
274
275     default:
276       proto_tree_add_text(tree, tvb, offset, 1,
277                           "Sender wants receiver to handle tab stop at %u",
278                           tabval);
279       break;
280
281     case 251:
282     case 252:
283     case 253:
284     case 254:
285       proto_tree_add_text(tree, tvb, offset, 1,
286                           "Invalid value: %u", tabval);
287       break;
288
289     case 255:
290       proto_tree_add_text(tree, tvb, offset, 1,
291                           "Sender wants receiver to handle tab stops");
292       break;
293     }
294     offset++;
295     len--;
296   }
297 }
298
299 static void
300 dissect_naws_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
301                     int len _U_, proto_tree *tree)
302 {
303   proto_tree_add_text(tree, tvb, offset, 2, "Width: %u",
304                       tvb_get_ntohs(tvb, offset));
305   offset += 2;
306   proto_tree_add_text(tree, tvb, offset, 2, "Height: %u",
307                       tvb_get_ntohs(tvb, offset));
308 }
309
310 /* BEGIN RFC-2217 (COM Port Control) Definitions */
311
312 #define TNCOMPORT_SIGNATURE             0
313 #define TNCOMPORT_SETBAUDRATE           1
314 #define TNCOMPORT_SETDATASIZE           2
315 #define TNCOMPORT_SETPARITY             3
316 #define TNCOMPORT_SETSTOPSIZE           4
317 #define TNCOMPORT_SETCONTROL            5
318 #define TNCOMPORT_NOTIFYLINESTATE       6
319 #define TNCOMPORT_NOTIFYMODEMSTATE      7
320 #define TNCOMPORT_FLOWCONTROLSUSPEND    8
321 #define TNCOMPORT_FLOWCONTROLRESUME      9
322 #define TNCOMPORT_SETLINESTATEMASK      10
323 #define TNCOMPORT_SETMODEMSTATEMASK     11
324 #define TNCOMPORT_PURGEDATA             12
325
326 /* END RFC-2217 (COM Port Control) Definitions */
327
328 static void
329 dissect_comport_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
330                        proto_tree *tree)
331 {static const char *datasizes[] = {
332     "Request",
333     "<invalid>",
334     "<invalid>",
335     "<invalid>",
336     "<invalid>",
337     "5",
338     "6",
339     "7",
340     "8"
341  };
342  static const char *parities[] = {
343     "Request",
344     "None",
345     "Odd",
346     "Even",
347     "Mark",
348     "Space"
349  };
350  static const char *stops[] = {
351     "Request",
352     "1",
353     "2",
354     "1.5"
355  };
356  static const char *control[] = {
357     "Output Flow Control Request",
358     "Output Flow: None",
359     "Output Flow: XON/XOFF",
360     "Output Flow: CTS/RTS",
361     "Break Request",
362     "Break: ON",
363     "Break: OFF",
364     "DTR Request",
365     "DTR: ON",
366     "DTR: OFF",
367     "RTS Request",
368     "RTS: ON",
369     "RTS: OFF",
370     "Input Flow Control Request",
371     "Input Flow: None",
372     "Input Flow: XON/XOFF",
373     "Input Flow: CTS/RTS",
374     "Output Flow: DCD",
375     "Input Flow: DTR",
376     "Output Flow: DSR"
377  };
378  static const char *linestate_bits[] = {
379     "Data Ready",
380     "Overrun Error",
381     "Parity Error",
382     "Framing Error",
383     "Break Detected",
384     "Transfer Holding Register Empty",
385     "Transfer Shift Register Empty",
386     "Timeout Error"
387  };
388  static const char *modemstate_bits[] = {
389      "DCTS",
390      "DDSR",
391      "TERI",
392      "DDCD",
393      "CTS",
394      "DSR",
395      "RI",
396      "DCD"
397  };
398  static const char *purges[] = {
399      "Purge None",
400      "Purge RX",
401      "Purge TX",
402      "Purge RX/TX"
403  };
404   
405   guint8 cmd;
406   guint8 isservercmd;
407   char *source;
408   
409   cmd = tvb_get_guint8(tvb, offset);
410   isservercmd = cmd > 99;
411   cmd = (isservercmd) ? (cmd - 100) : cmd;
412   source = (isservercmd) ? "Server" : "Client";
413   switch (cmd) {
414
415   case TNCOMPORT_SIGNATURE:
416     len--;
417     if (len == 0) {
418         proto_tree_add_text(tree, tvb, offset, 1, "%s Requests Signature",source);
419     } else {
420         guint8 *sig = tvb_get_string(tvb, offset + 1, len);
421         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s Signature: %s",source, sig);
422         g_free(sig);
423     }
424     break;
425
426   case TNCOMPORT_SETBAUDRATE:
427     len--;
428     if (len >= 4) {
429         guint32 baud = tvb_get_ntohl(tvb, offset+1);
430         if (baud == 0) {
431             proto_tree_add_text(tree, tvb, offset, 5, "%s Requests Baud Rate",source);            
432         } else {
433             proto_tree_add_text(tree, tvb, offset, 5, "%s Baud Rate: %d",source,baud);
434         }
435     } else {
436         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Baud Rate Packet>",source);
437     }
438     break;
439     
440   case TNCOMPORT_SETDATASIZE:
441     len--;
442     if (len >= 1) {
443         guint8 datasize = tvb_get_guint8(tvb, offset+1);
444         const char *ds = (datasize > 8) ? "<invalid>" : datasizes[datasize];
445         proto_tree_add_text(tree, tvb, offset, 2, "%s Data Size: %s",source,ds);
446     } else {
447         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Data Size Packet>",source);
448     }
449     break;
450
451   case TNCOMPORT_SETPARITY:
452     len--;
453     if (len >= 1) {
454         guint8 parity = tvb_get_guint8(tvb, offset+1);
455         const char *pr = (parity > 5) ? "<invalid>" : parities[parity];
456         proto_tree_add_text(tree, tvb, offset, 2, "%s Parity: %s",source,pr);
457     } else {
458         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Parity Packet>",source);
459     }
460     break;
461     
462   case TNCOMPORT_SETSTOPSIZE:
463     len--;
464     if (len >= 1) {
465         guint8 stop = tvb_get_guint8(tvb, offset+1);
466         const char *st = (stop > 3) ? "<invalid>" : stops[stop];
467         proto_tree_add_text(tree, tvb, offset, 2, "%s Stop: %s",source,st);
468     } else {
469         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Stop Packet>",source);
470     }
471     break;
472
473   case TNCOMPORT_SETCONTROL:
474     len--;
475     if (len >= 1) {
476         guint8 crt = tvb_get_guint8(tvb, offset+1);
477         const char *c = (crt > 19) ? "Control: <invalid>" : control[crt];
478         proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,c);
479     } else {
480         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Control Packet>",source);
481     }
482     break;
483     
484   case TNCOMPORT_SETLINESTATEMASK:
485   case TNCOMPORT_NOTIFYLINESTATE:
486     len--;
487     if (len >= 1) {
488         const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
489                                         "%s Set Linestate Mask: %s" : "%s Linestate: %s";
490         char ls_buffer[512];
491         guint8 ls = tvb_get_guint8(tvb, offset+1);
492         int print_count = 0;
493         int idx;
494         ls_buffer[0] = '\0';
495         for (idx = 0; idx < 8; idx++) {
496             int bit = ls & 1;
497             if (bit) {
498                 if (print_count != 0) {
499                     strcat(ls_buffer,", ");
500                 }
501                 strcat(ls_buffer,linestate_bits[idx]);
502                 print_count++;
503             }
504             ls = ls >> 1;
505         }
506         proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ls_buffer);
507     } else {
508         const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
509                                         "%s <Invalid Linestate Mask>" : "%s <Invalid Linestate Packet>";
510         proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
511     }
512     break;
513     
514   case TNCOMPORT_SETMODEMSTATEMASK:
515   case TNCOMPORT_NOTIFYMODEMSTATE:
516     len--;
517     if (len >= 1) {
518         const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
519                                         "%s Set Modemstate Mask: %s" : "%s Modemstate: %s";
520         char ms_buffer[256];
521             guint8 ms = tvb_get_guint8(tvb, offset+1);
522         int print_count = 0;
523         int idx;
524         ms_buffer[0] = '\0';
525         for (idx = 0; idx < 8; idx++) {
526             int bit = ms & 1;
527             if (bit) {
528                 if (print_count != 0) {
529                     strcat(ms_buffer,", ");
530                 }
531                 strcat(ms_buffer,modemstate_bits[idx]);
532                 print_count++;
533             }
534             ms = ms >> 1;
535         }
536         proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ms_buffer);
537     } else {
538         const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
539                                          "%s <Invalid Modemstate Mask>" : "%s <Invalid Modemstate Packet>";
540         proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
541     }
542     break;
543
544   case TNCOMPORT_FLOWCONTROLSUSPEND:
545     len--;
546     proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Suspend",source);
547     break;
548   
549   case TNCOMPORT_FLOWCONTROLRESUME:
550     len--;
551     proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Resume",source);
552     break;
553   
554   case TNCOMPORT_PURGEDATA:
555     len--;
556     if (len >= 1) {
557             guint8 purge = tvb_get_guint8(tvb, offset+1);
558         const char *p = (purge > 3) ? "<Purge invalid>" : purges[purge];
559         proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,p);
560     } else {
561         proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Purge Packet>",source);
562     }
563     break;
564   
565   default:
566     proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
567                         optname, cmd);
568     offset++;
569     len--;
570     if (len > 0)
571       proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
572     return;
573   }
574
575 }
576
577 static const value_string rfc_opt_vals[] = {
578         { 0, "OFF" },
579         { 1, "ON" },
580         { 2, "RESTART-ANY" },
581         { 3, "RESTART-XON" },
582         { 0, NULL }
583 };
584
585 static void
586 dissect_rfc_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
587                    int len _U_, proto_tree *tree)
588 {
589   guint8 cmd;
590
591   cmd = tvb_get_guint8(tvb, offset);
592   proto_tree_add_text(tree, tvb, offset, 2, "%s",
593                       val_to_str(cmd, rfc_opt_vals, "Unknown (%u)"));
594 }
595
596
597 #define TN_AC_IS        0
598 #define TN_AC_SEND      1
599 #define TN_AC_REPLY     2
600 #define TN_AC_NAME      3
601 static const value_string auth_cmd_vals[] = {
602         { TN_AC_IS,     "IS" },
603         { TN_AC_SEND,   "SEND" },
604         { TN_AC_REPLY,  "REPLY" },
605         { TN_AC_NAME,   "NAME" },
606         { 0, NULL }
607 };
608
609 #define TN_AT_NULL      0
610 #define TN_AT_KRB4      1
611 #define TN_AT_KRB5      2
612 #define TN_AT_SPX       3
613 #define TN_AT_MINK      4
614 #define TN_AT_SRP       5
615 #define TN_AT_RSA       6
616 #define TN_AT_SSL       7
617 #define TN_AT_LOKI      10
618 #define TN_AT_SSA       11
619 #define TN_AT_KEA_SJ    12
620 #define TN_AT_KEA_SJ_INTEG      13
621 #define TN_AT_DSS       14
622 #define TN_AT_NTLM      15
623 static const value_string auth_type_vals[] = {
624         { TN_AT_NULL,   "NULL" },
625         { TN_AT_KRB4,   "Kerberos v4" },
626         { TN_AT_KRB5,   "Kerberos v5" },
627         { TN_AT_SPX,    "SPX" },
628         { TN_AT_MINK,   "MINK" },
629         { TN_AT_SRP,    "SRP" },
630         { TN_AT_RSA,    "RSA" },
631         { TN_AT_SSL,    "SSL" },
632         { TN_AT_LOKI,   "LOKI" },
633         { TN_AT_SSA,    "SSA" },
634         { TN_AT_KEA_SJ, "KEA_SJ" },
635         { TN_AT_KEA_SJ_INTEG, "KEA_SJ_INTEG" },
636         { TN_AT_DSS,    "DSS" },
637         { TN_AT_NTLM,   "NTLM" },
638         { 0, NULL }
639 };
640 static const true_false_string auth_mod_cred_fwd = {
641         "Client WILL forward auth creds",
642         "Client will NOT forward auth creds"
643 };
644 static const true_false_string auth_mod_who = {
645         "Mask server to client",
646         "Mask client to server"
647 };
648 static const true_false_string auth_mod_how = {
649         "MUTUAL authentication",
650         "One Way authentication"
651 };
652 #define TN_AM_OFF               0x00
653 #define TN_AM_USING_TELOPT      0x01
654 #define TN_AM_AFTER_EXCHANGE    0x02
655 #define TN_AM_RESERVED          0x04
656 static const value_string auth_mod_enc[] = {
657         { TN_AM_OFF,            "Off" },
658         { TN_AM_USING_TELOPT,   "Telnet Options" },
659         { TN_AM_AFTER_EXCHANGE, "After Exchange" },
660         { TN_AM_RESERVED,       "Reserved" },
661         { 0, NULL }
662 };
663 #define TN_KRB5_TYPE_AUTH               0
664 #define TN_KRB5_TYPE_REJECT             1
665 #define TN_KRB5_TYPE_ACCEPT             2
666 #define TN_KRB5_TYPE_RESPONSE           3
667 #define TN_KRB5_TYPE_FORWARD            4
668 #define TN_KRB5_TYPE_FORWARD_ACCEPT     5
669 #define TN_KRB5_TYPE_FORWARD_REJECT     6
670 static const value_string auth_krb5_types[] = {
671         { TN_KRB5_TYPE_AUTH,            "Auth" },
672         { TN_KRB5_TYPE_REJECT,          "Reject" },
673         { TN_KRB5_TYPE_ACCEPT,          "Accept" },
674         { TN_KRB5_TYPE_RESPONSE,        "Response" },
675         { TN_KRB5_TYPE_FORWARD,         "Forward" },
676         { TN_KRB5_TYPE_FORWARD_ACCEPT,  "Forward Accept" },
677         { TN_KRB5_TYPE_FORWARD_REJECT,  "Forward Reject" },
678         { 0, NULL }
679 };
680 static void
681 dissect_authentication_type_pair(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, proto_tree *tree)
682 {
683         guint8 type, mod;
684
685         type=tvb_get_guint8(tvb, offset);
686         proto_tree_add_uint(tree, hf_telnet_auth_type, tvb, offset, 1, type);
687
688         mod=tvb_get_guint8(tvb, offset+1);
689         proto_tree_add_uint(tree, hf_telnet_auth_mod_enc, tvb, offset+1, 1, mod);
690         proto_tree_add_boolean(tree, hf_telnet_auth_mod_cred_fwd, tvb, offset+1, 1, mod);
691         proto_tree_add_boolean(tree, hf_telnet_auth_mod_how, tvb, offset+1, 1, mod);
692         proto_tree_add_boolean(tree, hf_telnet_auth_mod_who, tvb, offset+1, 1, mod);
693 }
694
695 /* no kerberos blobs are ever >10kb ? (arbitrary limit) */
696 #define MAX_KRB5_BLOB_LEN       10240
697
698 static tvbuff_t *
699 unescape_and_tvbuffify_telnet_option(packet_info *pinfo, tvbuff_t *tvb, int offset, int len)
700 {
701         tvbuff_t *krb5_tvb;
702         guint8 *buf;
703         const guint8 *spos;
704         guint8 *dpos;
705         int skip, l;
706
707         if(len>=MAX_KRB5_BLOB_LEN)
708                 return NULL;
709
710         spos=tvb_get_ptr(tvb, offset, len);
711         /* XXX we never g_free() this one.  This is done automagically
712            when the parent tvb is destroyed?
713         */
714         buf=g_malloc(len);
715         dpos=buf;
716         skip=0;
717         l=len;
718         while(l>0){
719                 if((spos[0]==0xff) && (spos[1]==0xff)){
720                         skip++;
721                         l-=2;
722                         *(dpos++)=0xff;
723                         spos+=2;
724                         continue;
725                 }
726                 *(dpos++)=*(spos++);
727                 l--;
728         }
729         krb5_tvb = tvb_new_real_data(buf, len-skip, len-skip); 
730         tvb_set_child_real_data_tvbuff(tvb, krb5_tvb);
731         add_new_data_source(pinfo, krb5_tvb, "Unpacked Telnet Uption");
732
733         return krb5_tvb;
734 }
735
736
737 /* as per RFC2942 */
738 static void
739 dissect_krb5_authentication_data(packet_info *pinfo, tvbuff_t *tvb, int offset, int len, proto_tree *tree, guint8 acmd)
740 {
741         tvbuff_t *krb5_tvb;
742         guint8 krb5_cmd;
743
744         dissect_authentication_type_pair(pinfo, tvb, offset, tree);
745         offset+=2;
746         len-=2;
747
748
749         krb5_cmd=tvb_get_guint8(tvb, offset);
750         proto_tree_add_uint(tree, hf_telnet_auth_krb5_type, tvb, offset, 1, krb5_cmd);
751         offset++;
752         len--;
753
754
755         /* IAC SB AUTHENTICATION IS <authentication-type-pair> AUTH <Kerberos V5 KRB_AP_REQ message> IAC SE */
756         if((acmd==TN_AC_IS)&&(krb5_cmd==TN_KRB5_TYPE_AUTH)){
757                 krb5_tvb=unescape_and_tvbuffify_telnet_option(pinfo, tvb, offset, len);
758                 if(krb5_tvb)
759                         dissect_kerberos_main(krb5_tvb, pinfo, tree, FALSE);
760                 else
761                         proto_tree_add_text(tree, tvb, offset, len, "Kerberos blob (too long to dissect - length %u > %u",
762                             len, MAX_KRB5_BLOB_LEN);
763         }
764
765
766
767         /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> ACCEPT IAC SE */
768         /* nothing more to dissect */
769
770
771
772         /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> REJECT <optional reason for rejection> IAC SE*/
773 /*qqq*/
774
775
776         /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> RESPONSE <KRB_AP_REP message> IAC SE */
777         if((acmd==TN_AC_REPLY)&&(krb5_cmd==TN_KRB5_TYPE_RESPONSE)){
778                 krb5_tvb=unescape_and_tvbuffify_telnet_option(pinfo, tvb, offset, len);
779                 dissect_kerberos_main(krb5_tvb, pinfo, tree, FALSE);
780         }
781
782
783         /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD <KRB_CRED message> IAC SE */
784         /* XXX unclear what this one looks like */
785
786
787         /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD_ACCEPT IAC SE */
788         /* nothing more to dissect */
789
790
791
792         /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD_REJECT */
793         /* nothing more to dissect */
794 }
795
796 static void
797 dissect_authentication_subopt(packet_info *pinfo, const char *optname _U_, tvbuff_t *tvb, int offset, int len, proto_tree *tree)
798 {
799         guint8 acmd;
800         char name[256];
801
802 /* XXX here we should really split it up in a conversation struct keeping
803        track of what method we actually use and not just assume it is always
804        kerberos v5
805 */
806         acmd=tvb_get_guint8(tvb, offset);
807         proto_tree_add_uint(tree, hf_telnet_auth_cmd, tvb, offset, 1, acmd);
808         offset++;
809         len--;
810
811         switch(acmd){
812         case TN_AC_REPLY:
813         case TN_AC_IS:
814                 /* XXX here we shouldnt just assume it is krb5 */
815                 dissect_krb5_authentication_data(pinfo, tvb, offset, len, tree, acmd);
816                 break;
817         case TN_AC_SEND:
818                 while(len>0){
819                         dissect_authentication_type_pair(pinfo, tvb, offset, tree);
820                         offset+=2;
821                         len-=2;
822                 }
823                 break;
824         case TN_AC_NAME:
825                 if(len<255){
826                         tvb_memcpy(tvb, name, offset, len);
827                         name[len]=0;
828                 } else {
829                         strcpy(name, "<...name too long...>");
830                 }
831                 proto_tree_add_string(tree, hf_telnet_auth_name, tvb, offset, len, name);
832                 break;
833         }
834 }
835
836 static tn_opt options[] = {
837   {
838     "Binary Transmission",                      /* RFC 856 */
839     NULL,                                       /* no suboption negotiation */
840     NO_LENGTH,
841     0,
842     NULL
843   },
844   {
845     "Echo",                                     /* RFC 857 */
846     NULL,                                       /* no suboption negotiation */
847     NO_LENGTH,
848     0,
849     NULL
850   },
851   {    
852     "Reconnection",                             /* DOD Protocol Handbook */
853     NULL,
854     NO_LENGTH,
855     0,
856     NULL
857   },
858   {
859     "Suppress Go Ahead",                        /* RFC 858 */
860     NULL,                                       /* no suboption negotiation */
861     NO_LENGTH,
862     0,
863     NULL
864   },
865   {
866     "Approx Message Size Negotiation",          /* Ethernet spec(!) */
867     NULL,
868     NO_LENGTH,
869     0,
870     NULL
871   },
872   {
873     "Status",                                   /* RFC 859 */
874     &ett_status_subopt,
875     VARIABLE_LENGTH,
876     1,
877     NULL                                        /* XXX - fill me in */
878   },
879   {
880     "Timing Mark",                              /* RFC 860 */
881     NULL,                                       /* no suboption negotiation */
882     NO_LENGTH,
883     0,
884     NULL
885   },
886   {
887     "Remote Controlled Trans and Echo",         /* RFC 726 */
888     &ett_rcte_subopt,
889     VARIABLE_LENGTH,
890     1,
891     NULL                                        /* XXX - fill me in */
892   },
893   {
894     "Output Line Width",                        /* DOD Protocol Handbook */
895     &ett_olw_subopt,
896     VARIABLE_LENGTH,                            /* XXX - fill me in */
897     0,                                          /* XXX - fill me in */
898     NULL                                        /* XXX - fill me in */
899   },
900   {
901     "Output Page Size",                         /* DOD Protocol Handbook */
902     &ett_ops_subopt,
903     VARIABLE_LENGTH,                            /* XXX - fill me in */
904     0,                                          /* XXX - fill me in */
905     NULL                                        /* XXX - fill me in */
906   },
907   {
908     "Output Carriage-Return Disposition",       /* RFC 652 */
909     &ett_crdisp_subopt,
910     FIXED_LENGTH,
911     2,
912     NULL                                        /* XXX - fill me in */
913   },
914   {
915     "Output Horizontal Tab Stops",              /* RFC 653 */
916     &ett_htstops_subopt,
917     VARIABLE_LENGTH,
918     1,
919     dissect_htstops_subopt
920   },
921   {
922     "Output Horizontal Tab Disposition",        /* RFC 654 */
923     &ett_htdisp_subopt,
924     FIXED_LENGTH,
925     2,
926     NULL                                        /* XXX - fill me in */
927   },
928   {
929     "Output Formfeed Disposition",              /* RFC 655 */
930     &ett_ffdisp_subopt,
931     FIXED_LENGTH,
932     2,
933     NULL                                        /* XXX - fill me in */
934   },
935   {
936     "Output Vertical Tabstops",                 /* RFC 656 */
937     &ett_vtstops_subopt,
938     VARIABLE_LENGTH,
939     1,
940     NULL                                        /* XXX - fill me in */
941   },
942   {
943     "Output Vertical Tab Disposition",          /* RFC 657 */
944     &ett_vtdisp_subopt,
945     FIXED_LENGTH,
946     2,
947     NULL                                        /* XXX - fill me in */
948   },
949   {
950     "Output Linefeed Disposition",              /* RFC 658 */
951     &ett_lfdisp_subopt,
952     FIXED_LENGTH,
953     2,
954     NULL                                        /* XXX - fill me in */
955   },
956   {
957     "Extended ASCII",                           /* RFC 698 */
958     &ett_extasc_subopt,
959     FIXED_LENGTH,
960     2,
961     NULL                                        /* XXX - fill me in */
962   },
963   {
964     "Logout",                                   /* RFC 727 */
965     NULL,                                       /* no suboption negotiation */
966     NO_LENGTH,
967     0,
968     NULL
969   },
970   {
971     "Byte Macro",                               /* RFC 735 */
972     &ett_bytemacro_subopt,
973     VARIABLE_LENGTH,
974     2,
975     NULL                                        /* XXX - fill me in */
976   },
977   {
978     "Data Entry Terminal",                      /* RFC 732, RFC 1043 */
979     &ett_det_subopt,
980     VARIABLE_LENGTH,
981     2,
982     NULL                                        /* XXX - fill me in */
983   },
984   {
985     "SUPDUP",                                   /* RFC 734, RFC 736 */
986     NULL,                                       /* no suboption negotiation */
987     NO_LENGTH,
988     0,
989     NULL
990   },
991   {
992     "SUPDUP Output",                            /* RFC 749 */
993     &ett_supdupout_subopt,
994     VARIABLE_LENGTH,
995     1,
996     NULL                                        /* XXX - fill me in */
997   },
998   {
999     "Send Location",                            /* RFC 779 */
1000     &ett_sendloc_subopt,
1001     VARIABLE_LENGTH,
1002     0,
1003     NULL                                        /* XXX - fill me in */
1004   },
1005   {
1006     "Terminal Type",                            /* RFC 1091 */
1007     &ett_termtype_subopt,
1008     VARIABLE_LENGTH,
1009     1,
1010     dissect_string_subopt
1011   },
1012   {
1013     "End of Record",                            /* RFC 885 */
1014     NULL,                                       /* no suboption negotiation */
1015     NO_LENGTH,
1016     0,
1017     NULL
1018   },
1019   {
1020     "TACACS User Identification",               /* RFC 927 */
1021     &ett_tacacsui_subopt,
1022     FIXED_LENGTH,
1023     4,
1024     NULL                                        /* XXX - fill me in */
1025   },
1026   {
1027     "Output Marking",                           /* RFC 933 */
1028     &ett_outmark_subopt,
1029     VARIABLE_LENGTH,
1030     1,
1031     dissect_outmark_subopt,
1032   },
1033   {
1034     "Terminal Location Number",                 /* RFC 946 */
1035     &ett_tlocnum_subopt,
1036     VARIABLE_LENGTH,
1037     1,
1038     NULL                                        /* XXX - fill me in */
1039   },
1040   {
1041     "Telnet 3270 Regime",                       /* RFC 1041 */
1042     &ett_tn3270reg_subopt,
1043     VARIABLE_LENGTH,
1044     1,
1045     NULL                                        /* XXX - fill me in */
1046   },
1047   {
1048     "X.3 PAD",                                  /* RFC 1053 */
1049     &ett_x3pad_subopt,
1050     VARIABLE_LENGTH,
1051     1,
1052     NULL                                        /* XXX - fill me in */
1053   },
1054   {
1055     "Negotiate About Window Size",              /* RFC 1073, DW183 */
1056     &ett_naws_subopt,
1057     FIXED_LENGTH,
1058     4,
1059     dissect_naws_subopt
1060   },
1061   {
1062     "Terminal Speed",                           /* RFC 1079 */
1063     &ett_tspeed_subopt,
1064     VARIABLE_LENGTH,
1065     1,
1066     NULL                                        /* XXX - fill me in */
1067   },
1068   {
1069     "Remote Flow Control",                      /* RFC 1372 */
1070     &ett_rfc_subopt,
1071     FIXED_LENGTH,
1072     1,
1073     dissect_rfc_subopt
1074   },
1075   {
1076     "Linemode",                                 /* RFC 1184 */
1077     &ett_linemode_subopt,
1078     VARIABLE_LENGTH,
1079     1,
1080     NULL                                        /* XXX - fill me in */
1081   },
1082   {
1083     "X Display Location",                       /* RFC 1096 */
1084     &ett_xdpyloc_subopt,
1085     VARIABLE_LENGTH,
1086     1,
1087     dissect_string_subopt
1088   },
1089   {
1090     "Environment Option",                       /* RFC 1408, RFC 1571 */
1091     &ett_env_subopt,
1092     VARIABLE_LENGTH,
1093     1,
1094     NULL                                        /* XXX - fill me in */
1095   },
1096   {
1097     "Authentication Option",                    /* RFC 2941 */
1098     &ett_auth_subopt,
1099     VARIABLE_LENGTH,
1100     1,
1101     dissect_authentication_subopt               
1102   },
1103   {
1104     "Encryption Option",                        /* RFC 2946 */
1105     &ett_enc_subopt,
1106     VARIABLE_LENGTH,
1107     1,
1108     NULL                                        /* XXX - fill me in */
1109   },
1110   {
1111     "New Environment Option",                   /* RFC 1572 */
1112     &ett_newenv_subopt,
1113     VARIABLE_LENGTH,
1114     1,
1115     NULL                                        /* XXX - fill me in */
1116   },
1117   {
1118     "TN3270E",                                  /* RFC 1647 */
1119     &ett_tn3270e_subopt,
1120     VARIABLE_LENGTH,
1121     1,
1122     NULL                                        /* XXX - fill me in */
1123   },
1124   {
1125     "XAUTH",                                    /* XAUTH  */
1126     &ett_xauth_subopt,
1127     VARIABLE_LENGTH,
1128     1,
1129     NULL                                        /* XXX - fill me in */
1130   },
1131   {
1132     "CHARSET",                                  /* CHARSET  */
1133     &ett_charset_subopt,
1134     VARIABLE_LENGTH,
1135     1,
1136     NULL                                        /* XXX - fill me in */
1137   },
1138   {
1139     "Remote Serial Port",                               /* Remote Serial Port */
1140     &ett_rsp_subopt,
1141     VARIABLE_LENGTH,
1142     1,
1143     NULL                                        /* XXX - fill me in */
1144   },
1145   {
1146     "COM Port Control",                                 /* RFC 2217 */
1147     &ett_comport_subopt,
1148     VARIABLE_LENGTH,
1149     1,
1150     dissect_comport_subopt
1151   },
1152   
1153 };
1154
1155 #define NOPTIONS        (sizeof options / sizeof options[0])
1156
1157 static int
1158 telnet_sub_option(packet_info *pinfo, proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
1159 {
1160   proto_tree *ti, *option_tree;
1161   int offset = start_offset;
1162   guint8 opt_byte;
1163   int subneg_len;
1164   const char *opt;
1165   gint ett;
1166   int iac_offset;
1167   guint len;
1168   void (*dissect)(packet_info *, const char *, tvbuff_t *, int, int, proto_tree *);
1169   gint cur_offset;
1170   gboolean iac_found;
1171
1172   offset += 2;  /* skip IAC and SB */
1173
1174   /* Get the option code */
1175   opt_byte = tvb_get_guint8(tvb, offset);
1176   if (opt_byte > NOPTIONS) {
1177     opt = "<unknown option>";
1178     ett = ett_telnet_subopt;
1179     dissect = NULL;
1180   } else {
1181     opt = options[opt_byte].name;
1182     if (options[opt_byte].subtree_index != NULL)
1183       ett = *(options[opt_byte].subtree_index);
1184     else
1185       ett = ett_telnet_subopt;
1186     dissect = options[opt_byte].dissect;
1187   }
1188   offset++;
1189
1190   /* Search for an unescaped IAC. */
1191   cur_offset = offset;
1192   iac_found = FALSE;
1193   len = tvb_length_remaining(tvb, offset);
1194   do {
1195       iac_offset = tvb_find_guint8(tvb, cur_offset, len, TN_IAC);
1196       iac_found = TRUE;
1197       if (iac_offset == -1) {
1198         /* None found - run to the end of the packet. */
1199         offset += len;
1200       } else {
1201         if (((guint)(iac_offset + 1) >= len) ||
1202              (tvb_get_guint8(tvb, iac_offset + 1) != TN_IAC)) {
1203             /* We really found a single IAC, so we're done */
1204             offset = iac_offset;
1205         } else {
1206             /*
1207              * We saw an escaped IAC, so we have to move ahead to the
1208              * next section
1209              */
1210             iac_found = FALSE;
1211             cur_offset = iac_offset + 2;
1212         }
1213       }
1214
1215   } while (!iac_found);
1216   
1217   subneg_len = offset - start_offset;
1218
1219   ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
1220                            "Suboption Begin: %s", opt);
1221   option_tree = proto_item_add_subtree(ti, ett);
1222   start_offset += 3;    /* skip IAC, SB, and option code */
1223   subneg_len -= 3;
1224
1225   if (subneg_len > 0) {
1226     switch (options[opt_byte].len_type) {
1227
1228     case NO_LENGTH:
1229       /* There isn't supposed to *be* sub-option negotiation for this. */
1230       proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1231                           "Bogus suboption data");
1232       return offset;
1233
1234     case FIXED_LENGTH:
1235       /* Make sure the length is what it's supposed to be. */
1236       if (subneg_len != options[opt_byte].optlen) {
1237         proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1238                           "Suboption parameter length is %d, should be %d",
1239                           subneg_len, options[opt_byte].optlen);
1240         return offset;
1241       }
1242       break;
1243
1244     case VARIABLE_LENGTH:
1245       /* Make sure the length is greater than the minimum. */
1246       if (subneg_len < options[opt_byte].optlen) {
1247         proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1248                             "Suboption parameter length is %d, should be at least %d",
1249                             subneg_len, options[opt_byte].optlen);
1250         return offset;
1251       }
1252       break;
1253     }
1254
1255     /* Now dissect the suboption parameters. */
1256     if (dissect != NULL) {
1257       /* We have a dissector for this suboption's parameters; call it. */
1258       (*dissect)(pinfo, opt, tvb, start_offset, subneg_len, option_tree);
1259     } else {
1260       /* We don't have a dissector for them; just show them as data. */
1261       proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1262                           "Option data");
1263     }
1264   }
1265   return offset;
1266 }
1267
1268 static int
1269 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
1270                         int start_offset, char *type)
1271 {
1272   int offset = start_offset;
1273   guint8 opt_byte;
1274   const char *opt;
1275
1276   offset += 2;  /* skip IAC and WILL,WONT,DO,DONT} */
1277   opt_byte = tvb_get_guint8(tvb, offset);
1278   if (opt_byte > NOPTIONS)
1279     opt = "<unknown option>";
1280   else
1281     opt = options[opt_byte].name;
1282   offset++;
1283
1284   proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
1285                         "Command: %s %s", type, opt);
1286   return offset;
1287 }
1288
1289 static int
1290 telnet_command(packet_info *pinfo, proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
1291 {
1292   int offset = start_offset;
1293   guchar optcode;
1294
1295   offset += 1;  /* skip IAC */
1296   optcode = tvb_get_guint8(tvb, offset);
1297   offset++;
1298   switch(optcode) {
1299
1300   case TN_EOF:
1301     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1302                         "Command: End of File");
1303     break;
1304
1305   case TN_SUSP:
1306     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1307                         "Command: Suspend Current Process");
1308     break;
1309
1310   case TN_ABORT:
1311     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1312                         "Command: Abort Process");
1313     break;
1314
1315   case TN_EOR:
1316     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1317                         "Command: End of Record");
1318     break;
1319
1320   case TN_SE:
1321     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1322                         "Command: Suboption End");
1323     break;
1324
1325   case TN_NOP:
1326     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1327                         "Command: No Operation");
1328     break;
1329
1330   case TN_DM:
1331     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1332                         "Command: Data Mark");
1333     break;
1334
1335   case TN_BRK:
1336     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1337                         "Command: Break");
1338     break;
1339
1340   case TN_IP:
1341     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1342                         "Command: Interrupt Process");
1343     break;
1344
1345   case TN_AO:
1346     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1347                         "Command: Abort Output");
1348     break;
1349
1350   case TN_AYT:
1351     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1352                         "Command: Are You There?");
1353     break;
1354
1355   case TN_EC:
1356     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1357                         "Command: Escape Character");
1358     break;
1359
1360   case TN_EL:
1361     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1362                         "Command: Erase Line");
1363     break;
1364
1365   case TN_GA:
1366     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1367                         "Command: Go Ahead");
1368     break;
1369
1370   case TN_SB:
1371     offset = telnet_sub_option(pinfo, telnet_tree, tvb, start_offset);
1372     break;
1373
1374   case TN_WILL:
1375     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1376                                         "Will");
1377     break;
1378
1379   case TN_WONT:
1380     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1381                                         "Won't");
1382     break;
1383
1384   case TN_DO:
1385     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1386                                         "Do");
1387     break;
1388
1389   case TN_DONT:
1390     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1391                                         "Don't");
1392     break;
1393
1394   default:
1395     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1396                         "Command: Unknown (0x%02x)", optcode);
1397     break;
1398   }
1399
1400   return offset;
1401 }
1402
1403 static void
1404 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
1405 {
1406   gint next_offset;
1407   int linelen;
1408   guint8 c;
1409   gboolean last_char_was_cr;
1410
1411   while (len != 0 && tvb_offset_exists(tvb, offset)) {
1412     /*
1413      * Find the end of the line.
1414      */
1415     linelen = tvb_find_line_end(tvb, offset, len, &next_offset, FALSE);
1416     len -= next_offset - offset;        /* subtract out the line's characters */
1417
1418     /*
1419      * In Telnet, CR NUL is the way you send a CR by itself in the
1420      * default ASCII mode; don't treat CR by itself as a line ending,
1421      * treat only CR NUL, CR LF, or LF by itself as a line ending.
1422      */
1423     if (next_offset == offset + linelen + 1 && len >= 1) {
1424       /*
1425        * Well, we saw a one-character line ending, so either it's a CR
1426        * or an LF; we have at least two characters left, including the
1427        * CR.
1428        *
1429        * If the line ending is a CR, skip all subsequent CRs; at
1430        * least one capture appeared to have multiple CRs at the end of
1431        * a line.
1432        */
1433       if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
1434         last_char_was_cr = TRUE;
1435         while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
1436           c = tvb_get_guint8(tvb, next_offset);
1437           next_offset++;        /* skip over that character */
1438           len--;
1439           if (c == '\n' || (c == '\0' && last_char_was_cr)) {
1440             /*
1441              * LF is a line ending, whether preceded by CR or not.
1442              * NUL is a line ending if preceded by CR.
1443              */
1444             break;
1445           }
1446           last_char_was_cr = (c == '\r');
1447         }
1448       }
1449     }
1450
1451     /*
1452      * Now compute the length of the line *including* the end-of-line
1453      * indication, if any; we display it all.
1454      */
1455     linelen = next_offset - offset;
1456
1457     proto_tree_add_text(tree, tvb, offset, linelen,
1458                         "Data: %s",
1459                         tvb_format_text(tvb, offset, linelen));
1460     offset = next_offset;
1461   }
1462 }
1463
1464 static void
1465 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1466 {
1467         proto_tree      *telnet_tree, *ti;
1468
1469
1470         if (check_col(pinfo->cinfo, COL_PROTOCOL))
1471                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
1472
1473         if (check_col(pinfo->cinfo, COL_INFO))
1474                 col_add_fstr(pinfo->cinfo, COL_INFO, "Telnet Data ...");
1475
1476         if (tree) {
1477           gint offset = 0;
1478           guint len;
1479           int data_len;
1480           gint iac_offset;
1481
1482           ti = proto_tree_add_item(tree, proto_telnet, tvb, offset, -1, FALSE);
1483           telnet_tree = proto_item_add_subtree(ti, ett_telnet);
1484
1485           /*
1486            * Scan through the buffer looking for an IAC byte.
1487            */
1488           while ((len = tvb_length_remaining(tvb, offset)) > 0) {
1489             iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
1490             if (iac_offset != -1) {
1491               /*
1492                * We found an IAC byte.
1493                * If there's any data before it, add that data to the
1494                * tree, a line at a time.
1495                */
1496               data_len = iac_offset - offset;
1497               if (data_len > 0)
1498                 telnet_add_text(telnet_tree, tvb, offset, data_len);
1499
1500               /*
1501                * Now interpret the command.
1502                */
1503               offset = telnet_command(pinfo, telnet_tree, tvb, iac_offset);
1504             }
1505             else {
1506               /*
1507                * We found no IAC byte, so what remains in the buffer
1508                * is the last of the data in the packet.
1509                * Add it to the tree, a line at a time, and then quit.
1510                */
1511               telnet_add_text(telnet_tree, tvb, offset, len);
1512               break;
1513             }
1514           }
1515         }
1516 }
1517
1518 void
1519 proto_register_telnet(void)
1520 {
1521         static hf_register_info hf[] = {  
1522         { &hf_telnet_auth_name,
1523                 { "Name", "telnet.auth.name", FT_STRING, BASE_NONE,
1524                   NULL, 0, "Name of user being authenticated", HFILL }},
1525         { &hf_telnet_auth_cmd,
1526                 { "Auth Cmd", "telnet.auth.cmd", FT_UINT8, BASE_DEC,
1527                   VALS(auth_cmd_vals), 0, "Authentication Command", HFILL }},
1528         { &hf_telnet_auth_type,
1529                 { "Auth Type", "telnet.auth.type", FT_UINT8, BASE_DEC,
1530                   VALS(auth_type_vals), 0, "Authentication Type", HFILL }},
1531         { &hf_telnet_auth_mod_cred_fwd,
1532                 { "Cred Fwd", "telnet.auth.mod.cred_fwd", FT_BOOLEAN, 8,
1533                   TFS(&auth_mod_cred_fwd), 0x08, "Modifier: Whether client will forward creds or not", HFILL }},
1534         { &hf_telnet_auth_mod_who,
1535                 { "Who", "telnet.auth.mod.who", FT_BOOLEAN, 8,
1536                   TFS(&auth_mod_who), 0x01, "Modifier: Who to mask", HFILL }},
1537         { &hf_telnet_auth_mod_how,
1538                 { "How", "telnet.auth.mod.how", FT_BOOLEAN, 8,
1539                   TFS(&auth_mod_how), 0x02, "Modifier: How to mask", HFILL }},
1540         { &hf_telnet_auth_mod_enc,
1541                 { "Encrypt", "telnet.auth.mod.enc", FT_UINT8, BASE_DEC,
1542                   VALS(auth_mod_enc), 0x14, "Modifier: How to enable Encryption", HFILL }},
1543         { &hf_telnet_auth_krb5_type,
1544                 { "Command", "telnet.auth.krb5.cmd", FT_UINT8, BASE_DEC,
1545                   VALS(auth_krb5_types), 0, "Krb5 Authentication sub-command", HFILL }},
1546                 
1547         };
1548         static gint *ett[] = {
1549                 &ett_telnet,
1550                 &ett_telnet_subopt,
1551                 &ett_status_subopt,
1552                 &ett_rcte_subopt,
1553                 &ett_olw_subopt,
1554                 &ett_ops_subopt,
1555                 &ett_crdisp_subopt,
1556                 &ett_htstops_subopt,
1557                 &ett_htdisp_subopt,
1558                 &ett_ffdisp_subopt,
1559                 &ett_vtstops_subopt,
1560                 &ett_vtdisp_subopt,
1561                 &ett_lfdisp_subopt,
1562                 &ett_extasc_subopt,
1563                 &ett_bytemacro_subopt,
1564                 &ett_det_subopt,
1565                 &ett_supdupout_subopt,
1566                 &ett_sendloc_subopt,
1567                 &ett_termtype_subopt,
1568                 &ett_tacacsui_subopt,
1569                 &ett_outmark_subopt,
1570                 &ett_tlocnum_subopt,
1571                 &ett_tn3270reg_subopt,
1572                 &ett_x3pad_subopt,
1573                 &ett_naws_subopt,
1574                 &ett_tspeed_subopt,
1575                 &ett_rfc_subopt,
1576                 &ett_linemode_subopt,
1577                 &ett_xdpyloc_subopt,
1578                 &ett_env_subopt,
1579                 &ett_auth_subopt,
1580                 &ett_enc_subopt,
1581                 &ett_newenv_subopt,
1582                 &ett_tn3270e_subopt,
1583                 &ett_xauth_subopt,
1584                 &ett_charset_subopt,
1585                 &ett_rsp_subopt,
1586                 &ett_comport_subopt,
1587         };
1588
1589         proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
1590         proto_register_field_array(proto_telnet, hf, array_length(hf));
1591         proto_register_subtree_array(ett, array_length(ett));
1592 }
1593
1594 void
1595 proto_reg_handoff_telnet(void)
1596 {
1597         dissector_handle_t telnet_handle;
1598
1599         telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
1600         dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);
1601 }