Dirk-Willem van Gulik (NXP/mbed)
/
Bonjour
Bonjour/Zerconf library
Embed:
(wiki syntax)
Show/hide line numbers
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:38:37 by 1.7.2