2 Copyright (c) 2003-2006 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
, ...)
44 va_start(args
, format
);
46 rc
= vsnprintf(buf
+ n
, len
- n
, format
, args
);
48 if(rc
>= 0 && n
+ rc
<= len
)
55 snnprint_n(char *restrict buf
, int n
, int len
, const char *s
, int slen
)
59 while(i
< slen
&& n
< len
)
68 strcmp_n(const char *string
, const char *buf
, int n
)
72 while(string
[i
] != '\0' && i
< n
) {
73 if(string
[i
] < buf
[i
])
75 else if(string
[i
] > buf
[i
])
79 if(string
[i
] == '\0' || i
== n
)
90 if(c
>= 'A' && c
<= 'Z') return 1;
91 if(c
>= 'a' && c
<= 'z') return 1;
98 if(c
>= '0' && c
<= '9')
106 if(a
>= 'A' && a
<= 'Z')
113 lwrcpy(char *restrict dst
, const char *restrict src
, int n
)
116 for(i
= 0; i
< n
; i
++)
117 dst
[i
] = lwr(src
[i
]);
122 lwrcmp(const char *as
, const char *bs
, int n
)
125 for(i
= 0; i
< n
; i
++) {
126 char a
= lwr(as
[i
]), b
= lwr(bs
[i
]);
136 strcasecmp_n(const char *string
, const char *buf
, int n
)
140 while(string
[i
] != '\0' && i
< n
) {
141 char a
= lwr(string
[i
]), b
= lwr(buf
[i
]);
148 if(string
[i
] == '\0' && i
== n
)
157 atoi_n(const char *restrict string
, int n
, int len
, int *value_return
)
162 if(i
>= len
|| !digit(string
[i
]))
165 while(i
< len
&& digit(string
[i
])) {
166 val
= val
* 10 + (string
[i
] - '0');
174 isWhitespace(const char *string
)
176 while(*string
!= '\0') {
177 if(*string
== ' ' || *string
== '\t')
187 memrchr(const void *s
, int c
, size_t n
)
189 const unsigned char *ss
= s
;
190 unsigned char cc
= c
;
192 for(i
= n
- 1; i
>= 0; i
--)
194 return (void*)(ss
+ i
);
202 if(h
>= '0' && h
<= '9')
204 else if(h
>= 'a' && h
<= 'f')
206 else if(h
>= 'A' && h
<= 'F')
259 vsprintf_a(const char *f
, va_list args
)
263 rc
= vasprintf(&r
, f
, args
);
271 vsprintf_a(const char *f
, va_list args
)
277 n
= vsnprintf(buf
, 64, f
, args
);
278 if(n
>= 0 && n
< 64) {
279 return strdup_n(buf
, n
);
287 string
= malloc(size
);
290 n
= vsnprintf(string
, size
, f
, args
);
291 if(n
>= 0 && n
< size
)
306 sprintf_a(const char *f
, ...)
311 s
= vsprintf_a(f
, args
);
317 hash(unsigned int seed
, const void *restrict key
, int key_size
,
318 unsigned int hash_size
)
324 for(i
= 0; i
< key_size
; i
++)
325 h
= (h
<< 5) + (h
>> (hash_size
- 5)) +
326 ((unsigned char*)key
)[i
];
327 return h
& ((1 << hash_size
) - 1);
337 case EDOSHUTDOWN
: s
= "Immediate shutdown requested"; break;
338 case EDOGRACEFUL
: s
= "Graceful shutdown requested"; break;
339 case EDOTIMEOUT
: s
= "Timeout"; break;
340 case ECLIENTRESET
: s
= "Connection reset by client"; break;
341 case ESYNTAX
: s
= "Incorrect syntax"; break;
342 case EREDIRECTOR
: s
= "Redirector error"; break;
343 case EDNS_HOST_NOT_FOUND
: s
= "Host not found"; break;
344 case EDNS_NO_ADDRESS
: s
= "No address"; break;
345 case EDNS_NO_RECOVERY
: s
= "Permanent name server failure"; break;
346 case EDNS_TRY_AGAIN
: s
= "Temporary name server failure"; break;
347 case EDNS_INVALID
: s
= "Invalid reply from name server"; break;
348 case EDNS_UNSUPPORTED
: s
= "Unsupported DNS reply"; break;
349 case EDNS_FORMAT
: s
= "Invalid DNS query"; break;
350 case EDNS_REFUSED
: s
= "DNS query refused by server"; break;
351 case EDNS_CNAME_LOOP
: s
= "DNS CNAME loop"; break;
353 case ESOCKS_PROTOCOL
: s
= "SOCKS protocol error"; break;
354 case ESOCKS_REJECT_FAIL
: s
= "SOCKS request rejected or failed"; break;
355 case ESOCKS_REJECT_IDENTD
: s
= "SOCKS request rejected: "
356 "server couldn't connect to identd";
357 case ESOCKS_REJECT_UID_MISMATCH
: s
= "SOCKS request rejected: "
360 case ESOCKS5_BASE
: s
= "SOCKS success"; break;
361 case ESOCKS5_BASE
+ 1: s
= "General SOCKS server failure"; break;
362 case ESOCKS5_BASE
+ 2: s
= "SOCKS connection not allowed"; break;
363 case ESOCKS5_BASE
+ 3: s
= "SOCKS error: network unreachable"; break;
364 case ESOCKS5_BASE
+ 4: s
= "SOCKS error: host unreachable"; break;
365 case ESOCKS5_BASE
+ 5: s
= "SOCKS error: connection refused"; break;
366 case ESOCKS5_BASE
+ 6: s
= "SOCKS error: TTL expired"; break;
367 case ESOCKS5_BASE
+ 7: s
= "SOCKS command not supported"; break;
368 case ESOCKS5_BASE
+ 8: s
= "SOCKS error: address type not supported";
371 case EUNKNOWN
: s
= "Unknown error"; break;
372 default: s
= NULL
; break;
374 if(!s
) s
= strerror(e
);
377 if(e
>= WSABASEERR
&& e
<= WSABASEERR
+ 2000) {
378 /* This should be okay, as long as the caller discards the
379 pointer before another error occurs. */
380 snprintf(buf
, 20, "Winsock error %d", e
);
386 snprintf(buf
, 20, "Unknown error %d", e
);
392 /* Like mktime(3), but UTC rather than local time */
393 #if defined(HAVE_TIMEGM)
395 mktime_gmt(struct tm
*tm
)
399 #elif defined(HAVE_TM_GMTOFF)
401 mktime_gmt(struct tm
*tm
)
412 return t
+ ltm
->tm_gmtoff
;
414 #elif defined(HAVE_TZSET)
416 /* Taken from the Linux timegm(3) man page. */
418 mktime_gmt(struct tm
*tm
)
436 mktime_gmt(struct tm
*tm
)
440 static char *old_tz
= NULL
;
449 old_tz
= sprintf_a("TZ=%s", tz
);
451 old_tz
= strdup("TZ"); /* XXX - non-portable? */
459 #error no mktime_gmt implementation on this platform
464 expandTilde(AtomPtr filename
)
471 if(filename
== NULL
|| filename
->length
< 1 ||
472 filename
->string
[0] != '~' || filename
->string
[1] != '/')
475 home
= getenv("HOME");
480 buf
= malloc(len
+ 1 + 1 + filename
->length
- 2);
482 do_log(L_ERROR
, "Could not allocate buffer.\n");
486 memcpy(buf
, home
, len
);
487 if(buf
[len
- 1] != '/')
489 memcpy(buf
+ len
, filename
->string
+ 2, filename
->length
- 2);
490 len
+= filename
->length
- 2;
491 ret
= internAtomN(buf
, len
);
494 releaseAtom(filename
);
500 do_daemonise(int noclose
)
509 do_log_error(L_ERROR
, errno
, "Couldn't fork");
523 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
531 do_daemonise(int noclose
)
533 do_log(L_ERROR
, "Cannot daemonise on this platform");
540 writePid(char *pidfile
)
545 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
547 do_log_error(L_ERROR
, errno
,
548 "Couldn't create pid file %s", pidfile
);
551 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
552 if(n
< 0 || n
>= 16) {
555 do_log(L_ERROR
, "Couldn't format pid.\n");
558 rc
= write(fd
, buf
, n
);
562 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
570 static const char b64
[64] =
571 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
573 /* "/" replaced with "-" */
574 static const char b64fss
[64] =
575 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
578 b64cpy(char *restrict dst
, const char *restrict src
, int n
, int fss
)
580 const char *b
= fss
? b64fss
: b64
;
584 for(i
= 0; i
< n
; i
+= 3) {
585 unsigned char a0
, a1
, a2
;
587 a1
= i
< n
- 1 ? src
[i
+ 1] : 0;
588 a2
= i
< n
- 2 ? src
[i
+ 2] : 0;
589 dst
[j
++] = b
[(a0
>> 2) & 0x3F];
590 dst
[j
++] = b
[((a0
<< 4) & 0x30) | ((a1
>> 4) & 0x0F)];
592 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
596 dst
[j
++] = b
[a2
& 0x3F];
604 b64cmp(const char *restrict a
, int an
, const char *restrict b
, int bn
)
611 if((bn
+ 2) / 3 != an
/ 4)
616 b64cpy(buf
, b
, bn
, 0);
617 r
= memcmp(buf
, a
, an
);
623 makeIntList(int size
)
629 list
= malloc(sizeof(IntListRec
));
633 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
634 if(list
->ranges
== NULL
) {
645 destroyIntList(IntListPtr list
)
652 intListMember(int n
, IntListPtr list
)
654 int lo
= 0, hi
= list
->length
- 1;
658 if(list
->ranges
[mid
].from
> n
)
660 else if(list
->ranges
[mid
].to
< n
)
669 deleteRange(IntListPtr list
, int i
)
671 assert(list
->length
> i
);
672 if(list
->length
> i
+ 1)
673 memmove(list
->ranges
+ i
, list
->ranges
+ i
+ 1,
674 (list
->length
- i
- 1) * sizeof(IntRangeRec
));
680 insertRange(int from
, int to
, IntListPtr list
, int i
)
682 assert(i
>= 0 && i
<= list
->length
);
683 assert(i
== 0 || list
->ranges
[i
- 1].to
< from
- 1);
684 assert(i
== list
->length
|| list
->ranges
[i
].from
> to
+ 1);
686 if(list
->length
>= list
->size
) {
687 int newsize
= list
->size
* 2 + 1;
688 IntRangePtr newranges
=
689 realloc(list
->ranges
, newsize
* sizeof(IntRangeRec
));
690 if(newranges
== NULL
)
692 list
->size
= newsize
;
693 list
->ranges
= newranges
;
697 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
700 list
->ranges
[i
].from
= from
;
701 list
->ranges
[i
].to
= to
;
706 maybeMergeRanges(IntListPtr list
, int i
)
710 while(i
> 0 && list
->ranges
[i
].from
<= list
->ranges
[i
- 1].to
+ 1) {
711 list
->ranges
[i
- 1].from
=
712 MIN(list
->ranges
[i
- 1].from
, list
->ranges
[i
].from
);
713 list
->ranges
[i
- 1].to
=
714 MAX(list
->ranges
[i
- 1].to
, list
->ranges
[i
].to
);
715 rc
= deleteRange(list
, i
);
716 if(rc
< 0) return -1;
720 while(i
< list
->length
- 1 &&
721 list
->ranges
[i
].to
>= list
->ranges
[i
+ 1].from
- 1) {
722 list
->ranges
[i
+ 1].from
=
723 MIN(list
->ranges
[i
+ 1].from
, list
->ranges
[i
].from
);
724 list
->ranges
[i
- 1].to
=
725 MAX(list
->ranges
[i
+ 1].to
, list
->ranges
[i
].to
);
726 rc
= deleteRange(list
, i
);
727 if(rc
< 0) return -1;
733 intListCons(int from
, int to
, IntListPtr list
)
737 /* Don't bother with the dichotomy. */
738 for(i
= 0; i
< list
->length
; i
++) {
739 if(list
->ranges
[i
].to
>= from
- 1)
743 if(i
< list
->length
&&
744 (from
>= list
->ranges
[i
].from
- 1 || to
<= list
->ranges
[i
].to
+ 1)) {
745 if(from
<= list
->ranges
[i
].from
)
746 list
->ranges
[i
].from
= from
;
747 if(to
>= list
->ranges
[i
].to
)
748 list
->ranges
[i
].to
= to
;
749 return maybeMergeRanges(list
, i
);
751 return insertRange(from
, to
, list
, i
);