File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpc / src / minissdpc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:21:37 2023 UTC (9 months ago) by misho
Branches: miniupnpc, MAIN
CVS tags: v2_2_5p0, HEAD
Version 2.2.5p0

    1: /* $Id: minissdpc.c,v 1.1.1.1 2023/09/27 11:21:37 misho 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(&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: 	}
 1016: error:
 1017: 	closesocket(sudp);
 1018: 	return devlist;
 1019: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>