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