Wed Oct 29 05:05:22 2014

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"
Include dependency graph for tcptls.c:

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 738 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().

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

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 843 of file tcptls.c.

References __ssl_setup().

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

00844 {
00845    return __ssl_setup(cfg, 0);
00846 }

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 848 of file tcptls.c.

References ast_tls_config::ssl_ctx.

Referenced by sip_tcptls_client_args_destructor(), and unload_module().

00849 {
00850 #ifdef DO_SSL
00851    if (cfg->ssl_ctx) {
00852       SSL_CTX_free(cfg->ssl_ctx);
00853       cfg->ssl_ctx = NULL;
00854    }
00855 #endif
00856 }

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

Definition at line 895 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().

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

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 858 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().

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

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 1026 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().

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

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 686 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().

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

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 957 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().

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

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 1051 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().

01052 {
01053    if (desc->master != AST_PTHREADT_NULL) {
01054       pthread_cancel(desc->master);
01055       pthread_kill(desc->master, SIGURG);
01056       pthread_join(desc->master, NULL);
01057       desc->master = AST_PTHREADT_NULL;
01058    }
01059    if (desc->accept_fd != -1)
01060       close(desc->accept_fd);
01061    desc->accept_fd = -1;
01062    ast_debug(2, "Stopped server :: %s\n", desc->name);
01063 }

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 1065 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().

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

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                   ASN1_STRING_to_UTF8(&str2, str);
00642                   if (str2) {
00643                      if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2))
00644                         found = 1;
00645                      ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
00646                      OPENSSL_free(str2);
00647                   }
00648                   if (found)
00649                      break;
00650                }
00651                if (!found) {
00652                   ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
00653                   X509_free(peer);
00654                   ast_tcptls_close_session_file(tcptls_session);
00655                   ao2_ref(tcptls_session, -1);
00656                   return NULL;
00657                }
00658             }
00659             X509_free(peer);
00660          }
00661       }
00662       if (!tcptls_session->f) /* no success opening descriptor stacking */
00663          SSL_free(tcptls_session->ssl);
00664    }
00665 #endif /* DO_SSL */
00666 
00667    if (!tcptls_session->f) {
00668       ast_tcptls_close_session_file(tcptls_session);
00669       ast_log(LOG_WARNING, "FILE * open failed!\n");
00670 #ifndef DO_SSL
00671       if (tcptls_session->parent->tls_cfg) {
00672          ast_log(LOG_WARNING, "Attempted a TLS connection without OpenSSL support.  This will not work!\n");
00673       }
00674 #endif
00675       ao2_ref(tcptls_session, -1);
00676       return NULL;
00677    }
00678 
00679    if (tcptls_session->parent->worker_fn) {
00680       return tcptls_session->parent->worker_fn(tcptls_session);
00681    } else {
00682       return tcptls_session;
00683    }
00684 }

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 29 Oct 2014 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1