config root man

Current Path : /usr/src/usr.sbin/portsnap/phttpget/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //usr/src/usr.sbin/portsnap/phttpget/phttpget.c

/*-
 * Copyright 2005 Colin Percival
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: release/9.1.0/usr.sbin/portsnap/phttpget/phttpget.c 190679 2009-04-03 21:13:18Z cperciva $");

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>

static const char *	env_HTTP_PROXY;
static char *		env_HTTP_PROXY_AUTH;
static const char *	env_HTTP_USER_AGENT;
static char *		env_HTTP_TIMEOUT;
static const char *	proxyport;
static char *		proxyauth;

static struct timeval	timo = { 15, 0};

static void
usage(void)
{

	fprintf(stderr, "usage: phttpget server [file ...]\n");
	exit(EX_USAGE);
}

/*
 * Base64 encode a string; the string returned, if non-NULL, is
 * allocated using malloc() and must be freed by the caller.
 */
static char *
b64enc(const char *ptext)
{
	static const char base64[] =
	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	    "abcdefghijklmnopqrstuvwxyz"
	    "0123456789+/";
	const char *pt;
	char *ctext, *pc;
	size_t ptlen, ctlen;
	uint32_t t;
	unsigned int j;

	/*
	 * Encoded length is 4 characters per 3-byte block or partial
	 * block of plaintext, plus one byte for the terminating NUL
	 */
	ptlen = strlen(ptext);
	if (ptlen > ((SIZE_MAX - 1) / 4) * 3 - 2)
		return NULL;	/* Possible integer overflow */
	ctlen = 4 * ((ptlen + 2) / 3) + 1;
	if ((ctext = malloc(ctlen)) == NULL)
		return NULL;
	ctext[ctlen - 1] = 0;

	/*
	 * Scan through ptext, reading up to 3 bytes from ptext and
	 * writing 4 bytes to ctext, until we run out of input.
	 */
	for (pt = ptext, pc = ctext; ptlen; ptlen -= 3, pc += 4) {
		/* Read 3 bytes */
		for (t = j = 0; j < 3; j++) {
			t <<= 8;
			if (j < ptlen)
				t += *pt++;
		}

		/* Write 4 bytes */
		for (j = 0; j < 4; j++) {
			if (j <= ptlen + 1)
				pc[j] = base64[(t >> 18) & 0x3f];
			else
				pc[j] = '=';
			t <<= 6;
		}

		/* If we're done, exit the loop */
		if (ptlen <= 3)
			break;
	}

	return (ctext);
}

static void
readenv(void)
{
	char *proxy_auth_userpass, *proxy_auth_userpass64, *p;
	char *proxy_auth_user = NULL;
	char *proxy_auth_pass = NULL;
	long http_timeout;

	env_HTTP_PROXY = getenv("HTTP_PROXY");
	if (env_HTTP_PROXY == NULL)
		env_HTTP_PROXY = getenv("http_proxy");
	if (env_HTTP_PROXY != NULL) {
		if (strncmp(env_HTTP_PROXY, "http://", 7) == 0)
			env_HTTP_PROXY += 7;
		p = strchr(env_HTTP_PROXY, '/');
		if (p != NULL)
			*p = 0;
		p = strchr(env_HTTP_PROXY, ':');
		if (p != NULL) {
			*p = 0;
			proxyport = p + 1;
		} else
			proxyport = "3128";
	}

	env_HTTP_PROXY_AUTH = getenv("HTTP_PROXY_AUTH");
	if ((env_HTTP_PROXY != NULL) &&
	    (env_HTTP_PROXY_AUTH != NULL) &&
	    (strncasecmp(env_HTTP_PROXY_AUTH, "basic:" , 6) == 0)) {
		/* Ignore authentication scheme */
		(void) strsep(&env_HTTP_PROXY_AUTH, ":");

		/* Ignore realm */
		(void) strsep(&env_HTTP_PROXY_AUTH, ":");

		/* Obtain username and password */
		proxy_auth_user = strsep(&env_HTTP_PROXY_AUTH, ":");
		proxy_auth_pass = env_HTTP_PROXY_AUTH;
	}

	if ((proxy_auth_user != NULL) && (proxy_auth_pass != NULL)) {
		asprintf(&proxy_auth_userpass, "%s:%s",
		    proxy_auth_user, proxy_auth_pass);
		if (proxy_auth_userpass == NULL)
			err(1, "asprintf");

		proxy_auth_userpass64 = b64enc(proxy_auth_userpass);
		if (proxy_auth_userpass64 == NULL)
			err(1, "malloc");

		asprintf(&proxyauth, "Proxy-Authorization: Basic %s\r\n",
		    proxy_auth_userpass64);
		if (proxyauth == NULL)
			err(1, "asprintf");

		free(proxy_auth_userpass);
		free(proxy_auth_userpass64);
	} else
		proxyauth = NULL;

	env_HTTP_USER_AGENT = getenv("HTTP_USER_AGENT");
	if (env_HTTP_USER_AGENT == NULL)
		env_HTTP_USER_AGENT = "phttpget/0.1";

	env_HTTP_TIMEOUT = getenv("HTTP_TIMEOUT");
	if (env_HTTP_TIMEOUT != NULL) {
		http_timeout = strtol(env_HTTP_TIMEOUT, &p, 10);
		if ((*env_HTTP_TIMEOUT == '\0') || (*p != '\0') ||
		    (http_timeout < 0))
			warnx("HTTP_TIMEOUT (%s) is not a positive integer",
			    env_HTTP_TIMEOUT);
		else
			timo.tv_sec = http_timeout;
	}
}

