- 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.
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.
--- /dev/null
+.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 <unbound.h>
+.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.
/** 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
};
/**
* 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.
*/
"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;
}
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];
#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));
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];
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. "
}
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 */
{
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);
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;
};
/**