From Joerg Mayer:
[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.38 2002/04/14 23:04:04 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,
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, proto_tree *tree) {
323
324 /* decode and display the v5 address, return offset of next byte */
325
326         int a_type = tvb_get_guint8(tvb, offset);
327
328         proto_tree_add_text( tree, tvb, offset, 1,
329                         "Address Type: %d (%s)", a_type, 
330                         address_type_table[ MIN( (guint) a_type,
331                                 array_length( address_type_table)-1) ]);
332
333         ++offset;
334
335         if ( a_type == 1){              /* IPv4 address */
336                 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset,
337                                         4, FALSE);
338                 offset += 4;
339         }       
340         else if ( a_type == 3){ /* domain name address */
341
342                 offset += display_string(tvb, offset, tree,
343                         "Remote name");
344         }
345         else if ( a_type == 4){ /* IPv6 address */
346                 proto_tree_add_item( tree, hf_socks_ip6_dst, tvb, offset,
347                                 16, FALSE);
348                 offset += 16;
349         }
350
351         return offset;
352 }
353
354
355 static int get_address_v5(tvbuff_t *tvb, int offset, 
356         socks_hash_entry_t *hash_info) {
357
358 /* decode the v5 address and return offset of next byte */
359 /*XXX this needs to handle IPV6 and domain name addresses */
360  
361
362         int a_type = tvb_get_guint8(tvb, offset++);
363
364         if ( a_type == 1){              /* IPv4 address */
365            
366                 if ( hash_info)
367                         tvb_memcpy(tvb, (guint8 *)&hash_info->dst_addr,
368                             offset, 4);
369                 offset += 4;
370         }
371                 
372         else if ( a_type == 4)          /* IPv6 address */
373                 offset += 16;
374         
375         else if ( a_type == 3)  /* domain name address */
376                 offset += tvb_get_guint8(tvb, offset) + 1;
377         return offset;
378 }       
379
380
381 /********************* V5 UDP Associate handlers ***********************/
382
383 static void
384 socks_udp_dissector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
385
386 /* Conversation dissector called from UDP dissector. Decode and display */
387 /* the socks header, the pass the rest of the data to the udp port      */
388 /* decode routine to  handle the payload.                               */
389
390         int offset = 0;
391         guint32 *ptr;
392         socks_hash_entry_t *hash_info;
393         conversation_t *conversation;
394         proto_tree      *socks_tree;
395         proto_item      *ti;
396         
397         conversation = find_conversation( &pinfo->src, &pinfo->dst, pinfo->ptype,
398                 pinfo->srcport, pinfo->destport, 0);
399
400         g_assert( conversation);        /* should always find a conversation */
401
402         hash_info = conversation_get_proto_data(conversation, proto_socks);
403
404         if (check_col(pinfo->cinfo, COL_PROTOCOL))
405                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks");
406
407         if (check_col(pinfo->cinfo, COL_INFO))
408                 col_add_fstr(pinfo->cinfo, COL_INFO, "Version: 5, UDP Associated packet");
409                         
410         if ( tree) {
411                 ti = proto_tree_add_protocol_format( tree, proto_socks, tvb,
412                         offset, -1, "Socks" );
413
414                 socks_tree = proto_item_add_subtree(ti, ett_socks);
415
416                 proto_tree_add_text( socks_tree, tvb, offset, 2, "Reserved");
417                 offset += 2;
418                 
419                 proto_tree_add_text( socks_tree, tvb, offset, 1, "Fragment Number: %d", tvb_get_guint8(tvb, offset));
420                 ++offset;
421         
422
423                 offset = display_address( tvb, offset, socks_tree);
424                 hash_info->udp_remote_port = tvb_get_ntohs(tvb, offset);
425                 
426                 proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb,
427                         offset, 2, hash_info->udp_remote_port);
428                         
429                 offset += 2;
430         }
431         else {          /* no tree, skip past the socks header */
432                 offset += 3;
433                 offset = get_address_v5( tvb, offset, 0) + 2;
434         }       
435
436
437 /* set pi src/dst port and call the udp sub-dissector lookup */
438
439         if ( pinfo->srcport == hash_info->port)                 
440                 ptr = &pinfo->destport;
441         else
442                 ptr = &pinfo->srcport;
443
444         *ptr = hash_info->udp_remote_port;
445         
446         decode_udp_ports( tvb, offset, pinfo, tree, pinfo->srcport, pinfo->destport);
447  
448         *ptr = hash_info->udp_port;
449
450 }
451
452                         
453 static void 
454 new_udp_conversation( socks_hash_entry_t *hash_info, packet_info *pinfo){
455
456         conversation_t *conversation = conversation_new( &pinfo->src, &pinfo->dst,  PT_UDP,
457                         hash_info->udp_port, hash_info->port, 0);
458
459         g_assert( conversation);
460         
461         conversation_add_proto_data(conversation, proto_socks, hash_info);
462         conversation_set_dissector(conversation, socks_udp_handle);
463 }
464
465
466
467
468 /**************** Protocol Tree Display routines  ******************/
469
470 static void
471 display_socks_v4(tvbuff_t *tvb, int offset, packet_info *pinfo,
472         proto_tree *tree, socks_hash_entry_t *hash_info) {
473
474
475 /* Display the protocol tree for the V4 version. This routine uses the  */
476 /* stored conversation information to decide what to do with the row.   */
477 /* Per packet information would have been better to do this, but we     */
478 /* didn't have that when I wrote this. And I didn't expect this to get  */
479 /* so messy.                                                            */
480
481
482         guint command;
483
484                                         /* Display command from client */
485         if (compare_packet( hash_info->connect_row)){
486
487                 proto_tree_add_text( tree, tvb, offset, 1,
488                                 "Version: %u ", hash_info->version);
489                 ++offset;
490                 command = tvb_get_guint8(tvb, offset);
491
492                 proto_tree_add_text( tree, tvb, offset, 1,
493                         "Command: %u (%s)", command, 
494                                 get_command_name( command));
495                 ++offset;
496
497                                                 /* Do remote port       */
498                 proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2,
499                                 FALSE);
500                 offset += 2;
501
502                                                 /* Do destination address */
503                 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset,
504                                 4, FALSE);
505
506                 offset += 4;
507
508 /*XXX check this, needs to do length checking    */             
509 /* Should perhaps do TCP reassembly as well */
510                 if ( tvb_offset_exists(tvb, offset)) {
511                                                 /* display user name    */
512                         proto_tree_add_string( tree, hf_user_name, tvb, offset, 
513                                 strlen( tvb_get_ptr(tvb, offset, -1)) + 1,
514                                 tvb_get_ptr(tvb, offset, -1));
515                 }
516
517         }
518                                 /*Display command response from server*/
519         
520         else if ( compare_packet( hash_info->cmd_reply_row)){
521                                  
522                 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1,
523                                 FALSE);
524                 ++offset;
525                                                 /* Do results code      */
526                 proto_tree_add_item( tree, hf_socks_results_4, tvb, offset, 1, FALSE);
527                 proto_tree_add_item_hidden(tree, hf_socks_results, tvb, offset, 1, FALSE);
528
529                 ++offset;
530
531                                                 /* Do remote port       */
532                 proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2,
533                                 FALSE);
534                 offset += 2;
535                                                 /* Do remote address    */
536                 proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4,
537                         FALSE);
538         }
539         
540         else if ( compare_packet( hash_info->v4_user_name_row)){
541                          
542 /*XXX check this, needs to do length checking    */
543 /* Should perhaps do TCP reassembly as well */
544                 if ( tvb_offset_exists(tvb, offset)) {
545                         proto_tree_add_text( tree, tvb, offset,
546                                 strlen( tvb_get_ptr(tvb, offset, -1)),
547                                 "User Name: %s", tvb_get_ptr(tvb, offset, -1));
548                 }
549         }
550 }                       
551
552
553 static void 
554 display_socks_v5(tvbuff_t *tvb, int offset, packet_info *pinfo,
555         proto_tree *tree, socks_hash_entry_t *hash_info) {
556         
557 /* Display the protocol tree for the version. This routine uses the     */
558 /* stored conversation information to decide what to do with the row.   */
559 /* Per packet information would have been better to do this, but we     */
560 /* didn't have that when I wrote this. And I didn't expect this to get  */
561 /* so messy.                                                            */
562
563         unsigned int i, command;
564         guint temp;
565         char *AuthMethodStr;
566
567
568         if (compare_packet( hash_info->connect_row)){
569
570                 proto_tree      *AuthTree;
571                 proto_item      *ti;
572
573                                                 /* Do version   */
574                 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1,
575                                 hash_info->version);
576                 ++offset;
577
578                 temp = tvb_get_guint8(tvb, offset);     /* Get Auth method count */
579                                                         /* build auth tree */
580                 ti = proto_tree_add_text( tree, tvb, offset, 1,
581                                 "Client Authentication Methods");
582                                 
583                 AuthTree = proto_item_add_subtree(ti, ett_socks_auth);
584
585                 proto_tree_add_text( AuthTree, tvb, offset, 1,
586                                 "Count: %u ", temp);
587                 ++offset;
588
589                 for( i = 0; i  < temp; ++i) {
590
591                         AuthMethodStr = get_auth_method_name(
592                                 tvb_get_guint8( tvb, offset + i));
593                         proto_tree_add_text( AuthTree, tvb, offset + i, 1,
594                                 "Method[%d]: %u (%s)", i,
595                                 tvb_get_guint8( tvb, offset + i), AuthMethodStr); 
596                 }
597                 return;
598         }                                       /* Get accepted auth method */
599         else if (compare_packet( hash_info->auth_method_row)) {
600
601                 ++offset;
602
603                 proto_tree_add_text( tree, tvb, offset, 1,
604                         "Accepted Auth Method: 0x%0x (%s)", tvb_get_guint8( tvb, offset),
605                                 get_auth_method_name( tvb_get_guint8( tvb, offset)));
606
607                 return;
608         }                                       /* handle user/password auth */
609         else if (compare_packet( hash_info->user_name_auth_row)) {
610
611                 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, FALSE);
612                 ++offset;
613                                                 /* process user name    */
614                 offset += display_string( tvb, offset, tree,
615                                 "User name");
616                                                 /* process password     */
617                 offset += display_string( tvb, offset, tree,
618                                 "Password");
619         }                                       
620                                         /* command to the server */     
621                                         /* command response from server */
622         else if ((compare_packet( hash_info->command_row)) || 
623                  (compare_packet( hash_info->cmd_reply_row)) ||
624                  (compare_packet( hash_info->bind_reply_row))){
625
626                 proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, FALSE);
627
628                 ++offset;
629
630                 command = tvb_get_guint8(tvb, offset);
631                 
632                 if (compare_packet( hash_info->command_row))
633                         proto_tree_add_uint( tree, hf_socks_cmd, tvb, offset, 1,
634                             command);
635
636                 else {
637                         proto_tree_add_item( tree, hf_socks_results_5, tvb, offset, 1, FALSE);
638                         proto_tree_add_item_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, 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, socks_tree,
1044                                 hash_info);
1045                         
1046                 else if ( hash_info->version == 5)
1047                         display_socks_v5(tvb, offset, pinfo, 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_UINT8,
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 }
1184