Fri Aug 17 00:17:49 2018

Asterisk developer's documentation


tcptls.c File Reference

Code to support TCP and TLS server/client. More...

#include "asterisk.h"
#include <fcntl.h>
#include <signal.h>
#include <sys/signal.h>
#include "asterisk/compat.h"
#include "asterisk/tcptls.h"
#include "asterisk/http.h"
#include "asterisk/utils.h"
#include "asterisk/strings.h"
#include "asterisk/options.h"
#include "asterisk/manager.h"
#include "asterisk/astobj2.h"
#include "asterisk/pbx.h"

Go to the source code of this file.

Data Structures

struct  ast_tcptls_stream

Functions

static int __ssl_setup (struct ast_tls_config *cfg, int client)
int ast_ssl_setup (struct ast_tls_config *cfg)
 Set up an SSL server.
void ast_ssl_teardown (struct ast_tls_config *cfg)
 free resources used by an SSL server
struct
ast_tcptls_session_instance
ast_tcptls_client_create (struct ast_tcptls_session_args *desc)
struct
ast_tcptls_session_instance
ast_tcptls_client_start (struct ast_tcptls_session_instance *tcptls_session)
 attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned.
void ast_tcptls_close_session_file (struct ast_tcptls_session_instance *tcptls_session)
 Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function.
HOOK_T ast_tcptls_server_read (struct ast_tcptls_session_instance *tcptls_session, void *buf, size_t count)
void * ast_tcptls_server_root (void *data)
void ast_tcptls_server_start (struct ast_tcptls_session_args *desc)
 This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept().
void ast_tcptls_server_stop (struct ast_tcptls_session_args *desc)
 Shutdown a running server if there is one.
HOOK_T ast_tcptls_server_write (struct ast_tcptls_session_instance *tcptls_session, const void *buf, size_t count)
void ast_tcptls_stream_set_exclusive_input (struct ast_tcptls_stream *stream, int exclusive_input)
 Set the TCP/TLS stream I/O if it can exclusively depend upon the set timeouts.
void ast_tcptls_stream_set_timeout_disable (struct ast_tcptls_stream *stream)
 Disable the TCP/TLS stream timeout timer.
void ast_tcptls_stream_set_timeout_inactivity (struct ast_tcptls_stream *stream, int timeout)
 Set the TCP/TLS stream inactivity timeout timer.
void ast_tcptls_stream_set_timeout_sequence (struct ast_tcptls_stream *stream, struct timeval start, int timeout)
 Set the TCP/TLS stream I/O sequence timeout timer.
int ast_tls_read_conf (struct ast_tls_config *tls_cfg, struct ast_tcptls_session_args *tls_desc, const char *varname, const char *value)
 Used to parse conf files containing tls/ssl options.
static void * handle_tcptls_connection (void *data)
 creates a FILE * from the fd passed by the accept thread. This operation is potentially expensive (certificate verification), so we do it in the child thread context.
static void session_instance_destructor (void *obj)
static struct ast_tcptls_streamtcptls_stream_alloc (void)
static int tcptls_stream_close (void *cookie)
static void tcptls_stream_dtor (void *cookie)
static FILE * tcptls_stream_fopen (struct ast_tcptls_stream *stream, SSL *ssl, int fd, int timeout)
static HOOK_T tcptls_stream_read (void *cookie, char *buf, LEN_T size)
static HOOK_T tcptls_stream_write (void *cookie, const char *buf, LEN_T size)

Detailed Description

Code to support TCP and TLS server/client.

Author:
Luigi Rizzo
Brett Bryant <brettbryant@gmail.com>

Definition in file tcptls.c.


Function Documentation

static int __ssl_setup ( struct ast_tls_config cfg,
int  client 
) [static]

Definition at line 745 of file tcptls.c.

References ast_debug, ast_log(), AST_SSL_SSLV2_CLIENT, AST_SSL_SSLV3_CLIENT, AST_SSL_TLSV1_CLIENT, AST_SSL_VERIFY_CLIENT, ast_strlen_zero(), ast_test_flag, ast_verb, ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, ast_tls_config::flags, LOG_WARNING, ast_tls_config::pvtfile, S_OR, and ast_tls_config::ssl_ctx.

Referenced by ast_ssl_setup(), and ast_tcptls_client_start().

