summaryrefslogtreecommitdiff
path: root/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.c')
-rw-r--r--src/util.c279
1 files changed, 216 insertions, 63 deletions
diff --git a/src/util.c b/src/util.c
index e64f1a6..469eaed 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2015 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
@@ -28,24 +28,12 @@
#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 */
static u32 seed[32];
static u32 in[12];
static u32 out[8];
+static int outleft = 0;
void rand_init()
{
@@ -83,19 +71,44 @@ static void surf(void)
unsigned short rand16(void)
{
+ if (!outleft)
+ {
+ if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+ surf();
+ outleft = 8;
+ }
+
+ return (unsigned short) out[--outleft];
+}
+
+u32 rand32(void)
+{
+ if (!outleft)
+ {
+ if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+ surf();
+ outleft = 8;
+ }
+
+ return out[--outleft];
+}
+
+u64 rand64(void)
+{
static int outleft = 0;
- if (!outleft) {
- if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
- surf();
- outleft = 8;
- }
+ if (outleft < 2)
+ {
+ if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+ surf();
+ outleft = 8;
+ }
+
+ outleft -= 2;
- return (unsigned short) out[--outleft];
+ return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
}
-#endif
-
static int check_name(char *in)
{
/* remove trailing .
@@ -108,10 +121,10 @@ static int check_name(char *in)
if (in[l-1] == '.')
{
- if (l == 1) return 0;
in[l-1] = 0;
+ nowhite = 1;
}
-
+
for (; (c = *in); in++)
{
if (c == '.')
@@ -142,17 +155,20 @@ static int check_name(char *in)
int legal_hostname(char *name)
{
char c;
+ int first;
if (!check_name(name))
return 0;
- for (; (c = *name); name++)
+ for (first = 1; (c = *name); name++, first = 0)
/* 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 == '_')
+ (c >= '0' && c <= '9'))
+ continue;
+
+ if (!first && (c == '-' || c == '_'))
continue;
/* end of hostname part */
@@ -210,7 +226,14 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
{
unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
- *p++ = *sval;
+ {
+#ifdef HAVE_DNSSEC
+ if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
+ *p++ = (*(++sval))-1;
+ else
+#endif
+ *p++ = *sval;
+ }
*cp = j;
if (*sval)
sval++;
@@ -258,6 +281,7 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
#ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 &&
s1->in6.sin6_port == s2->in6.sin6_port &&
+ s1->in6.sin6_scope_id == s2->in6.sin6_scope_id &&
IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
return 1;
#endif
@@ -280,7 +304,7 @@ int sa_len(union mysockaddr *addr)
}
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
-int hostname_isequal(char *a, char *b)
+int hostname_isequal(const char *a, const char *b)
{
unsigned int c1, c2;
@@ -315,11 +339,66 @@ time_t dnsmasq_time(void)
#endif
}
+int netmask_length(struct in_addr mask)
+{
+ int zero_count = 0;
+
+ while (0x0 == (mask.s_addr & 0x1) && zero_count < 32)
+ {
+ mask.s_addr >>= 1;
+ zero_count++;
+ }
+
+ return 32 - zero_count;
+}
+
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);
}
+#ifdef HAVE_IPV6
+int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
+{
+ int pfbytes = prefixlen >> 3;
+ int pfbits = prefixlen & 7;
+
+ if (memcmp(&a->s6_addr, &b->s6_addr, pfbytes) != 0)
+ return 0;
+
+ if (pfbits == 0 ||
+ (a->s6_addr[pfbytes] >> (8 - pfbits) == b->s6_addr[pfbytes] >> (8 - pfbits)))
+ return 1;
+
+ return 0;
+}
+
+/* return least signigicant 64 bits if IPv6 address */
+u64 addr6part(struct in6_addr *addr)
+{
+ int i;
+ u64 ret = 0;
+
+ for (i = 8; i < 16; i++)
+ ret = (ret << 8) + addr->s6_addr[i];
+
+ return ret;
+}
+
+void setaddr6part(struct in6_addr *addr, u64 host)
+{
+ int i;
+
+ for (i = 15; i >= 8; i--)
+ {
+ addr->s6_addr[i] = host;
+ host = host >> 8;
+ }
+}
+
+#endif
+
+
/* returns port number from address */
int prettyprint_addr(union mysockaddr *addr, char *buf)
{
@@ -333,7 +412,15 @@ int prettyprint_addr(union mysockaddr *addr, char *buf)
}
else if (addr->sa.sa_family == AF_INET6)
{
+ char name[IF_NAMESIZE];
inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
+ if (addr->in6.sin6_scope_id != 0 &&
+ if_indextoname(addr->in6.sin6_scope_id, name) &&
+ strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
+ {
+ strcat(buf, "%");
+ strcat(buf, name);
+ }
port = ntohs(addr->in6.sin6_port);
}
#else
@@ -376,7 +463,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
while (maxlen == -1 || i < maxlen)
{
- for (r = in; *r != 0 && *r != ':' && *r != '-'; r++)
+ for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
if (*r != '*' && !isxdigit((unsigned char)*r))
return -1;
@@ -394,12 +481,29 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
else
{
*r = 0;
- mask = mask << 1;
if (strcmp(in, "*") == 0)
- mask |= 1;
+ {
+ mask = (mask << 1) | 1;
+ i++;
+ }
else
- out[i] = strtol(in, NULL, 16);
- i++;
+ {
+ int j, bytes = (1 + (r - in))/2;
+ for (j = 0; j < bytes; j++)
+ {
+ char sav = sav;
+ if (j < bytes - 1)
+ {
+ sav = in[(j+1)*2];
+ in[(j+1)*2] = 0;
+ }
+ out[i] = strtol(&in[j*2], NULL, 16);
+ mask = mask << 1;
+ i++;
+ if (j < bytes - 1)
+ in[(j+1)*2] = sav;
+ }
+ }
}
}
in = r+1;
@@ -466,27 +570,41 @@ char *print_mac(char *buff, unsigned char *mac, int len)
return buff;
}
-void bump_maxfd(int fd, int *max)
+/* rc is return from sendto and friends.
+ Return 1 if we should retry.
+ Set errno to zero if we succeeded. */
+int retry_send(ssize_t rc)
{
- if (fd > *max)
- *max = fd;
-}
+ static int retries = 0;
+ struct timespec waiter;
+
+ if (rc != -1)
+ {
+ retries = 0;
+ errno = 0;
+ return 0;
+ }
+
+ /* Linux kernels can return EAGAIN in perpetuity when calling
+ sendmsg() and the relevant interface has gone. Here we loop
+ retrying in EAGAIN for 1 second max, to avoid this hanging
+ dnsmasq. */
-int retry_send(void)
-{
- struct timespec waiter;
- if (errno == EAGAIN)
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
{
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;
nanosleep(&waiter, NULL);
- return 1;
+ if (retries++ < 1000)
+ return 1;
}
-
- if (errno == EINTR)
- return 1;
-
- return 0;
+
+ retries = 0;
+
+ if (errno == EINTR)
+ return 1;
+
+ return 0;
}
int read_write(int fd, unsigned char *packet, int size, int rw)
@@ -495,22 +613,57 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
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;
- }
+ do {
+ 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;
+
+ } while (retry_send(n) || errno == ENOMEM || errno == ENOBUFS);
+
+ if (errno != 0)
+ return 0;
}
+
return 1;
}
+/* Basically match a string value against a wildcard pattern. */
+int wildcard_match(const char* wildcard, const char* match)
+{
+ while (*wildcard && *match)
+ {
+ if (*wildcard == '*')
+ return 1;
+
+ if (*wildcard != *match)
+ return 0;
+
+ ++wildcard;
+ ++match;
+ }
+
+ return *wildcard == *match;
+}
+
+/* The same but comparing a maximum of NUM characters, like strncmp. */
+int wildcard_matchn(const char* wildcard, const char* match, int num)
+{
+ while (*wildcard && *match && num)
+ {
+ if (*wildcard == '*')
+ return 1;
+
+ if (*wildcard != *match)
+ return 0;
+
+ ++wildcard;
+ ++match;
+ --num;
+ }
+
+ return (!num) || (*wildcard == *match);
+}