Skip to content

Commit 7b5a7c9

Browse files
committed
dnssd: added windows implementation with mdns.h
Implementation based on https://github.com/mjansson/mdns/ Signed-off-by: Adrian Suciu <[email protected]>
1 parent ab9a0e7 commit 7b5a7c9

File tree

4 files changed

+1473
-1
lines changed

4 files changed

+1473
-1
lines changed

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
261261
if(WITH_NETWORK_BACKEND)
262262
message(STATUS "Building with Network back end support")
263263
if (WIN32)
264-
list(APPEND LIBS_TO_LINK wsock32 ws2_32)
264+
list(APPEND LIBS_TO_LINK wsock32 iphlpapi ws2_32)
265265
endif()
266266

267267
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -301,6 +301,10 @@ if(WITH_NETWORK_BACKEND)
301301
list(APPEND LIBIIO_CFILES dns_sd_avahi.c dns_sd.c)
302302
set(AVAHI_LIBRARIES ${AVAHI_CLIENT_LIBRARIES} ${AVAHI_COMMON_LIBRARIES})
303303
list(APPEND LIBS_TO_LINK ${AVAHI_LIBRARIES})
304+
elseif(WIN32)
305+
set(HAVE_DNS_SD ON)
306+
list(APPEND LIBIIO_CFILES dns_sd_windows.c dns_sd.c)
307+
message(STATUS "Building with mdns, A Public domain mDNS/DNS-SD library in C ")
304308
else()
305309
message(STATUS "Building without DNS-SD (Zeroconf) support")
306310
endif()

dns_sd.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ static void dnssd_remove_node(struct dns_sd_discovery_data **ddata, int n)
3434
int i;
3535

3636
d = *ddata;
37+
ldata = NULL;
3738