00746 {
00747 #ifndef DO_SSL
00748    cfg->enabled = 0;
00749    return 0;
00750 #else
00751    int disable_ssl = 0;
00752 
00753    if (!cfg->enabled)
00754       return 0;
00755 
00756    /* Get rid of an old SSL_CTX since we're about to
00757     * allocate a new one
00758     */
00759    if (cfg->ssl_ctx) {
00760       SSL_CTX_free(cfg->ssl_ctx);
00761       cfg->ssl_ctx = NULL;
00762    }
00763 
00764    if (client) {
00765 #ifndef OPENSSL_NO_SSL2
00766       if (ast_test_flag(&cfg->flags, AST_SSL_SSLV2_CLIENT)) {
00767          ast_log(LOG_WARNING, "Usage of SSLv2 is discouraged due to known vulnerabilities. Please use 'tlsv1' or leave the TLS method unspecified!\n");
00768          cfg->ssl_ctx = SSL_CTX_new(SSLv2_client_method());
00769       } else
00770 #endif
00771       if (ast_test_flag(&cfg->flags, AST_SSL_SSLV3_CLIENT)) {
00772          ast_log(LOG_WARNING, "Usage of SSLv3 is discouraged due to known vulnerabilities. Please use 'tlsv1' or leave the TLS method unspecified!\n");
00773          cfg->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
00774       } else if (ast_test_flag(&cfg->flags, AST_SSL_TLSV1_CLIENT)) {
00775          cfg->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
00776       } else {
00777          disable_ssl = 1;
00778          cfg->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
00779       }
00780    } else {
00781       disable_ssl = 1;
00782       cfg->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
00783    }
00784 
00785    if (!cfg->ssl_ctx) {
00786       ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n");
00787       cfg->enabled = 0;
00788       return 0;
00789    }
00790 
00791    /* Due to the POODLE vulnerability, completely disable
00792     * SSLv2 and SSLv3 if we are not explicitly told to use
00793     * them. SSLv23_*_method supports TLSv1+.
00794     */
00795    if (disable_ssl) {
00796       long ssl_opts;
00797 
00798       ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
00799       SSL_CTX_set_options(cfg->ssl_ctx, ssl_opts);
00800    }
00801 
00802    SSL_CTX_set_verify(cfg->ssl_ctx,
00803       ast_test_flag(&cfg->flags, AST_SSL_VERIFY_CLIENT) ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE,
00804       NULL);
00805 
00806    if (!ast_strlen_zero(cfg->certfile)) {
00807       char *tmpprivate = ast_strlen_zero(cfg->pvtfile) ? cfg->certfile : cfg->pvtfile;
00808       if (SSL_CTX_use_certificate_chain_file(cfg->ssl_ctx, cfg->certfile) == 0) {
00809          if (!client) {
00810             /* Clients don't need a certificate, but if its setup we can use it */
00811             ast_verb(0, "SSL error loading cert file. <%s>", cfg->certfile);
00812             cfg->enabled = 0;
00813             SSL_CTX_free(cfg->ssl_ctx);
00814             cfg->ssl_ctx = NULL;
00815             return 0;
00816          }
00817       }
00818       if ((SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, tmpprivate, SSL_FILETYPE_PEM) == 0) || (SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 )) {
00819          if (!client) {
00820             /* Clients don't need a private key, but if its setup we can use it */
00821             ast_verb(0, "SSL error loading private key file. <%s>", tmpprivate);
00822             cfg->enabled = 0;
00823             SSL_CTX_free(cfg->ssl_ctx);
00824             cfg->ssl_ctx = NULL;
00825             return 0;
00826          }
00827       }
00828    }
00829    if (!ast_strlen_zero(cfg->cipher)) {
00830       if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
00831          if (!client) {
00832             ast_verb(0, "SSL cipher error <%s>", cfg->cipher);
00833             cfg->enabled = 0;
00834             SSL_CTX_free(cfg->ssl_ctx);
00835             cfg->ssl_ctx = NULL;
00836             return 0;
00837          }
00838       }
00839    }
00840    if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) {
00841       if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0)
00842          ast_verb(0, "SSL CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath);
00843    }
00844 
00845    ast_verb(0, "SSL certificate ok\n");
00846    return 1;
00847 #endif
00848 }

int ast_ssl_setup ( struct ast_tls_config cfg  ) 

Set up an SSL server.

Parameters:
cfg Configuration for the SSL server
Return values:
1 Success
0 Failure

Definition at line 850 of file tcptls.c.

References __ssl_setup().

Referenced by __ast_http_load(), __init_manager(), and reload_config().

00851 {
00852    return __ssl_setup(cfg, 0);
00853 }

void ast_ssl_teardown ( struct ast_tls_config cfg  ) 

free resources used by an SSL server

Note:
This only needs to be called if ast_ssl_setup() was directly called first.
Parameters:
cfg Configuration for the SSL server

Definition at line 855 of file tcptls.c.

References ast_tls_config::ssl_ctx.

Referenced by sip_tcptls_client_args_destructor(), and unload_module().

00856 {
00857 #ifdef DO_SSL
00858    if (cfg->ssl_ctx) {
00859       SSL_CTX_free(cfg->ssl_ctx);
00860       cfg->ssl_ctx = NULL;
00861    }
00862 #endif
00863 }

struct ast_tcptls_session_instance* ast_tcptls_client_create ( struct ast_tcptls_session_args desc  )  [read]

Definition at line 902 of file tcptls.c.

References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_bind(), ast_debug, ast_log(), ast_mutex_init, ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), ast_str_create(), ast_tcptls_session_instance::client, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_args::local_address, ast_tcptls_session_instance::lock, LOG_ERROR, LOG_WARNING, ast_tcptls_session_args::name, ast_tcptls_session_args::old_address, ast_tcptls_session_instance::overflow_buf, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::remote_address, ast_tcptls_session_args::remote_address, session_instance_destructor(), and ast_tcptls_session_args::worker_fn.

Referenced by app_exec(), and sip_prepare_socket().

00903 {
00904    int x = 1;
00905    struct ast_tcptls_session_instance *tcptls_session = NULL;
00906 
00907    /* Do nothing if nothing has changed */
00908    if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) {
00909       ast_debug(1, "Nothing changed in %s\n", desc->name);
00910       return NULL;
00911    }
00912 
00913    /* If we return early, there is no connection */
00914    ast_sockaddr_setnull(&desc->old_address);
00915 
00916    if (desc->accept_fd != -1)
00917       close(desc->accept_fd);
00918 
00919    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ?
00920              AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
00921    if (desc->accept_fd < 0) {
00922       ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
00923          desc->name, strerror(errno));
00924       return NULL;
00925    }
00926 
00927    /* if a local address was specified, bind to it so the connection will
00928       originate from the desired address */
00929    if (!ast_sockaddr_isnull(&desc->local_address)) {
00930       setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00931       if (ast_bind(desc->accept_fd, &desc->local_address)) {
00932          ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00933             desc->name,
00934             ast_sockaddr_stringify(&desc->local_address),
00935             strerror(errno));
00936          goto error;
00937       }
00938    }
00939 
00940    if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor)))
00941       goto error;
00942 
00943    ast_mutex_init(&tcptls_session->lock);
00944    tcptls_session->overflow_buf = ast_str_create(128);
00945    tcptls_session->client = 1;
00946    tcptls_session->fd = desc->accept_fd;
00947    tcptls_session->parent = desc;
00948    tcptls_session->parent->worker_fn = NULL;
00949    ast_sockaddr_copy(&tcptls_session->remote_address,
00950            &desc->remote_address);
00951 
00952    /* Set current info */
00953    ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
00954    return tcptls_session;
00955 
00956 error:
00957    close(desc->accept_fd);
00958    desc->accept_fd = -1;
00959    if (tcptls_session)
00960       ao2_ref(tcptls_session, -1);
00961    return NULL;
00962 }

