#include "dns.h" #include #include #include #include #include #include "alloc.h" #include "ip.h" #include "str.h" #include "stralloc.h" #include "ipalloc.h" /** * @file dns.c * @brief DNS helpers: dns_ipplus, dns_ipalloc, dns_ip (IPv4+IPv6), dns_mxip */ static stralloc glue = {0}; static stralloc ip = {0}; static int dns_ipplus(ipalloc *ia, stralloc *sa, int pref) { struct ip_mx ix; int error = 0; char ip4[4]; char ip6[16]; int i; /* Case 1: sa is just IPv4 */ if (ip4_scanbracket(sa->s, ip4)) { if (!stralloc_copys(&glue, sa->s)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { ix.pref = 0; ix.af = AF_INET; byte_copy(&ix.addr, 4, ip4); // = ip; //cp if (!ipalloc_append(ia, &ix)) return DNS_MEM; return 0; } } /* Case 2: sa is just IPv6 */ if (ip6_scanbracket(sa->s, ip6)) { if (!stralloc_copys(&glue, sa->s)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { ix.pref = 0; ix.af = AF_INET6; byte_copy(&ix.addr, 16, ip6); // = ip; //cp if (!ipalloc_append(ia, &ix)) return DNS_MEM; return 0; } } /* Case 3: sa is fqdn and looking for IPv6 */ if (dns_ip6(&ip, sa) > 0) { for (i = 0; i + 16 <= ip.len; i += 16) { if (ip6_isv4mapped(ip.s + i)) continue; ix.af = AF_INET6; ix.pref = pref; byte_copy(&ix.addr, 16, ip.s + i); // = ip; //cp str_copy(ix.mxh, sa->s); // mx hostname if (!ipalloc_append(ia, &ix)) { error = DNS_MEM; break; } error = 0; } } else { error = 1; } /* Case 4: sa is fqdn and looking for IPv4 */ if (dns_ip4(&ip, sa) > 0) { for (i = 0; i + 4 <= ip.len; i += 4) { ix.af = AF_INET; ix.pref = pref; byte_copy(&ix.addr, 4, ip.s + i); // = ip; //cp str_copy(ix.mxh, sa->s); // mx hostname if (!ipalloc_append(ia, &ix)) { error = DNS_MEM; break; } error = 0; } } else { error += 2; } return error; } static int dns_ipalloc(ipalloc *ia, stralloc *sa) { if (!ipalloc_readyplus(ia, 0)) return DNS_MEM; ia->len = 0; return dns_ipplus(ia, sa, 0); } /* dns_mxip */ int dns_mxip(ipalloc *ia, stralloc *sa, unsigned long random) { struct mx { stralloc sa; unsigned short p; } *mx; struct ip_mx ix; int nummx; int i; int j = 0; int len; int flagsoft; uint16 pref; /* Case 1: sa is just IPv4 or IPv6 */ if (!ipalloc_readyplus(ia, 0)) return DNS_MEM; ia->len = 0; if (!stralloc_copys(&glue, sa->s)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { ix.pref = 0; if (!glue.s[ip4_scan(glue.s, (char *)&ix.addr.ip4)] || !glue.s[ip4_scanbracket(glue.s, (char *)&ix.addr.ip4)]) { ix.af = AF_INET; if (!ipalloc_append(ia, &ix)) return DNS_MEM; return 0; } if (!glue.s[ip6_scan(glue.s, (char *)&ix.addr.ip6)] || !glue.s[ip6_scanbracket(glue.s, (char *)&ix.addr.ip6)]) { ix.af = AF_INET6; if (!ipalloc_append(ia, &ix)) return DNS_MEM; return 0; } } /* Case 2: sa is FQDN and do a mx lookup */ DNS_INIT nummx = 0; len = 0; i = dns_mx(&ip, sa); mx = (struct mx *)alloc(i * sizeof(struct mx)); if (!mx) return DNS_MEM; if (i) { do { j = str_chr(ip.s + len + 2, '\0'); /* several answers */ mx[nummx].sa.s = 0; if (!stralloc_copys(&mx[nummx].sa, ip.s + len + 2)) { /* mxhost name */ alloc_free(mx); return DNS_MEM; } ip.s[len + 3] = '\0'; uint16_unpack_big(ip.s + len, &pref); mx[nummx].p = pref; len += j + 3; ++nummx; } while (len < ip.len); } if (!nummx) return dns_ipalloc(ia, sa); /* e.g., CNAME -> A */ flagsoft = 0; while (nummx > 0) { unsigned long numsame; i = 0; numsame = 1; for (j = 1; j < nummx; ++j) { if (mx[j].p < mx[i].p) { i = j; numsame = 1; } else if (mx[j].p == mx[i].p) { ++numsame; random = random * 69069 + 1; if ((random / 2) < (2147483647 / numsame)) i = j; } } switch (dns_ipplus(ia, &mx[i].sa, mx[i].p)) { case -1: return DNS_MEM; case -2: case -3: flagsoft = -5; break; } alloc_free(mx[i].sa.s); mx[i] = mx[--nummx]; } alloc_free(mx); return flagsoft; } int dns_ip(ipalloc *ia, stralloc *sa) { if (!ipalloc_readyplus(ia, 0)) return DNS_MEM; ia->len = 0; return dns_ipplus(ia, sa, 0); }