miniwget.c 16 KB

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