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