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