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