From 5a62c294114083df5ac4759e8bda232efdf3e025 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 15 May 2005 17:06:08 +0000 Subject: * send "legacy unicast" packets instead of normal mDNS packets * prepare for release 0.4 git-svn-id: file:///home/lennart/svn/public/nss-mdns/trunk@75 0ee8848e-81ea-0310-a63a-f631d1a40d77 --- configure.ac | 2 +- doc/README.html.in | 45 +++++++++------- src/dns.c | 5 +- src/mdns-test.c | 8 +-- src/query.c | 154 +++++++++++++++++++++++++++++++++-------------------- 5 files changed, 129 insertions(+), 85 deletions(-) diff --git a/configure.ac b/configure.ac index f15f54e..e8cc023 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # USA. AC_PREREQ(2.57) -AC_INIT([nss-mdns],[0.3],[mzaffzqaf (at) 0pointer (dot) de]) +AC_INIT([nss-mdns],[0.4],[mzaffzqaf (at) 0pointer (dot) de]) AC_CONFIG_SRCDIR([src/query.c]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([foreign -Wall]) diff --git a/doc/README.html.in b/doc/README.html.in index 65955d0..5dc131f 100644 --- a/doc/README.html.in +++ b/doc/README.html.in @@ -42,9 +42,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

News

+
Sun May 15 2005:

Version 0.4 +released. Changes include: small portability fix for big endian +architectures; send "legacy unicast" packets instead of normal mDNS +packets (this should reduce traffic and improve response time)

+
Jan Sun 16 2005:

Version 0.3 -released. Changes include: add Debianization, use ip6.arpa instead of ip6.int for reverse IPv6 lookups.

+released. Changes include: add Debianization; use ip6.arpa instead of ip6.int for reverse IPv6 lookups.

Fri Dec 17 2004:

Version 0.2 @@ -70,11 +76,12 @@ means that you have to run a mDNS responder daemon seperately from nss-mdns if you want to register the local host name via mDNS (e.g. HOWL).

-

nss-mdns is very lightweight (22 KByte stripped binary .so compiled with --DNDEBUG=1 on i386, gcc 3.3), has no dependencies besides the glibc and requires only minimal -configuration.

+

nss-mdns is very lightweight (23 KByte stripped binary +.so compiled with -DNDEBUG=1 on i386, gcc +3.3), has no dependencies besides the glibc and requires only +minimal configuration.

-

Status

+

Current Status

It works!

@@ -100,7 +107,8 @@ lookup for IPv4.

To activate one of the NSS modules you have to edit /etc/nsswitch.conf and add mdns4 -(resp. mdns, mdns6) to the line starting with "hosts:". On Debian this looks like this:

+(resp. mdns, mdns6) to the line starting with +"hosts:". On Debian this looks like this:

# /etc/nsswitch.conf
 
@@ -125,23 +133,24 @@ use glibc's getent tool:
 
$ getent hosts foo.local
 192.168.50.4    foo.local
-

Replace foo whith a host name that has been registered with an mDNS responder.

- -

Due to some traffic suppression algorithms in mDNS responders -repeated mDNS resolutions are slowed down. Consider installing glibc's name -service cache daemon nscd to work around this limitation.

+

Replace foo whith a host name that has been registered with +an mDNS responder.

-

When doing troubleshooting for nss-mdns, don't forget to -disable nscd for getting sensible results.

+

To reduce the traffic nss-mdns is responsible for consider +installing glibc's name service cache daemon +nscd. However, when doing troubleshooting for +nss-mdns, don't forget to disable nscd for getting +sensible results.

-

If you run a firewall, don't forget to allow UDP traffic from and to port 5353 -from and to the the mDNS multicast address 224.0.0.251.

+

If you run a firewall, don't forget to allow UDP traffic to the the +mDNS multicast address 224.0.0.251 on port 5353.

Requirements

-

Currently, nss-mdns is tested on Linux only. A fairly modern glibc installation with development headers (2.0 or newer) is required. Not -suprisingly nss-mdns requires a kernel compiled with IPv4 -multicasting support enabled.

+

Currently, nss-mdns is tested on Linux only. A fairly +modern glibc installation with development headers (2.0 or +newer) is required. Not suprisingly nss-mdns requires a +kernel compiled with IPv4 multicasting support enabled.

