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 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.
| cfg | Configuration for the SSL server |
| 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
| 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().
| 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().
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.
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.
| 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 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.
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 }
1.6.1