When displaying Telnet data, split it into lines (perhaps not ideal if
[metze/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.17 2000/09/29 19:06:12 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, const u_char *pd,
125                 int start_offset)
126 {
127   proto_tree *ti, *option_tree;
128   int offset = start_offset;
129   int subneg_len, req;
130   gboolean not_found = TRUE;
131   const u_char *opt;
132
133   offset += 2;  /* skip IAC and SB */
134
135   /* Figure out the option and type */
136   if (pd[offset] > NOPTIONS)
137     opt = "<unknown option>";
138   else
139     opt = options[pd[offset]];
140   offset++;
141   req = pd[offset];
142   offset++;
143
144   while (offset < pi.captured_len && not_found) {  
145     if (pd[offset] == TN_IAC)
146       not_found = FALSE;
147     else
148       offset++;
149   }
150
151   subneg_len = offset - start_offset;
152
153   ti = proto_tree_add_text(telnet_tree, NullTVB, start_offset, subneg_len,
154                         "Suboption Begin: %s", opt);
155
156   option_tree = proto_item_add_subtree(ti, ett_telnet_subopt);
157
158   proto_tree_add_text(option_tree, NullTVB, start_offset + 2, 2,
159                         "%s %s", (req ? "Send your" : "Here's my"), opt);
160
161   if (req == 0) {  /* Add the value */
162     proto_tree_add_text(option_tree, NullTVB, start_offset + 4, subneg_len - 4,
163         "Value: %s", format_text(&pd[start_offset + 4], subneg_len - 4));
164   }
165   return offset;
166 }
167
168 static int
169 telnet_will_wont_do_dont(proto_tree *telnet_tree, const u_char *pd,
170                         int start_offset, char *type)
171 {
172   int offset = start_offset;
173   const char *opt;
174
175   offset += 2;  /* skip IAC and WILL,WONT,DO,DONT} */
176   if (pd[offset] > NOPTIONS)
177     opt = "<unknown option>";
178   else
179     opt = options[pd[offset]];
180   offset++;
181                       
182   proto_tree_add_text(telnet_tree, NullTVB, start_offset, 3,
183                         "Command: %s %s", type, opt);
184   return offset;
185 }
186
187 static int
188 telnet_command(proto_tree *telnet_tree, const u_char *pd, int start_offset)
189 {
190   int offset = start_offset;
191   u_char optcode;
192   
193   offset += 1;  /* skip IAC */
194   optcode = pd[offset];
195   offset++;
196   switch(optcode) {
197
198   case TN_EOF:
199     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
200                         "Command: End of File");
201     break;
202
203   case TN_SUSP:
204     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
205                         "Command: Suspend Current Process");
206     break;
207
208   case TN_ABORT:
209     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
210                         "Command: Abort Process");
211     break;
212
213   case TN_EOR:
214     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
215                         "Command: End of Record");
216     break;
217
218   case TN_SE:
219     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
220                         "Command: Suboption End");
221     break;
222
223   case TN_NOP:
224     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
225                         "Command: No Operation");
226     break;
227
228   case TN_DM:
229     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
230                         "Command: Data Mark");
231     break;
232
233   case TN_BRK:
234     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
235                         "Command: Break");
236     break;
237
238   case TN_IP:
239     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
240                         "Command: Interrupt Process");
241     break;
242
243   case TN_AO:
244     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
245                         "Command: Abort Output");
246     break;
247
248   case TN_AYT:
249     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
250                         "Command: Are You There?");
251     break;
252
253   case TN_EC:
254     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
255                         "Command: Escape Character");
256     break;
257
258   case TN_EL:
259     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
260                         "Command: Erase Line");
261     break;
262
263   case TN_GA:
264     proto_tree_add_text(telnet_tree, NullTVB, start_offset, 2,
265                         "Command: Go Ahead");
266     break;
267
268   case TN_SB:
269     offset = telnet_sub_option(telnet_tree, pd, start_offset);
270     break;
271
272   case TN_WILL:
273     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
274                                         "Will");
275     break;
276
277   case TN_WONT:
278     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
279                                         "Won't");
280     break;
281
282   case TN_DO:
283     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
284                                         "Do");
285     break;
286
287   case TN_DONT:
288     offset = telnet_will_wont_do_dont(telnet_tree, pd, start_offset,
289                                         "Don't");
290     break;
291   }
292
293   return offset;
294 }
295
296 static void
297 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, const u_char *pd,
298     int offset, int len)
299 {
300   const u_char *data, *dataend;
301   const u_char *lineend, *eol;
302   int linelen;
303
304   data = &pd[offset];
305   dataend = data + len;
306   while (data < dataend) {
307     /*
308      * Find the end of the line.
309      */
310     lineend = find_line_end(data, dataend, &eol);
311     linelen = lineend - data;
312
313     proto_tree_add_text(tree, NullTVB, offset, linelen,
314                         "Data: %s", format_text(data, linelen));
315     offset += linelen;
316     data = lineend;
317   }
318 }
319
320 static void
321 dissect_telnet(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
322 {
323         proto_tree      *telnet_tree, *ti;
324
325         OLD_CHECK_DISPLAY_AS_DATA(proto_telnet, pd, offset, fd, tree);
326
327         if (check_col(fd, COL_PROTOCOL))
328                 col_add_str(fd, COL_PROTOCOL, "TELNET");
329
330         if (check_col(fd, COL_INFO))
331           col_add_fstr(fd, COL_INFO, "Telnet Data ...");
332
333         if (tree) {
334           int data_offset;
335           int data_len;
336
337           ti = proto_tree_add_item(tree, proto_telnet, NullTVB, offset, END_OF_FRAME, FALSE);
338           telnet_tree = proto_item_add_subtree(ti, ett_telnet);
339
340           data_offset = offset;
341           data_len = 0;
342
343           /*
344            * Scan through the buffer looking for an IAC byte.
345            */
346           while (offset < pi.captured_len) {
347             if (pd[offset] == TN_IAC) {
348               /*
349                * We found an IAC byte.
350                * If there's any data before it, add that data to the
351                * tree, a line at a time.
352                */
353               if (data_len > 0) {
354                 telnet_add_text(telnet_tree, NullTVB, pd, data_offset, data_len);
355                 data_len = 0;
356                 data_offset = offset;
357               }
358               
359               /*
360                * Now interpret the command.
361                */
362               offset = telnet_command(telnet_tree, pd, offset);
363               data_offset = offset;
364             }
365             else {
366               data_len++;
367               offset++;
368             }
369           }
370
371           /*
372            * We've reached the end of the buffer.
373            * If there's any data left, add it to the tree.
374            */
375           if (data_len > 0)
376             telnet_add_text(telnet_tree, NullTVB, pd, data_offset, data_len);
377         }
378 }
379
380 void
381 proto_register_telnet(void)
382 {
383 /*        static hf_register_info hf[] = {
384                 { &variable,
385                 { "Name",           "telnet.abbreviation", TYPE, VALS_POINTER }},
386         };*/
387         static gint *ett[] = {
388                 &ett_telnet,
389                 &ett_telnet_subopt,
390         };
391
392         proto_telnet = proto_register_protocol("Telnet", "telnet");
393  /*       proto_register_field_array(proto_telnet, hf, array_length(hf));*/
394         proto_register_subtree_array(ett, array_length(ett));
395 }
396
397 void
398 proto_reg_handoff_telnet(void)
399 {
400         old_dissector_add("tcp.port", TCP_PORT_TELNET, dissect_telnet);
401 }