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