Index: pjproject/trunk/build.symbian/pjlib.mmp
===================================================================
--- pjproject/trunk/build.symbian/pjlib.mmp	(revision 2912)
+++ pjproject/trunk/build.symbian/pjlib.mmp	(revision 2913)
@@ -72,4 +72,6 @@
 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
Index: pjproject/trunk/build.symbian/pjsip.mmp
===================================================================
--- pjproject/trunk/build.symbian/pjsip.mmp	(revision 2912)
+++ pjproject/trunk/build.symbian/pjsip.mmp	(revision 2913)
@@ -52,4 +52,5 @@
 SOURCE	sip_transport_tcp.c
 SOURCE	sip_transport_udp.c
+SOURCE	sip_transport_tls.c
 SOURCE	sip_ua_layer.c
 SOURCE	sip_uri.c
Index: pjproject/trunk/build.symbian/symbian_ua.mmp
===================================================================
--- pjproject/trunk/build.symbian/symbian_ua.mmp	(revision 2912)
+++ pjproject/trunk/build.symbian/symbian_ua.mmp	(revision 2913)
@@ -70,5 +70,5 @@
 #endif
 
-LIBRARY			esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib 
+LIBRARY			esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib securesocket.lib 
 
 // The default 8KB seems to be insufficient with all bells and
