/thirdparty/libportfwd/third-party/miniupnpc-1.6/miniwget.c

http://github.com/tomahawk-player/tomahawk · C · 522 lines · 432 code · 20 blank · 70 comment · 124 complexity · 645073912423576c13cd21e047dc6964 MD5 · raw file

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