PageRenderTime 38ms CodeModel.GetById 10ms app.highlight 23ms RepoModel.GetById 2ms app.codeStats 0ms

/thirdparty/libportfwd/third-party/miniupnpc-1.6/miniwget.c

http://github.com/tomahawk-player/tomahawk
C | 522 lines | 432 code | 20 blank | 70 comment | 114 complexity | 645073912423576c13cd21e047dc6964 MD5 | raw file
  1/* $Id: miniwget.c,v 1.52 2011/06/17 22:59:42 nanard 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 */
 58void *
 59getHTTPResponse(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	}
272end_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. */
286static void *
287miniwget3(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. */
390static void *
391miniwget2(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         */
425int 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
491void * 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
506void * 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