summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2010-02-24 05:43:34 +0000
committerNanang Izzuddin <nanang@teluu.com>2010-02-24 05:43:34 +0000
commitbb2fc905eb58b9ebdf66e89330599be996821db7 (patch)
treef6bedef48655a824a1393efbb667a3b8af560b63
parentdf622f00fa10e2cbcde9df6169ad628fe3e72226 (diff)
Ticket #1032:
- Initial version of server domain name verification: - Updated SSL certificate info, especially identities info - Updated verification mechanism as in the specifications in ticket desc. - Added server domain name info in pjsip_tx_data. - Added alternative API for acquiring transport and creating transport of transport factory to include pjsip_tx_data param. - Server identity match criteria: - full host name match - wild card not accepted - if identity is URI, it must be SIP/SIPS URI - Initial version of transport state notifications: - Added new API to set transport state callback in PJSIP and PJSUA. - Defined states: connected/disconnected, accepted/rejected, verification errors. - Minors: - Updated SSL socket test: dump verification result, test of requiring client cert, and few minors. - Updated test cert to include subjectAltName extensions. - Added SSL certificate dump function. - Updated max number of socket async operations in Symbian sample apps (RSocketServ::Connect()) to 32 (was default 8). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3106 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--build.symbian/pjlib.mmp3
-rw-r--r--build.symbian/pjlib_test.mmp4
-rw-r--r--build.symbian/symbian_ua.mmp3
-rw-r--r--pjlib/build/Makefile3
-rw-r--r--pjlib/build/cacert.pem31
-rw-r--r--pjlib/build/pjlib.dsp4
-rw-r--r--pjlib/build/pjlib.vcproj4
-rw-r--r--pjlib/build/privkey.pem38
-rw-r--r--pjlib/include/pj/ssl_sock.h177
-rw-r--r--pjlib/src/pj/ssl_sock_common.c95
-rw-r--r--pjlib/src/pj/ssl_sock_dump.c147
-rw-r--r--pjlib/src/pj/ssl_sock_ossl.c414
-rw-r--r--pjlib/src/pj/ssl_sock_symbian.cpp152
-rw-r--r--pjlib/src/pjlib-test/ssl_sock.c213
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c80
-rw-r--r--pjsip-apps/src/symbian_ua/ua.cpp60
-rw-r--r--pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp1
-rw-r--r--pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp2
-rw-r--r--pjsip/include/pjsip/sip_endpoint.h29
-rw-r--r--pjsip/include/pjsip/sip_transport.h143
-rw-r--r--pjsip/include/pjsip/sip_transport_tls.h64
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h19
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c16
-rw-r--r--pjsip/src/pjsip/sip_transport.c94
-rw-r--r--pjsip/src/pjsip/sip_transport_tcp.c87
-rw-r--r--pjsip/src/pjsip/sip_transport_tls.c336
-rw-r--r--pjsip/src/pjsip/sip_util.c20
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c8
28 files changed, 1837 insertions, 410 deletions
diff --git a/build.symbian/pjlib.mmp b/build.symbian/pjlib.mmp
index 65ffcc5a..3765e0f5 100644
--- a/build.symbian/pjlib.mmp
+++ b/build.symbian/pjlib.mmp
@@ -49,6 +49,8 @@ SOURCE pool_buf.c
SOURCE pool_caching.c
SOURCE rand.c
SOURCE rbtree.c
+SOURCE ssl_sock_common.c
+SOURCE ssl_sock_dump.c
SOURCE sock_common.c
SOURCE sock_qos_common.c
SOURCE types.c
@@ -72,7 +74,6 @@ SOURCE os_timestamp_common.c
SOURCE os_time_unix.c
SOURCE os_timestamp_posix.c
SOURCE pool_policy_new.cpp
-SOURCE ssl_sock_common.c
SOURCE ssl_sock_symbian.cpp
SOURCE sock_symbian.cpp
SOURCE sock_select_symbian.cpp
diff --git a/build.symbian/pjlib_test.mmp b/build.symbian/pjlib_test.mmp
index e48a332c..65b654cf 100644
--- a/build.symbian/pjlib_test.mmp
+++ b/build.symbian/pjlib_test.mmp
@@ -69,7 +69,9 @@ SYSTEMINCLUDE ..\pjlib\include
SYSTEMINCLUDE \epoc32\include
SYSTEMINCLUDE \epoc32\include\libc
-LIBRARY esock.lib insock.lib charconv.lib euser.lib estlib.lib securesocket.lib
+LIBRARY esock.lib insock.lib charconv.lib euser.lib estlib.lib
+LIBRARY securesocket.lib x509.lib crypto.lib x500.lib
+
#ifdef WINSCW
STATICLIBRARY eexe.lib ecrt0.lib
#endif
diff --git a/build.symbian/symbian_ua.mmp b/build.symbian/symbian_ua.mmp
index 9514cde3..fc5c8aee 100644
--- a/build.symbian/symbian_ua.mmp
+++ b/build.symbian/symbian_ua.mmp
@@ -71,7 +71,8 @@ STATICLIBRARY libresample.lib
STATICLIBRARY eexe.lib ecrt0.lib
#endif
-LIBRARY esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib securesocket.lib
+LIBRARY esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib
+LIBRARY securesocket.lib x509.lib crypto.lib x500.lib
// The default 8KB seems to be insufficient with all bells and
// whistles turned on
diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
index a3482e00..1e0e88f9 100644
--- a/pjlib/build/Makefile
+++ b/pjlib/build/Makefile
@@ -26,7 +26,8 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \
pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \
rbtree.o sock_common.o sock_qos_common.o sock_qos_bsd.o \
- ssl_sock_common.o ssl_sock_ossl.o string.o timer.o types.o
+ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_dump.o \
+ string.o timer.o types.o
export PJLIB_CFLAGS += $(_CFLAGS)
###############################################################################
diff --git a/pjlib/build/cacert.pem b/pjlib/build/cacert.pem
index 0a2ee45b..cfce0920 100644
--- a/pjlib/build/cacert.pem
+++ b/pjlib/build/cacert.pem
@@ -1,21 +1,14 @@
-----BEGIN CERTIFICATE-----
-MIIDXzCCAkegAwIBAgIJAPqAwYU5OQLXMA0GCSqGSIb3DQEBBQUAMEYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ4wDAYDVQQKDAVwanNpcDESMBAG
-A1UEAwwJcGpzaXAubGFiMB4XDTA5MTAyMjE3MTczN1oXDTE5MTAyMDE3MTczN1ow
-RjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDjAMBgNVBAoMBXBq
-c2lwMRIwEAYDVQQDDAlwanNpcC5sYWIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDWWvnL+oSC0Q6OwGLt2YuhXTEzIVv3B+SGQ7tajB6H3WXVeq+1NmU9
-Yzca33g4FRrU7n4smYmKLzm1aniBhNmJjA+t+gbyizKnLMaCLoG52tUoULcANGKU
-aGwlmvZFugDn2eVg6UfUfRzEGbV3q3a/PzSsOEPwsMeF3YMQJPhkoyPQLtWgUXgP
-89Nyq3XjGGtw/qmUgQjE8a6/P0yXc+myI0hmApmZ9nB3YmlB5W3q6WoU2gGhLXf4
-12rH/LgdnPhM4ijS554Kv9EcUDdQTTrm6bYg66tj+qTet7DolUOlTZ3vKpuCK3tt
-eK9CbNPVzsMsB3yCALSLzQ347pIwfLaJAgMBAAGjUDBOMB0GA1UdDgQWBBRE/VNp
-kNQmLEXKQ+NM4bOVj95zYTAfBgNVHSMEGDAWgBRE/VNpkNQmLEXKQ+NM4bOVj95z
-YTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCj/gzJKTOZDEBD+zr7
-lvbVctiYE9o8ObxZQsnl/6zI2V9H/2yc1sqQyjBupzw6c37ehvk30yIyUfD3ts87
-2xaJ5VgtgUI3FI5DQ+ASyQXmDawUEmXIGqHb2gDrXBQLd6uMpvNDNW+7TouuniyA
-F12JUCNITeXaVJ0c8d4A9J9DlszBfYUzI45yIQu1gbpnpH74Sp/hG77EMxrRau+x
-EFFmV7gAmkCgOBnXm8jTKqNre/GfLfO7w2xoLsubSLnK46U3iLGBIJJRVGu3UQuN
-k1o7CiIKf0SSWj1bQI99ipTj8obBKRqj1nSbgKF/U6FIfd8DGcVvbJCSAG2czzyA
-5tdA
+MIICNDCCAZ2gAwIBAgIJAIa9mZggMk2WMA0GCSqGSIb3DQEBBAUAMDMxEjAQBgNV
+BAMTCXBqc2lwLmxhYjEdMBsGCSqGSIb3DQEJARYOdGVzdEBwanNpcC5sYWIwHhcN
+MTAwMjEwMDkwNTQ0WhcNMjAwMjA4MDkwNTQ0WjAzMRIwEAYDVQQDEwlwanNpcC5s
+YWIxHTAbBgkqhkiG9w0BCQEWDnRlc3RAcGpzaXAubGFiMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDI9T0Pf+1gKOTOAGEpZ481Q6xfm5vz6n1+6udxzQtfPKlQ
+rPD5x5im2u3tmy6ABxZeY5tCdeikBPiGlc5bRIRng6KM8kidkg3gEhwhRUxHCMWb
+mBpkz7rFERf/pWAOCqYCiy1RT8QrK+XOFoFdJhdF85UPDEUw+pHEsYetTDs9RQID
+AQABo1AwTjBMBgNVHREERTBDgglwanNpcC5sYWKCDXNpcC5wanNpcC5sYWKBDnRl
+c3RAcGpzaXAubGFihhFzaXA6c2lwLnBqc2lwLmxhYocEfwAAATANBgkqhkiG9w0B
+AQQFAAOBgQCLPl/WF1QvjT36kVLH0nxfHwDOJuAzlh6nv9rYBviOLw9FTEMgW6hA
+oG55YSdVjTnMynTMOH/kVp4Vxlk46A8neE+/LI8RPh6lJh52vb+iPAtBpsQoq06T
++u4DfJcN8Y/jy+QAn78jryKjwKuZWfuWny9gxsLWMUbH5Bc6v6wfQQ==
-----END CERTIFICATE-----
diff --git a/pjlib/build/pjlib.dsp b/pjlib/build/pjlib.dsp
index 6977f8a9..d63165eb 100644
--- a/pjlib/build/pjlib.dsp
+++ b/pjlib/build/pjlib.dsp
@@ -340,6 +340,10 @@ SOURCE=..\src\pj\ssl_sock_common.c
# End Source File
# Begin Source File
+SOURCE=..\src\pj\ssl_sock_dump.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pj\ssl_sock_ossl.c
# End Source File
# Begin Source File
diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj
index d151dfbb..9c3941bb 100644
--- a/pjlib/build/pjlib.vcproj
+++ b/pjlib/build/pjlib.vcproj
@@ -5649,6 +5649,10 @@
>
</File>
<File
+ RelativePath="..\src\pj\ssl_sock_dump.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pj\ssl_sock_ossl.c"
>
</File>
diff --git a/pjlib/build/privkey.pem b/pjlib/build/privkey.pem
index 3241be20..44c4b4e6 100644
--- a/pjlib/build/privkey.pem
+++ b/pjlib/build/privkey.pem
@@ -1,27 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEA1lr5y/qEgtEOjsBi7dmLoV0xMyFb9wfkhkO7Woweh91l1Xqv
-tTZlPWM3Gt94OBUa1O5+LJmJii85tWp4gYTZiYwPrfoG8osypyzGgi6BudrVKFC3
-ADRilGhsJZr2RboA59nlYOlH1H0cxBm1d6t2vz80rDhD8LDHhd2DECT4ZKMj0C7V
-oFF4D/PTcqt14xhrcP6plIEIxPGuvz9Ml3PpsiNIZgKZmfZwd2JpQeVt6ulqFNoB
-oS13+Ndqx/y4HZz4TOIo0ueeCr/RHFA3UE065um2IOurY/qk3rew6JVDpU2d7yqb
-git7bXivQmzT1c7DLAd8ggC0i80N+O6SMHy2iQIDAQABAoIBAQCAke7Ujz2d7WDq
-9LAh8+NRdUFGZtLvd9d1RPkCVZsWaRBknIL5kVfmGzV5M+K62MXQRACAJdOeg7b8
-fpErNpD4dH8PHjG+lwlZxnyGpvh+jqhd1xP81m7ujzeW0ry2k9tpNYPkveespyJy
-6Oy0i67dBT9FsTXnD1GNlJDBRTuLuEkTTBqbn/2s3+gUfChJ4HPmYMeO9HU4PcfM
-yUsHatBiIkXiCKdDZVMDr5AUVD0Wo3uHPGJ8ZreURAjH+ldG09+/EsVoPberbrDZ
-ZxJ70VKG+ZZTY8HZr9OsZhDZDrHiw9PdG7Hvg7bCvv+gDzZ/z8F+7YHjRjmD5Tp5
-Ex5hDco1AoGBAPfdfzwmqb79AXwYH0HZkkl2EXpzbR9LRgvWlOMSN3GlZhusvGQR
-up6iGk9QnmoEtQS2IAuK4JT3r+yoM/20Nadq5ZUpkZ49SHuZ6+eZpotv3Fh8Oay8
-TAi2vBGM7EQPUOjPOWMRaYGBz3FT/GvUGPTeQ8jYt1gy8F18+A8xD8pTAoGBAN1j
-7+yTly+M47U6mIUXcwoelaS4f/kMcwKHO0O182S4ktfjzc3TpQbHm68ws1rB3iFZ
-SFOP/d04tVxZqPBhN2SpXRHGqTJxXthdTbu3scLMedlf4jY11SRiHX4PDnoBQ1GJ
-NpdkMoex25Fw3AqSVpP61zo8sJkqpqjFfeQDbfgzAoGBAKyGx1ZmDwc6cjsfSzp5
-p+JsRVQ3XcBHk9UPooi/mEoJd55RyLvav0xFxwxoMCvZZOqHnpyKKTJniVOv7Khu
-NF55AJ6n1Y0QWRB3ngWSJKOv0+7fYQHD+yShlRyeO6JQCuBRxT8Y0phrc6oNbIjd
-lBV1VDdL6aqBol9gagWg/72zAoGBAK1rAx1F3z+YFSZ459AZNjvPCVkmTNhBMDXi
-yEGZ3TYgfqYuA6AfET3mTcVFWLjW87EbxtPuDuWi7i2Q7gydmk53fDfYbeDdfXXu
-YF2S3uPAWBI2UXQ1ZuhBEukT0jsvkhPkb6bXDd3NLDkZNsPxLXBtJPqxX4QbLME3
-Mg3RweqRAoGAJ4iXP2b4XWhg17qpDtpn8nhFxLNdhxdaDSKRL8oKQLteds3wS0fi
-ZlaU1P9a3ygTpNquKlmdLJTQEDAVjV5DDlrQAPxtSSytHulNzXRMQFaeydar3Ssv
-J07BPdQs6JEgV071rGGzBcL8ulo7qCdnGxU6GmhLkS4MBbTuqR6jmgU=
+MIICXgIBAAKBgQDI9T0Pf+1gKOTOAGEpZ481Q6xfm5vz6n1+6udxzQtfPKlQrPD5
+x5im2u3tmy6ABxZeY5tCdeikBPiGlc5bRIRng6KM8kidkg3gEhwhRUxHCMWbmBpk
+z7rFERf/pWAOCqYCiy1RT8QrK+XOFoFdJhdF85UPDEUw+pHEsYetTDs9RQIDAQAB
+AoGAGV+1xQY/H7wqH8S2f/begzg3RJ8uUt8R13urm5frTqwnKNOdXbyRDshn8G9+
+sJW0gliLWxnuNP+Xrc6ujqGZIguK/yAxJ3LprAN2Ay1lW2ONyZNMquBeIY5Txhyy
+SnU7U+NQYgA3+w9T7O7YQ575TTDm2gri558jIx8t55Wo9sUCQQDtjfGZ3sYXwpxR
+MvtdtfwDxSKhf6glT6dn7/37KITBZXFy6Eb/tHrEEUuwR46g30vTd2JElCB+QExu
+4sZDt813AkEA2I/WXdGVRXtHzVivf3AnqWyXfrfAAXlBmEkgPyIPwE1+mxeNxkU7
+TRn0MOqAfbQW4+GRIYCKSBLodRnRq2iKIwJBAJLYa8DyNQH7CyYmnbwQAvlRo1ax
+0v89ff6CHD5ljar/SmH9s+XdawZIqsENet13KyhNZDGAX5WrqZPiGy1BMYECQQC1
+FREawfUfdEZF3rJgzVdcxACpZNyYXtwKipr8L28cTbBf3wIdmCZOAjW98VgfxEaf
+pi3E5ca7HZRi1oQL4A4hAkEA5koHCQYl+5PDjbLtxl0VyVCpmT9BrcZ99MS+ZEaW
+2+HpKIhXrEFxePQaWbCaW7gjKmKUwC0qqu0moedqJC3mzg==
-----END RSA PRIVATE KEY-----
diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h
index 9c392143..f9fc69b0 100644
--- a/pjlib/include/pj/ssl_sock.h
+++ b/pjlib/include/pj/ssl_sock.h
@@ -60,17 +60,126 @@ typedef struct pj_ssl_sock_t pj_ssl_sock_t;
typedef struct pj_ssl_cert_t pj_ssl_cert_t;
+typedef enum pj_ssl_cert_verify_flag_t
+{
+ /**
+ * No error in verification.
+ */
+ PJ_SSL_CERT_ESUCCESS = 0,
+
+ /**
+ * The issuer certificate cannot be found.
+ */
+ PJ_SSL_CERT_EISSUER_NOT_FOUND = (1 << 0),
+
+ /**
+ * The certificate is untrusted.
+ */
+ PJ_SSL_CERT_EUNTRUSTED = (1 << 1),
+
+ /**
+ * The certificate has expired or not yet valid.
+ */
+ PJ_SSL_CERT_EVALIDITY_PERIOD = (1 << 2),
+
+ /**
+ * One or more fields of the certificate cannot be decoded due to
+ * invalid format.
+ */
+ PJ_SSL_CERT_EINVALID_FORMAT = (1 << 3),
+
+ /**
+ * The certificate cannot be used for the specified purpose.
+ */
+ PJ_SSL_CERT_EINVALID_PURPOSE = (1 << 4),
+
+ /**
+ * The issuer info in the certificate does not match to the (candidate)
+ * issuer certificate, e.g: issuer name not match to subject name
+ * of (candidate) issuer certificate.
+ */
+ PJ_SSL_CERT_EISSUER_MISMATCH = (1 << 5),
+
+ /**
+ * The CRL certificate cannot be found or cannot be read properly.
+ */
+ PJ_SSL_CERT_ECRL_FAILURE = (1 << 6),
+
+ /**
+ * The certificate has been revoked.
+ */
+ PJ_SSL_CERT_EREVOKED = (1 << 7),
+
+ /**
+ * The certificate chain length is too long.
+ */
+ PJ_SSL_CERT_ECHAIN_TOO_LONG = (1 << 8),
+
+ /**
+ * The server identity does not match to any identities specified in
+ * the certificate, e.g: subjectAltName extension, subject common name.
+ * This flag will only be set by application as SSL socket does not
+ * perform server identity verification.
+ */
+ PJ_SSL_CERT_EIDENTITY_NOT_MATCH = (1 << 30),
+
+ /**
+ * Unknown verification error.
+ */
+ PJ_SSL_CERT_EUNKNOWN = (1 << 31)
+
+} pj_ssl_cert_verify_flag_t;
+
+
+typedef enum pj_ssl_cert_name_type
+{
+ PJ_SSL_CERT_NAME_UNKNOWN = 0,
+ PJ_SSL_CERT_NAME_RFC822,
+ PJ_SSL_CERT_NAME_DNS,
+ PJ_SSL_CERT_NAME_URI,
+ PJ_SSL_CERT_NAME_IP
+} pj_ssl_cert_name_type;
+
/**
* Describe structure of certificate info.
*/
typedef struct pj_ssl_cert_info {
- pj_str_t subject; /**< Subject. */
- pj_str_t issuer; /**< Issuer. */
- unsigned version; /**< Certificate version. */
- pj_time_val validity_start; /**< Validity start. */
- pj_time_val validity_end; /**< Validity end. */
- pj_bool_t validity_use_gmt; /**< Flag if validity date/time
- use GMT. */
+
+ unsigned version; /**< Certificate version */
+
+ pj_uint8_t serial_no[20]; /**< Serial number, array of
+ octets, first index is
+ MSB */
+
+ struct {
+ pj_str_t cn; /**< Common name */
+ pj_str_t info; /**< One line subject, fields
+ are separated by slash */
+ } subject; /**< Subject */
+
+ struct {
+ pj_str_t cn; /**< Common name */
+ pj_str_t info; /**< One line subject, fields
+ are separated by slash.*/
+ } issuer; /**< Issuer */
+
+ struct {
+ pj_time_val start; /**< Validity start */
+ pj_time_val end; /**< Validity end */
+ pj_bool_t gmt; /**< Flag if validity date/time
+ use GMT */
+ } validity; /**< Validity */
+
+ struct {
+ unsigned cnt; /**< # of entry */
+ struct {
+ pj_ssl_cert_name_type type;
+ /**< Name type */
+ pj_str_t name; /**< The name */
+ } *entry; /**< Subject alt name entry */
+ } subj_alt_name; /**< Subject alternative
+ name extension */
+
} pj_ssl_cert_info;
@@ -93,6 +202,39 @@ PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
pj_ssl_cert_t **p_cert);
+/**
+ * Dump SSL certificate info.
+ *
+ * @param ci The certificate info.
+ * @param prefix Prefix string for each line.
+ * @param buf The buffer where certificate info will be printed on.
+ * @param buf_size The buffer size.
+ *
+ * @return PJ_SUCCESS when successful.
+ */
+PJ_DECL(pj_status_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci,
+ const char *prefix,
+ char *buf,
+ pj_size_t buf_size);
+
+
+/**
+ * Get SSL certificate verification error messages from verification status.
+ *
+ * @param verify_status The SSL certificate verification status.
+ * @param error_strings Array of strings to receive the verification error
+ * messages.
+ * @param count On input it specifies maximum error messages should be
+ * retrieved. On output it specifies the number of error
+ * messages retrieved.
+ *
+ * @return PJ_SUCCESS when successful.
+ */
+PJ_DECL(pj_status_t) pj_ssl_cert_verify_error_st(pj_uint32_t verify_status,
+ const char *error_strings[],
+ unsigned *count);
+
+
/**
* Cipher suites enumeration.
*/
@@ -363,13 +505,18 @@ typedef struct pj_ssl_sock_info
/**
* Describes active local certificate info.
*/
- pj_ssl_cert_info local_cert_info;
+ pj_ssl_cert_info *local_cert_info;
/**
* Describes active remote certificate info.
*/
- pj_ssl_cert_info remote_cert_info;
-
+ pj_ssl_cert_info *remote_cert_info;
+
+ /**
+ * Status of peer certificate verification.
+ */
+ pj_uint32_t verify_status;
+
} pj_ssl_sock_info;
@@ -523,11 +670,11 @@ typedef struct pj_ssl_sock_param
pj_bool_t require_client_cert;
/**
- * When secure socket is acting as client (perform outgoing connection)
- * and it needs to verify server name (e.g: host or domain name) by
- * matching it to the name specified in the server certificate. This
- * setting is useful when the server is hosting multiple domains for
- * the same listening socket.
+ * Server name indication. When secure socket is acting as client
+ * (perform outgoing connection) and the server may host multiple
+ * 'virtual' servers at a single underlying network address, setting
+ * this will allow client to tell the server a name of the server
+ * it is contacting.
*
* Default value is zero/not-set.
*/
diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
index 127099c6..988a8b2a 100644
--- a/pjlib/src/pj/ssl_sock_common.c
+++ b/pjlib/src/pj/ssl_sock_common.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pj/ssl_sock.h>
+#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/string.h>
@@ -128,6 +129,7 @@ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
}
+/* Get cipher name string */
PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
{
unsigned i, n;
@@ -140,3 +142,96 @@ PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
return NULL;
}
+
+
+
+
+PJ_DEF(pj_status_t) pj_ssl_cert_verify_error_st(pj_uint32_t verify_status,
+ const char *error_strings[],
+ unsigned *count)
+{
+ unsigned i = 0, shift_idx = 0;
+ unsigned unknown = 0;
+ pj_uint32_t errs;
+
+ PJ_ASSERT_RETURN(error_strings && count, PJ_EINVAL);
+
+ if (verify_status == PJ_SSL_CERT_ESUCCESS && *count) {
+ error_strings[0] = "OK";
+ *count = 1;
+ return PJ_SUCCESS;
+ }
+
+ errs = verify_status;
+
+ while (errs && i < *count) {
+ pj_uint32_t err;
+ const char *p = NULL;
+
+ if ((errs & 1) == 0) {
+ shift_idx++;
+ errs >>= 1;
+ continue;
+ }
+
+ err = (1 << shift_idx);
+
+ switch (err) {
+ case PJ_SSL_CERT_EISSUER_NOT_FOUND:
+ p = "The issuer certificate cannot be found";
+ break;
+ case PJ_SSL_CERT_EUNTRUSTED:
+ p = "The certificate is untrusted";
+ break;
+ case PJ_SSL_CERT_EVALIDITY_PERIOD:
+ p = "The certificate has expired or not yet valid";
+ break;
+ case PJ_SSL_CERT_EINVALID_FORMAT:
+ p = "One or more fields of the certificate cannot be decoded "
+ "due to invalid format";
+ break;
+ case PJ_SSL_CERT_EISSUER_MISMATCH:
+ p = "The issuer info in the certificate does not match to the "
+ "(candidate) issuer certificate";
+ break;
+ case PJ_SSL_CERT_ECRL_FAILURE:
+ p = "The CRL certificate cannot be found or cannot be read "
+ "properly";
+ break;
+ case PJ_SSL_CERT_EREVOKED:
+ p = "The certificate has been revoked";
+ break;
+ case PJ_SSL_CERT_EINVALID_PURPOSE:
+ p = "The certificate or CA certificate cannot be used for the "
+ "specified purpose";
+ break;
+ case PJ_SSL_CERT_ECHAIN_TOO_LONG:
+ p = "The certificate chain length is too long";
+ break;
+ case PJ_SSL_CERT_EIDENTITY_NOT_MATCH:
+ p = "The server identity does not match to any identities "
+ "specified in the certificate";
+ break;
+ case PJ_SSL_CERT_EUNKNOWN:
+ default:
+ unknown++;
+ break;
+ }
+
+ /* Set error string */
+ if (p)
+ error_strings[i++] = p;
+
+ /* Next */
+ shift_idx++;
+ errs >>= 1;
+ }
+
+ /* Unknown error */
+ if (unknown && i < *count)
+ error_strings[i++] = "Unknown verification error";
+
+ *count = i;
+
+ return PJ_SUCCESS;
+}
diff --git a/pjlib/src/pj/ssl_sock_dump.c b/pjlib/src/pj/ssl_sock_dump.c
new file mode 100644
index 00000000..45a6f7ee
--- /dev/null
+++ b/pjlib/src/pj/ssl_sock_dump.c
@@ -0,0 +1,147 @@
+/* $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/errno.h>
+#include <pj/os.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_dump.c"
+
+#define CHECK_BUF_LEN() \
+ if ((len < 0) || ((p+=len) >= end)) { \
+ *(p-1) = '\0'; \
+ return PJ_ETOOSMALL; \
+ }
+
+PJ_DEF(pj_status_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci,
+ const char *prefix,
+ char *buf,
+ pj_size_t buf_size)
+{
+ const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ pj_parsed_time pt1;
+ pj_parsed_time pt2;
+ unsigned i;
+ int len = 0;
+ char *p, *end;
+
+ p = buf;
+ end = buf + buf_size;
+
+ pj_time_decode(&ci->validity.start, &pt1);
+ pj_time_decode(&ci->validity.end, &pt2);
+
+ /* Version */
+ len = pj_ansi_snprintf(p, end-p, "%sVersion : v%d\n",
+ prefix, ci->version);
+ CHECK_BUF_LEN();
+
+ /* Serial number */
+ len = pj_ansi_snprintf(p, end-p, "%sSerial : ", prefix);
+ CHECK_BUF_LEN();
+
+ for (i = 0; i < sizeof(ci->serial_no) && !ci->serial_no[i]; ++i);
+ for (; i < sizeof(ci->serial_no); ++i) {
+ len = pj_ansi_snprintf(p, end-p, "%02X ", ci->serial_no[i]);
+ CHECK_BUF_LEN();
+ }
+ *(p-1) = '\n';
+
+ /* Subject */
+ len = pj_ansi_snprintf( p, end-p, "%sSubject : %.*s\n", prefix,
+ ci->subject.cn.slen,
+ ci->subject.cn.ptr);
+ CHECK_BUF_LEN();
+ len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", prefix,
+ ci->subject.info.slen,
+ ci->subject.info.ptr);
+ CHECK_BUF_LEN();
+
+ /* Issuer */
+ len = pj_ansi_snprintf( p, end-p, "%sIssuer : %.*s\n", prefix,
+ ci->issuer.cn.slen,
+ ci->issuer.cn.ptr);
+ CHECK_BUF_LEN();
+ len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", prefix,
+ ci->issuer.info.slen,
+ ci->issuer.info.ptr);
+ CHECK_BUF_LEN();
+
+ /* Validity period */
+ len = pj_ansi_snprintf( p, end-p, "%sValid from : %s %4d-%02d-%02d "
+ "%02d:%02d:%02d.%03d %s\n", prefix,
+ wdays[pt1.wday], pt1.year, pt1.mon+1, pt1.day,
+ pt1.hour, pt1.min, pt1.sec, pt1.msec,
+ (ci->validity.gmt? "GMT":""));
+ CHECK_BUF_LEN();
+
+ len = pj_ansi_snprintf( p, end-p, "%sValid to : %s %4d-%02d-%02d "
+ "%02d:%02d:%02d.%03d %s\n", prefix,
+ wdays[pt2.wday], pt2.year, pt2.mon+1, pt2.day,
+ pt2.hour, pt2.min, pt2.sec, pt2.msec,
+ (ci->validity.gmt? "GMT":""));
+ CHECK_BUF_LEN();
+
+ /* Subject alternative name extension */
+ if (ci->subj_alt_name.cnt) {
+ unsigned i;
+
+ len = pj_ansi_snprintf(p, end-p, "%ssubjectAltName extension\n",
+ prefix);
+ CHECK_BUF_LEN();
+
+ for (i = 0; i < ci->subj_alt_name.cnt; ++i) {
+ const char *type = NULL;
+
+ switch(ci->subj_alt_name.entry[i].type) {
+ case PJ_SSL_CERT_NAME_RFC822:
+ type = "MAIL";
+ break;
+ case PJ_SSL_CERT_NAME_DNS:
+ type = " DNS";
+ break;
+ case PJ_SSL_CERT_NAME_URI:
+ type = " URI";
+ break;
+ case PJ_SSL_CERT_NAME_IP:
+ type = " IP";
+ break;
+ default:
+ break;
+ }
+ if (type) {
+ len = pj_ansi_snprintf( p, end-p, "%s %s : %.*s\n", prefix,
+ type,
+ ci->subj_alt_name.entry[i].name.slen,
+ ci->subj_alt_name.entry[i].name.ptr);
+ CHECK_BUF_LEN();
+ }
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+#endif /* PJ_HAS_SSL_SOCK */
+
diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
index 3b0e86fe..7fe0c435 100644
--- a/pjlib/src/pj/ssl_sock_ossl.c
+++ b/pjlib/src/pj/ssl_sock_ossl.c
@@ -45,6 +45,7 @@
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/x509v3.h>
#ifdef _MSC_VER
@@ -152,6 +153,7 @@ struct pj_ssl_sock_t
enum ssl_state ssl_state;
pj_ioqueue_op_key_t handshake_op_key;
pj_timer_entry timer;
+ pj_status_t verify_status;
pj_sock_t sock;
pj_activesock_t *asock;
@@ -207,13 +209,17 @@ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
#define PJ_SSL_ERRNO_SPACE_SIZE PJ_ERRNO_SPACE_SIZE
-#define GET_SSL_STATUS(status) { \
- unsigned long e = ERR_get_error();\
- status = ERR_GET_LIB(e)*300 + ERR_GET_REASON(e);\
+#define STATUS_FROM_SSL_ERR(err, status) { \
+ status = ERR_GET_LIB(err)*300 + ERR_GET_REASON(err);\
pj_assert(status < PJ_SSL_ERRNO_SPACE_SIZE);\
if (status) status += PJ_SSL_ERRNO_START;\
}
+#define GET_SSL_STATUS(status) { \
+ unsigned long e = ERR_get_error();\
+ STATUS_FROM_SSL_ERR(e, status);\
+}
+
/*
* Get error string of OpenSSL.
*/
@@ -235,7 +241,11 @@ static pj_str_t ssl_strerror(pj_status_t status,
{
const char *tmp = NULL;
- tmp = ERR_reason_error_string(ssl_err);
+
+ if (ssl_err >= 300)
+ tmp = ERR_reason_error_string(ssl_err);
+ else
+ tmp = X509_verify_cert_error_string(ssl_err);
if (tmp) {
pj_ansi_strncpy(buf, tmp, bufsize);
@@ -263,6 +273,9 @@ static int openssl_reg_strerr;
static pj_ssl_cipher openssl_ciphers[100];
static unsigned openssl_cipher_num;
+/* OpenSSL application data index */
+static int sslsock_idx;
+
/* Initialize OpenSSL */
static pj_status_t init_openssl(void)
@@ -329,6 +342,9 @@ static pj_status_t init_openssl(void)
openssl_cipher_num = n;
}
+ /* Create OpenSSL application data index for SSL socket */
+ sslsock_idx = SSL_get_ex_new_index(0, "SSL socket", NULL, NULL, NULL);
+
return PJ_SUCCESS;
}
@@ -355,8 +371,107 @@ static int password_cb(char *buf, int num, int rwflag, void *user_data)
}
-/* Create and initialize new SSL context */
-static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
+/* SSL password callback. */
+static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ pj_ssl_sock_t *ssock;
+ SSL *ossl_ssl;
+ int err;
+
+ /* Get SSL instance */
+ ossl_ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ pj_assert(ossl_ssl);
+
+ /* Get SSL socket instance */
+ ssock = SSL_get_ex_data(ossl_ssl, sslsock_idx);
+ pj_assert(ssock);
+
+ /* Store verification status */
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ switch (err) {
+ case X509_V_OK:
+ break;
+
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND;
+ break;
+
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
+ break;
+
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
+ break;
+
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE;
+ break;
+
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
+ break;
+
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+ case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH;
+ break;
+
+ case X509_V_ERR_CERT_REVOKED:
+ ssock->verify_status |= PJ_SSL_CERT_EREVOKED;
+ break;
+
+ case X509_V_ERR_INVALID_PURPOSE:
+ case X509_V_ERR_CERT_REJECTED:
+ case X509_V_ERR_INVALID_CA:
+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE;
+ break;
+
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG: /* not really used */
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG;
+ break;
+
+ /* Unknown errors */
+ case X509_V_ERR_OUT_OF_MEM:
+ default:
+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
+ break;
+ }
+
+ /* When verification is not requested just return ok here, however
+ * application can still get the verification status.
+ */
+ if (PJ_FALSE == ssock->param.verify_peer)
+ preverify_ok = 1;
+
+ return preverify_ok;
+}
+
+/* Setting SSL sock cipher list */
+static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock);
+
+
+/* Create and initialize new SSL context and instance */
+static pj_status_t create_ssl(pj_ssl_sock_t *ssock)
{
SSL_METHOD *ssl_method;
SSL_CTX *ctx;
@@ -364,7 +479,7 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
int mode, rc;
pj_status_t status;
- pj_assert(ssock && p_ctx);
+ pj_assert(ssock);
cert = ssock->cert;
@@ -393,7 +508,7 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
return PJ_EINVAL;
}
- /* Create SSL context for the listener */
+ /* Create SSL context */
ctx = SSL_CTX_new(ssl_method);
if (ctx == NULL) {
GET_SSL_STATUS(status);
@@ -455,29 +570,55 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
}
}
-
- /* SSL verification options */
- if (ssock->param.verify_peer) {
- mode = SSL_VERIFY_PEER;
- } else {
- mode = SSL_VERIFY_NONE;
+ /* Create SSL instance */
+ ssock->ossl_ctx = ctx;
+ ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
+ if (ssock->ossl_ssl == NULL) {
+ GET_SSL_STATUS(status);
+ return status;
}
+ /* Set SSL sock as application data of SSL instance */
+ SSL_set_ex_data(ssock->ossl_ssl, sslsock_idx, ssock);
+
+ /* SSL verification options */
+ mode = SSL_VERIFY_PEER;
if (ssock->is_server && ssock->param.require_client_cert)
- mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_PEER;
+ mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- SSL_CTX_set_verify(ctx, mode, NULL);
+ SSL_set_verify(ssock->ossl_ssl, mode, &verify_cb);
- *p_ctx = ctx;
+ /* Set cipher list */
+ status = set_cipher_list(ssock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* 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);
return PJ_SUCCESS;
}
-/* Destroy SSL context */
-static void destroy_ssl_ctx(SSL_CTX *ctx)
+/* Destroy SSL context and instance */
+static void destroy_ssl(pj_ssl_sock_t *ssock)
{
- SSL_CTX_free(ctx);
+ /* Destroy SSL instance */
+ if (ssock->ossl_ssl) {
+ SSL_shutdown(ssock->ossl_ssl);
+ SSL_free(ssock->ossl_ssl); /* this will also close BIOs */
+ ssock->ossl_ssl = NULL;
+ }
+
+ /* Destroy SSL context */
+ if (ssock->ossl_ctx) {
+ SSL_CTX_free(ssock->ossl_ctx);
+ ssock->ossl_ctx = NULL;
+ }
/* Potentially shutdown OpenSSL library if this is the last
* context exists.
@@ -491,15 +632,8 @@ 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;
- }
+ destroy_ssl(ssock);
+
if (ssock->asock) {
pj_activesock_close(ssock->asock);
ssock->asock = NULL;
@@ -639,42 +773,143 @@ static pj_bool_t parse_ossl_asn1_time(pj_time_val *tv, pj_bool_t *gmt,
}
-/* Get certificate info from OpenSSL X509 */
+/* Get Common Name field string from a general name string */
+static void get_cn_from_gen_name(const pj_str_t *gen_name, pj_str_t *cn)
+{
+ pj_str_t CN_sign = {"/CN=", 4};
+ char *p, *q;
+
+ pj_bzero(cn, sizeof(cn));
+
+ p = pj_strstr(gen_name, &CN_sign);
+ if (!p)
+ return;
+
+ p += 4; /* shift pointer to value part */
+ pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
+ q = pj_strchr(cn, '/');
+ if (q)
+ cn->slen = q - p;
+}
+
+
+/* Get certificate info from OpenSSL X509, in case the certificate info
+ * hal already populated, this function will check if the contents need
+ * to be updated by inspecting the issuer and the serial number.
+ */
static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, X509 *x)
{
- pj_ssl_cert_info info;
- char buf1[256];
- char buf2[256];
+ pj_bool_t update_needed;
+ char buf[512];
+ pj_uint8_t serial_no[64] = {0}; /* should be >= sizeof(ci->serial_no) */
+ pj_uint8_t *p;
+ unsigned len;
+ GENERAL_NAMES *names = NULL;
+
+ pj_assert(pool && ci && x);
+
+ /* Get issuer */
+ X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof(buf));
- pj_assert(pool && ci);
+ /* Get serial no */
+ p = (pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x));
+ len = M_ASN1_STRING_length(X509_get_serialNumber(x));
+ if (len > sizeof(ci->serial_no))
+ len = sizeof(ci->serial_no);
+ pj_memcpy(serial_no + sizeof(ci->serial_no) - len, p, len);
- if (!x) {
- pj_bzero(ci, sizeof(pj_ssl_cert_info));
+ /* Check if the contents need to be updated. */
+ update_needed = pj_strcmp2(&ci->issuer.info, buf) ||
+ pj_memcmp(ci->serial_no, serial_no, sizeof(ci->serial_no));
+ if (!update_needed)
return;
- }
- pj_bzero(&info, sizeof(info));
+ /* Update cert info */
+
+ pj_bzero(ci, sizeof(pj_ssl_cert_info));
+
+ /* Version */
+ ci->version = X509_get_version(x) + 1;
+
+ /* Issuer */
+ pj_strdup2(pool, &ci->issuer.info, buf);
+ get_cn_from_gen_name(&ci->issuer.info, &ci->issuer.cn);
+
+ /* Serial number */
+ pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
- /* Populate cert info */
- info.subject = pj_str(X509_NAME_oneline(X509_get_subject_name(x),buf1,
- sizeof(buf1)));
- info.issuer = pj_str(X509_NAME_oneline(X509_get_issuer_name(x), buf2,
- sizeof(buf2)));
- info.version = X509_get_version(x) + 1;
- parse_ossl_asn1_time(&info.validity_start, &info.validity_use_gmt,
+ /* Subject */
+ pj_strdup2(pool, &ci->subject.info,
+ X509_NAME_oneline(X509_get_subject_name(x),
+ buf, sizeof(buf)));
+ get_cn_from_gen_name(&ci->subject.info, &ci->subject.cn);
+
+ /* Validity */
+ parse_ossl_asn1_time(&ci->validity.start, &ci->validity.gmt,
X509_get_notBefore(x));
- parse_ossl_asn1_time(&info.validity_end, &info.validity_use_gmt,
+ parse_ossl_asn1_time(&ci->validity.end, &ci->validity.gmt,
X509_get_notAfter(x));
- /* Update certificate info */
- if (pj_strcmp(&ci->subject, &info.subject))
- pj_strdup(pool, &ci->subject, &info.subject);
- if (pj_strcmp(&ci->issuer, &info.issuer))
- pj_strdup(pool, &ci->issuer, &info.issuer);
- ci->version = info.version;
- ci->validity_start = info.validity_start;
- ci->validity_end = info.validity_end;
- ci->validity_use_gmt = info.validity_use_gmt;
+ /* Subject Alternative Name extension */
+ if (ci->version >= 3) {
+ names = (GENERAL_NAMES*) X509_get_ext_d2i(x, NID_subject_alt_name,
+ NULL, NULL);
+ }
+ if (names) {
+ unsigned i, cnt;
+
+ cnt = sk_GENERAL_NAME_num(names);
+ ci->subj_alt_name.entry = pj_pool_calloc(pool, cnt,
+ sizeof(*ci->subj_alt_name.entry));
+
+ for (i = 0; i < cnt; ++i) {
+ unsigned char *p = 0;
+ pj_ssl_cert_name_type type = PJ_SSL_CERT_NAME_UNKNOWN;
+ const GENERAL_NAME *name;
+
+ name = sk_GENERAL_NAME_value(names, i);
+
+ switch (name->type) {
+ case GEN_EMAIL:
+ len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+ type = PJ_SSL_CERT_NAME_RFC822;
+ break;
+ case GEN_DNS:
+ len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+ type = PJ_SSL_CERT_NAME_DNS;
+ break;
+ case GEN_URI:
+ len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+ type = PJ_SSL_CERT_NAME_URI;
+ break;
+ case GEN_IPADD:
+ p = ASN1_STRING_data(name->d.ip);
+ len = ASN1_STRING_length(name->d.ip);
+ type = PJ_SSL_CERT_NAME_IP;
+ break;
+ default:
+ break;
+ }
+
+ if (p && len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
+ ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
+ if (type == PJ_SSL_CERT_NAME_IP) {
+ int af = pj_AF_INET();
+ if (len == sizeof(pj_in6_addr)) af = pj_AF_INET6();
+ pj_inet_ntop2(af, p, buf, sizeof(buf));
+ pj_strdup2(pool,
+ &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
+ buf);
+ } else {
+ pj_strdup2(pool,
+ &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
+ (char*)p);
+ OPENSSL_free(p);
+ }
+ ci->subj_alt_name.cnt++;
+ }
+ }
+ }
}
@@ -689,14 +924,22 @@ static void update_certs_info(pj_ssl_sock_t *ssock)
/* Active local certificate */
x = SSL_get_certificate(ssock->ossl_ssl);
- get_cert_info(ssock->pool, &ssock->local_cert_info, x);
- /* Don't free local's X509! */
+ if (x) {
+ get_cert_info(ssock->pool, &ssock->local_cert_info, x);
+ /* Don't free local's X509! */
+ } else {
+ pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info));
+ }
/* Active remote certificate */
x = SSL_get_peer_certificate(ssock->ossl_ssl);
- get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
- /* Free peer's X509 */
- X509_free(x);
+ if (x) {
+ get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
+ /* Free peer's X509 */
+ X509_free(x);
+ } else {
+ pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
+ }
}
@@ -1231,29 +1474,10 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
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) {
- GET_SSL_STATUS(status);
- goto on_return;
- }
-
- /* Set cipher list */
- status = set_cipher_list(ssock);
+ status = create_ssl(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,
@@ -1351,29 +1575,10 @@ static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
goto on_return;
/* Create SSL context */
- status = create_ssl_ctx(ssock, &ssock->ossl_ctx);
+ status = create_ssl(ssock);
if (status != PJ_SUCCESS)
goto on_return;
- /* Create SSL instance */
- ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
- if (ssock->ossl_ssl == NULL) {
- GET_SSL_STATUS(status);
- 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,
@@ -1651,9 +1856,9 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
if (info->established) {
- /* Current cipher */
const SSL_CIPHER *cipher;
+ /* Current cipher */
cipher = SSL_get_current_cipher(ssock->ossl_ssl);
info->cipher = (cipher->id & 0x00FFFFFF);
@@ -1661,8 +1866,11 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
/* Certificates info */
- info->local_cert_info = ssock->local_cert_info;
- info->remote_cert_info = ssock->remote_cert_info;
+ info->local_cert_info = &ssock->local_cert_info;
+ info->remote_cert_info = &ssock->remote_cert_info;
+
+ /* Verification status */
+ info->verify_status = ssock->verify_status;
}
return PJ_SUCCESS;
diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp
index efcf1702..ab808f62 100644
--- a/pjlib/src/pj/ssl_sock_symbian.cpp
+++ b/pjlib/src/pj/ssl_sock_symbian.cpp
@@ -127,6 +127,11 @@ public:
return securesock_->CurrentCipherSuite(cipher);
return KErrNotFound;
}
+ const CX509Certificate *GetPeerCert() {
+ if (securesock_)
+ return securesock_->ServerCert();
+ return NULL;
+ }
private:
enum ssl_state state_;
@@ -409,6 +414,7 @@ typedef struct write_state_t {
*/
struct pj_ssl_sock_t
{
+ pj_pool_t *pool;
pj_ssl_sock_cb cb;
void *user_data;
@@ -434,9 +440,132 @@ struct pj_ssl_sock_t
unsigned ciphers_num;
pj_ssl_cipher *ciphers;
pj_str_t servername;
+ pj_ssl_cert_info remote_cert_info;
};
+static pj_str_t get_cert_name(pj_pool_t *pool,
+ const CX500DistinguishedName &name)
+{
+ TInt i;
+ char buf[1024];
+ TUint8 *p;
+ TInt l = sizeof(buf);
+
+ p = (TUint8*)buf;
+ for(i = 0; i < name.Count(); ++i) {
+ const CX520AttributeTypeAndValue &attr = name.Element(i);
+
+ /* Print element separator */
+ *p++ = '/';
+ if (0 == --l) break;
+
+ /* Print the type. */
+ TPtr8 type(p, l);
+ type.Copy(attr.Type());
+ p += type.Length();
+ l -= type.Length();
+ if (0 >= --l) break;
+
+ /* Print equal sign */
+ *p++ = '=';
+ if (0 == --l) break;
+
+ /* Print the value. Let's just get the raw data here */
+ TPtr8 value(p, l);
+ value.Copy(attr.EncodedValue().Mid(2));
+ p += value.Length();
+ l -= value.Length();
+ if (0 >= --l) break;
+ }
+
+ pj_str_t src, res;
+ pj_strset(&src, buf, sizeof(buf) - l);
+ pj_strdup(pool, &res, &src);
+
+ return res;
+}
+
+/* Get certificate info from CX509Certificate.
+ */
+static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci,
+ const CX509Certificate *x)
+{
+ unsigned len;
+
+ pj_assert(pool && ci && x);
+
+ pj_bzero(ci, sizeof(*ci));
+
+ /* Version */
+ ci->version = x->Version();
+
+ /* Serial number */
+ len = x->SerialNumber().Length();
+ if (len > sizeof(ci->serial_no))
+ len = sizeof(ci->serial_no);
+ pj_memcpy(ci->serial_no + sizeof(ci->serial_no) - len,
+ x->SerialNumber().Ptr(), len);
+
+ /* Subject */
+ {
+ HBufC *subject = NULL;
+ TRAPD(err, subject = x->SubjectL());
+ if (err == KErrNone) {
+ TPtr16 ptr16(subject->Des());
+ len = ptr16.Length();
+ TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len);
+ ptr8.Copy(ptr16);
+ pj_strset(&ci->subject.cn, (char*)ptr8.Ptr(), ptr8.Length());
+ }
+ ci->subject.info = get_cert_name(pool, x->SubjectName());
+ }
+
+ /* Issuer */
+ {
+ HBufC *issuer = NULL;
+ TRAPD(err, issuer = x->IssuerL());
+ if (err == KErrNone) {
+ TPtr16 ptr16(issuer->Des());
+ len = ptr16.Length();
+ TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len);
+ ptr8.Copy(ptr16);
+ pj_strset(&ci->issuer.cn, (char*)ptr8.Ptr(), ptr8.Length());
+ }
+ ci->issuer.info = get_cert_name(pool, x->IssuerName());
+ }
+
+ /* Validity */
+ const CValidityPeriod &valid_period = x->ValidityPeriod();
+ TTime base_time(TDateTime(1970, EJanuary, 0, 0, 0, 0, 0));
+ TTimeIntervalSeconds tmp_sec;
+ valid_period.Start().SecondsFrom(base_time, tmp_sec);
+ ci->validity.start.sec = tmp_sec.Int();
+ valid_period.Finish().SecondsFrom(base_time, tmp_sec);
+ ci->validity.end.sec = tmp_sec.Int();
+}
+
+
+/* Update certificates info. This function should be called after handshake
+ * or renegotiation successfully completed.
+ */
+static void update_certs_info(pj_ssl_sock_t *ssock)
+{
+ const CX509Certificate *x;
+
+ pj_assert(ssock && ssock->sock &&
+ ssock->sock->GetState() == CPjSSLSocket::SSL_STATE_ESTABLISHED);
+
+ /* Active remote certificate */
+ x = ssock->sock->GetPeerCert();
+ if (x) {
+ get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
+ } else {
+ pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
+ }
+}
+
+
/*
* Get cipher list supported by SSL/TLS backend.
*/
@@ -504,6 +633,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
ssock->write_state.start = ssock->write_state.buf;
/* Init secure socket */
+ ssock->pool = pool;
ssock->sock_af = param->sock_af;
ssock->sock_type = param->sock_type;
ssock->cb = param->cb;
@@ -643,7 +773,10 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
/* Remote address */
pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr,
- (pj_sockaddr_t*)&ssock->rem_addr);
+ (pj_sockaddr_t*)&ssock->rem_addr);
+
+ /* Certificates info */
+ info->remote_cert_info = &ssock->remote_cert_info;
}
/* Protocol */
@@ -728,14 +861,20 @@ static void read_cb(int err, void *key)
ssock->read_state.read_buf->SetLength(0);
status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf,
ssock->read_state.flags);
- if (status != PJ_EPENDING) {
- /* Notify error */
- (*ssock->cb.on_data_read)(ssock, NULL, 0, status, NULL);
- }
}
+ /* Connection closed or something goes wrong */
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- /* Connection closed or something goes wrong */
+ /* Notify error */
+ if (ssock->cb.on_data_read) {
+ pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, NULL, 0,
+ status, NULL);
+ if (!ret) {
+ /* We've been destroyed */
+ return;
+ }
+ }
+
delete ssock->read_state.read_buf;
delete ssock->read_state.orig_buf;
ssock->read_state.read_buf = NULL;
@@ -1001,6 +1140,7 @@ static void connect_cb(int err, void *key)
status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
if (status == PJ_SUCCESS) {
ssock->established = PJ_TRUE;
+ update_certs_info(ssock);
} else {
delete ssock->sock;
ssock->sock = NULL;
diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c
index 8302e8f1..6e0d4515 100644
--- a/pjlib/src/pjlib-test/ssl_sock.c
+++ b/pjlib/src/pjlib-test/ssl_sock.c
@@ -22,7 +22,7 @@
#define CERT_DIR "../build/"
-#define CERT_CA_FILE NULL
+#define CERT_CA_FILE CERT_DIR "cacert.pem"
#define CERT_FILE CERT_DIR "cacert.pem"
#define CERT_PRIVKEY_FILE CERT_DIR "privkey.pem"
#define CERT_PRIVKEY_PASS ""
@@ -83,26 +83,40 @@ struct test_state
struct send_key send_key; /* send op key */
};
-static void dump_cert_info(const char *prefix, const pj_ssl_cert_info *ci)
+static void dump_ssl_info(const pj_ssl_sock_info *si)
{
- const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- pj_parsed_time pt1;
- pj_parsed_time pt2;
-
- pj_time_decode(&ci->validity_start, &pt1);
- pj_time_decode(&ci->validity_end, &pt2);
-
- PJ_LOG(3, ("", "%sSubject : %.*s", prefix, ci->subject.slen, ci->subject.ptr));
- PJ_LOG(3, ("", "%sIssuer : %.*s", prefix, ci->issuer.slen, ci->issuer.ptr));
- PJ_LOG(3, ("", "%sVersion : v%d", prefix, ci->version));
- PJ_LOG(3, ("", "%sValid from : %s %4d-%02d-%02d %02d:%02d:%02d.%03d %s",
- prefix, wdays[pt1.wday], pt1.year, pt1.mon+1, pt1.day,
- pt1.hour, pt1.min, pt1.sec, pt1.msec,
- (ci->validity_use_gmt? "GMT":"")));
- PJ_LOG(3, ("", "%sValid to : %s %4d-%02d-%02d %02d:%02d:%02d.%03d %s",
- prefix, wdays[pt2.wday], pt2.year, pt2.mon+1, pt2.day,
- pt2.hour, pt2.min, pt2.sec, pt2.msec,
- (ci->validity_use_gmt? "GMT":"")));
+ const char *tmp_st;
+
+ /* Print cipher name */
+ tmp_st = pj_ssl_cipher_name(si->cipher);
+ if (tmp_st == NULL)
+ tmp_st = "[Unknown]";
+ PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
+
+ /* Print remote certificate info and verification result */
+ if (si->remote_cert_info && si->remote_cert_info->subject.info.slen)
+ {
+ char buf[2048];
+ const char *verif_msgs[32];
+ unsigned verif_msg_cnt;
+
+ /* Dump remote TLS certificate info */
+ PJ_LOG(3, ("", ".....Remote certificate info:"));
+ pj_ssl_cert_info_dump(si->remote_cert_info, " ", buf, sizeof(buf));
+ PJ_LOG(3,("", "\n%s", buf));
+
+ /* Dump remote TLS certificate verification result */
+ verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
+ pj_ssl_cert_verify_error_st(si->verify_status,
+ verif_msgs, &verif_msg_cnt);
+ PJ_LOG(3,("", ".....Remote certificate verification result: %s",
+ (verif_msg_cnt == 1? verif_msgs[0]:"")));
+ if (verif_msg_cnt > 1) {
+ unsigned i;
+ for (i = 0; i < verif_msg_cnt; ++i)
+ PJ_LOG(3,("", "..... - %s", verif_msgs[i]));
+ }
+ }
}
@@ -130,25 +144,8 @@ static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock,
pj_sockaddr_print((pj_sockaddr_t*)&info.remote_addr, buf2, sizeof(buf2), 1);
PJ_LOG(3, ("", "...Connected %s -> %s!", buf1, buf2));
- if (st->is_verbose) {
- const char *tmp_st;
-
- /* Print cipher name */
- tmp_st = pj_ssl_cipher_name(info.cipher);
- if (tmp_st == NULL)
- tmp_st = "[Unknown]";
- PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
-
- /* Print certificates info */
- if (info.local_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Local certificate info:"));
- dump_cert_info(".......", &info.local_cert_info);
- }
- if (info.remote_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Remote certificate info:"));
- dump_cert_info(".......", &info.remote_cert_info);
- }
- }
+ if (st->is_verbose)
+ dump_ssl_info(&info);
/* Start reading data */
read_buf[0] = st->read_buf;
@@ -198,6 +195,8 @@ static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,
pj_ssl_sock_get_user_data(ssock);
struct test_state *st;
void *read_buf[1];
+ pj_ssl_sock_info info;
+ char buf[64];
pj_status_t status;
PJ_UNUSED_ARG(src_addr_len);
@@ -207,36 +206,17 @@ static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,
*st = *parent_st;
pj_ssl_sock_set_user_data(newsock, st);
- if (st->is_verbose) {
- pj_ssl_sock_info info;
- char buf[64];
- const char *tmp_st;
-
- status = pj_ssl_sock_get_info(newsock, &info);
- if (status != PJ_SUCCESS) {
- app_perror("...ERROR pj_ssl_sock_get_info()", status);
- goto on_return;
- }
-
- pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
- PJ_LOG(3, ("", "...Accepted connection from %s", buf));
+ status = pj_ssl_sock_get_info(newsock, &info);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_get_info()", status);
+ goto on_return;
+ }
- /* Print cipher name */
- tmp_st = pj_ssl_cipher_name(info.cipher);
- if (tmp_st == NULL)
- tmp_st = "[Unknown]";
- PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
+ pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
+ PJ_LOG(3, ("", "...Accepted connection from %s", buf));
- /* Print certificates info */
- if (info.local_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Local certificate info:"));
- dump_cert_info(".......", &info.local_cert_info);
- }
- if (info.remote_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Remote certificate info:"));
- dump_cert_info(".......", &info.remote_cert_info);
- }
- }
+ if (st->is_verbose)
+ dump_ssl_info(&info);
/* Start reading data */
read_buf[0] = st->read_buf;
@@ -460,6 +440,7 @@ static int https_client_test(unsigned ms_timeout)
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_timeout;
+ param.proto = PJ_SSL_SOCK_PROTO_SSL23;
pj_time_val_normalize(&param.timeout);
status = pj_ssl_sock_create(pool, &param, &ssock);
@@ -512,7 +493,8 @@ on_return:
static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
- pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher)
+ pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher,
+ pj_bool_t req_client_cert, pj_bool_t client_provide_cert)
{
pj_pool_t *pool = NULL;
pj_ioqueue_t *ioqueue = NULL;
@@ -533,21 +515,6 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
- /* Set cert */
- {
- pj_str_t tmp1, tmp2, tmp3, tmp4;
-
- status = pj_ssl_cert_load_from_files(pool,
- pj_strset2(&tmp1, (char*)CERT_CA_FILE),
- pj_strset2(&tmp2, (char*)CERT_FILE),
- pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE),
- pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS),
- &cert);
- if (status != PJ_SUCCESS) {
- goto on_return;
- }
- }
-
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete = &ssl_on_accept_complete;
param.cb.on_connect_complete = &ssl_on_connect_complete;
@@ -562,10 +529,11 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
- /* SERVER */
+ /* === SERVER === */
param.proto = srv_proto;
param.user_data = &state_serv;
param.ciphers_num = (srv_cipher == -1)? 0 : 1;
+ param.require_client_cert = req_client_cert;
ciphers[0] = srv_cipher;
state_serv.pool = pool;
@@ -578,9 +546,24 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
- status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
- if (status != PJ_SUCCESS) {
- goto on_return;
+ /* Set server cert */
+ {
+ pj_str_t tmp1, tmp2, tmp3, tmp4;
+
+ status = pj_ssl_cert_load_from_files(pool,
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE),
+ pj_strset2(&tmp2, (char*)CERT_FILE),
+ pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE),
+ pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS),
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
}
status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
@@ -596,7 +579,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
pj_sockaddr_cp(&listen_addr, &info.local_addr);
}
- /* CLIENT */
+ /* === CLIENT === */
param.proto = cli_proto;
param.user_data = &state_cli;
param.ciphers_num = (cli_cipher == -1)? 0 : 1;
@@ -625,6 +608,28 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
+ /* Set cert for client */
+ {
+
+ if (!client_provide_cert) {
+ pj_str_t tmp1, tmp2;
+
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE);
+ pj_strset2(&tmp2, NULL);
+ status = pj_ssl_cert_load_from_files(pool,
+ &tmp1, &tmp2, &tmp2, &tmp2,
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
if (status == PJ_SUCCESS) {
ssl_on_connect_complete(ssock_cli, PJ_SUCCESS);
@@ -1013,6 +1018,9 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
ssock_cli = pj_pool_calloc(pool, clients, sizeof(pj_ssl_sock_t*));
state_cli = pj_pool_calloc(pool, clients, sizeof(struct test_state));
+ /* Get start timestamp */
+ pj_gettimeofday(&start);
+
/* Setup clients */
for (i = 0; i < clients; ++i) {
param.user_data = &state_cli[i];
@@ -1064,9 +1072,6 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
}
}
- /* Get start timestamp */
- pj_gettimeofday(&start);
-
/* Wait until everything has been sent/received or error */
while (clients_num)
{
@@ -1150,28 +1155,46 @@ int ssl_sock_test(void)
PJ_LOG(3,("", "..echo test w/ TLSv1 and TLS_RSA_WITH_DES_CBC_SHA cipher"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1,
- TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA);
+ TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ SSLv23 and TLS_RSA_WITH_AES_256_CBC_SHA cipher"));
ret = echo_test(PJ_SSL_SOCK_PROTO_SSL23, PJ_SSL_SOCK_PROTO_SSL23,
- TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA);
+ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ incompatible proto"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_SSL3,
- TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA);
+ TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
PJ_LOG(3,("", "..echo test w/ incompatible ciphers"));
ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
- TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA);
+ TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
+ if (ret == 0)
+ return PJ_EBUG;
+
+ PJ_LOG(3,("", "..echo test w/ client cert required but not provided"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
+ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_TRUE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
+ PJ_LOG(3,("", "..echo test w/ client cert required and provided"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
+ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_TRUE, PJ_TRUE);
+ if (ret != 0)
+ return ret;
+
PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)"));
ret = client_non_ssl(5000);
if (ret != 0)
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 42ce0127..d3cb8c82 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -2788,6 +2788,85 @@ static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info)
/*
+ * Transport status notification
+ */
+static pj_bool_t on_transport_state(pjsip_transport *tp, pj_uint32_t state,
+ const pjsip_transport_state_info *info)
+{
+ char host_port[128];
+
+ pj_ansi_snprintf(host_port, sizeof(host_port), "[%.*s:%d]",
+ (int)tp->remote_name.host.slen,
+ tp->remote_name.host.ptr,
+ tp->remote_name.port);
+
+ if (state & PJSIP_TP_STATE_CONNECTED) {
+ PJ_LOG(3,(THIS_FILE, "SIP transport %s is connected to %s",
+ tp->type_name, host_port));
+ }
+ else if (state & PJSIP_TP_STATE_ACCEPTED) {
+ PJ_LOG(3,(THIS_FILE, "SIP transport %s accepted %s",
+ tp->type_name, host_port));
+ }
+ else if (state & PJSIP_TP_STATE_DISCONNECTED) {
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), "SIP transport %s is disconnected from %s",
+ tp->type_name, host_port);
+ pjsua_perror(THIS_FILE, buf, info->status);
+ }
+ else if (state & PJSIP_TP_STATE_REJECTED) {
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), "SIP transport %s rejected %s",
+ tp->type_name, host_port);
+ pjsua_perror(THIS_FILE, buf, info->status);
+ }
+
+#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
+
+ if (!pj_ansi_stricmp(tp->type_name, "tls") && info->ext_info &&
+ (state == PJSIP_TP_STATE_CONNECTED ||
+ (state & PJSIP_TP_STATE_TLS_VERIF_ERROR)))
+ {
+ pjsip_tls_state_info *tls_info = (pjsip_tls_state_info*)info->ext_info;
+ pj_ssl_sock_info *ssl_sock_info = (pj_ssl_sock_info*)
+ tls_info->ssl_sock_info;
+ char buf[2048];
+ const char *verif_msgs[32];
+ unsigned verif_msg_cnt;
+
+ /* Dump server TLS certificate */
+ pj_ssl_cert_info_dump(ssl_sock_info->remote_cert_info, " ",
+ buf, sizeof(buf));
+ PJ_LOG(4,(THIS_FILE, "TLS cert info of %s:\n%s", host_port, buf));
+
+ /* Dump server TLS certificate verification result */
+ verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
+ pj_ssl_cert_verify_error_st(ssl_sock_info->verify_status,
+ verif_msgs, &verif_msg_cnt);
+ PJ_LOG(3,(THIS_FILE, "TLS cert verification result of %s : %s",
+ host_port,
+ (verif_msg_cnt == 1? verif_msgs[0]:"")));
+ if (verif_msg_cnt > 1) {
+ unsigned i;
+ for (i = 0; i < verif_msg_cnt; ++i)
+ PJ_LOG(3,(THIS_FILE, "- %s", verif_msgs[i]));
+ }
+
+ if (state & PJSIP_TP_STATE_TLS_VERIF_ERROR &&
+ !app_config.udp_cfg.tls_setting.verify_server)
+ {
+ PJ_LOG(3,(THIS_FILE, "PJSUA is configured to ignore TLS cert "
+ "verification errors"));
+ }
+ }
+
+#endif
+ return PJ_TRUE;
+}
+
+/*
* Print buddy list.
*/
static void print_buddy_list(void)
@@ -4383,6 +4462,7 @@ pj_status_t app_init(int argc, char *argv[])
app_config.cfg.cb.on_call_replaced = &on_call_replaced;
app_config.cfg.cb.on_nat_detect = &on_nat_detect;
app_config.cfg.cb.on_mwi_info = &on_mwi_info;
+ app_config.cfg.cb.on_transport_state = &on_transport_state;
/* Set sound device latency */
if (app_config.capture_lat > 0)
diff --git a/pjsip-apps/src/symbian_ua/ua.cpp b/pjsip-apps/src/symbian_ua/ua.cpp
index 64e49aee..620e75a7 100644
--- a/pjsip-apps/src/symbian_ua/ua.cpp
+++ b/pjsip-apps/src/symbian_ua/ua.cpp
@@ -270,6 +270,63 @@ static void on_call_replaced(pjsua_call_id old_call_id,
(int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
}
+/*
+ * Transport status notification
+ */
+static pj_bool_t on_transport_state(pjsip_transport *tp, pj_uint32_t state,
+ const pjsip_transport_state_info *info)
+{
+ char host_port[128];
+
+ pj_ansi_snprintf(host_port, sizeof(host_port), "[%.*s:%d]",
+ (int)tp->remote_name.host.slen,
+ tp->remote_name.host.ptr,
+ tp->remote_name.port);
+
+ if (state & PJSIP_TP_STATE_CONNECTED) {
+ PJ_LOG(3,(THIS_FILE, "SIP transport %s is connected to %s",
+ tp->type_name, host_port));
+ }
+ else if (state & PJSIP_TP_STATE_ACCEPTED) {
+ PJ_LOG(3,(THIS_FILE, "SIP transport %s accepted %s",
+ tp->type_name, host_port));
+ }
+ else if (state & PJSIP_TP_STATE_DISCONNECTED) {
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), "SIP transport %s is disconnected from %s",
+ tp->type_name, host_port);
+ pjsua_perror(THIS_FILE, buf, info->status);
+ }
+ else if (state & PJSIP_TP_STATE_REJECTED) {
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), "SIP transport %s rejected %s",
+ tp->type_name, host_port);
+ pjsua_perror(THIS_FILE, buf, info->status);
+ }
+
+#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
+
+ if (!pj_ansi_stricmp(tp->type_name, "tls") && info->ext_info &&
+ (state == PJSIP_TP_STATE_CONNECTED ||
+ (state & PJSIP_TP_STATE_TLS_VERIF_ERROR)))
+ {
+ pjsip_tls_state_info *tls_info = (pjsip_tls_state_info*)info->ext_info;
+ pj_ssl_sock_info *ssl_sock_info = (pj_ssl_sock_info*)
+ tls_info->ssl_sock_info;
+ char buf[2048];
+
+ /* Dump server TLS certificate */
+ pj_ssl_cert_info_dump(ssl_sock_info->remote_cert_info, " ",
+ buf, sizeof(buf));
+ PJ_LOG(4,(THIS_FILE, "TLS cert info of %s:\n%s", host_port, buf));
+ }
+
+#endif
+ return PJ_TRUE;
+}
+
//#include<e32debug.h>
@@ -330,6 +387,7 @@ static pj_status_t app_startup()
cfg.cb.on_call_transfer_status = &on_call_transfer_status;
cfg.cb.on_call_replaced = &on_call_replaced;
cfg.cb.on_nat_detect = &on_nat_detect;
+ cfg.cb.on_transport_state = &on_transport_state;
if (SIP_PROXY) {
cfg.outbound_proxy_cnt = 1;
@@ -1054,7 +1112,7 @@ int ua_main()
SelectIAP();
// Initialize RSocketServ
- if ((err=aSocketServer.Connect()) != KErrNone)
+ if ((err=aSocketServer.Connect(32)) != KErrNone)
return PJ_STATUS_FROM_OS(err);
// Open up a connection
diff --git a/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp b/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp
index f5a7931b..0f964ace 100644
--- a/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp
+++ b/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp
@@ -41,6 +41,7 @@ LIBRARY eikctl.lib bafl.lib fbscli.lib aknnotify.lib aknicon.lib
LIBRARY etext.lib gdi.lib egul.lib insock.lib
LIBRARY ecom.lib inetprotutil.lib http.lib esock.lib
LIBRARY charconv.lib estlib.lib
+LIBRARY securesocket.lib x509.lib crypto.lib x500.lib
// Ordering static libs based on dependencies, most to least dependent,
// this could be necessary for some SDKs, e.g: S60 3rd MR
diff --git a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp
index f722336a..bf3f7a36 100644
--- a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp
+++ b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp
@@ -263,7 +263,7 @@ int symbian_ua_init()
pj_status_t status;
// Initialize RSocketServ
- if ((err=aSocketServer.Connect()) != KErrNone)
+ if ((err=aSocketServer.Connect(32)) != KErrNone)
return PJ_STATUS_FROM_OS(err);
// Open up a connection
diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h
index 1938e9d4..2abcf4a4 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -372,6 +372,35 @@ pjsip_endpt_acquire_transport( pjsip_endpoint *endpt,
pjsip_transport **p_tp);
+/**
+ * Find a SIP transport suitable for sending SIP message to the specified
+ * address by also considering the outgoing SIP message data. If transport
+ * selector ("sel") is set, then the function will check if the transport
+ * selected is suitable to send requests to the specified address.
+ *
+ * @see pjsip_tpmgr_acquire_transport
+ *
+ * @param endpt The SIP endpoint instance.
+ * @param type The type of transport to be acquired.
+ * @param remote The remote address to send message to.
+ * @param addr_len Length of the remote address.
+ * @param sel Optional pointer to transport selector instance which is
+ * used to find explicit transport, if required.
+ * @param tdata Optional pointer to SIP message data to be sent.
+ * @param p_tp Pointer to receive the transport instance, if one is found.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjsip_endpt_acquire_transport2(pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_t *remote,
+ int addr_len,
+ const pjsip_tpselector *sel,
+ pjsip_tx_data *tdata,
+ pjsip_transport **p_tp);
+
+
/*****************************************************************************
*
* Capabilities Management
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 6592fc98..9f6534cd 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -540,6 +540,10 @@ struct pjsip_tx_data
*/
struct
{
+ /** Server name.
+ */
+ pj_str_t name;
+
/** Server addresses resolved.
*/
pjsip_server_addresses addr;
@@ -689,6 +693,11 @@ typedef struct pjsip_transport_key
long type;
/**
+ * Hash of host name.
+ */
+ pj_uint32_t hname;
+
+ /**
* Destination address.
*/
pj_sockaddr rem_addr;
@@ -918,7 +927,8 @@ struct pjsip_tpfactory
pjsip_host_port addr_name; /**< Published name. */
/**
- * Create new outbound connection.
+ * Create new outbound connection suitable for sending SIP message
+ * to specified remote address.
* Note that the factory is responsible for both creating the
* transport and registering it to the transport manager.
*/
@@ -930,6 +940,21 @@ struct pjsip_tpfactory
pjsip_transport **transport);
/**
+ * Create new outbound connection suitable for sending SIP message
+ * to specified remote address by also considering outgoing SIP
+ * message data.
+ * Note that the factory is responsible for both creating the
+ * transport and registering it to the transport manager.
+ */
+ pj_status_t (*create_transport2)(pjsip_tpfactory *factory,
+ pjsip_tpmgr *mgr,
+ pjsip_endpoint *endpt,
+ const pj_sockaddr *rem_addr,
+ int addr_len,
+ pjsip_tx_data *tdata,
+ pjsip_transport **transport);
+
+ /**
* Destroy the listener.
*/
pj_status_t (*destroy)(pjsip_tpfactory *factory);
@@ -1100,6 +1125,34 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pjsip_transport **tp);
/**
+ * Find suitable transport for sending SIP message to specified remote
+ * destination by also considering the outgoing SIP message. If no suitable
+ * transport is found, a new one will be created.
+ *
+ * This is an internal function since normally application doesn't have access
+ * to transport manager. Application should use pjsip_endpt_acquire_transport()
+ * instead.
+ *
+ * @param mgr The transport manager instance.
+ * @param type The type of transport to be acquired.
+ * @param remote The remote address to send message to.
+ * @param addr_len Length of the remote address.
+ * @param sel Optional pointer to transport selector instance which is
+ * used to find explicit transport, if required.
+ * @param tdata Optional pointer to data to be sent.
+ * @param tp Pointer to receive the transport instance, if one is found.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_t *remote,
+ int addr_len,
+ const pjsip_tpselector *sel,
+ pjsip_tx_data *tdata,
+ pjsip_transport **tp);
+
+/**
* Type of callback to receive notification when message or raw data
* has been sent.
*
@@ -1187,6 +1240,94 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr,
void *token,
pjsip_tp_send_callback cb);
+
+/**
+ * Enumeration of transport state types.
+ */
+typedef enum pjsip_transport_state_type {
+
+ /** Transport connected. */
+ PJSIP_TP_STATE_CONNECTED = (1 << 0),
+
+ /** Transport accepted. */
+ PJSIP_TP_STATE_ACCEPTED = (1 << 1),
+
+ /** Transport disconnected. */
+ PJSIP_TP_STATE_DISCONNECTED = (1 << 2),
+
+ /** Incoming connection rejected. */
+ PJSIP_TP_STATE_REJECTED = (1 << 3),
+
+ /** TLS verification error. */
+ PJSIP_TP_STATE_TLS_VERIF_ERROR = (1 << 8)
+
+} pjsip_transport_state_type;
+
+
+/**
+ * Structure of transport state info.
+ */
+typedef struct pjsip_transport_state_info {
+ /**
+ * The last error code related to the transport state.
+ */
+ pj_status_t status;
+
+ /**
+ * Optional extended info, the content is specific for each transport type.
+ */
+ void *ext_info;
+} pjsip_transport_state_info;
+
+
+/**
+ * Type of callback to receive transport state notifications, such as
+ * transport connected, disconnected or TLS verification error.
+ *
+ * @param tp The transport instance.
+ * @param state The transport state, this may contain single or
+ * combination of transport state types defined in
+ * #pjsip_transport_state_type.
+ * @param info The transport state info.
+ *
+ * @return When TLS verification fails and peer verification in
+ * #pjsip_tls_setting is not set, application may return
+ * PJ_TRUE to ignore the verification result and continue
+ * using the transport. On other cases, this return value
+ * is currently not used and will be ignored.
+ */
+typedef pj_bool_t (*pjsip_tp_state_callback)(
+ pjsip_transport *tp,
+ pj_uint32_t state,
+ const pjsip_transport_state_info *info);
+
+
+/**
+ * Setting callback of transport state notification. The caller will be
+ * notified whenever the state of transport is changed. The type of
+ * events are defined in #pjsip_transport_state_type.
+ *
+ * @param mgr Transport manager.
+ * @param cb Callback to be called to notify caller about transport
+ * status changing.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_set_status_cb(pjsip_tpmgr *mgr,
+ pjsip_tp_state_callback *cb);
+
+
+/**
+ * Getting the callback of transport state notification.
+ *
+ * @param mgr Transport manager.
+ *
+ * @return The transport state callback or NULL if it is not set.
+ */
+PJ_DECL(pjsip_tp_state_callback*) pjsip_tpmgr_get_status_cb(
+ const pjsip_tpmgr *mgr);
+
+
/**
* @}
*/
diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h
index 8c41e167..f97414b3 100644
--- a/pjsip/include/pjsip/sip_transport_tls.h
+++ b/pjsip/include/pjsip/sip_transport_tls.h
@@ -26,6 +26,7 @@
*/
#include <pjsip/sip_transport.h>
+#include <pj/ssl_sock.h>
#include <pj/string.h>
#include <pj/sock_qos.h>
@@ -121,27 +122,44 @@ typedef struct pjsip_tls_setting
pj_str_t server_name;
/**
- * When PJSIP is acting as a client (outgoing TLS connections),
- * it will always receive a certificate from the peer.
- * If \a verify_server is disabled (set to zero), PJSIP will not
- * verifiy the certificate and allows TLS connections to servers
- * which do not present a valid certificate.
- * If \a tls_verify_server is non-zero, PJSIP verifies the server
- * certificate and will close the TLS connection if the server
- * certificate is not valid.
+ * Specifies the action when verification of server TLS certificate
+ * resulting errors:
+ * - If \a verify_server is disabled (set to PJ_FALSE), TLS transport
+ * will just notify the application via #pjsip_tp_state_callback with
+ * state (PJSIP_TP_STATE_CONNECTED | PJSIP_TP_STATE_TLS_VERIF_ERROR)
+ * whenever there is any TLS verification error, the return value of
+ * the callback will be used to decide whether transport should be
+ * shutdown.
+ * - If \a verify_server is enabled (set to PJ_TRUE), TLS transport
+ * will be shutdown and application will be notified with state
+ * (PJSIP_TP_STATE_DISCONNECTED | PJSIP_TP_STATE_TLS_VERIF_ERROR)
+ * whenever there is any TLS verification error.
*
- * This setting corresponds to OpenSSL SSL_VERIFY_PEER flag.
- * Default value is zero.
+ * When the verification resulting success, application will be notified
+ * via #pjsip_tp_state_callback with state PJSIP_TP_STATE_CONNECTED.
+ *
+ * Default value is PJ_FALSE.
*/
pj_bool_t verify_server;
/**
- * When acting as server (incoming TLS connections), setting
- * \a verify_client to non-zero will cause the transport to activate
- * peer verification upon receiving incoming TLS connection.
+ * Specifies the action when verification of server TLS certificate
+ * resulting errors:
+ * - If \a verify_client is disabled (set to PJ_FALSE), TLS transport
+ * will just notify the application via #pjsip_tp_state_callback with
+ * state (PJSIP_TP_STATE_ACCEPTED | PJSIP_TP_STATE_TLS_VERIF_ERROR)
+ * whenever there is any TLS verification error, the return value of
+ * the callback will be used to decide whether transport should be
+ * shutdown.
+ * - If \a verify_client is enabled (set to PJ_TRUE), TLS transport
+ * will be shutdown and application will be notified with state
+ * (PJSIP_TP_STATE_REJECTED | PJSIP_TP_STATE_TLS_VERIF_ERROR)
+ * whenever there is any TLS verification error.
+ *
+ * When the verification resulting success, application will be notified
+ * via #pjsip_tp_state_callback with state PJSIP_TP_STATE_ACCEPTED.
*
- * This setting corresponds to OpenSSL SSL_VERIFY_PEER flag.
- * Default value is zero.
+ * Default value is PJ_FALSE.
*/
pj_bool_t verify_client;
@@ -150,7 +168,7 @@ typedef struct pjsip_tls_setting
* connection if client doesn't have a valid certificate.
*
* This setting corresponds to SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag.
- * Default value is zero.
+ * Default value is PJ_FALSE.
*/
pj_bool_t require_client_cert;
@@ -191,6 +209,20 @@ typedef struct pjsip_tls_setting
/**
+ * This structure defines transport state extended info specifically for
+ * TLS transport.
+ */
+typedef struct pjsip_tls_state_info
+{
+ /**
+ * SSL socket info.
+ */
+ pj_ssl_sock_info *ssl_sock_info;
+
+} pjsip_tls_state_info;
+
+
+/**
* Initialize TLS setting with default values.
*
* @param tls_opt The TLS setting to be initialized.
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index ecafb25d..8d4d0a97 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -843,6 +843,25 @@ typedef struct pjsua_callback
*/
void (*on_mwi_info)(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info);
+ /**
+ * This callback is called when transport state is changed. See also
+ * #pjsip_tp_state_callback.
+ *
+ * @param tp The transport instance.
+ * @param state The transport state, this may contain single or
+ * combination of transport state types defined in
+ * #pjsip_transport_state_type.
+ * @param info The transport state info.
+ *
+ * @return When TLS verification fails and peer verification in
+ * #pjsip_tls_setting is not set, application may return
+ * PJ_TRUE to ignore the verification result and continue
+ * using the transport. On other cases, this return value
+ * is currently not used and will be ignored.
+ */
+ pj_bool_t (*on_transport_state)(pjsip_transport *tp, pj_uint32_t state,
+ const pjsip_transport_state_info *info);
+
} pjsua_callback;
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index a6861820..e0fe4f08 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -1075,6 +1075,22 @@ PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt,
/*
+ * Find/create transport.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport2(pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_t *remote,
+ int addr_len,
+ const pjsip_tpselector *sel,
+ pjsip_tx_data *tdata,
+ pjsip_transport **transport)
+{
+ return pjsip_tpmgr_acquire_transport2(endpt->transport_mgr, type, remote,
+ addr_len, sel, tdata, transport);
+}
+
+
+/*
* Report error.
*/
PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index f5d7ee57..92fbaf4d 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -89,6 +89,8 @@ struct pjsip_tpmgr
#endif
void (*on_rx_msg)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
pj_status_t (*on_tx_msg)(pjsip_endpoint*, pjsip_tx_data*);
+ pjsip_tp_state_callback *tp_state_cb;
+ void *tp_state_user_data;
};
@@ -864,7 +866,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
/*
* Register to hash table (see Trac ticket #42).
*/
- key_len = sizeof(tp->key.type) + tp->addr_len;
+ key_len = sizeof(tp->key.type) + sizeof(tp->key.hname) + tp->addr_len;
pj_lock_acquire(mgr->lock);
/* If entry already occupied, unregister previous entry */
@@ -914,7 +916,7 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr,
/*
* Unregister from hash table (see Trac ticket #42).
*/
- key_len = sizeof(tp->key.type) + tp->addr_len;
+ key_len = sizeof(tp->key.type) + sizeof(tp->key.hname) + tp->addr_len;
hval = 0;
entry = pj_hash_get(mgr->table, &tp->key, key_len, &hval);
if (entry == (void*)tp)
@@ -1502,6 +1504,24 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
const pjsip_tpselector *sel,
pjsip_transport **tp)
{
+ return pjsip_tpmgr_acquire_transport2(mgr, type, remote, addr_len, sel,
+ NULL, tp);
+}
+
+/*
+ * pjsip_tpmgr_acquire_transport2()
+ *
+ * Get transport suitable to communicate to remote. Create a new one
+ * if necessary.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_t *remote,
+ int addr_len,
+ const pjsip_tpselector *sel,
+ pjsip_tx_data *tdata,
+ pjsip_transport **tp)
+{
pjsip_tpfactory *factory;
pj_status_t status;
@@ -1571,19 +1591,43 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
int key_len;
pjsip_transport *transport;
+ /*
+ * Find factory that can create such transport.
+ */
+ factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+ if (factory->type == type)
+ break;
+ factory = factory->next;
+ }
+ if (factory == &mgr->factory_list)
+ factory = NULL;
+
pj_bzero(&key, sizeof(key));
- key_len = sizeof(key.type) + addr_len;
+ key_len = sizeof(key.type) + sizeof(key.hname) + addr_len;
/* First try to get exact destination. */
key.type = type;
pj_memcpy(&key.rem_addr, remote, addr_len);
+ if (factory && factory->create_transport2 &&
+ tdata && tdata->dest_info.name.slen)
+ {
+ /* Only include hostname hash in the key when the factory support
+ * create_transport2() and tdata is supplied.
+ */
+ key.hname = pj_hash_calc_tolower(0,
+ (char*)tdata->dest_info.name.ptr,
+ &tdata->dest_info.name);
+ }
transport = (pjsip_transport*)
pj_hash_get(mgr->table, &key, key_len, NULL);
+
if (transport == NULL) {
unsigned flag = pjsip_transport_get_flag_from_type(type);
const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote;
+
/* Ignore address for loop transports. */
if (type == PJSIP_TRANSPORT_LOOP ||
type == PJSIP_TRANSPORT_LOOP_DGRAM)
@@ -1591,7 +1635,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pj_sockaddr *addr = &key.rem_addr;
pj_bzero(addr, addr_len);
- key_len = sizeof(key.type) + addr_len;
+ key_len = sizeof(key.type) + sizeof(key.hname) + addr_len;
transport = (pjsip_transport*)
pj_hash_get(mgr->table, &key, key_len, NULL);
}
@@ -1604,7 +1648,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pj_bzero(addr, addr_len);
addr->addr.sa_family = remote_addr->addr.sa_family;
- key_len = sizeof(key.type) + addr_len;
+ key_len = sizeof(key.type) + sizeof(key.hname) + addr_len;
transport = (pjsip_transport*)
pj_hash_get(mgr->table, &key, key_len, NULL);
}
@@ -1624,31 +1668,28 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
/*
* Transport not found!
- * Find factory that can create such transport.
*/
- factory = mgr->factory_list.next;
- while (factory != &mgr->factory_list) {
- if (factory->type == type)
- break;
- factory = factory->next;
- }
-
- if (factory == &mgr->factory_list) {
+ if (NULL == factory) {
/* No factory can create the transport! */
pj_lock_release(mgr->lock);
TRACE_((THIS_FILE, "No suitable factory was found either"));
return PJSIP_EUNSUPTRANSPORT;
}
-
}
TRACE_((THIS_FILE, "Creating new transport from factory"));
/* Request factory to create transport. */
- status = factory->create_transport(factory, mgr, mgr->endpt,
- (const pj_sockaddr*) remote, addr_len,
- tp);
+ if (factory->create_transport2) {
+ status = factory->create_transport2(factory, mgr, mgr->endpt,
+ (const pj_sockaddr*) remote,
+ addr_len, tdata, tp);
+ } else {
+ status = factory->create_transport(factory, mgr, mgr->endpt,
+ (const pj_sockaddr*) remote,
+ addr_len, tp);
+ }
if (status == PJ_SUCCESS) {
PJ_ASSERT_ON_FAIL(tp!=NULL,
{pj_lock_release(mgr->lock); return PJ_EBUG;});
@@ -1711,3 +1752,20 @@ PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr)
#endif
}
+PJ_DEF(pj_status_t) pjsip_tpmgr_set_status_cb(pjsip_tpmgr *mgr,
+ pjsip_tp_state_callback *cb)
+{
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ mgr->tp_state_cb = cb;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pjsip_tp_state_callback*) pjsip_tpmgr_get_status_cb(
+ const pjsip_tpmgr *mgr)
+{
+ PJ_ASSERT_RETURN(mgr, NULL);
+
+ return mgr->tp_state_cb;
+}
diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
index 56a4fe8f..a7c7eeba 100644
--- a/pjsip/src/pjsip/sip_transport_tcp.c
+++ b/pjsip/src/pjsip/sip_transport_tcp.c
@@ -166,6 +166,36 @@ static void sockaddr_to_host_port( pj_pool_t *pool,
host_port->port = pj_sockaddr_get_port(addr);
}
+
+static void tcp_init_shutdown(struct tcp_transport *tcp, pj_status_t status)
+{
+ pjsip_tp_state_callback *state_cb;
+
+ if (tcp->close_reason == PJ_SUCCESS)
+ tcp->close_reason = status;
+
+ if (tcp->base.is_shutdown)
+ return;
+
+ /* Notify application of transport disconnected state */
+ state_cb = pjsip_tpmgr_get_status_cb(tcp->base.tpmgr);
+ if (state_cb) {
+ pjsip_transport_state_info state_info;
+
+ pj_bzero(&state_info, sizeof(state_info));
+ state_info.status = tcp->close_reason;
+ (*state_cb)(&tcp->base, PJSIP_TP_STATE_DISCONNECTED, &state_info);
+ }
+
+ /* 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.
+ */
+ pjsip_transport_shutdown(&tcp->base);
+}
+
+
/*
* Initialize pjsip_tcp_transport_cfg structure with default values.
*/
@@ -921,6 +951,7 @@ static pj_bool_t on_accept_complete(pj_activesock_t *asock,
struct tcp_listener *listener;
struct tcp_transport *tcp;
char addr[PJ_INET6_ADDRSTRLEN+10];
+ pjsip_tp_state_callback *state_cb;
pj_status_t status;
PJ_UNUSED_ARG(src_addr_len);
@@ -966,6 +997,15 @@ static pj_bool_t on_accept_complete(pj_activesock_t *asock,
tcp->ka_timer.id = PJ_TRUE;
pj_gettimeofday(&tcp->last_activity);
}
+
+ /* Notify application of transport state accepted */
+ state_cb = pjsip_tpmgr_get_status_cb(tcp->base.tpmgr);
+ if (state_cb) {
+ pjsip_transport_state_info state_info;
+
+ pj_bzero(&state_info, sizeof(state_info));
+ (*state_cb)(&tcp->base, PJSIP_TP_STATE_ACCEPTED, &state_info);
+ }
}
}
@@ -1013,8 +1053,8 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
status = (bytes_sent == 0) ? PJ_RETURN_OS_ERROR(OSERR_ENOTCONN) :
-bytes_sent;
- if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
- pjsip_transport_shutdown(&tcp->base);
+
+ tcp_init_shutdown(tcp, status);
return PJ_FALSE;
}
@@ -1109,8 +1149,8 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport,
if (status == PJ_SUCCESS)
status = PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
- if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
- pjsip_transport_shutdown(&tcp->base);
+
+ tcp_init_shutdown(tcp, status);
}
}
}
@@ -1199,14 +1239,7 @@ static pj_bool_t on_data_read(pj_activesock_t *asock,
/* Transport is closed */
PJ_LOG(4,(tcp->base.obj_name, "TCP connection closed"));
- /* 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 (tcp->close_reason==PJ_SUCCESS)
- tcp->close_reason = status;
- pjsip_transport_shutdown(&tcp->base);
+ tcp_init_shutdown(tcp, status);
return PJ_FALSE;
@@ -1229,6 +1262,8 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
pj_sockaddr_in addr;
int addrlen;
+ pjsip_tp_state_callback *state_cb;
+
tcp = (struct tcp_transport*) pj_activesock_get_user_data(asock);
/* Mark that pending connect() operation has completed. */
@@ -1252,14 +1287,7 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
on_data_sent(tcp->asock, 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 (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
- pjsip_transport_shutdown(&tcp->base);
- return PJ_FALSE;
+ tcp_init_shutdown(tcp, status);
}
PJ_LOG(4,(tcp->base.obj_name,
@@ -1293,16 +1321,19 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
/* Start pending read */
status = tcp_start_read(tcp);
if (status != PJ_SUCCESS) {
- /* 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 (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
- pjsip_transport_shutdown(&tcp->base);
+ tcp_init_shutdown(tcp, status);
return PJ_FALSE;
}
+ /* Notify application of transport state connected */
+ state_cb = pjsip_tpmgr_get_status_cb(tcp->base.tpmgr);
+ if (state_cb) {
+ pjsip_transport_state_info state_info;
+
+ pj_bzero(&state_info, sizeof(state_info));
+ (*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info);
+ }
+
/* Flush all pending send operations */
tcp_flush_pending_tx(tcp);
@@ -1358,7 +1389,7 @@ static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
tcp_perror(tcp->base.obj_name,
"Error sending keep-alive packet", status);
- pjsip_transport_shutdown(&tcp->base);
+ tcp_init_shutdown(tcp, status);
return;
}
diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
index ab96ecd9..a135c43f 100644
--- a/pjsip/src/pjsip/sip_transport_tls.c
+++ b/pjsip/src/pjsip/sip_transport_tls.c
@@ -24,6 +24,7 @@
#include <pj/addr_resolv.h>
#include <pj/ssl_sock.h>
#include <pj/assert.h>
+#include <pj/hash.h>
#include <pj/lock.h>
#include <pj/log.h>
#include <pj/os.h>
@@ -80,12 +81,14 @@ struct tls_transport
{
pjsip_transport base;
pj_bool_t is_server;
+ pj_str_t remote_name;
pj_bool_t is_registered;
pj_bool_t is_closing;
pj_status_t close_reason;
pj_ssl_sock_t *ssock;
pj_bool_t has_pending_connect;
+ pj_bool_t verify_server;
/* Keep-alive timer. */
pj_timer_entry ka_timer;
@@ -135,6 +138,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
pjsip_endpoint *endpt,
const pj_sockaddr *rem_addr,
int addr_len,
+ pjsip_tx_data *tdata,
pjsip_transport **transport);
/* Common function to create and initialize transport */
@@ -144,6 +148,7 @@ static pj_status_t tls_create(struct tls_listener *listener,
pj_bool_t is_server,
const pj_sockaddr_in *local,
const pj_sockaddr_in *remote,
+ const pj_str_t *remote_name,
struct tls_transport **p_tls);
@@ -169,6 +174,34 @@ static void sockaddr_to_host_port( pj_pool_t *pool,
}
+static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status)
+{
+ pjsip_tp_state_callback *state_cb;
+
+ if (tls->close_reason == PJ_SUCCESS)
+ tls->close_reason = status;
+
+ if (tls->base.is_shutdown)
+ return;
+
+ /* Notify application of transport disconnected state */
+ state_cb = pjsip_tpmgr_get_status_cb(tls->base.tpmgr);
+ if (state_cb) {
+ pjsip_transport_state_info state_info;
+
+ pj_bzero(&state_info, sizeof(state_info));
+ state_info.status = tls->close_reason;
+ (*state_cb)(&tls->base, PJSIP_TP_STATE_DISCONNECTED, &state_info);
+ }
+
+ /* 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.
+ */
+ pjsip_transport_shutdown(&tls->base);
+}
+
/****************************************************************************
* The TLS listener/transport factory.
@@ -243,10 +276,10 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
ssock_param.async_cnt = async_cnt;
ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt);
ssock_param.require_client_cert = listener->tls_setting.require_client_cert;
- ssock_param.server_name = 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;
+ ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket
+ * due to verification error */
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)
@@ -371,7 +404,7 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
/* Register to transport manager */
listener->endpt = endpt;
listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
- listener->factory.create_transport = lis_create_transport;
+ listener->factory.create_transport2 = lis_create_transport;
listener->factory.destroy = lis_destroy;
listener->is_registered = PJ_TRUE;
status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
@@ -480,6 +513,7 @@ static pj_status_t tls_create( struct tls_listener *listener,
pj_bool_t is_server,
const pj_sockaddr_in *local,
const pj_sockaddr_in *remote,
+ const pj_str_t *remote_name,
struct tls_transport **p_tls)
{
struct tls_transport *tls;
@@ -501,6 +535,7 @@ static pj_status_t tls_create( struct tls_listener *listener,
*/
tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport);
tls->is_server = is_server;
+ tls->verify_server = listener->tls_setting.verify_server;
pj_list_init(&tls->delayed_list);
tls->base.pool = pool;
@@ -517,8 +552,13 @@ static pj_status_t tls_create( struct tls_listener *listener,
goto on_error;
}
+ if (remote_name)
+ pj_strdup(pool, &tls->remote_name, remote_name);
+
tls->base.key.type = PJSIP_TRANSPORT_TLS;
pj_memcpy(&tls->base.key.rem_addr, remote, sizeof(pj_sockaddr_in));
+ tls->base.key.hname = pj_hash_calc_tolower(0, (char*)tls->remote_name.ptr,
+ &tls->remote_name);
tls->base.type_name = "tls";
tls->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS);
@@ -769,6 +809,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
pjsip_endpoint *endpt,
const pj_sockaddr *rem_addr,
int addr_len,
+ pjsip_tx_data *tdata,
pjsip_transport **p_transport)
{
struct tls_listener *listener;
@@ -777,6 +818,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
pj_ssl_sock_t *ssock;
pj_ssl_sock_param ssock_param;
pj_sockaddr_in local_addr;
+ pj_str_t remote_name;
pj_status_t status;
/* Sanity checks */
@@ -794,6 +836,12 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
POOL_TP_INIT, POOL_TP_INC);
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+ /* Get remote host name from tdata */
+ if (tdata)
+ remote_name = tdata->dest_info.name;
+ else
+ pj_bzero(&remote_name, sizeof(remote_name));
+
/* Build SSL socket param */
pj_ssl_sock_param_default(&ssock_param);
ssock_param.cb.on_connect_complete = &on_connect_complete;
@@ -801,12 +849,12 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
ssock_param.cb.on_data_sent = &on_data_sent;
ssock_param.async_cnt = 1;
ssock_param.ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
- PJ_TODO(set_proper_servername_based_on_target);
PJ_TODO(synchronize_tls_cipher_type_with_ssl_sock_cipher_type);
- ssock_param.server_name = listener->tls_setting.server_name;
+ ssock_param.server_name = remote_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;
+ ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket
+ * due to verification error */
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)
@@ -850,7 +898,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
/* Create the transport descriptor */
status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr,
- (pj_sockaddr_in*)rem_addr, &tls);
+ (pj_sockaddr_in*)rem_addr, &remote_name, &tls);
if (status != PJ_SUCCESS)
return status;
@@ -928,9 +976,13 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock,
{
struct tls_listener *listener;
struct tls_transport *tls;
+ pj_ssl_sock_info ssl_info;
char addr[PJ_INET6_ADDRSTRLEN+10];
pj_status_t status;
+ pjsip_tp_state_callback *state_cb;
+ pj_bool_t tls_verif_ignored;
+
PJ_UNUSED_ARG(src_addr_len);
listener = (struct tls_listener*) pj_ssl_sock_get_user_data(ssock);
@@ -946,32 +998,84 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock,
pj_sockaddr_print(src_addr, addr, sizeof(addr), 3),
new_ssock));
+ /* Retrieve SSL socket info, close the socket if this is failed
+ * as the SSL socket info availability is rather critical here.
+ */
+ status = pj_ssl_sock_get_info(new_ssock, &ssl_info);
+ if (status != PJ_SUCCESS) {
+ pj_ssl_sock_close(new_ssock);
+ return PJ_TRUE;
+ }
+
/*
* Incoming connection!
* Create TLS transport for the new socket.
*/
status = tls_create( listener, NULL, new_ssock, PJ_TRUE,
(const pj_sockaddr_in*)&listener->factory.local_addr,
- (const pj_sockaddr_in*)src_addr, &tls);
+ (const pj_sockaddr_in*)src_addr, NULL, &tls);
- if (status == PJ_SUCCESS) {
- /* Set the "pending" SSL socket user data */
- pj_ssl_sock_set_user_data(new_ssock, tls);
+ if (status != PJ_SUCCESS)
+ return PJ_TRUE;
- status = tls_start_read(tls);
- if (status != PJ_SUCCESS) {
- PJ_LOG(3,(tls->base.obj_name, "New transport cancelled"));
- tls_destroy(&tls->base, status);
+ /* Set the "pending" SSL socket user data */
+ pj_ssl_sock_set_user_data(new_ssock, tls);
+
+ tls_verif_ignored = !listener->tls_setting.verify_client;
+
+ /* Notify transport state to application */
+ state_cb = pjsip_tpmgr_get_status_cb(tls->base.tpmgr);
+ if (state_cb) {
+ pjsip_transport_state_info state_info;
+ pjsip_tls_state_info tls_info;
+ pj_uint32_t tp_state = 0;
+
+ /* Init transport state notification callback */
+ pj_bzero(&tls_info, sizeof(tls_info));
+ pj_bzero(&state_info, sizeof(state_info));
+
+ /* Set transport state based on verification status */
+ if (ssl_info.verify_status) {
+ state_info.status = PJSIP_TLS_EACCEPT;
+ tp_state |= PJSIP_TP_STATE_TLS_VERIF_ERROR;
+ if (listener->tls_setting.verify_client)
+ tp_state |= PJSIP_TP_STATE_REJECTED;
+ else
+ tp_state |= PJSIP_TP_STATE_ACCEPTED;
} else {
- /* Start keep-alive timer */
- if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
- pj_time_val delay = {PJSIP_TCP_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);
- }
+ tp_state |= PJSIP_TP_STATE_ACCEPTED;
+ }
+
+ tls_info.ssl_sock_info = &ssl_info;
+ state_info.ext_info = &tls_info;
+
+ tls_verif_ignored = (*state_cb)(&tls->base, tp_state, &state_info);
+ }
+
+ /* Transport should be destroyed when there is TLS verification error
+ * and application doesn't want to ignore it.
+ */
+ if (ssl_info.verify_status &&
+ (listener->tls_setting.verify_client || !tls_verif_ignored))
+ {
+ tls_destroy(&tls->base, PJSIP_TLS_EACCEPT);
+ return PJ_TRUE;
+ }
+
+ status = tls_start_read(tls);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,(tls->base.obj_name, "New transport cancelled"));
+ tls_init_shutdown(tls, status);
+ tls_destroy(&tls->base, status);
+ } else {
+ /* Start keep-alive timer */
+ if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+ pj_time_val delay = {PJSIP_TCP_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);
}
}
@@ -1019,8 +1123,8 @@ static pj_bool_t on_data_sent(pj_ssl_sock_t *ssock,
status = (bytes_sent == 0) ? PJ_RETURN_OS_ERROR(OSERR_ENOTCONN) :
-bytes_sent;
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
+
+ tls_init_shutdown(tls, status);
return PJ_FALSE;
}
@@ -1115,8 +1219,8 @@ static pj_status_t tls_send_msg(pjsip_transport *transport,
if (status == PJ_SUCCESS)
status = PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
- if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
- pjsip_transport_shutdown(&tls->base);
+
+ tls_init_shutdown(tls, status);
}
}
}
@@ -1204,15 +1308,8 @@ static pj_bool_t on_data_read(pj_ssl_sock_t *ssock,
/* Transport is closed */
PJ_LOG(4,(tls->base.obj_name, "TLS connection closed"));
-
- /* 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);
+
+ tls_init_shutdown(tls, status);
return PJ_FALSE;
@@ -1232,8 +1329,12 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
pj_status_t status)
{
struct tls_transport *tls;
- pj_ssl_sock_info info;
-
+ pj_ssl_sock_info ssl_info;
+ pj_sockaddr_in addr, *tp_addr;
+
+ pjsip_tp_state_callback *state_cb;
+ pj_bool_t tls_verif_ignored;
+
tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock);
/* Check connect() status */
@@ -1254,36 +1355,130 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
on_data_sent(tls->ssock, 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_FALSE;
+ goto on_error;
}
+ /* Retrieve SSL socket info, shutdown the transport if this is failed
+ * as the SSL socket info availability is rather critical here.
+ */
+ status = pj_ssl_sock_get_info(tls->ssock, &ssl_info);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
/* 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?).
*/
+ tp_addr = (pj_sockaddr_in*)&tls->base.local_addr;
+ pj_sockaddr_cp((pj_sockaddr_t*)&addr,
+ (pj_sockaddr_t*)&ssl_info.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);
+ }
- /* Retrieve the bound address */
- status = pj_ssl_sock_get_info(tls->ssock, &info);
- if (status == PJ_SUCCESS) {
- pj_sockaddr_in addr;
- pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tls->base.local_addr;
-
- pj_sockaddr_cp((pj_sockaddr_t*)&addr, (pj_sockaddr_t*)&info.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);
+ /* Server identity verification based on server certificate. */
+ if (ssl_info.remote_cert_info->version) {
+ pj_str_t *remote_name;
+ pj_ssl_cert_info *serv_cert = ssl_info.remote_cert_info;
+ pj_bool_t matched = PJ_FALSE;
+ unsigned i;
+
+ /* Remote name may be hostname or IP address */
+ if (tls->remote_name.slen)
+ remote_name = &tls->remote_name;
+ else
+ remote_name = &tls->base.remote_name.host;
+
+ /* Start matching remote name with SubjectAltName fields of
+ * server certificate.
+ */
+ for (i = 0; i < serv_cert->subj_alt_name.cnt && !matched; ++i) {
+ pj_str_t *cert_name = &serv_cert->subj_alt_name.entry[i].name;
+
+ switch (serv_cert->subj_alt_name.entry[i].type) {
+ case PJ_SSL_CERT_NAME_DNS:
+ case PJ_SSL_CERT_NAME_IP:
+ matched = !pj_stricmp(remote_name, cert_name);
+ break;
+ case PJ_SSL_CERT_NAME_URI:
+ if (pj_strnicmp2(cert_name, "sip:", 4) == 0 ||
+ pj_strnicmp2(cert_name, "sips:", 5) == 0)
+ {
+ pj_str_t host_part;
+ char *p;
+
+ p = pj_strchr(cert_name, ':') + 1;
+ pj_strset(&host_part, p, cert_name->slen -
+ (p - cert_name->ptr));
+ matched = !pj_stricmp(remote_name, &host_part);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* When still not matched or no SubjectAltName fields in server
+ * certificate, try with Common Name of Subject field.
+ */
+ if (!matched) {
+ matched = !pj_stricmp(remote_name, &serv_cert->subject.cn);
}
+
+ if (!matched)
+ ssl_info.verify_status |= PJ_SSL_CERT_EIDENTITY_NOT_MATCH;
}
+ tls_verif_ignored = !tls->verify_server;
+
+ /* Notify transport state to application */
+ state_cb = pjsip_tpmgr_get_status_cb(tls->base.tpmgr);
+ if (state_cb) {
+ pjsip_transport_state_info state_info;
+ pjsip_tls_state_info tls_info;
+ pj_uint32_t tp_state = 0;
+
+ /* Init transport state notification callback */
+ pj_bzero(&state_info, sizeof(state_info));
+ pj_bzero(&tls_info, sizeof(tls_info));
+
+ /* Set transport state info */
+ state_info.ext_info = &tls_info;
+ tls_info.ssl_sock_info = &ssl_info;
+
+ /* Set transport state based on verification status */
+ if (ssl_info.verify_status) {
+ state_info.status = PJSIP_TLS_ECONNECT;
+ tp_state |= PJSIP_TP_STATE_TLS_VERIF_ERROR;
+ if (tls->verify_server)
+ tp_state |= PJSIP_TP_STATE_DISCONNECTED;
+ else
+ tp_state |= PJSIP_TP_STATE_CONNECTED;
+ } else {
+ tp_state |= PJSIP_TP_STATE_CONNECTED;
+ }
+
+ tls_verif_ignored = (*state_cb)(&tls->base, tp_state, &state_info);
+ }
+
+ /* Transport should be shutdown when there is TLS verification error
+ * and application doesn't want to ignore it.
+ */
+ if (ssl_info.verify_status &&
+ (tls->verify_server || !tls_verif_ignored))
+ {
+ if (tls->close_reason == PJ_SUCCESS)
+ tls->close_reason = PJSIP_TLS_ECONNECT;
+ pjsip_transport_shutdown(&tls->base);
+ return PJ_FALSE;
+ }
+
+ /* Mark that pending connect() operation has completed. */
+ tls->has_pending_connect = 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,
@@ -1293,21 +1488,10 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
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) {
- /* 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_FALSE;
- }
+ if (status != PJ_SUCCESS)
+ goto on_error;
/* Flush all pending send operations */
tls_flush_pending_tx(tls);
@@ -1322,6 +1506,11 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
}
return PJ_TRUE;
+
+on_error:
+ tls_init_shutdown(tls, status);
+
+ return PJ_FALSE;
}
@@ -1365,7 +1554,8 @@ static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
tls_perror(tls->base.obj_name,
"Error sending keep-alive packet", status);
- pjsip_transport_shutdown(&tls->base);
+
+ tls_init_shutdown(tls, status);
return;
}
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index 5bd0f6bf..3b1bc102 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -779,6 +779,10 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
pjsip_hdr_clone(cancel_tdata->pool, req_tdata->saved_strict_route);
}
+ /* Copy the destination host name from the original request */
+ pj_strdup(cancel_tdata->pool, &cancel_tdata->dest_info.name,
+ &req_tdata->dest_info.name);
+
/* Finally copy the destination info from the original request */
pj_memcpy(&cancel_tdata->dest_info, &req_tdata->dest_info,
sizeof(req_tdata->dest_info));
@@ -1134,11 +1138,12 @@ static void stateless_send_transport_cb( void *token,
cur_addr_len = tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len;
/* Acquire transport. */
- status = pjsip_endpt_acquire_transport( stateless_data->endpt,
+ status = pjsip_endpt_acquire_transport2(stateless_data->endpt,
cur_addr_type,
cur_addr,
cur_addr_len,
&tdata->tp_sel,
+ tdata,
&stateless_data->cur_transport);
if (status != PJ_SUCCESS) {
sent = -status;
@@ -1319,6 +1324,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt,
* proceed to sending the request directly.
*/
if (tdata->dest_info.addr.count == 0) {
+ /* Copy the destination host name to TX data */
+ pj_strdup(tdata->pool, &tdata->dest_info.name, &dest_info.addr.host);
+
pjsip_endpt_resolve( endpt, tdata->pool, &dest_info, stateless_data,
&stateless_send_resolver_callback);
} else {
@@ -1466,6 +1474,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_send_raw_to_uri(pjsip_endpoint *endpt,
pjsip_tpselector_add_ref(sraw_data->sel);
}
+ /* Copy the destination host name to TX data */
+ pj_strdup(tdata->pool, &tdata->dest_info.name, &dest_info.addr.host);
+
/* Resolve destination host.
* The processing then resumed when the resolving callback is called.
*/
@@ -1622,11 +1633,12 @@ static void send_response_resolver_cb( pj_status_t status, void *token,
/* Only handle the first address resolved. */
/* Acquire transport. */
- status = pjsip_endpt_acquire_transport( send_state->endpt,
+ status = pjsip_endpt_acquire_transport2(send_state->endpt,
addr->entry[0].type,
&addr->entry[0].addr,
addr->entry[0].addr_len,
&send_state->tdata->tp_sel,
+ send_state->tdata,
&send_state->cur_transport);
if (status != PJ_SUCCESS) {
if (send_state->app_cb) {
@@ -1702,6 +1714,10 @@ PJ_DEF(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt,
return status;
}
} else {
+ /* Copy the destination host name to TX data */
+ pj_strdup(tdata->pool, &tdata->dest_info.name,
+ &res_addr->dst_host.addr.host);
+
pjsip_endpt_resolve(endpt, tdata->pool, &res_addr->dst_host,
send_state, &send_response_resolver_cb);
return PJ_SUCCESS;
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 52d436e3..8870b87e 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -1810,9 +1810,6 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
/*
* Create TLS transport.
*/
- /*
- * Create TCP transport.
- */
pjsua_transport_config config;
pjsip_host_port a_name;
pjsip_tpfactory *tls;
@@ -1867,6 +1864,11 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
goto on_return;
}
+ /* Set transport state callback */
+ if (pjsua_var.ua_cfg.cb.on_transport_state) {
+ pjsip_tpmgr_set_status_cb(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
+ &pjsua_var.ua_cfg.cb.on_transport_state);
+ }
/* Return the ID */
if (p_id) *p_id = id;