diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 |
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 |
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /rfc1035/rfc1035mxlist.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'rfc1035/rfc1035mxlist.c')
| -rw-r--r-- | rfc1035/rfc1035mxlist.c | 448 |
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); +} |
