2
0

miniwget.c 16 KB

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