struct ast_tcptls_session_instance* ast_tcptls_client_start ( struct ast_tcptls_session_instance tcptls_session  )  [read]

attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned.

Definition at line 865 of file tcptls.c.

References __ssl_setup(), ast_tcptls_session_args::accept_fd, ao2_ref, ast_connect(), ast_log(), ast_sockaddr_stringify(), desc, ast_tls_config::enabled, errno, handle_tcptls_connection(), LOG_ERROR, ast_tcptls_session_args::name, ast_tcptls_session_instance::parent, ast_tcptls_session_args::remote_address, and ast_tcptls_session_args::tls_cfg.

Referenced by _sip_tcp_helper_thread(), and app_exec().

00866 {
00867    struct ast_tcptls_session_args *desc;
00868    int flags;
00869 
00870    if (!(desc = tcptls_session->parent)) {
00871       goto client_start_error;
00872    }
00873 
00874    if (ast_connect(desc->accept_fd, &desc->remote_address)) {
00875       ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
00876          desc->name,
00877          ast_sockaddr_stringify(&desc->remote_address),
00878          strerror(errno));
00879       goto client_start_error;
00880    }
00881 
00882    flags = fcntl(desc->accept_fd, F_GETFL);
00883    fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
00884 
00885    if (desc->tls_cfg) {
00886       desc->tls_cfg->enabled = 1;
00887       __ssl_setup(desc->tls_cfg, 1);
00888    }
00889 
00890    return handle_tcptls_connection(tcptls_session);
00891 
00892 client_start_error:
00893    if (desc) {
00894       close(desc->accept_fd);
00895       desc->accept_fd = -1;
00896    }
00897    ao2_ref(tcptls_session, -1);
00898    return NULL;
00899 
00900 }

void ast_tcptls_close_session_file ( struct ast_tcptls_session_instance tcptls_session  ) 

Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function.

Definition at line 1033 of file tcptls.c.

References ast_log(), errno, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, and LOG_ERROR.

Referenced by _sip_tcp_helper_thread(), ast_http_send(), ast_tcptls_server_root(), handle_tcptls_connection(), httpd_helper_thread(), and sip_prepare_socket().

01034 {
01035    if (tcptls_session->f) {
01036       fflush(tcptls_session->f);
01037       if (fclose(tcptls_session->f)) {
01038          ast_log(LOG_ERROR, "fclose() failed: %s\n", strerror(errno));
01039       }
01040       tcptls_session->f = NULL;
01041       tcptls_session->fd = -1;
01042    } else if (tcptls_session->fd != -1) {
01043       /*
01044        * Issuing shutdown() is necessary here to avoid a race
01045        * condition where the last data written may not appear
01046        * in the TCP stream.  See ASTERISK-23548
01047        */
01048       shutdown(tcptls_session->fd, SHUT_RDWR);
01049       if (close(tcptls_session->fd)) {
01050          ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
01051       }
01052       tcptls_session->fd = -1;
01053    } else {
01054       ast_log(LOG_ERROR, "ast_tcptls_close_session_file invoked on session instance without file or file descriptor\n");
01055    }
01056 }

HOOK_T ast_tcptls_server_read ( struct ast_tcptls_session_instance tcptls_session,
void *  buf,
size_t  count 
)

Definition at line 519 of file tcptls.c.

References ast_log(), errno, ast_tcptls_stream::fd, LOG_ERROR, ast_tcptls_session_instance::stream_cookie, and tcptls_stream_read().

Referenced by sip_tcptls_read().

00520 {
00521    if (!tcptls_session->stream_cookie || tcptls_session->stream_cookie->fd == -1) {
00522       ast_log(LOG_ERROR, "TCP/TLS read called on invalid stream.\n");
00523       errno = EIO;
00524       return -1;
00525    }
00526 
00527    return tcptls_stream_read(tcptls_session->stream_cookie, buf, count);
00528 }

void* ast_tcptls_server_root ( void *  data  ) 

Definition at line 693 of file tcptls.c.

References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_accept(), ast_log(), ast_mutex_init, ast_pthread_create_detached_background, ast_sockaddr_copy(), ast_str_create(), ast_tcptls_close_session_file(), ast_wait_for_input(), ast_tcptls_session_instance::client, desc, errno, ast_tcptls_session_instance::fd, handle_tcptls_connection(), ast_tcptls_session_instance::lock, LOG_ERROR, LOG_WARNING, ast_tcptls_session_instance::overflow_buf, ast_tcptls_session_instance::parent, ast_tcptls_session_args::periodic_fn, ast_tcptls_session_args::poll_timeout, ast_tcptls_session_instance::remote_address, and session_instance_destructor().

00694 {
00695    struct ast_tcptls_session_args *desc = data;
00696    int fd;
00697    struct ast_sockaddr addr;
00698    struct ast_tcptls_session_instance *tcptls_session;
00699    pthread_t launched;
00700 
00701    for (;;) {
00702       int i, flags;
00703 
00704       if (desc->periodic_fn)
00705          desc->periodic_fn(desc);
00706       i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
00707       if (i <= 0)
00708          continue;
00709       fd = ast_accept(desc->accept_fd, &addr);
00710       if (fd < 0) {
00711          if ((errno != EAGAIN) && (errno != EINTR))
00712             ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
00713          continue;
00714       }
00715       tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor);
00716       if (!tcptls_session) {
00717          ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
00718          if (close(fd)) {
00719             ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
00720          }
00721          continue;
00722       }
00723 
00724       ast_mutex_init(&tcptls_session->lock);
00725       tcptls_session->overflow_buf = ast_str_create(128);
00726 
00727       flags = fcntl(fd, F_GETFL);
00728       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
00729       tcptls_session->fd = fd;
00730       tcptls_session->parent = desc;
00731       ast_sockaddr_copy(&tcptls_session->remote_address, &addr);
00732 
00733       tcptls_session->client = 0;
00734 
00735       /* This thread is now the only place that controls the single ref to tcptls_session */
00736       if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) {
00737          ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
00738          ast_tcptls_close_session_file(tcptls_session);
00739          ao2_ref(tcptls_session, -1);
00740       }
00741    }
00742    return NULL;
00743 }

