diff --git a/contrib/src/pjproject/rfc6062.patch b/contrib/src/pjproject/rfc6062.patch new file mode 100644 index 0000000000000000000000000000000000000000..0c4fc73406bd6bd19c447782032f767cc7322de8 --- /dev/null +++ b/contrib/src/pjproject/rfc6062.patch @@ -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); ++} diff --git a/contrib/src/pjproject/rules.mak b/contrib/src/pjproject/rules.mak index 009699eac9e9e064212c8a82a34fc2f38ea48b0f..3710fb2b1ab1e970c7ad4ba4b3212c5c51b248d7 100644 --- a/contrib/src/pjproject/rules.mak +++ b/contrib/src/pjproject/rules.mak @@ -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)