⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_transport_tls_ossl.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
	tdata_op_key->callback(&tls->base, tdata_op_key->token, bytes_sent);
    }
}


/* Add tdata to pending list */
static void add_pending_tx(struct tls_transport *tls,
			   pjsip_tx_data *tdata)
{
    struct delayed_tdata *delayed_tdata;

    delayed_tdata = pj_pool_alloc(tdata->pool, 
				  sizeof(*delayed_tdata));
    delayed_tdata->tdata_op_key = &tdata->op_key;
    pj_list_push_back(&tls->delayed_list, delayed_tdata);
}


/* 
 * This callback is called by transport manager to send SIP message 
 */
static pj_status_t tls_send_msg(pjsip_transport *transport, 
				pjsip_tx_data *tdata,
				const pj_sockaddr_t *rem_addr,
				int addr_len,
				void *token,
				void (*callback)(pjsip_transport *transport,
						 void *token, 
						 pj_ssize_t sent_bytes))
{
    struct tls_transport *tls = (struct tls_transport*)transport;
    pj_ssize_t size;
    pj_bool_t delayed = PJ_FALSE;
    pj_status_t status = PJ_SUCCESS;

    /* Sanity check */
    PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);

    /* Check that there's no pending operation associated with the tdata */
    PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
    
    /* Check the address is supported */
    PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL);



    /* Init op key. */
    tdata->op_key.tdata = tdata;
    tdata->op_key.token = token;
    tdata->op_key.callback = callback;

    /* If asynchronous connect() has not completed yet, just put the
     * transmit data in the pending transmission list since we can not
     * use the socket yet.
     */
    if (tls->has_pending_connect) {

	/*
	 * Looks like connect() is still in progress. Check again (this time
	 * with holding the lock) to be sure.
	 */
	pj_lock_acquire(tls->base.lock);

	if (tls->has_pending_connect) {
	    /*
	     * connect() is still in progress. Put the transmit data to
	     * the delayed list.
	     */
	    add_pending_tx(tls, tdata);
	    status = PJ_EPENDING;

	    /* Prevent ssl_write() to be called below */
	    delayed = PJ_TRUE;
	}

	pj_lock_release(tls->base.lock);
    } 
    
    if (!delayed) {

	pj_bool_t no_pending_tx;

	/* Make sure that we've flushed pending tx first so that
	 * stream is in order.
	 */
	no_pending_tx = tls_flush_pending_tx(tls);

	/* Send data immediately with SSL_write() if we don't have
	 * pending data in our list.
	 */
	if (no_pending_tx) {

	    status = ssl_write(tls, tdata);

	    /* On EWOULDBLOCK, put this tdata in the list */
	    if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
		add_pending_tx(tls, tdata);
		status = PJ_EPENDING;
	    }

	    if (status != PJ_EPENDING) {
		/* Not pending (could be immediate success or error) */
		tdata->op_key.tdata = NULL;

		/* Shutdown transport on closure/errors */
		if (status != PJ_SUCCESS) {
		    size = -status;

		    ssl_report_error(tls->base.obj_name, 4, status,
				     "TLS send() error");

		    if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
		    pjsip_transport_shutdown(&tls->base);
		}
	    }

	} else {
	    /* We have pending data in our list, so queue the txdata
	     * in the pending tx list.
	     */
	    add_pending_tx(tls, tdata);
	    status = PJ_EPENDING;
	}
    }

    return status;
}


/* 
 * This callback is called by transport manager to shutdown transport.
 * This normally is only used by UDP transport.
 */
static pj_status_t tls_shutdown(pjsip_transport *transport)
{
    struct tls_transport *tls = (struct tls_transport*)transport;

    /* Shutdown SSL */
    if (!tls->ssl_shutdown_called) {
	/* Release our reference counter and shutdown SSL */
	pjsip_transport_dec_ref(transport);
	SSL_shutdown(tls->ssl);
	tls->ssl_shutdown_called = PJ_TRUE;

	PJ_LOG(4,(transport->obj_name, "TLS transport shutdown"));
    }

    return PJ_SUCCESS;
}