void ast_tcptls_server_start ( struct ast_tcptls_session_args desc  ) 

This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept().

Version:
1.6.1 changed desc parameter to be of ast_tcptls_session_args type

Definition at line 964 of file tcptls.c.

References ast_tcptls_session_args::accept_fd, ast_tcptls_session_args::accept_fn, ast_bind(), ast_debug, ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), errno, ast_tcptls_session_args::local_address, LOG_ERROR, ast_tcptls_session_args::master, ast_tcptls_session_args::name, and ast_tcptls_session_args::old_address.

Referenced by __ast_http_load(), __init_manager(), and reload_config().

00965 {
00966    int flags;
00967    int x = 1;
00968 
00969    /* Do nothing if nothing has changed */
00970    if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) {
00971       ast_debug(1, "Nothing changed in %s\n", desc->name);
00972       return;
00973    }
00974 
00975    /* If we return early, there is no one listening */
00976    ast_sockaddr_setnull(&desc->old_address);
00977 
00978    /* Shutdown a running server if there is one */
00979    if (desc->master != AST_PTHREADT_NULL) {
00980       pthread_cancel(desc->master);
00981       pthread_kill(desc->master, SIGURG);
00982       pthread_join(desc->master, NULL);
00983    }
00984 
00985    if (desc->accept_fd != -1)
00986       close(desc->accept_fd);
00987 
00988    /* If there's no new server, stop here */
00989    if (ast_sockaddr_isnull(&desc->local_address)) {
00990       ast_debug(2, "Server disabled:  %s\n", desc->name);
00991       return;
00992    }
00993 
00994    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ?
00995              AF_INET6 : AF_INET, SOCK_STREAM, 0);
00996    if (desc->accept_fd < 0) {
00997       ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno));
00998       return;
00999    }
01000 
01001    setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
01002    if (ast_bind(desc->accept_fd, &desc->local_address)) {
01003       ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
01004          desc->name,
01005          ast_sockaddr_stringify(&desc->local_address),
01006          strerror(errno));
01007       goto error;
01008    }
01009    if (listen(desc->accept_fd, 10)) {
01010       ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
01011       goto error;
01012    }
01013    flags = fcntl(desc->accept_fd, F_GETFL);
01014    fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
01015    if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
01016       ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n",
01017          desc->name,
01018          ast_sockaddr_stringify(&desc->local_address),
01019          strerror(errno));
01020       goto error;
01021    }
01022 
01023    /* Set current info */
01024    ast_sockaddr_copy(&desc->old_address, &desc->local_address);
01025 
01026    return;
01027 
01028 error:
01029    close(desc->accept_fd);
01030    desc->accept_fd = -1;
01031 }

void ast_tcptls_server_stop ( struct ast_tcptls_session_args desc  ) 

Shutdown a running server if there is one.

Version:
1.6.1 changed desc parameter to be of ast_tcptls_session_args type

Definition at line 1058 of file tcptls.c.

References ast_tcptls_session_args::accept_fd, ast_debug, AST_PTHREADT_NULL, ast_tcptls_session_args::master, and ast_tcptls_session_args::name.

Referenced by __ast_http_load(), __init_manager(), http_shutdown(), manager_shutdown(), and unload_module().

01059 {
01060    if (desc->master != AST_PTHREADT_NULL) {
01061       pthread_cancel(desc->master);
01062       pthread_kill(desc->master, SIGURG);
01063       pthread_join(desc->master, NULL);
01064       desc->master = AST_PTHREADT_NULL;
01065    }
01066    if (desc->accept_fd != -1)
01067       close(desc->accept_fd);
01068    desc->accept_fd = -1;
01069    ast_debug(2, "Stopped server :: %s\n", desc->name);
01070 }

HOOK_T ast_tcptls_server_write ( struct ast_tcptls_session_instance tcptls_session,
const void *  buf,
size_t  count 
)

Definition at line 530 of file tcptls.c.

References ast_log(), errno, ast_tcptls_stream::fd, LOG_ERROR, ast_tcptls_session_instance::stream_cookie, and tcptls_stream_write().

Referenced by _sip_tcp_helper_thread().

00531 {
00532    if (!tcptls_session->stream_cookie || tcptls_session->stream_cookie->fd == -1) {
00533       ast_log(LOG_ERROR, "TCP/TLS write called on invalid stream.\n");
00534       errno = EIO;
00535       return -1;
00536    }
00537 
00538    return tcptls_stream_write(tcptls_session->stream_cookie, buf, count);
00539 }

void ast_tcptls_stream_set_exclusive_input ( struct ast_tcptls_stream stream,
int  exclusive_input 
)

Set the TCP/TLS stream I/O if it can exclusively depend upon the set timeouts.

Parameters:
stream TCP/TLS stream control data.
exclusive_input TRUE 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 107 of file tcptls.c.

References ast_assert, and ast_tcptls_stream::exclusive_input.

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

00108 {
00109    ast_assert(stream != NULL);
00110 
00111    stream->exclusive_input = exclusive_input;
00112 }

void ast_tcptls_stream_set_timeout_disable ( struct ast_tcptls_stream stream  ) 

Disable the TCP/TLS stream timeout timer.

Parameters:
stream TCP/TLS stream control data.
Returns:
Nothing

Definition at line 84 of file tcptls.c.

References ast_assert, and ast_tcptls_stream::timeout.

