mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-12 22:09:25 +08:00
contrib: implement rfc6062 in pjnath
A first try to implement the RFC 6062 into PJNATH. More explanations into the rfc6062.patch itself. Note: not ready to be pushed up-stream to pjproject. Change-Id: I445f0c8a8db03c9a4a5b04d24ef63ff4f711a725 Reviewed-by: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
This commit is contained in:

committed by
Anthony Léonard

parent
7b0bf45a92
commit
88c820ee60
877
contrib/src/pjproject/rfc6062.patch
Normal file
877
contrib/src/pjproject/rfc6062.patch
Normal file
@ -0,0 +1,877 @@
|
||||
Copyright (c) 2017 Savoir-faire Linux Inc.
|
||||
|
||||
turn: rfc 6062 support
|
||||
|
||||
Current implementation of TURN and STUN doesn't support TCP as a valid
|
||||
peer connection. This connection is defined by the rfc 6062.
|
||||
This patch is an implementation proposal of this rfc into PJNATH.
|
||||
Modifications are API backware compatible, not ABI.
|
||||
Users must rebuild their code.
|
||||
|
||||
Written by
|
||||
Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
on behalf of Savoir-faire Linux.
|
||||
|
||||
----
|
||||
|
||||
--- a/pjnath/include/pjnath/config.h 2017-09-27 16:48:31.000000000 -0400
|
||||
+++ b/pjnath/include/pjnath/config.h 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -220,6 +220,14 @@
|
||||
# define PJ_TURN_KEEP_ALIVE_SEC 15
|
||||
#endif
|
||||
|
||||
+/**
|
||||
+ * Maximal number of TCP data connection that a client can open/accept with
|
||||
+ * peers.
|
||||
+ */
|
||||
+#ifndef PJ_TURN_MAX_TCP_CNX
|
||||
+# define PJ_TURN_MAX_TCP_CNX 32
|
||||
+#endif
|
||||
+
|
||||
|
||||
/* **************************************************************************
|
||||
* ICE CONFIGURATION
|
||||
--- a/pjnath/include/pjnath/stun_msg.h 2017-01-17 22:50:32.000000000 -0500
|
||||
+++ b/pjnath/include/pjnath/stun_msg.h 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -92,6 +92,21 @@
|
||||
*/
|
||||
PJ_STUN_CHANNEL_BIND_METHOD = 9,
|
||||
|
||||
+ /*
|
||||
+ * STUN/TURN Connect as defined by RFC 6062
|
||||
+ */
|
||||
+ PJ_STUN_CONNECT_METHOD = 10,
|
||||
+
|
||||
+ /*
|
||||
+ * STUN/TURN ConnectionBind as defined by RFC 6062
|
||||
+ */
|
||||
+ PJ_STUN_CONNECTION_BIND_METHOD = 11,
|
||||
+
|
||||
+ /*
|
||||
+ * STUN/TURN ConnectionAttempt as defined by RFC 6062
|
||||
+ */
|
||||
+ PJ_STUN_CONNECTION_ATTEMPT_METHOD = 12,
|
||||
+
|
||||
/**
|
||||
* All known methods.
|
||||
*/
|
||||
@@ -261,6 +276,16 @@
|
||||
*/
|
||||
PJ_STUN_DATA_INDICATION = 0x0017,
|
||||
|
||||
+ /**
|
||||
+ * STUN/TURN ConnectBind Request
|
||||
+ */
|
||||
+ PJ_STUN_CONNECTION_BIND_REQUEST = 0x000b,
|
||||
+
|
||||
+ /**
|
||||
+ * TURN ConnectionAttempt indication
|
||||
+ */
|
||||
+ PJ_STUN_CONNECTION_ATTEMPT_INDICATION = 0x001c,
|
||||
+
|
||||
|
||||
/**
|
||||
* TURN CreatePermission request
|
||||
@@ -333,6 +358,7 @@
|
||||
PJ_STUN_ATTR_XOR_REFLECTED_FROM = 0x0023,/**< XOR-REFLECTED-FROM */
|
||||
PJ_STUN_ATTR_PRIORITY = 0x0024,/**< PRIORITY */
|
||||
PJ_STUN_ATTR_USE_CANDIDATE = 0x0025,/**< USE-CANDIDATE */
|
||||
+ PJ_STUN_ATTR_CONNECTION_ID = 0x002a,/**< CONNECTION-ID */
|
||||
PJ_STUN_ATTR_ICMP = 0x0030,/**< ICMP (TURN) */
|
||||
|
||||
PJ_STUN_ATTR_END_MANDATORY_ATTR,
|
||||
--- a/pjnath/include/pjnath/stun_session.h 2013-10-01 01:00:57.000000000 -0400
|
||||
+++ b/pjnath/include/pjnath/stun_session.h 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -751,6 +751,13 @@
|
||||
pj_stun_tx_data *tdata);
|
||||
|
||||
|
||||
+PJ_DEF(void) pj_stun_session_get_server_cred(
|
||||
+ pj_stun_session *sess,
|
||||
+ pj_pool_t *pool, pj_str_t *nonce, pj_str_t *realm);
|
||||
+
|
||||
+PJ_DEF(void) pj_stun_session_set_server_cred(
|
||||
+ pj_stun_session *sess, const pj_str_t *nonce, pj_str_t *realm);
|
||||
+
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
--- a/pjnath/include/pjnath/turn_session.h 2016-11-14 01:13:01.000000000 -0500
|
||||
+++ b/pjnath/include/pjnath/turn_session.h 2017-09-28 08:26:16.000000000 -0400
|
||||
@@ -184,6 +184,12 @@
|
||||
PJ_TURN_STATE_ALLOCATING,
|
||||
|
||||
/**
|
||||
+ * TURN session has issued CONNECTION-BIND request and is waiting for response
|
||||
+ * from the TURN server.
|
||||
+ */
|
||||
+ PJ_TURN_STATE_CONNECTION_BINDING,
|
||||
+
|
||||
+ /**
|
||||
* TURN session has successfully allocated relay resoruce and now is
|
||||
* ready to be used.
|
||||
*/
|
||||
@@ -298,6 +304,20 @@
|
||||
pj_turn_state_t old_state,
|
||||
pj_turn_state_t new_state);
|
||||
|
||||
+ /**
|
||||
+ * Notification when TURN session get a ConnectionAttempt indication.
|
||||
+ *
|
||||
+ * @param sess The TURN session.
|
||||
+ * @param conn_id The connection-id to use for connection binding.
|
||||
+ * @param peer_addr Peer address that tried to connect on the TURN server.
|
||||
+ * @param addr_len Length of the peer address.
|
||||
+
|
||||
+ */
|
||||
+ void (*on_peer_connection)(pj_turn_session *sess,
|
||||
+ pj_uint32_t conn_id,
|
||||
+ const pj_sockaddr_t *peer_addr,
|
||||
+ unsigned addr_len);
|
||||
+
|
||||
} pj_turn_session_cb;
|
||||
|
||||
|
||||
@@ -339,6 +359,14 @@
|
||||
*/
|
||||
int af;
|
||||
|
||||
+ /**
|
||||
+ * Type of connection to from TURN server to peer.
|
||||
+ *
|
||||
+ * Supported values: PJ_TURN_TP_UDP (rfc 5766), PJ_TURN_TP_TLS (rfc 6062)
|
||||
+ *
|
||||
+ * Default is PJ_TURN_TP_UDP.
|
||||
+ */
|
||||
+ pj_turn_tp_type peer_conn_type;
|
||||
|
||||
} pj_turn_alloc_param;
|
||||
|
||||
@@ -741,6 +769,15 @@
|
||||
pj_size_t pkt_len,
|
||||
pj_size_t *parsed_len);
|
||||
|
||||
+/**
|
||||
+ * rfc6062
|
||||
+ */
|
||||
+PJ_DEF(void) pj_turn_session_get_server_cred(
|
||||
+ pj_turn_session *sess,
|
||||
+ pj_pool_t *pool, pj_str_t *nonce, pj_str_t *realm);
|
||||
+
|
||||
+PJ_DEF(void) pj_turn_session_set_server_cred(
|
||||
+ pj_turn_session *sess, const pj_str_t *nonce, pj_str_t *realm);
|
||||
|
||||
/**
|
||||
* @}
|
||||
--- a/pjnath/include/pjnath/turn_sock.h 2013-10-01 01:00:57.000000000 -0400
|
||||
+++ b/pjnath/include/pjnath/turn_sock.h 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -98,6 +98,20 @@
|
||||
pj_turn_state_t old_state,
|
||||
pj_turn_state_t new_state);
|
||||
|
||||
+ /**
|
||||
+ * Notification when TURN session get a ConnectionAttempt indication.
|
||||
+ *
|
||||
+ * @param turn_sock The TURN client transport.
|
||||
+ * @param conn_id The connection-id to use for connection binding.
|
||||
+ * @param peer_addr Peer address that tried to connect on the TURN server.
|
||||
+ * @param addr_len Length of the peer address.
|
||||
+
|
||||
+ */
|
||||
+ void (*on_peer_connection)(pj_turn_sock *turn_sock,
|
||||
+ pj_uint32_t conn_id,
|
||||
+ const pj_sockaddr_t *peer_addr,
|
||||
+ unsigned addr_len);
|
||||
+
|
||||
} pj_turn_sock_cb;
|
||||
|
||||
|
||||
@@ -446,6 +460,13 @@
|
||||
const pj_sockaddr_t *peer,
|
||||
unsigned addr_len);
|
||||
|
||||
+/**
|
||||
+ * RFC 6062
|
||||
+ */
|
||||
+PJ_DECL(pj_status_t) pj_turn_connect_peer(pj_turn_sock *sock,
|
||||
+ pj_uint32_t conn_id,
|
||||
+ const pj_sockaddr_t *peer_addr,
|
||||
+ unsigned addr_len);
|
||||
|
||||
/**
|
||||
* @}
|
||||
--- a/pjnath/src/pjnath/stun_msg.c 2017-01-17 22:50:32.000000000 -0500
|
||||
+++ b/pjnath/src/pjnath/stun_msg.c 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -45,6 +45,9 @@
|
||||
"Data", /* 7 */
|
||||
"CreatePermission", /* 8 */
|
||||
"ChannelBind", /* 9 */
|
||||
+ "Connect", /* 10 */
|
||||
+ "ConnectionBind", /* 11 */
|
||||
+ "ConnectionAttempt", /* 12 */
|
||||
};
|
||||
|
||||
static struct
|
||||
@@ -476,11 +479,11 @@
|
||||
NULL
|
||||
},
|
||||
{
|
||||
- /* ID 0x002a is not assigned */
|
||||
- NULL,
|
||||
- NULL,
|
||||
- NULL,
|
||||
- NULL
|
||||
+ /* PJ_STUN_ATTR_CONNECTION_ID, */
|
||||
+ "CONNECTION-ID",
|
||||
+ &decode_uint_attr,
|
||||
+ &encode_uint_attr,
|
||||
+ &clone_uint_attr
|
||||
},
|
||||
{
|
||||
/* ID 0x002b is not assigned */
|
||||
--- a/pjnath/src/pjnath/stun_session.c 2016-01-05 09:34:22.000000000 -0500
|
||||
+++ b/pjnath/src/pjnath/stun_session.c 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -1511,3 +1511,14 @@
|
||||
return status;
|
||||
}
|
||||
|
||||
+PJ_DEF(void) pj_stun_session_get_server_cred(pj_stun_session *sess, pj_pool_t *pool, pj_str_t *nonce, pj_str_t *realm)
|
||||
+{
|
||||
+ pj_strdup(pool, nonce, &sess->next_nonce);
|
||||
+ pj_strdup(pool, realm, &sess->server_realm);
|
||||
+}
|
||||
+
|
||||
+PJ_DEF(void) pj_stun_session_set_server_cred(pj_stun_session *sess, const pj_str_t *nonce, pj_str_t *realm)
|
||||
+{
|
||||
+ pj_strdup(sess->pool, &sess->next_nonce, nonce);
|
||||
+ pj_strdup(sess->pool, &sess->server_realm, realm);
|
||||
+}
|
||||
--- a/pjnath/src/pjnath/turn_session.c 2017-09-27 16:48:31.000000000 -0400
|
||||
+++ b/pjnath/src/pjnath/turn_session.c 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -42,6 +42,7 @@
|
||||
"Resolving",
|
||||
"Resolved",
|
||||
"Allocating",
|
||||
+ "TcpBinding",
|
||||
"Ready",
|
||||
"Deallocating",
|
||||
"Deallocated",
|
||||
@@ -208,6 +209,7 @@
|
||||
PJ_DEF(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm)
|
||||
{
|
||||
pj_bzero(prm, sizeof(*prm));
|
||||
+ prm->peer_conn_type = PJ_TURN_TP_UDP;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -403,6 +405,11 @@
|
||||
sess->pending_destroy = PJ_TRUE;
|
||||
can_destroy = PJ_FALSE;
|
||||
break;
|
||||
+ case PJ_TURN_STATE_CONNECTION_BINDING:
|
||||
+ /* We need to wait until connection binding complete */
|
||||
+ sess->pending_destroy = PJ_TRUE;
|
||||
+ can_destroy = PJ_FALSE;
|
||||
+ break;
|
||||
case PJ_TURN_STATE_READY:
|
||||
/* Send REFRESH with LIFETIME=0 */
|
||||
can_destroy = PJ_FALSE;
|
||||
@@ -719,6 +726,9 @@
|
||||
PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL &&
|
||||
sess->state<=PJ_TURN_STATE_RESOLVED,
|
||||
PJ_EINVALIDOP);
|
||||
+ PJ_ASSERT_RETURN(param->peer_conn_type == PJ_TURN_TP_UDP ||
|
||||
+ param->peer_conn_type == PJ_TURN_TP_TCP,
|
||||
+ PJ_EINVAL);
|
||||
|
||||
/* Verify address family in allocation param */
|
||||
if (param && param->af) {
|
||||
@@ -756,7 +766,7 @@
|
||||
/* MUST include REQUESTED-TRANSPORT attribute */
|
||||
pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
|
||||
PJ_STUN_ATTR_REQ_TRANSPORT,
|
||||
- PJ_STUN_SET_RT_PROTO(PJ_TURN_TP_UDP));
|
||||
+ PJ_STUN_SET_RT_PROTO(param->peer_conn_type));
|
||||
|
||||
/* Include BANDWIDTH if requested */
|
||||
if (sess->alloc_param.bandwidth > 0) {
|
||||
@@ -994,6 +1004,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ /* rfc6062: direct send if peer connection is TCP */
|
||||
+ if (sess->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) {
|
||||
+ status = sess->cb.on_send_pkt(sess, pkt, pkt_len,
|
||||
+ addr, addr_len);
|
||||
+ goto on_return;
|
||||
+ }
|
||||
+
|
||||
/* See if the peer is bound to a channel number */
|
||||
ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr),
|
||||
PJ_FALSE, PJ_FALSE);
|
||||
@@ -1670,6 +1687,32 @@
|
||||
|
||||
sess = (pj_turn_session*)pj_stun_session_get_user_data(stun);
|
||||
|
||||
+ if (msg->hdr.type == PJ_STUN_CONNECTION_ATTEMPT_INDICATION) {
|
||||
+ pj_stun_uint_attr *connection_id_attr;
|
||||
+ /* Get CONNECTION-ID attribute */
|
||||
+ connection_id_attr = (pj_stun_uint_attr*)
|
||||
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CONNECTION_ID, 0);
|
||||
+
|
||||
+ /* Get XOR-PEER-ADDRESS attribute */
|
||||
+ peer_attr = (pj_stun_xor_peer_addr_attr*)
|
||||
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
|
||||
+
|
||||
+ /* Must have both XOR-PEER-ADDRESS and CONNECTION-ID attributes */
|
||||
+ if (!peer_attr || !connection_id_attr) {
|
||||
+ PJ_LOG(4,(sess->obj_name,
|
||||
+ "Received ConnectionAttempt indication with missing attributes"));
|
||||
+ return PJ_EINVALIDOP;
|
||||
+ }
|
||||
+
|
||||
+ /* Notify application */
|
||||
+ if (sess->cb.on_peer_connection) {
|
||||
+ (*sess->cb.on_peer_connection)(sess, connection_id_attr->value,
|
||||
+ &peer_attr->sockaddr,
|
||||
+ pj_sockaddr_get_len(&peer_attr->sockaddr));
|
||||
+ }
|
||||
+ return PJ_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
/* Expecting Data Indication only */
|
||||
if (msg->hdr.type != PJ_STUN_DATA_INDICATION) {
|
||||
PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s indication",
|
||||
@@ -2089,3 +2132,16 @@
|
||||
pj_grp_lock_release(sess->grp_lock);
|
||||
}
|
||||
|
||||
+PJ_DEF(void) pj_turn_session_get_server_cred(pj_turn_session *sess,
|
||||
+ pj_pool_t *pool, pj_str_t *nonce,
|
||||
+ pj_str_t *realm)
|
||||
+{
|
||||
+ pj_stun_session_get_server_cred(sess->stun, pool, nonce, realm);
|
||||
+}
|
||||
+
|
||||
+PJ_DEF(void) pj_turn_session_set_server_cred(pj_turn_session *sess,
|
||||
+ const pj_str_t *nonce,
|
||||
+ pj_str_t *realm)
|
||||
+{
|
||||
+ pj_stun_session_set_server_cred(sess->stun, nonce, realm);
|
||||
+}
|
||||
--- a/pjnath/src/pjnath/turn_sock.c 2017-01-19 02:41:25.000000000 -0500
|
||||
+++ b/pjnath/src/pjnath/turn_sock.c 2017-09-28 08:23:37.000000000 -0400
|
||||
@@ -35,9 +35,29 @@
|
||||
|
||||
enum { MAX_BIND_RETRY = 100 };
|
||||
|
||||
+enum { CONNECTION_USED = (1<<0), /* TCP connection slot is used or free */
|
||||
+ CONNECTION_READY = (1<<1) /* TCP connection bind and ready to use for data transfer */
|
||||
+};
|
||||
|
||||
#define INIT 0x1FFFFFFF
|
||||
|
||||
+/**
|
||||
+ * pj_turn_tcp_data_connection contains information on TCP connection open between
|
||||
+ * the client and the turn server, conveying data from/to a specific peer.
|
||||
+ * notes: part of RFC 6062 support
|
||||
+ */
|
||||
+typedef struct pj_turn_tcp_data_connection
|
||||
+{
|
||||
+ pj_uint32_t id; /* identity of this connection as given by the TURN server */
|
||||
+ pj_uint32_t flags; /* 0 or CONNECTION_USED */
|
||||
+ pj_sockaddr peer_addr; /* mapped address of connected peer */
|
||||
+ unsigned peer_addr_len;
|
||||
+ pj_activesock_t *active_tcp_sock; /* socket between client and TURN server */
|
||||
+ pj_ioqueue_op_key_t send_key;
|
||||
+ pj_stun_session *stun_sess; /* STUN session used to send ConnectBind msg */
|
||||
+ pj_turn_sock *turn_sock; /* up link */
|
||||
+} pj_turn_tcp_data_connection;
|
||||
+
|
||||
struct pj_turn_sock
|
||||
{
|
||||
pj_pool_t *pool;
|
||||
@@ -59,6 +79,11 @@
|
||||
pj_turn_tp_type conn_type;
|
||||
pj_activesock_t *active_sock;
|
||||
pj_ioqueue_op_key_t send_key;
|
||||
+
|
||||
+ /* RFC 6062 */
|
||||
+ pj_stun_auth_cred cred; /* saved from control connection */
|
||||
+ pj_size_t tcp_cnx_count; /* number of elements in tcp_cnx */
|
||||
+ pj_turn_tcp_data_connection tcp_cnx[PJ_TURN_MAX_TCP_CNX]; /* peer dedicated data connections throught the TURN server */
|
||||
};
|
||||
|
||||
|
||||
@@ -82,6 +107,11 @@
|
||||
static void turn_on_state(pj_turn_session *sess,
|
||||
pj_turn_state_t old_state,
|
||||
pj_turn_state_t new_state);
|
||||
+static void turn_on_peer_connection(pj_turn_session *sess,
|
||||
+ pj_uint32_t conn_id,
|
||||
+ const pj_sockaddr_t *peer_addr,
|
||||
+ unsigned addr_len);
|
||||
+
|
||||
|
||||
static pj_bool_t on_data_read(pj_activesock_t *asock,
|
||||
void *data,
|
||||
@@ -97,6 +127,26 @@
|
||||
static void destroy(pj_turn_sock *turn_sock);
|
||||
static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e);
|
||||
|
||||
+static pj_bool_t on_peer_data_read(pj_activesock_t *asock,
|
||||
+ void *data,
|
||||
+ pj_size_t size,
|
||||
+ pj_status_t status,
|
||||
+ pj_size_t *remainder);
|
||||
+static pj_bool_t on_peer_connect_complete(pj_activesock_t *asock,
|
||||
+ pj_status_t status);
|
||||
+static pj_status_t on_tcp_stun_send_msg(pj_stun_session *sess,
|
||||
+ void *token,
|
||||
+ const void *pkt,
|
||||
+ pj_size_t pkt_size,
|
||||
+ const pj_sockaddr_t *dst_addr,
|
||||
+ unsigned addr_len);
|
||||
+static void on_tcp_stun_request_complete(pj_stun_session *sess,
|
||||
+ pj_status_t status,
|
||||
+ void *token,
|
||||
+ pj_stun_tx_data *tdata,
|
||||
+ const pj_stun_msg *response,
|
||||
+ const pj_sockaddr_t *src_addr,
|
||||
+ unsigned src_addr_len);
|
||||
|
||||
/* Init config */
|
||||
PJ_DEF(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg)
|
||||
@@ -193,6 +243,7 @@
|
||||
sess_cb.on_channel_bound = &turn_on_channel_bound;
|
||||
sess_cb.on_rx_data = &turn_on_rx_data;
|
||||
sess_cb.on_state = &turn_on_state;
|
||||
+ sess_cb.on_peer_connection = &turn_on_peer_connection;
|
||||
status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type,
|
||||
turn_sock->grp_lock, &sess_cb, 0,
|
||||
turn_sock, &turn_sock->sess);
|
||||
@@ -238,6 +289,9 @@
|
||||
pj_turn_session_shutdown(turn_sock->sess);
|
||||
if (turn_sock->active_sock)
|
||||
pj_activesock_close(turn_sock->active_sock);
|
||||
+ for (int i=0; i < turn_sock->tcp_cnx_count; ++i) {
|
||||
+ pj_activesock_close(turn_sock->tcp_cnx[i].active_tcp_sock);
|
||||
+ }
|
||||
pj_grp_lock_dec_ref(turn_sock->grp_lock);
|
||||
pj_grp_lock_release(turn_sock->grp_lock);
|
||||
}
|
||||
@@ -411,6 +465,9 @@
|
||||
|
||||
/* Set credental */
|
||||
if (cred) {
|
||||
+ // save credentials for peer/TCP connections
|
||||
+ if (param->peer_conn_type == PJ_TURN_TP_TCP)
|
||||
+ pj_memcpy(&turn_sock->cred, cred, sizeof(turn_sock->cred));
|
||||
status = pj_turn_session_set_credential(turn_sock->sess, cred);
|
||||
if (status != PJ_SUCCESS) {
|
||||
sess_fail(turn_sock, "Error setting credential", status);
|
||||
@@ -676,11 +733,35 @@
|
||||
return PJ_EINVALIDOP;
|
||||
}
|
||||
|
||||
- PJ_UNUSED_ARG(dst_addr);
|
||||
- PJ_UNUSED_ARG(dst_addr_len);
|
||||
+ /* With TCP peer connection filter by address
|
||||
+ * if packet is for the server or the peer
|
||||
+ */
|
||||
+ pj_activesock_t *asock = NULL;
|
||||
+ pj_turn_session_info info;
|
||||
+ pj_turn_session_get_info(turn_sock->sess, &info);
|
||||
+ if (pj_sockaddr_cmp(&info.server, dst_addr) &&
|
||||
+ turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) {
|
||||
+ for (int i=0; i < turn_sock->tcp_cnx_count; ++i) {
|
||||
+ pj_turn_tcp_data_connection *tcp_cnx = &turn_sock->tcp_cnx[i];
|
||||
+ if ((tcp_cnx->flags & CONNECTION_READY) == 0)
|
||||
+ continue;
|
||||
+ if (!pj_sockaddr_cmp(&tcp_cnx->peer_addr, dst_addr)) {
|
||||
+ asock = tcp_cnx->active_tcp_sock;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (!asock) {
|
||||
+ status = PJ_ENOTFOUND;
|
||||
+ show_err(turn_sock, "socket send()", status);
|
||||
+ return status;
|
||||
+ }
|
||||
+ } else {
|
||||
+ asock = turn_sock->active_sock;
|
||||
+ }
|
||||
|
||||
- status = pj_activesock_send(turn_sock->active_sock, &turn_sock->send_key,
|
||||
+ status = pj_activesock_send(asock, &turn_sock->send_key,
|
||||
pkt, &len, 0);
|
||||
+
|
||||
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
|
||||
show_err(turn_sock, "socket send()", status);
|
||||
}
|
||||
@@ -927,4 +1008,365 @@
|
||||
}
|
||||
}
|
||||
|
||||
+static void turn_on_peer_connection(pj_turn_session *sess,
|
||||
+ pj_uint32_t conn_id,
|
||||
+ const pj_sockaddr_t *peer_addr,
|
||||
+ unsigned addr_len)
|
||||
+{
|
||||
+ pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess);
|
||||
+ if (turn_sock == NULL || turn_sock->is_destroying) {
|
||||
+ /* We've been destroyed */
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (turn_sock->cb.on_peer_connection) {
|
||||
+ (*turn_sock->cb.on_peer_connection)(turn_sock, conn_id,
|
||||
+ peer_addr, addr_len);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+PJ_DECL(pj_status_t) pj_turn_connect_peer(pj_turn_sock *turn_sock,
|
||||
+ pj_uint32_t conn_id,
|
||||
+ const pj_sockaddr_t *peer_addr,
|
||||
+ unsigned addr_len)
|
||||
+{
|
||||
+ pj_status_t status;
|
||||
+ pj_turn_tcp_data_connection *new_tcp_cnx = NULL;
|
||||
+
|
||||
+ for (int i=0; i < turn_sock->tcp_cnx_count; ++i) {
|
||||
+ pj_turn_tcp_data_connection *tcp_cnx = &turn_sock->tcp_cnx[i];
|
||||
+ if ((tcp_cnx->flags & CONNECTION_USED) == 0) {
|
||||
+ new_tcp_cnx = tcp_cnx;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (tcp_cnx->id == conn_id)
|
||||
+ // TODO: need log
|
||||
+ return PJ_EINVAL; // TODO: need better error code
|
||||
+ }
|
||||
+
|
||||
+ if (!new_tcp_cnx) {
|
||||
+ if (turn_sock->tcp_cnx_count == PJ_TURN_MAX_TCP_CNX) {
|
||||
+ // TODO: need log
|
||||
+ return PJ_ETOOMANY;
|
||||
+ }
|
||||
+ new_tcp_cnx = &turn_sock->tcp_cnx[turn_sock->tcp_cnx_count++];
|
||||
+ }
|
||||
+
|
||||
+ /* Initialize this TCP connection slot */
|
||||
+ pj_bzero(new_tcp_cnx, sizeof(*new_tcp_cnx));
|
||||
+ new_tcp_cnx->id = conn_id;
|
||||
+ new_tcp_cnx->flags = CONNECTION_USED;
|
||||
+ new_tcp_cnx->turn_sock = turn_sock;
|
||||
+ pj_sockaddr_cp(&new_tcp_cnx->peer_addr, peer_addr);
|
||||
+ new_tcp_cnx->peer_addr_len = addr_len;
|
||||
+
|
||||
+ pj_ioqueue_op_key_init(&new_tcp_cnx->send_key,
|
||||
+ sizeof(new_tcp_cnx->send_key));
|
||||
+
|
||||
+ /* Initiate a new TCP connection on TURN server
|
||||
+ * that will become the peer data connection */
|
||||
+ pj_turn_session_info info;
|
||||
+ int sock_type;
|
||||
+ pj_sock_t sock;
|
||||
+ pj_activesock_cfg asock_cfg;
|
||||
+ pj_activesock_cb asock_cb;
|
||||
+ pj_sockaddr bound_addr, *cfg_bind_addr;
|
||||
+ pj_uint16_t max_bind_retry;
|
||||
+
|
||||
+ /* Get server address from session info */
|
||||
+ pj_turn_session_get_info(turn_sock->sess, &info);
|
||||
+
|
||||
+ assert(turn_sock->conn_type == PJ_TURN_TP_TCP);
|
||||
+ sock_type = pj_SOCK_STREAM();
|
||||
+
|
||||
+ /* Init socket */
|
||||
+ status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_turn_sock_destroy(turn_sock);
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ /* Bind socket */
|
||||
+ cfg_bind_addr = &turn_sock->setting.bound_addr;
|
||||
+ max_bind_retry = MAX_BIND_RETRY;
|
||||
+ if (turn_sock->setting.port_range &&
|
||||
+ turn_sock->setting.port_range < max_bind_retry)
|
||||
+ {
|
||||
+ max_bind_retry = turn_sock->setting.port_range;
|
||||
+ }
|
||||
+ pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0);
|
||||
+ if (cfg_bind_addr->addr.sa_family == pj_AF_INET() ||
|
||||
+ cfg_bind_addr->addr.sa_family == pj_AF_INET6())
|
||||
+ {
|
||||
+ pj_sockaddr_cp(&bound_addr, cfg_bind_addr);
|
||||
+ }
|
||||
+ status = pj_sock_bind_random(sock, &bound_addr,
|
||||
+ turn_sock->setting.port_range,
|
||||
+ max_bind_retry);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_turn_sock_destroy(turn_sock);
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ /* Apply socket buffer size */
|
||||
+ if (turn_sock->setting.so_rcvbuf_size > 0) {
|
||||
+ unsigned sobuf_size = turn_sock->setting.so_rcvbuf_size;
|
||||
+ status = pj_sock_setsockopt_sobuf(sock, pj_SO_RCVBUF(),
|
||||
+ PJ_TRUE, &sobuf_size);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_perror(3, turn_sock->obj_name, status,
|
||||
+ "Failed setting SO_RCVBUF");
|
||||
+ } else {
|
||||
+ if (sobuf_size < turn_sock->setting.so_rcvbuf_size) {
|
||||
+ PJ_LOG(4, (turn_sock->obj_name,
|
||||
+ "Warning! Cannot set SO_RCVBUF as configured,"
|
||||
+ " now=%d, configured=%d", sobuf_size,
|
||||
+ turn_sock->setting.so_rcvbuf_size));
|
||||
+ } else {
|
||||
+ PJ_LOG(5, (turn_sock->obj_name, "SO_RCVBUF set to %d",
|
||||
+ sobuf_size));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (turn_sock->setting.so_sndbuf_size > 0) {
|
||||
+ unsigned sobuf_size = turn_sock->setting.so_sndbuf_size;
|
||||
+ status = pj_sock_setsockopt_sobuf(sock, pj_SO_SNDBUF(),
|
||||
+ PJ_TRUE, &sobuf_size);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_perror(3, turn_sock->obj_name, status,
|
||||
+ "Failed setting SO_SNDBUF");
|
||||
+ } else {
|
||||
+ if (sobuf_size < turn_sock->setting.so_sndbuf_size) {
|
||||
+ PJ_LOG(4, (turn_sock->obj_name,
|
||||
+ "Warning! Cannot set SO_SNDBUF as configured,"
|
||||
+ " now=%d, configured=%d", sobuf_size,
|
||||
+ turn_sock->setting.so_sndbuf_size));
|
||||
+ } else {
|
||||
+ PJ_LOG(5, (turn_sock->obj_name, "SO_SNDBUF set to %d",
|
||||
+ sobuf_size));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Create active socket */
|
||||
+ pj_activesock_cfg_default(&asock_cfg);
|
||||
+ asock_cfg.grp_lock = turn_sock->grp_lock;
|
||||
+
|
||||
+ pj_bzero(&asock_cb, sizeof(asock_cb));
|
||||
+ asock_cb.on_data_read = &on_peer_data_read;
|
||||
+ asock_cb.on_connect_complete = &on_peer_connect_complete;
|
||||
+ status = pj_activesock_create(turn_sock->pool, sock,
|
||||
+ sock_type, &asock_cfg,
|
||||
+ turn_sock->cfg.ioqueue, &asock_cb,
|
||||
+ new_tcp_cnx,
|
||||
+ &new_tcp_cnx->active_tcp_sock);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_turn_sock_destroy(turn_sock);
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ char addrtxt[PJ_INET6_ADDRSTRLEN+8];
|
||||
+ PJ_LOG(5,(turn_sock->pool->obj_name,
|
||||
+ "Connecting to %s",
|
||||
+ pj_sockaddr_print(&info.server, addrtxt,
|
||||
+ sizeof(addrtxt), 3)));
|
||||
+
|
||||
+ status = pj_activesock_start_connect(new_tcp_cnx->active_tcp_sock,
|
||||
+ turn_sock->pool,
|
||||
+ &info.server,
|
||||
+ pj_sockaddr_get_len(&info.server));
|
||||
+ if (status == PJ_SUCCESS) {
|
||||
+ on_peer_connect_complete(new_tcp_cnx->active_tcp_sock, PJ_SUCCESS);
|
||||
+ } else if (status != PJ_EPENDING) {
|
||||
+ pj_perror(3, turn_sock->pool->obj_name, status,
|
||||
+ "Failed to connect to %s",
|
||||
+ pj_sockaddr_print(&info.server, addrtxt,
|
||||
+ sizeof(addrtxt), 3));
|
||||
+ pj_turn_sock_destroy(turn_sock);
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ return PJ_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+static pj_bool_t on_peer_data_read(pj_activesock_t *asock,
|
||||
+ void *data,
|
||||
+ pj_size_t size,
|
||||
+ pj_status_t status,
|
||||
+ pj_size_t *remainder)
|
||||
+{
|
||||
+ pj_turn_tcp_data_connection *tcp_cnx;
|
||||
+ pj_turn_sock *turn_sock;
|
||||
+
|
||||
+ tcp_cnx = (pj_turn_tcp_data_connection*) pj_activesock_get_user_data(asock);
|
||||
+ pj_assert(tcp_cnx && tcp_cnx->turn_sock);
|
||||
+ turn_sock = tcp_cnx->turn_sock;
|
||||
+
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ // TODO: error handling
|
||||
+ char addrtxt[PJ_INET6_ADDRSTRLEN + 8];
|
||||
+ pj_perror(3, turn_sock->pool->obj_name, status,
|
||||
+ "Failed to read data from %s",
|
||||
+ pj_sockaddr_print(&tcp_cnx->peer_addr, addrtxt,
|
||||
+ sizeof(addrtxt), 3));
|
||||
+ return PJ_FALSE;
|
||||
+ }
|
||||
|
||||
+ pj_grp_lock_acquire(turn_sock->grp_lock);
|
||||
+
|
||||
+ *remainder = size;
|
||||
+ pj_uint8_t* pkt = data;
|
||||
+ while (*remainder > 0) {
|
||||
+ if ((tcp_cnx->flags & CONNECTION_READY) != 0) {
|
||||
+ if (turn_sock->cb.on_rx_data)
|
||||
+ turn_sock->cb.on_rx_data(turn_sock, pkt, *remainder,
|
||||
+ &tcp_cnx->peer_addr,
|
||||
+ tcp_cnx->peer_addr_len);
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ *remainder = 0;
|
||||
+ return PJ_TRUE;
|
||||
+ }
|
||||
+
|
||||
+ /* STUN session waiting for ConnectBind response */
|
||||
+ pj_size_t parsed_len;
|
||||
+ unsigned options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
|
||||
+ status = pj_stun_session_on_rx_pkt(tcp_cnx->stun_sess, pkt, size,
|
||||
+ options, NULL, &parsed_len,
|
||||
+ &tcp_cnx->peer_addr,
|
||||
+ tcp_cnx->peer_addr_len);
|
||||
+
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ // TODO: error handling
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ return PJ_FALSE;
|
||||
+ }
|
||||
+
|
||||
+ PJ_LOG(3, ("rfc6062",
|
||||
+ "parsed STUN msg (read %zu byte(s) over %zu), status=%u",
|
||||
+ parsed_len, size, status));
|
||||
+
|
||||
+ pkt += parsed_len;
|
||||
+ *remainder -= parsed_len;
|
||||
+ }
|
||||
+
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+
|
||||
+ return PJ_TRUE;
|
||||
+}
|
||||
+
|
||||
+static pj_bool_t on_peer_connect_complete(pj_activesock_t *asock,
|
||||
+ pj_status_t status)
|
||||
+{
|
||||
+ pj_turn_tcp_data_connection *tcp_cnx;
|
||||
+ pj_turn_sock *turn_sock;
|
||||
+
|
||||
+ tcp_cnx = (pj_turn_tcp_data_connection*) pj_activesock_get_user_data(asock);
|
||||
+ if (!tcp_cnx)
|
||||
+ return PJ_FALSE;
|
||||
+
|
||||
+ turn_sock = tcp_cnx->turn_sock;
|
||||
+ pj_assert(turn_sock);
|
||||
+
|
||||
+ pj_grp_lock_acquire(turn_sock->grp_lock);
|
||||
+
|
||||
+ PJ_LOG(3, ("rfc6062", "peer data connection %s", status == PJ_SUCCESS ? "ready" : "failed"));
|
||||
+
|
||||
+ // TODO: handle failures
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ return PJ_FALSE;
|
||||
+ }
|
||||
+
|
||||
+ /* start pending read operation */
|
||||
+ status = pj_activesock_start_read(asock, turn_sock->pool,
|
||||
+ turn_sock->setting.max_pkt_size, 0);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ // TODO: error handling
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ return PJ_FALSE;
|
||||
+ }
|
||||
+
|
||||
+ /* Create a temporary STUN session to send the ConnectBind request */
|
||||
+ pj_stun_session_cb stun_cb;
|
||||
+ pj_bzero(&stun_cb, sizeof(stun_cb));
|
||||
+ stun_cb.on_send_msg = &on_tcp_stun_send_msg;
|
||||
+ stun_cb.on_request_complete = &on_tcp_stun_request_complete;
|
||||
+ status = pj_stun_session_create(&turn_sock->cfg, NULL,
|
||||
+ &stun_cb, PJ_FALSE, NULL,
|
||||
+ &tcp_cnx->stun_sess);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ return PJ_FALSE;
|
||||
+ }
|
||||
+
|
||||
+ pj_stun_session_set_user_data(tcp_cnx->stun_sess, tcp_cnx);
|
||||
+
|
||||
+ /* Copy credentials from control connection */
|
||||
+ pj_stun_session_set_credential(tcp_cnx->stun_sess, PJ_STUN_AUTH_LONG_TERM, &turn_sock->cred);
|
||||
+ pj_str_t server_nonce, server_realm;
|
||||
+ pj_turn_session_get_server_cred(turn_sock->sess, turn_sock->pool, &server_nonce, &server_realm);
|
||||
+ pj_stun_session_set_server_cred(tcp_cnx->stun_sess, &server_nonce, &server_realm);
|
||||
+
|
||||
+ /* Send ConnectBind request */
|
||||
+ pj_stun_tx_data *tdata;
|
||||
+ status = pj_stun_session_create_req(tcp_cnx->stun_sess, PJ_STUN_CONNECTION_BIND_REQUEST,
|
||||
+ PJ_STUN_MAGIC, NULL, &tdata);
|
||||
+ if (status != PJ_SUCCESS) {
|
||||
+ pj_stun_session_destroy(tcp_cnx->stun_sess);
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ return PJ_FALSE;
|
||||
+ }
|
||||
+
|
||||
+ /* MUST include REQUESTED-TRANSPORT attribute */
|
||||
+ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_CONNECTION_ID, tcp_cnx->id);
|
||||
+
|
||||
+ PJ_LOG(3, ("rfc6062", "bind TCP connection id=%x", tcp_cnx->id));
|
||||
+ status = pj_stun_session_send_msg(tcp_cnx->stun_sess, tcp_cnx, PJ_FALSE, PJ_FALSE,
|
||||
+ &tcp_cnx->peer_addr, tcp_cnx->peer_addr_len,
|
||||
+ tdata);
|
||||
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
|
||||
+ pj_stun_session_destroy(tcp_cnx->stun_sess);
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ pj_grp_lock_release(turn_sock->grp_lock);
|
||||
+ return PJ_TRUE;
|
||||
+}
|
||||
+
|
||||
+static pj_status_t on_tcp_stun_send_msg(pj_stun_session *sess,
|
||||
+ void *token,
|
||||
+ const void *pkt,
|
||||
+ pj_size_t pkt_size,
|
||||
+ const pj_sockaddr_t *dst_addr,
|
||||
+ unsigned addr_len)
|
||||
+{
|
||||
+ pj_status_t status;
|
||||
+ pj_turn_tcp_data_connection *tcp_cnx = (pj_turn_tcp_data_connection*) token;
|
||||
+ pj_assert(tcp_cnx != NULL && tcp_cnx->turn_sock != NULL);
|
||||
+
|
||||
+ pj_grp_lock_acquire(tcp_cnx->turn_sock->grp_lock);
|
||||
+ status = pj_activesock_send(tcp_cnx->active_tcp_sock, &tcp_cnx->send_key, pkt, &pkt_size, 0);
|
||||
+ pj_grp_lock_release(tcp_cnx->turn_sock->grp_lock);
|
||||
+
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+static void on_tcp_stun_request_complete(pj_stun_session *sess,
|
||||
+ pj_status_t status,
|
||||
+ void *token,
|
||||
+ pj_stun_tx_data *tdata,
|
||||
+ const pj_stun_msg *response,
|
||||
+ const pj_sockaddr_t *src_addr,
|
||||
+ unsigned src_addr_len)
|
||||
+{
|
||||
+ pj_turn_tcp_data_connection *tcp_cnx = (pj_turn_tcp_data_connection*) token;
|
||||
+ pj_assert(tcp_cnx != NULL && tcp_cnx->turn_sock != NULL);
|
||||
+
|
||||
+ pj_grp_lock_acquire(tcp_cnx->turn_sock->grp_lock);
|
||||
+ pj_stun_session_destroy(tcp_cnx->stun_sess);
|
||||
+ tcp_cnx->stun_sess = NULL;
|
||||
+ tcp_cnx->flags |= CONNECTION_READY;
|
||||
+ PJ_LOG(3, ("rfc6062", "peer data connection bind %s", status == PJ_SUCCESS ? "succeed" : "failed"));
|
||||
+ pj_grp_lock_release(tcp_cnx->turn_sock->grp_lock);
|
||||
+}
|
@ -80,6 +80,7 @@ endif
|
||||
$(APPLY) $(SRC)/pjproject/fix_turn_fallback.patch
|
||||
$(APPLY) $(SRC)/pjproject/fix_ioqueue_ipv6_sendto.patch
|
||||
$(APPLY) $(SRC)/pjproject/add_dtls_transport.patch
|
||||
$(APPLY) $(SRC)/pjproject/rfc6062.patch
|
||||
$(UPDATE_AUTOCONFIG)
|
||||
$(MOVE)
|
||||
|
||||
|
Reference in New Issue
Block a user