Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: services/mDNS/mDNSResponder.cpp
- Revision:
- 4:d9f5c4abc5f8
- Parent:
- 3:e1d86543ec50
- Child:
- 5:8e53abda9900
diff -r e1d86543ec50 -r d9f5c4abc5f8 services/mDNS/mDNSResponder.cpp
--- a/services/mDNS/mDNSResponder.cpp Tue Jul 27 17:22:17 2010 +0000
+++ b/services/mDNS/mDNSResponder.cpp Sat Jul 31 14:30:45 2010 +0000
@@ -1,470 +1,498 @@
-/* Class: mDNSResponder
- * Copyright 1991, 2003, 2010 Dirk-Willem van Gulik <dirkx(at)apache(punto)org>
- *
- * License: Any BSD or ASF License.
- *
- * Rough and ready port of some mDNS code.
- *
- * Typical use is something like
- *
- * EthernetNetIf eth;
- * HTTPServer svr;
- * mDNSResponder mdns;
- *
- * int main()...
- *
- * // get internet
- * EthernetErr ethErr = eth.setup();
- * ... etc ..
- *
- * // set up some server
- * svr.addHandler<SimpleHandler>("/"); //Default handler
- * svr.bind(80); * *
-
- * // Extract the IP address.
- * IpAddr ip = eth.getIp();
- * printf("mbed IP Address is %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]);
- *
- * // Announce ourselves.
- * mdns.announce(ip, "fred", "_http._tcp", 80, "The Little Server that Could", "path=/demo");
- *
- * while()... enter some run loop
- * ...
- *
- * This will cause http://fred.local./demo to be announced as 'The Little Server that Could'.
- *
- * Or as another example: (http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt)
- * and the various RFCs:
- *
- * mdns.announce(ip, "_ssh._tcp", 22, SSH to Serial Gateway", NULL);
- *
- * CAVEAT - a lot of the buffer overrun and copy checks
- * where removed; and this is not anywhere near
- * threadsafve or sane. Not for production use.
- */
-#include "lwip/opt.h"
-#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
-
-#include "mDNSResponder.h"
-#include <stdio.h>
-
-#ifndef DNS_RRTYPE_SRV
-#define DNS_RRTYPE_SRV (33)
-#endif
-
-mDNSResponder::mDNSResponder() : NetService(false),
- moi_txt(NULL), moi_port(0), moi_ip(0),
- moi_name(NULL), moi_proto(NULL),
- moi_local_proto(NULL), moi_local_name(NULL), moi_local_pglue(NULL),
- announcer() {
- // nothing yet...
-}
-
-mDNSResponder::~mDNSResponder() {
- announcer.stop();
- close();
-}
-
-void mDNSResponder::announce(IpAddr ip, const char * ldn, const char * proto, uint16_t port, const char * name, char ** txt) {
- Host localhost(IpAddr(MCAST), MDNS_PORT, NULL /* fqdn */);
-
- m_pUDPSocket = new UDPSocket;
- m_pUDPSocket->bind(localhost);
-
- m_pUDPSocket->setOnEvent(this, &mDNSResponder::onUDPSocketEvent);
-
- moi_port = port;
- moi_txt = txt;
- moi_ip = ip;
- moi_proto = proto;
- moi_name = name;
-
- moi_local_proto = (char *)malloc(128);
- moi_local_name = (char *)malloc(128);
- moi_local_pglue = (char *)malloc(128);
-
-#define LOCAL "local"
- snprintf(moi_local_proto,128,"%s.%s.", moi_proto, LOCAL);
- snprintf(moi_local_name,128,"%s.%s.", ldn, LOCAL);
- snprintf(moi_local_pglue,128,"%s.%s.%s.", moi_name,moi_proto, LOCAL);
-
- // Gratuis intro - and repeat such regularly..
- //
- mDNSResponder::sendReply(DNS_RRTYPE_PTR, 0, localhost);
- announcer.start();
-}
-
-void mDNSResponder::close() {
- m_pUDPSocket->resetOnEvent();
- m_pUDPSocket->close();
- delete m_pUDPSocket;
-}
-
-
-
-void mDNSResponder::poll() { //Called by NetServices
- if (announcer.read_ms() > 1000 * MDNS_INTERVAL) {
-
- Host mch(IpAddr(MCAST), MDNS_PORT, NULL);
- mDNSResponder::sendReply(DNS_RRTYPE_PTR, 0, mch);
-
- announcer.reset();
- }
-}
-
-char * index(char * str, char c) {
- for (;str && *str;str++) {
- if (*str == c) return str;
- };
- return NULL;
-}
-
-#ifndef THREADINGCOMPRESS
-#ifndef MAXCACHEDNSLABELS
-#define MAXCACHEDNSLABELS (10)
-#endif
-static int labelCacheCnt = 0;
-static uint16_t _cache_off[MAXCACHEDNSLABELS];
-static char * _cache_och[MAXCACHEDNSLABELS];
-static char * labelCacheOffset = 0;
-
-void initLabelCache(char * off) {
- labelCacheOffset = off;
- labelCacheCnt = 0;
- memset(_cache_off,0, sizeof(_cache_off)); // Rely on min value to be 12
-};
-
-uint16_t checkLabelCache(char * name) {
- for (int i=0; i < MAXCACHEDNSLABELS; i++)
- if (_cache_off[i] && !strcmp(_cache_och[i],name))
- return _cache_off[i];
- return 0;
-};
-
-void addLabelCache(char * p, char * name) {
- _cache_off[labelCacheCnt] = p - labelCacheOffset;
- _cache_och[labelCacheCnt] = name;
-
- labelCacheCnt++;
- // we intentionally do not wack the first entries - as they are the most
- // likely ones to be used.
- if (labelCacheCnt>=MAXCACHEDNSLABELS)
- labelCacheCnt = MAXCACHEDNSLABELS / 3;
-};
-#else
-#define initLabelCache(x) {} /* Ignored */
-#define checkLabelCache(x) (0) /* never a hit */
-#define addLabelCache(x,y) {} /* Ignored */
-#endif
-
-char * breakname(char *p, char *name) {
- int l = 0, de = 1;
- char * q;
- uint16_t off;
-
- do {
- if ((off = checkLabelCache(name))) {
- *p++ = 192 + (off >> 8);
- *p++ = (off & 0xFF);
- return p;
- } else {
- addLabelCache(p, name);
- };
-
- q = index(name,'.');
- if (!q) {
- q = name + strlen(name);
- de = 0;
- };
- l = q - name;
- *p++ = l;
- memcpy(p, name, l);
- p+=l;
- name = q + 1;
- } while (l && *name && de);
-
- // terminating root field if any (not the case for
- // things like TXTs).
- if (de) *p++ = 0;
- return p;
-}
-
-char * mRR(char *p, char * name, uint16_t tpe, uint16_t cls, uint32_t ttl) {
- uint16_t i = 0;
- uint32_t j = 0;
-
- p = breakname(p, name);
-
- // NOTE: Cannot assume proper byte boundaries.
- //
- i = htons(tpe); // Type
- memcpy(p, &i, 2);
- p+=2;
-
- i = htons(cls); // Class
- memcpy(p, &i, 2);
- p+=2;
-
- j = htonl(ttl); // TTL (4 bytes)
- memcpy(p, &j, 4);
- p+=4;
-
- return p;
-}
-
-char * mRRLABEL(char *p, char * name, uint16_t tpe, char ** rr) {
- uint16_t i;
-
- p = mRR(p, name, tpe, DNS_RRCLASS_IN, MDNS_TTL); // Type, IN, TTL
-
- // RR String
- char * q = p + 2;
-
- for (;*rr;rr++)
- q = breakname(q, *rr);
-
- i = htons(q - p - 2); // RDLEN
- memcpy(p, &i, 2);
- return q;
-}
-
-char * mPTR(char *p, char * name, char * r) {
- char *rr[] = { r, NULL };
- return mRRLABEL(p, name, DNS_RRTYPE_PTR, rr );
-}
-
-char * mTXT(char *p, char * name, char ** rr) {
- return mRRLABEL(p, name, DNS_RRTYPE_TXT, rr);
-}
-
-char * mARR(char *p, char * name, IpAddr ip) {
- uint16_t i;
-
- p = mRR(p, name, DNS_RRTYPE_A, DNS_RRCLASS_IN, MDNS_TTL ); // A, IN
-
- i = htons(4); // RDLEN - we're just doing a single IPv4 address - our primary link ?
- memcpy(p, &i, 2);
-
- // IP already in network order.
- memcpy(p+2, &ip, 4);
-
- return p + 2 + 4;
-}
-
-char * mSRV(char *p, char * name, uint16_t port, char * rr) {
- uint16_t i = 0;
- char * q;
-
- p = mRR(p, name, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, MDNS_TTL); // SRV, IN
-
- // Keep space for RDLEN
- q = p;
- p+=2;
-
- i = htons(1); // Priority
- memcpy(p, &i, 2);
- p+=2;
-
- i = htons(0); // Weight (see rfc2782)
- memcpy(p, &i, 2);
- p+=2;
-
- i = htons(port); // Port
- memcpy(p, &i, 2);
- p+=2;
-
- p = breakname(p, rr);
-
- i = htons(p - q - 2); // RDLEN
- memcpy(q, &i, 2);
-
- return p;
-}
-
-char * qANY(char *p, char * name, uint16_t tpe, uint16_t cls) {
- uint16_t i = 0;
- p = breakname(p, name); // QNAME
-
- i = htons(tpe); // QTYPE
- memcpy(p, &i, 2);
- p+=2;
-
- i = htons(cls); // QCLASS
- memcpy(p, &i, 2);
- p+=2;
-
- return p;
-}
-
-char * qPTR(char *p, char *name) {
- return qANY(p,name,DNS_RRTYPE_PTR,DNS_RRCLASS_IN);
-}
-
-char * qA(char *p, char *name) {
- return qANY(p,name,DNS_RRTYPE_A,DNS_RRCLASS_IN);
-}
-
-char * qSRV(char *p, char *name) {
- return qANY(p,name,DNS_RRTYPE_SRV,DNS_RRCLASS_IN);
-}
-
-void mDNSResponder::sendReply(uint16_t t, uint16_t tid, Host dst) {
- // sent a reply - basically a precooked A/PTR/SRV with the TransactionID
- // and my URI squeezed in.
- char out[ 1500 ], *p;
- DNSPacket * op = (DNSPacket *) out;
-
- initLabelCache(out); // Offsets are relative to the ID header.
-
- op->tid = ntohs(tid);
- op->flags = ntohs(0x8000U); // Answer
- op->question_count = ntohs(1); // Courtesy copy of the question.
- op->answer_count = ntohs(1); // The record asked for
- op->a_count = ntohs(0);
- p = out + 12;
-
- switch (t) {
- case DNS_RRTYPE_PTR: // PTR record, SRV, optional TXT and A with my own address.
- op->aa_count = ntohs(moi_txt ? 3 : 2);
- p = qPTR(p,moi_local_proto);
-
- p = mPTR(p,moi_local_proto, moi_local_pglue);
- if (moi_txt && * moi_txt)
- p = mTXT(p,moi_local_pglue,moi_txt);
-
- break;
- case DNS_RRTYPE_A: // A record (and nothing else)
- op->aa_count = ntohs(1);
- p = qA(p,moi_local_name);
- break;
-
- case DNS_RRTYPE_SRV: // SRV record and a gratious A record to complete.
- op->aa_count = ntohs(moi_txt ? 2 : 1);
- p = qSRV(p,moi_local_pglue);
-
- p = mPTR(p,moi_local_proto, moi_local_pglue);
- if (moi_txt && * moi_txt)
- p = mTXT(p,moi_local_pglue,moi_txt);
- break;
- }
- p = mARR(p,moi_local_name,moi_ip);
- m_pUDPSocket->sendto(out,p-out,&dst);
-}
-
-char * decodeQBlock(char * udp, int len, char *p, char * fqdn, int fqdn_size, uint32_t *Qclass, uint32_t *Qtype) {
- char * endp = udp + len;
- int depth = 0;
- int l, ll = 0;
- char * q = 0;
- char * ep = fqdn;
-
- do {
- while (((l = *p++) < 192) && (l > 0)) {
- if (p + l >= endp) {
- printf("RR longer than packet\r\n");
- return NULL;
- };
-
- if (ll + l + 1 > fqdn_size) {
- printf("FQDN too long\r\n");
- return NULL;
- }
-
- memcpy(ep,p,l);
- ep[l]='.';
- ep += l + 1;
- p += l;
- ll += l + 1;
- };
-
- if (l >= 192) {
- // construct an offset pointer by wiping the top 1 bits
- // and then getting the remaining 8 bytes to make 14.
- l -= 192;
- l = l << 8;
- l += *p++;
-
- // rescue our reference; as we've not gotten the Qt's yet
- // and these follow the last entry in our record (and not
- // that from the compressed values.
- //
- if (!q) q = p;
-
- // printf(" [->%d] ",l);
- p = udp + l;
-
- if (p >= udp + len || p < udp + 12) {
- printf("Pointer to wrong place\r\n");
- return NULL;
- };
- };
-
- if (depth++ >= 128) {
- printf("Far too deep\r\n");
- return NULL;
- };
- } while (l);
-
- // Terminate the FQDN string.
- *ep = 0;
-
- // in case no compression was used at all.
- if (!q)
- q = p;
-
- *Qtype = htons(q[0] + q[1]*256);
- *Qclass = htons(q[2] + q[3]*256);
- return p;
-}
-
-void mDNSResponder::onUDPSocketEvent(UDPSocketEvent e) {
- char udp[ 1500 ];
- Host from;
- int len;
-
- //The only event for now
- if (e != UDPSOCKET_READABLE)
- return;
-
- // parse through the packet; find any PTR requests
- // and respond to any for _http._tcp._local.
- //
- while ((len = m_pUDPSocket->recvfrom( (char*)&udp, sizeof(udp), &from ))) {
- DNSPacket * dp = (DNSPacket *) udp;
-
- unsigned int tid = ntohs(dp->tid);
- unsigned int flags = ntohs(dp->flags);
- unsigned int nQ = ntohs(dp->question_count);
-
- if (flags & 2 != 0 || nQ < 1)
- continue; // we only want questions
-
- // assume nQ NON terminated fields followed by Qtype & Qclass
- //
- char * p = udp + 12;
- for (int i = 0; i < nQ; i++) {
- char fqdn[ 256 ];
- uint32_t Qt, Qc;
-
- if (!((p = decodeQBlock(udp,len,p,fqdn,sizeof(fqdn),&Qc, &Qt))))
- return;
-
- // We only deal with INternet.
- //
- if (Qc != DNS_RRCLASS_IN)
- continue;
-
- printf("Q type %d for %s\r\n", Qt, fqdn);
-
- // We want PTR records on the INternet of our type. Also do ANY.
- //
- if ((Qt == DNS_RRTYPE_PTR || Qt == 255) && !(strcmp(fqdn,moi_local_proto)))
- sendReply(DNS_RRTYPE_PTR,tid,from);
- else if ((Qt == DNS_RRTYPE_A || Qt == 255) && !(strcmp(fqdn,moi_local_name)))
- sendReply(DNS_RRTYPE_A,tid,from);
- else if ((Qt == DNS_RRTYPE_SRV || Qt == 255) && !(strcmp(fqdn,moi_local_pglue)))
- sendReply(DNS_RRTYPE_SRV,tid,from);
- };
- }
-}
-#endif
+/* Class: mDNSResponder
+ * Copyright 1991, 2003, 2010 Dirk-Willem van Gulik <dirkx(at)apache(punto)org>
+ *
+ * License: Any BSD or ASF License.
+ *
+ * Rough and ready port of some mDNS code.
+ *
+ * Typical use is something like
+ *
+ * EthernetNetIf eth;
+ * HTTPServer svr;
+ * mDNSResponder mdns;
+ *
+ * int main()...
+ *
+ * // get internet
+ * EthernetErr ethErr = eth.setup();
+ * ... etc ..
+ *
+ * // set up some server
+ * svr.addHandler<SimpleHandler>("/"); //Default handler
+ * svr.bind(80); * *
+
+ * // Extract the IP address.
+ * IpAddr ip = eth.getIp();
+ * printf("mbed IP Address is %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]);
+ *
+ * // Announce ourselves.
+ * mdns.announce(ip, "fred", "_http._tcp", 80, "The Little Server that Could", "path=/demo");
+ *
+ * while()... enter some run loop
+ * ...
+ *
+ * This will cause http://fred.local./demo to be announced as 'The Little Server that Could'.
+ *
+ * Or as another example: (http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt)
+ * and the various RFCs:
+ *
+ * mdns.announce(ip, "_ssh._tcp", 22, SSH to Serial Gateway", NULL);
+ *
+ * CAVEAT - a lot of the buffer overrun and copy checks
+ * where removed; and this is not anywhere near
+ * threadsafve or sane. Not for production use.
+ */
+#include "lwip/opt.h"
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "mDNSResponder.h"
+#include <stdio.h>
+
+#ifndef DNS_RRTYPE_SRV
+#define DNS_RRTYPE_SRV (33)
+#endif
+
+#ifndef DNS_LOCAL
+#define DNS_LOCAL "local"
+#endif
+
+mDNSResponder::mDNSResponder() : NetService(false),
+ moi_txt(NULL), moi_port(0), moi_ip(0),
+ moi_name(NULL), moi_proto(NULL),
+ moi_local_proto(NULL), moi_local_name(NULL), moi_local_pglue(NULL),
+#ifdef MDNS_QCACHE
+ labelCacheCnt(0), labelCacheOffset(0),
+#endif
+ announcer()
+{
+ initLabelCache(0);
+}
+
+mDNSResponder::~mDNSResponder() {
+ announcer.stop();
+ close();
+}
+
+void mDNSResponder::announce(IpAddr ip, const char * ldn, const char * proto, uint16_t port, const char * name, char ** txt) {
+ Host localhost(IpAddr(MCAST), MDNS_PORT, NULL /* fqdn */);
+
+ m_pUDPSocket = new UDPSocket;
+ m_pUDPSocket->bind(localhost);
+
+ m_pUDPSocket->setOnEvent(this, &mDNSResponder::onUDPSocketEvent);
+
+ moi_port = port;
+ moi_txt = txt;
+ moi_ip = ip;
+ moi_proto = proto;
+ moi_name = name;
+
+ moi_local_proto = (char *)malloc(128);
+ moi_local_name = (char *)malloc(128);
+ moi_local_pglue = (char *)malloc(128);
+
+ snprintf(moi_local_proto,128,"%s.%s.", moi_proto, DNS_LOCAL);
+ snprintf(moi_local_name,128,"%s.%s.", ldn, DNS_LOCAL);
+ snprintf(moi_local_pglue,128,"%s.%s.%s.", moi_name,moi_proto, DNS_LOCAL);
+
+ // Gratuis intro - and repeat such regularly.. taking some
+ // care to not go beyond DHCP limits - but not more often
+ // than makes sense for our TTL. See the .h file for the
+ // various tradeoffs.
+ //
+ mDNSResponder::sendReply(DNS_RRTYPE_PTR, 0, localhost);
+ announcer.start();
+}
+
+void mDNSResponder::close() {
+ m_pUDPSocket->resetOnEvent();
+ m_pUDPSocket->close();
+ delete m_pUDPSocket;
+}
+
+void mDNSResponder::poll() { //Called by NetServices
+ if (announcer.read_ms() > 1000 * MDNS_INTERVAL) {
+
+ Host mch(IpAddr(MCAST), MDNS_PORT, NULL);
+ mDNSResponder::sendReply(DNS_RRTYPE_PTR, 0, mch);
+
+ announcer.reset();
+ }
+}
+
+char * index(char * str, char c) {
+ for (;str && *str;str++) {
+ if (*str == c) return str;
+ };
+ return NULL;
+}
+
+#ifdef MDNS_QCACHE
+void mDNSResponder::initLabelCache(char * off) {
+ labelCacheOffset = off;
+ labelCacheCnt = 0;
+ memset(_cache_off,0, sizeof(_cache_off)); // Rely on min value to be 12
+};
+
+uint16_t mDNSResponder::checkLabelCache(char * name) {
+ // Bail out if not initialized with a valid (>12) offset.
+ //
+ if (!labelCacheOffset)
+ return 0;
+
+ for (int i=0; i < MDNS_MAXCACHEDNSLABELS; i++)
+ if (_cache_off[i] && !strcmp(_cache_och[i],name))
+ return _cache_off[i];
+ return 0;
+};
+
+void mDNSResponder::addLabelCache(char * p, char * name) {
+ // Bail out if not initialized with a valid (>12) offset.
+ //
+ if (!labelCacheOffset)
+ return;
+
+ _cache_off[labelCacheCnt] = p - labelCacheOffset;
+ _cache_och[labelCacheCnt] = name;
+
+ labelCacheCnt++;
+ // we intentionally do not wack the first entries - as they are the most
+ // likely ones to be used.
+ if (labelCacheCnt>= MDNS_MAXCACHEDNSLABELS)
+ labelCacheCnt = MDNS_MAXCACHEDNSLABELS / 3;
+};
+#else
+#define initLabelCache(x) {} /* Ignored */
+#define checkLabelCache(x) (0) /* never a hit */
+#define addLabelCache(x,y) {} /* Ignored */
+#endif
+
+char * mDNSResponder::breakname(char *p, char *name) {
+ int l = 0, de = 1;
+ char * q;
+ uint16_t off;
+
+ do {
+ if ((off = checkLabelCache(name)) != 0) {
+ *p++ = 192 + (off >> 8);
+ *p++ = (off & 0xFF);
+ return p;
+ } else {
+ addLabelCache(p, name);
+ };
+
+ q = index(name,'.');
+ if (!q) {
+ q = name + strlen(name);
+ de = 0;
+ };
+ l = q - name;
+ *p++ = l;
+ memcpy(p, name, l);
+ p+=l;
+ name = q + 1;
+ } while (l && *name && de);
+
+ // terminating root field if any (not the case for
+ // things like TXTs).
+ if (de) *p++ = 0;
+ return p;
+}
+
+char * mDNSResponder::mRR(char *p, char * name, uint16_t tpe, uint16_t cls, uint32_t ttl) {
+ uint16_t i = 0;
+ uint32_t j = 0;
+
+ p = breakname(p, name);
+
+ // NOTE: Cannot assume proper byte boundaries; so
+ // we assume the compiler does not allow that for
+ // casts - and write it out with memcpy's
+ //
+ i = htons(tpe); // Type
+ memcpy(p, &i, 2);
+ p+=2;
+
+ i = htons(cls); // Class
+ memcpy(p, &i, 2);
+ p+=2;
+
+ j = htonl(ttl); // TTL (4 bytes)
+ memcpy(p, &j, 4);
+ p+=4;
+
+ return p;
+}
+
+char * mDNSResponder::mRRLABEL(char *p, char * name, uint16_t tpe, char ** rr) {
+ uint16_t i = 0;
+
+ p = mRR(p, name, tpe, DNS_RRCLASS_IN, MDNS_TTL); // Type, IN, TTL
+
+ // RR String
+ char * q = p + 2;
+
+ for (;*rr;rr++)
+ q = breakname(q, *rr);
+
+ i = htons(q - p - 2); // RDLEN
+ memcpy(p, &i, 2);
+ return q;
+}
+
+char * mDNSResponder::mPTR(char *p, char * name, char * r) {
+ char *rr[] = { r, NULL };
+ return mRRLABEL(p, name, DNS_RRTYPE_PTR, rr );
+}
+
+char * mDNSResponder::mTXT(char *p, char * name, char ** rr) {
+ return mRRLABEL(p, name, DNS_RRTYPE_TXT, rr);
+}
+
+char * mDNSResponder::mARR(char *p, char * name, IpAddr ip) {
+ uint16_t i = 0;
+
+ p = mRR(p, name, DNS_RRTYPE_A, DNS_RRCLASS_IN, MDNS_TTL ); // A, IN
+
+ i = htons(4); // RDLEN - we're just doing a single IPv4 address - our primary link ?
+ memcpy(p, &i, 2);
+
+ // IP already in network order.
+ memcpy(p+2, &ip, 4);
+
+ return p + 2 + 4;
+}
+
+char * mDNSResponder::mSRV(char *p, char * name, uint16_t port, char * rr) {
+ uint16_t i = 0;
+ char * q;
+
+ p = mRR(p, name, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, MDNS_TTL); // SRV, IN
+
+ // Keep space for RDLEN
+ q = p;
+ p+=2;
+
+ i = htons(1); // Priority
+ memcpy(p, &i, 2);
+ p+=2;
+
+ i = htons(0); // Weight (see rfc2782)
+ memcpy(p, &i, 2);
+ p+=2;
+
+ i = htons(port); // Port
+ memcpy(p, &i, 2);
+ p+=2;
+
+ p = breakname(p, rr);
+
+ i = htons(p - q - 2); // RDLEN
+ memcpy(q, &i, 2);
+
+ return p;
+}
+
+char * mDNSResponder::qANY(char *p, char * name, uint16_t tpe, uint16_t cls) {
+ uint16_t i = 0;
+ p = breakname(p, name); // QNAME
+
+ i = htons(tpe); // QTYPE
+ memcpy(p, &i, 2);
+ p+=2;
+
+ i = htons(cls); // QCLASS
+ memcpy(p, &i, 2);
+ p+=2;
+
+ return p;
+}
+
+char * mDNSResponder::qPTR(char *p, char *name) {
+ return qANY(p,name,DNS_RRTYPE_PTR,DNS_RRCLASS_IN);
+}
+
+char * mDNSResponder::qA(char *p, char *name) {
+ return qANY(p,name,DNS_RRTYPE_A,DNS_RRCLASS_IN);
+}
+
+char * mDNSResponder::qSRV(char *p, char *name) {
+ return qANY(p,name,DNS_RRTYPE_SRV,DNS_RRCLASS_IN);
+}
+
+void mDNSResponder::mDNSResponder::sendReply(uint16_t t, uint16_t tid, Host dst) {
+ // sent a reply - basically a precooked A/PTR/SRV with the TransactionID
+ // and my URI squeezed in.
+ char out[ 1500 ], *p;
+ DNSPacket * op = (DNSPacket *) out;
+
+ initLabelCache(out); // Offsets are relative to the ID header.
+
+ op->tid = ntohs(tid);
+ op->flags = ntohs(0x8000U); // Answer
+ op->question_count = ntohs(1); // Courtesy copy of the question.
+ op->answer_count = ntohs(1); // The record asked for
+ op->a_count = ntohs(0);
+ p = out + 12;
+
+ switch (t) {
+ case DNS_RRTYPE_PTR: // PTR record, SRV, optional TXT and A with my own address.
+ op->aa_count = ntohs(moi_txt ? 3 : 2);
+ p = qPTR(p,moi_local_proto);
+
+ p = mPTR(p,moi_local_proto, moi_local_pglue);
+ if (moi_txt && * moi_txt)
+ p = mTXT(p,moi_local_pglue,moi_txt);
+
+ break;
+ case DNS_RRTYPE_A: // A record (and nothing else)
+ op->aa_count = ntohs(1);
+ p = qA(p,moi_local_name);
+ break;
+
+ case DNS_RRTYPE_SRV: // SRV record and a gratious A record to complete.
+ op->aa_count = ntohs(moi_txt ? 2 : 1);
+ p = qSRV(p,moi_local_pglue);
+
+ p = mPTR(p,moi_local_proto, moi_local_pglue);
+ if (moi_txt && * moi_txt)
+ p = mTXT(p,moi_local_pglue,moi_txt);
+ break;
+ }
+ p = mARR(p,moi_local_name,moi_ip);
+ m_pUDPSocket->sendto(out,p-out,&dst);
+ DBG("Reply type %d sent to %s\r\n",t,dst);
+}
+
+char * mDNSResponder::decodeQBlock(char * udp, int len, char *p, char * fqdn, int fqdn_size, uint32_t *Qclass, uint32_t *Qtype) {
+ char * endp = udp + len;
+ int depth = 0;
+ int l, ll = 0;
+ char * q = 0;
+ char * ep = fqdn;
+
+ do {
+ while (((l = *p++) < 192) && (l > 0)) {
+ if (p + l >= endp) {
+ DBG("Malformed packet or bug, RR field larger than packet itself\n\r");
+ return NULL;
+ };
+
+ if (ll + l + 1 > fqdn_size) {
+ DBG("Malformed packet or bug, FQDN exceeds %d bytes\n\r", fqdn_size);
+ return NULL;
+ }
+
+ memcpy(ep,p,l);
+ ep[l]='.';
+ ep += l + 1;
+ p += l;
+ ll += l + 1;
+ };
+
+ if (l >= 192) {
+ // construct an offset pointer by wiping the top 1 bits
+ // and then getting the remaining 8 bytes to make 14.
+ l -= 192;
+ l = l << 8;
+ l += *p++;
+
+ // rescue our reference; as we've not gotten the Qt's yet
+ // and these follow the last entry in our record (and not
+ // that from the compressed values.
+ //
+ if (!q) q = p;
+
+ // printf(" [->%d] ",l);
+ p = udp + l;
+
+ if (p >= udp + len || p < udp + 12) {
+ DBG("Malformed packet or bug, pointer outside UDP packet\n\r");
+ return NULL;
+ };
+ };
+
+ if (depth++ >= 128) {
+ DBG("Malformed packet or bug, depth too high\n\r");
+ return NULL;
+ };
+ } while (l);
+
+ // Terminate the FQDN string.
+ *ep = 0;
+
+ // in case no compression was used at all.
+ if (!q)
+ q = p;
+
+ *Qtype = htons(q[0] + q[1]*256);
+ *Qclass = htons(q[2] + q[3]*256);
+ return p;
+}
+
+void mDNSResponder::onUDPSocketEvent(UDPSocketEvent e) {
+ char udp[ 1500 ];
+ Host from;
+ int len;
+
+ //The only event for now
+ if (e != UDPSOCKET_READABLE)
+ return;
+
+ // parse through the packet; find any PTR requests
+ // and respond to any for _http._tcp._local.
+ //
+ while ((len = m_pUDPSocket->recvfrom( (char*)&udp, sizeof(udp), &from )) != 0) {
+ DNSPacket * dp = (DNSPacket *) udp;
+
+ unsigned int tid = ntohs(dp->tid);
+ unsigned int flags = ntohs(dp->flags);
+ unsigned int nQ = ntohs(dp->question_count);
+
+ if (flags & 2 != 0 || nQ < 1)
+ continue; // we only want questions
+
+ // assume nQ NON terminated fields followed by Qtype & Qclass
+ //
+ char * p = udp + 12;
+ for (int i = 0; i < nQ; i++) {
+ char fqdn[ 256 ];
+ uint32_t Qt, Qc;
+
+ if ( (p = decodeQBlock(udp,len,p,fqdn,sizeof(fqdn),&Qc, &Qt)) == 0)
+ return;
+
+ // We only deal with INternet.
+ //
+ if (Qc != DNS_RRCLASS_IN)
+ continue;
+
+ DBG("IN query %x for %s\n\r", Qt, fqdn);
+
+ // Normally we'll respond to a mCast query for the PTR of our conceptual
+ // service; to which we reply with the services we run; and then the A
+ // records for our actual endpoint. However if one of the requistors their
+ // cashing is not up to scratch - we may (also) get hit up a bit later for
+ // the secondary records under their logical names. So we respond to
+ // all 3. As this is likely to happen due to resource constraints on the
+ // side of the requestor; we are careful and reply as 'narrow' as possible.
+ //
+ // Note that we also respond to the ANY query - as to make a quick 'dig'
+ // testing command easier.
+ //
+ if ((Qt == DNS_RRTYPE_PTR || Qt == 255) && !(strcmp(fqdn,moi_local_proto)))
+ sendReply(DNS_RRTYPE_PTR,tid,from);
+ else if ((Qt == DNS_RRTYPE_A || Qt == 255) && !(strcmp(fqdn,moi_local_name)))
+ sendReply(DNS_RRTYPE_A,tid,from);
+ else if ((Qt == DNS_RRTYPE_SRV || Qt == 255) && !(strcmp(fqdn,moi_local_pglue)))
+ sendReply(DNS_RRTYPE_SRV,tid,from);
+ else
+ DBG(".. which was ignored\n\r", Qt, fqdn);
+
+ };
+ }
+}
+#endif