From: Wouter Wijngaards Date: Thu, 17 Jan 2008 15:35:34 +0000 (+0000) Subject: manual and pktinfo. X-Git-Tag: release-0.9~67 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=95b0a129e2500e32a4c817d14494b1ac1c5972a1;p=thirdparty%2Funbound.git manual and pktinfo. git-svn-id: file:///svn/unbound/trunk@873 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index d56123986..812489901 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,8 @@ - fixup a couple of doxygen warnings, about enum variables. - interface-automatic now copies the interface address from the PKT_INFO structure as well. + - manual page with library API, all on one page 'man libunbound'. + - rewrite of PKTINFO structure, it also captures IP4 PKTINFO. 16 January 2008: Wouter - incoming queries to the server with TC bit on are replied FORMERR. diff --git a/doc/TODO b/doc/TODO index d9b73477f..e30318e71 100644 --- a/doc/TODO +++ b/doc/TODO @@ -56,3 +56,4 @@ o in an ipv6 connected only environment unbound cannot use outgoing IP6 V6ONLY socket option. o support multiple dns messages in a TCP query stream for the unbound server. o SIG(0) and TSIG. +o examine errno threaded trouble with EAGAIN in netevent. diff --git a/doc/libunbound.3 b/doc/libunbound.3 new file mode 100644 index 000000000..e147d83bb --- /dev/null +++ b/doc/libunbound.3 @@ -0,0 +1,264 @@ +.TH "libunbound" "3" "@date@" "NLnet Labs" "unbound @version@" +.\" +.\" libunbound.3 -- unbound library functions manual +.\" +.\" Copyright (c) 2007, NLnet Labs. All rights reserved. +.\" +.\" See LICENSE for the license. +.\" +.\" +.SH "NAME" +.LP +.B libunbound, +.B unbound.h, +.B ub_val_ctx, +.B ub_val_result, +.B ub_val_callback_t, +.B ub_val_ctx_create, +.B ub_val_ctx_delete, +.B ub_val_ctx_config, +.B ub_val_ctx_add_ta, +.B ub_val_ctx_add_ta_file, +.B ub_val_ctx_trustedkeys, +.B ub_val_ctx_debuglevel, +.B ub_val_ctx_async, +.B ub_val_ctx_poll, +.B ub_val_ctx_wait, +.B ub_val_ctx_fd, +.B ub_val_ctx_process, +.B ub_val_resolve, +.B ub_val_resolve_async, +.B ub_val_cancel, +.B ub_val_result_free, +.B ub_val_strerror +\- Unbound DNS validating resolver @version@ functions. +.SH "SYNOPSIS" +.LP +.B #include +.LP +\fIstruct ub_val_ctx *\fR +\fBub_val_ctx_create\fR(\fIvoid\fR); +.LP +\fIvoid\fR +\fBub_val_ctx_delete\fR(\fIstruct ub_val_ctx*\fR ctx); +.LP +\fIint\fR +\fBub_val_ctx_config\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR fname); +.LP +\fIint\fR +\fBub_val_ctx_add_ta\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR ta); +.LP +\fIint\fR +\fBub_val_ctx_add_ta_file\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR fname); +.LP +\fIint\fR +\fBub_val_ctx_trustedkeys\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR fname); +.LP +\fIint\fR +\fBub_val_ctx_debuglevel\fR(\fIstruct ub_val_ctx*\fR ctx, \fIint\fR d); +.LP +\fIint\fR +\fBub_val_ctx_async\fR(\fIstruct ub_val_ctx*\fR ctx, \fIint\fR dothread); +.LP +\fIint\fR +\fBub_val_ctx_poll\fR(\fIstruct ub_val_ctx*\fR ctx); +.LP +\fIint\fR +\fBub_val_ctx_wait\fR(\fIstruct ub_val_ctx*\fR ctx); +.LP +\fIint\fR +\fBub_val_ctx_fd\fR(\fIstruct ub_val_ctx*\fR ctx); +.LP +\fIint\fR +\fBub_val_ctx_process\fR(\fIstruct ub_val_ctx*\fR ctx); +.LP +\fIint\fR +\fBub_val_resolve\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR name, +.br + \fIint\fR rrtype, \fIint\fR rrclass, \fIint*\fR secure, +.br + \fIint*\fR data, \fIstruct ub_val_result**\fR result); +.LP +\fIint\fR +\fBub_val_resolve_async\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR name, +.br + \fIint\fR rrtype, \fIint\fR rrclass, \fIvoid*\fR mydata, +.br + \fIub_val_callback_t\fR callback, \fIint*\fR async_id); +.LP +\fIint\fR +\fBub_val_cancel\fR(\fIstruct ub_val_ctx*\fR ctx, \fIint\fR async_id); +.LP +\fIvoid\fR +\fBub_val_result_free\fR(\fIstruct ub_val_result*\fR result); +.LP +\fIconst char *\fR +\fBub_val_strerror\fR(\fIint\fR err); +.SH "DESCRIPTION" +.LP +.B Unbound +is an implementation of a DNS resolver, that does caching and +DNSSEC validation. This is the library API, for using the \-lunbound library. +The server daemon is described in \fIunbound\fR(8). +The library can be used to convert hostnames to ip addresses, and back, +and obtain other information from the DNS. The library performs public\-key +validation of results with DNSSEC. +.P +The library uses a variable of type \fIstruct ub_val_ctx\fR to keep context +between calls. The user must maintain it, creating it with +.B ub_val_ctx_create +and deleting it with +.B ub_val_ctx_delete\fR. +It can be created and deleted at any time. Creating it anew removes any +previous configuration (such as trusted keys) and clears any cached results. +.P +The functions are thread\-safe, and a context an be used in a threaded (as +well as in a non\-threaded) environment. Also resolution (and validation) +can be performed blocking and non\-blocking (also called asynchronous). +The async method returns from the call immediately, so that processing +can go on, while the results become available later. +.P +The functions are discussed in turn below. +.SH "FUNCTIONS" +.TP +.B ub_val_ctx_create +Create a new context, initialised with defaults. +.TP +.B ub_val_ctx_delete +Delete validation context and free associated resources. +Outstanding async queries are killed and callbacks are not called for them. +.TP +.B ub_val_ctx_config +A power\-user interface that lets you specify an unbound config file, see +\fIunbound.conf\fR(5), which is read for configuration. Not all options are +relevant. For some specific options, such as adding trust anchors, special +routines exist. +.TP +.B +ub_val_ctx_add_ta +Add a trust anchor to the given context. +At this time it is only possible to add trusted keys before the +first resolve is done. +The format is a string, similar to the zone-file format, +[domainname] [type] [rdata contents]. Both DS and DNSKEY records are accepted. +.TP +.B ub_val_ctx_add_ta_file +Add trust anchors to the given context. +Pass name of a file with DS and DNSKEY records in zone file format. +At this time it is only possible to add trusted keys before the +first resolve is done. +.TP +.B ub_val_ctx_trustedkeys +Add trust anchors to the given context. +Pass the name of a bind-style config file with trusted-keys{}. +At this time it is only possible to add trusted keys before the +first resolve is done. +.TP +.B ub_val_ctx_debuglevel +Set debug verbosity for the context. Output is directed to stderr. +Higher debug level gives more output. +.TP +.B ub_val_ctx_async +Set a context behaviour for asynchronous action. +if set to true, enables threading and a call to resolve_async() +creates a thread to handle work in the background. +If false, a process is forked to handle work in the background. +Changes to this setting after async() calls have been made have +no effect (delete and re\-create the context to change). +.TP +.B ub_val_ctx_poll +Poll a context to see if it has any new results. +Do not poll in a loop, instead extract the fd below to poll for readiness, +and then check, or wait using the wait routine. +Returns 0 if nothing to read, or nonzero if a result is available. +If nonzero, call +.B ctx_process +to do callbacks. +.TP +.B ub_val_ctx_wait +Wait for a context to finish with results. Calls +.B ub_val_ctx_process after +the wait for you. After the wait, there are no more outstanding asynchronous +queries. +.TP +.B ub_val_ctx_fd +Get file descriptor. Wait for it to become readable, at this point +answers are returned from the asynchronous validating resolver. +Then call the \fBub_val_ctx_process\fR to continue processing. +.TP +.B ub_val_ctx_process +Call this routine to continue processing results from the validating +resolver (when the fd becomes readable). +Will perform necessary callbacks. +.TP +.B ub_val_resolve +Perform resolution and validation of the target name. +The name is a domain name in a zero terminated text string. +The rrtype and rrclass are DNS type and class codes. +The value secure returns true if the answer validated securely. +The value data returns true if there was data. +The result structure is newly allocated with the resulting data. +.TP +.B ub_val_resolve_async +Perform asynchronous resolution and validation of the target name. +Arguments mean the same as for \fBub_val_resolve\fR except no +data is returned immediately, instead a callback is called later. +The callback receives a copy of the mydata point, that you can use to pass +information to the callback. The callback type is a function pointer to +a function declared as +.IP +void my_callback_function(void* my_arg, int err, +.br + int secure, int havedata, +.br + struct ub_val_result* result); +.IP +The async_id is returned so you can (at your option) decide to track it +and cancel the request if needed. +.TP +.B ub_val_cancel +Cancel an async query in progress. +.TP +.B ub_val_result_free +Free struct ub_val_result contents after use. +.TP +.B ub_val_strerror +Convert error value from one of the unbound library functions +to a human readable string. +.SH "RESULT DATA STRUCTURE" +.LP +The result of the DNS resolution and validation is returned as +\fIstruct ub_val_result\fR. The result structure contains the following entries. +.P +.nf + struct ub_val_result { + char* qname; /* text string, original question */ + int qtype; /* type code asked for */ + int qclass; /* class code asked for */ + char** data; /* array of rdata items, NULL terminated*/ + int* len; /* array with lengths of rdata items */ + char* canonname; /* canonical name of result */ + int rcode; /* additional error code in case of error */ + int nxdomain; /* if nodata because no domain */ + int bogus; /* if not secure due to security failure */ + }; +.fi +.SH "RETURN VALUES" +Many routines return an error code. The value 0 (zero) denotes no error +happened. Other values can be passed to +.B ub_val_strerror +to obtain a readable error string. +.B ub_val_strerror +returns a zero terminated string. +.B ub_val_ctx_create +returns NULL on an error (a malloc failure). +.B ub_val_ctx_poll +returns true if some information may be available, false otherwise. +.B ub_val_ctx_fd +returns a file descriptor or -1 on error. +.SH "SEE ALSO" +\fIunbound.conf\fR(5), +\fIunbound\fR(8). +.SH "AUTHORS" +.B Unbound +developers are mentioned in the CREDITS file in the distribution. diff --git a/libunbound/context.h b/libunbound/context.h index e42509c74..8a472d63d 100644 --- a/libunbound/context.h +++ b/libunbound/context.h @@ -155,19 +155,19 @@ enum ub_ctx_err { /** no error */ UB_NOERROR = 0, /** alloc failure */ - UB_NOMEM, + UB_NOMEM = -1, /** socket operation */ - UB_SOCKET, + UB_SOCKET = -2, /** syntax error */ - UB_SYNTAX, + UB_SYNTAX = -3, /** DNS service failed */ - UB_SERVFAIL, + UB_SERVFAIL = -4, /** fork() failed */ - UB_FORKFAIL, + UB_FORKFAIL = -5, /** cfg change after finalize() */ - UB_AFTERFINAL, + UB_AFTERFINAL = -6, /** initialization failed (bad settings) */ - UB_INITFAIL + UB_INITFAIL = -7 }; /** diff --git a/libunbound/unbound.h b/libunbound/unbound.h index 7d60cd474..711caefa3 100644 --- a/libunbound/unbound.h +++ b/libunbound/unbound.h @@ -266,13 +266,14 @@ int ub_val_ctx_async(struct ub_val_ctx* ctx, int dothread); * and then check, or wait using the wait routine. * @param ctx: context. * @return: 0 if nothing to read, or nonzero if a result is available. - * If nonzero, call ctx_process() to get do any callbacks. + * If nonzero, call ctx_process() to do callbacks. */ int ub_val_ctx_poll(struct ub_val_ctx* ctx); /** * Wait for a context to finish with results. Calls ctx_process() after - * the wait for you. After the wait, there are no more outstanding queries. + * the wait for you. After the wait, there are no more outstanding + * asynchronous queries. * @param ctx: context. * @return: 0 if OK, else error. */ diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 22ccd6de8..6cd65c4de 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -263,6 +263,18 @@ set_ip6_recvpktinfo(int s) "disable interface-automatic in config"); return 0; #endif /* defined IPV6_RECVPKTINFO */ + +#ifdef IP_PKTINFO + if(setsockopt(s, IPPROTO_IP, IP_PKTINFO, + &on, (socklen_t)sizeof(on)) < 0) { + log_err("setsockopt(..., IP_PKTINFO, ...) failed: %s", + strerror(errno)); + } +#else + log_err("no IP_PKTINFO option, please disable " + "interface-automatic in config"); + return 0; +#endif /* IP_PKTINFO */ return 1; } diff --git a/util/netevent.c b/util/netevent.c index 37735e058..43bd1917f 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -178,12 +178,46 @@ comm_point_send_udp_msg(struct comm_point *c, ldns_buffer* packet, return 1; } +/** print debug ancillary info */ +void p_ancil(const char* str, struct comm_reply* r) +{ +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(IP_PKTINFO) + if(r->srctype != 4 && r->srctype != 6) { + log_info("%s: unknown srctype %d", str, r->srctype); + return; + } + if(r->srctype == 6) { + char buf[1024]; + if(inet_ntop(AF_INET6, &r->pktinfo.v6info.ipi6_addr, + buf, (socklen_t)sizeof(buf)) == 0) { + strncpy(buf, "(inet_ntop error)", sizeof(buf)); + } + buf[sizeof(buf)-1]=0; + log_info("%s: %s %d", str, buf, r->pktinfo.v6info.ipi6_ifindex); + } else if(r->srctype == 4) { + char buf1[1024], buf2[1024]; + if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_addr, + buf1, (socklen_t)sizeof(buf1)) == 0) { + strncpy(buf1, "(inet_ntop error)", sizeof(buf1)); + } + buf1[sizeof(buf1)-1]=0; + if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_spec_dst, + buf2, (socklen_t)sizeof(buf2)) == 0) { + strncpy(buf2, "(inet_ntop error)", sizeof(buf2)); + } + buf2[sizeof(buf2)-1]=0; + log_info("%s: %d %s %s", str, r->pktinfo.v4info.ipi_ifindex, + buf1, buf2); + } +#endif +} + /** send a UDP reply over specified interface*/ int comm_point_send_udp_msg_if(struct comm_point *c, ldns_buffer* packet, - struct sockaddr* addr, socklen_t addrlen, void* ifaddr, int ifnum) + struct sockaddr* addr, socklen_t addrlen, struct comm_reply* r) { -#if defined(AF_INET6) && defined(IPV6_PKTINFO) +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(IP_PKTINFO) ssize_t sent; struct msghdr msg; struct iovec iov[1]; @@ -210,15 +244,29 @@ comm_point_send_udp_msg_if(struct comm_point *c, ldns_buffer* packet, #ifndef S_SPLINT_S cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - memmove(&((struct in6_pktinfo*)CMSG_DATA(cmsg))->ipi6_addr, - ifaddr, sizeof(struct in6_addr)); - ((struct in6_pktinfo*)CMSG_DATA(cmsg))->ipi6_ifindex = ifnum; + if(r->srctype == 4) { + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + memmove(CMSG_DATA(cmsg), &r->pktinfo.v4info, + sizeof(struct in_pktinfo)); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + } else if(r->srctype == 6) { + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + memmove(CMSG_DATA(cmsg), &r->pktinfo.v6info, + sizeof(struct in6_pktinfo)); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + } else { + /* try to pass all 0 to use default route */ + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + memset(CMSG_DATA(cmsg), 0, sizeof(struct in6_pktinfo)); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + } msg.msg_controllen = cmsg->cmsg_len; #endif /* S_SPLINT_S */ + p_ancil("send_udp over interface", r); sent = sendmsg(c->fd, &msg, 0); if(sent == -1) { verbose(VERB_OPS, "sendmsg failed: %s", strerror(errno)); @@ -238,7 +286,7 @@ comm_point_send_udp_msg_if(struct comm_point *c, ldns_buffer* packet, void comm_point_udp_ancil_callback(int fd, short event, void* arg) { -#if defined(AF_INET6) && defined(IPV6_PKTINFO) +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(IP_PKTINFO) struct comm_reply rep; struct msghdr msg; struct iovec iov[1]; @@ -279,26 +327,35 @@ comm_point_udp_ancil_callback(int fd, short event, void* arg) rep.addrlen = msg.msg_namelen; ldns_buffer_skip(rep.c->buffer, recv); ldns_buffer_flip(rep.c->buffer); - rep.ifnum = 0; + rep.srctype = 0; #ifndef S_SPLINT_S for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + log_info("looking at hdr %d %d (need %d %d or %d %d)", + cmsg->cmsg_level, cmsg->cmsg_type, + IPPROTO_IPV6, IPV6_PKTINFO, + IPPROTO_IP, IP_PKTINFO); if( cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { - rep.ifnum = ((struct in6_pktinfo*)CMSG_DATA(cmsg))-> - ipi6_ifindex; - memmove(&rep.ifaddr, &((struct in6_pktinfo*) - CMSG_DATA(cmsg))->ipi6_addr, - sizeof(struct in6_addr)); + rep.srctype = 6; + memmove(&rep.pktinfo.v6info, CMSG_DATA(cmsg), + sizeof(struct in6_pktinfo)); + break; + } else if( cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_PKTINFO) { + rep.srctype = 4; + memmove(&rep.pktinfo.v4info, CMSG_DATA(cmsg), + sizeof(struct in_pktinfo)); + break; } } + p_ancil("receive_udp on interface", &rep); #endif /* S_SPLINT_S */ log_assert(fptr_whitelist_comm_point(rep.c->callback)); if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) { /* send back immediate reply */ (void)comm_point_send_udp_msg_if(rep.c, rep.c->buffer, - (struct sockaddr*)&rep.addr, rep.addrlen, - &rep.ifaddr, rep.ifnum); + (struct sockaddr*)&rep.addr, rep.addrlen, &rep); } #else fatal_exit("recvmsg: No support for IPV6_PKTINFO. " @@ -333,7 +390,7 @@ comm_point_udp_callback(int fd, short event, void* arg) } ldns_buffer_skip(rep.c->buffer, recv); ldns_buffer_flip(rep.c->buffer); - rep.ifnum = -1; + rep.srctype = 0; log_assert(fptr_whitelist_comm_point(rep.c->callback)); if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) { /* send back immediate reply */ @@ -1007,10 +1064,10 @@ comm_point_send_reply(struct comm_reply *repinfo) { log_assert(repinfo && repinfo->c); if(repinfo->c->type == comm_udp) { - if(repinfo->ifnum != -1) + if(repinfo->srctype) comm_point_send_udp_msg_if(repinfo->c, repinfo->c->buffer, (struct sockaddr*)&repinfo->addr, - repinfo->addrlen, &repinfo->ifaddr, repinfo->ifnum); + repinfo->addrlen, repinfo); else comm_point_send_udp_msg(repinfo->c, repinfo->c->buffer, (struct sockaddr*)&repinfo->addr, repinfo->addrlen); diff --git a/util/netevent.h b/util/netevent.h index 15d592188..bff08ebd7 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -98,12 +98,19 @@ struct comm_reply { struct sockaddr_storage addr; /** length of address */ socklen_t addrlen; -#ifdef AF_INET6 - /** the interface address */ - struct in6_addr ifaddr; + /** return type 0 (none), 4(IP4), 6(IP6) */ + int srctype; + /** the return source interface data */ + union { +#ifdef IPV6_PKTINFO + struct in6_pktinfo v6info; #endif - /** the interface received (for UDPautomaticinterface) or 0 */ - int ifnum; +#ifdef IP_PKTINFO + struct in_pktinfo v4info; +#endif + } + /** variable with return source data */ + pktinfo; }; /**