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 atoi_n(const char *restrict string
, int n
, int len
, int *value_return
)
170 if(i
>= len
|| !digit(string
[i
]))
173 while(i
< len
&& digit(string
[i
])) {
174 val
= val
* 10 + (string
[i
] - '0');
182 isWhitespace(const char *string
)
184 while(*string
!= '\0') {
185 if(*string
== ' ' || *string
== '\t')
195 memrchr(const void *s
, int c
, size_t n
)
197 const unsigned char *ss
= s
;
198 unsigned char cc
= c
;
200 for(i
= n
- 1; i
>= 0; i
--)
202 return (void*)(ss
+ i
);
210 if(h
>= '0' && h
<= '9')
212 else if(h
>= 'a' && h
<= 'f')
214 else if(h
>= 'A' && h
<= 'F')
267 vsprintf_a(const char *f
, va_list args
)
272 rc
= vasprintf(&r
, f
, args
);
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. */
284 #define va_copy(a, b) do { a = b; } while(0)
288 vsprintf_a(const char *f
, va_list args
)
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
);
306 string
= malloc(size
);
309 va_copy(args_copy
, args
);
310 n
= vsnprintf(string
, size
, f
, args_copy
);
311 if(n
>= 0 && n
< size
)
326 sprintf_a(const char *f
, ...)
331 s
= vsprintf_a(f
, args
);
337 hash(unsigned int seed
, const void *restrict key
, int key_size
,
338 unsigned int hash_size
)
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);
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;
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";
378 case ESOCKS_REJECT_UID_MISMATCH
: s
= "SOCKS request rejected: "
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";
392 case EUNKNOWN
: s
= "Unknown error"; break;
393 default: s
= NULL
; break;
395 if(!s
) s
= strerror(e
);
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
);
407 snprintf(buf
, 20, "Unknown error %d", e
);
413 /* Like mktime(3), but UTC rather than local time */
414 #if defined(HAVE_TIMEGM)
416 mktime_gmt(struct tm
*tm
)
420 #elif defined(HAVE_TM_GMTOFF)
422 mktime_gmt(struct tm
*tm
)
433 return t
+ ltm
->tm_gmtoff
;
435 #elif defined(HAVE_TZSET)
437 /* Taken from the Linux timegm(3) man page. */
439 mktime_gmt(struct tm
*tm
)
445 setenv("TZ", "GMT", 1);
457 mktime_gmt(struct tm
*tm
)
461 static char *old_tz
= NULL
;
470 old_tz
= sprintf_a("TZ=%s", tz
);
472 old_tz
= strdup("TZ"); /* XXX - non-portable? */
480 #error no mktime_gmt implementation on this platform
485 expandTilde(AtomPtr filename
)
492 if(filename
== NULL
|| filename
->length
< 1 ||
493 filename
->string
[0] != '~' || filename
->string
[1] != '/')
496 home
= getenv("HOME");
501 buf
= malloc(len
+ 1 + 1 + filename
->length
- 2);
503 do_log(L_ERROR
, "Could not allocate buffer.\n");
507 memcpy(buf
, home
, len
);
508 if(buf
[len
- 1] != '/')
510 memcpy(buf
+ len
, filename
->string
+ 2, filename
->length
- 2);
511 len
+= filename
->length
- 2;
512 ret
= internAtomN(buf
, len
);
515 releaseAtom(filename
);
521 do_daemonise(int noclose
)
530 do_log_error(L_ERROR
, errno
, "Couldn't fork");
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
);
550 fd
= open("/dev/null", O_WRONLY
);
556 if(fd
!= 1 && fd
!= 2)
562 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
570 do_daemonise(int noclose
)
572 do_log(L_ERROR
, "Cannot daemonise on this platform");
579 writePid(char *pidfile
)
584 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
586 do_log_error(L_ERROR
, errno
,
587 "Couldn't create pid file %s", pidfile
);
590 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
591 if(n
< 0 || n
>= 16) {
594 do_log(L_ERROR
, "Couldn't format pid.\n");
597 rc
= write(fd
, buf
, n
);
601 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
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
;
623 for(i
= 0; i
< n
; i
+= 3) {
624 unsigned char a0
, a1
, a2
;
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)];
631 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
635 dst
[j
++] = b
[a2
& 0x3F];
643 b64cmp(const char *restrict a
, int an
, const char *restrict b
, int bn
)
650 if((bn
+ 2) / 3 != an
/ 4)
655 b64cpy(buf
, b
, bn
, 0);
656 r
= memcmp(buf
, a
, an
);
662 makeIntList(int size
)
668 list
= malloc(sizeof(IntListRec
));
672 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
673 if(list
->ranges
== NULL
) {
684 destroyIntList(IntListPtr list
)
691 intListMember(int n
, IntListPtr list
)
693 int lo
= 0, hi
= list
->length
- 1;
697 if(list
->ranges
[mid
].from
> n
)
699 else if(list
->ranges
[mid
].to
< n
)
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
));
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
)
731 list
->size
= newsize
;
732 list
->ranges
= newranges
;
736 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
739 list
->ranges
[i
].from
= from
;
740 list
->ranges
[i
].to
= to
;
745 maybeMergeRanges(IntListPtr list
, int i
)
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;
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;
772 intListCons(int from
, int to
, IntListPtr list
)
776 /* Don't bother with the dichotomy. */
777 for(i
= 0; i
< list
->length
; i
++) {
778 if(list
->ranges
[i
].to
>= from
- 1)
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
795 #if defined(__linux__)
797 #include <sys/sysinfo.h>
808 if(info
.totalram
<= 0x7fffffff / info
.mem_unit
)
809 return (int)(info
.totalram
* info
.mem_unit
);
814 #elif defined(__FreeBSD__)
816 #include <sys/sysctl.h>
820 unsigned long membytes
;
824 len
= sizeof(membytes
);
825 res
= sysctlbyname("hw.physmem", &membytes
, &len
, NULL
, 0);
826 if (res
|| membytes
> INT_MAX
)
829 return (int)membytes
;