File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpc / miniwget.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:16:22 2012 UTC (12 years, 4 months ago) by misho
Branches: miniupnpc, elwix, MAIN
CVS tags: v1_6, HEAD
miniupnpc

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

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