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