Annotation of embedaddon/miniupnpc/miniwget.c, revision 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>