LIBUNBOUND(3)

πŸ’– libunbound(3) β€” NAME πŸŽ€

okay so bestie, listen: libunbound is the Unbound DNS validating resolver 1.26.2 functions. that's the whole name section and it ate. periodt.


🌈 SYNOPSIS πŸ’…

#include <unbound.h>

struct ub_ctx * ub_ctx_create(void);

void ub_ctx_delete(struct ub_ctx* ctx);

int ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val);

int ub_ctx_get_option(struct ub_ctx* ctx, char* opt, char** val);

int ub_ctx_config(struct ub_ctx* ctx, char* fname);

int ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr);

int ub_ctx_set_stub(struct ub_ctx* ctx, char* zone, char* addr,
       int isprime);

int ub_ctx_set_tls(struct ub_ctx* ctx, int tls);

int ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname);

int ub_ctx_hosts(struct ub_ctx* ctx, char* fname);

int ub_ctx_add_ta(struct ub_ctx* ctx, char* ta);

int ub_ctx_add_ta_autr(struct ub_ctx* ctx, char* fname);

int ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname);

int ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname);

int ub_ctx_debugout(struct ub_ctx* ctx, FILE* out);

int ub_ctx_debuglevel(struct ub_ctx* ctx, int d);

int ub_ctx_async(struct ub_ctx* ctx, int dothread);

int ub_poll(struct ub_ctx* ctx);

int ub_wait(struct ub_ctx* ctx);

int ub_fd(struct ub_ctx* ctx);

int ub_process(struct ub_ctx* ctx);

int ub_resolve(struct ub_ctx* ctx, char* name,
       int rrtype, int rrclass, struct ub_result** result);

int ub_resolve_async(struct ub_ctx* ctx, char* name,
       int rrtype, int rrclass, void* mydata, ub_callback_type*
       callback, int* async_id);

int ub_cancel(struct ub_ctx* ctx, int async_id);

void ub_resolve_free(struct ub_result* result);

const char * ub_strerror(int err);

int ub_ctx_print_local_zones(struct ub_ctx* ctx);

int ub_ctx_zone_add(struct ub_ctx* ctx, char* zone_name, char*
zone_type);

int ub_ctx_zone_remove(struct ub_ctx* ctx, char* zone_name);

int ub_ctx_data_add(struct ub_ctx* ctx, char* data);

int ub_ctx_data_remove(struct ub_ctx* ctx, char* data);

✨ DESCRIPTION πŸ¦‹

okay so picture this: Unbound is an implementation of a DNS resolver, and it does caching and DNSSEC validation. this page is the library API, the one you use with the -lunbound library. the actual server daemon? that's described over in unbound(8) β€” different icon, different vibe. and the important part: this library works independent from a running unbound server. you can use it to convert hostnames to ip addresses, and back, and obtain other information from the DNS. plus it performs public-key validation of results with DNSSEC. literally so secure of it. πŸ”₯

the library uses a variable of type struct ub_ctx to keep context between calls. you, the user, have to maintain it β€” creating it with ub_ctx_create and deleting it with ub_ctx_delete. it can be created and deleted at any time. and listen: creating it anew removes any previous configuration (such as trusted keys) and clears any cached results. so like, it's giving fresh start energy, but also don't do it by accident bestie. πŸ’€

the functions are thread-safe, and a context can 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. that's main character energy fr fr. 🫢

the functions are discussed in turn below. no cap, we're about to go through every single one. 🌟

✨ FUNCTIONS ✨

