diff options
Diffstat (limited to 'src/dns.c')
-rw-r--r-- | src/dns.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/dns.c b/src/dns.c new file mode 100644 index 0000000..3f1154b --- /dev/null +++ b/src/dns.c @@ -0,0 +1,201 @@ +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <sys/socket.h> +#include "ip.h" +#include "ipalloc.h" +#include "fmt.h" +#include "alloc.h" +#include "str.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "case.h" +#include "dns.h" +#include "buffer.h" +#include "exit.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; +} + +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); +} |