Bonjour/mDNS lib, works with the new network stack
Fork of BonjourLib 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/lwipopts.h" 00046 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ 00047 00048 #include "mDNSResponder.h" 00049 #include <stdio.h> 00050 #include <ctype.h> 00051 00052 #ifndef DNS_RRTYPE_SRV 00053 #define DNS_RRTYPE_SRV (33) 00054 #endif 00055 00056 #ifndef DNS_LOCAL 00057 #define DNS_LOCAL "local" 00058 #endif 00059 00060 void pretty_print_block(char *b, int len) 00061 { 00062 int x, y, indent, count = 0; 00063 00064 indent = 16; /* whatever */ 00065 00066 printf("\n\r"); 00067 00068 while (count < len) 00069 { 00070 printf("%04x : ", count); 00071 for (x = 0 ; x < indent ; x++) 00072 { 00073 printf("%02x ", b[x + count]); 00074 if ((x + count + 1) >= len) 00075 { 00076 x++; 00077 for (y = 0 ; y < (indent - x) ; y++) 00078 printf(" "); 00079 break; 00080 } 00081 } 00082 printf(": "); 00083 00084 for (x = 0 ; x < indent ; x++) 00085 { 00086 if (isprint(b[x + count])) 00087 putchar(b[x + count]); 00088 else if (b[x + count] == 0) 00089 putchar(' '); 00090 else 00091 putchar('.'); 00092 00093 if ((x + count + 1) >= len) 00094 { 00095 x++; 00096 for (y = 0 ; y < (indent - x) ; y++) 00097 putchar(' '); 00098 break; 00099 } 00100 } 00101 putchar('\n'); 00102 putchar('\r'); 00103 count += indent; 00104 } 00105 putchar('\n'); 00106 putchar('\r'); 00107 } 00108 00109 00110 00111 void announcer(void const *args) { 00112 printf("in announcer\r\n"); 00113 mDNSResponder *m = (mDNSResponder *)args; 00114 m->periodic(NULL); 00115 } 00116 00117 mDNSResponder::mDNSResponder() : 00118 moi_txt(NULL), moi_port(0), 00119 moi_name(NULL), moi_proto(NULL), 00120 moi_local_proto(NULL), moi_local_name(NULL), moi_local_pglue(NULL), 00121 #ifdef MDNS_QCACHE 00122 labelCacheCnt(0), labelCacheOffset(0) 00123 #endif 00124 { 00125 initLabelCache(0); 00126 } 00127 00128 mDNSResponder::~mDNSResponder() { 00129 close(); 00130 } 00131 00132 void mDNSResponder::announce(ip_addr_t ip, const char * ldn, const char * proto, uint16_t port, const char * name, char ** txt) { 00133 00134 m_pUDPSocket = new UDPSocket; 00135 // m_pUDPSocket->init(); 00136 00137 multicast_group = new Endpoint; 00138 multicast_group->set_address(MCAST, MDNS_PORT); 00139 00140 m_pUDPSocket->bind(MDNS_PORT); 00141 m_pUDPSocket->join_multicast_group(MCAST); 00142 00143 moi_port = port; 00144 moi_txt = txt; 00145 moi_ip = ip; 00146 moi_proto = proto; 00147 moi_name = name; 00148 00149 moi_local_proto = (char *)malloc(128); 00150 moi_local_name = (char *)malloc(128); 00151 moi_local_pglue = (char *)malloc(128); 00152 00153 snprintf(moi_local_proto,128,"%s.%s.", moi_proto, DNS_LOCAL); 00154 snprintf(moi_local_name,128,"%s.%s.", ldn, DNS_LOCAL); 00155 snprintf(moi_local_pglue,128,"%s.%s.%s.", moi_name, moi_proto, DNS_LOCAL); 00156 00157 printf("starting thread\r\n"); 00158 00159 // Gratuis intro - and repeat such regularly.. taking some 00160 // care to not go beyond DHCP limits - but not more often 00161 // than makes sense for our TTL. See the .h file for the 00162 // various tradeoffs. 00163 // 00164 // sendReply(DNS_RRTYPE_PTR, 0, NULL); 00165 00166 printf("starting announcer\r\n"); 00167 mdns_announcer = new RtosTimer(announcer, osTimerPeriodic, this); 00168 mdns_announcer->start(1000 * MDNS_INTERVAL); 00169 printf("kicking it\r\n"); 00170 00171 Listen(NULL); 00172 } 00173 00174 void mDNSResponder::close() { 00175 m_pUDPSocket->close(true); 00176 delete m_pUDPSocket; 00177 } 00178 00179 char * index(char * str, char c) { 00180 for (;str && *str;str++) { 00181 if (*str == c) return str; 00182 }; 00183 return NULL; 00184 } 00185 00186 #ifdef MDNS_QCACHE 00187 void mDNSResponder::initLabelCache(char * off) { 00188 labelCacheOffset = off; 00189 labelCacheCnt = 0; 00190 memset(_cache_off,0, sizeof(_cache_off)); // Rely on min value to be 12 00191 }; 00192 00193 uint16_t mDNSResponder::checkLabelCache(char * name) { 00194 // Bail out if not initialized with a valid (>12) offset. 00195 // 00196 if (!labelCacheOffset) 00197 return 0; 00198 00199 for (int i=0; i < MDNS_MAXCACHEDNSLABELS; i++) 00200 if (_cache_off[i] && !strcmp(_cache_och[i],name)) 00201 return _cache_off[i]; 00202 return 0; 00203 }; 00204 00205 void mDNSResponder::addLabelCache(char * p, char * name) { 00206 // Bail out if not initialized with a valid (>12) offset. 00207 // 00208 if (!labelCacheOffset) 00209 return; 00210 00211 _cache_off[labelCacheCnt] = p - labelCacheOffset; 00212 _cache_och[labelCacheCnt] = name; 00213 00214 labelCacheCnt++; 00215 // we intentionally do not wack the first entries - as they are the most 00216 // likely ones to be used. 00217 if (labelCacheCnt>= MDNS_MAXCACHEDNSLABELS) 00218 labelCacheCnt = MDNS_MAXCACHEDNSLABELS / 3; 00219 }; 00220 #else 00221 #define initLabelCache(x) {} /* Ignored */ 00222 #define checkLabelCache(x) (0) /* never a hit */ 00223 #define addLabelCache(x,y) {} /* Ignored */ 00224 #endif 00225 00226 char * mDNSResponder::breakname(char *p, char *name) { 00227 int l = 0, de = 1; 00228 char * q; 00229 uint16_t off; 00230 00231 do { 00232 if ((off = checkLabelCache(name)) != 0) { 00233 *p++ = 192 + (off >> 8); 00234 *p++ = (off & 0xFF); 00235 return p; 00236 } else { 00237 addLabelCache(p, name); 00238 }; 00239 00240 q = index(name,'.'); 00241 if (!q) { 00242 q = name + strlen(name); 00243 de = 0; 00244 }; 00245 l = q - name; 00246 *p++ = l; 00247 memcpy(p, name, l); 00248 p+=l; 00249 name = q + 1; 00250 } while (l && *name && de); 00251 00252 // terminating root field if any (not the case for 00253 // things like TXTs). 00254 if (de) *p++ = 0; 00255 return p; 00256 } 00257 00258 char * mDNSResponder::mRR(char *p, char * name, uint16_t tpe, uint16_t cls, uint32_t ttl) { 00259 uint16_t i = 0; 00260 uint32_t j = 0; 00261 00262 p = breakname(p, name); 00263 00264 // NOTE: Cannot assume proper byte boundaries; so 00265 // we assume the compiler does not allow that for 00266 // casts - and write it out with memcpy's 00267 // 00268 i = htons(tpe); // Type 00269 memcpy(p, &i, 2); 00270 p+=2; 00271 00272 i = htons(cls); // Class 00273 memcpy(p, &i, 2); 00274 p+=2; 00275 00276 j = htonl(ttl); // TTL (4 bytes) 00277 memcpy(p, &j, 4); 00278 p+=4; 00279 00280 return p; 00281 } 00282 00283 char * mDNSResponder::mRRLABEL(char *p, char * name, uint16_t tpe, char ** rr) { 00284 uint16_t i = 0; 00285 00286 p = mRR(p, name, tpe, DNS_RRCLASS_IN, MDNS_TTL); // Type, IN, TTL 00287 00288 // RR String 00289 char * q = p + 2; 00290 00291 for (;*rr;rr++) 00292 q = breakname(q, *rr); 00293 00294 i = htons(q - p - 2); // RDLEN 00295 memcpy(p, &i, 2); 00296 return q; 00297 } 00298 00299 char * mDNSResponder::mPTR(char *p, char * name, char * r) { 00300 char *rr[] = { r, NULL }; 00301 return mRRLABEL(p, name, DNS_RRTYPE_PTR, rr ); 00302 } 00303 00304 char * mDNSResponder::mTXT(char *p, char * name, char ** rr) { 00305 return mRRLABEL(p, name, DNS_RRTYPE_TXT, rr); 00306 } 00307 00308 char * mDNSResponder::mARR(char *p, char * name, ip_addr_t ip) { 00309 uint16_t i = 0; 00310 00311 p = mRR(p, name, DNS_RRTYPE_A, DNS_RRCLASS_IN, MDNS_TTL ); // A, IN 00312 00313 i = htons(4); // RDLEN - we're just doing a single IPv4 address - our primary link ? 00314 memcpy(p, &i, 2); 00315 00316 // IP already in network order. 00317 memcpy(p+2, &ip, 4); 00318 00319 return p + 2 + 4; 00320 } 00321 00322 char * mDNSResponder::mSRV(char *p, char * name, uint16_t port, char * rr) { 00323 uint16_t i = 0; 00324 char * q; 00325 00326 p = mRR(p, name, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, MDNS_TTL); // SRV, IN 00327 00328 // Keep space for RDLEN 00329 q = p; 00330 p+=2; 00331 00332 i = htons(1); // Priority 00333 memcpy(p, &i, 2); 00334 p+=2; 00335 00336 i = htons(0); // Weight (see rfc2782) 00337 memcpy(p, &i, 2); 00338 p+=2; 00339 00340 i = htons(port); // Port 00341 memcpy(p, &i, 2); 00342 p+=2; 00343 00344 p = breakname(p, rr); 00345 00346 i = htons(p - q - 2); // RDLEN 00347 memcpy(q, &i, 2); 00348 00349 return p; 00350 } 00351 00352 char * mDNSResponder::qANY(char *p, char * name, uint16_t tpe, uint16_t cls) { 00353 uint16_t i = 0; 00354 p = breakname(p, name); // QNAME 00355 00356 i = htons(tpe); // QTYPE 00357 memcpy(p, &i, 2); 00358 p+=2; 00359 00360 i = htons(cls); // QCLASS 00361 memcpy(p, &i, 2); 00362 p+=2; 00363 00364 return p; 00365 } 00366 00367 char * mDNSResponder::qPTR(char *p, char *name) { 00368 return qANY(p,name,DNS_RRTYPE_PTR,DNS_RRCLASS_IN); 00369 } 00370 00371 char * mDNSResponder::qA(char *p, char *name) { 00372 return qANY(p,name,DNS_RRTYPE_A,DNS_RRCLASS_IN); 00373 } 00374 00375 char * mDNSResponder::qSRV(char *p, char *name) { 00376 return qANY(p,name,DNS_RRTYPE_SRV,DNS_RRCLASS_IN); 00377 } 00378 00379 /* This si wrong, i think we're answering our own question? or something? */ 00380 void mDNSResponder::sendAnnounce(void) { 00381 char out[ 1500 ], *p; 00382 DNSPacket * op = (DNSPacket *)out; 00383 00384 Endpoint quack; 00385 quack.set_address(multicast_group->get_address(), multicast_group->get_port()); 00386 00387 initLabelCache(out); // Offsets are relative to the ID header. 00388 00389 op->tid = ntohs(0); 00390 op->flags = ntohs(0x0); 00391 op->question_count = ntohs(1); // Courtesy copy of the question. 00392 op->answer_count = ntohs(0); // The record asked for 00393 op->a_count = ntohs(0); 00394 p = out + 12; 00395 00396 p = qPTR(p, moi_local_proto); 00397 00398 printf("sendAnnounce1\r\n"); 00399 00400 m_pUDPSocket->sendTo(quack, out, p-out); 00401 00402 initLabelCache(out); // Offsets are relative to the ID header. 00403 00404 op->flags = ntohs(0x8400U); 00405 op->question_count = ntohs(0); 00406 op->answer_count = ntohs(4); 00407 00408 p = out + 12; 00409 00410 p = mPTR(p, moi_local_proto, moi_local_pglue); 00411 00412 if (moi_txt && * moi_txt) 00413 p = mTXT(p, moi_local_pglue, moi_txt); 00414 00415 p = mSRV(p, moi_local_pglue, moi_port, moi_local_name); 00416 00417 p = mARR(p, moi_local_name, moi_ip); 00418 00419 printf("sendAnnounce2\r\n"); 00420 00421 m_pUDPSocket->sendTo(quack, out, p-out); 00422 00423 } 00424 00425 void mDNSResponder::sendReply(uint16_t t, uint16_t tid, Endpoint *dst) { 00426 // sent a reply - basically a precooked A/PTR/SRV with the TransactionID 00427 // and my URI squeezed in. 00428 char *out, *p; 00429 out = space; 00430 DNSPacket * op = (DNSPacket *) out; 00431 00432 initLabelCache(out); // Offsets are relative to the ID header. 00433 00434 op->tid = ntohs(tid); 00435 op->flags = ntohs(0x8000U); // Answer 00436 op->question_count = ntohs(1); // Courtesy copy of the question. 00437 op->answer_count = ntohs(1); // The record asked for 00438 op->a_count = ntohs(0); 00439 p = out + 12; 00440 00441 switch (t) { 00442 case DNS_RRTYPE_PTR: // PTR record, SRV, optional TXT and A with my own address. 00443 op->aa_count = ntohs(moi_txt ? 3 : 2); 00444 p = qPTR(p,moi_local_proto); 00445 00446 p = mPTR(p,moi_local_proto, moi_local_pglue); 00447 p = mSRV(p,moi_local_pglue, moi_port, moi_local_name); 00448 00449 if (moi_txt && * moi_txt) 00450 p = mTXT(p,moi_local_pglue,moi_txt); 00451 00452 break; 00453 case DNS_RRTYPE_A: // A record (and nothing else) 00454 op->aa_count = ntohs(0); 00455 p = qA(p,moi_local_name); 00456 break; 00457 00458 case DNS_RRTYPE_SRV: // SRV record, optional TXT and a gratious A record to complete. 00459 op->aa_count = ntohs(moi_txt ? 2 : 1); 00460 p = qSRV(p,moi_local_pglue); 00461 00462 p = mSRV(p,moi_local_pglue, moi_port, moi_local_name); 00463 if (moi_txt && * moi_txt) 00464 p = mTXT(p,moi_local_pglue,moi_txt); 00465 break; 00466 } 00467 p = mARR(p, moi_local_name, moi_ip); 00468 00469 printf("sendReply\r\n"); 00470 00471 if (dst == NULL) { 00472 dst = multicast_group; 00473 } 00474 Endpoint quack; 00475 quack.set_address(dst->get_address(), dst->get_port()); 00476 00477 pretty_print_block(out, p - out); 00478 00479 m_pUDPSocket->sendTo(quack, out, p-out); 00480 } 00481 00482 char * mDNSResponder::decodeQBlock(char * udp, int len, char *p, char * fqdn, int fqdn_size, uint32_t *Qclass, uint32_t *Qtype) { 00483 char * endp = udp + len; 00484 int depth = 0; 00485 int l, ll = 0; 00486 char * q = 0; 00487 char * ep = fqdn; 00488 00489 do { 00490 while (((l = *p++) < 192) && (l > 0)) { 00491 if (p + l >= endp) { 00492 printf("Malformed packet or bug, RR field larger than packet itself\n\r"); 00493 return NULL; 00494 }; 00495 00496 if (ll + l + 1 > fqdn_size) { 00497 printf("Malformed packet or bug, FQDN exceeds %d bytes\n\r", fqdn_size); 00498 return NULL; 00499 } 00500 00501 memcpy(ep,p,l); 00502 ep[l]='.'; 00503 ep += l + 1; 00504 p += l; 00505 ll += l + 1; 00506 }; 00507 00508 if (l >= 192) { 00509 // construct an offset pointer by wiping the top 1 bits 00510 // and then getting the remaining 8 bytes to make 14. 00511 l -= 192; 00512 l = l << 8; 00513 l += *p++; 00514 00515 // rescue our reference; as we've not gotten the Qt's yet 00516 // and these follow the last entry in our record (and not 00517 // that from the compressed values. 00518 // 00519 if (!q) q = p; 00520 00521 // printf(" [->%d] ",l); 00522 p = udp + l; 00523 00524 if (p >= udp + len || p < udp + 12) { 00525 printf("Malformed packet or bug, pointer outside UDP packet\n\r"); 00526 return NULL; 00527 }; 00528 }; 00529 00530 if (depth++ >= 128) { 00531 printf("Malformed packet or bug, depth too high\n\r"); 00532 return NULL; 00533 }; 00534 } while (l); 00535 00536 // Terminate the FQDN string. 00537 *ep = 0; 00538 00539 // in case no compression was used at all. 00540 if (!q) 00541 q = p; 00542 00543 *Qtype = htons(q[0] + q[1]*256); 00544 *Qclass = htons(q[2] + q[3]*256); 00545 return p; 00546 } 00547 00548 void mDNSResponder::periodic(void const *n) { 00549 /* send this every timeout */ 00550 mDNSResponder::sendAnnounce(); 00551 } 00552 00553 /* 00554 Do we support _services._dns-sd._udp.local as well??!? 00555 */ 00556 void mDNSResponder::Listen(void const *args) { 00557 Endpoint from; 00558 int len; 00559 00560 printf("in Listen\r\n"); 00561 00562 // parse through the packet; find any PTR requests 00563 // and respond to any for _http._tcp._local. 00564 while (true) { 00565 printf("about to receive\r\n"); 00566 len = m_pUDPSocket->receiveFrom(from, space, sizeof(space)); 00567 printf("Got a packet: %s %d\r\n", from.get_address(), len); 00568 DNSPacket * dp = (DNSPacket *) space; 00569 00570 unsigned int tid = ntohs(dp->tid); 00571 unsigned int flags = ntohs(dp->flags); 00572 unsigned int nQ = ntohs(dp->question_count); 00573 00574 if (flags & 2 != 0 || nQ < 1) { 00575 printf("Not a Question\r\n"); 00576 continue; // we only want questions 00577 } 00578 00579 // assume nQ NON terminated fields followed by Qtype & Qclass 00580 // 00581 char * p = space + 12; 00582 for (int i = 0; i < nQ; i++) { 00583 char fqdn[ 256 ]; 00584 uint32_t Qt, Qc; 00585 00586 if ( (p = decodeQBlock(space, len, p, fqdn, sizeof(fqdn), &Qc, &Qt)) == 0) 00587 break; 00588 00589 // We only deal with INternet. 00590 // 00591 if (Qc != DNS_RRCLASS_IN) 00592 continue; 00593 00594 printf("IN query 0x%x for %s\n\r", Qt, fqdn); 00595 00596 // Normally we'll respond to a mCast query for the PTR of our conceptual 00597 // service; to which we reply with the services we run; and then the A 00598 // records for our actual endpoint. However if one of the requistors their 00599 // cashing is not up to scratch - we may (also) get hit up a bit later for 00600 // the secondary records under their logical names. So we respond to 00601 // all 3. As this is likely to happen due to resource constraints on the 00602 // side of the requestor; we are careful and reply as 'narrow' as possible. 00603 // 00604 // Note that we also respond to the ANY query - as to make a quick 'dig' 00605 // testing command easier. 00606 // 00607 if ((Qt == DNS_RRTYPE_PTR || Qt == 255) && !(strcmp(fqdn,moi_local_proto))) 00608 sendReply(DNS_RRTYPE_PTR, tid, &from); 00609 else if ((Qt == DNS_RRTYPE_A || Qt == 255) && !(strcmp(fqdn,moi_local_name))) 00610 sendReply(DNS_RRTYPE_A, tid, &from); 00611 else if ((Qt == DNS_RRTYPE_SRV || Qt == 255) && !(strcmp(fqdn,moi_local_pglue))) 00612 sendReply(DNS_RRTYPE_SRV, tid, &from); 00613 else 00614 printf(".. which was ignored\n\r"); 00615 } 00616 } 00617 } 00618 00619 #endif
Generated on Wed Jul 13 2022 13:05:00 by 1.7.2