1 /*
2 * ffproxy (c) 2002-2004 Niklas Olmes <niklas@noxa.de>
3 * http://faith.eu.org
4 *
5 * $Id: request.c,v 2.1 2004/12/31 08:59:15 niklas Exp $
6 *
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 675
19 * Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "configure.h"
23 #ifdef HAVE_SYS_TYPES_H
24 # include <sys/types.h>
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #ifdef HAVE_STDLIB_H
31 # include <stdlib.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36
37 #include <ctype.h>
38
39 #include "req.h"
40 #include "cfg.h"
41 #include "msg.h"
42 #include "alloc.h"
43 #include "print.h"
44 #include "http.h"
45 #include "filter.h"
46 #include "poll.h"
47 #include "request.h"
48 #include "configure.h"
49
50 static int read_header(int, struct req *);
51 static char sgetc(int);
52 static size_t get_line(int, char[], int);
53 static int do_request(int, struct req *);
54
55 void
handle_request(int cl,struct clinfo * clinfo)56 handle_request(int cl, struct clinfo * clinfo)
57 {
58 extern struct cfg config;
59 struct req r;
60 char buf[2048];
61
62 keep_alive:
63 (void) memset(&r, 0, sizeof(r));
64 r.cl = clinfo;
65
66 if (get_line(cl, buf, sizeof(buf)) < 1)
67 *buf = '\0';
68
69 if ((http_url(&r, buf)) == 0) {
70 int i;
71
72 r.loop = 0;
73
74 if (r.type == CONNECT) {
75 DEBUG(("handle_request => CONNECT request detected"));
76 if (read_header(cl, &r) != 0) {
77 info("invalid CONNECT header from (%s) [%s]",
78 clinfo->name, clinfo->ip);
79 err_msg(cl, &r, E_INV);
80 } else if (r.port != 443 && ! config.unr_con) {
81 info("invalid CONNECT port (%d) for host (%s) from (%s) [%s]",
82 r.port, r.host, clinfo->name, clinfo->ip);
83 err_msg(cl, &r, E_INV);
84 } else if (filter_request(&r) != 0) {
85 info("filtered CONNECT request for (%s:%d) from (%s) [%s]",
86 r.host, r.port, clinfo->name, clinfo->ip);
87 } else {
88 if (config.logrequests) {
89 if (r.port == 443)
90 info("HTTPS CONNECT to (%s) from (%s) [%s]",
91 r.host, clinfo->name, clinfo->ip);
92 else
93 info("CONNECT to host (%s:%d) from (%s) [%s]",
94 r.host, r.port, clinfo->ip);
95 }
96 i = do_request(cl, &r);
97 switch (i) {
98 case E_INV:
99 info("invalid CONNECT request for (%s:%d) from (%s) [%s]", r.host, r.port, clinfo->name, clinfo->ip);
100 break;
101 case E_RES:
102 info("resolve failure for host (%s) from (%s) [%s]", r.host, clinfo->name, clinfo->ip);
103 break;
104 case E_CON:
105 info("connection failure for host (%s) from (%s) [%s]", r.host, clinfo->name, clinfo->ip);
106 break;
107 default:
108 i = 0;
109 }
110 if (i != 0) {
111 err_msg(cl, &r, i);
112 r.kalive = 0;
113 }
114 }
115 i = 0;
116 while (r.header[i] != NULL)
117 free(r.header[i++]);
118 r.header[0] = NULL;
119 } else if (read_header(cl, &r) != 0) {
120 info("invalid header from (%s) [%s]", clinfo->name, clinfo->ip);
121 err_msg(cl, &r, E_INV);
122
123 i = 0;
124 while (r.header[i] != NULL)
125 free(r.header[i++]);
126 r.header[0] = NULL;
127 } else if (filter_request(&r) != 0) {
128 info("filtered request for URL (%s) from (%s) [%s]", r.url, clinfo->name, clinfo->ip);
129 if (r.loop)
130 warn("LOOP DETECTED for URL (%s) from (%s) [%s]", r.url, clinfo->name, clinfo->ip);
131 else
132 err_msg(cl, &r, E_FIL);
133
134 i = 0;
135 while (r.header[i] != NULL)
136 free(r.header[i++]);
137 r.header[0] = NULL;
138 } else {
139 if (config.logrequests)
140 info("request for URL (%s) from (%s) [%s]", r.url, clinfo->name, clinfo->ip);
141
142 i = do_request(cl, &r);
143 switch (i) {
144 case E_INV:
145 info("invalid request for URL (%s) from (%s) [%s]", r.url, clinfo->name, clinfo->ip);
146 break;
147 case E_RES:
148 info("resolve failure for host (%s) from (%s) [%s]", r.host, clinfo->name, clinfo->ip);
149 break;
150 case E_CON:
151 info("connection failure for host (%s) from (%s) [%s]", r.host, clinfo->name, clinfo->ip);
152 break;
153 case E_POST:
154 info("failure while post for URL (%s) from (%s) [%s]", r.url, clinfo->name, clinfo->ip);
155 break;
156 case E_FIL:
157 info("filtered request for URL (%s) from (%s) [%s]", r.url, clinfo->name, clinfo->ip);
158 break;
159 default:
160 i = 0;
161 }
162 if (i != 0) {
163 err_msg(cl, &r, i);
164 r.kalive = 0;
165 }
166
167 i = 0;
168 while (r.header[i] != NULL)
169 free(r.header[i++]);
170 r.header[0] = NULL;
171
172 if (config.kalive && r.kalive && r.clen > 0L)
173 goto keep_alive;
174 }
175 } else {
176 if (*buf == '\0') {
177 ;
178 } else {
179 info("invalid request from (%s) [%s]", clinfo->name, clinfo->ip);
180 }
181 }
182 }
183
184 static int
read_header(int cl,struct req * r)185 read_header(int cl, struct req * r)
186 {
187 size_t len, i;
188 char buf[2048];
189 char *b, *p;
190
191 i = 0;
192 while ((len = get_line(cl, buf, sizeof(buf))) > 0 && i < sizeof(r->header) - 1) {
193 b = buf;
194 while (isspace((int) *b) && *(b++) != '\0');
195 if (*b == '\0')
196 continue;
197
198 p = (char *) my_alloc(len + 1);
199 (void) strcpy(p, b);
200 r->header[i] = p;
201
202 DEBUG(("read_header() => entry %d (%s)", i, r->header[i]));
203
204 i++;
205
206 if (r->relative && http_rel(r, p) != 0) {
207 r->header[i] = NULL;
208 return 1;
209 }
210 }
211 r->header[i] = NULL;
212
213 if (i >= sizeof(r->header) - 1)
214 return 1;
215
216 return 0;
217 }
218
219 static char
sgetc(int s)220 sgetc(int s)
221 {
222 char c;
223
224 if (read(s, &c, 1) != 1 || c < 1)
225 return -1;
226 else
227 return c;
228 }
229
230 static size_t
get_line(int s,char buf[],int len)231 get_line(int s, char buf[], int len)
232 {
233 int c;
234 size_t i;
235
236 if (my_poll(s, IN) <= 0)
237 return 0;
238
239 i = 0;
240 while (--len > 0) {
241 c = sgetc(s);
242 if (c == '\n' || c == -1)
243 break;
244 else if (c != '\r')
245 buf[i++] = c;
246 }
247 buf[i] = '\0';
248
249 return i;
250 }
251
252 #ifdef HAVE_SYS_TYPES_H
253 # include <sys/types.h>
254 #endif
255 #ifdef HAVE_SYS_SOCKET_H
256 # include <sys/socket.h>
257 #endif
258 #ifdef HAVE_SYS_SELECT_H
259 # include <sys/select.h>
260 #endif
261 #ifdef HAVE_SYS_TIME_H
262 # include <sys/time.h>
263 #endif
264 #ifdef HAVE_NETINET_IN_H
265 # include <netinet/in.h>
266 #endif
267 #ifdef HAVE_ARPA_INET_H
268 # include <arpa/inet.h>
269 #endif
270
271 #ifdef HAVE_NETDB_H
272 # include <netdb.h>
273 #endif
274 #include <stdio.h>
275 #ifdef HAVE_STDLIB_H
276 # include <stdlib.h>
277 #endif
278 #include <string.h>
279 #ifdef HAVE_UNISTD_H
280 # include <unistd.h>
281 #endif
282
283 #include "dns.h"
284
285 static int
do_request(int cl,struct req * r)286 do_request(int cl, struct req * r)
287 {
288 extern struct cfg config;
289 unsigned long ip;
290 int s;
291 void *foo;
292 size_t len, i;
293 char buf[4096];
294
295 len = 0;
296 ip = 0L;
297 s = 0;
298
299 if (config.use_ipv6 && (config.aux_proxy_ipv6 || *config.proxyhost == '\0')) {
300 struct addrinfo hints, *res, *res0;
301 char port[6];
302
303 DEBUG(("do_request() => trying ipv6"));
304
305 port[0] = '\0';
306 (void) memset(&hints, 0, sizeof(hints));
307 hints.ai_family = PF_UNSPEC;
308 hints.ai_socktype = SOCK_STREAM;
309
310 if (*config.proxyhost != '\0' && config.proxyport) {
311 DEBUG(("do_request() => trying ipv6 for proxy %s port %d", config.proxyhost, config.proxyport));
312 (void) snprintf(port, 6, "%d", config.proxyport);
313 if (getaddrinfo(config.proxyhost, port, &hints, &res)) {
314 DEBUG(("do_request() => getaddrinfo() failed for proxy %s", config.proxyhost));
315 return E_RES;
316 }
317 } else {
318 (void) snprintf(port, 6, "%d", r->port);
319 if (getaddrinfo(r->host, port, &hints, &res)) {
320 DEBUG(("do_request() => getaddrinfo() failed for %s", r->host));
321 return E_RES;
322 }
323 }
324
325 s = -1;
326 for (res0 = res; res; res = res->ai_next) {
327 if ((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
328 continue;
329 else if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
330 (void) close(s);
331 s = -1;
332 continue;
333 } else
334 break;
335 }
336 freeaddrinfo(res0);
337
338 if (s == -1) {
339 if (*config.proxyhost != '\0' && config.proxyport) {
340 DEBUG(("do_request() => socket() or connect() after getaddrinfo() failed for proxy %s port %d", config.proxyhost, config.proxyport));
341 } else {
342 DEBUG(("do_request() => socket() or connect() after getaddrinfo() failed for %s port %d", r->host, r->port));
343 }
344 return E_CON;
345 }
346 } else {
347 struct sockaddr_in addr;
348
349 DEBUG(("do_request() => not trying ipv6"));
350
351 (void) memset(&addr, 0, sizeof(addr));
352
353 if (*config.proxyhost != '\0' && config.proxyport) {
354 DEBUG(("do_request() => using aux proxy w/o trying ipv6"));
355 if ((addr.sin_addr.s_addr = resolve(config.proxyhost)) == INADDR_NONE) {
356 DEBUG(("do_request() => resolve failure for proxy %s", config.proxyhost));
357 return E_RES;
358 }
359 addr.sin_port = htons(config.proxyport);
360 addr.sin_family = AF_INET;
361 } else {
362 if ((ip = resolve(r->host)) == INADDR_NONE) {
363 DEBUG(("do_request() => resolve failure for %s", r->host));
364 return E_RES;
365 }
366 addr.sin_addr.s_addr = ip;
367 addr.sin_port = htons(r->port);
368 addr.sin_family = AF_INET;
369 }
370
371 if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
372 DEBUG(("do_request() => socket() failed for %s port %d", r->host, r->port));
373 return E_CON;
374 } else if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &foo, sizeof(foo)) != 0) {
375 DEBUG(("do_request() => setsockopt() failed for %s port %d", r->host, r->port));
376 return E_CON;
377 } else if (connect(s, (struct sockaddr *) & addr, sizeof(addr)) == -1) {
378 DEBUG(("do_request() => connect() failed for %s port %d", r->host, r->port));
379 return E_CON;
380 }
381 }
382
383 #ifdef USE_DEBUG
384 i = 0;
385 DEBUG(("do_request() => header is:"));
386 while (r->header[i] != NULL)
387 DEBUG(("=> [%s]", r->header[i++]));
388 #endif
389
390 if (r->vmajor >= 1 && r->vminor >= 0)
391 r->vmajor = 1, r->vminor = 0;
392
393 if (config.accel && config.accelusrhost)
394 len = snprintf(buf, sizeof(buf),
395 "%s %s HTTP/%d.%d\r\n",
396 ((r->type == GET) ? "GET"
397 : ((r->type) == HEAD) ? "HEAD" : "POST"),
398 (*config.proxyhost && config.proxyport) != '\0' ? r->url : r->urlpath,
399 r->vmajor, r->vminor);
400 else if (r->port == 80)
401 len = snprintf(buf, sizeof(buf),
402 "%s %s HTTP/%d.%d\r\n"
403 "Host: %s\r\n",
404 ((r->type == GET) ? "GET"
405 : ((r->type) == HEAD) ? "HEAD" : "POST"),
406 (*config.proxyhost && config.proxyport) != '\0' ? r->url : r->urlpath,
407 r->vmajor, r->vminor,
408 r->host);
409 else if (r->port == 443 || r->type == CONNECT) {
410 *buf = '\0';
411 len = 0;
412 } else
413 len = snprintf(buf, sizeof(buf),
414 "%s %s HTTP/%d.%d\r\n"
415 "Host: %s:%d\r\n",
416 ((r->type == GET) ? "GET"
417 : ((r->type) == HEAD) ? "HEAD" : "POST"),
418 (*config.proxyhost && config.proxyport) != '\0' ? r->url : r->urlpath,
419 r->vmajor, r->vminor,
420 r->host, r->port);
421
422 if (r->type != CONNECT) {
423 i = 0;
424 while (r->header[i] != NULL) {
425 len += strlen(r->header[i]) + strlen("\r\n");
426 if (len < sizeof(buf)) {
427 (void) strncat(buf, r->header[i++], len);
428 (void) strncat(buf, "\r\n", strlen("\r\n"));
429 } else {
430 DEBUG(("do_request() => header too big"));
431 (void) close(s);
432 i = 0;
433 while (r->header[i] != NULL)
434 free(r->header[i++]);
435 r->header[0] = NULL;
436 return E_INV;
437 }
438 }
439 }
440 i = 0;
441 while (r->header[i] != NULL)
442 free(r->header[i++]);
443 r->header[0] = NULL;
444
445 if (r->type != CONNECT) {
446 len += strlen("\r\n");
447 if (len >= sizeof(buf) - 1) {
448 DEBUG(("do_request() => header too big"));
449 (void) close(s);
450 return E_INV;
451 }
452 (void) strncat(buf, "\r\n", strlen("\r\n"));
453
454 DEBUG(("do_request() => request ready: type %d url (%s) host (%s) port %d",
455 r->type, r->url, r->host, r->port));
456 DEBUG(("=> version maj %d min %d", r->vmajor, r->vminor));
457 DEBUG(("=> header: (%s)", buf));
458
459 if (my_poll(s, OUT) <= 0 || write(s, buf, len) < 1) {
460 DEBUG(("do_request() => sending request failed"));
461 (void) close(s);
462 return E_CON;
463 }
464 }
465 if (r->type == POST) {
466 long rest;
467
468 DEBUG(("do_request() => posting data"));
469
470 if ((rest = r->clen) < 0L) {
471 DEBUG(("do_request() => post: invalid clen %ld", r->clen));
472 (void) close(s);
473 return E_POST;
474 }
475 while (rest > 0L) {
476 if (my_poll(cl, OUT) <= 0) {
477 (void) close(s);
478 return E_POST;
479 }
480 len = read(cl, buf, sizeof(buf));
481 if (len < 1)
482 break;
483 else
484 rest -= len;
485
486 if (my_poll(s, OUT) <= 0 || write(s, buf, len) < 1) {
487 DEBUG(("do_request() => post: error writing post data"));
488 (void) close(s);
489 return E_POST;
490 }
491 }
492 DEBUG(("do_request() => post done"));
493 }
494 if (r->type != CONNECT) {
495 i = 0;
496 while ((len = get_line(s, buf, sizeof(buf))) > 0 && i < sizeof(r->header) - 1) {
497 DEBUG(("do_request() => got remote header line: (%s)", buf));
498 r->header[i] = (char *) my_alloc(len + 1);
499 (void) strcpy(r->header[i++], buf);
500 }
501 r->header[i] = NULL;
502
503 if (len > 0) {
504 DEBUG(("do_request() => remote header too big"));
505 (void) close(s);
506 i = 0;
507 while (r->header[i] != NULL)
508 free(r->header[i++]);
509 r->header[0] = NULL;
510 return E_FIL;
511 }
512 if (filter_remote(r) != 0) {
513 DEBUG(("do_request() => response was filtered"));
514 (void) close(s);
515 i = 0;
516 while (r->header[i] != NULL)
517 free(r->header[i++]);
518 r->header[0] = NULL;
519 return E_FIL;
520 }
521 *buf = '\0';
522 len = 0;
523 i = 0;
524 while (r->header[i] != NULL) {
525 len += strlen(r->header[i]) + strlen("\r\n");
526 if (len < sizeof(buf) - 1) {
527 (void) strcat(buf, r->header[i++]);
528 (void) strcat(buf, "\r\n");
529 } else {
530 DEBUG(("do_request() => remote header too big (at concatenation)"));
531 i = 0;
532 while (r->header[i] != NULL)
533 free(r->header[i++]);
534 r->header[0] = NULL;
535 (void) close(s);
536 return E_FIL;
537 }
538 }
539 i = 0;
540 while (r->header[i] != NULL)
541 free(r->header[i++]);
542 r->header[0] = NULL;
543
544 len += strlen("\r\n");
545 if (len >= sizeof(buf) - 1) {
546 DEBUG(("do_request() => remote header too big (at final)"));
547 (void) close(s);
548 return E_FIL;
549 }
550 (void) strcat(buf, "\r\n");
551
552 DEBUG(("do_request() => remote header ready: (%s)", buf));
553
554 if (my_poll(cl, OUT) <= 0 || write(cl, buf, len) < 1) {
555 (void) close(s);
556 return -1;
557 }
558 }
559 if (r->type == CONNECT) {
560 char *con_est = "HTTP/1.0 200 Connection established\r\n\r\n";
561 int max, sel;
562 struct timeval to;
563 fd_set fdset;
564
565 to.tv_sec = config.to_con;
566 to.tv_usec = 0;
567
568 if (write(cl, con_est, strlen(con_est)) < 1)
569 goto c_break;
570
571 if(cl >= s)
572 max = cl + 1;
573 else
574 max = s + 1;
575
576 i = 1;
577 sel = 1;
578 len = 1;
579 while (len > 0 && sel > 0 && i > 0) {
580 FD_ZERO(&fdset);
581 FD_SET(cl, &fdset);
582 FD_SET(s, &fdset);
583 sel = select(max, &fdset, (fd_set*) 0, (fd_set*) 0, &to);
584 if (FD_ISSET(cl, &fdset)) {
585 len = read(cl, buf, sizeof(buf));
586 i = write(s, buf, len);
587 }
588 if (FD_ISSET(s, &fdset)) {
589 len = read(s, buf, sizeof(buf));
590 i = write(cl, buf, len);
591 }
592 }
593 c_break:
594 (void) close(s);
595 return 0;
596 } else if (r->type != HEAD) {
597 while (my_poll(s, IN) > 0 && (len = read(s, buf, sizeof(buf))) > 0) {
598 if (my_poll(cl, OUT) <= 0 || write(cl, buf, len) < 1) {
599 (void) close(s);
600 return -1;
601 }
602 }
603 (void) close(s);
604 return 0;
605 }
606
607 return 0;
608 }
609