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_stream * | tcptls_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) |
Code to support TCP and TLS server/client.
Definition in file tcptls.c.
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.
cfg | Configuration for the SSL server |
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
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().
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().
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.
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.
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. |
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.
stream | TCP/TLS stream control data. |
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.
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.
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.
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.
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.
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 }