miniwget.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. /* $Id: miniwget.c,v 1.41 2010/12/12 23:52:02 nanard Exp $ */
  2. /* Project : miniupnp
  3. * Author : Thomas Bernard
  4. * Copyright (c) 2005-2010 Thomas Bernard
  5. * This software is subject to the conditions detailed in the
  6. * LICENCE file provided in this distribution. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include "miniupnpc.h"
  12. #ifdef WIN32
  13. #include <winsock2.h>
  14. #include <ws2tcpip.h>
  15. #include <io.h>
  16. #define MAXHOSTNAMELEN 64
  17. #define MIN(x,y) (((x)<(y))?(x):(y))
  18. #define snprintf _snprintf
  19. #define socklen_t int
  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. #else /* #ifdef WIN32 */
  26. #include <unistd.h>
  27. #include <sys/param.h>
  28. #if defined(__amigaos__) && !defined(__amigaos4__)
  29. #define socklen_t int
  30. #else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
  31. #include <sys/select.h>
  32. #endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
  33. #include <sys/socket.h>
  34. #include <arpa/inet.h>
  35. #include <netdb.h>
  36. #define closesocket close
  37. /* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
  38. * during the connect() call */
  39. #define MINIUPNPC_IGNORE_EINTR
  40. #endif /* #else WIN32 */
  41. #if defined(__sun) || defined(sun)
  42. #define MIN(x,y) (((x)<(y))?(x):(y))
  43. #endif
  44. // KevinJ: Remove need for cmake
  45. // #include "miniupnpcstrings.h"
  46. #include "miniupnpcstrings.h.in"
  47. #include "miniwget.h"
  48. #include "connecthostport.h"
  49. /*
  50. * Read a HTTP response from a socket.
  51. * Process Content-Length and Transfer-encoding headers.
  52. */
  53. void *
  54. getHTTPResponse(int s, int * size)
  55. {
  56. char buf[2048];
  57. int n;
  58. int headers = 1;
  59. int chunked = 0;
  60. int content_length = -1;
  61. unsigned int chunksize = 0;
  62. unsigned int bytestocopy = 0;
  63. /* buffers : */
  64. char * header_buf;
  65. int header_buf_len = 2048;
  66. int header_buf_used = 0;
  67. char * content_buf;
  68. int content_buf_len = 2048;
  69. int content_buf_used = 0;
  70. header_buf = malloc(header_buf_len);
  71. content_buf = malloc(content_buf_len);
  72. while((n = ReceiveData(s, buf, 2048, 5000)) > 0)
  73. {
  74. if(headers)
  75. {
  76. int i;
  77. int linestart=0;
  78. int colon=0;
  79. int valuestart=0;
  80. if(header_buf_used + n > header_buf_len) {
  81. header_buf = realloc(header_buf, header_buf_used + n);
  82. header_buf_len = header_buf_used + n;
  83. }
  84. memcpy(header_buf + header_buf_used, buf, n);
  85. header_buf_used += n;
  86. for(i = 0; i < (header_buf_used-3); i++) {
  87. if(colon <= linestart && header_buf[i]==':')
  88. {
  89. colon = i;
  90. while(i < (n-3)
  91. && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
  92. i++;
  93. valuestart = i + 1;
  94. }
  95. /* detecting end of line */
  96. else if(header_buf[i]=='\r' && header_buf[i+1]=='\n')
  97. {
  98. if(colon > linestart && valuestart > colon)
  99. {
  100. #ifdef DEBUG
  101. printf("header='%.*s', value='%.*s'\n",
  102. colon-linestart, header_buf+linestart,
  103. i-valuestart, header_buf+valuestart);
  104. #endif
  105. if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
  106. {
  107. content_length = atoi(header_buf+valuestart);
  108. #ifdef DEBUG
  109. printf("Content-Length: %d\n", content_length);
  110. #endif
  111. }
  112. else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
  113. && 0==strncasecmp(buf+valuestart, "chunked", 7))
  114. {
  115. #ifdef DEBUG
  116. printf("chunked transfer-encoding!\n");
  117. #endif
  118. chunked = 1;
  119. }
  120. }
  121. linestart = i+2;
  122. colon = linestart;
  123. valuestart = 0;
  124. }
  125. /* searching for the end of the HTTP headers */
  126. if(header_buf[i]=='\r' && header_buf[i+1]=='\n'
  127. && header_buf[i+2]=='\r' && header_buf[i+3]=='\n')
  128. {
  129. headers = 0; /* end */
  130. i += 4;
  131. if(i < header_buf_used)
  132. {
  133. if(chunked)
  134. {
  135. while(i<header_buf_used)
  136. {
  137. while(i<header_buf_used && isxdigit(header_buf[i]))
  138. {
  139. if(header_buf[i] >= '0' && header_buf[i] <= '9')
  140. chunksize = (chunksize << 4) + (header_buf[i] - '0');
  141. else
  142. chunksize = (chunksize << 4) + ((header_buf[i] | 32) - 'a' + 10);
  143. i++;
  144. }
  145. /* discarding chunk-extension */
  146. while(i < header_buf_used && header_buf[i] != '\r') i++;
  147. if(i < header_buf_used && header_buf[i] == '\r') i++;
  148. if(i < header_buf_used && header_buf[i] == '\n') i++;
  149. #ifdef DEBUG
  150. printf("chunksize = %u (%x)\n", chunksize, chunksize);
  151. #endif
  152. if(chunksize == 0)
  153. {
  154. #ifdef DEBUG
  155. printf("end of HTTP content !\n");
  156. #endif
  157. goto end_of_stream;
  158. }
  159. bytestocopy = ((int)chunksize < header_buf_used - i)?chunksize:(header_buf_used - i);
  160. #ifdef DEBUG
  161. printf("chunksize=%u bytestocopy=%u (i=%d header_buf_used=%d)\n",
  162. chunksize, bytestocopy, i, header_buf_used);
  163. #endif
  164. if(content_buf_len < (int)(content_buf_used + bytestocopy))
  165. {
  166. content_buf = realloc(content_buf, content_buf_used + bytestocopy);
  167. content_buf_len = content_buf_used + bytestocopy;
  168. }
  169. memcpy(content_buf + content_buf_used, header_buf + i, bytestocopy);
  170. content_buf_used += bytestocopy;
  171. chunksize -= bytestocopy;
  172. i += bytestocopy;
  173. }
  174. }
  175. else
  176. {
  177. if(content_buf_len < header_buf_used - i)
  178. {
  179. content_buf = realloc(content_buf, header_buf_used - i);
  180. content_buf_len = header_buf_used - i;
  181. }
  182. memcpy(content_buf, header_buf + i, header_buf_used - i);
  183. content_buf_used = header_buf_used - i;
  184. i = header_buf_used;
  185. }
  186. }
  187. }
  188. }
  189. }
  190. else
  191. {
  192. /* content */
  193. if(chunked)
  194. {
  195. int i = 0;
  196. while(i < n)
  197. {
  198. if(chunksize == 0)
  199. {
  200. /* reading chunk size */
  201. if(i<n && buf[i] == '\r') i++;
  202. if(i<n && buf[i] == '\n') i++;
  203. while(i<n && isxdigit(buf[i]))
  204. {
  205. if(buf[i] >= '0' && buf[i] <= '9')
  206. chunksize = (chunksize << 4) + (buf[i] - '0');
  207. else
  208. chunksize = (chunksize << 4) + ((buf[i] | 32) - 'a' + 10);
  209. i++;
  210. }
  211. while(i<n && buf[i] != '\r') i++; /* discarding chunk-extension */
  212. if(i<n && buf[i] == '\r') i++;
  213. if(i<n && buf[i] == '\n') i++;
  214. #ifdef DEBUG
  215. printf("chunksize = %u (%x)\n", chunksize, chunksize);
  216. #endif
  217. if(chunksize == 0)
  218. {
  219. #ifdef DEBUG
  220. printf("end of HTTP content - %d %d\n", i, n);
  221. /*printf("'%.*s'\n", n-i, buf+i);*/
  222. #endif
  223. goto end_of_stream;
  224. }
  225. }
  226. bytestocopy = ((int)chunksize < n - i)?chunksize:(n - i);
  227. if((int)(content_buf_used + bytestocopy) > content_buf_len)
  228. {
  229. content_buf = (char *)realloc((void *)content_buf,
  230. content_buf_used + bytestocopy);
  231. content_buf_len = content_buf_used + bytestocopy;
  232. }
  233. memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
  234. content_buf_used += bytestocopy;
  235. i += bytestocopy;
  236. chunksize -= bytestocopy;
  237. }
  238. }
  239. else
  240. {
  241. if(content_buf_used + n > content_buf_len)
  242. {
  243. content_buf = (char *)realloc((void *)content_buf,
  244. content_buf_used + n);
  245. content_buf_len = content_buf_used + n;
  246. }
  247. memcpy(content_buf + content_buf_used, buf, n);
  248. content_buf_used += n;
  249. }
  250. }
  251. if(content_length > 0 && content_buf_used >= content_length)
  252. {
  253. #ifdef DEBUG
  254. printf("End of HTTP content\n");
  255. #endif
  256. break;
  257. }
  258. }
  259. end_of_stream:
  260. free(header_buf); header_buf = NULL;
  261. *size = content_buf_used;
  262. if(content_buf_used == 0)
  263. {
  264. free(content_buf);
  265. content_buf = NULL;
  266. }
  267. return content_buf;
  268. }
  269. /* miniwget3() :
  270. * do all the work.
  271. * Return NULL if something failed. */
  272. static void *
  273. miniwget3(const char * url, const char * host,
  274. unsigned short port, const char * path,
  275. int * size, char * addr_str, int addr_str_len, const char * httpversion)
  276. {
  277. char buf[2048];
  278. int s;
  279. int n;
  280. int len;
  281. int sent;
  282. *size = 0;
  283. s = connecthostport(host, port);
  284. if(s < 0)
  285. return NULL;
  286. /* get address for caller ! */
  287. if(addr_str)
  288. {
  289. struct sockaddr saddr;
  290. socklen_t saddrlen;
  291. saddrlen = sizeof(saddr);
  292. if(getsockname(s, &saddr, &saddrlen) < 0)
  293. {
  294. perror("getsockname");
  295. }
  296. else
  297. {
  298. #if defined(__amigaos__) && !defined(__amigaos4__)
  299. /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
  300. * But his function make a string with the port : nn.nn.nn.nn:port */
  301. /* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
  302. NULL, addr_str, (DWORD *)&addr_str_len))
  303. {
  304. printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
  305. }*/
  306. strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
  307. #else
  308. /*inet_ntop(AF_INET, &saddr.sin_addr, addr_str, addr_str_len);*/
  309. n = getnameinfo(&saddr, saddrlen,
  310. addr_str, addr_str_len,
  311. NULL, 0,
  312. NI_NUMERICHOST | NI_NUMERICSERV);
  313. if(n != 0) {
  314. #ifdef WIN32
  315. fprintf(stderr, "getnameinfo() failed : %d\n", n);
  316. #else
  317. fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
  318. #endif
  319. }
  320. #endif
  321. }
  322. #ifdef DEBUG
  323. printf("address miniwget : %s\n", addr_str);
  324. #endif
  325. }
  326. len = snprintf(buf, sizeof(buf),
  327. "GET %s HTTP/%s\r\n"
  328. "Host: %s:%d\r\n"
  329. "Connection: Close\r\n"
  330. "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
  331. "\r\n",
  332. path, httpversion, host, port);
  333. sent = 0;
  334. /* sending the HTTP request */
  335. while(sent < len)
  336. {
  337. n = send(s, buf+sent, len-sent, 0);
  338. if(n < 0)
  339. {
  340. perror("send");
  341. closesocket(s);
  342. return NULL;
  343. }
  344. else
  345. {
  346. sent += n;
  347. }
  348. }
  349. return getHTTPResponse(s, size);
  350. }
  351. /* miniwget2() :
  352. * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
  353. static void *
  354. miniwget2(const char * url, const char * host,
  355. unsigned short port, const char * path,
  356. int * size, char * addr_str, int addr_str_len)
  357. {
  358. char * respbuffer;
  359. respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
  360. /*
  361. respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.0");
  362. if (*size == 0)
  363. {
  364. #ifdef DEBUG
  365. printf("Retrying with HTTP/1.1\n");
  366. #endif
  367. free(respbuffer);
  368. respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
  369. }
  370. */
  371. return respbuffer;
  372. }
  373. /* parseURL()
  374. * arguments :
  375. * url : source string not modified
  376. * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
  377. * port : port (destination)
  378. * path : pointer to the path part of the URL
  379. *
  380. * Return values :
  381. * 0 - Failure
  382. * 1 - Success */
  383. int parseURL(const char * url, char * hostname, unsigned short * port, char * * path)
  384. {
  385. char * p1, *p2, *p3;
  386. p1 = strstr(url, "://");
  387. if(!p1)
  388. return 0;
  389. p1 += 3;
  390. if( (url[0]!='h') || (url[1]!='t')
  391. ||(url[2]!='t') || (url[3]!='p'))
  392. return 0;
  393. p2 = strchr(p1, ':');
  394. p3 = strchr(p1, '/');
  395. if(!p3)
  396. return 0;
  397. memset(hostname, 0, MAXHOSTNAMELEN + 1);
  398. if(!p2 || (p2>p3))
  399. {
  400. strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
  401. *port = 80;
  402. }
  403. else
  404. {
  405. strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
  406. *port = 0;
  407. p2++;
  408. while( (*p2 >= '0') && (*p2 <= '9'))
  409. {
  410. *port *= 10;
  411. *port += (unsigned short)(*p2 - '0');
  412. p2++;
  413. }
  414. }
  415. *path = p3;
  416. return 1;
  417. }
  418. void * miniwget(const char * url, int * size)
  419. {
  420. unsigned short port;
  421. char * path;
  422. /* protocol://host:port/chemin */
  423. char hostname[MAXHOSTNAMELEN+1];
  424. *size = 0;
  425. if(!parseURL(url, hostname, &port, &path))
  426. return NULL;
  427. return miniwget2(url, hostname, port, path, size, 0, 0);
  428. }
  429. void * miniwget_getaddr(const char * url, int * size, char * addr, int addrlen)
  430. {
  431. unsigned short port;
  432. char * path;
  433. /* protocol://host:port/chemin */
  434. char hostname[MAXHOSTNAMELEN+1];
  435. *size = 0;
  436. if(addr)
  437. addr[0] = '\0';
  438. if(!parseURL(url, hostname, &port, &path))
  439. return NULL;
  440. return miniwget2(url, hostname, port, path, size, addr, addrlen);
  441. }
粤ICP备19079148号