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