--- /dev/null	Fri May 30 18:31:12 2014
+++ new/usr/src/common/net/wanboot/boot_http.c	Fri May 30 18:31:12 2014
@@ -0,0 +1,2929 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <netdb.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/pkcs12.h>
+
+/* this must be included after ssl.h to avoid re-defining 'offsetof' */
+#include <sys/sysmacros.h>
+
+#include <boot_http.h>
+#include <socket_inet.h>
+#include <p12access.h>
+
+#include "bootlog.h"
+
+#define	BOOT_HTTP_MAJOR_VERSION	1
+#define	BOOT_HTTP_MINOR_VERSION	0
+#define	BOOT_HTTP_MICRO_VERSION	0
+
+static boot_http_ver_t boot_http_ver = {
+	BOOT_HTTP_MAJOR_VERSION,
+	BOOT_HTTP_MINOR_VERSION,
+	BOOT_HTTP_MICRO_VERSION
+};
+
+static int	early_err;	/* Error from before error occurred */
+
+static boolean_t verbosemode = B_FALSE;
+static char	*cipher_list = NULL; /* Ciphers supported (if not default) */
+
+typedef struct {
+	int	i;		/* current position in buffer */
+	int	n;		/* number of bytes in buffer */
+	char	buf[512];	/* buffer */
+} buf_struct_t;
+
+typedef struct {
+	uint_t	errsrc;		/* Source of this error */
+	ulong_t	error;		/* Which error? */
+} errent_t;
+
+
+typedef enum {
+	HTTP_REQ_TYPE_HEAD = 1,
+	HTTP_REQ_TYPE_GET
+} http_req_t;
+
+#define	FAILSAFE 20		/* Max # empty lines to accept */
+#define	DEFAULT_TIMEOUT	10	/* Default socket read timeout value */
+#define	HTTP_CONN_INFO 0x90919293 /* Identifies a http_conn_t struct */
+#define	ESTACK_SIZE	20	/* Size of the stack */
+
+typedef struct http_conn_t {
+	uint_t	signature;	/* Cookie indicating this is a handle */
+	int	fd;		/* Connection's fd... */
+	SSL_CTX *ctx;
+	void	*ssl;		/* Handle to ssl data structure */
+	int	read_timeout;	/* Timeout to use on read requests in sec */
+	char    *basic_auth_userid;   /* Basic authentication user ID */
+	char   	*basic_auth_password; /* and password */
+	char	is_multipart;	/* B_TRUE if doing multipart/mixed download */
+	char	is_firstpart;	/* B_TRUE if first part in a multipart xfer */
+	char	is_firstchunk;	/* B_TRUE if first chunk in chunked xfer */
+	char	is_chunked;	/* B_TRUE if message body is chunked */
+	boolean_t keepalive;
+	struct	sockaddr_in  host_addr; /* Address of host */
+	url_t		uri;   		/* The current URI */
+	url_hport_t	proxy;		/* The proxy info */
+	boolean_t 	proxied;	/* Connection is proxied */
+	char	*random_file;	/* File with seed info for pseudo random  */
+				/* number generator */
+	char	*client_cert_file;	/* File holding client's certificate */
+	char	*private_key_file;	/* File with the private key */
+	char	*file_password;	/* file with password to key or pkcs12 file. */
+	http_respinfo_t resp;	/* Response summary info */
+	char	**resphdr;	/* Array of header response lines */
+	buf_struct_t inbuf;
+	char	*boundary;	/* Boundary text (multipart downloads only) */
+	uint_t	boundary_len;	/* Length of boundary string */
+	uint_t	numerrs;
+	uint_t	nexterr;	/* Next error to return */
+	ssize_t	body_size;	/* Size of message body or chunk */
+	ssize_t	body_read;	/* # of bytes of body_size processed */
+	ssize_t	body_size_tot;	/* Total message body size */
+	ssize_t	body_read_tot;	/* # of bytes of body_size_tot processed */
+	errent_t errs[ESTACK_SIZE]; /* stack of errors on the last request */
+				/* (libssl can return multiple errors on one */
+				/* operation) */
+} http_conn_t;
+
+/*
+ * Convenient macros for accessing fields in connection structure.
+ */
+#define	CONN_HOSTNAME		c_id->uri.hport.hostname
+#define	CONN_PORT		c_id->uri.hport.port
+#define	CONN_ABSPATH		c_id->uri.abspath
+#define	CONN_HTTPS		c_id->uri.https
+#define	CONN_PROXY_HOSTNAME	c_id->proxy.hostname
+#define	CONN_PROXY_PORT		c_id->proxy.port
+
+#define	RESET_ERR(c_id)	(c_id)->numerrs = 0, (c_id)->nexterr = 0
+#define	SET_ERR(c_id, src, err)	if ((c_id)->numerrs < ESTACK_SIZE) \
+		(c_id)->errs[(c_id)->numerrs].errsrc = (src), \
+		(c_id)->errs[(c_id)->numerrs ++].error = (err)
+
+#define	GET_ERR(c_id, e_src, e_code) \
+		if ((c_id)->nexterr < (c_id)->numerrs) \
+			(e_src) = (c_id)->errs[((c_id)->nexterr)].errsrc, \
+			(e_code) = (c_id)->errs[((c_id)->nexterr)++].error; \
+		else \
+			(e_src) = 0, (e_code) = 0
+
+/*
+ * Macro used to increment message body read counters
+ */
+#define	INC_BREAD_CNT(bool, bcnt) \
+	if (bool) { \
+		bcnt--; \
+		c_id->body_read++;\
+		c_id->body_read_tot++; \
+	}
+
+static int	ssl_init = 0;		/* 1 when ssl has been initialized */
+static char	*ca_verify_file;	/* List of trusted CA's  */
+static int	verify_depth = 16;	/* Certificate chain depth to verify */
+static int	p12_format = 0;		/* Default to PEM format */
+
+
+/* prototypes for local functions */
+static int	http_req(http_handle_t, const char *, http_req_t, offset_t,
+    offset_t);
+static boolean_t http_check_conn(http_conn_t *);
+static SSL_CTX *initialize_ctx(http_conn_t *);
+static int	tcp_connect(http_conn_t *, const char *, uint16_t);
+static int	readline(http_conn_t *, int, char *, int);
+static int	proxy_connect(http_conn_t *);
+static int	check_cert_chain(http_conn_t *, char *);
+static void	print_ciphers(SSL *);
+static int	read_headerlines(http_conn_t *, boolean_t);
+static void	free_response(http_conn_t *, int);
+static int	free_ctx_ssl(http_conn_t *);
+static int	get_chunk_header(http_conn_t *);
+static int	init_bread(http_conn_t *);
+static int	get_msgcnt(http_conn_t *, ssize_t *);
+static int	getaline(http_conn_t *, char *, int, boolean_t);
+static int	getbytes(http_conn_t *, char *, int);
+static int	http_srv_send(http_conn_t *, const void *, size_t);
+static int	http_srv_recv(http_conn_t *, void *, size_t);
+static void	handle_ssl_error(http_conn_t *, int);
+static int	count_digits(int);
+static int	hexdigit(char);
+static char	*eat_ws(const char *);
+static boolean_t startswith(const char **strp, const char *starts);
+
+/* ---------------------- public functions ----------------------- */
+
+/*
+ * http_set_p12_format - Set flag indicating that certs & keys will be in
+ *                    pkcs12 format.
+ *
+ * Default is PEM certs.  When this is called, the default can be changed to
+ * pcs12 format.
+ */
+void
+http_set_p12_format(int on_off)
+{
+	p12_format = on_off;
+}
+
+/*
+ * http_get_version - Get current boot http support version
+ *
+ *     pVer = http_get_version();
+ *
+ * Arguments:
+ *	None.
+ *
+ * Returns:
+ *	Pointer to struct with version information.
+ *
+ * Returns the version of the http support in the current library.  This
+ * is a struct with unsigned integsrs for <major>, <minor> and
+ * <micro> version numbers.  <major> changes when an incompatible change
+ * is made.  <minor> changes when an upwardly-compatible API change is
+ * made.  <micro> consists of bug fixes, etc.
+ */
+boot_http_ver_t const *
+http_get_version(void)
+{
+	return (&boot_http_ver);
+}
+
+/*
+ * http_set_verbose - Turn verbose on/off
+ *
+ *     http_set_verbose(on_off);
+ *
+ * Arguments:
+ *	on_off	- When TRUE, turn verbose mode one.  When FALSE, turn
+ *		  verbose off.
+ *
+ * Returns:
+ *	None.
+ *
+ * When enabled, information is logged to bootlog (or the Solaris equivalent).
+ */
+void
+http_set_verbose(boolean_t on_off)
+{
+	verbosemode = on_off;
+}
+
+/*
+ * http_set_cipher_list - Change the list of ciphers that can be used.
+ *
+ *     ret = http_set_cipher_list(handle, list);
+ *
+ * Arguments:
+ *	list	- List of ciphers that can be used.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ */
+int
+http_set_cipher_list(const char *list)
+{
+	early_err = 0;
+
+	if (list != NULL) {
+		list = strdup(list);
+		if (list == NULL) {
+			early_err = EHTTP_NOMEM;
+			return (-1);
+		}
+	}
+
+	free(cipher_list);
+	cipher_list = (char *)list;
+	return (0);
+}
+
+/*
+ * http_srv_init - Set up a structure for a connection.
+ *
+ *     handle = http_srv_init(url);
+ *
+ * Arguments:
+ *	url - the structure that contains the URI.
+ *
+ * Returns:
+ *	!= NULL	- A handle for referring to this connection.
+ *	== NULL - An error occurred.  Get the exact error from
+ *                http_get_lasterr().
+ */
+http_handle_t
+http_srv_init(const url_t *url)
+{
+	http_conn_t	*c_id;
+
+	early_err = 0;
+	if (url == NULL) {
+		early_err = EHTTP_BADARG;
+		return (NULL);
+	}
+
+	if ((c_id = malloc(sizeof (*c_id))) == NULL) {
+		early_err = EHTTP_NOMEM;
+		return (NULL);
+	}
+
+	bzero(c_id, sizeof (*c_id));
+	c_id->uri = *url;
+	c_id->proxied = B_FALSE;
+	c_id->read_timeout = DEFAULT_TIMEOUT;
+	c_id->keepalive = B_TRUE;
+	c_id->fd = -1;
+
+	/* Do this at the end, just in case.... */
+	c_id->signature = HTTP_CONN_INFO;
+
+	return (c_id);
+}
+
+/*
+ * http_conn_is_https - Determine whether the scheme is http or https.
+ *
+ *	B_TRUE	- Connection is an SSL connection.
+ *	B_FALSE - Connection isn't SSL.
+ *
+ *     ret = http_conn_is_https(handle, boolean_t *bool);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	bool	- Ptr to boolean in which to place result
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- Some error occurred.
+ */
+int
+http_conn_is_https(http_handle_t handle, boolean_t *bool)
+{
+	http_conn_t	*c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	*bool = CONN_HTTPS;
+	return (0);
+}
+
+/*
+ * http_set_proxy - Establish the proxy name/port.
+ *
+ *     ret = http_set_proxy(handle, proxy);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	proxy	- The hostport definition for the proxy. If NULL,
+ *		  The next connect will not use a proxy.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ */
+int
+http_set_proxy(http_handle_t handle, const url_hport_t *proxy)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (proxy != NULL) {
+		c_id->proxy = *proxy;
+		c_id->proxied = B_TRUE;
+	} else {
+		CONN_PROXY_HOSTNAME[0] = '\0';
+		c_id->proxied = B_FALSE;
+	}
+
+	return (0);
+}
+
+/*
+ * http_set_keepalive - Set keepalive for this connection.
+ *
+ *     http_set_keepalive(handle, on_off);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	on_off	- Boolean turning keepalive on (TRUE) or off (FALSE)
+ *
+ * Returns:
+ *	0	- Success.
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This setting takes effect next time a connection is opened using this
+ * handle.
+ */
+int
+http_set_keepalive(http_handle_t handle, boolean_t on_off)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	c_id->keepalive = on_off;
+	return (0);
+}
+
+/*
+ * http_set_socket_read_timeout - Set the timeout reads
+ *
+ *     http_set_socket_read_timeout(handle, timeout);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	timeout	- Timeout, in seconds.  Zero will default to 10 second
+ *		  timeouts.
+ *
+ * Returns:
+ *	0	- Success.
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This setting takes effect beginning with the next read operation on this
+ * connection.
+ */
+int
+http_set_socket_read_timeout(http_handle_t handle, uint_t timout)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	c_id->read_timeout = (timout) ? timout : DEFAULT_TIMEOUT;
+	return (0);
+}
+
+/*
+ * http_set_basic_auth - Set the basic authorization user ID and password
+ *
+ *     ret = http_set_basic_auth(handle, userid, password);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	userid	- ID to pass as part of http/https request
+ *	password- Password which goes with the user ID
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This must be set before a https connection is made.
+ */
+int
+http_set_basic_auth(http_handle_t handle, const char *userid,
+    const char *password)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (password == NULL || userid == NULL || userid[0] == '\0') {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+		return (-1);
+	}
+
+	userid = strdup(userid);
+	password = strdup(password);
+	if (userid == NULL || password == NULL) {
+		free((void *)userid);
+		free((void *)password);
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+		return (-1);
+	}
+
+	free(c_id->basic_auth_userid);
+	c_id->basic_auth_userid = (char *)userid;
+	free(c_id->basic_auth_password);
+	c_id->basic_auth_password = (char *)password;
+	return (0);
+}
+
+/*
+ * http_set_random_file - See the pseudo random number generator with file data
+ *
+ *     ret = http_set_random_file(handle, filename);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	filename
+ *		- filename (including path) with random number seed.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This must be set before a https connection is made.
+ */
+int
+http_set_random_file(http_handle_t handle, const char *fname)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (fname != NULL) {
+		fname = strdup(fname);
+		if (fname == NULL) {
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+	}
+
+	free(c_id->random_file);
+	c_id->random_file = (char *)fname;
+	return (0);
+}
+
+/*
+ * http_set_certificate_authority_file - Set the CA file.
+ *
+ *     ret = http_set_certificate_authority_file(filename);
+ *
+ * Arguments:
+ *	filename- File with the certificate authority certs
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This must be set before https connections to the servers is done.
+ */
+int
+http_set_certificate_authority_file(const char *fname)
+{
+	early_err = 0;
+
+	if (fname != NULL) {
+		fname = strdup(fname);
+		if (fname == NULL) {
+			early_err = EHTTP_NOMEM;
+			return (-1);
+		}
+	}
+
+	free(ca_verify_file);
+	ca_verify_file = (char *)fname;
+	return (0);
+}
+
+/*
+ * http_set_client_certificate_file - Set the file containing the PKCS#12
+ *		client certificate and optionally its certificate chain.
+ *
+ *     ret = http_set_client_certificate_file(handle, filename);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	filename- File (including path) containing certificate, etc.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This must be set before the handle is used to make a https connection
+ * which will require a client certificate.
+ */
+int
+http_set_client_certificate_file(http_handle_t handle, const char *fname)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (fname != NULL) {
+		fname = strdup(fname);
+		if (fname == NULL) {
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+	}
+
+	free(c_id->client_cert_file);
+	c_id->client_cert_file = (char *)fname;
+	return (0);
+}
+
+/*
+ * http_set_password - Set the password for the private key or pkcs12 file.
+ *
+ *     ret = http_set_password(handle, password);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	password- Password for the client's private key file or pkcs12 file.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This must be set before the handle is used to make a https connection.
+ */
+int
+http_set_password(http_handle_t handle, const char *password)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (password != NULL) {
+		password = strdup(password);
+		if (password == NULL) {
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+	}
+
+	free(c_id->file_password);
+	c_id->file_password = (char *)password;
+	return (0);
+}
+
+/*
+ * http_set_key_file_password - Set the password for the private key
+ *		file.
+ *
+ *     ret = http_set_key_file_password(handle, password);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	password- Password for the client's private key file.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This must be set before the handle is used to make a https connection.
+ */
+int
+http_set_key_file_password(http_handle_t handle, const char *password)
+{
+	return (http_set_password(handle, password));
+}
+
+/*
+ * http_set_private_key_file - Set the file containing the PKCS#12
+ *		private key for this client.
+ *
+ *     ret = http_set_private_key_file(handle, filename);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	filename- File (including path) containing the private key.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ *
+ * This must be set before the handle is used to make a https connection.
+ */
+int
+http_set_private_key_file(http_handle_t handle, const char *fname)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (fname != NULL) {
+		fname = strdup(fname);
+		if (fname == NULL) {
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+	}
+
+	free(c_id->private_key_file);
+	c_id->private_key_file = (char *)fname;
+	return (0);
+}
+
+/*
+ * http_srv_connect - Establish a connection to the server
+ *
+ *     ret = http_srv_connect(handle);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr() for specifics.
+ */
+int
+http_srv_connect(http_handle_t handle)
+{
+	http_conn_t	*c_id = handle;
+	SSL_CTX		*ctx = NULL;
+	int		retval;
+
+	ERR_clear_error();
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (CONN_HTTPS) {
+		/* Build our SSL context (this function sets any errors) */
+		ctx = initialize_ctx(c_id);
+		if (ctx == NULL) {
+			libbootlog(BOOTLOG_CRIT,
+			    "http_srv_connect: initialize_ctx returned NULL");
+			return (-1);
+		}
+	}
+
+	/* Connect the TCP socket */
+	if (c_id->proxied) {
+		c_id->fd = proxy_connect(c_id);
+	} else {
+		c_id->fd = tcp_connect(c_id, CONN_HOSTNAME, CONN_PORT);
+	}
+
+	if (c_id->fd < 0) {
+		if (ctx != NULL)
+			SSL_CTX_free(ctx);
+			libbootlog(BOOTLOG_CRIT,
+			    "http_srv_connect: %s returned %d",
+			    (c_id->proxied) ? "proxy_connect" : "tcp_connect",
+			    c_id->fd);
+		return (-1);
+	}
+
+	if (CONN_HTTPS) {
+		/* Connect the SSL socket */
+		if ((c_id->ssl = SSL_new(ctx)) == NULL) {
+			ulong_t err;
+			while ((err = ERR_get_error()) != 0)
+				SET_ERR(c_id, ERRSRC_LIBSSL, err);
+				libbootlog(BOOTLOG_CRIT,
+				    "http_srv_connect: SSL_new returned "
+				    "NULL");
+			(void) free_ctx_ssl(c_id);
+			return (-1);
+		}
+		if (verbosemode)
+			print_ciphers(c_id->ssl);
+
+		/* Ensure automatic negotiations will do things right */
+		SSL_set_connect_state(c_id->ssl);
+
+		if (SSL_set_fd(c_id->ssl, c_id->fd) == 0) {
+			ulong_t err;
+			while ((err = ERR_get_error()) != 0)
+				SET_ERR(c_id, ERRSRC_LIBSSL, err);
+				libbootlog(BOOTLOG_CRIT,
+				    "http_srv_connect: SSL_set_fd returned 0");
+			(void) free_ctx_ssl(c_id);
+			return (-1);
+		}
+
+		if ((retval = SSL_connect(c_id->ssl)) <= 0) {
+			handle_ssl_error(c_id, retval);
+			libbootlog(BOOTLOG_CRIT,
+			    "http_srv_connect: SSL_connect");
+			(void) free_ctx_ssl(c_id);
+			return (-1);
+		}
+
+		if (check_cert_chain(c_id, CONN_HOSTNAME) != 0) {
+			(void) free_ctx_ssl(c_id);
+			return (-1);
+		}
+
+		if (verbosemode)
+			print_ciphers(c_id->ssl);
+	}
+
+	return (0);
+}
+
+/*
+ * http_head_request - Issue http HEAD request
+ *
+ *     ret = http_head_request(handle, abs_path);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	abs_path- File name portion of the URI, beginning with a /.  Query,
+ *		  segment, etc are allowed.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ */
+int
+http_head_request(http_handle_t handle, const char *abs_path)
+{
+	return (http_req(handle, abs_path, HTTP_REQ_TYPE_HEAD, 0, 0));
+}
+
+/*
+ * http_get_request - Issue http GET request without a range.
+ *
+ *     ret = http_get_request(handle, abs_path);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	abs_path- File name portion of the URI, beginning with a /.  Query,
+ *		  segment, etc are allowed.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ */
+int
+http_get_request(http_handle_t handle, const char *abs_path)
+{
+	return (http_req(handle, abs_path, HTTP_REQ_TYPE_GET, -1, 0));
+}
+
+/*
+ * http_get_range_request - Issue http GET request using a range.
+ *
+ *     ret = http_get_range_request(handle, abs_path, curpos, len);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	abs_path- File name portion of the URI, beginning with a /.  Query,
+ *		  segment, etc are allowed.
+ *	curpos  - >=0 - Beginning of range
+ *	len	- = 0 - Range ends at the end of the file
+ *		  > 0 - Length of range.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ */
+int
+http_get_range_request(http_handle_t handle, const char *abs_path,
+    offset_t curpos, offset_t len)
+{
+	http_conn_t *c_id = handle;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (curpos < 0) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+		return (-1);
+	}
+
+	return (http_req(handle, abs_path, HTTP_REQ_TYPE_GET, curpos, len));
+}
+
+/*
+ * http_free_respinfo - Free a respinfo structure
+ *
+ *     ret = http_free_respinfo(resp);
+ *
+ * Arguments:
+ *	resp	- respinfo structure presumably allocated by
+ *		  http_process_headers() or http_process_part_headers()
+ *
+ * Note that if resp is NULL, then this results in a NOOP.
+ *
+ */
+void
+http_free_respinfo(http_respinfo_t *resp)
+{
+	if (resp == NULL) {
+		return;
+	}
+
+	if (resp->statusmsg != NULL) {
+		free(resp->statusmsg);
+	}
+	free(resp);
+}
+
+/*
+ * http_process_headers - Read in the header lines from the response
+ *
+ *     ret = http_process_headers(handle, resp);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the connection where the request
+ *		  was made.
+ *	resp	- Summary information about the response.
+ *
+ * Returns:
+ *	0	- Success
+ *	< 0	- An error occurred.  Specifics of the error can
+ *		  be gotten using http_get_lasterr().
+ *
+ * Process the HTTP headers in the response. Check for a valid response
+ * status line.  Allocate and return response information via the 'resp'
+ * argument. Header lines are stored locally, are are returned using calls
+ * to http_get_response_header() and http_get_header_value().
+ *
+ * Note that the errors will be set in the http_conn_t struct before the
+ * function which detected the error returns.
+ *
+ * Note that if resp is non-NULL, then upon a successful return, information
+ * about the status line, the code in the status line and the number of
+ * header lines are returned in the http_respinfo_t structure. The caller is
+ * responsible for freeing the resources allocated to this structure via
+ * http_free_respinfo().
+ *
+ * Note that the counters used to read message bodies are initialized here.
+ *
+ * Calling this function replaces the header information which is
+ * queried using http_get_response_header() and http_get_header_value().
+ * Once this function is called, headers read by the previous call
+ * to http_process_headers() or http_process_part_headers() is lost.
+ */
+int
+http_process_headers(http_handle_t handle, http_respinfo_t **resp)
+{
+	http_conn_t *c_id = handle;
+	http_respinfo_t *lresp;
+	char	line[MAXHOSTNAMELEN];
+	char	*ptr;
+	int	i;
+
+	ERR_clear_error();
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (resp != NULL) {
+		if ((lresp = malloc(sizeof (http_respinfo_t))) == NULL) {
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+
+		bzero(lresp, sizeof (http_respinfo_t));
+	}
+
+	/*
+	 * check the response status line, expecting
+	 * HTTP/1.1 200 OK
+	 */
+	i = getaline(c_id, line, sizeof (line), B_FALSE);
+	if (i == 0) {
+		if (resp != NULL) {
+			*resp = lresp;
+		}
+		return (0);
+	}
+
+	if (i < 0) {
+		/*
+		 * Cause of I/O error was already put into
+		 * error stack.  This is an additional error.
+		 */
+		http_free_respinfo(lresp);
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NODATA);
+		return (-1);
+	}
+
+	free_response(c_id, B_TRUE);
+
+	if (verbosemode)
+		libbootlog(BOOTLOG_VERBOSE, "http_process_headers: %s", line);
+
+	ptr = line;
+	if (strncmp(ptr, "HTTP/1.1", 8) != 0) {
+		http_free_respinfo(lresp);
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOT_1_1);
+		return (-1);
+	}
+
+	/* skip to the code */
+	ptr += 8;
+	while (isspace(*ptr))
+		ptr++;
+
+	/* make sure it's three digits */
+	i = 0;
+	while (isdigit(ptr[i]))
+		i++;
+	if (i != 3) {
+		http_free_respinfo(lresp);
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADHDR);
+		return (-1);
+	}
+	c_id->resp.code = strtol(ptr, NULL, 10);
+
+	/* skip to the message */
+	ptr += 3;
+	while (isspace(*ptr))
+		ptr++;
+
+	/* save the message */
+	c_id->resp.statusmsg = malloc(strlen(ptr) + 1);
+	if (c_id->resp.statusmsg == NULL) {
+		http_free_respinfo(lresp);
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+		return (-1);
+	}
+	(void) strcpy(c_id->resp.statusmsg, ptr);
+
+	if ((i = read_headerlines(c_id, B_FALSE)) < 0) {
+		/*
+		 * Error stack was already set at a lower level.
+		 * 'statusmsg' will be cleaned up next time
+		 * headers are read.
+		 */
+		http_free_respinfo(lresp);
+		return (-1);
+	}
+
+	/*
+	 * See if there is a 'content-type: multipart/mixed' line in the
+	 * headers.  If so, get the boundary string.
+	 */
+	ptr = http_get_header_value(handle, "Content-Type");
+	if (ptr != NULL) {
+		char *ptr2;
+
+		ptr2 = ptr;
+		while (isspace(*ptr2))
+			ptr2 ++;
+		if (startswith((const char **)&ptr2, "Multipart/Mixed;")) {
+			while (isspace(*ptr2))
+				ptr2 ++;
+			if (startswith((const char **)&ptr2, "Boundary=")) {
+				if (ptr2[0] == '"') {
+					ptr2 ++;
+					if (ptr2[strlen(ptr2) - 1] == '"')
+						ptr2[strlen(ptr2) - 1] = '\0';
+				}
+				c_id->boundary = strdup(ptr2);
+				if (c_id->boundary == NULL) {
+					free(ptr);
+					http_free_respinfo(lresp);
+					SET_ERR(c_id, ERRSRC_LIBHTTP,
+					    EHTTP_NOMEM);
+					return (-1);
+				}
+				c_id->boundary_len = strlen(c_id->boundary);
+				c_id->is_multipart = B_TRUE;
+				c_id->is_firstpart = B_TRUE;
+			}
+		}
+		free(ptr);
+	}
+
+	/*
+	 * Initialize the counters used to process message bodies.
+	 */
+	if (init_bread(c_id) != 0) {
+		/*
+		 * Error stack was already set at a lower level.
+		 */
+		http_free_respinfo(lresp);
+		return (-1);
+	}
+
+	/* Copy fields to the caller's structure */
+	if (resp != NULL) {
+		lresp->code = c_id->resp.code;
+		lresp->nresphdrs = c_id->resp.nresphdrs;
+		lresp->statusmsg = strdup(c_id->resp.statusmsg);
+		if (lresp->statusmsg == NULL) {
+			http_free_respinfo(lresp);
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+		*resp = lresp;
+	}
+
+	return (0);
+}
+
+/*
+ * http_process_part_headers - Read in part boundary and header lines for the
+ *                             next part of a multipart message.
+ *
+ *        ret = http_process_part_headers(handle, resp);
+ *
+ * Arguments:
+ *   handle	- Handle associated with the connection where the request
+ *		  was made.
+ *   resp	- Return address for summary information about the
+ *		  header block.
+ *
+ * Returns:
+ *   = 1	- The end part was found.
+ *   = 0	- Success, with header info returned in 'resp'
+ *   = -1	- An error occurred.  Specifics of the error can
+ *		  be gotten using http_get_lasterr().
+ *
+ * This function reads any \r\n sequences (empty lines) and expects to get
+ * a boundary line as the next non-empty line.  It then reads header lines
+ * (content-length, etc) until it gets another empty lines, which ends the
+ * header section.
+ *
+ * Note that if resp is non-NULL, then upon a successful return, information
+ * about the the number of header lines is returned in the http_respinfo_t
+ * structure. The caller is responsible for freeing the resources allocated
+ * to this structure via http_free_respinfo().
+ *
+ * Headers values can be returned using http_get_response_header() and
+ * http_get_header_value().
+ *
+ * Calling this function replaces the header information which is
+ * queried using http_get_response_header() and http_get_header_value().
+ * Once this function is called, information returned by the previous call
+ * to http_process_headers() or http_process_part_headers() is gone.
+ */
+int
+http_process_part_headers(http_handle_t handle, http_respinfo_t **resp)
+{
+	http_conn_t *c_id = handle;
+	char	line[MAXHOSTNAMELEN];
+	int	count;
+	int 	limit;
+	int	i;
+
+	ERR_clear_error();
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (c_id->is_multipart == 0) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOTMULTI);
+		return (-1);
+	}
+
+	/*
+	 * Figure out how many empty lines to allow.  Before the first
+	 * boundary of the transmission, there can be any number of
+	 * empty lines (from 0 up).  Limit these to some reasonable
+	 * failsafe.
+	 *
+	 * For the 2nd and later boundaries, there is supposed to be
+	 * one crlf pair.  However, many implementations don't require
+	 * it.  So don't require it.
+	 */
+	if (c_id->is_firstpart) {
+		limit = FAILSAFE;
+		c_id->is_firstpart = B_FALSE;
+	} else
+		limit = 1;
+
+	/* Look for the boundary line. */
+	count = 0;
+	while ((i = getaline(c_id, line, sizeof (line), B_TRUE)) == 0 &&
+	    count < FAILSAFE)
+		count ++;
+	if (i < 0 || count > limit) {
+		/*
+		 * If I/O error, cause was already put into
+		 * error stack.  This is an additional error.
+		 */
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOBOUNDARY);
+		return (-1);
+	}
+
+	free_response(c_id, B_FALSE);
+
+	if (verbosemode)
+		libbootlog(BOOTLOG_VERBOSE,
+		    "http_process_part_headers: %s", line);
+
+	/* Look for boundary line - '--<boundary text> */
+	if (line[0] != '-' || line[1] != '-' ||
+	    strncmp(&line[2], c_id->boundary, c_id->boundary_len) != 0) {
+		/* No boundary line.... */
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOBOUNDARY);
+		return (-1);
+	}
+
+	/* Is this the end-of-parts boundary (ends with a trailing '--') */
+	if (strcmp(&line[c_id->boundary_len + 2], "--") == 0) {
+		return (1);
+	}
+
+	free_response(c_id, B_FALSE);
+	if (read_headerlines(c_id, B_TRUE) < 0) {
+		/* Error stack was already set at a lower level. */
+		return (-1);
+	}
+
+	/* Copy fields to the caller's structure */
+	if (resp != NULL) {
+		if ((*resp = malloc(sizeof (http_respinfo_t))) == NULL) {
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+		bzero(*resp, sizeof (http_respinfo_t));
+		(*resp)->code = ' ';
+		(*resp)->nresphdrs = c_id->resp.nresphdrs;
+	}
+
+	return (0);
+}
+
+/*
+ * http_get_response_header - Get a line from the response header
+ *
+ *     ret = http_get_response_header(handle, whichline);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	whichline - Which line of the header to return.  This must be between
+ *		  zero and resp.nresphdrs which was returned by the call to
+ *		  http_process_headers().
+ *
+ * Returns:
+ *	ptr	- Points to a copy of the header line.
+ *	NULL	- An error occurred.  Check http_get_lasterr().
+ */
+char *
+http_get_response_header(http_handle_t handle, uint_t which)
+{
+	http_conn_t *c_id = handle;
+	char *res;
+
+	if (!http_check_conn(c_id))
+		return (NULL);
+
+	if (which >= c_id->resp.nresphdrs) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_OORANGE);
+		return (NULL);
+	}
+
+	res = strdup(c_id->resphdr[which]);
+	if (res == NULL) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+		return (NULL);
+	}
+	return (res);
+}
+
+/*
+ * http_get_header_value - Get the value of a header line.
+ *
+ *     ret = http_get_header_value(handle, what);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	what	- The field name to look up.
+ *
+ * Returns:
+ *	ptr	- Points to a copy of the header value.
+ *	NULL	- An error occurred.  Check http_get_lasterr().
+ */
+char *
+http_get_header_value(http_handle_t handle, const char *field_name)
+{
+	http_conn_t *c_id = handle;
+	char	*ptr;
+	char	*res;
+	int	i;
+	int	n;
+
+	if (!http_check_conn(c_id))
+		return (NULL);
+
+	if (field_name == NULL || field_name[0] == '\0') {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+		return (NULL);
+	}
+
+	for (i = 0; i < c_id->resp.nresphdrs; i++) {
+		ptr = c_id->resphdr[i];
+		n = strlen(field_name);
+		if (strncasecmp(field_name, ptr, n) == 0 && ptr[n] == ':') {
+			ptr += n + 1;
+
+			while (isspace(*ptr))
+				ptr++;
+
+			res = strdup(ptr);
+			if (res == NULL) {
+				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+				return (NULL);
+			}
+			return (res);
+		}
+	}
+	SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMATCH);
+	return (NULL);
+}
+
+/*
+ * http_read_body - Read the HTTP response body.
+ *
+ *     ret = http_read_body(handle, recv_buf_ptr, recv_buf_size);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the relevant connection
+ *	recv_buf_ptr - Points to buffer to receive buffer
+ *	recv_buf_size - Length in bytes of buffer.
+ *
+ * Returns:
+ *	n	- Number of bytes read..
+ *	< 0	- An error occurred.  This is (the number of bytes gotten + 1),
+ *		  negated.  In other words, if 'n' bytes were read and then an
+ *		  error occurred, this will return (-(n+1)).  So zero bytes
+ *		  were read and then an error occurs, this will return -1.  If
+ *		  1 byte was read, it will return -2, etc.  Specifics of the
+ *		  error can be gotten using http_get_lasterr().
+ *
+ * Note that the errors will be set in the http_conn_t struct before the
+ * function which detected the error returns.
+ */
+int
+http_read_body(http_handle_t handle, char *recv_buf_ptr, size_t recv_buf_size)
+{
+	http_conn_t *c_id = handle;
+
+	ERR_clear_error();
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (recv_buf_ptr == NULL || recv_buf_size == 0) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+		return (-1);
+	}
+
+	return (getbytes(c_id, recv_buf_ptr, recv_buf_size));
+}
+
+/*
+ * http_srv_disconnect - Get rid of the connection to the server without
+ *			freeing the http_conn_t structure.
+ *
+ *     ret = http_srv_disconnect(handle);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the connection
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Specifics of the error can
+ *		  be gotten using http_get_lasterr().
+ */
+int
+http_srv_disconnect(http_handle_t handle)
+{
+	http_conn_t *c_id = handle;
+	int err_ret;
+
+	ERR_clear_error();
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	err_ret = free_ctx_ssl(c_id);
+	bzero(&c_id->inbuf, sizeof (c_id->inbuf));
+	free_response(c_id, B_TRUE);
+
+	return (err_ret);
+}
+
+/*
+ * http_srv_close - Close the connection and clean up the http_conn_t
+ *		structure.
+ *
+ *     http_srv_close(handle);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Specifics of the error can
+ *		  be gotten using http_get_lasterr().
+ */
+int
+http_srv_close(http_handle_t handle)
+{
+	http_conn_t *c_id = handle;
+	int err_ret = 0;
+
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (c_id->ctx != NULL || c_id->ssl != NULL || c_id->fd != -1)
+		err_ret = http_srv_disconnect(handle);
+
+	free(c_id->basic_auth_userid);
+	free(c_id->basic_auth_password);
+	free(c_id->resp.statusmsg);
+	free(c_id->client_cert_file);
+	free(c_id->private_key_file);
+	free(c_id->random_file);
+	free(c_id->file_password);
+	c_id->signature = 0;
+
+	free(c_id);
+	return (err_ret);
+}
+
+/*
+ * http_get_conn_info - Return current information about the connection
+ *
+ *     err = http_get_conn_info(handle);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the connection in question
+ *
+ * Returns:
+ *	non_NULL- Points to structure
+ *	NULL	- An error exists.  Check http_get_lasterr().
+ */
+http_conninfo_t *
+http_get_conn_info(http_handle_t handle)
+{
+	http_conn_t *c_id = handle;
+	http_conninfo_t *info;
+
+	if (!http_check_conn(c_id))
+		return (NULL);
+
+	info = malloc(sizeof (*info));
+	if (info == NULL) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+		return (NULL);
+	}
+
+	bzero(info, sizeof (*info));
+
+	info->uri = c_id->uri;
+	info->proxy = c_id->proxy;
+	info->keepalive = c_id->keepalive;
+	info->read_timeout = c_id->read_timeout;
+
+	return (info);
+}
+
+/*
+ * http_get_lasterr - Return the next error on the last operation
+ *
+ *     err = http_get_lasterr(handle, errsrc);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the connection in question
+ *		  If no valid handle exists yet, this can be NULL.
+ *		  However, it must be checked with the very next call.
+ *	errsrc	- Returns the Sources of errors (ERRSRC_* values).
+ *
+ * Returns:
+ *	0	- No error exists
+ *	<> 0	- The error.
+ */
+ulong_t
+http_get_lasterr(http_handle_t handle, uint_t *errsrc)
+{
+	http_conn_t *c_id = handle;
+	ulong_t src;
+	ulong_t err;
+
+	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO) {
+		if (errsrc)
+			*errsrc = ERRSRC_LIBHTTP;
+		err = early_err;
+		early_err = 0;
+		return (err);
+	}
+
+	GET_ERR(c_id, src, err);
+	if (src == 0 && err == 0) {
+		if (errsrc)
+			*errsrc = ERRSRC_LIBHTTP;
+		err = early_err;
+		early_err = 0;
+		return (err);
+	}
+	if (errsrc)
+		*errsrc = src;
+	return (err);
+}
+
+/*
+ * http_decode_err - Decode a libssl error
+ *
+ *     err = http_decode_err(err, errlib, errfunc, errcode);
+ *
+ * Arguments:
+ *	err	- libssl/libcrypto error returned.
+ *	errlib	- returns libssl/libcrypto sublibrary that caused the error
+ *	errfunc	- returns function in that library
+ *	errcode - returns error code
+ *
+ * Returns:
+ *	None other than the above.
+ */
+void
+http_decode_err(ulong_t err, int *errlib, int *errfunc, int *errcode)
+{
+	if (errlib)
+		*errlib = ERR_GET_LIB(err);
+	if (errfunc)
+		*errfunc = ERR_GET_FUNC(err);
+	if (errcode)
+		*errcode = ERR_GET_REASON(err);
+}
+
+/* ---------------------- private functions ----------------------- */
+
+/*
+ * http_req - Issue http request (either HEAD or GET)
+ *
+ *     ret = http_req(handle, abs_path, reqtype, curpos, len);
+ *
+ * Arguments:
+ *	handle	- Handle associated with the desired connection
+ *	abs_path- File name portion of the URI, beginning with a /.  Query,
+ *		  segment, etc are allowed.
+ *	type	- HTTP_REQ_TYPE_HEAD or HTTP_REQ_TYPE_GET
+ *
+ *	In the case of GET requests,
+ *	  curpos- -1  - Range not used
+ *		  >=0 - Beginning of range
+ *	  len	- 0   - Range ends at the end of the file
+ *		  >0  - Length of range.
+ *
+ * Returns:
+ *	0	- Success
+ *	-1	- An error occurred.  Check http_get_lasterr().
+ */
+static int
+http_req(http_handle_t handle, const char *abs_path, http_req_t type,
+    offset_t curpos, offset_t len)
+{
+	http_conn_t *c_id = handle;
+	char	*request;
+	char	*reqtypename;
+	char	*newreq;
+	int	requestlen;
+	int	retval;
+	int	j;
+
+	ERR_clear_error();
+	if (!http_check_conn(c_id))
+		return (-1);
+
+	if (abs_path == NULL || abs_path[0] == '\0') {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+		return (-1);
+	}
+
+	/* Determine the name for the request type */
+	switch (type) {
+	case HTTP_REQ_TYPE_GET:
+		reqtypename = "GET";
+		if (curpos < 0 && curpos != -1) {
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+			return (-1);
+		}
+		break;
+
+	case HTTP_REQ_TYPE_HEAD:
+		reqtypename = "HEAD";
+		break;
+
+	default:
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+		return (-1);
+	}
+
+	/* Do rudimentary checks on the absolute path */
+	if (abs_path == NULL || *abs_path != '/') {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
+		libbootlog(BOOTLOG_CRIT, "http_req: invalid file path");
+		if (abs_path != NULL)
+			libbootlog(BOOTLOG_CRIT, " %s", abs_path);
+		return (-1);
+	}
+	(void) strlcpy(CONN_ABSPATH, abs_path, MAXHOSTNAMELEN);
+
+	/*
+	 * Size the request.
+	 *
+	 * With proxy:
+	 *   reqtypename + " http://" + host + ":" + port + path +
+	 *						" HTTP/1.1\r\n" +
+	 * Without proxy:
+	 *   reqtypename + " " + path + " HTTP/1.1\r\n" +
+	 */
+	requestlen = strlen(reqtypename) + 8 + strlen(CONN_HOSTNAME) + 1 +
+	    count_digits(CONN_PORT) + strlen(CONN_ABSPATH) + 11;
+
+	/*
+	 * Plus the rest:
+	 *   "Host: " + targethost + ":" + count_digits(port) + "\r\n" +
+	 *   "Connection: Keep-Alive\r\n" plus trailing "\r\n\0"
+	 */
+	requestlen += 6 + strlen(CONN_HOSTNAME) + 1 +
+	    count_digits(CONN_PORT) + 2 + 24 + 3;
+	if ((request = malloc(requestlen)) == NULL) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+		return (-1);
+	}
+
+	/* The request line */
+	if (c_id->proxied && c_id->ssl == NULL) {
+		j = snprintf(request, requestlen,
+		    "%s http://%s:%d%s HTTP/1.1\r\n",
+		    reqtypename, CONN_HOSTNAME, CONN_PORT,
+		    CONN_ABSPATH);
+	} else {
+		j = snprintf(request, requestlen, "%s %s HTTP/1.1\r\n",
+		    reqtypename, CONN_ABSPATH);
+	}
+
+	/* Ancillary headers */
+	j += snprintf(&request[j], requestlen - j, "Host: %s:%d\r\n",
+	    CONN_HOSTNAME, CONN_PORT);
+	if (!c_id->keepalive)
+		j += snprintf(&request[j], requestlen - j,
+		    "Connection: close\r\n");
+	else
+		j += snprintf(&request[j], requestlen - j,
+		    "Connection: Keep-Alive\r\n");
+	/*
+	 * We only send the range header on GET requests
+	 *
+	 * "Range: bytes=" + from + "-" + end + "\r\n" or
+	 * "Range: bytes=" + from + "-"  "\r\n"
+	 */
+	if (type == HTTP_REQ_TYPE_GET && curpos >= 0) {
+		offset_t endpos;
+
+		requestlen += 13 + count_digits(curpos) + 1 + 2;
+		if (len > 0) {
+			endpos = curpos + len - 1;
+			requestlen += count_digits(endpos);
+		}
+
+		if ((newreq = realloc(request, requestlen)) == NULL) {
+			free(request);
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+		request = newreq;
+
+		j += sprintf(&request[j], "Range: bytes=%lld-", curpos);
+		if (len > 0)
+			j += sprintf(&request[j], "%lld", endpos);
+		j += sprintf(&request[j], "\r\n");
+	}
+
+	/*
+	 * Authorization is added only if provided (RFC 2617, Section 2)
+	 *
+	 * "Authorization: Basic " + authencstr + "\r\n"
+	 */
+	if (c_id->basic_auth_userid && c_id->basic_auth_password) {
+		char *authstr;
+		char *authencstr;
+		int authlen;
+
+		/*
+		 * Allow for concat(basic_auth_userid ":" basic_auth_password)
+		 */
+		authlen = strlen(c_id->basic_auth_userid) + 2 +
+		    strlen(c_id->basic_auth_password);
+		if ((authstr = malloc(authlen + 1)) == NULL) {
+			free(request);
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+		(void) snprintf(authstr, authlen + 1, "%s:%s",
+		    c_id->basic_auth_userid, c_id->basic_auth_password);
+
+		/* 3 bytes encoded as 4 (round up) with null termination */
+		if ((authencstr = malloc((authlen + 2) / 3 * 4 + 1)) == NULL) {
+			free(authstr);
+			free(request);
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+
+		(void) EVP_EncodeBlock((unsigned char *)authencstr,
+		    (unsigned char *)authstr, authlen);
+
+		/*
+		 * Finally do concat(Authorization: Basic " authencstr "\r\n")
+		 */
+		requestlen += 21 + strlen(authencstr) + 2;
+		if ((newreq = realloc(request, requestlen)) == NULL) {
+			free(authencstr);
+			free(authstr);
+			free(request);
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+			return (-1);
+		}
+		request = newreq;
+
+		j += snprintf(&request[j], requestlen - j,
+		    "Authorization: Basic %s\r\n", authencstr);
+
+		free(authencstr);
+		free(authstr);
+	}
+
+	j += sprintf(&request[j], "\r\n");
+
+	if (verbosemode)
+		libbootlog(BOOTLOG_VERBOSE, "%s", request);
+
+	/* send the HTTP request */
+	retval = http_srv_send(c_id, request, j);
+
+	free(request);
+	if (retval != j) {
+		/* Assume error in was set by send request. */
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * password_cb - Callback to get private key password and return it
+ *               to SSL.  (Used for PEM certificates only.)
+ *
+ * 	len = passwd_cb(buf, buflen, rwflag, userdata);
+ *
+ *  Arguments:
+ *     buf	- Buffer for the password
+ *     buflen	- Length of 'buf'
+ *     rwflag	- password will be used for reading/decryption (== 0)
+ *		  or writing/encryption (== 1).
+ *     userdata	- Points to connection-specific information.
+ *
+ *  Returns:
+ *     > 0	- Length of password that was put into 'buf'.
+ *     0 	- No password was returned (usually error occurred)
+ *
+ * NOTE:  The password code is not thread safe
+ */
+/* ARGSUSED */
+static int
+password_cb(char *buf, int buflen, int rwflag, void *userdata)
+{
+	http_conn_t *c_id = userdata;
+
+	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO)
+		return (0);
+
+	if (c_id->file_password == NULL ||
+	    buflen < strlen(c_id->file_password) + 1)
+		return (0);
+
+	return (strlcpy(buf, c_id->file_password, buflen));
+}
+
+/*
+ * initialize_ctx - Initialize the context for a connection.
+ *
+ *       ctx = initialize_ctx(c_id);
+ *
+ *  Arguments:
+ *     None.
+ *
+ *  Returns:
+ *     non-NULL	- Points to ctx structure.
+ *     NULL	- An error occurred.  Any cleanup is done and error
+ *                information is in the error stack.
+ */
+static SSL_CTX *
+initialize_ctx(http_conn_t *c_id)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+	SSL_METHOD	*meth;
+#else
+	const SSL_METHOD	*meth;
+#endif
+	SSL_CTX		*ctx;
+
+	ERR_clear_error();
+
+	/* Global system initialization */
+	if (ssl_init == 0) {
+		sunw_crypto_init();
+		SSL_load_error_strings();
+		ssl_init = 1;
+	}
+
+	/* Create our context */
+	meth = SSLv3_client_method();
+	if ((ctx = SSL_CTX_new(meth)) == NULL) {
+		ulong_t err;
+		while ((err = ERR_get_error()) != 0)
+			SET_ERR(c_id, ERRSRC_LIBSSL, err);
+			libbootlog(BOOTLOG_CRIT,
+			    "initialize_ctx: SSL_CTX_new returned NULL");
+		return (NULL);
+	}
+
+	/*
+	 * Ensure that any renegotiations for blocking connections will
+	 * be done automatically.  (The alternative is to return partial
+	 * reads to the caller and let it oversee the renegotiations.)
+	 */
+	if (SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY) == 0) {
+		ulong_t err;
+		while ((err = ERR_get_error()) != 0)
+			SET_ERR(c_id, ERRSRC_LIBSSL, err);
+			libbootlog(BOOTLOG_CRIT,
+			    "initialize_ctx: SSL_CTX_set_mode returned 0");
+		(void) SSL_CTX_free(ctx);
+		return (NULL);
+	}
+
+	/* set cipher list if provided */
+	if (cipher_list != NULL) {
+		if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
+			ulong_t err;
+			while ((err = ERR_get_error()) != 0)
+				SET_ERR(c_id, ERRSRC_LIBSSL, err);
+				libbootlog(BOOTLOG_CRIT,
+				    "initialize_ctx: Error in cipher list");
+			SSL_CTX_free(ctx);
+			return (NULL);
+		}
+	}
+
+	/*
+	 * We attempt to use the client_certificate_file for the private
+	 * key input scheme *only* in the absence of private_key_file. In
+	 * this instance the scheme will be the same as that used for the
+	 * certificate input.
+	 */
+
+	/* Load our certificates */
+	if (c_id->client_cert_file != NULL) {
+		if (p12_format) {
+			/* Load pkcs12-formated files */
+			if (sunw_p12_use_certfile(ctx, c_id->client_cert_file,
+			    c_id->file_password)
+			    <= 0) {
+				ulong_t err;
+				while ((err = ERR_get_error()) != 0)
+					SET_ERR(c_id, ERRSRC_LIBSSL, err);
+					libbootlog(BOOTLOG_CRIT,
+					    "initialize_ctx: Couldn't read "
+					    "PKCS12 certificate file");
+				SSL_CTX_free(ctx);
+				return (NULL);
+			}
+		} else {
+			/* Load PEM-formated files */
+			if (SSL_CTX_use_certificate_file(ctx,
+			    c_id->client_cert_file, SSL_FILETYPE_PEM) <= 0) {
+				ulong_t err;
+				while ((err = ERR_get_error()) != 0)
+					SET_ERR(c_id, ERRSRC_LIBSSL, err);
+					libbootlog(BOOTLOG_CRIT,
+					    "initialize_ctx: Couldn't read "
+					    "PEM certificate file");
+				SSL_CTX_free(ctx);
+				return (NULL);
+			}
+		}
+		if (c_id->private_key_file == NULL)
+			c_id->private_key_file = c_id->client_cert_file;
+	}
+
+	/* Load our keys */
+	if (p12_format) {
+		/* Load pkcs12-formated files */
+		if (c_id->private_key_file != NULL) {
+			if (sunw_p12_use_keyfile(ctx, c_id->private_key_file,
+			    c_id->file_password)
+			    <= 0) {
+				ulong_t err;
+				while ((err = ERR_get_error()) != 0)
+					SET_ERR(c_id, ERRSRC_LIBSSL, err);
+					libbootlog(BOOTLOG_CRIT,
+					    "initialize_ctx: Couldn't read "
+					    "PKCS12 key file");
+				SSL_CTX_free(ctx);
+				return (NULL);
+			}
+		}
+	} else {
+		/* Load PEM-formated files */
+		SSL_CTX_set_default_passwd_cb(ctx, password_cb);
+		SSL_CTX_set_default_passwd_cb_userdata(ctx, c_id);
+		if (c_id->private_key_file != NULL) {
+			if (SSL_CTX_use_PrivateKey_file(ctx,
+			    c_id->private_key_file, SSL_FILETYPE_PEM) <= 0) {
+				ulong_t err;
+				while ((err = ERR_get_error()) != 0)
+					SET_ERR(c_id, ERRSRC_LIBSSL, err);
+					libbootlog(BOOTLOG_CRIT,
+					    "initialize_ctx: Couldn't read "
+					    "PEM key file");
+				SSL_CTX_free(ctx);
+				return (NULL);
+			}
+		}
+	}
+
+	/* Load the CAs we trust */
+	if (ca_verify_file != NULL) {
+		if (p12_format) {
+			if (sunw_p12_use_trustfile(ctx, ca_verify_file,
+			    c_id->file_password)
+			    <= 0) {
+				ulong_t err;
+				while ((err = ERR_get_error()) != 0)
+					SET_ERR(c_id, ERRSRC_LIBSSL, err);
+					libbootlog(BOOTLOG_CRIT,
+					    "initialize_ctx: Couldn't read "
+					    "PKCS12 CA list file");
+				SSL_CTX_free(ctx);
+				return (NULL);
+			}
+		} else {
+			if (SSL_CTX_load_verify_locations(ctx, ca_verify_file,
+			    NULL) == 0) {
+				ulong_t err;
+				while ((err = ERR_get_error()) != 0)
+					SET_ERR(c_id, ERRSRC_LIBSSL, err);
+					libbootlog(BOOTLOG_CRIT,
+					    "initialize_ctx: Couldn't read PEM"
+					    " CA list file");
+				SSL_CTX_free(ctx);
+				return (NULL);
+			}
+		}
+	}
+
+	SSL_CTX_set_verify_depth(ctx, verify_depth);
+
+	/* Load randomness */
+	if (c_id->random_file != NULL &&
+	    RAND_load_file(c_id->random_file, 1024 * 1024) <= 0) {
+		ulong_t err;
+		while ((err = ERR_get_error()) != 0)
+			SET_ERR(c_id, ERRSRC_LIBSSL, err);
+			libbootlog(BOOTLOG_CRIT,
+			    "initialize_ctx: Couldn't load random file");
+		SSL_CTX_free(ctx);
+		return (NULL);
+	}
+	if (RAND_status() <= 0) {
+		ulong_t err;
+		while ((err = ERR_get_error()) != 0)
+			SET_ERR(c_id, ERRSRC_LIBSSL, err);
+			libbootlog(BOOTLOG_CRIT,
+			    "initialize_ctx: PRNG not seeded");
+		SSL_CTX_free(ctx);
+		return (NULL);
+	}
+
+	return (ctx);
+}
+
+/*
+ * tcp_connect - Set up a TCP connection.
+ *
+ *         sock = tcp_connect(c_id, hostname, port);
+ *
+ * Arguments:
+ *      c_id	 - Structure associated with the desired connection
+ *	hostname - the host to connect to
+ *	port	 - the port to connect to
+ *
+ * Returns:
+ *      >= 0	- Socket number.
+ *      -1	- Error occurred.  Error information is set in the
+ *                error stack.  Any cleanup is done.
+ *
+ * This function established a connection to the target host.  When
+ * it returns, the connection is ready for a HEAD or GET request.
+ */
+static int
+tcp_connect(http_conn_t *c_id, const char *hostname, uint16_t port)
+{
+	struct hostent	*hp;
+	struct sockaddr_in addr;
+	int	sock;
+	int	status;
+
+	if ((hp = gethostbyname(hostname)) == NULL) {
+		SET_ERR(c_id, ERRSRC_RESOLVE, h_errno);
+		return (-1);
+	}
+
+	bzero(&addr, sizeof (addr));
+	/* LINTED */
+	addr.sin_addr = *(struct in_addr *)hp->h_addr;
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+
+	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+		return (-1);
+	}
+
+	status = connect(sock, (struct sockaddr *)&addr, sizeof (addr));
+	if (status < 0) {
+		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+		(void) socket_close(sock);
+		return (-1);
+	}
+
+	c_id->host_addr = addr;	/* save for future sendto calls */
+	c_id->fd = sock;
+
+	return (sock);
+}
+
+/*
+ * readline - Get a line from the socket.  Discard the end-of-line
+ *            (CR or CR/LF or LF).
+ *
+ *         ret = readline(c_id, sock, buf, len);
+ *
+ * Arguments:
+ *      c_id	- Structure associated with the desired connection
+ *      sock	- Socket to read
+ *      buf   	- Buffer for the line
+ *      len	- Length of the buffer
+ *
+ * Returns:
+ *      0	- Success.  'buf' contains the line.
+ *      -1	- Error occurred.  Error information is set in the
+ *                error stack.
+ */
+static int
+readline(http_conn_t *c_id, int sock, char *buf, int len)
+{
+	int	n, r;
+	char	*ptr = buf;
+
+	for (n = 0; n < len; n++) {
+		r = socket_read(sock, ptr, 1, c_id->read_timeout);
+
+		if (r < 0) {
+			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+			return (-1);
+		} else if (r == 0) {
+			libbootlog(BOOTLOG_WARNING, "Readline: no data");
+			return (0);
+		}
+
+		if (*ptr == '\n') {
+			*ptr = '\0';
+
+			/* Strip off the CR if it's there */
+			if (buf[n-1] == '\r') {
+				buf[n-1] = '\0';
+				n--;
+			}
+
+			return (n);
+		}
+
+		ptr++;
+	}
+
+	libbootlog(BOOTLOG_WARNING, "readline: Buffer too short\n");
+	return (0);
+}
+
+/*
+ * proxy_connect - Set up a proxied TCP connection to the target host.
+ *
+ *         sock = proxy_connect(c_id);
+ *
+ * Arguments:
+ *      c_id  -	Structure associated with the desired connection
+ *
+ * Returns:
+ *      >= 0	- Socket number.
+ *      -1	- Error occurred.  Error information is set in the
+ *                error stack.  Any cleanup is done.
+ *
+ * This function established a connection to the proxy and then sends
+ * the request to connect to the target host.  It reads the response
+ * (the status line and any headers).  When it returns, the connection
+ * is ready for a HEAD or GET request.
+ */
+static int
+proxy_connect(http_conn_t *c_id)
+{
+	struct sockaddr_in addr;
+	int	sock;
+	char	buf[1024];
+	char	*ptr;
+	int	i;
+
+	if ((sock = tcp_connect(c_id, CONN_PROXY_HOSTNAME,
+	    CONN_PROXY_PORT)) < 0) {
+		return (-1);
+	}
+
+	if (!CONN_HTTPS) {
+		return (sock);
+	}
+
+	/* Now that we're connected, do the proxy request */
+	(void) snprintf(buf, sizeof (buf),
+	    "CONNECT %s:%d HTTP/1.0\r\n\r\n", CONN_HOSTNAME, CONN_PORT);
+
+	/* socket_write sets the errors */
+	if (socket_write(sock, buf, strlen(buf), &addr) <= 0) {
+		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+		(void) socket_close(sock);
+		return (-1);
+	}
+
+	/* And read the response */
+	i = readline(c_id, sock, buf, sizeof (buf));
+	if (i <= 0) {
+		if (i == 0)
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NORESP);
+			libbootlog(BOOTLOG_CRIT,
+			    "proxy_connect: Empty response from proxy");
+		(void) socket_close(sock);
+		return (-1);
+	}
+
+	ptr = buf;
+	if (strncmp(ptr, "HTTP", 4) != 0) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOT_1_1);
+		libbootlog(BOOTLOG_CRIT,
+		    "proxy_connect: Unrecognized protocol");
+		(void) socket_close(sock);
+		return (-1);
+	}
+
+	/* skip to the code */
+	ptr += 4;
+	while (*ptr != ' ' && *ptr != '\0')
+		ptr++;
+	while (*ptr == ' ' && *ptr != '\0')
+		ptr++;
+
+	/* make sure it's three digits */
+	if (strncmp(ptr, "200", 3) != 0) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADRESP);
+		libbootlog(BOOTLOG_CRIT,
+		    "proxy_connect: Received error from proxy server");
+		(void) socket_close(sock);
+		return (-1);
+	}
+	ptr += 3;
+	if (isdigit(*ptr)) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADRESP);
+		(void) socket_close(sock);
+		return (-1);
+	}
+
+	/* Look for the blank line that signals end of proxy header */
+	while ((i = readline(c_id, sock, buf, sizeof (buf))) > 0)
+		;
+
+	if (i < 0) {
+		(void) socket_close(sock);
+		return (-1);
+	}
+
+	return (sock);
+}
+
+/*
+ * check_cert_chain - Check if we have a valid certificate chain.
+ *
+ *      ret = check_cert_chain(c_id, host);
+ *
+ * Arguments:
+ *    c_id	- Connection info.
+ *    host	- Name to compare with the common name in the certificate.
+ *
+ * Returns:
+ *    0		- Certificate chain and common name are both OK.
+ *    -1	- Certificate chain and/or common name is not valid.
+ */
+static int
+check_cert_chain(http_conn_t *c_id, char *host)
+{
+	X509	*peer;
+	char	peer_CN[256];
+	long	verify_err;
+
+	if ((verify_err = SSL_get_verify_result(c_id->ssl)) != X509_V_OK) {
+		SET_ERR(c_id, ERRSRC_VERIFERR, verify_err);
+		libbootlog(BOOTLOG_CRIT,
+		    "check_cert_chain: Certificate doesn't verify");
+		return (-1);
+	}
+
+	/*
+	 * Check the cert chain. The chain length
+	 * is automatically checked by OpenSSL when we
+	 * set the verify depth in the ctx
+	 *
+	 * All we need to do here is check that the CN
+	 * matches
+	 */
+
+	/* Check the common name */
+	if ((peer = SSL_get_peer_certificate(c_id->ssl)) == NULL) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOCERT);
+		libbootlog(BOOTLOG_CRIT,
+		    "check_cert_chain: Peer did not present a certificate");
+		return (-1);
+	}
+	(void) X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+	    NID_commonName, peer_CN, 256);
+
+	if (verbosemode)
+		libbootlog(BOOTLOG_VERBOSE,
+		    "server cert's peer_CN is %s, host is %s", peer_CN, host);
+
+	if (strcasecmp(peer_CN, host)) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMATCH);
+		libbootlog(BOOTLOG_CRIT,
+		    "check_cert_chain: Common name doesn't match host name");
+		libbootlog(BOOTLOG_CRIT,
+		    "peer_CN = %s, host = %s", peer_CN, host);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * print_ciphers - Print the list of ciphers for debugging.
+ *
+ *       print_ciphers(ssl);
+ *
+ * Arguments:
+ *     ssl	- SSL connection.
+ *
+ * Returns:
+ *     none
+ */
+static void
+print_ciphers(SSL *ssl)
+{
+	SSL_CIPHER	*c;
+	STACK_OF(SSL_CIPHER)	*sk;
+	int	i;
+	const char	*name;
+
+	if (ssl == NULL)
+		return;
+
+	sk = SSL_get_ciphers(ssl);
+	if (sk == NULL)
+		return;
+
+	for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
+		c = sk_SSL_CIPHER_value(sk, i);
+		libbootlog(BOOTLOG_VERBOSE, "%08lx %s", c->id, c->name);
+	}
+	name = SSL_get_cipher_name(ssl);
+	if (name == NULL)
+		name = "";
+	libbootlog(BOOTLOG_VERBOSE, "Current cipher = %s", name);
+}
+
+/*
+ * read_headerlines - Get the header lines from the server.  This reads
+ *              lines until it gets a empty line indicating end of headers.
+ *
+ *       ret = read_headerlines(c_id);
+ *
+ * Arguments:
+ *     c_id	- Info about the connection being read.
+ *     bread	- TRUE if the headerlines are part of the message body.
+ *
+ * Returns:
+ *     0	- Header lines were read.
+ *     -1	- Error occurred.  The errors information is already in
+ *                the error stack.
+ *
+ *  Read the lines.  If the current line begins with a space or tab, it is
+ *  a continuation.  Take the new line and append it to the end of the
+ *  previous line rather than making an entry for another line in
+ *  c_id->resphdr.
+ *
+ *  Note that I/O errors are put into the error stack by http_srv_recv(),
+ *  which is called by getaline().
+ */
+static int
+read_headerlines(http_conn_t *c_id, boolean_t bread)
+{
+	char	line[MAXHOSTNAMELEN];
+	char	**new_buf;
+	char	*ptr;
+	int	next;
+	int	cur;
+	int	n;
+
+	/* process headers, stop when we get to an empty line */
+	cur = 0;
+	next = 0;
+	while ((n = getaline(c_id, line, sizeof (line), bread)) > 0) {
+
+		if (verbosemode)
+			libbootlog(BOOTLOG_VERBOSE,
+			    "read_headerlines: %s", line);
+		/*
+		 * See if this is a continuation line (first col is a
+		 * space or a tab)
+		 */
+		if (line[0] != ' ' && line[0] != '	') {
+			cur = next;
+			next ++;
+			new_buf =
+			    realloc(c_id->resphdr, (cur + 1) * sizeof (void *));
+			if (new_buf == NULL) {
+				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+				return (-1);
+			}
+			c_id->resphdr = new_buf;
+
+			c_id->resphdr[cur] = strdup(line);
+			if (c_id->resphdr[cur] == NULL) {
+				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+				return (-1);
+			}
+		} else {
+			ptr = line;
+			while (isspace(*ptr))
+				ptr ++;
+			c_id->resphdr[cur] = realloc(c_id->resphdr[cur],
+			    strlen(c_id->resphdr[cur]) + strlen(ptr) + 1);
+			if (c_id->resphdr[cur] == NULL) {
+				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
+				return (-1);
+			}
+			(void) strcat(c_id->resphdr[cur], ptr);
+		}
+		ptr = &(c_id->resphdr[cur][strlen(c_id->resphdr[cur]) - 1]);
+		while (ptr > c_id->resphdr[cur] && isspace(*ptr))
+			ptr --;
+	}
+	c_id->resp.nresphdrs = next;
+
+	/* Cause of any I/O error was already put into error stack. */
+	return (n >= 0 ? 0 : -1);
+}
+
+static void
+free_response(http_conn_t *c_id, int free_boundary)
+{
+	int i;
+
+	/* free memory from previous calls */
+	if (c_id->resp.statusmsg != NULL) {
+		free(c_id->resp.statusmsg);
+		c_id->resp.statusmsg = NULL;
+	}
+	for (i = 0; i < c_id->resp.nresphdrs; i++) {
+		free(c_id->resphdr[i]);
+		c_id->resphdr[i] = NULL;
+	}
+	c_id->resp.nresphdrs = 0;
+	if (c_id->resphdr != NULL) {
+		free(c_id->resphdr);
+		c_id->resphdr = NULL;
+	}
+
+	if (free_boundary && c_id->boundary) {
+		free(c_id->boundary);
+		c_id->boundary = NULL;
+		c_id->is_multipart = B_FALSE;
+	}
+}
+
+static int
+free_ctx_ssl(http_conn_t *c_id)
+{
+	int err_ret = 0;
+
+	if (c_id->ssl != NULL) {
+		if (SSL_shutdown(c_id->ssl) <= 0) {
+			ulong_t err;
+			while ((err = ERR_get_error()) != 0)
+				SET_ERR(c_id, ERRSRC_LIBSSL, err);
+			err_ret = -1;
+		}
+		SSL_free(c_id->ssl);
+		c_id->ssl = NULL;
+	}
+
+	if (c_id->fd != -1 && socket_close(c_id->fd) < 0) {
+		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+		err_ret = -1;
+	}
+	c_id->fd = -1;
+
+	if (c_id->ctx != NULL) {
+		SSL_CTX_free(c_id->ctx);
+		c_id->ctx = NULL;
+	}
+
+	return (err_ret);
+}
+
+/*
+ * get_chunk_header - Get a chunk header line
+ *
+ * Arguments:
+ *   c_id   - Structure describing the connection in question.
+ *
+ * Returns:
+ *  >=0	- Length of next chunk
+ *  -1	- Error occurred.  The error information is in the error stack.
+ */
+static int
+get_chunk_header(http_conn_t *c_id)
+{
+	char	line[MAXHOSTNAMELEN];
+	char	*ptr;
+	int	value;
+	int	ok;
+	int	i;
+
+	/*
+	 * Determine whether an extra crlf pair will precede the
+	 * chunk header.  For the first one, there is no preceding
+	 * crlf.  For later chunks, there is one crlf.
+	 */
+	if (c_id->is_firstchunk) {
+		ok = 1;
+		c_id->is_firstchunk = B_FALSE;
+	} else {
+		ok = ((i = getaline(c_id, line, sizeof (line), B_FALSE)) == 0);
+	}
+
+	if (ok)
+		i = getaline(c_id, line, sizeof (line), B_FALSE);
+	if (!ok || i < 0) {
+		/*
+		 * If I/O error, the Cause was already put into
+		 * error stack.  This is an additional error.
+		 */
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOHEADER);
+		return (-1);
+	}
+
+	if (verbosemode)
+		libbootlog(BOOTLOG_VERBOSE, "get_chunk_header: <%s>", line);
+
+
+	/*
+	 * The first (and probably only) field in the line is the hex
+	 * length of the chunk.
+	 */
+	ptr = line;
+	value = 0;
+	while (*ptr != '\0' && (i = hexdigit(*ptr)) >= 0) {
+		value = (value << 4) + i;
+		ptr ++;
+	}
+
+	return (value);
+}
+
+/*
+ * init_bread - Initialize the counters used to read message bodies.
+ *
+ * Arguments:
+ *   c_id   - Structure describing the connection in question.
+ *
+ * Returns:
+ *   0	- Success
+ *  -1	- Error occurred.  The error information is in the error stack.
+ *
+ *  This routine will determine whether the message body being received is
+ *  chunked or non-chunked. Once determined, the counters used to read
+ *  message bodies will be initialized.
+ */
+static int
+init_bread(http_conn_t *c_id)
+{
+	char	*hdr;
+	char	*ptr;
+	boolean_t sized = B_FALSE;
+
+	/*
+	 * Assume non-chunked reads until proven otherwise.
+	 */
+	c_id->is_chunked = B_FALSE;
+	c_id->is_firstchunk = B_FALSE;
+	hdr = http_get_header_value(c_id, "Content-Length");
+	if (hdr != NULL) {
+		c_id->body_size = strtol(hdr, NULL, 10);
+		if (c_id->body_size == 0 && errno != 0) {
+			free(hdr);
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADSIZE);
+			return (-1);
+		}
+		free(hdr);
+		sized = B_TRUE;
+	}
+
+	/*
+	 * If size was not determined above, then see if this is a
+	 * chunked message. Keep in mind that the first chunk size is
+	 * "special".
+	 */
+	if (!sized) {
+		hdr = http_get_header_value(c_id, "Transfer-Encoding");
+		if (hdr != NULL) {
+			ptr = eat_ws(hdr);
+			if (startswith((const char **)&ptr, "chunked;") ||
+			    strcasecmp(ptr, "chunked") == 0) {
+				c_id->is_firstchunk = B_TRUE;
+				c_id->is_chunked = B_TRUE;
+			}
+			free(hdr);
+			if (c_id->is_chunked) {
+				c_id->body_size = get_chunk_header(c_id);
+				if (c_id->body_size == -1) {
+					/*
+					 * Error stack was already set at a
+					 * lower level.
+					 */
+					return (-1);
+				}
+				sized = B_TRUE;
+			}
+		}
+	}
+
+	/*
+	 * Well, isn't this a fine predicament? It wasn't chunked or
+	 * non-chunked as far as we can tell.
+	 */
+	if (!sized) {
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADSIZE);
+		return (-1);
+	}
+
+	c_id->body_read = 0;
+	c_id->body_size_tot = c_id->body_size;
+	c_id->body_read_tot = 0;
+
+	return (0);
+}
+
+/*
+ * get_msgcnt - Get the number of bytes left in the message body or chunk.
+ *
+ * Arguments:
+ *   c_id   - Structure describing the connection in question.
+ *   msgcnt - Where to store the message count.
+ *
+ * Returns:
+ *   0	- Success
+ *  -1	- Error occurred.  The error information is in the error stack.
+ *
+ *  Note that if the message being read is not chunked, then the byte count
+ *  is simply the message size minus the bytes read thus far. In the case of
+ *  chunked messages, the byte count returned will be the number of bytes
+ *  left in the chunk. If the current chunk has been exhausted, then this
+ *  routine will determine the size of the next chunk. When the next chunk
+ *  size is zero, the message has been read in its entirety.
+ */
+static int
+get_msgcnt(http_conn_t *c_id, ssize_t *msgcnt)
+{
+	/*
+	 * If there are more bytes in the message, then return.
+	 */
+	*msgcnt = c_id->body_size - c_id->body_read;
+	if (*msgcnt != 0) {
+		return (0);
+	}
+	/*
+	 * If this is not a chunked message and the body has been
+	 * read, then we're done.
+	 */
+	if (!c_id->is_chunked) {
+		return (0);
+	}
+
+	/*
+	 * We're looking at a chunked message whose immediate
+	 * chunk has been totally processed. See if there is
+	 * another chunk.
+	 */
+	c_id->body_size = get_chunk_header(c_id);
+	if (c_id->body_size == -1) {
+		/*
+		 * Error stack was already set at a
+		 * lower level.
+		 */
+		return (-1);
+	}
+
+	/*
+	 * No bytes of this chunk have been processed yet.
+	 */
+	c_id->body_read = 0;
+
+	/*
+	 * A zero length chunk signals the end of the
+	 * message body and chunking.
+	 */
+	if (c_id->body_size == 0) {
+		c_id->is_chunked = B_FALSE;
+		return (0);
+	}
+
+	/*
+	 * There is another chunk.
+	 */
+	c_id->body_size_tot += c_id->body_size;
+	*msgcnt = c_id->body_size - c_id->body_read;
+
+	return (0);
+}
+
+/*
+ * getaline - Get lines of data from the HTTP response, up to 'len' bytes.
+ *	  NOTE: the line will not end with a NULL if all 'len' bytes
+ *	  were read.
+ *
+ * Arguments:
+ *   c_id   - Structure describing the connection in question.
+ *   line   - Where to store the data.
+ *   len    - Maximum number of bytes in the line.
+ *   bread  - TRUE if the lines are part of the message body.
+ *
+ * Returns:
+ *   >=0    - The number of bytes successfully read.
+ *   <0	    - An error occurred.  This is (the number of bytes gotten + 1),
+ *	      negated.  In other words, if 'n' bytes were read and then an
+ *	      error occurred, this will return (-(n+1)).  So zero bytes read
+ *	      and then an error occurs, this will return -1.  If 1 bytes
+ *	      was read, it will return -2, etc.
+ *
+ *	      Specifics of the error can be gotten using http_get_lasterr();
+ *
+ *  Note that I/O errors are put into the error stack by http_srv_recv().1
+ */
+static int
+getaline(http_conn_t *c_id, char *line, int len, boolean_t bread)
+{
+	int	i = 0;
+	ssize_t	msgcnt = 0;
+	ssize_t	cnt;
+
+	while (i < len) {
+		/*
+		 * Special processing required for message body reads.
+		 */
+		if (bread) {
+			/*
+			 * See if there is another chunk. Obviously, in the
+			 * case of non-chunked messages, there won't be.
+			 * But in either case, chunked or not, if msgcnt
+			 * is still zero after the call to get_msgcnt(),
+			 * then we're done.
+			 */
+			if (msgcnt == 0) {
+				if (get_msgcnt(c_id, &msgcnt) == -1) {
+					return (-(i+1));
+				}
+				if (msgcnt == 0) {
+					break;
+				}
+			}
+			cnt = MIN(msgcnt, sizeof (c_id->inbuf.buf));
+		} else {
+			cnt = sizeof (c_id->inbuf.buf);
+		}
+
+		/* read more data if buffer empty */
+		if (c_id->inbuf.i == c_id->inbuf.n) {
+			c_id->inbuf.i = 0;
+			c_id->inbuf.n = http_srv_recv(c_id, c_id->inbuf.buf,
+			    cnt);
+			if (c_id->inbuf.n == 0) {
+				return (i);
+			}
+			if (c_id->inbuf.n < 0) {
+				return (-(i+1));
+			}
+		}
+		/* skip CR */
+		if (c_id->inbuf.buf[c_id->inbuf.i] == '\r') {
+			INC_BREAD_CNT(bread, msgcnt);
+			c_id->inbuf.i++;
+			continue;
+		}
+		if (c_id->inbuf.buf[c_id->inbuf.i] == '\n') {
+			INC_BREAD_CNT(bread, msgcnt);
+			c_id->inbuf.i++;
+			line[i] = '\0';
+			return (i);
+		}
+		/* copy buf from internal buffer */
+		INC_BREAD_CNT(bread, msgcnt);
+		line[i++] = c_id->inbuf.buf[c_id->inbuf.i++];
+	}
+	return (i);
+}
+
+/*
+ * getbytes - Get a block from the HTTP response. Used for the HTTP body.
+ *
+ * Arguments:
+ *   c_id   - Structure describing the connection in question.
+ *   line   - Where to store the data.
+ *   len    - Maximum number of bytes in the block.
+ *
+ * Returns:
+ *   >=0    - The number of bytes successfully read.
+ *   <0	    - An error occurred.  This is (the number of bytes gotten + 1),
+ *	      negated.  In other words, if 'n' bytes were read and then an
+ *	      error occurred, this will return (-(n+1)).  So zero bytes read
+ *	      and then an error occurs, this will return -1.  If 1 bytes
+ *	      was read, it will return -2, etc.
+ *
+ *	      Specifics of the error can be gotten using http_get_lasterr();
+ *
+ *  Note that all reads performed here assume that a message body is being
+ *  read. If this changes in the future, then the logic should more closely
+ *  resemble getaline().
+ *
+ *  Note that I/O errors are put into the error stack by http_srv_recv().
+ */
+static int
+getbytes(http_conn_t *c_id, char *line, int len)
+{
+	int	i = 0;
+	ssize_t	msgcnt = 0;
+	ssize_t	cnt;
+	int	nbytes;
+
+	while (i < len) {
+		/*
+		 * See if there is another chunk. Obviously, in the
+		 * case of non-chunked messages, there won't be.
+		 * But in either case, chunked or not, if msgcnt
+		 * is still zero after the call to get_msgcnt(), then
+		 * we're done.
+		 */
+		if (msgcnt == 0) {
+			if (get_msgcnt(c_id, &msgcnt) == -1) {
+				return (-(i+1));
+			}
+			if (msgcnt == 0) {
+				break;
+			}
+		}
+
+		cnt = MIN(msgcnt, len - i);
+
+		if (c_id->inbuf.n != c_id->inbuf.i) {
+			nbytes = (int)MIN(cnt, c_id->inbuf.n - c_id->inbuf.i);
+			(void) memcpy(line, &c_id->inbuf.buf[c_id->inbuf.i],
+			    nbytes);
+			c_id->inbuf.i += nbytes;
+		} else {
+			nbytes = http_srv_recv(c_id, line, cnt);
+			if (nbytes == 0) {
+				return (i);
+			}
+			if (nbytes < 0) {
+				return (-(i+1));
+			}
+		}
+
+		i += nbytes;
+		line += nbytes;
+		msgcnt -= nbytes;
+		c_id->body_read += nbytes;
+		c_id->body_read_tot += nbytes;
+	}
+
+	return (i);
+}
+
+static int
+http_srv_send(http_conn_t *c_id, const void *buf, size_t nbyte)
+{
+	int	retval;
+
+	if (c_id->ssl != NULL) {
+		if ((retval = SSL_write(c_id->ssl, buf, nbyte)) <= 0) {
+			handle_ssl_error(c_id, retval);
+		}
+		return (retval);
+	} else {
+		retval = socket_write(c_id->fd, buf, nbyte, &c_id->host_addr);
+		if (retval < 0) {
+			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+			return (-1);
+		}
+		return (retval);
+	}
+}
+
+static int
+http_srv_recv(http_conn_t *c_id, void *buf, size_t nbyte)
+{
+	int	retval;
+
+	if (c_id->ssl != NULL) {
+		if ((retval = SSL_read(c_id->ssl, buf, nbyte)) <= 0) {
+			handle_ssl_error(c_id, retval);
+		}
+		return (retval);
+	} else {
+		retval = socket_read(c_id->fd, buf, nbyte, c_id->read_timeout);
+		if (retval < 0) {
+			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+			return (-1);
+		}
+		return (retval);
+	}
+}
+
+static boolean_t
+http_check_conn(http_conn_t *c_id)
+{
+	early_err = 0;
+	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO) {
+		early_err = EHTTP_BADARG;
+		return (B_FALSE);
+	}
+	RESET_ERR(c_id);
+	return (B_TRUE);
+}
+
+static void
+handle_ssl_error(http_conn_t *c_id, int retval)
+{
+	ulong_t err;
+
+	err = SSL_get_error(c_id->ssl, retval);
+
+	switch (err) {
+	case SSL_ERROR_NONE:
+		return;
+
+	case SSL_ERROR_ZERO_RETURN:
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_CONCLOSED);
+		return;
+
+	case SSL_ERROR_WANT_READ:
+	case SSL_ERROR_WANT_WRITE:
+	case SSL_ERROR_WANT_CONNECT:
+	case SSL_ERROR_WANT_X509_LOOKUP:
+		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_UNEXPECTED);
+		return;
+
+	case SSL_ERROR_SYSCALL:
+		err = ERR_get_error();
+		if (err == 0)
+			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_EOFERR);
+		else if (err == (ulong_t)-1)
+			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
+		else {
+			SET_ERR(c_id, ERRSRC_LIBSSL, err);
+			while ((err = ERR_get_error()) != 0)
+				SET_ERR(c_id, ERRSRC_LIBSSL, err);
+		}
+		return;
+
+	case SSL_ERROR_SSL:
+		while ((err = ERR_get_error()) != 0) {
+			SET_ERR(c_id, ERRSRC_LIBSSL, err);
+		}
+		return;
+	}
+}
+
+static int
+count_digits(int value)
+{
+	int	count = 1;
+
+	if (value < 0) {
+		count++;
+		value = -value;
+	}
+
+	while (value > 9) {
+		value /= 10;
+		count++;
+	}
+	return (count);
+}
+
+static int
+hexdigit(char ch)
+{
+	if (ch >= '0' && ch <= '9')
+		return (ch - '0');
+	if (ch >= 'A' && ch <= 'F')
+		return (ch - 'A' + 10);
+	if (ch >= 'a' && ch <= 'f')
+		return (ch - 'a' + 10);
+	return (-1);
+}
+
+static char *
+eat_ws(const char *buf)
+{
+	char *ptr = (char *)buf;
+
+	while (isspace(*ptr))
+		ptr++;
+
+	return (ptr);
+}
+
+static boolean_t
+startswith(const char **strp, const char *starts)
+{
+	int len = strlen(starts);
+
+	if (strncasecmp(*strp, starts, len) == 0) {
+		*strp += len;
+		return (B_TRUE);
+	}
+	return (B_FALSE);
+}
