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