Index: pjproject/trunk/pjlib/include/pj/ssl_sock.h
===================================================================
--- pjproject/trunk/pjlib/include/pj/ssl_sock.h	(revision 2913)
+++ pjproject/trunk/pjlib/include/pj/ssl_sock.h	(revision 2913)
@@ -0,0 +1,652 @@
+/* $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 
+ */
+#ifndef __PJ_SSL_SOCK_H__
+#define __PJ_SSL_SOCK_H__
+
+/**
+ * @file ssl_sock.h
+ * @brief Secure socket
+ */
+
+#include <pj/ioqueue.h>
+#include <pj/sock.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_SSL_SOCK Secure socket I/O
+ * @brief Secure socket provides security on socket operation using standard
+ * security protocols such as SSL and TLS.
+ * @ingroup PJ_IO
+ * @{
+ *
+ * Secure socket wraps normal socket and applies security features, i.e: 
+ * privacy and data integrity, on the socket traffic, using standard security
+ * protocols such as SSL and TLS.
+ *
+ * Secure socket employs active socket operations, which is similar to (and
+ * described more detail) in \ref PJ_ACTIVESOCK.
+ */
+
+/**
+ * Opaque declaration of certificate or endpoint credentials. This may contains
+ * certificate, private key, and trusted Certificate Authorities lists.
+ */
+typedef struct pj_ssl_cert_t pj_ssl_cert_t;
+
+/**
+ * This opaque structure describes the secure socket.
+ */
+typedef struct pj_ssl_sock_t pj_ssl_sock_t;
+
+/**
+ * This structure contains the callbacks to be called by the secure socket.
+ */
+typedef struct pj_ssl_sock_cb
+{
+    /**
+     * This callback is called when a data arrives as the result of
+     * pj_ssl_sock_start_read().
+     *
+     * @param ssock	The secure socket.
+     * @param data	The buffer containing the new data, if any. If 
+     *			the status argument is non-PJ_SUCCESS, this 
+     *			argument may be NULL.
+     * @param size	The length of data in the buffer.
+     * @param status	The status of the read operation. This may contain
+     *			non-PJ_SUCCESS for example when the TCP connection
+     *			has been closed. In this case, the buffer may
+     *			contain left over data from previous callback which
+     *			the application may want to process.
+     * @param remainder	If application wishes to leave some data in the 
+     *			buffer (common for TCP applications), it should 
+     *			move the remainder data to the front part of the 
+     *			buffer and set the remainder length here. The value
+     *			of this parameter will be ignored for datagram
+     *			sockets.
+     *
+     * @return		PJ_TRUE if further read is desired, and PJ_FALSE 
+     *			when application no longer wants to receive data.
+     *			Application may destroy the secure socket in the
+     *			callback and return PJ_FALSE here.
+     */
+    pj_bool_t (*on_data_read)(pj_ssl_sock_t *ssock,
+			      void *data,
+			      pj_size_t size,
+			      pj_status_t status,
+			      pj_size_t *remainder);
+    /**
+     * This callback is called when a packet arrives as the result of
+     * pj_ssl_sock_start_recvfrom().
+     *
+     * @param ssock	The secure socket.
+     * @param data	The buffer containing the packet, if any. If 
+     *			the status argument is non-PJ_SUCCESS, this 
+     *			argument will be set to NULL.
+     * @param size	The length of packet in the buffer. If 
+     *			the status argument is non-PJ_SUCCESS, this 
+     *			argument will be set to zero.
+     * @param src_addr	Source address of the packet.
+     * @param addr_len	Length of the source address.
+     * @param status	This contains
+     *
+     * @return		PJ_TRUE if further read is desired, and PJ_FALSE 
+     *			when application no longer wants to receive data.
+     *			Application may destroy the secure socket in the
+     *			callback and return PJ_FALSE here.
+     */
+    pj_bool_t (*on_data_recvfrom)(pj_ssl_sock_t *ssock,
+				  void *data,
+				  pj_size_t size,
+				  const pj_sockaddr_t *src_addr,
+				  int addr_len,
+				  pj_status_t status);
+
+    /**
+     * This callback is called when data has been sent.
+     *
+     * @param ssock	The secure socket.
+     * @param send_key	Key associated with the send operation.
+     * @param sent	If value is positive non-zero it indicates the
+     *			number of data sent. When the value is negative,
+     *			it contains the error code which can be retrieved
+     *			by negating the value (i.e. status=-sent).
+     *
+     * @return		Application may destroy the secure socket in the
+     *			callback and return PJ_FALSE here.
+     */
+    pj_bool_t (*on_data_sent)(pj_ssl_sock_t *ssock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent);
+
+    /**
+     * This callback is called when new connection arrives as the result
+     * of pj_ssl_sock_start_accept().
+     *
+     * @param ssock	The secure socket.
+     * @param newsock	The new incoming secure socket.
+     * @param src_addr	The source address of the connection.
+     * @param addr_len	Length of the source address.
+     *
+     * @return		PJ_TRUE if further accept() is desired, and PJ_FALSE
+     *			when application no longer wants to accept incoming
+     *			connection. Application may destroy the secure socket
+     *			in the callback and return PJ_FALSE here.
+     */
+    pj_bool_t (*on_accept_complete)(pj_ssl_sock_t *ssock,
+				    pj_ssl_sock_t *newsock,
+				    const pj_sockaddr_t *src_addr,
+				    int src_addr_len);
+
+    /**
+     * This callback is called when pending connect operation has been
+     * completed.
+     *
+     * @param ssock	The secure socket.
+     * @param status	The connection result. If connection has been
+     *			successfully established, the status will contain
+     *			PJ_SUCCESS.
+     *
+     * @return		Application may destroy the secure socket in the
+     *			callback and return PJ_FALSE here. 
+     */
+    pj_bool_t (*on_connect_complete)(pj_ssl_sock_t *ssock,
+				     pj_status_t status);
+
+} pj_ssl_sock_cb;
+
+
+/** 
+ * Enumeration of secure socket protocol types.
+ */
+typedef enum pj_ssl_sock_proto
+{
+    PJ_SSL_SOCK_PROTO_DEFAULT,	    /**< Default protocol of backend.	*/
+    PJ_SSL_SOCK_PROTO_TLS1,	    /**< TLSv1.0 protocol.		*/
+    PJ_SSL_SOCK_PROTO_SSL2,	    /**< SSLv2.0 protocol.		*/
+    PJ_SSL_SOCK_PROTO_SSL3,	    /**< SSLv3.0 protocol.		*/
+    PJ_SSL_SOCK_PROTO_SSL23,	    /**< SSLv3.0 but can roll back to 
+					 SSLv2.0.			*/
+    PJ_SSL_SOCK_PROTO_DTLS1	    /**< DTLSv1.0 protocol.		*/
+} pj_ssl_sock_proto;
+
+
+/**
+ * Definition of secure socket info structure.
+ */
+typedef struct pj_ssl_sock_info 
+{
+    /**
+     * Describes whether secure socket connection is established, i.e: TLS/SSL 
+     * 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 can be known only when 
+     * connection is established.
+     */
+    pj_str_t cipher;
+    /**
+     * Describes local address.
+     */
+    pj_sockaddr local_addr;
+    /**
+     * Describes remote address.
+     */
+    pj_sockaddr remote_addr;
+   
+} pj_ssl_sock_info;
+
+/**
+ * Definition of secure socket creation parameters.
+ */
+typedef struct pj_ssl_sock_param
+{
+    /**
+     * Specifies socket address family, either pj_AF_INET() and pj_AF_INET6().
+     *
+     * Default is pj_AF_INET().
+     */
+    int sock_af;
+
+    /**
+     * Specify socket type, either pj_SOCK_DGRAM() or pj_SOCK_STREAM().
+     *
+     * Default is pj_SOCK_STREAM().
+     */
+    int sock_type;
+
+    /**
+     * Specify the ioqueue to use. Secure socket uses the ioqueue to perform
+     * active socket operations, see \ref PJ_ACTIVESOCK for more detail.
+     */
+    pj_ioqueue_t *ioqueue;
+
+    /**
+     * Specify secure socket callbacks, see #pj_ssl_sock_cb.
+     */
+    pj_ssl_sock_cb cb;
+
+    /**
+     * Specify secure socket user data.
+     */
+    void *user_data;
+
+    /**
+     * Specify security protocol to use, see #pj_ssl_sock_proto.
+     *
+     * Default is PJ_SSL_SOCK_PROTO_DEFAULT.
+     */
+    pj_ssl_sock_proto proto;
+
+    /**
+     * Number of concurrent asynchronous operations that is to be supported
+     * by the secure socket. This value only affects socket receive and
+     * accept operations -- the secure socket will issue one or more 
+     * asynchronous read and accept operations based on the value of this
+     * field. Setting this field to more than one will allow more than one
+     * incoming data or incoming connections to be processed simultaneously
+     * on multiprocessor systems, when the ioqueue is polled by more than
+     * one threads.
+     *
+     * The default value is 1.
+     */
+    unsigned async_cnt;
+
+    /**
+     * The ioqueue concurrency to be forced on the socket when it is 
+     * registered to the ioqueue. See #pj_ioqueue_set_concurrency() for more
+     * info about ioqueue concurrency.
+     *
+     * When this value is -1, the concurrency setting will not be forced for
+     * this socket, and the socket will inherit the concurrency setting of 
+     * the ioqueue. When this value is zero, the secure socket will disable
+     * concurrency for the socket. When this value is +1, the secure socket
+     * will enable concurrency for the socket.
+     *
+     * The default value is -1.
+     */
+    int concurrency;
+
+    /**
+     * If this option is specified, the secure socket will make sure that
+     * asynchronous send operation with stream oriented socket will only
+     * call the callback after all data has been sent. This means that the
+     * secure socket will automatically resend the remaining data until
+     * all data has been sent.
+     *
+     * Please note that when this option is specified, it is possible that
+     * error is reported after partial data has been sent. Also setting
+     * this will disable the ioqueue concurrency for the socket.
+     *
+     * Default value is 1.
+     */
+    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.
+     *  
+     * Default value is 0, except for Symbian 8192 bytes.
+     */
+    pj_size_t send_buffer_size;
+
+    /**
+     * Cipher list string. If empty, then default cipher list of the backend 
+     * will be used.
+     */
+    pj_str_t ciphers;
+
+    /**
+     * Security negotiation timeout. If this is set to zero (both sec and 
+     * msec), the negotiation doesn't have a timeout.
+     *
+     * Default value is zero.
+     */
+    pj_time_val	timeout;
+
+    /**
+     * Specify whether endpoint should verify peer certificate.
+     *
+     * Default value is PJ_FALSE.
+     */
+    pj_bool_t verify_peer;
+    
+    /**
+     * When secure socket is acting as server (handles incoming connection),
+     * it will require the client to provide certificate.
+     *
+     * Default value is PJ_FALSE.
+     */
+    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.
+     *
+     * Default value is zero/not-set.
+     */
+    pj_str_t servername;
+    
+} pj_ssl_sock_param;
+
+
+/**
+ * Initialize the secure socket parameters for its creation with 
+ * the default values.
+ *
+ * @param param		The parameter to be initialized.
+ */
+PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param);
+
+
+/**
+ * Create secure socket instance.
+ *
+ * @param pool		The pool for allocating secure socket instance.
+ * @param param		The secure socket parameter, see #pj_ssl_sock_param.
+ * @param p_ssock	Pointer to secure socket instance to be created.
+ *
+ * @return		PJ_SUCCESS when successful.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool,
+					const pj_ssl_sock_param *param,
+					pj_ssl_sock_t **p_ssock);
+
+
+/**
+ * Set secure socket certificate or credentials. Credentials may include 
+ * certificate, private key and trusted Certification Authorities list. 
+ * Normally, server socket must provide certificate (and private key).
+ * Socket client may also need to provide certificate in case requested
+ * by the server.
+ *
+ * @param ssock		The secure socket instance.
+ * @param pool		The pool.
+ * @param cert		The endpoint certificate/credentials, see
+ *			#pj_ssl_cert_t.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(
+					    pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    const pj_ssl_cert_t *cert);
+
+
+/**
+ * Close and destroy the secure socket.
+ *
+ * @param ssock		The secure socket.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock);
+
+
+/**
+ * Associate arbitrary data with the secure socket. Application may
+ * inspect this data in the callbacks and associate it with higher
+ * level processing.
+ *
+ * @param ssock		The secure socket.
+ * @param user_data	The user data to be associated with the secure
+ *			socket.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
+					       void *user_data);
+
+/**
+ * Retrieve the user data previously associated with this secure
+ * socket.
+ *
+ * @param ssock		The secure socket.
+ *
+ * @return		The user data.
+ */
+PJ_DECL(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock);
+
+
+/**
+ * Retrieve the local address and port used by specified secure socket.
+ *
+ * @param ssock		The secure socket.
+ * @param info		The info buffer to be set, see #pj_ssl_sock_info.
+ *
+ * @return		PJ_SUCCESS on successful.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_get_info(pj_ssl_sock_t *ssock,
+					  pj_ssl_sock_info *info);
+
+
+/**
+ * Starts read operation on this secure socket. This function will create
+ * \a async_cnt number of buffers (the \a async_cnt parameter was given
+ * in \a pj_ssl_sock_create() function) where each buffer is \a buff_size
+ * long. The buffers are allocated from the specified \a pool. Once the 
+ * buffers are created, it then issues \a async_cnt number of asynchronous
+ * \a recv() operations to the socket and returns back to caller. Incoming
+ * data on the socket will be reported back to application via the 
+ * \a on_data_read() callback.
+ *
+ * Application only needs to call this function once to initiate read
+ * operations. Further read operations will be done automatically by the
+ * secure socket when \a on_data_read() callback returns non-zero. 
+ *
+ * @param ssock		The secure socket.
+ * @param pool		Pool used to allocate buffers for incoming data.
+ * @param buff_size	The size of each buffer, in bytes.
+ * @param flags		Flags to be given to pj_ioqueue_recv().
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    unsigned buff_size,
+					    pj_uint32_t flags);
+
+/**
+ * Same as #pj_ssl_sock_start_read(), except that the application
+ * supplies the buffers for the read operation so that the acive socket
+ * does not have to allocate the buffers.
+ *
+ * @param ssock		The secure socket.
+ * @param pool		Pool used to allocate buffers for incoming data.
+ * @param buff_size	The size of each buffer, in bytes.
+ * @param readbuf	Array of packet buffers, each has buff_size size.
+ * @param flags		Flags to be given to pj_ioqueue_recv().
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_read2(pj_ssl_sock_t *ssock,
+					     pj_pool_t *pool,
+					     unsigned buff_size,
+					     void *readbuf[],
+					     pj_uint32_t flags);
+
+/**
+ * Same as pj_ssl_sock_start_read(), except that this function is used
+ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
+ * callback instead.
+ *
+ * @param ssock		The secure socket.
+ * @param pool		Pool used to allocate buffers for incoming data.
+ * @param buff_size	The size of each buffer, in bytes.
+ * @param flags		Flags to be given to pj_ioqueue_recvfrom().
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_recvfrom(pj_ssl_sock_t *ssock,
+						pj_pool_t *pool,
+						unsigned buff_size,
+						pj_uint32_t flags);
+
+/**
+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() 
+ * operation takes the buffer from the argument rather than creating
+ * new ones.
+ *
+ * @param ssock		The secure socket.
+ * @param pool		Pool used to allocate buffers for incoming data.
+ * @param buff_size	The size of each buffer, in bytes.
+ * @param readbuf	Array of packet buffers, each has buff_size size.
+ * @param flags		Flags to be given to pj_ioqueue_recvfrom().
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_recvfrom2(pj_ssl_sock_t *ssock,
+						 pj_pool_t *pool,
+						 unsigned buff_size,
+						 void *readbuf[],
+						 pj_uint32_t flags);
+
+/**
+ * Send data using the socket.
+ *
+ * @param ssock		The secure socket.
+ * @param send_key	The operation key to send the data, which is useful
+ *			if application wants to submit multiple pending
+ *			send operations and want to track which exact data 
+ *			has been sent in the \a on_data_sent() callback.
+ * @param data		The data to be sent. This data must remain valid
+ *			until the data has been sent.
+ * @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_DECL(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock,
+				      pj_ioqueue_op_key_t *send_key,
+				      const void *data,
+				      pj_ssize_t *size,
+				      unsigned flags);
+
+/**
+ * Send datagram using the socket.
+ *
+ * @param ssock		The secure socket.
+ * @param send_key	The operation key to send the data, which is useful
+ *			if application wants to submit multiple pending
+ *			send operations and want to track which exact data 
+ *			has been sent in the \a on_data_sent() callback.
+ * @param data		The data to be sent. This data must remain valid
+ *			until the data has been sent.
+ * @param size		The size of the data.
+ * @param flags		Flags to be given to pj_ioqueue_send().
+ * @param addr		The destination address.
+ * @param addr_len	Length of buffer containing destination address.
+ *
+ * @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_DECL(pj_status_t) pj_ssl_sock_sendto(pj_ssl_sock_t *ssock,
+					pj_ioqueue_op_key_t *send_key,
+					const void *data,
+					pj_ssize_t *size,
+					unsigned flags,
+					const pj_sockaddr_t *addr,
+					int addr_len);
+
+
+/**
+ * Starts asynchronous socket accept() operations on this secure socket. 
+ * This function will issue \a async_cnt number of asynchronous \a accept() 
+ * operations to the socket and returns back to caller. Incoming
+ * connection on the socket will be reported back to application via the
+ * \a on_accept_complete() callback.
+ *
+ * Application only needs to call this function once to initiate accept()
+ * operations. Further accept() operations will be done automatically by 
+ * the secure socket when \a on_accept_complete() callback returns non-zero.
+ *
+ * @param ssock		The secure socket.
+ * @param pool		Pool used to allocate some internal data for the
+ *			operation.
+ * @param localaddr	Local address to bind on.
+ * @param addr_len	Length of buffer containing local address.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_accept(pj_ssl_sock_t *ssock,
+					      pj_pool_t *pool,
+					      const pj_sockaddr_t *local_addr,
+					      int addr_len);
+
+
+/**
+ * Starts asynchronous socket connect() operation and SSL/TLS handshaking 
+ * for this socket. Once the connection is done (either successfully or not),
+ * the \a on_connect_complete() callback will be called.
+ *
+ * @param ssock		The secure socket.
+ * @param pool		The pool to allocate some internal data for the
+ *			operation.
+ * @param localaddr	Local address.
+ * @param remaddr	Remote address.
+ * @param addr_len	Length of buffer containing above addresses.
+ *
+ * @return		PJ_SUCCESS if connection can be established immediately
+ *			or PJ_EPENDING if connection cannot be established 
+ *			immediately. In this case the \a on_connect_complete()
+ *			callback will be called when connection is complete. 
+ *			Any other return value indicates error condition.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock,
+					       pj_pool_t *pool,
+					       const pj_sockaddr_t *localaddr,
+					       const pj_sockaddr_t *remaddr,
+					       int addr_len);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJ_SSL_SOCK_H__ */
Index: pjproject/trunk/pjlib/src/pj/ssl_sock_common.c
===================================================================
--- pjproject/trunk/pjlib/src/pj/ssl_sock_common.c	(revision 2913)
+++ pjproject/trunk/pjlib/src/pj/ssl_sock_common.c	(revision 2913)
@@ -0,0 +1,43 @@
+/* $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/string.h>
+
+/*
+ * Initialize the SSL socket configuration with the default values.
+ */
+PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
+{
+    pj_bzero(param, sizeof(*param));
+
+    /* Socket config */
+    param->sock_af = PJ_AF_INET;
+    param->sock_type = pj_SOCK_STREAM();
+    param->async_cnt = 1;
+    param->concurrency = -1;
+    param->whole_data = PJ_TRUE;
+#if PJ_SYMBIAN
+    param->send_buffer_size = 8192;
+#endif
+
+    /* Security config */
+    param->proto = PJ_SSL_SOCK_PROTO_DEFAULT;
+}
+
+
Index: pjproject/trunk/pjlib/src/pj/ssl_sock_symbian.cpp
===================================================================
--- pjproject/trunk/pjlib/src/pj/ssl_sock_symbian.cpp	(revision 2913)
+++ pjproject/trunk/pjlib/src/pj/ssl_sock_symbian.cpp	(revision 2913)
@@ -0,0 +1,1049 @@
+/* $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/compat/socket.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/pool.h>
+#include <pj/sock.h>
+#include <pj/string.h>
+
+#include "os_symbian.h"
+#include <securesocket.h>
+#include <x509cert.h>
+#include <e32des8.h>
+
+#define THIS_FILE "ssl_sock_symbian.cpp"
+
+typedef void (*CPjSSLSocket_cb)(int err, void *key);
+
+class CPjSSLSocketReader : public CActive
+{
+public:
+    static CPjSSLSocketReader *NewL(CSecureSocket &sock) 
+    {
+	CPjSSLSocketReader *self = new (ELeave) 
+				   CPjSSLSocketReader(sock);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+    }
+
+    ~CPjSSLSocketReader() {
+	Cancel();
+    }
+
+    /* Asynchronous read from the socket. */
+    int Read(CPjSSLSocket_cb cb, void *key, TPtr8 &data, TUint flags)
+    {
+	PJ_ASSERT_RETURN(!IsActive(), PJ_EBUSY);
+	
+	cb_ = cb;
+	key_ = key;
+	sock_.RecvOneOrMore(data, iStatus, len_received_);
+	SetActive();
+	
+	return PJ_EPENDING;
+    }
+
+private:
+    CSecureSocket  	&sock_;
+    CPjSSLSocket_cb	 cb_;
+    void		*key_;
+    TSockXfrLength  	 len_received_; /* not really useful? */
+
+    void DoCancel() {
+	sock_.CancelAll();
+    }
+    
+    void RunL() {
+	(*cb_)(iStatus.Int(), key_);
+    }
+
+    CPjSSLSocketReader(CSecureSocket &sock) : 
+	CActive(0), sock_(sock), cb_(NULL), key_(NULL) 
+    {}
+    
+    void ConstructL() {
+	CActiveScheduler::Add(this);
+    }
+};
+
+class CPjSSLSocket : public CActive
+{
+public:
+    enum ssl_state {
+	SSL_STATE_NULL,
+	SSL_STATE_CONNECTING,
+	SSL_STATE_HANDSHAKING,
+	SSL_STATE_ESTABLISHED
+    };
+    
+    static CPjSSLSocket *NewL(const TDesC8 &ssl_proto) {
+	CPjSSLSocket *self = new (ELeave) CPjSSLSocket();
+	CleanupStack::PushL(self);
+	self->ConstructL(ssl_proto);
+	CleanupStack::Pop(self);
+	return self;
+    }
+
+    ~CPjSSLSocket() {
+	Cancel();
+	CleanupSubObjects();
+    }
+
+    int Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, 
+		const TInetAddr &rem_addr, 
+		const TDesC8 &servername = TPtrC8(NULL,0));
+    int Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags);
+    int SendSync(const TDesC8 &aDesc, TUint flags);
+
+    CPjSSLSocketReader* GetReader();
+    enum ssl_state GetState() const { return state_; }
+    const TInetAddr* GetLocalAddr() const { return &local_addr_; }
+    int GetCipher(TDes8 &cipher) const {
+	if (securesock_)
+	    return securesock_->CurrentCipherSuite(cipher);
+	return KErrNotFound;
+    }
+
+private:
+    enum ssl_state	 state_;
+    pj_sock_t	    	 sock_;
+    CSecureSocket  	*securesock_;
+    bool	    	 is_connected_;
+    CPjSSLSocketReader  *reader_;
+    TBuf<32> 	    	 ssl_proto_;
+    TInetAddr       	 rem_addr_;
+    TPtrC8		 servername_;
+    TInetAddr       	 local_addr_;
+    TSockXfrLength 	 sent_len_;
+
+    CPjSSLSocket_cb 	 cb_;
+    void 	   	*key_;
+    
+    void DoCancel();
+    void RunL();
+
+    CPjSSLSocket() :
+	CActive(0), state_(SSL_STATE_NULL), sock_(PJ_INVALID_SOCKET), 
+	securesock_(NULL), 
+	is_connected_(false), reader_(NULL),
+	cb_(NULL), key_(NULL)
+    {}
+    
+    void ConstructL(const TDesC8 &ssl_proto) {
+	ssl_proto_.Copy(ssl_proto);
+	CActiveScheduler::Add(this);
+    }
+
+    void CleanupSubObjects() {
+	delete reader_;
+	reader_ = NULL;
+	if (securesock_) {
+	    securesock_->Close();
+	    delete securesock_;
+	    securesock_ = NULL;
+	}
+	if (sock_ != PJ_INVALID_SOCKET) {
+	    delete (CPjSocket*)sock_;
+	    sock_ = PJ_INVALID_SOCKET;
+	}	    
+    }
+};
+
+int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, 
+			  const TInetAddr &local_addr, 
+			  const TInetAddr &rem_addr,
+			  const TDesC8 &servername)
+{
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_NULL, PJ_EINVALIDOP);
+    
+    status = pj_sock_socket(rem_addr.Family(), pj_SOCK_STREAM(), 0, &sock_);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    RSocket &rSock = ((CPjSocket*)sock_)->Socket();
+
+    local_addr_ = local_addr;
+    
+    if (!local_addr_.IsUnspecified()) {
+	TInt err = rSock.Bind(local_addr_);
+	if (err != KErrNone)
+	    return PJ_RETURN_OS_ERROR(err);
+    }
+    
+    cb_ = cb;
+    key_ = key;
+    rem_addr_ = rem_addr;
+    servername_.Set(servername);
+    state_ = SSL_STATE_CONNECTING;
+
+    rSock.Connect(rem_addr_, iStatus);
+    SetActive();
+    
+    rSock.LocalName(local_addr_);
+
+    return PJ_EPENDING;
+}
+
+int CPjSSLSocket::Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, 
+		       TUint flags)
+{
+    PJ_UNUSED_ARG(flags);
+
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+    
+    if (IsActive())
+	return PJ_EBUSY;
+    
+    cb_ = cb;
+    key_ = key;
+    
+    securesock_->Send(aDesc, iStatus, sent_len_);
+    SetActive();
+    
+    return PJ_EPENDING;
+}
+
+int CPjSSLSocket::SendSync(const TDesC8 &aDesc, TUint flags)
+{
+    PJ_UNUSED_ARG(flags);
+
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+    
+    TRequestStatus reqStatus;
+    securesock_->Send(aDesc, reqStatus, sent_len_);
+    User::WaitForRequest(reqStatus);
+    
+    return PJ_RETURN_OS_ERROR(reqStatus.Int());
+}
+
+CPjSSLSocketReader* CPjSSLSocket::GetReader()
+{
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, NULL);
+    
+    if (reader_)
+	return reader_;
+    
+    TRAPD(err,	reader_ = CPjSSLSocketReader::NewL(*securesock_));
+    if (err != KErrNone)
+	return NULL;
+    
+    return reader_;
+}
+
+void CPjSSLSocket::DoCancel()
+{
+    /* Operation to be cancelled depends on current state */
+    switch (state_) {
+    case SSL_STATE_CONNECTING:
+	{
+	    RSocket &rSock = ((CPjSocket*)sock_)->Socket();
+	    rSock.CancelConnect();
+	    
+	    CleanupSubObjects();
+
+	    state_ = SSL_STATE_NULL;
+	}
+	break;
+    case SSL_STATE_HANDSHAKING:
+	{
+	    securesock_->CancelHandshake();
+	    securesock_->Close();
+	    
+	    CleanupSubObjects();
+	    
+	    state_ = SSL_STATE_NULL;
+	}
+	break;
+    case SSL_STATE_ESTABLISHED:
+	securesock_->CancelSend();
+	break;
+    default:
+	break;
+    }
+}
+
+void CPjSSLSocket::RunL()
+{
+    switch (state_) {
+    case SSL_STATE_CONNECTING:
+	if (iStatus != KErrNone) {
+	    CleanupSubObjects();
+	    state_ = SSL_STATE_NULL;
+	    /* Dispatch connect failure notification */
+	    if (cb_) (*cb_)(iStatus.Int(), key_);
+	} else {
+	    RSocket &rSock = ((CPjSocket*)sock_)->Socket();
+
+	    /* Get local addr */
+	    rSock.LocalName(local_addr_);
+	    
+	    /* Prepare and start handshake */
+	    securesock_ = CSecureSocket::NewL(rSock, ssl_proto_);
+	    securesock_->SetDialogMode(EDialogModeAttended);
+	    if (servername_.Length() > 0)
+		securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL,
+				    servername_);
+	    securesock_->FlushSessionCache();
+	    securesock_->StartClientHandshake(iStatus);
+	    SetActive();
+	    state_ = SSL_STATE_HANDSHAKING;
+	}
+	break;
+    case SSL_STATE_HANDSHAKING:
+	if (iStatus == KErrNone) {
+	    state_ = SSL_STATE_ESTABLISHED;
+	} else {
+	    state_ = SSL_STATE_NULL;
+	    CleanupSubObjects();
+	}
+	/* Dispatch connect status notification */
+	if (cb_) (*cb_)(iStatus.Int(), key_);
+	break;
+    case SSL_STATE_ESTABLISHED:
+	/* Dispatch data sent notification */
+	if (cb_) (*cb_)(iStatus.Int(), key_);
+	break;
+    default:
+	pj_assert(0);
+	break;
+    }
+}
+
+typedef void (*CPjTimer_cb)(void *user_data);
+
+class CPjTimer : public CActive 
+{
+public:
+    CPjTimer(const pj_time_val *delay, CPjTimer_cb cb, void *user_data) : 
+	CActive(0), cb_(cb), user_data_(user_data)
+    {
+	CActiveScheduler::Add(this);
+
+	rtimer_.CreateLocal();
+	pj_int32_t interval = PJ_TIME_VAL_MSEC(*delay) * 1000;
+	if (interval < 0) {
+	    interval = 0;
+	}
+	rtimer_.After(iStatus, interval);
+	SetActive();
+    }
+    
+    ~CPjTimer() { Cancel(); }
+    
+private:	
+    RTimer		 rtimer_;
+    CPjTimer_cb		 cb_;
+    void		*user_data_;
+    
+    void RunL() { if (cb_) (*cb_)(user_data_); }
+    void DoCancel() { rtimer_.Cancel(); }
+};
+
+/*
+ * Structure of recv/read state.
+ */
+typedef struct read_state_t {
+    TPtr8		*read_buf;
+    TPtr8		*orig_buf;
+    pj_uint32_t		 flags;    
+} read_state_t;
+
+/*
+ * Structure of send/write data.
+ */
+typedef struct write_data_t {
+    pj_size_t 	 	 len;
+    pj_ioqueue_op_key_t	*key;
+    pj_size_t 	 	 data_len;
+    char		 data[1];
+} write_data_t;
+
+/*
+ * Structure of send/write state.
+ */
+typedef struct write_state_t {
+    char		*buf;
+    pj_size_t		 max_len;    
+    char		*start;
+    pj_size_t		 len;
+    write_data_t	*current_data;
+    TPtrC8		 send_ptr;
+} write_state_t;
+
+/*
+ * Secure socket structure definition.
+ */
+struct pj_ssl_sock_t
+{
+    pj_ssl_sock_cb	 cb;
+    void		*user_data;
+    
+    pj_bool_t		 established;
+    write_state_t	 write_state;
+    read_state_t	 read_state;
+    CPjTimer		*connect_timer;
+
+    CPjSSLSocket   	*sock;
+    int			 sock_af;
+    int			 sock_type;
+    pj_sockaddr		 local_addr;
+    pj_sockaddr		 rem_addr;
+
+    pj_ssl_sock_proto	 proto;
+    pj_time_val		 timeout;
+    pj_str_t		 ciphers;
+    pj_str_t		 servername;
+};
+
+
+/*
+ * Create SSL socket instance. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
+					const pj_ssl_sock_param *param,
+					pj_ssl_sock_t **p_ssock)
+{
+    pj_ssl_sock_t *ssock;
+
+    PJ_ASSERT_RETURN(param->async_cnt == 1, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
+
+    /* Allocate secure socket */
+    ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
+    
+    /* Allocate write buffer */
+    ssock->write_state.buf = (char*)pj_pool_alloc(pool, 
+						  param->send_buffer_size);
+    ssock->write_state.max_len = param->send_buffer_size;
+    ssock->write_state.start = ssock->write_state.buf;
+    
+    /* Init secure socket */
+    ssock->sock_af = param->sock_af;
+    ssock->sock_type = param->sock_type;
+    ssock->cb = param->cb;
+    ssock->user_data = param->user_data;
+    pj_strdup_with_null(pool, &ssock->ciphers, &param->ciphers);
+    pj_strdup_with_null(pool, &ssock->servername, &param->servername);
+
+    /* Finally */
+    *p_ssock = ssock;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Set SSL socket credential.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
+					    pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    const pj_ssl_cert_t *cert)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(cert);
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Close the SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
+{
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+    
+    delete ssock->connect_timer;
+    ssock->connect_timer = NULL;
+    
+    delete ssock->sock;
+    ssock->sock = NULL;
+
+    delete ssock->read_state.read_buf;
+    delete ssock->read_state.orig_buf;
+    ssock->read_state.read_buf = NULL;
+    ssock->read_state.orig_buf = NULL;
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Associate arbitrary data with the SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data (pj_ssl_sock_t *ssock,
+					       void *user_data)
+{
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+    
+    ssock->user_data = user_data;
+    
+    return PJ_SUCCESS;
+}
+					       
+
+/*
+ * Retrieve the user data previously associated with this SSL
+ * socket.
+ */
+PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
+{
+    PJ_ASSERT_RETURN(ssock, NULL);
+    
+    return ssock->user_data;
+}
+
+
+/*
+ * Retrieve the local address and port used by specified SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
+					  pj_ssl_sock_info *info)
+{
+    const char *cipher_names[0x1B] = {
+	"TLS_RSA_WITH_NULL_MD5",
+	"TLS_RSA_WITH_NULL_SHA",
+	"TLS_RSA_EXPORT_WITH_RC4_40_MD5",
+	"TLS_RSA_WITH_RC4_128_MD5",
+	"TLS_RSA_WITH_RC4_128_SHA",
+	"TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
+	"TLS_RSA_WITH_IDEA_CBC_SHA",
+	"TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
+	"TLS_RSA_WITH_DES_CBC_SHA",
+	"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
+	"TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
+	"TLS_DH_DSS_WITH_DES_CBC_SHA",
+	"TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
+	"TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
+	"TLS_DH_RSA_WITH_DES_CBC_SHA",
+	"TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
+	"TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+	"TLS_DHE_DSS_WITH_DES_CBC_SHA",
+	"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+	"TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+	"TLS_DHE_RSA_WITH_DES_CBC_SHA",
+	"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+	"TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
+	"TLS_DH_anon_WITH_RC4_128_MD5",
+	"TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+	"TLS_DH_anon_WITH_DES_CBC_SHA",
+	"TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"
+    };
+    
+    PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL);
+    
+    pj_bzero(info, sizeof(*info));
+    
+    info->established = ssock->established;
+    
+    /* Local address */
+    if (ssock->sock) {
+	const TInetAddr* local_addr_ = ssock->sock->GetLocalAddr();
+	int addrlen = sizeof(pj_sockaddr);
+	pj_status_t status;
+	
+	status = PjSymbianOS::Addr2pj(*local_addr_, info->local_addr, &addrlen);
+	if (status != PJ_SUCCESS)
+	    return status;
+    } else {
+	pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
+    }
+
+    /* Remote address */
+    pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr, 
+		   (pj_sockaddr_t*)&ssock->rem_addr);
+
+    /* Cipher suite */
+    if (info->established) {
+	TBuf8<8> cipher;
+	if (ssock->sock->GetCipher(cipher) == KErrNone) {
+	    TLex8 lex(cipher);
+	    TUint cipher_code = cipher[1];    
+	    if (cipher_code>=1 && cipher_code<=0x1B)
+		info->cipher = pj_str((char*)cipher_names[cipher_code-1]); 
+	}
+    }
+
+    /* Protocol */
+    info->proto = ssock->proto;
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Starts read operation on this SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    unsigned buff_size,
+					    pj_uint32_t flags)
+{
+    PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
+
+    /* Reading is already started */
+    if (ssock->read_state.orig_buf) {
+	return PJ_SUCCESS;
+    }
+
+    void *readbuf[1];
+    readbuf[0] = pj_pool_alloc(pool, buff_size);
+    return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags);
+}
+
+static void read_cb(int err, void *key)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
+    pj_status_t status;
+
+    status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
+
+    /* Check connection status */
+    if (err == KErrEof || !PjSymbianOS::Instance()->IsConnectionUp() ||
+	!ssock->established) 
+    {
+	status = PJ_EEOF;
+    }
+    
+    /* Notify data arrival */
+    if (ssock->cb.on_data_read) {
+	pj_size_t remainder = 0;
+	char *data = (char*)ssock->read_state.orig_buf->Ptr();
+	pj_size_t data_len = ssock->read_state.read_buf->Length() + 
+			     ssock->read_state.read_buf->Ptr() -
+			     ssock->read_state.orig_buf->Ptr();
+	
+	if (data_len > 0) {
+	    /* Notify received data */
+	    pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, data, data_len, 
+						      status, &remainder);
+	    if (!ret) {
+		/* We've been destroyed */
+		return;
+	    }
+	    
+	    /* Calculate available data for next READ operation */
+	    if (remainder > 0) {
+		pj_size_t data_maxlen = ssock->read_state.orig_buf->MaxLength();
+		
+		/* There is some data left unconsumed by application, we give
+		 * smaller buffer for next READ operation.
+		 */
+		ssock->read_state.read_buf->Set((TUint8*)data+remainder, 0, 
+					        data_maxlen - remainder);
+	    } else {
+		/* Give all buffer for next READ operation. 
+		 */
+		ssock->read_state.read_buf->Set(*ssock->read_state.orig_buf);
+	    }
+	}
+    }
+
+    if (status == PJ_SUCCESS) {
+	/* Perform the "next" READ operation */
+	CPjSSLSocketReader *reader = ssock->sock->GetReader(); 
+	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);
+	}
+    }
+    
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	/* Connection closed or something goes wrong */
+	delete ssock->read_state.read_buf;
+	delete ssock->read_state.orig_buf;
+	ssock->read_state.read_buf = NULL;
+	ssock->read_state.orig_buf = NULL;
+	ssock->established = PJ_FALSE;
+    }
+}
+
+/*
+ * Same as #pj_ssl_sock_start_read(), except that the application
+ * supplies the buffers for the read operation so that the acive socket
+ * does not have to allocate the buffers.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
+					     pj_pool_t *pool,
+					     unsigned buff_size,
+					     void *readbuf[],
+					     pj_uint32_t flags)
+{
+    PJ_ASSERT_RETURN(ssock && buff_size && readbuf, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
+    
+    /* Return failure if access point is marked as down by app. */
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    /* Reading is already started */
+    if (ssock->read_state.orig_buf) {
+	return PJ_SUCCESS;
+    }
+    
+    PJ_UNUSED_ARG(pool);
+
+    /* Get reader instance */
+    CPjSSLSocketReader *reader = ssock->sock->GetReader();
+    if (!reader)
+	return PJ_ENOMEM;
+    
+    /* We manage two buffer pointers here:
+     * 1. orig_buf keeps the orginal buffer address (and its max length).
+     * 2. read_buf provides buffer for READ operation, mind that there may be
+     *    some remainder data left by application.
+     */
+    ssock->read_state.read_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
+    ssock->read_state.orig_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
+    ssock->read_state.flags = flags;
+    
+    pj_status_t status;
+    status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, 
+			  ssock->read_state.flags);
+    
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	delete ssock->read_state.read_buf;
+	delete ssock->read_state.orig_buf;
+	ssock->read_state.read_buf = NULL;
+	ssock->read_state.orig_buf = NULL;
+	
+	return status;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/*
+ * Same as pj_ssl_sock_start_read(), except that this function is used
+ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
+ * callback instead.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
+						pj_pool_t *pool,
+						unsigned buff_size,
+						pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(flags);
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() 
+ * operation takes the buffer from the argument rather than creating
+ * new ones.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
+						 pj_pool_t *pool,
+						 unsigned buff_size,
+						 void *readbuf[],
+						 pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(readbuf);
+    PJ_UNUSED_ARG(flags);
+    return PJ_ENOTSUP;
+}
+
+static void send_cb(int err, void *key)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
+    write_state_t *st = &ssock->write_state;
+
+    /* Check connection status */
+    if (err != KErrNone || !PjSymbianOS::Instance()->IsConnectionUp() ||
+	!ssock->established) 
+    {
+	ssock->established = PJ_FALSE;
+	return;
+    }
+
+    /* Remove sent data from buffer */
+    st->start += st->current_data->len;
+    st->len -= st->current_data->len;
+
+    /* Reset current outstanding send */
+    st->current_data = NULL;
+
+    /* Let's check if there is pending data to send */
+    if (st->len) {
+	write_data_t *wdata = (write_data_t*)st->start;
+	pj_status_t status;
+	
+	st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
+	st->current_data = wdata;
+	status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, 0);
+	if (status != PJ_EPENDING) {
+	    ssock->established = PJ_FALSE;
+	    st->len = 0;
+	    return;
+	}
+    }
+}
+
+/*
+ * Send data using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
+				      pj_ioqueue_op_key_t *send_key,
+				      const void *data,
+				      pj_ssize_t *size,
+				      unsigned flags)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(ssock && data && size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->write_state.max_len == 0 || 
+		     ssock->write_state.max_len >= (pj_size_t)*size, 
+		     PJ_ETOOSMALL);
+    
+    /* Check connection status */
+    if (!PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established) 
+    {
+	ssock->established = PJ_FALSE;
+	return PJ_ECANCELLED;
+    }
+
+    write_state_t *st = &ssock->write_state;
+    
+    /* Synchronous mode */
+    if (st->max_len == 0) {
+	st->send_ptr.Set((TUint8*)data, (TInt)*size);
+	return ssock->sock->SendSync(st->send_ptr, flags);
+    }
+
+    /* CSecureSocket only allows one outstanding send operation, so
+     * we use buffering mechanism to allow application to perform send 
+     * operations at any time.
+     */
+    
+    pj_size_t avail_len = st->max_len - st->len;
+    pj_size_t needed_len = *size + sizeof(write_data_t) - 1;
+    
+    /* Align needed_len to be multiplication of 4 */
+    needed_len = ((needed_len + 3) >> 2) << 2; 
+
+    /* Block until there is buffer slot available! */
+    while (needed_len >= avail_len) {
+	pj_symbianos_poll(-1, -1);
+	avail_len = st->max_len - st->len;
+    }
+
+    /* Ok, make sure the new data will not get wrapped */
+    if (st->start + st->len + needed_len > st->buf + st->max_len) {
+	/* Align buffer left */
+	pj_memmove(st->buf, st->start, st->len);
+	st->start = st->buf;
+    }
+    
+    /* Push back the send data into the buffer */
+    write_data_t *wdata = (write_data_t*)(st->start + st->len);
+    
+    wdata->len = needed_len;
+    wdata->key = send_key;
+    wdata->data_len = (pj_size_t)*size;
+    pj_memcpy(wdata->data, data, *size);
+    st->len += needed_len;
+
+    /* If no outstanding send, send it */
+    if (st->current_data == NULL) {
+	pj_status_t status;
+	    
+	wdata = (write_data_t*)st->start;
+	st->current_data = wdata;
+	st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
+	status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, flags);
+	
+	if (status != PJ_EPENDING) {
+	    *size = -status;
+	    return status;
+	}
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/*
+ * Send datagram using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
+					pj_ioqueue_op_key_t *send_key,
+					const void *data,
+					pj_ssize_t *size,
+					unsigned flags,
+					const pj_sockaddr_t *addr,
+					int addr_len)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(send_key);
+    PJ_UNUSED_ARG(data);
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(flags);
+    PJ_UNUSED_ARG(addr);
+    PJ_UNUSED_ARG(addr_len);
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Starts asynchronous socket accept() operations on this SSL socket. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+					      pj_pool_t *pool,
+					      const pj_sockaddr_t *local_addr,
+					      int addr_len)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(local_addr);
+    PJ_UNUSED_ARG(addr_len);
+    
+    return PJ_ENOTSUP;
+}
+
+static void connect_cb(int err, void *key)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
+    pj_status_t status;
+    
+    if (ssock->connect_timer) {
+	delete ssock->connect_timer;
+	ssock->connect_timer = NULL;
+    }
+
+    status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
+    if (status == PJ_SUCCESS) {
+	ssock->established = PJ_TRUE;
+    } else {
+	delete ssock->sock;
+	ssock->sock = NULL;
+    }
+    
+    if (ssock->cb.on_connect_complete) {
+	pj_bool_t ret = (*ssock->cb.on_connect_complete)(ssock, status);
+	if (!ret) {
+	    /* We've been destroyed */
+	    return;
+	}
+    }
+}
+
+static void connect_timer_cb(void *key)
+{
+    connect_cb(KErrTimedOut, key);
+}
+
+/*
+ * Starts asynchronous socket connect() operation and SSL/TLS handshaking 
+ * for this socket. Once the connection is done (either successfully or not),
+ * the \a on_connect_complete() callback will be called.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock,
+					       pj_pool_t *pool,
+					       const pj_sockaddr_t *localaddr,
+					       const pj_sockaddr_t *remaddr,
+					       int addr_len)
+{
+    CPjSSLSocket *sock = NULL;
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
+		     PJ_EINVAL);
+
+    /* Check connection status */
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    if (ssock->sock != NULL) {
+	CPjSSLSocket::ssl_state state = ssock->sock->GetState();
+	switch (state) {
+	case CPjSSLSocket::SSL_STATE_ESTABLISHED:
+	    return PJ_SUCCESS;
+	default:
+	    return PJ_EPENDING;
+	}
+    }
+
+    /* Set SSL protocol */
+    TPtrC8 proto;
+    
+    if (ssock->proto == PJ_SSL_SOCK_PROTO_DEFAULT)
+	ssock->proto = PJ_SSL_SOCK_PROTO_TLS1;
+
+    /* CSecureSocket only support TLS1.0 and SSL3.0 */
+    switch(ssock->proto) {
+    case PJ_SSL_SOCK_PROTO_TLS1:
+	proto.Set((const TUint8*)"TLS1.0", 6);
+	break;
+    case PJ_SSL_SOCK_PROTO_SSL3:
+	proto.Set((const TUint8*)"SSL3.0", 6);
+	break;
+    default:
+	return PJ_ENOTSUP;
+    }
+
+    /* Prepare addresses */
+    TInetAddr localaddr_, remaddr_;
+    status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)localaddr, addr_len, 
+				  localaddr_);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+    status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)remaddr, addr_len,
+				  remaddr_);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_sockaddr_cp((pj_sockaddr_t*)&ssock->rem_addr, remaddr);
+
+    /* Init SSL engine */
+    TRAPD(err, sock = CPjSSLSocket::NewL(proto));
+    if (err != KErrNone)
+	return PJ_ENOMEM;
+    
+    if (ssock->timeout.sec != 0 || ssock->timeout.msec != 0) {
+	ssock->connect_timer = new CPjTimer(&ssock->timeout, 
+					    &connect_timer_cb, ssock);
+    }
+    
+    /* Convert server name to Symbian descriptor */
+    TPtrC8 servername_((TUint8*)ssock->servername.ptr, 
+		       ssock->servername.slen);
+    
+    /* Try to connect */
+    status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_,
+			   servername_);
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	delete sock;
+	return status;
+    }
+
+    ssock->sock = sock;
+    return status;
+}
+
Index: pjproject/trunk/pjsip-apps/src/symbian_ua/ua.cpp
===================================================================
--- pjproject/trunk/pjsip-apps/src/symbian_ua/ua.cpp	(revision 2912)
+++ pjproject/trunk/pjsip-apps/src/symbian_ua/ua.cpp	(revision 2913)
@@ -36,5 +36,5 @@
 // Destination URI (to make call, or to subscribe presence)
 //
