| /* | |
| * 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) 1999, 2000 - 2014 | |
| * Intel 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. All advertising materials mentioning features or use of this software | |
| * must display the following acknowledgement: | |
| * | |
| * This product includes software developed by Intel Corporation and | |
| * its contributors. | |
| * | |
| * 4. Neither the name of Intel Corporation or its contributors may be | |
| * used to endorse or promote products derived from this software | |
| * without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION 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 INTEL CORPORATION 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. | |
| * | |
| */ | |
| /* Import. */ | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <arpa/nameser.h> | |
| #include <arpa/inet.h> | |
| #include <assert.h> | |
| #include <errno.h> | |
| #include <resolv.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #define SPRINTF(x) (sprintf x) | |
| /* 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 ((ssize_t)(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 x; | |
| size_t len; | |
| static char base64_key[NS_MD5RSA_MAX_BASE64]; | |
| static char t[255*3]; | |
| /* | |
| * Owner. | |
| */ | |
| if (name_ctx != NULL && strcasecmp(name_ctx, name) == 0) { | |
| T(addstr("\t\t\t", 3, &buf, &buflen)); | |
| } else { | |
| len = prune_origin(name, origin); | |
| if (len == 0) { | |
| T(addstr("@\t\t\t", 4, &buf, &buflen)); | |
| } else { | |
| T(addstr(name, len, &buf, &buflen)); | |
| /* Origin not used and no trailing dot? */ | |
| if ((!origin || !origin[0] || name[len] == '\0') && | |
| name[len - 1] != '.') { | |
| 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 != NS_INADDRSZ) | |
| goto formerr; | |
| (void) inet_ntop(AF_INET, rdata, buf, (socklen_t)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: | |
| 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. */ | |
| 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", (unsigned long)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 < NS_INT16SZ) | |
| goto formerr; | |
| /* Priority. */ | |
| t = ns_get16(rdata); | |
| rdata += NS_INT16SZ; | |
| len = SPRINTF((tmp, "%u ", (unsigned int)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 < NS_INT16SZ) | |
| goto formerr; | |
| /* Priority. */ | |
| t = ns_get16(rdata); | |
| rdata += NS_INT16SZ; | |
| len = SPRINTF((tmp, "%u ", (unsigned int)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: { | |
| (void) inet_nsap_ntoa((int)rdlen, rdata, t); | |
| T(addstr(t, strlen(t), &buf, &buflen)); | |
| break; | |
| } | |
| case ns_t_aaaa: | |
| if (rdlen != NS_IN6ADDRSZ) | |
| goto formerr; | |
| (void) inet_ntop(AF_INET6, rdata, buf, (socklen_t)buflen); | |
| addlen(strlen(buf), &buf, &buflen); | |
| break; | |
| case ns_t_loc: { | |
| /* 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; | |
| if (rdlen < 2*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 ", (unsigned int)order, (unsigned int)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 ((ssize_t)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; | |
| if (rdlen < NS_INT16SZ*3) | |
| 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 ", (unsigned int)priority, (unsigned int)weight, (unsigned int)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 < NS_INT32SZ + 1) | |
| goto formerr; | |
| /* Address. */ | |
| (void) inet_ntop(AF_INET, rdata, buf, (socklen_t)buflen); | |
| addlen(strlen(buf), &buf, &buflen); | |
| rdata += NS_INADDRSZ; | |
| /* Protocol. */ | |
| len = SPRINTF((tmp, " %u ( ", (unsigned int)*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: { | |
| u_int keyflags, protocol, algorithm; | |
| const char *leader; | |
| int n; | |
| if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ) | |
| goto formerr; | |
| /* Key flags, Protocol, Algorithm. */ | |
| keyflags = ns_get16(rdata); rdata += NS_INT16SZ; | |
| protocol = *rdata++; | |
| algorithm = *rdata++; | |
| len = SPRINTF((tmp, "0x%04x %u %u", | |
| (unsigned int)keyflags, (unsigned int)protocol, (unsigned int)algorithm)); | |
| T(addstr(tmp, len, &buf, &buflen)); | |
| /* Public key data. */ | |
| len = b64_ntop(rdata, edata - rdata, | |
| base64_key, sizeof base64_key); | |
| if ((ssize_t)len < 0) | |
| goto formerr; | |
| if (len > 15) { | |
| T(addstr(" (", 2, &buf, &buflen)); | |
| leader = "\n\t\t"; | |
| spaced = 0; | |
| } else | |
| leader = " "; | |
| for (n = 0; n < (int)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_sig: { | |
| u_int type, algorithm, labels, footprint; | |
| const char *leader; | |
| u_long t; | |
| int n; | |
| if (rdlen < 22) | |
| 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 %lu ", | |
| p_type((int)type), (int)algorithm, (unsigned long)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 ", (unsigned int)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 ((ssize_t)len < 0) | |
| goto formerr; | |
| for (n = 0; n < (int)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 = (int)(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; | |
| } | |
| default: | |
| comment = "unknown RR type"; | |
| goto hexify; | |
| } | |
| return ((int)(buf - obuf)); | |
| formerr: | |
| comment = "RR format error"; | |
| hexify: { | |
| int n, m; | |
| char *p; | |
| len = SPRINTF((tmp, "\\#(\t\t; %s", comment)); | |
| T(addstr(tmp, len, &buf, &buflen)); | |
| while (rdata < edata) { | |
| p = tmp; | |
| p += SPRINTF((p, "\n\t")); | |
| spaced = 0; | |
| n = MIN(16, (int)(edata - rdata)); | |
| for (m = 0; m < n; m++) | |
| p += SPRINTF((p, "%02x ", rdata[m])); | |
| T(addstr(tmp, (u_int)(p - tmp), &buf, &buflen)); | |
| if (n < 16) { | |
| T(addstr(")", 1, &buf, &buflen)); | |
| T(addtab((u_int)(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, (u_int)(p - tmp), &buf, &buflen)); | |
| rdata += n; | |
| } | |
| return ((int)(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 && strcasecmp(name, origin) == 0) | |
| return ((size_t)(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 ((size_t)(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 ((int)(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, (int)(*buflen)); | |
| if (n < 0) | |
| goto enospc; /* Guess. */ | |
| newlen = prune_origin(*buf, origin); | |
| if ((origin == NULL || origin[0] == '\0' || (*buf)[newlen] == '\0') && | |
| (newlen == 0 || (*buf)[newlen - 1] != '.')) { | |
| /* No trailing dot. */ | |
| if (newlen + 2 > *buflen) | |
| goto enospc; /* No room for ".\0". */ | |
| (*buf)[newlen++] = '.'; | |
| (*buf)[newlen] = '\0'; | |
| } | |
| if (newlen == 0) { | |
| /* Use "@" instead of name. */ | |
| if (newlen + 2 > *buflen) | |
| goto enospc; /* No room for "@\0". */ | |
| (*buf)[newlen++] = '@'; | |
| (*buf)[newlen] = '\0'; | |
| } | |
| *pp += n; | |
| addlen(newlen, buf, buflen); | |
| **buf = '\0'; | |
| return ((int)newlen); | |
| enospc: | |
| errno = ENOSPC; | |
| *buf = save_buf; | |
| *buflen = save_buflen; | |
| return (-1); | |
| } | |
| static void | |
| addlen(size_t len, char **buf, size_t *buflen) { | |
| assert(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 = (int)(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); | |
| } |