Referenced by _sip_tcp_helper_thread(), and session_do().

00085 {
00086    ast_assert(stream != NULL);
00087 
00088    stream->timeout = -1;
00089 }

void ast_tcptls_stream_set_timeout_inactivity ( struct ast_tcptls_stream stream,
int  timeout 
)

Set the TCP/TLS stream inactivity timeout timer.

Parameters:
stream TCP/TLS stream control data.
timeout Number 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 91 of file tcptls.c.

References ast_assert, ast_tcptls_stream::start, and ast_tcptls_stream::timeout.

Referenced by httpd_helper_thread().

00092 {
00093    ast_assert(stream != NULL);
00094 
00095    stream->start.tv_sec = 0;
00096    stream->timeout = timeout;
00097 }

void ast_tcptls_stream_set_timeout_sequence ( struct ast_tcptls_stream stream,
struct timeval  start,
int  timeout 
)

Set the TCP/TLS stream I/O sequence timeout timer.

Parameters:
stream TCP/TLS stream control data.
start Time the I/O sequence timer starts.
timeout Number 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 99 of file tcptls.c.

References ast_assert, ast_tcptls_stream::start, and ast_tcptls_stream::timeout.

Referenced by _sip_tcp_helper_thread(), and session_do().

00100 {
00101    ast_assert(stream != NULL);
00102 
00103    stream->start = start;
00104    stream->timeout = timeout;
00105 }

int ast_tls_read_conf ( struct ast_tls_config tls_cfg,
struct ast_tcptls_session_args tls_desc,
const char *  varname,
const char *  value 
)

Used to parse conf files containing tls/ssl options.

Definition at line 1072 of file tcptls.c.

References ast_clear_flag, ast_free, ast_log(), ast_parse_arg(), ast_set2_flag, ast_set_flag, AST_SSL_DONT_VERIFY_SERVER, AST_SSL_SSLV2_CLIENT, AST_SSL_SSLV3_CLIENT, AST_SSL_TLSV1_CLIENT, AST_SSL_VERIFY_CLIENT, ast_strdup, ast_true(), ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, ast_tls_config::flags, ast_tcptls_session_args::local_address, LOG_WARNING, PARSE_ADDR, and ast_tls_config::pvtfile.

Referenced by __ast_http_load(), __init_manager(), and reload_config().

01073 {
01074    if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) {
01075       tls_cfg->enabled = ast_true(value) ? 1 : 0;
01076    } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) {
01077       ast_free(tls_cfg->certfile);
01078       tls_cfg->certfile = ast_strdup(value);
01079    } else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) {
01080       ast_free(tls_cfg->pvtfile);
01081       tls_cfg->pvtfile = ast_strdup(value);
01082    } else if (!strcasecmp(varname, "tlscipher") || !strcasecmp(varname, "sslcipher")) {
01083       ast_free(tls_cfg->cipher);
01084       tls_cfg->cipher = ast_strdup(value);
01085    } else if (!strcasecmp(varname, "tlscafile")) {
01086       ast_free(tls_cfg->cafile);
01087       tls_cfg->cafile = ast_strdup(value);
01088    } else if (!strcasecmp(varname, "tlscapath") || !strcasecmp(varname, "tlscadir")) {
01089       ast_free(tls_cfg->capath);
01090       tls_cfg->capath = ast_strdup(value);
01091    } else if (!strcasecmp(varname, "tlsverifyclient")) {
01092       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_VERIFY_CLIENT);
01093    } else if (!strcasecmp(varname, "tlsdontverifyserver")) {
01094       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DONT_VERIFY_SERVER);
01095    } else if (!strcasecmp(varname, "tlsbindaddr") || !strcasecmp(varname, "sslbindaddr")) {
01096       if (ast_parse_arg(value, PARSE_ADDR, &tls_desc->local_address))
01097          ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value);
01098    } else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) {
01099       if (!strcasecmp(value, "tlsv1")) {
01100          ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
01101          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
01102          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
01103       } else if (!strcasecmp(value, "sslv3")) {
01104          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
01105          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
01106          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
01107       } else if (!strcasecmp(value, "sslv2")) {
01108          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
01109          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
01110          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
01111       }
01112    } else {
01113       return -1;
01114    }
01115 
01116    return 0;
01117 }

static void* handle_tcptls_connection ( void *  data  )  [static]

creates a FILE * from the fd passed by the accept thread. This operation is potentially expensive (certificate verification), so we do it in the child thread context.

Note:
must decrement ref count before returning NULL on error

Definition at line 560 of file tcptls.c.

References ao2_ref, ast_debug, ast_log(), AST_SSL_DONT_VERIFY_SERVER, AST_SSL_IGNORE_COMMON_NAME, AST_SSL_VERIFY_CLIENT, ast_tcptls_close_session_file(), ast_test_flag, ast_thread_inhibit_escalations(), ast_verb, ast_tcptls_session_instance::client, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, ast_tls_config::flags, ast_tcptls_session_args::hostname, LOG_ERROR, LOG_WARNING, name, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::ssl, ast_tls_config::ssl_ctx, str, ast_tcptls_session_instance::stream_cookie, tcptls_stream_alloc(), tcptls_stream_fopen(), ast_tcptls_session_args::tls_cfg, and ast_tcptls_session_args::worker_fn.

Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().

