Index: head/lib/libc/amd64/string/strcmp.S =================================================================== --- head/lib/libc/amd64/string/strcmp.S (revision 298829) +++ head/lib/libc/amd64/string/strcmp.S (revision 298830) @@ -1,76 +1,76 @@ /* * Written by J.T. Conklin * Public domain. */ #include __FBSDID("$FreeBSD$"); #if 0 RCSID("$NetBSD: strcmp.S,v 1.3 2004/07/19 20:04:41 drochner Exp $") #endif ENTRY(strcmp) /* * Align s1 to word boundary. * Consider unrolling loop? */ .Ls1align: testb $7,%dil je .Ls1aligned movb (%rdi),%al incq %rdi movb (%rsi),%dl incq %rsi testb %al,%al je .Ldone cmpb %al,%dl je .Ls1align jmp .Ldone /* - * Check whether s2 is aligned to a word boundry. If it is, we + * Check whether s2 is aligned to a word boundary. If it is, we * can compare by words. Otherwise we have to compare by bytes. */ .Ls1aligned: testb $7,%sil jne .Lbyte_loop movabsq $0x0101010101010101,%r8 subq $8,%rdi movabsq $0x8080808080808080,%r9 subq $8,%rsi .align 4 .Lword_loop: movq 8(%rdi),%rax addq $8,%rdi movq 8(%rsi),%rdx addq $8,%rsi cmpq %rax,%rdx jne .Lbyte_loop subq %r8,%rdx notq %rax andq %rax,%rdx testq %r9,%rdx je .Lword_loop .align 4 .Lbyte_loop: movb (%rdi),%al incq %rdi movb (%rsi),%dl incq %rsi testb %al,%al je .Ldone cmpb %al,%dl je .Lbyte_loop .Ldone: movzbq %al,%rax movzbq %dl,%rdx subq %rdx,%rax ret END(strcmp) .section .note.GNU-stack,"",%progbits Index: head/lib/libc/iconv/iconv-internal.h =================================================================== --- head/lib/libc/iconv/iconv-internal.h (revision 298829) +++ head/lib/libc/iconv/iconv-internal.h (revision 298830) @@ -1,45 +1,45 @@ /*- * Copyright (c) 2013 Peter Wemm * 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 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 AUTHOR 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. * * $FreeBSD$ */ /* - * Interal prototypes for our back-end functions. + * Internal prototypes for our back-end functions. */ size_t __bsd___iconv(iconv_t, char **, size_t *, char **, size_t *, __uint32_t, size_t *); void __bsd___iconv_free_list(char **, size_t); int __bsd___iconv_get_list(char ***, size_t *, __iconv_bool); size_t __bsd_iconv(iconv_t, char ** __restrict, size_t * __restrict, char ** __restrict, size_t * __restrict); const char *__bsd_iconv_canonicalize(const char *); int __bsd_iconv_close(iconv_t); iconv_t __bsd_iconv_open(const char *, const char *); int __bsd_iconv_open_into(const char *, const char *, iconv_allocation_t *); void __bsd_iconv_set_relocation_prefix(const char *, const char *); int __bsd_iconvctl(iconv_t, int, void *); void __bsd_iconvlist(int (*) (unsigned int, const char * const *, void *), void *); Index: head/lib/libc/include/compat.h =================================================================== --- head/lib/libc/include/compat.h (revision 298829) +++ head/lib/libc/include/compat.h (revision 298830) @@ -1,57 +1,57 @@ /*- * Copyright (c) 2009 Hudson River Trading LLC * Written by: John H. Baldwin * 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 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 AUTHOR 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. * * $FreeBSD$ */ /* - * This file defines compatiblity symbol versions for old system calls. It + * This file defines compatibility symbol versions for old system calls. It * is included in all generated system call files. */ #ifndef __LIBC_COMPAT_H__ #define __LIBC_COMPAT_H__ #define __sym_compat(sym,impl,verid) \ .symver impl, sym@verid #ifndef NO_COMPAT7 __sym_compat(__semctl, freebsd7___semctl, FBSD_1.0); __sym_compat(msgctl, freebsd7_msgctl, FBSD_1.0); __sym_compat(shmctl, freebsd7_shmctl, FBSD_1.0); #endif #undef __sym_compat #define __weak_reference(sym,alias) \ .weak alias;.equ alias,sym __weak_reference(__sys_fcntl,__fcntl_compat) #undef __weak_reference #endif /* __LIBC_COMPAT_H__ */ Index: head/lib/libc/include/reentrant.h =================================================================== --- head/lib/libc/include/reentrant.h (revision 298829) +++ head/lib/libc/include/reentrant.h (revision 298830) @@ -1,128 +1,128 @@ /*- * Copyright (c) 1997,98 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by J.T. Conklin. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. * * $FreeBSD$ */ /* * Requirements: * * 1. The thread safe mechanism should be lightweight so the library can * be used by non-threaded applications without unreasonable overhead. * * 2. There should be no dependency on a thread engine for non-threaded * applications. * * 3. There should be no dependency on any particular thread engine. * * 4. The library should be able to be compiled without support for thread * safety. * * * Rationale: * * One approach for thread safety is to provide discrete versions of the * library: one thread safe, the other not. The disadvantage of this is * that libc is rather large, and two copies of a library which are 99%+ - * identical is not an efficent use of resources. + * identical is not an efficient use of resources. * * Another approach is to provide a single thread safe library. However, * it should not add significant run time or code size overhead to non- * threaded applications. * * Since the NetBSD C library is used in other projects, it should be * easy to replace the mutual exclusion primitives with ones provided by * another system. Similarly, it should also be easy to remove all * support for thread safety completely if the target environment does * not support threads. * * * Implementation Details: * * The mutex primitives used by the library (mutex_t, mutex_lock, etc.) * are macros which expand to the cooresponding primitives provided by * the thread engine or to nothing. The latter is used so that code is * not unreasonably cluttered with #ifdefs when all thread safe support * is removed. * * The mutex macros can be directly mapped to the mutex primitives from * pthreads, however it should be reasonably easy to wrap another mutex * implementation so it presents a similar interface. * * Stub implementations of the mutex functions are provided with *weak* * linkage. These functions simply return success. When linked with a * thread library (i.e. -lpthread), the functions will override the * stubs. */ #include #include #include "libc_private.h" #define mutex_t pthread_mutex_t #define cond_t pthread_cond_t #define rwlock_t pthread_rwlock_t #define once_t pthread_once_t #define thread_key_t pthread_key_t #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #define RWLOCK_INITIALIZER PTHREAD_RWLOCK_INITIALIZER #define ONCE_INITIALIZER PTHREAD_ONCE_INIT #define mutex_init(m, a) _pthread_mutex_init(m, a) #define mutex_lock(m) if (__isthreaded) \ _pthread_mutex_lock(m) #define mutex_unlock(m) if (__isthreaded) \ _pthread_mutex_unlock(m) #define mutex_trylock(m) (__isthreaded ? 0 : _pthread_mutex_trylock(m)) #define cond_init(c, a, p) _pthread_cond_init(c, a) #define cond_signal(m) if (__isthreaded) \ _pthread_cond_signal(m) #define cond_broadcast(m) if (__isthreaded) \ _pthread_cond_broadcast(m) #define cond_wait(c, m) if (__isthreaded) \ _pthread_cond_wait(c, m) #define rwlock_init(l, a) _pthread_rwlock_init(l, a) #define rwlock_rdlock(l) if (__isthreaded) \ _pthread_rwlock_rdlock(l) #define rwlock_wrlock(l) if (__isthreaded) \ _pthread_rwlock_wrlock(l) #define rwlock_unlock(l) if (__isthreaded) \ _pthread_rwlock_unlock(l) #define thr_keycreate(k, d) _pthread_key_create(k, d) #define thr_setspecific(k, p) _pthread_setspecific(k, p) #define thr_getspecific(k) _pthread_getspecific(k) #define thr_sigsetmask(f, n, o) _pthread_sigmask(f, n, o) #define thr_once(o, i) _pthread_once(o, i) #define thr_self() _pthread_self() #define thr_exit(x) _pthread_exit(x) #define thr_main() _pthread_main_np() Index: head/lib/libc/locale/collate.c =================================================================== --- head/lib/libc/locale/collate.c (revision 298829) +++ head/lib/libc/locale/collate.c (revision 298830) @@ -1,703 +1,703 @@ /*- * Copyright 2014 Garrett D'Amore * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1995 Alex Tatmanjants * at Electronni Visti IA, Kiev, Ukraine. * All rights reserved. * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * 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. * * Adapted to xlocale by John Marino */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "collate.h" #include "setlocale.h" #include "ldpart.h" #include "libc_private.h" struct xlocale_collate __xlocale_global_collate = { {{0}, "C"}, 1, 0, 0, 0 }; struct xlocale_collate __xlocale_C_collate = { {{0}, "C"}, 1, 0, 0, 0 }; static int __collate_load_tables_l(const char *encoding, struct xlocale_collate *table); static void destruct_collate(void *t) { struct xlocale_collate *table = t; if (table->map && (table->maplen > 0)) { (void) munmap(table->map, table->maplen); } free(t); } void * __collate_load(const char *encoding, __unused locale_t unused) { if (strcmp(encoding, "C") == 0 || strcmp(encoding, "POSIX") == 0) { return &__xlocale_C_collate; } struct xlocale_collate *table = calloc(sizeof(struct xlocale_collate), 1); table->header.header.destructor = destruct_collate; // FIXME: Make sure that _LDP_CACHE is never returned. We should be doing // the caching outside of this section if (__collate_load_tables_l(encoding, table) != _LDP_LOADED) { xlocale_release(table); return NULL; } return table; } /** * Load the collation tables for the specified encoding into the global table. */ int __collate_load_tables(const char *encoding) { return (__collate_load_tables_l(encoding, &__xlocale_global_collate)); } int __collate_load_tables_l(const char *encoding, struct xlocale_collate *table) { int i, chains, z; char *buf; char *TMP; char *map; collate_info_t *info; struct stat sbuf; int fd; table->__collate_load_error = 1; /* 'encoding' must be already checked. */ if (strcmp(encoding, "C") == 0 || strcmp(encoding, "POSIX") == 0) { return (_LDP_CACHE); } asprintf(&buf, "%s/%s/LC_COLLATE", _PathLocale, encoding); if (buf == NULL) return (_LDP_ERROR); if ((fd = _open(buf, O_RDONLY)) < 0) { free(buf); return (_LDP_ERROR); } free(buf); if (_fstat(fd, &sbuf) < 0) { (void) _close(fd); return (_LDP_ERROR); } if (sbuf.st_size < (COLLATE_STR_LEN + sizeof (info))) { (void) _close(fd); errno = EINVAL; return (_LDP_ERROR); } map = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); (void) _close(fd); if ((TMP = map) == NULL) { return (_LDP_ERROR); } if (strncmp(TMP, COLLATE_VERSION, COLLATE_STR_LEN) != 0) { (void) munmap(map, sbuf.st_size); errno = EINVAL; return (_LDP_ERROR); } TMP += COLLATE_STR_LEN; info = (void *)TMP; TMP += sizeof (*info); if ((info->directive_count < 1) || (info->directive_count >= COLL_WEIGHTS_MAX) || ((chains = info->chain_count) < 0)) { (void) munmap(map, sbuf.st_size); errno = EINVAL; return (_LDP_ERROR); } i = (sizeof (collate_char_t) * (UCHAR_MAX + 1)) + (sizeof (collate_chain_t) * chains) + (sizeof (collate_large_t) * info->large_count); for (z = 0; z < info->directive_count; z++) { i += sizeof (collate_subst_t) * info->subst_count[z]; } if (i != (sbuf.st_size - (TMP - map))) { (void) munmap(map, sbuf.st_size); errno = EINVAL; return (_LDP_ERROR); } table->info = info; table->char_pri_table = (void *)TMP; TMP += sizeof (collate_char_t) * (UCHAR_MAX + 1); for (z = 0; z < info->directive_count; z++) { if (info->subst_count[z] > 0) { table->subst_table[z] = (void *)TMP; TMP += info->subst_count[z] * sizeof (collate_subst_t); } else { table->subst_table[z] = NULL; } } if (chains > 0) { table->chain_pri_table = (void *)TMP; TMP += chains * sizeof (collate_chain_t); } else table->chain_pri_table = NULL; if (info->large_count > 0) table->large_pri_table = (void *)TMP; else table->large_pri_table = NULL; table->__collate_load_error = 0; return (_LDP_LOADED); } static const int32_t * substsearch(struct xlocale_collate *table, const wchar_t key, int pass) { const collate_subst_t *p; int n = table->info->subst_count[pass]; if (n == 0) return (NULL); if (pass >= table->info->directive_count) return (NULL); if (!(key & COLLATE_SUBST_PRIORITY)) return (NULL); p = table->subst_table[pass] + (key & ~COLLATE_SUBST_PRIORITY); assert(p->key == key); return (p->pri); } static collate_chain_t * chainsearch(struct xlocale_collate *table, const wchar_t *key, int *len) { int low = 0; int high = table->info->chain_count - 1;; int next, compar, l; collate_chain_t *p; collate_chain_t *tab = table->chain_pri_table; if (high < 0) return (NULL); while (low <= high) { next = (low + high) / 2; p = tab + next; compar = *key - *p->str; if (compar == 0) { l = wcsnlen(p->str, COLLATE_STR_LEN); compar = wcsncmp(key, p->str, l); if (compar == 0) { *len = l; return (p); } } if (compar > 0) low = next + 1; else high = next - 1; } return (NULL); } static collate_large_t * largesearch(struct xlocale_collate *table, const wchar_t key) { int low = 0; int high = table->info->large_count - 1; int next, compar; collate_large_t *p; collate_large_t *tab = table->large_pri_table; if (high < 0) return (NULL); while (low <= high) { next = (low + high) / 2; p = tab + next; compar = key - p->val; if (compar == 0) return (p); if (compar > 0) low = next + 1; else high = next - 1; } return (NULL); } void _collate_lookup(struct xlocale_collate *table, const wchar_t *t, int *len, int *pri, int which, const int **state) { collate_chain_t *p2; collate_large_t *match; int p, l; const int *sptr; /* * If this is the "last" pass for the UNDEFINED, then * we just return the priority itself. */ if (which >= table->info->directive_count) { *pri = *t; *len = 1; *state = NULL; return; } /* * If we have remaining substitution data from a previous * call, consume it first. */ if ((sptr = *state) != NULL) { *pri = *sptr; sptr++; if ((sptr == *state) || (sptr == NULL)) *state = NULL; else *state = sptr; *len = 0; return; } /* No active substitutions */ *len = 1; /* - * Check for composites such as dipthongs that collate as a + * Check for composites such as diphthongs that collate as a * single element (aka chains or collating-elements). */ if (((p2 = chainsearch(table, t, &l)) != NULL) && ((p = p2->pri[which]) >= 0)) { *len = l; *pri = p; } else if (*t <= UCHAR_MAX) { /* * Character is a small (8-bit) character. * We just look these up directly for speed. */ *pri = table->char_pri_table[*t].pri[which]; } else if ((table->info->large_count > 0) && ((match = largesearch(table, *t)) != NULL)) { /* * Character was found in the extended table. */ *pri = match->pri.pri[which]; } else { /* * Character lacks a specific definition. */ if (table->info->directive[which] & DIRECTIVE_UNDEFINED) { /* Mask off sign bit to prevent ordering confusion. */ *pri = (*t & COLLATE_MAX_PRIORITY); } else { *pri = table->info->undef_pri[which]; } /* No substitutions for undefined characters! */ return; } /* * Try substituting (expanding) the character. We are * currently doing this *after* the chain compression. I * think it should not matter, but this way might be slightly * faster. * * We do this after the priority search, as this will help us * to identify a single key value. In order for this to work, * its important that the priority assigned to a given element * to be substituted be unique for that level. The localedef * code ensures this for us. */ if ((sptr = substsearch(table, *pri, which)) != NULL) { if ((*pri = *sptr) > 0) { sptr++; *state = *sptr ? sptr : NULL; } } } /* * This is the meaty part of wcsxfrm & strxfrm. Note that it does * NOT NULL terminate. That is left to the caller. */ size_t _collate_wxfrm(struct xlocale_collate *table, const wchar_t *src, wchar_t *xf, size_t room) { int pri; int len; const wchar_t *t; wchar_t *tr = NULL; int direc; int pass; const int32_t *state; size_t want = 0; size_t need = 0; int ndir = table->info->directive_count; assert(src); for (pass = 0; pass <= ndir; pass++) { state = NULL; if (pass != 0) { /* insert level separator from the previous pass */ if (room) { *xf++ = 1; room--; } want++; } /* special pass for undefined */ if (pass == ndir) { direc = DIRECTIVE_FORWARD | DIRECTIVE_UNDEFINED; } else { direc = table->info->directive[pass]; } t = src; if (direc & DIRECTIVE_BACKWARD) { wchar_t *bp, *fp, c; free(tr); if ((tr = wcsdup(t)) == NULL) { errno = ENOMEM; goto fail; } bp = tr; fp = tr + wcslen(tr) - 1; while (bp < fp) { c = *bp; *bp++ = *fp; *fp-- = c; } t = (const wchar_t *)tr; } if (direc & DIRECTIVE_POSITION) { while (*t || state) { _collate_lookup(table, t, &len, &pri, pass, &state); t += len; if (pri <= 0) { if (pri < 0) { errno = EINVAL; goto fail; } pri = COLLATE_MAX_PRIORITY; } if (room) { *xf++ = pri; room--; } want++; need = want; } } else { while (*t || state) { _collate_lookup(table, t, &len, &pri, pass, &state); t += len; if (pri <= 0) { if (pri < 0) { errno = EINVAL; goto fail; } continue; } if (room) { *xf++ = pri; room--; } want++; need = want; } } } free(tr); return (need); fail: free(tr); return ((size_t)(-1)); } /* * In the non-POSIX case, we transform each character into a string of * characters representing the character's priority. Since char is usually * signed, we are limited by 7 bits per byte. To avoid zero, we need to add * XFRM_OFFSET, so we can't use a full 7 bits. For simplicity, we choose 6 * bits per byte. * * It turns out that we sometimes have real priorities that are * 31-bits wide. (But: be careful using priorities where the high * order bit is set -- i.e. the priority is negative. The sort order * may be surprising!) * * TODO: This would be a good area to optimize somewhat. It turns out * that real prioririties *except for the last UNDEFINED pass* are generally * very small. We need the localedef code to precalculate the max * priority for us, and ideally also give us a mask, and then we could * severely limit what we expand to. */ #define XFRM_BYTES 6 #define XFRM_OFFSET ('0') /* make all printable characters */ #define XFRM_SHIFT 6 #define XFRM_MASK ((1 << XFRM_SHIFT) - 1) #define XFRM_SEP ('.') /* chosen to be less than XFRM_OFFSET */ static int xfrm(struct xlocale_collate *table, unsigned char *p, int pri, int pass) { /* we use unsigned to ensure zero fill on right shift */ uint32_t val = (uint32_t)table->info->pri_count[pass]; int nc = 0; while (val) { *p = (pri & XFRM_MASK) + XFRM_OFFSET; pri >>= XFRM_SHIFT; val >>= XFRM_SHIFT; p++; nc++; } return (nc); } size_t _collate_sxfrm(struct xlocale_collate *table, const wchar_t *src, char *xf, size_t room) { int pri; int len; const wchar_t *t; wchar_t *tr = NULL; int direc; int pass; const int32_t *state; size_t want = 0; size_t need = 0; int b; uint8_t buf[XFRM_BYTES]; int ndir = table->info->directive_count; assert(src); for (pass = 0; pass <= ndir; pass++) { state = NULL; if (pass != 0) { /* insert level separator from the previous pass */ if (room) { *xf++ = XFRM_SEP; room--; } want++; } /* special pass for undefined */ if (pass == ndir) { direc = DIRECTIVE_FORWARD | DIRECTIVE_UNDEFINED; } else { direc = table->info->directive[pass]; } t = src; if (direc & DIRECTIVE_BACKWARD) { wchar_t *bp, *fp, c; free(tr); if ((tr = wcsdup(t)) == NULL) { errno = ENOMEM; goto fail; } bp = tr; fp = tr + wcslen(tr) - 1; while (bp < fp) { c = *bp; *bp++ = *fp; *fp-- = c; } t = (const wchar_t *)tr; } if (direc & DIRECTIVE_POSITION) { while (*t || state) { _collate_lookup(table, t, &len, &pri, pass, &state); t += len; if (pri <= 0) { if (pri < 0) { errno = EINVAL; goto fail; } pri = COLLATE_MAX_PRIORITY; } b = xfrm(table, buf, pri, pass); want += b; if (room) { while (b) { b--; if (room) { *xf++ = buf[b]; room--; } } } need = want; } } else { while (*t || state) { _collate_lookup(table, t, &len, &pri, pass, &state); t += len; if (pri <= 0) { if (pri < 0) { errno = EINVAL; goto fail; } continue; } b = xfrm(table, buf, pri, pass); want += b; if (room) { while (b) { b--; if (room) { *xf++ = buf[b]; room--; } } } need = want; } } } free(tr); return (need); fail: free(tr); return ((size_t)(-1)); } /* * __collate_equiv_value returns the primary collation value for the given * collating symbol specified by str and len. Zero or negative is returned * if the collating symbol was not found. This function is used by bracket * code in the TRE regex library. */ int __collate_equiv_value(locale_t locale, const wchar_t *str, size_t len) { int32_t e; if (len < 1 || len >= COLLATE_STR_LEN) return (-1); FIX_LOCALE(locale); struct xlocale_collate *table = (struct xlocale_collate*)locale->components[XLC_COLLATE]; if (table->__collate_load_error) return ((len == 1 && *str <= UCHAR_MAX) ? *str : -1); if (len == 1) { e = -1; if (*str <= UCHAR_MAX) e = table->char_pri_table[*str].pri[0]; else if (table->info->large_count > 0) { collate_large_t *match_large; match_large = largesearch(table, *str); if (match_large) e = match_large->pri.pri[0]; } if (e == 0) return (1); return (e > 0 ? e : 0); } if (table->info->chain_count > 0) { wchar_t name[COLLATE_STR_LEN]; collate_chain_t *match_chain; int clen; wcsncpy (name, str, len); name[len] = 0; match_chain = chainsearch(table, name, &clen); if (match_chain) { e = match_chain->pri[0]; if (e == 0) return (1); return (e < 0 ? -e : e); } } return (0); } Index: head/lib/libc/locale/nl_langinfo.c =================================================================== --- head/lib/libc/locale/nl_langinfo.c (revision 298829) +++ head/lib/libc/locale/nl_langinfo.c (revision 298830) @@ -1,207 +1,207 @@ /*- * Copyright (c) 2001, 2003 Alexey Zelkin * All rights reserved. * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * 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 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "mblocal.h" #include "lnumeric.h" #include "lmessages.h" #include "lmonetary.h" #include "../stdtime/timelocal.h" #define _REL(BASE) ((int)item-BASE) char * nl_langinfo_l(nl_item item, locale_t loc) { char *ret, *cs; const char *s; FIX_LOCALE(loc); switch (item) { case CODESET: s = XLOCALE_CTYPE(loc)->runes->__encoding; if (strcmp(s, "EUC-CN") == 0) ret = "eucCN"; else if (strcmp(s, "EUC-JP") == 0) ret = "eucJP"; else if (strcmp(s, "EUC-KR") == 0) ret = "eucKR"; else if (strcmp(s, "EUC-TW") == 0) ret = "eucTW"; else if (strcmp(s, "BIG5") == 0) ret = "Big5"; else if (strcmp(s, "MSKanji") == 0) ret = "SJIS"; else if (strcmp(s, "NONE") == 0) ret = "US-ASCII"; else if (strncmp(s, "NONE:", 5) == 0) ret = (char *)(s + 5); else ret = (char *)s; break; case D_T_FMT: ret = (char *) __get_current_time_locale(loc)->c_fmt; break; case D_FMT: ret = (char *) __get_current_time_locale(loc)->x_fmt; break; case T_FMT: ret = (char *) __get_current_time_locale(loc)->X_fmt; break; case T_FMT_AMPM: ret = (char *) __get_current_time_locale(loc)->ampm_fmt; break; case AM_STR: ret = (char *) __get_current_time_locale(loc)->am; break; case PM_STR: ret = (char *) __get_current_time_locale(loc)->pm; break; case DAY_1: case DAY_2: case DAY_3: case DAY_4: case DAY_5: case DAY_6: case DAY_7: ret = (char*) __get_current_time_locale(loc)->weekday[_REL(DAY_1)]; break; case ABDAY_1: case ABDAY_2: case ABDAY_3: case ABDAY_4: case ABDAY_5: case ABDAY_6: case ABDAY_7: ret = (char*) __get_current_time_locale(loc)->wday[_REL(ABDAY_1)]; break; case MON_1: case MON_2: case MON_3: case MON_4: case MON_5: case MON_6: case MON_7: case MON_8: case MON_9: case MON_10: case MON_11: case MON_12: ret = (char*) __get_current_time_locale(loc)->month[_REL(MON_1)]; break; case ABMON_1: case ABMON_2: case ABMON_3: case ABMON_4: case ABMON_5: case ABMON_6: case ABMON_7: case ABMON_8: case ABMON_9: case ABMON_10: case ABMON_11: case ABMON_12: ret = (char*) __get_current_time_locale(loc)->mon[_REL(ABMON_1)]; break; case ALTMON_1: case ALTMON_2: case ALTMON_3: case ALTMON_4: case ALTMON_5: case ALTMON_6: case ALTMON_7: case ALTMON_8: case ALTMON_9: case ALTMON_10: case ALTMON_11: case ALTMON_12: ret = (char*) __get_current_time_locale(loc)->alt_month[_REL(ALTMON_1)]; break; case ERA: /* XXX: need to be implemented */ ret = ""; break; case ERA_D_FMT: /* XXX: need to be implemented */ ret = ""; break; case ERA_D_T_FMT: /* XXX: need to be implemented */ ret = ""; break; case ERA_T_FMT: /* XXX: need to be implemented */ ret = ""; break; case ALT_DIGITS: /* XXX: need to be implemented */ ret = ""; break; case RADIXCHAR: ret = (char*) __get_current_numeric_locale(loc)->decimal_point; break; case THOUSEP: ret = (char*) __get_current_numeric_locale(loc)->thousands_sep; break; case YESEXPR: ret = (char*) __get_current_messages_locale(loc)->yesexpr; break; case NOEXPR: ret = (char*) __get_current_messages_locale(loc)->noexpr; break; /* * YESSTR and NOSTR items marked with LEGACY are available, but not - * recomended by SUSv2 to be used in portable applications since + * recommended by SUSv2 to be used in portable applications since * they're subject to remove in future specification editions. */ case YESSTR: /* LEGACY */ ret = (char*) __get_current_messages_locale(loc)->yesstr; break; case NOSTR: /* LEGACY */ ret = (char*) __get_current_messages_locale(loc)->nostr; break; /* * SUSv2 special formatted currency string */ case CRNCYSTR: ret = ""; cs = (char*) __get_current_monetary_locale(loc)->currency_symbol; if (*cs != '\0') { char pos = localeconv_l(loc)->p_cs_precedes; if (pos == localeconv_l(loc)->n_cs_precedes) { char psn = '\0'; if (pos == CHAR_MAX) { if (strcmp(cs, __get_current_monetary_locale(loc)->mon_decimal_point) == 0) psn = '.'; } else psn = pos ? '-' : '+'; if (psn != '\0') { int clen = strlen(cs); if ((loc->csym = reallocf(loc->csym, clen + 2)) != NULL) { *loc->csym = psn; strcpy(loc->csym + 1, cs); ret = loc->csym; } } } } break; case D_MD_ORDER: /* FreeBSD local extension */ ret = (char *) __get_current_time_locale(loc)->md_order; break; default: ret = ""; } return (ret); } char * nl_langinfo(nl_item item) { return nl_langinfo_l(item, __get_locale()); } Index: head/lib/libc/nameser/ns_samedomain.c =================================================================== --- head/lib/libc/nameser/ns_samedomain.c (revision 298829) +++ head/lib/libc/nameser/ns_samedomain.c (revision 298830) @@ -1,211 +1,211 @@ /* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995,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 ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. */ #ifndef lint static const char rcsid[] = "$Id: ns_samedomain.c,v 1.6 2005/04/27 04:56:40 sra Exp $"; #endif #include __FBSDID("$FreeBSD$"); #include "port_before.h" #include #include #include #include #include "port_after.h" /*% * Check whether a name belongs to a domain. * * Inputs: - *\li a - the domain whose ancestory is being verified + *\li a - the domain whose ancestry is being verified *\li b - the potential ancestor we're checking against * * Return: *\li boolean - is a at or below b? * * Notes: *\li Trailing dots are first removed from name and domain. * Always compare complete subdomains, not only whether the * domain name is the trailing string of the given name. * *\li "host.foobar.top" lies in "foobar.top" and in "top" and in "" * but NOT in "bar.top" */ int ns_samedomain(const char *a, const char *b) { size_t la, lb; int diff, i, escaped; const char *cp; la = strlen(a); lb = strlen(b); /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */ if (la != 0U && a[la - 1] == '.') { escaped = 0; /* Note this loop doesn't get executed if la==1. */ for (i = la - 2; i >= 0; i--) if (a[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (!escaped) la--; } /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */ if (lb != 0U && b[lb - 1] == '.') { escaped = 0; /* note this loop doesn't get executed if lb==1 */ for (i = lb - 2; i >= 0; i--) if (b[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (!escaped) lb--; } /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */ if (lb == 0U) return (1); /* 'b' longer than 'a' means 'a' can't be in 'b'. */ if (lb > la) return (0); /* 'a' and 'b' being equal at this point indicates sameness. */ if (lb == la) return (strncasecmp(a, b, lb) == 0); /* Ok, we know la > lb. */ diff = la - lb; /* * If 'a' is only 1 character longer than 'b', then it can't be * a subdomain of 'b' (because of the need for the '.' label * separator). */ if (diff < 2) return (0); /* * If the character before the last 'lb' characters of 'b' * isn't '.', then it can't be a match (this lets us avoid * having "foobar.com" match "bar.com"). */ if (a[diff - 1] != '.') return (0); /* * We're not sure about that '.', however. It could be escaped * and thus not a really a label separator. */ escaped = 0; for (i = diff - 2; i >= 0; i--) if (a[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (escaped) return (0); /* Now compare aligned trailing substring. */ cp = a + diff; return (strncasecmp(cp, b, lb) == 0); } #ifndef _LIBC /*% * is "a" a subdomain of "b"? */ int ns_subdomain(const char *a, const char *b) { return (ns_samename(a, b) != 1 && ns_samedomain(a, b)); } #endif /*% * make a canonical copy of domain name "src" * * notes: * \code * foo -> foo. * foo. -> foo. * foo.. -> foo. * foo\. -> foo\.. * foo\\. -> foo\\. * \endcode */ int ns_makecanon(const char *src, char *dst, size_t dstsize) { size_t n = strlen(src); if (n + sizeof "." > dstsize) { /*%< Note: sizeof == 2 */ errno = EMSGSIZE; return (-1); } strcpy(dst, src); while (n >= 1U && dst[n - 1] == '.') /*%< Ends in "." */ if (n >= 2U && dst[n - 2] == '\\' && /*%< Ends in "\." */ (n < 3U || dst[n - 3] != '\\')) /*%< But not "\\." */ break; else dst[--n] = '\0'; dst[n++] = '.'; dst[n] = '\0'; return (0); } /*% * determine whether domain name "a" is the same as domain name "b" * * return: *\li -1 on error *\li 0 if names differ *\li 1 if names are the same */ int ns_samename(const char *a, const char *b) { char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; if (ns_makecanon(a, ta, sizeof ta) < 0 || ns_makecanon(b, tb, sizeof tb) < 0) return (-1); if (strcasecmp(ta, tb) == 0) return (1); else return (0); } /*! \file */ Index: head/lib/libc/net/getaddrinfo.c =================================================================== --- head/lib/libc/net/getaddrinfo.c (revision 298829) +++ head/lib/libc/net/getaddrinfo.c (revision 298830) @@ -1,3016 +1,3016 @@ /* $KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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 project 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 PROJECT 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 PROJECT 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. */ /* * Issues to be discussed: * - Return values. There are nonstandard return values defined and used * in the source code. This is because RFC2553 is silent about which error * code must be returned for which situation. * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is * invalid. current code - SEGV on freeaddrinfo(NULL) * * Note: * - The code filters out AFs that are not supported by the kernel, * when globbing NULL hostname (to loopback, or wildcard). Is it the right * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG * in ai_flags? * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. * (1) what should we do against numeric hostname (2) what should we do * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? * non-loopback address configured? global address configured? * * OS specific notes for freebsd4: * - FreeBSD supported $GAI. The code does not. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #ifdef INET6 #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "res_config.h" #ifdef DEBUG #include #endif #include #include #include "un-namespace.h" #include "netdb_private.h" #include "libc_private.h" #ifdef NS_CACHING #include "nscache.h" #endif #define ANY 0 #define YES 1 #define NO 0 static const char in_addrany[] = { 0, 0, 0, 0 }; static const char in_loopback[] = { 127, 0, 0, 1 }; #ifdef INET6 static const char in6_addrany[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const char in6_loopback[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; #endif struct policyqueue { TAILQ_ENTRY(policyqueue) pc_entry; #ifdef INET6 struct in6_addrpolicy pc_policy; #endif }; TAILQ_HEAD(policyhead, policyqueue); static const struct afd { int a_af; int a_addrlen; socklen_t a_socklen; int a_off; const char *a_addrany; const char *a_loopback; int a_scoped; } afdl [] = { #ifdef INET6 #define N_INET6 0 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), offsetof(struct sockaddr_in6, sin6_addr), in6_addrany, in6_loopback, 1}, #define N_INET 1 #define N_LOCAL 2 #else #define N_INET 0 #define N_LOCAL 1 #endif {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), offsetof(struct sockaddr_in, sin_addr), in_addrany, in_loopback, 0}, #define sizeofmember(type, member) (sizeof(((type *)0)->member)) {PF_LOCAL, sizeofmember(struct sockaddr_un, sun_path), sizeof(struct sockaddr_un), offsetof(struct sockaddr_un, sun_path), NULL, NULL, 0}, {0, 0, 0, 0, NULL, NULL, 0}, }; struct explore { int e_af; int e_socktype; int e_protocol; int e_wild; #define AF_ANY 0x01 #define SOCKTYPE_ANY 0x02 #define PROTOCOL_ANY 0x04 #define WILD_AF(ex) ((ex)->e_wild & AF_ANY) #define WILD_SOCKTYPE(ex) ((ex)->e_wild & SOCKTYPE_ANY) #define WILD_PROTOCOL(ex) ((ex)->e_wild & PROTOCOL_ANY) }; static const struct explore explore[] = { #ifdef INET6 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_INET6, SOCK_STREAM, IPPROTO_TCP, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_INET6, SOCK_STREAM, IPPROTO_SCTP, AF_ANY | SOCKTYPE_ANY }, { PF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE, AF_ANY | SOCKTYPE_ANY }, { PF_INET6, SOCK_RAW, ANY, AF_ANY | PROTOCOL_ANY }, #endif { PF_INET, SOCK_DGRAM, IPPROTO_UDP, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_INET, SOCK_STREAM, IPPROTO_TCP, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_INET, SOCK_STREAM, IPPROTO_SCTP, AF_ANY | SOCKTYPE_ANY }, { PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE, AF_ANY | SOCKTYPE_ANY }, { PF_INET, SOCK_RAW, ANY, AF_ANY | PROTOCOL_ANY }, { PF_LOCAL, SOCK_DGRAM, ANY, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_LOCAL, SOCK_STREAM, ANY, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { PF_LOCAL, SOCK_SEQPACKET, ANY, AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, { -1, 0, 0, 0 }, }; #ifdef INET6 #define PTON_MAX 16 #else #define PTON_MAX 4 #endif #define AIO_SRCFLAG_DEPRECATED 0x1 struct ai_order { union { struct sockaddr_storage aiou_ss; struct sockaddr aiou_sa; } aio_src_un; #define aio_srcsa aio_src_un.aiou_sa u_int32_t aio_srcflag; int aio_srcscope; int aio_dstscope; struct policyqueue *aio_srcpolicy; struct policyqueue *aio_dstpolicy; struct addrinfo *aio_ai; int aio_matchlen; }; static const ns_src default_dns_files[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; struct res_target { struct res_target *next; const char *name; /* domain name */ int qclass, qtype; /* class and type of query */ u_char *answer; /* buffer to put answer */ int anslen; /* size of answer buffer */ int n; /* result length */ }; #define MAXPACKET (64*1024) typedef union { HEADER hdr; u_char buf[MAXPACKET]; } querybuf; static int str2number(const char *, int *); static int explore_copy(const struct addrinfo *, const struct addrinfo *, struct addrinfo **); static int explore_null(const struct addrinfo *, const char *, struct addrinfo **); static int explore_numeric(const struct addrinfo *, const char *, const char *, struct addrinfo **, const char *); static int explore_numeric_scope(const struct addrinfo *, const char *, const char *, struct addrinfo **); static int get_canonname(const struct addrinfo *, struct addrinfo *, const char *); static struct addrinfo *get_ai(const struct addrinfo *, const struct afd *, const char *); static struct addrinfo *copy_ai(const struct addrinfo *); static int get_portmatch(const struct addrinfo *, const char *); static int get_port(struct addrinfo *, const char *, int); static const struct afd *find_afd(int); static int addrconfig(struct addrinfo *); #ifdef INET6 static int is_ifdisabled(char *); #endif static void set_source(struct ai_order *, struct policyhead *); static int comp_dst(const void *, const void *); #ifdef INET6 static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); #endif static int gai_addr2scopetype(struct sockaddr *); static int explore_fqdn(const struct addrinfo *, const char *, const char *, struct addrinfo **); static int reorder(struct addrinfo *); static int get_addrselectpolicy(struct policyhead *); static void free_addrselectpolicy(struct policyhead *); static struct policyqueue *match_addrselectpolicy(struct sockaddr *, struct policyhead *); static int matchlen(struct sockaddr *, struct sockaddr *); static struct addrinfo *getanswer(const querybuf *, int, const char *, int, const struct addrinfo *, res_state); #if defined(RESOLVSORT) static int addr4sort(struct addrinfo *, res_state); #endif static int _dns_getaddrinfo(void *, void *, va_list); static void _sethtent(FILE **); static void _endhtent(FILE **); static struct addrinfo *_gethtent(FILE **, const char *, const struct addrinfo *); static int _files_getaddrinfo(void *, void *, va_list); #ifdef YP static struct addrinfo *_yphostent(char *, const struct addrinfo *); static int _yp_getaddrinfo(void *, void *, va_list); #endif #ifdef NS_CACHING static int addrinfo_id_func(char *, size_t *, va_list, void *); static int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *); static int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *); #endif static int res_queryN(const char *, struct res_target *, res_state); static int res_searchN(const char *, struct res_target *, res_state); static int res_querydomainN(const char *, const char *, struct res_target *, res_state); /* XXX macros that make external reference is BAD. */ #define GET_AI(ai, afd, addr) \ do { \ /* external reference: pai, error, and label free */ \ (ai) = get_ai(pai, (afd), (addr)); \ if ((ai) == NULL) { \ error = EAI_MEMORY; \ goto free; \ } \ } while (/*CONSTCOND*/0) #define GET_PORT(ai, serv) \ do { \ /* external reference: error and label free */ \ error = get_port((ai), (serv), 0); \ if (error != 0) \ goto free; \ } while (/*CONSTCOND*/0) #define GET_CANONNAME(ai, str) \ do { \ /* external reference: pai, error and label free */ \ error = get_canonname(pai, (ai), (str)); \ if (error != 0) \ goto free; \ } while (/*CONSTCOND*/0) #define ERR(err) \ do { \ /* external reference: error, and label bad */ \ error = (err); \ goto bad; \ /*NOTREACHED*/ \ } while (/*CONSTCOND*/0) #define MATCH_FAMILY(x, y, w) \ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) #define MATCH(x, y, w) \ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) void freeaddrinfo(struct addrinfo *ai) { struct addrinfo *next; do { next = ai->ai_next; if (ai->ai_canonname) free(ai->ai_canonname); /* no need to free(ai->ai_addr) */ free(ai); ai = next; } while (ai); } static int str2number(const char *p, int *portp) { char *ep; unsigned long v; if (*p == '\0') return -1; ep = NULL; errno = 0; v = strtoul(p, &ep, 10); if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) { *portp = v; return 0; } else return -1; } int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo sentinel; struct addrinfo *cur; int error = 0; struct addrinfo ai, ai0, *afai; struct addrinfo *pai; const struct afd *afd; const struct explore *ex; struct addrinfo *afailist[nitems(afdl)]; struct addrinfo *afai_unspec; int found; int numeric = 0; /* ensure we return NULL on errors */ *res = NULL; memset(&ai, 0, sizeof(ai)); memset(afailist, 0, sizeof(afailist)); afai_unspec = NULL; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; pai = &ai; pai->ai_flags = 0; pai->ai_family = PF_UNSPEC; pai->ai_socktype = ANY; pai->ai_protocol = ANY; pai->ai_addrlen = 0; pai->ai_canonname = NULL; pai->ai_addr = NULL; pai->ai_next = NULL; if (hostname == NULL && servname == NULL) return EAI_NONAME; if (hints) { /* error check for hints */ if (hints->ai_addrlen || hints->ai_canonname || hints->ai_addr || hints->ai_next) ERR(EAI_BADHINTS); /* xxx */ if (hints->ai_flags & ~AI_MASK) ERR(EAI_BADFLAGS); switch (hints->ai_family) { case PF_UNSPEC: case PF_LOCAL: case PF_INET: #ifdef INET6 case PF_INET6: #endif break; default: ERR(EAI_FAMILY); } memcpy(pai, hints, sizeof(*pai)); /* * if both socktype/protocol are specified, check if they * are meaningful combination. */ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { for (ex = explore; ex->e_af >= 0; ex++) { if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) continue; if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) continue; /* matched */ break; } if (ex->e_af < 0) ERR(EAI_BADHINTS); } } /* * RFC 3493: AI_ALL and AI_V4MAPPED are effective only against * AF_INET6 query. They need to be ignored if specified in other - * occassions. + * occasions. */ switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { case AI_V4MAPPED: case AI_ALL | AI_V4MAPPED: #ifdef INET6 if (pai->ai_family != AF_INET6) pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); break; #endif case AI_ALL: pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); break; } /* * check for special cases. (1) numeric servname is disallowed if * socktype/protocol are left unspecified. (2) servname is disallowed * for raw and other inet{,6} sockets. */ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) #ifdef PF_INET6 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) #endif ) { ai0 = *pai; /* backup *pai */ if (pai->ai_family == PF_UNSPEC) { #ifdef PF_INET6 pai->ai_family = PF_INET6; #else pai->ai_family = PF_INET; #endif } error = get_portmatch(pai, servname); if (error) goto bad; *pai = ai0; } ai0 = *pai; /* * NULL hostname, or numeric hostname. * If numeric representation of AF1 can be interpreted as FQDN * representation of AF2, we need to think again about the code below. */ found = 0; for (afd = afdl; afd->a_af; afd++) { *pai = ai0; if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) continue; if (pai->ai_family == PF_UNSPEC) pai->ai_family = afd->a_af; if (hostname == NULL) { error = explore_null(pai, servname, &afailist[afd - afdl]); /* * Errors from explore_null should be unexpected and * be caught to avoid returning an incomplete result. */ if (error != 0) goto bad; } else { error = explore_numeric_scope(pai, hostname, servname, &afailist[afd - afdl]); /* * explore_numeric_scope returns an error for address * families that do not match that of hostname. * Thus we should not catch the error at this moment. */ } if (!error && afailist[afd - afdl]) found++; } if (found) { numeric = 1; goto globcopy; } if (hostname == NULL) ERR(EAI_NONAME); /* used to be EAI_NODATA */ if (pai->ai_flags & AI_NUMERICHOST) ERR(EAI_NONAME); if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) ERR(EAI_FAIL); /* * hostname as alphabetical name. */ *pai = ai0; error = explore_fqdn(pai, hostname, servname, &afai_unspec); globcopy: for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) continue; if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) continue; if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af; if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol; /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) continue; if (afai_unspec) afai = afai_unspec; else { if ((afd = find_afd(pai->ai_family)) == NULL) continue; /* XXX assumes that afd points inside afdl[] */ afai = afailist[afd - afdl]; } if (!afai) continue; error = explore_copy(pai, afai, &cur->ai_next); if (error != 0) goto bad; while (cur && cur->ai_next) cur = cur->ai_next; } /* * ensure we return either: * - error == 0, non-NULL *res * - error != 0, NULL *res */ if (error == 0) { if (sentinel.ai_next) { /* * If the returned entry is for an active connection, * and the given name is not numeric, reorder the * list, so that the application would try the list * in the most efficient order. Since the head entry * of the original list may contain ai_canonname and * that entry may be moved elsewhere in the new list, * we keep the pointer and will restore it in the new * head entry. (Note that RFC3493 requires the head * entry store it when requested by the caller). */ if (hints == NULL || !(hints->ai_flags & AI_PASSIVE)) { if (!numeric) { char *canonname; canonname = sentinel.ai_next->ai_canonname; sentinel.ai_next->ai_canonname = NULL; (void)reorder(&sentinel); if (sentinel.ai_next->ai_canonname == NULL) { sentinel.ai_next->ai_canonname = canonname; } else if (canonname != NULL) free(canonname); } } *res = sentinel.ai_next; } else error = EAI_FAIL; } bad: if (afai_unspec) freeaddrinfo(afai_unspec); for (afd = afdl; afd->a_af; afd++) { if (afailist[afd - afdl]) freeaddrinfo(afailist[afd - afdl]); } if (!*res) if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); return (error); } static int reorder(struct addrinfo *sentinel) { struct addrinfo *ai, **aip; struct ai_order *aio; int i, n; struct policyhead policyhead; /* count the number of addrinfo elements for sorting. */ for (n = 0, ai = sentinel->ai_next; ai != NULL; ai = ai->ai_next, n++) ; /* * If the number is small enough, we can skip the reordering process. */ if (n <= 1) return(n); /* allocate a temporary array for sort and initialization of it. */ if ((aio = malloc(sizeof(*aio) * n)) == NULL) return(n); /* give up reordering */ memset(aio, 0, sizeof(*aio) * n); /* retrieve address selection policy from the kernel */ TAILQ_INIT(&policyhead); if (!get_addrselectpolicy(&policyhead)) { /* no policy is installed into kernel, we don't sort. */ free(aio); return (n); } for (i = 0, ai = sentinel->ai_next; i < n; ai = ai->ai_next, i++) { aio[i].aio_ai = ai; aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr); aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr, &policyhead); set_source(&aio[i], &policyhead); } /* perform sorting. */ qsort(aio, n, sizeof(*aio), comp_dst); /* reorder the addrinfo chain. */ for (i = 0, aip = &sentinel->ai_next; i < n; i++) { *aip = aio[i].aio_ai; aip = &aio[i].aio_ai->ai_next; } *aip = NULL; /* cleanup and return */ free(aio); free_addrselectpolicy(&policyhead); return(n); } static int get_addrselectpolicy(struct policyhead *head) { #ifdef INET6 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; size_t l; char *buf; struct in6_addrpolicy *pol, *ep; if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) return (0); if (l == 0) return (0); if ((buf = malloc(l)) == NULL) return (0); if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { free(buf); return (0); } ep = (struct in6_addrpolicy *)(buf + l); for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { struct policyqueue *new; if ((new = malloc(sizeof(*new))) == NULL) { free_addrselectpolicy(head); /* make the list empty */ break; } new->pc_policy = *pol; TAILQ_INSERT_TAIL(head, new, pc_entry); } free(buf); return (1); #else return (0); #endif } static void free_addrselectpolicy(struct policyhead *head) { struct policyqueue *ent, *nent; for (ent = TAILQ_FIRST(head); ent; ent = nent) { nent = TAILQ_NEXT(ent, pc_entry); TAILQ_REMOVE(head, ent, pc_entry); free(ent); } } static struct policyqueue * match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) { #ifdef INET6 struct policyqueue *ent, *bestent = NULL; struct in6_addrpolicy *pol; int matchlen, bestmatchlen = -1; u_char *mp, *ep, *k, *p, m; struct sockaddr_in6 key; switch(addr->sa_family) { case AF_INET6: key = *(struct sockaddr_in6 *)addr; break; case AF_INET: /* convert the address into IPv4-mapped IPv6 address. */ memset(&key, 0, sizeof(key)); key.sin6_family = AF_INET6; key.sin6_len = sizeof(key); _map_v4v6_address( (char *)&((struct sockaddr_in *)addr)->sin_addr, (char *)&key.sin6_addr); break; default: return(NULL); } for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { pol = &ent->pc_policy; matchlen = 0; mp = (u_char *)&pol->addrmask.sin6_addr; ep = mp + 16; /* XXX: scope field? */ k = (u_char *)&key.sin6_addr; p = (u_char *)&pol->addr.sin6_addr; for (; mp < ep && *mp; mp++, k++, p++) { m = *mp; if ((*k & m) != *p) goto next; /* not match */ if (m == 0xff) /* short cut for a typical case */ matchlen += 8; else { while (m >= 0x80) { matchlen++; m <<= 1; } } } /* matched. check if this is better than the current best. */ if (matchlen > bestmatchlen) { bestent = ent; bestmatchlen = matchlen; } next: continue; } return(bestent); #else return(NULL); #endif } static void set_source(struct ai_order *aio, struct policyhead *ph) { struct addrinfo ai = *aio->aio_ai; struct sockaddr_storage ss; socklen_t srclen; int s; /* set unspec ("no source is available"), just in case */ aio->aio_srcsa.sa_family = AF_UNSPEC; aio->aio_srcscope = -1; switch(ai.ai_family) { case AF_INET: #ifdef INET6 case AF_INET6: #endif break; default: /* ignore unsupported AFs explicitly */ return; } /* XXX: make a dummy addrinfo to call connect() */ ai.ai_socktype = SOCK_DGRAM; ai.ai_protocol = IPPROTO_UDP; /* is UDP too specific? */ ai.ai_next = NULL; memset(&ss, 0, sizeof(ss)); memcpy(&ss, ai.ai_addr, ai.ai_addrlen); ai.ai_addr = (struct sockaddr *)&ss; get_port(&ai, "1", 0); /* open a socket to get the source address for the given dst */ if ((s = _socket(ai.ai_family, ai.ai_socktype | SOCK_CLOEXEC, ai.ai_protocol)) < 0) return; /* give up */ #ifdef INET6 if (ai.ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai.ai_addr; int off = 0; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) (void)_setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off)); } #endif if (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) goto cleanup; srclen = ai.ai_addrlen; if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { aio->aio_srcsa.sa_family = AF_UNSPEC; goto cleanup; } aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); aio->aio_matchlen = matchlen(&aio->aio_srcsa, aio->aio_ai->ai_addr); #ifdef INET6 if (ai.ai_family == AF_INET6) { struct in6_ifreq ifr6; u_int32_t flags6; memset(&ifr6, 0, sizeof(ifr6)); memcpy(&ifr6.ifr_addr, ai.ai_addr, ai.ai_addrlen); if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { flags6 = ifr6.ifr_ifru.ifru_flags6; if ((flags6 & IN6_IFF_DEPRECATED)) aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; } } #endif cleanup: _close(s); return; } static int matchlen(struct sockaddr *src, struct sockaddr *dst) { int match = 0; u_char *s, *d; u_char *lim, r; int addrlen; switch (src->sa_family) { #ifdef INET6 case AF_INET6: s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; addrlen = sizeof(struct in6_addr); lim = s + addrlen; break; #endif case AF_INET: s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; addrlen = sizeof(struct in_addr); lim = s + addrlen; break; default: return(0); } while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < addrlen * 8) { match++; r <<= 1; } break; } else match += 8; return(match); } static int comp_dst(const void *arg1, const void *arg2) { const struct ai_order *dst1 = arg1, *dst2 = arg2; /* * Rule 1: Avoid unusable destinations. * XXX: we currently do not consider if an appropriate route exists. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family == AF_UNSPEC) { return(-1); } if (dst1->aio_srcsa.sa_family == AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { return(1); } /* Rule 2: Prefer matching scope. */ if (dst1->aio_dstscope == dst1->aio_srcscope && dst2->aio_dstscope != dst2->aio_srcscope) { return(-1); } if (dst1->aio_dstscope != dst1->aio_srcscope && dst2->aio_dstscope == dst2->aio_srcscope) { return(1); } /* Rule 3: Avoid deprecated addresses. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(-1); } if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(1); } } /* Rule 4: Prefer home addresses. */ /* XXX: not implemented yet */ /* Rule 5: Prefer matching label. */ #ifdef INET6 if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && dst1->aio_srcpolicy->pc_policy.label == dst1->aio_dstpolicy->pc_policy.label && (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || dst2->aio_srcpolicy->pc_policy.label != dst2->aio_dstpolicy->pc_policy.label)) { return(-1); } if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && dst2->aio_srcpolicy->pc_policy.label == dst2->aio_dstpolicy->pc_policy.label && (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || dst1->aio_srcpolicy->pc_policy.label != dst1->aio_dstpolicy->pc_policy.label)) { return(1); } #endif /* Rule 6: Prefer higher precedence. */ #ifdef INET6 if (dst1->aio_dstpolicy && (dst2->aio_dstpolicy == NULL || dst1->aio_dstpolicy->pc_policy.preced > dst2->aio_dstpolicy->pc_policy.preced)) { return(-1); } if (dst2->aio_dstpolicy && (dst1->aio_dstpolicy == NULL || dst2->aio_dstpolicy->pc_policy.preced > dst1->aio_dstpolicy->pc_policy.preced)) { return(1); } #endif /* Rule 7: Prefer native transport. */ /* XXX: not implemented yet */ /* Rule 8: Prefer smaller scope. */ if (dst1->aio_dstscope >= 0 && dst1->aio_dstscope < dst2->aio_dstscope) { return(-1); } if (dst2->aio_dstscope >= 0 && dst2->aio_dstscope < dst1->aio_dstscope) { return(1); } /* * Rule 9: Use longest matching prefix. * We compare the match length in a same AF only. */ if (dst1->aio_ai->ai_addr->sa_family == dst2->aio_ai->ai_addr->sa_family && dst1->aio_ai->ai_addr->sa_family != AF_INET) { if (dst1->aio_matchlen > dst2->aio_matchlen) { return(-1); } if (dst1->aio_matchlen < dst2->aio_matchlen) { return(1); } } /* Rule 10: Otherwise, leave the order unchanged. */ return(-1); } /* * Copy from scope.c. * XXX: we should standardize the functions and link them as standard * library. */ static int gai_addr2scopetype(struct sockaddr *sa) { #ifdef INET6 struct sockaddr_in6 *sa6; #endif struct sockaddr_in *sa4; switch(sa->sa_family) { #ifdef INET6 case AF_INET6: sa6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { /* just use the scope field of the multicast address */ return(sa6->sin6_addr.s6_addr[2] & 0x0f); } /* * Unicast addresses: map scope type to corresponding scope * value defined for multcast addresses. * XXX: hardcoded scope type values are bad... */ if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) return(1); /* node local scope */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) return(2); /* link-local scope */ if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) return(5); /* site-local scope */ return(14); /* global scope */ break; #endif case AF_INET: /* * IPv4 pseudo scoping according to RFC 3484. */ sa4 = (struct sockaddr_in *)sa; /* IPv4 autoconfiguration addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 169 && ((u_char *)&sa4->sin_addr)[1] == 254) return(2); /* Private addresses have site-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 10 || (((u_char *)&sa4->sin_addr)[0] == 172 && (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || (((u_char *)&sa4->sin_addr)[0] == 192 && ((u_char *)&sa4->sin_addr)[1] == 168)) return(14); /* XXX: It should be 5 unless NAT */ /* Loopback addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 127) return(2); return(14); break; default: errno = EAFNOSUPPORT; /* is this a good error? */ return(-1); } } static int explore_copy(const struct addrinfo *pai, const struct addrinfo *src0, struct addrinfo **res) { int error; struct addrinfo sentinel, *cur; const struct addrinfo *src; error = 0; sentinel.ai_next = NULL; cur = &sentinel; for (src = src0; src != NULL; src = src->ai_next) { if (src->ai_family != pai->ai_family) continue; cur->ai_next = copy_ai(src); if (!cur->ai_next) { error = EAI_MEMORY; goto fail; } cur->ai_next->ai_socktype = pai->ai_socktype; cur->ai_next->ai_protocol = pai->ai_protocol; cur = cur->ai_next; } *res = sentinel.ai_next; return 0; fail: freeaddrinfo(sentinel.ai_next); return error; } /* * hostname == NULL. * passive socket -> anyaddr (0.0.0.0 or ::) * non-passive socket -> localhost (127.0.0.1 or ::1) */ static int explore_null(const struct addrinfo *pai, const char *servname, struct addrinfo **res) { int s; const struct afd *afd; struct addrinfo *ai; int error; *res = NULL; ai = NULL; if (pai->ai_family == PF_LOCAL) return (0); /* * filter out AFs that are not supported by the kernel * XXX errno? */ s = _socket(pai->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (s < 0) { if (errno != EMFILE) return 0; } else _close(s); afd = find_afd(pai->ai_family); if (afd == NULL) return 0; if (pai->ai_flags & AI_PASSIVE) { GET_AI(ai, afd, afd->a_addrany); GET_PORT(ai, servname); } else { GET_AI(ai, afd, afd->a_loopback); GET_PORT(ai, servname); } *res = ai; return 0; free: if (ai != NULL) freeaddrinfo(ai); return error; } /* * numeric hostname */ static int explore_numeric(const struct addrinfo *pai, const char *hostname, const char *servname, struct addrinfo **res, const char *canonname) { const struct afd *afd; struct addrinfo *ai, ai0; int error; char pton[PTON_MAX], path[PATH_MAX], *p; #ifdef CTASSERT CTASSERT(sizeofmember(struct sockaddr_un, sun_path) <= PATH_MAX); #endif *res = NULL; ai = NULL; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; switch (afd->a_af) { case AF_LOCAL: if (hostname[0] != '/') ERR(EAI_NONAME); if (strlen(hostname) > afd->a_addrlen) ERR(EAI_MEMORY); /* NUL-termination does not need to be guaranteed. */ strncpy(path, hostname, afd->a_addrlen); p = &path[0]; break; case AF_INET: /* * RFC3493 requires getaddrinfo() to accept AF_INET formats * that are accepted by inet_addr() and its family. The * accepted forms includes the "classful" one, which inet_pton * does not accept. So we need to separate the case for * AF_INET. */ if (inet_aton(hostname, (struct in_addr *)pton) != 1) return 0; p = pton; break; default: if (inet_pton(afd->a_af, hostname, pton) != 1) { if (pai->ai_family != AF_INET6 || (pai->ai_flags & AI_V4MAPPED) != AI_V4MAPPED) return 0; if (inet_aton(hostname, (struct in_addr *)pton) != 1) return 0; afd = &afdl[N_INET]; ai0 = *pai; ai0.ai_family = AF_INET; pai = &ai0; } p = pton; break; } if (pai->ai_family == afd->a_af) { GET_AI(ai, afd, p); GET_PORT(ai, servname); if ((pai->ai_family == AF_INET || pai->ai_family == AF_INET6) && (pai->ai_flags & AI_CANONNAME)) { /* * Set the numeric address itself as the canonical * name, based on a clarification in RFC3493. */ GET_CANONNAME(ai, canonname); } } else { /* * XXX: This should not happen since we already matched the AF * by find_afd. */ ERR(EAI_FAMILY); } *res = ai; return 0; free: bad: if (ai != NULL) freeaddrinfo(ai); return error; } /* * numeric hostname with scope */ static int explore_numeric_scope(const struct addrinfo *pai, const char *hostname, const char *servname, struct addrinfo **res) { #if !defined(SCOPE_DELIMITER) || !defined(INET6) return explore_numeric(pai, hostname, servname, res, hostname); #else const struct afd *afd; struct addrinfo *cur; int error; char *cp, *hostname2 = NULL, *scope, *addr; struct sockaddr_in6 *sin6; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; if (!afd->a_scoped) return explore_numeric(pai, hostname, servname, res, hostname); cp = strchr(hostname, SCOPE_DELIMITER); if (cp == NULL) return explore_numeric(pai, hostname, servname, res, hostname); /* * Handle special case of */ hostname2 = strdup(hostname); if (hostname2 == NULL) return EAI_MEMORY; /* terminate at the delimiter */ hostname2[cp - hostname] = '\0'; addr = hostname2; scope = cp + 1; error = explore_numeric(pai, addr, servname, res, hostname); if (error == 0) { u_int32_t scopeid; for (cur = *res; cur; cur = cur->ai_next) { if (cur->ai_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { free(hostname2); freeaddrinfo(*res); *res = NULL; return(EAI_NONAME); /* XXX: is return OK? */ } sin6->sin6_scope_id = scopeid; } } free(hostname2); if (error && *res) { freeaddrinfo(*res); *res = NULL; } return error; #endif } static int get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) { if ((pai->ai_flags & AI_CANONNAME) != 0) { ai->ai_canonname = strdup(str); if (ai->ai_canonname == NULL) return EAI_MEMORY; } return 0; } static struct addrinfo * get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) { char *p; struct addrinfo *ai; #ifdef INET6 struct in6_addr mapaddr; if (afd->a_af == AF_INET && (pai->ai_flags & AI_V4MAPPED) != 0) { afd = &afdl[N_INET6]; _map_v4v6_address(addr, (char *)&mapaddr); addr = (char *)&mapaddr; } #endif ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + (afd->a_socklen)); if (ai == NULL) return NULL; memcpy(ai, pai, sizeof(struct addrinfo)); ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); memset(ai->ai_addr, 0, (size_t)afd->a_socklen); ai->ai_addr->sa_len = afd->a_socklen; ai->ai_addrlen = afd->a_socklen; if (ai->ai_family == PF_LOCAL) { size_t n = strnlen(addr, afd->a_addrlen); ai->ai_addrlen -= afd->a_addrlen - n; ai->ai_addr->sa_len -= afd->a_addrlen - n; } ai->ai_addr->sa_family = ai->ai_family = afd->a_af; p = (char *)(void *)(ai->ai_addr); memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); return ai; } /* XXX need to malloc() the same way we do from other functions! */ static struct addrinfo * copy_ai(const struct addrinfo *pai) { struct addrinfo *ai; size_t l; l = sizeof(*ai) + pai->ai_addrlen; if ((ai = (struct addrinfo *)malloc(l)) == NULL) return NULL; memset(ai, 0, l); memcpy(ai, pai, sizeof(*ai)); ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen); if (pai->ai_canonname) { l = strlen(pai->ai_canonname) + 1; if ((ai->ai_canonname = malloc(l)) == NULL) { free(ai); return NULL; } strlcpy(ai->ai_canonname, pai->ai_canonname, l); } else { /* just to make sure */ ai->ai_canonname = NULL; } ai->ai_next = NULL; return ai; } static int get_portmatch(const struct addrinfo *ai, const char *servname) { /* get_port does not touch first argument when matchonly == 1. */ /* LINTED const cast */ return get_port((struct addrinfo *)ai, servname, 1); } static int get_port(struct addrinfo *ai, const char *servname, int matchonly) { const char *proto; struct servent *sp; int port, error; int allownumeric; if (servname == NULL) return 0; switch (ai->ai_family) { case AF_LOCAL: /* AF_LOCAL ignores servname silently. */ return (0); case AF_INET: #ifdef AF_INET6 case AF_INET6: #endif break; default: return 0; } switch (ai->ai_socktype) { case SOCK_RAW: return EAI_SERVICE; case SOCK_DGRAM: case SOCK_STREAM: case SOCK_SEQPACKET: allownumeric = 1; break; case ANY: switch (ai->ai_family) { case AF_INET: #ifdef AF_INET6 case AF_INET6: #endif allownumeric = 1; break; default: allownumeric = 0; break; } break; default: return EAI_SOCKTYPE; } error = str2number(servname, &port); if (error == 0) { if (!allownumeric) return EAI_SERVICE; if (port < 0 || port > 65535) return EAI_SERVICE; port = htons(port); } else { if (ai->ai_flags & AI_NUMERICSERV) return EAI_NONAME; switch (ai->ai_protocol) { case IPPROTO_UDP: proto = "udp"; break; case IPPROTO_TCP: proto = "tcp"; break; case IPPROTO_SCTP: proto = "sctp"; break; case IPPROTO_UDPLITE: proto = "udplite"; break; default: proto = NULL; break; } if ((sp = getservbyname(servname, proto)) == NULL) return EAI_SERVICE; port = sp->s_port; } if (!matchonly) { switch (ai->ai_family) { case AF_INET: ((struct sockaddr_in *)(void *) ai->ai_addr)->sin_port = port; break; #ifdef INET6 case AF_INET6: ((struct sockaddr_in6 *)(void *) ai->ai_addr)->sin6_port = port; break; #endif } } return 0; } static const struct afd * find_afd(int af) { const struct afd *afd; if (af == PF_UNSPEC) return NULL; for (afd = afdl; afd->a_af; afd++) { if (afd->a_af == af) return afd; } return NULL; } /* * RFC 3493: AI_ADDRCONFIG check. Determines which address families are * configured on the local system and correlates with pai->ai_family value. * If an address family is not configured on the system, it will not be * queried for. For this purpose, loopback addresses are not considered * configured addresses. * * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with * _dns_getaddrinfo. */ static int addrconfig(struct addrinfo *pai) { struct ifaddrs *ifaddrs, *ifa; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif int seen_inet = 0, seen_inet6 = 0; if (getifaddrs(&ifaddrs) != 0) return (0); for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL || (ifa->ifa_flags & IFF_UP) == 0) continue; switch (ifa->ifa_addr->sa_family) { case AF_INET: if (seen_inet) continue; sin = (struct sockaddr_in *)(ifa->ifa_addr); if (htonl(sin->sin_addr.s_addr) == INADDR_LOOPBACK) continue; seen_inet = 1; break; #ifdef INET6 case AF_INET6: if (seen_inet6) continue; sin6 = (struct sockaddr_in6 *)(ifa->ifa_addr); if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) continue; if ((ifa->ifa_flags & IFT_LOOP) != 0 && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) continue; if (is_ifdisabled(ifa->ifa_name)) continue; seen_inet6 = 1; break; #endif } } freeifaddrs(ifaddrs); switch(pai->ai_family) { case AF_INET6: return (seen_inet6); case AF_INET: return (seen_inet); case AF_UNSPEC: if (seen_inet == seen_inet6) return (seen_inet); pai->ai_family = seen_inet ? AF_INET : AF_INET6; return (1); } return (1); } #ifdef INET6 static int is_ifdisabled(char *name) { struct in6_ndireq nd; int fd; if ((fd = _socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) return (-1); memset(&nd, 0, sizeof(nd)); strlcpy(nd.ifname, name, sizeof(nd.ifname)); if (_ioctl(fd, SIOCGIFINFO_IN6, &nd) < 0) { _close(fd); return (-1); } _close(fd); return ((nd.ndi.flags & ND6_IFF_IFDISABLED) != 0); } /* convert a string to a scope identifier. XXX: IPv6 specific */ static int ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) { u_long lscopeid; struct in6_addr *a6; char *ep; a6 = &sin6->sin6_addr; /* empty scopeid portion is invalid */ if (*scope == '\0') return -1; if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || IN6_IS_ADDR_MC_NODELOCAL(a6)) { /* * We currently assume a one-to-one mapping between links * and interfaces, so we simply use interface indices for * like-local scopes. */ *scopeid = if_nametoindex(scope); if (*scopeid == 0) goto trynumeric; return 0; } /* still unclear about literal, allow numeric only - placeholder */ if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) goto trynumeric; if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) goto trynumeric; else goto trynumeric; /* global */ /* try to convert to a numeric id as a last resort */ trynumeric: errno = 0; lscopeid = strtoul(scope, &ep, 10); *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) return 0; else return -1; } #endif #ifdef NS_CACHING static int addrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { res_state statp; u_long res_options; const int op_id = 0; /* identifies the getaddrinfo for the cache */ char *hostname; struct addrinfo *hints; char *p; int ai_flags, ai_family, ai_socktype, ai_protocol; size_t desired_size, size; statp = __res_state(); res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); hostname = va_arg(ap, char *); hints = va_arg(ap, struct addrinfo *); desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4; if (hostname != NULL) { size = strlen(hostname); desired_size += size + 1; } else size = 0; if (desired_size > *buffer_size) { *buffer_size = desired_size; return (NS_RETURN); } if (hints == NULL) ai_flags = ai_family = ai_socktype = ai_protocol = 0; else { ai_flags = hints->ai_flags; ai_family = hints->ai_family; ai_socktype = hints->ai_socktype; ai_protocol = hints->ai_protocol; } p = buffer; memcpy(p, &res_options, sizeof(res_options)); p += sizeof(res_options); memcpy(p, &op_id, sizeof(int)); p += sizeof(int); memcpy(p, &ai_flags, sizeof(int)); p += sizeof(int); memcpy(p, &ai_family, sizeof(int)); p += sizeof(int); memcpy(p, &ai_socktype, sizeof(int)); p += sizeof(int); memcpy(p, &ai_protocol, sizeof(int)); p += sizeof(int); if (hostname != NULL) memcpy(p, hostname, size); *buffer_size = desired_size; return (NS_SUCCESS); } static int addrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { struct addrinfo *ai, *cai; char *p; size_t desired_size, size, ai_size; ai = *((struct addrinfo **)retval); desired_size = sizeof(size_t); ai_size = 0; for (cai = ai; cai != NULL; cai = cai->ai_next) { desired_size += sizeof(struct addrinfo) + cai->ai_addrlen; if (cai->ai_canonname != NULL) desired_size += sizeof(size_t) + strlen(cai->ai_canonname); ++ai_size; } if (desired_size > *buffer_size) { /* this assignment is here for future use */ errno = ERANGE; *buffer_size = desired_size; return (NS_RETURN); } memset(buffer, 0, desired_size); p = buffer; memcpy(p, &ai_size, sizeof(size_t)); p += sizeof(size_t); for (cai = ai; cai != NULL; cai = cai->ai_next) { memcpy(p, cai, sizeof(struct addrinfo)); p += sizeof(struct addrinfo); memcpy(p, cai->ai_addr, cai->ai_addrlen); p += cai->ai_addrlen; if (cai->ai_canonname != NULL) { size = strlen(cai->ai_canonname); memcpy(p, &size, sizeof(size_t)); p += sizeof(size_t); memcpy(p, cai->ai_canonname, size); p += size; } } return (NS_SUCCESS); } static int addrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { struct addrinfo new_ai, *result, *sentinel, *lasts; char *p; size_t ai_size, ai_i, size; p = buffer; memcpy(&ai_size, p, sizeof(size_t)); p += sizeof(size_t); result = NULL; lasts = NULL; for (ai_i = 0; ai_i < ai_size; ++ai_i) { memcpy(&new_ai, p, sizeof(struct addrinfo)); p += sizeof(struct addrinfo); size = new_ai.ai_addrlen + sizeof(struct addrinfo) + _ALIGNBYTES; sentinel = (struct addrinfo *)malloc(size); memset(sentinel, 0, size); memcpy(sentinel, &new_ai, sizeof(struct addrinfo)); sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel + sizeof(struct addrinfo)); memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen); p += new_ai.ai_addrlen; if (new_ai.ai_canonname != NULL) { memcpy(&size, p, sizeof(size_t)); p += sizeof(size_t); sentinel->ai_canonname = (char *)malloc(size + 1); memset(sentinel->ai_canonname, 0, size + 1); memcpy(sentinel->ai_canonname, p, size); p += size; } if (result == NULL) { result = sentinel; lasts = sentinel; } else { lasts->ai_next = sentinel; lasts = sentinel; } } *((struct addrinfo **)retval) = result; return (NS_SUCCESS); } #endif /* NS_CACHING */ /* * FQDN hostname, DNS lookup */ static int explore_fqdn(const struct addrinfo *pai, const char *hostname, const char *servname, struct addrinfo **res) { struct addrinfo *result; struct addrinfo *cur; int error = 0; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( hosts, NULL, addrinfo_id_func, addrinfo_marshal_func, addrinfo_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_files_getaddrinfo, NULL) { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ NS_NIS_CB(_yp_getaddrinfo, NULL) #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; result = NULL; /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) return 0; switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", default_dns_files, hostname, pai)) { case NS_TRYAGAIN: error = EAI_AGAIN; goto free; case NS_UNAVAIL: error = EAI_FAIL; goto free; case NS_NOTFOUND: error = EAI_NONAME; goto free; case NS_SUCCESS: error = 0; for (cur = result; cur; cur = cur->ai_next) { GET_PORT(cur, servname); /* canonname should be filled already */ } break; } *res = result; return 0; free: if (result) freeaddrinfo(result); return error; } #ifdef DEBUG static const char AskedForGot[] = "gethostby*.getanswer: asked for \"%s\", got \"%s\""; #endif static struct addrinfo * getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, const struct addrinfo *pai, res_state res) { struct addrinfo sentinel, *cur; struct addrinfo ai; const struct afd *afd; char *canonname; const HEADER *hp; const u_char *cp; int n; const u_char *eom; char *bp, *ep; int type, class, ancount, qdcount; int haveanswer, had_error; char tbuf[MAXDNAME]; int (*name_ok)(const char *); char hostbuf[8*1024]; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; canonname = NULL; eom = answer->buf + anslen; switch (qtype) { case T_A: case T_AAAA: case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ name_ok = res_hnok; break; default: return (NULL); /* XXX should be abort(); */ } /* * find first satisfactory answer */ hp = &answer->hdr; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); bp = hostbuf; ep = hostbuf + sizeof hostbuf; cp = answer->buf + HFIXEDSZ; if (qdcount != 1) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (NULL); } n = dn_expand(answer->buf, eom, cp, bp, ep - bp); if ((n < 0) || !(*name_ok)(bp)) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (NULL); } cp += n + QFIXEDSZ; if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { /* res_send() has already verified that the query name is the * same as the one we sent; this just gets the expanded name * (i.e., with the succeeding search-domain tacked on). */ n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (NULL); } canonname = bp; bp += n; /* The qname can be abbreviated, but h_name is now absolute. */ qname = canonname; } haveanswer = 0; had_error = 0; while (ancount-- > 0 && cp < eom && !had_error) { n = dn_expand(answer->buf, eom, cp, bp, ep - bp); if ((n < 0) || !(*name_ok)(bp)) { had_error++; continue; } cp += n; /* name */ type = _getshort(cp); cp += INT16SZ; /* type */ class = _getshort(cp); cp += INT16SZ + INT32SZ; /* class, TTL */ n = _getshort(cp); cp += INT16SZ; /* len */ if (class != C_IN) { /* XXX - debug? syslog? */ cp += n; continue; /* XXX - had_error++ ? */ } if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && type == T_CNAME) { n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); if ((n < 0) || !(*name_ok)(tbuf)) { had_error++; continue; } cp += n; /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > ep - bp || n >= MAXHOSTNAMELEN) { had_error++; continue; } strlcpy(bp, tbuf, ep - bp); canonname = bp; bp += n; continue; } if (qtype == T_ANY) { if (!(type == T_A || type == T_AAAA)) { cp += n; continue; } } else if (type != qtype) { #ifdef DEBUG if (type != T_KEY && type != T_SIG && type != ns_t_dname) syslog(LOG_NOTICE|LOG_AUTH, "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", qname, p_class(C_IN), p_type(qtype), p_type(type)); #endif cp += n; continue; /* XXX - had_error++ ? */ } switch (type) { case T_A: case T_AAAA: if (strcasecmp(canonname, bp) != 0) { #ifdef DEBUG syslog(LOG_NOTICE|LOG_AUTH, AskedForGot, canonname, bp); #endif cp += n; continue; /* XXX - had_error++ ? */ } if (type == T_A && n != INADDRSZ) { cp += n; continue; } if (type == T_AAAA && n != IN6ADDRSZ) { cp += n; continue; } #ifdef FILTER_V4MAPPED if (type == T_AAAA) { struct in6_addr in6; memcpy(&in6, cp, sizeof(in6)); if (IN6_IS_ADDR_V4MAPPED(&in6)) { cp += n; continue; } } #endif if (!haveanswer) { int nn; canonname = bp; nn = strlen(bp) + 1; /* for the \0 */ bp += nn; } /* don't overwrite pai */ ai = *pai; ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; afd = find_afd(ai.ai_family); if (afd == NULL) { cp += n; continue; } cur->ai_next = get_ai(&ai, afd, (const char *)cp); if (cur->ai_next == NULL) had_error++; while (cur && cur->ai_next) cur = cur->ai_next; cp += n; break; default: abort(); } if (!had_error) haveanswer++; } if (haveanswer) { #if defined(RESOLVSORT) /* * We support only IPv4 address for backward * compatibility against gethostbyname(3). */ if (res->nsort && qtype == T_A) { if (addr4sort(&sentinel, res) < 0) { freeaddrinfo(sentinel.ai_next); RES_SET_H_ERRNO(res, NO_RECOVERY); return NULL; } } #endif /*RESOLVSORT*/ if (!canonname) (void)get_canonname(pai, sentinel.ai_next, qname); else (void)get_canonname(pai, sentinel.ai_next, canonname); RES_SET_H_ERRNO(res, NETDB_SUCCESS); return sentinel.ai_next; } /* * We could have walked a CNAME chain, but the ultimate target * may not have what we looked for. */ RES_SET_H_ERRNO(res, ntohs(hp->ancount) > 0 ? NO_DATA : NO_RECOVERY); return NULL; } #ifdef RESOLVSORT struct addr_ptr { struct addrinfo *ai; int aval; }; static int addr4sort(struct addrinfo *sentinel, res_state res) { struct addrinfo *ai; struct addr_ptr *addrs, addr; struct sockaddr_in *sin; int naddrs, i, j; int needsort = 0; if (!sentinel) return -1; naddrs = 0; for (ai = sentinel->ai_next; ai; ai = ai->ai_next) naddrs++; if (naddrs < 2) return 0; /* We don't need sorting. */ if ((addrs = malloc(sizeof(struct addr_ptr) * naddrs)) == NULL) return -1; i = 0; for (ai = sentinel->ai_next; ai; ai = ai->ai_next) { sin = (struct sockaddr_in *)ai->ai_addr; for (j = 0; (unsigned)j < res->nsort; j++) { if (res->sort_list[j].addr.s_addr == (sin->sin_addr.s_addr & res->sort_list[j].mask)) break; } addrs[i].ai = ai; addrs[i].aval = j; if (needsort == 0 && i > 0 && j < addrs[i - 1].aval) needsort = i; i++; } if (!needsort) { free(addrs); return 0; } while (needsort < naddrs) { for (j = needsort - 1; j >= 0; j--) { if (addrs[j].aval > addrs[j+1].aval) { addr = addrs[j]; addrs[j] = addrs[j + 1]; addrs[j + 1] = addr; } else break; } needsort++; } ai = sentinel; for (i = 0; i < naddrs; ++i) { ai->ai_next = addrs[i].ai; ai = ai->ai_next; } ai->ai_next = NULL; free(addrs); return 0; } #endif /*RESOLVSORT*/ /*ARGSUSED*/ static int _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) { struct addrinfo *ai, ai0; querybuf *buf, *buf2; const char *hostname; const struct addrinfo *pai; struct addrinfo sentinel, *cur; struct res_target q, q2; res_state res; hostname = va_arg(ap, char *); pai = va_arg(ap, const struct addrinfo *); memset(&q, 0, sizeof(q)); memset(&q2, 0, sizeof(q2)); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; res = __res_state(); buf = malloc(sizeof(*buf)); if (!buf) { RES_SET_H_ERRNO(res, NETDB_INTERNAL); return NS_NOTFOUND; } buf2 = malloc(sizeof(*buf2)); if (!buf2) { free(buf); RES_SET_H_ERRNO(res, NETDB_INTERNAL); return NS_NOTFOUND; } if (pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) { ai0 = *pai; ai0.ai_family = AF_UNSPEC; pai = &ai0; } switch (pai->ai_family) { case AF_UNSPEC: q.name = hostname; q.qclass = C_IN; q.qtype = T_A; q.answer = buf->buf; q.anslen = sizeof(buf->buf); q.next = &q2; q2.name = hostname; q2.qclass = C_IN; q2.qtype = T_AAAA; q2.answer = buf2->buf; q2.anslen = sizeof(buf2->buf); break; case AF_INET: q.name = hostname; q.qclass = C_IN; q.qtype = T_A; q.answer = buf->buf; q.anslen = sizeof(buf->buf); break; case AF_INET6: q.name = hostname; q.qclass = C_IN; q.qtype = T_AAAA; q.answer = buf->buf; q.anslen = sizeof(buf->buf); break; default: free(buf); free(buf2); return NS_UNAVAIL; } if ((res->options & RES_INIT) == 0 && res_ninit(res) == -1) { RES_SET_H_ERRNO(res, NETDB_INTERNAL); free(buf); free(buf2); return NS_NOTFOUND; } if (res_searchN(hostname, &q, res) < 0) { free(buf); free(buf2); return NS_NOTFOUND; } /* prefer IPv6 */ if (q.next) { ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai, res); if (ai) { cur->ai_next = ai; while (cur && cur->ai_next) cur = cur->ai_next; } } if (!ai || pai->ai_family != AF_UNSPEC || (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) != AI_V4MAPPED) { ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); if (ai) cur->ai_next = ai; } free(buf); free(buf2); if (sentinel.ai_next == NULL) switch (res->res_h_errno) { case HOST_NOT_FOUND: case NO_DATA: return NS_NOTFOUND; case TRY_AGAIN: return NS_TRYAGAIN; default: return NS_UNAVAIL; } *((struct addrinfo **)rv) = sentinel.ai_next; return NS_SUCCESS; } static void _sethtent(FILE **hostf) { if (!*hostf) *hostf = fopen(_PATH_HOSTS, "re"); else rewind(*hostf); } static void _endhtent(FILE **hostf) { if (*hostf) { (void) fclose(*hostf); *hostf = NULL; } } static struct addrinfo * _gethtent(FILE **hostf, const char *name, const struct addrinfo *pai) { char *p; char *cp, *tname, *cname; struct addrinfo hints, *res0, *res; int error; const char *addr; char hostbuf[8*1024]; if (!*hostf && !(*hostf = fopen(_PATH_HOSTS, "re"))) return (NULL); again: if (!(p = fgets(hostbuf, sizeof hostbuf, *hostf))) return (NULL); if (*p == '#') goto again; cp = strpbrk(p, "#\n"); if (cp != NULL) *cp = '\0'; if (!(cp = strpbrk(p, " \t"))) goto again; *cp++ = '\0'; addr = p; cname = NULL; /* if this is not something we're looking for, skip it. */ while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } tname = cp; if (cname == NULL) cname = cp; if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = '\0'; if (strcasecmp(name, tname) == 0) goto found; } goto again; found: /* we should not glob socktype/protocol here */ memset(&hints, 0, sizeof(hints)); hints.ai_family = pai->ai_family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; hints.ai_flags = AI_NUMERICHOST; if (pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) hints.ai_flags |= AI_V4MAPPED; error = getaddrinfo(addr, "0", &hints, &res0); if (error) goto again; #ifdef FILTER_V4MAPPED /* XXX should check all items in the chain */ if (res0->ai_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) { freeaddrinfo(res0); goto again; } #endif for (res = res0; res; res = res->ai_next) { /* cover it up */ res->ai_flags = pai->ai_flags; res->ai_socktype = pai->ai_socktype; res->ai_protocol = pai->ai_protocol; if (pai->ai_flags & AI_CANONNAME) { if (get_canonname(pai, res, cname) != 0) { freeaddrinfo(res0); goto again; } } } return res0; } static struct addrinfo * _getht(FILE **hostf, const char *name, const struct addrinfo *pai, struct addrinfo *cur) { struct addrinfo *p; while ((p = _gethtent(hostf, name, pai)) != NULL) { cur->ai_next = p; while (cur && cur->ai_next) cur = cur->ai_next; } return (cur); } /*ARGSUSED*/ static int _files_getaddrinfo(void *rv, void *cb_data, va_list ap) { const char *name; const struct addrinfo *pai; struct addrinfo sentinel, *cur; FILE *hostf = NULL; name = va_arg(ap, char *); pai = va_arg(ap, struct addrinfo *); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; _sethtent(&hostf); if (pai->ai_family == AF_INET6 && (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) == AI_V4MAPPED) { struct addrinfo ai0 = *pai; ai0.ai_flags &= ~AI_V4MAPPED; cur = _getht(&hostf, name, &ai0, cur); if (sentinel.ai_next == NULL) { _sethtent(&hostf); ai0.ai_flags |= AI_V4MAPPED; cur = _getht(&hostf, name, &ai0, cur); } } else cur = _getht(&hostf, name, pai, cur); _endhtent(&hostf); *((struct addrinfo **)rv) = sentinel.ai_next; if (sentinel.ai_next == NULL) return NS_NOTFOUND; return NS_SUCCESS; } #ifdef YP /*ARGSUSED*/ static struct addrinfo * _yphostent(char *line, const struct addrinfo *pai) { struct addrinfo sentinel, *cur; struct addrinfo hints, *res, *res0; int error; char *p = line; const char *addr, *canonname; char *nextline; char *cp; addr = canonname = NULL; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; nextline: /* terminate line */ cp = strchr(p, '\n'); if (cp) { *cp++ = '\0'; nextline = cp; } else nextline = NULL; cp = strpbrk(p, " \t"); if (cp == NULL) { if (canonname == NULL) return (NULL); else goto done; } *cp++ = '\0'; addr = p; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (!canonname) canonname = cp; if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = '\0'; } hints = *pai; hints.ai_flags = AI_NUMERICHOST; if (pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) hints.ai_flags |= AI_V4MAPPED; error = getaddrinfo(addr, NULL, &hints, &res0); if (error == 0) { for (res = res0; res; res = res->ai_next) { /* cover it up */ res->ai_flags = pai->ai_flags; if (pai->ai_flags & AI_CANONNAME) (void)get_canonname(pai, res, canonname); } } else res0 = NULL; if (res0) { cur->ai_next = res0; while (cur && cur->ai_next) cur = cur->ai_next; } if (nextline) { p = nextline; goto nextline; } done: return sentinel.ai_next; } /*ARGSUSED*/ static int _yp_getaddrinfo(void *rv, void *cb_data, va_list ap) { struct addrinfo sentinel, *cur; struct addrinfo *ai = NULL; char *ypbuf; int ypbuflen, r; const char *name; const struct addrinfo *pai; char *ypdomain; if (_yp_check(&ypdomain) == 0) return NS_UNAVAIL; name = va_arg(ap, char *); pai = va_arg(ap, const struct addrinfo *); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; /* ipnodes.byname can hold both IPv4/v6 */ r = yp_match(ypdomain, "ipnodes.byname", name, (int)strlen(name), &ypbuf, &ypbuflen); if (r == 0) { ai = _yphostent(ypbuf, pai); if (ai) { cur->ai_next = ai; while (cur && cur->ai_next) cur = cur->ai_next; } free(ypbuf); } if (ai != NULL) { struct sockaddr_in6 *sin6; switch (ai->ai_family) { case AF_INET: goto done; case AF_INET6: sin6 = (struct sockaddr_in6 *)ai->ai_addr; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) goto done; break; } } /* hosts.byname is only for IPv4 (Solaris8) */ if (pai->ai_family == AF_UNSPEC || pai->ai_family == AF_INET || ((pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) && (ai == NULL || (pai->ai_flags & AI_ALL) == AI_ALL))) { r = yp_match(ypdomain, "hosts.byname", name, (int)strlen(name), &ypbuf, &ypbuflen); if (r == 0) { struct addrinfo ai4; ai4 = *pai; if (pai->ai_family == AF_UNSPEC) ai4.ai_family = AF_INET; ai = _yphostent(ypbuf, &ai4); if (ai) { cur->ai_next = ai; while (cur && cur->ai_next) cur = cur->ai_next; } free(ypbuf); } } done: if (sentinel.ai_next == NULL) { RES_SET_H_ERRNO(__res_state(), HOST_NOT_FOUND); return NS_NOTFOUND; } *((struct addrinfo **)rv) = sentinel.ai_next; return NS_SUCCESS; } #endif /* resolver logic */ /* * Formulate a normal query, send, and await answer. * Returned answer is placed in supplied buffer "answer". * Perform preliminary check of answer, returning success only * if no error is indicated and the answer count is nonzero. * Return the size of the response on success, -1 on error. * Error number is left in h_errno. * * Caller must parse answer and determine whether it answers the question. */ static int res_queryN(const char *name, struct res_target *target, res_state res) { u_char *buf; HEADER *hp; int n; u_int oflags; struct res_target *t; int rcode; int ancount; rcode = NOERROR; ancount = 0; buf = malloc(MAXPACKET); if (!buf) { RES_SET_H_ERRNO(res, NETDB_INTERNAL); return -1; } for (t = target; t; t = t->next) { int class, type; u_char *answer; int anslen; hp = (HEADER *)(void *)t->answer; /* make it easier... */ class = t->qclass; type = t->qtype; answer = t->answer; anslen = t->anslen; oflags = res->_flags; again: hp->rcode = NOERROR; /* default */ #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type); #endif n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, buf, MAXPACKET); if (n > 0 && (res->_flags & RES_F_EDNS0ERR) == 0 && (res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U) n = res_nopt(res, n, buf, MAXPACKET, anslen); if (n <= 0) { #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_query: mkquery failed\n"); #endif free(buf); RES_SET_H_ERRNO(res, NO_RECOVERY); return (n); } n = res_nsend(res, buf, n, answer, anslen); if (n < 0) { /* * if the query choked with EDNS0, retry * without EDNS0 */ if ((res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U && ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) { res->_flags |= RES_F_EDNS0ERR; if (res->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n"); goto again; } rcode = hp->rcode; /* record most recent error */ #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_query: send error\n"); #endif continue; } if (n > anslen) hp->rcode = FORMERR; /* XXX not very informative */ if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { rcode = hp->rcode; /* record most recent error */ #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; rcode = %u, ancount=%u\n", hp->rcode, ntohs(hp->ancount)); #endif continue; } ancount += ntohs(hp->ancount); t->n = n; } free(buf); if (ancount == 0) { switch (rcode) { case NXDOMAIN: RES_SET_H_ERRNO(res, HOST_NOT_FOUND); break; case SERVFAIL: RES_SET_H_ERRNO(res, TRY_AGAIN); break; case NOERROR: RES_SET_H_ERRNO(res, NO_DATA); break; case FORMERR: case NOTIMP: case REFUSED: default: RES_SET_H_ERRNO(res, NO_RECOVERY); break; } return (-1); } return (ancount); } /* * Formulate a normal query, send, and retrieve answer in supplied buffer. * Return the size of the response on success, -1 on error. * If enabled, implement search rules until answer or unrecoverable failure * is detected. Error code, if any, is left in h_errno. */ static int res_searchN(const char *name, struct res_target *target, res_state res) { const char *cp, * const *domain; HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ u_int dots; int trailing_dot, ret, saved_herrno; int got_nodata = 0, got_servfail = 0, root_on_list = 0; int tried_as_is = 0; int searched = 0; char abuf[MAXDNAME]; errno = 0; RES_SET_H_ERRNO(res, HOST_NOT_FOUND); /* default, if we never query */ dots = 0; for (cp = name; *cp; cp++) dots += (*cp == '.'); trailing_dot = 0; if (cp > name && *--cp == '.') trailing_dot++; /* * if there aren't any dots, it could be a user-level alias */ if (!dots && (cp = res_hostalias(res, name, abuf, sizeof(abuf))) != NULL) return (res_queryN(cp, target, res)); /* * If there are enough dots in the name, let's just give it a * try 'as is'. The threshold can be set with the "ndots" option. * Also, query 'as is', if there is a trailing dot in the name. */ saved_herrno = -1; if (dots >= res->ndots || trailing_dot) { ret = res_querydomainN(name, NULL, target, res); if (ret > 0 || trailing_dot) return (ret); if (errno == ECONNREFUSED) { RES_SET_H_ERRNO(res, TRY_AGAIN); return (-1); } switch (res->res_h_errno) { case NO_DATA: case HOST_NOT_FOUND: break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) break; /* FALLTHROUGH */ default: return (-1); } saved_herrno = res->res_h_errno; tried_as_is++; } /* * We do at least one level of search if * - there is no dot and RES_DEFNAME is set, or * - there is at least one dot, there is no trailing dot, * and RES_DNSRCH is set. */ if ((!dots && (res->options & RES_DEFNAMES)) || (dots && !trailing_dot && (res->options & RES_DNSRCH))) { int done = 0; for (domain = (const char * const *)res->dnsrch; *domain && !done; domain++) { searched = 1; if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++; if (root_on_list && tried_as_is) continue; ret = res_querydomainN(name, *domain, target, res); if (ret > 0) return (ret); /* * If no server present, give up. * If name isn't found in this domain, * keep trying higher domains in the search list * (if that's enabled). * On a NO_DATA error, keep trying, otherwise * a wildcard entry of another type could keep us * from finding this entry higher in the domain. * If we get some other error (negative answer or * server failure), then stop searching up, * but try the input name below in case it's * fully-qualified. */ if (errno == ECONNREFUSED) { RES_SET_H_ERRNO(res, TRY_AGAIN); return (-1); } switch (res->res_h_errno) { case NO_DATA: got_nodata++; /* FALLTHROUGH */ case HOST_NOT_FOUND: /* keep trying */ break; case TRY_AGAIN: got_servfail++; if (hp->rcode == SERVFAIL) { /* try next search element, if any */ break; } /* FALLTHROUGH */ default: /* anything else implies that we're done */ done++; } /* * if we got here for some reason other than DNSRCH, * we only wanted one iteration of the loop, so stop. */ if (!(res->options & RES_DNSRCH)) done++; } } switch (res->res_h_errno) { case NO_DATA: case HOST_NOT_FOUND: break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) break; /* FALLTHROUGH */ default: goto giveup; } /* * If the query has not already been tried as is then try it * unless RES_NOTLDQUERY is set and there were no dots. */ if ((dots || !searched || !(res->options & RES_NOTLDQUERY)) && !(tried_as_is || root_on_list)) { ret = res_querydomainN(name, NULL, target, res); if (ret > 0) return (ret); } /* * if we got here, we didn't satisfy the search. * if we did an initial full query, return that query's h_errno * (note that we wouldn't be here if that query had succeeded). * else if we ever got a nodata, send that back as the reason. * else send back meaningless h_errno, that being the one from * the last DNSRCH we did. */ giveup: if (saved_herrno != -1) RES_SET_H_ERRNO(res, saved_herrno); else if (got_nodata) RES_SET_H_ERRNO(res, NO_DATA); else if (got_servfail) RES_SET_H_ERRNO(res, TRY_AGAIN); return (-1); } /* * Perform a call on res_query on the concatenation of name and domain, * removing a trailing dot from name if domain is NULL. */ static int res_querydomainN(const char *name, const char *domain, struct res_target *target, res_state res) { char nbuf[MAXDNAME]; const char *longname = nbuf; size_t n, d; #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_querydomain(%s, %s)\n", name, domain?domain:""); #endif if (domain == NULL) { /* * Check for trailing '.'; * copy without '.' if present. */ n = strlen(name); if (n >= MAXDNAME) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (-1); } if (n > 0 && name[--n] == '.') { strncpy(nbuf, name, n); nbuf[n] = '\0'; } else longname = name; } else { n = strlen(name); d = strlen(domain); if (n + d + 1 >= MAXDNAME) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (-1); } snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); } return (res_queryN(longname, target, res)); } Index: head/lib/libc/net/gethostnamadr.c =================================================================== --- head/lib/libc/net/gethostnamadr.c (revision 298829) +++ head/lib/libc/net/gethostnamadr.c (revision 298830) @@ -1,727 +1,727 @@ /*- * Copyright (c) 1994, Garrett Wollman * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX hack for _res */ #include /* XXX hack for _res */ #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif static int gethostbyname_internal(const char *, int, struct hostent *, char *, size_t, struct hostent **, int *, res_state); -/* Host lookup order if nsswitch.conf is broken or nonexistant */ +/* Host lookup order if nsswitch.conf is broken or nonexistent */ static const ns_src default_src[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; #ifdef NS_CACHING static int host_id_func(char *, size_t *, va_list, void *); static int host_marshal_func(char *, size_t *, void *, va_list, void *); static int host_unmarshal_func(char *, size_t, void *, va_list, void *); #endif NETDB_THREAD_ALLOC(hostent) NETDB_THREAD_ALLOC(hostent_data) NETDB_THREAD_ALLOC(hostdata) static void hostent_free(void *ptr) { free(ptr); } static void hostent_data_free(void *ptr) { struct hostent_data *hed = ptr; if (hed == NULL) return; hed->stayopen = 0; _endhosthtent(hed); free(hed); } static void hostdata_free(void *ptr) { free(ptr); } int __copy_hostent(struct hostent *he, struct hostent *hptr, char *buf, size_t buflen) { char *cp; char **ptr; int i, n; int nptr, len; /* Find out the amount of space required to store the answer. */ nptr = 2; /* NULL ptrs */ len = (char *)ALIGN(buf) - buf; for (i = 0; he->h_addr_list[i]; i++, nptr++) { len += he->h_length; } for (i = 0; he->h_aliases[i]; i++, nptr++) { len += strlen(he->h_aliases[i]) + 1; } len += strlen(he->h_name) + 1; len += nptr * sizeof(char*); if (len > buflen) { errno = ERANGE; return (-1); } /* copy address size and type */ hptr->h_addrtype = he->h_addrtype; n = hptr->h_length = he->h_length; ptr = (char **)ALIGN(buf); cp = (char *)ALIGN(buf) + nptr * sizeof(char *); /* copy address list */ hptr->h_addr_list = ptr; for (i = 0; he->h_addr_list[i]; i++ , ptr++) { memcpy(cp, he->h_addr_list[i], n); hptr->h_addr_list[i] = cp; cp += n; } hptr->h_addr_list[i] = NULL; ptr++; /* copy official name */ n = strlen(he->h_name) + 1; strcpy(cp, he->h_name); hptr->h_name = cp; cp += n; /* copy aliases */ hptr->h_aliases = ptr; for (i = 0 ; he->h_aliases[i]; i++) { n = strlen(he->h_aliases[i]) + 1; strcpy(cp, he->h_aliases[i]); hptr->h_aliases[i] = cp; cp += n; } hptr->h_aliases[i] = NULL; return (0); } #ifdef NS_CACHING static int host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { res_state statp; u_long res_options; const int op_id = 1; char *str; void *addr; socklen_t len; int type; size_t desired_size, size; enum nss_lookup_type lookup_type; char *p; int res = NS_UNAVAIL; statp = __res_state(); res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); lookup_type = (enum nss_lookup_type)cache_mdata; switch (lookup_type) { case nss_lt_name: str = va_arg(ap, char *); type = va_arg(ap, int); size = strlen(str); desired_size = sizeof(res_options) + sizeof(int) + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } p = buffer; memcpy(p, &res_options, sizeof(res_options)); p += sizeof(res_options); memcpy(p, &op_id, sizeof(int)); p += sizeof(int); memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); p += sizeof(int); memcpy(p, &type, sizeof(int)); p += sizeof(int); memcpy(p, str, size + 1); res = NS_SUCCESS; break; case nss_lt_id: addr = va_arg(ap, void *); len = va_arg(ap, socklen_t); type = va_arg(ap, int); desired_size = sizeof(res_options) + sizeof(int) + sizeof(enum nss_lookup_type) + sizeof(int) + sizeof(socklen_t) + len; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } p = buffer; memcpy(p, &res_options, sizeof(res_options)); p += sizeof(res_options); memcpy(p, &op_id, sizeof(int)); p += sizeof(int); memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); p += sizeof(int); memcpy(p, &type, sizeof(int)); p += sizeof(int); memcpy(p, &len, sizeof(socklen_t)); p += sizeof(socklen_t); memcpy(p, addr, len); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *str; void *addr; socklen_t len; int type; struct hostent *ht; struct hostent new_ht; size_t desired_size, aliases_size, addr_size, size; char *p, **iter; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: str = va_arg(ap, char *); type = va_arg(ap, int); break; case nss_lt_id: addr = va_arg(ap, void *); len = va_arg(ap, socklen_t); type = va_arg(ap, int); break; default: /* should be unreachable */ return (NS_UNAVAIL); } ht = va_arg(ap, struct hostent *); desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); if (ht->h_name != NULL) desired_size += strlen(ht->h_name) + 1; if (ht->h_aliases != NULL) { aliases_size = 0; for (iter = ht->h_aliases; *iter; ++iter) { desired_size += strlen(*iter) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + (aliases_size + 1) * sizeof(char *); } if (ht->h_addr_list != NULL) { addr_size = 0; for (iter = ht->h_addr_list; *iter; ++iter) ++addr_size; desired_size += addr_size * _ALIGN(ht->h_length); desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); } if (desired_size > *buffer_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_ht, ht, sizeof(struct hostent)); memset(buffer, 0, desired_size); *buffer_size = desired_size; p = buffer + sizeof(struct hostent) + sizeof(char *); memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_ht.h_name != NULL) { size = strlen(new_ht.h_name); memcpy(p, new_ht.h_name, size); new_ht.h_name = p; p += size + 1; } if (new_ht.h_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); new_ht.h_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (iter = new_ht.h_aliases; *iter; ++iter) { size = strlen(*iter); memcpy(p, *iter, size); *iter = p; p += size + 1; } } if (new_ht.h_addr_list != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); new_ht.h_addr_list = (char **)p; p += sizeof(char *) * (addr_size + 1); size = _ALIGN(new_ht.h_length); for (iter = new_ht.h_addr_list; *iter; ++iter) { memcpy(p, *iter, size); *iter = p; p += size + 1; } } memcpy(buffer, &new_ht, sizeof(struct hostent)); return (NS_SUCCESS); } static int host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *str; void *addr; socklen_t len; int type; struct hostent *ht; char *p; char **iter; char *orig_buf; size_t orig_buf_size; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: str = va_arg(ap, char *); type = va_arg(ap, int); break; case nss_lt_id: addr = va_arg(ap, void *); len = va_arg(ap, socklen_t); type = va_arg(ap, int); break; default: /* should be unreachable */ return (NS_UNAVAIL); } ht = va_arg(ap, struct hostent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); if (orig_buf_size < buffer_size - sizeof(struct hostent) - sizeof(char *)) { errno = ERANGE; return (NS_RETURN); } memcpy(ht, buffer, sizeof(struct hostent)); memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct hostent) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); if (ht->h_aliases != NULL) { NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); for (iter = ht->h_aliases; *iter; ++iter) NS_APPLY_OFFSET(*iter, orig_buf, p, char *); } if (ht->h_addr_list != NULL) { NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); for (iter = ht->h_addr_list; *iter; ++iter) NS_APPLY_OFFSET(*iter, orig_buf, p, char *); } *((struct hostent **)retval) = ht; return (NS_SUCCESS); } #endif /* NS_CACHING */ static int fakeaddr(const char *name, int af, struct hostent *hp, char *buf, size_t buflen, res_state statp) { struct hostent_data *hed; struct hostent he; if ((hed = __hostent_data_init()) == NULL) { errno = ENOMEM; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } if ((af != AF_INET || inet_aton(name, (struct in_addr *)hed->host_addr) != 1) && inet_pton(af, name, hed->host_addr) != 1) { RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); return (-1); } strncpy(hed->hostbuf, name, MAXDNAME); hed->hostbuf[MAXDNAME] = '\0'; if (af == AF_INET && (statp->options & RES_USE_INET6) != 0U) { _map_v4v6_address((char *)hed->host_addr, (char *)hed->host_addr); af = AF_INET6; } he.h_addrtype = af; switch(af) { case AF_INET: he.h_length = NS_INADDRSZ; break; case AF_INET6: he.h_length = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } he.h_name = hed->hostbuf; he.h_aliases = hed->host_aliases; hed->host_aliases[0] = NULL; hed->h_addr_ptrs[0] = (char *)hed->host_addr; hed->h_addr_ptrs[1] = NULL; he.h_addr_list = hed->h_addr_ptrs; if (__copy_hostent(&he, hp, buf, buflen) != 0) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } RES_SET_H_ERRNO(statp, NETDB_SUCCESS); return (0); } int gethostbyname_r(const char *name, struct hostent *he, char *buffer, size_t buflen, struct hostent **result, int *h_errnop) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } if (statp->options & RES_USE_INET6) { if (fakeaddr(name, AF_INET, he, buffer, buflen, statp) == 0) { *result = he; return (0); } if (gethostbyname_internal(name, AF_INET6, he, buffer, buflen, result, h_errnop, statp) == 0) return (0); } return (gethostbyname_internal(name, AF_INET, he, buffer, buflen, result, h_errnop, statp)); } int gethostbyname2_r(const char *name, int af, struct hostent *he, char *buffer, size_t buflen, struct hostent **result, int *h_errnop) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } return (gethostbyname_internal(name, af, he, buffer, buflen, result, h_errnop, statp)); } int gethostbyname_internal(const char *name, int af, struct hostent *hp, char *buf, size_t buflen, struct hostent **result, int *h_errnop, res_state statp) { const char *cp; int rval, ret_errno = 0; char abuf[MAXDNAME]; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( hosts, (void *)nss_lt_name, host_id_func, host_marshal_func, host_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyname, NULL) { NSSRC_DNS, _dns_gethostbyname, NULL }, NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; switch (af) { case AF_INET: case AF_INET6: break; default: RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; errno = EAFNOSUPPORT; return (-1); } /* * if there aren't any dots, it could be a user-level alias. * this is also done in res_query() since we are not the only * function that looks up host names. */ if (!strchr(name, '.') && (cp = res_hostalias(statp, name, abuf, sizeof abuf))) name = cp; if (fakeaddr(name, af, hp, buf, buflen, statp) == 0) { *result = hp; return (0); } rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, "gethostbyname2_r", default_src, name, af, hp, buf, buflen, &ret_errno, h_errnop); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } int gethostbyaddr_r(const void *addr, socklen_t len, int af, struct hostent *hp, char *buf, size_t buflen, struct hostent **result, int *h_errnop) { const u_char *uaddr = (const u_char *)addr; const struct in6_addr *addr6; socklen_t size; int rval, ret_errno = 0; res_state statp; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( hosts, (void *)nss_lt_id, host_id_func, host_marshal_func, host_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyaddr, NULL) { NSSRC_DNS, _dns_gethostbyaddr, NULL }, NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } if (af == AF_INET6 && len == NS_IN6ADDRSZ) { addr6 = (const struct in6_addr *)addr; if (IN6_IS_ADDR_LINKLOCAL(addr6)) { RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); *h_errnop = statp->res_h_errno; return (-1); } if (IN6_IS_ADDR_V4MAPPED(addr6) || IN6_IS_ADDR_V4COMPAT(addr6)) { /* Unmap. */ uaddr += NS_IN6ADDRSZ - NS_INADDRSZ; af = AF_INET; len = NS_INADDRSZ; } } switch (af) { case AF_INET: size = NS_INADDRSZ; break; case AF_INET6: size = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } if (size != len) { errno = EINVAL; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, "gethostbyaddr_r", default_src, uaddr, len, af, hp, buf, buflen, &ret_errno, h_errnop); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } struct hostent * gethostbyname(const char *name) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyname_r(name, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct hostent * gethostbyname2(const char *name, int af) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyname2_r(name, af, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct hostent * gethostbyaddr(const void *addr, socklen_t len, int af) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyaddr_r(addr, len, af, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } void sethostent(int stayopen) { struct hostent_data *hed; if ((hed = __hostent_data_init()) == NULL) return; _sethosthtent(stayopen, hed); _sethostdnsent(stayopen); } void endhostent(void) { struct hostent_data *hed; if ((hed = __hostent_data_init()) == NULL) return; _endhosthtent(hed); _endhostdnsent(); } Index: head/lib/libc/net/getnetnamadr.c =================================================================== --- head/lib/libc/net/getnetnamadr.c (revision 298829) +++ head/lib/libc/net/getnetnamadr.c (revision 298830) @@ -1,451 +1,451 @@ /*- * Copyright (c) 1994, Garrett Wollman * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif -/* Network lookup order if nsswitch.conf is broken or nonexistant */ +/* Network lookup order if nsswitch.conf is broken or nonexistent */ static const ns_src default_src[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; NETDB_THREAD_ALLOC(netent_data) NETDB_THREAD_ALLOC(netdata) #ifdef NS_CACHING static int net_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; uint32_t net; int type; size_t desired_size, size; enum nss_lookup_type lookup_type; int res = NS_UNAVAIL; lookup_type = (enum nss_lookup_type)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); res = NS_SUCCESS; break; case nss_lt_id: net = va_arg(ap, uint32_t); type = va_arg(ap, int); desired_size = sizeof(enum nss_lookup_type) + sizeof(uint32_t) + sizeof(int); if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &net, sizeof(uint32_t)); memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(uint32_t), &type, sizeof(int)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int net_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uint32_t net; int type; struct netent *ne; char *orig_buf; size_t orig_buf_size; struct netent new_ne; size_t desired_size, size, aliases_size; char *p; char **alias; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: net = va_arg(ap, uint32_t); type = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } ne = va_arg(ap, struct netent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct netent) + sizeof(char *); if (ne->n_name != NULL) desired_size += strlen(ne->n_name) + 1; if (ne->n_aliases != NULL) { aliases_size = 0; for (alias = ne->n_aliases; *alias; ++alias) { desired_size += strlen(*alias) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + (aliases_size + 1) * sizeof(char *); } if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_ne, ne, sizeof(struct netent)); *buffer_size = desired_size; memset(buffer, 0, desired_size); p = buffer + sizeof(struct netent) + sizeof(char *); memcpy(buffer + sizeof(struct netent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_ne.n_name != NULL) { size = strlen(new_ne.n_name); memcpy(p, new_ne.n_name, size); new_ne.n_name = p; p += size + 1; } if (new_ne.n_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_ne.n_aliases, sizeof(char *) * aliases_size); new_ne.n_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (alias = new_ne.n_aliases; *alias; ++alias) { size = strlen(*alias); memcpy(p, *alias, size); *alias = p; p += size + 1; } } memcpy(buffer, &new_ne, sizeof(struct netent)); return (NS_SUCCESS); } static int net_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uint32_t net; int type; struct netent *ne; char *orig_buf; size_t orig_buf_size; int *ret_errno; char *p; char **alias; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: net = va_arg(ap, uint32_t); type = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } ne = va_arg(ap, struct netent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct netent) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(ne, buffer, sizeof(struct netent)); memcpy(&p, buffer + sizeof(struct netent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct netent) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct netent) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(ne->n_name, orig_buf, p, char *); if (ne->n_aliases != NULL) { NS_APPLY_OFFSET(ne->n_aliases, orig_buf, p, char **); for (alias = ne->n_aliases; *alias; ++alias) NS_APPLY_OFFSET(*alias, orig_buf, p, char *); } if (retval != NULL) *((struct netent **)retval) = ne; return (NS_SUCCESS); } #endif /* NS_CACHING */ static void netent_data_free(void *ptr) { struct netent_data *ned = ptr; if (ned == NULL) return; ned->stayopen = 0; _endnethtent(ned); free(ned); } static void netdata_free(void *ptr) { free(ptr); } int __copy_netent(struct netent *ne, struct netent *nptr, char *buf, size_t buflen) { char *cp; int i, n; int numptr, len; /* Find out the amount of space required to store the answer. */ numptr = 1; /* NULL ptr */ len = (char *)ALIGN(buf) - buf; for (i = 0; ne->n_aliases[i]; i++, numptr++) { len += strlen(ne->n_aliases[i]) + 1; } len += strlen(ne->n_name) + 1; len += numptr * sizeof(char*); if (len > (int)buflen) { errno = ERANGE; return (-1); } /* copy net value and type */ nptr->n_addrtype = ne->n_addrtype; nptr->n_net = ne->n_net; cp = (char *)ALIGN(buf) + numptr * sizeof(char *); /* copy official name */ n = strlen(ne->n_name) + 1; strcpy(cp, ne->n_name); nptr->n_name = cp; cp += n; /* copy aliases */ nptr->n_aliases = (char **)ALIGN(buf); for (i = 0 ; ne->n_aliases[i]; i++) { n = strlen(ne->n_aliases[i]) + 1; strcpy(cp, ne->n_aliases[i]); nptr->n_aliases[i] = cp; cp += n; } nptr->n_aliases[i] = NULL; return (0); } int getnetbyname_r(const char *name, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( networks, (void *)nss_lt_name, net_id_func, net_marshal_func, net_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyname, NULL) { NSSRC_DNS, _dns_getnetbyname, NULL }, NS_NIS_CB(_nis_getnetbyname, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; int rval, ret_errno = 0; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyname_r", default_src, name, ne, buffer, buflen, &ret_errno, h_errorp); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } int getnetbyaddr_r(uint32_t addr, int af, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( networks, (void *)nss_lt_id, net_id_func, net_marshal_func, net_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyaddr, NULL) { NSSRC_DNS, _dns_getnetbyaddr, NULL }, NS_NIS_CB(_nis_getnetbyaddr, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; int rval, ret_errno = 0; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyaddr_r", default_src, addr, af, ne, buffer, buflen, &ret_errno, h_errorp); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } struct netent * getnetbyname(const char *name) { struct netdata *nd; struct netent *rval; int ret_h_errno; if ((nd = __netdata_init()) == NULL) return (NULL); if (getnetbyname_r(name, &nd->net, nd->data, sizeof(nd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct netent * getnetbyaddr(uint32_t addr, int af) { struct netdata *nd; struct netent *rval; int ret_h_errno; if ((nd = __netdata_init()) == NULL) return (NULL); if (getnetbyaddr_r(addr, af, &nd->net, nd->data, sizeof(nd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } void setnetent(int stayopen) { struct netent_data *ned; if ((ned = __netent_data_init()) == NULL) return; _setnethtent(stayopen, ned); _setnetdnsent(stayopen); } void endnetent(void) { struct netent_data *ned; if ((ned = __netent_data_init()) == NULL) return; _endnethtent(ned); _endnetdnsent(); } Index: head/lib/libc/net/name6.c =================================================================== --- head/lib/libc/net/name6.c (revision 298829) +++ head/lib/libc/net/name6.c (revision 298830) @@ -1,1113 +1,1113 @@ /* $KAME: name6.c,v 1.25 2000/06/26 16:44:40 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * 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 project 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 PROJECT 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 PROJECT 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. */ /* * ++Copyright++ 1985, 1988, 1993 * - * Copyright (c) 1985, 1988, 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. * - * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * 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, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION 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. * - * --Copyright-- */ /* * Atsushi Onoe */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #ifdef INET6 #include #include #include #include /* XXX */ #endif #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "netdb_private.h" #include "res_private.h" #ifndef MAXALIASES #define MAXALIASES 10 #endif #ifndef MAXADDRS #define MAXADDRS 20 #endif #ifndef MAXDNAME #define MAXDNAME 1025 #endif #ifdef INET6 #define ADDRLEN(af) ((af) == AF_INET6 ? sizeof(struct in6_addr) : \ sizeof(struct in_addr)) #else #define ADDRLEN(af) sizeof(struct in_addr) #endif #define MAPADDR(ab, ina) \ do { \ memcpy(&(ab)->map_inaddr, ina, sizeof(struct in_addr)); \ memset((ab)->map_zero, 0, sizeof((ab)->map_zero)); \ memset((ab)->map_one, 0xff, sizeof((ab)->map_one)); \ } while (0) #define MAPADDRENABLED(flags) \ (((flags) & AI_V4MAPPED) || \ (((flags) & AI_V4MAPPED_CFG))) union inx_addr { struct in_addr in_addr; #ifdef INET6 struct in6_addr in6_addr; #endif struct { u_char mau_zero[10]; u_char mau_one[2]; struct in_addr mau_inaddr; } map_addr_un; #define map_zero map_addr_un.mau_zero #define map_one map_addr_un.mau_one #define map_inaddr map_addr_un.mau_inaddr }; struct policyqueue { TAILQ_ENTRY(policyqueue) pc_entry; #ifdef INET6 struct in6_addrpolicy pc_policy; #endif }; TAILQ_HEAD(policyhead, policyqueue); #define AIO_SRCFLAG_DEPRECATED 0x1 struct hp_order { union { struct sockaddr_storage aiou_ss; struct sockaddr aiou_sa; } aio_src_un; #define aio_srcsa aio_src_un.aiou_sa u_int32_t aio_srcflag; int aio_srcscope; int aio_dstscope; struct policyqueue *aio_srcpolicy; struct policyqueue *aio_dstpolicy; union { struct sockaddr_storage aiou_ss; struct sockaddr aiou_sa; } aio_un; #define aio_sa aio_un.aiou_sa int aio_matchlen; char *aio_h_addr; }; static struct hostent *_hpcopy(struct hostent *, int *); static struct hostent *_hpaddr(int, const char *, void *, int *); #ifdef INET6 static struct hostent *_hpmerge(struct hostent *, struct hostent *, int *); static struct hostent *_hpmapv6(struct hostent *, int *); #endif static struct hostent *_hpsort(struct hostent *, res_state); #ifdef INET6 static struct hostent *_hpreorder(struct hostent *); static int get_addrselectpolicy(struct policyhead *); static void free_addrselectpolicy(struct policyhead *); static struct policyqueue *match_addrselectpolicy(struct sockaddr *, struct policyhead *); static void set_source(struct hp_order *, struct policyhead *); static int matchlen(struct sockaddr *, struct sockaddr *); static int comp_dst(const void *, const void *); static int gai_addr2scopetype(struct sockaddr *); #endif /* * Functions defined in RFC2553 * getipnodebyname, getipnodebyaddr, freehostent */ struct hostent * getipnodebyname(const char *name, int af, int flags, int *errp) { struct hostent *hp; union inx_addr addrbuf; res_state statp; u_long options; switch (af) { case AF_INET: #ifdef INET6 case AF_INET6: #endif break; default: *errp = NO_RECOVERY; return NULL; } if (flags & AI_ADDRCONFIG) { int s; if ((s = _socket(af, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) return NULL; /* * TODO: * Note that implementation dependent test for address - * configuration should be done everytime called - * (or apropriate interval), + * configuration should be done every time called + * (or appropriate interval), * because addresses will be dynamically assigned or deleted. */ _close(s); } #ifdef INET6 /* special case for literal address */ if (inet_pton(AF_INET6, name, &addrbuf) == 1) { if (af != AF_INET6) { *errp = HOST_NOT_FOUND; return NULL; } return _hpaddr(af, name, &addrbuf, errp); } #endif if (inet_aton(name, (struct in_addr *)&addrbuf) == 1) { if (af != AF_INET) { if (MAPADDRENABLED(flags)) { MAPADDR(&addrbuf, &addrbuf.in_addr); } else { *errp = HOST_NOT_FOUND; return NULL; } } return _hpaddr(af, name, &addrbuf, errp); } statp = __res_state(); if ((statp->options & RES_INIT) == 0) { if (res_ninit(statp) < 0) { *errp = NETDB_INTERNAL; return NULL; } } options = statp->options; statp->options &= ~RES_USE_INET6; hp = gethostbyname2(name, af); hp = _hpcopy(hp, errp); #ifdef INET6 if (af == AF_INET6) hp = _hpreorder(hp); if (af == AF_INET6 && ((flags & AI_ALL) || hp == NULL) && MAPADDRENABLED(flags)) { struct hostent *hp2 = gethostbyname2(name, AF_INET); if (hp == NULL) if (hp2 == NULL) *errp = statp->res_h_errno; else hp = _hpmapv6(hp2, errp); else { if (hp2 && strcmp(hp->h_name, hp2->h_name) == 0) { struct hostent *hpb = hp; hp = _hpmerge(hpb, hp2, errp); freehostent(hpb); } } } #endif if (hp == NULL) *errp = statp->res_h_errno; statp->options = options; return _hpsort(hp, statp); } struct hostent * getipnodebyaddr(const void *src, size_t len, int af, int *errp) { struct hostent *hp; res_state statp; u_long options; #ifdef INET6 struct in6_addr addrbuf; #else struct in_addr addrbuf; #endif switch (af) { case AF_INET: if (len != sizeof(struct in_addr)) { *errp = NO_RECOVERY; return NULL; } if (rounddown2((long)src, sizeof(struct in_addr))) { memcpy(&addrbuf, src, len); src = &addrbuf; } if (((struct in_addr *)src)->s_addr == 0) return NULL; break; #ifdef INET6 case AF_INET6: if (len != sizeof(struct in6_addr)) { *errp = NO_RECOVERY; return NULL; } if (rounddown2((long)src, sizeof(struct in6_addr) / 2)) { /* XXX */ memcpy(&addrbuf, src, len); src = &addrbuf; } if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)src)) return NULL; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)src) || IN6_IS_ADDR_V4COMPAT((struct in6_addr *)src)) { src = (char *)src + (sizeof(struct in6_addr) - sizeof(struct in_addr)); af = AF_INET; len = sizeof(struct in_addr); } break; #endif default: *errp = NO_RECOVERY; return NULL; } statp = __res_state(); if ((statp->options & RES_INIT) == 0) { if (res_ninit(statp) < 0) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return NULL; } } options = statp->options; statp->options &= ~RES_USE_INET6; hp = gethostbyaddr(src, len, af); if (hp == NULL) *errp = statp->res_h_errno; statp->options = options; return (_hpcopy(hp, errp)); } void freehostent(struct hostent *ptr) { free(ptr); } /* * Private utility functions */ /* * _hpcopy: allocate and copy hostent structure */ static struct hostent * _hpcopy(struct hostent *hp, int *errp) { struct hostent *nhp; char *cp, **pp; int size, addrsize; int nalias = 0, naddr = 0; int al_off; int i; if (hp == NULL) return hp; /* count size to be allocated */ size = sizeof(struct hostent); if (hp->h_name != NULL) size += strlen(hp->h_name) + 1; if ((pp = hp->h_aliases) != NULL) { for (i = 0; *pp != NULL; i++, pp++) { if (**pp != '\0') { size += strlen(*pp) + 1; nalias++; } } } /* adjust alignment */ size = ALIGN(size); al_off = size; size += sizeof(char *) * (nalias + 1); addrsize = ALIGN(hp->h_length); if ((pp = hp->h_addr_list) != NULL) { while (*pp++ != NULL) naddr++; } size += addrsize * naddr; size += sizeof(char *) * (naddr + 1); /* copy */ if ((nhp = (struct hostent *)malloc(size)) == NULL) { *errp = TRY_AGAIN; return NULL; } cp = (char *)&nhp[1]; if (hp->h_name != NULL) { nhp->h_name = cp; strcpy(cp, hp->h_name); cp += strlen(cp) + 1; } else nhp->h_name = NULL; nhp->h_aliases = (char **)((char *)nhp + al_off); if ((pp = hp->h_aliases) != NULL) { for (i = 0; *pp != NULL; pp++) { if (**pp != '\0') { nhp->h_aliases[i++] = cp; strcpy(cp, *pp); cp += strlen(cp) + 1; } } } nhp->h_aliases[nalias] = NULL; cp = (char *)&nhp->h_aliases[nalias + 1]; nhp->h_addrtype = hp->h_addrtype; nhp->h_length = hp->h_length; nhp->h_addr_list = (char **)cp; if ((pp = hp->h_addr_list) != NULL) { cp = (char *)&nhp->h_addr_list[naddr + 1]; for (i = 0; *pp != NULL; pp++) { nhp->h_addr_list[i++] = cp; memcpy(cp, *pp, hp->h_length); cp += addrsize; } } nhp->h_addr_list[naddr] = NULL; return nhp; } /* * _hpaddr: construct hostent structure with one address */ static struct hostent * _hpaddr(int af, const char *name, void *addr, int *errp) { struct hostent *hp, hpbuf; char *addrs[2]; hp = &hpbuf; hp->h_name = (char *)name; hp->h_aliases = NULL; hp->h_addrtype = af; hp->h_length = ADDRLEN(af); hp->h_addr_list = addrs; addrs[0] = (char *)addr; addrs[1] = NULL; return (_hpcopy(hp, errp)); } #ifdef INET6 /* * _hpmerge: merge 2 hostent structure, arguments will be freed */ static struct hostent * _hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp) { int i, j; int naddr, nalias; char **pp; struct hostent *hp, hpbuf; char *aliases[MAXALIASES + 1], *addrs[MAXADDRS + 1]; union inx_addr addrbuf[MAXADDRS]; if (hp1 == NULL) return _hpcopy(hp2, errp); if (hp2 == NULL) return _hpcopy(hp1, errp); #define HP(i) (i == 1 ? hp1 : hp2) hp = &hpbuf; hp->h_name = (hp1->h_name != NULL ? hp1->h_name : hp2->h_name); hp->h_aliases = aliases; nalias = 0; for (i = 1; i <= 2; i++) { if ((pp = HP(i)->h_aliases) == NULL) continue; for (; nalias < MAXALIASES && *pp != NULL; pp++) { /* check duplicates */ for (j = 0; j < nalias; j++) if (strcasecmp(*pp, aliases[j]) == 0) break; if (j == nalias) aliases[nalias++] = *pp; } } aliases[nalias] = NULL; if (hp1->h_length != hp2->h_length) { hp->h_addrtype = AF_INET6; hp->h_length = sizeof(struct in6_addr); } else { hp->h_addrtype = hp1->h_addrtype; hp->h_length = hp1->h_length; } hp->h_addr_list = addrs; naddr = 0; for (i = 1; i <= 2; i++) { if ((pp = HP(i)->h_addr_list) == NULL) continue; if (HP(i)->h_length == hp->h_length) { while (naddr < MAXADDRS && *pp != NULL) addrs[naddr++] = *pp++; } else { /* copy IPv4 addr as mapped IPv6 addr */ while (naddr < MAXADDRS && *pp != NULL) { MAPADDR(&addrbuf[naddr], *pp++); addrs[naddr] = (char *)&addrbuf[naddr]; naddr++; } } } addrs[naddr] = NULL; return (_hpcopy(hp, errp)); } #endif /* * _hpmapv6: convert IPv4 hostent into IPv4-mapped IPv6 addresses */ #ifdef INET6 static struct hostent * _hpmapv6(struct hostent *hp, int *errp) { struct hostent hp6; if (hp == NULL) return NULL; if (hp->h_addrtype == AF_INET6) return _hpcopy(hp, errp); memset(&hp6, 0, sizeof(struct hostent)); hp6.h_addrtype = AF_INET6; hp6.h_length = sizeof(struct in6_addr); return _hpmerge(&hp6, hp, errp); } #endif /* * _hpsort: sort address by sortlist */ static struct hostent * _hpsort(struct hostent *hp, res_state statp) { int i, j, n; u_char *ap, *sp, *mp, **pp; char t; char order[MAXADDRS]; int nsort = statp->nsort; if (hp == NULL || hp->h_addr_list[1] == NULL || nsort == 0) return hp; for (i = 0; (ap = (u_char *)hp->h_addr_list[i]); i++) { for (j = 0; j < nsort; j++) { #ifdef INET6 if (statp->_u._ext.ext->sort_list[j].af != hp->h_addrtype) continue; sp = (u_char *)&statp->_u._ext.ext->sort_list[j].addr; mp = (u_char *)&statp->_u._ext.ext->sort_list[j].mask; #else sp = (u_char *)&statp->sort_list[j].addr; mp = (u_char *)&statp->sort_list[j].mask; #endif for (n = 0; n < hp->h_length; n++) { if ((ap[n] & mp[n]) != sp[n]) break; } if (n == hp->h_length) break; } order[i] = j; } n = i; pp = (u_char **)hp->h_addr_list; for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { if (order[i] > order[j]) { ap = pp[i]; pp[i] = pp[j]; pp[j] = ap; t = order[i]; order[i] = order[j]; order[j] = t; } } } return hp; } #ifdef INET6 /* * _hpreorder: sort address by default address selection */ static struct hostent * _hpreorder(struct hostent *hp) { struct hp_order *aio; int i, n; char *ap; struct sockaddr *sa; struct policyhead policyhead; if (hp == NULL) return hp; switch (hp->h_addrtype) { case AF_INET: #ifdef INET6 case AF_INET6: #endif break; default: free_addrselectpolicy(&policyhead); return hp; } /* count the number of addrinfo elements for sorting. */ for (n = 0; hp->h_addr_list[n] != NULL; n++) ; /* * If the number is small enough, we can skip the reordering process. */ if (n <= 1) return hp; /* allocate a temporary array for sort and initialization of it. */ if ((aio = malloc(sizeof(*aio) * n)) == NULL) return hp; /* give up reordering */ memset(aio, 0, sizeof(*aio) * n); /* retrieve address selection policy from the kernel */ TAILQ_INIT(&policyhead); if (!get_addrselectpolicy(&policyhead)) { /* no policy is installed into kernel, we don't sort. */ free(aio); return hp; } for (i = 0; i < n; i++) { ap = hp->h_addr_list[i]; aio[i].aio_h_addr = ap; sa = &aio[i].aio_sa; switch (hp->h_addrtype) { case AF_INET: sa->sa_family = AF_INET; sa->sa_len = sizeof(struct sockaddr_in); memcpy(&((struct sockaddr_in *)sa)->sin_addr, ap, sizeof(struct in_addr)); break; #ifdef INET6 case AF_INET6: if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { sa->sa_family = AF_INET; sa->sa_len = sizeof(struct sockaddr_in); memcpy(&((struct sockaddr_in *)sa)->sin_addr, &ap[12], sizeof(struct in_addr)); } else { sa->sa_family = AF_INET6; sa->sa_len = sizeof(struct sockaddr_in6); memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, ap, sizeof(struct in6_addr)); } break; #endif } aio[i].aio_dstscope = gai_addr2scopetype(sa); aio[i].aio_dstpolicy = match_addrselectpolicy(sa, &policyhead); set_source(&aio[i], &policyhead); } /* perform sorting. */ qsort(aio, n, sizeof(*aio), comp_dst); /* reorder the h_addr_list. */ for (i = 0; i < n; i++) hp->h_addr_list[i] = aio[i].aio_h_addr; /* cleanup and return */ free(aio); free_addrselectpolicy(&policyhead); return hp; } static int get_addrselectpolicy(struct policyhead *head) { #ifdef INET6 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; size_t l; char *buf; struct in6_addrpolicy *pol, *ep; if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) return (0); if ((buf = malloc(l)) == NULL) return (0); if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { free(buf); return (0); } ep = (struct in6_addrpolicy *)(buf + l); for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { struct policyqueue *new; if ((new = malloc(sizeof(*new))) == NULL) { free_addrselectpolicy(head); /* make the list empty */ break; } new->pc_policy = *pol; TAILQ_INSERT_TAIL(head, new, pc_entry); } free(buf); return (1); #else return (0); #endif } static void free_addrselectpolicy(struct policyhead *head) { struct policyqueue *ent, *nent; for (ent = TAILQ_FIRST(head); ent; ent = nent) { nent = TAILQ_NEXT(ent, pc_entry); TAILQ_REMOVE(head, ent, pc_entry); free(ent); } } static struct policyqueue * match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) { #ifdef INET6 struct policyqueue *ent, *bestent = NULL; struct in6_addrpolicy *pol; int matchlen, bestmatchlen = -1; u_char *mp, *ep, *k, *p, m; struct sockaddr_in6 key; switch(addr->sa_family) { case AF_INET6: key = *(struct sockaddr_in6 *)addr; break; case AF_INET: /* convert the address into IPv4-mapped IPv6 address. */ memset(&key, 0, sizeof(key)); key.sin6_family = AF_INET6; key.sin6_len = sizeof(key); _map_v4v6_address( (char *)&((struct sockaddr_in *)addr)->sin_addr, (char *)&key.sin6_addr); break; default: return(NULL); } for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { pol = &ent->pc_policy; matchlen = 0; mp = (u_char *)&pol->addrmask.sin6_addr; ep = mp + 16; /* XXX: scope field? */ k = (u_char *)&key.sin6_addr; p = (u_char *)&pol->addr.sin6_addr; for (; mp < ep && *mp; mp++, k++, p++) { m = *mp; if ((*k & m) != *p) goto next; /* not match */ if (m == 0xff) /* short cut for a typical case */ matchlen += 8; else { while (m >= 0x80) { matchlen++; m <<= 1; } } } /* matched. check if this is better than the current best. */ if (matchlen > bestmatchlen) { bestent = ent; bestmatchlen = matchlen; } next: continue; } return(bestent); #else return(NULL); #endif } static void set_source(struct hp_order *aio, struct policyhead *ph) { struct sockaddr_storage ss = aio->aio_un.aiou_ss; socklen_t srclen; int s; /* set unspec ("no source is available"), just in case */ aio->aio_srcsa.sa_family = AF_UNSPEC; aio->aio_srcscope = -1; switch(ss.ss_family) { case AF_INET: ((struct sockaddr_in *)&ss)->sin_port = htons(1); break; #ifdef INET6 case AF_INET6: ((struct sockaddr_in6 *)&ss)->sin6_port = htons(1); break; #endif default: /* ignore unsupported AFs explicitly */ return; } /* open a socket to get the source address for the given dst */ if ((s = _socket(ss.ss_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP)) < 0) return; /* give up */ if (_connect(s, (struct sockaddr *)&ss, ss.ss_len) < 0) goto cleanup; srclen = ss.ss_len; if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { aio->aio_srcsa.sa_family = AF_UNSPEC; goto cleanup; } aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); aio->aio_matchlen = matchlen(&aio->aio_srcsa, (struct sockaddr *)&ss); #ifdef INET6 if (ss.ss_family == AF_INET6) { struct in6_ifreq ifr6; u_int32_t flags6; memset(&ifr6, 0, sizeof(ifr6)); memcpy(&ifr6.ifr_addr, &ss, ss.ss_len); if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { flags6 = ifr6.ifr_ifru.ifru_flags6; if ((flags6 & IN6_IFF_DEPRECATED)) aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; } } #endif cleanup: _close(s); return; } static int matchlen(struct sockaddr *src, struct sockaddr *dst) { int match = 0; u_char *s, *d; u_char *lim, r; int addrlen; switch (src->sa_family) { #ifdef INET6 case AF_INET6: s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; addrlen = sizeof(struct in6_addr); lim = s + addrlen; break; #endif case AF_INET: s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; addrlen = sizeof(struct in_addr); lim = s + addrlen; break; default: return(0); } while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < addrlen * 8) { match++; r <<= 1; } break; } else match += 8; return(match); } static int comp_dst(const void *arg1, const void *arg2) { const struct hp_order *dst1 = arg1, *dst2 = arg2; /* * Rule 1: Avoid unusable destinations. * XXX: we currently do not consider if an appropriate route exists. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family == AF_UNSPEC) { return(-1); } if (dst1->aio_srcsa.sa_family == AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { return(1); } /* Rule 2: Prefer matching scope. */ if (dst1->aio_dstscope == dst1->aio_srcscope && dst2->aio_dstscope != dst2->aio_srcscope) { return(-1); } if (dst1->aio_dstscope != dst1->aio_srcscope && dst2->aio_dstscope == dst2->aio_srcscope) { return(1); } /* Rule 3: Avoid deprecated addresses. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(-1); } if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(1); } } /* Rule 4: Prefer home addresses. */ /* XXX: not implemented yet */ /* Rule 5: Prefer matching label. */ #ifdef INET6 if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && dst1->aio_srcpolicy->pc_policy.label == dst1->aio_dstpolicy->pc_policy.label && (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || dst2->aio_srcpolicy->pc_policy.label != dst2->aio_dstpolicy->pc_policy.label)) { return(-1); } if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && dst2->aio_srcpolicy->pc_policy.label == dst2->aio_dstpolicy->pc_policy.label && (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || dst1->aio_srcpolicy->pc_policy.label != dst1->aio_dstpolicy->pc_policy.label)) { return(1); } #endif /* Rule 6: Prefer higher precedence. */ #ifdef INET6 if (dst1->aio_dstpolicy && (dst2->aio_dstpolicy == NULL || dst1->aio_dstpolicy->pc_policy.preced > dst2->aio_dstpolicy->pc_policy.preced)) { return(-1); } if (dst2->aio_dstpolicy && (dst1->aio_dstpolicy == NULL || dst2->aio_dstpolicy->pc_policy.preced > dst1->aio_dstpolicy->pc_policy.preced)) { return(1); } #endif /* Rule 7: Prefer native transport. */ /* XXX: not implemented yet */ /* Rule 8: Prefer smaller scope. */ if (dst1->aio_dstscope >= 0 && dst1->aio_dstscope < dst2->aio_dstscope) { return(-1); } if (dst2->aio_dstscope >= 0 && dst2->aio_dstscope < dst1->aio_dstscope) { return(1); } /* * Rule 9: Use longest matching prefix. * We compare the match length in a same AF only. */ if (dst1->aio_sa.sa_family == dst2->aio_sa.sa_family) { if (dst1->aio_matchlen > dst2->aio_matchlen) { return(-1); } if (dst1->aio_matchlen < dst2->aio_matchlen) { return(1); } } /* Rule 10: Otherwise, leave the order unchanged. */ return(-1); } /* * Copy from scope.c. * XXX: we should standardize the functions and link them as standard * library. */ static int gai_addr2scopetype(struct sockaddr *sa) { #ifdef INET6 struct sockaddr_in6 *sa6; #endif struct sockaddr_in *sa4; switch(sa->sa_family) { #ifdef INET6 case AF_INET6: sa6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { /* just use the scope field of the multicast address */ return(sa6->sin6_addr.s6_addr[2] & 0x0f); } /* * Unicast addresses: map scope type to corresponding scope * value defined for multcast addresses. * XXX: hardcoded scope type values are bad... */ if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) return(1); /* node local scope */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) return(2); /* link-local scope */ if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) return(5); /* site-local scope */ return(14); /* global scope */ break; #endif case AF_INET: /* * IPv4 pseudo scoping according to RFC 3484. */ sa4 = (struct sockaddr_in *)sa; /* IPv4 autoconfiguration addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 169 && ((u_char *)&sa4->sin_addr)[1] == 254) return(2); /* Private addresses have site-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 10 || (((u_char *)&sa4->sin_addr)[0] == 172 && (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || (((u_char *)&sa4->sin_addr)[0] == 192 && ((u_char *)&sa4->sin_addr)[1] == 168)) return(14); /* XXX: It should be 5 unless NAT */ /* Loopback addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 127) return(2); return(14); break; default: errno = EAFNOSUPPORT; /* is this a good error? */ return(-1); } } #endif Index: head/lib/libc/net/nscachedcli.c =================================================================== --- head/lib/libc/net/nscachedcli.c (revision 298829) +++ head/lib/libc/net/nscachedcli.c (revision 298830) @@ -1,577 +1,577 @@ /*- * Copyright (c) 2005 Michael Bushkov * 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 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 AUTHOR 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. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "nscachedcli.h" #define NS_DEFAULT_CACHED_IO_TIMEOUT 4 static int safe_write(struct cached_connection_ *, const void *, size_t); static int safe_read(struct cached_connection_ *, void *, size_t); static int send_credentials(struct cached_connection_ *, int); /* * safe_write writes data to the specified connection and tries to do it in * the very safe manner. We ensure, that we can write to the socket with * kevent. If the data_size can't be sent in one piece, then it would be * splitted. */ static int safe_write(struct cached_connection_ *connection, const void *data, size_t data_size) { struct kevent eventlist; int nevents; size_t result; ssize_t s_result; struct timespec timeout; if (data_size == 0) return (0); timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; timeout.tv_nsec = 0; result = 0; do { nevents = _kevent(connection->write_queue, NULL, 0, &eventlist, 1, &timeout); if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { s_result = _sendto(connection->sockfd, data + result, eventlist.data < data_size - result ? eventlist.data : data_size - result, MSG_NOSIGNAL, NULL, 0); if (s_result == -1) return (-1); else result += s_result; if (eventlist.flags & EV_EOF) return (result < data_size ? -1 : 0); } else return (-1); } while (result < data_size); return (0); } /* * safe_read reads data from connection and tries to do it in the very safe - * and stable way. It uses kevent to ensure, that the data are availabe for + * and stable way. It uses kevent to ensure, that the data are available for * reading. If the amount of data to be read is too large, then they would * be splitted. */ static int safe_read(struct cached_connection_ *connection, void *data, size_t data_size) { struct kevent eventlist; size_t result; ssize_t s_result; struct timespec timeout; int nevents; if (data_size == 0) return (0); timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; timeout.tv_nsec = 0; result = 0; do { nevents = _kevent(connection->read_queue, NULL, 0, &eventlist, 1, &timeout); if (nevents == 1 && eventlist.filter == EVFILT_READ) { s_result = _read(connection->sockfd, data + result, eventlist.data <= data_size - result ? eventlist.data : data_size - result); if (s_result == -1) return (-1); else result += s_result; if (eventlist.flags & EV_EOF) return (result < data_size ? -1 : 0); } else return (-1); } while (result < data_size); return (0); } /* * Sends the credentials information to the connection along with the * communication element type. */ static int send_credentials(struct cached_connection_ *connection, int type) { struct kevent eventlist; int nevents; ssize_t result; int res; struct msghdr cred_hdr; struct iovec iov; struct { struct cmsghdr hdr; char cred[CMSG_SPACE(sizeof(struct cmsgcred))]; } cmsg; memset(&cmsg, 0, sizeof(cmsg)); cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; memset(&cred_hdr, 0, sizeof(struct msghdr)); cred_hdr.msg_iov = &iov; cred_hdr.msg_iovlen = 1; cred_hdr.msg_control = (caddr_t)&cmsg; cred_hdr.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred)); iov.iov_base = &type; iov.iov_len = sizeof(int); EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, NOTE_LOWAT, sizeof(int), NULL); res = _kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); nevents = _kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL); if (nevents == 1 && eventlist.filter == EVFILT_WRITE) { result = (_sendmsg(connection->sockfd, &cred_hdr, MSG_NOSIGNAL) == -1) ? -1 : 0; EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); _kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); return (result); } else return (-1); } /* * Opens the connection with the specified params. Initializes all kqueues. */ struct cached_connection_ * __open_cached_connection(struct cached_connection_params const *params) { struct cached_connection_ *retval; struct kevent eventlist; struct sockaddr_un client_address; int client_address_len, client_socket; int res; assert(params != NULL); client_socket = _socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); client_address.sun_family = PF_LOCAL; strncpy(client_address.sun_path, params->socket_path, sizeof(client_address.sun_path)); client_address_len = sizeof(client_address.sun_family) + strlen(client_address.sun_path) + 1; res = _connect(client_socket, (struct sockaddr *)&client_address, client_address_len); if (res == -1) { _close(client_socket); return (NULL); } _fcntl(client_socket, F_SETFL, O_NONBLOCK); retval = malloc(sizeof(struct cached_connection_)); assert(retval != NULL); memset(retval, 0, sizeof(struct cached_connection_)); retval->sockfd = client_socket; retval->write_queue = kqueue(); assert(retval->write_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); res = _kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); retval->read_queue = kqueue(); assert(retval->read_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); res = _kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); return (retval); } void __close_cached_connection(struct cached_connection_ *connection) { assert(connection != NULL); _close(connection->sockfd); _close(connection->read_queue); _close(connection->write_queue); free(connection); } /* * This function is very close to the cache_write function of the caching * library, which is used in the caching daemon. It caches the data with the * specified key in the cache entry with entry_name. */ int __cached_write(struct cached_connection_ *connection, const char *entry_name, const char *key, size_t key_size, const char *data, size_t data_size) { size_t name_size; int error_code; int result; error_code = -1; result = 0; result = send_credentials(connection, CET_WRITE_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, &key_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, &data_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_write(connection, key, key_size); if (result != 0) goto fin; result = safe_write(connection, data, data_size); if (result != 0) goto fin; result = safe_read(connection, &error_code, sizeof(int)); if (result != 0) error_code = -1; fin: return (error_code); } /* * This function is very close to the cache_read function of the caching * library, which is used in the caching daemon. It reads cached data with the * specified key from the cache entry with entry_name. */ int __cached_read(struct cached_connection_ *connection, const char *entry_name, const char *key, size_t key_size, char *data, size_t *data_size) { size_t name_size, result_size; int error_code, rec_error_code; int result; assert(connection != NULL); result = 0; error_code = -1; result = send_credentials(connection, CET_READ_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, &key_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_write(connection, key, key_size); if (result != 0) goto fin; result = safe_read(connection, &rec_error_code, sizeof(int)); if (result != 0) goto fin; if (rec_error_code != 0) { error_code = rec_error_code; goto fin; } result = safe_read(connection, &result_size, sizeof(size_t)); if (result != 0) goto fin; if (result_size > *data_size) { *data_size = result_size; error_code = -2; goto fin; } result = safe_read(connection, data, result_size); if (result != 0) goto fin; *data_size = result_size; error_code = 0; fin: return (error_code); } /* * Initializes the mp_write_session. For such a session the new connection * would be opened. The data should be written to the session with * __cached_mp_write function. The __close_cached_mp_write_session function * should be used to submit session and __abandon_cached_mp_write_session - to * abandon it. When the session is submitted, the whole se */ struct cached_connection_ * __open_cached_mp_write_session(struct cached_connection_params const *params, const char *entry_name) { struct cached_connection_ *connection, *retval; size_t name_size; int error_code; int result; retval = NULL; connection = __open_cached_connection(params); if (connection == NULL) return (NULL); connection->mp_flag = 1; result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_read(connection, &error_code, sizeof(int)); if (result != 0) goto fin; if (error_code != 0) result = error_code; fin: if (result != 0) __close_cached_connection(connection); else retval = connection; return (retval); } /* * Adds new portion of data to the opened write session */ int __cached_mp_write(struct cached_connection_ *ws, const char *data, size_t data_size) { int request, result; int error_code; error_code = -1; request = CET_MP_WRITE_SESSION_WRITE_REQUEST; result = safe_write(ws, &request, sizeof(int)); if (result != 0) goto fin; result = safe_write(ws, &data_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(ws, data, data_size); if (result != 0) goto fin; result = safe_read(ws, &error_code, sizeof(int)); if (result != 0) error_code = -1; fin: return (error_code); } /* * Abandons all operations with the write session. All data, that were written * to the session before, are discarded. */ int __abandon_cached_mp_write_session(struct cached_connection_ *ws) { int notification; int result; notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION; result = safe_write(ws, ¬ification, sizeof(int)); __close_cached_connection(ws); return (result); } /* * Gracefully closes the write session. The data, that were previously written * to the session, are committed. */ int __close_cached_mp_write_session(struct cached_connection_ *ws) { int notification; int result; notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION; result = safe_write(ws, ¬ification, sizeof(int)); __close_cached_connection(ws); return (0); } struct cached_connection_ * __open_cached_mp_read_session(struct cached_connection_params const *params, const char *entry_name) { struct cached_connection_ *connection, *retval; size_t name_size; int error_code; int result; retval = NULL; connection = __open_cached_connection(params); if (connection == NULL) return (NULL); connection->mp_flag = 1; result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST); if (result != 0) goto fin; name_size = strlen(entry_name); result = safe_write(connection, &name_size, sizeof(size_t)); if (result != 0) goto fin; result = safe_write(connection, entry_name, name_size); if (result != 0) goto fin; result = safe_read(connection, &error_code, sizeof(int)); if (result != 0) goto fin; if (error_code != 0) result = error_code; fin: if (result != 0) __close_cached_connection(connection); else retval = connection; return (retval); } int __cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size) { size_t result_size; int error_code, rec_error_code; int request, result; error_code = -1; request = CET_MP_READ_SESSION_READ_REQUEST; result = safe_write(rs, &request, sizeof(int)); if (result != 0) goto fin; result = safe_read(rs, &rec_error_code, sizeof(int)); if (result != 0) goto fin; if (rec_error_code != 0) { error_code = rec_error_code; goto fin; } result = safe_read(rs, &result_size, sizeof(size_t)); if (result != 0) goto fin; if (result_size > *data_size) { *data_size = result_size; error_code = -2; goto fin; } result = safe_read(rs, data, result_size); if (result != 0) goto fin; *data_size = result_size; error_code = 0; fin: return (error_code); } int __close_cached_mp_read_session(struct cached_connection_ *rs) { __close_cached_connection(rs); return (0); } Index: head/lib/libc/net/res_config.h =================================================================== --- head/lib/libc/net/res_config.h (revision 298829) +++ head/lib/libc/net/res_config.h (revision 298830) @@ -1,6 +1,6 @@ /* $FreeBSD$ */ #define DEBUG 1 /* enable debugging code (needed for dig) */ #define RESOLVSORT /* allow sorting of addresses in gethostbyname */ -#undef SUNSECURITY /* verify gethostbyaddr() calls - WE DONT NEED IT */ +#undef SUNSECURITY /* verify gethostbyaddr() calls - WE DON'T NEED IT */ #define MULTI_PTRS_ARE_ALIASES 1 /* fold multiple PTR records into aliases */ Index: head/lib/libc/posix1e/acl_delete_entry.c =================================================================== --- head/lib/libc/posix1e/acl_delete_entry.c (revision 298829) +++ head/lib/libc/posix1e/acl_delete_entry.c (revision 298830) @@ -1,162 +1,162 @@ /* * Copyright (c) 2001-2002 Chris D. Faulhaber * 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 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include "namespace.h" #include #include "un-namespace.h" #include #include #include #include "acl_support.h" static int _entry_matches(const acl_entry_t a, const acl_entry_t b) { /* * There is a semantical difference here between NFSv4 and POSIX * draft ACLs. In POSIX, there may be only one entry for the particular * user or group. In NFSv4 ACL, there may be any number of them. We're * trying to be more specific here in that case. */ switch (_entry_brand(a)) { case ACL_BRAND_NFS4: if (a->ae_tag != b->ae_tag || a->ae_entry_type != b->ae_entry_type) return (0); /* If ae_ids matter, compare them as well. */ if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) { if (a->ae_id != b->ae_id) return (0); } return (1); default: if ((a->ae_tag == b->ae_tag) && (a->ae_id == b->ae_id)) return (1); } return (0); } /* * acl_delete_entry() (23.4.9): remove the ACL entry indicated by entry_d * from acl. */ int acl_delete_entry(acl_t acl, acl_entry_t entry_d) { struct acl *acl_int; struct acl_entry entry_int; int i, j, found = 0; if (acl == NULL || entry_d == NULL) { errno = EINVAL; return (-1); } acl_int = &acl->ats_acl; if (_entry_brand(entry_d) != _acl_brand(acl)) { errno = EINVAL; return (-1); } if ((acl->ats_acl.acl_cnt < 1) || (acl->ats_acl.acl_cnt > ACL_MAX_ENTRIES)) { errno = EINVAL; return (-1); } /* Use a local copy to prevent deletion of more than this entry */ entry_int = *entry_d; for (i = 0; i < acl->ats_acl.acl_cnt;) { if (_entry_matches(&(acl->ats_acl.acl_entry[i]), &entry_int)) { /* ...shift the remaining entries... */ for (j = i; j < acl->ats_acl.acl_cnt - 1; ++j) acl->ats_acl.acl_entry[j] = acl->ats_acl.acl_entry[j+1]; /* ...drop the count and zero the unused entry... */ acl->ats_acl.acl_cnt--; bzero(&acl->ats_acl.acl_entry[j], sizeof(struct acl_entry)); acl->ats_cur_entry = 0; - /* Continue with the loop to remove all maching entries. */ + /* Continue with the loop to remove all matching entries. */ found = 1; } else i++; } if (found) return (0); errno = EINVAL; return (-1); } int acl_delete_entry_np(acl_t acl, int offset) { struct acl *acl_int; int i; if (acl == NULL) { errno = EINVAL; return (-1); } acl_int = &acl->ats_acl; if (offset < 0 || offset >= acl_int->acl_cnt) { errno = EINVAL; return (-1); } if ((acl->ats_acl.acl_cnt < 1) || (acl->ats_acl.acl_cnt > ACL_MAX_ENTRIES)) { errno = EINVAL; return (-1); } /* ...shift the remaining entries... */ for (i = offset; i < acl->ats_acl.acl_cnt - 1; ++i) acl->ats_acl.acl_entry[i] = acl->ats_acl.acl_entry[i+1]; /* ...drop the count and zero the unused entry... */ acl->ats_acl.acl_cnt--; bzero(&acl->ats_acl.acl_entry[i], sizeof(struct acl_entry)); acl->ats_cur_entry = 0; return (0); } Index: head/lib/libc/posix1e/acl_support.c =================================================================== --- head/lib/libc/posix1e/acl_support.c (revision 298829) +++ head/lib/libc/posix1e/acl_support.c (revision 298830) @@ -1,418 +1,418 @@ /*- * Copyright (c) 1999-2001, 2008 Robert N. M. Watson * 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 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 AUTHOR 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. */ /* * Support functionality for the POSIX.1e ACL interface * These calls are intended only to be called within the library. */ #include __FBSDID("$FreeBSD$"); #include #include "namespace.h" #include #include "un-namespace.h" #include #include #include #include #include #include #include #include "acl_support.h" #define ACL_STRING_PERM_WRITE 'w' #define ACL_STRING_PERM_READ 'r' #define ACL_STRING_PERM_EXEC 'x' #define ACL_STRING_PERM_NONE '-' /* * Return 0, if both ACLs are identical. */ int _acl_differs(const acl_t a, const acl_t b) { int i; struct acl_entry *entrya, *entryb; assert(_acl_brand(a) == _acl_brand(b)); assert(_acl_brand(a) != ACL_BRAND_UNKNOWN); assert(_acl_brand(b) != ACL_BRAND_UNKNOWN); if (a->ats_acl.acl_cnt != b->ats_acl.acl_cnt) return (1); for (i = 0; i < b->ats_acl.acl_cnt; i++) { entrya = &(a->ats_acl.acl_entry[i]); entryb = &(b->ats_acl.acl_entry[i]); if (entrya->ae_tag != entryb->ae_tag || entrya->ae_id != entryb->ae_id || entrya->ae_perm != entryb->ae_perm || entrya->ae_entry_type != entryb->ae_entry_type || entrya->ae_flags != entryb->ae_flags) return (1); } return (0); } /* * _posix1e_acl_entry_compare -- compare two acl_entry structures to * determine the order they should appear in. Used by _posix1e_acl_sort to * sort ACL entries into the kernel-desired order -- i.e., the order useful * for evaluation and O(n) validity checking. Beter to have an O(nlogn) sort * in userland and an O(n) in kernel than to have both in kernel. */ typedef int (*compare)(const void *, const void *); static int _posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b) { assert(_entry_brand(a) == ACL_BRAND_POSIX); assert(_entry_brand(b) == ACL_BRAND_POSIX); /* * First, sort between tags -- conveniently defined in the correct * order for verification. */ if (a->ae_tag < b->ae_tag) return (-1); if (a->ae_tag > b->ae_tag) return (1); /* * Next compare uids/gids on appropriate types. */ if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) { if (a->ae_id < b->ae_id) return (-1); if (a->ae_id > b->ae_id) return (1); /* shouldn't be equal, fall through to the invalid case */ } /* * Don't know how to sort multiple entries of the rest--either it's * a bad entry, or there shouldn't be more than one. Ignore and the * validity checker can get it later. */ return (0); } /* * _posix1e_acl_sort -- sort ACL entries in POSIX.1e-formatted ACLs. */ void _posix1e_acl_sort(acl_t acl) { struct acl *acl_int; acl_int = &acl->ats_acl; qsort(&acl_int->acl_entry[0], acl_int->acl_cnt, sizeof(struct acl_entry), (compare) _posix1e_acl_entry_compare); } /* * acl_posix1e -- in what situations should we acl_sort before submission? * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or * ACL_TYPE_DEFAULT */ int _posix1e_acl(acl_t acl, acl_type_t type) { if (_acl_brand(acl) != ACL_BRAND_POSIX) return (0); return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT)); } /* * _posix1e_acl_check -- given an ACL, check its validity. This is mirrored * from code in sys/kern/kern_acl.c, and if changes are made in one, they * should be made in the other also. This copy of acl_check is made * available * in userland for the benefit of processes wanting to check ACLs * for validity before submitting them to the kernel, or for performing * in userland file system checking. Needless to say, the kernel makes * the real checks on calls to get/setacl. * * See the comments in kernel for explanation -- just briefly, it assumes * an already sorted ACL, and checks based on that assumption. The * POSIX.1e interface, acl_valid(), will perform the sort before calling * this. Returns 0 on success, EINVAL on failure. */ int _posix1e_acl_check(acl_t acl) { struct acl *acl_int; struct acl_entry *entry; /* current entry */ uid_t highest_uid=0, highest_gid=0; int stage = ACL_USER_OBJ; int i = 0; int count_user_obj=0, count_user=0, count_group_obj=0, count_group=0, count_mask=0, count_other=0; acl_int = &acl->ats_acl; /* printf("_posix1e_acl_check: checking acl with %d entries\n", acl->acl_cnt); */ while (i < acl_int->acl_cnt) { entry = &acl_int->acl_entry[i]; if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS) return (EINVAL); switch(entry->ae_tag) { case ACL_USER_OBJ: /* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n", i); */ if (stage > ACL_USER_OBJ) return (EINVAL); stage = ACL_USER; count_user_obj++; break; case ACL_USER: /* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */ if (stage > ACL_USER) return (EINVAL); stage = ACL_USER; if (count_user && (entry->ae_id <= highest_uid)) return (EINVAL); highest_uid = entry->ae_id; count_user++; break; case ACL_GROUP_OBJ: /* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n", i); */ if (stage > ACL_GROUP_OBJ) return (EINVAL); stage = ACL_GROUP; count_group_obj++; break; case ACL_GROUP: /* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */ if (stage > ACL_GROUP) return (EINVAL); stage = ACL_GROUP; if (count_group && (entry->ae_id <= highest_gid)) return (EINVAL); highest_gid = entry->ae_id; count_group++; break; case ACL_MASK: /* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */ if (stage > ACL_MASK) return (EINVAL); stage = ACL_MASK; count_mask++; break; case ACL_OTHER: /* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */ if (stage > ACL_OTHER) return (EINVAL); stage = ACL_OTHER; count_other++; break; default: /* printf("_posix1e_acl_check: %d: INVALID\n", i); */ return (EINVAL); } i++; } if (count_user_obj != 1) return (EINVAL); if (count_group_obj != 1) return (EINVAL); if (count_mask != 0 && count_mask != 1) return (EINVAL); if (count_other != 1) return (EINVAL); return (0); } /* * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill * in a string describing the permissions. */ int _posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf) { if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) { errno = ENOMEM; return (-1); } if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) { errno = EINVAL; return (-1); } buf[3] = 0; /* null terminate */ if (perm & ACL_READ) buf[0] = ACL_STRING_PERM_READ; else buf[0] = ACL_STRING_PERM_NONE; if (perm & ACL_WRITE) buf[1] = ACL_STRING_PERM_WRITE; else buf[1] = ACL_STRING_PERM_NONE; if (perm & ACL_EXECUTE) buf[2] = ACL_STRING_PERM_EXEC; else buf[2] = ACL_STRING_PERM_NONE; return (0); } /* * given a string, return a permission describing it */ int _posix1e_acl_string_to_perm(char *string, acl_perm_t *perm) { acl_perm_t myperm = ACL_PERM_NONE; char *ch; ch = string; while (*ch) { switch(*ch) { case ACL_STRING_PERM_READ: myperm |= ACL_READ; break; case ACL_STRING_PERM_WRITE: myperm |= ACL_WRITE; break; case ACL_STRING_PERM_EXEC: myperm |= ACL_EXECUTE; break; case ACL_STRING_PERM_NONE: break; default: return (EINVAL); } ch++; } *perm = myperm; return (0); } /* * Add an ACL entry without doing much checking, et al */ int _posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm) { struct acl *acl_int; struct acl_entry *e; acl_int = &acl->ats_acl; if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) { errno = ENOMEM; return (-1); } e = &(acl_int->acl_entry[acl_int->acl_cnt]); e->ae_perm = perm; e->ae_tag = tag; e->ae_id = id; acl_int->acl_cnt++; return (0); } /* * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" - * counterpart. It's neccessary for the old (pre-NFSv4 ACLs) binaries + * counterpart. It's necessary for the old (pre-NFSv4 ACLs) binaries * to work with new libc and kernel. Fixing 'type' for old binaries with * old libc and new kernel is being done by kern/vfs_acl.c:type_unold(). */ int _acl_type_unold(acl_type_t type) { switch (type) { case ACL_TYPE_ACCESS_OLD: return (ACL_TYPE_ACCESS); case ACL_TYPE_DEFAULT_OLD: return (ACL_TYPE_DEFAULT); default: return (type); } } char * string_skip_whitespace(char *string) { while (*string && ((*string == ' ') || (*string == '\t'))) string++; return (string); } void string_trim_trailing_whitespace(char *string) { char *end; if (*string == '\0') return; end = string + strlen(string) - 1; while (end != string) { if ((*end == ' ') || (*end == '\t')) { *end = '\0'; end--; } else { return; } } return; } Index: head/lib/libc/regex/regcomp.c =================================================================== --- head/lib/libc/regex/regcomp.c (revision 298829) +++ head/lib/libc/regex/regcomp.c (revision 298830) @@ -1,1818 +1,1818 @@ /*- * Copyright (c) 1992, 1993, 1994 Henry Spencer. * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * This code is derived from software contributed to Berkeley by * Henry Spencer. * * 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. * 4. 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. * * @(#)regcomp.c 8.5 (Berkeley) 3/20/94 */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)regcomp.c 8.5 (Berkeley) 3/20/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "collate.h" #include "utils.h" #include "regex2.h" #include "cname.h" /* * parse structure, passed up and down to avoid global variables and * other clumsinesses */ struct parse { char *next; /* next character in RE */ char *end; /* end of string (-> NUL normally) */ int error; /* has an error been seen? */ sop *strip; /* malloced strip */ sopno ssize; /* malloced strip size (allocated) */ sopno slen; /* malloced strip length (used) */ int ncsalloc; /* number of csets allocated */ struct re_guts *g; # define NPAREN 10 /* we need to remember () 1-9 for back refs */ sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ sopno pend[NPAREN]; /* -> ) ([0] unused) */ }; /* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === regcomp.c === */ static void p_ere(struct parse *p, int stop); static void p_ere_exp(struct parse *p); static void p_str(struct parse *p); static void p_bre(struct parse *p, int end1, int end2); static int p_simp_re(struct parse *p, int starordinary); static int p_count(struct parse *p); static void p_bracket(struct parse *p); static void p_b_term(struct parse *p, cset *cs); static void p_b_cclass(struct parse *p, cset *cs); static void p_b_eclass(struct parse *p, cset *cs); static wint_t p_b_symbol(struct parse *p); static wint_t p_b_coll_elem(struct parse *p, wint_t endc); static wint_t othercase(wint_t ch); static void bothcases(struct parse *p, wint_t ch); static void ordinary(struct parse *p, wint_t ch); static void nonnewline(struct parse *p); static void repeat(struct parse *p, sopno start, int from, int to); static int seterr(struct parse *p, int e); static cset *allocset(struct parse *p); static void freeset(struct parse *p, cset *cs); static void CHadd(struct parse *p, cset *cs, wint_t ch); static void CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max); static void CHaddtype(struct parse *p, cset *cs, wctype_t wct); static wint_t singleton(cset *cs); static sopno dupl(struct parse *p, sopno start, sopno finish); static void doemit(struct parse *p, sop op, size_t opnd); static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos); static void dofwd(struct parse *p, sopno pos, sop value); static int enlarge(struct parse *p, sopno size); static void stripsnug(struct parse *p, struct re_guts *g); static void findmust(struct parse *p, struct re_guts *g); static int altoffset(sop *scan, int offset); static void computejumps(struct parse *p, struct re_guts *g); static void computematchjumps(struct parse *p, struct re_guts *g); static sopno pluscount(struct parse *p, struct re_guts *g); static wint_t wgetnext(struct parse *p); #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ static char nuls[10]; /* place to point scanner in event of error */ /* * macros for use with parse structure * BEWARE: these know that the parse structure is named `p' !!! */ #define PEEK() (*p->next) #define PEEK2() (*(p->next+1)) #define MORE() (p->next < p->end) #define MORE2() (p->next+1 < p->end) #define SEE(c) (MORE() && PEEK() == (c)) #define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) #define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) #define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) #define NEXT() (p->next++) #define NEXT2() (p->next += 2) #define NEXTn(n) (p->next += (n)) #define GETNEXT() (*p->next++) #define WGETNEXT() wgetnext(p) #define SETERROR(e) seterr(p, (e)) #define REQUIRE(co, e) ((co) || SETERROR(e)) #define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) #define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) #define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) #define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) #define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) #define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) #define ASTERN(sop, pos) EMIT(sop, HERE()-pos) #define HERE() (p->slen) #define THERE() (p->slen - 1) #define THERETHERE() (p->slen - 2) #define DROP(n) (p->slen -= (n)) #ifndef NDEBUG static int never = 0; /* for use in asserts; shuts lint up */ #else #define never 0 /* some s have bugs too */ #endif /* Macro used by computejump()/computematchjump() */ #define MIN(a,b) ((a)<(b)?(a):(b)) /* - regcomp - interface for parser and compilation = extern int regcomp(regex_t *, const char *, int); = #define REG_BASIC 0000 = #define REG_EXTENDED 0001 = #define REG_ICASE 0002 = #define REG_NOSUB 0004 = #define REG_NEWLINE 0010 = #define REG_NOSPEC 0020 = #define REG_PEND 0040 = #define REG_DUMP 0200 */ int /* 0 success, otherwise REG_something */ regcomp(regex_t * __restrict preg, const char * __restrict pattern, int cflags) { struct parse pa; struct re_guts *g; struct parse *p = &pa; int i; size_t len; size_t maxlen; #ifdef REDEBUG # define GOODFLAGS(f) (f) #else # define GOODFLAGS(f) ((f)&~REG_DUMP) #endif cflags = GOODFLAGS(cflags); if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) return(REG_INVARG); if (cflags®_PEND) { if (preg->re_endp < pattern) return(REG_INVARG); len = preg->re_endp - pattern; } else len = strlen((char *)pattern); /* do the mallocs early so failure handling is easy */ g = (struct re_guts *)malloc(sizeof(struct re_guts)); if (g == NULL) return(REG_ESPACE); /* * Limit the pattern space to avoid a 32-bit overflow on buffer * extension. Also avoid any signed overflow in case of conversion * so make the real limit based on a 31-bit overflow. * * Likely not applicable on 64-bit systems but handle the case * generically (who are we to stop people from using ~715MB+ * patterns?). */ maxlen = ((size_t)-1 >> 1) / sizeof(sop) * 2 / 3; if (len >= maxlen) { free((char *)g); return(REG_ESPACE); } p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ assert(p->ssize >= len); p->strip = (sop *)malloc(p->ssize * sizeof(sop)); p->slen = 0; if (p->strip == NULL) { free((char *)g); return(REG_ESPACE); } /* set things up */ p->g = g; p->next = (char *)pattern; /* convenience; we do not modify it */ p->end = p->next + len; p->error = 0; p->ncsalloc = 0; for (i = 0; i < NPAREN; i++) { p->pbegin[i] = 0; p->pend[i] = 0; } g->sets = NULL; g->ncsets = 0; g->cflags = cflags; g->iflags = 0; g->nbol = 0; g->neol = 0; g->must = NULL; g->moffset = -1; g->charjump = NULL; g->matchjump = NULL; g->mlen = 0; g->nsub = 0; g->backrefs = 0; /* do it */ EMIT(OEND, 0); g->firststate = THERE(); if (cflags®_EXTENDED) p_ere(p, OUT); else if (cflags®_NOSPEC) p_str(p); else p_bre(p, OUT, OUT); EMIT(OEND, 0); g->laststate = THERE(); /* tidy up loose ends and fill things in */ stripsnug(p, g); findmust(p, g); /* only use Boyer-Moore algorithm if the pattern is bigger * than three characters */ if(g->mlen > 3) { computejumps(p, g); computematchjumps(p, g); if(g->matchjump == NULL && g->charjump != NULL) { free(g->charjump); g->charjump = NULL; } } g->nplus = pluscount(p, g); g->magic = MAGIC2; preg->re_nsub = g->nsub; preg->re_g = g; preg->re_magic = MAGIC1; #ifndef REDEBUG /* not debugging, so can't rely on the assert() in regexec() */ if (g->iflags&BAD) SETERROR(REG_ASSERT); #endif /* win or lose, we're done */ if (p->error != 0) /* lose */ regfree(preg); return(p->error); } /* - p_ere - ERE parser top level, concatenation and alternation == static void p_ere(struct parse *p, int_t stop); */ static void p_ere(struct parse *p, int stop) /* character this ERE should end at */ { char c; sopno prevback; sopno prevfwd; sopno conc; int first = 1; /* is this the first alternative? */ for (;;) { /* do a bunch of concatenated expressions */ conc = HERE(); while (MORE() && (c = PEEK()) != '|' && c != stop) p_ere_exp(p); (void)REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ if (!EAT('|')) break; /* NOTE BREAK OUT */ if (first) { INSERT(OCH_, conc); /* offset is wrong */ prevfwd = conc; prevback = conc; first = 0; } ASTERN(OOR1, prevback); prevback = THERE(); AHEAD(prevfwd); /* fix previous offset */ prevfwd = HERE(); EMIT(OOR2, 0); /* offset is very wrong */ } if (!first) { /* tail-end fixups */ AHEAD(prevfwd); ASTERN(O_CH, prevback); } assert(!MORE() || SEE(stop)); } /* - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op == static void p_ere_exp(struct parse *p); */ static void p_ere_exp(struct parse *p) { char c; wint_t wc; sopno pos; int count; int count2; sopno subno; int wascaret = 0; assert(MORE()); /* caller should have ensured this */ c = GETNEXT(); pos = HERE(); switch (c) { case '(': (void)REQUIRE(MORE(), REG_EPAREN); p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); if (!SEE(')')) p_ere(p, ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); (void)MUSTEAT(')', REG_EPAREN); break; #ifndef POSIX_MISTAKE case ')': /* happens only if no current unmatched ( */ /* * You may ask, why the ifndef? Because I didn't notice * this until slightly too late for 1003.2, and none of the * other 1003.2 regular-expression reviewers noticed it at * all. So an unmatched ) is legal POSIX, at least until * we can get it fixed. */ SETERROR(REG_EPAREN); break; #endif case '^': EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; wascaret = 1; break; case '$': EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; break; case '|': SETERROR(REG_EMPTY); break; case '*': case '+': case '?': SETERROR(REG_BADRPT); break; case '.': if (p->g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case '\\': (void)REQUIRE(MORE(), REG_EESCAPE); wc = WGETNEXT(); switch (wc) { case '<': EMIT(OBOW, 0); break; case '>': EMIT(OEOW, 0); break; default: ordinary(p, wc); break; } break; case '{': /* okay as ordinary except if digit follows */ (void)REQUIRE(!MORE() || !isdigit((uch)PEEK()), REG_BADRPT); /* FALLTHROUGH */ default: p->next--; wc = WGETNEXT(); ordinary(p, wc); break; } if (!MORE()) return; c = PEEK(); /* we call { a repetition if followed by a digit */ if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit((uch)PEEK2())) )) return; /* no repetition, we're done */ NEXT(); (void)REQUIRE(!wascaret, REG_BADRPT); switch (c) { case '*': /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); break; case '+': INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); break; case '?': /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, pos); /* offset slightly wrong */ ASTERN(OOR1, pos); /* this one's right */ AHEAD(pos); /* fix the OCH_ */ EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); break; case '{': count = p_count(p); if (EAT(',')) { if (isdigit((uch)PEEK())) { count2 = p_count(p); (void)REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EAT('}')) { /* error heuristics */ while (MORE() && PEEK() != '}') NEXT(); (void)REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } break; } if (!MORE()) return; c = PEEK(); if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit((uch)PEEK2())) ) ) return; SETERROR(REG_BADRPT); } /* - p_str - string (no metacharacters) "parser" == static void p_str(struct parse *p); */ static void p_str(struct parse *p) { (void)REQUIRE(MORE(), REG_EMPTY); while (MORE()) ordinary(p, WGETNEXT()); } /* - p_bre - BRE parser top level, anchoring and concatenation == static void p_bre(struct parse *p, int end1, \ == int end2); * Giving end1 as OUT essentially eliminates the end1/end2 check. * * This implementation is a bit of a kludge, in that a trailing $ is first * taken as an ordinary character and then revised to be an anchor. * The amount of lookahead needed to avoid this kludge is excessive. */ static void p_bre(struct parse *p, int end1, /* first terminating character */ int end2) /* second terminating character */ { sopno start = HERE(); int first = 1; /* first subexpression? */ int wasdollar = 0; if (EAT('^')) { EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; } while (MORE() && !SEETWO(end1, end2)) { wasdollar = p_simp_re(p, first); first = 0; } if (wasdollar) { /* oops, that was a trailing anchor */ DROP(1); EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; } (void)REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ } /* - p_simp_re - parse a simple RE, an atom possibly followed by a repetition == static int p_simp_re(struct parse *p, int starordinary); */ static int /* was the simple RE an unbackslashed $? */ p_simp_re(struct parse *p, int starordinary) /* is a leading * an ordinary character? */ { int c; int count; int count2; sopno pos; int i; wint_t wc; sopno subno; # define BACKSL (1<g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case BACKSL|'<': EMIT(OBOW, 0); break; case BACKSL|'>': EMIT(OEOW, 0); break; case BACKSL|'{': SETERROR(REG_BADRPT); break; case BACKSL|'(': p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); /* the MORE here is an error heuristic */ if (MORE() && !SEETWO('\\', ')')) p_bre(p, '\\', ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); (void)REQUIRE(EATTWO('\\', ')'), REG_EPAREN); break; case BACKSL|')': /* should not get here -- must be user */ case BACKSL|'}': SETERROR(REG_EPAREN); break; case BACKSL|'1': case BACKSL|'2': case BACKSL|'3': case BACKSL|'4': case BACKSL|'5': case BACKSL|'6': case BACKSL|'7': case BACKSL|'8': case BACKSL|'9': i = (c&~BACKSL) - '0'; assert(i < NPAREN); if (p->pend[i] != 0) { assert(i <= p->g->nsub); EMIT(OBACK_, i); assert(p->pbegin[i] != 0); assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); assert(OP(p->strip[p->pend[i]]) == ORPAREN); (void) dupl(p, p->pbegin[i]+1, p->pend[i]); EMIT(O_BACK, i); } else SETERROR(REG_ESUBREG); p->g->backrefs = 1; break; case '*': (void)REQUIRE(starordinary, REG_BADRPT); /* FALLTHROUGH */ default: p->next--; wc = WGETNEXT(); ordinary(p, wc); break; } if (EAT('*')) { /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); } else if (EATTWO('\\', '{')) { count = p_count(p); if (EAT(',')) { if (MORE() && isdigit((uch)PEEK())) { count2 = p_count(p); (void)REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EATTWO('\\', '}')) { /* error heuristics */ while (MORE() && !SEETWO('\\', '}')) NEXT(); (void)REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } } else if (c == '$') /* $ (but not \$) ends it */ return(1); return(0); } /* - p_count - parse a repetition count == static int p_count(struct parse *p); */ static int /* the value */ p_count(struct parse *p) { int count = 0; int ndigits = 0; while (MORE() && isdigit((uch)PEEK()) && count <= DUPMAX) { count = count*10 + (GETNEXT() - '0'); ndigits++; } (void)REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); return(count); } /* - p_bracket - parse a bracketed character list == static void p_bracket(struct parse *p); */ static void p_bracket(struct parse *p) { cset *cs; wint_t ch; /* Dept of Truly Sickening Special-Case Kludges */ if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { EMIT(OBOW, 0); NEXTn(6); return; } if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { EMIT(OEOW, 0); NEXTn(6); return; } if ((cs = allocset(p)) == NULL) return; if (p->g->cflags®_ICASE) cs->icase = 1; if (EAT('^')) cs->invert = 1; if (EAT(']')) CHadd(p, cs, ']'); else if (EAT('-')) CHadd(p, cs, '-'); while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) p_b_term(p, cs); if (EAT('-')) CHadd(p, cs, '-'); (void)MUSTEAT(']', REG_EBRACK); if (p->error != 0) /* don't mess things up further */ return; if (cs->invert && p->g->cflags®_NEWLINE) cs->bmp['\n' >> 3] |= 1 << ('\n' & 7); if ((ch = singleton(cs)) != OUT) { /* optimize singleton sets */ ordinary(p, ch); freeset(p, cs); } else EMIT(OANYOF, (int)(cs - p->g->sets)); } /* - p_b_term - parse one term of a bracketed character list == static void p_b_term(struct parse *p, cset *cs); */ static void p_b_term(struct parse *p, cset *cs) { char c; wint_t start, finish; wint_t i; struct xlocale_collate *table = (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; /* classify what we've got */ switch ((MORE()) ? PEEK() : '\0') { case '[': c = (MORE2()) ? PEEK2() : '\0'; break; case '-': SETERROR(REG_ERANGE); return; /* NOTE RETURN */ default: c = '\0'; break; } switch (c) { case ':': /* character class */ NEXT2(); (void)REQUIRE(MORE(), REG_EBRACK); c = PEEK(); (void)REQUIRE(c != '-' && c != ']', REG_ECTYPE); p_b_cclass(p, cs); (void)REQUIRE(MORE(), REG_EBRACK); (void)REQUIRE(EATTWO(':', ']'), REG_ECTYPE); break; case '=': /* equivalence class */ NEXT2(); (void)REQUIRE(MORE(), REG_EBRACK); c = PEEK(); (void)REQUIRE(c != '-' && c != ']', REG_ECOLLATE); p_b_eclass(p, cs); (void)REQUIRE(MORE(), REG_EBRACK); (void)REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); break; default: /* symbol, ordinary character, or range */ start = p_b_symbol(p); if (SEE('-') && MORE2() && PEEK2() != ']') { /* range */ NEXT(); if (EAT('-')) finish = '-'; else finish = p_b_symbol(p); } else finish = start; if (start == finish) CHadd(p, cs, start); else { if (table->__collate_load_error) { (void)REQUIRE((uch)start <= (uch)finish, REG_ERANGE); CHaddrange(p, cs, start, finish); } else { (void)REQUIRE(__collate_range_cmp(table, start, finish) <= 0, REG_ERANGE); for (i = 0; i <= UCHAR_MAX; i++) { if ( __collate_range_cmp(table, start, i) <= 0 && __collate_range_cmp(table, i, finish) <= 0 ) CHadd(p, cs, i); } } } break; } } /* - p_b_cclass - parse a character-class name and deal with it == static void p_b_cclass(struct parse *p, cset *cs); */ static void p_b_cclass(struct parse *p, cset *cs) { char *sp = p->next; size_t len; wctype_t wct; char clname[16]; while (MORE() && isalpha((uch)PEEK())) NEXT(); len = p->next - sp; if (len >= sizeof(clname) - 1) { SETERROR(REG_ECTYPE); return; } memcpy(clname, sp, len); clname[len] = '\0'; if ((wct = wctype(clname)) == 0) { SETERROR(REG_ECTYPE); return; } CHaddtype(p, cs, wct); } /* - p_b_eclass - parse an equivalence-class name and deal with it == static void p_b_eclass(struct parse *p, cset *cs); * * This implementation is incomplete. xxx */ static void p_b_eclass(struct parse *p, cset *cs) { wint_t c; c = p_b_coll_elem(p, '='); CHadd(p, cs, c); } /* - p_b_symbol - parse a character or [..]ed multicharacter collating symbol == static wint_t p_b_symbol(struct parse *p); */ static wint_t /* value of symbol */ p_b_symbol(struct parse *p) { wint_t value; (void)REQUIRE(MORE(), REG_EBRACK); if (!EATTWO('[', '.')) return(WGETNEXT()); /* collating symbol */ value = p_b_coll_elem(p, '.'); (void)REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); return(value); } /* - p_b_coll_elem - parse a collating-element name and look it up == static wint_t p_b_coll_elem(struct parse *p, wint_t endc); */ static wint_t /* value of collating element */ p_b_coll_elem(struct parse *p, wint_t endc) /* name ended by endc,']' */ { char *sp = p->next; struct cname *cp; int len; mbstate_t mbs; wchar_t wc; size_t clen; while (MORE() && !SEETWO(endc, ']')) NEXT(); if (!MORE()) { SETERROR(REG_EBRACK); return(0); } len = p->next - sp; for (cp = cnames; cp->name != NULL; cp++) if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') return(cp->code); /* known name */ memset(&mbs, 0, sizeof(mbs)); if ((clen = mbrtowc(&wc, sp, len, &mbs)) == len) return (wc); /* single character */ else if (clen == (size_t)-1 || clen == (size_t)-2) SETERROR(REG_ILLSEQ); else SETERROR(REG_ECOLLATE); /* neither */ return(0); } /* - othercase - return the case counterpart of an alphabetic == static wint_t othercase(wint_t ch); */ static wint_t /* if no counterpart, return ch */ othercase(wint_t ch) { assert(iswalpha(ch)); if (iswupper(ch)) return(towlower(ch)); else if (iswlower(ch)) return(towupper(ch)); else /* peculiar, but could happen */ return(ch); } /* - bothcases - emit a dualcase version of a two-case character == static void bothcases(struct parse *p, wint_t ch); * * Boy, is this implementation ever a kludge... */ static void bothcases(struct parse *p, wint_t ch) { char *oldnext = p->next; char *oldend = p->end; char bracket[3 + MB_LEN_MAX]; size_t n; mbstate_t mbs; assert(othercase(ch) != ch); /* p_bracket() would recurse */ p->next = bracket; memset(&mbs, 0, sizeof(mbs)); n = wcrtomb(bracket, ch, &mbs); assert(n != (size_t)-1); bracket[n] = ']'; bracket[n + 1] = '\0'; p->end = bracket+n+1; p_bracket(p); assert(p->next == p->end); p->next = oldnext; p->end = oldend; } /* - ordinary - emit an ordinary character == static void ordinary(struct parse *p, wint_t ch); */ static void ordinary(struct parse *p, wint_t ch) { cset *cs; if ((p->g->cflags®_ICASE) && iswalpha(ch) && othercase(ch) != ch) bothcases(p, ch); else if ((ch & OPDMASK) == ch) EMIT(OCHAR, ch); else { /* * Kludge: character is too big to fit into an OCHAR operand. * Emit a singleton set. */ if ((cs = allocset(p)) == NULL) return; CHadd(p, cs, ch); EMIT(OANYOF, (int)(cs - p->g->sets)); } } /* - nonnewline - emit REG_NEWLINE version of OANY == static void nonnewline(struct parse *p); * * Boy, is this implementation ever a kludge... */ static void nonnewline(struct parse *p) { char *oldnext = p->next; char *oldend = p->end; char bracket[4]; p->next = bracket; p->end = bracket+3; bracket[0] = '^'; bracket[1] = '\n'; bracket[2] = ']'; bracket[3] = '\0'; p_bracket(p); assert(p->next == bracket+3); p->next = oldnext; p->end = oldend; } /* - repeat - generate code for a bounded repetition, recursively if needed == static void repeat(struct parse *p, sopno start, int from, int to); */ static void repeat(struct parse *p, sopno start, /* operand from here to end of strip */ int from, /* repeated from this number */ int to) /* to this number of times (maybe INFINITY) */ { sopno finish = HERE(); # define N 2 # define INF 3 # define REP(f, t) ((f)*8 + (t)) # define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) sopno copy; if (p->error != 0) /* head off possible runaway recursion */ return; assert(from <= to); switch (REP(MAP(from), MAP(to))) { case REP(0, 0): /* must be user doing this */ DROP(finish-start); /* drop the operand */ break; case REP(0, 1): /* as x{1,1}? */ case REP(0, N): /* as x{1,n}? */ case REP(0, INF): /* as x{1,}? */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); /* offset is wrong... */ repeat(p, start+1, 1, to); ASTERN(OOR1, start); AHEAD(start); /* ... fix it */ EMIT(OOR2, 0); AHEAD(THERE()); ASTERN(O_CH, THERETHERE()); break; case REP(1, 1): /* trivial case */ /* done */ break; case REP(1, N): /* as x?x{1,n-1} */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); ASTERN(OOR1, start); AHEAD(start); EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); copy = dupl(p, start+1, finish+1); assert(copy == finish+4); repeat(p, copy, 1, to-1); break; case REP(1, INF): /* as x+ */ INSERT(OPLUS_, start); ASTERN(O_PLUS, start); break; case REP(N, N): /* as xx{m-1,n-1} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to-1); break; case REP(N, INF): /* as xx{n-1,INF} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to); break; default: /* "can't happen" */ SETERROR(REG_ASSERT); /* just in case */ break; } } /* - wgetnext - helper function for WGETNEXT() macro. Gets the next wide - character from the parse struct, signals a REG_ILLSEQ error if the - character can't be converted. Returns the number of bytes consumed. */ static wint_t wgetnext(struct parse *p) { mbstate_t mbs; wchar_t wc; size_t n; memset(&mbs, 0, sizeof(mbs)); n = mbrtowc(&wc, p->next, p->end - p->next, &mbs); if (n == (size_t)-1 || n == (size_t)-2) { SETERROR(REG_ILLSEQ); return (0); } if (n == 0) n = 1; p->next += n; return (wc); } /* - seterr - set an error condition == static int seterr(struct parse *p, int e); */ static int /* useless but makes type checking happy */ seterr(struct parse *p, int e) { if (p->error == 0) /* keep earliest error condition */ p->error = e; p->next = nuls; /* try to bring things to a halt */ p->end = nuls; return(0); /* make the return value well-defined */ } /* - allocset - allocate a set of characters for [] == static cset *allocset(struct parse *p); */ static cset * allocset(struct parse *p) { cset *cs, *ncs; ncs = realloc(p->g->sets, (p->g->ncsets + 1) * sizeof(*ncs)); if (ncs == NULL) { SETERROR(REG_ESPACE); return (NULL); } p->g->sets = ncs; cs = &p->g->sets[p->g->ncsets++]; memset(cs, 0, sizeof(*cs)); return(cs); } /* - freeset - free a now-unused set == static void freeset(struct parse *p, cset *cs); */ static void freeset(struct parse *p, cset *cs) { cset *top = &p->g->sets[p->g->ncsets]; free(cs->wides); free(cs->ranges); free(cs->types); memset(cs, 0, sizeof(*cs)); if (cs == top-1) /* recover only the easy case */ p->g->ncsets--; } /* - singleton - Determine whether a set contains only one character, - returning it if so, otherwise returning OUT. */ static wint_t singleton(cset *cs) { wint_t i, s, n; for (i = n = 0; i < NC; i++) if (CHIN(cs, i)) { n++; s = i; } if (n == 1) return (s); if (cs->nwides == 1 && cs->nranges == 0 && cs->ntypes == 0 && cs->icase == 0) return (cs->wides[0]); /* Don't bother handling the other cases. */ return (OUT); } /* - CHadd - add character to character set. */ static void CHadd(struct parse *p, cset *cs, wint_t ch) { wint_t nch, *newwides; assert(ch >= 0); if (ch < NC) cs->bmp[ch >> 3] |= 1 << (ch & 7); else { newwides = realloc(cs->wides, (cs->nwides + 1) * sizeof(*cs->wides)); if (newwides == NULL) { SETERROR(REG_ESPACE); return; } cs->wides = newwides; cs->wides[cs->nwides++] = ch; } if (cs->icase) { if ((nch = towlower(ch)) < NC) cs->bmp[nch >> 3] |= 1 << (nch & 7); if ((nch = towupper(ch)) < NC) cs->bmp[nch >> 3] |= 1 << (nch & 7); } } /* - CHaddrange - add all characters in the range [min,max] to a character set. */ static void CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max) { crange *newranges; for (; min < NC && min <= max; min++) CHadd(p, cs, min); if (min >= max) return; newranges = realloc(cs->ranges, (cs->nranges + 1) * sizeof(*cs->ranges)); if (newranges == NULL) { SETERROR(REG_ESPACE); return; } cs->ranges = newranges; cs->ranges[cs->nranges].min = min; cs->ranges[cs->nranges].max = max; cs->nranges++; } /* - CHaddtype - add all characters of a certain type to a character set. */ static void CHaddtype(struct parse *p, cset *cs, wctype_t wct) { wint_t i; wctype_t *newtypes; for (i = 0; i < NC; i++) if (iswctype(i, wct)) CHadd(p, cs, i); newtypes = realloc(cs->types, (cs->ntypes + 1) * sizeof(*cs->types)); if (newtypes == NULL) { SETERROR(REG_ESPACE); return; } cs->types = newtypes; cs->types[cs->ntypes++] = wct; } /* - dupl - emit a duplicate of a bunch of sops == static sopno dupl(struct parse *p, sopno start, sopno finish); */ static sopno /* start of duplicate */ dupl(struct parse *p, sopno start, /* from here */ sopno finish) /* to this less one */ { sopno ret = HERE(); sopno len = finish - start; assert(finish >= start); if (len == 0) return(ret); if (!enlarge(p, p->ssize + len)) /* this many unexpected additions */ return(ret); (void) memcpy((char *)(p->strip + p->slen), (char *)(p->strip + start), (size_t)len*sizeof(sop)); p->slen += len; return(ret); } /* - doemit - emit a strip operator == static void doemit(struct parse *p, sop op, size_t opnd); * * It might seem better to implement this as a macro with a function as * hard-case backup, but it's just too big and messy unless there are * some changes to the data structures. Maybe later. */ static void doemit(struct parse *p, sop op, size_t opnd) { /* avoid making error situations worse */ if (p->error != 0) return; /* deal with oversize operands ("can't happen", more or less) */ assert(opnd < 1<slen >= p->ssize) if (!enlarge(p, (p->ssize+1) / 2 * 3)) /* +50% */ return; /* finally, it's all reduced to the easy case */ p->strip[p->slen++] = SOP(op, opnd); } /* - doinsert - insert a sop into the strip == static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos); */ static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos) { sopno sn; sop s; int i; /* avoid making error situations worse */ if (p->error != 0) return; sn = HERE(); EMIT(op, opnd); /* do checks, ensure space */ assert(HERE() == sn+1); s = p->strip[sn]; /* adjust paren pointers */ assert(pos > 0); for (i = 1; i < NPAREN; i++) { if (p->pbegin[i] >= pos) { p->pbegin[i]++; } if (p->pend[i] >= pos) { p->pend[i]++; } } memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], (HERE()-pos-1)*sizeof(sop)); p->strip[pos] = s; } /* - dofwd - complete a forward reference == static void dofwd(struct parse *p, sopno pos, sop value); */ static void dofwd(struct parse *p, sopno pos, sop value) { /* avoid making error situations worse */ if (p->error != 0) return; assert(value < 1<strip[pos] = OP(p->strip[pos]) | value; } /* - enlarge - enlarge the strip == static int enlarge(struct parse *p, sopno size); */ static int enlarge(struct parse *p, sopno size) { sop *sp; if (p->ssize >= size) return 1; sp = (sop *)realloc(p->strip, size*sizeof(sop)); if (sp == NULL) { SETERROR(REG_ESPACE); return 0; } p->strip = sp; p->ssize = size; return 1; } /* - stripsnug - compact the strip == static void stripsnug(struct parse *p, struct re_guts *g); */ static void stripsnug(struct parse *p, struct re_guts *g) { g->nstates = p->slen; g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); if (g->strip == NULL) { SETERROR(REG_ESPACE); g->strip = p->strip; } } /* - findmust - fill in must and mlen with longest mandatory literal string == static void findmust(struct parse *p, struct re_guts *g); * * This algorithm could do fancy things like analyzing the operands of | * for common subsequences. Someday. This code is simple and finds most * of the interesting cases. * * Note that must and mlen got initialized during setup. */ static void findmust(struct parse *p, struct re_guts *g) { sop *scan; sop *start = NULL; sop *newstart = NULL; sopno newlen; sop s; char *cp; int offset; char buf[MB_LEN_MAX]; size_t clen; mbstate_t mbs; /* avoid making error situations worse */ if (p->error != 0) return; /* * It's not generally safe to do a ``char'' substring search on * multibyte character strings, but it's safe for at least * UTF-8 (see RFC 3629). */ if (MB_CUR_MAX > 1 && strcmp(_CurrentRuneLocale->__encoding, "UTF-8") != 0) return; /* find the longest OCHAR sequence in strip */ newlen = 0; offset = 0; g->moffset = 0; scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OCHAR: /* sequence member */ if (newlen == 0) { /* new sequence */ memset(&mbs, 0, sizeof(mbs)); newstart = scan - 1; } clen = wcrtomb(buf, OPND(s), &mbs); if (clen == (size_t)-1) goto toohard; newlen += clen; break; case OPLUS_: /* things that don't break one */ case OLPAREN: case ORPAREN: break; case OQUEST_: /* things that must be skipped */ case OCH_: offset = altoffset(scan, offset); scan--; do { scan += OPND(s); s = *scan; /* assert() interferes w debug printouts */ if (OP(s) != O_QUEST && OP(s) != O_CH && OP(s) != OOR2) { g->iflags |= BAD; return; } } while (OP(s) != O_QUEST && OP(s) != O_CH); /* FALLTHROUGH */ case OBOW: /* things that break a sequence */ case OEOW: case OBOL: case OEOL: case O_QUEST: case O_CH: case OEND: if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } newlen = 0; break; case OANY: if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } if (offset > -1) offset++; newlen = 0; break; case OANYOF: /* may or may not invalidate offset */ /* First, everything as OANY */ if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } if (offset > -1) offset++; newlen = 0; break; toohard: default: /* Anything here makes it impossible or too hard * to calculate the offset -- so we give up; * save the last known good offset, in case the * must sequence doesn't occur later. */ if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) g->moffset += offset; else g->moffset = offset; } offset = -1; newlen = 0; break; } } while (OP(s) != OEND); if (g->mlen == 0) { /* there isn't one */ g->moffset = -1; return; } /* turn it into a character string */ g->must = malloc((size_t)g->mlen + 1); if (g->must == NULL) { /* argh; just forget it */ g->mlen = 0; g->moffset = -1; return; } cp = g->must; scan = start; memset(&mbs, 0, sizeof(mbs)); while (cp < g->must + g->mlen) { while (OP(s = *scan++) != OCHAR) continue; clen = wcrtomb(cp, OPND(s), &mbs); assert(clen != (size_t)-1); cp += clen; } assert(cp == g->must + g->mlen); *cp++ = '\0'; /* just on general principles */ } /* - altoffset - choose biggest offset among multiple choices == static int altoffset(sop *scan, int offset); * * Compute, recursively if necessary, the largest offset among multiple * re paths. */ static int altoffset(sop *scan, int offset) { int largest; int try; sop s; /* If we gave up already on offsets, return */ if (offset == -1) return -1; largest = 0; try = 0; s = *scan++; while (OP(s) != O_QUEST && OP(s) != O_CH) { switch (OP(s)) { case OOR1: if (try > largest) largest = try; try = 0; break; case OQUEST_: case OCH_: try = altoffset(scan, try); if (try == -1) return -1; scan--; do { scan += OPND(s); s = *scan; if (OP(s) != O_QUEST && OP(s) != O_CH && OP(s) != OOR2) return -1; } while (OP(s) != O_QUEST && OP(s) != O_CH); /* We must skip to the next position, or we'll * leave altoffset() too early. */ scan++; break; case OANYOF: case OCHAR: case OANY: try++; case OBOW: case OEOW: case OLPAREN: case ORPAREN: case OOR2: break; default: try = -1; break; } if (try == -1) return -1; s = *scan++; } if (try > largest) largest = try; return largest+offset; } /* - computejumps - compute char jumps for BM scan == static void computejumps(struct parse *p, struct re_guts *g); * * This algorithm assumes g->must exists and is has size greater than * zero. It's based on the algorithm found on Computer Algorithms by * Sara Baase. * * A char jump is the number of characters one needs to jump based on * the value of the character from the text that was mismatched. */ static void computejumps(struct parse *p, struct re_guts *g) { int ch; int mindex; /* Avoid making errors worse */ if (p->error != 0) return; g->charjump = (int*) malloc((NC + 1) * sizeof(int)); if (g->charjump == NULL) /* Not a fatal error */ return; /* Adjust for signed chars, if necessary */ g->charjump = &g->charjump[-(CHAR_MIN)]; /* If the character does not exist in the pattern, the jump * is equal to the number of characters in the pattern. */ for (ch = CHAR_MIN; ch < (CHAR_MAX + 1); ch++) g->charjump[ch] = g->mlen; /* If the character does exist, compute the jump that would * take us to the last character in the pattern equal to it * (notice that we match right to left, so that last character * is the first one that would be matched). */ for (mindex = 0; mindex < g->mlen; mindex++) g->charjump[(int)g->must[mindex]] = g->mlen - mindex - 1; } /* - computematchjumps - compute match jumps for BM scan == static void computematchjumps(struct parse *p, struct re_guts *g); * * This algorithm assumes g->must exists and is has size greater than * zero. It's based on the algorithm found on Computer Algorithms by * Sara Baase. * * A match jump is the number of characters one needs to advance based * on the already-matched suffix. * Notice that all values here are minus (g->mlen-1), because of the way * the search algorithm works. */ static void computematchjumps(struct parse *p, struct re_guts *g) { int mindex; /* General "must" iterator */ int suffix; /* Keeps track of matching suffix */ int ssuffix; /* Keeps track of suffixes' suffix */ int* pmatches; /* pmatches[k] points to the next i * such that i+1...mlen is a substring * of k+1...k+mlen-i-1 */ /* Avoid making errors worse */ if (p->error != 0) return; pmatches = (int*) malloc(g->mlen * sizeof(int)); if (pmatches == NULL) { g->matchjump = NULL; return; } g->matchjump = (int*) malloc(g->mlen * sizeof(int)); if (g->matchjump == NULL) { /* Not a fatal error */ free(pmatches); return; } /* Set maximum possible jump for each character in the pattern */ for (mindex = 0; mindex < g->mlen; mindex++) g->matchjump[mindex] = 2*g->mlen - mindex - 1; /* Compute pmatches[] */ for (mindex = g->mlen - 1, suffix = g->mlen; mindex >= 0; mindex--, suffix--) { pmatches[mindex] = suffix; /* If a mismatch is found, interrupting the substring, * compute the matchjump for that position. If no * mismatch is found, then a text substring mismatched * against the suffix will also mismatch against the * substring. */ while (suffix < g->mlen && g->must[mindex] != g->must[suffix]) { g->matchjump[suffix] = MIN(g->matchjump[suffix], g->mlen - mindex - 1); suffix = pmatches[suffix]; } } /* Compute the matchjump up to the last substring found to jump * to the beginning of the largest must pattern prefix matching * it's own suffix. */ for (mindex = 0; mindex <= suffix; mindex++) g->matchjump[mindex] = MIN(g->matchjump[mindex], g->mlen + suffix - mindex); ssuffix = pmatches[suffix]; while (suffix < g->mlen) { while (suffix <= ssuffix && suffix < g->mlen) { g->matchjump[suffix] = MIN(g->matchjump[suffix], g->mlen + ssuffix - suffix); suffix++; } if (suffix < g->mlen) ssuffix = pmatches[ssuffix]; } free(pmatches); } /* - pluscount - count + nesting == static sopno pluscount(struct parse *p, struct re_guts *g); */ static sopno /* nesting depth */ pluscount(struct parse *p, struct re_guts *g) { sop *scan; sop s; sopno plusnest = 0; sopno maxnest = 0; if (p->error != 0) return(0); /* there may not be an OEND */ scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OPLUS_: plusnest++; break; case O_PLUS: if (plusnest > maxnest) maxnest = plusnest; plusnest--; break; } } while (OP(s) != OEND); if (plusnest != 0) g->iflags |= BAD; return(maxnest); } Index: head/lib/libc/resolv/res_comp.c =================================================================== --- head/lib/libc/resolv/res_comp.c (revision 298829) +++ head/lib/libc/resolv/res_comp.c (revision 298830) @@ -1,276 +1,276 @@ /* * Copyright (c) 1985, 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. * 4. 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * 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, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION 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. */ /* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Portions 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 ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_comp.c 8.1 (Berkeley) 6/4/93"; static const char rcsid[] = "$Id: res_comp.c,v 1.5 2005/07/28 06:51:50 marka Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "port_before.h" #include #include #include #include #include #include #include #include #include "port_after.h" /*% * Expand compressed domain name 'src' to full domain name. * - * \li 'msg' is a pointer to the begining of the message, + * \li 'msg' is a pointer to the beginning of the message, * \li 'eom' points to the first location after the message, * \li 'dst' is a pointer to a buffer of size 'dstsiz' for the result. * \li Return size of compressed name or -1 if there was an error. */ int dn_expand(const u_char *msg, const u_char *eom, const u_char *src, char *dst, int dstsiz) { int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); if (n > 0 && dst[0] == '.') dst[0] = '\0'; return (n); } /*% * Pack domain name 'exp_dn' in presentation form into 'comp_dn'. * * \li Return the size of the compressed name or -1. * \li 'length' is the size of the array pointed to by 'comp_dn'. */ int dn_comp(const char *src, u_char *dst, int dstsiz, u_char **dnptrs, u_char **lastdnptr) { return (ns_name_compress(src, dst, (size_t)dstsiz, (const u_char **)dnptrs, (const u_char **)lastdnptr)); } /*% * Skip over a compressed domain name. Return the size or -1. */ int dn_skipname(const u_char *ptr, const u_char *eom) { const u_char *saveptr = ptr; if (ns_name_skip(&ptr, eom) == -1) return (-1); return (ptr - saveptr); } /*% * Verify that a domain name uses an acceptable character set. * * Note the conspicuous absence of ctype macros in these definitions. On * non-ASCII hosts, we can't depend on string literals or ctype macros to * tell us anything about network-format data. The rest of the BIND system * is not careful about this, but for some reason, we're doing it right here. */ #define PERIOD 0x2e #define hyphenchar(c) ((c) == 0x2d) #define bslashchar(c) ((c) == 0x5c) #define underscorechar(c) ((c) == 0x5f) #define periodchar(c) ((c) == PERIOD) #define asterchar(c) ((c) == 0x2a) #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ || ((c) >= 0x61 && (c) <= 0x7a)) #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) #ifdef RES_ENFORCE_RFC1034 #define borderchar(c) (alphachar(c) || digitchar(c)) #else #define borderchar(c) (alphachar(c) || digitchar(c) || underscorechar(c)) #endif #define middlechar(c) (borderchar(c) || hyphenchar(c)) #define domainchar(c) ((c) > 0x20 && (c) < 0x7f) int res_hnok(const char *dn) { int pch = PERIOD, ch = *dn++; while (ch != '\0') { int nch = *dn++; if (periodchar(ch)) { (void)NULL; } else if (periodchar(pch)) { if (!borderchar(ch)) return (0); } else if (periodchar(nch) || nch == '\0') { if (!borderchar(ch)) return (0); } else { if (!middlechar(ch)) return (0); } pch = ch, ch = nch; } return (1); } /*% * hostname-like (A, MX, WKS) owners can have "*" as their first label * but must otherwise be as a host name. */ int res_ownok(const char *dn) { if (asterchar(dn[0])) { if (periodchar(dn[1])) return (res_hnok(dn+2)); if (dn[1] == '\0') return (1); } return (res_hnok(dn)); } /*% * SOA RNAMEs and RP RNAMEs can have any printable character in their first * label, but the rest of the name has to look like a host name. */ int res_mailok(const char *dn) { int ch, escaped = 0; /* "." is a valid missing representation */ if (*dn == '\0') return (1); /* otherwise