Annotation of embedaddon/miniupnpc/src/minissdpc.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>