00561 {
00562    struct ast_tcptls_session_instance *tcptls_session = data;
00563 #ifdef DO_SSL
00564    int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept;
00565    int ret;
00566    char err[256];
00567 #endif
00568 
00569    /* TCP/TLS connections are associated with external protocols, and
00570     * should not be allowed to execute 'dangerous' functions. This may
00571     * need to be pushed down into the individual protocol handlers, but
00572     * this seems like a good general policy.
00573     */
00574    if (ast_thread_inhibit_escalations()) {
00575       ast_log(LOG_ERROR, "Failed to inhibit privilege escalations; killing connection\n");
00576       ast_tcptls_close_session_file(tcptls_session);
00577       ao2_ref(tcptls_session, -1);
00578       return NULL;
00579    }
00580 
00581    tcptls_session->stream_cookie = tcptls_stream_alloc();
00582    if (!tcptls_session->stream_cookie) {
00583       ast_tcptls_close_session_file(tcptls_session);
00584       ao2_ref(tcptls_session, -1);
00585       return NULL;
00586    }
00587 
00588    /*
00589    * open a FILE * as appropriate.
00590    */
00591    if (!tcptls_session->parent->tls_cfg) {
00592       tcptls_session->f = tcptls_stream_fopen(tcptls_session->stream_cookie, NULL,
00593          tcptls_session->fd, -1);
00594       if (tcptls_session->f) {
00595          if (setvbuf(tcptls_session->f, NULL, _IONBF, 0)) {
00596             ast_tcptls_close_session_file(tcptls_session);
00597          }
00598       }
00599    }
00600 #ifdef DO_SSL
00601    else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) {
00602       SSL_set_fd(tcptls_session->ssl, tcptls_session->fd);
00603       if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) {
00604          ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err));
00605       } else if ((tcptls_session->f = tcptls_stream_fopen(tcptls_session->stream_cookie,
00606          tcptls_session->ssl, tcptls_session->fd, -1))) {
00607          if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER))
00608             || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) {
00609             X509 *peer;
00610             long res;
00611             peer = SSL_get_peer_certificate(tcptls_session->ssl);
00612             if (!peer) {
00613                ast_log(LOG_ERROR, "No peer SSL certificate to verify\n");
00614                ast_tcptls_close_session_file(tcptls_session);
00615                ao2_ref(tcptls_session, -1);
00616                return NULL;
00617             }
00618 
00619             res = SSL_get_verify_result(tcptls_session->ssl);
00620             if (res != X509_V_OK) {
00621                ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res));
00622                X509_free(peer);
00623                ast_tcptls_close_session_file(tcptls_session);
00624                ao2_ref(tcptls_session, -1);
00625                return NULL;
00626             }
00627             if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
00628                ASN1_STRING *str;
00629                unsigned char *str2;
00630                X509_NAME *name = X509_get_subject_name(peer);
00631                int pos = -1;
00632                int found = 0;
00633 
00634                for (;;) {
00635                   /* Walk the certificate to check all available "Common Name" */
00636                   /* XXX Probably should do a gethostbyname on the hostname and compare that as well */
00637                   pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos);
00638                   if (pos < 0)
00639                      break;
00640                   str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
00641                   ret = ASN1_STRING_to_UTF8(&str2, str);
00642                   if (ret < 0) {
00643                      continue;
00644                   }
00645 
00646                   if (str2) {
00647                      if (strlen((char *) str2) != ret) {
00648                         ast_log(LOG_WARNING, "Invalid certificate common name length (contains NULL bytes?)\n");
00649                      } else if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) {
00650                         found = 1;
00651                      }
00652                      ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
00653                      OPENSSL_free(str2);
00654                   }
00655                   if (found)
00656                      break;
00657                }
00658                if (!found) {
00659                   ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
00660                   X509_free(peer);
00661                   ast_tcptls_close_session_file(tcptls_session);
00662                   ao2_ref(tcptls_session, -1);
00663                   return NULL;
00664                }
00665             }
00666             X509_free(peer);
00667          }
00668       }
00669       if (!tcptls_session->f) /* no success opening descriptor stacking */
00670          SSL_free(tcptls_session->ssl);
00671    }
00672 #endif /* DO_SSL */
00673 
00674    if (!tcptls_session->f) {
00675       ast_tcptls_close_session_file(tcptls_session);
00676       ast_log(LOG_WARNING, "FILE * open failed!\n");
00677 #ifndef DO_SSL
00678       if (tcptls_session->parent->tls_cfg) {
00679          ast_log(LOG_WARNING, "Attempted a TLS connection without OpenSSL support.  This will not work!\n");
00680       }
00681 #endif
00682       ao2_ref(tcptls_session, -1);
00683       return NULL;
00684    }
00685 
00686    if (tcptls_session->parent->worker_fn) {
00687       return tcptls_session->parent->worker_fn(tcptls_session);
00688    } else {
00689       return tcptls_session;
00690    }
00691 }

static void session_instance_destructor ( void *  obj  )  [static]

Definition at line 541 of file tcptls.c.

References ao2_t_ref, ast_free, ast_mutex_destroy, ast_tcptls_session_instance::lock, ast_tcptls_session_instance::overflow_buf, and ast_tcptls_session_instance::stream_cookie.

Referenced by ast_tcptls_client_create(), and ast_tcptls_server_root().

00542 {
00543    struct ast_tcptls_session_instance *i = obj;
00544 
00545    if (i->stream_cookie) {
00546       ao2_t_ref(i->stream_cookie, -1, "Destroying tcptls session instance");
00547       i->stream_cookie = NULL;
00548    }
00549    ast_free(i->overflow_buf);
00550    ast_mutex_destroy(&i->lock);
00551 }

static struct ast_tcptls_stream* tcptls_stream_alloc ( void   )  [static, read]

Definition at line 454 of file tcptls.c.

References ao2_alloc, ast_tcptls_stream::fd, tcptls_stream_dtor(), and ast_tcptls_stream::timeout.

Referenced by handle_tcptls_connection().

00455 {
00456    struct ast_tcptls_stream *stream;
00457 
00458    stream = ao2_alloc(sizeof(*stream), tcptls_stream_dtor);
00459    if (stream) {
00460       stream->fd = -1;
00461       stream->timeout = -1;
00462    }
00463    return stream;
00464 }

static int tcptls_stream_close ( void *  cookie  )  [static]

Definition at line 373 of file tcptls.c.

References ao2_t_ref, ast_log(), errno, ast_tcptls_stream::fd, LOG_ERROR, and ast_tcptls_stream::ssl.

Referenced by tcptls_stream_fopen().

