static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
				     pj_status_t status)
{
    struct tls_transport *tls;
    pj_ssl_sock_info ssl_info;
    pj_sockaddr addr, *tp_addr;
    pjsip_tp_state_callback state_cb;
    pj_bool_t is_shutdown;
    char local_addr_buf[PJ_INET6_ADDRSTRLEN+10];
    char remote_addr_buf[PJ_INET6_ADDRSTRLEN+10];

    tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock);

    /* If transport is being shutdown/destroyed, proceed as error connect.
     * Note that it is important to notify application via on_data_sent()
     * as otherwise the transport reference counter may never reach zero
     * (see #1898).
     */
    if ((tls->base.is_shutdown || tls->base.is_destroying) &&
	status == PJ_SUCCESS)
    {
	status = PJ_ECANCELLED;
    }

    /* Check connect() status */
    if (status != PJ_SUCCESS) {

	tls_perror(tls->base.obj_name, "TLS connect() error", status,
		   &tls->remote_name);

	/* Cancel all delayed transmits */
	while (!pj_list_empty(&tls->delayed_list)) {
	    struct delayed_tdata *pending_tx;
	    pj_ioqueue_op_key_t *op_key;

	    pending_tx = tls->delayed_list.next;
	    pj_list_erase(pending_tx);

	    op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;

	    on_data_sent(tls->ssock, op_key, -status);
	}

	goto on_error;
    }

    /* Retrieve SSL socket info, shutdown the transport if this is failed
     * as the SSL socket info availability is rather critical here.
     */
    status = pj_ssl_sock_get_info(tls->ssock, &ssl_info);
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Update (again) local address, just in case local address currently
     * set is different now that the socket is connected (could happen
     * on some systems, like old Win32 probably?).
     */
    tp_addr = &tls->base.local_addr;
    pj_sockaddr_cp((pj_sockaddr_t*)&addr, 
		   (pj_sockaddr_t*)&ssl_info.local_addr);
    if (pj_sockaddr_cmp(tp_addr, &addr) != 0) {
	pj_sockaddr_cp(tp_addr, &addr);
	sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
			      tp_addr);
    }

    /* Server identity verification based on server certificate. */
    if (ssl_info.remote_cert_info->version) {
	pj_str_t *remote_name;
	pj_ssl_cert_info *serv_cert = ssl_info.remote_cert_info;
	pj_bool_t matched = PJ_FALSE;
	unsigned i;

	/* Remote name may be hostname or IP address */
	if (tls->remote_name.slen)
	    remote_name = &tls->remote_name;
	else
	    remote_name = &tls->base.remote_name.host;

	/* Start matching remote name with SubjectAltName fields of 
	 * server certificate.
	 */
	for (i = 0; i < serv_cert->subj_alt_name.cnt && !matched; ++i) {
	    pj_str_t *cert_name = &serv_cert->subj_alt_name.entry[i].name;

	    switch (serv_cert->subj_alt_name.entry[i].type) {
	    case PJ_SSL_CERT_NAME_DNS:
	    case PJ_SSL_CERT_NAME_IP:
		matched = !pj_stricmp(remote_name, cert_name);
		break;
	    case PJ_SSL_CERT_NAME_URI:
		if (pj_strnicmp2(cert_name, "sip:", 4) == 0 ||
		    pj_strnicmp2(cert_name, "sips:", 5) == 0)
		{
		    pj_str_t host_part;
		    char *p;

		    p = pj_strchr(cert_name, ':') + 1;
		    pj_strset(&host_part, p, cert_name->slen - 
					     (p - cert_name->ptr));
		    matched = !pj_stricmp(remote_name, &host_part);
		}
		break;
	    default:
		break;
	    }
	}
    	
	/* When still not matched or no SubjectAltName fields in server
	 * certificate, try with Common Name of Subject field.
	 */
	if (!matched) {
	    matched = !pj_stricmp(remote_name, &serv_cert->subject.cn);
	}

	if (!matched) {
	    if (pj_strnicmp2(&serv_cert->subject.cn, "*.", 2) == 0) {
		PJ_LOG(1,(tls->base.obj_name,
		    "RFC 5922 (section 7.2) does not allow TLS wildcard "
			"certificates. Advise your SIP provider, please!"));
	    }
	    ssl_info.verify_status |= PJ_SSL_CERT_EIDENTITY_NOT_MATCH;
	}
    }

    /* Prevent immediate transport destroy as application may access it 
     * (getting info, etc) in transport state notification callback.
     */
    pjsip_transport_add_ref(&tls->base);

    /* If there is verification error and verification is mandatory, shutdown
     * and destroy the transport.
     */
    if (ssl_info.verify_status && tls->verify_server) {
	if (tls->close_reason == PJ_SUCCESS) 
	    tls->close_reason = PJSIP_TLS_ECERTVERIF;
	pjsip_transport_shutdown(&tls->base);
    }

    /* Notify transport state to application */
    state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr);
    if (state_cb) {
	pjsip_transport_state_info state_info;
	pjsip_tls_state_info tls_info;
	pjsip_transport_state tp_state;

	/* Init transport state info */
	pj_bzero(&state_info, sizeof(state_info));
	pj_bzero(&tls_info, sizeof(tls_info));
	state_info.ext_info = &tls_info;
	tls_info.ssl_sock_info = &ssl_info;

	/* Set transport state based on verification status */
	if (ssl_info.verify_status && tls->verify_server)
	{
	    tp_state = PJSIP_TP_STATE_DISCONNECTED;
	    state_info.status = PJSIP_TLS_ECERTVERIF;
	} else {
	    tp_state = PJSIP_TP_STATE_CONNECTED;
	    state_info.status = PJ_SUCCESS;
	}

	(*state_cb)(&tls->base, tp_state, &state_info);
    }

    /* Release transport reference. If transport is shutting down, it may
     * get destroyed here.
     */
    is_shutdown = tls->base.is_shutdown;
    pjsip_transport_dec_ref(&tls->base);
    if (is_shutdown) {
	status = tls->close_reason;
	tls_perror(tls->base.obj_name, "TLS connect() error", status, 
		   &tls->remote_name);

	/* Cancel all delayed transmits */
	while (!pj_list_empty(&tls->delayed_list)) {
	    struct delayed_tdata *pending_tx;
	    pj_ioqueue_op_key_t *op_key;

	    pending_tx = tls->delayed_list.next;
	    pj_list_erase(pending_tx);

	    op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;

	    on_data_sent(tls->ssock, op_key, -status);
	}

	return PJ_FALSE;
    }


    /* Mark that pending connect() operation has completed. */
    tls->has_pending_connect = PJ_FALSE;

    PJ_LOG(4,(tls->base.obj_name, 
	      "TLS transport %s is connected to %s",
	      pj_addr_str_print(&tls->base.local_name.host, 
				tls->base.local_name.port, local_addr_buf, 
				sizeof(local_addr_buf), 1),
	      pj_addr_str_print(&tls->base.remote_name.host, 
				tls->base.remote_name.port, remote_addr_buf, 
				sizeof(remote_addr_buf), 1)));

    /* Start pending read */
    status = tls_start_read(tls);
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Flush all pending send operations */
    tls_flush_pending_tx(tls);

    /* Start keep-alive timer */
    if (pjsip_cfg()->tls.keep_alive_interval) {
	pj_time_val delay = {0};	    
	delay.sec = pjsip_cfg()->tls.keep_alive_interval;
	pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, 
				   &delay);
	tls->ka_timer.id = PJ_TRUE;
	pj_gettimeofday(&tls->last_activity);
    }

    return PJ_TRUE;

on_error:
    tls_init_shutdown(tls, status);

    return PJ_FALSE;
}
static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
				     pj_status_t status)
{
    struct tls_transport *tls;
    pj_ssl_sock_info ssl_info;
    pj_sockaddr addr, *tp_addr;
    pjsip_tp_state_callback state_cb;
    pj_bool_t is_shutdown;
    char local_addr_buf[PJ_INET6_ADDRSTRLEN+10];
    char remote_addr_buf[PJ_INET6_ADDRSTRLEN+10];

    tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock);

     
    if ((tls->base.is_shutdown || tls->base.is_destroying) &&
	status == PJ_SUCCESS)
    {
	status = PJ_ECANCELLED;
    }

     
    if (status != PJ_SUCCESS) {

	tls_perror(tls->base.obj_name, "TLS connect() error", status,
		   &tls->remote_name);

	 
	while (!pj_list_empty(&tls->delayed_list)) {
	    struct delayed_tdata *pending_tx;
	    pj_ioqueue_op_key_t *op_key;

	    pending_tx = tls->delayed_list.next;
	    pj_list_erase(pending_tx);

	    op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;

	    on_data_sent(tls->ssock, op_key, -status);
	}

	goto on_error;
    }

     
    status = pj_ssl_sock_get_info(tls->ssock, &ssl_info);
    if (status != PJ_SUCCESS)
	goto on_error;

     
    tp_addr = &tls->base.local_addr;
    pj_sockaddr_cp((pj_sockaddr_t*)&addr, 
		   (pj_sockaddr_t*)&ssl_info.local_addr);
    if (pj_sockaddr_cmp(tp_addr, &addr) != 0) {
	pj_sockaddr_cp(tp_addr, &addr);
	sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
			      tp_addr);
    }

     
    if (ssl_info.remote_cert_info->version) {
	pj_str_t *remote_name;
	pj_ssl_cert_info *serv_cert = ssl_info.remote_cert_info;
	pj_bool_t matched = PJ_FALSE;
	unsigned i;

	 
	if (tls->remote_name.slen)
	    remote_name = &tls->remote_name;
	else
	    remote_name = &tls->base.remote_name.host;

	 
	for (i = 0; i < serv_cert->subj_alt_name.cnt && !matched; ++i) {
	    pj_str_t *cert_name = &serv_cert->subj_alt_name.entry[i].name;

	    switch (serv_cert->subj_alt_name.entry[i].type) {
	    case PJ_SSL_CERT_NAME_DNS:
	    case PJ_SSL_CERT_NAME_IP:
		matched = !pj_stricmp(remote_name, cert_name);
		break;
	    case PJ_SSL_CERT_NAME_URI:
		if (pj_strnicmp2(cert_name, "sip:", 4) == 0 ||
		    pj_strnicmp2(cert_name, "sips:", 5) == 0)
		{
		    pj_str_t host_part;
		    char *p;

		    p = pj_strchr(cert_name, ':') + 1;
		    pj_strset(&host_part, p, cert_name->slen - 
					     (p - cert_name->ptr));
		    matched = !pj_stricmp(remote_name, &host_part);
		}
		break;
	    default:
		break;
	    }
	}
    	
	 
	if (!matched) {
	    matched = !pj_stricmp(remote_name, &serv_cert->subject.cn);
	}

	if (!matched) {
	    if (pj_strnicmp2(&serv_cert->subject.cn, "*.", 2) == 0) {
		PJ_LOG(1,(tls->base.obj_name,
		    "RFC 5922 (section 7.2) does not allow TLS wildcard "
			"certificates. Advise your SIP provider, please!"));
	    }
	    ssl_info.verify_status |= PJ_SSL_CERT_EIDENTITY_NOT_MATCH;
	}
    }

     
    pjsip_transport_add_ref(&tls->base);

     
    if (ssl_info.verify_status && tls->verify_server) {
	if (tls->close_reason == PJ_SUCCESS) 
	    tls->close_reason = PJSIP_TLS_ECERTVERIF;
	pjsip_transport_shutdown(&tls->base);
    }

     
    state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr);
    if (state_cb) {
	pjsip_transport_state_info state_info;
	pjsip_tls_state_info tls_info;
	pjsip_transport_state tp_state;

	 
	pj_bzero(&state_info, sizeof(state_info));
	pj_bzero(&tls_info, sizeof(tls_info));
	state_info.ext_info = &tls_info;
	tls_info.ssl_sock_info = &ssl_info;

	 
	if (ssl_info.verify_status && tls->verify_server)
	{
	    tp_state = PJSIP_TP_STATE_DISCONNECTED;
	    state_info.status = PJSIP_TLS_ECERTVERIF;
	} else {
	    tp_state = PJSIP_TP_STATE_CONNECTED;
	    state_info.status = PJ_SUCCESS;
	}

	(*state_cb)(&tls->base, tp_state, &state_info);
    }

     
    is_shutdown = tls->base.is_shutdown;
    pjsip_transport_dec_ref(&tls->base);
    if (is_shutdown) {
	status = tls->close_reason;
	tls_perror(tls->base.obj_name, "TLS connect() error", status, 
		   &tls->remote_name);

	 
	while (!pj_list_empty(&tls->delayed_list)) {
	    struct delayed_tdata *pending_tx;
	    pj_ioqueue_op_key_t *op_key;

	    pending_tx = tls->delayed_list.next;
	    pj_list_erase(pending_tx);

	    op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;

	    on_data_sent(tls->ssock, op_key, -status);
	}

	return PJ_FALSE;
    }


     
    tls->has_pending_connect = PJ_FALSE;

    PJ_LOG(4,(tls->base.obj_name, 
	      "TLS transport %s is connected to %s",
	      pj_addr_str_print(&tls->base.local_name.host, 
				tls->base.local_name.port, local_addr_buf, 
				sizeof(local_addr_buf), 1),
	      pj_addr_str_print(&tls->base.remote_name.host, 
				tls->base.remote_name.port, remote_addr_buf, 
				sizeof(remote_addr_buf), 1)));

     
    status = tls_start_read(tls);
    if (status != PJ_SUCCESS)
	goto on_error;

     
    tls_flush_pending_tx(tls);

     
    if (pjsip_cfg()->tls.keep_alive_interval) {
	pj_time_val delay = {0};	    
	delay.sec = pjsip_cfg()->tls.keep_alive_interval;
	pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, 
				   &delay);
	tls->ka_timer.id = PJ_TRUE;
	pj_gettimeofday(&tls->last_activity);
    }

    return PJ_TRUE;

on_error:
    tls_init_shutdown(tls, status);

    return PJ_FALSE;
}