1 /*
2  * ffproxy (c) 2002-2004 Niklas Olmes <niklas@noxa.de>
3  * http://faith.eu.org
4  *
5  * $Id: http.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 <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 
26 #include "req.h"
27 #include "print.h"
28 #include "cfg.h"
29 #include "number.h"
30 #include "http.h"
31 
32 #define my_isblank(c) ((c) == ' ' || (c) == '\t')
33 
34 static const char http_get[] = "GET ";
35 static const char http_post[] = "POST ";
36 static const char http_head[] = "HEAD ";
37 static const char http_connect[] = "CONNECT ";
38 static const char http[] = "http://";
39 static const char httpv[] = "HTTP/";
40 
41 int
http_url(struct req * r,const char * s)42 http_url(struct req * r, const char *s)
43 {
44 	extern struct cfg config;
45 	size_t          i, k;
46 	char           *p;
47 
48 	if (strncmp(http_get, s, strlen(http_get)) == 0) {
49 		r->type = GET;
50 		s += strlen(http_get);
51 	} else if (strncmp(http_post, s, strlen(http_post)) == 0) {
52 		r->type = POST;
53 		s += strlen(http_post);
54 	} else if (strncmp(http_head, s, strlen(http_head)) == 0) {
55 		r->type = HEAD;
56 		s += strlen(http_head);
57 	} else if (strncmp(http_connect, s, strlen(http_connect)) == 0) {
58 		r->type = CONNECT;
59 		s += strlen(http_connect);
60 	} else {
61 		r->type = UNKNOWN;
62 		return -1;
63 	}
64 
65 	while (*s == ' ')
66 		s++;
67 
68 	DEBUG(("http_url() => got url part (%s)", s));
69 
70 	i = 0;
71 	if (config.accel) {
72 		r->relative = 0;
73 		DEBUG(("http_url() => using as accelerator proxy"));
74 		DEBUG(("http_url() => accelhost (%s) port %d", config.accelhost, config.accelport));
75 		i = snprintf(r->url, sizeof(r->url), "%s%s:%d", http, config.accelhost, config.accelport);
76 		if (i < 1)
77 			fatal_n("http_url() => accelhost is too long, can't create r->url");
78 		DEBUG(("http_url() => created url (%s) length (%d)", r->url, i));
79 	} else if (strncmp(s, http, strlen(http)) != 0) {
80 		r->relative = 1;
81 	} else {
82 		r->relative = 0;
83 
84 		while (i < strlen(http)) {
85 			r->url[i] = http[i];
86 			i++, s++;
87 		}
88 
89 		while (i < sizeof(r->url) - 1 && *s != '_'
90 		       && (isalnum((int) *s) || *s == '-' || *s == '.' || *s == ':'))
91 			r->url[i++] = tolower((int) *(s++));
92 		r->url[i] = '\0';
93 		if (*s != '/' && *s != ' ') {
94 			r->type = UNKNOWN;
95 			return -1;
96 		}
97 	}
98 
99 	if (config.accel && strncmp(s, http, strlen(http)) == 0) {
100 		r->url[i++] = '\0';
101 		s += strlen(http);
102 		while (*s != ' ' && *s != '/' && *s != '\0' && isprint((int) *s))
103 			s++;
104 	}
105 	k = 0;
106 	while (i < sizeof(r->url) - 1 && k < sizeof(r->urlpath) - 1 && *s != ' ' && *s != '\0' && isprint((int) *s)) {
107 		r->urlpath[k++] = *s;
108 		r->url[i++] = *(s++);
109 	}
110 	if (k == 0)
111 		r->urlpath[k++] = '/';
112 	r->urlpath[k] = '\0';
113 	r->url[i] = '\0';
114 	if (*s != ' ') {
115 		r->type = UNKNOWN;
116 		return -1;
117 	}
118 
119 	if (r->type == CONNECT)
120 		*r->url = '\0';
121 
122 	DEBUG(("http_url() => extracted urlpath (%s)", r->urlpath));
123 	DEBUG(("http_url() => extracted url (%s)", r->url));
124 
125 	while (*s == ' ')
126 		s++;
127 
128 	DEBUG(("http_url() => got version part (%s)", s));
129 
130 	if (strncasecmp(s, httpv, strlen(httpv)) != 0)
131 		return -1;
132 	s += strlen(httpv);
133 	r->vmajor = 0;
134 	r->vminor = 0;
135 	i = 0;
136 	while (i < 2 && *s != '\0' && isdigit((int) *s)) {
137 		r->vmajor = r->vmajor * 10 + my_ctoi(*(s++));
138 		i++;
139 	}
140 	if (*s == '.') {
141 		s++;
142 		while (i < 4 && *s != '\0' && isdigit((int) *s)) {
143 			r->vminor = r->vminor * 10 + my_ctoi(*(s++));
144 			i++;
145 		}
146 	}
147 	DEBUG(("http_url() => got type %d url (%s) version maj %d min %d",
148 	      r->type, r->url, r->vmajor, r->vminor));
149 
150 	p = r->url;
151 	p += strlen(http);
152 
153 	if(r->relative || r->type == CONNECT)
154 		return 0;
155 
156 	i = 0;
157 	while ((isalnum((int) *p) || *p == '-' || *p == '.') && i < sizeof(r->host) - 1)
158 		r->host[i++] = tolower((int) *(p++));
159 	r->host[i] = '\0';
160 
161 	if (i >= sizeof(r->host) - 1) {
162 		DEBUG(("http_url() => host: too long (%s)", r->host));
163 		*r->host = '\0';
164 		r->port = 0;
165 		return -1;
166 	}
167 	if (*p == ':') {
168 		p++;
169 		r->port = 0;
170 		while (isdigit((int) *p)) {
171 			r->port = r->port * 10 + my_ctoi(*(p++));
172 			if (r->port >= 65534) {
173 				DEBUG(("http_url() => port: bad port number"));
174 				r->port = 0;
175 				return -1;
176 			}
177 		}
178 		if (*p != '\0' && *p != ' ' && *p != '/') {
179 			DEBUG(("http_url() => port: bad port"));
180 			r->port = 0;
181 			return -1;
182 		}
183 		DEBUG(("http_url() => port: %d", r->port));
184 	} else {
185 		DEBUG(("http_url() => default port 80"));
186 		r->port = 80;
187 	}
188 
189 	return 0;
190 }
191 
192 static const char h_host[] = "Host: ";
193 
194 int
http_rel(struct req * r,const char * s)195 http_rel(struct req * r, const char *s)
196 {
197 	size_t i;
198 
199 	i = 0;
200 	if (r->relative && strncmp(s, h_host, strlen(h_host)) == 0) {
201 		r->relative = 0;
202 
203 		s += strlen(h_host);
204 		while (*s == ' ')
205 			s++;
206 		while ((isalnum((int) *s) || *s == '-' || *s == '.') && i < sizeof(r->host) - 1)
207 			r->host[i++] = tolower((int) *(s++));
208 		r->host[i] = '\0';
209 		if (i >= sizeof(r->host) || (*s != ':' && *s != '\0')) {
210 			DEBUG(("http_rel() => invalid host header (%s)", r->host));
211 			r->host[0] = '\0';
212 			return 1;
213 		}
214 		DEBUG(("http_rel() => extracted host (%s)", r->host));
215 		if (*s == ':') {
216 			i = 0;
217 			r->port = 0;
218 			s++;
219 			while (isdigit((int) *s) && i++ < 6)
220 				r->port = r->port * 10 + my_ctoi(*(s++));
221 			if (i > 5 && i == 0) {
222 				DEBUG(("http_rel() => bad port number"));
223 				r->port = 0;
224 				return 1;
225 			}
226 			DEBUG(("http_rel() => extracted port %d", r->port));
227 		} else {
228 			if (r->type == CONNECT)
229 				r->port = 443;
230 			else
231 				r->port = 80;
232 		}
233 		if (strlen(r->url) + strlen(http) + strlen(r->host) + 7 >= sizeof(r->url)) {
234 			DEBUG(("http_rel() => URL will get too long"));
235 			return 1;
236 		} else {
237 			char o_url[sizeof(r->url)];
238 			(void) strncpy(o_url, r->url, sizeof(o_url) - 1);
239 			o_url[sizeof(o_url) - 1] = '\0';
240 			if (r->port == 80)
241 				(void) snprintf(r->url, sizeof(r->url), "http://%s%s", r->host, o_url);
242 			else
243 				(void) snprintf(r->url, sizeof(r->url), "http://%s:%d%s", r->host, r->port, o_url);
244 			DEBUG(("http_rel() => extracted URL (%s)", r->url));
245 		}
246 	}
247 
248 	return 0;
249 }
250 
251 static const char http_clen[] = "Content-Length: ";
252 static const char http_pkalive[] = "Proxy-Connection: keep-alive";
253 static const char http_kalive[] = "Connection: keep-alive";
254 #ifdef TSTAMP
255 static const char http_tstamp[] = "Last-Modified: ";
256 #endif
257 
258 int
http_parse(struct req * r,const char * s)259 http_parse(struct req * r, const char *s)
260 {
261 	size_t          i;
262 
263 	if (strncasecmp(http_clen, s, strlen(http_clen)) == 0) {
264 		DEBUG(("http_parse() => found clen header (%s)", s));
265 
266 		s += strlen(http_clen);
267 
268 		while (my_isblank(*s))
269 			s++;
270 
271 		if (!isdigit((int) *s)) {
272 			DEBUG(("http_parse() => clen: no digit found (%s)", s));
273 			return -1;
274 		}
275 		r->clen = 0L;
276 		i = 0;
277 		while (i < 10 && isdigit((int) *s))
278 			r->clen = r->clen * 10L + (long) my_ctoi(*(s++));
279 
280 		if (*s != '\0') {
281 			r->clen = 0L;
282 			DEBUG(("http_parse() => clen: too long"));
283 			return -1;
284 		}
285 		DEBUG(("http_parse() => clen: %ld bytes", r->clen));
286 		return 0;
287 #ifdef TSTAMP
288 	} else if (strncasecmp(http_tstamp, s, strlen(http_tstamp)) == 0) {
289 		DEBUG(("http_parse() => found tstamp header (%s)", s));
290 
291 		s += strlen(http_tstamp);
292 
293 		while (my_isblank(*s))
294 			s++;
295 
296 		i = 0;
297 		while (i < sizeof(r->tstamp) - 1 && *s != '\0')
298 			r->tstamp[i++] = *(s++);
299 		r->tstamp[i] = '\0';
300 
301 		if (*s != '\0') {
302 			r->tstamp[0] = '\0';
303 			DEBUG(("http_parse()_ => tstamp: too long"));
304 			return -1;
305 		}
306 		DEBUG(("http_parse() => tstamp: extracted (%s)", r->tstamp));
307 		return 0;
308 #endif
309 	} else if (strncasecmp(http_pkalive, s, strlen(http_pkalive)) == 0
310 		|| strncasecmp(http_kalive, s, strlen(http_kalive)) == 0) {
311 		DEBUG(("http_parse() => keep alive header found"));
312 		r->kalive = 1;
313 		return 1;
314 	}
315 	return 1;
316 }
317