2 * Routines for unix rlogin packet dissection
3 * Copyright 2000, Jeffrey C. Foster <jfoste[AT]woodward.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * Based upon RFC-1282 - BSD Rlogin
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 #include <epan/packet.h>
35 #include <epan/conversation.h>
36 #include <epan/wmem/wmem.h>
38 #include "packet-tcp.h"
40 #define RLOGIN_PORT 513
42 static int proto_rlogin
= -1;
44 static int ett_rlogin
= -1;
45 static int ett_rlogin_window
= -1;
46 static int ett_rlogin_user_info
= -1;
47 static int ett_rlogin_window_rows
= -1;
48 static int ett_rlogin_window_cols
= -1;
49 static int ett_rlogin_window_x_pixels
= -1;
50 static int ett_rlogin_window_y_pixels
= -1;
52 static int hf_user_info
= -1;
53 static int hf_client_startup_flag
= -1;
54 static int hf_startup_info_received_flag
= -1;
55 static int hf_user_info_client_user_name
= -1;
56 static int hf_user_info_server_user_name
= -1;
57 static int hf_user_info_terminal_type
= -1;
58 static int hf_user_info_terminal_speed
= -1;
59 static int hf_control_message
= -1;
60 static int hf_window_info
= -1;
61 static int hf_window_info_ss
= -1;
62 static int hf_window_info_rows
= -1;
63 static int hf_window_info_cols
= -1;
64 static int hf_window_info_x_pixels
= -1;
65 static int hf_window_info_y_pixels
= -1;
66 static int hf_data
= -1;
68 static const value_string control_message_vals
[] =
70 { 0x02, "Clear buffer" },
72 { 0x20, "Cooked mode" },
73 { 0x80, "Window size request" },
86 session_state_t state
;
87 guint32 info_framenum
;
88 char user_name
[NAME_LEN
];
89 } rlogin_hash_entry_t
;
93 /* Decoder State Machine. Currently only used to snoop on
94 client-user-name as sent by the client up connection establishment.
97 rlogin_state_machine(rlogin_hash_entry_t
*hash_info
, tvbuff_t
*tvb
, packet_info
*pinfo
)
102 /* Won't change state if already seen this packet */
103 if (pinfo
->fd
->flags
.visited
)
108 /* rlogin stream decoder */
109 /* Just watch for the second packet from client with the user name and */
110 /* terminal type information. */
112 if (pinfo
->destport
!= RLOGIN_PORT
)
117 /* exit if already passed username in conversation */
118 if (hash_info
->state
== DONE
)
123 /* exit if no data */
124 length
= tvb_length(tvb
);
130 if (hash_info
->state
== NONE
)
133 if (tvb_get_guint8(tvb
, 0) != '\0')
135 /* We expected a null, but didn't get one; quit. */
136 hash_info
->state
= DONE
;
143 /* Still waiting for data */
144 hash_info
->state
= USER_INFO_WAIT
;
148 /* Have info, store frame number */
149 hash_info
->state
= DONE
;
150 hash_info
->info_framenum
= pinfo
->fd
->num
;
154 /* expect user data here */
155 /* TODO: may need to do more checking here? */
157 if (hash_info
->state
== USER_INFO_WAIT
)
159 /* Store frame number here */
160 hash_info
->state
= DONE
;
161 hash_info
->info_framenum
= pinfo
->fd
->num
;
163 /* Work out length of string to copy */
164 stringlen
= tvb_strnlen(tvb
, 0, NAME_LEN
);
166 stringlen
= NAME_LEN
- 1; /* no '\0' found */
167 else if (stringlen
> NAME_LEN
- 1)
168 stringlen
= NAME_LEN
- 1; /* name too long */
170 /* Copy and terminate string into hash name */
171 tvb_memcpy(tvb
, (guint8
*)hash_info
->user_name
, 0, stringlen
);
172 hash_info
->user_name
[stringlen
] = '\0';
174 col_append_str(pinfo
->cinfo
, COL_INFO
, ", (User information)");
178 /* Dissect details of packet */
179 static void rlogin_display(rlogin_hash_entry_t
*hash_info
,
183 struct tcpinfo
*tcpinfo
)
185 /* Display the proto tree */
187 proto_tree
*rlogin_tree
, *user_info_tree
, *window_tree
;
192 proto_item
*user_info_item
, *window_info_item
;
194 /* Create rlogin subtree */
195 ti
= proto_tree_add_item(tree
, proto_rlogin
, tvb
, 0, -1, ENC_NA
);
196 rlogin_tree
= proto_item_add_subtree(ti
, ett_rlogin
);
198 /* Return if data empty */
199 length
= tvb_length(tvb
);
206 * XXX - this works only if the urgent pointer points to something
207 * in this segment; to make it work if the urgent pointer points
208 * to something past this segment, we'd have to remember the urgent
209 * pointer setting for this conversation.
211 if (tcpinfo
->urgent
&& /* if urgent pointer set */
212 length
>= tcpinfo
->urgent_pointer
) /* and it's in this frame */
214 /* Get urgent byte into Temp */
215 int urgent_offset
= tcpinfo
->urgent_pointer
- 1;
218 /* Check for text data in front */
219 if (urgent_offset
> offset
)
221 proto_tree_add_item(rlogin_tree
, hf_data
, tvb
, offset
, urgent_offset
, ENC_ASCII
|ENC_NA
);
224 /* Show control byte */
225 proto_tree_add_item(rlogin_tree
, hf_control_message
, tvb
,
226 urgent_offset
, 1, ENC_BIG_ENDIAN
);
227 control_byte
= tvb_get_guint8(tvb
, urgent_offset
);
228 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
229 " (%s)", val_to_str_const(control_byte
, control_message_vals
, "Unknown"));
231 offset
= urgent_offset
+ 1; /* adjust offset */
234 if (tvb_get_guint8(tvb
, offset
) == '\0')
237 if (pinfo
->srcport
== RLOGIN_PORT
) /* from server */
239 proto_tree_add_item(rlogin_tree
, hf_startup_info_received_flag
,
240 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
244 proto_tree_add_item(rlogin_tree
, hf_client_startup_flag
,
245 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
250 if (!tvb_offset_exists(tvb
, offset
))
252 /* No more data to check */
256 if (hash_info
->info_framenum
== pinfo
->fd
->num
)
261 /* First frame of conversation, assume user info... */
263 info_len
= tvb_length_remaining(tvb
, offset
);
268 user_info_item
= proto_tree_add_string_format(rlogin_tree
, hf_user_info
, tvb
,
269 offset
, info_len
, FALSE
,
271 tvb_format_text(tvb
, offset
, info_len
));
272 user_info_tree
= proto_item_add_subtree(user_info_item
,
273 ett_rlogin_user_info
);
275 /* Client user name. */
276 str_len
= tvb_strsize(tvb
, offset
);
277 proto_tree_add_item(user_info_tree
, hf_user_info_client_user_name
,
278 tvb
, offset
, str_len
, ENC_ASCII
|ENC_NA
);
281 /* Server user name. */
282 str_len
= tvb_strsize(tvb
, offset
);
283 proto_tree_add_item(user_info_tree
, hf_user_info_server_user_name
,
284 tvb
, offset
, str_len
, ENC_ASCII
|ENC_NA
);
287 /* Terminal type/speed. */
288 slash_offset
= tvb_find_guint8(tvb
, offset
, -1, '/');
289 if (slash_offset
!= -1)
292 proto_tree_add_item(user_info_tree
, hf_user_info_terminal_type
,
293 tvb
, offset
, slash_offset
-offset
, ENC_ASCII
|ENC_NA
);
294 offset
= slash_offset
+ 1;
297 str_len
= tvb_strsize(tvb
, offset
);
298 proto_tree_add_uint(user_info_tree
, hf_user_info_terminal_speed
,
299 tvb
, offset
, str_len
,
300 atoi(tvb_format_text(tvb
, offset
, str_len
)));
305 if (!tvb_offset_exists(tvb
, offset
))
307 /* No more data to check */
311 /* Test for terminal information, the data will have 2 0xff bytes */
312 /* look for first 0xff byte */
313 ti_offset
= tvb_find_guint8(tvb
, offset
, -1, 0xff);
315 /* Next byte must also be 0xff */
316 if (ti_offset
!= -1 &&
317 tvb_bytes_exist(tvb
, ti_offset
+ 1, 1) &&
318 tvb_get_guint8(tvb
, ti_offset
+ 1) == 0xff)
320 guint16 rows
, columns
;
322 /* Have found terminal info. */
323 if (ti_offset
> offset
)
325 /* There's data before the terminal info. */
326 proto_tree_add_item(rlogin_tree
, hf_data
, tvb
,
327 offset
, ti_offset
- offset
, ENC_ASCII
|ENC_NA
);
330 /* Create window info tree */
332 proto_tree_add_item(rlogin_tree
, hf_window_info
, tvb
, offset
, 12, ENC_NA
);
333 window_tree
= proto_item_add_subtree(window_info_item
, ett_rlogin_window
);
336 proto_tree_add_text(window_tree
, tvb
, offset
, 2, "Magic Cookie: (0xff, 0xff)");
339 /* These bytes should be "ss" */
340 proto_tree_add_item(window_tree
, hf_window_info_ss
, tvb
, offset
, 2, ENC_ASCII
|ENC_NA
);
344 rows
= tvb_get_ntohs(tvb
, offset
);
345 proto_tree_add_item(window_tree
, hf_window_info_rows
, tvb
,
346 offset
, 2, ENC_BIG_ENDIAN
);
349 /* Characters per row */
350 columns
= tvb_get_ntohs(tvb
, offset
);
351 proto_tree_add_item(window_tree
, hf_window_info_cols
, tvb
,
352 offset
, 2, ENC_BIG_ENDIAN
);
356 proto_tree_add_item(window_tree
, hf_window_info_x_pixels
, tvb
,
357 offset
, 2, ENC_BIG_ENDIAN
);
361 proto_tree_add_item(window_tree
, hf_window_info_y_pixels
, tvb
,
362 offset
, 2, ENC_BIG_ENDIAN
);
365 /* Show setting highlights in info column */
366 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (rows=%u, cols=%u)",
370 if (tvb_offset_exists(tvb
, offset
))
372 /* There's more data in the frame. */
373 proto_tree_add_item(rlogin_tree
, hf_data
, tvb
, offset
, -1, ENC_ASCII
|ENC_NA
);
378 /****************************************************************
379 * Main dissection function
380 ****************************************************************/
382 dissect_rlogin(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
384 struct tcpinfo
*tcpinfo
= (struct tcpinfo
*)data
;
385 conversation_t
*conversation
;
386 rlogin_hash_entry_t
*hash_info
;
390 /* Get or create conversation */
391 conversation
= find_or_create_conversation(pinfo
);
393 /* Get or create data associated with this conversation */
394 hash_info
= (rlogin_hash_entry_t
*)conversation_get_proto_data(conversation
, proto_rlogin
);
397 /* Populate new data struct... */
398 hash_info
= wmem_new(wmem_file_scope(), rlogin_hash_entry_t
);
399 hash_info
->state
= NONE
;
400 hash_info
->info_framenum
= 0; /* no frame has the number 0 */
401 hash_info
->user_name
[0] = '\0';
403 /* ... and store in conversation */
404 conversation_add_proto_data(conversation
, proto_rlogin
, hash_info
);
407 /* Set protocol column text */
408 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Rlogin");
410 /* Set info column */
411 /* Show user-name if available */
412 if (hash_info
->user_name
[0])
414 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
415 "User name: %s, ", hash_info
->user_name
);
419 col_clear(pinfo
->cinfo
, COL_INFO
);
422 /* Work out packet content summary for display */
423 length
= tvb_length(tvb
);
426 /* Initial NULL byte represents part of connection handshake */
427 if (tvb_get_guint8(tvb
, 0) == '\0')
429 col_append_str(pinfo
->cinfo
, COL_INFO
,
430 (pinfo
->destport
== RLOGIN_PORT
) ?
432 "Startup info received");
435 if (tcpinfo
->urgent
&& length
>= tcpinfo
->urgent_pointer
)
437 /* Urgent pointer inside current data represents a control message */
438 col_append_str(pinfo
->cinfo
, COL_INFO
, "Control Message");
442 /* Search for 2 consecutive ff bytes
443 (signifies window change control message) */
444 ti_offset
= tvb_find_guint8(tvb
, 0, -1, 0xff);
445 if (ti_offset
!= -1 &&
446 tvb_bytes_exist(tvb
, ti_offset
+ 1, 1) &&
447 tvb_get_guint8(tvb
, ti_offset
+ 1) == 0xff)
449 col_append_str(pinfo
->cinfo
, COL_INFO
, "Terminal Info");
453 /* Show any text data in the frame */
454 int bytes_to_copy
= tvb_length(tvb
);
455 if (bytes_to_copy
> 128)
457 /* Truncate to 128 bytes for display */
461 /* Add data into info column */
462 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
464 tvb_format_text(tvb
, 0, bytes_to_copy
));
469 /* See if conversation state needs to be updated */
470 rlogin_state_machine(hash_info
, tvb
, pinfo
);
472 /* Dissect in detail */
473 rlogin_display(hash_info
, tvb
, pinfo
, tree
, tcpinfo
);
475 return tvb_length(tvb
);
479 void proto_register_rlogin(void)
481 static gint
*ett
[] = {
484 &ett_rlogin_window_rows
,
485 &ett_rlogin_window_cols
,
486 &ett_rlogin_window_x_pixels
,
487 &ett_rlogin_window_y_pixels
,
488 &ett_rlogin_user_info
491 static hf_register_info hf
[] =
494 { "User Info", "rlogin.user_info", FT_STRING
, BASE_NONE
,
495 NULL
, 0x0, NULL
, HFILL
498 { &hf_client_startup_flag
,
499 { "Client startup flag", "rlogin.client_startup_flag", FT_UINT8
, BASE_HEX
,
500 NULL
, 0x0, NULL
, HFILL
503 { &hf_startup_info_received_flag
,
504 { "Startup info received flag", "rlogin.startup_info_received_flag", FT_UINT8
, BASE_HEX
,
505 NULL
, 0x0, NULL
, HFILL
508 { &hf_user_info_client_user_name
,
509 { "Client-user-name", "rlogin.client_user_name", FT_STRING
, BASE_NONE
,
510 NULL
, 0x0, NULL
, HFILL
513 { &hf_user_info_server_user_name
,
514 { "Server-user-name", "rlogin.server_user_name", FT_STRING
, BASE_NONE
,
515 NULL
, 0x0, NULL
, HFILL
518 { &hf_user_info_terminal_type
,
519 { "Terminal-type", "rlogin.terminal_type", FT_STRING
, BASE_NONE
,
520 NULL
, 0x0, NULL
, HFILL
523 { &hf_user_info_terminal_speed
,
524 { "Terminal-speed", "rlogin.terminal_speed", FT_UINT32
, BASE_DEC
,
525 NULL
, 0x0, NULL
, HFILL
528 { &hf_control_message
,
529 { "Control message", "rlogin.control_message", FT_UINT8
, BASE_HEX
,
530 VALS(control_message_vals
), 0x0, NULL
, HFILL
534 { "Window Info", "rlogin.window_size", FT_NONE
, BASE_NONE
,
535 NULL
, 0x0, NULL
, HFILL
538 { &hf_window_info_ss
,
539 { "Window size marker", "rlogin.window_size.ss", FT_STRING
, BASE_NONE
,
540 NULL
, 0x0, NULL
, HFILL
543 { &hf_window_info_rows
,
544 { "Rows", "rlogin.window_size.rows", FT_UINT16
, BASE_DEC
,
545 NULL
, 0x0, NULL
, HFILL
548 { &hf_window_info_cols
,
549 { "Columns", "rlogin.window_size.cols", FT_UINT16
, BASE_DEC
,
550 NULL
, 0x0, NULL
, HFILL
553 { &hf_window_info_x_pixels
,
554 { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16
, BASE_DEC
,
555 NULL
, 0x0, NULL
, HFILL
558 { &hf_window_info_y_pixels
,
559 { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16
, BASE_DEC
,
560 NULL
, 0x0, NULL
, HFILL
564 { "Data", "rlogin.data", FT_STRING
, BASE_NONE
,
565 NULL
, 0x0, NULL
, HFILL
570 proto_rlogin
= proto_register_protocol("Rlogin Protocol", "Rlogin", "rlogin");
572 proto_register_field_array(proto_rlogin
, hf
, array_length(hf
));
573 proto_register_subtree_array(ett
, array_length(ett
));
576 void proto_reg_handoff_rlogin(void)
578 /* Dissector install routine */
579 dissector_handle_t rlogin_handle
= new_create_dissector_handle(dissect_rlogin
,proto_rlogin
);
580 dissector_add_uint("tcp.port", RLOGIN_PORT
, rlogin_handle
);