/* dnsmasq is Copyright (c) 2000-2025 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 . */ /* The SURF random number generator was taken from djbdns-1.05, by Daniel J Bernstein, which is public domain. */ #include "dnsmasq.h" #ifdef HAVE_BROKEN_RTC #include #endif #if defined(HAVE_LIBIDN2) #include #elif defined(HAVE_IDN) #include #endif #ifdef HAVE_LINUX_NETWORK #include #endif #ifdef HAVE_BSD_NETWORK #include #endif /* SURF random number generator */ static u32 seed[32]; static u32 in[12]; static u32 out[8]; static int outleft = 0; void rand_init(void) { int fd = open(RANDFILE, O_RDONLY); if (fd == -1 || !read_write(fd, (unsigned char *)&seed, sizeof(seed), RW_READ) || !read_write(fd, (unsigned char *)&in, sizeof(in), RW_READ)) 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) { u32 t[12]; u32 x; u32 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) { 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 < 2) { if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; surf(); outleft = 8; } outleft -= 2; return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); } int rr_on_list(struct rrlist *list, unsigned short rr) { while (list) { if (list->rr != 0 && list->rr == rr) return 1; list = list->next; } return 0; } /* returns 1 if name is OK and ascii printable * returns 2 if name should be processed by IDN */ 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; int idn_encode = 0; int hasuscore = 0; int hasucase = 0; if (l == 0 || l > MAXDNAME) return 0; if (in[l-1] == '.') { in[l-1] = 0; nowhite = 1; } for (; (c = *in); in++) { if (c == '.') dotgap = 0; else if (++dotgap > MAXLABEL) return 0; else if (isascii((unsigned char)c) && iscntrl((unsigned char)c)) /* iscntrl only gives expected results for ascii */ return 0; else if (!isascii((unsigned char)c)) #if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2) return 0; #else idn_encode = 1; #endif else if (c != ' ') { nowhite = 1; #if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003) if (c == '_') hasuscore = 1; #else (void)hasuscore; #endif #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) if (c >= 'A' && c <= 'Z') hasucase = 1; #else (void)hasucase; #endif } } if (!nowhite) return 0; #if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003) /* Older libidn2 strips underscores, so don't do IDN processing if the name has an underscore unless it also has non-ascii characters. */ idn_encode = idn_encode || (hasucase && !hasuscore); #else idn_encode = idn_encode || hasucase; #endif return (idn_encode) ? 2 : 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; int first; if (!check_name(name)) return 0; 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')) continue; if (!first && (c == '-' || c == '_')) continue; /* end of hostname part */ if (c == '.') return 1; return 0; } return 1; } char *canonicalise(char *in, int *nomem) { char *ret = NULL; int rc; if (nomem) *nomem = 0; if (!(rc = check_name(in))) return NULL; #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) if (rc == 2) { # ifdef HAVE_LIBIDN2 rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL); # else rc = idna_to_ascii_lz(in, &ret, 0); # endif if (rc != 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; } return ret; } #else (void)rc; #endif if ((ret = whine_malloc(strlen(in)+1))) strcpy(ret, in); else if (nomem) *nomem = 1; return ret; } unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit) { int j; while (sval && *sval) { unsigned char *cp = p++; if (limit && p > (unsigned char*)limit) return NULL; for (j = 0; *sval && (*sval != '.'); sval++, j++) { if (limit && p + 1 > (unsigned char*)limit) return NULL; if (*sval == NAME_ESCAPE) *p++ = (*(++sval))-1; else *p++ = *sval; } *cp = j; if (*sval) sval++; } return p; } /* for use during startup */ void *safe_malloc(size_t size) { void *ret = calloc(1, size); if (!ret) die(_("could not get memory"), NULL, EC_NOMEM); return ret; } /* Ensure limited size string is always terminated. * Can be replaced by (void)strlcpy() on some platforms */ void safe_strncpy(char *dest, const char *src, size_t size) { if (size != 0) { dest[size-1] = '\0'; strncpy(dest, src, size-1); } } 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 = calloc(1, size); if (!ret) my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size); return ret; } void *whine_realloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (!ret) my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size); return ret; } int sockaddr_isequal(const union mysockaddr *s1, const 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; 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; } return 0; } int sockaddr_isnull(const union mysockaddr *s) { if (s->sa.sa_family == AF_INET && s->in.sin_addr.s_addr == 0) return 1; if (s->sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&s->in6.sin6_addr)) return 1; return 0; } int sa_len(union mysockaddr *addr) { #ifdef HAVE_SOCKADDR_SA_LEN return addr->sa.sa_len; #else if (addr->sa.sa_family == AF_INET6) return sizeof(addr->in6); else return sizeof(addr->in); #endif } /* don't use strcasecmp and friends here - they may be messed up by LOCALE */ int hostname_order(const char *a, const 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 -1; else if (c1 > c2) return 1; } while (c1); return 0; } int hostname_isequal(const char *a, const char *b) { return strlen(a) == strlen(b) && hostname_order(a, b) == 0; } /* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */ int hostname_issubdomain(char *a, char *b) { char *ap, *bp; unsigned int c1, c2; /* move to the end */ for (ap = a; *ap; ap++); for (bp = b; *bp; bp++); /* a shorter than b or a empty. */ if ((bp - b) < (ap - a) || ap == a) return 0; do { c1 = (unsigned char) *(--ap); c2 = (unsigned char) *(--bp); if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; if (c1 != c2) return 0; } while (ap != a); if (bp == b) return 2; if (*(--bp) == '.') return 1; return 0; } time_t dnsmasq_time(void) { #ifdef HAVE_BROKEN_RTC struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) die(_("cannot read monotonic clock: %s"), NULL, EC_MISC); return ts.tv_sec; #else return time(NULL); #endif } u32 dnsmasq_milliseconds(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec) * 1000 + (tv.tv_usec / 1000); } 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); } int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix) { struct in_addr mask; mask.s_addr = htonl(~((1 << (32 - prefix)) - 1)); return is_same_net(a, b, mask); } 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 significant 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; } } /* returns port number from address */ int prettyprint_addr(union mysockaddr *addr, char *buf) { int port = 0; if (addr->sa.sa_family == AF_INET) { inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN); port = ntohs(addr->in.sin_port); } 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); } 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], "%ud", x); if ((x = (t/3600)%24)) p += sprintf(&buf[p], "%uh", x); if ((x = (t/60)%60)) p += sprintf(&buf[p], "%um", x); if ((x = t%60)) sprintf(&buf[p], "%us", x); } } /* in may equal out, when maxlen may be -1 (No max len). Return -1 for extraneous no-hex chars found. */ int parse_hex(char *in, unsigned char *out, int maxlen, unsigned int *wildcard_mask, int *mac_type) { int done = 0, mask = 0, i = 0; char *r; if (mac_type) *mac_type = 0; while (!done && (maxlen == -1 || i < maxlen)) { for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++) if (*r != '*' && !isxdigit((unsigned char)*r)) return -1; if (*r == 0) done = 1; if (r != in ) { if (*r == '-' && i == 0 && mac_type) { *r = 0; *mac_type = strtol(in, NULL, 16); mac_type = NULL; } else { *r = 0; if (strcmp(in, "*") == 0) { mask = (mask << 1) | 1; i++; } else { int j, bytes = (1 + (r - in))/2; for (j = 0; j < bytes; j++) { char sav; if (j < bytes - 1) { sav = in[(j+1)*2]; in[(j+1)*2] = 0; } /* checks above allow mix of hexdigit and *, which is illegal. */ if (strchr(&in[j*2], '*')) return -1; out[i] = strtol(&in[j*2], NULL, 16); mask = mask << 1; if (++i == maxlen) break; if (j < bytes - 1) in[(j+1)*2] = sav; } } } } 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, ""); else for (i = 0; i < len; i++) p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":"); return buff; } /* 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) { 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. */ if (errno == EAGAIN || errno == EWOULDBLOCK) { waiter.tv_sec = 0; waiter.tv_nsec = 10000; nanosleep(&waiter, NULL); if (retries++ < 1000) return 1; } retries = 0; if (errno == EINTR) return 1; return 0; } /* rw = 0 -> write rw = 1 -> read rw = 2 -> write once rw = 3 -> read once "once" fails on EAGAIN, as this a timeout. This indicates a timeout of a TCP socket. */ int read_write(int fd, unsigned char *packet, int size, int rw) { ssize_t n, done; for (done = 0; done < size; done += n) { if (rw & 1) n = read(fd, &packet[done], (size_t)(size - done)); else n = write(fd, &packet[done], (size_t)(size - done)); if (n == 0) return 0; if (n == -1) { n = 0; /* don't mess with counter when we loop. */ if (errno == EINTR || errno == ENOMEM || errno == ENOBUFS) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) { /* "once" variant */ if (rw & 2) return 0; continue; } return 0; } } return 1; } /* close all fds except STDIN, STDOUT and STDERR, spare1, spare2 and spare3 */ void close_fds(long max_fd, int spare1, int spare2, int spare3) { /* On Linux, use the /proc/ filesystem to find which files are actually open, rather than iterate over the whole space, for efficiency reasons. On *BSD, the same facility is found at /dev/fd. If this fails we drop back to the dumb code. */ #ifdef HAVE_LINUX_NETWORK #define FDESCFS "/proc/self/fd" #endif #ifdef HAVE_BSD_NETWORK #define FDESCFS "/dev/fd" #endif #ifdef FDESCFS DIR *d = NULL; # ifdef HAVE_BSD_NETWORK dev_t dirdev = 0; char fdescfs[] = FDESCFS; /* string must be writable */ struct stat statbuf; /* On BSD, fdescfs is normally mounted at /dev/fd. However if it is NOT mounted, devfs creates a directory at /dev/fd which contains (only) the file descriptors 0,1 and 2. Under these conditions, opendir() will succeed, and if we proceed we will fail to close extant file descriptors which should be closed. Check that there is a filesystem mounted at /dev/fd by checking that the device changes between /dev/fd and /dev. If if doesn't, fall back to the dumb path. */ if (stat(fdescfs, &statbuf) != -1) dirdev = statbuf.st_dev; if (stat(dirname(fdescfs), &statbuf) != -1 && dirdev != statbuf.st_dev) # endif d = opendir(FDESCFS); if (d) { struct dirent *de; while ((de = readdir(d))) { long fd; char *e = NULL; errno = 0; fd = strtol(de->d_name, &e, 10); if (errno != 0 || !e || *e || fd == dirfd(d) || fd == STDOUT_FILENO || fd == STDERR_FILENO || fd == STDIN_FILENO || fd == spare1 || fd == spare2 || fd == spare3) continue; close(fd); } closedir(d); return; } #endif /* fallback, dumb code. */ for (max_fd--; max_fd >= 0; max_fd--) if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO && max_fd != spare1 && max_fd != spare2 && max_fd != spare3) close(max_fd); } /* 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); } #ifdef HAVE_LINUX_NETWORK int kernel_version(void) { struct utsname utsname; int version; char *split; if (uname(&utsname) < 0) die(_("failed to find kernel version: %s"), NULL, EC_MISC); split = strtok(utsname.release, "."); version = (split ? atoi(split) : 0); split = strtok(NULL, "."); version = version * 256 + (split ? atoi(split) : 0); split = strtok(NULL, "."); return version * 256 + (split ? atoi(split) : 0); } #endif