Dirk-Willem van Gulik (NXP/mbed)
/
Bonjour
Bonjour/Zerconf library
services/mDNS/mDNSResponder.cpp
- Committer:
- dirkx
- Date:
- 2010-07-22
- Revision:
- 1:59820ca5c83a
- Parent:
- 0:355018f44c9f
- Child:
- 2:816cbd922d3e
File content as of revision 1:59820ca5c83a:
/* 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 "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 * 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(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 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); // 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; 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, 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 && * moi_txt) p = mTXT(p,moi_local_pglue,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; printf("Q for %s\r\n", buff); // We want PTR records on the INternet of our type if ((Qt == 12) && (Qc == 1) && !(strcmp(buff,moi_local_proto))) sendReply(tid,from); }; } break; } }