Add a comment explaining why we're defining S_ISDIR and company.
[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.13 2000/05/31 05:07:50 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
46 static int proto_telnet = -1;
47
48 static gint ett_telnet = -1;
49 static gint ett_telnet_subopt = -1;
50
51 /* Some defines for Telnet */
52
53 #define TCP_PORT_TELNET                 23
54
55 #define TN_IAC   255
56 #define TN_DONT  254
57 #define TN_DO    253
58 #define TN_WONT  252
59 #define TN_WILL  251
60 #define TN_SB    250
61 #define TN_GA    249
62 #define TN_EL    248
63 #define TN_EC    247
64 #define TN_AYT   246
65 #define TN_AO    245
66 #define TN_IP    244
67 #define TN_BRK   243
68 #define TN_DM    242
69 #define TN_NOP   241
70 #define TN_SE    240
71 #define TN_EOR   239
72 #define TN_ABORT 238
73 #define TN_SUSP  237
74 #define TN_EOF   236
75
76 static const char *options[] = {
77   "Binary Transmission",
78   "Echo",
79   "Reconnection",
80   "Suppress Go Ahead",
81   "Approx Message Size Negotiation",
82   "Status",
83   "Timing Mark",
84   "Remote Controlled Trans and Echo",
85   "Output Line Width",
86   "Output Page Size",
87   "Output Carriage-Return Disposition",
88   "Output Horizontal Tab Stops",
89   "Output Horizontal Tab Disposition",
90   "Output Formfeed Disposition",
91   "Output Vertical Tabstops",
92   "Output Vertical Tab Disposition",
93   "Output Linefeed Disposition",
94   "Extended ASCII",
95   "Logout",
96   "Byte Macro",
97   "Data Entry Terminal",
98   "SUPDUP",
99   "SUPDUP Output",
100   "Send Location",
101   "Terminal Type",
102   "End of Record",
103   "TACACS User Identification",
104   "Output Marking",
105   "Terminal Location Number",
106   "Telnet 3270 Regime",
107   "X.3 PAD",
108   "Negotiate About Window Size",
109   "Terminal Speed",
110   "Remote Flow Control",
111   "Linemode",
112   "X Display Location",
113   "Environment Option",
114   "Authentication Option",
115   "Encryption Option",
116   "New Environment Option",
117   "TN3270E"
118 };
119
120 #define NOPTIONS        (sizeof options / sizeof options[0])
121
122 static int
123 telnet_sub_option(proto_tree *telnet_tree, const u_char *pd,
124                 int start_offset)
125 {
126   proto_tree *ti, *option_tree;
127   int offset = start_offset;
128   int subneg_len, req;
129   gboolean not_found = TRUE;
130   const u_char *opt;
131
132   offset += 2;  /* skip IAC and SB */
133
134   /* Figure out the option and type */
135   if (pd[offset] > NOPTIONS)
136     opt = "<unknown option>";
137   else
138     opt = options[pd[offset]];
139   offset++;
140   req = pd[offset];
141   offset++;
142
143   while (offset < pi.captured_len && not_found) {  
144     if (pd[offset] == TN_IAC)
145       not_found = FALSE;
146     else
147       offset++;
148   }
149
150   subneg_len = offset - start_offset;
151
152   ti = proto_tree_add_text(telnet_tree, NullTVB, start_offset, subneg_len,
153                         "Suboption Begin: %s", opt);
154
155   option_tree = proto_item_add_subtree(ti, ett_telnet_subopt);
156
157   proto_tree_add_text(option_tree, NullTVB, start_offset + 2, 2,
158                         "%s %s", (req ? "Send your" : "Here's my"), opt);
159
160   if (req == 0) {  /* Add the value */
161     proto_tree_add_text(option_tree, NullTVB, start_offset + 4, subneg_len - 4,
162         "Value: %s", format_text(&pd[start_offset + 4], subneg_len - 4));
163   }
164   return offset;
165 }
166
167 static int
168 telnet_will_wont_do_dont(proto_tree *telnet_tree, const u_char *pd,
169                         int start_offset, char *type)
170 {
171   int offset = start_offset;
172   const char *opt;
173
174   offset += 2;  /* skip IAC and WILL,WONT,DO,DONT} */
175   if (pd[offset] > NOPTIONS)
176     opt = "<unknown option>";
177   else
178     opt = options[pd[offset]];
179   offset++;
180                       
181   proto_tree_add_text(telnet_tree, NullTVB, start_offset, 3,
182                         "Command: %s %s", type, opt);
183   return offset;
184 }
185
186 static int
187 telnet_command(proto_tree *telnet_tree, const u_char *pd, int start_offset)
188 {
189   int offset = start_offset;
190   u_char optcode;
191   
192   offset += 1;  /* skip IAC */
193   optcode = pd[offset];
194   offset++;
195   switch(optcode) {
196
197   case TN_EOF:
198     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
199                         "Command: End of File");
200     break;
201
202   case TN_SUSP:
203     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
204                         "Command: Suspend Current Process");
205     break;
206
207   case TN_ABORT:
208     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
209                         "Command: Abort Process");
210     break;
211
212   case TN_EOR:
213     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
214                         "Command: End of Record");
215     break;
216
217   case TN_SE:
218     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
219                         "Command: Suboption End");
220     break;
221
222   case TN_NOP:
223     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
224                         "Command: No Operation");
225     break;
226
227   case TN_DM:
228     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
229                         "Command: Data Mark");
230     break;
231
232   case TN_BRK:
233     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
234                         "Command: Break");
235     break;
236
237   case TN_IP:
238     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
239                         "Command: Interrupt Process");
240     break;
241
242   case TN_AO:
243     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
244                         "Command: Abort Output");
245     break;
246
247   case TN_AYT:
248     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
249                         "Command: Are You There?");
250     break;
251
252   case TN_EC:
253     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
254                         "Command: Escape Character");
255     break;
256
257   case TN_EL:
258     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
259                         "Command: Erase Line");
260     break;
261
262   case TN_GA:
263     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
264                         "Command: Go Ahead");
265     break;
266
267   case TN_SB:
268     offset = telnet_sub_option(telnet_tree, pd, start_offset);
269     break;
270
271   case TN_WILL:
272     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
273                                         "Will");
274     break;
275
276   case TN_WONT:
277     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
278                                         "Won't");
279     break;
280
281   case TN_DO:
282     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
283                                         "Do");
284     break;
285
286   case TN_DONT:
287     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
288                                         "Don't");
289     break;
290   }
291
292   return offset;
293 }
294
295 static void
296 dissect_telnet(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
297 {
298         proto_tree      *telnet_tree, *ti;
299
300         if (check_col(fd, COL_PROTOCOL))
301                 col_add_str(fd, COL_PROTOCOL, "TELNET");
302
303         if (check_col(fd, COL_INFO))
304           col_add_fstr(fd, COL_INFO, "Telnet Data ...");
305
306         if (tree) {
307           int data_offset;
308           int data_len;
309
310           ti = proto_tree_add_item(tree, proto_telnet, NullTVB, offset, END_OF_FRAME, FALSE);
311           telnet_tree = proto_item_add_subtree(ti, ett_telnet);
312
313           data_offset = offset;
314           data_len = 0;
315
316           /*
317            * Scan through the buffer looking for an IAC byte.
318            */
319           while (offset < pi.captured_len) {
320             if (pd[offset] == TN_IAC) {
321               /*
322                * We found an IAC byte.
323                * If there's any data before it, add that data to the
324                * tree.
325                */
326               if (data_len > 0) {
327                 proto_tree_add_text(telnet_tree, NullTVB, data_offset, data_len,
328                         "Data: %s", format_text(&pd[data_offset], data_len));
329                 data_len = 0;
330                 data_offset = offset;
331               }
332               
333               /*
334                * Now interpret the command.
335                */
336               offset = telnet_command(telnet_tree, pd, offset);
337               data_offset = offset;
338             }
339             else {
340               data_len++;
341               offset++;
342             }
343           }
344
345           /*
346            * We've reached the end of the buffer.
347            * If there's any data left, add it to the tree.
348            */
349           if (data_len > 0) {
350             proto_tree_add_text(telnet_tree, NullTVB, data_offset, data_len, "Data: %s",
351                         format_text(&pd[data_offset], data_len));
352           }
353         }
354 }
355
356 void
357 proto_register_telnet(void)
358 {
359 /*        static hf_register_info hf[] = {
360                 { &variable,
361                 { "Name",           "telnet.abbreviation", TYPE, VALS_POINTER }},
362         };*/
363         static gint *ett[] = {
364                 &ett_telnet,
365                 &ett_telnet_subopt,
366         };
367
368         proto_telnet = proto_register_protocol("Telnet", "telnet");
369  /*       proto_register_field_array(proto_telnet, hf, array_length(hf));*/
370         proto_register_subtree_array(ett, array_length(ett));
371 }
372
373 void
374 proto_reg_handoff_telnet(void)
375 {
376         dissector_add("tcp.port", TCP_PORT_TELNET, dissect_telnet);
377 }