diff options
Diffstat (limited to 'lib/libc/nameser')
-rw-r--r-- | lib/libc/nameser/ns_name.c | 963 | ||||
-rw-r--r-- | lib/libc/nameser/ns_netint.c | 56 | ||||
-rw-r--r-- | lib/libc/nameser/ns_parse.c | 209 | ||||
-rw-r--r-- | lib/libc/nameser/ns_print.c | 898 | ||||
-rw-r--r-- | lib/libc/nameser/ns_samedomain.c | 206 | ||||
-rw-r--r-- | lib/libc/nameser/ns_ttl.c | 160 |
6 files changed, 2492 insertions, 0 deletions
diff --git a/lib/libc/nameser/ns_name.c b/lib/libc/nameser/ns_name.c new file mode 100644 index 0000000..5ac91e3 --- /dev/null +++ b/lib/libc/nameser/ns_name.c @@ -0,0 +1,963 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_name.c,v 1.3.2.4.4.2 2004/05/04 03:27:47 marka Exp $"; +#endif + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <limits.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +#define NS_TYPE_ELT 0x40 /* EDNS0 extended label type */ +#define DNS_LABELTYPE_BITSTRING 0x41 + +/* Data. */ + +static const char digits[] = "0123456789"; + +static const char digitvalue[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ +}; + +/* Forward. */ + +static int special(int); +static int printable(int); +static int dn_find(const u_char *, const u_char *, + const u_char * const *, + const u_char * const *); +static int encode_bitsring(const char **, const char *, + unsigned char **, unsigned char **, + unsigned const char *); +static int labellen(const u_char *); +static int decode_bitstring(const unsigned char **, + char *, const char *); + +/* Public. */ + +/* + * ns_name_ntop(src, dst, dstsiz) + * Convert an encoded domain name to printable ascii as per RFC1035. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * The root is returned as "." + * All other domains are returned in non absolute form + */ +int +ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) +{ + const u_char *cp; + char *dn, *eom; + u_char c; + u_int n; + int l; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + if (dn != dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; /* XXX */ + return(-1); + } + if (dn + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) { + int m; + + if (n != DNS_LABELTYPE_BITSTRING) { + /* XXX: labellen should reject this case */ + errno = EINVAL; + return(-1); + } + if ((m = decode_bitstring(&cp, dn, eom)) < 0) + { + errno = EMSGSIZE; + return(-1); + } + dn += m; + continue; + } + for ((void)NULL; l > 0; l--) { + c = *cp++; + if (special(c)) { + if (dn + 1 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = (char)c; + } else if (!printable(c)) { + if (dn + 3 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = digits[c / 100]; + *dn++ = digits[(c % 100) / 10]; + *dn++ = digits[c % 10]; + } else { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = (char)c; + } + } + } + if (dn == dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\0'; + return (dn - dst); +} + +/* + * ns_name_pton(src, dst, dstsiz) + * Convert a ascii string into an encoded domain name as per RFC1035. + * return: + * -1 if it fails + * 1 if string was fully qualified + * 0 is string was not fully qualified + * notes: + * Enforces label and domain length limits. + */ + +int +ns_name_pton(const char *src, u_char *dst, size_t dstsiz) +{ + u_char *label, *bp, *eom; + int c, n, escaped, e = 0; + char *cp; + + escaped = 0; + bp = dst; + eom = dst + dstsiz; + label = bp++; + + while ((c = *src++) != 0) { + if (escaped) { + if (c == '[') { /* start a bit string label */ + if ((cp = strchr(src, ']')) == NULL) { + errno = EINVAL; /* ??? */ + return(-1); + } + if ((e = encode_bitsring(&src, cp + 2, + &label, &bp, eom)) + != 0) { + errno = e; + return(-1); + } + escaped = 0; + label = bp++; + if ((c = *src++) == 0) + goto done; + else if (c != '.') { + errno = EINVAL; + return(-1); + } + continue; + } + else if ((cp = strchr(digits, c)) != NULL) { + n = (cp - digits) * 100; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits) * 10; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits); + if (n > 255) { + errno = EMSGSIZE; + return (-1); + } + c = n; + } + escaped = 0; + } else if (c == '\\') { + escaped = 1; + continue; + } else if (c == '.') { + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + /* Fully qualified ? */ + if (*src == '\0') { + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = '\0'; + } + if ((bp - dst) > MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + return (1); + } + if (c == 0 || *src == '.') { + errno = EMSGSIZE; + return (-1); + } + label = bp++; + continue; + } + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = (u_char)c; + } + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + done: + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = 0; + } + if ((bp - dst) > MAXCDNAME) { /* src too big */ + errno = EMSGSIZE; + return (-1); + } + return (0); +} + +/* + * ns_name_ntol(src, dst, dstsiz) + * Convert a network strings labels into all lowercase. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * Enforces label and domain length limits. + */ + +int +ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) +{ + const u_char *cp; + u_char *dn, *eom; + u_char c; + u_int n; + int l; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + *dn++ = n; + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; + return (-1); + } + if (dn + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + for ((void)NULL; l > 0; l--) { + c = *cp++; + if (isupper(c)) + *dn++ = tolower(c); + else + *dn++ = c; + } + } + *dn++ = '\0'; + return (dn - dst); +} + +/* + * ns_name_unpack(msg, eom, src, dst, dstsiz) + * Unpack a domain name from a message, source may be compressed. + * return: + * -1 if it fails, or consumed octets if it succeeds. + */ +int +ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, + u_char *dst, size_t dstsiz) +{ + const u_char *srcp, *dstlim; + u_char *dstp; + int n, len, checked, l; + + len = -1; + checked = 0; + dstp = dst; + srcp = src; + dstlim = dst + dstsiz; + if (srcp < msg || srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + /* Fetch next label in domain name. */ + while ((n = *srcp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: + case NS_TYPE_ELT: + /* Limit checks. */ + if ((l = labellen(srcp - 1)) < 0) { + errno = EMSGSIZE; + return(-1); + } + if (dstp + l + 1 >= dstlim || srcp + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + checked += l + 1; + *dstp++ = n; + memcpy(dstp, srcp, l); + dstp += l; + srcp += l; + break; + + case NS_CMPRSFLGS: + if (srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + if (len < 0) + len = srcp - src + 1; + srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); + if (srcp < msg || srcp >= eom) { /* Out of range. */ + errno = EMSGSIZE; + return (-1); + } + checked += 2; + /* + * Check for loops in the compressed name; + * if we've looked at the whole message, + * there must be a loop. + */ + if (checked >= eom - msg) { + errno = EMSGSIZE; + return (-1); + } + break; + + default: + errno = EMSGSIZE; + return (-1); /* flag error */ + } + } + *dstp = '\0'; + if (len < 0) + len = srcp - src; + return (len); +} + +/* + * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) + * Pack domain name 'domain' into 'comp_dn'. + * return: + * Size of the compressed name, or -1. + * notes: + * 'dnptrs' is an array of pointers to previous compressed names. + * dnptrs[0] is a pointer to the beginning of the message. The array + * ends with NULL. + * 'lastdnptr' is a pointer to the end of the array pointed to + * by 'dnptrs'. + * Side effects: + * The list of pointers in dnptrs is updated for labels inserted into + * the message as we compress the name. If 'dnptr' is NULL, we don't + * try to compress names. If 'lastdnptr' is NULL, we don't update the + * list. + */ +int +ns_name_pack(const u_char *src, u_char *dst, int dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char *dstp; + const u_char **cpp, **lpp, *eob, *msg; + const u_char *srcp; + int n, l, first = 1; + + srcp = src; + dstp = dst; + eob = dstp + dstsiz; + lpp = cpp = NULL; + if (dnptrs != NULL) { + if ((msg = *dnptrs++) != NULL) { + for (cpp = dnptrs; *cpp != NULL; cpp++) + (void)NULL; + lpp = cpp; /* end of list to search */ + } + } else + msg = NULL; + + /* make sure the domain we are about to add is legal */ + l = 0; + do { + int l0; + + n = *srcp; + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + errno = EMSGSIZE; + return (-1); + } + if ((l0 = labellen(srcp)) < 0) { + errno = EINVAL; + return(-1); + } + l += l0 + 1; + if (l > MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + srcp += l0 + 1; + } while (n != 0); + + /* from here on we need to reset compression pointer array on error */ + srcp = src; + do { + /* Look to see if we can use pointers. */ + n = *srcp; + if (n != 0 && msg != NULL) { + l = dn_find(srcp, msg, (const u_char * const *)dnptrs, + (const u_char * const *)lpp); + if (l >= 0) { + if (dstp + 1 >= eob) { + goto cleanup; + } + *dstp++ = (l >> 8) | NS_CMPRSFLGS; + *dstp++ = l % 256; + return (dstp - dst); + } + /* Not found, save it. */ + if (lastdnptr != NULL && cpp < lastdnptr - 1 && + (dstp - msg) < 0x4000 && first) { + *cpp++ = dstp; + *cpp = NULL; + first = 0; + } + } + /* copy label to buffer */ + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Should not happen. */ + goto cleanup; + } + n = labellen(srcp); + if (dstp + 1 + n >= eob) { + goto cleanup; + } + memcpy(dstp, srcp, n + 1); + srcp += n + 1; + dstp += n + 1; + } while (n != 0); + + if (dstp > eob) { +cleanup: + if (msg != NULL) + *lpp = NULL; + errno = EMSGSIZE; + return (-1); + } + return (dstp - dst); +} + +/* + * ns_name_uncompress(msg, eom, src, dst, dstsiz) + * Expand compressed domain name to presentation format. + * return: + * Number of bytes read out of `src', or -1 (with errno set). + * note: + * Root domain returns as "." not "". + */ +int +ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, size_t dstsiz) +{ + u_char tmp[NS_MAXCDNAME]; + int n; + + if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) + return (-1); + if (ns_name_ntop(tmp, dst, dstsiz) == -1) + return (-1); + return (n); +} + +/* + * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr) + * Compress a domain name into wire format, using compression pointers. + * return: + * Number of bytes consumed in `dst' or -1 (with errno set). + * notes: + * 'dnptrs' is an array of pointers to previous compressed names. + * dnptrs[0] is a pointer to the beginning of the message. + * The list ends with NULL. 'lastdnptr' is a pointer to the end of the + * array pointed to by 'dnptrs'. Side effect is to update the list of + * pointers for labels inserted into the message as we compress the name. + * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' + * is NULL, we don't update the list. + */ +int +ns_name_compress(const char *src, u_char *dst, size_t dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char tmp[NS_MAXCDNAME]; + + if (ns_name_pton(src, tmp, sizeof tmp) == -1) + return (-1); + return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); +} + +/* + * Reset dnptrs so that there are no active references to pointers at or + * after src. + */ +void +ns_name_rollback(const u_char *src, const u_char **dnptrs, + const u_char **lastdnptr) +{ + while (dnptrs < lastdnptr && *dnptrs != NULL) { + if (*dnptrs >= src) { + *dnptrs = NULL; + break; + } + dnptrs++; + } +} + +/* + * ns_name_skip(ptrptr, eom) + * Advance *ptrptr to skip over the compressed name it points at. + * return: + * 0 on success, -1 (with errno set) on failure. + */ +int +ns_name_skip(const u_char **ptrptr, const u_char *eom) +{ + const u_char *cp; + u_int n; + int l; + + cp = *ptrptr; + while (cp < eom && (n = *cp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + cp += n; + continue; + case NS_TYPE_ELT: /* EDNS0 extended label */ + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; /* XXX */ + return(-1); + } + cp += l; + continue; + case NS_CMPRSFLGS: /* indirection */ + cp++; + break; + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } + break; + } + if (cp > eom) { + errno = EMSGSIZE; + return (-1); + } + *ptrptr = cp; + return (0); +} + +/* Private. */ + +/* + * special(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this characted special ("in need of quoting") ? + * return: + * boolean. + */ +static int +special(int ch) { + switch (ch) { + case 0x22: /* '"' */ + case 0x2E: /* '.' */ + case 0x3B: /* ';' */ + case 0x5C: /* '\\' */ + case 0x28: /* '(' */ + case 0x29: /* ')' */ + /* Special modifiers in zone files. */ + case 0x40: /* '@' */ + case 0x24: /* '$' */ + return (1); + default: + return (0); + } +} + +/* + * printable(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this character visible and not a space when printed ? + * return: + * boolean. + */ +static int +printable(int ch) { + return (ch > 0x20 && ch < 0x7f); +} + +/* + * Thinking in noninternationalized USASCII (per the DNS spec), + * convert this character to lower case if it's upper case. + */ +static int +mklower(int ch) { + if (ch >= 0x41 && ch <= 0x5A) + return (ch + 0x20); + return (ch); +} + +/* + * dn_find(domain, msg, dnptrs, lastdnptr) + * Search for the counted-label name in an array of compressed names. + * return: + * offset from msg if found, or -1. + * notes: + * dnptrs is the pointer to the first name on the list, + * not the pointer to the start of the message. + */ +static int +dn_find(const u_char *domain, const u_char *msg, + const u_char * const *dnptrs, + const u_char * const *lastdnptr) +{ + const u_char *dn, *cp, *sp; + const u_char * const *cpp; + u_int n; + + for (cpp = dnptrs; cpp < lastdnptr; cpp++) { + sp = *cpp; + /* + * terminate search on: + * root label + * compression pointer + * unusable offset + */ + while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && + (sp - msg) < 0x4000) { + dn = domain; + cp = sp; + while ((n = *cp++) != 0) { + /* + * check for indirection + */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + n = labellen(cp - 1); /* XXX */ + + if (n != *dn++) + goto next; + + for ((void)NULL; n > 0; n--) + if (mklower(*dn++) != + mklower(*cp++)) + goto next; + /* Is next root for both ? */ + if (*dn == '\0' && *cp == '\0') + return (sp - msg); + if (*dn) + continue; + goto next; + case NS_CMPRSFLGS: /* indirection */ + cp = msg + (((n & 0x3f) << 8) | *cp); + break; + + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } + } + next: ; + sp += *sp + 1; + } + } + errno = ENOENT; + return (-1); +} + +static int +decode_bitstring(const unsigned char **cpp, char *dn, const char *eom) +{ + const unsigned char *cp = *cpp; + char *beg = dn, tc; + int b, blen, plen, i; + + if ((blen = (*cp & 0xff)) == 0) + blen = 256; + plen = (blen + 3) / 4; + plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1); + if (dn + plen >= eom) + return(-1); + + cp++; + i = SPRINTF((dn, "\\[x")); + if (i < 0) + return (-1); + dn += i; + for (b = blen; b > 7; b -= 8, cp++) { + i = SPRINTF((dn, "%02x", *cp & 0xff)); + if (i < 0) + return (-1); + dn += i; + } + if (b > 4) { + tc = *cp++; + i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b)))); + if (i < 0) + return (-1); + dn += i; + } else if (b > 0) { + tc = *cp++; + i = SPRINTF((dn, "%1x", + ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); + if (i < 0) + return (-1); + dn += i; + } + i = SPRINTF((dn, "/%d]", blen)); + if (i < 0) + return (-1); + dn += i; + + *cpp = cp; + return(dn - beg); +} + +static int +encode_bitsring(const char **bp, const char *end, unsigned char **labelp, + unsigned char ** dst, unsigned const char *eom) +{ + int afterslash = 0; + const char *cp = *bp; + unsigned char *tp; + char c; + const char *beg_blen; + char *end_blen = NULL; + int value = 0, count = 0, tbcount = 0, blen = 0; + + beg_blen = end_blen = NULL; + + /* a bitstring must contain at least 2 characters */ + if (end - cp < 2) + return(EINVAL); + + /* XXX: currently, only hex strings are supported */ + if (*cp++ != 'x') + return(EINVAL); + if (!isxdigit((*cp) & 0xff)) /* reject '\[x/BLEN]' */ + return(EINVAL); + + for (tp = *dst + 1; cp < end && tp < eom; cp++) { + switch((c = *cp)) { + case ']': /* end of the bitstring */ + if (afterslash) { + if (beg_blen == NULL) + return(EINVAL); + blen = (int)strtol(beg_blen, &end_blen, 10); + if (*end_blen != ']') + return(EINVAL); + } + if (count) + *tp++ = ((value << 4) & 0xff); + cp++; /* skip ']' */ + goto done; + case '/': + afterslash = 1; + break; + default: + if (afterslash) { + if (!isdigit(c&0xff)) + return(EINVAL); + if (beg_blen == NULL) { + + if (c == '0') { + /* blen never begings with 0 */ + return(EINVAL); + } + beg_blen = cp; + } + } else { + if (!isxdigit(c&0xff)) + return(EINVAL); + value <<= 4; + value += digitvalue[(int)c]; + count += 4; + tbcount += 4; + if (tbcount > 256) + return(EINVAL); + if (count == 8) { + *tp++ = value; + count = 0; + } + } + break; + } + } + done: + if (cp >= end || tp >= eom) + return(EMSGSIZE); + + /* + * bit length validation: + * If a <length> is present, the number of digits in the <bit-data> + * MUST be just sufficient to contain the number of bits specified + * by the <length>. If there are insignificant bits in a final + * hexadecimal or octal digit, they MUST be zero. + * RFC 2673, Section 3.2. + */ + if (blen > 0) { + int traillen; + + if (((blen + 3) & ~3) != tbcount) + return(EINVAL); + traillen = tbcount - blen; /* between 0 and 3 */ + if (((value << (8 - traillen)) & 0xff) != 0) + return(EINVAL); + } + else + blen = tbcount; + if (blen == 256) + blen = 0; + + /* encode the type and the significant bit fields */ + **labelp = DNS_LABELTYPE_BITSTRING; + **dst = blen; + + *bp = cp; + *dst = tp; + + return(0); +} + +static int +labellen(const u_char *lp) +{ + int bitlen; + u_char l = *lp; + + if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* should be avoided by the caller */ + return(-1); + } + + if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) { + if (l == DNS_LABELTYPE_BITSTRING) { + if ((bitlen = *(lp + 1)) == 0) + bitlen = 256; + return((bitlen + 7 ) / 8 + 1); + } + return(-1); /* unknwon ELT */ + } + return(l); +} diff --git a/lib/libc/nameser/ns_netint.c b/lib/libc/nameser/ns_netint.c new file mode 100644 index 0000000..15fc93e --- /dev/null +++ b/lib/libc/nameser/ns_netint.c @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_netint.c,v 1.1.206.1 2004/03/09 08:33:44 marka Exp $"; +#endif + +/* Import. */ + +#include "port_before.h" + +#include <arpa/nameser.h> + +#include "port_after.h" + +/* Public. */ + +u_int +ns_get16(const u_char *src) { + u_int dst; + + NS_GET16(dst, src); + return (dst); +} + +u_long +ns_get32(const u_char *src) { + u_long dst; + + NS_GET32(dst, src); + return (dst); +} + +void +ns_put16(u_int src, u_char *dst) { + NS_PUT16(src, dst); +} + +void +ns_put32(u_long src, u_char *dst) { + NS_PUT32(src, dst); +} diff --git a/lib/libc/nameser/ns_parse.c b/lib/libc/nameser/ns_parse.c new file mode 100644 index 0000000..19a6f51 --- /dev/null +++ b/lib/libc/nameser/ns_parse.c @@ -0,0 +1,209 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_parse.c,v 1.3.2.1.4.3 2005/10/11 00:48:16 marka Exp $"; +#endif + +/* Import. */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv.h> +#include <string.h> + +#include "port_after.h" + +/* Forward. */ + +static void setsection(ns_msg *msg, ns_sect sect); + +/* Macros. */ + +#ifndef SOLARIS2 +#define RETERR(err) do { errno = (err); return (-1); } while (0) +#else +#define RETERR(err) \ + do { errno = (err); if (errno == errno) return (-1); } while (0) +#endif + +/* Public. */ + +/* These need to be in the same order as the nres.h:ns_flag enum. */ +struct _ns_flagdata _ns_flagdata[16] = { + { 0x8000, 15 }, /* qr. */ + { 0x7800, 11 }, /* opcode. */ + { 0x0400, 10 }, /* aa. */ + { 0x0200, 9 }, /* tc. */ + { 0x0100, 8 }, /* rd. */ + { 0x0080, 7 }, /* ra. */ + { 0x0040, 6 }, /* z. */ + { 0x0020, 5 }, /* ad. */ + { 0x0010, 4 }, /* cd. */ + { 0x000f, 0 }, /* rcode. */ + { 0x0000, 0 }, /* expansion (1/6). */ + { 0x0000, 0 }, /* expansion (2/6). */ + { 0x0000, 0 }, /* expansion (3/6). */ + { 0x0000, 0 }, /* expansion (4/6). */ + { 0x0000, 0 }, /* expansion (5/6). */ + { 0x0000, 0 }, /* expansion (6/6). */ +}; + +int ns_msg_getflag(ns_msg handle, int flag) { + return(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift); +} + +int +ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count) { + const u_char *optr = ptr; + + for ((void)NULL; count > 0; count--) { + int b, rdlength; + + b = dn_skipname(ptr, eom); + if (b < 0) + RETERR(EMSGSIZE); + ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/; + if (section != ns_s_qd) { + if (ptr + NS_INT32SZ + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + ptr += NS_INT32SZ/*TTL*/; + NS_GET16(rdlength, ptr); + ptr += rdlength/*RData*/; + } + } + if (ptr > eom) + RETERR(EMSGSIZE); + return (ptr - optr); +} + +int +ns_initparse(const u_char *msg, int msglen, ns_msg *handle) { + const u_char *eom = msg + msglen; + int i; + + memset(handle, 0x5e, sizeof *handle); + handle->_msg = msg; + handle->_eom = eom; + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_id, msg); + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_flags, msg); + for (i = 0; i < ns_s_max; i++) { + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_counts[i], msg); + } + for (i = 0; i < ns_s_max; i++) + if (handle->_counts[i] == 0) + handle->_sections[i] = NULL; + else { + int b = ns_skiprr(msg, eom, (ns_sect)i, + handle->_counts[i]); + + if (b < 0) + return (-1); + handle->_sections[i] = msg; + msg += b; + } + if (msg != eom) + RETERR(EMSGSIZE); + setsection(handle, ns_s_max); + return (0); +} + +int +ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) { + int b; + int tmp; + + /* Make section right. */ + tmp = section; + if (tmp < 0 || section >= ns_s_max) + RETERR(ENODEV); + if (section != handle->_sect) + setsection(handle, section); + + /* Make rrnum right. */ + if (rrnum == -1) + rrnum = handle->_rrnum; + if (rrnum < 0 || rrnum >= handle->_counts[(int)section]) + RETERR(ENODEV); + if (rrnum < handle->_rrnum) + setsection(handle, section); + if (rrnum > handle->_rrnum) { + b = ns_skiprr(handle->_msg_ptr, handle->_eom, section, + rrnum - handle->_rrnum); + + if (b < 0) + return (-1); + handle->_msg_ptr += b; + handle->_rrnum = rrnum; + } + + /* Do the parse. */ + b = dn_expand(handle->_msg, handle->_eom, + handle->_msg_ptr, rr->name, NS_MAXDNAME); + if (b < 0) + return (-1); + handle->_msg_ptr += b; + if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET16(rr->type, handle->_msg_ptr); + NS_GET16(rr->rr_class, handle->_msg_ptr); + if (section == ns_s_qd) { + rr->ttl = 0; + rr->rdlength = 0; + rr->rdata = NULL; + } else { + if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET32(rr->ttl, handle->_msg_ptr); + NS_GET16(rr->rdlength, handle->_msg_ptr); + if (handle->_msg_ptr + rr->rdlength > handle->_eom) + RETERR(EMSGSIZE); + rr->rdata = handle->_msg_ptr; + handle->_msg_ptr += rr->rdlength; + } + if (++handle->_rrnum > handle->_counts[(int)section]) + setsection(handle, (ns_sect)((int)section + 1)); + + /* All done. */ + return (0); +} + +/* Private. */ + +static void +setsection(ns_msg *msg, ns_sect sect) { + msg->_sect = sect; + if (sect == ns_s_max) { + msg->_rrnum = -1; + msg->_msg_ptr = NULL; + } else { + msg->_rrnum = 0; + msg->_msg_ptr = msg->_sections[(int)sect]; + } +} diff --git a/lib/libc/nameser/ns_print.c b/lib/libc/nameser/ns_print.c new file mode 100644 index 0000000..cb61cb1 --- /dev/null +++ b/lib/libc/nameser/ns_print.c @@ -0,0 +1,898 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_print.c,v 1.3.2.1.4.7 2004/09/16 07:01:12 marka Exp $"; +#endif + +/* Import. */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <isc/assertions.h> +#include <isc/dst.h> +#include <errno.h> +#include <resolv.h> +#include <string.h> +#include <ctype.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static size_t prune_origin(const char *name, const char *origin); +static int charstr(const u_char *rdata, const u_char *edata, + char **buf, size_t *buflen); +static int addname(const u_char *msg, size_t msglen, + const u_char **p, const char *origin, + char **buf, size_t *buflen); +static void addlen(size_t len, char **buf, size_t *buflen); +static int addstr(const char *src, size_t len, + char **buf, size_t *buflen); +static int addtab(size_t len, size_t target, int spaced, + char **buf, size_t *buflen); + +/* Macros. */ + +#define T(x) \ + do { \ + if ((x) < 0) \ + return (-1); \ + } while (0) + +/* Public. */ + +/* + * int + * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen) + * Convert an RR to presentation format. + * return: + * Number of characters written to buf, or -1 (check errno). + */ +int +ns_sprintrr(const ns_msg *handle, const ns_rr *rr, + const char *name_ctx, const char *origin, + char *buf, size_t buflen) +{ + int n; + + n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle), + ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr), + ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr), + name_ctx, origin, buf, buflen); + return (n); +} + +/* + * int + * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen, + * name_ctx, origin, buf, buflen) + * Convert the fields of an RR into presentation format. + * return: + * Number of characters written to buf, or -1 (check errno). + */ +int +ns_sprintrrf(const u_char *msg, size_t msglen, + const char *name, ns_class class, ns_type type, + u_long ttl, const u_char *rdata, size_t rdlen, + const char *name_ctx, const char *origin, + char *buf, size_t buflen) +{ + const char *obuf = buf; + const u_char *edata = rdata + rdlen; + int spaced = 0; + + const char *comment; + char tmp[100]; + int len, x; + + /* + * Owner. + */ + if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) { + T(addstr("\t\t\t", 3, &buf, &buflen)); + } else { + len = prune_origin(name, origin); + if (*name == '\0') { + goto root; + } else if (len == 0) { + T(addstr("@\t\t\t", 4, &buf, &buflen)); + } else { + T(addstr(name, len, &buf, &buflen)); + /* Origin not used or not root, and no trailing dot? */ + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + name[len] == '\0')) && name[len - 1] != '.') { + root: + T(addstr(".", 1, &buf, &buflen)); + len++; + } + T(spaced = addtab(len, 24, spaced, &buf, &buflen)); + } + } + + /* + * TTL, Class, Type. + */ + T(x = ns_format_ttl(ttl, buf, buflen)); + addlen(x, &buf, &buflen); + len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type))); + T(addstr(tmp, len, &buf, &buflen)); + T(spaced = addtab(x + len, 16, spaced, &buf, &buflen)); + + /* + * RData. + */ + switch (type) { + case ns_t_a: + if (rdlen != (size_t)NS_INADDRSZ) + goto formerr; + (void) inet_ntop(AF_INET, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + break; + + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ns: + case ns_t_ptr: + case ns_t_dname: + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + + case ns_t_hinfo: + case ns_t_isdn: + /* First word. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + + /* Second word, optional in ISDN records. */ + if (type == ns_t_isdn && rdata == edata) + break; + + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + break; + + case ns_t_soa: { + u_long t; + + /* Server name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Administrator name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" (\n", 3, &buf, &buflen)); + spaced = 0; + + if ((edata - rdata) != 5*NS_INT32SZ) + goto formerr; + + /* Serial number. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + len = SPRINTF((tmp, "%lu", t)); + T(addstr(tmp, len, &buf, &buflen)); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; serial\n", 9, &buf, &buflen)); + spaced = 0; + + /* Refresh interval. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; refresh\n", 10, &buf, &buflen)); + spaced = 0; + + /* Retry interval. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; retry\n", 8, &buf, &buflen)); + spaced = 0; + + /* Expiry. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; expiry\n", 9, &buf, &buflen)); + spaced = 0; + + /* Minimum TTL. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(addstr(" )", 2, &buf, &buflen)); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; minimum\n", 10, &buf, &buflen)); + + break; + } + + case ns_t_mx: + case ns_t_afsdb: + case ns_t_rt: { + u_int t; + + if (rdlen < (size_t)NS_INT16SZ) + goto formerr; + + /* Priority. */ + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Target. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_px: { + u_int t; + + if (rdlen < (size_t)NS_INT16SZ) + goto formerr; + + /* Priority. */ + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Name1. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Name2. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_x25: + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + break; + + case ns_t_txt: + while (rdata < edata) { + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + if (rdata < edata) + T(addstr(" ", 1, &buf, &buflen)); + } + break; + + case ns_t_nsap: { + char t[2+255*3]; + + (void) inet_nsap_ntoa(rdlen, rdata, t); + T(addstr(t, strlen(t), &buf, &buflen)); + break; + } + + case ns_t_aaaa: + if (rdlen != (size_t)NS_IN6ADDRSZ) + goto formerr; + (void) inet_ntop(AF_INET6, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + break; + + case ns_t_loc: { + char t[255]; + + /* XXX protocol format checking? */ + (void) loc_ntoa(rdata, t); + T(addstr(t, strlen(t), &buf, &buflen)); + break; + } + + case ns_t_naptr: { + u_int order, preference; + char t[50]; + + if (rdlen < 2U*NS_INT16SZ) + goto formerr; + + /* Order, Precedence. */ + order = ns_get16(rdata); rdata += NS_INT16SZ; + preference = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((t, "%u %u ", order, preference)); + T(addstr(t, len, &buf, &buflen)); + + /* Flags. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Service. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Regexp. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len < 0) + return (-1); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Server. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + } + + case ns_t_srv: { + u_int priority, weight, port; + char t[50]; + + if (rdlen < 3U*NS_INT16SZ) + goto formerr; + + /* Priority, Weight, Port. */ + priority = ns_get16(rdata); rdata += NS_INT16SZ; + weight = ns_get16(rdata); rdata += NS_INT16SZ; + port = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((t, "%u %u %u ", priority, weight, port)); + T(addstr(t, len, &buf, &buflen)); + + /* Server. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + } + + case ns_t_minfo: + case ns_t_rp: + /* Name1. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Name2. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + + case ns_t_wks: { + int n, lcnt; + + if (rdlen < 1U + NS_INT32SZ) + goto formerr; + + /* Address. */ + (void) inet_ntop(AF_INET, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += NS_INADDRSZ; + + /* Protocol. */ + len = SPRINTF((tmp, " %u ( ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata += NS_INT8SZ; + + /* Bit map. */ + n = 0; + lcnt = 0; + while (rdata < edata) { + u_int c = *rdata++; + do { + if (c & 0200) { + if (lcnt == 0) { + T(addstr("\n\t\t\t\t", 5, + &buf, &buflen)); + lcnt = 10; + spaced = 0; + } + len = SPRINTF((tmp, "%d ", n)); + T(addstr(tmp, len, &buf, &buflen)); + lcnt--; + } + c <<= 1; + } while (++n & 07); + } + T(addstr(")", 1, &buf, &buflen)); + + break; + } + + case ns_t_key: { + char base64_key[NS_MD5RSA_MAX_BASE64]; + u_int keyflags, protocol, algorithm, key_id; + const char *leader; + int n; + + if (rdlen < 0U + NS_INT16SZ + NS_INT8SZ + NS_INT8SZ) + goto formerr; + + /* Key flags, Protocol, Algorithm. */ + key_id = dst_s_dns_key_id(rdata, edata-rdata); + keyflags = ns_get16(rdata); rdata += NS_INT16SZ; + protocol = *rdata++; + algorithm = *rdata++; + len = SPRINTF((tmp, "0x%04x %u %u", + keyflags, protocol, algorithm)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Public key data. */ + len = b64_ntop(rdata, edata - rdata, + base64_key, sizeof base64_key); + if (len < 0) + goto formerr; + if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } else + leader = " "; + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), &buf, &buflen)); + T(addstr(base64_key + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + n = SPRINTF((tmp, " ; key_tag= %u", key_id)); + T(addstr(tmp, n, &buf, &buflen)); + + break; + } + + case ns_t_sig: { + char base64_key[NS_MD5RSA_MAX_BASE64]; + u_int type, algorithm, labels, footprint; + const char *leader; + u_long t; + int n; + + if (rdlen < 22U) + goto formerr; + + /* Type covered, Algorithm, Label count, Original TTL. */ + type = ns_get16(rdata); rdata += NS_INT16SZ; + algorithm = *rdata++; + labels = *rdata++; + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s %d %d %lu ", + p_type(type), algorithm, labels, t)); + T(addstr(tmp, len, &buf, &buflen)); + if (labels > (u_int)dn_count_labels(name)) + goto formerr; + + /* Signature expiry. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Time signed. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Signature Footprint. */ + footprint = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", footprint)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Signer's name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + /* Signature. */ + len = b64_ntop(rdata, edata - rdata, + base64_key, sizeof base64_key); + if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } else + leader = " "; + if (len < 0) + goto formerr; + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), &buf, &buflen)); + T(addstr(base64_key + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + break; + } + + case ns_t_nxt: { + int n, c; + + /* Next domain name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + /* Type bit map. */ + n = edata - rdata; + for (c = 0; c < n*8; c++) + if (NS_NXT_BIT_ISSET(c, rdata)) { + len = SPRINTF((tmp, " %s", p_type(c))); + T(addstr(tmp, len, &buf, &buflen)); + } + break; + } + + case ns_t_cert: { + u_int c_type, key_tag, alg; + int n; + unsigned int siz; + char base64_cert[8192], tmp[40]; + const char *leader; + + c_type = ns_get16(rdata); rdata += NS_INT16SZ; + key_tag = ns_get16(rdata); rdata += NS_INT16SZ; + alg = (u_int) *rdata++; + + len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg)); + T(addstr(tmp, len, &buf, &buflen)); + siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ + if (siz > sizeof(base64_cert) * 3/4) { + const char *str = "record too long to print"; + T(addstr(str, strlen(str), &buf, &buflen)); + } + else { + len = b64_ntop(rdata, edata-rdata, base64_cert, siz); + + if (len < 0) + goto formerr; + else if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } + else + leader = " "; + + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), + &buf, &buflen)); + T(addstr(base64_cert + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + } + break; + } + + case ns_t_tkey: { + /* KJD - need to complete this */ + u_long t; + int mode, err, keysize; + + /* Algorithm name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Inception. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Experation. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Mode , Error, Key Size. */ + /* Priority, Weight, Port. */ + mode = ns_get16(rdata); rdata += NS_INT16SZ; + err = ns_get16(rdata); rdata += NS_INT16SZ; + keysize = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize)); + T(addstr(tmp, len, &buf, &buflen)); + + /* XXX need to dump key, print otherdata length & other data */ + break; + } + + case ns_t_tsig: { + /* BEW - need to complete this */ + int n; + + T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + rdata += 8; /* time */ + n = ns_get16(rdata); rdata += INT16SZ; + rdata += n; /* sig */ + n = ns_get16(rdata); rdata += INT16SZ; /* original id */ + sprintf(buf, "%d", ns_get16(rdata)); + rdata += INT16SZ; + addlen(strlen(buf), &buf, &buflen); + break; + } + + case ns_t_a6: { + struct in6_addr a; + int pbyte, pbit; + + /* prefix length */ + if (rdlen == 0U) goto formerr; + len = SPRINTF((tmp, "%d ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + pbit = *rdata; + if (pbit > 128) goto formerr; + pbyte = (pbit & ~7) / 8; + rdata++; + + /* address suffix: provided only when prefix len != 128 */ + if (pbit < 128) { + if (rdata + pbyte >= edata) goto formerr; + memset(&a, 0, sizeof(a)); + memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte); + (void) inet_ntop(AF_INET6, &a, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += sizeof(a) - pbyte; + } + + /* prefix name: provided only when prefix len > 0 */ + if (pbit == 0) + break; + if (rdata >= edata) goto formerr; + T(addstr(" ", 1, &buf, &buflen)); + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_opt: { + len = SPRINTF((tmp, "%u bytes", class)); + T(addstr(tmp, len, &buf, &buflen)); + break; + } + + default: + comment = "unknown RR type"; + goto hexify; + } + return (buf - obuf); + formerr: + comment = "RR format error"; + hexify: { + int n, m; + char *p; + + len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata), + rdlen != 0U ? " (" : "", comment)); + T(addstr(tmp, len, &buf, &buflen)); + while (rdata < edata) { + p = tmp; + p += SPRINTF((p, "\n\t")); + spaced = 0; + n = MIN(16, edata - rdata); + for (m = 0; m < n; m++) + p += SPRINTF((p, "%02x ", rdata[m])); + T(addstr(tmp, p - tmp, &buf, &buflen)); + if (n < 16) { + T(addstr(")", 1, &buf, &buflen)); + T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen)); + } + p = tmp; + p += SPRINTF((p, "; ")); + for (m = 0; m < n; m++) + *p++ = (isascii(rdata[m]) && isprint(rdata[m])) + ? rdata[m] + : '.'; + T(addstr(tmp, p - tmp, &buf, &buflen)); + rdata += n; + } + return (buf - obuf); + } +} + +/* Private. */ + +/* + * size_t + * prune_origin(name, origin) + * Find out if the name is at or under the current origin. + * return: + * Number of characters in name before start of origin, + * or length of name if origin does not match. + * notes: + * This function should share code with samedomain(). + */ +static size_t +prune_origin(const char *name, const char *origin) { + const char *oname = name; + + while (*name != '\0') { + if (origin != NULL && ns_samename(name, origin) == 1) + return (name - oname - (name > oname)); + while (*name != '\0') { + if (*name == '\\') { + name++; + /* XXX need to handle \nnn form. */ + if (*name == '\0') + break; + } else if (*name == '.') { + name++; + break; + } + name++; + } + } + return (name - oname); +} + +/* + * int + * charstr(rdata, edata, buf, buflen) + * Format a <character-string> into the presentation buffer. + * return: + * Number of rdata octets consumed + * 0 for protocol format error + * -1 for output buffer error + * side effects: + * buffer is advanced on success. + */ +static int +charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) { + const u_char *odata = rdata; + size_t save_buflen = *buflen; + char *save_buf = *buf; + + if (addstr("\"", 1, buf, buflen) < 0) + goto enospc; + if (rdata < edata) { + int n = *rdata; + + if (rdata + 1 + n <= edata) { + rdata++; + while (n-- > 0) { + if (strchr("\n\"\\", *rdata) != NULL) + if (addstr("\\", 1, buf, buflen) < 0) + goto enospc; + if (addstr((const char *)rdata, 1, + buf, buflen) < 0) + goto enospc; + rdata++; + } + } + } + if (addstr("\"", 1, buf, buflen) < 0) + goto enospc; + return (rdata - odata); + enospc: + errno = ENOSPC; + *buf = save_buf; + *buflen = save_buflen; + return (-1); +} + +static int +addname(const u_char *msg, size_t msglen, + const u_char **pp, const char *origin, + char **buf, size_t *buflen) +{ + size_t newlen, save_buflen = *buflen; + char *save_buf = *buf; + int n; + + n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen); + if (n < 0) + goto enospc; /* Guess. */ + newlen = prune_origin(*buf, origin); + if (**buf == '\0') { + goto root; + } else if (newlen == 0U) { + /* Use "@" instead of name. */ + if (newlen + 2 > *buflen) + goto enospc; /* No room for "@\0". */ + (*buf)[newlen++] = '@'; + (*buf)[newlen] = '\0'; + } else { + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') { + /* No trailing dot. */ + root: + if (newlen + 2 > *buflen) + goto enospc; /* No room for ".\0". */ + (*buf)[newlen++] = '.'; + (*buf)[newlen] = '\0'; + } + } + *pp += n; + addlen(newlen, buf, buflen); + **buf = '\0'; + return (newlen); + enospc: + errno = ENOSPC; + *buf = save_buf; + *buflen = save_buflen; + return (-1); +} + +static void +addlen(size_t len, char **buf, size_t *buflen) { + INSIST(len <= *buflen); + *buf += len; + *buflen -= len; +} + +static int +addstr(const char *src, size_t len, char **buf, size_t *buflen) { + if (len >= *buflen) { + errno = ENOSPC; + return (-1); + } + memcpy(*buf, src, len); + addlen(len, buf, buflen); + **buf = '\0'; + return (0); +} + +static int +addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) { + size_t save_buflen = *buflen; + char *save_buf = *buf; + int t; + + if (spaced || len >= target - 1) { + T(addstr(" ", 2, buf, buflen)); + spaced = 1; + } else { + for (t = (target - len - 1) / 8; t >= 0; t--) + if (addstr("\t", 1, buf, buflen) < 0) { + *buflen = save_buflen; + *buf = save_buf; + return (-1); + } + spaced = 0; + } + return (spaced); +} diff --git a/lib/libc/nameser/ns_samedomain.c b/lib/libc/nameser/ns_samedomain.c new file mode 100644 index 0000000..d4ca550 --- /dev/null +++ b/lib/libc/nameser/ns_samedomain.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_samedomain.c,v 1.1.2.2.4.2 2004/03/16 12:34:17 marka Exp $"; +#endif + +#include "port_before.h" + +#include <sys/types.h> +#include <arpa/nameser.h> +#include <errno.h> +#include <string.h> + +#include "port_after.h" + +/* + * int + * ns_samedomain(a, b) + * Check whether a name belongs to a domain. + * Inputs: + * a - the domain whose ancestory is being verified + * b - the potential ancestor we're checking against + * Return: + * boolean - is a at or below b? + * Notes: + * Trailing dots are first removed from name and domain. + * Always compare complete subdomains, not only whether the + * domain name is the trailing string of the given name. + * + * "host.foobar.top" lies in "foobar.top" and in "top" and in "" + * but NOT in "bar.top" + */ + +int +ns_samedomain(const char *a, const char *b) { + size_t la, lb; + int diff, i, escaped; + const char *cp; + + la = strlen(a); + lb = strlen(b); + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */ + if (la != 0U && a[la - 1] == '.') { + escaped = 0; + /* Note this loop doesn't get executed if la==1. */ + for (i = la - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + la--; + } + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */ + if (lb != 0U && b[lb - 1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if lb==1 */ + for (i = lb - 2; i >= 0; i--) + if (b[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + lb--; + } + + /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */ + if (lb == 0U) + return (1); + + /* 'b' longer than 'a' means 'a' can't be in 'b'. */ + if (lb > la) + return (0); + + /* 'a' and 'b' being equal at this point indicates sameness. */ + if (lb == la) + return (strncasecmp(a, b, lb) == 0); + + /* Ok, we know la > lb. */ + + diff = la - lb; + + /* + * If 'a' is only 1 character longer than 'b', then it can't be + * a subdomain of 'b' (because of the need for the '.' label + * separator). + */ + if (diff < 2) + return (0); + + /* + * If the character before the last 'lb' characters of 'b' + * isn't '.', then it can't be a match (this lets us avoid + * having "foobar.com" match "bar.com"). + */ + if (a[diff - 1] != '.') + return (0); + + /* + * We're not sure about that '.', however. It could be escaped + * and thus not a really a label separator. + */ + escaped = 0; + for (i = diff - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (escaped) + return (0); + + /* Now compare aligned trailing substring. */ + cp = a + diff; + return (strncasecmp(cp, b, lb) == 0); +} + +/* + * int + * ns_subdomain(a, b) + * is "a" a subdomain of "b"? + */ +int +ns_subdomain(const char *a, const char *b) { + return (ns_samename(a, b) != 1 && ns_samedomain(a, b)); +} + +/* + * int + * ns_makecanon(src, dst, dstsize) + * make a canonical copy of domain name "src" + * notes: + * foo -> foo. + * foo. -> foo. + * foo.. -> foo. + * foo\. -> foo\.. + * foo\\. -> foo\\. + */ + +int +ns_makecanon(const char *src, char *dst, size_t dstsize) { + size_t n = strlen(src); + + if (n + sizeof "." > dstsize) { /* Note: sizeof == 2 */ + errno = EMSGSIZE; + return (-1); + } + strcpy(dst, src); + while (n >= 1U && dst[n - 1] == '.') /* Ends in "." */ + if (n >= 2U && dst[n - 2] == '\\' && /* Ends in "\." */ + (n < 3U || dst[n - 3] != '\\')) /* But not "\\." */ + break; + else + dst[--n] = '\0'; + dst[n++] = '.'; + dst[n] = '\0'; + return (0); +} + +/* + * int + * ns_samename(a, b) + * determine whether domain name "a" is the same as domain name "b" + * return: + * -1 on error + * 0 if names differ + * 1 if names are the same + */ + +int +ns_samename(const char *a, const char *b) { + char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; + + if (ns_makecanon(a, ta, sizeof ta) < 0 || + ns_makecanon(b, tb, sizeof tb) < 0) + return (-1); + if (strcasecmp(ta, tb) == 0) + return (1); + else + return (0); +} diff --git a/lib/libc/nameser/ns_ttl.c b/lib/libc/nameser/ns_ttl.c new file mode 100644 index 0000000..4d18d3f --- /dev/null +++ b/lib/libc/nameser/ns_ttl.c @@ -0,0 +1,160 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_ttl.c,v 1.1.206.2 2005/07/28 07:43:21 marka Exp $"; +#endif + +/* Import. */ + +#include "port_before.h" + +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static int fmt1(int t, char s, char **buf, size_t *buflen); + +/* Macros. */ + +#define T(x) if ((x) < 0) return (-1); else (void)NULL + +/* Public. */ + +int +ns_format_ttl(u_long src, char *dst, size_t dstlen) { + char *odst = dst; + int secs, mins, hours, days, weeks, x; + char *p; + + secs = src % 60; src /= 60; + mins = src % 60; src /= 60; + hours = src % 24; src /= 24; + days = src % 7; src /= 7; + weeks = src; src = 0; + + x = 0; + if (weeks) { + T(fmt1(weeks, 'W', &dst, &dstlen)); + x++; + } + if (days) { + T(fmt1(days, 'D', &dst, &dstlen)); + x++; + } + if (hours) { + T(fmt1(hours, 'H', &dst, &dstlen)); + x++; + } + if (mins) { + T(fmt1(mins, 'M', &dst, &dstlen)); + x++; + } + if (secs || !(weeks || days || hours || mins)) { + T(fmt1(secs, 'S', &dst, &dstlen)); + x++; + } + + if (x > 1) { + int ch; + + for (p = odst; (ch = *p) != '\0'; p++) + if (isascii(ch) && isupper(ch)) + *p = tolower(ch); + } + + return (dst - odst); +} + +int +ns_parse_ttl(const char *src, u_long *dst) { + u_long ttl, tmp; + int ch, digits, dirty; + + ttl = 0; + tmp = 0; + digits = 0; + dirty = 0; + while ((ch = *src++) != '\0') { + if (!isascii(ch) || !isprint(ch)) + goto einval; + if (isdigit(ch)) { + tmp *= 10; + tmp += (ch - '0'); + digits++; + continue; + } + if (digits == 0) + goto einval; + if (islower(ch)) + ch = toupper(ch); + switch (ch) { + case 'W': tmp *= 7; + case 'D': tmp *= 24; + case 'H': tmp *= 60; + case 'M': tmp *= 60; + case 'S': break; + default: goto einval; + } + ttl += tmp; + tmp = 0; + digits = 0; + dirty = 1; + } + if (digits > 0) { + if (dirty) + goto einval; + else + ttl += tmp; + } else if (!dirty) + goto einval; + *dst = ttl; + return (0); + + einval: + errno = EINVAL; + return (-1); +} + +/* Private. */ + +static int +fmt1(int t, char s, char **buf, size_t *buflen) { + char tmp[50]; + size_t len; + + len = SPRINTF((tmp, "%d%c", t, s)); + if (len + 1 > *buflen) + return (-1); + strcpy(*buf, tmp); + *buf += len; + *buflen -= len; + return (0); +} |