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";
377 case ESOCKS_REJECT_UID_MISMATCH
: s
= "SOCKS request rejected: "
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";
391 case EUNKNOWN
: s
= "Unknown error"; break;
392 default: s
= NULL
; break;
394 if(!s
) s
= strerror(e
);
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
);
406 snprintf(buf
, 20, "Unknown error %d", e
);
412 /* Like mktime(3), but UTC rather than local time */
413 #if defined(HAVE_TIMEGM)
415 mktime_gmt(struct tm
*tm
)
419 #elif defined(HAVE_TM_GMTOFF)
421 mktime_gmt(struct tm
*tm
)
432 return t
+ ltm
->tm_gmtoff
;
434 #elif defined(HAVE_TZSET)
436 /* Taken from the Linux timegm(3) man page. */
438 mktime_gmt(struct tm
*tm
)
444 setenv("TZ", "GMT", 1);
456 mktime_gmt(struct tm
*tm
)
460 static char *old_tz
= NULL
;
469 old_tz
= sprintf_a("TZ=%s", tz
);
471 old_tz
= strdup("TZ"); /* XXX - non-portable? */
479 #error no mktime_gmt implementation on this platform
484 expandTilde(AtomPtr filename
)
491 if(filename
== NULL
|| filename
->length
< 1 ||
492 filename
->string
[0] != '~' || filename
->string
[1] != '/')
495 home
= getenv("HOME");
500 buf
= malloc(len
+ 1 + 1 + filename
->length
- 2);
502 do_log(L_ERROR
, "Could not allocate buffer.\n");
506 memcpy(buf
, home
, len
);
507 if(buf
[len
- 1] != '/')
509 memcpy(buf
+ len
, filename
->string
+ 2, filename
->length
- 2);
510 len
+= filename
->length
- 2;
511 ret
= internAtomN(buf
, len
);
514 releaseAtom(filename
);
520 do_daemonise(int noclose
)
529 do_log_error(L_ERROR
, errno
, "Couldn't fork");
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
);
549 fd
= open("/dev/null", O_WRONLY
);
555 if(fd
!= 1 && fd
!= 2)
561 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
569 do_daemonise(int noclose
)
571 do_log(L_ERROR
, "Cannot daemonise on this platform");
578 writePid(char *pidfile
)
583 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
585 do_log_error(L_ERROR
, errno
,
586 "Couldn't create pid file %s", pidfile
);
589 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
590 if(n
< 0 || n
>= 16) {
593 do_log(L_ERROR
, "Couldn't format pid.\n");
596 rc
= write(fd
, buf
, n
);
600 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
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
;
622 for(i
= 0; i
< n
; i
+= 3) {
623 unsigned char a0
, a1
, a2
;
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)];
630 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
634 dst
[j
++] = b
[a2
& 0x3F];
642 b64cmp(const char *restrict a
, int an
, const char *restrict b
, int bn
)
649 if((bn
+ 2) / 3 != an
/ 4)
654 b64cpy(buf
, b
, bn
, 0);
655 r
= memcmp(buf
, a
, an
);
661 makeIntList(int size
)
667 list
= malloc(sizeof(IntListRec
));
671 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
672 if(list
->ranges
== NULL
) {
683 destroyIntList(IntListPtr list
)
690 intListMember(int n
, IntListPtr list
)
692 int lo
= 0, hi
= list
->length
- 1;
696 if(list
->ranges
[mid
].from
> n
)
698 else if(list
->ranges
[mid
].to
< n
)
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
));
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
)
730 list
->size
= newsize
;
731 list
->ranges
= newranges
;
735 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
738 list
->ranges
[i
].from
= from
;
739 list
->ranges
[i
].to
= to
;
744 maybeMergeRanges(IntListPtr list
, int i
)
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;
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;
771 intListCons(int from
, int to
, IntListPtr list
)
775 /* Don't bother with the dichotomy. */
776 for(i
= 0; i
< list
->length
; i
++) {
777 if(list
->ranges
[i
].to
>= from
- 1)
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
794 #if defined(__linux__)
796 #include <sys/sysinfo.h>
807 if(info
.totalram
<= 0x7fffffff / info
.mem_unit
)
808 return (int)(info
.totalram
* info
.mem_unit
);
813 #elif defined(__FreeBSD__)
815 #include <sys/sysctl.h>
819 unsigned long membytes
;
823 len
= sizeof(membytes
);
824 res
= sysctlbyname("hw.physmem", &membytes
, &len
, NULL
, 0);
825 if (res
|| membytes
> INT_MAX
)
828 return (int)membytes
;