From 8d4fd94fc8332b23f757443841a5c19c104a9bb8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 5 Dec 2004 17:33:51 +0000 Subject: move source files to src/ git-svn-id: file:///home/lennart/svn/public/nss-mdns/trunk@51 0ee8848e-81ea-0310-a63a-f631d1a40d77 --- Makefile | 28 --- dns.c | 263 ---------------------------- dns.h | 59 ------- main.c | 41 ----- nss.c | 298 -------------------------------- nsstest.c | 48 ------ query.c | 541 ---------------------------------------------------------- query.h | 32 ---- src/Makefile | 28 +++ src/dns.c | 263 ++++++++++++++++++++++++++++ src/dns.h | 59 +++++++ src/main.c | 41 +++++ src/nss.c | 298 ++++++++++++++++++++++++++++++++ src/nsstest.c | 48 ++++++ src/query.c | 541 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/query.h | 32 ++++ src/util.c | 185 ++++++++++++++++++++ src/util.h | 21 +++ util.c | 185 -------------------- util.h | 21 --- 20 files changed, 1516 insertions(+), 1516 deletions(-) delete mode 100644 Makefile delete mode 100644 dns.c delete mode 100644 dns.h delete mode 100644 main.c delete mode 100644 nss.c delete mode 100644 nsstest.c delete mode 100644 query.c delete mode 100644 query.h create mode 100644 src/Makefile create mode 100644 src/dns.c create mode 100644 src/dns.h create mode 100644 src/main.c create mode 100644 src/nss.c create mode 100644 src/nsstest.c create mode 100644 src/query.c create mode 100644 src/query.h create mode 100644 src/util.c create mode 100644 src/util.h delete mode 100644 util.c delete mode 100644 util.h diff --git a/Makefile b/Makefile deleted file mode 100644 index 4c1d3a0..0000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -CFLAGS=-Wall -fPIC -g -O0 -W -pipe '-DDEBUG_TRAP=__asm__("int $$3")' - -all: query nsstest libnss_mdns.so.2 libnss_mdns6.so.2 libnss_mdns4.so.2 - -query: query.o dns.o util.o main.o - $(CC) $(CFLAGS) -o $@ $^ $(LIBS) - -nsstest: nsstest.o - -libnss_mdns.so.2: query.o dns.o util.o nss.o - $(CC) -shared -o $@ -Wl,-soname,$@ $^ - -libnss_mdns4.so.2: query.o dns.o util.o nss4.o - $(CC) -shared -o $@ -Wl,-soname,$@ $^ - -libnss_mdns6.so.2: query.o dns.o util.o nss6.o - $(CC) -shared -o $@ -Wl,-soname,$@ $^ - -nss6.o: nss.c - $(CC) $(CFLAGS) -DNSS_IPV6_ONLY=1 -c -o $@ $< - -nss4.o: nss.c - $(CC) $(CFLAGS) -DNSS_IPV4_ONLY=1 -c -o $@ $< - -*.o: *.h - -clean: - rm -f *.o query *.so.2 nsstest diff --git a/dns.c b/dns.c deleted file mode 100644 index 642972d..0000000 --- a/dns.c +++ /dev/null @@ -1,263 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dns.h" - -struct dns_packet* dns_packet_new(void) { - struct dns_packet *p; - p = malloc(sizeof(struct dns_packet)); - assert(p); - p->size = p->rindex = 2*6; - memset(p->data, 0, p->size); - return p; -} - -void dns_packet_free(struct dns_packet *p) { - assert(p); - free(p); -} - -void dns_packet_set_field(struct dns_packet *p, unsigned index, uint16_t v) { - assert(p && index < 2*6); - - ((uint16_t*) p->data)[index] = htons(v); -} - -uint16_t dns_packet_get_field(struct dns_packet *p, unsigned index) { - assert(p && index < 2*6); - - return ntohs(((uint16_t*) p->data)[index]); -} - -uint8_t* dns_packet_append_name(struct dns_packet *p, const char *name) { - uint8_t *d, *f = NULL; - assert(p); - - for (;;) { - size_t n = strcspn(name, "."); - if (!n || n > 63) - return NULL; - - d = dns_packet_extend(p, n+1); - if (!f) - f = d; - d[0] = n; - memcpy(d+1, name, n); - - name += n; - - /* no trailing dot */ - if (!*name) - break; - - name ++; - - /* trailing dot */ - if (!*name) - break; - } - - d = dns_packet_extend(p, 1); - d[0] = 0; - - return f; -} - -uint8_t* dns_packet_append_uint16(struct dns_packet *p, uint16_t v) { - uint8_t *d; - assert(p); - d = dns_packet_extend(p, sizeof(uint16_t)); - *((uint16_t*) d) = htons(v); - return d; -} - -uint8_t *dns_packet_extend(struct dns_packet *p, size_t l) { - uint8_t *d; - assert(p); - - assert(p->size+l <= sizeof(p->data)); - - d = p->data + p->size; - p->size += l; - - return d; -} - -uint8_t *dns_packet_append_name_compressed(struct dns_packet *p, const char *name, uint8_t *prev) { - int16_t *d; - signed long k; - assert(p); - - if (!prev) - return dns_packet_append_name(p, name); - - k = prev - p->data; - if (k < 0 || k >= 0x4000 || (size_t) k >= p->size) - return dns_packet_append_name(p, name); - - d = (int16_t*) dns_packet_extend(p, sizeof(uint16_t)); - *d = htons((0xC000 | k)); - - return prev; -} - -int dns_packet_check_valid(struct dns_packet *p) { - assert(p); - uint16_t flags; - - if (p->size < 12) - return -1; - - flags = dns_packet_get_field(p, DNS_FIELD_FLAGS); - - if (flags & DNS_FLAG_OPCODE || flags & DNS_FLAG_RCODE) - return -1; - - return 0; -} - -int dns_packet_check_valid_response(struct dns_packet *p) { - uint16_t flags; - assert(p); - - if (dns_packet_check_valid(p) < 0) - return -1; - - flags = dns_packet_get_field(p, DNS_FIELD_FLAGS); - - if (!(flags & DNS_FLAG_QR)) - return -1; - - if (dns_packet_get_field(p, DNS_FIELD_QDCOUNT) > 0) - return -1; - - return 0; - -} - -static ssize_t consume_labels(struct dns_packet *p, size_t index, char *ret_name, size_t l) { - ssize_t ret = 0; - int compressed = 0; - int first_label = 1; - assert(p && ret_name && l); - - for (;;) { - uint8_t n; - - if (index+1 > p->size) - return -1; - - n = p->data[index]; - - if (!n) { - index++; - if (!compressed) - ret++; - - if (l < 1) - return -1; - *ret_name = 0; - - return ret; - - } else if (n <= 63) { - /* Uncompressed label */ - index++; - if (!compressed) - ret++; - - if (index + n > p->size) - return -1; - - if ((size_t) n + 1 > l) - return -1; - - if (!first_label) { - *(ret_name++) = '.'; - l--; - } else - first_label = 0; - - memcpy(ret_name, p->data + index, n); - index += n; - ret_name += n; - l -= n; - - if (!compressed) - ret += n; - } else if ((n & 0xC0) == 0xC0) { - /* Compressed label */ - - if (index+2 > p->size) - return -1; - - index = ((size_t) (p->data[index] & ~0xC0)) << 8 | p->data[index+1]; - - if (!compressed) - ret += 2; - - compressed = 1; - } else - return -1; - } -} - -int dns_packet_consume_name(struct dns_packet *p, char *ret_name, size_t l) { - ssize_t r; - - if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) - return -1; - - p->rindex += r; - return 0; -} - -int dns_packet_consume_uint16(struct dns_packet *p, uint16_t *ret_v) { - assert(p && ret_v); - - if (p->rindex + sizeof(uint16_t) > p->size) - return -1; - - *ret_v = ntohs(*((uint16_t*) (p->data + p->rindex))); - p->rindex += sizeof(uint16_t); - - return 0; -} - -int dns_packet_consume_uint32(struct dns_packet *p, uint32_t *ret_v) { - assert(p && ret_v); - - if (p->rindex + sizeof(uint32_t) > p->size) - return -1; - - *ret_v = ntohl(*((uint32_t*) (p->data + p->rindex))); - p->rindex += sizeof(uint32_t); - - return 0; -} - -int dns_packet_consume_bytes(struct dns_packet *p, void *ret_data, size_t l) { - assert(p && ret_data && l > 0); - - if (p->rindex + l > p->size) - return -1; - - memcpy(ret_data, p->data + p->rindex, l); - p->rindex += l; - - return 0; -} - -int dns_packet_consume_seek(struct dns_packet *p, size_t length) { - assert(p && length > 0); - - if (p->rindex + length > p->size) - return -1; - - p->rindex += length; - return 0; -} diff --git a/dns.h b/dns.h deleted file mode 100644 index 9ffb5ba..0000000 --- a/dns.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef foodnshfoo -#define foodnshfoo - -#include -#include - -struct dns_packet { - size_t size, rindex; - uint8_t data[9000]; -}; - -struct dns_packet* dns_packet_new(void); -void dns_packet_free(struct dns_packet *p); -void dns_packet_set_field(struct dns_packet *p, unsigned index, uint16_t v); -uint16_t dns_packet_get_field(struct dns_packet *p, unsigned index); - -uint8_t *dns_packet_append_uint16(struct dns_packet *p, uint16_t v); -uint8_t *dns_packet_append_name(struct dns_packet *p, const char *name); -uint8_t *dns_packet_append_name_compressed(struct dns_packet *p, const char *name, uint8_t *prev); -uint8_t *dns_packet_extend(struct dns_packet *p, size_t l); -int dns_packet_check_valid_response(struct dns_packet *p); -int dns_packet_check_valid(struct dns_packet *p); - -int dns_packet_consume_name(struct dns_packet *p, char *ret_name, size_t l); -int dns_packet_consume_uint16(struct dns_packet *p, uint16_t *ret_v); -int dns_packet_consume_uint32(struct dns_packet *p, uint32_t *ret_v); -int dns_packet_consume_bytes(struct dns_packet *p, void *ret_data, size_t l); -int dns_packet_consume_seek(struct dns_packet *p, size_t length); - -#define DNS_TYPE_A 0x01 -#define DNS_TYPE_AAAA 0x1C -#define DNS_TYPE_PTR 0x0C -#define DNS_CLASS_IN 0x01 - -#define DNS_FIELD_ID 0 -#define DNS_FIELD_FLAGS 1 -#define DNS_FIELD_QDCOUNT 2 -#define DNS_FIELD_ANCOUNT 3 -#define DNS_FIELD_NSCOUNT 4 -#define DNS_FIELD_ARCOUNT 5 - -#define DNS_FLAG_QR (1 << 15) -#define DNS_FLAG_OPCODE (15 << 11) -#define DNS_FLAG_RCODE (15) - -#define DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ - (((uint16_t) !!qr << 15) | \ - ((uint16_t) (opcode & 15) << 11) | \ - ((uint16_t) !!aa << 10) | \ - ((uint16_t) !!tc << 9) | \ - ((uint16_t) !!rd << 8) | \ - ((uint16_t) !!ra << 7) | \ - ((uint16_t) !!ad << 5) | \ - ((uint16_t) !!cd << 4) | \ - ((uint16_t) (rd & 15))) - - -#endif - diff --git a/main.c b/main.c deleted file mode 100644 index 5681204..0000000 --- a/main.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include - -#include "query.h" - -static void ipv4_func(const ipv4_address_t *ipv4, void *userdata) { - fprintf(stderr, "IPV4: %s\n", inet_ntoa(*(struct in_addr*) &ipv4->address)); -} - -static void ipv6_func(const ipv6_address_t *ipv6, void *userdata) { -} - -static void name_func(const char *name, void *userdata) { - fprintf(stderr, "NAME: %s\n", name); -} - -int main(int argc, char *argv[]) { - int ret = 1, fd = -1; - ipv4_address_t ipv4; - - if ((fd = mdns_open_socket()) < 0) - goto finish; - -/* if (mdns_query_name(fd, argc > 1 ? argv[1] : "ecstasy.local", &ipv4_func, &ipv6_func, NULL) < 0) */ -/* goto finish; */ - - ipv4.address = inet_addr(argc > 1 ? argv[1] : "192.168.100.1"); - - if (mdns_query_ipv4(fd, &ipv4, name_func, NULL) < 0) - goto finish; - - ret = 0; - -finish: - - if (fd >= 0) - close(fd); - - return ret; -} diff --git a/nss.c b/nss.c deleted file mode 100644 index 0515b2d..0000000 --- a/nss.c +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "query.h" - -#define MAX_ENTRIES 16 - -#ifdef NSS_IPV4_ONLY -#define _nss_mdns_gethostbyname2_r _nss_mdns4_gethostbyname2_r -#define _nss_mdns_gethostbyname_r _nss_mdns4_gethostbyname_r -#define _nss_mdns_gethostbyaddr_r _nss_mdns4_gethostbyaddr_r -#elif NSS_IPV6_ONLY -#define _nss_mdns_gethostbyname2_r _nss_mdns6_gethostbyname2_r -#define _nss_mdns_gethostbyname_r _nss_mdns6_gethostbyname_r -#define _nss_mdns_gethostbyaddr_r _nss_mdns6_gethostbyaddr_r -#endif - -struct userdata { - int count; - int data_len; /* only valid when doing reverse lookup */ - union { - ipv4_address_t ipv4[MAX_ENTRIES]; - ipv6_address_t ipv6[MAX_ENTRIES]; - char *name[MAX_ENTRIES]; - } data; -}; - -static void ipv4_callback(const ipv4_address_t *ipv4, void *userdata) { - struct userdata *u = userdata; - assert(ipv4 && userdata); - - if (u->count >= MAX_ENTRIES) - return; - - u->data.ipv4[u->count++] = *ipv4; - u->data_len += sizeof(ipv4_address_t); -} - -static void ipv6_callback(const ipv6_address_t *ipv6, void *userdata) { - struct userdata *u = userdata; - assert(ipv6 && userdata); - - if (u->count >= MAX_ENTRIES) - return; - - u->data.ipv6[u->count++] = *ipv6; - u->data_len += sizeof(ipv6_address_t); -} - -static void name_callback(const char*name, void *userdata) { - struct userdata *u = userdata; - assert(name && userdata); - - if (u->count >= MAX_ENTRIES) - return; - - u->data.name[u->count++] = strdup(name); - u->data_len += strlen(name)+1; -} - -enum nss_status _nss_mdns_gethostbyname2_r( - const char *name, - int af, - struct hostent * result, - char *buffer, - size_t buflen, - int *errnop, - int *h_errnop) { - - struct userdata u; - enum nss_status status = NSS_STATUS_UNAVAIL; - int fd = -1, r, i; - size_t address_length, l, index, astart; - -/* DEBUG_TRAP; */ - -#ifdef NSS_IPV4_ONLY - if (af != AF_INET) -#elif NSS_IPV6_ONLY - if (af != AF_INET6) -#else - if (af != AF_INET && af != AF_INET6) -#endif - { - *errnop = EINVAL; - *h_errnop = NO_RECOVERY; - goto finish; - } - - address_length = af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t); - if (buflen < - sizeof(char*)+ /* alias names */ - strlen(name)+1) { /* official name */ - - *errnop = ERANGE; - *h_errnop = NO_RECOVERY; - status = NSS_STATUS_TRYAGAIN; - - goto finish; - } - - if ((fd = mdns_open_socket()) < 0) { - - *errnop = errno; - *h_errnop = NO_RECOVERY; - goto finish; - } - - u.count = 0; - u.data_len = 0; - - if ((r = mdns_query_name(fd, name, af == AF_INET ? ipv4_callback : NULL, af == AF_INET6 ? ipv6_callback : NULL, &u)) < 0) { - *errnop = ETIMEDOUT; - *h_errnop = HOST_NOT_FOUND; - goto finish; - } - - /* Alias names */ - *((char**) buffer) = NULL; - result->h_aliases = (char**) buffer; - index = sizeof(char*); - - /* Official name */ - strcpy(buffer+index, name); - result->h_name = buffer+index; - index += strlen(name)+1; - - result->h_addrtype = af; - result->h_length = address_length; - - /* Check if there's enough space for the addresses */ - if (buflen < index+u.data_len+sizeof(char*)*(u.count+1)) { - *errnop = ERANGE; - *h_errnop = NO_RECOVERY; - status = NSS_STATUS_TRYAGAIN; - goto finish; - } - - /* Addresses */ - astart = index; - l = u.count*address_length; - memcpy(buffer+astart, &u.data, l); - index += l; - - /* Address array */ - for (i = 0; i < u.count; i++) - ((char**) (buffer+index))[i] = buffer+astart+address_length*i; - ((char**) (buffer+index))[i] = NULL; - - result->h_addr_list = (char**) (buffer+index); - - status = NSS_STATUS_SUCCESS; - -finish: - if (fd >= 0) - close(fd); - - return status; -} - -enum nss_status _nss_mdns_gethostbyname_r ( - const char *name, - struct hostent *result, - char *buffer, - size_t buflen, - int *errnop, - int *h_errnop) { - - return _nss_mdns_gethostbyname2_r( - name, -#ifdef NSS_IPV6_ONLY - AF_INET6, -#else - AF_INET, -#endif - result, - buffer, - buflen, - errnop, - h_errnop); -} - -enum nss_status _nss_mdns_gethostbyaddr_r( - const void* addr, - int len, - int af, - struct hostent *result, - char *buffer, - size_t buflen, - int *errnop, - int *h_errnop) { - - *errnop = EINVAL; - *h_errnop = NO_RECOVERY; - - struct userdata u; - enum nss_status status = NSS_STATUS_UNAVAIL; - int fd = -1, r; - size_t address_length, index, astart; - - u.count = 0; - u.data_len = 0; - - address_length = af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t); - - if (len != (int) address_length || -#ifdef NSS_IPV4_ONLY - af != AF_INET -#elif NSS_IPV6_ONLY - af != AF_INET6 -#else - (af != AF_INET && af != AF_INET6) -#endif - ) { - *errnop = EINVAL; - *h_errnop = NO_RECOVERY; - goto finish; - } - - if (buflen < - sizeof(char*)+ /* alias names */ - address_length) { /* address */ - - *errnop = ERANGE; - *h_errnop = NO_RECOVERY; - status = NSS_STATUS_TRYAGAIN; - - goto finish; - } - - if ((fd = mdns_open_socket()) < 0) { - - *errnop = errno; - *h_errnop = NO_RECOVERY; - goto finish; - } - - if (af == AF_INET) - r = mdns_query_ipv4(fd, (ipv4_address_t*) addr, name_callback, &u); - else - r = mdns_query_ipv6(fd, (ipv6_address_t*) addr, name_callback, &u); - - if (r < 0) { - *errnop = ETIMEDOUT; - *h_errnop = HOST_NOT_FOUND; - goto finish; - } - - /* Alias names */ - *((char**) buffer) = NULL; - result->h_aliases = (char**) buffer; - index = sizeof(char*); - - assert(u.count > 0 && u.data.name[0]); - if (buflen < - strlen(u.data.name[0])+1+ /* official names */ - sizeof(char*)+ /* alias names */ - address_length+ /* address */ - sizeof(void*)*2) { /* address list */ - - *errnop = ERANGE; - *h_errnop = NO_RECOVERY; - status = NSS_STATUS_TRYAGAIN; - goto finish; - } - - /* Official name */ - strcpy(buffer+index, u.data.name[0]); - result->h_name = buffer+index; - index += strlen(u.data.name[0])+1; - - result->h_addrtype = af; - result->h_length = address_length; - - /* Address */ - astart = index; - memcpy(buffer+astart, addr, address_length); - index += address_length; - - /* Address array */ - ((char**) (buffer+index))[0] = buffer+astart; - ((char**) (buffer+index))[1] = NULL; - result->h_addr_list = (char**) (buffer+index); - - status = NSS_STATUS_SUCCESS; - -finish: - if (fd >= 0) - close(fd); - - return status; -} - diff --git a/nsstest.c b/nsstest.c deleted file mode 100644 index d4f1c4c..0000000 --- a/nsstest.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) { - struct hostent *he; - in_addr_t **a; - char *arg= argc > 1 ? argv[1] : "whiskey.local"; - uint8_t t[256]; - - if (inet_pton(AF_INET, arg, &t) > 0) - he = gethostbyaddr(t, 4, AF_INET); - else if (inet_pton(AF_INET6, arg, &t) > 0) - he = gethostbyaddr(t, 16, AF_INET6); - else - he = gethostbyname(arg); - - if (!he) { - fprintf(stderr, "lookup failed\n"); - return 1; - } - - fprintf(stderr, "official name: %s\n", he->h_name); - - if (!he->h_aliases || !he->h_aliases[0]) - fprintf(stderr, "no aliases\n"); - else { - char **h; - fprintf(stderr, "aliases:"); - for (h = he->h_aliases; *h; h++) - fprintf(stderr, " %s", *h); - fprintf(stderr, "\n"); - } - - fprintf(stderr, "addr type: %s\n", he->h_addrtype == AF_INET ? "inet" : (he->h_addrtype == AF_INET6 ? "inet6" : NULL)); - fprintf(stderr, "addr length: %i\n", he->h_length); - - fprintf(stderr, "addresses:"); - for (a = (in_addr_t**) he->h_addr_list; *a; a++) { - char t[256]; - fprintf(stderr, " %s", inet_ntop(he->h_addrtype, *a, t, sizeof(t))); - } - fprintf(stderr, "\n"); - - return 0; -} diff --git a/query.c b/query.c deleted file mode 100644 index 5673c3e..0000000 --- a/query.c +++ /dev/null @@ -1,541 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dns.h" -#include "util.h" -#include "query.h" - -static const usec_t retry_ms[] = { 200000, 500000, 900000, 1400000, 0 }; - -static void mdns_mcast_group(struct sockaddr_in *ret_sa) { - assert(ret_sa); - - ret_sa->sin_family = AF_INET; - ret_sa->sin_port = htons(5353); - ret_sa->sin_addr.s_addr = inet_addr("224.0.0.251"); -} - -int mdns_open_socket(void) { - struct ip_mreqn mreq; - struct sockaddr_in sa; - int fd = -1, ttl, yes; - - mdns_mcast_group(&sa); - - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - fprintf(stderr, "socket() failed: %s\n", strerror(errno)); - goto fail; - } - - ttl = 255; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { - fprintf(stderr, "IP_MULTICAST_TTL failed: %s\n", strerror(errno)); - goto fail; - } - - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { - fprintf(stderr, "SO_REUSEADDR failed: %s\n", strerror(errno)); - goto fail; - } - - if (bind(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { - fprintf(stderr, "bind() failed: %s\n", strerror(errno)); - goto fail; - } - - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr = sa.sin_addr; - mreq.imr_address.s_addr = htonl(INADDR_ANY); - mreq.imr_ifindex = 0; - - if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - fprintf(stderr, "IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno)); - goto fail; - } - - if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { - fprintf(stderr, "O_RECVTTL failed: %s\n", strerror(errno)); - goto fail; - } - - if (set_cloexec(fd) < 0) { - fprintf(stderr, "FD_CLOEXEC failed: %s\n", strerror(errno)); - goto fail; - } - - if (set_nonblock(fd) < 0) { - fprintf(stderr, "O_ONONBLOCK failed: %s\n", strerror(errno)); - goto fail; - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - - return -1; -} - -static int send_dns_packet(int fd, struct dns_packet *p) { - struct sockaddr_in sa; - assert(fd >= 0 && p); - - assert(dns_packet_check_valid(p) >= 0); - - mdns_mcast_group(&sa); - - for (;;) { - if (sendto(fd, p->data, p->size, 0, (struct sockaddr*) &sa, sizeof(sa)) >= 0) - return 0; - - if (errno != EAGAIN) { - fprintf(stderr, "sendto() failed: %s\n", strerror(errno)); - return -1; - } - - if (wait_for_write(fd, NULL) < 0) - return -1; - } -} - -static int recv_dns_packet(int fd, struct dns_packet **ret_packet, uint8_t* ret_ttl, struct timeval *end) { - struct dns_packet *p= NULL; - struct msghdr msg; - struct iovec io; - int ret = -1; - uint8_t aux[16]; - assert(fd >= 0); - - p = dns_packet_new(); - - io.iov_base = p->data; - io.iov_len = sizeof(p->data); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_control = aux; - msg.msg_controllen = sizeof(aux); - msg.msg_flags = 0; - - for (;;) { - ssize_t l; - int r; - - if ((l = recvmsg(fd, &msg, 0)) >= 0) { - struct cmsghdr *cmsg; - *ret_ttl = 0; - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) { - if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) { - *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg); - break; - } - } - - if (cmsg == NULL) { - fprintf(stderr, "Didn't recieve TTL\n"); - goto fail; - } - - p->size = (size_t) l; - - *ret_packet = p; - return 0; - } - - if (errno != EAGAIN) { - fprintf(stderr, "recvfrom() failed: %s\n", strerror(errno)); - goto fail; - } - - if ((r = wait_for_read(fd, end)) < 0) - goto fail; - else if (r > 0) { /* timeout */ - ret = 1; - goto fail; - } - } - -fail: - if (p) - dns_packet_free(p); - - return ret; -} - -static int send_name_query(int fd, const char *name, int query_ipv4, int query_ipv6) { - int ret = -1; - struct dns_packet *p = NULL; - uint8_t *prev_name = NULL; - int qdcount = 0; - - assert(fd >= 0 && name && (query_ipv4 || query_ipv6)); - - if (!(p = dns_packet_new())) { - fprintf(stderr, "Failed to allocate DNS packet.\n"); - goto finish; - } - - dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); - - if (query_ipv4) { - if (!(prev_name = dns_packet_append_name(p, name))) { - fprintf(stderr, "Bad host name\n"); - goto finish; - - } - dns_packet_append_uint16(p, DNS_TYPE_A); - dns_packet_append_uint16(p, DNS_CLASS_IN); - qdcount++; - } - - if (query_ipv6) { - if (!dns_packet_append_name_compressed(p, name, prev_name)) { - fprintf(stderr, "Bad host name\n"); - goto finish; - } - - dns_packet_append_uint16(p, DNS_TYPE_AAAA); - dns_packet_append_uint16(p, DNS_CLASS_IN); - qdcount++; - } - - dns_packet_set_field(p, DNS_FIELD_QDCOUNT, qdcount); - - if (send_dns_packet(fd, p) < 0) - goto finish; - - ret = 0; - -finish: - if (p) - dns_packet_free(p); - - return ret; -} - -static int domain_cmp(const char *a, const char *b) { - size_t al, bl; - - al = strlen(a); - bl = strlen(b); - - if (al > 0 && a[al-1] == '.') - al --; - - if (bl > 0 && b[bl-1] == '.') - bl --; - - if (al != bl) - return al > bl ? 1 : (al < bl ? -1 : 0); - - return strncasecmp(a, b, al); -} - -static int process_name_response(int fd, const char *name, usec_t timeout, void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata), void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata), void *userdata) { - assert(fd >= 0 && name && (ipv4_func || ipv6_func)); - struct dns_packet *p = NULL; - int done = 0; - struct timeval end; - - gettimeofday(&end, NULL); - timeval_add(&end, timeout); - - while (!done) { - uint8_t ttl; - int r; - - if ((r = recv_dns_packet(fd, &p, &ttl, &end)) < 0) - return -1; - else if (r > 0) /* timeout */ - return 1; - - /* Ignore packets with RFC != 255 */ - if (ttl == 255) { - - /* Ignore corrupt packets */ - if (dns_packet_check_valid_response(p) >= 0) { - - for (;;) { - char pname[256]; - uint16_t type, class; - uint32_t rr_ttl; - uint16_t rdlength; - - if (dns_packet_consume_name(p, pname, sizeof(pname)) < 0 || - dns_packet_consume_uint16(p, &type) < 0 || - dns_packet_consume_uint16(p, &class) < 0 || - dns_packet_consume_uint32(p, &rr_ttl) < 0 || - dns_packet_consume_uint16(p, &rdlength) < 0) { - break; - } - - /* Remove mDNS cache flush bit */ - class &= ~0x8000; - - if (ipv4_func && - type == DNS_TYPE_A && - class == DNS_CLASS_IN && - !domain_cmp(name, pname) && - rdlength == sizeof(ipv4_address_t)) { - - ipv4_address_t ipv4; - - if (dns_packet_consume_bytes(p, &ipv4, sizeof(ipv4)) < 0) - break; - - ipv4_func(&ipv4, userdata); - done = 1; - - } else if (ipv6_func && - type == DNS_TYPE_AAAA && - class == DNS_CLASS_IN && - !domain_cmp(name, pname) && - rdlength == sizeof(ipv6_address_t)) { - - ipv6_address_t ipv6; - - if (dns_packet_consume_bytes(p, &ipv6, sizeof(ipv6_address_t)) < 0) - break; - - ipv6_func(&ipv6, userdata); - done = 1; - } else { - - /* Step over */ - - if (dns_packet_consume_seek(p, rdlength) < 0) - break; - } - } - } - } - - if (p) - dns_packet_free(p); - } - - return 0; -} - -int mdns_query_name(int fd, const char *name, void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata), void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata), void *userdata) { - const usec_t *timeout = retry_ms; - assert(fd >= 0 && name && (ipv4_func || ipv6_func)); - - while (*timeout > 0) { - int n; - - if (send_name_query(fd, name, !!ipv4_func, !!ipv6_func) < 0) - return -1; - - if ((n = process_name_response(fd, name, *timeout, ipv4_func, ipv6_func, userdata)) < 0) - return -1; - - if (n == 0) - return 0; - - /* Timeout */ - - timeout++; - } - - return -1; -} - -static int send_reverse_query(int fd, const char *name) { - int ret = -1; - struct dns_packet *p = NULL; - - assert(fd >= 0 && name); - - if (!(p = dns_packet_new())) { - fprintf(stderr, "Failed to allocate DNS packet.\n"); - goto finish; - } - - dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); - - if (!dns_packet_append_name(p, name)) { - fprintf(stderr, "Bad host name\n"); - goto finish; - } - - dns_packet_append_uint16(p, DNS_TYPE_PTR); - dns_packet_append_uint16(p, DNS_CLASS_IN); - - dns_packet_set_field(p, DNS_FIELD_QDCOUNT, 1); - - if (send_dns_packet(fd, p) < 0) - goto finish; - - ret = 0; - -finish: - if (p) - dns_packet_free(p); - - return ret; -} - -static int process_reverse_response(int fd, const char *name, usec_t timeout, void (*name_func)(const char *name, void *userdata), void *userdata) { - assert(fd >= 0 && name && name_func); - struct dns_packet *p = NULL; - int done = 0; - struct timeval end; - - gettimeofday(&end, NULL); - timeval_add(&end, timeout); - - while (!done) { - uint8_t ttl; - int r; - - if ((r = recv_dns_packet(fd, &p, &ttl, &end)) < 0) - return -1; - else if (r > 0) /* timeout */ - return 1; - - /* Ignore packets with RFC != 255 */ - if (ttl == 255) { - - /* Ignore corrupt packets */ - if (dns_packet_check_valid_response(p) >= 0) { - - for (;;) { - char pname[256]; - uint16_t type, class; - uint32_t rr_ttl; - uint16_t rdlength; - - if (dns_packet_consume_name(p, pname, sizeof(pname)) < 0 || - dns_packet_consume_uint16(p, &type) < 0 || - dns_packet_consume_uint16(p, &class) < 0 || - dns_packet_consume_uint32(p, &rr_ttl) < 0 || - dns_packet_consume_uint16(p, &rdlength) < 0) { - break; - } - - /* Remove mDNS cache flush bit */ - class &= ~0x8000; - - if (type == DNS_TYPE_PTR && - class == DNS_CLASS_IN && - !domain_cmp(name, pname)) { - - char rname[256]; - - if (dns_packet_consume_name(p, rname, sizeof(rname)) < 0) - break; - - name_func(rname, userdata); - done = 1; - - } else { - - /* Step over */ - - if (dns_packet_consume_seek(p, rdlength) < 0) - break; - } - } - } - } - - if (p) - dns_packet_free(p); - } - - return 0; -} - -static int query_reverse(int fd, const char *name, void (*name_func)(const char *name, void *userdata), void *userdata) { - const usec_t *timeout = retry_ms; - assert(fd >= 0 && name && name_func); - - while (*timeout > 0) { - int n; - - if (send_reverse_query(fd, name) < 0) - return -1; - - if ((n = process_reverse_response(fd, name, *timeout, name_func, userdata)) < 0) - return -1; - - if (n == 0) - return 0; - - /* Timeout */ - - timeout++; - } - - return -1; -} - - -int mdns_query_ipv4(int fd, const ipv4_address_t *ipv4, void (*name_func)(const char *name, void *userdata), void *userdata) { - char name[256]; - uint32_t a; - assert(fd >= 0 && ipv4 && name_func); - - a = ntohl(ipv4->address); - snprintf(name, sizeof(name), "%u.%u.%u.%u.in-addr.arpa", a & 0xFF, (a >> 8) & 0xFF, (a >> 16) & 0xFF, a >> 24); - - return query_reverse(fd, name, name_func, userdata); -} - -int mdns_query_ipv6(int fd, const ipv6_address_t *ipv6, void (*name_func)(const char *name, void *userdata), void *userdata) { - char name[256]; - assert(fd >= 0 && ipv6 && name_func); - - snprintf(name, sizeof(name), "%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.int", - ipv6->address[15] & 0xF, - ipv6->address[15] >> 4, - ipv6->address[14] & 0xF, - ipv6->address[14] >> 4, - ipv6->address[13] & 0xF, - ipv6->address[13] >> 4, - ipv6->address[12] & 0xF, - ipv6->address[12] >> 4, - ipv6->address[11] & 0xF, - ipv6->address[11] >> 4, - ipv6->address[10] & 0xF, - ipv6->address[10] >> 4, - ipv6->address[9] & 0xF, - ipv6->address[9] >> 4, - ipv6->address[8] & 0xF, - ipv6->address[8] >> 4, - ipv6->address[7] & 0xF, - ipv6->address[7] >> 4, - ipv6->address[6] & 0xF, - ipv6->address[6] >> 4, - ipv6->address[5] & 0xF, - ipv6->address[5] >> 4, - ipv6->address[4] & 0xF, - ipv6->address[4] >> 4, - ipv6->address[3] & 0xF, - ipv6->address[3] >> 4, - ipv6->address[2] & 0xF, - ipv6->address[2] >> 4, - ipv6->address[1] & 0xF, - ipv6->address[1] >> 4, - ipv6->address[0] & 0xF, - ipv6->address[0] >> 4); - - return query_reverse(fd, name, name_func, userdata); -} - diff --git a/query.h b/query.h deleted file mode 100644 index 0c22c49..0000000 --- a/query.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef fooqueryhfoo -#define fooqueryhfoo - -#include - -typedef struct { - uint32_t address; -} ipv4_address_t; - -typedef struct { - uint8_t address[16]; -} ipv6_address_t; - -int mdns_open_socket(void); - -int mdns_query_name(int fd, - const char *name, - void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata), - void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata), - void *userdata); - -int mdns_query_ipv4(int fd, - const ipv4_address_t *ipv4, - void (*name_func)(const char *name, void *userdata), - void *userdata); - -int mdns_query_ipv6(int fd, - const ipv6_address_t *ipv6, - void (*name_func)(const char *name, void *userdata), - void *userdata); - -#endif diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..4c1d3a0 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,28 @@ +CFLAGS=-Wall -fPIC -g -O0 -W -pipe '-DDEBUG_TRAP=__asm__("int $$3")' + +all: query nsstest libnss_mdns.so.2 libnss_mdns6.so.2 libnss_mdns4.so.2 + +query: query.o dns.o util.o main.o + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + +nsstest: nsstest.o + +libnss_mdns.so.2: query.o dns.o util.o nss.o + $(CC) -shared -o $@ -Wl,-soname,$@ $^ + +libnss_mdns4.so.2: query.o dns.o util.o nss4.o + $(CC) -shared -o $@ -Wl,-soname,$@ $^ + +libnss_mdns6.so.2: query.o dns.o util.o nss6.o + $(CC) -shared -o $@ -Wl,-soname,$@ $^ + +nss6.o: nss.c + $(CC) $(CFLAGS) -DNSS_IPV6_ONLY=1 -c -o $@ $< + +nss4.o: nss.c + $(CC) $(CFLAGS) -DNSS_IPV4_ONLY=1 -c -o $@ $< + +*.o: *.h + +clean: + rm -f *.o query *.so.2 nsstest diff --git a/src/dns.c b/src/dns.c new file mode 100644 index 0000000..642972d --- /dev/null +++ b/src/dns.c @@ -0,0 +1,263 @@ +#include +#include +#include +#include +#include +#include + +#include "dns.h" + +struct dns_packet* dns_packet_new(void) { + struct dns_packet *p; + p = malloc(sizeof(struct dns_packet)); + assert(p); + p->size = p->rindex = 2*6; + memset(p->data, 0, p->size); + return p; +} + +void dns_packet_free(struct dns_packet *p) { + assert(p); + free(p); +} + +void dns_packet_set_field(struct dns_packet *p, unsigned index, uint16_t v) { + assert(p && index < 2*6); + + ((uint16_t*) p->data)[index] = htons(v); +} + +uint16_t dns_packet_get_field(struct dns_packet *p, unsigned index) { + assert(p && index < 2*6); + + return ntohs(((uint16_t*) p->data)[index]); +} + +uint8_t* dns_packet_append_name(struct dns_packet *p, const char *name) { + uint8_t *d, *f = NULL; + assert(p); + + for (;;) { + size_t n = strcspn(name, "."); + if (!n || n > 63) + return NULL; + + d = dns_packet_extend(p, n+1); + if (!f) + f = d; + d[0] = n; + memcpy(d+1, name, n); + + name += n; + + /* no trailing dot */ + if (!*name) + break; + + name ++; + + /* trailing dot */ + if (!*name) + break; + } + + d = dns_packet_extend(p, 1); + d[0] = 0; + + return f; +} + +uint8_t* dns_packet_append_uint16(struct dns_packet *p, uint16_t v) { + uint8_t *d; + assert(p); + d = dns_packet_extend(p, sizeof(uint16_t)); + *((uint16_t*) d) = htons(v); + return d; +} + +uint8_t *dns_packet_extend(struct dns_packet *p, size_t l) { + uint8_t *d; + assert(p); + + assert(p->size+l <= sizeof(p->data)); + + d = p->data + p->size; + p->size += l; + + return d; +} + +uint8_t *dns_packet_append_name_compressed(struct dns_packet *p, const char *name, uint8_t *prev) { + int16_t *d; + signed long k; + assert(p); + + if (!prev) + return dns_packet_append_name(p, name); + + k = prev - p->data; + if (k < 0 || k >= 0x4000 || (size_t) k >= p->size) + return dns_packet_append_name(p, name); + + d = (int16_t*) dns_packet_extend(p, sizeof(uint16_t)); + *d = htons((0xC000 | k)); + + return prev; +} + +int dns_packet_check_valid(struct dns_packet *p) { + assert(p); + uint16_t flags; + + if (p->size < 12) + return -1; + + flags = dns_packet_get_field(p, DNS_FIELD_FLAGS); + + if (flags & DNS_FLAG_OPCODE || flags & DNS_FLAG_RCODE) + return -1; + + return 0; +} + +int dns_packet_check_valid_response(struct dns_packet *p) { + uint16_t flags; + assert(p); + + if (dns_packet_check_valid(p) < 0) + return -1; + + flags = dns_packet_get_field(p, DNS_FIELD_FLAGS); + + if (!(flags & DNS_FLAG_QR)) + return -1; + + if (dns_packet_get_field(p, DNS_FIELD_QDCOUNT) > 0) + return -1; + + return 0; + +} + +static ssize_t consume_labels(struct dns_packet *p, size_t index, char *ret_name, size_t l) { + ssize_t ret = 0; + int compressed = 0; + int first_label = 1; + assert(p && ret_name && l); + + for (;;) { + uint8_t n; + + if (index+1 > p->size) + return -1; + + n = p->data[index]; + + if (!n) { + index++; + if (!compressed) + ret++; + + if (l < 1) + return -1; + *ret_name = 0; + + return ret; + + } else if (n <= 63) { + /* Uncompressed label */ + index++; + if (!compressed) + ret++; + + if (index + n > p->size) + return -1; + + if ((size_t) n + 1 > l) + return -1; + + if (!first_label) { + *(ret_name++) = '.'; + l--; + } else + first_label = 0; + + memcpy(ret_name, p->data + index, n); + index += n; + ret_name += n; + l -= n; + + if (!compressed) + ret += n; + } else if ((n & 0xC0) == 0xC0) { + /* Compressed label */ + + if (index+2 > p->size) + return -1; + + index = ((size_t) (p->data[index] & ~0xC0)) << 8 | p->data[index+1]; + + if (!compressed) + ret += 2; + + compressed = 1; + } else + return -1; + } +} + +int dns_packet_consume_name(struct dns_packet *p, char *ret_name, size_t l) { + ssize_t r; + + if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) + return -1; + + p->rindex += r; + return 0; +} + +int dns_packet_consume_uint16(struct dns_packet *p, uint16_t *ret_v) { + assert(p && ret_v); + + if (p->rindex + sizeof(uint16_t) > p->size) + return -1; + + *ret_v = ntohs(*((uint16_t*) (p->data + p->rindex))); + p->rindex += sizeof(uint16_t); + + return 0; +} + +int dns_packet_consume_uint32(struct dns_packet *p, uint32_t *ret_v) { + assert(p && ret_v); + + if (p->rindex + sizeof(uint32_t) > p->size) + return -1; + + *ret_v = ntohl(*((uint32_t*) (p->data + p->rindex))); + p->rindex += sizeof(uint32_t); + + return 0; +} + +int dns_packet_consume_bytes(struct dns_packet *p, void *ret_data, size_t l) { + assert(p && ret_data && l > 0); + + if (p->rindex + l > p->size) + return -1; + + memcpy(ret_data, p->data + p->rindex, l); + p->rindex += l; + + return 0; +} + +int dns_packet_consume_seek(struct dns_packet *p, size_t length) { + assert(p && length > 0); + + if (p->rindex + length > p->size) + return -1; + + p->rindex += length; + return 0; +} diff --git a/src/dns.h b/src/dns.h new file mode 100644 index 0000000..9ffb5ba --- /dev/null +++ b/src/dns.h @@ -0,0 +1,59 @@ +#ifndef foodnshfoo +#define foodnshfoo + +#include +#include + +struct dns_packet { + size_t size, rindex; + uint8_t data[9000]; +}; + +struct dns_packet* dns_packet_new(void); +void dns_packet_free(struct dns_packet *p); +void dns_packet_set_field(struct dns_packet *p, unsigned index, uint16_t v); +uint16_t dns_packet_get_field(struct dns_packet *p, unsigned index); + +uint8_t *dns_packet_append_uint16(struct dns_packet *p, uint16_t v); +uint8_t *dns_packet_append_name(struct dns_packet *p, const char *name); +uint8_t *dns_packet_append_name_compressed(struct dns_packet *p, const char *name, uint8_t *prev); +uint8_t *dns_packet_extend(struct dns_packet *p, size_t l); +int dns_packet_check_valid_response(struct dns_packet *p); +int dns_packet_check_valid(struct dns_packet *p); + +int dns_packet_consume_name(struct dns_packet *p, char *ret_name, size_t l); +int dns_packet_consume_uint16(struct dns_packet *p, uint16_t *ret_v); +int dns_packet_consume_uint32(struct dns_packet *p, uint32_t *ret_v); +int dns_packet_consume_bytes(struct dns_packet *p, void *ret_data, size_t l); +int dns_packet_consume_seek(struct dns_packet *p, size_t length); + +#define DNS_TYPE_A 0x01 +#define DNS_TYPE_AAAA 0x1C +#define DNS_TYPE_PTR 0x0C +#define DNS_CLASS_IN 0x01 + +#define DNS_FIELD_ID 0 +#define DNS_FIELD_FLAGS 1 +#define DNS_FIELD_QDCOUNT 2 +#define DNS_FIELD_ANCOUNT 3 +#define DNS_FIELD_NSCOUNT 4 +#define DNS_FIELD_ARCOUNT 5 + +#define DNS_FLAG_QR (1 << 15) +#define DNS_FLAG_OPCODE (15 << 11) +#define DNS_FLAG_RCODE (15) + +#define DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ + (((uint16_t) !!qr << 15) | \ + ((uint16_t) (opcode & 15) << 11) | \ + ((uint16_t) !!aa << 10) | \ + ((uint16_t) !!tc << 9) | \ + ((uint16_t) !!rd << 8) | \ + ((uint16_t) !!ra << 7) | \ + ((uint16_t) !!ad << 5) | \ + ((uint16_t) !!cd << 4) | \ + ((uint16_t) (rd & 15))) + + +#endif + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..5681204 --- /dev/null +++ b/src/main.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "query.h" + +static void ipv4_func(const ipv4_address_t *ipv4, void *userdata) { + fprintf(stderr, "IPV4: %s\n", inet_ntoa(*(struct in_addr*) &ipv4->address)); +} + +static void ipv6_func(const ipv6_address_t *ipv6, void *userdata) { +} + +static void name_func(const char *name, void *userdata) { + fprintf(stderr, "NAME: %s\n", name); +} + +int main(int argc, char *argv[]) { + int ret = 1, fd = -1; + ipv4_address_t ipv4; + + if ((fd = mdns_open_socket()) < 0) + goto finish; + +/* if (mdns_query_name(fd, argc > 1 ? argv[1] : "ecstasy.local", &ipv4_func, &ipv6_func, NULL) < 0) */ +/* goto finish; */ + + ipv4.address = inet_addr(argc > 1 ? argv[1] : "192.168.100.1"); + + if (mdns_query_ipv4(fd, &ipv4, name_func, NULL) < 0) + goto finish; + + ret = 0; + +finish: + + if (fd >= 0) + close(fd); + + return ret; +} diff --git a/src/nss.c b/src/nss.c new file mode 100644 index 0000000..0515b2d --- /dev/null +++ b/src/nss.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "query.h" + +#define MAX_ENTRIES 16 + +#ifdef NSS_IPV4_ONLY +#define _nss_mdns_gethostbyname2_r _nss_mdns4_gethostbyname2_r +#define _nss_mdns_gethostbyname_r _nss_mdns4_gethostbyname_r +#define _nss_mdns_gethostbyaddr_r _nss_mdns4_gethostbyaddr_r +#elif NSS_IPV6_ONLY +#define _nss_mdns_gethostbyname2_r _nss_mdns6_gethostbyname2_r +#define _nss_mdns_gethostbyname_r _nss_mdns6_gethostbyname_r +#define _nss_mdns_gethostbyaddr_r _nss_mdns6_gethostbyaddr_r +#endif + +struct userdata { + int count; + int data_len; /* only valid when doing reverse lookup */ + union { + ipv4_address_t ipv4[MAX_ENTRIES]; + ipv6_address_t ipv6[MAX_ENTRIES]; + char *name[MAX_ENTRIES]; + } data; +}; + +static void ipv4_callback(const ipv4_address_t *ipv4, void *userdata) { + struct userdata *u = userdata; + assert(ipv4 && userdata); + + if (u->count >= MAX_ENTRIES) + return; + + u->data.ipv4[u->count++] = *ipv4; + u->data_len += sizeof(ipv4_address_t); +} + +static void ipv6_callback(const ipv6_address_t *ipv6, void *userdata) { + struct userdata *u = userdata; + assert(ipv6 && userdata); + + if (u->count >= MAX_ENTRIES) + return; + + u->data.ipv6[u->count++] = *ipv6; + u->data_len += sizeof(ipv6_address_t); +} + +static void name_callback(const char*name, void *userdata) { + struct userdata *u = userdata; + assert(name && userdata); + + if (u->count >= MAX_ENTRIES) + return; + + u->data.name[u->count++] = strdup(name); + u->data_len += strlen(name)+1; +} + +enum nss_status _nss_mdns_gethostbyname2_r( + const char *name, + int af, + struct hostent * result, + char *buffer, + size_t buflen, + int *errnop, + int *h_errnop) { + + struct userdata u; + enum nss_status status = NSS_STATUS_UNAVAIL; + int fd = -1, r, i; + size_t address_length, l, index, astart; + +/* DEBUG_TRAP; */ + +#ifdef NSS_IPV4_ONLY + if (af != AF_INET) +#elif NSS_IPV6_ONLY + if (af != AF_INET6) +#else + if (af != AF_INET && af != AF_INET6) +#endif + { + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + goto finish; + } + + address_length = af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t); + if (buflen < + sizeof(char*)+ /* alias names */ + strlen(name)+1) { /* official name */ + + *errnop = ERANGE; + *h_errnop = NO_RECOVERY; + status = NSS_STATUS_TRYAGAIN; + + goto finish; + } + + if ((fd = mdns_open_socket()) < 0) { + + *errnop = errno; + *h_errnop = NO_RECOVERY; + goto finish; + } + + u.count = 0; + u.data_len = 0; + + if ((r = mdns_query_name(fd, name, af == AF_INET ? ipv4_callback : NULL, af == AF_INET6 ? ipv6_callback : NULL, &u)) < 0) { + *errnop = ETIMEDOUT; + *h_errnop = HOST_NOT_FOUND; + goto finish; + } + + /* Alias names */ + *((char**) buffer) = NULL; + result->h_aliases = (char**) buffer; + index = sizeof(char*); + + /* Official name */ + strcpy(buffer+index, name); + result->h_name = buffer+index; + index += strlen(name)+1; + + result->h_addrtype = af; + result->h_length = address_length; + + /* Check if there's enough space for the addresses */ + if (buflen < index+u.data_len+sizeof(char*)*(u.count+1)) { + *errnop = ERANGE; + *h_errnop = NO_RECOVERY; + status = NSS_STATUS_TRYAGAIN; + goto finish; + } + + /* Addresses */ + astart = index; + l = u.count*address_length; + memcpy(buffer+astart, &u.data, l); + index += l; + + /* Address array */ + for (i = 0; i < u.count; i++) + ((char**) (buffer+index))[i] = buffer+astart+address_length*i; + ((char**) (buffer+index))[i] = NULL; + + result->h_addr_list = (char**) (buffer+index); + + status = NSS_STATUS_SUCCESS; + +finish: + if (fd >= 0) + close(fd); + + return status; +} + +enum nss_status _nss_mdns_gethostbyname_r ( + const char *name, + struct hostent *result, + char *buffer, + size_t buflen, + int *errnop, + int *h_errnop) { + + return _nss_mdns_gethostbyname2_r( + name, +#ifdef NSS_IPV6_ONLY + AF_INET6, +#else + AF_INET, +#endif + result, + buffer, + buflen, + errnop, + h_errnop); +} + +enum nss_status _nss_mdns_gethostbyaddr_r( + const void* addr, + int len, + int af, + struct hostent *result, + char *buffer, + size_t buflen, + int *errnop, + int *h_errnop) { + + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + + struct userdata u; + enum nss_status status = NSS_STATUS_UNAVAIL; + int fd = -1, r; + size_t address_length, index, astart; + + u.count = 0; + u.data_len = 0; + + address_length = af == AF_INET ? sizeof(ipv4_address_t) : sizeof(ipv6_address_t); + + if (len != (int) address_length || +#ifdef NSS_IPV4_ONLY + af != AF_INET +#elif NSS_IPV6_ONLY + af != AF_INET6 +#else + (af != AF_INET && af != AF_INET6) +#endif + ) { + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + goto finish; + } + + if (buflen < + sizeof(char*)+ /* alias names */ + address_length) { /* address */ + + *errnop = ERANGE; + *h_errnop = NO_RECOVERY; + status = NSS_STATUS_TRYAGAIN; + + goto finish; + } + + if ((fd = mdns_open_socket()) < 0) { + + *errnop = errno; + *h_errnop = NO_RECOVERY; + goto finish; + } + + if (af == AF_INET) + r = mdns_query_ipv4(fd, (ipv4_address_t*) addr, name_callback, &u); + else + r = mdns_query_ipv6(fd, (ipv6_address_t*) addr, name_callback, &u); + + if (r < 0) { + *errnop = ETIMEDOUT; + *h_errnop = HOST_NOT_FOUND; + goto finish; + } + + /* Alias names */ + *((char**) buffer) = NULL; + result->h_aliases = (char**) buffer; + index = sizeof(char*); + + assert(u.count > 0 && u.data.name[0]); + if (buflen < + strlen(u.data.name[0])+1+ /* official names */ + sizeof(char*)+ /* alias names */ + address_length+ /* address */ + sizeof(void*)*2) { /* address list */ + + *errnop = ERANGE; + *h_errnop = NO_RECOVERY; + status = NSS_STATUS_TRYAGAIN; + goto finish; + } + + /* Official name */ + strcpy(buffer+index, u.data.name[0]); + result->h_name = buffer+index; + index += strlen(u.data.name[0])+1; + + result->h_addrtype = af; + result->h_length = address_length; + + /* Address */ + astart = index; + memcpy(buffer+astart, addr, address_length); + index += address_length; + + /* Address array */ + ((char**) (buffer+index))[0] = buffer+astart; + ((char**) (buffer+index))[1] = NULL; + result->h_addr_list = (char**) (buffer+index); + + status = NSS_STATUS_SUCCESS; + +finish: + if (fd >= 0) + close(fd); + + return status; +} + diff --git a/src/nsstest.c b/src/nsstest.c new file mode 100644 index 0000000..d4f1c4c --- /dev/null +++ b/src/nsstest.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + struct hostent *he; + in_addr_t **a; + char *arg= argc > 1 ? argv[1] : "whiskey.local"; + uint8_t t[256]; + + if (inet_pton(AF_INET, arg, &t) > 0) + he = gethostbyaddr(t, 4, AF_INET); + else if (inet_pton(AF_INET6, arg, &t) > 0) + he = gethostbyaddr(t, 16, AF_INET6); + else + he = gethostbyname(arg); + + if (!he) { + fprintf(stderr, "lookup failed\n"); + return 1; + } + + fprintf(stderr, "official name: %s\n", he->h_name); + + if (!he->h_aliases || !he->h_aliases[0]) + fprintf(stderr, "no aliases\n"); + else { + char **h; + fprintf(stderr, "aliases:"); + for (h = he->h_aliases; *h; h++) + fprintf(stderr, " %s", *h); + fprintf(stderr, "\n"); + } + + fprintf(stderr, "addr type: %s\n", he->h_addrtype == AF_INET ? "inet" : (he->h_addrtype == AF_INET6 ? "inet6" : NULL)); + fprintf(stderr, "addr length: %i\n", he->h_length); + + fprintf(stderr, "addresses:"); + for (a = (in_addr_t**) he->h_addr_list; *a; a++) { + char t[256]; + fprintf(stderr, " %s", inet_ntop(he->h_addrtype, *a, t, sizeof(t))); + } + fprintf(stderr, "\n"); + + return 0; +} diff --git a/src/query.c b/src/query.c new file mode 100644 index 0000000..5673c3e --- /dev/null +++ b/src/query.c @@ -0,0 +1,541 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dns.h" +#include "util.h" +#include "query.h" + +static const usec_t retry_ms[] = { 200000, 500000, 900000, 1400000, 0 }; + +static void mdns_mcast_group(struct sockaddr_in *ret_sa) { + assert(ret_sa); + + ret_sa->sin_family = AF_INET; + ret_sa->sin_port = htons(5353); + ret_sa->sin_addr.s_addr = inet_addr("224.0.0.251"); +} + +int mdns_open_socket(void) { + struct ip_mreqn mreq; + struct sockaddr_in sa; + int fd = -1, ttl, yes; + + mdns_mcast_group(&sa); + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + fprintf(stderr, "socket() failed: %s\n", strerror(errno)); + goto fail; + } + + ttl = 255; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { + fprintf(stderr, "IP_MULTICAST_TTL failed: %s\n", strerror(errno)); + goto fail; + } + + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + fprintf(stderr, "SO_REUSEADDR failed: %s\n", strerror(errno)); + goto fail; + } + + if (bind(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { + fprintf(stderr, "bind() failed: %s\n", strerror(errno)); + goto fail; + } + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = sa.sin_addr; + mreq.imr_address.s_addr = htonl(INADDR_ANY); + mreq.imr_ifindex = 0; + + if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + fprintf(stderr, "IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno)); + goto fail; + } + + if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { + fprintf(stderr, "O_RECVTTL failed: %s\n", strerror(errno)); + goto fail; + } + + if (set_cloexec(fd) < 0) { + fprintf(stderr, "FD_CLOEXEC failed: %s\n", strerror(errno)); + goto fail; + } + + if (set_nonblock(fd) < 0) { + fprintf(stderr, "O_ONONBLOCK failed: %s\n", strerror(errno)); + goto fail; + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} + +static int send_dns_packet(int fd, struct dns_packet *p) { + struct sockaddr_in sa; + assert(fd >= 0 && p); + + assert(dns_packet_check_valid(p) >= 0); + + mdns_mcast_group(&sa); + + for (;;) { + if (sendto(fd, p->data, p->size, 0, (struct sockaddr*) &sa, sizeof(sa)) >= 0) + return 0; + + if (errno != EAGAIN) { + fprintf(stderr, "sendto() failed: %s\n", strerror(errno)); + return -1; + } + + if (wait_for_write(fd, NULL) < 0) + return -1; + } +} + +static int recv_dns_packet(int fd, struct dns_packet **ret_packet, uint8_t* ret_ttl, struct timeval *end) { + struct dns_packet *p= NULL; + struct msghdr msg; + struct iovec io; + int ret = -1; + uint8_t aux[16]; + assert(fd >= 0); + + p = dns_packet_new(); + + io.iov_base = p->data; + io.iov_len = sizeof(p->data); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = aux; + msg.msg_controllen = sizeof(aux); + msg.msg_flags = 0; + + for (;;) { + ssize_t l; + int r; + + if ((l = recvmsg(fd, &msg, 0)) >= 0) { + struct cmsghdr *cmsg; + *ret_ttl = 0; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) { + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) { + *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg); + break; + } + } + + if (cmsg == NULL) { + fprintf(stderr, "Didn't recieve TTL\n"); + goto fail; + } + + p->size = (size_t) l; + + *ret_packet = p; + return 0; + } + + if (errno != EAGAIN) { + fprintf(stderr, "recvfrom() failed: %s\n", strerror(errno)); + goto fail; + } + + if ((r = wait_for_read(fd, end)) < 0) + goto fail; + else if (r > 0) { /* timeout */ + ret = 1; + goto fail; + } + } + +fail: + if (p) + dns_packet_free(p); + + return ret; +} + +static int send_name_query(int fd, const char *name, int query_ipv4, int query_ipv6) { + int ret = -1; + struct dns_packet *p = NULL; + uint8_t *prev_name = NULL; + int qdcount = 0; + + assert(fd >= 0 && name && (query_ipv4 || query_ipv6)); + + if (!(p = dns_packet_new())) { + fprintf(stderr, "Failed to allocate DNS packet.\n"); + goto finish; + } + + dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + if (query_ipv4) { + if (!(prev_name = dns_packet_append_name(p, name))) { + fprintf(stderr, "Bad host name\n"); + goto finish; + + } + dns_packet_append_uint16(p, DNS_TYPE_A); + dns_packet_append_uint16(p, DNS_CLASS_IN); + qdcount++; + } + + if (query_ipv6) { + if (!dns_packet_append_name_compressed(p, name, prev_name)) { + fprintf(stderr, "Bad host name\n"); + goto finish; + } + + dns_packet_append_uint16(p, DNS_TYPE_AAAA); + dns_packet_append_uint16(p, DNS_CLASS_IN); + qdcount++; + } + + dns_packet_set_field(p, DNS_FIELD_QDCOUNT, qdcount); + + if (send_dns_packet(fd, p) < 0) + goto finish; + + ret = 0; + +finish: + if (p) + dns_packet_free(p); + + return ret; +} + +static int domain_cmp(const char *a, const char *b) { + size_t al, bl; + + al = strlen(a); + bl = strlen(b); + + if (al > 0 && a[al-1] == '.') + al --; + + if (bl > 0 && b[bl-1] == '.') + bl --; + + if (al != bl) + return al > bl ? 1 : (al < bl ? -1 : 0); + + return strncasecmp(a, b, al); +} + +static int process_name_response(int fd, const char *name, usec_t timeout, void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata), void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata), void *userdata) { + assert(fd >= 0 && name && (ipv4_func || ipv6_func)); + struct dns_packet *p = NULL; + int done = 0; + struct timeval end; + + gettimeofday(&end, NULL); + timeval_add(&end, timeout); + + while (!done) { + uint8_t ttl; + int r; + + if ((r = recv_dns_packet(fd, &p, &ttl, &end)) < 0) + return -1; + else if (r > 0) /* timeout */ + return 1; + + /* Ignore packets with RFC != 255 */ + if (ttl == 255) { + + /* Ignore corrupt packets */ + if (dns_packet_check_valid_response(p) >= 0) { + + for (;;) { + char pname[256]; + uint16_t type, class; + uint32_t rr_ttl; + uint16_t rdlength; + + if (dns_packet_consume_name(p, pname, sizeof(pname)) < 0 || + dns_packet_consume_uint16(p, &type) < 0 || + dns_packet_consume_uint16(p, &class) < 0 || + dns_packet_consume_uint32(p, &rr_ttl) < 0 || + dns_packet_consume_uint16(p, &rdlength) < 0) { + break; + } + + /* Remove mDNS cache flush bit */ + class &= ~0x8000; + + if (ipv4_func && + type == DNS_TYPE_A && + class == DNS_CLASS_IN && + !domain_cmp(name, pname) && + rdlength == sizeof(ipv4_address_t)) { + + ipv4_address_t ipv4; + + if (dns_packet_consume_bytes(p, &ipv4, sizeof(ipv4)) < 0) + break; + + ipv4_func(&ipv4, userdata); + done = 1; + + } else if (ipv6_func && + type == DNS_TYPE_AAAA && + class == DNS_CLASS_IN && + !domain_cmp(name, pname) && + rdlength == sizeof(ipv6_address_t)) { + + ipv6_address_t ipv6; + + if (dns_packet_consume_bytes(p, &ipv6, sizeof(ipv6_address_t)) < 0) + break; + + ipv6_func(&ipv6, userdata); + done = 1; + } else { + + /* Step over */ + + if (dns_packet_consume_seek(p, rdlength) < 0) + break; + } + } + } + } + + if (p) + dns_packet_free(p); + } + + return 0; +} + +int mdns_query_name(int fd, const char *name, void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata), void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata), void *userdata) { + const usec_t *timeout = retry_ms; + assert(fd >= 0 && name && (ipv4_func || ipv6_func)); + + while (*timeout > 0) { + int n; + + if (send_name_query(fd, name, !!ipv4_func, !!ipv6_func) < 0) + return -1; + + if ((n = process_name_response(fd, name, *timeout, ipv4_func, ipv6_func, userdata)) < 0) + return -1; + + if (n == 0) + return 0; + + /* Timeout */ + + timeout++; + } + + return -1; +} + +static int send_reverse_query(int fd, const char *name) { + int ret = -1; + struct dns_packet *p = NULL; + + assert(fd >= 0 && name); + + if (!(p = dns_packet_new())) { + fprintf(stderr, "Failed to allocate DNS packet.\n"); + goto finish; + } + + dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + + if (!dns_packet_append_name(p, name)) { + fprintf(stderr, "Bad host name\n"); + goto finish; + } + + dns_packet_append_uint16(p, DNS_TYPE_PTR); + dns_packet_append_uint16(p, DNS_CLASS_IN); + + dns_packet_set_field(p, DNS_FIELD_QDCOUNT, 1); + + if (send_dns_packet(fd, p) < 0) + goto finish; + + ret = 0; + +finish: + if (p) + dns_packet_free(p); + + return ret; +} + +static int process_reverse_response(int fd, const char *name, usec_t timeout, void (*name_func)(const char *name, void *userdata), void *userdata) { + assert(fd >= 0 && name && name_func); + struct dns_packet *p = NULL; + int done = 0; + struct timeval end; + + gettimeofday(&end, NULL); + timeval_add(&end, timeout); + + while (!done) { + uint8_t ttl; + int r; + + if ((r = recv_dns_packet(fd, &p, &ttl, &end)) < 0) + return -1; + else if (r > 0) /* timeout */ + return 1; + + /* Ignore packets with RFC != 255 */ + if (ttl == 255) { + + /* Ignore corrupt packets */ + if (dns_packet_check_valid_response(p) >= 0) { + + for (;;) { + char pname[256]; + uint16_t type, class; + uint32_t rr_ttl; + uint16_t rdlength; + + if (dns_packet_consume_name(p, pname, sizeof(pname)) < 0 || + dns_packet_consume_uint16(p, &type) < 0 || + dns_packet_consume_uint16(p, &class) < 0 || + dns_packet_consume_uint32(p, &rr_ttl) < 0 || + dns_packet_consume_uint16(p, &rdlength) < 0) { + break; + } + + /* Remove mDNS cache flush bit */ + class &= ~0x8000; + + if (type == DNS_TYPE_PTR && + class == DNS_CLASS_IN && + !domain_cmp(name, pname)) { + + char rname[256]; + + if (dns_packet_consume_name(p, rname, sizeof(rname)) < 0) + break; + + name_func(rname, userdata); + done = 1; + + } else { + + /* Step over */ + + if (dns_packet_consume_seek(p, rdlength) < 0) + break; + } + } + } + } + + if (p) + dns_packet_free(p); + } + + return 0; +} + +static int query_reverse(int fd, const char *name, void (*name_func)(const char *name, void *userdata), void *userdata) { + const usec_t *timeout = retry_ms; + assert(fd >= 0 && name && name_func); + + while (*timeout > 0) { + int n; + + if (send_reverse_query(fd, name) < 0) + return -1; + + if ((n = process_reverse_response(fd, name, *timeout, name_func, userdata)) < 0) + return -1; + + if (n == 0) + return 0; + + /* Timeout */ + + timeout++; + } + + return -1; +} + + +int mdns_query_ipv4(int fd, const ipv4_address_t *ipv4, void (*name_func)(const char *name, void *userdata), void *userdata) { + char name[256]; + uint32_t a; + assert(fd >= 0 && ipv4 && name_func); + + a = ntohl(ipv4->address); + snprintf(name, sizeof(name), "%u.%u.%u.%u.in-addr.arpa", a & 0xFF, (a >> 8) & 0xFF, (a >> 16) & 0xFF, a >> 24); + + return query_reverse(fd, name, name_func, userdata); +} + +int mdns_query_ipv6(int fd, const ipv6_address_t *ipv6, void (*name_func)(const char *name, void *userdata), void *userdata) { + char name[256]; + assert(fd >= 0 && ipv6 && name_func); + + snprintf(name, sizeof(name), "%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.int", + ipv6->address[15] & 0xF, + ipv6->address[15] >> 4, + ipv6->address[14] & 0xF, + ipv6->address[14] >> 4, + ipv6->address[13] & 0xF, + ipv6->address[13] >> 4, + ipv6->address[12] & 0xF, + ipv6->address[12] >> 4, + ipv6->address[11] & 0xF, + ipv6->address[11] >> 4, + ipv6->address[10] & 0xF, + ipv6->address[10] >> 4, + ipv6->address[9] & 0xF, + ipv6->address[9] >> 4, + ipv6->address[8] & 0xF, + ipv6->address[8] >> 4, + ipv6->address[7] & 0xF, + ipv6->address[7] >> 4, + ipv6->address[6] & 0xF, + ipv6->address[6] >> 4, + ipv6->address[5] & 0xF, + ipv6->address[5] >> 4, + ipv6->address[4] & 0xF, + ipv6->address[4] >> 4, + ipv6->address[3] & 0xF, + ipv6->address[3] >> 4, + ipv6->address[2] & 0xF, + ipv6->address[2] >> 4, + ipv6->address[1] & 0xF, + ipv6->address[1] >> 4, + ipv6->address[0] & 0xF, + ipv6->address[0] >> 4); + + return query_reverse(fd, name, name_func, userdata); +} + diff --git a/src/query.h b/src/query.h new file mode 100644 index 0000000..0c22c49 --- /dev/null +++ b/src/query.h @@ -0,0 +1,32 @@ +#ifndef fooqueryhfoo +#define fooqueryhfoo + +#include + +typedef struct { + uint32_t address; +} ipv4_address_t; + +typedef struct { + uint8_t address[16]; +} ipv6_address_t; + +int mdns_open_socket(void); + +int mdns_query_name(int fd, + const char *name, + void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata), + void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata), + void *userdata); + +int mdns_query_ipv4(int fd, + const ipv4_address_t *ipv4, + void (*name_func)(const char *name, void *userdata), + void *userdata); + +int mdns_query_ipv6(int fd, + const ipv6_address_t *ipv6, + void (*name_func)(const char *name, void *userdata), + void *userdata); + +#endif diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..5f89584 --- /dev/null +++ b/src/util.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include + +#include "util.h" + +/* Calculate the difference between the two specfified timeval + * timestamsps. */ +usec_t timeval_diff(const struct timeval *a, const struct timeval *b) { + usec_t r; + assert(a && b); + + /* Check which whan is the earlier time and swap the two arguments if reuqired. */ + if (timeval_cmp(a, b) < 0) { + const struct timeval *c; + c = a; + a = b; + b = c; + } + + /* Calculate the second difference*/ + r = ((usec_t) a->tv_sec - b->tv_sec)* 1000000; + + /* Calculate the microsecond difference */ + if (a->tv_usec > b->tv_usec) + r += ((usec_t) a->tv_usec - b->tv_usec); + else if (a->tv_usec < b->tv_usec) + r -= ((usec_t) b->tv_usec - a->tv_usec); + + return r; +} + +/* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ +int timeval_cmp(const struct timeval *a, const struct timeval *b) { + assert(a && b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +/* Return the time difference between now and the specified timestamp */ +usec_t timeval_age(const struct timeval *tv) { + struct timeval now; + assert(tv); + gettimeofday(&now, NULL); + return timeval_diff(&now, tv); +} + +/* Add the specified time inmicroseconds to the specified timeval structure */ +void timeval_add(struct timeval *tv, usec_t v) { + unsigned long secs; + assert(tv); + + secs = (v/1000000); + tv->tv_sec += (unsigned long) secs; + v -= secs*1000000; + + tv->tv_usec += v; + + /* Normalize */ + while (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } +} + +int set_cloexec(int fd) { + int n; + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFD)) < 0) + return -1; + + if (n & FD_CLOEXEC) + return 0; + + return fcntl(fd, F_SETFD, n|FD_CLOEXEC); +} + +int set_nonblock(int fd) { + int n; + assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (n & O_NONBLOCK) + return 0; + + return fcntl(fd, F_SETFL, n|O_NONBLOCK); +} + +int wait_for_write(int fd, struct timeval *end) { + struct timeval now; + + if (end) + gettimeofday(&now, NULL); + + for (;;) { + struct timeval tv; + fd_set fds; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (end) { + if (timeval_cmp(&now, end) >= 0) + return 1; + + tv.tv_sec = tv.tv_usec = 0; + timeval_add(&tv, timeval_diff(end, &now)); + } + + if ((r = select(fd+1, NULL, &fds, NULL, end ? &tv : NULL)) < 0) { + if (errno != EINTR) { + fprintf(stderr, "select() failed: %s\n", strerror(errno)); + return -1; + } + } else if (r == 0) + return 1; + else { + if (FD_ISSET(fd, &fds)) + return 0; + } + + if (end) + gettimeofday(&now, NULL); + } +} + +int wait_for_read(int fd, struct timeval *end) { + struct timeval now; + + if (end) + gettimeofday(&now, NULL); + + for (;;) { + struct timeval tv; + fd_set fds; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (end) { + if (timeval_cmp(&now, end) >= 0) + return 1; + + tv.tv_sec = tv.tv_usec = 0; + timeval_add(&tv, timeval_diff(end, &now)); + } + + if ((r = select(fd+1, &fds, NULL, NULL, end ? &tv : NULL)) < 0) { + if (errno != EINTR) { + fprintf(stderr, "select() failed: %s\n", strerror(errno)); + return -1; + } + } else if (r == 0) + return 1; + else { + + if (FD_ISSET(fd, &fds)) + return 0; + } + + if (end) + gettimeofday(&now, NULL); + } +} + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..c550b4f --- /dev/null +++ b/src/util.h @@ -0,0 +1,21 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +#include +#include +#include + +typedef uint64_t usec_t; + +usec_t timeval_diff(const struct timeval *a, const struct timeval *b); +int timeval_cmp(const struct timeval *a, const struct timeval *b); +usec_t timeval_age(const struct timeval *tv); +void timeval_add(struct timeval *tv, usec_t v); + +int set_nonblock(int fd); +int set_cloexec(int fd); + +int wait_for_write(int fd, struct timeval *end); +int wait_for_read(int fd, struct timeval *end); + +#endif diff --git a/util.c b/util.c deleted file mode 100644 index 5f89584..0000000 --- a/util.c +++ /dev/null @@ -1,185 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "util.h" - -/* Calculate the difference between the two specfified timeval - * timestamsps. */ -usec_t timeval_diff(const struct timeval *a, const struct timeval *b) { - usec_t r; - assert(a && b); - - /* Check which whan is the earlier time and swap the two arguments if reuqired. */ - if (timeval_cmp(a, b) < 0) { - const struct timeval *c; - c = a; - a = b; - b = c; - } - - /* Calculate the second difference*/ - r = ((usec_t) a->tv_sec - b->tv_sec)* 1000000; - - /* Calculate the microsecond difference */ - if (a->tv_usec > b->tv_usec) - r += ((usec_t) a->tv_usec - b->tv_usec); - else if (a->tv_usec < b->tv_usec) - r -= ((usec_t) b->tv_usec - a->tv_usec); - - return r; -} - -/* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ -int timeval_cmp(const struct timeval *a, const struct timeval *b) { - assert(a && b); - - if (a->tv_sec < b->tv_sec) - return -1; - - if (a->tv_sec > b->tv_sec) - return 1; - - if (a->tv_usec < b->tv_usec) - return -1; - - if (a->tv_usec > b->tv_usec) - return 1; - - return 0; -} - -/* Return the time difference between now and the specified timestamp */ -usec_t timeval_age(const struct timeval *tv) { - struct timeval now; - assert(tv); - gettimeofday(&now, NULL); - return timeval_diff(&now, tv); -} - -/* Add the specified time inmicroseconds to the specified timeval structure */ -void timeval_add(struct timeval *tv, usec_t v) { - unsigned long secs; - assert(tv); - - secs = (v/1000000); - tv->tv_sec += (unsigned long) secs; - v -= secs*1000000; - - tv->tv_usec += v; - - /* Normalize */ - while (tv->tv_usec >= 1000000) { - tv->tv_sec++; - tv->tv_usec -= 1000000; - } -} - -int set_cloexec(int fd) { - int n; - assert(fd >= 0); - - if ((n = fcntl(fd, F_GETFD)) < 0) - return -1; - - if (n & FD_CLOEXEC) - return 0; - - return fcntl(fd, F_SETFD, n|FD_CLOEXEC); -} - -int set_nonblock(int fd) { - int n; - assert(fd >= 0); - - if ((n = fcntl(fd, F_GETFL)) < 0) - return -1; - - if (n & O_NONBLOCK) - return 0; - - return fcntl(fd, F_SETFL, n|O_NONBLOCK); -} - -int wait_for_write(int fd, struct timeval *end) { - struct timeval now; - - if (end) - gettimeofday(&now, NULL); - - for (;;) { - struct timeval tv; - fd_set fds; - int r; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if (end) { - if (timeval_cmp(&now, end) >= 0) - return 1; - - tv.tv_sec = tv.tv_usec = 0; - timeval_add(&tv, timeval_diff(end, &now)); - } - - if ((r = select(fd+1, NULL, &fds, NULL, end ? &tv : NULL)) < 0) { - if (errno != EINTR) { - fprintf(stderr, "select() failed: %s\n", strerror(errno)); - return -1; - } - } else if (r == 0) - return 1; - else { - if (FD_ISSET(fd, &fds)) - return 0; - } - - if (end) - gettimeofday(&now, NULL); - } -} - -int wait_for_read(int fd, struct timeval *end) { - struct timeval now; - - if (end) - gettimeofday(&now, NULL); - - for (;;) { - struct timeval tv; - fd_set fds; - int r; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if (end) { - if (timeval_cmp(&now, end) >= 0) - return 1; - - tv.tv_sec = tv.tv_usec = 0; - timeval_add(&tv, timeval_diff(end, &now)); - } - - if ((r = select(fd+1, &fds, NULL, NULL, end ? &tv : NULL)) < 0) { - if (errno != EINTR) { - fprintf(stderr, "select() failed: %s\n", strerror(errno)); - return -1; - } - } else if (r == 0) - return 1; - else { - - if (FD_ISSET(fd, &fds)) - return 0; - } - - if (end) - gettimeofday(&now, NULL); - } -} - diff --git a/util.h b/util.h deleted file mode 100644 index c550b4f..0000000 --- a/util.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef fooutilhfoo -#define fooutilhfoo - -#include -#include -#include - -typedef uint64_t usec_t; - -usec_t timeval_diff(const struct timeval *a, const struct timeval *b); -int timeval_cmp(const struct timeval *a, const struct timeval *b); -usec_t timeval_age(const struct timeval *tv); -void timeval_add(struct timeval *tv, usec_t v); - -int set_nonblock(int fd); -int set_cloexec(int fd); - -int wait_for_write(int fd, struct timeval *end); -int wait_for_read(int fd, struct timeval *end); - -#endif -- cgit