-#define SIP_DST_URI	"sip:100@pjsip.lab"
+#define SIP_DST_URI	"<sip:100@pjsip.lab>"
 
 //
@@ -53,7 +53,9 @@
 
 //
-// Set to 1 if TCP is desired (experimental)
-//
-#define ENABLE_SIP_TCP	0
+// SIP transports
+//
+#define ENABLE_SIP_UDP	1
+#define ENABLE_SIP_TCP	0 // experimental
+#define ENABLE_SIP_TLS	0 // experimental
 
 //
@@ -374,9 +376,11 @@
         	&codec_id, PJMEDIA_CODEC_PRIO_DISABLED);
     }
-    
-    /* Add UDP transport. */
+
+    
     pjsua_transport_config tcfg;
     pjsua_transport_id tid;
 
+#if ENABLE_SIP_UDP
+    /* Add UDP transport. */
     pjsua_transport_config_default(&tcfg);
     tcfg.port = SIP_PORT;
@@ -387,7 +391,8 @@
 	    return status;
     }
-
+#endif
+    
+#if ENABLE_SIP_TCP
     /* Add TCP transport */
-#if ENABLE_SIP_TCP
     pjsua_transport_config_default(&tcfg);
     tcfg.port = SIP_PORT;
@@ -395,4 +400,16 @@
     if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Error creating TCP transport", status);
