Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/miniupnpc/src/minissdpc.c
9904 views
1
/* $Id: minissdpc.c,v 1.54 2025/03/29 17:59:01 nanard Exp $ */
2
/* vim: tabstop=4 shiftwidth=4 noexpandtab
3
* Project : miniupnp
4
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
5
* Author : Thomas BERNARD
6
* copyright (c) 2005-2025 Thomas Bernard
7
* This software is subjet to the conditions detailed in the
8
* provided LICENCE file. */
9
#include <stdio.h>
10
#include <string.h>
11
#include <stdlib.h>
12
#include <time.h>
13
#include <sys/types.h>
14
#if defined (__NetBSD__)
15
#include <net/if.h>
16
#endif
17
#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
18
#ifdef _WIN32
19
#define WIN32_LEAN_AND_MEAN
20
#include <winsock2.h>
21
#include <ws2tcpip.h>
22
#include <io.h>
23
#include <iphlpapi.h>
24
#include "win32_snprintf.h"
25
#if !defined(_MSC_VER)
26
#include <stdint.h>
27
#else /* !defined(_MSC_VER) */
28
typedef unsigned short uint16_t;
29
#endif /* !defined(_MSC_VER) */
30
#ifndef strncasecmp
31
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
32
#define strncasecmp _memicmp
33
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
34
#define strncasecmp memicmp
35
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
36
#endif /* #ifndef strncasecmp */
37
#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION)
38
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
39
#define in6addr_any in6addr_any_init
40
static const IN6_ADDR in6addr_any_init = {0};
41
#endif
42
#endif
43
#if !defined(_WIN32_WINNT_VISTA)
44
#define _WIN32_WINNT_VISTA 0x0600
45
#endif
46
#endif /* _WIN32 */
47
#if defined(__amigaos__) || defined(__amigaos4__)
48
#include <sys/socket.h>
49
#endif /* defined(__amigaos__) || defined(__amigaos4__) */
50
#if defined(__amigaos__)
51
#define uint16_t unsigned short
52
#endif /* defined(__amigaos__) */
53
/* Hack */
54
#define UNIX_PATH_LEN 108
55
struct sockaddr_un {
56
uint16_t sun_family;
57
char sun_path[UNIX_PATH_LEN];
58
};
59
#else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */
60
#include <strings.h>
61
#include <unistd.h>
62
#include <sys/socket.h>
63
#include <sys/param.h>
64
#include <sys/time.h>
65
#include <sys/un.h>
66
#include <netinet/in.h>
67
#include <arpa/inet.h>
68
#include <netdb.h>
69
#include <net/if.h>
70
#define closesocket close
71
#endif
72
73
#include "miniupnpc_socketdef.h"
74
75
#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) && !defined(__HAIKU__)
76
#define HAS_IP_MREQN
77
#endif
78
79
#ifndef _WIN32
80
#include <sys/ioctl.h>
81
#if defined(__sun) || defined(__HAIKU__)
82
#include <sys/sockio.h>
83
#endif
84
#endif
85
86
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
87
/* Several versions of glibc don't define this structure,
88
* define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
89
struct ip_mreqn
90
{
91
struct in_addr imr_multiaddr; /* IP multicast address of group */
92
struct in_addr imr_address; /* local IP address of interface */
93
int imr_ifindex; /* Interface index */
94
};
95
#endif
96
97
#if defined(__amigaos__) || defined(__amigaos4__)
98
/* Amiga OS specific stuff */
99
#define TIMEVAL struct timeval
100
#endif
101
102
#include "minissdpc.h"
103
#include "miniupnpc.h"
104
#include "receivedata.h"
105
106
#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
107
108
#include "codelength.h"
109
110
struct UPNPDev *
111
getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error)
112
{
113
struct UPNPDev * devlist = NULL;
114
int s;
115
int res;
116
117
s = connectToMiniSSDPD(socketpath);
118
if (s < 0) {
119
if (error)
120
*error = s;
121
return NULL;
122
}
123
res = requestDevicesFromMiniSSDPD(s, devtype);
124
if (res < 0) {
125
if (error)
126
*error = res;
127
} else {
128
devlist = receiveDevicesFromMiniSSDPD(s, error);
129
}
130
disconnectFromMiniSSDPD(s);
131
return devlist;
132
}
133
134
/* macros used to read from unix socket */
135
#define READ_BYTE_BUFFER(c) \
136
if((int)bufferindex >= n) { \
137
n = read(s, buffer, sizeof(buffer)); \
138
if(n<=0) break; \
139
bufferindex = 0; \
140
} \
141
c = buffer[bufferindex++];
142
143
#ifndef MIN
144
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
145
#endif /* MIN */
146
147
#define READ_COPY_BUFFER(dst, len) \
148
for(l = len, p = (unsigned char *)dst; l > 0; ) { \
149
unsigned int lcopy; \
150
if((int)bufferindex >= n) { \
151
n = read(s, buffer, sizeof(buffer)); \
152
if(n<=0) break; \
153
bufferindex = 0; \
154
} \
155
lcopy = MIN(l, (n - bufferindex)); \
156
memcpy(p, buffer + bufferindex, lcopy); \
157
l -= lcopy; \
158
p += lcopy; \
159
bufferindex += lcopy; \
160
}
161
162
#define READ_DISCARD_BUFFER(len) \
163
for(l = len; l > 0; ) { \
164
unsigned int lcopy; \
165
if(bufferindex >= n) { \
166
n = read(s, buffer, sizeof(buffer)); \
167
if(n<=0) break; \
168
bufferindex = 0; \
169
} \
170
lcopy = MIN(l, (n - bufferindex)); \
171
l -= lcopy; \
172
bufferindex += lcopy; \
173
}
174
175
int
176
connectToMiniSSDPD(const char * socketpath)
177
{
178
int s;
179
struct sockaddr_un addr;
180
#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
181
struct timeval timeout;
182
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
183
184
s = socket(AF_UNIX, SOCK_STREAM, 0);
185
if(s < 0)
186
{
187
/*syslog(LOG_ERR, "socket(unix): %m");*/
188
perror("socket(unix)");
189
return MINISSDPC_SOCKET_ERROR;
190
}
191
#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
192
/* setting a 3 seconds timeout */
193
/* not supported for AF_UNIX sockets under Solaris */
194
timeout.tv_sec = 3;
195
timeout.tv_usec = 0;
196
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
197
{
198
perror("setsockopt SO_RCVTIMEO unix");
199
}
200
timeout.tv_sec = 3;
201
timeout.tv_usec = 0;
202
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
203
{
204
perror("setsockopt SO_SNDTIMEO unix");
205
}
206
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
207
if(!socketpath)
208
socketpath = "/var/run/minissdpd.sock";
209
memset(&addr, 0, sizeof(addr));
210
addr.sun_family = AF_UNIX;
211
strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
212
/* TODO : check if we need to handle the EINTR */
213
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
214
{
215
/*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
216
close(s);
217
return MINISSDPC_SOCKET_ERROR;
218
}
219
return s;
220
}
221
222
int
223
disconnectFromMiniSSDPD(int s)
224
{
225
if (close(s) < 0)
226
return MINISSDPC_SOCKET_ERROR;
227
return MINISSDPC_SUCCESS;
228
}
229
230
int
231
requestDevicesFromMiniSSDPD(int s, const char * devtype)
232
{
233
unsigned char buffer[256];
234
unsigned char * p;
235
unsigned int stsize, l;
236
237
stsize = strlen(devtype);
238
if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8))
239
{
240
buffer[0] = 3; /* request type 3 : everything */
241
}
242
else
243
{
244
buffer[0] = 1; /* request type 1 : request devices/services by type */
245
}
246
p = buffer + 1;
247
l = stsize; CODELENGTH(l, p);
248
if(p + stsize > buffer + sizeof(buffer))
249
{
250
/* devtype is too long ! */
251
#ifdef DEBUG
252
fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n",
253
stsize, (unsigned)sizeof(buffer));
254
#endif /* DEBUG */
255
return MINISSDPC_INVALID_INPUT;
256
}
257
memcpy(p, devtype, stsize);
258
p += stsize;
259
if(write(s, buffer, p - buffer) < 0)
260
{
261
/*syslog(LOG_ERR, "write(): %m");*/
262
perror("minissdpc.c: write()");
263
return MINISSDPC_SOCKET_ERROR;
264
}
265
return MINISSDPC_SUCCESS;
266
}
267
268
struct UPNPDev *
269
receiveDevicesFromMiniSSDPD(int s, int * error)
270
{
271
struct UPNPDev * tmp;
272
struct UPNPDev * devlist = NULL;
273
unsigned char buffer[256];
274
ssize_t n;
275
unsigned char * p;
276
unsigned char * url;
277
unsigned char * st;
278
unsigned int bufferindex;
279
unsigned int i, ndev;
280
unsigned int urlsize, stsize, usnsize, l;
281
282
n = read(s, buffer, sizeof(buffer));
283
if(n<=0)
284
{
285
perror("minissdpc.c: read()");
286
if (error)
287
*error = MINISSDPC_SOCKET_ERROR;
288
return NULL;
289
}
290
ndev = buffer[0];
291
bufferindex = 1;
292
for(i = 0; i < ndev; i++)
293
{
294
DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);
295
if(n<=0) {
296
if (error)
297
*error = MINISSDPC_INVALID_SERVER_REPLY;
298
return devlist;
299
}
300
#ifdef DEBUG
301
printf(" urlsize=%u", urlsize);
302
#endif /* DEBUG */
303
url = malloc(urlsize);
304
if(url == NULL) {
305
if (error)
306
*error = MINISSDPC_MEMORY_ERROR;
307
return devlist;
308
}
309
READ_COPY_BUFFER(url, urlsize);
310
if(n<=0) {
311
if (error)
312
*error = MINISSDPC_INVALID_SERVER_REPLY;
313
goto free_url_and_return;
314
}
315
DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);
316
if(n<=0) {
317
if (error)
318
*error = MINISSDPC_INVALID_SERVER_REPLY;
319
goto free_url_and_return;
320
}
321
#ifdef DEBUG
322
printf(" stsize=%u", stsize);
323
#endif /* DEBUG */
324
st = malloc(stsize);
325
if (st == NULL) {
326
if (error)
327
*error = MINISSDPC_MEMORY_ERROR;
328
goto free_url_and_return;
329
}
330
READ_COPY_BUFFER(st, stsize);
331
if(n<=0) {
332
if (error)
333
*error = MINISSDPC_INVALID_SERVER_REPLY;
334
goto free_url_and_st_and_return;
335
}
336
DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);
337
if(n<=0) {
338
if (error)
339
*error = MINISSDPC_INVALID_SERVER_REPLY;
340
goto free_url_and_st_and_return;
341
}
342
#ifdef DEBUG
343
printf(" usnsize=%u\n", usnsize);
344
#endif /* DEBUG */
345
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);
346
if(tmp == NULL) {
347
if (error)
348
*error = MINISSDPC_MEMORY_ERROR;
349
goto free_url_and_st_and_return;
350
}
351
tmp->pNext = devlist;
352
tmp->descURL = tmp->buffer;
353
tmp->st = tmp->buffer + 1 + urlsize;
354
memcpy(tmp->buffer, url, urlsize);
355
tmp->buffer[urlsize] = '\0';
356
memcpy(tmp->st, st, stsize);
357
tmp->buffer[urlsize+1+stsize] = '\0';
358
free(url);
359
free(st);
360
url = NULL;
361
st = NULL;
362
tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize;
363
READ_COPY_BUFFER(tmp->usn, usnsize);
364
if(n<=0) {
365
if (error)
366
*error = MINISSDPC_INVALID_SERVER_REPLY;
367
goto free_tmp_and_return;
368
}
369
tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
370
tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */
371
devlist = tmp;
372
}
373
if (error)
374
*error = MINISSDPC_SUCCESS;
375
return devlist;
376
377
free_url_and_st_and_return:
378
free(st);
379
free_url_and_return:
380
free(url);
381
return devlist;
382
383
free_tmp_and_return:
384
free(tmp);
385
return devlist;
386
}
387
388
#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
389
390
/* parseMSEARCHReply()
391
* the last 4 arguments are filled during the parsing :
392
* - location/locationsize : "location:" field of the SSDP reply packet
393
* - st/stsize : "st:" field of the SSDP reply packet.
394
* - usn/usnsize : "usn:" field of the SSDP reply packet
395
* The strings are NOT null terminated */
396
static void
397
parseMSEARCHReply(const char * reply, int size,
398
const char * * location, int * locationsize,
399
const char * * st, int * stsize,
400
const char * * usn, int * usnsize)
401
{
402
int a, b, i;
403
i = 0;
404
a = i; /* start of the line */
405
b = 0; /* end of the "header" (position of the colon) */
406
while(i<size)
407
{
408
switch(reply[i])
409
{
410
case ':':
411
if(b==0)
412
{
413
b = i; /* end of the "header" */
414
/*for(j=a; j<b; j++)
415
{
416
putchar(reply[j]);
417
}
418
*/
419
}
420
break;
421
case '\x0a':
422
case '\x0d':
423
if(b!=0)
424
{
425
/*for(j=b+1; j<i; j++)
426
{
427
putchar(reply[j]);
428
}
429
putchar('\n');*/
430
/* skip the colon and white spaces */
431
do { b++; } while(reply[b]==' ');
432
if(0==strncasecmp(reply+a, "location:", 9))
433
{
434
*location = reply+b;
435
*locationsize = i-b;
436
}
437
else if(0==strncasecmp(reply+a, "st:", 3))
438
{
439
*st = reply+b;
440
*stsize = i-b;
441
}
442
else if(0==strncasecmp(reply+a, "usn:", 4))
443
{
444
*usn = reply+b;
445
*usnsize = i-b;
446
}
447
b = 0;
448
}
449
a = i+1;
450
break;
451
default:
452
break;
453
}
454
i++;
455
}
456
}
457
458
#if defined(CLOCK_MONOTONIC_FAST)
459
#define UPNP_CLOCKID CLOCK_MONOTONIC_FAST
460
#elif defined(CLOCK_MONOTONIC)
461
#define UPNP_CLOCKID CLOCK_MONOTONIC
462
#endif
463
464
static int upnp_gettimeofday(struct timeval * tv)
465
{
466
#if defined(_WIN32)
467
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
468
ULONGLONG ts = GetTickCount64();
469
#else
470
DWORD ts = GetTickCount();
471
#endif
472
tv->tv_sec = (long)(ts / 1000);
473
tv->tv_usec = (ts % 1000) * 1000;
474
return 0; /* success */
475
#elif defined(CLOCK_MONOTONIC_FAST) || defined(CLOCK_MONOTONIC)
476
#if defined(__APPLE__)
477
#if defined(__clang__)
478
if (__builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
479
#else /* !defined(__clang__) */
480
if (clock_gettime != NULL) {
481
#endif /* defined(__clang__) */
482
#endif /* defined(__APPLE__) */
483
struct timespec ts;
484
int ret_code = clock_gettime(UPNP_CLOCKID, &ts);
485
if (ret_code == 0)
486
{
487
tv->tv_sec = ts.tv_sec;
488
tv->tv_usec = ts.tv_nsec / 1000;
489
}
490
return ret_code;
491
#if defined(__APPLE__)
492
}
493
else
494
{
495
/* fall-back for earlier Apple platforms */
496
return gettimeofday(tv, NULL);
497
}
498
#endif /* defined(__APPLE__) */
499
#else
500
return gettimeofday(tv, NULL);
501
#endif
502
}
503
/* port upnp discover : SSDP protocol */
504
#define SSDP_PORT 1900
505
#define XSTR(s) STR(s)
506
#define STR(s) #s
507
#define UPNP_MCAST_ADDR "239.255.255.250"
508
/* for IPv6 */
509
#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
510
#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
511
512
/* direct discovery if minissdpd responses are not sufficient */
513
/* ssdpDiscoverDevices() :
514
* return a chained list of all devices found or NULL if
515
* no devices was found.
516
* It is up to the caller to free the chained list
517
* delay is in millisecond (poll).
518
* UDA v1.1 says :
519
* The TTL for the IP packet SHOULD default to 2 and
520
* SHOULD be configurable. */
521
struct UPNPDev *
522
ssdpDiscoverDevices(const char * const deviceTypes[],
523
int delay, const char * multicastif,
524
int localport,
525
int ipv6, unsigned char ttl,
526
int * error,
527
int searchalltypes)
528
{
529
struct UPNPDev * tmp;
530
struct UPNPDev * devlist = NULL;
531
unsigned int scope_id = 0;
532
int opt = 1;
533
static const char MSearchMsgFmt[] =
534
"M-SEARCH * HTTP/1.1\r\n"
535
"HOST: %s:" XSTR(SSDP_PORT) "\r\n"
536
"ST: %s\r\n"
537
"MAN: \"ssdp:discover\"\r\n"
538
"MX: %u\r\n"
539
"\r\n";
540
int deviceIndex;
541
char bufr[1536]; /* reception and emission buffer */
542
SOCKET sudp;
543
int n;
544
struct sockaddr_storage sockudp_r;
545
unsigned int mx;
546
int rv;
547
#ifdef NO_GETADDRINFO
548
struct sockaddr_storage sockudp_w;
549
#else
550
struct addrinfo hints, *servinfo;
551
#endif
552
#ifdef _WIN32
553
unsigned long _ttl = (unsigned long)ttl;
554
#endif
555
int linklocal = 0; /* try first with site-local multicast */
556
int sentok;
557
558
if(error)
559
*error = MINISSDPC_UNKNOWN_ERROR;
560
561
if(localport==UPNP_LOCAL_PORT_SAME)
562
localport = SSDP_PORT;
563
564
#ifdef _WIN32
565
sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
566
#else
567
sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
568
#endif
569
if(ISINVALID(sudp))
570
{
571
if(error)
572
*error = MINISSDPC_SOCKET_ERROR;
573
PRINT_SOCKET_ERROR("socket");
574
return NULL;
575
}
576
/* reception */
577
memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
578
if(ipv6) {
579
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
580
p->sin6_family = AF_INET6;
581
if(localport > 0 && localport < 65536)
582
p->sin6_port = htons((unsigned short)localport);
583
p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
584
} else {
585
struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
586
p->sin_family = AF_INET;
587
if(localport > 0 && localport < 65536)
588
p->sin_port = htons((unsigned short)localport);
589
p->sin_addr.s_addr = INADDR_ANY;
590
}
591
#ifdef _WIN32
592
/* This code could help us to use the right Network interface for
593
* SSDP multicast traffic */
594
/* Get IP associated with the index given in the ip_forward struct
595
* in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
596
if(!ipv6) {
597
DWORD ifbestidx;
598
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
599
// While we don't need IPv6 support, the IPv4 only function is not available in UWP apps.
600
SOCKADDR_IN destAddr;
601
memset(&destAddr, 0, sizeof(destAddr));
602
destAddr.sin_family = AF_INET;
603
destAddr.sin_addr.s_addr = inet_addr("223.255.255.255");
604
destAddr.sin_port = 0;
605
if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) {
606
#else
607
if (GetBestInterface(inet_addr("223.255.255.255"), &ifbestidx) == NO_ERROR) {
608
#endif
609
DWORD dwRetVal = NO_ERROR;
610
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
611
ULONG outBufLen = 15360;
612
int Iterations;
613
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
614
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
615
616
for (Iterations = 0; Iterations < 3; Iterations++) {
617
pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, outBufLen);
618
if (pAddresses == NULL) {
619
break;
620
}
621
622
dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen);
623
624
if (dwRetVal != ERROR_BUFFER_OVERFLOW) {
625
break;
626
}
627
HeapFree(GetProcessHeap(), 0, pAddresses);
628
pAddresses = NULL;
629
}
630
631
if (dwRetVal == NO_ERROR) {
632
pCurrAddresses = pAddresses;
633
while (pCurrAddresses) {
634
#ifdef DEBUG
635
int i;
636
PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
637
PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
638
639
printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex);
640
printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName);
641
pUnicast = pCurrAddresses->FirstUnicastAddress;
642
if (pUnicast != NULL) {
643
for (i = 0; pUnicast != NULL; i++) {
644
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pUnicast->Address.lpSockaddr)->sin_addr) );
645
pUnicast = pUnicast->Next;
646
}
647
printf("\tNumber of Unicast Addresses: %d\n", i);
648
}
649
pAnycast = pCurrAddresses->FirstAnycastAddress;
650
if (pAnycast) {
651
for (i = 0; pAnycast != NULL; i++) {
652
printf("\tAnycast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pAnycast->Address.lpSockaddr)->sin_addr) );
653
pAnycast = pAnycast->Next;
654
}
655
printf("\tNumber of Anycast Addresses: %d\n", i);
656
}
657
pMulticast = pCurrAddresses->FirstMulticastAddress;
658
if (pMulticast) {
659
for (i = 0; pMulticast != NULL; i++) {
660
printf("\tMulticast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pMulticast->Address.lpSockaddr)->sin_addr) );
661
pMulticast = pMulticast->Next;
662
}
663
}
664
printf("\n");
665
#endif
666
pUnicast = pCurrAddresses->FirstUnicastAddress;
667
if (pCurrAddresses->IfIndex == ifbestidx && pUnicast != NULL) {
668
SOCKADDR_IN *ipv4 = (SOCKADDR_IN *)(pUnicast->Address.lpSockaddr);
669
/* Set the address of this interface to be used */
670
struct in_addr mc_if;
671
memset(&mc_if, 0, sizeof(mc_if));
672
mc_if.s_addr = ipv4->sin_addr.s_addr;
673
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
674
PRINT_SOCKET_ERROR("setsockopt");
675
}
676
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = ipv4->sin_addr.s_addr;
677
#ifndef DEBUG
678
break;
679
#endif
680
}
681
pCurrAddresses = pCurrAddresses->Next;
682
}
683
}
684
if (pAddresses != NULL) {
685
HeapFree(GetProcessHeap(), 0, pAddresses);
686
pAddresses = NULL;
687
}
688
}
689
}
690
#endif /* _WIN32 */
691
692
#ifdef _WIN32
693
if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
694
#else
695
if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
696
#endif
697
{
698
if(error)
699
*error = MINISSDPC_SOCKET_ERROR;
700
PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");
701
goto error;
702
}
703
704
if(ipv6) {
705
#ifdef _WIN32
706
DWORD mcastHops = ttl;
707
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)
708
#else /* _WIN32 */
709
int mcastHops = ttl;
710
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
711
#endif /* _WIN32 */
712
{
713
PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
714
}
715
} else {
716
#ifdef _WIN32
717
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
718
#else /* _WIN32 */
719
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
720
#endif /* _WIN32 */
721
{
722
/* not a fatal error */
723
PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
724
}
725
}
726
727
if(multicastif && multicastif[0] != '\0')
728
{
729
if(ipv6) {
730
#if !defined(_WIN32)
731
/* according to MSDN, if_nametoindex() is supported since
732
* MS Windows Vista and MS Windows Server 2008.
733
* http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
734
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
735
if(ifindex == 0)
736
{
737
if(error)
738
*error = MINISSDPC_INVALID_INPUT;
739
fprintf(stderr, "Invalid multicast interface name %s\n", multicastif);
740
goto error;
741
}
742
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
743
{
744
PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
745
}
746
#else
747
#ifdef DEBUG
748
printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
749
#endif
750
#endif
751
} else {
752
struct in_addr mc_if;
753
#if defined(_WIN32)
754
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
755
InetPtonA(AF_INET, multicastif, &mc_if);
756
#else
757
mc_if.s_addr = inet_addr(multicastif); /* old Windows SDK do not support InetPtoA() */
758
#endif
759
#else
760
/* was : mc_if.s_addr = inet_addr(multicastif); */ /* ex: 192.168.x.x */
761
if (inet_pton(AF_INET, multicastif, &mc_if.s_addr) <= 0) {
762
mc_if.s_addr = INADDR_NONE;
763
}
764
#endif
765
if(mc_if.s_addr != INADDR_NONE)
766
{
767
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
768
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
769
{
770
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
771
}
772
} else {
773
/* was not an ip address, try with an interface name */
774
#ifndef _WIN32
775
#ifdef HAS_IP_MREQN
776
struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
777
#endif
778
struct ifreq ifr;
779
int ifrlen = sizeof(ifr);
780
strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
781
ifr.ifr_name[IFNAMSIZ-1] = '\0';
782
if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
783
{
784
PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
785
goto error;
786
}
787
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
788
#ifdef HAS_IP_MREQN
789
memset(&reqn, 0, sizeof(struct ip_mreqn));
790
reqn.imr_address.s_addr = mc_if.s_addr;
791
reqn.imr_ifindex = if_nametoindex(multicastif);
792
if(reqn.imr_ifindex == 0)
793
{
794
if(error)
795
*error = MINISSDPC_INVALID_INPUT;
796
fprintf(stderr, "Invalid multicast ip address / interface name %s\n", multicastif);
797
goto error;
798
}
799
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
800
{
801
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
802
}
803
#else
804
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
805
{
806
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
807
}
808
#endif
809
#else /* _WIN32 */
810
#ifdef DEBUG
811
printf("Setting of multicast interface not supported with interface name.\n");
812
#endif
813
#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */
814
}
815
}
816
}
817
818
/* Before sending the packed, we first "bind" in order to be able
819
* to receive the response */
820
if (bind(sudp, (const struct sockaddr *)&sockudp_r,
821
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
822
{
823
if(error)
824
*error = MINISSDPC_SOCKET_ERROR;
825
PRINT_SOCKET_ERROR("bind");
826
closesocket(sudp);
827
return NULL;
828
}
829
830
if(error)
831
*error = MINISSDPC_SUCCESS;
832
/* Calculating maximum response time in seconds */
833
mx = ((unsigned int)delay) / 1000u;
834
if(mx == 0) {
835
mx = 1;
836
delay = 1000;
837
}
838
/* receiving SSDP response packet */
839
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
840
sentok = 0;
841
/* sending the SSDP M-SEARCH packet */
842
n = snprintf(bufr, sizeof(bufr),
843
MSearchMsgFmt,
844
ipv6 ?
845
(linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
846
: UPNP_MCAST_ADDR,
847
deviceTypes[deviceIndex], mx);
848
if ((unsigned int)n >= sizeof(bufr)) {
849
if(error)
850
*error = MINISSDPC_MEMORY_ERROR;
851
goto error;
852
}
853
#ifdef DEBUG
854
/*printf("Sending %s", bufr);*/
855
printf("Sending M-SEARCH request to %s with ST: %s\n",
856
ipv6 ?
857
(linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
858
: UPNP_MCAST_ADDR,
859
deviceTypes[deviceIndex]);
860
#endif
861
#ifdef NO_GETADDRINFO
862
/* the following code is not using getaddrinfo */
863
/* emission */
864
memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
865
if(ipv6) {
866
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
867
p->sin6_family = AF_INET6;
868
p->sin6_port = htons(SSDP_PORT);
869
inet_pton(AF_INET6,
870
linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
871
&(p->sin6_addr));
872
} else {
873
struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
874
p->sin_family = AF_INET;
875
p->sin_port = htons(SSDP_PORT);
876
p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
877
}
878
rv = sendto(sudp, bufr, n, 0, (struct sockaddr *)&sockudp_w,
879
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
880
if (rv < 0) {
881
if(error)
882
*error = MINISSDPC_SOCKET_ERROR;
883
PRINT_SOCKET_ERROR("sendto");
884
} else {
885
sentok = 1;
886
}
887
#else /* #ifdef NO_GETADDRINFO */
888
memset(&hints, 0, sizeof(hints));
889
hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
890
hints.ai_socktype = SOCK_DGRAM;
891
/*hints.ai_flags = */
892
if ((rv = getaddrinfo(ipv6
893
? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
894
: UPNP_MCAST_ADDR,
895
XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {
896
if(error)
897
*error = MINISSDPC_SOCKET_ERROR;
898
#ifdef _WIN32
899
fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
900
#else
901
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
902
#endif
903
break;
904
} else {
905
struct addrinfo *p;
906
/* as getaddrinfo() returns a linked list, we are iterating it
907
* even thought it should only return one result here */
908
for(p = servinfo; p; p = p->ai_next) {
909
rv = sendto(sudp, bufr, n, 0, p->ai_addr, MSC_CAST_INT p->ai_addrlen);
910
if (rv < 0) {
911
#ifdef DEBUG
912
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
913
if (getnameinfo(p->ai_addr, (socklen_t)p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
914
sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
915
fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
916
}
917
#endif
918
PRINT_SOCKET_ERROR("sendto");
919
continue;
920
} else {
921
sentok = 1;
922
}
923
}
924
freeaddrinfo(servinfo);
925
}
926
if(!sentok) {
927
if(error)
928
*error = MINISSDPC_SOCKET_ERROR;
929
}
930
#endif /* #ifdef NO_GETADDRINFO */
931
/* Waiting for SSDP REPLY packet to M-SEARCH
932
* if searchalltypes is set, enter the loop only
933
* when the last deviceType is reached */
934
if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) {
935
struct timeval start = {0, 0}, current = {0, 0};
936
upnp_gettimeofday(&start);
937
do {
938
n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
939
if (n < 0) {
940
/* error */
941
if(error)
942
*error = MINISSDPC_SOCKET_ERROR;
943
goto error;
944
} else if (n == 0) {
945
/* no data or Time Out */
946
#ifdef DEBUG
947
printf("NODATA or TIMEOUT\n");
948
#endif /* DEBUG */
949
if (devlist && !searchalltypes) {
950
/* found some devices, stop now*/
951
if(error)
952
*error = MINISSDPC_SUCCESS;
953
goto error;
954
}
955
} else {
956
const char * descURL=NULL;
957
int urlsize=0;
958
const char * st=NULL;
959
int stsize=0;
960
const char * usn=NULL;
961
int usnsize=0;
962
parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
963
if(st&&descURL) {
964
#ifdef DEBUG
965
printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",
966
stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
967
#endif /* DEBUG */
968
for(tmp=devlist; tmp; tmp = tmp->pNext) {
969
if(strncmp(tmp->descURL, descURL, urlsize) == 0 &&
970
tmp->descURL[urlsize] == '\0' &&
971
strncmp(tmp->st, st, stsize) == 0 &&
972
tmp->st[stsize] == '\0' &&
973
(usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) &&
974
tmp->usn[usnsize] == '\0')
975
break;
976
}
977
/* at the exit of the loop above, tmp is null if
978
* no duplicate device was found */
979
if(tmp)
980
continue;
981
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);
982
if(!tmp) {
983
/* memory allocation error */
984
if(error)
985
*error = MINISSDPC_MEMORY_ERROR;
986
goto error;
987
}
988
tmp->pNext = devlist;
989
tmp->descURL = tmp->buffer;
990
tmp->st = tmp->buffer + 1 + urlsize;
991
tmp->usn = tmp->st + 1 + stsize;
992
memcpy(tmp->buffer, descURL, urlsize);
993
tmp->buffer[urlsize] = '\0';
994
memcpy(tmp->st, st, stsize);
995
tmp->buffer[urlsize+1+stsize] = '\0';
996
if(usn != NULL)
997
memcpy(tmp->usn, usn, usnsize);
998
tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
999
tmp->scope_id = scope_id;
1000
devlist = tmp;
1001
}
1002
if (upnp_gettimeofday(&current) >= 0) {
1003
/* exit the loop if delay is reached */
1004
long interval = (current.tv_sec - start.tv_sec) * 1000;
1005
interval += (current.tv_usec - start.tv_usec) / 1000;
1006
if (interval > (long)delay)
1007
break;
1008
}
1009
}
1010
} while(n > 0);
1011
}
1012
if(ipv6) {
1013
/* switch linklocal flag */
1014
if(linklocal) {
1015
linklocal = 0;
1016
} else {
1017
/* try again with linklocal multicast */
1018
linklocal = 1;
1019
--deviceIndex;
1020
}
1021
}
1022
}
1023
error:
1024
closesocket(sudp);
1025
return devlist;
1026
}
1027
1028