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