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