The hash table merely associates data structures with conversations,
[obnox/wireshark/wip.git] / 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: packet-rlogin.c,v 1.22 2001/11/03 00:58:49 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
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 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35
36 #ifdef HAVE_NETINET_IN_H
37 # include <netinet/in.h>
38 #endif
39
40 #include <stdio.h>
41 #include <string.h>
42 #include <glib.h>
43
44 #include "packet.h"
45 #include "conversation.h"
46
47 #include "packet-tcp.h"
48
49 #define TCP_PORT_RLOGIN 513
50
51 static int proto_rlogin = -1;
52
53 static int ett_rlogin = -1;
54 static int ett_rlogin_window = -1;
55 static int ett_rlogin_user_info = -1;
56 static int ett_rlogin_window_rows = -1;
57 static int ett_rlogin_window_cols = -1;
58 static int ett_rlogin_window_x_pixels = -1;
59 static int ett_rlogin_window_y_pixels = -1;
60
61 static int hf_user_info = -1;
62 static int hf_window_info = -1;
63 static int hf_window_info_rows = -1;
64 static int hf_window_info_cols = -1;
65 static int hf_window_info_x_pixels = -1;
66 static int hf_window_info_y_pixels = -1;
67
68 #define RLOGIN_PORT 513
69
70 #define NAME_LEN 32
71
72 typedef struct {
73         int     state;
74         guint32 info_framenum;
75         char    name[ NAME_LEN];
76         
77 } rlogin_hash_entry_t;
78
79 #define NONE            0
80 #define USER_INFO_WAIT  1
81 #define DONE            2
82 #define BAD             2
83
84 static GMemChunk *rlogin_vals = NULL;
85
86 #define rlogin_hash_init_count 20
87
88 static guint32 last_abs_sec = 0;
89 static guint32 last_abs_usec= 0;
90
91 static void
92 rlogin_init(void)
93 {
94
95 /* Routine to initialize rlogin protocol before each capture or filter pass. */
96 /* Release any memory if needed.  Then setup the memory chunks.         */
97
98         last_abs_sec = 0;
99         last_abs_usec= 0;
100
101         if (rlogin_vals)
102                 g_mem_chunk_destroy(rlogin_vals);
103
104         rlogin_vals = g_mem_chunk_new("rlogin_vals",
105                 sizeof( rlogin_hash_entry_t),
106                 rlogin_hash_init_count * sizeof( rlogin_hash_entry_t),
107                 G_ALLOC_AND_FREE);
108 }
109
110
111 /**************** Decoder State Machine ******************/
112
113 static void  
114 rlogin_state_machine( rlogin_hash_entry_t *hash_info, tvbuff_t *tvb,
115         packet_info *pinfo)
116 {
117         guint length;
118         gint stringlen;
119
120 /* rlogin stream decoder */
121 /* Just watched for second packet from client with the user name and    */
122 /* terminal type information.                                           */
123
124
125         if ( pinfo->destport != RLOGIN_PORT)   /* not from client */
126                 return;
127                                                 /* exit if not needed */
128         if (( hash_info->state == DONE) || (  hash_info->state == BAD))
129                 return;
130                 
131                                                 /* test timestamp */
132         if (( last_abs_sec > pinfo->fd->abs_secs) || 
133             (( last_abs_sec == pinfo->fd->abs_secs) &&
134              ( last_abs_usec >= pinfo->fd->abs_usecs)))
135                 return;
136
137         last_abs_sec = pinfo->fd->abs_secs;             /* save timestamp */
138         last_abs_usec = pinfo->fd->abs_usecs;
139
140         length = tvb_length(tvb);
141         if ( length == 0)                               /* exit if no data */
142                 return;   
143
144         if ( hash_info->state == NONE){                 /* new connection*/
145                 if (tvb_get_guint8(tvb, 0) != '\0') {
146                         /*
147                          * We expected a NUL, but didn't get one; quit.
148                          */
149                         hash_info->state = DONE;
150                         return;
151                 }
152                 else {
153                         if (length <= 1)                /* if no data   */
154                                 hash_info->state = USER_INFO_WAIT;
155                         else {
156                                 hash_info->state = DONE;
157                                 hash_info->info_framenum = pinfo->fd->num;
158                         }       
159                 }
160         }                                       /* expect user data here */
161 /*$$$ may need to do more checking here */      
162         else if ( hash_info->state == USER_INFO_WAIT) {
163                 hash_info->state = DONE;        
164                 hash_info->info_framenum = pinfo->fd->num;
165                                                         /* save name for later*/
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                 tvb_memcpy(tvb, (guint8 *)hash_info->name, 0, stringlen);
172                 hash_info->name[stringlen] = '\0';
173                 
174                 if (check_col(pinfo->fd, COL_INFO))     /* update summary */
175                         col_append_str(pinfo->fd, COL_INFO,
176                             ", User information");
177         }               
178 }
179
180 static void rlogin_display( rlogin_hash_entry_t *hash_info, tvbuff_t *tvb,
181         packet_info *pinfo, proto_tree *tree, struct tcpinfo *tcpinfo)
182 {
183 /* Display the proto tree */
184         int             offset = 0;
185         proto_tree      *rlogin_tree, *user_info_tree, *window_tree;
186         proto_item      *ti;
187         guint           length;
188         int             str_len;
189         gint            ti_offset;
190         proto_item      *user_info_item,  *window_info_item;
191
192         ti = proto_tree_add_item( tree, proto_rlogin, tvb, 0,
193                         tvb_length(tvb), FALSE);
194
195         rlogin_tree = proto_item_add_subtree(ti, ett_rlogin);
196
197         length = tvb_length(tvb);
198         if ( length == 0)                       /* exit if no captured data */
199                 return;
200
201         /*
202          * XXX - this works only if the urgent pointer points to something
203          * in this segment; to make it work if the urgent pointer points
204          * to something past this segment, we'd have to remember the urgent
205          * pointer setting for this conversation.
206          */
207         if ( tcpinfo->urgent &&                 /* if urgent pointer set */
208              length >= tcpinfo->urgent_pointer) {       /* and it's in this frame */
209
210                 int urgent_offset = tcpinfo->urgent_pointer - 1;
211                 guint8 Temp = tvb_get_guint8(tvb, urgent_offset);
212                 
213                 if (urgent_offset > offset)     /* check for data in front */
214                         proto_tree_add_text( rlogin_tree, tvb, offset,
215                             urgent_offset, "Data");
216                 
217                 proto_tree_add_text( rlogin_tree, tvb, urgent_offset, 1,
218                                 "Control byte: %u (%s)",
219                                 Temp,
220                                 (Temp == 0x02) ? "Clear buffer" :
221                                 (Temp == 0x10) ? "Raw mode" :
222                                 (Temp == 0x20) ? "Cooked mode" :
223                                 (Temp == 0x80) ? "Window size request" :
224                                 "Unknown");
225                 offset = urgent_offset + 1;     /* adjust offset */
226         }
227         else if ( tvb_get_guint8(tvb, offset) == '\0'){   /* startup */
228                 if ( pinfo->srcport== RLOGIN_PORT)   /* from server */
229                         proto_tree_add_text(rlogin_tree, tvb, offset, 1,
230                                         "Startup info received flag (0x00)");
231                                         
232                 else 
233                         proto_tree_add_text(rlogin_tree, tvb, offset, 1,
234                                         "Client Startup Flag (0x00)");
235                 ++offset;
236         }
237                 
238         if (!tvb_offset_exists(tvb, offset))
239                 return; /* No more data to check */
240
241         if ( hash_info->info_framenum == pinfo->fd->num){
242                 /*
243                  * First frame of conversation, hence user info?
244                  */
245                 user_info_item = proto_tree_add_item( rlogin_tree, hf_user_info, tvb,
246                         offset, tvb_length_remaining(tvb, offset), FALSE);
247
248                 /*
249                  * Do server user name.
250                  */
251                 str_len = tvb_strsize(tvb, offset);
252                 user_info_tree = proto_item_add_subtree( user_info_item,
253                         ett_rlogin_user_info);
254                 proto_tree_add_text(user_info_tree, tvb, offset, str_len,
255                                 "Server User Name: %.*s", str_len - 1,
256                                 tvb_get_ptr(tvb, offset, str_len - 1));
257                 offset += str_len;
258
259                 /*
260                  * Do client user name.
261                  */
262                 str_len = tvb_strsize(tvb, offset);
263                 proto_tree_add_text(user_info_tree, tvb, offset, str_len,
264                                 "Client User Name: %.*s", str_len - 1,
265                                 tvb_get_ptr(tvb, offset, str_len - 1));
266                 offset += str_len;
267                 
268                 /*
269                  * Do terminal type/speed.
270                  */
271                 str_len = tvb_strsize(tvb, offset);
272                 proto_tree_add_text(user_info_tree, tvb, offset, str_len,
273                                 "Terminal Type/Speed: %.*s", str_len - 1,
274                                 tvb_get_ptr(tvb, offset, str_len - 1));
275                 offset += str_len;
276         }
277
278         if (!tvb_offset_exists(tvb, offset))
279                 return; /* No more data to check */
280
281 /* test for terminal information, the data will have 2 0xff bytes */
282
283                                                 /* look for first 0xff byte */
284         ti_offset = tvb_find_guint8(tvb, offset, -1, 0xff);
285                 
286         if (ti_offset != -1 &&
287             tvb_bytes_exist(tvb, ti_offset + 1, 1) &&
288             tvb_get_guint8(tvb, ti_offset + 1) == 0xff) {
289                 /*
290                  * Found terminal info.
291                  */
292                 if (ti_offset > offset) {
293                         /*
294                          * There's data before the terminal info.
295                          */
296                         proto_tree_add_text( rlogin_tree, tvb, offset,
297                                 (ti_offset - offset), "Data");
298                         offset = ti_offset;
299                 }
300
301                 window_info_item = proto_tree_add_item(rlogin_tree,
302                                 hf_window_info, tvb, offset, 12, FALSE );
303                         
304                 window_tree = proto_item_add_subtree(window_info_item,
305                                          ett_rlogin_window);
306
307                 proto_tree_add_text(window_tree, tvb, offset, 2, 
308                         "Magic Cookie: (0xff, 0xff)");
309                 offset += 2;
310                                         
311                 proto_tree_add_text(window_tree, tvb, offset, 2, 
312                         "Window size marker: 'ss'");
313                 offset += 2;
314
315                 proto_tree_add_item(window_tree, hf_window_info_rows, tvb,
316                         offset, 2, FALSE);
317                 offset += 2;
318
319                 proto_tree_add_item(window_tree, hf_window_info_cols, tvb,
320                         offset, 2, FALSE);
321                 offset += 2;
322
323                 proto_tree_add_item(window_tree, hf_window_info_x_pixels, tvb,
324                         offset, 2, FALSE);
325                 offset += 2;
326
327                 proto_tree_add_item(window_tree, hf_window_info_y_pixels, tvb,
328                         offset, 2, FALSE);
329                 offset += 2;
330         }
331                         
332         if (tvb_offset_exists(tvb, offset)) {
333                 /*
334                  * There's more data in the frame.
335                  */
336                 proto_tree_add_text(rlogin_tree, tvb, offset,
337                     tvb_length_remaining(tvb, offset), "Data");
338         }
339 }       
340
341 static void
342 dissect_rlogin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
343 {
344         struct tcpinfo *tcpinfo = pinfo->private_data;
345         conversation_t *conversation;
346         rlogin_hash_entry_t *hash_info;
347         guint length;
348         gint ti_offset;
349
350                                                 /* Lookup this connection*/
351         conversation = find_conversation( &pinfo->src, &pinfo->dst,
352                 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
353
354         if ( !conversation) {
355                 conversation = conversation_new( &pinfo->src, &pinfo->dst,
356                         pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
357         }
358         hash_info = conversation_get_proto_data(conversation, proto_rlogin);
359         if ( !hash_info) {
360                 hash_info = g_mem_chunk_alloc(rlogin_vals);
361                 hash_info->state = NONE;
362                 hash_info->info_framenum = 0;   /* no frame has the number 0 */
363                 hash_info->name[ 0] = 0;
364                 conversation_add_proto_data(conversation, proto_rlogin,
365                         hash_info);
366         }
367         
368         if (check_col(pinfo->fd, COL_PROTOCOL))         /* update protocol  */
369                 col_set_str(pinfo->fd, COL_PROTOCOL, "Rlogin");
370
371         if (check_col(pinfo->fd, COL_INFO)){            /* display packet info*/
372
373                 char temp[1000];
374                 
375                 col_clear(pinfo->fd, COL_INFO);
376                 if ( hash_info->name[0]) {
377                         strcpy( temp, "User name: ");
378                         strcat( temp, hash_info->name);
379                         strcat( temp, ", ");
380                 }
381                 else 
382                         temp[0] = 0;
383
384                 length = tvb_length(tvb);
385                 if (length != 0) {
386                         if ( tvb_get_guint8(tvb, 0) == '\0')
387                                 strcat( temp, "Start Handshake"); 
388                         else if ( tcpinfo->urgent &&
389                                   length >= tcpinfo->urgent_pointer )
390                                 strcat( temp, "Control Message"); 
391
392                         else {                  /* check for terminal info */
393                                 ti_offset = tvb_find_guint8(tvb, 0, -1, 0xff);
394                                 if (ti_offset != -1 &&
395                                     tvb_bytes_exist(tvb, ti_offset + 1, 1) &&
396                                     tvb_get_guint8(tvb, ti_offset + 1) == 0xff)
397                                         strcat( temp, "Terminal Info");
398                                 else {
399                                         int i;
400                                         int bytes_to_copy;
401
402                                         strcat( temp, "Data: ");
403                                         i = strlen( temp);
404                                         bytes_to_copy = tvb_length(tvb);
405                                         if (bytes_to_copy > 128)
406                                                 bytes_to_copy = 128;
407                                         tvb_memcpy(tvb, (guint8 *)&temp[i], 0,
408                                             bytes_to_copy);
409                                         temp[i + bytes_to_copy] = '\0';
410                                 }
411                         }
412                 }               
413
414                 col_add_str(pinfo->fd, COL_INFO, temp);
415         }
416
417         rlogin_state_machine( hash_info, tvb, pinfo);
418
419         if ( tree)                              /* if proto tree, decode data */
420                 rlogin_display( hash_info, tvb, pinfo, tree, tcpinfo);
421 }
422
423
424 void
425 proto_register_rlogin( void){
426
427 /* Prep the rlogin protocol, for now, just register it  */
428
429         static gint *ett[] = {
430                 &ett_rlogin,
431                 &ett_rlogin_window,
432                 &ett_rlogin_window_rows,
433                 &ett_rlogin_window_cols,
434                 &ett_rlogin_window_x_pixels,
435                 &ett_rlogin_window_y_pixels,
436                 &ett_rlogin_user_info
437         };
438         
439         static hf_register_info hf[] = {
440
441                 { &hf_user_info,
442                         { "User Info", "rlogin.user_info", FT_NONE, BASE_NONE,
443                                  NULL, 0x0, "", HFILL
444                         }
445                 },
446                 { &hf_window_info,
447                         { "Window Info", "rlogin.window_size", FT_NONE, BASE_NONE,
448                                  NULL, 0x0, "", HFILL
449                         }
450                 },
451                 { &hf_window_info_rows,
452                         { "Rows", "rlogin.window_size.rows", FT_UINT16, BASE_DEC,
453                                  NULL, 0x0, "", HFILL
454                         }
455                 },
456                 { &hf_window_info_cols,
457                         { "Columns", "rlogin.window_size.cols", FT_UINT16, BASE_DEC,
458                                  NULL, 0x0, "", HFILL
459                         }
460                 },
461                 { &hf_window_info_x_pixels,
462                         { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16, BASE_DEC,
463                                  NULL, 0x0, "", HFILL
464                         }
465                 },
466                 { &hf_window_info_y_pixels,
467                         { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16, BASE_DEC,
468                                  NULL, 0x0, "", HFILL
469                         }
470                 }
471         };
472
473         proto_rlogin = proto_register_protocol (
474                 "Rlogin Protocol", "Rlogin", "rlogin");
475
476         proto_register_field_array(proto_rlogin, hf, array_length(hf));
477         proto_register_subtree_array(ett, array_length(ett));  
478
479         register_init_routine( &rlogin_init);   /* register re-init routine */
480 }
481
482 void
483 proto_reg_handoff_rlogin(void) {
484
485         /* dissector install routine */ 
486  
487         dissector_add("tcp.port", TCP_PORT_RLOGIN, dissect_rlogin,
488             proto_rlogin);
489 }