summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2012-04-02 08:30:58 +0000
committerBenny Prijono <bennylp@teluu.com>2012-04-02 08:30:58 +0000
commitf6fe35510762df40b7a6a8d410a0d428f545943c (patch)
treef39b5cab59e9f090ad72447af9f0d0bafe2a652e
parent51fb0e3e6e582a95c168792cc5889b63e3f6078e (diff)
Re #1417: removed the obsolete but misleading sip_transport_tls_ossl.c file
git-svn-id: http://svn.pjsip.org/repos/pjproject/branches/1.x@4004 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/src/pjmedia/jbuf.c19
-rw-r--r--pjsip/src/pjsip/sip_transport_tls_ossl.c2376
2 files changed, 16 insertions, 2379 deletions
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index db4491a2..4c7762d4 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -166,7 +166,7 @@ struct pjmedia_jbuf
/* Enabling this would log the jitter buffer state about once per
* second.
*/
-#if 1
+#if 0
# define TRACE__(args) PJ_LOG(5,args)
#else
# define TRACE__(args)
@@ -404,9 +404,13 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
if (index < framelist->origin) {
if (framelist->origin - index < MAX_MISORDER) {
/* too late */
+ PJ_LOG(5,(THIS_FILE, "frame too late (origin: %d, index: %d, size: %d, max: %d)",
+ framelist->origin, index, framelist->size, framelist->max_count));
return PJ_ETOOSMALL;
} else {
/* sequence restart */
+ PJ_LOG(5,(THIS_FILE, "sequence restart (origin: %d, index: %d, size: %d, max: %d)",
+ framelist->origin, index, framelist->size, framelist->max_count));
framelist->origin = index - framelist->size;
}
}
@@ -424,11 +428,15 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
if (distance >= (int)framelist->max_count) {
if (distance > MAX_DROPOUT) {
/* jump too far, reset the buffer */
+ PJ_LOG(5,(THIS_FILE, "jump too far, resetting (origin: %d, index: %d, size: %d, max: %d)",
+ framelist->origin, index, framelist->size, framelist->max_count));
jb_framelist_reset(framelist);
framelist->origin = index;
distance = 0;
} else {
/* otherwise, reject the frame */
+ PJ_LOG(5,(THIS_FILE, "frame too far (origin: %d, index: %d, size: %d, max: %d)",
+ framelist->origin, index, framelist->size, framelist->max_count));
return PJ_ETOOMANY;
}
}
@@ -437,8 +445,11 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
pos = (framelist->head + distance) % framelist->max_count;
/* if the slot is occupied, it must be duplicated frame, ignore it. */
- if (framelist->frame_type[pos] != PJMEDIA_JB_MISSING_FRAME)
+ if (framelist->frame_type[pos] != PJMEDIA_JB_MISSING_FRAME) {
+ PJ_LOG(5,(THIS_FILE, "exists (origin: %d, index: %d, size: %d, max: %d)",
+ framelist->origin, index, framelist->size, framelist->max_count));
return PJ_EEXISTS;
+ }
/* put the frame into the slot */
framelist->frame_type[pos] = frame_type;
@@ -517,7 +528,9 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool,
pj_math_stat_init(&jb->jb_delay);
pj_math_stat_init(&jb->jb_burst);
- pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE);
+ //pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE);
+ pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_STATIC);
+ //pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_NONE);
pjmedia_jbuf_reset(jb);
*p_jb = jb;
diff --git a/pjsip/src/pjsip/sip_transport_tls_ossl.c b/pjsip/src/pjsip/sip_transport_tls_ossl.c
deleted file mode 100644
index 7c979254..00000000
--- a/pjsip/src/pjsip/sip_transport_tls_ossl.c
+++ /dev/null
@@ -1,2376 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_transport_tls.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_errno.h>
-#include <pj/compat/socket.h>
-#include <pj/addr_resolv.h>
-#include <pj/assert.h>
-#include <pj/ioqueue.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/compat/socket.h>
-#include <pj/sock_select.h>
-#include <pj/string.h>
-
-
-/* Only build when PJSIP_HAS_TLS_TRANSPORT is enabled */
-#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
-
-
-
-/*
- * Include OpenSSL headers
- */
-#include <openssl/bio.h>
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-
-/*
- * With VisualC++, it's not possible to dynamically link against some
- * libraries when some macros are defined (unlike "make" based build
- * system where this can be easily manipulated).
- *
- * So for VisualC++ IDE, include OpenSSL libraries in the linking by
- * using the #pragma lib construct below.
- */
-#ifdef _MSC_VER
-# ifdef _DEBUG
-# pragma comment( lib, "libeay32MTd")
-# pragma comment( lib, "ssleay32MTd")
-#else
-# pragma comment( lib, "libeay32MT")
-# pragma comment( lib, "ssleay32MT")
-# endif
-#endif
-
-
-#define THIS_FILE "transport_tls_ossl.c"
-
-#define MAX_ASYNC_CNT 16
-#define POOL_LIS_INIT 512
-#define POOL_LIS_INC 512
-#define POOL_TP_INIT 512
-#define POOL_TP_INC 512
-
-
-
-/**
- * Get the number of descriptors in the set. This is defined in sock_select.c
- * This function will only return the number of sockets set from PJ_FD_SET
- * operation. When the set is modified by other means (such as by select()),
- * the count will not be reflected here.
- *
- * That's why don't export this function in the header file, to avoid
- * misunderstanding.
- *
- * @param fdsetp The descriptor set.
- *
- * @return Number of descriptors in the set.
- */
-PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp);
-
-
-
-struct tls_listener;
-struct tls_transport;
-
-
-/*
- * This structure is "descendant" of pj_ioqueue_op_key_t, and it is used to
- * track pending/asynchronous accept() operation. TLS transport may have
- * more than one pending accept() operations, depending on the value of
- * async_cnt.
- */
-struct pending_accept
-{
- pj_ioqueue_op_key_t op_key;
- struct tls_listener *listener;
- unsigned index;
- pj_pool_t *pool;
- pj_sock_t new_sock;
- int addr_len;
- pj_sockaddr_in local_addr;
- pj_sockaddr_in remote_addr;
-};
-
-
-/*
- * This is the TLS listener, which is a "descendant" of pjsip_tpfactory (the
- * SIP transport factory).
- */
-struct tls_listener
-{
- pjsip_tpfactory factory;
- pj_bool_t is_registered;
- pjsip_tls_setting setting;
- pjsip_endpoint *endpt;
- pjsip_tpmgr *tpmgr;
- pj_sock_t sock;
- pj_ioqueue_key_t *key;
- unsigned async_cnt;
- struct pending_accept *accept_op[MAX_ASYNC_CNT];
-
- SSL_CTX *ctx;
-};
-
-
-/*
- * This structure is used to keep delayed transmit operation in a list.
- * A delayed transmission occurs when application sends tx_data when
- * the TLS connect/establishment is still in progress. These delayed
- * transmission will be "flushed" once the socket is connected (either
- * successfully or with errors).
- */
-struct delayed_tdata
-{
- PJ_DECL_LIST_MEMBER(struct delayed_tdata);
- pjsip_tx_data_op_key *tdata_op_key;
-};
-
-
-/*
- * This structure describes the TLS transport, and it's descendant of
- * pjsip_transport.
- */
-struct tls_transport
-{
- pjsip_transport base;
- pj_bool_t is_server;
-
- /* Do not save listener instance in the transport, because
- * listener might be destroyed during transport's lifetime.
- * See http://trac.pjsip.org/repos/ticket/491
- struct tls_listener *listener;
- */
-
- /* TLS settings, copied from listener */
- struct {
- pj_str_t server_name;
- pj_time_val timeout;
- } setting;
-
- pj_bool_t is_registered;
- pj_bool_t is_closing;
- pj_status_t close_reason;
- pj_sock_t sock;
- pj_ioqueue_key_t *key;
- pj_bool_t has_pending_connect;
-
- /* SSL connection */
- SSL *ssl;
- pj_bool_t ssl_shutdown_called;
-
- /* Keep alive */
- pj_timer_entry ka_timer;
- pj_time_val last_activity;
-
- /* TLS transport can only have one rdata!
- * Otherwise chunks of incoming PDU may be received on different
- * buffer.
- */
- pjsip_rx_data rdata;
-
- /* Pending transmission list. */
- struct delayed_tdata delayed_list;
-};
-
-
-/****************************************************************************
- * PROTOTYPES
- */
-
-/* This callback is called when pending accept() operation completes. */
-static void on_accept_complete( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t sock,
- pj_status_t status);
-
-/* This callback is called by transport manager to destroy listener */
-static pj_status_t lis_destroy(pjsip_tpfactory *factory);
-
-/* This callback is called by transport manager to create transport */
-static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
- pjsip_tpmgr *mgr,
- pjsip_endpoint *endpt,
- const pj_sockaddr *rem_addr,
- int addr_len,
- pjsip_transport **transport);
-
-/* Common function to create and initialize transport */
-static pj_status_t tls_create(struct tls_listener *listener,
- pj_pool_t *pool,
- pj_sock_t sock, pj_bool_t is_server,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
- struct tls_transport **p_tls);
-
-
-/****************************************************************************
- * SSL FUNCTIONS
- */
-
-/* ssl_report_error() */
-static void ssl_report_error(const char *sender, int level,
- pj_status_t status,
- const char *format, ...)
-{
- va_list marker;
-
- va_start(marker, format);
-
-#if PJ_LOG_MAX_LEVEL > 0
- if (status != PJ_SUCCESS) {
- char err_format[PJ_ERR_MSG_SIZE + 512];
- int len;
-
- len = pj_ansi_snprintf(err_format, sizeof(err_format),
- "%s: ", format);
- pj_strerror(status, err_format+len, sizeof(err_format)-len);
-
- pj_log(sender, level, err_format, marker);
-
- } else {
- unsigned long ssl_err;
-
- ssl_err = ERR_get_error();
-
- if (ssl_err == 0) {
- pj_log(sender, level, format, marker);
- } else {
- char err_format[512];
- int len;
-
- len = pj_ansi_snprintf(err_format, sizeof(err_format),
- "%s: ", format);
- ERR_error_string(ssl_err, err_format+len);
-
- pj_log(sender, level, err_format, marker);
- }
- }
-#endif
-
- va_end(marker);
-}
-
-
-static void sockaddr_to_host_port( pj_pool_t *pool,
- pjsip_host_port *host_port,
- const pj_sockaddr_in *addr )
-{
- enum { M = 48 };
- host_port->host.ptr = (char*)pj_pool_alloc(pool, M);
- host_port->host.slen = pj_ansi_snprintf( host_port->host.ptr, M, "%s",
- pj_inet_ntoa(addr->sin_addr));
- host_port->port = pj_ntohs(addr->sin_port);
-}
-
-
-/* SSL password callback. */
-static int password_cb(char *buf, int num, int rwflag, void *user_data)
-{
- struct tls_listener *lis = (struct tls_listener*) user_data;
-
- PJ_UNUSED_ARG(rwflag);
-
- if(num < lis->setting.password.slen+1)
- return 0;
-
- pj_memcpy(buf, lis->setting.password.ptr, lis->setting.password.slen);
- return lis->setting.password.slen;
-}
-
-
-/* OpenSSL library initialization counter */
-static int openssl_init_count;
-
-/* Initialize OpenSSL */
-static pj_status_t init_openssl(void)
-{
- if (++openssl_init_count != 1)
- return PJ_SUCCESS;
-
- SSL_library_init();
- SSL_load_error_strings();
- OpenSSL_add_all_algorithms();
-
- return PJ_SUCCESS;
-}
-
-/* Shutdown OpenSSL */
-static void shutdown_openssl(void)
-{
- if (--openssl_init_count != 0)
- return;
-}
-
-
-/* Create and initialize new SSL context */
-static pj_status_t create_ctx( struct tls_listener *lis, SSL_CTX **p_ctx)
-{
- struct pjsip_tls_setting *opt = &lis->setting;
- int method;
- char *lis_name = lis->factory.obj_name;
- SSL_METHOD *ssl_method;
- SSL_CTX *ctx;
- int mode, rc;
-
- *p_ctx = NULL;
-
- /* Make sure OpenSSL library has been initialized */
- init_openssl();
-
- /* Determine SSL method to use */
- method = opt->method;
- if (method == PJSIP_SSL_UNSPECIFIED_METHOD)
- method = PJSIP_SSL_DEFAULT_METHOD;
-
- switch (method) {
- case PJSIP_SSLV23_METHOD:
- ssl_method = SSLv23_method();
- break;
- case PJSIP_TLSV1_METHOD:
- ssl_method = TLSv1_method();
- break;
- case PJSIP_SSLV2_METHOD:
- ssl_method = SSLv2_method();
- break;
- case PJSIP_SSLV3_METHOD:
- ssl_method = SSLv3_method();
- break;
- default:
- ssl_report_error(lis_name, 4, PJSIP_TLS_EINVMETHOD,
- "Error creating SSL context");
- return PJSIP_TLS_EINVMETHOD;
- }
-
- /* Create SSL context for the listener */
- ctx = SSL_CTX_new(ssl_method);
- if (ctx == NULL) {
- ssl_report_error(lis_name, 4, PJ_SUCCESS,
- "Error creating SSL context");
- return PJSIP_TLS_ECTX;
- }
-
-
- /* Load CA list if one is specified. */
- if (opt->ca_list_file.slen) {
-
- /* Can only take a NULL terminated filename in the setting */
- pj_assert(opt->ca_list_file.ptr[opt->ca_list_file.slen] == '\0');
-
- rc = SSL_CTX_load_verify_locations(ctx, opt->ca_list_file.ptr, NULL);
-
- if (rc != 1) {
- ssl_report_error(lis_name, 4, PJ_SUCCESS,
- "Error loading/verifying CA list file '%.*s'",
- (int)opt->ca_list_file.slen,
- opt->ca_list_file.ptr);
- SSL_CTX_free(ctx);
- return PJSIP_TLS_ECACERT;
- }
-
- PJ_LOG(5,(lis_name, "TLS CA file successfully loaded from '%.*s'",
- (int)opt->ca_list_file.slen,
- opt->ca_list_file.ptr));
- }
-
- /* Set password callback */
- SSL_CTX_set_default_passwd_cb(ctx, password_cb);
- SSL_CTX_set_default_passwd_cb_userdata(ctx, lis);
-
-
- /* Load certificate if one is specified */
- if (opt->cert_file.slen) {
-
- /* Can only take a NULL terminated filename in the setting */
- pj_assert(opt->cert_file.ptr[opt->cert_file.slen] == '\0');
-
- /* Load certificate chain from file into ctx */
- rc = SSL_CTX_use_certificate_chain_file(ctx, opt->cert_file.ptr);
-
- if(rc != 1) {
- ssl_report_error(lis_name, 4, PJ_SUCCESS,
- "Error loading certificate chain file '%.*s'",
- (int)opt->cert_file.slen,
- opt->cert_file.ptr);
- SSL_CTX_free(ctx);
- return PJSIP_TLS_ECERTFILE;
- }
-
- PJ_LOG(5,(lis_name, "TLS certificate successfully loaded from '%.*s'",
- (int)opt->cert_file.slen,
- opt->cert_file.ptr));
- }
-
-
- /* Load private key if one is specified */
- if (opt->privkey_file.slen) {
-
- /* Can only take a NULL terminated filename in the setting */
- pj_assert(opt->privkey_file.ptr[opt->privkey_file.slen] == '\0');
-
- /* Adds the first private key found in file to ctx */
- rc = SSL_CTX_use_PrivateKey_file(ctx, opt->privkey_file.ptr,
- SSL_FILETYPE_PEM);
-
- if(rc != 1) {
- ssl_report_error(lis_name, 4, PJ_SUCCESS,
- "Error adding private key from '%.*s'",
- (int)opt->privkey_file.slen,
- opt->privkey_file.ptr);
- SSL_CTX_free(ctx);
- return PJSIP_TLS_EKEYFILE;
- }
-
- PJ_LOG(5,(lis_name, "TLS private key successfully loaded from '%.*s'",
- (int)opt->privkey_file.slen,
- opt->privkey_file.ptr));
- }
-
-
- /* SSL verification options */
- if (lis->setting.verify_client || lis->setting.verify_server) {
- mode = SSL_VERIFY_PEER;
- if (lis->setting.require_client_cert)
- mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- } else {
- mode = SSL_VERIFY_NONE;
- }
-
- SSL_CTX_set_verify(ctx, mode, NULL);
-
- PJ_LOG(5,(lis_name, "TLS verification mode set to %d", mode));
-
- /* Optionally set cipher list if one is specified */
- if (opt->ciphers.slen) {
- /* Can only take a NULL terminated cipher list in the setting */
- pj_assert(opt->cert_file.ptr[opt->cert_file.slen] == '\0');
-
- rc = SSL_CTX_set_cipher_list(ctx, opt->ciphers.ptr);
- if (rc != 1) {
- ssl_report_error(lis_name, 4, PJ_SUCCESS,
- "Error setting cipher list '%.*s'",
- (int)opt->ciphers.slen,
- opt->ciphers.ptr);
- SSL_CTX_free(ctx);
- return PJSIP_TLS_ECIPHER;
- }
-
- PJ_LOG(5,(lis_name, "TLS ciphers set to '%.*s'",
- (int)opt->ciphers.slen,
- opt->ciphers.ptr));
- }
-
- /* Done! */
-
- *p_ctx = ctx;
- return PJ_SUCCESS;
-}
-
-/* Destroy SSL context */
-static void destroy_ctx(SSL_CTX *ctx)
-{
- SSL_CTX_free(ctx);
-
- /* Potentially shutdown OpenSSL library if this is the last
- * context exists.
- */
- shutdown_openssl();
-}
-
-/*
- * Perform SSL_connect upon completion of socket connect()
- */
-static pj_status_t ssl_connect(struct tls_transport *tls)
-{
- SSL *ssl = tls->ssl;
- int status;
-
- if (SSL_is_init_finished (ssl))
- return PJ_SUCCESS;
-
- if (SSL_get_fd(ssl) < 0)
- SSL_set_fd(ssl, (int)tls->sock);
-
- if (!SSL_in_connect_init(ssl))
- SSL_set_connect_state(ssl);
-
-#ifdef SSL_set_tlsext_host_name
- if (tls->setting.server_name.slen) {
- char server_name[PJ_MAX_HOSTNAME];
-
- if (tls->setting.server_name.slen >= PJ_MAX_HOSTNAME)
- return PJ_ENAMETOOLONG;
-
- pj_memcpy(server_name, tls->setting.server_name.ptr,
- tls->setting.server_name.slen);
- server_name[tls->setting.server_name.slen] = '\0';
-
- if (!SSL_set_tlsext_host_name(ssl, server_name)) {
- PJ_LOG(4,(tls->base.obj_name,
- "SSL_set_tlsext_host_name() failed"));
- }
- }
-#endif
-
- PJ_LOG(5,(tls->base.obj_name, "Starting SSL_connect() negotiation"));
-
- do {
- /* These handle sets are used to set up for whatever SSL_connect
- * says it wants next. They're reset on each pass around the loop.
- */
- pj_fd_set_t rd_set;
- pj_fd_set_t wr_set;
-
- PJ_FD_ZERO(&rd_set);
- PJ_FD_ZERO(&wr_set);
-
- status = SSL_connect (ssl);
- switch (SSL_get_error (ssl, status)) {
- case SSL_ERROR_NONE:
- /* Success */
- status = 0;
- PJ_LOG(5,(tls->base.obj_name,
- "SSL_connect() negotiation completes successfully"));
- break;
-
- case SSL_ERROR_WANT_WRITE:
- /* Wait for more activity */
- PJ_FD_SET(tls->sock, &wr_set);
- status = 1;
- break;
-
- case SSL_ERROR_WANT_READ:
- /* Wait for more activity */
- PJ_FD_SET(tls->sock, &rd_set);
- status = 1;
- break;
-
- case SSL_ERROR_ZERO_RETURN:
- /* The peer has notified us that it is shutting down via
- * the SSL "close_notify" message so we need to
- * shutdown, too.
- */
- PJ_LOG(4,(THIS_FILE, "SSL connect() failed, remote has"
- "shutdown connection."));
- return PJ_STATUS_FROM_OS(OSERR_ENOTCONN);
-
- case SSL_ERROR_SYSCALL:
- /* On some platforms (e.g. MS Windows) OpenSSL does not
- * store the last error in errno so explicitly do so.
- *
- * Explicitly check for EWOULDBLOCK since it doesn't get
- * converted to an SSL_ERROR_WANT_{READ,WRITE} on some
- * platforms. If SSL_connect failed outright, though, don't
- * bother checking more. This can happen if the socket gets
- * closed during the handshake.
- */
- if (pj_get_netos_error() == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
- status == -1)
- {
- /* Although the SSL_ERROR_WANT_READ/WRITE isn't getting
- * set correctly, the read/write state should be valid.
- * Use that to decide what to do.
- */
- status = 1; /* Wait for more activity */
- if (SSL_want_write (ssl))
- PJ_FD_SET(tls->sock, &wr_set);
- else if (SSL_want_read (ssl))
- PJ_FD_SET(tls->sock, &rd_set);
- else
- status = -1; /* Doesn't want anything - bail out */
- }
- else {
- status = -1;
- }
- break;
-
- default:
- ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS,
- "SSL_connect() error");
- status = -1;
- break;
- }
-
- if (status == 1) {
- pj_time_val timeout, *p_timeout;
-
- /* Must have at least one handle to wait for at this point. */
- pj_assert(PJ_FD_COUNT(&rd_set) == 1 || PJ_FD_COUNT(&wr_set) == 1);
-
- /* This will block the whole stack!!! */
- PJ_TODO(SUPPORT_SSL_ASYNCHRONOUS_CONNECT);
-
- if (tls->setting.timeout.sec == 0 &&
- tls->setting.timeout.msec == 0)
- {
- p_timeout = NULL;
- } else {
- timeout = tls->setting.timeout;
- p_timeout = &timeout;
- }
-
- /* Block indefinitely if timeout pointer is zero. */
- status = pj_sock_select(tls->sock+1, &rd_set, &wr_set,
- NULL, p_timeout);
-
- /* 0 is timeout, so we're done.
- * -1 is error, so we're done.
- * Could be both handles set (same handle in both masks) so set to 1.
- */
- if (status >= 1)
- status = 1;
- else if (status == 0)
- return PJSIP_TLS_ETIMEDOUT;
- else
- status = -1;
- }
-
- } while (status == 1 && !SSL_is_init_finished (ssl));
-
- return (status == -1 ? PJSIP_TLS_ECONNECT : PJ_SUCCESS);
-}
-
-
-/*
- * Perform SSL_accept() on the newly established incoming TLS connection.
- */
-static pj_status_t ssl_accept(struct tls_transport *tls)
-{
- SSL *ssl = tls->ssl;
- int rc;
-
- if (SSL_is_init_finished (ssl))
- return PJ_SUCCESS;
-
- if (!SSL_in_accept_init(ssl))
- SSL_set_accept_state(ssl);
-
- PJ_LOG(5,(tls->base.obj_name, "Starting SSL_accept() negotiation"));
-
- /* Repeat retrying SSL_accept() procedure until it completes either
- * successfully or with failure.
- */
- do {
-
- /* These handle sets are used to set up for whatever SSL_accept says
- * it wants next. They're reset on each pass around the loop.
- */
- pj_fd_set_t rd_set;
- pj_fd_set_t wr_set;
-
- PJ_FD_ZERO(&rd_set);
- PJ_FD_ZERO(&wr_set);
-
- rc = SSL_accept (ssl);
-
- switch (SSL_get_error (ssl, rc)) {
- case SSL_ERROR_NONE:
- /* Success! */
- rc = 0;
- PJ_LOG(5,(tls->base.obj_name,
- "SSL_accept() negotiation completes successfully"));
- break;
-
- case SSL_ERROR_WANT_WRITE:
- PJ_FD_SET(tls->sock, &wr_set);
- rc = 1; /* Wait for more activity */
- break;
-
- case SSL_ERROR_WANT_READ:
- PJ_FD_SET(tls->sock, &rd_set);
- rc = 1; /* Need to read more data */
- break;
-
- case SSL_ERROR_ZERO_RETURN:
- /* The peer has notified us that it is shutting down via the SSL
- * "close_notify" message so we need to shutdown, too.
- */
- PJ_LOG(4,(tls->base.obj_name,
- "Incoming SSL connection closed prematurely by client"));
- return PJ_STATUS_FROM_OS(OSERR_ENOTCONN);
-
- case SSL_ERROR_SYSCALL:
- /* Explicitly check for EWOULDBLOCK since it doesn't get converted
- * to an SSL_ERROR_WANT_{READ,WRITE} on some platforms.
- * If SSL_accept failed outright, though, don't bother checking
- * more. This can happen if the socket gets closed during the
- * handshake.
- */
- if (pj_get_netos_error()==PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)
- && rc==-1)
- {
- /* Although the SSL_ERROR_WANT_READ/WRITE isn't getting set
- * correctly, the read/write state should be valid. Use that
- * to decide what to do.
- */
- rc = 1; /* Wait for more activity */
- if (SSL_want_write(ssl))
- PJ_FD_SET(tls->sock, &wr_set);
- else if (SSL_want_read(ssl))
- PJ_FD_SET(tls->sock, &rd_set);
- else {
- /* Doesn't want anything - bail out */
- return PJ_STATUS_FROM_OS(OSERR_ENOTCONN);
- }
- }
- else {
- return PJSIP_TLS_EUNKNOWN;
- }
- break;
-
- default:
- ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS,
- "Error calling SSL_accept()");
- return pj_get_netos_error() ? pj_get_netos_error() :
- PJSIP_TLS_EUNKNOWN;
- }
-
- if (rc == 1) {
-
- pj_time_val timeout, *p_timeout;
-
- /* Must have at least one handle to wait for at this point. */
- pj_assert(PJ_FD_COUNT(&rd_set) == 1 || PJ_FD_COUNT(&wr_set) == 1);
-
- if (tls->setting.timeout.sec == 0 &&
- tls->setting.timeout.msec == 0)
- {
- p_timeout = NULL;
- } else {
- timeout = tls->setting.timeout;
- p_timeout = &timeout;
- }
-
- rc = pj_sock_select(tls->sock+1, &rd_set, &wr_set, NULL,
- p_timeout);
-
- if (rc >= 1)
- rc = 1;
- else if (rc == 0)
- return PJSIP_TLS_ETIMEDOUT;
- else
- return pj_get_netos_error();
- }
-
- } while (rc == 1 && !SSL_is_init_finished(ssl));
-
- return (rc == -1 ? PJSIP_TLS_EUNKNOWN : PJ_SUCCESS);
-}
-
-
-/* Send outgoing data with SSL connection */
-static pj_status_t ssl_write_bytes(struct tls_transport *tls,
- const void *data,
- int size,
- const char *data_name)
-{
- int sent = 0;
-
- do {
- const int fragment_sent = SSL_write(tls->ssl,
- ((pj_uint8_t*)data) + sent,
- size - sent);
-
- switch( SSL_get_error(tls->ssl, fragment_sent)) {
- case SSL_ERROR_NONE:
- sent += fragment_sent;
- break;
-
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- /* For now, we can't handle situation where WANT_READ/WANT_WRITE
- * is raised after some data has been sent, since we don't have
- * mechanism to keep track of how many bytes have been sent
- * inside the individual tdata.
- */
- pj_assert(sent == 0);
- PJ_TODO(PARTIAL_SSL_SENT);
- return PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK);
-
- case SSL_ERROR_ZERO_RETURN:
- /* The peer has notified us that it is shutting down via the SSL
- * "close_notify" message. Tell the transport manager that it
- * shouldn't use this transport any more and return ENOTCONN
- * to caller.
- */
-
- /* It is safe to call this multiple times. */
- pjsip_transport_shutdown(&tls->base);
-
- return PJ_STATUS_FROM_OS(OSERR_ENOTCONN);
-
- case SSL_ERROR_SYSCALL:
- if (fragment_sent == 0) {
- /* An EOF occured but the SSL "close_notify" message was not
- * sent. Shutdown the transport and return ENOTCONN.
- */
-
- /* It is safe to call this multiple times. */
- pjsip_transport_shutdown(&tls->base);
-
- return PJ_STATUS_FROM_OS(OSERR_ENOTCONN);
- }
-
- /* Other error */
- return pj_get_netos_error();
-
- default:
- ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS,
- "Error sending %s with SSL_write()",
- data_name);
- return pj_get_netos_error() ? pj_get_netos_error()
- : PJSIP_TLS_ESEND;
- }
-
- } while (sent < size);
-
- return PJ_SUCCESS;
-
-}
-
-/* Send outgoing tdata with SSL connection */
-static pj_status_t ssl_write(struct tls_transport *tls,
- pjsip_tx_data *tdata)
-{
- return ssl_write_bytes(tls, tdata->buf.start,
- tdata->buf.cur - tdata->buf.start,
- pjsip_tx_data_get_info(tdata));
-}
-
-
-/* Read data from SSL connection */
-static pj_status_t ssl_read(struct tls_transport *tls)
-{
- pjsip_rx_data *rdata = &tls->rdata;
-
- int bytes_read, max_size;
-
- max_size = sizeof(rdata->pkt_info.packet) - rdata->pkt_info.len;
- bytes_read = SSL_read(tls->ssl,
- rdata->pkt_info.packet+rdata->pkt_info.len,
- max_size);
-
- switch (SSL_get_error(tls->ssl, bytes_read)) {
- case SSL_ERROR_NONE:
- /* Data successfully read */
- rdata->pkt_info.len += bytes_read;
- return PJ_SUCCESS;
-
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- return PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK);
-
- case SSL_ERROR_ZERO_RETURN:
- /* The peer has notified us that it is shutting down via the SSL
- * "close_notify" message.
- */
- pjsip_transport_shutdown(&tls->base);
- return PJ_STATUS_FROM_OS(OSERR_ENOTCONN);
-
- case SSL_ERROR_SYSCALL:
- if (bytes_read == 0) {
- /* An EOF occured but the SSL "close_notify" message was not
- * sent.
- */
- pjsip_transport_shutdown(&tls->base);
- return PJ_STATUS_FROM_OS(OSERR_ENOTCONN);
- }
-
- /* Other error */
- return pj_get_netos_error();
-
- default:
- ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS,
- "Error reading data with SSL_read()");
- return pj_get_netos_error() ? pj_get_netos_error()
- : PJSIP_TLS_EREAD;
- }
-
- /* Should not reach here */
-}
-
-
-/****************************************************************************
- * The TLS listener/transport factory.
- */
-
-/*
- * This is the public API to create, initialize, register, and start the
- * TLS listener.
- */
-PJ_DEF(pj_status_t) pjsip_tls_transport_start( pjsip_endpoint *endpt,
- const pjsip_tls_setting *opt,
- const pj_sockaddr_in *local,
- const pjsip_host_port *a_name,
- unsigned async_cnt,
- pjsip_tpfactory **p_factory)
-{
- pj_pool_t *pool;
- struct tls_listener *listener;
- pj_ioqueue_callback listener_cb;
- pj_sockaddr_in *listener_addr;
- int addr_len;
- unsigned i;
- pj_status_t status;
-
- /* Sanity check */
- PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);
-
- /* Verify that address given in a_name (if any) is valid */
- if (a_name && a_name->host.slen) {
- pj_sockaddr_in tmp;
-
- status = pj_sockaddr_in_init(&tmp, &a_name->host,
- (pj_uint16_t)a_name->port);
- if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY ||
- tmp.sin_addr.s_addr == PJ_INADDR_NONE)
- {
- /* Invalid address */
- return PJ_EINVAL;
- }
- }
-
- pool = pjsip_endpt_create_pool(endpt, "tlslis", POOL_LIS_INIT,
- POOL_LIS_INC);
- PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
-
-
- listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener);
- listener->factory.pool = pool;
- listener->factory.type = PJSIP_TRANSPORT_TLS;
- listener->factory.type_name = "tls";
- listener->factory.flag =
- pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS);
- listener->sock = PJ_INVALID_SOCKET;
-
- /* Create object name */
- pj_ansi_snprintf(listener->factory.obj_name,
- sizeof(listener->factory.obj_name),
- "tls%p", listener);
-
- /* Create duplicate of TLS settings */
- if (opt)
- pjsip_tls_setting_copy(pool, &listener->setting, opt);
- else
- pjsip_tls_setting_default(&listener->setting);
-
- /* Initialize SSL context to be used by this listener */
- status = create_ctx(listener, &listener->ctx);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- status = pj_lock_create_recursive_mutex(pool, "tlslis",
- &listener->factory.lock);
- if (status != PJ_SUCCESS)
- goto on_error;
-
-
- /* Create and bind socket */
- status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &listener->sock);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr;
- if (local) {
- pj_memcpy(listener_addr, local, sizeof(pj_sockaddr_in));
- } else {
- pj_sockaddr_in_init(listener_addr, NULL, 0);
- }
-
- status = pj_sock_bind(listener->sock, listener_addr,
- sizeof(pj_sockaddr_in));
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Retrieve the bound address */
- addr_len = sizeof(pj_sockaddr_in);
- status = pj_sock_getsockname(listener->sock, listener_addr, &addr_len);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* If published host/IP is specified, then use that address as the
- * listener advertised address.
- */
- if (a_name && a_name->host.slen) {
- /* Copy the address */
- listener->factory.addr_name = *a_name;
- pj_strdup(listener->factory.pool, &listener->factory.addr_name.host,
- &a_name->host);
- listener->factory.addr_name.port = a_name->port;
-
- } else {
- /* No published address is given, use the bound address */
-
- /* If the address returns 0.0.0.0, use the default
- * interface address as the transport's address.
- */
- if (listener_addr->sin_addr.s_addr == 0) {
- pj_sockaddr hostip;
-
- status = pj_gethostip(pj_AF_INET(), &hostip);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- listener_addr->sin_addr = hostip.ipv4.sin_addr;
- }
-
- /* Save the address name */
- sockaddr_to_host_port(listener->factory.pool,
- &listener->factory.addr_name, listener_addr);
- }
-
- /* If port is zero, get the bound port */
- if (listener->factory.addr_name.port == 0) {
- listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port);
- }
-
- /* Start listening to the address */
- status = pj_sock_listen(listener->sock, PJSIP_TLS_TRANSPORT_BACKLOG);
- if (status != PJ_SUCCESS)
- goto on_error;
-
-
- /* Register socket to ioqeuue */
- pj_bzero(&listener_cb, sizeof(listener_cb));
- listener_cb.on_accept_complete = &on_accept_complete;
- status = pj_ioqueue_register_sock(pool, pjsip_endpt_get_ioqueue(endpt),
- listener->sock, listener,
- &listener_cb, &listener->key);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Register to transport manager */
- listener->endpt = endpt;
- listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
- listener->factory.create_transport = lis_create_transport;
- listener->factory.destroy = lis_destroy;
- listener->is_registered = PJ_TRUE;
- status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
- &listener->factory);
- if (status != PJ_SUCCESS) {
- listener->is_registered = PJ_FALSE;
- goto on_error;
- }
-
-
- /* Start pending accept() operations */
- if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT;
- listener->async_cnt = async_cnt;
-
- for (i=0; i<async_cnt; ++i) {
- pj_pool_t *pool;
-
- pool = pjsip_endpt_create_pool(endpt, "tlss%p", POOL_TP_INIT,
- POOL_TP_INIT);
- if (!pool) {
- status = PJ_ENOMEM;
- goto on_error;
- }
-
- listener->accept_op[i] = PJ_POOL_ZALLOC_T(pool, struct pending_accept);
- pj_ioqueue_op_key_init(&listener->accept_op[i]->op_key,
- sizeof(listener->accept_op[i]->op_key));
- listener->accept_op[i]->pool = pool;
- listener->accept_op[i]->listener = listener;
- listener->accept_op[i]->index = i;
-
- on_accept_complete(listener->key, &listener->accept_op[i]->op_key,
- listener->sock, PJ_EPENDING);
- }
-
- PJ_LOG(4,(listener->factory.obj_name,
- "SIP TLS listener ready for incoming connections at %.*s:%d",
- (int)listener->factory.addr_name.host.slen,
- listener->factory.addr_name.host.ptr,
- listener->factory.addr_name.port));
-
- /* Return the pointer to user */
- if (p_factory) *p_factory = &listener->factory;
-
- return PJ_SUCCESS;
-
-on_error:
- lis_destroy(&listener->factory);
- return status;
-}
-
-
-/* This callback is called by transport manager to destroy listener */
-static pj_status_t lis_destroy(pjsip_tpfactory *factory)
-{
- struct tls_listener *listener = (struct tls_listener *)factory;
- unsigned i;
-
- if (listener->is_registered) {
- pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory);
- listener->is_registered = PJ_FALSE;
- }
-
- if (listener->key) {
- pj_ioqueue_unregister(listener->key);
- listener->key = NULL;
- listener->sock = PJ_INVALID_SOCKET;
- }
-
- if (listener->sock != PJ_INVALID_SOCKET) {
- pj_sock_close(listener->sock);
- listener->sock = PJ_INVALID_SOCKET;
- }
-
- if (listener->factory.lock) {
- pj_lock_destroy(listener->factory.lock);
- listener->factory.lock = NULL;
- }
-
- for (i=0; i<PJ_ARRAY_SIZE(listener->accept_op); ++i) {
- if (listener->accept_op[i] && listener->accept_op[i]->pool) {
- pj_pool_t *pool = listener->accept_op[i]->pool;
- listener->accept_op[i]->pool = NULL;
- pj_pool_release(pool);
- }
- }
-
- if (listener->ctx) {
- destroy_ctx(listener->ctx);
- listener->ctx = NULL;
- }
-
- if (listener->factory.pool) {
- pj_pool_t *pool = listener->factory.pool;
-
- PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener destroyed"));
-
- listener->factory.pool = NULL;
- pj_pool_release(pool);
- }
-
- return PJ_SUCCESS;
-}
-
-
-/***************************************************************************/
-/*
- * TLS Transport
- */
-
-/*
- * Prototypes.
- */
-/* Called by transport manager to send message */
-static pj_status_t tls_send_msg(pjsip_transport *transport,
- pjsip_tx_data *tdata,
- const pj_sockaddr_t *rem_addr,
- int addr_len,
- void *token,
- void (*callback)(pjsip_transport *transport,
- void *token,
- pj_ssize_t sent_bytes));
-
-/* Called by transport manager to shutdown */
-static pj_status_t tls_shutdown(pjsip_transport *transport);
-
-/* Called by transport manager to destroy transport */
-static pj_status_t tls_destroy_transport(pjsip_transport *transport);
-
-/* Utility to destroy transport */
-static pj_status_t tls_destroy(pjsip_transport *transport,
- pj_status_t reason);
-
-/* Callback from ioqueue on incoming packet */
-static void on_read_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read);
-
-/* Callback from ioqueue when packet is sent */
-static void on_write_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent);
-
-/* Callback from ioqueue when connect completes */
-static void on_connect_complete(pj_ioqueue_key_t *key,
- pj_status_t status);
-
-/* TLS keep-alive timer callback */
-static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e);
-
-/*
- * Common function to create TLS transport, called when pending accept() and
- * pending connect() complete.
- */
-static pj_status_t tls_create( struct tls_listener *listener,
- pj_pool_t *pool,
- pj_sock_t sock, pj_bool_t is_server,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
- struct tls_transport **p_tls)
-{
- struct tls_transport *tls;
- pj_ioqueue_t *ioqueue;
- pj_ioqueue_callback tls_callback;
- int rc;
- pj_status_t status;
-
-
- PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL);
-
-
- if (pool == NULL) {
- pool = pjsip_endpt_create_pool(listener->endpt, "tls",
- POOL_TP_INIT, POOL_TP_INC);
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
- }
-
- /*
- * Create and initialize basic transport structure.
- */
- tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport);
- tls->sock = sock;
- tls->is_server = is_server;
- /*tls->listener = listener;*/
- pj_list_init(&tls->delayed_list);
- tls->base.pool = pool;
- tls->setting.timeout = listener->setting.timeout;
- pj_strdup(pool, &tls->setting.server_name,
- &listener->setting.server_name);
-
- pj_ansi_snprintf(tls->base.obj_name, PJ_MAX_OBJ_NAME,
- (is_server ? "tlss%p" :"tlsc%p"), tls);
-
- /* Initialize transport reference counter to 1 */
- status = pj_atomic_create(pool, 0, &tls->base.ref_cnt);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- status = pj_lock_create_recursive_mutex(pool, "tls", &tls->base.lock);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- tls->base.key.type = PJSIP_TRANSPORT_TLS;
- pj_memcpy(&tls->base.key.rem_addr, remote, sizeof(pj_sockaddr_in));
- tls->base.type_name = "tls";
- tls->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS);
-
- tls->base.info = (char*) pj_pool_alloc(pool, 64);
- pj_ansi_snprintf(tls->base.info, 64, "TLS to %s:%d",
- pj_inet_ntoa(remote->sin_addr),
- (int)pj_ntohs(remote->sin_port));
-
- tls->base.addr_len = sizeof(pj_sockaddr_in);
- pj_memcpy(&tls->base.local_addr, local, sizeof(pj_sockaddr_in));
- sockaddr_to_host_port(pool, &tls->base.local_name, local);
- sockaddr_to_host_port(pool, &tls->base.remote_name, remote);
-
- tls->base.endpt = listener->endpt;
- tls->base.tpmgr = listener->tpmgr;
- tls->base.send_msg = &tls_send_msg;
- tls->base.do_shutdown = &tls_shutdown;
- tls->base.destroy = &tls_destroy_transport;
-
- /* Create SSL connection object */
- tls->ssl = SSL_new(listener->ctx);
- if (tls->ssl == NULL) {
- ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS,
- "Error creating SSL connection object");
- status = PJSIP_TLS_ESSLCONN;
- goto on_error;
- }
-
- /* Associate network socket with SSL connection object */
- rc = SSL_set_fd(tls->ssl, (int)sock);
- if (rc != 1) {
- ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS,
- "Error calling SSL_set_fd");
- status = PJSIP_TLS_ESSLCONN;
- goto on_error;
- }
-
- /* Register socket to ioqueue */
- pj_bzero(&tls_callback, sizeof(pj_ioqueue_callback));
- tls_callback.on_read_complete = &on_read_complete;
- tls_callback.on_write_complete = &on_write_complete;
- tls_callback.on_connect_complete = &on_connect_complete;
-
- ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
- status = pj_ioqueue_register_sock(pool, ioqueue, sock,
- tls, &tls_callback, &tls->key);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Register transport to transport manager */
- status = pjsip_transport_register(listener->tpmgr, &tls->base);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- tls->is_registered = PJ_TRUE;
-
- /* Initialize keep-alive timer */
- tls->ka_timer.user_data = (void*) tls;
- tls->ka_timer.cb = &tls_keep_alive_timer;
-
-
- /* Done setting up basic transport. */
- *p_tls = tls;
-
- PJ_LOG(4,(tls->base.obj_name, "TLS %s transport created",
- (tls->is_server ? "server" : "client")));
-
- return PJ_SUCCESS;
-
-on_error:
- tls_destroy(&tls->base, status);
- return status;
-}
-
-
-/* Flush all delayed transmision once the socket is connected.
- * Return non-zero if pending transmission list is empty after
- * the function returns.
- */
-static pj_bool_t tls_flush_pending_tx(struct tls_transport *tls)
-{
- pj_bool_t empty;
-
- pj_lock_acquire(tls->base.lock);
- while (!pj_list_empty(&tls->delayed_list)) {
- struct delayed_tdata *pending_tx;
- pjsip_tx_data *tdata;
- pj_ioqueue_op_key_t *op_key;
- pj_ssize_t size;
- pj_status_t status;
-
- pending_tx = tls->delayed_list.next;
-
- tdata = pending_tx->tdata_op_key->tdata;
- op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
-
- /* send the txdata */
- status = ssl_write(tls, tdata);
-
- /* On EWOULDBLOCK, suspend further transmissions */
- if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
- break;
- }
-
- /* tdata has been transmitted (successfully or with failure).
- * In any case, remove it from pending transmission list.
- */
- pj_list_erase(pending_tx);
-
- /* Notify callback */
- if (status == PJ_SUCCESS)
- size = tdata->buf.cur - tdata->buf.start;
- else
- size = -status;
-
- on_write_complete(tls->key, op_key, size);
-
- }
-
- empty = pj_list_empty(&tls->delayed_list);
-
- pj_lock_release(tls->base.lock);
-
- return empty;
-}
-
-
-/* Called by transport manager to destroy transport */
-static pj_status_t tls_destroy_transport(pjsip_transport *transport)
-{
- struct tls_transport *tls = (struct tls_transport*)transport;
-
- /* Transport would have been unregistered by now since this callback
- * is called by transport manager.
- */
- tls->is_registered = PJ_FALSE;
-
- return tls_destroy(transport, tls->close_reason);
-}
-
-
-/* Destroy TLS transport */
-static pj_status_t tls_destroy(pjsip_transport *transport,
- pj_status_t reason)
-{
- struct tls_transport *tls = (struct tls_transport*)transport;
-
- if (tls->close_reason == 0)
- tls->close_reason = reason;
-
- if (tls->is_registered) {
- tls->is_registered = PJ_FALSE;
- pjsip_transport_destroy(transport);
-
- /* pjsip_transport_destroy will recursively call this function
- * again.
- */
- return PJ_SUCCESS;
- }
-
- /* Mark transport as closing */
- ++tls->is_closing;
-
- /* Stop keep-alive timer. */
- if (tls->ka_timer.id) {
- pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer);
- tls->ka_timer.id = PJ_FALSE;
- }
-
- /* Cancel all delayed transmits */
- while (!pj_list_empty(&tls->delayed_list)) {
- struct delayed_tdata *pending_tx;
- pj_ioqueue_op_key_t *op_key;
-
- pending_tx = tls->delayed_list.next;
- pj_list_erase(pending_tx);
-
- op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
-
- on_write_complete(tls->key, op_key, -reason);
- }
-
- if (tls->rdata.tp_info.pool) {
- pj_pool_release(tls->rdata.tp_info.pool);
- tls->rdata.tp_info.pool = NULL;
- }
-
- if (tls->key) {
- pj_ioqueue_unregister(tls->key);
- tls->key = NULL;
- tls->sock = PJ_INVALID_SOCKET;
- }
-
- if (tls->sock != PJ_INVALID_SOCKET) {
- pj_sock_close(tls->sock);
- tls->sock = PJ_INVALID_SOCKET;
- }
-
- if (tls->base.lock) {
- pj_lock_destroy(tls->base.lock);
- tls->base.lock = NULL;
- }
-
- if (tls->base.ref_cnt) {
- pj_atomic_destroy(tls->base.ref_cnt);
- tls->base.ref_cnt = NULL;
- }
-
- if (tls->ssl) {
- SSL_free(tls->ssl);
- tls->ssl = NULL;
- }
-
- if (tls->base.pool) {
- pj_pool_t *pool;
-
- if (reason != PJ_SUCCESS) {
- char errmsg[PJ_ERR_MSG_SIZE];
-
- pj_strerror(reason, errmsg, sizeof(errmsg));
- PJ_LOG(4,(tls->base.obj_name,
- "TLS transport destroyed with reason %d: %s",
- reason, errmsg));
-
- } else {
-
- PJ_LOG(4,(tls->base.obj_name,
- "TLS transport destroyed normally"));
-
- }
-
- pool = tls->base.pool;
- tls->base.pool = NULL;
- pj_pool_release(pool);
- }
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * This utility function creates receive data buffers and start
- * asynchronous recv() operations from the socket. It is called after
- * accept() or connect() operation complete.
- */
-static pj_status_t tls_start_read(struct tls_transport *tls)
-{
- pj_pool_t *pool;
- pj_ssize_t size;
- pj_sockaddr_in *rem_addr;
- pj_status_t status;
-
- /* Init rdata */
- pool = pjsip_endpt_create_pool(tls->base.endpt,
- "rtd%p",
- PJSIP_POOL_RDATA_LEN,
- PJSIP_POOL_RDATA_INC);
- if (!pool) {
- ssl_report_error(tls->base.obj_name, 4, PJ_ENOMEM,
- "Unable to create pool for listener rxdata");
- return PJ_ENOMEM;
- }
-
- tls->rdata.tp_info.pool = pool;
-
- tls->rdata.tp_info.transport = &tls->base;
- tls->rdata.tp_info.tp_data = tls;
- tls->rdata.tp_info.op_key.rdata = &tls->rdata;
- pj_ioqueue_op_key_init(&tls->rdata.tp_info.op_key.op_key,
- sizeof(pj_ioqueue_op_key_t));
-
- tls->rdata.pkt_info.src_addr = tls->base.key.rem_addr;
- tls->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in);
- rem_addr = (pj_sockaddr_in*) &tls->base.key.rem_addr;
- pj_ansi_strcpy(tls->rdata.pkt_info.src_name,
- pj_inet_ntoa(rem_addr->sin_addr));
- tls->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port);
-
- /* Here's the real trick with OpenSSL.
- * Since asynchronous socket operation with OpenSSL uses select() like
- * mechanism, it's not really compatible with PJLIB's ioqueue. So to
- * make them "talk" together, we simulate select() by using MSG_PEEK
- * when we call pj_ioqueue_recv().
- */
- size = 1;
- status = pj_ioqueue_recv(tls->key, &tls->rdata.tp_info.op_key.op_key,
- tls->rdata.pkt_info.packet, &size,
- PJ_IOQUEUE_ALWAYS_ASYNC | PJ_MSG_PEEK);
- if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- ssl_report_error(tls->base.obj_name, 4, status,
- "ioqueue_recv() error");
- return status;
- }
-
- return PJ_SUCCESS;
-}
-
-
-
-/* This callback is called by transport manager for the TLS factory
- * to create outgoing transport to the specified destination.
- */
-static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
- pjsip_tpmgr *mgr,
- pjsip_endpoint *endpt,
- const pj_sockaddr *rem_addr,
- int addr_len,
- pjsip_transport **p_transport)
-{
- struct tls_listener *listener;
- struct tls_transport *tls;
- pj_sock_t sock;
- pj_sockaddr_in local_addr;
- pj_status_t status;
-
- /* Sanity checks */
- PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr &&
- addr_len && p_transport, PJ_EINVAL);
-
- /* Check that address is a sockaddr_in */
- PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() &&
- addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL);
-
-
- listener = (struct tls_listener*)factory;
-
-
- /* Create socket */
- status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Bind to any port */
- status = pj_sock_bind_in(sock, 0, 0);
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock);
- return status;
- }
-
- /* Get the local port */
- addr_len = sizeof(pj_sockaddr_in);
- status = pj_sock_getsockname(sock, &local_addr, &addr_len);
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock);
- return status;
- }
-
- /* Initially set the address from the listener's address */
- local_addr.sin_addr.s_addr =
- ((pj_sockaddr_in*)&listener->factory.local_addr)->sin_addr.s_addr;
-
- /* Create the transport descriptor */
- status = tls_create(listener, NULL, sock, PJ_FALSE, &local_addr,
- (pj_sockaddr_in*)rem_addr, &tls);
- if (status != PJ_SUCCESS)
- return status;
-
-
- /* Start asynchronous connect() operation */
- tls->has_pending_connect = PJ_TRUE;
- status = pj_ioqueue_connect(tls->key, rem_addr, sizeof(pj_sockaddr_in));
- if (status == PJ_SUCCESS) {
-
- /* Immediate socket connect() ! */
- tls->has_pending_connect = PJ_FALSE;
-
- /* Perform SSL_connect() */
- status = ssl_connect(tls);
- if (status != PJ_SUCCESS) {
- tls_destroy(&tls->base, status);
- return status;
- }
-
- } else if (status != PJ_EPENDING) {
- tls_destroy(&tls->base, status);
- return status;
- }
-
- /* Update (again) local address, just in case local address currently
- * set is different now that asynchronous connect() is started.
- */
- addr_len = sizeof(pj_sockaddr_in);
- if (pj_sock_getsockname(tls->sock, &local_addr, &addr_len)==PJ_SUCCESS) {
- pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tls->base.local_addr;
-
- /* Some systems (like old Win32 perhaps) may not set local address
- * properly before socket is fully connected.
- */
- if (tp_addr->sin_addr.s_addr != local_addr.sin_addr.s_addr &&
- local_addr.sin_addr.s_addr != 0)
- {
- tp_addr->sin_addr.s_addr = local_addr.sin_addr.s_addr;
- tp_addr->sin_port = local_addr.sin_port;
- sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
- &local_addr);
- }
- }
-
- if (tls->has_pending_connect) {
- PJ_LOG(4,(tls->base.obj_name,
- "TLS transport %.*s:%d is connecting to %.*s:%d...",
- (int)tls->base.local_name.host.slen,
- tls->base.local_name.host.ptr,
- tls->base.local_name.port,
- (int)tls->base.remote_name.host.slen,
- tls->base.remote_name.host.ptr,
- tls->base.remote_name.port));
- }
-
- /* Done */
- *p_transport = &tls->base;
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * This callback is called by ioqueue when pending accept() operation has
- * completed.
- */
-static void on_accept_complete( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t sock,
- pj_status_t status)
-{
- struct tls_listener *listener;
- struct tls_transport *tls;
- struct pending_accept *accept_op;
- int err_cnt = 0;
-
- listener = (struct tls_listener*) pj_ioqueue_get_user_data(key);
- accept_op = (struct pending_accept*) op_key;
-
- /*
- * Loop while there is immediate connection or when there is error.
- */
- do {
- if (status == PJ_EPENDING) {
- /*
- * This can only happen when this function is called during
- * initialization to kick off asynchronous accept().
- */
-
- } else if (status != PJ_SUCCESS) {
-
- /*
- * Error in accept().
- */
- ssl_report_error(listener->factory.obj_name, 4, status,
- "Error in asynchronous accept() completion");
-
- /*
- * Prevent endless accept() error loop by limiting the
- * number of consecutive errors. Once the number of errors
- * is equal to maximum, we treat this as permanent error, and
- * we stop the accept() operation.
- */
- ++err_cnt;
- if (err_cnt >= 10) {
- PJ_LOG(1, (listener->factory.obj_name,
- "Too many errors, listener stopping"));
- }
-
- } else {
- pj_pool_t *pool;
- struct pending_accept *new_op;
-
- if (sock == PJ_INVALID_SOCKET) {
- sock = accept_op->new_sock;
- }
-
- if (sock == PJ_INVALID_SOCKET) {
- pj_assert(!"Should not happen. status should be error");
- goto next_accept;
- }
-
- PJ_LOG(4,(listener->factory.obj_name,
- "TLS listener %.*s:%d: got incoming TCP connection "
- "from %s:%d, sock=%d",
- (int)listener->factory.addr_name.host.slen,
- listener->factory.addr_name.host.ptr,
- listener->factory.addr_name.port,
- pj_inet_ntoa(accept_op->remote_addr.sin_addr),
- pj_ntohs(accept_op->remote_addr.sin_port),
- sock));
-
- /* Create new accept_opt */
- pool = pjsip_endpt_create_pool(listener->endpt, "tlss%p",
- POOL_TP_INIT, POOL_TP_INC);
- new_op = PJ_POOL_ZALLOC_T(pool, struct pending_accept);
- new_op->pool = pool;
- new_op->listener = listener;
- new_op->index = accept_op->index;
- pj_ioqueue_op_key_init(&new_op->op_key, sizeof(new_op->op_key));
- listener->accept_op[accept_op->index] = new_op;
-
- /*
- * Incoming connections!
- * Create TLS transport for the new socket.
- */
- status = tls_create( listener, accept_op->pool, sock, PJ_TRUE,
- &accept_op->local_addr,
- &accept_op->remote_addr, &tls);
- if (status == PJ_SUCCESS) {
- /* Complete SSL_accept() */
- status = ssl_accept(tls);
- }
-
- if (status == PJ_SUCCESS) {
- /* Start asynchronous read from the socket */
- status = tls_start_read(tls);
- }
-
- if (status != PJ_SUCCESS) {
- ssl_report_error(tls->base.obj_name, 4, status,
- "Error creating incoming TLS transport");
- pjsip_transport_shutdown(&tls->base);
-
- } else {
- /* Start keep-alive timer */
- if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) {
- pj_time_val delay = {PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0};
- pjsip_endpt_schedule_timer(listener->endpt,
- &tls->ka_timer,
- &delay);
- tls->ka_timer.id = PJ_TRUE;
- pj_gettimeofday(&tls->last_activity);
- }
- }
-
- accept_op = new_op;
- }
-
-next_accept:
- /*
- * Start the next asynchronous accept() operation.
- */
- accept_op->addr_len = sizeof(pj_sockaddr_in);
- accept_op->new_sock = PJ_INVALID_SOCKET;
-
- status = pj_ioqueue_accept(listener->key,
- &accept_op->op_key,
- &accept_op->new_sock,
- &accept_op->local_addr,
- &accept_op->remote_addr,
- &accept_op->addr_len);
-
- /*
- * Loop while we have immediate connection or when there is error.
- */
-
- } while (status != PJ_EPENDING);
-}
-
-
-/*
- * Callback from ioqueue when packet is sent.
- */
-static void on_write_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent)
-{
- struct tls_transport *tls;
- pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
-
- tls = (struct tls_transport*) pj_ioqueue_get_user_data(key);
- tdata_op_key->tdata = NULL;
-
- if (tdata_op_key->callback) {
- /*
- * Notify sip_transport.c that packet has been sent.
- */
- if (bytes_sent == 0)
- bytes_sent = -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
-
- tdata_op_key->callback(&tls->base, tdata_op_key->token, bytes_sent);
- }
-
- /* Check for error/closure */
- if (bytes_sent <= 0) {
- pj_status_t status;
-
- ssl_report_error(tls->base.obj_name, 4, -bytes_sent,
- "TLS send() error");
-
- status = (bytes_sent == 0) ? PJ_STATUS_FROM_OS(OSERR_ENOTCONN) :
- -bytes_sent;
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
- } else {
- /* Mark last activity */
- pj_gettimeofday(&tls->last_activity);
- }
-}
-
-
-/* Add tdata to pending list */
-static void add_pending_tx(struct tls_transport *tls,
- pjsip_tx_data *tdata)
-{
- struct delayed_tdata *delayed_tdata;
-
- delayed_tdata = PJ_POOL_ALLOC_T(tdata->pool, struct delayed_tdata);
- delayed_tdata->tdata_op_key = &tdata->op_key;
- pj_list_push_back(&tls->delayed_list, delayed_tdata);
-}
-
-
-/*
- * This callback is called by transport manager to send SIP message
- */
-static pj_status_t tls_send_msg(pjsip_transport *transport,
- pjsip_tx_data *tdata,
- const pj_sockaddr_t *rem_addr,
- int addr_len,
- void *token,
- void (*callback)(pjsip_transport *transport,
- void *token,
- pj_ssize_t sent_bytes))
-{
- struct tls_transport *tls = (struct tls_transport*)transport;
- pj_ssize_t size;
- pj_bool_t delayed = PJ_FALSE;
- pj_status_t status = PJ_SUCCESS;
-
- /* Sanity check */
- PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
-
- /* Check that there's no pending operation associated with the tdata */
- PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
-
- /* Check the address is supported */
- PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL);
-
-
-
- /* Init op key. */
- tdata->op_key.tdata = tdata;
- tdata->op_key.token = token;
- tdata->op_key.callback = callback;
-
- /* If asynchronous connect() has not completed yet, just put the
- * transmit data in the pending transmission list since we can not
- * use the socket yet.
- */
- if (tls->has_pending_connect) {
-
- /*
- * Looks like connect() is still in progress. Check again (this time
- * with holding the lock) to be sure.
- */
- pj_lock_acquire(tls->base.lock);
-
- if (tls->has_pending_connect) {
- /*
- * connect() is still in progress. Put the transmit data to
- * the delayed list.
- */
- add_pending_tx(tls, tdata);
- status = PJ_EPENDING;
-
- /* Prevent ssl_write() to be called below */
- delayed = PJ_TRUE;
- }
-
- pj_lock_release(tls->base.lock);
- }
-
- if (!delayed) {
-
- pj_bool_t no_pending_tx;
-
- /* Make sure that we've flushed pending tx first so that
- * stream is in order.
- */
- no_pending_tx = tls_flush_pending_tx(tls);
-
- /* Send data immediately with SSL_write() if we don't have
- * pending data in our list.
- */
- if (no_pending_tx) {
-
- status = ssl_write(tls, tdata);
-
- /* On EWOULDBLOCK, put this tdata in the list */
- if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
- add_pending_tx(tls, tdata);
- status = PJ_EPENDING;
- }
-
- if (status != PJ_EPENDING) {
- /* Not pending (could be immediate success or error) */
- tdata->op_key.tdata = NULL;
-
- /* Shutdown transport on closure/errors */
- if (status != PJ_SUCCESS) {
- size = -status;
-
- ssl_report_error(tls->base.obj_name, 4, status,
- "TLS send() error");
-
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
- }
- }
-
- } else {
- /* We have pending data in our list, so queue the txdata
- * in the pending tx list.
- */
- add_pending_tx(tls, tdata);
- status = PJ_EPENDING;
- }
- }
-
- return status;
-}
-
-
-/*
- * This callback is called by transport manager to shutdown transport.
- * This normally is only used by UDP transport.
- */
-static pj_status_t tls_shutdown(pjsip_transport *transport)
-{
- struct tls_transport *tls = (struct tls_transport*)transport;
-
- /* Shutdown SSL */
- if (!tls->ssl_shutdown_called) {
- /* shutdown SSL */
- SSL_shutdown(tls->ssl);
- tls->ssl_shutdown_called = PJ_TRUE;
-
- /* Stop keep-alive timer. */
- if (tls->ka_timer.id) {
- pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer);
- tls->ka_timer.id = PJ_FALSE;
- }
-
- PJ_LOG(4,(transport->obj_name, "TLS transport shutdown"));
- }
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Callback from ioqueue that an incoming data is received from the socket.
- */
-static void on_read_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read_unused)
-{
- enum { MAX_IMMEDIATE_PACKET = 10 };
- pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key;
- pjsip_rx_data *rdata = rdata_op_key->rdata;
- struct tls_transport *tls =
- (struct tls_transport*)rdata->tp_info.transport;
- int i;
- pj_status_t status;
-
- /* Don't do anything if transport is closing. */
- if (tls->is_closing) {
- tls->is_closing++;
- return;
- }
-
-
- /* Recall that we use MSG_PEEK when calling ioqueue_recv(), so
- * when this callback is called, data has not actually been read
- * from socket buffer.
- */
-
- for (i=0;; ++i) {
- pj_uint32_t flags;
-
- /* Read data from SSL connection */
- status = ssl_read(tls);
-
- if (status == PJ_SUCCESS) {
- /*
- * We have packet!
- */
- pj_size_t size_eaten;
-
- /* Mark last activity */
- pj_gettimeofday(&tls->last_activity);
-
- /* Init pkt_info part. */
- rdata->pkt_info.zero = 0;
- pj_gettimeofday(&rdata->pkt_info.timestamp);
-
- /* Report to transport manager.
- * The transport manager will tell us how many bytes of the packet
- * have been processed (as valid SIP message).
- */
- size_eaten =
- pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr,
- rdata);
-
- pj_assert(size_eaten <= (pj_size_t)rdata->pkt_info.len);
-
- /* Move unprocessed data to the front of the buffer */
- if (size_eaten>0 && size_eaten<(pj_size_t)rdata->pkt_info.len) {
- pj_memmove(rdata->pkt_info.packet,
- rdata->pkt_info.packet + size_eaten,
- rdata->pkt_info.len - size_eaten);
- }
-
- rdata->pkt_info.len -= size_eaten;
-
- } else if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
-
- /* Ignore EWOULDBLOCK error (?) */
-
- } else {
-
- /* For other errors, treat as transport being closed */
- ssl_report_error(tls->base.obj_name, 4, status,
- "Error reading SSL stream");
-
- /* We can not destroy the transport since high level objects may
- * still keep reference to this transport. So we can only
- * instruct transport manager to gracefully start the shutdown
- * procedure for this transport.
- */
- if (tls->close_reason==PJ_SUCCESS)
- tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
-
- return;
- }
-
- /* Reset pool. */
- pj_pool_reset(rdata->tp_info.pool);
-
- /* If we have pending data in SSL buffer, read it. */
- if (SSL_pending(tls->ssl)) {
-
- /* Check that we have enough space in buffer */
- if (rdata->pkt_info.len >= PJSIP_MAX_PKT_LEN-1) {
- PJ_LOG(4,(tls->base.obj_name,
- "Incoming packet dropped from tls:%.*s:%d "
- "because it's too big (%d bytes)",
- (int)tls->base.remote_name.host.slen,
- tls->base.remote_name.host.ptr,
- tls->base.remote_name.port,
- rdata->pkt_info.len));
- rdata->pkt_info.len = 0;
- }
-
- continue;
- }
-
- /* If we've reached maximum number of packets received on a single
- * poll, force the next reading to be asynchronous.
- */
- if (i >= MAX_IMMEDIATE_PACKET) {
- /* Receive quota reached. Force ioqueue_recv() to
- * return PJ_EPENDING
- */
- flags = PJ_IOQUEUE_ALWAYS_ASYNC;
- } else {
- /* This doesn't work too well when IOCP is used, as it seems that
- * IOCP refuses to do MSG_PEEK when WSARecv() is called without
- * OVERLAP structure. With OpenSSL it gives this error:
- * error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version
- * number
- *
- * flags = 0
- */
- flags = PJ_IOQUEUE_ALWAYS_ASYNC;
- }
-
- /* Read next packet from the network. Remember, we need to use
- * MSG_PEEK or otherwise the packet will be eaten by us!
- */
- bytes_read_unused = 1;
- status = pj_ioqueue_recv(key, op_key,
- rdata->pkt_info.packet+rdata->pkt_info.len,
- &bytes_read_unused, flags | PJ_MSG_PEEK);
-
- if (status == PJ_SUCCESS) {
-
- /* Continue loop. */
- pj_assert(i < MAX_IMMEDIATE_PACKET);
-
- } else if (status == PJ_EPENDING) {
- break;
-
- } else {
- /* Socket error */
-
- /* We can not destroy the transport since high level objects may
- * still keep reference to this transport. So we can only
- * instruct transport manager to gracefully start the shutdown
- * procedure for this transport.
- */
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
-
- return;
- }
- }
-}
-
-
-/*
- * Callback from ioqueue when asynchronous connect() operation completes.
- */
-static void on_connect_complete(pj_ioqueue_key_t *key,
- pj_status_t status)
-{
- struct tls_transport *tls;
- pj_sockaddr_in addr;
- int addrlen;
-
- tls = (struct tls_transport*) pj_ioqueue_get_user_data(key);
-
- /* Check connect() status */
- if (status != PJ_SUCCESS) {
-
- /* Mark that pending connect() operation has completed. */
- tls->has_pending_connect = PJ_FALSE;
-
- ssl_report_error(tls->base.obj_name, 4, status,
- "Error connecting to %.*s:%d",
- (int)tls->base.remote_name.host.slen,
- tls->base.remote_name.host.ptr,
- tls->base.remote_name.port);
-
- /* Cancel all delayed transmits */
- while (!pj_list_empty(&tls->delayed_list)) {
- struct delayed_tdata *pending_tx;
- pj_ioqueue_op_key_t *op_key;
-
- pending_tx = tls->delayed_list.next;
- pj_list_erase(pending_tx);
-
- op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
-
- on_write_complete(tls->key, op_key, -status);
- }
-
- /* We can not destroy the transport since high level objects may
- * still keep reference to this transport. So we can only
- * instruct transport manager to gracefully start the shutdown
- * procedure for this transport.
- */
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
- return;
- }
-
- PJ_LOG(4,(tls->base.obj_name,
- "TCP transport %.*s:%d is connected to %.*s:%d",
- (int)tls->base.local_name.host.slen,
- tls->base.local_name.host.ptr,
- tls->base.local_name.port,
- (int)tls->base.remote_name.host.slen,
- tls->base.remote_name.host.ptr,
- tls->base.remote_name.port));
-
-
- /* Update (again) local address, just in case local address currently
- * set is different now that the socket is connected (could happen
- * on some systems, like old Win32 probably?).
- */
- addrlen = sizeof(pj_sockaddr_in);
- if (pj_sock_getsockname(tls->sock, &addr, &addrlen)==PJ_SUCCESS) {
- pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tls->base.local_addr;
-
- if (tp_addr->sin_addr.s_addr != addr.sin_addr.s_addr) {
- tp_addr->sin_addr.s_addr = addr.sin_addr.s_addr;
- tp_addr->sin_port = addr.sin_port;
- sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
- tp_addr);
- }
- }
-
- /* Perform SSL_connect() */
- status = ssl_connect(tls);
- if (status != PJ_SUCCESS) {
-
- /* Cancel all delayed transmits */
- while (!pj_list_empty(&tls->delayed_list)) {
- struct delayed_tdata *pending_tx;
- pj_ioqueue_op_key_t *op_key;
-
- pending_tx = tls->delayed_list.next;
- pj_list_erase(pending_tx);
-
- op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
-
- on_write_complete(tls->key, op_key, -status);
- }
-
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
- return;
- }
-
- /* Mark that pending connect() operation has completed. */
- tls->has_pending_connect = PJ_FALSE;
-
- /* Start pending read */
- status = tls_start_read(tls);
- if (status != PJ_SUCCESS) {
-
- /* Cancel all delayed transmits */
- while (!pj_list_empty(&tls->delayed_list)) {
- struct delayed_tdata *pending_tx;
- pj_ioqueue_op_key_t *op_key;
-
- pending_tx = tls->delayed_list.next;
- pj_list_erase(pending_tx);
-
- op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
-
- on_write_complete(tls->key, op_key, -status);
- }
-
-
- /* We can not destroy the transport since high level objects may
- * still keep reference to this transport. So we can only
- * instruct transport manager to gracefully start the shutdown
- * procedure for this transport.
- */
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
- return;
- }
-
- /* Flush all pending send operations */
- tls_flush_pending_tx(tls);
-
- /* Start keep-alive timer */
- if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) {
- pj_time_val delay = { PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0 };
- pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer,
- &delay);
- tls->ka_timer.id = PJ_TRUE;
- pj_gettimeofday(&tls->last_activity);
- }
-
-}
-
-
-/* Transport keep-alive timer callback */
-static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
-{
- struct tls_transport *tls = (struct tls_transport*) e->user_data;
- const pj_str_t ka_data = PJSIP_TLS_KEEP_ALIVE_DATA;
- pj_time_val delay;
- pj_time_val now;
- pj_status_t status;
-
- PJ_UNUSED_ARG(th);
-
- tls->ka_timer.id = PJ_TRUE;
-
- pj_gettimeofday(&now);
- PJ_TIME_VAL_SUB(now, tls->last_activity);
-
- if (now.sec > 0 && now.sec < PJSIP_TLS_KEEP_ALIVE_INTERVAL) {
- /* There has been activity, so don't send keep-alive */
- delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL - now.sec;
- delay.msec = 0;
-
- pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer,
- &delay);
- tls->ka_timer.id = PJ_TRUE;
- return;
- }
-
- PJ_LOG(5,(tls->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d",
- (int)ka_data.slen, (int)tls->base.remote_name.host.slen,
- tls->base.remote_name.host.ptr,
- tls->base.remote_name.port));
-
- /* Send the data */
- status = ssl_write_bytes(tls, ka_data.ptr, (int)ka_data.slen,
- "keep-alive");
- if (status != PJ_SUCCESS &&
- status != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK))
- {
- ssl_report_error(tls->base.obj_name, 1, status,
- "Error sending keep-alive packet");
- return;
- }
-
- /* Register next keep-alive */
- delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL;
- delay.msec = 0;
-
- pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer,
- &delay);
- tls->ka_timer.id = PJ_TRUE;
-}
-
-#endif /* PJSIP_HAS_TLS_TRANSPORT */
-