/* Miscellaneous routines.
*
* IRC Services is copyright (c) 1996-2007 Andrew Church.
* E-mail: <achurch@achurch.org>
* Parts written by Andrew Kempe and others.
* This program is free but copyrighted software; see the file COPYING for
* details.
*/
#include "services.h"
#include <fcntl.h>
/*************************************************************************/
/* Table used by irc_tolower(). This table follows the RFC 1459
* requirement that [ \ ] and { | } be considered equivalent. Protocols
* which do not follow this requirement should modify the table
* accordingly.
*/
unsigned char irc_lowertable[256] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x5E,0x5F,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,
0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,
0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,
0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,
0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,
0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,
0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,
0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
};
/*************************************************************************/
/* irc_tolower: Like toupper/tolower, but for nicknames and channel names;
* the RFC requires that '[' and '{', '\' and '|', ']' and '}'
* be pairwise equivalent in such names. Declared inline for
* irc_str[n]icmp()'s benefit.
*/
inline unsigned char irc_tolower(char c)
{
return irc_lowertable[(uint8)c];
}
/*************************************************************************/
/* irc_str[n]icmp: Like str[n]icmp, but for nicknames and channel names. */
int irc_stricmp(const char *s1, const char *s2)
{
register char c1, c2;
while ((c1 = (char)irc_tolower(*s1)) == (c2 = (char)irc_tolower(*s2))) {
if (c1 == 0)
return 0;
s1++;
s2++;
}
return c1<c2 ? -1 : 1;
}
int irc_strnicmp(const char *s1, const char *s2, int max)
{
register char c1, c2;
if (max <= 0)
return 0;
while ((c1 = (char)irc_tolower(*s1)) == (c2 = (char)irc_tolower(*s2))) {
if (c1 == 0 || --max <= 0)
return 0;
s1++;
s2++;
}
return c1<c2 ? -1 : 1;
}
/*************************************************************************/
/* strscpy: Copy at most len-1 characters from a string to a buffer, and
* add a null terminator after the last character copied.
*/
char *strscpy(char *d, const char *s, size_t len)
{
char *d_orig = d;
if (!len)
return d;
while (--len && (*d++ = *s++))
;
*d = 0;
return d_orig;
}
/*************************************************************************/
/* strmove: Like strcpy(), but handles overlapping regions of memory
* properly. The str*() analog of memmove().
*/
char *strmove(char *d, char *s)
{
memmove(d, s, strlen(s)+1);
return d;
}
/*************************************************************************/
/* stristr: Search case-insensitively for string s2 within string s1,
* returning the first occurrence of s2 or NULL if s2 was not
* found.
*/
char *stristr(const char *s1, const char *s2)
{
register const char *s = s1, *d = s2;
while (*s1) {
if (tolower(*s1) == tolower(*d)) {
s1++;
d++;
if (*d == 0)
return (char *)s;
} else {
s = ++s1;
d = s2;
}
}
return NULL;
}
/*************************************************************************/
/* strupper, strlower: Convert a string to upper or lower case.
*/
char *strupper(char *s)
{
char *t = s;
while (*t) {
*t = toupper(*t);
t++;
}
return s;
}
char *strlower(char *s)
{
char *t = s;
while (*t) {
*t = tolower(*t);
t++;
}
return s;
}
/*************************************************************************/
/* strnrepl: Replace occurrences of `old' with `new' in string `s'. Stop
* replacing if a replacement would cause the string to exceed
* `size' bytes (including the null terminator). Return the
* string.
*/
char *strnrepl(char *s, int32 size, const char *old, const char *new)
{
char *ptr = s;
int32 left = strlen(s);
int32 avail = size - (left+1);
int32 oldlen = strlen(old);
int32 newlen = strlen(new);
int32 diff = newlen - oldlen;
while (left >= oldlen) {
if (strncmp(ptr, old, oldlen) != 0) {
left--;
ptr++;
continue;
}
if (diff > avail)
break;
if (diff != 0)
memmove(ptr+oldlen+diff, ptr+oldlen, left+1);
strncpy(ptr, new, newlen);
ptr += newlen;
left -= oldlen;
}
return s;
}
/*************************************************************************/
/* strtok_remaining: Return the result of strtok(NULL, "") with any
* leading or trailing whitespace discarded. If nothing
* but whitespace is left, return NULL.
*/
char *strtok_remaining(void)
{
char *s = strtok(NULL, ""), *t;
if (s) {
while (isspace(*s))
s++;
t = s + strlen(s)-1;
while (t >= s && isspace(*t))
*t-- = 0;
if (!*s)
return NULL;
}
return s;
}
/*************************************************************************/
/*************************************************************************/
/* merge_args: Take an argument count and argument vector and merge them
* into a single string in which each argument is separated by
* a space.
*/
char *merge_args(int argc, char **argv)
{
int i;
static char s[4096];
char *t;
t = s;
for (i = 0; i < argc; i++)
t += snprintf(t, sizeof(s)-(t-s), "%s%s", *argv++, (i<argc-1) ? " " : "");
return s;
}
/*************************************************************************/
/*************************************************************************/
/* match_wild: Attempt to match a string to a pattern which might contain
* '*' or '?' wildcards. Return 1 if the string matches the
* pattern, 0 if not.
*/
static int do_match_wild(const char *pattern, const char *str, int docase)
{
char c;
const char *s;
/* This WILL eventually terminate: either by *pattern == 0, or by a
* trailing '*' (or "*???..."). */
for (;;) {
switch (c = *pattern++) {
case 0:
if (!*str)
return 1;
return 0;
case '?':
if (!*str)
return 0;
str++;
break;
case '*':
while (*pattern == '?') {
if (!*str)
return 0;
str++; /* skip a character for each '?' */
pattern++;
}
if (!*pattern)
return 1; /* trailing '*' matches everything else */
s = str;
while (*s) {
if ((docase ? (*s==*pattern) : (tolower(*s)==tolower(*pattern)))
&& do_match_wild(pattern+1, s+1, docase))
return 1;
s++;
}
break;
default:
if (docase ? (*str != c) : (tolower(*str) != tolower(c)))
return 0;
str++;
break;
} /* switch */
}
/* not reached */
}
int match_wild(const char *pattern, const char *str)
{
return do_match_wild(pattern, str, 1);
}
int match_wild_nocase(const char *pattern, const char *str)
{
return do_match_wild(pattern, str, 0);
}
/*************************************************************************/
/*************************************************************************/
/* Tables used by valid_nick() and valid_chan(). These tables follow the
* RFC 1459 definitions; protocols with different definitions should modify
* the table accordingly (bit 0 = valid as a first character, bit 1 = valid
* as a subsequent character). valid_{nick,chan}_table[0] should never be
* modified.
*/
unsigned char valid_nick_table[256] = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,2,0,0, 2,2,2,2,2,2,2,2, 2,2,0,0,0,0,0,0,
0,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 3,3,3,2,0,2,2,0,
2,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 3,3,3,2,0,2,2,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
};
unsigned char valid_chan_table[256] = {
0,2,2,2,2,2,2,0, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
0,2,2,3,2,2,3,2, 2,2,2,2,0,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
};
/*************************************************************************/
/* valid_nick, valid_chan: Check whether the given string is a valid
* nickname or channel name, ignoring length.
* Returns nonzero if valid, zero if not.
*/
int valid_nick(const char *str)
{
if (!(valid_nick_table[*(const unsigned char *)str] & 1))
return 0;
while (*++str) {
if (!(valid_nick_table[*(const unsigned char *)str] & 2))
return 0;
}
return 1;
}
int valid_chan(const char *str)
{
if (!(valid_chan_table[*(const unsigned char *)str] & 1))
return 0;
while (*++str) {
if (!(valid_chan_table[*(const unsigned char *)str] & 2))
return 0;
}
return 1;
}
/*************************************************************************/
/* Check whether the given string is a valid domain name, according to RFC
* rules:
* - Contains only letters, digits, hyphens, and periods (dots).
* - Begins with a letter or digit.
* - Has a letter or digit after every dot (except for a trailing dot).
* - Is no more than DOMAIN_MAXLEN characters long.
* - Has no more than DOMPART_MAXLEN characters between periods.
* - Has at least one character and does not end with a dot. (not RFC)
*/
#define DOMAIN_MAXLEN 255
#define DOMPART_MAXLEN 63
int valid_domain(const char *str)
{
const char *s;
int i;
static const char valid_domain_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.";
if (!*str)
return 0;
if (str[strspn(str,valid_domain_chars)] != 0)
return 0;
s = str;
while (s-str < DOMAIN_MAXLEN && *s) {
if (*s == '-' || *s == '.')
return 0;
i = strcspn(s, ".");
if (i > DOMPART_MAXLEN)
return 0;
s += i;
if (*s)
s++;
}
if (s-str > DOMAIN_MAXLEN || *s)
return 0;
if (s[-1] == '.')
return 0;
return 1;
}
/*************************************************************************/
/* Check whether the given string is a valid E-mail address. A valid
* E-mail address:
* - Contains none of the following characters:
* + control characters (\000-\037)
* + space (\040)
* + vertical bar ('|') (because some mailers try to pipe with it)
* + RFC-822 specials, except [ ] @ .
* - Contains exactly one '@', which may not be the first character.
* - Contains a valid domain name or an IP address in square brackets
* after the '@'.
* - Does not contain [ or ] except when used with an IP address as above.
*/
int valid_email(const char *str)
{
const unsigned char *s;
const char *atmark;
for (s = (const unsigned char *)str; *s; s++) {
if (*s <= '\040') // 040 == 0x20 == ' '
return 0;
if (strchr("|,:;\\\"()<>", *s))
return 0;
}
/* Find the @, and abort if there isn't one */
atmark = strchr(str, '@');
if (!atmark || atmark == str)
return 0;
atmark++;
/* Don't allow [] in username */
s = (const unsigned char *)strpbrk(str, "[]");
if (s && (const char *)s < atmark)
return 0;
/* Check for a [1.2.3.4] type of domain */
if (*atmark == '[') {
unsigned char ipstr[16];
const char *bracket = strchr(atmark+1, ']');
int len = bracket - (atmark+1);
/* Valid IP addresses have no more than 15 characters */
if (len <= 15 && !bracket[1]) {
strncpy((char *)ipstr, atmark+1, len);
ipstr[len] = 0;
/* Use pack_ip() to see if it's a valid address */
if (pack_ip((const char *)ipstr))
return 1;
}
}
/* Don't allow [] in domains except for the above case */
if (strpbrk(atmark, "[]"))
return 0;
/* Valid domain names cannot contain '@' so we just check valid_domain().
* Also prohibit domains without dots. */
return strchr(atmark, '.') && valid_domain(atmark);
}
/*************************************************************************/
/* Check whether the given string is a valid URL. A valid URL:
* - Contains neither control characters (\000-\037) nor spaces (\040).
* - Contains a series of letters followed by "://" followed by a valid
* domain name, possibly followed by a : and a numeric port number in
* the range 1-65535, followed either by a slash and possibly more text
* by nothing.
*/
int valid_url(const char *str)
{
const unsigned char *s, *colon, *host;
static const char letters[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
char domainbuf[DOMAIN_MAXLEN+1];
for (s = (const unsigned char *)str; *s; s++) {
if (*s <= '\040') // 040 == 0x20 == ' '
return 0;
}
s = (const unsigned char *)strstr(str, "://");
if (!s)
return 0; /* No "://" */
if (strspn(str, letters) != s - (const unsigned char *)str)
return 0; /* Protocol has non-alphabetic characters */
host = s+3;
colon = (const unsigned char *)strchr((const char *)host, ':');
/* s will eventually point to the expected end of the host string */
s = host + strcspn((const char *)host, "/");
/* Make sure the port is valid if present. */
if (colon && colon < s) {
const unsigned char *tmp;
int port = strtol((const char *)colon+1, (char **)&tmp, 10);
if (port < 1 || port > 65535 || tmp != s)
return 0; /* Invalid port number or non-numeric characters */
s = colon;
}
/* The string from host through s-1 must be a valid domain name.
* Check length (must be >=1, <=DOMAIN_MAXLEN), then copy into
* temporary buffer and check. Also discard domain names without
* dots in them. */
if (s-host < 1 || s-host > DOMAIN_MAXLEN)
return 0;
memcpy(domainbuf, host, s-host);
domainbuf[s-host] = 0;
return strchr(domainbuf, '.') && valid_domain(domainbuf);
}
/*************************************************************************/
/*************************************************************************/
/* Process a string containing a number/range list in the form
* "n1[-n2][,n3[-n4]]...", calling a caller-specified routine for each
* number in the list. If the callback returns -1, stop immediately.
* Returns the sum of all nonnegative return values from the callback.
* If `count_ret' is non-NULL, *count_ret will be set to the total number
* of times the callback was called.
*
* The number list will never contain duplicates and will always be sorted
* ascendingly. This means the callback routines don't have to worry about
* being called twice for the same index. -TheShadow
* Also, the only values accepted are 0-65536 (inclusive), to avoid someone
* giving us 0-2^31 and causing freezes or out-of-memory. Numbers outside
* this range will be ignored.
*
* The callback should be of type range_callback_t, which is defined as:
* int (*range_callback_t)(User *u, int num, va_list args)
*/
int process_numlist(const char *numstr, int *count_ret,
range_callback_t callback, User *u, ...)
{
int n1, n2, min, max, i;
int retval = 0;
int numcount = 0;
va_list args;
static char numflag[65536/8+1]; /* 1 bit per index 0-65536 inclusive */
memset(numflag, 0, sizeof(numflag));
min = 65536;
max = 0;
va_start(args, u);
/* This algorithm ignores invalid characters, ignores a dash
* when it precedes a comma, and ignores everything from the
* end of a valid number or range to the next comma or null.
*/
while (*numstr) {
n1 = n2 = strtol(numstr, (char **)&numstr, 10);
numstr += strcspn(numstr, "0123456789,-");
if (*numstr == '-') {
numstr++;
numstr += strcspn(numstr, "0123456789,");
if (isdigit(*numstr)) {
n2 = strtol(numstr, (char **)&numstr, 10);
numstr += strcspn(numstr, "0123456789,-");
}
}
if (n1 < 0)
n1 = 0;
if (n2 > 65536)
n2 = 65536;
if (n1 < min)
min = n1;
if (n2 > max)
max = n2;
while (n1 <= n2) {
if ((n1&7) == 0 && n1+7 <= n2) {
/* Set a whole byte at once */
numflag[n1>>3] = 0xFF;
n1 += 8;
} else {
/* Set just a single bit */
numflag[n1>>3] |= 1 << (n1&7);
n1++;
}
}
numstr += strcspn(numstr, ",");
if (*numstr)
numstr++;
}
/* Now call the callback routine for each index. */
numcount = 0;
for (i = min; i <= max; i++) {
va_list args_copy;
int res;
if (!(numflag[i>>3] & (1 << (i&7))))
continue;
numcount++;
va_copy(args_copy, args);
res = callback(u, i, args_copy);
va_end(args_copy);
if (debug)
log("debug: process_numlist: tried to do %d; result = %d", i, res);
if (res < 0)
break;
retval += res;
}
va_end(args);
if (count_ret)
*count_ret = numcount;
return retval;
}
/*************************************************************************/
/*************************************************************************/
/* time_msec: Return the current time to millisecond resolution. */
uint32 time_msec(void)
{
#if HAVE_GETTIMEOFDAY
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*1000 + tv.tv_usec/1000;
#else
return time(NULL) * 1000;
#endif
}
/*************************************************************************/
/* strtotime: Convert a string into a time_t. Essentially the same as
* strtol(), but assumes base 10 and returns a time_t.
*/
time_t strtotime(const char *str, char **endptr)
{
time_t t = 0;
while (*str >= '0' && *str <= '9') {
if (t > MAX_TIME_T/10 || MAX_TIME_T - t*10 < *str-'0') {
t = MAX_TIME_T;
errno = ERANGE;
} else {
t = t*10 + *str-'0';
}
str++;
}
if (endptr)
*endptr = (char *)str;
return t;
}
/*************************************************************************/
/* dotime: Return the number of seconds corresponding to the given time
* string. If the given string does not represent a valid time,
* return -1.
*
* A time string is either a plain integer (representing a number
* of seconds), an integer followed by one of these characters:
* "s" (seconds), "m" (minutes), "h" (hours), or "d" (days), or a
* sequence of such integer-character pairs (without separators,
* e.g. "1h30m").
*/
int dotime(const char *s)
{
int amount;
amount = strtol(s, (char **)&s, 10);
if (*s) {
char c = *s++;
int rest = dotime(s);
if (rest < 0)
return -1;
switch (c) {
case 's': return rest + amount;
case 'm': return rest + amount*60;
case 'h': return rest + amount*3600;
case 'd': return rest + amount*86400;
default : return -1;
}
} else {
return amount;
}
}
/*************************************************************************/
/*************************************************************************/
/* Translate an IPv4 dotted-quad address into binary (4 bytes). Returns
* NULL if the given string is not in dotted-quad format or `ipaddr' is
* NULL. The returned buffer is static and will be overwritten on
* subsequent calls.
*/
uint8 *pack_ip(const char *ipaddr)
{
static uint8 ipbuf[4];
const char *s, *s2;
int i;
long tmp;
if (!ipaddr)
return NULL;
s = ipaddr;
for (i = 0; i < 4; i++) {
if (i > 0 && *s++ != '.')
return NULL;
if (isspace(*s)) /* because strtol() will skip whitespace */
return NULL;
tmp = strtol(s, (char **)&s2, 10);
if (s2 == s || tmp < 0 || tmp > 255)
return NULL;
ipbuf[i] = (uint8)tmp;
s = s2;
}
if (*s)
return NULL;
return ipbuf;
}
/*************************************************************************/
/* Translate a 4-byte binary IPv4 to its dotted-quad (ASCII) representation.
* Always succeeds and always returns a null-terminated string <= 15
* characters in length, unless `ip' is NULL, in which case the function
* returns NULL. The returned buffer is static and will be overwritten on
* subsequent calls.
*/
char *unpack_ip(const uint8 *ip)
{
static char ipbuf[17]; /* 1 byte extra to catch overflow (paranoia) */
if (!ip)
return NULL;
snprintf(ipbuf, sizeof(ipbuf), "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
if (strlen(ipbuf) > 15)
fatal("BUG: unpack_ip(): result too long (%s)", ipbuf);
return ipbuf;
}
/*************************************************************************/
/* Translate an IPv6 ASCII address into binary (16 bytes). Return NULL if
* the given string is not in IPv6 format or `ipaddr' is NULL. The
* returned buffer is static and will be overwritten on subsequent calls.
*/
uint8 *pack_ip6(const char *ipaddr)
{
static uint8 ipbuf[16];
int words[8];
int wordnum;
const char *s, *t;
int i;
if (!ipaddr)
return NULL;
/* Parse the address into 16-bit words */
s = ipaddr;
wordnum = 0;
if (*s == ':') {
words[wordnum++] = 0;
s++;
}
while (*s) {
if (wordnum >= 8) {
/* too many words, abort */
return NULL;
}
if (*s == ':') {
/* mark "::" with a -1 */
words[wordnum++] = -1;
s++;
} else {
words[wordnum++] = (int)strtol(s, (char **)&t, 16);
if (*t && *t != ':') {
/* invalid syntax */
return NULL;
}
if (*t) {
t++; /* skip past delimiter */
if (!*t) {
/* trailing ":", convert to :0000 */
if (wordnum >= 8)
return NULL;
words[wordnum++] = 0;
}
}
s = t;
}
}
/* Expand "::" into as many zeros as needed, and make sure there
* aren't multiple occurrences of "::" */
for (i = 0; i < wordnum; i++) {
if (words[i] == -1)
break;
}
if (i < wordnum) { /* found a "::" */
int j, offset;
for (j = i+1; j < wordnum; j++) {
if (words[j] == -1) {
/* multiple "::" */
return NULL;
}
}
offset = 8-wordnum;
for (j = 7; j >= i; j--) {
if (j-offset > i)
words[j] = words[j-offset];
else
words[j] = 0;
}
wordnum = 8;
}
/* Make sure we have exactly 8 words */
if (wordnum != 8)
return NULL;
/* Convert to binary and return */
for (i = 0; i < 8; i++) {
ipbuf[i*2 ] = words[i] >> 8;
ipbuf[i*2+1] = words[i] & 255;
}
return ipbuf;
}
/*************************************************************************/
/* Translate a 16-byte binary IPv6 address to its ASCII representation.
* Always succeeds and always returns a null-terminated string <= 39
* characters in length, unless `ip' is NULL, in which case the function
* returns NULL. The returned buffer is static and will be overwritten on
* subsequent calls.
*
* The address string returned by this function will always use 4-digit
* hexadecimal numbers for each word in the address, but will omit all-zero
* words when possible.
*/
char *unpack_ip6(const uint8 *ip)
{
static char ipbuf[41]; /* 1 byte extra to catch overflow (paranoia) */
char *out, *s;
int i;
if (!ip)
return NULL;
out = ipbuf;
for (i = 0; i < 8; i++) {
/* Skip 0000 at beginning or end */
if ((i != 0 && i != 7) || ip[i*2] || ip[i*2+1]) {
out += snprintf(out, sizeof(ipbuf)-(out-ipbuf), "%02X%02X",
ip[i*2], ip[i*2+1]);
}
if (i != 7)
*out++ = ':';
}
if (strlen(ipbuf) > 39)
fatal("BUG: unpack_ip6(): result too long (%s)", ipbuf);
if ((s = strstr(ipbuf,":0000:")) != NULL) {
/* Compress zeros */
memmove(s+1, s+5, strlen(s+5)+1);
s++;
while (strncmp(s,":0000:",6) == 0)
memmove(s, s+5, strlen(s+5)+1);
}
return ipbuf;
}
/*************************************************************************/
/*************************************************************************/
/* Lock the data directory if possible; return nonzero on success, zero on
* failure (data directory already locked or cannot create lock file).
* On failure, errno will be EEXIST if the directory was already locked or
* a value other than EEXIST if an error occurred creating the lock file.
*
* This does not attempt to correct for NFS brokenness w.r.t. O_EXCL and
* will contain a race condition when used on an NFS filesystem (or any
* other filesystem which does not support O_EXCL properly).
*/
int lock_data(void)
{
int fd;
errno = 0;
fd = open(LockFilename, O_WRONLY | O_CREAT | O_EXCL, 0);
if (fd >= 0) {
close(fd);
return 1;
}
return 0;
}
/*************************************************************************/
/* Check whether the data directory is locked without actually attempting
* to lock it. Returns 1 if locked, 0 if not, or -1 if an error occurred
* while trying to check (in which case errno will be set to an appropriate
* value, i.e. whatever access() returned).
*/
int is_data_locked(void)
{
errno = 0;
if (access(LockFilename, F_OK) == 0)
return 1;
if (errno == ENOENT)
return 0;
return -1;
}
/*************************************************************************/
/* Unlock the data directory. Assumes we locked it in the first place.
* Returns a positive nonzero value on success, zero on failure (unable to
* remove the lock file), or -1 if the lock file didn't exist in the first
* place (possibly because it was removed by another (misbehaving) program).
*/
int unlock_data(void)
{
errno = 0;
if (unlink(LockFilename) == 0)
return 1;
if (errno == ENOENT)
return -1;
return 0;
}
/*************************************************************************/
/*************************************************************************/
/* Encode buffer `in' of size `insize' to buffer `out' of size `outsize'
* using base64, appending a trailing null. Returns the number of bytes
* required by the encoded string. (The amount of space needed to store
* the encoded string can be found with encode_base64(...,NULL,0).)
* Returns -1 if the input or output buffer is NULL (except when the
* corresponding size is zero) or if the input or output size is negative.
* If `out' is too small to hold the entire encoded string, it will contain
* the first `outsize'-1 bytes of the encoded string followed by a null.
*/
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int encode_base64(const void *in, int insize, char *out, int outsize)
{
int required = ((insize+2)/3*4)+1;
const uint8 *inp = in;
int inpos, outpos;
if (insize < 0 || outsize < 0)
return -1;
if (outsize == 0)
return required;
if (!out)
return -1;
if (insize == 0) {
/* outsize is at least 1 */
*out = 0;
return 1;
}
if (!in)
return -1;
outsize--; /* leave room for trailing \0 */
/* Actually do the encoding */
outpos = 0;
for (inpos = 0; inpos < insize; inpos += 3) {
uint8 i0, i1, i2;
char o0, o1, o2, o3;
i0 = inp[inpos];
o0 = base64_chars[i0>>2];
if (inpos+1 < insize) {
i1 = inp[inpos+1];
o1 = base64_chars[(i0&3)<<4 | i1>>4];
if (inpos+2 < insize) {
i2 = inp[inpos+2];
o2 = base64_chars[(i1&15)<<2 | i2>>6];
o3 = base64_chars[i2&63];
} else {
o2 = base64_chars[(i1&15)<<2];
o3 = '=';
}
} else {
o1 = base64_chars[(i0&3)<<4];
o2 = '=';
o3 = '=';
}
if (outpos < outsize)
out[outpos++] = o0;
if (outpos < outsize)
out[outpos++] = o1;
if (outpos < outsize)
out[outpos++] = o2;
if (outpos < outsize)
out[outpos++] = o3;
}
/* Terminate the string and return; outpos is constrained above to
* still be within the buffer (remember that outsize was decremented
* before the loop) */
out[outpos] = 0;
return required;
}
/*************************************************************************/
/* Decode base64-encoded string `in' (null-terminated) to buffer `out' of
* size `outsize'. Returns the number of bytes required by the decoded
* data. (The amount of space needed to store the decoded data can be
* found with decode_base64(...,NULL,0).) Returns -1 if the input string
* is NULL, or if the output buffer is NULL (except when the size is zero)
* or the size is negative. If `out' is too small to hold all of the
* decoded data, it will contain the first `outsize' bytes.
*/
static const char base64_array[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 0x00 */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 0x40 */
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 0x80 */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 0xC0 */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
};
int decode_base64(const char *in, void *out, int outsize)
{
uint8 *outp = out;
int outpos;
if (!in || outsize < 0)
return -1;
outpos = 0;
while (*in) {
int i0, i1, i2, i3;
i0 = *in++;
if (*in) {
i1 = *in++;
if (*in) {
i2 = *in++;
if (*in) {
i3 = *in++;
} else {
i3 = 0;
}
} else {
i2 = 0;
i3 = 0;
}
} else {
i1 = 0;
i2 = 0;
i3 = 0;
}
i0 = base64_array[(int)i0 & 255];
i1 = base64_array[(int)i1 & 255];
i2 = base64_array[(int)i2 & 255];
i3 = base64_array[(int)i3 & 255];
if (i0 < 0 || i1 < 0)
break;
/* Only store if buffer space is available; increment outpos anyway
* to keep track of total space required (for return value) */
if (outpos < outsize)
outp[outpos] = i0<<2 | i1>>4;
outpos++;
if (i2 < 0)
break;
if (outpos < outsize)
outp[outpos] = (i1&15)<<4 | i2>>2;
outpos++;
if (i3 < 0)
break;
if (outpos < outsize)
outp[outpos] = (i2&3)<<6 | i3;
outpos++;
}
return outpos;
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1