File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpc / miniupnpc.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 00:36:10 2013 UTC (10 years, 10 months ago) by misho
Branches: miniupnpc, elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    1: /* $Id: miniupnpc.c,v 1.1.1.2 2013/07/22 00:36:10 misho Exp $ */
    2: /* Project : miniupnp
    3:  * Web : http://miniupnp.free.fr/
    4:  * Author : Thomas BERNARD
    5:  * copyright (c) 2005-2012 Thomas Bernard
    6:  * This software is subjet to the conditions detailed in the
    7:  * provided LICENSE file. */
    8: #define __EXTENSIONS__ 1
    9: #if !defined(MACOSX) && !defined(__sun)
   10: #if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
   11: #ifndef __cplusplus
   12: #define _XOPEN_SOURCE 600
   13: #endif
   14: #endif
   15: #ifndef __BSD_VISIBLE
   16: #define __BSD_VISIBLE 1
   17: #endif
   18: #endif
   19: 
   20: #if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun)
   21: #define HAS_IP_MREQN
   22: #endif
   23: 
   24: #include <stdlib.h>
   25: #include <stdio.h>
   26: #include <string.h>
   27: #ifdef _WIN32
   28: /* Win32 Specific includes and defines */
   29: #include <winsock2.h>
   30: #include <ws2tcpip.h>
   31: #include <io.h>
   32: #include <iphlpapi.h>
   33: #define snprintf _snprintf
   34: #define strdup _strdup
   35: #ifndef strncasecmp
   36: #if defined(_MSC_VER) && (_MSC_VER >= 1400)
   37: #define strncasecmp _memicmp
   38: #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
   39: #define strncasecmp memicmp
   40: #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
   41: #endif /* #ifndef strncasecmp */
   42: #define MAXHOSTNAMELEN 64
   43: #else /* #ifdef _WIN32 */
   44: /* Standard POSIX includes */
   45: #include <unistd.h>
   46: #if defined(__amigaos__) && !defined(__amigaos4__)
   47: /* Amiga OS 3 specific stuff */
   48: #define socklen_t int
   49: #else
   50: #include <sys/select.h>
   51: #endif
   52: #include <sys/socket.h>
   53: #include <sys/types.h>
   54: #include <sys/param.h>
   55: #include <netinet/in.h>
   56: #include <arpa/inet.h>
   57: #include <netdb.h>
   58: #include <net/if.h>
   59: #if !defined(__amigaos__) && !defined(__amigaos4__)
   60: #include <poll.h>
   61: #endif
   62: #include <strings.h>
   63: #include <errno.h>
   64: #define closesocket close
   65: #endif /* #else _WIN32 */
   66: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
   67: #include <sys/time.h>
   68: #endif
   69: #if defined(__amigaos__) || defined(__amigaos4__)
   70: /* Amiga OS specific stuff */
   71: #define TIMEVAL struct timeval
   72: #endif
   73: 
   74: #include "miniupnpc.h"
   75: #include "minissdpc.h"
   76: #include "miniwget.h"
   77: #include "minisoap.h"
   78: #include "minixml.h"
   79: #include "upnpcommands.h"
   80: #include "connecthostport.h"
   81: #include "receivedata.h"
   82: 
   83: #ifdef _WIN32
   84: #define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
   85: #else
   86: #define PRINT_SOCKET_ERROR(x) perror(x)
   87: #endif
   88: 
   89: #define SOAPPREFIX "s"
   90: #define SERVICEPREFIX "u"
   91: #define SERVICEPREFIX2 'u'
   92: 
   93: /* root description parsing */
   94: LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
   95: {
   96: 	struct xmlparser parser;
   97: 	/* xmlparser object */
   98: 	parser.xmlstart = buffer;
   99: 	parser.xmlsize = bufsize;
  100: 	parser.data = data;
  101: 	parser.starteltfunc = IGDstartelt;
  102: 	parser.endeltfunc = IGDendelt;
  103: 	parser.datafunc = IGDdata;
  104: 	parser.attfunc = 0;
  105: 	parsexml(&parser);
  106: #ifdef DEBUG
  107: 	printIGD(data);
  108: #endif
  109: }
  110: 
  111: /* simpleUPnPcommand2 :
  112:  * not so simple !
  113:  * return values :
  114:  *   pointer - OK
  115:  *   NULL - error */
  116: char * simpleUPnPcommand2(int s, const char * url, const char * service,
  117: 		       const char * action, struct UPNParg * args,
  118: 		       int * bufsize, const char * httpversion)
  119: {
  120: 	char hostname[MAXHOSTNAMELEN+1];
  121: 	unsigned short port = 0;
  122: 	char * path;
  123: 	char soapact[128];
  124: 	char soapbody[2048];
  125: 	char * buf;
  126:     int n;
  127: 
  128: 	*bufsize = 0;
  129: 	snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
  130: 	if(args==NULL)
  131: 	{
  132: 		/*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
  133: 						"<?xml version=\"1.0\"?>\r\n"
  134: 	    	              "<" SOAPPREFIX ":Envelope "
  135: 						  "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  136: 						  SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  137: 						  "<" SOAPPREFIX ":Body>"
  138: 						  "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
  139: 						  "</" SERVICEPREFIX ":%s>"
  140: 						  "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
  141: 					 	  "\r\n", action, service, action);
  142: 	}
  143: 	else
  144: 	{
  145: 		char * p;
  146: 		const char * pe, * pv;
  147: 		int soapbodylen;
  148: 		soapbodylen = snprintf(soapbody, sizeof(soapbody),
  149: 						"<?xml version=\"1.0\"?>\r\n"
  150: 	    	            "<" SOAPPREFIX ":Envelope "
  151: 						"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  152: 						SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  153: 						"<" SOAPPREFIX ":Body>"
  154: 						"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
  155: 						action, service);
  156: 		p = soapbody + soapbodylen;
  157: 		while(args->elt)
  158: 		{
  159: 			/* check that we are never overflowing the string... */
  160: 			if(soapbody + sizeof(soapbody) <= p + 100)
  161: 			{
  162: 				/* we keep a margin of at least 100 bytes */
  163: 				return NULL;
  164: 			}
  165: 			*(p++) = '<';
  166: 			pe = args->elt;
  167: 			while(*pe)
  168: 				*(p++) = *(pe++);
  169: 			*(p++) = '>';
  170: 			if((pv = args->val))
  171: 			{
  172: 				while(*pv)
  173: 					*(p++) = *(pv++);
  174: 			}
  175: 			*(p++) = '<';
  176: 			*(p++) = '/';
  177: 			pe = args->elt;
  178: 			while(*pe)
  179: 				*(p++) = *(pe++);
  180: 			*(p++) = '>';
  181: 			args++;
  182: 		}
  183: 		*(p++) = '<';
  184: 		*(p++) = '/';
  185: 		*(p++) = SERVICEPREFIX2;
  186: 		*(p++) = ':';
  187: 		pe = action;
  188: 		while(*pe)
  189: 			*(p++) = *(pe++);
  190: 		strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
  191: 		        soapbody + sizeof(soapbody) - p);
  192: 	}
  193: 	if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
  194: 	if(s < 0) {
  195: 		s = connecthostport(hostname, port, 0);
  196: 		if(s < 0) {
  197: 			/* failed to connect */
  198: 			return NULL;
  199: 		}
  200: 	}
  201: 
  202: 	n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
  203: 	if(n<=0) {
  204: #ifdef DEBUG
  205: 		printf("Error sending SOAP request\n");
  206: #endif
  207: 		closesocket(s);
  208: 		return NULL;
  209: 	}
  210: 
  211: 	buf = getHTTPResponse(s, bufsize);
  212: #ifdef DEBUG
  213: 	if(*bufsize > 0 && buf)
  214: 	{
  215: 		printf("SOAP Response :\n%.*s\n", *bufsize, buf);
  216: 	}
  217: #endif
  218: 	closesocket(s);
  219: 	return buf;
  220: }
  221: 
  222: /* simpleUPnPcommand :
  223:  * not so simple !
  224:  * return values :
  225:  *   pointer - OK
  226:  *   NULL    - error */
  227: char * simpleUPnPcommand(int s, const char * url, const char * service,
  228: 		       const char * action, struct UPNParg * args,
  229: 		       int * bufsize)
  230: {
  231: 	char * buf;
  232: 
  233: #if 1
  234: 	buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
  235: #else
  236: 	buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
  237: 	if (!buf || *bufsize == 0)
  238: 	{
  239: #if DEBUG
  240: 	    printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
  241: #endif
  242: 		buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
  243: 	}
  244: #endif
  245: 	return buf;
  246: }
  247: 
  248: /* parseMSEARCHReply()
  249:  * the last 4 arguments are filled during the parsing :
  250:  *    - location/locationsize : "location:" field of the SSDP reply packet
  251:  *    - st/stsize : "st:" field of the SSDP reply packet.
  252:  * The strings are NOT null terminated */
  253: static void
  254: parseMSEARCHReply(const char * reply, int size,
  255:                   const char * * location, int * locationsize,
  256: 			      const char * * st, int * stsize)
  257: {
  258: 	int a, b, i;
  259: 	i = 0;
  260: 	a = i;	/* start of the line */
  261: 	b = 0;	/* end of the "header" (position of the colon) */
  262: 	while(i<size)
  263: 	{
  264: 		switch(reply[i])
  265: 		{
  266: 		case ':':
  267: 				if(b==0)
  268: 				{
  269: 					b = i; /* end of the "header" */
  270: 					/*for(j=a; j<b; j++)
  271: 					{
  272: 						putchar(reply[j]);
  273: 					}
  274: 					*/
  275: 				}
  276: 				break;
  277: 		case '\x0a':
  278: 		case '\x0d':
  279: 				if(b!=0)
  280: 				{
  281: 					/*for(j=b+1; j<i; j++)
  282: 					{
  283: 						putchar(reply[j]);
  284: 					}
  285: 					putchar('\n');*/
  286: 					/* skip the colon and white spaces */
  287: 					do { b++; } while(reply[b]==' ');
  288: 					if(0==strncasecmp(reply+a, "location", 8))
  289: 					{
  290: 						*location = reply+b;
  291: 						*locationsize = i-b;
  292: 					}
  293: 					else if(0==strncasecmp(reply+a, "st", 2))
  294: 					{
  295: 						*st = reply+b;
  296: 						*stsize = i-b;
  297: 					}
  298: 					b = 0;
  299: 				}
  300: 				a = i+1;
  301: 				break;
  302: 		default:
  303: 				break;
  304: 		}
  305: 		i++;
  306: 	}
  307: }
  308: 
  309: /* port upnp discover : SSDP protocol */
  310: #define PORT 1900
  311: #define XSTR(s) STR(s)
  312: #define STR(s) #s
  313: #define UPNP_MCAST_ADDR "239.255.255.250"
  314: /* for IPv6 */
  315: #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
  316: #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
  317: 
  318: /* upnpDiscover() :
  319:  * return a chained list of all devices found or NULL if
  320:  * no devices was found.
  321:  * It is up to the caller to free the chained list
  322:  * delay is in millisecond (poll) */
  323: LIBSPEC struct UPNPDev *
  324: upnpDiscover(int delay, const char * multicastif,
  325:              const char * minissdpdsock, int sameport,
  326:              int ipv6,
  327:              int * error)
  328: {
  329: 	struct UPNPDev * tmp;
  330: 	struct UPNPDev * devlist = 0;
  331: 	unsigned int scope_id = 0;
  332: 	int opt = 1;
  333: 	static const char MSearchMsgFmt[] =
  334: 	"M-SEARCH * HTTP/1.1\r\n"
  335: 	"HOST: %s:" XSTR(PORT) "\r\n"
  336: 	"ST: %s\r\n"
  337: 	"MAN: \"ssdp:discover\"\r\n"
  338: 	"MX: %u\r\n"
  339: 	"\r\n";
  340: 	static const char * const deviceList[] = {
  341: #if 0
  342: 		"urn:schemas-upnp-org:device:InternetGatewayDevice:2",
  343: 		"urn:schemas-upnp-org:service:WANIPConnection:2",
  344: #endif
  345: 		"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
  346: 		"urn:schemas-upnp-org:service:WANIPConnection:1",
  347: 		"urn:schemas-upnp-org:service:WANPPPConnection:1",
  348: 		"upnp:rootdevice",
  349: 		0
  350: 	};
  351: 	int deviceIndex = 0;
  352: 	char bufr[1536];	/* reception and emission buffer */
  353: 	int sudp;
  354: 	int n;
  355: 	struct sockaddr_storage sockudp_r;
  356: 	unsigned int mx;
  357: #ifdef NO_GETADDRINFO
  358: 	struct sockaddr_storage sockudp_w;
  359: #else
  360: 	int rv;
  361: 	struct addrinfo hints, *servinfo, *p;
  362: #endif
  363: #ifdef _WIN32
  364: 	MIB_IPFORWARDROW ip_forward;
  365: #endif
  366: 	int linklocal = 1;
  367: 
  368: 	if(error)
  369: 		*error = UPNPDISCOVER_UNKNOWN_ERROR;
  370: #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
  371: 	/* first try to get infos from minissdpd ! */
  372: 	if(!minissdpdsock)
  373: 		minissdpdsock = "/var/run/minissdpd.sock";
  374: 	while(!devlist && deviceList[deviceIndex]) {
  375: 		devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
  376: 		                                  minissdpdsock);
  377: 		/* We return what we have found if it was not only a rootdevice */
  378: 		if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) {
  379: 			if(error)
  380: 				*error = UPNPDISCOVER_SUCCESS;
  381: 			return devlist;
  382: 		}
  383: 		deviceIndex++;
  384: 	}
  385: 	deviceIndex = 0;
  386: #endif
  387: 	/* fallback to direct discovery */
  388: #ifdef _WIN32
  389: 	sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  390: #else
  391: 	sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
  392: #endif
  393: 	if(sudp < 0)
  394: 	{
  395: 		if(error)
  396: 			*error = UPNPDISCOVER_SOCKET_ERROR;
  397: 		PRINT_SOCKET_ERROR("socket");
  398: 		return NULL;
  399: 	}
  400: 	/* reception */
  401: 	memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
  402: 	if(ipv6) {
  403: 		struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
  404: 		p->sin6_family = AF_INET6;
  405: 		if(sameport)
  406: 			p->sin6_port = htons(PORT);
  407: 		p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
  408: 	} else {
  409: 		struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
  410: 		p->sin_family = AF_INET;
  411: 		if(sameport)
  412: 			p->sin_port = htons(PORT);
  413: 		p->sin_addr.s_addr = INADDR_ANY;
  414: 	}
  415: #ifdef _WIN32
  416: /* This code could help us to use the right Network interface for
  417:  * SSDP multicast traffic */
  418: /* Get IP associated with the index given in the ip_forward struct
  419:  * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
  420: 	if(!ipv6
  421: 	   && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
  422: 		DWORD dwRetVal = 0;
  423: 		PMIB_IPADDRTABLE pIPAddrTable;
  424: 		DWORD dwSize = 0;
  425: #ifdef DEBUG
  426: 		IN_ADDR IPAddr;
  427: #endif
  428: 		int i;
  429: #ifdef DEBUG
  430: 		printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
  431: #endif
  432: 		pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
  433: 		if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
  434: 			free(pIPAddrTable);
  435: 			pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
  436: 		}
  437: 		if(pIPAddrTable) {
  438: 			dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
  439: #ifdef DEBUG
  440: 			printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
  441: #endif
  442: 			for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
  443: #ifdef DEBUG
  444: 				printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
  445: 				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
  446: 				printf("\tIP Address[%d]:     \t%s\n", i, inet_ntoa(IPAddr) );
  447: 				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
  448: 				printf("\tSubnet Mask[%d]:    \t%s\n", i, inet_ntoa(IPAddr) );
  449: 				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
  450: 				printf("\tBroadCast[%d]:      \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
  451: 				printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
  452: 				printf("\tType and State[%d]:", i);
  453: 				printf("\n");
  454: #endif
  455: 				if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
  456: 					/* Set the address of this interface to be used */
  457: 					struct in_addr mc_if;
  458: 					memset(&mc_if, 0, sizeof(mc_if));
  459: 					mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
  460: 					if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
  461: 						PRINT_SOCKET_ERROR("setsockopt");
  462: 					}
  463: 					((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
  464: #ifndef DEBUG
  465: 					break;
  466: #endif
  467: 				}
  468: 			}
  469: 			free(pIPAddrTable);
  470: 			pIPAddrTable = NULL;
  471: 		}
  472: 	}
  473: #endif
  474: 
  475: #ifdef _WIN32
  476: 	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
  477: #else
  478: 	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
  479: #endif
  480: 	{
  481: 		if(error)
  482: 			*error = UPNPDISCOVER_SOCKET_ERROR;
  483: 		PRINT_SOCKET_ERROR("setsockopt");
  484: 		return NULL;
  485: 	}
  486: 
  487: 	if(multicastif)
  488: 	{
  489: 		if(ipv6) {
  490: #if !defined(_WIN32)
  491: 			/* according to MSDN, if_nametoindex() is supported since
  492: 			 * MS Windows Vista and MS Windows Server 2008.
  493: 			 * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
  494: 			unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
  495: 			if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
  496: 			{
  497: 				PRINT_SOCKET_ERROR("setsockopt");
  498: 			}
  499: #else
  500: #ifdef DEBUG
  501: 			printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
  502: #endif
  503: #endif
  504: 		} else {
  505: 			struct in_addr mc_if;
  506: 			mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
  507: 			if(mc_if.s_addr != INADDR_NONE)
  508: 			{
  509: 				((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
  510: 				if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
  511: 				{
  512: 					PRINT_SOCKET_ERROR("setsockopt");
  513: 				}
  514: 			} else {
  515: #ifdef HAS_IP_MREQN
  516: 				/* was not an ip address, try with an interface name */
  517: 				struct ip_mreqn reqn;	/* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
  518: 				memset(&reqn, 0, sizeof(struct ip_mreqn));
  519: 				reqn.imr_ifindex = if_nametoindex(multicastif);
  520: 				if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
  521: 				{
  522: 					PRINT_SOCKET_ERROR("setsockopt");
  523: 				}
  524: #else
  525: #ifdef DEBUG
  526: 				printf("Setting of multicast interface not supported with interface name.\n");
  527: #endif
  528: #endif
  529: 			}
  530: 		}
  531: 	}
  532: 
  533: 	/* Avant d'envoyer le paquet on bind pour recevoir la reponse */
  534:     if (bind(sudp, (const struct sockaddr *)&sockudp_r,
  535: 	         ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
  536: 	{
  537: 		if(error)
  538: 			*error = UPNPDISCOVER_SOCKET_ERROR;
  539:         PRINT_SOCKET_ERROR("bind");
  540: 		closesocket(sudp);
  541: 		return NULL;
  542:     }
  543: 
  544: 	if(error)
  545: 		*error = UPNPDISCOVER_SUCCESS;
  546: 	/* Calculating maximum response time in seconds */
  547: 	mx = ((unsigned int)delay) / 1000u;
  548: 	/* receiving SSDP response packet */
  549: 	for(n = 0; deviceList[deviceIndex]; deviceIndex++)
  550: 	{
  551: 	if(n == 0)
  552: 	{
  553: 		/* sending the SSDP M-SEARCH packet */
  554: 		n = snprintf(bufr, sizeof(bufr),
  555: 		             MSearchMsgFmt,
  556: 		             ipv6 ?
  557: 		             (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" :  "[" UPNP_MCAST_SL_ADDR "]")
  558: 		             : UPNP_MCAST_ADDR,
  559: 		             deviceList[deviceIndex], mx);
  560: #ifdef DEBUG
  561: 		printf("Sending %s", bufr);
  562: #endif
  563: #ifdef NO_GETADDRINFO
  564: 		/* the following code is not using getaddrinfo */
  565: 		/* emission */
  566: 		memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
  567: 		if(ipv6) {
  568: 			struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
  569: 			p->sin6_family = AF_INET6;
  570: 			p->sin6_port = htons(PORT);
  571: 			inet_pton(AF_INET6,
  572: 			          linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
  573: 			          &(p->sin6_addr));
  574: 		} else {
  575: 			struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
  576: 			p->sin_family = AF_INET;
  577: 			p->sin_port = htons(PORT);
  578: 			p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
  579: 		}
  580: 		n = sendto(sudp, bufr, n, 0,
  581: 		           &sockudp_w,
  582: 		           ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
  583: 		if (n < 0) {
  584: 			if(error)
  585: 				*error = UPNPDISCOVER_SOCKET_ERROR;
  586: 			PRINT_SOCKET_ERROR("sendto");
  587: 			break;
  588: 		}
  589: #else /* #ifdef NO_GETADDRINFO */
  590: 		memset(&hints, 0, sizeof(hints));
  591: 		hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
  592: 		hints.ai_socktype = SOCK_DGRAM;
  593: 		/*hints.ai_flags = */
  594: 		if ((rv = getaddrinfo(ipv6
  595: 		                      ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
  596: 		                      : UPNP_MCAST_ADDR,
  597: 		                      XSTR(PORT), &hints, &servinfo)) != 0) {
  598: 			if(error)
  599: 				*error = UPNPDISCOVER_SOCKET_ERROR;
  600: #ifdef _WIN32
  601: 		    fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
  602: #else
  603: 		    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
  604: #endif
  605: 			break;
  606: 		}
  607: 		for(p = servinfo; p; p = p->ai_next) {
  608: 			n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
  609: 			if (n < 0) {
  610: #ifdef DEBUG
  611: 				char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
  612: 				if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
  613: 						sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
  614: 					fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
  615: 				}
  616: #endif
  617: 				PRINT_SOCKET_ERROR("sendto");
  618: 				continue;
  619: 			}
  620: 		}
  621: 		freeaddrinfo(servinfo);
  622: 		if(n < 0) {
  623: 			if(error)
  624: 				*error = UPNPDISCOVER_SOCKET_ERROR;
  625: 			break;
  626: 		}
  627: #endif /* #ifdef NO_GETADDRINFO */
  628: 	}
  629: 	/* Waiting for SSDP REPLY packet to M-SEARCH */
  630: 	n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
  631: 	if (n < 0) {
  632: 		/* error */
  633: 		if(error)
  634: 			*error = UPNPDISCOVER_SOCKET_ERROR;
  635: 		break;
  636: 	} else if (n == 0) {
  637: 		/* no data or Time Out */
  638: 		if (devlist) {
  639: 			/* no more device type to look for... */
  640: 			if(error)
  641: 				*error = UPNPDISCOVER_SUCCESS;
  642: 			break;
  643: 		}
  644: 		if(ipv6) {
  645: 			if(linklocal) {
  646: 				linklocal = 0;
  647: 				--deviceIndex;
  648: 			} else {
  649: 				linklocal = 1;
  650: 			}
  651: 		}
  652: 	} else {
  653: 		const char * descURL=NULL;
  654: 		int urlsize=0;
  655: 		const char * st=NULL;
  656: 		int stsize=0;
  657:         /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
  658: 		parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
  659: 		if(st&&descURL)
  660: 		{
  661: #ifdef DEBUG
  662: 			printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
  663: 			       stsize, st, urlsize, descURL);
  664: #endif
  665: 			for(tmp=devlist; tmp; tmp = tmp->pNext) {
  666: 				if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
  667: 				   tmp->descURL[urlsize] == '\0' &&
  668: 				   memcmp(tmp->st, st, stsize) == 0 &&
  669: 				   tmp->st[stsize] == '\0')
  670: 					break;
  671: 			}
  672: 			/* at the exit of the loop above, tmp is null if
  673: 			 * no duplicate device was found */
  674: 			if(tmp)
  675: 				continue;
  676: 			tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
  677: 			if(!tmp) {
  678: 				/* memory allocation error */
  679: 				if(error)
  680: 					*error = UPNPDISCOVER_MEMORY_ERROR;
  681: 				break;
  682: 			}
  683: 			tmp->pNext = devlist;
  684: 			tmp->descURL = tmp->buffer;
  685: 			tmp->st = tmp->buffer + 1 + urlsize;
  686: 			memcpy(tmp->buffer, descURL, urlsize);
  687: 			tmp->buffer[urlsize] = '\0';
  688: 			memcpy(tmp->buffer + urlsize + 1, st, stsize);
  689: 			tmp->buffer[urlsize+1+stsize] = '\0';
  690: 			tmp->scope_id = scope_id;
  691: 			devlist = tmp;
  692: 		}
  693: 	}
  694: 	}
  695: 	closesocket(sudp);
  696: 	return devlist;
  697: }
  698: 
  699: /* freeUPNPDevlist() should be used to
  700:  * free the chained list returned by upnpDiscover() */
  701: LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
  702: {
  703: 	struct UPNPDev * next;
  704: 	while(devlist)
  705: 	{
  706: 		next = devlist->pNext;
  707: 		free(devlist);
  708: 		devlist = next;
  709: 	}
  710: }
  711: 
  712: static void
  713: url_cpy_or_cat(char * dst, const char * src, int n)
  714: {
  715: 	if(  (src[0] == 'h')
  716: 	   &&(src[1] == 't')
  717: 	   &&(src[2] == 't')
  718: 	   &&(src[3] == 'p')
  719: 	   &&(src[4] == ':')
  720: 	   &&(src[5] == '/')
  721: 	   &&(src[6] == '/'))
  722: 	{
  723: 		strncpy(dst, src, n);
  724: 	}
  725: 	else
  726: 	{
  727: 		int l = strlen(dst);
  728: 		if(src[0] != '/')
  729: 			dst[l++] = '/';
  730: 		if(l<=n)
  731: 			strncpy(dst + l, src, n - l);
  732: 	}
  733: }
  734: 
  735: /* Prepare the Urls for usage...
  736:  */
  737: LIBSPEC void
  738: GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
  739:             const char * descURL, unsigned int scope_id)
  740: {
  741: 	char * p;
  742: 	int n1, n2, n3, n4;
  743: #ifdef IF_NAMESIZE
  744: 	char ifname[IF_NAMESIZE];
  745: #else
  746: 	char scope_str[8];
  747: #endif
  748: 
  749: 	n1 = strlen(data->urlbase);
  750: 	if(n1==0)
  751: 		n1 = strlen(descURL);
  752: 	if(scope_id != 0) {
  753: #ifdef IF_NAMESIZE
  754: 		if(if_indextoname(scope_id, ifname)) {
  755: 			n1 += 3 + strlen(ifname);	/* 3 == strlen(%25) */
  756: 		}
  757: #else
  758: 	/* under windows, scope is numerical */
  759: 	snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
  760: #endif
  761: 	}
  762: 	n1 += 2;	/* 1 byte more for Null terminator, 1 byte for '/' if needed */
  763: 	n2 = n1; n3 = n1; n4 = n1;
  764: 	n1 += strlen(data->first.scpdurl);
  765: 	n2 += strlen(data->first.controlurl);
  766: 	n3 += strlen(data->CIF.controlurl);
  767: 	n4 += strlen(data->IPv6FC.controlurl);
  768: 
  769: 	/* allocate memory to store URLs */
  770: 	urls->ipcondescURL = (char *)malloc(n1);
  771: 	urls->controlURL = (char *)malloc(n2);
  772: 	urls->controlURL_CIF = (char *)malloc(n3);
  773: 	urls->controlURL_6FC = (char *)malloc(n4);
  774: 
  775: 	/* strdup descURL */
  776: 	urls->rootdescURL = strdup(descURL);
  777: 
  778: 	/* get description of WANIPConnection */
  779: 	if(data->urlbase[0] != '\0')
  780: 		strncpy(urls->ipcondescURL, data->urlbase, n1);
  781: 	else
  782: 		strncpy(urls->ipcondescURL, descURL, n1);
  783: 	p = strchr(urls->ipcondescURL+7, '/');
  784: 	if(p) p[0] = '\0';
  785: 	if(scope_id != 0) {
  786: 		if(0 == memcmp(urls->ipcondescURL, "http://[fe80:", 13)) {
  787: 			/* this is a linklocal IPv6 address */
  788: 			p = strchr(urls->ipcondescURL, ']');
  789: 			if(p) {
  790: 				/* insert %25<scope> into URL */
  791: #ifdef IF_NAMESIZE
  792: 				memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
  793: 				memcpy(p, "%25", 3);
  794: 				memcpy(p + 3, ifname, strlen(ifname));
  795: #else
  796: 				memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
  797: 				memcpy(p, "%25", 3);
  798: 				memcpy(p + 3, scope_str, strlen(scope_str));
  799: #endif
  800: 			}
  801: 		}
  802: 	}
  803: 	strncpy(urls->controlURL, urls->ipcondescURL, n2);
  804: 	strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
  805: 	strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
  806: 
  807: 	url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
  808: 
  809: 	url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
  810: 
  811: 	url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
  812: 
  813: 	url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
  814: 
  815: #ifdef DEBUG
  816: 	printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
  817: 	       (unsigned)strlen(urls->ipcondescURL), n1);
  818: 	printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
  819: 	       (unsigned)strlen(urls->controlURL), n2);
  820: 	printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
  821: 	       (unsigned)strlen(urls->controlURL_CIF), n3);
  822: 	printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
  823: 	       (unsigned)strlen(urls->controlURL_6FC), n4);
  824: #endif
  825: }
  826: 
  827: LIBSPEC void
  828: FreeUPNPUrls(struct UPNPUrls * urls)
  829: {
  830: 	if(!urls)
  831: 		return;
  832: 	free(urls->controlURL);
  833: 	urls->controlURL = 0;
  834: 	free(urls->ipcondescURL);
  835: 	urls->ipcondescURL = 0;
  836: 	free(urls->controlURL_CIF);
  837: 	urls->controlURL_CIF = 0;
  838: 	free(urls->controlURL_6FC);
  839: 	urls->controlURL_6FC = 0;
  840: 	free(urls->rootdescURL);
  841: 	urls->rootdescURL = 0;
  842: }
  843: 
  844: int
  845: UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
  846: {
  847: 	char status[64];
  848: 	unsigned int uptime;
  849: 	status[0] = '\0';
  850: 	UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
  851: 	                   status, &uptime, NULL);
  852: 	if(0 == strcmp("Connected", status))
  853: 	{
  854: 		return 1;
  855: 	}
  856: 	else
  857: 		return 0;
  858: }
  859: 
  860: 
  861: /* UPNP_GetValidIGD() :
  862:  * return values :
  863:  *    -1 = Internal error
  864:  *     0 = NO IGD found
  865:  *     1 = A valid connected IGD has been found
  866:  *     2 = A valid IGD has been found but it reported as
  867:  *         not connected
  868:  *     3 = an UPnP device has been found but was not recognized as an IGD
  869:  *
  870:  * In any non zero return case, the urls and data structures
  871:  * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
  872:  * free allocated memory.
  873:  */
  874: LIBSPEC int
  875: UPNP_GetValidIGD(struct UPNPDev * devlist,
  876:                  struct UPNPUrls * urls,
  877: 				 struct IGDdatas * data,
  878: 				 char * lanaddr, int lanaddrlen)
  879: {
  880: 	struct xml_desc {
  881: 		char * xml;
  882: 		int size;
  883: 	} * desc = NULL;
  884: 	struct UPNPDev * dev;
  885: 	int ndev = 0;
  886: 	int i;
  887: 	int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
  888: 	if(!devlist)
  889: 	{
  890: #ifdef DEBUG
  891: 		printf("Empty devlist\n");
  892: #endif
  893: 		return 0;
  894: 	}
  895: 	for(dev = devlist; dev; dev = dev->pNext)
  896: 		ndev++;
  897: 	if(ndev > 0)
  898: 	{
  899: 		desc = calloc(ndev, sizeof(struct xml_desc));
  900: 		if(!desc)
  901: 			return -1; /* memory allocation error */
  902: 	}
  903: 	for(state = 1; state <= 3; state++)
  904: 	{
  905: 		for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
  906: 		{
  907: 			/* we should choose an internet gateway device.
  908: 		 	* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
  909: 			if(state == 1)
  910: 			{
  911: 				desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
  912: 				   	                           lanaddr, lanaddrlen,
  913: 				                               dev->scope_id);
  914: #ifdef DEBUG
  915: 				if(!desc[i].xml)
  916: 				{
  917: 					printf("error getting XML description %s\n", dev->descURL);
  918: 				}
  919: #endif
  920: 			}
  921: 			if(desc[i].xml)
  922: 			{
  923: 				memset(data, 0, sizeof(struct IGDdatas));
  924: 				memset(urls, 0, sizeof(struct UPNPUrls));
  925: 				parserootdesc(desc[i].xml, desc[i].size, data);
  926: 				if(0==strcmp(data->CIF.servicetype,
  927: 				   "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
  928: 				   || state >= 3 )
  929: 				{
  930: 				  GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
  931: 
  932: #ifdef DEBUG
  933: 				  printf("UPNPIGD_IsConnected(%s) = %d\n",
  934: 				     urls->controlURL,
  935: 			         UPNPIGD_IsConnected(urls, data));
  936: #endif
  937: 				  if((state >= 2) || UPNPIGD_IsConnected(urls, data))
  938: 					goto free_and_return;
  939: 				  FreeUPNPUrls(urls);
  940: 				  if(data->second.servicetype[0] != '\0') {
  941: #ifdef DEBUG
  942: 				    printf("We tried %s, now we try %s !\n",
  943: 				           data->first.servicetype, data->second.servicetype);
  944: #endif
  945: 				    /* swaping WANPPPConnection and WANIPConnection ! */
  946: 				    memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
  947: 				    memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
  948: 				    memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
  949: 				    GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
  950: #ifdef DEBUG
  951: 				    printf("UPNPIGD_IsConnected(%s) = %d\n",
  952: 				       urls->controlURL,
  953: 			           UPNPIGD_IsConnected(urls, data));
  954: #endif
  955: 				    if((state >= 2) || UPNPIGD_IsConnected(urls, data))
  956: 					  goto free_and_return;
  957: 				    FreeUPNPUrls(urls);
  958: 				  }
  959: 				}
  960: 				memset(data, 0, sizeof(struct IGDdatas));
  961: 			}
  962: 		}
  963: 	}
  964: 	state = 0;
  965: free_and_return:
  966: 	if(desc) {
  967: 		for(i = 0; i < ndev; i++) {
  968: 			if(desc[i].xml) {
  969: 				free(desc[i].xml);
  970: 			}
  971: 		}
  972: 		free(desc);
  973: 	}
  974: 	return state;
  975: }
  976: 
  977: /* UPNP_GetIGDFromUrl()
  978:  * Used when skipping the discovery process.
  979:  * return value :
  980:  *   0 - Not ok
  981:  *   1 - OK */
  982: int
  983: UPNP_GetIGDFromUrl(const char * rootdescurl,
  984:                    struct UPNPUrls * urls,
  985:                    struct IGDdatas * data,
  986:                    char * lanaddr, int lanaddrlen)
  987: {
  988: 	char * descXML;
  989: 	int descXMLsize = 0;
  990: 	descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
  991: 	   	                       lanaddr, lanaddrlen, 0);
  992: 	if(descXML) {
  993: 		memset(data, 0, sizeof(struct IGDdatas));
  994: 		memset(urls, 0, sizeof(struct UPNPUrls));
  995: 		parserootdesc(descXML, descXMLsize, data);
  996: 		free(descXML);
  997: 		descXML = NULL;
  998: 		GetUPNPUrls(urls, data, rootdescurl, 0);
  999: 		return 1;
 1000: 	} else {
 1001: 		return 0;
 1002: 	}
 1003: }
 1004: 

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