From b7e308d057551f315ef63d26aa26c79de26ba3f9 Mon Sep 17 00:00:00 2001 From: Dmitry Podgorny Date: Wed, 14 Oct 2015 18:48:55 +0300 Subject: [PATCH] Implemented resolver to replace sock_srv_lookup() --- .gitignore | 1 + Makefile.am | 16 +- configure.ac | 3 - src/conn.c | 17 +- src/ostypes.h | 10 + src/resolver.c | 641 ++++++++++++++++++++++++++++++++++++++ src/resolver.h | 41 +++ src/sock.c | 679 +---------------------------------------- src/sock.h | 6 +- tests/res_query_dump.c | 56 ++++ tests/test_resolver.c | 139 +++++++++ 11 files changed, 910 insertions(+), 699 deletions(-) create mode 100644 src/resolver.c create mode 100644 src/resolver.h create mode 100644 tests/res_query_dump.c create mode 100644 tests/test_resolver.c diff --git a/.gitignore b/.gitignore index 617d040..066a799 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ tests/test_hash tests/test_jid tests/test_md5 tests/test_rand +tests/test_resolver tests/test_sasl tests/test_scram tests/test_sha1 diff --git a/Makefile.am b/Makefile.am index f5614aa..2cc2f28 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,13 +22,13 @@ libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(DIST_LIBS) -no-undefined # Export only public API libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' libstrophe_la_SOURCES = src/auth.c src/conn.c src/ctx.c \ - src/event.c src/handler.c src/hash.c \ - src/jid.c src/md5.c src/sasl.c src/scram.c src/sha1.c \ + src/event.c src/handler.c src/hash.c src/jid.c src/md5.c \ + src/resolver.c src/sasl.c src/scram.c src/sha1.c \ src/snprintf.c src/sock.c src/stanza.c \ src/tls_openssl.c src/util.c src/rand.c src/uuid.c \ src/common.h src/hash.h src/md5.h src/ostypes.h src/parser.h \ - src/sasl.h src/scram.h src/sha1.h src/snprintf.h src/sock.h \ - src/tls.h src/util.h src/rand.h + src/resolver.h src/sasl.h src/scram.h src/sha1.h src/snprintf.h \ + src/sock.h src/tls.h src/util.h src/rand.h if PARSER_EXPAT libstrophe_la_SOURCES += src/parser_expat.c @@ -68,7 +68,8 @@ examples_uuid_LDADD = $(STROPHE_LIBS) ## Tests TESTS = tests/check_parser tests/test_sha1 tests/test_md5 tests/test_rand \ - tests/test_scram tests/test_base64 tests/test_snprintf + tests/test_scram tests/test_base64 tests/test_snprintf \ + tests/test_resolver check_PROGRAMS = $(TESTS) tests_check_parser_SOURCES = tests/check_parser.c tests/test.h @@ -82,6 +83,11 @@ tests_test_base64_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src tests_test_base64_LDADD = $(STROPHE_LIBS) tests_test_base64_LDFLAGS = -static +tests_test_resolver_SOURCES = tests/test_resolver.c tests/test.h +tests_test_resolver_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src +tests_test_resolver_LDADD = $(STROPHE_LIBS) +tests_test_resolver_LDFLAGS = -static + tests_test_rand_SOURCES = tests/test_rand.c tests/test.c src/sha1.c tests_test_rand_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src diff --git a/configure.ac b/configure.ac index 8f5fdfe..264c817 100644 --- a/configure.ac +++ b/configure.ac @@ -103,9 +103,6 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([ LIBS="${LIBS_TMP}" PC_LIBS="${DIST_LIBS} ${PC_LIBS}" -AC_CHECK_HEADERS([arpa/nameser_compat.h]) - - m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR], [AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], diff --git a/src/conn.c b/src/conn.c index c2f4177..1765a49 100644 --- a/src/conn.c +++ b/src/conn.c @@ -25,6 +25,7 @@ #include "common.h" #include "util.h" #include "parser.h" +#include "resolver.h" #ifndef DEFAULT_SEND_QUEUE_MAX /** @def DEFAULT_SEND_QUEUE_MAX @@ -56,7 +57,7 @@ static void _handle_stream_end(char *name, void * const userdata); static void _handle_stream_stanza(xmpp_stanza_t *stanza, void * const userdata); -static int _conn_default_port(xmpp_conn_t * const conn); +static unsigned short _conn_default_port(xmpp_conn_t * const conn); /** Create a new Strophe connection object. * @@ -407,7 +408,7 @@ int xmpp_connect_client(xmpp_conn_t * const conn, void * const userdata) { char domain[2048]; - int port; + unsigned short port; const char *prefdomain = NULL; int found; @@ -427,8 +428,8 @@ int xmpp_connect_client(xmpp_conn_t * const conn, prefdomain = altdomain; port = altport ? altport : _conn_default_port(conn); } else { - found = sock_srv_lookup("xmpp-client", "tcp", conn->domain, - domain, sizeof(domain), &port); + found = resolver_srv_lookup("xmpp-client", "tcp", conn->domain, + domain, sizeof(domain), &port); if (!found) { xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed, " "connecting via domain."); @@ -448,7 +449,7 @@ int xmpp_connect_client(xmpp_conn_t * const conn, domain[sizeof(domain) - 1] = '\0'; } conn->sock = sock_connect(domain, port); - xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%d returned %d", + xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%u returned %d", domain, port, conn->sock); if (conn->sock == -1) return -1; @@ -495,7 +496,7 @@ int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, unsigned short port, xmpp_conn_handler callback, void * const userdata) { - int connectport; + unsigned short connectport; if (conn->state != XMPP_STATE_DISCONNECTED) return -1; @@ -516,7 +517,7 @@ int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, xmpp_debug(conn->ctx, "xmpp", "Connecting via %s", server); conn->sock = sock_connect(server, connectport); - xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%d returned %d", + xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%u returned %d", server, connectport, conn->sock); if (conn->sock == -1) return -1; @@ -969,7 +970,7 @@ static void _handle_stream_stanza(xmpp_stanza_t *stanza, handler_fire_stanza(conn, stanza); } -static int _conn_default_port(xmpp_conn_t * const conn) +static unsigned short _conn_default_port(xmpp_conn_t * const conn) { switch (conn->type) { case XMPP_CLIENT: diff --git a/src/ostypes.h b/src/ostypes.h index 49a5dd3..c3c8f47 100644 --- a/src/ostypes.h +++ b/src/ostypes.h @@ -17,6 +17,8 @@ #ifndef __LIBSTROPHE_OSTYPES_H__ #define __LIBSTROPHE_OSTYPES_H__ +#include /* size_t */ + #if defined (_MSC_VER) && _MSC_VER < 1600 typedef signed char int8_t; typedef short int int16_t; @@ -27,6 +29,14 @@ typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t; + +#ifndef UINT32_MAX +#define UINT32_MAX 0xffffffff +#endif /* UINT32_MAX */ +#ifndef SIZE_MAX +#define SIZE_MAX UINT32_MAX +#endif /* SIZE_MAX */ + #else #include #endif diff --git a/src/resolver.c b/src/resolver.c new file mode 100644 index 0000000..5faa171 --- /dev/null +++ b/src/resolver.c @@ -0,0 +1,641 @@ +/* resolver.h + * strophe XMPP client library -- DNS resolver + * + * Copyright (C) 2015 Dmitry Podgorny + * + * This software is provided AS-IS with no warranty, either express + * or implied. + * + * This program is dual licensed under the MIT and GPLv3 licenses. + */ + +/** @file + * DNS resolver. + */ + +#ifndef _WIN32 +#include +#include +#include /* res_query */ +#endif /* _WIN32 */ + +#include /* strncpy */ + +#include "common.h" +#include "resolver.h" +#include "ostypes.h" + +#define MESSAGE_HEADER_LEN 12 +#define MESSAGE_RESPONSE 1 +#define MESSAGE_T_SRV 33 +#define MESSAGE_C_IN 1 + +struct message_header { + uint16_t id; + uint8_t octet2; + uint8_t octet3; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +}; + +#ifdef _WIN32 +static int resolver_win32_srv_lookup(const char *fulldomain, + char *target, size_t target_len, + unsigned short *port); +static int resolver_win32_srv_query(const char *fulldomain, + unsigned char *buf, size_t len); +#endif /* _WIN32 */ + +/* the same as ntohs(), but receives pointer to the value */ +static uint16_t xmpp_ntohs_ptr(const void *ptr) +{ + const uint8_t *p = (const uint8_t *)ptr; + + return (uint16_t)((p[0] << 8U) + p[1]); +} + +static uint8_t message_header_qr(const struct message_header *header) +{ + return (header->octet2 >> 7) & 1; +} + +static uint8_t message_header_rcode(const struct message_header *header) +{ + return header->octet3 & 0x0f; +} + +static unsigned message_name_get(const unsigned char *buf, size_t buf_len, + unsigned buf_offset, + char *name, size_t name_max) +{ + size_t name_len = 0; + unsigned i = buf_offset; + unsigned pointer; + unsigned char label_len; + + while ((label_len = buf[i++]) != 0) { + /* label */ + if ((label_len & 0xc0) == 0) { + if (name != NULL) { + if (name_len != 0) + name[name_len++] = '.'; + strncpy(&name[name_len], (char *)&buf[i], label_len); + } + i += label_len; + name_len += label_len; + + /* pointer */ + } else if ((label_len & 0xc0) == 0xc0) { + pointer = (label_len & 0x3f) << 8 | buf[i++]; + (void)message_name_get(buf, buf_len, pointer, &name[name_len], + name_max - name_len); + /* pointer is always the last */ + break; + + /* The 10 and 01 combinations are reserved for future use. */ + } else { + return 0; + } + } + if (label_len == 0 && name != NULL) + name[name_len] = '\0'; + + return i - buf_offset; +} + +static unsigned message_name_len(const unsigned char *buf, size_t buf_len, + unsigned buf_offset) +{ + return message_name_get(buf, buf_len, buf_offset, NULL, SIZE_MAX); +} + +int resolver_srv_lookup_buf(const unsigned char *buf, size_t len, + char *target, size_t target_len, + unsigned short *port) +{ + int set = 0; + unsigned i; + unsigned j; + unsigned name_len; + unsigned rdlength; + uint16_t type; + uint16_t class; + uint16_t priority; + uint16_t priority_min; + struct message_header header; + + if (len < MESSAGE_HEADER_LEN) + return 0; + + header.id = xmpp_ntohs_ptr(&buf[0]); + header.octet2 = buf[2]; + header.octet3 = buf[3]; + header.qdcount = xmpp_ntohs_ptr(&buf[4]); + header.ancount = xmpp_ntohs_ptr(&buf[6]); + header.nscount = xmpp_ntohs_ptr(&buf[8]); + header.arcount = xmpp_ntohs_ptr(&buf[10]); + if (message_header_qr(&header) != MESSAGE_RESPONSE || + message_header_rcode(&header) != 0) + { + return 0; + } + j = MESSAGE_HEADER_LEN; + + /* skip question section */ + for (i = 0; i < header.qdcount; ++i) { + name_len = message_name_len(buf, len, j); + if (name_len == 0) { + /* error in name format */ + return 0; + } + j += name_len + 4; + } + + /* + * RFC2052: A client MUST attempt to contact the target host + * with the lowest-numbered priority it can reach. + */ + for (i = 0; i < header.ancount; ++i) { + name_len = message_name_len(buf, len, j); + j += name_len; + type = xmpp_ntohs_ptr(&buf[j]); + class = xmpp_ntohs_ptr(&buf[j + 2]); + rdlength = xmpp_ntohs_ptr(&buf[j + 8]); + j += 10; + if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) { + priority = xmpp_ntohs_ptr(&buf[j]); + if (!set || priority < priority_min) { + *port = xmpp_ntohs_ptr(&buf[j + 4]); + name_len = message_name_get(buf, len, j + 6, target, target_len); + set = name_len > 0 ? 1 : 0; + priority_min = priority; + } + } + j += rdlength; + } + + return set; +} + +int resolver_srv_lookup(const char *service, const char *proto, + const char *domain, char *target, + size_t target_len, unsigned short *port) +{ + char fulldomain[2048]; + unsigned char buf[65535]; + int len; + int set = 0; + + xmpp_snprintf(fulldomain, sizeof(fulldomain), + "_%s._%s.%s", service, proto, domain); + +#ifdef _WIN32 + set = resolver_win32_srv_lookup(fulldomain, target, target_len, port); + if (set) + return set; + len = resolver_win32_srv_query(fulldomain, buf, sizeof(buf)); +#else /* _WIN32 */ + len = res_query(fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV, buf, sizeof(buf)); +#endif /* _WIN32 */ + + if (len > 0) + set = resolver_srv_lookup_buf(buf, (size_t)len, target, target_len, port); + + return set; +} + +/* FIXME: interface that returns array of results, maybe sorted by priority */ + +#ifdef _WIN32 + +/******************************************************************************* + * Next part was copied from sock.c and contains old win32 code. + * + * The idea is to get raw response from a name server and pass it to + * resolver_srv_lookup_buf(). In fact, resolver_win32_srv_query() replaces + * the call of res_query(). + * Dnsapi code is left unchanged and moved to a separated function + * resolver_srv_win32_lookup(). + * + * XXX If the code is compiled it should work like before. + ******************************************************************************/ + +#include +#include +#include +#include + +struct dnsquery_header +{ + unsigned short id; + unsigned char qr; + unsigned char opcode; + unsigned char aa; + unsigned char tc; + unsigned char rd; + unsigned char ra; + unsigned char z; + unsigned char rcode; + unsigned short qdcount; + unsigned short ancount; + unsigned short nscount; + unsigned short arcount; +}; + +struct dnsquery_question +{ + char qname[1024]; + unsigned short qtype; + unsigned short qclass; +}; + +static void netbuf_add_16bitnum(unsigned char *buf, int buflen, int *offset, unsigned short num) +{ + unsigned char *start = buf + *offset; + unsigned char *p = start; + + /* assuming big endian */ + *p++ = (num >> 8) & 0xff; + *p++ = (num) & 0xff; + + *offset += 2; +} + +static void netbuf_add_domain_name(unsigned char *buf, int buflen, int *offset, + char *name) +{ + unsigned char *start = buf + *offset; + unsigned char *p = start; + unsigned char *wordstart, *wordend; + + wordstart = (unsigned char *)name; + + while (*wordstart) + { + int len; + wordend = wordstart; + while (*wordend && *wordend != '.') + { + wordend++; + } + + len = (int)(wordend - wordstart); + + if (len > 0x3F) + { + len = 0x3F; + } + + *p++ = len; + + while (wordstart != wordend) + { + *p++ = *wordstart++; + } + + if (*wordstart == '.') + { + wordstart++; + } + } + + *p++ = '\0'; + + *offset += p - start; +} + +static void netbuf_add_dnsquery_header(unsigned char *buf, int buflen, int *offset, struct dnsquery_header *header) +{ + unsigned char *p; + + netbuf_add_16bitnum(buf, buflen, offset, header->id); + + p = buf + *offset; + *p++ = ((header->qr & 0x01) << 7) + | ((header->opcode & 0x0F) << 3) + | ((header->aa & 0x01) << 2) + | ((header->tc & 0x01) << 1) + | ((header->rd & 0x01)); + *p++ = ((header->ra & 0x01) << 7) + | ((header->z & 0x07) << 4) + | ((header->rcode & 0x0F)); + *offset += 2; + + netbuf_add_16bitnum(buf, buflen, offset, header->qdcount); + netbuf_add_16bitnum(buf, buflen, offset, header->ancount); + netbuf_add_16bitnum(buf, buflen, offset, header->nscount); + netbuf_add_16bitnum(buf, buflen, offset, header->arcount); +} + +static void netbuf_add_dnsquery_question(unsigned char *buf, int buflen, int *offset, struct dnsquery_question *question) +{ + netbuf_add_domain_name(buf, buflen, offset, question->qname); + netbuf_add_16bitnum(buf, buflen, offset, question->qtype); + netbuf_add_16bitnum(buf, buflen, offset, question->qclass); +} + +static int resolver_win32_srv_lookup(const char *fulldomain, + char *target, size_t target_len, + unsigned short *port) +{ + int set = 0; + + /* try using dnsapi first */ + if (!set) + { + HINSTANCE hdnsapi = NULL; + + DNS_STATUS (WINAPI * pDnsQuery_A)(PCSTR, WORD, DWORD, PIP4_ARRAY, PDNS_RECORD*, PVOID*); + void (WINAPI * pDnsRecordListFree)(PDNS_RECORD, DNS_FREE_TYPE); + + if (hdnsapi = LoadLibrary("dnsapi.dll")) { + + pDnsQuery_A = (void *)GetProcAddress(hdnsapi, "DnsQuery_A"); + pDnsRecordListFree = (void *)GetProcAddress(hdnsapi, "DnsRecordListFree"); + + if (pDnsQuery_A && pDnsRecordListFree) { + PDNS_RECORD dnsrecords = NULL; + DNS_STATUS error; + + error = pDnsQuery_A(fulldomain, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &dnsrecords, NULL); + + if (error == 0) { + PDNS_RECORD current = dnsrecords; + + while (current) { + if (current->wType == DNS_TYPE_SRV) { + xmpp_snprintf(target, target_len, "%s", current->Data.Srv.pNameTarget); + *port = current->Data.Srv.wPort; + set = 1; + + current = NULL; + } else { + current = current->pNext; + } + } + } + + pDnsRecordListFree(dnsrecords, DnsFreeRecordList); + } + + FreeLibrary(hdnsapi); + } + } + + return set; +} + +static int resolver_win32_srv_query(const char *fulldomain, + unsigned char *buf, size_t len) +{ + int set = 0; + int insize; + + /* if dnsapi didn't work/isn't there, try querying the dns server manually */ + if (!set) + { + struct dnsquery_header header; + struct dnsquery_question question; + int offset = 0; + int addrlen; + sock_t sock; + struct sockaddr_in dnsaddr; + char dnsserverips[16][256]; + int numdnsservers = 0; + int j; + + /* Try getting the DNS server ips from GetNetworkParams() in iphlpapi first */ + if (!numdnsservers) + { + HINSTANCE hiphlpapi = NULL; + DWORD (WINAPI * pGetNetworkParams)(PFIXED_INFO, PULONG); + + if (hiphlpapi = LoadLibrary("Iphlpapi.dll")) + { + pGetNetworkParams = (void *)GetProcAddress(hiphlpapi, "GetNetworkParams"); + + if (pGetNetworkParams) + { + FIXED_INFO *fi; + ULONG len; + DWORD error; + char buffer[65535]; + + len = 65535; + fi = buffer; + + if ((error = pGetNetworkParams(fi, &len)) == ERROR_SUCCESS) + { + IP_ADDR_STRING *pias = &(fi->DnsServerList); + + while (pias && numdnsservers < 16) + { + strcpy(dnsserverips[numdnsservers++], pias->IpAddress.String); + pias = pias->Next; + } + } + } + } + FreeLibrary(hiphlpapi); + } + + /* Next, try getting the DNS server ips from the registry */ + if (!numdnsservers) + { + HKEY search; + LONG error; + + error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", 0, KEY_READ, &search); + + if (error != ERROR_SUCCESS) + { + error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0, KEY_READ, &search); + } + + if (error == ERROR_SUCCESS) + { + char name[512]; + DWORD len = 512; + + error = RegQueryValueEx(search, "NameServer", NULL, NULL, (LPBYTE)name, &len); + + if (error != ERROR_SUCCESS) + { + error = RegQueryValueEx(search, "DhcpNameServer", NULL, NULL, (LPBYTE)name, &len); + } + + if (error == ERROR_SUCCESS) + { + char *parse = "0123456789.", *start, *end; + start = name; + end = name; + name[len] = '\0'; + + while (*start && numdnsservers < 16) + { + while (strchr(parse, *end)) + { + end++; + } + + strncpy(dnsserverips[numdnsservers++], start, end - start); + + while (*end && !strchr(parse, *end)) + { + end++; + } + + start = end; + } + } + } + + RegCloseKey(search); + } + + if (!numdnsservers) + { + HKEY searchlist; + LONG error; + + error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces", 0, KEY_READ, &searchlist); + + if (error == ERROR_SUCCESS) + { + unsigned int i; + DWORD numinterfaces = 0; + + RegQueryInfoKey(searchlist, NULL, NULL, NULL, &numinterfaces, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + for (i = 0; i < numinterfaces; i++) + { + char name[512]; + DWORD len = 512; + HKEY searchentry; + + RegEnumKeyEx(searchlist, i, (LPTSTR)name, &len, NULL, NULL, NULL, NULL); + + if (RegOpenKeyEx(searchlist, name, 0, KEY_READ, &searchentry) == ERROR_SUCCESS) + { + if (RegQueryValueEx(searchentry, "DhcpNameServer", NULL, NULL, (LPBYTE)name, &len) == ERROR_SUCCESS) + { + char *parse = "0123456789.", *start, *end; + start = name; + end = name; + name[len] = '\0'; + + while (*start && numdnsservers < 16) + { + while (strchr(parse, *end)) + { + end++; + } + + strncpy(dnsserverips[numdnsservers++], start, end - start); + + while (*end && !strchr(parse, *end)) + { + end++; + } + + start = end; + } + } + else if (RegQueryValueEx(searchentry, "NameServer", NULL, NULL, (LPBYTE)name, &len) == ERROR_SUCCESS) + { + char *parse = "0123456789.", *start, *end; + start = name; + end = name; + name[len] = '\0'; + + while (*start && numdnsservers < 16) + { + while (strchr(parse, *end)) + { + end++; + } + + strncpy(dnsserverips[numdnsservers++], start, end - start); + + while (*end && !strchr(parse, *end)) + { + end++; + } + + start = end; + } + } + RegCloseKey(searchentry); + } + } + RegCloseKey(searchlist); + } + } + + /* If we have a DNS server, use it */ + if (numdnsservers) + { + ULONG nonblocking = 1; + int i; + + memset(&header, 0, sizeof(header)); + header.id = 12345; /* FIXME: Get a better id here */ + header.rd = 1; + header.qdcount = 1; + + netbuf_add_dnsquery_header(buf, len, &offset, &header); + + memset(&question, 0, sizeof(question)); + strncpy(question.qname, fulldomain, 1024); + question.qtype = 33; /* SRV */ + question.qclass = 1; /* INTERNET! */ + + netbuf_add_dnsquery_question(buf, len, &offset, &question); + + insize = 0; + for (i = 0; i < numdnsservers && insize <= 0; i++) + { + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ioctlsocket(sock, FIONBIO, &nonblocking); + + memset(&dnsaddr, 0, sizeof(dnsaddr)); + + dnsaddr.sin_family = AF_INET; + dnsaddr.sin_port = htons(53); + dnsaddr.sin_addr.s_addr = inet_addr(dnsserverips[i]); + + addrlen = sizeof(dnsaddr); + sendto(sock, (char *)buf, offset, 0, (struct sockaddr *)&dnsaddr, addrlen); + for (j = 0; j < 50; j++) + { + insize = recvfrom(sock, (char *)buf, len, 0, (struct sockaddr *)&dnsaddr, &addrlen); + if (insize == SOCKET_ERROR) + { + if (sock_error() == WSAEWOULDBLOCK) + { + Sleep(100); + } + else + { + break; + } + } + else + { + break; + } + } + + closesocket(sock); + } + set = insize > 0; + } + + } + + return set ? insize : -1; +} + +#endif /* _WIN32 */ diff --git a/src/resolver.h b/src/resolver.h new file mode 100644 index 0000000..f4f662f --- /dev/null +++ b/src/resolver.h @@ -0,0 +1,41 @@ +/* resolver.h + * strophe XMPP client library -- DNS resolver + * + * Copyright (C) 2015 Dmitry Podgorny + * + * This software is provided AS-IS with no warranty, either express + * or implied. + * + * This program is dual licensed under the MIT and GPLv3 licenses. + */ + +/** @file + * DNS resolver. + */ + +#ifndef __LIBSTROPHE_RESOLVER_H__ +#define __LIBSTROPHE_RESOLVER_H__ + +#include "ostypes.h" + +/** Perform lookup for RFC1035 message format. */ +int resolver_srv_lookup_buf(const unsigned char *buf, size_t len, + char *target, size_t target_len, + unsigned short *port); + +/** Resolve SRV record. + * + * @param service service of the SRV record + * @param proto protocol of the SRV record + * @param domain resolving domain + * @param target pre-allocated string where result is stored + * @param target_len maximum size of the target + * @param port pointer where resulting port is stored + * + * @return 1 on success or 0 on fail + */ +int resolver_srv_lookup(const char *service, const char *proto, + const char *domain, char *target, + size_t target_len, unsigned short *port); + +#endif /* __LIBSTROPHE_RESOLVER_H__ */ diff --git a/src/sock.c b/src/sock.c index 05a13da..49ef205 100644 --- a/src/sock.c +++ b/src/sock.c @@ -21,7 +21,6 @@ #ifdef _WIN32 #include #include -#include #include #else #include @@ -30,11 +29,6 @@ #include #include #include -#include -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include -#endif -#include #endif #include "sock.h" @@ -73,7 +67,7 @@ static int _in_progress(int error) #endif } -sock_t sock_connect(const char * const host, const unsigned int port) +sock_t sock_connect(const char * const host, const unsigned short port) { sock_t sock; char service[6]; @@ -205,674 +199,3 @@ int sock_connect_error(const sock_t sock) return sock_error(); } - -struct dnsquery_header -{ - unsigned short id; - unsigned char qr; - unsigned char opcode; - unsigned char aa; - unsigned char tc; - unsigned char rd; - unsigned char ra; - unsigned char z; - unsigned char rcode; - unsigned short qdcount; - unsigned short ancount; - unsigned short nscount; - unsigned short arcount; -}; - -struct dnsquery_question -{ - char qname[1024]; - unsigned short qtype; - unsigned short qclass; -}; - -struct dnsquery_srvrdata -{ - unsigned short priority; - unsigned short weight; - unsigned short port; - char target[1024]; -}; - -struct dnsquery_resourcerecord -{ - char name[1024]; - unsigned short type; - unsigned short _class; - unsigned int ttl; - unsigned short rdlength; - struct dnsquery_srvrdata rdata; -}; - -static void netbuf_get_32bitnum(unsigned char *buf, int buflen, int *offset, unsigned int *num) -{ - unsigned char *start = buf + *offset; - unsigned char *p = start; - *num = 0; - - /* assuming big endian */ - *num |= (*p++) << 24; - *num |= (*p++) << 16; - *num |= (*p++) << 8; - *num |= (*p++); - - *offset += 4; -} - -static void netbuf_get_16bitnum(unsigned char *buf, int buflen, int *offset, unsigned short *num) -{ - unsigned char *start = buf + *offset; - unsigned char *p = start; - *num = 0; - - /* assuming big endian */ - *num |= (*p++) << 8; - *num |= (*p++); - - *offset += 2; -} - -static int netbuf_get_domain_name(unsigned char *buf, int buflen, int *offset, char *namebuf, int namebuflen) -{ - unsigned char *start = buf + *offset; - unsigned char *p, *p2; - int *curroffset = offset; - int len = 0; - - *namebuf = '\0'; - - /* measure length */ - p = start; - while (*p) - { - if ((*p & 0xC0) == 0xC0) - { - int newoffset = 0; - newoffset |= (*p++ & 0x3F) << 8; - newoffset |= *p++; - - p = buf + newoffset; - } - else - { - len += *p; - p += *p + 1; - } - } - - if (namebuflen < len) - { - return len; - } - - /* actually copy in name */ - p = start; - p2 = (unsigned char *)namebuf; - while (*p) - { - if ((*p & 0xC0) == 0xC0) - { - int newoffset = 0; - newoffset |= (*p++ & 0x3F) << 8; - newoffset |= *p++; - - if (curroffset) - { - *curroffset += (int)(p - start); - curroffset = NULL; - } - - p = buf + newoffset; - } - else - { - int i, partlen; - - if (*namebuf != '\0') - { - *p2++ = '.'; - } - - partlen = *p++; - - for (i=0; i < partlen; i++) - { - *p2++ = *p++; - } - } - } - - if (curroffset) - { - p++; - *curroffset += (int)(p - start); - curroffset = NULL; - } - - *p2 = '\0'; - - return 0; -} - -static void netbuf_get_dnsquery_header(unsigned char *buf, int buflen, int *offset, struct dnsquery_header *header) -{ - unsigned char *p; - - netbuf_get_16bitnum(buf, buflen, offset, &(header->id)); - - p = buf + *offset; - header->qr = (*p >> 7) & 0x01; - header->opcode = (*p >> 3) & 0x0F; - header->aa = (*p >> 2) & 0x01; - header->tc = (*p >> 1) & 0x01; - header->rd = (*p) & 0x01; - p++; - header->ra = (*p >> 7) & 0x01; - header->z = (*p >> 4) & 0x07; - header->rcode = (*p) & 0x0F; - p++; - *offset += 2; - - netbuf_get_16bitnum(buf, buflen, offset, &(header->qdcount)); - netbuf_get_16bitnum(buf, buflen, offset, &(header->ancount)); - netbuf_get_16bitnum(buf, buflen, offset, &(header->nscount)); - netbuf_get_16bitnum(buf, buflen, offset, &(header->arcount)); -} - -static void netbuf_get_dnsquery_question(unsigned char *buf, int buflen, int *offset, struct dnsquery_question *question) -{ - netbuf_get_domain_name(buf, buflen, offset, question->qname, 1024); - netbuf_get_16bitnum(buf, buflen, offset, &(question->qtype)); - netbuf_get_16bitnum(buf, buflen, offset, &(question->qclass)); -} - -static void netbuf_get_dnsquery_srvrdata(unsigned char *buf, int buflen, int *offset, struct dnsquery_srvrdata *srvrdata) -{ - netbuf_get_16bitnum(buf, buflen, offset, &(srvrdata->priority)); - netbuf_get_16bitnum(buf, buflen, offset, &(srvrdata->weight)); - netbuf_get_16bitnum(buf, buflen, offset, &(srvrdata->port)); - netbuf_get_domain_name(buf, buflen, offset, srvrdata->target, 1024); -} - -static void netbuf_get_dnsquery_resourcerecord(unsigned char *buf, int buflen, int *offset, struct dnsquery_resourcerecord *rr) -{ - netbuf_get_domain_name(buf, buflen, offset, rr->name, 1024); - netbuf_get_16bitnum(buf, buflen, offset, &(rr->type)); - netbuf_get_16bitnum(buf, buflen, offset, &(rr->_class)); - netbuf_get_32bitnum(buf, buflen, offset, &(rr->ttl)); - netbuf_get_16bitnum(buf, buflen, offset, &(rr->rdlength)); - if (rr->type == 33) /* SRV */ - { - int newoffset = *offset; - netbuf_get_dnsquery_srvrdata(buf, buflen, &newoffset, &(rr->rdata)); - } - *offset += rr->rdlength; -} - -#ifdef _WIN32 -static void netbuf_add_16bitnum(unsigned char *buf, int buflen, int *offset, unsigned short num) -{ - unsigned char *start = buf + *offset; - unsigned char *p = start; - - /* assuming big endian */ - *p++ = (num >> 8) & 0xff; - *p++ = (num) & 0xff; - - *offset += 2; -} - -static void netbuf_add_domain_name(unsigned char *buf, int buflen, int *offset, - char *name) -{ - unsigned char *start = buf + *offset; - unsigned char *p = start; - unsigned char *wordstart, *wordend; - - wordstart = (unsigned char *)name; - - while (*wordstart) - { - int len; - wordend = wordstart; - while (*wordend && *wordend != '.') - { - wordend++; - } - - len = (int)(wordend - wordstart); - - if (len > 0x3F) - { - len = 0x3F; - } - - *p++ = len; - - while (wordstart != wordend) - { - *p++ = *wordstart++; - } - - if (*wordstart == '.') - { - wordstart++; - } - } - - *p++ = '\0'; - - *offset += p - start; -} - -static void netbuf_add_dnsquery_header(unsigned char *buf, int buflen, int *offset, struct dnsquery_header *header) -{ - unsigned char *p; - - netbuf_add_16bitnum(buf, buflen, offset, header->id); - - p = buf + *offset; - *p++ = ((header->qr & 0x01) << 7) - | ((header->opcode & 0x0F) << 3) - | ((header->aa & 0x01) << 2) - | ((header->tc & 0x01) << 1) - | ((header->rd & 0x01)); - *p++ = ((header->ra & 0x01) << 7) - | ((header->z & 0x07) << 4) - | ((header->rcode & 0x0F)); - *offset += 2; - - netbuf_add_16bitnum(buf, buflen, offset, header->qdcount); - netbuf_add_16bitnum(buf, buflen, offset, header->ancount); - netbuf_add_16bitnum(buf, buflen, offset, header->nscount); - netbuf_add_16bitnum(buf, buflen, offset, header->arcount); -} - -static void netbuf_add_dnsquery_question(unsigned char *buf, int buflen, int *offset, struct dnsquery_question *question) -{ - netbuf_add_domain_name(buf, buflen, offset, question->qname); - netbuf_add_16bitnum(buf, buflen, offset, question->qtype); - netbuf_add_16bitnum(buf, buflen, offset, question->qclass); -} -#endif /* _WIN32 */ - -int sock_srv_lookup(const char *service, const char *proto, - const char *domain, char *resulttarget, - int resulttargetlength, int *resultport) -{ - int set = 0; - char fulldomain[2048]; - - xmpp_snprintf(fulldomain, sizeof(fulldomain), - "_%s._%s.%s", service, proto, domain); - -#ifdef _WIN32 - /* try using dnsapi first */ - if (!set) - { - HINSTANCE hdnsapi = NULL; - - DNS_STATUS (WINAPI * pDnsQuery_A)(PCSTR, WORD, DWORD, PIP4_ARRAY, PDNS_RECORD*, PVOID*); - void (WINAPI * pDnsRecordListFree)(PDNS_RECORD, DNS_FREE_TYPE); - - if (hdnsapi = LoadLibrary("dnsapi.dll")) { - - pDnsQuery_A = (void *)GetProcAddress(hdnsapi, "DnsQuery_A"); - pDnsRecordListFree = (void *)GetProcAddress(hdnsapi, "DnsRecordListFree"); - - if (pDnsQuery_A && pDnsRecordListFree) { - PDNS_RECORD dnsrecords = NULL; - DNS_STATUS error; - - error = pDnsQuery_A(fulldomain, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &dnsrecords, NULL); - - if (error == 0) { - PDNS_RECORD current = dnsrecords; - - while (current) { - if (current->wType == DNS_TYPE_SRV) { - xmpp_snprintf(resulttarget, resulttargetlength, "%s", current->Data.Srv.pNameTarget); - *resultport = current->Data.Srv.wPort; - set = 1; - - current = NULL; - } else { - current = current->pNext; - } - } - } - - pDnsRecordListFree(dnsrecords, DnsFreeRecordList); - } - - FreeLibrary(hdnsapi); - } - } - - /* if dnsapi didn't work/isn't there, try querying the dns server manually */ - if (!set) - { - unsigned char buf[65536]; - struct dnsquery_header header; - struct dnsquery_question question; - int offset = 0; - int addrlen; - sock_t sock; - struct sockaddr_in dnsaddr; - char dnsserverips[16][256]; - int numdnsservers = 0; - int j; - - /* Try getting the DNS server ips from GetNetworkParams() in iphlpapi first */ - if (!numdnsservers) - { - HINSTANCE hiphlpapi = NULL; - DWORD (WINAPI * pGetNetworkParams)(PFIXED_INFO, PULONG); - - if (hiphlpapi = LoadLibrary("Iphlpapi.dll")) - { - pGetNetworkParams = (void *)GetProcAddress(hiphlpapi, "GetNetworkParams"); - - if (pGetNetworkParams) - { - FIXED_INFO *fi; - ULONG len; - DWORD error; - char buffer[65535]; - - len = 65535; - fi = buffer; - - if ((error = pGetNetworkParams(fi, &len)) == ERROR_SUCCESS) - { - IP_ADDR_STRING *pias = &(fi->DnsServerList); - - while (pias && numdnsservers < 16) - { - strcpy(dnsserverips[numdnsservers++], pias->IpAddress.String); - pias = pias->Next; - } - } - } - } - FreeLibrary(hiphlpapi); - } - - /* Next, try getting the DNS server ips from the registry */ - if (!numdnsservers) - { - HKEY search; - LONG error; - - error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", 0, KEY_READ, &search); - - if (error != ERROR_SUCCESS) - { - error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0, KEY_READ, &search); - } - - if (error == ERROR_SUCCESS) - { - char name[512]; - DWORD len = 512; - - error = RegQueryValueEx(search, "NameServer", NULL, NULL, (LPBYTE)name, &len); - - if (error != ERROR_SUCCESS) - { - error = RegQueryValueEx(search, "DhcpNameServer", NULL, NULL, (LPBYTE)name, &len); - } - - if (error == ERROR_SUCCESS) - { - char *parse = "0123456789.", *start, *end; - start = name; - end = name; - name[len] = '\0'; - - while (*start && numdnsservers < 16) - { - while (strchr(parse, *end)) - { - end++; - } - - strncpy(dnsserverips[numdnsservers++], start, end - start); - - while (*end && !strchr(parse, *end)) - { - end++; - } - - start = end; - } - } - } - - RegCloseKey(search); - } - - if (!numdnsservers) - { - HKEY searchlist; - LONG error; - - error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces", 0, KEY_READ, &searchlist); - - if (error == ERROR_SUCCESS) - { - unsigned int i; - DWORD numinterfaces = 0; - - RegQueryInfoKey(searchlist, NULL, NULL, NULL, &numinterfaces, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - - for (i = 0; i < numinterfaces; i++) - { - char name[512]; - DWORD len = 512; - HKEY searchentry; - - RegEnumKeyEx(searchlist, i, (LPTSTR)name, &len, NULL, NULL, NULL, NULL); - - if (RegOpenKeyEx(searchlist, name, 0, KEY_READ, &searchentry) == ERROR_SUCCESS) - { - if (RegQueryValueEx(searchentry, "DhcpNameServer", NULL, NULL, (LPBYTE)name, &len) == ERROR_SUCCESS) - { - char *parse = "0123456789.", *start, *end; - start = name; - end = name; - name[len] = '\0'; - - while (*start && numdnsservers < 16) - { - while (strchr(parse, *end)) - { - end++; - } - - strncpy(dnsserverips[numdnsservers++], start, end - start); - - while (*end && !strchr(parse, *end)) - { - end++; - } - - start = end; - } - } - else if (RegQueryValueEx(searchentry, "NameServer", NULL, NULL, (LPBYTE)name, &len) == ERROR_SUCCESS) - { - char *parse = "0123456789.", *start, *end; - start = name; - end = name; - name[len] = '\0'; - - while (*start && numdnsservers < 16) - { - while (strchr(parse, *end)) - { - end++; - } - - strncpy(dnsserverips[numdnsservers++], start, end - start); - - while (*end && !strchr(parse, *end)) - { - end++; - } - - start = end; - } - } - RegCloseKey(searchentry); - } - } - RegCloseKey(searchlist); - } - } - - /* If we have a DNS server, use it */ - if (numdnsservers) - { - ULONG nonblocking = 1; - int i; - int insize; - - memset(&header, 0, sizeof(header)); - header.id = 12345; /* FIXME: Get a better id here */ - header.rd = 1; - header.qdcount = 1; - - netbuf_add_dnsquery_header(buf, 65536, &offset, &header); - - memset(&question, 0, sizeof(question)); - strncpy(question.qname, fulldomain, 1024); - question.qtype = 33; /* SRV */ - question.qclass = 1; /* INTERNET! */ - - netbuf_add_dnsquery_question(buf, 65536, &offset, &question); - - insize = 0; - for (i = 0; i < numdnsservers && insize <= 0; i++) - { - sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - ioctlsocket(sock, FIONBIO, &nonblocking); - - memset(&dnsaddr, 0, sizeof(dnsaddr)); - - dnsaddr.sin_family = AF_INET; - dnsaddr.sin_port = htons(53); - dnsaddr.sin_addr.s_addr = inet_addr(dnsserverips[i]); - - addrlen = sizeof(dnsaddr); - sendto(sock, (char *)buf, offset, 0, (struct sockaddr *)&dnsaddr, addrlen); - for (j = 0; j < 50; j++) - { - insize = recvfrom(sock, (char *)buf, 65536, 0, (struct sockaddr *)&dnsaddr, &addrlen); - if (insize == SOCKET_ERROR) - { - if (sock_error() == WSAEWOULDBLOCK) - { - Sleep(100); - } - else - { - break; - } - } - else - { - break; - } - } - - closesocket(sock); - } - - offset = insize; - - if (offset > 0) - { - int len = offset; - int i; - struct dnsquery_header header; - struct dnsquery_question question; - struct dnsquery_resourcerecord rr; - - offset = 0; - netbuf_get_dnsquery_header(buf, 65536, &offset, &header); - - for (i = 0; i < header.qdcount; i++) - { - netbuf_get_dnsquery_question(buf, 65536, &offset, &question); - } - - for (i = 0; i < header.ancount; i++) - { - netbuf_get_dnsquery_resourcerecord(buf, 65536, &offset, &rr); - - if (rr.type == 33) - { - struct dnsquery_srvrdata *srvrdata = &(rr.rdata); - - xmpp_snprintf(resulttarget, resulttargetlength, "%s", srvrdata->target); - *resultport = srvrdata->port; - set = 1; - } - } - - for (i = 0; i < header.ancount; i++) - { - netbuf_get_dnsquery_resourcerecord(buf, 65536, &offset, &rr); - } - } - } - - } - -#else - if (!set) { - unsigned char buf[65535]; - unsigned short min; - int len; - - if ((len = res_query(fulldomain, C_IN, T_SRV, buf, 65535)) > 0) { - int offset; - int i; - struct dnsquery_header header; - struct dnsquery_question question; - struct dnsquery_resourcerecord rr; - - offset = 0; - netbuf_get_dnsquery_header(buf, 65536, &offset, &header); - - for (i = 0; i < header.qdcount; i++) { - netbuf_get_dnsquery_question(buf, 65536, &offset, &question); - } - - min = 65535; - for (i = 0; i < header.ancount; i++) { - netbuf_get_dnsquery_resourcerecord(buf, 65536, &offset, &rr); - - if (rr.type == 33) { - struct dnsquery_srvrdata *srvrdata = &(rr.rdata); - - if (srvrdata->priority < min || !set) { - xmpp_snprintf(resulttarget, resulttargetlength, "%s", - srvrdata->target); - *resultport = srvrdata->port; - set = 1; - min = srvrdata->priority; - } - } - } - } - } -#endif - - return set; -} diff --git a/src/sock.h b/src/sock.h index 7e27a21..753d60c 100644 --- a/src/sock.h +++ b/src/sock.h @@ -30,7 +30,7 @@ void sock_shutdown(void); int sock_error(void); -sock_t sock_connect(const char * const host, const unsigned int port); +sock_t sock_connect(const char * const host, const unsigned short port); int sock_close(const sock_t sock); int sock_set_blocking(const sock_t sock); @@ -41,8 +41,4 @@ int sock_is_recoverable(const int error); /* checks for an error after connect, return 0 if connect successful */ int sock_connect_error(const sock_t sock); -int sock_srv_lookup(const char *service, const char *proto, - const char *domain, char *resulttarget, - int resulttargetlength, int *resultport); - #endif /* __LIBSTROPHE_SOCK_H__ */ diff --git a/tests/res_query_dump.c b/tests/res_query_dump.c new file mode 100644 index 0000000..c9965fb --- /dev/null +++ b/tests/res_query_dump.c @@ -0,0 +1,56 @@ +/* Simple program to dump res_query(3) response. */ + +#include +#include +#include + +#include +#include +#include + +#define STEP 8 + +int main(int argc, char **argv) +{ + unsigned char buf[65536]; + char fulldomain[2048]; + char *service = "xmpp-client"; + char *proto = "tcp"; + char *domain = NULL; + int len; + int i; + int j; + + if (argc < 2) { + fprintf(stderr, "%s: argument missed\n", argc > 0 ? argv[0] : "$0"); + fprintf(stderr, "Usage: %s \n", argc > 0 ? argv[0] : "$0"); + return 1; + } + + domain = argv[1]; + snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", + service, proto, domain); + errno = 0; + len = res_query(fulldomain, C_IN, T_SRV, buf, sizeof(buf)); + + if (len < 0) { + fprintf(stderr, "res_query(): Error occured (errno=%d)\n", errno); + } + if (len == 0) { + fprintf(stderr, "res_query(): Empty result\n"); + } + if (len > 0) { + printf("/* res_query(\"%s\", C_IN, T_SRV, ...) */\n", fulldomain); + printf("static const unsigned char data[] = {\n"); + for (i = 0; i < len; i += STEP) { + printf(" "); + for (j = i; j < len && j < i + STEP; ++j) { + printf(" 0x%02x,", buf[j]); + } + printf("\n"); + } + printf("};\n"); + } + + return len <= 0; +} diff --git a/tests/test_resolver.c b/tests/test_resolver.c new file mode 100644 index 0000000..2062087 --- /dev/null +++ b/tests/test_resolver.c @@ -0,0 +1,139 @@ +/* test_resolver.c + * strophe XMPP client library -- tests for resolver + * + * Copyright (C) 2015 Dmitry Podgorny + * + * This software is provided AS-IS with no warranty, either express + * or implied. + * + * This program is dual licensed under the MIT and GPLv3 licenses. + */ + +/* gcc -o test_resolver -I. -I./src tests/test_resolver.c -static -lstrophe */ + +#include +#include + +#include "resolver.h" +#include "test.h" + +/* res_query("_xmpp-client._tcp.jabber.kiev.ua", C_IN, T_SRV, ...) */ +static const unsigned char data1[] = { + 0x95, 0xf3, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, + 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, + 0x61, 0x62, 0x62, 0x65, 0x72, 0x04, 0x6b, 0x69, + 0x65, 0x76, 0x02, 0x75, 0x61, 0x00, 0x00, 0x21, + 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x3b, 0x00, 0x16, 0x00, 0x01, + 0x00, 0x00, 0x14, 0x66, 0x06, 0x6a, 0x61, 0x62, + 0x62, 0x65, 0x72, 0x04, 0x6b, 0x69, 0x65, 0x76, + 0x02, 0x75, 0x61, 0x00, +}; +/* res_query("_xmpp-client._tcp.jabber.org", C_IN, T_SRV, ...) */ +static const unsigned char data2[] = { + 0xf2, 0x98, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, + 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, + 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, 0x6f, 0x72, + 0x67, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, + 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, + 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x1e, 0x14, 0x66, + 0x07, 0x68, 0x65, 0x72, 0x6d, 0x65, 0x73, 0x32, + 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, + 0x6f, 0x72, 0x67, 0x00, 0xc0, 0x0c, 0x00, 0x21, + 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1c, + 0x00, 0x1f, 0x00, 0x1e, 0x14, 0x66, 0x09, 0x68, + 0x65, 0x72, 0x6d, 0x65, 0x73, 0x32, 0x76, 0x36, + 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, + 0x6f, 0x72, 0x67, 0x00, +}; +/* res_query("_xmpp-client._tcp.gmail.com", C_IN, T_SRV, ...) */ +static const unsigned char data3[] = { + 0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, + 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67, + 0x6d, 0x61, 0x69, 0x6c, 0x03, 0x63, 0x6f, 0x6d, + 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00, + 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, + 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, + 0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70, + 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, + 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, + 0x66, 0x04, 0x61, 0x6c, 0x74, 0x34, 0x04, 0x78, + 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, + 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, + 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32, + 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, + 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x19, 0x00, + 0x05, 0x00, 0x00, 0x14, 0x66, 0x04, 0x78, 0x6d, + 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, + 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, + 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x33, 0x04, + 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, + 0x6d, 0x00, +}; + +static const struct { + const unsigned char *data; + size_t len; + char *target; + unsigned short port; +} tests[] = { + { + .data = data1, + .len = sizeof(data1), + .target = "jabber.kiev.ua", + .port = 5222, + }, + { + .data = data2, + .len = sizeof(data2), + .target = "hermes2.jabber.org", + .port = 5222, + }, + { + .data = data3, + .len = sizeof(data3), + .target = "xmpp.l.google.com", + .port = 5222, + }, +}; + +int main(int argc, char **argv) +{ + char domain[2048]; + unsigned short port; + size_t i; + int ret; + + printf("resolver_srv_lookup_buf() tests.\n"); + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + printf("Test #%zu: ", i + 1); + memset(domain, 'x', sizeof(domain)); + ret = resolver_srv_lookup_buf(tests[i].data, tests[i].len, + domain, sizeof(domain), &port); + assert(ret == 1); + COMPARE(tests[i].target, domain); + if (tests[i].port != port) { + printf("fail! got port=%u, but should be %u\n", + (unsigned)port, (unsigned)tests[i].port); + return 1; + } + printf("ok\n"); + } + + return 0; +}