phttpget.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. /*-
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright 2005 Colin Percival
  5. * All rights reserved
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted providing that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  17. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  24. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
  25. * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  26. * POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. #include <sys/types.h>
  29. #include <sys/time.h>
  30. #include <sys/socket.h>
  31. #include <ctype.h>
  32. #include <err.h>
  33. #include <errno.h>
  34. #include <fcntl.h>
  35. #include <limits.h>
  36. #include <netdb.h>
  37. #include <stdint.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <sysexits.h>
  42. #include <unistd.h>
  43. static const char * env_HTTP_PROXY;
  44. static char * env_HTTP_PROXY_AUTH;
  45. static const char * env_HTTP_USER_AGENT;
  46. static char * env_HTTP_TIMEOUT;
  47. static const char * proxyport;
  48. static char * proxyauth;
  49. static struct timeval timo = { 15, 0};
  50. static void
  51. usage(void)
  52. {
  53. fprintf(stderr, "usage: phttpget server [file ...]\n");
  54. exit(EX_USAGE);
  55. }
  56. /*
  57. * Base64 encode a string; the string returned, if non-NULL, is
  58. * allocated using malloc() and must be freed by the caller.
  59. */
  60. static char *
  61. b64enc(const char *ptext)
  62. {
  63. static const char base64[] =
  64. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  65. "abcdefghijklmnopqrstuvwxyz"
  66. "0123456789+/";
  67. const char *pt;
  68. char *ctext, *pc;
  69. size_t ptlen, ctlen;
  70. uint32_t t;
  71. unsigned int j;
  72. /*
  73. * Encoded length is 4 characters per 3-byte block or partial
  74. * block of plaintext, plus one byte for the terminating NUL
  75. */
  76. ptlen = strlen(ptext);
  77. if (ptlen > ((SIZE_MAX - 1) / 4) * 3 - 2)
  78. return NULL; /* Possible integer overflow */
  79. ctlen = 4 * ((ptlen + 2) / 3) + 1;
  80. if ((ctext = malloc(ctlen)) == NULL)
  81. return NULL;
  82. ctext[ctlen - 1] = 0;
  83. /*
  84. * Scan through ptext, reading up to 3 bytes from ptext and
  85. * writing 4 bytes to ctext, until we run out of input.
  86. */
  87. for (pt = ptext, pc = ctext; ptlen; ptlen -= 3, pc += 4) {
  88. /* Read 3 bytes */
  89. for (t = j = 0; j < 3; j++) {
  90. t <<= 8;
  91. if (j < ptlen)
  92. t += *pt++;
  93. }
  94. /* Write 4 bytes */
  95. for (j = 0; j < 4; j++) {
  96. if (j <= ptlen + 1)
  97. pc[j] = base64[(t >> 18) & 0x3f];
  98. else
  99. pc[j] = '=';
  100. t <<= 6;
  101. }
  102. /* If we're done, exit the loop */
  103. if (ptlen <= 3)
  104. break;
  105. }
  106. return (ctext);
  107. }
  108. static void
  109. readenv(void)
  110. {
  111. char *proxy_auth_userpass, *proxy_auth_userpass64, *p;
  112. char *proxy_auth_user = NULL;
  113. char *proxy_auth_pass = NULL;
  114. long http_timeout;
  115. env_HTTP_PROXY = getenv("HTTP_PROXY");
  116. if (env_HTTP_PROXY == NULL)
  117. env_HTTP_PROXY = getenv("http_proxy");
  118. if (env_HTTP_PROXY != NULL) {
  119. if (strncmp(env_HTTP_PROXY, "http://", 7) == 0)
  120. env_HTTP_PROXY += 7;
  121. p = strchr(env_HTTP_PROXY, '/');
  122. if (p != NULL)
  123. *p = 0;
  124. p = strchr(env_HTTP_PROXY, ':');
  125. if (p != NULL) {
  126. *p = 0;
  127. proxyport = p + 1;
  128. } else
  129. proxyport = "3128";
  130. }
  131. env_HTTP_PROXY_AUTH = getenv("HTTP_PROXY_AUTH");
  132. if ((env_HTTP_PROXY != NULL) &&
  133. (env_HTTP_PROXY_AUTH != NULL) &&
  134. (strncasecmp(env_HTTP_PROXY_AUTH, "basic:" , 6) == 0)) {
  135. /* Ignore authentication scheme */
  136. (void) strsep(&env_HTTP_PROXY_AUTH, ":");
  137. /* Ignore realm */
  138. (void) strsep(&env_HTTP_PROXY_AUTH, ":");
  139. /* Obtain username and password */
  140. proxy_auth_user = strsep(&env_HTTP_PROXY_AUTH, ":");
  141. proxy_auth_pass = env_HTTP_PROXY_AUTH;
  142. }
  143. if ((proxy_auth_user != NULL) && (proxy_auth_pass != NULL)) {
  144. asprintf(&proxy_auth_userpass, "%s:%s",
  145. proxy_auth_user, proxy_auth_pass);
  146. if (proxy_auth_userpass == NULL)
  147. err(1, "asprintf");
  148. proxy_auth_userpass64 = b64enc(proxy_auth_userpass);
  149. if (proxy_auth_userpass64 == NULL)
  150. err(1, "malloc");
  151. asprintf(&proxyauth, "Proxy-Authorization: Basic %s\r\n",
  152. proxy_auth_userpass64);
  153. if (proxyauth == NULL)
  154. err(1, "asprintf");
  155. free(proxy_auth_userpass);
  156. free(proxy_auth_userpass64);
  157. } else
  158. proxyauth = NULL;
  159. env_HTTP_USER_AGENT = getenv("HTTP_USER_AGENT");
  160. if (env_HTTP_USER_AGENT == NULL)
  161. env_HTTP_USER_AGENT = "phttpget/0.1";
  162. env_HTTP_TIMEOUT = getenv("HTTP_TIMEOUT");
  163. if (env_HTTP_TIMEOUT != NULL) {
  164. http_timeout = strtol(env_HTTP_TIMEOUT, &p, 10);
  165. if ((*env_HTTP_TIMEOUT == '\0') || (*p != '\0') ||
  166. (http_timeout < 0))
  167. warnx("HTTP_TIMEOUT (%s) is not a positive integer",
  168. env_HTTP_TIMEOUT);
  169. else
  170. timo.tv_sec = http_timeout;
  171. }
  172. }
  173. static int
  174. makerequest(char ** buf, char * path, char * server, int connclose)
  175. {
  176. int buflen;
  177. buflen = asprintf(buf,
  178. "GET %s%s/%s HTTP/1.1\r\n"
  179. "Host: %s\r\n"
  180. "User-Agent: %s\r\n"
  181. "%s"
  182. "%s"
  183. "\r\n",
  184. env_HTTP_PROXY ? "http://" : "",
  185. env_HTTP_PROXY ? server : "",
  186. path, server, env_HTTP_USER_AGENT,
  187. proxyauth ? proxyauth : "",
  188. connclose ? "Connection: Close\r\n" : "Connection: Keep-Alive\r\n");
  189. if (buflen == -1)
  190. err(1, "asprintf");
  191. return(buflen);
  192. }
  193. static int
  194. readln(int sd, char * resbuf, int * resbuflen, int * resbufpos)
  195. {
  196. ssize_t len;
  197. while (strnstr(resbuf + *resbufpos, "\r\n",
  198. *resbuflen - *resbufpos) == NULL) {
  199. /* Move buffered data to the start of the buffer */
  200. if (*resbufpos != 0) {
  201. memmove(resbuf, resbuf + *resbufpos,
  202. *resbuflen - *resbufpos);
  203. *resbuflen -= *resbufpos;
  204. *resbufpos = 0;
  205. }
  206. /* If the buffer is full, complain */
  207. if (*resbuflen == BUFSIZ)
  208. return -1;
  209. /* Read more data into the buffer */
  210. len = recv(sd, resbuf + *resbuflen, BUFSIZ - *resbuflen, 0);
  211. if ((len == 0) ||
  212. ((len == -1) && (errno != EINTR)))
  213. return -1;
  214. if (len != -1)
  215. *resbuflen += len;
  216. }
  217. return 0;
  218. }
  219. static int
  220. copybytes(int sd, int fd, off_t copylen, char * resbuf, int * resbuflen,
  221. int * resbufpos)
  222. {
  223. ssize_t len;
  224. while (copylen) {
  225. /* Write data from resbuf to fd */
  226. len = *resbuflen - *resbufpos;
  227. if (copylen < len)
  228. len = copylen;
  229. if (len > 0) {
  230. if (fd != -1)
  231. len = write(fd, resbuf + *resbufpos, len);
  232. if (len == -1)
  233. err(1, "write");
  234. *resbufpos += len;
  235. copylen -= len;
  236. continue;
  237. }
  238. /* Read more data into buffer */
  239. len = recv(sd, resbuf, BUFSIZ, 0);
  240. if (len == -1) {
  241. if (errno == EINTR)
  242. continue;
  243. return -1;
  244. } else if (len == 0) {
  245. return -2;
  246. } else {
  247. *resbuflen = len;
  248. *resbufpos = 0;
  249. }
  250. }
  251. return 0;
  252. }
  253. int
  254. main(int argc, char *argv[])
  255. {
  256. struct addrinfo hints; /* Hints to getaddrinfo */
  257. struct addrinfo *res; /* Pointer to server address being used */
  258. struct addrinfo *res0; /* Pointer to server addresses */
  259. char * resbuf = NULL; /* Response buffer */
  260. int resbufpos = 0; /* Response buffer position */
  261. int resbuflen = 0; /* Response buffer length */
  262. char * eolp; /* Pointer to "\r\n" within resbuf */
  263. char * hln; /* Pointer within header line */
  264. char * servername; /* Name of server */
  265. char * fname = NULL; /* Name of downloaded file */
  266. char * reqbuf = NULL; /* Request buffer */
  267. int reqbufpos = 0; /* Request buffer position */
  268. int reqbuflen = 0; /* Request buffer length */
  269. ssize_t len; /* Length sent or received */
  270. int nreq = 0; /* Number of next request to send */
  271. int nres = 0; /* Number of next reply to receive */
  272. int pipelined = 0; /* != 0 if connection in pipelined mode. */
  273. int keepalive; /* != 0 if HTTP/1.0 keep-alive rcvd. */
  274. int sd = -1; /* Socket descriptor */
  275. int sdflags = 0; /* Flags on the socket sd */
  276. int fd = -1; /* Descriptor for download target file */
  277. int error; /* Error code */
  278. int statuscode; /* HTTP Status code */
  279. off_t contentlength; /* Value from Content-Length header */
  280. int chunked; /* != if transfer-encoding is chunked */
  281. off_t clen; /* Chunk length */
  282. int firstreq = 0; /* # of first request for this connection */
  283. int val; /* Value used for setsockopt call */
  284. /* Check that the arguments are sensible */
  285. if (argc < 2)
  286. usage();
  287. /* Read important environment variables */
  288. readenv();
  289. /* Get server name and adjust arg[cv] to point at file names */
  290. servername = argv[1];
  291. argv += 2;
  292. argc -= 2;
  293. /* Allocate response buffer */
  294. resbuf = malloc(BUFSIZ);
  295. if (resbuf == NULL)
  296. err(1, "malloc");
  297. /* Look up server */
  298. memset(&hints, 0, sizeof(hints));
  299. hints.ai_family = PF_UNSPEC;
  300. hints.ai_socktype = SOCK_STREAM;
  301. error = getaddrinfo(env_HTTP_PROXY ? env_HTTP_PROXY : servername,
  302. env_HTTP_PROXY ? proxyport : "http", &hints, &res0);
  303. if (error)
  304. errx(1, "host = %s, port = %s: %s",
  305. env_HTTP_PROXY ? env_HTTP_PROXY : servername,
  306. env_HTTP_PROXY ? proxyport : "http",
  307. gai_strerror(error));
  308. if (res0 == NULL)
  309. errx(1, "could not look up %s", servername);
  310. res = res0;
  311. /* Do the fetching */
  312. while (nres < argc) {
  313. /* Make sure we have a connected socket */
  314. for (; sd == -1; res = res->ai_next) {
  315. /* No addresses left to try :-( */
  316. if (res == NULL)
  317. errx(1, "Could not connect to %s", servername);
  318. /* Create a socket... */
  319. sd = socket(res->ai_family, res->ai_socktype,
  320. res->ai_protocol);
  321. if (sd == -1)
  322. continue;
  323. /* ... set 15-second timeouts ... */
  324. setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO,
  325. (void *)&timo, (socklen_t)sizeof(timo));
  326. setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
  327. (void *)&timo, (socklen_t)sizeof(timo));
  328. /* ... disable SIGPIPE generation ... */
  329. val = 1;
  330. setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE,
  331. (void *)&val, sizeof(int));
  332. /* ... and connect to the server. */
  333. if(connect(sd, res->ai_addr, res->ai_addrlen)) {
  334. close(sd);
  335. sd = -1;
  336. continue;
  337. }
  338. firstreq = nres;
  339. }
  340. /*
  341. * If in pipelined HTTP mode, put socket into non-blocking
  342. * mode, since we're probably going to want to try to send
  343. * several HTTP requests.
  344. */
  345. if (pipelined) {
  346. sdflags = fcntl(sd, F_GETFL);
  347. if (fcntl(sd, F_SETFL, sdflags | O_NONBLOCK) == -1)
  348. err(1, "fcntl");
  349. }
  350. /* Construct requests and/or send them without blocking */
  351. while ((nreq < argc) && ((reqbuf == NULL) || pipelined)) {
  352. /* If not in the middle of a request, make one */
  353. if (reqbuf == NULL) {
  354. reqbuflen = makerequest(&reqbuf, argv[nreq],
  355. servername, (nreq == argc - 1));
  356. reqbufpos = 0;
  357. }
  358. /* If in pipelined mode, try to send the request */
  359. if (pipelined) {
  360. while (reqbufpos < reqbuflen) {
  361. len = send(sd, reqbuf + reqbufpos,
  362. reqbuflen - reqbufpos, 0);
  363. if (len == -1)
  364. break;
  365. reqbufpos += len;
  366. }
  367. if (reqbufpos < reqbuflen) {
  368. if (errno != EAGAIN)
  369. goto conndied;
  370. break;
  371. } else {
  372. free(reqbuf);
  373. reqbuf = NULL;
  374. nreq++;
  375. }
  376. }
  377. }
  378. /* Put connection back into blocking mode */
  379. if (pipelined) {
  380. if (fcntl(sd, F_SETFL, sdflags) == -1)
  381. err(1, "fcntl");
  382. }
  383. /* Do we need to blocking-send a request? */
  384. if (nres == nreq) {
  385. while (reqbufpos < reqbuflen) {
  386. len = send(sd, reqbuf + reqbufpos,
  387. reqbuflen - reqbufpos, 0);
  388. if (len == -1)
  389. goto conndied;
  390. reqbufpos += len;
  391. }
  392. free(reqbuf);
  393. reqbuf = NULL;
  394. nreq++;
  395. }
  396. /* Scan through the response processing headers. */
  397. statuscode = 0;
  398. contentlength = -1;
  399. chunked = 0;
  400. keepalive = 0;
  401. do {
  402. /* Get a header line */
  403. error = readln(sd, resbuf, &resbuflen, &resbufpos);
  404. if (error)
  405. goto conndied;
  406. hln = resbuf + resbufpos;
  407. eolp = strnstr(hln, "\r\n", resbuflen - resbufpos);
  408. resbufpos = (eolp - resbuf) + 2;
  409. *eolp = '\0';
  410. /* Make sure it doesn't contain a NUL character */
  411. if (strchr(hln, '\0') != eolp)
  412. goto conndied;
  413. if (statuscode == 0) {
  414. /* The first line MUST be HTTP/1.x xxx ... */
  415. if ((strncmp(hln, "HTTP/1.", 7) != 0) ||
  416. ! isdigit(hln[7]))
  417. goto conndied;
  418. /*
  419. * If the minor version number isn't zero,
  420. * then we can assume that pipelining our
  421. * requests is OK -- as long as we don't
  422. * see a "Connection: close" line later
  423. * and we either have a Content-Length or
  424. * Transfer-Encoding: chunked header to
  425. * tell us the length.
  426. */
  427. if (hln[7] != '0')
  428. pipelined = 1;
  429. /* Skip over the minor version number */
  430. hln = strchr(hln + 7, ' ');
  431. if (hln == NULL)
  432. goto conndied;
  433. else
  434. hln++;
  435. /* Read the status code */
  436. while (isdigit(*hln)) {
  437. statuscode = statuscode * 10 +
  438. *hln - '0';
  439. hln++;
  440. }
  441. if (statuscode < 100 || statuscode > 599)
  442. goto conndied;
  443. /* Ignore the rest of the line */
  444. continue;
  445. }
  446. /*
  447. * Check for "Connection: close" or
  448. * "Connection: Keep-Alive" header
  449. */
  450. if (strncasecmp(hln, "Connection:", 11) == 0) {
  451. hln += 11;
  452. if (strcasestr(hln, "close") != NULL)
  453. pipelined = 0;
  454. if (strcasestr(hln, "Keep-Alive") != NULL)
  455. keepalive = 1;
  456. /* Next header... */
  457. continue;
  458. }
  459. /* Check for "Content-Length:" header */
  460. if (strncasecmp(hln, "Content-Length:", 15) == 0) {
  461. hln += 15;
  462. contentlength = 0;
  463. /* Find the start of the length */
  464. while (!isdigit(*hln) && (*hln != '\0'))
  465. hln++;
  466. /* Compute the length */
  467. while (isdigit(*hln)) {
  468. if (contentlength >= OFF_MAX / 10) {
  469. /* Nasty people... */
  470. goto conndied;
  471. }
  472. contentlength = contentlength * 10 +
  473. *hln - '0';
  474. hln++;
  475. }
  476. /* Next header... */
  477. continue;
  478. }
  479. /* Check for "Transfer-Encoding: chunked" header */
  480. if (strncasecmp(hln, "Transfer-Encoding:", 18) == 0) {
  481. hln += 18;
  482. if (strcasestr(hln, "chunked") != NULL)
  483. chunked = 1;
  484. /* Next header... */
  485. continue;
  486. }
  487. /* We blithely ignore any other header lines */
  488. /* No more header lines */
  489. if (strlen(hln) == 0) {
  490. /*
  491. * If the status code was 1xx, then there will
  492. * be a real header later. Servers may emit
  493. * 1xx header blocks at will, but since we
  494. * don't expect one, we should just ignore it.
  495. */
  496. if (100 <= statuscode && statuscode <= 199) {
  497. statuscode = 0;
  498. continue;
  499. }
  500. /* End of header; message body follows */
  501. break;
  502. }
  503. } while (1);
  504. /* No message body for 204 or 304 */
  505. if (statuscode == 204 || statuscode == 304) {
  506. nres++;
  507. continue;
  508. }
  509. /*
  510. * There should be a message body coming, but we only want
  511. * to send it to a file if the status code is 200
  512. */
  513. if (statuscode == 200) {
  514. /* Generate a file name for the download */
  515. fname = strrchr(argv[nres], '/');
  516. if (fname == NULL)
  517. fname = argv[nres];
  518. else
  519. fname++;
  520. if (strlen(fname) == 0)
  521. errx(1, "Cannot obtain file name from %s\n",
  522. argv[nres]);
  523. fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
  524. if (fd == -1)
  525. errx(1, "open(%s)", fname);
  526. }
  527. /* Read the message and send data to fd if appropriate */
  528. if (chunked) {
  529. /* Handle a chunked-encoded entity */
  530. /* Read chunks */
  531. do {
  532. error = readln(sd, resbuf, &resbuflen,
  533. &resbufpos);
  534. if (error)
  535. goto conndied;
  536. hln = resbuf + resbufpos;
  537. eolp = strstr(hln, "\r\n");
  538. resbufpos = (eolp - resbuf) + 2;
  539. clen = 0;
  540. while (isxdigit(*hln)) {
  541. if (clen >= OFF_MAX / 16) {
  542. /* Nasty people... */
  543. goto conndied;
  544. }
  545. if (isdigit(*hln))
  546. clen = clen * 16 + *hln - '0';
  547. else
  548. clen = clen * 16 + 10 +
  549. tolower(*hln) - 'a';
  550. hln++;
  551. }
  552. error = copybytes(sd, fd, clen, resbuf,
  553. &resbuflen, &resbufpos);
  554. if (error) {
  555. goto conndied;
  556. }
  557. } while (clen != 0);
  558. /* Read trailer and final CRLF */
  559. do {
  560. error = readln(sd, resbuf, &resbuflen,
  561. &resbufpos);
  562. if (error)
  563. goto conndied;
  564. hln = resbuf + resbufpos;
  565. eolp = strstr(hln, "\r\n");
  566. resbufpos = (eolp - resbuf) + 2;
  567. } while (hln != eolp);
  568. } else if (contentlength != -1) {
  569. error = copybytes(sd, fd, contentlength, resbuf,
  570. &resbuflen, &resbufpos);
  571. if (error)
  572. goto conndied;
  573. } else {
  574. /*
  575. * Not chunked, and no content length header.
  576. * Read everything until the server closes the
  577. * socket.
  578. */
  579. error = copybytes(sd, fd, OFF_MAX, resbuf,
  580. &resbuflen, &resbufpos);
  581. if (error == -1)
  582. goto conndied;
  583. pipelined = 0;
  584. }
  585. if (fd != -1) {
  586. close(fd);
  587. fd = -1;
  588. }
  589. fprintf(stderr, "http://%s/%s: %d ", servername, argv[nres],
  590. statuscode);
  591. if (statuscode == 200)
  592. fprintf(stderr, "OK\n");
  593. else if (statuscode < 300)
  594. fprintf(stderr, "Successful (ignored)\n");
  595. else if (statuscode < 400)
  596. fprintf(stderr, "Redirection (ignored)\n");
  597. else
  598. fprintf(stderr, "Error (ignored)\n");
  599. /* We've finished this file! */
  600. nres++;
  601. /*
  602. * If necessary, clean up this connection so that we
  603. * can start a new one.
  604. */
  605. if (pipelined == 0 && keepalive == 0)
  606. goto cleanupconn;
  607. continue;
  608. conndied:
  609. /*
  610. * Something went wrong -- our connection died, the server
  611. * sent us garbage, etc. If this happened on the first
  612. * request we sent over this connection, give up. Otherwise,
  613. * close this connection, open a new one, and reissue the
  614. * request.
  615. */
  616. if (nres == firstreq)
  617. errx(1, "Connection failure");
  618. cleanupconn:
  619. /*
  620. * Clean up our connection and keep on going
  621. */
  622. shutdown(sd, SHUT_RDWR);
  623. close(sd);
  624. sd = -1;
  625. if (fd != -1) {
  626. close(fd);
  627. fd = -1;
  628. }
  629. if (reqbuf != NULL) {
  630. free(reqbuf);
  631. reqbuf = NULL;
  632. }
  633. nreq = nres;
  634. res = res0;
  635. pipelined = 0;
  636. resbufpos = resbuflen = 0;
  637. continue;
  638. }
  639. free(resbuf);
  640. freeaddrinfo(res0);
  641. return 0;
  642. }