static int
makerequest(char ** buf, char * path, char * server, int connclose)
{
	int buflen;

	buflen = asprintf(buf,
	    "GET %s%s/%s HTTP/1.1\r\n"
	    "Host: %s\r\n"
	    "User-Agent: %s\r\n"
	    "%s"
	    "%s"
	    "\r\n",
	    env_HTTP_PROXY ? "http://" : "",
	    env_HTTP_PROXY ? server : "",
	    path, server, env_HTTP_USER_AGENT,
	    proxyauth ? proxyauth : "",
	    connclose ? "Connection: Close\r\n" : "Connection: Keep-Alive\r\n");
	if (buflen == -1)
		err(1, "asprintf");
	return(buflen);
}

static int
readln(int sd, char * resbuf, int * resbuflen, int * resbufpos)
{
	ssize_t len;

	while (strnstr(resbuf + *resbufpos, "\r\n",
	    *resbuflen - *resbufpos) == NULL) {
		/* Move buffered data to the start of the buffer */
		if (*resbufpos != 0) {
			memmove(resbuf, resbuf + *resbufpos,
			    *resbuflen - *resbufpos);
			*resbuflen -= *resbufpos;
			*resbufpos = 0;
		}

		/* If the buffer is full, complain */
		if (*resbuflen == BUFSIZ)
			return -1;

		/* Read more data into the buffer */
		len = recv(sd, resbuf + *resbuflen, BUFSIZ - *resbuflen, 0);
		if ((len == 0) ||
		    ((len == -1) && (errno != EINTR)))
			return -1;

		if (len != -1)
			*resbuflen += len;
	}

	return 0;
}

static int
copybytes(int sd, int fd, off_t copylen, char * resbuf, int * resbuflen,
    int * resbufpos)
{
	ssize_t len;

	while (copylen) {
		/* Write data from resbuf to fd */
		len = *resbuflen - *resbufpos;
		if (copylen < len)
			len = copylen;
		if (len > 0) {
			if (fd != -1)
				len = write(fd, resbuf + *resbufpos, len);
			if (len == -1)
				err(1, "write");
			*resbufpos += len;
			copylen -= len;
			continue;
		}

		/* Read more data into buffer */
		len = recv(sd, resbuf, BUFSIZ, 0);
		if (len == -1) {
			if (errno == EINTR)
				continue;
			return -1;
		} else if (len == 0) {
			return -2;
		} else {
			*resbuflen = len;
			*resbufpos = 0;
		}
	}

	return 0;
}

