diff options
Diffstat (limited to 'rfc1035/rfc1035qa.c')
| -rw-r--r-- | rfc1035/rfc1035qa.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/rfc1035/rfc1035qa.c b/rfc1035/rfc1035qa.c new file mode 100644 index 0000000..7335624 --- /dev/null +++ b/rfc1035/rfc1035qa.c @@ -0,0 +1,238 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include "rfc1035.h" +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <arpa/inet.h> + +/* Convenient function to do forward IP lookup */ + +#if RFC1035_IPV6 + +static int rfc1035_a_ipv4(struct rfc1035_res *res, + const char *name, struct in_addr **iaptr, unsigned *iasize) +#else +int rfc1035_a(struct rfc1035_res *res, + const char *name, RFC1035_ADDR **iaptr, unsigned *iasize) +#endif +{ +struct rfc1035_reply *reply; +int n, o; +char namebuf[RFC1035_MAXNAMESIZE+1]; + + namebuf[0]=0; + strncat(namebuf, name, RFC1035_MAXNAMESIZE); + + *iasize=0; + if (rfc1035_resolve_cname(res, namebuf, + RFC1035_TYPE_A, RFC1035_CLASS_IN, &reply, 0) < 0 || + reply == 0 || + (n=rfc1035_replysearch_an( res, reply, namebuf, RFC1035_TYPE_A, + RFC1035_CLASS_IN, 0)) < 0) + { + if (reply && reply->rcode != RFC1035_RCODE_NXDOMAIN && + reply->rcode != RFC1035_RCODE_NOERROR) + { + errno=EAGAIN; + rfc1035_replyfree(reply); + return (1); /* soft error */ + } + + if (reply) rfc1035_replyfree(reply); + errno=ENOENT; + return (-1); /* hard error */ + } + + for (o=n; o >= 0; o=rfc1035_replysearch_an(res, reply, namebuf, + RFC1035_TYPE_A, RFC1035_CLASS_IN, o+1)) + ++*iasize; + + if ( *iasize == 0 ) + { + errno=EAGAIN; + rfc1035_replyfree(reply); + return (-1); + } + + if ( (*iaptr=(struct in_addr *)malloc(sizeof(**iaptr)* *iasize)) == 0) + { + rfc1035_replyfree(reply); + return (-1); + } + + for (*iasize=0; n >= 0; n=rfc1035_replysearch_an(res, reply, namebuf, + RFC1035_TYPE_A, RFC1035_CLASS_IN, n+1)) + { + (*iaptr)[*iasize]= reply->allrrs[n]->rr.inaddr; + ++*iasize; + } + + rfc1035_replyfree(reply); + return (0); +} + +#if RFC1035_IPV6 + +/* +** The IPv6 version issues two queries - for both A and AAAA records, +** then maps any A record to IPv6. +** +** If we get back both an AAAA for the IPv4-mapped address, and the +** A record itself, ignore the dupe. +*/ + +static int we_have_that_ipv4(struct in6_addr in6, + const struct in_addr *ia4ptr, + unsigned ia4len) +{ +char buf[INET6_ADDRSTRLEN]; +const char *p; +struct in_addr in4; +unsigned i; + + if (!IN6_IS_ADDR_V4MAPPED((&in6))) return (0); /* Not an IPv4 addy */ + + if (inet_ntop(AF_INET6, &in6, buf, sizeof(buf)) == 0) + return (0); /* WTF??? */ + + if ((p=strrchr(buf, ':')) != 0) + ++p; + else + p=buf; + + rfc1035_aton_ipv4(p, &in4); + + for (i=0; i<ia4len; i++) + if (ia4ptr[i].s_addr == in4.s_addr) return (1); + return (0); +} + +int rfc1035_a(struct rfc1035_res *res, + const char *name, struct in6_addr **iaptr, unsigned *iasize) +{ +struct rfc1035_reply *reply; +int n, o; +char namebuf[RFC1035_MAXNAMESIZE+1]; +int enotfound=0; + +struct in_addr *ia4ptr; +unsigned ia4len; +unsigned k; + + n=rfc1035_a_ipv4(res, name, &ia4ptr, &ia4len); + + if (n > 0) return (n); + if (n < 0) + { + if (errno != ENOENT) return (n); + ia4len=0; + ia4ptr=0; + enotfound=1; + } + + namebuf[0]=0; + strncat(namebuf, name, RFC1035_MAXNAMESIZE); + + *iasize=ia4len; + reply=0; + + /* + ** Resist the temptation to stick in "ia4len > 0 &&", below. Why? + ** A) We get a connection from an IPv6 address. + ** B) Spam check: the IP address must resolve backwards and forwards. + ** C) There are IPv4 records for the same hostname as well. + ** D) This sux. + */ + + if (rfc1035_resolve_cname(res, namebuf, + RFC1035_TYPE_AAAA, RFC1035_CLASS_IN, &reply, 0) < 0 || + reply == 0 || + (n=rfc1035_replysearch_an( res, reply, namebuf, + RFC1035_TYPE_AAAA, + RFC1035_CLASS_IN, 0)) < 0) + { + if (reply && reply->rcode != RFC1035_RCODE_NXDOMAIN && + reply->rcode != RFC1035_RCODE_NOERROR && + *iasize == 0) + /* Unfortunately this is necessary. Some swervers + ** return a TEMPFAIL for AAAA queries. + */ + { + errno=EAGAIN; + rfc1035_replyfree(reply); + if (ia4len) + free(ia4ptr); + return (1); /* soft error */ + } + + if (reply) rfc1035_replyfree(reply); + if (enotfound) + return (-1); + enotfound=1; + reply=0; + n= -1; + } + else + { + for (o=n; o >= 0; o=rfc1035_replysearch_an(res, reply, namebuf, + RFC1035_TYPE_AAAA, RFC1035_CLASS_IN, o+1)) + { + if (we_have_that_ipv4(reply->allrrs[n]->rr.in6addr, + ia4ptr, ia4len)) + continue; + + ++*iasize; + } + } + + if ( *iasize == 0 && enotfound) + { + if (ia4len) + free(ia4ptr); + errno=ENOENT; + return (-1); + } + + if ( *iasize == 0 ) + { + errno=EAGAIN; + rfc1035_replyfree(reply); + return (-1); + } + + if ( (*iaptr=(struct in6_addr *)malloc(sizeof(**iaptr)* *iasize)) == 0) + { + rfc1035_replyfree(reply); + return (-1); + } + + for (*iasize=0; n >= 0; n=rfc1035_replysearch_an(res, reply, namebuf, + RFC1035_TYPE_AAAA, RFC1035_CLASS_IN, n+1)) + { + if (we_have_that_ipv4(reply->allrrs[n]->rr.in6addr, + ia4ptr, ia4len)) + continue; + (*iaptr)[*iasize]= reply->allrrs[n]->rr.in6addr; + ++*iasize; + } + + for (k=0; k<ia4len; k++) + { + char buf[INET6_ADDRSTRLEN]; + + strcpy(buf, "::ffff:"); + rfc1035_ntoa_ipv4( &ia4ptr[k], buf+sizeof("::ffff:")-1); + if (inet_pton( AF_INET6, buf, (*iaptr)+ *iasize) <= 0) + memset( (*iaptr)+ *iasize, 0, sizeof(*iaptr)); + ++*iasize; + } + if (ia4len) + free (ia4ptr); + rfc1035_replyfree(reply); + return (0); +} +#endif |
