MSWSP: add two more Property Sets
[wireshark-wip.git] / epan / dissectors / packet-socks.c
blob60b0576cec228ea0865f3387ddf60f521c28603d
1 /* packet-socks.c
2 * Routines for socks versions 4 &5 packet dissection
3 * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
4 * Copyright 2008, Jelmer Vernooij <jelmer@samba.org>
6 * $Id$
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
32 * http://www.openssh.org/txt/socks4.protocol
33 * http://www.openssh.org/txt/socks4a.protocol
35 * for information on SOCKS version 4 and 4a.
37 * Revisions:
39 * 2003-09-18 JCFoster Fixed problem with socks tunnel in socks tunnel
40 * causing heap overflow because of an infinite loop
41 * where the socks dissect was call over and over.
43 * Also remove some old code marked with __JUNK__
45 * 2001-01-08 JCFoster Fixed problem with NULL pointer for hash data.
46 * Now test and exit if hash_info is null.
49 /* Possible enhancements -
51 * Add GSS-API authentication per rfc-1961
52 * Add CHAP authentication
53 * Decode FLAG bits per
54 * http://archive.socks.permeo.com/draft/draft-ietf-aft-socks-pro-v5-04.txt
55 * In call_next_dissector, could load the destination address into
56 * pinfo->src or pinfo->dst structure before calling next dissector.
62 #include "config.h"
64 #include <glib.h>
66 #include <epan/packet.h>
67 #include <epan/exceptions.h>
68 #include <epan/conversation.h>
70 #include "packet-tcp.h"
71 #include "packet-udp.h"
72 #include <epan/strutil.h>
74 #include <epan/wmem/wmem.h>
76 #define TCP_PORT_SOCKS 1080
79 /**************** Socks commands ******************/
81 #define CONNECT_COMMAND 1
82 #define BIND_COMMAND 2
83 #define UDP_ASSOCIATE_COMMAND 3
84 #define PING_COMMAND 0x80
85 #define TRACERT_COMMAND 0x81
88 /********** V5 Authentication methods *************/
90 #define NO_AUTHENTICATION 0
91 #define GSS_API_AUTHENTICATION 1
92 #define USER_NAME_AUTHENTICATION 2
93 #define CHAP_AUTHENTICATION 3
94 #define AUTHENTICATION_FAILED 0xff
96 /*********** Header field identifiers *************/
98 static int proto_socks = -1;
100 static int ett_socks = -1;
101 static int ett_socks_auth = -1;
102 static int ett_socks_name = -1;
104 static int hf_socks_ver = -1;
105 static int hf_socks_ip_dst = -1;
106 static int hf_socks_ip6_dst = -1;
107 static int hf_gssapi_payload = -1;
108 static int hf_gssapi_command = -1;
109 static int hf_gssapi_length = -1;
110 static int hf_v4a_dns_name = -1;
111 static int hf_socks_dstport = -1;
112 static int hf_socks_cmd = -1;
113 static int hf_socks_results_4 = -1;
114 static int hf_socks_results_5 = -1;
115 static int hf_client_auth_method_count = -1;
116 static int hf_client_auth_method = -1;
117 static int hf_socks_reserved = -1;
118 static int hf_socks_reserved2 = -1;
119 static int hf_client_port = -1;
120 static int hf_server_accepted_auth_method = -1;
121 static int hf_server_auth_status = -1;
122 static int hf_server_remote_host_port = -1;
123 static int hf_socks_username = -1;
124 static int hf_socks_password = -1;
125 static int hf_socks_remote_name = -1;
126 static int hf_socks_address_type = -1;
127 static int hf_socks_fragment_number = -1;
129 /************* Dissector handles ***********/
131 static dissector_handle_t socks_handle;
132 static dissector_handle_t socks_udp_handle;
134 /************* State Machine names ***********/
136 enum ClientState {
137 clientNoInit = -1,
138 clientStart = 0,
139 clientWaitForAuthReply,
140 clientV5Command,
141 clientUserNameRequest,
142 clientGssApiAuthRequest,
143 clientDone,
144 clientError
147 enum ServerState {
148 serverNoInit = -1,
149 serverStart = 0,
150 serverInitReply,
151 serverCommandReply,
152 serverUserReply,
153 serverGssApiReply,
154 serverBindReply,
155 serverDone,
156 serverError
159 typedef struct {
160 int in_socks_dissector_flag;
161 enum ClientState client;
162 enum ServerState server;
163 } sock_state_t;
165 typedef struct {
166 enum ClientState clientState;
167 enum ServerState serverState;
168 int version;
169 int command;
170 int authentication_method;
171 guint32 server_port;
172 guint32 port;
173 guint32 udp_port;
174 guint32 udp_remote_port;
175 address dst_addr;
177 guint32 start_done_frame;
178 }socks_hash_entry_t;
181 static const value_string address_type_table[] = {
182 {1, "IPv4"},
183 {3, "Domain Name"},
184 {4, "IPv6"},
185 {0, NULL}
188 /* String table for the V4 reply status messages */
190 static const value_string reply_table_v4[] = {
191 {90, "Granted"},
192 {91, "Rejected or Failed"},
193 {92, "Rejected because SOCKS server cannot connect to identd on the client"},
194 {93, "Rejected because the client program and identd report different user-ids"},
195 {0, NULL}
198 /* String table for the V5 reply status messages */
200 static const value_string reply_table_v5[] = {
201 {0, "Succeeded"},
202 {1, "General SOCKS server failure"},
203 {2, "Connection not allowed by ruleset"},
204 {3, "Network unreachable"},
205 {4, "Host unreachable"},
206 {5, "Connection refused"},
207 {6, "TTL expired"},
208 {7, "Command not supported"},
209 {8, "Address type not supported"},
210 {0, NULL},
213 static const value_string cmd_strings[] = {
214 {CONNECT_COMMAND, "Connect"},
215 {BIND_COMMAND, "Bind"},
216 {UDP_ASSOCIATE_COMMAND, "UdpAssociate"},
217 {PING_COMMAND, "Ping"},
218 {TRACERT_COMMAND, "Traceroute"},
219 {0, NULL}
222 static const value_string gssapi_command_table[] = {
223 { 1, "Authentication" },
224 { 0xFF, "Failure" },
225 { 0, NULL }
229 /************************* Support routines ***************************/
231 static const char *get_auth_method_name( guint Number){
233 /* return the name of the authenication method */
235 if ( Number == 0) return "No authentication";
236 if ( Number == 1) return "GSSAPI";
237 if ( Number == 2) return "Username/Password";
238 if ( Number == 3) return "Chap";
239 if (( Number >= 4) && ( Number <= 0x7f))return "IANA assigned";
240 if (( Number >= 0x80) && ( Number <= 0xfe)) return "private method";
241 if ( Number == 0xff) return "no acceptable method";
243 /* shouldn't reach here */
245 return "Bad method number (not 0-0xff)";
248 static int display_address(tvbuff_t *tvb, int offset, proto_tree *tree) {
250 /* decode and display the v5 address, return offset of next byte */
252 int a_type = tvb_get_guint8(tvb, offset);
254 proto_tree_add_item( tree, hf_socks_address_type, tvb, offset, 1, ENC_NA);
255 ++offset;
257 switch (a_type)
259 case 1: /* IPv4 address */
260 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4, ENC_BIG_ENDIAN);
261 offset += 4;
262 break;
263 case 3: /* domain name address */
265 guint8 len;
266 gchar* str;
268 len = tvb_get_guint8(tvb, offset);
269 str = tvb_get_string(wmem_packet_scope(), tvb, offset+1, len);
270 proto_tree_add_string(tree, hf_socks_remote_name, tvb, offset, len+1, str);
271 offset += (len+1);
273 break;
274 case 4: /* IPv6 address */
275 proto_tree_add_item( tree, hf_socks_ip6_dst, tvb, offset, 16, ENC_NA);
276 offset += 16;
277 break;
280 return offset;
284 static int get_address_v5(tvbuff_t *tvb, int offset,
285 socks_hash_entry_t *hash_info) {
287 /* decode the v5 address and return offset of next byte */
288 int a_type;
289 address addr;
291 a_type = tvb_get_guint8(tvb, offset);
292 offset++;
294 switch(a_type)
296 case 1: /* IPv4 address */
297 if ( hash_info) {
298 TVB_SET_ADDRESS(&addr, AT_IPv4, tvb, offset, 4);
299 SE_COPY_ADDRESS(&hash_info->dst_addr, &addr);
301 offset += 4;
302 break;
304 case 4: /* IPv6 address */
305 if ( hash_info) {
306 TVB_SET_ADDRESS(&addr, AT_IPv6, tvb, offset, 16);
307 SE_COPY_ADDRESS(&hash_info->dst_addr, &addr);
309 offset += 16;
310 break;
312 case 3: /* domain name address */
313 offset += tvb_get_guint8(tvb, offset) + 1;
314 break;
317 return offset;
321 /********************* V5 UDP Associate handlers ***********************/
323 static void
324 socks_udp_dissector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
326 /* Conversation dissector called from UDP dissector. Decode and display */
327 /* the socks header, the pass the rest of the data to the udp port */
328 /* decode routine to handle the payload. */
330 int offset = 0;
331 guint32 *ptr;
332 socks_hash_entry_t *hash_info;
333 conversation_t *conversation;
334 proto_tree *socks_tree;
335 proto_item *ti;
337 conversation = find_conversation( pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
338 pinfo->srcport, pinfo->destport, 0);
340 DISSECTOR_ASSERT( conversation); /* should always find a conversation */
342 hash_info = (socks_hash_entry_t *)conversation_get_proto_data(conversation, proto_socks);
344 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks");
345 col_set_str(pinfo->cinfo, COL_INFO, "Version: 5, UDP Associated packet");
347 if ( tree) {
348 ti = proto_tree_add_protocol_format( tree, proto_socks, tvb, offset, -1, "Socks" );
350 socks_tree = proto_item_add_subtree(ti, ett_socks);
352 proto_tree_add_item(socks_tree, hf_socks_reserved2, tvb, offset, 2, ENC_BIG_ENDIAN);
353 offset += 2;
355 proto_tree_add_item(socks_tree, hf_socks_fragment_number, tvb, offset, 1, ENC_NA);
356 ++offset;
358 offset = display_address( tvb, offset, socks_tree);
359 hash_info->udp_remote_port = tvb_get_ntohs(tvb, offset);
361 proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb,
362 offset, 2, hash_info->udp_remote_port);
364 offset += 2;
366 else { /* no tree, skip past the socks header */
367 offset += 3;
368 offset = get_address_v5( tvb, offset, 0) + 2;
371 /* set pi src/dst port and call the udp sub-dissector lookup */
373 if ( pinfo->srcport == hash_info->port)
374 ptr = &pinfo->destport;
375 else
376 ptr = &pinfo->srcport;
378 *ptr = hash_info->udp_remote_port;
380 decode_udp_ports( tvb, offset, pinfo, tree, pinfo->srcport, pinfo->destport, -1);
382 *ptr = hash_info->udp_port;
386 static void
387 new_udp_conversation( socks_hash_entry_t *hash_info, packet_info *pinfo){
389 conversation_t *conversation = conversation_new( pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP,
390 hash_info->udp_port, hash_info->port, 0);
392 DISSECTOR_ASSERT( conversation);
394 conversation_add_proto_data(conversation, proto_socks, hash_info);
395 conversation_set_dissector(conversation, socks_udp_handle);
398 static void
399 save_client_state(packet_info *pinfo, enum ClientState state)
401 sock_state_t* state_info = (sock_state_t *)p_get_proto_data(pinfo->fd, proto_socks, 0);
402 if ((state_info != NULL) && (state_info->client == clientNoInit)) {
403 state_info->client = state;
407 static void
408 save_server_state(packet_info *pinfo, enum ServerState state)
410 sock_state_t* state_info = (sock_state_t *)p_get_proto_data(pinfo->fd, proto_socks, 0);
411 if ((state_info != NULL) && (state_info->server == serverNoInit)) {
412 state_info->server = state;
417 /**************** Protocol Tree Display routines ******************/
419 static void
420 display_socks_v4(tvbuff_t *tvb, int offset, packet_info *pinfo,
421 proto_tree *tree, socks_hash_entry_t *hash_info, sock_state_t* state_info) {
424 /* Display the protocol tree for the V4 version. This routine uses the */
425 /* stored frame information to decide what to do with the row. */
427 unsigned char ipaddr[4];
428 guint str_len;
430 /* Either there is an error, or we're done with the state machine
431 (so there's nothing to display) */
432 if (state_info == NULL)
433 return;
435 if (hash_info->server_port == pinfo->destport) {
436 /* Client side */
437 switch (state_info->client)
439 case clientStart:
440 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN);
441 ++offset;
443 proto_tree_add_item( tree, hf_socks_cmd, tvb, offset, 1, ENC_NA);
444 ++offset;
446 /* Do remote port */
447 proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2, ENC_BIG_ENDIAN);
448 offset += 2;
450 /* Do destination address */
451 tvb_memcpy(tvb, ipaddr, offset, 4);
452 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4, ENC_BIG_ENDIAN);
453 offset += 4;
455 /* display user name */
456 str_len = tvb_strsize(tvb, offset);
457 proto_tree_add_item( tree, hf_socks_username, tvb, offset, str_len, ENC_ASCII|ENC_NA);
458 offset += str_len;
460 if ( ipaddr[0] == 0 && ipaddr[1] == 0 &&
461 ipaddr[2] == 0 && ipaddr[3] != 0) {
462 /* 0.0.0.x , where x!=0 means v4a support */
463 str_len = tvb_strsize(tvb, offset);
464 proto_tree_add_item( tree, hf_v4a_dns_name, tvb, offset, str_len, ENC_ASCII|ENC_NA);
466 break;
467 default:
468 break;
470 } else {
471 /* Server side */
472 switch (state_info->server)
474 case serverStart:
475 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN);
476 ++offset;
477 /* Do results code */
478 proto_tree_add_item( tree, hf_socks_results_4, tvb, offset, 1, ENC_BIG_ENDIAN);
479 ++offset;
481 /* Do remote port */
482 proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2, ENC_BIG_ENDIAN);
483 offset += 2;
484 /* Do remote address */
485 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4, ENC_BIG_ENDIAN);
486 break;
487 default:
488 break;
493 static void
494 client_display_socks_v5(tvbuff_t *tvb, int offset, packet_info *pinfo,
495 proto_tree *tree, socks_hash_entry_t *hash_info, sock_state_t* state_info) {
497 /* Display the protocol tree for the version. This routine uses the */
498 /* stored conversation information to decide what to do with the row. */
499 /* Per packet information would have been better to do this, but we */
500 /* didn't have that when I wrote this. And I didn't expect this to get */
501 /* so messy. */
503 unsigned int i;
504 const char *AuthMethodStr;
505 sock_state_t new_state_info;
507 /* Either there is an error, or we're done with the state machine
508 (so there's nothing to display) */
509 if (state_info == NULL)
510 return;
512 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN);
513 ++offset;
515 if (state_info->client == clientStart)
517 proto_tree *AuthTree;
518 proto_item *ti;
519 guint8 num_auth_methods, auth;
521 ti = proto_tree_add_text( tree, tvb, offset, -1, "Client Authentication Methods");
522 AuthTree = proto_item_add_subtree(ti, ett_socks_auth);
524 num_auth_methods = tvb_get_guint8(tvb, offset);
525 proto_item_set_len(ti, num_auth_methods+1);
527 proto_tree_add_item( AuthTree, hf_client_auth_method_count, tvb, offset, 1, ENC_NA);
528 ++offset;
530 for( i = 0; i < num_auth_methods; ++i) {
531 auth = tvb_get_guint8( tvb, offset);
532 AuthMethodStr = get_auth_method_name(auth);
534 proto_tree_add_uint_format(AuthTree, hf_client_auth_method, tvb, offset, 1, auth,
535 "Method[%u]: %u (%s)", i, auth, AuthMethodStr);
536 ++offset;
539 if ((num_auth_methods == 1) &&
540 (tvb_bytes_exist(tvb, offset + 2, 1)) &&
541 (tvb_get_guint8(tvb, offset + 2) == 0) &&
542 (tvb_reported_length_remaining(tvb, offset + 2 + num_auth_methods) > 0)) {
543 new_state_info.client = clientV5Command;
544 client_display_socks_v5(tvb, offset, pinfo, tree, hash_info, &new_state_info);
547 else if (state_info->client == clientV5Command) {
549 proto_tree_add_item( tree, hf_socks_cmd, tvb, offset, 1, ENC_NA);
550 ++offset;
552 proto_tree_add_item( tree, hf_socks_reserved, tvb, offset, 1, ENC_NA);
553 ++offset;
555 offset = display_address(tvb, offset, tree);
556 proto_tree_add_item( tree, hf_client_port, tvb, offset, 2, ENC_BIG_ENDIAN);
558 else if ((state_info->client == clientWaitForAuthReply) &&
559 (state_info->server == serverInitReply)) {
560 guint16 len;
561 gchar* str;
563 switch(hash_info->authentication_method)
565 case NO_AUTHENTICATION:
566 break;
567 case USER_NAME_AUTHENTICATION:
568 /* process user name */
569 len = tvb_get_guint8(tvb, offset);
570 str = tvb_get_string(wmem_packet_scope(), tvb, offset+1, len);
571 proto_tree_add_string(tree, hf_socks_username, tvb, offset, len+1, str);
572 offset += (len+1);
574 len = tvb_get_guint8(tvb, offset);
575 str = tvb_get_string(wmem_packet_scope(), tvb, offset+1, len);
576 proto_tree_add_string(tree, hf_socks_password, tvb, offset, len+1, str);
577 /* offset += (len+1); */
578 break;
579 case GSS_API_AUTHENTICATION:
580 proto_tree_add_item( tree, hf_gssapi_command, tvb, offset, 1, ENC_BIG_ENDIAN);
581 proto_tree_add_item( tree, hf_gssapi_length, tvb, offset+1, 2, ENC_BIG_ENDIAN);
582 len = tvb_get_ntohs(tvb, offset+1);
583 if (len > 0)
584 proto_tree_add_item( tree, hf_gssapi_payload, tvb, offset+3, len, ENC_NA);
585 break;
586 default:
587 break;
592 static void
593 server_display_socks_v5(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
594 proto_tree *tree, socks_hash_entry_t *hash_info _U_, sock_state_t* state_info) {
596 /* Display the protocol tree for the version. This routine uses the */
597 /* stored conversation information to decide what to do with the row. */
598 /* Per packet information would have been better to do this, but we */
599 /* didn't have that when I wrote this. And I didn't expect this to get */
600 /* so messy. */
602 const char *AuthMethodStr;
603 guint8 auth, auth_status;
604 proto_item* ti;
606 /* Either there is an error, or we're done with the state machine
607 (so there's nothing to display) */
608 if (state_info == NULL)
609 return;
611 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN);
612 ++offset;
614 switch(state_info->server)
616 case serverStart:
617 auth = tvb_get_guint8( tvb, offset);
618 AuthMethodStr = get_auth_method_name(auth);
620 proto_tree_add_uint_format_value(tree, hf_server_accepted_auth_method, tvb, offset, 1, auth,
621 "0x%0x (%s)", auth, AuthMethodStr);
622 break;
624 case serverUserReply:
625 auth_status = tvb_get_guint8(tvb, offset);
626 ti = proto_tree_add_item(tree, hf_server_auth_status, tvb, offset, 1, ENC_NA);
627 if(auth_status != 0)
628 proto_item_append_text(ti, " (failure)");
629 else
630 proto_item_append_text(ti, " (success)");
631 break;
633 case serverGssApiReply:
634 auth_status = tvb_get_guint8(tvb, offset);
635 proto_tree_add_item( tree, hf_gssapi_command, tvb, offset, 1, ENC_BIG_ENDIAN);
636 if (auth_status != 0xFF) {
637 guint16 len;
639 proto_tree_add_item( tree, hf_gssapi_length, tvb, offset+1, 2, ENC_BIG_ENDIAN);
640 len = tvb_get_ntohs(tvb, offset+1);
641 if (len > 0)
642 proto_tree_add_item( tree, hf_gssapi_payload, tvb, offset+3, len, ENC_NA);
644 break;
646 case serverCommandReply:
647 proto_tree_add_item( tree, hf_socks_results_5, tvb, offset, 1, ENC_BIG_ENDIAN);
648 ++offset;
650 proto_tree_add_item( tree, hf_socks_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
651 ++offset;
653 offset = display_address(tvb, offset, tree);
654 proto_tree_add_item( tree, hf_client_port, tvb, offset, 2, ENC_BIG_ENDIAN);
655 break;
657 case serverBindReply:
658 proto_tree_add_item( tree, hf_socks_results_5, tvb, offset, 1, ENC_BIG_ENDIAN);
659 ++offset;
661 proto_tree_add_item( tree, hf_socks_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
662 ++offset;
664 offset = display_address(tvb, offset, tree);
665 proto_tree_add_item( tree, hf_server_remote_host_port, tvb, offset, 2, ENC_BIG_ENDIAN);
666 break;
667 default:
668 break;
673 /**************** Decoder State Machines ******************/
676 static void
677 state_machine_v4( socks_hash_entry_t *hash_info, tvbuff_t *tvb,
678 int offset, packet_info *pinfo) {
680 /* Decode V4 protocol. This is done on the first pass through the */
681 /* list. Based upon the current state, decode the packet and determine */
682 /* what the next state should be. */
683 address addr;
685 if (hash_info->clientState != clientDone)
686 save_client_state(pinfo, hash_info->clientState);
688 if (hash_info->serverState != serverDone)
689 save_server_state(pinfo, hash_info->serverState);
691 if (hash_info->server_port == pinfo->destport) {
692 /* Client side, only a single request */
693 col_append_str(pinfo->cinfo, COL_INFO, " Connect to server request");
695 hash_info->command = tvb_get_guint8(tvb, offset + 1);
697 /* get remote port */
698 if ( hash_info->command == CONNECT_COMMAND)
699 hash_info->port = tvb_get_ntohs(tvb, offset + 2);
701 /* get remote address */
702 TVB_SET_ADDRESS(&addr, AT_IPv4, tvb, offset, 4);
703 SE_COPY_ADDRESS(&hash_info->dst_addr, &addr);
705 hash_info->clientState = clientDone;
707 else {
708 col_append_str(pinfo->cinfo, COL_INFO, " Connect Response");
710 if (tvb_get_guint8(tvb, offset + 1) == 90)
711 hash_info->serverState = serverDone;
712 else
713 hash_info->serverState = serverError;
717 static void
718 client_state_machine_v5( socks_hash_entry_t *hash_info, tvbuff_t *tvb,
719 int offset, packet_info *pinfo, gboolean start_of_frame) {
721 /* Decode client side of V5 protocol. This is done on the first pass through the */
722 /* list. Based upon the current state, decode the packet and determine */
723 /* what the next state should be. */
725 if (start_of_frame)
726 save_client_state(pinfo, hash_info->clientState);
728 if (hash_info->clientState == clientStart)
730 guint8 num_auth_methods;
731 col_append_str(pinfo->cinfo, COL_INFO, " Connect to server request");
733 num_auth_methods = tvb_get_guint8(tvb, offset + 1);
734 /* skip past auth methods */
736 if ((num_auth_methods == 0) ||
737 ((num_auth_methods == 1) &&
738 (tvb_get_guint8(tvb, offset + 2) == 0))) {
739 /* No authentication needed */
740 hash_info->clientState = clientV5Command;
741 if (tvb_reported_length_remaining(tvb, offset + 2 + num_auth_methods) > 0) {
742 client_state_machine_v5(hash_info, tvb, offset + 2 + num_auth_methods, pinfo, FALSE);
744 } else {
745 hash_info->clientState = clientWaitForAuthReply;
747 } else if ((hash_info->clientState == clientWaitForAuthReply) &&
748 (hash_info->serverState == serverInitReply)) {
750 switch(hash_info->authentication_method)
752 case NO_AUTHENTICATION:
753 hash_info->clientState = clientV5Command;
754 hash_info->serverState = serverCommandReply;
755 break;
756 case USER_NAME_AUTHENTICATION:
757 hash_info->clientState = clientV5Command;
758 hash_info->serverState = serverUserReply;
759 break;
760 case GSS_API_AUTHENTICATION:
761 hash_info->clientState = clientV5Command;
762 hash_info->serverState = serverGssApiReply;
763 break;
764 default:
765 hash_info->clientState = clientError; /*Auth failed or error*/
766 break;
768 } else if (hash_info->clientState == clientV5Command) {
770 hash_info->command = tvb_get_guint8(tvb, offset + 1); /* get command */
772 col_append_fstr(pinfo->cinfo, COL_INFO, " Command Request - %s",
773 val_to_str_const(hash_info->command, cmd_strings, "Unknown"));
775 offset += 3; /* skip to address type */
777 offset = get_address_v5(tvb, offset, hash_info);
779 /** temp = tvb_get_guint8(tvb, offset); XX: what was this for ? **/
781 if (( hash_info->command == CONNECT_COMMAND) ||
782 ( hash_info->command == UDP_ASSOCIATE_COMMAND))
783 /* get remote port */
784 hash_info->port = tvb_get_ntohs(tvb, offset);
786 hash_info->clientState = clientDone;
790 static void
791 server_state_machine_v5( socks_hash_entry_t *hash_info, tvbuff_t *tvb,
792 int offset, packet_info *pinfo, gboolean start_of_frame) {
794 /* Decode server side of V5 protocol. This is done on the first pass through the */
795 /* list. Based upon the current state, decode the packet and determine */
796 /* what the next state should be. */
798 if (start_of_frame)
799 save_server_state(pinfo, hash_info->serverState);
801 switch (hash_info->serverState) {
802 case serverStart:
803 col_append_str(pinfo->cinfo, COL_INFO, " Connect to server response");
805 hash_info->authentication_method = tvb_get_guint8(tvb, offset + 1);
806 hash_info->serverState = serverInitReply;
808 switch (hash_info->authentication_method)
810 case NO_AUTHENTICATION:
811 hash_info->serverState = serverCommandReply;
812 break;
813 case USER_NAME_AUTHENTICATION:
814 hash_info->serverState = serverUserReply;
815 break;
816 case GSS_API_AUTHENTICATION:
817 hash_info->serverState = serverGssApiReply;
818 break;
819 default:
820 hash_info->serverState = serverError;
821 break;
823 break;
824 case serverUserReply:
825 col_append_str(pinfo->cinfo, COL_INFO, " User authentication reply");
826 break;
827 case serverGssApiReply:
828 if (tvb_get_guint8(tvb, offset+1) == 0xFF) {
829 col_append_str(pinfo->cinfo, COL_INFO, " GSSAPI Authentication failure");
830 hash_info->serverState = serverError;
831 } else {
832 col_append_str(pinfo->cinfo, COL_INFO, " GSSAPI Authentication reply");
833 if (tvb_get_ntohs(tvb, offset+2) == 0)
834 hash_info->serverState = serverCommandReply;
836 break;
837 case serverCommandReply:
838 col_append_fstr(pinfo->cinfo, COL_INFO, " Command Response - %s",
839 val_to_str_const(hash_info->command, cmd_strings, "Unknown"));
841 switch(hash_info->command)
843 case CONNECT_COMMAND:
844 case PING_COMMAND:
845 case TRACERT_COMMAND:
846 hash_info->serverState = serverDone;
847 break;
849 case BIND_COMMAND:
850 hash_info->serverState = serverBindReply;
851 if ((tvb_get_guint8(tvb, offset + 2) == 0) &&
852 (tvb_reported_length_remaining(tvb, offset) > 5)) {
853 offset = display_address(tvb, offset, NULL);
854 client_state_machine_v5(hash_info, tvb, offset, pinfo, FALSE);
856 break;
858 case UDP_ASSOCIATE_COMMAND:
859 offset += 3; /* skip to address type */
860 offset = get_address_v5(tvb, offset, hash_info);
862 /* save server udp port and create udp conversation */
863 hash_info->udp_port = tvb_get_ntohs(tvb, offset);
865 if (!pinfo->fd->flags.visited)
866 new_udp_conversation( hash_info, pinfo);
868 break;
870 break;
871 case serverBindReply:
872 col_append_str(pinfo->cinfo, COL_INFO, " Command Response: Bind remote host info");
873 break;
874 default:
875 break;
880 static void
881 display_ping_and_tracert(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, socks_hash_entry_t *hash_info) {
883 /* Display the ping/trace_route conversation */
885 const guchar *data, *dataend;
886 const guchar *lineend, *eol;
887 int linelen;
889 /* handle the end command */
890 if ( pinfo->destport == TCP_PORT_SOCKS){
891 col_append_str(pinfo->cinfo, COL_INFO, ", Terminate Request");
893 if ( tree)
894 proto_tree_add_text(tree, tvb, offset, 1,
895 (hash_info->command == PING_COMMAND) ?
896 "Ping: End command" :
897 "Traceroute: End command");
899 else { /* display the PING or Traceroute results */
900 col_append_str(pinfo->cinfo, COL_INFO, ", Results");
902 if ( tree){
903 proto_tree_add_text(tree, tvb, offset, -1,
904 (hash_info->command == PING_COMMAND) ?
905 "Ping Results:" :
906 "Traceroute Results");
908 data = tvb_get_ptr(tvb, offset, -1);
909 dataend = data + tvb_length_remaining(tvb, offset);
911 while (data < dataend) {
913 lineend = find_line_end(data, dataend, &eol);
914 linelen = (int)(lineend - data);
916 proto_tree_add_text( tree, tvb, offset, linelen,
917 "%s", format_text(data, linelen));
918 offset += linelen;
919 data = lineend;
925 static void clear_in_socks_dissector_flag(void *s)
927 sock_state_t* state_info = (sock_state_t*)s;
928 state_info->in_socks_dissector_flag = 0; /* avoid recursive overflow */
931 static void call_next_dissector(tvbuff_t *tvb, int offset, packet_info *pinfo,
932 proto_tree *tree, proto_tree *socks_tree,
933 socks_hash_entry_t *hash_info, sock_state_t* state_info, struct tcpinfo *tcpinfo)
936 /* Display the results for PING and TRACERT extensions or */
937 /* Call TCP dissector for the port that was passed during the */
938 /* connect process */
939 /* Load pointer to pinfo->XXXport depending upon the direction, */
940 /* change pinfo port to the remote port, call next dissecotr to decode */
941 /* the payload, and restore the pinfo port after that is done. */
943 guint32 *ptr;
944 guint16 save_can_desegment;
945 struct tcp_analysis *tcpd=NULL;
947 tcpd=get_tcp_conversation_data(NULL,pinfo);
949 if (( hash_info->command == PING_COMMAND) ||
950 ( hash_info->command == TRACERT_COMMAND))
952 display_ping_and_tracert(tvb, offset, pinfo, tree, hash_info);
954 else { /* call the tcp port decoder to handle the payload */
956 /*XXX may want to load dest address here */
958 if ( pinfo->destport == TCP_PORT_SOCKS)
959 ptr = &pinfo->destport;
960 else
961 ptr = &pinfo->srcport;
963 *ptr = hash_info->port;
965 /* 2003-09-18 JCFoster Fixed problem with socks tunnel in socks tunnel */
967 state_info->in_socks_dissector_flag = 1; /* avoid recursive overflow */
968 CLEANUP_PUSH(clear_in_socks_dissector_flag, state_info);
970 save_can_desegment = pinfo->can_desegment;
971 pinfo->can_desegment = pinfo->saved_can_desegment;
972 dissect_tcp_payload(tvb, pinfo, offset, tcpinfo->seq,
973 tcpinfo->nxtseq, pinfo->srcport, pinfo->destport,
974 tree, socks_tree, tcpd, tcpinfo);
975 pinfo->can_desegment = save_can_desegment;
977 CLEANUP_CALL_AND_POP;
979 *ptr = TCP_PORT_SOCKS;
985 static int
986 dissect_socks(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
988 int offset = 0;
989 proto_tree *socks_tree = NULL;
990 proto_item *ti;
991 socks_hash_entry_t *hash_info;
992 conversation_t *conversation;
993 sock_state_t* state_info;
994 guint8 version;
995 struct tcpinfo *tcpinfo = (struct tcpinfo*)data;
997 state_info = (sock_state_t *)p_get_proto_data(pinfo->fd, proto_socks, 0);
998 if (state_info == NULL) {
999 state_info = wmem_new(wmem_file_scope(), sock_state_t);
1000 state_info->in_socks_dissector_flag = 0;
1001 state_info->client = clientNoInit;
1002 state_info->server = serverNoInit;
1004 p_add_proto_data(pinfo->fd, proto_socks, 0, state_info);
1007 /* avoid recursive overflow */
1008 if (state_info->in_socks_dissector_flag)
1009 return 0;
1011 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
1012 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1013 if (conversation == NULL) {
1014 /* If we don't already have a conversation, make sure the first
1015 byte is a valid version number */
1016 version = tvb_get_guint8(tvb, offset);
1017 if ((version != 4) && (version != 5))
1018 return 0;
1020 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
1021 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1024 hash_info = (socks_hash_entry_t *)conversation_get_proto_data(conversation,proto_socks);
1025 if (hash_info == NULL){
1026 hash_info = wmem_new0(wmem_file_scope(), socks_hash_entry_t);
1027 hash_info->start_done_frame = G_MAXINT;
1028 hash_info->clientState = clientStart;
1029 hash_info->serverState = serverStart;
1031 hash_info->server_port = pinfo->destport;
1032 hash_info->port = 0;
1033 hash_info->version = tvb_get_guint8(tvb, offset); /* get version*/
1035 conversation_add_proto_data(conversation, proto_socks, hash_info);
1037 /* set dissector for now */
1038 conversation_set_dissector(conversation, socks_handle);
1041 /* display summary window information */
1042 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks");
1044 if (( hash_info->version == 4) || ( hash_info->version == 5)){
1045 col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %d",
1046 hash_info->version);
1048 else /* unknown version display error */
1049 col_set_str(pinfo->cinfo, COL_INFO, "Unknown");
1052 if ( hash_info->command == PING_COMMAND)
1053 col_append_str(pinfo->cinfo, COL_INFO, ", Ping Req");
1054 if ( hash_info->command == TRACERT_COMMAND)
1055 col_append_str(pinfo->cinfo, COL_INFO, ", Traceroute Req");
1057 if ( hash_info->port != 0)
1058 col_append_fstr(pinfo->cinfo, COL_INFO, ", Remote Port: %u",
1059 hash_info->port);
1061 /* run state machine if needed */
1062 if ((!pinfo->fd->flags.visited) &&
1063 (!((hash_info->clientState == clientDone) &&
1064 (hash_info->serverState == serverDone)))) {
1066 if (hash_info->server_port == pinfo->destport) {
1067 if ((hash_info->clientState != clientError) &&
1068 (hash_info->clientState != clientDone))
1070 if ( hash_info->version == 4) {
1071 state_machine_v4( hash_info, tvb, offset, pinfo);
1072 } else if ( hash_info->version == 5) {
1073 client_state_machine_v5( hash_info, tvb, offset, pinfo, TRUE);
1076 } else {
1077 if ((hash_info->serverState != serverError) &&
1078 (hash_info->serverState != serverDone)) {
1079 if ( hash_info->version == 4) {
1080 state_machine_v4( hash_info, tvb, offset, pinfo);
1081 } else if ( hash_info->version == 5) {
1082 server_state_machine_v5( hash_info, tvb, offset, pinfo, TRUE);
1087 if ((hash_info->clientState == clientDone) &&
1088 (hash_info->serverState == serverDone)) { /* if done now */
1089 hash_info->start_done_frame = pinfo->fd->num;
1093 /* if proto tree, decode and display */
1094 if (tree) {
1095 ti = proto_tree_add_item( tree, proto_socks, tvb, offset, -1, ENC_NA );
1096 socks_tree = proto_item_add_subtree(ti, ett_socks);
1098 if (hash_info->server_port == pinfo->destport) {
1099 if ( hash_info->version == 4) {
1100 display_socks_v4(tvb, offset, pinfo, socks_tree, hash_info, state_info);
1101 } else if ( hash_info->version == 5) {
1102 client_display_socks_v5(tvb, offset, pinfo, socks_tree, hash_info, state_info);
1104 } else {
1105 if ( hash_info->version == 4) {
1106 display_socks_v4(tvb, offset, pinfo, socks_tree, hash_info, state_info);
1107 } else if ( hash_info->version == 5) {
1108 server_display_socks_v5(tvb, offset, pinfo, socks_tree, hash_info, state_info);
1112 /* if past startup, add the faked stuff */
1113 if ( pinfo->fd->num > hash_info->start_done_frame){
1114 /* add info to tree */
1115 ti = proto_tree_add_uint( socks_tree, hf_socks_cmd, tvb, offset, 0, hash_info->command);
1116 PROTO_ITEM_SET_GENERATED(ti);
1118 if (hash_info->dst_addr.type == AT_IPv4) {
1119 ti = proto_tree_add_ipv4( socks_tree, hf_socks_ip_dst, tvb,
1120 offset, 0, *((guint32*)hash_info->dst_addr.data));
1121 PROTO_ITEM_SET_GENERATED(ti);
1122 } else if (hash_info->dst_addr.type == AT_IPv6) {
1123 ti = proto_tree_add_ipv6( socks_tree, hf_socks_ip6_dst, tvb,
1124 offset, 0, (const guint8*)hash_info->dst_addr.data);
1125 PROTO_ITEM_SET_GENERATED(ti);
1128 /* no fake address for ping & traceroute */
1130 if (( hash_info->command != PING_COMMAND) &&
1131 ( hash_info->command != TRACERT_COMMAND)){
1132 ti = proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb, offset, 0, hash_info->port);
1133 PROTO_ITEM_SET_GENERATED(ti);
1140 /* call next dissector if ready */
1141 if ( pinfo->fd->num > hash_info->start_done_frame){
1142 call_next_dissector(tvb, offset, pinfo, tree, socks_tree,
1143 hash_info, state_info, tcpinfo);
1146 return tvb_reported_length(tvb);
1151 void
1152 proto_register_socks( void){
1154 static gint *ett[] = {
1155 &ett_socks,
1156 &ett_socks_auth,
1157 &ett_socks_name
1160 static hf_register_info hf[] = {
1163 { &hf_socks_ver,
1164 { "Version", "socks.version", FT_UINT8, BASE_DEC, NULL,
1165 0x0, NULL, HFILL
1168 { &hf_socks_ip_dst,
1169 { "Remote Address", "socks.dst", FT_IPv4, BASE_NONE, NULL,
1170 0x0, NULL, HFILL
1173 { &hf_socks_ip6_dst,
1174 { "Remote Address(ipv6)", "socks.dstV6", FT_IPv6, BASE_NONE, NULL,
1175 0x0, NULL, HFILL
1178 { &hf_gssapi_payload,
1179 { "GSSAPI data", "socks.gssapi.data", FT_BYTES, BASE_NONE, NULL,
1180 0x0, NULL, HFILL
1183 { &hf_gssapi_command,
1184 { "SOCKS/GSSAPI command", "socks.gssapi.command", FT_UINT8, BASE_DEC,
1185 VALS(gssapi_command_table), 0x0, NULL, HFILL
1188 { &hf_gssapi_length,
1189 { "SOCKS/GSSAPI data length", "socks.gssapi.length", FT_UINT16, BASE_DEC, NULL,
1190 0x0, NULL, HFILL
1193 { &hf_v4a_dns_name,
1194 { "SOCKS v4a Remote Domain Name", "socks.v4a_dns_name", FT_STRINGZ, BASE_NONE,
1195 NULL, 0x0, NULL, HFILL
1198 { &hf_socks_dstport,
1199 { "Remote Port", "socks.dstport", FT_UINT16,
1200 BASE_DEC, NULL, 0x0, NULL, HFILL
1203 { &hf_socks_cmd,
1204 { "Command", "socks.command", FT_UINT8,
1205 BASE_DEC, VALS(cmd_strings), 0x0, NULL, HFILL
1208 { &hf_socks_results_4,
1209 { "Results(V4)", "socks.results", FT_UINT8,
1210 BASE_DEC, VALS(reply_table_v4), 0x0, NULL, HFILL
1213 { &hf_socks_results_5,
1214 { "Results(V5)", "socks.results", FT_UINT8,
1215 BASE_DEC, VALS(reply_table_v5), 0x0, NULL, HFILL
1218 { &hf_client_auth_method_count,
1219 { "Authentication Method Count", "socks.auth_method_count", FT_UINT8,
1220 BASE_DEC, NULL, 0x0, NULL, HFILL
1223 { &hf_client_auth_method,
1224 { "Method", "socks.auth_method", FT_UINT8,
1225 BASE_DEC, NULL, 0x0, NULL, HFILL
1228 { &hf_socks_reserved,
1229 { "Reserved", "socks.reserved", FT_UINT8,
1230 BASE_DEC, NULL, 0x0, NULL, HFILL
1233 { &hf_socks_reserved2,
1234 { "Reserved", "socks.reserved", FT_UINT16,
1235 BASE_DEC, NULL, 0x0, NULL, HFILL
1238 { &hf_client_port,
1239 { "Port", "socks.port", FT_UINT16,
1240 BASE_DEC, NULL, 0x0, NULL, HFILL
1243 { &hf_server_accepted_auth_method,
1244 { "Accepted Auth Method", "socks.auth_accepted_method", FT_UINT8,
1245 BASE_DEC, NULL, 0x0, NULL, HFILL
1248 { &hf_server_auth_status,
1249 { "Status", "socks.auth_status", FT_UINT8,
1250 BASE_DEC, NULL, 0x0, NULL, HFILL
1253 { &hf_server_remote_host_port,
1254 { "Remote Host Port", "socks.remote_host_port", FT_UINT16,
1255 BASE_DEC, NULL, 0x0, NULL, HFILL
1258 { &hf_socks_username,
1259 { "User name", "socks.username", FT_STRING, BASE_NONE,
1260 NULL, 0x0, NULL, HFILL
1263 { &hf_socks_password,
1264 { "Password", "socks.password", FT_STRING, BASE_NONE,
1265 NULL, 0x0, NULL, HFILL
1268 { &hf_socks_remote_name,
1269 { "Remote name", "socks.remote_name", FT_STRING, BASE_NONE,
1270 NULL, 0x0, NULL, HFILL
1273 { &hf_socks_address_type,
1274 { "Address Type", "socks.address_type", FT_UINT8,
1275 BASE_DEC, VALS(address_type_table), 0x0, NULL, HFILL
1278 { &hf_socks_fragment_number,
1279 { "Fragment Number", "socks.fragment_number", FT_UINT8,
1280 BASE_DEC, NULL, 0x0, NULL, HFILL
1285 proto_socks = proto_register_protocol ( "Socks Protocol", "Socks", "socks");
1287 proto_register_field_array(proto_socks, hf, array_length(hf));
1288 proto_register_subtree_array(ett, array_length(ett));
1292 void
1293 proto_reg_handoff_socks(void) {
1295 /* dissector install routine */
1296 socks_udp_handle = create_dissector_handle(socks_udp_dissector, proto_socks);
1297 socks_handle = new_create_dissector_handle(dissect_socks, proto_socks);
1299 dissector_add_uint("tcp.port", TCP_PORT_SOCKS, socks_handle);
1303 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1305 * Local variables:
1306 * c-basic-offset: 4
1307 * tab-width: 4
1308 * indent-tabs-mode: t
1309 * End:
1311 * ex: set shiftwidth=4 tabstop=4 expandtab:
1312 * :indentSize=4:tabSize=4:noTabs=false: