miniwget.c 17 KB

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