File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpc / miniwget.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: miniwget.c,v 1.1.1.2 2013/07/22 00:36:10 misho Exp $ */
    2: /* Project : miniupnp
    3:  * Website : http://miniupnp.free.fr/
    4:  * Author : Thomas Bernard
    5:  * Copyright (c) 2005-2012 Thomas Bernard
    6:  * This software is subject to the conditions detailed in the
    7:  * LICENCE file provided in this distribution. */
    8: 
    9: #include <stdio.h>
   10: #include <stdlib.h>
   11: #include <string.h>
   12: #include <ctype.h>
   13: #ifdef _WIN32
   14: #include <winsock2.h>
   15: #include <ws2tcpip.h>
   16: #include <io.h>
   17: #define MAXHOSTNAMELEN 64
   18: #define MIN(x,y) (((x)<(y))?(x):(y))
   19: #define snprintf _snprintf
   20: #define socklen_t int
   21: #ifndef strncasecmp
   22: #if defined(_MSC_VER) && (_MSC_VER >= 1400)
   23: #define strncasecmp _memicmp
   24: #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
   25: #define strncasecmp memicmp
   26: #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
   27: #endif /* #ifndef strncasecmp */
   28: #else /* #ifdef _WIN32 */
   29: #include <unistd.h>
   30: #include <sys/param.h>
   31: #if defined(__amigaos__) && !defined(__amigaos4__)
   32: #define socklen_t int
   33: #else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
   34: #include <sys/select.h>
   35: #endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
   36: #include <sys/socket.h>
   37: #include <netinet/in.h>
   38: #include <arpa/inet.h>
   39: #include <net/if.h>
   40: #include <netdb.h>
   41: #define closesocket close
   42: /* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
   43:  * during the connect() call */
   44: #define MINIUPNPC_IGNORE_EINTR
   45: #endif /* #else _WIN32 */
   46: #if defined(__sun) || defined(sun)
   47: #define MIN(x,y) (((x)<(y))?(x):(y))
   48: #endif
   49: 
   50: #include "miniupnpcstrings.h"
   51: #include "miniwget.h"
   52: #include "connecthostport.h"
   53: #include "receivedata.h"
   54: 
   55: /*
   56:  * Read a HTTP response from a socket.
   57:  * Process Content-Length and Transfer-encoding headers.
   58:  * return a pointer to the content buffer, which length is saved
   59:  * to the length parameter.
   60:  */
   61: void *
   62: getHTTPResponse(int s, int * size)
   63: {
   64: 	char buf[2048];
   65: 	int n;
   66: 	int endofheaders = 0;
   67: 	int chunked = 0;
   68: 	int content_length = -1;
   69: 	unsigned int chunksize = 0;
   70: 	unsigned int bytestocopy = 0;
   71: 	/* buffers : */
   72: 	char * header_buf;
   73: 	unsigned int header_buf_len = 2048;
   74: 	unsigned int header_buf_used = 0;
   75: 	char * content_buf;
   76: 	unsigned int content_buf_len = 2048;
   77: 	unsigned int content_buf_used = 0;
   78: 	char chunksize_buf[32];
   79: 	unsigned int chunksize_buf_index;
   80: 
   81: 	header_buf = malloc(header_buf_len);
   82: 	content_buf = malloc(content_buf_len);
   83: 	chunksize_buf[0] = '\0';
   84: 	chunksize_buf_index = 0;
   85: 
   86: 	while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
   87: 	{
   88: 		if(endofheaders == 0)
   89: 		{
   90: 			int i;
   91: 			int linestart=0;
   92: 			int colon=0;
   93: 			int valuestart=0;
   94: 			if(header_buf_used + n > header_buf_len) {
   95: 				header_buf = realloc(header_buf, header_buf_used + n);
   96: 				header_buf_len = header_buf_used + n;
   97: 			}
   98: 			memcpy(header_buf + header_buf_used, buf, n);
   99: 			header_buf_used += n;
  100: 			/* search for CR LF CR LF (end of headers)
  101: 			 * recognize also LF LF */
  102: 			i = 0;
  103: 			while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
  104: 				if(header_buf[i] == '\r') {
  105: 					i++;
  106: 					if(header_buf[i] == '\n') {
  107: 						i++;
  108: 						if(i < (int)header_buf_used && header_buf[i] == '\r') {
  109: 							i++;
  110: 							if(i < (int)header_buf_used && header_buf[i] == '\n') {
  111: 								endofheaders = i+1;
  112: 							}
  113: 						}
  114: 					}
  115: 				} else if(header_buf[i] == '\n') {
  116: 					i++;
  117: 					if(header_buf[i] == '\n') {
  118: 						endofheaders = i+1;
  119: 					}
  120: 				}
  121: 				i++;
  122: 			}
  123: 			if(endofheaders == 0)
  124: 				continue;
  125: 			/* parse header lines */
  126: 			for(i = 0; i < endofheaders - 1; i++) {
  127: 				if(colon <= linestart && header_buf[i]==':')
  128: 				{
  129: 					colon = i;
  130: 					while(i < (endofheaders-1)
  131: 					      && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
  132: 						i++;
  133: 					valuestart = i + 1;
  134: 				}
  135: 				/* detecting end of line */
  136: 				else if(header_buf[i]=='\r' || header_buf[i]=='\n')
  137: 				{
  138: 					if(colon > linestart && valuestart > colon)
  139: 					{
  140: #ifdef DEBUG
  141: 						printf("header='%.*s', value='%.*s'\n",
  142: 						       colon-linestart, header_buf+linestart,
  143: 						       i-valuestart, header_buf+valuestart);
  144: #endif
  145: 						if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
  146: 						{
  147: 							content_length = atoi(header_buf+valuestart);
  148: #ifdef DEBUG
  149: 							printf("Content-Length: %d\n", content_length);
  150: #endif
  151: 						}
  152: 						else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
  153: 						   && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
  154: 						{
  155: #ifdef DEBUG
  156: 							printf("chunked transfer-encoding!\n");
  157: #endif
  158: 							chunked = 1;
  159: 						}
  160: 					}
  161: 					while(header_buf[i]=='\r' || header_buf[i] == '\n')
  162: 						i++;
  163: 					linestart = i;
  164: 					colon = linestart;
  165: 					valuestart = 0;
  166: 				}
  167: 			}
  168: 			/* copy the remaining of the received data back to buf */
  169: 			n = header_buf_used - endofheaders;
  170: 			memcpy(buf, header_buf + endofheaders, n);
  171: 			/* if(headers) */
  172: 		}
  173: 		if(endofheaders)
  174: 		{
  175: 			/* content */
  176: 			if(chunked)
  177: 			{
  178: 				int i = 0;
  179: 				while(i < n)
  180: 				{
  181: 					if(chunksize == 0)
  182: 					{
  183: 						/* reading chunk size */
  184: 						if(chunksize_buf_index == 0) {
  185: 							/* skipping any leading CR LF */
  186: 							if(i<n && buf[i] == '\r') i++;
  187: 							if(i<n && buf[i] == '\n') i++;
  188: 						}
  189: 						while(i<n && isxdigit(buf[i])
  190: 						     && chunksize_buf_index < (sizeof(chunksize_buf)-1))
  191: 						{
  192: 							chunksize_buf[chunksize_buf_index++] = buf[i];
  193: 							chunksize_buf[chunksize_buf_index] = '\0';
  194: 							i++;
  195: 						}
  196: 						while(i<n && buf[i] != '\r' && buf[i] != '\n')
  197: 							i++; /* discarding chunk-extension */
  198: 						if(i<n && buf[i] == '\r') i++;
  199: 						if(i<n && buf[i] == '\n') {
  200: 							unsigned int j;
  201: 							for(j = 0; j < chunksize_buf_index; j++) {
  202: 							if(chunksize_buf[j] >= '0'
  203: 							   && chunksize_buf[j] <= '9')
  204: 								chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
  205: 							else
  206: 								chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
  207: 							}
  208: 							chunksize_buf[0] = '\0';
  209: 							chunksize_buf_index = 0;
  210: 							i++;
  211: 						} else {
  212: 							/* not finished to get chunksize */
  213: 							continue;
  214: 						}
  215: #ifdef DEBUG
  216: 						printf("chunksize = %u (%x)\n", chunksize, chunksize);
  217: #endif
  218: 						if(chunksize == 0)
  219: 						{
  220: #ifdef DEBUG
  221: 							printf("end of HTTP content - %d %d\n", i, n);
  222: 							/*printf("'%.*s'\n", n-i, buf+i);*/
  223: #endif
  224: 							goto end_of_stream;
  225: 						}
  226: 					}
  227: 					bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
  228: 					if((content_buf_used + bytestocopy) > content_buf_len)
  229: 					{
  230: 						if(content_length >= (int)(content_buf_used + bytestocopy)) {
  231: 							content_buf_len = content_length;
  232: 						} else {
  233: 							content_buf_len = content_buf_used + bytestocopy;
  234: 						}
  235: 						content_buf = (char *)realloc((void *)content_buf,
  236: 						                              content_buf_len);
  237: 					}
  238: 					memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
  239: 					content_buf_used += bytestocopy;
  240: 					i += bytestocopy;
  241: 					chunksize -= bytestocopy;
  242: 				}
  243: 			}
  244: 			else
  245: 			{
  246: 				/* not chunked */
  247: 				if(content_length > 0
  248: 				   && (int)(content_buf_used + n) > content_length) {
  249: 					/* skipping additional bytes */
  250: 					n = content_length - content_buf_used;
  251: 				}
  252: 				if(content_buf_used + n > content_buf_len)
  253: 				{
  254: 					if(content_length >= (int)(content_buf_used + n)) {
  255: 						content_buf_len = content_length;
  256: 					} else {
  257: 						content_buf_len = content_buf_used + n;
  258: 					}
  259: 					content_buf = (char *)realloc((void *)content_buf,
  260: 					                              content_buf_len);
  261: 				}
  262: 				memcpy(content_buf + content_buf_used, buf, n);
  263: 				content_buf_used += n;
  264: 			}
  265: 		}
  266: 		/* use the Content-Length header value if available */
  267: 		if(content_length > 0 && (int)content_buf_used >= content_length)
  268: 		{
  269: #ifdef DEBUG
  270: 			printf("End of HTTP content\n");
  271: #endif
  272: 			break;
  273: 		}
  274: 	}
  275: end_of_stream:
  276: 	free(header_buf); header_buf = NULL;
  277: 	*size = content_buf_used;
  278: 	if(content_buf_used == 0)
  279: 	{
  280: 		free(content_buf);
  281: 		content_buf = NULL;
  282: 	}
  283: 	return content_buf;
  284: }
  285: 
  286: /* miniwget3() :
  287:  * do all the work.
  288:  * Return NULL if something failed. */
  289: static void *
  290: miniwget3(const char * host,
  291:           unsigned short port, const char * path,
  292:           int * size, char * addr_str, int addr_str_len,
  293:           const char * httpversion, unsigned int scope_id)
  294: {
  295: 	char buf[2048];
  296:     int s;
  297: 	int n;
  298: 	int len;
  299: 	int sent;
  300: 	void * content;
  301: 
  302: 	*size = 0;
  303: 	s = connecthostport(host, port, scope_id);
  304: 	if(s < 0)
  305: 		return NULL;
  306: 
  307: 	/* get address for caller ! */
  308: 	if(addr_str)
  309: 	{
  310: 		struct sockaddr_storage saddr;
  311: 		socklen_t saddrlen;
  312: 
  313: 		saddrlen = sizeof(saddr);
  314: 		if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
  315: 		{
  316: 			perror("getsockname");
  317: 		}
  318: 		else
  319: 		{
  320: #if defined(__amigaos__) && !defined(__amigaos4__)
  321: 	/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
  322:      * But his function make a string with the port :  nn.nn.nn.nn:port */
  323: /*		if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
  324:                             NULL, addr_str, (DWORD *)&addr_str_len))
  325: 		{
  326: 		    printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
  327: 		}*/
  328: 			/* the following code is only compatible with ip v4 addresses */
  329: 			strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
  330: #else
  331: #if 0
  332: 			if(saddr.sa_family == AF_INET6) {
  333: 				inet_ntop(AF_INET6,
  334: 				          &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
  335: 				          addr_str, addr_str_len);
  336: 			} else {
  337: 				inet_ntop(AF_INET,
  338: 				          &(((struct sockaddr_in *)&saddr)->sin_addr),
  339: 				          addr_str, addr_str_len);
  340: 			}
  341: #endif
  342: 			/* getnameinfo return ip v6 address with the scope identifier
  343: 			 * such as : 2a01:e35:8b2b:7330::%4281128194 */
  344: 			n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
  345: 			                addr_str, addr_str_len,
  346: 			                NULL, 0,
  347: 			                NI_NUMERICHOST | NI_NUMERICSERV);
  348: 			if(n != 0) {
  349: #ifdef _WIN32
  350: 				fprintf(stderr, "getnameinfo() failed : %d\n", n);
  351: #else
  352: 				fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
  353: #endif
  354: 			}
  355: #endif
  356: 		}
  357: #ifdef DEBUG
  358: 		printf("address miniwget : %s\n", addr_str);
  359: #endif
  360: 	}
  361: 
  362: 	len = snprintf(buf, sizeof(buf),
  363:                  "GET %s HTTP/%s\r\n"
  364: 			     "Host: %s:%d\r\n"
  365: 				 "Connection: Close\r\n"
  366: 				 "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
  367: 
  368: 				 "\r\n",
  369: 			   path, httpversion, host, port);
  370: 	sent = 0;
  371: 	/* sending the HTTP request */
  372: 	while(sent < len)
  373: 	{
  374: 		n = send(s, buf+sent, len-sent, 0);
  375: 		if(n < 0)
  376: 		{
  377: 			perror("send");
  378: 			closesocket(s);
  379: 			return NULL;
  380: 		}
  381: 		else
  382: 		{
  383: 			sent += n;
  384: 		}
  385: 	}
  386: 	content = getHTTPResponse(s, size);
  387: 	closesocket(s);
  388: 	return content;
  389: }
  390: 
  391: /* miniwget2() :
  392:  * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
  393: static void *
  394: miniwget2(const char * host,
  395: 		  unsigned short port, const char * path,
  396: 		  int * size, char * addr_str, int addr_str_len,
  397:           unsigned int scope_id)
  398: {
  399: 	char * respbuffer;
  400: 
  401: #if 1
  402: 	respbuffer = miniwget3(host, port, path, size,
  403: 	                       addr_str, addr_str_len, "1.1", scope_id);
  404: #else
  405: 	respbuffer = miniwget3(host, port, path, size,
  406: 	                       addr_str, addr_str_len, "1.0", scope_id);
  407: 	if (*size == 0)
  408: 	{
  409: #ifdef DEBUG
  410: 		printf("Retrying with HTTP/1.1\n");
  411: #endif
  412: 		free(respbuffer);
  413: 		respbuffer = miniwget3(host, port, path, size,
  414: 		                       addr_str, addr_str_len, "1.1", scope_id);
  415: 	}
  416: #endif
  417: 	return respbuffer;
  418: }
  419: 
  420: 
  421: 
  422: 
  423: /* parseURL()
  424:  * arguments :
  425:  *   url :		source string not modified
  426:  *   hostname :	hostname destination string (size of MAXHOSTNAMELEN+1)
  427:  *   port :		port (destination)
  428:  *   path :		pointer to the path part of the URL
  429:  *
  430:  * Return values :
  431:  *    0 - Failure
  432:  *    1 - Success         */
  433: int
  434: parseURL(const char * url,
  435:          char * hostname, unsigned short * port,
  436:          char * * path, unsigned int * scope_id)
  437: {
  438: 	char * p1, *p2, *p3;
  439: 	if(!url)
  440: 		return 0;
  441: 	p1 = strstr(url, "://");
  442: 	if(!p1)
  443: 		return 0;
  444: 	p1 += 3;
  445: 	if(  (url[0]!='h') || (url[1]!='t')
  446: 	   ||(url[2]!='t') || (url[3]!='p'))
  447: 		return 0;
  448: 	memset(hostname, 0, MAXHOSTNAMELEN + 1);
  449: 	if(*p1 == '[')
  450: 	{
  451: 		/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
  452: 		char * scope;
  453: 		scope = strchr(p1, '%');
  454: 		p2 = strchr(p1, ']');
  455: 		if(p2 && scope && scope < p2 && scope_id) {
  456: 			/* parse scope */
  457: #ifdef IF_NAMESIZE
  458: 			char tmp[IF_NAMESIZE];
  459: 			int l;
  460: 			scope++;
  461: 			/* "%25" is just '%' in URL encoding */
  462: 			if(scope[0] == '2' && scope[1] == '5')
  463: 				scope += 2;	/* skip "25" */
  464: 			l = p2 - scope;
  465: 			if(l >= IF_NAMESIZE)
  466: 				l = IF_NAMESIZE - 1;
  467: 			memcpy(tmp, scope, l);
  468: 			tmp[l] = '\0';
  469: 			*scope_id = if_nametoindex(tmp);
  470: 			if(*scope_id == 0) {
  471: 				*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
  472: 			}
  473: #else
  474: 			/* under windows, scope is numerical */
  475: 			char tmp[8];
  476: 			int l;
  477: 			scope++;
  478: 			/* "%25" is just '%' in URL encoding */
  479: 			if(scope[0] == '2' && scope[1] == '5')
  480: 				scope += 2;	/* skip "25" */
  481: 			l = p2 - scope;
  482: 			if(l >= sizeof(tmp))
  483: 				l = sizeof(tmp) - 1;
  484: 			memcpy(tmp, scope, l);
  485: 			tmp[l] = '\0';
  486: 			*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
  487: #endif
  488: 		}
  489: 		p3 = strchr(p1, '/');
  490: 		if(p2 && p3)
  491: 		{
  492: 			p2++;
  493: 			strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
  494: 			if(*p2 == ':')
  495: 			{
  496: 				*port = 0;
  497: 				p2++;
  498: 				while( (*p2 >= '0') && (*p2 <= '9'))
  499: 				{
  500: 					*port *= 10;
  501: 					*port += (unsigned short)(*p2 - '0');
  502: 					p2++;
  503: 				}
  504: 			}
  505: 			else
  506: 			{
  507: 				*port = 80;
  508: 			}
  509: 			*path = p3;
  510: 			return 1;
  511: 		}
  512: 	}
  513: 	p2 = strchr(p1, ':');
  514: 	p3 = strchr(p1, '/');
  515: 	if(!p3)
  516: 		return 0;
  517: 	if(!p2 || (p2>p3))
  518: 	{
  519: 		strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
  520: 		*port = 80;
  521: 	}
  522: 	else
  523: 	{
  524: 		strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
  525: 		*port = 0;
  526: 		p2++;
  527: 		while( (*p2 >= '0') && (*p2 <= '9'))
  528: 		{
  529: 			*port *= 10;
  530: 			*port += (unsigned short)(*p2 - '0');
  531: 			p2++;
  532: 		}
  533: 	}
  534: 	*path = p3;
  535: 	return 1;
  536: }
  537: 
  538: void *
  539: miniwget(const char * url, int * size, unsigned int scope_id)
  540: {
  541: 	unsigned short port;
  542: 	char * path;
  543: 	/* protocol://host:port/chemin */
  544: 	char hostname[MAXHOSTNAMELEN+1];
  545: 	*size = 0;
  546: 	if(!parseURL(url, hostname, &port, &path, &scope_id))
  547: 		return NULL;
  548: #ifdef DEBUG
  549: 	printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
  550: 	       hostname, port, path, scope_id);
  551: #endif
  552: 	return miniwget2(hostname, port, path, size, 0, 0, scope_id);
  553: }
  554: 
  555: void *
  556: miniwget_getaddr(const char * url, int * size,
  557:                  char * addr, int addrlen, unsigned int scope_id)
  558: {
  559: 	unsigned short port;
  560: 	char * path;
  561: 	/* protocol://host:port/path */
  562: 	char hostname[MAXHOSTNAMELEN+1];
  563: 	*size = 0;
  564: 	if(addr)
  565: 		addr[0] = '\0';
  566: 	if(!parseURL(url, hostname, &port, &path, &scope_id))
  567: 		return NULL;
  568: #ifdef DEBUG
  569: 	printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
  570: 	       hostname, port, path, scope_id);
  571: #endif
  572: 	return miniwget2(hostname, port, path, size, addr, addrlen, scope_id);
  573: }
  574: 

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