Generate 416 errors correctly.
[polipo.git] / util.c
blobfe488216ebd8c46295448b997c079aad212209e7
1 /*
2 Copyright (c) 2003-2007 by Juliusz Chroboczek
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:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
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.
23 #include "polipo.h"
25 /* Note that this is different from GNU's strndup(3). */
26 char *
27 strdup_n(const char *restrict buf, int n)
29 char *s;
30 s = malloc(n + 1);
31 if(s == NULL)
32 return NULL;
33 memcpy(s, buf, n);
34 s[n] = '\0';
35 return s;
38 int
39 snnprintf(char *restrict buf, int n, int len, const char *format, ...)
41 va_list args;
42 int rc;
43 va_start(args, format);
44 rc = snnvprintf(buf, n, len, format, args);
45 va_end(args);
46 return rc;
49 int
50 snnvprintf(char *restrict buf, int n, int len, const char *format, va_list args)
52 int rc = -1;
53 if(n < 0) return -2;
54 if(n < len)
55 rc = vsnprintf(buf + n, len - n, format, args);
56 if(rc >= 0 && n + rc <= len)
57 return n + rc;
58 else
59 return -1;
62 int
63 snnprint_n(char *restrict buf, int n, int len, const char *s, int slen)
65 int i = 0;
66 if(n < 0) return -2;
67 while(i < slen && n < len)
68 buf[n++] = s[i++];
69 if(n < len)
70 return n;
71 else
72 return -1;
75 int
76 strcmp_n(const char *string, const char *buf, int n)
78 int i;
79 i = 0;
80 while(string[i] != '\0' && i < n) {
81 if(string[i] < buf[i])
82 return -1;
83 else if(string[i] > buf[i])
84 return 1;
85 i++;
87 if(string[i] == '\0' || i == n)
88 return 0;
89 else if(i == n)
90 return 1;
91 else
92 return -1;
95 int
96 letter(char c)
98 if(c >= 'A' && c <= 'Z') return 1;
99 if(c >= 'a' && c <= 'z') return 1;
100 return 0;
104 digit(char c)
106 if(c >= '0' && c <= '9')
107 return 1;
108 return 0;
111 char
112 lwr(char a)
114 if(a >= 'A' && a <= 'Z')
115 return a | 0x20;
116 else
117 return a;
120 char *
121 lwrcpy(char *restrict dst, const char *restrict src, int n)
123 int i;
124 for(i = 0; i < n; i++)
125 dst[i] = lwr(src[i]);
126 return dst;
130 lwrcmp(const char *as, const char *bs, int n)
132 int i;
133 for(i = 0; i < n; i++) {
134 char a = lwr(as[i]), b = lwr(bs[i]);
135 if(a < b)
136 return -1;
137 else if(a > b)
138 return 1;
140 return 0;
144 strcasecmp_n(const char *string, const char *buf, int n)
146 int i;
147 i = 0;
148 while(string[i] != '\0' && i < n) {
149 char a = lwr(string[i]), b = lwr(buf[i]);
150 if(a < b)
151 return -1;
152 else if(a > b)
153 return 1;
154 i++;
156 if(string[i] == '\0' && i == n)
157 return 0;
158 else if(i == n)
159 return 1;
160 else
161 return -1;
165 atoi_n(const char *restrict string, int n, int len, int *value_return)
167 int i = n;
168 int val = 0;
170 if(i >= len || !digit(string[i]))
171 return -1;
173 while(i < len && digit(string[i])) {
174 val = val * 10 + (string[i] - '0');
175 i++;
177 *value_return = val;
178 return i;
181 int
182 isWhitespace(const char *string)
184 while(*string != '\0') {
185 if(*string == ' ' || *string == '\t')
186 string++;
187 else
188 return 0;
190 return 1;
193 #ifndef HAVE_MEMRCHR
194 void *
195 memrchr(const void *s, int c, size_t n)
197 const unsigned char *ss = s;
198 unsigned char cc = c;
199 size_t i;
200 for(i = n - 1; i >= 0; i--)
201 if(ss[i] == cc)
202 return (void*)(ss + i);
203 return NULL;
205 #endif
208 h2i(char h)
210 if(h >= '0' && h <= '9')
211 return h - '0';
212 else if(h >= 'a' && h <= 'f')
213 return h - 'a' + 10;
214 else if(h >= 'A' && h <= 'F')
215 return h - 'A' + 10;
216 else
217 return -1;
220 char
221 i2h(int i)
223 if(i < 0 || i >= 16)
224 return '?';
225 if(i < 10)
226 return i + '0';
227 else
228 return i - 10 + 'A';
231 /* floor(log2(x)) */
233 log2_floor(int x)
235 int i, j;
237 assert(x > 0);
239 i = 0;
240 j = 1;
241 while(2 * j <= x) {
242 i++;
243 j *= 2;
245 return i;
248 /* ceil(log2(x)) */
250 log2_ceil(int x)
252 int i, j;
254 assert(x > 0);
256 i = 0;
257 j = 1;
258 while(j < x) {
259 i++;
260 j *= 2;
262 return i;
265 #ifdef HAVE_ASPRINTF
266 char *
267 vsprintf_a(const char *f, va_list args)
269 char *r;
270 int rc;
272 rc = vasprintf(&r, f, args);
273 if(rc < 0)
274 return NULL;
275 return r;
279 #else
281 /* This is not going to work if va_list is interesting. But then, if you
282 have a non-trivial implementation of va_list, you should have va_copy. */
283 #ifndef va_copy
284 #define va_copy(a, b) do { a = b; } while(0)
285 #endif
287 char*
288 vsprintf_a(const char *f, va_list args)
290 int n, size;
291 char buf[64];
292 char *string;
293 va_list args_copy;
295 va_copy(args_copy, args);
296 n = vsnprintf(buf, 64, f, args_copy);
297 if(n >= 0 && n < 64) {
298 return strdup_n(buf, n);
300 if(n >= 64)
301 size = n + 1;
302 else
303 size = 96;
305 while(1) {
306 string = malloc(size);
307 if(!string)
308 return NULL;
309 va_copy(args_copy, args);
310 n = vsnprintf(string, size, f, args_copy);
311 if(n >= 0 && n < size)
312 return string;
313 else if(n >= size)
314 size = n + 1;
315 else
316 size = size * 3 / 2;
317 free(string);
318 if(size > 16 * 1024)
319 return NULL;
321 /* NOTREACHED */
323 #endif
325 char*
326 sprintf_a(const char *f, ...)
328 char *s;
329 va_list args;
330 va_start(args, f);
331 s = vsprintf_a(f, args);
332 va_end(args);
333 return s;
336 unsigned int
337 hash(unsigned int seed, const void *restrict key, int key_size,
338 unsigned int hash_size)
340 int i;
341 unsigned int h;
343 h = seed;
344 for(i = 0; i < key_size; i++)
345 h = (h << 5) + (h >> (hash_size - 5)) +
346 ((unsigned char*)key)[i];
347 return h & ((1 << hash_size) - 1);
350 char *
351 pstrerror(int e)
353 char *s;
354 static char buf[20];
356 switch(e) {
357 case EDOSHUTDOWN: s = "Immediate shutdown requested"; break;
358 case EDOGRACEFUL: s = "Graceful shutdown requested"; break;
359 case EDOTIMEOUT: s = "Timeout"; break;
360 case ECLIENTRESET: s = "Connection reset by client"; break;
361 case ESYNTAX: s = "Incorrect syntax"; break;
362 case EREDIRECTOR: s = "Redirector error"; break;
363 case EDNS_HOST_NOT_FOUND: s = "Host not found"; break;
364 case EDNS_NO_ADDRESS: s = "No address"; break;
365 case EDNS_NO_RECOVERY: s = "Permanent name server failure"; break;
366 case EDNS_TRY_AGAIN: s = "Temporary name server failure"; break;
367 case EDNS_INVALID: s = "Invalid reply from name server"; break;
368 case EDNS_UNSUPPORTED: s = "Unsupported DNS reply"; break;
369 case EDNS_FORMAT: s = "Invalid DNS query"; break;
370 case EDNS_REFUSED: s = "DNS query refused by server"; break;
371 case EDNS_CNAME_LOOP: s = "DNS CNAME loop"; break;
372 #ifndef NO_SOCKS
373 case ESOCKS_PROTOCOL: s = "SOCKS protocol error"; break;
374 case ESOCKS_REJECT_FAIL: s = "SOCKS request rejected or failed"; break;
375 case ESOCKS_REJECT_IDENTD: s = "SOCKS request rejected: "
376 "server couldn't connect to identd";
377 case ESOCKS_REJECT_UID_MISMATCH: s = "SOCKS request rejected: "
378 "uid mismatch";
379 break;
380 case ESOCKS5_BASE: s = "SOCKS success"; break;
381 case ESOCKS5_BASE + 1: s = "General SOCKS server failure"; break;
382 case ESOCKS5_BASE + 2: s = "SOCKS connection not allowed"; break;
383 case ESOCKS5_BASE + 3: s = "SOCKS error: network unreachable"; break;
384 case ESOCKS5_BASE + 4: s = "SOCKS error: host unreachable"; break;
385 case ESOCKS5_BASE + 5: s = "SOCKS error: connection refused"; break;
386 case ESOCKS5_BASE + 6: s = "SOCKS error: TTL expired"; break;
387 case ESOCKS5_BASE + 7: s = "SOCKS command not supported"; break;
388 case ESOCKS5_BASE + 8: s = "SOCKS error: address type not supported";
389 break;
390 #endif
391 case EUNKNOWN: s = "Unknown error"; break;
392 default: s = NULL; break;
394 if(!s) s = strerror(e);
395 #ifdef MINGW
396 if(!s) {
397 if(e >= WSABASEERR && e <= WSABASEERR + 2000) {
398 /* This should be okay, as long as the caller discards the
399 pointer before another error occurs. */
400 snprintf(buf, 20, "Winsock error %d", e);
401 s = buf;
404 #endif
405 if(!s) {
406 snprintf(buf, 20, "Unknown error %d", e);
407 s = buf;
409 return s;
412 /* Like mktime(3), but UTC rather than local time */
413 #if defined(HAVE_TIMEGM)
414 time_t
415 mktime_gmt(struct tm *tm)
417 return timegm(tm);
419 #elif defined(HAVE_TM_GMTOFF)
420 time_t
421 mktime_gmt(struct tm *tm)
423 time_t t;
424 struct tm *ltm;
426 t = mktime(tm);
427 if(t < 0)
428 return -1;
429 ltm = localtime(&t);
430 if(ltm == NULL)
431 return -1;
432 return t + ltm->tm_gmtoff;
434 #elif defined(HAVE_TZSET)
435 #ifdef HAVE_SETENV
436 /* Taken from the Linux timegm(3) man page. */
437 time_t
438 mktime_gmt(struct tm *tm)
440 time_t t;
441 char *tz;
443 tz = getenv("TZ");
444 setenv("TZ", "GMT", 1);
445 tzset();
446 t = mktime(tm);
447 if(tz)
448 setenv("TZ", tz, 1);
449 else
450 unsetenv("TZ");
451 tzset();
452 return t;
454 #else
455 time_t
456 mktime_gmt(struct tm *tm)
458 time_t t;
459 char *tz;
460 static char *old_tz = NULL;
462 tz = getenv("TZ");
463 putenv("TZ=GMT");
464 tzset();
465 t = mktime(tm);
466 if(old_tz)
467 free(old_tz);
468 if(tz)
469 old_tz = sprintf_a("TZ=%s", tz);
470 else
471 old_tz = strdup("TZ"); /* XXX - non-portable? */
472 if(old_tz)
473 putenv(old_tz);
474 tzset();
475 return t;
477 #endif
478 #else
479 #error no mktime_gmt implementation on this platform
480 #endif
483 AtomPtr
484 expandTilde(AtomPtr filename)
486 char *buf;
487 char *home;
488 int len;
489 AtomPtr ret;
491 if(filename == NULL || filename->length < 1 ||
492 filename->string[0] != '~' || filename->string[1] != '/')
493 return filename;
495 home = getenv("HOME");
496 if(home == NULL) {
497 return NULL;
499 len = strlen(home);
500 buf = malloc(len + 1 + 1 + filename->length - 2);
501 if(buf == NULL) {
502 do_log(L_ERROR, "Could not allocate buffer.\n");
503 return NULL;
506 memcpy(buf, home, len);
507 if(buf[len - 1] != '/')
508 buf[len++] = '/';
509 memcpy(buf + len, filename->string + 2, filename->length - 2);
510 len += filename->length - 2;
511 ret = internAtomN(buf, len);
512 free(buf);
513 if(ret != NULL)
514 releaseAtom(filename);
515 return ret;
518 #ifdef HAVE_FORK
519 void
520 do_daemonise(int noclose)
522 int rc;
524 fflush(stdout);
525 fflush(stderr);
527 rc = fork();
528 if(rc < 0) {
529 do_log_error(L_ERROR, errno, "Couldn't fork");
530 exit(1);
533 if(rc > 0)
534 exit(0);
536 if(!noclose) {
537 int fd;
538 close(0);
539 close(1);
540 close(2);
541 /* Leaving the default file descriptors free is not a good
542 idea, as it will cause library functions such as abort to
543 thrash the on-disk cache. */
544 fd = open("/dev/null", O_RDONLY);
545 if(fd > 0) {
546 dup2(fd, 0);
547 close(fd);
549 fd = open("/dev/null", O_WRONLY);
550 if(fd >= 0) {
551 if(fd != 1)
552 dup2(fd, 1);
553 if(fd != 2)
554 dup2(fd, 2);
555 if(fd != 1 && fd != 2)
556 close(fd);
559 rc = setsid();
560 if(rc < 0) {
561 do_log_error(L_ERROR, errno, "Couldn't create new session");
562 exit(1);
566 #else
568 void
569 do_daemonise(int noclose)
571 do_log(L_ERROR, "Cannot daemonise on this platform");
572 exit(1);
574 #endif
577 void
578 writePid(char *pidfile)
580 int fd, n, rc;
581 char buf[16];
583 fd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
584 if(fd < 0) {
585 do_log_error(L_ERROR, errno,
586 "Couldn't create pid file %s", pidfile);
587 exit(1);
589 n = snprintf(buf, 16, "%ld\n", (long)getpid());
590 if(n < 0 || n >= 16) {
591 close(fd);
592 unlink(pidfile);
593 do_log(L_ERROR, "Couldn't format pid.\n");
594 exit(1);
596 rc = write(fd, buf, n);
597 if(rc != n) {
598 close(fd);
599 unlink(pidfile);
600 do_log_error(L_ERROR, errno, "Couldn't write pid");
601 exit(1);
604 close(fd);
605 return;
608 static const char b64[64] =
609 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
611 /* "/" replaced with "-" */
612 static const char b64fss[64] =
613 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
616 b64cpy(char *restrict dst, const char *restrict src, int n, int fss)
618 const char *b = fss ? b64fss: b64;
619 int i, j;
621 j = 0;
622 for(i = 0; i < n; i += 3) {
623 unsigned char a0, a1, a2;
624 a0 = src[i];
625 a1 = i < n - 1 ? src[i + 1] : 0;
626 a2 = i < n - 2 ? src[i + 2] : 0;
627 dst[j++] = b[(a0 >> 2) & 0x3F];
628 dst[j++] = b[((a0 << 4) & 0x30) | ((a1 >> 4) & 0x0F)];
629 if(i < n - 1)
630 dst[j++] = b[((a1 << 2) & 0x3C) | ((a2 >> 6) & 0x03)];
631 else
632 dst[j++] = '=';
633 if(i < n - 2)
634 dst[j++] = b[a2 & 0x3F];
635 else
636 dst[j++] = '=';
638 return j;
642 b64cmp(const char *restrict a, int an, const char *restrict b, int bn)
644 char *buf;
645 int r;
647 if(an % 4 != 0)
648 return -1;
649 if((bn + 2) / 3 != an / 4)
650 return -1;
651 buf = malloc(an);
652 if(buf == NULL)
653 return -1;
654 b64cpy(buf, b, bn, 0);
655 r = memcmp(buf, a, an);
656 free(buf);
657 return r;
660 IntListPtr
661 makeIntList(int size)
663 IntListPtr list;
664 if(size <= 0)
665 size = 4;
667 list = malloc(sizeof(IntListRec));
668 if(list == NULL)
669 return NULL;
671 list->ranges = malloc(size * sizeof(IntRangeRec));
672 if(list->ranges == NULL) {
673 free(list);
674 return NULL;
677 list->length = 0;
678 list->size = size;
679 return list;
682 void
683 destroyIntList(IntListPtr list)
685 free(list->ranges);
686 free(list);
690 intListMember(int n, IntListPtr list)
692 int lo = 0, hi = list->length - 1;
693 int mid;
694 while(hi >= lo) {
695 mid = (hi + lo) / 2;
696 if(list->ranges[mid].from > n)
697 hi = mid - 1;
698 else if(list->ranges[mid].to < n)
699 lo = mid + 1;
700 else
701 return 1;
703 return 0;
706 static int
707 deleteRange(IntListPtr list, int i)
709 assert(list->length > i);
710 if(list->length > i + 1)
711 memmove(list->ranges + i, list->ranges + i + 1,
712 (list->length - i - 1) * sizeof(IntRangeRec));
713 list->length--;
714 return 1;
717 static int
718 insertRange(int from, int to, IntListPtr list, int i)
720 assert(i >= 0 && i <= list->length);
721 assert(i == 0 || list->ranges[i - 1].to < from - 1);
722 assert(i == list->length || list->ranges[i].from > to + 1);
724 if(list->length >= list->size) {
725 int newsize = list->size * 2 + 1;
726 IntRangePtr newranges =
727 realloc(list->ranges, newsize * sizeof(IntRangeRec));
728 if(newranges == NULL)
729 return -1;
730 list->size = newsize;
731 list->ranges = newranges;
734 if(i < list->length)
735 memmove(list->ranges + i + 1, list->ranges + i,
736 list->length - i);
737 list->length++;
738 list->ranges[i].from = from;
739 list->ranges[i].to = to;
740 return 1;
743 static int
744 maybeMergeRanges(IntListPtr list, int i)
746 int rc;
748 while(i > 0 && list->ranges[i].from <= list->ranges[i - 1].to + 1) {
749 list->ranges[i - 1].from =
750 MIN(list->ranges[i - 1].from, list->ranges[i].from);
751 list->ranges[i - 1].to =
752 MAX(list->ranges[i - 1].to, list->ranges[i].to);
753 rc = deleteRange(list, i);
754 if(rc < 0) return -1;
755 i--;
758 while(i < list->length - 1 &&
759 list->ranges[i].to >= list->ranges[i + 1].from - 1) {
760 list->ranges[i + 1].from =
761 MIN(list->ranges[i + 1].from, list->ranges[i].from);
762 list->ranges[i - 1].to =
763 MAX(list->ranges[i + 1].to, list->ranges[i].to);
764 rc = deleteRange(list, i);
765 if(rc < 0) return -1;
767 return 1;
771 intListCons(int from, int to, IntListPtr list)
773 int i;
775 /* Don't bother with the dichotomy. */
776 for(i = 0; i < list->length; i++) {
777 if(list->ranges[i].to >= from - 1)
778 break;
781 if(i < list->length &&
782 (from >= list->ranges[i].from - 1 || to <= list->ranges[i].to + 1)) {
783 if(from <= list->ranges[i].from)
784 list->ranges[i].from = from;
785 if(to >= list->ranges[i].to)
786 list->ranges[i].to = to;
787 return maybeMergeRanges(list, i);
789 return insertRange(from, to, list, i);
792 /* Return the amount of physical memory on the box, -1 if unknown or
793 over two gigs. */
794 #if defined(__linux__)
796 #include <sys/sysinfo.h>
798 physicalMemory()
800 int rc;
801 struct sysinfo info;
803 rc = sysinfo(&info);
804 if(rc < 0)
805 return -1;
807 if(info.totalram <= 0x7fffffff / info.mem_unit)
808 return (int)(info.totalram * info.mem_unit);
810 return -1;
813 #elif defined(__FreeBSD__)
815 #include <sys/sysctl.h>
817 physicalMemory()
819 unsigned long membytes;
820 size_t len;
821 int res;
823 len = sizeof(membytes);
824 res = sysctlbyname("hw.physmem", &membytes, &len, NULL, 0);
825 if (res || membytes > INT_MAX)
826 return -1;
828 return (int)membytes;
831 #else
834 physicalMemory()
836 return -1;
838 #endif