summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjlib/include/pj/ssl_sock.h162
-rw-r--r--pjlib/src/pj/ssl_sock_common.c101
-rw-r--r--pjlib/src/pj/ssl_sock_ossl.c1780
-rw-r--r--pjlib/src/pj/ssl_sock_symbian.cpp122
-rw-r--r--pjsip/src/pjsip/sip_transport_tls.c84
5 files changed, 2172 insertions, 77 deletions
diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h
index 9836a3d6..ce319d4d 100644
--- a/pjlib/include/pj/ssl_sock.h
+++ b/pjlib/include/pj/ssl_sock.h
@@ -45,16 +45,140 @@ PJ_BEGIN_DECL
* described more detail) in \ref PJ_ACTIVESOCK.
*/
+
+ /**
+ * This opaque structure describes the secure socket.
+ */
+typedef struct pj_ssl_sock_t pj_ssl_sock_t;
+
+
/**
- * Opaque declaration of certificate or endpoint credentials. This may contains
- * certificate, private key, and trusted Certificate Authorities lists.
+ * Opaque declaration of endpoint certificate or credentials. This may contains
+ * certificate, private key, and trusted Certificate Authorities list.
*/
typedef struct pj_ssl_cert_t pj_ssl_cert_t;
+
/**
- * This opaque structure describes the secure socket.
+ * Create credential from files.
+ *
+ * @param CA_file The file of trusted CA list.
+ * @param cert_file The file of certificate.
+ * @param privkey_file The file of private key.
+ * @param privkey_pass The password of private key, if any.
+ *
+ * @return PJ_SUCCESS when successful.
*/
-typedef struct pj_ssl_sock_t pj_ssl_sock_t;
+PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
+ const pj_str_t *CA_file,
+ const pj_str_t *cert_file,
+ const pj_str_t *privkey_file,
+ const pj_str_t *privkey_pass,
+ pj_ssl_cert_t **p_cert);
+
+
+/**
+ * Cipher suites enumeration.
+ */
+typedef enum pj_ssl_cipher {
+
+ /* NULL */
+ TLS_NULL_WITH_NULL_NULL = 0x00000000,
+
+ /* TLS/SSLv3 */
+ TLS_RSA_WITH_NULL_MD5 = 0x00000001,
+ TLS_RSA_WITH_NULL_SHA = 0x00000002,
+ TLS_RSA_WITH_NULL_SHA256 = 0x0000003B,
+ TLS_RSA_WITH_RC4_128_MD5 = 0x00000004,
+ TLS_RSA_WITH_RC4_128_SHA = 0x00000005,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0000000A,
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0x0000002F,
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0x00000035,
+ TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003C,
+ TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x0000003D,
+ TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0000000D,
+ TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000010,
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x00000013,
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000016,
+ TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x00000030,
+ TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x00000031,
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x00000032,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x00000033,
+ TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x00000036,
+ TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x00000037,
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x00000038,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x00000039,
+ TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x0000003E,
+ TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003F,
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x00000040,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x00000067,
+ TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x00000068,
+ TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x00000069,
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x0000006A,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x0000006B,
+ TLS_DH_anon_WITH_RC4_128_MD5 = 0x00000018,
+ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x0000001B,
+ TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x00000034,
+ TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A,
+ TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C,
+ TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D,
+
+ /* TLS (deprecated) */
+ TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003,
+ TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006,
+ TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007,
+ TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000008,
+ TLS_RSA_WITH_DES_CBC_SHA = 0x00000009,
+ TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0000000B,
+ TLS_DH_DSS_WITH_DES_CBC_SHA = 0x0000000C,
+ TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0000000E,
+ TLS_DH_RSA_WITH_DES_CBC_SHA = 0x0000000F,
+ TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x00000011,
+ TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x00000012,
+ TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000014,
+ TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x00000015,
+ TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x00000017,
+ TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x00000019,
+ TLS_DH_anon_WITH_DES_CBC_SHA = 0x0000001A,
+
+ /* SSLv3 */
+ SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x0000001C,
+ SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA= 0x0000001D,
+ SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x0000001E,
+
+ /* SSLv2 */
+ SSL_CK_RC4_128_WITH_MD5 = 0x00010080,
+ SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x00020080,
+ SSL_CK_RC2_128_CBC_WITH_MD5 = 0x00030080,
+ SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x00040080,
+ SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x00050080,
+ SSL_CK_DES_64_CBC_WITH_MD5 = 0x00060040,
+ SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x000700C0
+
+} pj_ssl_cipher;
+
+
+/**
+ * Get cipher list supported by SSL/TLS backend.
+ *
+ * @param ciphers The ciphers buffer to receive cipher list.
+ * @param cipher_num Maximum number of ciphers to be received.
+ *
+ * @return PJ_SUCCESS when successful.
+ */
+PJ_DECL(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
+ unsigned *cipher_num);
+
+
+/**
+ * Get cipher name string.
+ *
+ * @param cipher The cipher.
+ *
+ * @return The cipher name or NULL if cipher is not recognized.
+ */
+PJ_DECL(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher);
+
/**
* This structure contains the callbacks to be called by the secure socket.
@@ -180,10 +304,10 @@ typedef enum pj_ssl_sock_proto
{
PJ_SSL_SOCK_PROTO_DEFAULT, /**< Default protocol of backend. */
PJ_SSL_SOCK_PROTO_TLS1, /**< TLSv1.0 protocol. */
- PJ_SSL_SOCK_PROTO_SSL2, /**< SSLv2.0 protocol. */
PJ_SSL_SOCK_PROTO_SSL3, /**< SSLv3.0 protocol. */
PJ_SSL_SOCK_PROTO_SSL23, /**< SSLv3.0 but can roll back to
SSLv2.0. */
+ PJ_SSL_SOCK_PROTO_SSL2, /**< SSLv2.0 protocol. */
PJ_SSL_SOCK_PROTO_DTLS1 /**< DTLSv1.0 protocol. */
} pj_ssl_sock_proto;
@@ -203,10 +327,10 @@ typedef struct pj_ssl_sock_info
*/
pj_ssl_sock_proto proto;
/**
- * Describes cipher suite being used, this can be known only when
- * connection is established.
+ * Describes cipher suite being used, this will only be set when connection
+ * is established.
*/
- pj_str_t cipher;
+ pj_ssl_cipher cipher;
/**
* Describes local address.
*/
@@ -218,6 +342,7 @@ typedef struct pj_ssl_sock_info
} pj_ssl_sock_info;
+
/**
* Definition of secure socket creation parameters.
*/
@@ -316,10 +441,27 @@ typedef struct pj_ssl_sock_param
pj_size_t send_buffer_size;
/**
- * Cipher list string. If empty, then default cipher list of the backend
+ * Specify buffer size for receiving encrypted (and perhaps compressed)
+ * data on underlying socket. This setting is unused on Symbian, since
+ * SSL/TLS Symbian backend, CSecureSocket, can use application buffer
+ * directly.
+ *
+ * Default value is 1500.
+ */
+ pj_size_t read_buffer_size;
+
+ /**
+ * Number of ciphers contained in the specified cipher preference.
+ * If this is set to zero, then default cipher list of the backend
* will be used.
*/
- pj_str_t ciphers;
+ unsigned ciphers_num;
+
+ /**
+ * Ciphers and order preference. If empty, then default cipher list and
+ * its default order of the backend will be used.
+ */
+ pj_ssl_cipher *ciphers;
/**
* Security negotiation timeout. If this is set to zero (both sec and
diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
index bcb249fb..30603795 100644
--- a/pjlib/src/pj/ssl_sock_common.c
+++ b/pjlib/src/pj/ssl_sock_common.c
@@ -17,12 +17,96 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pj/ssl_sock.h>
+#include <pj/errno.h>
#include <pj/string.h>
+/* Cipher name structure */
+typedef struct cipher_name_t {
+ pj_ssl_cipher cipher;
+ const char *name;
+} cipher_name_t;
+
+/* Cipher name constants */
+static cipher_name_t cipher_names[] =
+{
+ {TLS_NULL_WITH_NULL_NULL, "NULL"},
+
+ /* TLS/SSLv3 */
+ {TLS_RSA_WITH_NULL_MD5, "TLS_RSA_WITH_NULL_MD5"},
+ {TLS_RSA_WITH_NULL_SHA, "TLS_RSA_WITH_NULL_SHA"},
+ {TLS_RSA_WITH_NULL_SHA256, "TLS_RSA_WITH_NULL_SHA256"},
+ {TLS_RSA_WITH_RC4_128_MD5, "TLS_RSA_WITH_RC4_128_MD5"},
+ {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA"},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
+ {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"},
+ {TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"},
+ {TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"},
+ {TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"},
+ {TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"},
+ {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"},
+ {TLS_DH_DSS_WITH_AES_128_CBC_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"},
+ {TLS_DH_RSA_WITH_AES_128_CBC_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"},
+ {TLS_DHE_DSS_WITH_AES_128_CBC_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"},
+ {TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"},
+ {TLS_DH_DSS_WITH_AES_256_CBC_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"},
+ {TLS_DH_RSA_WITH_AES_256_CBC_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"},
+ {TLS_DHE_DSS_WITH_AES_256_CBC_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"},
+ {TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"},
+ {TLS_DH_DSS_WITH_AES_128_CBC_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"},
+ {TLS_DH_RSA_WITH_AES_128_CBC_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"},
+ {TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"},
+ {TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"},
+ {TLS_DH_DSS_WITH_AES_256_CBC_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"},
+ {TLS_DH_RSA_WITH_AES_256_CBC_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"},
+ {TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"},
+ {TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"},
+ {TLS_DH_anon_WITH_RC4_128_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"},
+ {TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"},
+ {TLS_DH_anon_WITH_AES_128_CBC_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"},
+ {TLS_DH_anon_WITH_AES_256_CBC_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"},
+ {TLS_DH_anon_WITH_AES_128_CBC_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"},
+ {TLS_DH_anon_WITH_AES_256_CBC_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"},
+
+ /* TLS (deprecated) */
+ {TLS_RSA_EXPORT_WITH_RC4_40_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"},
+ {TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"},
+ {TLS_RSA_WITH_IDEA_CBC_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"},
+ {TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+ {TLS_RSA_WITH_DES_CBC_SHA, "TLS_RSA_WITH_DES_CBC_SHA"},
+ {TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+ {TLS_DH_DSS_WITH_DES_CBC_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"},
+ {TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+ {TLS_DH_RSA_WITH_DES_CBC_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"},
+ {TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+ {TLS_DHE_DSS_WITH_DES_CBC_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"},
+ {TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+ {TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"},
+ {TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"},
+ {TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"},
+ {TLS_DH_anon_WITH_DES_CBC_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"},
+
+ /* SSLv3 */
+ {SSL_FORTEZZA_KEA_WITH_NULL_SHA, "SSL_FORTEZZA_KEA_WITH_NULL_SHA"},
+ {SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"},
+ {SSL_FORTEZZA_KEA_WITH_RC4_128_SHA, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"},
+
+ /* SSLv2 */
+ {SSL_CK_RC4_128_WITH_MD5, "SSL_CK_RC4_128_WITH_MD5"},
+ {SSL_CK_RC4_128_EXPORT40_WITH_MD5, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"},
+ {SSL_CK_RC2_128_CBC_WITH_MD5, "SSL_CK_RC2_128_CBC_WITH_MD5"},
+ {SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"},
+ {SSL_CK_IDEA_128_CBC_WITH_MD5, "SSL_CK_IDEA_128_CBC_WITH_MD5"},
+ {SSL_CK_DES_64_CBC_WITH_MD5, "SSL_CK_DES_64_CBC_WITH_MD5"},
+ {SSL_CK_DES_192_EDE3_CBC_WITH_MD5, "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"}
+};
+
+
/*
* Initialize the SSL socket configuration with the default values.
*/
-PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
+PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
{
pj_bzero(param, sizeof(*param));
@@ -32,8 +116,9 @@ PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
param->async_cnt = 1;
param->concurrency = -1;
param->whole_data = PJ_TRUE;
-#if PJ_SYMBIAN
param->send_buffer_size = 8192;
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+ param->read_buffer_size = 1500;
#endif
/* Security config */
@@ -41,3 +126,15 @@ PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
}
+PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
+{
+ unsigned i, n;
+
+ n = PJ_ARRAY_SIZE(cipher_names);
+ for (i = 0; i < n; ++i) {
+ if (cipher == cipher_names[i].cipher)
+ return cipher_names[i].name;
+ }
+
+ return NULL;
+}
diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
new file mode 100644
index 00000000..9c6c80c8
--- /dev/null
+++ b/pjlib/src/pj/ssl_sock_ossl.c
@@ -0,0 +1,1780 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2009 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pj/ssl_sock.h>
+#include <pj/activesock.h>
+#include <pj/compat/socket.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/list.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+/* Only build when PJ_HAS_SSL_SOCK is enabled */
+//#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0
+
+#define THIS_FILE "ssl_sock_ossl.c"
+
+/*
+ * Include OpenSSL headers
+ */
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+
+#ifdef _MSC_VER
+# ifdef _DEBUG
+# pragma comment( lib, "libeay32MTd")
+# pragma comment( lib, "ssleay32MTd")
+#else
+# pragma comment( lib, "libeay32MT")
+# pragma comment( lib, "ssleay32MT")
+# endif
+#endif
+
+
+/*
+ * SSL/TLS state enumeration.
+ */
+enum ssl_state {
+ SSL_STATE_NULL,
+ SSL_STATE_HANDSHAKING,
+ SSL_STATE_ESTABLISHED
+};
+
+/*
+ * Structure of SSL socket read buffer.
+ */
+typedef struct read_data_t
+{
+ void *data;
+ pj_size_t len;
+} read_data_t;
+
+/*
+ * Get the offset of pointer to read-buffer of SSL socket from read-buffer
+ * of active socket. Note that both SSL socket and active socket employ
+ * different but correlated read-buffers (as much as async_cnt for each),
+ * and to make it easier/faster to find corresponding SSL socket's read-buffer
+ * from known active socket's read-buffer, the pointer of corresponding
+ * SSL socket's read-buffer is stored right after the end of active socket's
+ * read-buffer.
+ */
+#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \
+ (read_data_t**) \
+ ((pj_int8_t*)(asock_rbuf) + \
+ ssock->param.read_buffer_size)
+
+/*
+ * Structure of SSL socket write buffer.
+ */
+typedef struct write_data_t {
+ pj_ioqueue_op_key_t key;
+ pj_size_t record_len;
+ pj_ioqueue_op_key_t *app_key;
+ pj_size_t plain_data_len;
+ pj_size_t data_len;
+ union {
+ char content[1];
+ const char *ptr;
+ } data;
+ unsigned flags;
+} write_data_t;
+
+/*
+ * Structure of SSL socket write state.
+ */
+typedef struct write_state_t {
+ char *buf;
+ pj_size_t max_len;
+ char *start;
+ pj_size_t len;
+ write_data_t *last_data;
+} write_state_t;
+
+/*
+ * Structure of write data pending.
+ */
+typedef struct write_pending_t {
+ PJ_DECL_LIST_MEMBER(struct write_pending_t);
+ write_data_t data;
+} write_pending_t;
+
+/*
+ * Secure socket structure definition.
+ */
+struct pj_ssl_sock_t
+{
+ pj_pool_t *pool;
+ pj_ssl_sock_t *parent;
+ pj_ssl_sock_param param;
+ pj_ssl_cert_t *cert;
+
+ pj_bool_t is_server;
+ enum ssl_state ssl_state;
+ pj_ioqueue_op_key_t handshake_op_key;
+
+ pj_sock_t sock;
+ pj_activesock_t *asock;
+
+ pj_sockaddr local_addr;
+ pj_sockaddr rem_addr;
+ int addr_len;
+
+ pj_bool_t read_started;
+ pj_size_t read_size;
+ pj_uint32_t read_flags;
+ void **asock_rbuf;
+ read_data_t *ssock_rbuf;
+
+ write_state_t write_state;
+ write_pending_t write_pending;
+ write_pending_t write_pending_empty;
+ pj_lock_t *write_mutex; /* protect write BIO and write_state */
+
+ SSL_CTX *ossl_ctx;
+ SSL *ossl_ssl;
+ BIO *ossl_rbio;
+ BIO *ossl_wbio;
+};
+
+
+/*
+ * Certificate/credential structure definition.
+ */
+struct pj_ssl_cert_t
+{
+ pj_str_t CA_file;
+ pj_str_t cert_file;
+ pj_str_t privkey_file;
+ pj_str_t privkey_pass;
+};
+
+
+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
+
+/*
+ *******************************************************************
+ * Static/internal 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);
+}
+
+
+
+/* OpenSSL library initialization counter */
+static int openssl_init_count;
+
+/* OpenSSL available ciphers */
+static pj_ssl_cipher openssl_ciphers[64];
+static unsigned openssl_cipher_num;
+
+
+/* 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();
+
+ /* Init available ciphers */
+ if (openssl_cipher_num == 0) {
+ SSL_METHOD *meth = NULL;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ STACK_OF(SSL_CIPHER) *sk_cipher;
+ unsigned i, n;
+
+ meth = (SSL_METHOD*)SSLv23_server_method();
+ if (!meth)
+ meth = (SSL_METHOD*)TLSv1_server_method();
+ if (!meth)
+ meth = (SSL_METHOD*)SSLv3_server_method();
+ if (!meth)
+ meth = (SSL_METHOD*)SSLv2_server_method();
+ pj_assert(meth);
+
+ ctx=SSL_CTX_new(meth);
+ SSL_CTX_set_cipher_list(ctx, "ALL");
+
+ ssl = SSL_new(ctx);
+ sk_cipher = SSL_get_ciphers(ssl);
+
+ n = sk_SSL_CIPHER_num(sk_cipher);
+ if (n > PJ_ARRAY_SIZE(openssl_ciphers))
+ n = PJ_ARRAY_SIZE(openssl_ciphers);
+
+ for (i = 0; i < n; ++i) {
+ SSL_CIPHER *c;
+ c = sk_SSL_CIPHER_value(sk_cipher,i);
+ openssl_ciphers[i] = (pj_ssl_cipher)
+ (pj_uint32_t)c->id & 0x00FFFFFF;
+ //printf("%3u: %08x=%s\n", i+1, c->id, SSL_CIPHER_get_name(c));
+ }
+
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+
+ openssl_cipher_num = n;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Shutdown OpenSSL */
+static void shutdown_openssl(void)
+{
+ if (--openssl_init_count != 0)
+ return;
+}
+
+
+/* SSL password callback. */
+static int password_cb(char *buf, int num, int rwflag, void *user_data)
+{
+ pj_ssl_cert_t *cert = (pj_ssl_cert_t*) user_data;
+
+ PJ_UNUSED_ARG(rwflag);
+
+ if(num < cert->privkey_pass.slen)
+ return 0;
+
+ pj_memcpy(buf, cert->privkey_pass.ptr, cert->privkey_pass.slen);
+ return cert->privkey_pass.slen;
+}
+
+
+/* Create and initialize new SSL context */
+static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
+{
+ SSL_METHOD *ssl_method;
+ SSL_CTX *ctx;
+ pj_ssl_cert_t *cert;
+ int mode, rc;
+
+ pj_assert(ssock && p_ctx);
+
+ cert = ssock->cert;
+
+ /* Make sure OpenSSL library has been initialized */
+ init_openssl();
+
+ /* Determine SSL method to use */
+ switch (ssock->param.proto) {
+ case PJ_SSL_SOCK_PROTO_DEFAULT:
+ case PJ_SSL_SOCK_PROTO_TLS1:
+ ssl_method = (SSL_METHOD*)TLSv1_method();
+ break;
+ case PJ_SSL_SOCK_PROTO_SSL2:
+ ssl_method = (SSL_METHOD*)SSLv2_method();
+ break;
+ case PJ_SSL_SOCK_PROTO_SSL3:
+ ssl_method = (SSL_METHOD*)SSLv3_method();
+ break;
+ case PJ_SSL_SOCK_PROTO_SSL23:
+ ssl_method = (SSL_METHOD*)SSLv23_method();
+ break;
+ case PJ_SSL_SOCK_PROTO_DTLS1:
+ ssl_method = (SSL_METHOD*)DTLSv1_method();
+ break;
+ default:
+ ssl_report_error(THIS_FILE, 4, PJ_EINVAL,
+ "Error creating SSL context");
+ return PJ_EINVAL;
+ }
+
+ /* Create SSL context for the listener */
+ ctx = SSL_CTX_new(ssl_method);
+ if (ctx == NULL) {
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "Error creating SSL context");
+ return PJ_EINVAL;
+ }
+
+ /* Apply credentials */
+ if (cert) {
+ /* Load CA list if one is specified. */
+ if (cert->CA_file.slen) {
+
+ rc = SSL_CTX_load_verify_locations(ctx, cert->CA_file.ptr, NULL);
+
+ if (rc != 1) {
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "Error loading/verifying CA list file '%s'",
+ cert->CA_file.ptr);
+ SSL_CTX_free(ctx);
+ return PJ_EINVAL;
+ }
+
+ PJ_LOG(5,(THIS_FILE, "CA file successfully loaded from '%s'",
+ cert->CA_file.ptr));
+ }
+
+ /* Set password callback */
+ if (cert->privkey_pass.slen) {
+ SSL_CTX_set_default_passwd_cb(ctx, password_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, cert);
+ }
+
+
+ /* Load certificate if one is specified */
+ if (cert->cert_file.slen) {
+
+ /* Load certificate chain from file into ctx */
+ rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr);
+
+ if(rc != 1) {
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "Error loading certificate chain file '%s'",
+ cert->cert_file.ptr);
+ SSL_CTX_free(ctx);
+ return PJ_EINVAL;
+ }
+
+ PJ_LOG(5,(THIS_FILE, "TLS certificate successfully loaded from '%s'",
+ cert->cert_file.ptr));
+ }
+
+
+ /* Load private key if one is specified */
+ if (cert->privkey_file.slen) {
+ /* Adds the first private key found in file to ctx */
+ rc = SSL_CTX_use_PrivateKey_file(ctx, cert->privkey_file.ptr,
+ SSL_FILETYPE_PEM);
+
+ if(rc != 1) {
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "Error adding private key from '%s'",
+ cert->privkey_file.ptr);
+ SSL_CTX_free(ctx);
+ return PJ_EINVAL;
+ }
+
+ PJ_LOG(5,(THIS_FILE, "Private key successfully loaded from '%s'",
+ cert->privkey_file.ptr));
+ }
+ }
+
+
+ /* SSL verification options */
+ if (ssock->param.verify_peer) {
+ mode = SSL_VERIFY_PEER;
+ } else {
+ mode = SSL_VERIFY_NONE;
+ }
+
+ if (ssock->is_server && ssock->param.require_client_cert)
+ mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+
+ SSL_CTX_set_verify(ctx, mode, NULL);
+
+ PJ_LOG(5,(THIS_FILE, "Verification mode set to %d", mode));
+
+ *p_ctx = ctx;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Destroy SSL context */
+static void destroy_ssl_ctx(SSL_CTX *ctx)
+{
+ SSL_CTX_free(ctx);
+
+ /* Potentially shutdown OpenSSL library if this is the last
+ * context exists.
+ */
+ shutdown_openssl();
+}
+
+
+/* Reset SSL socket state */
+static void reset_ssl_sock_state(pj_ssl_sock_t *ssock)
+{
+ ssock->ssl_state = SSL_STATE_NULL;
+
+ if (ssock->ossl_ssl) {
+ SSL_shutdown(ssock->ossl_ssl);
+ SSL_free(ssock->ossl_ssl); /* this will also close BIOs */
+ ssock->ossl_ssl = NULL;
+ }
+ if (ssock->ossl_ctx) {
+ destroy_ssl_ctx(ssock->ossl_ctx);
+ ssock->ossl_ctx = NULL;
+ }
+ if (ssock->asock) {
+ pj_activesock_close(ssock->asock);
+ ssock->asock = NULL;
+ ssock->sock = PJ_INVALID_SOCKET;
+ }
+}
+
+
+/* Generate cipher list with user preference order in OpenSSL format */
+static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock)
+{
+ char buf[1024];
+ pj_str_t cipher_list;
+ STACK_OF(SSL_CIPHER) *sk_cipher;
+ unsigned i;
+ int j, ret;
+
+ if (ssock->param.ciphers_num == 0)
+ return PJ_SUCCESS;
+
+ pj_strset(&cipher_list, buf, 0);
+
+ /* Set SSL with ALL available ciphers */
+ SSL_set_cipher_list(ssock->ossl_ssl, "ALL");
+
+ /* Generate user specified cipher list in OpenSSL format */
+ sk_cipher = SSL_get_ciphers(ssock->ossl_ssl);
+ for (i = 0; i < ssock->param.ciphers_num; ++i) {
+ for (j = 0; j < sk_SSL_CIPHER_num(sk_cipher); ++j) {
+ SSL_CIPHER *c;
+ c = sk_SSL_CIPHER_value(sk_cipher, j);
+ if (ssock->param.ciphers[i] == (pj_ssl_cipher)
+ ((pj_uint32_t)c->id & 0x00FFFFFF))
+ {
+ const char *c_name;
+
+ c_name = SSL_CIPHER_get_name(c);
+
+ /* Check buffer size */
+ if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > sizeof(buf)) {
+ pj_assert(!"Insufficient temporary buffer for cipher");
+ return PJ_ETOOMANY;
+ }
+
+ /* Add colon separator */
+ if (cipher_list.slen)
+ pj_strcat2(&cipher_list, ":");
+
+ /* Add the cipher */
+ pj_strcat2(&cipher_list, c_name);
+ break;
+ }
+ }
+ }
+
+ /* Put NULL termination in the generated cipher list */
+ cipher_list.ptr[cipher_list.slen] = '\0';
+
+ /* Finally, set chosen cipher list */
+ ret = SSL_set_cipher_list(ssock->ossl_ssl, buf);
+ if (ret < 1) {
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "Error setting cipher list");
+ return PJ_EINVAL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* When handshake completed:
+ * - notify application
+ * - if handshake failed, reset SSL state
+ * - return PJ_FALSE when SSL socket instance is destroyed by application.
+ */
+static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
+ pj_status_t status)
+{
+ /* Accepting */
+ if (ssock->is_server) {
+ if (status != PJ_SUCCESS) {
+ /* Handshake failed in accepting, destroy our self silently. */
+ pj_ssl_sock_close(ssock);
+ return PJ_FALSE;
+ }
+ /* Notify application the newly accepted SSL socket */
+ if (ssock->param.cb.on_accept_complete) {
+ pj_bool_t ret;
+ ret = (*ssock->param.cb.on_accept_complete)
+ (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
+ pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
+ if (ret == PJ_FALSE)
+ return PJ_FALSE;
+ }
+ }
+
+ /* Connecting */
+ else {
+ if (ssock->param.cb.on_connect_complete) {
+ pj_bool_t ret;
+ ret = (*ssock->param.cb.on_connect_complete)(ssock, status);
+ if (ret == PJ_FALSE)
+ return PJ_FALSE;
+ }
+ if (status != PJ_SUCCESS) {
+ /* Reset SSL socket state */
+ reset_ssl_sock_state(ssock);
+ }
+ }
+
+ return PJ_TRUE;
+}
+
+/* Flush write BIO to network socket. Note that any access to write BIO
+ * MUST be serialized, so mutex protection must cover any call to OpenSSL
+ * API (that possibly generate data for write BIO) along with the call to
+ * this function (flushing all data in write BIO generated by above
+ * OpenSSL API call).
+ */
+static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ pj_size_t orig_len,
+ unsigned flags)
+{
+ char *data;
+ pj_ssize_t len;
+
+ write_state_t *write_st = &ssock->write_state;
+ write_data_t *wdata;
+ pj_size_t avail_len, needed_len, skipped_len = 0;
+ pj_status_t status;
+
+ /* Check if there is data in write BIO, flush it if any */
+ if (!BIO_pending(ssock->ossl_wbio))
+ return PJ_SUCCESS;
+
+ /* Get data and its length */
+ len = BIO_get_mem_data(ssock->ossl_wbio, &data);
+ if (len == 0)
+ return PJ_SUCCESS;
+
+ /* Calculate buffer size needed, and align it to 8 */
+ needed_len = len + sizeof(write_data_t);
+ needed_len = ((needed_len + 7) >> 3) << 3;
+
+ /* Check buffer availability */
+ avail_len = write_st->max_len - write_st->len;
+ if (avail_len < needed_len)
+ return PJ_ENOMEM;
+
+ /* More buffer availability check, note that the write data must be in
+ * a contigue buffer.
+ */
+ if (write_st->len == 0) {
+
+ write_st->start = write_st->buf;
+ wdata = (write_data_t*)write_st->start;
+
+ } else {
+
+ char *reg1, *reg2;
+ pj_size_t reg1_len, reg2_len;
+
+ /* Unused slots may be wrapped/splitted into two regions, so let's
+ * analyze them if any region can hold the write data.
+ */
+ reg1 = write_st->start + write_st->len;
+ if (reg1 >= write_st->buf + write_st->max_len)
+ reg1 -= write_st->max_len;
+ reg1_len = write_st->max_len - write_st->len;
+ if (reg1 + reg1_len > write_st->buf + write_st->max_len) {
+ reg1_len = write_st->buf + write_st->max_len - reg1;
+ reg2 = write_st->buf;
+ reg2_len = write_st->start - write_st->buf;
+ } else {
+ reg2 = NULL;
+ reg2_len = 0;
+ }
+ avail_len = PJ_MAX(reg1_len, reg2_len);
+ if (avail_len < needed_len)
+ return PJ_ENOMEM;
+
+ /* Get write data pointer and update buffer length */
+ if (reg1_len >= needed_len) {
+ wdata = (write_data_t*)reg1;
+ } else {
+ wdata = (write_data_t*)reg2;
+ /* Unused slot in region 1 is skipped as current write data
+ * doesn't fit it.
+ */
+ skipped_len = reg1_len;
+ }
+ }
+
+ /* Copy the data and set its properties into the buffer */
+ pj_bzero(wdata, sizeof(write_data_t));
+ wdata->app_key = send_key;
+ wdata->record_len = needed_len;
+ wdata->data_len = len;
+ wdata->plain_data_len = orig_len;
+ wdata->flags = flags;
+ pj_memcpy(&wdata->data, data, len);
+
+ /* Send it */
+ if (ssock->param.sock_type == pj_SOCK_STREAM()) {
+ status = pj_activesock_send(ssock->asock, &wdata->key,
+ wdata->data.content, &len,
+ flags);
+ } else {
+ status = pj_activesock_sendto(ssock->asock, &wdata->key,
+ wdata->data.content, &len,
+ flags,
+ (pj_sockaddr_t*)&ssock->rem_addr,
+ ssock->addr_len);
+ }
+
+ /* Oh no, EWOULDBLOCK! */
+ if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+ /* Just return PJ_SUCCESS here, the pending data will be sent in next
+ * call of this function since the data is still stored in write BIO.
+ */
+ return PJ_SUCCESS;
+ }
+
+ /* Reset write BIO after flushed */
+ BIO_reset(ssock->ossl_wbio);
+
+ if (status == PJ_EPENDING) {
+ /* Update write state */
+ pj_assert(skipped_len==0 || write_st->last_data);
+ write_st->len += needed_len + skipped_len;
+ if (write_st->last_data)
+ write_st->last_data->record_len += skipped_len;
+ write_st->last_data = wdata;
+ }
+
+ return status;
+}
+
+static pj_status_t do_handshake(pj_ssl_sock_t *ssock)
+{
+ pj_status_t status;
+ int err;
+
+ pj_lock_acquire(ssock->write_mutex);
+
+ /* Perform SSL handshake */
+ err = SSL_do_handshake(ssock->ossl_ssl);
+ if (err < 0) {
+ err = SSL_get_error(ssock->ossl_ssl, err);
+ if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ)
+ {
+ /* Handshake fails */
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "SSL_do_handshake()");
+ pj_lock_release(ssock->write_mutex);
+ return PJ_ECANCELLED;
+ }
+ }
+
+ /* SSL_do_handshake() may put some pending data into SSL write BIO,
+ * flush it if any.
+ */
+ status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ pj_lock_release(ssock->write_mutex);
+ return status;
+ }
+
+ pj_lock_release(ssock->write_mutex);
+
+ /* Check if handshake has been completed */
+ if (SSL_is_init_finished(ssock->ossl_ssl)) {
+ ssock->ssl_state = SSL_STATE_ESTABLISHED;
+ return PJ_SUCCESS;
+ }
+
+ return PJ_EPENDING;
+}
+
+
+/*
+ *******************************************************************
+ * Active socket callbacks.
+ *******************************************************************
+ */
+
+static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
+ void *data,
+ pj_size_t size,
+ pj_status_t status,
+ pj_size_t *remainder)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+ pj_activesock_get_user_data(asock);
+ pj_size_t nwritten;
+
+ /* Socket error or closed */
+ if (data == NULL || size < 0)
+ goto on_error;
+
+ /* Consume the whole data */
+ nwritten = BIO_write(ssock->ossl_rbio, data, size);
+ if (nwritten < size) {
+ status = PJ_ENOMEM;
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "BIO_write()");
+ goto on_error;
+ }
+
+ /* Check if SSL handshake hasn't finished yet */
+ if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
+ pj_bool_t ret = PJ_TRUE;
+
+ if (status == PJ_SUCCESS)
+ status = do_handshake(ssock);
+
+ /* Not pending is either success or failed */
+ if (status != PJ_EPENDING)
+ ret = on_handshake_complete(ssock, status);
+
+ return ret;
+ }
+
+ /* See if there is any decrypted data for the application */
+ if (ssock->read_started) {
+ do {
+ read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
+ void *data_ = (pj_int8_t*)buf->data + buf->len;
+ int size_ = ssock->read_size - buf->len;
+
+ /* SSL_read() may write some data to BIO write when re-negotiation
+ * is on progress, so let's protect it with write mutex.
+ */
+ pj_lock_acquire(ssock->write_mutex);
+
+ size_ = SSL_read(ssock->ossl_ssl, data_, size_);
+ if (size_ > 0) {
+ pj_lock_release(ssock->write_mutex);
+ if (ssock->param.cb.on_data_read) {
+ pj_bool_t ret;
+ pj_size_t remainder_ = 0;
+
+ buf->len += size_;
+
+ ret = (*ssock->param.cb.on_data_read)(ssock, buf->data,
+ buf->len, status,
+ &remainder_);
+ if (!ret) {
+ /* We've been destroyed */
+ return PJ_FALSE;
+ }
+
+ /* Application may have left some data to be consumed
+ * later.
+ */
+ buf->len = remainder_;
+ }
+ } else {
+ int err = SSL_get_error(ssock->ossl_ssl, size);
+
+ /* SSL might just return SSL_ERROR_WANT_READ in
+ * re-negotiation.
+ */
+ if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ)
+ {
+ /* Reset SSL socket state, then return PJ_FALSE */
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "SSL_read()");
+ pj_lock_release(ssock->write_mutex);
+ reset_ssl_sock_state(ssock);
+ return PJ_FALSE;
+ }
+
+ /* SSL may write something in case of re-negotiation */
+ status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
+ pj_lock_release(ssock->write_mutex);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING)
+ goto on_error;
+
+ /* If re-negotiation has been completed, start flushing
+ * delayed send.
+ */
+ if (!SSL_renegotiate_pending(ssock->ossl_ssl)) {
+ status = flush_delayed_send(ssock);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING)
+ goto on_error;
+ }
+
+ break;
+ }
+ } while (1);
+ }
+
+ return PJ_TRUE;
+
+on_error:
+ if (ssock->ssl_state == SSL_STATE_HANDSHAKING)
+ return on_handshake_complete(ssock, status);
+
+ if (ssock->read_started && ssock->param.cb.on_data_read) {
+ pj_bool_t ret;
+ ret = (*ssock->param.cb.on_data_read)(ssock, NULL, 0, status,
+ remainder);
+ if (!ret) {
+ /* We've been destroyed */
+ return PJ_FALSE;
+ }
+ }
+
+ reset_ssl_sock_state(ssock);
+ return PJ_FALSE;
+}
+
+
+static pj_bool_t asock_on_data_sent (pj_activesock_t *asock,
+ pj_ioqueue_op_key_t *send_key,
+ pj_ssize_t sent)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+ pj_activesock_get_user_data(asock);
+
+ PJ_UNUSED_ARG(send_key);
+ PJ_UNUSED_ARG(sent);
+
+ if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
+ /* Initial handshaking */
+ pj_status_t status;
+
+ status = do_handshake(ssock);
+ /* Not pending is either success or failed */
+ if (status != PJ_EPENDING)
+ return on_handshake_complete(ssock, status);
+
+ } else if (send_key != &ssock->handshake_op_key) {
+ /* Some data has been sent, notify application */
+ write_data_t *wdata = (write_data_t*)send_key;
+ if (ssock->param.cb.on_data_sent) {
+ pj_bool_t ret;
+ ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key,
+ wdata->plain_data_len);
+ if (!ret) {
+ /* We've been destroyed */
+ return PJ_FALSE;
+ }
+ }
+
+ /* Update write buffer state */
+ pj_lock_acquire(ssock->write_mutex);
+ ssock->write_state.start += wdata->record_len;
+ ssock->write_state.len -= wdata->record_len;
+ if (ssock->write_state.last_data == wdata) {
+ pj_assert(ssock->write_state.len == 0);
+ ssock->write_state.last_data = NULL;
+ }
+ pj_lock_release(ssock->write_mutex);
+
+ } else {
+ /* SSL re-negotiation is on-progress, just do nothing */
+ }
+
+ return PJ_TRUE;
+}
+
+
+static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
+ pj_sock_t newsock,
+ const pj_sockaddr_t *src_addr,
+ int src_addr_len)
+{
+ pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t*)
+ pj_activesock_get_user_data(asock);
+ pj_ssl_sock_t *ssock;
+ pj_activesock_cb asock_cb;
+ pj_activesock_cfg asock_cfg;
+ unsigned i;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(src_addr_len);
+
+ /* Create new SSL socket instance */
+ status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param,
+ &ssock);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Update new SSL socket attributes */
+ ssock->sock = newsock;
+ ssock->parent = ssock_parent;
+ ssock->is_server = PJ_TRUE;
+ if (ssock_parent->cert) {
+ status = pj_ssl_sock_set_certificate(ssock, ssock->pool,
+ ssock_parent->cert);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+ }
+
+ /* Update local address */
+ ssock->addr_len = src_addr_len;
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Set remote address */
+ pj_sockaddr_cp(&ssock->rem_addr, src_addr);
+
+ /* Create SSL context */
+ status = create_ssl_ctx(ssock, &ssock->ossl_ctx);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Create SSL instance */
+ ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
+ if (ssock->ossl_ssl == NULL) {
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "Error creating SSL connection object");
+ status = PJ_EINVAL;
+ goto on_return;
+ }
+
+ /* Set cipher list */
+ status = set_cipher_list(ssock);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Setup SSL BIOs */
+ ssock->ossl_rbio = BIO_new(BIO_s_mem());
+ ssock->ossl_wbio = BIO_new(BIO_s_mem());
+ BIO_set_close(ssock->ossl_rbio, BIO_CLOSE);
+ BIO_set_close(ssock->ossl_wbio, BIO_CLOSE);
+ SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio);
+
+ /* Prepare read buffer */
+ ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool,
+ ssock->param.async_cnt,
+ sizeof(void*));
+ for (i = 0; i<ssock->param.async_cnt; ++i) {
+ ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
+ ssock->pool,
+ ssock->param.read_buffer_size +
+ sizeof(read_data_t*));
+ }
+
+ /* Create active socket */
+ pj_activesock_cfg_default(&asock_cfg);
+ asock_cfg.async_cnt = ssock->param.async_cnt;
+ asock_cfg.concurrency = ssock->param.concurrency;
+ asock_cfg.whole_data = PJ_TRUE;
+
+ pj_bzero(&asock_cb, sizeof(asock_cb));
+ asock_cb.on_data_read = asock_on_data_read;
+ asock_cb.on_data_sent = asock_on_data_sent;
+
+ status = pj_activesock_create(ssock->pool,
+ ssock->sock,
+ ssock->param.sock_type,
+ &asock_cfg,
+ ssock->param.ioqueue,
+ &asock_cb,
+ ssock,
+ &ssock->asock);
+
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Start read */
+ status = pj_activesock_start_read2(ssock->asock, ssock->pool,
+ ssock->param.read_buffer_size,
+ ssock->asock_rbuf,
+ PJ_IOQUEUE_ALWAYS_ASYNC);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Prepare write/send state */
+ pj_assert(ssock->write_state.max_len == 0);
+ ssock->write_state.buf = (char*)
+ pj_pool_alloc(ssock->pool,
+ ssock->param.send_buffer_size);
+ ssock->write_state.max_len = ssock->param.send_buffer_size;
+ ssock->write_state.start = ssock->write_state.buf;
+ ssock->write_state.len = 0;
+
+ /* Start SSL handshake */
+ ssock->ssl_state = SSL_STATE_HANDSHAKING;
+ SSL_set_accept_state(ssock->ossl_ssl);
+ status = do_handshake(ssock);
+ if (status != PJ_EPENDING)
+ goto on_return;
+
+ return PJ_TRUE;
+
+on_return:
+ return on_handshake_complete(ssock, status);
+}
+
+
+static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
+ pj_status_t status)
+{
+ pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+ pj_activesock_get_user_data(asock);
+ unsigned i;
+
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Update local address */
+ ssock->addr_len = sizeof(pj_sockaddr);
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Create SSL context */
+ status = create_ssl_ctx(ssock, &ssock->ossl_ctx);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Create SSL instance */
+ ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
+ if (ssock->ossl_ssl == NULL) {
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS,
+ "Error creating SSL connection object");
+ status = PJ_EINVAL;
+ goto on_return;
+ }
+
+ /* Set cipher list */
+ status = set_cipher_list(ssock);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Setup SSL BIOs */
+ ssock->ossl_rbio = BIO_new(BIO_s_mem());
+ ssock->ossl_wbio = BIO_new(BIO_s_mem());
+ BIO_set_close(ssock->ossl_rbio, BIO_CLOSE);
+ BIO_set_close(ssock->ossl_wbio, BIO_CLOSE);
+ SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio);
+
+ /* Prepare read buffer */
+ ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool,
+ ssock->param.async_cnt,
+ sizeof(void*));
+ for (i = 0; i<ssock->param.async_cnt; ++i) {
+ ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
+ ssock->pool,
+ ssock->param.read_buffer_size +
+ sizeof(read_data_t*));
+ }
+
+ /* Start read */
+ status = pj_activesock_start_read2(ssock->asock, ssock->pool,
+ ssock->param.read_buffer_size,
+ ssock->asock_rbuf,
+ PJ_IOQUEUE_ALWAYS_ASYNC);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Prepare write/send state */
+ pj_assert(ssock->write_state.max_len == 0);
+ ssock->write_state.buf = (char*)
+ pj_pool_alloc(ssock->pool,
+ ssock->param.send_buffer_size);
+ ssock->write_state.max_len = ssock->param.send_buffer_size;
+ ssock->write_state.start = ssock->write_state.buf;
+ ssock->write_state.len = 0;
+
+ /* Start SSL handshake */
+ ssock->ssl_state = SSL_STATE_HANDSHAKING;
+ SSL_set_connect_state(ssock->ossl_ssl);
+ status = do_handshake(ssock);
+ if (status != PJ_EPENDING)
+ goto on_return;
+
+ return PJ_TRUE;
+
+on_return:
+ return on_handshake_complete(ssock, status);
+}
+
+
+
+/*
+ *******************************************************************
+ * API
+ *******************************************************************
+ */
+
+/* Load credentials from files. */
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files (pj_pool_t *pool,
+ const pj_str_t *CA_file,
+ const pj_str_t *cert_file,
+ const pj_str_t *privkey_file,
+ const pj_str_t *privkey_pass,
+ pj_ssl_cert_t **p_cert)
+{
+ pj_ssl_cert_t *cert;
+
+ PJ_ASSERT_RETURN(pool && CA_file && cert_file && privkey_file, PJ_EINVAL);
+
+ cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+ pj_strdup_with_null(pool, &cert->CA_file, CA_file);
+ pj_strdup_with_null(pool, &cert->cert_file, cert_file);
+ pj_strdup_with_null(pool, &cert->privkey_file, privkey_file);
+ pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
+
+ *p_cert = cert;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Set SSL socket credentials. */
+PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(
+ pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ const pj_ssl_cert_t *cert)
+{
+ PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL);
+
+ ssock->cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+ pj_strdup_with_null(pool, &ssock->cert->CA_file, &cert->CA_file);
+ pj_strdup_with_null(pool, &ssock->cert->cert_file, &cert->cert_file);
+ pj_strdup_with_null(pool, &ssock->cert->privkey_file, &cert->privkey_file);
+ pj_strdup_with_null(pool, &ssock->cert->privkey_pass, &cert->privkey_pass);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Get available ciphers. */
+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
+ unsigned *cipher_num)
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
+
+ if (openssl_cipher_num == 0) {
+ init_openssl();
+ shutdown_openssl();
+ }
+
+ if (openssl_cipher_num == 0)
+ return PJ_ENOTFOUND;
+
+ *cipher_num = PJ_MIN(*cipher_num, openssl_cipher_num);
+
+ for (i = 0; i < *cipher_num; ++i)
+ ciphers[i] = openssl_ciphers[i];
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Create SSL socket instance.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
+ const pj_ssl_sock_param *param,
+ pj_ssl_sock_t **p_ssock)
+{
+ pj_ssl_sock_t *ssock;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP);
+
+ pool = pj_pool_create(pool->factory, "ssl%p", 512, 512, NULL);
+
+ /* Create secure socket */
+ ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
+ ssock->pool = pool;
+ ssock->sock = PJ_INVALID_SOCKET;
+ ssock->ssl_state = SSL_STATE_NULL;
+ pj_list_init(&ssock->write_pending);
+ pj_list_init(&ssock->write_pending_empty);
+
+ /* Create secure socket mutex */
+ status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
+ &ssock->write_mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init secure socket param */
+ ssock->param = *param;
+ ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3;
+ if (param->ciphers_num > 0) {
+ unsigned i;
+ ssock->param.ciphers = (pj_ssl_cipher*)
+ pj_pool_calloc(pool, param->ciphers_num,
+ sizeof(pj_ssl_cipher));
+ for (i = 0; i < param->ciphers_num; ++i)
+ ssock->param.ciphers[i] = param->ciphers[i];
+ }
+ pj_strdup_with_null(pool, &ssock->param.servername,
+ &param->servername);
+
+ /* Finally */
+ *p_ssock = ssock;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Close the secure socket. This will unregister the socket from the
+ * ioqueue and ultimately close the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
+{
+ pj_pool_t *pool;
+
+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+ reset_ssl_sock_state(ssock);
+ pj_lock_destroy(ssock->write_mutex);
+
+ pool = ssock->pool;
+ ssock->pool = NULL;
+ if (pool)
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Associate arbitrary data with the secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
+ void *user_data)
+{
+ PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+ ssock->param.user_data = user_data;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Retrieve the user data previously associated with this secure
+ * socket.
+ */
+PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
+{
+ PJ_ASSERT_RETURN(ssock, NULL);
+
+ return ssock->param.user_data;
+}
+
+
+/*
+ * Retrieve the local address and port used by specified SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
+ pj_ssl_sock_info *info)
+{
+ pj_bzero(info, sizeof(*info));
+
+ /* Established flag */
+ info->established = (ssock->ssl_state == SSL_STATE_ESTABLISHED);
+
+ /* Protocol */
+ info->proto = ssock->param.proto;
+
+ /* Local address */
+ pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
+
+ if (info->established) {
+ /* Current cipher */
+ const SSL_CIPHER *cipher;
+
+ cipher = SSL_get_current_cipher(ssock->ossl_ssl);
+ info->cipher = (cipher->id & 0x00FFFFFF);
+
+ /* Remote address */
+ pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Starts read operation on this secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ pj_uint32_t flags)
+{
+ void **readbuf;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+
+ readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt,
+ sizeof(void*));
+
+ for (i=0; i<ssock->param.async_cnt; ++i) {
+ readbuf[i] = pj_pool_alloc(pool, buff_size);
+ }
+
+ return pj_ssl_sock_start_read2(ssock, pool, buff_size,
+ readbuf, flags);
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_read(), except that the application
+ * supplies the buffers for the read operation so that the acive socket
+ * does not have to allocate the buffers.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ void *readbuf[],
+ pj_uint32_t flags)
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+
+ /* Create SSL socket read buffer */
+ ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool,
+ ssock->param.async_cnt,
+ sizeof(read_data_t));
+
+ /* Store SSL socket read buffer pointer in the activesock read buffer */
+ for (i=0; i<ssock->param.async_cnt; ++i) {
+ read_data_t **p_ssock_rbuf =
+ OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]);
+
+ ssock->ssock_rbuf[i].data = readbuf[i];
+ ssock->ssock_rbuf[i].len = 0;
+
+ *p_ssock_rbuf = &ssock->ssock_rbuf[i];
+ }
+
+ ssock->read_size = buff_size;
+ ssock->read_started = PJ_TRUE;
+ ssock->read_flags = flags;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Same as pj_ssl_sock_start_read(), except that this function is used
+ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
+ * callback instead.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ pj_uint32_t flags)
+{
+ PJ_UNUSED_ARG(ssock);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(buff_size);
+ PJ_UNUSED_ARG(flags);
+
+ return PJ_ENOTSUP;
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom()
+ * operation takes the buffer from the argument rather than creating
+ * new ones.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ unsigned buff_size,
+ void *readbuf[],
+ pj_uint32_t flags)
+{
+ PJ_UNUSED_ARG(ssock);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(buff_size);
+ PJ_UNUSED_ARG(readbuf);
+ PJ_UNUSED_ARG(flags);
+
+ return PJ_ENOTSUP;
+}
+
+/* Write plain data to SSL and flush write BIO. Note that accessing
+ * write BIO must be serialized, so a call to this function must be
+ * protected by write mutex of SSL socket.
+ */
+static pj_status_t ssl_write(pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data,
+ pj_ssize_t size,
+ unsigned flags)
+{
+ pj_status_t status;
+ int nwritten;
+
+ /* Write the plain data to SSL, after SSL encrypts it, write BIO will
+ * contain the secured data to be sent via socket. Note that re-
+ * negotitation may be on progress, so sending data should be delayed
+ * until re-negotiation is completed.
+ */
+ nwritten = SSL_write(ssock->ossl_ssl, data, size);
+
+ if (nwritten == size) {
+ /* All data written, flush write BIO to network socket */
+ status = flush_write_bio(ssock, send_key, size, flags);
+ } else if (nwritten <= 0) {
+ /* SSL failed to process the data, it may just that re-negotiation
+ * is on progress.
+ */
+ int err;
+ err = SSL_get_error(ssock->ossl_ssl, nwritten);
+ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) {
+ /* Re-negotiation is on progress, flush re-negotiation data */
+ status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
+ if (status == PJ_SUCCESS || status == PJ_EPENDING)
+ status = PJ_EBUSY;
+ } else {
+ /* Some problem occured */
+ ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "SSL_write()");
+ status = PJ_ECANCELLED;
+ }
+ } else {
+ /* nwritten < *size, shouldn't happen, unless write BIO cannot hold
+ * the whole secured data, perhaps because of insufficient memory.
+ */
+ status = PJ_ENOMEM;
+ }
+
+ return status;
+}
+
+/* Flush delayed data sending in the write pending list. Note that accessing
+ * write pending list must be serialized, so a call to this function must be
+ * protected by write mutex of SSL socket.
+ */
+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
+{
+ while (!pj_list_empty(&ssock->write_pending)) {
+ write_pending_t *wp;
+ pj_status_t status;
+
+ wp = ssock->write_pending.next;
+
+ status = ssl_write(ssock, &wp->data.key, wp->data.data.ptr,
+ wp->data.plain_data_len, wp->data.flags);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_list_erase(wp);
+ pj_list_push_back(&ssock->write_pending_empty, wp);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Sending is delayed, push back the sending data into pending list. Note that
+ * accessing write pending list must be serialized, so a call to this function
+ * must be protected by write mutex of SSL socket.
+ */
+static pj_status_t delay_send (pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data,
+ pj_ssize_t size,
+ unsigned flags)
+{
+ write_pending_t *wp;
+
+ /* Init write pending instance */
+ if (!pj_list_empty(&ssock->write_pending_empty)) {
+ wp = ssock->write_pending_empty.next;
+ pj_list_erase(wp);
+ } else {
+ wp = PJ_POOL_ZALLOC_T(ssock->pool, write_pending_t);
+ }
+
+ wp->data.app_key = send_key;
+ wp->data.plain_data_len = size;
+ wp->data.data.ptr = data;
+ wp->data.flags = flags;
+
+ pj_list_push_back(&ssock->write_pending, wp);
+
+ /* Must return PJ_EPENDING */
+ return PJ_EPENDING;
+}
+
+/**
+ * Send data using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data,
+ pj_ssize_t *size,
+ unsigned flags)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL);
+ PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+
+ pj_lock_acquire(ssock->write_mutex);
+
+ /* Flush delayed send first. Sending data might be delayed when
+ * re-negotiation is on-progress.
+ */
+ status = flush_delayed_send(ssock);
+ if (status == PJ_EBUSY) {
+ /* Re-negotiation is on progress, delay sending */
+ status = delay_send(ssock, send_key, data, *size, flags);
+ goto on_return;
+ } else if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Write data to SSL */
+ status = ssl_write(ssock, send_key, data, *size, flags);
+ if (status == PJ_EBUSY) {
+ /* Re-negotiation is on progress, delay sending */
+ status = delay_send(ssock, send_key, data, *size, flags);
+ }
+
+on_return:
+ pj_lock_release(ssock->write_mutex);
+ return status;
+}
+
+
+/**
+ * Send datagram using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *send_key,
+ const void *data,
+ pj_ssize_t *size,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addr_len)
+{
+ PJ_UNUSED_ARG(ssock);
+ PJ_UNUSED_ARG(send_key);
+ PJ_UNUSED_ARG(data);
+ PJ_UNUSED_ARG(size);
+ PJ_UNUSED_ARG(flags);
+ PJ_UNUSED_ARG(addr);
+ PJ_UNUSED_ARG(addr_len);
+
+ return PJ_ENOTSUP;
+}
+
+
+/**
+ * Starts asynchronous socket accept() operations on this secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ const pj_sockaddr_t *localaddr,
+ int addr_len)
+{
+ pj_activesock_cb asock_cb;
+ pj_activesock_cfg asock_cfg;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
+
+ /* Create socket */
+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
+ &ssock->sock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Bind socket */
+ status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Start listening to the address */
+ status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create active socket */
+ pj_activesock_cfg_default(&asock_cfg);
+ asock_cfg.async_cnt = ssock->param.async_cnt;
+ asock_cfg.concurrency = ssock->param.concurrency;
+ asock_cfg.whole_data = PJ_TRUE;
+
+ pj_bzero(&asock_cb, sizeof(asock_cb));
+ asock_cb.on_accept_complete = asock_on_accept_complete;
+
+ status = pj_activesock_create(pool,
+ ssock->sock,
+ ssock->param.sock_type,
+ &asock_cfg,
+ ssock->param.ioqueue,
+ &asock_cb,
+ ssock,
+ &ssock->asock);
+
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Start accepting */
+ status = pj_activesock_start_accept(ssock->asock, pool);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Update local address */
+ ssock->addr_len = addr_len;
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ if (status != PJ_SUCCESS)
+ pj_sockaddr_cp(&ssock->local_addr, localaddr);
+
+ ssock->is_server = PJ_TRUE;
+
+ return PJ_SUCCESS;
+
+on_error:
+ reset_ssl_sock_state(ssock);
+ return status;
+}
+
+
+/**
+ * Starts asynchronous socket connect() operation.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock,
+ pj_pool_t *pool,
+ const pj_sockaddr_t *localaddr,
+ const pj_sockaddr_t *remaddr,
+ int addr_len)
+{
+ pj_activesock_cb asock_cb;
+ pj_activesock_cfg asock_cfg;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
+ PJ_EINVAL);
+
+ /* Create socket */
+ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
+ &ssock->sock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Bind socket */
+ status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create active socket */
+ pj_activesock_cfg_default(&asock_cfg);
+ asock_cfg.async_cnt = ssock->param.async_cnt;
+ asock_cfg.concurrency = ssock->param.concurrency;
+ asock_cfg.whole_data = PJ_TRUE;
+
+ pj_bzero(&asock_cb, sizeof(asock_cb));
+ asock_cb.on_connect_complete = asock_on_connect_complete;
+ asock_cb.on_data_read = asock_on_data_read;
+ asock_cb.on_data_sent = asock_on_data_sent;
+
+ status = pj_activesock_create(pool,
+ ssock->sock,
+ ssock->param.sock_type,
+ &asock_cfg,
+ ssock->param.ioqueue,
+ &asock_cb,
+ ssock,
+ &ssock->asock);
+
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ status = pj_activesock_start_connect(ssock->asock, pool, remaddr,
+ addr_len);
+
+ if (status == PJ_SUCCESS)
+ asock_on_connect_complete(ssock->asock, PJ_SUCCESS);
+ else if (status != PJ_EPENDING)
+ goto on_error;
+
+ /* Update local address */
+ ssock->addr_len = addr_len;
+ status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+ &ssock->addr_len);
+ if (status != PJ_SUCCESS)
+ pj_sockaddr_cp(&ssock->local_addr, localaddr);
+
+ /* Set remote address */
+ pj_sockaddr_cp(&ssock->rem_addr, remaddr);
+
+ /* Update SSL state */
+ ssock->is_server = PJ_FALSE;
+
+ return PJ_EPENDING;
+
+on_error:
+ reset_ssl_sock_state(ssock);
+ return status;
+}
+
+
+//#endif /* PJ_HAS_SSL_SOCK */
+
diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp
index 41d3e159..06165f37 100644
--- a/pjlib/src/pj/ssl_sock_symbian.cpp
+++ b/pjlib/src/pj/ssl_sock_symbian.cpp
@@ -20,6 +20,7 @@
#include <pj/compat/socket.h>
#include <pj/assert.h>
#include <pj/errno.h>
+#include <pj/math.h>
#include <pj/pool.h>
#include <pj/sock.h>
#include <pj/string.h>
@@ -413,12 +414,57 @@ struct pj_ssl_sock_t
pj_ssl_sock_proto proto;
pj_time_val timeout;
- pj_str_t ciphers;
+ unsigned ciphers_num;
+ pj_ssl_cipher *ciphers;
pj_str_t servername;
};
/*
+ * Get cipher list supported by SSL/TLS backend.
+ */
+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[],
+ unsigned *cipher_num)
+{
+ /* Available ciphers */
+ static pj_ssl_cipher ciphers_[64];
+ static unsigned ciphers_num_ = 0;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
+
+ if (ciphers_num_ == 0) {
+ RSocket sock;
+ CSecureSocket *secure_sock;
+ TPtrC16 proto(_L16("TLS1.0"));
+
+ secure_sock = CSecureSocket::NewL(sock, proto);
+ if (secure_sock) {
+ TBuf8<128> ciphers_buf(0);
+ secure_sock->AvailableCipherSuites(ciphers_buf);
+
+ ciphers_num_ = ciphers_buf.Length() / 2;
+ if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_))
+ ciphers_num_ = PJ_ARRAY_SIZE(ciphers_);
+ for (i = 0; i < ciphers_num_; ++i)
+ ciphers_[i] = (pj_ssl_cipher)ciphers_buf[i*2];
+ }
+
+ delete secure_sock;
+ }
+
+ if (ciphers_num_ == 0) {
+ return PJ_ENOTFOUND;
+ }
+
+ *cipher_num = PJ_MIN(*cipher_num, ciphers_num_);
+ for (i = 0; i < *cipher_num; ++i)
+ ciphers[i] = ciphers_[i];
+
+ return PJ_SUCCESS;
+}
+
+/*
* Create SSL socket instance.
*/
PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
@@ -444,7 +490,15 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
ssock->sock_type = param->sock_type;
ssock->cb = param->cb;
ssock->user_data = param->user_data;
- pj_strdup_with_null(pool, &ssock->ciphers, &param->ciphers);
+ ssock->ciphers_num = param->ciphers_num;
+ if (param->ciphers_num > 0) {
+ unsigned i;
+ ssock->ciphers = (pj_ssl_cipher*)
+ pj_pool_calloc(pool, param->ciphers_num,
+ sizeof(pj_ssl_cipher));
+ for (i = 0; i < param->ciphers_num; ++i)
+ ssock->ciphers[i] = param->ciphers[i];
+ }
pj_strdup_with_null(pool, &ssock->servername, &param->servername);
/* Finally */
@@ -453,6 +507,23 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
return PJ_SUCCESS;
}
+
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
+ const pj_str_t *CA_file,
+ const pj_str_t *cert_file,
+ const pj_str_t *privkey_file,
+ const pj_str_t *privkey_pass,
+ pj_ssl_cert_t **p_cert)
+{
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(CA_file);
+ PJ_UNUSED_ARG(cert_file);
+ PJ_UNUSED_ARG(privkey_file);
+ PJ_UNUSED_ARG(privkey_pass);
+ PJ_UNUSED_ARG(p_cert);
+ return PJ_ENOTSUP;
+}
+
/*
* Set SSL socket credential.
*/
@@ -521,36 +592,6 @@ PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
pj_ssl_sock_info *info)
{
- const char *cipher_names[0x1B] = {
- "TLS_RSA_WITH_NULL_MD5",
- "TLS_RSA_WITH_NULL_SHA",
- "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
- "TLS_RSA_WITH_RC4_128_MD5",
- "TLS_RSA_WITH_RC4_128_SHA",
- "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
- "TLS_RSA_WITH_IDEA_CBC_SHA",
- "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_RSA_WITH_DES_CBC_SHA",
- "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DH_DSS_WITH_DES_CBC_SHA",
- "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DH_RSA_WITH_DES_CBC_SHA",
- "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DHE_DSS_WITH_DES_CBC_SHA",
- "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DHE_RSA_WITH_DES_CBC_SHA",
- "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
- "TLS_DH_anon_WITH_RC4_128_MD5",
- "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DH_anon_WITH_DES_CBC_SHA",
- "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"
- };
-
PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL);
pj_bzero(info, sizeof(*info));
@@ -570,19 +611,16 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
}
- /* Remote address */
- pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr,
- (pj_sockaddr_t*)&ssock->rem_addr);
-
- /* Cipher suite */
if (info->established) {
- TBuf8<8> cipher;
+ /* Cipher suite */
+ TBuf8<4> cipher;
if (ssock->sock->GetCipher(cipher) == KErrNone) {
- TLex8 lex(cipher);
- TUint cipher_code = cipher[1];
- if (cipher_code>=1 && cipher_code<=0x1B)
- info->cipher = pj_str((char*)cipher_names[cipher_code-1]);
+ info->cipher = (pj_ssl_cipher)cipher[1];
}
+
+ /* Remote address */
+ pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr,
+ (pj_sockaddr_t*)&ssock->rem_addr);
}
/* Protocol */
diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
index 2d58f751..927de70c 100644
--- a/pjsip/src/pjsip/sip_transport_tls.c
+++ b/pjsip/src/pjsip/sip_transport_tls.c
@@ -240,13 +240,18 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
ssock_param.cb.on_data_read = &on_data_read;
ssock_param.cb.on_data_sent = &on_data_sent;
ssock_param.async_cnt = async_cnt;
- ssock_param.ciphers = listener->tls_setting.ciphers;
ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt);
ssock_param.require_client_cert = listener->tls_setting.require_client_cert;
ssock_param.servername = listener->tls_setting.server_name;
ssock_param.timeout = listener->tls_setting.timeout;
ssock_param.user_data = listener;
ssock_param.verify_peer = listener->tls_setting.verify_client;
+ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN)
+ ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN;
+ if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN)
+ ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN;
+
+ has_listener = PJ_FALSE;
switch(listener->tls_setting.method) {
case PJSIP_TLSV1_METHOD:
@@ -279,10 +284,30 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
pj_sockaddr_in_init(listener_addr, NULL, 0);
}
+ /* Check if certificate for SSL socket is set */
+ if (listener->tls_setting.cert_file.slen)
+ {
+ pj_ssl_cert_t *cert;
+
+ status = pj_ssl_cert_load_from_files(pool,
+ &listener->tls_setting.ca_list_file,
+ &listener->tls_setting.cert_file,
+ &listener->tls_setting.privkey_file,
+ &listener->tls_setting.password,
+ &cert);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ status = pj_ssl_sock_set_certificate(listener->ssock, pool, cert);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
/* Start accepting incoming connections. Note that some TLS/SSL backends
* may not support for SSL socket server.
*/
has_listener = PJ_FALSE;
+
status = pj_ssl_sock_start_accept(listener->ssock, pool,
(pj_sockaddr_t*)listener_addr,
pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr));
@@ -351,12 +376,17 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
goto on_error;
}
- PJ_LOG(4,(listener->factory.obj_name,
- "SIP TLS listener is ready%s at %.*s:%d",
- (has_listener?" for incoming connections":""),
- (int)listener->factory.addr_name.host.slen,
- listener->factory.addr_name.host.ptr,
- listener->factory.addr_name.port));
+ if (has_listener) {
+ PJ_LOG(4,(listener->factory.obj_name,
+ "SIP TLS listener is 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));
+ } else {
+ PJ_LOG(4,(listener->factory.obj_name, "SIP TLS is ready "
+ "(client only)"));
+ }
/* Return the pointer to user */
if (p_factory) *p_factory = &listener->factory;
@@ -756,14 +786,18 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
ssock_param.cb.on_data_read = &on_data_read;
ssock_param.cb.on_data_sent = &on_data_sent;
ssock_param.async_cnt = 1;
- ssock_param.ciphers = listener->tls_setting.ciphers;
ssock_param.ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
- PJ_TODO(SET_PROPER_SERVERNAME_BASED_ON_TARGET);
+ PJ_TODO(set_proper_servername_based_on_target);
+ PJ_TODO(synchronize_tls_cipher_type_with_ssl_sock_cipher_type);
ssock_param.servername = listener->tls_setting.server_name;
ssock_param.timeout = listener->tls_setting.timeout;
ssock_param.user_data = NULL; /* pending, must be set later */
ssock_param.verify_peer = listener->tls_setting.verify_server;
-
+ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN)
+ ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN;
+ if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN)
+ ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN;
+
switch(listener->tls_setting.method) {
case PJSIP_TLSV1_METHOD:
ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1;
@@ -893,7 +927,11 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock,
status = tls_create( listener, NULL, new_ssock, PJ_TRUE,
(const pj_sockaddr_in*)&listener->factory.local_addr,
(const pj_sockaddr_in*)src_addr, &tls);
+
if (status == PJ_SUCCESS) {
+ /* Set the "pending" SSL socket user data */
+ pj_ssl_sock_set_user_data(new_ssock, tls);
+
status = tls_start_read(tls);
if (status != PJ_SUCCESS) {
PJ_LOG(3,(tls->base.obj_name, "New transport cancelled"));
@@ -1172,9 +1210,6 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock);
- /* Mark that pending connect() operation has completed. */
- tls->has_pending_connect = PJ_FALSE;
-
/* Check connect() status */
if (status != PJ_SUCCESS) {
@@ -1203,16 +1238,6 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
return PJ_FALSE;
}
- PJ_LOG(4,(tls->base.obj_name,
- "TLS 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?).
@@ -1233,6 +1258,18 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
}
}
+ PJ_LOG(4,(tls->base.obj_name,
+ "TLS 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));
+
+ /* 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) {
@@ -1261,6 +1298,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
return PJ_TRUE;
}
+
/* Transport keep-alive timer callback */
static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
{