diff options
Diffstat (limited to 'contrib/librdns/util.c')
-rw-r--r-- | contrib/librdns/util.c | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/contrib/librdns/util.c b/contrib/librdns/util.c new file mode 100644 index 0000000..0172ce0 --- /dev/null +++ b/contrib/librdns/util.c @@ -0,0 +1,1035 @@ +/* Copyright (c) 2014, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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. + */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <netdb.h> +#include <fcntl.h> +#include <ctype.h> + +#include "ottery.h" +#include "util.h" +#include "logger.h" +#include "rdns.h" + +inline void +rdns_request_remove_from_hash (struct rdns_request *req) +{ + /* Remove from id hashes */ + if (req->io) { + khiter_t k; + + k = kh_get(rdns_requests_hash, req->io->requests, req->id); + + if (k != kh_end(req->io->requests)) { + kh_del(rdns_requests_hash, req->io->requests, k); + } + } +} + +static int +rdns_make_socket_nonblocking (int fd) +{ + int ofl; + + ofl = fcntl (fd, F_GETFL, 0); + + if (fcntl (fd, F_SETFL, ofl | O_NONBLOCK) == -1) { + return -1; + } + return 0; +} + +static int +rdns_make_inet_socket (int type, struct addrinfo *addr, struct sockaddr **psockaddr, + socklen_t *psocklen) +{ + int fd = -1; + struct addrinfo *cur; + + cur = addr; + while (cur) { + /* Create socket */ + fd = socket (cur->ai_family, type, 0); + if (fd == -1) { + goto out; + } + + if (rdns_make_socket_nonblocking (fd) < 0) { + goto out; + } + + /* Set close on exec */ + if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) { + goto out; + } + + if (psockaddr) { + *psockaddr = cur->ai_addr; + *psocklen = cur->ai_addrlen; + } + break; +out: + if (fd != -1) { + close (fd); + } + fd = -1; + cur = cur->ai_next; + } + + return (fd); +} + +static int +rdns_make_unix_socket (const char *path, struct sockaddr_un *addr, int type) +{ + int fd = -1, serrno; + + if (path == NULL) { + return -1; + } + + addr->sun_family = AF_UNIX; + + memset (addr->sun_path, 0, sizeof (addr->sun_path)); + memccpy (addr->sun_path, path, 0, sizeof (addr->sun_path) - 1); +#ifdef FREEBSD + addr->sun_len = SUN_LEN (addr); +#endif + + fd = socket (PF_LOCAL, type, 0); + + if (fd == -1) { + return -1; + } + + if (rdns_make_socket_nonblocking (fd) < 0) { + goto out; + } + + /* Set close on exec */ + if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) { + goto out; + } + + return (fd); + + out: + serrno = errno; + if (fd != -1) { + close (fd); + } + errno = serrno; + return (-1); +} + +/** + * Make a universal socket + * @param credits host, ip or path to unix socket + * @param port port (used for network sockets) + * @param async make this socket asynced + * @param is_server make this socket as server socket + * @param try_resolve try name resolution for a socket (BLOCKING) + */ +int +rdns_make_client_socket (const char *credits, + uint16_t port, + int type, + struct sockaddr **psockaddr, + socklen_t *psocklen) +{ + struct sockaddr_un un; + struct stat st; + struct addrinfo hints, *res; + int r; + char portbuf[8]; + + if (*credits == '/') { + r = stat (credits, &st); + if (r == -1) { + /* Unix socket doesn't exists it must be created first */ + errno = ENOENT; + return -1; + } + else { + if ((st.st_mode & S_IFSOCK) == 0) { + /* Path is not valid socket */ + errno = EINVAL; + return -1; + } + else { + r = rdns_make_unix_socket (credits, &un, type); + + if (r != -1 && psockaddr) { + struct sockaddr *cpy; + + cpy = calloc (1, sizeof (un)); + *psocklen = sizeof (un); + + if (cpy == NULL) { + close (r); + + return -1; + } + + memcpy (cpy, &un, *psocklen); + *psockaddr = cpy; + } + + return r; + } + } + } + else { + /* TCP related part */ + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = type; /* Type of the socket */ + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; + + snprintf (portbuf, sizeof (portbuf), "%d", (int)port); + if (getaddrinfo (credits, portbuf, &hints, &res) == 0) { + r = rdns_make_inet_socket (type, res, psockaddr, psocklen); + + if (r != -1 && psockaddr) { + struct sockaddr *cpy; + + cpy = calloc (1, *psocklen); + + if (cpy == NULL) { + close (r); + freeaddrinfo (res); + + return -1; + } + + memcpy (cpy, *psockaddr, *psocklen); + *psockaddr = cpy; + } + + freeaddrinfo (res); + return r; + } + else { + return -1; + } + } + + /* Not reached */ + return -1; +} + +const char * +rdns_strerror (enum dns_rcode rcode) +{ + rcode &= 0xf; + static char numbuf[16]; + + if ('\0' == dns_rcodes[rcode][0]) { + snprintf (numbuf, sizeof (numbuf), "UNKNOWN: %d", (int)rcode); + return numbuf; + } + return dns_rcodes[rcode]; +} + +const char * +rdns_strtype (enum rdns_request_type type) +{ + return dns_types[type]; +} + +enum rdns_request_type +rdns_type_fromstr (const char *str) +{ + if (str) { + if (strcmp (str, "a") == 0) { + return RDNS_REQUEST_A; + } + else if (strcmp (str, "ns") == 0) { + return RDNS_REQUEST_NS; + } + else if (strcmp (str, "soa") == 0) { + return RDNS_REQUEST_SOA; + } + else if (strcmp (str, "ptr") == 0) { + return RDNS_REQUEST_PTR; + } + else if (strcmp (str, "mx") == 0) { + return RDNS_REQUEST_MX; + } + else if (strcmp (str, "srv") == 0) { + return RDNS_REQUEST_SRV; + } + else if (strcmp (str, "txt") == 0) { + return RDNS_REQUEST_TXT; + } + else if (strcmp (str, "spf") == 0) { + return RDNS_REQUEST_SPF; + } + else if (strcmp (str, "aaaa") == 0) { + return RDNS_REQUEST_AAAA; + } + else if (strcmp (str, "tlsa") == 0) { + return RDNS_REQUEST_TLSA; + } + else if (strcmp (str, "cname") == 0) { + return RDNS_REQUEST_CNAME; + } + else if (strcmp (str, "any") == 0) { + return RDNS_REQUEST_ANY; + } + } + + return RDNS_REQUEST_INVALID; +} + +const char * +rdns_str_from_type (enum rdns_request_type rcode) +{ + switch (rcode) { + case RDNS_REQUEST_INVALID: + return "(invalid)"; + case RDNS_REQUEST_A: + return "a"; + case RDNS_REQUEST_NS: + return "ns"; + case RDNS_REQUEST_SOA: + return "soa"; + case RDNS_REQUEST_PTR: + return "ptr"; + case RDNS_REQUEST_MX: + return "mx"; + case RDNS_REQUEST_TXT: + return "txt"; + case RDNS_REQUEST_SRV: + return "srv"; + case RDNS_REQUEST_SPF: + return "spf"; + case RDNS_REQUEST_AAAA: + return "aaaa"; + case RDNS_REQUEST_TLSA: + return "tlsa"; + case RDNS_REQUEST_CNAME: + return "cname"; + case RDNS_REQUEST_ANY: + return "any"; + default: + return "(unknown)"; + } + +} + +enum dns_rcode +rdns_rcode_fromstr (const char *str) +{ + if (str) { + if (strcmp (str, "noerror") == 0) { + return RDNS_RC_NOERROR; + } + else if (strcmp (str, "formerr") == 0) { + return RDNS_RC_FORMERR; + } + else if (strcmp (str, "servfail") == 0) { + return RDNS_RC_SERVFAIL; + } + else if (strcmp (str, "nxdomain") == 0) { + return RDNS_RC_NXDOMAIN; + } + else if (strcmp (str, "notimp") == 0) { + return RDNS_RC_NOTIMP; + } + else if (strcmp (str, "yxdomain") == 0) { + return RDNS_RC_YXDOMAIN; + } + else if (strcmp (str, "yxrrset") == 0) { + return RDNS_RC_YXRRSET; + } + else if (strcmp (str, "nxrrset") == 0) { + return RDNS_RC_NXRRSET; + } + else if (strcmp (str, "notauth") == 0) { + return RDNS_RC_NOTAUTH; + } + else if (strcmp (str, "notzone") == 0) { + return RDNS_RC_NOTZONE; + } + else if (strcmp (str, "timeout") == 0) { + return RDNS_RC_TIMEOUT; + } + else if (strcmp (str, "neterr") == 0) { + return RDNS_RC_NETERR; + } + else if (strcmp (str, "norec") == 0) { + return RDNS_RC_NOREC; + } + } + + return RDNS_RC_INVALID; +} + +uint16_t +rdns_permutor_generate_id (void) +{ + uint16_t id; + + id = ottery_rand_unsigned (); + + return id; +} + +struct rdns_reply * +rdns_make_reply (struct rdns_request *req, enum dns_rcode rcode) +{ + struct rdns_reply *rep; + + rep = malloc (sizeof (struct rdns_reply)); + if (rep != NULL) { + rep->request = req; + rep->resolver = req->resolver; + rep->entries = NULL; + rep->code = rcode; + req->reply = rep; + rep->flags = 0; + rep->requested_name = req->requested_names[0].name; + } + + return rep; +} + +void +rdns_reply_free (struct rdns_reply *rep) +{ + struct rdns_reply_entry *entry, *tmp; + + /* We don't need to free data for faked replies */ + if (!rep->request || rep->request->state != RDNS_REQUEST_FAKE) { + LL_FOREACH_SAFE (rep->entries, entry, tmp) { + switch (entry->type) { + case RDNS_REQUEST_PTR: + free (entry->content.ptr.name); + break; + case RDNS_REQUEST_NS: + free (entry->content.ns.name); + break; + case RDNS_REQUEST_MX: + free (entry->content.mx.name); + break; + case RDNS_REQUEST_TXT: + case RDNS_REQUEST_SPF: + free (entry->content.txt.data); + break; + case RDNS_REQUEST_SRV: + free (entry->content.srv.target); + break; + case RDNS_REQUEST_TLSA: + free (entry->content.tlsa.data); + break; + case RDNS_REQUEST_SOA: + free (entry->content.soa.mname); + free (entry->content.soa.admin); + break; + case RDNS_REQUEST_CNAME: + free(entry->content.cname.name); + break; + default: + break; + } + free (entry); + } + } + + free (rep); +} + +void +rdns_request_free (struct rdns_request *req) +{ + unsigned int i; + + if (req != NULL) { + if (req->packet != NULL) { + free (req->packet); + } + for (i = 0; i < req->qcount; i ++) { + free (req->requested_names[i].name); + } + if (req->requested_names != NULL) { + free (req->requested_names); + } + if (req->reply != NULL) { + rdns_reply_free (req->reply); + } + if (req->async_event) { + if (req->state == RDNS_REQUEST_WAIT_REPLY) { + /* Remove timer */ + req->async->del_timer (req->async->data, + req->async_event); + rdns_request_remove_from_hash(req); + req->async_event = NULL; + } + else if (req->state == RDNS_REQUEST_WAIT_SEND) { + /* Remove retransmit event */ + req->async->del_write (req->async->data, + req->async_event); + rdns_request_remove_from_hash(req); + req->async_event = NULL; + } + else if (req->state == RDNS_REQUEST_FAKE) { + req->async->del_write (req->async->data, + req->async_event); + req->async_event = NULL; + } + } + if (req->state == RDNS_REQUEST_TCP) { + if (req->async_event) { + req->async->del_timer (req->async->data, + req->async_event); + } + + rdns_request_remove_from_hash(req); + } +#ifdef TWEETNACL + if (req->curve_plugin_data != NULL) { + req->resolver->curve_plugin->cb.curve_plugin.finish_cb ( + req, req->resolver->curve_plugin->data); + } +#endif + if (req->io != NULL && req->state > RDNS_REQUEST_NEW) { + REF_RELEASE (req->io); + REF_RELEASE (req->resolver); + } + + free (req); + } +} + +void +rdns_ioc_free (struct rdns_io_channel *ioc) +{ + struct rdns_request *req; + + if (IS_CHANNEL_TCP(ioc)) { + rdns_ioc_tcp_reset(ioc); + } + + kh_foreach_value(ioc->requests, req, { + REF_RELEASE (req); + }); + + if (ioc->async_io) { + ioc->resolver->async->del_read(ioc->resolver->async->data, + ioc->async_io); + } + kh_destroy(rdns_requests_hash, ioc->requests); + + if (ioc->sock != -1) { + close(ioc->sock); + } + + if (ioc->saddr != NULL) { + free(ioc->saddr); + } + + free (ioc); +} + +struct rdns_io_channel * +rdns_ioc_new (struct rdns_server *serv, + struct rdns_resolver *resolver, + bool is_tcp) +{ + struct rdns_io_channel *nioc; + + if (is_tcp) { + nioc = calloc (1, sizeof (struct rdns_io_channel) + + sizeof (struct rdns_tcp_channel)); + } + else { + nioc = calloc (1, sizeof (struct rdns_io_channel)); + } + + if (nioc == NULL) { + rdns_err ("calloc fails to allocate rdns_io_channel"); + return NULL; + } + + nioc->struct_magic = RDNS_IO_CHANNEL_TAG; + nioc->srv = serv; + nioc->resolver = resolver; + + nioc->sock = rdns_make_client_socket (serv->name, serv->port, + is_tcp ? SOCK_STREAM : SOCK_DGRAM, &nioc->saddr, &nioc->slen); + if (nioc->sock == -1) { + rdns_err ("cannot open socket to %s: %s", serv->name, + strerror (errno)); + free (nioc); + return NULL; + } + + if (is_tcp) { + /* We also need to connect a TCP channel and set a TCP buffer */ + nioc->tcp = (struct rdns_tcp_channel *)(((unsigned char *)nioc) + sizeof(*nioc)); + + if (!rdns_ioc_tcp_connect(nioc)) { + rdns_err ("cannot connect TCP socket to %s: %s", serv->name, + strerror (errno)); + close (nioc->sock); + free (nioc); + + return NULL; + } + + nioc->flags |= RDNS_CHANNEL_TCP; + } + else { + nioc->flags |= RDNS_CHANNEL_ACTIVE; + nioc->async_io = resolver->async->add_read(resolver->async->data, + nioc->sock, nioc); + } + + nioc->requests = kh_init(rdns_requests_hash); + REF_INIT_RETAIN (nioc, rdns_ioc_free); + + return nioc; +} + +void +rdns_resolver_release (struct rdns_resolver *resolver) +{ + REF_RELEASE (resolver); +} + +struct rdns_request* +rdns_request_retain (struct rdns_request *req) +{ + REF_RETAIN (req); + return req; +} + +void +rdns_request_unschedule (struct rdns_request *req, bool remove_from_hash) +{ + struct rdns_resolver *resolver = req->resolver; + + switch (req->state) { + case RDNS_REQUEST_WAIT_REPLY: + /* We have a timer pending */ + if (req->async_event) { + req->async->del_timer (req->async->data, + req->async_event); + if (remove_from_hash) { + rdns_request_remove_from_hash(req); + } + req->async_event = NULL; + } + break; + case RDNS_REQUEST_WAIT_SEND: + /* We have write request pending */ + if (req->async_event) { + req->async->del_write (req->async->data, + req->async_event); + /* Remove from id hashes */ + if (remove_from_hash) { + rdns_request_remove_from_hash(req); + } + req->async_event = NULL; + } + break; + case RDNS_REQUEST_TCP: + /* We also have a timer */ + if (req->async_event) { + if (remove_from_hash) { + rdns_request_remove_from_hash(req); + } + + req->async->del_timer(req->async->data, + req->async_event); + + req->async_event = NULL; + } + default: + /* Nothing to unschedule, so blame if we have any event pending */ + if (req->async_event) { + rdns_err("internal error: have unexpected pending async state on stage %d", + req->state); + } + break; + } +} + +void +rdns_request_release (struct rdns_request *req) +{ + rdns_request_unschedule (req, true); + REF_RELEASE (req); +} + +void +rdns_ioc_tcp_reset (struct rdns_io_channel *ioc) +{ + struct rdns_resolver *resolver = ioc->resolver; + + if (IS_CHANNEL_CONNECTED(ioc)) { + if (ioc->tcp->async_write) { + resolver->async->del_write (resolver->async->data, ioc->tcp->async_write); + ioc->tcp->async_write = NULL; + } + if (ioc->tcp->async_read) { + resolver->async->del_read (resolver->async->data, ioc->tcp->async_read); + ioc->tcp->async_read = NULL; + } + + /* Clean all buffers and temporaries */ + if (ioc->tcp->cur_read_buf) { + free (ioc->tcp->cur_read_buf); + ioc->tcp->read_buf_allocated = 0; + ioc->tcp->next_read_size = 0; + ioc->tcp->cur_read = 0; + ioc->tcp->cur_read_buf = NULL; + } + + struct rdns_tcp_output_chain *oc, *tmp; + DL_FOREACH_SAFE(ioc->tcp->output_chain, oc, tmp) { + DL_DELETE (ioc->tcp->output_chain, oc); + free (oc); + } + + ioc->tcp->cur_output_chains = 0; + ioc->tcp->output_chain = NULL; + + ioc->flags &= ~RDNS_CHANNEL_CONNECTED; + } + + /* Remove all requests pending as we are unable to complete them */ + struct rdns_request *req; + kh_foreach_value(ioc->requests, req, { + struct rdns_reply *rep = rdns_make_reply (req, RDNS_RC_NETERR); + /* + * Unschedule request explicitly as we set state to RDNS_REQUEST_REPLIED + * that will prevent timer from being removed on req dtor. + * + * We skip hash removal here, as the hash will be cleared as a single + * operation afterwards. + */ + rdns_request_unschedule(req, false); + req->state = RDNS_REQUEST_REPLIED; + req->func (rep, req->arg); + REF_RELEASE (req); + }); + + if (ioc->sock != -1) { + close (ioc->sock); + ioc->sock = -1; + } + if (ioc->saddr) { + free (ioc->saddr); + ioc->saddr = NULL; + } + + kh_clear(rdns_requests_hash, ioc->requests); +} + +bool +rdns_ioc_tcp_connect (struct rdns_io_channel *ioc) +{ + struct rdns_resolver *resolver = ioc->resolver; + + if (IS_CHANNEL_CONNECTED(ioc)) { + rdns_err ("trying to connect already connected IO channel!"); + return false; + } + + if (ioc->flags & RDNS_CHANNEL_TCP_CONNECTING) { + /* Already connecting channel, ignore connect request */ + + return true; + } + + if (ioc->sock == -1) { + ioc->sock = rdns_make_client_socket (ioc->srv->name, ioc->srv->port, + SOCK_STREAM, &ioc->saddr, &ioc->slen); + if (ioc->sock == -1) { + rdns_err ("cannot open socket to %s: %s", ioc->srv->name, + strerror (errno)); + + if (ioc->saddr) { + free (ioc->saddr); + ioc->saddr = NULL; + } + + return false; + } + } + + int r = connect (ioc->sock, ioc->saddr, ioc->slen); + + if (r == -1) { + if (errno != EAGAIN && errno != EINTR && errno != EINPROGRESS) { + rdns_err ("cannot connect a TCP socket: %s for server %s", + strerror(errno), ioc->srv->name); + close (ioc->sock); + + if (ioc->saddr) { + free (ioc->saddr); + ioc->saddr = NULL; + } + + ioc->sock = -1; + + return false; + } + else { + /* We need to wait for write readiness here */ + if (ioc->tcp->async_write != NULL) { + rdns_err("internal rdns error: write event is already registered on connect"); + } + else { + ioc->tcp->async_write = resolver->async->add_write(resolver->async->data, + ioc->sock, ioc); + } + /* Prevent double connect attempts */ + ioc->flags |= RDNS_CHANNEL_TCP_CONNECTING; + } + } + else { + /* Always be ready to read from a TCP socket */ + ioc->flags |= RDNS_CHANNEL_CONNECTED|RDNS_CHANNEL_ACTIVE; + ioc->flags &= ~RDNS_CHANNEL_TCP_CONNECTING; + ioc->tcp->async_read = resolver->async->add_read(resolver->async->data, + ioc->sock, ioc); + } + + return true; +} + +static bool +rdns_resolver_conf_process_line (struct rdns_resolver *resolver, + const char *line, rdns_resolv_conf_cb cb, void *ud) +{ + const char *p, *c, *end; + bool has_obrace = false, ret; + unsigned int port = dns_port; + char *cpy_buf; + + end = line + strlen (line); + + if (end - line > sizeof ("nameserver") - 1 && + strncmp (line, "nameserver", sizeof ("nameserver") - 1) == 0) { + p = line + sizeof ("nameserver") - 1; + /* Skip spaces */ + while (isspace (*p)) { + p ++; + } + + if (*p == '[') { + has_obrace = true; + p ++; + } + + if (isxdigit (*p) || *p == ':') { + c = p; + while (isxdigit (*p) || *p == ':' || *p == '.') { + p ++; + } + if (has_obrace && *p != ']') { + return false; + } + else if (*p != '\0' && !isspace (*p) && *p != '#') { + return false; + } + + if (has_obrace) { + p ++; + if (*p == ':') { + /* Maybe we have a port definition */ + port = strtoul (p + 1, NULL, 10); + if (port == 0 || port > UINT16_MAX) { + return false; + } + } + } + + cpy_buf = malloc (p - c + 1); + assert (cpy_buf != NULL); + memcpy (cpy_buf, c, p - c); + cpy_buf[p - c] = '\0'; + + if (cb == NULL) { + ret = rdns_resolver_add_server (resolver, cpy_buf, port, 0, + default_io_cnt) != NULL; + } + else { + ret = cb (resolver, cpy_buf, port, 0, + default_io_cnt, ud); + } + + free (cpy_buf); + + return ret; + } + else { + return false; + } + } + /* XXX: skip unknown resolv.conf lines */ + + return false; +} + +bool +rdns_resolver_parse_resolv_conf_cb (struct rdns_resolver *resolver, + const char *path, rdns_resolv_conf_cb cb, void *ud) +{ + FILE *in; + char buf[BUFSIZ]; + char *p; + bool processed = false; + + in = fopen (path, "r"); + + if (in == NULL) { + return false; + } + + while (!feof (in)) { + if (fgets (buf, sizeof (buf) - 1, in) == NULL) { + break; + } + + /* Strip trailing spaces */ + p = buf + strlen (buf) - 1; + while (p > buf && + (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) { + *p-- = '\0'; + } + + if (rdns_resolver_conf_process_line (resolver, buf, cb, ud)) { + processed = true; + } + } + + fclose (in); + + return processed; +} + +bool +rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver, const char *path) +{ + return rdns_resolver_parse_resolv_conf_cb (resolver, path, NULL, NULL); +} + +bool +rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type) +{ + unsigned int i; + + for (i = 0; i < req->qcount; i ++) { + if (req->requested_names[i].type == type) { + return true; + } + } + + return false; +} + +const struct rdns_request_name * +rdns_request_get_name (struct rdns_request *req, unsigned int *count) +{ + + if (count != NULL) { + *count = req->qcount; + } + return req->requested_names; +} + +const char* +rdns_request_get_server (struct rdns_request *req) +{ + if (req && req->io) { + return req->io->srv->name; + } + + return NULL; +} + +char * +rdns_generate_ptr_from_str (const char *str) +{ + union { + struct in_addr v4; + struct in6_addr v6; + } addr; + char *res = NULL; + unsigned char *bytes; + size_t len; + + if (inet_pton (AF_INET, str, &addr.v4) == 1) { + bytes = (unsigned char *)&addr.v4; + + len = 4 * 4 + sizeof ("in-addr.arpa"); + res = malloc (len); + if (res) { + snprintf (res, len, "%u.%u.%u.%u.in-addr.arpa", + (unsigned)bytes[3]&0xFF, + (unsigned)bytes[2]&0xFF, + (unsigned)bytes[1]&0xFF, + (unsigned)bytes[0]&0xFF); + } + } + else if (inet_pton (AF_INET6, str, &addr.v6) == 1) { + bytes = (unsigned char *)&addr.v6; + + len = 2*32 + sizeof ("ip6.arpa"); + res = malloc (len); + if (res) { + snprintf(res, len, + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa", + bytes[15]&0xF, bytes[15] >> 4, bytes[14]&0xF, bytes[14] >> 4, + bytes[13]&0xF, bytes[13] >> 4, bytes[12]&0xF, bytes[12] >> 4, + bytes[11]&0xF, bytes[11] >> 4, bytes[10]&0xF, bytes[10] >> 4, + bytes[9]&0xF, bytes[9] >> 4, bytes[8]&0xF, bytes[8] >> 4, + bytes[7]&0xF, bytes[7] >> 4, bytes[6]&0xF, bytes[6] >> 4, + bytes[5]&0xF, bytes[5] >> 4, bytes[4]&0xF, bytes[4] >> 4, + bytes[3]&0xF, bytes[3] >> 4, bytes[2]&0xF, bytes[2] >> 4, + bytes[1]&0xF, bytes[1] >> 4, bytes[0]&0xF, bytes[0] >> 4); + } + } + + return res; +} |