3839
if (n == 0) {
3940
tdata = d->next;

dns_sd_windows.c

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
/*
2+
* libiio - Library for interfacing industrial I/O (IIO) devices
3+
*
4+
* Copyright (C) 2014-2020 Analog Devices, Inc.
5+
* Author: Adrian Suciu <[email protected]>
6+
*
7+
* This library is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU Lesser General Public
9+
* License as published by the Free Software Foundation; either
10+
* version 2.1 of the License, or (at your option) any later version.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* Based on https://github.com/mjansson/mdns/blob/ce2e4f789f06429008925ff8f18c22036e60201e/mdns.c
18+
* which is Licensed under Public Domain
19+
*/
20+
21+
#include <stdio.h>
22+
#include <errno.h>
23+
#include <winsock2.h>
24+
#include <iphlpapi.h>
25+
26+
#include "iio-private.h"
27+
#include "mdns.h"
28+
#include "network.h"
29+
#include "debug.h"
30+
31+
static int new_discovery_data(struct dns_sd_discovery_data** data)
32+
{
33+
struct dns_sd_discovery_data* d;
34+
35+
d = zalloc(sizeof(struct dns_sd_discovery_data));
36+
if (!d)
37+
return -ENOMEM;
38+
39+
*data = d;
40+
return 0;
41+
}
42+
43+
void dnssd_free_discovery_data(struct dns_sd_discovery_data* d)
44+
{
45+
free(d->hostname);
46+
free(d);
47+
}
48+
49+
static int
50+
open_client_sockets(int* sockets, int max_sockets) {
51+
// When sending, each socket can only send to one network interface
52+
// Thus we need to open one socket for each interface and address family
53+
int num_sockets = 0;
54+
55+
IP_ADAPTER_ADDRESSES* adapter_address = 0;
56+
ULONG address_size = 8000;
57+
unsigned int ret;
58+
unsigned int num_retries = 4;
59+
do {
60+
adapter_address = malloc(address_size);
61+
if (adapter_address == NULL) {
62+
return -ENOMEM;
63+
}
64+
ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST, 0,
65+
adapter_address, &address_size);
66+
if (ret == ERROR_BUFFER_OVERFLOW) {
67+
free(adapter_address);
68+
adapter_address = 0;
69+
}
70+
else {
71+
break;
72+
}
73+
} while (num_retries-- > 0);
74+
75+
if (!adapter_address || (ret != NO_ERROR)) {
76+
free(adapter_address);
77+
IIO_ERROR("Failed to get network adapter addresses\n");
78+
return num_sockets;
79+
}
80+
81+
for (PIP_ADAPTER_ADDRESSES adapter = adapter_address; adapter; adapter = adapter->Next) {
82+
if (adapter->TunnelType == TUNNEL_TYPE_TEREDO)
83+
continue;
84+
if (adapter->OperStatus != IfOperStatusUp)
85+
continue;
86+
87+
for (IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; unicast;
88+
unicast = unicast->Next) {
89+
if (unicast->Address.lpSockaddr->sa_family == AF_INET) {
90+
struct sockaddr_in* saddr = (struct sockaddr_in*)unicast->Address.lpSockaddr;
91+
if ((saddr->sin_addr.S_un.S_un_b.s_b1 != 127) ||
92+
(saddr->sin_addr.S_un.S_un_b.s_b2 != 0) ||
93+
(saddr->sin_addr.S_un.S_un_b.s_b3 != 0) ||
94+
(saddr->sin_addr.S_un.S_un_b.s_b4 != 1)) {
95+
if (num_sockets < max_sockets) {
96+
int sock = mdns_socket_open_ipv4(saddr);
97+
if (sock >= 0) {
98+
sockets[num_sockets++] = sock;
99+
}
100+
}
101+
}
102+
}
103+
#ifdef HAVE_IPV6
104+
else if (unicast->Address.lpSockaddr->sa_family == AF_INET6) {
105+
struct sockaddr_in6* saddr = (struct sockaddr_in6*)unicast->Address.lpSockaddr;
106+
static const unsigned char localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0,
107+
0, 0, 0, 0, 0, 0, 0, 1 };
108+
static const unsigned char localhost_mapped[] = { 0, 0, 0, 0, 0, 0, 0, 0,
109+
0, 0, 0xff, 0xff, 0x7f, 0, 0, 1 };
110+
if ((unicast->DadState == NldsPreferred) &&
111+
memcmp(saddr->sin6_addr.s6_addr, localhost, 16) &&
112+
memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) {
113+
if (num_sockets < max_sockets) {
114+
int sock = mdns_socket_open_ipv6(saddr);
115+
if (sock >= 0) {
116+
sockets[num_sockets++] = sock;
117+
}
118+
}
119+
}
120+
}
121+
#endif
122+
}
123+
}
124+
125+
free(adapter_address);
126+
127+
for (int isock = 0; isock < num_sockets; ++isock) {
128+
unsigned long param = 1;
129+
ioctlsocket(sockets[isock], FIONBIO, &param);
130+
}
131+
132+
return num_sockets;
133+
}
134+
135+
136+
static int
137+
query_callback(int sock, const struct sockaddr* from, size_t addrlen,
138+
mdns_entry_type_t entry, uint16_t transaction_id,
139+
uint16_t rtype, uint16_t rclass, uint32_t ttl,
140+
const void* data, size_t size, size_t offset, size_t length,
141+
void* user_data) {
142+
143+
char addrbuffer[64];
144+
char servicebuffer[64];
145+
char namebuffer[256];
146+
147+
struct dns_sd_discovery_data* dd = (struct dns_sd_discovery_data*)user_data;
148+
if (dd == NULL) {
149+
IIO_ERROR("DNS SD: Missing info structure. Stop browsing.\n");
150+
goto quit;
151+
}
152+
153+
if (rtype != MDNS_RECORDTYPE_SRV)
154+
goto quit;
155+
156+
getnameinfo((const struct sockaddr*)from, (socklen_t)addrlen,
157+
addrbuffer, NI_MAXHOST, servicebuffer, NI_MAXSERV,
158+
NI_NUMERICSERV | NI_NUMERICHOST);
159+
160+
mdns_record_srv_t srv = mdns_record_parse_srv(data, size, offset, length,
161+
namebuffer, sizeof(namebuffer));
162+
IIO_DEBUG("%s : SRV %.*s priority %d weight %d port %d\n",
163+
addrbuffer,
164+
MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port);
165+
166+
// Go to the last element in the list
167+
while (dd->next != NULL)
168+
dd = dd->next;
169+
170+
if (srv.name.length > 1)
171+
{
172+
dd->hostname = malloc(srv.name.length);
173+
if (dd->hostname == NULL) {
174+
return -ENOMEM;
175+
}
176+
iio_strlcpy(dd->hostname, srv.name.str, srv.name.length);
177+
}
178+
iio_strlcpy(dd->addr_str, addrbuffer, DNS_SD_ADDRESS_STR_MAX);
179+
dd->port = srv.port;
180+
181+
IIO_DEBUG("DNS SD: added %s (%s:%d)\n", dd->hostname, dd->addr_str, dd->port);
182+
// A list entry was filled, prepare new item on the list.
183+
if (new_discovery_data(&dd->next)) {
184+
IIO_ERROR("DNS SD mDNS Resolver : memory failure\n");
185+
}
186+
187+
quit:
188+
return 0;
189+
}
190+
191+
int dnssd_find_hosts(struct dns_sd_discovery_data** ddata)
192+
{
193+
194+
WORD versionWanted = MAKEWORD(1, 1);
195+
WSADATA wsaData;
196+
if (WSAStartup(versionWanted, &wsaData)) {
197+
printf("Failed to initialize WinSock\n");
198+
return -1;
199+
}
200+
201+
struct dns_sd_discovery_data* d;
202+
203+
IIO_DEBUG("DNS SD: Start service discovery.\n");
204+
205+
if (new_discovery_data(&d) < 0) {
206+
return -ENOMEM;
207+
}
208+
*ddata = d;
209+
210+
size_t capacity = 2048;
211+
void* buffer = malloc(capacity);
212+
if (buffer == NULL) {
213+
return -ENOMEM;
214+
}
215+
const char service[] = "_iio._tcp.local";
216+
217+
IIO_DEBUG("Sending DNS-SD discovery\n");
218+
219+
int sockets[32];
220+
int transaction_id[32];
221+
int num_sockets = open_client_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]));
222+
if (num_sockets <= 0) {
223+
IIO_ERROR("Failed to open any client sockets\n");
224+
return -1;
225+
}
226+
IIO_DEBUG("Opened %d socket%s for mDNS query\n", num_sockets, num_sockets ? "s" : "");
227+
228+
IIO_DEBUG("Sending mDNS query: %s\n", service);
229+
for (int isock = 0; isock < num_sockets; ++isock) {
230+
transaction_id[isock] = mdns_query_send(sockets[isock], MDNS_RECORDTYPE_PTR, service, sizeof(service)-1, buffer,
231+
capacity);
232+
if (transaction_id[isock] <= 0)
233+
{
234+
IIO_ERROR("Failed to send mDNS query: errno %d\n", errno);
235+
}
236+
}
237+
238+
// This is a simple implementation that loops for 10 seconds or as long as we get replies
239+
// A real world implementation would probably use select, poll or similar syscall to wait
240+
// until data is available on a socket and then read it
241+
IIO_DEBUG("Reading mDNS query replies\n");
242+
for (int i = 0; i < 10; ++i) {
243+
size_t records;
244+
do {
245+
records = 0;
246+
for (int isock = 0; isock < num_sockets; ++isock) {
247+
if (transaction_id[isock] > 0)
248+
records +=
249+
mdns_query_recv(sockets[isock], buffer, capacity, query_callback, d, transaction_id[isock]);
250+
}
251+
} while (records);
252+
if (records)
253+
i = 0;
254+
Sleep(100);
255+
}
256+
257+
free(buffer);
258+
for (int isock = 0; isock < num_sockets; ++isock)
259+
mdns_socket_close(sockets[isock]);
260+
IIO_DEBUG("Closed socket%s\n", num_sockets ? "s" : "");
261+
262+
WSACleanup();
263+
264+
return 0;
265+
}

0 commit comments

Comments
 (0)