Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Functions | Variables
iostream.c File Reference
#include "asterisk.h"
#include "asterisk/iostream.h"
#include <fcntl.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
#include <openssl/ssl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include "asterisk/astobj2.h"
#include "asterisk/logger.h"
#include "asterisk/strings.h"
#include "asterisk/threadstorage.h"
#include "asterisk/time.h"
#include "asterisk/utils.h"
Include dependency graph for iostream.c:

Go to the source code of this file.

Data Structures

struct  ast_iostream
 

Macros

#define ERR2STR_BUFSIZE   128
 

Functions

static void __init_err2str_threadbuf (void)
 
int ast_iostream_close (struct ast_iostream *stream)
 Close an iostream. More...
 
ssize_t ast_iostream_discard (struct ast_iostream *stream, size_t size)
 Discard the specified number of bytes from an iostream. More...
 
struct ast_iostreamast_iostream_from_fd (int *fd)
 Create an iostream from a file descriptor. More...
 
int ast_iostream_get_fd (struct ast_iostream *stream)
 Get an iostream's file descriptor. More...
 
SSLast_iostream_get_ssl (struct ast_iostream *stream)
 Get a pointer to an iostream's OpenSSL SSL structure. More...
 
ssize_t ast_iostream_gets (struct ast_iostream *stream, char *buffer, size_t size)
 Read a LF-terminated string from an iostream. More...
 
void ast_iostream_nonblock (struct ast_iostream *stream)
 Make an iostream non-blocking. More...
 
ssize_t ast_iostream_printf (struct ast_iostream *stream, const char *format,...)
 Write a formatted string to an iostream. More...
 
ssize_t ast_iostream_read (struct ast_iostream *stream, void *buffer, size_t count)
 Read data from an iostream. More...
 
void ast_iostream_set_exclusive_input (struct ast_iostream *stream, int exclusive_input)
 Set the iostream if it can exclusively depend upon the set timeouts. More...
 
void ast_iostream_set_timeout_disable (struct ast_iostream *stream)
 Disable the iostream timeout timer. More...
 
void ast_iostream_set_timeout_idle_inactivity (struct ast_iostream *stream, int timeout, int timeout_reset)
 Set the iostream inactivity & idle timeout timers. More...
 
void ast_iostream_set_timeout_inactivity (struct ast_iostream *stream, int timeout)
 Set the iostream inactivity timeout timer. More...
 
void ast_iostream_set_timeout_sequence (struct ast_iostream *stream, struct timeval start, int timeout)
 Set the iostream I/O sequence timeout timer. More...
 
int ast_iostream_start_tls (struct ast_iostream **pstream, SSL_CTX *ssl_ctx, int client)
 Begin TLS on an iostream. More...
 
int ast_iostream_wait_for_input (struct ast_iostream *stream, int timeout)
 Wait for input on the iostream's file descriptor. More...
 
ssize_t ast_iostream_write (struct ast_iostream *stream, const void *buffer, size_t size)
 Write data to an iostream. More...
 
static void iostream_dtor (void *cookie)
 
static ssize_t iostream_read (struct ast_iostream *stream, void *buf, size_t size)
 
static const char * ssl_error_to_string (int sslerr, int ret)
 

Variables

static struct ast_threadstorage err2str_threadbuf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_err2str_threadbuf , .custom_init = NULL , }
 

Macro Definition Documentation

◆ ERR2STR_BUFSIZE

#define ERR2STR_BUFSIZE   128

Definition at line 53 of file iostream.c.

Referenced by ssl_error_to_string().

Function Documentation

◆ __init_err2str_threadbuf()

static void __init_err2str_threadbuf ( void  )
static

Definition at line 52 of file iostream.c.

