Uploading this for others to look at
Change-Id: I00d676d580f9bbf2b41566b7f335c54394f2d03a
diff --git a/system/ulib/mdns/include/mdns/mdns.h b/system/ulib/mdns/include/mdns/mdns.h
new file mode 100644
index 0000000..0e42aaf
--- /dev/null
+++ b/system/ulib/mdns/include/mdns/mdns.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MDNS_MAX_DOMAIN_LENGTH 253
+
+typedef enum {
+ MDNS_RTYPE_AAAA = 28,
+ MDNS_RTYPE_TXT = 16
+} mdns_record_type;
+
+typedef struct {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t qcount;
+ uint16_t acount;
+ uint16_t nscount;
+ uint16_t arcount;
+} mdns_header;
+
+typedef struct {
+ mdns_header header;
+ char domain[MDNS_MAX_DOMAIN_LENGTH];
+ uint8_t ip[16];
+} mdns_answer;
+
+typedef struct {
+ mdns_header header;
+ char domain[MDNS_MAX_DOMAIN_LENGTH];
+ mdns_record_type rrclass;
+ bool unicast;
+} mdns_query;
+
+void mdns_send(char* domain, const uint8_t ip[16], uint16_t port, char* iface);
+void mdns_recv(void* data, size_t len, const uint8_t daddr[16], uint16_t dport,
+ const uint8_t saddr[16], uint16_t sport);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/system/ulib/mdns/mdns-example.c b/system/ulib/mdns/mdns-example.c
new file mode 100644
index 0000000..118d01f
--- /dev/null
+++ b/system/ulib/mdns/mdns-example.c
@@ -0,0 +1,2 @@
+
+int main() { }
diff --git a/system/ulib/mdns/mdns.c b/system/ulib/mdns/mdns.c
new file mode 100644
index 0000000..3482771
--- /dev/null
+++ b/system/ulib/mdns/mdns.c
@@ -0,0 +1,139 @@
+#include <arpa/inet.h>
+#include <mdns/mdns.h>
+
+// This assumes that mdns_header's layout agrees with the DNS header spec
+static uint8_t* mdns_build_header(const mdns_header* header, uint8_t* dst,
+ uint8_t* end) {
+ if ((size_t)(end - dst) < sizeof(mdns_header)) return NULL;
+ mdns_header* oheader = (mdns_header*)(void*)dst;
+ oheader->id = htons(header->id);
+ oheader->flags = htons(header->flags);
+ oheader->qcount = htons(header->qcount);
+ oheader->acount = htons(header->acount);
+ oheader->nscount = htons(header->nscount);
+ oheader->arcount = htons(header->arcount);
+ return dst + sizeof(mdns_header);
+}
+
+static uint8_t* mdns_build_domain(const char* domain, uint8_t* dst,
+ uint8_t* end) {
+ if (end - dst < 1) return NULL;
+ uint8_t* size_ptr = dst++;
+ while(*domain) {
+ while(*domain && *domain != '.') {
+ if (dst >= end) return NULL;
+ *dst++ = *domain++;
+ }
+ // Set the size of this part and update location of size_ptr
+ *size_ptr = dst - size_ptr;
+ size_ptr = dst;
+ }
+ // size_ptr should be dst and dst just point one past the end.
+ // we need to add a length zero domain terminator now. dst
+ // could have been exuasted so make sure that we can place the
+ // last byte there.
+ if (size_ptr >= end) return NULL;
+ *size_ptr = 0;
+ return size_ptr + 1;
+}
+
+uint8_t* mdns_build_query(const char* domain, uint8_t* dst, uint8_t* end) {
+ mdns_header header = {.qcount = 1};
+ dst = mdns_build_header(&header, dst, end);
+ if (!dst || dst >= end) return NULL;
+ dst = mdns_build_domain(domain, dst, end);
+ if (!dst || dst >= end) return NULL;
+ if (end - dst < 4) return NULL;
+ *(uint16_t*)(void*)dst = htons(MDNS_RTYPE_AAAA);
+ dst += 2;
+ *(uint16_t*)(void*)dst = htons(1 << 15 | 1); // Unicast=true QCLASS=1
+ dst += 2;
+ return dst;
+}
+
+uint8_t* mdns_build_response(const char* domain, uint8_t ip[16], uint32_t ttl,
+ uint8_t* dst, uint8_t* end) {
+ mdns_header header = {.qcount = 1};
+ dst = mdns_build_header(&header, dst, end);
+ if (!dst || dst >= end) return NULL;
+ dst = mdns_build_domain(domain, dst, end);
+ if (!dst || dst >= end) return NULL;
+ if (end - dst < 10 + 16) return NULL; // Fields plus 16 for the IP.
+ *(uint16_t*)(void*)dst = htons(MDNS_RTYPE_AAAA);
+ dst += 2;
+ *(uint16_t*)(void*)dst = htons(1); // FLUSH = false, RRCLASS = 1
+ dst += 2;
+ *(uint32_t*)(void*)dst = htonl(ttl); // How long client should cache this.
+ dst += 4;
+ *(uint16_t*)(void*)dst = htons(16); // Size of IP address
+ dst += 2;
+ return dst;
+}
+
+static const uint8_t* mdns_parse_short(uint16_t* out, const uint8_t* src,
+ const uint8_t* end) {
+ if (end - src < 2) return NULL;
+ *out = ntohs(*(uint16_t*)(void*)src);
+ return src + 2;
+}
+
+static const uint8_t* mdns_parse_header(mdns_header* out, const uint8_t* src,
+ const uint8_t* end) {
+ if (end - src < 12) return NULL;
+ src = mdns_parse_short(&out->id, src, end);
+ src = mdns_parse_short(&out->flags, src, end);
+ src = mdns_parse_short(&out->qcount, src, end);
+ src = mdns_parse_short(&out->acount, src, end);
+ src = mdns_parse_short(&out->nscount, src, end);
+ src = mdns_parse_short(&out->arcount, src, end);
+ return src;
+}
+
+static const uint8_t* mdns_parse_domain(char* dst, char* dend,
+ const uint8_t* src, const uint8_t* end) {
+ while (src < end && *src) {
+ size_t len = *src++;
+ const uint8_t* pend = src + len;
+ while (src < pend) {
+ if (dst == dend) return NULL;
+ *dst++ = *src++;
+ }
+ // If we're not at the end of the domain add a '.'
+ if (src < end && !*src) {
+ if (dst == dend) return NULL;
+ *dst++ = '.';
+ }
+ }
+ // If we're at the end, don't write the null terminator to match what strncpy
+ // does.
+ if (dst < dend)
+ *dst++ = 0;
+ return src;
+}
+
+const uint8_t* mdns_parse_query(mdns_query* dst, const uint8_t* src,
+ const uint8_t* end) {
+ src = mdns_parse_header(&dst->header, src, end);
+ if (!src || src >= end | dst->header.qcount != 1) return NULL;
+ src = mdns_parse_domain(dst->domain, dst->domain + MDNS_MAX_DOMAIN_LENGTH,
+ src, end);
+ if (!src || end - src < 4) return NULL;
+ uint16_t tmp;
+ src = mdns_parse_short(&tmp, src, end);
+ dst->rrclass = tmp; // TODO: Validate as supported?
+ src = mdns_parse_short(&tmp, src, end);
+ dst->unicast = tmp & 1; // TODO: read QCLASS too
+ return src;
+}
+
+const uint8_t* mdns_parse_answer(mdns_answer* dst, const uint8_t* src,
+ const uint8_t* end) {
+ src = mdns_parse_header(&dst->header, src, end);
+ if (!src || src >= end | dst->header.qcount != 1) return NULL;
+ src = mdns_parse_domain(dst->domain, dst->domain + MDNS_MAX_DOMAIN_LENGTH,
+ src, end);
+ if (!src || end - src < 11) return NULL;
+ // I want to skip a bunch of stuff that is going to be ignored by us anyhow
+ src += 8;
+
+}
diff --git a/system/ulib/mdns/rules.mk b/system/ulib/mdns/rules.mk
new file mode 100644
index 0000000..a189a2e
--- /dev/null
+++ b/system/ulib/mdns/rules.mk
@@ -0,0 +1,45 @@
+# Copyright 2017 The Fuchsia Authors. All rights reserved.
+# # Use of this source code is governed by a BSD-style license that can be
+# # found in the LICENSE file.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := userlib
+
+MODULE_SRCS += $(LOCAL_DIR)/mdns.c
+
+MODULE_EXPORT := a
+
+MODULE_HEADER_DEPS := system/ulib/inet6
+
+MODULE_LIBS := system/ulib/inet6 system/ulib/zircon system/ulib/c
+
+MODULE_COMPILEFLAGS := -DMDNS_USERLIB
+
+include make/module.mk
+
+MODULE := $(LOCAL_DIR).mdns-example
+
+MODULE_TYPE := hostapp
+
+MODULE_SRCS := $(LOCAL_DIR)/mdns.c $(LOCAL_DIR)/mdns-example.c
+
+MODULE_NAME := mdns-example
+
+MODULE_COMPILEFLAGS := -I$(LOCAL_DIR)/include -std=c11 -DTFTP_HOSTLIB
+
+include make/module.mk
+
+#MODULE := $(LOCAL_DIR).hostlib
+
+#MODULE_NAME := mdns
+
+#MODULE_TYPE := hostlib
+
+#MODULE_SRCS := $(LOCAL_DIR)/mdns.c
+
+#MODULE_COMPILEFLAGS := -DMDNS_HOSTLIB
+
+#include make/module.mk