2 * Routines for unix rlogin packet dissection
3 * Copyright 2000, Jeffrey C. Foster <jfoste[AT]woodward.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * Based upon RFC-1282 - BSD Rlogin
11 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include <epan/packet.h>
18 #include <epan/expert.h>
19 #include <wsutil/strtoi.h>
20 #include "packet-tcp.h"
22 #define RLOGIN_PORT 513
24 void proto_register_rlogin(void);
25 void proto_reg_handoff_rlogin(void);
27 static dissector_handle_t rlogin_handle
;
29 static int proto_rlogin
;
31 static int ett_rlogin
;
32 static int ett_rlogin_window
;
33 static int ett_rlogin_user_info
;
34 static int ett_rlogin_window_rows
;
35 static int ett_rlogin_window_cols
;
36 static int ett_rlogin_window_x_pixels
;
37 static int ett_rlogin_window_y_pixels
;
39 static int hf_user_info
;
40 static int hf_client_startup_flag
;
41 static int hf_startup_info_received_flag
;
42 static int hf_user_info_client_user_name
;
43 static int hf_user_info_server_user_name
;
44 static int hf_user_info_terminal_type
;
45 static int hf_user_info_terminal_speed
;
46 static int hf_control_message
;
47 static int hf_magic_cookie
;
48 static int hf_window_info
;
49 static int hf_window_info_ss
;
50 static int hf_window_info_rows
;
51 static int hf_window_info_cols
;
52 static int hf_window_info_x_pixels
;
53 static int hf_window_info_y_pixels
;
56 static expert_field ei_rlogin_termlen_invalid
;
58 static const value_string control_message_vals
[] =
60 { 0x02, "Clear buffer" },
62 { 0x20, "Cooked mode" },
63 { 0x80, "Window size request" },
76 session_state_t state
;
77 uint32_t info_framenum
;
78 char user_name
[NAME_LEN
];
79 } rlogin_hash_entry_t
;
83 /* Decoder State Machine. Currently only used to snoop on
84 client-user-name as sent by the client up connection establishment.
87 rlogin_state_machine(rlogin_hash_entry_t
*hash_info
, tvbuff_t
*tvb
, packet_info
*pinfo
)
92 /* Won't change state if already seen this packet */
93 if (pinfo
->fd
->visited
)
98 /* rlogin stream decoder */
99 /* Just watch for the second packet from client with the user name and */
100 /* terminal type information. */
102 if (pinfo
->destport
!= RLOGIN_PORT
)
107 /* exit if already passed username in conversation */
108 if (hash_info
->state
== DONE
)
113 /* exit if no data */
114 length
= tvb_captured_length(tvb
);
120 if (hash_info
->state
== NONE
)
123 if (tvb_get_uint8(tvb
, 0) != '\0')
125 /* We expected a null, but didn't get one; quit. */
126 hash_info
->state
= DONE
;
133 /* Still waiting for data */
134 hash_info
->state
= USER_INFO_WAIT
;
138 /* Have info, store frame number */
139 hash_info
->state
= DONE
;
140 hash_info
->info_framenum
= pinfo
->num
;
144 /* expect user data here */
145 /* TODO: may need to do more checking here? */
147 if (hash_info
->state
== USER_INFO_WAIT
)
149 /* Store frame number here */
150 hash_info
->state
= DONE
;
151 hash_info
->info_framenum
= pinfo
->num
;
153 /* Work out length of string to copy */
154 stringlen
= tvb_strnlen(tvb
, 0, NAME_LEN
);
156 stringlen
= NAME_LEN
- 1; /* no '\0' found */
157 else if (stringlen
> NAME_LEN
- 1)
158 stringlen
= NAME_LEN
- 1; /* name too long */
160 /* Copy and terminate string into hash name */
161 tvb_memcpy(tvb
, (uint8_t *)hash_info
->user_name
, 0, stringlen
);
162 hash_info
->user_name
[stringlen
] = '\0';
164 col_append_str(pinfo
->cinfo
, COL_INFO
, ", (User information)");
168 /* Dissect details of packet */
169 static void rlogin_display(rlogin_hash_entry_t
*hash_info
,
173 struct tcpinfo
*tcpinfo
)
175 /* Display the proto tree */
177 proto_tree
*rlogin_tree
, *user_info_tree
, *window_tree
;
182 proto_item
*user_info_item
, *window_info_item
;
184 /* Create rlogin subtree */
185 ti
= proto_tree_add_item(tree
, proto_rlogin
, tvb
, 0, -1, ENC_NA
);
186 rlogin_tree
= proto_item_add_subtree(ti
, ett_rlogin
);
188 /* Return if data empty */
189 length
= tvb_captured_length(tvb
);
196 * XXX - this works only if the urgent pointer points to something
197 * in this segment; to make it work if the urgent pointer points
198 * to something past this segment, we'd have to remember the urgent
199 * pointer setting for this conversation.
201 if (tcpinfo
&& IS_TH_URG(tcpinfo
->flags
) && /* if urgent pointer set */
202 length
>= tcpinfo
->urgent_pointer
) /* and it's in this frame */
204 /* Get urgent byte into Temp */
205 int urgent_offset
= tcpinfo
->urgent_pointer
- 1;
206 uint8_t control_byte
;
208 /* Check for text data in front */
209 if (urgent_offset
> offset
)
211 proto_tree_add_item(rlogin_tree
, hf_data
, tvb
, offset
, urgent_offset
, ENC_ASCII
);
214 /* Show control byte */
215 proto_tree_add_item(rlogin_tree
, hf_control_message
, tvb
,
216 urgent_offset
, 1, ENC_BIG_ENDIAN
);
217 control_byte
= tvb_get_uint8(tvb
, urgent_offset
);
218 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
219 " (%s)", val_to_str_const(control_byte
, control_message_vals
, "Unknown"));
221 offset
= urgent_offset
+ 1; /* adjust offset */
224 if (tvb_get_uint8(tvb
, offset
) == '\0')
227 if (pinfo
->srcport
== RLOGIN_PORT
) /* from server */
229 proto_tree_add_item(rlogin_tree
, hf_startup_info_received_flag
,
230 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
234 proto_tree_add_item(rlogin_tree
, hf_client_startup_flag
,
235 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
240 if (!tvb_offset_exists(tvb
, offset
))
242 /* No more data to check */
246 if (hash_info
->info_framenum
== pinfo
->num
)
251 /* First frame of conversation, assume user info... */
253 info_len
= tvb_captured_length_remaining(tvb
, offset
);
258 user_info_item
= proto_tree_add_string_format(rlogin_tree
, hf_user_info
, tvb
,
259 offset
, info_len
, NULL
,
261 tvb_format_text(pinfo
->pool
, tvb
, offset
, info_len
));
262 user_info_tree
= proto_item_add_subtree(user_info_item
,
263 ett_rlogin_user_info
);
265 /* Client user name. */
266 str_len
= tvb_strsize(tvb
, offset
);
267 proto_tree_add_item(user_info_tree
, hf_user_info_client_user_name
,
268 tvb
, offset
, str_len
, ENC_ASCII
);
271 /* Server user name. */
272 str_len
= tvb_strsize(tvb
, offset
);
273 proto_tree_add_item(user_info_tree
, hf_user_info_server_user_name
,
274 tvb
, offset
, str_len
, ENC_ASCII
);
277 /* Terminal type/speed. */
278 slash_offset
= tvb_find_uint8(tvb
, offset
, -1, '/');
279 if (slash_offset
!= -1)
282 uint32_t term_len
= 0;
284 proto_item
* pi
= NULL
;
287 proto_tree_add_item(user_info_tree
, hf_user_info_terminal_type
,
288 tvb
, offset
, slash_offset
-offset
, ENC_ASCII
);
289 offset
= slash_offset
+ 1;
292 str_len
= tvb_strsize(tvb
, offset
);
293 str
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, str_len
,
295 term_len_valid
= ws_strtou32(str
, NULL
, &term_len
);
296 pi
= proto_tree_add_uint(user_info_tree
,
297 hf_user_info_terminal_speed
,
298 tvb
, offset
, str_len
, term_len
);
300 expert_add_info(pinfo
, pi
, &ei_rlogin_termlen_invalid
);
306 if (!tvb_offset_exists(tvb
, offset
))
308 /* No more data to check */
312 /* Test for terminal information, the data will have 2 0xff bytes */
313 /* look for first 0xff byte */
314 ti_offset
= tvb_find_uint8(tvb
, offset
, -1, 0xff);
316 /* Next byte must also be 0xff */
317 if (ti_offset
!= -1 &&
318 tvb_bytes_exist(tvb
, ti_offset
+ 1, 1) &&
319 tvb_get_uint8(tvb
, ti_offset
+ 1) == 0xff)
321 uint16_t rows
, columns
;
323 /* Have found terminal info. */
324 if (ti_offset
> offset
)
326 /* There's data before the terminal info. */
327 proto_tree_add_item(rlogin_tree
, hf_data
, tvb
,
328 offset
, ti_offset
- offset
, ENC_ASCII
);
331 /* Create window info tree */
333 proto_tree_add_item(rlogin_tree
, hf_window_info
, tvb
, offset
, 12, ENC_NA
);
334 window_tree
= proto_item_add_subtree(window_info_item
, ett_rlogin_window
);
337 proto_tree_add_item(window_tree
, hf_magic_cookie
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
340 /* These bytes should be "ss" */
341 proto_tree_add_item(window_tree
, hf_window_info_ss
, tvb
, offset
, 2, ENC_ASCII
);
345 rows
= tvb_get_ntohs(tvb
, offset
);
346 proto_tree_add_item(window_tree
, hf_window_info_rows
, tvb
,
347 offset
, 2, ENC_BIG_ENDIAN
);
350 /* Characters per row */
351 columns
= tvb_get_ntohs(tvb
, offset
);
352 proto_tree_add_item(window_tree
, hf_window_info_cols
, tvb
,
353 offset
, 2, ENC_BIG_ENDIAN
);
357 proto_tree_add_item(window_tree
, hf_window_info_x_pixels
, tvb
,
358 offset
, 2, ENC_BIG_ENDIAN
);
362 proto_tree_add_item(window_tree
, hf_window_info_y_pixels
, tvb
,
363 offset
, 2, ENC_BIG_ENDIAN
);
366 /* Show setting highlights in info column */
367 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (rows=%u, cols=%u)",
371 if (tvb_offset_exists(tvb
, offset
))
373 /* There's more data in the frame. */
374 proto_tree_add_item(rlogin_tree
, hf_data
, tvb
, offset
, -1, ENC_ASCII
);
379 /****************************************************************
380 * Main dissection function
381 ****************************************************************/
383 dissect_rlogin(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
385 struct tcpinfo
*tcpinfo
= (struct tcpinfo
*)data
;
386 conversation_t
*conversation
;
387 rlogin_hash_entry_t
*hash_info
;
391 /* Get or create conversation */
392 conversation
= find_or_create_conversation(pinfo
);
394 /* Get or create data associated with this conversation */
395 hash_info
= (rlogin_hash_entry_t
*)conversation_get_proto_data(conversation
, proto_rlogin
);
398 /* Populate new data struct... */
399 hash_info
= wmem_new(wmem_file_scope(), rlogin_hash_entry_t
);
400 hash_info
->state
= NONE
;
401 hash_info
->info_framenum
= 0; /* no frame has the number 0 */
402 hash_info
->user_name
[0] = '\0';
404 /* ... and store in conversation */
405 conversation_add_proto_data(conversation
, proto_rlogin
, hash_info
);
408 /* Set protocol column text */
409 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Rlogin");
411 /* Set info column */
412 /* Show user-name if available */
413 if (hash_info
->user_name
[0])
415 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
416 "User name: %s, ", hash_info
->user_name
);
420 col_clear(pinfo
->cinfo
, COL_INFO
);
423 /* Work out packet content summary for display */
424 length
= tvb_reported_length(tvb
);
427 /* Initial NULL byte represents part of connection handshake */
428 if (tvb_get_uint8(tvb
, 0) == '\0')
430 col_append_str(pinfo
->cinfo
, COL_INFO
,
431 (pinfo
->destport
== RLOGIN_PORT
) ?
433 "Startup info received");
436 if (tcpinfo
&& IS_TH_URG(tcpinfo
->flags
) && length
>= tcpinfo
->urgent_pointer
)
438 /* Urgent pointer inside current data represents a control message */
439 col_append_str(pinfo
->cinfo
, COL_INFO
, "Control Message");
443 /* Search for 2 consecutive ff bytes
444 (signifies window change control message) */
445 ti_offset
= tvb_find_uint8(tvb
, 0, -1, 0xff);
446 if (ti_offset
!= -1 &&
447 tvb_bytes_exist(tvb
, ti_offset
+ 1, 1) &&
448 tvb_get_uint8(tvb
, ti_offset
+ 1) == 0xff)
450 col_append_str(pinfo
->cinfo
, COL_INFO
, "Terminal Info");
454 /* Show any text data in the frame */
455 int bytes_to_copy
= tvb_captured_length(tvb
);
456 if (bytes_to_copy
> 128)
458 /* Truncate to 128 bytes for display */
462 /* Add data into info column */
463 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
465 tvb_format_text(pinfo
->pool
, tvb
, 0, bytes_to_copy
));
470 /* See if conversation state needs to be updated */
471 rlogin_state_machine(hash_info
, tvb
, pinfo
);
473 /* Dissect in detail */
474 rlogin_display(hash_info
, tvb
, pinfo
, tree
, tcpinfo
);
476 return tvb_captured_length(tvb
);
480 void proto_register_rlogin(void)
482 expert_module_t
* expert_rlogin
;
484 static int *ett
[] = {
487 &ett_rlogin_window_rows
,
488 &ett_rlogin_window_cols
,
489 &ett_rlogin_window_x_pixels
,
490 &ett_rlogin_window_y_pixels
,
491 &ett_rlogin_user_info
494 static hf_register_info hf
[] =
497 { "User Info", "rlogin.user_info", FT_STRING
, BASE_NONE
,
498 NULL
, 0x0, NULL
, HFILL
501 { &hf_client_startup_flag
,
502 { "Client startup flag", "rlogin.client_startup_flag", FT_UINT8
, BASE_HEX
,
503 NULL
, 0x0, NULL
, HFILL
506 { &hf_startup_info_received_flag
,
507 { "Startup info received flag", "rlogin.startup_info_received_flag", FT_UINT8
, BASE_HEX
,
508 NULL
, 0x0, NULL
, HFILL
511 { &hf_user_info_client_user_name
,
512 { "Client-user-name", "rlogin.client_user_name", FT_STRING
, BASE_NONE
,
513 NULL
, 0x0, NULL
, HFILL
516 { &hf_user_info_server_user_name
,
517 { "Server-user-name", "rlogin.server_user_name", FT_STRING
, BASE_NONE
,
518 NULL
, 0x0, NULL
, HFILL
521 { &hf_user_info_terminal_type
,
522 { "Terminal-type", "rlogin.terminal_type", FT_STRING
, BASE_NONE
,
523 NULL
, 0x0, NULL
, HFILL
526 { &hf_user_info_terminal_speed
,
527 { "Terminal-speed", "rlogin.terminal_speed", FT_UINT32
, BASE_DEC
,
528 NULL
, 0x0, NULL
, HFILL
531 { &hf_control_message
,
532 { "Control message", "rlogin.control_message", FT_UINT8
, BASE_HEX
,
533 VALS(control_message_vals
), 0x0, NULL
, HFILL
537 { "Magic Cookie", "rlogin.magic_cookie", FT_UINT16
, BASE_HEX
,
538 NULL
, 0x0, NULL
, HFILL
542 { "Window Info", "rlogin.window_size", FT_NONE
, BASE_NONE
,
543 NULL
, 0x0, NULL
, HFILL
546 { &hf_window_info_ss
,
547 { "Window size marker", "rlogin.window_size.ss", FT_STRING
, BASE_NONE
,
548 NULL
, 0x0, NULL
, HFILL
551 { &hf_window_info_rows
,
552 { "Rows", "rlogin.window_size.rows", FT_UINT16
, BASE_DEC
,
553 NULL
, 0x0, NULL
, HFILL
556 { &hf_window_info_cols
,
557 { "Columns", "rlogin.window_size.cols", FT_UINT16
, BASE_DEC
,
558 NULL
, 0x0, NULL
, HFILL
561 { &hf_window_info_x_pixels
,
562 { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16
, BASE_DEC
,
563 NULL
, 0x0, NULL
, HFILL
566 { &hf_window_info_y_pixels
,
567 { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16
, BASE_DEC
,
568 NULL
, 0x0, NULL
, HFILL
572 { "Data", "rlogin.data", FT_STRING
, BASE_NONE
,
573 NULL
, 0x0, NULL
, HFILL
578 static ei_register_info ei
[] = {
579 { &ei_rlogin_termlen_invalid
, { "rlogin.terminal_speed.invalid", PI_MALFORMED
, PI_ERROR
,
580 "Terminal length must be a string containing an integer", EXPFILL
}}
583 proto_rlogin
= proto_register_protocol("Rlogin Protocol", "Rlogin", "rlogin");
585 proto_register_field_array(proto_rlogin
, hf
, array_length(hf
));
586 proto_register_subtree_array(ett
, array_length(ett
));
588 expert_rlogin
= expert_register_protocol(proto_rlogin
);
589 expert_register_field_array(expert_rlogin
, ei
, array_length(ei
));
591 rlogin_handle
= register_dissector("rlogin", dissect_rlogin
,proto_rlogin
);
594 void proto_reg_handoff_rlogin(void)
596 /* Dissector install routine */
597 dissector_add_uint_with_preference("tcp.port", RLOGIN_PORT
, rlogin_handle
);
601 * Editor modelines - https://www.wireshark.org/tools/modelines.html
606 * indent-tabs-mode: t
609 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
610 * :indentSize=8:tabSize=8:noTabs=false: