Annotation of embedaddon/miniupnpc/miniwget.c, revision 1.1.1.1
1.1 misho 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: */
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>