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("&amp;", out);
547             break;
548         case '<':
549             fputs("&lt;", out);
550             break;
551         case '>':
552             fputs("&gt;", 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