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
)
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");
543 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
551 do_daemonise(int noclose
)
553 do_log(L_ERROR
, "Cannot daemonise on this platform");
560 writePid(char *pidfile
)
565 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
567 do_log_error(L_ERROR
, errno
,
568 "Couldn't create pid file %s", pidfile
);
571 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
572 if(n
< 0 || n
>= 16) {
575 do_log(L_ERROR
, "Couldn't format pid.\n");
578 rc
= write(fd
, buf
, n
);
582 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
590 static const char b64
[64] =
591 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
593 /* "/" replaced with "-" */
594 static const char b64fss
[64] =
595 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
598 b64cpy(char *restrict dst
, const char *restrict src
, int n
, int fss
)
600 const char *b
= fss
? b64fss
: b64
;
604 for(i
= 0; i
< n
; i
+= 3) {
605 unsigned char a0
, a1
, a2
;
607 a1
= i
< n
- 1 ? src
[i
+ 1] : 0;
608 a2
= i
< n
- 2 ? src
[i
+ 2] : 0;
609 dst
[j
++] = b
[(a0
>> 2) & 0x3F];
610 dst
[j
++] = b
[((a0
<< 4) & 0x30) | ((a1
>> 4) & 0x0F)];
612 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
616 dst
[j
++] = b
[a2
& 0x3F];
624 b64cmp(const char *restrict a
, int an
, const char *restrict b
, int bn
)
631 if((bn
+ 2) / 3 != an
/ 4)
636 b64cpy(buf
, b
, bn
, 0);
637 r
= memcmp(buf
, a
, an
);
643 makeIntList(int size
)
649 list
= malloc(sizeof(IntListRec
));
653 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
654 if(list
->ranges
== NULL
) {
665 destroyIntList(IntListPtr list
)
672 intListMember(int n
, IntListPtr list
)
674 int lo
= 0, hi
= list
->length
- 1;
678 if(list
->ranges
[mid
].from
> n
)
680 else if(list
->ranges
[mid
].to
< n
)
689 deleteRange(IntListPtr list
, int i
)
691 assert(list
->length
> i
);
692 if(list
->length
> i
+ 1)
693 memmove(list
->ranges
+ i
, list
->ranges
+ i
+ 1,
694 (list
->length
- i
- 1) * sizeof(IntRangeRec
));
700 insertRange(int from
, int to
, IntListPtr list
, int i
)
702 assert(i
>= 0 && i
<= list
->length
);
703 assert(i
== 0 || list
->ranges
[i
- 1].to
< from
- 1);
704 assert(i
== list
->length
|| list
->ranges
[i
].from
> to
+ 1);
706 if(list
->length
>= list
->size
) {
707 int newsize
= list
->size
* 2 + 1;
708 IntRangePtr newranges
=
709 realloc(list
->ranges
, newsize
* sizeof(IntRangeRec
));
710 if(newranges
== NULL
)
712 list
->size
= newsize
;
713 list
->ranges
= newranges
;
717 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
720 list
->ranges
[i
].from
= from
;
721 list
->ranges
[i
].to
= to
;
726 maybeMergeRanges(IntListPtr list
, int i
)
730 while(i
> 0 && list
->ranges
[i
].from
<= list
->ranges
[i
- 1].to
+ 1) {
731 list
->ranges
[i
- 1].from
=
732 MIN(list
->ranges
[i
- 1].from
, list
->ranges
[i
].from
);
733 list
->ranges
[i
- 1].to
=
734 MAX(list
->ranges
[i
- 1].to
, list
->ranges
[i
].to
);
735 rc
= deleteRange(list
, i
);
736 if(rc
< 0) return -1;
740 while(i
< list
->length
- 1 &&
741 list
->ranges
[i
].to
>= list
->ranges
[i
+ 1].from
- 1) {
742 list
->ranges
[i
+ 1].from
=
743 MIN(list
->ranges
[i
+ 1].from
, list
->ranges
[i
].from
);
744 list
->ranges
[i
- 1].to
=
745 MAX(list
->ranges
[i
+ 1].to
, list
->ranges
[i
].to
);
746 rc
= deleteRange(list
, i
);
747 if(rc
< 0) return -1;
753 intListCons(int from
, int to
, IntListPtr list
)
757 /* Don't bother with the dichotomy. */
758 for(i
= 0; i
< list
->length
; i
++) {
759 if(list
->ranges
[i
].to
>= from
- 1)
763 if(i
< list
->length
&&
764 (from
>= list
->ranges
[i
].from
- 1 || to
<= list
->ranges
[i
].to
+ 1)) {
765 if(from
<= list
->ranges
[i
].from
)
766 list
->ranges
[i
].from
= from
;
767 if(to
>= list
->ranges
[i
].to
)
768 list
->ranges
[i
].to
= to
;
769 return maybeMergeRanges(list
, i
);
771 return insertRange(from
, to
, list
, i
);
774 /* Return the amount of physical memory on the box, -1 if unknown or
776 #if defined(__linux__)
778 #include <sys/sysinfo.h>
789 if(info
.totalram
<= 0x7fffffff / info
.mem_unit
)
790 return (int)(info
.totalram
* info
.mem_unit
);
795 #elif defined(__FreeBSD__)
797 #include <sys/sysctl.h>
805 len
= sizeof(membytes
);
806 res
= sysctlbyname("hw.physmem", &membytes
, &len
, NULL
, 0);