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