/* $OpenBSD: common.c,v 1.3 2014/08/10 07:31:58 guenther Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" int long_err; int gai_errno; int rrset_errno; char * gethostarg(char *n) { if (n == NULL) return (n); if (!strcmp(n, "NULL")) return (NULL); if (!strcmp(n, "EMPTY")) return (""); return (n); } const char *rrsetstrerror(int); char * print_addr(const struct sockaddr *, char *, size_t); struct kv { int code; const char *name; }; struct kv kv_family[] = { { AF_UNIX, "unix" }, { AF_INET, "inet" }, { AF_INET6, "inet6" }, { 0, NULL, } }; struct kv kv_socktype[] = { { SOCK_STREAM, "stream" }, { SOCK_DGRAM, "dgram" }, { SOCK_RAW, "raw" }, { SOCK_SEQPACKET, "seqpacket" }, { 0, NULL, } }; struct kv kv_protocol[] = { { IPPROTO_UDP, "udp" }, { IPPROTO_TCP, "tcp" }, { IPPROTO_ICMP, "icmp" }, { IPPROTO_ICMPV6, "icmpv6" }, { 0, NULL, } }; static const char * kv_lookup_name(struct kv *kv, int code, char *buf, size_t sz) { while (kv->name) { if (kv->code == code) return (kv->name); kv++; } snprintf(buf, sz, "%i", code); return (buf); } struct keyval { const char *key; uint16_t value; }; static struct keyval kv_class[] = { { "IN", C_IN }, { "CHAOS", C_CHAOS }, { "HS", C_HS }, { "ANY", C_ANY }, { NULL, 0 }, }; static struct keyval kv_type[] = { { "A", T_A }, { "NS", T_NS }, { "MD", T_MD }, { "MF", T_MF }, { "CNAME", T_CNAME }, { "SOA", T_SOA }, { "MB", T_MB }, { "MG", T_MG }, { "MR", T_MR }, { "NULL", T_NULL }, { "WKS", T_WKS }, { "PTR", T_PTR }, { "HINFO", T_HINFO }, { "MINFO", T_MINFO }, { "MX", T_MX }, { "TXT", T_TXT }, { "AAAA", T_AAAA }, { "AXFR", T_AXFR }, { "MAILB", T_MAILB }, { "MAILA", T_MAILA }, { "ANY", T_ANY }, { NULL, 0 }, }; static struct keyval kv_rcode[] = { { "NOERROR", NOERROR }, { "FORMERR", FORMERR }, { "SERVFAIL", SERVFAIL }, { "NXDOMAIN", NXDOMAIN }, { "NOTIMP", NOTIMP }, { "REFUSED", REFUSED }, { NULL, 0 }, }; const char * rcodetostr(uint16_t v) { static char buf[16]; size_t i; for(i = 0; kv_rcode[i].key; i++) if (kv_rcode[i].value == v) return (kv_rcode[i].key); snprintf(buf, sizeof buf, "%"PRIu16"?", v); return (buf); } const char * typetostr(uint16_t v) { static char buf[16]; size_t i; for(i = 0; kv_type[i].key; i++) if (kv_type[i].value == v) return (kv_type[i].key); snprintf(buf, sizeof buf, "%"PRIu16"?", v); return (buf); } const char * classtostr(uint16_t v) { static char buf[16]; size_t i; for(i = 0; kv_class[i].key; i++) if (kv_class[i].value == v) return (kv_class[i].key); snprintf(buf, sizeof buf, "%"PRIu16"?", v); return (buf); } uint16_t strtotype(const char *name) { size_t i; for(i = 0; kv_type[i].key; i++) if (!strcmp(kv_type[i].key, name)) return (kv_type[i].value); return (0); } uint16_t strtoclass(const char *name) { size_t i; for(i = 0; kv_class[i].key; i++) if (!strcmp(kv_class[i].key, name)) return (kv_class[i].value); return (0); } void print_hostent(struct hostent *e) { char buf[256], **c; printf("name = \"%s\"\n", e->h_name); printf("aliases ="); for(c = e->h_aliases; *c; c++) printf(" \"%s\"", *c); printf("\n"); printf("addrtype = %i\n", e->h_addrtype); printf("addrlength = %i\n", e->h_length); printf("addr_list ="); for(c = e->h_addr_list; *c; c++) { printf(" %s", inet_ntop(e->h_addrtype, *c, buf, sizeof buf)); } printf("\n"); } void print_netent(struct netent *e) { char buf[256], **c; uint32_t addr; /* network number are given in host order */ addr = htonl(e->n_net); printf("name = \"%s\"\n", e->n_name); printf("aliases ="); for (c = e->n_aliases; *c; c++) printf(" \"%s\"", *c); printf("\n"); printf("addrtype = %i\n", e->n_addrtype); printf("net = %s\n", inet_ntop(e->n_addrtype, &addr, buf, sizeof buf)); } void print_addrinfo(struct addrinfo *ai) { char buf[256], bf[64], bt[64], bp[64]; printf("family=%s socktype=%s protocol=%s addr=%s canonname=%s\n", kv_lookup_name(kv_family, ai->ai_family, bf, sizeof bf), kv_lookup_name(kv_socktype, ai->ai_socktype, bt, sizeof bt), kv_lookup_name(kv_protocol, ai->ai_protocol, bp, sizeof bp), print_addr(ai->ai_addr, buf, sizeof buf), ai->ai_canonname); } const char * rrsetstrerror(int e) { switch (e) { case 0: return "OK"; case ERRSET_NONAME: return "ERRSET_NONAME"; case ERRSET_NODATA: return "ERRSET_NODATA"; case ERRSET_NOMEMORY: return "ERRSET_NOMEMORY"; case ERRSET_INVAL: return "ERRSET_INVAL"; case ERRSET_FAIL: return "ERRSET_FAIL"; default: return "???"; } } void print_rrsetinfo(struct rrsetinfo * rrset) { printf("rri_flags=%u\n", rrset->rri_flags); printf("rri_rdclass=%u\n", rrset->rri_rdclass); printf("rri_rdtype=%u\n", rrset->rri_rdtype); printf("rri_ttl=%u\n", rrset->rri_ttl); printf("rri_nrdatas=%u\n", rrset->rri_nrdatas); printf("rri_nsigs=%u\n", rrset->rri_nsigs); printf("rri_name=\"%s\"\n", rrset->rri_name); } void print_errors(void) { switch (long_err) { case 0: return; case 1: printf(" => errno %i, h_errno %i", errno, h_errno); printf(", rrset_errno %i", rrset_errno); printf(", gai_errno %i", gai_errno); printf ("\n"); return; default: printf(" => errno %i: %s\n => h_errno %i: %s\n => rrset_errno %i: %s\n", errno, errno ? strerror(errno) : "ok", h_errno, h_errno ? hstrerror(h_errno) : "ok", rrset_errno, rrset_errno ? rrsetstrerror(rrset_errno) : "ok"); printf(" => gai_errno %i: %s\n", gai_errno, gai_errno ? gai_strerror(gai_errno) : "ok"); } } static char * print_host(const struct sockaddr *sa, char *buf, size_t len) { switch (sa->sa_family) { case AF_INET: inet_ntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr, buf, len); break; case AF_INET6: inet_ntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr, buf, len); break; default: buf[0] = '\0'; } return (buf); } char * print_addr(const struct sockaddr *sa, char *buf, size_t len) { char h[256]; print_host(sa, h, sizeof h); switch (sa->sa_family) { case AF_INET: snprintf(buf, len, "%s:%i", h, ntohs(((struct sockaddr_in*)(sa))->sin_port)); break; case AF_INET6: snprintf(buf, len, "[%s]:%i", h, ntohs(((struct sockaddr_in6*)(sa))->sin6_port)); break; default: snprintf(buf, len, "?"); break; } return (buf); } void packed_init(struct packed *pack, char *data, size_t len) { pack->data = data; pack->len = len; pack->offset = 0; pack->err = NULL; } static ssize_t dname_expand(const unsigned char *data, size_t len, size_t offset, size_t *newoffset, char *dst, size_t max) { size_t n, count, end, ptr, start; ssize_t res; if (offset >= len) return (-1); res = 0; end = start = offset; for(; (n = data[offset]); ) { if ((n & 0xc0) == 0xc0) { if (offset + 2 > len) return (-1); ptr = 256 * (n & ~0xc0) + data[offset + 1]; if (ptr >= start) return (-1); if (end < offset + 2) end = offset + 2; offset = ptr; continue; } if (offset + n + 1 > len) return (-1); /* copy n + at offset+1 */ if (dst != NULL && max != 0) { count = (max < n + 1) ? (max) : (n + 1); memmove(dst, data + offset, count); dst += count; max -= count; } res += n + 1; offset += n + 1; if (end < offset) end = offset; } if (end < offset + 1) end = offset + 1; if (dst != NULL && max != 0) dst[0] = 0; if (newoffset) *newoffset = end; return (res + 1); } static int unpack_data(struct packed *p, void *data, size_t len) { if (p->err) return (-1); if (p->len - p->offset < len) { p->err = "too short"; return (-1); } memmove(data, p->data + p->offset, len); p->offset += len; return (0); } static int unpack_u16(struct packed *p, uint16_t *u16) { if (unpack_data(p, u16, 2) == -1) return (-1); *u16 = ntohs(*u16); return (0); } static int unpack_u32(struct packed *p, uint32_t *u32) { if (unpack_data(p, u32, 4) == -1) return (-1); *u32 = ntohl(*u32); return (0); } static int unpack_inaddr(struct packed *p, struct in_addr *a) { return (unpack_data(p, a, 4)); } static int unpack_in6addr(struct packed *p, struct in6_addr *a6) { return (unpack_data(p, a6, 16)); } static int unpack_dname(struct packed *p, char *dst, size_t max) { ssize_t e; if (p->err) return (-1); e = dname_expand(p->data, p->len, p->offset, &p->offset, dst, max); if (e == -1) { p->err = "bad domain name"; return (-1); } if (e < 0 || e > MAXDNAME) { p->err = "domain name too long"; return (-1); } return (0); } int unpack_header(struct packed *p, struct header *h) { if (unpack_data(p, h, HFIXEDSZ) == -1) return (-1); h->flags = ntohs(h->flags); h->qdcount = ntohs(h->qdcount); h->ancount = ntohs(h->ancount); h->nscount = ntohs(h->nscount); h->arcount = ntohs(h->arcount); return (0); } int unpack_query(struct packed *p, struct query *q) { unpack_dname(p, q->q_dname, sizeof(q->q_dname)); unpack_u16(p, &q->q_type); unpack_u16(p, &q->q_class); return (p->err) ? (-1) : (0); } int unpack_rr(struct packed *p, struct rr *rr) { uint16_t rdlen; size_t save_offset; unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); unpack_u16(p, &rr->rr_type); unpack_u16(p, &rr->rr_class); unpack_u32(p, &rr->rr_ttl); unpack_u16(p, &rdlen); if (p->err) return (-1); if (p->len - p->offset < rdlen) { p->err = "too short"; return (-1); } save_offset = p->offset; switch(rr->rr_type) { case T_CNAME: unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); break; case T_MX: unpack_u16(p, &rr->rr.mx.preference); unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); break; case T_NS: unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); break; case T_PTR: unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); break; case T_SOA: unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); unpack_u32(p, &rr->rr.soa.serial); unpack_u32(p, &rr->rr.soa.refresh); unpack_u32(p, &rr->rr.soa.retry); unpack_u32(p, &rr->rr.soa.expire); unpack_u32(p, &rr->rr.soa.minimum); break; case T_A: if (rr->rr_class != C_IN) goto other; unpack_inaddr(p, &rr->rr.in_a.addr); break; case T_AAAA: if (rr->rr_class != C_IN) goto other; unpack_in6addr(p, &rr->rr.in_aaaa.addr6); break; default: other: rr->rr.other.rdata = p->data + p->offset; rr->rr.other.rdlen = rdlen; p->offset += rdlen; } if (p->err) return (-1); /* make sure that the advertised rdlen is really ok */ if (p->offset - save_offset != rdlen) p->err = "bad dlen"; return (p->err) ? (-1) : (0); } int sockaddr_from_str(struct sockaddr *sa, int family, const char *str) { struct in_addr ina; struct in6_addr in6a; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; switch (family) { case PF_UNSPEC: if (sockaddr_from_str(sa, PF_INET, str) == 0) return (0); return sockaddr_from_str(sa, PF_INET6, str); case PF_INET: if (inet_pton(PF_INET, str, &ina) != 1) return (-1); sin = (struct sockaddr_in *)sa; memset(sin, 0, sizeof *sin); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = PF_INET; sin->sin_addr.s_addr = ina.s_addr; return (0); case PF_INET6: if (inet_pton(PF_INET6, str, &in6a) != 1) return (-1); sin6 = (struct sockaddr_in6 *)sa; memset(sin6, 0, sizeof *sin6); sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = PF_INET6; sin6->sin6_addr = in6a; return (0); default: break; } return (-1); }