These documents are Copyright (c) 2009-2012 by Nick Mathewson, and are made available under the Creative Commons Attribution-Noncommercial-Share Alike license, version 3.0. Future versions may be made available under a less restrictive license. Additionally, the source code examples in these documents are also licensed under the so-called "3-Clause" or "Modified" BSD license. See the license_bsd file distributed with these documents for the full terms. For the latest version of this document, see http://www./~nickm/libevent-book/TOC.html To get the source for the latest version of this document, install git and run "git clone git://github.com/nmathewson/libevent-book.git" Using DNS with Libevent: high and low-level functionalityLibevent provides a few APIs to use for resolving DNS names, and a facility for implementing simple DNS servers. We’ll start by describing the higher-level facilities for name lookup, and then describe the low-level and server facilities. Note There are known limitations in Libevent’s current DNS client implementation. It doesn’t support TCP lookups, DNSSec, or arbitrary record types. We’d like to fix all of these in some future version of Libevent, but for now, they’re not there. Preliminaries: Portable blocking name resolutionTo aid in porting programs that already use blocking name resolution, Libevent provides a portable implementation of the standard getaddrinfo() interface. This can be helpful when your program needs to run on platforms where either there is no getaddrinfo() function, or where getaddrinfo() doesn’t conform to the standard as well as our replacement. (There are shockingly many of each.) The getaddrinfo() interface is specified in RFC 3493, section 6.1. See the "Compatibility Notes" section below for a summary of how we fall short of a conformant implemenation. Interface
struct evutil_addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; char *ai_canonname; struct sockaddr *ai_addr; struct evutil_addrinfo *ai_next; }; #define EVUTIL_AI_PASSIVE /* ... */ #define EVUTIL_AI_CANONNAME /* ... */ #define EVUTIL_AI_NUMERICHOST /* ... */ #define EVUTIL_AI_NUMERICSERV /* ... */ #define EVUTIL_AI_V4MAPPED /* ... */ #define EVUTIL_AI_ALL /* ... */ #define EVUTIL_AI_ADDRCONFIG /* ... */ int evutil_getaddrinfo(const char *nodename, const char *servname, const struct evutil_addrinfo *hints, struct evutil_addrinfo **res); void evutil_freeaddrinfo(struct evutil_addrinfo *ai); const char *evutil_gai_strerror(int err); The evutil_getaddrinfo() function tries to resolve the provided nodename and servname fields, according to the rules you give it in hints, and build you a linked list of evutil_addrinfo structures and store them in *res. It returns 0 on success, and a nonzero error code on failure. You must provide at least one of nodename and servname. If nodename is provided, it is either a literal IPv4 address (like "127.0.0.1"), a literal IPv6 address (like "::1"), or a DNS name (like "www.example.com"). If servname is provided, it is either the symbolic name of a network service (like "https") or a string containing a port number given in decimal (like "443"). If you do not specify servname, then the port values in *res will be set to zero. If you do not specify nodename, then the addresses in *res will either be for localhost (by default), or for "any" (if EVUTIL_AI_PASSIVE is set.) The ai_flags field of hints tells evutil_getaddrinfo how to perform the lookup. It can contain zero or more of the flags below, ORed together.
The ai_family field of hints is used to tell evutil_getaddrinfo() which addresses it should return. It can be AF_INET to request IPv4 addresses only, AF_INET6 to request IPv6 addresses only, or AF_UNSPEC to request all available addresses. The ai_socktype and ai_protocol fields of hints are used to tell evutil_getaddrinfo() how you’re going to use the address. They’re the same as the socktype and protocol fields you would pass to socket(). If evutil_getaddrinfo() is successful, it allocates a new linked list of evutil_addrinfo structures, where each points to the next with its "ai_next" pointer, and stores them in *res. Because this value is heap-allocated, you will need to use evutil_freeaddrinfo to free it. If it fails, it returns one of these numeric error codes:
You can use evutil_gai_strerror() to convert one of these results into a human-readable string. Note: If your OS defines struct addrinfo, then evutil_addrinfo is just an alias for your OS’s built-in structure. Similarly, if your operating system defines any of the AI_* flags, then the corresponding EVUTIL_AI_* flag is just an alias for the native flag; and if your operating system defines any of the EAI_* errors, then the corresponding EVUTIL_EAI_* code is the same as your platform’s native error code. Example: Resolving a hostname and making a blocking connection
#include <event2/util.h> #include <sys/socket.h> #include <sys/types.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <unistd.h> evutil_socket_t get_tcp_socket_for_host(const char *hostname, ev_uint16_t port) { char port_buf[6]; struct evutil_addrinfo hints; struct evutil_addrinfo *answer = NULL; int err; evutil_socket_t sock; /* Convert the port to decimal. */ evutil_snprintf(port_buf, sizeof(port_buf), "%d", (int)port); /* Build the hints to tell getaddrinfo how to act. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; /* v4 or v6 is fine. */ hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; /* We want a TCP socket */ /* Only return addresses we can use. */ hints.ai_flags = EVUTIL_AI_ADDRCONFIG; /* Look up the hostname. */ err = evutil_getaddrinfo(hostname, port_buf, &hints, &answer); if (err != 0) { fprintf(stderr, "Error while resolving '%s': %s", hostname, evutil_gai_strerror(err)); return -1; } /* If there was no error, we should have at least one answer. */ assert(answer); /* Just use the first answer. */ sock = socket(answer->ai_family, answer->ai_socktype, answer->ai_protocol); if (sock < 0) return -1; if (connect(sock, answer->ai_addr, answer->ai_addrlen)) { /* Note that we're doing a blocking connect in this function. * If this were nonblocking, we'd need to treat some errors * (like EINTR and EAGAIN) specially. */ EVUTIL_CLOSESOCKET(sock); return -1; } return sock; } These functions and constants were new in Libevent 2.0.3-alpha. They are declared in event2/util.h. Non-blocking hostname resolution with evdns_getaddrinfo()The main problem with the regular getaddrinfo() interface, and with evutil_getaddrinfo() above, is that they’re blocking: when you call them, the thread you’re in has to wait while they query your DNS server(s) and wait for a response. Since you’re using Libevent, that probably isn’t the behavior you want. So for nonblocking use, Libevent provides a set of functions to launch DNS requests, and use Libevent to wait for the server to answer. Interface
typedef void (*evdns_getaddrinfo_cb)( int result, struct evutil_addrinfo *res, void *arg); struct evdns_getaddrinfo_request; struct evdns_getaddrinfo_request *evdns_getaddrinfo( struct evdns_base *dns_base, const char *nodename, const char *servname, const struct evutil_addrinfo *hints_in, evdns_getaddrinfo_cb cb, void *arg); void evdns_getaddrinfo_cancel(struct evdns_getaddrinfo_request *req); The evdns_getaddrinfo() function behaves just like evutil_getaddrinfo(), except that instead of blocking on DNS servers, it uses Libevent’s low-level DNS facilities to look hostnames up for you. Because it can’t always return you the result immediately, you need to provide it a callback function of type evdns_getaddrinfo_cb, and an optional user-supplied argument for that callback function. Additionally, you need to provide evdns_getaddrinfo() with a pointer to an evdns_base. This structure holds the state and configuration for Libevent’s DNS resolver. See the next section for more information on how to get one. The evdns_getaddrinfo() function returns NULL if it fails or succeeds immediately. Otherwise, it returns a pointer to an evdns_getaddrinfo_request. You can use this to cancel the request with evdns_getaddrinfo_cancel() at any time before the request is finished. Note that the callback function will eventually be invoked whether evdns_getaddrinfo() returns NULL or not, and whether evdns_getaddrinfo_cancel() is called or not. When you call evdns_getaddrinfo(), it makes its own internal copies of its nodename, servname, and hints arguments: you do not need to ensure that they continue to exist while the name lookup is in progress. Example: Nonblocking lookups with evdns_getaddrinfo()
#include <event2/dns.h> #include <event2/util.h> #include <event2/event.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> int n_pending_requests = 0; struct event_base *base = NULL; struct user_data { char *name; /* the name we're resolving */ int idx; /* its position on the command line */ }; void callback(int errcode, struct evutil_addrinfo *addr, void *ptr) { struct user_data *data = ptr; const char *name = data->name; if (errcode) { printf("%d. %s -> %s\n", data->idx, name, evutil_gai_strerror(errcode)); } else { struct evutil_addrinfo *ai; printf("%d. %s", data->idx, name); if (addr->ai_canonname) printf(" [%s]", addr->ai_canonname); puts(""); for (ai = addr; ai; ai = ai->ai_next) { char buf[128]; const char *s = NULL; if (ai->ai_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; s = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128); } else if (ai->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128); } if (s) printf(" -> %s\n", s); } evutil_freeaddrinfo(addr); } free(data->name); free(data); if (--n_pending_requests == 0) event_base_loopexit(base, NULL); } /* Take a list of domain names from the command line and resolve them in * parallel. */ int main(int argc, char **argv) { int i; struct evdns_base *dnsbase; if (argc == 1) { puts("No addresses given."); return 0; } base = event_base_new(); if (!base) return 1; dnsbase = evdns_base_new(base, 1); if (!dnsbase) return 2; for (i = 1; i < argc; i) { struct evutil_addrinfo hints; struct evdns_getaddrinfo_request *req; struct user_data *user_data; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = EVUTIL_AI_CANONNAME; /* Unless we specify a socktype, we'll get at least two entries for * each address: one for TCP and one for UDP. That's not what we * want. */ hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (!(user_data = malloc(sizeof(user_data)))) { perror("malloc"); exit(1); } if (!(user_data->name = strdup(argv[i]))) { perror("strdup"); exit(1); } user_data->idx = i; n_pending_requests; req = evdns_getaddrinfo( dnsbase, argv[i], NULL /* no service name given */, &hints, callback, user_data); if (req == NULL) { printf(" [request for %s returned immediately]\n", argv[i]); /* No need to free user_data or decrement n_pending_requests; that * happened in the callback. */ } } if (n_pending_requests) event_base_dispatch(base); evdns_base_free(dnsbase, 0); event_base_free(base); return 0; } These functions were new in Libevent 2.0.3-alpha. They are declared in event2/dns.h. Creating and configuring an evdns_baseBefore you can do nonblocking DNS lookups with evdns, you’ll need to configure an evdns_base. Each evdns_base stores a list of nameservers, and DNS configuration options, and tracks active and in-flight DNS requests. Interface
struct evdns_base *evdns_base_new(struct event_base *event_base, int initialize); void evdns_base_free(struct evdns_base *base, int fail_requests); The evdns_base_new() function returns a new evdns_base on success, and NULL on failure. If the initialize argument is 1, it tries to configure the DNS base sensibly given your operating system’s default. If it is 0, it leaves the evdns_base empty, with no nameservers or options configured. When you no longer need an evdns_base, you can free it with evdns_base_free. If its fail_requests argument is true, it will make all in-flight requests get their callbacks invoked with a canceled error code before it frees the base. Initializing evdns from the system configurationIf you want a little more control over how the evdns_base is initialized, you can pass 0 as the initialize argument to evdns_base_new, and invoke one of these functions. Interface
#define DNS_OPTION_SEARCH 1 #define DNS_OPTION_NAMESERVERS 2 #define DNS_OPTION_MISC 4 #define DNS_OPTION_HOSTSFILE 8 #define DNS_OPTIONS_ALL 15 int evdns_base_resolv_conf_parse(struct evdns_base *base, int flags, const char *filename); #ifdef WIN32 int evdns_base_config_windows_nameservers(struct evdns_base *); #define EVDNS_BASE_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED #endif The evdns_base_resolv_conf_parse() function will scan the resolv.conf formatted file stored in filename, and read in all the options from it that are listed in flags. (For more information on the resolv.conf file, see your local Unix manual pages.)
On Windows, you don’t have a resolv.conf file to tell you where your nameservers are, so you can use the evdns_base_config_windows_nameservers() function to read all your nameservers from your registry (or your NetworkParams, or wherever they’re hidden). The resolv.conf file formatThe resolv.conf format we recognize is a text file, each line of which should either be empty, contain a comment starting with the # character, or consist of a token followed zero or more arguments. The tokens we recognize are:
Unrecognized tokens and options are ignored. Configuring evdns manuallyIf you want even more fine-grained control over evdns’s behavior, you can use these functions: Interface
int evdns_base_nameserver_sockaddr_add(struct evdns_base *base, const struct sockaddr *sa, ev_socklen_t len, unsigned flags); int evdns_base_nameserver_ip_add(struct evdns_base *base, const char *ip_as_string); int evdns_base_load_hosts(struct evdns_base *base, const char *hosts_fname); void evdns_base_search_clear(struct evdns_base *base); void evdns_base_search_add(struct evdns_base *base, const char *domain); void evdns_base_search_ndots_set(struct evdns_base *base, int ndots); int evdns_base_set_option(struct evdns_base *base, const char *option, const char *val); int evdns_base_count_nameservers(struct evdns_base *base); The evdns_base_nameserver_sockaddr_add() function adds a nameserver to an existing evdns_base by its address. The flags argument is currently ignored, and should be 0 for forward-compatibility. The function returns 0 on success and negative on failure. (It was added in Libevent 2.0.7-rc.) The evdns_base_nameserver_ip_add function adds a nameserver to an existing evdns_base. It takes the nameserver in a text string, either as an IPv4 address, an IPv6 address, an IPv4 address with a port (IPv4:Port), or an IPv6 address with a port ([IPv6]:Port). It returns 0 on success and negative on failure. The evdns_base_load_hosts() function loads a hosts file (in the same format as /etc/hosts) from hosts_fname. It also returns 0 on success and negative on failure. The evdns_base_search_clear() function removes all current search suffixes (as configured by the search option) from the evdns_base; the evdns_base_search_add() function adds a suffix. The evdns_base_set_option() function sets a given option to a given value in the evdns_base. Each one is given as a string. (Before Libevent 2.0.3, the option name needed to have a colon after it.) If you’ve just parsed a set of configuration files and want to see if any nameservers were added, you can use evdns_base_count_nameservers() to see how many there are. Library-side configurationThere are a couple of functions you can use to specify library-wide settings for the evdns module: Interface
typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg); void evdns_set_log_fn(evdns_debug_log_fn_type fn); void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)); For historical reasons, the evdns subsystem does its own logging; you can use evdns_set_log_fn() to give it a callback that does something with its messages besides discard them. For security, evdns needs a good source of random numbers: it uses this to pick hard-to-guess transaction IDs and to randomize queries when using the 0x20 hack. (See the "randomize-case" option for more info here.) Older versions of Libevent, did not provide a secure RNG of its own, however. You can give evdns a better random number generator by calling evdns_set_transaction_id_fn and giving it a function that returns a hard-to-predict two-byte unsigned integer. In Libevent 2.0.4-alpha and later, Libevent uses its own built-in secure RNG; evdns_set_transaction_id_fn() has no effect. Low-level DNS interfacesOccasionally, you’ll want the ability to launch specific DNS requests with more fine-grained control than you get from evdns_getaddrinfo(). Libevent gives you some interfaces to do that. Missing features Right now, Libevent’s DNS support lacks a few features that you’d expect from a low-level DNS system, like support for arbitrary request types and TCP requests. If you need features that evdns doesn’t have, please consider contributing a patch. You might also look into a more full-featured DNS library like c-ares. Interface
#define DNS_QUERY_NO_SEARCH /* ... */ #define DNS_IPv4_A /* ... */ #define DNS_PTR /* ... */ #define DNS_IPv6_AAAA /* ... */ typedef void (*evdns_callback_type)(int result, char type, int count, int ttl, void *addresses, void *arg); struct evdns_request *evdns_base_resolve_ipv4(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); struct evdns_request *evdns_base_resolve_ipv6(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); struct evdns_request *evdns_base_resolve_reverse(struct evdns_base *base, const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); struct evdns_request *evdns_base_resolve_reverse_ipv6( struct evdns_base *base, const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); These resolve functions initiate a DNS request for a particular record. Each takes an evdns_base to use for the request, a resource to look up (either a hostname for forward lookups, or an address for reverse lookups), a set of flags to determine how to do the lookup, a callback to invoke when the lookup is done, and a pointer to pass to the user-supplied callback. The flags argument is either 0 or DNS_QUERY_NO_SEARCH to explicitly suppress searching in the list of search if the original search fails. DNS_QUERY_NO_SEARCH has no effect for reverse lookups, since those never do searching. When the request is done---either successfully or not---the callback function will be invoked. The callback takes a result that indicates success or an error code (see DNS Errors table below), a record type (one of DNS_IPv4_A, DNS_IPv6_AAAA, or DNS_PTR), the number of records in addresses, a time-to-live in seconds, the addresses themselves, and the user-supplied argument pointer. The addresses argument to the callback is NULL in the event of an error. For a PTR record, it’s a NUL-terminated string. For IPv4 records, it is an array of four-byte values in network order. For IPv6 records, it is an array of 16-byte records in network order. (Note that the number of addresses can be 0 even if there was no error. This can happen when the name exists, but it has no records of the requested type.) The errors codes that can be passed to the callback are as follows: DNS Errors [options="header",width="70%"
(DNS_ERR_NODATA was new in 2.0.15-stable.) You can decode these error codes to a human-readable string with: Interface
const char *evdns_err_to_string(int err); Each resolve function returns a pointer to an opaque evdns_request structure. You can use this to cancel the request at any point before the callback is invoked: Interface
void evdns_cancel_request(struct evdns_base *base, struct evdns_request *req); Canceling a request with this function makes its callback get invoked with the DNS_ERR_CANCEL result code. Suspending DNS client operations and changing nameserversSometimes you want to reconfigure or shut down the DNS subsystem without affecting in-flight DNS request too much. Interface
int evdns_base_clear_nameservers_and_suspend(struct evdns_base *base); int evdns_base_resume(struct evdns_base *base); If you call evdns_base_clear_nameservers_and_suspend() on an evdns_base, all nameservers are removed, and pending requests are left in limbo until later you re-add nameservers and call evdns_base_resume(). These functions return 0 on success and -1 on failure. They were introduced in Libevent 2.0.1-alpha. DNS server interfacesLibevent provides simple functionality for acting as a trivial DNS server and responding to UDP DNS requests. This section assumes some familiarity with the DNS protocol. Creating and closing a DNS serverInterface
struct evdns_server_port *evdns_add_server_port_with_base( struct event_base *base, evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data); typedef void (*evdns_request_callback_fn_type)( struct evdns_server_request *request, void *user_data); void evdns_close_server_port(struct evdns_server_port *port); To begin listening for DNS requests, call evdns_add_server_port_with_base(). It takes an event_base to use for event handling; a UDP socket to listen on; a flags variable (always 0 for now); a callback function to call when a new DNS query is received; and a pointer to user data that will be passed to the callback. It returns a new evdns_server_port object. When you are done with the DNS server, you can pass it to evdns_close_server_port(). The evdns_add_server_port_with_base() function was new in 2.0.1-alpha; evdns_close_server_port() was introduced in 1.3. Examining a DNS requestUnfortunately, Libevent doesn’t currently provide a great way to look at DNS requests via a programmatic interface. Instead, you’re stuck including event2/dns_struct.h and looking at the evdns_server_request structure manually. It would be great if a future version of Libevent provided a better way to do this. Interface
struct evdns_server_request { int flags; int nquestions; struct evdns_server_question **questions; }; #define EVDNS_QTYPE_AXFR 252 #define EVDNS_QTYPE_ALL 255 struct evdns_server_question { int type; int dns_question_class; char name[1]; }; The flags field of the request contains the DNS flags set in the request; the nquestions field is the number of questions in the request; and questions is an array of pointers to struct evdns_server_question. Each evdns_server_question includes the resource type of the request (see below for a list of EVDNS_*_TYPE macros), the class of the request (typically EVDNS_CLASS_INET), and the name of the requested hostname. These structures were introduced in Libevent 1.3. Before Libevent 1.4, dns_question_class was called "class", which made trouble for the C people. C programs that still use the old "class" name will stop working in a future release. Interface
int evdns_server_request_get_requesting_addr(struct evdns_server_request *req, struct sockaddr *sa, int addr_len); Sometimes you’ll want to know which address made a particular DNS request. You can check this by calling evdns_server_request_get_requesting_addr() on it. You should pass in a sockaddr with enough storage to hold the address: struct sockaddr_storage is recommended. This function was introduced in Libevent 1.3c. Responding to DNS requestsEvery time your DNS server receives a request, the request is passed to the callback function you provided, along with your user_data pointer. The callback function must either respond to the request, ignore the request, or make sure that the request is eventually answered or ignored. Before you respond to a request, you can add one or more answers to your response: Interface
int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); The functions above all add a single RR (of type A, AAAA, or CNAME respectively) to the answers section of a DNS reply for the request req. In each case the argument name is the hostname to add an answer for, and ttl is the time-to-live value of the answer in seconds. For A and AAAA records, n is the number of addresses to add, and addrs is a pointer to the raw addresses, either given as a sequence of n*4 bytes for IPv4 addresses in an A record, or as a sequence of n*16 bytes for IPv6 addresses in an AAAA record. These functions return 0 on success and -1 on failure. Interface
int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); This function adds a PTR record to the answer section of a request. The arguments req and ttl are as above. You must provide exactly one of in (an IPv4 address) or inaddr_name (an address in the .arpa domain) to indicate which address you’re providing a response for. The hostname argument is the answer for the PTR lookup. Interface
#define EVDNS_ANSWER_SECTION 0 #define EVDNS_AUTHORITY_SECTION 1 #define EVDNS_ADDITIONAL_SECTION 2 #define EVDNS_TYPE_A 1 #define EVDNS_TYPE_NS 2 #define EVDNS_TYPE_CNAME 5 #define EVDNS_TYPE_SOA 6 #define EVDNS_TYPE_PTR 12 #define EVDNS_TYPE_MX 15 #define EVDNS_TYPE_TXT 16 #define EVDNS_TYPE_AAAA 28 #define EVDNS_CLASS_INET 1 int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int dns_class, int ttl, int datalen, int is_name, const char *data); This function adds an arbitrary RR to the DNS reply of a request req. The section argument describes which section to add it to, and should be one of the EVDNS_*_SECTION values. The name argument is the name field of the RR. The type argument is the type field of the RR, and should be one of the EVDNS_TYPE_* values if possible. The dns_class argument is the class field of the RR, and should generally be EVDNS_CLASS_INET. The ttl argument is the time-to-live in seconds of the RR. The rdata and rdlength fields of the RR will be generated from the datalen bytes provided in data. If is_name is true, the data will be encoded as a DNS name (i.e., with DNS name compression). Otherwise, it’s included verbatim. Interface
int evdns_server_request_respond(struct evdns_server_request *req, int err); int evdns_server_request_drop(struct evdns_server_request *req); The evdns_server_request_respond() function sends a DNS response to a request, including all of the RRs that you attached to it, with the error code err. If you get a request that you don’t want to respond to, you can ignore it by calling evdns_server_request_drop() on it to release all the associated memory and bookkeeping structures. Interface
#define EVDNS_FLAGS_AA 0x400 #define EVDNS_FLAGS_RD 0x080 void evdns_server_request_set_flags(struct evdns_server_request *req, int flags); If you want to set any flags on your response message, you can call this function at any time before you send the response. All the functions in this section were introduced in Libevent 1.3, except for evdns_server_request_set_flags() which first appeared in Libevent 2.0.1-alpha. DNS Server exampleExample: A trivial DNS responder
#include <event2/dns.h> #include <event2/dns_struct.h> #include <event2/util.h> #include <event2/event.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <assert.h> /* Let's try binding to 5353. Port 53 is more traditional, but on most operating systems it requires root privileges. */ #define LISTEN_PORT 5353 #define LOCALHOST_IPV4_ARPA "1.0.0.127.in-addr.arpa" #define LOCALHOST_IPV6_ARPA ("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0." "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") const ev_uint8_t LOCALHOST_IPV4[] = { 127, 0, 0, 1 }; const ev_uint8_t LOCALHOST_IPV6[] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1 }; #define TTL 4242 /* This toy DNS server callback answers requests for localhost (mapping it to 127.0.0.1 or ::1) and for 127.0.0.1 or ::1 (mapping them to localhost). */ void server_callback(struct evdns_server_request *request, void *data) { int i; int error=DNS_ERR_NONE; /* We should try to answer all the questions. Some DNS servers don't do this reliably, though, so you should think hard before putting two questions in one request yourself. */ for (i=0; i < request->nquestions; i) { const struct evdns_server_question *q = request->questions[i]; int ok=-1; /* We don't use regular strcasecmp here, since we want a locale- independent comparison. */ if (0 == evutil_ascii_strcasecmp(q->name, "localhost")) { if (q->type == EVDNS_TYPE_A) ok = evdns_server_request_add_a_reply( request, q->name, 1, LOCALHOST_IPV4, TTL); else if (q->type == EVDNS_TYPE_AAAA) ok = evdns_server_request_add_aaaa_reply( request, q->name, 1, LOCALHOST_IPV6, TTL); } else if (0 == evutil_ascii_strcasecmp(q->name, LOCALHOST_IPV4_ARPA)) { if (q->type == EVDNS_TYPE_PTR) ok = evdns_server_request_add_ptr_reply( request, NULL, q->name, "LOCALHOST", TTL); } else if (0 == evutil_ascii_strcasecmp(q->name, LOCALHOST_IPV6_ARPA)) { if (q->type == EVDNS_TYPE_PTR) ok = evdns_server_request_add_ptr_reply( request, NULL, q->name, "LOCALHOST", TTL); } else { error = DNS_ERR_NOTEXIST; } if (ok<0 && error==DNS_ERR_NONE) error = DNS_ERR_SERVERFAILED; } /* Now send the reply. */ evdns_server_request_respond(request, error); } int main(int argc, char **argv) { struct event_base *base; struct evdns_server_port *server; evutil_socket_t server_fd; struct sockaddr_in listenaddr; base = event_base_new(); if (!base) return 1; server_fd = socket(AF_INET, SOCK_DGRAM, 0); if (server_fd < 0) return 1; memset(&listenaddr, 0, sizeof(listenaddr)); listenaddr.sin_family = AF_INET; listenaddr.sin_port = htons(LISTEN_PORT); listenaddr.sin_addr.s_addr = INADDR_ANY; if (bind(server_fd, (struct sockaddr*)&listenaddr, sizeof(listenaddr))<0) return 1; server = evdns_add_server_port_with_base(base, server_fd, 0, server_callback, NULL); event_base_dispatch(base); evdns_close_server_port(server); event_base_free(base); return 0; } Obsolete DNS interfacesObsolete Interfaces
void evdns_base_search_ndots_set(struct evdns_base *base, const int ndots); int evdns_base_nameserver_add(struct evdns_base *base, unsigned long int address); void evdns_set_random_bytes_fn(void (*fn)(char *, size_t)); struct evdns_server_port *evdns_add_server_port(evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data); Calling evdns_base_search_ndots_set() is equivalent to using evdns_base_set_option() with the "ndots" option. The evdns_base_nameserver_add() function behaves as evdns_base_nameserver_ip_add(), except it can only add nameservers with IPv4 addresses. It takes them, idiosyncratically, as four bytes in network order. Before Libevent 2.0.1-alpha, there was no way to specify a event base for a DNS server port. You had to use evdns_add_server_port() instead, which took the default event_base. From Libevent 2.0.1-alpha through 2.0.3-alpha, you could use evdns_set_random_bytes_fn to specify a function to use for generating random numbers instead of evdns_set_transaction_id_fn. It no longer has any effect, now that Libevent provides its own secure RNG. The DNS_QUERY_NO_SEARCH flag has also been called DNS_NO_SEARCH. Before Libevent 2.0.1-alpha, there was no separate notion of an evdns_base: all information in the evdns subsystem was stored globally, and the functions that manipulated it took no evdns_base as an argument. They are all now deprecated, and declared only in event2/dns_compat.h. They are implemented via a single global evdns_base; you can access this base by calling the evdns_get_global_base() function introduced in Libevent 2.0.3-alpha.
The EVDNS_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED macro is defined if and only if evdns_config_windows_nameservers() is available. |
|