00374 {
00375    struct ast_tcptls_stream *stream = cookie;
00376 
00377    if (!stream) {
00378       errno = EBADF;
00379       return -1;
00380    }
00381 
00382    if (stream->fd != -1) {
00383 #if defined(DO_SSL)
00384       if (stream->ssl) {
00385          int res;
00386 
00387          /*
00388           * According to the TLS standard, it is acceptable for an
00389           * application to only send its shutdown alert and then
00390           * close the underlying connection without waiting for
00391           * the peer's response (this way resources can be saved,
00392           * as the process can already terminate or serve another
00393           * connection).
00394           */
00395          res = SSL_shutdown(stream->ssl);
00396          if (res < 0) {
00397             ast_log(LOG_ERROR, "SSL_shutdown() failed: %d\n",
00398                SSL_get_error(stream->ssl, res));
00399          }
00400 
00401          if (!stream->ssl->server) {
00402             /* For client threads, ensure that the error stack is cleared */
00403             ERR_remove_state(0);
00404          }
00405 
00406          SSL_free(stream->ssl);
00407          stream->ssl = NULL;
00408       }
00409 #endif   /* defined(DO_SSL) */
00410 
00411       /*
00412        * Issuing shutdown() is necessary here to avoid a race
00413        * condition where the last data written may not appear
00414        * in the TCP stream.  See ASTERISK-23548
00415        */
00416       shutdown(stream->fd, SHUT_RDWR);
00417       if (close(stream->fd)) {
00418          ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
00419       }
00420       stream->fd = -1;
00421    }
00422    ao2_t_ref(stream, -1, "Closed tcptls stream cookie");
00423 
00424    return 0;
00425 }

static void tcptls_stream_dtor ( void *  cookie  )  [static]

Definition at line 435 of file tcptls.c.

References ast_assert, and ast_tcptls_stream::fd.

Referenced by tcptls_stream_alloc().

00436 {
00437 #ifdef AST_DEVMODE
00438    /* Since the ast_assert below is the only one using stream,
00439     * and ast_assert is only available with AST_DEVMODE, we
00440     * put this in a conditional to avoid compiler warnings. */
00441    struct ast_tcptls_stream *stream = cookie;
00442 #endif
00443 
00444    ast_assert(stream->fd == -1);
00445 }

static FILE* tcptls_stream_fopen ( struct ast_tcptls_stream stream,
SSL *  ssl,
int  fd,
int  timeout 
) [static]

Definition at line 478 of file tcptls.c.

References ao2_t_ref, ast_debug, ast_tcptls_stream::fd, ast_tcptls_stream::ssl, tcptls_stream_close(), tcptls_stream_read(), tcptls_stream_write(), and ast_tcptls_stream::timeout.

Referenced by handle_tcptls_connection().

00479 {
00480    FILE *fp;
00481 
00482 #if defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
00483    static const cookie_io_functions_t cookie_funcs = {
00484       tcptls_stream_read,
00485       tcptls_stream_write,
00486       NULL,
00487       tcptls_stream_close
00488    };
00489 #endif   /* defined(HAVE_FOPENCOOKIE) */
00490 
00491    if (fd == -1) {
00492       /* Socket not open. */
00493       return NULL;
00494    }
00495 
00496    stream->ssl = ssl;
00497    stream->fd = fd;
00498    stream->timeout = timeout;
00499    ao2_t_ref(stream, +1, "Opening tcptls stream cookie");
00500 
00501 #if defined(HAVE_FUNOPEN)  /* the BSD interface */
00502    fp = funopen(stream, tcptls_stream_read, tcptls_stream_write, NULL,
00503       tcptls_stream_close);
00504 #elif defined(HAVE_FOPENCOOKIE)  /* the glibc/linux interface */
00505    fp = fopencookie(stream, "w+", cookie_funcs);
00506 #else
00507    /* could add other methods here */
00508    ast_debug(2, "No stream FILE methods attempted!\n");
00509    fp = NULL;
00510 #endif
00511 
00512    if (!fp) {
00513       stream->fd = -1;
00514       ao2_t_ref(stream, -1, "Failed to open tcptls stream cookie");
00515    }
00516    return fp;
00517 }

static HOOK_T tcptls_stream_read ( void *  cookie,
char *  buf,
LEN_T  size 
) [static]

Definition at line 126 of file tcptls.c.

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

Referenced by ast_tcptls_server_read(), and tcptls_stream_fopen().

