/* packet-rlogin.c * Routines for unix rlogin packet dissection * Copyright 2000, Jeffrey C. Foster * * $Id: packet-rlogin.c,v 1.18 2001/07/03 00:49:57 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * Based upon RFC-1282 - BSD Rlogin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #include #include #include #include "packet.h" #include "conversation.h" #include "packet-tcp.h" #define TCP_PORT_RLOGIN 513 static int proto_rlogin = -1; static int ett_rlogin = -1; static int ett_rlogin_window = -1; static int ett_rlogin_user_info = -1; static int ett_rlogin_window_rows = -1; static int ett_rlogin_window_cols = -1; static int ett_rlogin_window_x_pixels = -1; static int ett_rlogin_window_y_pixels = -1; static int hf_user_info = -1; static int hf_window_info = -1; static int hf_window_info_rows = -1; static int hf_window_info_cols = -1; static int hf_window_info_x_pixels = -1; static int hf_window_info_y_pixels = -1; #define RLOGIN_PORT 513 #define NAME_LEN 32 typedef struct { int state; guint32 info_framenum; char name[ NAME_LEN]; } rlogin_hash_entry_t; #define NONE 0 #define USER_INFO_WAIT 1 #define DONE 2 #define BAD 2 static GMemChunk *rlogin_vals = NULL; #define rlogin_hash_init_count 20 static guint32 last_abs_sec = 0; static guint32 last_abs_usec= 0; static void rlogin_init(void) { /* Routine to initialize rlogin protocol before each capture or filter pass. */ /* Release any memory if needed. Then setup the memory chunks. */ last_abs_sec = 0; last_abs_usec= 0; if (rlogin_vals) g_mem_chunk_destroy(rlogin_vals); rlogin_vals = g_mem_chunk_new("rlogin_vals", sizeof( rlogin_hash_entry_t), rlogin_hash_init_count * sizeof( rlogin_hash_entry_t), G_ALLOC_AND_FREE); } /**************** Decoder State Machine ******************/ static void rlogin_state_machine( rlogin_hash_entry_t *hash_info, tvbuff_t *tvb, packet_info *pinfo) { guint length; gint stringlen; /* rlogin stream decoder */ /* Just watched for second packet from client with the user name and */ /* terminal type information. */ if ( pinfo->destport != RLOGIN_PORT) /* not from client */ return; /* exit if not needed */ if (( hash_info->state == DONE) || ( hash_info->state == BAD)) return; /* test timestamp */ if (( last_abs_sec > pinfo->fd->abs_secs) || (( last_abs_sec == pinfo->fd->abs_secs) && ( last_abs_usec >= pinfo->fd->abs_usecs))) return; last_abs_sec = pinfo->fd->abs_secs; /* save timestamp */ last_abs_usec = pinfo->fd->abs_usecs; length = tvb_length(tvb); if ( length == 0) /* exit if no data */ return; if ( hash_info->state == NONE){ /* new connection*/ if (tvb_get_guint8(tvb, 0) != '\0') { /* * We expected a NUL, but didn't get one; quit. */ hash_info->state = DONE; return; } else { if (length <= 1) /* if no data */ hash_info->state = USER_INFO_WAIT; else { hash_info->state = DONE; hash_info->info_framenum = pinfo->fd->num; } } } /* expect user data here */ /*$$$ may need to do more checking here */ else if ( hash_info->state == USER_INFO_WAIT) { hash_info->state = DONE; hash_info->info_framenum = pinfo->fd->num; /* save name for later*/ stringlen = tvb_strnlen(tvb, 0, NAME_LEN); if (stringlen == -1) stringlen = NAME_LEN - 1; /* no '\0' found */ else if (stringlen > NAME_LEN - 1) stringlen = NAME_LEN - 1; /* name too long */ tvb_memcpy(tvb, (guint8 *)hash_info->name, 0, stringlen); hash_info->name[stringlen] = '\0'; if (check_col(pinfo->fd, COL_INFO)) /* update summary */ col_append_str(pinfo->fd, COL_INFO, ", User information"); } } static void rlogin_display( rlogin_hash_entry_t *hash_info, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { /* Display the proto tree */ int offset = 0; proto_tree *rlogin_tree, *user_info_tree, *window_tree; proto_item *ti; guint length; int str_len; gint ti_offset; proto_item *user_info_item, *window_info_item; ti = proto_tree_add_item( tree, proto_rlogin, tvb, 0, tvb_length(tvb), FALSE); rlogin_tree = proto_item_add_subtree(ti, ett_rlogin); length = tvb_length(tvb); if ( length == 0) /* exit if no captured data */ return; if ( tcp_urgent_pointer && /* if control message */ length >= tcp_urgent_pointer) { /* and it's in this frame */ int urgent_offset = tcp_urgent_pointer - 1; guint8 Temp = tvb_get_guint8(tvb, urgent_offset); if (urgent_offset > offset) /* check for data in front */ proto_tree_add_text( rlogin_tree, tvb, offset, urgent_offset, "Data"); proto_tree_add_text( rlogin_tree, tvb, urgent_offset, 1, "Control byte: %u (%s)", Temp, (Temp == 0x02) ? "Clear buffer" : (Temp == 0x10) ? "Raw mode" : (Temp == 0x20) ? "Cooked mode" : (Temp == 0x80) ? "Window size request" : "Unknown"); offset = urgent_offset + 1; /* adjust offset */ } else if ( tvb_get_guint8(tvb, offset) == '\0'){ /* startup */ if ( pinfo->srcport== RLOGIN_PORT) /* from server */ proto_tree_add_text(rlogin_tree, tvb, offset, 1, "Startup info received flag (0x00)"); else proto_tree_add_text(rlogin_tree, tvb, offset, 1, "Client Startup Flag (0x00)"); ++offset; } if (!tvb_offset_exists(tvb, offset)) return; /* No more data to check */ if ( hash_info->info_framenum == pinfo->fd->num){ /* * First frame of conversation, hence user info? */ user_info_item = proto_tree_add_item( rlogin_tree, hf_user_info, tvb, offset, tvb_length_remaining(tvb, offset), FALSE); /* * Do server user name. */ str_len = tvb_strsize(tvb, offset); user_info_tree = proto_item_add_subtree( user_info_item, ett_rlogin_user_info); proto_tree_add_text(user_info_tree, tvb, offset, str_len, "Server User Name: %.*s", str_len - 1, tvb_get_ptr(tvb, offset, str_len - 1)); offset += str_len; /* * Do client user name. */ str_len = tvb_strsize(tvb, offset); proto_tree_add_text(user_info_tree, tvb, offset, str_len, "Client User Name: %.*s", str_len - 1, tvb_get_ptr(tvb, offset, str_len - 1)); offset += str_len; /* * Do terminal type/speed. */ str_len = tvb_strsize(tvb, offset); proto_tree_add_text(user_info_tree, tvb, offset, str_len, "Terminal Type/Speed: %.*s", str_len - 1, tvb_get_ptr(tvb, offset, str_len - 1)); offset += str_len; } if (!tvb_offset_exists(tvb, offset)) return; /* No more data to check */ /* test for terminal information, the data will have 2 0xff bytes */ /* look for first 0xff byte */ ti_offset = tvb_find_guint8(tvb, offset, -1, 0xff); if (ti_offset != -1 && tvb_bytes_exist(tvb, ti_offset + 1, 1) && tvb_get_guint8(tvb, ti_offset + 1) == 0xff) { /* * Found terminal info. */ if (ti_offset > offset) { /* * There's data before the terminal info. */ proto_tree_add_text( rlogin_tree, tvb, offset, (ti_offset - offset), "Data"); offset = ti_offset; } window_info_item = proto_tree_add_item(rlogin_tree, hf_window_info, tvb, offset, 12, FALSE ); window_tree = proto_item_add_subtree(window_info_item, ett_rlogin_window); proto_tree_add_text(window_tree, tvb, offset, 2, "Magic Cookie: (0xff, 0xff)"); offset += 2; proto_tree_add_text(window_tree, tvb, offset, 2, "Window size marker: 'ss'"); offset += 2; proto_tree_add_item(window_tree, hf_window_info_rows, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(window_tree, hf_window_info_cols, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(window_tree, hf_window_info_x_pixels, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(window_tree, hf_window_info_y_pixels, tvb, offset, 2, FALSE); offset += 2; } if (tvb_offset_exists(tvb, offset)) { /* * There's more data in the frame. */ proto_tree_add_text(rlogin_tree, tvb, offset, tvb_length_remaining(tvb, offset), "Data"); } } static void dissect_rlogin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { rlogin_hash_entry_t *hash_info = 0; conversation_t *conversation; gint ti_offset; /* Lookup this connection*/ conversation = find_conversation( &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); if ( conversation) /* conversation found */ hash_info = conversation->data; /* new conversation create local data structure */ else { hash_info = g_mem_chunk_alloc(rlogin_vals); hash_info->state = NONE; hash_info->info_framenum = 0; /* no frame has the number 0 */ hash_info->name[ 0] = 0; conversation_new( &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, hash_info, 0); } if (check_col(pinfo->fd, COL_PROTOCOL)) /* update protocol */ col_set_str(pinfo->fd, COL_PROTOCOL, "Rlogin"); if (check_col(pinfo->fd, COL_INFO)){ /* display packet info*/ char temp[1000]; col_clear(pinfo->fd, COL_INFO); if ( hash_info->name[0]) { strcpy( temp, "User name: "); strcat( temp, hash_info->name); strcat( temp, ", "); } else temp[0] = 0; if ( tvb_get_guint8(tvb, 0) == '\0') strcat( temp, "Start Handshake"); else if ( tcp_urgent_pointer) strcat( temp, "Control Message"); else { /* check for terminal info */ ti_offset = tvb_find_guint8(tvb, 0, -1, 0xff); if (ti_offset != -1 && tvb_bytes_exist(tvb, ti_offset + 1, 1) && tvb_get_guint8(tvb, ti_offset + 1) == 0xff) strcat( temp, "Terminal Info"); else { int i; int bytes_to_copy; strcat( temp, "Data: "); i = strlen( temp); bytes_to_copy = tvb_length(tvb); if (bytes_to_copy > 128) bytes_to_copy = 128; tvb_memcpy(tvb, (guint8 *)&temp[i], 0, bytes_to_copy); temp[i + bytes_to_copy] = '\0'; } } col_add_str(pinfo->fd, COL_INFO, temp); } rlogin_state_machine( hash_info, tvb, pinfo); if ( tree) /* if proto tree, decode data */ rlogin_display( hash_info, tvb, pinfo, tree); } void proto_register_rlogin( void){ /* Prep the rlogin protocol, for now, just register it */ static gint *ett[] = { &ett_rlogin, &ett_rlogin_window, &ett_rlogin_window_rows, &ett_rlogin_window_cols, &ett_rlogin_window_x_pixels, &ett_rlogin_window_y_pixels, &ett_rlogin_user_info }; static hf_register_info hf[] = { { &hf_user_info, { "User Info", "rlogin.user_info", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL } }, { &hf_window_info, { "Window Info", "rlogin.window_size", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL } }, { &hf_window_info_rows, { "Rows", "rlogin.window_size.rows", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_window_info_cols, { "Columns", "rlogin.window_size.cols", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_window_info_x_pixels, { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_window_info_y_pixels, { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } } }; proto_rlogin = proto_register_protocol ( "Rlogin Protocol", "Rlogin", "rlogin"); proto_register_field_array(proto_rlogin, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); register_init_routine( &rlogin_init); /* register re-init routine */ } void proto_reg_handoff_rlogin(void) { /* dissector install routine */ dissector_add("tcp.port", TCP_PORT_RLOGIN, dissect_rlogin, proto_rlogin); }