/* 
 * Callback from ioqueue that an incoming data is received from the socket.
 */
static void on_read_complete(pj_ioqueue_key_t *key, 
                             pj_ioqueue_op_key_t *op_key, 
                             pj_ssize_t bytes_read_unused)
{
    enum { MAX_IMMEDIATE_PACKET = 10 };
    pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key;
    pjsip_rx_data *rdata = rdata_op_key->rdata;
    struct tls_transport *tls = 
	(struct tls_transport*)rdata->tp_info.transport;
    int i;
    pj_status_t status;

    /* Don't do anything if transport is closing. */
    if (tls->is_closing) {
	tls->is_closing++;
	return;
    }


    /* Recall that we use MSG_PEEK when calling ioqueue_recv(), so
     * when this callback is called, data has not actually been read
     * from socket buffer.
     */

    for (i=0;; ++i) {
	pj_uint32_t flags;

	/* Read data from SSL connection */
	status = ssl_read(tls);

	if (status == PJ_SUCCESS) {
	    /*
	     * We have packet!
	     */
	    pj_size_t size_eaten;

	    /* Init pkt_info part. */
	    rdata->pkt_info.zero = 0;
	    pj_gettimeofday(&rdata->pkt_info.timestamp);

	    /* Report to transport manager.
	     * The transport manager will tell us how many bytes of the packet
	     * have been processed (as valid SIP message).
	     */
	    size_eaten = 
		pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, 
					   rdata);

	    pj_assert(size_eaten <= (pj_size_t)rdata->pkt_info.len);

	    /* Move unprocessed data to the front of the buffer */
	    if (size_eaten>0 && size_eaten<(pj_size_t)rdata->pkt_info.len) {
		pj_memmove(rdata->pkt_info.packet,
			   rdata->pkt_info.packet + size_eaten,
			   rdata->pkt_info.len - size_eaten);
	    }
	    
	    rdata->pkt_info.len -= size_eaten;

	} else if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {

	    /* Ignore EWOULDBLOCK error (?) */

	} else {

	    /* For other errors, treat as transport being closed */
	    ssl_report_error(tls->base.obj_name, 4, status,
			     "Error reading SSL stream");
	    
	    /* We can not destroy the transport since high level objects may
	     * still keep reference to this transport. So we can only 
	     * instruct transport manager to gracefully start the shutdown
	     * procedure for this transport.
	     */
	    if (tls->close_reason==PJ_SUCCESS) 
		tls->close_reason = status;
	    pjsip_transport_shutdown(&tls->base);

	    return;
	}

	/* Reset pool. */
	pj_pool_reset(rdata->tp_info.pool);

	/* If we have pending data in SSL buffer, read it. */
	if (SSL_pending(tls->ssl)) {

	    /* Check that we have enough space in buffer */
	    if (rdata->pkt_info.len >= PJSIP_MAX_PKT_LEN-1) {
		PJ_LOG(4,(tls->base.obj_name, 
			  "Incoming packet dropped from tls:%.*s:%d "
			  "because it's too big (%d bytes)",
			  (int)tls->base.remote_name.host.slen,
			  tls->base.remote_name.host.ptr,
			  tls->base.remote_name.port,
			  rdata->pkt_info.len));
		rdata->pkt_info.len = 0;
	    }

	    continue;
	}

	/* If we've reached maximum number of packets received on a single
	 * poll, force the next reading to be asynchronous.
	 */
	if (i >= MAX_IMMEDIATE_PACKET) {
	    /* Receive quota reached. Force ioqueue_recv() to 
	     * return PJ_EPENDING 
	     */
	    flags = PJ_IOQUEUE_ALWAYS_ASYNC;
	} else {
	    /* This doesn't work too well when IOCP is used, as it seems that
	     * IOCP refuses to do MSG_PEEK when WSARecv() is called without
	     * OVERLAP structure. With OpenSSL it gives this error:
	     * error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version 
	     * number
	     *
	     * flags = 0 
	     */
	    flags = PJ_IOQUEUE_ALWAYS_ASYNC;
	}

	/* Read next packet from the network. Remember, we need to use
	 * MSG_PEEK or otherwise the packet will be eaten by us!
	 */
	bytes_read_unused = 1;
	status = pj_ioqueue_recv(key, op_key, 
				 rdata->pkt_info.packet+rdata->pkt_info.len,
				 &bytes_read_unused, flags | PJ_MSG_PEEK);

	if (status == PJ_SUCCESS) {

	    /* Continue loop. */
	    pj_assert(i < MAX_IMMEDIATE_PACKET);

	} else if (status == PJ_EPENDING) {
	    break;

	} else {
	    /* Socket error */

	    /* We can not destroy the transport since high level objects may
	     * still keep reference to this transport. So we can only 
	     * instruct transport manager to gracefully start the shutdown
	     * procedure for this transport.
	     */
	    if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
	    pjsip_transport_shutdown(&tls->base);

	    return;
	}
    }
}


