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