+	    pjsua_destroy();
+	    return status;
+    }
+#endif
+    
+#if ENABLE_SIP_TLS
+    /* Add TLS transport */
+    pjsua_transport_config_default(&tcfg);
+    tcfg.port = SIP_PORT + 1;
+    status = pjsua_transport_create(PJSIP_TRANSPORT_TLS, &tcfg, &tid);
+    if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Error creating TLS transport", status);
 	    pjsua_destroy();
 	    return status;
Index: pjproject/trunk/pjsip/src/pjsip/sip_transport_tls.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_transport_tls.c	(revision 2913)
+++ pjproject/trunk/pjsip/src/pjsip/sip_transport_tls.c	(revision 2913)
@@ -0,0 +1,1317 @@
+/* $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 <pjsip/sip_transport_tls.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
+#include <pj/compat/socket.h>
+#include <pj/addr_resolv.h>
+#include <pj/ssl_sock.h>
+#include <pj/assert.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
+
+#define THIS_FILE	"sip_transport_tls.c"
+
+#define MAX_ASYNC_CNT	16
+#define POOL_LIS_INIT	512
+#define POOL_LIS_INC	512
+#define POOL_TP_INIT	512
+#define POOL_TP_INC	512
+
+struct tls_listener;
+struct tls_transport;
+
+/*
+ * Definition of TLS/SSL transport listener, and it's descendant of
+ * pjsip_tpfactory.
+ */
+struct tls_listener
+{
+    pjsip_tpfactory	     factory;
+    pj_bool_t		     is_registered;
+    pjsip_endpoint	    *endpt;
+    pjsip_tpmgr		    *tpmgr;
+    pj_ssl_sock_t	    *ssock;
+    pjsip_tls_setting	     tls_setting;
+};
+
+
+/*
+ * This structure is used to keep delayed transmit operation in a list.
+ * A delayed transmission occurs when application sends tx_data when
+ * the TLS connect/establishment is still in progress. These delayed
+ * transmission will be "flushed" once the socket is connected (either
+ * successfully or with errors).
+ */
+struct delayed_tdata
+{
+    PJ_DECL_LIST_MEMBER(struct delayed_tdata);
+    pjsip_tx_data_op_key    *tdata_op_key;
+};
+
+
+/*
+ * TLS/SSL transport, and it's descendant of pjsip_transport.
+ */
+struct tls_transport
+{
+    pjsip_transport	     base;
+    pj_bool_t		     is_server;
+
+    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;
+
+    /* Keep-alive timer. */
+    pj_timer_entry	     ka_timer;
+    pj_time_val		     last_activity;
+    pjsip_tx_data_op_key     ka_op_key;
+    pj_str_t		     ka_pkt;
+
+    /* TLS transport can only have  one rdata!
+     * Otherwise chunks of incoming PDU may be received on different
+     * buffer.
+     */
+    pjsip_rx_data	     rdata;
+
+    /* Pending transmission list. */
+    struct delayed_tdata     delayed_list;
+};
+
+
+/****************************************************************************
+ * PROTOTYPES
+ */
+
+/* This callback is called when pending accept() operation completes. */
+static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock,
+				    pj_ssl_sock_t *new_ssock,
+				    const pj_sockaddr_t *src_addr,
+				    int src_addr_len);
+
+/* Callback on incoming data */
+static pj_bool_t on_data_read(pj_ssl_sock_t *ssock,
+			      void *data,
+			      pj_size_t size,
+			      pj_status_t status,
+			      pj_size_t *remainder);
+
+/* Callback when packet is sent */
+static pj_bool_t on_data_sent(pj_ssl_sock_t *ssock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent);
+
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory);
+
+/* This callback is called by transport manager to create transport */
+static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
+					pjsip_tpmgr *mgr,
+					pjsip_endpoint *endpt,
+					const pj_sockaddr *rem_addr,
+					int addr_len,
+					pjsip_transport **transport);
+
+/* Common function to create and initialize transport */
+static pj_status_t tls_create(struct tls_listener *listener,
+			      pj_pool_t *pool,
+			      pj_ssl_sock_t *ssock, 
+			      pj_bool_t is_server,
+			      const pj_sockaddr_in *local,
+			      const pj_sockaddr_in *remote,
+			      struct tls_transport **p_tls);
+
+
+static void tls_perror(const char *sender, const char *title,
+		       pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
+}
+
+
+static void sockaddr_to_host_port( pj_pool_t *pool,
+				   pjsip_host_port *host_port,
+				   const pj_sockaddr_in *addr )
+{
+    host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4);
+    pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 2);
+    host_port->host.slen = pj_ansi_strlen(host_port->host.ptr);
+    host_port->port = pj_sockaddr_get_port(addr);
+}
+
+
+
+/****************************************************************************
+ * The TLS listener/transport factory.
+ */
+
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TLS listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
+					       const pjsip_tls_setting *opt,
+					       const pj_sockaddr_in *local,
+					       const pjsip_host_port *a_name,
+					       unsigned async_cnt,
+					       pjsip_tpfactory **p_factory)
+{
+    pj_pool_t *pool;
+    struct tls_listener *listener;
+    pj_ssl_sock_param ssock_param;
+    pj_sockaddr_in *listener_addr;
+    pj_bool_t has_listener;
+    pj_status_t status;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);
+
+    /* Verify that address given in a_name (if any) is valid */
+    if (a_name && a_name->host.slen) {
+	pj_sockaddr_in tmp;
+
+	status = pj_sockaddr_in_init(&tmp, &a_name->host, 
+				     (pj_uint16_t)a_name->port);
+	if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY ||
+	    tmp.sin_addr.s_addr == PJ_INADDR_NONE)
+	{
+	    /* Invalid address */
+	    return PJ_EINVAL;
+	}
+    }
+
+    pool = pjsip_endpt_create_pool(endpt, "tlslis", POOL_LIS_INIT, 
+				   POOL_LIS_INC);
+    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+    listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener);
+    listener->factory.pool = pool;
+    listener->factory.type = PJSIP_TRANSPORT_TLS;
+    listener->factory.type_name = "tls";
+    listener->factory.flag = 
+	pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS);
+
+    pj_ansi_strcpy(listener->factory.obj_name, "tlslis");
+
+    if (opt)
+	pjsip_tls_setting_copy(pool, &listener->tls_setting, opt);
+    else
+	pjsip_tls_setting_default(&listener->tls_setting);
+
+    status = pj_lock_create_recursive_mutex(pool, "tlslis", 
+					    &listener->factory.lock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (async_cnt > MAX_ASYNC_CNT) 
+	async_cnt = MAX_ASYNC_CNT;
+
+    /* Build SSL socket param */
+    pj_ssl_sock_param_default(&ssock_param);
+    ssock_param.cb.on_accept_complete = &on_accept_complete;
+    ssock_param.cb.on_data_read = &on_data_read;
+    ssock_param.cb.on_data_sent = &on_data_sent;
+    ssock_param.async_cnt = async_cnt;
+    ssock_param.ciphers = listener->tls_setting.ciphers;
+    ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt);
+    ssock_param.require_client_cert = listener->tls_setting.require_client_cert;
+    ssock_param.servername = listener->tls_setting.server_name;
+    ssock_param.timeout = listener->tls_setting.timeout;
+    ssock_param.user_data = listener;
+    ssock_param.verify_peer = listener->tls_setting.verify_client;
+
+    switch(listener->tls_setting.method) {
+    case PJSIP_TLSV1_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1;
+	break;
+    case PJSIP_SSLV2_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2;
+	break;
+    case PJSIP_SSLV3_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3;
+	break;
+    case PJSIP_SSLV23_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23;
+	break;
+    default:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT;
+	break;
+    }
+
+    /* Create SSL socket */
+    status = pj_ssl_sock_create(pool, &ssock_param, &listener->ssock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr;
+    if (local) {
+	pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, 
+		       (const pj_sockaddr_t*)local);
+    } else {
+	pj_sockaddr_in_init(listener_addr, NULL, 0);
+    }
+
+    /* Start accepting incoming connections. Note that some TLS/SSL backends
+     * may not support for SSL socket server.
+     */
+    has_listener = PJ_FALSE;
+    status = pj_ssl_sock_start_accept(listener->ssock, pool, 
+			  (pj_sockaddr_t*)listener_addr, 
+			  pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr));
+    if (status == PJ_SUCCESS || status == PJ_EPENDING) {
+	pj_ssl_sock_info info;
+	has_listener = PJ_TRUE;
+
+	/* Retrieve the bound address */
+	status = pj_ssl_sock_get_info(listener->ssock, &info);
+	if (status == PJ_SUCCESS)
+	    pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr);
+    } else if (status != PJ_ENOTSUP) {
+	goto on_error;
+    }
+
+    /* If published host/IP is specified, then use that address as the
+     * listener advertised address.
+     */
+    if (a_name && a_name->host.slen) {
+	/* Copy the address */
+	listener->factory.addr_name = *a_name;
+	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, 
+		  &a_name->host);
+	listener->factory.addr_name.port = a_name->port;
+
+    } else {
+	/* No published address is given, use the bound address */
+
+	/* If the address returns 0.0.0.0, use the default
+	 * interface address as the transport's address.
+	 */
+	if (listener_addr->sin_addr.s_addr == 0) {
+	    pj_sockaddr hostip;
+
+	    status = pj_gethostip(pj_AF_INET(), &hostip);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    listener_addr->sin_addr.s_addr = hostip.ipv4.sin_addr.s_addr;
+	}
+
+	/* Save the address name */
+	sockaddr_to_host_port(listener->factory.pool, 
+			      &listener->factory.addr_name, listener_addr);
+    }
+
+    /* If port is zero, get the bound port */
+    if (listener->factory.addr_name.port == 0) {
+	listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port);
+    }
+
+    pj_ansi_snprintf(listener->factory.obj_name, 
+		     sizeof(listener->factory.obj_name),
+		     "tlslis:%d",  listener->factory.addr_name.port);
+
+    /* Register to transport manager */
+    listener->endpt = endpt;
+    listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
+    listener->factory.create_transport = lis_create_transport;
+    listener->factory.destroy = lis_destroy;
+    listener->is_registered = PJ_TRUE;
+    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
+					    &listener->factory);
+    if (status != PJ_SUCCESS) {
+	listener->is_registered = PJ_FALSE;
+	goto on_error;
+    }
+
+    PJ_LOG(4,(listener->factory.obj_name, 
+	     "SIP TLS listener is ready%s at %.*s:%d",
+	     (has_listener?" for incoming connections":""),
+	     (int)listener->factory.addr_name.host.slen,
+	     listener->factory.addr_name.host.ptr,
+	     listener->factory.addr_name.port));
+
+    /* Return the pointer to user */
+    if (p_factory) *p_factory = &listener->factory;
+
+    return PJ_SUCCESS;
+
+on_error:
+    lis_destroy(&listener->factory);
+    return status;
+}
+
+
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory)
+{
+    struct tls_listener *listener = (struct tls_listener *)factory;
+
+    if (listener->is_registered) {
+	pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory);
+	listener->is_registered = PJ_FALSE;
+    }
+
+    if (listener->ssock) {
+	pj_ssl_sock_close(listener->ssock);
+	listener->ssock = NULL;
+    }
+
+    if (listener->factory.lock) {
+	pj_lock_destroy(listener->factory.lock);
+	listener->factory.lock = NULL;
+    }
+
+    if (listener->factory.pool) {
+	pj_pool_t *pool = listener->factory.pool;
+
+	PJ_LOG(4,(listener->factory.obj_name,  "SIP TLS listener destroyed"));
+
+	listener->factory.pool = NULL;
+	pj_pool_release(pool);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/***************************************************************************/
+/*
+ * TLS Transport
+ */
+
+/*
+ * Prototypes.
+ */
+/* Called by transport manager to send message */
+static pj_status_t tls_send_msg(pjsip_transport *transport, 
+				pjsip_tx_data *tdata,
+				const pj_sockaddr_t *rem_addr,
+				int addr_len,
+				void *token,
+				pjsip_transport_callback callback);
+
+/* Called by transport manager to shutdown */
+static pj_status_t tls_shutdown(pjsip_transport *transport);
+
+/* Called by transport manager to destroy transport */
+static pj_status_t tls_destroy_transport(pjsip_transport *transport);
+
+/* Utility to destroy transport */
+static pj_status_t tls_destroy(pjsip_transport *transport,
+			       pj_status_t reason);
+
+/* Callback when connect completes */
+static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
+				     pj_status_t status);
+
+/* TLS keep-alive timer callback */
+static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e);
+
+/*
+ * Common function to create TLS transport, called when pending accept() and
+ * pending connect() complete.
+ */
+static pj_status_t tls_create( struct tls_listener *listener,
+			       pj_pool_t *pool,
+			       pj_ssl_sock_t *ssock,
+			       pj_bool_t is_server,
+			       const pj_sockaddr_in *local,
+			       const pj_sockaddr_in *remote,
+			       struct tls_transport **p_tls)
+{
+    struct tls_transport *tls;
+    const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA;
+    pj_status_t status;
+    
+
+    PJ_ASSERT_RETURN(listener && ssock && local && remote && p_tls, PJ_EINVAL);
+
+
+    if (pool == NULL) {
+	pool = pjsip_endpt_create_pool(listener->endpt, "tls",
+				       POOL_TP_INIT, POOL_TP_INC);
+	PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+    }    
+
+    /*
+     * Create and initialize basic transport structure.
+     */
+    tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport);
+    tls->is_server = is_server;
+    pj_list_init(&tls->delayed_list);
+    tls->base.pool = pool;
+
+    pj_ansi_snprintf(tls->base.obj_name, PJ_MAX_OBJ_NAME, 
+		     (is_server ? "tlss%p" :"tlsc%p"), tls);
+
+    status = pj_atomic_create(pool, 0, &tls->base.ref_cnt);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
+    status = pj_lock_create_recursive_mutex(pool, "tls", &tls->base.lock);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
+    tls->base.key.type = PJSIP_TRANSPORT_TLS;
+    pj_memcpy(&tls->base.key.rem_addr, remote, sizeof(pj_sockaddr_in));
+    tls->base.type_name = "tls";
+    tls->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS);
+
+    tls->base.info = (char*) pj_pool_alloc(pool, 64);
+    pj_ansi_snprintf(tls->base.info, 64, "TLS to %s:%d",
+		     pj_inet_ntoa(remote->sin_addr), 
+		     (int)pj_ntohs(remote->sin_port));
+
+    tls->base.addr_len = sizeof(pj_sockaddr_in);
+    pj_memcpy(&tls->base.local_addr, local, sizeof(pj_sockaddr_in));
+    sockaddr_to_host_port(pool, &tls->base.local_name, local);
+    sockaddr_to_host_port(pool, &tls->base.remote_name, remote);
+
+    tls->base.endpt = listener->endpt;
+    tls->base.tpmgr = listener->tpmgr;
+    tls->base.send_msg = &tls_send_msg;
+    tls->base.do_shutdown = &tls_shutdown;
+    tls->base.destroy = &tls_destroy_transport;
+
+    tls->ssock = ssock;
+
+    /* Register transport to transport manager */
+    status = pjsip_transport_register(listener->tpmgr, &tls->base);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
+    tls->is_registered = PJ_TRUE;
+
+    /* Initialize keep-alive timer */
+    tls->ka_timer.user_data = (void*)tls;
+    tls->ka_timer.cb = &tls_keep_alive_timer;
+    pj_ioqueue_op_key_init(&tls->ka_op_key.key, sizeof(pj_ioqueue_op_key_t));
+    pj_strdup(tls->base.pool, &tls->ka_pkt, &ka_pkt);
+
+    /* Done setting up basic transport. */
+    *p_tls = tls;
+
+    PJ_LOG(4,(tls->base.obj_name, "TLS %s transport created",
+	      (tls->is_server ? "server" : "client")));
+
+    return PJ_SUCCESS;
+
+on_error:
+    tls_destroy(&tls->base, status);
+    return status;
+}
+
+
+/* Flush all delayed transmision once the socket is connected. */
+static void tls_flush_pending_tx(struct tls_transport *tls)
+{
+    pj_lock_acquire(tls->base.lock);
+    while (!pj_list_empty(&tls->delayed_list)) {
+	struct delayed_tdata *pending_tx;
+	pjsip_tx_data *tdata;
+	pj_ioqueue_op_key_t *op_key;
+	pj_ssize_t size;
+	pj_status_t status;
+
+	pending_tx = tls->delayed_list.next;
+	pj_list_erase(pending_tx);
+
+	tdata = pending_tx->tdata_op_key->tdata;
+	op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+
+	/* send! */
+	size = tdata->buf.cur - tdata->buf.start;
+	status = pj_ssl_sock_send(tls->ssock, op_key, tdata->buf.start, 
+				  &size, 0);
+
+	if (status != PJ_EPENDING) {
+	    on_data_sent(tls->ssock, op_key, size);
+	}
+    }
+    pj_lock_release(tls->base.lock);
+}
+
+
+/* Called by transport manager to destroy transport */
+static pj_status_t tls_destroy_transport(pjsip_transport *transport)
+{
+    struct tls_transport *tls = (struct tls_transport*)transport;
+
+    /* Transport would have been unregistered by now since this callback
+     * is called by transport manager.
+     */
+    tls->is_registered = PJ_FALSE;
+
+    return tls_destroy(transport, tls->close_reason);
+}
+
+
+/* Destroy TLS transport */
+static pj_status_t tls_destroy(pjsip_transport *transport, 
+			       pj_status_t reason)
+{
+    struct tls_transport *tls = (struct tls_transport*)transport;
+
+    if (tls->close_reason == 0)
+	tls->close_reason = reason;
+
+    if (tls->is_registered) {
+	tls->is_registered = PJ_FALSE;
+	pjsip_transport_destroy(transport);
+
+	/* pjsip_transport_destroy will recursively call this function
+	 * again.
+	 */
+	return PJ_SUCCESS;
+    }
+
+    /* Mark transport as closing */
+    tls->is_closing = PJ_TRUE;
+
+    /* Stop keep-alive timer. */
+    if (tls->ka_timer.id) {
+	pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer);
+	tls->ka_timer.id = PJ_FALSE;
+    }
+
+    /* Cancel all delayed transmits */
+    while (!pj_list_empty(&tls->delayed_list)) {
+	struct delayed_tdata *pending_tx;
+	pj_ioqueue_op_key_t *op_key;
+
+	pending_tx = tls->delayed_list.next;
+	pj_list_erase(pending_tx);
+
+	op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+
+	on_data_sent(tls->ssock, op_key, -reason);
+    }
+
+    if (tls->rdata.tp_info.pool) {
+	pj_pool_release(tls->rdata.tp_info.pool);
+	tls->rdata.tp_info.pool = NULL;
+    }
+
+    if (tls->ssock) {
+	pj_ssl_sock_close(tls->ssock);
+	tls->ssock = NULL;
+    }
+    if (tls->base.lock) {
+	pj_lock_destroy(tls->base.lock);
+	tls->base.lock = NULL;
+    }
+
+    if (tls->base.ref_cnt) {
+	pj_atomic_destroy(tls->base.ref_cnt);
+	tls->base.ref_cnt = NULL;
+    }
+
+    if (tls->base.pool) {
+	pj_pool_t *pool;
+
+	if (reason != PJ_SUCCESS) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+
+	    pj_strerror(reason, errmsg, sizeof(errmsg));
+	    PJ_LOG(4,(tls->base.obj_name, 
+		      "TLS transport destroyed with reason %d: %s", 
+		      reason, errmsg));
+
+	} else {
+
+	    PJ_LOG(4,(tls->base.obj_name, 
+		      "TLS transport destroyed normally"));
+
+	}
+
+	pool = tls->base.pool;
+	tls->base.pool = NULL;
+	pj_pool_release(pool);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * This utility function creates receive data buffers and start
+ * asynchronous recv() operations from the socket. It is called after
+ * accept() or connect() operation complete.
+ */
+static pj_status_t tls_start_read(struct tls_transport *tls)
+{
+    pj_pool_t *pool;
+    pj_ssize_t size;
+    pj_sockaddr_in *rem_addr;
+    void *readbuf[1];
+    pj_status_t status;
+
+    /* Init rdata */
+    pool = pjsip_endpt_create_pool(tls->base.endpt,
+				   "rtd%p",
+				   PJSIP_POOL_RDATA_LEN,
+				   PJSIP_POOL_RDATA_INC);
+    if (!pool) {
+	tls_perror(tls->base.obj_name, "Unable to create pool", PJ_ENOMEM);
+	return PJ_ENOMEM;
+    }
+
+    tls->rdata.tp_info.pool = pool;
+
+    tls->rdata.tp_info.transport = &tls->base;
+    tls->rdata.tp_info.tp_data = tls;
+    tls->rdata.tp_info.op_key.rdata = &tls->rdata;
+    pj_ioqueue_op_key_init(&tls->rdata.tp_info.op_key.op_key, 
+			   sizeof(pj_ioqueue_op_key_t));
+
+    tls->rdata.pkt_info.src_addr = tls->base.key.rem_addr;
+    tls->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in);
+    rem_addr = (pj_sockaddr_in*) &tls->base.key.rem_addr;
+    pj_ansi_strcpy(tls->rdata.pkt_info.src_name,
+		   pj_inet_ntoa(rem_addr->sin_addr));
+    tls->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port);
+
+    size = sizeof(tls->rdata.pkt_info.packet);
+    readbuf[0] = tls->rdata.pkt_info.packet;
+    status = pj_ssl_sock_start_read2(tls->ssock, tls->base.pool, size,
+				     readbuf, 0);
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	PJ_LOG(4, (tls->base.obj_name, 
+		   "pj_ssl_sock_start_read() error, status=%d", 
+		   status));
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* This callback is called by transport manager for the TLS factory
+ * to create outgoing transport to the specified destination.
+ */
+static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
+					pjsip_tpmgr *mgr,
+					pjsip_endpoint *endpt,
+					const pj_sockaddr *rem_addr,
+					int addr_len,
+					pjsip_transport **p_transport)
+{
+    struct tls_listener *listener;
+    struct tls_transport *tls;
+    pj_pool_t *pool;
+    pj_ssl_sock_t *ssock;
+    pj_ssl_sock_param ssock_param;
+    pj_sockaddr_in local_addr;
+    pj_status_t status;
+
+    /* Sanity checks */
+    PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr &&
+		     addr_len && p_transport, PJ_EINVAL);
+
+    /* Check that address is a sockaddr_in */
+    PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() &&
+		     addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+
+    listener = (struct tls_listener*)factory;
+
+    pool = pjsip_endpt_create_pool(listener->endpt, "tls",
+				   POOL_TP_INIT, POOL_TP_INC);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    /* Build SSL socket param */
+    pj_ssl_sock_param_default(&ssock_param);
+    ssock_param.cb.on_connect_complete = &on_connect_complete;
+    ssock_param.cb.on_data_read = &on_data_read;
+    ssock_param.cb.on_data_sent = &on_data_sent;
+    ssock_param.async_cnt = 1;
+    ssock_param.ciphers = listener->tls_setting.ciphers;
+    ssock_param.ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
+    PJ_TODO(SET_PROPER_SERVERNAME_BASED_ON_TARGET);
+    ssock_param.servername = listener->tls_setting.server_name;
+    ssock_param.timeout = listener->tls_setting.timeout;
+    ssock_param.user_data = NULL; /* pending, must be set later */
+    ssock_param.verify_peer = listener->tls_setting.verify_server;
+    
+    switch(listener->tls_setting.method) {
+    case PJSIP_TLSV1_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1;
+	break;
+    case PJSIP_SSLV2_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2;
+	break;
+    case PJSIP_SSLV3_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3;
+	break;
+    case PJSIP_SSLV23_METHOD:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23;
+	break;
+    default:
+	ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT;
+	break;
+    }
+
+    status = pj_ssl_sock_create(pool, &ssock_param, &ssock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Initially set bind address to PJ_INADDR_ANY port 0 */
+    pj_sockaddr_in_init(&local_addr, NULL, 0);
+
+    /* Create the transport descriptor */
+    status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr, 
+			(pj_sockaddr_in*)rem_addr, &tls);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Set the "pending" SSL socket user data */
+    pj_ssl_sock_set_user_data(tls->ssock, tls);
+
+    /* Start asynchronous connect() operation */
+    tls->has_pending_connect = PJ_TRUE;
+    status = pj_ssl_sock_start_connect(tls->ssock, tls->base.pool, 
+				       (pj_sockaddr_t*)&local_addr,
+				       (pj_sockaddr_t*)rem_addr,
+				       addr_len);
+    if (status == PJ_SUCCESS) {
+	on_connect_complete(tls->ssock, PJ_SUCCESS);
+    } else if (status != PJ_EPENDING) {
+	tls_destroy(&tls->base, status);
+	return status;
+    }
+
+    if (tls->has_pending_connect) {
+	pj_ssl_sock_info info;
+
+	/* Update (again) local address, just in case local address currently
+	 * set is different now that asynchronous connect() is started.
+	 */
+
+	/* Retrieve the bound address */
+	status = pj_ssl_sock_get_info(tls->ssock, &info);
+	if (status == PJ_SUCCESS) {
+	    pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tls->base.local_addr;
+
+	    pj_assert(pj_sockaddr_get_len((pj_sockaddr_t*)&info.local_addr) <= 
+		      sizeof(local_addr));
+	    pj_sockaddr_cp((pj_sockaddr_t*)&local_addr, (pj_sockaddr_t*)&info.local_addr);
+
+	    /* Some systems (like old Win32 perhaps) may not set local address
+	     * properly before socket is fully connected.
+	     */
+	    if (tp_addr->sin_addr.s_addr != local_addr.sin_addr.s_addr &&
+		local_addr.sin_addr.s_addr != 0) 
+	    {
+		tp_addr->sin_addr.s_addr = local_addr.sin_addr.s_addr;
+		tp_addr->sin_port = local_addr.sin_port;
+		sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
+				      &local_addr);
+	    }
+	}
+	
+	PJ_LOG(4,(tls->base.obj_name, 
+		  "TLS transport %.*s:%d is connecting to %.*s:%d...",
+		  (int)tls->base.local_name.host.slen,
+		  tls->base.local_name.host.ptr,
+		  tls->base.local_name.port,
+		  (int)tls->base.remote_name.host.slen,
+		  tls->base.remote_name.host.ptr,
+		  tls->base.remote_name.port));
+    }
+
+    /* Done */
+    *p_transport = &tls->base;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * This callback is called by SSL socket when pending accept() operation
+ * has completed.
+ */
+static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock,
+				    pj_ssl_sock_t *new_ssock,
+				    const pj_sockaddr_t *src_addr,
+				    int src_addr_len)
+{
+    struct tls_listener *listener;
+    struct tls_transport *tls;
+    char addr[PJ_INET6_ADDRSTRLEN+10];
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(src_addr_len);
+
+    listener = (struct tls_listener*) pj_ssl_sock_get_user_data(ssock);
+
+    PJ_ASSERT_RETURN(new_ssock, PJ_TRUE);
+
+    PJ_LOG(4,(listener->factory.obj_name, 
+	      "TLS listener %.*s:%d: got incoming TLS connection "
+	      "from %s, sock=%d",
+	      (int)listener->factory.addr_name.host.slen,
+	      listener->factory.addr_name.host.ptr,
+	      listener->factory.addr_name.port,
+	      pj_sockaddr_print(src_addr, addr, sizeof(addr), 3),
+	      new_ssock));
+
+    /* 
+     * 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);
+    if (status == PJ_SUCCESS) {
+	status = tls_start_read(tls);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(3,(tls->base.obj_name, "New transport cancelled"));
+	    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);
+	    }
+	}
+    }
+
+    return PJ_TRUE;
+}
+
+
+/* 
+ * Callback from ioqueue when packet is sent.
+ */
+static pj_bool_t on_data_sent(pj_ssl_sock_t *ssock,
+			      pj_ioqueue_op_key_t *op_key,
+			      pj_ssize_t bytes_sent)
+{
+    struct tls_transport *tls = (struct tls_transport*) 
+    				pj_ssl_sock_get_user_data(ssock);
+    pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
+
+    /* Note that op_key may be the op_key from keep-alive, thus
+     * it will not have tdata etc.
+     */
+
+    tdata_op_key->tdata = NULL;
+
+    if (tdata_op_key->callback) {
+	/*
+	 * Notify sip_transport.c that packet has been sent.
+	 */
+	if (bytes_sent == 0)
+	    bytes_sent = -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
+
+	tdata_op_key->callback(&tls->base, tdata_op_key->token, bytes_sent);
+
+	/* Mark last activity time */
+	pj_gettimeofday(&tls->last_activity);
+
+    }
+
+    /* Check for error/closure */
+    if (bytes_sent <= 0) {
+	pj_status_t status;
+
+	PJ_LOG(5,(tls->base.obj_name, "TLS send() error, sent=%d", 
+		  bytes_sent));
+
+	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);
+
+	return PJ_FALSE;
+    }
+    
+    return PJ_TRUE;
+}
+
+
+/* 
+ * This callback is called by transport manager to send SIP message 
+ */
+static pj_status_t tls_send_msg(pjsip_transport *transport, 
+				pjsip_tx_data *tdata,
+				const pj_sockaddr_t *rem_addr,
+				int addr_len,
+				void *token,
+				pjsip_transport_callback callback)
+{
+    struct tls_transport *tls = (struct tls_transport*)transport;
+    pj_ssize_t size;
+    pj_bool_t delayed = PJ_FALSE;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
+
+    /* Check that there's no pending operation associated with the tdata */
+    PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
+    
+    /* Check the address is supported */
+    PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+
+
+    /* Init op key. */
+    tdata->op_key.tdata = tdata;
+    tdata->op_key.token = token;
+    tdata->op_key.callback = callback;
+
+    /* If asynchronous connect() has not completed yet, just put the
+     * transmit data in the pending transmission list since we can not
+     * use the socket yet.
+     */
+    if (tls->has_pending_connect) {
+
+	/*
+	 * Looks like connect() is still in progress. Check again (this time
+	 * with holding the lock) to be sure.
+	 */
+	pj_lock_acquire(tls->base.lock);
+
+	if (tls->has_pending_connect) {
+	    struct delayed_tdata *delayed_tdata;
+
+	    /*
+	     * connect() is still in progress. Put the transmit data to
+	     * the delayed list.
+	     */
+	    delayed_tdata = PJ_POOL_ALLOC_T(tdata->pool, 
+					    struct delayed_tdata);
+	    delayed_tdata->tdata_op_key = &tdata->op_key;
+
+	    pj_list_push_back(&tls->delayed_list, delayed_tdata);
+	    status = PJ_EPENDING;
+
+	    /* Prevent pj_ioqueue_send() to be called below */
+	    delayed = PJ_TRUE;
+	}
+
+	pj_lock_release(tls->base.lock);
+    } 
+    
+    if (!delayed) {
+	/*
+	 * Transport is ready to go. Send the packet to ioqueue to be
+	 * sent asynchronously.
+	 */
+	size = tdata->buf.cur - tdata->buf.start;
+	status = pj_ssl_sock_send(tls->ssock, 
+				    (pj_ioqueue_op_key_t*)&tdata->op_key,
+				    tdata->buf.start, &size, 0);
+
+	if (status != PJ_EPENDING) {
+	    /* Not pending (could be immediate success or error) */
+	    tdata->op_key.tdata = NULL;
+
+	    /* Shutdown transport on closure/errors */
+	    if (size <= 0) {
+
+		PJ_LOG(5,(tls->base.obj_name, "TLS send() error, sent=%d", 
+			  size));
+
+		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);
+	    }
+	}
+    }
+
+    return status;
+}
+
+
+/* 
+ * This callback is called by transport manager to shutdown transport.
+ */
+static pj_status_t tls_shutdown(pjsip_transport *transport)
+{
+    struct tls_transport *tls = (struct tls_transport*)transport;
+    
+    /* Stop keep-alive timer. */
+    if (tls->ka_timer.id) {
+	pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer);
+	tls->ka_timer.id = PJ_FALSE;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* 
+ * Callback from ioqueue that an incoming data is received from the socket.
+ */
+static pj_bool_t on_data_read(pj_ssl_sock_t *ssock,
+			      void *data,
+			      pj_size_t size,
+			      pj_status_t status,
+			      pj_size_t *remainder)
+{
+    enum { MAX_IMMEDIATE_PACKET = 10 };
+    struct tls_transport *tls;
+    pjsip_rx_data *rdata;
+
+    PJ_UNUSED_ARG(data);
+
+    tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock);
+    rdata = &tls->rdata;
+
+    /* Don't do anything if transport is closing. */
+    if (tls->is_closing) {
+	tls->is_closing++;
+	return PJ_FALSE;
+    }
+
+    /* Houston, we have packet! Report the packet to transport manager
+     * to be parsed.
+     */
+    if (status == PJ_SUCCESS) {
+	pj_size_t size_eaten;
+
+	/* Mark this as an activity */
+	pj_gettimeofday(&tls->last_activity);
+
+	pj_assert((void*)rdata->pkt_info.packet == data);
+
+	/* Init pkt_info part. */
+	rdata->pkt_info.len = size;
+	rdata->pkt_info.zero = 0;
+	pj_gettimeofday(&rdata->pkt_info.timestamp);
+
+	/* Report to transport manager.
+	 * The transport manager will tell us how many bytes of the packet
+	 * have been processed (as valid SIP message).
+	 */
+	size_eaten = 
+	    pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, 
+				       rdata);
+
+	pj_assert(size_eaten <= (pj_size_t)rdata->pkt_info.len);
+
+	/* Move unprocessed data to the front of the buffer */
+	*remainder = size - size_eaten;
+	if (*remainder > 0 && *remainder != size) {
+	    pj_memmove(rdata->pkt_info.packet,
+		       rdata->pkt_info.packet + size_eaten,
+		       *remainder);
+	}
+
+    } else {
+
+	/* 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);
+
+	return PJ_FALSE;
+
+    }
+
+    /* Reset pool. */
+    pj_pool_reset(rdata->tp_info.pool);
+
+    return PJ_TRUE;
+}
+
+
+/* 
+ * Callback from ioqueue when asynchronous connect() operation completes.
+ */
+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;
+    
+    tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock);
+
+    /* Mark that pending connect() operation has completed. */
+    tls->has_pending_connect = PJ_FALSE;
+
+    /* Check connect() status */
+    if (status != PJ_SUCCESS) {
+
+	tls_perror(tls->base.obj_name, "TLS connect() error", status);
+
+	/* Cancel all delayed transmits */
+	while (!pj_list_empty(&tls->delayed_list)) {
+	    struct delayed_tdata *pending_tx;
+	    pj_ioqueue_op_key_t *op_key;
+
+	    pending_tx = tls->delayed_list.next;
+	    pj_list_erase(pending_tx);
+
+	    op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+
+	    on_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;
+    }
+
+    PJ_LOG(4,(tls->base.obj_name, 
+	      "TLS transport %.*s:%d is connected to %.*s:%d",
+	      (int)tls->base.local_name.host.slen,
+	      tls->base.local_name.host.ptr,
+	      tls->base.local_name.port,
+	      (int)tls->base.remote_name.host.slen,
+	      tls->base.remote_name.host.ptr,
+	      tls->base.remote_name.port));
+
+
+    /* Update (again) local address, just in case local address currently
+     * set is different now that the socket is connected (could happen
+     * on some systems, like old Win32 probably?).
+     */
+
+    /* 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);
+	}
+    }
+
+    /* 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;
+    }
+
+    /* Flush all pending send operations */
+    tls_flush_pending_tx(tls);
+
+    /* Start keep-alive timer */
+    if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+	pj_time_val delay = { PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0 };
+	pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, 
+				   &delay);
+	tls->ka_timer.id = PJ_TRUE;
+	pj_gettimeofday(&tls->last_activity);
+    }
+
+    return PJ_TRUE;
+}
+
+/* Transport keep-alive timer callback */
+static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
+{
+    struct tls_transport *tls = (struct tls_transport*) e->user_data;
+    pj_time_val delay;
+    pj_time_val now;
+    pj_ssize_t size;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(th);
+
+    tls->ka_timer.id = PJ_TRUE;
+
+    pj_gettimeofday(&now);
+    PJ_TIME_VAL_SUB(now, tls->last_activity);
+
+    if (now.sec > 0 && now.sec < PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+	/* There has been activity, so don't send keep-alive */
+	delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL - now.sec;
+	delay.msec = 0;
+
+	pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, 
+				   &delay);
+	tls->ka_timer.id = PJ_TRUE;
+	return;
+    }
+
+    PJ_LOG(5,(tls->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d", 
+	      (int)tls->ka_pkt.slen, (int)tls->base.remote_name.host.slen,
+	      tls->base.remote_name.host.ptr,
+	      tls->base.remote_name.port));
+
+    /* Send the data */
+    size = tls->ka_pkt.slen;
+    status = pj_ssl_sock_send(tls->ssock, &tls->ka_op_key.key,
+			      tls->ka_pkt.ptr, &size, 0);
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	tls_perror(tls->base.obj_name, 
+		   "Error sending keep-alive packet", status);
+	pjsip_transport_shutdown(&tls->base);
+	return;
+    }
+
+    /* Register next keep-alive */
+    delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL;
+    delay.msec = 0;
+
+    pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, 
+			       &delay);
+    tls->ka_timer.id = PJ_TRUE;
+}
+
+#endif /* PJSIP_HAS_TLS_TRANSPORT */
