/* 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;
|
}
|