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