Annotation of embedaddon/miniupnpc/miniwget.c, revision 1.1.1.2
1.1.1.2 ! misho 1: /* $Id: miniwget.c,v 1.58 2012/08/11 05:52:49 nanard Exp $ */
1.1 misho 2: /* Project : miniupnp
1.1.1.2 ! misho 3: * Website : http://miniupnp.free.fr/
1.1 misho 4: * Author : Thomas Bernard
1.1.1.2 ! misho 5: * Copyright (c) 2005-2012 Thomas Bernard
1.1 misho 6: * This software is subject to the conditions detailed in the
7: * LICENCE file provided in this distribution. */
1.1.1.2 ! misho 8:
1.1 misho 9: #include <stdio.h>
10: #include <stdlib.h>
11: #include <string.h>
12: #include <ctype.h>
1.1.1.2 ! misho 13: #ifdef _WIN32
1.1 misho 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 */
1.1.1.2 ! misho 28: #else /* #ifdef _WIN32 */
1.1 misho 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>
1.1.1.2 ! misho 37: #include <netinet/in.h>
1.1 misho 38: #include <arpa/inet.h>
1.1.1.2 ! misho 39: #include <net/if.h>
1.1 misho 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
1.1.1.2 ! misho 45: #endif /* #else _WIN32 */
1.1 misho 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;
1.1.1.2 ! misho 73: unsigned int header_buf_len = 2048;
! 74: unsigned int header_buf_used = 0;
1.1 misho 75: char * content_buf;
1.1.1.2 ! misho 76: unsigned int content_buf_len = 2048;
! 77: unsigned int content_buf_used = 0;
1.1 misho 78: char chunksize_buf[32];
1.1.1.2 ! misho 79: unsigned int chunksize_buf_index;
1.1 misho 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:
1.1.1.2 ! misho 86: while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
1.1 misho 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;
1.1.1.2 ! misho 103: while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
1.1 misho 104: if(header_buf[i] == '\r') {
105: i++;
106: if(header_buf[i] == '\n') {
107: i++;
1.1.1.2 ! misho 108: if(i < (int)header_buf_used && header_buf[i] == '\r') {
1.1 misho 109: i++;
1.1.1.2 ! misho 110: if(i < (int)header_buf_used && header_buf[i] == '\n') {
1.1 misho 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;
1.1.1.2 ! misho 166: }
1.1 misho 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') {
1.1.1.2 ! misho 200: unsigned int j;
1.1 misho 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: }
1.1.1.2 ! misho 227: bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
! 228: if((content_buf_used + bytestocopy) > content_buf_len)
1.1 misho 229: {
1.1.1.2 ! misho 230: if(content_length >= (int)(content_buf_used + bytestocopy)) {
1.1 misho 231: content_buf_len = content_length;
232: } else {
1.1.1.2 ! misho 233: content_buf_len = content_buf_used + bytestocopy;
1.1 misho 234: }
1.1.1.2 ! misho 235: content_buf = (char *)realloc((void *)content_buf,
1.1 misho 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
1.1.1.2 ! misho 248: && (int)(content_buf_used + n) > content_length) {
1.1 misho 249: /* skipping additional bytes */
250: n = content_length - content_buf_used;
251: }
252: if(content_buf_used + n > content_buf_len)
253: {
1.1.1.2 ! misho 254: if(content_length >= (int)(content_buf_used + n)) {
1.1 misho 255: content_buf_len = content_length;
256: } else {
257: content_buf_len = content_buf_used + n;
258: }
1.1.1.2 ! misho 259: content_buf = (char *)realloc((void *)content_buf,
1.1 misho 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 */
1.1.1.2 ! misho 267: if(content_length > 0 && (int)content_buf_used >= content_length)
1.1 misho 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 *
1.1.1.2 ! misho 290: miniwget3(const char * host,
1.1 misho 291: unsigned short port, const char * path,
292: int * size, char * addr_str, int addr_str_len,
1.1.1.2 ! misho 293: const char * httpversion, unsigned int scope_id)
1.1 misho 294: {
295: char buf[2048];
296: int s;
297: int n;
298: int len;
299: int sent;
300: void * content;
301:
302: *size = 0;
1.1.1.2 ! misho 303: s = connecthostport(host, port, scope_id);
1.1 misho 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) {
1.1.1.2 ! misho 349: #ifdef _WIN32
1.1 misho 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 *
1.1.1.2 ! misho 394: miniwget2(const char * host,
1.1 misho 395: unsigned short port, const char * path,
1.1.1.2 ! misho 396: int * size, char * addr_str, int addr_str_len,
! 397: unsigned int scope_id)
1.1 misho 398: {
399: char * respbuffer;
400:
1.1.1.2 ! misho 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);
1.1 misho 407: if (*size == 0)
408: {
409: #ifdef DEBUG
410: printf("Retrying with HTTP/1.1\n");
411: #endif
412: free(respbuffer);
1.1.1.2 ! misho 413: respbuffer = miniwget3(host, port, path, size,
! 414: addr_str, addr_str_len, "1.1", scope_id);
1.1 misho 415: }
1.1.1.2 ! misho 416: #endif
1.1 misho 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)
1.1.1.2 ! misho 428: * path : pointer to the path part of the URL
1.1 misho 429: *
430: * Return values :
431: * 0 - Failure
432: * 1 - Success */
1.1.1.2 ! misho 433: int
! 434: parseURL(const char * url,
! 435: char * hostname, unsigned short * port,
! 436: char * * path, unsigned int * scope_id)
1.1 misho 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 */
1.1.1.2 ! misho 452: char * scope;
! 453: scope = strchr(p1, '%');
1.1 misho 454: p2 = strchr(p1, ']');
1.1.1.2 ! misho 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: }
1.1 misho 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:
1.1.1.2 ! misho 538: void *
! 539: miniwget(const char * url, int * size, unsigned int scope_id)
1.1 misho 540: {
541: unsigned short port;
542: char * path;
543: /* protocol://host:port/chemin */
544: char hostname[MAXHOSTNAMELEN+1];
545: *size = 0;
1.1.1.2 ! misho 546: if(!parseURL(url, hostname, &port, &path, &scope_id))
1.1 misho 547: return NULL;
548: #ifdef DEBUG
1.1.1.2 ! misho 549: printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
! 550: hostname, port, path, scope_id);
1.1 misho 551: #endif
1.1.1.2 ! misho 552: return miniwget2(hostname, port, path, size, 0, 0, scope_id);
1.1 misho 553: }
554:
1.1.1.2 ! misho 555: void *
! 556: miniwget_getaddr(const char * url, int * size,
! 557: char * addr, int addrlen, unsigned int scope_id)
1.1 misho 558: {
559: unsigned short port;
560: char * path;
1.1.1.2 ! misho 561: /* protocol://host:port/path */
1.1 misho 562: char hostname[MAXHOSTNAMELEN+1];
563: *size = 0;
564: if(addr)
565: addr[0] = '\0';
1.1.1.2 ! misho 566: if(!parseURL(url, hostname, &port, &path, &scope_id))
1.1 misho 567: return NULL;
568: #ifdef DEBUG
1.1.1.2 ! misho 569: printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
! 570: hostname, port, path, scope_id);
1.1 misho 571: #endif
1.1.1.2 ! misho 572: return miniwget2(hostname, port, path, size, addr, addrlen, scope_id);
1.1 misho 573: }
574:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>