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.23 2001/09/03 10:33:07 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 * The Version 4 decode is based on SOCKS4.protocol and SOCKS4A.protocol.
27 * The Version 5 decoder is based upon rfc-1928
28 * The Version 5 User/Password authentication is based on rfc-1929.
30 * See http://www.socks.nec.com/socksprot.html for these and other documents
34 * 2001-01-08 JCFoster Fixed problem with NULL pointer for hash data.
35 * Now test and exit if hash_info is null.
38 /* Possible enhancements -
40 * Add GSS-API authentication per rfc-1961
41 * Add CHAP authentication
42 * Decode FLAG bits per
43 * http://www.socks.nec.com/draft/draft-ietf-aft-socks-pro-v-04.txt
44 * In call_next_dissector, could load the destination address into the
45 * pi structure before calling next dissector.
46 * remove display_string or at least make it use protocol identifiers
47 * socks_hash_entry_t needs to handle V5 address type and domain names
58 #ifdef HAVE_SYS_TYPES_H
59 # include <sys/types.h>
62 #ifdef HAVE_NETINET_IN_H
63 # include <netinet/in.h>
70 #ifdef NEED_SNPRINTF_H
71 # include "snprintf.h"
76 #include "alignment.h"
77 #include "conversation.h"
79 #include "packet-tcp.h"
80 #include "packet-udp.h"
84 #define CHECK_PACKET_LENGTH(X) if (!BYTES_ARE_IN_FRAME(offset, X)){ \
85 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***"); \
88 #define compare_packet(X) (X == (fd->num))
89 #define get_packet_ptr (fd->num)
90 #define row_pointer_type guint32
92 #define TCP_PORT_SOCKS 1080
95 /**************** Socks commands ******************/
97 #define CONNECT_COMMAND 1
98 #define BIND_COMMAND 2
99 #define UDP_ASSOCIATE_COMMAND 3
100 #define PING_COMMAND 0x80
101 #define TRACERT_COMMAND 0x81
104 /********** V5 Authentication methods *************/
106 #define NO_AUTHENTICATION 0
107 #define GSS_API_AUTHENTICATION 1
108 #define USER_NAME_AUTHENTICATION 2
109 #define CHAP_AUTHENTICATION 3
110 #define AUTHENTICATION_FAILED 0xff
113 /*********** Header field identifiers *************/
115 static int proto_socks = -1;
117 static int ett_socks = -1;
118 static int ett_socks_auth = -1;
119 static int ett_socks_name = -1;
121 static int hf_socks_ver = -1;
122 static int hf_socks_ip_dst = -1;
123 static int hf_socks_ip6_dst = -1;
124 static int hf_user_name = -1;
125 static int hf_socks_dstport = -1;
126 static int hf_socks_command = -1;
129 /************* State Machine names ***********/
154 gint32 udp_remote_port;
157 row_pointer_type v4_name_row;
158 row_pointer_type v4_user_name_row;
159 row_pointer_type connect_row;
160 row_pointer_type cmd_reply_row;
161 row_pointer_type bind_reply_row;
162 row_pointer_type command_row;
163 row_pointer_type auth_method_row;
164 row_pointer_type user_name_auth_row;
165 guint32 start_done_row;
167 guint32 dst_addr; /* this needs to handle IPv6 */
173 static char *address_type_table[] = {
183 /* String table for the V4 reply status messages */
185 static char *reply_table_v4[] = {
187 "Rejected or Failed",
188 "Rejected because SOCKS server cannot connect to identd on the client",
189 "Rejected because the client program and identd report different user-ids",
194 /* String table for the V5 reply status messages */
196 static char *reply_table_v5[] = {
198 "General SOCKS server failure",
199 "Connection not allowed by ruleset",
200 "Network unreachable",
202 "Connection refused",
204 "Command not supported",
205 "Address type not supported",
210 #define socks_hash_init_count 20
211 #define socks_hash_val_length (sizeof(socks_hash_entry_t))
213 static GMemChunk *socks_vals = NULL;
216 /************************* Support routines ***************************/
219 static int display_string( const u_char *pd, int offset, frame_data *fd,
220 proto_tree *tree, char *label){
222 /* display a string with a length, characters encoding */
223 /* they are displayed under a tree with the name in Label variable */
224 /* return the length of the string and the length byte */
227 proto_tree *name_tree;
232 int length = GBYTE( pd, offset);
234 if (!BYTES_ARE_IN_FRAME(offset, 8)){
235 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***");
239 strncpy( temp, &pd[ offset + 1], length);
242 ti = proto_tree_add_text(tree, NullTVB, offset, length + 1,
243 "%s: %s" , label, temp);
246 name_tree = proto_item_add_subtree(ti, ett_socks_name);
248 proto_tree_add_text( name_tree, NullTVB, offset, 1, "Length: %d", length);
252 proto_tree_add_text( name_tree, NullTVB, offset, length, "String: %s", temp);
259 static char *get_auth_method_name( guint Number){
261 /* return the name of the authenication method */
263 if ( Number == 0) return "No authentication";
264 if ( Number == 1) return "GSSAPI";
265 if ( Number == 2) return "Username/Password";
266 if ( Number == 3) return "Chap";
267 if (( Number >= 4) && ( Number <= 0x7f))return "IANA assigned";
268 if (( Number >= 0x80) && ( Number <= 0xfe)) return "private method";
269 if ( Number == 0xff) return "no acceptable method";
271 /* shouldn't reach here */
273 return "Bad method number (not 0-0xff)";
277 static char *get_command_name( guint Number){
279 /* return the name of the command as a string */
281 if ( Number == 0) return "Unknow";
282 if ( Number == 1) return "Connect";
283 if ( Number == 2) return "Bind";
284 if ( Number == 3) return "UdpAssociate";
285 if ( Number == 0x80) return "Ping";
286 if ( Number == 0x81) return "Traceroute";
291 static int display_address( const u_char *pd, int offset,
292 frame_data *fd, proto_tree *tree) {
294 /* decode and display the v5 address, return offset of next byte */
296 int a_type = GBYTE( pd, offset);
298 proto_tree_add_text( tree, NullTVB, offset, 1,
299 "Address Type: %d (%s)", a_type,
300 address_type_table[ MAX( 0, MIN( a_type,
301 array_length( address_type_table)-1))]);
305 if ( a_type == 1){ /* IPv4 address */
306 if (!BYTES_ARE_IN_FRAME(offset, 4))
307 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***");
309 proto_tree_add_ipv4( tree, hf_socks_ip_dst, NullTVB, offset,
310 4, GWORD( pd, offset));
313 else if ( a_type == 3){ /* domain name address */
315 offset += display_string( pd, offset, fd, tree,
318 else if ( a_type == 4){ /* IPv6 address */
319 if (!BYTES_ARE_IN_FRAME(offset, 16))
320 proto_tree_add_text(tree, NullTVB, offset, 0, "*** FRAME TOO SHORT ***");
322 proto_tree_add_ipv6( tree, hf_socks_ip6_dst, NullTVB, offset,
331 static int get_address_v5( const u_char *pd, int offset,
332 socks_hash_entry_t *hash_info) {
334 /* decode the v5 address and return offset of next byte */
335 /*$$$ this needs to handle IPV6 and domain name addresses */
338 int a_type = GBYTE( pd, offset++);
340 if ( a_type == 1){ /* IPv4 address */
343 hash_info->dst_addr = GWORD( pd, offset);
347 else if ( a_type == 4) /* IPv6 address */
350 else if ( a_type == 3) /* domain name address */
351 offset += GBYTE( pd, offset) + 1;
357 /********************* V5 UDP Associate handlers ***********************/
359 static void socks_udp_dissector( const u_char *pd, int offset, frame_data *fd,
362 /* Conversation dissector called from UDP dissector. Decode and display */
363 /* the socks header, the pass the rest of the data to the udp port */
364 /* decode routine to handle the payload. */
367 socks_hash_entry_t *hash_info;
368 conversation_t *conversation;
369 proto_tree *socks_tree;
373 conversation = find_conversation( &pi.src, &pi.dst, pi.ptype,
374 pi.srcport, pi.destport, 0);
376 g_assert( conversation); /* should always find a conversation */
378 hash_info = conversation_get_proto_data(conversation, proto_socks);
380 if (check_col(fd, COL_PROTOCOL))
381 col_set_str(fd, COL_PROTOCOL, "Socks");
383 if (check_col(fd, COL_INFO))
384 col_add_fstr(fd, COL_INFO, "Version: 5, UDP Associated packet");
387 ti = proto_tree_add_protocol_format( tree, proto_socks, NullTVB, offset,
388 END_OF_FRAME, "Socks" );
390 socks_tree = proto_item_add_subtree(ti, ett_socks);
392 CHECK_PACKET_LENGTH( 3);
394 proto_tree_add_text( socks_tree, NullTVB, offset, 2, "Reserved");
397 proto_tree_add_text( socks_tree, NullTVB, offset, 1, "Fragment Number: %d", GBYTE( pd,offset));
401 offset = display_address( pd, offset, fd, socks_tree);
402 hash_info->udp_remote_port = pntohs( &pd[ offset]);
404 CHECK_PACKET_LENGTH( 2);
405 proto_tree_add_uint( socks_tree, hf_socks_dstport, NullTVB,
406 offset, 2, hash_info->udp_remote_port);
410 else { /* no tree, skip past the socks header */
411 CHECK_PACKET_LENGTH( 3);
413 offset = get_address_v5( pd,offset, 0) + 2;
417 /* set pi src/dst port and call the udp sub-dissector lookup */
419 if ( pi.srcport == hash_info->port)
424 *ptr = hash_info->udp_remote_port;
426 tvb = tvb_create_from_top(0);
427 decode_udp_ports( tvb, offset, &pi, 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, 0);
439 g_assert( conversation);
441 conversation_add_proto_data(conversation, proto_socks, hash_info);
442 old_conversation_set_dissector(conversation, 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 */
538 unsigned int i, command;
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 udp conversation */
831 if (!BYTES_ARE_IN_FRAME(offset, 2)){
832 hash_info->state = Done;
835 hash_info->udp_port = pntohs( &pd[ offset]);
837 if (!fd->flags.visited)
838 new_udp_conversation( hash_info);
840 /*$$ may need else statement to handle unknows and generate error message */
844 else if ( hash_info->state == V5BindReply) { /* V5 Bind Second Reply */
846 if (check_col(fd, COL_INFO))
847 col_append_str(fd, COL_INFO, " Command Response: Bind remote host info");
849 hash_info->bind_reply_row = get_packet_ptr;
850 hash_info->state = Done;
852 else if ( hash_info->state == UserNameAuth) { /* Handle V5 User Auth*/
853 if (check_col(fd, COL_INFO))
854 col_append_str(fd, COL_INFO,
855 " User authentication response");
857 hash_info->user_name_auth_row = get_packet_ptr;
858 hash_info->state = AuthReply;
861 else if ( hash_info->state == AuthReply){ /* V5 User Auth reply */
862 hash_info->cmd_reply_row = get_packet_ptr;
863 if (check_col(fd, COL_INFO))
864 col_append_str(fd, COL_INFO, " User authentication reply");
865 hash_info->state = V5Command;
871 static void display_ping_and_tracert( const u_char *pd, int offset,
872 frame_data *fd, proto_tree *tree, socks_hash_entry_t *hash_info) {
874 /* Display the ping/trace_route conversation */
877 const u_char *data, *dataend;
878 const u_char *lineend, *eol;
881 /* handle the end command */
882 if ( pi.destport == TCP_PORT_SOCKS){
883 if (check_col(fd, COL_INFO))
884 col_append_str(fd, COL_INFO, ", Terminate Request");
887 proto_tree_add_text(tree, NullTVB, offset, 1,
888 (hash_info->command == PING_COMMAND) ?
889 "Ping: End command" :
890 "Traceroute: End command");
892 else{ /* display the PING or Traceroute results */
893 if (check_col(fd, COL_INFO))
894 col_append_str(fd, COL_INFO, ", Results");
897 proto_tree_add_text(tree, NullTVB, offset, END_OF_FRAME,
898 (hash_info->command == PING_COMMAND) ?
900 "Traceroute Results");
903 dataend = data + END_OF_FRAME;
905 while (data < dataend) {
907 lineend = find_line_end(data, dataend, &eol);
908 linelen = lineend - data;
910 proto_tree_add_text( tree, NullTVB, offset, linelen,
911 format_text(data, linelen));
921 static void call_next_dissector( const u_char *pd, int offset, frame_data *fd,
922 proto_tree *tree, socks_hash_entry_t *hash_info) {
924 /* Display the results for PING and TRACERT extensions or */
925 /* Call TCP dissector for the port that was passed during the */
926 /* connect process */
927 /* Load pointer to pi.XXXport depending upon the direction, change */
928 /* pi port to the remote port, call next dissecotr to decode the */
929 /* payload, and restore the pi port after that is done. */
934 if (( hash_info->command == PING_COMMAND) ||
935 ( hash_info->command == TRACERT_COMMAND))
937 display_ping_and_tracert( pd, offset, fd, tree, hash_info);
939 else { /* call the tcp port decoder to handle the payload */
941 /*$$$ may want to load dest address here */
943 if ( pi.destport == TCP_PORT_SOCKS)
948 *ptr = hash_info->port;
949 tvb = tvb_create_from_top(0);
950 decode_tcp_ports( tvb, offset, &pi, tree, pi.srcport, pi.destport);
951 *ptr = TCP_PORT_SOCKS;
958 dissect_socks(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
960 proto_tree *socks_tree;
962 socks_hash_entry_t *hash_info;
963 conversation_t *conversation;
965 OLD_CHECK_DISPLAY_AS_DATA(proto_socks, pd, offset, fd, tree);
967 conversation = find_conversation( &pi.src, &pi.dst, pi.ptype,
968 pi.srcport, pi.destport, 0);
971 conversation = conversation_new( &pi.src, &pi.dst, pi.ptype,
972 pi.srcport, pi.destport, 0);
974 hash_info = conversation_get_proto_data(conversation,proto_socks);
976 hash_info = g_mem_chunk_alloc(socks_vals);
977 hash_info->start_done_row = G_MAXINT;
978 hash_info->state = None;
979 hash_info->port = -1;
980 hash_info->version = GBYTE( pd, offset); /* get version*/
982 if (( hash_info->version != 4) && /* error test version */
983 ( hash_info->version != 5))
984 hash_info->state = Done;
986 conversation_add_proto_data(conversation, proto_socks,
989 /* set dissector for now */
990 old_conversation_set_dissector(conversation, dissect_socks);
993 /* display summary window information */
995 if (check_col(fd, COL_PROTOCOL))
996 col_set_str(fd, COL_PROTOCOL, "Socks");
998 if (check_col(fd, COL_INFO)){
999 if (( hash_info->version == 4) || ( hash_info->version == 5)){
1000 col_add_fstr(fd, COL_INFO, "Version: %d",
1001 hash_info->version);
1003 else /* unknown version display error */
1004 col_set_str(fd, COL_INFO, "Unknown");
1007 if ( hash_info->command == PING_COMMAND)
1008 col_append_str(fd, COL_INFO, ", Ping Req");
1009 if ( hash_info->command == TRACERT_COMMAND)
1010 col_append_str(fd, COL_INFO, ", Traceroute Req");
1012 if ( hash_info->port != -1)
1013 col_append_fstr(fd, COL_INFO, ", Remote Port: %d",
1018 /* run state machine if needed */
1020 if ((hash_info->state != Done) && ( !fd->flags.visited)){
1022 if ( hash_info->version == 4)
1023 state_machine_v4( hash_info, pd, offset, fd);
1025 else if ( hash_info->version == 5)
1026 state_machine_v5( hash_info, pd, offset, fd);
1028 if (hash_info->state == Done) { /* if done now */
1029 hash_info->start_done_row = fd->num;
1033 /* if proto tree, decode and display */
1036 ti = proto_tree_add_item( tree, proto_socks, NullTVB, offset,
1037 END_OF_FRAME, FALSE );
1039 socks_tree = proto_item_add_subtree(ti, ett_socks);
1041 if ( hash_info->version == 4)
1042 display_socks_v4( pd, offset, fd, tree, socks_tree,
1045 else if ( hash_info->version == 5)
1046 display_socks_v5( pd, offset, fd, tree, socks_tree,
1049 /* if past startup, add the faked stuff */
1050 if ( fd->num > hash_info->start_done_row){
1051 /* add info to tree */
1052 proto_tree_add_text( socks_tree, NullTVB, offset, 0,
1053 "Command: %d (%s)", hash_info->command,
1054 get_command_name(hash_info->command));
1056 proto_tree_add_ipv4( socks_tree, hf_socks_ip_dst, NullTVB,
1057 offset, 0, hash_info->dst_addr);
1059 /* no fake address for ping & traceroute */
1061 if (( hash_info->command != PING_COMMAND) &&
1062 ( hash_info->command != TRACERT_COMMAND)){
1063 proto_tree_add_uint( socks_tree, hf_socks_dstport, NullTVB,
1064 offset, 0, hash_info->port);
1071 /* call next dissector if ready */
1073 if ( fd->num > hash_info->start_done_row){
1074 call_next_dissector( pd, offset, fd, tree, hash_info);
1080 static void socks_reinit( void){
1082 /* Do the cleanup work when a new pass through the packet list is */
1083 /* performed. Reset the highest row seen counter and re-initialize the */
1084 /* conversation memory chunks. */
1087 g_mem_chunk_destroy(socks_vals);
1089 socks_vals = g_mem_chunk_new("socks_vals", socks_hash_val_length,
1090 socks_hash_init_count * socks_hash_val_length,
1096 proto_register_socks( void){
1098 /*** Prep the socks protocol, register it and a initialization routine */
1099 /* to clear the hash stuff. */
1102 static gint *ett[] = {
1109 static hf_register_info hf[] = {
1113 { "Version", "socks.ver", FT_UINT8, BASE_DEC, NULL,
1118 { "Remote Address", "socks.dst", FT_IPv4, BASE_NONE, NULL,
1122 { &hf_socks_ip6_dst,
1123 { "Remote Address", "socks.dstV6", FT_IPv6, BASE_NONE, NULL,
1129 { "User Name", "socks.username", FT_STRING, BASE_NONE,
1130 NULL, 0x0, "", HFILL
1133 { &hf_socks_dstport,
1134 { "Remote Port", "socks.dstport", FT_UINT16,
1135 BASE_DEC, NULL, 0x0, "", HFILL
1138 { &hf_socks_command,
1139 { "Command", "socks.command", FT_UINT16,
1140 BASE_DEC, NULL, 0x0, "", HFILL
1147 proto_socks = proto_register_protocol (
1148 "Socks Protocol", "Socks", "socks");
1150 proto_register_field_array(proto_socks, hf, array_length(hf));
1151 proto_register_subtree_array(ett, array_length(ett));
1153 register_init_routine( &socks_reinit); /* register re-init routine */
1158 proto_reg_handoff_socks(void) {
1160 /* dissector install routine */
1162 old_dissector_add("tcp.port", TCP_PORT_SOCKS, dissect_socks,