2 * Routines for socks versions 4 &5 packet dissection
3 * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
5 * $Id: packet-socks.c,v 1.8 2000/08/07 03:21:12 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@zing.org>
9 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 * The Version 4 decode is based on SOCKS4.protocol and SOCKS4A.protocol.
28 * The Version 5 decoder is based upon rfc-1928
29 * The Version 5 User/Password authentication is based on rfc-1929.
31 * See http://www.socks.nec.com/socksprot.html for these and other documents
35 /* Possible enhancements -
37 * Add GSS-API authentication per rfc-1961
38 * Add CHAP authentication
39 * Decode FLAG bits per
40 * http://www.socks.nec.com/draft/draft-ietf-aft-socks-pro-v-04.txt
41 * In call_next_dissector, could load the destination address into the
42 * pi structure before calling next dissector.
43 * remove display_string or at least make it use protocol identifiers
44 * socks_hash_entry_t needs to handle V5 address type and domain names
55 #ifdef HAVE_SYS_TYPES_H
56 # include <sys/types.h>
59 #ifdef HAVE_NETINET_IN_H
60 # include <netinet/in.h>
69 #include "alignment.h"
70 #include "conversation.h"
72 #include "packet-tcp.h"
73 #include "packet-udp.h"
75 #ifdef NEED_SNPRINTF_H
81 # include "snprintf.h"
86 #define CHECK_PACKET_LENGTH(X) if (!BYTES_ARE_IN_FRAME(offset, X)){ \
87 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***"); \
90 #define compare_packet(X) (X == (fd->num))
91 #define get_packet_ptr (fd->num)
92 #define row_pointer_type guint32
94 #define TCP_PORT_SOCKS 1080
97 /**************** Socks commands ******************/
99 #define CONNECT_COMMAND 1
100 #define BIND_COMMAND 2
101 #define UDP_ASSOCIATE_COMMAND 3
102 #define PING_COMMAND 0x80
103 #define TRACERT_COMMAND 0x81
106 /********** V5 Authentication methods *************/
108 #define NO_AUTHENTICATION 0
109 #define GSS_API_AUTHENTICATION 1
110 #define USER_NAME_AUTHENTICATION 2
111 #define CHAP_AUTHENTICATION 3
112 #define AUTHENTICATION_FAILED 0xff
115 /*********** Header field identifiers *************/
117 static int proto_socks = -1;
119 static int ett_socks = -1;
120 static int ett_socks_auth = -1;
121 static int ett_socks_name = -1;
123 static int hf_socks_ver = -1;
124 static int hf_socks_ip_dst = -1;
125 static int hf_socks_ip6_dst = -1;
126 static int hf_user_name = -1;
127 static int hf_socks_dstport = -1;
128 static int hf_socks_command = -1;
131 /************* State Machine names ***********/
156 gint32 udp_remote_port;
159 row_pointer_type v4_name_row;
160 row_pointer_type v4_user_name_row;
161 row_pointer_type connect_row;
162 row_pointer_type cmd_reply_row;
163 row_pointer_type bind_reply_row;
164 row_pointer_type command_row;
165 row_pointer_type auth_method_row;
166 row_pointer_type user_name_auth_row;
167 guint32 start_done_row;
169 guint32 dst_addr; /* this needs to handle IPv6 */
175 static char *address_type_table[] = {
185 /* String table for the V4 reply status messages */
187 static char *reply_table_v4[] = {
189 "Rejected or Failed",
190 "Rejected because SOCKS server cannot connect to identd on the client",
191 "Rejected because the client program and identd report different user-ids",
196 /* String table for the V5 reply status messages */
198 static char *reply_table_v5[] = {
200 "General SOCKS server failure",
201 "Connection not allowed by ruleset",
202 "Network unreachable",
204 "Connection refused",
206 "Command not supported",
207 "Address type not supported",
212 #define socks_hash_init_count 20
213 #define socks_hash_val_length (sizeof(socks_hash_entry_t))
215 static GMemChunk *socks_vals = NULL;
218 /************************* Support routines ***************************/
221 static int display_string( const u_char *pd, int offset, frame_data *fd,
222 proto_tree *tree, char *label){
224 /* display a string with a length, characters encoding */
225 /* they are displayed under a tree with the name in Label variable */
226 /* return the length of the string and the length byte */
229 proto_tree *name_tree;
234 int length = GBYTE( pd, offset);
236 if (!BYTES_ARE_IN_FRAME(offset, 8)){
237 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***");
241 strncpy( temp, &pd[ offset + 1], length);
244 ti = proto_tree_add_text(tree, NullTVB, offset, length + 1,
245 "%s: %s" , label, temp);
248 name_tree = proto_item_add_subtree(ti, ett_socks_name);
250 proto_tree_add_text( name_tree, NullTVB, offset, 1, "Length: %d", length);
254 proto_tree_add_text( name_tree, NullTVB, offset, length, "String: %s", temp);
261 static char *get_auth_method_name( guint Number){
263 /* return the name of the authenication method */
265 if ( Number == 0) return "No authentication";
266 if ( Number == 1) return "GSSAPI";
267 if ( Number == 2) return "Username/Password";
268 if ( Number == 3) return "Chap";
269 if (( Number >= 4) && ( Number <= 0x7f))return "IANA assigned";
270 if (( Number >= 0x80) && ( Number <= 0xfe)) return "private method";
271 if ( Number == 0xff) return "no acceptable method";
273 /* shouldn't reach here */
275 return "Bad method number (not 0-0xff)";
279 static char *get_command_name( guint Number){
281 /* return the name of the command as a string */
283 if ( Number == 0) return "Unknow";
284 if ( Number == 1) return "Connect";
285 if ( Number == 2) return "Bind";
286 if ( Number == 3) return "UdpAssociate";
287 if ( Number == 0x80) return "Ping";
288 if ( Number == 0x81) return "Traceroute";
293 static int display_address( const u_char *pd, int offset,
294 frame_data *fd, proto_tree *tree) {
296 /* decode and display the v5 address, return offset of next byte */
298 int a_type = GBYTE( pd, offset);
300 proto_tree_add_text( tree, NullTVB, offset, 1,
301 "Address Type: %d (%s)", a_type,
302 address_type_table[ MAX( 0, MIN( a_type,
303 array_length( address_type_table)-1))]);
307 if ( a_type == 1){ /* IPv4 address */
308 if (!BYTES_ARE_IN_FRAME(offset, 4))
309 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***");
311 proto_tree_add_ipv4( tree, hf_socks_ip_dst, NullTVB, offset,
312 4, GWORD( pd, offset));
315 else if ( a_type == 3){ /* domain name address */
317 offset += display_string( pd, offset, fd, tree,
320 else if ( a_type == 4){ /* IPv6 address */
321 if (!BYTES_ARE_IN_FRAME(offset, 16))
322 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***");
324 proto_tree_add_ipv6( tree, hf_socks_ip6_dst, NullTVB, offset,
333 static int get_address_v5( const u_char *pd, int offset,
334 socks_hash_entry_t *hash_info) {
336 /* decode the v5 address and return offset of next byte */
337 /*$$$ this needs to handle IPV6 and domain name addresses */
340 int a_type = GBYTE( pd, offset++);
342 if ( a_type == 1){ /* IPv4 address */
345 hash_info->dst_addr = GWORD( pd, offset);
349 else if ( a_type == 4) /* IPv6 address */
352 else if ( a_type == 3) /* domain name address */
353 offset += GBYTE( pd, offset) + 1;
359 /********************* V5 UDP Associate handlers ***********************/
361 static void socks_udp_dissector( const u_char *pd, int offset, frame_data *fd,
364 /* Conversation dissector called from UDP dissector. Decode and display */
365 /* the socks header, the pass the rest of the data to the udp port */
366 /* decode routine to handle the payload. */
369 socks_hash_entry_t *hash_info;
370 conversation_t *conversation;
371 proto_tree *socks_tree;
374 conversation = find_conversation( &pi.src, &pi.dst, pi.ptype,
375 pi.srcport, pi.destport);
377 g_assert( conversation); /* should always find a conversation */
379 hash_info = (socks_hash_entry_t*)conversation->data;
381 if (check_col(fd, COL_PROTOCOL))
382 col_add_str(fd, COL_PROTOCOL, "Socks");
384 if (check_col(fd, COL_INFO))
385 col_add_fstr(fd, COL_INFO, "Version: 5, UDP Associated packet");
388 ti = proto_tree_add_protocol_format( tree, proto_socks, NullTVB, offset,
389 END_OF_FRAME, "Socks" );
391 socks_tree = proto_item_add_subtree(ti, ett_socks);
393 CHECK_PACKET_LENGTH( 3);
395 proto_tree_add_text( socks_tree, NullTVB, offset, 2, "Reserved");
398 proto_tree_add_text( socks_tree, NullTVB, offset, 1, "Fragment Number: %d", GBYTE( pd,offset));
402 offset = display_address( pd, offset, fd, socks_tree);
403 hash_info->udp_remote_port = pntohs( &pd[ offset]);
405 CHECK_PACKET_LENGTH( 2);
406 proto_tree_add_uint( socks_tree, hf_socks_dstport, NullTVB,
407 offset, 2, hash_info->udp_remote_port);
411 else { /* no tree, skip past the socks header */
412 CHECK_PACKET_LENGTH( 3);
414 offset = get_address_v5( pd,offset, 0) + 2;
418 /* set pi src/dst port and call the udp sub-dissector lookup */
420 if ( pi.srcport == hash_info->port)
425 *ptr = hash_info->udp_remote_port;
427 decode_udp_ports( pd, offset, fd, tree, pi.srcport, pi.destport);
429 *ptr = hash_info->udp_port;
434 void new_udp_conversation( socks_hash_entry_t *hash_info){
436 conversation_t *conversation = conversation_new( &pi.src, &pi.dst, PT_UDP,
437 hash_info->udp_port, hash_info->port, hash_info);
439 g_assert( conversation);
441 conversation->is_old_dissector = TRUE;
442 conversation->dissector.old = socks_udp_dissector;
448 /**************** Protocol Tree Display routines ******************/
451 void display_socks_v4( const u_char *pd, int offset, frame_data *fd,
452 proto_tree *parent, proto_tree *tree, socks_hash_entry_t *hash_info) {
455 /* Display the protocol tree for the V5 version. This routine uses the */
456 /* stored conversation information to decide what to do with the row. */
457 /* Per packet information would have been better to do this, but we */
458 /* didn't have that when I wrote this. And I didn't expect this to get */
464 /* Display command from client */
465 if (compare_packet( hash_info->connect_row)){
467 CHECK_PACKET_LENGTH( 8);
468 proto_tree_add_text( tree, NullTVB, offset, 1,
469 "Version: %u ", hash_info->version);
471 command = GBYTE( pd, offset);
473 proto_tree_add_text( tree, NullTVB, offset, 1,
474 "Command: %u (%s)", command,
475 get_command_name( command));
479 proto_tree_add_uint( tree, hf_socks_dstport, NullTVB, offset, 2,
480 pntohs( &pd[ offset]));
483 /* Do destination address */
484 proto_tree_add_ipv4( tree, hf_socks_ip_dst, NullTVB, offset,
485 4, GWORD( pd, offset));
489 /*$$ check this, needs to do length checking */
490 /* display user name */
491 proto_tree_add_string( tree, hf_user_name, NullTVB, offset,
492 strlen( &pd[offset]) + 1,
496 /*Display command response from server*/
498 else if ( compare_packet( hash_info->cmd_reply_row)){
500 CHECK_PACKET_LENGTH( 8);
501 proto_tree_add_text( tree, NullTVB, offset, 1,
502 "Version: %u (should be 0) ", GBYTE( pd, offset));
504 /* Do results code */
505 proto_tree_add_text( tree, NullTVB, offset, 1,
506 "Result Code: %u (%s)", GBYTE( pd, offset) ,
507 reply_table_v4[ MAX(0, MIN( GBYTE( pd, offset) - 90, 4))]);
511 proto_tree_add_uint( tree, hf_socks_dstport, NullTVB, offset, 2,
512 pntohs( &pd[ offset]));
514 /* Do remote address */
515 proto_tree_add_ipv4( tree, hf_socks_ip_dst, NullTVB, offset, 4,
519 else if ( compare_packet( hash_info->v4_user_name_row)){
521 /*$$ check this, needs to do length checking */
522 proto_tree_add_text( tree, NullTVB, offset, strlen( &pd[offset]),
523 "User Name: %s", &pd[offset]);
529 void display_socks_v5( const u_char *pd, int offset, frame_data *fd,
530 proto_tree *parent, proto_tree *tree, socks_hash_entry_t *hash_info) {
532 /* Display the protocol tree for the version. This routine uses the */
533 /* stored conversation information to decide what to do with the row. */
534 /* Per packet information would have been better to do this, but we */
535 /* didn't have that when I wrote this. And I didn't expect this to get */
543 if (compare_packet( hash_info->connect_row)){
545 proto_tree *AuthTree;
548 CHECK_PACKET_LENGTH( 2);
550 proto_tree_add_uint( tree, hf_socks_ver, NullTVB, offset, 1,
554 temp = GBYTE( pd, offset); /* Get Auth method count */
555 /* build auth tree */
556 ti = proto_tree_add_text( tree, NullTVB, offset, 1,
557 "Client Authentication Methods");
559 AuthTree = proto_item_add_subtree(ti, ett_socks_auth);
561 proto_tree_add_text( AuthTree, NullTVB, offset, 1,
565 CHECK_PACKET_LENGTH( temp);
567 for( i = 0; i < temp; ++i) {
569 AuthMethodStr = get_auth_method_name(
570 GBYTE( pd, offset + i));
571 proto_tree_add_text( AuthTree, NullTVB, offset + i, 1,
572 "Method[%d]: %u (%s)", i,
573 GBYTE( pd, offset + i), AuthMethodStr);
576 } /* Get accepted auth method */
577 else if (compare_packet( hash_info->auth_method_row)) {
580 CHECK_PACKET_LENGTH( 1);
582 proto_tree_add_text( tree, NullTVB, offset, 1,
583 "Accepted Auth Method: 0x%0x (%s)", GBYTE( pd, offset),
584 get_auth_method_name( GBYTE( pd, offset)));
587 } /* handle user/password auth */
588 else if (compare_packet( hash_info->user_name_auth_row)) {
590 proto_tree_add_text( tree, NullTVB, offset, 1,
591 "Version: %u ", hash_info->version);
593 /* process user name */
594 offset += display_string( pd, offset, fd, tree,
596 /* process password */
597 offset += display_string( pd, offset, fd, tree,
600 /* command to the server */
601 /* command response from server */
602 else if ((compare_packet( hash_info->command_row)) ||
603 (compare_packet( hash_info->cmd_reply_row)) ||
604 (compare_packet( hash_info->bind_reply_row))){
606 proto_tree_add_text( tree, NullTVB, offset, 1,
607 "Version: %u ", hash_info->version);
609 CHECK_PACKET_LENGTH( 1);
613 command = GBYTE( pd, offset);
615 if (compare_packet( hash_info->command_row))
616 proto_tree_add_text( tree, NullTVB, offset, 1, "Command: %u (%s)",
617 command, get_command_name( command));
619 proto_tree_add_text( tree, NullTVB, offset, 1, "Status: %d (%s)",
620 GBYTE( pd, offset), reply_table_v5[ MAX( 0,
621 MIN(GBYTE( pd, offset) - 90, 9))]);
624 proto_tree_add_text( tree, NullTVB, offset, 1,
625 "Reserved: 0x%0x (should = 0x00)", GBYTE( pd, offset));
628 offset = display_address( pd, offset, fd, tree);
630 CHECK_PACKET_LENGTH( 2);
632 proto_tree_add_text( tree, NullTVB, offset, 2,
634 (compare_packet( hash_info->bind_reply_row) ?
635 "Remote Host " : ""),
636 pntohs( &pd[ offset]));
642 /**************** Decoder State Machines ******************/
645 static guint state_machine_v4( socks_hash_entry_t *hash_info, const u_char *pd,
646 int offset, frame_data *fd) {
648 /* Decode V4 protocol. This is done on the first pass through the */
649 /* list. Based upon the current state, decode the packet and determine */
650 /* what the next state should be. If we had per packet information, */
651 /* this would be the place to load them up. */
653 if ( hash_info->state == None) { /* new connection */
655 if (check_col(fd, COL_INFO))
656 col_append_str(fd, COL_INFO, " Connect to server request");
658 hash_info->state = Connecting; /* change state */
660 hash_info->command = GBYTE( pd, offset + 1);
661 /* get remote port */
662 if ( hash_info->command == CONNECT_COMMAND)
663 hash_info->port = pntohs( &pd[ offset + 2]);
664 /* get remote address */
665 hash_info->dst_addr = GWORD( pd, offset + 4);
667 /* save the packet pointer */
668 hash_info->connect_row = get_packet_ptr;
670 /* skip past this stuff */
671 hash_info->connect_offset = offset + 8;
675 if ( offset == pi.len) /* if no user name */
677 hash_info->state = V4UserNameWait;
680 hash_info->connect_offset += strlen( &pd[ offset]) + 1;
682 if ( !hash_info->dst_addr){ /* if no dest address */
684 if ( hash_info->connect_offset < pi.len ) {
685 /*$$$ copy remote name here ??? */
686 hash_info->state = Connecting;
689 hash_info->state = V4NameWait;
691 /* waiting for V4 user name */
692 }else if ( hash_info->state == V4UserNameWait){
694 if (check_col(fd, COL_INFO))
695 col_append_str(fd, COL_INFO, " Connect Request (User name)");
697 hash_info->v4_user_name_row = get_packet_ptr;
698 /*$$$ may need to check for domain name here */
699 hash_info->state = Connecting;
701 /* waiting for V4 domain name */
702 else if ( hash_info->state == V4NameWait){
704 hash_info->v4_name_row = get_packet_ptr;
705 hash_info->state = Connecting;
708 else if ( hash_info->state == Connecting){
710 if (check_col(fd, COL_INFO))
711 col_append_str(fd, COL_INFO, " Connect Response");
713 /* save packet pointer */
714 hash_info->cmd_reply_row = get_packet_ptr;
715 hash_info->state = Done; /* change state */
724 static void state_machine_v5( socks_hash_entry_t *hash_info, const u_char *pd,
725 int offset, frame_data *fd) {
727 /* Decode V5 protocol. This is done on the first pass through the */
728 /* list. Based upon the current state, decode the packet and determine */
729 /* what the next state should be. If we had per packet information, */
730 /* this would be the place to load them up. */
735 if ( hash_info->state == None) {
737 if (check_col(fd, COL_INFO))
738 col_append_str(fd, COL_INFO, " Connect to server request");
740 hash_info->state = Connecting; /* change state */
741 hash_info->connect_row = get_packet_ptr;
743 if (!BYTES_ARE_IN_FRAME(offset, 1)){
744 hash_info->state = Done; /* change state */
748 temp = GBYTE( pd, offset + 1);
749 /* skip past auth methods */
750 offset = hash_info->connect_offset = offset + 1 + temp;
752 else if ( hash_info->state == Connecting){
754 guint AuthMethod = GBYTE( pd, offset + 1);
756 if (check_col(fd, COL_INFO))
757 col_append_str(fd, COL_INFO, " Connect to server response");
759 hash_info->auth_method_row = get_packet_ptr;
761 if ( AuthMethod == NO_AUTHENTICATION)
762 hash_info->state = V5Command;
764 else if ( AuthMethod == USER_NAME_AUTHENTICATION)
765 hash_info->state = UserNameAuth;
767 else if ( AuthMethod == GSS_API_AUTHENTICATION)
768 /*$$$ should be this hash_info->state = GssApiAuth; */
769 hash_info->state = Done;
771 else hash_info->state = Done; /*Auth failed or error*/
775 else if ( hash_info->state == V5Command) { /* Handle V5 Command */
779 if (!BYTES_ARE_IN_FRAME(offset, 1)){
780 hash_info->state = Done; /* change state */
784 hash_info->command = GBYTE( pd, offset + 1); /* get command */
786 if (check_col(fd, COL_INFO))
787 col_append_fstr(fd, COL_INFO, " Command Request - %s",
788 get_command_name(hash_info->command));
790 hash_info->state = V5Reply;
791 hash_info->command_row = get_packet_ptr;
793 offset += 3; /* skip to address type */
795 offset = get_address_v5( pd, offset, hash_info);
797 if (!BYTES_ARE_IN_FRAME(offset, 1)){
798 hash_info->state = Done;
801 temp = GBYTE( pd, offset);
803 if (( hash_info->command == CONNECT_COMMAND) ||
804 ( hash_info->command == UDP_ASSOCIATE_COMMAND))
805 /* get remote port */
806 hash_info->port = pntohs( &pd[ offset]);
809 else if ( hash_info->state == V5Reply) { /* V5 Command Reply */
812 if (check_col(fd, COL_INFO))
813 col_append_fstr(fd, COL_INFO, " Command Response - %s",
814 get_command_name(hash_info->command));
816 hash_info->cmd_reply_row = get_packet_ptr;
818 if (( hash_info->command == CONNECT_COMMAND) ||
819 (hash_info->command == PING_COMMAND) ||
820 (hash_info->command == TRACERT_COMMAND))
821 hash_info->state = Done;
823 else if ( hash_info->command == BIND_COMMAND)
824 hash_info->state = V5BindReply;
826 else if ( hash_info->command == UDP_ASSOCIATE_COMMAND){
827 offset += 3; /* skip to address type */
828 offset = get_address_v5( pd, offset, hash_info);
830 /* save server udp port and create upd conversation */
831 if (!BYTES_ARE_IN_FRAME(offset, 2)){
832 hash_info->state = Done;
835 hash_info->udp_port = pntohs( &pd[ offset]);
837 new_udp_conversation( hash_info);
839 /*$$ may need else statement to handle unknows and generate error message */
843 else if ( hash_info->state == V5BindReply) { /* V5 Bind Second Reply */
845 if (check_col(fd, COL_INFO))
846 col_append_str(fd, COL_INFO, " Command Response: Bind remote host info");
848 hash_info->bind_reply_row = get_packet_ptr;
849 hash_info->state = Done;
851 else if ( hash_info->state == UserNameAuth) { /* Handle V5 User Auth*/
852 if (check_col(fd, COL_INFO))
853 col_append_str(fd, COL_INFO,
854 " User authentication response");
856 hash_info->user_name_auth_row = get_packet_ptr;
857 hash_info->state = AuthReply;
860 else if ( hash_info->state == AuthReply){ /* V5 User Auth reply */
861 hash_info->cmd_reply_row = get_packet_ptr;
862 if (check_col(fd, COL_INFO))
863 col_append_str(fd, COL_INFO, " User authentication reply");
864 hash_info->state = V5Command;
870 static void display_ping_and_tracert( const u_char *pd, int offset,
871 frame_data *fd, proto_tree *tree, socks_hash_entry_t *hash_info) {
873 /* Display the ping/trace_route conversation */
876 const u_char *data, *dataend;
877 const u_char *lineend, *eol;
880 /* handle the end command */
881 if ( pi.destport == TCP_PORT_SOCKS){
882 if (check_col(fd, COL_INFO))
883 col_append_str(fd, COL_INFO, ", Terminate Request");
886 proto_tree_add_text(tree, NullTVB, offset, 1,
887 (hash_info->command == PING_COMMAND) ?
888 "Ping: End command" :
889 "Traceroute: End command");
891 else{ /* display the PING or Traceroute results */
892 if (check_col(fd, COL_INFO))
893 col_append_str(fd, COL_INFO, ", Results");
896 proto_tree_add_text(tree, NullTVB, offset, END_OF_FRAME,
897 (hash_info->command == PING_COMMAND) ?
899 "Traceroute Results");
902 dataend = data + END_OF_FRAME;
904 while (data < dataend) {
906 lineend = find_line_end(data, dataend, &eol);
907 linelen = lineend - data;
909 proto_tree_add_text( tree, NullTVB, offset, linelen,
910 format_text(data, linelen));
920 static void call_next_dissector( const u_char *pd, int offset, frame_data *fd,
921 proto_tree *tree, socks_hash_entry_t *hash_info) {
923 /* Display the results for PING and TRACERT extensions or */
924 /* Call TCP dissector for the port that was passed during the */
925 /* connect process */
926 /* Load pointer to pi.XXXport depending upon the direction, change */
927 /* pi port to the remote port, call next dissecotr to decode the */
928 /* payload, and restore the pi port after that is done. */
932 if (( hash_info->command == PING_COMMAND) ||
933 ( hash_info->command == TRACERT_COMMAND))
935 display_ping_and_tracert( pd, offset, fd, tree, hash_info);
937 else { /* call the tcp port decoder to handle the payload */
939 /*$$$ may want to load dest address here */
941 if ( pi.destport == TCP_PORT_SOCKS)
946 *ptr = hash_info->port;
947 decode_tcp_ports( pd, offset, fd, tree, pi.srcport, pi.destport);
948 *ptr = TCP_PORT_SOCKS;
955 dissect_socks(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
957 proto_tree *socks_tree;
959 socks_hash_entry_t *hash_info;
960 conversation_t *conversation;
963 conversation = find_conversation( &pi.src, &pi.dst, pi.ptype,
964 pi.srcport, pi.destport);
966 if ( conversation) /* conversation found */
967 hash_info = conversation->data;
969 /* new conversation create local data structure */
971 hash_info = g_mem_chunk_alloc(socks_vals);
972 hash_info->start_done_row = G_MAXINT;
973 hash_info->state = None;
974 hash_info->port = -1;
975 hash_info->version = GBYTE( pd, offset); /* get version*/
977 if (( hash_info->version != 4) && /* error test version */
978 ( hash_info->version != 5))
979 hash_info->state = Done;
981 conversation_new( &pi.src, &pi.dst, pi.ptype,
982 pi.srcport, pi.destport, hash_info);
985 /* display summary window information */
987 if (check_col(fd, COL_PROTOCOL))
988 col_add_str(fd, COL_PROTOCOL, "Socks");
990 if (check_col(fd, COL_INFO)){
991 if (( hash_info->version == 4) || ( hash_info->version == 5)){
992 col_add_fstr(fd, COL_INFO, "Version: %d",
995 else /* unknown version display error */
996 col_add_str(fd, COL_INFO, "Unknown");
999 if ( hash_info->command == PING_COMMAND)
1000 col_append_str(fd, COL_INFO, ", Ping Req");
1001 if ( hash_info->command == TRACERT_COMMAND)
1002 col_append_str(fd, COL_INFO, ", Traceroute Req");
1004 if ( hash_info->port != -1)
1005 col_append_fstr(fd, COL_INFO, ", Remote Port: %d",
1010 /* run state machine if needed */
1012 if ((hash_info->state != Done) && ( !fd->flags.visited)){
1014 if ( hash_info->version == 4)
1015 state_machine_v4( hash_info, pd, offset, fd);
1017 else if ( hash_info->version == 5)
1018 state_machine_v5( hash_info, pd, offset, fd);
1020 if (hash_info->state == Done) { /* if done now */
1021 hash_info->start_done_row = fd->num;
1025 /* if proto tree, decode and display */
1028 ti = proto_tree_add_item( tree, proto_socks, NullTVB, offset,
1029 END_OF_FRAME, FALSE );
1031 socks_tree = proto_item_add_subtree(ti, ett_socks);
1033 if ( hash_info->version == 4)
1034 display_socks_v4( pd, offset, fd, tree, socks_tree,
1037 else if ( hash_info->version == 5)
1038 display_socks_v5( pd, offset, fd, tree, socks_tree,
1041 /* if past startup, add the faked stuff */
1042 if ( fd->num > hash_info->start_done_row){
1043 /* add info to tree */
1044 proto_tree_add_text( socks_tree, NullTVB, offset, 0,
1045 "Command: %d (%s)", hash_info->command,
1046 get_command_name(hash_info->command));
1048 proto_tree_add_ipv4( socks_tree, hf_socks_ip_dst, NullTVB,
1049 offset, 0, hash_info->dst_addr);
1051 /* no fake address for ping & traceroute */
1053 if (( hash_info->command != PING_COMMAND) &&
1054 ( hash_info->command != TRACERT_COMMAND)){
1055 proto_tree_add_uint( socks_tree, hf_socks_dstport, NullTVB,
1056 offset, 0, hash_info->port);
1063 /* call next dissector if ready */
1065 if ( fd->num > hash_info->start_done_row){
1066 call_next_dissector( pd, offset, fd, tree, hash_info);
1072 static void socks_reinit( void){
1074 /* Do the cleanup work when a new pass through the packet list is */
1075 /* performed. Reset the highest row seen counter and re-initialize the */
1076 /* conversation memory chunks. */
1079 g_mem_chunk_destroy(socks_vals);
1081 socks_vals = g_mem_chunk_new("socks_vals", socks_hash_val_length,
1082 socks_hash_init_count * socks_hash_val_length,
1088 proto_register_socks( void){
1090 /*** Prep the socks protocol, register it and a initialization routine */
1091 /* to clear the hash stuff. */
1094 static gint *ett[] = {
1101 static hf_register_info hf[] = {
1105 { "Version", "socks.ver", FT_UINT8, BASE_NONE, NULL,
1110 { "Remote Address", "socks.dst", FT_IPv4, BASE_NONE, NULL,
1114 { &hf_socks_ip6_dst,
1115 { "Remote Address", "socks.dstV6", FT_IPv6, BASE_NONE, NULL,
1121 { "User Name", "socks.username", FT_STRING, BASE_NONE,
1125 { &hf_socks_dstport,
1126 { "Remote Port", "socks.dstport", FT_UINT16,
1127 BASE_DEC, NULL, 0x0, ""
1130 { &hf_socks_command,
1131 { "Command", "socks.command", FT_UINT16,
1132 BASE_DEC, NULL, 0x0, ""
1139 proto_socks = proto_register_protocol (
1140 "Socks Protocol", "socks");
1142 proto_register_field_array(proto_socks, hf, array_length(hf));
1143 proto_register_subtree_array(ett, array_length(ett));
1145 register_init_routine( &socks_reinit); /* register re-init routine */
1150 proto_reg_handoff_socks(void) {
1152 /* dissector install routine */
1154 old_dissector_add("tcp.port", TCP_PORT_SOCKS, dissect_socks);