waste a couple of bytes per tcp conversation and make the tree for acked_packets...
[obnox/wireshark/wip.git] / epan / dissectors / packet-socks.c
1 /* packet-socks.c
2  * Routines for socks versions 4 &5  packet dissection
3  * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
4  *
5  * $Id$
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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.
24  *
25  *
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.
29  *
30  * See
31  *
32  *      http://www.socks.permeo.com/TechnicalResources/ProtocolDocuments.asp
33  *
34  * for these and other documents.  See
35  *
36  *      http://www.socks.nec.com/protocol/socks4a.protocol
37  *
38  * for information on SOCKS version 4a.
39  *
40  * Revisions:
41  *
42  * 2003-09-18 JCFoster Fixed problem with socks tunnel in socks tunnel
43  *                      causing heap overflow because of an infinite loop
44  *                      where the socks dissect was call over and over.
45  *
46  *                      Also remove some old code marked with __JUNK__
47  *
48  * 2001-01-08 JCFoster Fixed problem with NULL pointer for hash data.
49  *                      Now test and exit if hash_info is null.
50  */
51
52 /* Possible enhancements -
53  *
54  * Add GSS-API authentication per rfc-1961
55  * Add CHAP authentication
56  * Decode FLAG bits per
57  *      http://archive.socks.permeo.com/draft/draft-ietf-aft-socks-pro-v5-04.txt
58  * In call_next_dissector, could load the destination address into
59  *      pinfo->src or pinfo->dst structure before calling next dissector.
60  * remove display_string or at least make it use protocol identifiers
61  * socks_hash_entry_t needs to handle V5 address type and domain names
62 */
63
64
65
66
67 #ifdef HAVE_CONFIG_H
68 # include "config.h"
69 #endif
70
71
72 #include <stdio.h>
73 #include <string.h>
74 #include <glib.h>
75
76 #include <epan/packet.h>
77 #include <epan/addr_resolv.h>
78 #include <epan/conversation.h>
79
80 #include <epan/emem.h>
81 #include "packet-tcp.h"
82 #include "packet-udp.h"
83 #include <epan/strutil.h>
84
85
86 #define compare_packet(X) (X == (pinfo->fd->num))
87 #define get_packet_ptr  (pinfo->fd->num)
88 #define row_pointer_type guint32
89
90 #define TCP_PORT_SOCKS 1080
91
92
93 /**************** Socks commands ******************/
94
95 #define CONNECT_COMMAND         1
96 #define BIND_COMMAND            2
97 #define UDP_ASSOCIATE_COMMAND   3
98 #define PING_COMMAND            0x80
99 #define TRACERT_COMMAND         0x81
100
101
102 /********** V5 Authentication methods *************/
103
104 #define NO_AUTHENTICATION       0
105 #define GSS_API_AUTHENTICATION  1
106 #define USER_NAME_AUTHENTICATION        2
107 #define CHAP_AUTHENTICATION     3
108 #define AUTHENTICATION_FAILED   0xff
109
110 /* 2003-09-18 JCFoster Fixed problem with socks tunnel in socks tunnel */
111
112 static int in_socks_dissector_flag = 0;         /* set to 1 to avoid recursive overflow */
113
114 /*********** Header field identifiers *************/
115
116 static int proto_socks = -1;
117
118 static int ett_socks = -1;
119 static int ett_socks_auth = -1;
120 static int ett_socks_name = -1;
121
122 static int hf_socks_ver = -1;
123 static int hf_socks_ip_dst = -1;
124 static int hf_socks_ip6_dst = -1;
125 static int hf_user_name = -1;
126 static int hf_v4a_dns_name = -1;
127 static int hf_socks_dstport = -1;
128 static int hf_socks_cmd = -1;
129 static int hf_socks_results = -1;
130 static int hf_socks_results_4 = -1;
131 static int hf_socks_results_5 = -1;
132
133
134 /************* Dissector handles ***********/
135
136 static dissector_handle_t socks_handle;
137 static dissector_handle_t socks_udp_handle;
138
139 /************* State Machine names ***********/
140
141 enum SockState {
142         None = 0,
143         Connecting,
144         V4UserNameWait,
145         V4NameWait,
146         V5Command,
147         V5Reply,
148         V5BindReply,
149         UserNameAuth,
150         UserNameAuthReply,
151         GssApiAuth,
152         AuthReply,
153         Done
154 };
155
156
157
158 typedef struct {
159         int             state;
160         int             version;
161         int             command;
162         int             grant;
163         guint32         port;
164         guint32         udp_port;
165         guint32         udp_remote_port;
166
167         int             connect_offset;
168         row_pointer_type        v4_name_row;
169         row_pointer_type        v4_user_name_row;
170         row_pointer_type        connect_row;
171         row_pointer_type        cmd_reply_row;
172         row_pointer_type        bind_reply_row;
173         row_pointer_type        command_row;
174         row_pointer_type        auth_method_row;
175         row_pointer_type        user_name_auth_row;
176         row_pointer_type        auth_version;
177         guint32 start_done_row;
178
179         guint32 dst_addr;       /* this needs to handle IPv6 */
180 }socks_hash_entry_t;
181
182
183
184
185 static const char *address_type_table[] = {
186         "Unknown",
187         "IPv4",
188         "Unknown",
189         "Domain Name",
190         "IPv6",
191         "Unknown"
192 };
193
194
195 /* String table for the V4 reply status messages */
196
197 static const value_string reply_table_v4[] = {
198         {90, "Granted"},
199         {91, "Rejected or Failed"},
200         {92, "Rejected because SOCKS server cannot connect to identd on the client"},
201         {93, "Rejected because the client program and identd report different user-ids"},
202         {0, NULL}
203 };
204
205 /* String table for the V5 reply status messages */
206
207 static const value_string reply_table_v5[] = {
208         {0, "Succeeded"},
209         {1, "General SOCKS server failure"},
210         {2, "Connection not allowed by ruleset"},
211         {3, "Network unreachable"},
212         {4, "Host unreachable"},
213         {5, "Connection refused"},
214         {6, "TTL expired"},
215         {7, "Command not supported"},
216         {8, "Address type not supported"},
217         {0, NULL},
218 };
219
220 static const value_string cmd_strings[] = {
221         {0, "Unknow"},
222         {1, "Connect"},
223         {2, "Bind"},
224         {3, "UdpAssociate"},
225         {0x80, "Ping"},
226         {0x81, "Traceroute"},
227         {0, NULL}
228 };
229
230
231 /************************* Support routines ***************************/
232
233
234 static int display_string(tvbuff_t *tvb, int offset,
235         proto_tree *tree, const char *label){
236
237 /* display a string with a length, characters encoding */
238 /* they are displayed under a tree with the name in Label variable */
239 /* return the length of the string and the length byte */
240
241
242         proto_tree      *name_tree;
243         proto_item      *ti;
244
245         char temp[ 256];
246         int length = tvb_get_guint8(tvb, offset);
247
248         tvb_memcpy(tvb, (guint8 *)temp, offset+1, length);
249         temp[ length ] = 0;
250
251         ti = proto_tree_add_text(tree, tvb, offset, length + 1,
252                 "%s: %s" , label, temp);
253
254
255         name_tree = proto_item_add_subtree(ti, ett_socks_name);
256
257         proto_tree_add_text( name_tree, tvb, offset, 1, "Length: %u", length);
258
259         ++offset;
260
261         proto_tree_add_text( name_tree, tvb, offset, length, "String: %s", temp);
262
263         return length + 1;
264 }
265
266
267
268 static const char *get_auth_method_name( guint Number){
269
270 /* return the name of the authenication method */
271
272         if ( Number == 0) return "No authentication";
273         if ( Number == 1) return "GSSAPI";
274         if ( Number == 2) return "Username/Password";
275         if ( Number == 3) return "Chap";
276         if (( Number >= 4) && ( Number <= 0x7f))return "IANA assigned";
277         if (( Number >= 0x80) && ( Number <= 0xfe)) return "private method";
278         if ( Number == 0xff) return "no acceptable method";
279
280         /* shouldn't reach here */
281
282         return "Bad method number (not 0-0xff)";
283 }
284
285
286 static const char *get_command_name( guint Number){
287
288 /* return the name of the command as a string */
289
290         if ( Number == 0) return "Unknow";
291         if ( Number == 1) return "Connect";
292         if ( Number == 2) return "Bind";
293         if ( Number == 3) return "UdpAssociate";
294         if ( Number == 0x80) return "Ping";
295         if ( Number == 0x81) return "Traceroute";
296         return "Unknown";
297 }
298
299
300 static int display_address(tvbuff_t *tvb, int offset, proto_tree *tree) {
301
302 /* decode and display the v5 address, return offset of next byte */
303
304         int a_type = tvb_get_guint8(tvb, offset);
305
306         proto_tree_add_text( tree, tvb, offset, 1,
307                         "Address Type: %d (%s)", a_type,
308                         address_type_table[ MIN( (guint) a_type,
309                                 array_length( address_type_table)-1) ]);
310
311         ++offset;
312
313         if ( a_type == 1){              /* IPv4 address */
314                 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset,
315                                         4, FALSE);
316                 offset += 4;
317         }
318         else if ( a_type == 3){ /* domain name address */
319
320                 offset += display_string(tvb, offset, tree,
321                         "Remote name");
322         }
323         else if ( a_type == 4){ /* IPv6 address */
324                 proto_tree_add_item( tree, hf_socks_ip6_dst, tvb, offset,
325                                 16, FALSE);
326                 offset += 16;
327         }
328
329         return offset;
330 }
331
332
333 static int get_address_v5(tvbuff_t *tvb, int offset,
334         socks_hash_entry_t *hash_info) {
335
336 /* decode the v5 address and return offset of next byte */
337 /*XXX this needs to handle IPV6 and domain name addresses */
338
339
340         int a_type = tvb_get_guint8(tvb, offset++);
341
342         if ( a_type == 1){              /* IPv4 address */
343
344                 if ( hash_info)
345                         hash_info->dst_addr = tvb_get_ipv4(tvb, offset);
346                 offset += 4;
347         }
348
349         else if ( a_type == 4)          /* IPv6 address */
350                 offset += 16;
351
352         else if ( a_type == 3)  /* domain name address */
353                 offset += tvb_get_guint8(tvb, offset) + 1;
354         return offset;
355 }
356
357
358 /********************* V5 UDP Associate handlers ***********************/
359
360 static void
361 socks_udp_dissector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
362
363 /* Conversation dissector called from UDP dissector. Decode and display */
364 /* the socks header, the pass the rest of the data to the udp port      */
365 /* decode routine to  handle the payload.                               */
366
367         int offset = 0;
368         guint32 *ptr;
369         socks_hash_entry_t *hash_info;
370         conversation_t *conversation;
371         proto_tree      *socks_tree;
372         proto_item      *ti;
373
374         conversation = find_conversation( pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
375                 pinfo->srcport, pinfo->destport, 0);
376
377         DISSECTOR_ASSERT( conversation);        /* should always find a conversation */
378
379         hash_info = conversation_get_proto_data(conversation, proto_socks);
380
381         if (check_col(pinfo->cinfo, COL_PROTOCOL))
382                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks");
383
384         if (check_col(pinfo->cinfo, COL_INFO))
385                 col_add_fstr(pinfo->cinfo, COL_INFO, "Version: 5, UDP Associated packet");
386
387         if ( tree) {
388                 ti = proto_tree_add_protocol_format( tree, proto_socks, tvb,
389                         offset, -1, "Socks" );
390
391                 socks_tree = proto_item_add_subtree(ti, ett_socks);
392
393                 proto_tree_add_text( socks_tree, tvb, offset, 2, "Reserved");
394                 offset += 2;
395
396                 proto_tree_add_text( socks_tree, tvb, offset, 1, "Fragment Number: %u", tvb_get_guint8(tvb, offset));
397                 ++offset;
398
399
400                 offset = display_address( tvb, offset, socks_tree);
401                 hash_info->udp_remote_port = tvb_get_ntohs(tvb, offset);
402
403                 proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb,
404                         offset, 2, hash_info->udp_remote_port);
405
406                 offset += 2;
407         }
408         else {          /* no tree, skip past the socks header */
409                 offset += 3;
410                 offset = get_address_v5( tvb, offset, 0) + 2;
411         }
412
413
414 /* set pi src/dst port and call the udp sub-dissector lookup */
415
416         if ( pinfo->srcport == hash_info->port)
417                 ptr = &pinfo->destport;
418         else
419                 ptr = &pinfo->srcport;
420
421         *ptr = hash_info->udp_remote_port;
422
423         decode_udp_ports( tvb, offset, pinfo, tree, pinfo->srcport, pinfo->destport, -1);
424
425         *ptr = hash_info->udp_port;
426
427 }
428
429
430 static void
431 new_udp_conversation( socks_hash_entry_t *hash_info, packet_info *pinfo){
432
433         conversation_t *conversation = conversation_new( pinfo->fd->num, &pinfo->src, &pinfo->dst,  PT_UDP,
434                         hash_info->udp_port, hash_info->port, 0);
435
436         DISSECTOR_ASSERT( conversation);
437
438         conversation_add_proto_data(conversation, proto_socks, hash_info);
439         conversation_set_dissector(conversation, socks_udp_handle);
440 }
441
442
443
444
445 /**************** Protocol Tree Display routines  ******************/
446
447 static void
448 display_socks_v4(tvbuff_t *tvb, int offset, packet_info *pinfo,
449         proto_tree *tree, socks_hash_entry_t *hash_info) {
450
451
452 /* Display the protocol tree for the V4 version. This routine uses the  */
453 /* stored conversation information to decide what to do with the row.   */
454 /* Per packet information would have been better to do this, but we     */
455 /* didn't have that when I wrote this. And I didn't expect this to get  */
456 /* so messy.                                                            */
457
458
459         guint command;
460         unsigned char ipaddr[4];
461         guint username_len, domainname_len;
462
463                                         /* Display command from client */
464         if (compare_packet( hash_info->connect_row)){
465
466                 proto_tree_add_text( tree, tvb, offset, 1,
467                                 "Version: %u", hash_info->version);
468                 ++offset;
469                 command = tvb_get_guint8(tvb, offset);
470
471                 proto_tree_add_text( tree, tvb, offset, 1,
472                         "Command: %u (%s)", command,
473                                 get_command_name( command));
474                 ++offset;
475
476                                                 /* Do remote port       */
477                 proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2,
478                                 FALSE);
479                 offset += 2;
480
481                                                 /* Do destination address */
482                 tvb_memcpy(tvb, ipaddr, offset, 4);
483                 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset,
484                                 4, FALSE);
485
486                 offset += 4;
487
488 /*XXX check this, needs to do length checking    */
489 /* Should perhaps do TCP reassembly as well */
490                 if ( tvb_offset_exists(tvb, offset)) {
491                                                 /* display user name    */
492                         username_len = tvb_strsize(tvb, offset);
493                         proto_tree_add_item( tree, hf_user_name, tvb, offset,
494                                 username_len, FALSE);
495                         offset += username_len;
496                         if ( ipaddr[0] == 0 && ipaddr[1] == 0 &&
497                              ipaddr[2] == 0 && ipaddr[3] != 0) {
498                                 /* 0.0.0.x , where x!=0 means v4a support */
499                                 domainname_len = tvb_strsize(tvb, offset);
500                                 proto_tree_add_item( tree, hf_v4a_dns_name,
501                                         tvb, offset, domainname_len,
502                                         FALSE);
503                         }
504                 }
505
506         }
507                                 /*Display command response from server*/
508
509         else if ( compare_packet( hash_info->cmd_reply_row)){
510
511                 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1,
512                                 FALSE);
513                 ++offset;
514                                                 /* Do results code      */
515                 proto_tree_add_item( tree, hf_socks_results_4, tvb, offset, 1, FALSE);
516                 proto_tree_add_item_hidden(tree, hf_socks_results, tvb, offset, 1, FALSE);
517
518                 ++offset;
519
520                                                 /* Do remote port       */
521                 proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2,
522                                 FALSE);
523                 offset += 2;
524                                                 /* Do remote address    */
525                 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4,
526                         FALSE);
527         }
528
529         else if ( compare_packet( hash_info->v4_user_name_row)){
530
531 /*XXX check this, needs to do length checking    */
532 /* Should perhaps do TCP reassembly as well */
533                 if ( tvb_offset_exists(tvb, offset)) {
534                         proto_tree_add_text( tree, tvb, offset,
535                                 tvb_strsize(tvb, offset),
536                                 "User Name: %s", tvb_get_ptr(tvb, offset, -1));
537                 }
538         }
539 }
540
541
542 static void
543 display_socks_v5(tvbuff_t *tvb, int offset, packet_info *pinfo,
544         proto_tree *tree, socks_hash_entry_t *hash_info) {
545
546 /* Display the protocol tree for the version. This routine uses the     */
547 /* stored conversation information to decide what to do with the row.   */
548 /* Per packet information would have been better to do this, but we     */
549 /* didn't have that when I wrote this. And I didn't expect this to get  */
550 /* so messy.                                                            */
551
552         unsigned int i, command;
553         guint temp;
554         const char *AuthMethodStr;
555         guint8 auth_status;
556
557         proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, FALSE);
558         ++offset;
559
560         if (compare_packet( hash_info->connect_row)){
561
562                 proto_tree      *AuthTree;
563                 proto_item      *ti;
564
565                 temp = tvb_get_guint8(tvb, offset);     /* Get Auth method count */
566                                                         /* build auth tree */
567                 ti = proto_tree_add_text( tree, tvb, offset, -1,
568                                 "Client Authentication Methods");
569
570                 AuthTree = proto_item_add_subtree(ti, ett_socks_auth);
571
572                 proto_tree_add_text( AuthTree, tvb, offset, 1,
573                                 "Count: %u", temp);
574                 ++offset;
575
576                 for( i = 0; i  < temp; ++i) {
577
578                         AuthMethodStr = get_auth_method_name(
579                                 tvb_get_guint8( tvb, offset));
580                         proto_tree_add_text( AuthTree, tvb, offset, 1,
581                                 "Method[%u]: %u (%s)", i,
582                                 tvb_get_guint8( tvb, offset), AuthMethodStr);
583                         ++offset;
584                 }
585                 proto_item_set_end( ti, tvb, offset);
586                 return;
587         }                                       /* Get accepted auth method */
588         else if (compare_packet( hash_info->auth_method_row)) {
589
590                 proto_tree_add_text( tree, tvb, offset, 1,
591                         "Accepted Auth Method: 0x%0x (%s)", tvb_get_guint8( tvb, offset),
592                                 get_auth_method_name( tvb_get_guint8( tvb, offset)));
593
594                 return;
595         }                                       /* handle user/password auth */
596         else if (compare_packet( hash_info->user_name_auth_row)) {
597
598                                                 /* process user name    */
599                 offset += display_string( tvb, offset, tree,
600                                 "User name");
601                                                 /* process password     */
602                 offset += display_string( tvb, offset, tree,
603                                 "Password");
604         }
605                                         /* command to the server */
606                                         /* command response from server */
607         else if (compare_packet( hash_info->auth_version)) {
608                 auth_status = tvb_get_guint8(tvb, offset);
609                 if(auth_status != 0)
610                         proto_tree_add_text( tree, tvb, offset, 1, "Status: %u (failure)", auth_status);
611                 else
612                         proto_tree_add_text( tree, tvb, offset, 1, "Status: success");
613                 offset ++;
614         }
615         else if ((compare_packet( hash_info->command_row)) ||
616                  (compare_packet( hash_info->cmd_reply_row)) ||
617                  (compare_packet( hash_info->bind_reply_row))){
618
619                 command = tvb_get_guint8(tvb, offset);
620
621                 if (compare_packet( hash_info->command_row))
622                         proto_tree_add_uint( tree, hf_socks_cmd, tvb, offset, 1,
623                             command);
624
625                 else {
626                         proto_tree_add_item( tree, hf_socks_results_5, tvb, offset, 1, FALSE);
627                         proto_tree_add_item_hidden(tree, hf_socks_results, tvb, offset, 1, FALSE);
628                 }
629
630                 ++offset;
631
632                 proto_tree_add_text( tree, tvb, offset, 1,
633                         "Reserved: 0x%0x (should = 0x00)", tvb_get_guint8(tvb, offset));
634                 ++offset;
635
636                 offset = display_address(tvb, offset, tree);
637 /*XXX Add remote port for search somehow */
638                                                 /* Do remote port       */
639                 proto_tree_add_text( tree, tvb, offset, 2,
640                                 "%sPort: %u",
641                                 (compare_packet( hash_info->bind_reply_row) ?
642                                         "Remote Host " : ""),
643                                  tvb_get_ntohs(tvb, offset));
644         }
645 }
646
647
648
649 /**************** Decoder State Machines ******************/
650
651
652 static guint
653 state_machine_v4( socks_hash_entry_t *hash_info, tvbuff_t *tvb,
654         int offset, packet_info *pinfo) {
655
656 /* Decode V4 protocol.  This is done on the first pass through the      */
657 /* list.  Based upon the current state, decode the packet and determine */
658 /* what the next state should be.  If we had per packet information,    */
659 /* this would be the place to load them up.                             */
660
661         if ( hash_info->state == None) {                /* new connection */
662
663                 if (check_col(pinfo->cinfo, COL_INFO))
664                         col_append_str(pinfo->cinfo, COL_INFO, " Connect to server request");
665
666                 hash_info->state = Connecting;  /* change state         */
667
668                 hash_info->command = tvb_get_guint8(tvb, offset + 1);
669                                                 /* get remote port      */
670                 if ( hash_info->command == CONNECT_COMMAND)
671                         hash_info->port =  tvb_get_ntohs(tvb, offset + 2);
672                                                 /* get remote address   */
673
674                 hash_info->dst_addr = tvb_get_ipv4(tvb, offset + 4);
675
676                                                 /* save the packet pointer */
677                 hash_info->connect_row = get_packet_ptr;
678
679                                                 /* skip past this stuff */
680                 hash_info->connect_offset = offset + 8;
681
682                 offset += 8;
683
684                 if ( !tvb_offset_exists(tvb, offset)) { /* if no user name */
685                                                         /* change state */
686                         hash_info->state = V4UserNameWait;
687                         /*
688                          * XXX - add 1, or leave it alone?
689                          * We were adding "strlen(...) + 1".
690                          */
691                         hash_info->connect_offset += 1;
692                 } else {
693                         /*
694                          * Add in the length of the user name.
695                          * XXX - what if the user name is split between
696                          * TCP segments?
697                          */
698                         hash_info->connect_offset += tvb_strsize(tvb, offset);
699                 }
700
701                 if ( !hash_info->dst_addr){             /* if no dest address */
702                                                         /* if more data */
703                         if ( tvb_offset_exists(tvb, hash_info->connect_offset)) {
704 /*XXX copy remote name here ??? */
705                                 hash_info->state = Connecting;
706                         }
707                         else
708                                 hash_info->state = V4NameWait;
709                                                 }
710                                                 /* waiting for V4 user name */
711         }else if ( hash_info->state == V4UserNameWait){
712
713                 if (check_col(pinfo->cinfo, COL_INFO))
714                         col_append_str(pinfo->cinfo, COL_INFO, " Connect Request (User name)");
715
716                 hash_info->v4_user_name_row = get_packet_ptr;
717 /*XXX may need to check for domain name here */
718                 hash_info->state = Connecting;
719         }
720                                         /* waiting for V4 domain name   */
721         else if ( hash_info->state == V4NameWait){
722
723                 hash_info->v4_name_row = get_packet_ptr;
724                 hash_info->state = Connecting;
725
726         }
727         else if ( hash_info->state == Connecting){
728
729                 if (check_col(pinfo->cinfo, COL_INFO))
730                         col_append_str(pinfo->cinfo, COL_INFO, " Connect Response");
731
732                                                 /* save packet pointer  */
733                 hash_info->cmd_reply_row = get_packet_ptr;
734                 hash_info->state = Done;                /* change state         */
735                 offset = offset + 8;
736         }
737
738         return offset;
739 }
740
741
742
743 static void
744 state_machine_v5( socks_hash_entry_t *hash_info, tvbuff_t *tvb,
745         int offset, packet_info *pinfo) {
746
747 /* Decode V5 protocol.  This is done on the first pass through the      */
748 /* list.  Based upon the current state, decode the packet and determine */
749 /* what the next state should be.  If we had per packet information,    */
750 /* this would be the place to load them up.                             */
751
752
753         int temp;
754
755         if ( hash_info->state == None) {
756
757                 if (check_col(pinfo->cinfo, COL_INFO))
758                         col_append_str(pinfo->cinfo, COL_INFO, " Connect to server request");
759
760                 hash_info->state = Connecting;  /* change state         */
761                 hash_info->connect_row = get_packet_ptr;
762
763                 temp = tvb_get_guint8(tvb, offset + 1);
764                                                 /* skip past auth methods */
765                 offset = hash_info->connect_offset = offset + 1 + temp;
766         }
767         else if ( hash_info->state == Connecting){
768
769                 guint AuthMethod = tvb_get_guint8(tvb, offset + 1);
770
771                 if (check_col(pinfo->cinfo, COL_INFO))
772                         col_append_str(pinfo->cinfo, COL_INFO, " Connect to server response");
773
774                 hash_info->auth_method_row = get_packet_ptr;
775
776                 if ( AuthMethod == NO_AUTHENTICATION)
777                         hash_info->state = V5Command;
778
779                 else if ( AuthMethod == USER_NAME_AUTHENTICATION)
780                         hash_info->state = UserNameAuth;
781
782                 else if ( AuthMethod == GSS_API_AUTHENTICATION)
783 /*XXX should be this            hash_info->state = GssApiAuth; */
784                         hash_info->state = Done;
785
786                 else    hash_info->state = Done;        /*Auth failed or error*/
787
788         }
789
790         else if ( hash_info->state == V5Command) {      /* Handle V5 Command */
791
792                 guint temp;
793
794                 hash_info->command = tvb_get_guint8(tvb, offset + 1); /* get command */
795
796                 if (check_col(pinfo->cinfo, COL_INFO))
797                         col_append_fstr(pinfo->cinfo, COL_INFO, " Command Request - %s",
798                                 get_command_name(hash_info->command));
799
800                 hash_info->state = V5Reply;
801                 hash_info->command_row = get_packet_ptr;
802
803                 offset += 3;                    /* skip to address type */
804
805                 offset = get_address_v5(tvb, offset, hash_info);
806
807                 temp = tvb_get_guint8(tvb, offset);
808
809                 if (( hash_info->command == CONNECT_COMMAND) ||
810                     ( hash_info->command == UDP_ASSOCIATE_COMMAND))
811                                                 /* get remote port      */
812                         hash_info->port =  tvb_get_ntohs(tvb, offset);
813         }
814
815         else if ( hash_info->state == V5Reply) {        /* V5 Command Reply */
816
817
818                 if (check_col(pinfo->cinfo, COL_INFO))
819                         col_append_fstr(pinfo->cinfo, COL_INFO, " Command Response - %s",
820                                 get_command_name(hash_info->command));
821
822                 hash_info->cmd_reply_row = get_packet_ptr;
823
824                 if (( hash_info->command == CONNECT_COMMAND) ||
825                     (hash_info->command == PING_COMMAND) ||
826                     (hash_info->command == TRACERT_COMMAND))
827                         hash_info->state = Done;
828
829                 else if ( hash_info->command == BIND_COMMAND)
830                         hash_info->state = V5BindReply;
831
832                 else if ( hash_info->command == UDP_ASSOCIATE_COMMAND){
833                         offset += 3;            /* skip to address type */
834                         offset = get_address_v5(tvb, offset, hash_info);
835
836         /* save server udp port and create udp conversation */
837                         hash_info->udp_port =  tvb_get_ntohs(tvb, offset);
838
839                         if (!pinfo->fd->flags.visited)
840                                 new_udp_conversation( hash_info, pinfo);
841
842 /*XXX may need else statement to handle unknows and generate error message */
843
844                 }
845         }
846         else if ( hash_info->state == V5BindReply) {    /* V5 Bind Second Reply */
847
848                 if (check_col(pinfo->cinfo, COL_INFO))
849                         col_append_str(pinfo->cinfo, COL_INFO, " Command Response: Bind remote host info");
850
851                 hash_info->bind_reply_row = get_packet_ptr;
852                 hash_info->state = Done;
853         }
854         else if ( hash_info->state == UserNameAuth) {   /* Handle V5 User Auth*/
855                 hash_info->auth_version = get_packet_ptr;
856                 if (check_col(pinfo->cinfo, COL_INFO))
857                         col_append_str(pinfo->cinfo, COL_INFO,
858                                 " User authentication request");
859
860                 hash_info->user_name_auth_row = get_packet_ptr;
861                 hash_info->state = AuthReply;
862
863         }
864         else if ( hash_info->state == AuthReply){       /* V5 User Auth reply */
865                 hash_info->auth_version = get_packet_ptr;
866                 if (check_col(pinfo->cinfo, COL_INFO))
867                         col_append_str(pinfo->cinfo, COL_INFO, " User authentication reply");
868                 hash_info->state = V5Command;
869         }
870 }
871
872
873
874 static void
875 display_ping_and_tracert(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, socks_hash_entry_t *hash_info) {
876
877 /* Display the ping/trace_route conversation */
878
879
880         const guchar    *data, *dataend;
881         const guchar   *lineend, *eol;
882         int             linelen;
883
884                                         /* handle the end command */
885         if ( pinfo->destport == TCP_PORT_SOCKS){
886                 if (check_col(pinfo->cinfo, COL_INFO))
887                         col_append_str(pinfo->cinfo, COL_INFO, ", Terminate Request");
888
889                 if ( tree)
890                         proto_tree_add_text(tree, tvb, offset, 1,
891                                 (hash_info->command  == PING_COMMAND) ?
892                                 "Ping: End command" :
893                                 "Traceroute: End command");
894         }
895         else{           /* display the PING or Traceroute results */
896                 if (check_col(pinfo->cinfo, COL_INFO))
897                         col_append_str(pinfo->cinfo, COL_INFO, ", Results");
898
899                 if ( tree){
900                         proto_tree_add_text(tree, tvb, offset, -1,
901                                 (hash_info->command  == PING_COMMAND) ?
902                                 "Ping Results:" :
903                                 "Traceroute Results");
904
905                         data = tvb_get_ptr(tvb, offset, -1);
906                         dataend = data + tvb_length_remaining(tvb, offset);
907
908                         while (data < dataend) {
909
910                                 lineend = find_line_end(data, dataend, &eol);
911                                 linelen = lineend - data;
912
913                                 proto_tree_add_text( tree, tvb, offset, linelen,
914                                         "%s", format_text(data, linelen));
915                                 offset += linelen;
916                                 data = lineend;
917                         }
918                 }
919         }
920 }
921
922
923
924 static void clear_in_socks_dissector_flag(void *dummy _U_)
925 {
926         in_socks_dissector_flag = 0; /* avoid recursive overflow */
927 }
928
929 static void call_next_dissector(tvbuff_t *tvb, int offset, packet_info *pinfo,
930         proto_tree *tree, proto_tree *socks_tree,
931         socks_hash_entry_t *hash_info)
932 {
933
934 /* Display the results for PING and TRACERT extensions or               */
935 /* Call TCP dissector for the port that was passed during the           */
936 /* connect process                                                      */
937 /* Load pointer to pinfo->XXXport depending upon the direction,         */
938 /* change pinfo port to the remote port, call next dissecotr to decode  */
939 /* the payload, and restore the pinfo port after that is done.          */
940
941         guint32 *ptr;
942         struct tcpinfo *tcpinfo = pinfo->private_data;
943         guint16 save_can_desegment;
944         struct tcp_analysis *tcpd=NULL;
945
946         tcpd=get_tcp_conversation_data(pinfo);
947
948         if (( hash_info->command  == PING_COMMAND) ||
949             ( hash_info->command  == TRACERT_COMMAND))
950
951                 display_ping_and_tracert(tvb, offset, pinfo, tree, hash_info);
952
953         else {          /* call the tcp port decoder to handle the payload */
954
955 /*XXX may want to load dest address here */
956
957                 if ( pinfo->destport  == TCP_PORT_SOCKS)
958                         ptr = &pinfo->destport;
959                 else
960                         ptr = &pinfo->srcport;
961
962                 *ptr = hash_info->port;
963
964 /* 2003-09-18 JCFoster Fixed problem with socks tunnel in socks tunnel */
965
966                 in_socks_dissector_flag = 1; /* avoid recursive overflow */
967                 CLEANUP_PUSH(clear_in_socks_dissector_flag, NULL);
968
969                 save_can_desegment = pinfo->can_desegment;
970                 pinfo->can_desegment = pinfo->saved_can_desegment;
971                 dissect_tcp_payload(tvb, pinfo, offset, tcpinfo->seq,
972                     tcpinfo->nxtseq, pinfo->srcport, pinfo->destport,
973                     tree, socks_tree, tcpd);
974                 pinfo->can_desegment = save_can_desegment;
975
976                 CLEANUP_CALL_AND_POP;
977
978                 *ptr = TCP_PORT_SOCKS;
979         }
980 }
981
982
983
984 static void
985 dissect_socks(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
986
987         int             offset = 0;
988         proto_tree      *socks_tree = NULL;
989         proto_item      *ti;
990         socks_hash_entry_t *hash_info;
991         conversation_t *conversation;
992
993 /* 2003-09-18 JCFoster Fixed problem with socks tunnel in socks tunnel */
994
995         /* avoid recursive overflow */
996
997         if ( in_socks_dissector_flag) {
998                 return;
999         }
1000
1001         conversation = find_conversation( pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
1002                 pinfo->srcport, pinfo->destport, 0);
1003
1004         if ( !conversation){
1005                 conversation = conversation_new( pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
1006                         pinfo->srcport, pinfo->destport, 0);
1007         }
1008         hash_info = conversation_get_proto_data(conversation,proto_socks);
1009         if ( !hash_info){
1010                 hash_info = se_alloc(sizeof(socks_hash_entry_t));
1011                 hash_info->start_done_row = G_MAXINT;
1012                 hash_info->state = None;
1013                 hash_info->port = 0;
1014                 hash_info->version = tvb_get_guint8(tvb, offset); /* get version*/
1015
1016                 if (( hash_info->version != 4) &&       /* error test version */
1017                    ( hash_info->version != 5))
1018                         hash_info->state = Done;
1019
1020                 conversation_add_proto_data(conversation, proto_socks,
1021                         hash_info);
1022
1023                                                 /* set dissector for now */
1024                 conversation_set_dissector(conversation, socks_handle);
1025         }
1026
1027 /* display summary window information  */
1028
1029         if (check_col(pinfo->cinfo, COL_PROTOCOL))
1030                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks");
1031
1032         if (check_col(pinfo->cinfo, COL_INFO)){
1033                 if (( hash_info->version == 4) || ( hash_info->version == 5)){
1034                         col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %d",
1035                                 hash_info->version);
1036                 }
1037                 else                    /* unknown version display error */
1038                         col_set_str(pinfo->cinfo, COL_INFO, "Unknown");
1039
1040
1041                 if ( hash_info->command == PING_COMMAND)
1042                         col_append_str(pinfo->cinfo, COL_INFO, ", Ping Req");
1043                 if ( hash_info->command == TRACERT_COMMAND)
1044                         col_append_str(pinfo->cinfo, COL_INFO, ", Traceroute Req");
1045
1046 /*XXX           if ( hash_info->port != -1) */
1047                 if ( hash_info->port != 0)
1048                         col_append_fstr(pinfo->cinfo, COL_INFO, ", Remote Port: %u",
1049                                 hash_info->port);
1050         }
1051
1052
1053 /* run state machine if needed */
1054
1055         if ((hash_info->state != Done) && ( !pinfo->fd->flags.visited)){
1056
1057                 if ( hash_info->version == 4)
1058                         state_machine_v4( hash_info, tvb, offset, pinfo);
1059
1060                 else if ( hash_info->version == 5)
1061                         state_machine_v5( hash_info, tvb, offset, pinfo);
1062
1063                 if (hash_info->state == Done) {         /* if done now  */
1064                         hash_info->start_done_row = pinfo->fd->num;
1065                 }
1066         }
1067
1068 /* if proto tree, decode and display */
1069
1070         if (tree) {
1071                 ti = proto_tree_add_item( tree, proto_socks, tvb, offset, -1,
1072                         FALSE );
1073
1074                 socks_tree = proto_item_add_subtree(ti, ett_socks);
1075
1076                 if ( hash_info->version == 4)
1077                         display_socks_v4(tvb, offset, pinfo, socks_tree,
1078                                 hash_info);
1079
1080                 else if ( hash_info->version == 5)
1081                         display_socks_v5(tvb, offset, pinfo, socks_tree,
1082                                 hash_info);
1083
1084                                 /* if past startup, add the faked stuff */
1085                 if ( pinfo->fd->num >  hash_info->start_done_row){
1086                                                 /*  add info to tree */
1087                         proto_tree_add_text( socks_tree, tvb, offset, 0,
1088                                 "Command: %d (%s)", hash_info->command,
1089                                 get_command_name(hash_info->command));
1090
1091                         proto_tree_add_ipv4( socks_tree, hf_socks_ip_dst, tvb,
1092                                         offset, 0, hash_info->dst_addr);
1093
1094                                 /* no fake address for ping & traceroute */
1095
1096                         if (( hash_info->command != PING_COMMAND) &&
1097                             ( hash_info->command != TRACERT_COMMAND)){
1098                                 proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb,
1099                                         offset, 0, hash_info->port);
1100                         }
1101                 }
1102
1103         }
1104
1105
1106 /* call next dissector if ready */
1107
1108         if ( pinfo->fd->num > hash_info->start_done_row){
1109                 call_next_dissector(tvb, offset, pinfo, tree, socks_tree,
1110                     hash_info);
1111         }
1112 }
1113
1114
1115
1116 void
1117 proto_register_socks( void){
1118
1119 /*** Prep the socks protocol, register it and a initialization routine  */
1120 /*      to clear the hash stuff.                                        */
1121
1122
1123         static gint *ett[] = {
1124                 &ett_socks,
1125                 &ett_socks_auth,
1126                 &ett_socks_name
1127
1128         };
1129
1130         static hf_register_info hf[] = {
1131
1132
1133                 { &hf_socks_ver,
1134                         { "Version", "socks.version", FT_UINT8, BASE_DEC, NULL,
1135                                 0x0, "", HFILL
1136                         }
1137                 },
1138                 { &hf_socks_ip_dst,
1139                         { "Remote Address", "socks.dst", FT_IPv4, BASE_NONE, NULL,
1140                                 0x0, "", HFILL
1141                         }
1142                 },
1143                 { &hf_socks_ip6_dst,
1144                         { "Remote Address(ipv6)", "socks.dstV6", FT_IPv6, BASE_NONE, NULL,
1145                                 0x0, "", HFILL
1146                         }
1147                 },
1148
1149                 { &hf_user_name,
1150                         { "User Name", "socks.username", FT_STRINGZ, BASE_NONE,
1151                                  NULL, 0x0, "", HFILL
1152                         }
1153                 },
1154                 { &hf_v4a_dns_name,
1155                         { "SOCKS v4a Remote Domain Name", "socks.v4a_dns_name", FT_STRINGZ, BASE_NONE,
1156                                 NULL, 0x0, "", HFILL
1157                         }
1158                 },
1159                 { &hf_socks_dstport,
1160                         { "Remote Port", "socks.dstport", FT_UINT16,
1161                                 BASE_DEC, NULL, 0x0, "", HFILL
1162                         }
1163                 },
1164                 { &hf_socks_cmd,
1165                         { "Command", "socks.command", FT_UINT8,
1166                                 BASE_DEC,  VALS(cmd_strings), 0x0, "", HFILL
1167                         }
1168                 },
1169                 { &hf_socks_results_4,
1170                         { "Results(V4)", "socks.results_v4", FT_UINT8,
1171                                 BASE_DEC, VALS(reply_table_v4), 0x0, "", HFILL
1172                         }
1173                 },
1174                 { &hf_socks_results_5,
1175                         { "Results(V5)", "socks.results_v5", FT_UINT8,
1176                                 BASE_DEC, VALS(reply_table_v5), 0x0, "", HFILL
1177                         }
1178                 },
1179                 { &hf_socks_results,
1180                         { "Results(V5)", "socks.results", FT_UINT8,
1181                                 BASE_DEC, NULL, 0x0, "", HFILL
1182                         }
1183                 }
1184
1185         };
1186
1187
1188         proto_socks = proto_register_protocol (
1189                 "Socks Protocol", "Socks", "socks");
1190
1191         proto_register_field_array(proto_socks, hf, array_length(hf));
1192         proto_register_subtree_array(ett, array_length(ett));
1193
1194         socks_udp_handle = create_dissector_handle(socks_udp_dissector,
1195             proto_socks);
1196         socks_handle = create_dissector_handle(dissect_socks, proto_socks);
1197 }
1198
1199
1200 void
1201 proto_reg_handoff_socks(void) {
1202
1203         /* dissector install routine */
1204
1205         dissector_add("tcp.port", TCP_PORT_SOCKS, socks_handle);
1206 }