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:
- 0:355018f44c9f
- Child:
- 1:59820ca5c83a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/services/mDNS/mDNSResponder.cpp Wed Jul 21 19:25:56 2010 +0000
@@ -0,0 +1,400 @@
+/* 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, "_http._tcp", 80, "The Little Server that Could", "path=/demo");
+ *
+ * while()... enter some run loop
+ *
+ * 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 "mDNSResponder.h"
+#include <stdio.h>
+
+// #define MCAST 192,168,1,71
+
+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 * proto, uint16_t port, const char * name, const 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.", moi_name, LOCAL);
+ snprintf(moi_local_pglue,128,"%s.%s.%s.", moi_name,moi_proto, LOCAL);
+
+ // Gratuis intro - and repeat such regularly..
+ //
+ mDNSResponder::sendReply(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(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 two - 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);
+
+ // 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;
+ uint32_t j;
+
+ 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, 1, MDNS_TTL); // Type, IN, TTL
+
+ // RR String
+ char * q = p + 2;
+ q = breakname(q, rr);
+
+ i = htons(q - p - 2); // RDLEN
+ memcpy(p, &i, 2);
+ return q;
+}
+
+char * mPTR(char *p, char * name, char * rr) {
+ return mRRLABEL(p, name, 12 /* PTR */, rr);
+}
+
+char * mTXT(char *p, char * name, char * rr) {
+ return mRRLABEL(p, name, 16 /* TXT */, rr);
+}
+
+char * mARR(char *p, char * name, IpAddr ip) {
+ uint16_t i;
+
+ p = mRR(p, name, 1, 1, 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;
+ char * q;
+
+ p = mRR(p, name, 33, 1, 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;
+ 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,12 /* PTR */,1);
+}
+
+
+void mDNSResponder::sendReply(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 PTR record asked for
+ op->a_count = ntohs(0);
+
+ // The 2 or 3 extra records (A, SRV and optional TXT) we know will be needed next
+ op->aa_count = ntohs(moi_txt ? 3 : 2);
+
+ p = out + 12;
+
+ p = qPTR(p,moi_local_proto);
+ p = mPTR(p,moi_local_proto, moi_local_pglue);
+ p = mSRV(p,moi_local_pglue,80, moi_local_name);
+ if (moi_txt)
+ p = mTXT(p,moi_local_pglue,(char *)moi_txt);
+ p = mARR(p,moi_local_name,moi_ip); // fill out my own IP address.
+
+ m_pUDPSocket->sendto(out,p-out,&dst);
+}
+
+void mDNSResponder::onUDPSocketEvent(UDPSocketEvent e) {
+ char udp[ 1500 ];
+ Host from;
+ int len;
+
+ switch (e) {
+ case UDPSOCKET_READABLE: //The only event for now
+ // 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);
+ unsigned int nA = ntohs(dp->answer_count);
+
+ if (flags & 2 != 0 || nQ == 0)
+ continue; // we only want questions
+
+ #define MAXDEPTH 64
+ #define MAXRR 192 /* including dot */
+
+ // assume nQ zero terminated fields followed by Qtype & Qclass
+ char * p = udp + 12;
+ for (int i = 0; i < nQ; i++) {
+ char buff[ MAXDEPTH * MAXRR ], * ep = buff;
+ int depth = 0;
+ int l;
+ char * q = 0;
+
+ do {
+ while (((l = *p++) < 192) && (l > 0)) {
+ if (p + l >= udp + len) {
+ printf("RR longer than packet\r\n");
+ return;
+ };
+
+ memcpy(ep,p,l);
+ ep[l]='.';
+ ep += l + 1;
+ // printf("%d:%s.", p-udp-12,buff);
+ p += l;
+ };
+ 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.
+ 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;
+ };
+ };
+ if (depth++ >= MAXDEPTH) {
+ printf("Too deep\r\n");
+ return;
+ };
+ } while (l);
+ *ep = 0;
+ // in case no compression was used at all.
+ if (!q) q = p;
+
+ unsigned int Qt = htons(q[0] + q[1]*256);
+ unsigned int Qc = htons(q[2] + q[3]*256);
+ p = q + 4;
+ // We want PTR records on the INternet of our type
+ if ((Qt == 12) && (Qc == 1) && !(strcmp(buff,moi_local_proto)))
+ sendReply(tid,from);
+ };
+ }
+ break;
+ }
+}
+
+