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 *p
= s
+ n
;
231 const unsigned char *begin
= s
;
232 unsigned char cc
= c
;
234 while (--p
>= begin
) {
246 if(h
>= '0' && h
<= '9')
248 else if(h
>= 'a' && h
<= 'f')
250 else if(h
>= 'A' && h
<= 'F')
303 vsprintf_a(const char *f
, va_list args
)
308 rc
= vasprintf(&r
, f
, args
);
317 /* This is not going to work if va_list is interesting. But then, if you
318 have a non-trivial implementation of va_list, you should have va_copy. */
320 #define va_copy(a, b) do { a = b; } while(0)
324 vsprintf_a(const char *f
, va_list args
)
331 va_copy(args_copy
, args
);
332 n
= vsnprintf(buf
, 64, f
, args_copy
);
333 if(n
>= 0 && n
< 64) {
334 return strdup_n(buf
, n
);
342 string
= malloc(size
);
345 va_copy(args_copy
, args
);
346 n
= vsnprintf(string
, size
, f
, args_copy
);
347 if(n
>= 0 && n
< size
)
362 sprintf_a(const char *f
, ...)
367 s
= vsprintf_a(f
, args
);
373 hash(unsigned int seed
, const void *restrict key
, int key_size
,
374 unsigned int hash_size
)
380 for(i
= 0; i
< key_size
; i
++)
381 h
= (h
<< 5) + (h
>> (hash_size
- 5)) +
382 ((unsigned char*)key
)[i
];
383 return h
& ((1 << hash_size
) - 1);
393 case EDOSHUTDOWN
: s
= "Immediate shutdown requested"; break;
394 case EDOGRACEFUL
: s
= "Graceful shutdown requested"; break;
395 case EDOTIMEOUT
: s
= "Timeout"; break;
396 case ECLIENTRESET
: s
= "Connection reset by client"; break;
397 case ESYNTAX
: s
= "Incorrect syntax"; break;
398 case EREDIRECTOR
: s
= "Redirector error"; break;
399 case EDNS_HOST_NOT_FOUND
: s
= "Host not found"; break;
400 case EDNS_NO_ADDRESS
: s
= "No address"; break;
401 case EDNS_NO_RECOVERY
: s
= "Permanent name server failure"; break;
402 case EDNS_TRY_AGAIN
: s
= "Temporary name server failure"; break;
403 case EDNS_INVALID
: s
= "Invalid reply from name server"; break;
404 case EDNS_UNSUPPORTED
: s
= "Unsupported DNS reply"; break;
405 case EDNS_FORMAT
: s
= "Invalid DNS query"; break;
406 case EDNS_REFUSED
: s
= "DNS query refused by server"; break;
407 case EDNS_CNAME_LOOP
: s
= "DNS CNAME loop"; break;
409 case ESOCKS_PROTOCOL
: s
= "SOCKS protocol error"; break;
410 case ESOCKS_REJECT_FAIL
: s
= "SOCKS request rejected or failed"; break;
411 case ESOCKS_REJECT_IDENTD
: s
= "SOCKS request rejected: "
412 "server couldn't connect to identd";
414 case ESOCKS_REJECT_UID_MISMATCH
: s
= "SOCKS request rejected: "
417 case ESOCKS5_BASE
: s
= "SOCKS success"; break;
418 case ESOCKS5_BASE
+ 1: s
= "General SOCKS server failure"; break;
419 case ESOCKS5_BASE
+ 2: s
= "SOCKS connection not allowed"; break;
420 case ESOCKS5_BASE
+ 3: s
= "SOCKS error: network unreachable"; break;
421 case ESOCKS5_BASE
+ 4: s
= "SOCKS error: host unreachable"; break;
422 case ESOCKS5_BASE
+ 5: s
= "SOCKS error: connection refused"; break;
423 case ESOCKS5_BASE
+ 6: s
= "SOCKS error: TTL expired"; break;
424 case ESOCKS5_BASE
+ 7: s
= "SOCKS command not supported"; break;
425 case ESOCKS5_BASE
+ 8: s
= "SOCKS error: address type not supported";
428 case EUNKNOWN
: s
= "Unknown error"; break;
429 default: s
= NULL
; break;
431 if(!s
) s
= strerror(e
);
432 #ifdef WIN32 /*MINGW*/
434 if(e
>= WSABASEERR
&& e
<= WSABASEERR
+ 2000) {
435 /* This should be okay, as long as the caller discards the
436 pointer before another error occurs. */
437 snprintf(buf
, 20, "Winsock error %d", e
);
443 snprintf(buf
, 20, "Unknown error %d", e
);
449 /* Like mktime(3), but UTC rather than local time */
450 #if defined(HAVE_TIMEGM)
452 mktime_gmt(struct tm
*tm
)
456 #elif defined(HAVE_TM_GMTOFF)
458 mktime_gmt(struct tm
*tm
)
469 return t
+ ltm
->tm_gmtoff
;
471 #elif defined(HAVE_TZSET)
473 /* Taken from the Linux timegm(3) man page. */
475 mktime_gmt(struct tm
*tm
)
481 setenv("TZ", "GMT", 1);
493 mktime_gmt(struct tm
*tm
)
497 static char *old_tz
= NULL
;
506 old_tz
= sprintf_a("TZ=%s", tz
);
508 old_tz
= strdup("TZ"); /* XXX - non-portable? */
516 #error no mktime_gmt implementation on this platform
521 expandTilde(AtomPtr filename
)
528 if(filename
== NULL
|| filename
->length
< 1 ||
529 filename
->string
[0] != '~' || filename
->string
[1] != '/')
532 home
= getenv("HOME");
537 buf
= malloc(len
+ 1 + 1 + filename
->length
- 2);
539 do_log(L_ERROR
, "Could not allocate buffer.\n");
543 memcpy(buf
, home
, len
);
544 if(buf
[len
- 1] != '/')
546 memcpy(buf
+ len
, filename
->string
+ 2, filename
->length
- 2);
547 len
+= filename
->length
- 2;
548 ret
= internAtomN(buf
, len
);
551 releaseAtom(filename
);
557 do_daemonise(int noclose
)
566 do_log_error(L_ERROR
, errno
, "Couldn't fork");
578 /* Leaving the default file descriptors free is not a good
579 idea, as it will cause library functions such as abort to
580 thrash the on-disk cache. */
581 fd
= open("/dev/null", O_RDONLY
);
586 fd
= open("/dev/null", O_WRONLY
);
592 if(fd
!= 1 && fd
!= 2)
598 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
606 do_daemonise(int noclose
)
608 do_log(L_ERROR
, "Cannot daemonise on this platform");
615 writePid(char *pidfile
)
620 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
622 do_log_error(L_ERROR
, errno
,
623 "Couldn't create pid file %s", pidfile
);
626 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
627 if(n
< 0 || n
>= 16) {
630 do_log(L_ERROR
, "Couldn't format pid.\n");
633 rc
= write(fd
, buf
, n
);
637 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
645 static const char b64
[64] =
646 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
648 /* "/" replaced with "-" */
649 static const char b64fss
[64] =
650 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
653 b64cpy(char *restrict dst
, const char *restrict src
, int n
, int fss
)
655 const char *b
= fss
? b64fss
: b64
;
659 for(i
= 0; i
< n
; i
+= 3) {
660 unsigned char a0
, a1
, a2
;
662 a1
= i
< n
- 1 ? src
[i
+ 1] : 0;
663 a2
= i
< n
- 2 ? src
[i
+ 2] : 0;
664 dst
[j
++] = b
[(a0
>> 2) & 0x3F];
665 dst
[j
++] = b
[((a0
<< 4) & 0x30) | ((a1
>> 4) & 0x0F)];
667 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
671 dst
[j
++] = b
[a2
& 0x3F];
679 b64cmp(const char *restrict a
, int an
, const char *restrict b
, int bn
)
686 if((bn
+ 2) / 3 != an
/ 4)
691 b64cpy(buf
, b
, bn
, 0);
692 r
= memcmp(buf
, a
, an
);
698 makeIntList(int size
)
704 list
= malloc(sizeof(IntListRec
));
708 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
709 if(list
->ranges
== NULL
) {
720 destroyIntList(IntListPtr list
)
727 intListMember(int n
, IntListPtr list
)
729 int lo
= 0, hi
= list
->length
- 1;
733 if(list
->ranges
[mid
].from
> n
)
735 else if(list
->ranges
[mid
].to
< n
)
744 deleteRange(IntListPtr list
, int i
)
746 assert(list
->length
> i
);
747 if(list
->length
> i
+ 1)
748 memmove(list
->ranges
+ i
, list
->ranges
+ i
+ 1,
749 (list
->length
- i
- 1) * sizeof(IntRangeRec
));
755 insertRange(int from
, int to
, IntListPtr list
, int i
)
757 assert(i
>= 0 && i
<= list
->length
);
758 assert(i
== 0 || list
->ranges
[i
- 1].to
< from
- 1);
759 assert(i
== list
->length
|| list
->ranges
[i
].from
> to
+ 1);
761 if(list
->length
>= list
->size
) {
762 int newsize
= list
->size
* 2 + 1;
763 IntRangePtr newranges
=
764 realloc(list
->ranges
, newsize
* sizeof(IntRangeRec
));
765 if(newranges
== NULL
)
767 list
->size
= newsize
;
768 list
->ranges
= newranges
;
772 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
775 list
->ranges
[i
].from
= from
;
776 list
->ranges
[i
].to
= to
;
781 maybeMergeRanges(IntListPtr list
, int i
)
785 while(i
> 0 && list
->ranges
[i
].from
<= list
->ranges
[i
- 1].to
+ 1) {
786 list
->ranges
[i
- 1].from
=
787 MIN(list
->ranges
[i
- 1].from
, list
->ranges
[i
].from
);
788 list
->ranges
[i
- 1].to
=
789 MAX(list
->ranges
[i
- 1].to
, list
->ranges
[i
].to
);
790 rc
= deleteRange(list
, i
);
791 if(rc
< 0) return -1;
795 while(i
< list
->length
- 1 &&
796 list
->ranges
[i
].to
>= list
->ranges
[i
+ 1].from
- 1) {
797 list
->ranges
[i
+ 1].from
=
798 MIN(list
->ranges
[i
+ 1].from
, list
->ranges
[i
].from
);
799 list
->ranges
[i
- 1].to
=
800 MAX(list
->ranges
[i
+ 1].to
, list
->ranges
[i
].to
);
801 rc
= deleteRange(list
, i
);
802 if(rc
< 0) return -1;
808 intListCons(int from
, int to
, IntListPtr list
)
812 /* Don't bother with the dichotomy. */
813 for(i
= 0; i
< list
->length
; i
++) {
814 if(list
->ranges
[i
].to
>= from
- 1)
818 if(i
< list
->length
&&
819 (from
>= list
->ranges
[i
].from
- 1 || to
<= list
->ranges
[i
].to
+ 1)) {
820 if(from
<= list
->ranges
[i
].from
)
821 list
->ranges
[i
].from
= from
;
822 if(to
>= list
->ranges
[i
].to
)
823 list
->ranges
[i
].to
= to
;
824 return maybeMergeRanges(list
, i
);
826 return insertRange(from
, to
, list
, i
);
829 /* Return the amount of physical memory on the box, -1 if unknown or
831 #if defined(__linux__)
833 #include <sys/sysinfo.h>
844 if(info
.totalram
<= 0x7fffffff / info
.mem_unit
)
845 return (int)(info
.totalram
* info
.mem_unit
);
850 #elif defined(__FreeBSD__)
852 #include <sys/sysctl.h>
856 unsigned long membytes
;
860 len
= sizeof(membytes
);
861 res
= sysctlbyname("hw.physmem", &membytes
, &len
, NULL
, 0);
862 if (res
|| membytes
> INT_MAX
)
865 return (int)membytes
;