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