Use "tvb_get_ntohieee_float()" to fetch floating-point numbers from the
[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.26 2002/01/24 09:20:51 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 <epan/packet.h>
45 #include <epan/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->cinfo, COL_INFO))  /* update summary */
175                         col_append_str(pinfo->cinfo, 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, -1, FALSE);
193
194         rlogin_tree = proto_item_add_subtree(ti, ett_rlogin);
195
196         length = tvb_length(tvb);
197         if ( length == 0)                       /* exit if no captured data */
198                 return;
199
200         /*
201          * XXX - this works only if the urgent pointer points to something
202          * in this segment; to make it work if the urgent pointer points
203          * to something past this segment, we'd have to remember the urgent
204          * pointer setting for this conversation.
205          */
206         if ( tcpinfo->urgent &&                 /* if urgent pointer set */
207              length >= tcpinfo->urgent_pointer) {       /* and it's in this frame */
208
209                 int urgent_offset = tcpinfo->urgent_pointer - 1;
210                 guint8 Temp = tvb_get_guint8(tvb, urgent_offset);
211                 
212                 if (urgent_offset > offset)     /* check for data in front */
213                         proto_tree_add_text( rlogin_tree, tvb, offset,
214                             urgent_offset, "Data");
215                 
216                 proto_tree_add_text( rlogin_tree, tvb, urgent_offset, 1,
217                                 "Control byte: %u (%s)",
218                                 Temp,
219                                 (Temp == 0x02) ? "Clear buffer" :
220                                 (Temp == 0x10) ? "Raw mode" :
221                                 (Temp == 0x20) ? "Cooked mode" :
222                                 (Temp == 0x80) ? "Window size request" :
223                                 "Unknown");
224                 offset = urgent_offset + 1;     /* adjust offset */
225         }
226         else if ( tvb_get_guint8(tvb, offset) == '\0'){   /* startup */
227                 if ( pinfo->srcport== RLOGIN_PORT)   /* from server */
228                         proto_tree_add_text(rlogin_tree, tvb, offset, 1,
229                                         "Startup info received flag (0x00)");
230                                         
231                 else 
232                         proto_tree_add_text(rlogin_tree, tvb, offset, 1,
233                                         "Client Startup Flag (0x00)");
234                 ++offset;
235         }
236                 
237         if (!tvb_offset_exists(tvb, offset))
238                 return; /* No more data to check */
239
240         if ( hash_info->info_framenum == pinfo->fd->num){
241                 /*
242                  * First frame of conversation, hence user info?
243                  */
244                 user_info_item = proto_tree_add_item( rlogin_tree, hf_user_info, tvb,
245                         offset, -1, FALSE);
246
247                 /*
248                  * Do server user name.
249                  */
250                 str_len = tvb_strsize(tvb, offset);
251                 user_info_tree = proto_item_add_subtree( user_info_item,
252                         ett_rlogin_user_info);
253                 proto_tree_add_text(user_info_tree, tvb, offset, str_len,
254                                 "Server User Name: %.*s", str_len - 1,
255                                 tvb_get_ptr(tvb, offset, str_len - 1));
256                 offset += str_len;
257
258                 /*
259                  * Do client user name.
260                  */
261                 str_len = tvb_strsize(tvb, offset);
262                 proto_tree_add_text(user_info_tree, tvb, offset, str_len,
263                                 "Client User Name: %.*s", str_len - 1,
264                                 tvb_get_ptr(tvb, offset, str_len - 1));
265                 offset += str_len;
266                 
267                 /*
268                  * Do terminal type/speed.
269                  */
270                 str_len = tvb_strsize(tvb, offset);
271                 proto_tree_add_text(user_info_tree, tvb, offset, str_len,
272                                 "Terminal Type/Speed: %.*s", str_len - 1,
273                                 tvb_get_ptr(tvb, offset, str_len - 1));
274                 offset += str_len;
275         }
276
277         if (!tvb_offset_exists(tvb, offset))
278                 return; /* No more data to check */
279
280 /* test for terminal information, the data will have 2 0xff bytes */
281
282                                                 /* look for first 0xff byte */
283         ti_offset = tvb_find_guint8(tvb, offset, -1, 0xff);
284                 
285         if (ti_offset != -1 &&
286             tvb_bytes_exist(tvb, ti_offset + 1, 1) &&
287             tvb_get_guint8(tvb, ti_offset + 1) == 0xff) {
288                 /*
289                  * Found terminal info.
290                  */
291                 if (ti_offset > offset) {
292                         /*
293                          * There's data before the terminal info.
294                          */
295                         proto_tree_add_text( rlogin_tree, tvb, offset,
296                                 (ti_offset - offset), "Data");
297                         offset = ti_offset;
298                 }
299
300                 window_info_item = proto_tree_add_item(rlogin_tree,
301                                 hf_window_info, tvb, offset, 12, FALSE );
302                         
303                 window_tree = proto_item_add_subtree(window_info_item,
304                                          ett_rlogin_window);
305
306                 proto_tree_add_text(window_tree, tvb, offset, 2, 
307                         "Magic Cookie: (0xff, 0xff)");
308                 offset += 2;
309                                         
310                 proto_tree_add_text(window_tree, tvb, offset, 2, 
311                         "Window size marker: 'ss'");
312                 offset += 2;
313
314                 proto_tree_add_item(window_tree, hf_window_info_rows, tvb,
315                         offset, 2, FALSE);
316                 offset += 2;
317
318                 proto_tree_add_item(window_tree, hf_window_info_cols, tvb,
319                         offset, 2, FALSE);
320                 offset += 2;
321
322                 proto_tree_add_item(window_tree, hf_window_info_x_pixels, tvb,
323                         offset, 2, FALSE);
324                 offset += 2;
325
326                 proto_tree_add_item(window_tree, hf_window_info_y_pixels, tvb,
327                         offset, 2, FALSE);
328                 offset += 2;
329         }
330                         
331         if (tvb_offset_exists(tvb, offset)) {
332                 /*
333                  * There's more data in the frame.
334                  */
335                 proto_tree_add_text(rlogin_tree, tvb, offset, -1, "Data");
336         }
337 }       
338
339 static void
340 dissect_rlogin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
341 {
342         struct tcpinfo *tcpinfo = pinfo->private_data;
343         conversation_t *conversation;
344         rlogin_hash_entry_t *hash_info;
345         guint length;
346         gint ti_offset;
347
348                                                 /* Lookup this connection*/
349         conversation = find_conversation( &pinfo->src, &pinfo->dst,
350                 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
351
352         if ( !conversation) {
353                 conversation = conversation_new( &pinfo->src, &pinfo->dst,
354                         pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
355         }
356         hash_info = conversation_get_proto_data(conversation, proto_rlogin);
357         if ( !hash_info) {
358                 hash_info = g_mem_chunk_alloc(rlogin_vals);
359                 hash_info->state = NONE;
360                 hash_info->info_framenum = 0;   /* no frame has the number 0 */
361                 hash_info->name[ 0] = 0;
362                 conversation_add_proto_data(conversation, proto_rlogin,
363                         hash_info);
364         }
365         
366         if (check_col(pinfo->cinfo, COL_PROTOCOL))              /* update protocol  */
367                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Rlogin");
368
369         if (check_col(pinfo->cinfo, COL_INFO)){         /* display packet info*/
370
371                 char temp[1000];
372                 
373                 col_clear(pinfo->cinfo, COL_INFO);
374                 if ( hash_info->name[0]) {
375                         strcpy( temp, "User name: ");
376                         strcat( temp, hash_info->name);
377                         strcat( temp, ", ");
378                 }
379                 else 
380                         temp[0] = 0;
381
382                 length = tvb_length(tvb);
383                 if (length != 0) {
384                         if ( tvb_get_guint8(tvb, 0) == '\0')
385                                 strcat( temp, "Start Handshake"); 
386                         else if ( tcpinfo->urgent &&
387                                   length >= tcpinfo->urgent_pointer )
388                                 strcat( temp, "Control Message"); 
389
390                         else {                  /* check for terminal info */
391                                 ti_offset = tvb_find_guint8(tvb, 0, -1, 0xff);
392                                 if (ti_offset != -1 &&
393                                     tvb_bytes_exist(tvb, ti_offset + 1, 1) &&
394                                     tvb_get_guint8(tvb, ti_offset + 1) == 0xff)
395                                         strcat( temp, "Terminal Info");
396                                 else {
397                                         int i;
398                                         int bytes_to_copy;
399
400                                         strcat( temp, "Data: ");
401                                         i = strlen( temp);
402                                         bytes_to_copy = tvb_length(tvb);
403                                         if (bytes_to_copy > 128)
404                                                 bytes_to_copy = 128;
405                                         tvb_memcpy(tvb, (guint8 *)&temp[i], 0,
406                                             bytes_to_copy);
407                                         temp[i + bytes_to_copy] = '\0';
408                                 }
409                         }
410                 }               
411
412                 col_add_str(pinfo->cinfo, COL_INFO, temp);
413         }
414
415         rlogin_state_machine( hash_info, tvb, pinfo);
416
417         if ( tree)                              /* if proto tree, decode data */
418                 rlogin_display( hash_info, tvb, pinfo, tree, tcpinfo);
419 }
420
421
422 void
423 proto_register_rlogin( void){
424
425 /* Prep the rlogin protocol, for now, just register it  */
426
427         static gint *ett[] = {
428                 &ett_rlogin,
429                 &ett_rlogin_window,
430                 &ett_rlogin_window_rows,
431                 &ett_rlogin_window_cols,
432                 &ett_rlogin_window_x_pixels,
433                 &ett_rlogin_window_y_pixels,
434                 &ett_rlogin_user_info
435         };
436         
437         static hf_register_info hf[] = {
438
439                 { &hf_user_info,
440                         { "User Info", "rlogin.user_info", FT_NONE, BASE_NONE,
441                                  NULL, 0x0, "", HFILL
442                         }
443                 },
444                 { &hf_window_info,
445                         { "Window Info", "rlogin.window_size", FT_NONE, BASE_NONE,
446                                  NULL, 0x0, "", HFILL
447                         }
448                 },
449                 { &hf_window_info_rows,
450                         { "Rows", "rlogin.window_size.rows", FT_UINT16, BASE_DEC,
451                                  NULL, 0x0, "", HFILL
452                         }
453                 },
454                 { &hf_window_info_cols,
455                         { "Columns", "rlogin.window_size.cols", FT_UINT16, BASE_DEC,
456                                  NULL, 0x0, "", HFILL
457                         }
458                 },
459                 { &hf_window_info_x_pixels,
460                         { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16, BASE_DEC,
461                                  NULL, 0x0, "", HFILL
462                         }
463                 },
464                 { &hf_window_info_y_pixels,
465                         { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16, BASE_DEC,
466                                  NULL, 0x0, "", HFILL
467                         }
468                 }
469         };
470
471         proto_rlogin = proto_register_protocol (
472                 "Rlogin Protocol", "Rlogin", "rlogin");
473
474         proto_register_field_array(proto_rlogin, hf, array_length(hf));
475         proto_register_subtree_array(ett, array_length(ett));  
476
477         register_init_routine( &rlogin_init);   /* register re-init routine */
478 }
479
480 void
481 proto_reg_handoff_rlogin(void) {
482
483         /* dissector install routine */ 
484  
485         dissector_handle_t rlogin_handle;
486
487         rlogin_handle = create_dissector_handle(dissect_rlogin, proto_rlogin);
488         dissector_add("tcp.port", TCP_PORT_RLOGIN, rlogin_handle);
489 }