In pstrerror(), add missing break for case ESOCKS_REJECT_IDENTD.
[polipo.git] / util.c
blob87b54a6bba0ff61eb852a2fa28867550efba51d4
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 break;
378 case ESOCKS_REJECT_UID_MISMATCH: s = "SOCKS request rejected: "
379 "uid mismatch";
380 break;
381 case ESOCKS5_BASE: s = "SOCKS success"; break;
382 case ESOCKS5_BASE + 1: s = "General SOCKS server failure"; break;
383 case ESOCKS5_BASE + 2: s = "SOCKS connection not allowed"; break;
384 case ESOCKS5_BASE + 3: s = "SOCKS error: network unreachable"; break;
385 case ESOCKS5_BASE + 4: s = "SOCKS error: host unreachable"; break;
386 case ESOCKS5_BASE + 5: s = "SOCKS error: connection refused"; break;
387 case ESOCKS5_BASE + 6: s = "SOCKS error: TTL expired"; break;
388 case ESOCKS5_BASE + 7: s = "SOCKS command not supported"; break;
389 case ESOCKS5_BASE + 8: s = "SOCKS error: address type not supported";
390 break;
391 #endif
392 case EUNKNOWN: s = "Unknown error"; break;
393 default: s = NULL; break;
395 if(!s) s = strerror(e);
396 #ifdef MINGW
397 if(!s) {
398 if(e >= WSABASEERR && e <= WSABASEERR + 2000) {
399 /* This should be okay, as long as the caller discards the
400 pointer before another error occurs. */
401 snprintf(buf, 20, "Winsock error %d", e);
402 s = buf;
405 #endif
406 if(!s) {
407 snprintf(buf, 20, "Unknown error %d", e);
408 s = buf;
410 return s;
413 /* Like mktime(3), but UTC rather than local time */
414 #if defined(HAVE_TIMEGM)
415 time_t
416 mktime_gmt(struct tm *tm)
418 return timegm(tm);
420 #elif defined(HAVE_TM_GMTOFF)
421 time_t
422 mktime_gmt(struct tm *tm)
424 time_t t;
425 struct tm *ltm;
427 t = mktime(tm);
428 if(t < 0)
429 return -1;
430 ltm = localtime(&t);
431 if(ltm == NULL)
432 return -1;
433 return t + ltm->tm_gmtoff;
435 #elif defined(HAVE_TZSET)
436 #ifdef HAVE_SETENV
437 /* Taken from the Linux timegm(3) man page. */
438 time_t
439 mktime_gmt(struct tm *tm)
441 time_t t;
442 char *tz;
444 tz = getenv("TZ");
445 setenv("TZ", "GMT", 1);
446 tzset();
447 t = mktime(tm);
448 if(tz)
449 setenv("TZ", tz, 1);
450 else
451 unsetenv("TZ");
452 tzset();
453 return t;
455 #else
456 time_t
457 mktime_gmt(struct tm *tm)
459 time_t t;
460 char *tz;
461 static char *old_tz = NULL;
463 tz = getenv("TZ");
464 putenv("TZ=GMT");
465 tzset();
466 t = mktime(tm);
467 if(old_tz)
468 free(old_tz);
469 if(tz)
470 old_tz = sprintf_a("TZ=%s", tz);
471 else
472 old_tz = strdup("TZ"); /* XXX - non-portable? */
473 if(old_tz)
474 putenv(old_tz);
475 tzset();
476 return t;
478 #endif
479 #else
480 #error no mktime_gmt implementation on this platform
481 #endif
484 AtomPtr
485 expandTilde(AtomPtr filename)
487 char *buf;
488 char *home;
489 int len;
490 AtomPtr ret;
492 if(filename == NULL || filename->length < 1 ||
493 filename->string[0] != '~' || filename->string[1] != '/')
494 return filename;
496 home = getenv("HOME");
497 if(home == NULL) {
498 return NULL;
500 len = strlen(home);
501 buf = malloc(len + 1 + 1 + filename->length - 2);
502 if(buf == NULL) {
503 do_log(L_ERROR, "Could not allocate buffer.\n");
504 return NULL;
507 memcpy(buf, home, len);
508 if(buf[len - 1] != '/')
509 buf[len++] = '/';
510 memcpy(buf + len, filename->string + 2, filename->length - 2);
511 len += filename->length - 2;
512 ret = internAtomN(buf, len);
513 free(buf);
514 if(ret != NULL)
515 releaseAtom(filename);
516 return ret;
519 #ifdef HAVE_FORK
520 void
521 do_daemonise(int noclose)
523 int rc;
525 fflush(stdout);
526 fflush(stderr);
528 rc = fork();
529 if(rc < 0) {
530 do_log_error(L_ERROR, errno, "Couldn't fork");
531 exit(1);
534 if(rc > 0)
535 exit(0);
537 if(!noclose) {
538 int fd;
539 close(0);
540 close(1);
541 close(2);
542 /* Leaving the default file descriptors free is not a good
543 idea, as it will cause library functions such as abort to
544 thrash the on-disk cache. */
545 fd = open("/dev/null", O_RDONLY);
546 if(fd > 0) {
547 dup2(fd, 0);
548 close(fd);
550 fd = open("/dev/null", O_WRONLY);
551 if(fd >= 0) {
552 if(fd != 1)
553 dup2(fd, 1);
554 if(fd != 2)
555 dup2(fd, 2);
556 if(fd != 1 && fd != 2)
557 close(fd);
560 rc = setsid();
561 if(rc < 0) {
562 do_log_error(L_ERROR, errno, "Couldn't create new session");
563 exit(1);
567 #else
569 void
570 do_daemonise(int noclose)
572 do_log(L_ERROR, "Cannot daemonise on this platform");
573 exit(1);
575 #endif
578 void
579 writePid(char *pidfile)
581 int fd, n, rc;
582 char buf[16];
584 fd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
585 if(fd < 0) {
586 do_log_error(L_ERROR, errno,
587 "Couldn't create pid file %s", pidfile);
588 exit(1);
590 n = snprintf(buf, 16, "%ld\n", (long)getpid());
591 if(n < 0 || n >= 16) {
592 close(fd);
593 unlink(pidfile);
594 do_log(L_ERROR, "Couldn't format pid.\n");
595 exit(1);
597 rc = write(fd, buf, n);
598 if(rc != n) {
599 close(fd);
600 unlink(pidfile);
601 do_log_error(L_ERROR, errno, "Couldn't write pid");
602 exit(1);
605 close(fd);
606 return;
609 static const char b64[64] =
610 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
612 /* "/" replaced with "-" */
613 static const char b64fss[64] =
614 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
617 b64cpy(char *restrict dst, const char *restrict src, int n, int fss)
619 const char *b = fss ? b64fss: b64;
620 int i, j;
622 j = 0;
623 for(i = 0; i < n; i += 3) {
624 unsigned char a0, a1, a2;
625 a0 = src[i];
626 a1 = i < n - 1 ? src[i + 1] : 0;
627 a2 = i < n - 2 ? src[i + 2] : 0;
628 dst[j++] = b[(a0 >> 2) & 0x3F];
629 dst[j++] = b[((a0 << 4) & 0x30) | ((a1 >> 4) & 0x0F)];
630 if(i < n - 1)
631 dst[j++] = b[((a1 << 2) & 0x3C) | ((a2 >> 6) & 0x03)];
632 else
633 dst[j++] = '=';
634 if(i < n - 2)
635 dst[j++] = b[a2 & 0x3F];
636 else
637 dst[j++] = '=';
639 return j;
643 b64cmp(const char *restrict a, int an, const char *restrict b, int bn)
645 char *buf;
646 int r;
648 if(an % 4 != 0)
649 return -1;
650 if((bn + 2) / 3 != an / 4)
651 return -1;
652 buf = malloc(an);
653 if(buf == NULL)
654 return -1;
655 b64cpy(buf, b, bn, 0);
656 r = memcmp(buf, a, an);
657 free(buf);
658 return r;
661 IntListPtr
662 makeIntList(int size)
664 IntListPtr list;
665 if(size <= 0)
666 size = 4;
668 list = malloc(sizeof(IntListRec));
669 if(list == NULL)
670 return NULL;
672 list->ranges = malloc(size * sizeof(IntRangeRec));
673 if(list->ranges == NULL) {
674 free(list);
675 return NULL;
678 list->length = 0;
679 list->size = size;
680 return list;
683 void
684 destroyIntList(IntListPtr list)
686 free(list->ranges);
687 free(list);
691 intListMember(int n, IntListPtr list)
693 int lo = 0, hi = list->length - 1;
694 int mid;
695 while(hi >= lo) {
696 mid = (hi + lo) / 2;
697 if(list->ranges[mid].from > n)
698 hi = mid - 1;
699 else if(list->ranges[mid].to < n)
700 lo = mid + 1;
701 else
702 return 1;
704 return 0;
707 static int
708 deleteRange(IntListPtr list, int i)
710 assert(list->length > i);
711 if(list->length > i + 1)
712 memmove(list->ranges + i, list->ranges + i + 1,
713 (list->length - i - 1) * sizeof(IntRangeRec));
714 list->length--;
715 return 1;
718 static int
719 insertRange(int from, int to, IntListPtr list, int i)
721 assert(i >= 0 && i <= list->length);
722 assert(i == 0 || list->ranges[i - 1].to < from - 1);
723 assert(i == list->length || list->ranges[i].from > to + 1);
725 if(list->length >= list->size) {
726 int newsize = list->size * 2 + 1;
727 IntRangePtr newranges =
728 realloc(list->ranges, newsize * sizeof(IntRangeRec));
729 if(newranges == NULL)
730 return -1;
731 list->size = newsize;
732 list->ranges = newranges;
735 if(i < list->length)
736 memmove(list->ranges + i + 1, list->ranges + i,
737 list->length - i);
738 list->length++;
739 list->ranges[i].from = from;
740 list->ranges[i].to = to;
741 return 1;
744 static int
745 maybeMergeRanges(IntListPtr list, int i)
747 int rc;
749 while(i > 0 && list->ranges[i].from <= list->ranges[i - 1].to + 1) {
750 list->ranges[i - 1].from =
751 MIN(list->ranges[i - 1].from, list->ranges[i].from);
752 list->ranges[i - 1].to =
753 MAX(list->ranges[i - 1].to, list->ranges[i].to);
754 rc = deleteRange(list, i);
755 if(rc < 0) return -1;
756 i--;
759 while(i < list->length - 1 &&
760 list->ranges[i].to >= list->ranges[i + 1].from - 1) {
761 list->ranges[i + 1].from =
762 MIN(list->ranges[i + 1].from, list->ranges[i].from);
763 list->ranges[i - 1].to =
764 MAX(list->ranges[i + 1].to, list->ranges[i].to);
765 rc = deleteRange(list, i);
766 if(rc < 0) return -1;
768 return 1;
772 intListCons(int from, int to, IntListPtr list)
774 int i;
776 /* Don't bother with the dichotomy. */
777 for(i = 0; i < list->length; i++) {
778 if(list->ranges[i].to >= from - 1)
779 break;
782 if(i < list->length &&
783 (from >= list->ranges[i].from - 1 || to <= list->ranges[i].to + 1)) {
784 if(from <= list->ranges[i].from)
785 list->ranges[i].from = from;
786 if(to >= list->ranges[i].to)
787 list->ranges[i].to = to;
788 return maybeMergeRanges(list, i);
790 return insertRange(from, to, list, i);
793 /* Return the amount of physical memory on the box, -1 if unknown or
794 over two gigs. */
795 #if defined(__linux__)
797 #include <sys/sysinfo.h>
799 physicalMemory()
801 int rc;
802 struct sysinfo info;
804 rc = sysinfo(&info);
805 if(rc < 0)
806 return -1;
808 if(info.totalram <= 0x7fffffff / info.mem_unit)
809 return (int)(info.totalram * info.mem_unit);
811 return -1;
814 #elif defined(__FreeBSD__)
816 #include <sys/sysctl.h>
818 physicalMemory()
820 unsigned long membytes;
821 size_t len;
822 int res;
824 len = sizeof(membytes);
825 res = sysctlbyname("hw.physmem", &membytes, &len, NULL, 0);
826 if (res || membytes > INT_MAX)
827 return -1;
829 return (int)membytes;
832 #else
835 physicalMemory()
837 return -1;
839 #endif