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
25 /* Note that this is different from GNU's strndup(3). */
27 strdup_n(const char *restrict buf
, int n
)
39 snnprintf(char *restrict buf
, int n
, int len
, const char *format
, ...)
43 va_start(args
, format
);
44 rc
= snnvprintf(buf
, n
, len
, format
, args
);
50 snnvprintf(char *restrict buf
, int n
, int len
, const char *format
, va_list args
)
55 rc
= vsnprintf(buf
+ n
, len
- n
, format
, args
);
56 if(rc
>= 0 && n
+ rc
<= len
)
63 snnprint_n(char *restrict buf
, int n
, int len
, const char *s
, int slen
)
67 while(i
< slen
&& n
< len
)
76 strcmp_n(const char *string
, const char *buf
, int n
)
80 while(string
[i
] != '\0' && i
< n
) {
81 if(string
[i
] < buf
[i
])
83 else if(string
[i
] > buf
[i
])
87 if(string
[i
] == '\0' || i
== n
)
98 if(c
>= 'A' && c
<= 'Z') return 1;
99 if(c
>= 'a' && c
<= 'z') return 1;
106 if(c
>= '0' && c
<= '9')
114 if(a
>= 'A' && a
<= 'Z')
121 lwrcpy(char *restrict dst
, const char *restrict src
, int n
)
124 for(i
= 0; i
< n
; i
++)
125 dst
[i
] = lwr(src
[i
]);
130 lwrcmp(const char *as
, const char *bs
, int n
)
133 for(i
= 0; i
< n
; i
++) {
134 char a
= lwr(as
[i
]), b
= lwr(bs
[i
]);
144 strcasecmp_n(const char *string
, const char *buf
, int n
)
148 while(string
[i
] != '\0' && i
< n
) {
149 char a
= lwr(string
[i
]), b
= lwr(buf
[i
]);
156 if(string
[i
] == '\0' && i
== n
)
165 parseIntN(const char *restrict str
, int m
, int n
, int min
, int max
,
166 int base
, int *value_return
)
173 while (m
< n
&& (str
[m
] == ' ' || str
[m
] == '\t'))
179 /* We hope here that 63 bytes is more than enough to hold
180 a number that won't overflow. */
181 len
= MIN(n
- m
, sizeof(buf
) - 1);
182 memcpy(buf
, str
+ m
, len
);
186 val
= strtol(buf
, &endp
, base
);
187 if (errno
!= 0 || endp
== buf
|| val
< min
|| val
> max
)
197 parseInt(const char *restrict str
, int m
, int min
, int max
,
198 int base
, int *value_return
)
204 val
= strtol(str
+ m
, &endp
, base
);
205 if (errno
!= 0 || endp
== str
+ m
|| val
< min
|| val
> max
)
215 isWhitespace(const char *string
)
217 while(*string
!= '\0') {
218 if(*string
== ' ' || *string
== '\t')
228 memrchr(const void *s
, int c
, size_t n
)
230 const unsigned char *ss
= s
;
231 unsigned char cc
= c
;
233 for(i
= n
- 1; i
>= 0; i
--)
235 return (void*)(ss
+ i
);
243 if(h
>= '0' && h
<= '9')
245 else if(h
>= 'a' && h
<= 'f')
247 else if(h
>= 'A' && h
<= 'F')
300 vsprintf_a(const char *f
, va_list args
)
305 rc
= vasprintf(&r
, f
, args
);
314 /* This is not going to work if va_list is interesting. But then, if you
315 have a non-trivial implementation of va_list, you should have va_copy. */
317 #define va_copy(a, b) do { a = b; } while(0)
321 vsprintf_a(const char *f
, va_list args
)
328 va_copy(args_copy
, args
);
329 n
= vsnprintf(buf
, 64, f
, args_copy
);
330 if(n
>= 0 && n
< 64) {
331 return strdup_n(buf
, n
);
339 string
= malloc(size
);
342 va_copy(args_copy
, args
);
343 n
= vsnprintf(string
, size
, f
, args_copy
);
344 if(n
>= 0 && n
< size
)
359 sprintf_a(const char *f
, ...)
364 s
= vsprintf_a(f
, args
);
370 hash(unsigned int seed
, const void *restrict key
, int key_size
,
371 unsigned int hash_size
)
377 for(i
= 0; i
< key_size
; i
++)
378 h
= (h
<< 5) + (h
>> (hash_size
- 5)) +
379 ((unsigned char*)key
)[i
];
380 return h
& ((1 << hash_size
) - 1);
390 case EDOSHUTDOWN
: s
= "Immediate shutdown requested"; break;
391 case EDOGRACEFUL
: s
= "Graceful shutdown requested"; break;
392 case EDOTIMEOUT
: s
= "Timeout"; break;
393 case ECLIENTRESET
: s
= "Connection reset by client"; break;
394 case ESYNTAX
: s
= "Incorrect syntax"; break;
395 case EREDIRECTOR
: s
= "Redirector error"; break;
396 case EDNS_HOST_NOT_FOUND
: s
= "Host not found"; break;
397 case EDNS_NO_ADDRESS
: s
= "No address"; break;
398 case EDNS_NO_RECOVERY
: s
= "Permanent name server failure"; break;
399 case EDNS_TRY_AGAIN
: s
= "Temporary name server failure"; break;
400 case EDNS_INVALID
: s
= "Invalid reply from name server"; break;
401 case EDNS_UNSUPPORTED
: s
= "Unsupported DNS reply"; break;
402 case EDNS_FORMAT
: s
= "Invalid DNS query"; break;
403 case EDNS_REFUSED
: s
= "DNS query refused by server"; break;
404 case EDNS_CNAME_LOOP
: s
= "DNS CNAME loop"; break;
406 case ESOCKS_PROTOCOL
: s
= "SOCKS protocol error"; break;
407 case ESOCKS_REJECT_FAIL
: s
= "SOCKS request rejected or failed"; break;
408 case ESOCKS_REJECT_IDENTD
: s
= "SOCKS request rejected: "
409 "server couldn't connect to identd";
411 case ESOCKS_REJECT_UID_MISMATCH
: s
= "SOCKS request rejected: "
414 case ESOCKS5_BASE
: s
= "SOCKS success"; break;
415 case ESOCKS5_BASE
+ 1: s
= "General SOCKS server failure"; break;
416 case ESOCKS5_BASE
+ 2: s
= "SOCKS connection not allowed"; break;
417 case ESOCKS5_BASE
+ 3: s
= "SOCKS error: network unreachable"; break;
418 case ESOCKS5_BASE
+ 4: s
= "SOCKS error: host unreachable"; break;
419 case ESOCKS5_BASE
+ 5: s
= "SOCKS error: connection refused"; break;
420 case ESOCKS5_BASE
+ 6: s
= "SOCKS error: TTL expired"; break;
421 case ESOCKS5_BASE
+ 7: s
= "SOCKS command not supported"; break;
422 case ESOCKS5_BASE
+ 8: s
= "SOCKS error: address type not supported";
425 case EUNKNOWN
: s
= "Unknown error"; break;
426 default: s
= NULL
; break;
428 if(!s
) s
= strerror(e
);
429 #ifdef WIN32 /*MINGW*/
431 if(e
>= WSABASEERR
&& e
<= WSABASEERR
+ 2000) {
432 /* This should be okay, as long as the caller discards the
433 pointer before another error occurs. */
434 snprintf(buf
, 20, "Winsock error %d", e
);
440 snprintf(buf
, 20, "Unknown error %d", e
);
446 /* Like mktime(3), but UTC rather than local time */
447 #if defined(HAVE_TIMEGM)
449 mktime_gmt(struct tm
*tm
)
453 #elif defined(HAVE_TM_GMTOFF)
455 mktime_gmt(struct tm
*tm
)
466 return t
+ ltm
->tm_gmtoff
;
468 #elif defined(HAVE_TZSET)
470 /* Taken from the Linux timegm(3) man page. */
472 mktime_gmt(struct tm
*tm
)
478 setenv("TZ", "GMT", 1);
490 mktime_gmt(struct tm
*tm
)
494 static char *old_tz
= NULL
;
503 old_tz
= sprintf_a("TZ=%s", tz
);
505 old_tz
= strdup("TZ"); /* XXX - non-portable? */
513 #error no mktime_gmt implementation on this platform
518 expandTilde(AtomPtr filename
)
525 if(filename
== NULL
|| filename
->length
< 1 ||
526 filename
->string
[0] != '~' || filename
->string
[1] != '/')
529 home
= getenv("HOME");
534 buf
= malloc(len
+ 1 + 1 + filename
->length
- 2);
536 do_log(L_ERROR
, "Could not allocate buffer.\n");
540 memcpy(buf
, home
, len
);
541 if(buf
[len
- 1] != '/')
543 memcpy(buf
+ len
, filename
->string
+ 2, filename
->length
- 2);
544 len
+= filename
->length
- 2;
545 ret
= internAtomN(buf
, len
);
548 releaseAtom(filename
);
554 do_daemonise(int noclose
)
563 do_log_error(L_ERROR
, errno
, "Couldn't fork");
575 /* Leaving the default file descriptors free is not a good
576 idea, as it will cause library functions such as abort to
577 thrash the on-disk cache. */
578 fd
= open("/dev/null", O_RDONLY
);
583 fd
= open("/dev/null", O_WRONLY
);
589 if(fd
!= 1 && fd
!= 2)
595 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
603 do_daemonise(int noclose
)
605 do_log(L_ERROR
, "Cannot daemonise on this platform");
612 writePid(char *pidfile
)
617 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
619 do_log_error(L_ERROR
, errno
,
620 "Couldn't create pid file %s", pidfile
);
623 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
624 if(n
< 0 || n
>= 16) {
627 do_log(L_ERROR
, "Couldn't format pid.\n");
630 rc
= write(fd
, buf
, n
);
634 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
642 static const char b64
[64] =
643 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
645 /* "/" replaced with "-" */
646 static const char b64fss
[64] =
647 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
650 b64cpy(char *restrict dst
, const char *restrict src
, int n
, int fss
)
652 const char *b
= fss
? b64fss
: b64
;
656 for(i
= 0; i
< n
; i
+= 3) {
657 unsigned char a0
, a1
, a2
;
659 a1
= i
< n
- 1 ? src
[i
+ 1] : 0;
660 a2
= i
< n
- 2 ? src
[i
+ 2] : 0;
661 dst
[j
++] = b
[(a0
>> 2) & 0x3F];
662 dst
[j
++] = b
[((a0
<< 4) & 0x30) | ((a1
>> 4) & 0x0F)];
664 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
668 dst
[j
++] = b
[a2
& 0x3F];
676 b64cmp(const char *restrict a
, int an
, const char *restrict b
, int bn
)
683 if((bn
+ 2) / 3 != an
/ 4)
688 b64cpy(buf
, b
, bn
, 0);
689 r
= memcmp(buf
, a
, an
);
695 makeIntList(int size
)
701 list
= malloc(sizeof(IntListRec
));
705 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
706 if(list
->ranges
== NULL
) {
717 destroyIntList(IntListPtr list
)
724 intListMember(int n
, IntListPtr list
)
726 int lo
= 0, hi
= list
->length
- 1;
730 if(list
->ranges
[mid
].from
> n
)
732 else if(list
->ranges
[mid
].to
< n
)
741 deleteRange(IntListPtr list
, int i
)
743 assert(list
->length
> i
);
744 if(list
->length
> i
+ 1)
745 memmove(list
->ranges
+ i
, list
->ranges
+ i
+ 1,
746 (list
->length
- i
- 1) * sizeof(IntRangeRec
));
752 insertRange(int from
, int to
, IntListPtr list
, int i
)
754 assert(i
>= 0 && i
<= list
->length
);
755 assert(i
== 0 || list
->ranges
[i
- 1].to
< from
- 1);
756 assert(i
== list
->length
|| list
->ranges
[i
].from
> to
+ 1);
758 if(list
->length
>= list
->size
) {
759 int newsize
= list
->size
* 2 + 1;
760 IntRangePtr newranges
=
761 realloc(list
->ranges
, newsize
* sizeof(IntRangeRec
));
762 if(newranges
== NULL
)
764 list
->size
= newsize
;
765 list
->ranges
= newranges
;
769 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
772 list
->ranges
[i
].from
= from
;
773 list
->ranges
[i
].to
= to
;
778 maybeMergeRanges(IntListPtr list
, int i
)
782 while(i
> 0 && list
->ranges
[i
].from
<= list
->ranges
[i
- 1].to
+ 1) {
783 list
->ranges
[i
- 1].from
=
784 MIN(list
->ranges
[i
- 1].from
, list
->ranges
[i
].from
);
785 list
->ranges
[i
- 1].to
=
786 MAX(list
->ranges
[i
- 1].to
, list
->ranges
[i
].to
);
787 rc
= deleteRange(list
, i
);
788 if(rc
< 0) return -1;
792 while(i
< list
->length
- 1 &&
793 list
->ranges
[i
].to
>= list
->ranges
[i
+ 1].from
- 1) {
794 list
->ranges
[i
+ 1].from
=
795 MIN(list
->ranges
[i
+ 1].from
, list
->ranges
[i
].from
);
796 list
->ranges
[i
- 1].to
=
797 MAX(list
->ranges
[i
+ 1].to
, list
->ranges
[i
].to
);
798 rc
= deleteRange(list
, i
);
799 if(rc
< 0) return -1;
805 intListCons(int from
, int to
, IntListPtr list
)
809 /* Don't bother with the dichotomy. */
810 for(i
= 0; i
< list
->length
; i
++) {
811 if(list
->ranges
[i
].to
>= from
- 1)
815 if(i
< list
->length
&&
816 (from
>= list
->ranges
[i
].from
- 1 || to
<= list
->ranges
[i
].to
+ 1)) {
817 if(from
<= list
->ranges
[i
].from
)
818 list
->ranges
[i
].from
= from
;
819 if(to
>= list
->ranges
[i
].to
)
820 list
->ranges
[i
].to
= to
;
821 return maybeMergeRanges(list
, i
);
823 return insertRange(from
, to
, list
, i
);
826 /* Return the amount of physical memory on the box, -1 if unknown or
828 #if defined(__linux__)
830 #include <sys/sysinfo.h>
841 if(info
.totalram
<= 0x7fffffff / info
.mem_unit
)
842 return (int)(info
.totalram
* info
.mem_unit
);
847 #elif defined(__FreeBSD__)
849 #include <sys/sysctl.h>
853 unsigned long membytes
;
857 len
= sizeof(membytes
);
858 res
= sysctlbyname("hw.physmem", &membytes
, &len
, NULL
, 0);
859 if (res
|| membytes
> INT_MAX
)
862 return (int)membytes
;