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