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.
Fork of Bonjour by
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
Generated on Tue Jul 12 2022 18:11:29 by
