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