PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/upnp/miniwget.c

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