1 /*
2 Copyright (c) 2003-2006 by Juliusz Chroboczek
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23 #include "polipo.h"
24
25 int disableProxy = 0;
26 AtomPtr proxyName = NULL;
27 int proxyPort = 8123;
28
29 int clientTimeout = 120;
30 int serverTimeout = 90;
31 int serverIdleTimeout = 45;
32
33 int bigBufferSize = (32 * 1024);
34
35 AtomPtr displayName = NULL;
36
37 AtomPtr authRealm = NULL;
38 AtomPtr authCredentials = NULL;
39
40 AtomPtr parentAuthCredentials = NULL;
41
42 AtomListPtr allowedClients = NULL;
43 NetAddressPtr allowedNets = NULL;
44
45 IntListPtr allowedPorts = NULL;
46 IntListPtr tunnelAllowedPorts = NULL;
47 int expectContinue = 1;
48 int dontTrustVaryETag = 1;
49
50 AtomPtr atom100Continue;
51
52 int disableVia = 1;
53
54 /* 0 means that all failures lead to errors. 1 means that failures to
55 connect are reported in a Warning header when stale objects are
56 served. 2 means that only missing data is fetched from the net,
57 stale data is served without revalidation (browser-side
58 Cache-Control directives are still honoured). 3 means that no
59 connections are ever attempted. */
60
61 int proxyOffline = 0;
62 int relaxTransparency = 0;
63 AtomPtr proxyAddress = NULL;
64
65 static int timeoutSetter(ConfigVariablePtr var, void *value);
66
67 void
preinitHttp()68 preinitHttp()
69 {
70 proxyAddress = internAtom("127.0.0.1");
71 CONFIG_VARIABLE_SETTABLE(disableProxy, CONFIG_BOOLEAN, configIntSetter,
72 "Whether to be a web server only.");
73 CONFIG_VARIABLE_SETTABLE(proxyOffline, CONFIG_BOOLEAN, configIntSetter,
74 "Avoid contacting remote servers.");
75 CONFIG_VARIABLE_SETTABLE(relaxTransparency, CONFIG_TRISTATE,
76 configIntSetter,
77 "Avoid contacting remote servers.");
78 CONFIG_VARIABLE(proxyPort, CONFIG_INT,
79 "The TCP port on which the proxy listens.");
80 CONFIG_VARIABLE(proxyAddress, CONFIG_ATOM_LOWER,
81 "The IP address on which the proxy listens.");
82 CONFIG_VARIABLE_SETTABLE(proxyName, CONFIG_ATOM_LOWER, configAtomSetter,
83 "The name by which the proxy is known.");
84 CONFIG_VARIABLE_SETTABLE(clientTimeout, CONFIG_TIME,
85 timeoutSetter, "Client-side timeout.");
86 CONFIG_VARIABLE_SETTABLE(serverTimeout, CONFIG_TIME,
87 timeoutSetter, "Server-side timeout.");
88 CONFIG_VARIABLE_SETTABLE(serverIdleTimeout, CONFIG_TIME,
89 timeoutSetter, "Server-side idle timeout.");
90 CONFIG_VARIABLE(authRealm, CONFIG_ATOM,
91 "Authentication realm.");
92 CONFIG_VARIABLE(displayName, CONFIG_ATOM,
93 "Server name displayed on error pages.");
94 CONFIG_VARIABLE(authCredentials, CONFIG_PASSWORD,
95 "username:password.");
96 CONFIG_VARIABLE(parentAuthCredentials, CONFIG_PASSWORD,
97 "username:password.");
98 CONFIG_VARIABLE(allowedClients, CONFIG_ATOM_LIST_LOWER,
99 "Networks from which clients are allowed to connect.");
100 CONFIG_VARIABLE(tunnelAllowedPorts, CONFIG_INT_LIST,
101 "Ports to which tunnelled connections are allowed.");
102 CONFIG_VARIABLE(allowedPorts, CONFIG_INT_LIST,
103 "Ports to which connections are allowed.");
104 CONFIG_VARIABLE(expectContinue, CONFIG_TRISTATE,
105 "Send Expect-Continue to servers.");
106 CONFIG_VARIABLE(bigBufferSize, CONFIG_INT,
107 "Size of big buffers (max size of headers).");
108 CONFIG_VARIABLE_SETTABLE(disableVia, CONFIG_BOOLEAN, configIntSetter,
109 "Don't use Via headers.");
110 CONFIG_VARIABLE(dontTrustVaryETag, CONFIG_TRISTATE,
111 "Whether to trust the ETag when there's Vary.");
112 preinitHttpParser();
113 }
114
115 static int
timeoutSetter(ConfigVariablePtr var,void * value)116 timeoutSetter(ConfigVariablePtr var, void *value)
117 {
118 configIntSetter(var, value);
119 if(clientTimeout <= serverTimeout)
120 clientTimeout = serverTimeout + 1;
121 return 1;
122 }
123
124 void
initHttp()125 initHttp()
126 {
127 char *buf = NULL;
128 int namelen;
129 int n;
130 struct hostent *host;
131
132 initHttpParser();
133
134 atom100Continue = internAtom("100-continue");
135
136 if(clientTimeout <= serverTimeout) {
137 clientTimeout = serverTimeout + 1;
138 do_log(L_WARN, "Value of clientTimeout too small -- setting to %d.\n",
139 clientTimeout);
140 }
141
142 if(displayName == NULL)
143 displayName = internAtom("Polipo");
144
145 if(authCredentials != NULL && authRealm == NULL)
146 authRealm = internAtom("Polipo");
147
148 if(allowedClients) {
149 allowedNets = parseNetAddress(allowedClients);
150 if(allowedNets == NULL)
151 exit(1);
152 }
153
154 if(allowedPorts == NULL) {
155 allowedPorts = makeIntList(0);
156 if(allowedPorts == NULL) {
157 do_log(L_ERROR, "Couldn't allocate allowedPorts.\n");
158 exit(1);
159 }
160 intListCons(80, 100, allowedPorts);
161 intListCons(1024, 0xFFFF, allowedPorts);
162 }
163
164 if(tunnelAllowedPorts == NULL) {
165 tunnelAllowedPorts = makeIntList(0);
166 if(tunnelAllowedPorts == NULL) {
167 do_log(L_ERROR, "Couldn't allocate tunnelAllowedPorts.\n");
168 exit(1);
169 }
170 intListCons(22, 22, tunnelAllowedPorts); /* ssh */
171 intListCons(80, 80, tunnelAllowedPorts); /* HTTP */
172 intListCons(109, 110, tunnelAllowedPorts); /* POP 2 and 3*/
173 intListCons(143, 143, tunnelAllowedPorts); /* IMAP 2/4 */
174 intListCons(443, 443, tunnelAllowedPorts); /* HTTP/SSL */
175 intListCons(873, 873, tunnelAllowedPorts); /* rsync */
176 intListCons(993, 993, tunnelAllowedPorts); /* IMAP/SSL */
177 intListCons(995, 995, tunnelAllowedPorts); /* POP/SSL */
178 intListCons(2401, 2401, tunnelAllowedPorts); /* CVS */
179 intListCons(5222, 5223, tunnelAllowedPorts); /* Jabber */
180 intListCons(9418, 9418, tunnelAllowedPorts); /* Git */
181 }
182
183 if(proxyName)
184 return;
185
186 buf = get_chunk();
187 if(buf == NULL) {
188 do_log(L_ERROR, "Couldn't allocate chunk for host name.\n");
189 goto fail;
190 }
191
192 n = gethostname(buf, CHUNK_SIZE);
193 if(n != 0) {
194 do_log_error(L_WARN, errno, "Gethostname");
195 strcpy(buf, "polipo");
196 goto success;
197 }
198 /* gethostname doesn't necessarily NUL-terminate on overflow */
199 buf[CHUNK_SIZE - 1] = '\0';
200
201 if(strcmp(buf, "(none)") == 0 ||
202 strcmp(buf, "localhost") == 0 ||
203 strcmp(buf, "localhost.localdomain") == 0) {
204 do_log(L_WARN, "Couldn't determine host name -- using ``polipo''.\n");
205 strcpy(buf, "polipo");
206 goto success;
207 }
208
209 if(strchr(buf, '.') != NULL)
210 goto success;
211
212 host = gethostbyname(buf);
213 if(host == NULL) {
214 goto success;
215 }
216
217 if(host->h_addrtype != AF_INET)
218 goto success;
219
220 host = gethostbyaddr(host->h_addr_list[0], host->h_length, AF_INET);
221
222 if(!host || !host->h_name || strcmp(host->h_name, "localhost") == 0 ||
223 strcmp(host->h_name, "localhost.localdomain") == 0)
224 goto success;
225
226 namelen = strlen(host->h_name);
227 if(namelen >= CHUNK_SIZE) {
228 do_log(L_ERROR, "Host name too long.\n");
229 goto success;
230 }
231
232 memcpy(buf, host->h_name, namelen + 1);
233
234 success:
235 proxyName = internAtom(buf);
236 if(proxyName == NULL) {
237 do_log(L_ERROR, "Couldn't allocate proxy name.\n");
238 goto fail;
239 }
240 dispose_chunk(buf);
241 return;
242
243 fail:
244 if(buf)
245 dispose_chunk(buf);
246 exit(1);
247 return;
248 }
249
250 int
httpSetTimeout(HTTPConnectionPtr connection,int secs)251 httpSetTimeout(HTTPConnectionPtr connection, int secs)
252 {
253 TimeEventHandlerPtr new;
254
255 if(connection->timeout)
256 cancelTimeEvent(connection->timeout);
257 connection->timeout = NULL;
258
259 if(secs > 0) {
260 new = scheduleTimeEvent(secs, httpTimeoutHandler,
261 sizeof(connection), &connection);
262 if(!new) {
263 do_log(L_ERROR, "Couldn't schedule timeout for connection 0x%lx\n",
264 (unsigned long)connection);
265 return -1;
266 }
267 } else {
268 new = NULL;
269 }
270
271 connection->timeout = new;
272 return 1;
273 }
274
275 int
httpTimeoutHandler(TimeEventHandlerPtr event)276 httpTimeoutHandler(TimeEventHandlerPtr event)
277 {
278 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
279
280 if(connection->fd >= 0) {
281 int rc;
282 rc = shutdown(connection->fd, 2);
283 if(rc < 0 && errno != ENOTCONN)
284 do_log_error(L_ERROR, errno, "Timeout: shutdown failed");
285 pokeFdEvent(connection->fd, -EDOTIMEOUT, POLLIN | POLLOUT);
286 }
287 connection->timeout = NULL;
288 return 1;
289 }
290
291 int
httpWriteObjectHeaders(char * buf,int offset,int len,ObjectPtr object,int from,int to)292 httpWriteObjectHeaders(char *buf, int offset, int len,
293 ObjectPtr object, int from, int to)
294 {
295 int n = offset;
296 CacheControlRec cache_control;
297
298 cache_control.flags = object->cache_control;
299 cache_control.max_age = object->max_age;
300 cache_control.s_maxage = object->s_maxage;
301 cache_control.max_stale = -1;
302 cache_control.min_fresh = -1;
303
304 if(from <= 0 && to < 0) {
305 if(object->length >= 0) {
306 n = snnprintf(buf, n, len,
307 "\r\nContent-Length: %d", object->length);
308 }
309 } else {
310 if(to >= 0) {
311 n = snnprintf(buf, n, len,
312 "\r\nContent-Length: %d", to - from);
313 }
314 }
315
316 if(from > 0 || to > 0) {
317 if(object->length >= 0) {
318 if(from >= to) {
319 n = snnprintf(buf, n, len,
320 "\r\nContent-Range: bytes */%d",
321 object->length);
322 } else {
323 n = snnprintf(buf, n, len,
324 "\r\nContent-Range: bytes %d-%d/%d",
325 from, to - 1,
326 object->length);
327 }
328 } else {
329 if(to >= 0) {
330 n = snnprintf(buf, n, len,
331 "\r\nContent-Range: bytes %d-/*",
332 from);
333 } else {
334 n = snnprintf(buf, n, len,
335 "\r\nContent-Range: bytes %d-%d/*",
336 from, to);
337 }
338 }
339 }
340
341 if(object->etag) {
342 n = snnprintf(buf, n, len, "\r\nETag: \"%s\"", object->etag);
343 }
344 if((object->flags & OBJECT_LOCAL) || object->date >= 0) {
345 n = snnprintf(buf, n, len, "\r\nDate: ");
346 n = format_time(buf, n, len,
347 (object->flags & OBJECT_LOCAL) ?
348 current_time.tv_sec : object->date);
349 if(n < 0)
350 goto fail;
351 }
352
353 if(object->last_modified >= 0) {
354 n = snnprintf(buf, n, len, "\r\nLast-Modified: ");
355 n = format_time(buf, n, len, object->last_modified);
356 if(n < 0)
357 goto fail;
358 }
359
360 if(object->expires >= 0) {
361 n = snnprintf(buf, n, len, "\r\nExpires: ");
362 n = format_time(buf, n, len, object->expires);
363 if(n < 0)
364 goto fail;
365 }
366
367 n = httpPrintCacheControl(buf, n, len,
368 object->cache_control, &cache_control);
369 if(n < 0)
370 goto fail;
371
372 if(!disableVia && object->via)
373 n = snnprintf(buf, n, len, "\r\nVia: %s", object->via->string);
374
375 if(object->headers)
376 n = snnprint_n(buf, n, len, object->headers->string,
377 object->headers->length);
378
379 if(n < len)
380 return n;
381 else
382 return -1;
383
384 fail:
385 return -1;
386 }
387
388 static int
cachePrintSeparator(char * buf,int offset,int len,int subsequent)389 cachePrintSeparator(char *buf, int offset, int len,
390 int subsequent)
391 {
392 int n = offset;
393 if(subsequent)
394 n = snnprintf(buf, offset, len, ", ");
395 else
396 n = snnprintf(buf, offset, len, "\r\nCache-Control: ");
397 return n;
398 }
399
400 int
httpPrintCacheControl(char * buf,int offset,int len,int flags,CacheControlPtr cache_control)401 httpPrintCacheControl(char *buf, int offset, int len,
402 int flags, CacheControlPtr cache_control)
403 {
404 int n = offset;
405 int sub = 0;
406
407 #define PRINT_SEP() \
408 do {\
409 n = cachePrintSeparator(buf, n, len, sub); \
410 sub = 1; \
411 } while(0)
412
413 if(cache_control)
414 flags |= cache_control->flags;
415
416 if(flags & CACHE_NO) {
417 PRINT_SEP();
418 n = snnprintf(buf, n, len, "no-cache");
419 }
420 if(flags & CACHE_PUBLIC) {
421 PRINT_SEP();
422 n = snnprintf(buf, n, len, "public");
423 }
424 if(flags & CACHE_PRIVATE) {
425 PRINT_SEP();
426 n = snnprintf(buf, n, len, "private");
427 }
428 if(flags & CACHE_NO_STORE) {
429 PRINT_SEP();
430 n = snnprintf(buf, n, len, "no-store");
431 }
432 if(flags & CACHE_NO_TRANSFORM) {
433 PRINT_SEP();
434 n = snnprintf(buf, n, len, "no-transform");
435 }
436 if(flags & CACHE_MUST_REVALIDATE) {
437 PRINT_SEP();
438 n = snnprintf(buf, n, len, "must-revalidate");
439 }
440 if(flags & CACHE_PROXY_REVALIDATE) {
441 PRINT_SEP();
442 n = snnprintf(buf, n, len, "proxy-revalidate");
443 }
444 if(flags & CACHE_ONLY_IF_CACHED) {
445 PRINT_SEP();
446 n = snnprintf(buf, n, len, "only-if-cached");
447 }
448 if(cache_control) {
449 if(cache_control->max_age >= 0) {
450 PRINT_SEP();
451 n = snnprintf(buf, n, len, "max-age=%d",
452 cache_control->max_age);
453 }
454 if(cache_control->s_maxage >= 0) {
455 PRINT_SEP();
456 n = snnprintf(buf, n, len, "s-maxage=%d",
457 cache_control->s_maxage);
458 }
459 if(cache_control->min_fresh > 0) {
460 PRINT_SEP();
461 n = snnprintf(buf, n, len, "min-fresh=%d",
462 cache_control->min_fresh);
463 }
464 if(cache_control->max_stale > 0) {
465 PRINT_SEP();
466 n = snnprintf(buf, n, len, "max-stale=%d",
467 cache_control->min_fresh);
468 }
469 }
470 return n;
471 #undef PRINT_SEP
472 }
473
474 char *
httpMessage(int code)475 httpMessage(int code)
476 {
477 switch(code) {
478 case 200:
479 return "Okay";
480 case 206:
481 return "Partial content";
482 case 300:
483 return "Multiple choices";
484 case 301:
485 return "Moved permanently";
486 case 302:
487 return "Found";
488 case 303:
489 return "See other";
490 case 304:
491 return "Not changed";
492 case 307:
493 return "Temporary redirect";
494 case 401:
495 return "Authentication Required";
496 case 403:
497 return "Forbidden";
498 case 404:
499 return "Not found";
500 case 405:
501 return "Method not allowed";
502 case 407:
503 return "Proxy authentication required";
504 default:
505 return "Unknown error code";
506 }
507 }
508
509 int
htmlString(char * buf,int n,int len,char * s,int slen)510 htmlString(char *buf, int n, int len, char *s, int slen)
511 {
512 int i = 0;
513 while(i < slen && n + 5 < len) {
514 switch(s[i]) {
515 case '&':
516 buf[n++] = '&'; buf[n++] = 'a'; buf[n++] = 'm'; buf[n++] = 'p';
517 buf[n++] = ';';
518 break;
519 case '<':
520 buf[n++] = '&'; buf[n++] = 'l'; buf[n++] = 't'; buf[n++] = ';';
521 break;
522 case '>':
523 buf[n++] = '&'; buf[n++] = 'g'; buf[n++] = 't'; buf[n++] = ';';
524 break;
525 case '"':
526 buf[n++] = '&'; buf[n++] = 'q'; buf[n++] = 'u'; buf[n++] = 'o';
527 buf[n++] = 't'; buf[n++] = ';';
528 break;
529 case '\0':
530 break;
531 default:
532 buf[n++] = s[i];
533 }
534 i++;
535 }
536 return n;
537 }
538
539 void
htmlPrint(FILE * out,char * s,int slen)540 htmlPrint(FILE *out, char *s, int slen)
541 {
542 int i;
543 for(i = 0; i < slen; i++) {
544 switch(s[i]) {
545 case '&':
546 fputs("&", out);
547 break;
548 case '<':
549 fputs("<", out);
550 break;
551 case '>':
552 fputs(">", out);
553 break;
554 default:
555 fputc(s[i], out);
556 }
557 }
558 }
559
560 HTTPConnectionPtr
httpMakeConnection()561 httpMakeConnection()
562 {
563 HTTPConnectionPtr connection;
564 connection = malloc(sizeof(HTTPConnectionRec));
565 if(connection == NULL)
566 return NULL;
567 connection->flags = 0;
568 connection->fd = -1;
569 connection->buf = NULL;
570 connection->len = 0;
571 connection->offset = 0;
572 connection->request = NULL;
573 connection->request_last = NULL;
574 connection->serviced = 0;
575 connection->version = HTTP_UNKNOWN;
576 connection->time = current_time.tv_sec;
577 connection->timeout = NULL;
578 connection->te = TE_IDENTITY;
579 connection->reqbuf = NULL;
580 connection->reqlen = 0;
581 connection->reqbegin = 0;
582 connection->reqoffset = 0;
583 connection->bodylen = -1;
584 connection->reqte = TE_IDENTITY;
585 connection->chunk_remaining = 0;
586 connection->server = NULL;
587 connection->pipelined = 0;
588 connection->connecting = 0;
589 connection->server = NULL;
590 return connection;
591 }
592
593 void
httpDestroyConnection(HTTPConnectionPtr connection)594 httpDestroyConnection(HTTPConnectionPtr connection)
595 {
596 assert(connection->flags == 0);
597 httpConnectionDestroyBuf(connection);
598 assert(!connection->request);
599 assert(!connection->request_last);
600 httpConnectionDestroyReqbuf(connection);
601 assert(!connection->timeout);
602 assert(!connection->server);
603 free(connection);
604 }
605
606 void
httpConnectionDestroyBuf(HTTPConnectionPtr connection)607 httpConnectionDestroyBuf(HTTPConnectionPtr connection)
608 {
609 if(connection->buf) {
610 if(connection->flags & CONN_BIGBUF)
611 free(connection->buf);
612 else
613 dispose_chunk(connection->buf);
614 }
615 connection->flags &= ~CONN_BIGBUF;
616 connection->buf = NULL;
617 }
618
619 void
httpConnectionDestroyReqbuf(HTTPConnectionPtr connection)620 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection)
621 {
622 if(connection->reqbuf) {
623 if(connection->flags & CONN_BIGREQBUF)
624 free(connection->reqbuf);
625 else
626 dispose_chunk(connection->reqbuf);
627 }
628 connection->flags &= ~CONN_BIGREQBUF;
629 connection->reqbuf = NULL;
630 }
631
632 HTTPRequestPtr
httpMakeRequest()633 httpMakeRequest()
634 {
635 HTTPRequestPtr request;
636 request = malloc(sizeof(HTTPRequestRec));
637 if(request == NULL)
638 return NULL;
639 request->flags = 0;
640 request->connection = NULL;
641 request->object = NULL;
642 request->method = METHOD_UNKNOWN;
643 request->from = 0;
644 request->to = -1;
645 request->cache_control = no_cache_control;
646 request->condition = NULL;
647 request->via = NULL;
648 request->chandler = NULL;
649 request->can_mutate = NULL;
650 request->error_code = 0;
651 request->error_message = NULL;
652 request->error_headers = NULL;
653 request->headers = NULL;
654 request->time0 = null_time;
655 request->time1 = null_time;
656 request->request = NULL;
657 request->next = NULL;
658 return request;
659 }
660
661 void
httpDestroyRequest(HTTPRequestPtr request)662 httpDestroyRequest(HTTPRequestPtr request)
663 {
664 if(request->object)
665 releaseObject(request->object);
666 if(request->condition)
667 httpDestroyCondition(request->condition);
668 releaseAtom(request->via);
669 assert(request->chandler == NULL);
670 releaseAtom(request->error_message);
671 releaseAtom(request->headers);
672 releaseAtom(request->error_headers);
673 assert(request->request == NULL);
674 assert(request->next == NULL);
675 free(request);
676 }
677
678 void
httpQueueRequest(HTTPConnectionPtr connection,HTTPRequestPtr request)679 httpQueueRequest(HTTPConnectionPtr connection, HTTPRequestPtr request)
680 {
681 assert(request->next == NULL && request->connection == NULL);
682 request->connection = connection;
683 if(connection->request_last) {
684 assert(connection->request);
685 connection->request_last->next = request;
686 connection->request_last = request;
687 } else {
688 assert(!connection->request_last);
689 connection->request = request;
690 connection->request_last = request;
691 }
692 }
693
694 HTTPRequestPtr
httpDequeueRequest(HTTPConnectionPtr connection)695 httpDequeueRequest(HTTPConnectionPtr connection)
696 {
697 HTTPRequestPtr request = connection->request;
698 if(request) {
699 assert(connection->request_last);
700 connection->request = request->next;
701 if(!connection->request) connection->request_last = NULL;
702 request->next = NULL;
703 }
704 return request;
705 }
706
707 int
httpConnectionBigify(HTTPConnectionPtr connection)708 httpConnectionBigify(HTTPConnectionPtr connection)
709 {
710 char *bigbuf;
711 assert(!(connection->flags & CONN_BIGBUF));
712
713 if(bigBufferSize <= CHUNK_SIZE)
714 return 0;
715
716 bigbuf = malloc(bigBufferSize);
717 if(bigbuf == NULL)
718 return -1;
719 if(connection->len > 0)
720 memcpy(bigbuf, connection->buf, connection->len);
721 if(connection->buf)
722 dispose_chunk(connection->buf);
723 connection->buf = bigbuf;
724 connection->flags |= CONN_BIGBUF;
725 return 1;
726 }
727
728 int
httpConnectionBigifyReqbuf(HTTPConnectionPtr connection)729 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection)
730 {
731 char *bigbuf;
732 assert(!(connection->flags & CONN_BIGREQBUF));
733
734 if(bigBufferSize <= CHUNK_SIZE)
735 return 0;
736
737 bigbuf = malloc(bigBufferSize);
738 if(bigbuf == NULL)
739 return -1;
740 if(connection->reqlen > 0)
741 memcpy(bigbuf, connection->reqbuf, connection->reqlen);
742 if(connection->reqbuf)
743 dispose_chunk(connection->reqbuf);
744 connection->reqbuf = bigbuf;
745 connection->flags |= CONN_BIGREQBUF;
746 return 1;
747 }
748
749 int
httpConnectionUnbigify(HTTPConnectionPtr connection)750 httpConnectionUnbigify(HTTPConnectionPtr connection)
751 {
752 char *buf;
753 assert(connection->flags & CONN_BIGBUF);
754 assert(connection->len < CHUNK_SIZE);
755
756 buf = get_chunk();
757 if(buf == NULL)
758 return -1;
759 if(connection->len > 0)
760 memcpy(buf, connection->buf, connection->len);
761 free(connection->buf);
762 connection->buf = buf;
763 connection->flags &= ~CONN_BIGBUF;
764 return 1;
765 }
766
767 int
httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection)768 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection)
769 {
770 char *buf;
771 assert(connection->flags & CONN_BIGREQBUF);
772 assert(connection->reqlen < CHUNK_SIZE);
773
774 buf = get_chunk();
775 if(buf == NULL)
776 return -1;
777 if(connection->reqlen > 0)
778 memcpy(buf, connection->reqbuf, connection->reqlen);
779 free(connection->reqbuf);
780 connection->reqbuf = buf;
781 connection->flags &= ~CONN_BIGREQBUF;
782 return 1;
783 }
784
785 HTTPConditionPtr
httpMakeCondition()786 httpMakeCondition()
787 {
788 HTTPConditionPtr condition;
789 condition = malloc(sizeof(HTTPConditionRec));
790 if(condition == NULL)
791 return NULL;
792 condition->ims = -1;
793 condition->inms = -1;
794 condition->im = NULL;
795 condition->inm = NULL;
796 condition->ifrange = NULL;
797 return condition;
798 }
799
800 void
httpDestroyCondition(HTTPConditionPtr condition)801 httpDestroyCondition(HTTPConditionPtr condition)
802 {
803 if(condition->inm)
804 free(condition->inm);
805 if(condition->im)
806 free(condition->im);
807 if(condition->ifrange)
808 free(condition->ifrange);
809 free(condition);
810 }
811
812 int
httpCondition(ObjectPtr object,HTTPConditionPtr condition)813 httpCondition(ObjectPtr object, HTTPConditionPtr condition)
814 {
815 int rc = CONDITION_MATCH;
816
817 assert(!(object->flags & OBJECT_INITIAL));
818
819 if(!condition) return CONDITION_MATCH;
820
821 if(condition->ims >= 0) {
822 if(object->last_modified < 0 ||
823 condition->ims < object->last_modified)
824 return rc;
825 else
826 rc = CONDITION_NOT_MODIFIED;
827 }
828
829 if(condition->inms >= 0) {
830 if(object->last_modified < 0 ||
831 condition->inms >= object->last_modified)
832 return rc;
833 else
834 rc = CONDITION_FAILED;
835 }
836
837 if(condition->inm) {
838 if(!object->etag || strcmp(object->etag, condition->inm) != 0)
839 return rc;
840 else
841 rc = CONDITION_NOT_MODIFIED;
842 }
843
844 if(condition->im) {
845 if(!object->etag || strcmp(object->etag, condition->im) != 0)
846 rc = CONDITION_FAILED;
847 else
848 return rc;
849 }
850
851 return rc;
852 }
853
854 int
httpWriteErrorHeaders(char * buf,int size,int offset,int do_body,int code,AtomPtr message,int close,AtomPtr headers,char * url,int url_len,char * etag)855 httpWriteErrorHeaders(char *buf, int size, int offset, int do_body,
856 int code, AtomPtr message, int close, AtomPtr headers,
857 char *url, int url_len, char *etag)
858 {
859 int n, m, i;
860 char *body;
861 char htmlMessage[100];
862 char timeStr[100];
863
864 assert(code != 0);
865
866 i = htmlString(htmlMessage, 0, 100, message->string, message->length);
867 if(i < 0)
868 strcpy(htmlMessage, "(Couldn't format message)");
869 else
870 htmlMessage[MIN(i, 99)] = '\0';
871
872 if(code != 304) {
873 body = get_chunk();
874 if(!body) {
875 do_log(L_ERROR, "Couldn't allocate body buffer.\n");
876 return -1;
877 }
878 m = snnprintf(body, 0, CHUNK_SIZE,
879 "<!DOCTYPE HTML PUBLIC "
880 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
881 "\"http://www.w3.org/TR/html4/loose.dtd\">"
882 "\n<html><head>"
883 "\n<title>Proxy %s: %3d %s.</title>"
884 "\n</head><body>"
885 "\n<h1>%3d %s</h1>"
886 "\n<p>The following %s",
887 code >= 400 ? "error" : "result",
888 code, htmlMessage,
889 code, htmlMessage,
890 code >= 400 ?
891 "error occurred" :
892 "status was returned");
893 if(url_len > 0) {
894 m = snnprintf(body, m, CHUNK_SIZE,
895 " while trying to access <strong>");
896 m = htmlString(body, m, CHUNK_SIZE, url, url_len);
897 m = snnprintf(body, m, CHUNK_SIZE, "</strong>");
898 }
899
900 {
901 /* On BSD systems, tv_sec is a long. */
902 const time_t ct = current_time.tv_sec;
903 /*Mon, 24 Sep 2004 17:46:35 GMT*/
904 strftime(timeStr, sizeof(timeStr), "%a, %d %b %Y %H:%M:%S %Z",
905 localtime(&ct));
906 }
907
908 m = snnprintf(body, m, CHUNK_SIZE,
909 ":<br><br>"
910 "\n<strong>%3d %s</strong></p>"
911 "\n<hr>Generated %s by %s on <em>%s:%d</em>."
912 "\n</body></html>\r\n",
913 code, htmlMessage,
914 timeStr, displayName->string, proxyName->string, proxyPort);
915 if(m <= 0 || m >= CHUNK_SIZE) {
916 do_log(L_ERROR, "Couldn't write error body.\n");
917 dispose_chunk(body);
918 return -1;
919 }
920 } else {
921 body = NULL;
922 m = 0;
923 }
924
925 n = snnprintf(buf, 0, size,
926 "HTTP/1.1 %3d %s"
927 "\r\nConnection: %s"
928 "\r\nDate: ",
929 code, atomString(message),
930 close ? "close" : "keep-alive");
931 n = format_time(buf, n, size, current_time.tv_sec);
932 if(code != 304) {
933 n = snnprintf(buf, n, size,
934 "\r\nContent-Type: text/html"
935 "\r\nContent-Length: %d", m);
936 } else {
937 if(etag)
938 n = snnprintf(buf, n, size, "\r\nETag: \"%s\"", etag);
939 }
940
941 if(code != 304 && code != 412) {
942 n = snnprintf(buf, n, size,
943 "\r\nExpires: 0"
944 "\r\nCache-Control: no-cache"
945 "\r\nPragma: no-cache");
946 }
947
948 if(headers)
949 n = snnprint_n(buf, n, size,
950 headers->string, headers->length);
951
952 n = snnprintf(buf, n, size, "\r\n\r\n");
953
954 if(n < 0 || n >= size) {
955 do_log(L_ERROR, "Couldn't write error.\n");
956 dispose_chunk(body);
957 return -1;
958 }
959
960 if(code != 304 && do_body) {
961 if(m > 0) memcpy(buf + n, body, m);
962 n += m;
963 }
964
965 if(body)
966 dispose_chunk(body);
967
968 return n;
969 }
970
971 AtomListPtr
urlDecode(char * buf,int n)972 urlDecode(char *buf, int n)
973 {
974 char mybuf[500];
975 int i, j = 0;
976 AtomListPtr list;
977 AtomPtr atom;
978
979 list = makeAtomList(NULL, 0);
980 if(list == NULL)
981 return NULL;
982
983 i = 0;
984 while(i < n) {
985 if(buf[i] == '%') {
986 int a, b;
987 if(i + 3 > n)
988 goto fail;
989 a = h2i(buf[i + 1]);
990 b = h2i(buf[i + 2]);
991 if(a < 0 || b < 0)
992 goto fail;
993 mybuf[j++] = (char)((a << 4) | b);
994 i += 3;
995 if(j >= 500) goto fail;
996 } else if(buf[i] == '&') {
997 atom = internAtomN(mybuf, j);
998 if(atom == NULL)
999 goto fail;
1000 atomListCons(atom, list);
1001 j = 0;
1002 i++;
1003 } else {
1004 mybuf[j++] = buf[i++];
1005 if(j >= 500) goto fail;
1006 }
1007 }
1008
1009 atom = internAtomN(mybuf, j);
1010 if(atom == NULL)
1011 goto fail;
1012 atomListCons(atom, list);
1013 return list;
1014
1015 fail:
1016 destroyAtomList(list);
1017 return NULL;
1018 }
1019
1020 void
httpTweakCachability(ObjectPtr object)1021 httpTweakCachability(ObjectPtr object)
1022 {
1023 int code = object->code;
1024
1025 if((object->cache_control & CACHE_AUTHORIZATION) &&
1026 !(object->cache_control & CACHE_PUBLIC)) {
1027 object->cache_control |= CACHE_NO_HIDDEN;
1028 object->flags |= OBJECT_LINEAR;
1029 }
1030
1031 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
1032 manically avoid caching replies that we don't know how to
1033 handle, even if Expires or Cache-Control says otherwise. As to
1034 known uncacheable replies, we obey Cache-Control and default to
1035 allowing sharing but not caching. */
1036 if(code != 200 && code != 206 &&
1037 code != 300 && code != 301 && code != 302 && code != 303 &&
1038 code != 304 && code != 307 &&
1039 code != 403 && code != 404 && code != 405 && code != 416) {
1040 object->cache_control |= (CACHE_NO_HIDDEN | CACHE_MISMATCH);
1041 object->flags |= OBJECT_LINEAR;
1042 } else if(code != 200 && code != 206 &&
1043 code != 300 && code != 301 && code != 304 &&
1044 code != 410) {
1045 if(object->expires < 0 && !(object->cache_control & CACHE_PUBLIC)) {
1046 object->cache_control |= CACHE_NO_HIDDEN;
1047 }
1048 } else if(dontCacheRedirects && (code == 301 || code == 302)) {
1049 object->cache_control |= CACHE_NO_HIDDEN;
1050 }
1051
1052 if(urlIsUncachable(object->key, object->key_size)) {
1053 object->cache_control |= CACHE_NO_HIDDEN;
1054 }
1055
1056 if((object->cache_control & CACHE_NO_STORE) != 0) {
1057 object->cache_control |= CACHE_NO_HIDDEN;
1058 }
1059
1060 if(object->cache_control & CACHE_VARY) {
1061 if(!object->etag || dontTrustVaryETag >= 2) {
1062 object->cache_control |= CACHE_MISMATCH;
1063 }
1064 }
1065 }
1066
1067 int
httpHeaderMatch(AtomPtr header,AtomPtr headers1,AtomPtr headers2)1068 httpHeaderMatch(AtomPtr header, AtomPtr headers1, AtomPtr headers2)
1069 {
1070 int rc1, b1, e1, rc2, b2, e2;
1071
1072 /* Short cut if both sets of headers are identical */
1073 if(headers1 == headers2)
1074 return 1;
1075
1076 rc1 = httpFindHeader(header, headers1->string, headers1->length,
1077 &b1, &e1);
1078 rc2 = httpFindHeader(header, headers2->string, headers2->length,
1079 &b2, &e2);
1080
1081 if(rc1 == 0 && rc2 == 0)
1082 return 1;
1083
1084 if(rc1 == 0 || rc2 == 0)
1085 return 0;
1086
1087 if(e1 - b1 != e2 - b2)
1088 return 0;
1089
1090 if(memcmp(headers1->string + b1, headers2->string + b2, e1 - b1) != 0)
1091 return 0;
1092
1093 return 1;
1094 }
1095