blob: 53e31371d036167a6910385f4822783b76682114 [file] [log] [blame]
/* dnsmasq is Copyright (c) 2000-2009 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* The SURF random number generator was taken from djbdns-1.05, by
Daniel J Bernstein, which is public domain. */
#include "dnsmasq.h"
#include <netdb.h>
#ifdef HAVE_BROKEN_RTC
#include <sys/times.h>
#endif
#ifdef LOCALEDIR
#include <idna.h>
#endif
#ifdef HAVE_ARC4RANDOM
void rand_init(void) {
return;
}
unsigned short rand16(void) {
return (unsigned short) (arc4random() >> 15);
}
#else
/* SURF random number generator */
typedef unsigned int uint32;
static uint32 seed[32];
static uint32 in[12];
static uint32 out[8];
void rand_init() {
int fd = open(RANDFILE, O_RDONLY);
if (fd == -1 || !read_write(fd, (unsigned char*) &seed, sizeof(seed), 1) ||
!read_write(fd, (unsigned char*) &in, sizeof(in), 1))
die(_("failed to seed the random number generator: %s"), NULL, EC_MISC);
close(fd);
}
#define ROTATE(x, b) (((x) << (b)) | ((x) >> (32 - (b))))
#define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x, b));
static void surf(void) {
uint32 t[12];
uint32 x;
uint32 sum = 0;
int r;
int i;
int loop;
for (i = 0; i < 12; ++i) t[i] = in[i] ^ seed[12 + i];
for (i = 0; i < 8; ++i) out[i] = seed[24 + i];
x = t[11];
for (loop = 0; loop < 2; ++loop) {
for (r = 0; r < 16; ++r) {
sum += 0x9e3779b9;
MUSH(0, 5)
MUSH(1, 7) MUSH(2, 9) MUSH(3, 13) MUSH(4, 5) MUSH(5, 7) MUSH(6, 9) MUSH(7, 13)
MUSH(8, 5) MUSH(9, 7) MUSH(10, 9) MUSH(11, 13)
}
for (i = 0; i < 8; ++i) out[i] ^= t[i + 4];
}
}
unsigned short rand16(void) {
static int outleft = 0;
if (!outleft) {
if (!++in[0])
if (!++in[1])
if (!++in[2]) ++in[3];
surf();
outleft = 8;
}
return (unsigned short) out[--outleft];
}
#endif
static int check_name(char* in) {
/* remove trailing .
also fail empty string and label > 63 chars */
size_t dotgap = 0, l = strlen(in);
char c;
int nowhite = 0;
if (l == 0 || l > MAXDNAME) return 0;
if (in[l - 1] == '.') {
if (l == 1) return 0;
in[l - 1] = 0;
}
for (; (c = *in); in++) {
if (c == '.')
dotgap = 0;
else if (++dotgap > MAXLABEL)
return 0;
else if (isascii(c) && iscntrl(c))
/* iscntrl only gives expected results for ascii */
return 0;
#ifndef LOCALEDIR
else if (!isascii(c))
return 0;
#endif
else if (c != ' ')
nowhite = 1;
}
if (!nowhite) return 0;
return 1;
}
/* Hostnames have a more limited valid charset than domain names
so check for legal char a-z A-Z 0-9 - _
Note that this may receive a FQDN, so only check the first label
for the tighter criteria. */
int legal_hostname(char* name) {
char c;
if (!check_name(name)) return 0;
for (; (c = *name); name++)
/* check for legal char a-z A-Z 0-9 - _ . */
{
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
c == '-' || c == '_')
continue;
/* end of hostname part */
if (c == '.') return 1;
return 0;
}
return 1;
}
char* canonicalise(char* in, int* nomem) {
char* ret = NULL;
#ifdef LOCALEDIR
int rc;
#endif
if (nomem) *nomem = 0;
if (!check_name(in)) return NULL;
#ifdef LOCALEDIR
if ((rc = idna_to_ascii_lz(in, &ret, 0)) != IDNA_SUCCESS) {
if (ret) free(ret);
if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR)) {
my_syslog(LOG_ERR, _("failed to allocate memory"));
*nomem = 1;
}
return NULL;
}
#else
if ((ret = whine_malloc(strlen(in) + 1)))
strcpy(ret, in);
else if (nomem)
*nomem = 1;
#endif
return ret;
}
unsigned char* do_rfc1035_name(unsigned char* p, char* sval) {
int j;
while (sval && *sval) {
unsigned char* cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++) *p++ = *sval;
*cp = j;
if (*sval) sval++;
}
return p;
}
/* for use during startup */
void* safe_malloc(size_t size) {
void* ret = malloc(size);
if (!ret) die(_("could not get memory"), NULL, EC_NOMEM);
return ret;
}
void safe_pipe(int* fd, int read_noblock) {
if (pipe(fd) == -1 || !fix_fd(fd[1]) || (read_noblock && !fix_fd(fd[0])))
die(_("cannot create pipe: %s"), NULL, EC_MISC);
}
void* whine_malloc(size_t size) {
void* ret = malloc(size);
if (!ret) my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
return ret;
}
int sockaddr_isequal(union mysockaddr* s1, union mysockaddr* s2) {
if (s1->sa.sa_family == s2->sa.sa_family) {
if (s1->sa.sa_family == AF_INET && s1->in.sin_port == s2->in.sin_port &&
s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
return 1;
#ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 && s1->in6.sin6_port == s2->in6.sin6_port &&
IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr) &&
(!IN6_IS_ADDR_LINKLOCAL(&s1->in6.sin6_addr) ||
(s1->in6.sin6_scope_id == s2->in6.sin6_scope_id)))
return 1;
#endif
}
return 0;
}
int sa_len(union mysockaddr* addr) {
if (addr->sa.sa_family == AF_INET6)
return sizeof(addr->in6);
else
return sizeof(addr->in);
}
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
int hostname_isequal(char* a, char* b) {
unsigned int c1, c2;
do {
c1 = (unsigned char) *a++;
c2 = (unsigned char) *b++;
if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A';
if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A';
if (c1 != c2) return 0;
} while (c1);
return 1;
}
time_t dnsmasq_time(void) {
#ifdef HAVE_BROKEN_RTC
struct tms dummy;
static long tps = 0;
if (tps == 0) tps = sysconf(_SC_CLK_TCK);
return (time_t)(times(&dummy) / tps);
#else
return time(NULL);
#endif
}
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask) {
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}
int parse_addr(int family, const char* addrstr, union mysockaddr* addr) {
struct addrinfo *res, hints = {
.ai_flags = AI_NUMERICHOST,
.ai_family = family,
.ai_socktype = SOCK_DGRAM,
};
int ret = getaddrinfo(addrstr, NULL, &hints, &res);
if (ret) {
return ret;
}
switch (res->ai_family) {
case AF_INET:
addr->in = *((struct sockaddr_in*) res->ai_addr);
break;
#ifdef HAVE_IPV6
case AF_INET6:
addr->in6 = *((struct sockaddr_in6*) res->ai_addr);
break;
#endif
default:
errno = EAFNOSUPPORT;
ret = -1;
break;
}
freeaddrinfo(res);
return ret;
}
/* returns port number from address */
int prettyprint_addr(const union mysockaddr* addr, char* buf) {
int port = 0;
#ifdef HAVE_IPV6
char portstr[strlen("65535")];
getnameinfo((const struct sockaddr*) addr, sizeof(*addr), buf, ADDRSTRLEN, portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
port = atoi(portstr);
#else
strcpy(buf, inet_ntoa(addr->in.sin_addr));
port = ntohs(addr->in.sin_port);
#endif
return port;
}
void prettyprint_time(char* buf, unsigned int t) {
if (t == 0xffffffff)
sprintf(buf, _("infinite"));
else {
unsigned int x, p = 0;
if ((x = t / 86400)) p += sprintf(&buf[p], "%dd", x);
if ((x = (t / 3600) % 24)) p += sprintf(&buf[p], "%dh", x);
if ((x = (t / 60) % 60)) p += sprintf(&buf[p], "%dm", x);
if ((x = t % 60)) p += sprintf(&buf[p], "%ds", x);
}
}
/* in may equal out, when maxlen may be -1 (No max len). */
int parse_hex(char* in, unsigned char* out, int maxlen, unsigned int* wildcard_mask, int* mac_type) {
int mask = 0, i = 0;
char* r;
if (mac_type) *mac_type = 0;
while (maxlen == -1 || i < maxlen) {
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++)
;
if (*r == 0) maxlen = i;
if (r != in) {
if (*r == '-' && i == 0 && mac_type) {
*r = 0;
*mac_type = strtol(in, NULL, 16);
mac_type = NULL;
} else {
*r = 0;
mask = mask << 1;
if (strcmp(in, "*") == 0)
mask |= 1;
else
out[i] = strtol(in, NULL, 16);
i++;
}
}
in = r + 1;
}
if (wildcard_mask) *wildcard_mask = mask;
return i;
}
/* return 0 for no match, or (no matched octets) + 1 */
int memcmp_masked(unsigned char* a, unsigned char* b, int len, unsigned int mask) {
int i, count;
for (count = 1, i = len - 1; i >= 0; i--, mask = mask >> 1)
if (!(mask & 1)) {
if (a[i] == b[i])
count++;
else
return 0;
}
return count;
}
/* _note_ may copy buffer */
int expand_buf(struct iovec* iov, size_t size) {
void* new;
if (size <= (size_t) iov->iov_len) return 1;
if (!(new = whine_malloc(size))) {
errno = ENOMEM;
return 0;
}
if (iov->iov_base) {
memcpy(new, iov->iov_base, iov->iov_len);
free(iov->iov_base);
}
iov->iov_base = new;
iov->iov_len = size;
return 1;
}
char* print_mac(char* buff, unsigned char* mac, int len) {
char* p = buff;
int i;
if (len == 0)
sprintf(p, "<null>");
else
for (i = 0; i < len; i++) p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
return buff;
}
void bump_maxfd(int fd, int* max) {
if (fd > *max) *max = fd;
}
int retry_send(void) {
struct timespec waiter;
if (errno == EAGAIN) {
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;
nanosleep(&waiter, NULL);
return 1;
}
if (errno == EINTR) return 1;
return 0;
}
int read_write(int fd, unsigned char* packet, int size, int rw) {
ssize_t n, done;
for (done = 0; done < size; done += n) {
retry:
if (rw)
n = read(fd, &packet[done], (size_t)(size - done));
else
n = write(fd, &packet[done], (size_t)(size - done));
if (n == 0)
return 0;
else if (n == -1) {
if (retry_send() || errno == ENOMEM || errno == ENOBUFS)
goto retry;
else
return 0;
}
}
return 1;
}