nss-mdns was developed and tested on Debian GNU/Linux "testing" from December 2004, it should work on most other Linux diff --git a/src/dns.c b/src/dns.c index c3be024..b32dc2f 100644 --- a/src/dns.c +++ b/src/dns.c @@ -157,9 +157,6 @@ int dns_packet_check_valid_response(struct dns_packet *p) { if (!(flags & DNS_FLAG_QR)) return -1; - if (dns_packet_get_field(p, DNS_FIELD_QDCOUNT) > 0) - return -1; - return 0; } @@ -278,7 +275,7 @@ 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) { - assert(p && length >= 0); + assert(p); if (!length) return 0; diff --git a/src/mdns-test.c b/src/mdns-test.c index c8fbdb9..e3c7981 100644 --- a/src/mdns-test.c +++ b/src/mdns-test.c @@ -47,10 +47,10 @@ int main(int argc, char *argv[]) { 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_name(fd, argc > 1 ? argv[1] : "cocaine.local", &ipv4_func, &ipv6_func, NULL) < 0) + goto finish; + + ipv4.address = inet_addr(argc > 1 ? argv[1] : "192.168.50.1"); if (mdns_query_ipv4(fd, &ipv4, name_func, NULL) < 0) goto finish; diff --git a/src/query.c b/src/query.c index 546d072..1b66df9 100644 --- a/src/query.c +++ b/src/query.c @@ -36,15 +36,33 @@ #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 const usec_t retry_ms[] = { 500000, 1000000, 0 }; + +static uint16_t get_random_id(void) { + uint16_t id = 0; + int ok = 0, fd; + + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) { + ok = read(fd, &id, sizeof(id)) == 2; + close(fd); + } + + if (!ok) + ok = random() & 0xFFFF; + + return id; +} static void mdns_mcast_group(struct sockaddr_in *ret_sa) { assert(ret_sa); + + memset(ret_sa, 0, sizeof(struct sockaddr_in)); ret_sa->sin_family = AF_INET; ret_sa->sin_port = htons(5353); @@ -52,44 +70,30 @@ static void mdns_mcast_group(struct sockaddr_in *ret_sa) { } 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; - } - + sa.sin_family = AF_INET; + sa.sin_port = 0; + sa.sin_addr.s_addr = INADDR_ANY; + 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; - } - + yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { fprintf(stderr, "IP_RECVTTL failed: %s\n", strerror(errno)); goto fail; @@ -284,7 +288,7 @@ fail: return ret; } -static int send_name_query(int fd, const char *name, int query_ipv4, int query_ipv6) { +static int send_name_query(int fd, const char *name, uint16_t id, int query_ipv4, int query_ipv6) { int ret = -1; struct dns_packet *p = NULL; uint8_t *prev_name = NULL; @@ -297,6 +301,7 @@ static int send_name_query(int fd, const char *name, int query_ipv4, int query_i goto finish; } + dns_packet_set_field(p, DNS_FIELD_ID, id); dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); #ifndef NSS_IP6_ONLY @@ -354,7 +359,24 @@ static int domain_cmp(const char *a, const char *b) { 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) { +static int skip_questions(struct dns_packet *p) { + unsigned i; + assert(p); + + for (i = dns_packet_get_field(p, DNS_FIELD_QDCOUNT); i > 0; i--) { + char pname[256]; + uint16_t type, class; + + if (dns_packet_consume_name(p, pname, sizeof(pname)) < 0 || + dns_packet_consume_uint16(p, &type) < 0 || + dns_packet_consume_uint16(p, &class) < 0) + return -1; + } + + return 0; +} + +static int process_name_response(int fd, const char *name, usec_t timeout, uint16_t id, void (*ipv4_func)(const ipv4_address_t *ipv4, void *userdata), void (*ipv6_func)(const ipv6_address_t *ipv6, void *userdata), void *userdata) { struct dns_packet *p = NULL; int done = 0; struct timeval end; @@ -374,12 +396,15 @@ static int process_name_response(int fd, const char *name, usec_t timeout, void return 1; /* Ignore packets with RFC != 255 */ - if (ttl == 255) { + if (/* ttl == 255 && */ + dns_packet_check_valid_response(p) >= 0 && + dns_packet_get_field(p, DNS_FIELD_ID) == id) { + + unsigned i; - /* Ignore corrupt packets */ - if (dns_packet_check_valid_response(p) >= 0) { + if (skip_questions(p) >= 0) - for (;;) { + for (i = dns_packet_get_field(p, DNS_FIELD_ANCOUNT); i > 0; i--) { char pname[256]; uint16_t type, class; uint32_t rr_ttl; @@ -392,7 +417,7 @@ static int process_name_response(int fd, const char *name, usec_t timeout, void dns_packet_consume_uint16(p, &rdlength) < 0) { break; } - + /* Remove mDNS cache flush bit */ class &= ~0x8000; @@ -402,27 +427,27 @@ static int process_name_response(int fd, const char *name, usec_t timeout, void 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; } #endif #if ! defined(NSS_IPV6_ONLY) && ! defined(NSS_IPV4_ONLY) - else +/* else */ #endif #ifndef NSS_IPV4_ONLY - if (ipv6_func && - type == DNS_TYPE_AAAA && - class == DNS_CLASS_IN && - !domain_cmp(name, pname) && - rdlength == sizeof(ipv6_address_t)) { - + 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) @@ -432,35 +457,40 @@ static int process_name_response(int fd, const char *name, usec_t timeout, void done = 1; } #endif - else { - + 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; + uint16_t id; + assert(fd >= 0 && name && (ipv4_func || ipv6_func)); + id = get_random_id(); + while (*timeout > 0) { int n; - if (send_name_query(fd, name, !!ipv4_func, !!ipv6_func) < 0) + if (send_name_query(fd, name, id, !!ipv4_func, !!ipv6_func) < 0) return -1; - if ((n = process_name_response(fd, name, *timeout, ipv4_func, ipv6_func, userdata)) < 0) + if ((n = process_name_response(fd, name, *timeout, id, ipv4_func, ipv6_func, userdata)) < 0) return -1; if (n == 0) @@ -474,7 +504,7 @@ int mdns_query_name(int fd, const char *name, void (*ipv4_func)(const ipv4_addre return -1; } -static int send_reverse_query(int fd, const char *name) { +static int send_reverse_query(int fd, const char *name, uint16_t id) { int ret = -1; struct dns_packet *p = NULL; @@ -485,6 +515,7 @@ static int send_reverse_query(int fd, const char *name) { goto finish; } + dns_packet_set_field(p, DNS_FIELD_ID, id); 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)) { @@ -506,7 +537,7 @@ finish: 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) { +static int process_reverse_response(int fd, const char *name, usec_t timeout, uint16_t id, void (*name_func)(const char *name, void *userdata), void *userdata) { struct dns_packet *p = NULL; int done = 0; struct timeval end; @@ -526,12 +557,15 @@ static int process_reverse_response(int fd, const char *name, usec_t timeout, vo return 1; /* Ignore packets with RFC != 255 */ - if (ttl == 255) { + if (/* ttl == 255 && */ + dns_packet_check_valid_response(p) >= 0 && + dns_packet_get_field(p, DNS_FIELD_ID) == id) { - /* Ignore corrupt packets */ - if (dns_packet_check_valid_response(p) >= 0) { - - for (;;) { + unsigned i; + + if (skip_questions(p) >= 0) { + + for (i = dns_packet_get_field(p, DNS_FIELD_ANCOUNT); i > 0; i--) { char pname[256]; uint16_t type, class; uint32_t rr_ttl; @@ -544,24 +578,24 @@ static int process_reverse_response(int fd, const char *name, usec_t timeout, vo 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) @@ -580,15 +614,19 @@ static int process_reverse_response(int fd, const char *name, usec_t timeout, vo 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; + uint16_t id; + assert(fd >= 0 && name && name_func); + id = get_random_id(); + while (*timeout > 0) { int n; - if (send_reverse_query(fd, name) <= 0) /* error or no interface to send data on */ + if (send_reverse_query(fd, name, id) <= 0) /* error or no interface to send data on */ return -1; - if ((n = process_reverse_response(fd, name, *timeout, name_func, userdata)) < 0) + if ((n = process_reverse_response(fd, name, *timeout, id, name_func, userdata)) < 0) return -1; if (n == 0) @@ -616,7 +654,7 @@ int mdns_query_ipv4(int fd, const ipv4_address_t *ipv4, void (*name_func)(const #endif #ifndef NSS_IPV4_ONLY -static int mdns_query_ipv6(int fd, const ipv6_address_t *ipv6, 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) { char name[256]; assert(fd >= 0 && ipv6 && name_func); -- cgit