Make last modifications work with gtk2.
[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.34 2002/10/25 21:13:38 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
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   int iac_offset;
124   guint len;
125
126   offset += 2;  /* skip IAC and SB */
127
128   /* Figure out the option and type */
129   opt_byte = tvb_get_guint8(tvb, offset);
130   if (opt_byte > NOPTIONS)
131     opt = "<unknown option>";
132   else
133     opt = options[opt_byte];
134   offset++;
135   req = tvb_get_guint8(tvb, offset);
136   offset++;
137
138   /* Search for an IAC. */
139   len = tvb_length_remaining(tvb, offset);
140   iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
141   if (iac_offset == -1) {
142     /* None found - run to the end of the packet. */
143     offset += len;
144   } else
145     offset = iac_offset;
146
147   subneg_len = offset - start_offset;
148
149   if (subneg_len > 0) {
150       ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
151                 "Suboption Begin: %s", opt);
152
153       option_tree = proto_item_add_subtree(ti, ett_telnet_subopt);
154
155       proto_tree_add_text(option_tree, tvb, start_offset + 2, 2,
156                 "%s %s", (req ? "Send your" : "Here's my"), opt);
157
158       if (req == 0) {  /* Add the value */
159         proto_tree_add_text(option_tree, tvb, start_offset + 4, subneg_len - 4,
160         "Value: %s", tvb_format_text(tvb, start_offset + 4, subneg_len - 4));
161       }
162   }
163   return offset;
164 }
165
166 static int
167 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
168                         int start_offset, char *type)
169 {
170   int offset = start_offset;
171   guint8 opt_byte;
172   const char *opt;
173
174   offset += 2;  /* skip IAC and WILL,WONT,DO,DONT} */
175   opt_byte = tvb_get_guint8(tvb, offset);
176   if (opt_byte > NOPTIONS)
177     opt = "<unknown option>";
178   else
179     opt = options[opt_byte];
180   offset++;
181
182   proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
183                         "Command: %s %s", type, opt);
184   return offset;
185 }
186
187 static int
188 telnet_command(proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
189 {
190   int offset = start_offset;
191   guchar optcode;
192
193   offset += 1;  /* skip IAC */
194   optcode = tvb_get_guint8(tvb, offset);
195   offset++;
196   switch(optcode) {
197
198   case TN_EOF:
199     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
200                         "Command: End of File");
201     break;
202
203   case TN_SUSP:
204     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
205                         "Command: Suspend Current Process");
206     break;
207
208   case TN_ABORT:
209     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
210                         "Command: Abort Process");
211     break;
212
213   case TN_EOR:
214     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
215                         "Command: End of Record");
216     break;
217
218   case TN_SE:
219     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
220                         "Command: Suboption End");
221     break;
222
223   case TN_NOP:
224     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
225                         "Command: No Operation");
226     break;
227
228   case TN_DM:
229     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
230                         "Command: Data Mark");
231     break;
232
233   case TN_BRK:
234     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
235                         "Command: Break");
236     break;
237
238   case TN_IP:
239     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
240                         "Command: Interrupt Process");
241     break;
242
243   case TN_AO:
244     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
245                         "Command: Abort Output");
246     break;
247
248   case TN_AYT:
249     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
250                         "Command: Are You There?");
251     break;
252
253   case TN_EC:
254     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
255                         "Command: Escape Character");
256     break;
257
258   case TN_EL:
259     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
260                         "Command: Erase Line");
261     break;
262
263   case TN_GA:
264     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
265                         "Command: Go Ahead");
266     break;
267
268   case TN_SB:
269     offset = telnet_sub_option(telnet_tree, tvb, start_offset);
270     break;
271
272   case TN_WILL:
273     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
274                                         "Will");
275     break;
276
277   case TN_WONT:
278     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
279                                         "Won't");
280     break;
281
282   case TN_DO:
283     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
284                                         "Do");
285     break;
286
287   case TN_DONT:
288     offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
289                                         "Don't");
290     break;
291
292   default:
293     proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
294                         "Command: Unknown (0x%02x)", optcode);
295     break;
296   }
297
298   return offset;
299 }
300
301 static void
302 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
303 {
304   gint next_offset;
305   int linelen;
306   guint8 c;
307   gboolean last_char_was_cr;
308
309   while (len != 0 && tvb_offset_exists(tvb, offset)) {
310     /*
311      * Find the end of the line.
312      */
313     linelen = tvb_find_line_end(tvb, offset, len, &next_offset, FALSE);
314     len -= next_offset - offset;        /* subtract out the line's characters */
315
316     /*
317      * In Telnet, CR NUL is the way you send a CR by itself in the
318      * default ASCII mode; don't treat CR by itself as a line ending,
319      * treat only CR NUL, CR LF, or LF by itself as a line ending.
320      */
321     if (next_offset == offset + linelen + 1 && len >= 1) {
322       /*
323        * Well, we saw a one-character line ending, so either it's a CR
324        * or an LF; we have at least two characters left, including the
325        * CR.
326        *
327        * If the line ending is a CR, skip all subsequent CRs; at
328        * least one capture appeared to have multiple CRs at the end of
329        * a line.
330        */
331       if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
332         last_char_was_cr = TRUE;
333         while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
334           c = tvb_get_guint8(tvb, next_offset);
335           next_offset++;        /* skip over that character */
336           len--;
337           if (c == '\n' || (c == '\0' && last_char_was_cr)) {
338             /*
339              * LF is a line ending, whether preceded by CR or not.
340              * NUL is a line ending if preceded by CR.
341              */
342             break;
343           }
344           last_char_was_cr = (c == '\r');
345         }
346       }
347     }
348
349     /*
350      * Now compute the length of the line *including* the end-of-line
351      * indication, if any; we display it all.
352      */
353     linelen = next_offset - offset;
354
355     proto_tree_add_text(tree, tvb, offset, linelen,
356                         "Data: %s",
357                         tvb_format_text(tvb, offset, linelen));
358     offset = next_offset;
359   }
360 }
361
362 static void
363 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
364 {
365         proto_tree      *telnet_tree, *ti;
366
367         if (check_col(pinfo->cinfo, COL_PROTOCOL))
368                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
369
370         if (check_col(pinfo->cinfo, COL_INFO))
371                 col_add_fstr(pinfo->cinfo, COL_INFO, "Telnet Data ...");
372
373         if (tree) {
374           gint offset = 0;
375           guint len;
376           int data_len;
377           gint iac_offset;
378
379           ti = proto_tree_add_item(tree, proto_telnet, tvb, offset, -1, FALSE);
380           telnet_tree = proto_item_add_subtree(ti, ett_telnet);
381
382           /*
383            * Scan through the buffer looking for an IAC byte.
384            */
385           while ((len = tvb_length_remaining(tvb, offset)) > 0) {
386             iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
387             if (iac_offset != -1) {
388               /*
389                * We found an IAC byte.
390                * If there's any data before it, add that data to the
391                * tree, a line at a time.
392                */
393               data_len = iac_offset - offset;
394               if (data_len > 0)
395                 telnet_add_text(telnet_tree, tvb, offset, data_len);
396
397               /*
398                * Now interpret the command.
399                */
400               offset = telnet_command(telnet_tree, tvb, iac_offset);
401             }
402             else {
403               /*
404                * We found no IAC byte, so what remains in the buffer
405                * is the last of the data in the packet.
406                * Add it to the tree, a line at a time, and then quit.
407                */
408               telnet_add_text(telnet_tree, tvb, offset, len);
409               break;
410             }
411           }
412         }
413 }
414
415 void
416 proto_register_telnet(void)
417 {
418 /*        static hf_register_info hf[] = {
419                 { &variable,
420                 { "Name",           "telnet.abbreviation", TYPE, VALS_POINTER }},
421         };*/
422         static gint *ett[] = {
423                 &ett_telnet,
424                 &ett_telnet_subopt,
425         };
426
427         proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
428  /*       proto_register_field_array(proto_telnet, hf, array_length(hf));*/
429         proto_register_subtree_array(ett, array_length(ett));
430 }
431
432 void
433 proto_reg_handoff_telnet(void)
434 {
435         dissector_handle_t telnet_handle;
436
437         telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
438         dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);
439 }