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 *dst
, const char *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);
335 case EDOSHUTDOWN
: s
= "Immediate shutdown requested"; break;
336 case EDOGRACEFUL
: s
= "Graceful shutdown requested"; break;
337 case EDOTIMEOUT
: s
= "Timeout"; break;
338 case ECLIENTRESET
: s
= "Connection reset by client"; break;
339 case ESYNTAX
: s
= "Incorrect syntax"; break;
340 case EDNS_HOST_NOT_FOUND
: s
= "Host not found"; break;
341 case EDNS_NO_ADDRESS
: s
= "No address"; break;
342 case EDNS_NO_RECOVERY
: s
= "Permanent name server failure"; break;
343 case EDNS_TRY_AGAIN
: s
= "Temporary name server failure"; break;
344 case EDNS_INVALID
: s
= "Invalid reply from name server"; break;
345 case EDNS_UNSUPPORTED
: s
= "Unsupported DNS reply"; break;
346 case EDNS_FORMAT
: s
= "Invalid DNS query"; break;
347 case EDNS_REFUSED
: s
= "DNS query refused by server"; break;
348 case EDNS_CNAME_LOOP
: s
= "DNS CNAME loop"; break;
350 case ESOCKS_PROTOCOL
: s
= "SOCKS protocol error"; break;
351 case ESOCKS_REJECT_FAIL
: s
= "SOCKS request rejected or failed"; break;
352 case ESOCKS_REJECT_IDENTD
: s
= "SOCKS request rejected: "
353 "server couldn't connect to identd";
354 case ESOCKS_REJECT_UID_MISMATCH
: s
= "SOCKS request rejected: "
357 case ESOCKS5_BASE
: s
= "SOCKS success"; break;
358 case ESOCKS5_BASE
+ 1: s
= "General SOCKS server failure"; break;
359 case ESOCKS5_BASE
+ 2: s
= "SOCKS connection not allowed"; break;
360 case ESOCKS5_BASE
+ 3: s
= "SOCKS error: network unreachable"; break;
361 case ESOCKS5_BASE
+ 4: s
= "SOCKS error: host unreachable"; break;
362 case ESOCKS5_BASE
+ 5: s
= "SOCKS error: connection refused"; break;
363 case ESOCKS5_BASE
+ 6: s
= "SOCKS error: TTL expired"; break;
364 case ESOCKS5_BASE
+ 7: s
= "SOCKS command not supported"; break;
365 case ESOCKS5_BASE
+ 8: s
= "SOCKS error: address type not supported";
368 case EUNKNOWN
: s
= "Unknown error"; break;
369 default: s
= NULL
; break;
371 if(!s
) s
= strerror(e
);
374 if(e
>= WSABASEERR
&& e
<= WSABASEERR
+ 2000) {
375 /* This should be okay, as long as the caller discards the
376 pointer before another error occurs. */
377 static char buf
[200];
378 snprintf(buf
, 200, "Winsock error %d", e
);
383 if(!s
) s
= "Unknown error";
387 /* Like mktime(3), but UTC rather than local time */
388 #if defined(HAVE_TIMEGM)
390 mktime_gmt(struct tm
*tm
)
394 #elif defined(HAVE_TM_GMTOFF)
396 mktime_gmt(struct tm
*tm
)
407 return t
+ ltm
->tm_gmtoff
;
409 #elif defined(HAVE_TZSET)
411 /* Taken from the Linux timegm(3) man page. */
413 mktime_gmt(struct tm
*tm
)
431 mktime_gmt(struct tm
*tm
)
435 static char *old_tz
= NULL
;
444 old_tz
= sprintf_a("TZ=%s", tz
);
446 old_tz
= strdup("TZ"); /* XXX - non-portable? */
454 #error no mktime_gmt implementation on this platform
459 expandTilde(AtomPtr filename
)
466 if(filename
== NULL
|| filename
->length
< 1 ||
467 filename
->string
[0] != '~' || filename
->string
[1] != '/')
470 home
= getenv("HOME");
472 do_log(L_ERROR
, "Could not expand $HOME.\n");
476 buf
= malloc(len
+ 1 + 1 + filename
->length
- 2);
478 do_log(L_ERROR
, "Could not allocate buffer.\n");
482 memcpy(buf
, home
, len
);
483 if(buf
[len
- 1] != '/')
485 memcpy(buf
+ len
, filename
->string
+ 2, filename
->length
- 2);
486 len
+= filename
->length
- 2;
487 ret
= internAtomN(buf
, len
);
490 releaseAtom(filename
);
496 do_daemonise(int noclose
)
498 /* If we cannot call fork(), then we cannot become a daemon. */
506 do_log_error(L_ERROR
, errno
, "Couldn't fork");
520 do_log_error(L_ERROR
, errno
, "Couldn't create new session");
526 do_daemonise(int noclose
)
528 do_log(L_ERROR
, "Cannot daemonise on this platform");
535 writePid(char *pidfile
)
540 fd
= open(pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
542 do_log_error(L_ERROR
, errno
,
543 "Couldn't create pid file %s", pidfile
);
546 n
= snprintf(buf
, 16, "%ld\n", (long)getpid());
547 if(n
< 0 || n
>= 16) {
550 do_log(L_ERROR
, "Couldn't format pid.\n");
553 rc
= write(fd
, buf
, n
);
557 do_log_error(L_ERROR
, errno
, "Couldn't write pid");
565 static const char b64
[64] =
566 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
568 /* "/" replaced with "-" */
569 static const char b64fss
[64] =
570 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
573 b64cpy(char *restrict dst
, const char *restrict src
, int n
, int fss
)
575 const char *b
= fss
? b64fss
: b64
;
579 for(i
= 0; i
< n
; i
+= 3) {
580 unsigned char a0
, a1
, a2
;
582 a1
= i
< n
- 1 ? src
[i
+ 1] : 0;
583 a2
= i
< n
- 2 ? src
[i
+ 2] : 0;
584 dst
[j
++] = b
[(a0
>> 2) & 0x3F];
585 dst
[j
++] = b
[((a0
<< 4) & 0x30) | ((a1
>> 4) & 0x0F)];
587 dst
[j
++] = b
[((a1
<< 2) & 0x3C) | ((a2
>> 6) & 0x03)];
591 dst
[j
++] = b
[a2
& 0x3F];
599 b64cmp(const char *a
, int an
, const char *b
, int bn
)
606 if((bn
+ 2) / 3 != an
/ 4)
611 b64cpy(buf
, b
, bn
, 0);
612 r
= memcmp(buf
, a
, an
);
618 makeIntList(int size
)
624 list
= malloc(sizeof(IntListRec
));
628 list
->ranges
= malloc(size
* sizeof(IntRangeRec
));
629 if(list
->ranges
== NULL
) {
640 destroyIntList(IntListPtr list
)
647 intListMember(int n
, IntListPtr list
)
649 int lo
= 0, hi
= list
->length
- 1;
653 if(list
->ranges
[mid
].from
> n
)
655 else if(list
->ranges
[mid
].to
< n
)
664 deleteRange(IntListPtr list
, int i
)
666 assert(list
->length
> i
);
667 if(list
->length
> i
+ 1)
668 memmove(list
->ranges
+ i
, list
->ranges
+ i
+ 1,
669 (list
->length
- i
- 1) * sizeof(IntRangeRec
));
675 insertRange(int from
, int to
, IntListPtr list
, int i
)
677 assert(i
>= 0 && i
<= list
->length
);
678 assert(i
== 0 || list
->ranges
[i
- 1].to
< from
- 1);
679 assert(i
== list
->length
|| list
->ranges
[i
].from
> to
+ 1);
681 if(list
->length
>= list
->size
) {
682 int newsize
= list
->size
* 2 + 1;
683 IntRangePtr newranges
=
684 realloc(list
->ranges
, newsize
* sizeof(IntRangeRec
));
685 if(newranges
== NULL
)
687 list
->size
= newsize
;
688 list
->ranges
= newranges
;
692 memmove(list
->ranges
+ i
+ 1, list
->ranges
+ i
,
695 list
->ranges
[i
].from
= from
;
696 list
->ranges
[i
].to
= to
;
701 maybeMergeRanges(IntListPtr list
, int i
)
705 while(i
> 0 && list
->ranges
[i
].from
<= list
->ranges
[i
- 1].to
+ 1) {
706 list
->ranges
[i
- 1].from
=
707 MIN(list
->ranges
[i
- 1].from
, list
->ranges
[i
].from
);
708 list
->ranges
[i
- 1].to
=
709 MAX(list
->ranges
[i
- 1].to
, list
->ranges
[i
].to
);
710 rc
= deleteRange(list
, i
);
711 if(rc
< 0) return -1;
715 while(i
< list
->length
- 1 &&
716 list
->ranges
[i
].to
>= list
->ranges
[i
+ 1].from
- 1) {
717 list
->ranges
[i
+ 1].from
=
718 MIN(list
->ranges
[i
+ 1].from
, list
->ranges
[i
].from
);
719 list
->ranges
[i
- 1].to
=
720 MAX(list
->ranges
[i
+ 1].to
, list
->ranges
[i
].to
);
721 rc
= deleteRange(list
, i
);
722 if(rc
< 0) return -1;
728 intListCons(int from
, int to
, IntListPtr list
)
732 /* Don't bother with the dichotomy. */
733 for(i
= 0; i
< list
->length
; i
++) {
734 if(list
->ranges
[i
].to
>= from
- 1)
738 if(i
< list
->length
&&
739 (from
>= list
->ranges
[i
].from
- 1 || to
<= list
->ranges
[i
].to
+ 1)) {
740 if(from
<= list
->ranges
[i
].from
)
741 list
->ranges
[i
].from
= from
;
742 if(to
>= list
->ranges
[i
].to
)
743 list
->ranges
[i
].to
= to
;
744 return maybeMergeRanges(list
, i
);
746 return insertRange(from
, to
, list
, i
);