Does it make any difference if it's
[privoxy.git] / gateway.c
1 const char gateway_rcs[] = "$Id: gateway.c,v 1.64 2009/10/03 10:37:49 fabiankeil Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/gateway.c,v $
5  *
6  * Purpose     :  Contains functions to connect to a server, possibly
7  *                using a "forwarder" (i.e. HTTP proxy and/or a SOCKS4
8  *                or SOCKS5 proxy).
9  *
10  * Copyright   :  Written by and Copyright (C) 2001-2009 the
11  *                Privoxy team. http://www.privoxy.org/
12  *
13  *                Based on the Internet Junkbuster originally written
14  *                by and Copyright (C) 1997 Anonymous Coders and
15  *                Junkbusters Corporation.  http://www.junkbusters.com
16  *
17  *                This program is free software; you can redistribute it
18  *                and/or modify it under the terms of the GNU General
19  *                Public License as published by the Free Software
20  *                Foundation; either version 2 of the License, or (at
21  *                your option) any later version.
22  *
23  *                This program is distributed in the hope that it will
24  *                be useful, but WITHOUT ANY WARRANTY; without even the
25  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
26  *                PARTICULAR PURPOSE.  See the GNU General Public
27  *                License for more details.
28  *
29  *                The GNU General Public License should be included with
30  *                this file.  If not, you can view it at
31  *                http://www.gnu.org/copyleft/gpl.html
32  *                or write to the Free Software Foundation, Inc., 59
33  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
34  *
35  *********************************************************************/
36
37
38 #include "config.h"
39
40 #include <stdio.h>
41 #include <sys/types.h>
42
43 #ifndef _WIN32
44 #include <netinet/in.h>
45 #endif
46
47 #include <errno.h>
48 #include <string.h>
49 #include "assert.h"
50
51 #ifdef _WIN32
52 #include <winsock2.h>
53 #endif /* def _WIN32 */
54
55 #ifdef __BEOS__
56 #include <netdb.h>
57 #endif /* def __BEOS__ */
58
59 #ifdef __OS2__
60 #include <utils.h>
61 #endif /* def __OS2__ */
62
63 #include "project.h"
64 #include "jcc.h"
65 #include "errlog.h"
66 #include "jbsockets.h"
67 #include "gateway.h"
68 #include "miscutil.h"
69 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
70 #ifdef HAVE_POLL
71 #ifdef __GLIBC__ 
72 #include <sys/poll.h>
73 #else
74 #include <poll.h>
75 #endif /* def __GLIBC__ */
76 #endif /* HAVE_POLL */
77 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
78
79 const char gateway_h_rcs[] = GATEWAY_H_VERSION;
80
81 static jb_socket socks4_connect(const struct forward_spec * fwd,
82                                 const char * target_host,
83                                 int target_port,
84                                 struct client_state *csp);
85
86 static jb_socket socks5_connect(const struct forward_spec *fwd,
87                                 const char *target_host,
88                                 int target_port,
89                                 struct client_state *csp);
90
91
92 #define SOCKS_REQUEST_GRANTED          90
93 #define SOCKS_REQUEST_REJECT           91
94 #define SOCKS_REQUEST_IDENT_FAILED     92
95 #define SOCKS_REQUEST_IDENT_CONFLICT   93
96
97 #define SOCKS5_REQUEST_GRANTED             0
98 #define SOCKS5_REQUEST_FAILED              1
99 #define SOCKS5_REQUEST_DENIED              2
100 #define SOCKS5_REQUEST_NETWORK_UNREACHABLE 3
101 #define SOCKS5_REQUEST_HOST_UNREACHABLE    4
102 #define SOCKS5_REQUEST_CONNECTION_REFUSED  5
103 #define SOCKS5_REQUEST_TTL_EXPIRED         6
104 #define SOCKS5_REQUEST_PROTOCOL_ERROR      7
105 #define SOCKS5_REQUEST_BAD_ADDRESS_TYPE    8
106
107 /* structure of a socks client operation */
108 struct socks_op {
109    unsigned char vn;          /* socks version number */
110    unsigned char cd;          /* command code */
111    unsigned char dstport[2];  /* destination port */
112    unsigned char dstip[4];    /* destination address */
113    char userid;               /* first byte of userid */
114    char padding[3];           /* make sure sizeof(struct socks_op) is endian-independent. */
115    /* more bytes of the userid follow, terminated by a NULL */
116 };
117
118 /* structure of a socks server reply */
119 struct socks_reply {
120    unsigned char vn;          /* socks version number */
121    unsigned char cd;          /* command code */
122    unsigned char dstport[2];  /* destination port */
123    unsigned char dstip[4];    /* destination address */
124 };
125
126 static const char socks_userid[] = "anonymous";
127
128 #ifdef FEATURE_CONNECTION_SHARING
129
130 #define MAX_REUSABLE_CONNECTIONS 100
131 static unsigned int keep_alive_timeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
132
133 static struct reusable_connection reusable_connection[MAX_REUSABLE_CONNECTIONS];
134 static int mark_connection_unused(const struct reusable_connection *connection);
135
136 /*********************************************************************
137  *
138  * Function    :  initialize_reusable_connections
139  *
140  * Description :  Initializes the reusable_connection structures.
141  *                Must be called with connection_reuse_mutex locked.
142  *
143  * Parameters  : N/A
144  *
145  * Returns     : void
146  *
147  *********************************************************************/
148 extern void initialize_reusable_connections(void)
149 {
150    unsigned int slot = 0;
151
152 #if !defined(HAVE_POLL) && !defined(_WIN32)
153    log_error(LOG_LEVEL_INFO,
154       "Detecting already dead connections might not work "
155       "correctly on your platform. In case of problems, "
156       "unset the keep-alive-timeout option.");
157 #endif
158
159    for (slot = 0; slot < SZ(reusable_connection); slot++)
160    {
161       mark_connection_closed(&reusable_connection[slot]);
162    }
163
164    log_error(LOG_LEVEL_CONNECT, "Initialized %d socket slots.", slot);
165 }
166
167
168 /*********************************************************************
169  *
170  * Function    :  remember_connection
171  *
172  * Description :  Remembers a server connection for reuse later on.
173  *
174  * Parameters  :
175  *          1  :  connection = The server connection to remember.
176  *
177  * Returns     : void
178  *
179  *********************************************************************/
180 void remember_connection(const struct reusable_connection *connection)
181 {
182    unsigned int slot = 0;
183    int free_slot_found = FALSE;
184
185    assert(NULL != connection);
186    assert(connection->sfd != JB_INVALID_SOCKET);
187
188    if (mark_connection_unused(connection))
189    {
190       return;
191    }
192
193    privoxy_mutex_lock(&connection_reuse_mutex);
194
195    /* Find free socket slot. */
196    for (slot = 0; slot < SZ(reusable_connection); slot++)
197    {
198       if (reusable_connection[slot].sfd == JB_INVALID_SOCKET)
199       {
200          assert(reusable_connection[slot].in_use == 0);
201          log_error(LOG_LEVEL_CONNECT,
202             "Remembering socket %d for %s:%d in slot %d.",
203             connection->sfd, connection->host, connection->port, slot);
204          free_slot_found = TRUE;
205          break;
206       }
207    }
208
209    if (!free_slot_found)
210    {
211       log_error(LOG_LEVEL_CONNECT,
212         "No free slots found to remembering socket for %s:%d. Last slot %d.",
213         connection->host, connection->port, slot);
214       privoxy_mutex_unlock(&connection_reuse_mutex);
215       close_socket(connection->sfd);
216       return;
217    }
218
219    assert(NULL != connection->host);
220    reusable_connection[slot].host = strdup(connection->host);
221    if (NULL == reusable_connection[slot].host)
222    {
223       log_error(LOG_LEVEL_FATAL, "Out of memory saving socket.");
224    }
225    reusable_connection[slot].sfd = connection->sfd;
226    reusable_connection[slot].port = connection->port;
227    reusable_connection[slot].in_use = 0;
228    reusable_connection[slot].timestamp = connection->timestamp;
229    reusable_connection->request_sent = connection->request_sent;
230    reusable_connection->response_received = connection->response_received;
231    reusable_connection[slot].keep_alive_timeout = connection->keep_alive_timeout;
232
233    assert(reusable_connection[slot].gateway_host == NULL);
234    assert(reusable_connection[slot].gateway_port == 0);
235    assert(reusable_connection[slot].forwarder_type == SOCKS_NONE);
236    assert(reusable_connection[slot].forward_host == NULL);
237    assert(reusable_connection[slot].forward_port == 0);
238
239    reusable_connection[slot].forwarder_type = connection->forwarder_type;
240    if (NULL != connection->gateway_host)
241    {
242       reusable_connection[slot].gateway_host = strdup(connection->gateway_host);
243       if (NULL == reusable_connection[slot].gateway_host)
244       {
245          log_error(LOG_LEVEL_FATAL, "Out of memory saving gateway_host.");
246       }
247    }
248    else
249    {
250       reusable_connection[slot].gateway_host = NULL;
251    }
252    reusable_connection[slot].gateway_port = connection->gateway_port;
253
254    if (NULL != connection->forward_host)
255    {
256       reusable_connection[slot].forward_host = strdup(connection->forward_host);
257       if (NULL == reusable_connection[slot].forward_host)
258       {
259          log_error(LOG_LEVEL_FATAL, "Out of memory saving forward_host.");
260       }
261    }
262    else
263    {
264       reusable_connection[slot].forward_host = NULL;
265    }
266    reusable_connection[slot].forward_port = connection->forward_port;
267
268    privoxy_mutex_unlock(&connection_reuse_mutex);
269 }
270 #endif /* def FEATURE_CONNECTION_SHARING */
271
272
273 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
274 /*********************************************************************
275  *
276  * Function    :  mark_connection_closed
277  *
278  * Description : Marks a reused connection closed.
279  *
280  * Parameters  :
281  *          1  :  closed_connection = The connection to mark as closed.
282  *
283  * Returns     : void
284  *
285  *********************************************************************/
286 void mark_connection_closed(struct reusable_connection *closed_connection)
287 {
288    closed_connection->in_use = FALSE;
289    closed_connection->sfd = JB_INVALID_SOCKET;
290    freez(closed_connection->host);
291    closed_connection->port = 0;
292    closed_connection->timestamp = 0;
293    closed_connection->request_sent = 0;
294    closed_connection->response_received = 0;
295    closed_connection->keep_alive_timeout = 0;
296    closed_connection->forwarder_type = SOCKS_NONE;
297    freez(closed_connection->gateway_host);
298    closed_connection->gateway_port = 0;
299    freez(closed_connection->forward_host);
300    closed_connection->forward_port = 0;
301 }
302 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
303
304
305 #ifdef FEATURE_CONNECTION_SHARING
306 /*********************************************************************
307  *
308  * Function    :  forget_connection
309  *
310  * Description :  Removes a previously remembered connection from
311  *                the list of reusable connections.
312  *
313  * Parameters  :
314  *          1  :  sfd = The socket belonging to the connection in question.
315  *
316  * Returns     : void
317  *
318  *********************************************************************/
319 void forget_connection(jb_socket sfd)
320 {
321    unsigned int slot = 0;
322
323    assert(sfd != JB_INVALID_SOCKET);
324
325    privoxy_mutex_lock(&connection_reuse_mutex);
326
327    for (slot = 0; slot < SZ(reusable_connection); slot++)
328    {
329       if (reusable_connection[slot].sfd == sfd)
330       {
331          assert(reusable_connection[slot].in_use);
332
333          log_error(LOG_LEVEL_CONNECT,
334             "Forgetting socket %d for %s:%d in slot %d.",
335             sfd, reusable_connection[slot].host,
336             reusable_connection[slot].port, slot);
337          mark_connection_closed(&reusable_connection[slot]);
338          privoxy_mutex_unlock(&connection_reuse_mutex);
339
340          return;
341       }
342    }
343
344    log_error(LOG_LEVEL_CONNECT,
345       "Socket %d already forgotten or never remembered.", sfd);
346
347    privoxy_mutex_unlock(&connection_reuse_mutex);
348 }
349 #endif /* def FEATURE_CONNECTION_SHARING */
350
351
352 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
353 /*********************************************************************
354  *
355  * Function    :  connection_destination_matches
356  *
357  * Description :  Determines whether a remembered connection can
358  *                be reused. That is, whether the destination and
359  *                the forwarding settings match.
360  *
361  * Parameters  :
362  *          1  :  connection = The connection to check.
363  *          2  :  http = The destination for the connection.
364  *          3  :  fwd  = The forwarder settings.
365  *
366  * Returns     :  TRUE for yes, FALSE otherwise.
367  *
368  *********************************************************************/
369 int connection_destination_matches(const struct reusable_connection *connection,
370                                    const struct http_request *http,
371                                    const struct forward_spec *fwd)
372 {
373    if ((connection->forwarder_type != fwd->type)
374     || (connection->gateway_port   != fwd->gateway_port)
375     || (connection->forward_port   != fwd->forward_port)
376     || (connection->port           != http->port))
377    {
378       return FALSE;
379    }
380
381    if ((    (NULL != connection->gateway_host)
382          && (NULL != fwd->gateway_host)
383          && strcmpic(connection->gateway_host, fwd->gateway_host))
384        && (connection->gateway_host != fwd->gateway_host))
385    {
386       log_error(LOG_LEVEL_CONNECT, "Gateway mismatch.");
387       return FALSE;
388    }
389
390    if ((    (NULL != connection->forward_host)
391          && (NULL != fwd->forward_host)
392          && strcmpic(connection->forward_host, fwd->forward_host))
393       && (connection->forward_host != fwd->forward_host))
394    {
395       log_error(LOG_LEVEL_CONNECT, "Forwarding proxy mismatch.");
396       return FALSE;
397    }
398
399    return (!strcmpic(connection->host, http->host));
400
401 }
402 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
403
404
405 #ifdef FEATURE_CONNECTION_SHARING
406 /*********************************************************************
407  *
408  * Function    :  close_unusable_connections
409  *
410  * Description :  Closes remembered connections that have timed
411  *                out or have been closed on the other side.
412  *
413  * Parameters  :  none
414  *
415  * Returns     :  Number of connections that are still alive.
416  *
417  *********************************************************************/
418 int close_unusable_connections(void)
419 {
420    unsigned int slot = 0;
421    int connections_alive = 0;
422
423    privoxy_mutex_lock(&connection_reuse_mutex);
424
425    for (slot = 0; slot < SZ(reusable_connection); slot++)
426    {
427       if (!reusable_connection[slot].in_use
428          && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
429       {
430          time_t time_open = time(NULL) - reusable_connection[slot].timestamp;
431          time_t latency = (reusable_connection[slot].response_received -
432             reusable_connection[slot].request_sent) / 2;
433
434          if (reusable_connection[slot].keep_alive_timeout < time_open + latency)
435          {
436             log_error(LOG_LEVEL_CONNECT,
437                "The connection to %s:%d in slot %d timed out. "
438                "Closing socket %d. Timeout is: %d. Assumed latency: %d.",
439                reusable_connection[slot].host,
440                reusable_connection[slot].port, slot,
441                reusable_connection[slot].sfd,
442                reusable_connection[slot].keep_alive_timeout,
443                latency);
444             close_socket(reusable_connection[slot].sfd);
445             mark_connection_closed(&reusable_connection[slot]);
446          }
447          else if (!socket_is_still_alive(reusable_connection[slot].sfd))
448          {
449             log_error(LOG_LEVEL_CONNECT,
450                "The connection to %s:%d in slot %d is no longer usable. "
451                "Closing socket %d.", reusable_connection[slot].host,
452                reusable_connection[slot].port, slot,
453                reusable_connection[slot].sfd);
454             close_socket(reusable_connection[slot].sfd);
455             mark_connection_closed(&reusable_connection[slot]);
456          }
457          else
458          {
459             connections_alive++;
460          }
461       }
462    }
463
464    privoxy_mutex_unlock(&connection_reuse_mutex);
465
466    return connections_alive;
467
468 }
469
470
471 /*********************************************************************
472  *
473  * Function    :  get_reusable_connection
474  *
475  * Description :  Returns an open socket to a previously remembered
476  *                open connection (if there is one).
477  *
478  * Parameters  :
479  *          1  :  http = The destination for the connection.
480  *          2  :  fwd  = The forwarder settings.
481  *
482  * Returns     :  JB_INVALID_SOCKET => No reusable connection found,
483  *                otherwise a usable socket.
484  *
485  *********************************************************************/
486 static jb_socket get_reusable_connection(const struct http_request *http,
487                                          const struct forward_spec *fwd)
488 {
489    jb_socket sfd = JB_INVALID_SOCKET;
490    unsigned int slot = 0;
491
492    close_unusable_connections();
493
494    privoxy_mutex_lock(&connection_reuse_mutex);
495
496    for (slot = 0; slot < SZ(reusable_connection); slot++)
497    {
498       if (!reusable_connection[slot].in_use
499          && (JB_INVALID_SOCKET != reusable_connection[slot].sfd))
500       {
501          if (connection_destination_matches(&reusable_connection[slot], http, fwd))
502          {
503             reusable_connection[slot].in_use = TRUE;
504             sfd = reusable_connection[slot].sfd;
505             log_error(LOG_LEVEL_CONNECT,
506                "Found reusable socket %d for %s:%d in slot %d. "
507                "Timestamp made %d seconds ago. Timeout: %d. Latency: %d.",
508                sfd, reusable_connection[slot].host, reusable_connection[slot].port,
509                slot, time(NULL) - reusable_connection[slot].timestamp,
510                reusable_connection[slot].keep_alive_timeout,
511                (int)(reusable_connection[slot].response_received -
512                reusable_connection[slot].request_sent));
513             break;
514          }
515       }
516    }
517
518    privoxy_mutex_unlock(&connection_reuse_mutex);
519
520    return sfd;
521
522 }
523
524
525 /*********************************************************************
526  *
527  * Function    :  mark_connection_unused
528  *
529  * Description :  Gives a remembered connection free for reuse.
530  *
531  * Parameters  :
532  *          1  :  connection = The connection in question.
533  *
534  * Returns     :  TRUE => Socket found and marked as unused.
535  *                FALSE => Socket not found.
536  *
537  *********************************************************************/
538 static int mark_connection_unused(const struct reusable_connection *connection)
539 {
540    unsigned int slot = 0;
541    int socket_found = FALSE;
542
543    assert(connection->sfd != JB_INVALID_SOCKET);
544
545    privoxy_mutex_lock(&connection_reuse_mutex);
546
547    for (slot = 0; slot < SZ(reusable_connection); slot++)
548    {
549       if (reusable_connection[slot].sfd == connection->sfd)
550       {
551          assert(reusable_connection[slot].in_use);
552          socket_found = TRUE;
553          log_error(LOG_LEVEL_CONNECT,
554             "Marking open socket %d for %s:%d in slot %d as unused.",
555             connection->sfd, reusable_connection[slot].host,
556             reusable_connection[slot].port, slot);
557          reusable_connection[slot].in_use = 0;
558          reusable_connection[slot].timestamp = connection->timestamp;
559          break;
560       }
561    }
562
563    privoxy_mutex_unlock(&connection_reuse_mutex);
564
565    return socket_found;
566
567 }
568
569
570 /*********************************************************************
571  *
572  * Function    :  set_keep_alive_timeout
573  *
574  * Description :  Sets the timeout after which open
575  *                connections will no longer be reused.
576  *
577  * Parameters  :
578  *          1  :  timeout = The timeout in seconds.
579  *
580  * Returns     :  void
581  *
582  *********************************************************************/
583 void set_keep_alive_timeout(unsigned int timeout)
584 {
585    keep_alive_timeout = timeout;
586 }
587 #endif /* def FEATURE_CONNECTION_SHARING */
588
589
590 /*********************************************************************
591  *
592  * Function    :  forwarded_connect
593  *
594  * Description :  Connect to a specified web server, possibly via
595  *                a HTTP proxy and/or a SOCKS proxy.
596  *
597  * Parameters  :
598  *          1  :  fwd = the proxies to use when connecting.
599  *          2  :  http = the http request and apropos headers
600  *          3  :  csp = Current client state (buffers, headers, etc...)
601  *
602  * Returns     :  JB_INVALID_SOCKET => failure, else it is the socket file descriptor.
603  *
604  *********************************************************************/
605 jb_socket forwarded_connect(const struct forward_spec * fwd,
606                             struct http_request *http,
607                             struct client_state *csp)
608 {
609    const char * dest_host;
610    int dest_port;
611    jb_socket sfd = JB_INVALID_SOCKET;
612
613 #ifdef FEATURE_CONNECTION_SHARING
614    if ((csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
615       && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
616    {
617       sfd = get_reusable_connection(http, fwd);
618       if (JB_INVALID_SOCKET != sfd)
619       {
620          return sfd;
621       }
622    }
623 #endif /* def FEATURE_CONNECTION_SHARING */
624
625    /* Figure out if we need to connect to the web server or a HTTP proxy. */
626    if (fwd->forward_host)
627    {
628       /* HTTP proxy */
629       dest_host = fwd->forward_host;
630       dest_port = fwd->forward_port;
631    }
632    else
633    {
634       /* Web server */
635       dest_host = http->host;
636       dest_port = http->port;
637    }
638
639    /* Connect, maybe using a SOCKS proxy */
640    switch (fwd->type)
641    {
642       case SOCKS_NONE:
643          sfd = connect_to(dest_host, dest_port, csp);
644          break;
645       case SOCKS_4:
646       case SOCKS_4A:
647          sfd = socks4_connect(fwd, dest_host, dest_port, csp);
648          break;
649       case SOCKS_5:
650          sfd = socks5_connect(fwd, dest_host, dest_port, csp);
651          break;
652       default:
653          /* Should never get here */
654          log_error(LOG_LEVEL_FATAL,
655             "SOCKS4 impossible internal error - bad SOCKS type.");
656    }
657
658    if (JB_INVALID_SOCKET != sfd)
659    {
660       log_error(LOG_LEVEL_CONNECT,
661          "Created new connection to %s:%d on socket %d.",
662          http->host, http->port, sfd);
663    }
664
665    return sfd;
666
667 }
668
669
670 /*********************************************************************
671  *
672  * Function    :  socks4_connect
673  *
674  * Description :  Connect to the SOCKS server, and connect through
675  *                it to the specified server.   This handles
676  *                all the SOCKS negotiation, and returns a file
677  *                descriptor for a socket which can be treated as a
678  *                normal (non-SOCKS) socket.
679  *
680  *                Logged error messages are saved to csp->error_message
681  *                and later reused by error_response() for the CGI
682  *                message. strdup allocation failures are handled there.
683  *
684  * Parameters  :
685  *          1  :  fwd = Specifies the SOCKS proxy to use.
686  *          2  :  target_host = The final server to connect to.
687  *          3  :  target_port = The final port to connect to.
688  *          4  :  csp = Current client state (buffers, headers, etc...)
689  *
690  * Returns     :  JB_INVALID_SOCKET => failure, else a socket file descriptor.
691  *
692  *********************************************************************/
693 static jb_socket socks4_connect(const struct forward_spec * fwd,
694                                 const char * target_host,
695                                 int target_port,
696                                 struct client_state *csp)
697 {
698    unsigned long web_server_addr;
699    char buf[BUFFER_SIZE];
700    struct socks_op    *c = (struct socks_op    *)buf;
701    struct socks_reply *s = (struct socks_reply *)buf;
702    size_t n;
703    size_t csiz;
704    jb_socket sfd;
705    int err = 0;
706    char *errstr = NULL;
707
708    if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
709    {
710       /* XXX: Shouldn't the config file parser prevent this? */
711       errstr = "NULL gateway host specified.";
712       err = 1;
713    }
714
715    if (fwd->gateway_port <= 0)
716    {
717       errstr = "invalid gateway port specified.";
718       err = 1;
719    }
720
721    if (err)
722    {
723       log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
724       csp->error_message = strdup(errstr); 
725       errno = EINVAL;
726       return(JB_INVALID_SOCKET);
727    }
728
729    /* build a socks request for connection to the web server */
730
731    strlcpy(&(c->userid), socks_userid, sizeof(buf) - sizeof(struct socks_op));
732
733    csiz = sizeof(*c) + sizeof(socks_userid) - sizeof(c->userid) - sizeof(c->padding);
734
735    switch (fwd->type)
736    {
737       case SOCKS_4:
738          web_server_addr = resolve_hostname_to_ip(target_host);
739          if (web_server_addr == INADDR_NONE)
740          {
741             errstr = "could not resolve target host";
742             log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s %s", errstr, target_host);
743             err = 1;
744          }
745          else
746          {
747             web_server_addr = htonl(web_server_addr);
748          }
749          break;
750       case SOCKS_4A:
751          web_server_addr = 0x00000001;
752          n = csiz + strlen(target_host) + 1;
753          if (n > sizeof(buf))
754          {
755             errno = EINVAL;
756             errstr = "buffer cbuf too small.";
757             log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
758             err = 1;
759          }
760          else
761          {
762             strlcpy(buf + csiz, target_host, sizeof(buf) - sizeof(struct socks_op) - csiz);
763             /*
764              * What we forward to the socks4a server should have the
765              * size of socks_op, plus the length of the userid plus
766              * its \0 byte (which we don't have to add because the
767              * first byte of the userid is counted twice as it's also
768              * part of sock_op) minus the padding bytes (which are part
769              * of the userid as well), plus the length of the target_host
770              * (which is stored csiz bytes after the beginning of the buffer),
771              * plus another \0 byte.
772              */
773             assert(n == sizeof(struct socks_op) + strlen(&(c->userid)) - sizeof(c->padding) + strlen(buf + csiz) + 1);
774             csiz = n;
775          }
776          break;
777       default:
778          /* Should never get here */
779          log_error(LOG_LEVEL_FATAL,
780             "socks4_connect: SOCKS4 impossible internal error - bad SOCKS type.");
781          /* Not reached */
782          return(JB_INVALID_SOCKET);
783    }
784
785    if (err)
786    {
787       csp->error_message = strdup(errstr);
788       return(JB_INVALID_SOCKET);
789    }
790
791    c->vn          = 4;
792    c->cd          = 1;
793    c->dstport[0]  = (unsigned char)((target_port       >> 8  ) & 0xff);
794    c->dstport[1]  = (unsigned char)((target_port             ) & 0xff);
795    c->dstip[0]    = (unsigned char)((web_server_addr   >> 24 ) & 0xff);
796    c->dstip[1]    = (unsigned char)((web_server_addr   >> 16 ) & 0xff);
797    c->dstip[2]    = (unsigned char)((web_server_addr   >>  8 ) & 0xff);
798    c->dstip[3]    = (unsigned char)((web_server_addr         ) & 0xff);
799
800    /* pass the request to the socks server */
801    sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
802
803    if (sfd == JB_INVALID_SOCKET)
804    {
805       /*
806        * XXX: connect_to should fill in the exact reason.
807        * Most likely resolving the IP of the forwarder failed.
808        */
809       errstr = "connect_to failed: see logfile for details";
810       err = 1;
811    }
812    else if (write_socket(sfd, (char *)c, csiz))
813    {
814       errstr = "SOCKS4 negotiation write failed.";
815       log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
816       err = 1;
817       close_socket(sfd);
818    }
819    else if (read_socket(sfd, buf, sizeof(buf)) != sizeof(*s))
820    {
821       errstr = "SOCKS4 negotiation read failed.";
822       log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
823       err = 1;
824       close_socket(sfd);
825    }
826
827    if (err)
828    {
829       csp->error_message = strdup(errstr);      
830       return(JB_INVALID_SOCKET);
831    }
832
833    switch (s->cd)
834    {
835       case SOCKS_REQUEST_GRANTED:
836          return(sfd);
837       case SOCKS_REQUEST_REJECT:
838          errstr = "SOCKS request rejected or failed.";
839          errno = EINVAL;
840          break;
841       case SOCKS_REQUEST_IDENT_FAILED:
842          errstr = "SOCKS request rejected because "
843             "SOCKS server cannot connect to identd on the client.";
844          errno = EACCES;
845          break;
846       case SOCKS_REQUEST_IDENT_CONFLICT:
847          errstr = "SOCKS request rejected because "
848             "the client program and identd report "
849             "different user-ids.";
850          errno = EACCES;
851          break;
852       default:
853          errno = ENOENT;
854          snprintf(buf, sizeof(buf),
855             "SOCKS request rejected for reason code %d.", s->cd);
856          errstr = buf;
857    }
858
859    log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
860    csp->error_message = strdup(errstr);
861    close_socket(sfd);
862
863    return(JB_INVALID_SOCKET);
864
865 }
866
867 /*********************************************************************
868  *
869  * Function    :  translate_socks5_error
870  *
871  * Description :  Translates a SOCKS errors to a string.
872  *
873  * Parameters  :
874  *          1  :  socks_error = The error code to translate.
875  *
876  * Returns     :  The string translation.
877  *
878  *********************************************************************/
879 static const char *translate_socks5_error(int socks_error)
880 {
881    switch (socks_error)
882    {
883       /* XXX: these should be more descriptive */
884       case SOCKS5_REQUEST_FAILED:
885          return "SOCKS5 request failed";
886       case SOCKS5_REQUEST_DENIED:
887          return "SOCKS5 request denied";
888       case SOCKS5_REQUEST_NETWORK_UNREACHABLE:
889          return "SOCKS5 network unreachable";
890       case SOCKS5_REQUEST_HOST_UNREACHABLE:
891          return "SOCKS5 host unreachable";
892       case SOCKS5_REQUEST_CONNECTION_REFUSED:
893          return "SOCKS5 connection refused";
894       case SOCKS5_REQUEST_TTL_EXPIRED:
895          return "SOCKS5 TTL expired";
896       case SOCKS5_REQUEST_PROTOCOL_ERROR:
897          return "SOCKS5 client protocol error";
898       case SOCKS5_REQUEST_BAD_ADDRESS_TYPE:
899          return "SOCKS5 domain names unsupported";
900       case SOCKS5_REQUEST_GRANTED:
901          return "everything's peachy";
902       default:
903          return "SOCKS5 negotiation protocol error";
904    }
905 }
906
907 /*********************************************************************
908  *
909  * Function    :  socks5_connect
910  *
911  * Description :  Connect to the SOCKS server, and connect through
912  *                it to the specified server.   This handles
913  *                all the SOCKS negotiation, and returns a file
914  *                descriptor for a socket which can be treated as a
915  *                normal (non-SOCKS) socket.
916  *
917  * Parameters  :
918  *          1  :  fwd = Specifies the SOCKS proxy to use.
919  *          2  :  target_host = The final server to connect to.
920  *          3  :  target_port = The final port to connect to.
921  *          4  :  csp = Current client state (buffers, headers, etc...)
922  *
923  * Returns     :  JB_INVALID_SOCKET => failure, else a socket file descriptor.
924  *
925  *********************************************************************/
926 static jb_socket socks5_connect(const struct forward_spec *fwd,
927                                 const char *target_host,
928                                 int target_port,
929                                 struct client_state *csp)
930 {
931    int err = 0;
932    char cbuf[300];
933    char sbuf[30];
934    size_t client_pos = 0;
935    int server_size = 0;
936    size_t hostlen = 0;
937    jb_socket sfd;
938    const char *errstr = NULL;
939
940    assert(fwd->gateway_host);
941    if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
942    {
943       errstr = "NULL gateway host specified";
944       err = 1;
945    }
946
947    if (fwd->gateway_port <= 0)
948    {
949       /*
950        * XXX: currently this can't happen because in
951        * case of invalid gateway ports we use the defaults.
952        * Of course we really shouldn't do that.
953        */
954       errstr = "invalid gateway port specified";
955       err = 1;
956    }
957
958    hostlen = strlen(target_host);
959    if (hostlen > (size_t)255)
960    {
961       errstr = "target host name is longer than 255 characters";
962       err = 1;
963    }
964
965    if (fwd->type != SOCKS_5)
966    {
967       /* Should never get here */
968       log_error(LOG_LEVEL_FATAL,
969          "SOCKS5 impossible internal error - bad SOCKS type");
970       err = 1;
971    }
972
973    if (err)
974    {
975       errno = EINVAL;
976       assert(errstr != NULL);
977       log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
978       csp->error_message = strdup(errstr);
979       return(JB_INVALID_SOCKET);
980    }
981
982    /* pass the request to the socks server */
983    sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
984
985    if (sfd == JB_INVALID_SOCKET)
986    {
987       errstr = "socks5 server unreachable";
988       log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
989       csp->error_message = strdup(errstr);
990       return(JB_INVALID_SOCKET);
991    }
992
993    client_pos = 0;
994    cbuf[client_pos++] = '\x05'; /* Version */
995    cbuf[client_pos++] = '\x01'; /* One authentication method supported */
996    cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */
997
998    if (write_socket(sfd, cbuf, client_pos))
999    {
1000       errstr = "SOCKS5 negotiation write failed";
1001       csp->error_message = strdup(errstr);
1002       log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1003       close_socket(sfd);
1004       return(JB_INVALID_SOCKET);
1005    }
1006
1007    if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
1008    {
1009       errstr = "SOCKS5 negotiation read failed";
1010       err = 1;
1011    }
1012
1013    if (!err && (sbuf[0] != '\x05'))
1014    {
1015       errstr = "SOCKS5 negotiation protocol version error";
1016       err = 1;
1017    }
1018
1019    if (!err && (sbuf[1] == '\xff'))
1020    {
1021       errstr = "SOCKS5 authentication required";
1022       err = 1;
1023    }
1024
1025    if (!err && (sbuf[1] != '\x00'))
1026    {
1027       errstr = "SOCKS5 negotiation protocol error";
1028       err = 1;
1029    }
1030
1031    if (err)
1032    {
1033       assert(errstr != NULL);
1034       log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1035       csp->error_message = strdup(errstr);
1036       close_socket(sfd);
1037       errno = EINVAL;
1038       return(JB_INVALID_SOCKET);
1039    }
1040
1041    client_pos = 0;
1042    cbuf[client_pos++] = '\x05'; /* Version */
1043    cbuf[client_pos++] = '\x01'; /* TCP connect */
1044    cbuf[client_pos++] = '\x00'; /* Reserved, must be 0x00 */
1045    cbuf[client_pos++] = '\x03'; /* Address is domain name */
1046    cbuf[client_pos++] = (char)(hostlen & 0xffu);
1047    assert(sizeof(cbuf) - client_pos > (size_t)255);
1048    /* Using strncpy because we really want the nul byte padding. */
1049    strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos);
1050    client_pos += (hostlen & 0xffu);
1051    cbuf[client_pos++] = (char)((target_port >> 8) & 0xff);
1052    cbuf[client_pos++] = (char)((target_port     ) & 0xff);
1053
1054    if (write_socket(sfd, cbuf, client_pos))
1055    {
1056       errstr = "SOCKS5 negotiation read failed";
1057       csp->error_message = strdup(errstr);
1058       log_error(LOG_LEVEL_CONNECT, "%s", errstr);
1059       close_socket(sfd);
1060       errno = EINVAL;
1061       return(JB_INVALID_SOCKET);
1062    }
1063
1064    server_size = read_socket(sfd, sbuf, sizeof(sbuf));
1065    if (server_size < 3)
1066    {
1067       errstr = "SOCKS5 negotiation read failed";
1068       err = 1;
1069    }
1070    else if (server_size > 20)
1071    {
1072       /* This is somewhat unexpected but doesn't realy matter. */
1073       log_error(LOG_LEVEL_CONNECT, "socks5_connect: read %d bytes "
1074          "from socks server. Would have accepted up to %d.",
1075          server_size, sizeof(sbuf));
1076    }
1077
1078    if (!err && (sbuf[0] != '\x05'))
1079    {
1080       errstr = "SOCKS5 negotiation protocol version error";
1081       err = 1;
1082    }
1083
1084    if (!err && (sbuf[2] != '\x00'))
1085    {
1086       errstr = "SOCKS5 negotiation protocol error";
1087       err = 1;
1088    }
1089
1090    if (!err)
1091    {
1092       if (sbuf[1] == SOCKS5_REQUEST_GRANTED)
1093       {
1094          return(sfd);
1095       }
1096       errstr = translate_socks5_error(sbuf[1]);
1097    }
1098
1099    assert(errstr != NULL);
1100    csp->error_message = strdup(errstr);
1101    log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
1102    close_socket(sfd);
1103    errno = EINVAL;
1104
1105    return(JB_INVALID_SOCKET);
1106
1107 }
1108
1109 /*
1110   Local Variables:
1111   tab-width: 3
1112   end:
1113 */