From Adam Sulmicki: fixes to typos in comments.
[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.35 2002/01/24 09:20:51 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,
413                         offset, -1, "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, -1,
901                                 (hash_info->command  == PING_COMMAND) ?
902                                 "Ping Results:" :
903                                 "Traceroute Results");
904
905                         data = tvb_get_ptr(tvb, offset, -1);
906                         dataend = data + tvb_length_remaining(tvb, offset);
907                 
908                         while (data < dataend) {
909         
910                                 lineend = find_line_end(data, dataend, &eol);
911                                 linelen = lineend - data;
912
913                                 proto_tree_add_text( tree, tvb, offset, linelen,
914                                         format_text(data, linelen));
915                                 offset += linelen;
916                                 data = lineend;
917                         }
918                 }
919         }
920 }
921
922
923
924 static void call_next_dissector(tvbuff_t *tvb, int offset, packet_info *pinfo,
925         proto_tree *tree, socks_hash_entry_t *hash_info) {
926
927 /* Display the results for PING and TRACERT extensions or               */
928 /* Call TCP dissector for the port that was passed during the           */
929 /* connect process                                                      */
930 /* Load pointer to pinfo->XXXport depending upon the direction,         */
931 /* change pinfo port to the remote port, call next dissecotr to decode  */
932 /* the payload, and restore the pinfo port after that is done.          */
933
934         guint32 *ptr;
935  
936         if (( hash_info->command  == PING_COMMAND) ||
937             ( hash_info->command  == TRACERT_COMMAND))
938                  
939                 display_ping_and_tracert(tvb, offset, pinfo, tree, hash_info);
940
941         else {          /* call the tcp port decoder to handle the payload */
942         
943 /*XXX may want to load dest address here */
944
945                 if ( pinfo->destport  == TCP_PORT_SOCKS)
946                         ptr = &pinfo->destport;
947                 else
948                         ptr = &pinfo->srcport;
949
950                 *ptr = hash_info->port;
951                 decode_tcp_ports( tvb, offset, pinfo, tree, pinfo->srcport, pinfo->destport);
952                 *ptr = TCP_PORT_SOCKS;
953         }
954 }                
955
956
957
958 static void
959 dissect_socks(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
960
961         int             offset = 0;
962         proto_tree      *socks_tree;
963         proto_item      *ti;
964         socks_hash_entry_t *hash_info;
965         conversation_t *conversation;
966         
967         conversation = find_conversation( &pinfo->src, &pinfo->dst, pinfo->ptype,
968                 pinfo->srcport, pinfo->destport, 0);
969
970         if ( !conversation){
971                 conversation = conversation_new( &pinfo->src, &pinfo->dst, pinfo->ptype,
972                         pinfo->srcport, pinfo->destport, 0);
973         }
974         hash_info = conversation_get_proto_data(conversation,proto_socks);
975         if ( !hash_info){
976                 hash_info = g_mem_chunk_alloc(socks_vals);
977                 hash_info->start_done_row = G_MAXINT;
978                 hash_info->state = None;
979                 hash_info->port = 0;
980                 hash_info->version = tvb_get_guint8(tvb, offset); /* get version*/
981
982                 if (( hash_info->version != 4) &&       /* error test version */
983                    ( hash_info->version != 5))
984                         hash_info->state = Done;
985
986                 conversation_add_proto_data(conversation, proto_socks,
987                         hash_info);
988
989                                                 /* set dissector for now */
990                 conversation_set_dissector(conversation, socks_handle);
991         }
992
993 /* display summary window information  */
994
995         if (check_col(pinfo->cinfo, COL_PROTOCOL))
996                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks");
997
998         if (check_col(pinfo->cinfo, COL_INFO)){
999                 if (( hash_info->version == 4) || ( hash_info->version == 5)){
1000                         col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %d",
1001                                 hash_info->version);
1002                 }               
1003                 else                    /* unknown version display error */
1004                         col_set_str(pinfo->cinfo, COL_INFO, "Unknown");
1005                 
1006
1007                 if ( hash_info->command == PING_COMMAND)
1008                         col_append_str(pinfo->cinfo, COL_INFO, ", Ping Req");
1009                 if ( hash_info->command == TRACERT_COMMAND)
1010                         col_append_str(pinfo->cinfo, COL_INFO, ", Traceroute Req");
1011                 
1012 /*XXX           if ( hash_info->port != -1) */
1013                 if ( hash_info->port != 0)
1014                         col_append_fstr(pinfo->cinfo, COL_INFO, ", Remote Port: %d",
1015                                 hash_info->port);
1016         }
1017
1018
1019 /* run state machine if needed */
1020
1021         if ((hash_info->state != Done) && ( !pinfo->fd->flags.visited)){
1022
1023                 if ( hash_info->version == 4)
1024                         state_machine_v4( hash_info, tvb, offset, pinfo);
1025
1026                 else if ( hash_info->version == 5)
1027                         state_machine_v5( hash_info, tvb, offset, pinfo);
1028
1029                 if (hash_info->state == Done) {         /* if done now  */
1030                         hash_info->start_done_row = pinfo->fd->num;
1031                 }
1032         }
1033         
1034 /* if proto tree, decode and display */
1035
1036         if (tree) {                     
1037                 ti = proto_tree_add_item( tree, proto_socks, tvb, offset, -1,
1038                         FALSE );
1039
1040                 socks_tree = proto_item_add_subtree(ti, ett_socks);
1041
1042                 if ( hash_info->version == 4)
1043                         display_socks_v4(tvb, offset, pinfo, tree, socks_tree,
1044                                 hash_info);
1045                         
1046                 else if ( hash_info->version == 5)
1047                         display_socks_v5(tvb, offset, pinfo, tree, socks_tree,
1048                                 hash_info);
1049
1050                                 /* if past startup, add the faked stuff */
1051                 if ( pinfo->fd->num >  hash_info->start_done_row){
1052                                                 /*  add info to tree */
1053                         proto_tree_add_text( socks_tree, tvb, offset, 0,
1054                                 "Command: %d (%s)", hash_info->command,
1055                                 get_command_name(hash_info->command));
1056
1057                         proto_tree_add_ipv4( socks_tree, hf_socks_ip_dst, tvb,
1058                                         offset, 0, hash_info->dst_addr);
1059
1060                                 /* no fake address for ping & traceroute */
1061                                 
1062                         if (( hash_info->command != PING_COMMAND) &&
1063                             ( hash_info->command != TRACERT_COMMAND)){
1064                                 proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb,
1065                                         offset, 0, hash_info->port);
1066                         }
1067                 }
1068
1069         }
1070
1071
1072 /* call next dissector if ready */
1073
1074         if ( pinfo->fd->num > hash_info->start_done_row){
1075                 call_next_dissector(tvb, offset, pinfo, tree, hash_info);
1076         }
1077 }
1078
1079
1080
1081 static void socks_reinit( void){
1082
1083 /* Do the cleanup work when a new pass through the packet list is       */
1084 /* performed. Reset the highest row seen counter and re-initialize the  */
1085 /* conversation memory chunks.                                          */
1086
1087         if (socks_vals)
1088                 g_mem_chunk_destroy(socks_vals);
1089
1090         socks_vals = g_mem_chunk_new("socks_vals", socks_hash_val_length,
1091                 socks_hash_init_count * socks_hash_val_length,
1092                 G_ALLOC_AND_FREE);
1093 }
1094
1095
1096 void
1097 proto_register_socks( void){
1098
1099 /*** Prep the socks protocol, register it and a initialization routine  */
1100 /*      to clear the hash stuff.                                        */
1101
1102
1103         static gint *ett[] = {
1104                 &ett_socks,
1105                 &ett_socks_auth,
1106                 &ett_socks_name
1107                 
1108         };
1109
1110         static hf_register_info hf[] = {
1111     
1112
1113                 { &hf_socks_ver,
1114                         { "Version", "socks.version", FT_UINT8, BASE_DEC, NULL,
1115                                 0x0, "", HFILL
1116                         }
1117                 },
1118                 { &hf_socks_ip_dst,
1119                         { "Remote Address", "socks.dst", FT_IPv4, BASE_NONE, NULL,
1120                                 0x0, "", HFILL
1121                         }
1122                 },
1123                 { &hf_socks_ip6_dst,
1124                         { "Remote Address(ipv6)", "socks.dstV6", FT_IPv6, BASE_NONE, NULL,
1125                                 0x0, "", HFILL
1126                         }
1127                 },
1128
1129                 { &hf_user_name,
1130                         { "User Name", "socks.username", FT_STRING, BASE_NONE,
1131                                  NULL, 0x0, "", HFILL
1132                         }
1133                 },
1134                 { &hf_socks_dstport,
1135                         { "Remote Port", "socks.dstport", FT_UINT16,
1136                                 BASE_DEC, NULL, 0x0, "", HFILL
1137                         }
1138                 },
1139                 { &hf_socks_cmd,
1140                         { "Command", "socks.command", FT_UINT16,
1141                                 BASE_DEC,  VALS(cmd_strings), 0x0, "", HFILL
1142                         }
1143                 },
1144                 { &hf_socks_results_4,
1145                         { "Results(V4)", "socks.results_v4", FT_UINT8,
1146                                 BASE_DEC, VALS(reply_table_v4), 0x0, "", HFILL
1147                         }
1148                 },
1149                 { &hf_socks_results_5,
1150                         { "Results(V5)", "socks.results_v5", FT_UINT8,
1151                                 BASE_DEC, VALS(reply_table_v5), 0x0, "", HFILL
1152                         }
1153                 },
1154                 { &hf_socks_results,
1155                         { "Results(V5)", "socks.results", FT_UINT8,
1156                                 BASE_DEC, NULL, 0x0, "", HFILL
1157                         }
1158                 }
1159
1160         };
1161
1162
1163         proto_socks = proto_register_protocol (
1164                 "Socks Protocol", "Socks", "socks");           
1165
1166         proto_register_field_array(proto_socks, hf, array_length(hf));
1167         proto_register_subtree_array(ett, array_length(ett));  
1168
1169         register_init_routine( &socks_reinit);  /* register re-init routine */
1170
1171         socks_udp_handle = create_dissector_handle(socks_udp_dissector,
1172             proto_socks);
1173         socks_handle = create_dissector_handle(dissect_socks, proto_socks);
1174 }
1175
1176
1177 void
1178 proto_reg_handoff_socks(void) {
1179
1180         /* dissector install routine */ 
1181  
1182         dissector_add("tcp.port", TCP_PORT_SOCKS, socks_handle);
1183 }