From 73edcb89a3f29e6ff929b8471d1f6cac45071c89 Mon Sep 17 00:00:00 2001
From: ghzhang <zhangguohong@lnxall.com>
Date: Mon, 28 Aug 2023 10:35:21 +0800
Subject: [PATCH] 1234

---
 deps/mdnsd/libmdnsd/.gitignore                |    3 +
 deps/mdnsd/libmdnsd/1035.c                    |  654 +++++++++
 deps/mdnsd/libmdnsd/1035.h                    |  194 +++
 deps/mdnsd/libmdnsd/CMakeLists.txt            |  120 ++
 deps/mdnsd/libmdnsd/mdnsd.c                   | 1304 +++++++++++++++++
 deps/mdnsd/libmdnsd/mdnsd.h                   |  235 +++
 deps/mdnsd/libmdnsd/mdnsd_config_extra.in     |   27 +
 .../libmdnsd/mdnsd_debug_dump_pkgs_file.c     |   43 +
 deps/mdnsd/libmdnsd/ms_stdint.h               |  257 ++++
 deps/mdnsd/libmdnsd/sdtxt.c                   |   93 ++
 deps/mdnsd/libmdnsd/sdtxt.h                   |   24 +
 deps/mdnsd/libmdnsd/xht.c                     |  182 +++
 deps/mdnsd/libmdnsd/xht.h                     |   53 +
 13 files changed, 3189 insertions(+)
 create mode 100644 deps/mdnsd/libmdnsd/.gitignore
 create mode 100644 deps/mdnsd/libmdnsd/1035.c
 create mode 100644 deps/mdnsd/libmdnsd/1035.h
 create mode 100644 deps/mdnsd/libmdnsd/CMakeLists.txt
 create mode 100644 deps/mdnsd/libmdnsd/mdnsd.c
 create mode 100644 deps/mdnsd/libmdnsd/mdnsd.h
 create mode 100644 deps/mdnsd/libmdnsd/mdnsd_config_extra.in
 create mode 100644 deps/mdnsd/libmdnsd/mdnsd_debug_dump_pkgs_file.c
 create mode 100644 deps/mdnsd/libmdnsd/ms_stdint.h
 create mode 100644 deps/mdnsd/libmdnsd/sdtxt.c
 create mode 100644 deps/mdnsd/libmdnsd/sdtxt.h
 create mode 100644 deps/mdnsd/libmdnsd/xht.c
 create mode 100644 deps/mdnsd/libmdnsd/xht.h

