If a PrincipalName has at least one name-string, put the first of the
[obnox/wireshark/wip.git] / packet-telnet.c
1 /* packet-telnet.c
2  * Routines for telnet packet dissection
3  * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
4  *
5  * $Id: packet-telnet.c,v 1.21 2000/11/19 08:54:09 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.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
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
40 #endif
41
42 #include <string.h>
43 #include <glib.h>
44 #include "packet.h"
45 #include "strutil.h"
46
47 static int proto_telnet = -1;
48
49 static gint ett_telnet = -1;
50 static gint ett_telnet_subopt = -1;
51
52 /* Some defines for Telnet */
53
54 #define TCP_PORT_TELNET                 23
55
56 #define TN_IAC   255
57 #define TN_DONT  254
58 #define TN_DO    253
59 #define TN_WONT  252
60 #define TN_WILL  251
61 #define TN_SB    250
62 #define TN_GA    249
63 #define TN_EL    248
64 #define TN_EC    247
65 #define TN_AYT   246
66 #define TN_AO    245
67 #define TN_IP    244
68 #define TN_BRK   243
69 #define TN_DM    242
70 #define TN_NOP   241
71 #define TN_SE    240
72 #define TN_EOR   239
73 #define TN_ABORT 238
74 #define TN_SUSP  237
75 #define TN_EOF   236
76
77 static const char *options[] = {
78   "Binary Transmission",
79   "Echo",
80   "Reconnection",
81   "Suppress Go Ahead",
82   "Approx Message Size Negotiation",
83   "Status",
84   "Timing Mark",
85   "Remote Controlled Trans and Echo",
86   "Output Line Width",
87   "Output Page Size",
88   "Output Carriage-Return Disposition",
89   "Output Horizontal Tab Stops",
90   "Output Horizontal Tab Disposition",
91   "Output Formfeed Disposition",
92   "Output Vertical Tabstops",
93   "Output Vertical Tab Disposition",
94   "Output Linefeed Disposition",
95   "Extended ASCII",
96   "Logout",
97   "Byte Macro",
98   "Data Entry Terminal",
99   "SUPDUP",
100   "SUPDUP Output",
101   "Send Location",
102   "Terminal Type",
103   "End of Record",
104   "TACACS User Identification",
105   "Output Marking",
106   "Terminal Location Number",
107   "Telnet 3270 Regime",
108   "X.3 PAD",
109   "Negotiate About Window Size",
110   "Terminal Speed",
111   "Remote Flow Control",
112   "Linemode",
113   "X Display Location",
114   "Environment Option",
115   "Authentication Option",
116   "Encryption Option",
117   "New Environment Option",
118   "TN3270E"
119 };
120
121 #define NOPTIONS        (sizeof options / sizeof options[0])
122
123 static int
124 telnet_sub_option(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
125 {
126   proto_tree *ti, *option_tree;
127   int offset = start_offset;
128   guint8 opt_byte;
129   int subneg_len, req;
130   const u_char *opt;
131   guint len;
132
133   offset += 2;  /* skip IAC and SB */
134
135   /* Figure out the option and type */
136   opt_byte = tvb_get_guint8(tvb, offset);
137   if (opt_byte > NOPTIONS)
138     opt = "<unknown option>";
139   else
140     opt = options[opt_byte];
141   offset++;
142   req = tvb_get_guint8(tvb, offset);
143   offset++;
144
145   /* Search for an IAC. */
146   len = tvb_length_remaining(tvb, offset);
147   offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
148   if (offset == -1) {
149     /* None found - run to the end of the packet. */
150     offset += len;
151   }
152
153   subneg_len = offset - start_offset;
154
155   ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
156                         "Suboption Begin: %s", opt);
157
158   option_tree = proto_item_add_subtree(ti, ett_telnet_subopt);
159
160   proto_tree_add_text(option_tree, tvb, start_offset + 2, 2,
161                         "%s %s", (req ? "Send your" : "Here's my"), opt);
162
163   if (req == 0) {  /* Add the value */
164     proto_tree_add_text(option_tree, tvb, start_offset + 4, subneg_len - 4,
165         "Value: %s", tvb_format_text(tvb, start_offset + 4, subneg_len - 4));
166   }
167   return offset;
168 }
169
170 static int
171 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
172                         int start_offset, char *type)
173 {
174   int offset = start_offset;
175   guint8 opt_byte;
176   const char *opt;
177
178   offset += 2;  /* skip IAC and WILL,WONT,DO,DONT} */
179   opt_byte = tvb_get_guint8(tvb, offset);
180   if (opt_byte > NOPTIONS)
181     opt = "<unknown option>";
182   else
183     opt = options[opt_byte];
184   offset++;
185                       
186   proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
187                         "Command: %s %s", type, opt);
188   return offset;
189 }
190
191 static int
192 telnet_command(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
193 {
194   int offset = start_offset;
195   u_char optcode;
196   
197   offset += 1;  /* skip IAC */
198   optcode = tvb_get_guint8(tvb, offset);
199   offset++;
200   switch(optcode) {
201
202   case TN_EOF:
203     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
204                         "Command: End of File");
205     break;
206
207   case TN_SUSP:
208     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
209                         "Command: Suspend Current Process");
210     break;
211
212   case TN_ABORT:
213     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
214                         "Command: Abort Process");
215     break;
216
217   case TN_EOR:
218     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
219                         "Command: End of Record");
220     break;
221
222   case TN_SE:
223     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
224                         "Command: Suboption End");
225     break;
226
227   case TN_NOP:
228     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
229                         "Command: No Operation");
230     break;
231
232   case TN_DM:
233     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
234                         "Command: Data Mark");
235     break;
236
237   case TN_BRK:
238     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
239                         "Command: Break");
240     break;
241
242   case TN_IP:
243     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
244                         "Command: Interrupt Process");
245     break;
246
247   case TN_AO:
248     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
249                         "Command: Abort Output");
250     break;
251
252   case TN_AYT:
253     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
254                         "Command: Are You There?");
255     break;
256
257   case TN_EC:
258     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
259                         "Command: Escape Character");
260     break;
261
262   case TN_EL:
263     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
264                         "Command: Erase Line");
265     break;
266
267   case TN_GA:
268     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
269                         "Command: Go Ahead");
270     break;
271
272   case TN_SB:
273     offset = telnet_sub_option(telnet_tree, tvb, start_offset);
274     break;
275
276   case TN_WILL:
277     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
278                                         "Will");
279     break;
280
281   case TN_WONT:
282     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
283                                         "Won't");
284     break;
285
286   case TN_DO:
287     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
288                                         "Do");
289     break;
290
291   case TN_DONT:
292     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
293                                         "Don't");
294     break;
295   }
296
297   return offset;
298 }
299
300 static void
301 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
302 {
303   gint next_offset;
304   int linelen;
305   guint8 c;
306   gboolean last_char_was_cr;
307
308   while (len != 0 && tvb_offset_exists(tvb, offset)) {
309     /*
310      * Find the end of the line.
311      */
312     linelen = tvb_find_line_end(tvb, offset, len, &next_offset);
313     len -= next_offset - offset;        /* subtract out the line's characters */
314
315     /*
316      * In Telnet, CR NUL is the way you send a CR by itself in the
317      * default ASCII mode; don't treat CR by itself as a line ending,
318      * treat only CR NUL, CR LF, or LF by itself as a line ending.
319      */
320     if (next_offset == offset + linelen + 1 && len >= 1) {
321       /*
322        * Well, we saw a one-character line ending, so either it's a CR
323        * or an LF; we have at least two characters left, including the
324        * CR.
325        *
326        * If the line ending is a CR, skip all subsequent CRs; at
327        * least one capture appeared to have multiple CRs at the end of
328        * a line.
329        */
330       if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
331         last_char_was_cr = TRUE;
332         while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
333           c = tvb_get_guint8(tvb, next_offset);
334           next_offset++;        /* skip over that character */
335           len--;
336           if (c == '\n' || (c == '\0' && last_char_was_cr)) {
337             /*
338              * LF is a line ending, whether preceded by CR or not.
339              * NUL is a line ending if preceded by CR.
340              */
341             break;
342           }
343           last_char_was_cr = (c == '\r');
344         }
345       }
346     }
347
348     /*
349      * Now compute the length of the line *including* the end-of-line
350      * indication, if any; we display it all.
351      */
352     linelen = next_offset - offset;
353
354     proto_tree_add_text(tree, tvb, offset, linelen,
355                         "Data: %s",
356                         tvb_format_text(tvb, offset, linelen));
357     offset = next_offset;
358   }
359 }
360
361 static void
362 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
363 {
364         proto_tree      *telnet_tree, *ti;
365
366         CHECK_DISPLAY_AS_DATA(proto_telnet, tvb, pinfo, tree);
367
368         pinfo->current_proto = "TELNET";
369
370         if (check_col(pinfo->fd, COL_PROTOCOL))
371                 col_set_str(pinfo->fd, COL_PROTOCOL, "TELNET");
372
373         if (check_col(pinfo->fd, COL_INFO))
374                 col_add_fstr(pinfo->fd, COL_INFO, "Telnet Data ...");
375
376         if (tree) {
377           gint offset = 0;
378           guint len;
379           int data_len;
380           gint iac_offset;
381
382           ti = proto_tree_add_item(tree, proto_telnet, tvb, offset,
383             tvb_length_remaining(tvb, offset), FALSE);
384           telnet_tree = proto_item_add_subtree(ti, ett_telnet);
385
386           /*
387            * Scan through the buffer looking for an IAC byte.
388            */
389           while ((len = tvb_length_remaining(tvb, offset)) > 0) {
390             iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
391             if (iac_offset != -1) {
392               /*
393                * We found an IAC byte.
394                * If there's any data before it, add that data to the
395                * tree, a line at a time.
396                */
397               data_len = iac_offset - offset;
398               if (data_len > 0)
399                 telnet_add_text(telnet_tree, tvb, offset, data_len);
400               
401               /*
402                * Now interpret the command.
403                */
404               offset = telnet_command(telnet_tree, tvb, iac_offset);
405             }
406             else {
407               /*
408                * We found no IAC byte, so what remains in the buffer
409                * is the last of the data in the packet.
410                * Add it to the tree, a line at a time, and then quit.
411                */
412               telnet_add_text(telnet_tree, tvb, offset, len);
413               break;
414             }
415           }
416         }
417 }
418
419 void
420 proto_register_telnet(void)
421 {
422 /*        static hf_register_info hf[] = {
423                 { &variable,
424                 { "Name",           "telnet.abbreviation", TYPE, VALS_POINTER }},
425         };*/
426         static gint *ett[] = {
427                 &ett_telnet,
428                 &ett_telnet_subopt,
429         };
430
431         proto_telnet = proto_register_protocol("Telnet", "telnet");
432  /*       proto_register_field_array(proto_telnet, hf, array_length(hf));*/
433         proto_register_subtree_array(ett, array_length(ett));
434 }
435
436 void
437 proto_reg_handoff_telnet(void)
438 {
439         dissector_add("tcp.port", TCP_PORT_TELNET, dissect_telnet);
440 }