Bonjour/Zerconf library

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mDNSResponder.cpp Source File

mDNSResponder.cpp

00001 /* Class: mDNSResponder
00002  * Copyright 1991, 2003, 2010 Dirk-Willem van Gulik <dirkx(at)apache(punto)org>
00003  *
00004  * License: Any BSD or ASF License.
00005  *
00006  * Rough and ready port of some mDNS code.
00007  *
00008  * Typical use is something like
00009  *
00010  * EthernetNetIf eth;
00011  * HTTPServer svr;
00012  * mDNSResponder mdns;
00013  *
00014  * int main()...
00015  *
00016  *   // get internet
00017  *   EthernetErr ethErr = eth.setup();
00018  *   ... etc ..
00019  *
00020  *   // set up some server
00021  *   svr.addHandler<SimpleHandler>("/"); //Default handler
00022  *   svr.bind(80); * *
00023 
00024  *  // Extract the IP address.
00025  *   IpAddr ip = eth.getIp();
00026  *   printf("mbed IP Address is %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]);
00027  *
00028  *   // Announce ourselves.
00029  *   mdns.announce(ip, "fred", "_http._tcp", 80, "The Little Server that Could", "path=/demo");
00030  *
00031  *   while()... enter some run loop
00032  *   ...
00033  *
00034  *  This will cause http://fred.local./demo to be announced as 'The Little Server that Could'.
00035  *
00036  *  Or as another example: (http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt)
00037  *  and the various RFCs:
00038  *
00039  *     mdns.announce(ip, "_ssh._tcp", 22, SSH to Serial Gateway", NULL);
00040  *
00041  * CAVEAT - a lot of the buffer overrun and copy checks
00042  *          where removed; and this is not anywhere near
00043  *          threadsafve or sane. Not for production use.
00044  */
00045 #include "lwip/opt.h"
00046 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
00047 
00048 #include "dbg/dbg.h"
00049 
00050 #include "mDNSResponder.h"
00051 #include <stdio.h>
00052 
00053 #ifndef DNS_RRTYPE_SRV
00054 #define DNS_RRTYPE_SRV (33)
00055 #endif
00056 
00057 #ifndef DNS_LOCAL
00058 #define DNS_LOCAL "local"
00059 #endif
00060 
00061 mDNSResponder::mDNSResponder() : NetService(false),
00062         moi_txt(NULL), moi_port(0), moi_ip(0),
00063         moi_name(NULL), moi_proto(NULL),
00064         moi_local_proto(NULL), moi_local_name(NULL), moi_local_pglue(NULL),
00065 #ifdef MDNS_QCACHE
00066         labelCacheCnt(0), labelCacheOffset(0),
00067 #endif
00068         announcer() 
00069 {
00070     initLabelCache(0);
00071 }
00072 
00073 mDNSResponder::~mDNSResponder() {
00074     announcer.stop();
00075     close();
00076 }
00077 
00078 void mDNSResponder::announce(IpAddr ip, const char * ldn, const char * proto, uint16_t port, const char * name, char ** txt) {
00079     Host localhost(IpAddr(MCAST), MDNS_PORT, NULL /* fqdn */);
00080 
00081     m_pUDPSocket = new UDPSocket;
00082     m_pUDPSocket->bind(localhost);
00083 
00084     m_pUDPSocket->setOnEvent(this, &mDNSResponder::onUDPSocketEvent);
00085 
00086     moi_port = port;
00087     moi_txt = txt;
00088     moi_ip = ip;
00089     moi_proto = proto;
00090     moi_name = name;
00091 
00092     moi_local_proto = (char *)malloc(128);
00093     moi_local_name = (char *)malloc(128);
00094     moi_local_pglue = (char *)malloc(128);
00095 
00096     snprintf(moi_local_proto,128,"%s.%s.", moi_proto, DNS_LOCAL);
00097     snprintf(moi_local_name,128,"%s.%s.", ldn, DNS_LOCAL);
00098     snprintf(moi_local_pglue,128,"%s.%s.%s.", moi_name,moi_proto, DNS_LOCAL);
00099 
00100     // Gratuis intro - and repeat such regularly.. taking some
00101     // care to not go beyond DHCP limits - but not more often
00102     // than makes sense for our TTL. See the .h file for the
00103     // various tradeoffs.
00104     //
00105     mDNSResponder::sendReply(DNS_RRTYPE_PTR, 0, localhost);
00106     announcer.start();
00107 }
00108 
00109 void mDNSResponder::close() {
00110     m_pUDPSocket->resetOnEvent();
00111     m_pUDPSocket->close();
00112     delete m_pUDPSocket;
00113 }
00114 
00115 void mDNSResponder::poll() { //Called by NetServices
00116     if (announcer.read_ms() > 1000  * MDNS_INTERVAL) {
00117 
00118         Host mch(IpAddr(MCAST), MDNS_PORT, NULL);
00119         mDNSResponder::sendReply(DNS_RRTYPE_PTR, 0, mch);
00120 
00121         announcer.reset();
00122     }
00123 }
00124 
00125 char * index(char * str, char c) {
00126     for (;str && *str;str++) {
00127         if (*str == c) return str;
00128     };
00129     return NULL;
00130 }
00131 
00132 #ifdef MDNS_QCACHE
00133 void mDNSResponder::initLabelCache(char * off) {
00134     labelCacheOffset = off;
00135     labelCacheCnt = 0;
00136     memset(_cache_off,0, sizeof(_cache_off)); // Rely on min value to be 12
00137 };
00138 
00139 uint16_t mDNSResponder::checkLabelCache(char * name) {
00140     // Bail out if not initialized with a valid (>12) offset.
00141     //
00142     if (!labelCacheOffset)
00143         return 0;
00144     
00145     for (int i=0; i < MDNS_MAXCACHEDNSLABELS; i++)
00146         if (_cache_off[i] && !strcmp(_cache_och[i],name))
00147             return _cache_off[i];
00148     return 0;
00149 };
00150 
00151 void mDNSResponder::addLabelCache(char * p, char * name) {
00152     // Bail out if not initialized with a valid (>12) offset.
00153     //
00154     if (!labelCacheOffset)
00155         return;
00156         
00157     _cache_off[labelCacheCnt] = p - labelCacheOffset;
00158     _cache_och[labelCacheCnt] = name;
00159 
00160     labelCacheCnt++;
00161     // we intentionally do not wack the first entries - as they are the most
00162     // likely ones to be used.
00163     if (labelCacheCnt>= MDNS_MAXCACHEDNSLABELS)
00164         labelCacheCnt = MDNS_MAXCACHEDNSLABELS / 3;
00165 };
00166 #else
00167 #define initLabelCache(x) {} /* Ignored */
00168 #define checkLabelCache(x) (0) /* never a hit */
00169 #define addLabelCache(x,y) {} /* Ignored */
00170 #endif
00171 
00172 char * mDNSResponder::breakname(char *p, char *name) {
00173     int l = 0, de = 1;
00174     char * q;
00175     uint16_t off;
00176 
00177     do {
00178         if ((off = checkLabelCache(name)) != 0) {
00179             *p++ = 192 + (off >> 8);
00180             *p++ =       (off &  0xFF);
00181             return p;
00182         } else {
00183             addLabelCache(p, name);
00184         };
00185 
00186         q = index(name,'.');
00187         if (!q) {
00188             q = name + strlen(name);
00189             de = 0;
00190         };
00191         l = q - name;
00192         *p++ = l;
00193         memcpy(p, name, l);
00194         p+=l;
00195         name = q + 1;
00196     } while (l && *name && de);
00197 
00198     // terminating root field if any (not the case for
00199     // things like TXTs).
00200     if (de) *p++ = 0;
00201     return p;
00202 }
00203 
00204 char * mDNSResponder::mRR(char *p, char * name, uint16_t tpe, uint16_t cls, uint32_t ttl) {
00205     uint16_t i = 0;
00206     uint32_t j = 0;
00207 
00208     p = breakname(p, name);
00209 
00210     // NOTE: Cannot assume proper byte boundaries; so
00211     // we assume the compiler does not allow that for
00212     // casts - and write it out with memcpy's
00213     //
00214     i = htons(tpe); // Type
00215     memcpy(p, &i,  2);
00216     p+=2;
00217 
00218     i = htons(cls); // Class
00219     memcpy(p, &i, 2);
00220     p+=2;
00221 
00222     j = htonl(ttl); // TTL (4 bytes)
00223     memcpy(p, &j, 4);
00224     p+=4;
00225 
00226     return p;
00227 }
00228 
00229 char * mDNSResponder::mRRLABEL(char *p, char * name, uint16_t tpe, char ** rr) {
00230     uint16_t i = 0;
00231 
00232     p = mRR(p, name, tpe, DNS_RRCLASS_IN, MDNS_TTL); // Type, IN, TTL
00233 
00234     // RR String
00235     char * q = p + 2;
00236 
00237     for (;*rr;rr++)
00238         q = breakname(q, *rr);
00239 
00240     i = htons(q - p - 2); // RDLEN
00241     memcpy(p, &i, 2);
00242     return q;
00243 }
00244 
00245 char * mDNSResponder::mPTR(char *p, char * name, char * r) {
00246     char *rr[] = { r, NULL };
00247     return mRRLABEL(p, name, DNS_RRTYPE_PTR, rr );
00248 }
00249 
00250 char * mDNSResponder::mTXT(char *p, char * name, char ** rr) {
00251     return mRRLABEL(p, name, DNS_RRTYPE_TXT, rr);
00252 }
00253 
00254 char * mDNSResponder::mARR(char *p, char * name, IpAddr ip) {
00255     uint16_t i = 0;
00256 
00257     p = mRR(p, name, DNS_RRTYPE_A, DNS_RRCLASS_IN, MDNS_TTL ); // A, IN
00258 
00259     i = htons(4); // RDLEN - we're just doing a single IPv4 address - our primary link ?
00260     memcpy(p, &i, 2);
00261 
00262     // IP already in network order.
00263     memcpy(p+2, &ip, 4);
00264 
00265     return p + 2 + 4;
00266 }
00267 
00268 char * mDNSResponder::mSRV(char *p, char * name, uint16_t port, char * rr) {
00269     uint16_t i = 0;
00270     char * q;
00271 
00272     p = mRR(p, name, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, MDNS_TTL); // SRV, IN
00273 
00274     // Keep space for RDLEN
00275     q = p;
00276     p+=2;
00277 
00278     i = htons(1); // Priority
00279     memcpy(p, &i, 2);
00280     p+=2;
00281 
00282     i = htons(0); // Weight (see rfc2782)
00283     memcpy(p, &i, 2);
00284     p+=2;
00285 
00286     i = htons(port); // Port
00287     memcpy(p, &i, 2);
00288     p+=2;
00289 
00290     p = breakname(p, rr);
00291 
00292     i = htons(p - q - 2); // RDLEN
00293     memcpy(q, &i, 2);
00294 
00295     return p;
00296 }
00297 
00298 char * mDNSResponder::qANY(char *p, char * name, uint16_t tpe, uint16_t cls) {
00299     uint16_t i = 0;
00300     p = breakname(p, name); // QNAME
00301 
00302     i = htons(tpe); // QTYPE
00303     memcpy(p, &i, 2);
00304     p+=2;
00305 
00306     i = htons(cls); // QCLASS
00307     memcpy(p, &i, 2);
00308     p+=2;
00309 
00310     return p;
00311 }
00312 
00313 char * mDNSResponder::qPTR(char *p, char *name) {
00314     return qANY(p,name,DNS_RRTYPE_PTR,DNS_RRCLASS_IN);
00315 }
00316 
00317 char * mDNSResponder::qA(char *p, char *name) {
00318     return qANY(p,name,DNS_RRTYPE_A,DNS_RRCLASS_IN);
00319 }
00320 
00321 char * mDNSResponder::qSRV(char *p, char *name) {
00322     return qANY(p,name,DNS_RRTYPE_SRV,DNS_RRCLASS_IN);
00323 }
00324 
00325 void mDNSResponder::mDNSResponder::sendReply(uint16_t t, uint16_t tid, Host dst) {
00326     // sent a reply - basically a precooked A/PTR/SRV with the TransactionID
00327     // and my URI squeezed in.
00328     char out[ 1500 ], *p;
00329     DNSPacket * op = (DNSPacket *) out;
00330 
00331     initLabelCache(out); // Offsets are relative to the ID header.
00332 
00333     op->tid = ntohs(tid);
00334     op->flags = ntohs(0x8000U); // Answer
00335     op->question_count = ntohs(1); // Courtesy copy of the question.
00336     op->answer_count = ntohs(1); // The record asked for
00337     op->a_count = ntohs(0);
00338     p = out + 12;
00339 
00340     switch (t) {
00341         case DNS_RRTYPE_PTR:    // PTR record, SRV, optional TXT and A with my own address.
00342             op->aa_count = ntohs(moi_txt ? 3 : 2);
00343             p = qPTR(p,moi_local_proto);
00344 
00345             p = mPTR(p,moi_local_proto, moi_local_pglue);
00346             p = mSRV(p,moi_local_pglue, moi_port, moi_local_name);
00347 
00348             if (moi_txt && * moi_txt)
00349                 p = mTXT(p,moi_local_pglue,moi_txt);
00350 
00351             break;
00352         case DNS_RRTYPE_A:    // A record (and nothing else)
00353             op->aa_count = ntohs(1);
00354             p = qA(p,moi_local_name);
00355             break;
00356 
00357         case DNS_RRTYPE_SRV:   // SRV record, optional TXT and a gratious A record to complete.
00358             op->aa_count = ntohs(moi_txt ? 2 : 1);
00359             p = qSRV(p,moi_local_pglue);
00360 
00361             p = mSRV(p,moi_local_pglue, moi_port, moi_local_name);
00362             if (moi_txt && * moi_txt)
00363                 p = mTXT(p,moi_local_pglue,moi_txt);
00364             break;        
00365     }
00366     p = mARR(p,moi_local_name,moi_ip);
00367     m_pUDPSocket->sendto(out,p-out,&dst);
00368 }
00369 
00370 char * mDNSResponder::decodeQBlock(char * udp, int len, char *p, char  * fqdn, int fqdn_size, uint32_t *Qclass, uint32_t *Qtype) {
00371     char * endp = udp + len;
00372     int depth = 0;
00373     int l, ll = 0;
00374     char * q = 0;
00375     char * ep = fqdn;
00376 
00377     do {
00378         while (((l = *p++) < 192) && (l > 0)) {
00379             if (p + l >= endp) {
00380                 DBG("Malformed packet or bug, RR field larger than packet itself\n\r");
00381                 return NULL;
00382             };
00383 
00384             if (ll + l + 1 > fqdn_size) {
00385                 DBG("Malformed packet or bug, FQDN exceeds %d bytes\n\r", fqdn_size);
00386                 return NULL;
00387             }
00388 
00389             memcpy(ep,p,l);
00390             ep[l]='.';
00391             ep += l + 1;
00392             p += l;
00393             ll += l + 1;
00394         };
00395 
00396         if (l >= 192) {
00397             // construct an offset pointer by wiping the top 1 bits
00398             // and then getting the remaining 8 bytes to make 14.
00399             l -= 192;
00400             l = l << 8;
00401             l += *p++;
00402 
00403             // rescue our reference; as we've not gotten the Qt's yet
00404             // and these follow the last entry in our record (and not
00405             // that from the compressed values.
00406             //
00407             if (!q) q = p;
00408 
00409             // printf(" [->%d] ",l);
00410             p = udp + l;
00411 
00412             if (p >= udp + len || p < udp + 12) {
00413                 DBG("Malformed packet or bug, pointer outside UDP packet\n\r");
00414                 return NULL;
00415             };
00416         };
00417 
00418         if (depth++ >= 128) {
00419             DBG("Malformed packet or bug, depth too high\n\r");
00420             return NULL;
00421         };
00422     } while (l);
00423 
00424     // Terminate the FQDN string.
00425     *ep = 0;
00426 
00427     // in case no compression was used at all.
00428     if (!q)
00429         q = p;
00430 
00431     *Qtype = htons(q[0] + q[1]*256);
00432     *Qclass = htons(q[2] + q[3]*256);
00433     return p;
00434 }
00435 
00436 void mDNSResponder::onUDPSocketEvent(UDPSocketEvent e) {
00437     char udp[ 1500 ];
00438     Host from;
00439     int len;
00440 
00441     //The only event for now
00442     if (e != UDPSOCKET_READABLE)
00443         return;
00444         
00445     // parse through the packet; find any PTR requests
00446     // and respond to any for _http._tcp._local.
00447     //
00448     while ((len = m_pUDPSocket->recvfrom( (char*)&udp, sizeof(udp), &from )) != 0) {
00449         DNSPacket * dp = (DNSPacket *) udp;
00450         
00451         unsigned int tid = ntohs(dp->tid);
00452         unsigned int flags = ntohs(dp->flags);
00453         unsigned int nQ = ntohs(dp->question_count);
00454 
00455         if (flags & 2 != 0 || nQ < 1)
00456             continue; // we only want questions
00457 
00458         // assume nQ NON terminated fields followed by Qtype & Qclass
00459         //
00460         char * p = udp + 12;
00461         for (int i = 0; i < nQ; i++) {
00462             char fqdn[ 256 ];
00463             uint32_t Qt, Qc;
00464 
00465             if ( (p = decodeQBlock(udp,len,p,fqdn,sizeof(fqdn),&Qc, &Qt)) == 0)
00466                 return;
00467 
00468             // We only deal with INternet.
00469             //
00470             if (Qc != DNS_RRCLASS_IN)
00471                 continue;
00472 
00473            DBG("IN query 0x%x for %s\n\r", Qt, fqdn);
00474             
00475             // Normally we'll respond to a mCast query for the PTR of our conceptual
00476             // service; to which we reply with the services we run; and then the A
00477             // records for our actual endpoint. However if one of the requistors their
00478             // cashing is not up to scratch - we may (also) get hit up a bit later for
00479             // the secondary records under their logical names. So we respond to
00480             // all 3. As this is likely to happen due to resource constraints on the
00481             // side of the requestor; we are careful and reply as 'narrow' as possible.
00482             //
00483             // Note that we also respond to the ANY query - as to make a quick 'dig'
00484             // testing command easier.
00485             //
00486             if ((Qt == DNS_RRTYPE_PTR || Qt == 255) && !(strcmp(fqdn,moi_local_proto)))
00487                 sendReply(DNS_RRTYPE_PTR,tid,from);
00488             else if ((Qt == DNS_RRTYPE_A || Qt == 255)  && !(strcmp(fqdn,moi_local_name)))
00489                 sendReply(DNS_RRTYPE_A,tid,from);
00490             else if ((Qt == DNS_RRTYPE_SRV || Qt == 255)  && !(strcmp(fqdn,moi_local_pglue)))
00491                 sendReply(DNS_RRTYPE_SRV,tid,from);
00492             else
00493                DBG(".. which was ignored\n\r", Qt, fqdn);
00494             
00495         };
00496     }
00497 }
00498 #endif