00127 {
00128    struct ast_tcptls_stream *stream = cookie;
00129    struct timeval start;
00130    int ms;
00131    int res;
00132 
00133    if (!size) {
00134       /* You asked for no data you got no data. */
00135       return 0;
00136    }
00137 
00138    if (!stream || stream->fd == -1) {
00139       errno = EBADF;
00140       return -1;
00141    }
00142 
00143    if (stream->start.tv_sec) {
00144       start = stream->start;
00145    } else {
00146       start = ast_tvnow();
00147    }
00148 
00149 #if defined(DO_SSL)
00150    if (stream->ssl) {
00151       for (;;) {
00152          res = SSL_read(stream->ssl, buf, size);
00153          if (0 < res) {
00154             /* We read some payload data. */
00155             return res;
00156          }
00157          switch (SSL_get_error(stream->ssl, res)) {
00158          case SSL_ERROR_ZERO_RETURN:
00159             /* Report EOF for a shutdown */
00160             ast_debug(1, "TLS clean shutdown alert reading data\n");
00161             return 0;
00162          case SSL_ERROR_WANT_READ:
00163             if (!stream->exclusive_input) {
00164                /* We cannot wait for data now. */
00165                errno = EAGAIN;
00166                return -1;
00167             }
00168             while ((ms = ast_remaining_ms(start, stream->timeout))) {
00169                res = ast_wait_for_input(stream->fd, ms);
00170                if (0 < res) {
00171                   /* Socket is ready to be read. */
00172                   break;
00173                }
00174                if (res < 0) {
00175                   if (errno == EINTR || errno == EAGAIN) {
00176                      /* Try again. */
00177                      continue;
00178                   }
00179                   ast_debug(1, "TLS socket error waiting for read data: %s\n",
00180                      strerror(errno));
00181                   return -1;
00182                }
00183             }
00184             break;
00185          case SSL_ERROR_WANT_WRITE:
00186             while ((ms = ast_remaining_ms(start, stream->timeout))) {
00187                res = ast_wait_for_output(stream->fd, ms);
00188                if (0 < res) {
00189                   /* Socket is ready to be written. */
00190                   break;
00191                }
00192                if (res < 0) {
00193                   if (errno == EINTR || errno == EAGAIN) {
00194                      /* Try again. */
00195                      continue;
00196                   }
00197                   ast_debug(1, "TLS socket error waiting for write space: %s\n",
00198                      strerror(errno));
00199                   return -1;
00200                }
00201             }
00202             break;
00203          default:
00204             /* Report EOF for an undecoded SSL or transport error. */
00205             ast_debug(1, "TLS transport or SSL error reading data\n");
00206             return 0;
00207          }
00208          if (!ms) {
00209             /* Report EOF for a timeout */
00210             ast_debug(1, "TLS timeout reading data\n");
00211             return 0;
00212          }
00213       }
00214    }
00215 #endif   /* defined(DO_SSL) */
00216 
00217    for (;;) {
00218       res = read(stream->fd, buf, size);
00219       if (0 <= res || !stream->exclusive_input) {
00220          /* Got data or we cannot wait for it. */
00221          return res;
00222       }
00223       if (errno != EINTR && errno != EAGAIN) {
00224          /* Not a retryable error. */
00225          ast_debug(1, "TCP socket error reading data: %s\n",
00226             strerror(errno));
00227          return -1;
00228       }
00229       ms = ast_remaining_ms(start, stream->timeout);
00230       if (!ms) {
00231          /* Report EOF for a timeout */
00232          ast_debug(1, "TCP timeout reading data\n");
00233          return 0;
00234       }
00235       ast_wait_for_input(stream->fd, ms);
00236    }
00237 }

static HOOK_T tcptls_stream_write ( void *  cookie,
const char *  buf,
LEN_T  size 
) [static]

Definition at line 250 of file tcptls.c.

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

Referenced by ast_tcptls_server_write(), and tcptls_stream_fopen().

00251 {
00252    struct ast_tcptls_stream *stream = cookie;
00253    struct timeval start;
00254    int ms;
00255    int res;
00256    int written;
00257    int remaining;
00258 
00259    if (!size) {
00260       /* You asked to write no data you wrote no data. */
00261       return 0;
00262    }
00263 
00264    if (!stream || stream->fd == -1) {
00265       errno = EBADF;
00266       return -1;
00267    }
00268 
00269    if (stream->start.tv_sec) {
00270       start = stream->start;
00271    } else {
00272       start = ast_tvnow();
00273    }
00274 
00275 #if defined(DO_SSL)
00276    if (stream->ssl) {
00277       written = 0;
00278       remaining = size;
00279       for (;;) {
00280          res = SSL_write(stream->ssl, buf + written, remaining);
00281          if (res == remaining) {
00282             /* Everything was written. */
00283             return size;
00284          }
00285          if (0 < res) {
00286             /* Successfully wrote part of the buffer.  Try to write the rest. */
00287             written += res;
00288             remaining -= res;
00289             continue;
00290          }
00291          switch (SSL_get_error(stream->ssl, res)) {
00292          case SSL_ERROR_ZERO_RETURN:
00293             ast_debug(1, "TLS clean shutdown alert writing data\n");
00294             if (written) {
00295                /* Report partial write. */
00296                return written;
00297             }
00298             errno = EBADF;
00299             return -1;
00300          case SSL_ERROR_WANT_READ:
00301             ms = ast_remaining_ms(start, stream->timeout);
00302             if (!ms) {
00303                /* Report partial write. */
00304                ast_debug(1, "TLS timeout writing data (want read)\n");
00305                return written;
00306             }
00307             ast_wait_for_input(stream->fd, ms);
00308             break;
00309          case SSL_ERROR_WANT_WRITE:
00310             ms = ast_remaining_ms(start, stream->timeout);
00311             if (!ms) {
00312                /* Report partial write. */
00313                ast_debug(1, "TLS timeout writing data (want write)\n");
00314                return written;
00315             }
00316             ast_wait_for_output(stream->fd, ms);
00317             break;
00318          default:
00319             /* Undecoded SSL or transport error. */
00320             ast_debug(1, "TLS transport or SSL error writing data\n");
00321             if (written) {
00322                /* Report partial write. */
00323                return written;
00324             }
00325             errno = EBADF;
00326             return -1;
00327          }
00328       }
00329    }
00330 #endif   /* defined(DO_SSL) */
00331 
00332    written = 0;
00333    remaining = size;
00334    for (;;) {
00335       res = write(stream->fd, buf + written, remaining);
00336       if (res == remaining) {
00337          /* Yay everything was written. */
00338          return size;
00339       }
00340       if (0 < res) {
00341          /* Successfully wrote part of the buffer.  Try to write the rest. */
00342          written += res;
00343          remaining -= res;
00344          continue;
00345       }
00346       if (errno != EINTR && errno != EAGAIN) {
00347          /* Not a retryable error. */
00348          ast_debug(1, "TCP socket error writing: %s\n", strerror(errno));
00349          if (written) {
00350             return written;
00351          }
00352          return -1;
00353       }
00354       ms = ast_remaining_ms(start, stream->timeout);
00355       if (!ms) {
00356          /* Report partial write. */
00357          ast_debug(1, "TCP timeout writing data\n");
00358          return written;
00359       }
00360       ast_wait_for_output(stream->fd, ms);
00361    }
00362 }


Generated on 17 Aug 2018 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1