Viewing file:
rfc1035mxlist.c (9.33 KB) -rw-rw-rw-Select action/file-type:

(
+) |

(
+) |

(
+) |
Code (
+) |
Session (
+) |

(
+) |
SDB (
+) |

(
+) |

(
+) |

(
+) |

(
+) |

(
+) |
/*
** Copyright 1998 - 2006 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"
#include "rfc1035_res.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 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->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, 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,
RFC1035_RESOLVE_RECURSIVE, 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, 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, port))
return (RFC1035_MX_INTERNAL);
return (RFC1035_MX_OK);
}
index=rfc1035_resolve_cname(res,
RFC1035_RESOLVE_RECURSIVE, 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);
}