efl/src/lib/ecore_con/dns.c

7879 lines
172 KiB
C

/* ==========================================================================
* dns.c - Recursive, Reentrant DNS Resolver.
* --------------------------------------------------------------------------
* Copyright (c) 2008, 2009, 2010 William Ahern
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* ==========================================================================
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if !defined(__FreeBSD__)
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#undef _BSD_SOURCE
#define _BSD_SOURCE
#undef _DARWIN_C_SOURCE
#define _DARWIN_C_SOURCE
#undef _NETBSD_SOURCE
#define _NETBSD_SOURCE
#endif
#include <stddef.h> /* offsetof() */
#include <stdint.h> /* uint32_t */
#include <stdlib.h> /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */
#include <stdio.h> /* FILE fopen(3) fclose(3) getc(3) rewind(3) */
#include <string.h> /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */
#include <strings.h> /* strcasecmp(3) strncasecmp(3) */
#include <ctype.h> /* isspace(3) isdigit(3) */
#include <time.h> /* time_t time(2) */
#include <signal.h> /* sig_atomic_t */
#include <errno.h> /* errno EINVAL ENOENT */
#undef NDEBUG
#include <assert.h> /* assert(3) */
#if _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h> /* FD_SETSIZE socklen_t */
#include <sys/select.h> /* FD_ZERO FD_SET fd_set select(2) */
#include <sys/socket.h> /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */
#if defined(AF_UNIX)
#include <sys/un.h> /* struct sockaddr_un */
#endif
#include <fcntl.h> /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */
#include <unistd.h> /* gethostname(3) close(2) */
#include <poll.h> /* POLLIN POLLOUT */
#include <netinet/in.h> /* struct sockaddr_in struct sockaddr_in6 */
#include <arpa/inet.h> /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */
#include <netdb.h> /* struct addrinfo */
#endif
#include "dns.h"
/*
* S T A N D A R D M A C R O S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MIN
#define MIN(a, b) (((a) < (b))? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
#ifndef lengthof
#define lengthof(a) (sizeof (a) / sizeof (a)[0])
#endif
#ifndef endof
#define endof(a) (&(a)[lengthof((a))])
#endif
/*
* D E B U G M A C R O S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int dns_debug = 0;
#if DNS_DEBUG
#undef DNS_DEBUG
#define DNS_DEBUG dns_debug
#define DNS_SAY_(fmt, ...) \
do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0)
#define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n")
#define DNS_HAI DNS_SAY("HAI")
#define DNS_SHOW_(P, fmt, ...) do { \
if (DNS_DEBUG > 1) { \
fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \
fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \
dns_p_dump((P), stderr); \
fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \
} \
} while (0)
#define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "")
#else /* !DNS_DEBUG */
#undef DNS_DEBUG
#define DNS_DEBUG 0
#define DNS_SAY(...)
#define DNS_HAI
#define DNS_SHOW(...)
#endif /* DNS_DEBUG */
/*
* V E R S I O N R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
const char *dns_vendor(void) {
return DNS_VENDOR;
} /* dns_vendor() */
int dns_v_rel(void) {
return DNS_V_REL;
} /* dns_v_rel() */
int dns_v_abi(void) {
return DNS_V_ABI;
} /* dns_v_abi() */
int dns_v_api(void) {
return DNS_V_API;
} /* dns_v_api() */
/*
* E R R O R R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#if _WIN32
#define DNS_EINTR WSAEINTR
#define DNS_EINPROGRESS WSAEINPROGRESS
#define DNS_EISCONN WSAEISCONN
#define DNS_EWOULDBLOCK WSAEWOULDBLOCK
#define DNS_EALREADY WSAEALREADY
#define DNS_EAGAIN EAGAIN
#define DNS_ETIMEDOUT WSAETIMEDOUT
#define dns_syerr() ((int)GetLastError())
#define dns_soerr() ((int)WSAGetLastError())
#else
#define DNS_EINTR EINTR
#define DNS_EINPROGRESS EINPROGRESS
#define DNS_EISCONN EISCONN
#define DNS_EWOULDBLOCK EWOULDBLOCK
#define DNS_EALREADY EALREADY
#define DNS_EAGAIN EAGAIN
#define DNS_ETIMEDOUT ETIMEDOUT
#define dns_syerr() errno
#define dns_soerr() errno
#endif
const char *dns_strerror(int error) {
switch (error) {
case DNS_ENOBUFS:
return "DNS packet buffer too small";
case DNS_EILLEGAL:
return "Illegal DNS RR name or data";
case DNS_EORDER:
return "Attempt to push RR out of section order";
case DNS_ESECTION:
return "Invalid section specified";
case DNS_EUNKNOWN:
return "Unknown DNS error";
default:
return strerror(error);
} /* switch() */
} /* dns_strerror() */
/*
* A T O M I C R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static unsigned dns_atomic_inc(dns_atomic_t *i) {
return (*i)++;
} /* dns_atomic_inc() */
static unsigned dns_atomic_dec(dns_atomic_t *i) {
return (*i)--;
} /* dns_atomic_dec() */
/*
* C R Y P T O R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* P R N G
*/
#ifndef DNS_RANDOM
#if defined(HAVE_ARC4RANDOM) \
|| defined(__OpenBSD__) \
|| defined(__FreeBSD__) \
|| defined(__NetBSD__) \
|| defined(__APPLE__)
#define DNS_RANDOM arc4random
#elif __linux
#define DNS_RANDOM random
#else
#define DNS_RANDOM rand
#endif
#endif
#define DNS_RANDOM_arc4random 1
#define DNS_RANDOM_random 2
#define DNS_RANDOM_rand 3
#define DNS_RANDOM_RAND_bytes 4
#define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM))
#if DNS_RANDOM_OPENSSL
#include <openssl/rand.h>
#endif
static unsigned dns_random_(void) {
#if DNS_RANDOM_OPENSSL
unsigned r;
assert(1 == RAND_bytes((unsigned char *)&r, sizeof r));
return r;
#else
return DNS_RANDOM();
#endif
} /* dns_random_() */
unsigned (*dns_random)(void) __attribute__((weak)) = &dns_random_;
/*
* P E R M U T A T I O N G E N E R A T O R
*/
#define DNS_K_TEA_KEY_SIZE 16
#define DNS_K_TEA_BLOCK_SIZE 8
#define DNS_K_TEA_CYCLES 32
#define DNS_K_TEA_MAGIC 0x9E3779B9U
struct dns_k_tea {
uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)];
unsigned cycles;
}; /* struct dns_k_tea */
static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) {
memcpy(tea->key, key, sizeof tea->key);
tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES;
} /* dns_k_tea_init() */
static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) {
uint32_t y, z, sum, n;
y = v[0];
z = v[1];
sum = 0;
for (n = 0; n < tea->cycles; n++) {
sum += DNS_K_TEA_MAGIC;
y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]);
z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]);
}
w[0] = y;
w[1] = z;
return /* void */;
} /* dns_k_tea_encrypt() */
/*
* Permutation generator, based on a Luby-Rackoff Feistel construction.
*
* Specifically, this is a generic balanced Feistel block cipher using TEA
* (another block cipher) as the pseudo-random function, F. At best it's as
* strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or
* perhaps Bernstein's Salsa20 core; I am naively trying to keep things
* simple.
*
* The generator can create a permutation of any set of numbers, as long as
* the size of the set is an even power of 2. This limitation arises either
* out of an inherent property of balanced Feistel constructions, or by my
* own ignorance. I'll tackle an unbalanced construction after I wrap my
* head around Schneier and Kelsey's paper.
*
* CAVEAT EMPTOR. IANAC.
*/
#define DNS_K_PERMUTOR_ROUNDS 8
struct dns_k_permutor {
unsigned stepi, length, limit;
unsigned shift, mask, rounds;
struct dns_k_tea tea;
}; /* struct dns_k_permutor */
static inline unsigned dns_k_permutor_powof(unsigned n) {
unsigned m, i = 0;
for (m = 1; m < n; m <<= 1, i++)
;;
return i;
} /* dns_k_permutor_powof() */
static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) {
uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)];
unsigned width, i;
p->stepi = 0;
p->length = (high - low) + 1;
p->limit = high;
width = dns_k_permutor_powof(p->length);
width += width % 2;
p->shift = width / 2;
p->mask = (1U << p->shift) - 1;
p->rounds = DNS_K_PERMUTOR_ROUNDS;
for (i = 0; i < lengthof(key); i++)
key[i] = dns_random();
dns_k_tea_init(&p->tea, key, 0);
return /* void */;
} /* dns_k_permutor_init() */
static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) {
uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)];
memset(in, '\0', sizeof in);
in[0] = k;
in[1] = x;
dns_k_tea_encrypt(&p->tea, in, out);
return p->mask & out[0];
} /* dns_k_permutor_F() */
static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) {
unsigned l[2], r[2];
unsigned i;
i = 0;
l[i] = p->mask & (n >> p->shift);
r[i] = p->mask & (n >> 0);
do {
l[(i + 1) % 2] = r[i % 2];
r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]);
i++;
} while (i < p->rounds - 1);
return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0);
} /* dns_k_permutor_E() */
static unsigned dns_k_permutor_step(struct dns_k_permutor *p) {
unsigned n;
do {
n = dns_k_permutor_E(p, p->stepi++);
} while (n >= p->length);
return n + (p->limit + 1 - p->length);
} /* dns_k_permutor_step() */
/*
* Simple permutation box. Useful for shuffling rrsets from an iterator.
* Uses AES s-box to provide good diffusion.
*
* Seems to pass muster under runs test.
*
* $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done
* $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }'
* library(lawstat)
* runs.test(scan(file="/tmp/out"))
* EOF
*/
static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) {
static const unsigned char sbox[256] =
{ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
unsigned char a, b;
unsigned i;
a = 0xff & (n >> 0);
b = 0xff & (n >> 8);
for (i = 0; i < 4; i++) {
a ^= 0xff & s;
a = sbox[a] ^ b;
b = sbox[b] ^ a;
s >>= 8;
}
return ((0xff00 & (a << 8)) | (0x00ff & (b << 0)));
} /* dns_k_shuffle16() */
/*
* U T I L I T Y R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Monotonic Time
*
*/
static time_t dns_now(void) {
/* XXX: Assumes sizeof (time_t) <= sizeof (sig_atomic_t) */
static volatile sig_atomic_t last, tick;
volatile sig_atomic_t tmp_last, tmp_tick;
time_t now;
time(&now);
tmp_last = last;
if (now > tmp_last) {
tmp_tick = tick;
tmp_tick += now - tmp_last;
tick = tmp_tick;
}
last = now;
return tick;
} /* dns_now() */
static time_t dns_elapsed(time_t from) {
time_t now = dns_now();
return (now > from)? now - from : 0;
} /* dns_elpased() */
static size_t dns_af_len(int af) {
static const size_t table[AF_MAX] = {
[AF_INET6] = sizeof (struct sockaddr_in6),
[AF_INET] = sizeof (struct sockaddr_in),
#if defined(AF_UNIX) && !defined(_WIN32)
[AF_UNIX] = sizeof (struct sockaddr_un),
#endif
};
return table[af];
} /* dns_af_len() */
#define dns_sa_len(sa) dns_af_len(dns_sa_family(sa))
#define DNS_SA_NOPORT &dns_sa_noport
static unsigned short dns_sa_noport;
unsigned short *dns_sa_port(int af, void *sa) {
switch (af) {
case AF_INET6:
return &((struct sockaddr_in6 *)sa)->sin6_port;
case AF_INET:
return &((struct sockaddr_in *)sa)->sin_port;
default:
return DNS_SA_NOPORT;
}
} /* dns_sa_port() */
void *dns_sa_addr(int af, void *sa) {
switch (af) {
case AF_INET6:
return &((struct sockaddr_in6 *)sa)->sin6_addr;
case AF_INET:
return &((struct sockaddr_in *)sa)->sin_addr;
default:
return 0;
}
} /* dns_sa_addr() */
#if _WIN32
static int dns_inet_pton(int af, const void *src, void *dst) {
union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u;
u.sin.sin_family = af;
if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u }))
return -1;
switch (af) {
case AF_INET6:
*(struct in6_addr *)dst = u.sin6.sin6_addr;
return 1;
case AF_INET:
*(struct in_addr *)dst = u.sin.sin_addr;
return 1;
default:
return 0;
}
} /* dns_inet_pton() */
const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) {
union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u;
/* NOTE: WSAAddressToString will print .sin_port unless zeroed. */
memset(&u, 0, sizeof u);
u.sin.sin_family = af;
switch (af) {
case AF_INET6:
u.sin6.sin6_addr = *(struct in6_addr *)src;
break;
case AF_INET:
u.sin.sin_addr = *(struct in_addr *)src;
break;
default:
return 0;
}
if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim))
return 0;
return dst;
} /* dns_inet_ntop() */
#endif
size_t dns_strlcpy(char *dst, const char *src, size_t lim) {
char *d = dst;
char *e = &dst[lim];
const char *s = src;
if (d < e) {
do {
if ('\0' == (*d++ = *s++))
return s - src - 1;
} while (d < e);
d[-1] = '\0';
}
while (*s++ != '\0')
;;
return s - src - 1;
} /* dns_strlcpy() */
size_t dns_strlcat(char *dst, const char *src, size_t lim) {
char *d = memchr(dst, '\0', lim);
char *e = &dst[lim];
const char *s = src;
const char *p;
if (d && d < e) {
do {
if ('\0' == (*d++ = *s++))
return d - dst - 1;
} while (d < e);
d[-1] = '\0';
}
p = s;
while (*s++ != '\0')
;;
return lim + (s - p - 1);
} /* dns_strlcat() */
#if defined(_WIN32) || defined(__SUNPRO_C)
static char *dns_strsep(char **sp, const char *delim) {
char *p;
if (!(p = *sp))
return 0;
*sp += strcspn(p, delim);
if (**sp != '\0') {
**sp = '\0';
++*sp;
} else
*sp = NULL;
return p;
} /* dns_strsep() */
#else
#define dns_strsep(...) strsep(__VA_ARGS__)
#endif
#if _WIN32
#define strcasecmp(...) _stricmp(__VA_ARGS__)
#define strncasecmp(...) _strnicmp(__VA_ARGS__)
#endif
static int dns_poll(int fd, short events, int timeout) {
fd_set rset, wset;
if (!events)
return 0;
assert(fd >= 0 && fd < FD_SETSIZE);
FD_ZERO(&rset);
FD_ZERO(&wset);
if (events & DNS_POLLIN)
FD_SET(fd, &rset);
if (events & DNS_POLLOUT)
FD_SET(fd, &wset);
select(fd + 1, &rset, &wset, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : NULL);
return 0;
} /* dns_poll() */
/*
* P A C K E T R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
unsigned dns_p_count(struct dns_packet *P, enum dns_section section) {
unsigned count;
switch (section) {
case DNS_S_QD:
return ntohs(dns_header(P)->qdcount);
case DNS_S_AN:
return ntohs(dns_header(P)->ancount);
case DNS_S_NS:
return ntohs(dns_header(P)->nscount);
case DNS_S_AR:
return ntohs(dns_header(P)->arcount);
default:
count = 0;
if (section & DNS_S_QD)
count += ntohs(dns_header(P)->qdcount);
if (section & DNS_S_AN)
count += ntohs(dns_header(P)->ancount);
if (section & DNS_S_NS)
count += ntohs(dns_header(P)->nscount);
if (section & DNS_S_AR)
count += ntohs(dns_header(P)->arcount);
return count;
}
} /* dns_p_count() */
struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) {
if (!P)
return 0;
assert(size >= offsetof(struct dns_packet, data) + 12);
memset(P, 0, sizeof *P);
P->size = size - offsetof(struct dns_packet, data);
P->end = 12;
memset(P->data, '\0', 12);
return P;
} /* dns_p_init() */
static unsigned short dns_p_qend(struct dns_packet *P) {
unsigned short qend = 12;
unsigned i, count = dns_p_count(P, DNS_S_QD);
for (i = 0; i < count && qend < P->end; i++) {
if (P->end == (qend = dns_d_skip(qend, P)))
goto invalid;
if (P->end - qend < 4)
goto invalid;
qend += 4;
}
return MIN(qend, P->end);
invalid:
return P->end;
} /* dns_p_qend() */
struct dns_packet *dns_p_make(size_t len, int *error) {
struct dns_packet *P;
size_t size = dns_p_calcsize(len);
if (!(P = dns_p_init(malloc(size), size)))
*error = dns_syerr();
return P;
} /* dns_p_make() */
int dns_p_grow(struct dns_packet **P) {
struct dns_packet *tmp;
size_t size;
int error;
if (!*P) {
if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error)))
return error;
return 0;
}
size = dns_p_sizeof(*P);
size |= size >> 1;
size |= size >> 2;
size |= size >> 4;
size |= size >> 8;
size++;
if (size > 65536)
return DNS_ENOBUFS;
if (!(tmp = realloc(*P, dns_p_calcsize(size))))
return dns_syerr();
tmp->size = size;
*P = tmp;
return 0;
} /* dns_p_grow() */
struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) {
if (!P)
return 0;
P->end = MIN(P->size, P0->end);
memcpy(P->data, P0->data, P->end);
return P;
} /* dns_p_copy() */
struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) {
size_t bufsiz = MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0));
struct dns_packet *M;
enum dns_section section;
struct dns_rr rr, mr;
int error, copy;
if (!A && B) {
A = B;
Amask = Bmask;
B = 0;
}
merge:
if (!(M = dns_p_make(bufsiz, &error)))
goto error;
for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) {
if (A && (section & Amask)) {
dns_rr_foreach(&rr, A, .section = section) {
if ((error = dns_rr_copy(M, &rr, A)))
goto error;
}
}
if (B && (section & Bmask)) {
dns_rr_foreach(&rr, B, .section = section) {
copy = 1;
dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) {
if (!(copy = dns_rr_cmp(&rr, B, &mr, M)))
break;
}
if (copy && (error = dns_rr_copy(M, &rr, B)))
goto error;
}
}
}
return M;
error:
free(M); M = 0;
if (error == DNS_ENOBUFS && bufsiz < 65535) {
bufsiz = MIN(65535, bufsiz * 2);
goto merge;
}
*error_ = error;
return 0;
} /* dns_p_merge() */
static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t);
void dns_p_dictadd(struct dns_packet *P, unsigned short dn) {
unsigned short lp, lptr, i;
lp = dn;
while (lp < P->end) {
if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) {
lptr = ((0x3f & P->data[lp + 0]) << 8)
| ((0xff & P->data[lp + 1]) << 0);
for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) {
if (P->dict[i] == lptr) {
P->dict[i] = dn;
return;
}
}
}
lp = dns_l_skip(lp, P->data, P->end);
}
for (i = 0; i < lengthof(P->dict); i++) {
if (!P->dict[i]) {
P->dict[i] = dn;
break;
}
}
} /* dns_p_dictadd() */
int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) {
size_t end = P->end;
int error;
if ((error = dns_d_push(P, dn, dnlen)))
goto error;
if (P->size - P->end < 4)
goto nobufs;
P->data[P->end++] = 0xff & (type >> 8);
P->data[P->end++] = 0xff & (type >> 0);
P->data[P->end++] = 0xff & (class >> 8);
P->data[P->end++] = 0xff & (class >> 0);
if (section == DNS_S_QD)
goto update;
if (P->size - P->end < 6)
goto nobufs;
P->data[P->end++] = 0x7f & (ttl >> 24);
P->data[P->end++] = 0xff & (ttl >> 16);
P->data[P->end++] = 0xff & (ttl >> 8);
P->data[P->end++] = 0xff & (ttl >> 0);
if ((error = dns_any_push(P, (union dns_any *)any, type)))
goto error;
update:
switch (section) {
case DNS_S_QD:
if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR))
goto order;
if (!P->qd.base && (error = dns_p_study(P)))
goto error;
dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1);
P->qd.end = P->end;
P->an.base = P->end;
P->an.end = P->end;
P->ns.base = P->end;
P->ns.end = P->end;
P->ar.base = P->end;
P->ar.end = P->end;
break;
case DNS_S_AN:
if (dns_p_count(P, DNS_S_NS|DNS_S_AR))
goto order;
if (!P->an.base && (error = dns_p_study(P)))
goto error;
dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1);
P->an.end = P->end;
P->ns.base = P->end;
P->ns.end = P->end;
P->ar.base = P->end;
P->ar.end = P->end;
break;
case DNS_S_NS:
if (dns_p_count(P, DNS_S_AR))
goto order;
if (!P->ns.base && (error = dns_p_study(P)))
goto error;
dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1);
P->ns.end = P->end;
P->ar.base = P->end;
P->ar.end = P->end;
break;
case DNS_S_AR:
if (!P->ar.base && (error = dns_p_study(P)))
goto error;
dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1);
P->ar.end = P->end;
break;
default:
error = DNS_ESECTION;
goto error;
} /* switch() */
return 0;
nobufs:
error = DNS_ENOBUFS;
goto error;
order:
error = DNS_EORDER;
goto error;
error:
P->end = end;
return error;
} /* dns_p_push() */
static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) {
enum dns_section section;
struct dns_rr rr;
int error;
union dns_any any;
char pretty[sizeof any * 2];
size_t len;
fputs(";; [HEADER]\n", fp);
fprintf(fp, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr);
fprintf(fp, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode);
fprintf(fp, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa);
fprintf(fp, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc);
fprintf(fp, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd);
fprintf(fp, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra);
fprintf(fp, ";; rcode : %s(%d)\n", dns_strrcode(dns_header(P)->rcode), dns_header(P)->rcode);
section = 0;
while (dns_rr_grep(&rr, 1, I, P, &error)) {
if (section != rr.section)
fprintf(fp, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section));
if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error)))
fprintf(fp, "%s\n", pretty);
section = rr.section;
}
} /* dns_p_dump3() */
void dns_p_dump(struct dns_packet *P, FILE *fp) {
dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp);
} /* dns_p_dump() */
static void dns_s_unstudy(struct dns_s_memo *m)
{ m->base = 0; m->end = 0; }
static void dns_p_unstudy(struct dns_packet *P) {
dns_s_unstudy(&P->qd);
dns_s_unstudy(&P->an);
dns_s_unstudy(&P->ns);
dns_s_unstudy(&P->ar);
} /* dns_p_unstudy() */
static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned base, struct dns_packet *P) {
unsigned short count, rp;
count = dns_p_count(P, section);
for (rp = base; count && rp < P->end; count--)
rp = dns_rr_skip(rp, P);
m->base = base;
m->end = rp;
return 0;
} /* dns_s_study() */
int dns_p_study(struct dns_packet *P) {
int error;
if ((error = dns_s_study(&P->qd, DNS_S_QD, 12, P)))
goto error;
if ((error = dns_s_study(&P->an, DNS_S_AN, P->qd.end, P)))
goto error;
if ((error = dns_s_study(&P->ns, DNS_S_NS, P->an.end, P)))
goto error;
if ((error = dns_s_study(&P->ar, DNS_S_AR, P->ns.end, P)))
goto error;
return 0;
error:
dns_p_unstudy(P);
return error;
} /* dns_p_study() */
/*
* D O M A I N N A M E R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DNS_D_MAXPTRS
#define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */
#endif
static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) {
unsigned short len;
unsigned nptrs = 0;
retry:
if (src >= end)
goto invalid;
switch (0x03 & (data[src] >> 6)) {
case 0x00:
len = (0x3f & (data[src++]));
if (end - src < len)
goto invalid;
if (lim > 0) {
memcpy(dst, &data[src], MIN(lim, len));
dst[MIN(lim - 1, len)] = '\0';
}
*nxt = src + len;
return len;
case 0x01:
goto invalid;
case 0x02:
goto invalid;
case 0x03:
if (++nptrs > DNS_D_MAXPTRS)
goto invalid;
if (end - src < 2)
goto invalid;
src = ((0x3f & data[src + 0]) << 8)
| ((0xff & data[src + 1]) << 0);
goto retry;
} /* switch() */
/* NOT REACHED */
invalid:
*nxt = end;
return 0;
} /* dns_l_expand() */
static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) {
unsigned short len;
if (src >= end)
goto invalid;
switch (0x03 & (data[src] >> 6)) {
case 0x00:
len = (0x3f & (data[src++]));
if (end - src < len)
goto invalid;
return (len)? src + len : end;
case 0x01:
goto invalid;
case 0x02:
goto invalid;
case 0x03:
return end;
} /* switch() */
/* NOT REACHED */
invalid:
return end;
} /* dns_l_skip() */
size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) {
unsigned char *dst = dst_;
const unsigned char *src = src_;
size_t dp = 0, sp = 0;
int lc;
/* trim any leading dot(s) */
while (sp < len && src[sp] == '.')
sp++;
for (lc = 0; sp < len; lc = src[sp]) {
if (dp < lim)
dst[dp] = src[sp];
sp++;
dp++;
/* trim extra dot(s) */
while (sp < len && src[sp] == '.')
sp++;
}
if ((flags & DNS_D_ANCHOR) && lc != '.') {
if (dp < lim)
dst[dp] = '.';
dp++;
}
if (lim > 0)
dst[MIN(dp, lim - 1)] = '\0';
return dp;
} /* dns_d_trim() */
char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) {
if (flags & DNS_D_TRIM) {
dns_d_trim(dst, lim, src, len, flags);
} if (flags & DNS_D_ANCHOR) {
dns_d_anchor(dst, lim, src, len);
} else {
memmove(dst, src, MIN(lim, len));
if (lim > 0)
((char *)dst)[MIN(len, lim - 1)] = '\0';
}
return dst;
} /* dns_d_init() */
size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) {
if (len == 0)
return 0;
memmove(dst, src, MIN(lim, len));
if (((const char *)src)[len - 1] != '.') {
if (len < lim)
((char *)dst)[len] = '.';
len++;
}
if (lim > 0)
((char *)dst)[MIN(lim - 1, len)] = '\0';
return len;
} /* dns_d_anchor() */
size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) {
const char *dot;
/* XXX: Skip any leading dot. Handles cleaving root ".". */
if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1)))
return 0;
len -= dot - (const char *)src;
/* XXX: Unless root, skip the label's trailing dot. */
if (len > 1) {
src = ++dot;
len--;
} else
src = dot;
memmove(dst, src, MIN(lim, len));
if (lim > 0)
((char *)dst)[MIN(lim - 1, len)] = '\0';
return len;
} /* dns_d_cleave() */
size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error EINA_UNUSED) {
struct { unsigned char *b; size_t p, x; } dst, src;
unsigned char ch = '.';
dst.b = dst_;
dst.p = 0;
dst.x = 1;
src.b = (unsigned char *)src_;
src.p = 0;
src.x = 0;
while (src.x < len) {
ch = src.b[src.x];
if (ch == '.') {
if (dst.p < lim)
dst.b[dst.p] = (0x3f & (src.x - src.p));
dst.p = dst.x++;
src.p = ++src.x;
} else {
if (dst.x < lim)
dst.b[dst.x] = ch;
dst.x++;
src.x++;
}
} /* while() */
if (src.x > src.p) {
if (dst.p < lim)
dst.b[dst.p] = (0x3f & (src.x - src.p));
dst.p = dst.x;
}
if (dst.p > 1) {
if (dst.p < lim)
dst.b[dst.p] = 0x00;
dst.p++;
}
#if 1
if (dst.p < lim) {
struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b;
unsigned i;
a.p = 0;
while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) {
for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) {
b.p = P->dict[i];
while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) {
a.y = a.x;
b.y = b.x;
while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) {
a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim);
b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end);
}
if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) {
dst.b[a.p++] = 0xc0
| (0x3f & (b.p >> 8));
dst.b[a.p++] = (0xff & (b.p >> 0));
return a.p;
}
b.p = b.x;
} /* while() */
} /* for() */
a.p = a.x;
} /* while() */
} /* if () */
#endif
return dst.p;
} /* dns_d_comp() */
unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) {
unsigned short len;
while (src < P->end) {
switch (0x03 & (P->data[src] >> 6)) {
case 0x00: /* FOLLOWS */
len = (0x3f & P->data[src++]);
if (0 == len) {
/* success ==> */ return src;
} else if (P->end - src > len) {
src += len;
break;
} else
goto invalid;
/* NOT REACHED */
case 0x01: /* RESERVED */
goto invalid;
case 0x02: /* RESERVED */
goto invalid;
case 0x03: /* POINTER */
if (P->end - src < 2)
goto invalid;
src += 2;
/* success ==> */ return src;
} /* switch() */
} /* while() */
invalid:
//assert(0);
return P->end;
} /* dns_d_skip() */
#include <stdio.h>
size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) {
size_t dstp = 0;
unsigned nptrs = 0;
unsigned char len;
while (src < P->end) {
switch ((0x03 & (P->data[src] >> 6))) {
case 0x00: /* FOLLOWS */
len = (0x3f & P->data[src]);
if (0 == len) {
if (dstp == 0) {
if (dstp < lim)
((unsigned char *)dst)[dstp] = '.';
dstp++;
}
/* NUL terminate */
if (lim > 0)
((unsigned char *)dst)[MIN(dstp, lim - 1)] = '\0';
/* success ==> */ return dstp;
}
src++;
if (P->end - src < len)
goto toolong;
if (dstp < lim)
memcpy(&((unsigned char *)dst)[dstp], &P->data[src], MIN(len, lim - dstp));
src += len;
dstp += len;
if (dstp < lim)
((unsigned char *)dst)[dstp] = '.';
dstp++;
nptrs = 0;
continue;
case 0x01: /* RESERVED */
goto reserved;
case 0x02: /* RESERVED */
goto reserved;
case 0x03: /* POINTER */
if (++nptrs > DNS_D_MAXPTRS)
goto toolong;
if (P->end - src < 2)
goto toolong;
src = ((0x3f & P->data[src + 0]) << 8)
| ((0xff & P->data[src + 1]) << 0);
continue;
} /* switch() */
} /* while() */
toolong:
*error = DNS_EILLEGAL;
if (lim > 0)
((unsigned char *)dst)[MIN(dstp, lim - 1)] = '\0';
return 0;
reserved:
*error = DNS_EILLEGAL;
if (lim > 0)
((unsigned char *)dst)[MIN(dstp, lim - 1)] = '\0';
return 0;
} /* dns_d_expand() */
int dns_d_push(struct dns_packet *P, const void *dn, size_t len) {
size_t lim = P->size - P->end;
unsigned dp = P->end;
int error;
len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error);
if (len == 0)
return error;
if (len > lim)
return DNS_ENOBUFS;
P->end += len;
dns_p_dictadd(P, dp);
return 0;
} /* dns_d_push() */
size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) {
char host[DNS_D_MAXNAME + 1];
struct dns_rr_i i;
struct dns_rr rr;
unsigned depth;
int error;
if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len))
{ error = ENAMETOOLONG; goto error; }
for (depth = 0; depth < 7; depth++) {
dns_rr_i_init(memset(&i, 0, sizeof i), P);
i.section = DNS_S_ALL & ~DNS_S_QD;
i.name = host;
i.type = DNS_T_CNAME;
if (!dns_rr_grep(&rr, 1, &i, P, &error))
break;
if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P)))
goto error;
}
return dns_strlcpy(dst, host, lim);
error:
*error_ = error;
return 0;
} /* dns_d_cname() */
/*
* R E S O U R C E R E C O R D R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) {
unsigned char dn[DNS_D_MAXNAME + 1];
union dns_any any;
size_t len;
int error;
if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error)))
return error;
else if (len >= sizeof dn)
return DNS_EILLEGAL;
if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q)))
return error;
return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any);
} /* dns_rr_copy() */
int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) {
unsigned short p = src;
if (src >= P->end)
goto invalid;
rr->dn.p = p;
rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p;
if (P->end - p < 4)
goto invalid;
rr->type = ((0xff & P->data[p + 0]) << 8)
| ((0xff & P->data[p + 1]) << 0);
rr->class = ((0xff & P->data[p + 2]) << 8)
| ((0xff & P->data[p + 3]) << 0);
p += 4;
if (src < dns_p_qend(P)) {
rr->section = DNS_S_QUESTION;
rr->ttl = 0;
rr->rd.p = 0;
rr->rd.len = 0;
return 0;
}
if (P->end - p < 4)
goto invalid;
rr->ttl = ((0x7f & P->data[p + 0]) << 24)
| ((0xff & P->data[p + 1]) << 16)
| ((0xff & P->data[p + 2]) << 8)
| ((0xff & P->data[p + 3]) << 0);
p += 4;
if (P->end - p < 2)
goto invalid;
rr->rd.len = ((0xff & P->data[p + 0]) << 8)
| ((0xff & P->data[p + 1]) << 0);
rr->rd.p = p + 2;
p += 2;
if (P->end - p < rr->rd.len)
goto invalid;
return 0;
invalid:
//assert(0);
return DNS_EILLEGAL;
} /* dns_rr_parse() */
static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) {
unsigned short rp, rdlen;
rp = dns_d_skip(src, P);
if (P->end - rp < 4)
return P->end - src;
rp += 4; /* TYPE, CLASS */
if (rp <= dns_p_qend(P))
return rp - src;
if (P->end - rp < 6)
return P->end - src;
rp += 6; /* TTL, RDLEN */
rdlen = ((0xff & P->data[rp - 2]) << 8)
| ((0xff & P->data[rp - 1]) << 0);
if (P->end - rp < rdlen)
return P->end - src;
rp += rdlen;
return rp - src;
} /* dns_rr_len() */
unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) {
return src + dns_rr_len(src, P);
} /* dns_rr_skip() */
static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) {
enum dns_section section;
unsigned count, ind;
unsigned short rp;
if (src >= P->qd.base && src < P->qd.end)
return DNS_S_QD;
if (src >= P->an.base && src < P->an.end)
return DNS_S_AN;
if (src >= P->ns.base && src < P->ns.end)
return DNS_S_NS;
if (src >= P->ar.base && src < P->ar.end)
return DNS_S_AR;
/* NOTE: Possibly bad memoization. Try it the hard-way. */
for (rp = 12, ind = 0; rp < src && rp < P->end; ind++)
rp = dns_rr_skip(rp, P);
section = DNS_S_QD;
count = dns_p_count(P, section);
while (ind >= count && section <= DNS_S_AR) {
section <<= 1;
count += dns_p_count(P, section);
}
return DNS_S_ALL & section;
} /* dns_rr_section() */
static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) {
struct dns_rr rr;
int error;
if ((error = dns_rr_parse(&rr, src, P)))
return 0;
return rr.type;
} /* dns_rr_type() */
int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) {
char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1];
union dns_any any0, any1;
int cmp, error;
size_t len;
if ((cmp = r0->type - r1->type))
return cmp;
if ((cmp = r0->class - r1->class))
return cmp;
/*
* FIXME: Do label-by-label comparison to handle illegally long names?
*/
if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error))
|| len >= sizeof host0)
return -1;
if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error))
|| len >= sizeof host1)
return 1;
if ((cmp = strcasecmp(host0, host1)))
return cmp;
if (DNS_S_QD & (r0->section | r1->section)) {
if (r0->section == r1->section)
return 0;
return (r0->section == DNS_S_QD)? -1 : 1;
}
if ((error = dns_any_parse(&any0, r0, P0)))
return -1;
if ((error = dns_any_parse(&any1, r1, P1)))
return 1;
return dns_any_cmp(&any0, r0->type, &any1, r1->type);
} /* dns_rr_cmp() */
static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) {
struct dns_rr rr1;
dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) {
if (0 == dns_rr_cmp(rr0, P0, &rr1, P1))
return 1;
}
return 0;
} /* dns_rr_exists() */
static unsigned short dns_rr_offset(struct dns_rr *rr) {
return rr->dn.p;
} /* dns_rr_offset() */
static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) {
if (i->section && !(rr->section & i->section))
return 0;
if (i->type && rr->type != i->type && i->type != DNS_T_ALL)
return 0;
if (i->class && rr->class != i->class && i->class != DNS_C_ANY)
return 0;
if (i->name) {
char dn[DNS_D_MAXNAME + 1];
size_t len;
int error;
if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error))
|| len >= sizeof dn)
return 0;
if (0 != strcasecmp(dn, i->name))
return 0;
}
if (i->data && i->type && rr->section > DNS_S_QD) {
union dns_any rd;
int error;
if ((error = dns_any_parse(&rd, rr, P)))
return 0;
if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type))
return 0;
}
return 1;
} /* dns_rr_i_match() */
static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) {
unsigned short rp;
struct dns_rr r0, rr;
int error;
if ((i->section & DNS_S_QD) && P->qd.base)
rp = P->qd.base;
else if ((i->section & DNS_S_AN) && P->an.base)
rp = P->an.base;
else if ((i->section & DNS_S_NS) && P->ns.base)
rp = P->ns.base;
else if ((i->section & DNS_S_AR) && P->ar.base)
rp = P->ar.base;
else
rp = 12;
for (rp = 12; rp < P->end; rp = dns_rr_skip(rp, P)) {
if ((error = dns_rr_parse(&rr, rp, P)))
continue;
rr.section = dns_rr_section(rp, P);
if (!dns_rr_i_match(&rr, i, P))
continue;
r0 = rr;
goto lower;
}
return P->end;
lower:
if (i->sort == &dns_rr_i_packet)
return dns_rr_offset(&r0);
while ((rp = dns_rr_skip(rp, P)) < P->end) {
if ((error = dns_rr_parse(&rr, rp, P)))
continue;
rr.section = dns_rr_section(rp, P);
if (!dns_rr_i_match(&rr, i, P))
continue;
if (i->sort(&rr, &r0, i, P) < 0)
r0 = rr;
}
return dns_rr_offset(&r0);
} /* dns_rr_i_start() */
static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) {
struct dns_rr r0, r1, rr;
int error;
if ((error = dns_rr_parse(&r0, rp, P)))
return P->end;
r0.section = dns_rr_section(rp, P);
rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12;
for (; rp < P->end; rp = dns_rr_skip(rp, P)) {
if ((error = dns_rr_parse(&rr, rp, P)))
continue;
rr.section = dns_rr_section(rp, P);
if (!dns_rr_i_match(&rr, i, P))
continue;
if (i->sort(&rr, &r0, i, P) <= 0)
continue;
r1 = rr;
goto lower;
}
return P->end;
lower:
if (i->sort == &dns_rr_i_packet)
return dns_rr_offset(&r1);
while ((rp = dns_rr_skip(rp, P)) < P->end) {
if ((error = dns_rr_parse(&rr, rp, P)))
continue;
rr.section = dns_rr_section(rp, P);
if (!dns_rr_i_match(&rr, i, P))
continue;
if (i->sort(&rr, &r0, i, P) <= 0)
continue;
if (i->sort(&rr, &r1, i, P) >= 0)
continue;
r1 = rr;
}
return dns_rr_offset(&r1);
} /* dns_rr_i_skip() */
int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i EINA_UNUSED, struct dns_packet *P EINA_UNUSED) {
return (int)a->dn.p - (int)b->dn.p;
} /* dns_rr_i_packet() */
int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i EINA_UNUSED, struct dns_packet *P) {
int cmp;
if ((cmp = a->section - b->section))
return cmp;
if (a->type != b->type)
return (int)a->dn.p - (int)b->dn.p;
return dns_rr_cmp(a, P, b, P);
} /* dns_rr_i_order() */
int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P EINA_UNUSED) {
int cmp;
while (!i->state.regs[0])
i->state.regs[0] = dns_random();
if ((cmp = a->section - b->section))
return cmp;
return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]);
} /* dns_rr_i_shuffle() */
struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P EINA_UNUSED) {
static const struct dns_rr_i i_initializer;
i->state = i_initializer.state;
i->saved = i->state;
return i;
} /* dns_rr_i_init() */
unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) {
unsigned count = 0;
int error;
switch (i->state.exec) {
case 0:
if (!i->sort)
i->sort = &dns_rr_i_packet;
i->state.next = dns_rr_i_start(i, P);
i->state.exec++;
/* FALL THROUGH */
case 1:
while (count < lim && i->state.next < P->end) {
if ((error = dns_rr_parse(rr, i->state.next, P)))
goto error;
rr->section = dns_rr_section(i->state.next, P);
rr++;
count++;
i->state.count++;
i->state.next = dns_rr_i_skip(i->state.next, i, P);
} /* while() */
break;
} /* switch() */
return count;
error:
*error_ = error;
return count;
} /* dns_rr_grep() */
static size_t dns__printchar(void *dst, size_t lim, size_t cp, unsigned char ch) {
if (cp < lim)
((unsigned char *)dst)[cp] = ch;
return 1;
} /* dns__printchar() */
static size_t dns__printstring(void *dst, size_t lim, size_t cp, const void *src, size_t len) {
if (cp < lim)
memcpy(&((unsigned char *)dst)[cp], src, MIN(len, lim - cp));
return len;
} /* dns__printstring() */
#define dns__printstring5(a, b, c, d, e) dns__printstring((a), (b), (c), (d), (e))
#define dns__printstring4(a, b, c, d) dns__printstring((a), (b), (c), (d), strlen((d)))
#define dns__printstring(...) DNS_PP_CALL(DNS_PP_XPASTE(dns__printstring, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
static void dns__printnul(void *dst, size_t lim, size_t off) {
if (lim > 0)
((unsigned char *)dst)[MIN(off, lim - 1)] = '\0';
} /* dns__printnul() */
static size_t dns__print10(void *dst, size_t lim, size_t off, unsigned n, unsigned pad) {
unsigned char tmp[32];
unsigned dp = off;
unsigned cp = 0;
unsigned d = 1000000000;
unsigned ch;
pad = MAX(1, pad);
while (d) {
if ((ch = n / d) || cp > 0) {
n -= ch * d;
tmp[cp] = '0' + ch;
cp++;
}
d /= 10;
}
while (cp < pad) {
dp += dns__printchar(dst, lim, dp, '0');
pad--;
}
dp += dns__printstring(dst, lim, dp, tmp, cp);
return dp - off;
} /* dns__print10() */
size_t dns_rr_print(void *dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *error_) {
union dns_any any;
size_t cp, n, rdlen;
void *rd;
int error;
cp = 0;
if (rr->section == DNS_S_QD)
cp += dns__printchar(dst, lim, cp, ';');
if (!(n = dns_d_expand(&((unsigned char *)dst)[cp], (cp < lim)? lim - cp : 0, rr->dn.p, P, &error)))
goto error;
cp += n;
if (rr->section != DNS_S_QD) {
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__print10(dst, lim, cp, rr->ttl, 0);
}
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__printstring(dst, lim, cp, dns_strclass(rr->class), strlen(dns_strclass(rr->class)));
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__printstring(dst, lim, cp, dns_strtype(rr->type), strlen(dns_strtype(rr->type)));
if (rr->section == DNS_S_QD)
goto epilog;
cp += dns__printchar(dst, lim, cp, ' ');
if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P)))
goto error;
if (cp < lim) {
rd = &((unsigned char *)dst)[cp];
rdlen = lim - cp;
} else {
rd = 0;
rdlen = 0;
}
cp += dns_any_print(rd, rdlen, &any, rr->type);
epilog:
dns__printnul(dst, lim, cp);
return cp;
error:
*error_ = error;
return 0;
} /* dns_rr_print() */
int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) {
unsigned long addr;
if (rr->rd.len != 4)
return DNS_EILLEGAL;
addr = ((0xff & P->data[rr->rd.p + 0]) << 24)
| ((0xff & P->data[rr->rd.p + 1]) << 16)
| ((0xff & P->data[rr->rd.p + 2]) << 8)
| ((0xff & P->data[rr->rd.p + 3]) << 0);
a->addr.s_addr = htonl(addr);
return 0;
} /* dns_a_parse() */
int dns_a_push(struct dns_packet *P, struct dns_a *a) {
unsigned long addr;
if (P->size - P->end < 6)
return DNS_ENOBUFS;
P->data[P->end++] = 0x00;
P->data[P->end++] = 0x04;
addr = ntohl(a->addr.s_addr);
P->data[P->end++] = 0xff & (addr >> 24);
P->data[P->end++] = 0xff & (addr >> 16);
P->data[P->end++] = 0xff & (addr >> 8);
P->data[P->end++] = 0xff & (addr >> 0);
return 0;
} /* dns_a_push() */
size_t dns_a_arpa(void *dst, size_t lim, const struct dns_a *a) {
unsigned long a4 = ntohl(a->addr.s_addr);
size_t cp = 0;
unsigned i;
for (i = 4; i > 0; i--) {
cp += dns__print10(dst, lim, cp, (0xff & a4), 0);
cp += dns__printchar(dst, lim, cp, '.');
a4 >>= 8;
}
cp += dns__printstring(dst, lim, cp, "in-addr.arpa.");
dns__printnul(dst, lim, cp);
return cp;
} /* dns_a_arpa() */
int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) {
if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr))
return -1;
if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr))
return 1;
return 0;
} /* dns_a_cmp() */
size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) {
char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0";
size_t len;
dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr);
dns__printnul(dst, lim, (len = dns__printstring(dst, lim, 0, addr)));
return len;
} /* dns_a_print() */
int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) {
if (rr->rd.len != sizeof aaaa->addr.s6_addr)
return DNS_EILLEGAL;
memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr);
return 0;
} /* dns_aaaa_parse() */
int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) {
if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr)
return DNS_ENOBUFS;
P->data[P->end++] = 0x00;
P->data[P->end++] = 0x10;
memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr);
P->end += sizeof aaaa->addr.s6_addr;
return 0;
} /* dns_aaaa_push() */
int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) {
unsigned i;
int cmp;
for (i = 0; i < lengthof(a->addr.s6_addr); i++) {
if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i])))
return cmp;
}
return 0;
} /* dns_aaaa_cmp() */
size_t dns_aaaa_arpa(void *dst, size_t lim, const struct dns_aaaa *aaaa) {
static const unsigned char hex[16] = "0123456789abcdef";
size_t cp = 0;
unsigned nyble;
int i, j;
for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) {
nyble = aaaa->addr.s6_addr[i];
for (j = 0; j < 2; j++) {
cp += dns__printchar(dst, lim, cp, hex[0x0f & nyble]);
cp += dns__printchar(dst, lim, cp, '.');
nyble >>= 4;
}
}
cp += dns__printstring(dst, lim, cp, "ip6.arpa.");
dns__printnul(dst, lim, cp);
return cp;
} /* dns_aaaa_arpa() */
size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) {
char addr[INET6_ADDRSTRLEN + 1] = "::";
size_t len;
dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr);
dns__printnul(dst, lim, (len = dns__printstring(dst, lim, 0, addr)));
return len;
} /* dns_aaaa_print() */
int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) {
size_t len;
int error;
if (rr->rd.len < 3)
return DNS_EILLEGAL;
mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8))
| (0x00ff & (P->data[rr->rd.p + 1] << 0));
if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error)))
return error;
else if (len >= sizeof mx->host)
return DNS_EILLEGAL;
return 0;
} /* dns_mx_parse() */
int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) {
size_t end, len;
int error;
if (P->size - P->end < 5)
return DNS_ENOBUFS;
end = P->end;
P->end += 2;
P->data[P->end++] = 0xff & (mx->preference >> 8);
P->data[P->end++] = 0xff & (mx->preference >> 0);
if ((error = dns_d_push(P, mx->host, strlen(mx->host))))
goto error;
len = P->end - end - 2;
P->data[end + 0] = 0xff & (len >> 8);
P->data[end + 1] = 0xff & (len >> 0);
return 0;
error:
P->end = end;
return error;
} /* dns_mx_push() */
int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) {
int cmp;
if ((cmp = a->preference - b->preference))
return cmp;
return strcasecmp(a->host, b->host);
} /* dns_mx_cmp() */
size_t dns_mx_print(void *dst, size_t lim, struct dns_mx *mx) {
size_t cp = 0;
cp += dns__print10(dst, lim, cp, mx->preference, 0);
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__printstring(dst, lim, cp, mx->host, strlen(mx->host));
dns__printnul(dst, lim, cp);
return cp;
} /* dns_mx_print() */
size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) {
return dns_strlcpy(dst, mx->host, lim);
} /* dns_mx_cname() */
int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) {
size_t len;
int error;
if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error)))
return error;
else if (len >= sizeof ns->host)
return DNS_EILLEGAL;
return 0;
} /* dns_ns_parse() */
int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) {
size_t end, len;
int error;
if (P->size - P->end < 3)
return DNS_ENOBUFS;
end = P->end;
P->end += 2;
if ((error = dns_d_push(P, ns->host, strlen(ns->host))))
goto error;
len = P->end - end - 2;
P->data[end + 0] = 0xff & (len >> 8);
P->data[end + 1] = 0xff & (len >> 0);
return 0;
error:
P->end = end;
return error;
} /* dns_ns_push() */
int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) {
return strcasecmp(a->host, b->host);
} /* dns_ns_cmp() */
size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) {
size_t cp;
cp = dns__printstring(dst, lim, 0, ns->host, strlen(ns->host));
dns__printnul(dst, lim, cp);
return cp;
} /* dns_ns_print() */
size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) {
return dns_strlcpy(dst, ns->host, lim);
} /* dns_ns_cname() */
int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) {
return dns_ns_parse((struct dns_ns *)cname, rr, P);
} /* dns_cname_parse() */
int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) {
return dns_ns_push(P, (struct dns_ns *)cname);
} /* dns_cname_push() */
int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) {
return strcasecmp(a->host, b->host);
} /* dns_cname_cmp() */
size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) {
return dns_ns_print(dst, lim, (struct dns_ns *)cname);
} /* dns_cname_print() */
size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) {
return dns_strlcpy(dst, cname->host, lim);
} /* dns_cname_cname() */
int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) {
struct { void *dst; size_t lim; } dn[] =
{ { soa->mname, sizeof soa->mname },
{ soa->rname, sizeof soa->rname } };
unsigned *ts[] =
{ &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum };
unsigned short rp;
unsigned i, j, n;
int error;
/* MNAME / RNAME */
if ((rp = rr->rd.p) >= P->end)
return DNS_EILLEGAL;
for (i = 0; i < lengthof(dn); i++) {
if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error)))
return error;
else if (n >= dn[i].lim)
return DNS_EILLEGAL;
if ((rp = dns_d_skip(rp, P)) >= P->end)
return DNS_EILLEGAL;
}
/* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */
for (i = 0; i < lengthof(ts); i++) {
for (j = 0; j < 4; j++, rp++) {
if (rp >= P->end)
return DNS_EILLEGAL;
*ts[i] <<= 8;
*ts[i] |= (0xff & P->data[rp]);
}
}
return 0;
} /* dns_soa_parse() */
int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) {
void *dn[] = { soa->mname, soa->rname };
unsigned ts[] = { (0xffffffff & soa->serial),
(0x7fffffff & soa->refresh),
(0x7fffffff & soa->retry),
(0x7fffffff & soa->expire),
(0xffffffff & soa->minimum) };
unsigned i, j;
size_t end, len;
int error;
end = P->end;
if ((P->end += 2) >= P->size)
goto toolong;
/* MNAME / RNAME */
for (i = 0; i < lengthof(dn); i++) {
if ((error = dns_d_push(P, dn[i], strlen(dn[i]))))
goto error;
}
/* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */
for (i = 0; i < lengthof(ts); i++) {
if ((P->end += 4) >= P->size)
goto toolong;
for (j = 1; j <= 4; j++) {
P->data[P->end - j] = (0xff & ts[i]);
ts[i] >>= 8;
}
}
len = P->end - end - 2;
P->data[end + 0] = (0xff & (len >> 8));
P->data[end + 1] = (0xff & (len >> 0));
return 0;
toolong:
error = DNS_ENOBUFS;
/* FALL THROUGH */
error:
P->end = end;
return error;
} /* dns_soa_push() */
int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) {
int cmp;
if ((cmp = strcasecmp(a->mname, b->mname)))
return cmp;
if ((cmp = strcasecmp(a->rname, b->rname)))
return cmp;
if (a->serial > b->serial)
return -1;
else if (a->serial < b->serial)
return 1;
if (a->refresh > b->refresh)
return -1;
else if (a->refresh < b->refresh)
return 1;
if (a->retry > b->retry)
return -1;
else if (a->retry < b->retry)
return 1;
if (a->expire > b->expire)
return -1;
else if (a->expire < b->expire)
return 1;
if (a->minimum > b->minimum)
return -1;
else if (a->minimum < b->minimum)
return 1;
return 0;
} /* dns_soa_cmp() */
size_t dns_soa_print(void *dst, size_t lim, struct dns_soa *soa) {
size_t cp = 0;
cp += dns__printstring(dst, lim, cp, soa->mname, strlen(soa->mname));
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__printstring(dst, lim, cp, soa->rname, strlen(soa->rname));
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__print10(dst, lim, cp, soa->serial, 0);
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__print10(dst, lim, cp, soa->refresh, 0);
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__print10(dst, lim, cp, soa->retry, 0);
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__print10(dst, lim, cp, soa->expire, 0);
cp += dns__printchar(dst, lim, cp, ' ');
cp += dns__print10(dst, lim, cp, soa->minimum, 0);
dns__printnul(dst, lim, cp);
return cp;
} /* dns_soa_print() */
int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) {
unsigned short rp;
unsigned i;
size_t n;
int error;
memset(srv, '\0', sizeof *srv);
rp = rr->rd.p;
if (P->size - P->end < 6)
return DNS_EILLEGAL;
for (i = 0; i < 2; i++, rp++) {
srv->priority <<= 8;
srv->priority |= (0xff & P->data[rp]);
}
for (i = 0; i < 2; i++, rp++) {
srv->weight <<= 8;
srv->weight |= (0xff & P->data[rp]);
}
for (i = 0; i < 2; i++, rp++) {
srv->port <<= 8;
srv->port |= (0xff & P->data[rp]);
}
if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error)))
return error;
else if (n >= sizeof srv->target)
return DNS_EILLEGAL;
return 0;
} /* dns_srv_parse() */
int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) {
size_t end, len;
int error;
end = P->end;
if (P->size - P->end < 2)
goto toolong;