Return to minissdpc.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / miniupnpc / src |
1.1 ! misho 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) */ ! 27: typedef 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 ! 39: static 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 ! 51: struct 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 */ ! 85: struct 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: ! 106: struct UPNPDev * ! 107: getDevicesFromMiniSSDPD(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: ! 171: int ! 172: connectToMiniSSDPD(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: ! 218: int ! 219: disconnectFromMiniSSDPD(int s) ! 220: { ! 221: if (close(s) < 0) ! 222: return MINISSDPC_SOCKET_ERROR; ! 223: return MINISSDPC_SUCCESS; ! 224: } ! 225: ! 226: int ! 227: requestDevicesFromMiniSSDPD(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: ! 264: struct UPNPDev * ! 265: receiveDevicesFromMiniSSDPD(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: ! 373: free_url_and_st_and_return: ! 374: free(st); ! 375: free_url_and_return: ! 376: free(url); ! 377: return devlist; ! 378: ! 379: free_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 */ ! 392: static void ! 393: parseMSEARCHReply(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: ! 460: static 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. */ ! 517: struct UPNPDev * ! 518: ssdpDiscoverDevices(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(¤t) >= 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: } ! 1016: error: ! 1017: closesocket(sudp); ! 1018: return devlist; ! 1019: }