diff options
author | Jakob Schlyter <jakob@cvs.openbsd.org> | 2001-08-06 14:40:48 +0000 |
---|---|---|
committer | Jakob Schlyter <jakob@cvs.openbsd.org> | 2001-08-06 14:40:48 +0000 |
commit | 6d6b959d90ab71646ae3d8e9b0798a7179fbf725 (patch) | |
tree | caf89438af58cfce3fc42814fb4f861d8b7ef0a9 /lib | |
parent | bd67df5d2888cbfc629fc90ac4de2ff35b095391 (diff) |
add getrrsetbyname(3) - API to retrieve arbitrary DNS records
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libc/net/Makefile.inc | 11 | ||||
-rw-r--r-- | lib/libc/net/getrrsetbyname.3 | 149 | ||||
-rw-r--r-- | lib/libc/net/getrrsetbyname.c | 506 | ||||
-rw-r--r-- | lib/libc/shlib_version | 2 |
4 files changed, 662 insertions, 6 deletions
diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 641d6121051..805ae5d2247 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.inc,v 1.30 2000/02/23 15:39:53 itojun Exp $ +# $OpenBSD: Makefile.inc,v 1.31 2001/08/06 14:40:47 jakob Exp $ # net sources .PATH: ${LIBCSRCDIR}/arch/${MACHINE_ARCH}/net ${LIBCSRCDIR}/net @@ -8,8 +8,8 @@ CFLAGS+=-DRESOLVSORT SRCS+= base64.c freeaddrinfo.c gai_strerror.c getaddrinfo.c gethostnamadr.c \ getifaddrs.c getnameinfo.c getnetbyaddr.c getnetbyname.c getnetent.c \ getnetnamadr.c getproto.c getprotoent.c getprotoname.c \ - getservbyname.c getservbyport.c getservent.c herror.c \ - if_indextoname.c if_nameindex.c if_nametoindex.c inet_addr.c \ + getservbyname.c getservbyport.c getservent.c getrrsetbyname.c \ + herror.c if_indextoname.c if_nameindex.c if_nametoindex.c inet_addr.c \ inet_lnaof.c inet_makeaddr.c inet_neta.c inet_netof.c inet_network.c \ inet_net_ntop.c inet_net_pton.c inet_ntoa.c inet_ntop.c inet_pton.c \ ipx_addr.c ipx_ntoa.c iso_addr.c linkaddr.c ns_addr.c ns_ntoa.c \ @@ -32,8 +32,8 @@ MAN+= byteorder.3 ethers.3 getaddrinfo.3 gethostbyname.3 getifaddrs.3 \ getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 inet.3 \ if_indextoname.3 inet_net.3 iso_addr.3 link_addr.3 ns.3 ipx.3 \ rcmd.3 rcmdsh.3 resolver.3 net_addrcmp.3 \ - inet6_option_space.3 inet6_rthdr_space.3 - + inet6_option_space.3 inet6_rthdr_space.3 \ + getrrsetbyname.3 MLINKS+=byteorder.3 htonl.3 byteorder.3 htons.3 byteorder.3 ntohl.3 \ byteorder.3 ntohs.3 byteorder.3 htobe16.3 byteorder.3 htobe32.3 \ @@ -82,3 +82,4 @@ MLINKS+=inet6_rthdr_space.3 inet6_rthdr_init.3 \ inet6_rthdr_space.3 inet6_rthdr_segments.3 \ inet6_rthdr_space.3 inet6_rthdr_getaddr.3 \ inet6_rthdr_space.3 inet6_rthdr_getflags.3 +MLINKS+=getrrsetbyname.3 freerrset.3 diff --git a/lib/libc/net/getrrsetbyname.3 b/lib/libc/net/getrrsetbyname.3 new file mode 100644 index 00000000000..3b8c06999ec --- /dev/null +++ b/lib/libc/net/getrrsetbyname.3 @@ -0,0 +1,149 @@ +.\" $Id: getrrsetbyname.3,v 1.1 2001/08/06 14:40:47 jakob Exp $ +.\" +.\" Copyright (C) 2000, 2001 Internet Software Consortium. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM +.\" DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +.\" INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd Oct 18, 2000 +.Dt GETRRSETBYNAME 3 +.Os +.Sh NAME +.Nm getrrsetbyname +.Nd retrieve DNS records +.Sh SYNOPSIS +.Fd #include <netdb.h> +.Ft int +.Fn getrrsetbyname "const char *hostname" "unsigned int rdclass" \ +"unsigned int rdtype" "unsigned int flags" "struct rrsetinfo **res" +.Ft int +.Fn freerrset "struct rrsetinfo **rrset" +.Sh DESCRIPTION +.Fn getrrsetbyname +gets a set of resource records associated with a +.Fa hostname , +.Fa class +and +.Fa type . +.Fa hostname +is a pointer a to null-terminated string. +The +.Fa flags +field is currently unused and must be zero. +.Pp +After a successful call to +.Fn getrrsetbyname , +.Fa *res +is a pointer to an +.Li rrsetinfo +structure, containing a list of one or more +.Li rdatainfo +structures containing resource records and potentially another list of +.Li rdatainfo +structures containing SIG resource records associated with those records. +The members +.Li rri_rdclass +and +.Li rri_rdtype +are copied from the parameters. +.Li rri_ttl +and +.Li rri_name +are properties of the obtained rrset. +The resource records contained in +.Li rri_rdatas +and +.Li rri_sigs +are in uncompressed DNS wire format. +Properties of the rdataset are represented in the +.Li rri_flags +bitfield. If the +.Dv RRSET_VALIDATED +bit is set, the data has been DNSSEC +validated and the signatures verified. +.Pp +The following structures are used: +.Bd -literal -offset +struct rdatainfo { + unsigned int rdi_length; /* length of data */ + unsigned char *rdi_data; /* record data */ +}; + +struct rrsetinfo { + unsigned int rri_flags; /* RRSET_VALIDATED ... */ + unsigned int rri_rdclass; /* class number */ + unsigned int rri_rdtype; /* RR type number */ + unsigned int rri_ttl; /* time to live */ + unsigned int rri_nrdatas; /* size of rdatas array */ + unsigned int rri_nsigs; /* size of sigs array */ + char *rri_name; /* canonical name */ + struct rdatainfo *rri_rdatas; /* individual records */ + struct rdatainfo *rri_sigs; /* individual signatures */ +}; +.Ed +.Pp +All of the information returned by +.Fn getrrsetbyname +is dynamically allocated: the +.Li rrsetinfo +and +.Li rdatainfo +structures, +and the canonical host name strings pointed to by the +.Li rrsetinfostructure. +Memory allocated for the dynamically allocated structures created by +a successful call to +.Fn getrrsetbyname +is released by +.Fn freerrset . +.Li rrset +is a pointer to a +.Li struct rrset +created by a call to +.Fn getrrsetbyname . +.Pp +If the EDNS0 option is activated in +.Xr resolv.conf 3 , +.Fn getrrsetbyname +will request DNSSEC authentication using the EDNS0 DNSSEC OK (DO) bit. +.Sh "RETURN VALUES" +.Fn getrrsetbyname +returns zero on success, and one of the following error +codes if an error occurred: +.Pp +.Bl -tag -width ERRSET_NOMEMORY -compact +.It Dv ERRSET_NONAME +the name does not exist +.It Dv ERRSET_NODATA +the name exists, but does not have data of the desired type +.It Dv ERRSET_NOMEMORY +memory could not be allocated +.It Dv ERRSET_INVAL +a parameter is invalid +.It Dv ERRSET_FAIL +other failure +.El +.Sh SEE ALSO +.Xr resolver 3 , +.Xr resolv.conf 5 , +.Xr named 8 +.Sh HISTORY +.Nm +first appeared in +.Ox 3.0 . +.Sh BUGS +The data in +.Li *rdi_data +should be returned in uncompressed wire format. +Currently, the data is in compressed format and the caller can't +uncompress since it doesn't have the full message. diff --git a/lib/libc/net/getrrsetbyname.c b/lib/libc/net/getrrsetbyname.c new file mode 100644 index 00000000000..cacf9157e78 --- /dev/null +++ b/lib/libc/net/getrrsetbyname.c @@ -0,0 +1,506 @@ +/* $Id: getrrsetbyname.c,v 1.1 2001/08/06 14:40:47 jakob Exp $ */ + +/* + * Copyright (c) 2001 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <netdb.h> +#include <resolv.h> +#include <stdlib.h> +#include <string.h> + +#define ANSWER_BUFFER_SIZE 1024*64 + + +struct dns_query { + char *name; + u_int16_t type; + u_int16_t class; + struct dns_query *next; +}; + +struct dns_rr { + char *name; + u_int16_t type; + u_int16_t class; + u_int16_t ttl; + u_int16_t size; + void *rdata; + struct dns_rr *next; +}; + +struct dns_response { + HEADER header; + struct dns_query *query; + struct dns_rr *answer; + struct dns_rr *authority; + struct dns_rr *additional; +}; + +static struct dns_response *parse_dns_response(const char *, int); +static struct dns_query *parse_dns_qsection(const char *, int, const char **, + int); +static struct dns_rr *parse_dns_rrsection(const char *, int, const char **, + int); + +static void free_dns_query(struct dns_query *); +static void free_dns_rr(struct dns_rr *); +static void free_dns_response(struct dns_response *); + +static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t); + +int +getrrsetbyname(const char *hostname, unsigned int rdclass, + unsigned int rdtype, unsigned int flags, + struct rrsetinfo **res) +{ + int result; + struct rrsetinfo *rrset = NULL; + struct dns_response *response; + struct dns_rr *rr; + struct rdatainfo *rdata; + unsigned length, index_ans, index_sig; + char answer[ANSWER_BUFFER_SIZE]; + + /* check for invalid class and type */ + if (rdclass > 0xffff || rdtype > 0xffff) { + result = ERRSET_INVAL; + goto fail; + } + + /* don't allow queries of class or type ANY */ + if (rdclass == 0xff || rdtype == 0xff) { + result = ERRSET_INVAL; + goto fail; + } + + /* don't allow flags yet, unimplemented */ + if (flags) { + result = ERRSET_INVAL; + goto fail; + } + + /* initialize resolver */ + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + result = ERRSET_FAIL; + goto fail; + } + +#ifdef DEBUG + _res.options |= RES_DEBUG; +#endif /* DEBUG */ + +#ifdef RES_USE_DNSSEC + /* turn on DNSSEC if EDNS0 is configured */ + if (_res.options & RES_USE_EDNS0) + _res.options |= RES_USE_DNSSEC; +#endif /* RES_USE_DNSEC */ + + /* make query */ + length = res_query(hostname, rdclass, rdtype, answer, sizeof(answer)); + if (length < 0) { + switch(h_errno) { + case HOST_NOT_FOUND: + result = ERRSET_NONAME; + goto fail; + case NO_DATA: + result = ERRSET_NODATA; + goto fail; + default: + result = ERRSET_FAIL; + goto fail; + } + } + + /* parse result */ + response = parse_dns_response(answer, length); + if (response == NULL) { + result = ERRSET_FAIL; + goto fail; + } + + if (response->header.qdcount != 1 ) { + result = ERRSET_FAIL; + goto fail; + } + + /* initialize rrset */ + rrset = calloc(1, sizeof(struct rrsetinfo)); + if (rrset == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + rrset->rri_rdclass = response->query->class; + rrset->rri_rdtype = response->query->type; + rrset->rri_ttl = response->answer->ttl; + rrset->rri_nrdatas = response->header.ancount; + + /* check for authenticated data */ + if (response->header.ad == 1) + rrset->rri_flags |= RRSET_VALIDATED; + + /* copy name from answer section */ + length = strlen(response->answer->name); + rrset->rri_name = malloc(length + 1); + if (rrset->rri_name == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + strlcpy(rrset->rri_name, response->answer->name, length + 1); + + /* count answers */ + rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass, + rrset->rri_rdtype); + rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass, + T_SIG); + + /* allocate memory for answers */ + rrset->rri_rdatas = calloc(rrset->rri_nrdatas, + sizeof(struct rdatainfo)); + if (rrset->rri_rdatas == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + + /* allocate memory for signatures */ + rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo)); + if (rrset->rri_sigs == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + + /* copy answers & signatures */ + for (rr = response->answer, index_ans = 0, index_sig = 0; + rr; rr = rr->next) { + + rdata = NULL; + + if (rr->class == rrset->rri_rdclass && + rr->type == rrset->rri_rdtype) + rdata = &rrset->rri_rdatas[index_ans++]; + + if (rr->class == rrset->rri_rdclass && + rr->type == T_SIG) + rdata = &rrset->rri_sigs[index_sig++]; + + if (rdata) { + rdata->rdi_length = rr->size; + rdata->rdi_data = malloc(rr->size); + + if (rdata->rdi_data == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + memcpy(rdata->rdi_data, rr->rdata, rr->size); + } + } + + *res = rrset; + return (ERRSET_SUCCESS); + +fail: + if (rrset != NULL) + freerrset(rrset); + return (result); +} + +void +freerrset(struct rrsetinfo *rrset) +{ + u_int16_t i; + + if (rrset == NULL) { + return; + } + + for (i = 0; i < rrset->rri_nrdatas; i++) { + if (rrset->rri_rdatas[i].rdi_data == NULL) + break; + free(rrset->rri_rdatas[i].rdi_data); + } + free(rrset->rri_rdatas); + + for (i = 0; i < rrset->rri_nsigs; i++) { + if (rrset->rri_sigs[i].rdi_data == NULL) + break; + free(rrset->rri_sigs[i].rdi_data); + } + free(rrset->rri_sigs); + + free(rrset->rri_name); + + free(rrset); +} + + +/* + * DNS response parsing routines + */ + +static struct dns_response * +parse_dns_response(const char *answer, int size) +{ + struct dns_response *resp; + const char *cp; + + /* allocate memory for the response */ + resp = malloc(sizeof(*resp)); + if (resp == NULL) + return (NULL); + + /* initialize current pointer */ + cp = answer; + + /* copy header */ + memcpy(&resp->header, cp, HFIXEDSZ); + cp += HFIXEDSZ; + + /* fix header byte order */ + resp->header.qdcount = ntohs(resp->header.qdcount); + resp->header.ancount = ntohs(resp->header.ancount); + resp->header.nscount = ntohs(resp->header.nscount); + resp->header.arcount = ntohs(resp->header.arcount); + + /* there must be at least one query */ + if (resp->header.qdcount < 1) { + free_dns_response(resp); + return (NULL); + } + + /* parse query section */ + resp->query = parse_dns_qsection(answer, size, &cp, + resp->header.qdcount); + if (resp->header.qdcount && resp->query == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse answer section */ + resp->answer = parse_dns_rrsection(answer, size, &cp, + resp->header.ancount); + if (resp->header.ancount && resp->answer == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse authority section */ + resp->authority = parse_dns_rrsection(answer, size, &cp, + resp->header.nscount); + if (resp->header.nscount && resp->authority == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse additional section */ + resp->additional = parse_dns_rrsection(answer, size, &cp, + resp->header.arcount); + if (resp->header.arcount && resp->additional == NULL) { + free_dns_response(resp); + return (NULL); + } + + return (resp); +} + +static struct dns_query * +parse_dns_qsection(const char *answer, int size, const char **cp, int count) +{ + struct dns_query *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_query)); + if (curr == NULL) { + free_dns_query(head); + return (NULL); + } + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_query(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_query(head); + return (NULL); + } + *cp += length; + + /* type */ + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + curr->class = _getshort(*cp); + *cp += INT16SZ; + } + + return (head); +} + +static struct dns_rr * +parse_dns_rrsection(const char *answer, int size, const char **cp, int count) +{ + struct dns_rr *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_rr)); + if (curr == NULL) { + free_dns_rr(head); + return (NULL); + } + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_rr(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_rr(head); + return (NULL); + } + *cp += length; + + /* type */ + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + curr->class = _getshort(*cp); + *cp += INT16SZ; + + /* ttl */ + curr->ttl = _getlong(*cp); + *cp += INT32SZ; + + /* rdata size */ + curr->size = _getshort(*cp); + *cp += INT16SZ; + + /* rdata itself */ + curr->rdata = malloc(curr->size); + if (curr->rdata == NULL) { + free_dns_rr(head); + return (NULL); + } + memcpy(curr->rdata, *cp, curr->size); + *cp += curr->size; + } + + return (head); +} + + +static void +free_dns_query(struct dns_query *p) +{ + if (p == NULL) + return; + + free(p->name); + free_dns_query(p->next); + free(p); +} + +static void +free_dns_rr(struct dns_rr *p) +{ + if (p == NULL) + return; + + free(p->name); + free(p->rdata); + free_dns_rr(p->next); + free(p); +} + +static void +free_dns_response(struct dns_response *p) +{ + if (p == NULL) + return; + + free_dns_query(p->query); + free_dns_rr(p->answer); + free_dns_rr(p->authority); + free_dns_rr(p->additional); + free(p); +} + +static int +count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type) +{ + int n = 0; + + while(p) { + if (p->class == class && p->type == type) + n++; + p = p->next; + }; + + return (n); +} diff --git a/lib/libc/shlib_version b/lib/libc/shlib_version index 544b4b21d49..b9c68890d55 100644 --- a/lib/libc/shlib_version +++ b/lib/libc/shlib_version @@ -1,2 +1,2 @@ major=27 -minor=2 +minor=3 |