Added section on JB internal pages in Appendix.
[privoxy.git] / gateway.c
1 const char gateway_rcs[] = "$Id: gateway.c,v 1.11 2002/03/08 17:46:04 jongfoster 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  *                proxy).
9  *
10  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
11  *                IJBSWA team.  http://ijbswa.sourceforge.net
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  * Revisions   :
36  *    $Log: gateway.c,v $
37  *    Revision 1.11  2002/03/08 17:46:04  jongfoster
38  *    Fixing int/size_t warnings
39  *
40  *    Revision 1.10  2002/03/07 03:50:19  oes
41  *     - Improved handling of failed DNS lookups
42  *     - Fixed compiler warnings
43  *
44  *    Revision 1.9  2001/10/25 03:40:48  david__schmidt
45  *    Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
46  *    threads to call select() simultaneously.  So, it's time to do a real, live,
47  *    native OS/2 port.  See defines for __EMX__ (the porting layer) vs. __OS2__
48  *    (native). Both versions will work, but using __OS2__ offers multi-threading.
49  *
50  *    Revision 1.8  2001/09/13 20:10:12  jongfoster
51  *    Fixing missing #include under Windows
52  *
53  *    Revision 1.7  2001/09/12 17:58:26  steudten
54  *
55  *    add #include <string.h>
56  *
57  *    Revision 1.6  2001/09/10 10:41:16  oes
58  *    Added #include in.h
59  *
60  *    Revision 1.5  2001/07/29 18:47:57  jongfoster
61  *    Adding missing #include project.h
62  *
63  *    Revision 1.4  2001/07/24 12:47:06  oes
64  *    Applied BeOS support update by Eugenia
65  *
66  *    Revision 1.3  2001/06/09 10:55:28  jongfoster
67  *    Changing BUFSIZ ==> BUFFER_SIZE
68  *
69  *    Revision 1.2  2001/06/07 23:11:38  jongfoster
70  *    Removing gateways[] list - no longer used.
71  *    Replacing function pointer in struct gateway with a directly
72  *    called function forwarded_connect(), which can do the common
73  *    task of deciding whether to connect to the web server or HTTP
74  *    proxy.
75  *    Replacing struct gateway with struct forward_spec
76  *    Fixing bug with SOCKS4A and HTTP proxy server in combination.
77  *    It was a bug which led to the connection being made to the web
78  *    server rather than the HTTP proxy, and also a buffer overrun.
79  *
80  *    Revision 1.1.1.1  2001/05/15 13:58:54  oes
81  *    Initial import of version 2.9.3 source tree
82  *
83  *
84  *********************************************************************/
85 \f
86
87 #include "config.h"
88
89 #include <stdio.h>
90 #include <sys/types.h>
91
92 #ifndef _WIN32
93 #include <netinet/in.h>
94 #endif
95
96 #include <errno.h>
97 #include <string.h>
98
99 #ifdef _WIN32
100 #include <winsock2.h>
101 #endif /* def _WIN32 */
102
103 #ifdef __BEOS__
104 #include <netdb.h>
105 #endif /* def __BEOS__ */
106
107 #ifdef __OS2__
108 #include <utils.h>
109 #endif /* def __OS2__ */
110
111 #include "project.h"
112 #include "jcc.h"
113 #include "errlog.h"
114 #include "jbsockets.h"
115 #include "gateway.h"
116
117 const char gateway_h_rcs[] = GATEWAY_H_VERSION;
118
119 static jb_socket socks4_connect(const struct forward_spec * fwd,
120                                 const char * target_host,
121                                 int target_port,
122                                 struct client_state *csp);
123
124
125 #define SOCKS_REQUEST_GRANTED          90
126 #define SOCKS_REQUEST_REJECT           91
127 #define SOCKS_REQUEST_IDENT_FAILED     92
128 #define SOCKS_REQUEST_IDENT_CONFLICT   93
129
130 /* structure of a socks client operation */
131 struct socks_op {
132    unsigned char vn;          /* socks version number */
133    unsigned char cd;          /* command code */
134    unsigned char dstport[2];  /* destination port */
135    unsigned char dstip[4];    /* destination address */
136    unsigned char userid;      /* first byte of userid */
137    /* more bytes of the userid follow, terminated by a NULL */
138 };
139
140 /* structure of a socks server reply */
141 struct socks_reply {
142    unsigned char vn;          /* socks version number */
143    unsigned char cd;          /* command code */
144    unsigned char dstport[2];  /* destination port */
145    unsigned char dstip[4];    /* destination address */
146 };
147
148 static const char socks_userid[] = "anonymous";
149
150
151 /*********************************************************************
152  *
153  * Function    :  forwarded_connect
154  *
155  * Description :  Connect to a specified web server, possibly via
156  *                a HTTP proxy and/or a SOCKS proxy.
157  *
158  * Parameters  :
159  *          1  :  gw = pointer to a gateway structure (such as gw_default)
160  *          2  :  http = the http request and apropos headers
161  *          3  :  csp = Current client state (buffers, headers, etc...)
162  *
163  * Returns     :  -1 => failure, else it is the socket file descriptor.
164  *
165  *********************************************************************/
166 jb_socket forwarded_connect(const struct forward_spec * fwd,
167                             struct http_request *http,
168                             struct client_state *csp)
169 {
170    const char * dest_host;
171    int dest_port;
172
173    /* Figure out if we need to connect to the web server or a HTTP proxy. */
174    if (fwd->forward_host)
175    {
176       /* HTTP proxy */
177       dest_host = fwd->forward_host;
178       dest_port = fwd->forward_port;
179    }
180    else
181    {
182       /* Web server */
183       dest_host = http->host;
184       dest_port = http->port;
185    }
186
187    /* Connect, maybe using a SOCKS proxy */
188    switch (fwd->type)
189    {
190       case SOCKS_NONE:
191          return (connect_to(dest_host, dest_port, csp));
192
193       case SOCKS_4:
194       case SOCKS_4A:
195          return (socks4_connect(fwd, dest_host, dest_port, csp));
196
197       default:
198          /* Should never get here */
199          log_error(LOG_LEVEL_FATAL, "SOCKS4 impossible internal error - bad SOCKS type.");
200          errno = EINVAL;
201          return(JB_INVALID_SOCKET);
202    }
203 }
204
205
206 /*********************************************************************
207  *
208  * Function    :  socks4_connect
209  *
210  * Description :  Connect to the SOCKS server, and connect through
211  *                it to the specified server.   This handles
212  *                all the SOCKS negotiation, and returns a file
213  *                descriptor for a socket which can be treated as a
214  *                normal (non-SOCKS) socket.
215  *
216  * Parameters  :
217  *          1  :  gw = pointer to a gateway structure (such as gw_default)
218  *          2  :  http = the http request and apropos headers
219  *          3  :  csp = Current client state (buffers, headers, etc...)
220  *
221  * Returns     :  -1 => failure, else a socket file descriptor.
222  *
223  *********************************************************************/
224 static jb_socket socks4_connect(const struct forward_spec * fwd,
225                                 const char * target_host,
226                                 int target_port,
227                                 struct client_state *csp)
228 {
229    int web_server_addr;
230    char cbuf[BUFFER_SIZE];
231    char sbuf[BUFFER_SIZE];
232    struct socks_op    *c = (struct socks_op    *)cbuf;
233    struct socks_reply *s = (struct socks_reply *)sbuf;
234    size_t n;
235    size_t csiz;
236    jb_socket sfd;
237    int err = 0;
238    char *errstr;
239
240    if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
241    {
242       log_error(LOG_LEVEL_CONNECT, "socks4_connect: NULL gateway host specified");
243       err = 1;
244    }
245
246    if (fwd->gateway_port <= 0)
247    {
248       log_error(LOG_LEVEL_CONNECT, "socks4_connect: invalid gateway port specified");
249       err = 1;
250    }
251
252    if (err)
253    {
254       errno = EINVAL;
255       return(JB_INVALID_SOCKET);
256    }
257
258    /* build a socks request for connection to the web server */
259
260    strcpy((char *)&(c->userid), socks_userid);
261
262    csiz = sizeof(*c) + sizeof(socks_userid) - 1;
263
264    switch (fwd->type)
265    {
266       case SOCKS_4:
267          web_server_addr = htonl(resolve_hostname_to_ip(target_host));
268          if (web_server_addr == INADDR_NONE)
269          {
270             log_error(LOG_LEVEL_CONNECT, "socks4_connect: could not resolve target host %s", target_host);
271             return(JB_INVALID_SOCKET);
272          }
273          break;
274       case SOCKS_4A:
275          web_server_addr = 0x00000001;
276          n = csiz + strlen(target_host) + 1;
277          if (n > sizeof(cbuf))
278          {
279             errno = EINVAL;
280             return(JB_INVALID_SOCKET);
281          }
282          strcpy(cbuf + csiz, target_host);
283          csiz = n;
284          break;
285       default:
286          /* Should never get here */
287          log_error(LOG_LEVEL_FATAL, "SOCKS4 impossible internal error - bad SOCKS type.");
288          errno = EINVAL;
289          return(JB_INVALID_SOCKET);
290    }
291
292    c->vn          = 4;
293    c->cd          = 1;
294    c->dstport[0]  = (target_port       >> 8  ) & 0xff;
295    c->dstport[1]  = (target_port             ) & 0xff;
296    c->dstip[0]    = (web_server_addr   >> 24 ) & 0xff;
297    c->dstip[1]    = (web_server_addr   >> 16 ) & 0xff;
298    c->dstip[2]    = (web_server_addr   >>  8 ) & 0xff;
299    c->dstip[3]    = (web_server_addr         ) & 0xff;
300
301    /* pass the request to the socks server */
302    sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
303
304    if (sfd == JB_INVALID_SOCKET)
305    {
306       return(JB_INVALID_SOCKET);
307    }
308
309    if (write_socket(sfd, (char *)c, (int)csiz))
310    {
311       log_error(LOG_LEVEL_CONNECT, "SOCKS4 negotiation write failed...");
312       close_socket(sfd);
313       return(JB_INVALID_SOCKET);
314    }
315
316    if (read_socket(sfd, sbuf, sizeof(sbuf)) != sizeof(*s))
317    {
318       log_error(LOG_LEVEL_CONNECT, "SOCKS4 negotiation read failed...");
319       close_socket(sfd);
320       return(JB_INVALID_SOCKET);
321    }
322
323    switch (s->cd)
324    {
325       case SOCKS_REQUEST_GRANTED:
326          return(sfd);
327          break;
328       case SOCKS_REQUEST_REJECT:
329          errstr = "SOCKS request rejected or failed";
330          errno = EINVAL;
331          break;
332       case SOCKS_REQUEST_IDENT_FAILED:
333          errstr = "SOCKS request rejected because "
334             "SOCKS server cannot connect to identd on the client";
335          errno = EACCES;
336          break;
337       case SOCKS_REQUEST_IDENT_CONFLICT:
338          errstr = "SOCKS request rejected because "
339             "the client program and identd report "
340             "different user-ids";
341          errno = EACCES;
342          break;
343       default:
344          errstr = cbuf;
345          errno = ENOENT;
346          sprintf(errstr,
347                  "SOCKS request rejected for reason code %d\n", s->cd);
348    }
349
350    log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s ...", errstr);
351
352    close_socket(sfd);
353    return(JB_INVALID_SOCKET);
354
355 }
356
357
358 /*
359   Local Variables:
360   tab-width: 3
361   end:
362 */