]> git.meshlink.io Git - meshlink/commitdiff
Improved mDNS compliance.
authorGuus Sliepen <guus@meshlink.io>
Tue, 18 Aug 2020 22:35:31 +0000 (00:35 +0200)
committerGuus Sliepen <guus@meshlink.io>
Thu, 15 Apr 2021 18:30:51 +0000 (20:30 +0200)
Split the mDNS packets into separate request and response packets, and only
send responses when we receive a valid request.

It's now possible to find local MeshLink nodes using tools like dns-sd and
avahi-browse, if told to explicitly search for the _appname._tcp service.

src/discovery.c
src/mdns.c
src/mdns.h

index 439bcbef479a1e6053ab2109bee4f9650098934f..31dc63b616ad73682f0a2a2452a8a545ada96b7a 100644 (file)
@@ -178,11 +178,12 @@ static void send_mdns_packet_ipv6(meshlink_handle_t *mesh, int fd, int index, co
 static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t *addr) {
        // Configure the socket to send the packet to the right interface
        int fd;
-       uint8_t data[1024];
+       uint8_t request[1024], response[1024];
        char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
        const char *keys[] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
        const char *values[] = {mesh->name, fingerprint};
-       size_t size = prepare_packet(data, sizeof data, fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, values, false);
+       size_t request_size = prepare_request(request, sizeof request, mesh->appname, "tcp");
+       size_t response_size = prepare_response(response, sizeof response, fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, values);
        free(fingerprint);
 
        switch(addr->address.sa.sa_family) {
@@ -203,7 +204,8 @@ static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t
 
 #endif
 
-               send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, data, size);
+               send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, request, request_size);
+               send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, response, response_size);
                break;
 
        case AF_INET6:
@@ -217,7 +219,8 @@ static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t
 
 #endif
 
-               send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, data, size);
+               send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, request, request_size);
+               send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, response, response_size);
                break;
 
        default:
