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