summaryrefslogtreecommitdiffstats
path: root/rfc1035/rfc1035mxlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'rfc1035/rfc1035mxlist.c')
-rw-r--r--rfc1035/rfc1035mxlist.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/rfc1035/rfc1035mxlist.c b/rfc1035/rfc1035mxlist.c
new file mode 100644
index 0000000..557210f
--- /dev/null
+++ b/rfc1035/rfc1035mxlist.c
@@ -0,0 +1,448 @@
+/*
+** Copyright 1998 - 2011 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rfc1035.h"
+#include "rfc1035mxlist.h"
+
+void rfc1035_mxlist_free(struct rfc1035_mxlist *p)
+{
+struct rfc1035_mxlist *q;
+
+ while (p)
+ {
+ q=p->next;
+ if (p->hostname) free(p->hostname);
+ free(p);
+ p=q;
+ }
+}
+
+static int addrecord(struct rfc1035_mxlist **list, const char *mxname,
+ int mxpreference,
+
+#if RFC1035_IPV6
+ struct in6_addr *in,
+#else
+ struct in_addr *in,
+#endif
+ int ad,
+ int port)
+{
+#if RFC1035_IPV6
+struct sockaddr_in6 sin;
+#else
+struct sockaddr_in sin;
+#endif
+struct rfc1035_mxlist *p;
+
+ if ((p=(struct rfc1035_mxlist *)malloc(sizeof(struct rfc1035_mxlist)))
+ == 0 || (p->hostname=malloc(strlen(mxname)+1)) == 0)
+ {
+ if (p) free ( (char *)p);
+ return (-1);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+
+#if RFC1035_IPV6
+ sin.sin6_family=AF_INET6;
+ sin.sin6_addr= *in;
+ sin.sin6_port=htons(port);
+ p->protocol=PF_INET6;
+#else
+ sin.sin_family=AF_INET;
+ sin.sin_addr= *in;
+ sin.sin_port=htons(port);
+ p->protocol=PF_INET;
+#endif
+
+ while ( *list && (*list)->priority < mxpreference )
+ list= &(*list)->next;
+
+ p->next=*list;
+ *list=p;
+ p->ad=ad;
+ p->priority=mxpreference;
+ strcpy(p->hostname, mxname);
+ memcpy(&p->address, &sin, sizeof(sin));
+ return (0);
+}
+
+static int harvest_records(struct rfc1035_res *res,
+ struct rfc1035_mxlist **list,
+ struct rfc1035_reply *mxreply,
+ int mxpreference,
+ char *mxname,
+ int q_type, int *found, int autoquery, int port);
+
+#define HARVEST_AUTOQUERY 1
+#define HARVEST_NODUPE 2
+
+static int add_arecords(struct rfc1035_res *res, struct rfc1035_mxlist **list,
+ struct rfc1035_reply *mxreply,
+ int mxpreference,
+ char *mxname, int port)
+{
+#if RFC1035_IPV6
+struct in6_addr in;
+#else
+struct in_addr in;
+#endif
+int found=0;
+int rc;
+
+ if (rfc1035_aton(mxname, &in) == 0)
+ { /* Broken MX record */
+ char buf[RFC1035_NTOABUFSIZE];
+
+ rfc1035_ntoa(&in, buf);
+
+ if (addrecord(list, buf, mxpreference, &in, 0, port))
+ return (RFC1035_MX_INTERNAL);
+
+ return (RFC1035_MX_OK);
+ }
+
+#if RFC1035_IPV6
+
+/*
+ Here's the IPv6 strategy:
+
+If we have an existing MX record to work with, try to harvest
+both A and AAAA addresses from it. If we find either an A or an AAAA
+record, stop.
+
+If we don't have an existing MX record, or we didn't find A or AAAA
+records, then query for A records. Query for AAAA records only if A
+records weren't found.
+
+*/
+ if (mxreply)
+ {
+ if ((rc=harvest_records(res, list, mxreply, mxpreference,
+ mxname, RFC1035_TYPE_AAAA, &found, 0, port))
+ != RFC1035_MX_OK)
+ return (rc);
+
+ if ((rc=harvest_records(res, list, mxreply, mxpreference,
+ mxname, RFC1035_TYPE_A, &found, HARVEST_NODUPE,
+ port))
+ != RFC1035_MX_OK)
+ return (rc);
+ if (found) return (RFC1035_MX_OK);
+ }
+
+ if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
+ RFC1035_TYPE_A, &found, HARVEST_AUTOQUERY|HARVEST_NODUPE, port))
+ != RFC1035_MX_OK)
+ return (rc);
+ if (found) return (RFC1035_MX_OK);
+
+ if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
+ RFC1035_TYPE_AAAA, &found, HARVEST_AUTOQUERY, port))
+ != RFC1035_MX_OK)
+ return (rc);
+
+#else
+ if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
+ RFC1035_TYPE_A, &found, HARVEST_AUTOQUERY, port))
+ != RFC1035_MX_OK)
+ return (rc);
+#endif
+
+ if (!found) return (RFC1035_MX_HARDERR);
+ return (RFC1035_MX_OK);
+}
+
+static int harvest_records(struct rfc1035_res *res,
+ struct rfc1035_mxlist **list,
+ struct rfc1035_reply *mxreply,
+ int mxpreference,
+ char *mxname,
+ int q_type, int *found,
+ int flags, int port)
+{
+struct rfc1035_reply *areply=0;
+int index;
+#if RFC1035_IPV6
+struct in6_addr in;
+#else
+struct in_addr in;
+#endif
+
+ index= -1;
+
+ if (!mxreply || (
+ ((index=rfc1035_replysearch_all( res, mxreply, mxname,
+ q_type,
+ RFC1035_CLASS_IN,
+ 0)) < 0 ||
+ mxreply->allrrs[index]->rrtype != q_type)
+ && (flags & HARVEST_AUTOQUERY))
+ )
+ {
+ index=rfc1035_resolve_cname(res, mxname,
+ q_type,
+ RFC1035_CLASS_IN, &areply, RFC1035_X_RANDOMIZE);
+ if (index < 0)
+ {
+ if (!areply)
+ {
+ if (index == RFC1035_ERR_CNAME_RECURSIVE)
+ return (RFC1035_MX_BADDNS);
+ return (RFC1035_MX_INTERNAL);
+ }
+
+ if (areply->rcode == RFC1035_RCODE_NXDOMAIN ||
+ areply->rcode == RFC1035_RCODE_NOERROR)
+ {
+ rfc1035_replyfree(areply);
+ return (RFC1035_MX_OK);
+ }
+ rfc1035_replyfree(areply);
+ return (RFC1035_MX_SOFTERR);
+ }
+ mxreply=areply;
+ }
+
+ for ( ; index >= 0 ;
+ index=rfc1035_replysearch_all( res, mxreply, mxname,
+ q_type,
+ RFC1035_CLASS_IN,
+ index+1))
+ {
+ if (mxreply->allrrs[index]->rrtype != q_type)
+ continue;
+
+#if RFC1035_IPV6
+ if (q_type == RFC1035_TYPE_A)
+ {
+ struct rfc1035_mxlist *q;
+
+ /* Map it to an IPv4 address */
+
+ rfc1035_ipv4to6(&in,
+ &mxreply->allrrs[index]->rr.inaddr);
+
+ /* See if it's already here */
+
+ for (q= *list; q; q=q->next)
+ {
+ struct sockaddr_in6 sin6;
+
+ if (q->protocol != PF_INET6)
+ continue;
+ memcpy(&sin6, &q->address, sizeof(sin6));
+
+ if (memcmp(&sin6.sin6_addr, &in, sizeof(in))
+ == 0 && q->priority == mxpreference)
+ break;
+ }
+ if ((flags & HARVEST_NODUPE) && q) continue;
+ }
+ else
+ in=mxreply->allrrs[index]->rr.in6addr;
+#else
+ in.s_addr=mxreply->allrrs[index]->rr.inaddr.s_addr;
+#endif
+ *found=1;
+ if (addrecord(list, mxname, mxpreference, &in, mxreply->ad,
+ port))
+ {
+ if (areply)
+ rfc1035_replyfree(areply);
+ return (RFC1035_MX_INTERNAL);
+ }
+ }
+ if (areply)
+ rfc1035_replyfree(areply);
+
+ return (RFC1035_MX_OK);
+}
+
+static int domxlistcreate(struct rfc1035_res *res,
+ const char *q_name, int opts,
+ struct rfc1035_mxlist **list, int port)
+{
+char namebuf[RFC1035_MAXNAMESIZE+1];
+struct rfc1035_reply *replyp;
+int index;
+RFC1035_ADDR in;
+int seen_softerr=0;
+int seen_good=0;
+
+ *list=0;
+ if (rfc1035_aton(q_name, &in) == 0)
+ return (RFC1035_MX_HARDERR); /* Don't gimme an IP address */
+
+ namebuf[0]=0;
+ strncat(namebuf, q_name, RFC1035_MAXNAMESIZE);
+ if (namebuf[0] == '[')
+ {
+ char *q=strchr(namebuf, ']');
+
+ if (!q || q[1]) return (RFC1035_MX_HARDERR); /* Bad addr */
+ *q=0;
+ if (rfc1035_aton(namebuf+1, &in))
+ return (RFC1035_MX_HARDERR);
+
+ if (addrecord(list, q_name, -1, &in, 0, port))
+ return (RFC1035_MX_INTERNAL);
+ return (RFC1035_MX_OK);
+ }
+
+ index=rfc1035_resolve_cname(res, namebuf,
+ RFC1035_TYPE_MX,
+ RFC1035_CLASS_IN, &replyp, RFC1035_X_RANDOMIZE);
+
+ if (index < 0)
+ {
+ if (!replyp)
+ {
+ if (index == RFC1035_ERR_CNAME_RECURSIVE)
+ return (RFC1035_MX_BADDNS);
+ return (RFC1035_MX_INTERNAL);
+ }
+
+ if (replyp->rcode == RFC1035_RCODE_NXDOMAIN ||
+ replyp->rcode == RFC1035_RCODE_NOERROR)
+ {
+ rfc1035_replyfree(replyp);
+ strcpy(namebuf, q_name);
+
+ if (opts & RFC1035_MX_AFALLBACK)
+ return (add_arecords(res, list, 0, -1,
+ namebuf, port));
+ return RFC1035_MX_HARDERR;
+ }
+
+ rfc1035_replyfree(replyp);
+ return (RFC1035_MX_SOFTERR);
+ }
+
+ for ( ; index >= 0;
+ index=rfc1035_replysearch_all( res, replyp, namebuf,
+ RFC1035_TYPE_MX,
+ RFC1035_CLASS_IN,
+ index+1))
+ {
+ char mxname[RFC1035_MAXNAMESIZE+1];
+
+ if (replyp->allrrs[index]->rrtype != RFC1035_TYPE_MX)
+ continue;
+
+ if (rfc1035_replyhostname(replyp,
+ replyp->allrrs[index]->rr.mx.mx_label, mxname) == 0)
+ continue;
+
+ switch (add_arecords(res, list, replyp,
+ replyp->allrrs[index]->rr.mx.preference, mxname,
+ port)) {
+ case RFC1035_MX_SOFTERR:
+ seen_softerr=1;
+ continue;
+ case RFC1035_MX_INTERNAL:
+ rfc1035_replyfree(replyp);
+ return (RFC1035_MX_INTERNAL);
+ case RFC1035_MX_BADDNS:
+ rfc1035_replyfree(replyp);
+ return (RFC1035_MX_BADDNS);
+ default:
+ seen_good=1;
+ continue;
+ }
+ }
+
+ rfc1035_replyfree(replyp);
+
+ if (seen_good && (opts & RFC1035_MX_IGNORESOFTERR))
+ seen_softerr=0;
+ /* At least some A records were probably fetched */
+
+ if (seen_softerr)
+ return (RFC1035_MX_SOFTERR);
+
+ if (*list) return (RFC1035_MX_OK);
+ return (RFC1035_MX_HARDERR);
+}
+
+static int domxlistcreate2(struct rfc1035_res *res,
+ const char *q_name,
+ int opts,
+ struct rfc1035_mxlist **list,
+ int port)
+{
+char *buf;
+int rc;
+
+ if (strchr(q_name, '.') || strchr(q_name, ':') ||
+ !res->rfc1035_defaultdomain)
+ return (domxlistcreate(res, q_name, opts, list, port));
+
+ if ((buf=malloc(strlen(q_name)+
+ strlen(res->rfc1035_defaultdomain)+2)) == 0)
+ return (-1);
+
+ strcat(strcat(strcpy(buf, q_name), "."), res->rfc1035_defaultdomain);
+
+ rc=domxlistcreate(res, buf, opts, list, port);
+
+ free(buf);
+ return (rc);
+}
+
+static int domxlistcreate3(struct rfc1035_res *res,
+ const char *q_name,
+ int opts,
+ struct rfc1035_mxlist **list)
+{
+char *buf;
+int rc;
+const char *p;
+
+ p=strchr(q_name, ',');
+
+ if (p == 0) return (domxlistcreate2(res, q_name, opts, list, 25));
+
+ if ((buf=malloc(p-q_name+1)) == 0)
+ return (-1);
+
+ memcpy(buf, q_name, p-q_name);
+ buf[p-q_name]=0;
+
+ rc=domxlistcreate2(res, buf, opts, list, atoi(p+1));
+
+ free(buf);
+ return (rc);
+}
+
+int rfc1035_mxlist_create_x(struct rfc1035_res *res,
+ const char *q_name,
+ int opts,
+ struct rfc1035_mxlist **list)
+{
+int rc=domxlistcreate3(res, q_name, opts, list);
+
+ if (rc != RFC1035_MX_OK)
+ {
+ rfc1035_mxlist_free(*list);
+ *list=0;
+ }
+ return (rc);
+}
+
+int rfc1035_mxlist_create(struct rfc1035_res *res,
+ const char *q_name,
+ struct rfc1035_mxlist **list)
+{
+ return rfc1035_mxlist_create_x(res, q_name,
+ RFC1035_MX_AFALLBACK |
+ RFC1035_MX_IGNORESOFTERR,
+ list);
+}