turn_usage.c
来自「一个开源的sip源代码」· C语言 代码 · 共 1,431 行 · 第 1/3 页
C
1,431 行
status = client_create(sd->tu, src_addr, src_addr_len, &client);
if (status != PJ_SUCCESS) {
pj_stun_perror(THIS_FILE, "Error creating new TURN client",
status);
return status;
}
/* Hand over message to client */
pj_mutex_lock(client->mutex);
status = client_handle_stun_msg(client, msg, src_addr, src_addr_len);
pj_mutex_unlock(client->mutex);
return status;
}
/*
* This callback is called by STUN session when it needs to send packet
* to the network.
*/
static pj_status_t tu_sess_on_send_msg(pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *dst_addr,
unsigned addr_len)
{
struct session_data *sd;
sd = (struct session_data*) pj_stun_session_get_user_data(sess);
if (sd->tu->type == PJ_SOCK_DGRAM) {
return pj_stun_usage_sendto(sd->tu->usage, pkt, pkt_size, 0,
dst_addr, addr_len);
} else {
return PJ_ENOTSUP;
}
}
/****************************************************************************/
/*
* TURN client operations.
*/
/* Function prototypes */
static pj_status_t client_create_relay(struct turn_client *client);
static pj_status_t client_destroy_relay(struct turn_client *client);
static void client_on_expired(pj_timer_heap_t *th, pj_timer_entry *e);
static void client_on_read_complete(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read);
static pj_status_t client_respond(struct turn_client *client,
const pj_stun_msg *msg,
int err_code,
const char *err_msg,
const pj_sockaddr_t *dst_addr,
int dst_addr_len);
static struct peer* client_get_peer(struct turn_client *client,
const pj_sockaddr_in *peer_addr,
pj_uint32_t *hval);
static struct peer* client_add_peer(struct turn_client *client,
const pj_sockaddr_in *peer_addr,
pj_uint32_t hval);
static const char *get_tp_type(int type)
{
if (type==PJ_SOCK_DGRAM)
return "udp";
else if (type==PJ_SOCK_STREAM)
return "tcp";
else
return "???";
}
/*
* This callback is called when incoming STUN message is received
* in the TURN usage. This is called from by tu_on_rx_data() when
* the packet is handed over to the client.
*/
static pj_status_t client_sess_on_rx_msg(pj_stun_session *sess,
const pj_uint8_t *pkt,
unsigned pkt_len,
const pj_stun_msg *msg,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
struct session_data *sd;
PJ_UNUSED_ARG(pkt);
PJ_UNUSED_ARG(pkt_len);
sd = (struct session_data*) pj_stun_session_get_user_data(sess);
pj_assert(sd->client != PJ_SUCCESS);
return client_handle_stun_msg(sd->client, msg, src_addr, src_addr_len);
}
/*
* This callback is called by client's STUN session to send outgoing
* STUN packet. It's called when client calls pj_stun_session_send_msg()
* function.
*/
static pj_status_t client_sess_on_send_msg(pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *dst_addr,
unsigned addr_len)
{
struct session_data *sd;
sd = (struct session_data*) pj_stun_session_get_user_data(sess);
if (sd->tu->type == PJ_SOCK_DGRAM) {
return pj_stun_usage_sendto(sd->tu->usage, pkt, pkt_size, 0,
dst_addr, addr_len);
} else {
return PJ_ENOTSUP;
}
}
/*
* Create a new TURN client for the specified source address.
*/
static pj_status_t client_create(struct turn_usage *tu,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len,
struct turn_client **p_client)
{
pj_pool_t *pool;
struct turn_client *client;
pj_stun_session_cb sess_cb;
struct session_data *sd;
pj_status_t status;
PJ_ASSERT_RETURN(src_addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL);
pool = pj_pool_create(tu->pf, "turnc%p", 4000, 4000, NULL);
client = PJ_POOL_ZALLOC_T(pool, struct turn_client);
client->pool = pool;
client->tu = tu;
client->sock = PJ_INVALID_SOCKET;
pj_memcpy(&client->client_src_addr, src_addr,
sizeof(client->client_src_addr));
if (src_addr) {
const pj_sockaddr_in *a4 = (const pj_sockaddr_in *)src_addr;
pj_ansi_snprintf(client->obj_name, sizeof(client->obj_name),
"%s:%s:%d",
get_tp_type(tu->type),
pj_inet_ntoa(a4->sin_addr),
(int)pj_ntohs(a4->sin_port));
client->obj_name[sizeof(client->obj_name)-1] = '\0';
}
/* Create session */
pj_bzero(&sess_cb, sizeof(sess_cb));
sess_cb.on_send_msg = &client_sess_on_send_msg;
sess_cb.on_rx_request = &client_sess_on_rx_msg;
sess_cb.on_rx_indication = &client_sess_on_rx_msg;
status = pj_stun_session_create(tu->cfg, client->obj_name,
&sess_cb, tu->use_fingerprint,
&client->session);
if (status != PJ_SUCCESS) {
pj_pool_release(pool);
return status;
}
if (tu->cred)
pj_stun_session_set_credential(client->session, tu->cred);
sd = PJ_POOL_ZALLOC_T(pool, struct session_data);
sd->tu = tu;
sd->client = client;
pj_stun_session_set_user_data(client->session, sd);
/* Mutex */
status = pj_mutex_create_recursive(client->pool, pool->obj_name,
&client->mutex);
if (status != PJ_SUCCESS) {
client_destroy(client, status);
return status;
}
/* Create hash table */
client->peer_htable = pj_hash_create(client->pool, MAX_PEER_PER_CLIENT);
if (client->peer_htable == NULL) {
client_destroy(client, status);
return PJ_ENOMEM;
}
/* Init timer entry */
client->expiry_timer.user_data = client;
client->expiry_timer.cb = &client_on_expired;
client->expiry_timer.id = 0;
/* Register to hash table */
pj_mutex_lock(tu->mutex);
pj_hash_set(pool, tu->client_htable, src_addr, src_addr_len, 0, client);
pj_mutex_unlock(tu->mutex);
/* Done */
*p_client = client;
PJ_LOG(4,(THIS_FILE, "TURN client %s created", client->obj_name));
return PJ_SUCCESS;
}
/*
* Destroy TURN client.
*/
static pj_status_t client_destroy(struct turn_client *client,
pj_status_t reason)
{
struct turn_usage *tu = client->tu;
char name[PJ_MAX_OBJ_NAME];
pj_assert(sizeof(name)==sizeof(client->obj_name));
pj_memcpy(name, client->obj_name, sizeof(name));
/* Kill timer if it's active */
if (client->expiry_timer.id != 0) {
pj_timer_heap_cancel(tu->timer_heap, &client->expiry_timer);
client->expiry_timer.id = PJ_FALSE;
}
/* Destroy relay */
client_destroy_relay(client);
/* Unregister client from hash table */
pj_mutex_lock(tu->mutex);
pj_hash_set(NULL, tu->client_htable,
&client->client_src_addr, sizeof(client->client_src_addr),
0, NULL);
pj_mutex_unlock(tu->mutex);
/* Destroy STUN session */
if (client->session) {
pj_stun_session_destroy(client->session);
client->session = NULL;
}
/* Mutex */
if (client->mutex) {
pj_mutex_destroy(client->mutex);
client->mutex = NULL;
}
/* Finally destroy pool */
if (client->pool) {
pj_pool_t *pool = client->pool;
client->pool = NULL;
pj_pool_release(pool);
}
if (reason == PJ_SUCCESS) {
PJ_LOG(4,(THIS_FILE, "TURN client %s destroyed", name));
}
return PJ_SUCCESS;
}
/*
* This utility function is used to setup relay (with ioqueue) after
* socket has been allocated for the TURN client.
*/
static pj_status_t client_create_relay(struct turn_client *client)
{
pj_ioqueue_callback client_ioq_cb;
int addrlen;
pj_status_t status;
/* Update address */
addrlen = sizeof(pj_sockaddr_in);
status = pj_sock_getsockname(client->sock, &client->alloc_addr,
&addrlen);
if (status != PJ_SUCCESS) {
pj_sock_close(client->sock);
client->sock = PJ_INVALID_SOCKET;
return status;
}
if (client->alloc_addr.sin_addr.s_addr == 0) {
status = pj_gethostip(&client->alloc_addr.sin_addr);
if (status != PJ_SUCCESS) {
pj_sock_close(client->sock);
client->sock = PJ_INVALID_SOCKET;
return status;
}
}
/* Register to ioqueue */
pj_bzero(&client_ioq_cb, sizeof(client_ioq_cb));
client_ioq_cb.on_read_complete = &client_on_read_complete;
status = pj_ioqueue_register_sock(client->pool, client->tu->ioqueue,
client->sock, client,
&client_ioq_cb, &client->key);
if (status != PJ_SUCCESS) {
pj_sock_close(client->sock);
client->sock = PJ_INVALID_SOCKET;
return status;
}
pj_ioqueue_op_key_init(&client->pkt_read_key,
sizeof(client->pkt_read_key));
pj_ioqueue_op_key_init(&client->pkt_write_key,
sizeof(client->pkt_write_key));
/* Trigger the first read */
client_on_read_complete(client->key, &client->pkt_read_key, 0);
PJ_LOG(4,(THIS_FILE, "TURN client %s: relay allocated on %s:%s:%d",
client->obj_name,
get_tp_type(client->sock_type),
pj_inet_ntoa(client->alloc_addr.sin_addr),
(int)pj_ntohs(client->alloc_addr.sin_port)));
return PJ_SUCCESS;
}
/*
* This utility function is used to destroy the port allocated for
* the TURN client.
*/
static pj_status_t client_destroy_relay(struct turn_client *client)
{
/* Close socket */
if (client->key) {
pj_ioqueue_unregister(client->key);
client->key = NULL;
client->sock = PJ_INVALID_SOCKET;
} else if (client->sock && client->sock != PJ_INVALID_SOCKET) {
pj_sock_close(client->sock);
client->sock = PJ_INVALID_SOCKET;
}
PJ_LOG(4,(THIS_FILE, "TURN client %s: relay allocation %s:%s:%d destroyed",
client->obj_name,
get_tp_type(client->sock_type),
pj_inet_ntoa(client->alloc_addr.sin_addr),
(int)pj_ntohs(client->alloc_addr.sin_port)));
return PJ_SUCCESS;
}
/*
* From the source packet address, get the peer instance from hash table.
*/
static struct peer* client_get_peer(struct turn_client *client,
const pj_sockaddr_in *peer_addr,
pj_uint32_t *hval)
{
return (struct peer*)
pj_hash_get(client->peer_htable, peer_addr, sizeof(*peer_addr), hval);
}
/*
* Add a peer instance to the peer hash table.
*/
static struct peer* client_add_peer(struct turn_client *client,
const pj_sockaddr_in *peer_addr,
unsigned hval)
{
struct peer *peer;
peer = PJ_POOL_ZALLOC_T(client->pool, struct peer);
peer->client = client;
pj_memcpy(&peer->addr, peer_addr, sizeof(peer->addr));
pj_hash_set(client->pool, client->peer_htable,
&peer->addr, sizeof(peer->addr), hval, peer);
PJ_LOG(4,(THIS_FILE, "TURN client %s: peer %s:%s:%d added",
client->obj_name, get_tp_type(client->sock_type),
pj_inet_ntoa(peer->addr.sin_addr),
(int)pj_ntohs(peer->addr.sin_port)));
return peer;
}
/*
* Utility to send STUN response message (normally to send error response).
*/
static pj_status_t client_respond(struct turn_client *client,
const pj_stun_msg *msg,
int err_code,
const char *custom_msg,
const pj_sockaddr_t *dst_addr,
int dst_addr_len)
{
pj_str_t err_msg;
pj_str_t *p_err_msg = NULL;
pj_stun_tx_data *response;
pj_status_t status;
if (custom_msg)
pj_cstr(&err_msg, custom_msg), p_err_msg = &err_msg;
status = pj_stun_session_create_res(client->session, msg,
err_code, p_err_msg,
&response);
if (status == PJ_SUCCESS)
status = pj_stun_session_send_msg(client->session, PJ_TRUE,
dst_addr, dst_addr_len, response);
return status;
}
/*
* Handle incoming initial or subsequent Allocate Request.
* This function is called by client_handle_stun_msg() below.
*/
static pj_status_t client_handle_allocate_req(struct turn_client *client,
const pj_stun_msg *msg,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
const pj_stun_bandwidth_attr *a_bw;
const pj_stun_lifetime_attr *a_lf;
const pj_stun_req_port_props_attr *a_rpp;
const pj_stun_req_transport_attr *a_rt;
const pj_stun_req_ip_attr *a_rip;
pj_stun_tx_data *response;
pj_sockaddr_in req_addr;
int addr_len;
unsigned req_bw, rpp_bits;
pj_time_val timeout;
pj_status_t status;
a_bw = (const pj_stun_bandwidth_attr *)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0);
a_lf = (const pj_stun_lifetime_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
a_rpp = (const pj_stun_req_port_props_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_PORT_PROPS, 0);
a_rt = (const pj_stun_req_transport_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
a_rip = (const pj_stun_req_ip_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_IP, 0);
/* Init requested local address */
pj_sockaddr_in_init(&req_addr, NULL, 0);
/* Process BANDWIDTH attribute */
if (a_bw && a_bw->value > client->tu->max_bw_kbps) {
client_respond(client, msg, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL,
src_addr, src_addr_len);
return PJ_SUCCESS;
} else if (a_bw) {
client->bw_kbps = req_bw = a_bw->value;
} else {
req_bw = 0;
client->bw_kbps = client->tu->max_bw_kbps;
}
/* Process REQUESTED-TRANSPORT attribute */
if (a_rt && a_rt->value != 0) {
client_respond(client, msg, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, NULL,
src_addr, src_addr_len);
return PJ_SUCCESS;
} else if (a_rt) {
client->sock_type = a_rt->value ? PJ_SOCK_STREAM : PJ_SOCK_DGRAM;
} else {
client->sock_type = client->tu->type;;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?