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