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