Index: head/lib/libc/db/btree/bt_split.c =================================================================== --- head/lib/libc/db/btree/bt_split.c (revision 298225) +++ head/lib/libc/db/btree/bt_split.c (revision 298226) @@ -1,803 +1,802 @@ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_split.c 8.10 (Berkeley) 1/9/95"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); -#include #include #include #include #include #include #include #include "btree.h" static int bt_broot(BTREE *, PAGE *, PAGE *, PAGE *); static PAGE *bt_page(BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t); static int bt_preserve(BTREE *, pgno_t); static PAGE *bt_psplit(BTREE *, PAGE *, PAGE *, PAGE *, indx_t *, size_t); static PAGE *bt_root(BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t); static int bt_rroot(BTREE *, PAGE *, PAGE *, PAGE *); static recno_t rec_total(PAGE *); #ifdef STATISTICS u_long bt_rootsplit, bt_split, bt_sortsplit, bt_pfxsaved; #endif /* * __BT_SPLIT -- Split the tree. * * Parameters: * t: tree * sp: page to split * key: key to insert * data: data to insert * flags: BIGKEY/BIGDATA flags * ilen: insert length * skip: index to leave open * * Returns: * RET_ERROR, RET_SUCCESS */ int __bt_split(BTREE *t, PAGE *sp, const DBT *key, const DBT *data, int flags, size_t ilen, u_int32_t argskip) { BINTERNAL *bi; BLEAF *bl, *tbl; DBT a, b; EPGNO *parent; PAGE *h, *l, *r, *lchild, *rchild; indx_t nxtindex; u_int16_t skip; u_int32_t n, nbytes, nksize; int parentsplit; char *dest; /* * Split the page into two pages, l and r. The split routines return * a pointer to the page into which the key should be inserted and with * skip set to the offset which should be used. Additionally, l and r * are pinned. */ skip = argskip; h = sp->pgno == P_ROOT ? bt_root(t, sp, &l, &r, &skip, ilen) : bt_page(t, sp, &l, &r, &skip, ilen); if (h == NULL) return (RET_ERROR); /* * Insert the new key/data pair into the leaf page. (Key inserts * always cause a leaf page to split first.) */ h->linp[skip] = h->upper -= ilen; dest = (char *)h + h->upper; if (F_ISSET(t, R_RECNO)) WR_RLEAF(dest, data, flags) else WR_BLEAF(dest, key, data, flags) /* If the root page was split, make it look right. */ if (sp->pgno == P_ROOT && (F_ISSET(t, R_RECNO) ? bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) goto err2; /* * Now we walk the parent page stack -- a LIFO stack of the pages that * were traversed when we searched for the page that split. Each stack * entry is a page number and a page index offset. The offset is for * the page traversed on the search. We've just split a page, so we * have to insert a new key into the parent page. * * If the insert into the parent page causes it to split, may have to * continue splitting all the way up the tree. We stop if the root * splits or the page inserted into didn't have to split to hold the * new key. Some algorithms replace the key for the old page as well * as the new page. We don't, as there's no reason to believe that the * first key on the old page is any better than the key we have, and, * in the case of a key being placed at index 0 causing the split, the * key is unavailable. * * There are a maximum of 5 pages pinned at any time. We keep the left * and right pages pinned while working on the parent. The 5 are the * two children, left parent and right parent (when the parent splits) * and the root page or the overflow key page when calling bt_preserve. * This code must make sure that all pins are released other than the * root page or overflow page which is unlocked elsewhere. */ while ((parent = BT_POP(t)) != NULL) { lchild = l; rchild = r; /* Get the parent page. */ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) goto err2; /* * The new key goes ONE AFTER the index, because the split * was to the right. */ skip = parent->index + 1; /* * Calculate the space needed on the parent page. * * Prefix trees: space hack when inserting into BINTERNAL * pages. Retain only what's needed to distinguish between * the new entry and the LAST entry on the page to its left. * If the keys compare equal, retain the entire key. Note, * we don't touch overflow keys, and the entire key must be * retained for the next-to-left most key on the leftmost * page of each level, or the search will fail. Applicable * ONLY to internal pages that have leaf pages as children. * Further reduction of the key between pairs of internal * pages loses too much information. */ switch (rchild->flags & P_TYPE) { case P_BINTERNAL: bi = GETBINTERNAL(rchild, 0); nbytes = NBINTERNAL(bi->ksize); break; case P_BLEAF: bl = GETBLEAF(rchild, 0); nbytes = NBINTERNAL(bl->ksize); if (t->bt_pfx && !(bl->flags & P_BIGKEY) && (h->prevpg != P_INVALID || skip > 1)) { tbl = GETBLEAF(lchild, NEXTINDEX(lchild) - 1); a.size = tbl->ksize; a.data = tbl->bytes; b.size = bl->ksize; b.data = bl->bytes; nksize = t->bt_pfx(&a, &b); n = NBINTERNAL(nksize); if (n < nbytes) { #ifdef STATISTICS bt_pfxsaved += nbytes - n; #endif nbytes = n; } else nksize = 0; } else nksize = 0; break; case P_RINTERNAL: case P_RLEAF: nbytes = NRINTERNAL; break; default: abort(); } /* Split the parent page if necessary or shift the indices. */ if ((u_int32_t)(h->upper - h->lower) < nbytes + sizeof(indx_t)) { sp = h; h = h->pgno == P_ROOT ? bt_root(t, h, &l, &r, &skip, nbytes) : bt_page(t, h, &l, &r, &skip, nbytes); if (h == NULL) goto err1; parentsplit = 1; } else { if (skip < (nxtindex = NEXTINDEX(h))) memmove(h->linp + skip + 1, h->linp + skip, (nxtindex - skip) * sizeof(indx_t)); h->lower += sizeof(indx_t); parentsplit = 0; } /* Insert the key into the parent page. */ switch (rchild->flags & P_TYPE) { case P_BINTERNAL: h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; memmove(dest, bi, nbytes); ((BINTERNAL *)dest)->pgno = rchild->pgno; break; case P_BLEAF: h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; WR_BINTERNAL(dest, nksize ? nksize : bl->ksize, rchild->pgno, bl->flags & P_BIGKEY); memmove(dest, bl->bytes, nksize ? nksize : bl->ksize); if (bl->flags & P_BIGKEY) { pgno_t pgno; memcpy(&pgno, bl->bytes, sizeof(pgno)); if (bt_preserve(t, pgno) == RET_ERROR) goto err1; } break; case P_RINTERNAL: /* * Update the left page count. If split * added at index 0, fix the correct page. */ if (skip > 0) dest = (char *)h + h->linp[skip - 1]; else dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; ((RINTERNAL *)dest)->nrecs = rec_total(lchild); ((RINTERNAL *)dest)->pgno = lchild->pgno; /* Update the right page count. */ h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; ((RINTERNAL *)dest)->nrecs = rec_total(rchild); ((RINTERNAL *)dest)->pgno = rchild->pgno; break; case P_RLEAF: /* * Update the left page count. If split * added at index 0, fix the correct page. */ if (skip > 0) dest = (char *)h + h->linp[skip - 1]; else dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; ((RINTERNAL *)dest)->nrecs = NEXTINDEX(lchild); ((RINTERNAL *)dest)->pgno = lchild->pgno; /* Update the right page count. */ h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; ((RINTERNAL *)dest)->nrecs = NEXTINDEX(rchild); ((RINTERNAL *)dest)->pgno = rchild->pgno; break; default: abort(); } /* Unpin the held pages. */ if (!parentsplit) { mpool_put(t->bt_mp, h, MPOOL_DIRTY); break; } /* If the root page was split, make it look right. */ if (sp->pgno == P_ROOT && (F_ISSET(t, R_RECNO) ? bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) goto err1; mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); } /* Unpin the held pages. */ mpool_put(t->bt_mp, l, MPOOL_DIRTY); mpool_put(t->bt_mp, r, MPOOL_DIRTY); /* Clear any pages left on the stack. */ return (RET_SUCCESS); /* * If something fails in the above loop we were already walking back * up the tree and the tree is now inconsistent. Nothing much we can * do about it but release any memory we're holding. */ err1: mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); err2: mpool_put(t->bt_mp, l, 0); mpool_put(t->bt_mp, r, 0); __dbpanic(t->bt_dbp); return (RET_ERROR); } /* * BT_PAGE -- Split a non-root page of a btree. * * Parameters: * t: tree * h: root page * lp: pointer to left page pointer * rp: pointer to right page pointer * skip: pointer to index to leave open * ilen: insert length * * Returns: * Pointer to page in which to insert or NULL on error. */ static PAGE * bt_page(BTREE *t, PAGE *h, PAGE **lp, PAGE **rp, indx_t *skip, size_t ilen) { PAGE *l, *r, *tp; pgno_t npg; #ifdef STATISTICS ++bt_split; #endif /* Put the new right page for the split into place. */ if ((r = __bt_new(t, &npg)) == NULL) return (NULL); r->pgno = npg; r->lower = BTDATAOFF; r->upper = t->bt_psize; r->nextpg = h->nextpg; r->prevpg = h->pgno; r->flags = h->flags & P_TYPE; /* * If we're splitting the last page on a level because we're appending * a key to it (skip is NEXTINDEX()), it's likely that the data is * sorted. Adding an empty page on the side of the level is less work * and can push the fill factor much higher than normal. If we're * wrong it's no big deal, we'll just do the split the right way next * time. It may look like it's equally easy to do a similar hack for * reverse sorted data, that is, split the tree left, but it's not. * Don't even try. */ if (h->nextpg == P_INVALID && *skip == NEXTINDEX(h)) { #ifdef STATISTICS ++bt_sortsplit; #endif h->nextpg = r->pgno; r->lower = BTDATAOFF + sizeof(indx_t); *skip = 0; *lp = h; *rp = r; return (r); } /* Put the new left page for the split into place. */ if ((l = (PAGE *)calloc(1, t->bt_psize)) == NULL) { mpool_put(t->bt_mp, r, 0); return (NULL); } l->pgno = h->pgno; l->nextpg = r->pgno; l->prevpg = h->prevpg; l->lower = BTDATAOFF; l->upper = t->bt_psize; l->flags = h->flags & P_TYPE; /* Fix up the previous pointer of the page after the split page. */ if (h->nextpg != P_INVALID) { if ((tp = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) { free(l); /* XXX mpool_free(t->bt_mp, r->pgno); */ return (NULL); } tp->prevpg = r->pgno; mpool_put(t->bt_mp, tp, MPOOL_DIRTY); } /* * Split right. The key/data pairs aren't sorted in the btree page so * it's simpler to copy the data from the split page onto two new pages * instead of copying half the data to the right page and compacting * the left page in place. Since the left page can't change, we have * to swap the original and the allocated left page after the split. */ tp = bt_psplit(t, h, l, r, skip, ilen); /* Move the new left page onto the old left page. */ memmove(h, l, t->bt_psize); if (tp == l) tp = h; free(l); *lp = h; *rp = r; return (tp); } /* * BT_ROOT -- Split the root page of a btree. * * Parameters: * t: tree * h: root page * lp: pointer to left page pointer * rp: pointer to right page pointer * skip: pointer to index to leave open * ilen: insert length * * Returns: * Pointer to page in which to insert or NULL on error. */ static PAGE * bt_root(BTREE *t, PAGE *h, PAGE **lp, PAGE **rp, indx_t *skip, size_t ilen) { PAGE *l, *r, *tp; pgno_t lnpg, rnpg; #ifdef STATISTICS ++bt_split; ++bt_rootsplit; #endif /* Put the new left and right pages for the split into place. */ if ((l = __bt_new(t, &lnpg)) == NULL || (r = __bt_new(t, &rnpg)) == NULL) return (NULL); l->pgno = lnpg; r->pgno = rnpg; l->nextpg = r->pgno; r->prevpg = l->pgno; l->prevpg = r->nextpg = P_INVALID; l->lower = r->lower = BTDATAOFF; l->upper = r->upper = t->bt_psize; l->flags = r->flags = h->flags & P_TYPE; /* Split the root page. */ tp = bt_psplit(t, h, l, r, skip, ilen); *lp = l; *rp = r; return (tp); } /* * BT_RROOT -- Fix up the recno root page after it has been split. * * Parameters: * t: tree * h: root page * l: left page * r: right page * * Returns: * RET_ERROR, RET_SUCCESS */ static int bt_rroot(BTREE *t, PAGE *h, PAGE *l, PAGE *r) { char *dest; /* Insert the left and right keys, set the header information. */ h->linp[0] = h->upper = t->bt_psize - NRINTERNAL; dest = (char *)h + h->upper; WR_RINTERNAL(dest, l->flags & P_RLEAF ? NEXTINDEX(l) : rec_total(l), l->pgno); __PAST_END(h->linp, 1) = h->upper -= NRINTERNAL; dest = (char *)h + h->upper; WR_RINTERNAL(dest, r->flags & P_RLEAF ? NEXTINDEX(r) : rec_total(r), r->pgno); h->lower = BTDATAOFF + 2 * sizeof(indx_t); /* Unpin the root page, set to recno internal page. */ h->flags &= ~P_TYPE; h->flags |= P_RINTERNAL; mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /* * BT_BROOT -- Fix up the btree root page after it has been split. * * Parameters: * t: tree * h: root page * l: left page * r: right page * * Returns: * RET_ERROR, RET_SUCCESS */ static int bt_broot(BTREE *t, PAGE *h, PAGE *l, PAGE *r) { BINTERNAL *bi; BLEAF *bl; u_int32_t nbytes; char *dest; /* * If the root page was a leaf page, change it into an internal page. * We copy the key we split on (but not the key's data, in the case of * a leaf page) to the new root page. * * The btree comparison code guarantees that the left-most key on any * level of the tree is never used, so it doesn't need to be filled in. */ nbytes = NBINTERNAL(0); h->linp[0] = h->upper = t->bt_psize - nbytes; dest = (char *)h + h->upper; WR_BINTERNAL(dest, 0, l->pgno, 0); switch (h->flags & P_TYPE) { case P_BLEAF: bl = GETBLEAF(r, 0); nbytes = NBINTERNAL(bl->ksize); __PAST_END(h->linp, 1) = h->upper -= nbytes; dest = (char *)h + h->upper; WR_BINTERNAL(dest, bl->ksize, r->pgno, 0); memmove(dest, bl->bytes, bl->ksize); /* * If the key is on an overflow page, mark the overflow chain * so it isn't deleted when the leaf copy of the key is deleted. */ if (bl->flags & P_BIGKEY) { pgno_t pgno; memcpy(&pgno, bl->bytes, sizeof(pgno)); if (bt_preserve(t, pgno) == RET_ERROR) return (RET_ERROR); } break; case P_BINTERNAL: bi = GETBINTERNAL(r, 0); nbytes = NBINTERNAL(bi->ksize); __PAST_END(h->linp, 1) = h->upper -= nbytes; dest = (char *)h + h->upper; memmove(dest, bi, nbytes); ((BINTERNAL *)dest)->pgno = r->pgno; break; default: abort(); } /* There are two keys on the page. */ h->lower = BTDATAOFF + 2 * sizeof(indx_t); /* Unpin the root page, set to btree internal page. */ h->flags &= ~P_TYPE; h->flags |= P_BINTERNAL; mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /* * BT_PSPLIT -- Do the real work of splitting the page. * * Parameters: * t: tree * h: page to be split * l: page to put lower half of data * r: page to put upper half of data * pskip: pointer to index to leave open * ilen: insert length * * Returns: * Pointer to page in which to insert. */ static PAGE * bt_psplit(BTREE *t, PAGE *h, PAGE *l, PAGE *r, indx_t *pskip, size_t ilen) { BINTERNAL *bi; BLEAF *bl; CURSOR *c; RLEAF *rl; PAGE *rval; void *src; indx_t full, half, nxt, off, skip, top, used; u_int32_t nbytes; int bigkeycnt, isbigkey; /* * Split the data to the left and right pages. Leave the skip index * open. Additionally, make some effort not to split on an overflow * key. This makes internal page processing faster and can save * space as overflow keys used by internal pages are never deleted. */ bigkeycnt = 0; skip = *pskip; full = t->bt_psize - BTDATAOFF; half = full / 2; used = 0; for (nxt = off = 0, top = NEXTINDEX(h); nxt < top; ++off) { if (skip == off) { nbytes = ilen; isbigkey = 0; /* XXX: not really known. */ } else switch (h->flags & P_TYPE) { case P_BINTERNAL: src = bi = GETBINTERNAL(h, nxt); nbytes = NBINTERNAL(bi->ksize); isbigkey = bi->flags & P_BIGKEY; break; case P_BLEAF: src = bl = GETBLEAF(h, nxt); nbytes = NBLEAF(bl); isbigkey = bl->flags & P_BIGKEY; break; case P_RINTERNAL: src = GETRINTERNAL(h, nxt); nbytes = NRINTERNAL; isbigkey = 0; break; case P_RLEAF: src = rl = GETRLEAF(h, nxt); nbytes = NRLEAF(rl); isbigkey = 0; break; default: abort(); } /* * If the key/data pairs are substantial fractions of the max * possible size for the page, it's possible to get situations * where we decide to try and copy too much onto the left page. * Make sure that doesn't happen. */ if ((skip <= off && used + nbytes + sizeof(indx_t) >= full) || nxt == top - 1) { --off; break; } /* Copy the key/data pair, if not the skipped index. */ if (skip != off) { ++nxt; l->linp[off] = l->upper -= nbytes; memmove((char *)l + l->upper, src, nbytes); } used += nbytes + sizeof(indx_t); if (used >= half) { if (!isbigkey || bigkeycnt == 3) break; else ++bigkeycnt; } } /* * Off is the last offset that's valid for the left page. * Nxt is the first offset to be placed on the right page. */ l->lower += (off + 1) * sizeof(indx_t); /* * If splitting the page that the cursor was on, the cursor has to be * adjusted to point to the same record as before the split. If the * cursor is at or past the skipped slot, the cursor is incremented by * one. If the cursor is on the right page, it is decremented by the * number of records split to the left page. */ c = &t->bt_cursor; if (F_ISSET(c, CURS_INIT) && c->pg.pgno == h->pgno) { if (c->pg.index >= skip) ++c->pg.index; if (c->pg.index < nxt) /* Left page. */ c->pg.pgno = l->pgno; else { /* Right page. */ c->pg.pgno = r->pgno; c->pg.index -= nxt; } } /* * If the skipped index was on the left page, just return that page. * Otherwise, adjust the skip index to reflect the new position on * the right page. */ if (skip <= off) { skip = MAX_PAGE_OFFSET; rval = l; } else { rval = r; *pskip -= nxt; } for (off = 0; nxt < top; ++off) { if (skip == nxt) { ++off; skip = MAX_PAGE_OFFSET; } switch (h->flags & P_TYPE) { case P_BINTERNAL: src = bi = GETBINTERNAL(h, nxt); nbytes = NBINTERNAL(bi->ksize); break; case P_BLEAF: src = bl = GETBLEAF(h, nxt); nbytes = NBLEAF(bl); break; case P_RINTERNAL: src = GETRINTERNAL(h, nxt); nbytes = NRINTERNAL; break; case P_RLEAF: src = rl = GETRLEAF(h, nxt); nbytes = NRLEAF(rl); break; default: abort(); } ++nxt; r->linp[off] = r->upper -= nbytes; memmove((char *)r + r->upper, src, nbytes); } r->lower += off * sizeof(indx_t); /* If the key is being appended to the page, adjust the index. */ if (skip == top) r->lower += sizeof(indx_t); return (rval); } /* * BT_PRESERVE -- Mark a chain of pages as used by an internal node. * * Chains of indirect blocks pointed to by leaf nodes get reclaimed when the * record that references them gets deleted. Chains pointed to by internal * pages never get deleted. This routine marks a chain as pointed to by an * internal page. * * Parameters: * t: tree * pg: page number of first page in the chain. * * Returns: * RET_SUCCESS, RET_ERROR. */ static int bt_preserve(BTREE *t, pgno_t pg) { PAGE *h; if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); h->flags |= P_PRESERVE; mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /* * REC_TOTAL -- Return the number of recno entries below a page. * * Parameters: * h: page * * Returns: * The number of recno entries below a page. * * XXX * These values could be set by the bt_psplit routine. The problem is that the * entry has to be popped off of the stack etc. or the values have to be passed * all the way back to bt_split/bt_rroot and it's not very clean. */ static recno_t rec_total(PAGE *h) { recno_t recs; indx_t nxt, top; for (recs = 0, nxt = 0, top = NEXTINDEX(h); nxt < top; ++nxt) recs += GETRINTERNAL(h, nxt)->nrecs; return (recs); } Index: head/lib/libc/gen/arc4random.c =================================================================== --- head/lib/libc/gen/arc4random.c (revision 298225) +++ head/lib/libc/gen/arc4random.c (revision 298226) @@ -1,314 +1,313 @@ /* $OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $ */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Arc4 random number generator for OpenBSD. * * This code is derived from section 17.1 of Applied Cryptography, * second edition, which describes a stream cipher allegedly * compatible with RSA Labs "RC4" cipher (the actual description of * which is a trade secret). The same algorithm is used as a stream * cipher called "arcfour" in Tatu Ylonen's ssh package. * * RC4 is a registered trademark of RSA Laboratories. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include -#include #include #include #include #include #include "libc_private.h" #include "un-namespace.h" #ifdef __GNUC__ #define inline __inline #else /* !__GNUC__ */ #define inline #endif /* !__GNUC__ */ struct arc4_stream { u_int8_t i; u_int8_t j; u_int8_t s[256]; }; static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; #define RANDOMDEV "/dev/random" #define KEYSIZE 128 #define _ARC4_LOCK() \ do { \ if (__isthreaded) \ _pthread_mutex_lock(&arc4random_mtx); \ } while (0) #define _ARC4_UNLOCK() \ do { \ if (__isthreaded) \ _pthread_mutex_unlock(&arc4random_mtx); \ } while (0) static int rs_initialized; static struct arc4_stream rs; static pid_t arc4_stir_pid; static int arc4_count; extern int __sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen); static inline u_int8_t arc4_getbyte(void); static void arc4_stir(void); static inline void arc4_init(void) { int n; for (n = 0; n < 256; n++) rs.s[n] = n; rs.i = 0; rs.j = 0; } static inline void arc4_addrandom(u_char *dat, int datlen) { int n; u_int8_t si; rs.i--; for (n = 0; n < 256; n++) { rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si + dat[n % datlen]); rs.s[rs.i] = rs.s[rs.j]; rs.s[rs.j] = si; } rs.j = rs.i; } static size_t arc4_sysctl(u_char *buf, size_t size) { int mib[2]; size_t len, done; mib[0] = CTL_KERN; mib[1] = KERN_ARND; done = 0; do { len = size; if (__sysctl(mib, 2, buf, &len, NULL, 0) == -1) return (done); done += len; buf += len; size -= len; } while (size > 0); return (done); } static void arc4_stir(void) { int done, fd, i; struct { struct timeval tv; pid_t pid; u_char rnd[KEYSIZE]; } rdat; if (!rs_initialized) { arc4_init(); rs_initialized = 1; } done = 0; if (arc4_sysctl((u_char *)&rdat, KEYSIZE) == KEYSIZE) done = 1; if (!done) { fd = _open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0); if (fd >= 0) { if (_read(fd, &rdat, KEYSIZE) == KEYSIZE) done = 1; (void)_close(fd); } } if (!done) { (void)gettimeofday(&rdat.tv, NULL); rdat.pid = getpid(); /* We'll just take whatever was on the stack too... */ } arc4_addrandom((u_char *)&rdat, KEYSIZE); /* * Discard early keystream, as per recommendations in: * "(Not So) Random Shuffles of RC4" by Ilya Mironov. */ for (i = 0; i < 1024; i++) (void)arc4_getbyte(); arc4_count = 1600000; } static void arc4_stir_if_needed(void) { pid_t pid = getpid(); if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) { arc4_stir_pid = pid; arc4_stir(); } } static inline u_int8_t arc4_getbyte(void) { u_int8_t si, sj; rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si); sj = rs.s[rs.j]; rs.s[rs.i] = sj; rs.s[rs.j] = si; return (rs.s[(si + sj) & 0xff]); } static inline u_int32_t arc4_getword(void) { u_int32_t val; val = arc4_getbyte() << 24; val |= arc4_getbyte() << 16; val |= arc4_getbyte() << 8; val |= arc4_getbyte(); return val; } void arc4random_stir(void) { _ARC4_LOCK(); arc4_stir(); _ARC4_UNLOCK(); } void arc4random_addrandom(u_char *dat, int datlen) { _ARC4_LOCK(); if (!rs_initialized) arc4_stir(); arc4_addrandom(dat, datlen); _ARC4_UNLOCK(); } u_int32_t arc4random(void) { u_int32_t val; _ARC4_LOCK(); arc4_count -= 4; arc4_stir_if_needed(); val = arc4_getword(); _ARC4_UNLOCK(); return val; } void arc4random_buf(void *_buf, size_t n) { u_char *buf = (u_char *)_buf; _ARC4_LOCK(); arc4_stir_if_needed(); while (n--) { if (--arc4_count <= 0) arc4_stir(); buf[n] = arc4_getbyte(); } _ARC4_UNLOCK(); } /* * Calculate a uniformly distributed random number less than upper_bound * avoiding "modulo bias". * * Uniformity is achieved by generating new random numbers until the one * returned is outside the range [0, 2**32 % upper_bound). This * guarantees the selected random number will be inside * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) * after reduction modulo upper_bound. */ u_int32_t arc4random_uniform(u_int32_t upper_bound) { u_int32_t r, min; if (upper_bound < 2) return 0; /* 2**32 % x == (2**32 - x) % x */ min = -upper_bound % upper_bound; /* * This could theoretically loop forever but each retry has * p > 0.5 (worst case, usually far better) of selecting a * number inside the range we need, so it should rarely need * to re-roll. */ for (;;) { r = arc4random(); if (r >= min) break; } return r % upper_bound; } #if 0 /*-------- Test code for i386 --------*/ #include #include int main(int argc, char **argv) { const int iter = 1000000; int i; pctrval v; v = rdtsc(); for (i = 0; i < iter; i++) arc4random(); v = rdtsc() - v; v /= iter; printf("%qd cycles\n", v); } #endif Index: head/lib/libc/gen/devname.c =================================================================== --- head/lib/libc/gen/devname.c (revision 298225) +++ head/lib/libc/gen/devname.c (revision 298226) @@ -1,76 +1,75 @@ /* * Copyright (c) 1989, 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)devname.c 8.2 (Berkeley) 4/29/95"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); -#include +#include #include #include #include #include #include -#include #include char * devname_r(dev_t dev, mode_t type, char *buf, int len) { int i; size_t j; if (dev == NODEV || !(S_ISCHR(type) || S_ISBLK(dev))) { strlcpy(buf, "#NODEV", len); return (buf); } if (S_ISCHR(type)) { j = len; i = sysctlbyname("kern.devname", buf, &j, &dev, sizeof (dev)); if (i == 0) return (buf); } /* Finally just format it */ snprintf(buf, len, "#%c:%#jx", S_ISCHR(type) ? 'C' : 'B', (uintmax_t)dev); return (buf); } char * devname(dev_t dev, mode_t type) { static char buf[SPECNAMELEN + 1]; return (devname_r(dev, type, buf, sizeof(buf))); } Index: head/lib/libc/gen/exec.c =================================================================== --- head/lib/libc/gen/exec.c (revision 298225) +++ head/lib/libc/gen/exec.c (revision 298226) @@ -1,282 +1,281 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)exec.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include -#include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" extern char **environ; int execl(const char *name, const char *arg, ...) { va_list ap; const char **argv; int n; va_start(ap, arg); n = 1; while (va_arg(ap, char *) != NULL) n++; va_end(ap); argv = alloca((n + 1) * sizeof(*argv)); if (argv == NULL) { errno = ENOMEM; return (-1); } va_start(ap, arg); n = 1; argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; va_end(ap); return (_execve(name, __DECONST(char **, argv), environ)); } int execle(const char *name, const char *arg, ...) { va_list ap; const char **argv; char **envp; int n; va_start(ap, arg); n = 1; while (va_arg(ap, char *) != NULL) n++; va_end(ap); argv = alloca((n + 1) * sizeof(*argv)); if (argv == NULL) { errno = ENOMEM; return (-1); } va_start(ap, arg); n = 1; argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; envp = va_arg(ap, char **); va_end(ap); return (_execve(name, __DECONST(char **, argv), envp)); } int execlp(const char *name, const char *arg, ...) { va_list ap; const char **argv; int n; va_start(ap, arg); n = 1; while (va_arg(ap, char *) != NULL) n++; va_end(ap); argv = alloca((n + 1) * sizeof(*argv)); if (argv == NULL) { errno = ENOMEM; return (-1); } va_start(ap, arg); n = 1; argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; va_end(ap); return (execvp(name, __DECONST(char **, argv))); } int execv(const char *name, char * const *argv) { (void)_execve(name, argv, environ); return (-1); } int execvp(const char *name, char * const *argv) { return (_execvpe(name, argv, environ)); } static int execvPe(const char *name, const char *path, char * const *argv, char * const *envp) { const char **memp; size_t cnt, lp, ln; int eacces, save_errno; char *cur, buf[MAXPATHLEN]; const char *p, *bp; struct stat sb; eacces = 0; /* If it's an absolute or relative path name, it's easy. */ if (strchr(name, '/')) { bp = name; cur = NULL; goto retry; } bp = buf; /* If it's an empty path name, fail in the usual POSIX way. */ if (*name == '\0') { errno = ENOENT; return (-1); } cur = alloca(strlen(path) + 1); if (cur == NULL) { errno = ENOMEM; return (-1); } strcpy(cur, path); while ((p = strsep(&cur, ":")) != NULL) { /* * It's a SHELL path -- double, leading and trailing colons * mean the current directory. */ if (*p == '\0') { p = "."; lp = 1; } else lp = strlen(p); ln = strlen(name); /* * If the path is too long complain. This is a possible * security issue; given a way to make the path too long * the user may execute the wrong program. */ if (lp + ln + 2 > sizeof(buf)) { (void)_write(STDERR_FILENO, "execvP: ", 8); (void)_write(STDERR_FILENO, p, lp); (void)_write(STDERR_FILENO, ": path too long\n", 16); continue; } bcopy(p, buf, lp); buf[lp] = '/'; bcopy(name, buf + lp + 1, ln); buf[lp + ln + 1] = '\0'; retry: (void)_execve(bp, argv, envp); switch (errno) { case E2BIG: goto done; case ELOOP: case ENAMETOOLONG: case ENOENT: break; case ENOEXEC: for (cnt = 0; argv[cnt]; ++cnt) ; memp = alloca((cnt + 2) * sizeof(char *)); if (memp == NULL) { /* errno = ENOMEM; XXX override ENOEXEC? */ goto done; } memp[0] = "sh"; memp[1] = bp; bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); (void)_execve(_PATH_BSHELL, __DECONST(char **, memp), envp); goto done; case ENOMEM: goto done; case ENOTDIR: break; case ETXTBSY: /* * We used to retry here, but sh(1) doesn't. */ goto done; default: /* * EACCES may be for an inaccessible directory or * a non-executable file. Call stat() to decide * which. This also handles ambiguities for EFAULT * and EIO, and undocumented errors like ESTALE. * We hope that the race for a stat() is unimportant. */ save_errno = errno; if (stat(bp, &sb) != 0) break; if (save_errno == EACCES) { eacces = 1; continue; } errno = save_errno; goto done; } } if (eacces) errno = EACCES; else errno = ENOENT; done: return (-1); } int execvP(const char *name, const char *path, char * const argv[]) { return execvPe(name, path, argv, environ); } int _execvpe(const char *name, char * const argv[], char * const envp[]) { const char *path; /* Get the path we're searching. */ if ((path = getenv("PATH")) == NULL) path = _PATH_DEFPATH; return (execvPe(name, path, argv, envp)); } Index: head/lib/libc/gen/getnetgrent.c =================================================================== --- head/lib/libc/gen/getnetgrent.c (revision 298225) +++ head/lib/libc/gen/getnetgrent.c (revision 298226) @@ -1,659 +1,658 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getnetgrent.c 8.2 (Berkeley) 4/27/95"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #ifdef YP /* * Notes: * We want to be able to use NIS netgroups properly while retaining * the ability to use a local /etc/netgroup file. Unfortunately, you * can't really do both at the same time - at least, not efficiently. * NetBSD deals with this problem by creating a netgroup database * using Berkeley DB (just like the password database) that allows * for lookups using netgroup, netgroup.byuser or netgroup.byhost * searches. This is a neat idea, but I don't have time to implement * something like that now. (I think ultimately it would be nice * if we DB-fied the group and netgroup stuff all in one shot, but * for now I'm satisfied just to have something that works well * without requiring massive code changes.) * * Therefore, to still permit the use of the local file and maintain * optimum NIS performance, we allow for the following conditions: * * - If /etc/netgroup does not exist and NIS is turned on, we use * NIS netgroups only. * * - If /etc/netgroup exists but is empty, we use NIS netgroups * only. * * - If /etc/netgroup exists and contains _only_ a '+', we use * NIS netgroups only. * * - If /etc/netgroup exists, contains locally defined netgroups * and a '+', we use a mixture of NIS and the local entries. * This method should return the same NIS data as just using * NIS alone, but it will be slower if the NIS netgroup database * is large (innetgr() in particular will suffer since extra * processing has to be done in order to determine memberships * using just the raw netgroup data). * * - If /etc/netgroup exists and contains only locally defined * netgroup entries, we use just those local entries and ignore * NIS (this is the original, pre-NIS behavior). */ #include #include #include -#include -#include #include +#include #include static char *_netgr_yp_domain; int _use_only_yp; static int _netgr_yp_enabled; static int _yp_innetgr; #endif #ifndef _PATH_NETGROUP #define _PATH_NETGROUP "/etc/netgroup" #endif /* * Static Variables and functions used by setnetgrent(), getnetgrent() and * endnetgrent(). * There are two linked lists: * - linelist is just used by setnetgrent() to parse the net group file via. * parse_netgrp() * - netgrp is the list of entries for the current netgroup */ struct linelist { struct linelist *l_next; /* Chain ptr. */ int l_parsed; /* Flag for cycles */ char *l_groupname; /* Name of netgroup */ char *l_line; /* Netgroup entrie(s) to be parsed */ }; struct netgrp { struct netgrp *ng_next; /* Chain ptr */ char *ng_str[3]; /* Field pointers, see below */ }; #define NG_HOST 0 /* Host name */ #define NG_USER 1 /* User name */ #define NG_DOM 2 /* and Domain name */ static struct linelist *linehead = (struct linelist *)0; static struct netgrp *nextgrp = (struct netgrp *)0; static struct { struct netgrp *gr; char *grname; } grouphead = { (struct netgrp *)0, (char *)0, }; static FILE *netf = (FILE *)0; static int parse_netgrp(const char *); static struct linelist *read_for_group(const char *); void setnetgrent(const char *); void endnetgrent(void); int getnetgrent(char **, char **, char **); int innetgr(const char *, const char *, const char *, const char *); #define LINSIZ 1024 /* Length of netgroup file line */ /* * setnetgrent() * Parse the netgroup file looking for the netgroup and build the list * of netgrp structures. Let parse_netgrp() and read_for_group() do * most of the work. */ void setnetgrent(const char *group) { #ifdef YP struct stat _yp_statp; char _yp_plus; #endif /* Sanity check */ if (group == NULL || !strlen(group)) return; if (grouphead.gr == NULL || strcmp(group, grouphead.grname)) { endnetgrent(); #ifdef YP /* Presumed guilty until proven innocent. */ _use_only_yp = 0; /* * If /etc/netgroup doesn't exist or is empty, * use NIS exclusively. */ if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) && errno == ENOENT) || _yp_statp.st_size == 0) _use_only_yp = _netgr_yp_enabled = 1; if ((netf = fopen(_PATH_NETGROUP,"re")) != NULL ||_use_only_yp){ /* * Icky: grab the first character of the netgroup file * and turn on NIS if it's a '+'. rewind the stream * afterwards so we don't goof up read_for_group() later. */ if (netf) { fscanf(netf, "%c", &_yp_plus); rewind(netf); if (_yp_plus == '+') _use_only_yp = _netgr_yp_enabled = 1; } /* * If we were called specifically for an innetgr() * lookup and we're in NIS-only mode, short-circuit * parse_netgroup() and cut directly to the chase. */ if (_use_only_yp && _yp_innetgr) { /* dohw! */ if (netf != NULL) fclose(netf); return; } #else if ((netf = fopen(_PATH_NETGROUP, "re"))) { #endif if (parse_netgrp(group)) endnetgrent(); else { grouphead.grname = strdup(group); } if (netf) fclose(netf); } } nextgrp = grouphead.gr; } /* * Get the next netgroup off the list. */ int getnetgrent(char **hostp, char **userp, char **domp) { #ifdef YP _yp_innetgr = 0; #endif if (nextgrp) { *hostp = nextgrp->ng_str[NG_HOST]; *userp = nextgrp->ng_str[NG_USER]; *domp = nextgrp->ng_str[NG_DOM]; nextgrp = nextgrp->ng_next; return (1); } return (0); } /* * endnetgrent() - cleanup */ void endnetgrent(void) { struct linelist *lp, *olp; struct netgrp *gp, *ogp; lp = linehead; while (lp) { olp = lp; lp = lp->l_next; free(olp->l_groupname); free(olp->l_line); free(olp); } linehead = NULL; if (grouphead.grname) { free(grouphead.grname); grouphead.grname = NULL; } gp = grouphead.gr; while (gp) { ogp = gp; gp = gp->ng_next; free(ogp->ng_str[NG_HOST]); free(ogp->ng_str[NG_USER]); free(ogp->ng_str[NG_DOM]); free(ogp); } grouphead.gr = NULL; nextgrp = NULL; #ifdef YP _netgr_yp_enabled = 0; #endif } #ifdef YP static int _listmatch(const char *list, const char *group, int len) { const char *ptr = list; const char *cptr; int glen = strlen(group); /* skip possible leading whitespace */ while (isspace((unsigned char)*ptr)) ptr++; while (ptr < list + len) { cptr = ptr; while(*ptr != ',' && *ptr != '\0' && !isspace((unsigned char)*ptr)) ptr++; if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr)) return (1); while (*ptr == ',' || isspace((unsigned char)*ptr)) ptr++; } return (0); } static int _revnetgr_lookup(char* lookupdom, char* map, const char* str, const char* dom, const char* group) { int y, rv, rot; char key[MAXHOSTNAMELEN]; char *result; int resultlen; for (rot = 0; ; rot++) { switch (rot) { case 0: snprintf(key, MAXHOSTNAMELEN, "%s.%s", str, dom ? dom : lookupdom); break; case 1: snprintf(key, MAXHOSTNAMELEN, "%s.*", str); break; case 2: snprintf(key, MAXHOSTNAMELEN, "*.%s", dom ? dom : lookupdom); break; case 3: snprintf(key, MAXHOSTNAMELEN, "*.*"); break; default: return (0); } y = yp_match(lookupdom, map, key, strlen(key), &result, &resultlen); if (y == 0) { rv = _listmatch(result, group, resultlen); free(result); if (rv) return (1); } else if (y != YPERR_KEY) { /* * If we get an error other than 'no * such key in map' then something is * wrong and we should stop the search. */ return (-1); } } } #endif /* * Search for a match in a netgroup. */ int innetgr(const char *group, const char *host, const char *user, const char *dom) { char *hst, *usr, *dm; /* Sanity check */ if (group == NULL || !strlen(group)) return (0); #ifdef YP _yp_innetgr = 1; #endif setnetgrent(group); #ifdef YP _yp_innetgr = 0; /* * If we're in NIS-only mode, do the search using * NIS 'reverse netgroup' lookups. * * What happens with 'reverse netgroup' lookups: * * 1) try 'reverse netgroup' lookup * 1.a) if host is specified and user is null: * look in netgroup.byhost * (try host.domain, host.*, *.domain or *.*) * if found, return yes * 1.b) if user is specified and host is null: * look in netgroup.byuser * (try host.domain, host.*, *.domain or *.*) * if found, return yes * 1.c) if both host and user are specified, * don't do 'reverse netgroup' lookup. It won't work. * 1.d) if neither host ane user are specified (why?!?) * don't do 'reverse netgroup' lookup either. * 2) if domain is specified and 'reverse lookup' is done: * 'reverse lookup' was authoritative. bye bye. * 3) otherwise, too bad, try it the slow way. */ if (_use_only_yp && (host == NULL) != (user == NULL)) { int ret; if(yp_get_default_domain(&_netgr_yp_domain)) return (0); ret = _revnetgr_lookup(_netgr_yp_domain, host?"netgroup.byhost":"netgroup.byuser", host?host:user, dom, group); if (ret == 1) return (1); else if (ret == 0 && dom != NULL) return (0); } setnetgrent(group); #endif /* YP */ while (getnetgrent(&hst, &usr, &dm)) if ((host == NULL || hst == NULL || !strcmp(host, hst)) && (user == NULL || usr == NULL || !strcmp(user, usr)) && ( dom == NULL || dm == NULL || !strcmp(dom, dm))) { endnetgrent(); return (1); } endnetgrent(); return (0); } /* * Parse the netgroup file setting up the linked lists. */ static int parse_netgrp(const char *group) { struct netgrp *grp; struct linelist *lp = linehead; char **ng; char *epos, *gpos, *pos, *spos; int freepos, len, strpos; #ifdef DEBUG int fields; #endif /* * First, see if the line has already been read in. */ while (lp) { if (!strcmp(group, lp->l_groupname)) break; lp = lp->l_next; } if (lp == NULL && (lp = read_for_group(group)) == NULL) return (1); if (lp->l_parsed) { #ifdef DEBUG /* * This error message is largely superflous since the * code handles the error condition sucessfully, and * spewing it out from inside libc can actually hose * certain programs. */ fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); #endif return (1); } else lp->l_parsed = 1; pos = lp->l_line; /* Watch for null pointer dereferences, dammit! */ while (pos != NULL && *pos != '\0') { if (*pos == '(') { grp = malloc(sizeof(*grp)); if (grp == NULL) return (1); ng = grp->ng_str; bzero(grp, sizeof(*grp)); pos++; gpos = strsep(&pos, ")"); #ifdef DEBUG fields = 0; #endif for (strpos = 0; strpos < 3; strpos++) { if ((spos = strsep(&gpos, ",")) == NULL) { /* * All other systems I've tested * return NULL for empty netgroup * fields. It's up to user programs * to handle the NULLs appropriately. */ ng[strpos] = NULL; continue; } #ifdef DEBUG fields++; #endif while (*spos == ' ' || *spos == '\t') spos++; if ((epos = strpbrk(spos, " \t"))) { *epos = '\0'; len = epos - spos; } else len = strlen(spos); if (len <= 0) continue; ng[strpos] = malloc(len + 1); if (ng[strpos] == NULL) { for (freepos = 0; freepos < strpos; freepos++) free(ng[freepos]); free(grp); return (1); } bcopy(spos, ng[strpos], len + 1); } grp->ng_next = grouphead.gr; grouphead.gr = grp; #ifdef DEBUG /* * Note: on other platforms, malformed netgroup * entries are not normally flagged. While we * can catch bad entries and report them, we should * stay silent by default for compatibility's sake. */ if (fields < 3) { fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", ng[NG_HOST] == NULL ? "" : ng[NG_HOST], ng[NG_USER] == NULL ? "" : ",", ng[NG_USER] == NULL ? "" : ng[NG_USER], ng[NG_DOM] == NULL ? "" : ",", ng[NG_DOM] == NULL ? "" : ng[NG_DOM], lp->l_groupname); } #endif } else { spos = strsep(&pos, ", \t"); if (parse_netgrp(spos)) continue; } if (pos == NULL) break; while (*pos == ' ' || *pos == ',' || *pos == '\t') pos++; } return (0); } /* * Read the netgroup file and save lines until the line for the netgroup * is found. Return 1 if eof is encountered. */ static struct linelist * read_for_group(const char *group) { char *linep, *olinep, *pos, *spos; int len, olen; int cont; struct linelist *lp; char line[LINSIZ + 2]; #ifdef YP char *result; int resultlen; linep = NULL; while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) { if (_netgr_yp_enabled) { if(!_netgr_yp_domain) if(yp_get_default_domain(&_netgr_yp_domain)) continue; if (yp_match(_netgr_yp_domain, "netgroup", group, strlen(group), &result, &resultlen)) { free(result); if (_use_only_yp) return ((struct linelist *)0); else { _netgr_yp_enabled = 0; continue; } } snprintf(line, LINSIZ, "%s %s", group, result); free(result); } #else linep = NULL; while (fgets(line, LINSIZ, netf) != NULL) { #endif pos = (char *)&line; #ifdef YP if (*pos == '+') { _netgr_yp_enabled = 1; continue; } #endif if (*pos == '#') continue; while (*pos == ' ' || *pos == '\t') pos++; spos = pos; while (*pos != ' ' && *pos != '\t' && *pos != '\n' && *pos != '\0') pos++; len = pos - spos; while (*pos == ' ' || *pos == '\t') pos++; if (*pos != '\n' && *pos != '\0') { lp = (struct linelist *)malloc(sizeof (*lp)); if (lp == NULL) return (NULL); lp->l_parsed = 0; lp->l_groupname = (char *)malloc(len + 1); if (lp->l_groupname == NULL) { free(lp); return (NULL); } bcopy(spos, lp->l_groupname, len); *(lp->l_groupname + len) = '\0'; len = strlen(pos); olen = 0; /* * Loop around handling line continuations. */ do { if (*(pos + len - 1) == '\n') len--; if (*(pos + len - 1) == '\\') { len--; cont = 1; } else cont = 0; if (len > 0) { linep = malloc(olen + len + 1); if (linep == NULL) { free(lp->l_groupname); free(lp); return (NULL); } if (olen > 0) { bcopy(olinep, linep, olen); free(olinep); } bcopy(pos, linep + olen, len); olen += len; *(linep + olen) = '\0'; olinep = linep; } if (cont) { if (fgets(line, LINSIZ, netf)) { pos = line; len = strlen(pos); } else cont = 0; } } while (cont); lp->l_line = linep; lp->l_next = linehead; linehead = lp; /* * If this is the one we wanted, we are done. */ if (!strcmp(lp->l_groupname, group)) return (lp); } } #ifdef YP /* * Yucky. The recursive nature of this whole mess might require * us to make more than one pass through the netgroup file. * This might be best left outside the #ifdef YP, but YP is * defined by default anyway, so I'll leave it like this * until I know better. */ rewind(netf); #endif return (NULL); } Index: head/lib/libc/gen/setproctitle.c =================================================================== --- head/lib/libc/gen/setproctitle.c (revision 298225) +++ head/lib/libc/gen/setproctitle.c (revision 298226) @@ -1,175 +1,174 @@ /* * Copyright (c) 1995 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * 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. Absolutely no warranty of function or purpose is made by the author * Peter Wemm. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" -#include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" /* * Older FreeBSD 2.0, 2.1 and 2.2 had different ps_strings structures and * in different locations. * 1: old_ps_strings at the very top of the stack. * 2: old_ps_strings at SPARE_USRSPACE below the top of the stack. * 3: ps_strings at the very top of the stack. * We only support a kernel providing #3 style ps_strings. * * For historical purposes, a definition of the old ps_strings structure * and location is preserved below: struct old_ps_strings { char *old_ps_argvstr; int old_ps_nargvstr; char *old_ps_envstr; int old_ps_nenvstr; }; #define OLD_PS_STRINGS ((struct old_ps_strings *) \ (USRSTACK - SPARE_USRSPACE - sizeof(struct old_ps_strings))) */ #include #define SPT_BUFSIZE 2048 /* from other parts of sendmail */ void setproctitle(const char *fmt, ...) { static struct ps_strings *ps_strings; static char *buf = NULL; static char *obuf = NULL; static char **oargv, *kbuf; static int oargc = -1; static char *nargv[2] = { NULL, NULL }; char **nargvp; int nargc; int i; va_list ap; size_t len; unsigned long ul_ps_strings; int oid[4]; if (buf == NULL) { buf = malloc(SPT_BUFSIZE); if (buf == NULL) return; nargv[0] = buf; } if (obuf == NULL ) { obuf = malloc(SPT_BUFSIZE); if (obuf == NULL) return; *obuf = '\0'; } va_start(ap, fmt); if (fmt) { buf[SPT_BUFSIZE - 1] = '\0'; if (fmt[0] == '-') { /* skip program name prefix */ fmt++; len = 0; } else { /* print program name heading for grep */ (void)snprintf(buf, SPT_BUFSIZE, "%s: ", _getprogname()); len = strlen(buf); } /* print the argument string */ (void) vsnprintf(buf + len, SPT_BUFSIZE - len, fmt, ap); nargvp = nargv; nargc = 1; kbuf = buf; } else if (*obuf != '\0') { /* Idea from NetBSD - reset the title on fmt == NULL */ nargvp = oargv; nargc = oargc; kbuf = obuf; } else /* Nothing to restore */ return; va_end(ap); /* Set the title into the kernel cached command line */ oid[0] = CTL_KERN; oid[1] = KERN_PROC; oid[2] = KERN_PROC_ARGS; oid[3] = getpid(); sysctl(oid, 4, 0, 0, kbuf, strlen(kbuf) + 1); if (ps_strings == NULL) { len = sizeof(ul_ps_strings); if (sysctlbyname("kern.ps_strings", &ul_ps_strings, &len, NULL, 0) == -1) return; ps_strings = (struct ps_strings *)ul_ps_strings; } /* * PS_STRINGS points to zeroed memory on a style #2 kernel. * Should not happen. */ if (ps_strings->ps_argvstr == NULL) return; /* style #3 */ if (oargc == -1) { /* Record our original args */ oargc = ps_strings->ps_nargvstr; oargv = ps_strings->ps_argvstr; for (i = len = 0; i < oargc; i++) { /* * The program may have scribbled into its * argv array, e.g., to remove some arguments. * If that has happened, break out before * trying to call strlen on a NULL pointer. */ if (oargv[i] == NULL) { oargc = i; break; } snprintf(obuf + len, SPT_BUFSIZE - len, "%s%s", len != 0 ? " " : "", oargv[i]); if (len != 0) len++; len += strlen(oargv[i]); if (len >= SPT_BUFSIZE) break; } } ps_strings->ps_nargvstr = nargc; ps_strings->ps_argvstr = nargvp; } Index: head/lib/libc/inet/inet_addr.c =================================================================== --- head/lib/libc/inet/inet_addr.c (revision 298225) +++ head/lib/libc/inet/inet_addr.c (revision 298226) @@ -1,215 +1,214 @@ /* * Copyright (c) 1983, 1990, 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[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93"; static const char rcsid[] = "$Id: inet_addr.c,v 1.5 2005/04/27 04:56:19 sra Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "port_before.h" -#include #include #include #include #include #include "port_after.h" /*% * Ascii internet address interpretation routine. * The value returned is in network order. */ in_addr_t /* XXX should be struct in_addr :( */ inet_addr(const char *cp) { struct in_addr val; if (inet_aton(cp, &val)) return (val.s_addr); return (INADDR_NONE); } /*% * Check whether "cp" is a valid ascii representation * of an Internet address and convert to a binary address. * Returns 1 if the address is valid, 0 if not. * This replaces inet_addr, the return value from which * cannot distinguish between failure and a local broadcast address. */ int inet_aton(const char *cp, struct in_addr *addr) { u_long val; int base, n; char c; u_int8_t parts[4]; u_int8_t *pp = parts; int digit; c = *cp; for (;;) { /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, isdigit=decimal. */ if (!isdigit((unsigned char)c)) return (0); val = 0; base = 10; digit = 0; if (c == '0') { c = *++cp; if (c == 'x' || c == 'X') base = 16, c = *++cp; else { base = 8; digit = 1 ; } } for (;;) { if (isascii(c) && isdigit((unsigned char)c)) { if (base == 8 && (c == '8' || c == '9')) return (0); val = (val * base) + (c - '0'); c = *++cp; digit = 1; } else if (base == 16 && isascii(c) && isxdigit((unsigned char)c)) { val = (val << 4) | (c + 10 - (islower((unsigned char)c) ? 'a' : 'A')); c = *++cp; digit = 1; } else break; } if (c == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16 bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 3 || val > 0xffU) return (0); *pp++ = val; c = *++cp; } else break; } /* * Check for trailing characters. */ if (c != '\0' && (!isascii(c) || !isspace((unsigned char)c))) return (0); /* * Did we get a valid digit? */ if (!digit) return (0); /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; switch (n) { case 1: /*%< a -- 32 bits */ break; case 2: /*%< a.b -- 8.24 bits */ if (val > 0xffffffU) return (0); val |= parts[0] << 24; break; case 3: /*%< a.b.c -- 8.8.16 bits */ if (val > 0xffffU) return (0); val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /*%< a.b.c.d -- 8.8.8.8 bits */ if (val > 0xffU) return (0); val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } if (addr != NULL) addr->s_addr = htonl(val); return (1); } /* * Weak aliases for applications that use certain private entry points, * and fail to include . */ #undef inet_addr __weak_reference(__inet_addr, inet_addr); #undef inet_aton __weak_reference(__inet_aton, inet_aton); /*! \file */ Index: head/lib/libc/inet/inet_ntop.c =================================================================== --- head/lib/libc/inet/inet_ntop.c (revision 298225) +++ head/lib/libc/inet/inet_ntop.c (revision 298226) @@ -1,205 +1,204 @@ /* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * 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 rcsid[] = "$Id: inet_ntop.c,v 1.5 2005/11/03 22:59:52 marka Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "port_before.h" #include -#include #include #include #include #include #include #include #include #include "port_after.h" /*% * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static const char *inet_ntop4(const u_char *src, char *dst, socklen_t size); static const char *inet_ntop6(const u_char *src, char *dst, socklen_t size); /* const char * * inet_ntop(af, src, dst, size) * convert a network format address to presentation format. * return: * pointer to presentation format address (`dst'), or NULL (see errno). * author: * Paul Vixie, 1996. */ const char * inet_ntop(int af, const void * __restrict src, char * __restrict dst, socklen_t size) { switch (af) { case AF_INET: return (inet_ntop4(src, dst, size)); case AF_INET6: return (inet_ntop6(src, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); } /* NOTREACHED */ } /* const char * * inet_ntop4(src, dst, size) * format an IPv4 address * return: * `dst' (as a const) * notes: * (1) uses no statics * (2) takes a u_char* not an in_addr as input * author: * Paul Vixie, 1996. */ static const char * inet_ntop4(const u_char *src, char *dst, socklen_t size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[sizeof "255.255.255.255"]; int l; l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); if (l <= 0 || (socklen_t) l >= size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } /* const char * * inet_ntop6(src, dst, size) * convert IPv6 binary address into presentation (printable) format * author: * Paul Vixie, 1996. */ static const char * inet_ntop6(const u_char *src, char *dst, socklen_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; struct { int base, len; } best, cur; u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; int i; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < NS_IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = -1; best.len = 0; cur.base = -1; cur.len = 0; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) *tp++ = ':'; continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) *tp++ = ':'; /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 7 && words[7] != 0x0001) || (best.len == 5 && words[5] == 0xffff))) { if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) { errno = ENOSPC; return (NULL); } tp += strlen(tp); break; } tp += sprintf(tp, "%x", words[i]); } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) *tp++ = ':'; *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((socklen_t)(tp - tmp) > size) { errno = ENOSPC; return (NULL); } strcpy(dst, tmp); return (dst); } /* * Weak aliases for applications that use certain private entry points, * and fail to include . */ #undef inet_ntop __weak_reference(__inet_ntop, inet_ntop); /*! \file */ Index: head/lib/libc/inet/inet_pton.c =================================================================== --- head/lib/libc/inet/inet_pton.c (revision 298225) +++ head/lib/libc/inet/inet_pton.c (revision 298226) @@ -1,225 +1,224 @@ /* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * 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 rcsid[] = "$Id: inet_pton.c,v 1.5 2005/07/28 06:51:47 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" /*% * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, u_char *dst); static int inet_pton6(const char *src, u_char *dst); /* int * inet_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family * 0 if the address wasn't valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * author: * Paul Vixie, 1996. */ int inet_pton(int af, const char * __restrict src, void * __restrict dst) { switch (af) { case AF_INET: return (inet_pton4(src, dst)); case AF_INET6: return (inet_pton6(src, dst)); default: errno = EAFNOSUPPORT; return (-1); } /* NOTREACHED */ } /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(const char *src, u_char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; u_char tmp[NS_INADDRSZ], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { u_int new = *tp * 10 + (pch - digits); if (saw_digit && *tp == 0) return (0); if (new > 255) return (0); *tp = new; if (!saw_digit) { if (++octets > 4) return (0); saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return (0); *++tp = 0; saw_digit = 0; } else return (0); } if (octets < 4) return (0); memcpy(dst, tmp, NS_INADDRSZ); return (1); } /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * (1) does not touch `dst' unless it's returning 1. * (2) :: in a full address is silently ignored. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(const char *src, u_char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, seen_xdigits; u_int val; memset((tp = tmp), '\0', NS_IN6ADDRSZ); endp = tp + NS_IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return (0); curtok = src; seen_xdigits = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (++seen_xdigits > 4) return (0); continue; } if (ch == ':') { curtok = src; if (!seen_xdigits) { if (colonp) return (0); colonp = tp; continue; } else if (*src == '\0') { return (0); } if (tp + NS_INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; seen_xdigits = 0; val = 0; continue; } if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { tp += NS_INADDRSZ; seen_xdigits = 0; break; /*%< '\\0' was seen by inet_pton4(). */ } return (0); } if (seen_xdigits) { if (tp + NS_INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) return (0); for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return (0); memcpy(dst, tmp, NS_IN6ADDRSZ); return (1); } /* * Weak aliases for applications that use certain private entry points, * and fail to include . */ #undef inet_pton __weak_reference(__inet_pton, inet_pton); /*! \file */ Index: head/lib/libc/inet/nsap_addr.c =================================================================== --- head/lib/libc/inet/nsap_addr.c (revision 298225) +++ head/lib/libc/inet/nsap_addr.c (revision 298226) @@ -1,122 +1,121 @@ /* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * 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 rcsid[] = "$Id: nsap_addr.c,v 1.5 2005/07/28 06:51:48 marka Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "port_before.h" -#include #include #include #include #include #include #include #include #include #include "port_after.h" static char xtob(int c) { return (c - (((c >= '0') && (c <= '9')) ? '0' : '7')); } u_int inet_nsap_addr(const char *ascii, u_char *binary, int maxlen) { u_char c, nib; u_int len = 0; if (ascii[0] != '0' || (ascii[1] != 'x' && ascii[1] != 'X')) return (0); ascii += 2; while ((c = *ascii++) != '\0' && len < (u_int)maxlen) { if (c == '.' || c == '+' || c == '/') continue; if (!isascii(c)) return (0); if (islower(c)) c = toupper(c); if (isxdigit(c)) { nib = xtob(c); c = *ascii++; if (c != '\0') { c = toupper(c); if (isxdigit(c)) { *binary++ = (nib << 4) | xtob(c); len++; } else return (0); } else return (0); } else return (0); } return (len); } char * inet_nsap_ntoa(int binlen, const u_char *binary, char *ascii) { int nib; int i; char *tmpbuf = inet_nsap_ntoa_tmpbuf; char *start; if (ascii) start = ascii; else { ascii = tmpbuf; start = tmpbuf; } *ascii++ = '0'; *ascii++ = 'x'; if (binlen > 255) binlen = 255; for (i = 0; i < binlen; i++) { nib = *binary >> 4; *ascii++ = nib + (nib < 10 ? '0' : '7'); nib = *binary++ & 0x0f; *ascii++ = nib + (nib < 10 ? '0' : '7'); if (((i % 2) == 0 && (i + 1) < binlen)) *ascii++ = '.'; } *ascii = '\0'; return (start); } /* * Weak aliases for applications that use certain private entry points, * and fail to include . */ #undef inet_nsap_addr __weak_reference(__inet_nsap_addr, inet_nsap_addr); #undef inet_nsap_ntoa __weak_reference(__inet_nsap_ntoa, inet_nsap_ntoa); /*! \file */ Index: head/lib/libc/isc/eventlib_p.h =================================================================== --- head/lib/libc/isc/eventlib_p.h (revision 298225) +++ head/lib/libc/isc/eventlib_p.h (revision 298226) @@ -1,290 +1,289 @@ /* * Copyright (c) 2005 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. */ /*! \file * \brief private interfaces for eventlib * \author vix 09sep95 [initial] * * $Id: eventlib_p.h,v 1.9 2006/03/09 23:57:56 marka Exp $ * $FreeBSD$ */ #ifndef _EVENTLIB_P_H #define _EVENTLIB_P_H #include -#include #include #include #include #define EVENTLIB_DEBUG 1 #include #include #include #include #include #ifndef _LIBC #include #include #include #endif #define EV_MASK_ALL (EV_READ | EV_WRITE | EV_EXCEPT) #define EV_ERR(e) return (errno = (e), -1) #define OK(x) if ((x) < 0) EV_ERR(errno); else (void)NULL #define OKFREE(x, y) if ((x) < 0) { FREE((y)); EV_ERR(errno); } \ else (void)NULL #define NEW(p) if (((p) = memget(sizeof *(p))) != NULL) \ FILL(p); \ else \ (void)NULL; #define OKNEW(p) if (!((p) = memget(sizeof *(p)))) { \ errno = ENOMEM; \ return (-1); \ } else \ FILL(p) #define FREE(p) memput((p), sizeof *(p)) #if EVENTLIB_DEBUG #define FILL(p) memset((p), 0xF5, sizeof *(p)) #else #define FILL(p) #endif #ifdef USE_POLL #ifdef HAVE_STROPTS_H #include #endif #include #endif /* USE_POLL */ typedef struct evConn { evConnFunc func; void * uap; int fd; int flags; #define EV_CONN_LISTEN 0x0001 /*%< Connection is a listener. */ #define EV_CONN_SELECTED 0x0002 /*%< evSelectFD(conn->file). */ #define EV_CONN_BLOCK 0x0004 /*%< Listener fd was blocking. */ evFileID file; struct evConn * prev; struct evConn * next; } evConn; #ifndef _LIBC typedef struct evAccept { int fd; union { struct sockaddr sa; struct sockaddr_in in; #ifndef NO_SOCKADDR_UN struct sockaddr_un un; #endif } la; ISC_SOCKLEN_T lalen; union { struct sockaddr sa; struct sockaddr_in in; #ifndef NO_SOCKADDR_UN struct sockaddr_un un; #endif } ra; ISC_SOCKLEN_T ralen; int ioErrno; evConn * conn; LINK(struct evAccept) link; } evAccept; typedef struct evFile { evFileFunc func; void * uap; int fd; int eventmask; int preemptive; struct evFile * prev; struct evFile * next; struct evFile * fdprev; struct evFile * fdnext; } evFile; typedef struct evStream { evStreamFunc func; void * uap; evFileID file; evTimerID timer; int flags; #define EV_STR_TIMEROK 0x0001 /*%< IFF timer valid. */ int fd; struct iovec * iovOrig; int iovOrigCount; struct iovec * iovCur; int iovCurCount; int ioTotal; int ioDone; int ioErrno; struct evStream *prevDone, *nextDone; struct evStream *prev, *next; } evStream; typedef struct evTimer { evTimerFunc func; void * uap; struct timespec due, inter; int index; int mode; #define EV_TMR_RATE 1 } evTimer; typedef struct evWait { evWaitFunc func; void * uap; const void * tag; struct evWait * next; } evWait; typedef struct evWaitList { evWait * first; evWait * last; struct evWaitList * prev; struct evWaitList * next; } evWaitList; typedef struct evEvent_p { enum { Accept, File, Stream, Timer, Wait, Free, Null } type; union { struct { evAccept *this; } accept; struct { evFile *this; int eventmask; } file; struct { evStream *this; } stream; struct { evTimer *this; } timer; struct { evWait *this; } wait; struct { struct evEvent_p *next; } free; struct { const void *placeholder; } null; } u; } evEvent_p; #endif #ifdef USE_POLL typedef struct { void *ctx; /* pointer to the evContext_p */ uint32_t type; /* READ, WRITE, EXCEPT, nonblk */ uint32_t result; /* 1 => revents, 0 => events */ } __evEmulMask; #define emulMaskInit(ctx, field, ev, lastnext) \ ctx->field.ctx = ctx; \ ctx->field.type = ev; \ ctx->field.result = lastnext; extern short *__fd_eventfield(int fd, __evEmulMask *maskp); extern short __poll_event(__evEmulMask *maskp); extern void __fd_clr(int fd, __evEmulMask *maskp); extern void __fd_set(int fd, __evEmulMask *maskp); #undef FD_ZERO #define FD_ZERO(maskp) #undef FD_SET #define FD_SET(fd, maskp) \ __fd_set(fd, maskp) #undef FD_CLR #define FD_CLR(fd, maskp) \ __fd_clr(fd, maskp) #undef FD_ISSET #define FD_ISSET(fd, maskp) \ ((*__fd_eventfield(fd, maskp) & __poll_event(maskp)) != 0) #endif /* USE_POLL */ #ifndef _LIBC typedef struct { /* Global. */ const evEvent_p *cur; /* Debugging. */ int debug; FILE *output; /* Connections. */ evConn *conns; LIST(evAccept) accepts; /* Files. */ evFile *files, *fdNext; #ifndef USE_POLL fd_set rdLast, rdNext; fd_set wrLast, wrNext; fd_set exLast, exNext; fd_set nonblockBefore; int fdMax, fdCount, highestFD; evFile *fdTable[FD_SETSIZE]; #else struct pollfd *pollfds; /* Allocated as needed */ evFile **fdTable; /* Ditto */ int maxnfds; /* # elements in above */ int firstfd; /* First active fd */ int fdMax; /* Last active fd */ int fdCount; /* # fd:s with I/O */ int highestFD; /* max fd allowed by OS */ __evEmulMask rdLast, rdNext; __evEmulMask wrLast, wrNext; __evEmulMask exLast, exNext; __evEmulMask nonblockBefore; #endif /* USE_POLL */ #ifdef EVENTLIB_TIME_CHECKS struct timespec lastSelectTime; int lastFdCount; #endif /* Streams. */ evStream *streams; evStream *strDone, *strLast; /* Timers. */ struct timespec lastEventTime; heap_context timers; /* Waits. */ evWaitList *waitLists; evWaitList waitDone; } evContext_p; /* eventlib.c */ #define evPrintf __evPrintf void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4); #ifdef USE_POLL extern int evPollfdRealloc(evContext_p *ctx, int pollfd_chunk_size, int fd); #endif /* USE_POLL */ /* ev_timers.c */ #define evCreateTimers __evCreateTimers heap_context evCreateTimers(const evContext_p *); #define evDestroyTimers __evDestroyTimers void evDestroyTimers(const evContext_p *); /* ev_waits.c */ #define evFreeWait __evFreeWait evWait *evFreeWait(evContext_p *ctx, evWait *old); #endif /* Global options */ #ifndef _LIBC extern int __evOptMonoTime; #endif #endif /*_EVENTLIB_P_H*/ Index: head/lib/libc/net/base64.c =================================================================== --- head/lib/libc/net/base64.c (revision 298225) +++ head/lib/libc/net/base64.c (revision 298226) @@ -1,320 +1,319 @@ /* * Copyright (c) 1996, 1998 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); -#include #include #include #include #include #include #include #include #include #include #include #define Assert(Cond) if (!(Cond)) abort() static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the ------------------------------------------------- following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; u_char input[3]; u_char output[4]; size_t i; while (2 < srclength) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); Assert(output[3] < 64); if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; target[datalength++] = Base64[output[2]]; target[datalength++] = Base64[output[3]]; } /* Now we worry about padding. */ if (0 != srclength) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (i = 0; i < srclength; i++) input[i] = *src++; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; if (srclength == 1) target[datalength++] = Pad64; else target[datalength++] = Base64[output[2]]; target[datalength++] = Pad64; } if (datalength >= targsize) return (-1); target[datalength] = '\0'; /* Returned value doesn't count \0. */ return (datalength); } /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. it returns the number of data bytes stored at the target, or -1 on error. */ int b64_pton(const char *src, u_char *target, size_t targsize) { int tarindex, state, ch; u_char nextbyte; char *pos; state = 0; tarindex = 0; while ((ch = *src++) != '\0') { if (isspace((unsigned char)ch)) /* Skip whitespace anywhere. */ continue; if (ch == Pad64) break; pos = strchr(Base64, ch); if (pos == NULL) /* A non-base64 character. */ return (-1); switch (state) { case 0: if (target) { if ((size_t)tarindex >= targsize) return (-1); target[tarindex] = (pos - Base64) << 2; } state = 1; break; case 1: if (target) { if ((size_t)tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 4; nextbyte = ((pos - Base64) & 0x0f) << 4; if ((size_t)tarindex + 1 < targsize) target[tarindex + 1] = nextbyte; else if (nextbyte) return (-1); } tarindex++; state = 2; break; case 2: if (target) { if ((size_t)tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 2; nextbyte = ((pos - Base64) & 0x03) << 6; if ((size_t)tarindex + 1 < targsize) target[tarindex + 1] = nextbyte; else if (nextbyte) return (-1); } tarindex++; state = 3; break; case 3: if (target) { if ((size_t)tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64); } tarindex++; state = 0; break; default: abort(); } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = *src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return (-1); case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for ((void)NULL; ch != '\0'; ch = *src++) if (!isspace((unsigned char)ch)) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); ch = *src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for ((void)NULL; ch != '\0'; ch = *src++) if (!isspace((unsigned char)ch)) return (-1); /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ if (target && (size_t)tarindex < targsize && target[tarindex] != 0) return (-1); } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return (-1); } return (tarindex); } Index: head/lib/libc/net/ether_addr.c =================================================================== --- head/lib/libc/net/ether_addr.c (revision 298225) +++ head/lib/libc/net/ether_addr.c (revision 298226) @@ -1,232 +1,231 @@ /* * Copyright (c) 1995 Bill Paul . * Copyright (c) 2007 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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. * * ethernet address conversion and lookup routines * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ #include __FBSDID("$FreeBSD$"); -#include #include #include #include #ifdef YP #include #include #include #endif #include #include #include #include #ifndef _PATH_ETHERS #define _PATH_ETHERS "/etc/ethers" #endif /* * Parse a string of text containing an ethernet address and hostname and * separate it into its component parts. */ int ether_line(const char *l, struct ether_addr *e, char *hostname) { int i, o[6]; i = sscanf(l, "%x:%x:%x:%x:%x:%x %s", &o[0], &o[1], &o[2], &o[3], &o[4], &o[5], hostname); if (i == 7) { for (i = 0; i < 6; i++) e->octet[i] = o[i]; return (0); } else { return (-1); } } /* * Convert an ASCII representation of an ethernet address to binary form. */ struct ether_addr * ether_aton_r(const char *a, struct ether_addr *e) { int i; unsigned int o0, o1, o2, o3, o4, o5; i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5); if (i != 6) return (NULL); e->octet[0]=o0; e->octet[1]=o1; e->octet[2]=o2; e->octet[3]=o3; e->octet[4]=o4; e->octet[5]=o5; return (e); } struct ether_addr * ether_aton(const char *a) { static struct ether_addr e; return (ether_aton_r(a, &e)); } /* * Convert a binary representation of an ethernet address to an ASCII string. */ char * ether_ntoa_r(const struct ether_addr *n, char *a) { int i; i = sprintf(a, "%02x:%02x:%02x:%02x:%02x:%02x", n->octet[0], n->octet[1], n->octet[2], n->octet[3], n->octet[4], n->octet[5]); if (i < 17) return (NULL); return (a); } char * ether_ntoa(const struct ether_addr *n) { static char a[18]; return (ether_ntoa_r(n, a)); } /* * Map an ethernet address to a hostname. Use either /etc/ethers or NIS/YP. */ int ether_ntohost(char *hostname, const struct ether_addr *e) { FILE *fp; char buf[BUFSIZ + 2]; struct ether_addr local_ether; char local_host[MAXHOSTNAMELEN]; #ifdef YP char *result; int resultlen; char *ether_a; char *yp_domain; #endif if ((fp = fopen(_PATH_ETHERS, "re")) == NULL) return (1); while (fgets(buf,BUFSIZ,fp)) { if (buf[0] == '#') continue; #ifdef YP if (buf[0] == '+') { if (yp_get_default_domain(&yp_domain)) continue; ether_a = ether_ntoa(e); if (yp_match(yp_domain, "ethers.byaddr", ether_a, strlen(ether_a), &result, &resultlen)) { continue; } strncpy(buf, result, resultlen); buf[resultlen] = '\0'; free(result); } #endif if (!ether_line(buf, &local_ether, local_host)) { if (!bcmp((char *)&local_ether.octet[0], (char *)&e->octet[0], 6)) { /* We have a match. */ strcpy(hostname, local_host); fclose(fp); return(0); } } } fclose(fp); return (1); } /* * Map a hostname to an ethernet address using /etc/ethers or NIS/YP. */ int ether_hostton(const char *hostname, struct ether_addr *e) { FILE *fp; char buf[BUFSIZ + 2]; struct ether_addr local_ether; char local_host[MAXHOSTNAMELEN]; #ifdef YP char *result; int resultlen; char *yp_domain; #endif if ((fp = fopen(_PATH_ETHERS, "re")) == NULL) return (1); while (fgets(buf,BUFSIZ,fp)) { if (buf[0] == '#') continue; #ifdef YP if (buf[0] == '+') { if (yp_get_default_domain(&yp_domain)) continue; if (yp_match(yp_domain, "ethers.byname", hostname, strlen(hostname), &result, &resultlen)) { continue; } strncpy(buf, result, resultlen); buf[resultlen] = '\0'; free(result); } #endif if (!ether_line(buf, &local_ether, local_host)) { if (!strcmp(hostname, local_host)) { /* We have a match. */ bcopy((char *)&local_ether.octet[0], (char *)&e->octet[0], 6); fclose(fp); return(0); } } } fclose(fp); return (1); } Index: head/lib/libc/net/eui64.c =================================================================== --- head/lib/libc/net/eui64.c (revision 298225) +++ head/lib/libc/net/eui64.c (revision 298226) @@ -1,312 +1,311 @@ /* * Copyright 2004 The Aerospace Corporation. 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. The name of The Aerospace Corporation may not be used to endorse or * promote products derived from this software. * * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION "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 AEROSPACE CORPORATION 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 (c) 1995 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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. * * EUI-64 conversion and lookup routines * * * Converted from ether_addr.c rev * FreeBSD: src/lib/libc/net/eui64.c,v 1.15 2002/04/08 07:51:10 ru Exp * by Brooks Davis * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ #include __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include #include -#include #ifdef YP #include #include #include #endif #ifndef _PATH_EUI64 #define _PATH_EUI64 "/etc/eui64" #endif static int eui64_line(const char *l, struct eui64 *e, char *hostname, size_t len); /* * Parse a string of text containing an EUI-64 and hostname * and separate it into its component parts. */ static int eui64_line(const char *l, struct eui64 *e, char *hostname, size_t len) { char *line, *linehead, *cur; linehead = strdup(l); if (linehead == NULL) return (-1); line = linehead; /* Find and parse the EUI64 */ while ((cur = strsep(&line, " \t\r\n")) != NULL) { if (*cur != '\0') { if (eui64_aton(cur, e) == 0) break; else goto bad; } } /* Find the hostname */ while ((cur = strsep(&line, " \t\r\n")) != NULL) { if (*cur != '\0') { if (strlcpy(hostname, cur, len) <= len) break; else goto bad; } } /* Make sure what remains is either whitespace or a comment */ while ((cur = strsep(&line, " \t\r\n")) != NULL) { if (*cur == '#') break; if (*cur != '\0') goto bad; } free(linehead); return (0); bad: free(linehead); return (-1); } /* * Convert an ASCII representation of an EUI-64 to binary form. */ int eui64_aton(const char *a, struct eui64 *e) { int i; unsigned int o0, o1, o2, o3, o4, o5, o6, o7; /* canonical form */ i = sscanf(a, "%x-%x-%x-%x-%x-%x-%x-%x", &o0, &o1, &o2, &o3, &o4, &o5, &o6, &o7); if (i == EUI64_LEN) goto good; /* ethernet form */ i = sscanf(a, "%x:%x:%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5, &o6, &o7); if (i == EUI64_LEN) goto good; /* classic fwcontrol/dconschat form */ i = sscanf(a, "0x%2x%2x%2x%2x%2x%2x%2x%2x", &o0, &o1, &o2, &o3, &o4, &o5, &o6, &o7); if (i == EUI64_LEN) goto good; /* MAC format (-) */ i = sscanf(a, "%x-%x-%x-%x-%x-%x", &o0, &o1, &o2, &o5, &o6, &o7); if (i == 6) { o3 = 0xff; o4 = 0xfe; goto good; } /* MAC format (:) */ i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o5, &o6, &o7); if (i == 6) { o3 = 0xff; o4 = 0xfe; goto good; } return (-1); good: e->octet[0]=o0; e->octet[1]=o1; e->octet[2]=o2; e->octet[3]=o3; e->octet[4]=o4; e->octet[5]=o5; e->octet[6]=o6; e->octet[7]=o7; return (0); } /* * Convert a binary representation of an EUI-64 to an ASCII string. */ int eui64_ntoa(const struct eui64 *id, char *a, size_t len) { int i; i = snprintf(a, len, "%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x", id->octet[0], id->octet[1], id->octet[2], id->octet[3], id->octet[4], id->octet[5], id->octet[6], id->octet[7]); if (i < 23 || i >= len) return (-1); return (0); } /* * Map an EUI-64 to a hostname. Use either /etc/eui64 or NIS/YP. */ int eui64_ntohost(char *hostname, size_t len, const struct eui64 *id) { FILE *fp; char buf[BUFSIZ + 2]; struct eui64 local_eui64; char local_host[MAXHOSTNAMELEN]; #ifdef YP char *result; int resultlen; char eui64_a[24]; char *yp_domain; #endif if ((fp = fopen(_PATH_EUI64, "re")) == NULL) return (1); while (fgets(buf,BUFSIZ,fp)) { if (buf[0] == '#') continue; #ifdef YP if (buf[0] == '+') { if (yp_get_default_domain(&yp_domain)) continue; eui64_ntoa(id, eui64_a, sizeof(eui64_a)); if (yp_match(yp_domain, "eui64.byid", eui64_a, strlen(eui64_a), &result, &resultlen)) { continue; } strncpy(buf, result, resultlen); buf[resultlen] = '\0'; free(result); } #endif if (eui64_line(buf, &local_eui64, local_host, sizeof(local_host)) == 0) { if (bcmp(&local_eui64.octet[0], &id->octet[0], EUI64_LEN) == 0) { /* We have a match */ strcpy(hostname, local_host); fclose(fp); return(0); } } } fclose(fp); return (1); } /* * Map a hostname to an EUI-64 using /etc/eui64 or NIS/YP. */ int eui64_hostton(const char *hostname, struct eui64 *id) { FILE *fp; char buf[BUFSIZ + 2]; struct eui64 local_eui64; char local_host[MAXHOSTNAMELEN]; #ifdef YP char *result; int resultlen; char *yp_domain; #endif if ((fp = fopen(_PATH_EUI64, "re")) == NULL) return (1); while (fgets(buf,BUFSIZ,fp)) { if (buf[0] == '#') continue; #ifdef YP if (buf[0] == '+') { if (yp_get_default_domain(&yp_domain)) continue; if (yp_match(yp_domain, "eui64.byname", hostname, strlen(hostname), &result, &resultlen)) { continue; } strncpy(buf, result, resultlen); buf[resultlen] = '\0'; free(result); } #endif if (eui64_line(buf, &local_eui64, local_host, sizeof(local_host)) == 0) { if (strcmp(hostname, local_host) == 0) { /* We have a match */ bcopy(&local_eui64, id, sizeof(struct eui64)); fclose(fp); return(0); } } } fclose(fp); return (1); } Index: head/lib/libc/net/getaddrinfo.c =================================================================== --- head/lib/libc/net/getaddrinfo.c (revision 298225) +++ head/lib/libc/net/getaddrinfo.c (revision 298226) @@ -1,3017 +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 #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. */ 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/gethostbydns.c =================================================================== --- head/lib/libc/net/gethostbydns.c (revision 298225) +++ head/lib/libc/net/gethostbydns.c (revision 298226) @@ -1,776 +1,775 @@ /* * ++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. * 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-- */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)gethostnamadr.c 8.1 (Berkeley) 6/4/93"; static char fromrcsid[] = "From: Id: gethnamaddr.c,v 8.23 1998/04/07 04:59:46 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netdb_private.h" #include "res_config.h" #define SPRINTF(x) ((size_t)sprintf x) static const char AskedForGot[] = "gethostby*.gethostanswer: asked for \"%s\", got \"%s\""; #ifdef RESOLVSORT static void addrsort(char **, int, res_state); #endif #ifdef DEBUG static void dprintf(char *, int, res_state) __printflike(1, 0); #endif #define MAXPACKET (64*1024) typedef union { HEADER hdr; u_char buf[MAXPACKET]; } querybuf; typedef union { int32_t al; char ac; } align; int _dns_ttl_; #ifdef DEBUG static void dprintf(char *msg, int num, res_state res) { if (res->options & RES_DEBUG) { int save = errno; printf(msg, num); errno = save; } } #else # define dprintf(msg, num, res) /*nada*/ #endif #define BOUNDED_INCR(x) \ do { \ cp += x; \ if (cp > eom) { \ RES_SET_H_ERRNO(statp, NO_RECOVERY); \ return (-1); \ } \ } while (0) #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ RES_SET_H_ERRNO(statp, NO_RECOVERY); \ return (-1); \ } \ } while (0) static int gethostanswer(const querybuf *answer, int anslen, const char *qname, int qtype, struct hostent *he, struct hostent_data *hed, res_state statp) { const HEADER *hp; const u_char *cp; int n; const u_char *eom, *erdata; char *bp, *ep, **ap, **hap; int type, class, ancount, qdcount; int haveanswer, had_error; int toobig = 0; char tbuf[MAXDNAME]; const char *tname; int (*name_ok)(const char *); tname = qname; he->h_name = NULL; eom = answer->buf + anslen; switch (qtype) { case T_A: case T_AAAA: name_ok = res_hnok; break; case T_PTR: name_ok = res_dnok; break; default: RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); /* XXX should be abort(); */ } /* * find first satisfactory answer */ hp = &answer->hdr; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); bp = hed->hostbuf; ep = hed->hostbuf + sizeof hed->hostbuf; cp = answer->buf; BOUNDED_INCR(HFIXEDSZ); if (qdcount != 1) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } n = dn_expand(answer->buf, eom, cp, bp, ep - bp); if ((n < 0) || !(*name_ok)(bp)) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } BOUNDED_INCR(n + QFIXEDSZ); if (qtype == T_A || qtype == T_AAAA) { /* 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(statp, NO_RECOVERY); return (-1); } he->h_name = bp; bp += n; /* The qname can be abbreviated, but h_name is now absolute. */ qname = he->h_name; } ap = hed->host_aliases; *ap = NULL; he->h_aliases = hed->host_aliases; hap = hed->h_addr_ptrs; *hap = NULL; he->h_addr_list = hed->h_addr_ptrs; haveanswer = 0; had_error = 0; _dns_ttl_ = -1; 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 */ BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); type = _getshort(cp); cp += INT16SZ; /* type */ class = _getshort(cp); cp += INT16SZ; /* class */ if (qtype == T_A && type == T_A) _dns_ttl_ = _getlong(cp); cp += INT32SZ; /* TTL */ n = _getshort(cp); cp += INT16SZ; /* len */ BOUNDS_CHECK(cp, n); erdata = cp + n; if (class != C_IN) { /* XXX - debug? syslog? */ cp += n; continue; /* XXX - had_error++ ? */ } if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { if (ap >= &hed->host_aliases[_MAXALIASES-1]) continue; n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); if ((n < 0) || !(*name_ok)(tbuf)) { had_error++; continue; } cp += n; if (cp != erdata) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } /* Store alias. */ *ap++ = bp; n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { had_error++; continue; } bp += n; /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > ep - bp || n >= MAXHOSTNAMELEN) { had_error++; continue; } strcpy(bp, tbuf); he->h_name = bp; bp += n; continue; } if (qtype == T_PTR && type == T_CNAME) { n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); if (n < 0 || !res_dnok(tbuf)) { had_error++; continue; } cp += n; if (cp != erdata) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > ep - bp || n >= MAXHOSTNAMELEN) { had_error++; continue; } strcpy(bp, tbuf); tname = bp; bp += n; continue; } if (type != qtype) { if (type != T_SIG && type != ns_t_dname) syslog(LOG_NOTICE|LOG_AUTH, "gethostby*.gethostanswer: asked for \"%s %s %s\", got type \"%s\"", qname, p_class(C_IN), p_type(qtype), p_type(type)); cp += n; continue; /* XXX - had_error++ ? */ } switch (type) { case T_PTR: if (strcasecmp(tname, bp) != 0) { syslog(LOG_NOTICE|LOG_AUTH, AskedForGot, qname, bp); cp += n; continue; /* XXX - had_error++ ? */ } n = dn_expand(answer->buf, eom, cp, bp, ep - bp); if ((n < 0) || !res_hnok(bp)) { had_error++; break; } #if MULTI_PTRS_ARE_ALIASES cp += n; if (cp != erdata) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } if (!haveanswer) he->h_name = bp; else if (ap < &hed->host_aliases[_MAXALIASES-1]) *ap++ = bp; else n = -1; if (n != -1) { n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { had_error++; break; } bp += n; } break; #else he->h_name = bp; if (statp->options & RES_USE_INET6) { n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { had_error++; break; } bp += n; _map_v4v6_hostent(he, &bp, ep); } RES_SET_H_ERRNO(statp, NETDB_SUCCESS); return (0); #endif case T_A: case T_AAAA: if (strcasecmp(he->h_name, bp) != 0) { syslog(LOG_NOTICE|LOG_AUTH, AskedForGot, he->h_name, bp); cp += n; continue; /* XXX - had_error++ ? */ } if (n != he->h_length) { cp += n; continue; } if (!haveanswer) { int nn; he->h_name = bp; nn = strlen(bp) + 1; /* for the \0 */ bp += nn; } bp += sizeof(align) - ((u_long)bp % sizeof(align)); if (bp + n >= ep) { dprintf("size (%d) too big\n", n, statp); had_error++; continue; } if (hap >= &hed->h_addr_ptrs[_MAXADDRS-1]) { if (!toobig++) dprintf("Too many addresses (%d)\n", _MAXADDRS, statp); cp += n; continue; } memcpy(*hap++ = bp, cp, n); bp += n; cp += n; if (cp != erdata) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } break; default: dprintf("Impossible condition (type=%d)\n", type, statp); RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); /* BIND has abort() here, too risky on bad data */ } if (!had_error) haveanswer++; } if (haveanswer) { *ap = NULL; *hap = NULL; # if defined(RESOLVSORT) /* * Note: we sort even if host can take only one address * in its return structures - should give it the "best" * address in that case, not some random one */ if (statp->nsort && haveanswer > 1 && qtype == T_A) addrsort(hed->h_addr_ptrs, haveanswer, statp); # endif /*RESOLVSORT*/ if (!he->h_name) { n = strlen(qname) + 1; /* for the \0 */ if (n > ep - bp || n >= MAXHOSTNAMELEN) goto no_recovery; strcpy(bp, qname); he->h_name = bp; bp += n; } if (statp->options & RES_USE_INET6) _map_v4v6_hostent(he, &bp, ep); RES_SET_H_ERRNO(statp, NETDB_SUCCESS); return (0); } no_recovery: RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } /* XXX: for async DNS resolver in ypserv */ struct hostent * __dns_getanswer(const char *answer, int anslen, const char *qname, int qtype) { struct hostent *he; struct hostent_data *hed; int error; res_state statp; statp = __res_state(); if ((he = __hostent_init()) == NULL || (hed = __hostent_data_init()) == NULL) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (NULL); } switch (qtype) { case T_AAAA: he->h_addrtype = AF_INET6; he->h_length = NS_IN6ADDRSZ; break; case T_A: default: he->h_addrtype = AF_INET; he->h_length = NS_INADDRSZ; break; } error = gethostanswer((const querybuf *)answer, anslen, qname, qtype, he, hed, statp); return (error == 0) ? he : NULL; } int _dns_gethostbyname(void *rval, void *cb_data, va_list ap) { const char *name; int af; char *buffer; size_t buflen; int *errnop, *h_errnop; struct hostent *hptr, he; struct hostent_data *hed; querybuf *buf; int n, type, error; res_state statp; name = va_arg(ap, const char *); af = va_arg(ap, int); hptr = va_arg(ap, struct hostent *); buffer = va_arg(ap, char *); buflen = va_arg(ap, size_t); errnop = va_arg(ap, int *); h_errnop = va_arg(ap, int *); *((struct hostent **)rval) = NULL; statp = __res_state(); if ((hed = __hostent_data_init()) == NULL) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (NS_NOTFOUND); } he.h_addrtype = af; switch (af) { case AF_INET: he.h_length = NS_INADDRSZ; type = T_A; break; case AF_INET6: he.h_length = NS_IN6ADDRSZ; type = T_AAAA; break; default: RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; errno = EAFNOSUPPORT; return (NS_UNAVAIL); } if ((buf = malloc(sizeof(*buf))) == NULL) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (NS_NOTFOUND); } n = res_nsearch(statp, name, C_IN, type, buf->buf, sizeof(buf->buf)); if (n < 0) { free(buf); dprintf("res_nsearch failed (%d)\n", n, statp); *h_errnop = statp->res_h_errno; return (NS_NOTFOUND); } else if (n > sizeof(buf->buf)) { free(buf); dprintf("static buffer is too small (%d)\n", n, statp); *h_errnop = statp->res_h_errno; return (NS_UNAVAIL); } error = gethostanswer(buf, n, name, type, &he, hed, statp); free(buf); if (error != 0) { *h_errnop = statp->res_h_errno; switch (statp->res_h_errno) { case HOST_NOT_FOUND: return (NS_NOTFOUND); case TRY_AGAIN: return (NS_TRYAGAIN); default: return (NS_UNAVAIL); } /*NOTREACHED*/ } if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { *errnop = errno; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (NS_RETURN); } RES_SET_H_ERRNO(statp, NETDB_SUCCESS); *((struct hostent **)rval) = hptr; return (NS_SUCCESS); } int _dns_gethostbyaddr(void *rval, void *cb_data, va_list ap) { const void *addr; socklen_t len; int af; char *buffer; size_t buflen; int *errnop, *h_errnop; const u_char *uaddr; struct hostent *hptr, he; struct hostent_data *hed; int n; querybuf *buf; char qbuf[MAXDNAME+1], *qp; res_state statp; #ifdef SUNSECURITY struct hostdata rhd; struct hostent *rhe; char **haddr; u_long old_options; char hname2[MAXDNAME+1], numaddr[46]; int ret_h_error; #endif /*SUNSECURITY*/ addr = va_arg(ap, const void *); len = va_arg(ap, socklen_t); af = va_arg(ap, int); hptr = va_arg(ap, struct hostent *); buffer = va_arg(ap, char *); buflen = va_arg(ap, size_t); errnop = va_arg(ap, int *); h_errnop = va_arg(ap, int *); uaddr = (const u_char *)addr; *((struct hostent **)rval) = NULL; statp = __res_state(); if ((hed = __hostent_data_init()) == NULL) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (NS_NOTFOUND); } switch (af) { case AF_INET: (void) sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff), (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff)); break; case AF_INET6: qp = qbuf; for (n = NS_IN6ADDRSZ - 1; n >= 0; n--) { qp += SPRINTF((qp, "%x.%x.", uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf)); } strlcat(qbuf, "ip6.arpa", sizeof(qbuf)); break; default: abort(); } if ((buf = malloc(sizeof(*buf))) == NULL) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return NS_NOTFOUND; } n = res_nquery(statp, qbuf, C_IN, T_PTR, (u_char *)buf->buf, sizeof buf->buf); if (n < 0) { free(buf); dprintf("res_nquery failed (%d)\n", n, statp); *h_errnop = statp->res_h_errno; return (NS_UNAVAIL); } if (n > sizeof buf->buf) { free(buf); dprintf("static buffer is too small (%d)\n", n, statp); *h_errnop = statp->res_h_errno; return (NS_UNAVAIL); } if (gethostanswer(buf, n, qbuf, T_PTR, &he, hed, statp) != 0) { free(buf); *h_errnop = statp->res_h_errno; switch (statp->res_h_errno) { case HOST_NOT_FOUND: return (NS_NOTFOUND); case TRY_AGAIN: return (NS_TRYAGAIN); default: return (NS_UNAVAIL); } /*NOTREACHED*/ } free(buf); #ifdef SUNSECURITY if (af == AF_INET) { /* * turn off search as the name should be absolute, * 'localhost' should be matched by defnames */ strncpy(hname2, he.h_name, MAXDNAME); hname2[MAXDNAME] = '\0'; old_options = statp->options; statp->options &= ~RES_DNSRCH; statp->options |= RES_DEFNAMES; memset(&rhd, 0, sizeof rhd); rhe = gethostbyname_r(hname2, &rhd.host, &rhd.data, sizeof(rhd.data), &ret_h_error); if (rhe == NULL) { if (inet_ntop(af, addr, numaddr, sizeof(numaddr)) == NULL) strlcpy(numaddr, "UNKNOWN", sizeof(numaddr)); syslog(LOG_NOTICE|LOG_AUTH, "gethostbyaddr: No A record for %s (verifying [%s])", hname2, numaddr); statp->options = old_options; RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); *h_errnop = statp->res_h_errno; return (NS_NOTFOUND); } statp->options = old_options; for (haddr = rhe->h_addr_list; *haddr; haddr++) if (!memcmp(*haddr, addr, NS_INADDRSZ)) break; if (!*haddr) { if (inet_ntop(af, addr, numaddr, sizeof(numaddr)) == NULL) strlcpy(numaddr, "UNKNOWN", sizeof(numaddr)); syslog(LOG_NOTICE|LOG_AUTH, "gethostbyaddr: A record of %s != PTR record [%s]", hname2, numaddr); RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); *h_errnop = statp->res_h_errno; return (NS_NOTFOUND); } } #endif /*SUNSECURITY*/ he.h_addrtype = af; he.h_length = len; memcpy(hed->host_addr, uaddr, len); hed->h_addr_ptrs[0] = (char *)hed->host_addr; hed->h_addr_ptrs[1] = NULL; if (af == AF_INET && (statp->options & RES_USE_INET6)) { _map_v4v6_address((char*)hed->host_addr, (char*)hed->host_addr); he.h_addrtype = AF_INET6; he.h_length = NS_IN6ADDRSZ; } if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { *errnop = errno; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (NS_RETURN); } RES_SET_H_ERRNO(statp, NETDB_SUCCESS); *((struct hostent **)rval) = hptr; return (NS_SUCCESS); } #ifdef RESOLVSORT static void addrsort(char **ap, int num, res_state res) { int i, j; char **p; short aval[_MAXADDRS]; int needsort = 0; p = ap; for (i = 0; i < num; i++, p++) { for (j = 0 ; (unsigned)j < res->nsort; j++) if (res->sort_list[j].addr.s_addr == (((struct in_addr *)(*p))->s_addr & res->sort_list[j].mask)) break; aval[i] = j; if (needsort == 0 && i > 0 && j < aval[i-1]) needsort = i; } if (!needsort) return; while (needsort < num) { for (j = needsort - 1; j >= 0; j--) { if (aval[j] > aval[j+1]) { char *hp; i = aval[j]; aval[j] = aval[j+1]; aval[j+1] = i; hp = ap[j]; ap[j] = ap[j+1]; ap[j+1] = hp; } else break; } needsort++; } } #endif void _sethostdnsent(int stayopen) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) return; if (stayopen) statp->options |= RES_STAYOPEN | RES_USEVC; } void _endhostdnsent(void) { res_state statp; statp = __res_state(); statp->options &= ~(RES_STAYOPEN | RES_USEVC); res_nclose(statp); } Index: head/lib/libc/net/getprotoent.c =================================================================== --- head/lib/libc/net/getprotoent.c (revision 298225) +++ head/lib/libc/net/getprotoent.c (revision 298226) @@ -1,554 +1,553 @@ /* * Copyright (c) 1983, 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getprotoent.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include #include #include #include #include #include "namespace.h" #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif #include "nss_tls.h" static const ns_src defaultsrc[] = { { NSSRC_FILES, NS_SUCCESS }, { NULL, 0 } }; NETDB_THREAD_ALLOC(protoent_data) NETDB_THREAD_ALLOC(protodata) static void protoent_data_clear(struct protoent_data *ped) { if (ped->fp) { fclose(ped->fp); ped->fp = NULL; } } static void protoent_data_free(void *ptr) { struct protoent_data *ped = ptr; protoent_data_clear(ped); free(ped); } static void protodata_free(void *ptr) { free(ptr); } #ifdef NS_CACHING int __proto_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; int proto; 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: proto = va_arg(ap, int); desired_size = sizeof(enum nss_lookup_type) + 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), &proto, sizeof(int)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } int __proto_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; int num; struct protoent *proto; char *orig_buf; size_t orig_buf_size; struct protoent new_proto; 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: num = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } proto = va_arg(ap, struct protoent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct protoent) + sizeof(char *); if (proto->p_name != NULL) desired_size += strlen(proto->p_name) + 1; if (proto->p_aliases != NULL) { aliases_size = 0; for (alias = proto->p_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_proto, proto, sizeof(struct protoent)); *buffer_size = desired_size; memset(buffer, 0, desired_size); p = buffer + sizeof(struct protoent) + sizeof(char *); memcpy(buffer + sizeof(struct protoent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_proto.p_name != NULL) { size = strlen(new_proto.p_name); memcpy(p, new_proto.p_name, size); new_proto.p_name = p; p += size + 1; } if (new_proto.p_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_proto.p_aliases, sizeof(char *) * aliases_size); new_proto.p_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (alias = new_proto.p_aliases; *alias; ++alias) { size = strlen(*alias); memcpy(p, *alias, size); *alias = p; p += size + 1; } } memcpy(buffer, &new_proto, sizeof(struct protoent)); return (NS_SUCCESS); } int __proto_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; int num; struct protoent *proto; 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: num = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } proto = va_arg(ap, struct protoent *); 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 protoent) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(proto, buffer, sizeof(struct protoent)); memcpy(&p, buffer + sizeof(struct protoent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct protoent) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct protoent) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(proto->p_name, orig_buf, p, char *); if (proto->p_aliases != NULL) { NS_APPLY_OFFSET(proto->p_aliases, orig_buf, p, char **); for (alias = proto->p_aliases; *alias; ++alias) NS_APPLY_OFFSET(*alias, orig_buf, p, char *); } if (retval != NULL) *((struct protoent **)retval) = proto; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(protocols); #endif /* NS_CACHING */ int __copy_protoent(struct protoent *pe, struct protoent *pptr, 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; pe->p_aliases[i]; i++, numptr++) { len += strlen(pe->p_aliases[i]) + 1; } len += strlen(pe->p_name) + 1; len += numptr * sizeof(char*); if (len > (int)buflen) { errno = ERANGE; return (-1); } /* copy protocol value*/ pptr->p_proto = pe->p_proto; cp = (char *)ALIGN(buf) + numptr * sizeof(char *); /* copy official name */ n = strlen(pe->p_name) + 1; strcpy(cp, pe->p_name); pptr->p_name = cp; cp += n; /* copy aliases */ pptr->p_aliases = (char **)ALIGN(buf); for (i = 0 ; pe->p_aliases[i]; i++) { n = strlen(pe->p_aliases[i]) + 1; strcpy(cp, pe->p_aliases[i]); pptr->p_aliases[i] = cp; cp += n; } pptr->p_aliases[i] = NULL; return (0); } void __setprotoent_p(int f, struct protoent_data *ped) { if (ped->fp == NULL) ped->fp = fopen(_PATH_PROTOCOLS, "re"); else rewind(ped->fp); ped->stayopen |= f; } void __endprotoent_p(struct protoent_data *ped) { if (ped->fp) { fclose(ped->fp); ped->fp = NULL; } ped->stayopen = 0; } int __getprotoent_p(struct protoent *pe, struct protoent_data *ped) { char *p; char *cp, **q, *endp; long l; if (ped->fp == NULL && (ped->fp = fopen(_PATH_PROTOCOLS, "re")) == NULL) return (-1); again: if ((p = fgets(ped->line, sizeof ped->line, ped->fp)) == NULL) return (-1); if (*p == '#') goto again; cp = strpbrk(p, "#\n"); if (cp != NULL) *cp = '\0'; pe->p_name = p; cp = strpbrk(p, " \t"); if (cp == NULL) goto again; *cp++ = '\0'; while (*cp == ' ' || *cp == '\t') cp++; p = strpbrk(cp, " \t"); if (p != NULL) *p++ = '\0'; l = strtol(cp, &endp, 10); if (endp == cp || *endp != '\0' || l < 0 || l > USHRT_MAX) goto again; pe->p_proto = l; q = pe->p_aliases = ped->aliases; if (p != NULL) { cp = p; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &ped->aliases[_MAXALIASES - 1]) *q++ = cp; cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; } } *q = NULL; return (0); } static int files_getprotoent_r(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; struct protoent *pptr; char *buffer; size_t buflen; int *errnop; pptr = va_arg(ap, struct protoent *); buffer = va_arg(ap, char *); buflen = va_arg(ap, size_t); errnop = va_arg(ap, int *); if ((ped = __protoent_data_init()) == NULL) { *errnop = errno; return (NS_NOTFOUND); } if (__getprotoent_p(&pe, ped) != 0) { *errnop = errno; return (NS_NOTFOUND); } if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { *errnop = errno; return (NS_RETURN); } *((struct protoent **)retval) = pptr; return (NS_SUCCESS); } static int files_setprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; int f; f = va_arg(ap, int); if ((ped = __protoent_data_init()) == NULL) return (NS_UNAVAIL); __setprotoent_p(f, ped); return (NS_UNAVAIL); } static int files_endprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; if ((ped = __protoent_data_init()) == NULL) return (NS_UNAVAIL); __endprotoent_p(ped); return (NS_UNAVAIL); } int getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen, struct protoent **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( protocols, (void *)nss_lt_all, __proto_marshal_func, __proto_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_getprotoent_r, (void *)nss_lt_all }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotoent_r", defaultsrc, pptr, buffer, buflen, &ret_errno); if (rv != NS_SUCCESS) { errno = ret_errno; return (ret_errno); } return (0); } void setprotoent(int stayopen) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( protocols, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setprotoent, NULL }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "setprotoent", defaultsrc, stayopen); } void endprotoent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( protocols, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_endprotoent, NULL }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "endprotoent", defaultsrc); } struct protoent * getprotoent(void) { struct protodata *pd; struct protoent *rval; if ((pd = __protodata_init()) == NULL) return (NULL); if (getprotoent_r(&pd->proto, pd->data, sizeof(pd->data), &rval) != 0) return (NULL); return (rval); } Index: head/lib/libc/net/getservent.c =================================================================== --- head/lib/libc/net/getservent.c (revision 298225) +++ head/lib/libc/net/getservent.c (revision 298226) @@ -1,1373 +1,1372 @@ /* * Copyright (c) 1983, 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getservent.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef YP #include #include #include #endif #include "namespace.h" #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif #include "nss_tls.h" enum constants { SETSERVENT = 1, ENDSERVENT = 2, SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ SERVENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ }; struct servent_mdata { enum nss_lookup_type how; int compat_mode; }; static const ns_src defaultsrc[] = { { NSSRC_COMPAT, NS_SUCCESS }, { NULL, 0 } }; static int servent_unpack(char *, struct servent *, char **, size_t, int *); /* files backend declarations */ struct files_state { FILE *fp; int stayopen; int compat_mode_active; }; static void files_endstate(void *); NSS_TLS_HANDLING(files); static int files_servent(void *, void *, va_list); static int files_setservent(void *, void *, va_list); /* db backend declarations */ struct db_state { DB *db; int stayopen; int keynum; }; static void db_endstate(void *); NSS_TLS_HANDLING(db); static int db_servent(void *, void *, va_list); static int db_setservent(void *, void *, va_list); #ifdef YP /* nis backend declarations */ static int nis_servent(void *, void *, va_list); static int nis_setservent(void *, void *, va_list); struct nis_state { int yp_stepping; char yp_domain[MAXHOSTNAMELEN]; char *yp_key; int yp_keylen; }; static void nis_endstate(void *); NSS_TLS_HANDLING(nis); static int nis_servent(void *, void *, va_list); static int nis_setservent(void *, void *, va_list); #endif /* compat backend declarations */ static int compat_setservent(void *, void *, va_list); /* get** wrappers for get**_r functions declarations */ struct servent_state { struct servent serv; char *buffer; size_t bufsize; }; static void servent_endstate(void *); NSS_TLS_HANDLING(servent); struct key { const char *proto; union { const char *name; int port; }; }; static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t, struct servent **); static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t, struct servent **); static int wrap_getservent_r(struct key, struct servent *, char *, size_t, struct servent **); static struct servent *getserv(int (*fn)(struct key, struct servent *, char *, size_t, struct servent **), struct key); #ifdef NS_CACHING static int serv_id_func(char *, size_t *, va_list, void *); static int serv_marshal_func(char *, size_t *, void *, va_list, void *); static int serv_unmarshal_func(char *, size_t, void *, va_list, void *); #endif static int servent_unpack(char *p, struct servent *serv, char **aliases, size_t aliases_size, int *errnop) { char *cp, **q, *endp; long l; if (*p == '#') return -1; memset(serv, 0, sizeof(struct servent)); cp = strpbrk(p, "#\n"); if (cp != NULL) *cp = '\0'; serv->s_name = p; p = strpbrk(p, " \t"); if (p == NULL) return -1; *p++ = '\0'; while (*p == ' ' || *p == '\t') p++; cp = strpbrk(p, ",/"); if (cp == NULL) return -1; *cp++ = '\0'; l = strtol(p, &endp, 10); if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) return -1; serv->s_port = htons((in_port_t)l); serv->s_proto = cp; q = serv->s_aliases = aliases; cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &aliases[aliases_size - 1]) { *q++ = cp; } else { *q = NULL; *errnop = ERANGE; return -1; } cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; } *q = NULL; return 0; } static int parse_result(struct servent *serv, char *buffer, size_t bufsize, char *resultbuf, size_t resultbuflen, int *errnop) { char **aliases; int aliases_size; if (bufsize <= resultbuflen + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; return (NS_RETURN); } aliases = (char **)_ALIGN(&buffer[resultbuflen + 1]); aliases_size = (buffer + bufsize - (char *)aliases) / sizeof(char *); if (aliases_size < 1) { *errnop = ERANGE; return (NS_RETURN); } memcpy(buffer, resultbuf, resultbuflen); buffer[resultbuflen] = '\0'; if (servent_unpack(buffer, serv, aliases, aliases_size, errnop) != 0) return ((*errnop == 0) ? NS_NOTFOUND : NS_RETURN); return (NS_SUCCESS); } /* files backend implementation */ static void files_endstate(void *p) { FILE * f; if (p == NULL) return; f = ((struct files_state *)p)->fp; if (f != NULL) fclose(f); free(p); } /* * compat structures. compat and files sources functionalities are almost * equal, so they all are managed by files_servent function */ static int files_servent(void *retval, void *mdata, va_list ap) { static const ns_src compat_src[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab compat_dtab[] = { { NSSRC_DB, db_servent, (void *)((struct servent_mdata *)mdata)->how }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)((struct servent_mdata *)mdata)->how }, #endif { NULL, NULL, NULL } }; struct files_state *st; int rv; int stayopen; struct servent_mdata *serv_mdata; char *name; char *proto; int port; struct servent *serv; char *buffer; size_t bufsize; int *errnop; size_t linesize; char *line; char **cp; name = NULL; proto = NULL; serv_mdata = (struct servent_mdata *)mdata; switch (serv_mdata->how) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: return NS_NOTFOUND; } serv = va_arg(ap, struct servent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap,int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->fp == NULL) st->compat_mode_active = 0; if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "re")) == NULL) { *errnop = errno; return (NS_UNAVAIL); } if (serv_mdata->how == nss_lt_all) stayopen = 1; else { rewind(st->fp); stayopen = st->stayopen; } rv = NS_NOTFOUND; do { if (!st->compat_mode_active) { if ((line = fgetln(st->fp, &linesize)) == NULL) { *errnop = errno; rv = NS_RETURN; break; } if (*line=='+' && serv_mdata->compat_mode != 0) st->compat_mode_active = 1; } if (st->compat_mode_active != 0) { switch (serv_mdata->how) { case nss_lt_name: rv = nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "getservbyname_r", compat_src, name, proto, serv, buffer, bufsize, errnop); break; case nss_lt_id: rv = nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "getservbyport_r", compat_src, port, proto, serv, buffer, bufsize, errnop); break; case nss_lt_all: rv = nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "getservent_r", compat_src, serv, buffer, bufsize, errnop); break; } if (!(rv & NS_TERMINATE) || serv_mdata->how != nss_lt_all) st->compat_mode_active = 0; continue; } rv = parse_result(serv, buffer, bufsize, line, linesize, errnop); if (rv == NS_NOTFOUND) continue; if (rv == NS_RETURN) break; rv = NS_NOTFOUND; switch (serv_mdata->how) { case nss_lt_name: if (strcmp(name, serv->s_name) == 0) goto gotname; for (cp = serv->s_aliases; *cp; cp++) if (strcmp(name, *cp) == 0) goto gotname; continue; gotname: if (proto == NULL || strcmp(serv->s_proto, proto) == 0) rv = NS_SUCCESS; break; case nss_lt_id: if (port != serv->s_port) continue; if (proto == NULL || strcmp(serv->s_proto, proto) == 0) rv = NS_SUCCESS; break; case nss_lt_all: rv = NS_SUCCESS; break; } } while (!(rv & NS_TERMINATE)); if (!stayopen && st->fp != NULL) { fclose(st->fp); st->fp = NULL; } if ((rv == NS_SUCCESS) && (retval != NULL)) *(struct servent **)retval=serv; return (rv); } static int files_setservent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv; int f; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETSERVENT: f = va_arg(ap,int); if (st->fp == NULL) st->fp = fopen(_PATH_SERVICES, "re"); else rewind(st->fp); st->stayopen |= f; break; case ENDSERVENT: if (st->fp != NULL) { fclose(st->fp); st->fp = NULL; } st->stayopen = 0; break; default: break; } st->compat_mode_active = 0; return (NS_UNAVAIL); } /* db backend implementation */ static void db_endstate(void *p) { DB *db; if (p == NULL) return; db = ((struct db_state *)p)->db; if (db != NULL) db->close(db); free(p); } static int db_servent(void *retval, void *mdata, va_list ap) { char buf[BUFSIZ]; DBT key, data, *result; DB *db; struct db_state *st; int rv; int stayopen; enum nss_lookup_type how; char *name; char *proto; int port; struct servent *serv; char *buffer; size_t bufsize; int *errnop; name = NULL; proto = NULL; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: return NS_NOTFOUND; } serv = va_arg(ap, struct servent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap,int *); *errnop = db_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) return (NS_NOTFOUND); if (st->db == NULL) { st->db = dbopen(_PATH_SERVICES_DB, O_RDONLY, 0, DB_HASH, NULL); if (st->db == NULL) { *errnop = errno; return (NS_UNAVAIL); } } stayopen = (how == nss_lt_all) ? 1 : st->stayopen; db = st->db; do { switch (how) { case nss_lt_name: key.data = buf; if (proto == NULL) key.size = snprintf(buf, sizeof(buf), "\376%s", name); else key.size = snprintf(buf, sizeof(buf), "\376%s/%s", name, proto); key.size++; if (db->get(db, &key, &data, 0) != 0 || db->get(db, &data, &key, 0) != 0) { rv = NS_NOTFOUND; goto db_fin; } result = &key; break; case nss_lt_id: key.data = buf; port = htons(port); if (proto == NULL) key.size = snprintf(buf, sizeof(buf), "\377%d", port); else key.size = snprintf(buf, sizeof(buf), "\377%d/%s", port, proto); key.size++; if (db->get(db, &key, &data, 0) != 0 || db->get(db, &data, &key, 0) != 0) { rv = NS_NOTFOUND; goto db_fin; } result = &key; break; case nss_lt_all: key.data = buf; key.size = snprintf(buf, sizeof(buf), "%d", st->keynum++); key.size++; if (db->get(db, &key, &data, 0) != 0) { st->keynum = -1; rv = NS_NOTFOUND; goto db_fin; } result = &data; break; } rv = parse_result(serv, buffer, bufsize, result->data, result->size - 1, errnop); } while (!(rv & NS_TERMINATE) && how == nss_lt_all); db_fin: if (!stayopen && st->db != NULL) { db->close(db); st->db = NULL; } if (rv == NS_SUCCESS && retval != NULL) *(struct servent **)retval = serv; return (rv); } static int db_setservent(void *retval, void *mdata, va_list ap) { DB *db; struct db_state *st; int rv; int f; rv = db_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETSERVENT: f = va_arg(ap, int); st->stayopen |= f; st->keynum = 0; break; case ENDSERVENT: db = st->db; if (db != NULL) { db->close(db); st->db = NULL; } st->stayopen = 0; break; default: break; } return (NS_UNAVAIL); } /* nis backend implementation */ #ifdef YP static void nis_endstate(void *p) { if (p == NULL) return; free(((struct nis_state *)p)->yp_key); free(p); } static int nis_servent(void *retval, void *mdata, va_list ap) { char *resultbuf, *lastkey; int resultbuflen; char buf[YPMAXRECORD + 2]; struct nis_state *st; int rv; enum nss_lookup_type how; char *name; char *proto; int port; struct servent *serv; char *buffer; size_t bufsize; int *errnop; name = NULL; proto = NULL; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: return NS_NOTFOUND; } serv = va_arg(ap, struct servent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->yp_domain[0] == '\0') { if (getdomainname(st->yp_domain, sizeof st->yp_domain)) { *errnop = errno; return (NS_UNAVAIL); } } do { switch (how) { case nss_lt_name: snprintf(buf, sizeof(buf), "%s/%s", name, proto); if (yp_match(st->yp_domain, "services.byname", buf, strlen(buf), &resultbuf, &resultbuflen)) { rv = NS_NOTFOUND; goto fin; } break; case nss_lt_id: snprintf(buf, sizeof(buf), "%d/%s", ntohs(port), proto); /* * We have to be a little flexible * here. Ideally you're supposed to have both * a services.byname and a services.byport * map, but some systems have only * services.byname. FreeBSD cheats a little by * putting the services.byport information in * the same map as services.byname so that * either case will work. We allow for both * possibilities here: if there is no * services.byport map, we try services.byname * instead. */ rv = yp_match(st->yp_domain, "services.byport", buf, strlen(buf), &resultbuf, &resultbuflen); if (rv) { if (rv == YPERR_MAP) { if (yp_match(st->yp_domain, "services.byname", buf, strlen(buf), &resultbuf, &resultbuflen)) { rv = NS_NOTFOUND; goto fin; } } else { rv = NS_NOTFOUND; goto fin; } } break; case nss_lt_all: if (!st->yp_stepping) { free(st->yp_key); rv = yp_first(st->yp_domain, "services.byname", &st->yp_key, &st->yp_keylen, &resultbuf, &resultbuflen); if (rv) { rv = NS_NOTFOUND; goto fin; } st->yp_stepping = 1; } else { lastkey = st->yp_key; rv = yp_next(st->yp_domain, "services.byname", st->yp_key, st->yp_keylen, &st->yp_key, &st->yp_keylen, &resultbuf, &resultbuflen); free(lastkey); if (rv) { st->yp_stepping = 0; rv = NS_NOTFOUND; goto fin; } } break; } rv = parse_result(serv, buffer, bufsize, resultbuf, resultbuflen, errnop); free(resultbuf); } while (!(rv & NS_TERMINATE) && how == nss_lt_all); fin: if (rv == NS_SUCCESS && retval != NULL) *(struct servent **)retval = serv; return (rv); } static int nis_setservent(void *result, void *mdata, va_list ap) { struct nis_state *st; int rv; rv = nis_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETSERVENT: case ENDSERVENT: free(st->yp_key); st->yp_key = NULL; st->yp_stepping = 0; break; default: break; } return (NS_UNAVAIL); } #endif /* compat backend implementation */ static int compat_setservent(void *retval, void *mdata, va_list ap) { static const ns_src compat_src[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab compat_dtab[] = { { NSSRC_DB, db_setservent, mdata }, #ifdef YP { NSSRC_NIS, nis_setservent, mdata }, #endif { NULL, NULL, NULL } }; int f; (void)files_setservent(retval, mdata, ap); switch ((enum constants)mdata) { case SETSERVENT: f = va_arg(ap,int); (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "setservent", compat_src, f); break; case ENDSERVENT: (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "endservent", compat_src); break; default: break; } return (NS_UNAVAIL); } #ifdef NS_CACHING static int serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; char *proto; int port; size_t desired_size, size, size2; 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 *); proto = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (proto != NULL) { size2 = strlen(proto); desired_size += size2 + 1; } else size2 = 0; 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); if (proto != NULL) memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1, proto, size2 + 1); res = NS_SUCCESS; break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); desired_size = sizeof(enum nss_lookup_type) + sizeof(int); if (proto != NULL) { size = strlen(proto); desired_size += size + 1; } else size = 0; 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), &port, sizeof(int)); if (proto != NULL) memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(int), proto, size + 1); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } int serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; char *proto; int port; struct servent *serv; char *orig_buf; size_t orig_buf_size; struct servent new_serv; size_t desired_size; char **alias; char *p; size_t size; size_t aliases_size; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } serv = va_arg(ap, struct servent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *); if (serv->s_name != NULL) desired_size += strlen(serv->s_name) + 1; if (serv->s_proto != NULL) desired_size += strlen(serv->s_proto) + 1; aliases_size = 0; if (serv->s_aliases != NULL) { for (alias = serv->s_aliases; *alias; ++alias) { desired_size += strlen(*alias) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + sizeof(char *) * (aliases_size + 1); } if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_serv, serv, sizeof(struct servent)); memset(buffer, 0, desired_size); *buffer_size = desired_size; p = buffer + sizeof(struct servent) + sizeof(char *); memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_serv.s_name != NULL) { size = strlen(new_serv.s_name); memcpy(p, new_serv.s_name, size); new_serv.s_name = p; p += size + 1; } if (new_serv.s_proto != NULL) { size = strlen(new_serv.s_proto); memcpy(p, new_serv.s_proto, size); new_serv.s_proto = p; p += size + 1; } if (new_serv.s_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); new_serv.s_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (alias = new_serv.s_aliases; *alias; ++alias) { size = strlen(*alias); memcpy(p, *alias, size); *alias = p; p += size + 1; } } memcpy(buffer, &new_serv, sizeof(struct servent)); return (NS_SUCCESS); } int serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; char *proto; int port; struct servent *serv; char *orig_buf; char *p; char **alias; size_t orig_buf_size; int *ret_errno; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } serv = va_arg(ap, struct servent *); 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 servent) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(serv, buffer, sizeof(struct servent)); memcpy(&p, buffer + sizeof(struct servent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) + (_ALIGN(p) - (size_t)p), buffer_size - sizeof(struct servent) - sizeof(char *) - (_ALIGN(p) - (size_t)p)); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *); NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *); if (serv->s_aliases != NULL) { NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **); for (alias = serv->s_aliases; *alias; ++alias) NS_APPLY_OFFSET(*alias, orig_buf, p, char *); } if (retval != NULL) *((struct servent **)retval) = serv; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(services); #endif /* NS_CACHING */ /* get**_r functions implementation */ int getservbyname_r(const char *name, const char *proto, struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { static const struct servent_mdata mdata = { nss_lt_name, 0 }; static const struct servent_mdata compat_mdata = { nss_lt_name, 1 }; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_name, serv_id_func, serv_marshal_func, serv_unmarshal_func); #endif /* NS_CACHING */ static const ns_dtab dtab[] = { { NSSRC_FILES, files_servent, (void *)&mdata }, { NSSRC_DB, db_servent, (void *)nss_lt_name }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)nss_lt_name }, #endif { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r", defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getservbyport_r(int port, const char *proto, struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { static const struct servent_mdata mdata = { nss_lt_id, 0 }; static const struct servent_mdata compat_mdata = { nss_lt_id, 1 }; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_id, serv_id_func, serv_marshal_func, serv_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_servent, (void *)&mdata }, { NSSRC_DB, db_servent, (void *)nss_lt_id }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)nss_lt_id }, #endif { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r", defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getservent_r(struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { static const struct servent_mdata mdata = { nss_lt_all, 0 }; static const struct servent_mdata compat_mdata = { nss_lt_all, 1 }; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_all, serv_marshal_func, serv_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_servent, (void *)&mdata }, { NSSRC_DB, db_servent, (void *)nss_lt_all }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)nss_lt_all }, #endif { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r", defaultsrc, serv, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } void setservent(int stayopen) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setservent, (void *)SETSERVENT }, { NSSRC_DB, db_setservent, (void *)SETSERVENT }, #ifdef YP { NSSRC_NIS, nis_setservent, (void *)SETSERVENT }, #endif { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc, stayopen); } void endservent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setservent, (void *)ENDSERVENT }, { NSSRC_DB, db_setservent, (void *)ENDSERVENT }, #ifdef YP { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT }, #endif { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc); } /* get** wrappers for get**_r functions implementation */ static void servent_endstate(void *p) { if (p == NULL) return; free(((struct servent_state *)p)->buffer); free(p); } static int wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer, size_t bufsize, struct servent **res) { return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize, res)); } static int wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer, size_t bufsize, struct servent **res) { return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize, res)); } static int wrap_getservent_r(struct key key, struct servent *serv, char *buffer, size_t bufsize, struct servent **res) { return (getservent_r(serv, buffer, bufsize, res)); } static struct servent * getserv(int (*fn)(struct key, struct servent *, char *, size_t, struct servent **), struct key key) { int rv; struct servent *res; struct servent_state * st; rv = servent_getstate(&st); if (rv != 0) { errno = rv; return NULL; } if (st->buffer == NULL) { st->buffer = malloc(SERVENT_STORAGE_INITIAL); if (st->buffer == NULL) return (NULL); st->bufsize = SERVENT_STORAGE_INITIAL; } do { rv = fn(key, &st->serv, st->buffer, st->bufsize, &res); if (res == NULL && rv == ERANGE) { free(st->buffer); if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) { st->buffer = NULL; errno = ERANGE; return (NULL); } st->bufsize <<= 1; st->buffer = malloc(st->bufsize); if (st->buffer == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } struct servent * getservbyname(const char *name, const char *proto) { struct key key; key.name = name; key.proto = proto; return (getserv(wrap_getservbyname_r, key)); } struct servent * getservbyport(int port, const char *proto) { struct key key; key.port = port; key.proto = proto; return (getserv(wrap_getservbyport_r, key)); } struct servent * getservent(void) { struct key key; key.proto = NULL; key.port = 0; return (getserv(wrap_getservent_r, key)); } Index: head/lib/libc/net/hesiod.c =================================================================== --- head/lib/libc/net/hesiod.c (revision 298225) +++ head/lib/libc/net/hesiod.c (revision 298226) @@ -1,584 +1,583 @@ /* $NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Copyright 1996 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. */ /* This file is part of the hesiod library. It implements the core * portion of the hesiod resolver. * * This file is loosely based on an interim version of hesiod.c from * the BIND IRS library, which was in turn based on an earlier version * of this file. Extensive changes have been made on each step of the * path. * * This implementation is not truly thread-safe at the moment because * it uses res_send() and accesses _res. */ #include #if 0 static char *orig_rcsid = "$NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $"; #endif #include __FBSDID("$FreeBSD$"); -#include #include #include #include #include #include #include #include #include #include #include #include struct hesiod_p { char *lhs; /* normally ".ns" */ char *rhs; /* AKA the default hesiod domain */ int classes[2]; /* The class search order. */ }; #define MAX_HESRESP 1024 static int read_config_file(struct hesiod_p *, const char *); static char **get_txt_records(int, const char *); static int init_context(void); static void translate_errors(void); /* * hesiod_init -- * initialize a hesiod_p. */ int hesiod_init(context) void **context; { struct hesiod_p *ctx; const char *p, *configname; ctx = malloc(sizeof(struct hesiod_p)); if (ctx) { *context = ctx; if (!issetugid()) configname = getenv("HESIOD_CONFIG"); else configname = NULL; if (!configname) configname = _PATH_HESIOD_CONF; if (read_config_file(ctx, configname) >= 0) { /* * The default rhs can be overridden by an * environment variable. */ if (!issetugid()) p = getenv("HES_DOMAIN"); else p = NULL; if (p) { if (ctx->rhs) free(ctx->rhs); ctx->rhs = malloc(strlen(p) + 2); if (ctx->rhs) { *ctx->rhs = '.'; strcpy(ctx->rhs + 1, (*p == '.') ? p + 1 : p); return 0; } else errno = ENOMEM; } else return 0; } } else errno = ENOMEM; if (ctx->lhs) free(ctx->lhs); if (ctx->rhs) free(ctx->rhs); if (ctx) free(ctx); return -1; } /* * hesiod_end -- * Deallocates the hesiod_p. */ void hesiod_end(context) void *context; { struct hesiod_p *ctx = (struct hesiod_p *) context; free(ctx->rhs); if (ctx->lhs) free(ctx->lhs); free(ctx); } /* * hesiod_to_bind -- * takes a hesiod (name, type) and returns a DNS * name which is to be resolved. */ char * hesiod_to_bind(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char bindname[MAXDNAME], *p, *ret, **rhs_list = NULL; const char *rhs; int len; if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) { errno = EMSGSIZE; return NULL; } /* * Find the right right hand side to use, possibly * truncating bindname. */ p = strchr(bindname, '@'); if (p) { *p++ = 0; if (strchr(p, '.')) rhs = name + (p - bindname); else { rhs_list = hesiod_resolve(context, p, "rhs-extension"); if (rhs_list) rhs = *rhs_list; else { errno = ENOENT; return NULL; } } } else rhs = ctx->rhs; /* See if we have enough room. */ len = strlen(bindname) + 1 + strlen(type); if (ctx->lhs) len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0); len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0); if (len > sizeof(bindname) - 1) { if (rhs_list) hesiod_free_list(context, rhs_list); errno = EMSGSIZE; return NULL; } /* Put together the rest of the domain. */ strcat(bindname, "."); strcat(bindname, type); /* Only append lhs if it isn't empty. */ if (ctx->lhs && ctx->lhs[0] != '\0' ) { if (ctx->lhs[0] != '.') strcat(bindname, "."); strcat(bindname, ctx->lhs); } if (rhs[0] != '.') strcat(bindname, "."); strcat(bindname, rhs); /* rhs_list is no longer needed, since we're done with rhs. */ if (rhs_list) hesiod_free_list(context, rhs_list); /* Make a copy of the result and return it to the caller. */ ret = strdup(bindname); if (!ret) errno = ENOMEM; return ret; } /* * hesiod_resolve -- * Given a hesiod name and type, return an array of strings returned * by the resolver. */ char ** hesiod_resolve(context, name, type) void *context; const char *name; const char *type; { struct hesiod_p *ctx = (struct hesiod_p *) context; char *bindname, **retvec; bindname = hesiod_to_bind(context, name, type); if (!bindname) return NULL; retvec = get_txt_records(ctx->classes[0], bindname); if (retvec == NULL && errno == ENOENT && ctx->classes[1]) retvec = get_txt_records(ctx->classes[1], bindname); free(bindname); return retvec; } /*ARGSUSED*/ void hesiod_free_list(context, list) void *context; char **list; { char **p; if (list == NULL) return; for (p = list; *p; p++) free(*p); free(list); } /* read_config_file -- * Parse the /etc/hesiod.conf file. Returns 0 on success, * -1 on failure. On failure, it might leave values in ctx->lhs * or ctx->rhs which need to be freed by the caller. */ static int read_config_file(ctx, filename) struct hesiod_p *ctx; const char *filename; { char *key, *data, *p, **which; char buf[MAXDNAME + 7]; int n; FILE *fp; /* Set default query classes. */ ctx->classes[0] = C_IN; ctx->classes[1] = C_HS; /* Try to open the configuration file. */ fp = fopen(filename, "re"); if (!fp) { /* Use compiled in default domain names. */ ctx->lhs = strdup(DEF_LHS); ctx->rhs = strdup(DEF_RHS); if (ctx->lhs && ctx->rhs) return 0; else { errno = ENOMEM; return -1; } } ctx->lhs = NULL; ctx->rhs = NULL; while (fgets(buf, sizeof(buf), fp) != NULL) { p = buf; if (*p == '#' || *p == '\n' || *p == '\r') continue; while (*p == ' ' || *p == '\t') p++; key = p; while (*p != ' ' && *p != '\t' && *p != '=') p++; *p++ = 0; while (isspace(*p) || *p == '=') p++; data = p; while (!isspace(*p)) p++; *p = 0; if (strcasecmp(key, "lhs") == 0 || strcasecmp(key, "rhs") == 0) { which = (strcasecmp(key, "lhs") == 0) ? &ctx->lhs : &ctx->rhs; *which = strdup(data); if (!*which) { fclose(fp); errno = ENOMEM; return -1; } } else { if (strcasecmp(key, "classes") == 0) { n = 0; while (*data && n < 2) { p = data; while (*p && *p != ',') p++; if (*p) *p++ = 0; if (strcasecmp(data, "IN") == 0) ctx->classes[n++] = C_IN; else if (strcasecmp(data, "HS") == 0) ctx->classes[n++] = C_HS; data = p; } while (n < 2) ctx->classes[n++] = 0; } } } fclose(fp); if (!ctx->rhs || ctx->classes[0] == 0 || ctx->classes[0] == ctx->classes[1]) { errno = ENOEXEC; return -1; } return 0; } /* * get_txt_records -- * Given a DNS class and a DNS name, do a lookup for TXT records, and * return a list of them. */ static char ** get_txt_records(qclass, name) int qclass; const char *name; { HEADER *hp; unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor; char *dst, **list; int ancount, qdcount, i, j, n, skip, type, class, len; /* Make sure the resolver is initialized. */ if ((_res.options & RES_INIT) == 0 && res_init() == -1) return NULL; /* Construct the query. */ n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0, NULL, qbuf, PACKETSZ); if (n < 0) return NULL; /* Send the query. */ n = res_send(qbuf, n, abuf, MAX_HESRESP); if (n < 0 || n > MAX_HESRESP) { errno = ECONNREFUSED; /* XXX */ return NULL; } /* Parse the header of the result. */ hp = (HEADER *) (void *) abuf; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); p = abuf + sizeof(HEADER); eom = abuf + n; /* * Skip questions, trying to get to the answer section * which follows. */ for (i = 0; i < qdcount; i++) { skip = dn_skipname(p, eom); if (skip < 0 || p + skip + QFIXEDSZ > eom) { errno = EMSGSIZE; return NULL; } p += skip + QFIXEDSZ; } /* Allocate space for the text record answers. */ list = malloc((ancount + 1) * sizeof(char *)); if (!list) { errno = ENOMEM; return NULL; } /* Parse the answers. */ j = 0; for (i = 0; i < ancount; i++) { /* Parse the header of this answer. */ skip = dn_skipname(p, eom); if (skip < 0 || p + skip + 10 > eom) break; type = p[skip + 0] << 8 | p[skip + 1]; class = p[skip + 2] << 8 | p[skip + 3]; len = p[skip + 8] << 8 | p[skip + 9]; p += skip + 10; if (p + len > eom) { errno = EMSGSIZE; break; } /* Skip entries of the wrong class and type. */ if (class != qclass || type != T_TXT) { p += len; continue; } /* Allocate space for this answer. */ list[j] = malloc((size_t)len); if (!list[j]) { errno = ENOMEM; break; } dst = list[j++]; /* Copy answer data into the allocated area. */ eor = p + len; while (p < eor) { n = (unsigned char) *p++; if (p + n > eor) { errno = EMSGSIZE; break; } memcpy(dst, p, (size_t)n); p += n; dst += n; } if (p < eor) { errno = EMSGSIZE; break; } *dst = 0; } /* * If we didn't terminate the loop normally, something * went wrong. */ if (i < ancount) { for (i = 0; i < j; i++) free(list[i]); free(list); return NULL; } if (j == 0) { errno = ENOENT; free(list); return NULL; } list[j] = NULL; return list; } /* * COMPATIBILITY FUNCTIONS */ static int inited = 0; static void *context; static int errval = HES_ER_UNINIT; int hes_init() { init_context(); return errval; } char * hes_to_bind(name, type) const char *name; const char *type; { static char *bindname; if (init_context() < 0) return NULL; if (bindname) free(bindname); bindname = hesiod_to_bind(context, name, type); if (!bindname) translate_errors(); return bindname; } char ** hes_resolve(name, type) const char *name; const char *type; { static char **list; if (init_context() < 0) return NULL; /* * In the old Hesiod interface, the caller was responsible for * freeing the returned strings but not the vector of strings itself. */ if (list) free(list); list = hesiod_resolve(context, name, type); if (!list) translate_errors(); return list; } int hes_error() { return errval; } void hes_free(hp) char **hp; { hesiod_free_list(context, hp); } static int init_context() { if (!inited) { inited = 1; if (hesiod_init(&context) < 0) { errval = HES_ER_CONFIG; return -1; } errval = HES_ER_OK; } return 0; } static void translate_errors() { switch (errno) { case ENOENT: errval = HES_ER_NOTFOUND; break; case ECONNREFUSED: case EMSGSIZE: errval = HES_ER_NET; break; case ENOMEM: default: /* Not a good match, but the best we can do. */ errval = HES_ER_CONFIG; break; } } Index: head/lib/libc/net/ip6opt.c =================================================================== --- head/lib/libc/net/ip6opt.c (revision 298225) +++ head/lib/libc/net/ip6opt.c (revision 298226) @@ -1,602 +1,601 @@ /* $KAME: ip6opt.c,v 1.13 2003/06/06 10:08:20 suz 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. */ #include __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include #include static int ip6optlen(u_int8_t *opt, u_int8_t *lim); static void inet6_insert_padopt(u_char *p, int len); #ifndef IPV6_2292HOPOPTS #define IPV6_2292HOPOPTS 22 #endif #ifndef IPV6_2292DSTOPTS #define IPV6_2292DSTOPTS 23 #endif #define is_ipv6_hopopts(x) \ ((x) == IPV6_HOPOPTS || (x) == IPV6_2292HOPOPTS) #define is_ipv6_dstopts(x) \ ((x) == IPV6_DSTOPTS || (x) == IPV6_2292DSTOPTS) /* * This function returns the number of bytes required to hold an option * when it is stored as ancillary data, including the cmsghdr structure * at the beginning, and any padding at the end (to make its size a * multiple of 8 bytes). The argument is the size of the structure * defining the option, which must include any pad bytes at the * beginning (the value y in the alignment term "xn + y"), the type * byte, the length byte, and the option data. */ int inet6_option_space(int nbytes) { nbytes += 2; /* we need space for nxt-hdr and length fields */ return(CMSG_SPACE((nbytes + 7) & ~7)); } /* * This function is called once per ancillary data object that will * contain either Hop-by-Hop or Destination options. It returns 0 on * success or -1 on an error. */ int inet6_option_init(void *bp, struct cmsghdr **cmsgp, int type) { struct cmsghdr *ch = (struct cmsghdr *)bp; /* argument validation */ if (!is_ipv6_hopopts(type) && !is_ipv6_dstopts(type)) return(-1); ch->cmsg_level = IPPROTO_IPV6; ch->cmsg_type = type; ch->cmsg_len = CMSG_LEN(0); *cmsgp = ch; return(0); } /* * This function appends a Hop-by-Hop option or a Destination option * into an ancillary data object that has been initialized by * inet6_option_init(). This function returns 0 if it succeeds or -1 on * an error. * multx is the value x in the alignment term "xn + y" described * earlier. It must have a value of 1, 2, 4, or 8. * plusy is the value y in the alignment term "xn + y" described * earlier. It must have a value between 0 and 7, inclusive. */ int inet6_option_append(struct cmsghdr *cmsg, const u_int8_t *typep, int multx, int plusy) { int padlen, optlen, off; u_char *bp = (u_char *)cmsg + cmsg->cmsg_len; struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); /* argument validation */ if (multx != 1 && multx != 2 && multx != 4 && multx != 8) return(-1); if (plusy < 0 || plusy > 7) return(-1); /* * If this is the first option, allocate space for the * first 2 bytes(for next header and length fields) of * the option header. */ if (bp == (u_char *)eh) { bp += 2; cmsg->cmsg_len += 2; } /* calculate pad length before the option. */ off = bp - (u_char *)eh; padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - (off % multx); padlen += plusy; padlen %= multx; /* keep the pad as short as possible */ /* insert padding */ inet6_insert_padopt(bp, padlen); cmsg->cmsg_len += padlen; bp += padlen; /* copy the option */ if (typep[0] == IP6OPT_PAD1) optlen = 1; else optlen = typep[1] + 2; memcpy(bp, typep, optlen); bp += optlen; cmsg->cmsg_len += optlen; /* calculate pad length after the option and insert the padding */ off = bp - (u_char *)eh; padlen = ((off + 7) & ~7) - off; inet6_insert_padopt(bp, padlen); bp += padlen; cmsg->cmsg_len += padlen; /* update the length field of the ip6 option header */ eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; return(0); } /* * This function appends a Hop-by-Hop option or a Destination option * into an ancillary data object that has been initialized by * inet6_option_init(). This function returns a pointer to the 8-bit * option type field that starts the option on success, or NULL on an * error. * The difference between this function and inet6_option_append() is * that the latter copies the contents of a previously built option into * the ancillary data object while the current function returns a * pointer to the space in the data object where the option's TLV must * then be built by the caller. * */ u_int8_t * inet6_option_alloc(struct cmsghdr *cmsg, int datalen, int multx, int plusy) { int padlen, off; u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; u_int8_t *retval; struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); /* argument validation */ if (multx != 1 && multx != 2 && multx != 4 && multx != 8) return(NULL); if (plusy < 0 || plusy > 7) return(NULL); /* * If this is the first option, allocate space for the * first 2 bytes(for next header and length fields) of * the option header. */ if (bp == (u_char *)eh) { bp += 2; cmsg->cmsg_len += 2; } /* calculate pad length before the option. */ off = bp - (u_char *)eh; padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - (off % multx); padlen += plusy; padlen %= multx; /* keep the pad as short as possible */ /* insert padding */ inet6_insert_padopt(bp, padlen); cmsg->cmsg_len += padlen; bp += padlen; /* keep space to store specified length of data */ retval = bp; bp += datalen; cmsg->cmsg_len += datalen; /* calculate pad length after the option and insert the padding */ off = bp - (u_char *)eh; padlen = ((off + 7) & ~7) - off; inet6_insert_padopt(bp, padlen); bp += padlen; cmsg->cmsg_len += padlen; /* update the length field of the ip6 option header */ eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; return(retval); } /* * This function processes the next Hop-by-Hop option or Destination * option in an ancillary data object. If another option remains to be * processed, the return value of the function is 0 and *tptrp points to * the 8-bit option type field (which is followed by the 8-bit option * data length, followed by the option data). If no more options remain * to be processed, the return value is -1 and *tptrp is NULL. If an * error occurs, the return value is -1 and *tptrp is not NULL. * (RFC 2292, 6.3.5) */ int inet6_option_next(const struct cmsghdr *cmsg, u_int8_t **tptrp) { struct ip6_ext *ip6e; int hdrlen, optlen; u_int8_t *lim; if (cmsg->cmsg_level != IPPROTO_IPV6 || (!is_ipv6_hopopts(cmsg->cmsg_type) && !is_ipv6_dstopts(cmsg->cmsg_type))) return(-1); /* message length validation */ if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) return(-1); ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); hdrlen = (ip6e->ip6e_len + 1) << 3; if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) return(-1); /* * If the caller does not specify the starting point, * simply return the 1st option. * Otherwise, search the option list for the next option. */ lim = (u_int8_t *)ip6e + hdrlen; if (*tptrp == NULL) *tptrp = (u_int8_t *)(ip6e + 1); else { if ((optlen = ip6optlen(*tptrp, lim)) == 0) return(-1); *tptrp = *tptrp + optlen; } if (*tptrp >= lim) { /* there is no option */ *tptrp = NULL; return(-1); } /* * Finally, checks if the next option is safely stored in the * cmsg data. */ if (ip6optlen(*tptrp, lim) == 0) return(-1); else return(0); } /* * This function is similar to the inet6_option_next() function, * except this function lets the caller specify the option type to be * searched for, instead of always returning the next option in the * ancillary data object. * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think * it's a typo. The variable should be type of u_int8_t **. */ int inet6_option_find(const struct cmsghdr *cmsg, u_int8_t **tptrp, int type) { struct ip6_ext *ip6e; int hdrlen, optlen; u_int8_t *optp, *lim; if (cmsg->cmsg_level != IPPROTO_IPV6 || (!is_ipv6_hopopts(cmsg->cmsg_type) && !is_ipv6_dstopts(cmsg->cmsg_type))) return(-1); /* message length validation */ if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) return(-1); ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); hdrlen = (ip6e->ip6e_len + 1) << 3; if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) return(-1); /* * If the caller does not specify the starting point, * search from the beginning of the option list. * Otherwise, search from *the next option* of the specified point. */ lim = (u_int8_t *)ip6e + hdrlen; if (*tptrp == NULL) *tptrp = (u_int8_t *)(ip6e + 1); else { if ((optlen = ip6optlen(*tptrp, lim)) == 0) return(-1); *tptrp = *tptrp + optlen; } for (optp = *tptrp; optp < lim; optp += optlen) { if (*optp == type) { *tptrp = optp; return(0); } if ((optlen = ip6optlen(optp, lim)) == 0) return(-1); } /* search failed */ *tptrp = NULL; return(-1); } /* * Calculate the length of a given IPv6 option. Also checks * if the option is safely stored in user's buffer according to the * calculated length and the limitation of the buffer. */ static int ip6optlen(u_int8_t *opt, u_int8_t *lim) { int optlen; if (*opt == IP6OPT_PAD1) optlen = 1; else { /* is there enough space to store type and len? */ if (opt + 2 > lim) return(0); optlen = *(opt + 1) + 2; } if (opt + optlen <= lim) return(optlen); return(0); } static void inet6_insert_padopt(u_char *p, int len) { switch(len) { case 0: return; case 1: p[0] = IP6OPT_PAD1; return; default: p[0] = IP6OPT_PADN; p[1] = len - 2; memset(&p[2], 0, len - 2); return; } } /* * The following functions are defined in RFC3542, which is a successor * of RFC2292. */ int inet6_opt_init(void *extbuf, socklen_t extlen) { struct ip6_ext *ext = (struct ip6_ext *)extbuf; if (ext) { if (extlen <= 0 || (extlen % 8)) return(-1); ext->ip6e_len = (extlen >> 3) - 1; } return(2); /* sizeof the next and the length fields */ } int inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, socklen_t len, u_int8_t align, void **databufp) { int currentlen = offset, padlen = 0; /* * The option type must have a value from 2 to 255, inclusive. * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) */ if (type < 2) return(-1); /* * The option data length must have a value between 0 and 255, * inclusive, and is the length of the option data that follows. */ if (len > 255 || len < 0 ) return(-1); /* * The align parameter must have a value of 1, 2, 4, or 8. * The align value can not exceed the value of len. */ if (align != 1 && align != 2 && align != 4 && align != 8) return(-1); if (align > len) return(-1); /* Calculate the padding length. */ currentlen += 2 + len; /* 2 means "type + len" */ if (currentlen % align) padlen = align - (currentlen % align); /* The option must fit in the extension header buffer. */ currentlen += padlen; if (extlen && /* XXX: right? */ currentlen > extlen) return(-1); if (extbuf) { u_int8_t *optp = (u_int8_t *)extbuf + offset; if (padlen == 1) { /* insert a Pad1 option */ *optp = IP6OPT_PAD1; optp++; } else if (padlen > 0) { /* insert a PadN option for alignment */ *optp++ = IP6OPT_PADN; *optp++ = padlen - 2; memset(optp, 0, padlen - 2); optp += (padlen - 2); } *optp++ = type; *optp++ = len; *databufp = optp; } return(currentlen); } int inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) { int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0; if (extbuf) { u_int8_t *padp; int padlen = updatelen - offset; if (updatelen > extlen) return(-1); padp = (u_int8_t *)extbuf + offset; if (padlen == 1) *padp = IP6OPT_PAD1; else if (padlen > 0) { *padp++ = IP6OPT_PADN; *padp++ = (padlen - 2); memset(padp, 0, padlen - 2); } } return(updatelen); } int inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) { memcpy((u_int8_t *)databuf + offset, val, vallen); return(offset + vallen); } int inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, socklen_t *lenp, void **databufp) { u_int8_t *optp, *lim; int optlen; /* Validate extlen. XXX: is the variable really necessary?? */ if (extlen == 0 || (extlen % 8)) return(-1); lim = (u_int8_t *)extbuf + extlen; /* * If this is the first time this function called for this options * header, simply return the 1st option. * Otherwise, search the option list for the next option. */ if (offset == 0) { optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); } else optp = (u_int8_t *)extbuf + offset; /* Find the next option skipping any padding options. */ while(optp < lim) { switch(*optp) { case IP6OPT_PAD1: optp++; break; case IP6OPT_PADN: if ((optlen = ip6optlen(optp, lim)) == 0) goto optend; optp += optlen; break; default: /* found */ if ((optlen = ip6optlen(optp, lim)) == 0) goto optend; *typep = *optp; *lenp = optlen - 2; *databufp = optp + 2; return(optp + optlen - (u_int8_t *)extbuf); } } optend: *databufp = NULL; /* for safety */ return(-1); } int inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, socklen_t *lenp, void **databufp) { u_int8_t *optp, *lim; int optlen; /* Validate extlen. XXX: is the variable really necessary?? */ if (extlen == 0 || (extlen % 8)) return(-1); lim = (u_int8_t *)extbuf + extlen; /* * If this is the first time this function called for this options * header, simply return the 1st option. * Otherwise, search the option list for the next option. */ if (offset == 0) { optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); } else optp = (u_int8_t *)extbuf + offset; /* Find the specified option */ while(optp < lim) { if ((optlen = ip6optlen(optp, lim)) == 0) goto optend; if (*optp == type) { /* found */ *lenp = optlen - 2; *databufp = optp + 2; return(optp + optlen - (u_int8_t *)extbuf); } optp += optlen; } optend: *databufp = NULL; /* for safety */ return(-1); } int inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) { /* we can't assume alignment here */ memcpy(val, (u_int8_t *)databuf + offset, vallen); return(offset + vallen); } Index: head/lib/libc/net/map_v4v6.c =================================================================== --- head/lib/libc/net/map_v4v6.c (revision 298225) +++ head/lib/libc/net/map_v4v6.c (revision 298226) @@ -1,112 +1,111 @@ /* * ++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. * 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-- */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)gethostnamadr.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); -#include #include #include #include #include #include #include #include #include #include #include #include #include "netdb_private.h" typedef union { int32_t al; char ac; } align; void _map_v4v6_address(const char *src, char *dst) { /* Our caller may update in place. */ memmove(&dst[12], src, NS_INADDRSZ); /* Mark this ipv6 addr as a mapped ipv4. */ memset(&dst[10], 0xff, 2); memset(&dst[0], 0, 10); } void _map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep) { char **ap; if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ) return; hp->h_addrtype = AF_INET6; hp->h_length = IN6ADDRSZ; for (ap = hp->h_addr_list; *ap; ap++) { int i = (u_long)*bpp % sizeof(align); if (i != 0) i = sizeof(align) - i; if ((ep - *bpp) < (i + IN6ADDRSZ)) { /* Out of memory. Truncate address list here. */ *ap = NULL; return; } *bpp += i; _map_v4v6_address(*ap, *bpp); *ap = *bpp; *bpp += IN6ADDRSZ; } } Index: head/lib/libc/net/rthdr.c =================================================================== --- head/lib/libc/net/rthdr.c (revision 298225) +++ head/lib/libc/net/rthdr.c (revision 298226) @@ -1,435 +1,434 @@ /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 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. */ #include __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include #include /* * RFC2292 API */ size_t inet6_rthdr_space(int type, int seg) { switch (type) { case IPV6_RTHDR_TYPE_0: if (seg < 1 || seg > 23) return (0); #ifdef COMPAT_RFC2292 return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) + sizeof(struct ip6_rthdr0))); #else return (CMSG_SPACE(sizeof(struct in6_addr) * seg + sizeof(struct ip6_rthdr0))); #endif default: return (0); } } struct cmsghdr * inet6_rthdr_init(void *bp, int type) { struct cmsghdr *ch = (struct cmsghdr *)bp; struct ip6_rthdr *rthdr; rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); ch->cmsg_level = IPPROTO_IPV6; ch->cmsg_type = IPV6_RTHDR; switch (type) { case IPV6_RTHDR_TYPE_0: #ifdef COMPAT_RFC2292 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr)); #else ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); #endif bzero(rthdr, sizeof(struct ip6_rthdr0)); rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; return (ch); default: return (NULL); } } /* ARGSUSED */ int inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags) { struct ip6_rthdr *rthdr; rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); switch (rthdr->ip6r_type) { case IPV6_RTHDR_TYPE_0: { struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) return (-1); if (rt0->ip6r0_segleft == 23) return (-1); #ifdef COMPAT_RFC1883 /* XXX */ if (flags == IPV6_RTHDR_STRICT) { int c, b; c = rt0->ip6r0_segleft / 8; b = rt0->ip6r0_segleft % 8; rt0->ip6r0_slmap[c] |= (1 << (7 - b)); } #else if (flags != IPV6_RTHDR_LOOSE) return (-1); #endif rt0->ip6r0_segleft++; bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), sizeof(struct in6_addr)); rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); break; } default: return (-1); } return (0); } /* ARGSUSED */ int inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags) { struct ip6_rthdr *rthdr; rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); switch (rthdr->ip6r_type) { case IPV6_RTHDR_TYPE_0: { struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; #ifdef COMPAT_RFC1883 /* XXX */ if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) return (-1); #endif /* COMPAT_RFC1883 */ if (rt0->ip6r0_segleft > 23) return (-1); #ifdef COMPAT_RFC1883 /* XXX */ if (flags == IPV6_RTHDR_STRICT) { int c, b; c = rt0->ip6r0_segleft / 8; b = rt0->ip6r0_segleft % 8; rt0->ip6r0_slmap[c] |= (1 << (7 - b)); } #else if (flags != IPV6_RTHDR_LOOSE) return (-1); #endif /* COMPAT_RFC1883 */ break; } default: return (-1); } return (0); } #if 0 int inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out) { return (-1); } #endif int inet6_rthdr_segments(const struct cmsghdr *cmsg) { struct ip6_rthdr *rthdr; rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); switch (rthdr->ip6r_type) { case IPV6_RTHDR_TYPE_0: { struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) return (-1); return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); } default: return (-1); } } struct in6_addr * inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx) { struct ip6_rthdr *rthdr; rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); switch (rthdr->ip6r_type) { case IPV6_RTHDR_TYPE_0: { struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; int naddr; if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) return NULL; naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); if (idx <= 0 || naddr < idx) return NULL; #ifdef COMPAT_RFC2292 return (((struct in6_addr *)(rt0 + 1)) + idx - 1); #else return (((struct in6_addr *)(rt0 + 1)) + idx); #endif } default: return NULL; } } int inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx) { struct ip6_rthdr *rthdr; rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); switch (rthdr->ip6r_type) { case IPV6_RTHDR_TYPE_0: { struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; int naddr; if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) return (-1); naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); if (idx < 0 || naddr < idx) return (-1); #ifdef COMPAT_RFC1883 /* XXX */ if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8))) return IPV6_RTHDR_STRICT; else return IPV6_RTHDR_LOOSE; #else return IPV6_RTHDR_LOOSE; #endif /* COMPAT_RFC1883 */ } default: return (-1); } } /* * RFC3542 API */ socklen_t inet6_rth_space(int type, int segments) { switch (type) { case IPV6_RTHDR_TYPE_0: if ((segments >= 0) && (segments <= 127)) return (((segments * 2) + 1) << 3); /* FALLTHROUGH */ default: return (0); /* type not suppported */ } } void * inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) { struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; struct ip6_rthdr0 *rth0; switch (type) { case IPV6_RTHDR_TYPE_0: /* length validation */ if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) return (NULL); /* segment validation */ if ((segments < 0) || (segments > 127)) return (NULL); memset(bp, 0, bp_len); rth0 = (struct ip6_rthdr0 *)rth; rth0->ip6r0_len = segments * 2; rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; rth0->ip6r0_segleft = 0; rth0->ip6r0_reserved = 0; break; default: return (NULL); /* type not supported */ } return (bp); } int inet6_rth_add(void *bp, const struct in6_addr *addr) { struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; struct ip6_rthdr0 *rth0; struct in6_addr *nextaddr; switch (rth->ip6r_type) { case IPV6_RTHDR_TYPE_0: rth0 = (struct ip6_rthdr0 *)rth; /* Don't exceed the number of stated segments */ if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2)) return (-1); nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; *nextaddr = *addr; rth0->ip6r0_segleft++; break; default: return (-1); /* type not supported */ } return (0); } int inet6_rth_reverse(const void *in, void *out) { struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; struct ip6_rthdr0 *rth0_in, *rth0_out; int i, segments; switch (rth_in->ip6r_type) { case IPV6_RTHDR_TYPE_0: rth0_in = (struct ip6_rthdr0 *)in; rth0_out = (struct ip6_rthdr0 *)out; /* parameter validation XXX too paranoid? */ if (rth0_in->ip6r0_len % 2) return (-1); segments = rth0_in->ip6r0_len / 2; /* we can't use memcpy here, since in and out may overlap */ memmove((void *)rth0_out, (void *)rth0_in, ((rth0_in->ip6r0_len) + 1) << 3); rth0_out->ip6r0_segleft = segments; /* reverse the addresses */ for (i = 0; i < segments / 2; i++) { struct in6_addr addr_tmp, *addr1, *addr2; addr1 = (struct in6_addr *)(rth0_out + 1) + i; addr2 = (struct in6_addr *)(rth0_out + 1) + (segments - i - 1); addr_tmp = *addr1; *addr1 = *addr2; *addr2 = addr_tmp; } break; default: return (-1); /* type not supported */ } return (0); } int inet6_rth_segments(const void *bp) { struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; struct ip6_rthdr0 *rh0; int addrs; switch (rh->ip6r_type) { case IPV6_RTHDR_TYPE_0: rh0 = (struct ip6_rthdr0 *)bp; /* * Validation for a type-0 routing header. * Is this too strict? */ if ((rh0->ip6r0_len % 2) != 0 || (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) return (-1); return (addrs); default: return (-1); /* unknown type */ } } struct in6_addr * inet6_rth_getaddr(const void *bp, int idx) { struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; struct ip6_rthdr0 *rh0; int addrs; switch (rh->ip6r_type) { case IPV6_RTHDR_TYPE_0: rh0 = (struct ip6_rthdr0 *)bp; /* * Validation for a type-0 routing header. * Is this too strict? */ if ((rh0->ip6r0_len % 2) != 0 || (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) return (NULL); if (idx < 0 || addrs <= idx) return (NULL); return (((struct in6_addr *)(rh0 + 1)) + idx); default: return (NULL); /* unknown type */ break; } } Index: head/lib/libc/net/sourcefilter.c =================================================================== --- head/lib/libc/net/sourcefilter.c (revision 298225) +++ head/lib/libc/net/sourcefilter.c (revision 298226) @@ -1,403 +1,402 @@ /*- * Copyright (c) 2007-2009 Bruce Simpson. * 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 #include #include #include #include "un-namespace.h" /* * Advanced (Full-state) multicast group membership APIs [RFC3678] * Currently this module assumes IPv4 support (INET) in the base system. */ #ifndef INET #define INET #endif union sockunion { struct sockaddr_storage ss; struct sockaddr sa; struct sockaddr_dl sdl; #ifdef INET struct sockaddr_in sin; #endif #ifdef INET6 struct sockaddr_in6 sin6; #endif }; typedef union sockunion sockunion_t; #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /* * Internal: Map an IPv4 unicast address to an interface index. * This is quite inefficient so it is recommended applications use * the newer, more portable, protocol independent API. */ static uint32_t __inaddr_to_index(in_addr_t ifaddr) { struct ifaddrs *ifa; struct ifaddrs *ifaddrs; char *ifname; int ifindex; sockunion_t *psu; if (getifaddrs(&ifaddrs) < 0) return (0); ifindex = 0; ifname = NULL; /* * Pass #1: Find the ifaddr entry corresponding to the * supplied IPv4 address. We should really use the ifindex * consistently for matches, however it is not available to * us on this pass. */ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { psu = (sockunion_t *)ifa->ifa_addr; if (psu && psu->ss.ss_family == AF_INET && psu->sin.sin_addr.s_addr == ifaddr) { ifname = ifa->ifa_name; break; } } if (ifname == NULL) goto out; /* * Pass #2: Find the index of the interface matching the name * we obtained from looking up the IPv4 ifaddr in pass #1. * There must be a better way of doing this. */ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { psu = (sockunion_t *)ifa->ifa_addr; if (psu && psu->ss.ss_family == AF_LINK && strcmp(ifa->ifa_name, ifname) == 0) { ifindex = LLINDEX(&psu->sdl); break; } } assert(ifindex != 0); out: freeifaddrs(ifaddrs); return (ifindex); } /* * Set IPv4 source filter list in use on socket. * * Stubbed to setsourcefilter(). Performs conversion of structures which * may be inefficient; applications are encouraged to use the * protocol-independent API. */ int setipv4sourcefilter(int s, struct in_addr interface, struct in_addr group, uint32_t fmode, uint32_t numsrc, struct in_addr *slist) { #ifdef INET sockunion_t tmpgroup; struct in_addr *pina; sockunion_t *psu, *tmpslist; int err; size_t i; uint32_t ifindex; assert(s != -1); tmpslist = NULL; if (!IN_MULTICAST(ntohl(group.s_addr)) || (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE)) { errno = EINVAL; return (-1); } ifindex = __inaddr_to_index(interface.s_addr); if (ifindex == 0) { errno = EADDRNOTAVAIL; return (-1); } memset(&tmpgroup, 0, sizeof(sockunion_t)); tmpgroup.sin.sin_family = AF_INET; tmpgroup.sin.sin_len = sizeof(struct sockaddr_in); tmpgroup.sin.sin_addr = group; if (numsrc != 0 || slist != NULL) { tmpslist = calloc(numsrc, sizeof(sockunion_t)); if (tmpslist == NULL) { errno = ENOMEM; return (-1); } pina = slist; psu = tmpslist; for (i = 0; i < numsrc; i++, pina++, psu++) { psu->sin.sin_family = AF_INET; psu->sin.sin_len = sizeof(struct sockaddr_in); psu->sin.sin_addr = *pina; } } err = setsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup, sizeof(struct sockaddr_in), fmode, numsrc, (struct sockaddr_storage *)tmpslist); if (tmpslist != NULL) free(tmpslist); return (err); #else /* !INET */ return (EAFNOSUPPORT); #endif /* INET */ } /* * Get IPv4 source filter list in use on socket. * * Stubbed to getsourcefilter(). Performs conversion of structures which * may be inefficient; applications are encouraged to use the * protocol-independent API. * An slist of NULL may be used for guessing the required buffer size. */ int getipv4sourcefilter(int s, struct in_addr interface, struct in_addr group, uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist) { sockunion_t *psu, *tmpslist; sockunion_t tmpgroup; struct in_addr *pina; int err; size_t i; uint32_t ifindex, onumsrc; assert(s != -1); assert(fmode != NULL); assert(numsrc != NULL); onumsrc = *numsrc; *numsrc = 0; tmpslist = NULL; if (!IN_MULTICAST(ntohl(group.s_addr)) || (onumsrc != 0 && slist == NULL)) { errno = EINVAL; return (-1); } ifindex = __inaddr_to_index(interface.s_addr); if (ifindex == 0) { errno = EADDRNOTAVAIL; return (-1); } memset(&tmpgroup, 0, sizeof(sockunion_t)); tmpgroup.sin.sin_family = AF_INET; tmpgroup.sin.sin_len = sizeof(struct sockaddr_in); tmpgroup.sin.sin_addr = group; if (onumsrc != 0 || slist != NULL) { tmpslist = calloc(onumsrc, sizeof(sockunion_t)); if (tmpslist == NULL) { errno = ENOMEM; return (-1); } } err = getsourcefilter(s, ifindex, (struct sockaddr *)&tmpgroup, sizeof(struct sockaddr_in), fmode, numsrc, (struct sockaddr_storage *)tmpslist); if (tmpslist != NULL && *numsrc != 0) { pina = slist; psu = tmpslist; for (i = 0; i < MIN(onumsrc, *numsrc); i++, psu++) { if (psu->ss.ss_family != AF_INET) continue; *pina++ = psu->sin.sin_addr; } free(tmpslist); } return (err); } /* * Set protocol-independent source filter list in use on socket. */ int setsourcefilter(int s, uint32_t interface, struct sockaddr *group, socklen_t grouplen, uint32_t fmode, uint32_t numsrc, struct sockaddr_storage *slist) { struct __msfilterreq msfr; sockunion_t *psu; int level, optname; if (fmode != MCAST_INCLUDE && fmode != MCAST_EXCLUDE) { errno = EINVAL; return (-1); } psu = (sockunion_t *)group; switch (psu->ss.ss_family) { #ifdef INET case AF_INET: if ((grouplen != sizeof(struct sockaddr_in) || !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) { errno = EINVAL; return (-1); } level = IPPROTO_IP; optname = IP_MSFILTER; break; #endif #ifdef INET6 case AF_INET6: if (grouplen != sizeof(struct sockaddr_in6) || !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) { errno = EINVAL; return (-1); } level = IPPROTO_IPV6; optname = IPV6_MSFILTER; break; #endif default: errno = EAFNOSUPPORT; return (-1); } memset(&msfr, 0, sizeof(msfr)); msfr.msfr_ifindex = interface; msfr.msfr_fmode = fmode; msfr.msfr_nsrcs = numsrc; memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len); msfr.msfr_srcs = slist; /* pointer */ return (_setsockopt(s, level, optname, &msfr, sizeof(msfr))); } /* * Get protocol-independent source filter list in use on socket. * An slist of NULL may be used for guessing the required buffer size. */ int getsourcefilter(int s, uint32_t interface, struct sockaddr *group, socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc, struct sockaddr_storage *slist) { struct __msfilterreq msfr; sockunion_t *psu; socklen_t optlen; int err, level, nsrcs, optname; if (interface == 0 || group == NULL || numsrc == NULL || fmode == NULL) { errno = EINVAL; return (-1); } nsrcs = *numsrc; *numsrc = 0; *fmode = 0; psu = (sockunion_t *)group; switch (psu->ss.ss_family) { #ifdef INET case AF_INET: if ((grouplen != sizeof(struct sockaddr_in) || !IN_MULTICAST(ntohl(psu->sin.sin_addr.s_addr)))) { errno = EINVAL; return (-1); } level = IPPROTO_IP; optname = IP_MSFILTER; break; #endif #ifdef INET6 case AF_INET6: if (grouplen != sizeof(struct sockaddr_in6) || !IN6_IS_ADDR_MULTICAST(&psu->sin6.sin6_addr)) { errno = EINVAL; return (-1); } level = IPPROTO_IPV6; optname = IPV6_MSFILTER; break; #endif default: errno = EAFNOSUPPORT; return (-1); break; } optlen = sizeof(struct __msfilterreq); memset(&msfr, 0, optlen); msfr.msfr_ifindex = interface; msfr.msfr_fmode = 0; msfr.msfr_nsrcs = nsrcs; memcpy(&msfr.msfr_group, &psu->ss, psu->ss.ss_len); /* * msfr_srcs is a pointer to a vector of sockaddr_storage. It * may be NULL. The kernel will always return the total number * of filter entries for the group in msfr.msfr_nsrcs. */ msfr.msfr_srcs = slist; err = _getsockopt(s, level, optname, &msfr, &optlen); if (err == 0) { *numsrc = msfr.msfr_nsrcs; *fmode = msfr.msfr_fmode; } return (err); } Index: head/lib/libc/resolv/herror.c =================================================================== --- head/lib/libc/resolv/herror.c (revision 298225) +++ head/lib/libc/resolv/herror.c (revision 298226) @@ -1,126 +1,125 @@ /* * Copyright (c) 1987, 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. */ /* * 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[] = "@(#)herror.c 8.1 (Berkeley) 6/4/93"; static const char rcsid[] = "$Id: herror.c,v 1.4 2005/04/27 04:56:41 sra Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "port_before.h" #include "namespace.h" -#include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "port_after.h" const char *h_errlist[] = { "Resolver Error 0 (no error)", "Unknown host", /*%< 1 HOST_NOT_FOUND */ "Host name lookup failure", /*%< 2 TRY_AGAIN */ "Unknown server error", /*%< 3 NO_RECOVERY */ "No address associated with name", /*%< 4 NO_ADDRESS */ }; const int h_nerr = { nitems(h_errlist) }; #undef h_errno int h_errno; /*% * herror -- * print the error indicated by the h_errno value. */ void herror(const char *s) { struct iovec iov[4], *v = iov; char *t; if (s != NULL && *s != '\0') { DE_CONST(s, t); v->iov_base = t; v->iov_len = strlen(t); v++; DE_CONST(": ", t); v->iov_base = t; v->iov_len = 2; v++; } DE_CONST(hstrerror(*__h_errno()), t); v->iov_base = t; v->iov_len = strlen(v->iov_base); v++; DE_CONST("\n", t); v->iov_base = t; v->iov_len = 1; _writev(STDERR_FILENO, iov, (v - iov) + 1); } /*% * hstrerror -- * return the string associated with a given "host" errno value. */ const char * hstrerror(int err) { if (err < 0) return ("Resolver internal error"); else if (err < h_nerr) return (h_errlist[err]); return ("Unknown resolver error"); } /*! \file */ Index: head/lib/libc/resolv/res_comp.c =================================================================== --- head/lib/libc/resolv/res_comp.c (revision 298225) +++ head/lib/libc/resolv/res_comp.c (revision 298226) @@ -1,277 +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 #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 '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