summaryrefslogtreecommitdiff
path: root/src/dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns.c')
-rw-r--r--src/dns.c201
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);
+}