/** \file dns.c \brief DNS query functions */ #include #include #include "qdns.h" #include "libowfatconn.h" /** * get info out of the DNS * * @param name the name to look up * @param result first element of a list of results will be placed * @return 0 on success * 1 if host is not existent * 2 if temporary DNS error * 3 if permanent DNS error * -1 on error */ int ask_dnsmx(const char *name, struct ips **result) { int i; char *r; unsigned int l = 0; i = dnsmx(&r, &l, name); if (!i || ((i < 0) && (errno == ENOENT)) ) { char *s = r; struct ips **q = result; int errtype = 0; /* there is no MX record, so we look for an AAAA record */ if (!l) { int rc = ask_dnsaaaa(name, result); if (!rc) { struct ips *a = *result; while (a) { /* the DNS priority is 2 bytes long so 65536 can never be returned from a real DNS_MX lookup */ a->priority = 65536; a = a->next; } } return rc; } while (r + l > s) { struct ips *p; int rc; rc = ask_dnsaaaa(s + 2, &p); if (rc < 0) { freeips(*result); free(r); if (errno == ENOMEM) return -1; return 2; } else if (!rc) { struct ips *u; unsigned int pri = ntohs(*((unsigned short *) s)); /* add the new results to the list */ *q = p; /* set priority for each of the new entries */ for (u = p; u; u = p->next) { u->priority = pri; p = u; } q = &(p->next); } else { errtype = (1 << rc); } s += 3 + strlen(s + 2); } free(r); if (!*result) { if (errtype & 4) { return 2; } else if (errtype & 2) { return 1; } else { return 3; } } return 0; } switch (errno) { case ETIMEDOUT: case EAGAIN: return 2; case ENFILE: case EMFILE: case ENOBUFS: errno = ENOMEM; case ENOMEM: return -1; case ENOENT: return 1; default: return 3; } } /** * get AAAA record from of the DNS * * @param name the name to look up * @param result first element of a list of results will be placed * @return 0 on success * 1 if host is not existent * 2 if temporary DNS error * 3 if permanent DNS error * -1 on error */ int ask_dnsaaaa(const char *name, struct ips **result) { int i; char *r; unsigned int l; i = dnsip6(&r, &l, name); if (!i) { char *s = r; struct ips **q = result; if (!l) { *result = NULL; return 1; } while (r + l > s) { struct ips *p = malloc(sizeof(*p)); if (!p) { freeips(*result); free(r); return -1; } *q = p; p->next = NULL; memcpy(&(p->addr), s, 16); q = &(p->next); s += 16; } free(r); return 0; } free(r); switch (errno) { case ETIMEDOUT: case EAGAIN: return 2; case ENFILE: case EMFILE: case ENOBUFS: errno = ENOMEM; case ENOMEM: return -1; case ENOENT: return 1; default: return 3; } } /** * get A record from of the DNS * * @param name the name to look up * @param result first element of a list of results will be placed, or NULL if only return code is of interest * @return 0 on success * 1 if host is not existent * 2 if temporary DNS error * 3 if permanent DNS error * -1 on error */ int ask_dnsa(const char *name, struct ips **result) { int i; char *r; unsigned int l; i = dnsip4(&r, &l, name); if (!i) { if (result) { char *s = r; struct ips **q = result; if (!l) { *result = NULL; return 1; } while (r + l > s) { struct ips *p = malloc(sizeof(*p)); if (!p) { freeips(*result); free(r); return -1; } *q = p; p->next = NULL; p->addr.s6_addr32[0] = 0; p->addr.s6_addr32[1] = 0; p->addr.s6_addr32[2] = htonl(0xffff); memcpy(&(p->addr.s6_addr32[3]), s, 4); q = &(p->next); s += 4; } } free(r); return l ? 0 : 1; } switch (errno) { case ETIMEDOUT: case EAGAIN: return 2; case ENFILE: case EMFILE: case ENOBUFS: errno = ENOMEM; case ENOMEM: return -1; case ENOENT: return 1; default: return 3; } } /** * get host name for IP address * * @param ip the IP to look up * @param result name will be stored here * @return 0 on success * 1 if host is not existent * 2 if temporary DNS error * 3 if permanent DNS error * -1 on error */ int ask_dnsname(const struct in6_addr *ip, char **result) { int r; r = dnsname(result, ip->s6_addr); if (!r) return *result ? 0 : 1; switch (errno) { case ETIMEDOUT: case EAGAIN: return 2; case ENFILE: case EMFILE: case ENOBUFS: errno = ENOMEM; case ENOMEM: return -1; case ENOENT: return 1; default: return 3; } }