1/* $Id: miniwget.c,v 1.66 2014/11/19 15:18:47 nanard Exp $ */ 2/* Project : miniupnp 3 * Website : http://miniupnp.free.fr/ 4 * Author : Thomas Bernard 5 * Copyright (c) 2005-2014 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 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 <netdb.h> 36#include <sys/socket.h> 37#include <netinet/in.h> 38#include <arpa/inet.h> 39#include <net/if.h> 40#include <netdb.h> 41#define closesocket close 42#include <strings.h> 43#endif /* #else _WIN32 */ 44#ifdef __GNU__ 45#define MAXHOSTNAMELEN 64 46#endif /* __GNU__ */ 47 48#ifndef MIN 49#define MIN(x,y) (((x)<(y))?(x):(y)) 50#endif /* MIN */ 51 52 53#include "miniupnpcstrings.h" 54#include "miniwget.h" 55#include "connecthostport.h" 56#include "receivedata.h" 57#include "miniupnpc.h" 58 59#ifndef MAXHOSTNAMELEN 60#define MAXHOSTNAMELEN 64 61#endif 62 63/* 64 * Read a HTTP response from a socket. 65 * Process Content-Length and Transfer-encoding headers. 66 * return a pointer to the content buffer, which length is saved 67 * to the length parameter. 68 */ 69void * 70getHTTPResponse(int s, int * size, struct UPNPDevInfo *devinfo) 71{ 72 char buf[2048]; 73 int n; 74 int endofheaders = 0; 75 int chunked = 0; 76 int content_length = -1; 77 unsigned int chunksize = 0; 78 unsigned int bytestocopy = 0; 79 /* buffers : */ 80 char * header_buf; 81 unsigned int header_buf_len = 2048; 82 unsigned int header_buf_used = 0; 83 char * content_buf; 84 unsigned int content_buf_len = 2048; 85 unsigned int content_buf_used = 0; 86 char chunksize_buf[32]; 87 unsigned int chunksize_buf_index; 88 89 header_buf = malloc(header_buf_len); 90 content_buf = malloc(content_buf_len); 91 chunksize_buf[0] = '\0'; 92 chunksize_buf_index = 0; 93 94 while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0) 95 { 96/* 97printf("==============\n"); 98printf("%s\n",buf); 99printf("==============\n"); 100*/ 101 if(endofheaders == 0) 102 { 103 int i; 104 int linestart=0; 105 int colon=0; 106 int valuestart=0; 107 if(header_buf_used + n > header_buf_len) { 108 header_buf = realloc(header_buf, header_buf_used + n); 109 header_buf_len = header_buf_used + n; 110 } 111 memcpy(header_buf + header_buf_used, buf, n); 112 header_buf_used += n; 113 /* search for CR LF CR LF (end of headers) 114 * recognize also LF LF */ 115 i = 0; 116 while(i < ((int)header_buf_used-1) && (endofheaders == 0)) { 117 if(header_buf[i] == '\r') { 118 i++; 119 if(header_buf[i] == '\n') { 120 i++; 121 if(i < (int)header_buf_used && header_buf[i] == '\r') { 122 i++; 123 if(i < (int)header_buf_used && header_buf[i] == '\n') { 124 endofheaders = i+1; 125 } 126 } 127 } 128 } else if(header_buf[i] == '\n') { 129 i++; 130 if(header_buf[i] == '\n') { 131 endofheaders = i+1; 132 } 133 } 134 i++; 135 } 136 if(endofheaders == 0) 137 continue; 138 /* parse header lines */ 139 for(i = 0; i < endofheaders - 1; i++) { 140 if(colon <= linestart && header_buf[i]==':') 141 { 142 colon = i; 143 while(i < (endofheaders-1) 144 && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t')) 145 i++; 146 valuestart = i + 1; 147 } 148 /* detecting end of line */ 149 else if(header_buf[i]=='\r' || header_buf[i]=='\n') 150 { 151 if(colon > linestart && valuestart > colon) 152 { 153#ifdef DEBUG 154 printf("header='%.*s', value='%.*s'\n", 155 colon-linestart, header_buf+linestart, 156 i-valuestart, header_buf+valuestart); 157/*printf("colon= %d, i= %d\n", colon-linestart, i-valuestart);*/ 158#endif 159 if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart)) 160 { 161 content_length = atoi(header_buf+valuestart); 162#ifdef DEBUG 163 printf("Content-Length: %d\n", content_length); 164#endif 165 } 166 else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart) 167 && 0==strncasecmp(header_buf+valuestart, "chunked", 7)) 168 { 169#ifdef DEBUG 170 printf("chunked transfer-encoding!\n"); 171#endif 172 chunked = 1; 173 } 174 else if(0==strncasecmp(header_buf+linestart, "server", colon-linestart)) 175 { 176 char *end_ptr; 177 int get_server = 0; 178 end_ptr = strchr(header_buf+valuestart, ' '); 179 if( end_ptr != NULL ) 180 { 181 *end_ptr = '\0'; 182 get_server = 1; 183 } 184 end_ptr = strchr(header_buf+valuestart,'\n'); 185 if( end_ptr != NULL ) 186 { 187 *end_ptr = '\0'; 188 get_server = 1; 189 } 190 /*printf("Server: %s\n", header_buf+valuestart);*/ 191 if( get_server ) 192 strcpy(devinfo->type, header_buf+valuestart); 193 } 194 } 195 while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n')) 196 i++; 197 linestart = i; 198 colon = linestart; 199 valuestart = 0; 200 } 201 } 202 /* copy the remaining of the received data back to buf */ 203 n = header_buf_used - endofheaders; 204 memcpy(buf, header_buf + endofheaders, n); 205 /* if(headers) */ 206 } 207 if(endofheaders) 208 { 209 /* content */ 210 if(chunked) 211 { 212 int i = 0; 213 while(i < n) 214 { 215 if(chunksize == 0) 216 { 217 /* reading chunk size */ 218 if(chunksize_buf_index == 0) { 219 /* skipping any leading CR LF */ 220 if(i<n && buf[i] == '\r') i++; 221 if(i<n && buf[i] == '\n') i++; 222 } 223 while(i<n && isxdigit(buf[i]) 224 && chunksize_buf_index < (sizeof(chunksize_buf)-1)) 225 { 226 chunksize_buf[chunksize_buf_index++] = buf[i]; 227 chunksize_buf[chunksize_buf_index] = '\0'; 228 i++; 229 } 230 while(i<n && buf[i] != '\r' && buf[i] != '\n') 231 i++; /* discarding chunk-extension */ 232 if(i<n && buf[i] == '\r') i++; 233 if(i<n && buf[i] == '\n') { 234 unsigned int j; 235 for(j = 0; j < chunksize_buf_index; j++) { 236 if(chunksize_buf[j] >= '0' 237 && chunksize_buf[j] <= '9') 238 chunksize = (chunksize << 4) + (chunksize_buf[j] - '0'); 239 else 240 chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10); 241 } 242 chunksize_buf[0] = '\0'; 243 chunksize_buf_index = 0; 244 i++; 245 } else { 246 /* not finished to get chunksize */ 247 continue; 248 } 249#ifdef DEBUG 250 printf("chunksize = %u (%x)\n", chunksize, chunksize); 251#endif 252 if(chunksize == 0) 253 { 254#ifdef DEBUG 255 printf("end of HTTP content - %d %d\n", i, n); 256 /*printf("'%.*s'\n", n-i, buf+i);*/ 257#endif 258 goto end_of_stream; 259 } 260 } 261 bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i); 262 if((content_buf_used + bytestocopy) > content_buf_len) 263 { 264 if(content_length >= (int)(content_buf_used + bytestocopy)) { 265 content_buf_len = content_length; 266 } else { 267 content_buf_len = content_buf_used + bytestocopy; 268 } 269 content_buf = (char *)realloc((void *)content_buf, 270 content_buf_len); 271 } 272 memcpy(content_buf + content_buf_used, buf + i, bytestocopy); 273 content_buf_used += bytestocopy; 274 i += bytestocopy; 275 chunksize -= bytestocopy; 276 } 277 } 278 else 279 { 280 /* not chunked */ 281 if(content_length > 0 282 && (int)(content_buf_used + n) > content_length) { 283 /* skipping additional bytes */ 284 n = content_length - content_buf_used; 285 } 286 if(content_buf_used + n > content_buf_len) 287 { 288 if(content_length >= (int)(content_buf_used + n)) { 289 content_buf_len = content_length; 290 } else { 291 content_buf_len = content_buf_used + n; 292 } 293 content_buf = (char *)realloc((void *)content_buf, 294 content_buf_len); 295 } 296 memcpy(content_buf + content_buf_used, buf, n); 297 content_buf_used += n; 298 } 299 } 300 /* use the Content-Length header value if available */ 301 if(content_length > 0 && (int)content_buf_used >= content_length) 302 { 303#ifdef DEBUG 304 printf("End of HTTP content\n"); 305#endif 306 break; 307 } 308 } 309end_of_stream: 310 free(header_buf); header_buf = NULL; 311 *size = content_buf_used; 312 if(content_buf_used == 0) 313 { 314 free(content_buf); 315 content_buf = NULL; 316 } 317 return content_buf; 318} 319 320/* miniwget3() : 321 * do all the work. 322 * Return NULL if something failed. */ 323static void * 324miniwget3(const char * host, 325 unsigned short port, const char * path, 326 int * size, char * addr_str, int addr_str_len, 327 const char * httpversion, unsigned int scope_id, 328 struct UPNPDevInfo *devinfo) 329{ 330 char buf[2048]; 331 int s; 332 int n; 333 int len; 334 int sent; 335 void * content; 336 337 *size = 0; 338 s = connecthostport(host, port, scope_id); 339 if(s < 0) 340 return NULL; 341 342 /* get address for caller ! */ 343 if(addr_str) 344 { 345 struct sockaddr_storage saddr; 346 socklen_t saddrlen; 347 348 saddrlen = sizeof(saddr); 349 if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0) 350 { 351 perror("getsockname"); 352 } 353 else 354 { 355#if defined(__amigaos__) && !defined(__amigaos4__) 356 /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD); 357 * But his function make a string with the port : nn.nn.nn.nn:port */ 358/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr), 359 NULL, addr_str, (DWORD *)&addr_str_len)) 360 { 361 printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError()); 362 }*/ 363 /* the following code is only compatible with ip v4 addresses */ 364 strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len); 365#else 366#if 0 367 if(saddr.sa_family == AF_INET6) { 368 inet_ntop(AF_INET6, 369 &(((struct sockaddr_in6 *)&saddr)->sin6_addr), 370 addr_str, addr_str_len); 371 } else { 372 inet_ntop(AF_INET, 373 &(((struct sockaddr_in *)&saddr)->sin_addr), 374 addr_str, addr_str_len); 375 } 376#endif 377 /* getnameinfo return ip v6 address with the scope identifier 378 * such as : 2a01:e35:8b2b:7330::%4281128194 */ 379 n = getnameinfo((const struct sockaddr *)&saddr, saddrlen, 380 addr_str, addr_str_len, 381 NULL, 0, 382 NI_NUMERICHOST | NI_NUMERICSERV); 383 if(n != 0) { 384#ifdef _WIN32 385 fprintf(stderr, "getnameinfo() failed : %d\n", n); 386#else 387 fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n)); 388#endif 389 } 390#endif 391 } 392#ifdef DEBUG 393 printf("address miniwget : %s\n", addr_str); 394#endif 395 } 396 397 len = snprintf(buf, sizeof(buf), 398 "GET %s HTTP/%s\r\n" 399 "Host: %s:%d\r\n" 400 "Connection: Close\r\n" 401 "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" 402 403 "\r\n", 404 path, httpversion, host, port); 405 sent = 0; 406 /* sending the HTTP request */ 407 while(sent < len) 408 { 409 n = send(s, buf+sent, len-sent, 0); 410 if(n < 0) 411 { 412 perror("send"); 413 closesocket(s); 414 return NULL; 415 } 416 else 417 { 418 sent += n; 419 } 420 } 421 content = getHTTPResponse(s, size, devinfo); 422 closesocket(s); 423 return content; 424} 425 426/* miniwget2() : 427 * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */ 428static void * 429miniwget2(const char * host, 430 unsigned short port, const char * path, 431 int * size, char * addr_str, int addr_str_len, 432 unsigned int scope_id, struct UPNPDevInfo *devinfo) 433{ 434 char * respbuffer; 435 436#if 1 437 respbuffer = miniwget3(host, port, path, size, 438 addr_str, addr_str_len, "1.1", scope_id, devinfo); 439#else 440 respbuffer = miniwget3(host, port, path, size, 441 addr_str, addr_str_len, "1.0", scope_id); 442 if (*size == 0) 443 { 444#ifdef DEBUG 445 printf("Retrying with HTTP/1.1\n"); 446#endif 447 free(respbuffer); 448 respbuffer = miniwget3(host, port, path, size, 449 addr_str, addr_str_len, "1.1", scope_id); 450 } 451#endif 452 return respbuffer; 453} 454 455 456 457 458/* parseURL() 459 * arguments : 460 * url : source string not modified 461 * hostname : hostname destination string (size of MAXHOSTNAMELEN+1) 462 * port : port (destination) 463 * path : pointer to the path part of the URL 464 * 465 * Return values : 466 * 0 - Failure 467 * 1 - Success */ 468int 469parseURL(const char * url, 470 char * hostname, unsigned short * port, 471 char * * path, unsigned int * scope_id) 472{ 473 char * p1, *p2, *p3; 474 if(!url) 475 return 0; 476 p1 = strstr(url, "://"); 477 if(!p1) 478 return 0; 479 p1 += 3; 480 if( (url[0]!='h') || (url[1]!='t') 481 ||(url[2]!='t') || (url[3]!='p')) 482 return 0; 483 memset(hostname, 0, MAXHOSTNAMELEN + 1); 484 if(*p1 == '[') 485 { 486 /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */ 487 char * scope; 488 scope = strchr(p1, '%'); 489 p2 = strchr(p1, ']'); 490 if(p2 && scope && scope < p2 && scope_id) { 491 /* parse scope */ 492#ifdef IF_NAMESIZE 493 char tmp[IF_NAMESIZE]; 494 int l; 495 scope++; 496 /* "%25" is just '%' in URL encoding */ 497 if(scope[0] == '2' && scope[1] == '5') 498 scope += 2; /* skip "25" */ 499 l = p2 - scope; 500 if(l >= IF_NAMESIZE) 501 l = IF_NAMESIZE - 1; 502 memcpy(tmp, scope, l); 503 tmp[l] = '\0'; 504 *scope_id = if_nametoindex(tmp); 505 if(*scope_id == 0) { 506 *scope_id = (unsigned int)strtoul(tmp, NULL, 10); 507 } 508#else 509 /* under windows, scope is numerical */ 510 char tmp[8]; 511 int l; 512 scope++; 513 /* "%25" is just '%' in URL encoding */ 514 if(scope[0] == '2' && scope[1] == '5') 515 scope += 2; /* skip "25" */ 516 l = p2 - scope; 517 if(l >= sizeof(tmp)) 518 l = sizeof(tmp) - 1; 519 memcpy(tmp, scope, l); 520 tmp[l] = '\0'; 521 *scope_id = (unsigned int)strtoul(tmp, NULL, 10); 522#endif 523 } 524 p3 = strchr(p1, '/'); 525 if(p2 && p3) 526 { 527 p2++; 528 strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1))); 529 if(*p2 == ':') 530 { 531 *port = 0; 532 p2++; 533 while( (*p2 >= '0') && (*p2 <= '9')) 534 { 535 *port *= 10; 536 *port += (unsigned short)(*p2 - '0'); 537 p2++; 538 } 539 } 540 else 541 { 542 *port = 80; 543 } 544 *path = p3; 545 return 1; 546 } 547 } 548 p2 = strchr(p1, ':'); 549 p3 = strchr(p1, '/'); 550 if(!p3) 551 return 0; 552 if(!p2 || (p2>p3)) 553 { 554 strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1))); 555 *port = 80; 556 } 557 else 558 { 559 strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1))); 560 *port = 0; 561 p2++; 562 while( (*p2 >= '0') && (*p2 <= '9')) 563 { 564 *port *= 10; 565 *port += (unsigned short)(*p2 - '0'); 566 p2++; 567 } 568 } 569 *path = p3; 570 return 1; 571} 572 573void * 574miniwget(const char * url, int * size, unsigned int scope_id, struct UPNPDevInfo *devinfo) 575{ 576 unsigned short port; 577 char * path; 578 /* protocol://host:port/chemin */ 579 char hostname[MAXHOSTNAMELEN+1]; 580 *size = 0; 581 if(!parseURL(url, hostname, &port, &path, &scope_id)) 582 return NULL; 583#ifdef DEBUG 584 printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", 585 hostname, port, path, scope_id); 586#endif 587 return miniwget2(hostname, port, path, size, 0, 0, scope_id, devinfo); 588} 589 590void * 591miniwget_getaddr(const char * url, int * size, 592 char * addr, int addrlen, unsigned int scope_id, struct UPNPDevInfo *devinfo, char * dut_addr) 593{ 594 unsigned short port; 595 char * path; 596 /* protocol://host:port/path */ 597 char hostname[MAXHOSTNAMELEN+1]; 598 *size = 0; 599 600 if(addr) 601 addr[0] = '\0'; 602 if(!parseURL(url, hostname, &port, &path, &scope_id)) 603 return NULL; 604#ifdef DEBUG 605 printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", 606 hostname, port, path, scope_id); 607#endif 608 if(0)//(dut_addr != NULL) //bypass subnetwork check 609 { 610 char *ptr, *head; 611 int i = 0; 612 head = dut_addr; 613 while (1) 614 { 615 ptr = strchr(head, '.'); 616 if(ptr) 617 { 618 head = (ptr+1); 619 i++; 620 if(i == 3) 621 { 622 i = ptr - dut_addr; 623 break; 624 } 625 } 626 else 627 { 628 i = 0; 629 break; 630 } 631 } 632 if(strncmp(hostname, dut_addr, i)) 633 return NULL; 634 } 635 636 strcpy(devinfo->hostname, hostname); 637 638 return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, devinfo); 639} 640 641