miniwget.c 15 KB

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