From c7b5a2411a316bff3e3bda9e5fdac72db66f6269 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 26 Oct 2009 15:47:52 +0000 Subject: Ticket #957: - Added features in secure socket: handshake timeout timer, certificate info, renegotiation API. - Added unit test for secure socket, along with testing purpose certificate & private key. - Updated build configs for secure socket. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2970 74dad513-b988-da41-8d7b-12977e46ad98 --- aconfigure | 4 + aconfigure.ac | 1 + pjlib/build/Makefile | 4 +- pjlib/build/cacert.pem | 21 + pjlib/build/pjlib.dsp | 12 + pjlib/build/pjlib.vcproj | 12 + pjlib/build/pjlib_test.dsp | 6 +- pjlib/build/pjlib_test.vcproj | 840 ++++++++++++++++++----------------- pjlib/build/privkey.pem | 27 ++ pjlib/include/pj/compat/os_auto.h.in | 6 + pjlib/include/pj/config.h | 12 + pjlib/include/pj/ssl_sock.h | 76 +++- pjlib/include/pjlib.h | 1 + pjlib/src/pj/ssl_sock_ossl.c | 473 +++++++++++++++----- pjlib/src/pj/ssl_sock_symbian.cpp | 8 +- pjlib/src/pjlib-test/ssl_sock.c | 820 ++++++++++++++++++++++++++++++++++ pjlib/src/pjlib-test/test.c | 4 + pjlib/src/pjlib-test/test.h | 3 + pjsip/build/Makefile | 2 +- pjsip/build/pjsip_core.dsp | 13 + pjsip/build/pjsip_core.vcproj | 634 +++++++++++++------------- pjsip/src/pjsip/sip_transport_tls.c | 4 +- 22 files changed, 2122 insertions(+), 861 deletions(-) create mode 100644 pjlib/build/cacert.pem create mode 100644 pjlib/build/privkey.pem create mode 100644 pjlib/src/pjlib-test/ssl_sock.c diff --git a/aconfigure b/aconfigure index 6784e3f0..39fae4bf 100755 --- a/aconfigure +++ b/aconfigure @@ -11498,6 +11498,10 @@ fi echo "${ECHO_T}OpenSSL library found, SSL support enabled" >&6; } cat >>confdefs.h <<\_ACEOF #define PJSIP_HAS_TLS_TRANSPORT 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define PJ_HAS_SSL_SOCK 1 _ACEOF else diff --git a/aconfigure.ac b/aconfigure.ac index ec4ae542..7c914907 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -759,6 +759,7 @@ AC_ARG_ENABLE(ssl, if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then AC_MSG_RESULT([OpenSSL library found, SSL support enabled]) AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) + AC_DEFINE(PJ_HAS_SSL_SOCK, 1) else AC_MSG_RESULT([** OpenSSL libraries not found, disabling SSL support **]) fi diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index 439c7fa6..a3482e00 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -26,7 +26,7 @@ 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 \ - string.o timer.o types.o + ssl_sock_common.o ssl_sock_ossl.o string.o timer.o types.o export PJLIB_CFLAGS += $(_CFLAGS) ############################################################################### @@ -37,7 +37,7 @@ export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \ fifobuf.o file.o hash_test.o ioq_perf.o ioq_udp.o \ ioq_unreg.o ioq_tcp.o \ list.o mutex.o os.o pool.o pool_perf.o rand.o rbtree.o \ - select.o sleep.o sock.o sock_perf.o \ + select.o sleep.o sock.o sock_perf.o ssl_sock.o \ string.o test.o thread.o timer.o timestamp.o \ udp_echo_srv_sync.o udp_echo_srv_ioqueue.o \ util.o diff --git a/pjlib/build/cacert.pem b/pjlib/build/cacert.pem new file mode 100644 index 00000000..0a2ee45b --- /dev/null +++ b/pjlib/build/cacert.pem @@ -0,0 +1,21 @@ +-----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 +-----END CERTIFICATE----- diff --git a/pjlib/build/pjlib.dsp b/pjlib/build/pjlib.dsp index 2b042567..541a2d82 100644 --- a/pjlib/build/pjlib.dsp +++ b/pjlib/build/pjlib.dsp @@ -353,6 +353,14 @@ SOURCE=..\src\pj\sock_select.c # End Source File # Begin Source File +SOURCE=..\src\pj\ssl_sock_common.c +# End Source File +# Begin Source File + +SOURCE=..\src\pj\ssl_sock_ossl.c +# End Source File +# Begin Source File + SOURCE=..\src\pj\string.c # End Source File # Begin Source File @@ -605,6 +613,10 @@ SOURCE=..\include\pj\sock_select.h # End Source File # Begin Source File +SOURCE=..\include\pj\ssl_sock.h +# End Source File +# Begin Source File + SOURCE=..\include\pj\string.h # End Source File # Begin Source File diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index 7f4ee00e..6e8b5bf9 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -5644,6 +5644,14 @@ /> + + + + @@ -11637,6 +11645,10 @@ RelativePath="..\include\pj\sock_select.h" > + + diff --git a/pjlib/build/pjlib_test.dsp b/pjlib/build/pjlib_test.dsp index 8252f12f..067dea1d 100644 --- a/pjlib/build/pjlib_test.dsp +++ b/pjlib/build/pjlib_test.dsp @@ -74,7 +74,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjlib-test-i386-win32-vc6-debug.exe" /pdbtype:sept +# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libeay32MT.lib ssleay32MT.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjlib-test-i386-win32-vc6-debug.exe" /pdbtype:sept !ENDIF @@ -193,6 +193,10 @@ SOURCE="..\src\pjlib-test\sock_perf.c" # End Source File # Begin Source File +SOURCE="..\src\pjlib-test\ssl_sock.c" +# End Source File +# Begin Source File + SOURCE="..\src\pjlib-test\string.c" # End Source File # Begin Source File diff --git a/pjlib/build/pjlib_test.vcproj b/pjlib/build/pjlib_test.vcproj index 6156d7ce..050f6792 100644 --- a/pjlib/build/pjlib_test.vcproj +++ b/pjlib/build/pjlib_test.vcproj @@ -34,11 +34,11 @@ @@ -272,15 +271,12 @@ /> - @@ -288,24 +284,26 @@ Name="VCBscMakeTool" /> - - @@ -472,15 +469,12 @@ /> - @@ -488,24 +482,27 @@ Name="VCBscMakeTool" /> - - + @@ -552,26 +552,24 @@ Name="VCBscMakeTool" /> - - @@ -672,15 +669,12 @@ /> - @@ -688,24 +682,26 @@ Name="VCBscMakeTool" /> - - @@ -872,15 +867,12 @@ /> - @@ -888,24 +880,26 @@ Name="VCBscMakeTool" /> - - + @@ -1018,27 +1016,24 @@ Name="VCBscMakeTool" /> - - @@ -1072,15 +1067,12 @@ /> - @@ -1088,24 +1080,26 @@ Name="VCBscMakeTool" /> - - + @@ -1482,26 +1480,24 @@ Name="VCBscMakeTool" /> - - + @@ -1944,26 +1944,24 @@ Name="VCBscMakeTool" /> - - + @@ -2406,26 +2408,24 @@ Name="VCBscMakeTool" /> - - + + diff --git a/pjlib/build/privkey.pem b/pjlib/build/privkey.pem new file mode 100644 index 00000000..3241be20 --- /dev/null +++ b/pjlib/build/privkey.pem @@ -0,0 +1,27 @@ +-----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= +-----END RSA PRIVATE KEY----- diff --git a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in index 2e8562eb..f474d60f 100644 --- a/pjlib/include/pj/compat/os_auto.h.in +++ b/pjlib/include/pj/compat/os_auto.h.in @@ -181,5 +181,11 @@ */ #undef PJ_THREAD_ALLOCATE_STACK +/* SSL socket availability. */ +#ifndef PJ_HAS_SSL_SOCK +#undef PJ_HAS_SSL_SOCK +#endif + + #endif /* __PJ_COMPAT_OS_AUTO_H__ */ diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 71ca8a04..ab37d2df 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -779,6 +779,18 @@ #endif +/** + * Enable secure socket. For most platforms, this is implemented using + * OpenSSL, so this will require OpenSSL to be installed. For Symbian + * platform, this is implemented natively using CSecureSocket. + * + * Default: 0 (for now) + */ +#ifndef PJ_HAS_SSL_SOCK +# define PJ_HAS_SSL_SOCK 0 +#endif + + /** @} */ /******************************************************************** diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h index ce319d4d..ad4556e4 100644 --- a/pjlib/include/pj/ssl_sock.h +++ b/pjlib/include/pj/ssl_sock.h @@ -59,6 +59,20 @@ typedef struct pj_ssl_sock_t pj_ssl_sock_t; typedef struct pj_ssl_cert_t pj_ssl_cert_t; +/** + * 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. */ +} pj_ssl_cert_info; + + /** * Create credential from files. * @@ -66,6 +80,7 @@ typedef struct pj_ssl_cert_t pj_ssl_cert_t; * @param cert_file The file of certificate. * @param privkey_file The file of private key. * @param privkey_pass The password of private key, if any. + * @param p_cert Pointer to credential instance to be created. * * @return PJ_SUCCESS when successful. */ @@ -322,24 +337,38 @@ typedef struct pj_ssl_sock_info * handshaking has been done successfully. */ pj_bool_t established; + /** * Describes secure socket protocol being used. */ pj_ssl_sock_proto proto; + /** * Describes cipher suite being used, this will only be set when connection * is established. */ pj_ssl_cipher cipher; + /** * Describes local address. */ pj_sockaddr local_addr; + /** * Describes remote address. */ pj_sockaddr remote_addr; + /** + * Describes active local certificate info. + */ + pj_ssl_cert_info local_cert_info; + + /** + * Describes active remote certificate info. + */ + pj_ssl_cert_info remote_cert_info; + } pj_ssl_sock_info; @@ -368,6 +397,13 @@ typedef struct pj_ssl_sock_param */ pj_ioqueue_t *ioqueue; + /** + * Specify the timer heap to use. Secure socket uses the timer to provide + * auto cancelation on asynchronous operation when it takes longer time + * than specified timeout period, e.g: security negotiation timeout. + */ + pj_timer_heap_t *timer_heap; + /** * Specify secure socket callbacks, see #pj_ssl_sock_cb. */ @@ -430,13 +466,12 @@ typedef struct pj_ssl_sock_param pj_bool_t whole_data; /** - * Specify buffer size for delayed send operation. This setting is only - * applied for some platforms that restrict more than one outstanding - * send operation at a time, e.g: Symbian. So delaying/buffering send - * mechanism is used to allow application to send data anytime without - * worrying about current outstanding send operations. + * Specify buffer size for sending operation. Buffering sending data + * is used for allowing application to perform multiple outstanding + * send operations. Whenever application specifies this setting too + * small, sending operation may return PJ_ENOMEM. * - * Default value is 0, except for Symbian 8192 bytes. + * Default value is 8192 bytes. */ pj_size_t send_buffer_size; @@ -495,7 +530,7 @@ typedef struct pj_ssl_sock_param * * Default value is zero/not-set. */ - pj_str_t servername; + pj_str_t server_name; } pj_ssl_sock_param; @@ -691,12 +726,12 @@ PJ_DECL(pj_status_t) pj_ssl_sock_start_recvfrom2(pj_ssl_sock_t *ssock, * @param size The size of the data. * @param flags Flags to be given to pj_ioqueue_send(). * - * * @return PJ_SUCCESS if data has been sent immediately, or - * PJ_EPENDING if data cannot be sent immediately. In - * this case the \a on_data_sent() callback will be - * called when data is actually sent. Any other return - * value indicates error condition. + * PJ_EPENDING if data cannot be sent immediately or + * PJ_ENOMEM when sending buffer could not handle all + * queued data, see \a send_buffer_size. The callback + * \a on_data_sent() will be called when data is actually + * sent. Any other return value indicates error condition. */ PJ_DECL(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, @@ -785,6 +820,23 @@ PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock, int addr_len); +/** + * Starts SSL/TLS renegotiation over an already established SSL connection + * for this socket. This operation is performed transparently, no callback + * will be called once the renegotiation completed successfully. However, + * when the renegotiation fails, the connection will be closed and callback + * \a on_data_read() will be invoked with non-PJ_SUCCESS status code. + * + * @param ssock The secure socket. + * + * @return PJ_SUCCESS if renegotiation is completed immediately, + * or PJ_EPENDING if renegotiation has been started and + * waiting for completion, or the appropriate error code + * on failure. + */ +PJ_DECL(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock); + + /** * @} */ diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index 648c594a..0709a09c 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 9c6c80c8..eca60d2e 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -25,12 +25,14 @@ #include #include #include +#include #include #include +#include /* Only build when PJ_HAS_SSL_SOCK is enabled */ -//#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 #define THIS_FILE "ssl_sock_ossl.c" @@ -129,10 +131,14 @@ struct pj_ssl_sock_t pj_ssl_sock_t *parent; pj_ssl_sock_param param; pj_ssl_cert_t *cert; + + pj_ssl_cert_info local_cert_info; + pj_ssl_cert_info remote_cert_info; pj_bool_t is_server; enum ssl_state ssl_state; pj_ioqueue_op_key_t handshake_op_key; + pj_timer_entry handshake_timer; pj_sock_t sock; pj_activesock_t *asock; @@ -179,56 +185,57 @@ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock); ******************************************************************* */ -/* ssl_report_error() */ -static void ssl_report_error(const char *sender, int level, - pj_status_t status, - const char *format, ...) -{ - va_list marker; +/** + * Mapping from OpenSSL error codes to pjlib error space. + */ - va_start(marker, format); +#define PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + \ + PJ_ERRNO_SPACE_SIZE*6) -#if PJ_LOG_MAX_LEVEL > 0 - if (status != PJ_SUCCESS) { - char err_format[PJ_ERR_MSG_SIZE + 512]; - int len; +#define PJ_SSL_ERRNO_SPACE_SIZE 5000 - len = pj_ansi_snprintf(err_format, sizeof(err_format), - "%s: ", format); - pj_strerror(status, err_format+len, sizeof(err_format)-len); - - pj_log(sender, level, err_format, marker); +#define PJ_STATUS_FROM_OSSL(ossl_err) (ossl_err == SSL_ERROR_NONE? \ + PJ_SUCCESS : \ + (PJ_SSL_ERRNO_START + ossl_err)) - } else { - unsigned long ssl_err; +#define PJ_STATUS_TO_OSSL(status) (status == PJ_SUCCESS? \ + SSL_ERROR_NONE : \ + (status - PJ_SSL_ERRNO_START)) - ssl_err = ERR_get_error(); - if (ssl_err == 0) { - pj_log(sender, level, format, marker); - } else { - char err_format[512]; - int len; - - len = pj_ansi_snprintf(err_format, sizeof(err_format), - "%s: ", format); - ERR_error_string(ssl_err, err_format+len); - - pj_log(sender, level, err_format, marker); - } - } -#endif +/* + * Get error string of OpenSSL. + */ +static pj_str_t ssl_strerror(pj_status_t status, + char *buf, pj_size_t bufsize) +{ + pj_str_t errstr; + unsigned long ssl_err = PJ_STATUS_TO_OSSL(status); - va_end(marker); -} +#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) + + ERR_error_string_n(ssl_err, buf, bufsize); + errstr = pj_str(buf); + +#else + errstr.ptr = buf; + errstr.slen = pj_ansi_snprintf(buf, bufsize, + "Unknown OpenSSL error %d", + ssl_err); + +#endif /* PJ_HAS_ERROR_STRING */ + + return errstr; +} /* OpenSSL library initialization counter */ static int openssl_init_count; +static int openssl_reg_strerr; /* OpenSSL available ciphers */ -static pj_ssl_cipher openssl_ciphers[64]; +static pj_ssl_cipher openssl_ciphers[100]; static unsigned openssl_cipher_num; @@ -238,6 +245,18 @@ static pj_status_t init_openssl(void) if (++openssl_init_count != 1) return PJ_SUCCESS; + /* Register error subsystem */ + if (!openssl_reg_strerr) { + pj_status_t status; + + openssl_reg_strerr = 1; + status = pj_register_strerror(PJ_SSL_ERRNO_START, + PJ_SSL_ERRNO_SPACE_SIZE, + &ssl_strerror); + pj_assert(status == PJ_SUCCESS); + } + + /* Init OpenSSL lib */ SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); @@ -344,17 +363,14 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx) ssl_method = (SSL_METHOD*)DTLSv1_method(); break; default: - ssl_report_error(THIS_FILE, 4, PJ_EINVAL, - "Error creating SSL context"); return PJ_EINVAL; } /* Create SSL context for the listener */ ctx = SSL_CTX_new(ssl_method); if (ctx == NULL) { - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "Error creating SSL context"); - return PJ_EINVAL; + PJ_LOG(1,(ssock->pool->obj_name, "Error creating OpenSSL context")); + return PJ_STATUS_FROM_OSSL(ERR_get_error()); } /* Apply credentials */ @@ -365,15 +381,11 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx) rc = SSL_CTX_load_verify_locations(ctx, cert->CA_file.ptr, NULL); if (rc != 1) { - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "Error loading/verifying CA list file '%s'", - cert->CA_file.ptr); + PJ_LOG(1,(ssock->pool->obj_name, "Error loading CA list file " + "'%s'", cert->CA_file.ptr)); SSL_CTX_free(ctx); - return PJ_EINVAL; + return PJ_STATUS_FROM_OSSL(ERR_get_error()); } - - PJ_LOG(5,(THIS_FILE, "CA file successfully loaded from '%s'", - cert->CA_file.ptr)); } /* Set password callback */ @@ -390,15 +402,11 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx) rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr); if(rc != 1) { - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "Error loading certificate chain file '%s'", - cert->cert_file.ptr); + PJ_LOG(1,(ssock->pool->obj_name, "Error loading certificate " + "chain file '%s'", cert->cert_file.ptr)); SSL_CTX_free(ctx); - return PJ_EINVAL; + return PJ_STATUS_FROM_OSSL(ERR_get_error()); } - - PJ_LOG(5,(THIS_FILE, "TLS certificate successfully loaded from '%s'", - cert->cert_file.ptr)); } @@ -409,15 +417,11 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx) SSL_FILETYPE_PEM); if(rc != 1) { - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "Error adding private key from '%s'", - cert->privkey_file.ptr); + PJ_LOG(1,(ssock->pool->obj_name, "Error adding private key " + "from '%s'", cert->privkey_file.ptr)); SSL_CTX_free(ctx); - return PJ_EINVAL; + return PJ_STATUS_FROM_OSSL(ERR_get_error()); } - - PJ_LOG(5,(THIS_FILE, "Private key successfully loaded from '%s'", - cert->privkey_file.ptr)); } } @@ -430,12 +434,10 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx) } if (ssock->is_server && ssock->param.require_client_cert) - mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_PEER; SSL_CTX_set_verify(ctx, mode, NULL); - PJ_LOG(5,(THIS_FILE, "Verification mode set to %d", mode)); - *p_ctx = ctx; return PJ_SUCCESS; @@ -473,6 +475,10 @@ static void reset_ssl_sock_state(pj_ssl_sock_t *ssock) ssock->asock = NULL; ssock->sock = PJ_INVALID_SOCKET; } + if (ssock->sock != PJ_INVALID_SOCKET) { + pj_sock_close(ssock->sock); + ssock->sock = PJ_INVALID_SOCKET; + } } @@ -528,16 +534,131 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) /* Finally, set chosen cipher list */ ret = SSL_set_cipher_list(ssock->ossl_ssl, buf); - if (ret < 1) { - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "Error setting cipher list"); - return PJ_EINVAL; - } + if (ret < 1) + return PJ_STATUS_FROM_OSSL(SSL_get_error(ssock->ossl_ssl, ret)); return PJ_SUCCESS; } +/* Parse OpenSSL ASN1_TIME to pj_time_val and GMT info */ +static pj_bool_t parse_ossl_asn1_time(pj_time_val *tv, pj_bool_t *gmt, + const ASN1_TIME *tm) +{ + unsigned long parts[7] = {0}; + char *p, *end; + unsigned len; + pj_bool_t utc; + pj_parsed_time pt; + int i; + + utc = tm->type == V_ASN1_UTCTIME; + p = (char*)tm->data; + len = tm->length; + end = p + len - 1; + + /* GMT */ + *gmt = (*end == 'Z'); + + /* parse parts */ + for (i = 0; i < 7 && p < end; ++i) { + pj_str_t st; + + if (i==0 && !utc) { + /* 4 digits year part for non-UTC time format */ + st.slen = 4; + } else if (i==6) { + /* fraction of seconds */ + if (*p == '.') ++p; + st.slen = end - p + 1; + } else { + /* other parts always 2 digits length */ + st.slen = 2; + } + st.ptr = p; + + parts[i] = pj_strtoul(&st); + p += st.slen; + } + + /* encode parts to pj_time_val */ + pt.year = parts[0]; + if (utc) + pt.year += (pt.year < 50)? 2000:1900; + pt.mon = parts[1] - 1; + pt.day = parts[2]; + pt.hour = parts[3]; + pt.min = parts[4]; + pt.sec = parts[5]; + pt.msec = parts[6]; + + pj_time_encode(&pt, tv); + + return PJ_TRUE; +} + + +/* Get certificate info from OpenSSL X509 */ +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_assert(pool && ci); + + if (!x) { + pj_bzero(ci, sizeof(pj_ssl_cert_info)); + return; + } + + pj_bzero(&info, sizeof(info)); + + /* 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, + X509_get_notBefore(x)); + parse_ossl_asn1_time(&info.validity_end, &info.validity_use_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; +} + + +/* Update local & remote certificates info. This function should be + * called after handshake or renegotiation successfully completed. + */ +static void update_certs_info(pj_ssl_sock_t *ssock) +{ + X509 *x; + + pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED); + + /* 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! */ + + /* 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); +} + + /* When handshake completed: * - notify application * - if handshake failed, reset SSL state @@ -546,6 +667,14 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, pj_status_t status) { + /* Cancel handshake timer */ + if (ssock->param.timer_heap) + pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->handshake_timer); + + /* Update certificates info on successful handshake */ + if (status == PJ_SUCCESS) + update_certs_info(ssock); + /* Accepting */ if (ssock->is_server) { if (status != PJ_SUCCESS) { @@ -707,6 +836,22 @@ static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock, return status; } + +static void handshake_timeout_cb(pj_timer_heap_t *th, + struct pj_timer_entry *te) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data; + + PJ_UNUSED_ARG(th); + + PJ_LOG(1,(ssock->pool->obj_name, "SSL handshake timeout after %d.%ds", + ssock->param.timeout.sec, ssock->param.timeout.msec)); + + on_handshake_complete(ssock, PJ_ETIMEDOUT); +} + + +/* Asynchronouse handshake */ static pj_status_t do_handshake(pj_ssl_sock_t *ssock) { pj_status_t status; @@ -721,10 +866,8 @@ static pj_status_t do_handshake(pj_ssl_sock_t *ssock) if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) { /* Handshake fails */ - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "SSL_do_handshake()"); pj_lock_release(ssock->write_mutex); - return PJ_ECANCELLED; + return PJ_STATUS_FROM_OSSL(err); } } @@ -766,15 +909,13 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock, pj_size_t nwritten; /* Socket error or closed */ - if (data == NULL || size < 0) - goto on_error; - - /* Consume the whole data */ - nwritten = BIO_write(ssock->ossl_rbio, data, size); - if (nwritten < size) { - status = PJ_ENOMEM; - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "BIO_write()"); - goto on_error; + if (data && size > 0) { + /* Consume the whole data */ + nwritten = BIO_write(ssock->ossl_rbio, data, size); + if (nwritten < size) { + status = PJ_STATUS_FROM_OSSL(ERR_get_error()); + goto on_error; + } } /* Check if SSL handshake hasn't finished yet */ @@ -802,15 +943,16 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock, * is on progress, so let's protect it with write mutex. */ pj_lock_acquire(ssock->write_mutex); - size_ = SSL_read(ssock->ossl_ssl, data_, size_); - if (size_ > 0) { - pj_lock_release(ssock->write_mutex); + pj_lock_release(ssock->write_mutex); + + if (size_ > 0 || status != PJ_SUCCESS) { if (ssock->param.cb.on_data_read) { pj_bool_t ret; pj_size_t remainder_ = 0; - buf->len += size_; + if (size_ > 0) + buf->len += size_; ret = (*ssock->param.cb.on_data_read)(ssock, buf->data, buf->len, status, @@ -825,7 +967,18 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock, */ buf->len = remainder_; } + + /* Active socket signalled connection closed/error, this has + * been signalled to the application along with any remaining + * buffer. So, let's just reset SSL socket now. + */ + if (status != PJ_SUCCESS) { + reset_ssl_sock_state(ssock); + return PJ_FALSE; + } + } else { + int err = SSL_get_error(ssock->ossl_ssl, size); /* SSL might just return SSL_ERROR_WANT_READ in @@ -833,26 +986,43 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock, */ if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) { + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(1,(ssock->pool->obj_name, "SSL_read() failed: %s", + errmsg)); + /* Reset SSL socket state, then return PJ_FALSE */ - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "SSL_read()"); - pj_lock_release(ssock->write_mutex); reset_ssl_sock_state(ssock); - return PJ_FALSE; + goto on_error; } - /* SSL may write something in case of re-negotiation */ - status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); - pj_lock_release(ssock->write_mutex); - if (status != PJ_SUCCESS && status != PJ_EPENDING) - goto on_error; + status = do_handshake(ssock); + if (status == PJ_SUCCESS) { + /* Renegotiation completed */ - /* If re-negotiation has been completed, start flushing - * delayed send. - */ - if (!SSL_renegotiate_pending(ssock->ossl_ssl)) { + /* Update certificates */ + update_certs_info(ssock); + + pj_lock_acquire(ssock->write_mutex); status = flush_delayed_send(ssock); - if (status != PJ_SUCCESS && status != PJ_EPENDING) + pj_lock_release(ssock->write_mutex); + + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(1,(ssock->pool->obj_name, "Failed to flush " + "delayed send: %s", errmsg)); goto on_error; + } + } else if (status != PJ_EPENDING) { + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(1,(ssock->pool->obj_name, "Renegotiation failed: " + "%s", errmsg)); + goto on_error; } break; @@ -943,9 +1113,13 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock, pj_activesock_cfg asock_cfg; unsigned i; pj_status_t status; + char buf[64]; PJ_UNUSED_ARG(src_addr_len); + PJ_LOG(4,(ssock_parent->pool->obj_name, "Incoming connection from %s", + pj_sockaddr_print(src_addr, buf, sizeof(buf), 3))); + /* Create new SSL socket instance */ status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param, &ssock); @@ -981,9 +1155,8 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock, /* Create SSL instance */ ssock->ossl_ssl = SSL_new(ssock->ossl_ctx); if (ssock->ossl_ssl == NULL) { - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "Error creating SSL connection object"); - status = PJ_EINVAL; + PJ_LOG(1,(ssock->pool->obj_name, "Error creating SSL instance")); + status = PJ_STATUS_FROM_OSSL(ERR_get_error()); goto on_return; } @@ -1049,6 +1222,16 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock, ssock->write_state.start = ssock->write_state.buf; ssock->write_state.len = 0; + /* Start handshake timer */ + if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || + ssock->param.timeout.msec != 0)) + { + pj_timer_entry_init(&ssock->handshake_timer, 0, ssock, + &handshake_timeout_cb); + pj_timer_heap_schedule(ssock->param.timer_heap, &ssock->handshake_timer, + &ssock->param.timeout); + } + /* Start SSL handshake */ ssock->ssl_state = SSL_STATE_HANDSHAKING; SSL_set_accept_state(ssock->ossl_ssl); @@ -1088,9 +1271,8 @@ static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, /* Create SSL instance */ ssock->ossl_ssl = SSL_new(ssock->ossl_ctx); if (ssock->ossl_ssl == NULL) { - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, - "Error creating SSL connection object"); - status = PJ_EINVAL; + PJ_LOG(1,(ssock->pool->obj_name, "Error creating SSL instance")); + status = PJ_STATUS_FROM_OSSL(ERR_get_error()); goto on_return; } @@ -1134,9 +1316,37 @@ static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, ssock->write_state.start = ssock->write_state.buf; ssock->write_state.len = 0; + /* Start handshake timer */ + if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || + ssock->param.timeout.msec != 0)) + { + pj_timer_entry_init(&ssock->handshake_timer, 0, ssock, + &handshake_timeout_cb); + pj_timer_heap_schedule(ssock->param.timer_heap, + &ssock->handshake_timer, + &ssock->param.timeout); + } + +#ifdef SSL_set_tlsext_host_name + /* Set server name to connect */ + if (ssock->param.server_name.slen) { + /* Server name is null terminated already */ + if (!SSL_set_tlsext_host_name(ssock->ossl_ssl, + ssock->param.server_name.ptr)) + { + char err_str[PJ_ERR_MSG_SIZE]; + + ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str)); + PJ_LOG(3,(ssock->pool->obj_name, "SSL_set_tlsext_host_name() " + "failed: %s", err_str)); + } + } +#endif + /* Start SSL handshake */ ssock->ssl_state = SSL_STATE_HANDSHAKING; SSL_set_connect_state(ssock->ossl_ssl); + status = do_handshake(ssock); if (status != PJ_EPENDING) goto on_return; @@ -1185,13 +1395,18 @@ PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate( pj_pool_t *pool, const pj_ssl_cert_t *cert) { + pj_ssl_cert_t *cert_; + PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL); - ssock->cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); - pj_strdup_with_null(pool, &ssock->cert->CA_file, &cert->CA_file); - pj_strdup_with_null(pool, &ssock->cert->cert_file, &cert->cert_file); - pj_strdup_with_null(pool, &ssock->cert->privkey_file, &cert->privkey_file); - pj_strdup_with_null(pool, &ssock->cert->privkey_pass, &cert->privkey_pass); + cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); + pj_memcpy(cert_, cert, sizeof(cert)); + pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file); + pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file); + pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file); + pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass); + + ssock->cert = cert_; return PJ_SUCCESS; } @@ -1262,8 +1477,10 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, for (i = 0; i < param->ciphers_num; ++i) ssock->param.ciphers[i] = param->ciphers[i]; } - pj_strdup_with_null(pool, &ssock->param.servername, - ¶m->servername); + + /* Server name must be null-terminated */ + pj_strdup_with_null(pool, &ssock->param.server_name, + ¶m->server_name); /* Finally */ *p_ssock = ssock; @@ -1345,6 +1562,10 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, /* Remote address */ 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; } return PJ_SUCCESS; @@ -1489,11 +1710,11 @@ static pj_status_t ssl_write(pj_ssl_sock_t *ssock, /* Re-negotiation is on progress, flush re-negotiation data */ status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); if (status == PJ_SUCCESS || status == PJ_EPENDING) + /* Just return PJ_EBUSY when re-negotiation is on progress */ status = PJ_EBUSY; } else { /* Some problem occured */ - ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "SSL_write()"); - status = PJ_ECANCELLED; + status = PJ_STATUS_FROM_OSSL(err); } } else { /* nwritten < *size, shouldn't happen, unless write BIO cannot hold @@ -1776,5 +1997,25 @@ on_error: } -//#endif /* PJ_HAS_SSL_SOCK */ +PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) +{ + int ret; + pj_status_t status; + + PJ_ASSERT_RETURN(ssock->ssl_state == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + if (SSL_renegotiate_pending(ssock->ossl_ssl)) + return PJ_EPENDING; + + ret = SSL_renegotiate(ssock->ossl_ssl); + if (ret <= 0) { + status = PJ_STATUS_FROM_OSSL(SSL_get_error(ssock->ossl_ssl, ret)); + } else { + status = do_handshake(ssock); + } + + return status; +} + +#endif /* PJ_HAS_SSL_SOCK */ diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp index 06165f37..0619fd98 100644 --- a/pjlib/src/pj/ssl_sock_symbian.cpp +++ b/pjlib/src/pj/ssl_sock_symbian.cpp @@ -499,7 +499,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, for (i = 0; i < param->ciphers_num; ++i) ssock->ciphers[i] = param->ciphers[i]; } - pj_strdup_with_null(pool, &ssock->servername, ¶m->servername); + pj_strdup_with_null(pool, &ssock->servername, ¶m->server_name); /* Finally */ *p_ssock = ssock; @@ -1085,3 +1085,9 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock, return status; } + +PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) +{ + PJ_UNUSED_ARG(ssock); + return PJ_ENOTSUP; +} diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c new file mode 100644 index 00000000..8fd42ef0 --- /dev/null +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -0,0 +1,820 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 "test.h" +#include + +#define ECHO_SERVER_NAME "localhost" +#define ECHO_SERVER_ADDR "localhost" +#define ECHO_SERVER_PORT 12345 + +#define CERT_DIR "..\\build\\" +#define CERT_CA_FILE NULL +#define CERT_FILE CERT_DIR "cacert.pem" +#define CERT_PRIVKEY_FILE CERT_DIR "privkey.pem" +#define CERT_PRIVKEY_PASS "" + + +#if INCLUDE_SSLSOCK_TEST + + +struct send_key { + pj_ioqueue_op_key_t op_key; +}; + + +static int get_cipher_list(void) { + pj_status_t status; + pj_ssl_cipher ciphers[100]; + unsigned cipher_num; + unsigned i; + + cipher_num = PJ_ARRAY_SIZE(ciphers); + status = pj_ssl_cipher_get_availables(ciphers, &cipher_num); + if (status != PJ_SUCCESS) { + app_perror("...FAILED to get available ciphers", status); + return -10; + } + + PJ_LOG(3, ("", "...Found %u ciphers:", cipher_num)); + for (i = 0; i < cipher_num; ++i) { + const char* st; + st = pj_ssl_cipher_name(ciphers[i]); + if (st == NULL) + st = "[Unknown]"; + + PJ_LOG(3, ("", "...%3u: 0x%08x=%s", i+1, ciphers[i], st)); + } + + return PJ_SUCCESS; +} + + +struct test_state +{ + pj_pool_t *pool; /* pool */ + pj_bool_t echo; /* echo received data */ + pj_status_t err; /* error flag */ + unsigned sent; /* bytes sent */ + unsigned recv; /* bytes received */ + pj_uint8_t read_buf[256]; /* read buffer */ + pj_bool_t done; /* test done flag */ + char *send_str; /* data to send once connected */ + unsigned send_str_len; /* send data length */ + pj_bool_t check_echo; /* flag to compare sent & echoed data */ + const char *check_echo_ptr; /* pointer/cursor for comparing data */ + struct send_key send_key; /* send op key */ +}; + +static void dump_cert_info(const char *prefix, const pj_ssl_cert_info *ci) +{ + 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":""))); +} + + +static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock, + pj_status_t status) +{ + struct test_state *st = (struct test_state*) + pj_ssl_sock_get_user_data(ssock); + void *read_buf[1]; + pj_ssl_sock_info info; + const char *tmp_st; + char buf[64]; + + if (status != PJ_SUCCESS) { + app_perror("...ERROR ssl_on_connect_complete()", status); + goto on_return; + } + + status = pj_ssl_sock_get_info(ssock, &info); + if (status != PJ_SUCCESS) { + app_perror("...ERROR pj_ssl_sock_get_info()", status); + goto on_return; + } + + pj_sockaddr_print((pj_sockaddr_t*)&info.remote_addr, buf, sizeof(buf), 1); + PJ_LOG(3, ("", "...Connected to %s!", buf)); + + /* 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); + } + + /* Start sending data */ + while (st->sent < st->send_str_len) { + pj_ssize_t size; + + size = st->send_str_len - st->sent; + status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, + st->send_str + st->sent, &size, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror("...ERROR pj_ssl_sock_send()", status); + goto on_return; + } + + if (status == PJ_SUCCESS) + st->sent += size; + else + break; + } + + /* Start reading data */ + read_buf[0] = st->read_buf; + status = pj_ssl_sock_start_read2(ssock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror("...ERROR pj_ssl_sock_start_read2()", status); + goto on_return; + } + +on_return: + st->err = status; + return PJ_TRUE; +} + + +static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock, + pj_ssl_sock_t *newsock, + const pj_sockaddr_t *src_addr, + int src_addr_len) +{ + struct test_state *st = (struct test_state*) + pj_ssl_sock_get_user_data(ssock); + void *read_buf[1]; + pj_ssl_sock_info info; + pj_status_t status; + const char *tmp_st; + char buf[64]; + + PJ_UNUSED_ARG(src_addr_len); + + 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)); + + /* 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); + } + + pj_ssl_sock_set_user_data(newsock, st); + + /* Start sending data */ + while (st->sent < st->send_str_len) { + pj_ssize_t size; + + size = st->send_str_len - st->sent; + status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, + st->send_str + st->sent, &size, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror("...ERROR pj_ssl_sock_send()", status); + goto on_return; + } + + if (status == PJ_SUCCESS) + st->sent += size; + else + break; + } + + /* Start reading data */ + read_buf[0] = st->read_buf; + status = pj_ssl_sock_start_read2(newsock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror("...ERROR pj_ssl_sock_start_read2()", status); + goto on_return; + } + +on_return: + st->err = status; + return PJ_TRUE; +} + +static pj_bool_t ssl_on_data_read(pj_ssl_sock_t *ssock, + void *data, + pj_size_t size, + pj_status_t status, + pj_size_t *remainder) +{ + struct test_state *st = (struct test_state*) + pj_ssl_sock_get_user_data(ssock); + + PJ_UNUSED_ARG(remainder); + PJ_UNUSED_ARG(data); + + if (size > 0) { + pj_size_t consumed; + + /* Set random remainder */ + *remainder = pj_rand() % 100; + + /* Apply zero remainder if: + * - remainder is less than size, or + * - connection closed/error + * - echo/check_eco set + */ + if (*remainder > size || status != PJ_SUCCESS || st->echo || st->check_echo) + *remainder = 0; + + consumed = size - *remainder; + st->recv += consumed; + + //printf("%.*s", consumed, (char*)data); + + pj_memmove(data, (char*)data + consumed, *remainder); + + /* Echo data when specified to */ + if (st->echo) { + pj_ssize_t size_ = consumed; + status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, data, &size_, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror("...ERROR pj_ssl_sock_send()", status); + goto on_return; + } + + if (status == PJ_SUCCESS) + st->sent += size_; + } + + /* Verify echoed data when specified to */ + if (st->check_echo) { + if (!st->check_echo_ptr) + st->check_echo_ptr = st->send_str; + + if (pj_memcmp(st->check_echo_ptr, data, consumed)) { + status = PJ_EINVAL; + app_perror("...ERROR echoed data not exact", status); + goto on_return; + } + st->check_echo_ptr += consumed; + + if (st->send_str_len == st->recv) + st->done = PJ_TRUE; + } + } + + if (status != PJ_SUCCESS) { + if (status == PJ_EEOF) { + status = PJ_SUCCESS; + st->done = PJ_TRUE; + } else { + app_perror("...ERROR ssl_on_data_read()", status); + } + } + +on_return: + st->err = status; + return PJ_TRUE; +} + +static pj_bool_t ssl_on_data_sent(pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t sent) +{ + struct test_state *st = (struct test_state*) + pj_ssl_sock_get_user_data(ssock); + PJ_UNUSED_ARG(op_key); + + if (sent < 1) { + st->err++; + } else { + st->sent += sent; + + /* Send more if any */ + while (st->sent < st->send_str_len) { + pj_ssize_t size; + pj_status_t status; + + size = st->send_str_len - st->sent; + status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, + st->send_str + st->sent, &size, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror("...ERROR pj_ssl_sock_send()", status); + st->err++; + break; + } + + if (status == PJ_SUCCESS) + st->sent += size; + else + break; + } + } + + return PJ_TRUE; +} + +#define HTTP_REQ "GET / HTTP/1.0\r\n\r\n"; +#define HTTP_SERVER_ADDR "trac.pjsip.org" +#define HTTP_SERVER_PORT 443 + +static int https_client_test(void) +{ + pj_pool_t *pool = NULL; + pj_ioqueue_t *ioqueue = NULL; + pj_ssl_sock_t *ssock = NULL; + pj_ssl_sock_param param; + pj_status_t status; + struct test_state state = {0}; + pj_sockaddr local_addr, rem_addr; + pj_str_t tmp_st; + + pool = pj_pool_create(mem, "http_get", 256, 256, NULL); + + status = pj_ioqueue_create(pool, 4, &ioqueue); + if (status != PJ_SUCCESS) { + goto on_return; + } + + state.pool = pool; + state.send_str = HTTP_REQ; + state.send_str_len = pj_ansi_strlen(state.send_str); + + pj_ssl_sock_param_default(¶m); + param.cb.on_connect_complete = &ssl_on_connect_complete; + param.cb.on_data_read = &ssl_on_data_read; + param.cb.on_data_sent = &ssl_on_data_sent; + param.ioqueue = ioqueue; + param.user_data = &state; + param.server_name = pj_str((char*)HTTP_SERVER_ADDR); + + status = pj_ssl_sock_create(pool, ¶m, &ssock); + if (status != PJ_SUCCESS) { + goto on_return; + } + + pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, "0.0.0.0"), 0); + pj_sockaddr_init(PJ_AF_INET, &rem_addr, pj_strset2(&tmp_st, HTTP_SERVER_ADDR), HTTP_SERVER_PORT); + status = pj_ssl_sock_start_connect(ssock, pool, &local_addr, &rem_addr, sizeof(rem_addr)); + if (status == PJ_SUCCESS) { + ssl_on_connect_complete(ssock, PJ_SUCCESS); + } else if (status == PJ_EPENDING) { + status = PJ_SUCCESS; + } else { + goto on_return; + } + + /* Wait until everything has been sent/received */ + while (state.err == PJ_SUCCESS && !state.done) { +#ifdef PJ_SYMBIAN + pj_symbianos_poll(-1, 1000); +#else + pj_time_val delay = {0, 100}; + pj_ioqueue_poll(ioqueue, &delay); +#endif + } + + if (state.err) { + status = state.err; + goto on_return; + } + + PJ_LOG(3, ("", "...Done!")); + PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state.sent, state.recv)); + +on_return: + if (ssock) + pj_ssl_sock_close(ssock); + if (ioqueue) + pj_ioqueue_destroy(ioqueue); + if (pool) + pj_pool_release(pool); + + return status; +} + + +static int echo_test(pj_ssl_sock_proto proto, pj_ssl_cipher srv_cipher, + pj_ssl_cipher cli_cipher) +{ + pj_pool_t *pool = NULL; + pj_ioqueue_t *ioqueue = NULL; + pj_ssl_sock_t *ssock_serv = NULL; + pj_ssl_sock_t *ssock_cli = NULL; + pj_ssl_sock_param param; + struct test_state state_serv = { 0 }; + struct test_state state_cli = { 0 }; + pj_sockaddr local_addr, rem_addr; + pj_str_t tmp_st; + pj_ssl_cipher ciphers[1]; + pj_ssl_cert_t *cert = NULL; + pj_status_t status; + + pool = pj_pool_create(mem, "echo", 256, 256, NULL); + + status = pj_ioqueue_create(pool, 4, &ioqueue); + if (status != PJ_SUCCESS) { + 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(¶m); + param.proto = proto; + param.cb.on_accept_complete = &ssl_on_accept_complete; + param.cb.on_connect_complete = &ssl_on_connect_complete; + param.cb.on_data_read = &ssl_on_data_read; + param.cb.on_data_sent = &ssl_on_data_sent; + param.ioqueue = ioqueue; + param.ciphers_num = 1; + param.ciphers = ciphers; + + /* SERVER */ + param.user_data = &state_serv; + ciphers[0] = srv_cipher; + + state_serv.pool = pool; + state_serv.echo = PJ_TRUE; + + status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); + if (status != PJ_SUCCESS) { + goto on_return; + } + + status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + + pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, ECHO_SERVER_ADDR), ECHO_SERVER_PORT); + status = pj_ssl_sock_start_accept(ssock_serv, pool, &local_addr, sizeof(local_addr)); + if (status != PJ_SUCCESS) { + goto on_return; + } + + /* CLIENT */ + param.user_data = &state_cli; + ciphers[0] = cli_cipher; + + state_cli.pool = pool; + state_cli.check_echo = PJ_TRUE; + + { + pj_time_val now; + + pj_gettimeofday(&now); + pj_srand((pj_rand()%now.sec) * (pj_rand()%now.msec)); + state_cli.send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024; + } + state_cli.send_str = pj_pool_alloc(pool, state_cli.send_str_len); + { + unsigned i; + for (i = 0; i < state_cli.send_str_len; ++i) + state_cli.send_str[i] = (char)(pj_rand() % 256); + } + + status = pj_ssl_sock_create(pool, ¶m, &ssock_cli); + if (status != PJ_SUCCESS) { + goto on_return; + } + + pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, "0.0.0.0"), 0); + pj_sockaddr_init(PJ_AF_INET, &rem_addr, pj_strset2(&tmp_st, ECHO_SERVER_ADDR), ECHO_SERVER_PORT); + status = pj_ssl_sock_start_connect(ssock_cli, pool, &local_addr, &rem_addr, sizeof(rem_addr)); + if (status == PJ_SUCCESS) { + ssl_on_connect_complete(ssock_cli, PJ_SUCCESS); + } else if (status == PJ_EPENDING) { + status = PJ_SUCCESS; + } else { + goto on_return; + } + + /* Wait until everything has been sent/received or error */ + while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done) + { +#ifdef PJ_SYMBIAN + pj_symbianos_poll(-1, 1000); +#else + pj_time_val delay = {0, 100}; + pj_ioqueue_poll(ioqueue, &delay); +#endif + } + + if (state_serv.err || state_cli.err) { + if (state_serv.err != PJ_SUCCESS) + status = state_serv.err; + else + status = state_cli.err; + + goto on_return; + } + + PJ_LOG(3, ("", "...Done!")); + PJ_LOG(3, ("", ".....Server sent/recv: %d/%d bytes", state_serv.sent, state_serv.recv)); + PJ_LOG(3, ("", ".....Client sent/recv: %d/%d bytes", state_cli.sent, state_cli.recv)); + +on_return: + if (ssock_serv) + pj_ssl_sock_close(ssock_serv); + if (ssock_cli) + pj_ssl_sock_close(ssock_cli); + if (ioqueue) + pj_ioqueue_destroy(ioqueue); + if (pool) + pj_pool_release(pool); + + return status; +} + + +static pj_bool_t asock_on_data_read(pj_activesock_t *asock, + void *data, + pj_size_t size, + pj_status_t status, + pj_size_t *remainder) +{ + struct test_state *st = (struct test_state*) + pj_activesock_get_user_data(asock); + + PJ_UNUSED_ARG(data); + PJ_UNUSED_ARG(size); + PJ_UNUSED_ARG(remainder); + + if (status != PJ_SUCCESS) { + if (status == PJ_EEOF) { + status = PJ_SUCCESS; + st->done = PJ_TRUE; + } else { + app_perror("...ERROR asock_on_data_read()", status); + } + } + + st->err = status; + + return PJ_TRUE; +} + + +static pj_bool_t asock_on_connect_complete(pj_activesock_t *asock, + pj_status_t status) +{ + struct test_state *st = (struct test_state*) + pj_activesock_get_user_data(asock); + + if (status == PJ_SUCCESS) { + status = pj_activesock_start_read(asock, st->pool, 1, 0); + } + + st->err = status; + + return PJ_TRUE; +} + + +/* Set ms_timeout to 0 to disable timer */ +static int client_non_ssl(unsigned ms_timeout) +{ + pj_pool_t *pool = NULL; + pj_ioqueue_t *ioqueue = NULL; + pj_timer_heap_t *timer = NULL; + pj_ssl_sock_t *ssock_serv = NULL; + pj_activesock_t *asock_cli = NULL; + pj_activesock_cb asock_cb = { 0 }; + pj_sock_t sock = PJ_INVALID_SOCKET; + pj_ssl_sock_param param; + struct test_state state_serv = { 0 }; + struct test_state state_cli = { 0 }; + pj_sockaddr listen_addr; + pj_str_t tmp_st; + pj_ssl_cert_t *cert = NULL; + pj_status_t status; + + pool = pj_pool_create(mem, "echo", 256, 256, NULL); + + status = pj_ioqueue_create(pool, 4, &ioqueue); + if (status != PJ_SUCCESS) { + goto on_return; + } + + status = pj_timer_heap_create(pool, 4, &timer); + if (status != PJ_SUCCESS) { + 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(¶m); + param.cb.on_accept_complete = &ssl_on_accept_complete; + param.cb.on_data_read = &ssl_on_data_read; + param.cb.on_data_sent = &ssl_on_data_sent; + param.ioqueue = ioqueue; + param.timeout.sec = 0; + param.timeout.msec = ms_timeout; + param.timer_heap = timer; + pj_time_val_normalize(¶m.timeout); + + /* SERVER */ + param.user_data = &state_serv; + state_serv.pool = pool; + + status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); + if (status != PJ_SUCCESS) { + goto on_return; + } + + status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + + pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, ECHO_SERVER_ADDR), ECHO_SERVER_PORT); + status = pj_ssl_sock_start_accept(ssock_serv, pool, &listen_addr, sizeof(listen_addr)); + if (status != PJ_SUCCESS) { + goto on_return; + } + + /* CLIENT */ + state_cli.pool = pool; + status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); + if (status != PJ_SUCCESS) { + goto on_return; + } + + asock_cb.on_connect_complete = &asock_on_connect_complete; + asock_cb.on_data_read = &asock_on_data_read; + status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL, + ioqueue, &asock_cb, &state_cli, &asock_cli); + if (status != PJ_SUCCESS) { + goto on_return; + } + + status = pj_activesock_start_connect(asock_cli, pool, (pj_sockaddr_t*)&listen_addr, + pj_sockaddr_get_len(&listen_addr)); + if (status == PJ_SUCCESS) { + asock_on_connect_complete(asock_cli, PJ_SUCCESS); + } else if (status == PJ_EPENDING) { + status = PJ_SUCCESS; + } else { + goto on_return; + } + + /* Wait until everything has been sent/received or error */ + while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done) + { +#ifdef PJ_SYMBIAN + pj_symbianos_poll(-1, 1000); +#else + pj_time_val delay = {0, 100}; + pj_ioqueue_poll(ioqueue, &delay); + pj_timer_heap_poll(timer, &delay); +#endif + } + + if (state_serv.err || state_cli.err) { + if (state_serv.err != PJ_SUCCESS) + status = state_serv.err; + else + status = state_cli.err; + + goto on_return; + } + + PJ_LOG(3, ("", "...Done!")); + +on_return: + if (ssock_serv) + pj_ssl_sock_close(ssock_serv); + if (asock_cli) + pj_activesock_close(asock_cli); + if (timer) + pj_timer_heap_destroy(timer); + if (ioqueue) + pj_ioqueue_destroy(ioqueue); + if (pool) + pj_pool_release(pool); + + return status; +} + + +int ssl_sock_test(void) +{ + int ret; + + PJ_LOG(3,("", "..get cipher list test")); + ret = get_cipher_list(); + if (ret != 0) + return ret; + +#if 0 + PJ_LOG(3,("", "..https client test")); + ret = https_client_test(); + if (ret != 0) + return ret; +#endif + + PJ_LOG(3,("", "..echo test w/ TLSv1 and TLS_RSA_WITH_DES_CBC_SHA cipher")); + ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA); + 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, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA); + if (ret != 0) + return ret; + + PJ_LOG(3,("", "..echo test w/ incompatible ciphers")); + ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA); + if (ret == 0) + return -10; + + PJ_LOG(3,("", "..client non-SSL timeout in 5 secs")); + ret = client_non_ssl(5000); + if (ret != 0) + return ret; + + return 0; +} + +#else /* INCLUDE_SSLSOCK_TEST */ +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_ssl_sock_test; +#endif /* INCLUDE_SSLSOCK_TEST */ + diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index b3e2951d..9d7c1ba4 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -167,6 +167,10 @@ int test_inner(void) DO_TEST( file_test() ); #endif +#if INCLUDE_SSLSOCK_TEST + DO_TEST( ssl_sock_test() ); +#endif + #if INCLUDE_ECHO_SERVER //echo_server(); //echo_srv_sync(); diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 0f128b05..2d65d15d 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -54,6 +54,7 @@ #define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK #define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK #define INCLUDE_ACTIVESOCK_TEST GROUP_NETWORK +#define INCLUDE_SSLSOCK_TEST (PJ_HAS_SSL_SOCK && GROUP_NETWORK) #define INCLUDE_IOQUEUE_PERF_TEST (PJ_HAS_THREADS && GROUP_NETWORK) #define INCLUDE_IOQUEUE_UNREG_TEST (PJ_HAS_THREADS && GROUP_NETWORK) #define INCLUDE_FILE_TEST GROUP_FILE @@ -96,6 +97,7 @@ extern int tcp_ioqueue_test(void); extern int ioqueue_perf_test(void); extern int activesock_test(void); extern int file_test(void); +extern int ssl_sock_test(void); extern int echo_server(void); extern int echo_client(int sock_type, const char *server, int port); @@ -104,6 +106,7 @@ extern int echo_srv_sync(void); extern int udp_echo_srv_ioqueue(void); extern int echo_srv_common_loop(pj_atomic_t *bytes_counter); + extern pj_pool_factory *mem; extern int test_main(void); diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index c0ecf992..d825db83 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -41,7 +41,7 @@ export PJSIP_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ sip_endpoint.o sip_util.o sip_util_proxy.o \ sip_resolve.o sip_transport.o sip_transport_loop.o \ sip_transport_udp.o sip_transport_tcp.o \ - sip_transport_tls_ossl.o sip_auth_aka.o sip_auth_client.o \ + sip_transport_tls.o sip_auth_aka.o sip_auth_client.o \ sip_auth_msg.o sip_auth_parser.o \ sip_auth_server.o \ sip_transaction.o sip_util_statefull.o \ diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp index 4a5389b5..95a02a1b 100644 --- a/pjsip/build/pjsip_core.dsp +++ b/pjsip/build/pjsip_core.dsp @@ -156,7 +156,20 @@ SOURCE=..\src\pjsip\sip_transport_tcp.c # End Source File # Begin Source File +SOURCE=..\src\pjsip\sip_transport_tls.c +# End Source File +# Begin Source File + SOURCE=..\src\pjsip\sip_transport_tls_ossl.c + +!IF "$(CFG)" == "pjsip_core - Win32 Release" + +!ELSEIF "$(CFG)" == "pjsip_core - Win32 Debug" + +# PROP Exclude_From_Build 1 + +!ENDIF + # End Source File # Begin Source File diff --git a/pjsip/build/pjsip_core.vcproj b/pjsip/build/pjsip_core.vcproj index 2aef6d4c..8d0228a5 100644 --- a/pjsip/build/pjsip_core.vcproj +++ b/pjsip/build/pjsip_core.vcproj @@ -34,11 +34,11 @@ + + + + - - + + + + - - + + - - - - - - + + + + + tls_setting.require_client_cert; - ssock_param.servername = listener->tls_setting.server_name; + 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; @@ -789,7 +789,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, 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.servername = listener->tls_setting.server_name; + ssock_param.server_name = listener->tls_setting.server_name; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = NULL; /* pending, must be set later */ ssock_param.verify_peer = listener->tls_setting.verify_server; -- cgit v1.2.3