xref: /titanic_51/usr/src/common/net/wanboot/boot_http.c (revision d7141854234c22ab8fe0547bf51a2f3a30781870)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
523a1cceaSRoger A. Faulkner  * Common Development and Distribution License (the "License").
623a1cceaSRoger A. Faulkner  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate 
2223a1cceaSRoger A. Faulkner /*
2323a1cceaSRoger A. Faulkner  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
2470f9559bSTheo Schlossnagle  * Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
2523a1cceaSRoger A. Faulkner  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <errno.h>
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <sys/socket.h>
307c478bd9Sstevel@tonic-gate #include <netinet/in.h>
317c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
327c478bd9Sstevel@tonic-gate #include <ctype.h>
337c478bd9Sstevel@tonic-gate #include <stdio.h>
347c478bd9Sstevel@tonic-gate #include <strings.h>
357c478bd9Sstevel@tonic-gate #include <stdlib.h>
367c478bd9Sstevel@tonic-gate #include <netdb.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include <openssl/ssl.h>
397c478bd9Sstevel@tonic-gate #include <openssl/err.h>
407c478bd9Sstevel@tonic-gate #include <openssl/rand.h>
417c478bd9Sstevel@tonic-gate #include <openssl/pkcs12.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /* this must be included after ssl.h to avoid re-defining 'offsetof' */
447c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #include <boot_http.h>
477c478bd9Sstevel@tonic-gate #include <socket_inet.h>
487c478bd9Sstevel@tonic-gate #include <p12access.h>
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #include "bootlog.h"
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #define	BOOT_HTTP_MAJOR_VERSION	1
537c478bd9Sstevel@tonic-gate #define	BOOT_HTTP_MINOR_VERSION	0
547c478bd9Sstevel@tonic-gate #define	BOOT_HTTP_MICRO_VERSION	0
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate static boot_http_ver_t boot_http_ver = {
577c478bd9Sstevel@tonic-gate 	BOOT_HTTP_MAJOR_VERSION,
587c478bd9Sstevel@tonic-gate 	BOOT_HTTP_MINOR_VERSION,
597c478bd9Sstevel@tonic-gate 	BOOT_HTTP_MICRO_VERSION
607c478bd9Sstevel@tonic-gate };
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate static int	early_err;	/* Error from before error occurred */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate static boolean_t verbosemode = B_FALSE;
657c478bd9Sstevel@tonic-gate static char	*cipher_list = NULL; /* Ciphers supported (if not default) */
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate typedef struct {
687c478bd9Sstevel@tonic-gate 	int	i;		/* current position in buffer */
697c478bd9Sstevel@tonic-gate 	int	n;		/* number of bytes in buffer */
707c478bd9Sstevel@tonic-gate 	char	buf[512];	/* buffer */
717c478bd9Sstevel@tonic-gate } buf_struct_t;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate typedef struct {
747c478bd9Sstevel@tonic-gate 	uint_t	errsrc;		/* Source of this error */
757c478bd9Sstevel@tonic-gate 	ulong_t	error;		/* Which error? */
767c478bd9Sstevel@tonic-gate } errent_t;
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate typedef enum {
807c478bd9Sstevel@tonic-gate 	HTTP_REQ_TYPE_HEAD = 1,
817c478bd9Sstevel@tonic-gate 	HTTP_REQ_TYPE_GET
827c478bd9Sstevel@tonic-gate } http_req_t;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate #define	FAILSAFE 20		/* Max # empty lines to accept */
857c478bd9Sstevel@tonic-gate #define	DEFAULT_TIMEOUT	10	/* Default socket read timeout value */
867c478bd9Sstevel@tonic-gate #define	HTTP_CONN_INFO 0x90919293 /* Identifies a http_conn_t struct */
877c478bd9Sstevel@tonic-gate #define	ESTACK_SIZE	20	/* Size of the stack */
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate typedef struct http_conn_t {
907c478bd9Sstevel@tonic-gate 	uint_t	signature;	/* Cookie indicating this is a handle */
917c478bd9Sstevel@tonic-gate 	int	fd;		/* Connection's fd... */
927c478bd9Sstevel@tonic-gate 	SSL_CTX *ctx;
937c478bd9Sstevel@tonic-gate 	void	*ssl;		/* Handle to ssl data structure */
947c478bd9Sstevel@tonic-gate 	int	read_timeout;	/* Timeout to use on read requests in sec */
957c478bd9Sstevel@tonic-gate 	char    *basic_auth_userid;   /* Basic authentication user ID */
967c478bd9Sstevel@tonic-gate 	char   	*basic_auth_password; /* and password */
977c478bd9Sstevel@tonic-gate 	char	is_multipart;	/* B_TRUE if doing multipart/mixed download */
987c478bd9Sstevel@tonic-gate 	char	is_firstpart;	/* B_TRUE if first part in a multipart xfer */
997c478bd9Sstevel@tonic-gate 	char	is_firstchunk;	/* B_TRUE if first chunk in chunked xfer */
1007c478bd9Sstevel@tonic-gate 	char	is_chunked;	/* B_TRUE if message body is chunked */
1017c478bd9Sstevel@tonic-gate 	boolean_t keepalive;
1027c478bd9Sstevel@tonic-gate 	struct	sockaddr_in  host_addr; /* Address of host */
1037c478bd9Sstevel@tonic-gate 	url_t		uri;   		/* The current URI */
1047c478bd9Sstevel@tonic-gate 	url_hport_t	proxy;		/* The proxy info */
1057c478bd9Sstevel@tonic-gate 	boolean_t 	proxied;	/* Connection is proxied */
1067c478bd9Sstevel@tonic-gate 	char	*random_file;	/* File with seed info for pseudo random  */
1077c478bd9Sstevel@tonic-gate 				/* number generator */
1087c478bd9Sstevel@tonic-gate 	char	*client_cert_file;	/* File holding client's certificate */
1097c478bd9Sstevel@tonic-gate 	char	*private_key_file;	/* File with the private key */
1107c478bd9Sstevel@tonic-gate 	char	*file_password;	/* file with password to key or pkcs12 file. */
1117c478bd9Sstevel@tonic-gate 	http_respinfo_t resp;	/* Response summary info */
1127c478bd9Sstevel@tonic-gate 	char	**resphdr;	/* Array of header response lines */
1137c478bd9Sstevel@tonic-gate 	buf_struct_t inbuf;
1147c478bd9Sstevel@tonic-gate 	char	*boundary;	/* Boundary text (multipart downloads only) */
1157c478bd9Sstevel@tonic-gate 	uint_t	boundary_len;	/* Length of boundary string */
1167c478bd9Sstevel@tonic-gate 	uint_t	numerrs;
1177c478bd9Sstevel@tonic-gate 	uint_t	nexterr;	/* Next error to return */
1187c478bd9Sstevel@tonic-gate 	ssize_t	body_size;	/* Size of message body or chunk */
1197c478bd9Sstevel@tonic-gate 	ssize_t	body_read;	/* # of bytes of body_size processed */
1207c478bd9Sstevel@tonic-gate 	ssize_t	body_size_tot;	/* Total message body size */
1217c478bd9Sstevel@tonic-gate 	ssize_t	body_read_tot;	/* # of bytes of body_size_tot processed */
1227c478bd9Sstevel@tonic-gate 	errent_t errs[ESTACK_SIZE]; /* stack of errors on the last request */
1237c478bd9Sstevel@tonic-gate 				/* (libssl can return multiple errors on one */
1247c478bd9Sstevel@tonic-gate 				/* operation) */
1257c478bd9Sstevel@tonic-gate } http_conn_t;
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate /*
1287c478bd9Sstevel@tonic-gate  * Convenient macros for accessing fields in connection structure.
1297c478bd9Sstevel@tonic-gate  */
1307c478bd9Sstevel@tonic-gate #define	CONN_HOSTNAME		c_id->uri.hport.hostname
1317c478bd9Sstevel@tonic-gate #define	CONN_PORT		c_id->uri.hport.port
1327c478bd9Sstevel@tonic-gate #define	CONN_ABSPATH		c_id->uri.abspath
1337c478bd9Sstevel@tonic-gate #define	CONN_HTTPS		c_id->uri.https
1347c478bd9Sstevel@tonic-gate #define	CONN_PROXY_HOSTNAME	c_id->proxy.hostname
1357c478bd9Sstevel@tonic-gate #define	CONN_PROXY_PORT		c_id->proxy.port
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate #define	RESET_ERR(c_id)	(c_id)->numerrs = 0, (c_id)->nexterr = 0
1387c478bd9Sstevel@tonic-gate #define	SET_ERR(c_id, src, err)	if ((c_id)->numerrs < ESTACK_SIZE) \
1397c478bd9Sstevel@tonic-gate 		(c_id)->errs[(c_id)->numerrs].errsrc = (src), \
1407c478bd9Sstevel@tonic-gate 		(c_id)->errs[(c_id)->numerrs ++].error = (err)
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate #define	GET_ERR(c_id, e_src, e_code) \
1437c478bd9Sstevel@tonic-gate 		if ((c_id)->nexterr < (c_id)->numerrs) \
1447c478bd9Sstevel@tonic-gate 			(e_src) = (c_id)->errs[((c_id)->nexterr)].errsrc, \
1457c478bd9Sstevel@tonic-gate 			(e_code) = (c_id)->errs[((c_id)->nexterr)++].error; \
1467c478bd9Sstevel@tonic-gate 		else \
1477c478bd9Sstevel@tonic-gate 			(e_src) = 0, (e_code) = 0
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate /*
1507c478bd9Sstevel@tonic-gate  * Macro used to increment message body read counters
1517c478bd9Sstevel@tonic-gate  */
1527c478bd9Sstevel@tonic-gate #define	INC_BREAD_CNT(bool, bcnt) \
1537c478bd9Sstevel@tonic-gate 	if (bool) { \
1547c478bd9Sstevel@tonic-gate 		bcnt--; \
1557c478bd9Sstevel@tonic-gate 		c_id->body_read++;\
1567c478bd9Sstevel@tonic-gate 		c_id->body_read_tot++; \
1577c478bd9Sstevel@tonic-gate 	}
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate static int	ssl_init = 0;		/* 1 when ssl has been initialized */
1607c478bd9Sstevel@tonic-gate static char	*ca_verify_file;	/* List of trusted CA's  */
1617c478bd9Sstevel@tonic-gate static int	verify_depth = 16;	/* Certificate chain depth to verify */
1627c478bd9Sstevel@tonic-gate static int	p12_format = 0;		/* Default to PEM format */
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate /* prototypes for local functions */
166cb4c2b01Svh115876 static int	http_req(http_handle_t, const char *, http_req_t, offset_t,
167cb4c2b01Svh115876     offset_t);
1687c478bd9Sstevel@tonic-gate static boolean_t http_check_conn(http_conn_t *);
1697c478bd9Sstevel@tonic-gate static SSL_CTX *initialize_ctx(http_conn_t *);
1707c478bd9Sstevel@tonic-gate static int	tcp_connect(http_conn_t *, const char *, uint16_t);
1717c478bd9Sstevel@tonic-gate static int	readline(http_conn_t *, int, char *, int);
1727c478bd9Sstevel@tonic-gate static int	proxy_connect(http_conn_t *);
1737c478bd9Sstevel@tonic-gate static int	check_cert_chain(http_conn_t *, char *);
1747c478bd9Sstevel@tonic-gate static void	print_ciphers(SSL *);
1757c478bd9Sstevel@tonic-gate static int	read_headerlines(http_conn_t *, boolean_t);
1767c478bd9Sstevel@tonic-gate static void	free_response(http_conn_t *, int);
1777c478bd9Sstevel@tonic-gate static int	free_ctx_ssl(http_conn_t *);
1787c478bd9Sstevel@tonic-gate static int	get_chunk_header(http_conn_t *);
1797c478bd9Sstevel@tonic-gate static int	init_bread(http_conn_t *);
1807c478bd9Sstevel@tonic-gate static int	get_msgcnt(http_conn_t *, ssize_t *);
18123a1cceaSRoger A. Faulkner static int	getaline(http_conn_t *, char *, int, boolean_t);
1827c478bd9Sstevel@tonic-gate static int	getbytes(http_conn_t *, char *, int);
1837c478bd9Sstevel@tonic-gate static int	http_srv_send(http_conn_t *, const void *, size_t);
1847c478bd9Sstevel@tonic-gate static int	http_srv_recv(http_conn_t *, void *, size_t);
1857c478bd9Sstevel@tonic-gate static void	handle_ssl_error(http_conn_t *, int);
1867c478bd9Sstevel@tonic-gate static int	count_digits(int);
1877c478bd9Sstevel@tonic-gate static int	hexdigit(char);
1887c478bd9Sstevel@tonic-gate static char	*eat_ws(const char *);
1897c478bd9Sstevel@tonic-gate static boolean_t startswith(const char **strp, const char *starts);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate /* ---------------------- public functions ----------------------- */
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate /*
1947c478bd9Sstevel@tonic-gate  * http_set_p12_format - Set flag indicating that certs & keys will be in
1957c478bd9Sstevel@tonic-gate  *                    pkcs12 format.
1967c478bd9Sstevel@tonic-gate  *
1977c478bd9Sstevel@tonic-gate  * Default is PEM certs.  When this is called, the default can be changed to
1987c478bd9Sstevel@tonic-gate  * pcs12 format.
1997c478bd9Sstevel@tonic-gate  */
2007c478bd9Sstevel@tonic-gate void
2017c478bd9Sstevel@tonic-gate http_set_p12_format(int on_off)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate 	p12_format = on_off;
2047c478bd9Sstevel@tonic-gate }
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate  * http_get_version - Get current boot http support version
2087c478bd9Sstevel@tonic-gate  *
2097c478bd9Sstevel@tonic-gate  *     pVer = http_get_version();
2107c478bd9Sstevel@tonic-gate  *
2117c478bd9Sstevel@tonic-gate  * Arguments:
2127c478bd9Sstevel@tonic-gate  *	None.
2137c478bd9Sstevel@tonic-gate  *
2147c478bd9Sstevel@tonic-gate  * Returns:
2157c478bd9Sstevel@tonic-gate  *	Pointer to struct with version information.
2167c478bd9Sstevel@tonic-gate  *
2177c478bd9Sstevel@tonic-gate  * Returns the version of the http support in the current library.  This
2187c478bd9Sstevel@tonic-gate  * is a struct with unsigned integsrs for <major>, <minor> and
2197c478bd9Sstevel@tonic-gate  * <micro> version numbers.  <major> changes when an incompatible change
2207c478bd9Sstevel@tonic-gate  * is made.  <minor> changes when an upwardly-compatible API change is
2217c478bd9Sstevel@tonic-gate  * made.  <micro> consists of bug fixes, etc.
2227c478bd9Sstevel@tonic-gate  */
2237c478bd9Sstevel@tonic-gate boot_http_ver_t const *
2247c478bd9Sstevel@tonic-gate http_get_version(void)
2257c478bd9Sstevel@tonic-gate {
2267c478bd9Sstevel@tonic-gate 	return (&boot_http_ver);
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate /*
2307c478bd9Sstevel@tonic-gate  * http_set_verbose - Turn verbose on/off
2317c478bd9Sstevel@tonic-gate  *
2327c478bd9Sstevel@tonic-gate  *     http_set_verbose(on_off);
2337c478bd9Sstevel@tonic-gate  *
2347c478bd9Sstevel@tonic-gate  * Arguments:
2357c478bd9Sstevel@tonic-gate  *	on_off	- When TRUE, turn verbose mode one.  When FALSE, turn
2367c478bd9Sstevel@tonic-gate  *		  verbose off.
2377c478bd9Sstevel@tonic-gate  *
2387c478bd9Sstevel@tonic-gate  * Returns:
2397c478bd9Sstevel@tonic-gate  *	None.
2407c478bd9Sstevel@tonic-gate  *
2417c478bd9Sstevel@tonic-gate  * When enabled, information is logged to bootlog (or the Solaris equivalent).
2427c478bd9Sstevel@tonic-gate  */
2437c478bd9Sstevel@tonic-gate void
2447c478bd9Sstevel@tonic-gate http_set_verbose(boolean_t on_off)
2457c478bd9Sstevel@tonic-gate {
2467c478bd9Sstevel@tonic-gate 	verbosemode = on_off;
2477c478bd9Sstevel@tonic-gate }
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate /*
2507c478bd9Sstevel@tonic-gate  * http_set_cipher_list - Change the list of ciphers that can be used.
2517c478bd9Sstevel@tonic-gate  *
2527c478bd9Sstevel@tonic-gate  *     ret = http_set_cipher_list(handle, list);
2537c478bd9Sstevel@tonic-gate  *
2547c478bd9Sstevel@tonic-gate  * Arguments:
2557c478bd9Sstevel@tonic-gate  *	list	- List of ciphers that can be used.
2567c478bd9Sstevel@tonic-gate  *
2577c478bd9Sstevel@tonic-gate  * Returns:
2587c478bd9Sstevel@tonic-gate  *	0	- Success
2597c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
2607c478bd9Sstevel@tonic-gate  */
2617c478bd9Sstevel@tonic-gate int
2627c478bd9Sstevel@tonic-gate http_set_cipher_list(const char *list)
2637c478bd9Sstevel@tonic-gate {
2647c478bd9Sstevel@tonic-gate 	early_err = 0;
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	if (list != NULL) {
2677c478bd9Sstevel@tonic-gate 		list = strdup(list);
2687c478bd9Sstevel@tonic-gate 		if (list == NULL) {
2697c478bd9Sstevel@tonic-gate 			early_err = EHTTP_NOMEM;
2707c478bd9Sstevel@tonic-gate 			return (-1);
2717c478bd9Sstevel@tonic-gate 		}
2727c478bd9Sstevel@tonic-gate 	}
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	free(cipher_list);
2757c478bd9Sstevel@tonic-gate 	cipher_list = (char *)list;
2767c478bd9Sstevel@tonic-gate 	return (0);
2777c478bd9Sstevel@tonic-gate }
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate /*
2807c478bd9Sstevel@tonic-gate  * http_srv_init - Set up a structure for a connection.
2817c478bd9Sstevel@tonic-gate  *
2827c478bd9Sstevel@tonic-gate  *     handle = http_srv_init(url);
2837c478bd9Sstevel@tonic-gate  *
2847c478bd9Sstevel@tonic-gate  * Arguments:
2857c478bd9Sstevel@tonic-gate  *	url - the structure that contains the URI.
2867c478bd9Sstevel@tonic-gate  *
2877c478bd9Sstevel@tonic-gate  * Returns:
2887c478bd9Sstevel@tonic-gate  *	!= NULL	- A handle for referring to this connection.
2897c478bd9Sstevel@tonic-gate  *	== NULL - An error occurred.  Get the exact error from
2907c478bd9Sstevel@tonic-gate  *                http_get_lasterr().
2917c478bd9Sstevel@tonic-gate  */
2927c478bd9Sstevel@tonic-gate http_handle_t
2937c478bd9Sstevel@tonic-gate http_srv_init(const url_t *url)
2947c478bd9Sstevel@tonic-gate {
2957c478bd9Sstevel@tonic-gate 	http_conn_t	*c_id;
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	early_err = 0;
2987c478bd9Sstevel@tonic-gate 	if (url == NULL) {
2997c478bd9Sstevel@tonic-gate 		early_err = EHTTP_BADARG;
3007c478bd9Sstevel@tonic-gate 		return (NULL);
3017c478bd9Sstevel@tonic-gate 	}
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	if ((c_id = malloc(sizeof (*c_id))) == NULL) {
3047c478bd9Sstevel@tonic-gate 		early_err = EHTTP_NOMEM;
3057c478bd9Sstevel@tonic-gate 		return (NULL);
3067c478bd9Sstevel@tonic-gate 	}
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	bzero(c_id, sizeof (*c_id));
3097c478bd9Sstevel@tonic-gate 	c_id->uri = *url;
3107c478bd9Sstevel@tonic-gate 	c_id->proxied = B_FALSE;
3117c478bd9Sstevel@tonic-gate 	c_id->read_timeout = DEFAULT_TIMEOUT;
3127c478bd9Sstevel@tonic-gate 	c_id->keepalive = B_TRUE;
3137c478bd9Sstevel@tonic-gate 	c_id->fd = -1;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	/* Do this at the end, just in case.... */
3167c478bd9Sstevel@tonic-gate 	c_id->signature = HTTP_CONN_INFO;
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	return (c_id);
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate /*
3227c478bd9Sstevel@tonic-gate  * http_conn_is_https - Determine whether the scheme is http or https.
3237c478bd9Sstevel@tonic-gate  *
3247c478bd9Sstevel@tonic-gate  *	B_TRUE	- Connection is an SSL connection.
3257c478bd9Sstevel@tonic-gate  *	B_FALSE - Connection isn't SSL.
3267c478bd9Sstevel@tonic-gate  *
3277c478bd9Sstevel@tonic-gate  *     ret = http_conn_is_https(handle, boolean_t *bool);
3287c478bd9Sstevel@tonic-gate  *
3297c478bd9Sstevel@tonic-gate  * Arguments:
3307c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
3317c478bd9Sstevel@tonic-gate  *	bool	- Ptr to boolean in which to place result
3327c478bd9Sstevel@tonic-gate  *
3337c478bd9Sstevel@tonic-gate  * Returns:
3347c478bd9Sstevel@tonic-gate  *	0	- Success
3357c478bd9Sstevel@tonic-gate  *	-1	- Some error occurred.
3367c478bd9Sstevel@tonic-gate  */
3377c478bd9Sstevel@tonic-gate int
3387c478bd9Sstevel@tonic-gate http_conn_is_https(http_handle_t handle, boolean_t *bool)
3397c478bd9Sstevel@tonic-gate {
3407c478bd9Sstevel@tonic-gate 	http_conn_t	*c_id = handle;
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
3437c478bd9Sstevel@tonic-gate 		return (-1);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	*bool = CONN_HTTPS;
3467c478bd9Sstevel@tonic-gate 	return (0);
3477c478bd9Sstevel@tonic-gate }
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate /*
3507c478bd9Sstevel@tonic-gate  * http_set_proxy - Establish the proxy name/port.
3517c478bd9Sstevel@tonic-gate  *
3527c478bd9Sstevel@tonic-gate  *     ret = http_set_proxy(handle, proxy);
3537c478bd9Sstevel@tonic-gate  *
3547c478bd9Sstevel@tonic-gate  * Arguments:
3557c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
3567c478bd9Sstevel@tonic-gate  *	proxy	- The hostport definition for the proxy. If NULL,
3577c478bd9Sstevel@tonic-gate  *		  The next connect will not use a proxy.
3587c478bd9Sstevel@tonic-gate  *
3597c478bd9Sstevel@tonic-gate  * Returns:
3607c478bd9Sstevel@tonic-gate  *	0	- Success
3617c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
3627c478bd9Sstevel@tonic-gate  */
3637c478bd9Sstevel@tonic-gate int
3647c478bd9Sstevel@tonic-gate http_set_proxy(http_handle_t handle, const url_hport_t *proxy)
3657c478bd9Sstevel@tonic-gate {
3667c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
3697c478bd9Sstevel@tonic-gate 		return (-1);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if (proxy != NULL) {
3727c478bd9Sstevel@tonic-gate 		c_id->proxy = *proxy;
3737c478bd9Sstevel@tonic-gate 		c_id->proxied = B_TRUE;
3747c478bd9Sstevel@tonic-gate 	} else {
3757c478bd9Sstevel@tonic-gate 		CONN_PROXY_HOSTNAME[0] = '\0';
3767c478bd9Sstevel@tonic-gate 		c_id->proxied = B_FALSE;
3777c478bd9Sstevel@tonic-gate 	}
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	return (0);
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate /*
3837c478bd9Sstevel@tonic-gate  * http_set_keepalive - Set keepalive for this connection.
3847c478bd9Sstevel@tonic-gate  *
3857c478bd9Sstevel@tonic-gate  *     http_set_keepalive(handle, on_off);
3867c478bd9Sstevel@tonic-gate  *
3877c478bd9Sstevel@tonic-gate  * Arguments:
3887c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
3897c478bd9Sstevel@tonic-gate  *	on_off	- Boolean turning keepalive on (TRUE) or off (FALSE)
3907c478bd9Sstevel@tonic-gate  *
3917c478bd9Sstevel@tonic-gate  * Returns:
3927c478bd9Sstevel@tonic-gate  *	0	- Success.
3937c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
3947c478bd9Sstevel@tonic-gate  *
3957c478bd9Sstevel@tonic-gate  * This setting takes effect next time a connection is opened using this
3967c478bd9Sstevel@tonic-gate  * handle.
3977c478bd9Sstevel@tonic-gate  */
3987c478bd9Sstevel@tonic-gate int
3997c478bd9Sstevel@tonic-gate http_set_keepalive(http_handle_t handle, boolean_t on_off)
4007c478bd9Sstevel@tonic-gate {
4017c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
4047c478bd9Sstevel@tonic-gate 		return (-1);
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	c_id->keepalive = on_off;
4077c478bd9Sstevel@tonic-gate 	return (0);
4087c478bd9Sstevel@tonic-gate }
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate /*
4117c478bd9Sstevel@tonic-gate  * http_set_socket_read_timeout - Set the timeout reads
4127c478bd9Sstevel@tonic-gate  *
4137c478bd9Sstevel@tonic-gate  *     http_set_socket_read_timeout(handle, timeout);
4147c478bd9Sstevel@tonic-gate  *
4157c478bd9Sstevel@tonic-gate  * Arguments:
4167c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
4177c478bd9Sstevel@tonic-gate  *	timeout	- Timeout, in seconds.  Zero will default to 10 second
4187c478bd9Sstevel@tonic-gate  *		  timeouts.
4197c478bd9Sstevel@tonic-gate  *
4207c478bd9Sstevel@tonic-gate  * Returns:
4217c478bd9Sstevel@tonic-gate  *	0	- Success.
4227c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
4237c478bd9Sstevel@tonic-gate  *
4247c478bd9Sstevel@tonic-gate  * This setting takes effect beginning with the next read operation on this
4257c478bd9Sstevel@tonic-gate  * connection.
4267c478bd9Sstevel@tonic-gate  */
4277c478bd9Sstevel@tonic-gate int
4287c478bd9Sstevel@tonic-gate http_set_socket_read_timeout(http_handle_t handle, uint_t timout)
4297c478bd9Sstevel@tonic-gate {
4307c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
4337c478bd9Sstevel@tonic-gate 		return (-1);
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	c_id->read_timeout = (timout) ? timout : DEFAULT_TIMEOUT;
4367c478bd9Sstevel@tonic-gate 	return (0);
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate /*
4407c478bd9Sstevel@tonic-gate  * http_set_basic_auth - Set the basic authorization user ID and password
4417c478bd9Sstevel@tonic-gate  *
4427c478bd9Sstevel@tonic-gate  *     ret = http_set_basic_auth(handle, userid, password);
4437c478bd9Sstevel@tonic-gate  *
4447c478bd9Sstevel@tonic-gate  * Arguments:
4457c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
4467c478bd9Sstevel@tonic-gate  *	userid	- ID to pass as part of http/https request
4477c478bd9Sstevel@tonic-gate  *	password- Password which goes with the user ID
4487c478bd9Sstevel@tonic-gate  *
4497c478bd9Sstevel@tonic-gate  * Returns:
4507c478bd9Sstevel@tonic-gate  *	0	- Success
4517c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
4527c478bd9Sstevel@tonic-gate  *
4537c478bd9Sstevel@tonic-gate  * This must be set before a https connection is made.
4547c478bd9Sstevel@tonic-gate  */
4557c478bd9Sstevel@tonic-gate int
4567c478bd9Sstevel@tonic-gate http_set_basic_auth(http_handle_t handle, const char *userid,
4577c478bd9Sstevel@tonic-gate     const char *password)
4587c478bd9Sstevel@tonic-gate {
4597c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
4627c478bd9Sstevel@tonic-gate 		return (-1);
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	if (password == NULL || userid == NULL || userid[0] == '\0') {
4657c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
4667c478bd9Sstevel@tonic-gate 		return (-1);
4677c478bd9Sstevel@tonic-gate 	}
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	userid = strdup(userid);
4707c478bd9Sstevel@tonic-gate 	password = strdup(password);
4717c478bd9Sstevel@tonic-gate 	if (userid == NULL || password == NULL) {
4727c478bd9Sstevel@tonic-gate 		free((void *)userid);
4737c478bd9Sstevel@tonic-gate 		free((void *)password);
4747c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
4757c478bd9Sstevel@tonic-gate 		return (-1);
4767c478bd9Sstevel@tonic-gate 	}
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	free(c_id->basic_auth_userid);
4797c478bd9Sstevel@tonic-gate 	c_id->basic_auth_userid = (char *)userid;
4807c478bd9Sstevel@tonic-gate 	free(c_id->basic_auth_password);
4817c478bd9Sstevel@tonic-gate 	c_id->basic_auth_password = (char *)password;
4827c478bd9Sstevel@tonic-gate 	return (0);
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate /*
4867c478bd9Sstevel@tonic-gate  * http_set_random_file - See the pseudo random number generator with file data
4877c478bd9Sstevel@tonic-gate  *
4887c478bd9Sstevel@tonic-gate  *     ret = http_set_random_file(handle, filename);
4897c478bd9Sstevel@tonic-gate  *
4907c478bd9Sstevel@tonic-gate  * Arguments:
4917c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
4927c478bd9Sstevel@tonic-gate  *	filename
4937c478bd9Sstevel@tonic-gate  *		- filename (including path) with random number seed.
4947c478bd9Sstevel@tonic-gate  *
4957c478bd9Sstevel@tonic-gate  * Returns:
4967c478bd9Sstevel@tonic-gate  *	0	- Success
4977c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
4987c478bd9Sstevel@tonic-gate  *
4997c478bd9Sstevel@tonic-gate  * This must be set before a https connection is made.
5007c478bd9Sstevel@tonic-gate  */
5017c478bd9Sstevel@tonic-gate int
5027c478bd9Sstevel@tonic-gate http_set_random_file(http_handle_t handle, const char *fname)
5037c478bd9Sstevel@tonic-gate {
5047c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
5077c478bd9Sstevel@tonic-gate 		return (-1);
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	if (fname != NULL) {
5107c478bd9Sstevel@tonic-gate 		fname = strdup(fname);
5117c478bd9Sstevel@tonic-gate 		if (fname == NULL) {
5127c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
5137c478bd9Sstevel@tonic-gate 			return (-1);
5147c478bd9Sstevel@tonic-gate 		}
5157c478bd9Sstevel@tonic-gate 	}
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	free(c_id->random_file);
5187c478bd9Sstevel@tonic-gate 	c_id->random_file = (char *)fname;
5197c478bd9Sstevel@tonic-gate 	return (0);
5207c478bd9Sstevel@tonic-gate }
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate /*
5237c478bd9Sstevel@tonic-gate  * http_set_certificate_authority_file - Set the CA file.
5247c478bd9Sstevel@tonic-gate  *
5257c478bd9Sstevel@tonic-gate  *     ret = http_set_certificate_authority_file(filename);
5267c478bd9Sstevel@tonic-gate  *
5277c478bd9Sstevel@tonic-gate  * Arguments:
5287c478bd9Sstevel@tonic-gate  *	filename- File with the certificate authority certs
5297c478bd9Sstevel@tonic-gate  *
5307c478bd9Sstevel@tonic-gate  * Returns:
5317c478bd9Sstevel@tonic-gate  *	0	- Success
5327c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
5337c478bd9Sstevel@tonic-gate  *
5347c478bd9Sstevel@tonic-gate  * This must be set before https connections to the servers is done.
5357c478bd9Sstevel@tonic-gate  */
5367c478bd9Sstevel@tonic-gate int
5377c478bd9Sstevel@tonic-gate http_set_certificate_authority_file(const char *fname)
5387c478bd9Sstevel@tonic-gate {
5397c478bd9Sstevel@tonic-gate 	early_err = 0;
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	if (fname != NULL) {
5427c478bd9Sstevel@tonic-gate 		fname = strdup(fname);
5437c478bd9Sstevel@tonic-gate 		if (fname == NULL) {
5447c478bd9Sstevel@tonic-gate 			early_err = EHTTP_NOMEM;
5457c478bd9Sstevel@tonic-gate 			return (-1);
5467c478bd9Sstevel@tonic-gate 		}
5477c478bd9Sstevel@tonic-gate 	}
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	free(ca_verify_file);
5507c478bd9Sstevel@tonic-gate 	ca_verify_file = (char *)fname;
5517c478bd9Sstevel@tonic-gate 	return (0);
5527c478bd9Sstevel@tonic-gate }
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate /*
5557c478bd9Sstevel@tonic-gate  * http_set_client_certificate_file - Set the file containing the PKCS#12
5567c478bd9Sstevel@tonic-gate  *		client certificate and optionally its certificate chain.
5577c478bd9Sstevel@tonic-gate  *
5587c478bd9Sstevel@tonic-gate  *     ret = http_set_client_certificate_file(handle, filename);
5597c478bd9Sstevel@tonic-gate  *
5607c478bd9Sstevel@tonic-gate  * Arguments:
5617c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
5627c478bd9Sstevel@tonic-gate  *	filename- File (including path) containing certificate, etc.
5637c478bd9Sstevel@tonic-gate  *
5647c478bd9Sstevel@tonic-gate  * Returns:
5657c478bd9Sstevel@tonic-gate  *	0	- Success
5667c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
5677c478bd9Sstevel@tonic-gate  *
5687c478bd9Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection
5697c478bd9Sstevel@tonic-gate  * which will require a client certificate.
5707c478bd9Sstevel@tonic-gate  */
5717c478bd9Sstevel@tonic-gate int
5727c478bd9Sstevel@tonic-gate http_set_client_certificate_file(http_handle_t handle, const char *fname)
5737c478bd9Sstevel@tonic-gate {
5747c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
5777c478bd9Sstevel@tonic-gate 		return (-1);
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	if (fname != NULL) {
5807c478bd9Sstevel@tonic-gate 		fname = strdup(fname);
5817c478bd9Sstevel@tonic-gate 		if (fname == NULL) {
5827c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
5837c478bd9Sstevel@tonic-gate 			return (-1);
5847c478bd9Sstevel@tonic-gate 		}
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	free(c_id->client_cert_file);
5887c478bd9Sstevel@tonic-gate 	c_id->client_cert_file = (char *)fname;
5897c478bd9Sstevel@tonic-gate 	return (0);
5907c478bd9Sstevel@tonic-gate }
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate /*
5937c478bd9Sstevel@tonic-gate  * http_set_password - Set the password for the private key or pkcs12 file.
5947c478bd9Sstevel@tonic-gate  *
5957c478bd9Sstevel@tonic-gate  *     ret = http_set_password(handle, password);
5967c478bd9Sstevel@tonic-gate  *
5977c478bd9Sstevel@tonic-gate  * Arguments:
5987c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
5997c478bd9Sstevel@tonic-gate  *	password- Password for the client's private key file or pkcs12 file.
6007c478bd9Sstevel@tonic-gate  *
6017c478bd9Sstevel@tonic-gate  * Returns:
6027c478bd9Sstevel@tonic-gate  *	0	- Success
6037c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
6047c478bd9Sstevel@tonic-gate  *
6057c478bd9Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection.
6067c478bd9Sstevel@tonic-gate  */
6077c478bd9Sstevel@tonic-gate int
6087c478bd9Sstevel@tonic-gate http_set_password(http_handle_t handle, const char *password)
6097c478bd9Sstevel@tonic-gate {
6107c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
6137c478bd9Sstevel@tonic-gate 		return (-1);
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	if (password != NULL) {
6167c478bd9Sstevel@tonic-gate 		password = strdup(password);
6177c478bd9Sstevel@tonic-gate 		if (password == NULL) {
6187c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
6197c478bd9Sstevel@tonic-gate 			return (-1);
6207c478bd9Sstevel@tonic-gate 		}
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	free(c_id->file_password);
6247c478bd9Sstevel@tonic-gate 	c_id->file_password = (char *)password;
6257c478bd9Sstevel@tonic-gate 	return (0);
6267c478bd9Sstevel@tonic-gate }
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate /*
6297c478bd9Sstevel@tonic-gate  * http_set_key_file_password - Set the password for the private key
6307c478bd9Sstevel@tonic-gate  *		file.
6317c478bd9Sstevel@tonic-gate  *
6327c478bd9Sstevel@tonic-gate  *     ret = http_set_key_file_password(handle, password);
6337c478bd9Sstevel@tonic-gate  *
6347c478bd9Sstevel@tonic-gate  * Arguments:
6357c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
6367c478bd9Sstevel@tonic-gate  *	password- Password for the client's private key file.
6377c478bd9Sstevel@tonic-gate  *
6387c478bd9Sstevel@tonic-gate  * Returns:
6397c478bd9Sstevel@tonic-gate  *	0	- Success
6407c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
6417c478bd9Sstevel@tonic-gate  *
6427c478bd9Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection.
6437c478bd9Sstevel@tonic-gate  */
6447c478bd9Sstevel@tonic-gate int
6457c478bd9Sstevel@tonic-gate http_set_key_file_password(http_handle_t handle, const char *password)
6467c478bd9Sstevel@tonic-gate {
6477c478bd9Sstevel@tonic-gate 	return (http_set_password(handle, password));
6487c478bd9Sstevel@tonic-gate }
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate /*
6517c478bd9Sstevel@tonic-gate  * http_set_private_key_file - Set the file containing the PKCS#12
6527c478bd9Sstevel@tonic-gate  *		private key for this client.
6537c478bd9Sstevel@tonic-gate  *
6547c478bd9Sstevel@tonic-gate  *     ret = http_set_private_key_file(handle, filename);
6557c478bd9Sstevel@tonic-gate  *
6567c478bd9Sstevel@tonic-gate  * Arguments:
6577c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
6587c478bd9Sstevel@tonic-gate  *	filename- File (including path) containing the private key.
6597c478bd9Sstevel@tonic-gate  *
6607c478bd9Sstevel@tonic-gate  * Returns:
6617c478bd9Sstevel@tonic-gate  *	0	- Success
6627c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
6637c478bd9Sstevel@tonic-gate  *
6647c478bd9Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection.
6657c478bd9Sstevel@tonic-gate  */
6667c478bd9Sstevel@tonic-gate int
6677c478bd9Sstevel@tonic-gate http_set_private_key_file(http_handle_t handle, const char *fname)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
6727c478bd9Sstevel@tonic-gate 		return (-1);
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	if (fname != NULL) {
6757c478bd9Sstevel@tonic-gate 		fname = strdup(fname);
6767c478bd9Sstevel@tonic-gate 		if (fname == NULL) {
6777c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
6787c478bd9Sstevel@tonic-gate 			return (-1);
6797c478bd9Sstevel@tonic-gate 		}
6807c478bd9Sstevel@tonic-gate 	}
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate 	free(c_id->private_key_file);
6837c478bd9Sstevel@tonic-gate 	c_id->private_key_file = (char *)fname;
6847c478bd9Sstevel@tonic-gate 	return (0);
6857c478bd9Sstevel@tonic-gate }
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate /*
6887c478bd9Sstevel@tonic-gate  * http_srv_connect - Establish a connection to the server
6897c478bd9Sstevel@tonic-gate  *
6907c478bd9Sstevel@tonic-gate  *     ret = http_srv_connect(handle);
6917c478bd9Sstevel@tonic-gate  *
6927c478bd9Sstevel@tonic-gate  * Arguments:
6937c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
6947c478bd9Sstevel@tonic-gate  *
6957c478bd9Sstevel@tonic-gate  * Returns:
6967c478bd9Sstevel@tonic-gate  *	0	- Success
6977c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr() for specifics.
6987c478bd9Sstevel@tonic-gate  */
6997c478bd9Sstevel@tonic-gate int
7007c478bd9Sstevel@tonic-gate http_srv_connect(http_handle_t handle)
7017c478bd9Sstevel@tonic-gate {
7027c478bd9Sstevel@tonic-gate 	http_conn_t	*c_id = handle;
7037c478bd9Sstevel@tonic-gate 	SSL_CTX		*ctx = NULL;
7047c478bd9Sstevel@tonic-gate 	int		retval;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	ERR_clear_error();
7077c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
7087c478bd9Sstevel@tonic-gate 		return (-1);
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 	if (CONN_HTTPS) {
7117c478bd9Sstevel@tonic-gate 		/* Build our SSL context (this function sets any errors) */
7127c478bd9Sstevel@tonic-gate 		ctx = initialize_ctx(c_id);
7137c478bd9Sstevel@tonic-gate 		if (ctx == NULL) {
7147c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
7157c478bd9Sstevel@tonic-gate 			    "http_srv_connect: initialize_ctx returned NULL");
7167c478bd9Sstevel@tonic-gate 			return (-1);
7177c478bd9Sstevel@tonic-gate 		}
7187c478bd9Sstevel@tonic-gate 	}
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 	/* Connect the TCP socket */
7217c478bd9Sstevel@tonic-gate 	if (c_id->proxied) {
7227c478bd9Sstevel@tonic-gate 		c_id->fd = proxy_connect(c_id);
7237c478bd9Sstevel@tonic-gate 	} else {
7247c478bd9Sstevel@tonic-gate 		c_id->fd = tcp_connect(c_id, CONN_HOSTNAME, CONN_PORT);
7257c478bd9Sstevel@tonic-gate 	}
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	if (c_id->fd < 0) {
7287c478bd9Sstevel@tonic-gate 		if (ctx != NULL)
7297c478bd9Sstevel@tonic-gate 			SSL_CTX_free(ctx);
7307c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
7317c478bd9Sstevel@tonic-gate 			    "http_srv_connect: %s returned %d",
7327c478bd9Sstevel@tonic-gate 			    (c_id->proxied) ? "proxy_connect" : "tcp_connect",
7337c478bd9Sstevel@tonic-gate 			    c_id->fd);
7347c478bd9Sstevel@tonic-gate 		return (-1);
7357c478bd9Sstevel@tonic-gate 	}
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate 	if (CONN_HTTPS) {
7387c478bd9Sstevel@tonic-gate 		/* Connect the SSL socket */
7397c478bd9Sstevel@tonic-gate 		if ((c_id->ssl = SSL_new(ctx)) == NULL) {
7407c478bd9Sstevel@tonic-gate 			ulong_t err;
7417c478bd9Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
7427c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
7437c478bd9Sstevel@tonic-gate 				libbootlog(BOOTLOG_CRIT,
7447c478bd9Sstevel@tonic-gate 				    "http_srv_connect: SSL_new returned "
7457c478bd9Sstevel@tonic-gate 				    "NULL");
7467c478bd9Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7477c478bd9Sstevel@tonic-gate 			return (-1);
7487c478bd9Sstevel@tonic-gate 		}
7497c478bd9Sstevel@tonic-gate 		if (verbosemode)
7507c478bd9Sstevel@tonic-gate 			print_ciphers(c_id->ssl);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 		/* Ensure automatic negotiations will do things right */
7537c478bd9Sstevel@tonic-gate 		SSL_set_connect_state(c_id->ssl);
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 		if (SSL_set_fd(c_id->ssl, c_id->fd) == 0) {
7567c478bd9Sstevel@tonic-gate 			ulong_t err;
7577c478bd9Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
7587c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
7597c478bd9Sstevel@tonic-gate 				libbootlog(BOOTLOG_CRIT,
7607c478bd9Sstevel@tonic-gate 				    "http_srv_connect: SSL_set_fd returned 0");
7617c478bd9Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7627c478bd9Sstevel@tonic-gate 			return (-1);
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 		if ((retval = SSL_connect(c_id->ssl)) <= 0) {
7667c478bd9Sstevel@tonic-gate 			handle_ssl_error(c_id, retval);
7677c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
7687c478bd9Sstevel@tonic-gate 			    "http_srv_connect: SSL_connect");
7697c478bd9Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7707c478bd9Sstevel@tonic-gate 			return (-1);
7717c478bd9Sstevel@tonic-gate 		}
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 		if (check_cert_chain(c_id, CONN_HOSTNAME) != 0) {
7747c478bd9Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7757c478bd9Sstevel@tonic-gate 			return (-1);
7767c478bd9Sstevel@tonic-gate 		}
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 		if (verbosemode)
7797c478bd9Sstevel@tonic-gate 			print_ciphers(c_id->ssl);
7807c478bd9Sstevel@tonic-gate 	}
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	return (0);
7837c478bd9Sstevel@tonic-gate }
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate /*
7867c478bd9Sstevel@tonic-gate  * http_head_request - Issue http HEAD request
7877c478bd9Sstevel@tonic-gate  *
7887c478bd9Sstevel@tonic-gate  *     ret = http_head_request(handle, abs_path);
7897c478bd9Sstevel@tonic-gate  *
7907c478bd9Sstevel@tonic-gate  * Arguments:
7917c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
7927c478bd9Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
7937c478bd9Sstevel@tonic-gate  *		  segment, etc are allowed.
7947c478bd9Sstevel@tonic-gate  *
7957c478bd9Sstevel@tonic-gate  * Returns:
7967c478bd9Sstevel@tonic-gate  *	0	- Success
7977c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
7987c478bd9Sstevel@tonic-gate  */
7997c478bd9Sstevel@tonic-gate int
8007c478bd9Sstevel@tonic-gate http_head_request(http_handle_t handle, const char *abs_path)
8017c478bd9Sstevel@tonic-gate {
8027c478bd9Sstevel@tonic-gate 	return (http_req(handle, abs_path, HTTP_REQ_TYPE_HEAD, 0, 0));
8037c478bd9Sstevel@tonic-gate }
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate /*
8067c478bd9Sstevel@tonic-gate  * http_get_request - Issue http GET request without a range.
8077c478bd9Sstevel@tonic-gate  *
8087c478bd9Sstevel@tonic-gate  *     ret = http_get_request(handle, abs_path);
8097c478bd9Sstevel@tonic-gate  *
8107c478bd9Sstevel@tonic-gate  * Arguments:
8117c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
8127c478bd9Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
8137c478bd9Sstevel@tonic-gate  *		  segment, etc are allowed.
8147c478bd9Sstevel@tonic-gate  *
8157c478bd9Sstevel@tonic-gate  * Returns:
8167c478bd9Sstevel@tonic-gate  *	0	- Success
8177c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
8187c478bd9Sstevel@tonic-gate  */
8197c478bd9Sstevel@tonic-gate int
8207c478bd9Sstevel@tonic-gate http_get_request(http_handle_t handle, const char *abs_path)
8217c478bd9Sstevel@tonic-gate {
8227c478bd9Sstevel@tonic-gate 	return (http_req(handle, abs_path, HTTP_REQ_TYPE_GET, -1, 0));
8237c478bd9Sstevel@tonic-gate }
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate /*
8267c478bd9Sstevel@tonic-gate  * http_get_range_request - Issue http GET request using a range.
8277c478bd9Sstevel@tonic-gate  *
8287c478bd9Sstevel@tonic-gate  *     ret = http_get_range_request(handle, abs_path, curpos, len);
8297c478bd9Sstevel@tonic-gate  *
8307c478bd9Sstevel@tonic-gate  * Arguments:
8317c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
8327c478bd9Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
8337c478bd9Sstevel@tonic-gate  *		  segment, etc are allowed.
8347c478bd9Sstevel@tonic-gate  *	curpos  - >=0 - Beginning of range
8357c478bd9Sstevel@tonic-gate  *	len	- = 0 - Range ends at the end of the file
8367c478bd9Sstevel@tonic-gate  *		  > 0 - Length of range.
8377c478bd9Sstevel@tonic-gate  *
8387c478bd9Sstevel@tonic-gate  * Returns:
8397c478bd9Sstevel@tonic-gate  *	0	- Success
8407c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
8417c478bd9Sstevel@tonic-gate  */
8427c478bd9Sstevel@tonic-gate int
8437c478bd9Sstevel@tonic-gate http_get_range_request(http_handle_t handle, const char *abs_path,
844cb4c2b01Svh115876     offset_t curpos, offset_t len)
8457c478bd9Sstevel@tonic-gate {
8467c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
8497c478bd9Sstevel@tonic-gate 		return (-1);
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	if (curpos < 0) {
8527c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
8537c478bd9Sstevel@tonic-gate 		return (-1);
8547c478bd9Sstevel@tonic-gate 	}
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 	return (http_req(handle, abs_path, HTTP_REQ_TYPE_GET, curpos, len));
8577c478bd9Sstevel@tonic-gate }
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate /*
8607c478bd9Sstevel@tonic-gate  * http_free_respinfo - Free a respinfo structure
8617c478bd9Sstevel@tonic-gate  *
8627c478bd9Sstevel@tonic-gate  *     ret = http_free_respinfo(resp);
8637c478bd9Sstevel@tonic-gate  *
8647c478bd9Sstevel@tonic-gate  * Arguments:
8657c478bd9Sstevel@tonic-gate  *	resp	- respinfo structure presumably allocated by
8667c478bd9Sstevel@tonic-gate  *		  http_process_headers() or http_process_part_headers()
8677c478bd9Sstevel@tonic-gate  *
8687c478bd9Sstevel@tonic-gate  * Note that if resp is NULL, then this results in a NOOP.
8697c478bd9Sstevel@tonic-gate  *
8707c478bd9Sstevel@tonic-gate  */
8717c478bd9Sstevel@tonic-gate void
8727c478bd9Sstevel@tonic-gate http_free_respinfo(http_respinfo_t *resp)
8737c478bd9Sstevel@tonic-gate {
8747c478bd9Sstevel@tonic-gate 	if (resp == NULL) {
8757c478bd9Sstevel@tonic-gate 		return;
8767c478bd9Sstevel@tonic-gate 	}
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	if (resp->statusmsg != NULL) {
8797c478bd9Sstevel@tonic-gate 		free(resp->statusmsg);
8807c478bd9Sstevel@tonic-gate 	}
8817c478bd9Sstevel@tonic-gate 	free(resp);
8827c478bd9Sstevel@tonic-gate }
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate /*
8857c478bd9Sstevel@tonic-gate  * http_process_headers - Read in the header lines from the response
8867c478bd9Sstevel@tonic-gate  *
8877c478bd9Sstevel@tonic-gate  *     ret = http_process_headers(handle, resp);
8887c478bd9Sstevel@tonic-gate  *
8897c478bd9Sstevel@tonic-gate  * Arguments:
8907c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the connection where the request
8917c478bd9Sstevel@tonic-gate  *		  was made.
8927c478bd9Sstevel@tonic-gate  *	resp	- Summary information about the response.
8937c478bd9Sstevel@tonic-gate  *
8947c478bd9Sstevel@tonic-gate  * Returns:
8957c478bd9Sstevel@tonic-gate  *	0	- Success
8967c478bd9Sstevel@tonic-gate  *	< 0	- An error occurred.  Specifics of the error can
8977c478bd9Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
8987c478bd9Sstevel@tonic-gate  *
8997c478bd9Sstevel@tonic-gate  * Process the HTTP headers in the response. Check for a valid response
9007c478bd9Sstevel@tonic-gate  * status line.  Allocate and return response information via the 'resp'
9017c478bd9Sstevel@tonic-gate  * argument. Header lines are stored locally, are are returned using calls
9027c478bd9Sstevel@tonic-gate  * to http_get_response_header() and http_get_header_value().
9037c478bd9Sstevel@tonic-gate  *
9047c478bd9Sstevel@tonic-gate  * Note that the errors will be set in the http_conn_t struct before the
9057c478bd9Sstevel@tonic-gate  * function which detected the error returns.
9067c478bd9Sstevel@tonic-gate  *
9077c478bd9Sstevel@tonic-gate  * Note that if resp is non-NULL, then upon a successful return, information
9087c478bd9Sstevel@tonic-gate  * about the status line, the code in the status line and the number of
9097c478bd9Sstevel@tonic-gate  * header lines are returned in the http_respinfo_t structure. The caller is
9107c478bd9Sstevel@tonic-gate  * responsible for freeing the resources allocated to this structure via
9117c478bd9Sstevel@tonic-gate  * http_free_respinfo().
9127c478bd9Sstevel@tonic-gate  *
9137c478bd9Sstevel@tonic-gate  * Note that the counters used to read message bodies are initialized here.
9147c478bd9Sstevel@tonic-gate  *
9157c478bd9Sstevel@tonic-gate  * Calling this function replaces the header information which is
9167c478bd9Sstevel@tonic-gate  * queried using http_get_response_header() and http_get_header_value().
9177c478bd9Sstevel@tonic-gate  * Once this function is called, headers read by the previous call
9187c478bd9Sstevel@tonic-gate  * to http_process_headers() or http_process_part_headers() is lost.
9197c478bd9Sstevel@tonic-gate  */
9207c478bd9Sstevel@tonic-gate int
9217c478bd9Sstevel@tonic-gate http_process_headers(http_handle_t handle, http_respinfo_t **resp)
9227c478bd9Sstevel@tonic-gate {
9237c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
9247c478bd9Sstevel@tonic-gate 	http_respinfo_t *lresp;
9257c478bd9Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
9267c478bd9Sstevel@tonic-gate 	char	*ptr;
9277c478bd9Sstevel@tonic-gate 	int	i;
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 	ERR_clear_error();
9307c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
9317c478bd9Sstevel@tonic-gate 		return (-1);
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 	if (resp != NULL) {
9347c478bd9Sstevel@tonic-gate 		if ((lresp = malloc(sizeof (http_respinfo_t))) == NULL) {
9357c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
9367c478bd9Sstevel@tonic-gate 			return (-1);
9377c478bd9Sstevel@tonic-gate 		}
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 		bzero(lresp, sizeof (http_respinfo_t));
9407c478bd9Sstevel@tonic-gate 	}
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate 	/*
9437c478bd9Sstevel@tonic-gate 	 * check the response status line, expecting
9447c478bd9Sstevel@tonic-gate 	 * HTTP/1.1 200 OK
9457c478bd9Sstevel@tonic-gate 	 */
94623a1cceaSRoger A. Faulkner 	i = getaline(c_id, line, sizeof (line), B_FALSE);
9477c478bd9Sstevel@tonic-gate 	if (i == 0) {
9487c478bd9Sstevel@tonic-gate 		if (resp != NULL) {
9497c478bd9Sstevel@tonic-gate 			*resp = lresp;
9507c478bd9Sstevel@tonic-gate 		}
9517c478bd9Sstevel@tonic-gate 		return (0);
9527c478bd9Sstevel@tonic-gate 	}
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 	if (i < 0) {
9557c478bd9Sstevel@tonic-gate 		/*
9567c478bd9Sstevel@tonic-gate 		 * Cause of I/O error was already put into
9577c478bd9Sstevel@tonic-gate 		 * error stack.  This is an additional error.
9587c478bd9Sstevel@tonic-gate 		 */
9597c478bd9Sstevel@tonic-gate 		http_free_respinfo(lresp);
9607c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NODATA);
9617c478bd9Sstevel@tonic-gate 		return (-1);
9627c478bd9Sstevel@tonic-gate 	}
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	free_response(c_id, B_TRUE);
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate 	if (verbosemode)
9677c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "http_process_headers: %s", line);
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 	ptr = line;
9707c478bd9Sstevel@tonic-gate 	if (strncmp(ptr, "HTTP/1.1", 8) != 0) {
9717c478bd9Sstevel@tonic-gate 		http_free_respinfo(lresp);
9727c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOT_1_1);
9737c478bd9Sstevel@tonic-gate 		return (-1);
9747c478bd9Sstevel@tonic-gate 	}
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 	/* skip to the code */
9777c478bd9Sstevel@tonic-gate 	ptr += 8;
9787c478bd9Sstevel@tonic-gate 	while (isspace(*ptr))
9797c478bd9Sstevel@tonic-gate 		ptr++;
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate 	/* make sure it's three digits */
9827c478bd9Sstevel@tonic-gate 	i = 0;
9837c478bd9Sstevel@tonic-gate 	while (isdigit(ptr[i]))
9847c478bd9Sstevel@tonic-gate 		i++;
9857c478bd9Sstevel@tonic-gate 	if (i != 3) {
9867c478bd9Sstevel@tonic-gate 		http_free_respinfo(lresp);
9877c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADHDR);
9887c478bd9Sstevel@tonic-gate 		return (-1);
9897c478bd9Sstevel@tonic-gate 	}
9907c478bd9Sstevel@tonic-gate 	c_id->resp.code = strtol(ptr, NULL, 10);
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	/* skip to the message */
9937c478bd9Sstevel@tonic-gate 	ptr += 3;
9947c478bd9Sstevel@tonic-gate 	while (isspace(*ptr))
9957c478bd9Sstevel@tonic-gate 		ptr++;
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	/* save the message */
9987c478bd9Sstevel@tonic-gate 	c_id->resp.statusmsg = malloc(strlen(ptr) + 1);
9997c478bd9Sstevel@tonic-gate 	if (c_id->resp.statusmsg == NULL) {
10007c478bd9Sstevel@tonic-gate 		http_free_respinfo(lresp);
10017c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
10027c478bd9Sstevel@tonic-gate 		return (-1);
10037c478bd9Sstevel@tonic-gate 	}
10047c478bd9Sstevel@tonic-gate 	(void) strcpy(c_id->resp.statusmsg, ptr);
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	if ((i = read_headerlines(c_id, B_FALSE)) < 0) {
10077c478bd9Sstevel@tonic-gate 		/*
10087c478bd9Sstevel@tonic-gate 		 * Error stack was already set at a lower level.
10097c478bd9Sstevel@tonic-gate 		 * 'statusmsg' will be cleaned up next time
10107c478bd9Sstevel@tonic-gate 		 * headers are read.
10117c478bd9Sstevel@tonic-gate 		 */
10127c478bd9Sstevel@tonic-gate 		http_free_respinfo(lresp);
10137c478bd9Sstevel@tonic-gate 		return (-1);
10147c478bd9Sstevel@tonic-gate 	}
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate 	/*
10177c478bd9Sstevel@tonic-gate 	 * See if there is a 'content-type: multipart/mixed' line in the
10187c478bd9Sstevel@tonic-gate 	 * headers.  If so, get the boundary string.
10197c478bd9Sstevel@tonic-gate 	 */
10207c478bd9Sstevel@tonic-gate 	ptr = http_get_header_value(handle, "Content-Type");
10217c478bd9Sstevel@tonic-gate 	if (ptr != NULL) {
10227c478bd9Sstevel@tonic-gate 		char *ptr2;
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 		ptr2 = ptr;
10257c478bd9Sstevel@tonic-gate 		while (isspace(*ptr2))
10267c478bd9Sstevel@tonic-gate 			ptr2 ++;
10277c478bd9Sstevel@tonic-gate 		if (startswith((const char **)&ptr2, "Multipart/Mixed;")) {
10287c478bd9Sstevel@tonic-gate 			while (isspace(*ptr2))
10297c478bd9Sstevel@tonic-gate 				ptr2 ++;
10307c478bd9Sstevel@tonic-gate 			if (startswith((const char **)&ptr2, "Boundary=")) {
10317c478bd9Sstevel@tonic-gate 				if (ptr2[0] == '"') {
10327c478bd9Sstevel@tonic-gate 					ptr2 ++;
10337c478bd9Sstevel@tonic-gate 					if (ptr2[strlen(ptr2) - 1] == '"')
10347c478bd9Sstevel@tonic-gate 						ptr2[strlen(ptr2) - 1] = '\0';
10357c478bd9Sstevel@tonic-gate 				}
10367c478bd9Sstevel@tonic-gate 				c_id->boundary = strdup(ptr2);
10377c478bd9Sstevel@tonic-gate 				if (c_id->boundary == NULL) {
10387c478bd9Sstevel@tonic-gate 					free(ptr);
10397c478bd9Sstevel@tonic-gate 					http_free_respinfo(lresp);
10407c478bd9Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBHTTP,
10417c478bd9Sstevel@tonic-gate 					    EHTTP_NOMEM);
10427c478bd9Sstevel@tonic-gate 					return (-1);
10437c478bd9Sstevel@tonic-gate 				}
10447c478bd9Sstevel@tonic-gate 				c_id->boundary_len = strlen(c_id->boundary);
10457c478bd9Sstevel@tonic-gate 				c_id->is_multipart = B_TRUE;
10467c478bd9Sstevel@tonic-gate 				c_id->is_firstpart = B_TRUE;
10477c478bd9Sstevel@tonic-gate 			}
10487c478bd9Sstevel@tonic-gate 		}
10497c478bd9Sstevel@tonic-gate 		free(ptr);
10507c478bd9Sstevel@tonic-gate 	}
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/*
10537c478bd9Sstevel@tonic-gate 	 * Initialize the counters used to process message bodies.
10547c478bd9Sstevel@tonic-gate 	 */
10557c478bd9Sstevel@tonic-gate 	if (init_bread(c_id) != 0) {
10567c478bd9Sstevel@tonic-gate 		/*
10577c478bd9Sstevel@tonic-gate 		 * Error stack was already set at a lower level.
10587c478bd9Sstevel@tonic-gate 		 */
10597c478bd9Sstevel@tonic-gate 		http_free_respinfo(lresp);
10607c478bd9Sstevel@tonic-gate 		return (-1);
10617c478bd9Sstevel@tonic-gate 	}
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	/* Copy fields to the caller's structure */
10647c478bd9Sstevel@tonic-gate 	if (resp != NULL) {
10657c478bd9Sstevel@tonic-gate 		lresp->code = c_id->resp.code;
10667c478bd9Sstevel@tonic-gate 		lresp->nresphdrs = c_id->resp.nresphdrs;
10677c478bd9Sstevel@tonic-gate 		lresp->statusmsg = strdup(c_id->resp.statusmsg);
10687c478bd9Sstevel@tonic-gate 		if (lresp->statusmsg == NULL) {
10697c478bd9Sstevel@tonic-gate 			http_free_respinfo(lresp);
10707c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
10717c478bd9Sstevel@tonic-gate 			return (-1);
10727c478bd9Sstevel@tonic-gate 		}
10737c478bd9Sstevel@tonic-gate 		*resp = lresp;
10747c478bd9Sstevel@tonic-gate 	}
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 	return (0);
10777c478bd9Sstevel@tonic-gate }
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate /*
10807c478bd9Sstevel@tonic-gate  * http_process_part_headers - Read in part boundary and header lines for the
10817c478bd9Sstevel@tonic-gate  *                             next part of a multipart message.
10827c478bd9Sstevel@tonic-gate  *
10837c478bd9Sstevel@tonic-gate  *        ret = http_process_part_headers(handle, resp);
10847c478bd9Sstevel@tonic-gate  *
10857c478bd9Sstevel@tonic-gate  * Arguments:
10867c478bd9Sstevel@tonic-gate  *   handle	- Handle associated with the connection where the request
10877c478bd9Sstevel@tonic-gate  *		  was made.
10887c478bd9Sstevel@tonic-gate  *   resp	- Return address for summary information about the
10897c478bd9Sstevel@tonic-gate  *		  header block.
10907c478bd9Sstevel@tonic-gate  *
10917c478bd9Sstevel@tonic-gate  * Returns:
10927c478bd9Sstevel@tonic-gate  *   = 1	- The end part was found.
10937c478bd9Sstevel@tonic-gate  *   = 0	- Success, with header info returned in 'resp'
10947c478bd9Sstevel@tonic-gate  *   = -1	- An error occurred.  Specifics of the error can
10957c478bd9Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
10967c478bd9Sstevel@tonic-gate  *
10977c478bd9Sstevel@tonic-gate  * This function reads any \r\n sequences (empty lines) and expects to get
10987c478bd9Sstevel@tonic-gate  * a boundary line as the next non-empty line.  It then reads header lines
10997c478bd9Sstevel@tonic-gate  * (content-length, etc) until it gets another empty lines, which ends the
11007c478bd9Sstevel@tonic-gate  * header section.
11017c478bd9Sstevel@tonic-gate  *
11027c478bd9Sstevel@tonic-gate  * Note that if resp is non-NULL, then upon a successful return, information
11037c478bd9Sstevel@tonic-gate  * about the the number of header lines is returned in the http_respinfo_t
11047c478bd9Sstevel@tonic-gate  * structure. The caller is responsible for freeing the resources allocated
11057c478bd9Sstevel@tonic-gate  * to this structure via http_free_respinfo().
11067c478bd9Sstevel@tonic-gate  *
11077c478bd9Sstevel@tonic-gate  * Headers values can be returned using http_get_response_header() and
11087c478bd9Sstevel@tonic-gate  * http_get_header_value().
11097c478bd9Sstevel@tonic-gate  *
11107c478bd9Sstevel@tonic-gate  * Calling this function replaces the header information which is
11117c478bd9Sstevel@tonic-gate  * queried using http_get_response_header() and http_get_header_value().
11127c478bd9Sstevel@tonic-gate  * Once this function is called, information returned by the previous call
11137c478bd9Sstevel@tonic-gate  * to http_process_headers() or http_process_part_headers() is gone.
11147c478bd9Sstevel@tonic-gate  */
11157c478bd9Sstevel@tonic-gate int
11167c478bd9Sstevel@tonic-gate http_process_part_headers(http_handle_t handle, http_respinfo_t **resp)
11177c478bd9Sstevel@tonic-gate {
11187c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
11197c478bd9Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
11207c478bd9Sstevel@tonic-gate 	int	count;
11217c478bd9Sstevel@tonic-gate 	int 	limit;
11227c478bd9Sstevel@tonic-gate 	int	i;
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 	ERR_clear_error();
11257c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
11267c478bd9Sstevel@tonic-gate 		return (-1);
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate 	if (c_id->is_multipart == 0) {
11297c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOTMULTI);
11307c478bd9Sstevel@tonic-gate 		return (-1);
11317c478bd9Sstevel@tonic-gate 	}
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate 	/*
11347c478bd9Sstevel@tonic-gate 	 * Figure out how many empty lines to allow.  Before the first
11357c478bd9Sstevel@tonic-gate 	 * boundary of the transmission, there can be any number of
11367c478bd9Sstevel@tonic-gate 	 * empty lines (from 0 up).  Limit these to some reasonable
11377c478bd9Sstevel@tonic-gate 	 * failsafe.
11387c478bd9Sstevel@tonic-gate 	 *
11397c478bd9Sstevel@tonic-gate 	 * For the 2nd and later boundaries, there is supposed to be
11407c478bd9Sstevel@tonic-gate 	 * one crlf pair.  However, many implementations don't require
11417c478bd9Sstevel@tonic-gate 	 * it.  So don't require it.
11427c478bd9Sstevel@tonic-gate 	 */
11437c478bd9Sstevel@tonic-gate 	if (c_id->is_firstpart) {
11447c478bd9Sstevel@tonic-gate 		limit = FAILSAFE;
11457c478bd9Sstevel@tonic-gate 		c_id->is_firstpart = B_FALSE;
11467c478bd9Sstevel@tonic-gate 	} else
11477c478bd9Sstevel@tonic-gate 		limit = 1;
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	/* Look for the boundary line. */
11507c478bd9Sstevel@tonic-gate 	count = 0;
115123a1cceaSRoger A. Faulkner 	while ((i = getaline(c_id, line, sizeof (line), B_TRUE)) == 0 &&
11527c478bd9Sstevel@tonic-gate 	    count < FAILSAFE)
11537c478bd9Sstevel@tonic-gate 		count ++;
11547c478bd9Sstevel@tonic-gate 	if (i < 0 || count > limit) {
11557c478bd9Sstevel@tonic-gate 		/*
11567c478bd9Sstevel@tonic-gate 		 * If I/O error, cause was already put into
11577c478bd9Sstevel@tonic-gate 		 * error stack.  This is an additional error.
11587c478bd9Sstevel@tonic-gate 		 */
11597c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOBOUNDARY);
11607c478bd9Sstevel@tonic-gate 		return (-1);
11617c478bd9Sstevel@tonic-gate 	}
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 	free_response(c_id, B_FALSE);
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate 	if (verbosemode)
11667c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE,
11677c478bd9Sstevel@tonic-gate 		    "http_process_part_headers: %s", line);
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 	/* Look for boundary line - '--<boundary text> */
11707c478bd9Sstevel@tonic-gate 	if (line[0] != '-' || line[1] != '-' ||
11717c478bd9Sstevel@tonic-gate 	    strncmp(&line[2], c_id->boundary, c_id->boundary_len) != 0) {
11727c478bd9Sstevel@tonic-gate 		/* No boundary line.... */
11737c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOBOUNDARY);
11747c478bd9Sstevel@tonic-gate 		return (-1);
11757c478bd9Sstevel@tonic-gate 	}
11767c478bd9Sstevel@tonic-gate 
11777c478bd9Sstevel@tonic-gate 	/* Is this the end-of-parts boundary (ends with a trailing '--') */
11787c478bd9Sstevel@tonic-gate 	if (strcmp(&line[c_id->boundary_len + 2], "--") == 0) {
11797c478bd9Sstevel@tonic-gate 		return (1);
11807c478bd9Sstevel@tonic-gate 	}
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 	free_response(c_id, B_FALSE);
11837c478bd9Sstevel@tonic-gate 	if (read_headerlines(c_id, B_TRUE) < 0) {
11847c478bd9Sstevel@tonic-gate 		/* Error stack was already set at a lower level. */
11857c478bd9Sstevel@tonic-gate 		return (-1);
11867c478bd9Sstevel@tonic-gate 	}
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 	/* Copy fields to the caller's structure */
11897c478bd9Sstevel@tonic-gate 	if (resp != NULL) {
11907c478bd9Sstevel@tonic-gate 		if ((*resp = malloc(sizeof (http_respinfo_t))) == NULL) {
11917c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
11927c478bd9Sstevel@tonic-gate 			return (-1);
11937c478bd9Sstevel@tonic-gate 		}
11947c478bd9Sstevel@tonic-gate 		bzero(*resp, sizeof (http_respinfo_t));
11957c478bd9Sstevel@tonic-gate 		(*resp)->code = ' ';
11967c478bd9Sstevel@tonic-gate 		(*resp)->nresphdrs = c_id->resp.nresphdrs;
11977c478bd9Sstevel@tonic-gate 	}
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 	return (0);
12007c478bd9Sstevel@tonic-gate }
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate /*
12037c478bd9Sstevel@tonic-gate  * http_get_response_header - Get a line from the response header
12047c478bd9Sstevel@tonic-gate  *
12057c478bd9Sstevel@tonic-gate  *     ret = http_get_response_header(handle, whichline);
12067c478bd9Sstevel@tonic-gate  *
12077c478bd9Sstevel@tonic-gate  * Arguments:
12087c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
12097c478bd9Sstevel@tonic-gate  *	whichline - Which line of the header to return.  This must be between
12107c478bd9Sstevel@tonic-gate  *		  zero and resp.nresphdrs which was returned by the call to
12117c478bd9Sstevel@tonic-gate  *		  http_process_headers().
12127c478bd9Sstevel@tonic-gate  *
12137c478bd9Sstevel@tonic-gate  * Returns:
12147c478bd9Sstevel@tonic-gate  *	ptr	- Points to a copy of the header line.
12157c478bd9Sstevel@tonic-gate  *	NULL	- An error occurred.  Check http_get_lasterr().
12167c478bd9Sstevel@tonic-gate  */
12177c478bd9Sstevel@tonic-gate char *
12187c478bd9Sstevel@tonic-gate http_get_response_header(http_handle_t handle, uint_t which)
12197c478bd9Sstevel@tonic-gate {
12207c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
12217c478bd9Sstevel@tonic-gate 	char *res;
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
12247c478bd9Sstevel@tonic-gate 		return (NULL);
12257c478bd9Sstevel@tonic-gate 
12267c478bd9Sstevel@tonic-gate 	if (which >= c_id->resp.nresphdrs) {
12277c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_OORANGE);
12287c478bd9Sstevel@tonic-gate 		return (NULL);
12297c478bd9Sstevel@tonic-gate 	}
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 	res = strdup(c_id->resphdr[which]);
12327c478bd9Sstevel@tonic-gate 	if (res == NULL) {
12337c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
12347c478bd9Sstevel@tonic-gate 		return (NULL);
12357c478bd9Sstevel@tonic-gate 	}
12367c478bd9Sstevel@tonic-gate 	return (res);
12377c478bd9Sstevel@tonic-gate }
12387c478bd9Sstevel@tonic-gate 
12397c478bd9Sstevel@tonic-gate /*
12407c478bd9Sstevel@tonic-gate  * http_get_header_value - Get the value of a header line.
12417c478bd9Sstevel@tonic-gate  *
12427c478bd9Sstevel@tonic-gate  *     ret = http_get_header_value(handle, what);
12437c478bd9Sstevel@tonic-gate  *
12447c478bd9Sstevel@tonic-gate  * Arguments:
12457c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
12467c478bd9Sstevel@tonic-gate  *	what	- The field name to look up.
12477c478bd9Sstevel@tonic-gate  *
12487c478bd9Sstevel@tonic-gate  * Returns:
12497c478bd9Sstevel@tonic-gate  *	ptr	- Points to a copy of the header value.
12507c478bd9Sstevel@tonic-gate  *	NULL	- An error occurred.  Check http_get_lasterr().
12517c478bd9Sstevel@tonic-gate  */
12527c478bd9Sstevel@tonic-gate char *
12537c478bd9Sstevel@tonic-gate http_get_header_value(http_handle_t handle, const char *field_name)
12547c478bd9Sstevel@tonic-gate {
12557c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
12567c478bd9Sstevel@tonic-gate 	char	*ptr;
12577c478bd9Sstevel@tonic-gate 	char	*res;
12587c478bd9Sstevel@tonic-gate 	int	i;
12597c478bd9Sstevel@tonic-gate 	int	n;
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
12627c478bd9Sstevel@tonic-gate 		return (NULL);
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 	if (field_name == NULL || field_name[0] == '\0') {
12657c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
12667c478bd9Sstevel@tonic-gate 		return (NULL);
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	for (i = 0; i < c_id->resp.nresphdrs; i++) {
12707c478bd9Sstevel@tonic-gate 		ptr = c_id->resphdr[i];
12717c478bd9Sstevel@tonic-gate 		n = strlen(field_name);
12727c478bd9Sstevel@tonic-gate 		if (strncasecmp(field_name, ptr, n) == 0 && ptr[n] == ':') {
12737c478bd9Sstevel@tonic-gate 			ptr += n + 1;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 			while (isspace(*ptr))
12767c478bd9Sstevel@tonic-gate 				ptr++;
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 			res = strdup(ptr);
12797c478bd9Sstevel@tonic-gate 			if (res == NULL) {
12807c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
12817c478bd9Sstevel@tonic-gate 				return (NULL);
12827c478bd9Sstevel@tonic-gate 			}
12837c478bd9Sstevel@tonic-gate 			return (res);
12847c478bd9Sstevel@tonic-gate 		}
12857c478bd9Sstevel@tonic-gate 	}
12867c478bd9Sstevel@tonic-gate 	SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMATCH);
12877c478bd9Sstevel@tonic-gate 	return (NULL);
12887c478bd9Sstevel@tonic-gate }
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate /*
12917c478bd9Sstevel@tonic-gate  * http_read_body - Read the HTTP response body.
12927c478bd9Sstevel@tonic-gate  *
12937c478bd9Sstevel@tonic-gate  *     ret = http_read_body(handle, recv_buf_ptr, recv_buf_size);
12947c478bd9Sstevel@tonic-gate  *
12957c478bd9Sstevel@tonic-gate  * Arguments:
12967c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the relevant connection
12977c478bd9Sstevel@tonic-gate  *	recv_buf_ptr - Points to buffer to receive buffer
12987c478bd9Sstevel@tonic-gate  *	recv_buf_size - Length in bytes of buffer.
12997c478bd9Sstevel@tonic-gate  *
13007c478bd9Sstevel@tonic-gate  * Returns:
13017c478bd9Sstevel@tonic-gate  *	n	- Number of bytes read..
13027c478bd9Sstevel@tonic-gate  *	< 0	- An error occurred.  This is (the number of bytes gotten + 1),
13037c478bd9Sstevel@tonic-gate  *		  negated.  In other words, if 'n' bytes were read and then an
13047c478bd9Sstevel@tonic-gate  *		  error occurred, this will return (-(n+1)).  So zero bytes
13057c478bd9Sstevel@tonic-gate  *		  were read and then an error occurs, this will return -1.  If
13067c478bd9Sstevel@tonic-gate  *		  1 byte was read, it will return -2, etc.  Specifics of the
13077c478bd9Sstevel@tonic-gate  *		  error can be gotten using http_get_lasterr().
13087c478bd9Sstevel@tonic-gate  *
13097c478bd9Sstevel@tonic-gate  * Note that the errors will be set in the http_conn_t struct before the
13107c478bd9Sstevel@tonic-gate  * function which detected the error returns.
13117c478bd9Sstevel@tonic-gate  */
13127c478bd9Sstevel@tonic-gate int
13137c478bd9Sstevel@tonic-gate http_read_body(http_handle_t handle, char *recv_buf_ptr, size_t recv_buf_size)
13147c478bd9Sstevel@tonic-gate {
13157c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate 	ERR_clear_error();
13187c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
13197c478bd9Sstevel@tonic-gate 		return (-1);
13207c478bd9Sstevel@tonic-gate 
13217c478bd9Sstevel@tonic-gate 	if (recv_buf_ptr == NULL || recv_buf_size == 0) {
13227c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
13237c478bd9Sstevel@tonic-gate 		return (-1);
13247c478bd9Sstevel@tonic-gate 	}
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate 	return (getbytes(c_id, recv_buf_ptr, recv_buf_size));
13277c478bd9Sstevel@tonic-gate }
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate /*
13307c478bd9Sstevel@tonic-gate  * http_srv_disconnect - Get rid of the connection to the server without
13317c478bd9Sstevel@tonic-gate  *			freeing the http_conn_t structure.
13327c478bd9Sstevel@tonic-gate  *
13337c478bd9Sstevel@tonic-gate  *     ret = http_srv_disconnect(handle);
13347c478bd9Sstevel@tonic-gate  *
13357c478bd9Sstevel@tonic-gate  * Arguments:
13367c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the connection
13377c478bd9Sstevel@tonic-gate  *
13387c478bd9Sstevel@tonic-gate  * Returns:
13397c478bd9Sstevel@tonic-gate  *	0	- Success
13407c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Specifics of the error can
13417c478bd9Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
13427c478bd9Sstevel@tonic-gate  */
13437c478bd9Sstevel@tonic-gate int
13447c478bd9Sstevel@tonic-gate http_srv_disconnect(http_handle_t handle)
13457c478bd9Sstevel@tonic-gate {
13467c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
13477c478bd9Sstevel@tonic-gate 	int err_ret;
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	ERR_clear_error();
13507c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
13517c478bd9Sstevel@tonic-gate 		return (-1);
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	err_ret = free_ctx_ssl(c_id);
13547c478bd9Sstevel@tonic-gate 	bzero(&c_id->inbuf, sizeof (c_id->inbuf));
13557c478bd9Sstevel@tonic-gate 	free_response(c_id, B_TRUE);
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate 	return (err_ret);
13587c478bd9Sstevel@tonic-gate }
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate /*
13617c478bd9Sstevel@tonic-gate  * http_srv_close - Close the connection and clean up the http_conn_t
13627c478bd9Sstevel@tonic-gate  *		structure.
13637c478bd9Sstevel@tonic-gate  *
13647c478bd9Sstevel@tonic-gate  *     http_srv_close(handle);
13657c478bd9Sstevel@tonic-gate  *
13667c478bd9Sstevel@tonic-gate  * Arguments:
13677c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
13687c478bd9Sstevel@tonic-gate  *
13697c478bd9Sstevel@tonic-gate  * Returns:
13707c478bd9Sstevel@tonic-gate  *	0	- Success
13717c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Specifics of the error can
13727c478bd9Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
13737c478bd9Sstevel@tonic-gate  */
13747c478bd9Sstevel@tonic-gate int
13757c478bd9Sstevel@tonic-gate http_srv_close(http_handle_t handle)
13767c478bd9Sstevel@tonic-gate {
13777c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
13787c478bd9Sstevel@tonic-gate 	int err_ret = 0;
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
13817c478bd9Sstevel@tonic-gate 		return (-1);
13827c478bd9Sstevel@tonic-gate 
13837c478bd9Sstevel@tonic-gate 	if (c_id->ctx != NULL || c_id->ssl != NULL || c_id->fd != -1)
13847c478bd9Sstevel@tonic-gate 		err_ret = http_srv_disconnect(handle);
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 	free(c_id->basic_auth_userid);
13877c478bd9Sstevel@tonic-gate 	free(c_id->basic_auth_password);
13887c478bd9Sstevel@tonic-gate 	free(c_id->resp.statusmsg);
13897c478bd9Sstevel@tonic-gate 	free(c_id->client_cert_file);
13907c478bd9Sstevel@tonic-gate 	free(c_id->private_key_file);
13917c478bd9Sstevel@tonic-gate 	free(c_id->random_file);
13927c478bd9Sstevel@tonic-gate 	free(c_id->file_password);
13937c478bd9Sstevel@tonic-gate 	c_id->signature = 0;
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	free(c_id);
13967c478bd9Sstevel@tonic-gate 	return (err_ret);
13977c478bd9Sstevel@tonic-gate }
13987c478bd9Sstevel@tonic-gate 
13997c478bd9Sstevel@tonic-gate /*
14007c478bd9Sstevel@tonic-gate  * http_get_conn_info - Return current information about the connection
14017c478bd9Sstevel@tonic-gate  *
14027c478bd9Sstevel@tonic-gate  *     err = http_get_conn_info(handle);
14037c478bd9Sstevel@tonic-gate  *
14047c478bd9Sstevel@tonic-gate  * Arguments:
14057c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the connection in question
14067c478bd9Sstevel@tonic-gate  *
14077c478bd9Sstevel@tonic-gate  * Returns:
14087c478bd9Sstevel@tonic-gate  *	non_NULL- Points to structure
14097c478bd9Sstevel@tonic-gate  *	NULL	- An error exists.  Check http_get_lasterr().
14107c478bd9Sstevel@tonic-gate  */
14117c478bd9Sstevel@tonic-gate http_conninfo_t *
14127c478bd9Sstevel@tonic-gate http_get_conn_info(http_handle_t handle)
14137c478bd9Sstevel@tonic-gate {
14147c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
14157c478bd9Sstevel@tonic-gate 	http_conninfo_t *info;
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
14187c478bd9Sstevel@tonic-gate 		return (NULL);
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 	info = malloc(sizeof (*info));
14217c478bd9Sstevel@tonic-gate 	if (info == NULL) {
14227c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
14237c478bd9Sstevel@tonic-gate 		return (NULL);
14247c478bd9Sstevel@tonic-gate 	}
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate 	bzero(info, sizeof (*info));
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 	info->uri = c_id->uri;
14297c478bd9Sstevel@tonic-gate 	info->proxy = c_id->proxy;
14307c478bd9Sstevel@tonic-gate 	info->keepalive = c_id->keepalive;
14317c478bd9Sstevel@tonic-gate 	info->read_timeout = c_id->read_timeout;
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate 	return (info);
14347c478bd9Sstevel@tonic-gate }
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate /*
14377c478bd9Sstevel@tonic-gate  * http_get_lasterr - Return the next error on the last operation
14387c478bd9Sstevel@tonic-gate  *
14397c478bd9Sstevel@tonic-gate  *     err = http_get_lasterr(handle, errsrc);
14407c478bd9Sstevel@tonic-gate  *
14417c478bd9Sstevel@tonic-gate  * Arguments:
14427c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the connection in question
14437c478bd9Sstevel@tonic-gate  *		  If no valid handle exists yet, this can be NULL.
14447c478bd9Sstevel@tonic-gate  *		  However, it must be checked with the very next call.
14457c478bd9Sstevel@tonic-gate  *	errsrc	- Returns the Sources of errors (ERRSRC_* values).
14467c478bd9Sstevel@tonic-gate  *
14477c478bd9Sstevel@tonic-gate  * Returns:
14487c478bd9Sstevel@tonic-gate  *	0	- No error exists
14497c478bd9Sstevel@tonic-gate  *	<> 0	- The error.
14507c478bd9Sstevel@tonic-gate  */
14517c478bd9Sstevel@tonic-gate ulong_t
14527c478bd9Sstevel@tonic-gate http_get_lasterr(http_handle_t handle, uint_t *errsrc)
14537c478bd9Sstevel@tonic-gate {
14547c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
14557c478bd9Sstevel@tonic-gate 	ulong_t src;
14567c478bd9Sstevel@tonic-gate 	ulong_t err;
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO) {
14597c478bd9Sstevel@tonic-gate 		if (errsrc)
14607c478bd9Sstevel@tonic-gate 			*errsrc = ERRSRC_LIBHTTP;
14617c478bd9Sstevel@tonic-gate 		err = early_err;
14627c478bd9Sstevel@tonic-gate 		early_err = 0;
14637c478bd9Sstevel@tonic-gate 		return (err);
14647c478bd9Sstevel@tonic-gate 	}
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 	GET_ERR(c_id, src, err);
14677c478bd9Sstevel@tonic-gate 	if (src == 0 && err == 0) {
14687c478bd9Sstevel@tonic-gate 		if (errsrc)
14697c478bd9Sstevel@tonic-gate 			*errsrc = ERRSRC_LIBHTTP;
14707c478bd9Sstevel@tonic-gate 		err = early_err;
14717c478bd9Sstevel@tonic-gate 		early_err = 0;
14727c478bd9Sstevel@tonic-gate 		return (err);
14737c478bd9Sstevel@tonic-gate 	}
14747c478bd9Sstevel@tonic-gate 	if (errsrc)
14757c478bd9Sstevel@tonic-gate 		*errsrc = src;
14767c478bd9Sstevel@tonic-gate 	return (err);
14777c478bd9Sstevel@tonic-gate }
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate /*
14807c478bd9Sstevel@tonic-gate  * http_decode_err - Decode a libssl error
14817c478bd9Sstevel@tonic-gate  *
14827c478bd9Sstevel@tonic-gate  *     err = http_decode_err(err, errlib, errfunc, errcode);
14837c478bd9Sstevel@tonic-gate  *
14847c478bd9Sstevel@tonic-gate  * Arguments:
14857c478bd9Sstevel@tonic-gate  *	err	- libssl/libcrypto error returned.
14867c478bd9Sstevel@tonic-gate  *	errlib	- returns libssl/libcrypto sublibrary that caused the error
14877c478bd9Sstevel@tonic-gate  *	errfunc	- returns function in that library
14887c478bd9Sstevel@tonic-gate  *	errcode - returns error code
14897c478bd9Sstevel@tonic-gate  *
14907c478bd9Sstevel@tonic-gate  * Returns:
14917c478bd9Sstevel@tonic-gate  *	None other than the above.
14927c478bd9Sstevel@tonic-gate  */
14937c478bd9Sstevel@tonic-gate void
14947c478bd9Sstevel@tonic-gate http_decode_err(ulong_t err, int *errlib, int *errfunc, int *errcode)
14957c478bd9Sstevel@tonic-gate {
14967c478bd9Sstevel@tonic-gate 	if (errlib)
14977c478bd9Sstevel@tonic-gate 		*errlib = ERR_GET_LIB(err);
14987c478bd9Sstevel@tonic-gate 	if (errfunc)
14997c478bd9Sstevel@tonic-gate 		*errfunc = ERR_GET_FUNC(err);
15007c478bd9Sstevel@tonic-gate 	if (errcode)
15017c478bd9Sstevel@tonic-gate 		*errcode = ERR_GET_REASON(err);
15027c478bd9Sstevel@tonic-gate }
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate /* ---------------------- private functions ----------------------- */
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate /*
15077c478bd9Sstevel@tonic-gate  * http_req - Issue http request (either HEAD or GET)
15087c478bd9Sstevel@tonic-gate  *
15097c478bd9Sstevel@tonic-gate  *     ret = http_req(handle, abs_path, reqtype, curpos, len);
15107c478bd9Sstevel@tonic-gate  *
15117c478bd9Sstevel@tonic-gate  * Arguments:
15127c478bd9Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
15137c478bd9Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
15147c478bd9Sstevel@tonic-gate  *		  segment, etc are allowed.
15157c478bd9Sstevel@tonic-gate  *	type	- HTTP_REQ_TYPE_HEAD or HTTP_REQ_TYPE_GET
15167c478bd9Sstevel@tonic-gate  *
15177c478bd9Sstevel@tonic-gate  *	In the case of GET requests,
15187c478bd9Sstevel@tonic-gate  *	  curpos- -1  - Range not used
15197c478bd9Sstevel@tonic-gate  *		  >=0 - Beginning of range
15207c478bd9Sstevel@tonic-gate  *	  len	- 0   - Range ends at the end of the file
15217c478bd9Sstevel@tonic-gate  *		  >0  - Length of range.
15227c478bd9Sstevel@tonic-gate  *
15237c478bd9Sstevel@tonic-gate  * Returns:
15247c478bd9Sstevel@tonic-gate  *	0	- Success
15257c478bd9Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
15267c478bd9Sstevel@tonic-gate  */
15277c478bd9Sstevel@tonic-gate static int
15287c478bd9Sstevel@tonic-gate http_req(http_handle_t handle, const char *abs_path, http_req_t type,
1529cb4c2b01Svh115876     offset_t curpos, offset_t len)
15307c478bd9Sstevel@tonic-gate {
15317c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = handle;
15327c478bd9Sstevel@tonic-gate 	char	*request;
15337c478bd9Sstevel@tonic-gate 	char	*reqtypename;
15347c478bd9Sstevel@tonic-gate 	char	*newreq;
15357c478bd9Sstevel@tonic-gate 	int	requestlen;
15367c478bd9Sstevel@tonic-gate 	int	retval;
15377c478bd9Sstevel@tonic-gate 	int	j;
15387c478bd9Sstevel@tonic-gate 
15397c478bd9Sstevel@tonic-gate 	ERR_clear_error();
15407c478bd9Sstevel@tonic-gate 	if (!http_check_conn(c_id))
15417c478bd9Sstevel@tonic-gate 		return (-1);
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 	if (abs_path == NULL || abs_path[0] == '\0') {
15447c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15457c478bd9Sstevel@tonic-gate 		return (-1);
15467c478bd9Sstevel@tonic-gate 	}
15477c478bd9Sstevel@tonic-gate 
15487c478bd9Sstevel@tonic-gate 	/* Determine the name for the request type */
15497c478bd9Sstevel@tonic-gate 	switch (type) {
15507c478bd9Sstevel@tonic-gate 	case HTTP_REQ_TYPE_GET:
15517c478bd9Sstevel@tonic-gate 		reqtypename = "GET";
15527c478bd9Sstevel@tonic-gate 		if (curpos < 0 && curpos != -1) {
15537c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15547c478bd9Sstevel@tonic-gate 			return (-1);
15557c478bd9Sstevel@tonic-gate 		}
15567c478bd9Sstevel@tonic-gate 		break;
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	case HTTP_REQ_TYPE_HEAD:
15597c478bd9Sstevel@tonic-gate 		reqtypename = "HEAD";
15607c478bd9Sstevel@tonic-gate 		break;
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	default:
15637c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15647c478bd9Sstevel@tonic-gate 		return (-1);
15657c478bd9Sstevel@tonic-gate 	}
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 	/* Do rudimentary checks on the absolute path */
15687c478bd9Sstevel@tonic-gate 	if (abs_path == NULL || *abs_path != '/') {
15697c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15707c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT, "http_req: invalid file path");
15717c478bd9Sstevel@tonic-gate 		if (abs_path != NULL)
15727c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT, " %s", abs_path);
15737c478bd9Sstevel@tonic-gate 		return (-1);
15747c478bd9Sstevel@tonic-gate 	}
15757c478bd9Sstevel@tonic-gate 	(void) strlcpy(CONN_ABSPATH, abs_path, MAXHOSTNAMELEN);
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	/*
15787c478bd9Sstevel@tonic-gate 	 * Size the request.
15797c478bd9Sstevel@tonic-gate 	 *
15807c478bd9Sstevel@tonic-gate 	 * With proxy:
15817c478bd9Sstevel@tonic-gate 	 *   reqtypename + " http://" + host + ":" + port + path +
15827c478bd9Sstevel@tonic-gate 	 *						" HTTP/1.1\r\n" +
15837c478bd9Sstevel@tonic-gate 	 * Without proxy:
15847c478bd9Sstevel@tonic-gate 	 *   reqtypename + " " + path + " HTTP/1.1\r\n" +
15857c478bd9Sstevel@tonic-gate 	 */
15867c478bd9Sstevel@tonic-gate 	requestlen = strlen(reqtypename) + 8 + strlen(CONN_HOSTNAME) + 1 +
15877c478bd9Sstevel@tonic-gate 	    count_digits(CONN_PORT) + strlen(CONN_ABSPATH) + 11;
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 	/*
15907c478bd9Sstevel@tonic-gate 	 * Plus the rest:
15917c478bd9Sstevel@tonic-gate 	 *   "Host: " + targethost + ":" + count_digits(port) + "\r\n" +
15927c478bd9Sstevel@tonic-gate 	 *   "Connection: Keep-Alive\r\n" plus trailing "\r\n\0"
15937c478bd9Sstevel@tonic-gate 	 */
15947c478bd9Sstevel@tonic-gate 	requestlen += 6 + strlen(CONN_HOSTNAME) + 1 +
15957c478bd9Sstevel@tonic-gate 	    count_digits(CONN_PORT) + 2 + 24 + 3;
15967c478bd9Sstevel@tonic-gate 	if ((request = malloc(requestlen)) == NULL) {
15977c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
15987c478bd9Sstevel@tonic-gate 		return (-1);
15997c478bd9Sstevel@tonic-gate 	}
16007c478bd9Sstevel@tonic-gate 
16017c478bd9Sstevel@tonic-gate 	/* The request line */
16027c478bd9Sstevel@tonic-gate 	if (c_id->proxied && c_id->ssl == NULL) {
16037c478bd9Sstevel@tonic-gate 		j = snprintf(request, requestlen,
16047c478bd9Sstevel@tonic-gate 		    "%s http://%s:%d%s HTTP/1.1\r\n",
16057c478bd9Sstevel@tonic-gate 		    reqtypename, CONN_HOSTNAME, CONN_PORT,
16067c478bd9Sstevel@tonic-gate 		    CONN_ABSPATH);
16077c478bd9Sstevel@tonic-gate 	} else {
16087c478bd9Sstevel@tonic-gate 		j = snprintf(request, requestlen, "%s %s HTTP/1.1\r\n",
16097c478bd9Sstevel@tonic-gate 		    reqtypename, CONN_ABSPATH);
16107c478bd9Sstevel@tonic-gate 	}
16117c478bd9Sstevel@tonic-gate 
16127c478bd9Sstevel@tonic-gate 	/* Ancillary headers */
16137c478bd9Sstevel@tonic-gate 	j += snprintf(&request[j], requestlen - j, "Host: %s:%d\r\n",
16147c478bd9Sstevel@tonic-gate 	    CONN_HOSTNAME, CONN_PORT);
16157c478bd9Sstevel@tonic-gate 	if (!c_id->keepalive)
16167c478bd9Sstevel@tonic-gate 		j += snprintf(&request[j], requestlen - j,
16177c478bd9Sstevel@tonic-gate 		    "Connection: close\r\n");
16187c478bd9Sstevel@tonic-gate 	else
16197c478bd9Sstevel@tonic-gate 		j += snprintf(&request[j], requestlen - j,
16207c478bd9Sstevel@tonic-gate 		    "Connection: Keep-Alive\r\n");
16217c478bd9Sstevel@tonic-gate 	/*
16227c478bd9Sstevel@tonic-gate 	 * We only send the range header on GET requests
16237c478bd9Sstevel@tonic-gate 	 *
16247c478bd9Sstevel@tonic-gate 	 * "Range: bytes=" + from + "-" + end + "\r\n" or
16257c478bd9Sstevel@tonic-gate 	 * "Range: bytes=" + from + "-"  "\r\n"
16267c478bd9Sstevel@tonic-gate 	 */
16277c478bd9Sstevel@tonic-gate 	if (type == HTTP_REQ_TYPE_GET && curpos >= 0) {
1628cb4c2b01Svh115876 		offset_t endpos;
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate 		requestlen += 13 + count_digits(curpos) + 1 + 2;
16317c478bd9Sstevel@tonic-gate 		if (len > 0) {
16327c478bd9Sstevel@tonic-gate 			endpos = curpos + len - 1;
16337c478bd9Sstevel@tonic-gate 			requestlen += count_digits(endpos);
16347c478bd9Sstevel@tonic-gate 		}
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 		if ((newreq = realloc(request, requestlen)) == NULL) {
16377c478bd9Sstevel@tonic-gate 			free(request);
16387c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16397c478bd9Sstevel@tonic-gate 			return (-1);
16407c478bd9Sstevel@tonic-gate 		}
16417c478bd9Sstevel@tonic-gate 		request = newreq;
16427c478bd9Sstevel@tonic-gate 
1643e00d7246Svh115876 		j += sprintf(&request[j], "Range: bytes=%lld-", curpos);
16447c478bd9Sstevel@tonic-gate 		if (len > 0)
1645e00d7246Svh115876 			j += sprintf(&request[j], "%lld", endpos);
16467c478bd9Sstevel@tonic-gate 		j += sprintf(&request[j], "\r\n");
16477c478bd9Sstevel@tonic-gate 	}
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate 	/*
16507c478bd9Sstevel@tonic-gate 	 * Authorization is added only if provided (RFC 2617, Section 2)
16517c478bd9Sstevel@tonic-gate 	 *
16527c478bd9Sstevel@tonic-gate 	 * "Authorization: Basic " + authencstr + "\r\n"
16537c478bd9Sstevel@tonic-gate 	 */
16547c478bd9Sstevel@tonic-gate 	if (c_id->basic_auth_userid && c_id->basic_auth_password) {
16557c478bd9Sstevel@tonic-gate 		char *authstr;
16567c478bd9Sstevel@tonic-gate 		char *authencstr;
16577c478bd9Sstevel@tonic-gate 		int authlen;
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate 		/*
16607c478bd9Sstevel@tonic-gate 		 * Allow for concat(basic_auth_userid ":" basic_auth_password)
16617c478bd9Sstevel@tonic-gate 		 */
16627c478bd9Sstevel@tonic-gate 		authlen = strlen(c_id->basic_auth_userid) + 2 +
16637c478bd9Sstevel@tonic-gate 		    strlen(c_id->basic_auth_password);
16647c478bd9Sstevel@tonic-gate 		if ((authstr = malloc(authlen + 1)) == NULL) {
16657c478bd9Sstevel@tonic-gate 			free(request);
16667c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16677c478bd9Sstevel@tonic-gate 			return (-1);
16687c478bd9Sstevel@tonic-gate 		}
16697c478bd9Sstevel@tonic-gate 		(void) snprintf(authstr, authlen + 1, "%s:%s",
16707c478bd9Sstevel@tonic-gate 		    c_id->basic_auth_userid, c_id->basic_auth_password);
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 		/* 3 bytes encoded as 4 (round up) with null termination */
16737c478bd9Sstevel@tonic-gate 		if ((authencstr = malloc((authlen + 2) / 3 * 4 + 1)) == NULL) {
16747c478bd9Sstevel@tonic-gate 			free(authstr);
16757c478bd9Sstevel@tonic-gate 			free(request);
16767c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16777c478bd9Sstevel@tonic-gate 			return (-1);
16787c478bd9Sstevel@tonic-gate 		}
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 		(void) EVP_EncodeBlock((unsigned char *)authencstr,
16817c478bd9Sstevel@tonic-gate 		    (unsigned char *)authstr, authlen);
16827c478bd9Sstevel@tonic-gate 
16837c478bd9Sstevel@tonic-gate 		/*
16847c478bd9Sstevel@tonic-gate 		 * Finally do concat(Authorization: Basic " authencstr "\r\n")
16857c478bd9Sstevel@tonic-gate 		 */
16867c478bd9Sstevel@tonic-gate 		requestlen += 21 + strlen(authencstr) + 2;
16877c478bd9Sstevel@tonic-gate 		if ((newreq = realloc(request, requestlen)) == NULL) {
16887c478bd9Sstevel@tonic-gate 			free(authencstr);
16897c478bd9Sstevel@tonic-gate 			free(authstr);
16907c478bd9Sstevel@tonic-gate 			free(request);
16917c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16927c478bd9Sstevel@tonic-gate 			return (-1);
16937c478bd9Sstevel@tonic-gate 		}
16947c478bd9Sstevel@tonic-gate 		request = newreq;
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 		j += snprintf(&request[j], requestlen - j,
16977c478bd9Sstevel@tonic-gate 		    "Authorization: Basic %s\r\n", authencstr);
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 		free(authencstr);
17007c478bd9Sstevel@tonic-gate 		free(authstr);
17017c478bd9Sstevel@tonic-gate 	}
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate 	j += sprintf(&request[j], "\r\n");
17047c478bd9Sstevel@tonic-gate 
17057c478bd9Sstevel@tonic-gate 	if (verbosemode)
17067c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "%s", request);
17077c478bd9Sstevel@tonic-gate 
17087c478bd9Sstevel@tonic-gate 	/* send the HTTP request */
17097c478bd9Sstevel@tonic-gate 	retval = http_srv_send(c_id, request, j);
17107c478bd9Sstevel@tonic-gate 
17117c478bd9Sstevel@tonic-gate 	free(request);
17127c478bd9Sstevel@tonic-gate 	if (retval != j) {
17137c478bd9Sstevel@tonic-gate 		/* Assume error in was set by send request. */
17147c478bd9Sstevel@tonic-gate 		return (-1);
17157c478bd9Sstevel@tonic-gate 	}
17167c478bd9Sstevel@tonic-gate 
17177c478bd9Sstevel@tonic-gate 	return (0);
17187c478bd9Sstevel@tonic-gate }
17197c478bd9Sstevel@tonic-gate 
17207c478bd9Sstevel@tonic-gate /*
17217c478bd9Sstevel@tonic-gate  * password_cb - Callback to get private key password and return it
17227c478bd9Sstevel@tonic-gate  *               to SSL.  (Used for PEM certificates only.)
17237c478bd9Sstevel@tonic-gate  *
17247c478bd9Sstevel@tonic-gate  * 	len = passwd_cb(buf, buflen, rwflag, userdata);
17257c478bd9Sstevel@tonic-gate  *
17267c478bd9Sstevel@tonic-gate  *  Arguments:
17277c478bd9Sstevel@tonic-gate  *     buf	- Buffer for the password
17287c478bd9Sstevel@tonic-gate  *     buflen	- Length of 'buf'
17297c478bd9Sstevel@tonic-gate  *     rwflag	- password will be used for reading/decryption (== 0)
17307c478bd9Sstevel@tonic-gate  *		  or writing/encryption (== 1).
17317c478bd9Sstevel@tonic-gate  *     userdata	- Points to connection-specific information.
17327c478bd9Sstevel@tonic-gate  *
17337c478bd9Sstevel@tonic-gate  *  Returns:
17347c478bd9Sstevel@tonic-gate  *     > 0	- Length of password that was put into 'buf'.
17357c478bd9Sstevel@tonic-gate  *     0 	- No password was returned (usually error occurred)
17367c478bd9Sstevel@tonic-gate  *
17377c478bd9Sstevel@tonic-gate  * NOTE:  The password code is not thread safe
17387c478bd9Sstevel@tonic-gate  */
17397c478bd9Sstevel@tonic-gate /* ARGSUSED */
17407c478bd9Sstevel@tonic-gate static int
17417c478bd9Sstevel@tonic-gate password_cb(char *buf, int buflen, int rwflag, void *userdata)
17427c478bd9Sstevel@tonic-gate {
17437c478bd9Sstevel@tonic-gate 	http_conn_t *c_id = userdata;
17447c478bd9Sstevel@tonic-gate 
17457c478bd9Sstevel@tonic-gate 	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO)
17467c478bd9Sstevel@tonic-gate 		return (0);
17477c478bd9Sstevel@tonic-gate 
17487c478bd9Sstevel@tonic-gate 	if (c_id->file_password == NULL ||
17497c478bd9Sstevel@tonic-gate 	    buflen < strlen(c_id->file_password) + 1)
17507c478bd9Sstevel@tonic-gate 		return (0);
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate 	return (strlcpy(buf, c_id->file_password, buflen));
17537c478bd9Sstevel@tonic-gate }
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate /*
17567c478bd9Sstevel@tonic-gate  * initialize_ctx - Initialize the context for a connection.
17577c478bd9Sstevel@tonic-gate  *
17587c478bd9Sstevel@tonic-gate  *       ctx = initialize_ctx(c_id);
17597c478bd9Sstevel@tonic-gate  *
17607c478bd9Sstevel@tonic-gate  *  Arguments:
17617c478bd9Sstevel@tonic-gate  *     None.
17627c478bd9Sstevel@tonic-gate  *
17637c478bd9Sstevel@tonic-gate  *  Returns:
17647c478bd9Sstevel@tonic-gate  *     non-NULL	- Points to ctx structure.
17657c478bd9Sstevel@tonic-gate  *     NULL	- An error occurred.  Any cleanup is done and error
17667c478bd9Sstevel@tonic-gate  *                information is in the error stack.
17677c478bd9Sstevel@tonic-gate  */
17687c478bd9Sstevel@tonic-gate static SSL_CTX *
17697c478bd9Sstevel@tonic-gate initialize_ctx(http_conn_t *c_id)
17707c478bd9Sstevel@tonic-gate {
177170f9559bSTheo Schlossnagle #if OPENSSL_VERSION_NUMBER < 0x10000000L
17727c478bd9Sstevel@tonic-gate 	SSL_METHOD	*meth;
177370f9559bSTheo Schlossnagle #else
177470f9559bSTheo Schlossnagle 	const SSL_METHOD	*meth;
177570f9559bSTheo Schlossnagle #endif
17767c478bd9Sstevel@tonic-gate 	SSL_CTX		*ctx;
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 	ERR_clear_error();
17797c478bd9Sstevel@tonic-gate 
17807c478bd9Sstevel@tonic-gate 	/* Global system initialization */
17817c478bd9Sstevel@tonic-gate 	if (ssl_init == 0) {
17827c478bd9Sstevel@tonic-gate 		sunw_crypto_init();
17837c478bd9Sstevel@tonic-gate 		SSL_load_error_strings();
17847c478bd9Sstevel@tonic-gate 		ssl_init = 1;
17857c478bd9Sstevel@tonic-gate 	}
17867c478bd9Sstevel@tonic-gate 
17877c478bd9Sstevel@tonic-gate 	/* Create our context */
17887c478bd9Sstevel@tonic-gate 	meth = SSLv3_client_method();
17897c478bd9Sstevel@tonic-gate 	if ((ctx = SSL_CTX_new(meth)) == NULL) {
17907c478bd9Sstevel@tonic-gate 		ulong_t err;
17917c478bd9Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
17927c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
17937c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
17947c478bd9Sstevel@tonic-gate 			    "initialize_ctx: SSL_CTX_new returned NULL");
17957c478bd9Sstevel@tonic-gate 		return (NULL);
17967c478bd9Sstevel@tonic-gate 	}
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 	/*
17997c478bd9Sstevel@tonic-gate 	 * Ensure that any renegotiations for blocking connections will
18007c478bd9Sstevel@tonic-gate 	 * be done automatically.  (The alternative is to return partial
18017c478bd9Sstevel@tonic-gate 	 * reads to the caller and let it oversee the renegotiations.)
18027c478bd9Sstevel@tonic-gate 	 */
18037c478bd9Sstevel@tonic-gate 	if (SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY) == 0) {
18047c478bd9Sstevel@tonic-gate 		ulong_t err;
18057c478bd9Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
18067c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
18077c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
18087c478bd9Sstevel@tonic-gate 			    "initialize_ctx: SSL_CTX_set_mode returned 0");
18097c478bd9Sstevel@tonic-gate 		(void) SSL_CTX_free(ctx);
18107c478bd9Sstevel@tonic-gate 		return (NULL);
18117c478bd9Sstevel@tonic-gate 	}
18127c478bd9Sstevel@tonic-gate 
18137c478bd9Sstevel@tonic-gate 	/* set cipher list if provided */
18147c478bd9Sstevel@tonic-gate 	if (cipher_list != NULL) {
18157c478bd9Sstevel@tonic-gate 		if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
18167c478bd9Sstevel@tonic-gate 			ulong_t err;
18177c478bd9Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
18187c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
18197c478bd9Sstevel@tonic-gate 				libbootlog(BOOTLOG_CRIT,
18207c478bd9Sstevel@tonic-gate 				    "initialize_ctx: Error in cipher list");
18217c478bd9Sstevel@tonic-gate 			SSL_CTX_free(ctx);
18227c478bd9Sstevel@tonic-gate 			return (NULL);
18237c478bd9Sstevel@tonic-gate 		}
18247c478bd9Sstevel@tonic-gate 	}
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate 	/*
18277c478bd9Sstevel@tonic-gate 	 * We attempt to use the client_certificate_file for the private
18287c478bd9Sstevel@tonic-gate 	 * key input scheme *only* in the absence of private_key_file. In
18297c478bd9Sstevel@tonic-gate 	 * this instance the scheme will be the same as that used for the
18307c478bd9Sstevel@tonic-gate 	 * certificate input.
18317c478bd9Sstevel@tonic-gate 	 */
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate 	/* Load our certificates */
18347c478bd9Sstevel@tonic-gate 	if (c_id->client_cert_file != NULL) {
18357c478bd9Sstevel@tonic-gate 		if (p12_format) {
18367c478bd9Sstevel@tonic-gate 			/* Load pkcs12-formated files */
18377c478bd9Sstevel@tonic-gate 			if (sunw_p12_use_certfile(ctx, c_id->client_cert_file,
18387c478bd9Sstevel@tonic-gate 			    c_id->file_password)
18397c478bd9Sstevel@tonic-gate 			    <= 0) {
18407c478bd9Sstevel@tonic-gate 				ulong_t err;
18417c478bd9Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18427c478bd9Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18437c478bd9Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18447c478bd9Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18457c478bd9Sstevel@tonic-gate 					    "PKCS12 certificate file");
18467c478bd9Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18477c478bd9Sstevel@tonic-gate 				return (NULL);
18487c478bd9Sstevel@tonic-gate 			}
18497c478bd9Sstevel@tonic-gate 		} else {
18507c478bd9Sstevel@tonic-gate 			/* Load PEM-formated files */
18517c478bd9Sstevel@tonic-gate 			if (SSL_CTX_use_certificate_file(ctx,
18527c478bd9Sstevel@tonic-gate 			    c_id->client_cert_file, SSL_FILETYPE_PEM) <= 0) {
18537c478bd9Sstevel@tonic-gate 				ulong_t err;
18547c478bd9Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18557c478bd9Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18567c478bd9Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18577c478bd9Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18587c478bd9Sstevel@tonic-gate 					    "PEM certificate file");
18597c478bd9Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18607c478bd9Sstevel@tonic-gate 				return (NULL);
18617c478bd9Sstevel@tonic-gate 			}
18627c478bd9Sstevel@tonic-gate 		}
18637c478bd9Sstevel@tonic-gate 		if (c_id->private_key_file == NULL)
18647c478bd9Sstevel@tonic-gate 			c_id->private_key_file = c_id->client_cert_file;
18657c478bd9Sstevel@tonic-gate 	}
18667c478bd9Sstevel@tonic-gate 
18677c478bd9Sstevel@tonic-gate 	/* Load our keys */
18687c478bd9Sstevel@tonic-gate 	if (p12_format) {
18697c478bd9Sstevel@tonic-gate 		/* Load pkcs12-formated files */
18707c478bd9Sstevel@tonic-gate 		if (c_id->private_key_file != NULL) {
18717c478bd9Sstevel@tonic-gate 			if (sunw_p12_use_keyfile(ctx, c_id->private_key_file,
18727c478bd9Sstevel@tonic-gate 			    c_id->file_password)
18737c478bd9Sstevel@tonic-gate 			    <= 0) {
18747c478bd9Sstevel@tonic-gate 				ulong_t err;
18757c478bd9Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18767c478bd9Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18777c478bd9Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18787c478bd9Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18797c478bd9Sstevel@tonic-gate 					    "PKCS12 key file");
18807c478bd9Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18817c478bd9Sstevel@tonic-gate 				return (NULL);
18827c478bd9Sstevel@tonic-gate 			}
18837c478bd9Sstevel@tonic-gate 		}
18847c478bd9Sstevel@tonic-gate 	} else {
18857c478bd9Sstevel@tonic-gate 		/* Load PEM-formated files */
18867c478bd9Sstevel@tonic-gate 		SSL_CTX_set_default_passwd_cb(ctx, password_cb);
18877c478bd9Sstevel@tonic-gate 		SSL_CTX_set_default_passwd_cb_userdata(ctx, c_id);
18887c478bd9Sstevel@tonic-gate 		if (c_id->private_key_file != NULL) {
18897c478bd9Sstevel@tonic-gate 			if (SSL_CTX_use_PrivateKey_file(ctx,
18907c478bd9Sstevel@tonic-gate 			    c_id->private_key_file, SSL_FILETYPE_PEM) <= 0) {
18917c478bd9Sstevel@tonic-gate 				ulong_t err;
18927c478bd9Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18937c478bd9Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18947c478bd9Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18957c478bd9Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18967c478bd9Sstevel@tonic-gate 					    "PEM key file");
18977c478bd9Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18987c478bd9Sstevel@tonic-gate 				return (NULL);
18997c478bd9Sstevel@tonic-gate 			}
19007c478bd9Sstevel@tonic-gate 		}
19017c478bd9Sstevel@tonic-gate 	}
19027c478bd9Sstevel@tonic-gate 
19037c478bd9Sstevel@tonic-gate 	/* Load the CAs we trust */
19047c478bd9Sstevel@tonic-gate 	if (ca_verify_file != NULL) {
19057c478bd9Sstevel@tonic-gate 		if (p12_format) {
19067c478bd9Sstevel@tonic-gate 			if (sunw_p12_use_trustfile(ctx, ca_verify_file,
19077c478bd9Sstevel@tonic-gate 			    c_id->file_password)
19087c478bd9Sstevel@tonic-gate 			    <= 0) {
19097c478bd9Sstevel@tonic-gate 				ulong_t err;
19107c478bd9Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
19117c478bd9Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
19127c478bd9Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
19137c478bd9Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
19147c478bd9Sstevel@tonic-gate 					    "PKCS12 CA list file");
19157c478bd9Sstevel@tonic-gate 				SSL_CTX_free(ctx);
19167c478bd9Sstevel@tonic-gate 				return (NULL);
19177c478bd9Sstevel@tonic-gate 			}
19187c478bd9Sstevel@tonic-gate 		} else {
19197c478bd9Sstevel@tonic-gate 			if (SSL_CTX_load_verify_locations(ctx, ca_verify_file,
19207c478bd9Sstevel@tonic-gate 			    NULL) == 0) {
19217c478bd9Sstevel@tonic-gate 				ulong_t err;
19227c478bd9Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
19237c478bd9Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
19247c478bd9Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
19257c478bd9Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read PEM"
19267c478bd9Sstevel@tonic-gate 					    " CA list file");
19277c478bd9Sstevel@tonic-gate 				SSL_CTX_free(ctx);
19287c478bd9Sstevel@tonic-gate 				return (NULL);
19297c478bd9Sstevel@tonic-gate 			}
19307c478bd9Sstevel@tonic-gate 		}
19317c478bd9Sstevel@tonic-gate 	}
19327c478bd9Sstevel@tonic-gate 
19337c478bd9Sstevel@tonic-gate 	SSL_CTX_set_verify_depth(ctx, verify_depth);
19347c478bd9Sstevel@tonic-gate 
19357c478bd9Sstevel@tonic-gate 	/* Load randomness */
19367c478bd9Sstevel@tonic-gate 	if (c_id->random_file != NULL &&
19377c478bd9Sstevel@tonic-gate 	    RAND_load_file(c_id->random_file, 1024 * 1024) <= 0) {
19387c478bd9Sstevel@tonic-gate 		ulong_t err;
19397c478bd9Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
19407c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
19417c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
19427c478bd9Sstevel@tonic-gate 			    "initialize_ctx: Couldn't load random file");
19437c478bd9Sstevel@tonic-gate 		SSL_CTX_free(ctx);
19447c478bd9Sstevel@tonic-gate 		return (NULL);
19457c478bd9Sstevel@tonic-gate 	}
19467c478bd9Sstevel@tonic-gate 	if (RAND_status() <= 0) {
19477c478bd9Sstevel@tonic-gate 		ulong_t err;
19487c478bd9Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
19497c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
19507c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
19517c478bd9Sstevel@tonic-gate 			    "initialize_ctx: PRNG not seeded");
19527c478bd9Sstevel@tonic-gate 		SSL_CTX_free(ctx);
19537c478bd9Sstevel@tonic-gate 		return (NULL);
19547c478bd9Sstevel@tonic-gate 	}
19557c478bd9Sstevel@tonic-gate 
19567c478bd9Sstevel@tonic-gate 	return (ctx);
19577c478bd9Sstevel@tonic-gate }
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate /*
19607c478bd9Sstevel@tonic-gate  * tcp_connect - Set up a TCP connection.
19617c478bd9Sstevel@tonic-gate  *
19627c478bd9Sstevel@tonic-gate  *         sock = tcp_connect(c_id, hostname, port);
19637c478bd9Sstevel@tonic-gate  *
19647c478bd9Sstevel@tonic-gate  * Arguments:
19657c478bd9Sstevel@tonic-gate  *      c_id	 - Structure associated with the desired connection
19667c478bd9Sstevel@tonic-gate  *	hostname - the host to connect to
19677c478bd9Sstevel@tonic-gate  *	port	 - the port to connect to
19687c478bd9Sstevel@tonic-gate  *
19697c478bd9Sstevel@tonic-gate  * Returns:
19707c478bd9Sstevel@tonic-gate  *      >= 0	- Socket number.
19717c478bd9Sstevel@tonic-gate  *      -1	- Error occurred.  Error information is set in the
19727c478bd9Sstevel@tonic-gate  *                error stack.  Any cleanup is done.
19737c478bd9Sstevel@tonic-gate  *
19747c478bd9Sstevel@tonic-gate  * This function established a connection to the target host.  When
19757c478bd9Sstevel@tonic-gate  * it returns, the connection is ready for a HEAD or GET request.
19767c478bd9Sstevel@tonic-gate  */
19777c478bd9Sstevel@tonic-gate static int
19787c478bd9Sstevel@tonic-gate tcp_connect(http_conn_t *c_id, const char *hostname, uint16_t port)
19797c478bd9Sstevel@tonic-gate {
19807c478bd9Sstevel@tonic-gate 	struct hostent	*hp;
19817c478bd9Sstevel@tonic-gate 	struct sockaddr_in addr;
19827c478bd9Sstevel@tonic-gate 	int	sock;
19837c478bd9Sstevel@tonic-gate 	int	status;
19847c478bd9Sstevel@tonic-gate 
19857c478bd9Sstevel@tonic-gate 	if ((hp = gethostbyname(hostname)) == NULL) {
19867c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_RESOLVE, h_errno);
19877c478bd9Sstevel@tonic-gate 		return (-1);
19887c478bd9Sstevel@tonic-gate 	}
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate 	bzero(&addr, sizeof (addr));
19917c478bd9Sstevel@tonic-gate 	/* LINTED */
19927c478bd9Sstevel@tonic-gate 	addr.sin_addr = *(struct in_addr *)hp->h_addr;
19937c478bd9Sstevel@tonic-gate 	addr.sin_family = AF_INET;
19947c478bd9Sstevel@tonic-gate 	addr.sin_port = htons(port);
19957c478bd9Sstevel@tonic-gate 
19967c478bd9Sstevel@tonic-gate 	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
19977c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
19987c478bd9Sstevel@tonic-gate 		return (-1);
19997c478bd9Sstevel@tonic-gate 	}
20007c478bd9Sstevel@tonic-gate 
20017c478bd9Sstevel@tonic-gate 	status = connect(sock, (struct sockaddr *)&addr, sizeof (addr));
20027c478bd9Sstevel@tonic-gate 	if (status < 0) {
20037c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
20047c478bd9Sstevel@tonic-gate 		(void) socket_close(sock);
20057c478bd9Sstevel@tonic-gate 		return (-1);
20067c478bd9Sstevel@tonic-gate 	}
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 	c_id->host_addr = addr;	/* save for future sendto calls */
20097c478bd9Sstevel@tonic-gate 	c_id->fd = sock;
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 	return (sock);
20127c478bd9Sstevel@tonic-gate }
20137c478bd9Sstevel@tonic-gate 
20147c478bd9Sstevel@tonic-gate /*
20157c478bd9Sstevel@tonic-gate  * readline - Get a line from the socket.  Discard the end-of-line
20167c478bd9Sstevel@tonic-gate  *            (CR or CR/LF or LF).
20177c478bd9Sstevel@tonic-gate  *
20187c478bd9Sstevel@tonic-gate  *         ret = readline(c_id, sock, buf, len);
20197c478bd9Sstevel@tonic-gate  *
20207c478bd9Sstevel@tonic-gate  * Arguments:
20217c478bd9Sstevel@tonic-gate  *      c_id	- Structure associated with the desired connection
20227c478bd9Sstevel@tonic-gate  *      sock	- Socket to read
20237c478bd9Sstevel@tonic-gate  *      buf   	- Buffer for the line
20247c478bd9Sstevel@tonic-gate  *      len	- Length of the buffer
20257c478bd9Sstevel@tonic-gate  *
20267c478bd9Sstevel@tonic-gate  * Returns:
20277c478bd9Sstevel@tonic-gate  *      0	- Success.  'buf' contains the line.
20287c478bd9Sstevel@tonic-gate  *      -1	- Error occurred.  Error information is set in the
20297c478bd9Sstevel@tonic-gate  *                error stack.
20307c478bd9Sstevel@tonic-gate  */
20317c478bd9Sstevel@tonic-gate static int
20327c478bd9Sstevel@tonic-gate readline(http_conn_t *c_id, int sock, char *buf, int len)
20337c478bd9Sstevel@tonic-gate {
20347c478bd9Sstevel@tonic-gate 	int	n, r;
20357c478bd9Sstevel@tonic-gate 	char	*ptr = buf;
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	for (n = 0; n < len; n++) {
20387c478bd9Sstevel@tonic-gate 		r = socket_read(sock, ptr, 1, c_id->read_timeout);
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 		if (r < 0) {
20417c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
20427c478bd9Sstevel@tonic-gate 			return (-1);
20437c478bd9Sstevel@tonic-gate 		} else if (r == 0) {
20447c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_WARNING, "Readline: no data");
20457c478bd9Sstevel@tonic-gate 			return (0);
20467c478bd9Sstevel@tonic-gate 		}
20477c478bd9Sstevel@tonic-gate 
20487c478bd9Sstevel@tonic-gate 		if (*ptr == '\n') {
20497c478bd9Sstevel@tonic-gate 			*ptr = '\0';
20507c478bd9Sstevel@tonic-gate 
20517c478bd9Sstevel@tonic-gate 			/* Strip off the CR if it's there */
20527c478bd9Sstevel@tonic-gate 			if (buf[n-1] == '\r') {
20537c478bd9Sstevel@tonic-gate 				buf[n-1] = '\0';
20547c478bd9Sstevel@tonic-gate 				n--;
20557c478bd9Sstevel@tonic-gate 			}
20567c478bd9Sstevel@tonic-gate 
20577c478bd9Sstevel@tonic-gate 			return (n);
20587c478bd9Sstevel@tonic-gate 		}
20597c478bd9Sstevel@tonic-gate 
20607c478bd9Sstevel@tonic-gate 		ptr++;
20617c478bd9Sstevel@tonic-gate 	}
20627c478bd9Sstevel@tonic-gate 
20637c478bd9Sstevel@tonic-gate 	libbootlog(BOOTLOG_WARNING, "readline: Buffer too short\n");
20647c478bd9Sstevel@tonic-gate 	return (0);
20657c478bd9Sstevel@tonic-gate }
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate /*
20687c478bd9Sstevel@tonic-gate  * proxy_connect - Set up a proxied TCP connection to the target host.
20697c478bd9Sstevel@tonic-gate  *
20707c478bd9Sstevel@tonic-gate  *         sock = proxy_connect(c_id);
20717c478bd9Sstevel@tonic-gate  *
20727c478bd9Sstevel@tonic-gate  * Arguments:
20737c478bd9Sstevel@tonic-gate  *      c_id  -	Structure associated with the desired connection
20747c478bd9Sstevel@tonic-gate  *
20757c478bd9Sstevel@tonic-gate  * Returns:
20767c478bd9Sstevel@tonic-gate  *      >= 0	- Socket number.
20777c478bd9Sstevel@tonic-gate  *      -1	- Error occurred.  Error information is set in the
20787c478bd9Sstevel@tonic-gate  *                error stack.  Any cleanup is done.
20797c478bd9Sstevel@tonic-gate  *
20807c478bd9Sstevel@tonic-gate  * This function established a connection to the proxy and then sends
20817c478bd9Sstevel@tonic-gate  * the request to connect to the target host.  It reads the response
20827c478bd9Sstevel@tonic-gate  * (the status line and any headers).  When it returns, the connection
20837c478bd9Sstevel@tonic-gate  * is ready for a HEAD or GET request.
20847c478bd9Sstevel@tonic-gate  */
20857c478bd9Sstevel@tonic-gate static int
20867c478bd9Sstevel@tonic-gate proxy_connect(http_conn_t *c_id)
20877c478bd9Sstevel@tonic-gate {
20887c478bd9Sstevel@tonic-gate 	struct sockaddr_in addr;
20897c478bd9Sstevel@tonic-gate 	int	sock;
20907c478bd9Sstevel@tonic-gate 	char	buf[1024];
20917c478bd9Sstevel@tonic-gate 	char	*ptr;
20927c478bd9Sstevel@tonic-gate 	int	i;
20937c478bd9Sstevel@tonic-gate 
20947c478bd9Sstevel@tonic-gate 	if ((sock = tcp_connect(c_id, CONN_PROXY_HOSTNAME,
20957c478bd9Sstevel@tonic-gate 	    CONN_PROXY_PORT)) < 0) {
20967c478bd9Sstevel@tonic-gate 		return (-1);
20977c478bd9Sstevel@tonic-gate 	}
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	if (!CONN_HTTPS) {
21007c478bd9Sstevel@tonic-gate 		return (sock);
21017c478bd9Sstevel@tonic-gate 	}
21027c478bd9Sstevel@tonic-gate 
21037c478bd9Sstevel@tonic-gate 	/* Now that we're connected, do the proxy request */
21047c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
21057c478bd9Sstevel@tonic-gate 	    "CONNECT %s:%d HTTP/1.0\r\n\r\n", CONN_HOSTNAME, CONN_PORT);
21067c478bd9Sstevel@tonic-gate 
21077c478bd9Sstevel@tonic-gate 	/* socket_write sets the errors */
21087c478bd9Sstevel@tonic-gate 	if (socket_write(sock, buf, strlen(buf), &addr) <= 0) {
21097c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
21107c478bd9Sstevel@tonic-gate 		(void) socket_close(sock);
21117c478bd9Sstevel@tonic-gate 		return (-1);
21127c478bd9Sstevel@tonic-gate 	}
21137c478bd9Sstevel@tonic-gate 
21147c478bd9Sstevel@tonic-gate 	/* And read the response */
21157c478bd9Sstevel@tonic-gate 	i = readline(c_id, sock, buf, sizeof (buf));
21167c478bd9Sstevel@tonic-gate 	if (i <= 0) {
21177c478bd9Sstevel@tonic-gate 		if (i == 0)
21187c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NORESP);
21197c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
21207c478bd9Sstevel@tonic-gate 			    "proxy_connect: Empty response from proxy");
21217c478bd9Sstevel@tonic-gate 		(void) socket_close(sock);
21227c478bd9Sstevel@tonic-gate 		return (-1);
21237c478bd9Sstevel@tonic-gate 	}
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 	ptr = buf;
21267c478bd9Sstevel@tonic-gate 	if (strncmp(ptr, "HTTP", 4) != 0) {
21277c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOT_1_1);
21287c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
21297c478bd9Sstevel@tonic-gate 		    "proxy_connect: Unrecognized protocol");
21307c478bd9Sstevel@tonic-gate 		(void) socket_close(sock);
21317c478bd9Sstevel@tonic-gate 		return (-1);
21327c478bd9Sstevel@tonic-gate 	}
21337c478bd9Sstevel@tonic-gate 
21347c478bd9Sstevel@tonic-gate 	/* skip to the code */
21357c478bd9Sstevel@tonic-gate 	ptr += 4;
21367c478bd9Sstevel@tonic-gate 	while (*ptr != ' ' && *ptr != '\0')
21377c478bd9Sstevel@tonic-gate 		ptr++;
21387c478bd9Sstevel@tonic-gate 	while (*ptr == ' ' && *ptr != '\0')
21397c478bd9Sstevel@tonic-gate 		ptr++;
21407c478bd9Sstevel@tonic-gate 
21417c478bd9Sstevel@tonic-gate 	/* make sure it's three digits */
21427c478bd9Sstevel@tonic-gate 	if (strncmp(ptr, "200", 3) != 0) {
21437c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADRESP);
21447c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
21457c478bd9Sstevel@tonic-gate 		    "proxy_connect: Received error from proxy server");
21467c478bd9Sstevel@tonic-gate 		(void) socket_close(sock);
21477c478bd9Sstevel@tonic-gate 		return (-1);
21487c478bd9Sstevel@tonic-gate 	}
21497c478bd9Sstevel@tonic-gate 	ptr += 3;
21507c478bd9Sstevel@tonic-gate 	if (isdigit(*ptr)) {
21517c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADRESP);
21527c478bd9Sstevel@tonic-gate 		(void) socket_close(sock);
21537c478bd9Sstevel@tonic-gate 		return (-1);
21547c478bd9Sstevel@tonic-gate 	}
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate 	/* Look for the blank line that signals end of proxy header */
21577c478bd9Sstevel@tonic-gate 	while ((i = readline(c_id, sock, buf, sizeof (buf))) > 0)
21587c478bd9Sstevel@tonic-gate 		;
21597c478bd9Sstevel@tonic-gate 
21607c478bd9Sstevel@tonic-gate 	if (i < 0) {
21617c478bd9Sstevel@tonic-gate 		(void) socket_close(sock);
21627c478bd9Sstevel@tonic-gate 		return (-1);
21637c478bd9Sstevel@tonic-gate 	}
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 	return (sock);
21667c478bd9Sstevel@tonic-gate }
21677c478bd9Sstevel@tonic-gate 
21687c478bd9Sstevel@tonic-gate /*
21697c478bd9Sstevel@tonic-gate  * check_cert_chain - Check if we have a valid certificate chain.
21707c478bd9Sstevel@tonic-gate  *
21717c478bd9Sstevel@tonic-gate  *      ret = check_cert_chain(c_id, host);
21727c478bd9Sstevel@tonic-gate  *
21737c478bd9Sstevel@tonic-gate  * Arguments:
21747c478bd9Sstevel@tonic-gate  *    c_id	- Connection info.
21757c478bd9Sstevel@tonic-gate  *    host	- Name to compare with the common name in the certificate.
21767c478bd9Sstevel@tonic-gate  *
21777c478bd9Sstevel@tonic-gate  * Returns:
21787c478bd9Sstevel@tonic-gate  *    0		- Certificate chain and common name are both OK.
21797c478bd9Sstevel@tonic-gate  *    -1	- Certificate chain and/or common name is not valid.
21807c478bd9Sstevel@tonic-gate  */
21817c478bd9Sstevel@tonic-gate static int
21827c478bd9Sstevel@tonic-gate check_cert_chain(http_conn_t *c_id, char *host)
21837c478bd9Sstevel@tonic-gate {
21847c478bd9Sstevel@tonic-gate 	X509	*peer;
21857c478bd9Sstevel@tonic-gate 	char	peer_CN[256];
21867c478bd9Sstevel@tonic-gate 	long	verify_err;
21877c478bd9Sstevel@tonic-gate 
21887c478bd9Sstevel@tonic-gate 	if ((verify_err = SSL_get_verify_result(c_id->ssl)) != X509_V_OK) {
21897c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_VERIFERR, verify_err);
21907c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
21917c478bd9Sstevel@tonic-gate 		    "check_cert_chain: Certificate doesn't verify");
21927c478bd9Sstevel@tonic-gate 		return (-1);
21937c478bd9Sstevel@tonic-gate 	}
21947c478bd9Sstevel@tonic-gate 
21957c478bd9Sstevel@tonic-gate 	/*
21967c478bd9Sstevel@tonic-gate 	 * Check the cert chain. The chain length
21977c478bd9Sstevel@tonic-gate 	 * is automatically checked by OpenSSL when we
21987c478bd9Sstevel@tonic-gate 	 * set the verify depth in the ctx
21997c478bd9Sstevel@tonic-gate 	 *
22007c478bd9Sstevel@tonic-gate 	 * All we need to do here is check that the CN
22017c478bd9Sstevel@tonic-gate 	 * matches
22027c478bd9Sstevel@tonic-gate 	 */
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 	/* Check the common name */
22057c478bd9Sstevel@tonic-gate 	if ((peer = SSL_get_peer_certificate(c_id->ssl)) == NULL) {
22067c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOCERT);
22077c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
22087c478bd9Sstevel@tonic-gate 		    "check_cert_chain: Peer did not present a certificate");
22097c478bd9Sstevel@tonic-gate 		return (-1);
22107c478bd9Sstevel@tonic-gate 	}
22117c478bd9Sstevel@tonic-gate 	(void) X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
22127c478bd9Sstevel@tonic-gate 	    NID_commonName, peer_CN, 256);
22137c478bd9Sstevel@tonic-gate 
22147c478bd9Sstevel@tonic-gate 	if (verbosemode)
22157c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE,
22167c478bd9Sstevel@tonic-gate 		    "server cert's peer_CN is %s, host is %s", peer_CN, host);
22177c478bd9Sstevel@tonic-gate 
22187c478bd9Sstevel@tonic-gate 	if (strcasecmp(peer_CN, host)) {
22197c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMATCH);
22207c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
22217c478bd9Sstevel@tonic-gate 		    "check_cert_chain: Common name doesn't match host name");
22227c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
22237c478bd9Sstevel@tonic-gate 		    "peer_CN = %s, host = %s", peer_CN, host);
22247c478bd9Sstevel@tonic-gate 		return (-1);
22257c478bd9Sstevel@tonic-gate 	}
22267c478bd9Sstevel@tonic-gate 
22277c478bd9Sstevel@tonic-gate 	return (0);
22287c478bd9Sstevel@tonic-gate }
22297c478bd9Sstevel@tonic-gate 
22307c478bd9Sstevel@tonic-gate /*
22317c478bd9Sstevel@tonic-gate  * print_ciphers - Print the list of ciphers for debugging.
22327c478bd9Sstevel@tonic-gate  *
22337c478bd9Sstevel@tonic-gate  *       print_ciphers(ssl);
22347c478bd9Sstevel@tonic-gate  *
22357c478bd9Sstevel@tonic-gate  * Arguments:
22367c478bd9Sstevel@tonic-gate  *     ssl	- SSL connection.
22377c478bd9Sstevel@tonic-gate  *
22387c478bd9Sstevel@tonic-gate  * Returns:
22397c478bd9Sstevel@tonic-gate  *     none
22407c478bd9Sstevel@tonic-gate  */
22417c478bd9Sstevel@tonic-gate static void
22427c478bd9Sstevel@tonic-gate print_ciphers(SSL *ssl)
22437c478bd9Sstevel@tonic-gate {
22447c478bd9Sstevel@tonic-gate 	SSL_CIPHER	*c;
22457c478bd9Sstevel@tonic-gate 	STACK_OF(SSL_CIPHER)	*sk;
22467c478bd9Sstevel@tonic-gate 	int	i;
22477c478bd9Sstevel@tonic-gate 	const char	*name;
22487c478bd9Sstevel@tonic-gate 
22497c478bd9Sstevel@tonic-gate 	if (ssl == NULL)
22507c478bd9Sstevel@tonic-gate 		return;
22517c478bd9Sstevel@tonic-gate 
22527c478bd9Sstevel@tonic-gate 	sk = SSL_get_ciphers(ssl);
22537c478bd9Sstevel@tonic-gate 	if (sk == NULL)
22547c478bd9Sstevel@tonic-gate 		return;
22557c478bd9Sstevel@tonic-gate 
22567c478bd9Sstevel@tonic-gate 	for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
2257*d7141854SRobert Mustacchi 		/* LINTED */
22587c478bd9Sstevel@tonic-gate 		c = sk_SSL_CIPHER_value(sk, i);
22597c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "%08lx %s", c->id, c->name);
22607c478bd9Sstevel@tonic-gate 	}
22617c478bd9Sstevel@tonic-gate 	name = SSL_get_cipher_name(ssl);
22627c478bd9Sstevel@tonic-gate 	if (name == NULL)
22637c478bd9Sstevel@tonic-gate 		name = "";
22647c478bd9Sstevel@tonic-gate 	libbootlog(BOOTLOG_VERBOSE, "Current cipher = %s", name);
22657c478bd9Sstevel@tonic-gate }
22667c478bd9Sstevel@tonic-gate 
22677c478bd9Sstevel@tonic-gate /*
22687c478bd9Sstevel@tonic-gate  * read_headerlines - Get the header lines from the server.  This reads
22697c478bd9Sstevel@tonic-gate  *              lines until it gets a empty line indicating end of headers.
22707c478bd9Sstevel@tonic-gate  *
22717c478bd9Sstevel@tonic-gate  *       ret = read_headerlines(c_id);
22727c478bd9Sstevel@tonic-gate  *
22737c478bd9Sstevel@tonic-gate  * Arguments:
22747c478bd9Sstevel@tonic-gate  *     c_id	- Info about the connection being read.
22757c478bd9Sstevel@tonic-gate  *     bread	- TRUE if the headerlines are part of the message body.
22767c478bd9Sstevel@tonic-gate  *
22777c478bd9Sstevel@tonic-gate  * Returns:
22787c478bd9Sstevel@tonic-gate  *     0	- Header lines were read.
22797c478bd9Sstevel@tonic-gate  *     -1	- Error occurred.  The errors information is already in
22807c478bd9Sstevel@tonic-gate  *                the error stack.
22817c478bd9Sstevel@tonic-gate  *
22827c478bd9Sstevel@tonic-gate  *  Read the lines.  If the current line begins with a space or tab, it is
22837c478bd9Sstevel@tonic-gate  *  a continuation.  Take the new line and append it to the end of the
22847c478bd9Sstevel@tonic-gate  *  previous line rather than making an entry for another line in
22857c478bd9Sstevel@tonic-gate  *  c_id->resphdr.
22867c478bd9Sstevel@tonic-gate  *
22877c478bd9Sstevel@tonic-gate  *  Note that I/O errors are put into the error stack by http_srv_recv(),
228823a1cceaSRoger A. Faulkner  *  which is called by getaline().
22897c478bd9Sstevel@tonic-gate  */
22907c478bd9Sstevel@tonic-gate static int
22917c478bd9Sstevel@tonic-gate read_headerlines(http_conn_t *c_id, boolean_t bread)
22927c478bd9Sstevel@tonic-gate {
22937c478bd9Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
22947c478bd9Sstevel@tonic-gate 	char	**new_buf;
22957c478bd9Sstevel@tonic-gate 	char	*ptr;
22967c478bd9Sstevel@tonic-gate 	int	next;
22977c478bd9Sstevel@tonic-gate 	int	cur;
22987c478bd9Sstevel@tonic-gate 	int	n;
22997c478bd9Sstevel@tonic-gate 
23007c478bd9Sstevel@tonic-gate 	/* process headers, stop when we get to an empty line */
23017c478bd9Sstevel@tonic-gate 	cur = 0;
23027c478bd9Sstevel@tonic-gate 	next = 0;
230323a1cceaSRoger A. Faulkner 	while ((n = getaline(c_id, line, sizeof (line), bread)) > 0) {
23047c478bd9Sstevel@tonic-gate 
23057c478bd9Sstevel@tonic-gate 		if (verbosemode)
23067c478bd9Sstevel@tonic-gate 			libbootlog(BOOTLOG_VERBOSE,
23077c478bd9Sstevel@tonic-gate 			    "read_headerlines: %s", line);
23087c478bd9Sstevel@tonic-gate 		/*
23097c478bd9Sstevel@tonic-gate 		 * See if this is a continuation line (first col is a
23107c478bd9Sstevel@tonic-gate 		 * space or a tab)
23117c478bd9Sstevel@tonic-gate 		 */
23127c478bd9Sstevel@tonic-gate 		if (line[0] != ' ' && line[0] != '	') {
23137c478bd9Sstevel@tonic-gate 			cur = next;
23147c478bd9Sstevel@tonic-gate 			next ++;
23157c478bd9Sstevel@tonic-gate 			new_buf =
23167c478bd9Sstevel@tonic-gate 			    realloc(c_id->resphdr, (cur + 1) * sizeof (void *));
23177c478bd9Sstevel@tonic-gate 			if (new_buf == NULL) {
23187c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
23197c478bd9Sstevel@tonic-gate 				return (-1);
23207c478bd9Sstevel@tonic-gate 			}
23217c478bd9Sstevel@tonic-gate 			c_id->resphdr = new_buf;
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 			c_id->resphdr[cur] = strdup(line);
23247c478bd9Sstevel@tonic-gate 			if (c_id->resphdr[cur] == NULL) {
23257c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
23267c478bd9Sstevel@tonic-gate 				return (-1);
23277c478bd9Sstevel@tonic-gate 			}
23287c478bd9Sstevel@tonic-gate 		} else {
23297c478bd9Sstevel@tonic-gate 			ptr = line;
23307c478bd9Sstevel@tonic-gate 			while (isspace(*ptr))
23317c478bd9Sstevel@tonic-gate 				ptr ++;
23327c478bd9Sstevel@tonic-gate 			c_id->resphdr[cur] = realloc(c_id->resphdr[cur],
23337c478bd9Sstevel@tonic-gate 			    strlen(c_id->resphdr[cur]) + strlen(ptr) + 1);
23347c478bd9Sstevel@tonic-gate 			if (c_id->resphdr[cur] == NULL) {
23357c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
23367c478bd9Sstevel@tonic-gate 				return (-1);
23377c478bd9Sstevel@tonic-gate 			}
23387c478bd9Sstevel@tonic-gate 			(void) strcat(c_id->resphdr[cur], ptr);
23397c478bd9Sstevel@tonic-gate 		}
23407c478bd9Sstevel@tonic-gate 		ptr = &(c_id->resphdr[cur][strlen(c_id->resphdr[cur]) - 1]);
23417c478bd9Sstevel@tonic-gate 		while (ptr > c_id->resphdr[cur] && isspace(*ptr))
23427c478bd9Sstevel@tonic-gate 			ptr --;
23437c478bd9Sstevel@tonic-gate 	}
23447c478bd9Sstevel@tonic-gate 	c_id->resp.nresphdrs = next;
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 	/* Cause of any I/O error was already put into error stack. */
23477c478bd9Sstevel@tonic-gate 	return (n >= 0 ? 0 : -1);
23487c478bd9Sstevel@tonic-gate }
23497c478bd9Sstevel@tonic-gate 
23507c478bd9Sstevel@tonic-gate static void
23517c478bd9Sstevel@tonic-gate free_response(http_conn_t *c_id, int free_boundary)
23527c478bd9Sstevel@tonic-gate {
23537c478bd9Sstevel@tonic-gate 	int i;
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate 	/* free memory from previous calls */
23567c478bd9Sstevel@tonic-gate 	if (c_id->resp.statusmsg != NULL) {
23577c478bd9Sstevel@tonic-gate 		free(c_id->resp.statusmsg);
23587c478bd9Sstevel@tonic-gate 		c_id->resp.statusmsg = NULL;
23597c478bd9Sstevel@tonic-gate 	}
23607c478bd9Sstevel@tonic-gate 	for (i = 0; i < c_id->resp.nresphdrs; i++) {
23617c478bd9Sstevel@tonic-gate 		free(c_id->resphdr[i]);
23627c478bd9Sstevel@tonic-gate 		c_id->resphdr[i] = NULL;
23637c478bd9Sstevel@tonic-gate 	}
23647c478bd9Sstevel@tonic-gate 	c_id->resp.nresphdrs = 0;
23657c478bd9Sstevel@tonic-gate 	if (c_id->resphdr != NULL) {
23667c478bd9Sstevel@tonic-gate 		free(c_id->resphdr);
23677c478bd9Sstevel@tonic-gate 		c_id->resphdr = NULL;
23687c478bd9Sstevel@tonic-gate 	}
23697c478bd9Sstevel@tonic-gate 
23707c478bd9Sstevel@tonic-gate 	if (free_boundary && c_id->boundary) {
23717c478bd9Sstevel@tonic-gate 		free(c_id->boundary);
23727c478bd9Sstevel@tonic-gate 		c_id->boundary = NULL;
23737c478bd9Sstevel@tonic-gate 		c_id->is_multipart = B_FALSE;
23747c478bd9Sstevel@tonic-gate 	}
23757c478bd9Sstevel@tonic-gate }
23767c478bd9Sstevel@tonic-gate 
23777c478bd9Sstevel@tonic-gate static int
23787c478bd9Sstevel@tonic-gate free_ctx_ssl(http_conn_t *c_id)
23797c478bd9Sstevel@tonic-gate {
23807c478bd9Sstevel@tonic-gate 	int err_ret = 0;
23817c478bd9Sstevel@tonic-gate 
23827c478bd9Sstevel@tonic-gate 	if (c_id->ssl != NULL) {
23837c478bd9Sstevel@tonic-gate 		if (SSL_shutdown(c_id->ssl) <= 0) {
23847c478bd9Sstevel@tonic-gate 			ulong_t err;
23857c478bd9Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
23867c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
23877c478bd9Sstevel@tonic-gate 			err_ret = -1;
23887c478bd9Sstevel@tonic-gate 		}
23897c478bd9Sstevel@tonic-gate 		SSL_free(c_id->ssl);
23907c478bd9Sstevel@tonic-gate 		c_id->ssl = NULL;
23917c478bd9Sstevel@tonic-gate 	}
23927c478bd9Sstevel@tonic-gate 
23937c478bd9Sstevel@tonic-gate 	if (c_id->fd != -1 && socket_close(c_id->fd) < 0) {
23947c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
23957c478bd9Sstevel@tonic-gate 		err_ret = -1;
23967c478bd9Sstevel@tonic-gate 	}
23977c478bd9Sstevel@tonic-gate 	c_id->fd = -1;
23987c478bd9Sstevel@tonic-gate 
23997c478bd9Sstevel@tonic-gate 	if (c_id->ctx != NULL) {
24007c478bd9Sstevel@tonic-gate 		SSL_CTX_free(c_id->ctx);
24017c478bd9Sstevel@tonic-gate 		c_id->ctx = NULL;
24027c478bd9Sstevel@tonic-gate 	}
24037c478bd9Sstevel@tonic-gate 
24047c478bd9Sstevel@tonic-gate 	return (err_ret);
24057c478bd9Sstevel@tonic-gate }
24067c478bd9Sstevel@tonic-gate 
24077c478bd9Sstevel@tonic-gate /*
24087c478bd9Sstevel@tonic-gate  * get_chunk_header - Get a chunk header line
24097c478bd9Sstevel@tonic-gate  *
24107c478bd9Sstevel@tonic-gate  * Arguments:
24117c478bd9Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
24127c478bd9Sstevel@tonic-gate  *
24137c478bd9Sstevel@tonic-gate  * Returns:
24147c478bd9Sstevel@tonic-gate  *  >=0	- Length of next chunk
24157c478bd9Sstevel@tonic-gate  *  -1	- Error occurred.  The error information is in the error stack.
24167c478bd9Sstevel@tonic-gate  */
24177c478bd9Sstevel@tonic-gate static int
24187c478bd9Sstevel@tonic-gate get_chunk_header(http_conn_t *c_id)
24197c478bd9Sstevel@tonic-gate {
24207c478bd9Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
24217c478bd9Sstevel@tonic-gate 	char	*ptr;
24227c478bd9Sstevel@tonic-gate 	int	value;
24237c478bd9Sstevel@tonic-gate 	int	ok;
24247c478bd9Sstevel@tonic-gate 	int	i;
24257c478bd9Sstevel@tonic-gate 
24267c478bd9Sstevel@tonic-gate 	/*
24277c478bd9Sstevel@tonic-gate 	 * Determine whether an extra crlf pair will precede the
24287c478bd9Sstevel@tonic-gate 	 * chunk header.  For the first one, there is no preceding
24297c478bd9Sstevel@tonic-gate 	 * crlf.  For later chunks, there is one crlf.
24307c478bd9Sstevel@tonic-gate 	 */
24317c478bd9Sstevel@tonic-gate 	if (c_id->is_firstchunk) {
24327c478bd9Sstevel@tonic-gate 		ok = 1;
24337c478bd9Sstevel@tonic-gate 		c_id->is_firstchunk = B_FALSE;
24347c478bd9Sstevel@tonic-gate 	} else {
243523a1cceaSRoger A. Faulkner 		ok = ((i = getaline(c_id, line, sizeof (line), B_FALSE)) == 0);
24367c478bd9Sstevel@tonic-gate 	}
24377c478bd9Sstevel@tonic-gate 
24387c478bd9Sstevel@tonic-gate 	if (ok)
243923a1cceaSRoger A. Faulkner 		i = getaline(c_id, line, sizeof (line), B_FALSE);
24407c478bd9Sstevel@tonic-gate 	if (!ok || i < 0) {
24417c478bd9Sstevel@tonic-gate 		/*
24427c478bd9Sstevel@tonic-gate 		 * If I/O error, the Cause was already put into
24437c478bd9Sstevel@tonic-gate 		 * error stack.  This is an additional error.
24447c478bd9Sstevel@tonic-gate 		 */
24457c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOHEADER);
24467c478bd9Sstevel@tonic-gate 		return (-1);
24477c478bd9Sstevel@tonic-gate 	}
24487c478bd9Sstevel@tonic-gate 
24497c478bd9Sstevel@tonic-gate 	if (verbosemode)
24507c478bd9Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "get_chunk_header: <%s>", line);
24517c478bd9Sstevel@tonic-gate 
24527c478bd9Sstevel@tonic-gate 
24537c478bd9Sstevel@tonic-gate 	/*
24547c478bd9Sstevel@tonic-gate 	 * The first (and probably only) field in the line is the hex
24557c478bd9Sstevel@tonic-gate 	 * length of the chunk.
24567c478bd9Sstevel@tonic-gate 	 */
24577c478bd9Sstevel@tonic-gate 	ptr = line;
24587c478bd9Sstevel@tonic-gate 	value = 0;
24597c478bd9Sstevel@tonic-gate 	while (*ptr != '\0' && (i = hexdigit(*ptr)) >= 0) {
24607c478bd9Sstevel@tonic-gate 		value = (value << 4) + i;
24617c478bd9Sstevel@tonic-gate 		ptr ++;
24627c478bd9Sstevel@tonic-gate 	}
24637c478bd9Sstevel@tonic-gate 
24647c478bd9Sstevel@tonic-gate 	return (value);
24657c478bd9Sstevel@tonic-gate }
24667c478bd9Sstevel@tonic-gate 
24677c478bd9Sstevel@tonic-gate /*
24687c478bd9Sstevel@tonic-gate  * init_bread - Initialize the counters used to read message bodies.
24697c478bd9Sstevel@tonic-gate  *
24707c478bd9Sstevel@tonic-gate  * Arguments:
24717c478bd9Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
24727c478bd9Sstevel@tonic-gate  *
24737c478bd9Sstevel@tonic-gate  * Returns:
24747c478bd9Sstevel@tonic-gate  *   0	- Success
24757c478bd9Sstevel@tonic-gate  *  -1	- Error occurred.  The error information is in the error stack.
24767c478bd9Sstevel@tonic-gate  *
24777c478bd9Sstevel@tonic-gate  *  This routine will determine whether the message body being received is
24787c478bd9Sstevel@tonic-gate  *  chunked or non-chunked. Once determined, the counters used to read
24797c478bd9Sstevel@tonic-gate  *  message bodies will be initialized.
24807c478bd9Sstevel@tonic-gate  */
24817c478bd9Sstevel@tonic-gate static int
24827c478bd9Sstevel@tonic-gate init_bread(http_conn_t *c_id)
24837c478bd9Sstevel@tonic-gate {
24847c478bd9Sstevel@tonic-gate 	char	*hdr;
24857c478bd9Sstevel@tonic-gate 	char	*ptr;
24867c478bd9Sstevel@tonic-gate 	boolean_t sized = B_FALSE;
24877c478bd9Sstevel@tonic-gate 
24887c478bd9Sstevel@tonic-gate 	/*
24897c478bd9Sstevel@tonic-gate 	 * Assume non-chunked reads until proven otherwise.
24907c478bd9Sstevel@tonic-gate 	 */
24917c478bd9Sstevel@tonic-gate 	c_id->is_chunked = B_FALSE;
24927c478bd9Sstevel@tonic-gate 	c_id->is_firstchunk = B_FALSE;
24937c478bd9Sstevel@tonic-gate 	hdr = http_get_header_value(c_id, "Content-Length");
24947c478bd9Sstevel@tonic-gate 	if (hdr != NULL) {
24957c478bd9Sstevel@tonic-gate 		c_id->body_size = strtol(hdr, NULL, 10);
24967c478bd9Sstevel@tonic-gate 		if (c_id->body_size == 0 && errno != 0) {
24977c478bd9Sstevel@tonic-gate 			free(hdr);
24987c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADSIZE);
24997c478bd9Sstevel@tonic-gate 			return (-1);
25007c478bd9Sstevel@tonic-gate 		}
25017c478bd9Sstevel@tonic-gate 		free(hdr);
25027c478bd9Sstevel@tonic-gate 		sized = B_TRUE;
25037c478bd9Sstevel@tonic-gate 	}
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	/*
25067c478bd9Sstevel@tonic-gate 	 * If size was not determined above, then see if this is a
25077c478bd9Sstevel@tonic-gate 	 * chunked message. Keep in mind that the first chunk size is
25087c478bd9Sstevel@tonic-gate 	 * "special".
25097c478bd9Sstevel@tonic-gate 	 */
25107c478bd9Sstevel@tonic-gate 	if (!sized) {
25117c478bd9Sstevel@tonic-gate 		hdr = http_get_header_value(c_id, "Transfer-Encoding");
25127c478bd9Sstevel@tonic-gate 		if (hdr != NULL) {
25137c478bd9Sstevel@tonic-gate 			ptr = eat_ws(hdr);
25147c478bd9Sstevel@tonic-gate 			if (startswith((const char **)&ptr, "chunked;") ||
25157c478bd9Sstevel@tonic-gate 			    strcasecmp(ptr, "chunked") == 0) {
25167c478bd9Sstevel@tonic-gate 				c_id->is_firstchunk = B_TRUE;
25177c478bd9Sstevel@tonic-gate 				c_id->is_chunked = B_TRUE;
25187c478bd9Sstevel@tonic-gate 			}
25197c478bd9Sstevel@tonic-gate 			free(hdr);
25207c478bd9Sstevel@tonic-gate 			if (c_id->is_chunked) {
25217c478bd9Sstevel@tonic-gate 				c_id->body_size = get_chunk_header(c_id);
25227c478bd9Sstevel@tonic-gate 				if (c_id->body_size == -1) {
25237c478bd9Sstevel@tonic-gate 					/*
25247c478bd9Sstevel@tonic-gate 					 * Error stack was already set at a
25257c478bd9Sstevel@tonic-gate 					 * lower level.
25267c478bd9Sstevel@tonic-gate 					 */
25277c478bd9Sstevel@tonic-gate 					return (-1);
25287c478bd9Sstevel@tonic-gate 				}
25297c478bd9Sstevel@tonic-gate 				sized = B_TRUE;
25307c478bd9Sstevel@tonic-gate 			}
25317c478bd9Sstevel@tonic-gate 		}
25327c478bd9Sstevel@tonic-gate 	}
25337c478bd9Sstevel@tonic-gate 
25347c478bd9Sstevel@tonic-gate 	/*
25357c478bd9Sstevel@tonic-gate 	 * Well, isn't this a fine predicament? It wasn't chunked or
25367c478bd9Sstevel@tonic-gate 	 * non-chunked as far as we can tell.
25377c478bd9Sstevel@tonic-gate 	 */
25387c478bd9Sstevel@tonic-gate 	if (!sized) {
25397c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADSIZE);
25407c478bd9Sstevel@tonic-gate 		return (-1);
25417c478bd9Sstevel@tonic-gate 	}
25427c478bd9Sstevel@tonic-gate 
25437c478bd9Sstevel@tonic-gate 	c_id->body_read = 0;
25447c478bd9Sstevel@tonic-gate 	c_id->body_size_tot = c_id->body_size;
25457c478bd9Sstevel@tonic-gate 	c_id->body_read_tot = 0;
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate 	return (0);
25487c478bd9Sstevel@tonic-gate }
25497c478bd9Sstevel@tonic-gate 
25507c478bd9Sstevel@tonic-gate /*
25517c478bd9Sstevel@tonic-gate  * get_msgcnt - Get the number of bytes left in the message body or chunk.
25527c478bd9Sstevel@tonic-gate  *
25537c478bd9Sstevel@tonic-gate  * Arguments:
25547c478bd9Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
25557c478bd9Sstevel@tonic-gate  *   msgcnt - Where to store the message count.
25567c478bd9Sstevel@tonic-gate  *
25577c478bd9Sstevel@tonic-gate  * Returns:
25587c478bd9Sstevel@tonic-gate  *   0	- Success
25597c478bd9Sstevel@tonic-gate  *  -1	- Error occurred.  The error information is in the error stack.
25607c478bd9Sstevel@tonic-gate  *
25617c478bd9Sstevel@tonic-gate  *  Note that if the message being read is not chunked, then the byte count
25627c478bd9Sstevel@tonic-gate  *  is simply the message size minus the bytes read thus far. In the case of
25637c478bd9Sstevel@tonic-gate  *  chunked messages, the byte count returned will be the number of bytes
25647c478bd9Sstevel@tonic-gate  *  left in the chunk. If the current chunk has been exhausted, then this
25657c478bd9Sstevel@tonic-gate  *  routine will determine the size of the next chunk. When the next chunk
25667c478bd9Sstevel@tonic-gate  *  size is zero, the message has been read in its entirety.
25677c478bd9Sstevel@tonic-gate  */
25687c478bd9Sstevel@tonic-gate static int
25697c478bd9Sstevel@tonic-gate get_msgcnt(http_conn_t *c_id, ssize_t *msgcnt)
25707c478bd9Sstevel@tonic-gate {
25717c478bd9Sstevel@tonic-gate 	/*
25727c478bd9Sstevel@tonic-gate 	 * If there are more bytes in the message, then return.
25737c478bd9Sstevel@tonic-gate 	 */
25747c478bd9Sstevel@tonic-gate 	*msgcnt = c_id->body_size - c_id->body_read;
25757c478bd9Sstevel@tonic-gate 	if (*msgcnt != 0) {
25767c478bd9Sstevel@tonic-gate 		return (0);
25777c478bd9Sstevel@tonic-gate 	}
25787c478bd9Sstevel@tonic-gate 	/*
25797c478bd9Sstevel@tonic-gate 	 * If this is not a chunked message and the body has been
25807c478bd9Sstevel@tonic-gate 	 * read, then we're done.
25817c478bd9Sstevel@tonic-gate 	 */
25827c478bd9Sstevel@tonic-gate 	if (!c_id->is_chunked) {
25837c478bd9Sstevel@tonic-gate 		return (0);
25847c478bd9Sstevel@tonic-gate 	}
25857c478bd9Sstevel@tonic-gate 
25867c478bd9Sstevel@tonic-gate 	/*
25877c478bd9Sstevel@tonic-gate 	 * We're looking at a chunked message whose immediate
25887c478bd9Sstevel@tonic-gate 	 * chunk has been totally processed. See if there is
25897c478bd9Sstevel@tonic-gate 	 * another chunk.
25907c478bd9Sstevel@tonic-gate 	 */
25917c478bd9Sstevel@tonic-gate 	c_id->body_size = get_chunk_header(c_id);
25927c478bd9Sstevel@tonic-gate 	if (c_id->body_size == -1) {
25937c478bd9Sstevel@tonic-gate 		/*
25947c478bd9Sstevel@tonic-gate 		 * Error stack was already set at a
25957c478bd9Sstevel@tonic-gate 		 * lower level.
25967c478bd9Sstevel@tonic-gate 		 */
25977c478bd9Sstevel@tonic-gate 		return (-1);
25987c478bd9Sstevel@tonic-gate 	}
25997c478bd9Sstevel@tonic-gate 
26007c478bd9Sstevel@tonic-gate 	/*
26017c478bd9Sstevel@tonic-gate 	 * No bytes of this chunk have been processed yet.
26027c478bd9Sstevel@tonic-gate 	 */
26037c478bd9Sstevel@tonic-gate 	c_id->body_read = 0;
26047c478bd9Sstevel@tonic-gate 
26057c478bd9Sstevel@tonic-gate 	/*
26067c478bd9Sstevel@tonic-gate 	 * A zero length chunk signals the end of the
26077c478bd9Sstevel@tonic-gate 	 * message body and chunking.
26087c478bd9Sstevel@tonic-gate 	 */
26097c478bd9Sstevel@tonic-gate 	if (c_id->body_size == 0) {
26107c478bd9Sstevel@tonic-gate 		c_id->is_chunked = B_FALSE;
26117c478bd9Sstevel@tonic-gate 		return (0);
26127c478bd9Sstevel@tonic-gate 	}
26137c478bd9Sstevel@tonic-gate 
26147c478bd9Sstevel@tonic-gate 	/*
26157c478bd9Sstevel@tonic-gate 	 * There is another chunk.
26167c478bd9Sstevel@tonic-gate 	 */
26177c478bd9Sstevel@tonic-gate 	c_id->body_size_tot += c_id->body_size;
26187c478bd9Sstevel@tonic-gate 	*msgcnt = c_id->body_size - c_id->body_read;
26197c478bd9Sstevel@tonic-gate 
26207c478bd9Sstevel@tonic-gate 	return (0);
26217c478bd9Sstevel@tonic-gate }
26227c478bd9Sstevel@tonic-gate 
26237c478bd9Sstevel@tonic-gate /*
262423a1cceaSRoger A. Faulkner  * getaline - Get lines of data from the HTTP response, up to 'len' bytes.
26257c478bd9Sstevel@tonic-gate  *	  NOTE: the line will not end with a NULL if all 'len' bytes
26267c478bd9Sstevel@tonic-gate  *	  were read.
26277c478bd9Sstevel@tonic-gate  *
26287c478bd9Sstevel@tonic-gate  * Arguments:
26297c478bd9Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
26307c478bd9Sstevel@tonic-gate  *   line   - Where to store the data.
26317c478bd9Sstevel@tonic-gate  *   len    - Maximum number of bytes in the line.
26327c478bd9Sstevel@tonic-gate  *   bread  - TRUE if the lines are part of the message body.
26337c478bd9Sstevel@tonic-gate  *
26347c478bd9Sstevel@tonic-gate  * Returns:
26357c478bd9Sstevel@tonic-gate  *   >=0    - The number of bytes successfully read.
26367c478bd9Sstevel@tonic-gate  *   <0	    - An error occurred.  This is (the number of bytes gotten + 1),
26377c478bd9Sstevel@tonic-gate  *	      negated.  In other words, if 'n' bytes were read and then an
26387c478bd9Sstevel@tonic-gate  *	      error occurred, this will return (-(n+1)).  So zero bytes read
26397c478bd9Sstevel@tonic-gate  *	      and then an error occurs, this will return -1.  If 1 bytes
26407c478bd9Sstevel@tonic-gate  *	      was read, it will return -2, etc.
26417c478bd9Sstevel@tonic-gate  *
26427c478bd9Sstevel@tonic-gate  *	      Specifics of the error can be gotten using http_get_lasterr();
26437c478bd9Sstevel@tonic-gate  *
26447c478bd9Sstevel@tonic-gate  *  Note that I/O errors are put into the error stack by http_srv_recv().1
26457c478bd9Sstevel@tonic-gate  */
26467c478bd9Sstevel@tonic-gate static int
264723a1cceaSRoger A. Faulkner getaline(http_conn_t *c_id, char *line, int len, boolean_t bread)
26487c478bd9Sstevel@tonic-gate {
26497c478bd9Sstevel@tonic-gate 	int	i = 0;
26507c478bd9Sstevel@tonic-gate 	ssize_t	msgcnt = 0;
26517c478bd9Sstevel@tonic-gate 	ssize_t	cnt;
26527c478bd9Sstevel@tonic-gate 
26537c478bd9Sstevel@tonic-gate 	while (i < len) {
26547c478bd9Sstevel@tonic-gate 		/*
26557c478bd9Sstevel@tonic-gate 		 * Special processing required for message body reads.
26567c478bd9Sstevel@tonic-gate 		 */
26577c478bd9Sstevel@tonic-gate 		if (bread) {
26587c478bd9Sstevel@tonic-gate 			/*
26597c478bd9Sstevel@tonic-gate 			 * See if there is another chunk. Obviously, in the
26607c478bd9Sstevel@tonic-gate 			 * case of non-chunked messages, there won't be.
26617c478bd9Sstevel@tonic-gate 			 * But in either case, chunked or not, if msgcnt
26627c478bd9Sstevel@tonic-gate 			 * is still zero after the call to get_msgcnt(),
26637c478bd9Sstevel@tonic-gate 			 * then we're done.
26647c478bd9Sstevel@tonic-gate 			 */
26657c478bd9Sstevel@tonic-gate 			if (msgcnt == 0) {
26667c478bd9Sstevel@tonic-gate 				if (get_msgcnt(c_id, &msgcnt) == -1) {
26677c478bd9Sstevel@tonic-gate 					return (-(i+1));
26687c478bd9Sstevel@tonic-gate 				}
26697c478bd9Sstevel@tonic-gate 				if (msgcnt == 0) {
26707c478bd9Sstevel@tonic-gate 					break;
26717c478bd9Sstevel@tonic-gate 				}
26727c478bd9Sstevel@tonic-gate 			}
26737c478bd9Sstevel@tonic-gate 			cnt = MIN(msgcnt, sizeof (c_id->inbuf.buf));
26747c478bd9Sstevel@tonic-gate 		} else {
26757c478bd9Sstevel@tonic-gate 			cnt = sizeof (c_id->inbuf.buf);
26767c478bd9Sstevel@tonic-gate 		}
26777c478bd9Sstevel@tonic-gate 
26787c478bd9Sstevel@tonic-gate 		/* read more data if buffer empty */
26797c478bd9Sstevel@tonic-gate 		if (c_id->inbuf.i == c_id->inbuf.n) {
26807c478bd9Sstevel@tonic-gate 			c_id->inbuf.i = 0;
26817c478bd9Sstevel@tonic-gate 			c_id->inbuf.n = http_srv_recv(c_id, c_id->inbuf.buf,
26827c478bd9Sstevel@tonic-gate 			    cnt);
26837c478bd9Sstevel@tonic-gate 			if (c_id->inbuf.n == 0) {
26847c478bd9Sstevel@tonic-gate 				return (i);
26857c478bd9Sstevel@tonic-gate 			}
26867c478bd9Sstevel@tonic-gate 			if (c_id->inbuf.n < 0) {
26877c478bd9Sstevel@tonic-gate 				return (-(i+1));
26887c478bd9Sstevel@tonic-gate 			}
26897c478bd9Sstevel@tonic-gate 		}
26907c478bd9Sstevel@tonic-gate 		/* skip CR */
26917c478bd9Sstevel@tonic-gate 		if (c_id->inbuf.buf[c_id->inbuf.i] == '\r') {
26927c478bd9Sstevel@tonic-gate 			INC_BREAD_CNT(bread, msgcnt);
26937c478bd9Sstevel@tonic-gate 			c_id->inbuf.i++;
26947c478bd9Sstevel@tonic-gate 			continue;
26957c478bd9Sstevel@tonic-gate 		}
26967c478bd9Sstevel@tonic-gate 		if (c_id->inbuf.buf[c_id->inbuf.i] == '\n') {
26977c478bd9Sstevel@tonic-gate 			INC_BREAD_CNT(bread, msgcnt);
26987c478bd9Sstevel@tonic-gate 			c_id->inbuf.i++;
26997c478bd9Sstevel@tonic-gate 			line[i] = '\0';
27007c478bd9Sstevel@tonic-gate 			return (i);
27017c478bd9Sstevel@tonic-gate 		}
27027c478bd9Sstevel@tonic-gate 		/* copy buf from internal buffer */
27037c478bd9Sstevel@tonic-gate 		INC_BREAD_CNT(bread, msgcnt);
27047c478bd9Sstevel@tonic-gate 		line[i++] = c_id->inbuf.buf[c_id->inbuf.i++];
27057c478bd9Sstevel@tonic-gate 	}
27067c478bd9Sstevel@tonic-gate 	return (i);
27077c478bd9Sstevel@tonic-gate }
27087c478bd9Sstevel@tonic-gate 
27097c478bd9Sstevel@tonic-gate /*
27107c478bd9Sstevel@tonic-gate  * getbytes - Get a block from the HTTP response. Used for the HTTP body.
27117c478bd9Sstevel@tonic-gate  *
27127c478bd9Sstevel@tonic-gate  * Arguments:
27137c478bd9Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
27147c478bd9Sstevel@tonic-gate  *   line   - Where to store the data.
27157c478bd9Sstevel@tonic-gate  *   len    - Maximum number of bytes in the block.
27167c478bd9Sstevel@tonic-gate  *
27177c478bd9Sstevel@tonic-gate  * Returns:
27187c478bd9Sstevel@tonic-gate  *   >=0    - The number of bytes successfully read.
27197c478bd9Sstevel@tonic-gate  *   <0	    - An error occurred.  This is (the number of bytes gotten + 1),
27207c478bd9Sstevel@tonic-gate  *	      negated.  In other words, if 'n' bytes were read and then an
27217c478bd9Sstevel@tonic-gate  *	      error occurred, this will return (-(n+1)).  So zero bytes read
27227c478bd9Sstevel@tonic-gate  *	      and then an error occurs, this will return -1.  If 1 bytes
27237c478bd9Sstevel@tonic-gate  *	      was read, it will return -2, etc.
27247c478bd9Sstevel@tonic-gate  *
27257c478bd9Sstevel@tonic-gate  *	      Specifics of the error can be gotten using http_get_lasterr();
27267c478bd9Sstevel@tonic-gate  *
27277c478bd9Sstevel@tonic-gate  *  Note that all reads performed here assume that a message body is being
27287c478bd9Sstevel@tonic-gate  *  read. If this changes in the future, then the logic should more closely
272923a1cceaSRoger A. Faulkner  *  resemble getaline().
27307c478bd9Sstevel@tonic-gate  *
27317c478bd9Sstevel@tonic-gate  *  Note that I/O errors are put into the error stack by http_srv_recv().
27327c478bd9Sstevel@tonic-gate  */
27337c478bd9Sstevel@tonic-gate static int
27347c478bd9Sstevel@tonic-gate getbytes(http_conn_t *c_id, char *line, int len)
27357c478bd9Sstevel@tonic-gate {
27367c478bd9Sstevel@tonic-gate 	int	i = 0;
27377c478bd9Sstevel@tonic-gate 	ssize_t	msgcnt = 0;
27387c478bd9Sstevel@tonic-gate 	ssize_t	cnt;
27397c478bd9Sstevel@tonic-gate 	int	nbytes;
27407c478bd9Sstevel@tonic-gate 
27417c478bd9Sstevel@tonic-gate 	while (i < len) {
27427c478bd9Sstevel@tonic-gate 		/*
27437c478bd9Sstevel@tonic-gate 		 * See if there is another chunk. Obviously, in the
27447c478bd9Sstevel@tonic-gate 		 * case of non-chunked messages, there won't be.
27457c478bd9Sstevel@tonic-gate 		 * But in either case, chunked or not, if msgcnt
27467c478bd9Sstevel@tonic-gate 		 * is still zero after the call to get_msgcnt(), then
27477c478bd9Sstevel@tonic-gate 		 * we're done.
27487c478bd9Sstevel@tonic-gate 		 */
27497c478bd9Sstevel@tonic-gate 		if (msgcnt == 0) {
27507c478bd9Sstevel@tonic-gate 			if (get_msgcnt(c_id, &msgcnt) == -1) {
27517c478bd9Sstevel@tonic-gate 				return (-(i+1));
27527c478bd9Sstevel@tonic-gate 			}
27537c478bd9Sstevel@tonic-gate 			if (msgcnt == 0) {
27547c478bd9Sstevel@tonic-gate 				break;
27557c478bd9Sstevel@tonic-gate 			}
27567c478bd9Sstevel@tonic-gate 		}
27577c478bd9Sstevel@tonic-gate 
27587c478bd9Sstevel@tonic-gate 		cnt = MIN(msgcnt, len - i);
27597c478bd9Sstevel@tonic-gate 
27607c478bd9Sstevel@tonic-gate 		if (c_id->inbuf.n != c_id->inbuf.i) {
27617c478bd9Sstevel@tonic-gate 			nbytes = (int)MIN(cnt, c_id->inbuf.n - c_id->inbuf.i);
27627c478bd9Sstevel@tonic-gate 			(void) memcpy(line, &c_id->inbuf.buf[c_id->inbuf.i],
27637c478bd9Sstevel@tonic-gate 			    nbytes);
27647c478bd9Sstevel@tonic-gate 			c_id->inbuf.i += nbytes;
27657c478bd9Sstevel@tonic-gate 		} else {
27667c478bd9Sstevel@tonic-gate 			nbytes = http_srv_recv(c_id, line, cnt);
27677c478bd9Sstevel@tonic-gate 			if (nbytes == 0) {
27687c478bd9Sstevel@tonic-gate 				return (i);
27697c478bd9Sstevel@tonic-gate 			}
27707c478bd9Sstevel@tonic-gate 			if (nbytes < 0) {
27717c478bd9Sstevel@tonic-gate 				return (-(i+1));
27727c478bd9Sstevel@tonic-gate 			}
27737c478bd9Sstevel@tonic-gate 		}
27747c478bd9Sstevel@tonic-gate 
27757c478bd9Sstevel@tonic-gate 		i += nbytes;
27767c478bd9Sstevel@tonic-gate 		line += nbytes;
27777c478bd9Sstevel@tonic-gate 		msgcnt -= nbytes;
27787c478bd9Sstevel@tonic-gate 		c_id->body_read += nbytes;
27797c478bd9Sstevel@tonic-gate 		c_id->body_read_tot += nbytes;
27807c478bd9Sstevel@tonic-gate 	}
27817c478bd9Sstevel@tonic-gate 
27827c478bd9Sstevel@tonic-gate 	return (i);
27837c478bd9Sstevel@tonic-gate }
27847c478bd9Sstevel@tonic-gate 
27857c478bd9Sstevel@tonic-gate static int
27867c478bd9Sstevel@tonic-gate http_srv_send(http_conn_t *c_id, const void *buf, size_t nbyte)
27877c478bd9Sstevel@tonic-gate {
27887c478bd9Sstevel@tonic-gate 	int	retval;
27897c478bd9Sstevel@tonic-gate 
27907c478bd9Sstevel@tonic-gate 	if (c_id->ssl != NULL) {
27917c478bd9Sstevel@tonic-gate 		if ((retval = SSL_write(c_id->ssl, buf, nbyte)) <= 0) {
27927c478bd9Sstevel@tonic-gate 			handle_ssl_error(c_id, retval);
27937c478bd9Sstevel@tonic-gate 		}
27947c478bd9Sstevel@tonic-gate 		return (retval);
27957c478bd9Sstevel@tonic-gate 	} else {
27967c478bd9Sstevel@tonic-gate 		retval = socket_write(c_id->fd, buf, nbyte, &c_id->host_addr);
27977c478bd9Sstevel@tonic-gate 		if (retval < 0) {
27987c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
27997c478bd9Sstevel@tonic-gate 			return (-1);
28007c478bd9Sstevel@tonic-gate 		}
28017c478bd9Sstevel@tonic-gate 		return (retval);
28027c478bd9Sstevel@tonic-gate 	}
28037c478bd9Sstevel@tonic-gate }
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate static int
28067c478bd9Sstevel@tonic-gate http_srv_recv(http_conn_t *c_id, void *buf, size_t nbyte)
28077c478bd9Sstevel@tonic-gate {
28087c478bd9Sstevel@tonic-gate 	int	retval;
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate 	if (c_id->ssl != NULL) {
28117c478bd9Sstevel@tonic-gate 		if ((retval = SSL_read(c_id->ssl, buf, nbyte)) <= 0) {
28127c478bd9Sstevel@tonic-gate 			handle_ssl_error(c_id, retval);
28137c478bd9Sstevel@tonic-gate 		}
28147c478bd9Sstevel@tonic-gate 		return (retval);
28157c478bd9Sstevel@tonic-gate 	} else {
28167c478bd9Sstevel@tonic-gate 		retval = socket_read(c_id->fd, buf, nbyte, c_id->read_timeout);
28177c478bd9Sstevel@tonic-gate 		if (retval < 0) {
28187c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
28197c478bd9Sstevel@tonic-gate 			return (-1);
28207c478bd9Sstevel@tonic-gate 		}
28217c478bd9Sstevel@tonic-gate 		return (retval);
28227c478bd9Sstevel@tonic-gate 	}
28237c478bd9Sstevel@tonic-gate }
28247c478bd9Sstevel@tonic-gate 
28257c478bd9Sstevel@tonic-gate static boolean_t
28267c478bd9Sstevel@tonic-gate http_check_conn(http_conn_t *c_id)
28277c478bd9Sstevel@tonic-gate {
28287c478bd9Sstevel@tonic-gate 	early_err = 0;
28297c478bd9Sstevel@tonic-gate 	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO) {
28307c478bd9Sstevel@tonic-gate 		early_err = EHTTP_BADARG;
28317c478bd9Sstevel@tonic-gate 		return (B_FALSE);
28327c478bd9Sstevel@tonic-gate 	}
28337c478bd9Sstevel@tonic-gate 	RESET_ERR(c_id);
28347c478bd9Sstevel@tonic-gate 	return (B_TRUE);
28357c478bd9Sstevel@tonic-gate }
28367c478bd9Sstevel@tonic-gate 
28377c478bd9Sstevel@tonic-gate static void
28387c478bd9Sstevel@tonic-gate handle_ssl_error(http_conn_t *c_id, int retval)
28397c478bd9Sstevel@tonic-gate {
28407c478bd9Sstevel@tonic-gate 	ulong_t err;
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 	err = SSL_get_error(c_id->ssl, retval);
28437c478bd9Sstevel@tonic-gate 
28447c478bd9Sstevel@tonic-gate 	switch (err) {
28457c478bd9Sstevel@tonic-gate 	case SSL_ERROR_NONE:
28467c478bd9Sstevel@tonic-gate 		return;
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 	case SSL_ERROR_ZERO_RETURN:
28497c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_CONCLOSED);
28507c478bd9Sstevel@tonic-gate 		return;
28517c478bd9Sstevel@tonic-gate 
28527c478bd9Sstevel@tonic-gate 	case SSL_ERROR_WANT_READ:
28537c478bd9Sstevel@tonic-gate 	case SSL_ERROR_WANT_WRITE:
28547c478bd9Sstevel@tonic-gate 	case SSL_ERROR_WANT_CONNECT:
28557c478bd9Sstevel@tonic-gate 	case SSL_ERROR_WANT_X509_LOOKUP:
28567c478bd9Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_UNEXPECTED);
28577c478bd9Sstevel@tonic-gate 		return;
28587c478bd9Sstevel@tonic-gate 
28597c478bd9Sstevel@tonic-gate 	case SSL_ERROR_SYSCALL:
28607c478bd9Sstevel@tonic-gate 		err = ERR_get_error();
28617c478bd9Sstevel@tonic-gate 		if (err == 0)
28627c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_EOFERR);
28637c478bd9Sstevel@tonic-gate 		else if (err == (ulong_t)-1)
28647c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
28657c478bd9Sstevel@tonic-gate 		else {
28667c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
28677c478bd9Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
28687c478bd9Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
28697c478bd9Sstevel@tonic-gate 		}
28707c478bd9Sstevel@tonic-gate 		return;
28717c478bd9Sstevel@tonic-gate 
28727c478bd9Sstevel@tonic-gate 	case SSL_ERROR_SSL:
28737c478bd9Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0) {
28747c478bd9Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
28757c478bd9Sstevel@tonic-gate 		}
28767c478bd9Sstevel@tonic-gate 		return;
28777c478bd9Sstevel@tonic-gate 	}
28787c478bd9Sstevel@tonic-gate }
28797c478bd9Sstevel@tonic-gate 
28807c478bd9Sstevel@tonic-gate static int
28817c478bd9Sstevel@tonic-gate count_digits(int value)
28827c478bd9Sstevel@tonic-gate {
28837c478bd9Sstevel@tonic-gate 	int	count = 1;
28847c478bd9Sstevel@tonic-gate 
28857c478bd9Sstevel@tonic-gate 	if (value < 0) {
28867c478bd9Sstevel@tonic-gate 		count++;
28877c478bd9Sstevel@tonic-gate 		value = -value;
28887c478bd9Sstevel@tonic-gate 	}
28897c478bd9Sstevel@tonic-gate 
28907c478bd9Sstevel@tonic-gate 	while (value > 9) {
28917c478bd9Sstevel@tonic-gate 		value /= 10;
28927c478bd9Sstevel@tonic-gate 		count++;
28937c478bd9Sstevel@tonic-gate 	}
28947c478bd9Sstevel@tonic-gate 	return (count);
28957c478bd9Sstevel@tonic-gate }
28967c478bd9Sstevel@tonic-gate 
28977c478bd9Sstevel@tonic-gate static int
28987c478bd9Sstevel@tonic-gate hexdigit(char ch)
28997c478bd9Sstevel@tonic-gate {
29007c478bd9Sstevel@tonic-gate 	if (ch >= '0' && ch <= '9')
29017c478bd9Sstevel@tonic-gate 		return (ch - '0');
29027c478bd9Sstevel@tonic-gate 	if (ch >= 'A' && ch <= 'F')
29037c478bd9Sstevel@tonic-gate 		return (ch - 'A' + 10);
29047c478bd9Sstevel@tonic-gate 	if (ch >= 'a' && ch <= 'f')
29057c478bd9Sstevel@tonic-gate 		return (ch - 'a' + 10);
29067c478bd9Sstevel@tonic-gate 	return (-1);
29077c478bd9Sstevel@tonic-gate }
29087c478bd9Sstevel@tonic-gate 
29097c478bd9Sstevel@tonic-gate static char *
29107c478bd9Sstevel@tonic-gate eat_ws(const char *buf)
29117c478bd9Sstevel@tonic-gate {
29127c478bd9Sstevel@tonic-gate 	char *ptr = (char *)buf;
29137c478bd9Sstevel@tonic-gate 
29147c478bd9Sstevel@tonic-gate 	while (isspace(*ptr))
29157c478bd9Sstevel@tonic-gate 		ptr++;
29167c478bd9Sstevel@tonic-gate 
29177c478bd9Sstevel@tonic-gate 	return (ptr);
29187c478bd9Sstevel@tonic-gate }
29197c478bd9Sstevel@tonic-gate 
29207c478bd9Sstevel@tonic-gate static boolean_t
29217c478bd9Sstevel@tonic-gate startswith(const char **strp, const char *starts)
29227c478bd9Sstevel@tonic-gate {
29237c478bd9Sstevel@tonic-gate 	int len = strlen(starts);
29247c478bd9Sstevel@tonic-gate 
29257c478bd9Sstevel@tonic-gate 	if (strncasecmp(*strp, starts, len) == 0) {
29267c478bd9Sstevel@tonic-gate 		*strp += len;
29277c478bd9Sstevel@tonic-gate 		return (B_TRUE);
29287c478bd9Sstevel@tonic-gate 	}
29297c478bd9Sstevel@tonic-gate 	return (B_FALSE);
29307c478bd9Sstevel@tonic-gate }
2931