diff --git a/deps/mdnsd/libmdnsd/.gitignore b/deps/mdnsd/libmdnsd/.gitignore
new file mode 100644
index 0000000..61f17f7
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.o
+libmdnsd*
\ No newline at end of file
diff --git a/deps/mdnsd/libmdnsd/1035.c b/deps/mdnsd/libmdnsd/1035.c
new file mode 100644
index 0000000..214720b
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/1035.c
@@ -0,0 +1,654 @@
+#include "1035.h"
+#include <string.h>
+#include <stdio.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+
+__inline int msnds_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
+{
+    int count = -1;
+
+    if (size != 0)
+        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+    if (count == -1)
+        count = _vscprintf(format, ap);
+
+    return count;
+}
+
+__inline int msnds_snprintf(char *outBuf, size_t size, const char *format, ...)
+{
+    int count;
+    va_list ap;
+
+    va_start(ap, format);
+    count = msnds_vsnprintf(outBuf, size, format, ap);
+    va_end(ap);
+
+    return count;
+}
+
+#else
+
+#define msnds_snprintf snprintf
+
+#endif
+
+uint16_t net2short(const unsigned char **bufp)
+{
+	unsigned short int i;
+	memcpy(&i, *bufp, sizeof(short int));
+	*bufp += 2;
+	return ntohs(i);
+}
+
+uint32_t net2long(const unsigned char **bufp)
+{
+	uint32_t l;
+
+    memcpy(&l, *bufp, sizeof(uint32_t));
+	*bufp += 4;
+
+	return ntohl(l);
+}
+
+void short2net(uint16_t i, unsigned char **bufp)
+{
+    uint16_t x = htons(i);
+    memcpy(*bufp, &x, sizeof(uint16_t));
+	*bufp += 2;
+}
+
+void long2net(uint32_t l, unsigned char **bufp)
+{
+    uint32_t x = htonl(l);
+    memcpy(*bufp, &x, sizeof(uint32_t));
+	*bufp += 4;
+}
+
+static unsigned short int _ldecomp(const unsigned char *ptr)
+{
+	unsigned short int i;
+
+	i = (unsigned short int)(0xc0 ^ ptr[0]);
+	i = (unsigned short int)(i<<8);
+	i = (unsigned short int)(i | ptr[1]);
+
+	return i;
+}
+
+static bool _label(struct message *m, const unsigned char **bufp, const unsigned char *bufEnd, char **namep)
+{
+	int x;
+	const unsigned char *label;
+	char *name;
+
+	/* Set namep to the end of the block */
+	*namep = name = (char *)m->_packet + m->_len;
+
+	if (*bufp >= bufEnd)
+	    return false;
+
+    // forward buffer pointer until we find the first compressed label
+    bool moveBufp = true;
+	/* Loop storing label in the block */
+	do {
+	    if (moveBufp) {
+            label = *bufp;
+	    }
+
+        if (label >= bufEnd) {
+            break;
+        }
+
+        /* Since every domain name ends with the null label of
+            the root, a domain name is terminated by a length byte of zero. */
+        if (*label == 0) {
+            if (moveBufp) {
+                *bufp += 1;
+            }
+            break;
+        }
+
+
+		/* Skip past any compression pointers, kick out if end encountered (bad data prolly) */
+
+		/* If a label is compressed, it has following structure of 2 bytes:
+		 *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    	 *    | 1  1|                OFFSET                   |
+		 *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+		 *
+		 * The OFFSET field specifies an offset from
+		 * the start of the message (i.e., the first octet of the ID field in the
+		 * domain header).  A zero offset specifies the first byte of the ID field,
+		 * etc.
+		 **/
+		if (*label & 0xc0) {
+		    if (label + 2 > bufEnd)
+		        return false;
+            unsigned short int offset = _ldecomp(label);
+            if (offset > m->_len)
+                return false;
+            if (m->_buf + offset >= bufEnd)
+                return false;
+            label = m->_buf + offset;
+            // chek if label is again pointer, then abort.
+            if (*label & 0xc0)
+                return false;
+            moveBufp = false;
+            *bufp += 2;
+		}
+
+
+		/* Make sure we're not over the limits
+		 * See https://tools.ietf.org/html/rfc1035
+		 * 2.3.4. Size limits
+		 * */
+		const unsigned char labelLen = (unsigned char)*label;
+		if (labelLen > 63)
+		    // maximum label length is 63 octets
+		    return false;
+		long nameLen = (name + labelLen) - *namep;
+		if (nameLen > 255)
+            // maximum names length is 255 octets
+		    return false;
+
+		if (label + 1 + labelLen > bufEnd) {
+            return false;
+        }
+        if ((unsigned char*)name + labelLen > m->_packet + MAX_PACKET_LEN) {
+            return false;
+        }
+		/* Copy chars for this label */
+		memcpy(name, label + 1, labelLen);
+		name[labelLen] = '.';
+
+        name += labelLen + 1;
+
+        if (moveBufp) {
+            *bufp += labelLen + 1;
+        }
+        else {
+            label += labelLen +1;
+        }
+
+	} while (*bufp <= bufEnd);
+
+    if ((unsigned char*)name >= m->_packet + MAX_PACKET_LEN) {
+        return false;
+    }
+
+	/* Terminate name and check for cache or cache it */
+	*name = '\0';
+	for (x = 0; x < MAX_NUM_LABELS && m->_labels[x]; x++) {
+		if (strcmp(*namep, m->_labels[x]))
+			continue;
+
+		*namep = m->_labels[x];
+		return true;
+	}
+
+	/* No cache, so cache it if room */
+	if (x < MAX_NUM_LABELS && m->_labels[x] == 0) {
+        m->_labels[x] = *namep;
+    }
+    m->_len += (unsigned long)((name - *namep) + 1);
+
+	return true;
+}
+
+/* Internal label matching */
+static int _lmatch(struct message *m, const char *l1, const char *l2)
+{
+	int len;
+
+	/* Always ensure we get called w/o a pointer */
+	if (*l1 & 0xc0)
+		return _lmatch(m, (char*)m->_buf + _ldecomp((const unsigned char*)l1), l2);
+	if (*l2 & 0xc0)
+		return _lmatch(m, l1, (char*)m->_buf + _ldecomp((const unsigned char*) l2));
+
+	/* Same already? */
+	if (l1 == l2)
+		return 1;
+
+	/* Compare all label characters */
+	if (*l1 != *l2)
+		return 0;
+	for (len = 1; len <= *l1; len++) {
+		if (l1[len] != l2[len])
+			return 0;
+	}
+
+	/* Get new labels */
+	l1 += *l1 + 1;
+	l2 += *l2 + 1;
+
+	/* At the end, all matched */
+	if (*l1 == 0 && *l2 == 0)
+		return 1;
+
+	/* Try next labels */
+	return _lmatch(m, l1, l2);
+}
+
+/* Nasty, convert host into label using compression */
+static int _host(struct message *m, unsigned char **bufp, char *name)
+{
+	char label[256], *l;
+	int len = 0, x = 1, y = 0, last = 0;
+
+	if (name == 0)
+		return 0;
+
+	/* Make our label */
+	while (name[y]) {
+		if (name[y] == '.') {
+			if (!name[y + 1])
+				break;
+			label[last] = (char)(x - (last + 1));
+			last = x;
+		} else {
+			label[x] = name[y];
+		}
+
+		if (x++ == 255)
+			return 0;
+
+		y++;
+	}
+
+	label[last] = (char)(x - (last + 1));
+	if (x == 1)
+		x--;		/* Special case, bad names, but handle correctly */
+	len = x + 1;
+	label[x] = 0;		/* Always terminate w/ a 0 */
+
+	/* Double-loop checking each label against all m->_labels for match */
+	for (x = 0; label[x]; x += label[x] + 1) {
+		for (y = 0; y < MAX_NUM_LABELS && m->_labels[y]; y++) {
+			if (_lmatch(m, label + x, m->_labels[y])) {
+				/* Matching label, set up pointer */
+				l = label + x;
+				short2net((unsigned short)((unsigned char *)m->_labels[y] - m->_packet), (unsigned char **)&l);
+				label[x] = (char)(label[x] | 0xc0);
+				len = x + 2;
+				break;
+			}
+		}
+	
+		if (label[x] & 0xc0)
+			break;
+	}
+
+	/* Copy into buffer, point there now */
+	memcpy(*bufp, label, (size_t)len);
+	l = (char *)*bufp;
+	*bufp += len;
+
+	/* For each new label, store it's location for future compression */
+	for (x = 0; l[x] && m->_label < MAX_NUM_LABELS; x += l[x] + 1) {
+		if (l[x] & 0xc0)
+			break;
+
+		m->_labels[m->_label++] = l + x;
+	}
+
+	return len;
+}
+
+static bool _rrparse(struct message *m, struct resource *rr, int count, const unsigned char **bufp, const unsigned char* bufferEnd)
+{
+	int i;
+    const unsigned char *addr_bytes = NULL;
+
+    if (count == 0) {
+        return true;
+    }
+
+    if (*bufp >= m->_bufEnd) {
+        return false;
+    }
+
+	for (i = 0; i < count; i++) {
+	    if (*bufp >= bufferEnd) {
+            return false;
+        }
+
+		if (!_label(m, bufp, bufferEnd, &(rr[i].name))) {
+            return false;
+        }
+		if (*bufp + 10 > bufferEnd) {
+            return false;
+        }
+		rr[i].type     = net2short(bufp);
+		rr[i].clazz    = net2short(bufp);
+		rr[i].ttl      = net2long(bufp);
+		rr[i].rdlength = net2short(bufp);
+//		fprintf(stderr, "Record type %d class 0x%2x ttl %lu len %d\n", rr[i].type, rr[i].clazz, rr[i].ttl, rr[i].rdlength);
+
+		/* For the following records the rdata will be parsed later. So don't set it here:
+		 * NS, CNAME, PTR, DNAME, SOA, MX, AFSDB, RT, KX, RP, PX, SRV, NSEC
+		 * See 18.14 of https://tools.ietf.org/html/rfc6762#page-47 */
+		if (rr[i].type == QTYPE_NS || rr[i].type == QTYPE_CNAME || rr[i].type == QTYPE_PTR || rr[i].type == QTYPE_SRV) {
+			rr[i].rdlength = 0;
+		} else {
+            /* If not going to overflow, make copy of source rdata */
+            if (*bufp + rr[i].rdlength > bufferEnd) {
+                return false;
+            }
+
+            if (m->_len + rr[i].rdlength > MAX_PACKET_LEN) {
+                return false;
+            }
+			rr[i].rdata = m->_packet + m->_len;
+			m->_len += rr[i].rdlength;
+			memcpy(rr[i].rdata, *bufp, rr[i].rdlength);
+		}
+
+
+		/* Parse commonly known ones */
+		switch (rr[i].type) {
+		case QTYPE_A:
+			if (m->_len + 16 > MAX_PACKET_LEN) {
+                return false;
+            }
+			rr[i].known.a.name = (char *)m->_packet + m->_len;
+			m->_len += 16;
+            if (*bufp + 4 > bufferEnd) {
+                return false;
+            }
+			msnds_snprintf(rr[i].known.a.name,16, "%d.%d.%d.%d", (*bufp)[0], (*bufp)[1], (*bufp)[2], (*bufp)[3]);
+			addr_bytes = (const unsigned char *) *bufp;
+			rr[i].known.a.ip.s_addr = (in_addr_t) (
+					((in_addr_t) addr_bytes[0]) |
+					(((in_addr_t) addr_bytes[1]) << 8) |
+					(((in_addr_t) addr_bytes[2]) << 16) |
+					(((in_addr_t) addr_bytes[3]) << 24));
+            *bufp += 4;
+			break;
+
+		case QTYPE_AAAA:
+			if (m->_len + INET6_ADDRSTRLEN > MAX_PACKET_LEN) {
+				return false;
+			}
+			rr[i].known.aaaa.name = (char *)m->_packet + m->_len;
+			m->_len += INET6_ADDRSTRLEN;
+			if (*bufp + rr->rdlength > bufferEnd) {
+				return false;
+			}
+			addr_bytes = (const unsigned char *)*bufp;
+			inet_ntop(AF_INET6,addr_bytes,rr[i].known.aaaa.name,INET6_ADDRSTRLEN);
+			for (size_t j = 0; j < rr->rdlength; j++) {
+				rr[i].known.aaaa.ip6.s6_addr[j] = addr_bytes[j];
+			}
+			*bufp += rr->rdlength;
+			break;
+
+		case QTYPE_NS:
+			if (!_label(m, bufp, bufferEnd, &(rr[i].known.ns.name))) {
+                return false;
+            }
+			break;
+
+		case QTYPE_CNAME:
+			if (!_label(m, bufp, bufferEnd, &(rr[i].known.cname.name))) {
+                return false;
+            }
+			break;
+
+		case QTYPE_PTR:
+			if (!_label(m, bufp, bufferEnd, &(rr[i].known.ptr.name))) {
+                return false;
+            }
+			break;
+
+		case QTYPE_SRV:
+            if (*bufp + 6 > bufferEnd) {
+                return false;
+            }
+			rr[i].known.srv.priority = net2short(bufp);
+			rr[i].known.srv.weight = net2short(bufp);
+			rr[i].known.srv.port = net2short(bufp);
+			if (!_label(m, bufp, bufferEnd, &(rr[i].known.srv.name))) {
+                return false;
+            }
+			break;
+
+		case QTYPE_TXT:
+		default:
+			*bufp += rr[i].rdlength;
+		}
+	}
+
+	return true;
+}
+
+/* Keep all our mem in one (aligned) block for easy freeing */
+#define my(x,y, cast)				\
+	while (m->_len & 7)			\
+		m->_len++;			\
+		                        \
+    if (m->_len + y > MAX_PACKET_LEN) { return false; } \
+	x = (cast)(void *)(m->_packet + m->_len);	\
+	m->_len += y;
+
+bool message_parse(struct message *m, unsigned char *packet, size_t packetLen)
+{
+	int i;
+	const unsigned char *buf;
+	m->_bufEnd = packet + packetLen;
+
+	/* Message format: https://tools.ietf.org/html/rfc1035
+
+	+---------------------+
+    |        Header       |
+    +---------------------+
+    |       Question      | the question for the name server
+    +---------------------+
+    |        Answer       | RRs answering the question
+    +---------------------+
+    |      Authority      | RRs pointing toward an authority
+    +---------------------+
+    |      Additional     | RRs holding additional information
+    +---------------------+
+    */
+
+	if (packet == 0 || m == 0)
+		return false;
+
+	/* See https://tools.ietf.org/html/rfc1035
+	                                1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      ID                       |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    QDCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ANCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    NSCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ARCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+	 */
+
+	/* The header always needs to be present. Size = 12 byte */
+	if (packetLen < 12)
+	    return false;
+
+	/* Header stuff bit crap */
+    buf = m->_buf = packet;
+	m->id = net2short(&buf);
+	if (buf[0] & 0x80)
+		m->header.qr = 1;
+	m->header.opcode = (unsigned short)(((buf[0] & 0x78) >> 3) & 15);
+	if (buf[0] & 0x04)
+		m->header.aa = 1;
+	if (buf[0] & 0x02)
+		m->header.tc = 1;
+	if (buf[0] & 0x01)
+		m->header.rd = 1;
+	if (buf[1] & 0x80)
+		m->header.ra = 1;
+	m->header.z = (unsigned short)(((buf[1] & 0x70) >> 4) & 7);
+	m->header.rcode = (unsigned short)(buf[1] & 0x0F);
+	buf += 2;
+
+	m->qdcount = net2short(&buf);
+	m->ancount = net2short(&buf);
+    m->nscount = net2short(&buf);
+    m->arcount = net2short(&buf);
+
+    // check if the message has the correct size, i.e. the count matches the number of bytes
+
+	/* Process questions */
+	my(m->qd, (sizeof(struct question) * m->qdcount), struct question *);
+	for (i = 0; i < m->qdcount; i++) {
+		if (!_label(m, &buf, m->_bufEnd, &(m->qd[i].name))) {
+            return false;
+        }
+		if (buf + 4 > m->_bufEnd) {
+            return false;
+        }
+		m->qd[i].type  = net2short(&buf);
+		m->qd[i].clazz = net2short(&buf);
+	}
+    if (buf > m->_bufEnd) {
+        return false;
+    }
+
+	/* Process rrs */
+	my(m->an, (sizeof(struct resource) * m->ancount), struct resource *);
+	my(m->ns, (sizeof(struct resource) * m->nscount), struct resource *);
+	my(m->ar, (sizeof(struct resource) * m->arcount), struct resource *);
+	if (!_rrparse(m, m->an, m->ancount, &buf, m->_bufEnd))
+		return false;
+	if (!_rrparse(m, m->ns, m->nscount, &buf, m->_bufEnd))
+		return false;
+	if (!_rrparse(m, m->ar, m->arcount, &buf, m->_bufEnd))
+		return false;
+	return true;
+}
+
+void message_qd(struct message *m, char *name, unsigned short int type, unsigned short int clazz)
+{
+	m->qdcount++;
+    if (m->_buf == 0) {
+        m->_buf = m->_packet + 12;
+        m->_bufEnd = m->_packet + sizeof(m->_packet);
+    }
+	_host(m, &(m->_buf), name);
+	short2net(type, &(m->_buf));
+	short2net(clazz, &(m->_buf));
+}
+
+static void _rrappend(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl)
+{
+	if (m->_buf == 0) {
+        m->_buf = m->_packet + 12;
+        m->_bufEnd = m->_packet + sizeof(m->_packet);
+    }
+	_host(m, &(m->_buf), name);
+	short2net(type, &(m->_buf));
+	short2net(clazz, &(m->_buf));
+	long2net((uint32_t)ttl, &(m->_buf));
+}
+
+void message_an(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl)
+{
+	m->ancount++;
+	_rrappend(m, name, type, clazz, ttl);
+}
+
+void message_ns(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl)
+{
+	m->nscount++;
+	_rrappend(m, name, type, clazz, ttl);
+}
+
+void message_ar(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl)
+{
+	m->arcount++;
+	_rrappend(m, name, type, clazz, ttl);
+}
+
+void message_rdata_long(struct message *m, struct in_addr l)
+{
+	short2net(4, &(m->_buf));
+	long2net(l.s_addr, &(m->_buf));
+}
+
+void message_rdata_name(struct message *m, char *name)
+{
+	unsigned char *mybuf = m->_buf;
+
+	m->_buf += 2;
+	short2net((unsigned short)_host(m, &(m->_buf), name), &mybuf);
+}
+
+void message_rdata_srv(struct message *m, unsigned short int priority, unsigned short int weight, unsigned short int port, char *name)
+{
+	unsigned char *mybuf = m->_buf;
+
+	m->_buf += 2;
+	short2net(priority, &(m->_buf));
+	short2net(weight, &(m->_buf));
+	short2net(port, &(m->_buf));
+	short2net((unsigned short)(_host(m, &(m->_buf), name) + 6), &mybuf);
+}
+
+void message_rdata_raw(struct message *m, unsigned char *rdata, unsigned short int rdlength)
+{
+	if (((unsigned char *)m->_buf - m->_packet) + rdlength > 4096)
+		rdlength = 0;
+	short2net(rdlength, &(m->_buf));
+	memcpy(m->_buf, rdata, rdlength);
+	m->_buf += rdlength;
+}
+
+unsigned char *message_packet(struct message *m)
+{
+	unsigned char c, *buf = m->_buf, *bufEnd = m->_bufEnd;
+
+	m->_buf = m->_packet;
+    m->_bufEnd = m->_packet + sizeof(m->_packet);
+	short2net(m->id, &(m->_buf));
+
+	if (m->header.qr)
+		m->_buf[0] |= 0x80;
+	if ((c = (unsigned char)m->header.opcode))
+		m->_buf[0] |= (unsigned char)(c << 3);
+	if (m->header.aa)
+		m->_buf[0] |= 0x04;
+	if (m->header.tc)
+		m->_buf[0] |= 0x02;
+	if (m->header.rd)
+		m->_buf[0] |= 0x01;
+	if (m->header.ra)
+		m->_buf[1] |= 0x80;
+	if ((c = (unsigned char)m->header.z))
+		m->_buf[1] |= (unsigned char)(c << 4);
+	if (m->header.rcode)
+		m->_buf[1] = (unsigned char)(m->_buf[1] | m->header.rcode);
+
+	m->_buf += 2;
+	short2net(m->qdcount, &(m->_buf));
+	short2net(m->ancount, &(m->_buf));
+	short2net(m->nscount, &(m->_buf));
+	short2net(m->arcount, &(m->_buf));
+	m->_buf = buf;		/* Restore, so packet_len works */
+	m->_bufEnd = bufEnd;
+
+	return m->_packet;
+}
+
+int message_packet_len(struct message *m)
+{
+	if (m->_buf == 0)
+		return 12;
+
+	return (int)((unsigned char *)m->_buf - m->_packet);
+}
diff --git a/deps/mdnsd/libmdnsd/1035.h b/deps/mdnsd/libmdnsd/1035.h
new file mode 100644
index 0000000..33e8d69
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/1035.h
@@ -0,0 +1,194 @@
+/* Familiarize yourself with RFC1035 if you want to know what all the
+ * variable names mean.  This file hides most of the dirty work all of
+ * this code depends on the buffer space a packet is in being 4096 and
+ * zero'd before the packet is copied in also conveniently decodes srv
+ * rr's, type 33, see RFC2782
+ */
+#ifndef MDNS_1035_H_
+#define MDNS_1035_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mdnsd_config.h"
+
+#ifdef _WIN32
+
+/* Backup definition of SLIST_ENTRY on mingw winnt.h */
+#ifdef SLIST_ENTRY
+# pragma push_macro("SLIST_ENTRY")
+# undef SLIST_ENTRY
+# define POP_SLIST_ENTRY
+#endif
+
+/* winnt.h redefines SLIST_ENTRY */
+# define _WINSOCK_DEPRECATED_NO_WARNINGS /* inet_ntoa is deprecated on MSVC but used for compatibility */
+# include <winsock2.h>
+# include <ws2tcpip.h>
+
+#ifndef in_addr_t
+#define in_addr_t unsigned __int32
+#endif
+
+/* restore definition */
+#ifdef POP_SLIST_ENTRY
+# undef SLIST_ENTRY
+# undef POP_SLIST_ENTRY
+# pragma pop_macro("SLIST_ENTRY")
+#endif
+
+
+#else
+# include <arpa/inet.h>
+#endif
+
+#if !defined(__bool_true_false_are_defined) && defined(_MSC_VER) && _MSC_VER < 1600
+// VS 2008 has no stdbool.h
+#define bool	short
+#define true	1
+#define false	0
+#else
+#include <stdbool.h>
+#endif
+
+#ifdef _MSC_VER
+#include "ms_stdint.h" /* Includes stdint.h or workaround for older Visual Studios */
+#else
+#include <stdint.h>
+#endif
+
+/* Should be reasonably large, for UDP */
+#define MAX_PACKET_LEN 10000
+#define MAX_NUM_LABELS 20
+
+struct question {
+	char *name;
+	unsigned short type, clazz;
+};
+
+#define QTYPE_A      1
+#define QTYPE_NS     2
+#define QTYPE_CNAME  5
+#define QTYPE_PTR    12
+#define QTYPE_TXT    16
+#define QTYPE_AAAA   28
+#define QTYPE_SRV    33
+
+struct resource {
+	char *name;
+	unsigned short type, clazz;
+	unsigned long int ttl;
+	unsigned short int rdlength;
+	unsigned char *rdata;
+	union {
+		struct {
+			struct in_addr ip;
+			//cppcheck-suppress unusedStructMember
+			char *name;
+		} a;
+		struct {
+			struct in6_addr ip6;
+			//cppcheck-suppress unusedStructMember
+			char *name;
+		} aaaa;
+		struct {
+			//cppcheck-suppress unusedStructMember
+			char *name;
+		} ns;
+		struct {
+			//cppcheck-suppress unusedStructMember
+			char *name;
+		} cname;
+		struct {
+			//cppcheck-suppress unusedStructMember
+			char *name;
+		} ptr;
+		struct {
+			//cppcheck-suppress unusedStructMember
+			unsigned short int priority, weight, port;
+			//cppcheck-suppress unusedStructMember
+			char *name;
+		} srv;
+	} known;
+};
+
+struct message {
+	/* External data */
+	unsigned short int id;
+	struct {
+		//it would be better to use unsigned short, but gcc < 5 complaints when using pedantic
+		//cppcheck-suppress unusedStructMember
+		unsigned int qr:1, opcode:4, aa:1, tc:1, rd:1, ra:1, z:3, rcode:4;
+	} header;
+	unsigned short int qdcount, ancount, nscount, arcount;
+	struct question *qd;
+	struct resource *an, *ns, *ar;
+
+	/* Internal variables */
+	unsigned char *_buf;
+	unsigned char *_bufEnd;
+	char *_labels[MAX_NUM_LABELS];
+	size_t _len;
+	int _label;
+
+	/* Packet acts as padding, easier mem management */
+	unsigned char _packet[MAX_PACKET_LEN];
+};
+
+/**
+ * Returns the next short/long off the buffer (and advances it)
+ */
+uint16_t net2short(const unsigned char **bufp);
+uint32_t  net2long (const unsigned char **bufp);
+
+/**
+ * copies the short/long into the buffer (and advances it)
+ */
+void MDNSD_EXPORT short2net(uint16_t i, unsigned char **bufp);
+void MDNSD_EXPORT long2net (uint32_t  l, unsigned char **bufp);
+
+/**
+ * parse packet into message, packet must be at least MAX_PACKET_LEN and
+ * message must be zero'd for safety
+ */
+bool MDNSD_EXPORT message_parse(struct message *m, unsigned char *packet, size_t packetLen);
+
+/**
+ * create a message for sending out on the wire
+ */
+struct message *message_wire(void);
+
+/**
+ * append a question to the wire message
+ */
+void MDNSD_EXPORT message_qd(struct message *m, char *name, unsigned short int type, unsigned short int clazz);
+
+/**
+ * append a resource record to the message, all called in order!
+ */
+void MDNSD_EXPORT message_an(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl);
+void MDNSD_EXPORT message_ns(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl);
+void MDNSD_EXPORT message_ar(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl);
+
+/**
+ * Append various special types of resource data blocks
+ */
+void MDNSD_EXPORT message_rdata_long (struct message *m, struct in_addr l);
+void MDNSD_EXPORT message_rdata_name (struct message *m, char *name);
+void MDNSD_EXPORT message_rdata_srv  (struct message *m, unsigned short int priority, unsigned short int weight,
+			 unsigned short int port, char *name);
+void MDNSD_EXPORT message_rdata_raw  (struct message *m, unsigned char *rdata, unsigned short int rdlength);
+
+/**
+ * Return the wire format (and length) of the message, just free message
+ * when done
+ */
+unsigned char MDNSD_EXPORT * message_packet     (struct message *m);
+int   MDNSD_EXPORT           message_packet_len (struct message *m);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif	/* MDNS_1035_H_ */
diff --git a/deps/mdnsd/libmdnsd/CMakeLists.txt b/deps/mdnsd/libmdnsd/CMakeLists.txt
new file mode 100644
index 0000000..16f154e
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/CMakeLists.txt
@@ -0,0 +1,120 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(libmdnsd C)
+
+include(GenerateExportHeader)
+
+set(MDNSD_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")
+
+# Force compilation with as C++
+option(MDNSD_COMPILE_AS_CXX "Force compilation with a C++ compiler" OFF)
+mark_as_advanced(MDNSD_COMPILE_AS_CXX)
+
+option(CMAKE_POSITION_INDEPENDENT_CODE "Build mdnsd using -fPIC" ON)
+
+if(NOT MDNSD_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang"))
+  add_definitions(-std=c99 -pipe
+                  -Wall -Wextra -Werror
+                  -Wno-unused-parameter # some methods may require unused arguments to cast to a method pointer
+                  -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls
+                  -Wformat -Wformat-security -Wformat-nonliteral
+                  -Wuninitialized -Winit-self
+                  -Wcast-qual -Wcast-align
+                  -Wstrict-overflow
+                  -Wnested-externs
+                  -Wmultichar
+                  -Wundef
+                  -Wc++-compat)
+  if(NOT MINGW AND NOT MSYS AND NOT MSVC)
+    add_definitions(-Wpedantic)
+  endif()
+  if(NOT WIN32 AND NOT CYGWIN)
+    add_definitions(-Wshadow -Wconversion)
+  endif()
+endif()
+
+set(CMAKE_C_VISIBILITY_PRESET hidden)
+set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+
+option(MDNSD_ENABLE_COVERAGE "Enable gcov coverage" OFF)
+if(MDNSD_ENABLE_COVERAGE)
+  set(CMAKE_BUILD_TYPE DEBUG)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
+  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
+endif()
+
+# Building shared libs (dll, so)
+set(MDNSD_DYNAMIC_LINKING OFF)
+if(BUILD_SHARED_LIBS)
+  set(MDNSD_DYNAMIC_LINKING ON)
+endif()
+
+configure_file("mdnsd_config_extra.in" "${PROJECT_BINARY_DIR}/mdnsd_config_extra")
+include_directories(${PROJECT_BINARY_DIR})
+
+set(PUBLIC_INCLUDES mdnsd.h 1035.h sdtxt.h xht.h "${PROJECT_BINARY_DIR}/mdnsd_config.h")
+if(CMAKE_C_COMPILER_ID MATCHES MSVC)
+  list(APPEND PUBLIC_INCLUDES ms_stdint.h)
+endif()
+
+set(LIBRARY_FILES mdnsd.c 1035.c xht.c sdtxt.c)
+
+if (MDNSD_BUILD_FUZZING_CORPUS)
+    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+
+    add_definitions(-DMDNSD_CORPUS_OUTPUT_DIR="${PROJECT_BINARY_DIR}/corpus/custom")
+    file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/corpus/custom)
+
+    list(APPEND LIBRARY_FILES mdnsd_debug_dump_pkgs_file.c)
+endif()
+
+if(MDNSD_COMPILE_AS_CXX)
+    set_source_files_properties(${LIBRARY_FILES} PROPERTIES LANGUAGE CXX)
+    enable_language(CXX)
+endif()
+
+add_library(libmdnsd ${LIBRARY_FILES} ${PUBLIC_INCLUDES})
+target_include_directories(libmdnsd PUBLIC
+    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/..>"
+    "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>"
+    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+)
+set_target_properties(libmdnsd PROPERTIES
+    OUTPUT_NAME "mdnsd"
+    DEFINE_SYMBOL "MDNSD_DYNAMIC_LINKING_EXPORT"
+)
+target_compile_definitions(libmdnsd PUBLIC
+    $<$<STREQUAL:$<TARGET_PROPERTY:libmdnsd,TYPE>,STATIC_LIBRARY>:MDNSD_STATIC_DEFINE>
+)
+
+
+file(READ "${PROJECT_BINARY_DIR}/mdnsd_config_extra" MDNSD_CONFIG_EXTRA)
+generate_export_header(libmdnsd
+    EXPORT_FILE_NAME "${PROJECT_BINARY_DIR}/mdnsd_config.h"
+    BASE_NAME MDNSD
+    DEFINE_NO_DEPRECATED
+    CUSTOM_CONTENT_FROM_VARIABLE MDNSD_CONFIG_EXTRA
+)
+
+if(WIN32)
+  target_link_libraries(libmdnsd PUBLIC wsock32 ws2_32)
+endif()
+
+install(
+    TARGETS libmdnsd
+    EXPORT mdnsd
+    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
+
+install(
+    FILES ${PUBLIC_INCLUDES}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}"
+)
+
+install(
+    FILES ${PROJECT_BINARY_DIR}/mdnsd_config.h
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}"
+)
diff --git a/deps/mdnsd/libmdnsd/mdnsd.c b/deps/mdnsd/libmdnsd/mdnsd.c
new file mode 100644
index 0000000..227bb76
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/mdnsd.c
@@ -0,0 +1,1304 @@
+#include "mdnsd_config.h"
+#include "mdnsd.h"
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define SPRIME 108		/* Size of query/publish hashes */
+#define LPRIME 1009		/* Size of cache hash */
+
+#define GC 86400                /* Brute force garbage cleanup
+				 * frequency, rarely needed (daily
+				 * default) */
+
+#ifdef _MSC_VER
+#include "ms_stdint.h" /* Includes stdint.h or workaround for older Visual Studios */
+
+int gettimeofday(struct timeval * tp, struct timezone * tzp)
+{
+	// Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
+	static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);
+
+	SYSTEMTIME  system_time;
+	FILETIME    file_time;
+	uint64_t    time;
+
+	GetSystemTime( &system_time );
+	SystemTimeToFileTime( &system_time, &file_time );
+	time =  ((uint64_t)file_time.dwLowDateTime )      ;
+	time += ((uint64_t)file_time.dwHighDateTime) << 32;
+
+	tp->tv_sec  = (long) ((time - EPOCH) / 10000000L);
+	tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
+	return 0;
+}
+#else
+#include <sys/time.h>
+#endif
+
+#if defined(__MINGW32__)
+static char *my_strdup(const char *s) {
+    char *p = (char *)MDNSD_malloc(strlen(s) + 1);
+    if(p) { strcpy(p, s); }
+    return p;
+}
+#define STRDUP my_strdup
+#elif defined(_WIN32)
+#define STRDUP _strdup
+#else
+#define STRDUP strdup
+#endif
+
+#ifndef _WIN32
+# include <netdb.h>
+#endif
+
+/**
+ * Messy, but it's the best/simplest balance I can find at the moment
+ *
+ * Some internal data types, and a few hashes: querys, answers, cached,
+ * and records (published, unique and shared).  Each type has different
+ * semantics for processing, both for timeouts, incoming, and outgoing
+ * I/O.  They inter-relate too, like records affect the querys they are
+ * relevant to.  Nice things about MDNS: we only publish once (and then
+ * ask asked), and only query once, then just expire records we've got
+ * cached
+*/
+
+struct query {
+	char *name;
+	int type;
+	unsigned long int nexttry;
+	int tries;
+	int (*answer)(mdns_answer_t *, void *);
+	void *arg;
+	struct query *next, *list;
+};
+
+struct unicast {
+	int id;
+	struct sockaddr *to;
+	unsigned short int port;
+	mdns_record_t *r;
+	struct unicast *next;
+};
+
+struct cached {
+	struct mdns_answer rr;
+	struct query *q;
+	struct cached *next;
+};
+
+struct mdns_record {
+	struct mdns_answer rr;
+	char unique;		/* # of checks performed to ensure */
+	int tries;
+	void (*conflict)(char *, int, void *);
+	void *arg;
+	struct timeval last_sent;
+	struct mdns_record *next, *list;
+};
+
+struct mdns_daemon {
+	char shutdown;
+	unsigned long int expireall, checkqlist;
+	struct timeval now, sleep, pause, probe, publish;
+	int clazz, frame;
+	struct cached *cache[LPRIME];
+	struct mdns_record *published[SPRIME], *probing, *a_now, *a_pause, *a_publish;
+	struct unicast *uanswers;
+	struct query *queries[SPRIME], *qlist;
+	mdnsd_record_received_callback received_callback;
+	void *received_callback_data;
+};
+
+static int _namehash(const char *s)
+{
+	const unsigned char *name = (const unsigned char *)s;
+	unsigned long h = 0;
+
+	while (*name) {		/* do some fancy bitwanking on the string */
+		unsigned long int g;
+		h = (h << 4) + (unsigned long int)(*name++);
+		if ((g = (h & 0xF0000000UL)) != 0)
+			h ^= (g >> 24);
+		h &= ~g;
+	}
+
+	return (int)h;
+}
+
+/* Basic linked list and hash primitives */
+static struct query *_q_next(mdns_daemon_t *d, struct query *q, const char *host, int type)
+{
+	if (q == 0)
+		q = d->queries[_namehash(host) % SPRIME];
+	else
+		q = q->next;
+
+	for (; q != 0; q = q->next) {
+		if (q->type == type && strcmp(q->name, host) == 0)
+			return q;
+	}
+
+	return 0;
+}
+
+static struct cached *_c_next(mdns_daemon_t *d, struct cached *c,const char *host, int type)
+{
+	if (c == 0)
+		c = d->cache[_namehash(host) % LPRIME];
+	else
+		c = c->next;
+
+	for (; c != 0; c = c->next) {
+		if ((type == c->rr.type || type == 255) && strcmp(c->rr.name, host) == 0)
+			return c;
+	}
+
+	return 0;
+}
+
+static mdns_record_t *_r_next(mdns_daemon_t *d, mdns_record_t *r, const char *host, int type)
+{
+	if (r == 0)
+		r = d->published[_namehash(host) % SPRIME];
+	else
+		r = r->next;
+
+	for (; r != 0; r = r->next) {
+		if (type == r->rr.type && strcmp(r->rr.name, host) == 0)
+			return r;
+	}
+
+	return 0;
+}
+
+static int _rr_len(mdns_answer_t *rr)
+{
+	int len = 12;		/* name is always compressed (dup of earlier), plus normal stuff */
+
+	if (rr->rdata)
+		len += rr->rdlen;
+	if (rr->rdname)
+		len += (int)strlen(rr->rdname); /* worst case */
+	if (rr->ip.s_addr)
+		len += 4;
+	if (rr->type == QTYPE_PTR)
+		len += 6;	/* srv record stuff */
+
+	return len;
+}
+
+/* Compares new rdata with known a, painfully */
+static int _a_match(struct resource *r, mdns_answer_t *a)
+{
+	if (!a->name)
+		return 0;
+	if (strcmp(r->name, a->name) != 0 || r->type != a->type)
+		return 0;
+
+	if (r->type == QTYPE_SRV && !strcmp(r->known.srv.name, a->rdname) && a->srv.port == r->known.srv.port &&
+		a->srv.weight == r->known.srv.weight && a->srv.priority == r->known.srv.priority)
+		return 1;
+
+	if ((r->type == QTYPE_PTR || r->type == QTYPE_NS || r->type == QTYPE_CNAME) && !strcmp(a->rdname, r->known.ns.name))
+		return 1;
+
+	if (r->rdlength == a->rdlen && r->rdlength == 0)
+	    return 1;
+
+	if ((r->rdlength == a->rdlen) && !memcmp(r->rdata, a->rdata, r->rdlength))
+		return 1;
+
+	return 0;
+}
+
+/* Compare time values easily */
+static int _tvdiff(struct timeval old_time, struct timeval new_time)
+{
+	int udiff = 0;
+
+	if (old_time.tv_sec != new_time.tv_sec)
+		udiff = (int)((new_time.tv_sec - old_time.tv_sec) * 1000000);
+
+	return (int)((new_time.tv_usec - old_time.tv_usec) + udiff);
+}
+
+static void _r_remove_list(mdns_record_t **list, mdns_record_t *r) {
+	if (*list == r) {
+		*list = r->list;
+	} else {
+		mdns_record_t *tmp = *list;
+		while (tmp) {
+			if (tmp->list == r) {
+				tmp->list = r->list;
+				break;
+			}
+			if (tmp == tmp->list)
+				break;
+			tmp = tmp->list;
+		}
+	}
+}
+
+static void _r_remove_lists(mdns_daemon_t *d, mdns_record_t *r, mdns_record_t **skip) {
+	if (d->probing && &d->probing != skip) {
+		_r_remove_list(&d->probing, r);
+	}
+	if (d->a_now && &d->a_now != skip) {
+		_r_remove_list(&d->a_now, r);
+	}
+	if (d->a_pause && &d->a_pause != skip) {
+		_r_remove_list(&d->a_pause, r);
+	}
+	if (d->a_publish && &d->a_publish != skip) {
+		_r_remove_list(&d->a_publish, r);
+	}
+}
+
+/* Make sure not already on the list, then insert */
+static void _r_push(mdns_record_t **list, mdns_record_t *r)
+{
+	mdns_record_t *cur;
+
+	for (cur = *list; cur != 0; cur = cur->list) {
+		if (cur == r)
+			return;
+	}
+
+	r->list = *list;
+	*list = r;
+}
+
+/* Force any r out right away, if valid */
+static void _r_publish(mdns_daemon_t *d, mdns_record_t *r)
+{
+	if (r->unique && r->unique < 5)
+		return;		/* Probing already */
+
+	r->tries = 0;
+	d->publish.tv_sec = d->now.tv_sec;
+	d->publish.tv_usec = d->now.tv_usec;
+	_r_push(&d->a_publish, r);
+}
+
+/* send r out asap */
+static void _r_send(mdns_daemon_t *d, mdns_record_t *r)
+{
+	/* Being published, make sure that happens soon */
+	if (r->tries < 4) {
+		d->publish.tv_sec = d->now.tv_sec;
+		d->publish.tv_usec = d->now.tv_usec;
+		return;
+	}
+
+	/* Known unique ones can be sent asap */
+	if (r->unique) {
+
+		// check if r already in other lists. If yes, remove it from there
+		_r_remove_lists(d,r, &d->a_now);
+		_r_push(&d->a_now, r);
+		return;
+	}
+
+	/* Set d->pause.tv_usec to random 20-120 msec */
+	d->pause.tv_sec = d->now.tv_sec;
+	d->pause.tv_usec = d->now.tv_usec + (d->now.tv_usec % 100) + 20;
+	_r_push(&d->a_pause, r);
+}
+
+/* Create generic unicast response struct */
+static void _u_push(mdns_daemon_t *d, mdns_record_t *r, int id, struct sockaddr *to, unsigned short int port)
+{
+	struct unicast *u;
+
+	u = (struct unicast *)MDNSD_calloc(1, sizeof(struct unicast));
+	u->r = r;
+	u->id = id;
+	u->to = to;
+	u->port = port;
+	u->next = d->uanswers;
+	d->uanswers = u;
+}
+
+static void _q_reset(mdns_daemon_t *d, struct query *q)
+{
+	struct cached *cur = 0;
+
+	q->nexttry = 0;
+	q->tries = 0;
+
+	while ((cur = _c_next(d, cur, q->name, q->type))) {
+		if (q->nexttry == 0 || cur->rr.ttl - 7 < q->nexttry)
+			q->nexttry = cur->rr.ttl - 7;
+	}
+
+	if (q->nexttry != 0 && q->nexttry < d->checkqlist)
+		d->checkqlist = q->nexttry;
+}
+
+/* No more query, update all it's cached entries, remove from lists */
+static void _q_done(mdns_daemon_t *d, struct query *q)
+{
+	struct cached *c = 0;
+	struct query *cur;
+	int i = _namehash(q->name) % LPRIME;
+
+	while ((c = _c_next(d, c, q->name, q->type)))
+		c->q = 0;
+
+	if (d->qlist == q) {
+		d->qlist = q->list;
+	} else {
+		for (cur = d->qlist; cur->list != q; cur = cur->list)
+			;
+		cur->list = q->list;
+	}
+
+	if (d->queries[i] == q) {
+		d->queries[i] = q->next;
+	} else {
+		for (cur = d->queries[i]; cur->next != q; cur = cur->next)
+			;
+		cur->next = q->next;
+	}
+
+	MDNSD_free(q->name);
+	MDNSD_free(q);
+}
+
+/* buh-bye, remove from hash and free */
+static void _r_done(mdns_daemon_t *d, mdns_record_t *r)
+{
+	mdns_record_t *cur = 0;
+	int i = _namehash(r->rr.name) % SPRIME;
+
+	if (d->published[i] == r)
+		d->published[i] = r->next;
+	else {
+		for (cur = d->published[i]; cur && cur->next != r; cur = cur->next) ;
+		if (cur)
+			cur->next = r->next;
+	}
+	MDNSD_free(r->rr.name);
+	MDNSD_free(r->rr.rdata);
+	MDNSD_free(r->rr.rdname);
+	MDNSD_free(r);
+}
+
+/* Call the answer function with this cached entry */
+static void _q_answer(mdns_daemon_t *d, struct cached *c)
+{
+	if (c->rr.ttl <= (unsigned long int)d->now.tv_sec)
+		c->rr.ttl = 0;
+	if (c->q->answer(&c->rr, c->q->arg) == -1)
+		_q_done(d, c->q);
+}
+
+static void _conflict(mdns_daemon_t *d, mdns_record_t *r)
+{
+	r->conflict(r->rr.name, r->rr.type, r->arg);
+	mdnsd_done(d, r);
+}
+
+/* Expire any old entries in this list */
+static void _c_expire(mdns_daemon_t *d, struct cached **list)
+{
+	struct cached *next, *cur = *list, *last = 0;
+
+	while (cur != 0) {
+		next = cur->next;
+		if ((unsigned long int)d->now.tv_sec >= cur->rr.ttl) {
+			if (last)
+				last->next = next;
+
+			/* Update list pointer if the first one expired */
+			if (*list == cur)
+				*list = next;
+
+			if (cur->q)
+				_q_answer(d, cur);
+
+			MDNSD_free(cur->rr.name);
+			MDNSD_free(cur->rr.rdata);
+			MDNSD_free(cur->rr.rdname);
+			MDNSD_free(cur);
+		} else {
+			last = cur;
+		}
+		cur = next;
+	}
+}
+
+/* Brute force expire any old cached records */
+static void _gc(mdns_daemon_t *d)
+{
+	int i;
+
+	for (i = 0; i < LPRIME; i++) {
+		if (d->cache[i])
+			_c_expire(d, &d->cache[i]);
+	}
+
+	d->expireall = (unsigned long int)(d->now.tv_sec + GC);
+}
+
+static int _cache(mdns_daemon_t *d, struct resource *r)
+{
+	struct cached *c = 0;
+	int i = _namehash(r->name) % LPRIME;
+
+	/* Cache flush for unique entries */
+	if (r->clazz == 32768 + d->clazz) {
+		while ((c = _c_next(d, c, r->name, r->type)))
+			c->rr.ttl = 0;
+		_c_expire(d, &d->cache[i]);
+	}
+
+	/* Process deletes */
+	if (r->ttl == 0) {
+		while ((c = _c_next(d, c, r->name, r->type))) {
+			if (_a_match(r, &c->rr)) {
+				c->rr.ttl = 0;
+				_c_expire(d, &d->cache[i]);
+				c = NULL;
+			}
+		}
+
+		return 0;
+	}
+
+	/*
+	 * XXX: The c->rr.ttl is a hack for now, BAD SPEC, start
+	 *      retrying just after half-waypoint, then expire
+	 */
+	c = (struct cached *)MDNSD_calloc(1, sizeof(struct cached));
+	c->rr.name = STRDUP(r->name);
+	c->rr.type = r->type;
+	c->rr.ttl = (unsigned int)((unsigned long)d->now.tv_sec + (r->ttl / 2) + 8);
+	c->rr.rdlen = r->rdlength;
+	if (r->rdlength && !r->rdata) {
+		//MDNSD_LOG_ERROR("rdlength is %d but rdata is NULL for domain name %s, type: %d, ttl: %ld", r->rdlength, r->name, r->type, r->ttl);
+		MDNSD_free(c->rr.name);
+		MDNSD_free(c);
+		return 1;
+	}
+	if (r->rdlength) {
+		c->rr.rdata = (unsigned char *)MDNSD_malloc(r->rdlength);
+		memcpy(c->rr.rdata, r->rdata, r->rdlength);
+	} else {
+		c->rr.rdata = NULL;
+	}
+
+	switch (r->type) {
+		case QTYPE_AAAA:
+			c->rr.ip6 = r->known.aaaa.ip6;
+		break;
+
+		case QTYPE_A:
+			c->rr.ip = r->known.a.ip;
+			break;
+
+		case QTYPE_NS:
+		case QTYPE_CNAME:
+		case QTYPE_PTR:
+			c->rr.rdname = STRDUP(r->known.ns.name);
+			break;
+
+		case QTYPE_SRV:
+			c->rr.rdname = STRDUP(r->known.srv.name);
+			c->rr.srv.port = r->known.srv.port;
+			c->rr.srv.weight = r->known.srv.weight;
+			c->rr.srv.priority = r->known.srv.priority;
+			break;
+	}
+
+	c->next = d->cache[i];
+	d->cache[i] = c;
+
+	if ((c->q = _q_next(d, 0, r->name, r->type)))
+		_q_answer(d, c);
+
+	return 0;
+}
+
+/* Copy the data bits only */
+static void _a_copy(struct message *m, mdns_answer_t *a)
+{
+	if (a->rdata) {
+		message_rdata_raw(m, a->rdata, a->rdlen);
+		return;
+	}
+
+	if (a->ip.s_addr)
+		message_rdata_long(m, a->ip);
+	if (a->type == QTYPE_SRV)
+		message_rdata_srv(m, a->srv.priority, a->srv.weight, a->srv.port, a->rdname);
+	else if (a->rdname)
+		message_rdata_name(m, a->rdname);
+}
+
+/* Copy a published record into an outgoing message */
+static int _r_out(mdns_daemon_t *d, struct message *m, mdns_record_t **list)
+{
+	mdns_record_t *r;
+	int ret = 0;
+
+	while ((r = *list) != 0 && message_packet_len(m) + _rr_len(&r->rr) < d->frame) {
+		if (r != r->list)
+			*list = r->list;
+		else
+			*list = NULL;
+		ret++;
+
+		if (r->unique)
+			message_an(m, r->rr.name, r->rr.type, (unsigned short int)(d->clazz + 32768), r->rr.ttl);
+		else
+			message_an(m, r->rr.name, r->rr.type,  (unsigned short int)d->clazz, r->rr.ttl);
+		r->last_sent = d->now;
+
+		_a_copy(m, &r->rr);
+		if (r->rr.ttl == 0) {
+
+			// also remove from other lists, because record may be in multiple lists at the same time
+			_r_remove_lists(d, r, list);
+
+			_r_done(d, r);
+
+		}
+	}
+
+	return ret;
+}
+
+
+mdns_daemon_t *mdnsd_new(int clazz, int frame)
+{
+	mdns_daemon_t *d;
+
+	d = (mdns_daemon_t *)MDNSD_calloc(1, sizeof(struct mdns_daemon));
+	gettimeofday(&d->now, 0);
+	d->expireall = (unsigned long int)(d->now.tv_sec + GC);
+	d->clazz = clazz;
+	d->frame = frame;
+	d->received_callback = NULL;
+
+	return d;
+}
+
+/* Shutting down, zero out ttl and push out all records */
+void mdnsd_shutdown(mdns_daemon_t *d)
+{
+	int i;
+	mdns_record_t *cur, *next;
+
+	d->a_now = 0;
+	for (i = 0; i < SPRIME; i++) {
+		for (cur = d->published[i]; cur != 0;) {
+			next = cur->next;
+			cur->rr.ttl = 0;
+			cur->list = d->a_now;
+			d->a_now = cur;
+			cur = next;
+		}
+	}
+
+	d->shutdown = 1;
+}
+
+void mdnsd_flush(mdns_daemon_t *d)
+{
+	(void)d;
+	/* - Set all querys to 0 tries
+	 * - Free whole cache
+	 * - Set all mdns_record_t *to probing
+	 * - Reset all answer lists
+	 */
+}
+
+void mdnsd_free(mdns_daemon_t *d)
+{
+	size_t i;
+	for (i = 0; i< LPRIME; i++) {
+		struct cached* cur = d->cache[i];
+		while (cur) {
+			struct cached* next = cur->next;
+			MDNSD_free(cur->rr.name);
+			MDNSD_free(cur->rr.rdata);
+			MDNSD_free(cur->rr.rdname);
+			MDNSD_free(cur);
+			cur = next;
+		}
+	}
+
+	for (i = 0; i< SPRIME; i++) {
+		struct mdns_record* cur = d->published[i];
+		struct query* curq = NULL;
+		while (cur) {
+			struct mdns_record* next = cur->next;
+			MDNSD_free(cur->rr.name);
+			MDNSD_free(cur->rr.rdata);
+			MDNSD_free(cur->rr.rdname);
+			MDNSD_free(cur);
+			cur = next;
+		}
+
+
+		curq = d->queries[i];
+		while (curq) {
+			struct query* next = curq->next;
+			MDNSD_free(curq->name);
+			MDNSD_free(curq);
+			curq = next;
+		}
+
+	}
+
+	{
+		struct unicast *u = d->uanswers;
+		while (u) {
+			struct unicast *next = u->next;
+			MDNSD_free(u);
+			u=next;
+		}
+	}
+
+	MDNSD_free(d);
+}
+
+
+void mdnsd_register_receive_callback(mdns_daemon_t *d, mdnsd_record_received_callback cb, void* data) {
+	d->received_callback = cb;
+	d->received_callback_data = data;
+}
+
+int mdnsd_in(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int port)
+{
+	int i;
+	mdns_record_t *r = 0;
+
+	if (d->shutdown)
+		return 1;
+
+	gettimeofday(&d->now, 0);
+
+	if (m->header.qr == 0) {
+		/* Process each query */
+		for (i = 0; i < m->qdcount; i++) {
+			mdns_record_t *r_start, *r_next = NULL;
+			bool hasConflict = false;
+			if (m->qd[i].clazz != d->clazz || (r = _r_next(d, 0, m->qd[i].name, m->qd[i].type)) == 0)
+				continue;
+			r_start = r;
+
+
+			/* Check all of our potential answers */
+			for (; r != 0; r = r_next) {
+
+				MDNSD_LOG_TRACE("Got Query: Name: %s, Type: %d", r->rr.name, r->rr.type);
+
+				// do this here, because _conflict deletes r and thus next is not valid anymore
+				r_next = _r_next(d, r, m->qd[i].name, m->qd[i].type);
+				/* probing state, check for conflicts */
+				if (r->unique && r->unique < 5) {
+					/* Check all to-be answers against our own */
+					int j;
+					for (j = 0; j < m->nscount; j++) {
+						if (m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name, m->an[j].name))
+							continue;
+
+						/* This answer isn't ours, conflict! */
+						if (!_a_match(&m->an[j], &r->rr)) {
+							_conflict(d, r);
+							hasConflict = true;
+							break;
+						}
+					}
+					continue;
+				}
+
+				/* Check the known answers for this question */
+				{
+					int j;
+					for (j = 0; j < m->ancount; j++) {
+						if (m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name, m->an[j].name))
+							continue;
+
+						if (d->received_callback) {
+							d->received_callback(&m->an[j], d->received_callback_data);
+						}
+
+						/* Do they already have this answer? */
+						if (_a_match(&m->an[j], &r->rr))
+							break;
+					}
+					if (j == m->ancount)
+						_r_send(d, r);
+				}
+
+			}
+
+			/* Send the matching unicast reply */
+			if (!hasConflict && port != 5353)
+				_u_push(d, r_start, m->id, ip, port);
+		}
+
+		return 0;
+	}
+
+	/* Process each answer, check for a conflict, and cache */
+	for (i = 0; i < m->ancount; i++) {
+		if (m->an[i].name == NULL) {
+			MDNSD_LOG_ERROR("Got answer with NULL name at %p. Type: %d, TTL: %ld\n", (void*)&m->an[i], m->an[i].type, m->an[i].ttl);
+			return 3;
+		}
+
+		MDNSD_LOG_TRACE("Got Answer: Name: %s, Type: %d", m->an[i].name, m->an[i].type);
+		if ((r = _r_next(d, 0, m->an[i].name, m->an[i].type)) != 0 &&
+			r->unique && _a_match(&m->an[i], &r->rr) == 0)
+			_conflict(d, r);
+
+		if (d->received_callback) {
+			d->received_callback(&m->an[i], d->received_callback_data);
+		}
+		if (_cache(d, &m->an[i]) != 0)
+			return 2;
+	}
+	return 0;
+}
+
+int mdnsd_out(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int *port)
+{
+	mdns_record_t *r;
+	int ret = 0;
+
+	gettimeofday(&d->now, 0);
+	memset(m, 0, sizeof(struct message));
+
+	/* Defaults, multicast */
+	*port = htons(5353);
+	if(ip->sa_family == AF_INET6) {
+		struct sockaddr_in6 addr6;
+
+		memset(&addr6, 0, sizeof(addr6));
+		addr6.sin6_family = AF_INET6;
+		addr6.sin6_port = *port;
+		inet_pton(AF_INET6, "ff02::fb", &(addr6.sin6_addr));
+		memcpy(ip, &addr6, sizeof(addr6));
+	} else {
+			struct sockaddr_in addr;
+
+			memset(&addr, 0, sizeof(addr));
+			addr.sin_family = AF_INET;
+			addr.sin_port = *port;
+			inet_pton(AF_INET, "224.0.0.251", &(addr.sin_addr));
+			memcpy(ip, &addr, sizeof(addr));
+	}
+	m->header.qr = 1;
+	m->header.aa = 1;
+
+	/* Send out individual unicast answers */
+	if (d->uanswers) {
+		struct unicast *u = d->uanswers;
+
+		MDNSD_LOG_TRACE("Send Unicast Answer: Name: %s, Type: %d", u->r->rr.name, u->r->rr.type);
+
+		d->uanswers = u->next;
+		*port = u->port;
+		ip = u->to;
+		m->id = (unsigned short int)u->id;
+		message_qd(m, u->r->rr.name, u->r->rr.type, (unsigned short int)d->clazz);
+		message_an(m, u->r->rr.name, u->r->rr.type, (unsigned short int)d->clazz, u->r->rr.ttl);
+		u->r->last_sent = d->now;
+		_a_copy(m, &u->r->rr);
+		MDNSD_free(u);
+
+		return 1;
+	}
+
+//	printf("OUT: probing %X now %X pause %X publish %X\n",d->probing,d->a_now,d->a_pause,d->a_publish);
+
+	/* Accumulate any immediate responses */
+	if (d->a_now)
+		ret += _r_out(d, m, &d->a_now);
+
+	/* Check if it's time to send the publish retries (unlink if done) */
+	if (d->a_publish && _tvdiff(d->now, d->publish) <= 0) {
+
+		mdns_record_t *next, *cur = d->a_publish, *last = NULL;
+
+		while (cur && message_packet_len(m) + _rr_len(&cur->rr) < d->frame) {
+
+			if (cur->rr.type == QTYPE_PTR) {
+				MDNSD_LOG_TRACE("Send Publish PTR: Name: %s, rdlen: %d, rdata: %.*s, rdname: %s",
+				        cur->rr.name,cur->rr.rdlen, cur->rr.rdlen, cur->rr.rdata, cur->rr.rdname == NULL ? "" : cur->rr.rdname);
+			} else if (cur->rr.type == QTYPE_SRV) {
+				MDNSD_LOG_TRACE("Send Publish SRV: Name: %s, rdlen: %d, rdata: %.*s, rdname: %s, port: %d, prio: %d, weight: %d",
+				        cur->rr.name,cur->rr.rdlen, cur->rr.rdlen, cur->rr.rdata, cur->rr.rdname == NULL ? "" : cur->rr.rdname,
+				        cur->rr.srv.port, cur->rr.srv.priority, cur->rr.srv.weight);
+			} else {
+				MDNSD_LOG_TRACE("Send Publish: Name: %s, Type: %d", cur->rr.name, cur->rr.type);
+			}
+			next = cur->list;
+			ret++;
+			cur->tries++;
+
+			if (cur->unique)
+				message_an(m, cur->rr.name, cur->rr.type, (unsigned short int)(d->clazz + 32768), cur->rr.ttl);
+			else
+				message_an(m, cur->rr.name, cur->rr.type, (unsigned short int)d->clazz, cur->rr.ttl);
+			_a_copy(m, &cur->rr);
+			cur->last_sent = d->now;
+
+			if (cur->rr.ttl != 0 && cur->tries < 4) {
+				last = cur;
+				cur = next;
+				continue;
+			}
+
+			if (d->a_publish == cur)
+				d->a_publish = next;
+			if (last)
+				last->list = next;
+			if (cur->rr.ttl == 0)
+				_r_done(d, cur);
+			cur = next;
+		}
+
+		if (d->a_publish) {
+			d->publish.tv_sec = d->now.tv_sec + 2;
+			d->publish.tv_usec = d->now.tv_usec;
+		}
+	}
+
+	/* If we're in shutdown, we're done */
+	if (d->shutdown)
+		return ret;
+
+	/* Check if a_pause is ready */
+	if (d->a_pause && _tvdiff(d->now, d->pause) <= 0)
+		ret += _r_out(d, m, &d->a_pause);
+
+	/* Now process questions */
+	if (ret > 0)
+		return ret;
+
+	m->header.qr = 0;
+	m->header.aa = 0;
+
+	if (d->probing && _tvdiff(d->now, d->probe) <= 0) {
+		mdns_record_t *last = 0;
+
+		/* Scan probe list to ask questions and process published */
+		for (r = d->probing; r != 0;) {
+			/* Done probing, publish */
+			if (r->unique == 4) {
+				mdns_record_t *next = r->list;
+
+				if (d->probing == r)
+					d->probing = r->list;
+				else
+					last->list = r->list;
+
+				r->list = 0;
+				r->unique = 5;
+				_r_publish(d, r);
+				r = next;
+				continue;
+			}
+
+			MDNSD_LOG_TRACE("Send Probing: Name: %s, Type: %d", r->rr.name, r->rr.type);
+
+			message_qd(m, r->rr.name, r->rr.type, (unsigned short int)d->clazz);
+			r->last_sent = d->now;
+			last = r;
+			r = r->list;
+		}
+
+		/* Scan probe list again to append our to-be answers */
+		for (r = d->probing; r != 0; r = r->list) {
+			r->unique++;
+
+			MDNSD_LOG_TRACE("Send Answer in Probe: Name: %s, Type: %d", r->rr.name, r->rr.type);
+			message_ns(m, r->rr.name, r->rr.type, (unsigned short int)d->clazz, r->rr.ttl);
+			_a_copy(m, &r->rr);
+			r->last_sent = d->now;
+			ret++;
+		}
+
+		/* Process probes again in the future */
+		if (ret) {
+			d->probe.tv_sec = d->now.tv_sec;
+			d->probe.tv_usec = d->now.tv_usec + 250000;
+			return ret;
+		}
+	}
+
+	/* Process qlist for retries or expirations */
+	if (d->checkqlist && (unsigned long int)d->now.tv_sec >= d->checkqlist) {
+		struct query *q;
+		struct cached *c;
+		unsigned long int nextbest = 0;
+
+		/* Ask questions first, track nextbest time */
+		for (q = d->qlist; q != 0; q = q->list) {
+			if (q->nexttry > 0 && q->nexttry <= (unsigned long int)d->now.tv_sec && q->tries < 3)
+				message_qd(m, q->name, (unsigned short int)q->type, (unsigned short int)d->clazz);
+			else if (q->nexttry > 0 && (nextbest == 0 || q->nexttry < nextbest))
+				nextbest = q->nexttry;
+		}
+
+		/* Include known answers, update questions */
+		for (q = d->qlist; q != 0; q = q->list) {
+			if (q->nexttry == 0 || q->nexttry > (unsigned long int)d->now.tv_sec)
+				continue;
+
+			/* Done retrying, expire and reset */
+			if (q->tries == 3) {
+				_c_expire(d, &d->cache[_namehash(q->name) % LPRIME]);
+				_q_reset(d, q);
+				continue;
+			}
+
+			ret++;
+			q->nexttry = (unsigned long int)(d->now.tv_sec + ++q->tries);
+			if (nextbest == 0 || q->nexttry < nextbest)
+				nextbest = q->nexttry;
+
+			/* If room, add all known good entries */
+			c = 0;
+			while ((c = _c_next(d, c, q->name, q->type)) != 0 && c->rr.ttl > (unsigned long int)d->now.tv_sec + 8 &&
+				   message_packet_len(m) + _rr_len(&c->rr) < d->frame) {
+
+				MDNSD_LOG_TRACE("Add known answer: Name: %s, Type: %d", c->rr.name, c->rr.type);
+				message_an(m, q->name, (unsigned short int)q->type, (unsigned short int)d->clazz, c->rr.ttl - (unsigned long int)d->now.tv_sec);
+				_a_copy(m, &c->rr);
+			}
+		}
+		d->checkqlist = nextbest;
+	}
+
+	if ((unsigned long int)d->now.tv_sec > d->expireall)
+		_gc(d);
+
+	return ret;
+}
+
+
+#define RET					\
+	while (d->sleep.tv_usec > 1000000) {	\
+		d->sleep.tv_sec++;		\
+		d->sleep.tv_usec -= 1000000;	\
+	}					\
+	return &d->sleep;
+
+struct timeval *mdnsd_sleep(mdns_daemon_t *d)
+{
+	int usec, minExpire;
+
+	d->sleep.tv_sec = d->sleep.tv_usec = 0;
+
+	/* First check for any immediate items to handle */
+	if (d->uanswers || d->a_now)
+		return &d->sleep;
+
+	gettimeofday(&d->now, 0);
+
+	/* Then check for paused answers or nearly expired records */
+	if (d->a_pause) {
+		if ((usec = _tvdiff(d->now, d->pause)) > 0)
+			d->sleep.tv_usec = usec;
+		RET;
+	}
+
+	/* Now check for probe retries */
+	if (d->probing) {
+		if ((usec = _tvdiff(d->now, d->probe)) > 0)
+			d->sleep.tv_usec = usec;
+		RET;
+	}
+
+	/* Now check for publish retries */
+	if (d->a_publish) {
+		if ((usec = _tvdiff(d->now, d->publish)) > 0)
+			d->sleep.tv_usec = usec;
+		RET;
+	}
+
+	/* Also check for queries with known answer expiration/retry */
+	if (d->checkqlist) {
+		int sec;
+		if ((sec = (int)(d->checkqlist - (unsigned long int)d->now.tv_sec)) > 0)
+			d->sleep.tv_sec = sec;
+		RET;
+	}
+
+	/* Resend published records before TTL expires */
+	// latest expire is garbage collection
+	minExpire = (int)(d->expireall - (unsigned long int)d->now.tv_sec);
+	if (minExpire < 0)
+		return &d->sleep;
+
+	{
+		size_t i;
+		for (i=0; i<SPRIME; i++) {
+			int expire;
+			if (!d->published[i])
+				continue;
+			expire = (int)((d->published[i]->last_sent.tv_sec + (long int)d->published[i]->rr.ttl) - d->now.tv_sec);
+			if (expire < minExpire)
+				d->a_pause = NULL;
+			minExpire = expire < minExpire ? expire : minExpire;
+			_r_push(&d->a_pause, d->published[i]);
+		}
+	}
+	// publish 2 seconds before expire.
+	d->sleep.tv_sec = minExpire > 2 ? minExpire-2 : 0;
+	d->pause.tv_sec = d->now.tv_sec + d->sleep.tv_sec;
+	RET;
+}
+
+void mdnsd_query(mdns_daemon_t *d, const char *host, int type, int (*answer)(mdns_answer_t *a, void *arg), void *arg)
+{
+	struct query *q;
+	int i = _namehash(host) % SPRIME;
+
+	if (!(q = _q_next(d, 0, host, type))) {
+		if (!answer)
+			return;
+
+		q = (struct query *)MDNSD_calloc(1, sizeof(struct query));
+		q->name = STRDUP(host);
+		q->type = type;
+		q->next = d->queries[i];
+		q->list = d->qlist;
+		d->qlist = d->queries[i] = q;
+
+		/* Any cached entries should be associated */
+		{
+			struct cached *cur = 0;
+			while ((cur = _c_next(d, cur, q->name, q->type)))
+				cur->q = q;
+		}
+		_q_reset(d, q);
+
+		/* New question, immediately send out */
+		q->nexttry = d->checkqlist = (unsigned long int)d->now.tv_sec;
+	}
+
+	/* No answer means we don't care anymore */
+	if (!answer) {
+		_q_done(d, q);
+		return;
+	}
+
+	q->answer = answer;
+	q->arg = arg;
+}
+
+mdns_answer_t *mdnsd_list(mdns_daemon_t *d,const char *host, int type, mdns_answer_t *last)
+{
+	return (mdns_answer_t *)_c_next(d, (struct cached *)last, host, type);
+}
+
+mdns_record_t *mdnsd_record_next(const mdns_record_t* r) {
+	return r ? r->next : NULL;
+}
+
+const mdns_answer_t *mdnsd_record_data(const mdns_record_t* r) {
+	return &r->rr;
+}
+
+mdns_record_t *mdnsd_shared(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl)
+{
+	int i = _namehash(host) % SPRIME;
+	mdns_record_t *r;
+
+	r = (struct mdns_record *)MDNSD_calloc(1, sizeof(struct mdns_record));
+	r->rr.name = STRDUP(host);
+	r->rr.type = type;
+	r->rr.ttl = ttl;
+	r->next = d->published[i];
+	d->published[i] = r;
+
+	return r;
+}
+
+mdns_record_t *mdnsd_unique(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl, void (*conflict)(char *host, int type, void *arg), void *arg)
+{
+	mdns_record_t *r;
+
+	r = mdnsd_shared(d, host, type, ttl);
+	r->conflict = conflict;
+	r->arg = arg;
+	r->unique = 1;
+	_r_push(&d->probing, r);
+	d->probe.tv_sec = d->now.tv_sec;
+	d->probe.tv_usec = d->now.tv_usec;
+
+	return r;
+}
+
+mdns_record_t * mdnsd_get_published(const mdns_daemon_t *d, const char *host) {
+	return d->published[_namehash(host) % SPRIME];
+}
+
+int mdnsd_has_query(const mdns_daemon_t *d, const char *host) {
+	return d->queries[_namehash(host) % SPRIME]!=NULL;
+}
+
+void mdnsd_done(mdns_daemon_t *d, mdns_record_t *r)
+{
+	mdns_record_t *cur;
+
+	if (r->unique && r->unique < 5) {
+		/* Probing yet, zap from that list first! */
+		if (d->probing == r) {
+			d->probing = r->list;
+		} else {
+			for (cur = d->probing; cur->list != r; cur = cur->list)
+				;
+			cur->list = r->list;
+		}
+
+		_r_done(d, r);
+		return;
+	}
+
+	r->rr.ttl = 0;
+	_r_send(d, r);
+}
+
+void mdnsd_set_raw(mdns_daemon_t *d, mdns_record_t *r, const char *data, unsigned short int len)
+{
+	MDNSD_free(r->rr.rdata);
+	r->rr.rdata = (unsigned char *)MDNSD_malloc(len);
+	memcpy(r->rr.rdata, data, len);
+	r->rr.rdlen = len;
+	_r_publish(d, r);
+}
+
+void mdnsd_set_host(mdns_daemon_t *d, mdns_record_t *r, const char *name)
+{
+	MDNSD_free(r->rr.rdname);
+	r->rr.rdname = STRDUP(name);
+	_r_publish(d, r);
+}
+
+void mdnsd_set_ip(mdns_daemon_t *d, mdns_record_t *r, struct in_addr ip)
+{
+	r->rr.ip = ip;
+	_r_publish(d, r);
+}
+
+void mdnsd_set_srv(mdns_daemon_t *d, mdns_record_t *r, unsigned short int priority, unsigned short int weight, unsigned short int port, char *name)
+{
+	r->rr.srv.priority = priority;
+	r->rr.srv.weight = weight;
+	r->rr.srv.port = port;
+	mdnsd_set_host(d, r, name);
+}
+
+#if MDNSD_LOGLEVEL <= 100
+#include <ctype.h>
+static void dump_hex_pkg(char* buffer, int bufferLen) {
+	char ascii[17];
+	memset(ascii,0,17);
+	for (int i = 0; i < bufferLen; i++)
+	{
+		if (i%16 == 0)
+			printf("%s\n%06x ", ascii, i);
+		if (isprint((int)(buffer[i])))
+			ascii[i%16] = buffer[i];
+		else
+			ascii[i%16] = '.';
+		printf("%02X ", (unsigned char)buffer[i]);
+	}
+	printf("%s\n%06x ", ascii, bufferLen);
+	printf("\n");
+}
+#endif
+
+unsigned short int mdnsd_step(mdns_daemon_t *d, int mdns_socket, bool processIn, bool processOut, struct timeval *nextSleep) {
+
+	struct message m;
+
+	if (processIn) {
+		int bsize;
+		unsigned char buf[MAX_PACKET_LEN];
+		struct sockaddr_storage from;
+		socklen_t ssize = sizeof(from);
+
+		while ((bsize = (int)recvfrom(mdns_socket, (char*)buf, MAX_PACKET_LEN, 0, (struct sockaddr *)&from, &ssize)) > 0) {
+			memset(&m, 0, sizeof(struct message));
+#if MDNSD_LOGLEVEL <= 100
+			MDNSD_LOG_TRACE("Got Data:");
+			dump_hex_pkg((char*)buf, bsize);
+#endif
+#ifdef MDNSD_DEBUG_DUMP_PKGS_FILE
+            mdnsd_debug_dumpCompleteChunk(d, (char*)buf, (size_t) bsize);
+#endif
+			if (!message_parse(&m, buf, (size_t)bsize))
+			    continue;
+
+			unsigned short int fromPort;
+			if (from.ss_family == AF_INET){
+				fromPort = ((struct sockaddr_in *)&from)->sin_port;
+			} else {
+				fromPort = ((struct sockaddr_in6 *)&from)->sin6_port;
+			}
+			if (mdnsd_in(d, &m,(struct sockaddr *)&from, fromPort) !=0)
+				return 2;
+		}
+#ifdef _WIN32
+		if (bsize < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
+#else
+		if (bsize < 0 && errno != EAGAIN)
+#endif
+		{
+			return 1;
+		}
+	}
+
+	if (processOut) {
+		struct sockaddr_storage addrStorage;
+		struct sockaddr *to = (struct sockaddr*)&addrStorage;
+		socklen_t addrLength = sizeof(struct sockaddr_storage);
+		unsigned short int port;
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-align"
+#endif
+
+		if(getsockname(mdns_socket, to, &addrLength))
+			return 2;
+		while (mdnsd_out(d, &m, to, &port)) {
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+			int len = message_packet_len(&m);
+			char* buf = (char*)message_packet(&m);
+#if MDNSD_LOGLEVEL <= 100
+			MDNSD_LOG_TRACE("Send Data:");
+			dump_hex_pkg(buf, (int)len);
+#endif
+
+#ifdef MDNSD_DEBUG_DUMP_PKGS_FILE
+			mdnsd_debug_dumpCompleteChunk(d, buf, (size_t) len);
+#endif
+			if ((sendto(mdns_socket, buf, (unsigned int)len, 0, to,
+						addrLength)) != len) {
+				return 2;
+			}
+		}
+	}
+
+	if (nextSleep) {
+		struct timeval *tv = mdnsd_sleep(d);
+		nextSleep->tv_sec = tv->tv_sec;
+		nextSleep->tv_usec = tv->tv_usec;
+	}
+
+	return 0;
+}
diff --git a/deps/mdnsd/libmdnsd/mdnsd.h b/deps/mdnsd/libmdnsd/mdnsd.h
new file mode 100644
index 0000000..4f9bdaa
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/mdnsd.h
@@ -0,0 +1,235 @@
+#ifndef MDNSD_H_
+#define MDNSD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mdnsd_config.h"
+#include "1035.h"
+#if !defined(__bool_true_false_are_defined) && defined(_MSC_VER) && _MSC_VER < 1600
+// VS 2008 has no stdbool.h
+#define bool	short
+#define true	1
+#define false	0
+#else
+#include <stdbool.h>
+#endif
+#include <stdio.h>
+
+#define QCLASS_IN (1)
+
+#if MDNSD_LOGLEVEL <= 100
+#define MDNSD_LOG_TRACE(...) do { \
+		printf("mdnsd: TRACE - "); printf(__VA_ARGS__); printf("\n"); } while(0)
+#else
+#define MDNSD_LOG_TRACE(...) do {} while(0)
+#endif
+
+#if MDNSD_LOGLEVEL <= 200
+#define MDNSD_LOG_DEBUG(...) do { \
+		printf("mdnsd: DEBUG - "); printf(__VA_ARGS__); printf("\n"); } while(0)
+#else
+#define MDNSD_LOG_DEBUG(...) do {} while(0)
+#endif
+
+#if MDNSD_LOGLEVEL <= 300
+#define MDNSD_LOG_INFO(...) do { \
+		printf("mdnsd: INFO  - "); printf(__VA_ARGS__); printf("\n"); } while(0)
+#else
+#define MDNSD_LOG_INFO(...) do {} while(0)
+#endif
+
+#if MDNSD_LOGLEVEL <= 400
+#define MDNSD_LOG_WARNING(...) do { \
+		printf("mdnsd: WARN  - "); printf(__VA_ARGS__); printf("\n"); } while(0)
+#else
+#define MDNSD_LOG_WARNING(...) do {} while(0)
+#endif
+
+#if MDNSD_LOGLEVEL <= 500
+#define MDNSD_LOG_ERROR(...) do { \
+		printf("mdnsd: ERROR - "); printf(__VA_ARGS__); printf("\n"); } while(0)
+#else
+#define MDNSD_LOG_ERROR(...) do {} while(0)
+#endif
+
+#if MDNSD_LOGLEVEL <= 600
+#define MDNSD_LOG_FATAL(...) do { \
+		printf("mdnsd: FATAL - "); printf(__VA_ARGS__); printf("\n"); } while(0)
+#else
+#define MDNSD_LOG_FATAL(...) do {} while(0)
+#endif
+
+
+/* Main daemon data */
+typedef struct mdns_daemon mdns_daemon_t;
+/* Record entry */
+typedef struct mdns_record mdns_record_t;
+
+/* Callback for received record. Data is passed from the register call */
+typedef void (*mdnsd_record_received_callback)(const struct resource* r, void* data);
+
+/* Answer data */
+typedef struct mdns_answer {
+	char *name;
+	unsigned short type;
+	unsigned long ttl;
+	unsigned short rdlen;
+	unsigned char *rdata;
+	struct in_addr ip;	/* A */
+	struct in6_addr ip6;	/* AAAA */
+	char *rdname;		/* NS/CNAME/PTR/SRV */
+	struct {
+		//cppcheck-suppress unusedStructMember
+		unsigned short int priority, weight, port;
+	} srv;			/* SRV */
+} mdns_answer_t;
+
+/**
+ * Global functions
+ */
+
+/**
+ * Create a new mdns daemon for the given class of names (usually 1) and
+ * maximum frame size
+ */
+mdns_daemon_t MDNSD_EXPORT * mdnsd_new(int clazz, int frame);
+
+/**
+ * Gracefully shutdown the daemon, use mdnsd_out() to get the last
+ * packets
+ */
+void MDNSD_EXPORT mdnsd_shutdown(mdns_daemon_t *d);
+
+/**
+ * Flush all cached records (network/interface changed)
+ */
+void MDNSD_EXPORT mdnsd_flush(mdns_daemon_t *d);
+
+/**
+ * Free given mdns_daemon_t *(should have used mdnsd_shutdown() first!)
+ */
+void MDNSD_EXPORT mdnsd_free(mdns_daemon_t *d);
+
+/**
+ * Register callback which is called when a record is received. The data parameter is passed to the callback.
+ * Calling this multiple times overwrites the previous register.
+ */
+void MDNSD_EXPORT mdnsd_register_receive_callback(mdns_daemon_t *d, mdnsd_record_received_callback cb, void* data);
+
+/**
+ * I/O functions
+ */
+
+/**
+ * Oncoming message from host (to be cached/processed)
+ */
+int MDNSD_EXPORT mdnsd_in(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int port);
+
+/**
+ * Outgoing messge to be delivered to host, returns >0 if one was
+ * returned and m/ip/port set
+ */
+int MDNSD_EXPORT mdnsd_out(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int *port);
+
+/**
+ * returns the max wait-time until mdnsd_out() needs to be called again 
+ */
+struct timeval MDNSD_EXPORT * mdnsd_sleep(mdns_daemon_t *d);
+
+/**
+ * Q/A functions
+ */
+
+/**
+ * Register a new query
+ *
+ * The answer() callback is called whenever one is found/changes/expires
+ * (immediate or anytime after, mdns_answer_t valid until ->ttl==0)
+ * either answer returns -1, or another mdnsd_query() with a %NULL answer
+ * will remove/unregister this query
+ */
+void MDNSD_EXPORT mdnsd_query(mdns_daemon_t *d, const char *host, int type, int (*answer)(mdns_answer_t *a, void *arg), void *arg);
+
+/**
+ * Returns the first (if last == NULL) or next answer after last from
+ * the cache mdns_answer_t only valid until an I/O function is called
+ */
+mdns_answer_t MDNSD_EXPORT *mdnsd_list(mdns_daemon_t *d, const char *host, int type, mdns_answer_t *last);
+
+/**
+ * Returns the next record of the given record, i.e. the value of next field.
+ * @param r the base record
+ * @return r->next
+ */
+mdns_record_t MDNSD_EXPORT *mdnsd_record_next(const mdns_record_t* r) ;
+
+/**
+ * Gets the record data
+ */
+const mdns_answer_t MDNSD_EXPORT *mdnsd_record_data(const mdns_record_t* r) ;
+
+
+/**
+ * Publishing functions
+ */
+
+/**
+ * Create a new unique record
+ *
+ * Call mdnsd_list() first to make sure the record is not used yet.
+ *
+ * The conflict() callback is called at any point when one is detected
+ * and unable to recover after the first data is set_*(), any future
+ * changes effectively expire the old one and attempt to create a new
+ * unique record
+ */
+mdns_record_t MDNSD_EXPORT * mdnsd_unique(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl, void (*conflict)(char *host, int type, void *arg), void *arg);
+
+/** 
+ * Create a new shared record
+ */
+mdns_record_t MDNSD_EXPORT * mdnsd_shared(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl);
+
+/**
+ * Get a previously created record based on the host name. NULL if not found. Does not return records for other hosts.
+ * If multiple records are found, use record->next to iterate over all the results.
+ */
+mdns_record_t MDNSD_EXPORT * mdnsd_get_published(const mdns_daemon_t *d, const char *host);
+
+/**
+ * Check if there is already a query for the given host
+ */
+int MDNSD_EXPORT mdnsd_has_query(const mdns_daemon_t *d, const char *host);
+
+/**
+ * de-list the given record
+ */
+void MDNSD_EXPORT mdnsd_done(mdns_daemon_t *d, mdns_record_t *r);
+
+/**
+ * These all set/update the data for the given record, nothing is
+ * published until they are called
+ */
+void MDNSD_EXPORT mdnsd_set_raw(mdns_daemon_t *d, mdns_record_t *r, const char *data, unsigned short int len);
+void MDNSD_EXPORT mdnsd_set_host(mdns_daemon_t *d, mdns_record_t *r, const char *name);
+void MDNSD_EXPORT mdnsd_set_ip(mdns_daemon_t *d, mdns_record_t *r, struct in_addr ip);
+void MDNSD_EXPORT mdnsd_set_srv(mdns_daemon_t *d, mdns_record_t *r, unsigned short int priority, unsigned short int weight, unsigned short int port, char *name);
+
+/**
+ * Process input queue and output queue. Should be called at least the time which is returned in nextSleep.
+ * Returns 0 on success, 1 on read error, 2 on write error
+ */
+unsigned short int MDNSD_EXPORT mdnsd_step(mdns_daemon_t *d, int mdns_socket, bool processIn, bool processOut, struct timeval *nextSleep);
+
+
+#ifdef MDNSD_DEBUG_DUMP_PKGS_FILE
+void mdnsd_debug_dumpCompleteChunk(mdns_daemon_t *d, const char* data, size_t len);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif	/* MDNSD_H_ */
diff --git a/deps/mdnsd/libmdnsd/mdnsd_config_extra.in b/deps/mdnsd/libmdnsd/mdnsd_config_extra.in
new file mode 100644
index 0000000..e8195dc
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/mdnsd_config_extra.in
@@ -0,0 +1,27 @@
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+#endif
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE 1
+#endif
+
+#if defined __APPLE__
+// required for ip_mreq
+#define _DARWIN_C_SOURCE 1
+#endif
+
+#define MDNSD_free(ptr) free(ptr)
+#define MDNSD_malloc(size) malloc(size)
+#define MDNSD_calloc(num, size) calloc(num, size)
+#define MDNSD_realloc(ptr, size) realloc(ptr, size)
+
+#define MDNSD_LOGLEVEL ${MDNSD_LOGLEVEL}
+
+/**
+ * Inline Functions
+ * ---------------- */
+#ifdef _MSC_VER
+# define MDNSD_INLINE __inline
+#else
+# define MDNSD_INLINE inline
+#endif
diff --git a/deps/mdnsd/libmdnsd/mdnsd_debug_dump_pkgs_file.c b/deps/mdnsd/libmdnsd/mdnsd_debug_dump_pkgs_file.c
new file mode 100644
index 0000000..17345b1
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/mdnsd_debug_dump_pkgs_file.c
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This code is used to generate a binary file for every request type
+ * which can be sent from a client to the server.
+ * These files form the basic corpus for fuzzing the server.
+ */
+
+#ifndef MDNSD_DEBUG_DUMP_PKGS_FILE
+#error MDNSD_DEBUG_DUMP_PKGS_FILE must be defined
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "mdnsd.h"
+
+
+unsigned int mdnsd_dump_chunkCount = 0;
+
+void mdnsd_debug_dumpCompleteChunk(mdns_daemon_t *d, const char* data, size_t len) {
+
+    char fileName[250];
+    struct timeval t;
+    gettimeofday(&t, 0);
+    snprintf(fileName, sizeof(fileName), "%s/%05u_%ld", MDNSD_CORPUS_OUTPUT_DIR, ++mdnsd_dump_chunkCount, t.tv_sec);
+
+    char dumpOutputFile[266];
+    snprintf(dumpOutputFile, 255, "%s.bin", fileName);
+    // check if file exists and if yes create a counting filename to avoid overwriting
+    unsigned cnt = 1;
+    while ( access( dumpOutputFile, F_OK ) != -1 ) {
+        snprintf(dumpOutputFile, 266, "%s_%u.bin", fileName, cnt);
+        cnt++;
+    }
+
+    FILE *write_ptr = fopen(dumpOutputFile, "ab");
+    fwrite(data, len, 1, write_ptr); // write 10 bytes from our buffer
+    fclose(write_ptr);
+}
diff --git a/deps/mdnsd/libmdnsd/ms_stdint.h b/deps/mdnsd/libmdnsd/ms_stdint.h
new file mode 100644
index 0000000..4ab6722
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/ms_stdint.h
@@ -0,0 +1,257 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2013 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. Neither the name of the product nor the names of its contributors may
+//      be used to endorse or promote products derived from this software
+//      without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1600 // [
+#include <stdint.h>
+#else
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifndef UNDER_CE
+#ifdef __cplusplus
+extern "C" {
+#endif
+#  include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#     define _W64 __w64
+#  else
+#     define _W64
+#  endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+   typedef signed char       int8_t;
+   typedef signed short      int16_t;
+   typedef signed int        int32_t;
+   typedef unsigned char     uint8_t;
+   typedef unsigned short    uint16_t;
+   typedef unsigned int      uint32_t;
+#else
+   typedef signed __int8     int8_t;
+   typedef signed __int16    int16_t;
+   typedef signed __int32    int32_t;
+   typedef unsigned __int8   uint8_t;
+   typedef unsigned __int16  uint16_t;
+   typedef unsigned __int32  uint32_t;
+#endif
+typedef signed __int64       int64_t;
+typedef unsigned __int64     uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef signed __int64    intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef _W64 signed int   intptr_t;
+   typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C //   [
+#  define INTMAX_C   INT64_C
+#endif // INTMAX_C    ]
+#ifndef UINTMAX_C //  [
+#  define UINTMAX_C  UINT64_C
+#endif // UINTMAX_C   ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_STDINT_H_ ]
+
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1600 ]
diff --git a/deps/mdnsd/libmdnsd/sdtxt.c b/deps/mdnsd/libmdnsd/sdtxt.c
new file mode 100644
index 0000000..5e17e05
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/sdtxt.c
@@ -0,0 +1,93 @@
+#include "sdtxt.h"
+#include <stdlib.h>
+#include <string.h>
+
+static size_t _sd2txt_len(const char *key, char *val)
+{
+	size_t ret = strlen(key);
+
+	if (!*val)
+		return ret;
+
+	ret += strlen(val);
+	ret++;
+
+	return ret;
+}
+
+static void _sd2txt_count(xht_t *h, char *key, void *val, void *arg)
+{
+	int *count = (int *)arg;
+
+	*count += (int)_sd2txt_len(key, (char *)val) + 1;
+}
+
+static void _sd2txt_write(xht_t *h, char *key, void *val, void *arg)
+{
+	unsigned char **txtp = (unsigned char **)arg;
+	char *cval = (char *)val;
+
+	/* Copy in lengths, then strings */
+	**txtp = (unsigned char)_sd2txt_len(key, (char *)val);
+	(*txtp)++;
+	memcpy(*txtp, key, strlen(key));
+	*txtp += strlen(key);
+	if (!*cval)
+		return;
+
+	**txtp = '=';
+	(*txtp)++;
+	memcpy(*txtp, cval, strlen(cval));
+	*txtp += strlen(cval);
+}
+
+unsigned char *sd2txt(xht_t *h, int *len)
+{
+	unsigned char *buf, *raw;
+
+	*len = 0;
+
+	xht_walk(h, _sd2txt_count, (void *)len);
+	if (!*len) {
+		*len = 1;
+		buf = (unsigned char *)MDNSD_malloc(1);
+		*buf = 0;
+		return buf;
+	}
+
+	raw = buf = (unsigned char *)MDNSD_malloc((size_t)(*len));
+	xht_walk(h, _sd2txt_write, &buf);
+
+	return raw;
+}
+
+xht_t *txt2sd(const unsigned char *txt, int len)
+{
+	char key[256];
+	xht_t *h = NULL;
+
+	if (txt == 0 || len == 0 || *txt == 0)
+		return NULL;
+
+	h = xht_new(23);
+
+	/* Loop through data breaking out each block, storing into hashtable */
+	for (; len > 0 && *txt <= len; len -= *txt, txt += *txt + 1) {
+		char* val;
+		if (*txt == 0)
+			break;
+
+		memcpy(key, txt + 1, *txt);
+		key[*txt] = 0;
+		if ((val = strchr(key, '=')) != 0) {
+			*val = 0;
+			val++;
+		}
+		if (val != NULL)
+			xht_store(h, key, (int)strlen(key), val, (int)strlen(val));
+		if (*txt +1 > len)
+		    break;
+	}
+
+	return h;
+}
diff --git a/deps/mdnsd/libmdnsd/sdtxt.h b/deps/mdnsd/libmdnsd/sdtxt.h
new file mode 100644
index 0000000..403a82a
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/sdtxt.h
@@ -0,0 +1,24 @@
+#ifndef MDNS_SDTXT_H_
+#define MDNS_SDTXT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "xht.h"
+
+/**
+ * returns hashtable of strings from the SD TXT record rdata
+ */
+xht_t MDNSD_EXPORT *txt2sd(const unsigned char *txt, int len);
+
+/**
+ * returns a raw block that can be sent with a SD TXT record, sets length
+ */
+unsigned char MDNSD_EXPORT *sd2txt(xht_t *h, int *len);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif	/* MDNS_SDTXT_H_ */
diff --git a/deps/mdnsd/libmdnsd/xht.c b/deps/mdnsd/libmdnsd/xht.c
new file mode 100644
index 0000000..ca3a11a
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/xht.c
@@ -0,0 +1,182 @@
+#include "xht.h"
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct xhn {
+	char flag;
+	struct xhn *next;
+	char *key;
+	void *val;
+} xhn_t;
+
+struct xht {
+	int prime;
+	xhn_t *zen;
+};
+
+/* Generates a hash code for a string.
+ * This function uses the ELF hashing algorithm as reprinted in 
+ * Andrew Binstock, "Hashing Rehashed," Dr. Dobb's Journal, April 1996.
+ */
+static int _xhter(const char *s)
+{
+	/* ELF hash uses unsigned chars and unsigned arithmetic for portability */
+	const unsigned char *name = (const unsigned char *)s;
+	unsigned long int h = 0;
+
+	while (*name) {		/* do some fancy bitwanking on the string */
+		unsigned long int g;
+		h = (h << 4) + (unsigned long int)(*name++);
+		if ((g = (h & 0xF0000000UL)) != 0)
+			h ^= (g >> 24);
+		h &= ~g;
+
+	}
+
+	return (int)h;
+}
+
+
+static xhn_t *_xht_node_find(xhn_t *n, const char *key)
+{
+	for (; n != 0; n = n->next)
+		if (n->key != 0 && strcmp(key, n->key) == 0)
+			return n;
+	return 0;
+}
+
+
+xht_t *xht_new(int prime)
+{
+	xht_t *xnew;
+
+	xnew = (xht_t *)MDNSD_malloc(sizeof(struct xht));
+	xnew->prime = prime;
+	xnew->zen = (xhn_t *)MDNSD_calloc(1, (sizeof(struct xhn) * (size_t)prime));	/* array of xhn_t size of prime */
+
+	return xnew;
+}
+
+/* does the set work, used by xht_set and xht_store */
+static xhn_t *_xht_set(xht_t *h, char *key, void *val, char flag)
+{
+	int i;
+	xhn_t *n;
+
+	/* get our index for this key */
+	i = _xhter(key) % h->prime;
+
+	/* check for existing key first, or find an empty one */
+	if ((n = _xht_node_find(&h->zen[i], key)) == 0) {
+		for (n = &h->zen[i]; n != 0; n = n->next) {
+			if (n->val == 0)
+				break;
+		}
+	}
+
+	/* if none, make a new one, link into this index */
+	if (n == NULL) {
+		if (h->zen != NULL) {
+			n = (xhn_t *)MDNSD_malloc(sizeof(struct xhn));
+			n->next = NULL;
+			n->next = h->zen[i].next;
+			h->zen[i].next = n;
+		}
+	} else if (n->flag) {
+		/* When flag is set, we manage their mem and free em first */
+		MDNSD_free((void *)n->key);
+		MDNSD_free(n->val);
+	}
+
+	if (n != NULL) {
+		n->flag = flag;
+		n->key = key;
+		n->val = val;
+	} else {
+		MDNSD_free(key);
+		MDNSD_free(val);
+	}
+
+	return n;
+}
+
+void xht_set(xht_t *h, char *key, void *val)
+{
+	if (h == 0 || key == 0)
+		return;
+	_xht_set(h, key, val, 0);
+}
+
+void xht_store(xht_t *h, char *key, int klen, void *val, int vlen)
+{
+	char *ckey, *cval;
+
+	if (h == 0 || key == 0 || klen == 0)
+		return;
+
+	ckey = (char *)MDNSD_malloc((size_t)klen + 1);
+	memcpy(ckey, key, (size_t)klen);
+	ckey[klen] = '\0';
+	cval = (char *)MDNSD_malloc((size_t)vlen + 1);
+	memcpy(cval, val, (size_t)vlen);
+	cval[vlen] = '\0';	/* convenience, in case it was a string too */
+	_xht_set(h, ckey, cval, 1);
+}
+
+
+void *xht_get(xht_t *h, char *key)
+{
+	xhn_t *n;
+
+	if (h == 0 || key == 0 || (n = _xht_node_find(&h->zen[_xhter(key) % h->prime], key)) == 0)
+		return 0;
+
+	return n->val;
+}
+
+
+void xht_free(xht_t *h)
+{
+	int i;
+	xhn_t *n, *f;
+
+	if (h == 0)
+		return;
+
+	for (i = 0; i < h->prime; i++) {
+		if ((n = (&h->zen[i])) == NULL)
+			continue;
+		if (n->flag) {
+			MDNSD_free((void *)n->key);
+			MDNSD_free(n->val);
+		}
+		for (n = (&h->zen[i])->next; n != 0;) {
+			f = n->next;
+			if (n->flag) {
+				MDNSD_free((void *)n->key);
+				MDNSD_free(n->val);
+			}
+			MDNSD_free(n);
+			n = f;
+		}
+	}
+
+	MDNSD_free(h->zen);
+	MDNSD_free(h);
+}
+
+void xht_walk(xht_t *h, xht_walker w, void *arg)
+{
+	int i;
+	xhn_t *n;
+
+	if (h == 0 || w == 0)
+		return;
+
+	for (i = 0; i < h->prime; i++) {
+		for (n = &h->zen[i]; n != 0; n = n->next) {
+			if (n->key != 0 && n->val != 0)
+				(*w)(h, n->key, n->val, arg);
+		}
+	}
+}
diff --git a/deps/mdnsd/libmdnsd/xht.h b/deps/mdnsd/libmdnsd/xht.h
new file mode 100644
index 0000000..745611d
--- /dev/null
+++ b/deps/mdnsd/libmdnsd/xht.h
@@ -0,0 +1,53 @@
+/*  Simple string->void* hashtable, very static and bare minimal, but efficient */
+#ifndef MDNS_XHT_H_
+#define MDNS_XHT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mdnsd_config.h"
+typedef struct xht xht_t;
+
+/**
+ * must pass a prime#
+ */
+xht_t MDNSD_EXPORT *xht_new(int prime);
+
+/**
+ * caller responsible for key storage, no copies made
+ *
+ * set val to NULL to clear an entry, memory is reused but never free'd
+ * (# of keys only grows to peak usage)
+ *
+ * Note: don't free it b4 xht_free()!
+ */
+void MDNSD_EXPORT xht_set(xht_t *h, char *key, void *val);
+
+/**
+ * Unlike xht_set() where key/val is in caller's mem, here they are
+ * copied into xht and free'd when val is 0 or xht_free()
+ */
+void MDNSD_EXPORT xht_store(xht_t *h, char *key, int klen, void *val, int vlen);
+
+/**
+ * returns value of val if found, or NULL
+ */
+void MDNSD_EXPORT *xht_get(xht_t *h, char *key);
+
+/**
+ * free the hashtable and all entries
+ */
+void MDNSD_EXPORT xht_free(xht_t *h);
+
+/**
+ * pass a function that is called for every key that has a value set
+ */
+typedef void (*xht_walker)(xht_t *h, char *key, void *val, void *arg);
+void xht_walk(xht_t *h, xht_walker w, void *arg);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif	/* MDNS_XHT_H_ */
-- 
2.17.1

