From 798e297be094c9872d7dd4121e61d39698e09ebc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 16 Feb 2007 12:49:17 +0000 Subject: Merge res_query patch from Sjoerd Simons git-svn-id: file:///home/lennart/svn/public/libasyncns/trunk@23 cc0fb855-19ed-0310-866e-8c1d96e4abae --- libasyncns/Makefile.am | 2 +- libasyncns/asyncns-test.c | 59 +++++++++++++++++- libasyncns/asyncns.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++ libasyncns/asyncns.h | 20 +++++++ 4 files changed, 227 insertions(+), 3 deletions(-) (limited to 'libasyncns') diff --git a/libasyncns/Makefile.am b/libasyncns/Makefile.am index 568d3fa..98d51e8 100644 --- a/libasyncns/Makefile.am +++ b/libasyncns/Makefile.am @@ -22,7 +22,7 @@ AM_LIBADD=$(PTHREAD_LIBS) AM_LDADD=$(PTHREAD_LIBS) lib_LTLIBRARIES=libasyncns.la -libasyncns_la_LIBADD=$(AM_LIBADD) +libasyncns_la_LIBADD=$(AM_LIBADD) -lresolv libasyncns_la_CC=$(PTHREAD_CC) libasyncns_la_SOURCES=asyncns.c asyncns.h diff --git a/libasyncns/asyncns-test.c b/libasyncns/asyncns-test.c index a6e2d72..a59ee30 100644 --- a/libasyncns/asyncns-test.c +++ b/libasyncns/asyncns-test.c @@ -24,16 +24,21 @@ #include #include #include +#include +#include +#include +#include #include "asyncns.h" int main(int argc, char *argv[]) { asyncns_t* asyncns = NULL; - asyncns_query_t *q1, *q2; + asyncns_query_t *q1, *q2, *q3; int r = 1, ret; struct addrinfo *ai, hints; struct sockaddr_in sa; char host[NI_MAXHOST] = "", serv[NI_MAXSERV] = ""; + unsigned char *srv; if (!(asyncns = asyncns_new(10))) { fprintf(stderr, "asyncns_new() failed\n"); @@ -55,8 +60,12 @@ int main(int argc, char *argv[]) { q2 = asyncns_getnameinfo(asyncns, (struct sockaddr*) &sa, sizeof(sa), 0, 1, 1); + q3 = asyncns_res_query(asyncns, "_xmpp-client._tcp.gmail.com", C_IN, T_SRV); + /* Wait until the two queries are completed */ - while (!asyncns_isdone(asyncns, q1) || !asyncns_isdone(asyncns, q2)) { + while (!asyncns_isdone(asyncns, q1) + || !asyncns_isdone(asyncns, q2) + || !asyncns_isdone(asyncns, q3)) { if (asyncns_wait(asyncns, 1) < 0) goto fail; } @@ -87,6 +96,52 @@ int main(int argc, char *argv[]) { fprintf(stderr, "error: %s %i\n", gai_strerror(ret), ret); else printf("%s -- %s\n", host, serv); + + /* Interpret the result of the SRV lookup */ + if ((ret = asyncns_res_done(asyncns, q3, &srv)) < 0) { + fprintf(stderr, "error: %s %i\n", strerror(ret), ret); + } else if (ret == 0) { + fprintf(stderr, "No reply for SRV lookup\n"); + } else { + int qdcount; + int ancount; + int len; + const unsigned char *pos = srv + sizeof(HEADER); + unsigned char *end = srv + ret; + HEADER *head = (HEADER *)srv; + + qdcount = ntohs(head->qdcount); + ancount = ntohs(head->ancount); + + char name[256]; + + printf("%d answers for srv lookup:\n", ancount); + + /* Ignore the questions */ + while (qdcount-- > 0 && (len = dn_expand(srv, end, pos, name, 255)) >= 0) { + assert(len >= 0); + pos += len + QFIXEDSZ; + } + + /* Parse the answers */ + while (ancount-- > 0 && (len = dn_expand(srv, end, pos, name, 255)) >= 0) { + /* Ignore the initial string */ + uint16_t pref, weight, port; + assert(len >= 0); + pos += len; + /* Ignore type, ttl, class and dlen */ + pos += 10; + + GETSHORT(pref, pos); + GETSHORT(weight, pos); + GETSHORT(port, pos); + len = dn_expand(srv, end, pos, name, 255); + printf("\tpreference: %2d weight: %2d port: %d host: %s\n", + pref, weight, port, name); + + pos += len; + } + } r = 0; diff --git a/libasyncns/asyncns.c b/libasyncns/asyncns.c index 1a86c05..e312e36 100644 --- a/libasyncns/asyncns.c +++ b/libasyncns/asyncns.c @@ -37,6 +37,10 @@ #include #include #include +#include +#include +#include + #ifdef HAVE_SYS_PRCTL_H #include @@ -57,6 +61,9 @@ typedef enum { RESPONSE_ADDRINFO, REQUEST_NAMEINFO, RESPONSE_NAMEINFO, + REQUEST_RES_QUERY, + REQUEST_RES_SEARCH, + RESPONSE_RES, REQUEST_TERMINATE } query_type_t; @@ -140,6 +147,18 @@ typedef struct nameinfo_response { int ret; } nameinfo_response_t; +typedef struct res_query_request { + struct rheader header; + int class; + int type; + size_t dlen; +} res_request_t; + +typedef struct res_query_response { + struct rheader header; + int ret; +} res_response_t; + #ifndef HAVE_STRNDUP static char *strndup(const char *s, size_t l) { @@ -271,6 +290,25 @@ static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *hos return send(out_fd, resp, resp->header.length, 0); } +static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret) { + uint8_t data[BUFSIZE]; + res_response_t *resp = (res_response_t *) data; + + assert(out_fd >= 0); + + resp->header.type = RESPONSE_RES; + resp->header.id = id; + resp->header.length = sizeof(res_response_t) + (ret < 0 ? 0 : ret); + resp->ret = (ret < 0) ? -errno : ret; + + assert(sizeof(data) >= resp->header.length); + + if (ret > 0) + memcpy(data + sizeof(res_response_t), answer, ret); + + return send(out_fd, resp, resp->header.length, 0); +} + static int handle_request(int out_fd, const rheader_t *req, size_t length) { assert(out_fd >= 0); assert(req); @@ -325,6 +363,28 @@ static int handle_request(int out_fd, const rheader_t *req, size_t length) { ret == 0 && ni_req->getserv ? servbuf : NULL); } + case REQUEST_RES_QUERY: + case REQUEST_RES_SEARCH: { + int ret; + unsigned char answer[BUFSIZE]; + const res_request_t *res_req = (res_request_t *)req; + const char *dname; + + assert(length >= sizeof(res_request_t)); + assert(length == sizeof(res_request_t) + res_req->dlen); + + dname = (const char *) req + sizeof(res_request_t); + + if (req->type == REQUEST_RES_QUERY) { + ret = res_query(dname, res_req->class, res_req->type, + answer, BUFSIZE); + } else { + ret = res_search(dname, res_req->class, res_req->type, + answer, BUFSIZE); + } + return send_res_reply(out_fd, req->id, answer, ret); + } + case REQUEST_TERMINATE: { /* Quit */ return -1; @@ -700,6 +760,23 @@ static int handle_response(asyncns_t *asyncns, rheader_t *resp, size_t length) { complete_query(asyncns, q); break; } + + case RESPONSE_RES: { + const res_response_t *res_resp = (res_response_t *)resp; + + assert(length >= sizeof(res_response_t)); + assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH); + + q->ret = res_resp->ret; + + if (res_resp->ret >= 0) { + q->serv = malloc(res_resp->ret); + memcpy(q->serv, (char *)resp + sizeof(res_response_t), res_resp->ret); + } + + complete_query(asyncns, q); + break; + } default: ; @@ -908,6 +985,78 @@ int asyncns_getnameinfo_done(asyncns_t *asyncns, asyncns_query_t* q, char *ret_h return ret; } +static asyncns_query_t * +asyncns_res(asyncns_t *asyncns, query_type_t qtype, + const char *dname, int class, int type) { + uint8_t data[BUFSIZE]; + res_request_t *req = (res_request_t*) data; + asyncns_query_t *q; + size_t dlen; + + assert(asyncns); + assert(dname); + + dlen = strlen(dname); + + if (!(q = alloc_query(asyncns))) + return NULL; + + memset(req, 0, sizeof(res_request_t)); + + req->header.id = q->id; + req->header.type = q->type = qtype; + req->header.length = sizeof(res_request_t) + dlen; + + if (req->header.length > BUFSIZE) + goto fail; + + req->class = class; + req->type = type; + req->dlen = dlen; + + memcpy((uint8_t*) req + sizeof(res_request_t), dname, dlen); + + if (send(asyncns->fds[REQUEST_SEND_FD], req, req->header.length, 0) < 0) + goto fail; + + return q; + +fail: + if (q) + asyncns_cancel(asyncns, q); + + return NULL; +} + +asyncns_query_t* asyncns_res_query(asyncns_t *asyncns, const char *dname, int class, int type) { + return asyncns_res(asyncns, REQUEST_RES_QUERY, dname, class, type); +} + +asyncns_query_t* asyncns_res_search(asyncns_t *asyncns, const char *dname, int class, int type) { + return asyncns_res(asyncns, REQUEST_RES_SEARCH, dname, class, type); +} + +int asyncns_res_done(asyncns_t *asyncns, asyncns_query_t* q, unsigned char **answer) { + int ret; + assert(asyncns); + assert(q); + assert(q->asyncns == asyncns); + assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH); + assert(answer); + + if (!q->done) + return -EAGAIN; + + *answer = (unsigned char *)q->serv; + q->serv = NULL; + + ret = q->ret; + + asyncns_cancel(asyncns, q); + + return ret; +} + asyncns_query_t* asyncns_getnext(asyncns_t *asyncns) { assert(asyncns); return asyncns->done_head; diff --git a/libasyncns/asyncns.h b/libasyncns/asyncns.h index 6d3b542..87f0744 100644 --- a/libasyncns/asyncns.h +++ b/libasyncns/asyncns.h @@ -104,6 +104,26 @@ asyncns_query_t* asyncns_getnameinfo(asyncns_t *asyncns, const struct sockaddr * * returned. */ int asyncns_getnameinfo_done(asyncns_t *asyncns, asyncns_query_t* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen); +/** Issue an resolver query on the specified session. The arguments are + * compatible with the ones of libc's res_query(3). The function returns a new + * query object. When the query is completed you may retrieve the results using + * asyncns_res_done(). */ +asyncns_query_t* asyncns_res_query(asyncns_t *asyncns, const char *dname, int class, int type); + +/** Issue an resolver query on the specified session. The arguments are + * compatible with the ones of libc's res_search(3). The function returns a new + * query object. When the query is completed you may retrieve the results using + * asyncns_res_done(). */ +asyncns_query_t* asyncns_res_search(asyncns_t *asyncns, const char *dname, int class, int type); + +/** Retrieve the results of a preceding asyncns_res_query)( or + * asyncns_res_search call. The query object q is destroyed by this call and + * may not be used any further. Returns a pointer to the answer of the + * res_query call. If the query is not completed yet -EAGAIN is returned, on + * failure -errno is returned otherwise the length of answer is returned. */ +int asyncns_res_done(asyncns_t *asyncns, asyncns_query_t* q, unsigned char +**answer); + /** Return the next completed query object. If no query has been * completed yet, return NULL. Please note that you need to run * asyncns_wait() before this function will return sensible data. */ -- cgit