@@ -248,9 +251,8 @@ static void mdns_io_handler(event_loop_t *loop, void *data, int flags) {
        uint16_t port = 0;
        const char *keys[2] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
        char *values[2] = {NULL, NULL};
-       bool response;
 
-       if(parse_packet(buf, len, &name, mesh->appname, "tcp", &port, 2, keys, values, &response)) {
+       if(parse_response(buf, len, &name, mesh->appname, "tcp", &port, 2, keys, values)) {
                node_t *n = (node_t *)meshlink_get_node(mesh, values[0]);
 
                if(n) {
@@ -258,15 +260,6 @@ static void mdns_io_handler(event_loop_t *loop, void *data, int flags) {
                                logger(mesh, MESHLINK_INFO, "Node %s discovered on the local network.\n", n->name);
                        }
 
-                       if(!response && n != mesh->self) {
-                               // Send a unicast response back
-                               char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
-                               const char *response_values[] = {mesh->name, fingerprint};
-                               size_t size = prepare_packet(buf, sizeof(buf), fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, response_values, true);
-                               sendto(io->fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL, &sa.sa, sl);
-                               free(fingerprint);
-                       }
-
                        switch(sa.sa.sa_family) {
                        case AF_INET:
                                sa.in.sin_port = htons(port);
@@ -306,6 +299,13 @@ static void mdns_io_handler(event_loop_t *loop, void *data, int flags) {
                                }
                        }
                }
+       } else if(parse_request(buf, len, mesh->appname, "tcp")) {
+               // Send a unicast response back
+               char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
+               const char *response_values[] = {mesh->name, fingerprint};
+               size_t size = prepare_response(buf, sizeof(buf), fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, response_values);
+               sendto(io->fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL, &sa.sa, sl);
+               free(fingerprint);
        }
 
        free(name);
index 79de309f8805f12442671fa469ea50b33ede58b3..e92a83346822fa03d38fa97a456b13be45a2df39 100644 (file)
@@ -247,63 +247,119 @@ static void buf_check_len_end(cbuf_t *buf, const uint8_t *ptr) {
        }
 }
 
-size_t prepare_packet(void *vdata, size_t size, const char *name, const char *protocol, const char *transport, uint16_t port, int nkeys, const char **keys, const char **values, bool response) {
-       // Create the request/response packet right now
+size_t prepare_request(void *vdata, size_t size, const char *protocol, const char *transport) {
        uint8_t *data = vdata;
        buf_t buf = {data, size};
 
        // Header
        buf_add_uint16(&buf, 0); // TX ID
-       buf_add_uint16(&buf, response ? ntohs(1 << 15) : 0); // flags
+       buf_add_uint16(&buf, 0); // flags
        buf_add_uint16(&buf, 1); // 1 question
-       buf_add_uint16(&buf, 1); // 1 answer RR
+       buf_add_uint16(&buf, 0); // 0 answer RR
        buf_add_uint16(&buf, 0); // 0 authority RRs
-       buf_add_uint16(&buf, 2); // 1 additional RR
+       buf_add_uint16(&buf, 0); // 0 additional RR
 
        // Question section: _protocol._transport.local PTR IN
        buf_add_ulabel(&buf, protocol);
        buf_add_ulabel(&buf, transport);
-       uint16_t local = buf.ptr - data;
        buf_add_label(&buf, "local");
        buf_add_uint8(&buf, 0);
        buf_add_uint16(&buf, 0xc); // PTR
        buf_add_uint16(&buf, 0x1); // IN
 
-       // Answer section: _protocol._transport local PTR IN 3600 name._protocol._transport
-       buf_add_uint16(&buf, 0xc00c); // _protocol._transport.local
-       buf_add_uint16(&buf, 0xc); // PTR
+       // Done.
+       if(buf.len < 0) {
+               return 0;
+       } else {
+               return buf.ptr - data;
+       }
+}
+
+bool parse_request(const void *vdata, size_t size, const char *protocol, const char *transport) {
+       const uint8_t *data = vdata;
+       cbuf_t buf = {data, size};
+
+       // Header
+       buf_get_uint16(&buf); // TX ID
+       buf_check_uint16(&buf, 0); // flags
+       buf_check_uint16(&buf, 1); // 1 question
+       buf_get_uint16(&buf); // ? answer RR
+       buf_get_uint16(&buf); // ? authority RRs
+       buf_get_uint16(&buf); // ? additional RR
+
+       if(buf.len == -1) {
+               return false;
+       }
+
+       // Question section: _protocol._transport.local PTR IN
+       buf_check_ulabel(&buf, protocol);
+       buf_check_ulabel(&buf, transport);
+       buf_check_label(&buf, "local");
+       buf_check_uint8(&buf, 0);
+       buf_check_uint16(&buf, 0xc); // PTR
+       buf_check_uint16(&buf, 0x1); // IN
+
+       if(buf.len == -1) {
+               return false;
+       }
+
+       // Done.
+       return buf.len != -1;
+}
+
+size_t prepare_response(void *vdata, size_t size, const char *name, const char *protocol, const char *transport, uint16_t port, int nkeys, const char **keys, const char **values) {
+       uint8_t *data = vdata;
+       buf_t buf = {data, size};
+
+       // Header
+       buf_add_uint16(&buf, 0); // TX ID
+       buf_add_uint16(&buf, 0x8400); // flags
+       buf_add_uint16(&buf, 0); // 1 question
+       buf_add_uint16(&buf, 3); // 1 answer RR
+       buf_add_uint16(&buf, 0); // 0 authority RRs
+       buf_add_uint16(&buf, 0); // 1 additional RR
+
+       // Add the TXT record: _protocol._transport local TXT IN 3600 name._protocol._transport key=value...
+       uint16_t full_name = buf.ptr - data; // remember start of full name
+       buf_add_label(&buf, name);
+       uint16_t protocol_offset = buf.ptr - data; // remember start of _protocol
+       buf_add_ulabel(&buf, protocol);
+       buf_add_ulabel(&buf, transport);
+       uint16_t local_offset = buf.ptr - data; // remember start of local
+       buf_add_label(&buf, "local");
+       buf_add_uint8(&buf, 0);
+       buf_add_uint16(&buf, 0x10); // TXT
        buf_add_uint16(&buf, 0x1); // IN
        buf_add_uint32(&buf, 3600); // TTL
+
        uint8_t *len_ptr = buf_len_start(&buf);
-       uint16_t full_name = buf.ptr - data; // remember start of full name
-       buf_add_label(&buf, name);
-       buf_add_uint16(&buf, 0xc00c); // _protocol._transport.local
+
+       for(int i = 0; i < nkeys; i++) {
+               buf_add_kvp(&buf, keys[i], values[i]);
+       }
+
+       buf_len_end(&buf, len_ptr);
+
+       // Add the PTR record: _protocol._transport.local PTR IN 3600 name._protocol._transport.local
+       buf_add_uint16(&buf, 0xc000 | protocol_offset);
+       buf_add_uint16(&buf, 0xc); // PTR
+       buf_add_uint16(&buf, 0x8001); // IN (flush)
+       buf_add_uint32(&buf, 3600); // TTL
+       len_ptr = buf_len_start(&buf);
+       buf_add_uint16(&buf, 0xc000 | full_name);
        buf_len_end(&buf, len_ptr);
 
        // Add the SRV record: name._protocol._transport.local SRV IN 120 0 0 port name.local
        buf_add_uint16(&buf, 0xc000 | full_name);
        buf_add_uint16(&buf, 0x21); // SRV
-       buf_add_uint16(&buf, 0x1); // IN
+       buf_add_uint16(&buf, 0x8001); // IN (flush)
        buf_add_uint32(&buf, 120); // TTL
        len_ptr = buf_len_start(&buf);
        buf_add_uint16(&buf, 0); // priority
        buf_add_uint16(&buf, 0); // weight
        buf_add_uint16(&buf, port); // port
        buf_add_label(&buf, name);
-       buf_add_uint16(&buf, 0xc000 | local);
-       buf_len_end(&buf, len_ptr);
-
-       // Add the TXT records: name._protocol._transport.local TXT IN 3600 key=value...
-       buf_add_uint16(&buf, 0xc000 | full_name);
-       buf_add_uint16(&buf, 0x10); // TXT
-       buf_add_uint16(&buf, 0x1); // IN
-       buf_add_uint32(&buf, 3600); // TTL
-       len_ptr = buf_len_start(&buf);
-
-       for(int i = 0; i < nkeys; i++) {
-               buf_add_kvp(&buf, keys[i], values[i]);
-       }
-
+       buf_add_uint16(&buf, 0xc000 | local_offset);
        buf_len_end(&buf, len_ptr);
 
        // Done.
@@ -314,46 +370,53 @@ size_t prepare_packet(void *vdata, size_t size, const char *name, const char *pr
        }
 }
 
-bool parse_packet(const void *vdata, size_t size, char **name, const char *protocol, const char *transport, uint16_t *port, int nkeys, const char **keys, char **values, bool *response) {
+bool parse_response(const void *vdata, size_t size, char **name, const char *protocol, const char *transport, uint16_t *port, int nkeys, const char **keys, char **values) {
        const uint8_t *data = vdata;
        cbuf_t buf = {data, size};
 
        // Header
        buf_check_uint16(&buf, 0); // TX ID
-       uint16_t flags = buf_get_uint16(&buf); // flags
-       buf_check_uint16(&buf, 1); // 1 question
-       buf_check_uint16(&buf, 1); // 1 answer RR
+       buf_check_uint16(&buf, 0x8400); // flags
+       buf_check_uint16(&buf, 0); // 0 question
+       buf_check_uint16(&buf, 3); // 1 answer RR
        buf_check_uint16(&buf, 0); // 0 authority RRs
-       buf_check_uint16(&buf, 2); // 1 checkitional RR
+       buf_check_uint16(&buf, 0); // 0 checkitional RR
 
-       if(buf.len == -1 || flags & ~ntohs(1 << 15)) {
+       if(buf.len == -1) {
                return false;
        }
 
-       *response = flags & ntohs(1 << 15);
-
-       // Question section: _protocol._transport.local PTR IN
+       // Check the TXT record: _protocol._transport local TXT IN 3600 name._protocol._transport key=value...
+       uint16_t full_name = buf.ptr - data; // remember start of full name
+       *name = buf_get_label(&buf);
+       uint16_t protocol_offset = buf.ptr - data; // remember start of _protocol
        buf_check_ulabel(&buf, protocol);
        buf_check_ulabel(&buf, transport);
-       uint16_t local = buf.ptr - data;
+       uint16_t local_offset = buf.ptr - data; // remember start of local
        buf_check_label(&buf, "local");
        buf_check_uint8(&buf, 0);
-       buf_check_uint16(&buf, 0xc); // PTR
+       buf_check_uint16(&buf, 0x10); // TXT
        buf_check_uint16(&buf, 0x1); // IN
+       buf_check_uint32(&buf, 3600); // TTL
+       const uint8_t *len_ptr = buf_check_len_start(&buf);
+
+       for(int i = 0; i < nkeys; i++) {
+               buf_get_kvp(&buf, keys[i], &values[i]);
+       }
+
+       buf_check_len_end(&buf, len_ptr);
 
        if(buf.len == -1) {
                return false;
        }
 
-       // Answer section: _protocol._transport local PTR IN 3600 name._protocol._transport
-       buf_check_uint16(&buf, 0xc00c); // _protocol._transport.local
+       // Check the PTR record: _protocol._transport.local PTR IN 3600 name._protocol._transport.local
+       buf_check_uint16(&buf, 0xc000 | protocol_offset);
        buf_check_uint16(&buf, 0xc); // PTR
-       buf_check_uint16(&buf, 0x1); // IN
+       buf_check_uint16(&buf, 0x8001); // IN (flush)
        buf_check_uint32(&buf, 3600); // TTL
-       const uint8_t *len_ptr = buf_check_len_start(&buf);
-       uint16_t full_name = buf.ptr - data; // remember start of full name
-       *name = buf_get_label(&buf);
-       buf_check_uint16(&buf, 0xc00c); // _protocol._transport.local
+       len_ptr = buf_check_len_start(&buf);
+       buf_check_uint16(&buf, 0xc000 | full_name);
        buf_check_len_end(&buf, len_ptr);
 
        if(buf.len == -1) {
@@ -363,31 +426,14 @@ bool parse_packet(const void *vdata, size_t size, char **name, const char *proto
        // Check the SRV record: name._protocol._transport.local SRV IN 120 0 0 port name.local
        buf_check_uint16(&buf, 0xc000 | full_name);
        buf_check_uint16(&buf, 0x21); // SRV
-       buf_check_uint16(&buf, 0x1); // IN
+       buf_check_uint16(&buf, 0x8001); // IN (flush)
        buf_check_uint32(&buf, 120); // TTL
        len_ptr = buf_check_len_start(&buf);
        buf_check_uint16(&buf, 0); // priority
        buf_check_uint16(&buf, 0); // weight
        *port = buf_get_uint16(&buf); // port
        buf_check_label(&buf, *name);
-       buf_check_uint16(&buf, 0xc000 | local);
-       buf_check_len_end(&buf, len_ptr);
-
-       if(buf.len == -1) {
-               return false;
-       }
-
-       // Check the TXT records: name._protocol._transport.local TXT IN 3600 key=value...
-       buf_check_uint16(&buf, 0xc000 | full_name);
-       buf_check_uint16(&buf, 0x10); // TXT
-       buf_check_uint16(&buf, 0x1); // IN
-       buf_check_uint32(&buf, 3600); // TTL
-       len_ptr = buf_check_len_start(&buf);
-
-       for(int i = 0; i < nkeys; i++) {
-               buf_get_kvp(&buf, keys[i], &values[i]);
-       }
-
+       buf_check_uint16(&buf, 0xc000 | local_offset);
        buf_check_len_end(&buf, len_ptr);
 
        // Done.
index 1bd8451da0c1dc5940a1bb40ffca0fe2d1f51a8c..6029158e52c771407e9469b8e0af3f45c688feac 100644 (file)
@@ -7,5 +7,8 @@
 #include <unistd.h>
 
 size_t prepare_packet(void *buf, size_t size, const char *name, const char *protocol, const char *transport, uint16_t port, int nkeys, const char **keys, const char **values, bool response);
-bool parse_packet(const void *buf, size_t size, char **name, const char *protocol, const char *transport, uint16_t *port, int nkeys, const char **keys, char **values, bool *response);
+size_t prepare_request(void *buf, size_t size, const char *protocol, const char *transport);
+size_t prepare_response(void *buf, size_t size, const char *name, const char *protocol, const char *transport, uint16_t port, int nkeys, const char **keys, const char **values);
+bool parse_response(const void *buf, size_t size, char **name, const char *protocol, const char *transport, uint16_t *port, int nkeys, const char **keys, char **values);
+bool parse_request(const void *buf, size_t size, const char *protocol, const char *transport);