/* 
 * Callback from ioqueue when asynchronous connect() operation completes.
 */
static void on_connect_complete(pj_ioqueue_key_t *key, 
                                pj_status_t status)
{
    struct tls_transport *tls;
    pj_sockaddr_in addr;
    int addrlen;

    tls = pj_ioqueue_get_user_data(key);

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

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

	ssl_report_error(tls->base.obj_name, 4, status,
			 "Error connecting to %.*s:%d",
			 (int)tls->base.remote_name.host.slen,
			 tls->base.remote_name.host.ptr,
			 tls->base.remote_name.port);

	/* 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_write_complete(tls->key, op_key, -status);
	}

	/* We can not destroy the transport since high level objects may
	 * still keep reference to this transport. So we can only 
	 * instruct transport manager to gracefully start the shutdown
	 * procedure for this transport.
	 */
	if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
	pjsip_transport_shutdown(&tls->base);
	return;
    }

    PJ_LOG(4,(tls->base.obj_name, 
	      "TCP transport %.*s:%d is connected to %.*s:%d",
	      (int)tls->base.local_name.host.slen,
	      tls->base.local_name.host.ptr,
	      tls->base.local_name.port,
	      (int)tls->base.remote_name.host.slen,
	      tls->base.remote_name.host.ptr,
	      tls->base.remote_name.port));


    /* Update (again) local address, just in case local address currently
     * set is different now that the socket is connected (could happen
     * on some systems, like old Win32 probably?).
     */
    addrlen = sizeof(pj_sockaddr_in);
    if (pj_sock_getsockname(tls->sock, &addr, &addrlen)==PJ_SUCCESS) {
	pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tls->base.local_addr;

	if (tp_addr->sin_addr.s_addr != addr.sin_addr.s_addr) {
	    tp_addr->sin_addr.s_addr = addr.sin_addr.s_addr;
	    tp_addr->sin_port = addr.sin_port;
	    sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
				  tp_addr);
	}
    }

    /* Perform SSL_connect() */
    status = ssl_connect(tls);
    if (status != PJ_SUCCESS) {

	/* 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_write_complete(tls->key, op_key, -status);
	}

	if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
	pjsip_transport_shutdown(&tls->base);
	return;
    }

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

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

	/* 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_write_complete(tls->key, op_key, -status);
	}


	/* We can not destroy the transport since high level objects may
	 * still keep reference to this transport. So we can only 
	 * instruct transport manager to gracefully start the shutdown
	 * procedure for this transport.
	 */
	if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
	pjsip_transport_shutdown(&tls->base);
	return;
    }

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

#endif	/* PJSIP_HAS_TLS_TRANSPORT */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -