Index: /pjproject/trunk/pjsip/include/pjsip/sip_config.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_config.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_config.h	(revision 54)
@@ -63,4 +63,13 @@
 #define PJSIP_POOL_LEN_DIALOG		1200
 #define PJSIP_POOL_INC_DIALOG		512
+
+/* Transport manager hash table size (must be 2^n-1). */
+#define PJSIP_TPMGR_HTABLE_SIZE		31
+
+/* Transport idle timeout before it's destroyed. */
+#define PJSIP_TRANSPORT_IDLE_TIME	30
+
+/* Max entries to process in timer heap per poll. */
+#define PJSIP_MAX_TIMED_OUT_ENTRIES	10
 
 /* Module related constants. */
Index: /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h	(revision 54)
@@ -260,49 +260,8 @@
  * @see pjsip_transport_get
  */
-PJ_DECL(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
-					 pj_pool_t *pool,
-					 pjsip_transport_type_e type,
-					 const pj_sockaddr_in *remote,
-					 void *token,
-					 pjsip_transport_completion_callback *cb);
-
-/**
- * Create listener a new transport listener. A listener is transport object
- * that is capable of receiving SIP messages. For UDP listener, normally
- * application should use #pjsip_endpt_create_udp_listener instead if the 
- * application has already created the socket.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt	    The endpoint instance.
- * @param type	    Transport type (eg. UDP, TCP, etc.)
- * @param addr	    The bound address of the transport.
- * @param addr_name The address to be advertised in SIP messages. For example,
- *		    the bound address can be 0.0.0.0, but the advertised address
- *		    normally will be the IP address of the host.
- *
- * @return	    Zero if listener is created successfully.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
+PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
 						  pjsip_transport_type_e type,
-						  pj_sockaddr_in *addr,
-						  const pj_sockaddr_in *addr_name);
-
-/**
- * Create UDP listener. For UDP, normally the application would create the
- * socket by itself (for STUN purpose), then it can register the socket as
- * listener by calling this function.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt	    The endpoint instance.
- * @param sock	    The socket handle.
- * @param addr_name The address to be advertised in SIP message. If the socket
- *		    has been resolved with STUN, then application may specify 
- *		    the mapped address in this parameter.
- *
- * @return	    Zero if listener is created successfully.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
-						      pj_sock_t sock,
-						      const pj_sockaddr_in *addr_name);
+						  const pj_sockaddr_in *remote,
+						  pjsip_transport **p_transport);
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsip/sip_errno.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_errno.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_errno.h	(revision 54)
@@ -55,4 +55,9 @@
 /**
  * @hideinitializer
+ * Invalid message (syntax error)
+ */
+#define PJSIP_EINVALIDMSG       (PJSIP_ERRNO_START_PJSIP + 0)
+/**
+ * @hideinitializer
  * Missing required header(s).
  */
@@ -98,4 +103,20 @@
  */
 #define PJSIP_EINVALIDVIA	(PJSIP_ERRNO_START_PJSIP + 9)
+/**
+ * @hideinitializer
+ * Multiple Via headers in response.
+ */
+#define PJSIP_EMULTIPLEVIA	(PJSIP_ERRNO_START_PJSIP + 9)
+/**
+ * @hideinitializer
+ * SIP object is busy.
+ */
+#define PJSIP_EBUSY		(PJSIP_ERRNO_START_PJSIP + 10)
+/**
+ * @hideinitializer
+ * SIP object with the same type already exists.
+ */
+#define PJSIP_ETYPEEXISTS	(PJSIP_ERRNO_START_PJSIP + 11)
+
 
 
Index: /pjproject/trunk/pjsip/include/pjsip/sip_misc.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_misc.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_misc.h	(revision 54)
@@ -179,5 +179,5 @@
  */
 PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
-					     const pjsip_transport_t *tr,
+					     const pjsip_transport *tr,
 					     const pjsip_via_hdr *via,
 					     pjsip_host_port *addr);
Index: /pjproject/trunk/pjsip/include/pjsip/sip_private.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_private.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_private.h	(revision 54)
@@ -36,59 +36,4 @@
 
 
-/** 
- * Create a new transport manager.
- * @param pool The pool
- * @param endpt The endpoint
- * @param cb Callback to be called to receive messages from transport.
- */
-PJ_DECL(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool,
-						 pjsip_endpoint *endpt,
-						 void (*cb)(pjsip_endpoint *,
-							    pjsip_rx_data *),
-						 pjsip_transport_mgr **);
-
-
-/**
- * Destroy transport manager and release all transports.
- * @param mgr Transport manager to be destroyed.
- */
-PJ_DECL(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr );
-
-/**
- * Poll for transport events.
- * Incoming messages will be parsed by the transport manager, and the callback
- * will be called for each of this message.
- * @param endpt The endpoint.
- * @param timeout Timeout value, or NULL to wait forever.
- */
-PJ_DECL(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
-					        const pj_time_val *timeout );
-
-/**
- * Get the pointer to the first transport iterator.
- * @param mgr The transport manager.
- * @param it  The iterator used for iterating the hash element.
- * @return the iterator to the first transport, or NULL.
- */
-PJ_DECL(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
-						    pj_hash_iterator_t *it );
-
-
-/**
- * Get the next transport iterator.
- * @param itr the iterator to the transport.
- * @return the iterator pointed to the next transport, or NULL.
- */
-PJ_DECL(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
-						   pj_hash_iterator_t *itr );
-
-/**
- * Get the value of transport iterator.
- * @param mgr the transport manager.
- * @param itr the transport iterator.
- * @return the transport associated with the iterator.
- */
-PJ_DECL(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
-						  pj_hash_iterator_t *itr );
 
 /** 
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transaction.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transaction.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transaction.h	(revision 54)
@@ -112,5 +112,5 @@
     int				current_addr;   /**< Address currently used. */
 
-    pjsip_transport_t	       *transport;      /**< Transport to use.      */
+    pjsip_transport	       *transport;      /**< Transport to use.      */
 
     /*
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 54)
@@ -30,4 +30,5 @@
 #include <pj/list.h>
 #include <pj/ioqueue.h>
+#include <pj/timer.h>
 
 PJ_BEGIN_DECL
@@ -43,4 +44,76 @@
  * @{
  */
+
+/*****************************************************************************
+ *
+ * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
+ *
+ *****************************************************************************/
+
+/**
+ * Flags for SIP transports.
+ */
+enum pjsip_transport_flags_e
+{
+    PJSIP_TRANSPORT_RELIABLE	    = 1,    /**< Transport is reliable.	    */
+    PJSIP_TRANSPORT_SECURE	    = 2,    /**< Transport is secure.	    */
+    PJSIP_TRANSPORT_DATAGRAM	    = 4,    /**< Datagram based transport.  */
+};
+
+/**
+ * Check if transport tp is reliable.
+ */
+#define PJSIP_TRANSPORT_IS_RELIABLE(tp)	    \
+	    ((tp)->flag & PJSIP_TRANSPORT_RELIABLE)
+
+/**
+ * Get the transport type from the transport name.
+ *
+ * @param name	    Transport name, such as "TCP", or "UDP".
+ *
+ * @return	    The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if 
+ *		    the name is not recognized as the name of supported 
+ *		    transport.
+ */
+PJ_DECL(pjsip_transport_type_e) 
+pjsip_transport_get_type_from_name(const pj_str_t *name);
+
+/**
+ * Get the transport type for the specified flags.
+ *
+ * @param flag	    The transport flag.
+ *
+ * @return	    Transport type.
+ */
+PJ_DECL(pjsip_transport_type_e) 
+pjsip_transport_get_type_from_flag(unsigned flag);
+
+/**
+ * Get transport flag from type.
+ *
+ * @param type	    Transport type.
+ *
+ * @return	    Transport flags.
+ */
+PJ_DECL(unsigned)
+pjsip_transport_get_flag_from_type( pjsip_transport_type_e type );
+
+/**
+ * Get the default SIP port number for the specified type.
+ *
+ * @param type	    Transport type.
+ *
+ * @return	    The port number, which is the default SIP port number for
+ *		    the specified type.
+ */
+PJ_DECL(int) 
+pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);
+
+
+/*****************************************************************************
+ *
+ * RECEIVE DATA BUFFER.
+ *
+ *****************************************************************************/
 
 /**
@@ -53,75 +126,127 @@
 struct pjsip_rx_data
 {
-    //PJ_DECL_LIST_MEMBER(struct pjsip_rx_data);
-
-    /** Memory pool for this buffer. */
-    pj_pool_t		*pool;
-
-    /** Ioqueue op key. */
-    pj_ioqueue_op_key_t	 op_key;
-
-    /** Time when the message was received. */
-    pj_time_val		 timestamp;
-
-    /** The packet buffer. */
-    char		 packet[PJSIP_MAX_PKT_LEN];
-
-    /** The length of the packet received. */
-    int			 len;
-
-    /** The source address from which the packet was received. */
-    pj_sockaddr_in	 addr;
-
-    /** The length of the source address. */
-    int			 addr_len;
-
-    /** The transport object which received this packet. */
-    pjsip_transport_t	*transport;
-
-    /** The parsed message, if any. */
-    pjsip_msg		*msg;
-
-    /** This the transaction key generated from the message. This key is only
-     *  available after the rdata has reached the endpoint. 
-     */
-    pj_str_t		 key;
-
-    /** The Call-ID header as found in the message. */
-    pj_str_t		 call_id;
-
-    /** The From header as found in the message. */
-    pjsip_from_hdr	*from;
-
-    /** The To header as found in the message. */
-    pjsip_to_hdr	*to;
-
-    /** The topmost Via header as found in the message. */
-    pjsip_via_hdr	*via;
-
-    /** The CSeq header as found in the message. */
-    pjsip_cseq_hdr	*cseq;
-
-    /** Max forwards header. */
-    pjsip_max_forwards_hdr *max_fwd;
-
-    /** The first route header. */
-    pjsip_route_hdr     *route;
-
-    /** The first record-route header. */
-    pjsip_rr_hdr        *record_route;
-
-    /** Content-type header. */
-    pjsip_ctype_hdr     *ctype;
-
-    /** Content-length header. */
-    pjsip_clen_hdr      *clen;
-
-    /** The first Require header. */
-    pjsip_require_hdr   *require;
-
-    /** The list of error generated by the parser when parsing this message. */
-    pjsip_parser_err_report parse_err;
+
+    /**
+     * tp_info is part of rdata that remains static for the duration of the
+     * buffer. It is initialized when the buffer was created by transport.
+     */
+    struct 
+    {
+	/** Memory pool for this buffer. */
+	pj_pool_t		*pool;
+
+	/** The transport object which received this packet. */
+	pjsip_transport		*transport;
+
+	/** Ioqueue key. */
+	pj_ioqueue_op_key_t	 op_key;
+
+    } tp_info;
+
+
+    /**
+     * pkt_info is initialized by transport when it receives an incoming
+     * packet.
+     */
+    struct
+    {
+	/** Time when the message was received. */
+	pj_time_val		 timestamp;
+
+	/** Pointer to the original packet. */
+	char			 packet[PJSIP_MAX_PKT_LEN];
+
+	/** Zero termination for the packet. */
+	pj_uint32_t		 zero;
+
+	/** The length of the packet received. */
+	int			 len;
+
+	/** The source address from which the packet was received. */
+	pj_sockaddr_in		 addr;
+
+	/** The length of the source address. */
+	int			 addr_len;
+
+    } pkt_info;
+
+
+    /**
+     * msg_info is initialized by transport mgr (tpmgr) before this buffer
+     * is passed to endpoint.
+     */
+    struct
+    {
+	/** Start of msg buffer. */
+	char			*msg_buf;
+
+	/** Length fo message. */
+	int			 len;
+
+	/** The parsed message, if any. */
+	pjsip_msg		*msg;
+
+	/** The Call-ID header as found in the message. */
+	pj_str_t		 call_id;
+
+	/** The From header as found in the message. */
+	pjsip_from_hdr		*from;
+
+	/** The To header as found in the message. */
+	pjsip_to_hdr		*to;
+
+	/** The topmost Via header as found in the message. */
+	pjsip_via_hdr		*via;
+
+	/** The CSeq header as found in the message. */
+	pjsip_cseq_hdr		*cseq;
+
+	/** Max forwards header. */
+	pjsip_max_forwards_hdr	*max_fwd;
+
+	/** The first route header. */
+	pjsip_route_hdr		*route;
+
+	/** The first record-route header. */
+	pjsip_rr_hdr		*record_route;
+
+	/** Content-type header. */
+	pjsip_ctype_hdr		*ctype;
+
+	/** Content-length header. */
+	pjsip_clen_hdr		*clen;
+
+	/** The first Require header. */
+	pjsip_require_hdr	*require;
+
+	/** The list of error generated by the parser when parsing 
+	    this message. 
+	 */
+	pjsip_parser_err_report parse_err;
+
+    } msg_info;
+
+
+    /**
+     * endpt_info is initialized by endpoint after this buffer reaches
+     * endpoint.
+     */
+    struct
+    {
+	/** 
+	 * This the transaction key generated for the message. 
+	 */
+	pj_str_t		 key;
+
+    } endpt_info;
+
 };
 
+
+/*****************************************************************************
+ *
+ * TRANSMIT DATA BUFFER MANIPULATION.
+ *
+ *****************************************************************************/
 
 /**
@@ -157,8 +282,11 @@
 
     /** The transport manager for this buffer. */
-    pjsip_transport_mgr *mgr;
+    pjsip_tpmgr		*mgr;
 
     /** Ioqueue asynchronous operation key. */
     pj_ioqueue_op_key_t	 op_key;
+
+    /** Lock object. */
+    pj_lock_t		*lock;
 
     /** The message in this buffer. */
@@ -175,6 +303,27 @@
     /** Reference counter. */
     pj_atomic_t		*ref_cnt;
+
+    /** Being sent? */
+    int			 is_pending;
+
+    /** Transport internal. */
+    void		*token;
+    void	       (*cb)(void*, pjsip_tx_data*, pj_status_t);
 };
 
+
+/**
+ * Create a new, blank transmit buffer. The reference count is initialized
+ * to zero.
+ *
+ * @param mgr		The transport manager.
+ * @param tdata		Pointer to receive transmit data.
+ *
+ * @return		PJ_SUCCESS, or the appropriate error code.
+ *
+ * @see pjsip_endpt_create_tdata
+ */
+pj_status_t pjsip_tx_data_create( pjsip_tpmgr *mgr,
+                                  pjsip_tx_data **tdata );
 
 /**
@@ -194,4 +343,11 @@
  */
 PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
+
+/**
+ * Check if transmit data buffer contains a valid message.
+ *
+ * @param tdata	    The transmit buffer.
+ */
+PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata );
 
 /**
@@ -208,196 +364,243 @@
 
 
-/**
- * Flags for SIP transports.
- */
-enum pjsip_transport_flags_e
+/*****************************************************************************
+ *
+ * TRANSPORT
+ *
+ *****************************************************************************/
+
+/**
+ * This structure represent the "public" interface of a SIP transport.
+ * Applications normally extend this structure to include transport
+ * specific members.
+ */
+typedef struct pjsip_transport
 {
-    PJSIP_TRANSPORT_RELIABLE	    = 1,    /**< Transport is reliable. */
-    PJSIP_TRANSPORT_SECURE	    = 2,    /**< Transport is secure. */
-    PJSIP_TRANSPORT_IOQUEUE_BUSY    = 4,    /**< WTH?? */
+    char		    obj_name[PJ_MAX_OBJ_NAME];	/**< Name. */
+
+    pj_pool_t		   *pool;	    /**< Pool used by transport.    */
+    pj_atomic_t		   *ref_cnt;	    /**< Reference counter.	    */
+    pj_lock_t		   *lock;	    /**< Lock object.		    */
+    int			    tracing;	    /**< Tracing enabled?	    */
+
+    pjsip_transport_type_e  type;	    /**< Transport type.	    */
+    char		    type_name[8];   /**< Type name.		    */
+    unsigned		    flag;	    /**< #pjsip_transport_flags_e   */
+
+    pj_sockaddr_in	    local_addr;	    /**< Bound address.		    */
+    pj_sockaddr_in	    public_addr;    /**< STUN addres.		    */
+    pj_sockaddr_in	    rem_addr;	    /**< Remote addr (zero for UDP) */
+
+    pjsip_tpmgr		   *tpmgr;	    /**< Transport manager.	    */
+    pj_timer_entry	    idle_timer;	    /**< Timer when ref cnt is zero.*/
+
+    /**
+     * Function to be called by transport manager to send SIP message.
+     *
+     * @param transport	    The transport to send the message.
+     * @param packet	    The buffer to send.
+     * @param length	    The length of the buffer to send.
+     * @param op_key	    Completion token, which will be supplied to
+     *			    caller when pending send operation completes.
+     * @param rem_addr	    The remote destination address.
+     * @param callback	    If supplied, the callback will be called
+     *			    once a pending transmission has completed. If
+     *			    the function completes immediately (i.e. return
+     *			    code is not PJ_EPENDING), the callback will not
+     *			    be called.
+     *
+     * @return		    Should return PJ_SUCCESS only if data has been
+     *			    succesfully queued to operating system for 
+     *			    transmission. Otherwise it may return PJ_EPENDING
+     *			    if the underlying transport can not send the
+     *			    data immediately and will send it later, which in
+     *			    this case caller doesn't have to do anything 
+     *			    except wait the calback to be called, if it 
+     *			    supplies one.
+     *			    Other return values indicate the error code.
+     */
+    pj_status_t (*send_msg)(pjsip_transport *transport, 
+			    const void *packet, 
+			    pj_size_t length,
+			    pj_ioqueue_op_key_t *op_key,
+			    const pj_sockaddr_in *rem_addr,
+			    void *token,
+			    void (*callback)(pjsip_transport *transport,
+					     void *token, 
+					     pj_status_t status));
+
+    /**
+     * Destroy this transport.
+     */
+    pj_status_t (*destroy)(pjsip_transport *transport);
+
+    /*
+     * Application may extend this structure..
+     */
+} pjsip_transport;
+
+
+/**
+ * Register a transport.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
+					       pjsip_transport *tp );
+
+
+/**
+ * Unregister transport. This will eventually call the transport to
+ * destroy itself.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
+						 pjsip_transport *tp);
+
+/**
+ * Add ref.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp );
+
+/**
+ * Dec ref.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp );
+
+
+/**
+ * Call for incoming message.
+ */
+PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr,
+					       pjsip_rx_data *rdata);
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT FACTORY
+ *
+ *****************************************************************************/
+
+
+/**
+ * Transport factory.
+ */
+typedef struct pjsip_tpfactory pjsip_tpfactory;
+
+/**
+ * Transport factory.
+ */
+struct pjsip_tpfactory
+{
+    /* This list is managed by transport manager. */
+    PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory);
+
+    pj_pool_t		   *pool;
+    pj_lock_t		   *lock;
+
+    pjsip_transport_type_e  type;
+    char		    type_name[8];
+    unsigned		    flag;
+
+    pj_sockaddr_in	    local_addr;
+    pj_sockaddr_in	    public_addr;
+
+    /**
+     * Create new outbound connection.
+     */
+    pj_status_t (*create_transport)(pjsip_tpfactory *factory,
+				    pjsip_tpmgr *mgr,
+				    pjsip_endpoint *endpt,
+				    pj_ioqueue_t *ioqueue,
+				    const pj_sockaddr_in *rem_addr,
+				    pjsip_transport **transport);
+
+    /*
+     * Application may extend this structure..
+     */
 };
 
-/**
- * Get the transport type from the transport name.
- *
- * @param name	    Transport name, such as "TCP", or "UDP".
- *
- * @return	    The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if 
- *		    the name is not recognized as the name of supported 
- *		    transport.
- */
-PJ_DECL(pjsip_transport_type_e) 
-pjsip_transport_get_type_from_name(const pj_str_t *name);
-
-/**
- * Get the transport type for the specified flags.
- *
- * @param flag	    The transport flag.
- *
- * @return	    Transport type.
- */
-PJ_DECL(pjsip_transport_type_e) 
-pjsip_transport_get_type_from_flag(unsigned flag);
-
-/**
- * Get the default SIP port number for the specified type.
- *
- * @param type	    Transport type.
- *
- * @return	    The port number, which is the default SIP port number for
- *		    the specified type.
- */
-PJ_DECL(int) 
-pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);
-
-
-/**
- * Add reference to transport.
- * Transactions or dialogs that uses a particular transport must call this 
- * function to indicate that the transport is being used, thus preventing the
- * transport from being closed.
- *
- * @param transport	The transport.
- */
-PJ_DECL(void) 
-pjsip_transport_add_ref( pjsip_transport_t *transport );
-
-/**
- * Decrease reference to transport.
- * When the transport reference counter becomes zero, a timer will be started
- * and when this timer expires and the reference counter is still zero, the
- * transport will be released.
- *
- * @param transport	The transport
- */
-PJ_DECL(void) 
-pjsip_transport_dec_ref( pjsip_transport_t *transport );
-
-
-/**
- * Macro to check whether the transport is reliable.
- *
- * @param transport	The transport
- *
- * @return		non-zero (not necessarily 1) if transport is reliable.
- */
-#define PJSIP_TRANSPORT_IS_RELIABLE(transport)	\
-	(pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_RELIABLE)
-
-
-/**
- * Macro to check whether the transport is secure.
- *
- * @param transport	The transport
- *
- * @return		non-zero (not necessarily one) if transport is secure.
- */
-#define PJSIP_TRANSPORT_IS_SECURE(transport)	\
-	(pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_SECURE)
-
-/**
- * Get the transport type.
- *
- * @param tr		The transport.
- *
- * @return		Transport type.
- */
-PJ_DECL(pjsip_transport_type_e) 
-pjsip_transport_get_type( const pjsip_transport_t * tr);
-
-/**
- * Get the transport type name (ie "UDP", or "TCP").
- *
- * @param tr		The transport.
- * @return		The string type.
- */
-PJ_DECL(const char *) 
-pjsip_transport_get_type_name( const pjsip_transport_t * tr);
-
-/**
- * Get the transport's object name.
- *
- * @param tr		The transport.
- * @return		The object name.
- */
-PJ_DECL(const char*) 
-pjsip_transport_get_obj_name( const pjsip_transport_t *tr );
-
-/**
- * Get the transport's reference counter.
- *
- * @param tr		The transport.
- * @return		The reference count value.
- */
-PJ_DECL(int) 
-pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr );
-
-/**
- * Get transport flag.
- *
- * @param tr		The transport.
- * @return		Transport flag.
- */
-PJ_DECL(unsigned) 
-pjsip_transport_get_flag( const pjsip_transport_t * tr );
-
-/**
- * Get the local address of the transport, ie. the address which the socket
- * is bound.
- *
- * @param tr		The transport.
- * @return		The address.
- */
-PJ_DECL(const pj_sockaddr_in *) 
-pjsip_transport_get_local_addr( pjsip_transport_t * tr );
-
-/**
- * Get the address name of the transport. Address name can be an arbitrary
- * address assigned to a transport. This is usefull for example when STUN
- * is used, then the address name of an UDP transport can specify the public
- * address of the transport. When the address name is not set, then value
- * will be equal to the local/bound address. Application should normally
- * prefer to use the address name instead of the local address.
- *
- * @param tr		The transport.
- * @return		The address name.
- */
-PJ_DECL(const pj_sockaddr_in*) 
-pjsip_transport_get_addr_name (pjsip_transport_t *tr);
-
-/**
- * Get the remote address of the transport. Not all transports will have 
- * a valid remote address. UDP transports, for example, will likely to have
- * zero has their remote address, because UDP transport can be used to send
- * and receive messages from multiple destinations.
- *
- * @param tr		The transport.
- * @return		The address.
- */
-PJ_DECL(const pj_sockaddr_in *) 
-pjsip_transport_get_remote_addr( const pjsip_transport_t * tr );
-
-/**
- * Send a SIP message using the specified transport, to the address specified
- * in the outgoing data. This function is only usefull for application when it
- * wants to handle the message statelessly, because otherwise it should create
- * a transaction and let the transaction handles the transmission of the 
- * message.
- *
- * This function will send the message immediately, so application must be
- * sure that the transport is ready to do so before calling this function.
- *
- * @param tr		The transport to send the message.
- * @param tdata		The outgoing message buffer.
- * @param addr		The remote address.
- * @param sent          If not null, it will be filled up with the length of
- *                      data sent.
- *
- * @return		PJ_SUCCESS on success, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr, 
-					       pjsip_tx_data *tdata,
-					       const pj_sockaddr_in *addr,
-					       pj_ssize_t *sent);
+
+
+/**
+ * Register a transport factory.
+ *
+ * @param mgr		The transport manager.
+ * @param factory	Transport factory.
+ *
+ * @return		PJ_SUCCESS if listener was successfully created.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_register_tpfactory(pjsip_tpmgr *mgr,
+						    pjsip_tpfactory *tpf);
+
+/**
+ * Unregister factory.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_unregister_tpfactory(pjsip_tpmgr *mgr,
+						      pjsip_tpfactory *tpf);
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT MANAGER
+ *
+ *****************************************************************************/
+
+/**
+ * Create a new transport manager.
+ *
+ * @param pool	    Pool.
+ * @param endpt	    Endpoint instance.
+ * @param cb	    Callback to receive incoming message.
+ * @param p_mgr	    Pointer to receive the new transport manager.
+ *
+ * @return	    PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
+					 pjsip_endpoint * endpt,
+					 pj_ioqueue_t *ioqueue,
+					 pj_timer_heap_t *timer_heap,
+					 void (*cb)(pjsip_endpoint*,
+						    pj_status_t,
+						    pjsip_rx_data *),
+					 pjsip_tpmgr **p_mgr);
+
+
+/**
+ * Destroy transport manager.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_destroy(pjsip_tpmgr *mgr);
+
+
+/**
+ * Dump transport info.
+ */
+PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr);
+
+
+/*****************************************************************************
+ *
+ * PUBLIC API
+ *
+ *****************************************************************************/
+
+
+/**
+ * Find transport to be used to send message to remote destination. If no
+ * suitable transport is found, a new one will be created.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
+					   pjsip_transport_type_e type,
+					   const pj_sockaddr_in *remote,
+					   pjsip_transport **p_transport );
+
+
+/**
+ * Send a SIP message using the specified transport.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_send( pjsip_transport *tr, 
+					   pjsip_tx_data *tdata,
+					   const pj_sockaddr_in *addr,
+					   void *token,
+					   void (*cb)(void *token, 
+						      pjsip_tx_data *tdata,
+						      pj_status_t));
 
 
@@ -406,88 +609,4 @@
  */
 
-/*
- * PRIVATE FUNCTIONS!!!
- *
- * These functions are normally to be used by endpoint. Application should
- * use the variant provided by the endpoint instance.
- *
- * Application normally wouldn't be able to call these functions because it
- * has no reference of the transport manager (the instance of the transport
- * manager is hidden by endpoint!).
- */
-
-/*
- * Create a new transmit buffer.
- *
- * @param mgr		The transport manager.
- * @return		The transmit buffer data, or NULL on error.
- */
-pj_status_t pjsip_tx_data_create( pjsip_transport_mgr *mgr,
-                                  pjsip_tx_data **tdata );
-
-
-/**
- * Create listener.
- *
- * @param mgr		The transport manager.
- * @param type		Transport type.
- * @param local_addr	The address to bind.
- * @param addr_name	If not null, sets the address name. If NULL, 
- *			then the local address will be used.
- *
- * @return		PJ_SUCCESS if listener was successfully created.
- */
-PJ_DECL(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
-					    pjsip_transport_type_e type,
-					    pj_sockaddr_in *local_addr,
-					    const pj_sockaddr_in *addr_name);
-
-
-/**
- * Create UDP listener.
- *
- * @param mgr		The transport manager.
- * @param sock		The UDP socket.
- * @param addr_name	If not null, sets the address name. If NULL, 
- *			then the local address will be used.
- *
- * @return		PJ_SUCCESS if listener was successfully created.
- */
-PJ_DECL(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
-						pj_sock_t sock,
-						const pj_sockaddr_in *addr_name);
-
-/** 
- * Type of function to receive asynchronous transport completion for
- * pjsip_transport_get() operation.
- *
- * @param tr		The transport.
- * @param token		Token registered previously.
- * @param status	Status of operation.
- */
-typedef void pjsip_transport_completion_callback(pjsip_transport_t *tr, 
-						 void *token, 
-						 pj_status_t status);
-
-/**
- * Find transport to be used to send message to remote destination. If no
- * suitable transport is found, a new one will be created. If transport
- * can not be available immediately (for example, an outgoing TCP connec()),
- * then the caller will be notified later via the callback.
- *
- * @param mgr		The transport manager.
- * @param pool		Pool to allocate asychronous job (if required).
- * @param type		The transport type.
- * @param remote	The remote address.
- * @param token		The token that will be passed to the callback.
- * @param cb		The callback to be called to report the completion of 
- *			the operation.
- */
-PJ_DECL(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
-				   pj_pool_t *pool,
-				   pjsip_transport_type_e type,
-				   const pj_sockaddr_in *remote,
-				   void *token,
-				   pjsip_transport_completion_callback *cb);
 
 PJ_END_DECL
Index: /pjproject/trunk/pjsip/include/pjsip/sip_types.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_types.h	(revision 53)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_types.h	(revision 54)
@@ -26,10 +26,10 @@
  * Opaque data structure for transports (sip_transport.h).
  */
-typedef struct pjsip_transport_t pjsip_transport_t;
+typedef struct pjsip_transport pjsip_transport;
 
 /**
  * Opaque data type for transport manager (sip_transport.h).
  */
-typedef struct pjsip_transport_mgr pjsip_transport_mgr;
+typedef struct pjsip_tpmgr pjsip_tpmgr;
 
 /**
@@ -44,5 +44,4 @@
     PJSIP_TRANSPORT_UDP,
 
-#if PJ_HAS_TCP
     /** TCP. */
     PJSIP_TRANSPORT_TCP,
@@ -53,5 +52,4 @@
     /** SCTP. */
     PJSIP_TRANSPORT_SCTP,
-#endif
 
 } pjsip_transport_type_e;
Index: /pjproject/trunk/pjsip/src/pjsip-ua/sip_dialog.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip-ua/sip_dialog.c	(revision 53)
+++ /pjproject/trunk/pjsip/src/pjsip-ua/sip_dialog.c	(revision 54)
@@ -1080,4 +1080,5 @@
      * We will rewrite the message with a new status code.
      */
+    only if tdata is not pending!!!
     tdata = dlg->invite_tsx->last_tx;
     msg = tdata->msg;
Index: /pjproject/trunk/pjsip/src/pjsip/sip_auth.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_auth.c	(revision 53)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_auth.c	(revision 54)
@@ -36,5 +36,5 @@
 
 /* A macro just to get rid of type mismatch between char and unsigned char */
-#define MD5_APPEND(pms,buf,len)	md5_append(pms, (const unsigned char*)buf, len)
+#define MD5_APPEND(pms,buf,len)	pj_md5_update(pms, (const pj_uint8_t*)buf, len)
 
 /* Logging. */
@@ -81,5 +81,5 @@
     char ha2[MD5STRLEN];
     unsigned char digest[16];
-    md5_state_t pms;
+    pj_md5_context pms;
 
     pj_assert(result->slen >= MD5STRLEN);
@@ -91,5 +91,5 @@
 	 *** ha1 = MD5(username ":" realm ":" password) 
 	 ***/
-	md5_init(&pms);
+	pj_md5_init(&pms);
 	MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);
 	MD5_APPEND( &pms, ":", 1);
@@ -97,5 +97,5 @@
 	MD5_APPEND( &pms, ":", 1);
 	MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);
-	md5_finish(&pms, digest);
+	pj_md5_final(&pms, digest);
 
 	digest2str(digest, ha1);
@@ -111,9 +111,9 @@
      *** ha2 = MD5(method ":" req_uri) 
      ***/
-    md5_init(&pms);
+    pj_md5_init(&pms);
     MD5_APPEND( &pms, method->ptr, method->slen);
     MD5_APPEND( &pms, ":", 1);
     MD5_APPEND( &pms, uri->ptr, uri->slen);
-    md5_finish(&pms, digest);
+    pj_md5_final(&pms, digest);
     digest2str(digest, ha2);
 
@@ -127,5 +127,5 @@
      ***    response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
      ***/
-    md5_init(&pms);
+    pj_md5_init(&pms);
     MD5_APPEND( &pms, ha1, MD5STRLEN);
     MD5_APPEND( &pms, ":", 1);
@@ -143,5 +143,5 @@
 
     /* This is the final response digest. */
-    md5_finish(&pms, digest);
+    pj_md5_final(&pms, digest);
     
     /* Convert digest to string and store in chal->response. */
@@ -735,13 +735,13 @@
     PJ_UNUSED_ARG(endpt);
 
-    pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG);
-    pj_assert(rdata->msg->line.status.code == 401 ||
-	      rdata->msg->line.status.code == 407 );
+    pj_assert(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG);
+    pj_assert(rdata->msg_info.msg->line.status.code == 401 ||
+	      rdata->msg_info.msg->line.status.code == 407 );
 
     /*
      * Respond to each authentication challenge.
      */
-    hdr = rdata->msg->hdr.next;
-    while (hdr != &rdata->msg->hdr) {
+    hdr = rdata->msg_info.msg->hdr.next;
+    while (hdr != &rdata->msg_info.msg->hdr) {
 	pjsip_auth_session *sess;
 	const pjsip_www_authenticate_hdr *hchal;
@@ -751,9 +751,9 @@
 	while (hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
 	       hdr->type != PJSIP_H_PROXY_AUTHENTICATE &&
-	       hdr != &rdata->msg->hdr)
+	       hdr != &rdata->msg_info.msg->hdr)
 	{
 	    hdr = hdr->next;
 	}
-	if (hdr == &rdata->msg->hdr)
+	if (hdr == &rdata->msg_info.msg->hdr)
 	    break;
 
Index: /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 53)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 54)
@@ -33,5 +33,5 @@
 #include <pj/assert.h>
 #include <pj/errno.h>
-
+#include <pj/lock.h>
 
 #define PJSIP_EX_NO_MEMORY  PJ_NO_MEMORY_EXCEPTION
@@ -64,5 +64,8 @@
 
     /** Transport manager. */
-    pjsip_transport_mgr *transport_mgr;
+    pjsip_tpmgr		*transport_mgr;
+
+    /** Ioqueue. */
+    pj_ioqueue_t	*ioqueue;
 
     /** DNS Resolver. */
@@ -96,5 +99,6 @@
  * Prototypes.
  */
-static void endpt_transport_callback( pjsip_endpoint *, pjsip_rx_data *rdata );
+static void endpt_transport_callback(pjsip_endpoint*, 
+				     pj_status_t, pjsip_rx_data*);
 
 
@@ -355,4 +359,5 @@
     pjsip_endpoint *endpt;
     pjsip_max_forwards_hdr *mf_hdr;
+    pj_lock_t *lock = NULL;
 
     PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()"));
@@ -399,9 +404,26 @@
     }
 
+    /* Set recursive lock for the timer heap. */
+    status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+    pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
+
+    /* Set maximum timed out entries to process in a single poll. */
+    pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap, 
+					     PJSIP_MAX_TIMED_OUT_ENTRIES);
+
+    /* Create ioqueue. */
+    status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
     /* Create transport manager. */
-    status = pjsip_transport_mgr_create( endpt->pool,
-					 endpt,
-					 &endpt_transport_callback,
-					 &endpt->transport_mgr);
+    status = pjsip_tpmgr_create( endpt->pool, endpt,
+				 endpt->ioqueue, endpt->timer_heap,
+			         &endpt_transport_callback,
+				 &endpt->transport_mgr);
     if (status != PJ_SUCCESS) {
 	goto on_error;
@@ -446,5 +468,5 @@
 on_error:
     if (endpt->transport_mgr) {
-	pjsip_transport_mgr_destroy(endpt->transport_mgr);
+	pjsip_tpmgr_destroy(endpt->transport_mgr);
 	endpt->transport_mgr = NULL;
     }
@@ -471,5 +493,5 @@
 
     /* Shutdown and destroy all transports. */
-    pjsip_transport_mgr_destroy(endpt->transport_mgr);
+    pjsip_tpmgr_destroy(endpt->transport_mgr);
 
     /* Delete endpoint mutex. */
@@ -533,18 +555,14 @@
 					const pj_time_val *max_timeout)
 {
-    pj_time_val timeout;
-    int i;
+    /* timeout is 'out' var. This just to make compiler happy. */
+    pj_time_val timeout = { 0, 0};
 
     PJ_LOG(5, (THIS_FILE, "pjsip_endpt_handle_events()"));
 
     /* Poll the timer. The timer heap has its own mutex for better 
-     * granularity, so we don't need to lock end endpoint. We also keep
-     * polling the timer while we have events.
+     * granularity, so we don't need to lock end endpoint. 
      */
-    timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */
-    for (i=0; i<10; ++i) {
-	if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1)
-	    break;
-    }
+    timeout.sec = timeout.msec = 0;
+    pj_timer_heap_poll( endpt->timer_heap, &timeout );
 
     /* If caller specifies maximum time to wait, then compare the value with
@@ -555,6 +573,6 @@
     }
 
-    /* Poll events in the transport manager. */
-    pjsip_transport_mgr_handle_events( endpt->transport_mgr, &timeout);
+    /* Poll ioqueue. */
+    pj_ioqueue_poll( endpt->ioqueue, &timeout);
 }
 
@@ -659,11 +677,11 @@
 {
     pjsip_role_e role;
-    if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+    if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
 	role = PJSIP_ROLE_UAS;
     } else {
 	role = PJSIP_ROLE_UAC;
     }
-    pjsip_tsx_create_key(rdata->pool, &rdata->key, role,
-			 &rdata->cseq->method, rdata);
+    pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
+			 &rdata->msg_info.cseq->method, rdata);
 }
 
@@ -673,11 +691,23 @@
  */
 static void endpt_transport_callback( pjsip_endpoint *endpt,
+				      pj_status_t status,
 				      pjsip_rx_data *rdata )
 {
-    pjsip_msg *msg = rdata->msg;
+    pjsip_msg *msg = rdata->msg_info.msg;
     pjsip_transaction *tsx;
     pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
 
     PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
+
+    if (status != PJ_SUCCESS) {
+	const char *src_addr = pj_inet_ntoa(rdata->pkt_info.addr.sin_addr);
+	int port = pj_ntohs(rdata->pkt_info.addr.sin_port);
+	PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
+			       "Src.addr=%s:%d, packet:--\n"
+			       "%s\n"
+			       "-- end of packet. Error",
+			       src_addr, port, rdata->msg_info.msg_buf));
+	return;
+    }
 
     /* For response, check that the value in Via sent-by match the transport.
@@ -688,14 +718,14 @@
 	const pj_sockaddr_in *addr;
 	const char *addr_addr;
-	int port = rdata->via->sent_by.port;
+	int port = rdata->msg_info.via->sent_by.port;
 	pj_bool_t mismatch = PJ_FALSE;
 	if (port == 0) {
 	    int type;
-	    type = pjsip_transport_get_type(rdata->transport);
+	    type = rdata->tp_info.transport->type;
 	    port = pjsip_transport_get_default_port_for_type(type);
 	}
-	addr = pjsip_transport_get_addr_name(rdata->transport);
+	addr = &rdata->tp_info.transport->public_addr;
 	addr_addr = pj_inet_ntoa(addr->sin_addr);
-	if (pj_strcmp2(&rdata->via->sent_by.host, addr_addr) != 0)
+	if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
 	    mismatch = PJ_TRUE;
 	else if (port != pj_ntohs(addr->sin_port)) {
@@ -707,5 +737,5 @@
 	     * both the port in sent-by and rport. We try to be lenient here!
 	     */
-	    if (rdata->via->rport_param != pj_sockaddr_in_get_port(addr))
+	    if (rdata->msg_info.via->rport_param != pj_sockaddr_in_get_port(addr))
 		mismatch = PJ_TRUE;
 	    else {
@@ -730,5 +760,5 @@
     /* Find the transaction for the received message. */
     PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s", 
-			 rdata->key.slen, rdata->key.ptr));
+			 rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
 
     /* Start lock mutex in the endpoint. */
@@ -736,5 +766,5 @@
 
     /* Find the transaction in the hash table. */
-    tsx = pj_hash_get( endpt->tsx_table, rdata->key.ptr, rdata->key.slen );
+    tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
 
     /* Unlock mutex. */
@@ -753,5 +783,5 @@
 	    /* Inform TU about the 200 message, only if it's INVITE. */
 	    if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
-		rdata->cseq->method.id == PJSIP_INVITE_METHOD) 
+		rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) 
 	    {
 		pjsip_event e;
@@ -777,5 +807,5 @@
 	 * For non-ACK request message, create a new transaction.
 	 */
-	} else if (rdata->msg->line.req.method.id != PJSIP_ACK_METHOD) {
+	} else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
 
 	    pj_status_t status;
@@ -806,5 +836,5 @@
 	pjsip_tsx_on_rx_msg( tsx, rdata );
 
-    } else if (rdata->msg->line.req.method.id == PJSIP_ACK_METHOD) {
+    } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
 	/*
 	 * This is an ACK message, but the INVITE transaction could not
@@ -915,33 +945,14 @@
  * Find/create transport.
  */
-PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
-					pj_pool_t *pool,
-					pjsip_transport_type_e type,
-					const pj_sockaddr_in *remote,
-					void *token,
-					pjsip_transport_completion_callback *cb)
-{
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_get_transport()"));
-    pjsip_transport_get( endpt->transport_mgr, pool, type,
-			 remote, token, cb);
-}
-
-
-PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
-						 pjsip_transport_type_e type,
-						 pj_sockaddr_in *addr,
-						 const pj_sockaddr_in *addr_name)
-{
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_listener()"));
-    return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name );
-}
-
-PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
-						     pj_sock_t sock,
-						     const pj_sockaddr_in *addr_name)
-{
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_udp_listener()"));
-    return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name );
-}
+PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
+						  pjsip_transport_type_e type,
+						  const pj_sockaddr_in *remote,
+						  pjsip_transport **p_transport)
+{
+    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()"));
+    return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, remote,
+					p_transport);
+}
+
 
 PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
@@ -949,6 +960,4 @@
 #if PJ_LOG_MAX_LEVEL >= 3
     unsigned count;
-    pj_hash_iterator_t itr_val;
-    pj_hash_iterator_t *itr;
 
     PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
@@ -1015,36 +1024,6 @@
 
     /* Transports. 
-     * Note: transport is not properly locked in this function.
-     *       See pjsip_transport_first, pjsip_transport_next.
      */
-    itr = pjsip_transport_first( endpt->transport_mgr, &itr_val );
-    if (itr) {
-	PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
-
-	do {
-	    char src_addr[128], dst_addr[128];
-	    int src_port, dst_port;
-	    const pj_sockaddr_in *addr;
-	    pjsip_transport_t *t;
-
-	    t = pjsip_transport_this(endpt->transport_mgr, itr);
-	    addr = pjsip_transport_get_local_addr(t);
-	    pj_native_strcpy(src_addr, pj_inet_ntoa(addr->sin_addr));
-	    src_port = pj_ntohs(addr->sin_port);
-
-	    addr = pjsip_transport_get_remote_addr(t);
-	    pj_native_strcpy(dst_addr, pj_inet_ntoa(addr->sin_addr));
-	    dst_port = pj_ntohs(addr->sin_port);
-
-	    PJ_LOG(3, (THIS_FILE, "  %s %s %s:%d --> %s:%d (refcnt=%d)", 
-		       pjsip_transport_get_type_name(t),
-		       pjsip_transport_get_obj_name(t),
-		       src_addr, src_port,
-		       dst_addr, dst_port,
-		       pjsip_transport_get_ref_cnt(t)));
-
-	    itr = pjsip_transport_next(endpt->transport_mgr, itr);
-	} while (itr);
-    }
+    pjsip_tpmgr_dump_transports( endpt->transport_mgr );
 
     /* Timer. */
Index: /pjproject/trunk/pjsip/src/pjsip/sip_misc.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_misc.c	(revision 53)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_misc.c	(revision 54)
@@ -430,5 +430,5 @@
 
     /* rdata must be a request message. */
-    req_msg = rdata->msg;
+    req_msg = rdata->msg_info.msg;
     pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
 
@@ -450,8 +450,8 @@
 
     /* Set TX data attributes. */
-    tdata->rx_timestamp = rdata->timestamp;
+    tdata->rx_timestamp = rdata->pkt_info.timestamp;
 
     /* Copy all the via headers, in order. */
-    via = rdata->via;
+    via = rdata->msg_info.via;
     while (via) {
 	pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via));
@@ -479,13 +479,13 @@
 
     /* Copy From header. */
-    hdr = pjsip_hdr_clone(tdata->pool, rdata->from);
+    hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.from);
     pjsip_msg_add_hdr( msg, hdr);
 
     /* Copy To header. */
-    hdr = pjsip_hdr_clone(tdata->pool, rdata->to);
+    hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.to);
     pjsip_msg_add_hdr( msg, hdr);
 
     /* Copy CSeq header. */
-    hdr = pjsip_hdr_clone(tdata->pool, rdata->cseq);
+    hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.cseq);
     pjsip_msg_add_hdr( msg, hdr);
 
@@ -515,6 +515,6 @@
 
     /* rdata must be a final response. */
-    pj_assert(rdata->msg->type==PJSIP_RESPONSE_MSG &&
-	      rdata->msg->line.status.code >= 300);
+    pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG &&
+	      rdata->msg_info.msg->line.status.code >= 300);
 
     /* Log this action. */
@@ -543,5 +543,5 @@
     to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg, 
 						   PJSIP_H_TO, NULL);
-    pj_strdup(tdata->pool, &to->tag, &rdata->to->tag);
+    pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag);
     pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
 
@@ -675,5 +675,5 @@
  */
 PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
-					    const pjsip_transport_t *req_transport,
+					    const pjsip_transport *req_transport,
 					    const pjsip_via_hdr *via,
 					    pjsip_host_port *send_addr)
@@ -688,10 +688,10 @@
      * - otherwise send to the address in sent-by.
      */
-    send_addr->flag = pjsip_transport_get_flag(req_transport);
-    send_addr->type = pjsip_transport_get_type(req_transport);
+    send_addr->flag = req_transport->flag;
+    send_addr->type = req_transport->type;
 
     if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
 	const pj_sockaddr_in *remote_addr;
-	remote_addr = pjsip_transport_get_remote_addr(req_transport);
+	remote_addr = &req_transport->rem_addr;
 	pj_strdup2(pool, &send_addr->host, 
 		   pj_inet_ntoa(remote_addr->sin_addr));
Index: /pjproject/trunk/pjsip/src/pjsip/sip_parser.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_parser.c	(revision 53)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_parser.c	(revision 54)
@@ -559,17 +559,17 @@
 
     context.scanner = &scanner;
-    context.pool = rdata->pool;
+    context.pool = rdata->tp_info.pool;
     context.rdata = rdata;
 
     PJ_TRY {
-	rdata->msg = int_parse_msg(&context, &rdata->parse_err);
+	rdata->msg_info.msg = int_parse_msg(&context, &rdata->msg_info.parse_err);
     } 
     PJ_DEFAULT {
-	rdata->msg = NULL;
+	rdata->msg_info.msg = NULL;
     }
     PJ_END
 
     pj_scan_fini(&scanner);
-    return rdata->msg;
+    return rdata->msg_info.msg;
 }
 
@@ -1259,5 +1259,5 @@
 
     if (ctx->rdata)
-        ctx->rdata->call_id = hdr->id;
+        ctx->rdata->msg_info.call_id = hdr->id;
 
     return (pjsip_hdr*)hdr;
@@ -1340,5 +1340,5 @@
 
     if (ctx->rdata)
-        ctx->rdata->clen = hdr;
+        ctx->rdata->msg_info.clen = hdr;
 
     return (pjsip_hdr*)hdr;
@@ -1368,5 +1368,5 @@
 
     if (ctx->rdata)
-        ctx->rdata->ctype = hdr;
+        ctx->rdata->msg_info.ctype = hdr;
 
     return (pjsip_hdr*)hdr;
@@ -1389,5 +1389,5 @@
 
     if (ctx->rdata)
-        ctx->rdata->cseq = hdr;
+        ctx->rdata->msg_info.cseq = hdr;
 
     return (pjsip_hdr*)hdr;
@@ -1433,5 +1433,5 @@
     parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
     if (ctx->rdata)
-        ctx->rdata->from = hdr;
+        ctx->rdata->msg_info.from = hdr;
 
     return (pjsip_hdr*)hdr;
@@ -1444,6 +1444,6 @@
     parse_generic_array_hdr(hdr, ctx->scanner);
 
-    if (ctx->rdata && ctx->rdata->require == NULL)
-        ctx->rdata->require = hdr;
+    if (ctx->rdata && ctx->rdata->msg_info.require == NULL)
+        ctx->rdata->msg_info.require = hdr;
 
     return (pjsip_hdr*)hdr;
@@ -1475,5 +1475,5 @@
 
     if (ctx->rdata)
-        ctx->rdata->to = hdr;
+        ctx->rdata->msg_info.to = hdr;
 
     return (pjsip_hdr*)hdr;
@@ -1529,5 +1529,5 @@
 
     if (ctx->rdata)
-        ctx->rdata->max_fwd = hdr;
+        ctx->rdata->msg_info.max_fwd = hdr;
 
     return (pjsip_hdr*)hdr;
@@ -1579,6 +1579,6 @@
     parse_hdr_end(scanner);
 
-    if (ctx->rdata && ctx->rdata->record_route==NULL)
-        ctx->rdata->record_route = first;
+    if (ctx->rdata && ctx->rdata->msg_info.record_route==NULL)
+        ctx->rdata->msg_info.record_route = first;
 
     return (pjsip_hdr*)first;
@@ -1607,6 +1607,6 @@
     parse_hdr_end(scanner);
 
-    if (ctx->rdata && ctx->rdata->route==NULL)
-        ctx->rdata->route = first;
+    if (ctx->rdata && ctx->rdata->msg_info.route==NULL)
+        ctx->rdata->msg_info.route = first;
 
     return (pjsip_hdr*)first;
@@ -1660,6 +1660,6 @@
     parse_hdr_end(scanner);
 
-    if (ctx->rdata && ctx->rdata->via == NULL)
-        ctx->rdata->via = first;
+    if (ctx->rdata && ctx->rdata->msg_info.via == NULL)
+        ctx->rdata->msg_info.via = first;
 
     return (pjsip_hdr*)first;
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transaction.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 53)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 54)
@@ -193,16 +193,16 @@
 
     PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL);
-    PJ_ASSERT_RETURN(rdata->msg, PJ_EINVAL);
-    PJ_ASSERT_RETURN(rdata->via, PJSIP_EMISSINGHDR);
-    PJ_ASSERT_RETURN(rdata->cseq, PJSIP_EMISSINGHDR);
-    PJ_ASSERT_RETURN(rdata->from, PJSIP_EMISSINGHDR);
-
-    host = &rdata->via->sent_by.host;
-    req_uri = (pjsip_uri*)rdata->msg->line.req.uri;
+    PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL);
+    PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR);
+    PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR);
+    PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR);
+
+    host = &rdata->msg_info.via->sent_by.host;
+    req_uri = (pjsip_uri*)rdata->msg_info.msg->line.req.uri;
 
     /* Calculate length required. */
     len_required = 9 +			    /* CSeq number */
-		   rdata->from->tag.slen +   /* From tag. */
-		   rdata->call_id.slen +    /* Call-ID */
+		   rdata->msg_info.from->tag.slen +   /* From tag. */
+		   rdata->msg_info.call_id.slen +    /* Call-ID */
 		   host->slen +		    /* Via host. */
 		   9 +			    /* Via port. */
@@ -232,17 +232,17 @@
 
     /* Add CSeq (only the number). */
-    len = pj_utoa(rdata->cseq->cseq, p);
+    len = pj_utoa(rdata->msg_info.cseq->cseq, p);
     p += len;
     *p++ = SEPARATOR;
 
     /* Add From tag. */
-    len = rdata->from->tag.slen;
-    pj_memcpy( p, rdata->from->tag.ptr, len);
+    len = rdata->msg_info.from->tag.slen;
+    pj_memcpy( p, rdata->msg_info.from->tag.ptr, len);
     p += len;
     *p++ = SEPARATOR;
 
     /* Add Call-ID. */
-    len = rdata->call_id.slen;
-    pj_memcpy( p, rdata->call_id.ptr, len );
+    len = rdata->msg_info.call_id.slen;
+    pj_memcpy( p, rdata->msg_info.call_id.ptr, len );
     p += len;
     *p++ = SEPARATOR;
@@ -258,5 +258,5 @@
     *p++ = ':';
 
-    len = pj_utoa(rdata->via->sent_by.port, p);
+    len = pj_utoa(rdata->msg_info.via->sent_by.port, p);
     p += len;
     *p++ = SEPARATOR;
@@ -325,5 +325,5 @@
      * handled as RFC2543.
      */
-    const pj_str_t *branch = &rdata->via->branch_param;
+    const pj_str_t *branch = &rdata->msg_info.via->branch_param;
 
     if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
@@ -643,5 +643,5 @@
  * has completed, with or without error.
  */
-static void tsx_transport_callback(pjsip_transport_t *tr, 
+static void tsx_transport_callback(pjsip_transport *tr, 
 				   void *token, 
 				   pj_status_t status)
@@ -657,9 +657,9 @@
     if (status == PJ_SUCCESS) {
 	PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d",
-				  pjsip_transport_get_type_name(tr),
+				  tr->type_name,
 				  addr, tsx->dest_name.port));
     } else {
 	PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d", 
-				  pjsip_transport_get_type_name(tr),
+				  tr->type_name,
 				  addr, tsx->dest_name.port, status));
     }
@@ -878,5 +878,5 @@
 					pjsip_rx_data *rdata)
 {
-    pjsip_msg *msg = rdata->msg;
+    pjsip_msg *msg = rdata->msg_info.msg;
     pj_str_t *branch;
     pjsip_cseq_hdr *cseq;
@@ -890,5 +890,5 @@
 
     /* Keep shortcut to message */
-    msg = rdata->msg;
+    msg = rdata->msg_info.msg;
 
     /* Role is UAS */
@@ -909,5 +909,5 @@
 
     /* Duplicate branch parameter for transaction. */
-    branch = &rdata->via->branch_param;
+    branch = &rdata->msg_info.via->branch_param;
     pj_strdup(tsx->pool, &tsx->branch, branch);
 
@@ -916,5 +916,5 @@
 
     /* Save CSeq */
-    cseq = rdata->cseq;
+    cseq = rdata->msg_info.cseq;
     tsx->cseq = cseq->cseq;
 
@@ -932,8 +932,8 @@
      * then return the response using the same transport.
      */
-    if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->transport) || 
-	rdata->via->rport_param >= 0) 
+    if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport) || 
+	rdata->msg_info.via->rport_param >= 0) 
     {
-	tsx->transport = rdata->transport;
+	tsx->transport = rdata->tp_info.transport;
 	pjsip_transport_add_ref(tsx->transport);
 	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
@@ -944,11 +944,11 @@
 		pjsip_transport_get_type(tsx->transport);
 	pj_memcpy(&tsx->remote_addr.entry[0].addr, 
-		  &rdata->addr, rdata->addr_len);
+		  &rdata->pkt_info.addr, rdata->pkt_info.addr_len);
 	
     } else {
 	pj_status_t status;
 
-	status = pjsip_get_response_addr(tsx->pool, rdata->transport,
-					 rdata->via, &tsx->dest_name);
+	status = pjsip_get_response_addr(tsx->pool, rdata->tp_info.transport,
+					 rdata->msg_info.via, &tsx->dest_name);
 	if (status != PJ_SUCCESS) {
 	    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
@@ -1213,7 +1213,7 @@
 	    /* Don't update Via sent-by on retransmission. */
 	    if (via->sent_by.host.slen == 0) {
-		addr_name = pjsip_transport_get_addr_name(tsx->transport);
+		addr_name = &tsx->transport->public_addr;
 		pj_strdup2(tdata->pool, &via->transport, 
-			pjsip_transport_get_type_name(tsx->transport));
+			   tsx->transport->type_name);
 		pj_strdup2(tdata->pool, &via->sent_by.host, 
 			   pj_inet_ntoa(addr_name->sin_addr));
@@ -1397,5 +1397,5 @@
 	 */
 	/* Keep last_tx for authorization. */
-	code = event->body.rx_msg.rdata->msg->line.status.code;
+	code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
 	if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
 	    pjsip_tx_data_dec_ref(tsx->last_tx);
@@ -1666,5 +1666,5 @@
 	}
 
-	tsx->status_code = event->body.rx_msg.rdata->msg->line.status.code;
+	tsx->status_code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
     } else {
 	tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
@@ -1763,5 +1763,5 @@
 
     if (event->type == PJSIP_EVENT_RX_MSG) {
-	pjsip_msg *msg = event->body.rx_msg.rdata->msg;
+	pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
 	pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL );
 
@@ -1856,5 +1856,5 @@
 	     * TU doesn't need to be informed.
 	     */
-	    pjsip_msg *msg = event->body.rx_msg.rdata->msg;
+	    pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
 	    pj_assert(msg->type == PJSIP_RESPONSE_MSG);
 	    if (msg->type==PJSIP_RESPONSE_MSG &&
@@ -1922,8 +1922,8 @@
 
         pjsip_method_e method_id = 
-            event->body.rx_msg.rdata->msg->line.req.method.id;
+            event->body.rx_msg.rdata->msg_info.msg->line.req.method.id;
 
 	/* Must be a request message. */
-	pj_assert(event->body.rx_msg.rdata->msg->type == PJSIP_REQUEST_MSG);
+	pj_assert(event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG);
 
 	/* Must be an ACK request or a late INVITE retransmission. */
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 53)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 54)
@@ -30,127 +30,203 @@
 #include <pj/pool.h>
 #include <pj/assert.h>
-
-#define MGR_IDLE_CHECK_INTERVAL	30
-#define MGR_HASH_TABLE_SIZE	PJSIP_MAX_DIALOG_COUNT
-#define BACKLOG			5
-#define DEFAULT_SO_SNDBUF	(8 * 1024 * 1024)
-#define DEFAULT_SO_RCVBUF	(8 * 1024 * 1024)
-
-#define LOG_TRANSPORT_MGR	"trmgr"	
-#define THIS_FILE		"sip_transport"
-
-static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr );
-
-
-/**
- * New TCP socket for accept.
- */
-typedef struct incoming_socket_rec
-{
-    pj_sock_t		sock;
-    pj_sockaddr_in	remote;
-    pj_sockaddr_in	local;
-    int			addrlen;
-} incoming_socket_rec;
-
-/**
- * SIP Transport.
- */
-struct pjsip_transport_t
-{
-    /** Standard list members, for chaining the transport in the 
-     *  listener list. 
-     */
-    PJ_DECL_LIST_MEMBER(struct pjsip_transport_t);
-
-    /** Transport's pool. */
-    pj_pool_t		*pool;
-
-    /** Mutex */
-    pj_mutex_t		*tr_mutex;
-
-    /** Transport name for logging purpose */
-    char		 obj_name[PJ_MAX_OBJ_NAME];
-
-    /** Socket handle */
-    pj_sock_t		 sock;
-
-    /** Transport type. */
-    pjsip_transport_type_e type;
-
-    /** Flags to keep various states (see pjsip_transport_flags_e). */
-    pj_uint32_t		 flag;
-
-    /** I/O Queue key */
-    pj_ioqueue_key_t	*key;
-
-    /** Accept key. */
-    pj_ioqueue_op_key_t	 accept_op;
-
-    /** Receive data buffer */
-    pjsip_rx_data	*rdata;
-
-    /** Pointer to transport manager */
-    pjsip_transport_mgr *mgr;
-
-    /** Reference counter, to prevent this transport from being closed while
-     *  it's being used. 
-     */
-    pj_atomic_t		 *ref_cnt;
-
-    /** Local address. */
-    pj_sockaddr_in	 local_addr;
-
-    /** Address name (what to put in Via address field). */
-    pj_sockaddr_in	 addr_name;
-
-    /** Remote address (can be zero for UDP and for listeners). UDP listener
-     *  bound to local loopback interface (127.0.0.1) has remote address set
-     *  to 127.0.0.1 to prevent client from using it to send to remote hosts,
-     *  because remote host then will receive 127.0.0.1 as the packet's 
-     *  source address.
-     */
-    pj_sockaddr_in	 remote_addr;
-
-    /** Struct to save incoming socket information. */
-    incoming_socket_rec	 accept_data;
-
-    /** When this transport should be closed. */
-    pj_time_val		 close_time;
-
-    /** List of callbacks to be called when client attempt to use this 
-     *  transport while it's not connected (i.e. still connecting). 
-     */
-    pj_list		 cb_list;
-};
-
+#include <pj/lock.h>
+
+
+#define THIS_FILE    "transport"
 
 /*
  * Transport manager.
  */
-struct pjsip_transport_mgr 
-{
-    pj_hash_table_t *transport_table;
-    pj_mutex_t	    *mutex;
+struct pjsip_tpmgr 
+{
+    pj_hash_table_t *table;
+    pj_lock_t	    *lock;
     pjsip_endpoint  *endpt;
     pj_ioqueue_t    *ioqueue;
-    pj_time_val	     next_idle_check;
-    pj_size_t        send_buf_size;
-    pj_size_t        recv_buf_size;
-    void           (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata);
+    pj_timer_heap_t *timer_heap;
+    pjsip_tpfactory  factory_list;
+    void           (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
 };
 
-/*
- * Transport role.
- */
-typedef enum transport_role_e
-{
-    TRANSPORT_ROLE_LISTENER,
-    TRANSPORT_ROLE_TRANSPORT,
-} transport_role_e;
+
+
+/*****************************************************************************
+ *
+ * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
+ *
+ *****************************************************************************/
+
+/*
+ * Transport names.
+ */
+const struct
+{
+    pjsip_transport_type_e type;
+    pj_uint16_t		   port;
+    pj_str_t		   name;
+    unsigned		   flag;
+} transport_names[] = 
+{
+    { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}, 0},
+    { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM},
+    { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE},
+    { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE},
+    { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE}
+};
+
+
+/*
+ * Get transport type from name.
+ */
+PJ_DEF(pjsip_transport_type_e) 
+pjsip_transport_get_type_from_name(const pj_str_t *name)
+{
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+	if (pj_stricmp(name, &transport_names[i].name) == 0) {
+	    return transport_names[i].type;
+	}
+    }
+
+    pj_assert(!"Invalid transport name");
+    return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+
+/*
+ * Get the transport type for the specified flags.
+ */
+PJ_DEF(pjsip_transport_type_e) 
+pjsip_transport_get_type_from_flag(unsigned flag)
+{
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+	if (transport_names[i].flag == flag) {
+	    return transport_names[i].type;
+	}
+    }
+
+    pj_assert(!"Invalid transport type");
+    return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+PJ_DEF(unsigned)
+pjsip_transport_get_flag_from_type( pjsip_transport_type_e type )
+{
+    PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 0);
+    return transport_names[type].flag;
+}
+
+/*
+ * Get the default SIP port number for the specified type.
+ */
+PJ_DEF(int) 
+pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
+{
+    PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 5060);
+    return transport_names[type].port;
+}
+
+
+/*****************************************************************************
+ *
+ * TRANSMIT DATA BUFFER MANIPULATION.
+ *
+ *****************************************************************************/
+
+/*
+ * Create new transmit buffer.
+ */
+PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
+					  pjsip_tx_data **p_tdata )
+{
+    pj_pool_t *pool;
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
+
+    PJ_LOG(5, ("", "pjsip_tx_data_create"));
+
+    pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p",
+				    PJSIP_POOL_LEN_TDATA,
+				    PJSIP_POOL_INC_TDATA );
+    if (!pool)
+	return PJ_ENOMEM;
+
+    tdata = pj_pool_zalloc(pool, sizeof(pjsip_tx_data));
+    tdata->pool = pool;
+    tdata->mgr = mgr;
+    pj_snprintf(tdata->obj_name, PJ_MAX_OBJ_NAME, "tdta%p", tdata);
+
+    status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+	return status;
+    }
+    
+    //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock);
+    status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+	return status;
+    }
+
+    pj_ioqueue_op_key_init(&tdata->op_key, sizeof(tdata->op_key));
+
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Add reference to tx buffer.
+ */
+PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
+{
+    pj_atomic_inc(tdata->ref_cnt);
+}
+
+/*
+ * Decrease transport data reference, destroy it when the reference count
+ * reaches zero.
+ */
+PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+{
+    pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
+    if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
+	PJ_LOG(5,(tdata->obj_name, "destroying txdata"));
+	pj_atomic_destroy( tdata->ref_cnt );
+	pj_lock_destroy( tdata->lock );
+	pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
+    }
+}
+
+/*
+ * Invalidate the content of the print buffer to force the message to be
+ * re-printed when sent.
+ */
+PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
+{
+    tdata->buf.cur = tdata->buf.start;
+}
+
+PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
+{
+    return tdata->buf.cur != tdata->buf.start;
+}
+
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT KEY
+ *
+ *****************************************************************************/
 
 /*
  * Transport key for indexing in the hash table.
- * WATCH OUT FOR ALIGNMENT PROBLEM HERE!
  */
 typedef struct transport_key
@@ -162,555 +238,53 @@
 } transport_key;
 
-/*
- * Transport callback.
- */
-struct transport_callback
-{
-    PJ_DECL_LIST_MEMBER(struct transport_callback);
-
-    /** User defined token to be passed to the callback. */
-    void *token;
-
-    /** The callback function. */
-    void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status);
-
-};
-
-/*
- * Transport names.
- */
-const struct
-{
-    pjsip_transport_type_e type;
-    pj_uint16_t		   port;
-    pj_str_t		   name;
-} transport_names[] = 
-{
-    { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}},
-    { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}},
-#if PJ_HAS_TCP
-    { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}},
-    { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}},
-    { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}}
-#endif
-};
-
-static void on_ioqueue_read(pj_ioqueue_key_t *key, 
-                            pj_ioqueue_op_key_t *op_key, 
-                            pj_ssize_t bytes_read);
-static void on_ioqueue_write(pj_ioqueue_key_t *key, 
-                             pj_ioqueue_op_key_t *op_key, 
-                             pj_ssize_t bytes_sent);
-static void on_ioqueue_accept(pj_ioqueue_key_t *key, 
-                              pj_ioqueue_op_key_t *op_key, 
-			      pj_sock_t newsock,
-                              int status);
-static void on_ioqueue_connect(pj_ioqueue_key_t *key, 
-                               int status);
-
-static pj_ioqueue_callback ioqueue_transport_callback = 
-{
-    &on_ioqueue_read,
-    &on_ioqueue_write,
-    &on_ioqueue_accept,
-    &on_ioqueue_connect
-};
-
-static void init_key_from_transport(transport_key *key, 
-				    const pjsip_transport_t *tr)
-{
-    /* This is to detect alignment problems. */
-    pj_assert(sizeof(transport_key) == 8);
-
-    key->type = (pj_uint8_t)tr->type;
-    key->zero = 0;
-    key->addr = pj_sockaddr_in_get_addr(&tr->remote_addr).s_addr;
-    key->port = pj_sockaddr_in_get_port(&tr->remote_addr);
-    /*
-    if (key->port == 0) {
-	key->port = pj_sockaddr_in_get_port(&tr->local_addr);
-    }
-    */
-}
-
-#if PJ_HAS_TCP
-static void init_tcp_key(transport_key *key, pjsip_transport_type_e type,
-			 const pj_sockaddr_in *addr)
-{
-    /* This is to detect alignment problems. */
-    pj_assert(sizeof(transport_key) == 8);
-
-    key->type = (pj_uint8_t)type;
-    key->zero = 0;
-    key->addr = pj_sockaddr_in_get_addr(addr).s_addr;
-    key->port = pj_sockaddr_in_get_port(addr);
-}
-#endif
-
-static void init_udp_key(transport_key *key, pjsip_transport_type_e type,
-			  const pj_sockaddr_in *addr)
-{
-    PJ_UNUSED_ARG(addr);
-
-    /* This is to detect alignment problems. */
-    pj_assert(sizeof(transport_key) == 8);
-
-    pj_memset(key, 0, sizeof(*key));
-    key->type = (pj_uint8_t)type;
-
-#if 0	/* Not sure why we need to make 127.0.0.1 a special case */
-    if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) {
-	/* This looks more complicated than it is because key->addr uses
-	 * the host version of the address (i.e. converted with ntohl()).
-	 */
-	pj_str_t localaddr = pj_str("127.0.0.1");
-	pj_sockaddr_in addr;
-	pj_sockaddr_set_str_addr(&addr, &localaddr);
-	key->addr = pj_sockaddr_in_get_addr(&addr);
-    }
-#endif
-}
-
-/*
- * Get type format name (for pool name).
- */
-static const char *transport_get_name_format( int type )
-{
-    switch (type) {
-    case PJSIP_TRANSPORT_UDP:
-	return " udp%p";
-#if PJ_HAS_TCP
-    case PJSIP_TRANSPORT_TCP:
-	return " tcp%p";
-    case PJSIP_TRANSPORT_TLS:
-	return " tls%p";
-    case PJSIP_TRANSPORT_SCTP:
-	return "sctp%p";
-#endif
-    }
-    pj_assert(0);
-    return 0;
-}
-
-/*
- * Get the default SIP port number for the specified type.
- */
-PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
-{
-    return transport_names[type].port;
-}
-
-/*
- * Get transport name.
- */
-static const char *get_type_name(int type)
-{
-    return transport_names[type].name.ptr;
-}
-
-/*
- * Get transport type from name.
- */
-PJ_DEF(pjsip_transport_type_e) 
-pjsip_transport_get_type_from_name(const pj_str_t *name)
-{
-    unsigned i;
-
-    for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
-	if (pj_stricmp(name, &transport_names[i].name) == 0) {
-	    return transport_names[i].type;
-	}
-    }
-    return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-/*
- * Create new transmit buffer.
- */
-pj_status_t pjsip_tx_data_create( pjsip_transport_mgr *mgr,
-                                  pjsip_tx_data **p_tdata )
-{
-    pj_pool_t *pool;
-    pjsip_tx_data *tdata;
+
+/*****************************************************************************
+ *
+ * TRANSPORT
+ *
+ *****************************************************************************/
+
+static void transport_send_callback(pjsip_transport *transport,
+				    void *token,
+				    pj_status_t status)
+{
+    pjsip_tx_data *tdata = token;
+
+    PJ_UNUSED_ARG(transport);
+
+    /* Mark pending off so that app can resend/reuse txdata from inside
+     * the callback.
+     */
+    tdata->is_pending = 0;
+
+    /* Call callback, if any. */
+    if (tdata->cb) {
+	(*tdata->cb)(tdata->token, tdata, status);
+    }
+
+    /* Decrement reference count. */
+    pjsip_tx_data_dec_ref(tdata);
+}
+
+/*
+ * Send a SIP message using the specified transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_send(  pjsip_transport *tr, 
+					   pjsip_tx_data *tdata,
+					   const pj_sockaddr_in *addr,
+					   void *token,
+					   void (*cb)(void *token, 
+						      pjsip_tx_data *tdata,
+						      pj_status_t))
+{
     pj_status_t status;
 
-    PJ_LOG(5, ("", "pjsip_tx_data_create"));
-
-    PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
-
-    pool = pjsip_endpt_create_pool( mgr->endpt, "ptdt%p",
-				    PJSIP_POOL_LEN_TDATA,
-				    PJSIP_POOL_INC_TDATA );
-    if (!pool) {
-	return PJ_ENOMEM;
-    }
-    tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data));
-    tdata->pool = pool;
-    tdata->mgr = mgr;
-    pj_sprintf(tdata->obj_name,"txd%p", tdata);
-
-    status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
-    if (status != PJ_SUCCESS) {
-	pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
-	return status;
-    }
-    
-    *p_tdata = tdata;
-    return PJ_SUCCESS;
-}
-
-/*
- * Add reference to tx buffer.
- */
-PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
-{
-    pj_atomic_inc(tdata->ref_cnt);
-}
-
-/*
- * Decrease transport data reference, destroy it when the reference count
- * reaches zero.
- */
-PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
-{
-    pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
-    if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
-	PJ_LOG(6,(tdata->obj_name, "destroying txdata"));
-	pj_atomic_destroy( tdata->ref_cnt );
-	pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
-    }
-}
-
-/*
- * Invalidate the content of the print buffer to force the message to be
- * re-printed when sent.
- */
-PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
-{
-    tdata->buf.cur = tdata->buf.start;
-}
-
-/*
- * Get the transport type.
- */
-PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr)
-{
-    return tr->type;
-}
-
-/*
- * Get transport type from transport flag.
- */
-PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag)
-{
-#if PJ_HAS_TCP
-    if (flag & PJSIP_TRANSPORT_SECURE) {
-	return PJSIP_TRANSPORT_TLS;
-    } else if (flag & PJSIP_TRANSPORT_RELIABLE) {
-	return PJSIP_TRANSPORT_TCP;
-    } else 
-#else
-    PJ_UNUSED_ARG(flag);
-#endif
-    {
-	return PJSIP_TRANSPORT_UDP;
-    }
-}
-
-/*
- * Get the transport type name.
- */
-PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr)
-{
-    return get_type_name(tr->type);
-}
-
-/*
- * Get the transport's object name.
- */
-PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr )
-{
-    return tr->obj_name;
-}
-
-/*
- * Get the transport's reference counter.
- */
-PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr )
-{
-    return pj_atomic_get(tr->ref_cnt);
-}
-
-/*
- * Get transport local address.
- */
-PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr )
-{
-    return &tr->local_addr;
-}
-
-/*
- * Get address name.
- */
-PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr)
-{
-    return &tr->addr_name;
-}
-
-/*
- * Get transport remote address.
- */
-PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr )
-{
-    return &tr->remote_addr;
-}
-
-/*
- * Get transport flag.
- */
-PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr )
-{
-    return tr->flag;
-}
-
-/*
- * Add reference to the specified transport.
- */
-PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr )
-{
-    pj_atomic_inc(tr->ref_cnt);
-}
-
-/*
- * Decrease the reference time of the transport.
- */
-PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr )
-{
-    pj_assert(tr->ref_cnt > 0);
-    if (pj_atomic_dec_and_get(tr->ref_cnt) == 0) {
-	pj_gettimeofday(&tr->close_time);
-	tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT;
-    }
-}
-
-/*
- * Open the underlying transport.
- */
-static pj_status_t create_socket( pjsip_transport_type_e type,
-				  pj_sockaddr_in *local,
-                                  pj_sock_t *p_sock)
-{
-    int sock_family;
-    int sock_type;
-    int sock_proto;
-    int len;
-    pj_status_t status;
-    pj_sock_t sock;
-
-    /* Set socket parameters */
-    if (type == PJSIP_TRANSPORT_UDP) {
-	sock_family = PJ_AF_INET;
-	sock_type = PJ_SOCK_DGRAM;
-	sock_proto = 0;
-
-#if PJ_HAS_TCP
-    } else if (type == PJSIP_TRANSPORT_TCP) {
-	sock_family = PJ_AF_INET;
-	sock_type = PJ_SOCK_STREAM;
-	sock_proto = 0;
-#endif
-    } else {
-	return PJ_EINVAL;
-    }
-
-    /* Create socket. */
-    status = pj_sock_socket( sock_family, sock_type, sock_proto, &sock);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Bind the socket to the requested address, or if no address is
-     * specified, let the operating system chooses the address.
-     */
-    if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) {
-	/* Bind to the requested address. */
-        status = pj_sock_bind(sock, local, sizeof(*local));
-	if (status != PJ_SUCCESS) {
-	    pj_sock_close(sock);
-	    return status;
-	}
-    } else if (type == PJSIP_TRANSPORT_UDP) {
-	/* Only for UDP sockets: bind to any address so that the operating 
-	 * system allocates the port for us. For TCP, let the OS implicitly
-	 * bind the socket with connect() syscall (if we bind now, then we'll
-	 * get 0.0.0.0 as local address).
-	 */
-	pj_memset(local, 0, sizeof(*local));
-	local->sin_family = PJ_AF_INET;
-        status = pj_sock_bind(sock, local, sizeof(*local));
-	if (status != PJ_SUCCESS) {
-	    pj_sock_close(sock);
-	    return status;
-	}
-
-	/* Get the local address. */
-	len = sizeof(pj_sockaddr_in);
-        status = pj_sock_getsockname(sock, local, &len);
-	if (status != PJ_SUCCESS) {
-	    pj_sock_close(sock);
-	    return status;
-	}
-    }
-
-    *p_sock = sock;
-    return PJ_SUCCESS;
-}
-
-/*
- * Close the transport.
- */
-static void destroy_socket( pjsip_transport_t * tr)
-{
-    pj_assert( pj_atomic_get(tr->ref_cnt) == 0);
-    pj_sock_close(tr->sock);
-    tr->sock = -1;
-}
-
-/*
- * Create a new transport object.
- */
-static pj_status_t create_transport( pjsip_transport_mgr *mgr,
-				     pjsip_transport_type_e type,
-				     pj_sock_t sock_hnd,
-				     const pj_sockaddr_in *local_addr,
-				     const pj_sockaddr_in *addr_name,
-				     pjsip_transport_t **p_transport )
-{
-    pj_pool_t *tr_pool=NULL, *rdata_pool=NULL;
-    pjsip_transport_t *tr = NULL;
-    pj_status_t status;
-
-    /* Allocate pool for transport from endpoint. */
-    tr_pool = pjsip_endpt_create_pool( mgr->endpt,
-				       transport_get_name_format(type),
-				       PJSIP_POOL_LEN_TRANSPORT,
-				       PJSIP_POOL_INC_TRANSPORT );
-    if (!tr_pool) {
-	status = PJ_ENOMEM;
-	goto on_error;
-    }
-
-    /* Allocate pool for rdata from endpoint. */
-    rdata_pool = pjsip_endpt_create_pool( mgr->endpt,
-					     "prdt%p",
-					     PJSIP_POOL_LEN_RDATA,
-					     PJSIP_POOL_INC_RDATA );
-    if (!rdata_pool) {
-	status = PJ_ENOMEM;
-	goto on_error;
-    }
-
-    /* Allocate and initialize the transport. */
-    tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr));
-    tr->pool = tr_pool;
-    tr->type = type;
-    tr->mgr = mgr;
-    tr->sock = sock_hnd;
-    pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in));
-    pj_list_init(&tr->cb_list);
-    pj_sprintf(tr->obj_name, transport_get_name_format(type), tr);
-
-    if (type != PJSIP_TRANSPORT_UDP) {
-	tr->flag |= PJSIP_TRANSPORT_RELIABLE;
-    }
-
-    /* Address name. */
-    if (addr_name == NULL) {
-	addr_name = &tr->local_addr;
-    } 
-    pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name));
-
-    /* Create atomic */
-    status = pj_atomic_create(tr_pool, 0, &tr->ref_cnt);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-    
-    /* Init rdata in the transport. */
-    tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata));
-    tr->rdata->pool = rdata_pool;
-    tr->rdata->len = 0;
-    tr->rdata->transport = tr;
-    
-    /* Init transport mutex. */
-    status = pj_mutex_create_recursive(tr_pool, "mtr%p", &tr->tr_mutex);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Register to I/O Queue */
-    status = pj_ioqueue_register_sock( tr_pool, mgr->ioqueue, 
-				       tr->sock, tr,
-				       &ioqueue_transport_callback,
-				       &tr->key);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    *p_transport = tr;
-    return PJ_SUCCESS;
-
-on_error:
-    if (tr && tr->tr_mutex) {
-	pj_mutex_destroy(tr->tr_mutex);
-    }
-    if (tr_pool) {
-	pjsip_endpt_destroy_pool(mgr->endpt, tr_pool);
-    }
-    if (rdata_pool) {
-	pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool);
-    }
-    return status;
-}
-
-/*
- * Destroy transport.
- */
-static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr)
-{
-    transport_key hash_key;
-
-    /* Remove from I/O queue. */
-    pj_ioqueue_unregister( tr->key );
-
-    /* Remove from hash table */
-    init_key_from_transport(&hash_key, tr);
-    pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL);
-
-    /* Close transport. */
-    destroy_socket(tr);
-
-    /* Destroy the transport mutex. */
-    pj_mutex_destroy(tr->tr_mutex);
-
-    /* Destroy atomic */
-    pj_atomic_destroy( tr->ref_cnt );
-
-    /* Release the pool associated with the rdata. */
-    pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool );
-
-    /* Release the pool associated with the transport. */
-    pjsip_endpt_destroy_pool(mgr->endpt, tr->pool );
-}
-
-
-static pj_status_t transport_send_msg( pjsip_transport_t *tr, 
-				       pjsip_tx_data *tdata,
-				       const pj_sockaddr_in *addr, 
-				       pj_ssize_t *p_sent)
-{
-    const char *buf = tdata->buf.start;
-    pj_ssize_t size;
-    pj_status_t status;
-
-    /* Can only send if tdata is not being sent! */
-    if (pj_ioqueue_is_pending(tr->key, &tdata->op_key))
+    PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL);
+
+    /* Is it currently being sent? */
+    if (tdata->is_pending) {
+	pj_assert(!"Invalid operation step!");
 	return PJSIP_EPENDINGTX;
+    }
 
     /* Allocate buffer if necessary. */
@@ -721,6 +295,8 @@
     }
 
-    /* Print the message if it's not printed */
-    if (tdata->buf.cur <= tdata->buf.start) {
+    /* Do we need to reprint? */
+    if (!pjsip_tx_data_is_valid(tdata)) {
+	pj_ssize_t size;
+
 	size = pjsip_msg_print( tdata->msg, tdata->buf.start, 
 			        tdata->buf.end - tdata->buf.start);
@@ -733,120 +309,285 @@
     }
 
-    /* Send the message. */
-    buf = tdata->buf.start;
-    size = tdata->buf.cur - tdata->buf.start;
-
-    if (tr->type == PJSIP_TRANSPORT_UDP) {
-	PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n"
-		  "----------- begin msg ------------\n"
-		  "%s"
-		  "------------ end msg -------------",
-		  pj_inet_ntoa(addr->sin_addr), 
-		  pj_sockaddr_in_get_port(addr),
-		  size, buf));
-
-	status = pj_ioqueue_sendto( tr->key, &tdata->op_key,
-				    buf, &size, 0, addr, sizeof(*addr));
-    } 
-#if PJ_HAS_TCP
-    else {
-	PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n"
-		  "----------- begin msg ------------\n"
-		  "%s"
-		  "------------ end msg -------------",
-		  size, buf));
-
-	status = pj_ioqueue_send(tr->key, &tdata->op_key, buf, &size, 0);
-    }
-#else
-    else {
-	pj_assert(!"Unsupported transport");
-	status = PJSIP_EUNSUPTRANSPORT;
-    }
-#endif
-
-    *p_sent = size;
+    /* Save callback data. */
+    tdata->token = token;
+    tdata->cb = cb;
+
+    /* Add reference counter. */
+    pjsip_tx_data_add_ref(tdata);
+
+    /* Mark as pending. */
+    tdata->is_pending = 1;
+
+    /* Send to transport. */
+    status = (*tr->send_msg)(tr, tdata->buf.start, 
+			     tdata->buf.cur - tdata->buf.start,
+			     &tdata->op_key,
+			     addr, tdata, &transport_send_callback);
+
+    if (status != PJ_EPENDING) {
+	tdata->is_pending = 0;
+	pjsip_tx_data_dec_ref(tdata);
+    }
+
     return status;
 }
 
-/*
- * Send a SIP message using the specified transport, to the address specified
- * in the outgoing data.
- */
-PJ_DEF(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr,
-					      pjsip_tx_data *tdata,
-					      const pj_sockaddr_in *addr,
-					      pj_ssize_t *sent)
-{
-    PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name));
-
-    return transport_send_msg(tr, tdata, addr, sent );
-}
-
-///////////////////////////////////////////////////////////////////////////////
+static void transport_idle_callback(pj_timer_heap_t *timer_heap,
+				    struct pj_timer_entry *entry)
+{
+    pjsip_transport *tp = entry->user_data;
+    pj_assert(tp != NULL);
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    entry->id = PJ_FALSE;
+    pjsip_transport_unregister(tp->tpmgr, tp);
+}
+
+/*
+ * Add ref.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp )
+{
+    PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
+
+    if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) {
+	pj_lock_acquire(tp->tpmgr->lock);
+	/* Verify again. */
+	if (pj_atomic_get(tp->ref_cnt) == 1) {
+	    if (tp->idle_timer.id != PJ_FALSE) {
+		pj_timer_heap_cancel(tp->tpmgr->timer_heap, &tp->idle_timer);
+		tp->idle_timer.id = PJ_FALSE;
+	    }
+	}
+	pj_lock_release(tp->tpmgr->lock);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Dec ref.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
+{
+    PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
+
+    pj_assert(pj_atomic_get(tp->ref_cnt) > 0);
+
+    if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) {
+	pj_lock_acquire(tp->tpmgr->lock);
+	/* Verify again. */
+	if (pj_atomic_get(tp->ref_cnt) == 0) {
+	    pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 };
+
+	    pj_assert(tp->idle_timer.id == 0);
+	    tp->idle_timer.id = PJ_TRUE;
+	    pj_timer_heap_schedule(tp->tpmgr->timer_heap, &tp->idle_timer, &delay);
+	}
+	pj_lock_release(tp->tpmgr->lock);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Register a transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
+					      pjsip_transport *tp )
+{
+    transport_key key;
+
+    /* Init. */
+    tp->tpmgr = mgr;
+    pj_memset(&tp->idle_timer, 0, sizeof(tp->idle_timer));
+    tp->idle_timer.user_data = tp;
+    tp->idle_timer.cb = &transport_idle_callback;
+
+    /* 
+     * Register to hash table.
+     */
+    key.type = (pj_uint8_t)tp->type;
+    key.zero = 0;
+    key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
+    key.port = pj_ntohs(tp->rem_addr.sin_port);
+
+    pj_lock_acquire(mgr->lock);
+    pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), tp);
+    pj_lock_release(mgr->lock);
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Unregister transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
+						pjsip_transport *tp)
+{
+    transport_key key;
+
+    PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY);
+
+    pj_lock_acquire(tp->lock);
+    pj_lock_acquire(mgr->lock);
+
+    /*
+     * Unregister timer, if any.
+     */
+    pj_assert(tp->idle_timer.id == PJ_FALSE);
+    if (tp->idle_timer.id != PJ_FALSE) {
+	pj_timer_heap_cancel(mgr->timer_heap, &tp->idle_timer);
+	tp->idle_timer.id = PJ_FALSE;
+    }
+
+    /*
+     * Unregister from hash table.
+     */
+    key.type = (pj_uint8_t)tp->type;
+    key.zero = 0;
+    key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
+    key.port = pj_ntohs(tp->rem_addr.sin_port);
+
+    pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), NULL);
+
+    pj_lock_release(mgr->lock);
+
+    /* Destroy. */
+    return tp->destroy(tp);
+}
+
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT FACTORY
+ *
+ *****************************************************************************/
+
+
+PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr,
+						    pjsip_tpfactory *tpf)
+{
+    pjsip_tpfactory *p;
+    pj_status_t status;
+
+    pj_lock_acquire(mgr->lock);
+
+    /* Check that no factory with the same type has been registered. */
+    status = PJ_SUCCESS;
+    for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) {
+	if (p->type == tpf->type) {
+	    status = PJSIP_ETYPEEXISTS;
+	    break;
+	}
+	if (p == tpf) {
+	    status = PJ_EEXISTS;
+	    break;
+	}
+    }
+
+    if (status != PJ_SUCCESS) {
+	pj_lock_release(mgr->lock);
+	return status;
+    }
+
+    pj_list_insert_before(&mgr->factory_list, tpf);
+
+    pj_lock_release(mgr->lock);
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Unregister factory.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr,
+						      pjsip_tpfactory *tpf)
+{
+    pj_lock_acquire(mgr->lock);
+
+    pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf);
+    pj_list_erase(tpf);
+
+    pj_lock_release(mgr->lock);
+
+    return PJ_SUCCESS;
+}
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT MANAGER
+ *
+ *****************************************************************************/
 
 /*
  * Create a new transport manager.
  */
-PJ_DEF(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool,
-						pjsip_endpoint * endpt,
-						void (*cb)(pjsip_endpoint*,
-							   pjsip_rx_data *),
-						pjsip_transport_mgr **p_mgr)
-{
-    pjsip_transport_mgr *mgr;
+PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
+					pjsip_endpoint *endpt,
+					pj_ioqueue_t *ioqueue,
+					pj_timer_heap_t *timer_heap,
+					void (*cb)(pjsip_endpoint*,
+						   pj_status_t,
+						   pjsip_rx_data *),
+					pjsip_tpmgr **p_mgr)
+{
+    pjsip_tpmgr *mgr;
     pj_status_t status;
 
-    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()"));
-
-    mgr = pj_pool_alloc(pool, sizeof(*mgr));
+    PJ_ASSERT_RETURN(pool && endpt && cb && p_mgr, PJ_EINVAL);
+
+    PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_create()"));
+
+    mgr = pj_pool_zalloc(pool, sizeof(*mgr));
     mgr->endpt = endpt;
-    mgr->message_callback = cb;
-    mgr->send_buf_size = DEFAULT_SO_SNDBUF;
-    mgr->recv_buf_size = DEFAULT_SO_RCVBUF;
-
-    mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE);
-    if (!mgr->transport_table) {
+    mgr->msg_cb = cb;
+    mgr->ioqueue = ioqueue;
+    mgr->timer_heap = timer_heap;
+    pj_list_init(&mgr->factory_list);
+
+    mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE);
+    if (!mgr->table)
 	return PJ_ENOMEM;
-    }
-    status = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS, &mgr->ioqueue);
-    if (status != PJ_SUCCESS) {
+
+    status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock);
+    if (status != PJ_SUCCESS)
 	return status;
-    }
-    status = pj_mutex_create_recursive(pool, "tmgr%p", &mgr->mutex);
-    if (status != PJ_SUCCESS) {
-	pj_ioqueue_destroy(mgr->ioqueue);
-	return status;
-    }
-    pj_gettimeofday(&mgr->next_idle_check);
-    mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
 
     *p_mgr = mgr;
-    return status;
-}
-
-/*
+    return PJ_SUCCESS;
+}
+
+/*
+ * pjsip_tpmgr_destroy()
+ *
  * Destroy transport manager.
  */
-PJ_DEF(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr )
+PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
 {
     pj_hash_iterator_t itr_val;
     pj_hash_iterator_t *itr;
     
-    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()"));
-
-    pj_mutex_lock(mgr->mutex);
-
-    itr = pjsip_transport_first(mgr, &itr_val);
+    PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()"));
+
+    pj_lock_acquire(mgr->lock);
+
+    itr = pj_hash_first(mgr->table, &itr_val);
     while (itr != NULL) {
 	pj_hash_iterator_t *next;
-	pjsip_transport_t *transport;
+	pjsip_transport *transport;
 	
-	transport = pjsip_transport_this(mgr, itr);
-
-	next = pjsip_transport_next(mgr, itr);
+	transport = pj_hash_this(mgr->table, itr);
+
+	next = pj_hash_next(mgr->table, itr);
 
 	pj_atomic_set(transport->ref_cnt, 0);
-	destroy_transport( mgr, transport);
+	pjsip_transport_unregister(mgr, transport);
 
 	itr = next;
@@ -854,615 +595,112 @@
     pj_ioqueue_destroy(mgr->ioqueue);
 
-    pj_mutex_unlock(mgr->mutex);
+    pj_lock_release(mgr->lock);
 
     return PJ_SUCCESS;
 }
 
-/*
- * Create listener
- */
-static pj_status_t create_listener( pjsip_transport_mgr *mgr,
-				    pjsip_transport_type_e type,
-				    pj_sock_t sock_hnd,
-				    pj_sockaddr_in *local_addr,
-				    const pj_sockaddr_in *addr_name)
-{
-    pjsip_transport_t *tr;
-    struct transport_key *hash_key;
-    const pj_str_t loopback_addr = { "127.0.0.1", 9 };
-    pj_status_t status;
-
-    if (mgr->send_buf_size != 0) {
-        int opt_val = mgr->send_buf_size;
-        status = pj_sock_setsockopt( sock_hnd, PJ_SOL_SOCKET, 
-                                     PJ_SO_SNDBUF, 
-                                     &opt_val, sizeof(opt_val));
-
-        if (status != PJ_SUCCESS) {
-	    return status;
-        }
-    }
-
-    if (mgr->recv_buf_size != 0) {
-        int opt_val = mgr->recv_buf_size;
-        status = pj_sock_setsockopt( sock_hnd, PJ_SOL_SOCKET, 
-                                     PJ_SO_RCVBUF, 
-                                     &opt_val, sizeof(opt_val));
-        if (status != PJ_SUCCESS) {
-	    return status;
-        }
-    }
-
-    status = create_transport(mgr, type, sock_hnd, local_addr, addr_name, &tr);
-    if (status != PJ_SUCCESS) {
-	pj_sock_close(sock_hnd);
-	return status;
-    }
-#if PJ_HAS_TCP
-    if (type == PJSIP_TRANSPORT_TCP) {
-
-	status = pj_sock_listen(tr->sock, BACKLOG);
-	if (status != 0) {
-	    destroy_transport(mgr, tr);
-	    return status;
-	}
-	
-	/* Discard immediate connections. */
-	do {
-	    tr->accept_data.addrlen = sizeof(tr->accept_data.local);
-	    status = pj_ioqueue_accept(tr->key, &tr->accept_op,
-				       &tr->accept_data.sock, 
-				       &tr->accept_data.local, 
-				       &tr->accept_data.remote,
-				       &tr->accept_data.addrlen);
-	    if (status==PJ_SUCCESS) {
-		pj_sock_close(tr->accept_data.sock);
-	    } else if (status != PJ_EPENDING) {
-		destroy_transport(mgr, tr);
-		return status;
-	    }
-	} while (status==PJ_SUCCESS);
-
-    } else 
-#endif
-    if (type == PJSIP_TRANSPORT_UDP) {
-	pj_ssize_t bytes;
-
-	/* Discard immediate data. */
-	do {
-	    tr->rdata->addr_len = sizeof(tr->rdata->addr);
-	    bytes = PJSIP_MAX_PKT_LEN;
-	    status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
-					  tr->rdata->packet, &bytes, 0,
-					  &tr->rdata->addr, 
-					  &tr->rdata->addr_len);
-	    if (status == PJ_SUCCESS) {
-		;
-	    } else if (status != PJ_EPENDING) {
-		destroy_transport(mgr, tr);
-		return status;
-	    }
-	} while (status == PJ_SUCCESS);
-    }
-
-    pj_atomic_set(tr->ref_cnt, 1);
-
-    /* Listeners normally have no remote address */
-    pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr));
-
-    /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1. 
-     * See further comments on struct pjsip_transport_t definition.
+
+/*
+ * pjsip_tpmgr_receive_packet()
+ *
+ * Called by tranports when they receive a new packet.
+ */
+PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
+					       pjsip_rx_data *rdata)
+{
+    pjsip_transport *tr = rdata->tp_info.transport;
+    pj_str_t s;
+
+    char *current_pkt;
+    pj_size_t remaining_len;
+    pj_size_t total_processed = 0;
+
+    /* Check size. */
+    pj_assert(rdata->pkt_info.len > 0);
+    if (rdata->pkt_info.len <= 0)
+	return -1;
+    
+    current_pkt = rdata->pkt_info.packet;
+    remaining_len = rdata->pkt_info.len;
+    
+    /* Must NULL terminate buffer. This is the requirement of the 
+     * parser etc. 
      */
-    if (type == PJSIP_TRANSPORT_UDP && 
-	local_addr->sin_addr.s_addr == pj_inet_addr(&loopback_addr).s_addr)
-    {
-	pj_str_t localaddr = pj_str("127.0.0.1");
-	pj_sockaddr_in_set_str_addr( &tr->remote_addr, &localaddr);
-    }
-    hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
-    init_key_from_transport(hash_key, tr);
-
-    pj_mutex_lock(mgr->mutex);
-    pj_hash_set(tr->pool, mgr->transport_table, 
-		hash_key, sizeof(transport_key), tr);
-    pj_mutex_unlock(mgr->mutex);
-
-    PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d", 
-			 get_type_name(tr->type), 
-			 pj_inet_ntoa(tr->local_addr.sin_addr),
-			 pj_sockaddr_in_get_port(&tr->local_addr)));
-    PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d", 
-			 get_type_name(tr->type), 
-			 pj_inet_ntoa(tr->addr_name.sin_addr),
-			 pj_sockaddr_in_get_port(&tr->addr_name)));
-    return PJ_SUCCESS;
-}
-
-/*
- * Create listener.
- */
-PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
-					   pjsip_transport_type_e type,
-					   pj_sockaddr_in *local_addr,
-					   const pj_sockaddr_in *addr_name)
-{
-    pj_sock_t sock_hnd;
-    pj_status_t status;
-
-    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type));
-
-    status = create_socket(type, local_addr, &sock_hnd);
-    if (status != PJ_SUCCESS) {
-	return status;
-    }
-
-    return create_listener(mgr, type, sock_hnd, local_addr, addr_name);
-}
-
-/*
- * Create UDP listener.
- */
-PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
-					       pj_sock_t sock,
-					       const pj_sockaddr_in *addr_name)
-{
-    pj_sockaddr_in local_addr;
-    pj_status_t status;
-    int addrlen = sizeof(local_addr);
-
-    status = pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    return create_listener(mgr, PJSIP_TRANSPORT_UDP, sock, 
-			   &local_addr, addr_name);
-}
-
-/*
- * Find transport to be used to send message to remote destination. If no
- * suitable transport is found, a new one will be created.
- */
-PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
-			          pj_pool_t *pool,
-				  pjsip_transport_type_e type,
-				  const pj_sockaddr_in *remote,
-				  void *token,
-				  pjsip_transport_completion_callback *cb)
-{
-    transport_key search_key, *hash_key;
-    pjsip_transport_t *tr;
-    pj_sockaddr_in local;
-    pj_sock_t sock_hnd;
-    pj_status_t status;
-    struct transport_callback *cb_rec;
-
-    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()"));
-
-    /* Create the callback record.
-     */
-    cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec));
-    cb_rec->token = token;
-    cb_rec->cb = cb;
-
-    /* Create key for hash table look-up.
-     * The key creation is different for TCP and UDP.
-     */
-#if PJ_HAS_TCP
-    if (type==PJSIP_TRANSPORT_TCP) {
-	init_tcp_key(&search_key, type, remote);
-    } else 
-#endif
-    if (type==PJSIP_TRANSPORT_UDP) {
-	init_udp_key(&search_key, type, remote);
-    }
-
-    /* Start lock the manager. */
-    pj_mutex_lock(mgr->mutex);
-
-    /* Lookup the transport in the hash table. */
-    tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key));
-
-    if (tr) {
-	/* Transport found. If the transport is still busy (i.e. connecting
-	 * is in progress), then just register the callback. Otherwise
-	 * report via the callback if callback is specified. 
+    current_pkt[remaining_len] = '\0';
+
+    /* Process all message fragments. */
+    while (total_processed < remaining_len) {
+
+	pjsip_msg *msg;
+	pj_size_t msg_fragment_size = 0;
+
+	/* Initialize default fragment size. */
+	msg_fragment_size = remaining_len;
+
+	/* Null terminate packet. */
+
+	/* Clear and init msg_info in rdata. 
+	 * Endpoint might inspect the values there when we call the callback
+	 * to report some errors.
 	 */
-	pj_mutex_unlock(mgr->mutex);
-	pj_mutex_lock(tr->tr_mutex);
-
-	if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) {
-	    /* Transport is busy. Just register the callback. */
-	    pj_list_insert_before(&tr->cb_list, cb_rec);
-
-	} else {
-	    /* Transport is ready. Call callback now.
-	     */
-	    (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
-	}
-	pj_mutex_unlock(tr->tr_mutex);
-
-	return;
-    }
-
-
-    /* Transport not found. Create new one. */
-    pj_memset(&local, 0, sizeof(local));
-    local.sin_family = PJ_AF_INET;
-    status = create_socket(type, &local, &sock_hnd);
-    if (status != PJ_SUCCESS) {
-	pj_mutex_unlock(mgr->mutex);
-	(*cb_rec->cb)(NULL, cb_rec->token, status);
-	return;
-    }
-    status = create_transport(mgr, type, sock_hnd, &local, NULL, &tr);
-    if (status != PJ_SUCCESS) {
-	pj_mutex_unlock(mgr->mutex);
-	(*cb_rec->cb)(NULL, cb_rec->token, status);
-	return;
-    }
-
-#if PJ_HAS_TCP
-    if (type == PJSIP_TRANSPORT_TCP) {
-	pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in));
-	status = pj_ioqueue_connect(tr->key, &tr->remote_addr, 
-				    sizeof(pj_sockaddr_in));
-	pj_assert(status != 0);
-	if (status != PJ_EPENDING) {
-	    PJ_TODO(HANDLE_IMMEDIATE_CONNECT);
-	    destroy_transport(mgr, tr);
-	    pj_mutex_unlock(mgr->mutex);
-	    (*cb_rec->cb)(NULL, cb_rec->token, status);
-	    return;
-	}
-    } else 
-#endif
-    if (type == PJSIP_TRANSPORT_UDP) {
-	pj_ssize_t size;
-
-	do {
-	    tr->rdata->addr_len = sizeof(tr->rdata->addr);
-	    size = PJSIP_MAX_PKT_LEN;
-	    status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
-					  tr->rdata->packet, &size, 0,
-					  &tr->rdata->addr, 
-					  &tr->rdata->addr_len);
-	    if (status == PJ_SUCCESS)
-		;
-	    else if (status != PJ_EPENDING) {
-		destroy_transport(mgr, tr);
-		pj_mutex_unlock(mgr->mutex);
-		(*cb_rec->cb)(NULL, cb_rec->token, status);
-		return;
-	    }
-
-	    /* Bug here.
-	     * If data is immediately available, although not likely, it will
-	     * be dropped because we don't expect to have data right after
-	     * the socket is created, do we ?!
-	     */
-	    PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA);
-
-	} while (status == PJ_SUCCESS);
-
-	//Bug: cb will never be called!
-	//     Must force status to PJ_SUCCESS;
-	//status = PJ_IOQUEUE_PENDING;
-
-	status = PJ_SUCCESS;
-
-    } else {
-	pj_mutex_unlock(mgr->mutex);
-	(*cb_rec->cb)(NULL, cb_rec->token, PJSIP_EUNSUPTRANSPORT);
-	return;
-    }
-
-    pj_assert(status==PJ_EPENDING || status==PJ_SUCCESS);
-    pj_mutex_lock(tr->tr_mutex);
-    hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
-    pj_memcpy(hash_key, &search_key, sizeof(transport_key));
-    pj_hash_set(tr->pool, mgr->transport_table, 
-		hash_key, sizeof(transport_key), tr);
-    if (status == PJ_SUCCESS) {
-	pj_mutex_unlock(tr->tr_mutex);
-	pj_mutex_unlock(mgr->mutex);
-	(*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
-    } else {
-	pj_list_insert_before(&tr->cb_list, cb_rec);
-	pj_mutex_unlock(tr->tr_mutex);
-	pj_mutex_unlock(mgr->mutex);
-    }
-
-}
-
-#if PJ_HAS_TCP
-/*
- * Handle completion of asynchronous accept() operation.
- * This function is called by handle_events() function.
- */
-static void handle_new_connection( pjsip_transport_mgr *mgr,
-				   pjsip_transport_t *listener,
-				   pj_status_t status )
-{
-    pjsip_transport_t *tr;
-    transport_key *hash_key;
-    pj_ssize_t size;
-
-    pj_assert (listener->type == PJSIP_TRANSPORT_TCP);
-
-    if (status != PJ_SUCCESS) {
-	PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
-			       "Error in accept() completion"));
-	goto on_return;
-    }
-
-    PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d",
-	      pj_inet_ntoa(listener->accept_data.remote.sin_addr),
-	      pj_sockaddr_in_get_port(&listener->accept_data.remote)));
-
-    status = create_transport(mgr, listener->type, 
-			      listener->accept_data.sock,
-			      &listener->accept_data.local,
-			      NULL, &tr);
-    if (status != PJ_SUCCESS) {
-	PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
-			       "Error in creating new incoming TCP"));
-	goto on_return;
-    }
-
-    /*
-    tr->rdata->addr_len = sizeof(tr->rdata->addr);
-    status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, 
-				  tr->rdata->packet, PJSIP_MAX_PKT_LEN,
-				  &tr->rdata->addr, 
-				  &tr->rdata->addr_len);
-    */
-    tr->rdata->addr = listener->accept_data.remote;
-    tr->rdata->addr_len = listener->accept_data.addrlen;
-
-    size = PJSIP_MAX_PKT_LEN;
-    status = pj_ioqueue_recv(tr->key, &tr->rdata->op_key, 
-			     tr->rdata->packet, &size, 0);
-    if (status != PJ_EPENDING) {
-	PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
-			       "Error in receiving data"));
-	PJ_TODO(IMMEDIATE_DATA);
-	destroy_transport(mgr, tr);
-	goto on_return;
-    }
-
-    pj_memcpy(&tr->remote_addr, &listener->accept_data.remote, 
-	      listener->accept_data.addrlen);
-    hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
-    init_key_from_transport(hash_key, tr);
-
-    pj_mutex_lock(mgr->mutex);
-    pj_hash_set(tr->pool, mgr->transport_table, hash_key, 
-		sizeof(transport_key), tr);
-    pj_mutex_unlock(mgr->mutex);
-
-on_return:
-    /* Re-initiate asynchronous accept() */
-    listener->accept_data.addrlen = sizeof(listener->accept_data.local);
-    status = pj_ioqueue_accept(listener->key, &listener->accept_op,
-			       &listener->accept_data.sock, 
-			       &listener->accept_data.local, 
-			       &listener->accept_data.remote,
-			       &listener->accept_data.addrlen);
-    if (status != PJ_EPENDING) {
-	PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
-			       "Error in receiving data"));
-	PJ_TODO(IMMEDIATE_ACCEPT);
-	return;
-    }
-}
-
-/*
- * Handle completion of asynchronous connect() function.
- * This function is called by the handle_events() function.
- */
-static void handle_connect_completion( pjsip_transport_mgr *mgr,
-				       pjsip_transport_t *tr,
-				       pj_status_t status )
-{
-    struct transport_callback new_list;
-    struct transport_callback *cb_rec;
-    pj_ssize_t recv_size;
-
-    PJ_UNUSED_ARG(mgr);
-
-    /* On connect completion, we must call all registered callbacks in
-     * the transport.
-     */
-
-    /* Initialize new list. */
-    pj_list_init(&new_list);
-
-    /* Hold transport's mutex. We don't want other thread to register a 
-     * callback while we're dealing with it. 
-    */
-    pj_mutex_lock(tr->tr_mutex);
-
-    /* Copy callback list to new list so that we can call the callbacks
-     * without holding the mutex.
-     */
-    pj_list_merge_last(&new_list, &tr->cb_list);
-
-    /* Clear transport's busy flag. */
-    tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY;
-
-    /* If success, update local address. 
-     * Local address is only available after connect() has returned.
-     */
-    if (status == PJ_SUCCESS) {
-	int addrlen = sizeof(tr->local_addr);
-	
-	status = pj_sock_getsockname(tr->sock, 
-				     (pj_sockaddr_t*)&tr->local_addr, 
-				     &addrlen);
-	if (status == PJ_SUCCESS) {
-	    pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name));
-	}
-    }
-
-    /* Unlock mutex. */
-    pj_mutex_unlock(tr->tr_mutex);
-
-    /* Call all registered callbacks. */
-    cb_rec = new_list.next;
-    while (cb_rec != &new_list) {
-	struct transport_callback *next;
-	next = cb_rec->next;
-	(*cb_rec->cb)(tr, cb_rec->token, status);
-	cb_rec = next;
-    }
-
-    /* Success? */
-    if (status != PJ_SUCCESS) {
-	destroy_transport(mgr, tr);
-	PJ_TODO(WTF);
-	return;
-    }
-
-    /* Initiate read operation to socket. */
-    recv_size = PJSIP_MAX_PKT_LEN;
-    status = pj_ioqueue_recv( tr->key, &tr->rdata->op_key, tr->rdata->packet, 
-			      &recv_size, 0);
-    if (status != PJ_EPENDING) {
-	destroy_transport(mgr, tr);
-	PJ_TODO(IMMEDIATE_DATA);
-	return;
-    }
-}
-#endif /* PJ_HAS_TCP */
-
-/*
- * Handle incoming data.
- * This function is called when the transport manager receives 'notification'
- * from the I/O Queue that the receive operation has completed.
- * This function will then attempt to parse the message, and hands over the
- * message to the endpoint.
- */
-static void handle_received_data( pjsip_transport_mgr *mgr,
-				  pjsip_transport_t *tr,
-				  pj_ssize_t size )
-{
-    pjsip_msg *msg;
-    pjsip_rx_data *rdata = tr->rdata;
-    pj_pool_t *rdata_pool;
-    pjsip_hdr *hdr;
-    pj_str_t s;
-    char *src_addr;
-    int src_port;
-    pj_size_t msg_fragment_size = 0;
-
-    /* Check size. */
-    if (size < 1) {
-	if (tr->type != PJSIP_TRANSPORT_UDP) {
-	    /* zero bytes indicates transport has been closed for TCP.
-	     * But alas, we can't destroy it now since transactions may still
-	     * have reference to it. In that case, just do nothing, the 
-	     * transaction will receive error when it tries to send anything.
-	     * But alas!! UAC transactions wont send anything!!.
-	     * So this is a bug!
-	     */
-	    if (pj_atomic_get(tr->ref_cnt)==0) {
-		PJ_LOG(4,(tr->obj_name, "connection closed"));
-		destroy_transport(mgr, tr);
-	    } else {
-		PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED);
-		//PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED);
-	    }
-	    return;
-	} else {
-	    /* On Windows machines, UDP recv() will return zero upon receiving
-	     * ICMP port unreachable message.
-	     */
-	    PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)"));
-	    goto on_return;
-	}
-    }
-    
-    /* Save received time. */
-    pj_gettimeofday(&rdata->timestamp);
-    
-    /* Update length. */
-    rdata->len += size;
-
-    /* Null terminate packet, this is the requirement of the parser. */
-    rdata->packet[rdata->len] = '\0';
-
-    /* Get source address and port for logging purpose. */
-    src_addr = pj_inet_ntoa(rdata->addr.sin_addr);
-    src_port = pj_sockaddr_in_get_port(&rdata->addr);
-
-    /* Print the whole data to the log. */
-    PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n"
-		    "----------- begin msg ------------\n"
-		    "%s"
-		    "------------ end msg -------------", 
-	       rdata->len, src_addr, src_port, rdata->packet));
-
-
-    /* Process all message fragments. */
-    while (rdata->len > 0) {
-
-	msg_fragment_size = rdata->len;
-#if PJ_HAS_TCP
+	pj_memset(&rdata->msg_info, 0, sizeof(rdata->msg_info));
+	pj_list_init(&rdata->msg_info.parse_err);
+	rdata->msg_info.msg_buf = current_pkt;
+	rdata->msg_info.len = remaining_len;
+
 	/* For TCP transport, check if the whole message has been received. */
-	if (tr->type != PJSIP_TRANSPORT_UDP) {
+	if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) {
 	    pj_status_t msg_status;
-	    msg_status = pjsip_find_msg(rdata->packet, rdata->len, PJ_FALSE, 
+	    msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, 
                                         &msg_fragment_size);
 	    if (msg_status != PJ_SUCCESS) {
-		if (rdata->len == PJSIP_MAX_PKT_LEN) {
-                    PJSIP_ENDPT_LOG_ERROR((mgr->endpt, tr->obj_name, 
-                                           PJSIP_EOVERFLOW,
-                                           "Buffer discarded for %s:%d",
-			                   src_addr, src_port));
-		    goto on_return;
+		if (remaining_len == PJSIP_MAX_PKT_LEN) {
+		    mgr->msg_cb(mgr->endpt, PJSIP_EOVERFLOW, rdata);
+		    /* Exhaust all data. */
+		    return rdata->pkt_info.len;
 		} else {
-		    goto tcp_read_packet;
+		    /* Not enough data in packet. */
+		    return total_processed;
 		}
 	    }
 	}
-#endif
-
-	/* Clear parser error report */
-	pj_list_init(&rdata->parse_err);
+
+	/* Update msg_info. */
+	rdata->msg_info.len = msg_fragment_size;
 
 	/* Parse the message. */
-	PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size,
-		src_addr, src_port));
-
-	msg = pjsip_parse_rdata( rdata->packet, msg_fragment_size, rdata);
+	rdata->msg_info.msg = msg = 
+	    pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata);
 	if (msg == NULL) {
-	    PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size,
-		    src_addr, src_port));
+	    mgr->msg_cb(mgr->endpt, PJSIP_EINVALIDMSG, rdata);
 	    goto finish_process_fragment;
 	}
 
 	/* Perform basic header checking. */