Okay bestie, here comes the whole roster of things you can actually call. Every single one, no skips. πŸ“‹

  • ub_ctx_create β€” makes you a brand-new context, all set up with defaults. Heads up though: it does not pull in the info from /etc/resolv.conf and /etc/hosts by default β€” you gotta go get those yourself with ub_ctx_resolvconf and ub_ctx_hosts. And before you call this one, if you're using lib openssl, call the openssl functions CRYPTO_set_id_callback and CRYPTO_set_locking_callback to set up async operation (your app calls these once, just for initialisation). Openssl 1.0.0 or later uses the CRYPTO_THREADID_set_callback function instead. 🌱
  • ub_ctx_delete β€” deletes the validation context and frees up all the resources hanging off it. Queries that are still async out there? They get killed, and their callbacks never get called. Bye girl πŸ’€
  • ub_ctx_set_option β€” the power-user interface πŸ’… lets you hand it one of the options from the config file format, peep unbound.conf(5). Not every option is relevant here, bestie. For some specific ones β€” like adding trust anchors β€” there are special routines instead. Pass the option name with the trailing ':'.
  • ub_ctx_get_option β€” the power-user interface for getting an option value. Some options just cannot be gotten, and others hand you back a newline separated list. Pass the option name without the trailing ':'. The value you get back has to be free(2)d by you, the caller. 🫢
  • ub_ctx_config β€” power-user interface again: point it at an unbound config file, see unbound.conf(5), and it gets read for configuration. Not all options are relevant. For some specific options, like adding trust anchors, special routines exist instead. ⚠️ Thread-safety tea: this function is thread-safe only if a single instance of ub_ctx* exists in the application. If several instances exist, the application itself has to make sure ub_ctx_config is not called in parallel by those different instances.
  • ub_ctx_set_fwd β€” sets the machine to forward DNS queries to, i.e. the caching resolver you wanna use. IP4 or IP6 address. It forwards all DNS requests to that machine, which is expected to be running a recursive resolver. If that proxy isn't DNSSEC capable, validation may fail. 😬 You can call this several times β€” in that case the addresses get used as backup servers. At this time it is only possible to set configuration before the first resolve is done.
  • ub_ctx_set_stub β€” sets a stub zone: the authoritative dns servers to use for a particular zone. IP4 or IP6 address. If the address is NULL the stub entry gets removed. Set isprime true if you're configuring root hints with it. Otherwise it's similar to the stub zone item from unbound's config file. Can be called several times, for different zones, or to add multiple addresses for a particular zone. At this time it is only possible to set configuration before the first resolve is done.
  • ub_ctx_set_tls β€” enables DNS over TLS (DoT) πŸ”’ for machines that were set with ub_ctx_set_fwd. At this time it is only possible to set configuration before the first resolve is done.
  • ub_ctx_resolvconf β€” by default the root servers are queried and full resolver mode is used, but you can use this call to read the list of nameservers to use from the filename you give it. Usually that's "/etc/resolv.conf". It uses those nameservers as caching proxies. If they don't support DNSSEC, validation may fail. Only nameservers get picked up β€” the searchdomain, ndots and other settings from resolv.conf(5) are ignored. πŸ™ˆ If you pass fname NULL, "/etc/resolv.conf" gets used (if you're on Windows, the system-wide configured nameserver is picked instead). At this time it is only possible to set configuration before the first resolve is done.
  • ub_ctx_hosts β€” reads the list of hosts from the filename you give. Usually "/etc/hosts". When queried for, these addresses are not marked DNSSEC secure. If you pass fname NULL, "/etc/hosts" gets used (if you're on Windows, etc/hosts from WINDIR is picked instead). At this time it is only possible to set configuration before the first resolve is done.
  • ub_ctx_add_ta β€” adds 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. πŸ”‘
  • ub_ctx_add_ta_autr β€” adds a filename with an automatically tracked trust anchor to the given context. Pass the name of a file with the managed trust anchor. You can create this file with unbound-anchor(8) for the root anchor. You can also create it with an initial file containing one line with a DNSKEY or DS record. If the file is writable, it gets updated when the trust anchor changes. ✨ At this time it is only possible to add trusted keys before the first resolve is done.
  • ub_ctx_add_ta_file β€” adds trust anchors to the given context. Pass the 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.
  • ub_ctx_trustedkeys β€” adds 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.
  • ub_ctx_debugout β€” sets debug and error log output to the given stream. Pass NULL to disable output. Default is stderr. File-names or using syslog can be enabled using config options β€” this routine is for using your own stream, iykwim.
  • ub_ctx_debuglevel β€” sets debug verbosity for the context. Output goes to stderr. Higher debug level = more output. Spammy but informative, honestly.
  • ub_ctx_async β€” sets a context behaviour for asynchronous action. If set to true, it enables threading and a call to ub_resolve_async creates a thread to handle work in the background. If false, a process is forked to handle the work in the background instead. 🧡 Changes to this setting after ub_resolve_async calls have been made have no effect β€” delete and re-create the context if you wanna change it.
  • ub_poll β€” polls a context to see if it's got any new results. Do not poll in a loop, bestie, that's not the move β€” instead extract the fd below to poll for readiness and then check, or wait using the wait routine. Returns 0 if there's nothing to read, or nonzero if a result is available. If it's nonzero, call ub_process to do callbacks.
  • ub_wait β€” waits for a context to finish with results. It calls ub_process after the wait for you 🫢. After the wait, there are no more outstanding asynchronous queries.
  • ub_fd β€” gets the file descriptor. Wait for it to become readable β€” at that point answers are returned from the asynchronous validating resolver. Then call ub_process to continue processing. πŸ‘‘
  • ub_process β€” call this routine to continue processing results from the validating resolver (when the fd becomes readable). It will perform the necessary callbacks.
  • ub_resolve β€” performs 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 result structure is newly allocated with the resulting data.
  • ub_resolve_async β€” performs asynchronous resolution and validation of the target name. The arguments mean the same as for ub_resolve, except no data is returned immediately β€” instead a callback gets called later. The callback receives a copy of the mydata pointer, which you can use to pass information to the callback. The callback type is a function pointer to a function declared as:
void my_callback_function(void* my_arg, int err,
                struct ub_result* result);

The async_id is returned so you can (at your option) decide to track it and cancel the request if needed. If you pass a NULL pointer, the async_id is not returned.

  • ub_cancel β€” cancels an async query in progress. This may return an error if the query does not exist, or if the query is already being delivered β€” in that case you may still get a callback for the query. 😭
  • ub_resolve_free β€” frees the struct ub_result contents after use. Clean up after yourself, periodt.
  • ub_strerror β€” converts an error value from one of the unbound library functions into a human readable string. Fr fr, so useful.
  • ub_ctx_print_local_zones β€” debug printout of the local authority information to debug output.
  • ub_ctx_zone_add β€” adds a new zone to the local authority info, just like the local-zone unbound.conf(5) statement.
  • ub_ctx_zone_remove β€” deletes a zone from the local authority info.
  • ub_ctx_data_add β€” adds resource record data to the local authority info, like the local-data unbound.conf(5) statement.
  • ub_ctx_data_remove β€” deletes local authority data for the name you give it.

πŸ¦‹ RESULT DATA STRUCTURE πŸ¦‹

The result of the DNS resolution and validation comes back to you as struct ub_result. That result structure holds these entries, and yes, we're listing all of them:

struct ub_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 no data */
     void* answer_packet; /* full network format answer packet */
     int answer_len;      /* length of packet in octets */
     int havedata;        /* true if there is data */
     int nxdomain;        /* true if nodata because name does not exist */
     int secure;          /* true if result is secure */
     int bogus;           /* true if a security failure happened */
     char* why_bogus;     /* string with error if bogus */
     int was_ratelimited; /* true if the query was ratelimited (SERVFAIL) by unbound */
     int ttl;             /* number of seconds the result is valid */
};

So here's the vibe check on secure and bogus: if both secure and bogus are false, that means security was not enabled for the domain of the query. Otherwise, they are not both true β€” exactly one of them is true. 🌈

✨ RETURN VALUES ✨

okay bestie, here's the tea on what these functions hand back to you πŸ’–

  • Lots of these routines return an error code. The magic number 0 (zero, yes the plain one) means no error happened, we're thriving, nothing is wrong, periodt ✨
  • Got some other number back? πŸ‘€ No stress β€” you can pass it straight to ub_strerror and it'll give you a readable error string. And just so you know, ub_strerror returns a zero terminated string. πŸ“
  • ub_ctx_create returns NULL on an error β€” and that error is specifically a malloc failure. So if you see NULL, the memory said no πŸ™ˆ
  • ub_poll returns true if some information may be available, and false otherwise. Hopeful little guy, isn't it 🫢
  • ub_fd returns a file descriptor, or -1 on error πŸ’€
  • ub_ctx_config and ub_ctx_resolvconf will attempt to leave errno informative when they return with a file read failure. Attempt, bestie. They try their best 😭

🌸 SEE ALSO 🌸

Go read these next, they're part of the lore, iykyk πŸ’…


πŸ‘‘ AUTHOR πŸ‘‘

The Unbound developers are mentioned in the CREDITS file in the distribution. Shoutout to the whole squad, iconic behaviour honestly πŸŽ€


1999-2026, NLnet Labs 🌈


and that's a wrap on the whole page, bestie!! this was LIBUNBOUND(3), living in source/version 1.26.2, dated Oct 21, 2026 πŸ¦‹ go forth and bind some names, you main character πŸ’–βœ¨πŸŒΈπŸ«Ά

πŸ“¦ where this page comes from