/* packet-rlogin.c * Routines for unix rlogin packet dissection * Copyright 2000, Jeffrey C. Foster * * $Id: packet-rlogin.c,v 1.9 2000/08/13 14:08:37 deniel 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 #ifdef NEED_SNPRINTF_H # include "snprintf.h" #endif #include "packet.h" #include "resolv.h" #include "globals.h" #include "alignment.h" #include "conversation.h" #include "packet-tcp.h" #define CHECK_PACKET_LENGTH(X) if (!BYTES_ARE_IN_FRAME(offset, X)){ \ proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***"); \ return; } #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; int 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; /* Find the length of a '\0'-terminated string, *INCLUDING* the terminating '\0', but don't run past the end of the packet doing so. */ static int string_len(const char *str, int maxlen) { const char *str_end; str_end = memchr(str, '\0', maxlen); if (str_end == NULL) { /* No '\0' found - return the length as of when we stopped, plus one to force the length to be too long and and CHECK_PACKET_LENGTH to fail. */ return maxlen + 1; } return str_end - str + 1; } 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, const u_char *pd, int offset, frame_data *fd) { /* rlogin stream decoder */ /* Just watched for second packet from client with the user name and */ /* terminal type information. */ if ( pi.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 > fd->abs_secs) || (( last_abs_sec == fd->abs_secs) && ( last_abs_usec >= fd->abs_usecs))) return; last_abs_sec = fd->abs_secs; /* save timestamp */ last_abs_usec = fd->abs_usecs; if ( !IS_DATA_IN_FRAME(offset)) /* exit if no data */ return; if ( hash_info->state == NONE){ /* new connection*/ if ( GBYTE( pd, offset + 1)) { /* expect a NULL */ hash_info->state = DONE; /* quit, no NULL */ return; } else { if (( END_OF_FRAME) <= 1) /* if no data */ hash_info->state = USER_INFO_WAIT; else { hash_info->state = DONE; hash_info->info_framenum = 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 = fd->num; /* save name for later*/ strncpy( hash_info->name, &pd[ offset], NAME_LEN); hash_info->name[ NAME_LEN] = 0; if (check_col(fd, COL_INFO)) /* update summary */ col_append_str( fd, COL_INFO, ", User information"); } } static void rlogin_display( rlogin_hash_entry_t *hash_info, const u_char *pd, int offset, frame_data *fd, proto_tree *tree) { /* Display the proto tree */ proto_tree *rlogin_tree, *user_info_tree, *window_tree; proto_item *ti; guint8 *Ptr; const char *str; int str_len; proto_item *user_info_item, *window_info_item; ti = proto_tree_add_item( tree, proto_rlogin, NullTVB, offset, END_OF_FRAME, FALSE); rlogin_tree = proto_item_add_subtree(ti, ett_rlogin); if ( !IS_DATA_IN_FRAME(offset)) /* exit if no data */ return; if ( tcp_urgent_pointer && /* if control message */ BYTES_ARE_IN_FRAME(offset + tcp_urgent_pointer - 1, 1)) { int i = offset + tcp_urgent_pointer - 1; guint16 Temp = GBYTE( pd, i); if ( i < offset) /* check for data in front */ proto_tree_add_text( rlogin_tree, NullTVB, offset, i - offset, "Data"); proto_tree_add_text( rlogin_tree, NullTVB, i, 1, "Control byte: %u (%s)", Temp, (Temp == 2) ? "Clear buffer" : (Temp == 0x10) ? "Raw mode" : (Temp == 0x20) ? "Cooked mode" : (Temp == 0x80) ? "Window size request" : "Unknown"); offset = i; /* adjust offset */ } else if ( !GBYTE( pd, offset)){ /* startup */ if ( pi.srcport== RLOGIN_PORT) /* from server */ proto_tree_add_text(rlogin_tree, NullTVB, offset, 1, "Startup info received flag (0x00)"); else proto_tree_add_text(rlogin_tree, NullTVB, offset, 1, "Client Startup Flag (0x00)"); ++offset; } if (!IS_DATA_IN_FRAME(offset)) return; /* No more data to check */ if ( hash_info->info_framenum == fd->num){ /* user info ?*/ user_info_item = proto_tree_add_item( rlogin_tree, hf_user_info, NullTVB, offset, END_OF_FRAME, FALSE); str = &pd[ offset]; /* do server user name */ str_len = string_len( str, END_OF_FRAME); CHECK_PACKET_LENGTH( str_len); user_info_tree = proto_item_add_subtree( user_info_item, ett_rlogin_user_info); proto_tree_add_text( user_info_tree, NullTVB, offset, str_len, "Server User Name: %.*s", str_len, str); offset += str_len; if (!IS_DATA_IN_FRAME(offset)) return; /* No more data to check */ str = &pd[ offset]; /* do client user name */ str_len = string_len( str, END_OF_FRAME); CHECK_PACKET_LENGTH( str_len); proto_tree_add_text( user_info_tree, NullTVB, offset, str_len, "Client User Name: %.*s", str_len, str); offset += str_len; if (!IS_DATA_IN_FRAME(offset)) return; /* No more data to check */ str = &pd[ offset]; /* do terminal type/speed */ str_len = string_len( str, END_OF_FRAME); CHECK_PACKET_LENGTH( str_len); proto_tree_add_text( user_info_tree, NullTVB, offset, str_len, "Terminal Type/Speed: %.*s", str_len, str); offset += str_len; } if (!IS_DATA_IN_FRAME(offset)) return; /* No more data to check */ /* test for terminal information, the data will have 2 0xff bytes */ /* look for first 0xff byte */ Ptr = (guint8*)memchr( &pd[ offset], 0xff, END_OF_FRAME); if (( Ptr) && (*(Ptr + 1) == 0xff)) { /* found terminal info */ int ti_offset = Ptr - pd; /* get offset */ if ( ti_offset < offset){ /*if data before terminal info*/ proto_tree_add_text( rlogin_tree, NullTVB, offset, (ti_offset - offset), "Data"); offset = ti_offset; } CHECK_PACKET_LENGTH( 12); window_info_item = proto_tree_add_item( rlogin_tree, hf_window_info, NullTVB, offset, 12, FALSE ); window_tree = proto_item_add_subtree( window_info_item, ett_rlogin_window); CHECK_PACKET_LENGTH( 2); proto_tree_add_text( window_tree, NullTVB, offset, 2, "Magic Cookie: (0xff, 0xff)"); offset += 2; CHECK_PACKET_LENGTH( 2); proto_tree_add_text( window_tree, NullTVB, offset, 2, "Window size marker: 'ss'"); offset += 2; CHECK_PACKET_LENGTH( 2); proto_tree_add_uint( window_tree, hf_window_info_rows, NullTVB, offset, 2, pntohs( &pd[offset])); offset += 2; CHECK_PACKET_LENGTH( 2); proto_tree_add_uint( window_tree, hf_window_info_cols, NullTVB, offset, 2, pntohs( &pd[offset]) ); offset += 2; CHECK_PACKET_LENGTH( 2); proto_tree_add_uint( window_tree, hf_window_info_x_pixels, NullTVB, offset, 2, pntohs( &pd[offset])); offset += 2; CHECK_PACKET_LENGTH( 2); proto_tree_add_uint( window_tree, hf_window_info_y_pixels, NullTVB, offset, 2, pntohs( &pd[offset]) ); offset += 2; } if ( END_OF_FRAME != 0) /* if more data */ proto_tree_add_text(rlogin_tree, NullTVB, offset, END_OF_FRAME, "Data"); } static void dissect_rlogin(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) { guint8 *Ptr; rlogin_hash_entry_t *hash_info = 0; conversation_t *conversation; OLD_CHECK_DISPLAY_AS_DATA(proto_rlogin, pd, offset, fd, tree); /* Lookup this connection*/ conversation = find_conversation( &pi.src, &pi.dst, pi.ptype, pi.srcport, pi.destport); 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 = -1; hash_info->name[ 0] = 0; conversation_new( &pi.src, &pi.dst, pi.ptype, pi.srcport, pi.destport, hash_info); } if (check_col(fd, COL_PROTOCOL)) /* update protocol */ col_add_str(fd, COL_PROTOCOL, "Rlogin"); if (check_col(fd, COL_INFO)){ /* display packet info*/ char temp[1000]; if ( hash_info->name[0]) { strcpy( temp, "User name: "); strcat( temp, hash_info->name); strcat( temp, ", "); } else temp[0] = 0; if ( !GBYTE(pd, offset)) strcat( temp, "Start Handshake"); else if ( tcp_urgent_pointer) strcat( temp, "Control Message"); else { /* check for terminal info */ Ptr = (guint8*)memchr( &pd[ offset], 0xff, END_OF_FRAME); if (( Ptr) && (*(Ptr + 1) == 0xff)) strcat( temp, "Terminal Info"); else { int i; strcat( temp, "Data:"); i = strlen( temp); strncat( temp, &pd[ offset], 128); temp[ i + MIN( 128, END_OF_FRAME)] = 0; } } col_add_str(fd, COL_INFO, temp); } rlogin_state_machine( hash_info, pd, offset, fd); if ( tree) /* if proto tree, decode data */ rlogin_display( hash_info, pd, offset, fd, 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, "" } }, { &hf_window_info, { "Window Info", "rlogin.window_size", FT_NONE, BASE_NONE, NULL, 0x0, "" } }, { &hf_window_info_rows, { "Rows", "rlogin.window_size.rows", FT_UINT16, BASE_DEC, NULL, 0x0, "" } }, { &hf_window_info_cols, { "Columns", "rlogin.window_size.cols", FT_UINT16, BASE_DEC, NULL, 0x0, "" } }, { &hf_window_info_x_pixels, { "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16, BASE_DEC, NULL, 0x0, "" } }, { &hf_window_info_y_pixels, { "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16, BASE_DEC, NULL, 0x0, "" } } }; proto_rlogin = proto_register_protocol ( "Rlogin Protocol", "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 */ old_dissector_add("tcp.port", TCP_PORT_RLOGIN, dissect_rlogin); }