56 {

◆ ast_iostream_close()

int ast_iostream_close ( struct ast_iostream stream)

Close an iostream.

Parameters
streamA pointer to an iostream
Return values
0success
-1failure
Note
On failure, errno may be set providing additional information on why the failure occurred.

Definition at line 528 of file iostream.c.

References ao2_t_ref, ast_log, errno, ast_iostream::fd, LOG_ERROR, NULL, ast_iostream::ssl, and ssl_error_to_string().

Referenced by app_exec(), ast_tcptls_close_session_file(), ast_websocket_close(), auth_http_callback(), close_mansession_file(), generic_http_callback(), session_destroy_fn(), and session_instance_destructor().

529 {
530  if (!stream) {
531  errno = EBADF;
532  return -1;
533  }
534 
535  if (stream->fd != -1) {
536 #if defined(DO_SSL)
537  if (stream->ssl) {
538  int res;
539 
540  /*
541  * According to the TLS standard, it is acceptable for an
542  * application to only send its shutdown alert and then
543  * close the underlying connection without waiting for
544  * the peer's response (this way resources can be saved,
545  * as the process can already terminate or serve another
546  * connection).
547  */
548  res = SSL_shutdown(stream->ssl);
549  if (res < 0) {
550  int sslerr = SSL_get_error(stream->ssl, res);
551  char err[256];
552  ast_log(LOG_ERROR, "SSL_shutdown() failed: %s, %s\n",
553  ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
554  }
555 
556 #if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
557  if (!SSL_is_server(stream->ssl)) {
558 #else
559  if (!stream->ssl->server) {
560 #endif
561  /* For client threads, ensure that the error stack is cleared */
562 #if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L)
563 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
564  ERR_remove_thread_state(NULL);
565 #else
566  ERR_remove_state(0);
567 #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */
568 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
569  }
570 
571  SSL_free(stream->ssl);
572  stream->ssl = NULL;
573  }
574 #endif /* defined(DO_SSL) */
575 
576  /*
577  * Issuing shutdown() is necessary here to avoid a race
578  * condition where the last data written may not appear
579  * in the TCP stream. See ASTERISK-23548
580  */
581  shutdown(stream->fd, SHUT_RDWR);
582  if (close(stream->fd)) {
583  ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
584  }
585  stream->fd = -1;
586  }
587  ao2_t_ref(stream, -1, "Closed ast_iostream");
588 
589  return 0;
590 }
#define ao2_t_ref(o, delta, tag)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:463
static const char * ssl_error_to_string(int sslerr, int ret)
Definition: iostream.c:55
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
SSL * ssl
Definition: iostream.c:40
int errno

◆ ast_iostream_discard()

ssize_t ast_iostream_discard ( struct ast_iostream stream,
size_t  count 
)

Discard the specified number of bytes from an iostream.

Parameters
streamA pointer to an iostream
countThe number of bytes to discard.
Returns
Upon successful completion, returns the number of bytes discarded. Otherwise, -1 is returned and errno may be set indicating the error.

Definition at line 357 of file iostream.c.

References ast_iostream_read(), and buf.

Referenced by http_body_discard_contents().

358 {
359  char buf[1024];
360  size_t remaining = size;
361  ssize_t ret;
362 
363  while (remaining) {
364  ret = ast_iostream_read(stream, buf, remaining > sizeof(buf) ? sizeof(buf) : remaining);
365  if (ret <= 0) {
366  return ret;
367  }
368  remaining -= ret;
369  }
370 
371  return size;
372 }
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition: iostream.c:273

◆ ast_iostream_from_fd()

struct ast_iostream* ast_iostream_from_fd ( int *  fd)

Create an iostream from a file descriptor.

Parameters
fdA pointer to an open file descriptor
Returns
A newly allocated iostream or NULL if allocation fails.

Definition at line 604 of file iostream.c.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ast_iostream::fd, iostream_dtor(), ast_iostream::timeout, and ast_iostream::timeout_reset.

Referenced by app_exec(), ast_tcptls_client_create(), ast_tcptls_server_root(), auth_http_callback(), and generic_http_callback().

605 {
606  struct ast_iostream *stream;
607 
608  stream = ao2_alloc_options(sizeof(*stream), iostream_dtor,
610  if (stream) {
611  stream->timeout = -1;
612  stream->timeout_reset = -1;
613  stream->fd = *fd;
614  *fd = -1;
615  }
616 
617  return stream;
618 }
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:406
int timeout
Definition: iostream.c:43
static void iostream_dtor(void *cookie)
Definition: iostream.c:592
int timeout_reset
Definition: iostream.c:44

◆ ast_iostream_get_fd()

int ast_iostream_get_fd ( struct ast_iostream stream)

Get an iostream's file descriptor.

Parameters
streamA pointer to an iostream
Returns
The file descriptor for the given iostream, or -1 if the iostream has no open file descriptor.

Definition at line 84 of file iostream.c.

References ast_iostream::fd.

Referenced by _sip_tcp_helper_thread(), action_waitevent(), ast_websocket_fd(), ast_websocket_uri_cb(), auth_http_callback(), eivr_comm(), get_input(), handle_showmanconn(), httpd_helper_thread(), process_output(), session_do(), sip_prepare_socket(), and sip_tcptls_read().

85 {
86  return stream->fd;
87 }

◆ ast_iostream_get_ssl()

SSL* ast_iostream_get_ssl ( struct ast_iostream stream)

Get a pointer to an iostream's OpenSSL SSL structure.

Parameters
streamA pointer to an iostream
Returns
A pointer to the OpenSSL SSL structure for the given iostream, or NULL if TLS has not been initiated.
Note
If OpenSSL support is not included in the build, this will always return NULL.

Definition at line 108 of file iostream.c.

References ast_iostream::ssl.

Referenced by _sip_tcp_helper_thread(), ast_websocket_uri_cb(), handle_tcptls_connection(), sip_threadinfo_create(), and websocket_client_connect().

109 {
110  return stream->ssl;
111 }
SSL * ssl
Definition: iostream.c:40

◆ ast_iostream_gets()

ssize_t ast_iostream_gets ( struct ast_iostream stream,
char *  buffer,
size_t  size 
)

Read a LF-terminated string from an iostream.

Parameters
streamA pointer to an iostream
bufferPointer to a buffer to store the read bytes.
sizeThe total size of buffer in bytes.
Returns
The number of bytes stored in buffer, excluding the null byte used to terminate the string. If the size of buffer (indicated by the caller with the size argument) is not sufficient to store the entire line it will be truncated to fit the available space. The contents of buffer will always be terminated with a null byte. In the case of an error, -1 will be returned and errno may be set indicating the error.

Definition at line 300 of file iostream.c.

References iostream_read(), len(), ast_iostream::rbuf, ast_iostream::rbufhead, and ast_iostream::rbuflen.

Referenced by eivr_comm(), http_body_discard_chunk_trailer_headers(), http_body_get_chunk_length(), http_request_headers_get(), httpd_process_request(), and websocket_client_handshake_get_response().

301 {
302  size_t remaining = size;
303  ssize_t accum_size = 0;
304  ssize_t len;
305  char *newline;
306 
307  for (;;) {
308  /* Search for newline */
309  newline = memchr(stream->rbufhead, '\n', stream->rbuflen);
310  if (newline) {
311  len = newline - stream->rbufhead + 1;
312  if (len > remaining - 1) {
313  len = remaining - 1;
314  }
315  break;
316  }
317 
318  /* Enough buffered line data to fill request buffer? */
319  if (stream->rbuflen >= remaining - 1) {
320  len = remaining - 1;
321  break;
322  }
323  if (stream->rbuflen) {
324  /* Put leftover buffered line data into request buffer */
325  memcpy(buffer + accum_size, stream->rbufhead, stream->rbuflen);
326  remaining -= stream->rbuflen;
327  accum_size += stream->rbuflen;
328  stream->rbuflen = 0;
329  }
330  stream->rbufhead = stream->rbuf;
331 
332  len = iostream_read(stream, stream->rbuf, sizeof(stream->rbuf));
333  if (len == 0) {
334  /* Nothing new was read. Return whatever we have accumulated. */
335  break;
336  }
337  if (len < 0) {
338  if (accum_size) {
339  /* We have an accumulated buffer so return that instead. */
340  len = 0;
341  break;
342  }
343  return len;
344  }
345  stream->rbuflen += len;
346  }
347 
348  /* Return read buffer string length */
349  memcpy(buffer + accum_size, stream->rbufhead, len);
350  buffer[accum_size + len] = 0;
351  stream->rbuflen -= len;
352  stream->rbufhead += len;
353 
354  return accum_size + len;
355 }
int rbuflen
Definition: iostream.c:46
char * rbufhead
Definition: iostream.c:47
char rbuf[2048]
Definition: iostream.c:48
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static ssize_t iostream_read(struct ast_iostream *stream, void *buf, size_t size)
Definition: iostream.c:155

◆ ast_iostream_nonblock()

void ast_iostream_nonblock ( struct ast_iostream stream)

Make an iostream non-blocking.

Parameters
streamA pointer to an iostream
Returns
Nothing

Definition at line 103 of file iostream.c.

References ast_fd_set_flags, and ast_iostream::fd.

Referenced by _sip_tcp_helper_thread(), ast_websocket_set_nonblock(), httpd_helper_thread(), and session_do().

104 {
105  ast_fd_set_flags(stream->fd, O_NONBLOCK);
106 }
#define ast_fd_set_flags(fd, flags)
Set flags on the given file descriptor.
Definition: utils.h:1009

◆ ast_iostream_printf()

ssize_t ast_iostream_printf ( struct ast_iostream stream,
const char *  format,
  ... 
)

Write a formatted string to an iostream.

Parameters
streamA pointer to an iostream
formatA format string, as documented by printf(3)
...Arguments for the provided format string
Returns
The number of bytes written, or -1 if an error occurs. Note that if -1 is returned, the number of bytes written to the iostream is unspecified.

Definition at line 491 of file iostream.c.

References ast_free, ast_iostream_write(), ast_malloc, buf, error(), and len().

Referenced by ast_http_send(), ast_websocket_uri_cb(), and websocket_client_handshake().

492 {
493  char sbuf[512], *buf = sbuf;
494  int len, len2, ret = -1;
495  va_list va;
496 
497  va_start(va, format);
498  len = vsnprintf(buf, sizeof(sbuf), format, va);
499  va_end(va);
500 
501  if (len > sizeof(sbuf) - 1) {
502  /* Add one to the string length to accommodate the NULL byte */
503  size_t buf_len = len + 1;
504 
505  buf = ast_malloc(buf_len);
506  if (!buf) {
507  return -1;
508  }
509  va_start(va, format);
510  len2 = vsnprintf(buf, buf_len, format, va);
511  va_end(va);
512  if (len2 != len) {
513  goto error;
514  }
515  }
516 
517  if (ast_iostream_write(stream, buf, len) == len)
518  ret = len;
519 
520 error:
521  if (buf != sbuf) {
522  ast_free(buf);
523  }
524 
525  return ret;
526 }
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t size)
Write data to an iostream.
Definition: iostream.c:374
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_free(a)
Definition: astmm.h:182
int error(const char *format,...)
Definition: utils/frame.c:999
static snd_pcm_format_t format
Definition: chan_alsa.c:102

◆ ast_iostream_read()

ssize_t ast_iostream_read ( struct ast_iostream stream,
void *  buffer,
size_t  count 
)

Read data from an iostream.

Parameters
streamA pointer to an iostream
bufferPointer to a buffer to store the read bytes.
countThe number of bytes to read.
Returns
Upon successful completion, returns a non-negative integer indicating the number of bytes actually read. Otherwise, returns -1 and may set errno to indicate the error.

Definition at line 273 of file iostream.c.

References errno, ast_iostream::fd, iostream_read(), ast_iostream::rbufhead, and ast_iostream::rbuflen.

Referenced by ast_iostream_discard(), get_input(), http_body_check_chunk_sync(), http_body_read_contents(), readmimefile(), sip_tcptls_read(), and ws_safe_read().

274 {
275  if (!count) {
276  /* You asked for no data you got no data. */
277  return 0;
278  }
279 
280  if (!stream || stream->fd == -1) {
281  errno = EBADF;
282  return -1;
283  }
284 
285  /* Get any remains from the read buffer */
286  if (stream->rbuflen) {
287  size_t r = count;
288  if (r > stream->rbuflen) {
289  r = stream->rbuflen;
290  }
291  memcpy(buffer, stream->rbufhead, r);
292  stream->rbuflen -= r;
293  stream->rbufhead += r;
294  return r;
295  }
296 
297  return iostream_read(stream, buffer, count);
298 }
int rbuflen
Definition: iostream.c:46
char * rbufhead
Definition: iostream.c:47
int errno
static ssize_t iostream_read(struct ast_iostream *stream, void *buf, size_t size)
Definition: iostream.c:155

◆ ast_iostream_set_exclusive_input()

void ast_iostream_set_exclusive_input ( struct ast_iostream stream,
int  exclusive_input 
)

Set the iostream if it can exclusively depend upon the set timeouts.

Parameters
streamA pointer to an iostream
exclusive_inputTRUE if stream can exclusively wait for fd input. Otherwise, the stream will not wait for fd input. It will wait while trying to send data.
Note
The stream timeouts still need to be set.
Returns
Nothing

Definition at line 148 of file iostream.c.

References ast_assert, ast_iostream::exclusive_input, and NULL.

Referenced by _sip_tcp_helper_thread(), ast_websocket_set_nonblock(), ast_websocket_uri_cb(), httpd_helper_thread(), and session_do().

149 {
150  ast_assert(stream != NULL);
151 
153 }
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
int exclusive_input
Definition: iostream.c:45

◆ ast_iostream_set_timeout_disable()

void ast_iostream_set_timeout_disable ( struct ast_iostream stream)

Disable the iostream timeout timer.

Parameters
streamA pointer to an iostream
Returns
Nothing

Definition at line 113 of file iostream.c.

References ast_assert, NULL, ast_iostream::timeout, and ast_iostream::timeout_reset.

Referenced by _sip_tcp_helper_thread(), ast_websocket_close(), ast_websocket_write(), send_string(), and session_do().

114 {
115  ast_assert(stream != NULL);
116 
117  stream->timeout = -1;
118  stream->timeout_reset = -1;
119 }
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
int timeout
Definition: iostream.c:43
int timeout_reset
Definition: iostream.c:44

◆ ast_iostream_set_timeout_idle_inactivity()

void ast_iostream_set_timeout_idle_inactivity ( struct ast_iostream stream,
int  timeout,
int  timeout_reset 
)

Set the iostream inactivity & idle timeout timers.

Parameters
streamA pointer to an iostream
timeoutNumber of milliseconds to wait for initial data transfer with the peer.
timeout_resetNumber of milliseconds to wait for subsequent data transfer with the peer.

As an example, if you want to timeout a peer if they do not send an initial message within 5 seconds or if they do not send a message at least every 30 seconds, you would set timeout to 5000 and timeout_reset to 30000.

Note
Setting either of these timeouts to -1 will disable them.
Returns
Nothing

Definition at line 130 of file iostream.c.

References ast_assert, NULL, ast_iostream::start, ast_iostream::timeout, and ast_iostream::timeout_reset.

Referenced by httpd_helper_thread().

131 {
132  ast_assert(stream != NULL);
133 
134  stream->start.tv_sec = 0;
135  stream->timeout = timeout;
136  stream->timeout_reset = timeout_reset;
137 }
static int timeout
Definition: cdr_mysql.c:86
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
int timeout
Definition: iostream.c:43
struct timeval start
Definition: iostream.c:41
int timeout_reset
Definition: iostream.c:44

◆ ast_iostream_set_timeout_inactivity()

void ast_iostream_set_timeout_inactivity ( struct ast_iostream stream,
int  timeout 
)

Set the iostream inactivity timeout timer.

Parameters
streamA pointer to an iostream
timeoutNumber of milliseconds to wait for data transfer with the peer.

This is basically how much time we are willing to spend in an I/O call before we declare the peer unresponsive.

Note
Setting timeout to -1 disables the timeout.
Setting this timeout replaces the I/O sequence timeout timer.
Returns
Nothing

Definition at line 121 of file iostream.c.

References ast_assert, NULL, ast_iostream::start, ast_iostream::timeout, and ast_iostream::timeout_reset.

Referenced by ast_websocket_close(), and send_string().

122 {
123  ast_assert(stream != NULL);
124 
125  stream->start.tv_sec = 0;
126  stream->timeout = timeout;
127  stream->timeout_reset = timeout;
128 }
static int timeout
Definition: cdr_mysql.c:86
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
int timeout
Definition: iostream.c:43
struct timeval start
Definition: iostream.c:41
int timeout_reset
Definition: iostream.c:44

◆ ast_iostream_set_timeout_sequence()

void ast_iostream_set_timeout_sequence ( struct ast_iostream stream,
struct timeval  start,
int  timeout 
)

Set the iostream I/O sequence timeout timer.

Parameters
streamA pointer to an iostream
startTime the I/O sequence timer starts.
timeoutNumber of milliseconds from the start time before timeout.

This is how much time are we willing to allow the peer to complete an operation that can take several I/O calls. The main use is as an authentication timer with us.

Note
Setting timeout to -1 disables the timeout.
Setting this timeout replaces the inactivity timeout timer.
Returns
Nothing

Definition at line 139 of file iostream.c.

References ast_assert, NULL, ast_iostream::start, ast_iostream::timeout, and ast_iostream::timeout_reset.

Referenced by _sip_tcp_helper_thread(), ast_websocket_write(), and session_do().

140 {
141  ast_assert(stream != NULL);
142 
143  stream->start = start;
144  stream->timeout = timeout;
145  stream->timeout_reset = timeout;
146 }
static int timeout
Definition: cdr_mysql.c:86
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
int timeout
Definition: iostream.c:43
struct timeval start
Definition: iostream.c:41
int timeout_reset
Definition: iostream.c:44

◆ ast_iostream_start_tls()

int ast_iostream_start_tls ( struct ast_iostream **  stream,
SSL_CTX ctx,
int  client 
)

Begin TLS on an iostream.

Parameters
streamA pointer to an iostream pointer
ctxA pointer to an SSL_CTX which will be passed to SSL_new()
clientNon-zero to indicate that we are the client, zero to indicate that we are the server.
Return values
0success
-1failure
Note
The iostream that is passed in stream may be replaced with a different one before this function returns.
On failure, errno may be set providing additional information on why the failure occurred.

Definition at line 620 of file iostream.c.

References ast_log, errno, ast_iostream::fd, LOG_ERROR, ast_iostream::ssl, and ssl_error_to_string().

Referenced by handle_tcptls_connection().

621 {
622 #ifdef DO_SSL
623  struct ast_iostream *stream = *pstream;
624  int (*ssl_setup)(SSL *) = client ? SSL_connect : SSL_accept;
625  int res;
626 
627  stream->ssl = SSL_new(ssl_ctx);
628  if (!stream->ssl) {
629  ast_log(LOG_ERROR, "Unable to create new SSL connection\n");
630  errno = ENOMEM;
631  return -1;
632  }
633 
634  /*
635  * This function takes struct ast_iostream **, so it can chain
636  * SSL over any ast_iostream. For now we assume it's a file descriptor.
637  * But later this should instead use BIO wrapper to tie SSL to another
638  * ast_iostream.
639  */
640  SSL_set_fd(stream->ssl, stream->fd);
641 
642  res = ssl_setup(stream->ssl);
643  if (res <= 0) {
644  int sslerr = SSL_get_error(stream->ssl, res);
645  char err[256];
646 
647  ast_log(LOG_ERROR, "Problem setting up ssl connection: %s, %s\n",
648  ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
649  errno = EIO;
650  return -1;
651  }
652 
653  return 0;
654 #else
655  ast_log(LOG_ERROR, "SSL not enabled in this build\n");
656  errno = ENOTSUP;
657  return -1;
658 #endif
659 }
static const char * ssl_error_to_string(int sslerr, int ret)
Definition: iostream.c:55
struct ssl_st SSL
Definition: iostream.h:37
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
SSL * ssl
Definition: iostream.c:40
int errno

◆ ast_iostream_wait_for_input()

int ast_iostream_wait_for_input ( struct ast_iostream stream,
int  timeout 
)

Wait for input on the iostream's file descriptor.

Since
16.8.0
17.2.0
Parameters
streamA pointer to an iostream
timeoutthe number of milliseconds to wait
Return values
-1if error occurred
0if the timeout expired
1if the stream is ready for reading

Definition at line 89 of file iostream.c.

References ast_wait_for_input(), ast_iostream::fd, and ast_iostream::ssl.

Referenced by ast_websocket_wait_for_input(), and ws_safe_read().

90 {
91 #if defined(DO_SSL)
92  /* Because SSL is read in blocks, it's possible that the last time we read we
93  got more than we asked for and it is now buffered inside OpenSSL. If that
94  is the case, calling ast_wait_for_input() will block until the fd is ready
95  for reading again, which might never happen. */
96  if (stream->ssl && SSL_pending(stream->ssl)) {
97  return 1;
98  }
99 #endif
100  return ast_wait_for_input(stream->fd, timeout);
101 }
static int timeout
Definition: cdr_mysql.c:86
SSL * ssl
Definition: iostream.c:40
int ast_wait_for_input(int fd, int ms)
Definition: main/utils.c:1519

◆ ast_iostream_write()

ssize_t ast_iostream_write ( struct ast_iostream stream,
const void *  buffer,
size_t  count 
)

Write data to an iostream.

Parameters
streamA pointer to an iostream
bufferPointer to a buffer from which to read bytes.
countThe number of bytes from buffer to write.
Returns
Upon successful completion, returns the number of bytes actually written to the iostream. This number shall never be greater than count. Otherwise, returns -1 and may set errno to indicate the error.

Definition at line 374 of file iostream.c.

References ast_debug, ast_remaining_ms(), ast_tvnow(), ast_wait_for_input(), ast_wait_for_output(), errno, ast_iostream::fd, ast_iostream::ssl, ssl_error_to_string(), ast_iostream::start, and ast_iostream::timeout.

Referenced by _sip_tcp_helper_thread(), ast_http_send(), ast_iostream_printf(), ast_websocket_close(), ast_websocket_write(), process_output(), send_eivr_event(), and send_string().

375 {
376  struct timeval start;
377  int ms;
378  int res;
379  int written;
380  int remaining;
381 
382  if (!size) {
383  /* You asked to write no data you wrote no data. */
384  return 0;
385  }
386 
387  if (!stream || stream->fd == -1) {
388  errno = EBADF;
389  return -1;
390  }
391 
392  if (stream->start.tv_sec) {
393  start = stream->start;
394  } else {
395  start = ast_tvnow();
396  }
397 
398 #if defined(DO_SSL)
399  if (stream->ssl) {
400  written = 0;
401  remaining = size;
402  for (;;) {
403  int sslerr;
404  char err[256];
405  res = SSL_write(stream->ssl, buffer + written, remaining);
406  if (res == remaining) {
407  /* Everything was written. */
408  return size;
409  }
410  if (0 < res) {
411  /* Successfully wrote part of the buffer. Try to write the rest. */
412  written += res;
413  remaining -= res;
414  continue;
415  }
416  sslerr = SSL_get_error(stream->ssl, res);
417  switch (sslerr) {
418  case SSL_ERROR_ZERO_RETURN:
419  ast_debug(1, "TLS clean shutdown alert writing data\n");
420  if (written) {
421  /* Report partial write. */
422  return written;
423  }
424  errno = EBADF;
425  return -1;
426  case SSL_ERROR_WANT_READ:
427  ms = ast_remaining_ms(start, stream->timeout);
428  if (!ms) {
429  /* Report partial write. */
430  ast_debug(1, "TLS timeout writing data (want read)\n");
431  return written;
432  }
433  ast_wait_for_input(stream->fd, ms);
434  break;
435  case SSL_ERROR_WANT_WRITE:
436  ms = ast_remaining_ms(start, stream->timeout);
437  if (!ms) {
438  /* Report partial write. */
439  ast_debug(1, "TLS timeout writing data (want write)\n");
440  return written;
441  }
442  ast_wait_for_output(stream->fd, ms);
443  break;
444  default:
445  /* Undecoded SSL or transport error. */
446  ast_debug(1, "TLS transport or SSL error writing data: %s, %s\n", ERR_error_string(sslerr, err),
447  ssl_error_to_string(sslerr, res));
448  if (written) {
449  /* Report partial write. */
450  return written;
451  }
452  errno = EBADF;
453  return -1;
454  }
455  }
456  }
457 #endif /* defined(DO_SSL) */
458 
459  written = 0;
460  remaining = size;
461  for (;;) {
462  res = write(stream->fd, buffer + written, remaining);
463  if (res == remaining) {
464  /* Yay everything was written. */
465  return size;
466  }
467  if (0 < res) {
468  /* Successfully wrote part of the buffer. Try to write the rest. */
469  written += res;
470  remaining -= res;
471  continue;
472  }
473  if (errno != EINTR && errno != EAGAIN) {
474  /* Not a retryable error. */
475  ast_debug(1, "TCP socket error writing: %s\n", strerror(errno));
476  if (written) {
477  return written;
478  }
479  return -1;
480  }
481  ms = ast_remaining_ms(start, stream->timeout);
482  if (!ms) {
483  /* Report partial write. */
484  ast_debug(1, "TCP timeout writing data\n");
485  return written;
486  }
487  ast_wait_for_output(stream->fd, ms);
488  }
489 }
static const char * ssl_error_to_string(int sslerr, int ret)
Definition: iostream.c:55
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
int timeout
Definition: iostream.c:43
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: main/utils.c:2033
SSL * ssl
Definition: iostream.c:40
int errno
struct timeval start
Definition: iostream.c:41
int ast_wait_for_output(int fd, int ms)
Definition: main/utils.c:1529
int ast_wait_for_input(int fd, int ms)
Definition: main/utils.c:1519

◆ iostream_dtor()

static void iostream_dtor ( void *  cookie)
static

Definition at line 592 of file iostream.c.

References ast_assert, and ast_iostream::fd.

Referenced by ast_iostream_from_fd().

593 {
594 #ifdef AST_DEVMODE
595  /* Since the ast_assert below is the only one using stream,
596  * and ast_assert is only available with AST_DEVMODE, we
597  * put this in a conditional to avoid compiler warnings. */
598  struct ast_iostream *stream = cookie;
599 #endif
600 
601  ast_assert(stream->fd == -1);
602 }
#define ast_assert(a)
Definition: utils.h:695

◆ iostream_read()

static ssize_t iostream_read ( struct ast_iostream stream,
void *  buf,
size_t  size 
)
static

Definition at line 155 of file iostream.c.

References ast_debug, ast_remaining_ms(), ast_tvnow(), ast_wait_for_input(), ast_wait_for_output(), errno, ast_iostream::exclusive_input, ast_iostream::fd, ast_iostream::ssl, ssl_error_to_string(), ast_iostream::start, ast_iostream::timeout, and ast_iostream::timeout_reset.

Referenced by ast_iostream_gets(), and ast_iostream_read().

156 {
157  struct timeval start;
158  int ms;
159  int res;
160 
161  if (stream->start.tv_sec) {
162  start = stream->start;
163  } else {
164  start = ast_tvnow();
165  }
166 
167 #if defined(DO_SSL)
168  if (stream->ssl) {
169  for (;;) {
170  int sslerr;
171  char err[256];
172  res = SSL_read(stream->ssl, buf, size);
173  if (0 < res) {
174  /* We read some payload data. */
175  stream->timeout = stream->timeout_reset;
176  return res;
177  }
178  sslerr = SSL_get_error(stream->ssl, res);
179  switch (sslerr) {
180  case SSL_ERROR_ZERO_RETURN:
181  /* Report EOF for a shutdown */
182  ast_debug(1, "TLS clean shutdown alert reading data\n");
183  return 0;
184  case SSL_ERROR_WANT_READ:
185  if (!stream->exclusive_input) {
186  /* We cannot wait for data now. */
187  errno = EAGAIN;
188  return -1;
189  }
190  while ((ms = ast_remaining_ms(start, stream->timeout))) {
191  res = ast_wait_for_input(stream->fd, ms);
192  if (0 < res) {
193  /* Socket is ready to be read. */
194  break;
195  }
196  if (res < 0) {
197  if (errno == EINTR || errno == EAGAIN) {
198  /* Try again. */
199  continue;
200  }
201  ast_debug(1, "TLS socket error waiting for read data: %s\n",
202  strerror(errno));
203  return -1;
204  }
205  }
206  break;
207  case SSL_ERROR_WANT_WRITE:
208  while ((ms = ast_remaining_ms(start, stream->timeout))) {
209  res = ast_wait_for_output(stream->fd, ms);
210  if (0 < res) {
211  /* Socket is ready to be written. */
212  break;
213  }
214  if (res < 0) {
215  if (errno == EINTR || errno == EAGAIN) {
216  /* Try again. */
217  continue;
218  }
219  ast_debug(1, "TLS socket error waiting for write space: %s\n",
220  strerror(errno));
221  return -1;
222  }
223  }
224  break;
225  case SSL_ERROR_SYSCALL:
226  /* Some non-recoverable I/O error occurred. The OpenSSL error queue may
227  * contain more information on the error. For socket I/O on Unix systems,
228  * consult errno for details. */
229  ast_debug(1, "TLS non-recoverable I/O error occurred: %s, %s\n", ERR_error_string(sslerr, err),
230  ssl_error_to_string(sslerr, res));
231  return -1;
232  default:
233  /* Report EOF for an undecoded SSL or transport error. */
234  ast_debug(1, "TLS transport or SSL error reading data: %s, %s\n", ERR_error_string(sslerr, err),
235  ssl_error_to_string(sslerr, res));
236  return -1;
237  }
238  if (!ms) {
239  /* Report EOF for a timeout */
240  ast_debug(1, "TLS timeout reading data\n");
241  return 0;
242  }
243  }
244  }
245 #endif /* defined(DO_SSL) */
246 
247  for (;;) {
248  res = read(stream->fd, buf, size);
249  if (0 <= res) {
250  /* Got data or we cannot wait for it. */
251  stream->timeout = stream->timeout_reset;
252  return res;
253  }
254  if (!stream->exclusive_input) {
255  return res;
256  }
257  if (errno != EINTR && errno != EAGAIN) {
258  /* Not a retryable error. */
259  ast_debug(1, "TCP socket error reading data: %s\n",
260  strerror(errno));
261  return -1;
262  }
263  ms = ast_remaining_ms(start, stream->timeout);
264  if (!ms) {
265  /* Report EOF for a timeout */
266  ast_debug(1, "TCP timeout reading data\n");
267  return 0;
268  }
269  ast_wait_for_input(stream->fd, ms);
270  }
271 }
static const char * ssl_error_to_string(int sslerr, int ret)
Definition: iostream.c:55
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
int timeout
Definition: iostream.c:43
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: main/utils.c:2033
SSL * ssl
Definition: iostream.c:40
int errno
struct timeval start
Definition: iostream.c:41
int timeout_reset
Definition: iostream.c:44
int ast_wait_for_output(int fd, int ms)
Definition: main/utils.c:1529
int ast_wait_for_input(int fd, int ms)
Definition: main/utils.c:1519
int exclusive_input
Definition: iostream.c:45

◆ ssl_error_to_string()

static const char* ssl_error_to_string ( int  sslerr,
int  ret 
)
static

Definition at line 55 of file iostream.c.

References ast_threadstorage_get(), buf, ERR2STR_BUFSIZE, err2str_threadbuf, and errno.

Referenced by ast_iostream_close(), ast_iostream_start_tls(), ast_iostream_write(), and iostream_read().

56 {
57  switch (sslerr) {
58  case SSL_ERROR_SSL:
59  return "Internal SSL error";
60  case SSL_ERROR_SYSCALL:
61  if (!ret) {
62  return "System call EOF";
63  } else if (ret == -1) {
64  char *buf;
65 
67  if (!buf) {
68  return "Unknown";
69  }
70 
71  snprintf(buf, ERR2STR_BUFSIZE, "Underlying BIO error: %s", strerror(errno));
72  return buf;
73  } else {
74  return "System call other";
75  }
76  default:
77  break;
78  }
79 
80  return "Unknown";
81 }
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static struct ast_threadstorage err2str_threadbuf
Definition: iostream.c:52
#define ERR2STR_BUFSIZE
Definition: iostream.c:53
int errno

Variable Documentation

◆ err2str_threadbuf

struct ast_threadstorage err2str_threadbuf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_err2str_threadbuf , .custom_init = NULL , }
static

Definition at line 52 of file iostream.c.

Referenced by ssl_error_to_string().