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