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