add mdns discovery and make it the default
This commit is contained in:
+460
-62
@@ -46,38 +46,91 @@ bool TCPSocket::isValid() {
|
||||
return fcntl(m_sfd, F_GETFL) != -1;
|
||||
}
|
||||
|
||||
bool parseIp(const char* server, struct in_addr *sin_addr) {
|
||||
if (inet_aton(server, sin_addr) == 1) {
|
||||
return true;
|
||||
}
|
||||
struct hostent* he = gethostbyname(server);
|
||||
if (he == nullptr) {
|
||||
return false;
|
||||
}
|
||||
memcpy(sin_addr, he->h_addr_list[0], he->h_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
int socketConnect(const char* server, uint16_t port, bool udp, socketaddress* storeAddress, int tcpConnectTimeout,
|
||||
int tcpKeepAliveInterval) {
|
||||
int socketConnect(const char* server, uint16_t port, int udpProto, socketaddress* storeAddress,
|
||||
int tcpConnToUdpOptions, int tcpKeepAliveInterval, struct in_addr* storeIntf) {
|
||||
socketaddress localAddress;
|
||||
socketaddress* address = storeAddress ? storeAddress : &localAddress;
|
||||
memset(reinterpret_cast<char*>(address), 0, sizeof(*address));
|
||||
|
||||
if (inet_aton(server, &address->sin_addr) == 0) {
|
||||
struct hostent* he = gethostbyname(server);
|
||||
if (he == nullptr) {
|
||||
// parse "address[@intf]"
|
||||
const char* pos = strchr(server, '@');
|
||||
struct in_addr intf;
|
||||
intf.s_addr = INADDR_ANY;
|
||||
if (pos) {
|
||||
char* str = strdupa(server);
|
||||
char* ifa = strchr(str, '@');
|
||||
ifa[0] = 0;
|
||||
ifa++;
|
||||
if (!str[0] || !parseIp(str, &address->sin_addr)) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(&address->sin_addr, he->h_addr_list[0], he->h_length);
|
||||
if (!parseIp(ifa, &intf)) {
|
||||
return -1;
|
||||
}
|
||||
} else if (!parseIp(server, &address->sin_addr)) {
|
||||
return -1;
|
||||
}
|
||||
if (storeIntf) {
|
||||
*storeIntf = intf;
|
||||
}
|
||||
address->sin_family = AF_INET;
|
||||
address->sin_port = (in_port_t)htons(port);
|
||||
|
||||
int sfd = socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0);
|
||||
int sfd = socket(AF_INET, udpProto ? SOCK_DGRAM : SOCK_STREAM, udpProto);
|
||||
if (sfd < 0) {
|
||||
return -1;
|
||||
return -2;
|
||||
}
|
||||
int ret;
|
||||
if (udp) {
|
||||
int ret = 0;
|
||||
if (udpProto) {
|
||||
#define RET(chk, next) if (ret >= 0) { ret = chk; if (ret < 0) ret = next;}
|
||||
struct sockaddr_in bindAddress = *address;
|
||||
bindAddress.sin_addr.s_addr = INADDR_ANY;
|
||||
ret = bind(sfd, (struct sockaddr*)&bindAddress, sizeof(bindAddress));
|
||||
if (ret >= 0) {
|
||||
ret = ::connect(sfd, (struct sockaddr*)address, sizeof(*address));
|
||||
// allow multiple processes using the same port for multicast on the same host
|
||||
int optint = 1;
|
||||
RET(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optint, sizeof(optint)), -3);
|
||||
#ifdef SO_REUSEPORT
|
||||
RET(setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &optint, sizeof(optint)), -3);
|
||||
#endif
|
||||
bool isMcast = IN_MULTICAST(ntohl(address->sin_addr.s_addr));
|
||||
if (isMcast) {
|
||||
// loop-back sent multicast packets
|
||||
unsigned char optchar = 1;
|
||||
RET(setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_LOOP, &optchar, sizeof(optchar)), -3);
|
||||
if (ret >= 0) {
|
||||
// join the multicast inbound
|
||||
ip_mreq req = {};
|
||||
req.imr_multiaddr = address->sin_addr;
|
||||
req.imr_interface = intf;
|
||||
RET(setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req)), -7);
|
||||
}
|
||||
if (ret >= 0 && intf.s_addr != INADDR_ANY) {
|
||||
// set outgoing interface to other than default (determined by routing table)
|
||||
RET(setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_IF, &intf, sizeof(intf)), -3);
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
close(sfd);
|
||||
return -1;
|
||||
bindAddress.sin_addr = intf;
|
||||
if (!(tcpConnToUdpOptions&0x01)) {
|
||||
bindAddress.sin_port = 0; // do not bind to same source port for outgoing packets
|
||||
}
|
||||
RET(bind(sfd, (struct sockaddr*)&bindAddress, sizeof(bindAddress)), -4);
|
||||
if (tcpConnToUdpOptions&0x02) {
|
||||
// set the default target address for later use by send()
|
||||
RET(::connect(sfd, (struct sockaddr*)address, sizeof(*address)), -5);
|
||||
if (ret < 0) {
|
||||
close(sfd);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return sfd;
|
||||
}
|
||||
@@ -85,7 +138,7 @@ int tcpKeepAliveInterval) {
|
||||
ret = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
if (ret < 0) {
|
||||
close(sfd);
|
||||
return -1;
|
||||
return -3;
|
||||
}
|
||||
if (tcpKeepAliveInterval > 0) {
|
||||
value = 1;
|
||||
@@ -124,66 +177,80 @@ int tcpKeepAliveInterval) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (tcpConnectTimeout > 0 && fcntl(sfd, F_SETFL, O_NONBLOCK) < 0) { // set non-blocking
|
||||
if (tcpConnToUdpOptions > 0 && fcntl(sfd, F_SETFL, O_NONBLOCK) < 0) { // set non-blocking
|
||||
close(sfd);
|
||||
return -1;
|
||||
return -4;
|
||||
}
|
||||
ret = ::connect(sfd, (struct sockaddr*)address, sizeof(*address));
|
||||
if (ret != 0) {
|
||||
if (ret < 0 && (tcpConnectTimeout <= 0 || errno != EINPROGRESS)) {
|
||||
if (ret < 0 && (tcpConnToUdpOptions <= 0 || errno != EINPROGRESS)) {
|
||||
close(sfd);
|
||||
return -1;
|
||||
return -5;
|
||||
}
|
||||
if (tcpConnectTimeout > 0) {
|
||||
#if defined(HAVE_PPOLL) || defined(HAVE_PSELECT)
|
||||
struct timespec tdiff;
|
||||
tdiff.tv_sec = tcpConnectTimeout;
|
||||
tdiff.tv_nsec = 0;
|
||||
#else
|
||||
struct timeval tdiff;
|
||||
tdiff.tv_sec = tcpConnectTimeout;
|
||||
tdiff.tv_usec = 0;
|
||||
#endif
|
||||
#ifdef HAVE_PPOLL
|
||||
nfds_t nfds = 1;
|
||||
struct pollfd fds[nfds];
|
||||
memset(fds, 0, sizeof(fds));
|
||||
fds[0].fd = sfd;
|
||||
fds[0].events = POLLIN|POLLOUT;
|
||||
ret = ppoll(fds, nfds, &tdiff, nullptr);
|
||||
if (ret == 1 && fds[0].revents & POLLERR) {
|
||||
ret = -1;
|
||||
}
|
||||
#else
|
||||
fd_set readfds, writefds, exceptfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
FD_SET(sfd, &readfds);
|
||||
FD_SET(sfd, &writefds);
|
||||
FD_SET(sfd, &exceptfds);
|
||||
#ifdef HAVE_PSELECT
|
||||
ret = pselect(sfd + 1, &readfds, &writefds, &exceptfds, &tdiff, nullptr);
|
||||
#else
|
||||
ret = select(sfd + 1, &readfds, &writefds, &exceptfds, &tdiff);
|
||||
#endif
|
||||
if (ret >= 1 && FD_ISSET(sfd, &exceptfds)) {
|
||||
ret = -1;
|
||||
}
|
||||
#endif
|
||||
if (ret == -1 || ret == 0) {
|
||||
if (tcpConnToUdpOptions > 0) {
|
||||
ret = socketPoll(sfd, POLLIN|POLLOUT, tcpConnToUdpOptions);
|
||||
if (ret <= 0) {
|
||||
close(sfd);
|
||||
return -1;
|
||||
return -6;
|
||||
}
|
||||
if (fcntl(sfd, F_SETFL, 0) < 0) { // set blocking again
|
||||
close(sfd);
|
||||
return -1;
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sfd;
|
||||
}
|
||||
|
||||
int socketPoll(int sfd, int which, int timeoutSeconds) {
|
||||
int ret;
|
||||
#if defined(HAVE_PPOLL) || defined(HAVE_PSELECT)
|
||||
struct timespec tdiff;
|
||||
tdiff.tv_sec = timeoutSeconds;
|
||||
tdiff.tv_nsec = 0;
|
||||
#else
|
||||
struct timeval tdiff;
|
||||
tdiff.tv_sec = timeoutSeconds;
|
||||
tdiff.tv_usec = 0;
|
||||
#endif
|
||||
#ifdef HAVE_PPOLL
|
||||
nfds_t nfds = 1;
|
||||
struct pollfd fds[nfds];
|
||||
memset(fds, 0, sizeof(fds));
|
||||
fds[0].fd = sfd;
|
||||
fds[0].events = which;
|
||||
ret = ppoll(fds, nfds, &tdiff, nullptr);
|
||||
if (ret >= 1 && fds[0].revents & POLLERR) {
|
||||
ret = -1;
|
||||
} else if (ret >= 1) {
|
||||
ret = fds[0].revents;
|
||||
}
|
||||
#else
|
||||
fd_set readfds, writefds, exceptfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
if (which & POLLIN) {
|
||||
FD_SET(sfd, &readfds);
|
||||
}
|
||||
if (which & POLLOUT) {
|
||||
FD_SET(sfd, &writefds);
|
||||
}
|
||||
FD_SET(sfd, &exceptfds);
|
||||
#ifdef HAVE_PSELECT
|
||||
ret = pselect(sfd + 1, &readfds, &writefds, &exceptfds, &tdiff, nullptr);
|
||||
#else
|
||||
ret = select(sfd + 1, &readfds, &writefds, &exceptfds, &tdiff);
|
||||
#endif
|
||||
if (ret >= 1 && FD_ISSET(sfd, &exceptfds)) {
|
||||
ret = -1;
|
||||
} else if (ret >= 1) {
|
||||
ret = (FD_ISSET(sfd, &readfds) ? POLLIN : 0) | (FD_ISSET(sfd, &writefds) ? POLLOUT : 0);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
TCPSocket* TCPSocket::connect(const string& server, const uint16_t& port, int timeout) {
|
||||
socketaddress address;
|
||||
@@ -243,4 +310,335 @@ TCPSocket* TCPServer::newSocket() {
|
||||
return new TCPSocket(sfd, &address);
|
||||
}
|
||||
|
||||
size_t readNameRecursive(uint8_t *data, size_t len, size_t pos, size_t maxPos, int maxDepth, char* str, size_t slen,
|
||||
size_t* spos) {
|
||||
size_t nlen = data[pos++];
|
||||
if ((nlen&0xc0) == 0xc0) {
|
||||
// pointer
|
||||
size_t p = ((nlen&0x3f) << 8) | data[pos];
|
||||
if (p >= len || maxDepth < 1) {
|
||||
return 0;
|
||||
}
|
||||
readNameRecursive(data, len, p, len, maxDepth-1, str, slen, spos);
|
||||
return 2;
|
||||
}
|
||||
if (!nlen) {
|
||||
return 1;
|
||||
}
|
||||
if (pos+nlen > maxPos || *spos+1+nlen > slen) {
|
||||
return 0;
|
||||
}
|
||||
if (*spos > 0) {
|
||||
str[*spos] = '.';
|
||||
*spos += 1;
|
||||
}
|
||||
memcpy(str+*spos, data+pos, nlen);
|
||||
*spos += nlen;
|
||||
pos += nlen;
|
||||
size_t add;
|
||||
if (pos >= maxPos || maxDepth < 1) {
|
||||
add = 0;
|
||||
} else {
|
||||
add = readNameRecursive(data, len, pos, maxPos, maxDepth-1, str, slen, spos);
|
||||
if (add == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1+nlen+add;
|
||||
}
|
||||
|
||||
size_t readName(uint8_t *data, size_t len, size_t pos, size_t maxPos, char* str, size_t slen, size_t* spos) {
|
||||
return readNameRecursive(data, len, pos, maxPos, 4, str, slen, spos);
|
||||
}
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
uint16_t id;
|
||||
struct {
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
bool qr: 1; // 0=query, 1=answer
|
||||
uint8_t opcode: 4; // 0=standard query, 1=inverse query, 2=status request
|
||||
bool aa: 1; // authoritive answer
|
||||
bool tc: 1; // truncation
|
||||
bool rd: 1; // recursion desired
|
||||
#else
|
||||
bool rd: 1; // recursion desired
|
||||
bool tc: 1; // truncation
|
||||
bool aa: 1; // authoritive answer
|
||||
uint8_t opcode: 4; // 0=standard query, 1=inverse query, 2=status request
|
||||
bool qr: 1; // 0=query, 1=answer
|
||||
#endif
|
||||
};
|
||||
struct {
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
bool ra: 1; // recursion available
|
||||
uint8_t z: 3; // zero
|
||||
uint8_t rcode: 4; // response code: 0=OK
|
||||
#else
|
||||
uint8_t rcode: 4; // response code: 0=OK
|
||||
uint8_t z: 3; // zero
|
||||
bool ra: 1; // recursion available
|
||||
#endif
|
||||
};
|
||||
uint16_t qdCount; // question section entry count
|
||||
uint16_t anCount; // answer section entry count
|
||||
uint16_t nsCount; // name server section entry count
|
||||
uint16_t arCount; // additional records section entry count
|
||||
} dns_query_t;
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
uint8_t len;
|
||||
// unsigned char *name;
|
||||
} dns_qname_t;
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
dns_qname_t qname;
|
||||
uint16_t qtype;
|
||||
uint16_t qclass; // top bit used for unicast-response
|
||||
} dns_question_t;
|
||||
|
||||
#define DNS_TYPE_A 0x01
|
||||
#define DNS_TYPE_PTR 0x0c
|
||||
#define DNS_TYPE_TXT 0x10
|
||||
#define DNS_TYPE_SRV 0x21
|
||||
#define DNS_CLASS_AA 0x01
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
dns_qname_t aname;
|
||||
uint16_t atype;
|
||||
uint16_t aclass;
|
||||
uint32_t ttl;
|
||||
uint16_t rdLength;
|
||||
// uint8_t *rData;
|
||||
} dns_answer_t;
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
uint16_t priority;
|
||||
uint16_t weight;
|
||||
uint16_t port;
|
||||
dns_qname_t target;
|
||||
} dns_rr_srv_t;
|
||||
|
||||
int resolveMdnsOneShot(const char* url, mdns_oneshot_t *result, mdns_oneshot_t *moreResults, size_t *moreCount) {
|
||||
memset(result, 0, sizeof(mdns_oneshot_t));
|
||||
socketaddress address;
|
||||
const char* pos = strchr(url, '@');
|
||||
string limitId = string(url);
|
||||
string device = "224.0.0.251";
|
||||
if (pos) {
|
||||
limitId = limitId.substr(0, pos-url);
|
||||
device += string(pos);
|
||||
}
|
||||
int sock = socketConnect(device.c_str(), 5353, IPPROTO_UDP, &address);
|
||||
if (sock < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t record[1500];
|
||||
memset(record, 0, sizeof(record));
|
||||
dns_query_t *dnsr = reinterpret_cast<dns_query_t*>(record);
|
||||
dnsr->qdCount = htons(1);
|
||||
size_t len = sizeof(dns_query_t);
|
||||
dns_question_t *q = reinterpret_cast<dns_question_t*>(reinterpret_cast<uint8_t*>(dnsr)+len);
|
||||
const uint8_t serviceName[] = {
|
||||
0x06, 0x5f, 0x65, 0x62, 0x75, 0x73, 0x64, // _ebusd
|
||||
0x04, 0x5f, 0x74, 0x63, 0x70, // _tcp
|
||||
0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // local
|
||||
0x00
|
||||
};
|
||||
memcpy(&q->qname.len, serviceName, sizeof(serviceName));
|
||||
len += sizeof(serviceName)-1; // -1 for final empty qname
|
||||
q = reinterpret_cast<dns_question_t*>(reinterpret_cast<uint8_t*>(dnsr)+len);
|
||||
q->qtype = htons(DNS_TYPE_PTR);
|
||||
q->qclass = htons(
|
||||
0x8000 | // unicast response bit
|
||||
DNS_CLASS_AA);
|
||||
len += sizeof(dns_question_t);
|
||||
ssize_t done = sendto(sock, record, len, 0, reinterpret_cast<sockaddr*>(&address), sizeof(address));
|
||||
#ifdef DEBUG_MDNS
|
||||
printf("mdns: sent %ld, err %d\n", done, errno);
|
||||
#endif
|
||||
fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||
bool found = false, foundMore = false;
|
||||
size_t moreRemain = moreResults && moreCount && *moreCount > 0 ? *moreCount : 0;
|
||||
if (moreRemain > 0) {
|
||||
*moreCount = 0;
|
||||
}
|
||||
#ifdef DEBUG_MDNS
|
||||
socketaddress aaddr;
|
||||
socklen_t aaddrlen = 0;
|
||||
#endif
|
||||
for (int i=0; i < (found ? 3 : 5); i++) { // up to 5 seconds, at least 3 seconds
|
||||
int ret = socketPoll(sock, POLLIN, 1);
|
||||
done = 0;
|
||||
if (ret > 0 && (ret&POLLIN)) {
|
||||
#ifdef DEBUG_MDNS
|
||||
aaddrlen = sizeof(aaddr);
|
||||
done = recvfrom(sock, record, sizeof(record), 0, reinterpret_cast<sockaddr*>(&aaddr), &aaddrlen);
|
||||
#else
|
||||
done = recv(sock, record, sizeof(record), 0);
|
||||
#endif
|
||||
}
|
||||
if (done == 0 || (done < 0 && errno == EAGAIN) || done < sizeof(dns_query_t)) {
|
||||
continue;
|
||||
}
|
||||
dnsr = reinterpret_cast<dns_query_t*>(record);
|
||||
// todo length check
|
||||
#ifdef DEBUG_MDNS
|
||||
printf("mdns: got %d from %2.2x:%d, q=%d, an=%d, ns=%d, ar=%d\n", done, aaddr.sin_addr.s_addr,
|
||||
ntohs(aaddr.sin_port), ntohs(dnsr->qdCount), ntohs(dnsr->anCount), ntohs(dnsr->nsCount),
|
||||
ntohs(dnsr->arCount));
|
||||
#endif
|
||||
if (dnsr->qdCount || done < sizeof(dns_query_t)+sizeof(serviceName)+4*sizeof(dns_answer_t)+(26+2)+4+1+1+
|
||||
sizeof(dns_rr_srv_t)+(2+1+sizeof(mdns_oneshot_t::id)-1+1+5+1+sizeof(mdns_oneshot_t::proto)-1)+4
|
||||
// "eBUS Adapter Shield xxxxxx", "id=xxxxxxxxxxxx.proto=ens"
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
int anCnt = ntohs(dnsr->anCount);
|
||||
int arCnt = ntohs(dnsr->arCount);
|
||||
if (anCnt < 1 || dnsr->nsCount || arCnt < 1) {
|
||||
continue;
|
||||
}
|
||||
len = sizeof(dns_query_t);
|
||||
char name[256];
|
||||
bool validPort = false;
|
||||
struct in_addr validAddress;
|
||||
validAddress.s_addr = INADDR_ANY;
|
||||
char id[sizeof(mdns_oneshot_t::id)] = {0};
|
||||
char proto[sizeof(mdns_oneshot_t::proto)] = {0};
|
||||
for (int i=0; i < anCnt+arCnt && len < done; i++) {
|
||||
dns_answer_t *a = reinterpret_cast<dns_answer_t*>(reinterpret_cast<uint8_t*>(dnsr)+len);
|
||||
if (i == 0) {
|
||||
if (memcmp(&a->aname.len, serviceName, sizeof(serviceName)) != 0) {
|
||||
#ifdef DEBUG_MDNS
|
||||
printf("mdns: an 0 mismatch\n");
|
||||
#endif
|
||||
anCnt = 0;
|
||||
break; // skip this one
|
||||
}
|
||||
#ifdef DEBUG_MDNS
|
||||
printf("mdns: an 0 match\n");
|
||||
#endif
|
||||
len += sizeof(serviceName)-1; // -1 for final empty qname
|
||||
} else {
|
||||
// read name
|
||||
size_t pos = 0;
|
||||
size_t nlen = readName(record, done, len, done, name, sizeof(name), &pos);
|
||||
if (nlen == 0) {
|
||||
anCnt = 0;
|
||||
break; // skip this one
|
||||
}
|
||||
len += nlen-1; // -1 for final empty qname / right pointer for below
|
||||
name[pos] = 0;
|
||||
#ifdef DEBUG_MDNS
|
||||
printf("mdns: a%c %d name=%s\n", i >= anCnt ? 'r' : 'n', i >= anCnt ? i-anCnt : i, name);
|
||||
#endif
|
||||
}
|
||||
a = reinterpret_cast<dns_answer_t*>(reinterpret_cast<uint8_t*>(dnsr)+len);
|
||||
int atype = ntohs(a->atype);
|
||||
int aclass = ntohs(a->aclass);
|
||||
#ifdef DEBUG_MDNS
|
||||
printf(" atype %d, aclass %d\n", atype, aclass);
|
||||
#endif
|
||||
if (i == 0 && (atype != DNS_TYPE_PTR
|
||||
|| aclass != DNS_CLASS_AA)) {
|
||||
anCnt = 0;
|
||||
break; // skip this one
|
||||
}
|
||||
len += sizeof(dns_answer_t);
|
||||
int rdLen = ntohs(a->rdLength);
|
||||
#ifdef DEBUG_MDNS
|
||||
printf(" rd %d @%2.2x = ", rdLen, len);
|
||||
for (int i=0; i < rdLen && len+i < done; i++) {
|
||||
printf("%2.2x ", reinterpret_cast<uint8_t*>(dnsr)[len+i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
if (atype == DNS_TYPE_PTR || atype == DNS_TYPE_TXT) {
|
||||
size_t pos = 0;
|
||||
if (readName(record, done, len, len+rdLen, name, sizeof(name), &pos) == 0) {
|
||||
anCnt = 0;
|
||||
break; // skip this one
|
||||
}
|
||||
name[pos] = 0;
|
||||
#ifdef DEBUG_MDNS
|
||||
printf(" %s=%s\n", (atype == DNS_TYPE_TXT) ? "txt" : "ptr", name);
|
||||
#endif
|
||||
if (atype == DNS_TYPE_TXT && name[0]) {
|
||||
char* sep = strchr(name, '=');
|
||||
char* sep2;
|
||||
if (sep && strncmp(name, "id", sep-name) == 0) {
|
||||
sep2 = strchr(name, '.');
|
||||
if (sep2-sep-1 == sizeof(mdns_oneshot_t::id)-1) {
|
||||
memcpy(id, sep+1, sizeof(mdns_oneshot_t::id)-1);
|
||||
} else {
|
||||
sep = nullptr;
|
||||
}
|
||||
sep = sep ? strchr(sep2+1, '=') : nullptr;
|
||||
}
|
||||
if (sep && strncmp(sep2+1, "proto", sep-sep2-1) == 0 && (
|
||||
pos == sep+1+sizeof(mdns_oneshot_t::proto)-1-name
|
||||
|| strchr(sep+1, '.') == sep+1+sizeof(mdns_oneshot_t::proto)-1)) {
|
||||
memcpy(proto, sep+1, sizeof(mdns_oneshot_t::proto)-1);
|
||||
}
|
||||
}
|
||||
} else if (atype == DNS_TYPE_SRV && rdLen >= sizeof(dns_rr_srv_t)) {
|
||||
dns_rr_srv_t *srv = reinterpret_cast<dns_rr_srv_t*>(record+len);
|
||||
size_t pos = 0;
|
||||
if (readName(record, done, len+sizeof(dns_rr_srv_t)-1, len+rdLen, name, sizeof(name), &pos) == 0) {
|
||||
anCnt = 0;
|
||||
break; // skip this one
|
||||
}
|
||||
name[pos] = 0;
|
||||
validPort = ntohs(srv->port) == 9999;
|
||||
#ifdef DEBUG_MDNS
|
||||
printf(" srv port %d target %s\n", ntohs(srv->port), name);
|
||||
#endif
|
||||
} else if (atype == DNS_TYPE_A) {
|
||||
// ipv4 address
|
||||
#ifdef DEBUG_MDNS
|
||||
printf(" address %d.%d.%d.%d\n", record[len], record[len+1], record[len+2], record[len+3]);
|
||||
#endif
|
||||
memcpy(reinterpret_cast<uint8_t*>(&validAddress.s_addr), record+len, 4);
|
||||
}
|
||||
len += rdLen;
|
||||
}
|
||||
if (!anCnt) {
|
||||
continue;
|
||||
}
|
||||
if (validPort && validAddress.s_addr != INADDR_ANY && validAddress.s_addr != INADDR_NONE && proto[0]) {
|
||||
mdns_oneshot_t *storeTo;
|
||||
if (!found && (!limitId.length() || limitId.compare(id) == 0)) {
|
||||
storeTo = result;
|
||||
found = true;
|
||||
if (limitId.length()) {
|
||||
break; // found the desired one, no need to wait for another
|
||||
}
|
||||
} else if (found && strcmp(id, result->id) == 0) {
|
||||
// skip duplicate answer
|
||||
continue;
|
||||
} else {
|
||||
foundMore = !limitId.length();
|
||||
if (moreRemain > 0) {
|
||||
storeTo = moreResults++;
|
||||
moreRemain--;
|
||||
(*moreCount)++;
|
||||
} else if (!found) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
storeTo->address = validAddress;
|
||||
strncpy(storeTo->id, id, sizeof(mdns_oneshot_t::id));
|
||||
strncpy(storeTo->proto, proto, sizeof(mdns_oneshot_t::proto));
|
||||
if (found && moreRemain == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
return found ? foundMore ? 2 : 1 : 0;
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
Reference in New Issue
Block a user