-	if (rdata->call_id.ptr == NULL || rdata->from == NULL || 
-	    rdata->to == NULL || rdata->via == NULL || rdata->cseq == NULL) 
+	if (rdata->msg_info.call_id.ptr == NULL || 
+	    rdata->msg_info.from == NULL || 
+	    rdata->msg_info.to == NULL || 
+	    rdata->msg_info.via == NULL || 
+	    rdata->msg_info.cseq == NULL) 
 	{
-	    PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header", 
-		    src_addr, src_port));
+	    mgr->msg_cb(mgr->endpt, PJSIP_EMISSINGHDR, rdata);
 	    goto finish_process_fragment;
 	}
 
-	/* If message is received from address that's different from the sent-by,
+	/* If message is received from address that's different from sent-by,
   	 * MUST add received parameter to the via.
-	 * In our case, we add Via receive param for EVERY received message, 
-	 * because it saves us from resolving the host HERE in case sent-by is in
-	 * FQDN format. And it doesn't hurt either.
 	 */
-	s = pj_str(src_addr);
-	pj_strdup(rdata->pool, &rdata->via->recvd_param, &s);
+	s = pj_str(pj_inet_ntoa(rdata->pkt_info.addr.sin_addr));
+	if (pj_strcmp(&s, &rdata->msg_info.via->sent_by.host) != 0) {
+	    pj_strdup(rdata->tp_info.pool, 
+		      &rdata->msg_info.via->recvd_param, &s);
+	}
 
 	/* RFC 3581:
 	 * If message contains "rport" param, put the received port there.
 	 */
-	if (rdata->via->rport_param == 0) {
-	    rdata->via->rport_param = pj_sockaddr_in_get_port(&rdata->addr);
+	if (rdata->msg_info.via->rport_param == 0) {
+	    rdata->msg_info.via->rport_param = 
+		pj_ntohs(rdata->pkt_info.addr.sin_port);
 	}
 
@@ -1470,11 +708,10 @@
 	*/
 	if (msg->type == PJSIP_RESPONSE_MSG) {
-	    hdr = (pjsip_hdr*)rdata->via->next;
-	    if (hdr != &rdata->msg->hdr) {
+	    pjsip_hdr *hdr;
+	    hdr = (pjsip_hdr*)rdata->msg_info.via->next;
+	    if (hdr != &msg->hdr) {
 		hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
 		if (hdr) {
-		    PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: "
-					    "multiple Via in response message",
-					    src_addr, src_port));
+		    mgr->msg_cb(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata);
 		    goto finish_process_fragment;
 		}
@@ -1483,201 +720,132 @@
 
 	/* Call the transport manager's upstream message callback.
-	*/
-	(*mgr->message_callback)(mgr->endpt, rdata);
+	 */
+	mgr->msg_cb(mgr->endpt, PJ_SUCCESS, rdata);
+
 
 finish_process_fragment:
-	rdata->len -= msg_fragment_size;
-	if (rdata->len > 0) {
-	    pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len);
-	    PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len));
-	}
-
-    }	/* while (rdata->len > 0) */
-
-on_return:
-    /* Reset the pool and rdata */
-    rdata_pool = rdata->pool;
-    pj_pool_reset(rdata_pool);
-    rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) );
-    rdata->len = 0;
-    rdata->transport = tr;
-    rdata->pool = rdata_pool;
-    tr->rdata = rdata;
-
-    /* Read the next packet. */
-    rdata->addr_len = sizeof(rdata->addr);
-    if (tr->type == PJSIP_TRANSPORT_UDP) {
-	pj_ssize_t size = PJSIP_MAX_PKT_LEN;
-	pj_ioqueue_recvfrom(tr->key, &tr->rdata->op_key,
-			    tr->rdata->packet, &size, 0, 
-			    &rdata->addr, &rdata->addr_len);
-	PJ_TODO(HANDLE_IMMEDIATE_DATA);
-    }
-
-#if PJ_HAS_TCP
-    /* The next 'if' should have been 'else if', but we need to put the
-       label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning.
+	total_processed += msg_fragment_size;
+	current_pkt += msg_fragment_size;
+	remaining_len -= msg_fragment_size;
+
+    }	/* while (rdata->pkt_info.len > 0) */
+
+
+    return total_processed;
+}
+
+
+/*
+ * pjsip_tpmgr_alloc_transport()
+ *
+ * Get transport suitable to communicate to remote. Create a new one
+ * if necessary.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
+						 pjsip_transport_type_e type,
+						 const pj_sockaddr_in *remote,
+						 pjsip_transport **p_transport)
+{
+    transport_key key;
+    pjsip_transport *transport;
+    pjsip_tpfactory *factory;
+    pj_status_t status;
+
+    pj_lock_acquire(mgr->lock);
+
+    /* First try to get exact destination. */
+    key.type = (pj_uint8_t)type;
+    key.zero = 0;
+    key.addr = pj_ntohl(remote->sin_addr.s_addr);
+    key.port = pj_ntohs(remote->sin_port);
+
+    transport = pj_hash_get(mgr->table, &key, sizeof(key));
+    if (transport != NULL) {
+	unsigned flag = pjsip_transport_get_flag_from_type(type);
+	
+	/* For datagram transports, try lookup with zero address. */
+	if (flag & PJSIP_TRANSPORT_DATAGRAM) {
+	    key.addr = 0;
+	    key.port = 0;
+
+	    transport = pj_hash_get(mgr->table, &key, sizeof(key));
+	}
+    }
+    
+    if (transport != NULL) {
+	/*
+	 * Transport found!
+	 */
+	pjsip_transport_add_ref(transport);
+	pj_lock_release(mgr->lock);
+	*p_transport = transport;
+	return PJ_SUCCESS;
+    }
+
+    /*
+     * Transport not found!
+     * Find factory that can create such transport.
      */
-tcp_read_packet:
-    if (tr->type == PJSIP_TRANSPORT_TCP) {
-	pj_ssize_t size = PJSIP_MAX_PKT_LEN - tr->rdata->len;
-	pj_ioqueue_recv( tr->key, &tr->rdata->op_key,
-			 tr->rdata->packet + tr->rdata->len,
-			 &size, 0);
-	PJ_TODO(HANDLE_IMMEDIATE_DATA_1);
-    }
-#endif
-}
-
-static void transport_mgr_on_idle( pjsip_transport_mgr *mgr )
-{
-    pj_time_val now;
+    factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+	if (factory->type == type)
+	    break;
+	factory = factory->next;
+    }
+
+    if (factory == &mgr->factory_list) {
+	/* No factory can create the transport! */
+	pj_lock_release(mgr->lock);
+	return PJSIP_EUNSUPTRANSPORT;
+    }
+
+    /* Request factory to create transport. */
+    status = factory->create_transport(factory, mgr, mgr->endpt,
+				       mgr->ioqueue, remote, p_transport);
+
+    pj_lock_release(mgr->lock);
+    return status;
+}
+
+/**
+ * Dump transport info.
+ */
+PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
     pj_hash_iterator_t itr_val;
     pj_hash_iterator_t *itr;
 
-
-    /* Get time for comparing transport's close time. */
-    pj_gettimeofday(&now);
-    if (now.sec < mgr->next_idle_check.sec) {
-	return;
-    }
-
-    /* Acquire transport manager's lock. */
-    pj_mutex_lock(mgr->mutex);
-
-    /* Update next idle check. */
-    mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
-
-    /* Iterate all transports, and close transports that are not used for
-       some periods.
-     */
-    itr = pjsip_transport_first(mgr, &itr_val);
-    while (itr != NULL) {
-	pj_hash_iterator_t *next;
-	pjsip_transport_t *transport;
-	
-	transport = pjsip_transport_this(mgr, itr);
-
-	next = pjsip_transport_next(mgr, itr);
-
-	if (pj_atomic_get(transport->ref_cnt)==0 && 
-	    PJ_TIME_VAL_LTE(transport->close_time, now))
-	{
-	    destroy_transport(mgr, transport);
-	}
-
-	itr = next;
-    }
-
-    /* Release transport manager's lock. */
-    pj_mutex_unlock(mgr->mutex);
-}
-
-static void on_ioqueue_read(pj_ioqueue_key_t *key, 
-			    pj_ioqueue_op_key_t *op_key, 
-			    pj_ssize_t bytes_read)
-{
-    pjsip_transport_t *t;
-    t = pj_ioqueue_get_user_data(key);
-
-    handle_received_data( t->mgr, t, bytes_read );
-}
-
-static void on_ioqueue_write(pj_ioqueue_key_t *key, 
-			     pj_ioqueue_op_key_t *op_key, 
-			     pj_ssize_t bytes_sent)
-{
-    PJ_UNUSED_ARG(key);
-    PJ_UNUSED_ARG(bytes_sent);
-
-    /* Completion of write operation. 
-     * Do nothing.
-     */
-}
-
-static void on_ioqueue_accept(pj_ioqueue_key_t *key, 
-			      pj_ioqueue_op_key_t *op_key, 
-			      pj_sock_t newsock,
-			      int status)
-{
-#if PJ_HAS_TCP
-    pjsip_transport_t *t;
-    t = pj_ioqueue_get_user_data(key);
-
-    handle_new_connection( t->mgr, t, status );
-#else
-    PJ_UNUSED_ARG(key);
-    PJ_UNUSED_ARG(status);
+    pj_lock_acquire(mgr->lock);
+
+    itr = pj_hash_first(mgr->table, &itr_val);
+    if (itr) {
+	PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
+
+	do {
+	    char src_addr[128], dst_addr[128];
+	    int src_port, dst_port;
+	    pjsip_transport *t;
+
+	    t = pj_hash_this(mgr->table, itr);
+	    pj_native_strcpy(src_addr, pj_inet_ntoa(t->local_addr.sin_addr));
+	    src_port = pj_ntohs(t->local_addr.sin_port);
+
+	    pj_native_strcpy(dst_addr, pj_inet_ntoa(t->rem_addr.sin_addr));
+	    dst_port = pj_ntohs(t->rem_addr.sin_port);
+
+	    PJ_LOG(3, (THIS_FILE, "  %s %s %s:%d --> %s:%d (refcnt=%d)", 
+		       t->type_name,
+		       t->obj_name,
+		       src_addr, src_port,
+		       dst_addr, dst_port,
+		       pj_atomic_get(t->ref_cnt)));
+
+	    itr = pj_hash_next(mgr->table, itr);
+	} while (itr);
+    }
+
+    pj_lock_release(mgr->lock);
 #endif
 }
 
-static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
-{
-#if PJ_HAS_TCP
-    pjsip_transport_t *t;
-    t = pj_ioqueue_get_user_data(key);
-
-    handle_connect_completion( t->mgr, t, status);
-#else
-    PJ_UNUSED_ARG(key);
-    PJ_UNUSED_ARG(status);
-#endif
-}
-
-
-/*
- * Poll for events.
- */
-PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
-					       const pj_time_val *req_timeout )
-{
-    int event_count;
-    int break_loop;
-    int result;
-    pj_time_val timeout;
-
-    PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()"));
-
-    event_count = 0;
-    break_loop = 0;
-    timeout = *req_timeout;
-    do {
-	result = pj_ioqueue_poll( mgr->ioqueue, &timeout);
-	if (result == 1) {
-	    ++event_count;
-
-	    /* Break the loop. */
-	    //if (timeout.msec==0 && timeout.sec==0) {
-	    	break_loop = 1;
-	    //}
-
-	} else {
-	    /* On idle, cleanup transport. */
-	    transport_mgr_on_idle(mgr);
-
-	    break_loop = 1;
-	}
-	timeout.sec = timeout.msec = 0;
-    } while (!break_loop);
-
-    return event_count;
-}
-
-
-PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
-						   pj_hash_iterator_t *it )
-{
-    return pj_hash_first(mgr->transport_table, it);
-}
-
-PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
-						  pj_hash_iterator_t *itr )
-{
-    return pj_hash_next(mgr->transport_table, itr);
-}
-
-PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
-						 pj_hash_iterator_t *itr )
-{
-    return pj_hash_this(mgr->transport_table, itr);
-}
