mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +00:00 
			
		
		
		
	Fix the following build failure with libressl by using SSL_is_server which is available since version 2.7.0 andd7ec516916: iostream.c: In function 'ast_iostream_close': iostream.c:559:41: error: invalid use of incomplete typedef 'SSL' {aka 'struct ssl_st'} 559 | if (!stream->ssl->server) { | ^~ ASTERISK-30107 #close Fixes: - http://autobuild.buildroot.org/results/ce4d62d00bb77ba5b303cacf6be7e350581a62f9 Change-Id: Iea7f34970297f2fb50285d73462d0174ba7e9587 (cherry picked from commitf34d9d5618)
		
			
				
	
	
		
			660 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			660 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Asterisk -- An open source telephony toolkit.
 | |
|  *
 | |
|  * Copyright (C) 1999 - 2015, Digium, Inc.
 | |
|  *
 | |
|  * Timo Teräs <timo.teras@iki.fi>
 | |
|  *
 | |
|  * See http://www.asterisk.org for more information about
 | |
|  * the Asterisk project. Please do not directly contact
 | |
|  * any of the maintainers of this project for assistance;
 | |
|  * the project provides a web site, mailing lists and IRC
 | |
|  * channels for your use.
 | |
|  *
 | |
|  * This program is free software, distributed under the terms of
 | |
|  * the GNU General Public License Version 2. See the LICENSE file
 | |
|  * at the top of the source tree.
 | |
|  */
 | |
| 
 | |
| #include "asterisk.h"
 | |
| 
 | |
| #include "asterisk/iostream.h"          /* for DO_SSL */
 | |
| 
 | |
| #include <fcntl.h>                      /* for O_NONBLOCK */
 | |
| #ifdef DO_SSL
 | |
| #include <openssl/err.h>                /* for ERR_error_string */
 | |
| #include <openssl/opensslv.h>           /* for OPENSSL_VERSION_NUMBER */
 | |
| #include <openssl/ssl.h>                /* for SSL_get_error, SSL_free, SSL_... */
 | |
| #endif
 | |
| #include <sys/socket.h>                 /* for shutdown, SHUT_RDWR */
 | |
| #include <sys/time.h>                   /* for timeval */
 | |
| 
 | |
| #include "asterisk/astobj2.h"           /* for ao2_alloc_options, ao2_alloc_... */
 | |
| #include "asterisk/logger.h"            /* for ast_debug, ast_log, LOG_ERROR */
 | |
| #include "asterisk/strings.h"           /* for asterisk/threadstorage.h */
 | |
| #include "asterisk/threadstorage.h"     /* for ast_threadstorage_get, AST_TH... */
 | |
| #include "asterisk/time.h"              /* for ast_remaining_ms, ast_tvnow */
 | |
| #include "asterisk/utils.h"             /* for ast_wait_for_input, ast_wait_... */
 | |
| 
 | |
| struct ast_iostream {
 | |
| 	SSL *ssl;
 | |
| 	struct timeval start;
 | |
| 	int fd;
 | |
| 	int timeout;
 | |
| 	int timeout_reset;
 | |
| 	int exclusive_input;
 | |
| 	int rbuflen;
 | |
| 	char *rbufhead;
 | |
| 	char rbuf[2048];
 | |
| };
 | |
| 
 | |
| #if defined(DO_SSL)
 | |
| AST_THREADSTORAGE(err2str_threadbuf);
 | |
| #define ERR2STR_BUFSIZE   128
 | |
| 
 | |
| static const char *ssl_error_to_string(int sslerr, int ret)
 | |
| {
 | |
| 	switch (sslerr) {
 | |
| 	case SSL_ERROR_SSL:
 | |
| 		return "Internal SSL error";
 | |
| 	case SSL_ERROR_SYSCALL:
 | |
| 		if (!ret) {
 | |
| 			return "System call EOF";
 | |
| 		} else if (ret == -1) {
 | |
| 			char *buf;
 | |
| 
 | |
| 			buf = ast_threadstorage_get(&err2str_threadbuf, ERR2STR_BUFSIZE);
 | |
| 			if (!buf) {
 | |
| 				return "Unknown";
 | |
| 			}
 | |
| 
 | |
| 			snprintf(buf, ERR2STR_BUFSIZE, "Underlying BIO error: %s", strerror(errno));
 | |
| 			return buf;
 | |
| 		} else {
 | |
| 			return "System call other";
 | |
| 		}
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return "Unknown";
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int ast_iostream_get_fd(struct ast_iostream *stream)
 | |
| {
 | |
| 	return stream->fd;
 | |
| }
 | |
| 
 | |
| int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout)
 | |
| {
 | |
| #if defined(DO_SSL)
 | |
| 	/* Because SSL is read in blocks, it's possible that the last time we read we
 | |
| 	   got more than we asked for and it is now buffered inside OpenSSL. If that
 | |
| 	   is the case, calling ast_wait_for_input() will block until the fd is ready
 | |
| 	   for reading again, which might never happen. */
 | |
| 	if (stream->ssl && SSL_pending(stream->ssl)) {
 | |
| 		return 1;
 | |
| 	}
 | |
| #endif
 | |
| 	return ast_wait_for_input(stream->fd, timeout);
 | |
| }
 | |
| 
 | |
| void ast_iostream_nonblock(struct ast_iostream *stream)
 | |
| {
 | |
| 	ast_fd_set_flags(stream->fd, O_NONBLOCK);
 | |
| }
 | |
| 
 | |
| SSL *ast_iostream_get_ssl(struct ast_iostream *stream)
 | |
| {
 | |
| 	return stream->ssl;
 | |
| }
 | |
| 
 | |
| void ast_iostream_set_timeout_disable(struct ast_iostream *stream)
 | |
| {
 | |
| 	ast_assert(stream != NULL);
 | |
| 
 | |
| 	stream->timeout = -1;
 | |
| 	stream->timeout_reset = -1;
 | |
| }
 | |
| 
 | |
| void ast_iostream_set_timeout_inactivity(struct ast_iostream *stream, int timeout)
 | |
| {
 | |
| 	ast_assert(stream != NULL);
 | |
| 
 | |
| 	stream->start.tv_sec = 0;
 | |
| 	stream->timeout = timeout;
 | |
| 	stream->timeout_reset = timeout;
 | |
| }
 | |
| 
 | |
| void ast_iostream_set_timeout_idle_inactivity(struct ast_iostream *stream, int timeout, int timeout_reset)
 | |
| {
 | |
| 	ast_assert(stream != NULL);
 | |
| 
 | |
| 	stream->start.tv_sec = 0;
 | |
| 	stream->timeout = timeout;
 | |
| 	stream->timeout_reset = timeout_reset;
 | |
| }
 | |
| 
 | |
| void ast_iostream_set_timeout_sequence(struct ast_iostream *stream, struct timeval start, int timeout)
 | |
| {
 | |
| 	ast_assert(stream != NULL);
 | |
| 
 | |
| 	stream->start = start;
 | |
| 	stream->timeout = timeout;
 | |
| 	stream->timeout_reset = timeout;
 | |
| }
 | |
| 
 | |
| void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive_input)
 | |
| {
 | |
| 	ast_assert(stream != NULL);
 | |
| 
 | |
| 	stream->exclusive_input = exclusive_input;
 | |
| }
 | |
| 
 | |
| static ssize_t iostream_read(struct ast_iostream *stream, void *buf, size_t size)
 | |
| {
 | |
| 	struct timeval start;
 | |
| 	int ms;
 | |
| 	int res;
 | |
| 
 | |
| 	if (stream->start.tv_sec) {
 | |
| 		start = stream->start;
 | |
| 	} else {
 | |
| 		start = ast_tvnow();
 | |
| 	}
 | |
| 
 | |
| #if defined(DO_SSL)
 | |
| 	if (stream->ssl) {
 | |
| 		for (;;) {
 | |
| 			int sslerr;
 | |
| 			char err[256];
 | |
| 			res = SSL_read(stream->ssl, buf, size);
 | |
| 			if (0 < res) {
 | |
| 				/* We read some payload data. */
 | |
| 				stream->timeout = stream->timeout_reset;
 | |
| 				return res;
 | |
| 			}
 | |
| 			sslerr = SSL_get_error(stream->ssl, res);
 | |
| 			switch (sslerr) {
 | |
| 			case SSL_ERROR_ZERO_RETURN:
 | |
| 				/* Report EOF for a shutdown */
 | |
| 				ast_debug(1, "TLS clean shutdown alert reading data\n");
 | |
| 				return 0;
 | |
| 			case SSL_ERROR_WANT_READ:
 | |
| 				if (!stream->exclusive_input) {
 | |
| 					/* We cannot wait for data now. */
 | |
| 					errno = EAGAIN;
 | |
| 					return -1;
 | |
| 				}
 | |
| 				while ((ms = ast_remaining_ms(start, stream->timeout))) {
 | |
| 					res = ast_wait_for_input(stream->fd, ms);
 | |
| 					if (0 < res) {
 | |
| 						/* Socket is ready to be read. */
 | |
| 						break;
 | |
| 					}
 | |
| 					if (res < 0) {
 | |
| 						if (errno == EINTR || errno == EAGAIN) {
 | |
| 							/* Try again. */
 | |
| 							continue;
 | |
| 						}
 | |
| 						ast_debug(1, "TLS socket error waiting for read data: %s\n",
 | |
| 							strerror(errno));
 | |
| 						return -1;
 | |
| 					}
 | |
| 				}
 | |
| 				break;
 | |
| 			case SSL_ERROR_WANT_WRITE:
 | |
| 				while ((ms = ast_remaining_ms(start, stream->timeout))) {
 | |
| 					res = ast_wait_for_output(stream->fd, ms);
 | |
| 					if (0 < res) {
 | |
| 						/* Socket is ready to be written. */
 | |
| 						break;
 | |
| 					}
 | |
| 					if (res < 0) {
 | |
| 						if (errno == EINTR || errno == EAGAIN) {
 | |
| 							/* Try again. */
 | |
| 							continue;
 | |
| 						}
 | |
| 						ast_debug(1, "TLS socket error waiting for write space: %s\n",
 | |
| 							strerror(errno));
 | |
| 						return -1;
 | |
| 					}
 | |
| 				}
 | |
| 				break;
 | |
| 			case SSL_ERROR_SYSCALL:
 | |
| 				/* Some non-recoverable I/O error occurred. The OpenSSL error queue may
 | |
| 				 * contain more information on the error. For socket I/O on Unix systems,
 | |
| 				 * consult errno for details. */
 | |
| 				ast_debug(1, "TLS non-recoverable I/O error occurred: %s, %s\n", ERR_error_string(sslerr, err),
 | |
| 					ssl_error_to_string(sslerr, res));
 | |
| 				return -1;
 | |
| 			default:
 | |
| 				/* Report EOF for an undecoded SSL or transport error. */
 | |
| 				ast_debug(1, "TLS transport or SSL error reading data:  %s, %s\n", ERR_error_string(sslerr, err),
 | |
| 					ssl_error_to_string(sslerr, res));
 | |
| 				return -1;
 | |
| 			}
 | |
| 			if (!ms) {
 | |
| 				/* Report EOF for a timeout */
 | |
| 				ast_debug(1, "TLS timeout reading data\n");
 | |
| 				return 0;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| #endif	/* defined(DO_SSL) */
 | |
| 
 | |
| 	for (;;) {
 | |
| 		res = read(stream->fd, buf, size);
 | |
| 		if (0 <= res) {
 | |
| 			/* Got data or we cannot wait for it. */
 | |
| 			stream->timeout = stream->timeout_reset;
 | |
| 			return res;
 | |
| 		}
 | |
| 		if (!stream->exclusive_input) {
 | |
| 			return res;
 | |
| 		}
 | |
| 		if (errno != EINTR && errno != EAGAIN) {
 | |
| 			/* Not a retryable error. */
 | |
| 			ast_debug(1, "TCP socket error reading data: %s\n",
 | |
| 				strerror(errno));
 | |
| 			return -1;
 | |
| 		}
 | |
| 		ms = ast_remaining_ms(start, stream->timeout);
 | |
| 		if (!ms) {
 | |
| 			/* Report EOF for a timeout */
 | |
| 			ast_debug(1, "TCP timeout reading data\n");
 | |
| 			return 0;
 | |
| 		}
 | |
| 		ast_wait_for_input(stream->fd, ms);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
 | |
| {
 | |
| 	if (!count) {
 | |
| 		/* You asked for no data you got no data. */
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (!stream || stream->fd == -1) {
 | |
| 		errno = EBADF;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Get any remains from the read buffer */
 | |
| 	if (stream->rbuflen) {
 | |
| 		size_t r = count;
 | |
| 		if (r > stream->rbuflen) {
 | |
| 			r = stream->rbuflen;
 | |
| 		}
 | |
| 		memcpy(buffer, stream->rbufhead, r);
 | |
| 		stream->rbuflen -= r;
 | |
| 		stream->rbufhead += r;
 | |
| 		return r;
 | |
| 	}
 | |
| 
 | |
| 	return iostream_read(stream, buffer, count);
 | |
| }
 | |
| 
 | |
| ssize_t ast_iostream_gets(struct ast_iostream *stream, char *buffer, size_t size)
 | |
| {
 | |
| 	size_t remaining = size;
 | |
| 	ssize_t accum_size = 0;
 | |
| 	ssize_t len;
 | |
| 	char *newline;
 | |
| 
 | |
| 	for (;;) {
 | |
| 		/* Search for newline */
 | |
| 		newline = memchr(stream->rbufhead, '\n', stream->rbuflen);
 | |
| 		if (newline) {
 | |
| 			len = newline - stream->rbufhead + 1;
 | |
| 			if (len > remaining - 1) {
 | |
| 				len = remaining - 1;
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		/* Enough buffered line data to fill request buffer? */
 | |
| 		if (stream->rbuflen >= remaining - 1) {
 | |
| 			len = remaining - 1;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (stream->rbuflen) {
 | |
| 			/* Put leftover buffered line data into request buffer */
 | |
| 			memcpy(buffer + accum_size, stream->rbufhead, stream->rbuflen);
 | |
| 			remaining -= stream->rbuflen;
 | |
| 			accum_size += stream->rbuflen;
 | |
| 			stream->rbuflen = 0;
 | |
| 		}
 | |
| 		stream->rbufhead = stream->rbuf;
 | |
| 
 | |
| 		len = iostream_read(stream, stream->rbuf, sizeof(stream->rbuf));
 | |
| 		if (len == 0) {
 | |
| 			/* Nothing new was read.  Return whatever we have accumulated. */
 | |
| 			break;
 | |
| 		}
 | |
| 		if (len < 0) {
 | |
| 			if (accum_size) {
 | |
| 				/* We have an accumulated buffer so return that instead. */
 | |
| 				len = 0;
 | |
| 				break;
 | |
| 			}
 | |
| 			return len;
 | |
| 		}
 | |
| 		stream->rbuflen += len;
 | |
| 	}
 | |
| 
 | |
| 	/* Return read buffer string length */
 | |
| 	memcpy(buffer + accum_size, stream->rbufhead, len);
 | |
| 	buffer[accum_size + len] = 0;
 | |
| 	stream->rbuflen -= len;
 | |
| 	stream->rbufhead += len;
 | |
| 
 | |
| 	return accum_size + len;
 | |
| }
 | |
| 
 | |
| ssize_t ast_iostream_discard(struct ast_iostream *stream, size_t size)
 | |
| {
 | |
| 	char buf[1024];
 | |
| 	size_t remaining = size;
 | |
| 	ssize_t ret;
 | |
| 
 | |
| 	while (remaining) {
 | |
| 		ret = ast_iostream_read(stream, buf, remaining > sizeof(buf) ? sizeof(buf) : remaining);
 | |
| 		if (ret <= 0) {
 | |
| 			return ret;
 | |
| 		}
 | |
| 		remaining -= ret;
 | |
| 	}
 | |
| 
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t size)
 | |
| {
 | |
| 	struct timeval start;
 | |
| 	int ms;
 | |
| 	int res;
 | |
| 	int written;
 | |
| 	int remaining;
 | |
| 
 | |
| 	if (!size) {
 | |
| 		/* You asked to write no data you wrote no data. */
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (!stream || stream->fd == -1) {
 | |
| 		errno = EBADF;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (stream->start.tv_sec) {
 | |
| 		start = stream->start;
 | |
| 	} else {
 | |
| 		start = ast_tvnow();
 | |
| 	}
 | |
| 
 | |
| #if defined(DO_SSL)
 | |
| 	if (stream->ssl) {
 | |
| 		written = 0;
 | |
| 		remaining = size;
 | |
| 		for (;;) {
 | |
| 			int sslerr;
 | |
| 			char err[256];
 | |
| 			res = SSL_write(stream->ssl, buffer + written, remaining);
 | |
| 			if (res == remaining) {
 | |
| 				/* Everything was written. */
 | |
| 				return size;
 | |
| 			}
 | |
| 			if (0 < res) {
 | |
| 				/* Successfully wrote part of the buffer.  Try to write the rest. */
 | |
| 				written += res;
 | |
| 				remaining -= res;
 | |
| 				continue;
 | |
| 			}
 | |
| 			sslerr = SSL_get_error(stream->ssl, res);
 | |
| 			switch (sslerr) {
 | |
| 			case SSL_ERROR_ZERO_RETURN:
 | |
| 				ast_debug(1, "TLS clean shutdown alert writing data\n");
 | |
| 				if (written) {
 | |
| 					/* Report partial write. */
 | |
| 					return written;
 | |
| 				}
 | |
| 				errno = EBADF;
 | |
| 				return -1;
 | |
| 			case SSL_ERROR_WANT_READ:
 | |
| 				ms = ast_remaining_ms(start, stream->timeout);
 | |
| 				if (!ms) {
 | |
| 					/* Report partial write. */
 | |
| 					ast_debug(1, "TLS timeout writing data (want read)\n");
 | |
| 					return written;
 | |
| 				}
 | |
| 				ast_wait_for_input(stream->fd, ms);
 | |
| 				break;
 | |
| 			case SSL_ERROR_WANT_WRITE:
 | |
| 				ms = ast_remaining_ms(start, stream->timeout);
 | |
| 				if (!ms) {
 | |
| 					/* Report partial write. */
 | |
| 					ast_debug(1, "TLS timeout writing data (want write)\n");
 | |
| 					return written;
 | |
| 				}
 | |
| 				ast_wait_for_output(stream->fd, ms);
 | |
| 				break;
 | |
| 			default:
 | |
| 				/* Undecoded SSL or transport error. */
 | |
| 				ast_debug(1, "TLS transport or SSL error writing data: %s, %s\n", ERR_error_string(sslerr, err),
 | |
| 					ssl_error_to_string(sslerr, res));
 | |
| 				if (written) {
 | |
| 					/* Report partial write. */
 | |
| 					return written;
 | |
| 				}
 | |
| 				errno = EBADF;
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| #endif	/* defined(DO_SSL) */
 | |
| 
 | |
| 	written = 0;
 | |
| 	remaining = size;
 | |
| 	for (;;) {
 | |
| 		res = write(stream->fd, buffer + written, remaining);
 | |
| 		if (res == remaining) {
 | |
| 			/* Yay everything was written. */
 | |
| 			return size;
 | |
| 		}
 | |
| 		if (0 < res) {
 | |
| 			/* Successfully wrote part of the buffer.  Try to write the rest. */
 | |
| 			written += res;
 | |
| 			remaining -= res;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (errno != EINTR && errno != EAGAIN) {
 | |
| 			/* Not a retryable error. */
 | |
| 			ast_debug(1, "TCP socket error writing: %s\n", strerror(errno));
 | |
| 			if (written) {
 | |
| 				return written;
 | |
| 			}
 | |
| 			return -1;
 | |
| 		}
 | |
| 		ms = ast_remaining_ms(start, stream->timeout);
 | |
| 		if (!ms) {
 | |
| 			/* Report partial write. */
 | |
| 			ast_debug(1, "TCP timeout writing data\n");
 | |
| 			return written;
 | |
| 		}
 | |
| 		ast_wait_for_output(stream->fd, ms);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| ssize_t ast_iostream_printf(struct ast_iostream *stream, const char *format, ...)
 | |
| {
 | |
| 	char sbuf[512], *buf = sbuf;
 | |
| 	int len, len2, ret = -1;
 | |
| 	va_list va;
 | |
| 
 | |
| 	va_start(va, format);
 | |
| 	len = vsnprintf(buf, sizeof(sbuf), format, va);
 | |
| 	va_end(va);
 | |
| 
 | |
| 	if (len > sizeof(sbuf) - 1) {
 | |
| 		/* Add one to the string length to accommodate the NULL byte */
 | |
| 		size_t buf_len = len + 1;
 | |
| 
 | |
| 		buf = ast_malloc(buf_len);
 | |
| 		if (!buf) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 		va_start(va, format);
 | |
| 		len2 = vsnprintf(buf, buf_len, format, va);
 | |
| 		va_end(va);
 | |
| 		if (len2 != len) {
 | |
| 			goto error;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (ast_iostream_write(stream, buf, len) == len)
 | |
| 		ret = len;
 | |
| 
 | |
| error:
 | |
| 	if (buf != sbuf) {
 | |
| 		ast_free(buf);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int ast_iostream_close(struct ast_iostream *stream)
 | |
| {
 | |
| 	if (!stream) {
 | |
| 		errno = EBADF;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (stream->fd != -1) {
 | |
| #if defined(DO_SSL)
 | |
| 		if (stream->ssl) {
 | |
| 			int res;
 | |
| 
 | |
| 			/*
 | |
| 			 * According to the TLS standard, it is acceptable for an
 | |
| 			 * application to only send its shutdown alert and then
 | |
| 			 * close the underlying connection without waiting for
 | |
| 			 * the peer's response (this way resources can be saved,
 | |
| 			 * as the process can already terminate or serve another
 | |
| 			 * connection).
 | |
| 			 */
 | |
| 			res = SSL_shutdown(stream->ssl);
 | |
| 			if (res < 0) {
 | |
| 				int sslerr = SSL_get_error(stream->ssl, res);
 | |
| 				char err[256];
 | |
| 				ast_log(LOG_ERROR, "SSL_shutdown() failed: %s, %s\n",
 | |
| 					ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
 | |
| 			}
 | |
| 
 | |
| #if !(defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000L)) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
 | |
| 			if (!SSL_is_server(stream->ssl)) {
 | |
| #else
 | |
| 			if (!stream->ssl->server) {
 | |
| #endif
 | |
| 				/* For client threads, ensure that the error stack is cleared */
 | |
| #if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L)
 | |
| #if OPENSSL_VERSION_NUMBER >= 0x10000000L
 | |
| 				ERR_remove_thread_state(NULL);
 | |
| #else
 | |
| 				ERR_remove_state(0);
 | |
| #endif	/* OPENSSL_VERSION_NUMBER >= 0x10000000L */
 | |
| #endif  /* OPENSSL_VERSION_NUMBER  < 0x10100000L */
 | |
| 			}
 | |
| 
 | |
| 			SSL_free(stream->ssl);
 | |
| 			stream->ssl = NULL;
 | |
| 		}
 | |
| #endif	/* defined(DO_SSL) */
 | |
| 
 | |
| 		/*
 | |
| 		 * Issuing shutdown() is necessary here to avoid a race
 | |
| 		 * condition where the last data written may not appear
 | |
| 		 * in the TCP stream.  See ASTERISK-23548
 | |
| 		 */
 | |
| 		shutdown(stream->fd, SHUT_RDWR);
 | |
| 		if (close(stream->fd)) {
 | |
| 			ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
 | |
| 		}
 | |
| 		stream->fd = -1;
 | |
| 	}
 | |
| 	ao2_t_ref(stream, -1, "Closed ast_iostream");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void iostream_dtor(void *cookie)
 | |
| {
 | |
| #ifdef AST_DEVMODE
 | |
| 	/* Since the ast_assert below is the only one using stream,
 | |
| 	 * and ast_assert is only available with AST_DEVMODE, we
 | |
| 	 * put this in a conditional to avoid compiler warnings. */
 | |
| 	struct ast_iostream *stream = cookie;
 | |
| #endif
 | |
| 
 | |
| 	ast_assert(stream->fd == -1);
 | |
| }
 | |
| 
 | |
| struct ast_iostream *ast_iostream_from_fd(int *fd)
 | |
| {
 | |
| 	struct ast_iostream *stream;
 | |
| 
 | |
| 	stream = ao2_alloc_options(sizeof(*stream), iostream_dtor,
 | |
| 		AO2_ALLOC_OPT_LOCK_NOLOCK);
 | |
| 	if (stream) {
 | |
| 		stream->timeout = -1;
 | |
| 		stream->timeout_reset = -1;
 | |
| 		stream->fd = *fd;
 | |
| 		*fd = -1;
 | |
| 	}
 | |
| 
 | |
| 	return stream;
 | |
| }
 | |
| 
 | |
| int ast_iostream_start_tls(struct ast_iostream **pstream, SSL_CTX *ssl_ctx, int client)
 | |
| {
 | |
| #ifdef DO_SSL
 | |
| 	struct ast_iostream *stream = *pstream;
 | |
| 	int (*ssl_setup)(SSL *) = client ? SSL_connect : SSL_accept;
 | |
| 	int res;
 | |
| 
 | |
| 	stream->ssl = SSL_new(ssl_ctx);
 | |
| 	if (!stream->ssl) {
 | |
| 		ast_log(LOG_ERROR, "Unable to create new SSL connection\n");
 | |
| 		errno = ENOMEM;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * This function takes struct ast_iostream **, so it can chain
 | |
| 	 * SSL over any ast_iostream. For now we assume it's a file descriptor.
 | |
| 	 * But later this should instead use BIO wrapper to tie SSL to another
 | |
| 	 * ast_iostream.
 | |
| 	 */
 | |
| 	SSL_set_fd(stream->ssl, stream->fd);
 | |
| 
 | |
| 	res = ssl_setup(stream->ssl);
 | |
| 	if (res <= 0) {
 | |
| 		int sslerr = SSL_get_error(stream->ssl, res);
 | |
| 		char err[256];
 | |
| 
 | |
| 		ast_log(LOG_ERROR, "Problem setting up ssl connection: %s, %s\n",
 | |
| 			ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
 | |
| 		errno = EIO;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| #else
 | |
| 	ast_log(LOG_ERROR, "SSL not enabled in this build\n");
 | |
| 	errno = ENOTSUP;
 | |
| 	return -1;
 | |
| #endif
 | |
| }
 |