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
)
271 rc
= vasprintf(&r
, f
, args
);
279 vsprintf_a(const char *f
, va_list args
)
285 n
= vsnprintf(buf
, 64, f
, args
);
286 if(n
>= 0 && n
< 64) {
287 return strdup_n(buf
, n
);
295 string
= malloc(size
);
298 n
= vsnprintf(string
, size
, f
, args
);
299 if(n
>= 0 && n
< size
)
314 sprintf_a(const char *f
, ...)
319 s
= vsprintf_a(f
, args
);
325 hash(unsigned int seed
, const void *restrict key
, int key_size
,
326 unsigned int hash_size
)
332 for(i
= 0; i
< key_size
; i
++)
333 h
= (h
<< 5) + (h
>> (hash_size
- 5)) +
334 ((unsigned char*)key
)[i
];
335 return h
& ((1 << hash_size
) - 1);
345 case EDOSHUTDOWN
: s
= "Immediate shutdown requested"; break;
346 case EDOGRACEFUL
: s
= "Graceful shutdown requested"; break;
347 case EDOTIMEOUT
: s
= "Timeout"; break;
348 case ECLIENTRESET
: s
= "Connection reset by client"; break;
349 case ESYNTAX
: s
= "Incorrect syntax"; break;
350 case EREDIRECTOR
: s
= "Redirector error"; break;
351 case EDNS_HOST_NOT_FOUND
: s
= "Host not found"; break;
352 case EDNS_NO_ADDRESS
: s
= "No address"; break;
353 case EDNS_NO_RECOVERY
: s
= "Permanent name server failure"; break;
354 case EDNS_TRY_AGAIN
: s
= "Temporary name server failure"; break;
355 case EDNS_INVALID
: s
= "Invalid reply from name server"; break;
356 case EDNS_UNSUPPORTED
: s
= "Unsupported DNS reply"; break;
357 case EDNS_FORMAT
: s
= "Invalid DNS query"; break;
358 case EDNS_REFUSED
: s
= "DNS query refused by server"; break;
359 case EDNS_CNAME_LOOP
: s
= "DNS CNAME loop"; break;
361 case ESOCKS_PROTOCOL
: s
= "SOCKS protocol error"; break;
362 case ESOCKS_REJECT_FAIL
: s
= "SOCKS request rejected or failed"; break;
363 case ESOCKS_REJECT_IDENTD
: s
= "SOCKS request rejected: "
364 "server couldn't connect to identd";
365 case ESOCKS_REJECT_UID_MISMATCH
: s
= "SOCKS request rejected: "
368 case ESOCKS5_BASE
: s
= "SOCKS success"; break;
369 case ESOCKS5_BASE
+ 1: s
= "General SOCKS server failure"; break;
370 case ESOCKS5_BASE
+ 2: s
= "SOCKS connection not allowed"; break;
371 case ESOCKS5_BASE
+ 3: s
= "SOCKS error: network unreachable"; break;
372 case ESOCKS5_BASE
+ 4: s
= "SOCKS error: host unreachable"; break;
373 case ESOCKS5_BASE
+ 5: s
= "SOCKS error: connection refused"; break;
374 case ESOCKS5_BASE
+ 6: s
= "SOCKS error: TTL expired"; break;
375 case ESOCKS5_BASE
+ 7: s
= "SOCKS command not supported"; break;
376 case ESOCKS5_BASE
+ 8: s
= "SOCKS error: address type not supported";
379 case EUNKNOWN
: s
= "Unknown error"; break;
380 default: s
= NULL
; break;
382 if(!s
) s
= strerror(e
);
385 if(e
>= WSABASEERR
&& e
<= WSABASEERR
+ 2000) {
386 /* This should be okay, as long as the caller discards the
387 pointer before another error occurs. */
388 snprintf(buf
, 20, "Winsock error %d", e
);
394 snprintf(buf
, 20, "Unknown error %d", e
);
400 /* Like mktime(3), but UTC rather than local time */
401 #if defined(HAVE_TIMEGM)
403 mktime_gmt(struct tm
*tm
)
407 #elif defined(HAVE_TM_GMTOFF)
409 mktime_gmt(struct tm
*tm
)
420 return t
+ ltm
->tm_gmtoff
;
422 #elif defined(HAVE_TZSET)
424 /* Taken from the Linux timegm(3) man page. */
426 mktime_gmt(struct tm
*tm
)
444 mktime_gmt(struct tm
*tm
)
448 static char *old_tz
= NULL
;
457 old_tz
= sprintf_a("TZ=%s", tz
);
459 old_tz
= strdup("TZ"); /* XXX - non-portable? */
467 #error no mktime_gmt implementation on this platform
472 expandTilde(AtomPtr filename
)
479 if(filename
== NULL
|| filename
->length
< 1 ||
480 filename
->string
[0] != '~' || filename
->string
[1] != '/')
483 home
= getenv("HOME");
488 buf
= malloc(len
+ 1 + 1 + filename
->length
- 2);
490 do_log(L_ERROR
, "Could not allocate buffer.\n");
494 memcpy(buf
, home
, len
);
495 if(buf
[len
- 1] != '/')
497 memcpy(buf
+ len
, filename
->string
+ 2, filename
->length
- 2);
498 len
+= filename
->length
- 2;
499 ret
= internAtomN(buf
, len
);
502 releaseAtom(filename
);
508 do_daemonise(int noclose
)
517 do_log_error(L_ERROR
, errno
, "Couldn't fork");
531 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
539 do_daemonise(int noclose
)
541 do_log(L_ERROR
, "Cannot daemonise on this platform");
548 writePid(char *pidfile
)
553 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
555 do_log_error(L_ERROR
, errno
,
556 "Couldn't create pid file %s", pidfile
);
559 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
560 if(n
< 0 || n
>= 16) {
563 do_log(L_ERROR
, "Couldn't format pid.\n");
566 rc
= write(fd
, buf
, n
);
570 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
578 static const char b64
[64] =
579 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
581 /* "/" replaced with "-" */
582 static const char b64fss
[64] =
583 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
586 b64cpy(char *restrict dst
, const char *restrict src
, int n
, int fss
)
588 const char *b
= fss
? b64fss
: b64
;
592 for(i
= 0; i
< n
; i
+= 3) {
593 unsigned char a0
, a1
, a2
;
595 a1
= i
< n
- 1 ? src
[i
+ 1] : 0;
596 a2
= i
< n
- 2 ? src
[i
+ 2] : 0;
597 dst
[j
++] = b
[(a0
>> 2) & 0x3F];
598 dst
[j
++] = b
[((a0
<< 4) & 0x30) | ((a1
>> 4) & 0x0F)];
600 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
604 dst
[j
++] = b
[a2
& 0x3F];
612 b64cmp(const char *restrict a
, int an
, const char *restrict b
, int bn
)
619 if((bn
+ 2) / 3 != an
/ 4)
624 b64cpy(buf
, b
, bn
, 0);
625 r
= memcmp(buf
, a
, an
);
631 makeIntList(int size
)
637 list
= malloc(sizeof(IntListRec
));
641 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
642 if(list
->ranges
== NULL
) {
653 destroyIntList(IntListPtr list
)
660 intListMember(int n
, IntListPtr list
)
662 int lo
= 0, hi
= list
->length
- 1;
666 if(list
->ranges
[mid
].from
> n
)
668 else if(list
->ranges
[mid
].to
< n
)
677 deleteRange(IntListPtr list
, int i
)
679 assert(list
->length
> i
);
680 if(list
->length
> i
+ 1)
681 memmove(list
->ranges
+ i
, list
->ranges
+ i
+ 1,
682 (list
->length
- i
- 1) * sizeof(IntRangeRec
));
688 insertRange(int from
, int to
, IntListPtr list
, int i
)
690 assert(i
>= 0 && i
<= list
->length
);
691 assert(i
== 0 || list
->ranges
[i
- 1].to
< from
- 1);
692 assert(i
== list
->length
|| list
->ranges
[i
].from
> to
+ 1);
694 if(list
->length
>= list
->size
) {
695 int newsize
= list
->size
* 2 + 1;
696 IntRangePtr newranges
=
697 realloc(list
->ranges
, newsize
* sizeof(IntRangeRec
));
698 if(newranges
== NULL
)
700 list
->size
= newsize
;
701 list
->ranges
= newranges
;
705 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
708 list
->ranges
[i
].from
= from
;
709 list
->ranges
[i
].to
= to
;
714 maybeMergeRanges(IntListPtr list
, int i
)
718 while(i
> 0 && list
->ranges
[i
].from
<= list
->ranges
[i
- 1].to
+ 1) {
719 list
->ranges
[i
- 1].from
=
720 MIN(list
->ranges
[i
- 1].from
, list
->ranges
[i
].from
);
721 list
->ranges
[i
- 1].to
=
722 MAX(list
->ranges
[i
- 1].to
, list
->ranges
[i
].to
);
723 rc
= deleteRange(list
, i
);
724 if(rc
< 0) return -1;
728 while(i
< list
->length
- 1 &&
729 list
->ranges
[i
].to
>= list
->ranges
[i
+ 1].from
- 1) {
730 list
->ranges
[i
+ 1].from
=
731 MIN(list
->ranges
[i
+ 1].from
, list
->ranges
[i
].from
);
732 list
->ranges
[i
- 1].to
=
733 MAX(list
->ranges
[i
+ 1].to
, list
->ranges
[i
].to
);
734 rc
= deleteRange(list
, i
);
735 if(rc
< 0) return -1;
741 intListCons(int from
, int to
, IntListPtr list
)
745 /* Don't bother with the dichotomy. */
746 for(i
= 0; i
< list
->length
; i
++) {
747 if(list
->ranges
[i
].to
>= from
- 1)
751 if(i
< list
->length
&&
752 (from
>= list
->ranges
[i
].from
- 1 || to
<= list
->ranges
[i
].to
+ 1)) {
753 if(from
<= list
->ranges
[i
].from
)
754 list
->ranges
[i
].from
= from
;
755 if(to
>= list
->ranges
[i
].to
)
756 list
->ranges
[i
].to
= to
;
757 return maybeMergeRanges(list
, i
);
759 return insertRange(from
, to
, list
, i
);
762 /* Return the amount of physical memory on the box, -1 if unknown or
764 #if defined(__linux__)
766 #include <sys/sysinfo.h>
777 if(info
.totalram
<= 0x7fffffff / info
.mem_unit
)
778 return (int)(info
.totalram
* info
.mem_unit
);
783 #elif defined(__FreeBSD__)
785 #include <sys/sysctl.h>
793 len
= sizeof(membytes
);
794 res
= sysctlbyname("hw.physmem", &membytes
, &len
, NULL
, 0);