📄 sip_transport_tls_ossl.c
字号:
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 + -