commit 45ea00cefb6cb3e120c5fdb33b5c1bd812dfef77 from: Kirill A. Korinsky date: Mon Apr 07 21:32:40 2025 UTC Fix build on Fedora 42 commit - e8c445814acc14577e11dd1160ee4342c9c34264 commit + 45ea00cefb6cb3e120c5fdb33b5c1bd812dfef77 blob - /dev/null blob + a38707df0b2ccdd05830769ebc14a753fe3439bf (mode 644) --- /dev/null +++ Makefile.gnu @@ -0,0 +1,139 @@ +LOCALBASE?= /usr/ + +PROG= filter-auth +MAN= filter-auth.8 +BINDIR= ${LOCALBASE}/libexec/opensmtpd/ +MANDIR= ${LOCALBASE}/share/man/man8 + +SRCS+= main.c ltok.c unpack_dns.c + +ifdef HAVE_ED25519 +CFLAGS+= -DHAVE_ED25519 +endif +ifdef LIBCRYPTOPC +CRYPT_CFLAGS!= pkg-config --cflags ${LIBCRYPTOPC} +CRYPT_LDFLAGS_L!=pkg-config --libs-only-L ${LIBCRYPTOPC} +CRYPT_LDFLAGS_libdir!=pkg-config --variable libdir ${LIBCRYPTOPC} +CRYPT_LDFLAGS= ${CRYPT_LDFLAGS_L} +CRYPT_LDFLAGS+= -Wl,-rpath,${CRYPT_LDFLAGS_libdir} +CRYPT_LDADD!= pkg-config --libs-only-l ${LIBCRYPTOPC} +else +CRYPT_CFLAGS= +CRYPT_LDFLAGS= +CRYPT_LDADD= -lcrypto +endif + +CFLAGS+= -I${LOCALBASE}/include +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +CFLAGS+= ${CRYPT_CFLAGS} +CFLAGS+= -I${CURDIR} -I${CURDIR}/openbsd-compat/ + +LDFLAGS+= -L${LOCALBASE}/lib +LDFLAGS+= ${CRYPT_LDFLAGS} +LDADD+= ${CRYPT_LDADD} -lopensmtpd -levent + +INSTALL?= install + +NEED_RECALLOCARRAY?= 1 +NEED_STRTONUM?= 1 +NEED_PLEDGE?= 1 +NEED_INET_NET_PTON?= 1 +NEED_FGETLN?= 1 +NEED_LIBASR?= 1 + +MANFORMAT?= mangz + +BINOWN?= root +BINGRP?= root +BINPERM?= 755 +MANOWN?= root +MANGRP?= root +MANPERM?= 644 + +ifeq (${MANFORMAT}, mangz) +TARGET_MAN= ${MAN}.gz +CLEANFILES+= ${TARGET_MAN} +${TARGET_MAN}: ${MAN} + mandoc -Tman ${MAN} | gzip > $@ +else +TARGET_MAN= ${MAN} +endif + +ifeq (${NEED_RECALLOCARRAY}, 1) +SRCS+= ${CURDIR}/openbsd-compat/recallocarray.c +CFLAGS+= -DNEED_RECALLOCARRAY=1 + +recallocarray.o: ${CURDIR}/openbsd-compat/recallocarray.c + ${CC} ${CFLAGS} -c -o recallocarray.o ${CURDIR}/openbsd-compat/recallocarray.c +endif +ifeq (${NEED_STRTONUM}, 1) +SRCS+= ${CURDIR}/openbsd-compat/strtonum.c +CFLAGS+= -DNEED_STRTONUM=1 + +strtonum.o: ${CURDIR}/openbsd-compat/strtonum.c + ${CC} ${CFLAGS} -c -o strtonum.o ${CURDIR}/openbsd-compat/strtonum.c +endif +ifeq (${NEED_PLEDGE}, 1) +CFLAGS+= -DNEED_PLEDGE=1 +endif +ifeq (${NEED_INET_NET_PTON}, 1) +SRCS+= ${CURDIR}/openbsd-compat/inet_net_pton.c +CFLAGS+= -DNEED_INET_NET_PTON=1 + +inet_net_pton.o: ${CURDIR}/openbsd-compat/inet_net_pton.c + ${CC} ${CFLAGS} -c -o inet_net_pton.o ${CURDIR}/openbsd-compat/inet_net_pton.c +endif +ifeq (${NEED_FGETLN}, 1) +SRCS+= ${CURDIR}/openbsd-compat/fgetln.c +CFLAGS+= -DNEED_FGETLN=1 + +fgetln.o: ${CURDIR}/openbsd-compat/fgetln.c + ${CC} ${CFLAGS} -c -o fgetln.o ${CURDIR}/openbsd-compat/fgetln.c +endif +ifeq (${NEED_LIBASR}, 1) +SRCS+= ${CURDIR}/openbsd-compat/event_asr_run.c +SRCS+= ${CURDIR}/openbsd-compat/libasr/asr.c +SRCS+= ${CURDIR}/openbsd-compat/libasr/asr_utils.c +SRCS+= ${CURDIR}/openbsd-compat/libasr/res_search_async.c +SRCS+= ${CURDIR}/openbsd-compat/libasr/res_send_async.c +CFLAGS+= -I${CURDIR}/openbsd-compat/libasr -DNEED_LIBASR=1 + +event_asr_run.o: ${CURDIR}/openbsd-compat/event_asr_run.c + ${CC} ${CFLAGS} -c -o event_asr_run.o ${CURDIR}/openbsd-compat/event_asr_run.c +asr.o: ${CURDIR}/openbsd-compat/libasr/asr.c + ${CC} ${CFLAGS} -c -o asr.o ${CURDIR}/openbsd-compat/libasr/asr.c +asr_utils.o: ${CURDIR}/openbsd-compat/libasr/asr_utils.c + ${CC} ${CFLAGS} -c -o asr_utils.o ${CURDIR}/openbsd-compat/libasr/asr_utils.c +res_search_async.o: ${CURDIR}/openbsd-compat/libasr/res_search_async.c + ${CC} ${CFLAGS} -c -o res_search_async.o ${CURDIR}/openbsd-compat/libasr/res_search_async.c +res_send_async.o: ${CURDIR}/openbsd-compat/libasr/res_send_async.c + ${CC} ${CFLAGS} -c -o res_send_async.o ${CURDIR}/openbsd-compat/libasr/res_send_async.c +endif + +${SRCS:.c=.d}:%.d:%.c + ${CC} ${CFLAGS} -MM $< >$@ +CLEANFILES+= ${SRCS:.c=.d} + +OBJS= ${notdir ${SRCS:.c=.o}} +CLEANFILES+= ${OBJS} + +${PROG}: ${OBJS} + ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD} + +.DEFAULT_GOAL= all +.PHONY: all +all: ${PROG} ${TARGET_MAN} +CLEANFILES+= ${PROG} + +.PHONY: clean +clean: + rm -f ${CLEANFILES} + +.PHONY: install +install: ${PROG} + ${INSTALL} -D -o ${BINOWN} -g ${BINGRP} -m ${BINPERM} ${PROG} ${DESTDIR}${BINDIR}/${PROG} + ${INSTALL} -D -o ${MANOWN} -g ${MANGRP} -m ${MANPERM} ${TARGET_MAN} ${DESTDIR}${MANDIR}/${TARGET_MAN} blob - 0957e827087dd85686be6c5bacd7b005491fbd28 blob + fb6db0bbfc8bdf343569d8bd77b33424befeff20 --- main.c +++ main.c @@ -41,6 +41,7 @@ #include #include +#include "openbsd-compat.h" #include "unpack_dns.h" #include "ltok.h" @@ -158,8 +159,13 @@ struct header { }; #define AUTHENTICATION_RESULTS_LINELEN 78 +#ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef s6_addr32 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif /* RFC 8617 Section 4.2.1 */ #define ARC_MIN_I 1 @@ -2716,8 +2722,8 @@ spf_check_cidr6(struct spf_record *spf, struct in6_add addr = &(((struct sockaddr_in6 *)(&ses->src))->sin6_addr); - a = addr->__u6_addr.__u6_addr32; - n = net->__u6_addr.__u6_addr32; + a = addr->s6_addr32; + n = net->s6_addr32; whole = bits >> 5; incomplete = bits & 0x1f; blob - /dev/null blob + dc5f765f0375b556181636e7a784f2f7232d2eff (mode 644) --- /dev/null +++ openbsd-compat/event_asr_run.c @@ -0,0 +1,88 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "openbsd-compat.h" + +#include +#include +#include + +#include +#include +#include + +struct event_asr { + struct event ev; + struct asr_query *async; + void (*cb)(struct asr_result *, void *); + void *arg; +}; + +struct event_asr * event_asr_run(struct asr_query *, + void (*)(struct asr_result *, void *), void *); +void event_asr_abort(struct event_asr *); + +static void +event_asr_dispatch(int fd __attribute__((__unused__)), + short ev __attribute__((__unused__)), void *arg) +{ + struct event_asr *eva = arg; + struct asr_result ar; + struct timeval tv; + + event_del(&eva->ev); + + if (asr_run(eva->async, &ar)) { + eva->cb(&ar, eva->arg); + free(eva); + } else { + event_set(&eva->ev, ar.ar_fd, + ar.ar_cond == ASR_WANT_READ ? EV_READ : EV_WRITE, + event_asr_dispatch, eva); + tv.tv_sec = ar.ar_timeout / 1000; + tv.tv_usec = (ar.ar_timeout % 1000) * 1000; + event_add(&eva->ev, &tv); + } +} + +struct event_asr * +event_asr_run(struct asr_query *async, void (*cb)(struct asr_result *, void *), + void *arg) +{ + struct event_asr *eva; + struct timeval tv; + + eva = calloc(1, sizeof *eva); + if (eva == NULL) + return (NULL); + eva->async = async; + eva->cb = cb; + eva->arg = arg; + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_set(&eva->ev, event_asr_dispatch, eva); + evtimer_add(&eva->ev, &tv); + return (eva); +} + +void +event_asr_abort(struct event_asr *eva) +{ + asr_abort(eva->async); + event_del(&eva->ev); + free(eva); +} blob - /dev/null blob + 35233f5f2393673d5624e76fe0451556294d7a12 (mode 644) --- /dev/null +++ openbsd-compat/explicit_bzero.c @@ -0,0 +1,15 @@ +/* $OpenBSD: explicit_bzero.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */ +/* + * Public domain. + * Written by Matthew Dempsky. + */ + +#include "openbsd-compat.h" + +#include + +void +explicit_bzero(void *buf, size_t len) +{ + memset(buf, 0, len); +} blob - /dev/null blob + df2c60c8d45bda0389e8320f09eef1a114cd1052 (mode 644) --- /dev/null +++ openbsd-compat/fgetln.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015 Joerg Jung + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * portable fgetln() version, NOT reentrant + */ + +#include "openbsd-compat.h" + +#include +#include +#include + +void *reallocarray(void *, size_t, size_t); + +char * +fgetln(FILE *fp, size_t *len) +{ + static char *buf = NULL; + static size_t bufsz = 0; + size_t r = 0; + char *p; + int c, e; + + if (buf == NULL) { + if ((buf = calloc(1, BUFSIZ)) == NULL) + return NULL; + bufsz = BUFSIZ; + } + + while ((c = getc(fp)) != EOF) { + buf[r++] = c; + if (r == bufsz) { + if (!(p = reallocarray(buf, 2, bufsz))) { + e = errno; + free(buf); + errno = e; + buf = NULL, bufsz = 0; + return NULL; + } + buf = p, bufsz = 2 * bufsz; + } + if (c == '\n') + break; + } + return (*len = r) ? buf : NULL; +} + blob - /dev/null blob + 46babd8088793077d4680ff85c551f698a1eda61 (mode 644) --- /dev/null +++ openbsd-compat/inet_net_pton.c @@ -0,0 +1,252 @@ +/* $OpenBSD: inet_net_pton.c,v 1.14 2022/12/27 17:10:06 jmc Exp $ */ + +/* + * Copyright (c) 2012 by Gilles Chehade + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "openbsd-compat.h" + +#if !defined(HAVE_INET_NET_PTON) || defined(HAVE_LIBBSD) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int inet_net_pton_ipv4(const char *, u_char *, size_t); +static int inet_net_pton_ipv6(const char *, u_char *, size_t); + +/* + * static int + * inet_net_pton(af, src, dst, size) + * convert network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not a valid network specification. + * author: + * Paul Vixie (ISC), June 1996 + */ +int +inet_net_pton(int af, const char *src, void *dst, size_t size) +{ + switch (af) { + case AF_INET: + return (inet_net_pton_ipv4(src, dst, size)); + case AF_INET6: + return (inet_net_pton_ipv6(src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (-1); + } +} + +/* + * static int + * inet_net_pton_ipv4(src, dst, size) + * convert IPv4 network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not an IPv4 network specification. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), June 1996 + */ +static int +inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) +{ + static const char + xdigits[] = "0123456789abcdef", + digits[] = "0123456789"; + int n, ch, tmp, dirty, bits; + const u_char *odst = dst; + + ch = (unsigned char)*src++; + if (ch == '0' && (src[0] == 'x' || src[0] == 'X') + && isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) { + /* Hexadecimal: Eat nybble string. */ + if (size == 0) + goto emsgsize; + tmp = 0, dirty = 0; + src++; /* skip x or X. */ + while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isxdigit(ch)) { + if (isupper(ch)) + ch = tolower(ch); + n = strchr(xdigits, ch) - xdigits; + assert(n >= 0 && n <= 15); + if (dirty == 0) + tmp = n; + else + tmp = (tmp << 4) | n; + if (++dirty == 2) { + if (size-- == 0) + goto emsgsize; + *dst++ = (u_char) tmp; + dirty = 0; + } + } + if (dirty) { /* Odd trailing nybble? */ + if (size-- == 0) + goto emsgsize; + *dst++ = (u_char) (tmp << 4); + } + } else if (isascii(ch) && isdigit(ch)) { + /* Decimal: eat dotted digit string. */ + for (;;) { + tmp = 0; + do { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isdigit(ch)); + if (size-- == 0) + goto emsgsize; + *dst++ = (u_char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + ch = (unsigned char)*src++; + if (!isascii(ch) || !isdigit(ch)) + goto enoent; + } + } else + goto enoent; + + bits = -1; + if (ch == '/' && isascii((unsigned char)src[0]) && + isdigit((unsigned char)src[0]) && dst > odst) { + /* CIDR width specifier. Nothing can follow it. */ + ch = (unsigned char)*src++; /* Skip over the /. */ + bits = 0; + do { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + bits *= 10; + bits += n; + if (bits > 32) + goto emsgsize; + } while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isdigit(ch)); + if (ch != '\0') + goto enoent; + } + + /* Fiery death and destruction unless we prefetched EOS. */ + if (ch != '\0') + goto enoent; + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + /* If no CIDR spec was given, infer width from net class. */ + if (bits == -1) { + if (*odst >= 240) /* Class E */ + bits = 32; + else if (*odst >= 224) /* Class D */ + bits = 4; + else if (*odst >= 192) /* Class C */ + bits = 24; + else if (*odst >= 128) /* Class B */ + bits = 16; + else /* Class A */ + bits = 8; + /* If imputed mask is narrower than specified octets, widen. */ + if (bits < ((dst - odst) * 8)) + bits = (dst - odst) * 8; + } + /* Extend network to cover the actual mask. */ + while (bits > ((dst - odst) * 8)) { + if (size-- == 0) + goto emsgsize; + *dst++ = '\0'; + } + return (bits); + + enoent: + errno = ENOENT; + return (-1); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + + +static int +inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) +{ + struct in6_addr in6; + int ret; + int bits; + size_t bytes; + char buf[INET6_ADDRSTRLEN + sizeof("/128")]; + char *sep; + const char *errstr; + + if (strlcpy(buf, src, sizeof buf) >= sizeof buf) { + errno = EMSGSIZE; + return (-1); + } + + sep = strchr(buf, '/'); + if (sep != NULL) + *sep++ = '\0'; + + ret = inet_pton(AF_INET6, buf, &in6); + if (ret != 1) + return (-1); + + if (sep == NULL) + bits = 128; + else { + bits = strtonum(sep, 0, 128, &errstr); + if (errstr) { + errno = EINVAL; + return (-1); + } + } + + bytes = (bits + 7) / 8; + if (bytes > size) { + errno = EMSGSIZE; + return (-1); + } + memcpy(dst, &in6.s6_addr, bytes); + return (bits); +} + +#endif blob - /dev/null blob + dd787a8b45a8923f1947546941026c359d7a91d7 (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr.c @@ -0,0 +1,957 @@ +/* $OpenBSD: asr.c,v 1.68 2022/01/20 14:18:10 naddy Exp $ */ +/* + * Copyright (c) 2010-2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "openbsd-compat.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asr_private.h" + +#include "thread_private.h" + +#define DEFAULT_CONF "lookup file\n" +#define DEFAULT_LOOKUP "lookup bind file" + +#define RELOAD_DELAY 15 /* seconds */ + +static void asr_check_reload(struct asr *); +static struct asr_ctx *asr_ctx_create(void); +static void asr_ctx_ref(struct asr_ctx *); +static void asr_ctx_free(struct asr_ctx *); +static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *); +static int asr_ctx_from_file(struct asr_ctx *, const char *); +static int asr_ctx_from_string(struct asr_ctx *, const char *); +static int asr_ctx_parse(struct asr_ctx *, const char *); +static int asr_parse_nameserver(struct sockaddr *, const char *); +static int asr_ndots(const char *); +static void pass0(char **, int, struct asr_ctx *); +static int strsplit(char *, char **, int); +static void asr_ctx_envopts(struct asr_ctx *); +static void *__THREAD_NAME(_asr); + +static struct asr *_asr = NULL; + +#ifndef HAVE_ISSETUGID +#define issetugid() ((getuid() != geteuid())) +#endif + +/* Allocate and configure an async "resolver". */ +static void * +_asr_resolver(void) +{ + static int init = 0; + struct asr *asr; + + if (init == 0) { +#ifdef DEBUG + if (getenv("ASR_DEBUG")) + _asr_debug = stderr; +#endif + init = 1; + } + + if ((asr = calloc(1, sizeof(*asr))) == NULL) + goto fail; + + asr_check_reload(asr); + if (asr->a_ctx == NULL) { + if ((asr->a_ctx = asr_ctx_create()) == NULL) + goto fail; + if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1) + goto fail; + asr_ctx_envopts(asr->a_ctx); + } + +#ifdef DEBUG + _asr_dump_config(_asr_debug, asr); +#endif + return (asr); + + fail: + if (asr) { + if (asr->a_ctx) + asr_ctx_free(asr->a_ctx); + free(asr); + } + + return (NULL); +} + +/* + * Free the "asr" async resolver (or the thread-local resolver if NULL). + * Drop the reference to the current context. + */ +void +_asr_resolver_done(void *arg) +{ + struct asr_ctx *ac = arg; + struct asr *asr; + struct asr **priv; + + if (ac) { + _asr_ctx_unref(ac); + return; + } else { + priv = _THREAD_PRIVATE_DT(_asr, _asr, NULL, &_asr); + if (*priv == NULL) + return; + asr = *priv; + *priv = NULL; + } + + _asr_ctx_unref(asr->a_ctx); + free(asr); +} + +static void +_asr_resolver_done_tp(void *arg) +{ + struct asr **priv = arg; + struct asr *asr; + + if (*priv == NULL) + return; + asr = *priv; + + _asr_ctx_unref(asr->a_ctx); + free(asr); + free(priv); +} + +void * +asr_resolver_from_string(const char *str) +{ + struct asr_ctx *ac; + + if ((ac = asr_ctx_create()) == NULL) + return NULL; + + if (asr_ctx_from_string(ac, str) == -1) { + asr_ctx_free(ac); + return NULL; + } + + return ac; +} +DEF_WEAK(asr_resolver_from_string); + +void +asr_resolver_free(void *arg) +{ + _asr_ctx_unref(arg); +} +DEF_WEAK(asr_resolver_free); + +/* + * Cancel an async query. + */ +void +asr_abort(struct asr_query *as) +{ + _asr_async_free(as); +} + +/* + * Resume the "as" async query resolution. Return one of ASYNC_COND, + * or ASYNC_DONE and put query-specific return values in the user-allocated + * memory at "ar". + */ +int +asr_run(struct asr_query *as, struct asr_result *ar) +{ + int r, saved_errno = errno; + + memset(ar, 0, sizeof(*ar)); + + DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar, + _asr_querystr(as->as_type), as->as_ctx); + r = as->as_run(as, ar); + + DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r)); +#ifdef DEBUG + if (r == ASYNC_COND) +#endif + DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout); + DPRINT("\n"); + if (r == ASYNC_DONE) + _asr_async_free(as); + + errno = saved_errno; + + return (r); +} +DEF_WEAK(asr_run); + +static int +poll_intrsafe(struct pollfd *fds, nfds_t nfds, int timeout) +{ + struct timespec pollstart, pollend, elapsed; + int r; + + if (clock_gettime(CLOCK_MONOTONIC, &pollstart)) + return -1; + + while ((r = poll(fds, 1, timeout)) == -1 && errno == EINTR) { + if (clock_gettime(CLOCK_MONOTONIC, &pollend)) + return -1; + timespecsub(&pollend, &pollstart, &elapsed); + timeout -= elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000; + if (timeout < 1) + return 0; + } + + return r; +} + +/* + * Same as asr_run, but run in a loop that handles the fd conditions result. + */ +int +asr_run_sync(struct asr_query *as, struct asr_result *ar) +{ + struct pollfd fds[1]; + int r, saved_errno = errno; + + while ((r = asr_run(as, ar)) == ASYNC_COND) { + fds[0].fd = ar->ar_fd; + fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT; + + if (poll_intrsafe(fds, 1, ar->ar_timeout) == -1) { + memset(ar, 0, sizeof(*ar)); + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_gai_errno = EAI_SYSTEM; + ar->ar_rrset_errno = NETDB_INTERNAL; + _asr_async_free(as); + errno = saved_errno; + return ASYNC_DONE; + } + + /* + * Otherwise, just ignore the error and let asr_run() + * catch the failure. + */ + } + + errno = saved_errno; + + return (r); +} +DEF_WEAK(asr_run_sync); + +/* + * Create a new async request of the given "type" on the async context "ac". + * Take a reference on it so it does not get deleted while the async query + * is running. + */ +struct asr_query * +_asr_async_new(struct asr_ctx *ac, int type) +{ + struct asr_query *as; + + DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type, + ac ? ac->ac_refcount : 0); + if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL) + return (NULL); + + ac->ac_refcount += 1; + as->as_ctx = ac; + as->as_fd = -1; + as->as_type = type; + as->as_state = ASR_STATE_INIT; + + return (as); +} + +/* + * Free an async query and unref the associated context. + */ +void +_asr_async_free(struct asr_query *as) +{ + DPRINT("asr: asr_async_free(%p)\n", as); + + if (as->as_subq) + _asr_async_free(as->as_subq); + + switch (as->as_type) { + case ASR_SEND: + if (as->as_fd != -1) + close(as->as_fd); + if (as->as.dns.obuf && !(as->as_flags & ASYNC_EXTOBUF)) + free(as->as.dns.obuf); + if (as->as.dns.ibuf) + free(as->as.dns.ibuf); + if (as->as.dns.dname) + free(as->as.dns.dname); + break; + + case ASR_SEARCH: + if (as->as.search.name) + free(as->as.search.name); + break; + + case ASR_GETRRSETBYNAME: + if (as->as.rrset.name) + free(as->as.rrset.name); + break; + + case ASR_GETHOSTBYNAME: + case ASR_GETHOSTBYADDR: + if (as->as.hostnamadr.name) + free(as->as.hostnamadr.name); + break; + + case ASR_GETADDRINFO: + if (as->as.ai.aifirst) + freeaddrinfo(as->as.ai.aifirst); + if (as->as.ai.hostname) + free(as->as.ai.hostname); + if (as->as.ai.servname) + free(as->as.ai.servname); + if (as->as.ai.fqdn) + free(as->as.ai.fqdn); + break; + + case ASR_GETNAMEINFO: + break; + } + + _asr_ctx_unref(as->as_ctx); + free(as); +} + +/* + * Get a context from the given resolver. This takes a new reference to + * the returned context, which *must* be explicitly dropped when done + * using this context. + */ +struct asr_ctx * +_asr_use_resolver(void *arg) +{ + struct asr_ctx *ac = arg; + struct asr *asr; + struct asr **priv; + + if (ac) { + asr_ctx_ref(ac); + return ac; + } + else { + DPRINT("using thread-local resolver\n"); + priv = _THREAD_PRIVATE_DT(_asr, _asr, _asr_resolver_done_tp, + &_asr); + if (*priv == NULL) { + DPRINT("setting up thread-local resolver\n"); + *priv = _asr_resolver(); + } + asr = *priv; + } + if (asr != NULL) { + asr_check_reload(asr); + asr_ctx_ref(asr->a_ctx); + return (asr->a_ctx); + } + return (NULL); +} + +static void +asr_ctx_ref(struct asr_ctx *ac) +{ + DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount); + ac->ac_refcount += 1; +} + +/* + * Drop a reference to an async context, freeing it if the reference + * count drops to 0. + */ +void +_asr_ctx_unref(struct asr_ctx *ac) +{ + DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac, + ac ? ac->ac_refcount : 0); + if (ac == NULL) + return; + if (--ac->ac_refcount) + return; + + asr_ctx_free(ac); +} + +static void +asr_ctx_free(struct asr_ctx *ac) +{ + int i; + + if (ac->ac_domain) + free(ac->ac_domain); + for (i = 0; i < ASR_MAXNS; i++) + free(ac->ac_ns[i]); + for (i = 0; i < ASR_MAXDOM; i++) + free(ac->ac_dom[i]); + + free(ac); +} + +/* + * Reload the configuration file if it has changed on disk. + */ +static void +asr_check_reload(struct asr *asr) +{ + struct asr_ctx *ac; + struct stat st; + struct timespec ts; + pid_t pid; + + pid = getpid(); + if (pid != asr->a_pid) { + asr->a_pid = pid; + asr->a_rtime = 0; + } + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) + return; + + if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0) + return; + asr->a_rtime = ts.tv_sec; + + DPRINT("asr: checking for update of \"%s\"\n", _PATH_RESCONF); + if (stat(_PATH_RESCONF, &st) == -1 || + asr->a_mtime == st.st_mtime || + (ac = asr_ctx_create()) == NULL) + return; + asr->a_mtime = st.st_mtime; + + DPRINT("asr: reloading config file\n"); + if (asr_ctx_from_file(ac, _PATH_RESCONF) == -1) { + asr_ctx_free(ac); + return; + } + + asr_ctx_envopts(ac); + if (asr->a_ctx) + _asr_ctx_unref(asr->a_ctx); + asr->a_ctx = ac; +} + +/* + * Construct a fully-qualified domain name for the given name and domain. + * If "name" ends with a '.' it is considered as a FQDN by itself. + * Otherwise, the domain, which must be a FQDN, is appended to "name" (it + * may have a leading dot which would be ignored). If the domain is null, + * then "." is used. Return the length of the constructed FQDN or (0) on + * error. + */ +size_t +_asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen) +{ + size_t len; + + if (domain == NULL) + domain = "."; + else if ((len = strlen(domain)) == 0) + return (0); + else if (domain[len -1] != '.') + return (0); + + len = strlen(name); + if (len == 0) { + if (strlcpy(buf, domain, buflen) >= buflen) + return (0); + } else if (name[len - 1] != '.') { + if (domain[0] == '.') + domain += 1; + if (strlcpy(buf, name, buflen) >= buflen || + strlcat(buf, ".", buflen) >= buflen || + strlcat(buf, domain, buflen) >= buflen) + return (0); + } else { + if (strlcpy(buf, name, buflen) >= buflen) + return (0); + } + + return (strlen(buf)); +} + +/* + * Count the dots in a string. + */ +static int +asr_ndots(const char *s) +{ + int n; + + for (n = 0; *s; s++) + if (*s == '.') + n += 1; + + return (n); +} + +/* + * Allocate a new empty context. + */ +static struct asr_ctx * +asr_ctx_create(void) +{ + struct asr_ctx *ac; + + if ((ac = calloc(1, sizeof(*ac))) == NULL) + return (NULL); + + ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; + ac->ac_refcount = 1; + ac->ac_ndots = 1; + + /* + * opensmtpd-portable knob to prefer IPv6 over IPv4. + * + * See: https://github.com/poolpOrg/OpenSMTPD/issues/182 + */ +#ifndef ASR_IPV4_BEFORE_IPV6 + ac->ac_family[0] = AF_INET6; + ac->ac_family[1] = AF_INET; +#else + ac->ac_family[0] = AF_INET; + ac->ac_family[1] = AF_INET6; +#endif + ac->ac_family[2] = -1; + + ac->ac_nscount = 0; + ac->ac_nstimeout = 5; + ac->ac_nsretries = 4; + + return (ac); +} + +struct asr_ctx * +_asr_no_resolver(void) +{ + return asr_ctx_create(); +} + +/* + * Add a search domain to the async context. + */ +static int +asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain) +{ + char buf[MAXDNAME]; + + if (ac->ac_domcount == ASR_MAXDOM) + return (-1); + + if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0) + return (-1); + + if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL) + return (0); + + ac->ac_domcount += 1; + + return (1); +} + +static int +strsplit(char *line, char **tokens, int ntokens) +{ + int ntok; + char *cp, **tp; + + for (cp = line, tp = tokens, ntok = 0; + ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) + if (**tp != '\0') { + tp++; + ntok++; + } + + return (ntok); +} + +/* + * Pass on a split config line. + */ +static void +pass0(char **tok, int n, struct asr_ctx *ac) +{ + int i, j, d; + const char *e; + struct sockaddr_storage ss; + + if (!strcmp(tok[0], "nameserver")) { + if (ac->ac_nscount == ASR_MAXNS) + return; + if (n != 2) + return; + if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1])) + return; + if ((ac->ac_ns[ac->ac_nscount] = calloc(1, SS_LEN(&ss))) == NULL) + return; + memmove(ac->ac_ns[ac->ac_nscount], &ss, SS_LEN(&ss)); + ac->ac_nscount += 1; + + } else if (!strcmp(tok[0], "domain")) { + if (n != 2) + return; + if (ac->ac_domain) + return; + ac->ac_domain = strdup(tok[1]); + + } else if (!strcmp(tok[0], "lookup")) { + /* ensure that each lookup is only given once */ + for (i = 1; i < n; i++) + for (j = i + 1; j < n; j++) + if (!strcmp(tok[i], tok[j])) + return; + ac->ac_dbcount = 0; + for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) { + if (!strcmp(tok[i], "yp")) { + /* silently deprecated */ + } else if (!strcmp(tok[i], "bind")) + ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS; + else if (!strcmp(tok[i], "file")) + ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE; + } + } else if (!strcmp(tok[0], "search")) { + /* resolv.conf says the last line wins */ + for (i = 0; i < ASR_MAXDOM; i++) { + free(ac->ac_dom[i]); + ac->ac_dom[i] = NULL; + } + ac->ac_domcount = 0; + for (i = 1; i < n; i++) + asr_ctx_add_searchdomain(ac, tok[i]); + + } else if (!strcmp(tok[0], "family")) { + if (n == 1 || n > 3) + return; + for (i = 1; i < n; i++) + if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6")) + return; + for (i = 1; i < n; i++) + ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \ + AF_INET6 : AF_INET; + ac->ac_family[i - 1] = -1; + + } else if (!strcmp(tok[0], "options")) { + for (i = 1; i < n; i++) { + if (!strcmp(tok[i], "tcp")) + ac->ac_options |= RES_USEVC; + else if (!strcmp(tok[i], "edns0")) + ac->ac_options |= RES_USE_EDNS0; + else if ((!strncmp(tok[i], "ndots:", 6))) { + e = NULL; + d = strtonum(tok[i] + 6, 1, 16, &e); + if (e == NULL) + ac->ac_ndots = d; + } else if (!strcmp(tok[i], "trust-ad")) +#ifdef RES_TRUSTAD + ac->ac_options |= RES_TRUSTAD; +#else + /* nop */ ; +#endif + } + } +} + +/* + * Setup an async context with the config specified in the string "str". + */ +static int +asr_ctx_from_string(struct asr_ctx *ac, const char *str) +{ + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; + int i, trustad; + char buf[512], *ch; + + asr_ctx_parse(ac, str); + + if (ac->ac_dbcount == 0) { + /* No lookup directive */ + asr_ctx_parse(ac, DEFAULT_LOOKUP); + } + + if (ac->ac_nscount == 0) + asr_ctx_parse(ac, "nameserver 127.0.0.1"); + + if (ac->ac_domain == NULL) + if (gethostname(buf, sizeof buf) == 0) { + ch = strchr(buf, '.'); + if (ch) + ac->ac_domain = strdup(ch + 1); + else /* Assume root. see resolv.conf(5) */ + ac->ac_domain = strdup(""); + } + + /* If no search domain was specified, use the local subdomains */ + if (ac->ac_domcount == 0) + for (ch = ac->ac_domain; ch; ) { + asr_ctx_add_searchdomain(ac, ch); + ch = strchr(ch, '.'); + if (ch && asr_ndots(++ch) == 0) + break; + } + + trustad = 1; + for (i = 0; i < ac->ac_nscount && trustad; i++) { + switch (ac->ac_ns[i]->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)ac->ac_ns[i]; + if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) + trustad = 0; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)ac->ac_ns[i]; + if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) + trustad = 0; + break; + default: + trustad = 0; + break; + } + } + if (trustad) +#ifdef RES_TRUSTAD + ac->ac_options |= RES_TRUSTAD; +#else + /* nop */ ; +#endif + + return (0); +} + +/* + * Setup the "ac" async context from the file at location "path". + */ +static int +asr_ctx_from_file(struct asr_ctx *ac, const char *path) +{ + FILE *cf; + char buf[4096]; + ssize_t r; + + cf = fopen(path, "re"); + if (cf == NULL) + return (-1); + + r = fread(buf, 1, sizeof buf - 1, cf); + if (feof(cf) == 0) { + DPRINT("asr: config file too long: \"%s\"\n", path); + r = -1; + } + fclose(cf); + if (r == -1) + return (-1); + buf[r] = '\0'; + + return asr_ctx_from_string(ac, buf); +} + +/* + * Parse lines in the configuration string. For each one, split it into + * tokens and pass them to "pass0" for processing. + */ +static int +asr_ctx_parse(struct asr_ctx *ac, const char *str) +{ + size_t len; + const char *line; + char buf[1024]; + char *tok[10]; + int ntok; + + line = str; + while (*line) { + len = strcspn(line, "\n\0"); + if (len < sizeof buf) { + memmove(buf, line, len); + buf[len] = '\0'; + } else + buf[0] = '\0'; + line += len; + if (*line == '\n') + line++; + buf[strcspn(buf, ";#")] = '\0'; + if ((ntok = strsplit(buf, tok, 10)) == 0) + continue; + + pass0(tok, ntok, ac); + } + + return (0); +} + +/* + * Check for environment variables altering the configuration as described + * in resolv.conf(5). Although not documented there, this feature is disabled + * for setuid/setgid programs. + */ +static void +asr_ctx_envopts(struct asr_ctx *ac) +{ + char buf[4096], *e; + size_t s; + + if (issetugid()) { + ac->ac_options |= RES_NOALIASES; + return; + } + + if ((e = getenv("RES_OPTIONS")) != NULL) { + strlcpy(buf, "options ", sizeof buf); + strlcat(buf, e, sizeof buf); + s = strlcat(buf, "\n", sizeof buf); + if (s < sizeof buf) + asr_ctx_parse(ac, buf); + } + + if ((e = getenv("LOCALDOMAIN")) != NULL) { + strlcpy(buf, "search ", sizeof buf); + strlcat(buf, e, sizeof buf); + s = strlcat(buf, "\n", sizeof buf); + if (s < sizeof buf) + asr_ctx_parse(ac, buf); + } +} + +/* + * Parse a resolv.conf(5) nameserver string into a sockaddr. + */ +static int +asr_parse_nameserver(struct sockaddr *sa, const char *s) +{ + in_port_t portno = 53; + + if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1) + return (-1); + + if (sa->sa_family == PF_INET) + ((struct sockaddr_in *)sa)->sin_port = htons(portno); + else if (sa->sa_family == PF_INET6) + ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); + + return (0); +} + +/* + * Turn a (uncompressed) DNS domain name into a regular nul-terminated string + * where labels are separated by dots. The result is put into the "buf" buffer, + * truncated if it exceeds "max" chars. The function returns "buf". + */ +char * +_asr_strdname(const char *_dname, char *buf, size_t max) +{ + const unsigned char *dname = _dname; + char *res; + size_t left, count; + + if (_dname[0] == 0) { + strlcpy(buf, ".", max); + return buf; + } + + res = buf; + left = max - 1; + while (dname[0] && left) { + count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); + memmove(buf, dname + 1, count); + dname += dname[0] + 1; + left -= count; + buf += count; + if (left) { + left -= 1; + *buf++ = '.'; + } + } + buf[0] = 0; + + return (res); +} + +/* + * Read and split the next line from the given namedb file. + * Return -1 on error, or put the result in the "tokens" array of + * size "ntoken" and returns the number of token on the line. + */ +int +_asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz) +{ + size_t len; + char *buf; + int ntok; + + again: + if ((buf = fgetln(file, &len)) == NULL) + return (-1); + + if (len >= sz) + goto again; + + if (buf[len - 1] == '\n') + len--; + else { + memcpy(lbuf, buf, len); + buf = lbuf; + } + + buf[len] = '\0'; + buf[strcspn(buf, "#")] = '\0'; + if ((ntok = strsplit(buf, tokens, ntoken)) == 0) + goto again; + + return (ntok); +} + +/* + * Update the async context so that it uses the next configured DB. + * Return 0 on success, or -1 if no more DBs is available. + */ +int +_asr_iter_db(struct asr_query *as) +{ + if (as->as_db_idx >= as->as_ctx->ac_dbcount) { + DPRINT("asr_iter_db: done\n"); + return (-1); + } + + as->as_db_idx += 1; + DPRINT("asr_iter_db: %i\n", as->as_db_idx); + + return (0); +} blob - /dev/null blob + e9725e6be7ad1cfb8e4fbdd95c1b468552853553 (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr.h @@ -0,0 +1,95 @@ +/* $OpenBSD: asr.h,v 1.1 2014/03/26 18:13:15 eric Exp $ */ +/* + * Copyright (c) 2012-2014 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Expected fd conditions + */ +#define ASR_WANT_READ 1 +#define ASR_WANT_WRITE 2 + +/* + * Structure through which asynchronous query results are returned when + * calling asr_run(). + */ +struct asr_result { + /* Fields set if the query is not done yet (asr_run returns 0) */ + int ar_cond; /* ASR_WANT_READ or ASR_WANT_WRITE */ + int ar_fd; /* the fd waiting for io condition */ + int ar_timeout; /* time to wait for in milliseconds */ + + /* Error fields. Depends on the query type. */ + int ar_errno; + int ar_h_errno; + int ar_gai_errno; + int ar_rrset_errno; + + /* Result for res_*_async() calls */ + int ar_count; /* number of answers in the dns reply */ + int ar_rcode; /* response code in the dns reply */ + void *ar_data; /* raw reply packet (must be freed) */ + int ar_datalen; /* reply packet length */ + struct sockaddr_storage ar_ns; /* nameserver that responded */ + + /* Result for other calls. Must be freed properly. */ + struct addrinfo *ar_addrinfo; + struct rrsetinfo *ar_rrsetinfo; + struct hostent *ar_hostent; + struct netent *ar_netent; +}; + +/* + * Asynchronous query management. + */ + +/* Forward declaration. The API uses opaque pointers as query handles. */ +struct asr_query; + +int asr_run(struct asr_query *, struct asr_result *); +int asr_run_sync(struct asr_query *, struct asr_result *); +void asr_abort(struct asr_query *); + +/* + * Asynchronous version of the resolver functions. Similar prototypes, with + * an extra context parameter at the end which must currently be set to NULL. + * All functions return a handle suitable for use with the management functions + * above. + */ +struct asr_query *res_send_async(const unsigned char *, int, void *); +struct asr_query *res_query_async(const char *, int, int, void *); +struct asr_query *res_search_async(const char *, int, int, void *); + +struct asr_query *getrrsetbyname_async(const char *, unsigned int, unsigned int, + unsigned int, void *); + +struct asr_query *gethostbyname_async(const char *, void *); +struct asr_query *gethostbyname2_async(const char *, int, void *); +struct asr_query *gethostbyaddr_async(const void *, socklen_t, int, void *); + +struct asr_query *getnetbyname_async(const char *, void *); +struct asr_query *getnetbyaddr_async(in_addr_t, int, void *); + +struct asr_query *getaddrinfo_async(const char *, const char *, + const struct addrinfo *, void *); +struct asr_query *getnameinfo_async(const struct sockaddr *, socklen_t, char *, + size_t, char *, size_t, int, void *); + +/* only there for -portable */ +void asr_freeaddrinfo(struct addrinfo *); + +/* from in event.h */ +struct event_asr * event_asr_run(struct asr_query *, + void (*)(struct asr_result *, void *), void *); blob - /dev/null blob + ee9583573aaf336454f2e53e7a5c3c5d95117483 (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr_compat.c @@ -0,0 +1,102 @@ +/* $OpenBSD: asr_debug.c,v 1.25 2018/04/28 15:16:49 schwarze Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif + +#include "asr_compat.h" + +#ifndef HAVE___P_CLASS +const char * +__p_class(int c) +{ + switch(c) { + case C_IN: return "IN"; + case C_CHAOS: return "CHAOS"; + case C_HS: return "HESIOD"; + case C_ANY: return "ANY"; + default: return "?"; + } +}; +#endif /* !HAVE___P_CLASS */ + +#ifndef HAVE___P_TYPE +const char * +__p_type(int t) +{ + switch(t) { + case T_A: return "A"; + case T_NS: return "NS"; + case T_MD: return "MD"; + case T_MF: return "MF"; + case T_CNAME: return "CNAME"; + case T_SOA: return "SOA"; + case T_MB: return "MB"; + case T_MG: return "MG"; + case T_MR: return "MR"; + case T_NULL: return "NULL"; + case T_WKS: return "WKS"; + case T_PTR: return "PTR"; + case T_HINFO: return "HINFO"; + case T_MINFO: return "MINFO"; + case T_MX: return "MX"; + case T_TXT: return "TXT"; + case T_RP: return "RP"; + case T_AFSDB: return "AFSDB"; + case T_X25: return "X25"; + case T_ISDN: return "ISDN"; + case T_RT: return "RT"; + case T_NSAP: return "NSAP"; + case T_NSAP_PTR:return"NSAP_PTR"; + case T_SIG: return "SIG"; + case T_KEY: return "KEY"; + case T_PX: return "PX"; + case T_GPOS: return "GPOS"; + case T_AAAA: return "AAAA"; + case T_LOC: return "LOC"; + case T_NXT: return "NXT"; + case T_EID: return "EID"; + case T_NIMLOC: return "NIMLOC"; + case T_SRV: return "SRV"; + case T_ATMA: return "ATMA"; + case T_OPT: return "OPT"; + case T_IXFR: return "IXFR"; + case T_AXFR: return "AXFR"; + case T_MAILB: return "MAILB"; + case T_MAILA: return "MAILA"; +#ifdef T_UINFO + case T_UINFO: return "UINFO"; +#endif +#ifdef T_UID + case T_UID: return "UID"; +#endif +#ifdef T_GID + case T_GID: return "GID"; +#endif + case T_NAPTR: return "NAPTR"; +#ifdef T_UNSPEC + case T_UNSPEC: return "UNSPEC"; +#endif + case T_ANY: return "ANY"; + default: return "?"; + } +} +#endif /* !HAVE___P_TYPE */ blob - /dev/null blob + af17b976512b52cf7e8c901e38447a6d42261204 (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr_compat.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +/* source compat */ +#define ASR_BUFSIZ 1024 + +#define DEF_WEAK(x) +#define __THREAD_NAME(x) __thread_name_ ## x + +#ifndef __BEGIN_HIDDEN_DECLS +#define __BEGIN_HIDDEN_DECLS +#endif +#ifndef __END_HIDDEN_DECLS +#define __END_HIDDEN_DECLS +#endif + +/* + * sys/socket.h + */ +#ifndef SOCK_DNS +#define SOCK_DNS 0 +#endif + +/* + * netdb.h + */ +#ifndef NETDB_SUCCESS +#define NETDB_SUCCESS 0 +#endif + +#ifndef NETDB_INTERNAL +#define NETDB_INTERNAL -1 +#endif + +#ifndef AI_FQDN +#define AI_FQDN AI_CANONNAME +#endif + +#ifndef AI_MASK +#define AI_MASK \ + (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_FQDN) +#endif + +#ifndef SCOPE_DELIMITER +#define SCOPE_DELIMITER '%' +#endif + +#ifndef _PATH_HOSTS +#define _PATH_HOSTS "/etc/hosts" +#endif + +#ifndef _PATH_NETWORKS +#define _PATH_NETWORKS "/etc/networks" +#endif + +#ifndef EAI_BADHINTS +#define EAI_BADHINTS EAI_FAIL +#endif + +/* + * arpa/nameserv.h + */ +#ifndef T_OPT +#define T_OPT 41 +#endif + +#ifndef DNS_MESSAGEEXTFLAG_DO +#define DNS_MESSAGEEXTFLAG_DO 0x8000U +#endif + +#ifndef HAVE___P_CLASS +const char * __p_class(int); +#endif + +#ifndef HAVE___P_TYPE +const char * __p_type(int); +#endif + +/* + * netinet6/in6.h + */ +#ifndef IN6_IS_ADDR_MC_INTFACELOCAL +#define IN6_IS_ADDR_MC_INTFACELOCAL(x) IN6_IS_ADDR_MC_NODELOCAL(x) +#endif blob - /dev/null blob + 1d842a6fa5255be90e3d74fa65d1697b33acafab (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr_debug.c @@ -0,0 +1,368 @@ +/* $OpenBSD: asr_debug.c,v 1.28 2021/11/22 20:18:27 jca Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif +#include +#include + +#include +#include +#include + +#include "asr_private.h" + +static const char *rcodetostr(uint16_t); +static const char *print_dname(const char *, char *, size_t); +static const char *print_header(const struct asr_dns_header *, char *, size_t); +static const char *print_query(const struct asr_dns_query *, char *, size_t); +static const char *print_rr(const struct asr_dns_rr *, char *, size_t); + +FILE *_asr_debug = NULL; + +#define OPCODE_SHIFT 11 + +static const char * +rcodetostr(uint16_t v) +{ + switch (v) { + case NOERROR: return "NOERROR"; + case FORMERR: return "FORMERR"; + case SERVFAIL: return "SERVFAIL"; + case NXDOMAIN: return "NXDOMAIN"; + case NOTIMP: return "NOTIMP"; + case REFUSED: return "REFUSED"; + default: return "?"; + } +} + +static const char * +print_dname(const char *_dname, char *buf, size_t max) +{ + return (_asr_strdname(_dname, buf, max)); +} + +static const char * +print_rr(const struct asr_dns_rr *rr, char *buf, size_t max) +{ + char *res; + char tmp[256]; + char tmp2[256]; + int r; + + res = buf; + + r = snprintf(buf, max, "%s %u %s %s ", + print_dname(rr->rr_dname, tmp, sizeof tmp), + rr->rr_ttl, + __p_class(rr->rr_class), + __p_type(rr->rr_type)); + if (r < 0 || (size_t)r >= max) { + buf[0] = '\0'; + return (buf); + } + + if ((size_t)r >= max) + return (buf); + + max -= r; + buf += r; + + switch (rr->rr_type) { + case T_CNAME: + print_dname(rr->rr.cname.cname, buf, max); + break; + case T_MX: + snprintf(buf, max, "%lu %s", + (unsigned long)rr->rr.mx.preference, + print_dname(rr->rr.mx.exchange, tmp, sizeof tmp)); + break; + case T_NS: + print_dname(rr->rr.ns.nsname, buf, max); + break; + case T_PTR: + print_dname(rr->rr.ptr.ptrname, buf, max); + break; + case T_SOA: + snprintf(buf, max, "%s %s %lu %lu %lu %lu %lu", + print_dname(rr->rr.soa.mname, tmp, sizeof tmp), + print_dname(rr->rr.soa.rname, tmp2, sizeof tmp2), + (unsigned long)rr->rr.soa.serial, + (unsigned long)rr->rr.soa.refresh, + (unsigned long)rr->rr.soa.retry, + (unsigned long)rr->rr.soa.expire, + (unsigned long)rr->rr.soa.minimum); + break; + case T_A: + if (rr->rr_class != C_IN) + goto other; + snprintf(buf, max, "%s", inet_ntop(AF_INET, + &rr->rr.in_a.addr, tmp, sizeof tmp)); + break; + case T_AAAA: + if (rr->rr_class != C_IN) + goto other; + snprintf(buf, max, "%s", inet_ntop(AF_INET6, + &rr->rr.in_aaaa.addr6, tmp, sizeof tmp)); + break; + default: + other: + snprintf(buf, max, "(rdlen=%i)", (int)rr->rr.other.rdlen); + break; + } + + return (res); +} + +static const char * +print_query(const struct asr_dns_query *q, char *buf, size_t max) +{ + char b[256]; + + snprintf(buf, max, "%s %s %s", + print_dname(q->q_dname, b, sizeof b), + __p_class(q->q_class), __p_type(q->q_type)); + + return (buf); +} + +static const char * +print_header(const struct asr_dns_header *h, char *buf, size_t max) +{ + snprintf(buf, max, + "id:0x%04x %s op:%i %s %s %s %s z:%i %s %s r:%s qd:%i an:%i ns:%i ar:%i", + ((int)h->id), + (h->flags & QR_MASK) ? "QR":" ", + (int)(OPCODE(h->flags) >> OPCODE_SHIFT), + (h->flags & AA_MASK) ? "AA":" ", + (h->flags & TC_MASK) ? "TC":" ", + (h->flags & RD_MASK) ? "RD":" ", + (h->flags & RA_MASK) ? "RA":" ", + (h->flags & Z_MASK), + (h->flags & AD_MASK) ? "AD":" ", + (h->flags & CD_MASK) ? "CD":" ", + rcodetostr(RCODE(h->flags)), + h->qdcount, h->ancount, h->nscount, h->arcount); + + return (buf); +} + +void +_asr_dump_packet(FILE *f, const void *data, size_t len) +{ + char buf[1024]; + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + struct asr_dns_rr rr; + int i, an, ns, ar, n; + + if (f == NULL) + return; + + _asr_unpack_init(&p, data, len); + + if (_asr_unpack_header(&p, &h) == -1) { + fprintf(f, ";; BAD PACKET: %s\n", strerror(p.err)); + return; + } + + fprintf(f, ";; HEADER %s\n", print_header(&h, buf, sizeof buf)); + + if (h.qdcount) + fprintf(f, ";; QUERY SECTION:\n"); + for (i = 0; i < h.qdcount; i++) { + if (_asr_unpack_query(&p, &q) == -1) + goto error; + fprintf(f, "%s\n", print_query(&q, buf, sizeof buf)); + } + + an = 0; + ns = an + h.ancount; + ar = ns + h.nscount; + n = ar + h.arcount; + + for (i = 0; i < n; i++) { + if (i == an) + fprintf(f, "\n;; ANSWER SECTION:\n"); + if (i == ns) + fprintf(f, "\n;; AUTHORITY SECTION:\n"); + if (i == ar) + fprintf(f, "\n;; ADDITIONAL SECTION:\n"); + + if (_asr_unpack_rr(&p, &rr) == -1) + goto error; + fprintf(f, "%s\n", print_rr(&rr, buf, sizeof buf)); + } + + if (p.offset != len) + fprintf(f, ";; REMAINING GARBAGE %zu\n", len - p.offset); + + error: + if (p.err) + fprintf(f, ";; ERROR AT OFFSET %zu/%zu: %s\n", p.offset, p.len, + strerror(p.err)); +} + +const char * +_asr_print_sockaddr(const struct sockaddr *sa, char *buf, size_t len) +{ + char h[256]; + int portno; + union { + const struct sockaddr *sa; + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + } s; + + s.sa = sa; + + switch (sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &s.sin->sin_addr, h, sizeof h); + portno = ntohs(s.sin->sin_port); + break; + case AF_INET6: + inet_ntop(AF_INET6, &s.sin6->sin6_addr, h, sizeof h); + portno = ntohs(s.sin6->sin6_port); + break; + default: + snprintf(buf, len, "?"); + return (buf); + } + + snprintf(buf, len, "%s:%i", h, portno); + return (buf); +} + +void +_asr_dump_config(FILE *f, struct asr *a) +{ + char buf[256]; + int i; + struct asr_ctx *ac; + unsigned int o; + + if (f == NULL) + return; + + ac = a->a_ctx; + + fprintf(f, "--------- ASR CONFIG ---------------\n"); + fprintf(f, "DOMAIN \"%s\"\n", ac->ac_domain); + fprintf(f, "SEARCH\n"); + for (i = 0; i < ac->ac_domcount; i++) + fprintf(f, " \"%s\"\n", ac->ac_dom[i]); + fprintf(f, "OPTIONS\n"); + fprintf(f, " options:"); + o = ac->ac_options; + +#define PRINTOPT(flag, n) if (o & (flag)) { fprintf(f, " " n); o &= ~(flag); } + PRINTOPT(RES_INIT, "INIT"); + PRINTOPT(RES_DEBUG, "DEBUG"); + PRINTOPT(RES_USEVC, "USEVC"); + PRINTOPT(RES_IGNTC, "IGNTC"); + PRINTOPT(RES_RECURSE, "RECURSE"); + PRINTOPT(RES_DEFNAMES, "DEFNAMES"); + PRINTOPT(RES_STAYOPEN, "STAYOPEN"); + PRINTOPT(RES_DNSRCH, "DNSRCH"); + PRINTOPT(RES_NOALIASES, "NOALIASES"); + PRINTOPT(RES_USE_EDNS0, "USE_EDNS0"); + PRINTOPT(RES_USE_DNSSEC, "USE_DNSSEC"); +#ifdef RES_USE_CD + PRINTOPT(RES_USE_CD, "USE_CD"); +#endif +#ifdef RES_TRUSTAD + PRINTOPT(RES_TRUSTAD, "TRUSTAD"); +#endif + if (o) + fprintf(f, " 0x%08x", o); + fprintf(f, "\n"); + + fprintf(f, " ndots: %i\n", ac->ac_ndots); + fprintf(f, " family:"); + for (i = 0; ac->ac_family[i] != -1; i++) + fprintf(f, " %s", (ac->ac_family[i] == AF_INET)?"inet4":"inet6"); + fprintf(f, "\n"); + fprintf(f, "NAMESERVERS timeout=%i retry=%i\n", + ac->ac_nstimeout, + ac->ac_nsretries); + for (i = 0; i < ac->ac_nscount; i++) + fprintf(f, " %s\n", _asr_print_sockaddr(ac->ac_ns[i], buf, + sizeof buf)); + fprintf(f, "LOOKUP %s", ac->ac_db); + fprintf(f, "\n------------------------------------\n"); +} + +#define CASE(n) case n: return #n + +const char * +_asr_statestr(int state) +{ + switch (state) { + CASE(ASR_STATE_INIT); + CASE(ASR_STATE_NEXT_DOMAIN); + CASE(ASR_STATE_NEXT_DB); + CASE(ASR_STATE_SAME_DB); + CASE(ASR_STATE_NEXT_FAMILY); + CASE(ASR_STATE_NEXT_NS); + CASE(ASR_STATE_UDP_SEND); + CASE(ASR_STATE_UDP_RECV); + CASE(ASR_STATE_TCP_WRITE); + CASE(ASR_STATE_TCP_READ); + CASE(ASR_STATE_PACKET); + CASE(ASR_STATE_SUBQUERY); + CASE(ASR_STATE_NOT_FOUND); + CASE(ASR_STATE_HALT); + default: + return "?"; + } +}; + +const char * +_asr_querystr(int type) +{ + switch (type) { + CASE(ASR_SEND); + CASE(ASR_SEARCH); + CASE(ASR_GETRRSETBYNAME); + CASE(ASR_GETHOSTBYNAME); + CASE(ASR_GETHOSTBYADDR); + CASE(ASR_GETADDRINFO); + CASE(ASR_GETNAMEINFO); + default: + return "?"; + } +} + +const char * +_asr_transitionstr(int type) +{ + switch (type) { + CASE(ASYNC_COND); + CASE(ASYNC_DONE); + default: + return "?"; + } +} blob - /dev/null blob + 7f551d78d1e2609821b2fe702a90ee670257b1a5 (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr_private.h @@ -0,0 +1,361 @@ +/* $OpenBSD: asr_private.h,v 1.49 2023/11/20 12:15:16 florian Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "asr_compat.h" + +#define QR_MASK (0x1 << 15) +#define OPCODE_MASK (0xf << 11) +#define AA_MASK (0x1 << 10) +#define TC_MASK (0x1 << 9) +#define RD_MASK (0x1 << 8) +#define RA_MASK (0x1 << 7) +#define Z_MASK (0x1 << 6) +#define AD_MASK (0x1 << 5) +#define CD_MASK (0x1 << 4) +#define RCODE_MASK (0xf) + +#define OPCODE(v) ((v) & OPCODE_MASK) +#define RCODE(v) ((v) & RCODE_MASK) + + +struct asr_pack { + char *buf; + size_t len; + size_t offset; + int err; +}; + +struct asr_unpack { + const char *buf; + size_t len; + size_t offset; + int err; +}; + +struct asr_dns_header { + uint16_t id; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +}; + +struct asr_dns_query { + char q_dname[MAXDNAME]; + uint16_t q_type; + uint16_t q_class; +}; + +struct asr_dns_rr { + char rr_dname[MAXDNAME]; + uint16_t rr_type; + uint16_t rr_class; + uint32_t rr_ttl; + union { + struct { + char cname[MAXDNAME]; + } cname; + struct { + uint16_t preference; + char exchange[MAXDNAME]; + } mx; + struct { + char nsname[MAXDNAME]; + } ns; + struct { + char ptrname[MAXDNAME]; + } ptr; + struct { + char mname[MAXDNAME]; + char rname[MAXDNAME]; + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; + } soa; + struct { + struct in_addr addr; + } in_a; + struct { + struct in6_addr addr6; + } in_aaaa; + struct { + uint16_t rdlen; + const void *rdata; + } other; + } rr; +}; + + +#define ASR_MAXNS 5 +#define ASR_MAXDB 3 +#define ASR_MAXDOM 10 + +enum async_type { + ASR_SEND, + ASR_SEARCH, + ASR_GETRRSETBYNAME, + ASR_GETHOSTBYNAME, + ASR_GETHOSTBYADDR, + ASR_GETADDRINFO, + ASR_GETNAMEINFO, +}; + +#define ASR_DB_FILE 'f' +#define ASR_DB_DNS 'b' + +struct asr_ctx { + int ac_refcount; + int ac_options; + int ac_ndots; + char *ac_domain; + int ac_domcount; + char *ac_dom[ASR_MAXDOM]; + int ac_dbcount; + char ac_db[ASR_MAXDB + 1]; + int ac_family[3]; + + int ac_nscount; + int ac_nstimeout; + int ac_nsretries; + struct sockaddr *ac_ns[ASR_MAXNS]; + +}; + +struct asr { + pid_t a_pid; + time_t a_mtime; + time_t a_rtime; + struct asr_ctx *a_ctx; +}; + +#define ASYNC_COND 0 +#define ASYNC_DONE 1 + +#define ASYNC_DOM_FQDN 0x00000001 +#define ASYNC_DOM_NDOTS 0x00000002 +#define ASYNC_DOM_DOMAIN 0x00000004 +#define ASYNC_DOM_ASIS 0x00000008 + +#define ASYNC_NODATA 0x00000100 +#define ASYNC_AGAIN 0x00000200 + +#define ASYNC_GETNET 0x00001000 +#define ASYNC_EXTOBUF 0x00002000 + +#define ASYNC_NO_INET 0x00010000 +#define ASYNC_NO_INET6 0x00020000 + +struct asr_query { + int (*as_run)(struct asr_query *, struct asr_result *); + struct asr_ctx *as_ctx; + int as_type; + int as_flags; + int as_state; + + /* cond */ + int as_timeout; + int as_fd; + struct asr_query *as_subq; + + /* loop indices in ctx */ + int as_dom_step; + int as_dom_idx; + int as_dom_flags; + int as_family_idx; + int as_db_idx; + + int as_count; + + union { + struct { + uint16_t reqid; + int class; + int type; + char *dname; /* not fqdn! */ + int rcode; /* response code */ + int ancount; /* answer count */ + + int nsidx; + int nsloop; + + /* io buffers for query/response */ + unsigned char *obuf; + size_t obuflen; + size_t obufsize; + unsigned char *ibuf; + size_t ibuflen; + size_t ibufsize; + size_t datalen; /* for tcp io */ + uint16_t pktlen; + } dns; + + struct { + int class; + int type; + char *name; + int saved_h_errno; + } search; + + struct { + int flags; + int class; + int type; + char *name; + } rrset; + + struct { + char *name; + int family; + char addr[16]; + int addrlen; + int subq_h_errno; + } hostnamadr; + + struct { + char *hostname; + char *servname; + int port_tcp; + int port_udp; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } sa; + + struct addrinfo hints; + char *fqdn; + struct addrinfo *aifirst; + struct addrinfo *ailast; + } ai; + + struct { + char *hostname; + char *servname; + size_t hostnamelen; + size_t servnamelen; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } sa; + int flags; + } ni; +#define MAXTOKEN 10 + } as; + +}; + +#define AS_DB(p) ((p)->as_ctx->ac_db[(p)->as_db_idx - 1]) +#define AS_FAMILY(p) ((p)->as_ctx->ac_family[(p)->as_family_idx]) + +enum asr_state { + ASR_STATE_INIT, + ASR_STATE_NEXT_DOMAIN, + ASR_STATE_NEXT_DB, + ASR_STATE_SAME_DB, + ASR_STATE_NEXT_FAMILY, + ASR_STATE_NEXT_NS, + ASR_STATE_UDP_SEND, + ASR_STATE_UDP_RECV, + ASR_STATE_TCP_WRITE, + ASR_STATE_TCP_READ, + ASR_STATE_PACKET, + ASR_STATE_SUBQUERY, + ASR_STATE_NOT_FOUND, + ASR_STATE_HALT, +}; + +#define MAXPACKETSZ 4096 + +__BEGIN_HIDDEN_DECLS + +/* asr_utils.c */ +void _asr_pack_init(struct asr_pack *, char *, size_t); +int _asr_pack_header(struct asr_pack *, const struct asr_dns_header *); +int _asr_pack_query(struct asr_pack *, uint16_t, uint16_t, const char *); +int _asr_pack_edns0(struct asr_pack *, uint16_t, int); +void _asr_unpack_init(struct asr_unpack *, const char *, size_t); +int _asr_unpack_header(struct asr_unpack *, struct asr_dns_header *); +int _asr_unpack_query(struct asr_unpack *, struct asr_dns_query *); +int _asr_unpack_rr(struct asr_unpack *, struct asr_dns_rr *); +int _asr_sockaddr_from_str(struct sockaddr *, int, const char *); +ssize_t _asr_dname_from_fqdn(const char *, char *, size_t); +ssize_t _asr_addr_as_fqdn(const char *, int, char *, size_t); +int hnok_lenient(const char *); +int _asr_is_localhost(const char*); + +/* asr.c */ +void _asr_resolver_done(void *); +struct asr_ctx *_asr_use_resolver(void *); +struct asr_ctx *_asr_no_resolver(void); +void _asr_ctx_unref(struct asr_ctx *); +struct asr_query *_asr_async_new(struct asr_ctx *, int); +void _asr_async_free(struct asr_query *); +size_t _asr_make_fqdn(const char *, const char *, char *, size_t); +char *_asr_strdname(const char *, char *, size_t); +int _asr_iter_db(struct asr_query *); +int _asr_parse_namedb_line(FILE *, char **, int, char *, size_t); + +/* *_async.c */ +struct asr_query *_res_query_async_ctx(const char *, int, int, struct asr_ctx *); +struct asr_query *_res_search_async_ctx(const char *, int, int, struct asr_ctx *); +struct asr_query *_gethostbyaddr_async_ctx(const void *, socklen_t, int, + struct asr_ctx *); + +int _asr_iter_domain(struct asr_query *, const char *, char *, size_t); + +#ifdef DEBUG + +#define DPRINT(...) do { if(_asr_debug) { \ + fprintf(_asr_debug, __VA_ARGS__); \ + } } while (0) +#define DPRINT_PACKET(n, p, s) do { if(_asr_debug) { \ + fprintf(_asr_debug, "----- %s -----\n", n); \ + _asr_dump_packet(_asr_debug, (p), (s)); \ + fprintf(_asr_debug, "--------------\n"); \ + } } while (0) + +#else /* DEBUG */ + +#define DPRINT(...) +#define DPRINT_PACKET(...) + +#endif /* DEBUG */ + +const char *_asr_querystr(int); +const char *_asr_statestr(int); +const char *_asr_transitionstr(int); +const char *_asr_print_sockaddr(const struct sockaddr *, char *, size_t); +void _asr_dump_config(FILE *, struct asr *); +void _asr_dump_packet(FILE *, const void *, size_t); + +extern FILE *_asr_debug; + +#define async_set_state(a, s) do { \ + DPRINT("asr: [%s@%p] %s -> %s\n", \ + _asr_querystr((a)->as_type), \ + as, \ + _asr_statestr((a)->as_state), \ + _asr_statestr((s))); \ + (a)->as_state = (s); } while (0) + +__END_HIDDEN_DECLS blob - /dev/null blob + d9b3282f3b7526bd775c90a862986fd97050115c (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr_run.3 @@ -0,0 +1,335 @@ +.\" $OpenBSD: asr_run.3,v 1.5 2022/03/31 17:27:15 naddy Exp $ +.\" +.\" Copyright (c) 2012-2014, Eric Faurot +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt ASR_RUN 3 +.Os +.Sh NAME +.Nm asr_run , +.Nm asr_run_sync , +.Nm asr_abort , +.Nm asr_resolver_from_string , +.Nm asr_resolver_free , +.Nm res_send_async , +.Nm res_query_async , +.Nm res_search_async , +.Nm getrrsetbyname_async , +.Nm gethostbyname_async , +.Nm gethostbyname2_async , +.Nm gethostbyaddr_async , +.Nm getnetbyname_async , +.Nm getnetbyaddr_async , +.Nm getaddrinfo_async , +.Nm getnameinfo_async +.Nd asynchronous resolver functions +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In netdb.h +.In asr.h +.Ft int +.Fn asr_run "struct asr_query *aq" "struct asr_result *ar" +.Ft int +.Fn asr_run_sync "struct asr_query *aq" "struct asr_result *ar" +.Ft void +.Fn asr_abort "struct asr_query *aq" +.Ft void * +.Fn asr_resolver_from_string "const char *str" +.Ft void +.Fn asr_resolver_free "void *asr" +.Ft struct asr_query * +.Fn res_send_async "const unsigned char *pkt" "int pktlen" "void *asr" +.Ft struct asr_query * +.Fn res_query_async "const char *name" "int class" "int type" "void *asr" +.Ft struct asr_query * +.Fn res_search_async "const char *name" "int class" "int type" "void *asr" +.Ft struct asr_query * +.Fn getrrsetbyname_async "const char *hostname" "unsigned int rdclass" "unsigned int rdtype" "unsigned int flags" "void *asr" +.Ft struct asr_query * +.Fn gethostbyname_async "const char *name" "void *asr" +.Ft struct asr_query * +.Fn gethostbyname2_async "const char *name" "int af" "void *asr" +.Ft struct asr_query * +.Fn gethostbyaddr_async "const void *addr" "socklen_t len" "int af" "void *asr" +.Ft struct asr_query * +.Fn getnetbyname_async "const char *name" "void *asr" +.Ft struct asr_query * +.Fn getnetbyaddr_async "in_addr_t net" "int type" "void *asr" +.Ft struct asr_query * +.Fn getaddrinfo_async "const char *hostname" "const char *servname" "const struct addrinfo *hints" "void *asr" +.Ft struct asr_query * +.Fn getnameinfo_async "const struct sockaddr *sa" "socklen_t salen" "char *host" "size_t hostlen" "char *serv" "size_t servlen" "int flags" "void *asr" +.Sh DESCRIPTION +The +.Nm asr +functions provide a simple interface for asynchronous address +resolution and nameserver querying. +They should be used in place of the classical resolver functions +of libc when blocking is not desirable. +.Pp +The principle of operation is as follows: +All async requests are made against an +.Nm asr +context which basically defines a list of sources to query and a +strategy to do so. +The user creates a query through one of the dedicated functions, and +gets a handle representing the internal query. +A query is a state-machine that can be run to try to fulfill a +particular request. +This is done by calling in a generic API that performs the state +transitions until it needs to give the control back to the user, +either because a result is available, or because the next transition +implies a blocking call (a file descriptor needs to be read from or +written to). +The user is responsible for dealing with the situation: either get +the result, or wait until the fd conditions are met, and then call +back into the resolving machinery when it is ready to proceed. +.Pp +The +.Fn asr_run +function drives the resolving process. +It runs the asynchronous query represented by the +.Fa aq +handle until a result is available, or until it cannot continue +without blocking. +The results are returned to the user through the +.Fa ar +parameter, which must be a valid pointer to user allocated memory. +.Fa ar +is defined as: +.Bd -literal +struct asr_result { + + /* Fields set if the query is not done yet (asr_run returns 0) */ + int ar_cond; /* ASR_WANT_READ or ASR_WANT_WRITE */ + int ar_fd; /* the fd waiting for io condition */ + int ar_timeout; /* time to wait for in milliseconds */ + + /* Error fields. Depends on the query type. */ + int ar_errno; + int ar_h_errno; + int ar_gai_errno; + int ar_rrset_errno; + + /* Result for res_*_async() calls */ + int ar_count; /* number of answers in the dns reply */ + int ar_rcode; /* response code in the dns reply */ + void *ar_data; /* raw reply packet (must be freed) */ + int ar_datalen; /* reply packet length */ + struct sockaddr_storage ar_ns; /* nameserver that responded */ + + /* Result for other calls. Must be freed properly. */ + struct addrinfo *ar_addrinfo; + struct rrsetinfo *ar_rrsetinfo; + struct hostent *ar_hostent; + struct netent *ar_netent; +}; +.Ed +.Pp +The function returns one of the following values: +.Bl -tag -width "0 " -offset indent +.It 0 +The query cannot be processed further until a specific condition on a +file descriptor becomes true. +The following members of the +.Fa ar +structure are filled: +.Pp +.Bl -tag -width "ar_timeout " -compact +.It Fa ar_cond +one of ASR_WANT_READ or ASR_WANT_WRITE, +.It Fa ar_fd +the file descriptor waiting for an IO operation, +.It Fa ar_timeout +the amount of time to wait for in milliseconds. +.El +.Pp +The caller is expected to call +.Fn asr_run +again once the condition holds or the timeout expires. +.It 1 +The query is completed. +The members relevant to the actual async query type are set accordingly, +including error conditions. +In any case, the query is cleared and its handle is invalidated. +.El +.Pp +Note that although the query itself may fail (the error being properly reported +in the +.Fa ar +structure), the +.Fn asr_run +function itself cannot fail and it always preserves errno. +.Pp +The +.Fn asr_run_sync +function is a wrapper around +.Fn asr_run +that handles the read/write conditions, thus falling back to a blocking +interface. +It only returns 1. +It also preserves errno. +.Pp +The +.Fn asr_abort +function clears a running query. +It can be called when the query is waiting on a file descriptor. +Note that a completed query is already cleared when +.Fn asr_run +returns, so +.Fn asr_abort +must not be called in this case. +.Pp +The +.Fn asr_resolver_from_string +function constructs an asr context from a string that conforms to the +.Xr resolv.conf 5 +file format. +.Fn asr_resolver_free +frees an asr context obtained from +.Fn asr_resolver_from_string . +.Pp +The remaining functions are used to initiate different kinds of query +on the +.Fa asr +resolver context. +The specific operational details for each of them are described below. +All functions return a handle to an internal query, or NULL if they could +not allocate the necessary resources to initiate the query. +All other errors (especially invalid parameters) are reported when calling +.Fn asr_run . +They usually have the same interface as an existing resolver function, with +an additional +.Ar asr +argument, which specifies the context to use for this request. +An +.Ar asr +argument of NULL will use the default context for the current thread. +This is constructed from +.Pa /etc/resolv.conf +and takes care of reloading the file when it changes. +.Pp +The +.Fn res_send_async , +.Fn res_query_async +and +.Fn res_search_async +functions are asynchronous versions of the standard libc resolver routines. +Their interface is very similar, except that the response buffer is always +allocated internally. +The return value is found upon completion in the +.Fa ar_datalen +member of the response structure. +In addition, the +.Fa ar_ns +structure contains the address of the DNS server that sent the response, +.Fa ar_rcode +contains the code returned by the server in the DNS response packet, and +.Fa ar_count +contains the number of answers in the packet. +If a response is received, it is placed in a newly allocated buffer +and returned as +.Fa ar_data +member. +This buffer must be freed by the caller. +On error, the +.Fa ar_errno +and +.Fa ar_h_errno +members are set accordingly. +.Pp +The +.Fn getrrsetbyname_async +function is an asynchronous version of +.Xr getrrsetbyname 3 . +Upon completion, the return code is found in +.Fa ar_rrset_errno +and the address to the newly allocated result set is set in +.Fa ar_rrsetinfo . +As for the blocking function, it must be freed by calling +.Xr freerrset 3 . +.Pp +The +.Fn gethostbyname_async , +.Fn gethostbyname2_async +and +.Fn gethostbyaddr_async +functions provide an asynchronous version of the network host entry functions. +Upon completion, +.Ar ar_h_errno +is set and the resulting hostent address, if found, is set +in the +.Ar ar_hostent +field. +Note that unlike their blocking counterparts, these functions always return a +pointer to newly allocated memory, which must be released by the caller using +.Xr free 3 . +.Pp +Similarly, the +.Fn getnetbyname_async +and +.Fn getnetbyaddr_async +functions provide an asynchronous version of the network entry functions. +Upon completion, +.Ar ar_h_errno +is set and the resulting netent address, if found, is set +in the +.Ar ar_netent +field. +The memory there is also allocated for the request, and it must be freed by +.Xr free 3 . +.Pp +The +.Fn getaddrinfo_async +function is an asynchronous version of the +.Xr getaddrinfo 3 +call. +It provides a chain of addrinfo structures with all valid combinations of +socket address for the given +.Fa hostname , +.Fa servname +and +.Fa hints . +Those three parameters have the same meaning as for the blocking counterpart. +Upon completion the return code is set in +.Fa ar_gai_errno . +The +.Fa ar_errno +member may also be set. +On success, the +.Fa ar_addrinfo +member points to a newly allocated list of addrinfo. +This list must be freed with +.Xr freeaddrinfo 3 . +.Sh WORKING WITH THREADS +This implementation of the asynchronous resolver interface is thread-safe +and lock-free internally, but the following restriction applies: +Two different threads must not create queries on the same context or +run queries originating from the same context at the same time. +If they want to do that, all calls must be protected by a mutex around +that context. +.Pp +It is generally not a problem since the main point of the asynchronous +resolver is to multiplex queries within a single thread of control, +so sharing a resolver among threads is not useful. +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr gethostbyname 3 , +.Xr getnameinfo 3 , +.Xr getnetbyname 3 , +.Xr getrrsetbyname 3 , +.Xr res_send 3 , +.Xr resolv.conf 5 blob - /dev/null blob + 40052573b2ac0505b7c59baf7bed58cbe58976f9 (mode 644) --- /dev/null +++ openbsd-compat/libasr/asr_utils.c @@ -0,0 +1,625 @@ +/* $OpenBSD: asr_utils.c,v 1.22 2023/11/20 12:15:16 florian Exp $ */ +/* + * Copyright (c) 2009-2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "openbsd-compat.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asr_private.h" + +static int dname_check_label(const char *, size_t); +static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *, + char *, size_t); + +static int unpack_data(struct asr_unpack *, void *, size_t); +static int unpack_u16(struct asr_unpack *, uint16_t *); +static int unpack_u32(struct asr_unpack *, uint32_t *); +static int unpack_inaddr(struct asr_unpack *, struct in_addr *); +static int unpack_in6addr(struct asr_unpack *, struct in6_addr *); +static int unpack_dname(struct asr_unpack *, char *, size_t); + +static int pack_data(struct asr_pack *, const void *, size_t); +static int pack_u16(struct asr_pack *, uint16_t); +static int pack_dname(struct asr_pack *, const char *); + +static int +dname_check_label(const char *s, size_t l) +{ + if (l == 0 || l > 63) + return (-1); + + return (0); +} + +ssize_t +_asr_dname_from_fqdn(const char *str, char *dst, size_t max) +{ + ssize_t res; + size_t l, n; + char *d; + + res = 0; + + /* special case: the root domain */ + if (str[0] == '.') { + if (str[1] != '\0') + return (-1); + if (dst && max >= 1) + *dst = '\0'; + return (1); + } + + for (; *str; str = d + 1) { + + d = strchr(str, '.'); + if (d == NULL || d == str) + return (-1); + + l = (d - str); + + if (dname_check_label(str, l) == -1) + return (-1); + + res += l + 1; + + if (dst) { + *dst++ = l; + max -= 1; + n = (l > max) ? max : l; + memmove(dst, str, n); + max -= n; + if (max == 0) + dst = NULL; + else + dst += n; + } + } + + if (dst) + *dst++ = '\0'; + + return (res + 1); +} + +static ssize_t +dname_expand(const unsigned char *data, size_t len, size_t offset, + size_t *newoffset, char *dst, size_t max) +{ + size_t n, count, end, ptr, start; + ssize_t res; + + if (offset >= len) + return (-1); + + res = 0; + end = start = offset; + + for (; (n = data[offset]); ) { + if ((n & 0xc0) == 0xc0) { + if (offset + 1 >= len) + return (-1); + ptr = 256 * (n & ~0xc0) + data[offset + 1]; + if (ptr >= start) + return (-1); + if (end < offset + 2) + end = offset + 2; + offset = start = ptr; + continue; + } + if (offset + n + 1 >= len) + return (-1); + + if (dname_check_label(data + offset + 1, n) == -1) + return (-1); + + /* copy n + at offset+1 */ + if (dst != NULL && max != 0) { + count = (max < n + 1) ? (max) : (n + 1); + memmove(dst, data + offset, count); + dst += count; + max -= count; + } + res += n + 1; + offset += n + 1; + if (end < offset) + end = offset; + } + if (end < offset + 1) + end = offset + 1; + + if (dst != NULL && max != 0) + dst[0] = 0; + if (newoffset) + *newoffset = end; + return (res + 1); +} + +void +_asr_pack_init(struct asr_pack *pack, char *buf, size_t len) +{ + pack->buf = buf; + pack->len = len; + pack->offset = 0; + pack->err = 0; +} + +void +_asr_unpack_init(struct asr_unpack *unpack, const char *buf, size_t len) +{ + unpack->buf = buf; + unpack->len = len; + unpack->offset = 0; + unpack->err = 0; +} + +static int +unpack_data(struct asr_unpack *p, void *data, size_t len) +{ + if (p->err) + return (-1); + + if (p->len - p->offset < len) { + p->err = EOVERFLOW; + return (-1); + } + + memmove(data, p->buf + p->offset, len); + p->offset += len; + + return (0); +} + +static int +unpack_u16(struct asr_unpack *p, uint16_t *u16) +{ + if (unpack_data(p, u16, 2) == -1) + return (-1); + + *u16 = ntohs(*u16); + + return (0); +} + +static int +unpack_u32(struct asr_unpack *p, uint32_t *u32) +{ + if (unpack_data(p, u32, 4) == -1) + return (-1); + + *u32 = ntohl(*u32); + + return (0); +} + +static int +unpack_inaddr(struct asr_unpack *p, struct in_addr *a) +{ + return (unpack_data(p, a, 4)); +} + +static int +unpack_in6addr(struct asr_unpack *p, struct in6_addr *a6) +{ + return (unpack_data(p, a6, 16)); +} + +static int +unpack_dname(struct asr_unpack *p, char *dst, size_t max) +{ + ssize_t e; + + if (p->err) + return (-1); + + e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max); + if (e == -1) { + p->err = EINVAL; + return (-1); + } + if (e < 0 || e > MAXDNAME) { + p->err = ERANGE; + return (-1); + } + + return (0); +} + +int +_asr_unpack_header(struct asr_unpack *p, struct asr_dns_header *h) +{ + if (unpack_data(p, h, HFIXEDSZ) == -1) + return (-1); + + h->flags = ntohs(h->flags); + h->qdcount = ntohs(h->qdcount); + h->ancount = ntohs(h->ancount); + h->nscount = ntohs(h->nscount); + h->arcount = ntohs(h->arcount); + + return (0); +} + +int +_asr_unpack_query(struct asr_unpack *p, struct asr_dns_query *q) +{ + unpack_dname(p, q->q_dname, sizeof(q->q_dname)); + unpack_u16(p, &q->q_type); + unpack_u16(p, &q->q_class); + + return (p->err) ? (-1) : (0); +} + +int +_asr_unpack_rr(struct asr_unpack *p, struct asr_dns_rr *rr) +{ + uint16_t rdlen; + size_t save_offset; + + unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); + unpack_u16(p, &rr->rr_type); + unpack_u16(p, &rr->rr_class); + unpack_u32(p, &rr->rr_ttl); + unpack_u16(p, &rdlen); + + if (p->err) + return (-1); + + if (p->len - p->offset < rdlen) { + p->err = EOVERFLOW; + return (-1); + } + + save_offset = p->offset; + + switch (rr->rr_type) { + + case T_CNAME: + unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); + break; + + case T_MX: + unpack_u16(p, &rr->rr.mx.preference); + unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); + break; + + case T_NS: + unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); + break; + + case T_PTR: + unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); + break; + + case T_SOA: + unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); + unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); + unpack_u32(p, &rr->rr.soa.serial); + unpack_u32(p, &rr->rr.soa.refresh); + unpack_u32(p, &rr->rr.soa.retry); + unpack_u32(p, &rr->rr.soa.expire); + unpack_u32(p, &rr->rr.soa.minimum); + break; + + case T_A: + if (rr->rr_class != C_IN) + goto other; + unpack_inaddr(p, &rr->rr.in_a.addr); + break; + + case T_AAAA: + if (rr->rr_class != C_IN) + goto other; + unpack_in6addr(p, &rr->rr.in_aaaa.addr6); + break; + default: + other: + rr->rr.other.rdata = p->buf + p->offset; + rr->rr.other.rdlen = rdlen; + p->offset += rdlen; + } + + if (p->err) + return (-1); + + /* make sure that the advertised rdlen is really ok */ + if (p->offset - save_offset != rdlen) + p->err = EINVAL; + + return (p->err) ? (-1) : (0); +} + +static int +pack_data(struct asr_pack *p, const void *data, size_t len) +{ + if (p->err) + return (-1); + + if (p->len < p->offset + len) { + p->err = EOVERFLOW; + return (-1); + } + + memmove(p->buf + p->offset, data, len); + p->offset += len; + + return (0); +} + +static int +pack_u16(struct asr_pack *p, uint16_t v) +{ + v = htons(v); + + return (pack_data(p, &v, 2)); +} + +static int +pack_dname(struct asr_pack *p, const char *dname) +{ + /* dname compression would be nice to have here. + * need additional context. + */ + return (pack_data(p, dname, strlen(dname) + 1)); +} + +int +_asr_pack_header(struct asr_pack *p, const struct asr_dns_header *h) +{ + struct asr_dns_header c; + + c.id = h->id; + c.flags = htons(h->flags); + c.qdcount = htons(h->qdcount); + c.ancount = htons(h->ancount); + c.nscount = htons(h->nscount); + c.arcount = htons(h->arcount); + + return (pack_data(p, &c, HFIXEDSZ)); +} + +int +_asr_pack_query(struct asr_pack *p, uint16_t type, uint16_t class, const char *dname) +{ + pack_dname(p, dname); + pack_u16(p, type); + pack_u16(p, class); + + return (p->err) ? (-1) : (0); +} + +int +_asr_pack_edns0(struct asr_pack *p, uint16_t pktsz, int dnssec_do) +{ + DPRINT("asr EDNS0 pktsz:%hu dnssec:%s\n", pktsz, + dnssec_do ? "yes" : "no"); + + pack_dname(p, ""); /* root */ + pack_u16(p, T_OPT); /* OPT */ + pack_u16(p, pktsz); /* UDP payload size */ + + /* extended RCODE and flags */ + pack_u16(p, 0); + pack_u16(p, dnssec_do ? DNS_MESSAGEEXTFLAG_DO : 0); + + pack_u16(p, 0); /* RDATA len */ + + return (p->err) ? (-1) : (0); +} + +int +_asr_sockaddr_from_str(struct sockaddr *sa, int family, const char *str) +{ + struct in_addr ina; + struct in6_addr in6a; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + char *cp, *str2; + const char *errstr; + + switch (family) { + case PF_UNSPEC: + if (_asr_sockaddr_from_str(sa, PF_INET, str) == 0) + return (0); + return _asr_sockaddr_from_str(sa, PF_INET6, str); + + case PF_INET: + if (inet_pton(PF_INET, str, &ina) != 1) + return (-1); + + sin = (struct sockaddr_in *)sa; + memset(sin, 0, sizeof *sin); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin->sin_len = sizeof(struct sockaddr_in); +#endif + sin->sin_family = PF_INET; + sin->sin_addr.s_addr = ina.s_addr; + return (0); + + case PF_INET6: + cp = strchr(str, SCOPE_DELIMITER); + if (cp) { + str2 = strdup(str); + if (str2 == NULL) + return (-1); + str2[cp - str] = '\0'; + if (inet_pton(PF_INET6, str2, &in6a) != 1) { + free(str2); + return (-1); + } + cp++; + free(str2); + } else if (inet_pton(PF_INET6, str, &in6a) != 1) + return (-1); + + sin6 = (struct sockaddr_in6 *)sa; + memset(sin6, 0, sizeof *sin6); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif + sin6->sin6_family = PF_INET6; + sin6->sin6_addr = in6a; + + if (cp == NULL) + return (0); + + if (IN6_IS_ADDR_LINKLOCAL(&in6a) || + IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || + IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)) + if ((sin6->sin6_scope_id = if_nametoindex(cp))) + return (0); + + sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); + if (errstr) + return (-1); + return (0); + + default: + break; + } + + return (-1); +} + +ssize_t +_asr_addr_as_fqdn(const char *addr, int family, char *dst, size_t max) +{ + const struct in6_addr *in6_addr; + in_addr_t in_addr; + + switch (family) { + case AF_INET: + in_addr = ntohl(*((const in_addr_t *)addr)); + snprintf(dst, max, + "%d.%d.%d.%d.in-addr.arpa.", + in_addr & 0xff, + (in_addr >> 8) & 0xff, + (in_addr >> 16) & 0xff, + (in_addr >> 24) & 0xff); + break; + case AF_INET6: + in6_addr = (const struct in6_addr *)addr; + snprintf(dst, max, + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." + "ip6.arpa.", + in6_addr->s6_addr[15] & 0xf, + (in6_addr->s6_addr[15] >> 4) & 0xf, + in6_addr->s6_addr[14] & 0xf, + (in6_addr->s6_addr[14] >> 4) & 0xf, + in6_addr->s6_addr[13] & 0xf, + (in6_addr->s6_addr[13] >> 4) & 0xf, + in6_addr->s6_addr[12] & 0xf, + (in6_addr->s6_addr[12] >> 4) & 0xf, + in6_addr->s6_addr[11] & 0xf, + (in6_addr->s6_addr[11] >> 4) & 0xf, + in6_addr->s6_addr[10] & 0xf, + (in6_addr->s6_addr[10] >> 4) & 0xf, + in6_addr->s6_addr[9] & 0xf, + (in6_addr->s6_addr[9] >> 4) & 0xf, + in6_addr->s6_addr[8] & 0xf, + (in6_addr->s6_addr[8] >> 4) & 0xf, + in6_addr->s6_addr[7] & 0xf, + (in6_addr->s6_addr[7] >> 4) & 0xf, + in6_addr->s6_addr[6] & 0xf, + (in6_addr->s6_addr[6] >> 4) & 0xf, + in6_addr->s6_addr[5] & 0xf, + (in6_addr->s6_addr[5] >> 4) & 0xf, + in6_addr->s6_addr[4] & 0xf, + (in6_addr->s6_addr[4] >> 4) & 0xf, + in6_addr->s6_addr[3] & 0xf, + (in6_addr->s6_addr[3] >> 4) & 0xf, + in6_addr->s6_addr[2] & 0xf, + (in6_addr->s6_addr[2] >> 4) & 0xf, + in6_addr->s6_addr[1] & 0xf, + (in6_addr->s6_addr[1] >> 4) & 0xf, + in6_addr->s6_addr[0] & 0xf, + (in6_addr->s6_addr[0] >> 4) & 0xf); + break; + default: + return (-1); + } + return (0); +} + +int +hnok_lenient(const char *dn) +{ + int pch = '\0', ch = *dn++; + + while (ch != '\0') { + /* can't start with . or - */ + if (pch == '\0' && (ch == '.' || ch == '-')) + return 0; + if (pch == '.' && ch == '.') + return 0; + if (!(isalpha((unsigned char)ch) || isdigit((unsigned char)ch) || + ch == '.' || ch == '-' || ch == '_')) + return 0; + pch = ch; ch = *dn++; + } + return 1; +} + +/* Check if the hostname is localhost or if it's in the localhost domain */ +int +_asr_is_localhost(const char *hn) +{ + size_t hnlen, localhostlen; + + if (hn == NULL) + return 0; + + if (strcasecmp(hn, "localhost") == 0 || + strcasecmp(hn, "localhost.") == 0) + return 1; + + hnlen = strlen(hn); + localhostlen = strlen(".localhost"); + + if (hnlen < localhostlen) + return 0; + + if (strcasecmp(hn + hnlen - localhostlen, ".localhost") == 0) + return 1; + + localhostlen++; + if (hnlen < localhostlen) + return 0; + + if (strcasecmp(hn + hnlen - localhostlen, ".localhost.") == 0) + return 1; + + return 0; +} blob - /dev/null blob + 37fe2d4d4493b4bafb263693b81676e062f977c6 (mode 644) --- /dev/null +++ openbsd-compat/libasr/getaddrinfo.c @@ -0,0 +1,55 @@ +/* $OpenBSD: getaddrinfo.c,v 1.9 2015/10/08 14:08:44 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct asr_query *as; + struct asr_result ar; + int saved_errno = errno; + + if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0) + res_init(); + + as = getaddrinfo_async(hostname, servname, hints, NULL); + if (as == NULL) { + if (errno == ENOMEM) { + errno = saved_errno; + return (EAI_MEMORY); + } + return (EAI_SYSTEM); + } + + asr_run_sync(as, &ar); + + *res = ar.ar_addrinfo; + if (ar.ar_gai_errno == EAI_SYSTEM) + errno = ar.ar_errno; + + return (ar.ar_gai_errno); +} +DEF_WEAK(getaddrinfo); blob - /dev/null blob + d32707cb82152a6178aeb1a382d182437d9b1c4b (mode 644) --- /dev/null +++ openbsd-compat/libasr/getaddrinfo_async.c @@ -0,0 +1,771 @@ +/* $OpenBSD: getaddrinfo_async.c,v 1.62 2024/01/15 18:03:39 florian Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asr_private.h" + +struct match { + int family; + int socktype; + int protocol; +}; + +static int getaddrinfo_async_run(struct asr_query *, struct asr_result *); +static int get_port(const char *, const char *, int); +static int iter_family(struct asr_query *, int); +static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); +static int addrinfo_from_file(struct asr_query *, int, FILE *); +static int addrinfo_from_pkt(struct asr_query *, char *, size_t); +static int addrconfig_setup(struct asr_query *); + +static const struct match matches[] = { + { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP }, + { PF_INET, SOCK_RAW, 0 }, + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, + { PF_INET6, SOCK_RAW, 0 }, + { -1, 0, 0, }, +}; + +#define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) +#define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0) +/* Do not match SOCK_RAW unless explicitly specified */ +#define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ + matches[(b)].socktype != SOCK_RAW)) + +enum { + DOM_INIT, + DOM_DOMAIN, + DOM_DONE +}; + +struct asr_query * +getaddrinfo_async(const char *hostname, const char *servname, + const struct addrinfo *hints, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0) + ac = _asr_use_resolver(asr); + else + ac = _asr_no_resolver(); + if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL) + goto abort; /* errno set */ + as->as_run = getaddrinfo_async_run; + + if (hostname) { + if ((as->as.ai.hostname = strdup(hostname)) == NULL) + goto abort; /* errno set */ + } + if (servname && (as->as.ai.servname = strdup(servname)) == NULL) + goto abort; /* errno set */ + if (hints) + memmove(&as->as.ai.hints, hints, sizeof *hints); + else { + memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); + as->as.ai.hints.ai_family = PF_UNSPEC; + as->as.ai.hints.ai_flags = AI_ADDRCONFIG; + } + + _asr_ctx_unref(ac); + return (as); + abort: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(getaddrinfo_async); + +static int +getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) +{ + char fqdn[MAXDNAME]; + const char *str; + struct addrinfo *ai; + int i, family, r, is_localhost = 0; + FILE *f; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } sa; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + /* + * First, make sure the parameters are valid. + */ + + as->as_count = 0; + + if (as->as.ai.hostname == NULL && + as->as.ai.servname == NULL) { + ar->ar_gai_errno = EAI_NONAME; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') { + ar->ar_gai_errno = EAI_NODATA; + async_set_state(as, ASR_STATE_HALT); + break; + } + + ai = &as->as.ai.hints; + + if (ai->ai_addrlen || + ai->ai_canonname || + ai->ai_addr || + ai->ai_next) { + ar->ar_gai_errno = EAI_BADHINTS; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_flags & ~AI_MASK || + (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { + ar->ar_gai_errno = EAI_BADFLAGS; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_family != PF_UNSPEC && + ai->ai_family != PF_INET && + ai->ai_family != PF_INET6) { + ar->ar_gai_errno = EAI_FAMILY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_socktype && + ai->ai_socktype != SOCK_DGRAM && + ai->ai_socktype != SOCK_STREAM && + ai->ai_socktype != SOCK_RAW) { + ar->ar_gai_errno = EAI_SOCKTYPE; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_socktype == SOCK_RAW && + get_port(as->as.ai.servname, NULL, 1) != 0) { + ar->ar_gai_errno = EAI_SERVICE; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Restrict result set to configured address families */ + if (ai->ai_flags & AI_ADDRCONFIG) { + if (addrconfig_setup(as) == -1) { + ar->ar_errno = errno; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + } + + /* Make sure there is at least a valid combination */ + for (i = 0; matches[i].family != -1; i++) + if (MATCH_FAMILY(ai->ai_family, i) && + MATCH_SOCKTYPE(ai->ai_socktype, i) && + MATCH_PROTO(ai->ai_protocol, i)) + break; + if (matches[i].family == -1) { + ar->ar_gai_errno = EAI_BADHINTS; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) + as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", + as->as.ai.hints.ai_flags & AI_NUMERICSERV); + if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) + as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", + as->as.ai.hints.ai_flags & AI_NUMERICSERV); + if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || + (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || + (ai->ai_protocol && (as->as.ai.port_udp == -1 || + as->as.ai.port_tcp == -1))) { + ar->ar_gai_errno = EAI_SERVICE; + async_set_state(as, ASR_STATE_HALT); + break; + } + + ar->ar_gai_errno = 0; + + if (!(ai->ai_flags & AI_NUMERICHOST)) + is_localhost = _asr_is_localhost(as->as.ai.hostname); + /* + * If hostname is NULL, "localhost" or falls within the + * ".localhost." domain, use local address. + * RFC 6761, 6.3: + * 3. Name resolution APIs and libraries SHOULD recognize + * localhost names as special and SHOULD always return the IP + * loopback address for address queries and negative responses + * for all other query types. Name resolution APIs SHOULD NOT + * send queries for localhost names to their configured caching + * DNS server(s). + */ + if (as->as.ai.hostname == NULL || is_localhost) { + for (family = iter_family(as, 1); + family != -1; + family = iter_family(as, 0)) { + /* + * We could use statically built sockaddrs for + * those, rather than parsing over and over. + */ + if (family == PF_INET) + str = (ai->ai_flags & AI_PASSIVE && + !is_localhost) ? "0.0.0.0" : + "127.0.0.1"; + else /* PF_INET6 */ + str = (ai->ai_flags & AI_PASSIVE && + !is_localhost) ? "::" : "::1"; + /* This can't fail */ + _asr_sockaddr_from_str(&sa.sa, family, str); + if ((r = addrinfo_add(as, &sa.sa, + "localhost."))) { + ar->ar_gai_errno = r; + break; + } + } + if (ar->ar_gai_errno == 0 && as->as_count == 0) { + ar->ar_gai_errno = EAI_NODATA; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Try numeric addresses first */ + for (family = iter_family(as, 1); + family != -1; + family = iter_family(as, 0)) { + + if (_asr_sockaddr_from_str(&sa.sa, family, + as->as.ai.hostname) == -1) + continue; + + if ((r = addrinfo_add(as, &sa.sa, NULL))) + ar->ar_gai_errno = r; + break; + } + if (ar->ar_gai_errno || as->as_count) { + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_flags & AI_NUMERICHOST) { + ar->ar_gai_errno = EAI_NONAME; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* make sure there are no funny characters in hostname */ + if (!hnok_lenient(as->as.ai.hostname)) { + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_NEXT_DB); + break; + + case ASR_STATE_NEXT_DB: + if (_asr_iter_db(as) == -1) { + async_set_state(as, ASR_STATE_NOT_FOUND); + break; + } + as->as_family_idx = 0; + async_set_state(as, ASR_STATE_SAME_DB); + break; + + case ASR_STATE_NEXT_FAMILY: + as->as_family_idx += 1; + if (as->as.ai.hints.ai_family != AF_UNSPEC || + AS_FAMILY(as) == -1) { + /* The family was specified, or we have tried all + * families with this DB. + */ + if (as->as_count) { + ar->ar_gai_errno = 0; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + } + async_set_state(as, ASR_STATE_SAME_DB); + break; + + case ASR_STATE_NEXT_DOMAIN: + /* domain search is only for dns */ + if (AS_DB(as) != ASR_DB_DNS) { + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + as->as_family_idx = 0; + + free(as->as.ai.fqdn); + as->as.ai.fqdn = NULL; + r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); + if (r == -1) { + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + if (r == 0) { + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + as->as.ai.fqdn = strdup(fqdn); + if (as->as.ai.fqdn == NULL) { + ar->ar_gai_errno = EAI_MEMORY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SAME_DB); + break; + + case ASR_STATE_SAME_DB: + /* query the current DB again */ + switch (AS_DB(as)) { + case ASR_DB_DNS: + if (as->as.ai.fqdn == NULL) { + /* First try, initialize domain iteration */ + as->as_dom_flags = 0; + as->as_dom_step = DOM_INIT; + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + } + + family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? + AS_FAMILY(as) : as->as.ai.hints.ai_family; + + if (family == AF_INET && + as->as_flags & ASYNC_NO_INET) { + async_set_state(as, ASR_STATE_NEXT_FAMILY); + break; + } else if (family == AF_INET6 && + as->as_flags & ASYNC_NO_INET6) { + async_set_state(as, ASR_STATE_NEXT_FAMILY); + break; + } + + as->as_subq = _res_query_async_ctx(as->as.ai.fqdn, + C_IN, (family == AF_INET6) ? T_AAAA : T_A, + as->as_ctx); + + if (as->as_subq == NULL) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_DB_FILE: + f = fopen(_PATH_HOSTS, "re"); + if (f == NULL) { + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? + AS_FAMILY(as) : as->as.ai.hints.ai_family; + + r = addrinfo_from_file(as, family, f); + if (r == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_FAMILY); + fclose(f); + break; + + default: + async_set_state(as, ASR_STATE_NEXT_DB); + } + break; + + case ASR_STATE_SUBQUERY: + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + as->as_subq = NULL; + + if (ar->ar_datalen == -1) { + async_set_state(as, ASR_STATE_NEXT_FAMILY); + break; + } + + r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); + if (r == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_FAMILY); + free(ar->ar_data); + break; + + case ASR_STATE_NOT_FOUND: + /* No result found. Maybe we can try again. */ + if (as->as_flags & ASYNC_AGAIN) + ar->ar_gai_errno = EAI_AGAIN; + else + ar->ar_gai_errno = EAI_NODATA; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + if (ar->ar_gai_errno == 0) { + ar->ar_count = as->as_count; + ar->ar_addrinfo = as->as.ai.aifirst; + as->as.ai.aifirst = NULL; + } else { + ar->ar_count = 0; + ar->ar_addrinfo = NULL; + } + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* + * Retrieve the port number for the service name "servname" and + * the protocol "proto". + */ +static int +get_port(const char *servname, const char *proto, int numonly) +{ +#ifdef HAVE_GETSERVBYNAME_R_4_ARGS + struct servent se; +#endif +#ifdef HAVE_STRUCT_SERVENT_DATA + struct servent_data sed; +#endif + int port; + const char *e; + + if (servname == NULL) + return (0); + + e = NULL; + port = strtonum(servname, 0, USHRT_MAX, &e); + if (e == NULL) + return (port); + if (errno == ERANGE) + return (-2); /* invalid */ + if (numonly) + return (-2); + + port = -1; +#ifdef HAVE_STRUCT_SERVENT_DATA + memset(&sed, 0, sizeof(sed)); +#endif +#ifdef HAVE_GETSERVBYNAME_R_4_ARGS + if (getservbyname_r(servname, proto, &se, &sed) != -1) + port = ntohs(se.s_port); +#endif +#ifdef HAVE_ENDSERVENT_R + endservent_r(&sed); +#endif + + return (port); +} + +/* + * Iterate over the address families that are to be queried. Use the + * list on the async context, unless a specific family was given in hints. + */ +static int +iter_family(struct asr_query *as, int first) +{ + if (first) { + as->as_family_idx = 0; + if (as->as.ai.hints.ai_family != PF_UNSPEC) + return as->as.ai.hints.ai_family; + return AS_FAMILY(as); + } + + if (as->as.ai.hints.ai_family != PF_UNSPEC) + return (-1); + + as->as_family_idx++; + + return AS_FAMILY(as); +} + +/* + * Use the sockaddr at "sa" to extend the result list on the "as" context, + * with the specified canonical name "cname". This function adds one + * entry per protocol/socktype match. + */ +static int +addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) +{ + struct addrinfo *ai; + int i, port, proto; + + for (i = 0; matches[i].family != -1; i++) { + if (matches[i].family != sa->sa_family || + !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || + !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) + continue; + + proto = as->as.ai.hints.ai_protocol; + if (!proto) + proto = matches[i].protocol; + + if (proto == IPPROTO_TCP) + port = as->as.ai.port_tcp; + else if (proto == IPPROTO_UDP) + port = as->as.ai.port_udp; + else + port = 0; + + /* servname specified, but not defined for this protocol */ + if (port == -1) + continue; + + ai = calloc(1, sizeof(*ai) + SA_LEN(sa)); + if (ai == NULL) + return (EAI_MEMORY); + ai->ai_family = sa->sa_family; + ai->ai_socktype = matches[i].socktype; + ai->ai_protocol = proto; + ai->ai_flags = as->as.ai.hints.ai_flags; + ai->ai_addrlen = SA_LEN(sa); + ai->ai_addr = (void *)(ai + 1); + if (cname && + as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { + if ((ai->ai_canonname = strdup(cname)) == NULL) { + free(ai); + return (EAI_MEMORY); + } + } + memmove(ai->ai_addr, sa, SA_LEN(sa)); + if (sa->sa_family == PF_INET) + ((struct sockaddr_in *)ai->ai_addr)->sin_port = + htons(port); + else if (sa->sa_family == PF_INET6) + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = + htons(port); + + if (as->as.ai.aifirst == NULL) + as->as.ai.aifirst = ai; + if (as->as.ai.ailast) + as->as.ai.ailast->ai_next = ai; + as->as.ai.ailast = ai; + as->as_count += 1; + } + + return (0); +} + +void +asr_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *ai_next; + + while (ai) { + ai_next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + free(ai); + ai = ai_next; + } +} + +static int +addrinfo_from_file(struct asr_query *as, int family, FILE *f) +{ + char *tokens[MAXTOKEN], *c, buf[ASR_BUFSIZ + 1]; + int n, i; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } u; + + for (;;) { + n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); + if (n == -1) + break; /* ignore errors reading the file */ + + for (i = 1; i < n; i++) { + if (strcasecmp(as->as.ai.hostname, tokens[i])) + continue; + if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) + continue; + break; + } + if (i == n) + continue; + + if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) + c = tokens[1]; + else + c = NULL; + + if (addrinfo_add(as, &u.sa, c)) + return (-1); /* errno set */ + } + return (0); +} + +static int +addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen) +{ + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + struct asr_dns_rr rr; + int i; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } u; + char buf[MAXDNAME], *c; + + _asr_unpack_init(&p, pkt, pktlen); + _asr_unpack_header(&p, &h); + for (; h.qdcount; h.qdcount--) + _asr_unpack_query(&p, &q); + + for (i = 0; i < h.ancount; i++) { + _asr_unpack_rr(&p, &rr); + if (rr.rr_type != q.q_type || + rr.rr_class != q.q_class) + continue; + + memset(&u, 0, sizeof u); + if (rr.rr_type == T_A) { +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + u.sain.sin_len = sizeof u.sain; +#endif + u.sain.sin_family = AF_INET; + u.sain.sin_addr = rr.rr.in_a.addr; + u.sain.sin_port = 0; + } else if (rr.rr_type == T_AAAA) { +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + u.sain6.sin6_len = sizeof u.sain6; +#endif + u.sain6.sin6_family = AF_INET6; + u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; + u.sain6.sin6_port = 0; + } else + continue; + + if (as->as.ai.hints.ai_flags & AI_CANONNAME) { + _asr_strdname(rr.rr_dname, buf, sizeof buf); + buf[strlen(buf) - 1] = '\0'; + c = res_hnok(buf) ? buf : NULL; + } else if (as->as.ai.hints.ai_flags & AI_FQDN) + c = as->as.ai.fqdn; + else + c = NULL; + + if (addrinfo_add(as, &u.sa, c)) + return (-1); /* errno set */ + } + return (0); +} + +static int +addrconfig_setup(struct asr_query *as) +{ + struct ifaddrs *ifa, *ifa0; + struct sockaddr_in *sinp; + struct sockaddr_in6 *sin6p; + + if (getifaddrs(&ifa0) == -1) + return (-1); + + as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6; + + for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + + switch (ifa->ifa_addr->sa_family) { + case PF_INET: + sinp = (struct sockaddr_in *)ifa->ifa_addr; + + if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) + continue; + + as->as_flags &= ~ASYNC_NO_INET; + break; + case PF_INET6: + sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; + + if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr)) + continue; + + if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)) + continue; + + as->as_flags &= ~ASYNC_NO_INET6; + break; + } + } + + freeifaddrs(ifa0); + + return (0); +} blob - /dev/null blob + 2fce46b37fdef84a1c82ec3c1ccf9edbe8019321 (mode 644) --- /dev/null +++ openbsd-compat/libasr/gethostnamadr.c @@ -0,0 +1,200 @@ +/* $OpenBSD: gethostnamadr.c,v 1.13 2015/09/14 07:38:37 guenther Exp $ */ +/* + * Copyright (c) 2012,2013 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* ALIGN */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int _gethostbyname(const char *, int, struct hostent *, char *, size_t, + int *); +static int _fillhostent(const struct hostent *, struct hostent *, char *, + size_t); + +static struct hostent _hostent; +static char _entbuf[4096]; + +static char *_empty[] = { NULL, }; + +static int +_fillhostent(const struct hostent *h, struct hostent *r, char *buf, size_t len) +{ + char **ptr, *end, *pos; + size_t n, i; + int naliases, naddrs; + + bzero(buf, len); + bzero(r, sizeof(*r)); + r->h_aliases = _empty; + r->h_addr_list = _empty; + + end = buf + len; + ptr = (char **)ALIGN(buf); + + if ((char *)ptr >= end) + return (ERANGE); + + for (naliases = 0; h->h_aliases[naliases]; naliases++) + ; + for (naddrs = 0; h->h_addr_list[naddrs]; naddrs++) + ; + + pos = (char *)(ptr + (naliases + 1) + (naddrs + 1)); + if (pos >= end) + return (ERANGE); + + r->h_name = NULL; + r->h_addrtype = h->h_addrtype; + r->h_length = h->h_length; + r->h_aliases = ptr; + r->h_addr_list = ptr + naliases + 1; + + n = strlcpy(pos, h->h_name, end - pos); + if (n >= end - pos) + return (ERANGE); + r->h_name = pos; + pos += n + 1; + + for (i = 0; i < naliases; i++) { + n = strlcpy(pos, h->h_aliases[i], end - pos); + if (n >= end - pos) + return (ERANGE); + r->h_aliases[i] = pos; + pos += n + 1; + } + + pos = (char *)ALIGN(pos); + if (pos >= end) + return (ERANGE); + + for (i = 0; i < naddrs; i++) { + if (r->h_length > end - pos) + return (ERANGE); + memmove(pos, h->h_addr_list[i], r->h_length); + r->h_addr_list[i] = pos; + pos += r->h_length; + } + + return (0); +} + +static int +_gethostbyname(const char *name, int af, struct hostent *ret, char *buf, + size_t buflen, int *h_errnop) +{ + struct asr_query *as; + struct asr_result ar; + int r; + + if (af == -1) + as = gethostbyname_async(name, NULL); + else + as = gethostbyname2_async(name, af, NULL); + + if (as == NULL) + return (errno); + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + *h_errnop = ar.ar_h_errno; + if (ar.ar_hostent == NULL) + return (0); + + r = _fillhostent(ar.ar_hostent, ret, buf, buflen); + free(ar.ar_hostent); + + return (r); +} + +struct hostent * +gethostbyname(const char *name) +{ + struct hostent *h; + + res_init(); + + if (_res.options & RES_USE_INET6 && + (h = gethostbyname2(name, AF_INET6))) + return (h); + + return gethostbyname2(name, AF_INET); +} +DEF_WEAK(gethostbyname); + +struct hostent * +gethostbyname2(const char *name, int af) +{ + int r; + + res_init(); + + r = _gethostbyname(name, af, &_hostent, _entbuf, sizeof(_entbuf), + &h_errno); + if (r) { + h_errno = NETDB_INTERNAL; + errno = r; + } + + if (h_errno) + return (NULL); + + return (&_hostent); +} +DEF_WEAK(gethostbyname2); + +struct hostent * +gethostbyaddr(const void *addr, socklen_t len, int af) +{ + struct asr_query *as; + struct asr_result ar; + int r; + + res_init(); + + as = gethostbyaddr_async(addr, len, af, NULL); + if (as == NULL) { + h_errno = NETDB_INTERNAL; + return (NULL); + } + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + if (ar.ar_hostent == NULL) + return (NULL); + + r = _fillhostent(ar.ar_hostent, &_hostent, _entbuf, sizeof(_entbuf)); + free(ar.ar_hostent); + + if (r) { + h_errno = NETDB_INTERNAL; + errno = r; + return (NULL); + } + + return (&_hostent); +} blob - /dev/null blob + caa62dcb70db097e0bc139762696d8704d467641 (mode 644) --- /dev/null +++ openbsd-compat/libasr/gethostnamadr_async.c @@ -0,0 +1,714 @@ +/* $OpenBSD: gethostnamadr_async.c,v 1.50 2024/09/03 18:20:35 op Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif +#include + +#include +#include +#include +#include /* for res_hnok */ +#include +#include +#include +#include + +#include "asr_private.h" + +#define MAXALIASES 35 +#define MAXADDRS 35 + +struct hostent_ext { + struct hostent h; + char *aliases[MAXALIASES + 1]; + char *addrs[MAXADDRS + 1]; + char *end; + char *pos; +}; + +struct netent_ext { + struct netent n; + char *aliases[MAXALIASES + 1]; + char *end; + char *pos; +}; + +static int gethostnamadr_async_run(struct asr_query *, struct asr_result *); +static struct hostent_ext *hostent_alloc(int); +static int hostent_set_cname(struct hostent_ext *, const char *, int); +static int hostent_add_alias(struct hostent_ext *, const char *, int); +static int hostent_add_addr(struct hostent_ext *, const void *, size_t); +static struct hostent_ext *hostent_from_addr(int, const char *, const char *); +static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *, + int); +static struct hostent_ext *hostent_from_packet(int, int, char *, size_t); +static void netent_from_hostent(struct asr_result *ar); + +struct asr_query * +gethostbyname_async(const char *name, void *asr) +{ + return gethostbyname2_async(name, AF_INET, asr); +} +DEF_WEAK(gethostbyname_async); + +struct asr_query * +gethostbyname2_async(const char *name, int af, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + /* the original segfaults */ + if (name == NULL) { + errno = EINVAL; + return (NULL); + } + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_GETHOSTBYNAME)) == NULL) + goto abort; /* errno set */ + as->as_run = gethostnamadr_async_run; + + as->as.hostnamadr.family = af; + if (af == AF_INET) + as->as.hostnamadr.addrlen = INADDRSZ; + else if (af == AF_INET6) + as->as.hostnamadr.addrlen = IN6ADDRSZ; + as->as.hostnamadr.name = strdup(name); + if (as->as.hostnamadr.name == NULL) + goto abort; /* errno set */ + + _asr_ctx_unref(ac); + return (as); + + abort: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(gethostbyname2_async); + +struct asr_query * +gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + ac = _asr_use_resolver(asr); + as = _gethostbyaddr_async_ctx(addr, len, af, ac); + _asr_ctx_unref(ac); + + return (as); +} +DEF_WEAK(gethostbyaddr_async); + +struct asr_query * +_gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af, + struct asr_ctx *ac) +{ + struct asr_query *as; + + if ((as = _asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL) + goto abort; /* errno set */ + as->as_run = gethostnamadr_async_run; + + as->as.hostnamadr.family = af; + as->as.hostnamadr.addrlen = len; + if (len > 0) + memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len); + + return (as); + + abort: + if (as) + _asr_async_free(as); + return (NULL); +} + +static int +gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar) +{ + struct hostent_ext *h; + int r, type, saved_errno; + FILE *f; + char name[MAXDNAME], *data, addr[16], *c; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + if (as->as.hostnamadr.family != AF_INET && + as->as.hostnamadr.family != AF_INET6) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = EAFNOSUPPORT; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if ((as->as.hostnamadr.family == AF_INET && + as->as.hostnamadr.addrlen != INADDRSZ) || + (as->as.hostnamadr.family == AF_INET6 && + as->as.hostnamadr.addrlen != IN6ADDRSZ)) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = EINVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as_type == ASR_GETHOSTBYNAME) { + + if (as->as.hostnamadr.name[0] == '\0') { + ar->ar_h_errno = NO_DATA; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Name might be an IP address string */ + for (c = as->as.hostnamadr.name; *c; c++) + if (!isdigit((unsigned char)*c) && + *c != '.' && *c != ':') + break; + if (*c == 0 && + inet_pton(as->as.hostnamadr.family, + as->as.hostnamadr.name, addr) == 1) { + h = hostent_from_addr(as->as.hostnamadr.family, + as->as.hostnamadr.name, addr); + if (h == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + } + else { + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (!hnok_lenient(as->as.hostnamadr.name)) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = EINVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* + * If hostname is "localhost" or falls within the + * ".localhost." domain, use local address. + * RFC 6761, 6.3: + * 3. Name resolution APIs and libraries SHOULD + * recognize localhost names as special and SHOULD + * always return the IP loopback address for address + * queries and negative responses for all other query + * types. Name resolution APIs SHOULD NOT send queries + * for localhost names to their configured caching DNS + * server(s). + */ + + if (_asr_is_localhost(as->as.hostnamadr.name)) { + inet_pton(as->as.hostnamadr.family, + as->as.hostnamadr.family == AF_INET ? + "127.0.0.1" : "::1", addr); + h = hostent_from_addr(as->as.hostnamadr.family, + as->as.hostnamadr.name, addr); + if (h == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + } + else { + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + } + async_set_state(as, ASR_STATE_NEXT_DB); + break; + + case ASR_STATE_NEXT_DB: + + if (_asr_iter_db(as) == -1) { + async_set_state(as, ASR_STATE_NOT_FOUND); + break; + } + + switch (AS_DB(as)) { + + case ASR_DB_DNS: + + /* Create a subquery to do the DNS lookup */ + + if (as->as_type == ASR_GETHOSTBYNAME) { + type = (as->as.hostnamadr.family == AF_INET) ? + T_A : T_AAAA; + as->as_subq = _res_search_async_ctx( + as->as.hostnamadr.name, + C_IN, type, as->as_ctx); + } else { + _asr_addr_as_fqdn(as->as.hostnamadr.addr, + as->as.hostnamadr.family, + name, sizeof(name)); + as->as_subq = _res_query_async_ctx( + name, C_IN, T_PTR, as->as_ctx); + } + + if (as->as_subq == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_DB_FILE: + + /* Try to find a match in the host file */ + + if ((f = fopen(_PATH_HOSTS, "re")) == NULL) + break; + + if (as->as_type == ASR_GETHOSTBYNAME) + data = as->as.hostnamadr.name; + else + data = as->as.hostnamadr.addr; + + h = hostent_file_match(f, as->as_type, + as->as.hostnamadr.family, data, + as->as.hostnamadr.addrlen); + saved_errno = errno; + fclose(f); + errno = saved_errno; + + if (h == NULL) { + if (errno) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + } + /* otherwise not found */ + break; + } + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + async_set_state(as, ASR_STATE_HALT); + break; + } + break; + + case ASR_STATE_SUBQUERY: + + /* Run the DNS subquery. */ + + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + /* Done. */ + as->as_subq = NULL; + + /* + * We either got no packet or a packet without an answer. + * Safeguard the h_errno and use the next DB. + */ + if (ar->ar_count == 0) { + free(ar->ar_data); + as->as.hostnamadr.subq_h_errno = ar->ar_h_errno; + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + + /* Read the hostent from the packet. */ + + h = hostent_from_packet(as->as_type, + as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen); + free(ar->ar_data); + if (h == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as_type == ASR_GETHOSTBYADDR) { + if (hostent_add_addr(h, as->as.hostnamadr.addr, + as->as.hostnamadr.addrlen) == -1) { + free(h); + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + } + + /* + * No valid hostname or address found in the dns packet. + * Ignore it. + */ + if ((as->as_type == ASR_GETHOSTBYNAME && + h->h.h_addr_list[0] == NULL) || + h->h.h_name == NULL) { + free(h); + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_NOT_FOUND: + ar->ar_errno = 0; + if (as->as.hostnamadr.subq_h_errno) + ar->ar_h_errno = as->as.hostnamadr.subq_h_errno; + else + ar->ar_h_errno = HOST_NOT_FOUND; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + if (ar->ar_h_errno == NETDB_SUCCESS && + as->as_flags & ASYNC_GETNET) + netent_from_hostent(ar); + if (ar->ar_h_errno) { + ar->ar_hostent = NULL; + ar->ar_netent = NULL; + } else + ar->ar_errno = 0; + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* + * Create a hostent from a numeric address string. + */ +static struct hostent_ext * +hostent_from_addr(int family, const char *name, const char *addr) +{ + struct hostent_ext *h; + + if ((h = hostent_alloc(family)) == NULL) + return (NULL); + if (hostent_set_cname(h, name, 0) == -1) + goto fail; + if (hostent_add_addr(h, addr, h->h.h_length) == -1) + goto fail; + return (h); +fail: + free(h); + return (NULL); +} + +/* + * Lookup the first matching entry in the hostfile, either by address or by + * name depending on reqtype, and build a hostent from the line. + */ +static struct hostent_ext * +hostent_file_match(FILE *f, int reqtype, int family, const char *data, + int datalen) +{ + char *tokens[MAXTOKEN], addr[16], buf[ASR_BUFSIZ + 1]; + struct hostent_ext *h; + int n, i; + + for (;;) { + n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); + if (n == -1) { + errno = 0; /* ignore errors reading the file */ + return (NULL); + } + + /* there must be an address and at least one name */ + if (n < 2) + continue; + + if (reqtype == ASR_GETHOSTBYNAME) { + for (i = 1; i < n; i++) { + if (strcasecmp(data, tokens[i])) + continue; + if (inet_pton(family, tokens[0], addr) == 1) + goto found; + } + } else { + if (inet_pton(family, tokens[0], addr) == 1 && + memcmp(addr, data, datalen) == 0) + goto found; + } + } + +found: + if ((h = hostent_alloc(family)) == NULL) + return (NULL); + if (hostent_set_cname(h, tokens[1], 0) == -1) + goto fail; + for (i = 2; i < n; i ++) + if (hostent_add_alias(h, tokens[i], 0) == -1) + goto fail; + if (hostent_add_addr(h, addr, h->h.h_length) == -1) + goto fail; + return (h); +fail: + free(h); + return (NULL); +} + +/* + * Fill the hostent from the given DNS packet. + */ +static struct hostent_ext * +hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen) +{ + struct hostent_ext *h; + struct asr_unpack p; + struct asr_dns_header hdr; + struct asr_dns_query q; + struct asr_dns_rr rr; + char dname[MAXDNAME]; + + if ((h = hostent_alloc(family)) == NULL) + return (NULL); + + _asr_unpack_init(&p, pkt, pktlen); + _asr_unpack_header(&p, &hdr); + for (; hdr.qdcount; hdr.qdcount--) + _asr_unpack_query(&p, &q); + strlcpy(dname, q.q_dname, sizeof(dname)); + + for (; hdr.ancount; hdr.ancount--) { + _asr_unpack_rr(&p, &rr); + if (rr.rr_class != C_IN) + continue; + switch (rr.rr_type) { + + case T_CNAME: + if (reqtype == ASR_GETHOSTBYNAME) { + if (hostent_add_alias(h, rr.rr_dname, 1) == -1) + goto fail; + } else { + if (strcasecmp(rr.rr_dname, dname) == 0) + strlcpy(dname, rr.rr.cname.cname, + sizeof(dname)); + } + break; + + case T_PTR: + if (reqtype != ASR_GETHOSTBYADDR) + break; + if (strcasecmp(rr.rr_dname, dname) != 0) + continue; + if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1) + hostent_add_alias(h, rr.rr.ptr.ptrname, 1); + break; + + case T_A: + if (reqtype != ASR_GETHOSTBYNAME) + break; + if (family != AF_INET) + break; + if (hostent_set_cname(h, rr.rr_dname, 1) == -1) + ; + if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1) + goto fail; + break; + + case T_AAAA: + if (reqtype != ASR_GETHOSTBYNAME) + break; + if (family != AF_INET6) + break; + if (hostent_set_cname(h, rr.rr_dname, 1) == -1) + ; + if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1) + goto fail; + break; + } + } + + return (h); +fail: + free(h); + return (NULL); +} + +static struct hostent_ext * +hostent_alloc(int family) +{ + struct hostent_ext *h; + size_t alloc; + + alloc = sizeof(*h) + 1024; + if ((h = calloc(1, alloc)) == NULL) + return (NULL); + + h->h.h_addrtype = family; + h->h.h_length = (family == AF_INET) ? 4 : 16; + h->h.h_aliases = h->aliases; + h->h.h_addr_list = h->addrs; + h->pos = (char *)(h) + sizeof(*h); + h->end = h->pos + 1024; + + return (h); +} + +static int +hostent_set_cname(struct hostent_ext *h, const char *name, int isdname) +{ + char buf[MAXDNAME]; + size_t n; + + if (h->h.h_name) + return (-1); + + if (isdname) { + _asr_strdname(name, buf, sizeof buf); + buf[strlen(buf) - 1] = '\0'; + if (!res_hnok(buf)) + return (-1); + name = buf; + } + + n = strlen(name) + 1; + if (h->pos + n >= h->end) + return (-1); + + h->h.h_name = h->pos; + memmove(h->pos, name, n); + h->pos += n; + return (0); +} + +static int +hostent_add_alias(struct hostent_ext *h, const char *name, int isdname) +{ + char buf[MAXDNAME]; + size_t i, n; + + for (i = 0; i < MAXALIASES; i++) + if (h->aliases[i] == NULL) + break; + if (i == MAXALIASES) + return (0); + + if (isdname) { + _asr_strdname(name, buf, sizeof buf); + buf[strlen(buf)-1] = '\0'; + if (!res_hnok(buf)) + return (-1); + name = buf; + } + + n = strlen(name) + 1; + if (h->pos + n >= h->end) + return (0); + + h->aliases[i] = h->pos; + memmove(h->pos, name, n); + h->pos += n; + return (0); +} + +static int +hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size) +{ + int i; + + for (i = 0; i < MAXADDRS; i++) + if (h->addrs[i] == NULL) + break; + if (i == MAXADDRS) + return (0); + + if (h->pos + size >= h->end) + return (0); + + h->addrs[i] = h->pos; + memmove(h->pos, addr, size); + h->pos += size; + return (0); +} + +static void +netent_from_hostent(struct asr_result *ar) +{ + struct in_addr *addr; + struct netent_ext *n; + struct hostent_ext *h; + char **na, **ha; + size_t sz; + + /* Allocate and initialize the output. */ + if ((n = calloc(1, sizeof(*n) + 1024)) == NULL) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = errno; + goto out; + } + n->pos = (char *)(n) + sizeof(*n); + n->end = n->pos + 1024; + n->n.n_name = n->pos; + n->n.n_aliases = n->aliases; + + /* Copy the fixed-size data. */ + h = (struct hostent_ext *)ar->ar_hostent; + addr = (struct in_addr *)h->h.h_addr; + n->n.n_net = ntohl(addr->s_addr); + n->n.n_addrtype = h->h.h_addrtype; + + /* Copy the network name. */ + sz = strlen(h->h.h_name) + 1; + memcpy(n->pos, h->h.h_name, sz); + n->pos += sz; + + /* + * Copy the aliases. + * No overflow check is needed because we are merely copying + * a part of the data from a structure of the same size. + */ + na = n->aliases; + for (ha = h->aliases; *ha != NULL; ha++) { + sz = strlen(*ha) + 1; + memcpy(n->pos, *ha, sz); + *na++ = n->pos; + n->pos += sz; + } + *na = NULL; + + /* Handle the return values. */ + ar->ar_netent = &n->n; +out: + free(ar->ar_hostent); + ar->ar_hostent = NULL; +} blob - /dev/null blob + 72564cb8d574fecf19c5fb470f85f817ea045302 (mode 644) --- /dev/null +++ openbsd-compat/libasr/getnameinfo.c @@ -0,0 +1,206 @@ +/* $OpenBSD: getnameinfo.c,v 1.11 2022/12/27 17:10:06 jmc Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static size_t asr_print_addr(const struct sockaddr *, char *, size_t); +static size_t asr_print_port(const struct sockaddr *, const char *, char *, size_t); + +#define SA_IN(sa) ((struct sockaddr_in*)(sa)) +#define SA_IN6(sa) ((struct sockaddr_in6*)(sa)) + +/* + * Print the textual representation (as given by inet_ntop(3)) of the address + * set in "sa". + * + * Return the total length of the string it tried to create or 0 if an error + * occurred, in which case errno is set. On success, the constructed string + * is guaranteed to be NUL-terminated. Overflow must be detected by checking + * the returned size against buflen. + * + */ +static size_t +asr_print_addr(const struct sockaddr *sa, char *buf, size_t buflen) +{ + unsigned int ifidx; + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + char scope[IF_NAMESIZE + 1], *ifname; + const void *addr; + size_t s; + + switch(sa->sa_family) { + case AF_INET: + addr = &SA_IN(sa)->sin_addr; + break; + case AF_INET6: + addr = &SA_IN6(sa)->sin6_addr; + break; + default: + errno = EINVAL; + return (0); + } + + if (inet_ntop(sa->sa_family, addr, tmp, sizeof(tmp)) == NULL) + return (0); /* errno set */ + + s = strlcpy(buf, tmp, buflen); + + if (sa->sa_family == AF_INET6 && SA_IN6(sa)->sin6_scope_id) { + + scope[0] = SCOPE_DELIMITER; + scope[1] = '\0'; + + ifidx = SA_IN6(sa)->sin6_scope_id; + ifname = NULL; + + if (IN6_IS_ADDR_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) || + IN6_IS_ADDR_MC_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) || + IN6_IS_ADDR_MC_INTFACELOCAL(&(SA_IN6(sa)->sin6_addr))) + ifname = if_indextoname(ifidx, scope + 1); + + if (ifname == NULL) + (void)snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); + + if (s < buflen) + (void)strlcat(buf, scope, buflen); + + s += strlen(scope); + } + + return (s); +} + +/* + * Print the textual representation of the port set on "sa". + * + * If proto is not NULL, it is used as parameter to "getservbyport_r(3)" to + * return a service name. If it's not set, or if no matching service is found, + * it prints the portno. + * + * Return the total length of the string it tried to create or 0 if an error + * occurred, in which case errno is set. On success, the constructed string + * is guaranteed to be NUL-terminated. Overflow must be detected by checking + * the returned size against buflen. + */ +static size_t +asr_print_port(const struct sockaddr *sa, const char *proto, char *buf, size_t buflen) +{ + struct servent s; + struct servent_data sd; + int port, r, saved_errno; + size_t n; + + switch(sa->sa_family) { + case AF_INET: + port = SA_IN(sa)->sin_port; + break; + case AF_INET6: + port = SA_IN6(sa)->sin6_port; + break; + default: + errno = EINVAL; + return (0); + } + + if (proto) { + memset(&sd, 0, sizeof (sd)); + saved_errno = errno; + r = getservbyport_r(port, proto, &s, &sd); + if (r == 0) + n = strlcpy(buf, s.s_name, buflen); + endservent_r(&sd); + errno = saved_errno; + if (r == 0) + return (n); + } + + r = snprintf(buf, buflen, "%u", ntohs(port)); + if (r < 0 || r >= buflen) /* Actually, this can not happen */ + return (0); + + return (r); +} + +int +getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct asr_query *as; + struct asr_result ar; + int saved_errno = errno; + const char *proto; + size_t r; + + /* + * Take a shortcut if we don't care about hostname, + * or if NI_NUMERICHOST is set. + */ + if (host == NULL || hostlen == 0 || + (host && hostlen && (flags & NI_NUMERICHOST))) { + if (host) { + r = asr_print_addr(sa, host, hostlen); + if (r == 0) + return (EAI_SYSTEM); /* errno set */ + if (r >= hostlen) + return (EAI_OVERFLOW); + } + + if (serv && servlen) { + if (flags & NI_NUMERICSERV) + proto = NULL; + else + proto = (flags & NI_DGRAM) ? "udp" : "tcp"; + r = asr_print_port(sa, proto, serv, servlen); + if (r == 0) + return (EAI_SYSTEM); /* errno set */ + if (r >= servlen) + return (EAI_OVERFLOW); + } + + errno = saved_errno; + return (0); + } + + res_init(); + + as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags, + NULL); + if (as == NULL) { + if (errno == ENOMEM) { + errno = saved_errno; + return (EAI_MEMORY); + } + return (EAI_SYSTEM); + } + + asr_run_sync(as, &ar); + if (ar.ar_gai_errno == EAI_SYSTEM) + errno = ar.ar_errno; + + return (ar.ar_gai_errno); +} +DEF_WEAK(getnameinfo); blob - /dev/null blob + be07c0f12f8a94abcc8facc491a910c9ac754d69 (mode 644) --- /dev/null +++ openbsd-compat/libasr/getnameinfo_async.c @@ -0,0 +1,302 @@ +/* $OpenBSD: getnameinfo_async.c,v 1.15 2020/12/21 09:40:35 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "asr_private.h" + +static int getnameinfo_async_run(struct asr_query *, struct asr_result *); +static int _servname(struct asr_query *); +static int _numerichost(struct asr_query *); + +struct asr_query * +getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_GETNAMEINFO)) == NULL) + goto abort; /* errno set */ + as->as_run = getnameinfo_async_run; + + if (sa->sa_family == AF_INET) + memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain)); + else if (sa->sa_family == AF_INET6) + memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6)); + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + as->as.ni.sa.sa.sa_len = slen; +#endif + as->as.ni.hostname = host; + as->as.ni.hostnamelen = hostlen; + as->as.ni.servname = serv; + as->as.ni.servnamelen = servlen; + as->as.ni.flags = flags; + + _asr_ctx_unref(ac); + return (as); + + abort: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(getnameinfo_async); + +static int +getnameinfo_async_run(struct asr_query *as, struct asr_result *ar) +{ + void *addr; + socklen_t addrlen; + int r; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + /* Make sure the parameters are all valid. */ + + if (as->as.ni.sa.sa.sa_family != AF_INET && + as->as.ni.sa.sa.sa_family != AF_INET6) { + ar->ar_gai_errno = EAI_FAMILY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if ((as->as.ni.sa.sa.sa_family == AF_INET && + (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain))) || + (as->as.ni.sa.sa.sa_family == AF_INET6 && + (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain6)))) { + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Set the service name first, if needed. */ + if (_servname(as) == -1) { + ar->ar_gai_errno = EAI_OVERFLOW; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) { + ar->ar_gai_errno = 0; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ni.flags & NI_NUMERICHOST) { + if (_numerichost(as) == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else if (errno == ENOSPC) + ar->ar_gai_errno = EAI_OVERFLOW; + else { + ar->ar_errno = errno; + ar->ar_gai_errno = EAI_SYSTEM; + } + } else + ar->ar_gai_errno = 0; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ni.sa.sa.sa_family == AF_INET) { + addrlen = sizeof(as->as.ni.sa.sain.sin_addr); + addr = &as->as.ni.sa.sain.sin_addr; + } else { + addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr); + addr = &as->as.ni.sa.sain6.sin6_addr; + } + + /* + * Create a subquery to lookup the address. + */ + as->as_subq = _gethostbyaddr_async_ctx(addr, addrlen, + as->as.ni.sa.sa.sa_family, + as->as_ctx); + if (as->as_subq == NULL) { + ar->ar_gai_errno = EAI_MEMORY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_STATE_SUBQUERY: + + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + /* + * Request done. + */ + as->as_subq = NULL; + + if (ar->ar_hostent == NULL) { + if (as->as.ni.flags & NI_NAMEREQD) { + ar->ar_gai_errno = EAI_NONAME; + } else if (_numerichost(as) == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else if (errno == ENOSPC) + ar->ar_gai_errno = EAI_OVERFLOW; + else { + ar->ar_errno = errno; + ar->ar_gai_errno = EAI_SYSTEM; + } + } else + ar->ar_gai_errno = 0; + } else { + if (strlcpy(as->as.ni.hostname, + ar->ar_hostent->h_name, + as->as.ni.hostnamelen) >= as->as.ni.hostnamelen) + ar->ar_gai_errno = EAI_OVERFLOW; + else + ar->ar_gai_errno = 0; + free(ar->ar_hostent); + } + + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + + +/* + * Set the service name on the result buffer is not NULL. + * return (-1) if the buffer is too small. + */ +static int +_servname(struct asr_query *as) +{ + struct servent s; +#ifdef HAVE_STRUCT_SERVENT_DATA + struct servent_data sd; +#endif + int port, r; + char *buf = as->as.ni.servname; + size_t n, buflen = as->as.ni.servnamelen; + + if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0) + return (0); + + if (as->as.ni.sa.sa.sa_family == AF_INET) + port = as->as.ni.sa.sain.sin_port; + else + port = as->as.ni.sa.sain6.sin6_port; + + if (!(as->as.ni.flags & NI_NUMERICSERV)) { +#ifdef HAVE_STRUCT_SERVENT_DATA + memset(&sd, 0, sizeof (sd)); +#endif +#ifdef HAVE_GETSERVBYPORT_R_4_ARGS + r = getservbyport_r(port, (as->as.ni.flags & NI_DGRAM) ? + "udp" : "tcp", &s, &sd); + if (r == 0) + n = strlcpy(buf, s.s_name, buflen); +#else + r = -1; +#endif +#ifdef HAVE_ENDSERVENT_R + endservent_r(&sd); +#endif + if (r == 0) { + if (n >= buflen) + return (-1); + return (0); + } + } + + r = snprintf(buf, buflen, "%u", ntohs(port)); + if (r < 0 || (size_t)r >= buflen) + return (-1); + + return (0); +} + +/* + * Write the numeric address + */ +static int +_numerichost(struct asr_query *as) +{ + unsigned int ifidx; + char scope[IF_NAMESIZE + 1], *ifname; + void *addr; + char *buf = as->as.ni.hostname; + size_t buflen = as->as.ni.hostnamelen; + + if (as->as.ni.sa.sa.sa_family == AF_INET) + addr = &as->as.ni.sa.sain.sin_addr; + else + addr = &as->as.ni.sa.sain6.sin6_addr; + + if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL) + return (-1); /* errno set */ + + if (as->as.ni.sa.sa.sa_family == AF_INET6 && + as->as.ni.sa.sain6.sin6_scope_id) { + + scope[0] = SCOPE_DELIMITER; + scope[1] = '\0'; + + ifidx = as->as.ni.sa.sain6.sin6_scope_id; + ifname = NULL; + + if (IN6_IS_ADDR_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || + IN6_IS_ADDR_MC_INTFACELOCAL(&as->as.ni.sa.sain6.sin6_addr)) + ifname = if_indextoname(ifidx, scope + 1); + + if (ifname == NULL) + snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); + + strlcat(buf, scope, buflen); + } + + return (0); +} blob - /dev/null blob + 141b4a9ab8fa6c639942b2a863ca16877f8e15a4 (mode 644) --- /dev/null +++ openbsd-compat/libasr/getnetnamadr.c @@ -0,0 +1,134 @@ +/* $OpenBSD: getnetnamadr.c,v 1.9 2015/01/16 16:48:51 deraadt Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include /* ALIGN */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static void _fillnetent(const struct netent *, struct netent *, char *buf, + size_t); + +static struct netent _netent; +static char _entbuf[4096]; + +static char *_empty[] = { NULL, }; + +static void +_fillnetent(const struct netent *e, struct netent *r, char *buf, size_t len) +{ + char **ptr, *end, *pos; + size_t n, i; + int naliases; + + bzero(buf, len); + bzero(r, sizeof(*r)); + r->n_aliases = _empty; + + end = buf + len; + ptr = (char **)ALIGN(buf); + + if ((char *)ptr >= end) + return; + + for (naliases = 0; e->n_aliases[naliases]; naliases++) + ; + + r->n_name = NULL; + r->n_addrtype = e->n_addrtype; + r->n_net = e->n_net; + r->n_aliases = ptr; + + pos = (char *)(ptr + (naliases + 1)); + if (pos > end) + r->n_aliases = _empty; + + n = strlcpy(pos, e->n_name, end - pos); + if (n >= end - pos) + return; + r->n_name = pos; + pos += n + 1; + + for (i = 0; i < naliases; i++) { + n = strlcpy(pos, e->n_aliases[i], end - pos); + if (n >= end - pos) + return; + r->n_aliases[i] = pos; + pos += n + 1; + } +} + +struct netent * +getnetbyname(const char *name) +{ + struct asr_query *as; + struct asr_result ar; + + res_init(); + + as = getnetbyname_async(name, NULL); + if (as == NULL) { + h_errno = NETDB_INTERNAL; + return (NULL); + } + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + if (ar.ar_netent == NULL) + return (NULL); + + _fillnetent(ar.ar_netent, &_netent, _entbuf, sizeof(_entbuf)); + free(ar.ar_netent); + + return (&_netent); +} + +struct netent * +getnetbyaddr(in_addr_t net, int type) +{ + struct asr_query *as; + struct asr_result ar; + + res_init(); + + as = getnetbyaddr_async(net, type, NULL); + if (as == NULL) { + h_errno = NETDB_INTERNAL; + return (NULL); + } + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + if (ar.ar_netent == NULL) + return (NULL); + + _fillnetent(ar.ar_netent, &_netent, _entbuf, sizeof(_entbuf)); + free(ar.ar_netent); + + return (&_netent); +} blob - /dev/null blob + 1e02d0082861eb2bd864b376eea127902feab6ef (mode 644) --- /dev/null +++ openbsd-compat/libasr/getnetnamadr_async.c @@ -0,0 +1,52 @@ +/* $OpenBSD: getnetnamadr_async.c,v 1.26 2018/04/28 15:16:49 schwarze Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include "asr_private.h" + +struct asr_query * +getnetbyname_async(const char *name, void *asr) +{ + struct asr_query *as; + + if ((as = gethostbyname_async(name, asr)) != NULL) + as->as_flags |= ASYNC_GETNET; + return (as); +} +DEF_WEAK(getnetbyname_async); + +struct asr_query * +getnetbyaddr_async(in_addr_t net, int family, void *asr) +{ + struct in_addr in; + struct asr_query *as; + + in.s_addr = htonl(net); + as = gethostbyaddr_async(&in, sizeof(in), family, asr); + if (as != NULL) + as->as_flags |= ASYNC_GETNET; + return (as); +} +DEF_WEAK(getnetbyaddr_async); blob - /dev/null blob + 24df2c8bfa03cf6ac7926454216cfacda9e72613 (mode 644) --- /dev/null +++ openbsd-compat/libasr/getrrsetbyname.c @@ -0,0 +1,83 @@ +/* $OpenBSD: getrrsetbyname.c,v 1.6 2015/09/14 07:38:37 guenther Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +int +getrrsetbyname(const char *name, unsigned int class, unsigned int type, + unsigned int flags, struct rrsetinfo **res) +{ + struct asr_query *as; + struct asr_result ar; + int r, saved_errno = errno; + + res_init(); + + as = getrrsetbyname_async(name, class, type, flags, NULL); + if (as == NULL) { + r = (errno == ENOMEM) ? ERRSET_NOMEMORY : ERRSET_FAIL; + errno = saved_errno; + return (r); + } + + asr_run_sync(as, &ar); + + *res = ar.ar_rrsetinfo; + + return (ar.ar_rrset_errno); +} + +/* from net/getrrsetbyname.c */ +void +freerrset(struct rrsetinfo *rrset) +{ + u_int16_t i; + + if (rrset == NULL) + return; + + if (rrset->rri_rdatas) { + for (i = 0; i < rrset->rri_nrdatas; i++) { + if (rrset->rri_rdatas[i].rdi_data == NULL) + break; + free(rrset->rri_rdatas[i].rdi_data); + } + free(rrset->rri_rdatas); + } + + if (rrset->rri_sigs) { + for (i = 0; i < rrset->rri_nsigs; i++) { + if (rrset->rri_sigs[i].rdi_data == NULL) + break; + free(rrset->rri_sigs[i].rdi_data); + } + free(rrset->rri_sigs); + } + + if (rrset->rri_name) + free(rrset->rri_name); + free(rrset); +} +DEF_WEAK(freerrset); blob - /dev/null blob + 494855b7e63e050c69aadbc47fa66f90c3b86956 (mode 644) --- /dev/null +++ openbsd-compat/libasr/getrrsetbyname_async.c @@ -0,0 +1,621 @@ +/* $OpenBSD: getrrsetbyname_async.c,v 1.13 2023/03/15 22:12:00 millert Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "asr_private.h" + +static int getrrsetbyname_async_run(struct asr_query *, struct asr_result *); +static void get_response(struct asr_result *, const char *, int); + +struct asr_query * +getrrsetbyname_async(const char *hostname, unsigned int rdclass, + unsigned int rdtype, unsigned int flags, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_GETRRSETBYNAME)) == NULL) + goto abort; /* errno set */ + as->as_run = getrrsetbyname_async_run; + + as->as.rrset.flags = flags; + as->as.rrset.class = rdclass; + as->as.rrset.type = rdtype; + as->as.rrset.name = strdup(hostname); + if (as->as.rrset.name == NULL) + goto abort; /* errno set */ + + _asr_ctx_unref(ac); + return (as); + abort: + if (as) + _asr_async_free(as); + + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(getrrsetbyname_async); + +static int +getrrsetbyname_async_run(struct asr_query *as, struct asr_result *ar) +{ + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + /* Check for invalid class and type. */ + if (as->as.rrset.class > 0xffff || as->as.rrset.type > 0xffff) { + ar->ar_rrset_errno = ERRSET_INVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Do not allow queries of class or type ANY. */ + if (as->as.rrset.class == 0xff || as->as.rrset.type == 0xff) { + ar->ar_rrset_errno = ERRSET_INVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Do not allow flags yet, unimplemented. */ + if (as->as.rrset.flags) { + ar->ar_rrset_errno = ERRSET_INVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Create a delegate the lookup to a subquery. */ + as->as_subq = _res_query_async_ctx( + as->as.rrset.name, + as->as.rrset.class, + as->as.rrset.type, + as->as_ctx); + if (as->as_subq == NULL) { + ar->ar_rrset_errno = ERRSET_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_STATE_SUBQUERY: + + if ((asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + as->as_subq = NULL; + + /* No packet received.*/ + if (ar->ar_datalen == -1) { + switch (ar->ar_h_errno) { + case HOST_NOT_FOUND: + ar->ar_rrset_errno = ERRSET_NONAME; + break; + case NO_DATA: + ar->ar_rrset_errno = ERRSET_NODATA; + break; + default: + ar->ar_rrset_errno = ERRSET_FAIL; + break; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Got a packet but no answer. */ + if (ar->ar_count == 0) { + free(ar->ar_data); + switch (ar->ar_rcode) { + case NXDOMAIN: + ar->ar_rrset_errno = ERRSET_NONAME; + break; + case NOERROR: + ar->ar_rrset_errno = ERRSET_NODATA; + break; + default: + ar->ar_rrset_errno = ERRSET_FAIL; + break; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + + get_response(ar, ar->ar_data, ar->ar_datalen); + free(ar->ar_data); + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + if (ar->ar_rrset_errno) + ar->ar_rrsetinfo = NULL; + return (ASYNC_DONE); + + default: + ar->ar_rrset_errno = ERRSET_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* The rest of this file is taken from the original implementation. */ + +/* $OpenBSD: getrrsetbyname_async.c,v 1.13 2023/03/15 22:12:00 millert Exp $ */ + +/* + * Copyright (c) 2001 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define MAXPACKET 1024*64 + +struct dns_query { + char *name; + u_int16_t type; + u_int16_t class; + struct dns_query *next; +}; + +struct dns_rr { + char *name; + u_int16_t type; + u_int16_t class; + u_int16_t ttl; + u_int16_t size; + void *rdata; + struct dns_rr *next; +}; + +struct dns_response { + HEADER header; + struct dns_query *query; + struct dns_rr *answer; + struct dns_rr *authority; + struct dns_rr *additional; +}; + +static struct dns_response *parse_dns_response(const u_char *, int); +static struct dns_query *parse_dns_qsection(const u_char *, int, + const u_char **, int); +static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **, + int); + +static void free_dns_query(struct dns_query *); +static void free_dns_rr(struct dns_rr *); +static void free_dns_response(struct dns_response *); + +static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t); + +static void +get_response(struct asr_result *ar, const char *pkt, int pktlen) +{ + struct rrsetinfo *rrset = NULL; + struct dns_response *response = NULL; + struct dns_rr *rr; + struct rdatainfo *rdata; + unsigned int index_ans, index_sig; + + /* parse result */ + response = parse_dns_response(pkt, pktlen); + if (response == NULL) { + ar->ar_rrset_errno = ERRSET_FAIL; + goto fail; + } + + if (response->header.qdcount != 1) { + ar->ar_rrset_errno = ERRSET_FAIL; + goto fail; + } + + /* initialize rrset */ + rrset = calloc(1, sizeof(struct rrsetinfo)); + if (rrset == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + rrset->rri_rdclass = response->query->class; + rrset->rri_rdtype = response->query->type; + rrset->rri_ttl = response->answer->ttl; + rrset->rri_nrdatas = response->header.ancount; + + /* check for authenticated data */ + if (response->header.ad == 1) + rrset->rri_flags |= RRSET_VALIDATED; + + /* copy name from answer section */ + rrset->rri_name = strdup(response->answer->name); + if (rrset->rri_name == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + + /* count answers */ + rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass, + rrset->rri_rdtype); + rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass, + T_RRSIG); + + /* allocate memory for answers */ + rrset->rri_rdatas = calloc(rrset->rri_nrdatas, + sizeof(struct rdatainfo)); + if (rrset->rri_rdatas == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + + /* allocate memory for signatures */ + rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo)); + if (rrset->rri_sigs == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + + /* copy answers & signatures */ + for (rr = response->answer, index_ans = 0, index_sig = 0; + rr; rr = rr->next) { + + rdata = NULL; + + if (rr->class == rrset->rri_rdclass && + rr->type == rrset->rri_rdtype) + rdata = &rrset->rri_rdatas[index_ans++]; + + if (rr->class == rrset->rri_rdclass && + rr->type == T_RRSIG) + rdata = &rrset->rri_sigs[index_sig++]; + + if (rdata) { + rdata->rdi_length = rr->size; + rdata->rdi_data = malloc(rr->size); + + if (rdata->rdi_data == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + memcpy(rdata->rdi_data, rr->rdata, rr->size); + } + } + free_dns_response(response); + + ar->ar_rrsetinfo = rrset; + ar->ar_rrset_errno = ERRSET_SUCCESS; + return; + +fail: + if (rrset != NULL) + freerrset(rrset); + if (response != NULL) + free_dns_response(response); +} + +/* + * DNS response parsing routines + */ +static struct dns_response * +parse_dns_response(const u_char *answer, int size) +{ + struct dns_response *resp; + const u_char *cp; + + if (size <= HFIXEDSZ) + return (NULL); + + /* allocate memory for the response */ + resp = calloc(1, sizeof(*resp)); + if (resp == NULL) + return (NULL); + + /* initialize current pointer */ + cp = answer; + + /* copy header */ + memcpy(&resp->header, cp, HFIXEDSZ); + cp += HFIXEDSZ; + + /* fix header byte order */ + resp->header.qdcount = ntohs(resp->header.qdcount); + resp->header.ancount = ntohs(resp->header.ancount); + resp->header.nscount = ntohs(resp->header.nscount); + resp->header.arcount = ntohs(resp->header.arcount); + + /* there must be at least one query */ + if (resp->header.qdcount < 1) { + free_dns_response(resp); + return (NULL); + } + + /* parse query section */ + resp->query = parse_dns_qsection(answer, size, &cp, + resp->header.qdcount); + if (resp->header.qdcount && resp->query == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse answer section */ + resp->answer = parse_dns_rrsection(answer, size, &cp, + resp->header.ancount); + if (resp->header.ancount && resp->answer == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse authority section */ + resp->authority = parse_dns_rrsection(answer, size, &cp, + resp->header.nscount); + if (resp->header.nscount && resp->authority == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse additional section */ + resp->additional = parse_dns_rrsection(answer, size, &cp, + resp->header.arcount); + if (resp->header.arcount && resp->additional == NULL) { + free_dns_response(resp); + return (NULL); + } + + return (resp); +} + +static struct dns_query * +parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count) +{ + struct dns_query *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + +#define NEED(need) \ + do { \ + if (*cp + need > answer + size) \ + goto fail; \ + } while (0) + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + if (*cp >= answer + size) { + fail: + free_dns_query(head); + return (NULL); + } + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_query)); + if (curr == NULL) + goto fail; + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_query(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_query(head); + return (NULL); + } + NEED(length); + *cp += length; + + /* type */ + NEED(INT16SZ); + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + NEED(INT16SZ); + curr->class = _getshort(*cp); + *cp += INT16SZ; + } +#undef NEED + + return (head); +} + +static struct dns_rr * +parse_dns_rrsection(const u_char *answer, int size, const u_char **cp, + int count) +{ + struct dns_rr *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + +#define NEED(need) \ + do { \ + if (*cp + need > answer + size) \ + goto fail; \ + } while (0) + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + if (*cp >= answer + size) { + fail: + free_dns_rr(head); + return (NULL); + } + + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_rr)); + if (curr == NULL) + goto fail; + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_rr(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_rr(head); + return (NULL); + } + NEED(length); + *cp += length; + + /* type */ + NEED(INT16SZ); + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + NEED(INT16SZ); + curr->class = _getshort(*cp); + *cp += INT16SZ; + + /* ttl */ + NEED(INT32SZ); + curr->ttl = _getlong(*cp); + *cp += INT32SZ; + + /* rdata size */ + NEED(INT16SZ); + curr->size = _getshort(*cp); + *cp += INT16SZ; + + /* rdata itself */ + NEED(curr->size); + curr->rdata = malloc(curr->size); + if (curr->rdata == NULL) { + free_dns_rr(head); + return (NULL); + } + memcpy(curr->rdata, *cp, curr->size); + *cp += curr->size; + } +#undef NEED + + return (head); +} + +static void +free_dns_query(struct dns_query *p) +{ + if (p == NULL) + return; + + if (p->name) + free(p->name); + free_dns_query(p->next); + free(p); +} + +static void +free_dns_rr(struct dns_rr *p) +{ + if (p == NULL) + return; + + if (p->name) + free(p->name); + if (p->rdata) + free(p->rdata); + free_dns_rr(p->next); + free(p); +} + +static void +free_dns_response(struct dns_response *p) +{ + if (p == NULL) + return; + + free_dns_query(p->query); + free_dns_rr(p->answer); + free_dns_rr(p->authority); + free_dns_rr(p->additional); + free(p); +} + +static int +count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type) +{ + int n = 0; + + while (p) { + if (p->class == class && p->type == type) + n++; + p = p->next; + } + + return (n); +} blob - /dev/null blob + 71346e8abb339823cdf79992c302fedac9c3a0a5 (mode 644) --- /dev/null +++ openbsd-compat/libasr/libasr.la @@ -0,0 +1,41 @@ +# libasr.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libasr.0.dylib' + +# Names of this library. +library_names='libasr.0.dylib libasr.dylib' + +# The name of the static archive. +old_library='libasr.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags=' ' + +# Libraries that this one depends upon. +dependency_libs=' -L/usr/local/Cellar/openssl@1.1/1.1.1d//lib -L/usr/local/lib -lz -lcrypto -lssl -levent -lresolv' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libasr. +current=0 +age=0 +revision=3 + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/tmp/lib' blob - /dev/null blob + ca9c5ee0f1a2fce370d3cffbf343d102d96a3129 (mode 644) --- /dev/null +++ openbsd-compat/libasr/res_debug.c @@ -0,0 +1,2 @@ +/* $OpenBSD: res_debug.c,v 1.1 2012/09/08 11:08:21 eric Exp $ */ +/* NOTHING */ blob - /dev/null blob + 04243c47bef132e21171db2f15e33d590ec1457f (mode 644) --- /dev/null +++ openbsd-compat/libasr/res_init.c @@ -0,0 +1,103 @@ +/* $OpenBSD: res_init.c,v 1.11 2019/06/17 05:54:45 otto Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "asr_private.h" +#include "thread_private.h" + + +struct __res_state _res; +struct __res_state_ext _res_ext; + +int h_errno; + +int +res_init(void) +{ + static void *resinit_mutex; + struct asr_ctx *ac; + int i; + + ac = _asr_use_resolver(NULL); + + /* + * The first thread to call res_init() will setup the global _res + * structure from the async context, not overriding fields set early + * by the user. + */ + _MUTEX_LOCK(&resinit_mutex); + if (!(_res.options & RES_INIT)) { + if (_res.retry == 0) + _res.retry = ac->ac_nsretries; + if (_res.retrans == 0) + _res.retrans = ac->ac_nstimeout; + if (_res.options == 0) + _res.options = ac->ac_options; + if (_res.lookups[0] == '\0') + strlcpy(_res.lookups, ac->ac_db, sizeof(_res.lookups)); + + for (i = 0; i < ac->ac_nscount && i < MAXNS; i++) { + /* + * No need to check for length since we copy to a + * struct sockaddr_storage with a size of 256 bytes + * and sa_len has only 8 bits. + */ + memcpy(&_res_ext.nsaddr_list[i], ac->ac_ns[i], + ac->ac_ns[i]->sa_len); + if (ac->ac_ns[i]->sa_len <= sizeof(_res.nsaddr_list[i])) + memcpy(&_res.nsaddr_list[i], ac->ac_ns[i], + ac->ac_ns[i]->sa_len); + else + memset(&_res.nsaddr_list[i], 0, + sizeof(_res.nsaddr_list[i])); + } + _res.nscount = i; + _res.options |= RES_INIT; + } + _MUTEX_UNLOCK(&resinit_mutex); + + /* + * If the program is not threaded, we want to reflect (some) changes + * made by the user to the global _res structure. + * This is a bit of a hack: if there is already an async query on + * this context, it might change things in its back. It is ok + * as long as the user only uses the blocking resolver API. + * If needed we could consider cloning the context if there is + * a running query. + */ + if (!__isthreaded) { + ac->ac_nsretries = _res.retry; + ac->ac_nstimeout = _res.retrans; + ac->ac_options = _res.options; + strlcpy(ac->ac_db, _res.lookups, sizeof(ac->ac_db)); + ac->ac_dbcount = strlen(ac->ac_db); + } + + _asr_ctx_unref(ac); + + return (0); +} +DEF_WEAK(res_init); blob - /dev/null blob + bc69203c28c8be6bd26c7f6a005367a5b66a4559 (mode 644) --- /dev/null +++ openbsd-compat/libasr/res_mkquery.c @@ -0,0 +1,123 @@ +/* $OpenBSD: res_mkquery.c,v 1.14 2021/11/22 20:18:27 jca Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include /* for MAXDNAME */ +#include + +#include +#include +#include +#include + +#include "asr_private.h" + +/* This function is apparently needed by some ports. */ +int +res_mkquery(int op, const char *dname, int class, int type, + const unsigned char *data, int datalen, const unsigned char *newrr, + unsigned char *buf, int buflen) +{ + struct asr_ctx *ac; + struct asr_pack p; + struct asr_dns_header h; + char fqdn[MAXDNAME]; + char dn[MAXDNAME]; + + /* we currently only support QUERY */ + if (op != QUERY || data) + return (-1); + + if (dname[0] == '\0' || dname[strlen(dname) - 1] != '.') { + if (strlcpy(fqdn, dname, sizeof(fqdn)) >= sizeof(fqdn) || + strlcat(fqdn, ".", sizeof(fqdn)) >= sizeof(fqdn)) + return (-1); + dname = fqdn; + } + + if (_asr_dname_from_fqdn(dname, dn, sizeof(dn)) == -1) + return (-1); + + ac = _asr_use_resolver(NULL); + + memset(&h, 0, sizeof h); + h.id = res_randomid(); + if (ac->ac_options & RES_RECURSE) + h.flags |= RD_MASK; +#ifdef RES_USE_CD + if (ac->ac_options & RES_USE_CD) + h.flags |= CD_MASK; +#endif +#ifdef RES_TRUSTAD + if (ac->ac_options & RES_TRUSTAD) + h.flags |= AD_MASK; +#endif + h.qdcount = 1; + if (ac->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + h.arcount = 1; + + _asr_pack_init(&p, buf, buflen); + _asr_pack_header(&p, &h); + _asr_pack_query(&p, type, class, dn); + if (ac->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + _asr_pack_edns0(&p, MAXPACKETSZ, + ac->ac_options & RES_USE_DNSSEC); + + _asr_ctx_unref(ac); + + if (p.err) + return (-1); + + return (p.offset); +} + +/* + * This function is not documented, but used by sendmail. + * Put here because it uses asr_private.h too. + */ +int +res_querydomain(const char *name, + const char *domain, + int class, + int type, + u_char *answer, + int anslen) +{ + char fqdn[MAXDNAME], ndom[MAXDNAME]; + size_t n; + + /* we really want domain to end with a dot for now */ + if (domain && ((n = strlen(domain)) == 0 || domain[n - 1 ] != '.')) { + if (strlcpy(ndom, domain, sizeof(ndom)) >= sizeof(ndom) || + strlcat(ndom, ".", sizeof(ndom)) >= sizeof(ndom)) { + h_errno = NETDB_INTERNAL; + errno = EINVAL; + return (-1); + } + domain = ndom; + } + + if (_asr_make_fqdn(name, domain, fqdn, sizeof fqdn) == 0) { + h_errno = NETDB_INTERNAL; + errno = EINVAL; + return (-1); + } + + return (res_query(fqdn, class, type, answer, anslen)); +} blob - /dev/null blob + 3f8914163873f519d60f7e2d80b325a4981b5017 (mode 644) --- /dev/null +++ openbsd-compat/libasr/res_query.c @@ -0,0 +1,112 @@ +/* $OpenBSD: res_query.c,v 1.9 2015/10/05 02:57:16 guenther Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int +res_query(const char *name, int class, int type, u_char *ans, int anslen) +{ + struct asr_query *as; + struct asr_result ar; + size_t len; + + res_init(); + + if (ans == NULL || anslen <= 0) { + h_errno = NO_RECOVERY; + errno = EINVAL; + return (-1); + } + + as = res_query_async(name, class, type, NULL); + if (as == NULL) { + if (errno == EINVAL) + h_errno = NO_RECOVERY; + else + h_errno = NETDB_INTERNAL; + return (-1); /* errno set */ + } + + asr_run_sync(as, &ar); + + if (ar.ar_errno) + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + + if (ar.ar_h_errno != NETDB_SUCCESS) + return (-1); + + len = anslen; + if (ar.ar_datalen < len) + len = ar.ar_datalen; + memmove(ans, ar.ar_data, len); + free(ar.ar_data); + + return (ar.ar_datalen); +} +DEF_WEAK(res_query); + +int +res_search(const char *name, int class, int type, u_char *ans, int anslen) +{ + struct asr_query *as; + struct asr_result ar; + size_t len; + + res_init(); + + if (ans == NULL || anslen <= 0) { + h_errno = NO_RECOVERY; + errno = EINVAL; + return (-1); + } + + as = res_search_async(name, class, type, NULL); + if (as == NULL) { + if (errno == EINVAL) + h_errno = NO_RECOVERY; + else + h_errno = NETDB_INTERNAL; + return (-1); /* errno set */ + } + + asr_run_sync(as, &ar); + + if (ar.ar_errno) + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + + if (ar.ar_h_errno != NETDB_SUCCESS) + return (-1); + + len = anslen; + if (ar.ar_datalen < len) + len = ar.ar_datalen; + memmove(ans, ar.ar_data, len); + free(ar.ar_data); + + return (ar.ar_datalen); +} blob - /dev/null blob + 3430f9578f50e74310bab02d72177f81896c5224 (mode 644) --- /dev/null +++ openbsd-compat/libasr/res_search_async.c @@ -0,0 +1,327 @@ +/* $OpenBSD: res_search_async.c,v 1.21 2017/02/27 10:44:46 jca Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "openbsd-compat.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "asr_private.h" + +static int res_search_async_run(struct asr_query *, struct asr_result *); +static size_t domcat(const char *, const char *, char *, size_t); + +/* + * Unlike res_query_async(), this function returns a valid packet only if + * h_errno is NETDB_SUCCESS. + */ +struct asr_query * +res_search_async(const char *name, int class, int type, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type); + + ac = _asr_use_resolver(asr); + as = _res_search_async_ctx(name, class, type, ac); + _asr_ctx_unref(ac); + + return (as); +} +DEF_WEAK(res_search_async); + +struct asr_query * +_res_search_async_ctx(const char *name, int class, int type, struct asr_ctx *ac) +{ + struct asr_query *as; + + DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class, + type); + + if ((as = _asr_async_new(ac, ASR_SEARCH)) == NULL) + goto err; /* errno set */ + as->as_run = res_search_async_run; + if ((as->as.search.name = strdup(name)) == NULL) + goto err; /* errno set */ + + as->as.search.class = class; + as->as.search.type = type; + + return (as); + err: + if (as) + _asr_async_free(as); + return (NULL); +} + +#define HERRNO_UNSET -2 + +static int +res_search_async_run(struct asr_query *as, struct asr_result *ar) +{ + int r; + char fqdn[MAXDNAME]; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + if (as->as.search.name[0] == '\0') { + ar->ar_h_errno = NO_DATA; + async_set_state(as, ASR_STATE_HALT); + break; + } + + as->as.search.saved_h_errno = HERRNO_UNSET; + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + + case ASR_STATE_NEXT_DOMAIN: + /* + * Reset flags to be able to identify the case in + * STATE_SUBQUERY. + */ + as->as_dom_flags = 0; + + r = _asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn)); + if (r == -1) { + async_set_state(as, ASR_STATE_NOT_FOUND); + break; + } + if (r == 0) { + ar->ar_errno = EINVAL; + ar->ar_h_errno = NO_RECOVERY; + ar->ar_datalen = -1; + ar->ar_data = NULL; + async_set_state(as, ASR_STATE_HALT); + break; + } + as->as_subq = _res_query_async_ctx(fqdn, + as->as.search.class, as->as.search.type, as->as_ctx); + if (as->as_subq == NULL) { + ar->ar_errno = errno; + if (errno == EINVAL) + ar->ar_h_errno = NO_RECOVERY; + else + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_datalen = -1; + ar->ar_data = NULL; + async_set_state(as, ASR_STATE_HALT); + break; + } + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_STATE_SUBQUERY: + + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + as->as_subq = NULL; + + if (ar->ar_h_errno == NETDB_SUCCESS) { + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* + * The original res_search() does this in the domain search + * loop, but only for ECONNREFUSED. I think we can do better + * because technically if we get an errno, it means + * we couldn't reach any nameserver, so there is no point + * in trying further. + */ + if (ar->ar_errno) { + async_set_state(as, ASR_STATE_HALT); + break; + } + + free(ar->ar_data); + + /* + * The original resolver does something like this. + */ + if (as->as_dom_flags & ASYNC_DOM_NDOTS) + as->as.search.saved_h_errno = ar->ar_h_errno; + + if (as->as_dom_flags & ASYNC_DOM_DOMAIN) { + if (ar->ar_h_errno == NO_DATA) + as->as_flags |= ASYNC_NODATA; + else if (ar->ar_h_errno == TRY_AGAIN) + as->as_flags |= ASYNC_AGAIN; + } + + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + + case ASR_STATE_NOT_FOUND: + + if (as->as.search.saved_h_errno != HERRNO_UNSET) + ar->ar_h_errno = as->as.search.saved_h_errno; + else if (as->as_flags & ASYNC_NODATA) + ar->ar_h_errno = NO_DATA; + else if (as->as_flags & ASYNC_AGAIN) + ar->ar_h_errno = TRY_AGAIN; + /* + * Else, we got the ar_h_errno value set by res_query_async() + * for the last domain. + */ + ar->ar_datalen = -1; + ar->ar_data = NULL; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* + * Concatenate a name and a domain name. The result has no trailing dot. + * Return the resulting string length, or 0 in case of error. + */ +static size_t +domcat(const char *name, const char *domain, char *buf, size_t buflen) +{ + size_t r; + + r = _asr_make_fqdn(name, domain, buf, buflen); + if (r == 0) + return (0); + buf[r - 1] = '\0'; + + return (r - 1); +} + +enum { + DOM_INIT, + DOM_DOMAIN, + DOM_DONE +}; + +/* + * Implement the search domain strategy. + * + * This function works as a generator that constructs complete domains in + * buffer "buf" of size "len" for the given host name "name", according to the + * search rules defined by the resolving context. It is supposed to be called + * multiple times (with the same name) to generate the next possible domain + * name, if any. + * + * It returns -1 if all possibilities have been exhausted, 0 if there was an + * error generating the next name, or the resulting name length. + */ +int +_asr_iter_domain(struct asr_query *as, const char *name, char * buf, size_t len) +{ + const char *c; + int dots; + + switch (as->as_dom_step) { + + case DOM_INIT: + /* First call */ + + /* + * If "name" is an FQDN, that's the only result and we + * don't try anything else. + */ + if (strlen(name) && name[strlen(name) - 1] == '.') { + DPRINT("asr: iter_domain(\"%s\") fqdn\n", name); + as->as_dom_flags |= ASYNC_DOM_FQDN; + as->as_dom_step = DOM_DONE; + return (domcat(name, NULL, buf, len)); + } + + /* + * Otherwise, we iterate through the specified search domains. + */ + as->as_dom_step = DOM_DOMAIN; + as->as_dom_idx = 0; + + /* + * If "name" as enough dots, use it as-is first, as indicated + * in resolv.conf(5). + */ + dots = 0; + for (c = name; *c; c++) + dots += (*c == '.'); + if (dots >= as->as_ctx->ac_ndots) { + DPRINT("asr: iter_domain(\"%s\") ndots\n", name); + as->as_dom_flags |= ASYNC_DOM_NDOTS; + if (strlcpy(buf, name, len) >= len) + return (0); + return (strlen(buf)); + } + /* Otherwise, starts using the search domains */ + /* FALLTHROUGH */ + + case DOM_DOMAIN: + if (as->as_dom_idx < as->as_ctx->ac_domcount && + (as->as_ctx->ac_options & RES_DNSRCH || ( + as->as_ctx->ac_options & RES_DEFNAMES && + as->as_dom_idx == 0 && + strchr(name, '.') == NULL))) { + DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n", + name, as->as_ctx->ac_dom[as->as_dom_idx]); + as->as_dom_flags |= ASYNC_DOM_DOMAIN; + return (domcat(name, + as->as_ctx->ac_dom[as->as_dom_idx++], buf, len)); + } + + /* No more domain to try. */ + + as->as_dom_step = DOM_DONE; + + /* + * If the name was not tried as an absolute name before, + * do it now. + */ + if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) { + DPRINT("asr: iter_domain(\"%s\") as is\n", name); + as->as_dom_flags |= ASYNC_DOM_ASIS; + if (strlcpy(buf, name, len) >= len) + return (0); + return (strlen(buf)); + } + /* Otherwise, we are done. */ + + case DOM_DONE: + default: + DPRINT("asr: iter_domain(\"%s\") done\n", name); + return (-1); + } +} blob - /dev/null blob + 32c940815afeaffc8626888c49dd5b21a3d6e7fb (mode 644) --- /dev/null +++ openbsd-compat/libasr/res_send.c @@ -0,0 +1,61 @@ +/* $OpenBSD: res_send.c,v 1.8 2014/03/26 18:13:15 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int +res_send(const u_char *buf, int buflen, u_char *ans, int anslen) +{ + struct asr_query *as; + struct asr_result ar; + size_t len; + + res_init(); + + if (ans == NULL || anslen <= 0) { + errno = EINVAL; + return (-1); + } + + as = res_send_async(buf, buflen, NULL); + if (as == NULL) + return (-1); /* errno set */ + + asr_run_sync(as, &ar); + + if (ar.ar_errno) { + errno = ar.ar_errno; + return (-1); + } + + len = anslen; + if (ar.ar_datalen < len) + len = ar.ar_datalen; + memmove(ans, ar.ar_data, len); + free(ar.ar_data); + + return (ar.ar_datalen); +} blob - /dev/null blob + 63fc231f247211a58d08f5e5ee12b47f2877e4c6 (mode 644) --- /dev/null +++ openbsd-compat/libasr/res_send_async.c @@ -0,0 +1,836 @@ +/* $OpenBSD: res_send_async.c,v 1.41 2022/06/20 06:45:31 jca Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "openbsd-compat.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif +#include + +#include +#include +#include +#include +#include /* for res_random */ +#include +#include +#include + +#include "asr_private.h" + +#define OP_QUERY (0) + +static int res_send_async_run(struct asr_query *, struct asr_result *); +static int sockaddr_connect(const struct sockaddr *, int); +static int udp_send(struct asr_query *); +static int udp_recv(struct asr_query *); +static int tcp_write(struct asr_query *); +static int tcp_read(struct asr_query *); +static int validate_packet(struct asr_query *); +#ifdef RES_TRUSTAD +static void clear_ad(struct asr_result *); +#endif +static int setup_query(struct asr_query *, const char *, const char *, int, int); +static int ensure_ibuf(struct asr_query *, size_t); +static int iter_ns(struct asr_query *); + +#define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1]) + + +struct asr_query * +res_send_async(const unsigned char *buf, int buflen, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + + DPRINT_PACKET("asr: res_send_async()", buf, buflen); + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) { + _asr_ctx_unref(ac); + return (NULL); /* errno set */ + } + as->as_run = res_send_async_run; + + as->as_flags |= ASYNC_EXTOBUF; + as->as.dns.obuf = (unsigned char *)buf; + as->as.dns.obuflen = buflen; + as->as.dns.obufsize = buflen; + + _asr_unpack_init(&p, buf, buflen); + _asr_unpack_header(&p, &h); + _asr_unpack_query(&p, &q); + if (p.err) { + errno = EINVAL; + goto err; + } + as->as.dns.reqid = h.id; + as->as.dns.type = q.q_type; + as->as.dns.class = q.q_class; + as->as.dns.dname = strdup(q.q_dname); + if (as->as.dns.dname == NULL) + goto err; /* errno set */ + + _asr_ctx_unref(ac); + return (as); + err: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(res_send_async); + +/* + * Unlike res_query(), this version will actually return the packet + * if it has received a valid one (errno == 0) even if h_errno is + * not NETDB_SUCCESS. So the packet *must* be freed if necessary. + */ +struct asr_query * +res_query_async(const char *name, int class, int type, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + DPRINT("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type); + + ac = _asr_use_resolver(asr); + as = _res_query_async_ctx(name, class, type, ac); + _asr_ctx_unref(ac); + + return (as); +} +DEF_WEAK(res_query_async); + +struct asr_query * +_res_query_async_ctx(const char *name, int class, int type, struct asr_ctx *a_ctx) +{ + struct asr_query *as; + + DPRINT("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class, type); + + if ((as = _asr_async_new(a_ctx, ASR_SEND)) == NULL) + return (NULL); /* errno set */ + as->as_run = res_send_async_run; + + /* This adds a "." to name if it doesn't already have one. + * That's how res_query() behaves (through res_mkquery). + */ + if (setup_query(as, name, NULL, class, type) == -1) + goto err; /* errno set */ + + return (as); + + err: + if (as) + _asr_async_free(as); + + return (NULL); +} + +static int +res_send_async_run(struct asr_query *as, struct asr_result *ar) +{ + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + if (as->as_ctx->ac_nscount == 0) { + ar->ar_errno = ECONNREFUSED; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_NEXT_NS); + break; + + case ASR_STATE_NEXT_NS: + + if (iter_ns(as) == -1) { + ar->ar_errno = ETIMEDOUT; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as_ctx->ac_options & RES_USEVC || + as->as.dns.obuflen > PACKETSZ) + async_set_state(as, ASR_STATE_TCP_WRITE); + else + async_set_state(as, ASR_STATE_UDP_SEND); + break; + + case ASR_STATE_UDP_SEND: + + if (udp_send(as) == -1) { + async_set_state(as, ASR_STATE_NEXT_NS); + break; + } + async_set_state(as, ASR_STATE_UDP_RECV); + ar->ar_cond = ASR_WANT_READ; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + break; + + case ASR_STATE_UDP_RECV: + + if (udp_recv(as) == -1) { + if (errno == ENOMEM) { + ar->ar_errno = errno; + async_set_state(as, ASR_STATE_HALT); + break; + } + if (errno != EOVERFLOW) { + /* Fail or timeout */ + async_set_state(as, ASR_STATE_NEXT_NS); + break; + } + if (as->as_ctx->ac_options & RES_IGNTC) + async_set_state(as, ASR_STATE_PACKET); + else + async_set_state(as, ASR_STATE_TCP_WRITE); + } else + async_set_state(as, ASR_STATE_PACKET); + break; + + case ASR_STATE_TCP_WRITE: + + switch (tcp_write(as)) { + case -1: /* fail or timeout */ + async_set_state(as, ASR_STATE_NEXT_NS); + break; + case 0: + async_set_state(as, ASR_STATE_TCP_READ); + ar->ar_cond = ASR_WANT_READ; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + case 1: + ar->ar_cond = ASR_WANT_WRITE; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + } + break; + + case ASR_STATE_TCP_READ: + + switch (tcp_read(as)) { + case -1: /* Fail or timeout */ + if (errno == ENOMEM) { + ar->ar_errno = errno; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_NS); + break; + case 0: + async_set_state(as, ASR_STATE_PACKET); + break; + case 1: + ar->ar_cond = ASR_WANT_READ; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + } + break; + + case ASR_STATE_PACKET: + + memmove(&ar->ar_ns, AS_NS_SA(as), SA_LEN(AS_NS_SA(as))); + ar->ar_datalen = as->as.dns.ibuflen; + ar->ar_data = as->as.dns.ibuf; + as->as.dns.ibuf = NULL; + ar->ar_errno = 0; + ar->ar_rcode = as->as.dns.rcode; +#ifdef RES_TRUSTAD + if (!(as->as_ctx->ac_options & RES_TRUSTAD)) + clear_ad(ar); +#endif + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + + if (ar->ar_errno) { + ar->ar_h_errno = TRY_AGAIN; + ar->ar_count = 0; + ar->ar_datalen = -1; + ar->ar_data = NULL; + } else if (as->as.dns.ancount) { + ar->ar_h_errno = NETDB_SUCCESS; + ar->ar_count = as->as.dns.ancount; + } else { + ar->ar_count = 0; + switch (as->as.dns.rcode) { + case NXDOMAIN: + ar->ar_h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + ar->ar_h_errno = TRY_AGAIN; + break; + case NOERROR: + ar->ar_h_errno = NO_DATA; + break; + default: + ar->ar_h_errno = NO_RECOVERY; + } + } + return (ASYNC_DONE); + + default: + + ar->ar_errno = EOPNOTSUPP; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +static int +sockaddr_connect(const struct sockaddr *sa, int socktype) +{ + int errno_save, sock, flags; + + if ((sock = socket(sa->sa_family, + socktype | SOCK_DNS, 0)) == -1) + goto fail; + + if ((flags = fcntl(sock, F_GETFL, 0)) == -1) + goto fail; + + flags |= O_NONBLOCK; + + if ((flags = fcntl(sock, F_SETFL, flags)) == -1) + goto fail; + + if (connect(sock, sa, SA_LEN(sa)) == -1) { + /* + * In the TCP case, the caller will be asked to poll for + * POLLOUT so that we start writing the packet in tcp_write() + * when the connection is established, or fail there on error. + */ + if (errno == EINPROGRESS) + return (sock); + goto fail; + } + + return (sock); + + fail: + + if (sock != -1) { + errno_save = errno; + close(sock); + errno = errno_save; + } + + return (-1); +} + +/* + * Prepare the DNS packet for the query type "type", class "class" and domain + * name created by the concatenation on "name" and "dom". + * Return 0 on success, set errno and return -1 on error. + */ +static int +setup_query(struct asr_query *as, const char *name, const char *dom, + int class, int type) +{ + struct asr_pack p; + struct asr_dns_header h; + char fqdn[MAXDNAME]; + char dname[MAXDNAME]; + + if (as->as_flags & ASYNC_EXTOBUF) { + errno = EINVAL; + DPRINT("attempting to write in user packet"); + return (-1); + } + + if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) { + errno = EINVAL; + DPRINT("asr_make_fqdn: name too long\n"); + return (-1); + } + + if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) { + errno = EINVAL; + DPRINT("asr_dname_from_fqdn: invalid\n"); + return (-1); + } + + if (as->as.dns.obuf == NULL) { + as->as.dns.obufsize = PACKETSZ; + as->as.dns.obuf = malloc(as->as.dns.obufsize); + if (as->as.dns.obuf == NULL) + return (-1); /* errno set */ + } + as->as.dns.obuflen = 0; + + memset(&h, 0, sizeof h); + h.id = res_randomid(); + if (as->as_ctx->ac_options & RES_RECURSE) + h.flags |= RD_MASK; +#ifdef RES_USE_CD + if (as->as_ctx->ac_options & RES_USE_CD) + h.flags |= CD_MASK; +#endif +#ifdef RES_TRUSTAD + if (as->as_ctx->ac_options & RES_TRUSTAD) + h.flags |= AD_MASK; +#endif + + h.qdcount = 1; + if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + h.arcount = 1; + + _asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize); + _asr_pack_header(&p, &h); + _asr_pack_query(&p, type, class, dname); + if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + _asr_pack_edns0(&p, MAXPACKETSZ, + as->as_ctx->ac_options & RES_USE_DNSSEC); + if (p.err) { + DPRINT("error packing query"); + errno = EINVAL; + return (-1); + } + + /* Remember the parameters. */ + as->as.dns.reqid = h.id; + as->as.dns.type = type; + as->as.dns.class = class; + if (as->as.dns.dname) + free(as->as.dns.dname); + as->as.dns.dname = strdup(dname); + if (as->as.dns.dname == NULL) { + DPRINT("strdup"); + return (-1); /* errno set */ + } + as->as.dns.obuflen = p.offset; + + DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen); + + return (0); +} + +/* + * Create a connect UDP socket and send the output packet. + * + * Return 0 on success, or -1 on error (errno set). + */ +static int +udp_send(struct asr_query *as) +{ + ssize_t n; + int save_errno; +#ifdef DEBUG + char buf[256]; +#endif + + DPRINT("asr: [%p] connecting to %s UDP\n", as, + _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); + + as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM); + if (as->as_fd == -1) + return (-1); /* errno set */ + + n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0); + if (n == -1) { + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + return (-1); + } + + return (0); +} + +/* + * Try to receive a valid packet from the current UDP socket. + * + * Return 0 if a full packet could be read, or -1 on error (errno set). + */ +static int +udp_recv(struct asr_query *as) +{ + ssize_t n; + int save_errno; + + if (ensure_ibuf(as, MAXPACKETSZ) == -1) { + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + return (-1); + } + + n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0); + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + if (n == -1) + return (-1); + + as->as.dns.ibuflen = n; + + DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen); + + if (validate_packet(as) == -1) + return (-1); /* errno set */ + + return (0); +} + +/* + * Write the output packet to the TCP socket. + * + * Return 0 when all bytes have been sent, 1 there is no buffer space on the + * socket or it is not connected yet, or -1 on error (errno set). + */ +static int +tcp_write(struct asr_query *as) +{ + struct msghdr msg; + struct iovec iov[2]; + uint16_t len; + ssize_t n; + size_t offset; + int i; +#ifdef DEBUG + char buf[256]; +#endif + + /* First try to connect if not already */ + if (as->as_fd == -1) { + DPRINT("asr: [%p] connecting to %s TCP\n", as, + _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); + as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM); + if (as->as_fd == -1) + return (-1); /* errno set */ +/* + * Some systems (MacOS X) have SO_NOSIGPIPE instead of MSG_NOSIGNAL. + * If neither is available the system is probably broken. We might + * want to detect this at configure time. + */ +#ifdef SO_NOSIGPIPE + i = 1; + if (setsockopt(as->as_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&i, + sizeof(i)) == -1) + return (-1); /* errno set */ +#endif + as->as.dns.datalen = 0; /* bytes sent */ + return (1); + } + + i = 0; + + /* Prepend de packet length if not sent already. */ + if (as->as.dns.datalen < sizeof(len)) { + offset = 0; + len = htons(as->as.dns.obuflen); + iov[i].iov_base = (char *)(&len) + as->as.dns.datalen; + iov[i].iov_len = sizeof(len) - as->as.dns.datalen; + i++; + } else + offset = as->as.dns.datalen - sizeof(len); + + iov[i].iov_base = as->as.dns.obuf + offset; + iov[i].iov_len = as->as.dns.obuflen - offset; + i++; + + memset(&msg, 0, sizeof msg); + msg.msg_iov = iov; + msg.msg_iovlen = i; + + send_again: +/* See above. */ +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL); + if (n == -1) { + if (errno == EINTR) + goto send_again; + goto close; /* errno set */ + } + + as->as.dns.datalen += n; + + if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) { + /* All sent. Prepare for TCP read */ + as->as.dns.datalen = 0; + return (0); + } + + /* More data to write */ + return (1); + +close: + close(as->as_fd); + as->as_fd = -1; + return (-1); +} + +/* + * Try to read a valid packet from the current TCP socket. + * + * Return 0 if a full packet could be read, 1 if more data is needed and the + * socket must be read again, or -1 on error (errno set). + */ +static int +tcp_read(struct asr_query *as) +{ + ssize_t n; + size_t offset, len; + char *pos; + int save_errno, nfds; + struct pollfd pfd; + + /* We must read the packet len first */ + if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) { + + pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen; + len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen; + + read_again0: + n = read(as->as_fd, pos, len); + if (n == -1) { + if (errno == EINTR) + goto read_again0; + goto close; /* errno set */ + } + if (n == 0) { + errno = ECONNRESET; + goto close; + } + as->as.dns.datalen += n; + if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) + return (1); /* need more data */ + + as->as.dns.ibuflen = ntohs(as->as.dns.pktlen); + if (ensure_ibuf(as, as->as.dns.ibuflen) == -1) + goto close; /* errno set */ + + pfd.fd = as->as_fd; + pfd.events = POLLIN; + poll_again: + nfds = poll(&pfd, 1, 0); + if (nfds == -1) { + if (errno == EINTR) + goto poll_again; + goto close; /* errno set */ + } + if (nfds == 0) + return (1); /* no more data available */ + } + + offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen); + pos = as->as.dns.ibuf + offset; + len = as->as.dns.ibuflen - offset; + + read_again: + n = read(as->as_fd, pos, len); + if (n == -1) { + if (errno == EINTR) + goto read_again; + goto close; /* errno set */ + } + if (n == 0) { + errno = ECONNRESET; + goto close; + } + as->as.dns.datalen += n; + + /* See if we got all the advertised bytes. */ + if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen)) + return (1); + + DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen); + + if (validate_packet(as) == -1) + goto close; /* errno set */ + + errno = 0; +close: + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + return (errno ? -1 : 0); +} + +/* + * Make sure the input buffer is at least "n" bytes long, and allocate or + * extend it if necessary. Return 0 on success, or set errno and return -1. + */ +static int +ensure_ibuf(struct asr_query *as, size_t n) +{ + char *t; + + if (as->as.dns.ibufsize >= n) + return (0); + + t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1); + if (t == NULL) + return (-1); /* errno set */ + as->as.dns.ibuf = t; + as->as.dns.ibufsize = n; + + return (0); +} + +/* + * Check if the received packet is valid. + * Return 0 on success, or set errno and return -1. + */ +static int +validate_packet(struct asr_query *as) +{ + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + struct asr_dns_rr rr; + int r; + + _asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen); + + _asr_unpack_header(&p, &h); + if (p.err) + goto inval; + + if (h.id != as->as.dns.reqid) { + DPRINT("incorrect reqid\n"); + goto inval; + } + if (h.qdcount != 1) + goto inval; + /* Should be zero, we could allow this */ + if ((h.flags & Z_MASK) != 0) + goto inval; + /* Actually, it depends on the request but we only use OP_QUERY */ + if (OPCODE(h.flags) != OP_QUERY) + goto inval; + /* Must be a response */ + if ((h.flags & QR_MASK) == 0) + goto inval; + + as->as.dns.rcode = RCODE(h.flags); + as->as.dns.ancount = h.ancount; + + _asr_unpack_query(&p, &q); + if (p.err) + goto inval; + + if (q.q_type != as->as.dns.type || + q.q_class != as->as.dns.class || + strcasecmp(q.q_dname, as->as.dns.dname)) { + DPRINT("incorrect type/class/dname '%s' != '%s'\n", + q.q_dname, as->as.dns.dname); + goto inval; + } + + /* Check for truncation */ + if (h.flags & TC_MASK && !(as->as_ctx->ac_options & RES_IGNTC)) { + DPRINT("truncated\n"); + errno = EOVERFLOW; + return (-1); + } + + /* Validate the rest of the packet */ + for (r = h.ancount + h.nscount + h.arcount; r; r--) + _asr_unpack_rr(&p, &rr); + + /* Report any error found when unpacking the RRs. */ + if (p.err) { + DPRINT("unpack: %s\n", strerror(p.err)); + errno = p.err; + return (-1); + } + + if (p.offset != as->as.dns.ibuflen) { + DPRINT("trailing garbage\n"); + errno = EMSGSIZE; + return (-1); + } + + return (0); + + inval: + errno = EINVAL; + return (-1); +} + +#ifdef RES_TRUSTAD +/* + * Clear AD flag in the answer. + */ +static void +clear_ad(struct asr_result *ar) +{ + struct asr_dns_header *h; + uint16_t flags; + + h = (struct asr_dns_header *)ar->ar_data; + flags = ntohs(h->flags); + flags &= ~(AD_MASK); + h->flags = htons(flags); +} +#endif + +/* + * Set the async context nameserver index to the next nameserver, cycling + * over the list until the maximum retry counter is reached. Return 0 on + * success, or -1 if all nameservers were used. + */ +static int +iter_ns(struct asr_query *as) +{ + for (;;) { + if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries) + return (-1); + + as->as.dns.nsidx += 1; + if (as->as.dns.nsidx <= as->as_ctx->ac_nscount) + break; + as->as.dns.nsidx = 0; + as->as.dns.nsloop++; + DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop); + } + + as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop); + if (as->as.dns.nsloop > 0) + as->as_timeout /= as->as_ctx->ac_nscount; + if (as->as_timeout < 1000) + as->as_timeout = 1000; + + return (0); +} blob - /dev/null blob + 61fa3e2fbc97a49a6db32d3ad20d8d895f842dae (mode 644) --- /dev/null +++ openbsd-compat/libasr/sethostent.c @@ -0,0 +1,36 @@ +/* $OpenBSD: sethostent.c,v 1.2 2018/04/28 15:09:35 schwarze Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +void +sethostent(int stayopen) +{ +} + +void +endhostent(void) +{ +} + +struct hostent * +gethostent(void) +{ + h_errno = NETDB_INTERNAL; + return NULL; +} blob - /dev/null blob + 6edbcd71ba3bf35f4f6ac3f8731eb22ac5029d91 (mode 644) --- /dev/null +++ openbsd-compat/libasr/thread_private.h @@ -0,0 +1,9 @@ +/* + * + */ +#define __is_threaded (0) +#define _THREAD_PRIVATE_MUTEX(x) +#define _THREAD_PRIVATE_MUTEX_LOCK(x) +#define _THREAD_PRIVATE_MUTEX_UNLOCK(x) +#define _THREAD_PRIVATE(a, b, c) (c) +#define _THREAD_PRIVATE_DT(a, b, c, d) (d) blob - /dev/null blob + 4ced2710643edfae8d1aedf1c9616da974571135 (mode 644) --- /dev/null +++ openbsd-compat/openbsd-compat.h @@ -0,0 +1,110 @@ +/* $Id: openbsd-compat.h,v 1.51 2010/10/07 10:25:29 djm Exp $ */ + +/* + * Copyright (c) 1999-2003 Damien Miller. All rights reserved. + * Copyright (c) 2003 Ben Lindstrom. All rights reserved. + * Copyright (c) 2002 Tim Rice. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#ifdef NEED_EXPLICIT_BZERO +void explicit_bzero(void *p, size_t n); +#endif +#ifdef NEED_RECALLOCARRAY +void *recallocarray(void *, size_t, size_t, size_t); +#endif +#ifdef NEED_REALLOCARRAY +void *reallocarray(void *, size_t, size_t); +#endif +#ifdef NEED_STRLCAT +size_t strlcat(char *dst, const char *src, size_t size); +#endif +#ifdef NEED_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t size); +#endif +#ifdef NEED_STRTONUM +long long strtonum(const char *nptr, long long minval, long long maxval, const char **errstr); +#endif +#ifdef NEED_PLEDGE +static inline int +pledge(const char *promises, const char *execpromises) +{ + return 0; +} +#endif +#ifdef NEED_INET_NET_PTON +int +inet_net_pton(int af, const char *src, void *dst, size_t size); +#endif +#ifdef NEED_FGETLN +#include +#include +char * fgetln(FILE *stream, size_t *len); +#endif +#ifdef NEED_LIBASR +struct event_asr; +void event_asr_abort(struct event_asr *); + +/* From OpenNTPD portable */ +#if !defined(SA_LEN) +# if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) +# define SA_LEN(x) ((x)->sa_len) +# else +# define SA_LEN(x) ((x)->sa_family == AF_INET6 ? \ + sizeof(struct sockaddr_in6) : \ + sizeof(struct sockaddr_in)) +# endif +#endif +#endif + +/* From OpenBGPD portable */ +#if !defined(SS_LEN) +# if defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN) +# define SS_LEN(x) ((x)->ss_len) +# else +# define SS_LEN(x) SA_LEN((struct sockaddr *)(x)) +# endif +#endif + +#ifdef HAVE_SS_LEN +# define STORAGE_LEN(X) ((X).ss_len) +# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0) +#elif defined(HAVE___SS_LEN) +# define STORAGE_LEN(X) ((X).__ss_len) +# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0) +#else +# define STORAGE_LEN(X) (STORAGE_FAMILY(X) == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) +# define SET_STORAGE_LEN(X, Y) (void) 0 +#endif + +#ifndef timespecsub +#define timespecsub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_nsec += 1000000000L; \ + } \ + } while (0) +#endif blob - /dev/null blob + 840171219741eae591837d011ac207ea241b3507 (mode 644) --- /dev/null +++ openbsd-compat/reallocarray.c @@ -0,0 +1,42 @@ +/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/reallocarray.c */ + +#include "openbsd-compat.h" + +#include +#include +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} blob - /dev/null blob + df7a23bb369a3be4684464cc641b4509badb6ae0 (mode 644) --- /dev/null +++ openbsd-compat/recallocarray.c @@ -0,0 +1,84 @@ +/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */ +/* + * Copyright (c) 2008, 2017 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/recallocarray.c */ + +#include +#include +#include +#include +#include + +#include "openbsd-compat.h" + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) +{ + size_t oldsize, newsize; + void *newptr; + + if (ptr == NULL) + return calloc(newnmemb, size); + + if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + newnmemb > 0 && SIZE_MAX / newnmemb < size) { + errno = ENOMEM; + return NULL; + } + newsize = newnmemb * size; + + if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { + errno = EINVAL; + return NULL; + } + oldsize = oldnmemb * size; + + /* + * Don't bother too much if we're shrinking just a bit, + * we do not shrink for series of small steps, oh well. + */ + if (newsize <= oldsize) { + size_t d = oldsize - newsize; + + if (d < oldsize / 2 && d < (size_t)getpagesize()) { + memset((char *)ptr + newsize, 0, d); + return ptr; + } + } + + newptr = malloc(newsize); + if (newptr == NULL) + return NULL; + + if (newsize > oldsize) { + memcpy(newptr, ptr, oldsize); + memset((char *)newptr + oldsize, 0, newsize - oldsize); + } else + memcpy(newptr, ptr, newsize); + + explicit_bzero(ptr, oldsize); + free(ptr); + + return newptr; +} blob - /dev/null blob + 53b6d0bec6d5dd2cf949b74d769903e073196b6e (mode 644) --- /dev/null +++ openbsd-compat/strlcat.c @@ -0,0 +1,59 @@ +/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ + +#include "openbsd-compat.h" + +#include +#include + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} blob - /dev/null blob + b393c327d29109876ac8ec112af1a2ad58f3cce6 (mode 644) --- /dev/null +++ openbsd-compat/strlcpy.c @@ -0,0 +1,55 @@ +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ + +#include "openbsd-compat.h" + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} blob - /dev/null blob + bb37f2eff144c2e8d273be0f4ddc07134712d929 (mode 644) --- /dev/null +++ openbsd-compat/strtonum.c @@ -0,0 +1,72 @@ +/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */ + +#include "openbsd-compat.h" + +#ifndef HAVE_STRTONUM +#include +#include +#include + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + char *ep; + int error = 0; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) + error = INVALID; + else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} + +#endif /* HAVE_STRTONUM */ blob - /dev/null blob + 28aaaa37a8ce5f186d0efae40695fe4bd5c7fde4 (mode 644) --- /dev/null +++ openbsd-compat/sys/queue.h @@ -0,0 +1,653 @@ +/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +/* OPENBSD ORIGINAL: sys/sys/queue.h */ + +#ifndef _FAKE_QUEUE_H_ +#define _FAKE_QUEUE_H_ + +/* + * Require for OS/X and other platforms that have old/broken/incomplete + * . + */ +#undef SLIST_HEAD +#undef SLIST_HEAD_INITIALIZER +#undef SLIST_ENTRY +#undef SLIST_FOREACH_PREVPTR +#undef SLIST_FIRST +#undef SLIST_END +#undef SLIST_EMPTY +#undef SLIST_NEXT +#undef SLIST_FOREACH +#undef SLIST_INIT +#undef SLIST_INSERT_AFTER +#undef SLIST_INSERT_HEAD +#undef SLIST_REMOVE_HEAD +#undef SLIST_REMOVE +#undef SLIST_REMOVE_NEXT +#undef LIST_HEAD +#undef LIST_HEAD_INITIALIZER +#undef LIST_ENTRY +#undef LIST_FIRST +#undef LIST_END +#undef LIST_EMPTY +#undef LIST_NEXT +#undef LIST_FOREACH +#undef LIST_INIT +#undef LIST_INSERT_AFTER +#undef LIST_INSERT_BEFORE +#undef LIST_INSERT_HEAD +#undef LIST_REMOVE +#undef LIST_REPLACE +#undef SIMPLEQ_HEAD +#undef SIMPLEQ_HEAD_INITIALIZER +#undef SIMPLEQ_ENTRY +#undef SIMPLEQ_FIRST +#undef SIMPLEQ_END +#undef SIMPLEQ_EMPTY +#undef SIMPLEQ_NEXT +#undef SIMPLEQ_FOREACH +#undef SIMPLEQ_INIT +#undef SIMPLEQ_INSERT_HEAD +#undef SIMPLEQ_INSERT_TAIL +#undef SIMPLEQ_INSERT_AFTER +#undef SIMPLEQ_REMOVE_HEAD +#undef TAILQ_HEAD +#undef TAILQ_HEAD_INITIALIZER +#undef TAILQ_ENTRY +#undef TAILQ_FIRST +#undef TAILQ_END +#undef TAILQ_NEXT +#undef TAILQ_LAST +#undef TAILQ_PREV +#undef TAILQ_EMPTY +#undef TAILQ_FOREACH +#undef TAILQ_FOREACH_REVERSE +#undef TAILQ_INIT +#undef TAILQ_INSERT_HEAD +#undef TAILQ_INSERT_TAIL +#undef TAILQ_INSERT_AFTER +#undef TAILQ_INSERT_BEFORE +#undef TAILQ_REMOVE +#undef TAILQ_REPLACE +#undef CIRCLEQ_HEAD +#undef CIRCLEQ_HEAD_INITIALIZER +#undef CIRCLEQ_ENTRY +#undef CIRCLEQ_FIRST +#undef CIRCLEQ_LAST +#undef CIRCLEQ_END +#undef CIRCLEQ_NEXT +#undef CIRCLEQ_PREV +#undef CIRCLEQ_EMPTY +#undef CIRCLEQ_FOREACH +#undef CIRCLEQ_FOREACH_REVERSE +#undef CIRCLEQ_INIT +#undef CIRCLEQ_INSERT_AFTER +#undef CIRCLEQ_INSERT_BEFORE +#undef CIRCLEQ_INSERT_HEAD +#undef CIRCLEQ_INSERT_TAIL +#undef CIRCLEQ_REMOVE +#undef CIRCLEQ_REPLACE + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + _Q_INVALIDATE((elm)->field.sle_next); \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = CIRCLEQ_LAST(head, headname); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#endif /* !_FAKE_QUEUE_H_ */ blob - /dev/null blob + 753b3cfd5979bc41f8370e63c245656d0bbaa611 (mode 644) --- /dev/null +++ openbsd-compat/sys/tree.h @@ -0,0 +1,1006 @@ +/* $OpenBSD: tree.h,v 1.29 2017/07/30 19:27:20 deraadt Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +#include + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __unused __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + + +/* + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct rb_type { + int (*t_compare)(const void *, const void *); + void (*t_augment)(void *); + unsigned int t_offset; /* offset of rb_entry in type */ +}; + +struct rb_tree { + struct rb_entry *rbt_root; +}; + +struct rb_entry { + struct rb_entry *rbt_parent; + struct rb_entry *rbt_left; + struct rb_entry *rbt_right; + unsigned int rbt_color; +}; + +#define RBT_HEAD(_name, _type) \ +struct _name { \ + struct rb_tree rbh_root; \ +} + +#define RBT_ENTRY(_type) struct rb_entry + +static inline void +_rb_init(struct rb_tree *rbt) +{ + rbt->rbt_root = NULL; +} + +static inline int +_rb_empty(struct rb_tree *rbt) +{ + return (rbt->rbt_root == NULL); +} + +void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); +void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); +void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_root(const struct rb_type *, struct rb_tree *); +void *_rb_min(const struct rb_type *, struct rb_tree *); +void *_rb_max(const struct rb_type *, struct rb_tree *); +void *_rb_next(const struct rb_type *, void *); +void *_rb_prev(const struct rb_type *, void *); +void *_rb_left(const struct rb_type *, void *); +void *_rb_right(const struct rb_type *, void *); +void *_rb_parent(const struct rb_type *, void *); +void _rb_set_left(const struct rb_type *, void *, void *); +void _rb_set_right(const struct rb_type *, void *, void *); +void _rb_set_parent(const struct rb_type *, void *, void *); +void _rb_poison(const struct rb_type *, void *, unsigned long); +int _rb_check(const struct rb_type *, void *, unsigned long); + +#define RBT_INITIALIZER(_head) { { NULL } } + +#define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ +extern const struct rb_type *const _name##_RBT_TYPE; \ + \ +__unused static inline void \ +_name##_RBT_INIT(struct _name *head) \ +{ \ + _rb_init(&head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_INSERT(struct _name *head, struct _type *elm) \ +{ \ + return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ +{ \ + return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_FIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_NFIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_ROOT(struct _name *head) \ +{ \ + return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline int \ +_name##_RBT_EMPTY(struct _name *head) \ +{ \ + return _rb_empty(&head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_MIN(struct _name *head) \ +{ \ + return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_MAX(struct _name *head) \ +{ \ + return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_NEXT(struct _type *elm) \ +{ \ + return _rb_next(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_PREV(struct _type *elm) \ +{ \ + return _rb_prev(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_LEFT(struct _type *elm) \ +{ \ + return _rb_left(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_RIGHT(struct _type *elm) \ +{ \ + return _rb_right(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_PARENT(struct _type *elm) \ +{ \ + return _rb_parent(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ +{ \ + return _rb_set_left(_name##_RBT_TYPE, elm, left); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ +{ \ + return _rb_set_right(_name##_RBT_TYPE, elm, right); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ +{ \ + return _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ +} \ + \ +__unused static inline void \ +_name##_RBT_POISON(struct _type *elm, unsigned long poison) \ +{ \ + return _rb_poison(_name##_RBT_TYPE, elm, poison); \ +} \ + \ +__unused static inline int \ +_name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ +{ \ + return _rb_check(_name##_RBT_TYPE, elm, poison); \ +} + +#define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ +static int \ +_name##_RBT_COMPARE(const void *lptr, const void *rptr) \ +{ \ + const struct _type *l = lptr, *r = rptr; \ + return _cmp(l, r); \ +} \ +static const struct rb_type _name##_RBT_INFO = { \ + _name##_RBT_COMPARE, \ + _aug, \ + offsetof(struct _type, _field), \ +}; \ +const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO + +#define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ +static void \ +_name##_RBT_AUGMENT(void *ptr) \ +{ \ + struct _type *p = ptr; \ + return _aug(p); \ +} \ +RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) + +#define RBT_GENERATE(_name, _type, _field, _cmp) \ + RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) + +#define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) +#define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) +#define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) +#define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) +#define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) +#define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) +#define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) +#define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) +#define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) +#define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) +#define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) +#define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) +#define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) +#define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) +#define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) +#define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) +#define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) +#define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) +#define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) + +#define RBT_FOREACH(_e, _name, _head) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_NEXT(_name, (_e))) + +#define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ + (_e) = (_n)) + +#define RBT_FOREACH_REVERSE(_e, _name, _head) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_PREV(_name, (_e))) + +#define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ + (_e) = (_n)) + +#endif /* _SYS_TREE_H_ */