Asterisk - The Open Source Telephony Project  21.4.1
iostream.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2015, Digium, Inc.
5  *
6  * Timo Teräs <timo.teras@iki.fi>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 #include "asterisk.h"
20 
21 #include "asterisk/iostream.h" /* for DO_SSL */
22 
23 #include <fcntl.h> /* for O_NONBLOCK */
24 #ifdef DO_SSL
25 #include <openssl/err.h> /* for ERR_error_string */
26 #include <openssl/opensslv.h> /* for OPENSSL_VERSION_NUMBER */
27 #include <openssl/ssl.h> /* for SSL_get_error, SSL_free, SSL_... */
28 #endif
29 #include <sys/socket.h> /* for shutdown, SHUT_RDWR */
30 #include <sys/time.h> /* for timeval */
31 
32 #include "asterisk/astobj2.h" /* for ao2_alloc_options, ao2_alloc_... */
33 #include "asterisk/logger.h" /* for ast_debug, ast_log, LOG_ERROR */
34 #include "asterisk/strings.h" /* for asterisk/threadstorage.h */
35 #include "asterisk/threadstorage.h" /* for ast_threadstorage_get, AST_TH... */
36 #include "asterisk/time.h" /* for ast_remaining_ms, ast_tvnow */
37 #include "asterisk/utils.h" /* for ast_wait_for_input, ast_wait_... */
38 
39 struct ast_iostream {
40  SSL *ssl;
41  struct timeval start;
42  int fd;
43  int timeout;
44  int timeout_reset;
45  int exclusive_input;
46  int rbuflen;
47  char *rbufhead;
48  char rbuf[2048];
49  char *sni_hostname;
50 };
51 
52 #if defined(DO_SSL)
53 AST_THREADSTORAGE(err2str_threadbuf);
54 #define ERR2STR_BUFSIZE 128
55 
56 static const char *ssl_error_to_string(int sslerr, int ret)
57 {
58  switch (sslerr) {
59  case SSL_ERROR_SSL:
60  return "Internal SSL error";
61  case SSL_ERROR_SYSCALL:
62  if (!ret) {
63  return "System call EOF";
64  } else if (ret == -1) {
65  char *buf;
66 
67  buf = ast_threadstorage_get(&err2str_threadbuf, ERR2STR_BUFSIZE);
68  if (!buf) {
69  return "Unknown";
70  }
71 
72  snprintf(buf, ERR2STR_BUFSIZE, "Underlying BIO error: %s", strerror(errno));
73  return buf;
74  } else {
75  return "System call other";
76  }
77  default:
78  break;
79  }
80 
81  return "Unknown";
82 }
83 #endif
84 
85 int ast_iostream_get_fd(struct ast_iostream *stream)
86 {
87  return stream->fd;
88 }
89 
90 int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout)
91 {
92 #if defined(DO_SSL)
93  /* Because SSL is read in blocks, it's possible that the last time we read we
94  got more than we asked for and it is now buffered inside OpenSSL. If that
95  is the case, calling ast_wait_for_input() will block until the fd is ready
96  for reading again, which might never happen. */
97  if (stream->ssl && SSL_pending(stream->ssl)) {
98  return 1;
99  }
100 #endif
101  return ast_wait_for_input(stream->fd, timeout);
102 }
103 
105 {
106  ast_fd_set_flags(stream->fd, O_NONBLOCK);
107 }
108 
109 SSL *ast_iostream_get_ssl(struct ast_iostream *stream)
110 {
111  return stream->ssl;
112 }
113 
115 {
116  ast_assert(stream != NULL);
117 
118  stream->timeout = -1;
119  stream->timeout_reset = -1;
120 }
121 
122 void ast_iostream_set_timeout_inactivity(struct ast_iostream *stream, int timeout)
123 {
124  ast_assert(stream != NULL);
125 
126  stream->start.tv_sec = 0;
127  stream->timeout = timeout;
128  stream->timeout_reset = timeout;
129 }
130 
131 void ast_iostream_set_timeout_idle_inactivity(struct ast_iostream *stream, int timeout, int timeout_reset)
132 {
133  ast_assert(stream != NULL);
134 
135  stream->start.tv_sec = 0;
136  stream->timeout = timeout;
137  stream->timeout_reset = timeout_reset;
138 }
139 
140 void ast_iostream_set_timeout_sequence(struct ast_iostream *stream, struct timeval start, int timeout)
141 {
142  ast_assert(stream != NULL);
143 
144  stream->start = start;
145  stream->timeout = timeout;
146  stream->timeout_reset = timeout;
147 }
148 
149 void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive_input)
150 {
151  ast_assert(stream != NULL);
152 
153  stream->exclusive_input = exclusive_input;
154 }
155 
156 int ast_iostream_set_sni_hostname(struct ast_iostream *stream, const char *sni_hostname)
157 {
158  ast_assert(stream != NULL);
159 
160  ast_free(stream->sni_hostname);
161  stream->sni_hostname = ast_strdup(sni_hostname);
162 
163  return stream->sni_hostname ? 0 : -1;
164 }
165 
166 static ssize_t iostream_read(struct ast_iostream *stream, void *buf, size_t size)
167 {
168  struct timeval start;
169  int ms;
170  int res;
171 
172  if (stream->start.tv_sec) {
173  start = stream->start;
174  } else {
175  start = ast_tvnow();
176  }
177 
178 #if defined(DO_SSL)
179  if (stream->ssl) {
180  for (;;) {
181  int sslerr;
182  char err[256];
183  res = SSL_read(stream->ssl, buf, size);
184  if (0 < res) {
185  /* We read some payload data. */
186  stream->timeout = stream->timeout_reset;
187  return res;
188  }
189  sslerr = SSL_get_error(stream->ssl, res);
190  switch (sslerr) {
191  case SSL_ERROR_ZERO_RETURN:
192  /* Report EOF for a shutdown */
193  ast_debug(1, "TLS clean shutdown alert reading data\n");
194  return 0;
195  case SSL_ERROR_WANT_READ:
196  if (!stream->exclusive_input) {
197  /* We cannot wait for data now. */
198  errno = EAGAIN;
199  return -1;
200  }
201  while ((ms = ast_remaining_ms(start, stream->timeout))) {
202  res = ast_wait_for_input(stream->fd, ms);
203  if (0 < res) {
204  /* Socket is ready to be read. */
205  break;
206  }
207  if (res < 0) {
208  if (errno == EINTR || errno == EAGAIN) {
209  /* Try again. */
210  continue;
211  }
212  ast_debug(1, "TLS socket error waiting for read data: %s\n",
213  strerror(errno));
214  return -1;
215  }
216  }
217  break;
218  case SSL_ERROR_WANT_WRITE:
219  while ((ms = ast_remaining_ms(start, stream->timeout))) {
220  res = ast_wait_for_output(stream->fd, ms);
221  if (0 < res) {
222  /* Socket is ready to be written. */
223  break;
224  }
225  if (res < 0) {
226  if (errno == EINTR || errno == EAGAIN) {
227  /* Try again. */
228  continue;
229  }
230  ast_debug(1, "TLS socket error waiting for write space: %s\n",
231  strerror(errno));
232  return -1;
233  }
234  }
235  break;
236  case SSL_ERROR_SYSCALL:
237  /* Some non-recoverable I/O error occurred. The OpenSSL error queue may
238  * contain more information on the error. For socket I/O on Unix systems,
239  * consult errno for details. */
240  ast_debug(1, "TLS non-recoverable I/O error occurred: %s, %s\n", ERR_error_string(sslerr, err),
241  ssl_error_to_string(sslerr, res));
242  return -1;
243  default:
244  /* Report EOF for an undecoded SSL or transport error. */
245  ast_debug(1, "TLS transport or SSL error reading data: %s, %s\n", ERR_error_string(sslerr, err),
246  ssl_error_to_string(sslerr, res));
247  return -1;
248  }
249  if (!ms) {
250  /* Report EOF for a timeout */
251  ast_debug(1, "TLS timeout reading data\n");
252  return 0;
253  }
254  }
255  }
256 #endif /* defined(DO_SSL) */
257 
258  for (;;) {
259  res = read(stream->fd, buf, size);
260  if (0 <= res) {
261  /* Got data or we cannot wait for it. */
262  stream->timeout = stream->timeout_reset;
263  return res;
264  }
265  if (!stream->exclusive_input) {
266  return res;
267  }
268  if (errno != EINTR && errno != EAGAIN) {
269  /* Not a retryable error. */
270  ast_debug(1, "TCP socket error reading data: %s\n",
271  strerror(errno));
272  return -1;
273  }
274  ms = ast_remaining_ms(start, stream->timeout);
275  if (!ms) {
276  /* Report EOF for a timeout */
277  ast_debug(1, "TCP timeout reading data\n");
278  return 0;
279  }
280  ast_wait_for_input(stream->fd, ms);
281  }
282 }
283 
284 ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
285 {
286  if (!count) {
287  /* You asked for no data you got no data. */
288  return 0;
289  }
290 
291  if (!stream || stream->fd == -1) {
292  errno = EBADF;
293  return -1;
294  }
295 
296  /* Get any remains from the read buffer */
297  if (stream->rbuflen) {
298  size_t r = count;
299  if (r > stream->rbuflen) {
300  r = stream->rbuflen;
301  }
302  memcpy(buffer, stream->rbufhead, r);
303  stream->rbuflen -= r;
304  stream->rbufhead += r;
305  return r;
306  }
307 
308  return iostream_read(stream, buffer, count);
309 }
310 
311 ssize_t ast_iostream_gets(struct ast_iostream *stream, char *buffer, size_t size)
312 {
313  size_t remaining = size;
314  ssize_t accum_size = 0;
315  ssize_t len;
316  char *newline;
317 
318  for (;;) {
319  /* Search for newline */
320  newline = memchr(stream->rbufhead, '\n', stream->rbuflen);
321  if (newline) {
322  len = newline - stream->rbufhead + 1;
323  if (len > remaining - 1) {
324  len = remaining - 1;
325  }
326  break;
327  }
328 
329  /* Enough buffered line data to fill request buffer? */
330  if (stream->rbuflen >= remaining - 1) {
331  len = remaining - 1;
332  break;
333  }
334  if (stream->rbuflen) {
335  /* Put leftover buffered line data into request buffer */
336  memcpy(buffer + accum_size, stream->rbufhead, stream->rbuflen);
337  remaining -= stream->rbuflen;
338  accum_size += stream->rbuflen;
339  stream->rbuflen = 0;
340  }
341  stream->rbufhead = stream->rbuf;
342 
343  len = iostream_read(stream, stream->rbuf, sizeof(stream->rbuf));
344  if (len == 0) {
345  /* Nothing new was read. Return whatever we have accumulated. */
346  break;
347  }
348  if (len < 0) {
349  if (accum_size) {
350  /* We have an accumulated buffer so return that instead. */
351  len = 0;
352  break;
353  }
354  return len;
355  }
356  stream->rbuflen += len;
357  }
358 
359  /* Return read buffer string length */
360  memcpy(buffer + accum_size, stream->rbufhead, len);
361  buffer[accum_size + len] = 0;
362  stream->rbuflen -= len;
363  stream->rbufhead += len;
364 
365  return accum_size + len;
366 }
367 
368 ssize_t ast_iostream_discard(struct ast_iostream *stream, size_t size)
369 {
370  char buf[1024];
371  size_t remaining = size;
372  ssize_t ret;
373 
374  while (remaining) {
375  ret = ast_iostream_read(stream, buf, remaining > sizeof(buf) ? sizeof(buf) : remaining);
376  if (ret <= 0) {
377  return ret;
378  }
379  remaining -= ret;
380  }
381 
382  return size;
383 }
384 
385 ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t size)
386 {
387  struct timeval start;
388  int ms;
389  int res;
390  int written;
391  int remaining;
392 
393  if (!size) {
394  /* You asked to write no data you wrote no data. */
395  return 0;
396  }
397 
398  if (!stream || stream->fd == -1) {
399  errno = EBADF;
400  return -1;
401  }
402 
403  if (stream->start.tv_sec) {
404  start = stream->start;
405  } else {
406  start = ast_tvnow();
407  }
408 
409 #if defined(DO_SSL)
410  if (stream->ssl) {
411  written = 0;
412  remaining = size;
413  for (;;) {
414  int sslerr;
415  char err[256];
416  res = SSL_write(stream->ssl, buffer + written, remaining);
417  if (res == remaining) {
418  /* Everything was written. */
419  return size;
420  }
421  if (0 < res) {
422  /* Successfully wrote part of the buffer. Try to write the rest. */
423  written += res;
424  remaining -= res;
425  continue;
426  }
427  sslerr = SSL_get_error(stream->ssl, res);
428  switch (sslerr) {
429  case SSL_ERROR_ZERO_RETURN:
430  ast_debug(1, "TLS clean shutdown alert writing data\n");
431  if (written) {
432  /* Report partial write. */
433  return written;
434  }
435  errno = EBADF;
436  return -1;
437  case SSL_ERROR_WANT_READ:
438  ms = ast_remaining_ms(start, stream->timeout);
439  if (!ms) {
440  /* Report partial write. */
441  ast_debug(1, "TLS timeout writing data (want read)\n");
442  return written;
443  }
444  ast_wait_for_input(stream->fd, ms);
445  break;
446  case SSL_ERROR_WANT_WRITE:
447  ms = ast_remaining_ms(start, stream->timeout);
448  if (!ms) {
449  /* Report partial write. */
450  ast_debug(1, "TLS timeout writing data (want write)\n");
451  return written;
452  }
453  ast_wait_for_output(stream->fd, ms);
454  break;
455  default:
456  /* Undecoded SSL or transport error. */
457  ast_debug(1, "TLS transport or SSL error writing data: %s, %s\n", ERR_error_string(sslerr, err),
458  ssl_error_to_string(sslerr, res));
459  if (written) {
460  /* Report partial write. */
461  return written;
462  }
463  errno = EBADF;
464  return -1;
465  }
466  }
467  }
468 #endif /* defined(DO_SSL) */
469 
470  written = 0;
471  remaining = size;
472  for (;;) {
473  res = write(stream->fd, buffer + written, remaining);
474  if (res == remaining) {
475  /* Yay everything was written. */
476  return size;
477  }
478  if (0 < res) {
479  /* Successfully wrote part of the buffer. Try to write the rest. */
480  written += res;
481  remaining -= res;
482  continue;
483  }
484  if (errno != EINTR && errno != EAGAIN) {
485  /* Not a retryable error. */
486  ast_debug(1, "TCP socket error writing: %s\n", strerror(errno));
487  if (written) {
488  return written;
489  }
490  return -1;
491  }
492  ms = ast_remaining_ms(start, stream->timeout);
493  if (!ms) {
494  /* Report partial write. */
495  ast_debug(1, "TCP timeout writing data\n");
496  return written;
497  }
498  ast_wait_for_output(stream->fd, ms);
499  }
500 }
501 
502 ssize_t ast_iostream_printf(struct ast_iostream *stream, const char *format, ...)
503 {
504  char sbuf[512], *buf = sbuf;
505  int len, len2, ret = -1;
506  va_list va;
507 
508  va_start(va, format);
509  len = vsnprintf(buf, sizeof(sbuf), format, va);
510  va_end(va);
511 
512  if (len > sizeof(sbuf) - 1) {
513  /* Add one to the string length to accommodate the NULL byte */
514  size_t buf_len = len + 1;
515 
516  buf = ast_malloc(buf_len);
517  if (!buf) {
518  return -1;
519  }
520  va_start(va, format);
521  len2 = vsnprintf(buf, buf_len, format, va);
522  va_end(va);
523  if (len2 != len) {
524  goto error;
525  }
526  }
527 
528  if (ast_iostream_write(stream, buf, len) == len)
529  ret = len;
530 
531 error:
532  if (buf != sbuf) {
533  ast_free(buf);
534  }
535 
536  return ret;
537 }
538 
539 int ast_iostream_close(struct ast_iostream *stream)
540 {
541  if (!stream) {
542  errno = EBADF;
543  return -1;
544  }
545 
546  if (stream->fd != -1) {
547 #if defined(DO_SSL)
548  if (stream->ssl) {
549  int res;
550 
551  /*
552  * According to the TLS standard, it is acceptable for an
553  * application to only send its shutdown alert and then
554  * close the underlying connection without waiting for
555  * the peer's response (this way resources can be saved,
556  * as the process can already terminate or serve another
557  * connection).
558  */
559  res = SSL_shutdown(stream->ssl);
560  if (res < 0) {
561  int sslerr = SSL_get_error(stream->ssl, res);
562  char err[256];
563  ast_log(LOG_ERROR, "SSL_shutdown() failed: %s, %s\n",
564  ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
565  }
566 
567 #if !(defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000L)) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
568  if (!SSL_is_server(stream->ssl)) {
569 #else
570  if (!stream->ssl->server) {
571 #endif
572  /* For client threads, ensure that the error stack is cleared */
573 #if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L)
574 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
575  ERR_remove_thread_state(NULL);
576 #else
577  ERR_remove_state(0);
578 #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */
579 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
580  }
581 
582  SSL_free(stream->ssl);
583  stream->ssl = NULL;
584  }
585 #endif /* defined(DO_SSL) */
586 
587  /*
588  * Issuing shutdown() is necessary here to avoid a race
589  * condition where the last data written may not appear
590  * in the TCP stream. See ASTERISK-23548
591  */
592  shutdown(stream->fd, SHUT_RDWR);
593  if (close(stream->fd)) {
594  ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
595  }
596  stream->fd = -1;
597  }
598  ao2_t_ref(stream, -1, "Closed ast_iostream");
599 
600  return 0;
601 }
602 
603 static void iostream_dtor(void *cookie)
604 {
605  struct ast_iostream *stream = cookie;
606 
607  ast_free(stream->sni_hostname);
608  ast_assert(stream->fd == -1);
609 }
610 
612 {
613  struct ast_iostream *stream;
614 
615  stream = ao2_alloc_options(sizeof(*stream), iostream_dtor,
617  if (stream) {
618  stream->timeout = -1;
619  stream->timeout_reset = -1;
620  stream->fd = *fd;
621  *fd = -1;
622  }
623 
624  return stream;
625 }
626 
627 int ast_iostream_start_tls(struct ast_iostream **pstream, SSL_CTX *ssl_ctx, int client)
628 {
629 #ifdef DO_SSL
630  struct ast_iostream *stream = *pstream;
631  int (*ssl_setup)(SSL *) = client ? SSL_connect : SSL_accept;
632  int res;
633 
634  stream->ssl = SSL_new(ssl_ctx);
635  if (!stream->ssl) {
636  ast_log(LOG_ERROR, "Unable to create new SSL connection\n");
637  errno = ENOMEM;
638  return -1;
639  }
640 
641  /*
642  * This function takes struct ast_iostream **, so it can chain
643  * SSL over any ast_iostream. For now we assume it's a file descriptor.
644  * But later this should instead use BIO wrapper to tie SSL to another
645  * ast_iostream.
646  */
647  SSL_set_fd(stream->ssl, stream->fd);
648 
649  if (client && !ast_strlen_zero(stream->sni_hostname)) {
650  if (!SSL_set_tlsext_host_name(stream->ssl, stream->sni_hostname)) {
651  ast_log(LOG_ERROR, "Unable to set SNI hostname '%s'\n",
652  stream->sni_hostname);
653  errno = EIO;
654  return -1;
655  }
656  }
657 
658  res = ssl_setup(stream->ssl);
659  if (res <= 0) {
660  int sslerr = SSL_get_error(stream->ssl, res);
661  char err[256];
662 
663  ast_log(LOG_ERROR, "Problem setting up ssl connection: %s, %s\n",
664  ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
665  errno = EIO;
666  return -1;
667  }
668 
669  return 0;
670 #else
671  ast_log(LOG_ERROR, "SSL not enabled in this build\n");
672  errno = ENOTSUP;
673  return -1;
674 #endif
675 }
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.
Definition: iostream.c:149
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout)
Wait for input on the iostream's file descriptor.
Definition: iostream.c:90
Asterisk main include file. File version handling, generic pbx functions.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
String manipulation functions.
int ast_iostream_start_tls(struct ast_iostream **stream, SSL_CTX *ctx, int client)
Begin TLS on an iostream.
Definition: iostream.c:627
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:385
Time-related functions and macros.
ssize_t ast_iostream_discard(struct ast_iostream *stream, size_t count)
Discard the specified number of bytes from an iostream.
Definition: iostream.c:368
void ast_iostream_set_timeout_inactivity(struct ast_iostream *stream, int timeout)
Set the iostream inactivity timeout timer.
Definition: iostream.c:122
struct ast_iostream * ast_iostream_from_fd(int *fd)
Create an iostream from a file descriptor.
Definition: iostream.c:611
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition: iostream.c:85
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Definitions to aid in the use of thread local storage.
void ast_iostream_set_timeout_idle_inactivity(struct ast_iostream *stream, int timeout, int timeout_reset)
Set the iostream inactivity & idle timeout timers.
Definition: iostream.c:131
Utility functions.
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition: iostream.c:284
SSL * ast_iostream_get_ssl(struct ast_iostream *stream)
Get a pointer to an iostream's OpenSSL SSL structure.
Definition: iostream.c:109
#define ast_fd_set_flags(fd, flags)
Set flags on the given file descriptor.
Definition: utils.h:1039
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition: iostream.c:539
ssize_t ast_iostream_printf(struct ast_iostream *stream, const char *format,...)
Write a formatted string to an iostream.
Definition: iostream.c:502
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_debug(level,...)
Log a DEBUG message.
void ast_iostream_set_timeout_disable(struct ast_iostream *stream)
Disable the iostream timeout timer.
Definition: iostream.c:114
void ast_iostream_set_timeout_sequence(struct ast_iostream *stream, struct timeval start, int timeout)
Set the iostream I/O sequence timeout timer.
Definition: iostream.c:140
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281
Generic abstraction for input/output streams.
Support for logging to various files, console and syslog Configuration in file logger.conf.
ssize_t ast_iostream_gets(struct ast_iostream *stream, char *buffer, size_t size)
Read a LF-terminated string from an iostream.
Definition: iostream.c:311
int ast_iostream_set_sni_hostname(struct ast_iostream *stream, const char *sni_hostname)
Set the iostream's SNI hostname for TLS client connections.
Definition: iostream.c:156
void ast_iostream_nonblock(struct ast_iostream *stream)
Make an iostream non-blocking.
Definition: iostream.c:104