2 * Routines for unix rlogin packet dissection
3 * Copyright 2000, Jeffrey C. Foster <jfoste@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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <epan/packet.h>
37 #include <epan/conversation.h>
38 #include <epan/emem.h>
40 #include "packet-tcp.h"
42 #define RLOGIN_PORT 513
44 static int proto_rlogin = -1;
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;
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;
70 static const value_string control_message_vals[] =
72 { 0x02, "Clear buffer" },
74 { 0x20, "Cooked mode" },
75 { 0x80, "Window size request" },
88 session_state_t state;
89 guint32 info_framenum;
90 char user_name[NAME_LEN];
91 } rlogin_hash_entry_t;
95 /* Decoder State Machine. Currently only used to snoop on
96 client-user-name as sent by the client up connection establishment.
99 rlogin_state_machine(rlogin_hash_entry_t *hash_info, tvbuff_t *tvb, packet_info *pinfo)
104 /* Won't change state if already seen this packet */
105 if (pinfo->fd->flags.visited)
110 /* rlogin stream decoder */
111 /* Just watch for the second packet from client with the user name and */
112 /* terminal type information. */
114 if (pinfo->destport != RLOGIN_PORT)
119 /* exit if already passed username in conversation */
120 if (hash_info->state == DONE)
125 /* exit if no data */
126 length = tvb_length(tvb);
132 if (hash_info->state == NONE)
135 if (tvb_get_guint8(tvb, 0) != '\0')
137 /* We expected a null, but didn't get one; quit. */
138 hash_info->state = DONE;
145 /* Still waiting for data */
146 hash_info->state = USER_INFO_WAIT;
150 /* Have info, store frame number */
151 hash_info->state = DONE;
152 hash_info->info_framenum = pinfo->fd->num;
156 /* expect user data here */
157 /* TODO: may need to do more checking here? */
159 if (hash_info->state == USER_INFO_WAIT)
161 /* Store frame number here */
162 hash_info->state = DONE;
163 hash_info->info_framenum = pinfo->fd->num;
165 /* Work out length of string to copy */
166 stringlen = tvb_strnlen(tvb, 0, NAME_LEN);
168 stringlen = NAME_LEN - 1; /* no '\0' found */
169 else if (stringlen > NAME_LEN - 1)
170 stringlen = NAME_LEN - 1; /* name too long */
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';
176 if (check_col(pinfo->cinfo, COL_INFO))
178 col_append_str(pinfo->cinfo, COL_INFO, ", (User information)");
183 /* Dissect details of packet */
184 static void rlogin_display(rlogin_hash_entry_t *hash_info,
188 struct tcpinfo *tcpinfo)
190 /* Display the proto tree */
192 proto_tree *rlogin_tree, *user_info_tree, *window_tree;
197 proto_item *user_info_item, *window_info_item;
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);
203 /* Return if data empty */
204 length = tvb_length(tvb);
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.
216 if (tcpinfo->urgent && /* if urgent pointer set */
217 length >= tcpinfo->urgent_pointer) /* and it's in this frame */
219 /* Get urgent byte into Temp */
220 int urgent_offset = tcpinfo->urgent_pointer - 1;
223 /* Check for text data in front */
224 if (urgent_offset > offset)
226 proto_tree_add_item(rlogin_tree, hf_data, tvb, offset, urgent_offset, FALSE);
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))
235 col_append_fstr(pinfo->cinfo, COL_INFO,
236 " (%s)", val_to_str(control_byte, control_message_vals, "Unknown"));
239 offset = urgent_offset + 1; /* adjust offset */
242 if (tvb_get_guint8(tvb, offset) == '\0')
245 if (pinfo->srcport == RLOGIN_PORT) /* from server */
247 proto_tree_add_item(rlogin_tree, hf_startup_info_received_flag,
248 tvb, offset, 1, FALSE);
252 proto_tree_add_item(rlogin_tree, hf_client_startup_flag,
253 tvb, offset, 1, FALSE);
258 if (!tvb_offset_exists(tvb, offset))
260 /* No more data to check */
264 if (hash_info->info_framenum == pinfo->fd->num)
269 /* First frame of conversation, assume user info... */
271 info_len = tvb_length_remaining(tvb, offset);
274 user_info_item = proto_tree_add_string_format(rlogin_tree, hf_user_info, tvb,
275 offset, info_len, FALSE,
277 tvb_format_text(tvb, offset, info_len));
278 user_info_tree = proto_item_add_subtree(user_info_item,
279 ett_rlogin_user_info);
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);
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);
293 /* Terminal type/speed. */
294 slash_offset = tvb_find_guint8(tvb, offset, -1, '/');
295 if (slash_offset != -1)
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;
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)));
311 if (!tvb_offset_exists(tvb, offset))
313 /* No more data to check */
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);
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)
326 guint16 rows, columns;
328 /* Have found terminal info. */
329 if (ti_offset > offset)
331 /* There's data before the terminal info. */
332 proto_tree_add_item(rlogin_tree, hf_data, tvb,
333 offset, ti_offset - offset, FALSE);
336 /* Create window info tree */
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);
342 proto_tree_add_text(window_tree, tvb, offset, 2, "Magic Cookie: (0xff, 0xff)");
345 /* These bytes should be "ss" */
346 proto_tree_add_item(window_tree, hf_window_info_ss, tvb, offset, 2, FALSE);
350 rows = tvb_get_ntohs(tvb, offset);
351 proto_tree_add_item(window_tree, hf_window_info_rows, tvb,
355 /* Characters per row */
356 columns = tvb_get_ntohs(tvb, offset);
357 proto_tree_add_item(window_tree, hf_window_info_cols, tvb,
362 proto_tree_add_item(window_tree, hf_window_info_x_pixels, tvb,
367 proto_tree_add_item(window_tree, hf_window_info_y_pixels, tvb,
371 /* Show setting highlights in info column */
372 if (check_col(pinfo->cinfo, COL_INFO))
374 col_append_fstr(pinfo->cinfo, COL_INFO, " (rows=%u, cols=%u)",
379 if (tvb_offset_exists(tvb, offset))
381 /* There's more data in the frame. */
382 proto_tree_add_item(rlogin_tree, hf_data, tvb, offset, -1, FALSE);
387 /****************************************************************
388 * Main dissection function
389 ****************************************************************/
391 dissect_rlogin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
393 struct tcpinfo *tcpinfo = pinfo->private_data;
394 conversation_t *conversation;
395 rlogin_hash_entry_t *hash_info;
399 /* Get conversation */
400 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
401 pinfo->ptype, pinfo->srcport, pinfo->destport,
404 /* Create if didn't previously exist */
407 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
408 pinfo->ptype, pinfo->srcport, pinfo->destport,
412 /* Get or create data associated with this conversation */
413 hash_info = conversation_get_proto_data(conversation, proto_rlogin);
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';
422 /* ... and store in conversation */
423 conversation_add_proto_data(conversation, proto_rlogin, hash_info);
426 /* Set protocol column text */
427 if (check_col(pinfo->cinfo, COL_PROTOCOL))
429 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Rlogin");
432 /* Set info column */
433 if (check_col(pinfo->cinfo, COL_INFO))
435 /* Show user-name if available */
436 if (hash_info->user_name[0])
438 col_add_fstr(pinfo->cinfo, COL_INFO,
439 "User name: %s, ", hash_info->user_name);
443 col_clear(pinfo->cinfo, COL_INFO);
446 /* Work out packet content summary for display */
447 length = tvb_length(tvb);
450 /* Initial NULL byte represents part of connection handshake */
451 if (tvb_get_guint8(tvb, 0) == '\0')
453 col_append_str(pinfo->cinfo, COL_INFO,
454 (pinfo->destport == RLOGIN_PORT) ?
456 "Startup info received");
459 if (tcpinfo->urgent && length >= tcpinfo->urgent_pointer)
461 /* Urgent pointer inside current data represents a control message */
462 col_append_str(pinfo->cinfo, COL_INFO, "Control Message");
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)
473 col_append_str(pinfo->cinfo, COL_INFO, "Terminal Info");
477 /* Show any text data in the frame */
478 int bytes_to_copy = tvb_length(tvb);
479 if (bytes_to_copy > 128)
481 /* Truncate to 128 bytes for display */
485 /* Add data into info column */
486 col_append_fstr(pinfo->cinfo, COL_INFO,
488 tvb_format_text(tvb, 0, bytes_to_copy));
494 /* See if conversation state needs to be updated */
495 rlogin_state_machine(hash_info, tvb, pinfo);
497 /* Dissect in detail */
498 rlogin_display(hash_info, tvb, pinfo, tree, tcpinfo);
502 void proto_register_rlogin(void)
504 static gint *ett[] = {
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
514 static hf_register_info hf[] =
517 { "User Info", "rlogin.user_info", FT_STRING, BASE_NONE,
521 { &hf_client_startup_flag,
522 { "Client startup flag", "rlogin.client_startup_flag", FT_UINT8, BASE_HEX,
526 { &hf_startup_info_received_flag,
527 { "Startup info received flag", "rlogin.startup_info_received_flag", FT_UINT8, BASE_HEX,
531 { &hf_user_info_client_user_name,
532 { "Client-user-name", "rlogin.client_user_name", FT_STRING, BASE_NONE,
536 { &hf_user_info_server_user_name,
537 { "Server-user-name", "rlogin.server_user_name", FT_STRING, BASE_NONE,
541 { &hf_user_info_terminal_type,
542 { "Terminal-type", "rlogin.terminal_type", FT_STRING, BASE_NONE,
546 { &hf_user_info_terminal_speed,
547 { "Terminal-speed", "rlogin.terminal_speed", FT_UINT32, BASE_DEC,
551 { &hf_control_message,
552 { "Control message", "rlogin.control_message", FT_UINT8, BASE_HEX,
553 VALS(control_message_vals), 0x0, "", HFILL
557 { "Window Info", "rlogin.window_size", FT_NONE, BASE_NONE,
561 { &hf_window_info_ss,
562 { "Window size marker", "rlogin.window_size.ss", FT_STRING, BASE_NONE,
566 { &hf_window_info_rows,
567 { "Rows", "rlogin.window_size.rows", FT_UINT16, BASE_DEC,
571 { &hf_window_info_cols,
572 { "Columns", "rlogin.window_size.cols", FT_UINT16, BASE_DEC,
576 { &hf_window_info_x_pixels,
577 { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16, BASE_DEC,
581 { &hf_window_info_y_pixels,
582 { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16, BASE_DEC,
587 { "Data", "rlogin.data", FT_STRING, BASE_NONE,
593 proto_rlogin = proto_register_protocol("Rlogin Protocol", "Rlogin", "rlogin");
595 proto_register_field_array(proto_rlogin, hf, array_length(hf));
596 proto_register_subtree_array(ett, array_length(ett));
599 void proto_reg_handoff_rlogin(void)
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);