mDNS-responder: ipv6 support and fixes

* Adds ipv6 support. Compiles with ipv6 enabled and then accepts ipv6
  connections and answers AAAA questions.

* Fixes a few overflows of the reply buffer. Reduce the default reply buffer
  size, name it MDNS_RESPONDER_REPLY_SIZE, and allow overriding.

* Fix mdns_add_TXT.

* Prefer malloc to large stack buffers, to control stack sizes, and try to
  malloc only the buffer size needed where known in advance.

* Determine the IP addresses when responding, eliminating the update function
  and the update task.

* Allow use in SoftAP and StationAP mode too.

* Fix to compile without the debug output.

* Slightly better integration with lwip.

* Accept a NULL TXT entry.

* Some code style changes, not comprehensive.
This commit is contained in:
Our Air Quality 2017-12-07 11:50:43 +11:00
parent 61f23d0cf4
commit 85338ee672
2 changed files with 340 additions and 249 deletions

View file

@ -1,9 +1,10 @@
/* /*
* Basic multicast DNS responder * Basic multicast DNS responder
* *
* Advertises the IP address, port, and characteristics of a service to other devices using multicast DNS on the same LAN, * Advertises the IP address, port, and characteristics of a service to other
* so they can find devices with addresses dynamically allocated by DHCP. See avahi, Bonjour, etc * devices using multicast DNS on the same LAN, so they can find devices with
* See RFC6762, RFC6763 * addresses dynamically allocated by DHCP. See avahi, Bonjour, etc. See
* RFC6762, RFC6763
* *
* This sample code is in the public domain. * This sample code is in the public domain.
* *
@ -26,22 +27,21 @@
#include <lwip/netdb.h> #include <lwip/netdb.h>
#include <lwip/dns.h> #include <lwip/dns.h>
#include <lwip/prot/dns.h> #include <lwip/prot/dns.h>
#include <lwip/prot/iana.h>
#include <lwip/udp.h> #include <lwip/udp.h>
#include <lwip/igmp.h> #include <lwip/igmp.h>
#include <lwip/netif.h> #include <lwip/netif.h>
#include "mdnsresponder.h" #include "mdnsresponder.h"
#if !LWIP_IGMP
#error "LWIP_IGMP needs to be defined in lwipopts.h"
#endif
#define qDebugLog // Log activity generally #define qDebugLog // Log activity generally
#define qLogIncoming // Log all arriving multicast packets #define qLogIncoming // Log all arriving multicast packets
#define qLogAllTraffic // Log and decode all mDNS packets #define qLogAllTraffic // Log and decode all mDNS packets
#define kMDNSStackSize 800
#define DNS_MULTICAST_ADDRESS "224.0.0.251" // RFC 6762
#define DNS_MDNS_PORT 5353 // RFC 6762
#define DNS_MSG_SIZE 512
//------------------------------------------------------------------- //-------------------------------------------------------------------
#ifdef PACK_STRUCT_USE_INCLUDES #ifdef PACK_STRUCT_USE_INCLUDES
@ -120,13 +120,15 @@ typedef struct mdns_rsrc {
} mdns_rsrc; } mdns_rsrc;
static struct udp_pcb* gMDNS_pcb = NULL; static struct udp_pcb* gMDNS_pcb = NULL;
static ip_addr_t gMulticastAddr; // == DNS_MULTICAST_ADDRESS static const ip_addr_t gMulticastV4Addr = DNS_MQUERY_IPV4_GROUP_INIT;
#if LWIP_IPV6
#include "lwip/mld6.h"
static const ip_addr_t gMulticastV6Addr = DNS_MQUERY_IPV6_GROUP_INIT;
#endif
static mdns_rsrc* gDictP = NULL; // RR database, linked list static mdns_rsrc* gDictP = NULL; // RR database, linked list
//---------------------- Debug/logging utilities ------------------------- //---------------------- Debug/logging utilities -------------------------
#ifdef qDebugLog
// DNS field TYPE used for "Resource Records", some additions // DNS field TYPE used for "Resource Records", some additions
#define DNS_RRTYPE_AAAA 28 /* IPv6 host address */ #define DNS_RRTYPE_AAAA 28 /* IPv6 host address */
#define DNS_RRTYPE_SRV 33 /* Service record */ #define DNS_RRTYPE_SRV 33 /* Service record */
@ -146,6 +148,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
#define DNS_FLAG2_RA 0x80 #define DNS_FLAG2_RA 0x80
#define DNS_FLAG2_RESMASK 0x0F #define DNS_FLAG2_RESMASK 0x0F
#ifdef qDebugLog
static char qstr[12]; static char qstr[12];
static char* mdns_qrtype(uint16_t typ) static char* mdns_qrtype(uint16_t typ)
@ -183,7 +186,9 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
n = *p++; n = *p++;
cp = (char*)p; cp = (char*)p;
for (i=0; i<n; i++) putchar(*cp++); for (i = 0; i < n; i++) {
putchar(*cp++);
}
} }
static char cstr[16]; static char cstr[16];
@ -198,9 +203,9 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
return cstr; return cstr;
} }
static u8_t* mdns_print_name(u8_t* p, struct mdns_hdr* hp)
// Sequence of Pascal strings, terminated by zero-length string // Sequence of Pascal strings, terminated by zero-length string
// Handles compression, returns ptr to next item // Handles compression, returns ptr to next item
static u8_t* mdns_print_name(u8_t* p, struct mdns_hdr* hp)
{ {
char* cp = (char*)p; char* cp = (char*)p;
int i, n; int i, n;
@ -249,8 +254,8 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
return (u8_t*)hdr + SIZEOF_DNS_HDR; return (u8_t*)hdr + SIZEOF_DNS_HDR;
} }
static u8_t* mdns_print_query(u8_t* p)
// Copy needed because it may be misaligned // Copy needed because it may be misaligned
static u8_t* mdns_print_query(u8_t* p)
{ {
struct mdns_query q; struct mdns_query q;
uint16_t c; uint16_t c;
@ -263,8 +268,8 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
return p + SIZEOF_DNS_QUERY; return p + SIZEOF_DNS_QUERY;
} }
static u8_t* mdns_print_answer(u8_t* p, struct mdns_hdr* hp)
// Copy needed because it may be misaligned // Copy needed because it may be misaligned
static u8_t* mdns_print_answer(u8_t* p, struct mdns_hdr* hp)
{ {
struct mdns_answer ans; struct mdns_answer ans;
u16_t rrlen, atype, rrClass;; u16_t rrlen, atype, rrClass;;
@ -289,7 +294,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
} else if (atype == DNS_RRTYPE_SRV && rrlen > SIZEOF_DNS_RR_SRV) { } else if (atype == DNS_RRTYPE_SRV && rrlen > SIZEOF_DNS_RR_SRV) {
struct mdns_rr_srv srvRR; struct mdns_rr_srv srvRR;
memcpy(&srvRR, rp, SIZEOF_DNS_RR_SRV); memcpy(&srvRR, rp, SIZEOF_DNS_RR_SRV);
printf("prio %d, weight %d, port %d, target ", srvRR.prio, srvRR.weight, srvRR.port); printf("prio %d, weight %d, port %d, target ", srvRR.prio, srvRR.weight, ntohs(srvRR.port));
mdns_print_name(rp + SIZEOF_DNS_RR_SRV, hp); mdns_print_name(rp + SIZEOF_DNS_RR_SRV, hp);
printf("\n"); printf("\n");
} else { } else {
@ -339,15 +344,14 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
} }
return 1; return 1;
} }
#endif #endif // qLogAllTraffic
#endif // qDebugLog #endif // qDebugLog
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static u8_t* mdns_labels2str(u8_t* hdrP, u8_t* p, char* qStr)
// Convert a DNS domain name label sequence into C string with . seperators // Convert a DNS domain name label sequence into C string with . seperators
// Handles compression // Handles compression
static u8_t* mdns_labels2str(u8_t* hdrP, u8_t* p, char* qStr)
{ {
int i, n; int i, n;
@ -371,8 +375,8 @@ static u8_t* mdns_labels2str(u8_t* hdrP, u8_t* p, char* qStr)
return p; return p;
} }
static int mdns_str2labels(const char* name, u8_t* lseq, int max)
// Encode a <string>.<string>.<string> as a sequence of labels, return length // Encode a <string>.<string>.<string> as a sequence of labels, return length
static int mdns_str2labels(const char* name, u8_t* lseq, int max)
{ {
int i, n, sdx, idx = 0; int i, n, sdx, idx = 0;
int lc = 0; int lc = 0;
@ -381,12 +385,12 @@ static int mdns_str2labels(const char* name, u8_t* lseq, int max)
sdx = idx; sdx = idx;
while (name[idx] != '.' && name[idx] != 0) idx++; while (name[idx] != '.' && name[idx] != 0) idx++;
n = idx - sdx; n = idx - sdx;
*lseq++ = n; if (lc + 1 + n > max) {
lc++; printf(">>> mdns_str2labels: oversize (%d)\n", lc + 1 + n);
if (lc+n > max) {
printf(">>> mdns_str2labels: oversize (%d)\n",lc+n);
return 0; return 0;
} }
*lseq++ = n;
lc++;
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
*lseq++ = name[sdx + i]; *lseq++ = name[sdx + i];
lc += n; lc += n;
@ -396,8 +400,8 @@ static int mdns_str2labels(const char* name, u8_t* lseq, int max)
return lc; return lc;
} }
static u8_t* mdns_get_question(u8_t* hdrP, u8_t* qp, char* qStr, uint16_t* qClass, uint16_t* qType, u8_t* qUnicast)
// Unpack a DNS question RR at qp, return pointer to next RR // Unpack a DNS question RR at qp, return pointer to next RR
static u8_t* mdns_get_question(u8_t* hdrP, u8_t* qp, char* qStr, uint16_t* qClass, uint16_t* qType, u8_t* qUnicast)
{ {
struct mdns_query qr; struct mdns_query qr;
uint16_t cls; uint16_t cls;
@ -414,8 +418,8 @@ static u8_t* mdns_get_question(u8_t* hdrP, u8_t* qp, char* qStr, uint16_t* qClas
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static void mdns_add_response(const char* vKey, u16_t vType, u32_t ttl, const void* dataP, u16_t vDataSize)
// Add a record to the RR database list // Add a record to the RR database list
static void mdns_add_response(const char* vKey, u16_t vType, u32_t ttl, const void* dataP, u16_t vDataSize)
{ {
mdns_rsrc* rsrcP; mdns_rsrc* rsrcP;
int keyLen, recSize; int keyLen, recSize;
@ -423,9 +427,9 @@ static void mdns_add_response(const char* vKey, u16_t vType, u32_t ttl, const vo
keyLen = strlen(vKey) + 1; keyLen = strlen(vKey) + 1;
recSize = sizeof(mdns_rsrc) - kDummyDataSize + keyLen + vDataSize; recSize = sizeof(mdns_rsrc) - kDummyDataSize + keyLen + vDataSize;
rsrcP = (mdns_rsrc*)malloc(recSize); rsrcP = (mdns_rsrc*)malloc(recSize);
if (rsrcP==NULL) if (rsrcP == NULL) {
printf(">>> mdns_add_response: couldn't alloc %d\n",recSize); printf(">>> mdns_add_response: couldn't alloc %d\n",recSize);
else { } else {
rsrcP->rType = vType; rsrcP->rType = vType;
rsrcP->rTTL = ttl; rsrcP->rTTL = ttl;
rsrcP->rKeySize = keyLen; rsrcP->rKeySize = keyLen;
@ -442,13 +446,14 @@ static void mdns_add_response(const char* vKey, u16_t vType, u32_t ttl, const vo
void mdns_add_PTR(const char* rKey, u32_t ttl, const char* nmStr) void mdns_add_PTR(const char* rKey, u32_t ttl, const char* nmStr)
{ {
int nl; size_t nl;
u8_t lBuff[kMaxNameSize]; u8_t lBuff[kMaxNameSize];
nl = mdns_str2labels(nmStr, lBuff, sizeof(lBuff)); nl = mdns_str2labels(nmStr, lBuff, sizeof(lBuff));
if (nl>0) if (nl > 0) {
mdns_add_response(rKey, DNS_RRTYPE_PTR, ttl, lBuff, nl); mdns_add_response(rKey, DNS_RRTYPE_PTR, ttl, lBuff, nl);
} }
}
void mdns_add_SRV(const char* rKey, u32_t ttl, u16_t rPort, const char* targName) void mdns_add_SRV(const char* rKey, u32_t ttl, u16_t rPort, const char* targName)
{ {
@ -468,75 +473,85 @@ void mdns_add_SRV(const char* rKey, u32_t ttl, u16_t rPort, const char* targName
mdns_add_response(rKey, DNS_RRTYPE_SRV, ttl, &temp, SIZEOF_DNS_RR_SRV + nl); mdns_add_response(rKey, DNS_RRTYPE_SRV, ttl, &temp, SIZEOF_DNS_RR_SRV + nl);
} }
void mdns_add_TXT(const char* rKey, u32_t ttl, const char* txStr)
// Single TXT str, can be concatenated // Single TXT str, can be concatenated
void mdns_add_TXT(const char* rKey, u32_t ttl, const char* txStr)
{ {
char pstr[256];
u16_t n = strlen(txStr); u16_t n = strlen(txStr);
if (n > 255) if (n > 255) {
printf(">>> mdns_add_TXT oversize (%d)\n",n); printf(">>> mdns_add_TXT oversize (%d)\n",n);
else { return;
}
char *pstr = malloc(n + 1);
pstr[0] = n; pstr[0] = n;
memcpy(&pstr[1], txStr, n); memcpy(&pstr[1], txStr, n);
mdns_add_response(rKey, DNS_RRTYPE_TXT, ttl, txStr, n+1); mdns_add_response(rKey, DNS_RRTYPE_TXT, ttl, pstr, n + 1);
} free(pstr);
} }
void mdns_add_A(const char* rKey, u32_t ttl, ip_addr_t addr) void mdns_add_A(const char* rKey, u32_t ttl, const ip4_addr_t *addr)
{ {
mdns_add_response(rKey, DNS_RRTYPE_A, ttl, &addr, sizeof(addr)); mdns_add_response(rKey, DNS_RRTYPE_A, ttl, addr, sizeof(ip4_addr_t));
} }
#if LWIP_IPV6
void mdns_add_AAAA(const char* rKey, u32_t ttl, const ip6_addr_t *addr)
{
mdns_add_response(rKey, DNS_RRTYPE_AAAA, ttl, addr, sizeof(addr->addr));
}
#endif
void mdns_add_facility( const char* instanceName, // Friendly name, need not be unique void mdns_add_facility( const char* instanceName, // Friendly name, need not be unique
const char* serviceName, // Must be "name", e.g. "hap" or "http" const char* serviceName, // Must be "_name", e.g. "_hap" or "_http"
const char* addText, // Must be <key>=<value> const char* addText, // Must be <key>=<value>
mdns_flags flags, // TCP or UDP mdns_flags flags, // TCP or UDP
u16_t onPort, // port number u16_t onPort, // port number
u32_t ttl // seconds u32_t ttl // seconds
) )
{ {
char key[64]; size_t key_len = strlen(serviceName) + 12;
char fullName[128]; char *key = malloc(key_len + 1);
char devName[96]; size_t full_name_len = strlen(instanceName) + 1 + key_len;
struct ip_info ipInfo; char *fullName = malloc(full_name_len + 1);
size_t dev_name_len = strlen(instanceName) + 7;
char *devName = malloc(dev_name_len + 1);
#ifdef qDebugLog #ifdef qDebugLog
printf("\nmDNS advertising instance %s protocol %s text %s on port %d %s TTL %d secs\n", printf("\nmDNS advertising instance %s protocol %s", instanceName, serviceName);
instanceName, serviceName, addText, onPort, (flags & mdns_UDP) ? "UDP" : "TCP", ttl); if (addText) {
printf(" text %s", addText);
}
printf(" on port %d %s TTL %d secs\n", onPort, (flags & mdns_UDP) ? "UDP" : "TCP", ttl);
#endif #endif
snprintf(key, sizeof(key), "%s.%s.local.", serviceName, (flags & mdns_UDP) ? "_udp" :"_tcp"); snprintf(key, key_len + 1, "%s.%s.local.", serviceName, (flags & mdns_UDP) ? "_udp" :"_tcp");
snprintf(fullName, sizeof(fullName), "%s.%s", instanceName, key); snprintf(fullName, full_name_len + 1, "%s.%s", instanceName, key);
snprintf(devName, sizeof(devName), "%s.local.", instanceName); snprintf(devName, dev_name_len + 1, "%s.local.", instanceName);
if (!sdk_wifi_get_ip_info(STATION_IF,&ipInfo))
ipInfo.ip.addr = IPADDR_NONE;
// Order has significance for extraRR feature // Order has significance for extraRR feature
if (addText) {
mdns_add_TXT(fullName, ttl, addText); mdns_add_TXT(fullName, ttl, addText);
mdns_add_A(devName, ttl, ipInfo.ip); }
#if LWIP_IPV6
const ip6_addr_t addr6 = { {0ul, 0ul, 0ul, 0ul} };
mdns_add_AAAA(devName, ttl, &addr6);
#endif
const ip4_addr_t addr4 = { 0 };
mdns_add_A(devName, ttl, &addr4);
mdns_add_SRV(fullName, ttl, onPort, devName); mdns_add_SRV(fullName, ttl, onPort, devName);
mdns_add_PTR(key, ttl, fullName); mdns_add_PTR(key, ttl, fullName);
// Optional, makes us browsable // Optional, makes us browsable
if (flags & mdns_Browsable) if (flags & mdns_Browsable) {
mdns_add_PTR("_services._dns-sd._udp.local.", ttl, key); mdns_add_PTR("_services._dns-sd._udp.local.", ttl, key);
} }
static void mdns_update_ipaddr(struct ip_info* ipInfo) free(key);
// IP address has been defined/changed: update any A records with the new IP free(fullName);
{ free(devName);
mdns_rsrc* rp = gDictP;
while (rp != NULL) {
if (rp->rType==DNS_RRTYPE_A) {
#ifdef qDebugLog
printf("Updating A record for '%s' to %d.%d.%d.%d\n", rp->rData,
ip4_addr1(&ipInfo->ip), ip4_addr2(&ipInfo->ip), ip4_addr3(&ipInfo->ip), ip4_addr4(&ipInfo->ip));
#endif
memcpy(&rp->rData[rp->rKeySize], &ipInfo->ip, sizeof(ip_addr_t));
}
rp = rp->rNext;
}
} }
static mdns_rsrc* mdns_match(const char* qstr, u16_t qType) static mdns_rsrc* mdns_match(const char* qstr, u16_t qType)
@ -556,11 +571,22 @@ static mdns_rsrc* mdns_match(const char* qstr, u16_t qType)
return rp; return rp;
} }
static int mdns_add_to_answer(mdns_rsrc* rsrcP, u8_t* resp, int respLen)
// Create answer RR and append to resp[respLen], return new length // Create answer RR and append to resp[respLen], return new length
static int mdns_add_to_answer(mdns_rsrc* rsrcP, u8_t* resp, int respLen)
{ {
// Key is stored as C str, convert to labels // Key is stored as C str, convert to labels
respLen += mdns_str2labels(rsrcP->rData, &resp[respLen], DNS_MSG_SIZE-respLen); size_t rem = MDNS_RESPONDER_REPLY_SIZE - respLen;
size_t len = mdns_str2labels(rsrcP->rData, &resp[respLen], rem);
if (len == 0) {
// Overflow, skip this answer.
return respLen;
}
if ((len + SIZEOF_DNS_ANSWER + rsrcP->rDataSize) > rem) {
// Overflow, skip this answer.
printf(">>> mdns_add_to_answer: oversize (%d)\n", len + SIZEOF_DNS_ANSWER + rsrcP->rDataSize);
return respLen;
}
respLen += len;
// Answer fields: may be misaligned, so build and memcpy // Answer fields: may be misaligned, so build and memcpy
struct mdns_answer ans; struct mdns_answer ans;
@ -580,16 +606,29 @@ static int mdns_add_to_answer(mdns_rsrc* rsrcP, u8_t* resp, int respLen)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static void mdns_send_mcast(u8_t* msgP, int nBytes)
// Send UDP to multicast address // Send UDP to multicast address
static void mdns_send_mcast(const ip_addr_t *addr, u8_t* msgP, int nBytes)
{ {
struct pbuf* p; struct pbuf* p;
err_t err; err_t err;
#ifdef qLogAllTraffic
mdns_print_msg(msgP, nBytes);
#endif
p = pbuf_alloc(PBUF_TRANSPORT, nBytes, PBUF_RAM); p = pbuf_alloc(PBUF_TRANSPORT, nBytes, PBUF_RAM);
if (p) { if (p) {
memcpy(p->payload, msgP, nBytes); memcpy(p->payload, msgP, nBytes);
err = udp_sendto(gMDNS_pcb, p, &gMulticastAddr, DNS_MDNS_PORT); const ip_addr_t *dest_addr;
if (IP_IS_V6_VAL(*addr)) {
#if LWIP_IPV6
dest_addr = &gMulticastV6Addr;
#endif
} else {
dest_addr = &gMulticastV4Addr;
}
struct netif *netif = ip_current_input_netif();
err = udp_sendto_if(gMDNS_pcb, p, dest_addr, LWIP_IANA_PORT_MDNS, netif);
if (err == ERR_OK) { if (err == ERR_OK) {
#ifdef qDebugLog #ifdef qDebugLog
printf(" - responded with %d bytes err %d\n", nBytes, err); printf(" - responded with %d bytes err %d\n", nBytes, err);
@ -597,12 +636,13 @@ static void mdns_send_mcast(u8_t* msgP, int nBytes)
} else } else
printf(">>> mdns_send failed %d\n", err); printf(">>> mdns_send failed %d\n", err);
pbuf_free(p); pbuf_free(p);
} else } else {
printf(">>> mdns_send: alloc failed[%d]\n", nBytes); printf(">>> mdns_send: alloc failed[%d]\n", nBytes);
} }
}
static void mdns_reply(struct mdns_hdr* hdrP)
// Message has passed tests, may want to send an answer // Message has passed tests, may want to send an answer
static void mdns_reply(const ip_addr_t *addr, struct mdns_hdr* hdrP)
{ {
int i, nquestions, respLen; int i, nquestions, respLen;
struct mdns_hdr* rHdr; struct mdns_hdr* rHdr;
@ -611,9 +651,9 @@ static void mdns_reply(struct mdns_hdr* hdrP)
u8_t* qp; u8_t* qp;
u8_t* mdns_response; u8_t* mdns_response;
mdns_response = malloc(DNS_MSG_SIZE); mdns_response = malloc(MDNS_RESPONDER_REPLY_SIZE);
if (mdns_response == NULL) { if (mdns_response == NULL) {
printf(">>> mdns_reply could not alloc %d\n",DNS_MSG_SIZE); printf(">>> mdns_reply could not alloc %d\n", MDNS_RESPONDER_REPLY_SIZE);
return; return;
} }
@ -642,8 +682,46 @@ static void mdns_reply(struct mdns_hdr* hdrP)
if (qClass == DNS_RRCLASS_IN || qClass == DNS_RRCLASS_ANY) { if (qClass == DNS_RRCLASS_IN || qClass == DNS_RRCLASS_ANY) {
rsrcP = mdns_match(qStr, qType); rsrcP = mdns_match(qStr, qType);
if (rsrcP) { if (rsrcP) {
respLen = mdns_add_to_answer(rsrcP, mdns_response, respLen); #if LWIP_IPV6
if (rsrcP->rType == DNS_RRTYPE_AAAA) {
// Emit an answer for each ipv6 address.
struct netif *netif = ip_current_input_netif();
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
const ip6_addr_t *addr6 = netif_ip6_addr(netif, i);
#ifdef qDebugLog
char addr6_str[IP6ADDR_STRLEN_MAX];
ip6addr_ntoa_r(addr6, addr6_str, IP6ADDR_STRLEN_MAX);
printf("Updating AAAA record for '%s' to %s\n", rsrcP->rData, addr6_str);
#endif
memcpy(&rsrcP->rData[rsrcP->rKeySize], addr6, sizeof(addr6->addr));
size_t new_len = mdns_add_to_answer(rsrcP, mdns_response, respLen);
if (new_len > respLen) {
rHdr->numanswers = htons(htons(rHdr->numanswers) + 1); rHdr->numanswers = htons(htons(rHdr->numanswers) + 1);
respLen = new_len;
}
}
}
continue;
}
#endif
if (rsrcP->rType == DNS_RRTYPE_A) {
struct netif *netif = ip_current_input_netif();
#ifdef qDebugLog
char addr4_str[IP4ADDR_STRLEN_MAX];
ip4addr_ntoa_r(netif_ip4_addr(netif), addr4_str, IP4ADDR_STRLEN_MAX);
printf("Updating A record for '%s' to %s\n", rsrcP->rData, addr4_str);
#endif
memcpy(&rsrcP->rData[rsrcP->rKeySize], netif_ip4_addr(netif), sizeof(ip4_addr_t));
}
size_t new_len = mdns_add_to_answer(rsrcP, mdns_response, respLen);
if (new_len > respLen) {
rHdr->numanswers = htons(htons(rHdr->numanswers) + 1);
respLen = new_len;
}
// Extra RR logic: if SRV follows PTR, or A follows SRV, volunteer it in extraRR // Extra RR logic: if SRV follows PTR, or A follows SRV, volunteer it in extraRR
// Not required, but could do more here, see RFC6763 s12 // Not required, but could do more here, see RFC6763 s12
if (qType == DNS_RRTYPE_PTR) { if (qType == DNS_RRTYPE_PTR) {
@ -659,16 +737,29 @@ static void mdns_reply(struct mdns_hdr* hdrP)
if (respLen > SIZEOF_DNS_HDR) { if (respLen > SIZEOF_DNS_HDR) {
if (extra) { if (extra) {
respLen = mdns_add_to_answer(extra, mdns_response, respLen); if (extra->rType == DNS_RRTYPE_A) {
struct netif *netif = ip_current_input_netif();
#ifdef qDebugLog
char addr4_str[IP4ADDR_STRLEN_MAX];
ip4addr_ntoa_r(netif_ip4_addr(netif), addr4_str, IP4ADDR_STRLEN_MAX);
printf("Updating A record for '%s' to %s\n", extra->rData, addr4_str);
#endif
memcpy(&extra->rData[extra->rKeySize], netif_ip4_addr(netif), sizeof(ip4_addr_t));
}
size_t new_len = mdns_add_to_answer(extra, mdns_response, respLen);
if (new_len > respLen) {
rHdr->numextrarr = htons(htons(rHdr->numextrarr) + 1); rHdr->numextrarr = htons(htons(rHdr->numextrarr) + 1);
respLen = new_len;
} }
mdns_send_mcast(mdns_response, respLen);
} }
mdns_send_mcast(addr, mdns_response, respLen);
}
free(mdns_response); free(mdns_response);
} }
static void mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
// Callback from udp_recv // Callback from udp_recv
static void mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{ {
UNUSED_ARG(pcb); UNUSED_ARG(pcb);
UNUSED_ARG(port); UNUSED_ARG(port);
@ -676,30 +767,32 @@ static void mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t
u8_t* mdns_payload; u8_t* mdns_payload;
int plen; int plen;
// Sanity checks on size
plen = p->tot_len; plen = p->tot_len;
if (plen > DNS_MSG_SIZE) { #ifdef qLogIncoming
char addr_str[IPADDR_STRLEN_MAX];
ipaddr_ntoa_r(addr, addr_str, IPADDR_STRLEN_MAX);
printf("\n\nmDNS IPv%d got %d bytes from %s\n", IP_IS_V6(addr) ? 6 : 4, plen, addr_str);
#endif
// Sanity checks on size
if (plen > MDNS_RESPONDER_REPLY_SIZE) {
printf(">>> mdns_recv: pbuf too big\n"); printf(">>> mdns_recv: pbuf too big\n");
} else if (plen < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + 1 + SIZEOF_DNS_ANSWER + 1)) { } else if (plen < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + 1 + SIZEOF_DNS_ANSWER + 1)) {
printf(">>> mdns_recv: pbuf too small\n"); printf(">>> mdns_recv: pbuf too small\n");
} else { } else {
#ifdef qLogIncoming
printf("\n\nmDNS got %d bytes from %d.%d.%d.%d\n",plen, ip4_addr1(addr), ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr));
#endif
mdns_payload = malloc(plen); mdns_payload = malloc(plen);
if (!mdns_payload) if (!mdns_payload) {
printf(">>> mdns_recv, could not alloc %d\n",plen); printf(">>> mdns_recv, could not alloc %d\n",plen);
else { } else {
if (pbuf_copy_partial(p, mdns_payload, plen, 0) == plen) { if (pbuf_copy_partial(p, mdns_payload, plen, 0) == plen) {
struct mdns_hdr* hdrP = (struct mdns_hdr*) mdns_payload; struct mdns_hdr* hdrP = (struct mdns_hdr*) mdns_payload;
#ifdef qLogAllTraffic #ifdef qLogAllTraffic
mdns_print_msg(mdns_payload, plen); mdns_print_msg(mdns_payload, plen);
#endif #endif
if ( (hdrP->flags1 & (DNS_FLAG1_RESP + DNS_FLAG1_OPMASK + DNS_FLAG1_TRUNC) ) == 0 if ( (hdrP->flags1 & (DNS_FLAG1_RESP + DNS_FLAG1_OPMASK + DNS_FLAG1_TRUNC) ) == 0
&& hdrP->numquestions > 0 ) && hdrP->numquestions > 0 )
mdns_reply(hdrP); mdns_reply(addr, hdrP);
} }
free(mdns_payload); free(mdns_payload);
} }
@ -707,92 +800,82 @@ static void mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t
pbuf_free(p); pbuf_free(p);
} }
static void mdns_start()
// If we are in station mode and have an IP address, start a multicast UDP receive // If we are in station mode and have an IP address, start a multicast UDP receive
void mdns_init()
{ {
struct ip_info ipInfo;
err_t err; err_t err;
if (sdk_wifi_get_opmode() != STATION_MODE) { struct netif *station_netif = sdk_system_get_netif(STATION_IF);
printf(">>> mDNS_start: wifi opmode not station\n");
if (station_netif) {
// Start IGMP on the netif for our interface: this isn't done for us
if (!(station_netif->flags & NETIF_FLAG_IGMP)) {
station_netif->flags |= NETIF_FLAG_IGMP;
err = igmp_start(station_netif);
if (err != ERR_OK) {
printf(">>> mDNS_init: igmp_start on %c%c failed %d\n", station_netif->name[0], station_netif->name[1],err);
return;
}
}
if ((err = igmp_joingroup_netif(station_netif, ip_2_ip4(&gMulticastV4Addr))) != ERR_OK) {
printf(">>> mDNS_init: igmp_join failed %d\n",err);
return; return;
} }
if (!sdk_wifi_get_ip_info(STATION_IF,&ipInfo)) { #if LWIP_IPV6
printf(">>> mDNS_start: no IP addr\n"); if ((err = mld6_joingroup_netif(station_netif, ip_2_ip6(&gMulticastV6Addr))) != ERR_OK) {
printf(">>> mDNS_init: igmp_join failed %d\n",err);
return; return;
} }
#endif
}
mdns_update_ipaddr(&ipInfo); struct netif *softap_netif = sdk_system_get_netif(SOFTAP_IF);
if (softap_netif) {
if (softap_netif == NULL) {
printf(">>> mDNS_init: wifi opmode not softap\n");
return;
}
// Start IGMP on the netif for our interface: this isn't done for us // Start IGMP on the netif for our interface: this isn't done for us
struct netif* nfp = netif_list; if (!(softap_netif->flags & NETIF_FLAG_IGMP)) {
while (nfp!=NULL) { softap_netif->flags |= NETIF_FLAG_IGMP;
if ( ip_addr_cmp(&ipInfo.ip, &(nfp->ip_addr)) ) { err = igmp_start(softap_netif);
if (!(nfp->flags & NETIF_FLAG_IGMP)) {
nfp->flags |= NETIF_FLAG_IGMP;
err = igmp_start(nfp);
if (err != ERR_OK) { if (err != ERR_OK) {
printf(">>> mDNS_start: igmp_start on %c%c failed %d\n",nfp->name[0], nfp->name[1],err); printf(">>> mDNS_init: igmp_start on %c%c failed %d\n", softap_netif->name[0], softap_netif->name[1],err);
return; return;
} }
} }
}
nfp = nfp->next; if ((err = igmp_joingroup_netif(softap_netif, ip_2_ip4(&gMulticastV4Addr))) != ERR_OK) {
printf(">>> mDNS_init: igmp_join failed %d\n",err);
return;
} }
gMDNS_pcb = udp_new(); #if LWIP_IPV6
if ((err = mld6_joingroup_netif(softap_netif, ip_2_ip6(&gMulticastV6Addr))) != ERR_OK) {
printf(">>> mDNS_init: igmp_join failed %d\n",err);
return;
}
#endif
}
if (station_netif == NULL && softap_netif == NULL) {
printf(">>> mDNS_init: wifi opmode none\n");
return;
}
gMDNS_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
if (!gMDNS_pcb) { if (!gMDNS_pcb) {
printf(">>> mDNS_start: udp_new failed\n"); printf(">>> mDNS_init: udp_new failed\n");
return; return;
} }
if ((err=igmp_joingroup(&ipInfo.ip, &gMulticastAddr)) != ERR_OK) { if ((err = udp_bind(gMDNS_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS)) != ERR_OK) {
printf(">>> mDNS_start: igmp_join failed %d\n",err); printf(">>> mDNS_init: udp_bind failed %d\n",err);
return;
}
if ((err=udp_bind(gMDNS_pcb, IP_ADDR_ANY, DNS_MDNS_PORT)) != ERR_OK) {
printf(">>> mDNS_start: udp_bind failed %d\n",err);
return; return;
} }
udp_recv(gMDNS_pcb, mdns_recv, NULL); udp_recv(gMDNS_pcb, mdns_recv, NULL);
} }
static void mdns_close()
{
udp_remove(gMDNS_pcb);
gMDNS_pcb = NULL;
#ifdef qDebugLog
printf("Closing mDNS\n");
#endif
}
static void mdns_task(void *pvParameters)
{
uint8_t hasIP = 0;
uint8_t status;
UNUSED_ARG(pvParameters);
ipaddr_aton(DNS_MULTICAST_ADDRESS, &gMulticastAddr);
// Wait until we have joined AP and are assigned an IP
while (1) {
status = (sdk_wifi_station_get_connect_status() == STATION_GOT_IP);
if (status != hasIP) {
if (status) mdns_start();
else mdns_close();
hasIP = status;
}
vTaskDelayMs(status ? 1000 : 100);
}
}
void mdns_init()
{
#if LWIP_IGMP
xTaskCreate(mdns_task, "MDNS", kMDNSStackSize, NULL, 2, NULL);
#else
#error "LWIP_IGMP needs to be defined in lwipopts.h"
#endif
}

View file

@ -1,20 +1,25 @@
#ifndef __MDNSRESPONDER_H__
#define __MDNSRESPONDER_H__
#include <lwip/ip_addr.h>
/* /*
* Basic multicast DNS responder * Basic multicast DNS responder
* *
* Advertises the IP address, port, and characteristics of a service to other devices using multicast DNS on the same LAN, * Advertises the IP address, port, and characteristics of a service to other
* so they can find devices with addresses dynamically allocated by DHCP. See avahi, Bonjour, etc * devices using multicast DNS on the same LAN, so they can find devices with
* See RFC6762, RFC6763 * addresses dynamically allocated by DHCP. See avahi, Bonjour, etc See RFC6762,
* RFC6763
* *
* This sample code is in the public domain. * This sample code is in the public domain.
* *
* by M J A Hamel 2016 * by M J A Hamel 2016
*/ */
#ifndef __MDNSRESPONDER_H__
#define __MDNSRESPONDER_H__
#include <lwip/ip_addr.h>
/* The default maximum reply size, increase as necessary. */
#ifndef MDNS_RESPONDER_REPLY_SIZE
#define MDNS_RESPONDER_REPLY_SIZE 320
#endif
// Starts the mDNS responder task, call first // Starts the mDNS responder task, call first
void mdns_init(); void mdns_init();
@ -42,7 +47,10 @@ void mdns_add_facility( const char* instanceName, // Short user-friendly insta
void mdns_add_PTR(const char* rKey, u32_t ttl, const char* nameStr); void mdns_add_PTR(const char* rKey, u32_t ttl, const char* nameStr);
void mdns_add_SRV(const char* rKey, u32_t ttl, u16_t rPort, const char* targname); void mdns_add_SRV(const char* rKey, u32_t ttl, u16_t rPort, const char* targname);
void mdns_add_TXT(const char* rKey, u32_t ttl, const char* txtStr); void mdns_add_TXT(const char* rKey, u32_t ttl, const char* txtStr);
void mdns_add_A (const char* rKey, u32_t ttl, ip_addr_t addr); void mdns_add_A (const char* rKey, u32_t ttl, const ip4_addr_t *addr);
#if LWIP_IPV6
void mdns_add_AAAA(const char* rKey, u32_t ttl, const ip6_addr_t *addr);
#endif
/* Sample usage, advertising a secure web service /* Sample usage, advertising a secure web service