int
main(int argc, char *argv[])
{
	struct addrinfo hints;	/* Hints to getaddrinfo */
	struct addrinfo *res;	/* Pointer to server address being used */
	struct addrinfo *res0;	/* Pointer to server addresses */
	char * resbuf = NULL;	/* Response buffer */
	int resbufpos = 0;	/* Response buffer position */
	int resbuflen = 0;	/* Response buffer length */
	char * eolp;		/* Pointer to "\r\n" within resbuf */
	char * hln;		/* Pointer within header line */
	char * servername;	/* Name of server */
	char * fname = NULL;	/* Name of downloaded file */
	char * reqbuf = NULL;	/* Request buffer */
	int reqbufpos = 0;	/* Request buffer position */
	int reqbuflen = 0;	/* Request buffer length */
	ssize_t len;		/* Length sent or received */
	int nreq = 0;		/* Number of next request to send */
	int nres = 0;		/* Number of next reply to receive */
	int pipelined = 0;	/* != 0 if connection in pipelined mode. */
	int keepalive;		/* != 0 if HTTP/1.0 keep-alive rcvd. */
	int sd = -1;		/* Socket descriptor */
	int sdflags = 0;	/* Flags on the socket sd */
	int fd = -1;		/* Descriptor for download target file */
	int error;		/* Error code */
	int statuscode;		/* HTTP Status code */
	off_t contentlength;	/* Value from Content-Length header */
	int chunked;		/* != if transfer-encoding is chunked */
	off_t clen;		/* Chunk length */
	int firstreq = 0;	/* # of first request for this connection */
	int val;		/* Value used for setsockopt call */

	/* Check that the arguments are sensible */
	if (argc < 2)
		usage();

	/* Read important environment variables */
	readenv();

	/* Get server name and adjust arg[cv] to point at file names */
	servername = argv[1];
	argv += 2;
	argc -= 2;

	/* Allocate response buffer */
	resbuf = malloc(BUFSIZ);
	if (resbuf == NULL)
		err(1, "malloc");

	/* Look up server */
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	error = getaddrinfo(env_HTTP_PROXY ? env_HTTP_PROXY : servername,
	    env_HTTP_PROXY ? proxyport : "http", &hints, &res0);
	if (error)
		errx(1, "host = %s, port = %s: %s",
		    env_HTTP_PROXY ? env_HTTP_PROXY : servername,
		    env_HTTP_PROXY ? proxyport : "http",
		    gai_strerror(error));
	if (res0 == NULL)
		errx(1, "could not look up %s", servername);
	res = res0;

	/* Do the fetching */
	while (nres < argc) {
		/* Make sure we have a connected socket */
		for (; sd == -1; res = res->ai_next) {
			/* No addresses left to try :-( */
			if (res == NULL)
				errx(1, "Could not connect to %s", servername);

			/* Create a socket... */
			sd = socket(res->ai_family, res->ai_socktype,
			    res->ai_protocol);
			if (sd == -1)
				continue;

			/* ... set 15-second timeouts ... */
			setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO,
			    (void *)&timo, (socklen_t)sizeof(timo));
			setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
			    (void *)&timo, (socklen_t)sizeof(timo));

			/* ... disable SIGPIPE generation ... */
			val = 1;
			setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE,
			    (void *)&val, sizeof(int));

			/* ... and connect to the server. */
			if(connect(sd, res->ai_addr, res->ai_addrlen)) {
				close(sd);
				sd = -1;
				continue;
			}

			firstreq = nres;
		}

		/*
		 * If in pipelined HTTP mode, put socket into non-blocking
		 * mode, since we're probably going to want to try to send
		 * several HTTP requests.
		 */
		if (pipelined) {
			sdflags = fcntl(sd, F_GETFL);
			if (fcntl(sd, F_SETFL, sdflags | O_NONBLOCK) == -1)
				err(1, "fcntl");
		}

		/* Construct requests and/or send them without blocking */
		while ((nreq < argc) && ((reqbuf == NULL) || pipelined)) {
			/* If not in the middle of a request, make one */
			if (reqbuf == NULL) {
				reqbuflen = makerequest(&reqbuf, argv[nreq],
				    servername, (nreq == argc - 1));
				reqbufpos = 0;
			}

			/* If in pipelined mode, try to send the request */
			if (pipelined) {
				while (reqbufpos < reqbuflen) {
					len = send(sd, reqbuf + reqbufpos,
					    reqbuflen - reqbufpos, 0);
					if (len == -1)
						break;
					reqbufpos += len;
				}
				if (reqbufpos < reqbuflen) {
					if (errno != EAGAIN)
						goto conndied;
					break;
				} else {
					free(reqbuf);
					reqbuf = NULL;
					nreq++;
				}
			}
		}

		/* Put connection back into blocking mode */
		if (pipelined) {
			if (fcntl(sd, F_SETFL, sdflags) == -1)
				err(1, "fcntl");
		}

		/* Do we need to blocking-send a request? */
		if (nres == nreq) {
			while (reqbufpos < reqbuflen) {
				len = send(sd, reqbuf + reqbufpos,
				    reqbuflen - reqbufpos, 0);
				if (len == -1)
					goto conndied;
				reqbufpos += len;
			}
			free(reqbuf);
			reqbuf = NULL;
			nreq++;
		}

		/* Scan through the response processing headers. */
		statuscode = 0;
		contentlength = -1;
		chunked = 0;
		keepalive = 0;
		do {
			/* Get a header line */
			error = readln(sd, resbuf, &resbuflen, &resbufpos);
			if (error)
				goto conndied;
			hln = resbuf + resbufpos;
			eolp = strnstr(hln, "\r\n", resbuflen - resbufpos);
			resbufpos = (eolp - resbuf) + 2;
			*eolp = '\0';

			/* Make sure it doesn't contain a NUL character */
			if (strchr(hln, '\0') != eolp)
				goto conndied;

			if (statuscode == 0) {
				/* The first line MUST be HTTP/1.x xxx ... */
				if ((strncmp(hln, "HTTP/1.", 7) != 0) ||
				    ! isdigit(hln[7]))
					goto conndied;

				/*
				 * If the minor version number isn't zero,
				 * then we can assume that pipelining our
				 * requests is OK -- as long as we don't
				 * see a "Connection: close" line later
				 * and we either have a Content-Length or
				 * Transfer-Encoding: chunked header to
				 * tell us the length.
				 */
				if (hln[7] != '0')
					pipelined = 1;

				/* Skip over the minor version number */
				hln = strchr(hln + 7, ' ');
				if (hln == NULL)
					goto conndied;
				else
					hln++;

				/* Read the status code */
				while (isdigit(*hln)) {
					statuscode = statuscode * 10 +
					    *hln - '0';
					hln++;
				}

				if (statuscode < 100 || statuscode > 599)
					goto conndied;

				/* Ignore the rest of the line */
				continue;
			}

			/*
			 * Check for "Connection: close" or
			 * "Connection: Keep-Alive" header
			 */
			if (strncasecmp(hln, "Connection:", 11) == 0) {
				hln += 11;
				if (strcasestr(hln, "close") != NULL)
					pipelined = 0;
				if (strcasestr(hln, "Keep-Alive") != NULL)
					keepalive = 1;

				/* Next header... */
				continue;
			}

			/* Check for "Content-Length:" header */
			if (strncasecmp(hln, "Content-Length:", 15) == 0) {
				hln += 15;
				contentlength = 0;

				/* Find the start of the length */
				while (!isdigit(*hln) && (*hln != '\0'))
					hln++;

				/* Compute the length */
				while (isdigit(*hln)) {
					if (contentlength >= OFF_MAX / 10) {
						/* Nasty people... */
						goto conndied;
					}
					contentlength = contentlength * 10 +
					    *hln - '0';
					hln++;
				}

				/* Next header... */
				continue;
			}

			/* Check for "Transfer-Encoding: chunked" header */
			if (strncasecmp(hln, "Transfer-Encoding:", 18) == 0) {
				hln += 18;
				if (strcasestr(hln, "chunked") != NULL)
					chunked = 1;

				/* Next header... */
				continue;
			}

			/* We blithely ignore any other header lines */

			/* No more header lines */
			if (strlen(hln) == 0) {
				/*
				 * If the status code was 1xx, then there will
				 * be a real header later.  Servers may emit
				 * 1xx header blocks at will, but since we
				 * don't expect one, we should just ignore it.
				 */
				if (100 <= statuscode && statuscode <= 199) {
					statuscode = 0;
					continue;
				}

				/* End of header; message body follows */
				break;
			}
		} while (1);

		/* No message body for 204 or 304 */
		if (statuscode == 204 || statuscode == 304) {
			nres++;
			continue;
		}

		/*
		 * There should be a message body coming, but we only want
		 * to send it to a file if the status code is 200
		 */
		if (statuscode == 200) {
			/* Generate a file name for the download */
			fname = strrchr(argv[nres], '/');
			if (fname == NULL)
				fname = argv[nres];
			else
				fname++;
			if (strlen(fname) == 0)
				errx(1, "Cannot obtain file name from %s\n",
				    argv[nres]);

			fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
			if (fd == -1)
				errx(1, "open(%s)", fname);
		};

		/* Read the message and send data to fd if appropriate */
		if (chunked) {
			/* Handle a chunked-encoded entity */

			/* Read chunks */
			do {
				error = readln(sd, resbuf, &resbuflen,
				    &resbufpos);
				if (error)
					goto conndied;
				hln = resbuf + resbufpos;
				eolp = strstr(hln, "\r\n");
				resbufpos = (eolp - resbuf) + 2;

				clen = 0;
				while (isxdigit(*hln)) {
					if (clen >= OFF_MAX / 16) {
						/* Nasty people... */
						goto conndied;
					}
					if (isdigit(*hln))
						clen = clen * 16 + *hln - '0';
					else
						clen = clen * 16 + 10 +
						    tolower(*hln) - 'a';
					hln++;
				}

				error = copybytes(sd, fd, clen, resbuf,
				    &resbuflen, &resbufpos);
				if (error) {
					goto conndied;
				}
			} while (clen != 0);

			/* Read trailer and final CRLF */
			do {
				error = readln(sd, resbuf, &resbuflen,
				    &resbufpos);
				if (error)
					goto conndied;
				hln = resbuf + resbufpos;
				eolp = strstr(hln, "\r\n");
				resbufpos = (eolp - resbuf) + 2;
			} while (hln != eolp);
		} else if (contentlength != -1) {
			error = copybytes(sd, fd, contentlength, resbuf,
			    &resbuflen, &resbufpos);
			if (error)
				goto conndied;
		} else {
			/*
			 * Not chunked, and no content length header.
			 * Read everything until the server closes the
			 * socket.
			 */
			error = copybytes(sd, fd, OFF_MAX, resbuf,
			    &resbuflen, &resbufpos);
			if (error == -1)
				goto conndied;
			pipelined = 0;
		}

		if (fd != -1) {
			close(fd);
			fd = -1;
		}

		fprintf(stderr, "http://%s/%s: %d ", servername, argv[nres],
		    statuscode);
		if (statuscode == 200)
			fprintf(stderr, "OK\n");
		else if (statuscode < 300)
			fprintf(stderr, "Successful (ignored)\n");
		else if (statuscode < 400)
			fprintf(stderr, "Redirection (ignored)\n");
		else
			fprintf(stderr, "Error (ignored)\n");

		/* We've finished this file! */
		nres++;

		/*
		 * If necessary, clean up this connection so that we
		 * can start a new one.
		 */
		if (pipelined == 0 && keepalive == 0)
			goto cleanupconn;
		continue;

conndied:
		/*
		 * Something went wrong -- our connection died, the server
		 * sent us garbage, etc.  If this happened on the first
		 * request we sent over this connection, give up.  Otherwise,
		 * close this connection, open a new one, and reissue the
		 * request.
		 */
		if (nres == firstreq)
			errx(1, "Connection failure");

cleanupconn:
		/*
		 * Clean up our connection and keep on going
		 */
		shutdown(sd, SHUT_RDWR);
		close(sd);
		sd = -1;
		if (fd != -1) {
			close(fd);
			fd = -1;
		}
		if (reqbuf != NULL) {
			free(reqbuf);
			reqbuf = NULL;
		}
		nreq = nres;
		res = res0;
		pipelined = 0;
		resbufpos = resbuflen = 0;
		continue;
	}

	free(resbuf);
	freeaddrinfo(res0);

	return 0;
}

Man Man