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