change the signature that asn2wrs generates for functions to marm all parameters...
[obnox/wireshark/wip.git] / epan / dissectors / packet-rlogin.c
1 /* packet-rlogin.c
2  * Routines for unix rlogin packet dissection
3  * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * Based upon RFC-1282 - BSD Rlogin
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 #include <string.h>
34 #include <glib.h>
35
36 #include <epan/packet.h>
37 #include <epan/conversation.h>
38 #include <epan/emem.h>
39
40 #include "packet-tcp.h"
41
42 #define RLOGIN_PORT 513
43
44 static int proto_rlogin = -1;
45
46 static int ett_rlogin = -1;
47 static int ett_rlogin_window = -1;
48 static int ett_rlogin_user_info = -1;
49 static int ett_rlogin_window_rows = -1;
50 static int ett_rlogin_window_cols = -1;
51 static int ett_rlogin_window_x_pixels = -1;
52 static int ett_rlogin_window_y_pixels = -1;
53
54 static int hf_user_info = -1;
55 static int hf_client_startup_flag = -1;
56 static int hf_startup_info_received_flag = -1;
57 static int hf_user_info_client_user_name = -1;
58 static int hf_user_info_server_user_name = -1;
59 static int hf_user_info_terminal_type = -1;
60 static int hf_user_info_terminal_speed = -1;
61 static int hf_control_message = -1;
62 static int hf_window_info = -1;
63 static int hf_window_info_ss = -1;
64 static int hf_window_info_rows = -1;
65 static int hf_window_info_cols = -1;
66 static int hf_window_info_x_pixels = -1;
67 static int hf_window_info_y_pixels = -1;
68 static int hf_data = -1;
69
70 static const value_string control_message_vals[] =
71 {
72     { 0x02,     "Clear buffer"        },
73     { 0x10,     "Raw mode"            },
74     { 0x20,     "Cooked mode"         },
75     { 0x80,     "Window size request" },
76     { 0, NULL }
77 };
78
79
80 typedef enum  {
81         NONE=0,
82         USER_INFO_WAIT=1,
83         DONE=2
84 } session_state_t;
85
86 #define NAME_LEN 32
87 typedef struct {
88         session_state_t  state;
89         guint32          info_framenum;
90         char             user_name[NAME_LEN];
91 } rlogin_hash_entry_t;
92
93
94
95 /* Decoder State Machine.  Currently only used to snoop on
96    client-user-name as sent by the client up connection establishment.
97 */
98 static void
99 rlogin_state_machine(rlogin_hash_entry_t *hash_info, tvbuff_t *tvb, packet_info *pinfo)
100 {
101         guint length;
102         gint stringlen;
103
104         /* Won't change state if already seen this packet */
105         if (pinfo->fd->flags.visited)
106         {
107                 return;
108         }
109
110         /* rlogin stream decoder */
111         /* Just watch for the second packet from client with the user name and */
112         /* terminal type information. */
113
114         if (pinfo->destport != RLOGIN_PORT)
115         {
116                 return;
117         }
118         
119         /* exit if already passed username in conversation */
120         if (hash_info->state == DONE)
121         {
122                 return;
123         }
124
125         /* exit if no data */
126         length = tvb_length(tvb);
127         if (length == 0)
128         {
129                 return;
130         }
131
132         if (hash_info->state == NONE)
133         {
134                 /* new connection*/
135                 if (tvb_get_guint8(tvb, 0) != '\0')
136                 {
137                         /* We expected a null, but didn't get one; quit. */
138                         hash_info->state = DONE;
139                         return;
140                 }
141                 else 
142                 {
143                         if (length <= 1)
144                         {
145                                 /* Still waiting for data */
146                                 hash_info->state = USER_INFO_WAIT;
147                         }
148                         else
149                         {
150                                 /* Have info, store frame number */
151                                 hash_info->state = DONE;
152                                 hash_info->info_framenum = pinfo->fd->num;
153                         }
154                 }
155         }
156         /* expect user data here */
157         /* TODO: may need to do more checking here? */
158         else
159         if (hash_info->state == USER_INFO_WAIT)
160         {
161                 /* Store frame number here */
162                 hash_info->state = DONE;
163                 hash_info->info_framenum = pinfo->fd->num;
164
165                 /* Work out length of string to copy */
166                 stringlen = tvb_strnlen(tvb, 0, NAME_LEN);
167                 if (stringlen == -1)
168                         stringlen = NAME_LEN - 1;   /* no '\0' found */
169                 else if (stringlen > NAME_LEN - 1)
170                         stringlen = NAME_LEN - 1;   /* name too long */
171
172                 /* Copy and terminate string into hash name */
173                 tvb_memcpy(tvb, (guint8 *)hash_info->user_name, 0, stringlen);
174                 hash_info->user_name[stringlen] = '\0';
175
176                 if (check_col(pinfo->cinfo, COL_INFO))
177                 {
178                         col_append_str(pinfo->cinfo, COL_INFO, ", (User information)");
179                 }
180         }
181 }
182
183 /* Dissect details of packet */
184 static void rlogin_display(rlogin_hash_entry_t *hash_info,
185                            tvbuff_t *tvb,
186                            packet_info *pinfo,
187                            proto_tree *tree,
188                            struct tcpinfo *tcpinfo)
189 {
190         /* Display the proto tree */
191         int             offset = 0;
192         proto_tree      *rlogin_tree, *user_info_tree, *window_tree;
193         proto_item      *ti;
194         guint           length;
195         int             str_len;
196         gint            ti_offset;
197         proto_item      *user_info_item, *window_info_item;
198
199         /* Create rlogin subtree */
200         ti = proto_tree_add_item(tree, proto_rlogin, tvb, 0, -1, FALSE);
201         rlogin_tree = proto_item_add_subtree(ti, ett_rlogin);
202
203         /* Return if data empty */
204         length = tvb_length(tvb);
205         if (length == 0)
206         {
207                 return;
208         }
209
210         /*
211          * XXX - this works only if the urgent pointer points to something
212          * in this segment; to make it work if the urgent pointer points
213          * to something past this segment, we'd have to remember the urgent
214          * pointer setting for this conversation.
215          */
216         if (tcpinfo->urgent &&                 /* if urgent pointer set */
217             length >= tcpinfo->urgent_pointer) /* and it's in this frame */
218         {
219                 /* Get urgent byte into Temp */
220                 int urgent_offset = tcpinfo->urgent_pointer - 1;
221                 guint8 control_byte;
222
223                 /* Check for text data in front */
224                 if (urgent_offset > offset)
225                 {
226                         proto_tree_add_item(rlogin_tree, hf_data, tvb, offset, urgent_offset, FALSE);
227                 }
228
229                 /* Show control byte */
230                 proto_tree_add_item(rlogin_tree, hf_control_message, tvb,
231                                     urgent_offset, 1, FALSE);
232                 control_byte = tvb_get_guint8(tvb, urgent_offset);
233                 if (check_col(pinfo->cinfo, COL_INFO))
234                 {
235                         col_append_fstr(pinfo->cinfo, COL_INFO,
236                                        " (%s)", val_to_str(control_byte, control_message_vals, "Unknown"));
237                 }
238
239                 offset = urgent_offset + 1; /* adjust offset */
240         }
241         else
242         if (tvb_get_guint8(tvb, offset) == '\0')
243         {
244                 /* Startup */
245                 if (pinfo->srcport == RLOGIN_PORT)   /* from server */
246                 {
247                         proto_tree_add_item(rlogin_tree, hf_startup_info_received_flag,
248                                             tvb, offset, 1, FALSE);
249                 }
250                 else
251                 {
252                         proto_tree_add_item(rlogin_tree, hf_client_startup_flag,
253                                             tvb, offset, 1, FALSE);
254                 }
255                 ++offset;
256         }
257
258         if (!tvb_offset_exists(tvb, offset))
259         {
260                 /* No more data to check */
261                 return;
262         }
263
264         if (hash_info->info_framenum == pinfo->fd->num)
265         {
266                 gint info_len;
267                 gint slash_offset;
268
269                 /* First frame of conversation, assume user info... */
270                 
271                 info_len = tvb_length_remaining(tvb, offset);
272
273                 /* User info tree */
274                 user_info_item = proto_tree_add_string_format(rlogin_tree, hf_user_info, tvb,
275                                                               offset, info_len, FALSE,
276                                                               "User info (%s)",
277                                                               tvb_format_text(tvb, offset, info_len));
278                 user_info_tree = proto_item_add_subtree(user_info_item,
279                                                         ett_rlogin_user_info);
280
281                 /* Client user name. */
282                 str_len = tvb_strsize(tvb, offset);
283                 proto_tree_add_item(user_info_tree, hf_user_info_client_user_name,
284                                     tvb, offset, str_len, FALSE);
285                 offset += str_len;
286
287                 /* Server user name. */
288                 str_len = tvb_strsize(tvb, offset);
289                 proto_tree_add_item(user_info_tree, hf_user_info_server_user_name,
290                                     tvb, offset, str_len, FALSE);
291                 offset += str_len;
292
293                 /* Terminal type/speed. */
294                 slash_offset = tvb_find_guint8(tvb, offset, -1, '/');
295                 if (slash_offset != -1)
296                 {
297                         /* Terminal type */
298                         proto_tree_add_item(user_info_tree, hf_user_info_terminal_type,
299                                             tvb, offset, slash_offset-offset, FALSE);
300                         offset = slash_offset + 1;
301                         
302                         /* Terminal speed */
303                         str_len = tvb_strsize(tvb, offset);
304                         proto_tree_add_uint(user_info_tree, hf_user_info_terminal_speed,
305                                             tvb, offset, str_len,
306                                             atoi(tvb_format_text(tvb, offset, str_len)));
307                         offset += str_len;
308                 }
309         }
310
311         if (!tvb_offset_exists(tvb, offset))
312         {
313                 /* No more data to check */
314                 return;
315         }
316
317         /* Test for terminal information, the data will have 2 0xff bytes */
318         /* look for first 0xff byte */
319         ti_offset = tvb_find_guint8(tvb, offset, -1, 0xff);
320
321         /* Next byte must also be 0xff */
322         if (ti_offset != -1 &&
323             tvb_bytes_exist(tvb, ti_offset + 1, 1) &&
324             tvb_get_guint8(tvb, ti_offset + 1) == 0xff)
325         {
326                 guint16 rows, columns;
327                 
328                 /* Have found terminal info. */
329                 if (ti_offset > offset)
330                 {
331                         /* There's data before the terminal info. */
332                         proto_tree_add_item(rlogin_tree, hf_data, tvb,
333                                             offset, ti_offset - offset, FALSE);
334                 }
335
336                 /* Create window info tree */
337                 window_info_item =
338                         proto_tree_add_item(rlogin_tree, hf_window_info, tvb, offset, 12, FALSE);
339                 window_tree = proto_item_add_subtree(window_info_item, ett_rlogin_window);
340
341                 /* Cookie */
342                 proto_tree_add_text(window_tree, tvb, offset, 2, "Magic Cookie: (0xff, 0xff)");
343                 offset += 2;
344
345                 /* These bytes should be "ss" */
346                 proto_tree_add_item(window_tree, hf_window_info_ss, tvb, offset, 2, FALSE);
347                 offset += 2;
348
349                 /* Character rows */
350                 rows = tvb_get_ntohs(tvb, offset);
351                 proto_tree_add_item(window_tree, hf_window_info_rows, tvb,
352                                     offset, 2, FALSE);
353                 offset += 2;
354
355                 /* Characters per row */
356                 columns = tvb_get_ntohs(tvb, offset);
357                 proto_tree_add_item(window_tree, hf_window_info_cols, tvb,
358                                     offset, 2, FALSE);
359                 offset += 2;
360
361                 /* x pixels */
362                 proto_tree_add_item(window_tree, hf_window_info_x_pixels, tvb,
363                                     offset, 2, FALSE);
364                 offset += 2;
365
366                 /* y pixels */
367                 proto_tree_add_item(window_tree, hf_window_info_y_pixels, tvb,
368                                     offset, 2, FALSE);
369                 offset += 2;
370                 
371                 /* Show setting highlights in info column */
372                 if (check_col(pinfo->cinfo, COL_INFO))
373                 {
374                         col_append_fstr(pinfo->cinfo, COL_INFO, " (rows=%u, cols=%u)",
375                                         rows, columns);
376                 }
377         }
378
379         if (tvb_offset_exists(tvb, offset))
380         {
381                 /* There's more data in the frame. */
382                 proto_tree_add_item(rlogin_tree, hf_data, tvb, offset, -1, FALSE);
383         }
384 }
385
386
387 /****************************************************************
388  * Main dissection function
389  ****************************************************************/
390 static void
391 dissect_rlogin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
392 {
393         struct tcpinfo *tcpinfo = pinfo->private_data;
394         conversation_t *conversation;
395         rlogin_hash_entry_t *hash_info;
396         guint length;
397         gint ti_offset;
398
399         /* Get conversation */
400         conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
401                                          pinfo->ptype, pinfo->srcport, pinfo->destport,
402                                      0);
403
404         /* Create if didn't previously exist */
405         if (!conversation)
406         {
407                 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
408                                                 pinfo->ptype, pinfo->srcport, pinfo->destport,
409                                                 0);
410         }
411
412         /* Get or create data associated with this conversation */
413         hash_info = conversation_get_proto_data(conversation, proto_rlogin);
414         if (!hash_info)
415         {
416                 /* Populate new data struct... */
417                 hash_info = se_alloc(sizeof(rlogin_hash_entry_t));
418                 hash_info->state = NONE;
419                 hash_info->info_framenum = 0;  /* no frame has the number 0 */
420                 hash_info->user_name[0] = '\0';
421
422                 /* ... and store in conversation */
423                 conversation_add_proto_data(conversation, proto_rlogin, hash_info);
424         }
425
426         /* Set protocol column text */
427         if (check_col(pinfo->cinfo, COL_PROTOCOL))
428         {
429                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Rlogin");
430         }
431
432         /* Set info column */
433         if (check_col(pinfo->cinfo, COL_INFO))
434         {
435                 /* Show user-name if available */
436                 if (hash_info->user_name[0])
437                 {
438                         col_add_fstr(pinfo->cinfo, COL_INFO,
439                                       "User name: %s, ", hash_info->user_name);
440                 }
441                 else
442                 {
443                         col_clear(pinfo->cinfo, COL_INFO);
444                 }
445
446                 /* Work out packet content summary for display */
447                 length = tvb_length(tvb);
448                 if (length != 0)
449                 {
450                         /* Initial NULL byte represents part of connection handshake */
451                         if (tvb_get_guint8(tvb, 0) == '\0')
452                         {
453                                 col_append_str(pinfo->cinfo, COL_INFO,
454                                                (pinfo->destport == RLOGIN_PORT) ?
455                                                    "Start Handshake" :
456                                                    "Startup info received");
457                         }
458                         else
459                         if (tcpinfo->urgent && length >= tcpinfo->urgent_pointer)
460                         {
461                                 /* Urgent pointer inside current data represents a control message */
462                                 col_append_str(pinfo->cinfo, COL_INFO, "Control Message");
463                         }
464                         else
465                         {
466                                 /* Search for 2 consecutive ff bytes
467                                   (signifies window change control message) */
468                                 ti_offset = tvb_find_guint8(tvb, 0, -1, 0xff);
469                                 if (ti_offset != -1 &&
470                                     tvb_bytes_exist(tvb, ti_offset + 1, 1) &&
471                                     tvb_get_guint8(tvb, ti_offset + 1) == 0xff)
472                                 {
473                                         col_append_str(pinfo->cinfo, COL_INFO, "Terminal Info");
474                                 }
475                                 else
476                                 {
477                                         /* Show any text data in the frame */
478                                         int bytes_to_copy = tvb_length(tvb);
479                                         if (bytes_to_copy > 128)
480                                         {
481                                                 /* Truncate to 128 bytes for display */
482                                                 bytes_to_copy = 128;
483                                         }
484         
485                                         /* Add data into info column */
486                                         col_append_fstr(pinfo->cinfo, COL_INFO,
487                                                         "Data: %s",
488                                                          tvb_format_text(tvb, 0, bytes_to_copy));
489                                 }
490                         }
491                 }
492         }
493
494         /* See if conversation state needs to be updated */
495         rlogin_state_machine(hash_info, tvb, pinfo);
496
497         /* Dissect in detail */
498         rlogin_display(hash_info, tvb, pinfo, tree, tcpinfo);
499 }
500
501
502 void proto_register_rlogin(void)
503 {
504         static gint *ett[] = {
505                 &ett_rlogin,
506                 &ett_rlogin_window,
507                 &ett_rlogin_window_rows,
508                 &ett_rlogin_window_cols,
509                 &ett_rlogin_window_x_pixels,
510                 &ett_rlogin_window_y_pixels,
511                 &ett_rlogin_user_info
512         };
513
514         static hf_register_info hf[] =
515         {
516                 { &hf_user_info,
517                         { "User Info", "rlogin.user_info", FT_STRING, BASE_NONE,
518                                  NULL, 0x0, "", HFILL
519                         }
520                 },
521                 { &hf_client_startup_flag,
522                         { "Client startup flag", "rlogin.client_startup_flag", FT_UINT8, BASE_HEX,
523                                  NULL, 0x0, "", HFILL
524                         }
525                 },
526                 { &hf_startup_info_received_flag,
527                         { "Startup info received flag", "rlogin.startup_info_received_flag", FT_UINT8, BASE_HEX,
528                                  NULL, 0x0, "", HFILL
529                         }
530                 },
531                 { &hf_user_info_client_user_name,
532                         { "Client-user-name", "rlogin.client_user_name", FT_STRING, BASE_NONE,
533                                  NULL, 0x0, "", HFILL
534                         }
535                 },
536                 { &hf_user_info_server_user_name,
537                         { "Server-user-name", "rlogin.server_user_name", FT_STRING, BASE_NONE,
538                                  NULL, 0x0, "", HFILL
539                         }
540                 },
541                 { &hf_user_info_terminal_type,
542                         { "Terminal-type", "rlogin.terminal_type", FT_STRING, BASE_NONE,
543                                  NULL, 0x0, "", HFILL
544                         }
545                 },
546                 { &hf_user_info_terminal_speed,
547                         { "Terminal-speed", "rlogin.terminal_speed", FT_UINT32, BASE_DEC,
548                                  NULL, 0x0, "", HFILL
549                         }
550                 },
551                 { &hf_control_message,
552                         { "Control message", "rlogin.control_message", FT_UINT8, BASE_HEX,
553                                  VALS(control_message_vals), 0x0, "", HFILL
554                         }
555                 },              
556                 { &hf_window_info,
557                         { "Window Info", "rlogin.window_size", FT_NONE, BASE_NONE,
558                                  NULL, 0x0, "", HFILL
559                         }
560                 },
561                 { &hf_window_info_ss,
562                         { "Window size marker", "rlogin.window_size.ss", FT_STRING, BASE_NONE,
563                                  NULL, 0x0, "", HFILL
564                         }
565                 },              
566                 { &hf_window_info_rows,
567                         { "Rows", "rlogin.window_size.rows", FT_UINT16, BASE_DEC,
568                                  NULL, 0x0, "", HFILL
569                         }
570                 },
571                 { &hf_window_info_cols,
572                         { "Columns", "rlogin.window_size.cols", FT_UINT16, BASE_DEC,
573                                  NULL, 0x0, "", HFILL
574                         }
575                 },
576                 { &hf_window_info_x_pixels,
577                         { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16, BASE_DEC,
578                                  NULL, 0x0, "", HFILL
579                         }
580                 },
581                 { &hf_window_info_y_pixels,
582                         { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16, BASE_DEC,
583                                  NULL, 0x0, "", HFILL
584                         }
585                 },
586                 { &hf_data,
587                         { "Data", "rlogin.data", FT_STRING, BASE_NONE,
588                                  NULL, 0x0, "", HFILL
589                         }
590                 }
591         };
592
593         proto_rlogin = proto_register_protocol("Rlogin Protocol", "Rlogin", "rlogin");
594
595         proto_register_field_array(proto_rlogin, hf, array_length(hf));
596         proto_register_subtree_array(ett, array_length(ett));
597 }
598
599 void proto_reg_handoff_rlogin(void)
600 {
601         /* Dissector install routine */
602         dissector_handle_t rlogin_handle = create_dissector_handle(dissect_rlogin,proto_rlogin);
603         dissector_add("tcp.port", RLOGIN_PORT, rlogin_handle);
604 }