diff --git a/contrib/src/pjproject/0001-android.patch b/contrib/src/pjproject/0001-android.patch new file mode 100644 index 0000000000000000000000000000000000000000..0e063cb97a1588ffb9b0a5101a2ec8542146b205 --- /dev/null +++ b/contrib/src/pjproject/0001-android.patch @@ -0,0 +1,75 @@ +From 34ca3353abf3e6bdaab4abb7d5b996421500f3e0 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Fri, 28 Feb 2020 12:33:24 -0500 +Subject: [PATCH] android + +--- + aconfigure | 3 --- + aconfigure.ac | 3 --- + pjlib/include/pj/config_site.h | 11 +++++++++++ + pjlib/src/pj/os_timestamp_posix.c | 2 +- + 4 files changed, 12 insertions(+), 7 deletions(-) + +diff --git a/aconfigure b/aconfigure +index 7fcb12ec4..066a1d0f4 100755 +--- a/aconfigure ++++ b/aconfigure +@@ -5954,9 +5954,6 @@ case $target in + esac + # UUID + case $target in +- *android*) +- ac_os_objs="$ac_os_objs guid_android.o" +- ;; + *) + if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then + ac_os_objs="$ac_os_objs guid_uuid.o" +diff --git a/aconfigure.ac b/aconfigure.ac +index 0367679e2..0280f6894 100644 +--- a/aconfigure.ac ++++ b/aconfigure.ac +@@ -489,9 +489,6 @@ case $target in + esac + # UUID + case $target in +- *android*) +- ac_os_objs="$ac_os_objs guid_android.o" +- ;; + *) + if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then + ac_os_objs="$ac_os_objs guid_uuid.o" +diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h +index 47dd40ad5..f8cf75f80 100644 +--- a/pjlib/include/pj/config_site.h ++++ b/pjlib/include/pj/config_site.h +@@ -21,3 +21,14 @@ + #define PJ_ICE_MAX_TURN 4 + #define PJ_ICE_COMP_BITS 2 + #define PJ_ICE_MAX_CHECKS 1024 ++ ++/* ++ * ANDROID settings. ++ */ ++#undef PJ_ANDROID ++#define PJ_ANDROID 0 ++#undef PJ_JNI_HAS_JNI_ONLOAD ++#define PJ_JNI_HAS_JNI_ONLOAD 0 ++#undef PJMEDIA_HAS_G722_CODEC ++#define PJMEDIA_HAS_G722_CODEC 1 ++#define PJMEDIA_VID_DEV_INFO_FMT_CNT 128 +diff --git a/pjlib/src/pj/os_timestamp_posix.c b/pjlib/src/pj/os_timestamp_posix.c +index 0aed8c470..1ec156b0d 100644 +--- a/pjlib/src/pj/os_timestamp_posix.c ++++ b/pjlib/src/pj/os_timestamp_posix.c +@@ -203,7 +203,7 @@ PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) + return PJ_SUCCESS; + } + +-#elif defined(__ANDROID__) ++#elif defined(PJ_ANDROID) && PJ_ANDROID + + #include <errno.h> + #include <time.h> +-- +2.24.1 + diff --git a/contrib/src/pjproject/0001-rfc6544.patch b/contrib/src/pjproject/0001-rfc6544.patch new file mode 100644 index 0000000000000000000000000000000000000000..182478456618d4502fe52b6f55af0c68a1091527 --- /dev/null +++ b/contrib/src/pjproject/0001-rfc6544.patch @@ -0,0 +1,3929 @@ +From bbf656dd46216c2551895abfb487b51ff786d1ae Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Wed, 26 Feb 2020 13:27:51 -0500 +Subject: [PATCH] rfc6544 + +--- + pjnath/include/pjnath/ice_session.h | 173 +++- + pjnath/include/pjnath/ice_strans.h | 21 + + pjnath/include/pjnath/stun_session.h | 75 +- + pjnath/include/pjnath/stun_sock.h | 67 +- + pjnath/include/pjnath/turn_sock.h | 11 + + pjnath/src/pjnath-test/concur_test.c | 5 +- + pjnath/src/pjnath-test/sess_auth.c | 14 +- + pjnath/src/pjnath-test/stun_sock_test.c | 7 +- + pjnath/src/pjnath/ice_session.c | 513 ++++++++++- + pjnath/src/pjnath/ice_strans.c | 727 ++++++++++++--- + pjnath/src/pjnath/nat_detect.c | 7 +- + pjnath/src/pjnath/stun_session.c | 15 +- + pjnath/src/pjnath/stun_sock.c | 1076 +++++++++++++++++++---- + pjnath/src/pjnath/stun_transaction.c | 3 + + pjnath/src/pjnath/turn_session.c | 3 +- + pjnath/src/pjnath/turn_sock.c | 17 + + pjnath/src/pjturn-client/client_main.c | 2 +- + pjnath/src/pjturn-srv/allocation.c | 3 +- + pjnath/src/pjturn-srv/server.c | 2 +- + pjsip-apps/src/samples/icedemo.c | 116 ++- + pjsip/src/pjsua-lib/pjsua_core.c | 2 +- + 21 files changed, 2481 insertions(+), 378 deletions(-) + +diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h +index 8971220f0..39c197c29 100644 +--- a/pjnath/include/pjnath/ice_session.h ++++ b/pjnath/include/pjnath/ice_session.h +@@ -163,6 +163,52 @@ typedef enum pj_ice_cand_type + + } pj_ice_cand_type; + ++/** ++ * ICE candidates types like described by RFC 6544. ++ */ ++typedef enum pj_ice_cand_transport { ++ /** ++ * Candidates UDP compatible ++ */ ++ PJ_CAND_UDP, ++ /** ++ * Candidates sending outgoing TCP connections ++ */ ++ PJ_CAND_TCP_ACTIVE, ++ /** ++ * Candidates accepting incoming TCP connections ++ */ ++ PJ_CAND_TCP_PASSIVE, ++ /** ++ * Candidates capable of receiving incoming connections and sending ++ * connections ++ */ ++ PJ_CAND_TCP_SO ++} pj_ice_cand_transport; ++ ++/** ++ * ICE transport types, which will be used both to specify the connection ++ * type for reaching candidates and other client ++ */ ++typedef enum pj_ice_tp_type { ++ /** ++ * UDP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_ICE_TP_UDP = 17, ++ ++ /** ++ * TCP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_ICE_TP_TCP = 6, ++ ++ /** ++ * TLS transport. The TLS transport will only be used as the connection ++ * type to reach the server and never as the allocation transport type. ++ */ ++ PJ_ICE_TP_TLS = 255 ++ ++} pj_ice_tp_type; ++ + + /** Forward declaration for pj_ice_sess */ + typedef struct pj_ice_sess pj_ice_sess; +@@ -309,6 +355,11 @@ typedef struct pj_ice_sess_cand + */ + pj_sockaddr rel_addr; + ++ /** ++ * Transport used (TCP or UDP) ++ */ ++ pj_ice_cand_transport transport; ++ + } pj_ice_sess_cand; + + +@@ -324,6 +375,22 @@ typedef enum pj_ice_sess_check_state + */ + PJ_ICE_SESS_CHECK_STATE_FROZEN, + ++ /** ++ * The following status is used when a packet sent via TURN got a ++ * "Connection reset by peer". This mean that the peer didn't allow ++ * us to connect yet. The socket will be reconnected during the next ++ * loop. ++ */ ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, ++ ++ /** ++ * TODO (sblin): REMOVE THIS! - https://github.com/coturn/coturn/issues/408 ++ * For now, this status is only used because sometimes, the first packet ++ * doesn't receive any response. So, we retry to send the packet every ++ * 50 loops. ++ */ ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ + /** + * A check has not been performed for this pair, and can be + * performed as soon as it is the highest priority Waiting pair on +@@ -331,6 +398,12 @@ typedef enum pj_ice_sess_check_state + */ + PJ_ICE_SESS_CHECK_STATE_WAITING, + ++ /** ++ * A check has not been performed for this pair, but TCP socket ++ * is currently connecting to the pair. Wait to finish the connection. ++ */ ++ PJ_ICE_SESS_CHECK_STATE_PENDING, ++ + /** + * A check has not been performed for this pair, and can be + * performed as soon as it is the highest priority Waiting pair on +@@ -520,6 +593,52 @@ typedef struct pj_ice_sess_cb + void *pkt, pj_size_t size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); ++ ++ /** ++ * Wait for TCP and send connectivity check ++ * ++ * @param ice The ICE session. ++ * @param clist The ICE connection list ++ * @param check_id The wanted check. ++ */ ++ pj_status_t (*wait_tcp_connection)(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++ ++ /** ++ * Select incoming TURN dataconn ++ * ++ * @param ice The ICE session. ++ * @param clist The ICE connection list ++ * @param check_id The wanted check. ++ */ ++ pj_status_t (*select_turn_dataconn)(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++ ++ /** ++ * Reconnect a resetted TCP connection and send connectivity check ++ * cf. PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY ++ * ++ * @param ice The ICE session. ++ * @param clist The ICE connection list ++ * @param check_id The wanted check. ++ */ ++ pj_status_t (*reconnect_tcp_connection)(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++ ++ /** ++ * Close TCP socket ++ * ++ * @param ice The ICE session. ++ * @param clist The ICE connection list ++ * @param check_id The wanted check. ++ */ ++ pj_status_t (*close_tcp_connection)(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++ + } pj_ice_sess_cb; + + +@@ -636,6 +755,7 @@ struct pj_ice_sess + pj_bool_t valid_pair_found; /**< First pair found */ + pj_status_t ice_status; /**< Error status. */ + pj_timer_entry timer; /**< ICE timer. */ ++ pj_timer_entry timer_connect; /**< ICE timer tcp timeout*/ + pj_ice_sess_cb cb; /**< Callback. */ + + pj_stun_config stun_cfg; /**< STUN settings. */ +@@ -855,6 +975,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, + * @param rel_addr Optional related address. + * @param addr_len Length of addresses. + * @param p_cand_id Optional pointer to receive the candidate ID. ++ * @param transport Candidate's type + * + * @return PJ_SUCCESS if candidate is successfully added. + */ +@@ -868,7 +989,8 @@ PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + const pj_sockaddr_t *base_addr, + const pj_sockaddr_t *rel_addr, + int addr_len, +- unsigned *p_cand_id); ++ unsigned *p_cand_id, ++ pj_ice_cand_transport transport); + + /** + * Find default candidate for the specified component ID, using this +@@ -980,6 +1102,55 @@ PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, + const pj_sockaddr_t *src_addr, + int src_addr_len); + ++/** ++ * Notification when ICE session get a new incoming connection ++ * ++ * @param ice The ICE session. ++ * @param transport_id Related transport ++ * @param status PJ_SUCCESS when connection is made, or any errors ++ * if the connection has failed (or if the peer has ++ * disconnected after an established connection). ++ * @param remote_addr Connected remove address ++ */ ++PJ_DECL(void) ice_sess_on_peer_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr); ++ ++/** ++ * Notification when ICE session get a new resetted connection ++ * cf PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY ++ * ++ * @param ice The ICE session. ++ * @param transport_id Related transport ++ * @param remote_addr Connected remove address ++ */ ++PJ_DECL(void) ice_sess_on_peer_reset_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr); ++ ++/** ++ * Notification when ICE session get a new packet ++ * Used to remove the PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET status ++ * ++ * @param ice The ICE session. ++ * @param transport_id Related transport ++ * @param remote_addr Connected remove address ++ */ ++PJ_DECL(void) ice_sess_on_peer_packet(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr); ++ ++/** ++ * Select dataconn from TURN ++ * ++ * @param ice The ICE session. ++ * @param clist The ice check list ++ * @param check_id The wanted check ++ */ ++PJ_DECL(void) ice_select_incoming_turn(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); + + + /** +diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h +index d0af76679..9eb74b35f 100644 +--- a/pjnath/include/pjnath/ice_strans.h ++++ b/pjnath/include/pjnath/ice_strans.h +@@ -274,6 +274,13 @@ typedef struct pj_ice_strans_stun_cfg + */ + pj_bool_t ignore_stun_error; + ++ /** ++ * Type of connection to the STUN server. ++ * ++ * Default is PJ_STUN_TP_UDP. ++ */ ++ pj_stun_tp_type conn_type; ++ + } pj_ice_strans_stun_cfg; + + +@@ -289,6 +296,13 @@ typedef struct pj_ice_strans_turn_cfg + */ + int af; + ++ /** ++ * If we want to use UDP or TCP as described by RFC 6544. ++ * This will discover candidates via TCP sockets. Then it will ++ * transfer messages on the transport via TCP. ++ */ ++ pj_ice_tp_type protocol; ++ + /** + * Optional TURN socket settings. The default values will be + * initialized by #pj_turn_sock_cfg_default(). This contains +@@ -368,6 +382,13 @@ typedef struct pj_ice_strans_cfg + */ + int af; + ++ /** ++ * If we want to use UDP or TCP as described by RFC 6544. ++ * This will discover candidates via TCP sockets. Then it will ++ * transfer messages on the transport via TCP. ++ */ ++ pj_ice_tp_type protocol; ++ + /** + * STUN configuration which contains the timer heap and + * ioqueue instance to be used, and STUN retransmission +diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h +index bee630ab4..3f2ecf739 100644 +--- a/pjnath/include/pjnath/stun_session.h ++++ b/pjnath/include/pjnath/stun_session.h +@@ -174,6 +174,29 @@ typedef struct pj_stun_rx_data pj_stun_rx_data; + /** Forward declaration for pj_stun_session */ + typedef struct pj_stun_session pj_stun_session; + ++/** ++ * STUN transport types, which will be used both to specify the connection ++ * type for reaching STUN server and the type of allocation transport to be ++ * requested to server (the REQUESTED-TRANSPORT attribute). ++ */ ++typedef enum pj_stun_tp_type { ++ /** ++ * UDP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_STUN_TP_UDP = 17, ++ ++ /** ++ * TCP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_STUN_TP_TCP = 6, ++ ++ /** ++ * TLS transport. The TLS transport will only be used as the connection ++ * type to reach the server and never as the allocation transport type. ++ */ ++ PJ_STUN_TP_TLS = 255 ++ ++} pj_stun_tp_type; + + /** + * This is the callback to be registered to pj_stun_session, to send +@@ -307,6 +330,38 @@ typedef struct pj_stun_session_cb + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + ++ /** ++ * Notification when STUN session get a ConnectionAttempt indication. ++ * ++ * @param stun_session The STUN session. ++ * @param status PJ_SUCCESS when connection is made, or any errors ++ * if the connection has failed (or if the peer has ++ * disconnected after an established connection). ++ * @param remote_addr The remote connected ++ */ ++ void (*on_peer_connection)(pj_stun_session *sess, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr); ++ ++ /** ++ * Notification when STUN connection is resetted (TCP only). ++ * ++ * @param stun_session The STUN session. ++ * @param remote_addr The remote resetted ++ */ ++ void (*on_peer_reset_connection)(pj_stun_session *sess, ++ pj_sockaddr_t* ++ remote_addr); ++ ++ /** ++ * Notification when STUN connection is resetted (TCP only). ++ * ++ * @param stun_session The STUN session. ++ * @param remote_addr The remote resetted ++ */ ++ void (*on_peer_packet)(pj_stun_session *sess, ++ pj_sockaddr_t* remote_addr); ++ + } pj_stun_session_cb; + + +@@ -390,6 +445,7 @@ typedef enum pj_stun_sess_msg_log_flag + * @param grp_lock Optional group lock to be used by this session. + * If NULL, the session will create one itself. + * @param p_sess Pointer to receive STUN session instance. ++ * @param conn_type If the session use UDP or TCP + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +@@ -398,7 +454,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg, + const pj_stun_session_cb *cb, + pj_bool_t fingerprint, + pj_grp_lock_t *grp_lock, +- pj_stun_session **p_sess); ++ pj_stun_session **p_sess, ++ pj_stun_tp_type conn_type); + + /** + * Destroy the STUN session and all objects created in the context of +@@ -752,6 +809,22 @@ PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + PJ_DECL(void) pj_stun_msg_destroy_tdata(pj_stun_session *sess, + pj_stun_tx_data *tdata); + ++/** ++ * ++ * @param sess The STUN session. ++ * ++ * @return The callback linked to the STUN session ++ */ ++PJ_DECL(pj_stun_session_cb *) pj_stun_session_callback(pj_stun_session *sess); ++ ++/** ++ * ++ * @param sess The STUN session. ++ * ++ * @return The connection type linked to the STUN session ++ */ ++PJ_DECL(pj_stun_tp_type) pj_stun_session_tp_type(pj_stun_session *sess); ++ + + /** + * @} +diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h +index fff4df885..bfc9c1415 100644 +--- a/pjnath/include/pjnath/stun_sock.h ++++ b/pjnath/include/pjnath/stun_sock.h +@@ -24,10 +24,14 @@ + * @file stun_sock.h + * @brief STUN aware socket transport + */ ++#include <pj/activesock.h> + #include <pjnath/stun_config.h> ++#include <pjnath/stun_session.h> + #include <pjlib-util/resolver.h> ++#include <pjlib-util/srv_resolver.h> + #include <pj/ioqueue.h> + #include <pj/lock.h> ++#include <pj/pool.h> + #include <pj/sock.h> + #include <pj/sock_qos.h> + +@@ -87,7 +91,17 @@ typedef enum pj_stun_sock_op + /** + * IP address change notification from the keep-alive operation. + */ +- PJ_STUN_SOCK_MAPPED_ADDR_CHANGE ++ PJ_STUN_SOCK_MAPPED_ADDR_CHANGE, ++ ++ /** ++ * STUN session was destroyed. ++ */ ++ PJ_STUN_SESS_DESTROYED, ++ ++ /** ++ * TCP fails to connect ++ */ ++ PJ_STUN_TCP_CONNECT_ERROR + + + } pj_stun_sock_op; +@@ -197,6 +211,11 @@ typedef struct pj_stun_sock_info + */ + pj_sockaddr mapped_addr; + ++ /** ++ * If connected, the remote address will be stored here. ++ */ ++ pj_sockaddr outgoing_addr; ++ + /** + * Number of interface address aliases. The interface address aliases + * are list of all interface addresses in this host. +@@ -208,6 +227,11 @@ typedef struct pj_stun_sock_info + */ + pj_sockaddr aliases[PJ_ICE_ST_MAX_CAND]; + ++ /** ++ * The tranport type of the socket ++ */ ++ pj_stun_tp_type conn_type; ++ + } pj_stun_sock_info; + + +@@ -343,6 +367,9 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); + * the operation of this transport. + * @param af Address family of socket. Currently pj_AF_INET() + * and pj_AF_INET6() are supported. ++ * @param conn_type Connection type to the STUN server. Both TCP and UDP are ++ * supported. ++ * + * @param name Optional name to be given to this transport to + * assist debugging. + * @param cb Callback to receive events/data from the transport. +@@ -357,6 +384,7 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); + PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg, + const char *name, + int af, ++ pj_stun_tp_type conn_type, + const pj_stun_sock_cb *cb, + const pj_stun_sock_cfg *cfg, + void *user_data, +@@ -485,6 +513,43 @@ PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock, + const pj_sockaddr_t *dst_addr, + unsigned addr_len); + ++ ++#if PJ_HAS_TCP ++/** ++ * Connect active socket to remote address ++ * @param stun_sock ++ * @param remote_addr the destination ++ * @param af address family ++ */ ++PJ_DECL(pj_status_t) pj_stun_sock_connect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af); ++ ++/** ++ * Connect active socket to remote address ++ * @param stun_sock ++ * @param remote_addr the destination ++ * @param af address family ++ */ ++PJ_DECL(pj_status_t) pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af); ++ ++/** ++ * Close active socket ++ * @param stun_sock ++ * @param remote_addr The remote address linked ++ */ ++PJ_DECL(pj_status_t) pj_stun_sock_close(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr); ++ ++#endif ++ ++/** ++ * Retrieve the linked session ++ * @param stun_sock ++ */ ++PJ_DECL(pj_stun_session *) pj_stun_sock_get_session(pj_stun_sock *stun_sock); + /** + * @} + */ +diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h +index e4d306174..35388809f 100644 +--- a/pjnath/include/pjnath/turn_sock.h ++++ b/pjnath/include/pjnath/turn_sock.h +@@ -623,6 +623,17 @@ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock, + const pj_sockaddr_t *peer, + unsigned addr_len); + ++/** ++ * Check if peer is a dataconn ++ * ++ * @param turn_sock The turn sock ++ * @param peer The peer addr to check ++ * ++ * @return true if dataconn else false ++ */ ++PJ_DECL(pj_bool_t) pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, ++ const pj_sockaddr_t *peer); ++ + + /** + * @} +diff --git a/pjnath/src/pjnath-test/concur_test.c b/pjnath/src/pjnath-test/concur_test.c +index c3013d2ab..7777c637c 100644 +--- a/pjnath/src/pjnath-test/concur_test.c ++++ b/pjnath/src/pjnath-test/concur_test.c +@@ -184,8 +184,9 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess) + char name[10]; + sprintf(name, "stun%02d", i); + status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(), +- &stun_cb, NULL, test_sess, +- &stun_sock[i]); ++ PJ_STUN_TP_UDP, ++ &stun_cb, NULL, test_sess, ++ &stun_sock[i]); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, "Error creating stun socket")); + return -10; +diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c +index 055eaad61..1d07bf299 100644 +--- a/pjnath/src/pjnath-test/sess_auth.c ++++ b/pjnath/src/pjnath-test/sess_auth.c +@@ -248,7 +248,8 @@ static int create_std_server(pj_stun_auth_type auth_type, + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_rx_request = &server_on_rx_request; + sess_cb.on_send_msg = &server_send_msg; +- status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, NULL, &server->sess); ++ status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, ++ NULL, &server->sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + destroy_server(); + return -10; +@@ -489,7 +490,8 @@ static int run_client_test(const char *title, + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_request_complete = &client_on_request_complete; + sess_cb.on_send_msg = &client_send_msg; +- status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess); ++ status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, ++ NULL, &client->sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -200; +@@ -575,8 +577,12 @@ static int run_client_test(const char *title, + } + + /* Send the request */ +- status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr, +- pj_sockaddr_get_len(&server->addr), tdata); ++ status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, ++ (pj_stun_session_tp_type(client->sess) == ++ PJ_STUN_TP_UDP), ++ &server->addr, ++ pj_sockaddr_get_len(&server->addr), ++ tdata); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -270; +diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c +index fff4fad26..e7f8b84eb 100644 +--- a/pjnath/src/pjnath-test/stun_sock_test.c ++++ b/pjnath/src/pjnath-test/stun_sock_test.c +@@ -255,8 +255,9 @@ static pj_status_t create_client(pj_stun_config *cfg, + pj_bzero(&cb, sizeof(cb)); + cb.on_status = &stun_sock_on_status; + cb.on_rx_data = &stun_sock_on_rx_data; +- status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), &cb, &sock_cfg, +- client, &client->sock); ++ status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), ++ PJ_STUN_TP_UDP, ++ &cb, &sock_cfg, client, &client->sock); + if (status != PJ_SUCCESS) { + app_perror(" pj_stun_sock_create()", status); + pj_pool_release(pool); +@@ -585,7 +586,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3))); + } + status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret), +- 0, &info.srv_addr, ++ 0, &info.srv_addr, + pj_sockaddr_get_len(&info.srv_addr)); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror(" error: server sending data", status); +diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c +index 2a4125bc5..cd9e32e25 100644 +--- a/pjnath/src/pjnath/ice_session.c ++++ b/pjnath/src/pjnath/ice_session.c +@@ -18,6 +18,7 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include <pjnath/ice_session.h> ++#include <pjnath/stun_session.h> + #include <pj/addr_resolv.h> + #include <pj/array.h> + #include <pj/assert.h> +@@ -44,7 +45,10 @@ static const char *cand_type_names[] = + static const char *check_state_name[] = + { + "Frozen", ++ "Needs Retry", ++ "Needs First Packet", + "Waiting", ++ "Pending", + "In Progress", + "Succeeded", + "Failed" +@@ -75,7 +79,8 @@ enum timer_type + valid check for every components. */ + TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity + checks with USE-CANDIDATE flag. */ +- TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */ ++ TIMER_KEEP_ALIVE, /**< ICE keep-alive timer. */ ++ TIMER_CONNECTION_TIMEOUT + + }; + +@@ -123,6 +128,8 @@ typedef struct timer_data + { + pj_ice_sess *ice; + pj_ice_sess_checklist *clist; ++ /* TODO (remove), for now, needed for the NEEDS_FIRST_PACKET state */ ++ unsigned first_packet_counter; + } timer_data; + + +@@ -133,6 +140,7 @@ typedef struct timer_data + + /* Forward declarations */ + static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te); ++static void on_tcp_connect_timeout(pj_ice_sess *ice); + static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); + static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now); + static void ice_on_destroy(void *obj); +@@ -291,7 +299,8 @@ static pj_status_t init_comp(pj_ice_sess *ice, + status = pj_stun_session_create(&ice->stun_cfg, NULL, + &sess_cb, PJ_TRUE, + ice->grp_lock, +- &comp->stun_sess); ++ &comp->stun_sess, ++ PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) + return status; + +@@ -717,7 +726,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + const pj_sockaddr_t *base_addr, + const pj_sockaddr_t *rel_addr, + int addr_len, +- unsigned *p_cand_id) ++ unsigned *p_cand_id, ++ pj_ice_cand_transport transport) + { + pj_ice_sess_cand *lcand; + pj_status_t status = PJ_SUCCESS; +@@ -740,6 +750,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + lcand->comp_id = (pj_uint8_t)comp_id; + lcand->transport_id = (pj_uint8_t)transport_id; + lcand->type = type; ++ lcand->transport = transport; + pj_strdup(ice->pool, &lcand->foundation, foundation); + lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); + pj_sockaddr_cp(&lcand->addr, addr); +@@ -1081,6 +1092,17 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, + return PJNATH_EICENOHOSTCAND; + } + } ++ ++ /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) ++ * When the agent prunes the check list, it MUST also remove any pair ++ * for which the local candidate is a passive TCP candidate ++ */ ++ if (clist->checks[i].lcand->transport == PJ_CAND_TCP_PASSIVE) { ++ pj_array_erase(clist->checks, sizeof(clist->checks[0]), ++ clist->count, i); ++ --clist->count; ++ --i; ++ } + } + + /* Next remove a pair if its local and remote candidates are identical +@@ -1183,6 +1205,9 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) + case TIMER_KEEP_ALIVE: + ice_keep_alive(ice, PJ_TRUE); + break; ++ case TIMER_CONNECTION_TIMEOUT: ++ on_tcp_connect_timeout(ice); ++ break; + case TIMER_NONE: + /* Nothing to do, just to get rid of gcc warning */ + break; +@@ -1619,6 +1644,43 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, + return PJ_FALSE; + } + ++static void on_tcp_connect_timeout(pj_ice_sess* ice) ++{ ++ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,&ice->timer, ++ TIMER_NONE); ++ ++ pj_bool_t first_found = PJ_FALSE, set_timer = PJ_FALSE; ++ ++ for (int i = 0; i<ice->clist.count && !set_timer; ++i) { ++ pj_ice_sess_check *check = &ice->clist.checks[i]; ++ if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { ++ if (first_found) { ++ set_timer = PJ_TRUE; ++ } else { ++ first_found = PJ_TRUE; ++ if (*ice->cb.close_tcp_connection) ++ (*ice->cb.close_tcp_connection)(ice, &ice->clist, i); ++ ++ check_set_state(ice, check, ++ PJ_ICE_SESS_CHECK_STATE_FAILED, PJ_ECANCELLED); ++ on_check_complete(ice, check); ++ } ++ } ++ } ++ ++ if (set_timer && ice->timer.id == TIMER_NONE) { ++ /* Reschedule */ ++ pj_time_val delay = { ++ .sec = 0, ++ .msec = 1500 ++ }; ++ pj_time_val_normalize(&delay); ++ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, ++ &ice->timer, &delay, ++ TIMER_CONNECTION_TIMEOUT, ++ ice->grp_lock); ++ } ++} + + /* Create checklist by pairing local candidates with remote candidates */ + PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( +@@ -1705,6 +1767,30 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( + continue; + } + ++ /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) ++ * As with UDP, check lists are formed only by full ICE implementations. ++ * When forming candidate pairs, the following types of TCP candidates ++ * can be paired with each other: ++ * ++ * Local Remote ++ * Candidate Candidate ++ * --------------------------- ++ * tcp-so tcp-so ++ * tcp-active tcp-passive ++ * tcp-passive tcp-active ++ */ ++ if ((lcand->transport == PJ_CAND_UDP && ++ rcand->transport != PJ_CAND_UDP) || ++ (lcand->transport == PJ_CAND_TCP_PASSIVE && ++ rcand->transport != PJ_CAND_TCP_ACTIVE) || ++ (lcand->transport == PJ_CAND_TCP_ACTIVE && ++ rcand->transport != PJ_CAND_TCP_PASSIVE) || ++ (lcand->transport == PJ_CAND_TCP_SO && ++ rcand->transport != PJ_CAND_TCP_SO)) ++ { ++ continue; ++ } ++ + + chk->lcand = lcand; + chk->rcand = rcand; +@@ -1749,6 +1835,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( + td = PJ_POOL_ZALLOC_T(ice->pool, timer_data); + td->ice = ice; + td->clist = clist; ++ td->first_packet_counter = 1; + clist->timer.user_data = (void*)td; + clist->timer.cb = &periodic_timer; + +@@ -1761,6 +1848,36 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( + return PJ_SUCCESS; + } + ++static pj_status_t send_connectivity_check(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id, ++ pj_bool_t nominate, ++ pj_ice_msg_data *msg_data) ++{ ++ pj_ice_sess_check *check; ++ const pj_ice_sess_cand *lcand; ++ const pj_ice_sess_cand *rcand; ++ pj_ice_sess_comp *comp; ++ ++ check = &clist->checks[check_id]; ++ lcand = check->lcand; ++ rcand = check->rcand; ++ comp = find_comp(ice, lcand->comp_id); ++ ++ /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the ++ * STUN session. ++ */ ++ ++ /* Initiate STUN transaction to send the request */ ++ ++ return pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, ++ pj_stun_session_tp_type(comp->stun_sess)== ++ PJ_STUN_TP_UDP, ++ &rcand->addr, ++ pj_sockaddr_get_len(&rcand->addr), ++ check->tdata); ++} ++ + /* Perform check on the specified candidate pair. */ + static pj_status_t perform_check(pj_ice_sess *ice, + pj_ice_sess_checklist *clist, +@@ -1771,19 +1888,17 @@ static pj_status_t perform_check(pj_ice_sess *ice, + pj_ice_msg_data *msg_data; + pj_ice_sess_check *check; + const pj_ice_sess_cand *lcand; +- const pj_ice_sess_cand *rcand; + pj_uint32_t prio; + pj_status_t status; + + check = &clist->checks[check_id]; + lcand = check->lcand; +- rcand = check->rcand; + comp = find_comp(ice, lcand->comp_id); + ++ pj_log_push_indent(); + LOG5((ice->obj_name, + "Sending connectivity check for check %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); +- pj_log_push_indent(); + + /* Create request */ + status = pj_stun_session_create_req(comp->stun_sess, +@@ -1831,32 +1946,92 @@ static pj_status_t perform_check(pj_ice_sess *ice, + &ice->tie_breaker); + + } else { ++ if (nominate) { ++ check->nominated = PJ_TRUE; ++ } + pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_ICE_CONTROLLED, + &ice->tie_breaker); + } + ++#if PJ_HAS_TCP ++ switch (lcand->transport) { ++ case PJ_CAND_UDP: ++ status = send_connectivity_check(ice, clist, check_id, ++ nominate, msg_data); ++ break; ++ case PJ_CAND_TCP_ACTIVE: ++ switch (check->state) { ++ case PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY: ++ status = (*ice->cb.reconnect_tcp_connection)(ice, clist, check_id); ++ break; ++ case PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET: ++ status = send_connectivity_check(ice, clist, check_id, ++ nominate, msg_data); ++ break; ++ default: ++ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, ++ &ice->timer, TIMER_NONE); ++ status = (*ice->cb.wait_tcp_connection)(ice, clist, check_id); ++ if (ice->timer.id != TIMER_NONE) { ++ pj_assert(!"Not expected any timer active"); ++ } else { ++ pj_time_val delay = { ++ .sec = 0, ++ .msec = 1500, ++ }; ++ pj_time_val_normalize(&delay); ++ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, ++ &ice->timer, &delay, ++ TIMER_CONNECTION_TIMEOUT, ++ ice->grp_lock); ++ } ++ break; ++ } ++ break; ++ case PJ_CAND_TCP_PASSIVE: ++ case PJ_CAND_TCP_SO: ++ default: ++ if (lcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, ++ &ice->timer, TIMER_NONE); ++ status = (*ice->cb.select_turn_dataconn)(ice, clist, check_id); ++ if (ice->timer.id == TIMER_NONE) { ++ pj_time_val delay = { ++ .sec = 0, ++ .msec = 1500 ++ }; ++ pj_time_val_normalize(&delay); ++ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, ++ &ice->timer, &delay, ++ TIMER_CONNECTION_TIMEOUT, ++ ice->grp_lock); ++ } ++ } else { ++ status = send_connectivity_check(ice, clist, check_id, ++ nominate, msg_data); ++ } ++ break; ++ } ++#else ++ status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); ++#endif + +- /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the +- * STUN session. +- */ +- +- /* Initiate STUN transaction to send the request */ +- status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, +- PJ_TRUE, &rcand->addr, +- pj_sockaddr_get_len(&rcand->addr), +- check->tdata); +- if (status != PJ_SUCCESS) { ++ if (status == PJ_SUCCESS) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, ++ status); ++ } else if (status == PJ_EPENDING) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_PENDING, status); ++ } else if (check->rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ /* TODO (sblin) remove this - https://github.com/coturn/coturn/issues/408 */ ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ status); ++ } else { + check->tdata = NULL; + pjnath_perror(ice->obj_name, "Error sending STUN request", status); +- pj_log_pop_indent(); +- return status; + } +- +- check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, +- PJ_SUCCESS); + pj_log_pop_indent(); +- return PJ_SUCCESS; ++ return status; + } + + +@@ -1892,40 +2067,96 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, + LOG5((ice->obj_name, "Starting checklist periodic check")); + pj_log_push_indent(); + ++ /* Send STUN Binding request for check with highest priority on ++ * Retry state. ++ */ ++ ++ if (start_count == 0) { ++ for (i = 0; i < clist->count; ++i) { ++ pj_ice_sess_check *check = &clist->checks[i]; ++ // Reconnect closed TURN sockets ++ if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) { ++ status = perform_check(ice, clist, i, ice->is_nominating); ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, ++ status); ++ on_check_complete(ice, check); ++ } ++ ++start_count; ++ break; ++ } ++ } ++ } ++ ++ if (start_count == 0) { ++ // TODO (sblin) remove - https://github.com/coturn/coturn/issues/408 ++ pj_bool_t inc_counter = PJ_TRUE; ++ for (i = 0; i < clist->count; ++i) { ++ pj_ice_sess_check *check = &clist->checks[i]; ++ if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET) { ++ if (inc_counter) { ++ td->first_packet_counter += 1; ++ inc_counter = PJ_FALSE; ++ } ++ if (td->first_packet_counter % 50 == 0) { ++ status = perform_check(ice, clist, i, ice->is_nominating); ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, ++ status); ++ on_check_complete(ice, check); ++ } ++ } ++ ++start_count; ++ break; ++ } ++ } ++ } ++ + /* Send STUN Binding request for check with highest priority on + * Waiting state. + */ +- for (i=0; i<clist->count; ++i) { +- pj_ice_sess_check *check = &clist->checks[i]; + +- if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { +- status = perform_check(ice, clist, i, ice->is_nominating); +- if (status != PJ_SUCCESS) { +- check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, +- status); +- on_check_complete(ice, check); ++ if (start_count == 0) { ++ for (i = 0; i < clist->count; ++i) { ++ pj_ice_sess_check *check = &clist->checks[i]; ++ ++ if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { ++ status = perform_check(ice, clist, i, ice->is_nominating); ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, ++ status); ++ on_check_complete(ice, check); ++ } ++ ++start_count; ++ break; + } +- +- ++start_count; +- break; + } + } + + /* If we don't have anything in Waiting state, perform check to + * highest priority pair that is in Frozen state. + */ +- if (start_count==0) { +- for (i=0; i<clist->count; ++i) { ++ if (start_count == 0) { ++ for (i = 0; i < clist->count; ++i) { + pj_ice_sess_check *check = &clist->checks[i]; + + if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { + status = perform_check(ice, clist, i, ice->is_nominating); +- if (status != PJ_SUCCESS) { +- check_set_state(ice, check, +- PJ_ICE_SESS_CHECK_STATE_FAILED, status); ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + } ++ ++start_count; ++ break; ++ } ++ } ++ } + ++ if (start_count == 0) { ++ // If all sockets are pending, do nothing ++ for (i = 0; i < clist->count; ++i) { ++ pj_ice_sess_check *check = &clist->checks[i]; ++ if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { + ++start_count; + break; + } +@@ -1933,14 +2164,14 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, + } + + /* Cannot start check because there's no suitable candidate pair. +- */ ++ */ + if (start_count!=0) { + /* Schedule for next timer */ + pj_time_val timeout = {0, PJ_ICE_TA_VAL}; + + pj_time_val_normalize(&timeout); + pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE, +- ice->grp_lock); ++ ice->grp_lock); + } + + pj_grp_lock_release(ice->grp_lock); +@@ -2181,6 +2412,192 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, + return status; + } + ++static pj_ice_sess_check* get_current_check_at_state(pj_ice_sess *ice, ++ pj_sockaddr_t *remote_addr, ++ pj_ice_sess_check_state state, ++ int *current_check) ++{ ++ if (!ice || !remote_addr) ++ return NULL; ++ // NOTE: Multiple checks can have the same remote, we only take care of the first ++ // First, check if the TCP is really connected. If not, abort ++ pj_ice_sess_check *check = NULL; ++ for (int i = 0; i < ice->clist.count; ++i) { ++ // Find related check ++ pj_ice_sess_check *c = &ice->clist.checks[i]; ++ /* Host candidate not found this this srflx! */ ++ if (pj_sockaddr_cmp(remote_addr, &c->rcand->addr) == 0) { ++ if (c->tdata == NULL || c->state != state) ++ continue; ++ /* Match */ ++ check = c; ++ if (current_check) *current_check = i; ++ break; ++ } ++ } ++ return check; ++} ++ ++void ice_sess_on_peer_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr) ++{ ++ // The TCP link is now ready. We can now send the first STUN message (send ++ // connectivity check) This should trigger on_stun_request_complete when ++ // finished ++ if (!remote_addr) ++ return; ++ ++ int current_check = -1; ++ pj_ice_sess_check *check = get_current_check_at_state(ice,remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_PENDING, ++ ¤t_check); ++ if (!check) { ++ // Handle peer reflexive candidates (incoming are still waiting here) ++ check = get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_WAITING, ++ ¤t_check); ++ if (!check) { ++ return; ++ } ++ } ++ ++ if (status != PJ_SUCCESS) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); ++ on_check_complete(ice, check); ++ return; ++ } ++ ++ // TCP is correctly connected. Craft the message to send ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ if (check->tdata == NULL) { ++ LOG5((ice->obj_name, "Error sending STUN request, empty data")); ++ return; ++ } ++ pj_ice_msg_data *msg_data = ++ PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_msg_data); ++ ++ msg_data->transport_id = transport_id; ++ msg_data->has_req_data = PJ_TRUE; ++ msg_data->data.req.ice = ice; ++ msg_data->data.req.clist = &ice->clist; ++ msg_data->data.req.ckid = current_check; ++ ++ pj_ice_sess_comp *comp = find_comp(ice, lcand->comp_id); ++ pj_status_t status_send_msg; ++ // Note that USERNAME and MESSAGE-INTEGRITY will be added by the ++ // STUN session. ++ ++ // Initiate STUN transaction to send the request ++ status_send_msg = pj_stun_session_send_msg(comp->stun_sess, msg_data, ++ PJ_FALSE, PJ_FALSE, &rcand->addr, ++ pj_sockaddr_get_len(&rcand->addr), ++ check->tdata); ++ if (status_send_msg == PJ_EBUSY /* EBUSY */) { ++ check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET; ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ status_send_msg); ++ return; ++ } ++ if (status_send_msg == 120033 /* BROKEN PIPE */) { ++ check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY; ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, ++ status_send_msg); ++ return; ++ } ++ ++ if ((status_send_msg == 120104 || status_send_msg == 130054)/* CONNECTION RESET BY PEER */ ++ && rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ /** ++ * This part of the code is triggered when using ICE over TCP via TURN ++ * In fact, the other peer has to authorize this peer to connect to ++ * the relayed candidate. This is done by set_perm from the other case. ++ * But from this side, we can't know if the peer has authorized us. If it's ++ * not the case, the connection will got a CONNECTION RESET BY PEER status. ++ * In this case, we can try to reconnect a bit after and this until the check ++ * reached its timeout. ++ */ ++ check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY; ++ check_set_state(ice, check,PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, ++ status_send_msg); ++ } else if (status_send_msg != PJ_SUCCESS) { ++ check->tdata = NULL; ++ pjnath_perror(ice->obj_name, "Error sending STUN request", status_send_msg); ++ pj_log_pop_indent(); ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); ++ on_check_complete(ice, check); ++ } else if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, status); ++ } else { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, status); ++ } ++} ++ ++void ice_sess_on_peer_reset_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr) ++{ ++ // The TCP link is reseted ++ if (!remote_addr) ++ return; ++ ++ pj_ice_sess_check *check = get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_PENDING, ++ NULL); ++ if (!check) { ++ // Just check if it's not the first packet failing ++ check = get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ NULL); ++ if (!check) ++ return; ++ } ++ ++ const pj_ice_sess_cand *rcand = check->rcand; ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY; ++ check_set_state(ice, check, ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, 120104); ++ } ++} ++ ++void ice_sess_on_peer_packet(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr) ++{ ++ // The TCP link received its bind request response ++ if (!ice || !remote_addr) { ++ return; ++ } ++ pj_ice_sess_check *check = ++ get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ NULL); ++ if (!check) { ++ return; ++ } ++ ++ const pj_ice_sess_cand *rcand = check->rcand; ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ check->state = PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS; ++ check_set_state(ice, check, ++ PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS); ++ } ++} ++ ++ ++void ice_select_incoming_turn(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id) ++{ ++ check_set_state(ice, &clist->checks[check_id], ++ PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, PJ_SUCCESS); ++ update_comp_check(ice, clist->checks[check_id].lcand->comp_id, ++ &clist->checks[check_id]); ++ on_check_complete(ice, &clist->checks[check_id]); ++} + + /* This callback is called when outgoing STUN request completed */ + static void on_stun_request_complete(pj_stun_session *stun_sess, +@@ -2411,7 +2828,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + &check->lcand->base_addr, + &check->lcand->base_addr, + pj_sockaddr_get_len(&xaddr->sockaddr), +- &cand_id); ++ &cand_id, ++ check->rcand->transport == PJ_CAND_UDP ? ++ PJ_CAND_UDP : PJ_CAND_TCP_PASSIVE); + if (status != PJ_SUCCESS) { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, + status); +@@ -2673,7 +3092,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + msg_data->has_req_data = PJ_FALSE; + + /* Send the response */ +- status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE, ++ status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, ++ pj_stun_session_tp_type(sess) == ++ PJ_STUN_TP_UDP, + src_addr, src_addr_len, tdata); + + +@@ -2795,11 +3216,11 @@ static void handle_incoming_check(pj_ice_sess *ice, + * for the specified component ID in the checklist. + */ + for (i=0; i<ice->clist.count; ++i) { +- pj_ice_sess_check *c = &ice->clist.checks[i]; +- if (c->lcand->comp_id == rcheck->comp_id && +- c->lcand->transport_id == rcheck->transport_id) ++ pj_ice_sess_cand* lcand_tmp = &ice->lcand[i]; ++ if (lcand_tmp->comp_id == rcheck->comp_id && ++ lcand_tmp->transport_id == rcheck->transport_id) + { +- lcand = c->lcand; ++ lcand = lcand_tmp; + break; + } + } +diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c +index 3cb350c2a..1cb6a6d14 100644 +--- a/pjnath/src/pjnath/ice_strans.c ++++ b/pjnath/src/pjnath/ice_strans.c +@@ -69,6 +69,7 @@ enum tp_type + # define RELAY_PREF 0 + #endif + ++#define MAX_RTP_SIZE 65536 + + /* The candidate type preference when STUN candidate is used */ + static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = +@@ -87,6 +88,14 @@ static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = + #endif + }; + ++////////////////////////////////////////////////////////////////////////////// ++ ++static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2) ++{ ++ return (pj_uint16_t) ((buf1[0] << 8) | (buf2[0] << 0)); ++} ++ ++////////////////////////////////////////////////////////////////////////////// + + /* ICE callbacks */ + static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); +@@ -103,6 +112,24 @@ static void ice_rx_data(pj_ice_sess *ice, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + ++#if PJ_HAS_TCP ++static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++ ++static pj_status_t ice_select_turn_dataconn(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++ ++static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++ ++static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id); ++#endif ++ + + /* STUN socket callbacks */ + /* Notification when incoming packet has been received. */ +@@ -182,6 +209,16 @@ typedef struct pj_ice_strans_comp + } pj_ice_strans_comp; + + ++static pj_bool_t add_local_candidate(pj_ice_sess_cand *cand, ++ unsigned idx, ++ unsigned i, ++ unsigned *cand_cnt, ++ unsigned *max_cand_cnt, ++ pj_stun_sock_info stun_sock_info, ++ pj_ice_strans *ice_st, ++ pj_ice_strans_comp *comp, ++ pj_ice_cand_transport transport); ++ + /* Pending send buffer */ + typedef struct pending_send + { +@@ -225,6 +262,12 @@ struct pj_ice_strans + pj_bool_t destroy_req;/**< Destroy has been called? */ + pj_bool_t cb_called; /**< Init error callback called?*/ + pj_bool_t call_send_cb;/**< Need to call send cb? */ ++ ++ pj_uint8_t rtp_pkt[MAX_RTP_SIZE]; ++ pj_uint8_t rx_buffer[MAX_RTP_SIZE]; ++ pj_uint16_t rx_buffer_size; ++ pj_uint16_t rx_wanted_size; ++ + }; + + +@@ -261,6 +304,7 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) + pj_bzero(cfg, sizeof(*cfg)); + + cfg->af = pj_AF_INET(); ++ cfg->protocol = PJ_ICE_TP_UDP; + pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL); + pj_ice_strans_stun_cfg_default(&cfg->stun); + pj_ice_strans_turn_cfg_default(&cfg->turn); +@@ -278,6 +322,7 @@ PJ_DEF(void) pj_ice_strans_stun_cfg_default(pj_ice_strans_stun_cfg *cfg) + pj_bzero(cfg, sizeof(*cfg)); + + cfg->af = pj_AF_INET(); ++ cfg->conn_type = PJ_STUN_TP_UDP; + cfg->port = PJ_STUN_PORT; + cfg->max_host_cands = 64; + cfg->ignore_stun_error = PJ_FALSE; +@@ -421,6 +466,9 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, + cand->transport_id = tp_id; + cand->comp_id = (pj_uint8_t) comp->comp_id; + new_cand = PJ_TRUE; ++ cand->transport = turn_cfg->conn_type == PJ_TURN_TP_UDP ? ++ PJ_CAND_UDP : ++ PJ_CAND_TCP_PASSIVE; + } + + /* Allocate and initialize TURN socket data */ +@@ -428,6 +476,10 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, + data->comp = comp; + data->transport_id = cand->transport_id; + ++ if (turn_cfg->conn_type == PJ_TURN_TP_TCP) { ++ turn_cfg->alloc_param.peer_conn_type = PJ_TURN_TP_TCP; ++ } ++ + /* Create the TURN transport */ + status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, turn_cfg->af, + turn_cfg->conn_type, +@@ -481,6 +533,7 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, + || lcand->transport_id != rcand->transport_id + || lcand->local_pref != rcand->local_pref + || lcand->prio != rcand->prio ++ || lcand->transport != rcand->transport + || pj_sockaddr_cmp(&lcand->addr, &rcand->addr) != 0 + || pj_sockaddr_cmp(&lcand->base_addr, &rcand->base_addr) != 0) + { +@@ -490,7 +543,6 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, + return PJ_TRUE; + } + +- + static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + pj_ice_strans_comp *comp, + unsigned idx, +@@ -541,6 +593,9 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + cand->local_pref = SRFLX_PREF; + cand->transport_id = CREATE_TP_ID(TP_STUN, idx); + cand->comp_id = (pj_uint8_t) comp->comp_id; ++ cand->transport = stun_cfg->conn_type == PJ_STUN_TP_UDP ? ++ PJ_CAND_UDP : ++ PJ_CAND_TCP_PASSIVE; + + /* Allocate and initialize STUN socket data */ + data = PJ_POOL_ZALLOC_T(ice_st->pool, sock_user_data); +@@ -549,8 +604,9 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + + /* Create the STUN transport */ + status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, +- stun_cfg->af, &stun_sock_cb, +- sock_cfg, data, &comp->stun[idx].sock); ++ stun_cfg->af, stun_cfg->conn_type, ++ &stun_sock_cb, sock_cfg, data, ++ &comp->stun[idx].sock); + if (status != PJ_SUCCESS) + return status; + +@@ -635,105 +691,154 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + } + + for (i = 0; i < stun_sock_info.alias_cnt && +- cand_cnt < stun_cfg->max_host_cands; ++i) ++ cand_cnt < stun_cfg->max_host_cands && ++ status == PJ_SUCCESS; ++i) + { +- unsigned j; +- pj_bool_t cand_duplicate = PJ_FALSE; +- char addrinfo[PJ_INET6_ADDRSTRLEN+10]; +- const pj_sockaddr *addr = &stun_sock_info.aliases[i]; +- +- if (max_cand_cnt==0) { +- PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); +- break; +- } +- +- /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ +- if (stun_cfg->loop_addr==PJ_FALSE) { +- if (stun_cfg->af == pj_AF_INET() && +- (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) +- { +- continue; +- } +- else if (stun_cfg->af == pj_AF_INET6()) { +- pj_in6_addr in6addr = {{0}}; +- in6addr.s6_addr[15] = 1; +- if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, +- sizeof(in6addr))==0) +- { +- continue; +- } +- } +- } +- +- /* Ignore IPv6 link-local address, unless it is the default +- * address (first alias). +- */ +- if (stun_cfg->af == pj_AF_INET6() && i != 0) { +- const pj_in6_addr *a = &addr->ipv6.sin6_addr; +- if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) +- continue; +- } +- +- cand = &comp->cand_list[comp->cand_cnt]; +- +- cand->type = PJ_ICE_CAND_TYPE_HOST; +- cand->status = PJ_SUCCESS; +- cand->local_pref = HOST_PREF; +- cand->transport_id = CREATE_TP_ID(TP_STUN, idx); +- cand->comp_id = (pj_uint8_t) comp->comp_id; +- pj_sockaddr_cp(&cand->addr, addr); +- pj_sockaddr_cp(&cand->base_addr, addr); +- pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); +- +- /* Check if not already in list */ +- for (j=0; j<comp->cand_cnt; j++) { +- if (ice_cand_equals(cand, &comp->cand_list[j])) { +- cand_duplicate = PJ_TRUE; +- break; +- } +- } +- +- if (cand_duplicate) { +- PJ_LOG(4, (ice_st->obj_name, +- "Comp %d: host candidate %s (tpid=%d) is a duplicate", +- comp->comp_id, pj_sockaddr_print(&cand->addr, addrinfo, +- sizeof(addrinfo), 3), cand->transport_id)); +- +- pj_bzero(&cand->addr, sizeof(cand->addr)); +- pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); +- continue; +- } else { +- comp->cand_cnt+=1; +- cand_cnt++; +- max_cand_cnt--; +- } +- +- pj_ice_calc_foundation(ice_st->pool, &cand->foundation, +- cand->type, &cand->base_addr); +- +- /* Set default candidate with the preferred default +- * address family +- */ +- if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && +- addr->addr.sa_family == comp->ice_st->cfg.af && +- comp->cand_list[comp->default_cand].base_addr.addr.sa_family != +- ice_st->cfg.af) +- { +- comp->default_cand = (unsigned)(cand - comp->cand_list); +- } +- +- PJ_LOG(4,(ice_st->obj_name, +- "Comp %d/%d: host candidate %s (tpid=%d) added", +- comp->comp_id, comp->cand_cnt-1, +- pj_sockaddr_print(&cand->addr, addrinfo, +- sizeof(addrinfo), 3), +- cand->transport_id)); +- } ++ status = !PJ_SUCCESS; ++ if (stun_sock_info.conn_type == PJ_STUN_TP_UDP) { ++ status = add_local_candidate(cand, idx, i, ++ &cand_cnt, &max_cand_cnt, ++ stun_sock_info, ice_st, comp, ++ PJ_CAND_UDP); ++ } else { ++ status = add_local_candidate(cand, idx, i, ++ &cand_cnt, &max_cand_cnt, ++ stun_sock_info, ice_st, comp, ++ PJ_CAND_TCP_PASSIVE); ++ /** RFC 6544, Section 4.1: ++ * First, agents SHOULD obtain host candidates as described in ++ * Section 5.1. Then, each agent SHOULD "obtain" (allocate a ++ * placeholder for) an active host candidate for each component of ++ * each TCP-capable media stream on each interface that the host ++ * has. The agent does not yet have to actually allocate a port for ++ * these candidates, but they are used for the creation of the check ++ * lists. ++ */ ++ status = add_local_candidate(cand, idx, i, ++ &cand_cnt, &max_cand_cnt, ++ stun_sock_info, ice_st, comp, ++ PJ_CAND_TCP_ACTIVE); ++ } ++ } + } + + return status; + } + ++static pj_bool_t add_local_candidate(pj_ice_sess_cand *cand, ++ unsigned idx, ++ unsigned i, ++ unsigned *cand_cnt, ++ unsigned *max_cand_cnt, ++ pj_stun_sock_info stun_sock_info, ++ pj_ice_strans *ice_st, ++ pj_ice_strans_comp *comp, ++ pj_ice_cand_transport transport) ++{ ++ unsigned j; ++ pj_bool_t cand_duplicate = PJ_FALSE; ++ char addrinfo[PJ_INET6_ADDRSTRLEN+10]; ++ const pj_sockaddr *addr = &stun_sock_info.aliases[i]; ++ pj_ice_strans_stun_cfg *stun_cfg = &ice_st->cfg.stun_tp[idx]; ++ ++ ++ if (*max_cand_cnt==0) { ++ PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); ++ return !PJ_SUCCESS; ++ } ++ ++ /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ ++ if (stun_cfg->loop_addr==PJ_FALSE) { ++ if (stun_cfg->af == pj_AF_INET() && ++ (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) ++ { ++ return PJ_SUCCESS; ++ } ++ else if (stun_cfg->af == pj_AF_INET6()) { ++ pj_in6_addr in6addr = {{0}}; ++ in6addr.s6_addr[15] = 1; ++ if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, ++ sizeof(in6addr))==0) ++ { ++ return PJ_SUCCESS; ++ } ++ } ++ } ++ ++ /* Ignore IPv6 link-local address, unless it is the default ++ * address (first alias). ++ */ ++ if (stun_cfg->af == pj_AF_INET6() && i != 0) { ++ const pj_in6_addr *a = &addr->ipv6.sin6_addr; ++ if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) ++ return PJ_SUCCESS; ++ } ++ ++ cand = &comp->cand_list[comp->cand_cnt]; ++ ++ cand->type = PJ_ICE_CAND_TYPE_HOST; ++ cand->status = PJ_SUCCESS; ++ cand->local_pref = HOST_PREF; ++ cand->transport_id = CREATE_TP_ID(TP_STUN, idx); ++ cand->comp_id = (pj_uint8_t) comp->comp_id; ++ cand->transport = transport; ++ ++ pj_sockaddr_cp(&cand->addr, addr); ++ pj_sockaddr_cp(&cand->base_addr, addr); ++ pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); ++ ++ /* Check if not already in list */ ++ for (j=0; j<comp->cand_cnt; j++) { ++ if (ice_cand_equals(cand, &comp->cand_list[j])) { ++ cand_duplicate = PJ_TRUE; ++ return !PJ_SUCCESS; ++ } ++ } ++ ++ if (cand_duplicate) { ++ PJ_LOG(4, (ice_st->obj_name, ++ "Comp %d: host candidate %s (tpid=%d) is a duplicate", ++ comp->comp_id, ++ pj_sockaddr_print(&cand->addr, ++ addrinfo, sizeof(addrinfo), 3), ++ cand->transport_id)); ++ ++ pj_bzero(&cand->addr, sizeof(cand->addr)); ++ pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); ++ return PJ_SUCCESS; ++ } else { ++ comp->cand_cnt+=1; ++ (*cand_cnt)++; ++ (*max_cand_cnt)--; ++ } ++ ++ pj_ice_calc_foundation(ice_st->pool, &cand->foundation, ++ cand->type, &cand->base_addr); ++ ++ /* Set default candidate with the preferred default ++ * address family ++ */ ++ if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && ++ addr->addr.sa_family == comp->ice_st->cfg.af && ++ comp->cand_list[comp->default_cand].base_addr.addr.sa_family != ++ ice_st->cfg.af) ++ { ++ comp->default_cand = (unsigned)(cand - comp->cand_list); ++ } ++ ++ if (transport == PJ_CAND_TCP_ACTIVE) { ++ // Use the port 9 (DISCARD Protocol) for TCP active candidates. ++ pj_sockaddr_set_port(&cand->addr, 9); ++ } ++ ++ PJ_LOG(4,(ice_st->obj_name, ++ "Comp %d/%d: host candidate %s (tpid=%d) added", ++ comp->comp_id, comp->cand_cnt-1, ++ pj_sockaddr_print(&cand->addr, addrinfo, ++ sizeof(addrinfo), 3), ++ cand->transport_id)); ++ return PJ_SUCCESS; ++} + + /* + * Create the component. +@@ -1207,6 +1312,12 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, + ice_cb.on_ice_complete = &on_ice_complete; + ice_cb.on_rx_data = &ice_rx_data; + ice_cb.on_tx_pkt = &ice_tx_pkt; ++#if PJ_HAS_TCP ++ ice_cb.wait_tcp_connection = &ice_wait_tcp_connection; ++ ice_cb.select_turn_dataconn = &ice_select_turn_dataconn; ++ ice_cb.reconnect_tcp_connection = &ice_reconnect_tcp_connection; ++ ice_cb.close_tcp_connection = &ice_close_tcp_connection; ++#endif + + /* Create! */ + status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role, +@@ -1282,7 +1393,8 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, + &cand->foundation, &cand->addr, + &cand->base_addr, &cand->rel_addr, + pj_sockaddr_get_len(&cand->addr), +- (unsigned*)&ice_cand_id); ++ (unsigned*)&ice_cand_id, ++ cand->transport); + if (status != PJ_SUCCESS) + goto on_error; + } +@@ -1668,7 +1780,31 @@ static pj_status_t send_data(pj_ice_strans *ice_st, + pj_grp_lock_release(ice_st->grp_lock); + + def_cand = &comp->cand_list[comp->default_cand]; +- ++ ++ pj_bool_t add_header = def_cand->transport != PJ_CAND_UDP; ++ /* TCP, add header */ ++ if (add_header) { ++ /* ++ * RFC6544 ICE requires an agent to demultiplex STUN and ++ * application-layer traffic, since they appear on the same port. This ++ * demultiplexing is described in [RFC5245] and is done using the magic ++ * cookie and other fields of the message. Stream-oriented transports ++ * introduce another wrinkle, since they require a way to frame the ++ * connection so that the application and STUN packets can be extracted ++ * in order to differentiate STUN packets from application-layer ++ * traffic. For this reason, TCP media streams utilizing ICE use the ++ * basic framing provided in RFC 4571 [RFC4571], even if the application ++ * layer protocol is not RTP. ++ */ ++ pj_uint8_t header_1 = data_len % 256; ++ pj_uint8_t header_0 = data_len >> 8; ++ pj_memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[2], (unsigned char *)data, data_len); ++ buf = &ice_st->rtp_pkt; ++ data_len += 2; ++ } ++ + if (def_cand->status == PJ_SUCCESS) { + unsigned tp_idx = GET_TP_IDX(def_cand->transport_id); + +@@ -1730,6 +1866,11 @@ static pj_status_t send_data(pj_ice_strans *ice_st, + status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, buf, + (unsigned)data_len, 0, dest_addr, + dest_addr_len); ++ /* Do not count the header */ ++ if (add_header) { ++ data_len -= sizeof(pj_uint16_t); ++ } ++ + goto on_return; + } + +@@ -1842,7 +1983,22 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) + sizeof(lip), 3); + pj_sockaddr_print(&check->rcand->addr, rip, + sizeof(rip), 3); +- ++#if PJ_HAS_TCP ++ int idx = -1; ++ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) { ++ if (ice_st->cfg.stun_tp[i].af == ++ check->rcand->addr.addr.sa_family) ++ { ++ idx = i; ++ break; ++ } ++ } ++ if (idx == -1) { ++ PJ_LOG(4, (ice_st->obj_name, ++ "Comp %d: No STUN sock found.", ++ comp->comp_id)); ++ } ++#endif + if (tp_typ == TP_TURN) { + /* Activate channel binding for the remote address + * for more efficient data transfer using TURN. +@@ -1936,6 +2092,29 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, + pj_sockaddr_get_port(dst_addr), + tp_typ)); + ++ /* TCP, add header */ ++ if (comp->ice_st->cfg.stun_tp->conn_type == PJ_STUN_TP_TCP) { ++ /* ++ * RFC6544 ICE requires an agent to demultiplex STUN and ++ * application-layer traffic, since they appear on the same port. This ++ * demultiplexing is described in [RFC5245] and is done using the magic ++ * cookie and other fields of the message. Stream-oriented transports ++ * introduce another wrinkle, since they require a way to frame the ++ * connection so that the application and STUN packets can be extracted ++ * in order to differentiate STUN packets from application-layer ++ * traffic. For this reason, TCP media streams utilizing ICE use the ++ * basic framing provided in RFC 4571 [RFC4571], even if the application ++ * layer protocol is not RTP. ++ */ ++ pj_uint8_t header_1 = size % 256; ++ pj_uint8_t header_0 = size >> 8; ++ pj_memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[2], (unsigned char *)pkt, size); ++ buf = &ice_st->rtp_pkt; ++ size += 2; ++ } ++ + if (tp_typ == TP_TURN) { + if (comp->turn[tp_idx].sock) { + status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, +@@ -1969,9 +2148,13 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, + dest_addr_len = dst_addr_len; + } + +- status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, +- buf, (unsigned)size, 0, +- dest_addr, dest_addr_len); ++ if (comp->stun[tp_idx].sock) { ++ status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, ++ buf, (unsigned)size, 0, ++ dest_addr, dest_addr_len); ++ } else { ++ status = PJ_EINVALIDOP; ++ } + } else { + pj_assert(!"Invalid transport ID"); + status = PJ_EINVALIDOP; +@@ -2031,6 +2214,237 @@ static void check_pending_send(pj_ice_strans *ice_st) + } + } + ++static void on_peer_connection(pj_stun_session* sess, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr) ++{ ++ ++ pj_stun_sock *stun_sock; ++ sock_user_data *data; ++ pj_ice_strans_comp *comp; ++ pj_ice_strans *ice_st; ++ ++ stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); ++ /* We have disassociated ourselves from the STUN session */ ++ if (!stun_sock) ++ return; ++ ++ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); ++ /* We have disassociated ourselves from the STUN socket */ ++ if (!data) ++ return; ++ ++ comp = data->comp; ++ ice_st = comp->ice_st; ++ ++ /* Incorrect ICE */ ++ if (!ice_st || !ice_st->ice) ++ return; ++ ++ ice_st->is_pending = PJ_FALSE; ++ ice_sess_on_peer_connection(ice_st->ice, ++ data->transport_id, status, remote_addr); ++} ++ ++static void on_peer_reset_connection(pj_stun_session* sess, ++ pj_sockaddr_t* remote_addr) ++{ ++ pj_stun_sock *stun_sock; ++ sock_user_data *data; ++ pj_ice_strans_comp *comp; ++ pj_ice_strans *ice_st; ++ ++ stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); ++ /* We have disassociated ourselves from the STUN session */ ++ if (!stun_sock) ++ return; ++ ++ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); ++ /* We have disassociated ourselves from the STUN socket */ ++ if (!data) ++ return; ++ ++ comp = data->comp; ++ ice_st = comp->ice_st; ++ ++ /* Incorrect ICE */ ++ if (!ice_st || !ice_st->ice) ++ return; ++ ++ ice_sess_on_peer_reset_connection(ice_st->ice, ++ data->transport_id, remote_addr); ++} ++ ++static void on_peer_packet(pj_stun_session* sess, pj_sockaddr_t* remote_addr) ++{ ++ ++ if (!sess || !remote_addr) ++ return; ++ ++ pj_stun_sock *stun_sock; ++ sock_user_data *data; ++ pj_ice_strans_comp *comp; ++ pj_ice_strans *ice_st; ++ ++ stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); ++ /* We have disassociated ourselves from the STUN session */ ++ if (!stun_sock) ++ return; ++ ++ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); ++ /* We have disassociated ourselves from the STUN socket */ ++ if (!data) ++ return; ++ ++ comp = data->comp; ++ if (!comp) ++ return; ++ ++ ice_st = comp->ice_st; ++ /* Incorrect ICE */ ++ if (!ice_st || !ice_st->ice) ++ return; ++ ++ ice_sess_on_peer_packet(ice_st->ice, data->transport_id, remote_addr); ++} ++ ++#if PJ_HAS_TCP ++static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id) ++{ ++ pj_ice_sess_check *check = &clist->checks[check_id]; ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; ++ ++ int idx = -1; ++ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) ++ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { ++ idx = i; ++ break; ++ } ++ ++ if (idx == -1) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ if (st_comp->stun[idx].sock) { ++ pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); ++ if (!sess) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ pj_stun_session_callback(sess)->on_peer_connection = ++ &on_peer_connection; ++ pj_stun_session_callback(sess)->on_peer_reset_connection = ++ &on_peer_reset_connection; ++ pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; ++ ++ return pj_stun_sock_connect_active(st_comp->stun[idx].sock, ++ &rcand->addr, ++ rcand->addr.addr.sa_family); ++ } ++ ++ return PJ_EINVAL; ++} ++ ++static pj_status_t ice_select_turn_dataconn(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id) ++{ ++ pj_ice_sess_check *check = &clist->checks[check_id]; ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; ++ ++ for (int i=0; i<ice_st->cfg.turn_tp_cnt; ++i) { ++ pj_turn_session_info info; ++ pj_turn_sock_get_info(st_comp->turn[i].sock, &info); ++ if (st_comp->turn[i].sock ++ && pj_turn_sock_has_dataconn(st_comp->turn[i].sock, &rcand->addr)) { ++ ice_select_incoming_turn(ice, clist, check_id); ++ return PJ_SUCCESS; // Already connected via TURN ++ } ++ } ++ ++ return PJ_EINVAL; ++} ++ ++static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id) ++{ ++ pj_ice_sess_check *check = &clist->checks[check_id]; ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; ++ ++ int idx = -1; ++ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) ++ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { ++ idx = i; ++ break; ++ } ++ ++ if (idx == -1) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ ++ if (st_comp->stun[idx].sock) { ++ pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); ++ if (!sess) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ pj_stun_session_callback(sess)->on_peer_connection = ++ &on_peer_connection; ++ pj_stun_session_callback(sess)->on_peer_reset_connection = ++ &on_peer_reset_connection; ++ pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; ++ return pj_stun_sock_reconnect_active(st_comp->stun[idx].sock, ++ &rcand->addr, ++ rcand->addr.addr.sa_family); ++ } ++ ++ return PJ_EINVAL; ++} ++ ++static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id) ++{ ++ pj_ice_sess_check *check = &clist->checks[check_id]; ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; ++ ++ int idx = -1; ++ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) ++ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { ++ idx = i; ++ break; ++ } ++ ++ if (idx != -1 && st_comp->stun[idx].sock) { ++ const pj_ice_sess_cand *rcand = check->rcand; ++ ice_st->is_pending = PJ_FALSE; ++ return pj_stun_sock_close(st_comp->stun[idx].sock, &rcand->addr); ++ } ++ ++ return PJ_EINVAL; ++} ++#endif ++ + /* Notifification when asynchronous send operation via STUN/TURN + * has completed. + */ +@@ -2318,6 +2732,10 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + } + } + break; ++ case PJ_STUN_SESS_DESTROYED: ++ case PJ_STUN_TCP_CONNECT_ERROR: ++ default: ++ break; + } + + return pj_grp_lock_dec_ref(ice_st->grp_lock)? PJ_FALSE : PJ_TRUE; +@@ -2358,14 +2776,103 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock, + } else { + + /* Hand over the packet to ICE */ +- status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, +- data->transport_id, pkt, pkt_len, +- peer_addr, addr_len); +- +- if (status != PJ_SUCCESS) { +- ice_st_perror(comp->ice_st, +- "Error processing packet from TURN relay", +- status); ++ if (comp->ice_st->cfg.turn_tp->conn_type == PJ_TURN_TP_TCP && pkt_len > 0) { ++ unsigned parsed = 0; ++ pj_status_t status; ++ ++ do { ++ pj_uint16_t leftover = pkt_len - parsed; ++ pj_uint8_t *current_packet = ((pj_uint8_t *)(pkt)) + parsed; ++ ++ /** ++ * RFC6544, the packet is wrapped into a packet following the ++ * RFC4571 ++ */ ++ pj_bool_t store_remaining = PJ_TRUE; ++ if (comp->ice_st->rx_buffer_size || ++ comp->ice_st->rx_wanted_size) ++ { ++ /* a single packet left to process */ ++ if (comp->ice_st->rx_buffer_size == 1) { ++ /* get last frame's lenght from its header */ ++ leftover = GETVAL16H(comp->ice_st->rx_buffer, ++ current_packet); ++ /* adjust counters accordingly */ ++ comp->ice_st->rx_buffer_size = 0; ++ current_packet++; ++ parsed++; ++ ++ if (leftover + parsed <= pkt_len) { ++ /* we didn't get what we were promissed in the ++ * header. furthermore, this was the last frame and ++ * therefore we're done. ++ */ ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ comp->ice_st->rx_wanted_size = leftover; ++ } ++ } else if (leftover + comp->ice_st->rx_buffer_size >= ++ comp->ice_st->rx_wanted_size) ++ { ++ /* We have enough leftover bytes in buffer to build a new ++ * packet and parse it ++ */ ++ store_remaining = PJ_FALSE; ++ ++ pj_uint16_t eaten_bytes = comp->ice_st->rx_wanted_size - ++ comp->ice_st->rx_buffer_size; ++ pj_memcpy(comp->ice_st->rx_buffer + ++ comp->ice_st->rx_buffer_size, ++ current_packet, eaten_bytes); ++ ++ leftover = comp->ice_st->rx_wanted_size; ++ current_packet = comp->ice_st->rx_buffer; ++ parsed += eaten_bytes; ++ ++ comp->ice_st->rx_buffer_size = 0; ++ comp->ice_st->rx_wanted_size = 0; ++ } ++ } else if (leftover > 1) { ++ leftover = GETVAL16H(current_packet, current_packet+1); ++ current_packet += 2; ++ parsed += 2; ++ if (leftover + parsed <= pkt_len) { ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ comp->ice_st->rx_wanted_size = leftover; ++ } ++ } ++ ++ if (store_remaining) { ++ leftover = pkt_len - parsed; ++ pj_memcpy(comp->ice_st->rx_buffer + ++ comp->ice_st->rx_buffer_size, ++ current_packet, leftover); ++ comp->ice_st->rx_buffer_size += leftover; ++ status = PJ_SUCCESS; ++ break; ++ } ++ ++ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, ++ data->transport_id, ++ current_packet, leftover, ++ peer_addr, addr_len); ++ if (status != PJ_SUCCESS) { ++ ice_st_perror(comp->ice_st, ++ "Error processing packet from TURN relay", ++ status); ++ } ++ } while (parsed < pkt_len); ++ } else { ++ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, ++ data->transport_id, pkt, pkt_len, ++ peer_addr, addr_len); ++ if (status != PJ_SUCCESS) ++ ice_st_perror(comp->ice_st, ++ "Error processing packet from TURN relay", ++ status); + } + } + +diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c +index db0de10bc..808342bec 100644 +--- a/pjnath/src/pjnath/nat_detect.c ++++ b/pjnath/src/pjnath/nat_detect.c +@@ -329,7 +329,8 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, + sess_cb.on_request_complete = &on_request_complete; + sess_cb.on_send_msg = &on_send_msg; + status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb, +- PJ_FALSE, sess->grp_lock, &sess->stun_sess); ++ PJ_FALSE, sess->grp_lock, &sess->stun_sess, ++ PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) + goto on_error; + +@@ -875,7 +876,9 @@ static pj_status_t send_test(nat_detect_session *sess, + + /* Send the request */ + status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE, +- PJ_TRUE, sess->cur_server, ++ (pj_stun_session_tp_type(sess->stun_sess) == ++ PJ_STUN_TP_UDP), ++ sess->cur_server, + pj_sockaddr_get_len(sess->cur_server), + sess->result[test_id].tdata); + if (status != PJ_SUCCESS) +diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c +index f2b4f7058..ed17b904f 100644 +--- a/pjnath/src/pjnath/stun_session.c ++++ b/pjnath/src/pjnath/stun_session.c +@@ -49,6 +49,8 @@ struct pj_stun_session + + pj_stun_tx_data pending_request_list; + pj_stun_tx_data cached_response_list; ++ ++ pj_stun_tp_type conn_type; + }; + + #define SNAME(s_) ((s_)->pool->obj_name) +@@ -524,7 +526,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, + const pj_stun_session_cb *cb, + pj_bool_t fingerprint, + pj_grp_lock_t *grp_lock, +- pj_stun_session **p_sess) ++ pj_stun_session **p_sess, ++ pj_stun_tp_type conn_type) + { + pj_pool_t *pool; + pj_stun_session *sess; +@@ -545,6 +548,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, + pj_memcpy(&sess->cb, cb, sizeof(*cb)); + sess->use_fingerprint = fingerprint; + sess->log_flag = 0xFFFF; ++ sess->conn_type = conn_type; + + if (grp_lock) { + sess->grp_lock = grp_lock; +@@ -1538,3 +1542,12 @@ on_return: + return status; + } + ++PJ_DECL(pj_stun_session_cb *) pj_stun_session_callback(pj_stun_session *sess) ++{ ++ return sess ? &sess->cb : NULL; ++} ++ ++PJ_DECL(pj_stun_tp_type) pj_stun_session_tp_type(pj_stun_session *sess) ++{ ++ return sess ? sess->conn_type : PJ_STUN_TP_UDP; ++} +diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c +index 5fe825cf5..c4519f82e 100644 +--- a/pjnath/src/pjnath/stun_sock.c ++++ b/pjnath/src/pjnath/stun_sock.c +@@ -40,6 +40,36 @@ + + enum { MAX_BIND_RETRY = 100 }; + ++#if PJ_HAS_TCP ++// The head of a RTP packet is stored in a 16 bits header, so the max size of a ++// packet is 65536 ++#define MAX_RTP_SIZE 65536 ++#endif ++ ++// TODO (sblin) The incoming socks are a bit HACKY for now. ++// Need a better approach ++typedef struct outgoing_sock { ++ pj_sock_t fd; ++ pj_activesock_t *sock; ++ pj_sockaddr_t *addr; ++} outgoing_sock; ++ ++typedef struct incoming_sock { ++ pj_sock_t fd; ++ pj_activesock_t *sock; ++ pj_sockaddr addr; ++ int addr_len; ++} incoming_sock; ++ ++typedef struct rx_buf { ++ pj_activesock_t *asock; ++ pj_uint8_t rx_buffer[MAX_RTP_SIZE]; ++ pj_uint16_t rx_buffer_size; ++ pj_uint16_t rx_wanted_size; ++ struct rx_buf *next; ++ struct rx_buf *prev; ++} rx_buf; ++ + struct pj_stun_sock + { + char *obj_name; /* Log identification */ +@@ -47,6 +77,8 @@ struct pj_stun_sock + void *user_data; /* Application user data */ + pj_bool_t is_destroying; /* Destroy already called */ + int af; /* Address family */ ++ pj_stun_tp_type conn_type; ++ pj_stun_sock_cfg cfg; + pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/ + pj_stun_sock_cb cb; /* Application callbacks */ + +@@ -59,6 +91,13 @@ struct pj_stun_sock + pj_dns_srv_async_query *q; /* Pending DNS query */ + pj_sock_t sock_fd; /* Socket descriptor */ + pj_activesock_t *active_sock; /* Active socket object */ ++#if PJ_HAS_TCP ++ int outgoing_nb; ++ outgoing_sock outgoing_socks[PJ_ICE_MAX_CHECKS]; ++ int incoming_nb; ++ incoming_sock incoming_socks[PJ_ICE_MAX_CHECKS]; ++ rx_buf *rx_buffers; ++#endif + pj_ioqueue_op_key_t send_key; /* Default send key for app */ + pj_ioqueue_op_key_t int_send_key; /* Send key for internal */ + pj_status_t last_err; /* Last error status */ +@@ -68,6 +107,15 @@ struct pj_stun_sock + pj_grp_lock_t *grp_lock; /* Session group lock */ + }; + ++////////////////////////////////////////////////////////////////////////////// ++ ++static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2) ++{ ++ return (pj_uint16_t) ((buf1[0] << 8) | (buf2[0] << 0)); ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++ + /* + * Prototypes for static functions + */ +@@ -120,6 +168,24 @@ static void start_ka_timer(pj_stun_sock *stun_sock); + /* Keep-alive timer callback */ + static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); + ++ ++static pj_bool_t on_stun_sock_ready(pj_activesock_t *asock, ++ pj_status_t status); ++ ++static pj_bool_t on_stun_sock_accept(pj_activesock_t *asock, ++ pj_sock_t newsock, ++ const pj_sockaddr_t *src_addr, ++ int src_addr_len); ++ ++static pj_bool_t on_connect_complete(pj_activesock_t *asock, ++ pj_status_t status); ++ ++/* Notify application that session has failed */ ++static pj_bool_t sess_fail(pj_stun_sock *stun_sock, ++ pj_stun_sock_op op, ++ pj_status_t status); ++ ++ + #define INTERNAL_MSG_TOKEN (void*)(pj_ssize_t)1 + + +@@ -160,12 +226,183 @@ static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg) + return cfg->max_pkt_size > 1 && cfg->async_cnt >= 1; + } + ++/* ++ * Initialize. ++ */ ++PJ_DEF(pj_status_t) pj_stun_sock_alloc(pj_stun_sock *stun_sock) ++{ ++ pj_status_t status; ++ pj_sockaddr bound_addr; ++ pj_uint16_t max_bind_retry; ++ int sock_type; ++ ++ pj_grp_lock_acquire(stun_sock->grp_lock); ++ ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP) ++ sock_type = pj_SOCK_DGRAM(); ++ else ++ sock_type = pj_SOCK_STREAM(); ++ ++ stun_sock->ka_interval = stun_sock->cfg.ka_interval; ++ if (stun_sock->ka_interval == 0) ++ stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; ++ /* Create socket and bind socket */ ++ status = pj_sock_socket(stun_sock->af, sock_type, 0, &stun_sock->sock_fd); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Apply QoS, if specified */ ++ status = pj_sock_apply_qos2(stun_sock->sock_fd, stun_sock->cfg.qos_type, ++ &stun_sock->cfg.qos_params, 2, ++ stun_sock->obj_name, NULL); ++ if (status != PJ_SUCCESS && !stun_sock->cfg.qos_ignore_error) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Apply socket buffer size */ ++ if (stun_sock->cfg.so_rcvbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_rcvbuf_size; ++ status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_RCVBUF(), ++ PJ_TRUE, &sobuf_size); ++ if (status != PJ_SUCCESS) { ++ pj_perror(3, stun_sock->obj_name, status, ++ "Failed setting SO_RCVBUF"); ++ } else { ++ if (sobuf_size < stun_sock->cfg.so_rcvbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, ++ "Warning! Cannot set SO_RCVBUF as configured, " ++ "now=%d, configured=%d", ++ sobuf_size, stun_sock->cfg.so_rcvbuf_size)); ++ } else { ++ PJ_LOG(5, (stun_sock->obj_name, ++ "SO_RCVBUF set to %d", sobuf_size)); ++ } ++ } ++ } ++ if (stun_sock->cfg.so_sndbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_sndbuf_size; ++ status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(), ++ PJ_TRUE, &sobuf_size); ++ if (status != PJ_SUCCESS) { ++ pj_perror(3, stun_sock->obj_name, status, ++ "Failed setting SO_SNDBUF"); ++ } else { ++ if (sobuf_size < stun_sock->cfg.so_sndbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, ++ "Warning! Cannot set SO_SNDBUF as configured, " ++ "now=%d, configured=%d", ++ sobuf_size, stun_sock->cfg.so_sndbuf_size)); ++ } else { ++ PJ_LOG(5, (stun_sock->obj_name, ++ "SO_SNDBUF set to %d", sobuf_size)); ++ } ++ } ++ } ++ ++ /* Bind socket */ ++ max_bind_retry = MAX_BIND_RETRY; ++ if (stun_sock->cfg.port_range && ++ stun_sock->cfg.port_range < max_bind_retry) ++ max_bind_retry = stun_sock->cfg.port_range; ++ ++ pj_sockaddr_init(stun_sock->af, &bound_addr, NULL, 0); ++ if (stun_sock->cfg.bound_addr.addr.sa_family == pj_AF_INET() || ++ stun_sock->cfg.bound_addr.addr.sa_family == pj_AF_INET6()) ++ { ++ pj_sockaddr_cp(&bound_addr, &stun_sock->cfg.bound_addr); ++ } ++ ++ status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, ++ stun_sock->cfg.port_range, max_bind_retry); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Init active socket configuration */ ++ { ++ pj_activesock_cfg activesock_cfg; ++ pj_activesock_cb activesock_cb; ++ ++ pj_activesock_cfg_default(&activesock_cfg); ++ activesock_cfg.grp_lock = stun_sock->grp_lock; ++ activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; ++ activesock_cfg.concurrency = 0; ++ ++ /* Create the active socket */ ++ pj_bzero(&activesock_cb, sizeof(activesock_cb)); ++ activesock_cb.on_data_sent = &on_data_sent; ++ activesock_cb.on_data_recvfrom = &on_data_recvfrom; ++ ++#if PJ_HAS_TCP ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { ++ activesock_cb.on_accept_complete = &on_stun_sock_accept; ++ // Will be ready to accept incoming connections from the external world ++ status = pj_sock_listen(stun_sock->sock_fd, PJ_SOMAXCONN); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ } else { ++ activesock_cb.on_connect_complete = &on_stun_sock_ready; ++ } ++#else ++ activesock_cb.on_connect_complete = &on_stun_sock_ready; ++#endif ++ ++ status = pj_activesock_create(stun_sock->pool, stun_sock->sock_fd, ++ sock_type, &activesock_cfg, ++ stun_sock->stun_cfg.ioqueue, ++ &activesock_cb, stun_sock, ++ &stun_sock->active_sock); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++#if PJ_HAS_TCP ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { ++ status = pj_activesock_start_accept(stun_sock->active_sock, ++ stun_sock->pool); ++ } else { ++ status = PJ_SUCCESS; ++ } ++ if (status == PJ_SUCCESS) { ++ on_stun_sock_ready(stun_sock->active_sock, PJ_SUCCESS); ++ } else if (status != PJ_EPENDING) { ++ char addrinfo[PJ_INET6_ADDRSTRLEN + 10]; ++ pj_perror(3, stun_sock->pool->obj_name, status, ++ "Failed to connect to %s", ++ pj_sockaddr_print(&bound_addr, addrinfo, ++ sizeof(addrinfo), 3)); ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++#else ++ on_stun_sock_ready(stun_sock->active_sock, PJ_SUCCESS); ++#endif ++ } ++ ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++} ++ + /* + * Create the STUN transport using the specified configuration. + */ + PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + const char *name, + int af, ++ pj_stun_tp_type conn_type, + const pj_stun_sock_cb *cb, + const pj_stun_sock_cfg *cfg, + void *user_data, +@@ -174,22 +411,32 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + pj_pool_t *pool; + pj_stun_sock *stun_sock; + pj_stun_sock_cfg default_cfg; +- pj_sockaddr bound_addr; +- unsigned i; +- pj_uint16_t max_bind_retry; + pj_status_t status; + + PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); + PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); + PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); + PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); ++ PJ_ASSERT_RETURN(conn_type != PJ_STUN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL); + + status = pj_stun_config_check_valid(stun_cfg); + if (status != PJ_SUCCESS) + return status; + +- if (name == NULL) +- name = "stuntp%p"; ++ if (name == NULL) { ++ switch (conn_type) { ++ case PJ_STUN_TP_UDP: ++ name = "udpstun%p"; ++ break; ++ case PJ_STUN_TP_TCP: ++ name = "tcpstun%p"; ++ break; ++ default: ++ PJ_ASSERT_RETURN(!"Invalid STUN conn_type", PJ_EINVAL); ++ name = "tcpstun%p"; ++ break; ++ } ++ } + + if (cfg == NULL) { + pj_stun_sock_cfg_default(&default_cfg); +@@ -204,9 +451,16 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + stun_sock->obj_name = pool->obj_name; + stun_sock->user_data = user_data; + stun_sock->af = af; ++ stun_sock->conn_type = conn_type; + stun_sock->sock_fd = PJ_INVALID_SOCKET; ++#if PJ_HAS_TCP ++ stun_sock->outgoing_nb = -1; ++ stun_sock->incoming_nb = -1; ++#endif + pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); + pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); ++ /* Copy socket settings; QoS parameters etc */ ++ pj_memcpy(&stun_sock->cfg, cfg, sizeof(*cfg)); + + stun_sock->ka_interval = cfg->ka_interval; + if (stun_sock->ka_interval == 0) +@@ -226,125 +480,6 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, + &stun_sock_destructor); + +- /* Create socket and bind socket */ +- status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); +- if (status != PJ_SUCCESS) +- goto on_error; +- +- /* Apply QoS, if specified */ +- status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, +- &cfg->qos_params, 2, stun_sock->obj_name, +- NULL); +- if (status != PJ_SUCCESS && !cfg->qos_ignore_error) +- goto on_error; +- +- /* Apply socket buffer size */ +- if (cfg->so_rcvbuf_size > 0) { +- unsigned sobuf_size = cfg->so_rcvbuf_size; +- status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_RCVBUF(), +- PJ_TRUE, &sobuf_size); +- if (status != PJ_SUCCESS) { +- PJ_PERROR(3, (stun_sock->obj_name, status, +- "Failed setting SO_RCVBUF")); +- } else { +- if (sobuf_size < cfg->so_rcvbuf_size) { +- PJ_LOG(4, (stun_sock->obj_name, +- "Warning! Cannot set SO_RCVBUF as configured, " +- "now=%d, configured=%d", +- sobuf_size, cfg->so_rcvbuf_size)); +- } else { +- PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", +- sobuf_size)); +- } +- } +- } +- if (cfg->so_sndbuf_size > 0) { +- unsigned sobuf_size = cfg->so_sndbuf_size; +- status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(), +- PJ_TRUE, &sobuf_size); +- if (status != PJ_SUCCESS) { +- PJ_PERROR(3, (stun_sock->obj_name, status, +- "Failed setting SO_SNDBUF")); +- } else { +- if (sobuf_size < cfg->so_sndbuf_size) { +- PJ_LOG(4, (stun_sock->obj_name, +- "Warning! Cannot set SO_SNDBUF as configured, " +- "now=%d, configured=%d", +- sobuf_size, cfg->so_sndbuf_size)); +- } else { +- PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", +- sobuf_size)); +- } +- } +- } +- +- /* Bind socket */ +- max_bind_retry = MAX_BIND_RETRY; +- if (cfg->port_range && cfg->port_range < max_bind_retry) +- max_bind_retry = cfg->port_range; +- pj_sockaddr_init(af, &bound_addr, NULL, 0); +- if (cfg->bound_addr.addr.sa_family == pj_AF_INET() || +- cfg->bound_addr.addr.sa_family == pj_AF_INET6()) +- { +- pj_sockaddr_cp(&bound_addr, &cfg->bound_addr); +- } +- status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, +- cfg->port_range, max_bind_retry); +- if (status != PJ_SUCCESS) +- goto on_error; +- +- /* Create more useful information string about this transport */ +-#if 0 +- { +- pj_sockaddr bound_addr; +- int addr_len = sizeof(bound_addr); +- +- status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, +- &addr_len); +- if (status != PJ_SUCCESS) +- goto on_error; +- +- stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); +- pj_sockaddr_print(&bound_addr, stun_sock->info, +- PJ_INET6_ADDRSTRLEN, 3); +- } +-#endif +- +- /* Init active socket configuration */ +- { +- pj_activesock_cfg activesock_cfg; +- pj_activesock_cb activesock_cb; +- +- pj_activesock_cfg_default(&activesock_cfg); +- activesock_cfg.grp_lock = stun_sock->grp_lock; +- activesock_cfg.async_cnt = cfg->async_cnt; +- activesock_cfg.concurrency = 0; +- +- /* Create the active socket */ +- pj_bzero(&activesock_cb, sizeof(activesock_cb)); +- activesock_cb.on_data_recvfrom = &on_data_recvfrom; +- activesock_cb.on_data_sent = &on_data_sent; +- status = pj_activesock_create(pool, stun_sock->sock_fd, +- pj_SOCK_DGRAM(), +- &activesock_cfg, stun_cfg->ioqueue, +- &activesock_cb, stun_sock, +- &stun_sock->active_sock); +- if (status != PJ_SUCCESS) +- goto on_error; +- +- /* Start asynchronous read operations */ +- status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, +- cfg->max_pkt_size, 0); +- if (status != PJ_SUCCESS) +- goto on_error; +- +- /* Init send keys */ +- pj_ioqueue_op_key_init(&stun_sock->send_key, +- sizeof(stun_sock->send_key)); +- pj_ioqueue_op_key_init(&stun_sock->int_send_key, +- sizeof(stun_sock->int_send_key)); +- } +- + /* Create STUN session */ + { + pj_stun_session_cb sess_cb; +@@ -356,11 +491,58 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + stun_sock->obj_name, + &sess_cb, PJ_FALSE, + stun_sock->grp_lock, +- &stun_sock->stun_sess); +- if (status != PJ_SUCCESS) +- goto on_error; ++ &stun_sock->stun_sess, ++ conn_type); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ return status; ++ } + } + ++ pj_stun_sock_alloc(stun_sock); ++ ++ /* Done */ ++ *p_stun_sock = stun_sock; ++ return PJ_SUCCESS; ++} ++ ++/* ++ * Notification when outgoing TCP socket has been connected. ++ */ ++static pj_bool_t on_stun_sock_ready(pj_activesock_t *asock, pj_status_t status) ++{ ++ pj_stun_sock *stun_sock; ++ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); ++ if (!stun_sock) ++ return PJ_FALSE; ++ ++ pj_grp_lock_acquire(stun_sock->grp_lock); ++ ++ /* TURN session may have already been destroyed here. ++ * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). ++ */ ++ if (!stun_sock->stun_sess) { ++ sess_fail(stun_sock, PJ_STUN_SESS_DESTROYED, status); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++ } ++ ++ if (status != PJ_SUCCESS) { ++ sess_fail(stun_sock, PJ_STUN_TCP_CONNECT_ERROR, status); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++ } ++ ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) ++ PJ_LOG(5,(stun_sock->obj_name, "TCP connected")); ++ ++ /* Start asynchronous read operations */ ++ pj_status_t result; ++ result = pj_activesock_start_recvfrom(asock, stun_sock->pool, ++ stun_sock->cfg.max_pkt_size, 0); ++ if (result != PJ_SUCCESS) ++ return PJ_FALSE; ++ + /* Associate us with the STUN session */ + pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); + +@@ -369,24 +551,303 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + * STUN messages we sent with STUN messages that the application sends. + * The last 16bit value in the array is a counter. + */ ++ unsigned i; + for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) { + stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); + } + stun_sock->tsx_id[5] = 0; + +- + /* Init timer entry */ + stun_sock->ka_timer.cb = &ka_timer_cb; + stun_sock->ka_timer.user_data = stun_sock; + +- /* Done */ +- *p_stun_sock = stun_sock; +- return PJ_SUCCESS; ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } + +-on_error: +- pj_stun_sock_destroy(stun_sock); +- return status; ++ /* Init send keys */ ++ pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); ++ pj_ioqueue_op_key_init(&stun_sock->int_send_key, ++ sizeof(stun_sock->int_send_key)); ++ ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_TRUE; ++} ++ ++static pj_bool_t parse_rx_packet(pj_activesock_t *asock, ++ void *data, ++ pj_size_t size, ++ const pj_sockaddr_t *rx_addr, ++ unsigned sock_addr_len) ++{ ++ ++ pj_stun_sock *stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); ++ if (!stun_sock) ++ return PJ_FALSE; ++ ++ pj_grp_lock_acquire(stun_sock->grp_lock); ++ pj_uint16_t parsed = 0; ++ pj_status_t result = PJ_TRUE; ++ pj_status_t status; ++ ++#if PJ_HAS_TCP ++ // Search current rx_buf ++ rx_buf* buf = NULL; ++ rx_buf* stun_sock_buf = stun_sock->rx_buffers; ++ while (stun_sock_buf) { ++ if (stun_sock_buf->asock == asock) { ++ buf = stun_sock_buf; ++ break; ++ } ++ stun_sock_buf = stun_sock_buf->next; ++ } ++ if (!buf) { ++ // Create rx_buf, this buf will be released when the pool is released ++ buf = (rx_buf*)pj_pool_calloc(stun_sock->pool, 1, sizeof(rx_buf)); ++ if (!buf) { ++ PJ_LOG(5, (stun_sock->obj_name, "Cannot allocate memory for rx_buf")); ++ status = pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++ } ++ buf->asock = asock; ++ buf->next = stun_sock->rx_buffers; ++ if (stun_sock->rx_buffers) ++ stun_sock->rx_buffers->prev = buf; ++ stun_sock->rx_buffers = buf; ++ } ++#endif ++ ++ do { ++ pj_uint16_t leftover = size - parsed; ++ pj_uint8_t *current_packet = ((pj_uint8_t *)(data)) + parsed; ++ ++#if PJ_HAS_TCP ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { ++ /* RFC6544, the packet is wrapped into a packet following the RFC4571 */ ++ pj_bool_t store_remaining = PJ_TRUE; ++ if (buf->rx_buffer_size != 0 || buf->rx_wanted_size != 0) { ++ if (buf->rx_buffer_size == 1) { ++ leftover = GETVAL16H(buf->rx_buffer, current_packet); ++ ++ buf->rx_buffer_size = 0; ++ current_packet++; ++ parsed++; ++ ++ if (leftover + parsed <= size) { ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ buf->rx_wanted_size = leftover; ++ } ++ ++ } else if (leftover + buf->rx_buffer_size >= buf->rx_wanted_size) { ++ // We have enough data Build new packet to parse ++ store_remaining = PJ_FALSE; ++ pj_uint16_t eaten_bytes = buf->rx_wanted_size - buf->rx_buffer_size; ++ pj_memcpy(buf->rx_buffer + buf->rx_buffer_size, ++ current_packet, eaten_bytes); ++ ++ leftover = buf->rx_wanted_size; ++ current_packet = buf->rx_buffer; ++ parsed += eaten_bytes; ++ ++ buf->rx_buffer_size = 0; ++ buf->rx_wanted_size = 0; ++ } ++ } else if (leftover > 1) { ++ leftover = GETVAL16H(current_packet, current_packet+1); ++ current_packet += 2; ++ parsed += 2; ++ if (leftover + parsed <= size) { ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ buf->rx_wanted_size = leftover; ++ } ++ } ++ if (store_remaining) { ++ leftover = size - parsed; ++ pj_memcpy(buf->rx_buffer + buf->rx_buffer_size, ++ current_packet, leftover); ++ buf->rx_buffer_size += leftover; ++ break; ++ } ++ } else { ++#endif ++ parsed = size; ++#if PJ_HAS_TCP ++ } ++#endif ++ /* Check that this is STUN message */ ++ status = pj_stun_msg_check((const pj_uint8_t *)current_packet, leftover, ++ PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); ++ if (status != PJ_SUCCESS) { ++ /* Not STUN -- give it to application */ ++ goto process_app_data; ++ } ++ ++ /* Treat packet as STUN header and copy the STUN message type. ++ * We don't want to access the type directly from the header ++ * since it may not be properly aligned. ++ */ ++ pj_stun_msg_hdr *hdr = (pj_stun_msg_hdr *)current_packet; ++ pj_uint16_t type; ++ pj_memcpy(&type, &hdr->type, 2); ++ type = pj_ntohs(type); ++ ++ /* If the packet is a STUN Binding response and part of the ++ * transaction ID matches our internal ID, then this is ++ * our internal STUN message (Binding request or keep alive). ++ * Give it to our STUN session. ++ */ ++ if (!PJ_STUN_IS_RESPONSE(type) || ++ PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || ++ pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) ++ { ++ /* Not STUN Binding response, or STUN transaction ID mismatch. ++ * This is not our message too -- give it to application. ++ */ ++ goto process_app_data; ++ } ++ ++ /* This is our STUN Binding response. Give it to the STUN session */ ++ status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, current_packet, ++ leftover, PJ_STUN_IS_DATAGRAM, NULL, ++ NULL, rx_addr, sock_addr_len); ++ ++ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; ++ continue; ++ ++process_app_data: ++ if (stun_sock->cb.on_rx_data) ++ (*stun_sock->cb.on_rx_data)(stun_sock, current_packet, ++ (unsigned)leftover, rx_addr, sock_addr_len); ++ ++ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; ++ } while (parsed < size && result); ++ ++ status = pj_grp_lock_release(stun_sock->grp_lock); ++ return result; ++} ++ ++static pj_bool_t on_data_read(pj_activesock_t *asock, ++ void *data, ++ pj_size_t size, ++ pj_status_t status, ++ pj_size_t *remainder) ++{ ++ ++ pj_stun_sock *stun_sock; ++ ++ if (!(stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock))) ++ return PJ_FALSE; ++ ++ pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); ++ /* Log socket error or disconnection */ ++ if (status != PJ_SUCCESS) { ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP ++ || (status != PJ_EEOF && status != 120104 && status != 130054)) ++ { ++ PJ_PERROR(2, (stun_sock->obj_name, status, "read() error")); ++ } else if (status == 120104 ++ || status == 130054 /* RESET BY PEER */) ++ { ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) ++ if (stun_sock->outgoing_socks[i].sock == asock ++ && cb ++ && (cb->on_peer_reset_connection)) ++ { ++ (cb->on_peer_reset_connection)(stun_sock->stun_sess, ++ stun_sock->outgoing_socks[i].addr); ++ } ++ } ++ return PJ_FALSE; ++ } ++#if PJ_HAS_TCP ++ pj_sockaddr_t *rx_addr = NULL; ++ unsigned sock_addr_len = 0; ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) ++ if (stun_sock->outgoing_socks[i].sock == asock) { ++ rx_addr = stun_sock->outgoing_socks[i].addr; ++ sock_addr_len = pj_sockaddr_get_len(rx_addr); ++ if (cb && (cb->on_peer_packet)) ++ (cb->on_peer_packet)(stun_sock->stun_sess, ++ stun_sock->outgoing_socks[i].addr); ++ } ++ ++ if (rx_addr == NULL && stun_sock->incoming_nb != -1) { ++ // It's an incoming message ++ for (int i = 0; i <= stun_sock->incoming_nb; ++i) ++ if (stun_sock->incoming_socks[i].sock == asock) { ++ rx_addr = &stun_sock->incoming_socks[i].addr; ++ sock_addr_len = stun_sock->incoming_socks[i].addr_len; ++ } ++ } ++ return parse_rx_packet(asock, data, size, rx_addr, sock_addr_len); ++#else ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++#endif ++} ++ ++#if PJ_HAS_TCP ++/* ++ * Notification when incoming TCP socket has been connected. ++ * NOTE: cf https://www.pjsip.org/docs/latest/pjlib/docs/html//structpj__activesock__cb.htm if status needed ++ */ ++static pj_bool_t on_stun_sock_accept(pj_activesock_t *active_sock, ++ pj_sock_t sock, ++ const pj_sockaddr_t *src_addr, ++ int src_addr_len) ++{ ++ pj_status_t status; ++ pj_stun_sock *stun_sock; ++ int sock_type = pj_SOCK_STREAM(); ++ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(active_sock); ++ ++ stun_sock->incoming_nb += 1; ++ int nb_check = stun_sock->incoming_nb; ++ pj_sock_t *fd = &stun_sock->incoming_socks[nb_check].fd; ++ pj_activesock_t **asock = &stun_sock->incoming_socks[nb_check].sock; ++ ++ pj_sockaddr_cp(&stun_sock->incoming_socks[nb_check].addr, src_addr); ++ stun_sock->incoming_socks[nb_check].addr_len = src_addr_len; ++ *fd = sock; ++ ++ pj_activesock_cfg activesock_cfg; ++ pj_activesock_cb activesock_cb; ++ ++ pj_activesock_cfg_default(&activesock_cfg); ++ activesock_cfg.grp_lock = stun_sock->grp_lock; ++ activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; ++ activesock_cfg.concurrency = 0; ++ ++ /* Create the active socket */ ++ pj_bzero(&activesock_cb, sizeof(activesock_cb)); ++ activesock_cb.on_data_read = &on_data_read; ++ activesock_cb.on_data_sent = &on_data_sent; ++ ++ status = pj_activesock_create(stun_sock->pool, *fd, sock_type, ++ &activesock_cfg, stun_sock->stun_cfg.ioqueue, ++ &activesock_cb, stun_sock, asock); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Start asynchronous read operations */ ++ pj_status_t result; ++ result = pj_activesock_start_read(*asock, stun_sock->pool, ++ stun_sock->cfg.max_pkt_size, 0); ++ if (result != PJ_SUCCESS) ++ return PJ_FALSE; ++ ++ return PJ_TRUE; + } ++#endif + + /* Start socket. */ + PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, +@@ -526,6 +987,26 @@ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock) + stun_sock->sock_fd = PJ_INVALID_SOCKET; + } + ++ for (int i = 0; i < stun_sock->incoming_nb ; ++i) { ++ if (stun_sock->incoming_socks[i].sock != NULL) { ++ stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; ++ pj_activesock_close(stun_sock->incoming_socks[i].sock); ++ } else if (stun_sock->incoming_socks[i].fd != PJ_INVALID_SOCKET) { ++ pj_sock_close(stun_sock->incoming_socks[i].fd); ++ stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; ++ } ++ } ++ ++ for (int i = 0; i < stun_sock->outgoing_nb ; ++i) { ++ if (stun_sock->outgoing_socks[i].sock != NULL) { ++ stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; ++ pj_activesock_close(stun_sock->outgoing_socks[i].sock); ++ } else if (stun_sock->outgoing_socks[i].fd != PJ_INVALID_SOCKET) { ++ pj_sock_close(stun_sock->outgoing_socks[i].fd); ++ stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; ++ } ++ } ++ + if (stun_sock->stun_sess) { + pj_stun_session_destroy(stun_sock->stun_sess); + } +@@ -634,10 +1115,12 @@ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) + + /* Send request */ + status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, +- PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, ++ PJ_FALSE, ++ (stun_sock->conn_type == PJ_STUN_TP_UDP), ++ &stun_sock->srv_addr, + pj_sockaddr_get_len(&stun_sock->srv_addr), + tdata); +- if (status != PJ_SUCCESS) ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) + goto on_error; + + return PJ_SUCCESS; +@@ -658,6 +1141,8 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, + + pj_grp_lock_acquire(stun_sock->grp_lock); + ++ info->conn_type = stun_sock->conn_type; ++ + /* Copy STUN server address and mapped address */ + pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, + sizeof(pj_sockaddr)); +@@ -770,13 +1255,241 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, + send_key = &stun_sock->send_key; + + size = pkt_len; +- status = pj_activesock_sendto(stun_sock->active_sock, send_key, +- pkt, &size, flag, dst_addr, addr_len); ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP) { ++ status = pj_activesock_sendto(stun_sock->active_sock, send_key, ++ pkt, &size, flag, dst_addr, addr_len); ++ } else { ++#if PJ_HAS_TCP ++ pj_bool_t is_outgoing = PJ_FALSE; ++ pj_bool_t is_incoming = PJ_FALSE; ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { ++ if (pj_sockaddr_cmp(stun_sock->outgoing_socks[i].addr, dst_addr) == 0) { ++ is_outgoing = PJ_TRUE; ++ status = pj_activesock_send(stun_sock->outgoing_socks[i].sock, ++ send_key, pkt, &size, flag); ++ break; ++ } ++ } ++ if (is_outgoing == PJ_FALSE) { ++ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { ++ if (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, ++ dst_addr) == 0) { ++ status = pj_activesock_send(stun_sock->incoming_socks[i].sock, ++ send_key, pkt, &size, flag); ++ is_incoming = PJ_TRUE; ++ break; ++ } ++ } ++ } ++ if (is_outgoing == PJ_FALSE && is_incoming == PJ_FALSE) { ++ status = pj_activesock_send(stun_sock->active_sock, send_key, pkt, ++ &size, flag); ++ } ++ ++#endif ++ } ++ ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++} ++ ++#if PJ_HAS_TCP ++ ++PJ_DECL(pj_status_t) pj_stun_sock_connect(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af, ++ int nb_check) ++{ ++ ++ pj_grp_lock_acquire(stun_sock->grp_lock); ++ int sock_type = pj_SOCK_STREAM(); ++ ++ pj_sock_t *fd = &stun_sock->outgoing_socks[nb_check].fd; ++ pj_activesock_t **asock = &stun_sock->outgoing_socks[nb_check].sock; ++ pj_sockaddr_t **addr = &stun_sock->outgoing_socks[nb_check].addr; ++ ++ pj_status_t status = pj_sock_socket(af, sock_type, 0, fd); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Apply QoS, if specified */ ++ status = pj_sock_apply_qos2(*fd, stun_sock->cfg.qos_type, ++ &stun_sock->cfg.qos_params, 2, stun_sock->obj_name, NULL); ++ if (status != PJ_SUCCESS && !stun_sock->cfg.qos_ignore_error) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Apply socket buffer size */ ++ if (stun_sock->cfg.so_rcvbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_rcvbuf_size; ++ status = pj_sock_setsockopt_sobuf(*fd, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); ++ if (status != PJ_SUCCESS) { ++ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_RCVBUF"); ++ } else { ++ if (sobuf_size < stun_sock->cfg.so_rcvbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, ++ "Warning! Cannot set SO_RCVBUF as configured, " ++ "now=%d, configured=%d", ++ sobuf_size, stun_sock->cfg.so_rcvbuf_size)); ++ } else { ++ PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); ++ } ++ } ++ } ++ ++ if (stun_sock->cfg.so_sndbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_sndbuf_size; ++ status = pj_sock_setsockopt_sobuf(*fd, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); ++ if (status != PJ_SUCCESS) { ++ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_SNDBUF"); ++ } else { ++ if (sobuf_size < stun_sock->cfg.so_sndbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, ++ "Warning! Cannot set SO_SNDBUF as configured, " ++ "now=%d, configured=%d", ++ sobuf_size, stun_sock->cfg.so_sndbuf_size)); ++ } else { ++ PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); ++ } ++ } ++ } ++ ++ /* Init active socket configuration */ ++ { ++ pj_activesock_cfg activesock_cfg; ++ pj_activesock_cb activesock_cb; ++ ++ pj_activesock_cfg_default(&activesock_cfg); ++ activesock_cfg.grp_lock = stun_sock->grp_lock; ++ activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; ++ activesock_cfg.concurrency = 0; ++ ++ /* Create the active socket */ ++ pj_bzero(&activesock_cb, sizeof(activesock_cb)); ++ activesock_cb.on_data_read = &on_data_read; ++ activesock_cb.on_data_sent = &on_data_sent; ++ activesock_cb.on_connect_complete = &on_connect_complete; ++ ++ status = pj_activesock_create(stun_sock->pool, *fd, ++ sock_type, &activesock_cfg, ++ stun_sock->stun_cfg.ioqueue, &activesock_cb, ++ stun_sock, asock); ++ ++ if (status != PJ_SUCCESS) { ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ *addr = (pj_sockaddr_t*)remote_addr; ++ ++ status = pj_activesock_start_connect( ++ *asock, stun_sock->pool, *addr, ++ pj_sockaddr_get_len(*addr)); ++ if (status == PJ_SUCCESS) { ++ on_connect_complete(*asock, status); ++ } else if (status != PJ_EPENDING) { ++ char addrinfo[PJ_INET6_ADDRSTRLEN+8]; ++ pj_perror(3, stun_sock->pool->obj_name, status, "Failed to connect to %s", ++ pj_sockaddr_print(*addr, addrinfo, sizeof(addrinfo), 3)); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ } + + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + ++PJ_DECL(pj_status_t) pj_stun_sock_connect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af) ++{ ++ ++ if (stun_sock->incoming_nb != -1) { ++ // Check if not incoming, if so, already connected (mainly for PRFLX candidates) ++ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { ++ if (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr)==0) { ++ pj_stun_session_cb *cb = ++ pj_stun_session_callback(stun_sock->stun_sess); ++ (cb->on_peer_connection)(stun_sock->stun_sess, PJ_SUCCESS, ++ (pj_sockaddr_t *)remote_addr); ++ return PJ_SUCCESS; ++ } ++ } ++ } ++ ++ /* Create socket and bind socket */ ++ stun_sock->outgoing_nb += 1; ++ int nb_check = stun_sock->outgoing_nb; ++ return pj_stun_sock_connect(stun_sock, remote_addr, af, nb_check); ++ ++} ++ ++PJ_DECL(pj_status_t) pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af) ++{ ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { ++ if (pj_sockaddr_cmp(stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { ++ pj_activesock_close(stun_sock->outgoing_socks[i].sock); ++ return pj_stun_sock_connect(stun_sock, remote_addr, af, i); ++ } ++ } ++ return PJ_EINVAL; ++} ++ ++PJ_DECL(pj_status_t) pj_stun_sock_close(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr) ++{ ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { ++ if (pj_sockaddr_cmp(stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { ++ return pj_activesock_close(stun_sock->outgoing_socks[i].sock); ++ } ++ } ++ ++ for (int i = 0; i <= stun_sock->incoming_nb; ++i) { ++ if (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) == 0) { ++ return pj_activesock_close(stun_sock->incoming_socks[i].sock); ++ } ++ } ++ return PJ_EINVAL; ++} ++ ++static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) ++{ ++ pj_stun_sock *stun_sock; ++ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); ++ ++ pj_status_t result = pj_activesock_start_read(asock, stun_sock->pool, ++ stun_sock->cfg.max_pkt_size, 0); ++ if (result != PJ_SUCCESS) { ++ return PJ_FALSE; ++ }; ++ ++ pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); ++ if (!cb->on_peer_connection) { ++ return PJ_FALSE; ++ } ++ ++ // Get remote connected address ++ pj_sockaddr_t* remote_addr = NULL; ++ for (int i = 0 ; i <= stun_sock->outgoing_nb ; ++i) { ++ if (stun_sock->outgoing_socks[i].sock == asock) { ++ remote_addr = stun_sock->outgoing_socks[i].addr; ++ } ++ } ++ if (!remote_addr) return PJ_FALSE; ++ (cb->on_peer_connection)(stun_sock->stun_sess, status, remote_addr); ++ return PJ_TRUE; ++} ++ ++#endif ++ + /* This callback is called by the STUN session to send packet */ + static pj_status_t sess_on_send_msg(pj_stun_session *sess, + void *token, +@@ -787,6 +1500,7 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, + { + pj_stun_sock *stun_sock; + pj_ssize_t size; ++ pj_status_t status; + + stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); + if (!stun_sock || !stun_sock->active_sock) { +@@ -800,9 +1514,29 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, + PJ_UNUSED_ARG(token); + + size = pkt_size; +- return pj_activesock_sendto(stun_sock->active_sock, +- &stun_sock->int_send_key, +- pkt, &size, 0, dst_addr, addr_len); ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP) { ++ status = pj_activesock_sendto(stun_sock->active_sock, ++ &stun_sock->int_send_key,pkt, &size, 0, ++ dst_addr, addr_len); ++ } ++#if PJ_HAS_TCP ++ else { ++ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { ++ if (!pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, dst_addr)) { ++ status = pj_activesock_send(stun_sock->incoming_socks[i].sock, ++ &stun_sock->int_send_key, ++ pkt, &size, 0); ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) ++ PJ_PERROR(4,(stun_sock->obj_name, status, ++ "Error sending answer on incoming_sock(s)")); ++ } ++ } ++ /* last attempt */ ++ status = pj_activesock_send(stun_sock->active_sock, ++ &stun_sock->int_send_key, pkt, &size, 0); ++ } ++#endif ++ return status; + } + + /* This callback is called by the STUN session when outgoing transaction +@@ -942,8 +1676,6 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, + pj_status_t status) + { + pj_stun_sock *stun_sock; +- pj_stun_msg_hdr *hdr; +- pj_uint16_t type; + + stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); + if (!stun_sock) +@@ -955,58 +1687,7 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, + return PJ_TRUE; + } + +- pj_grp_lock_acquire(stun_sock->grp_lock); +- +- /* Check that this is STUN message */ +- status = pj_stun_msg_check((const pj_uint8_t*)data, size, +- PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); +- if (status != PJ_SUCCESS) { +- /* Not STUN -- give it to application */ +- goto process_app_data; +- } +- +- /* Treat packet as STUN header and copy the STUN message type. +- * We don't want to access the type directly from the header +- * since it may not be properly aligned. +- */ +- hdr = (pj_stun_msg_hdr*) data; +- pj_memcpy(&type, &hdr->type, 2); +- type = pj_ntohs(type); +- +- /* If the packet is a STUN Binding response and part of the +- * transaction ID matches our internal ID, then this is +- * our internal STUN message (Binding request or keep alive). +- * Give it to our STUN session. +- */ +- if (!PJ_STUN_IS_RESPONSE(type) || +- PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || +- pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) +- { +- /* Not STUN Binding response, or STUN transaction ID mismatch. +- * This is not our message too -- give it to application. +- */ +- goto process_app_data; +- } +- +- /* This is our STUN Binding response. Give it to the STUN session */ +- status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, +- PJ_STUN_IS_DATAGRAM, NULL, NULL, +- src_addr, addr_len); +- +- status = pj_grp_lock_release(stun_sock->grp_lock); +- +- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; +- +-process_app_data: +- if (stun_sock->cb.on_rx_data) { +- (*stun_sock->cb.on_rx_data)(stun_sock, data, (unsigned)size, +- src_addr, addr_len); +- status = pj_grp_lock_release(stun_sock->grp_lock); +- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; +- } +- +- status = pj_grp_lock_release(stun_sock->grp_lock); +- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; ++ return parse_rx_packet(asock, data, size, src_addr, addr_len); + } + + /* Callback from active socket about send status */ +@@ -1047,3 +1728,8 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock, + return PJ_TRUE; + } + ++pj_stun_session* pj_stun_sock_get_session(pj_stun_sock *stun_sock) ++{ ++ return stun_sock ? stun_sock->stun_sess : NULL; ++} ++ +diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c +index e4d67db0f..569b4826d 100644 +--- a/pjnath/src/pjnath/stun_transaction.c ++++ b/pjnath/src/pjnath/stun_transaction.c +@@ -396,6 +396,9 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, + PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, + pj_bool_t mod_count) + { ++ if (!tsx) ++ return PJ_EINVAL; ++ + if (tsx->destroy_timer.id != 0) { + return PJ_SUCCESS; + } +diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c +index a378b8672..88985af69 100644 +--- a/pjnath/src/pjnath/turn_session.c ++++ b/pjnath/src/pjnath/turn_session.c +@@ -311,7 +311,8 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg, + stun_cb.on_request_complete = &stun_on_request_complete; + stun_cb.on_rx_indication = &stun_on_rx_indication; + status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb, +- PJ_FALSE, sess->grp_lock, &sess->stun); ++ PJ_FALSE, sess->grp_lock, &sess->stun, ++ conn_type); + if (status != PJ_SUCCESS) { + do_destroy(sess); + return status; +diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c +index dc6304d9f..9b69a6a21 100644 +--- a/pjnath/src/pjnath/turn_sock.c ++++ b/pjnath/src/pjnath/turn_sock.c +@@ -1766,3 +1766,20 @@ static void turn_on_connection_bind_status(pj_turn_session *sess, + peer_addr, addr_len); + } + } ++ ++pj_bool_t pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, ++ const pj_sockaddr_t *peer) ++{ ++ if (!turn_sock) return PJ_FALSE; ++ ++ for (int i = 0; i < turn_sock->data_conn_cnt; ++i) { ++ tcp_data_conn_t* dataconn = &turn_sock->data_conn[i]; ++ if (dataconn) { ++ pj_sockaddr_t* conn_peer = &dataconn->peer_addr; ++ if (pj_sockaddr_cmp(conn_peer, peer) == 0) ++ return PJ_TRUE; ++ } ++ } ++ ++ return PJ_FALSE; ++} +diff --git a/pjnath/src/pjturn-client/client_main.c b/pjnath/src/pjturn-client/client_main.c +index 6f9f1ff1a..e56c510c8 100644 +--- a/pjnath/src/pjturn-client/client_main.c ++++ b/pjnath/src/pjturn-client/client_main.c +@@ -155,7 +155,7 @@ static int init() + + name[strlen(name)-1] = '0'+i; + status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), +- &stun_sock_cb, &ss_cfg, ++ PJ_STUN_TP_UDP, &stun_sock_cb, &ss_cfg, + &g.peer[i], &g.peer[i].stun_sock); + if (status != PJ_SUCCESS) { + my_perror("pj_stun_sock_create()", status); +diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c +index 6c9c9ce11..99d1545c7 100644 +--- a/pjnath/src/pjturn-srv/allocation.c ++++ b/pjnath/src/pjturn-srv/allocation.c +@@ -338,7 +338,8 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, + sess_cb.on_rx_request = &stun_on_rx_request; + sess_cb.on_rx_indication = &stun_on_rx_indication; + status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, +- &sess_cb, PJ_FALSE, NULL, &alloc->sess); ++ &sess_cb, PJ_FALSE, NULL, &alloc->sess, ++ PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + goto on_error; + } +diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c +index 94dda29a3..95ad1793d 100644 +--- a/pjnath/src/pjturn-srv/server.c ++++ b/pjnath/src/pjturn-srv/server.c +@@ -156,7 +156,7 @@ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf, + + status = pj_stun_session_create(&srv->core.stun_cfg, srv->obj_name, + &sess_cb, PJ_FALSE, NULL, +- &srv->core.stun_sess); ++ &srv->core.stun_sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + goto on_error; + } +diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c +index 07ccc31f0..3b93b9417 100644 +--- a/pjsip-apps/src/samples/icedemo.c ++++ b/pjsip-apps/src/samples/icedemo.c +@@ -44,6 +44,7 @@ static struct app_t + pj_str_t stun_srv; + pj_str_t turn_srv; + pj_bool_t turn_tcp; ++ pj_bool_t ice_tcp; + pj_str_t turn_username; + pj_str_t turn_password; + pj_bool_t turn_fingerprint; +@@ -341,6 +342,12 @@ static pj_status_t icedemo_init(void) + else + icedemo.ice_cfg.opt.aggressive = PJ_TRUE; + ++ /* Connection type to STUN server */ ++ if (icedemo.opt.ice_tcp) ++ icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_TCP; ++ else ++ icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_UDP; ++ + /* Configure STUN/srflx candidate resolution */ + if (icedemo.opt.stun_srv.slen) { + char *pos; +@@ -384,7 +391,7 @@ static pj_status_t icedemo_init(void) + icedemo.ice_cfg.turn.auth_cred.data.static_cred.data = icedemo.opt.turn_password; + + /* Connection type to TURN server */ +- if (icedemo.opt.turn_tcp) ++ if (icedemo.opt.ice_tcp) + icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_TCP; + else + icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP; +@@ -395,6 +402,10 @@ static pj_status_t icedemo_init(void) + icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; + } + ++ if (icedemo.opt.ice_tcp) { ++ icedemo.ice_cfg.protocol = PJ_ICE_TP_TCP; ++ } ++ + /* -= That's it for now, initialization is complete =- */ + return PJ_SUCCESS; + } +@@ -530,10 +541,27 @@ static int print_cand(char buffer[], unsigned maxlen, + char *p = buffer; + int printed; + +- PRINT("a=candidate:%.*s %u UDP %u %s %u typ ", ++ /** Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544) ++ * candidate-attribute = "candidate" ":" foundation SP component-id SP ++ * "TCP" SP ++ * priority SP ++ * connection-address SP ++ * port SP ++ * cand-type ++ * [SP rel-addr] ++ * [SP rel-port] ++ * SP tcp-type-ext ++ * *(SP extension-att-name SP ++ * extension-att-value) ++ * ++ * tcp-type-ext = "tcptype" SP tcp-type ++ * tcp-type = "active" / "passive" / "so" ++ */ ++ PRINT("a=candidate:%.*s %u %s %u %s %u typ ", + (int)cand->foundation.slen, + cand->foundation.ptr, + (unsigned)cand->comp_id, ++ cand->transport == PJ_CAND_UDP? "UDP" : "TCP", + cand->prio, + pj_sockaddr_print(&cand->addr, ipaddr, + sizeof(ipaddr), 0), +@@ -542,6 +570,23 @@ static int print_cand(char buffer[], unsigned maxlen, + PRINT("%s\n", + pj_ice_get_cand_type_name(cand->type)); + ++ if (cand->transport != PJ_CAND_UDP) { ++ PRINT(" tcptype"); ++ switch (cand->transport) { ++ case PJ_CAND_TCP_ACTIVE: ++ PRINT(" active"); ++ break; ++ case PJ_CAND_TCP_PASSIVE: ++ PRINT(" passive"); ++ break; ++ case PJ_CAND_TCP_SO: ++ default: ++ PRINT(" so"); ++ break; ++ } ++ } ++ PRINT("\n"); ++ + if (p == buffer+maxlen) + return -PJ_ETOOSMALL; + +@@ -608,6 +653,26 @@ static int encode_session(char buffer[], unsigned maxlen) + sizeof(ipaddr), 0)); + } + ++ if (cand[0].transport != PJ_CAND_UDP) { ++ /** RFC 6544, Section 4.5: ++ * If the default candidate is TCP-based, the agent MUST include the ++ * a=setup and a=connection attributes from RFC 4145 [RFC4145], ++ * following the procedures defined there as if ICE were not in use. ++ */ ++ PRINT("a=setup:"); ++ switch (cand[0].transport) { ++ case PJ_CAND_TCP_ACTIVE: ++ PRINT("active\n"); ++ break; ++ case PJ_CAND_TCP_PASSIVE: ++ PRINT("passive\n"); ++ break; ++ default: ++ return PJ_EINVALIDOP; ++ } ++ PRINT("a=connection:new\n"); ++ } ++ + /* Enumerate all candidates for this component */ + cand_cnt = PJ_ARRAY_SIZE(cand); + status = pj_ice_strans_enum_cands(icedemo.icest, comp+1, +@@ -709,7 +774,7 @@ static void icedemo_show_ice(void) + */ + static void icedemo_input_remote(void) + { +- char linebuf[80]; ++ char linebuf[120]; + unsigned media_cnt = 0; + unsigned comp0_port = 0; + char comp0_addr[80]; +@@ -819,27 +884,43 @@ static void icedemo_input_remote(void) + pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port); + + } else if (strcmp(attr, "candidate")==0) { ++ /** Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544) ++ * candidate-attribute = "candidate" ":" foundation SP component-id ++ * SP "TCP" SP priority SP connection-address SP port SP cand-type [SP ++ * rel-addr] [SP rel-port] SP tcp-type-ext ++ * *(SP extension-att-name SP ++ * extension-att-value) ++ * ++ * tcp-type-ext = "tcptype" SP tcp-type ++ * tcp-type = "active" / "passive" / "so" ++ */ + char *sdpcand = attr+strlen(attr)+1; + int af, cnt; +- char foundation[32], transport[12], ipaddr[80], type[32]; ++ char foundation[32], transport[12], ipaddr[80], type[32], tcp_type[32]; + pj_str_t tmpaddr; + int comp_id, prio, port; + pj_ice_sess_cand *cand; + pj_status_t status; ++ pj_bool_t is_tcp = PJ_FALSE; + +- cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s", ++ cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s tcptype %s\n", + foundation, + &comp_id, + transport, + &prio, + ipaddr, + &port, +- type); +- if (cnt != 7) { ++ type, ++ tcp_type); ++ if (cnt != 7 && cnt != 8) { + PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line")); + goto on_error; + } + ++ if (strcmp(transport, "TCP") == 0) { ++ is_tcp = PJ_TRUE; ++ } ++ + cand = &icedemo.rem.cand[icedemo.rem.cand_cnt]; + pj_bzero(cand, sizeof(*cand)); + +@@ -855,6 +936,23 @@ static void icedemo_input_remote(void) + goto on_error; + } + ++ if (is_tcp) { ++ if (strcmp(tcp_type, "active") == 0) ++ cand->transport = PJ_CAND_TCP_ACTIVE; ++ else if (strcmp(tcp_type, "passive") == 0) ++ cand->transport = PJ_CAND_TCP_PASSIVE; ++ else if (strcmp(tcp_type, "so") == 0) ++ cand->transport = PJ_CAND_TCP_SO; ++ else { ++ PJ_LOG(1, (THIS_FILE, ++ "Error: invalid transport type '%s'", ++ tcp_type)); ++ goto on_error; ++ } ++ } else { ++ cand->transport = PJ_CAND_UDP; ++ } ++ + cand->comp_id = (pj_uint8_t)comp_id; + pj_strdup2(icedemo.pool, &cand->foundation, foundation); + cand->prio = prio; +@@ -879,6 +977,10 @@ static void icedemo_input_remote(void) + + if (cand->comp_id > icedemo.rem.comp_cnt) + icedemo.rem.comp_cnt = cand->comp_id; ++ } else if (strcmp(attr, "setup") == 0) { ++ // TODO ++ } else if (strcmp(attr, "connection") == 0) { ++ // TODO + } + } + break; +diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c +index 474a8d07c..9257f07a4 100644 +--- a/pjsip/src/pjsua-lib/pjsua_core.c ++++ b/pjsip/src/pjsua-lib/pjsua_core.c +@@ -1548,7 +1548,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) + stun_sock_cb.on_status = &test_stun_on_status; + sess->async_wait = PJ_FALSE; + status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve", +- sess->af, &stun_sock_cb, ++ sess->af, PJ_STUN_TP_UDP, &stun_sock_cb, + NULL, sess, &sess->stun_sock); + if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; +-- +2.24.1 + diff --git a/contrib/src/pjproject/0001-win-config.patch b/contrib/src/pjproject/0001-win-config.patch new file mode 100644 index 0000000000000000000000000000000000000000..495d31fcfd873f29e3f032c98a2379d8d571dc14 --- /dev/null +++ b/contrib/src/pjproject/0001-win-config.patch @@ -0,0 +1,25 @@ +From 205d5003e099a62dcb7504dab20ed575081eb597 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Wed, 4 Mar 2020 09:34:42 -0500 +Subject: [PATCH 1/3] win config + +--- + pjlib/include/pj/config_site.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h +index 47dd40ad5..5d4e3d10d 100644 +--- a/pjlib/include/pj/config_site.h ++++ b/pjlib/include/pj/config_site.h +@@ -21,3 +21,8 @@ + #define PJ_ICE_MAX_TURN 4 + #define PJ_ICE_COMP_BITS 2 + #define PJ_ICE_MAX_CHECKS 1024 ++ ++/* ++ * WINDOWS settings. ++ */ ++#define THIRD_PARTY_MEDIA 0 +-- +2.24.1 + diff --git a/contrib/src/pjproject/0002-rfc2466.patch b/contrib/src/pjproject/0002-rfc2466.patch new file mode 100644 index 0000000000000000000000000000000000000000..1080e494fbdf444d6a23bf5bed1c6485df2dadb3 --- /dev/null +++ b/contrib/src/pjproject/0002-rfc2466.patch @@ -0,0 +1,207 @@ +From 753c3c04da7465622d6f0ce2efebdf428fba536f Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 27 Feb 2020 11:34:40 -0500 +Subject: [PATCH 2/9] rfc2466 + +--- + pjnath/src/pjnath/ice_session.c | 170 ++++++++++++++++++++++++++++++++ + 1 file changed, 170 insertions(+) + +diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c +index 960c55156..a50c21c94 100644 +--- a/pjnath/src/pjnath/ice_session.c ++++ b/pjnath/src/pjnath/ice_session.c +@@ -30,6 +30,21 @@ + #include <pj/rand.h> + #include <pj/string.h> + ++#if defined(_WIN32) || defined(__APPLE__) ++/* TODO(sblin): find an alternative for these paltforms */ ++#else ++/* The following headers are used to get DEPRECATED addresses ++ * as specified in RFC 2462 Section 5.5.4 ++ * https://tools.ietf.org/html/rfc2462#section-5.5.4 ++ */ ++#include <arpa/inet.h> ++#include <asm/types.h> ++#include <linux/netlink.h> ++#include <linux/rtnetlink.h> ++#include <sys/socket.h> ++#include <unistd.h> ++#endif ++ + /* String names for candidate types */ + static const char *cand_type_names[] = + { +@@ -712,6 +727,138 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, + #endif + } + ++/* retrieve invalid addresses and store it in a string */ ++static PJ_DEF(void) get_invalid_addresses(char** addresses, size_t* size) ++{ ++#if defined(_WIN32) || defined(__APPLE__) ++ // PJ_TODO("sblin: find alternative for WIN32 and APPLE"); ++#else ++ struct { ++ struct nlmsghdr nlmsg_info; ++ struct ifaddrmsg ifaddrmsg_info; ++ } netlink_req; ++ ++ int fd; ++ ++ long pagesize = sysconf(_SC_PAGESIZE); ++ ++ if (!pagesize) ++ pagesize = 4096; /* Assume pagesize is 4096 if sysconf() failed */ ++ ++ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ++ if(fd < 0) { ++ perror("socket initialization error: abort"); ++ return; ++ } ++ ++ int rtn; ++ ++ bzero(&netlink_req, sizeof(netlink_req)); ++ ++ netlink_req.nlmsg_info.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); ++ netlink_req.nlmsg_info.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; ++ netlink_req.nlmsg_info.nlmsg_type = RTM_GETADDR; ++ netlink_req.nlmsg_info.nlmsg_pid = getpid(); ++ ++ netlink_req.ifaddrmsg_info.ifa_family = AF_INET6; ++ ++ rtn = send(fd, &netlink_req, netlink_req.nlmsg_info.nlmsg_len, 0); ++ if(rtn < 0) { ++ perror("send error: abort"); ++ return; ++ } ++ ++ char read_buffer[pagesize]; ++ struct nlmsghdr *nlmsg_ptr; ++ int nlmsg_len; ++ ++ size_t idx = 0; ++ /* Will store all deprecated addresses into a string */ ++ char* deprecatedAddrs = malloc(256*sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ if (!deprecatedAddrs) { ++ perror("malloc error: abort"); ++ return; ++ } ++ ++ while(1) { ++ int rtn; ++ ++ bzero(read_buffer, pagesize); ++ rtn = recv(fd, read_buffer, pagesize, 0); ++ if(rtn < 0) { ++ perror ("recv(): "); ++ return; ++ } ++ ++ nlmsg_ptr = (struct nlmsghdr *) read_buffer; ++ nlmsg_len = rtn; ++ ++ if (nlmsg_len < sizeof (struct nlmsghdr)) { ++ perror ("Received an uncomplete netlink packet"); ++ return; ++ } ++ ++ if (nlmsg_ptr->nlmsg_type == NLMSG_DONE) ++ break; ++ ++ for(; NLMSG_OK(nlmsg_ptr, nlmsg_len); ++ nlmsg_ptr = NLMSG_NEXT(nlmsg_ptr, nlmsg_len)) ++ { ++ struct ifaddrmsg *ifaddrmsg_ptr; ++ struct rtattr *rtattr_ptr; ++ int ifaddrmsg_len; ++ ++ ifaddrmsg_ptr = (struct ifaddrmsg *) NLMSG_DATA(nlmsg_ptr); ++ ++ if (ifaddrmsg_ptr->ifa_flags & IFA_F_DEPRECATED || ++ ifaddrmsg_ptr->ifa_flags & IFA_F_TENTATIVE) ++ { ++ rtattr_ptr = (struct rtattr *) IFA_RTA(ifaddrmsg_ptr); ++ ifaddrmsg_len = IFA_PAYLOAD(nlmsg_ptr); ++ ++ for(;RTA_OK(rtattr_ptr, ifaddrmsg_len); ++ rtattr_ptr = RTA_NEXT(rtattr_ptr, ifaddrmsg_len)) ++ { ++ switch(rtattr_ptr->rta_type) { ++ case IFA_ADDRESS: ++ /* Any 256 obsolete ips (should not happen), resize the array. */ ++ if (idx > 0 && idx % 256 == 0) { ++ char* newDeprecated = realloc(deprecatedAddrs, ++ (idx + 256)*sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ if (newDeprecated == NULL) { ++ perror("realloc error: abort"); ++ return; ++ } ++ deprecatedAddrs = newDeprecated; ++ } ++ /* Store deprecated IP */ ++ inet_ntop(ifaddrmsg_ptr->ifa_family, ++ RTA_DATA(rtattr_ptr), ++ &deprecatedAddrs[idx*PJ_INET6_ADDRSTRLEN], ++ sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ ++idx; ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ close(fd); ++ *size = idx; ++ if (idx > 0) { ++ char *final = realloc(deprecatedAddrs, ++ idx*sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ if (final) { ++ *addresses = final; ++ } else { ++ perror("realloc error: abort"); ++ } ++ } ++#endif ++} + + /* + * Add ICE candidate +@@ -729,6 +876,29 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + unsigned *p_cand_id, + pj_ice_cand_transport transport) + { ++ /** ++ * RFC 2466: an ip address can have the status DEPRECATED and SHOULD NOT ++ * be used by new by applications unless they already use it. ++ * So, we should ignore these addresses. ++ * Also, ips with the TENTATIVE state are not ready and SHOULD NOT be ++ * used for now. Ignore these addresses too. ++ */ ++ char* deprecatedAddrs = NULL; ++ size_t size = 0; ++ get_invalid_addresses(&deprecatedAddrs, &size); ++ if (deprecatedAddrs != NULL) { ++ char tmpAddrStr[PJ_INET6_ADDRSTRLEN]; ++ pj_sockaddr_print(addr, tmpAddrStr, sizeof(tmpAddrStr), 0); ++ for (int i = 0; i<size*PJ_INET6_ADDRSTRLEN; i+=PJ_INET6_ADDRSTRLEN) { ++ if (!strncmp(tmpAddrStr, &deprecatedAddrs[i], PJ_INET6_ADDRSTRLEN)) { ++ free(deprecatedAddrs); ++ /* This address is considered as deprecated ignore it. */ ++ return PJ_SUCCESS; ++ } ++ } ++ free(deprecatedAddrs); ++ } ++ + pj_ice_sess_cand *lcand; + pj_status_t status = PJ_SUCCESS; + char address[PJ_INET6_ADDRSTRLEN]; +-- +2.24.1 + diff --git a/contrib/src/pjproject/win32_vs_gnutls.patch b/contrib/src/pjproject/0002-win-vs-gnutls.patch similarity index 85% rename from contrib/src/pjproject/win32_vs_gnutls.patch rename to contrib/src/pjproject/0002-win-vs-gnutls.patch index 01c12e5e082979f2ea00b839e537d3c1be45a000..c2fccd54d4cb5716d1a87c209a14ef8718aba576 100644 --- a/contrib/src/pjproject/win32_vs_gnutls.patch +++ b/contrib/src/pjproject/0002-win-vs-gnutls.patch @@ -1,3 +1,16 @@ +From c8dfc738005dbc44975855cca8ac626af0f4ca4d Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 27 Feb 2020 14:40:07 -0500 +Subject: [PATCH 2/3] win vs gnutls + +--- + pjlib/build/pjlib.vcxproj | 12 +++++++++--- + pjlib/include/pj/config_site.h | 3 +++ + pjmedia/build/pjmedia.vcxproj | 12 +++++++++--- + 3 files changed, 21 insertions(+), 6 deletions(-) + +diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj +index f2d6fb42c..dee805a7c 100644 --- a/pjlib/build/pjlib.vcxproj +++ b/pjlib/build/pjlib.vcxproj @@ -309,14 +309,17 @@ @@ -35,34 +48,19 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Static|Win32'"> <ClCompile> ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -570,6 +570,7 @@ - * that this will increase the footprint of the libraries since it - * tracks the filename and line number of each functions. - */ -+#define PJ_OS_HAS_CHECK_STACK 0 - #ifndef PJ_OS_HAS_CHECK_STACK - # define PJ_OS_HAS_CHECK_STACK 0 - #endif -@@ -919,7 +920,7 @@ - * Default: 0 (for now) +diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h +index 5d4e3d10d..a37d6d88c 100644 +--- a/pjlib/include/pj/config_site.h ++++ b/pjlib/include/pj/config_site.h +@@ -26,3 +26,6 @@ + * WINDOWS settings. */ - #ifndef PJ_HAS_SSL_SOCK --# define PJ_HAS_SSL_SOCK 0 -+# define PJ_HAS_SSL_SOCK 1 - #endif - - -@@ -945,7 +946,7 @@ - # if PJ_HAS_SSL_SOCK==0 - # define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_NONE - # else --# define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_OPENSSL -+# define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_GNUTLS - # endif - #endif - + #define THIRD_PARTY_MEDIA 0 ++#define PJ_OS_HAS_CHECK_STACK 0 ++#define PJ_HAS_SSL_SOCK 1 ++#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_GNUTLS +diff --git a/pjmedia/build/pjmedia.vcxproj b/pjmedia/build/pjmedia.vcxproj +index e596939d0..0c7108b83 100644 --- a/pjmedia/build/pjmedia.vcxproj +++ b/pjmedia/build/pjmedia.vcxproj @@ -307,14 +307,17 @@ @@ -101,5 +99,5 @@ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> -- -2.19.0.windows.1 +2.24.1 diff --git a/contrib/src/pjproject/0003-add-tcp-keep-alive.patch b/contrib/src/pjproject/0003-add-tcp-keep-alive.patch new file mode 100644 index 0000000000000000000000000000000000000000..9d8b846a5bd372a0fd5aba0e4dc65e0c6ab5dd6b --- /dev/null +++ b/contrib/src/pjproject/0003-add-tcp-keep-alive.patch @@ -0,0 +1,336 @@ +From 0d46f77fe2cd2e3197f3b7b745d822b6c1cb9790 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 27 Feb 2020 12:57:17 -0500 +Subject: [PATCH 3/9] add tcp keep alive + +--- + pjlib/include/pj/sock.h | 29 +++++++++++++++++++ + pjlib/src/pj/sock_bsd.c | 45 +++++++++++++++++++++++++++++ + pjlib/src/pj/sock_common.c | 20 +++++++++++++ + pjlib/src/pj/sock_uwp.cpp | 24 +++++++++++++++ + pjlib/src/pj/symbols.c | 3 ++ + pjnath/include/pjnath/ice_session.h | 5 ++++ + pjnath/include/pjnath/ice_strans.h | 7 +++++ + pjnath/src/pjnath/ice_session.c | 6 ++++ + pjnath/src/pjnath/ice_strans.c | 10 +++++++ + 9 files changed, 149 insertions(+) + +diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h +index 4daf298c5..095a38335 100644 +--- a/pjlib/include/pj/sock.h ++++ b/pjlib/include/pj/sock.h +@@ -313,6 +313,11 @@ extern const pj_uint16_t PJ_SO_REUSEADDR; + /** Do not generate SIGPIPE. @see pj_SO_NOSIGPIPE */ + extern const pj_uint16_t PJ_SO_NOSIGPIPE; + ++extern const pj_uint16_t PJ_SO_KEEPALIVE; ++extern const pj_uint16_t PJ_TCP_KEEPIDLE; ++extern const pj_uint16_t PJ_TCP_KEEPINTVL; ++extern const pj_uint16_t PJ_TCP_KEEPCNT; ++ + /** Set the protocol-defined priority for all packets to be sent on socket. + */ + extern const pj_uint16_t PJ_SO_PRIORITY; +@@ -343,9 +348,21 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; + /** Get #PJ_SO_SNDBUF constant */ + PJ_DECL(pj_uint16_t) pj_SO_SNDBUF(void); + ++ /** Get #PJ_SO_KEEPALIVE constant */ ++# define pj_SO_KEEPALIVE() PJ_SO_KEEPALIVE(void); ++ + /** Get #PJ_TCP_NODELAY constant */ + PJ_DECL(pj_uint16_t) pj_TCP_NODELAY(void); + ++ /** Get #PJ_TCP_KEEPIDLE constant */ ++# define pj_TCP_KEEPIDLE() PJ_TCP_KEEPIDLE(void); ++ ++ /** Get #PJ_TCP_KEEPINTVL constant */ ++# define pj_TCP_KEEPINTVL() PJ_TCP_KEEPINTVL(void); ++ ++ /** Get #PJ_TCP_KEEPCNT constant */ ++# define pj_TCP_KEEPCNT() PJ_TCP_KEEPCNT(void); ++ + /** Get #PJ_SO_REUSEADDR constant */ + PJ_DECL(pj_uint16_t) pj_SO_REUSEADDR(void); + +@@ -379,9 +396,21 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; + /** Get #PJ_SO_SNDBUF constant */ + # define pj_SO_SNDBUF() PJ_SO_SNDBUF + ++ /** Get #PJ_SO_KEEPALIVE constant */ ++# define pj_SO_KEEPALIVE() PJ_SO_KEEPALIVE ++ + /** Get #PJ_TCP_NODELAY constant */ + # define pj_TCP_NODELAY() PJ_TCP_NODELAY + ++ /** Get #PJ_TCP_KEEPIDLE constant */ ++# define pj_TCP_KEEPIDLE() PJ_TCP_KEEPIDLE ++ ++ /** Get #PJ_TCP_KEEPINTVL constant */ ++# define pj_TCP_KEEPINTVL() PJ_TCP_KEEPINTVL ++ ++ /** Get #PJ_TCP_KEEPCNT constant */ ++# define pj_TCP_KEEPCNT() PJ_TCP_KEEPCNT ++ + /** Get #PJ_SO_REUSEADDR constant */ + # define pj_SO_REUSEADDR() PJ_SO_REUSEADDR + +diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c +index e416991de..0e9bfdbe9 100644 +--- a/pjlib/src/pj/sock_bsd.c ++++ b/pjlib/src/pj/sock_bsd.c +@@ -28,6 +28,15 @@ + + #define THIS_FILE "sock_bsd.c" + ++#if !defined(PJ_WIN32) && !defined(PJ_WIN64) ++# if !defined(SOL_TCP) && defined(IPPROTO_TCP) ++# define SOL_TCP IPPROTO_TCP ++# endif ++# if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) ++# define TCP_KEEPIDLE TCP_KEEPALIVE ++# endif ++#endif ++ + /* + * Address families conversion. + * The values here are indexed based on pj_addr_family. +@@ -144,7 +153,19 @@ const pj_uint16_t PJ_IPV6_TCLASS = 0xFFFF; + const pj_uint16_t PJ_SO_TYPE = SO_TYPE; + const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; + const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; ++const pj_uint16_t PJ_SO_KEEPALIVE = SO_KEEPALIVE; + const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY; ++#if !defined(PJ_WIN32) && !defined(PJ_WIN64) ++# ifdef TCP_KEEPIDLE ++const pj_uint16_t PJ_TCP_KEEPIDLE = TCP_KEEPIDLE; ++# endif ++# ifdef TCP_KEEPINTVL ++const pj_uint16_t PJ_TCP_KEEPINTVL = TCP_KEEPINTVL; ++# endif ++# ifdef TCP_KEEPCNT ++const pj_uint16_t PJ_TCP_KEEPCNT = TCP_KEEPCNT; ++# endif ++#endif + const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR; + #ifdef SO_NOSIGPIPE + const pj_uint16_t PJ_SO_NOSIGPIPE = SO_NOSIGPIPE; +@@ -517,6 +538,20 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + if (rc==SOCKET_ERROR) { + // Ignored.. + } ++ } else if(type == pj_SOCK_STREAM()) { ++#ifndef SIO_KEEPALIVE_VALS ++# define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4) ++#endif ++ DWORD dwBytesReturned = 0; ++ struct tcp_keepalive { ++ ULONG onoff; ++ ULONG keepalivetime; ++ ULONG keepaliveinterval; ++ } vals = { TRUE, 30000, 30000 }; ++ WSAIoctl(*sock, SIO_KEEPALIVE_VALS, ++ &vals, sizeof(vals), ++ NULL, 0, &dwBytesReturned, ++ NULL, NULL); + } + #endif + +@@ -548,6 +583,16 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + if (type == pj_SOCK_STREAM()) { + pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(), + &val, sizeof(val)); ++ pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_KEEPALIVE(), ++ &val, sizeof(val)); ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPCNT(), ++ &val, sizeof(val)); ++ val = 30; ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPIDLE(), ++ &val, sizeof(val)); ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPINTVL(), ++ &val, sizeof(val)); ++ val = 1; + } + #if defined(PJ_SOCK_HAS_IPV6_V6ONLY) && PJ_SOCK_HAS_IPV6_V6ONLY != 0 + if (af == PJ_AF_INET6) { +diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c +index 693f3af62..6beb28590 100644 +--- a/pjlib/src/pj/sock_common.c ++++ b/pjlib/src/pj/sock_common.c +@@ -1346,11 +1346,31 @@ PJ_DEF(pj_uint16_t) pj_SO_SNDBUF(void) + return PJ_SO_SNDBUF; + } + ++PJ_DEF(pj_uint16_t) pj_SO_KEEPALIVE(void) ++{ ++ return PJ_SO_KEEPALIVE; ++} ++ + PJ_DEF(pj_uint16_t) pj_TCP_NODELAY(void) + { + return PJ_TCP_NODELAY; + } + ++PJ_DEF(pj_uint16_t) pj_TCP_KEEPIDLE(void) ++{ ++ return PJ_TCP_KEEPIDLE ++} ++ ++PJ_DEF(pj_uint16_t) pj_TCP_KEEPINTVL(void) ++{ ++ return PJ_TCP_KEEPINTVL ++} ++ ++PJ_DEF(pj_uint16_t) pj_TCP_KEEPCNT(void) ++{ ++ return PJ_TCP_KEEPCNT ++} ++ + PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void) + { + return PJ_SO_REUSEADDR; +diff --git a/pjlib/src/pj/sock_uwp.cpp b/pjlib/src/pj/sock_uwp.cpp +index 876c3287a..34baebcee 100644 +--- a/pjlib/src/pj/sock_uwp.cpp ++++ b/pjlib/src/pj/sock_uwp.cpp +@@ -69,6 +69,24 @@ const pj_uint16_t PJ_SOL_IP = IPPROTO_IP; + const pj_uint16_t PJ_SOL_IP = 0; + #endif /* SOL_IP */ + ++#if defined(TCP_KEEPIDLE) ++const pj_uint16_t PJ_TCP_KEEPIDLE = TCP_KEEPIDLE; ++#else ++const pj_uint16_t PJ_TCP_KEEPIDLE = 4; ++#endif ++ ++#if defined(TCP_KEEPINTVL) ++const pj_uint16_t PJ_TCP_KEEPINTVL = TCP_KEEPINTVL; ++#else ++const pj_uint16_t PJ_TCP_KEEPINTVL = 5; ++#endif ++ ++#if defined(TCP_KEEPCNT) ++const pj_uint16_t PJ_TCP_KEEPCNT = TCP_KEEPCNT; ++#else ++const pj_uint16_t PJ_TCP_KEEPCNT = 6; ++#endif ++ + #if defined(SOL_TCP) + const pj_uint16_t PJ_SOL_TCP = SOL_TCP; + #elif defined(IPPROTO_TCP) +@@ -79,6 +97,12 @@ const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; + const pj_uint16_t PJ_SOL_TCP = 6; + #endif /* SOL_TCP */ + ++#if defined(SOL_KEEPALIVE) ++const pj_uint16_t PJ_SOL_KEEPALIVE = SOL_KEEPALIVE; ++#else ++const pj_uint16_t PJ_SOL_KEEPALIVE = 9; ++#endif ++ + #ifdef SOL_UDP + const pj_uint16_t PJ_SOL_UDP = SOL_UDP; + #elif defined(IPPROTO_UDP) +diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c +index ab83af956..966a9fc43 100644 +--- a/pjlib/src/pj/symbols.c ++++ b/pjlib/src/pj/symbols.c +@@ -259,6 +259,9 @@ PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) + PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) + PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) + PJ_EXPORT_SYMBOL(PJ_SOL_IP) ++PJ_EXPORT_SYMBOL(PJ_TCP_KEEPIDLE) ++PJ_EXPORT_SYMBOL(PJ_TCP_KEEPINTVL) ++PJ_EXPORT_SYMBOL(PJ_TCP_KEEPCNT) + PJ_EXPORT_SYMBOL(PJ_SOL_TCP) + PJ_EXPORT_SYMBOL(PJ_SOL_UDP) + PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) +diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h +index 39c197c29..d44e3e896 100644 +--- a/pjnath/include/pjnath/ice_session.h ++++ b/pjnath/include/pjnath/ice_session.h +@@ -639,6 +639,11 @@ typedef struct pj_ice_sess_cb + pj_ice_sess_checklist *clist, + unsigned check_id); + ++ /** ++ * If an internal TCP keep alive, this mount the error to the application ++ */ ++ void (*on_ice_destroy)(pj_ice_sess *ice); ++ + } pj_ice_sess_cb; + + +diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h +index 9eb74b35f..1269442cd 100644 +--- a/pjnath/include/pjnath/ice_strans.h ++++ b/pjnath/include/pjnath/ice_strans.h +@@ -192,6 +192,13 @@ typedef struct pj_ice_strans_cb + pj_ice_strans_op op, + pj_status_t status); + ++ /** ++ * This callback is called if an internal operation fails ++ * ++ * @param ice_st The ICE stream transport. ++ */ ++ void (*on_destroy)(pj_ice_strans *ice_st); ++ + } pj_ice_strans_cb; + + +diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c +index a50c21c94..4ba44f38b 100644 +--- a/pjnath/src/pjnath/ice_session.c ++++ b/pjnath/src/pjnath/ice_session.c +@@ -1435,6 +1435,12 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) + PJ_FALSE, PJ_FALSE, + &the_check->rcand->addr, + addr_len, tdata); ++ if (status == 120032) { ++ if (ice->cb.on_ice_destroy) { ++ ice->cb.on_ice_destroy(ice); ++ } ++ return; ++ } + + /* Restore FINGERPRINT usage */ + pj_stun_session_use_fingerprint(comp->stun_sess, saved); +diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c +index 8ba2172e5..4adc9bc70 100644 +--- a/pjnath/src/pjnath/ice_strans.c ++++ b/pjnath/src/pjnath/ice_strans.c +@@ -98,6 +98,7 @@ static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2) + ////////////////////////////////////////////////////////////////////////////// + + /* ICE callbacks */ ++static void on_ice_destroy(pj_ice_sess *ice); + static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); + static pj_status_t ice_tx_pkt(pj_ice_sess *ice, + unsigned comp_id, +@@ -1322,6 +1323,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, + ice_cb.select_turn_dataconn = &ice_select_turn_dataconn; + ice_cb.reconnect_tcp_connection = &ice_reconnect_tcp_connection; + ice_cb.close_tcp_connection = &ice_close_tcp_connection; ++ ice_cb.on_ice_destroy = &on_ice_destroy; + #endif + + /* Create! */ +@@ -1938,6 +1940,14 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st, + dst_addr_len, PJ_TRUE, PJ_FALSE); + } + ++static void on_ice_destroy(pj_ice_sess *ice) ++{ ++ pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; ++ ++ if (ice_st->cb.on_destroy) { ++ (*ice_st->cb.on_destroy)(ice_st); ++ } ++} + + /* + * Callback called by ICE session when ICE processing is complete, either +-- +2.24.1 + diff --git a/contrib/src/pjproject/win_vs2017_props.patch b/contrib/src/pjproject/0003-win-vs2017-props.patch similarity index 66% rename from contrib/src/pjproject/win_vs2017_props.patch rename to contrib/src/pjproject/0003-win-vs2017-props.patch index 6ccc60359a9373ceeac868a1ad3a70496276ab99..6c306c3dddde534ae57c8f5669ce0ce1f72ec89c 100644 --- a/contrib/src/pjproject/win_vs2017_props.patch +++ b/contrib/src/pjproject/0003-win-vs2017-props.patch @@ -1,43 +1,64 @@ ---- a/build/vs/pjproject-vs14-common-config.props -+++ b/build/vs/pjproject-vs14-common-config.props -@@ -18,22 +18,22 @@ - <PropertyGroup> - <API_Family Condition="'$(API_Family)'==''">WinDesktop</API_Family> - <PreprocessorDef></PreprocessorDef> -- <DefaultToolset>v140</DefaultToolset> -+ <DefaultToolset>v141</DefaultToolset> - </PropertyGroup> - <Choose> - <When Condition="'$(API_Family)'=='WinDesktop'"> - <PropertyGroup> -- <BuildToolset>v140</BuildToolset> -+ <BuildToolset>v141</BuildToolset> - <PreprocessorDef Condition="'$(Platform)'=='Win32'">WIN32;PJ_WIN32=1;PJ_M_I386=1;</PreprocessorDef> - <PreprocessorDef Condition="'$(Platform)'=='x64'">WIN64;PJ_WIN64=1;PJ_M_X86_64=1;</PreprocessorDef> - </PropertyGroup> - </When> - <When Condition="'$(API_Family)'=='UWP'"> - <PropertyGroup> -- <BuildToolset>v140</BuildToolset> -+ <BuildToolset>v141</BuildToolset> - <PreprocessorDef>PJ_WIN32_UWP;UNICODE;_UNICODE;</PreprocessorDef> - <PreprocessorDef Condition="'$(Platform)'=='ARM'">$(PreprocessorDef);PJ_M_ARMV7=1;</PreprocessorDef> -- <PlatformVersion>10.0.10586.0</PlatformVersion> -+ <PlatformVersion>10.0.16299.0</PlatformVersion> - <MinPlatformVersion>10.0.10240.0</MinPlatformVersion> - <AppTypeRev>10.0</AppTypeRev> - ---- a/build/vs/pjproject-vs14-common-defaults.props -+++ b/build/vs/pjproject-vs14-common-defaults.props -@@ -3,7 +3,7 @@ - <ImportGroup Label="PropertySheets"> - </ImportGroup> - <PropertyGroup Label="UserMacros"> -- <VSVer>14</VSVer> -+ <VSVer>15</VSVer> - </PropertyGroup> - <PropertyGroup> - <_ProjectFileVersion>14.0.22823.1</_ProjectFileVersion> --- -2.10.2.windows.1 - +From e9c6d327b059866dbf02a3ffd1004bbc677b87db Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 27 Feb 2020 14:43:27 -0500 +Subject: [PATCH 3/3] win vs2017 props + +--- + build/vs/pjproject-vs14-common-config.props | 8 ++++---- + build/vs/pjproject-vs14-common-defaults.props | 4 ++-- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/build/vs/pjproject-vs14-common-config.props b/build/vs/pjproject-vs14-common-config.props +index de8848ff5..6aa0276ba 100644 +--- a/build/vs/pjproject-vs14-common-config.props ++++ b/build/vs/pjproject-vs14-common-config.props +@@ -18,22 +18,22 @@ + <PropertyGroup> + <API_Family Condition="'$(API_Family)'==''">WinDesktop</API_Family> + <PreprocessorDef></PreprocessorDef> +- <DefaultToolset>v140</DefaultToolset> ++ <DefaultToolset>v141</DefaultToolset> + </PropertyGroup> + <Choose> + <When Condition="'$(API_Family)'=='WinDesktop'"> + <PropertyGroup> +- <BuildToolset>v140</BuildToolset> ++ <BuildToolset>v141</BuildToolset> + <PreprocessorDef Condition="'$(Platform)'=='Win32'">WIN32;PJ_WIN32=1;PJ_M_I386=1;</PreprocessorDef> + <PreprocessorDef Condition="'$(Platform)'=='x64'">WIN64;PJ_WIN64=1;PJ_M_X86_64=1;</PreprocessorDef> + </PropertyGroup> + </When> + <When Condition="'$(API_Family)'=='UWP'"> + <PropertyGroup> +- <BuildToolset>v140</BuildToolset> ++ <BuildToolset>v141</BuildToolset> + <PreprocessorDef>PJ_WIN32_UWP;UNICODE;_UNICODE;</PreprocessorDef> + <PreprocessorDef Condition="'$(Platform)'=='ARM'">$(PreprocessorDef);PJ_M_ARMV7=1;</PreprocessorDef> +- <PlatformVersion>10.0.10586.0</PlatformVersion> ++ <PlatformVersion>10.0.16299.0</PlatformVersion> + <MinPlatformVersion>10.0.10240.0</MinPlatformVersion> + <AppTypeRev>10.0</AppTypeRev> + +diff --git a/build/vs/pjproject-vs14-common-defaults.props b/build/vs/pjproject-vs14-common-defaults.props +index 526f6c925..198282b90 100644 +--- a/build/vs/pjproject-vs14-common-defaults.props ++++ b/build/vs/pjproject-vs14-common-defaults.props +@@ -3,7 +3,7 @@ + <ImportGroup Label="PropertySheets"> + </ImportGroup> + <PropertyGroup Label="UserMacros"> +- <VSVer>14</VSVer> ++ <VSVer>15</VSVer> + </PropertyGroup> + <PropertyGroup> + <_ProjectFileVersion>14.0.22823.1</_ProjectFileVersion> +@@ -35,4 +35,4 @@ + <Value>$(VSVer)</Value> + </BuildMacro> + </ItemGroup> +-</Project> +\ No newline at end of file ++</Project> +-- +2.24.1 + diff --git a/contrib/src/pjproject/0004-multiple_listeners.patch b/contrib/src/pjproject/0004-multiple_listeners.patch new file mode 100644 index 0000000000000000000000000000000000000000..58494dba77be407ebf68ff1430cdbabc54e0154c --- /dev/null +++ b/contrib/src/pjproject/0004-multiple_listeners.patch @@ -0,0 +1,64 @@ +From 395a08ba6ebacf0e4039a73128fa147510f36f97 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 20 Feb 2020 12:26:02 -0500 +Subject: [PATCH] multiple_listeners + +--- + pjsip/src/pjsip/sip_transport.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c +index 24b59153b..a336a8648 100644 +--- a/pjsip/src/pjsip/sip_transport.c ++++ b/pjsip/src/pjsip/sip_transport.c +@@ -1471,10 +1471,16 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, + /* Check that no same factory has been registered. */ + status = PJ_SUCCESS; + for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { +- if (p == tpf) { +- status = PJ_EEXISTS; +- break; +- } ++ if (p->type == tpf->type && ++ !pj_sockaddr_cmp(&tpf->local_addr, &p->local_addr)) ++ { ++ status = PJSIP_ETYPEEXISTS; ++ break; ++ } ++ if (p == tpf) { ++ status = PJ_EEXISTS; ++ break; ++ } + } + + if (status != PJ_SUCCESS) { +@@ -2327,10 +2333,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, + } + } + ++ unsigned flag = pjsip_transport_get_flag_from_type(type); + if (tp_ref == NULL && + (!sel || sel->disable_connection_reuse == PJ_FALSE)) + { +- unsigned flag = pjsip_transport_get_flag_from_type(type); + const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote; + + +@@ -2429,6 +2435,14 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, + + } else { + ++ /* Make sure we don't use another factory than the one given if ++ secure flag is set */ ++ if (flag & PJSIP_TRANSPORT_SECURE) { ++ TRACE_((THIS_FILE, "Can't create new TLS transport with no " ++ "provided suitable TLS listener.")); ++ return PJSIP_ETPNOTSUITABLE; ++ } ++ + /* Find factory with type matches the destination type */ + factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { +-- +2.24.1 + diff --git a/contrib/src/pjproject/0005-fix_ebusy_turn.patch b/contrib/src/pjproject/0005-fix_ebusy_turn.patch new file mode 100644 index 0000000000000000000000000000000000000000..9bcdf129bb35beb4b78d732517a84c10b512780a --- /dev/null +++ b/contrib/src/pjproject/0005-fix_ebusy_turn.patch @@ -0,0 +1,582 @@ +From a9a8cc6b62dc08e34ed4f7893a822809dd051572 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Wed, 19 Feb 2020 15:17:38 -0500 +Subject: [PATCH 5/9] fix_ebusy_turn + +--- + pjnath/include/pjnath/turn_session.h | 126 ++++++++++++++++++++------- + pjnath/include/pjnath/turn_sock.h | 27 ++++++ + pjnath/src/pjnath/ice_strans.c | 2 +- + pjnath/src/pjnath/turn_session.c | 39 ++++++--- + pjnath/src/pjnath/turn_sock.c | 102 +++++++++++++++++++++- + 5 files changed, 248 insertions(+), 48 deletions(-) + +diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h +index 956f57bf0..04add93db 100644 +--- a/pjnath/include/pjnath/turn_session.h ++++ b/pjnath/include/pjnath/turn_session.h +@@ -1,5 +1,5 @@ + /* $Id$ */ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * +@@ -15,7 +15,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #ifndef __PJNATH_TURN_SESSION_H__ + #define __PJNATH_TURN_SESSION_H__ +@@ -50,10 +50,10 @@ or to support new transport types to TURN, such as TLS. + These steps describes how to use the TURN session: + + - <b>Creating the session</b>:\n +- use #pj_turn_session_create() to create the session. ++ use #pj_turn_session_create() to create the session. + + - <b>Configuring credential</b>:\n +- all TURN operations requires the use of authentication (it uses STUN ++ all TURN operations requires the use of authentication (it uses STUN + long term autentication method). Use #pj_turn_session_set_credential() + to configure the TURN credential to be used by the session. + +@@ -62,8 +62,8 @@ These steps describes how to use the TURN session: + Allocate request (with pj_turn_session_alloc()). This function will + resolve the TURN server using DNS SRV resolution if the \a resolver + is set. The server resolution process will complete asynchronously, +- and application will be notified in \a on_state() callback of the +- #pj_turn_session_cb structurewith the session state set to ++ and application will be notified in \a on_state() callback of the ++ #pj_turn_session_cb structurewith the session state set to + PJ_TURN_STATE_RESOLVED. + + - <b>Creating allocation</b>:\n +@@ -75,40 +75,40 @@ These steps describes how to use the TURN session: + + - <b>Getting the allocation result</b>:\n + if allocation is successful, the session state will progress to +- \a PJ_TURN_STATE_READY, otherwise the state will be ++ \a PJ_TURN_STATE_READY, otherwise the state will be + \a PJ_TURN_STATE_DEALLOCATED or higher. Session state progression is +- reported in the \a on_state() callback of the #pj_turn_session_cb ++ reported in the \a on_state() callback of the #pj_turn_session_cb + structure. On successful allocation, application may retrieve the + allocation info by calling #pj_turn_session_get_info(). + + - <b>Sending data through the relay</b>.\n +- Once allocation has been created, client may send data to any remote ++ Once allocation has been created, client may send data to any remote + endpoints (called peers in TURN terminology) via the "relay port". It does +- so by calling #pj_turn_session_sendto(), giving the peer address ++ so by calling #pj_turn_session_sendto(), giving the peer address + in the function argument. But note that at this point peers are not allowed + to send data towards the client (via the "relay port") before permission is + installed for that peer. + + - <b>Creating permissions</b>.\n +- Permission needs to be created in the TURN server so that a peer can send ++ Permission needs to be created in the TURN server so that a peer can send + data to the client via the relay port (a peer in this case is identified by + its IP address). Without this, when the TURN server receives data from the + peer in the "relay port", it will drop this data. Create the permission by + calling #pj_turn_session_set_perm(), specifying the peer IP address in the +- argument (the port part of the address is ignored). More than one IP ++ argument (the port part of the address is ignored). More than one IP + addresses may be specified. + + - <b>Receiving data from peers</b>.\n +- Once permission has been installed for the peer, any data received by the +- TURN server (from that peer) in the "relay port" will be relayed back to ++ Once permission has been installed for the peer, any data received by the ++ TURN server (from that peer) in the "relay port" will be relayed back to + client by the server, and application will be notified via \a on_rx_data + callback of the #pj_turn_session_cb. + + - <b>Using ChannelData</b>.\n +- TURN provides optimized framing to the data by using ChannelData ++ TURN provides optimized framing to the data by using ChannelData + packetization. The client activates this format for the specified peer by + calling #pj_turn_session_bind_channel(). Data sent or received to/for +- this peer will then use ChannelData format instead of Send or Data ++ this peer will then use ChannelData format instead of Send or Data + Indications. + + - <b>Refreshing the allocation, permissions, and channel bindings</b>.\n +@@ -125,15 +125,15 @@ These steps describes how to use the TURN session: + + */ + +-/** ++/** + * Opaque declaration for TURN client session. + */ + typedef struct pj_turn_session pj_turn_session; + + +-/** +- * TURN transport types, which will be used both to specify the connection +- * type for reaching TURN server and the type of allocation transport to be ++/** ++ * TURN transport types, which will be used both to specify the connection ++ * type for reaching TURN server and the type of allocation transport to be + * requested to server (the REQUESTED-TRANSPORT attribute). + */ + typedef enum pj_turn_tp_type +@@ -246,7 +246,7 @@ typedef struct pj_turn_session_cb + * @param addr_len Length of the destination address. + * + * @return The callback should return the status of the +- * send operation. ++ * send operation. + */ + pj_status_t (*on_send_pkt)(pj_turn_session *sess, + const pj_uint8_t *pkt, +@@ -254,6 +254,35 @@ typedef struct pj_turn_session_cb + const pj_sockaddr_t *dst_addr, + unsigned addr_len); + ++ /** ++ * This callback will be called by the TURN session whenever it ++ * needs to send outgoing message. Since the TURN session doesn't ++ * have a socket on its own, this callback must be implemented. ++ * ++ * The difference with on_send_pkt is that this function returns ++ * the size of the packet actually sent to predict when a busy will ++ * occurs. Indeed, activesock send the data asynchronously. When the ++ * data are actually sent, on_data_sent will be triggered. ++ * ++ * @param sess The TURN session. ++ * @param pkt The packet/data to be sent. ++ * @param pkt_len Length of the packet/data. ++ * @param dst_addr Destination address of the packet. ++ * @param addr_len Length of the destination address. ++ * @param send_size Length sent. ++ * @param original_size The length of the packet without the HEADER ++ * ++ * @return The callback should return the status of the ++ * send operation. ++ */ ++ pj_status_t (*on_send_pkt2)(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *dst_addr, ++ unsigned addr_len, ++ unsigned* sent_size, ++ unsigned original_size); ++ + /** + * This callback will be called by the TURN session whenever it + * needs to send outgoing STUN requests/messages for TURN signalling +@@ -268,7 +297,7 @@ typedef struct pj_turn_session_cb + * @param addr_len Length of the destination address. + * + * @return The callback should return the status of the +- * send operation. ++ * send operation. + */ + pj_status_t (*on_stun_send_pkt)(pj_turn_session *sess, + const pj_uint8_t *pkt, +@@ -277,7 +306,7 @@ typedef struct pj_turn_session_cb + unsigned addr_len); + + /** +- * Notification when peer address has been bound successfully to ++ * Notification when peer address has been bound successfully to + * a channel number. + * + * This callback is optional since the nature of this callback is +@@ -320,7 +349,7 @@ typedef struct pj_turn_session_cb + * @param old_state The previous state of the session. + * @param new_state The current state of the session. + */ +- void (*on_state)(pj_turn_session *sess, ++ void (*on_state)(pj_turn_session *sess, + pj_turn_state_t old_state, + pj_turn_state_t new_state); + +@@ -365,7 +394,7 @@ typedef struct pj_turn_session_cb + + + /** +- * Allocation parameter, which can be given when application calls ++ * Allocation parameter, which can be given when application calls + * pj_turn_session_alloc() to allocate relay address in the TURN server. + * Application should call pj_turn_alloc_param_default() to initialize + * this structure with the default values. +@@ -505,7 +534,7 @@ PJ_DECL(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm); + * @param dst Destination parameter. + * @param src Source parameter. + */ +-PJ_DECL(void) pj_turn_alloc_param_copy(pj_pool_t *pool, ++PJ_DECL(void) pj_turn_alloc_param_copy(pj_pool_t *pool, + pj_turn_alloc_param *dst, + const pj_turn_alloc_param *src); + +@@ -703,7 +732,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_server(pj_turn_session *sess, + + + /** +- * Set credential to be used to authenticate against TURN server. ++ * Set credential to be used to authenticate against TURN server. + * Application must call this function before sending Allocate request + * with pj_turn_session_alloc(). + * +@@ -724,7 +753,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_credential(pj_turn_session *sess, + * used with pj_turn_session_set_credential() before calling this function. + * + * This function will complete asynchronously, and the application will be +- * notified about the allocation result in \a on_state() callback. The ++ * notified about the allocation result in \a on_state() callback. The + * TURN session state will move to PJ_TURN_STATE_READY if allocation is + * successful, and PJ_TURN_STATE_DEALLOCATING or greater state if allocation + * has failed. +@@ -768,7 +797,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_perm(pj_turn_session *sess, + + + /** +- * Send a data to the specified peer address via the TURN relay. This ++ * Send a data to the specified peer address via the TURN relay. This + * function will encapsulate the data as STUN Send Indication or TURN + * ChannelData packet and send the message to the TURN server. The TURN + * server then will send the data to the peer. +@@ -777,7 +806,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_perm(pj_turn_session *sess, + * created before application can relay any data. + * + * Since TURN session is transport independent, this function will +- * ultimately call \a on_send_pkt() callback to request the application ++ * ultimately call \a on_send_pkt() callback to request the application + * to actually send the packet containing the data to the TURN server. + * + * @param sess The TURN client session. +@@ -798,6 +827,42 @@ PJ_DECL(pj_status_t) pj_turn_session_sendto(pj_turn_session *sess, + const pj_sockaddr_t *peer_addr, + unsigned addr_len); + ++/** ++ * Send a data to the specified peer address via the TURN relay. This ++ * function will encapsulate the data as STUN Send Indication or TURN ++ * ChannelData packet and send the message to the TURN server. The TURN ++ * server then will send the data to the peer. ++ * ++ * The allocation (pj_turn_session_alloc()) must have been successfully ++ * created before application can relay any data. ++ * ++ * Since TURN session is transport independent, this function will ++ * ultimately call \a on_send_pkt() callback to request the application ++ * to actually send the packet containing the data to the TURN server. ++ * ++ * The difference with pj_turn_session_sendto is that this function returns ++ * the size of the packet actually sent to predict when a busy will ++ * occurs. Indeed, activesock send the data asynchronously. When the ++ * data are actually sent, on_data_sent will be triggered. ++ * ++ * @param sess The TURN client session. ++ * @param pkt The data/packet to be sent to peer. ++ * @param pkt_len Length of the data. ++ * @param peer_addr The remote peer address (the ultimate destination ++ * of the data, and not the TURN server address). ++ * @param addr_len Length of the address. ++ * @param sent The size of the packet actually sent ++ * ++ * @return PJ_SUCCESS if the operation has been successful, ++ * or the appropriate error code on failure. ++ */ ++PJ_DECL(pj_status_t) pj_turn_session_sendto2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *peer_addr, ++ unsigned addr_len, ++ unsigned *sent); ++ + /** + * Optionally establish channel binding for the specified a peer address. + * This function will assign a unique channel number for the peer address +@@ -907,4 +972,3 @@ PJ_END_DECL + + + #endif /* __PJNATH_TURN_SESSION_H__ */ +- +diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h +index 35388809f..645c414fe 100644 +--- a/pjnath/include/pjnath/turn_sock.h ++++ b/pjnath/include/pjnath/turn_sock.h +@@ -603,6 +603,33 @@ PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock, + const pj_sockaddr_t *peer_addr, + unsigned addr_len); + ++/** ++ * Send a data to the specified peer address via the TURN relay. This ++ * function will encapsulate the data as STUN Send Indication or TURN ++ * ChannelData packet and send the message to the TURN server. The TURN ++ * server then will send the data to the peer. ++ * ++ * The allocation (pj_turn_sock_alloc()) must have been successfully ++ * created before application can relay any data. ++ * ++ * @param turn_sock The TURN transport instance. ++ * @param pkt The data/packet to be sent to peer. ++ * @param pkt_len Length of the data. ++ * @param peer_addr The remote peer address (the ultimate destination ++ * of the data, and not the TURN server address). ++ * @param addr_len Length of the address. ++ * @param sent Size actually sent. ++ * ++ * @return PJ_SUCCESS if the operation has been successful, ++ * or the appropriate error code on failure. ++ */ ++PJ_DECL(pj_status_t) pj_turn_sock_sendto2(pj_turn_sock *turn_sock, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *peer_addr, ++ unsigned addr_len, ++ unsigned* sent); ++ + /** + * Optionally establish channel binding for the specified a peer address. + * This function will assign a unique channel number for the peer address +diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c +index 4adc9bc70..643bbbac9 100644 +--- a/pjnath/src/pjnath/ice_strans.c ++++ b/pjnath/src/pjnath/ice_strans.c +@@ -1937,7 +1937,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st, + { + ice_st->call_send_cb = PJ_TRUE; + return send_data(ice_st, comp_id, data, data_len, dst_addr, +- dst_addr_len, PJ_TRUE, PJ_FALSE); ++ dst_addr_len, PJ_TRUE, PJ_FALSE); + } + + static void on_ice_destroy(pj_ice_sess *ice) +diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c +index 88985af69..12738c8e7 100644 +--- a/pjnath/src/pjnath/turn_session.c ++++ b/pjnath/src/pjnath/turn_session.c +@@ -971,10 +971,21 @@ on_error: + * Relay data to the specified peer through the session. + */ + PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, +- const pj_uint8_t *pkt, +- unsigned pkt_len, +- const pj_sockaddr_t *addr, +- unsigned addr_len) ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *addr, ++ unsigned addr_len) ++{ ++ unsigned sent; ++ return pj_turn_session_sendto2(sess, pkt, pkt_len, addr, addr_len, &sent); ++} ++ ++PJ_DEF(pj_status_t) pj_turn_session_sendto2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *addr, ++ unsigned addr_len, ++ unsigned *sent) + { + struct ch_t *ch; + struct perm_t *perm; +@@ -1011,7 +1022,8 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + + /* If peer connection is TCP (RFC 6062), send it directly */ + if (sess->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { +- status = sess->cb.on_send_pkt(sess, pkt, pkt_len, addr, addr_len); ++ status = sess->cb.on_send_pkt2(sess, pkt, pkt_len, addr, addr_len,sent, ++ pkt_len); + goto on_return; + } + +@@ -1039,9 +1051,10 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + + pj_assert(sess->srv_addr != NULL); + +- status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len, +- sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr)); ++ status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, total_len, ++ sess->srv_addr, ++ pj_sockaddr_get_len(sess->srv_addr), ++ sent, pkt_len); + + } else { + /* Use Send Indication. */ +@@ -1079,10 +1092,11 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + goto on_return; + + /* Send the Send Indication */ +- status = sess->cb.on_send_pkt(sess, sess->tx_pkt, +- (unsigned)send_ind_len, +- sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr)); ++ status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, ++ (unsigned)send_ind_len, ++ sess->srv_addr, ++ pj_sockaddr_get_len(sess->srv_addr), ++ sent, pkt_len); + } + + on_return: +@@ -1090,7 +1104,6 @@ on_return: + return status; + } + +- + /** + * Bind a peer address to a channel number. + */ +diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c +index 9b69a6a21..bacc80859 100644 +--- a/pjnath/src/pjnath/turn_sock.c ++++ b/pjnath/src/pjnath/turn_sock.c +@@ -100,6 +100,10 @@ struct pj_turn_sock + /* Data connection, when peer_conn_type==PJ_TURN_TP_TCP (RFC 6062) */ + unsigned data_conn_cnt; + tcp_data_conn_t data_conn[PJ_TURN_MAX_TCP_CONN_CNT]; ++ ++ /* The following variables are used by the on_data_sent callback */ ++ unsigned current_pkt_len; ++ unsigned current_body_len; + }; + + +@@ -111,6 +115,13 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, + unsigned pkt_len, + const pj_sockaddr_t *dst_addr, + unsigned dst_addr_len); ++static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *dst_addr, ++ unsigned dst_addr_len, ++ unsigned *sent, ++ unsigned body_len); + static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, +@@ -182,8 +193,8 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock, + pj_status_t status, + pj_size_t *remainder); + static pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock, +- pj_ioqueue_op_key_t *send_key, +- pj_ssize_t sent); ++ pj_ioqueue_op_key_t *send_key, ++ pj_ssize_t sent); + static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock, + pj_status_t status); + static void dataconn_cleanup(tcp_data_conn_t *conn); +@@ -345,6 +356,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_send_pkt = &turn_on_send_pkt; + sess_cb.on_stun_send_pkt = &turn_on_stun_send_pkt; ++ sess_cb.on_send_pkt2 = &turn_on_send_pkt2; + 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; +@@ -653,6 +665,22 @@ PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock, + addr, addr_len); + } + ++PJ_DEF(pj_status_t) pj_turn_sock_sendto2( pj_turn_sock *turn_sock, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *addr, ++ unsigned addr_len, ++ unsigned *sent) ++{ ++ PJ_ASSERT_RETURN(turn_sock && addr && addr_len, PJ_EINVAL); ++ ++ if (turn_sock->sess == NULL) ++ return PJ_EINVALIDOP; ++ ++ return pj_turn_session_sendto2(turn_sock->sess, pkt, pkt_len, ++ addr, addr_len, sent); ++} ++ + /* + * Bind a peer address to a channel number. + */ +@@ -1043,7 +1071,75 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, + unsigned dst_addr_len) + { + return send_pkt(sess, PJ_FALSE, pkt, pkt_len, +- dst_addr, dst_addr_len); ++ dst_addr, dst_addr_len); ++} ++ ++static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *dst_addr, ++ unsigned dst_addr_len, ++ unsigned *sent, ++ unsigned body_len) ++{ ++ *sent = pkt_len; ++ pj_turn_sock *turn_sock = (pj_turn_sock*)pj_turn_session_get_user_data(sess); ++ pj_status_t status = PJ_SUCCESS; ++ ++ pj_ssize_t len = pkt_len; ++ turn_sock->current_body_len = body_len; ++ turn_sock->current_pkt_len = pkt_len; ++ ++ if (turn_sock == NULL || turn_sock->is_destroying) { ++ /* We've been destroyed */ ++ // https://trac.pjsip.org/repos/ticket/1316 ++ //pj_assert(!"We should shutdown gracefully"); ++ return PJ_EINVALIDOP; ++ } ++ ++ if (turn_sock->conn_type == PJ_TURN_TP_UDP) { ++ status = pj_activesock_sendto(turn_sock->active_sock, ++ &turn_sock->send_key, pkt, &len, 0, ++ dst_addr, dst_addr_len); ++ } else if (turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { ++ pj_turn_session_info info; ++ pj_turn_session_get_info(turn_sock->sess, &info); ++ if (pj_sockaddr_cmp(&info.server, dst_addr) == 0) { ++ /* Destination address is TURN server */ ++ status = pj_activesock_send(turn_sock->active_sock, ++ &turn_sock->send_key, pkt, &len, 0); ++ } else { ++ /* Destination address is peer, lookup data connection */ ++ unsigned i; ++ ++ status = PJ_ENOTFOUND; ++ for (i=0; i < PJ_TURN_MAX_TCP_CONN_CNT; ++i) { ++ tcp_data_conn_t *conn = &turn_sock->data_conn[i]; ++ if (conn->state < DATACONN_STATE_CONN_BINDING) ++ continue; ++ if (pj_sockaddr_cmp(&conn->peer_addr, dst_addr) == 0) { ++ status = pj_activesock_send(conn->asock, ++ &conn->send_key, ++ pkt, &len, 0); ++ break; ++ } ++ } ++ } ++ } else { ++ status = pj_activesock_send(turn_sock->active_sock, ++ &turn_sock->send_key, pkt, &len, 0); ++ } ++ ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) { ++ show_err(turn_sock, "socket send()", status); ++ } ++ ++ // Remove header from sent size. ++ // The application only wants to know if the packet is actually sent. ++ unsigned header_len = pkt_len - body_len; ++ *sent = (len > header_len)? (len - header_len) : 0; ++ ++ return status; + } + + static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess, +-- +2.24.1 + diff --git a/contrib/src/pjproject/0006-ignore_ipv6_on_transport_check.patch b/contrib/src/pjproject/0006-ignore_ipv6_on_transport_check.patch new file mode 100644 index 0000000000000000000000000000000000000000..0de4f7ecf10982cbd0342d15342055cf6d14270f --- /dev/null +++ b/contrib/src/pjproject/0006-ignore_ipv6_on_transport_check.patch @@ -0,0 +1,28 @@ +From d8cbda261c7cebec8251742b1e383b69614951c5 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 20 Feb 2020 13:37:23 -0500 +Subject: [PATCH 6/9] ignore_ipv6_on_transport_check + +--- + pjsip/src/pjsip/sip_transport.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c +index e19b90fee..62a21526c 100644 +--- a/pjsip/src/pjsip/sip_transport.c ++++ b/pjsip/src/pjsip/sip_transport.c +@@ -2248,7 +2248,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, + pjsip_transport *seltp = sel->u.transport; + + /* See if the transport is (not) suitable */ +- if (seltp->key.type != type) { ++ pjsip_transport_type_e type_no_ipv6 = type % PJSIP_TRANSPORT_IPV6; ++ pjsip_transport_type_e key_type_no_ipv6 = seltp->key.type % ++ PJSIP_TRANSPORT_IPV6; ++ if (type_no_ipv6 != key_type_no_ipv6) { + pj_lock_release(mgr->lock); + TRACE_((THIS_FILE, "Transport type in tpsel not matched")); + return PJSIP_ETPNOTSUITABLE; +-- +2.24.1 + diff --git a/contrib/src/pjproject/0007-pj_ice_sess.patch b/contrib/src/pjproject/0007-pj_ice_sess.patch new file mode 100644 index 0000000000000000000000000000000000000000..6f6263582358d6cf31a2b3b202da62ba5c83b2bb --- /dev/null +++ b/contrib/src/pjproject/0007-pj_ice_sess.patch @@ -0,0 +1,42 @@ +From 02c53ba6beb9e9faeb59059bf466e444dfdb14a6 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 20 Feb 2020 12:33:47 -0500 +Subject: [PATCH 7/9] pj_ice_sess + +--- + pjnath/include/pjnath/ice_strans.h | 2 ++ + pjnath/src/pjnath/ice_strans.c | 5 +++++ + 2 files changed, 7 insertions(+) + +diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h +index 1269442cd..41f3b6297 100644 +--- a/pjnath/include/pjnath/ice_strans.h ++++ b/pjnath/include/pjnath/ice_strans.h +@@ -1054,6 +1054,8 @@ PJ_DECL(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st, + const pj_sockaddr_t *dst_addr, + int dst_addr_len); + ++PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans *ice_st); ++ + + /** + * @} +diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c +index 643bbbac9..603b75121 100644 +--- a/pjnath/src/pjnath/ice_strans.c ++++ b/pjnath/src/pjnath/ice_strans.c +@@ -1949,6 +1949,11 @@ static void on_ice_destroy(pj_ice_sess *ice) + } + } + ++PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess( pj_ice_strans *ice_st ) ++{ ++ return ice_st->ice; ++} ++ + /* + * Callback called by ICE session when ICE processing is complete, either + * successfully or with failure. +-- +2.24.1 + diff --git a/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch b/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch new file mode 100644 index 0000000000000000000000000000000000000000..b0071dbdeb4142b7a402e6858b4397bbd9255d1b --- /dev/null +++ b/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch @@ -0,0 +1,85 @@ +From 52f0c422e41ace1f2b682adbbaf67c4ddb0d17ed Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 20 Feb 2020 12:39:23 -0500 +Subject: [PATCH 8/9] fix_ioqueue_ipv6_sendto + +--- + pjlib/src/pj/ioqueue_common_abs.c | 5 ++++- + pjlib/src/pj/ioqueue_common_abs.h | 17 ++++++++--------- + 2 files changed, 12 insertions(+), 10 deletions(-) + +diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c +index aaefc80e8..e8b929e7a 100644 +--- a/pjlib/src/pj/ioqueue_common_abs.c ++++ b/pjlib/src/pj/ioqueue_common_abs.c +@@ -1053,7 +1053,10 @@ retry_on_restart: + /* + * Check that address storage can hold the address parameter. + */ +- PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), PJ_EBUG); ++ PJ_ASSERT_RETURN((((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET() && ++ addrlen <= (int)sizeof(pj_sockaddr_in)) || ++ (((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET6() && ++ addrlen <= (int)sizeof(pj_sockaddr_in6)), PJ_EBUG); + + /* + * Schedule asynchronous send. +diff --git a/pjlib/src/pj/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h +index d5e36b4d6..b1e9b2083 100644 +--- a/pjlib/src/pj/ioqueue_common_abs.h ++++ b/pjlib/src/pj/ioqueue_common_abs.h +@@ -1,5 +1,5 @@ + /* $Id */ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * +@@ -15,14 +15,14 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + /* ioqueue_common_abs.h + * +- * This file contains private declarations for abstracting various +- * event polling/dispatching mechanisms (e.g. select, poll, epoll) +- * to the ioqueue. ++ * This file contains private declarations for abstracting various ++ * event polling/dispatching mechanisms (e.g. select, poll, epoll) ++ * to the ioqueue. + */ + + #include <pj/list.h> +@@ -63,7 +63,7 @@ struct write_operation + pj_size_t size; + pj_ssize_t written; + unsigned flags; +- pj_sockaddr_in rmt_addr; ++ pj_sockaddr rmt_addr; + int rmt_addrlen; + }; + +@@ -93,7 +93,7 @@ union operation_key + unsigned ref_count; \ + pj_bool_t closing; \ + pj_time_val free_time; \ +- ++ + #else + # define UNREG_FIELDS + #endif +@@ -135,6 +135,5 @@ static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type ); + static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, +- pj_ioqueue_key_t *key, ++ pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type); +- +-- +2.24.1 + diff --git a/contrib/src/pjproject/0009-add-config-site.patch b/contrib/src/pjproject/0009-add-config-site.patch new file mode 100644 index 0000000000000000000000000000000000000000..9891484d3cfdb51201efbfd6da964b8599c7a8e0 --- /dev/null +++ b/contrib/src/pjproject/0009-add-config-site.patch @@ -0,0 +1,42 @@ +From e657b352dba5b23c82fa29028e7ff4eb19a7dd50 Mon Sep 17 00:00:00 2001 +From: jrun <darwinskernel@gmail.com> +Date: Thu, 27 Feb 2020 13:44:47 -0500 +Subject: [PATCH 9/9] add config site + +--- + pjlib/include/pj/config_site.h | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + create mode 100644 pjlib/include/pj/config_site.h + +diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h +new file mode 100644 +index 000000000..47dd40ad5 +--- /dev/null ++++ b/pjlib/include/pj/config_site.h +@@ -0,0 +1,23 @@ ++#include "config_site_sample.h" ++ ++/* ++* PJLIB settings. ++*/ ++#define PJ_HAS_IPV6 1 ++#define PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION 1 ++ ++/* ++* PJSIP settings. ++*/ ++#define PJSIP_MAX_PKT_LEN 8000 ++#define PJSIP_TRANSPORT_SERVER_IDLE_TIME 3 ++ ++/* ++* PJNAT settings. ++*/ ++#define PJ_ICE_MAX_CAND 256 ++#define PJ_ICE_ST_MAX_CAND 32 ++#define PJ_ICE_MAX_STUN 6 ++#define PJ_ICE_MAX_TURN 4 ++#define PJ_ICE_COMP_BITS 2 ++#define PJ_ICE_MAX_CHECKS 1024 +-- +2.24.1 + diff --git a/contrib/src/pjproject/SHA512SUMS b/contrib/src/pjproject/SHA512SUMS index 23d0102fe341144a8101b9e3d9d56e4a1a0522d3..54e6366f11adf05a3926d2bca543192731eb6a56 100644 --- a/contrib/src/pjproject/SHA512SUMS +++ b/contrib/src/pjproject/SHA512SUMS @@ -1 +1 @@ -a9433b47294434288d61524dea556687fb02137ed56a10e8e2ba85d7888a2ca2a5bea4ae4a9ad008a4c208c5ec53fe364b10ed14481700d6aa8b9b6137e5e9ee pjproject-5dfa75be7d69047387f9b0436dd9492bbbf03fe4.tar.gz \ No newline at end of file +a67f083df175b536b4e6a7b7fe39e07d3ee805d6917ec64a50694542a7455c33a100889191044ab3fa679b6656774a6be045621aa53510b5f04cdde9ddd59893 pjproject-2.10.tar.gz diff --git a/contrib/src/pjproject/add_dtls_transport.patch b/contrib/src/pjproject/add_dtls_transport.patch deleted file mode 100644 index b7f9f296e794354bc299092b6d3cc7e5e2d5d3a4..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/add_dtls_transport.patch +++ /dev/null @@ -1,63 +0,0 @@ ---- a/pjsip/src/pjsip/sip_transport.c -+++ b/pjsip/src/pjsip/sip_transport.c -@@ -183,6 +183,13 @@ - PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE - }, - { -+ PJSIP_TRANSPORT_DTLS, -+ 5061, -+ {"DTLS", 4}, -+ "DTLS transport", -+ PJSIP_TRANSPORT_SECURE -+ }, -+ { - PJSIP_TRANSPORT_SCTP, - 5060, - {"SCTP", 4}, -@@ -224,6 +231,13 @@ - "TLS IPv6 transport", - PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE - }, -+ { -+ PJSIP_TRANSPORT_DTLS6, -+ 5061, -+ {"DTLS", 4}, -+ "DTLS IPv6 transport", -+ PJSIP_TRANSPORT_SECURE -+ }, - }; - - static void tp_state_callback(pjsip_transport *tp, -@@ -249,7 +263,7 @@ - */ - PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag, - const char *tp_name, -- int def_port, -+ int def_port, - int *p_tp_type) - { - unsigned i; ---- a/pjsip/include/pjsip/sip_types.h -+++ b/pjsip/include/pjsip/sip_types.h -@@ -73,6 +73,9 @@ - /** TLS. */ - PJSIP_TRANSPORT_TLS, - -+ /** DTLS. */ -+ PJSIP_TRANSPORT_DTLS, -+ - /** SCTP. */ - PJSIP_TRANSPORT_SCTP, - -@@ -95,7 +98,10 @@ - PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6, - - /** TLS over IPv6 */ -- PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6 -+ PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6, -+ -+ /** DTLS over IPv6 */ -+ PJSIP_TRANSPORT_DTLS6 = PJSIP_TRANSPORT_DTLS + PJSIP_TRANSPORT_IPV6 - - } pjsip_transport_type_e; - diff --git a/contrib/src/pjproject/android.patch b/contrib/src/pjproject/android.patch deleted file mode 100644 index 01621b9886f7fb052ab32b806b7eb7f21adea4d1..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/android.patch +++ /dev/null @@ -1,49 +0,0 @@ ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -293,6 +293,21 @@ - */ - #include <pj/config_site.h> - -+#undef PJ_ANDROID -+#define PJ_ANDROID 0 -+#define PJ_JNI_HAS_JNI_ONLOAD 0 -+#undef PJ_HAS_FLOATING_POINT -+#define PJ_HAS_FLOATING_POINT 0 -+#define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 -+#define PJMEDIA_AUDIO_DEV_HAS_WMME 0 -+#define PJMEDIA_AUDIO_DEV_HAS_OPENSL 0 -+#define PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI 0 -+#define PJMEDIA_HAS_L16_CODEC 0 -+#define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5 -+#define PJMEDIA_VID_DEV_INFO_FMT_CNT 128 -+#define PJSIP_MAX_TSX_COUNT 31 -+#define PJSIP_MAX_DIALOG_COUNT 31 -+ - /******************************************************************** - * PJLIB Features. - */ ---- a/aconfigure -+++ b/aconfigure -@@ -5809,6 +5809,3 @@ - case $target in -- *android*) -- ac_os_objs="$ac_os_objs guid_android.o" -- ;; - *) - if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then ---- a/aconfigure.ac -+++ b/aconfigure.ac -@@ -457,6 +457,3 @@ - case $target in -- *android*) -- ac_os_objs="$ac_os_objs guid_android.o" -- ;; - *) - if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then ---- a/pjlib/src/pj/os_timestamp_posix.c -+++ b/pjlib/src/pj/os_timestamp_posix.c -@@ -163,3 +163,3 @@ - --#elif defined(__ANDROID__) -+#elif defined(PJ_ANDROID) && PJ_ANDROID - \ No newline at end of file diff --git a/contrib/src/pjproject/disable_local_resolution.patch b/contrib/src/pjproject/disable_local_resolution.patch deleted file mode 100644 index 0f06cbaaa2e00063b9095156e34941a1ada64e9c..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/disable_local_resolution.patch +++ /dev/null @@ -1,17 +0,0 @@ - pjlib/include/pj/config.h | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h -index ad0abbb5..6bba5a55 100644 ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -1003,6 +1003,9 @@ - #endif - - -+#ifndef PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION -+# define PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION 1 -+#endif - - /** @} */ - diff --git a/contrib/src/pjproject/fix_assert_on_connection_attempt.patch b/contrib/src/pjproject/fix_assert_on_connection_attempt.patch deleted file mode 100644 index 43ffa2bb35f381743292811494fecc57e4d7195a..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/fix_assert_on_connection_attempt.patch +++ /dev/null @@ -1,16 +0,0 @@ - pjnath/src/pjnath/turn_sock.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c -index e6c60126..aa764019 100644 ---- a/pjnath/src/pjnath/turn_sock.c -+++ b/pjnath/src/pjnath/turn_sock.c -@@ -1597,7 +1597,7 @@ static void turn_on_connection_attempt(pj_turn_session *sess, - if (turn_sock->data_conn[i].state == DATACONN_STATE_NULL) - break; - } -- pj_assert(i < turn_sock->data_conn_cnt); -+ pj_assert(i < PJ_TURN_MAX_TCP_CONN_CNT); - ++turn_sock->data_conn_cnt; - - /* Init new data connection */ diff --git a/contrib/src/pjproject/fix_ebusy_turn.patch b/contrib/src/pjproject/fix_ebusy_turn.patch deleted file mode 100644 index cde32247c443ba80825b9b12dddcdd0bae8085c4..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/fix_ebusy_turn.patch +++ /dev/null @@ -1,435 +0,0 @@ - pjnath/include/pjnath/turn_session.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - pjnath/include/pjnath/turn_sock.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ - pjnath/src/pjnath/ice_strans.c | 14 ++++++++------ - pjnath/src/pjnath/turn_session.c | 22 +++++++++++++++++----- - pjnath/src/pjnath/turn_sock.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- - 5 files changed, 228 insertions(+), 12 deletions(-) - -diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h -index 9984c1f57..890d70c90 100644 ---- a/pjnath/include/pjnath/turn_session.h -+++ b/pjnath/include/pjnath/turn_session.h -@@ -250,6 +250,35 @@ typedef struct pj_turn_session_cb - const pj_sockaddr_t *dst_addr, - unsigned addr_len); - -+ /** -+ * This callback will be called by the TURN session whenever it -+ * needs to send outgoing message. Since the TURN session doesn't -+ * have a socket on its own, this callback must be implemented. -+ * -+ * The difference with on_send_pkt is that this function returns -+ * the size of the packet actually sent to predict when a busy will -+ * occurs. Indeed, activesock send the data asynchronously. When the -+ * data are actually sent, on_data_sent will be triggered. -+ * -+ * @param sess The TURN session. -+ * @param pkt The packet/data to be sent. -+ * @param pkt_len Length of the packet/data. -+ * @param dst_addr Destination address of the packet. -+ * @param addr_len Length of the destination address. -+ * @param send_size Length sent. -+ * @param original_size The length of the packet without the HEADER -+ * -+ * @return The callback should return the status of the -+ * send operation. -+ */ -+ pj_status_t (*on_send_pkt2)(pj_turn_session *sess, -+ const pj_uint8_t *pkt, -+ unsigned pkt_len, -+ const pj_sockaddr_t *dst_addr, -+ unsigned addr_len, -+ unsigned* sent_size, -+ unsigned original_size); -+ - /** - * Notification when peer address has been bound successfully to - * a channel number. -@@ -770,6 +799,42 @@ PJ_DECL(pj_status_t) pj_turn_session_sendto(pj_turn_session *sess, - const pj_sockaddr_t *peer_addr, - unsigned addr_len); - -+/** -+ * Send a data to the specified peer address via the TURN relay. This -+ * function will encapsulate the data as STUN Send Indication or TURN -+ * ChannelData packet and send the message to the TURN server. The TURN -+ * server then will send the data to the peer. -+ * -+ * The allocation (pj_turn_session_alloc()) must have been successfully -+ * created before application can relay any data. -+ * -+ * Since TURN session is transport independent, this function will -+ * ultimately call \a on_send_pkt() callback to request the application -+ * to actually send the packet containing the data to the TURN server. -+ * -+ * The difference with pj_turn_session_sendto is that this function returns -+ * the size of the packet actually sent to predict when a busy will -+ * occurs. Indeed, activesock send the data asynchronously. When the -+ * data are actually sent, on_data_sent will be triggered. -+ * -+ * @param sess The TURN client session. -+ * @param pkt The data/packet to be sent to peer. -+ * @param pkt_len Length of the data. -+ * @param peer_addr The remote peer address (the ultimate destination -+ * of the data, and not the TURN server address). -+ * @param addr_len Length of the address. -+ * @param sent The size of the packet actually sent -+ * -+ * @return PJ_SUCCESS if the operation has been successful, -+ * or the appropriate error code on failure. -+ */ -+PJ_DECL(pj_status_t) pj_turn_session_sendto2(pj_turn_session *sess, -+ const pj_uint8_t *pkt, -+ unsigned pkt_len, -+ const pj_sockaddr_t *peer_addr, -+ unsigned addr_len, -+ unsigned *sent); -+ - /** - * Optionally establish channel binding for the specified a peer address. - * This function will assign a unique channel number for the peer address -diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h -index 99570ac25..f86682c35 100644 ---- a/pjnath/include/pjnath/turn_sock.h -+++ b/pjnath/include/pjnath/turn_sock.h -@@ -86,6 +86,23 @@ typedef struct pj_turn_sock_cb - const pj_sockaddr_t *peer_addr, - unsigned addr_len); - -+ /** -+ * Notifification when asynchronous send operation has completed. -+ * -+ * @param turn_sock The TURN transport. -+ * @param sent If value is positive non-zero it indicates the -+ * number of data sent. When the value is negative, -+ * it contains the error code which can be retrieved -+ * by negating the value (i.e. status=-sent). -+ * -+ * @return Application should normally return PJ_TRUE to let -+ * the STUN transport continue its operation. However -+ * it must return PJ_FALSE if it has destroyed the -+ * STUN transport in this callback. -+ */ -+ pj_bool_t (*on_data_sent)(pj_turn_sock *sock, -+ pj_ssize_t sent); -+ - /** - * Notification when TURN session state has changed. Application should - * implement this callback to monitor the progress of the TURN session. -@@ -469,6 +486,33 @@ PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock, - const pj_sockaddr_t *peer_addr, - unsigned addr_len); - -+/** -+ * Send a data to the specified peer address via the TURN relay. This -+ * function will encapsulate the data as STUN Send Indication or TURN -+ * ChannelData packet and send the message to the TURN server. The TURN -+ * server then will send the data to the peer. -+ * -+ * The allocation (pj_turn_sock_alloc()) must have been successfully -+ * created before application can relay any data. -+ * -+ * @param turn_sock The TURN transport instance. -+ * @param pkt The data/packet to be sent to peer. -+ * @param pkt_len Length of the data. -+ * @param peer_addr The remote peer address (the ultimate destination -+ * of the data, and not the TURN server address). -+ * @param addr_len Length of the address. -+ * @param sent Size actually sent. -+ * -+ * @return PJ_SUCCESS if the operation has been successful, -+ * or the appropriate error code on failure. -+ */ -+PJ_DECL(pj_status_t) pj_turn_sock_sendto2(pj_turn_sock *turn_sock, -+ const pj_uint8_t *pkt, -+ unsigned pkt_len, -+ const pj_sockaddr_t *peer_addr, -+ unsigned addr_len, -+ unsigned* sent); -+ - /** - * Optionally establish channel binding for the specified a peer address. - * This function will assign a unique channel number for the peer address -diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c -index d048adfd6..11992ac44 100644 ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -391,6 +391,7 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, - pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); - turn_sock_cb.on_rx_data = &turn_on_rx_data; - turn_sock_cb.on_state = &turn_on_state; -+ turn_sock_cb.on_data_sent = &turn_on_data_sent; - - /* Override with component specific QoS settings, if any */ - if (ice_st->cfg.comp[comp_idx].qos_type) -@@ -1654,9 +1655,9 @@ pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data, - comp->turn[tp_idx].log_off = PJ_TRUE; - } - -- status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, final_pkt, -- final_len, dst_addr, dst_addr_len); -- ice_st->is_pending = ((status == PJ_EPENDING) && ice_st); -+ status = pj_turn_sock_sendto2(comp->turn[tp_idx].sock, final_pkt, -+ final_len, dst_addr, dst_addr_len, size); -+ ice_st->is_pending = ((status == PJ_EPENDING || *size != data_len) && ice_st); - } else { - const pj_sockaddr_t *dest_addr; - unsigned dest_addr_len; -@@ -1860,9 +1861,10 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, - - if (tp_typ == TP_TURN) { - if (comp->turn[tp_idx].sock) { -- status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, -- final_pkt, final_len, dst_addr, dst_addr_len); -- ice_st->is_pending = status == PJ_EPENDING; -+ status = pj_turn_sock_sendto2(comp->turn[tp_idx].sock, -+ final_pkt, final_len, dst_addr, dst_addr_len, &sent_size); -+ ice_st->is_pending = (status == PJ_EPENDING || (unsigned)sent_size != final_len); -+ - } else { - status = PJ_EINVALIDOP; - } -diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c -index 1c0430bc7..773427c82 100644 ---- a/pjnath/src/pjnath/turn_session.c -+++ b/pjnath/src/pjnath/turn_session.c -@@ -975,6 +975,18 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, - unsigned pkt_len, - const pj_sockaddr_t *addr, - unsigned addr_len) -+{ -+ unsigned sent; -+ return pj_turn_session_sendto2(sess, pkt, pkt_len, addr, addr_len, &sent); -+} -+ -+ -+PJ_DEF(pj_status_t) pj_turn_session_sendto2( pj_turn_session *sess, -+ const pj_uint8_t *pkt, -+ unsigned pkt_len, -+ const pj_sockaddr_t *addr, -+ unsigned addr_len, -+ unsigned *sent) - { - struct ch_t *ch; - struct perm_t *perm; -@@ -1011,7 +1023,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, - - /* If peer connection is TCP (RFC 6062), send it directly */ - if (sess->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { -- status = sess->cb.on_send_pkt(sess, pkt, pkt_len, addr, addr_len); -+ status = sess->cb.on_send_pkt2(sess, pkt, pkt_len, addr, addr_len, sent, pkt_len); - goto on_return; - } - -@@ -1039,9 +1051,9 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, - - pj_assert(sess->srv_addr != NULL); - -- status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len, -+ status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, total_len, - sess->srv_addr, -- pj_sockaddr_get_len(sess->srv_addr)); -+ pj_sockaddr_get_len(sess->srv_addr), sent, pkt_len); - - } else { - /* Use Send Indication. */ -@@ -1079,10 +1091,10 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, - goto on_return; - - /* Send the Send Indication */ -- status = sess->cb.on_send_pkt(sess, sess->tx_pkt, -+ status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, - (unsigned)send_ind_len, - sess->srv_addr, -- pj_sockaddr_get_len(sess->srv_addr)); -+ pj_sockaddr_get_len(sess->srv_addr), sent, pkt_len); - } - - on_return: -diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c -index 7033fd939..1a0890942 100644 ---- a/pjnath/src/pjnath/turn_sock.c -+++ b/pjnath/src/pjnath/turn_sock.c -@@ -89,6 +89,10 @@ struct pj_turn_sock - /* Data connection, when peer_conn_type==PJ_TURN_TP_TCP (RFC 6062) */ - unsigned data_conn_cnt; - tcp_data_conn_t data_conn[PJ_TURN_MAX_TCP_CONN_CNT]; -+ -+ // The following variables are used by the on_data_sent callback -+ unsigned current_pkt_len; -+ unsigned current_body_len; - }; - - -@@ -100,6 +104,13 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, - unsigned pkt_len, - const pj_sockaddr_t *dst_addr, - unsigned dst_addr_len); -+static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, -+ const pj_uint8_t *pkt, -+ unsigned pkt_len, -+ const pj_sockaddr_t *dst_addr, -+ unsigned dst_addr_len, -+ unsigned *sent, -+ unsigned body_len); - static void turn_on_channel_bound(pj_turn_session *sess, - const pj_sockaddr_t *peer_addr, - unsigned addr_len, -@@ -127,6 +138,9 @@ static pj_bool_t on_data_read(pj_activesock_t *asock, - pj_size_t size, - pj_status_t status, - pj_size_t *remainder); -+static pj_bool_t on_data_sent(pj_activesock_t *asock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_ssize_t sent); - static pj_bool_t on_connect_complete(pj_activesock_t *asock, - pj_status_t status); - -@@ -135,6 +149,9 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock, - pj_size_t size, - pj_status_t status, - pj_size_t *remainder); -+static pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_ssize_t sent); - static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock, - pj_status_t status); - static void dataconn_cleanup(tcp_data_conn_t *conn); -@@ -236,6 +253,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, - /* Init TURN session */ - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_send_pkt = &turn_on_send_pkt; -+ sess_cb.on_send_pkt2 = &turn_on_send_pkt2; - 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; -@@ -531,6 +549,22 @@ PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock, - addr, addr_len); - } - -+PJ_DEF(pj_status_t) pj_turn_sock_sendto2( pj_turn_sock *turn_sock, -+ const pj_uint8_t *pkt, -+ unsigned pkt_len, -+ const pj_sockaddr_t *addr, -+ unsigned addr_len, -+ unsigned *sent) -+{ -+ PJ_ASSERT_RETURN(turn_sock && addr && addr_len, PJ_EINVAL); -+ -+ if (turn_sock->sess == NULL) -+ return PJ_EINVALIDOP; -+ -+ return pj_turn_session_sendto2(turn_sock->sess, pkt, pkt_len, -+ addr, addr_len, sent); -+} -+ - /* - * Bind a peer address to a channel number. - */ -@@ -710,6 +744,22 @@ on_return: - return ret; - } - -+pj_bool_t on_data_sent(pj_activesock_t *asock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_ssize_t sent) -+{ -+ pj_turn_sock *turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); -+ -+ unsigned header_len = turn_sock->current_pkt_len - turn_sock->current_body_len; -+ unsigned sent_size = (sent > header_len)? (sent - header_len) : 0; -+ -+ if (turn_sock->cb.on_data_sent) { -+ (*turn_sock->cb.on_data_sent)(turn_sock, sent_size); -+ } -+ -+ return PJ_TRUE; -+} -+ - - /* - * Callback from TURN session to send outgoing packet. -@@ -720,11 +770,28 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, - const pj_sockaddr_t *dst_addr, - unsigned dst_addr_len) - { -+ unsigned sent = pkt_len; -+ return turn_on_send_pkt2(sess, pkt, pkt_len, dst_addr, dst_addr_len, &sent, pkt_len); -+} -+ -+ -+static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, -+ const pj_uint8_t *pkt, -+ unsigned pkt_len, -+ const pj_sockaddr_t *dst_addr, -+ unsigned dst_addr_len, -+ unsigned *sent, -+ unsigned body_len) -+{ -+ *sent = pkt_len; - pj_turn_sock *turn_sock = (pj_turn_sock*) - pj_turn_session_get_user_data(sess); -- pj_ssize_t len = pkt_len; - pj_status_t status = PJ_SUCCESS; - -+ pj_ssize_t len = pkt_len; -+ turn_sock->current_body_len = body_len; -+ turn_sock->current_pkt_len = pkt_len; -+ - if (turn_sock == NULL || turn_sock->is_destroying) { - /* We've been destroyed */ - // https://trac.pjsip.org/repos/ticket/1316 -@@ -769,6 +836,11 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, - show_err(turn_sock, "socket send()", status); - } - -+ // Remove header from sent size. -+ // The application only wants to know if the packet is actually sent. -+ unsigned header_len = pkt_len - body_len; -+ *sent = (len > header_len)? (len - header_len) : 0; -+ - return status; - } - -@@ -960,6 +1032,7 @@ static void turn_on_state(pj_turn_session *sess, - - pj_bzero(&asock_cb, sizeof(asock_cb)); - asock_cb.on_data_read = &on_data_read; -+ asock_cb.on_data_sent = &on_data_sent; - asock_cb.on_connect_complete = &on_connect_complete; - status = pj_activesock_create(turn_sock->pool, sock, - sock_type, &asock_cfg, -@@ -1090,6 +1163,25 @@ on_return: - return PJ_TRUE; - } - -+pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock, -+ pj_ioqueue_op_key_t *send_key, -+ pj_ssize_t sent) -+{ -+ tcp_data_conn_t *conn = (tcp_data_conn_t*) -+ pj_activesock_get_user_data(asock); -+ pj_turn_sock *turn_sock = conn->turn_sock; -+ -+ unsigned header_len = turn_sock->current_pkt_len - turn_sock->current_body_len; -+ unsigned sent_size = (sent > header_len)? (sent - header_len) : 0; -+ -+ if (turn_sock->cb.on_data_sent) { -+ (*turn_sock->cb.on_data_sent)(turn_sock, sent_size); -+ } -+ -+ return PJ_TRUE; -+} -+ -+ - static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock, - pj_status_t status) - { -@@ -1268,6 +1360,7 @@ static void turn_on_connection_attempt(pj_turn_session *sess, - - pj_bzero(&asock_cb, sizeof(asock_cb)); - asock_cb.on_data_read = &dataconn_on_data_read; -+ asock_cb.on_data_sent = &dataconn_on_data_sent; - asock_cb.on_connect_complete = &dataconn_on_connect_complete; - status = pj_activesock_create(pool, sock, - pj_SOCK_STREAM(), &asock_cfg, diff --git a/contrib/src/pjproject/fix_first_packet_turn_tcp.patch b/contrib/src/pjproject/fix_first_packet_turn_tcp.patch deleted file mode 100644 index dbedf6a1512025699c52962cc3421fd6f0f1835a..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/fix_first_packet_turn_tcp.patch +++ /dev/null @@ -1,79 +0,0 @@ - pjnath/src/pjnath/turn_sock.c | 64 +++++++++++++++++++++++++++++++++------------------------------- - 1 file changed, 33 insertions(+), 31 deletions(-) - -diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c -index 7033fd93..6a3a3e31 100644 ---- a/pjnath/src/pjnath/turn_sock.c -+++ b/pjnath/src/pjnath/turn_sock.c -@@ -1052,38 +1052,40 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock, - return PJ_FALSE; - } - -- if (conn->state == DATACONN_STATE_READY) { -- /* Application data */ -- if (turn_sock->cb.on_rx_data) { -- (*turn_sock->cb.on_rx_data)(turn_sock, data, size, -- &conn->peer_addr, -- conn->peer_addr_len); -+ *remainder = size; -+ while (*remainder > 0) { -+ if (conn->state == DATACONN_STATE_READY) { -+ /* Application data */ -+ if (turn_sock->cb.on_rx_data) { -+ (*turn_sock->cb.on_rx_data)(turn_sock, data, *remainder, -+ &conn->peer_addr, -+ conn->peer_addr_len); -+ } -+ *remainder = 0; -+ } else if (conn->state == DATACONN_STATE_CONN_BINDING) { -+ /* Waiting for ConnectionBind response */ -+ pj_bool_t is_stun; -+ pj_turn_session_on_rx_pkt_param prm; -+ -+ /* Ignore if this is not a STUN message */ -+ is_stun = ((((pj_uint8_t*)data)[0] & 0xC0) == 0); -+ if (!is_stun) -+ goto on_return; -+ -+ pj_bzero(&prm, sizeof(prm)); -+ prm.pkt = data; -+ prm.pkt_len = *remainder; -+ prm.src_addr = &conn->peer_addr; -+ prm.src_addr_len = conn->peer_addr_len; -+ pj_turn_session_on_rx_pkt2(conn->turn_sock->sess, &prm); -+ /* Got remainder? */ -+ if (prm.parsed_len < *remainder && prm.parsed_len > 0) { -+ pj_memmove(data, (pj_uint8_t*)data+prm.parsed_len, *remainder); -+ } -+ *remainder -= prm.parsed_len; -+ } else -+ goto on_return; - } -- } else if (conn->state == DATACONN_STATE_CONN_BINDING) { -- /* Waiting for ConnectionBind response */ -- pj_bool_t is_stun; -- pj_turn_session_on_rx_pkt_param prm; -- -- /* Ignore if this is not a STUN message */ -- is_stun = ((((pj_uint8_t*)data)[0] & 0xC0) == 0); -- if (!is_stun) -- goto on_return; -- -- pj_bzero(&prm, sizeof(prm)); -- prm.pkt = data; -- prm.pkt_len = size; -- prm.src_addr = &conn->peer_addr; -- prm.src_addr_len = conn->peer_addr_len; -- pj_turn_session_on_rx_pkt2(conn->turn_sock->sess, &prm); -- /* Got remainder? */ -- if (prm.parsed_len < size) { -- *remainder = size - prm.parsed_len; -- if (prm.parsed_len) { -- pj_memmove(data, (pj_uint8_t*)data+prm.parsed_len, -- *remainder); -- } -- } -- } - - on_return: - pj_grp_lock_release(turn_sock->grp_lock); diff --git a/contrib/src/pjproject/fix_ioqueue_ipv6_sendto.patch b/contrib/src/pjproject/fix_ioqueue_ipv6_sendto.patch deleted file mode 100644 index bc53fd2cb4a82964befbcde117fc5a5e35af517d..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/fix_ioqueue_ipv6_sendto.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/pjlib/src/pj/ioqueue_common_abs.c 2015-11-05 23:18:46.000000000 -0500 -+++ b/pjlib/src/pj/ioqueue_common_abs.c 2016-10-21 13:49:09.183662433 -0400 -@@ -1048,5 +1048,6 @@ - * Check that address storage can hold the address parameter. - */ -- PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), PJ_EBUG); -+ PJ_ASSERT_RETURN((((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET() && addrlen <= (int)sizeof(pj_sockaddr_in)) || -+ (((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET6() && addrlen <= (int)sizeof(pj_sockaddr_in6)), PJ_EBUG); - - /* ---- a/pjlib/src/pj/ioqueue_common_abs.h 2013-02-21 06:18:36.000000000 -0500 -+++ b/pjlib/src/pj/ioqueue_common_abs.h 2016-10-21 14:04:04.148928591 -0400 -@@ -64,5 +64,5 @@ - pj_ssize_t written; - unsigned flags; -- pj_sockaddr_in rmt_addr; -+ pj_sockaddr rmt_addr; - int rmt_addrlen; - }; diff --git a/contrib/src/pjproject/fix_turn_alloc_failure.patch b/contrib/src/pjproject/fix_turn_alloc_failure.patch deleted file mode 100644 index 1f8438e35fde7f9ea5811ff4ce9938b3e96995ae..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/fix_turn_alloc_failure.patch +++ /dev/null @@ -1,27 +0,0 @@ - pjnath/src/pjnath/ice_strans.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c -index ca15a74e8..040ab4595 100644 ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -396,6 +396,9 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, - data->comp = comp; - data->transport_id = cand->transport_id; - -+ /* Commit the relayed candidate. */ -+ comp->cand_cnt++; -+ - /* Create the TURN transport */ - status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, turn_cfg->af, - turn_cfg->conn_type, -@@ -420,9 +423,6 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, - return status; - } - -- /* Commit the relayed candidate. */ -- comp->cand_cnt++; -- - PJ_LOG(4,(ice_st->obj_name, - "Comp %d/%d: TURN relay candidate (tpid=%d) " - "waiting for allocation", diff --git a/contrib/src/pjproject/fix_turn_connection_failure.patch b/contrib/src/pjproject/fix_turn_connection_failure.patch deleted file mode 100644 index f42a15bd72348b9dd1f710e76f378183afea4ae6..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/fix_turn_connection_failure.patch +++ /dev/null @@ -1,16 +0,0 @@ - pjnath/src/pjnath/ice_strans.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c -index 72f12700..d4790049 100644 ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -2854,7 +2854,7 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, - - sess_init_update(comp->ice_st); - -- } else if ((old_state == PJ_TURN_STATE_RESOLVING || old_state == PJ_TURN_STATE_ALLOCATING) && -+ } else if (old_state >= PJ_TURN_STATE_RESOLVING && old_state <= PJ_TURN_STATE_ALLOCATING && - new_state >= PJ_TURN_STATE_DEALLOCATING) - { - pj_ice_sess_cand *cand = NULL; diff --git a/contrib/src/pjproject/fix_turn_fallback.patch b/contrib/src/pjproject/fix_turn_fallback.patch deleted file mode 100644 index 50229dd01ccf7d8bfcd016acfa967cab7b606a0c..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/fix_turn_fallback.patch +++ /dev/null @@ -1,53 +0,0 @@ ---- a/pjnath/src/pjnath/turn_session.c 2016-09-19 18:21:09.073614574 -0400 -+++ b/pjnath/src/pjnath/turn_session.c 2016-09-19 18:21:30.648631620 -0400 -@@ -653,3 +653,3 @@ - -- cnt = PJ_TURN_MAX_DNS_SRV_CNT; -+ cnt = 1; - ai = (pj_addrinfo*) ---- a/pjnath/src/pjnath/ice_strans.c 2016-09-19 18:36:04.180104330 -0400 -+++ b/pjnath/src/pjnath/ice_strans.c 2016-09-19 18:37:10.614136809 -0400 -@@ -1304,2 +1304,5 @@ - -+ if (!comp->turn[n].sock) -+ continue; -+ - /* Gather remote addresses for this component */ -@@ -1995,4 +1998,37 @@ - sess_init_update(comp->ice_st); - -+ } else if ((old_state == PJ_TURN_STATE_RESOLVING || old_state == PJ_TURN_STATE_ALLOCATING) && -+ new_state >= PJ_TURN_STATE_DEALLOCATING) -+ { -+ pj_ice_sess_cand *cand = NULL; -+ unsigned i; -+ -+ /* DNS resolution has failed! */ -+ ++comp->turn[tp_idx].err_cnt; -+ -+ /* Unregister ourself from the TURN relay */ -+ pj_turn_sock_set_user_data(turn_sock, NULL); -+ comp->turn[tp_idx].sock = NULL; -+ -+ /* Wait until initialization completes */ -+ pj_grp_lock_acquire(comp->ice_st->grp_lock); -+ -+ /* Find relayed candidate in the component */ -+ for (i=0; i<comp->cand_cnt; ++i) { -+ if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED && -+ comp->cand_list[i].transport_id == data->transport_id) -+ { -+ cand = &comp->cand_list[i]; -+ break; -+ } -+ } -+ pj_assert(cand != NULL); -+ -+ pj_grp_lock_release(comp->ice_st->grp_lock); -+ -+ cand->status = old_state == PJ_TURN_STATE_RESOLVING ? PJ_ERESOLVE : PJ_EINVALIDOP; -+ -+ sess_init_update(comp->ice_st); -+ - } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) { - pj_turn_session_info info; diff --git a/contrib/src/pjproject/ice_config.patch b/contrib/src/pjproject/ice_config.patch deleted file mode 100644 index 3eaa383819972f1f315ef243e946211c9e6a0604..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/ice_config.patch +++ /dev/null @@ -1,61 +0,0 @@ -Description: - Increase PJ_ICE_MAX_CAND to 256. Upstream's default, 16, is - not enough to handle cases with numerous local/remote candidates - like the case when multiple hosts in IPv6 and IPv4 exists. - Tests show that 40 entries are needed with 5 local host IPs. - . - Increase PJ_ICE_MAX_CHECKS, which would be way too low for the - number of candidates. - . - Increase PJ_ICE_MAX_STUN to 6: - - One active, one passive for IPv4 - - One active, one passive for IPv6 - - One active, one passive set by the user - . - Increase PJ_ICE_COMP_BITS so that we can have more components: - - 2 Audio components - - 2 Video components -Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> - - pjnath/include/pjnath/config.h | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h -index 5c2f04bd..d8b546c8 100644 ---- a/pjnath/include/pjnath/config.h -+++ b/pjnath/include/pjnath/config.h -@@ -232,7 +232,7 @@ - * Default: 16 - */ - #ifndef PJ_ICE_MAX_CAND --# define PJ_ICE_MAX_CAND 16 -+# define PJ_ICE_MAX_CAND 256 - #endif - - -@@ -253,7 +253,7 @@ - * Default: 4 (one active and one passive for IPv4 and IPv6) - */ - #ifndef PJ_ICE_MAX_STUN --# define PJ_ICE_MAX_STUN 4 -+# define PJ_ICE_MAX_STUN 6 - #endif - - -@@ -273,7 +273,7 @@ - * the maximum number of components (PJ_ICE_MAX_COMP) value. - */ - #ifndef PJ_ICE_COMP_BITS --# define PJ_ICE_COMP_BITS 1 -+# define PJ_ICE_COMP_BITS 2 - #endif - - -@@ -324,7 +324,7 @@ - * Default: 32 - */ - #ifndef PJ_ICE_MAX_CHECKS --# define PJ_ICE_MAX_CHECKS 32 -+# define PJ_ICE_MAX_CHECKS 1024 - #endif - \ No newline at end of file diff --git a/contrib/src/pjproject/ignore_ipv6_on_transport_check.patch b/contrib/src/pjproject/ignore_ipv6_on_transport_check.patch deleted file mode 100644 index ec472b5168a17c00303a6ebe00e0f19a7b8f6a4b..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/ignore_ipv6_on_transport_check.patch +++ /dev/null @@ -1,18 +0,0 @@ - pjsip/src/pjsip/sip_transport.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c -index 1d0e28d32..cd9e462a4 100644 ---- a/pjsip/src/pjsip/sip_transport.c -+++ b/pjsip/src/pjsip/sip_transport.c -@@ -2151,7 +2151,9 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, - pjsip_transport *seltp = sel->u.transport; - - /* See if the transport is (not) suitable */ -- if (seltp->key.type != type) { -+ pjsip_transport_type_e type_no_ipv6 = type % PJSIP_TRANSPORT_IPV6; -+ pjsip_transport_type_e key_type_no_ipv6 = seltp->key.type % PJSIP_TRANSPORT_IPV6; -+ if (type_no_ipv6 != key_type_no_ipv6) { - pj_lock_release(mgr->lock); - return PJSIP_ETPNOTSUITABLE; - } diff --git a/contrib/src/pjproject/ipv6.patch b/contrib/src/pjproject/ipv6.patch deleted file mode 100644 index 8b42fbbda1bd8ab36f61182e0d4cf6d096b26418..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/ipv6.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/pjlib/include/pj/config.h -+++ b/pjlib/include/pj/config.h -@@ -549,7 +549,7 @@ - * Default: 0 (disabled, for now) - */ - #ifndef PJ_HAS_IPV6 --# define PJ_HAS_IPV6 0 -+# define PJ_HAS_IPV6 1 - #endif - - /** diff --git a/contrib/src/pjproject/keep_alive.patch b/contrib/src/pjproject/keep_alive.patch deleted file mode 100644 index 05ca2152eab56abbb4e12b798b57dc6a3be056d9..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/keep_alive.patch +++ /dev/null @@ -1,149 +0,0 @@ - pjlib/src/pj/sock_bsd.c | 31 +++++++++++++++++++++++++++++++ - pjnath/include/pjnath/ice_session.h | 4 ++++ - pjnath/include/pjnath/ice_strans.h | 7 +++++++ - pjnath/src/pjnath/ice_session.c | 6 ++++++ - pjnath/src/pjnath/ice_strans.c | 11 +++++++++++ - 5 files changed, 59 insertions(+) - -diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c -index e416991d..9db16b93 100644 ---- a/pjlib/src/pj/sock_bsd.c -+++ b/pjlib/src/pj/sock_bsd.c -@@ -28,6 +28,15 @@ - - #define THIS_FILE "sock_bsd.c" - -+#if !defined(PJ_WIN32) && !defined(PJ_WIN64) -+# if !defined(SOL_TCP) && defined(IPPROTO_TCP) -+# define SOL_TCP IPPROTO_TCP -+# endif -+# if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) -+# define TCP_KEEPIDLE TCP_KEEPALIVE -+# endif -+#endif -+ - /* - * Address families conversion. - * The values here are indexed based on pj_addr_family. -@@ -517,6 +526,20 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, - if (rc==SOCKET_ERROR) { - // Ignored.. - } -+ } else if(type == pj_SOCK_STREAM()) { -+#ifndef SIO_KEEPALIVE_VALS -+# define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4) -+#endif -+ DWORD dwBytesReturned = 0; -+ struct tcp_keepalive { -+ ULONG onoff; -+ ULONG keepalivetime; -+ ULONG keepaliveinterval; -+ } vals = { TRUE, 30000, 30000 }; -+ WSAIoctl(*sock, SIO_KEEPALIVE_VALS, -+ &vals, sizeof(vals), -+ NULL, 0, &dwBytesReturned, -+ NULL, NULL); - } - #endif - -@@ -548,6 +571,14 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, - if (type == pj_SOCK_STREAM()) { - pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(), - &val, sizeof(val)); -+ uint32_t val = 1; -+ setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(uint32_t)); -+ uint32_t keepint = 30; // seconds -+ setsockopt(*sock, SOL_TCP, TCP_KEEPIDLE, &keepint, sizeof(uint32_t)); -+ keepint = 30; // seconds -+ setsockopt(*sock, SOL_TCP, TCP_KEEPINTVL, &keepint, sizeof(uint32_t)); -+ uint32_t cnt = 1; -+ setsockopt(*sock, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(uint32_t)); - } - #if defined(PJ_SOCK_HAS_IPV6_V6ONLY) && PJ_SOCK_HAS_IPV6_V6ONLY != 0 - if (af == PJ_AF_INET6) { -diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h -index 77e1278d..141d5b3e 100644 ---- a/pjnath/include/pjnath/ice_session.h -+++ b/pjnath/include/pjnath/ice_session.h -@@ -630,6 +630,10 @@ typedef struct pj_ice_sess_cb - pj_status_t (*close_tcp_connection)(pj_ice_sess *ice, - pj_ice_sess_checklist *clist, - unsigned check_id); -+ /** -+ * If an internal TCP keep alive, this mount the error to the application -+ */ -+ void (*on_ice_destroy)(pj_ice_sess *ice); - - } pj_ice_sess_cb; - -diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h -index afaddce2..de14be0b 100644 ---- a/pjnath/include/pjnath/ice_strans.h -+++ b/pjnath/include/pjnath/ice_strans.h -@@ -182,6 +182,13 @@ typedef struct pj_ice_strans_cb - void (*on_data_sent)(pj_ice_strans *ice_st, unsigned comp_id, - pj_ssize_t size); - -+ /** -+ * This callback is called if an internal operation fails -+ * -+ * @param ice_st The ICE stream transport. -+ */ -+ void (*on_destroy)(pj_ice_strans *ice_st); -+ - } pj_ice_strans_cb; - - -diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c -index 7373cdf3..6e47ca9d 100644 ---- a/pjnath/src/pjnath/ice_session.c -+++ b/pjnath/src/pjnath/ice_session.c -@@ -1413,6 +1413,12 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) - PJ_FALSE, PJ_FALSE, - &the_check->rcand->addr, - addr_len, tdata); -+ if (status == 120032) { -+ if (ice->cb.on_ice_destroy) { -+ ice->cb.on_ice_destroy(ice); -+ } -+ return; -+ } - - /* Restore FINGERPRINT usage */ - pj_stun_session_use_fingerprint(comp->stun_sess, saved); -diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c -index e758ad84..5537c7a0 100644 ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -90,6 +90,7 @@ static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = - - - /* ICE callbacks */ -+static void on_ice_destroy(pj_ice_sess *ice); - static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); - static pj_status_t ice_tx_pkt(pj_ice_sess *ice, - unsigned comp_id, -@@ -1174,6 +1175,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, - ice_cb.on_rx_data = &ice_rx_data; - ice_cb.on_tx_pkt = &ice_tx_pkt; - #if PJ_HAS_TCP -+ ice_cb.on_ice_destroy = &on_ice_destroy; - ice_cb.wait_tcp_connection = &ice_wait_tcp_connection; - ice_cb.select_turn_dataconn = &ice_select_turn_dataconn; - ice_cb.reconnect_tcp_connection = &ice_reconnect_tcp_connection; -@@ -1700,6 +1702,15 @@ pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data, - return PJ_EINVALIDOP; - } - -+static void on_ice_destroy(pj_ice_sess *ice) -+{ -+ pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; -+ -+ if (ice_st->cb.on_destroy) { -+ (*ice_st->cb.on_destroy)(ice_st); -+ } -+} -+ - /* - * Callback called by ICE session when ICE processing is complete, either - * successfully or with failure. diff --git a/contrib/src/pjproject/multiple_listeners.patch b/contrib/src/pjproject/multiple_listeners.patch deleted file mode 100644 index ebdcc75f5d1c3d0e7723f782d92e561a61beda1a..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/multiple_listeners.patch +++ /dev/null @@ -1,59 +0,0 @@ - pjsip/src/pjsip/sip_transport.c | 26 ++++++++++++++++++-------- - 1 file changed, 18 insertions(+), 8 deletions(-) - -diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c -index 67e235a3..a270d0a9 100644 ---- a/pjsip/src/pjsip/sip_transport.c -+++ b/pjsip/src/pjsip/sip_transport.c -@@ -1338,14 +1338,16 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, - - pj_lock_acquire(mgr->lock); - -- /* Check that no factory with the same type has been registered. */ -+ /* Check that no factory with the same type and bound address has been -+ * registered. */ - status = PJ_SUCCESS; - for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { -- if (p->type == tpf->type) { -- status = PJSIP_ETYPEEXISTS; -- break; -- } -- if (p == tpf) { -+ if (p->type == tpf->type && -+ !pj_sockaddr_cmp(&tpf->local_addr, &p->local_addr)) { -+ status = PJSIP_ETYPEEXISTS; -+ break; -+ } -+ if (p == tpf) { - status = PJ_EEXISTS; - break; - } -@@ -2166,10 +2168,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, - pj_hash_get(mgr->table, &key, key_len, NULL); - } - -+ unsigned flag = pjsip_transport_get_flag_from_type(type); - if (transport == NULL && - (!sel || sel->disable_connection_reuse == PJ_FALSE)) - { -- unsigned flag = pjsip_transport_get_flag_from_type(type); - const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote; - - -@@ -2258,7 +2260,15 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, - - } else { - -- /* Find factory with type matches the destination type */ -+ /* Make sure we don't use another factory than the one given if -+ secure flag is set */ -+ if (flag & PJSIP_TRANSPORT_SECURE) { -+ TRACE_((THIS_FILE, "Can't create new TLS transport with no " -+ "provided suitable TLS listener.")); -+ return PJSIP_ETPNOTSUITABLE; -+ } -+ -+ /* Find factory with type matches the destination type */ - factory = mgr->factory_list.next; - while (factory != &mgr->factory_list) { - if (factory->type == type) diff --git a/contrib/src/pjproject/package.json b/contrib/src/pjproject/package.json index 3144fc3bdca8cdb9a779390b3fe4cafce7e4e0ae..f32ec6ca809c8f2a698660bd3539d83402721acf 100644 --- a/contrib/src/pjproject/package.json +++ b/contrib/src/pjproject/package.json @@ -1,31 +1,21 @@ { "name": "pjproject", - "version": "5dfa75be7d69047387f9b0436dd9492bbbf03fe4", + "version": "3e7b75cb2e482baee58c1991bd2fa4fb06774e0d", "url": "https://github.com/pjsip/pjproject/archive/__VERSION__.tar.gz", "deps": ["gnutls"], "patches": [ - "fix_turn_alloc_failure.patch", - "ipv6.patch", - "multiple_listeners.patch", - "pj_ice_sess.patch", - "fix_turn_fallback.patch", - "fix_ioqueue_ipv6_sendto.patch", - "add_dtls_transport.patch", - "rfc6544.patch", - "ice_config.patch", - "fix_first_packet_turn_tcp.patch", - "fix_ebusy_turn.patch", - "ignore_ipv6_on_transport_check.patch", - "fix_turn_connection_failure.patch", - "disable_local_resolution.patch", - "fix_assert_on_connection_attempt.patch", - "keep_alive.patch", - "sip_td_timeout.patch" - ], - "win_patches": [ - "win32_vs_gnutls.patch", - "win_config.patch", - "win_vs2017_props.patch" + "0001-rfc6544.patch", + "0002-rfc2466.patch", + "0003-add-tcp-keep-alive.patch", + "0004-multiple_listeners.patch", + "0005-fix_ebusy_turn.patch", + "0006-ignore_ipv6_on_transport_check.patch", + "0007-pj_ice_sess.patch", + "0008-fix_ioqueue_ipv6_sendto.patch", + "0009-add-config-site.patch", + "0001-win-config.patch", + "0002-win-vs-gnutls.patch", + "0003-win-vs2017-props.patch" ], "project_paths": [ "pjlib-util/build/pjlib_util.vcxproj", @@ -45,4 +35,4 @@ "build": [], "post_build": [] } -} \ No newline at end of file +} diff --git a/contrib/src/pjproject/pj_ice_sess.patch b/contrib/src/pjproject/pj_ice_sess.patch deleted file mode 100644 index f8cf88c5423045dfd3dcd7669f5b93898ba455aa..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/pj_ice_sess.patch +++ /dev/null @@ -1,29 +0,0 @@ -Description: Expose the ice session to allow adding candidates. - . - Ring has other ways than stun to determine its IP address. - . - See ice_transport.cpp for usage details. -Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com> - ---- a/pjnath/include/pjnath/ice_strans.h -+++ b/pjnath/include/pjnath/ice_strans.h -@@ -845,6 +845,8 @@ PJ_DECL(pj_status_t) pj_ice_strans_sendt - int dst_addr_len); - - -+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans *ice_st); -+ - /** - * @} - */ ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -1243,6 +1243,11 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto - return PJ_EINVALIDOP; - } - -+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess( pj_ice_strans *ice_st ) -+{ -+ return ice_st->ice; -+} -+ diff --git a/contrib/src/pjproject/rfc2466.patch b/contrib/src/pjproject/rfc2466.patch deleted file mode 100644 index cd5143a66b16993ec9248b4f53fbc5d92881cafc..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/rfc2466.patch +++ /dev/null @@ -1,197 +0,0 @@ -From 2c4a267fbd68f394630acbab0a0e358d0c905464 Mon Sep 17 00:00:00 2001 -From: Sebastien Blin <sebastien.blin@savoirfairelinux.com> -Date: Wed, 18 Jul 2018 12:12:04 -0400 -Subject: [PATCH 1/1] RFC 2466: ignore deprecated candidates for new ICE - candidates - ---- - pjnath/src/pjnath/ice_session.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 162 insertions(+) - -diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c -index c51dba77..a5c7a5b0 100644 ---- a/pjnath/src/pjnath/ice_session.c -+++ b/pjnath/src/pjnath/ice_session.c -@@ -29,6 +29,21 @@ - #include <pj/rand.h> - #include <pj/string.h> - -+#if defined(_WIN32) || defined(__APPLE__) -+/* TODO(sblin): find an alternative for these paltforms */ -+#else -+/* The following headers are used to get DEPRECATED addresses -+ * as specified in RFC 2462 Section 5.5.4 -+ * https://tools.ietf.org/html/rfc2462#section-5.5.4 -+ */ -+#include <arpa/inet.h> -+#include <asm/types.h> -+#include <linux/netlink.h> -+#include <linux/rtnetlink.h> -+#include <sys/socket.h> -+#include <unistd.h> -+#endif -+ - /* String names for candidate types */ - static const char *cand_type_names[] = - { -@@ -701,6 +716,128 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, - #endif - } - -+/* retrieve invalid addresses and store it in a string */ -+static PJ_DEF(void) get_invalid_addresses(char** addresses, size_t* size) -+{ -+#if defined(_WIN32) || defined(__APPLE__) -+ /* TODO(sblin): find an alternative for these paltforms */ -+#else -+ struct { -+ struct nlmsghdr nlmsg_info; -+ struct ifaddrmsg ifaddrmsg_info; -+ } netlink_req; -+ -+ int fd; -+ -+ long pagesize = sysconf(_SC_PAGESIZE); -+ -+ if (!pagesize) -+ pagesize = 4096; /* Assume pagesize is 4096 if sysconf() failed */ -+ -+ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); -+ if(fd < 0) { -+ perror("socket initialization error: abort"); -+ return; -+ } -+ -+ int rtn; -+ -+ bzero(&netlink_req, sizeof(netlink_req)); -+ -+ netlink_req.nlmsg_info.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); -+ netlink_req.nlmsg_info.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; -+ netlink_req.nlmsg_info.nlmsg_type = RTM_GETADDR; -+ netlink_req.nlmsg_info.nlmsg_pid = getpid(); -+ -+ netlink_req.ifaddrmsg_info.ifa_family = AF_INET6; -+ -+ rtn = send(fd, &netlink_req, netlink_req.nlmsg_info.nlmsg_len, 0); -+ if(rtn < 0) { -+ perror("send error: abort"); -+ return; -+ } -+ -+ char read_buffer[pagesize]; -+ struct nlmsghdr *nlmsg_ptr; -+ int nlmsg_len; -+ -+ size_t idx = 0; -+ // Will store all deprecated addresses into a string -+ char* deprecatedAddrs = malloc(256*sizeof(char)*PJ_INET6_ADDRSTRLEN); -+ if (!deprecatedAddrs) { -+ perror("malloc error: abort"); -+ return; -+ } -+ -+ while(1) { -+ int rtn; -+ -+ bzero(read_buffer, pagesize); -+ rtn = recv(fd, read_buffer, pagesize, 0); -+ if(rtn < 0) { -+ perror ("recv(): "); -+ return; -+ } -+ -+ nlmsg_ptr = (struct nlmsghdr *) read_buffer; -+ nlmsg_len = rtn; -+ -+ if (nlmsg_len < sizeof (struct nlmsghdr)) { -+ perror ("Received an uncomplete netlink packet"); -+ return; -+ } -+ -+ if (nlmsg_ptr->nlmsg_type == NLMSG_DONE) -+ break; -+ -+ for(; NLMSG_OK(nlmsg_ptr, nlmsg_len); nlmsg_ptr = NLMSG_NEXT(nlmsg_ptr, nlmsg_len)) { -+ struct ifaddrmsg *ifaddrmsg_ptr; -+ struct rtattr *rtattr_ptr; -+ int ifaddrmsg_len; -+ -+ ifaddrmsg_ptr = (struct ifaddrmsg *) NLMSG_DATA(nlmsg_ptr); -+ -+ if (ifaddrmsg_ptr->ifa_flags & IFA_F_DEPRECATED || ifaddrmsg_ptr->ifa_flags & IFA_F_TENTATIVE) { -+ rtattr_ptr = (struct rtattr *) IFA_RTA(ifaddrmsg_ptr); -+ ifaddrmsg_len = IFA_PAYLOAD(nlmsg_ptr); -+ -+ for(;RTA_OK(rtattr_ptr, ifaddrmsg_len); rtattr_ptr = RTA_NEXT(rtattr_ptr, ifaddrmsg_len)) { -+ switch(rtattr_ptr->rta_type) { -+ case IFA_ADDRESS: -+ // Any 256 obsolete ips (should not happen), resize the array. -+ if (idx > 0 && idx % 256 == 0) { -+ char* newDeprecated = realloc(deprecatedAddrs, (idx + 256)*sizeof(char)*PJ_INET6_ADDRSTRLEN); -+ if (newDeprecated == NULL) { -+ perror("realloc error: abort"); -+ return; -+ } -+ deprecatedAddrs = newDeprecated; -+ } -+ // Store deprecated IP -+ inet_ntop(ifaddrmsg_ptr->ifa_family, RTA_DATA(rtattr_ptr), &deprecatedAddrs[idx*PJ_INET6_ADDRSTRLEN], sizeof(char)*PJ_INET6_ADDRSTRLEN); -+ ++idx; -+ break; -+ default: -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ close(fd); -+ *size = idx; -+ if (idx > 0) { -+ char *final = realloc(deprecatedAddrs, idx*sizeof(char)*PJ_INET6_ADDRSTRLEN); -+ if (final) { -+ *addresses = final; -+ } else { -+ perror("realloc error: abort"); -+ } -+ } -+#endif -+} -+ - - /* - * Add ICE candidate -@@ -717,6 +854,31 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, - int addr_len, - unsigned *p_cand_id) - { -+ -+ -+ /** -+ * RFC 2466: an ip address can have the status DEPRECATED and SHOULD NOT -+ * be used by new by applications unless they already use it. -+ * So, we should ignore these addresses. -+ * Also, ips with the TENTATIVE state are not ready and SHOULD NOT be -+ * used for now. Ignore these addresses too. -+ */ -+ char* deprecatedAddrs = NULL; -+ size_t size = 0; -+ get_invalid_addresses(&deprecatedAddrs, &size); -+ if (deprecatedAddrs != NULL) { -+ char tmpAddrStr[PJ_INET6_ADDRSTRLEN]; -+ pj_sockaddr_print(addr, tmpAddrStr, sizeof(tmpAddrStr), 0); -+ for (int i = 0; i < size * PJ_INET6_ADDRSTRLEN; i += PJ_INET6_ADDRSTRLEN) { -+ if (strncmp(tmpAddrStr, &deprecatedAddrs[i], PJ_INET6_ADDRSTRLEN) == 0) { -+ free(deprecatedAddrs); -+ // This address is considered as deprecated so, we can ignore this one. -+ return PJ_SUCCESS; -+ } -+ } -+ free(deprecatedAddrs); -+ } -+ - pj_ice_sess_cand *lcand; - pj_status_t status = PJ_SUCCESS; - char address[PJ_INET6_ADDRSTRLEN]; diff --git a/contrib/src/pjproject/rfc6544.patch b/contrib/src/pjproject/rfc6544.patch deleted file mode 100644 index 89b96600561cad459cb3d08d0649a5f25aaae871..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/rfc6544.patch +++ /dev/null @@ -1,5501 +0,0 @@ -Copyright (C) 2018-2019 Savoir-faire Linux Inc. - -ice: rfc6544 support - -This patch is an implementation proposal of the RFC 6544 into PJNATH. -This allow PJNATH to support TCP ICE candidates and open a direct TCP -connection between peers. - -+ BUG (semi-fixed with this patch): If an active_sock is busy due to -a pending packet and receives a new packet to send, the final sent -packet will be a mix between the pending packet and the new one. -To avoid this, pj_ice_strans_sendto2 is now introduced. - -Written by -Sébastien Blin <sebastien.blin@savoirfairelinux.com> -on behalf of Savoir-faire Linux. - ---- - pjnath/include/pjnath/config.h | 13 +- - pjnath/include/pjnath/ice_session.h | 187 +++++++++- - pjnath/include/pjnath/ice_strans.h | 61 +++- - pjnath/include/pjnath/stun_session.h | 82 ++++- - pjnath/include/pjnath/stun_sock.h | 93 ++++- - pjnath/include/pjnath/turn_sock.h | 11 + - pjnath/src/pjnath-test/concur_test.c | 8 +- - pjnath/src/pjnath-test/sess_auth.c | 12 +- - pjnath/src/pjnath-test/stun_sock_test.c | 7 +- - pjnath/src/pjnath/ice_session.c | 595 +++++++++++++++++++++++------- - pjnath/src/pjnath/ice_strans.c | 986 +++++++++++++++++++++++++++++++++++++------------- - pjnath/src/pjnath/nat_detect.c | 12 +- - pjnath/src/pjnath/stun_session.c | 12 +- - pjnath/src/pjnath/stun_sock.c | 1359 ++++++++++++++++++++++++++++++++++++++++++++++++++------------------- - pjnath/src/pjnath/stun_transaction.c | 1 + - pjnath/src/pjnath/turn_session.c | 3 +- - pjnath/src/pjnath/turn_sock.c | 16 + - pjnath/src/pjturn-client/client_main.c | 11 +- - pjnath/src/pjturn-srv/allocation.c | 2 +- - pjnath/src/pjturn-srv/server.c | 2 +- - pjsip-apps/src/samples/icedemo.c | 671 +++++++++++++++++++--------------- - pjsip/src/pjsua-lib/pjsua_core.c | 8 +- - 22 files changed, 3049 insertions(+), 1103 deletions(-) - -diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h -index fc1e2755..6f17a663 100644 ---- a/pjnath/include/pjnath/config.h -+++ b/pjnath/include/pjnath/config.h -@@ -1,5 +1,5 @@ - /* $Id$ */ --/* -+/* - * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> - * -@@ -248,7 +248,7 @@ - * Default: 8 - */ - #ifndef PJ_ICE_ST_MAX_CAND --# define PJ_ICE_ST_MAX_CAND 8 -+# define PJ_ICE_ST_MAX_CAND 32 - #endif - - -@@ -256,21 +256,20 @@ - * Maximum number of STUN transports for each ICE stream transport component. - * Valid values are 1 - 64. - * -- * Default: 2 -+ * Default: 4 (one active and one passive for IPv4 and IPv6) - */ - #ifndef PJ_ICE_MAX_STUN --# define PJ_ICE_MAX_STUN 2 -+# define PJ_ICE_MAX_STUN 4 - #endif - -- - /** - * Maximum number of TURN transports for each ICE stream transport component. - * Valid values are 1 - 64. - * -- * Default: 2 -+ * Default: 4 (one active and one passive for IPv4 and IPv6) - */ - #ifndef PJ_ICE_MAX_TURN --# define PJ_ICE_MAX_TURN 3 -+# define PJ_ICE_MAX_TURN 4 - #endif - - -diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h -index fa13a3b7..77e1278d 100644 ---- a/pjnath/include/pjnath/ice_session.h -+++ b/pjnath/include/pjnath/ice_session.h -@@ -163,6 +163,51 @@ typedef enum pj_ice_cand_type - - } pj_ice_cand_type; - -+/** -+ * ICE candidates types like described by RFC 6544. -+ */ -+typedef enum pj_ice_cand_transport { -+ /** -+ * Candidates UDP compatible -+ */ -+ PJ_CAND_UDP, -+ /** -+ * Candidates sending outgoing TCP connections -+ */ -+ PJ_CAND_TCP_ACTIVE, -+ /** -+ * Candidates accepting incoming TCP connections -+ */ -+ PJ_CAND_TCP_PASSIVE, -+ /** -+ * Candidates capable of receiving incoming connections and sending -+ * connections -+ */ -+ PJ_CAND_TCP_SO -+} pj_ice_cand_transport; -+ -+/** -+ * ICE transport types, which will be used both to specify the connection -+ * type for reaching candidates and other client -+ */ -+typedef enum pj_ice_tp_type { -+ /** -+ * UDP transport, which value corresponds to IANA protocol number. -+ */ -+ PJ_ICE_TP_UDP = 17, -+ -+ /** -+ * TCP transport, which value corresponds to IANA protocol number. -+ */ -+ PJ_ICE_TP_TCP = 6, -+ -+ /** -+ * TLS transport. The TLS transport will only be used as the connection -+ * type to reach the server and never as the allocation transport type. -+ */ -+ PJ_ICE_TP_TLS = 255 -+ -+} pj_ice_tp_type; - - /** Forward declaration for pj_ice_sess */ - typedef struct pj_ice_sess pj_ice_sess; -@@ -309,6 +354,11 @@ typedef struct pj_ice_sess_cand - */ - pj_sockaddr rel_addr; - -+ /** -+ * Transport used (TCP or UDP) -+ */ -+ pj_ice_cand_transport transport; -+ - } pj_ice_sess_cand; - - -@@ -324,6 +374,22 @@ typedef enum pj_ice_sess_check_state - */ - PJ_ICE_SESS_CHECK_STATE_FROZEN, - -+ /** -+ * The following status is used when a packet sent via TURN got a -+ * "Connection reset by peer". This mean that the peer didn't allow -+ * us to connect yet. The socket will be reconnected during the next -+ * loop. -+ */ -+ PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, -+ -+ /** -+ * TODO (sblin): REMOVE THIS! - https://github.com/coturn/coturn/issues/408 -+ * For now, this status is only used because sometimes, the first packet -+ * doesn't receive any response. So, we retry to send the packet every -+ * 50 loops. -+ */ -+ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, -+ - /** - * A check has not been performed for this pair, and can be - * performed as soon as it is the highest priority Waiting pair on -@@ -331,6 +397,13 @@ typedef enum pj_ice_sess_check_state - */ - PJ_ICE_SESS_CHECK_STATE_WAITING, - -+ -+ /** -+ * A check has not been performed for this pair, but TCP socket -+ * is currently connecting to the pair. Wait to finish the connection. -+ */ -+ PJ_ICE_SESS_CHECK_STATE_PENDING, -+ - /** - * A check has not been performed for this pair, and can be - * performed as soon as it is the highest priority Waiting pair on -@@ -512,6 +585,52 @@ typedef struct pj_ice_sess_cb - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); -+ -+ /** -+ * Wait for TCP and send connectivity check -+ * -+ * @param ice The ICE session. -+ * @param clist The ICE connection list -+ * @param check_id The wanted check. -+ */ -+ pj_status_t (*wait_tcp_connection)(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+ -+ /** -+ * Select incoming TURN dataconn -+ * -+ * @param ice The ICE session. -+ * @param clist The ICE connection list -+ * @param check_id The wanted check. -+ */ -+ pj_status_t (*select_turn_dataconn)(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+ -+ /** -+ * Reconnect a resetted TCP connection and send connectivity check -+ * cf. PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY -+ * -+ * @param ice The ICE session. -+ * @param clist The ICE connection list -+ * @param check_id The wanted check. -+ */ -+ pj_status_t (*reconnect_tcp_connection)(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+ -+ /** -+ * Close TCP socket -+ * -+ * @param ice The ICE session. -+ * @param clist The ICE connection list -+ * @param check_id The wanted check. -+ */ -+ pj_status_t (*close_tcp_connection)(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+ - } pj_ice_sess_cb; - - -@@ -627,6 +746,7 @@ struct pj_ice_sess - pj_bool_t is_destroying; /**< Destroy is called */ - pj_status_t ice_status; /**< Error status. */ - pj_timer_entry timer; /**< ICE timer. */ -+ pj_timer_entry timer_connect; /**< ICE timer tcp timeout. */ - pj_ice_sess_cb cb; /**< Callback. */ - - pj_stun_config stun_cfg; /**< STUN settings. */ -@@ -669,6 +789,7 @@ struct pj_ice_sess - char txt[128]; - char errmsg[PJ_ERR_MSG_SIZE]; - } tmp; -+ - }; - - -@@ -826,8 +947,6 @@ PJ_DECL(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, - PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, - const pj_uint8_t prefs[4]); - -- -- - /** - * Add a candidate to this ICE session. Application must add candidates for - * each components ID before it can start pairing the candidates and -@@ -846,20 +965,17 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, - * @param rel_addr Optional related address. - * @param addr_len Length of addresses. - * @param p_cand_id Optional pointer to receive the candidate ID. -+ * @param transport Candidate's type - * - * @return PJ_SUCCESS if candidate is successfully added. - */ --PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, -- unsigned comp_id, -- unsigned transport_id, -- pj_ice_cand_type type, -- pj_uint16_t local_pref, -- const pj_str_t *foundation, -- const pj_sockaddr_t *addr, -- const pj_sockaddr_t *base_addr, -- const pj_sockaddr_t *rel_addr, -- int addr_len, -- unsigned *p_cand_id); -+PJ_DECL(pj_status_t) -+pj_ice_sess_add_cand(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, -+ pj_ice_cand_type type, pj_uint16_t local_pref, -+ const pj_str_t *foundation, const pj_sockaddr_t *addr, -+ const pj_sockaddr_t *base_addr, -+ const pj_sockaddr_t *rel_addr, int addr_len, -+ unsigned *p_cand_id, pj_ice_cand_transport transport); - - /** - * Find default candidate for the specified component ID, using this -@@ -968,7 +1084,52 @@ PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, - const pj_sockaddr_t *src_addr, - int src_addr_len); - -+/** -+ * Notification when ICE session get a new incoming connection -+ * -+ * @param ice The ICE session. -+ * @param transport_id Related transport -+ * @param status PJ_SUCCESS when connection is made, or any errors -+ * if the connection has failed (or if the peer has -+ * disconnected after an established connection). -+ * @param remote_addr Connected remove address -+ */ -+PJ_DECL(void) -+ice_sess_on_peer_connection(pj_ice_sess *ice, -+ pj_uint8_t transport_id, pj_status_t status, pj_sockaddr_t* remote_addr); - -+/** -+ * Notification when ICE session get a new resetted connection -+ * cf PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY -+ * -+ * @param ice The ICE session. -+ * @param transport_id Related transport -+ * @param remote_addr Connected remove address -+ */ -+PJ_DECL(void) -+ice_sess_on_peer_reset_connection(pj_ice_sess *ice, -+ pj_uint8_t transport_id, pj_sockaddr_t* remote_addr); -+ -+/** -+ * Notification when ICE session get a new packet -+ * Used to remove the PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET status -+ * -+ * @param ice The ICE session. -+ * @param transport_id Related transport -+ * @param remote_addr Connected remove address -+ */ -+PJ_DECL(void) -+ice_sess_on_peer_packet(pj_ice_sess *ice, pj_uint8_t transport_id, pj_sockaddr_t* remote_addr); -+ -+/** -+ * Select dataconn from TURN -+ * -+ * @param ice The ICE session. -+ * @param clist The ice check list -+ * @param check_id The wanted check -+ */ -+PJ_DECL(void) -+ice_select_incoming_turn(pj_ice_sess *ice, pj_ice_sess_checklist *clist, unsigned check_id); - - /** - * @} -diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h -index cb677724..afaddce2 100644 ---- a/pjnath/include/pjnath/ice_strans.h -+++ b/pjnath/include/pjnath/ice_strans.h -@@ -1,5 +1,5 @@ - /* $Id$ */ --/* -+/* - * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> - * -@@ -171,6 +171,17 @@ typedef struct pj_ice_strans_cb - pj_ice_strans_op op, - pj_status_t status); - -+ /** -+ * This callback will be called when the ICE transport has -+ * sent data asynchronously. -+ * -+ * @param ice_st The ICE stream transport. -+ * @param comp_id The component ID. -+ * @param size Size of the packet. -+ */ -+ void (*on_data_sent)(pj_ice_strans *ice_st, unsigned comp_id, -+ pj_ssize_t size); -+ - } pj_ice_strans_cb; - - -@@ -253,6 +264,13 @@ typedef struct pj_ice_strans_stun_cfg - */ - pj_bool_t ignore_stun_error; - -+ /** -+ * Type of connection to the STUN server. -+ * -+ * Default is PJ_STUN_TP_UDP. -+ */ -+ pj_stun_tp_type conn_type; -+ - } pj_ice_strans_stun_cfg; - - -@@ -268,6 +286,13 @@ typedef struct pj_ice_strans_turn_cfg - */ - int af; - -+ /** -+ * If we want to use UDP or TCP as described by RFC 6544. -+ * This will discover candidates via TCP sockets. Then it will -+ * transfer messages on the transport via TCP. -+ */ -+ pj_ice_tp_type protocol; -+ - /** - * Optional TURN socket settings. The default values will be - * initialized by #pj_turn_sock_cfg_default(). This contains -@@ -347,6 +372,13 @@ typedef struct pj_ice_strans_cfg - */ - int af; - -+ /** -+ * If we want to use UDP or TCP as described by RFC 6544. -+ * This will discover candidates via TCP sockets. Then it will -+ * transfer messages on the transport via TCP. -+ */ -+ pj_ice_tp_type protocol; -+ - /** - * STUN configuration which contains the timer heap and - * ioqueue instance to be used, and STUN retransmission -@@ -932,6 +964,33 @@ PJ_DECL(pj_status_t) pj_ice_strans_sendto(pj_ice_strans *ice_st, - const pj_sockaddr_t *dst_addr, - int dst_addr_len); - -+/** -+ * Send outgoing packet using this transport. -+ * Application can send data (normally RTP or RTCP packets) at any time -+ * by calling this function. This function takes a destination -+ * address as one of the arguments, and this destination address should -+ * be taken from the default transport address of the component (that is -+ * the address in SDP c= and m= lines, or in a=rtcp attribute). -+ * If ICE negotiation is in progress, this function will send the data -+ * to the destination address. Otherwise if ICE negotiation has completed -+ * successfully, this function will send the data to the nominated remote -+ * address, as negotiated by ICE. -+ * -+ * @param ice_st The ICE stream transport. -+ * @param comp_id Component ID. -+ * @param data The data or packet to be sent. -+ * @param data_len Size of data or packet, in bytes. -+ * @param dst_addr The destination address. -+ * @param dst_addr_len Length of destination address. -+ * @param size Length of sent packet. -+ * -+ * @return PJ_SUCCESS if data is sent successfully. PJ_EPENDING if -+ * sending -+ */ -+PJ_DECL(pj_status_t) -+pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data, -+ pj_size_t data_len, const pj_sockaddr_t *dst_addr, -+ int dst_addr_len, pj_ssize_t *size); - - PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans *ice_st); - -diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h -index f8ea4d1d..3d42af5a 100644 ---- a/pjnath/include/pjnath/stun_session.h -+++ b/pjnath/include/pjnath/stun_session.h -@@ -1,5 +1,5 @@ - /* $Id$ */ --/* -+/* - * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> - * -@@ -174,6 +174,29 @@ typedef struct pj_stun_rx_data pj_stun_rx_data; - /** Forward declaration for pj_stun_session */ - typedef struct pj_stun_session pj_stun_session; - -+/** -+ * STUN transport types, which will be used both to specify the connection -+ * type for reaching STUN server and the type of allocation transport to be -+ * requested to server (the REQUESTED-TRANSPORT attribute). -+ */ -+typedef enum pj_stun_tp_type { -+ /** -+ * UDP transport, which value corresponds to IANA protocol number. -+ */ -+ PJ_STUN_TP_UDP = 17, -+ -+ /** -+ * TCP transport, which value corresponds to IANA protocol number. -+ */ -+ PJ_STUN_TP_TCP = 6, -+ -+ /** -+ * TLS transport. The TLS transport will only be used as the connection -+ * type to reach the server and never as the allocation transport type. -+ */ -+ PJ_STUN_TP_TLS = 255 -+ -+} pj_stun_tp_type; - - /** - * This is the callback to be registered to pj_stun_session, to send -@@ -307,6 +330,33 @@ typedef struct pj_stun_session_cb - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); - -+ /** -+ * Notification when STUN session get a ConnectionAttempt indication. -+ * -+ * @param stun_session The STUN session. -+ * @param status PJ_SUCCESS when connection is made, or any errors -+ * if the connection has failed (or if the peer has -+ * disconnected after an established connection). -+ * @param remote_addr The remote connected -+ */ -+ void (*on_peer_connection)(pj_stun_session *sess, pj_status_t status, pj_sockaddr_t* remote_addr); -+ -+ /** -+ * Notification when STUN connection is resetted (TCP only). -+ * -+ * @param stun_session The STUN session. -+ * @param remote_addr The remote resetted -+ */ -+ void (*on_peer_reset_connection)(pj_stun_session *sess, pj_sockaddr_t* remote_addr); -+ -+ /** -+ * Notification when STUN connection is resetted (TCP only). -+ * -+ * @param stun_session The STUN session. -+ * @param remote_addr The remote resetted -+ */ -+ void (*on_peer_packet)(pj_stun_session *sess, pj_sockaddr_t* remote_addr); -+ - } pj_stun_session_cb; - - -@@ -388,15 +438,15 @@ typedef enum pj_stun_sess_msg_log_flag - * @param grp_lock Optional group lock to be used by this session. - * If NULL, the session will create one itself. - * @param p_sess Pointer to receive STUN session instance. -+ * @param conn_type if the session use UDP or TCP - * - * @return PJ_SUCCESS on success, or the appropriate error code. - */ --PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg, -- const char *name, -- const pj_stun_session_cb *cb, -- pj_bool_t fingerprint, -- pj_grp_lock_t *grp_lock, -- pj_stun_session **p_sess); -+PJ_DECL(pj_status_t) -+pj_stun_session_create(pj_stun_config *cfg, const char *name, -+ const pj_stun_session_cb *cb, pj_bool_t fingerprint, -+ pj_grp_lock_t *grp_lock, -+ pj_stun_session **p_sess, pj_stun_tp_type conn_type); - - /** - * Destroy the STUN session and all objects created in the context of -@@ -751,6 +801,24 @@ PJ_DECL(void) pj_stun_msg_destroy_tdata(pj_stun_session *sess, - pj_stun_tx_data *tdata); - - -+/** -+ * -+ * @param sess The STUN session. -+ * -+ * @return The callback linked to the STUN session -+ */ -+PJ_DECL(pj_stun_session_cb *) -+pj_stun_session_callback(pj_stun_session *sess); -+ -+/** -+ * -+ * @param sess The STUN session. -+ * -+ * @return The connection type linked to the STUN session -+ */ -+PJ_DECL(pj_stun_tp_type) -+pj_stun_session_tp_type(pj_stun_session *sess); -+ - /** - * @} - */ -diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h -index fff4df88..05a61bb1 100644 ---- a/pjnath/include/pjnath/stun_sock.h -+++ b/pjnath/include/pjnath/stun_sock.h -@@ -1,5 +1,5 @@ - /* $Id$ */ --/* -+/* - * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> - * -@@ -24,17 +24,19 @@ - * @file stun_sock.h - * @brief STUN aware socket transport - */ --#include <pjnath/stun_config.h> --#include <pjlib-util/resolver.h> -+#include <pj/activesock.h> - #include <pj/ioqueue.h> - #include <pj/lock.h> -+#include <pj/pool.h> - #include <pj/sock.h> - #include <pj/sock_qos.h> -- -+#include <pjlib-util/resolver.h> -+#include <pjlib-util/srv_resolver.h> -+#include <pjnath/stun_config.h> -+#include <pjnath/stun_session.h> - - PJ_BEGIN_DECL - -- - /** - * @addtogroup PJNATH_STUN_SOCK - * @{ -@@ -87,9 +89,17 @@ typedef enum pj_stun_sock_op - /** - * IP address change notification from the keep-alive operation. - */ -- PJ_STUN_SOCK_MAPPED_ADDR_CHANGE -+ PJ_STUN_SOCK_MAPPED_ADDR_CHANGE, - -+ /** -+ * STUN session was destroyed. -+ */ -+ PJ_STUN_SESS_DESTROYED, - -+ /** -+ * TCP fails to connect -+ */ -+ PJ_STUN_TCP_CONNECT_ERROR - } pj_stun_sock_op; - - -@@ -195,7 +205,12 @@ typedef struct pj_stun_sock_info - * mapped address has not been resolved. Application may query whether - * this field contains valid address with pj_sockaddr_has_addr(). - */ -- pj_sockaddr mapped_addr; -+ pj_sockaddr mapped_addr; -+ -+ /** -+ * If connected, the remote address will be stored here. -+ */ -+ pj_sockaddr outgoing_addr; - - /** - * Number of interface address aliases. The interface address aliases -@@ -208,6 +223,11 @@ typedef struct pj_stun_sock_info - */ - pj_sockaddr aliases[PJ_ICE_ST_MAX_CAND]; - -+ /** -+ * The tranport type of the socket -+ */ -+ pj_stun_tp_type conn_type; -+ - } pj_stun_sock_info; - - -@@ -332,7 +352,6 @@ PJ_DECL(const char*) pj_stun_sock_op_name(pj_stun_sock_op op); - */ - PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); - -- - /** - * Create the STUN transport using the specified configuration. Once - * the STUN transport has been create, application should call -@@ -342,7 +361,9 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); - * things the ioqueue and timer heap instance for - * the operation of this transport. - * @param af Address family of socket. Currently pj_AF_INET() -- * and pj_AF_INET6() are supported. -+ * and pj_AF_INET6() are supported. -+ * @param conn_type Connection type to the STUN server. Both TCP and -+ * UDP are supported. - * @param name Optional name to be given to this transport to - * assist debugging. - * @param cb Callback to receive events/data from the transport. -@@ -354,14 +375,11 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); - * @return PJ_SUCCESS if the operation has been successful, - * or the appropriate error code on failure. - */ --PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg, -- const char *name, -- int af, -- const pj_stun_sock_cb *cb, -- const pj_stun_sock_cfg *cfg, -- void *user_data, -- pj_stun_sock **p_sock); -- -+PJ_DECL(pj_status_t) -+pj_stun_sock_create(pj_stun_config *stun_cfg, const char *name, int af, -+ pj_stun_tp_type conn_type, const pj_stun_sock_cb *cb, -+ const pj_stun_sock_cfg *cfg, void *user_data, -+ pj_stun_sock **p_sock); - - /** - * Start the STUN transport. This will start the DNS SRV resolution for -@@ -470,6 +488,7 @@ PJ_DECL(pj_status_t) pj_stun_sock_get_info(pj_stun_sock *stun_sock, - * @param flag pj_ioqueue_sendto() flag. - * @param dst_addr The remote address. - * @param addr_len Length of the address. -+ * @param size size sent - * - * @return PJ_SUCCESS if data has been sent immediately, or - * PJ_EPENDING if data cannot be sent immediately. In -@@ -483,7 +502,45 @@ PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock, - unsigned pkt_len, - unsigned flag, - const pj_sockaddr_t *dst_addr, -- unsigned addr_len); -+ unsigned addr_len, -+ pj_ssize_t *size); -+ -+#if PJ_HAS_TCP -+ -+/** -+ * Connect active socket to remote address -+ * @param stun_sock -+ * @param remote_addr the destination -+ * @param af address family -+ */ -+PJ_DECL(pj_status_t) -+pj_stun_sock_connect_active(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr, int af); -+ -+/** -+ * Connect active socket to remote address -+ * @param stun_sock -+ * @param remote_addr the destination -+ * @param af address family -+ */ -+PJ_DECL(pj_status_t) -+pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr, int af); -+ -+/** -+ * Close active socket -+ * @param stun_sock -+ * @param remote_addr The remote address linked -+ */ -+PJ_DECL(pj_status_t) -+pj_stun_sock_close(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr); -+ -+#endif -+ -+/** -+ * Retrieve the linked session -+ * @param stun_sock -+ */ -+PJ_DECL(pj_stun_session *) -+pj_stun_sock_get_session(pj_stun_sock *stun_sock); - - /** - * @} -diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h -index 8650cfc5..99570ac2 100644 ---- a/pjnath/include/pjnath/turn_sock.h -+++ b/pjnath/include/pjnath/turn_sock.h -@@ -490,6 +490,17 @@ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock, - unsigned addr_len); - - -+/** -+ * Check if peer is a dataconn -+ * -+ * @param turn_sock The turn sock -+ * @param peer The peer addr to check -+ * -+ * @return true if dataconn else false -+ */ -+PJ_DECL(pj_bool_t) -+pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, const pj_sockaddr_t *peer); -+ - /** - * @} - */ -diff --git a/pjnath/src/pjnath-test/concur_test.c b/pjnath/src/pjnath-test/concur_test.c -index c3013d2a..ebe17392 100644 ---- a/pjnath/src/pjnath-test/concur_test.c -+++ b/pjnath/src/pjnath-test/concur_test.c -@@ -183,10 +183,10 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess) - for (i=0; i<MAX_SOCK_CLIENTS; ++i) { - char name[10]; - sprintf(name, "stun%02d", i); -- status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(), -- &stun_cb, NULL, test_sess, -- &stun_sock[i]); -- if (status != PJ_SUCCESS) { -+ status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(), -+ PJ_STUN_TP_UDP, &stun_cb, NULL, test_sess, -+ &stun_sock[i]); -+ if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, "Error creating stun socket")); - return -10; - } -diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c -index 055eaad6..d1ad137a 100644 ---- a/pjnath/src/pjnath-test/sess_auth.c -+++ b/pjnath/src/pjnath-test/sess_auth.c -@@ -248,7 +248,8 @@ static int create_std_server(pj_stun_auth_type auth_type, - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_rx_request = &server_on_rx_request; - sess_cb.on_send_msg = &server_send_msg; -- status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, NULL, &server->sess); -+ status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, -+ NULL, &server->sess, PJ_STUN_TP_UDP); - if (status != PJ_SUCCESS) { - destroy_server(); - return -10; -@@ -489,7 +490,8 @@ static int run_client_test(const char *title, - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_request_complete = &client_on_request_complete; - sess_cb.on_send_msg = &client_send_msg; -- status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess); -+ status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, -+ NULL, &client->sess, PJ_STUN_TP_UDP); - if (status != PJ_SUCCESS) { - destroy_client_server(); - return -200; -@@ -575,8 +577,10 @@ static int run_client_test(const char *title, - } - - /* Send the request */ -- status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr, -- pj_sockaddr_get_len(&server->addr), tdata); -+ status = pj_stun_session_send_msg( -+ client->sess, NULL, PJ_FALSE, -+ (pj_stun_session_tp_type(client->sess) == PJ_STUN_TP_UDP), -+ &server->addr, pj_sockaddr_get_len(&server->addr), tdata); - if (status != PJ_SUCCESS) { - destroy_client_server(); - return -270; -diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c -index fff4fad2..a54df74d 100644 ---- a/pjnath/src/pjnath-test/stun_sock_test.c -+++ b/pjnath/src/pjnath-test/stun_sock_test.c -@@ -255,8 +255,8 @@ static pj_status_t create_client(pj_stun_config *cfg, - pj_bzero(&cb, sizeof(cb)); - cb.on_status = &stun_sock_on_status; - cb.on_rx_data = &stun_sock_on_rx_data; -- status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), &cb, &sock_cfg, -- client, &client->sock); -+ status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), PJ_STUN_TP_UDP, -+ &cb, &sock_cfg, client, &client->sock); - if (status != PJ_SUCCESS) { - app_perror(" pj_stun_sock_create()", status); - pj_pool_release(pool); -@@ -584,9 +584,10 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) - char txt[100]; - PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3))); - } -+ pj_ssize_t size; - status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret), - 0, &info.srv_addr, -- pj_sockaddr_get_len(&info.srv_addr)); -+ pj_sockaddr_get_len(&info.srv_addr), &size); - if (status != PJ_SUCCESS && status != PJ_EPENDING) { - app_perror(" error: server sending data", status); - ret = -390; -diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c -index a5c7a5b0..94d797ca 100644 ---- a/pjnath/src/pjnath/ice_session.c -+++ b/pjnath/src/pjnath/ice_session.c -@@ -1,5 +1,5 @@ - /* $Id$ */ --/* -+/* - * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> - * -@@ -59,7 +59,10 @@ static const char *cand_type_names[] = - static const char *check_state_name[] = - { - "Frozen", -+ "Needs Retry", -+ "Needs First Packet", - "Waiting", -+ "Pending", - "In Progress", - "Succeeded", - "Failed" -@@ -90,8 +93,8 @@ enum timer_type - valid check for every components. */ - TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity - checks with USE-CANDIDATE flag. */ -- TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */ -- -+ TIMER_KEEP_ALIVE, /**< ICE keep-alive timer. */ -+ TIMER_CONNECTION_TIMEOUT - }; - - /* Candidate type preference */ -@@ -138,6 +141,7 @@ typedef struct timer_data - { - pj_ice_sess *ice; - pj_ice_sess_checklist *clist; -+ unsigned first_packet_counter; // TODO (remove), for now, needed for the NEEDS_FIRST_PACKET state - } timer_data; - - -@@ -148,6 +152,7 @@ typedef struct timer_data - - /* Forward declarations */ - static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te); -+static void on_tcp_connect_timeout(pj_ice_sess *ice); - static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); - static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now); - static void ice_on_destroy(void *obj); -@@ -303,10 +308,9 @@ static pj_status_t init_comp(pj_ice_sess *ice, - sess_cb.on_send_msg = &on_stun_send_msg; - - /* Create STUN session for this candidate */ -- status = pj_stun_session_create(&ice->stun_cfg, NULL, -- &sess_cb, PJ_TRUE, -- ice->grp_lock, -- &comp->stun_sess); -+ status = pj_stun_session_create(&ice->stun_cfg, NULL, &sess_cb, PJ_TRUE, -+ ice->grp_lock, &comp->stun_sess, -+ PJ_STUN_TP_UDP); - if (status != PJ_SUCCESS) - return status; - -@@ -852,7 +856,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, - const pj_sockaddr_t *base_addr, - const pj_sockaddr_t *rel_addr, - int addr_len, -- unsigned *p_cand_id) -+ unsigned *p_cand_id, -+ pj_ice_cand_transport transport) - { - - -@@ -900,6 +905,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, - lcand->comp_id = (pj_uint8_t)comp_id; - lcand->transport_id = (pj_uint8_t)transport_id; - lcand->type = type; -+ lcand->transport = transport; - pj_strdup(ice->pool, &lcand->foundation, foundation); - lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); - pj_sockaddr_cp(&lcand->addr, addr); -@@ -1121,6 +1127,8 @@ static void check_set_state(pj_ice_sess *ice, pj_ice_sess_check *check, - pj_ice_sess_check_state st, - pj_status_t err_code) - { -+ if (check->state == st) return; // nothing to do -+ if (st == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET && check->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) return; - pj_assert(check->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); - - LOG5((ice->obj_name, "Check %s: state changed from %s to %s", -@@ -1241,6 +1249,17 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, - return PJNATH_EICENOHOSTCAND; - } - } -+ -+ /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) -+ * When the agent prunes the check list, it MUST also remove any pair -+ * for which the local candidate is a passive TCP candidate -+ */ -+ if (clist->checks[i].lcand->transport == PJ_CAND_TCP_PASSIVE) { -+ pj_array_erase(clist->checks, sizeof(clist->checks[0]), -+ clist->count, i); -+ --clist->count; -+ --i; -+ } - } - - /* Next remove a pair if its local and remote candidates are identical -@@ -1343,6 +1362,8 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) - case TIMER_KEEP_ALIVE: - ice_keep_alive(ice, PJ_TRUE); - break; -+ case TIMER_CONNECTION_TIMEOUT: -+ on_tcp_connect_timeout(ice); - case TIMER_NONE: - /* Nothing to do, just to get rid of gcc warning */ - break; -@@ -1364,9 +1385,9 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) - pj_bool_t saved; - pj_status_t status; - -- /* Must have nominated check by now */ -- pj_assert(comp->nominated_check != NULL); -- the_check = comp->nominated_check; -+ /* Must have nominated check by now */ -+ pj_assert(comp->nominated_check != NULL); -+ the_check = comp->nominated_check; - - /* Create the Binding Indication */ - status = pj_stun_session_create_ind(comp->stun_sess, -@@ -1505,7 +1526,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, - check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); - } - } -- - LOG5((ice->obj_name, "Check %d is successful%s", - GET_CHECK_ID(&ice->clist, check), - (check->nominated ? " and nominated" : ""))); -@@ -1771,6 +1791,35 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, - return PJ_FALSE; - } - -+static void on_tcp_connect_timeout(pj_ice_sess* ice) { -+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); -+ -+ pj_bool_t first_found = PJ_FALSE, set_timer = PJ_FALSE; -+ -+ for (int i = 0; i < ice->clist.count; ++i) { -+ pj_ice_sess_check *check = &ice->clist.checks[i]; -+ if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING && !first_found) { -+ if (*ice->cb.close_tcp_connection) (*ice->cb.close_tcp_connection)(ice, &ice->clist, i); -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, PJ_ECANCELLED); -+ on_check_complete(ice, check); -+ first_found = PJ_TRUE; -+ } else if(check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { -+ set_timer = PJ_TRUE; -+ break; -+ } -+ } -+ -+ if (set_timer && ice->timer.id == TIMER_NONE) { -+ // Reschedule -+ pj_time_val delay = {0, 0}; -+ delay.msec = 1500; -+ pj_time_val_normalize(&delay); -+ pj_timer_heap_schedule_w_grp_lock( -+ ice->stun_cfg.timer_heap, &ice->timer, &delay, -+ TIMER_CONNECTION_TIMEOUT, ice->grp_lock); -+ } -+} -+ - - /* Create checklist by pairing local candidates with remote candidates */ - PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( -@@ -1857,6 +1906,25 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( - continue; - } - -+ /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) -+ * As with UDP, check lists are formed only by full ICE implementations. -+ * When forming candidate pairs, the following types of TCP candidates -+ * can be paired with each other: -+ * -+ * Local Remote -+ * Candidate Candidate -+ * --------------------------- -+ * tcp-so tcp-so -+ * tcp-active tcp-passive -+ * tcp-passive tcp-active -+ */ -+ if ((lcand->transport == PJ_CAND_UDP && rcand->transport != PJ_CAND_UDP) -+ || (lcand->transport == PJ_CAND_TCP_PASSIVE && rcand->transport != PJ_CAND_TCP_ACTIVE) -+ || (lcand->transport == PJ_CAND_TCP_ACTIVE && rcand->transport != PJ_CAND_TCP_PASSIVE) -+ || (lcand->transport == PJ_CAND_TCP_SO && rcand->transport != PJ_CAND_TCP_SO)) -+ { -+ continue; -+ } - - chk->lcand = lcand; - chk->rcand = rcand; -@@ -1901,6 +1969,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( - td = PJ_POOL_ZALLOC_T(ice->pool, timer_data); - td->ice = ice; - td->clist = clist; -+ td->first_packet_counter = 1; - clist->timer.user_data = (void*)td; - clist->timer.cb = &periodic_timer; - -@@ -1913,40 +1982,73 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( - return PJ_SUCCESS; - } - --/* Perform check on the specified candidate pair. */ --static pj_status_t perform_check(pj_ice_sess *ice, -- pj_ice_sess_checklist *clist, -- unsigned check_id, -- pj_bool_t nominate) -+static pj_status_t send_connectivity_check(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id, -+ pj_bool_t nominate, -+ pj_ice_msg_data *msg_data) - { -- pj_ice_sess_comp *comp; -- pj_ice_msg_data *msg_data; - pj_ice_sess_check *check; -+ check = &clist->checks[check_id]; - const pj_ice_sess_cand *lcand; -+ lcand = check->lcand; - const pj_ice_sess_cand *rcand; -- pj_uint32_t prio; -+ rcand = check->rcand; -+ pj_ice_sess_comp *comp; -+ comp = find_comp(ice, lcand->comp_id); - pj_status_t status; -+ /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the -+ * STUN session. -+ */ -+ -+ /* Initiate STUN transaction to send the request */ -+ status = pj_stun_session_send_msg( -+ comp->stun_sess, msg_data, PJ_FALSE, -+ pj_stun_session_tp_type(comp->stun_sess) == PJ_STUN_TP_UDP, -+ &rcand->addr, pj_sockaddr_get_len(&rcand->addr), check->tdata); -+ if (status != PJ_SUCCESS) { -+ check->tdata = NULL; -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); -+ on_check_complete(ice, check); -+ pjnath_perror(ice->obj_name, "Error sending STUN request", status); -+ pj_log_pop_indent(); -+ return status; -+ } -+ - -+ return PJ_SUCCESS; -+} -+ -+/* Perform check on the specified candidate pair. */ -+static pj_status_t perform_check(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id, -+ pj_bool_t nominate) -+{ -+ pj_ice_sess_check *check; - check = &clist->checks[check_id]; -+ const pj_ice_sess_cand *lcand; - lcand = check->lcand; -- rcand = check->rcand; -+ pj_ice_sess_comp *comp; - comp = find_comp(ice, lcand->comp_id); -+ pj_status_t status; - -- LOG5((ice->obj_name, -- "Sending connectivity check for check %s", -- dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); - pj_log_push_indent(); -+ LOG5((ice->obj_name, -+ "Sending connectivity check for check %s", -+ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); - - /* Create request */ -- status = pj_stun_session_create_req(comp->stun_sess, -- PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, -- NULL, &check->tdata); -+ status = pj_stun_session_create_req(comp->stun_sess, -+ PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, -+ NULL, &check->tdata); - if (status != PJ_SUCCESS) { -- pjnath_perror(ice->obj_name, "Error creating STUN request", status); -- pj_log_pop_indent(); -- return status; -+ pjnath_perror(ice->obj_name, "Error creating STUN request", status); -+ pj_log_pop_indent(); -+ return status; - } - -+ pj_ice_msg_data *msg_data; - /* Attach data to be retrieved later when STUN request transaction - * completes and on_stun_request_complete() callback is called. - */ -@@ -1958,57 +2060,102 @@ static pj_status_t perform_check(pj_ice_sess *ice, - msg_data->data.req.ckid = check_id; - - /* Add PRIORITY */ -+ pj_uint32_t prio; - #if PJNATH_ICE_PRIO_STD -- prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, -- lcand->comp_id); -+ prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, -+ lcand->comp_id); - #else -- prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0, -- lcand->comp_id); -+ prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0, -+ lcand->comp_id); - #endif -- pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, -- PJ_STUN_ATTR_PRIORITY, prio); -+ -+ -+ pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, -+ PJ_STUN_ATTR_PRIORITY, prio); - - /* Add USE-CANDIDATE and set this check to nominated. - * Also add ICE-CONTROLLING or ICE-CONTROLLED - */ - if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { -- if (nominate) { -- pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg, -- PJ_STUN_ATTR_USE_CANDIDATE); -- check->nominated = PJ_TRUE; -- } -+ if (nominate) { -+ pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg, -+ PJ_STUN_ATTR_USE_CANDIDATE); -+ check->nominated = PJ_TRUE; -+ } - -- pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, -- PJ_STUN_ATTR_ICE_CONTROLLING, -- &ice->tie_breaker); -+ pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, -+ PJ_STUN_ATTR_ICE_CONTROLLING, -+ &ice->tie_breaker); - - } else { -- pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, -- PJ_STUN_ATTR_ICE_CONTROLLED, -- &ice->tie_breaker); -+ if (nominate) { -+ check->nominated = PJ_TRUE; -+ } -+ pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, -+ PJ_STUN_ATTR_ICE_CONTROLLED, -+ &ice->tie_breaker); -+ } -+ -+#if PJ_HAS_TCP -+ if (lcand->transport == PJ_CAND_UDP) { -+ status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); -+ } else if (lcand->transport == PJ_CAND_TCP_ACTIVE) { -+ if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) { -+ status = (*ice->cb.reconnect_tcp_connection)(ice, clist, check_id); -+ } else if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET) { -+ status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); -+ } else { -+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); -+ status = (*ice->cb.wait_tcp_connection)(ice, clist, check_id); -+ if (ice->timer.id == TIMER_NONE) { -+ pj_time_val delay = {0, 0}; -+ delay.msec = 1500; -+ pj_time_val_normalize(&delay); -+ pj_timer_heap_schedule_w_grp_lock( -+ ice->stun_cfg.timer_heap, &ice->timer, &delay, -+ TIMER_CONNECTION_TIMEOUT, ice->grp_lock); -+ } else if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) { -+ pj_assert(!"Not expected any timer active"); -+ } -+ } -+ } else { -+ // TCP PASSIVE -+ if (lcand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); -+ status = (*ice->cb.select_turn_dataconn)(ice, clist, check_id); -+ if (ice->timer.id == TIMER_NONE) { -+ pj_time_val delay = {0, 0}; -+ delay.msec = 1500; -+ pj_time_val_normalize(&delay); -+ pj_timer_heap_schedule_w_grp_lock( -+ ice->stun_cfg.timer_heap, &ice->timer, &delay, -+ TIMER_CONNECTION_TIMEOUT, ice->grp_lock); -+ } -+ } else { -+ status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); -+ } - } -+#else -+ status = send_connectivity_check(&ice, &clist, check_id, nominate, msg_data); -+#endif - - -- /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the -- * STUN session. -- */ -- -- /* Initiate STUN transaction to send the request */ -- status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, -- PJ_TRUE, &rcand->addr, -- pj_sockaddr_get_len(&rcand->addr), -- check->tdata); -- if (status != PJ_SUCCESS) { -- check->tdata = NULL; -- pjnath_perror(ice->obj_name, "Error sending STUN request", status); -- pj_log_pop_indent(); -- return status; -+ if (status == PJ_EPENDING) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_PENDING, -+ status); -+ } else { -+ if (check->rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ // TODO (sblin) remove this - https://github.com/coturn/coturn/issues/408 -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, status); -+ } else { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, -+ PJ_SUCCESS); -+ } - } - -- check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, -- PJ_SUCCESS); - pj_log_pop_indent(); -- return PJ_SUCCESS; -+ -+ return status; - } - - -@@ -2044,44 +2191,101 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, - LOG5((ice->obj_name, "Starting checklist periodic check")); - pj_log_push_indent(); - -+ /* Send STUN Binding request for check with highest priority on -+ * retry state. -+ */ -+ -+ if (start_count == 0) { -+ for (i = 0; i < clist->count; ++i) { -+ pj_ice_sess_check *check = &clist->checks[i]; -+ // Reconnect closed TURN sockets -+ if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) { -+ status = perform_check(ice, clist, i, ice->is_nominating); -+ if (status != PJ_SUCCESS && status != PJ_EPENDING) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, -+ status); -+ on_check_complete(ice, check); -+ } -+ ++start_count; -+ break; -+ } -+ } -+ } -+ -+ if (start_count == 0) { -+ // TODO (sblin) remove - https://github.com/coturn/coturn/issues/408 -+ pj_bool_t inc_counter = PJ_TRUE; -+ for (i = 0; i < clist->count; ++i) { -+ pj_ice_sess_check *check = &clist->checks[i]; -+ if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET) { -+ if (inc_counter) { -+ td->first_packet_counter += 1; -+ inc_counter = PJ_FALSE; -+ } -+ if (td->first_packet_counter % 50 == 0) { -+ status = perform_check(ice, clist, i, ice->is_nominating); -+ if (status != PJ_SUCCESS && status != PJ_EPENDING) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, -+ status); -+ on_check_complete(ice, check); -+ } -+ } -+ ++start_count; -+ break; -+ } -+ } -+ } -+ - /* Send STUN Binding request for check with highest priority on - * Waiting state. - */ -- for (i=0; i<clist->count; ++i) { -+ -+ if (start_count == 0) { -+ for (i = 0; i < clist->count; ++i) { - pj_ice_sess_check *check = &clist->checks[i]; - - if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { - status = perform_check(ice, clist, i, ice->is_nominating); -- if (status != PJ_SUCCESS) { -- check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, -- status); -- on_check_complete(ice, check); -- } -- -- ++start_count; -+ if (status != PJ_SUCCESS && status != PJ_EPENDING) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, -+ status); -+ on_check_complete(ice, check); -+ } -+ ++start_count; - break; - } -+ } - } - - /* If we don't have anything in Waiting state, perform check to - * highest priority pair that is in Frozen state. - */ -- if (start_count==0) { -- for (i=0; i<clist->count; ++i) { -- pj_ice_sess_check *check = &clist->checks[i]; -- -- if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { -- status = perform_check(ice, clist, i, ice->is_nominating); -- if (status != PJ_SUCCESS) { -- check_set_state(ice, check, -- PJ_ICE_SESS_CHECK_STATE_FAILED, status); -- on_check_complete(ice, check); -- } -+ if (start_count == 0) { -+ for (i = 0; i < clist->count; ++i) { -+ pj_ice_sess_check *check = &clist->checks[i]; -+ -+ if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { -+ status = perform_check(ice, clist, i, ice->is_nominating); -+ if (status != PJ_SUCCESS && status != PJ_EPENDING) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); -+ on_check_complete(ice, check); -+ } -+ ++start_count; -+ break; -+ } -+ } -+ } - -- ++start_count; -- break; -- } -- } -+ if (start_count == 0) { -+ // If all sockets are pending, do nothing -+ pj_bool_t inc_counter = PJ_TRUE; -+ for (i = 0; i < clist->count; ++i) { -+ pj_ice_sess_check *check = &clist->checks[i]; -+ if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { -+ ++start_count; -+ break; -+ } -+ } - } - - /* Cannot start check because there's no suitable candidate pair. -@@ -2098,8 +2302,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, - pj_grp_lock_release(ice->grp_lock); - pj_log_pop_indent(); - return PJ_SUCCESS; --} -- -+ } - - /* Start sending connectivity check with USE-CANDIDATE */ - static void start_nominated_check(pj_ice_sess *ice) -@@ -2271,13 +2474,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) - /* First, perform all pending triggered checks, simultaneously. */ - rcheck = ice->early_check.next; - while (rcheck != &ice->early_check) { -- LOG4((ice->obj_name, -- "Performing delayed triggerred check for component %d", -- rcheck->comp_id)); -- pj_log_push_indent(); -- handle_incoming_check(ice, rcheck); -- rcheck = rcheck->next; -- pj_log_pop_indent(); -+ LOG4((ice->obj_name, -+ "Performing delayed triggerred check for component %d", -+ rcheck->comp_id)); -+ pj_log_push_indent(); -+ handle_incoming_check(ice, rcheck); -+ rcheck = rcheck->next; -+ pj_log_pop_indent(); - } - pj_list_init(&ice->early_check); - -@@ -2323,7 +2526,7 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, - /* Stray retransmit timer that could happen while - * we're being destroyed */ - pj_grp_lock_release(ice->grp_lock); -- return PJ_EINVALIDOP; -+ return PJ_EINVALIDOP; - } - - status = (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id, -@@ -2333,6 +2536,154 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, - return status; - } - -+static pj_ice_sess_check* get_current_check_at_state(pj_ice_sess *ice, -+ pj_sockaddr_t* remote_addr, pj_ice_sess_check_state state, int* current_check) -+{ -+ if (!ice || !remote_addr) return NULL; -+ // NOTE: Multiple checks can have the same remote, we only take care of the first -+ // First, check if the TCP is really connected. If not, abort -+ pj_ice_sess_check *check = NULL; -+ for (int i = 0; i < ice->clist.count; ++i) { -+ // Find related check -+ pj_ice_sess_check *c = &ice->clist.checks[i]; -+ /* Host candidate not found this this srflx! */ -+ if (pj_sockaddr_cmp(remote_addr, &c->rcand->addr) == 0) { -+ if (c->tdata == NULL || c->state != state) -+ continue; -+ /* Match */ -+ check = c; -+ if (current_check) *current_check = i; -+ break; -+ } -+ } -+ return check; -+} -+ -+void ice_sess_on_peer_connection(pj_ice_sess *ice, pj_uint8_t transport_id, -+ pj_status_t status, -+ pj_sockaddr_t* remote_addr) { -+ // The TCP link is now ready. We can now send the first STUN message (send -+ // connectivity check) This should trigger on_stun_request_complete when -+ // finished -+ if (!remote_addr) return; -+ -+ int current_check = -1; -+ pj_ice_sess_check *check = get_current_check_at_state(ice, remote_addr, -+ PJ_ICE_SESS_CHECK_STATE_PENDING, ¤t_check); -+ if (!check) { -+ // Handle peer reflexive candidates (incoming are still waiting here) -+ check = get_current_check_at_state(ice, remote_addr, PJ_ICE_SESS_CHECK_STATE_WAITING, ¤t_check); -+ if (!check) { -+ return; -+ } -+ } -+ -+ if (status != PJ_SUCCESS) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); -+ on_check_complete(ice, check); -+ return; -+ } -+ -+ // TCP is correctly connected. Craft the message to send -+ const pj_ice_sess_cand *lcand = check->lcand; -+ const pj_ice_sess_cand *rcand = check->rcand; -+ if (check->tdata == NULL) { -+ LOG5((ice->obj_name, "Error sending STUN request, empty data")); -+ return; -+ } -+ pj_ice_msg_data *msg_data = -+ PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_msg_data); -+ -+ msg_data->transport_id = transport_id; -+ msg_data->has_req_data = PJ_TRUE; -+ msg_data->data.req.ice = ice; -+ msg_data->data.req.clist = &ice->clist; -+ msg_data->data.req.ckid = current_check; -+ -+ pj_ice_sess_comp *comp = find_comp(ice, lcand->comp_id); -+ pj_status_t status_send_msg; -+ // Note that USERNAME and MESSAGE-INTEGRITY will be added by the -+ // STUN session. -+ -+ // Initiate STUN transaction to send the request -+ status_send_msg = pj_stun_session_send_msg( -+ comp->stun_sess, msg_data, PJ_FALSE, PJ_FALSE, &rcand->addr, -+ pj_sockaddr_get_len(&rcand->addr), check->tdata); -+ if (status_send_msg == PJ_EBUSY /* EBUSY */) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, status); -+ return; -+ } -+ if (status_send_msg == 120033 /* BROKEN PIPE */) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, status); -+ return; -+ } -+ -+ if ((status_send_msg == 120104 || status_send_msg == 130054)/* CONNECTION RESET BY PEER */ -+ && rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ /** -+ * This part of the code is triggered when using ICE over TCP via TURN -+ * In fact, the other peer has to authorize this peer to connect to -+ * the relayed candidate. This is done by set_perm from the other case. -+ * But from this side, we can't know if the peer has authorized us. If it's -+ * not the case, the connection will got a CONNECTION RESET BY PEER status. -+ * In this case, we can try to reconnect a bit after and this until the check -+ * reached its timeout. -+ */ -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, status); -+ } else if (status_send_msg != PJ_SUCCESS) { -+ check->tdata = NULL; -+ pjnath_perror(ice->obj_name, "Error sending STUN request", status_send_msg); -+ pj_log_pop_indent(); -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); -+ on_check_complete(ice, check); -+ } else if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, status); -+ } else { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, status); -+ } -+} -+ -+void ice_sess_on_peer_reset_connection(pj_ice_sess *ice, pj_uint8_t transport_id, -+ pj_sockaddr_t* remote_addr) { -+ // The TCP link is reseted -+ if (!remote_addr) return; -+ pj_ice_sess_check *check = get_current_check_at_state(ice, remote_addr, -+ PJ_ICE_SESS_CHECK_STATE_PENDING, NULL); -+ if (!check) { -+ // Just check if it's not the first packet failing -+ check = get_current_check_at_state(ice, remote_addr, -+ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, NULL); -+ if (!check) return; -+ } -+ -+ const pj_ice_sess_cand *rcand = check->rcand; -+ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, 120104); -+ } -+} -+ -+void ice_sess_on_peer_packet(pj_ice_sess *ice, pj_uint8_t transport_id, -+ pj_sockaddr_t* remote_addr) { -+ // The TCP link received its bind request response -+ if (!ice || !remote_addr) return; -+ pj_ice_sess_check *check = get_current_check_at_state(ice, remote_addr, -+ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, NULL); -+ if (!check) return; -+ -+ const pj_ice_sess_cand *rcand = check->rcand; -+ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS); -+ } -+} -+ -+ -+void -+ice_select_incoming_turn(pj_ice_sess *ice, pj_ice_sess_checklist *clist, unsigned check_id) -+{ -+ check_set_state(ice, &clist->checks[check_id], PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, PJ_SUCCESS); -+ update_comp_check(ice, clist->checks[check_id].lcand->comp_id, &clist->checks[check_id]); -+ on_check_complete(ice, &clist->checks[check_id]); -+} - - /* This callback is called when outgoing STUN request completed */ - static void on_stun_request_complete(pj_stun_session *stun_sess, -@@ -2563,7 +2914,8 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, - &check->lcand->base_addr, - &check->lcand->base_addr, - pj_sockaddr_get_len(&xaddr->sockaddr), -- &cand_id); -+ &cand_id, -+ check->rcand->transport == PJ_CAND_UDP ? PJ_CAND_UDP : PJ_CAND_TCP_PASSIVE); - if (status != PJ_SUCCESS) { - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, - status); -@@ -2678,8 +3030,8 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, - pj_grp_lock_acquire(ice->grp_lock); - - if (ice->is_destroying) { -- pj_grp_lock_release(ice->grp_lock); -- return PJ_EINVALIDOP; -+ pj_grp_lock_release(ice->grp_lock); -+ return PJ_EINVALIDOP; - } - - /* -@@ -2694,9 +3046,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, - prio_attr = (pj_stun_priority_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0); - if (prio_attr == NULL) { -- LOG5((ice->obj_name, "Received Binding request with no PRIORITY")); -- pj_grp_lock_release(ice->grp_lock); -- return PJ_SUCCESS; -+ LOG5((ice->obj_name, "Received Binding request with no PRIORITY")); -+ pj_grp_lock_release(ice->grp_lock); -+ return PJ_SUCCESS; - } - - /* Get USE-CANDIDATE attribute */ -@@ -2741,7 +3093,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, - NULL, token, PJ_TRUE, - src_addr, src_addr_len); - pj_grp_lock_release(ice->grp_lock); -- return PJ_SUCCESS; -+ return PJ_SUCCESS; - } - - } else if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED && -@@ -2753,7 +3105,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, - NULL, token, PJ_TRUE, - src_addr, src_addr_len); - pj_grp_lock_release(ice->grp_lock); -- return PJ_SUCCESS; -+ return PJ_SUCCESS; - } else { - /* Switch role to controlled */ - LOG4((ice->obj_name, -@@ -2768,7 +3120,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, - status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata); - if (status != PJ_SUCCESS) { - pj_grp_lock_release(ice->grp_lock); -- return status; -+ return status; - } - - if (((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6()) { -@@ -2825,9 +3177,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, - msg_data->has_req_data = PJ_FALSE; - - /* Send the response */ -- status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE, -- src_addr, src_addr_len, tdata); -- -+ status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, -+ pj_stun_session_tp_type(sess) == PJ_STUN_TP_UDP, -+ src_addr, src_addr_len, tdata); - - /* - * Handling early check. -@@ -2946,14 +3298,16 @@ static void handle_incoming_check(pj_ice_sess *ice, - /* Just get candidate with the highest priority and same transport ID - * for the specified component ID in the checklist. - */ -- for (i=0; i<ice->clist.count; ++i) { -- pj_ice_sess_check *c = &ice->clist.checks[i]; -- if (c->lcand->comp_id == rcheck->comp_id && -- c->lcand->transport_id == rcheck->transport_id) -- { -- lcand = c->lcand; -- break; -- } -+ for (i=0; i < ice->lcand_cnt; ++i) { -+ -+ pj_ice_sess_cand* lcand_tmp = &ice->lcand[i]; -+ -+ if (lcand_tmp->comp_id == rcheck->comp_id && -+ lcand_tmp->transport_id == rcheck->transport_id) -+ { -+ lcand = lcand_tmp; -+ break; -+ } - } - if (lcand == NULL) { - /* Should not happen, but just in case remote is sending a -@@ -2976,9 +3330,9 @@ static void handle_incoming_check(pj_ice_sess *ice, - * have this pair in our checklist. - */ - for (i=0; i<ice->clist.count; ++i) { -- pj_ice_sess_check *c = &ice->clist.checks[i]; -- if (c->lcand == lcand && c->rcand == rcand) -- break; -+ pj_ice_sess_check *c = &ice->clist.checks[i]; -+ if (c->lcand == lcand && c->rcand == rcand) -+ break; - } - - /* If the pair is already on the check list: -@@ -3001,7 +3355,6 @@ static void handle_incoming_check(pj_ice_sess *ice, - * Note: DO NOT overwrite nominated flag if one is already set. - */ - c->nominated = ((rcheck->use_candidate) || c->nominated); -- - if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN || - c->state == PJ_ICE_SESS_CHECK_STATE_WAITING) - { -diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c -index c7c2a5e7..7f2b6470 100644 ---- a/pjnath/src/pjnath/ice_strans.c -+++ b/pjnath/src/pjnath/ice_strans.c -@@ -69,6 +69,7 @@ enum tp_type - # define RELAY_PREF 0 - #endif - -+#define MAX_RTP_SIZE 65536 - - /* The candidate type preference when STUN candidate is used */ - static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = -@@ -102,8 +103,23 @@ static void ice_rx_data(pj_ice_sess *ice, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); -- -- -+#if PJ_HAS_TCP -+static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+ -+static pj_status_t ice_select_turn_dataconn(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+ -+static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+ -+static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id); -+#endif - /* STUN socket callbacks */ - /* Notification when incoming packet has been received. */ - static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock, -@@ -115,6 +131,7 @@ static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock, - static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock, - pj_ioqueue_op_key_t *send_key, - pj_ssize_t sent); -+static pj_bool_t turn_on_data_sent(pj_turn_sock *turn_sock, pj_ssize_t sent); - /* Notification when the status of the STUN transport has changed. */ - static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, - pj_stun_sock_op op, -@@ -130,8 +147,6 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock, - static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, - pj_turn_state_t new_state); - -- -- - /* Forward decls */ - static void ice_st_on_destroy(void *obj); - static void destroy_ice_st(pj_ice_strans *ice_st); -@@ -201,6 +216,14 @@ struct pj_ice_strans - - pj_bool_t destroy_req;/**< Destroy has been called? */ - pj_bool_t cb_called; /**< Init error callback called?*/ -+ -+ pj_bool_t is_pending; -+ -+ pj_uint8_t rtp_pkt[MAX_RTP_SIZE]; -+ -+ pj_uint8_t rx_buffer[MAX_RTP_SIZE]; -+ pj_uint16_t rx_buffer_size; -+ pj_uint16_t rx_wanted_size; - }; - - -@@ -237,6 +260,7 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) - pj_bzero(cfg, sizeof(*cfg)); - - cfg->af = pj_AF_INET(); -+ cfg->protocol = PJ_ICE_TP_UDP; - pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL); - pj_ice_strans_stun_cfg_default(&cfg->stun); - pj_ice_strans_turn_cfg_default(&cfg->turn); -@@ -252,6 +276,7 @@ PJ_DEF(void) pj_ice_strans_stun_cfg_default(pj_ice_strans_stun_cfg *cfg) - pj_bzero(cfg, sizeof(*cfg)); - - cfg->af = pj_AF_INET(); -+ cfg->conn_type = PJ_TURN_TP_UDP; - cfg->port = PJ_STUN_PORT; - cfg->max_host_cands = 64; - cfg->ignore_stun_error = PJ_FALSE; -@@ -389,6 +414,7 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, - cand->local_pref = RELAY_PREF; - cand->transport_id = tp_id; - cand->comp_id = (pj_uint8_t) comp->comp_id; -+ cand->transport = turn_cfg->conn_type == PJ_TURN_TP_UDP ? PJ_CAND_UDP : PJ_CAND_TCP_PASSIVE; - } - - /* Allocate and initialize TURN socket data */ -@@ -399,6 +425,10 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, - /* Commit the relayed candidate. */ - comp->cand_cnt++; - -+ if (turn_cfg->conn_type == PJ_TURN_TP_TCP) { -+ turn_cfg->alloc_param.peer_conn_type = PJ_TURN_TP_TCP; -+ } -+ - /* Create the TURN transport */ - status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, turn_cfg->af, - turn_cfg->conn_type, -@@ -447,6 +477,7 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, - || lcand->transport_id != rcand->transport_id - || lcand->local_pref != rcand->local_pref - || lcand->prio != rcand->prio -+ || lcand->transport != rcand->transport - || pj_sockaddr_cmp(&lcand->addr, &rcand->addr) != 0 - || pj_sockaddr_cmp(&lcand->base_addr, &rcand->base_addr) != 0) - { -@@ -456,6 +487,115 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, - return PJ_TRUE; - } - -+static pj_bool_t -+add_local_candidate(pj_ice_sess_cand *cand, unsigned idx, unsigned i, -+ pj_stun_sock_info stun_sock_info, pj_ice_strans *ice_st, -+ pj_ice_strans_comp *comp, pj_ice_cand_transport transport) -+{ -+ pj_ice_strans_stun_cfg *stun_cfg = &ice_st->cfg.stun_tp[idx]; -+ unsigned j; -+ pj_bool_t cand_duplicate = PJ_FALSE; -+ char addrinfo[PJ_INET6_ADDRSTRLEN+10]; -+ const pj_sockaddr *addr = &stun_sock_info.aliases[i]; -+ -+ /* Leave one candidate for relay */ -+ if (comp->cand_cnt >= PJ_ICE_ST_MAX_CAND-1) { -+ PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); -+ return PJ_FALSE; -+ } -+ -+ /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ -+ if (stun_cfg->loop_addr==PJ_FALSE) { -+ if (stun_cfg->af == pj_AF_INET() && -+ (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) -+ { -+ return PJ_TRUE; -+ } -+ else if (stun_cfg->af == pj_AF_INET6()) { -+ pj_in6_addr in6addr = {{0}}; -+ in6addr.s6_addr[15] = 1; -+ if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, -+ sizeof(in6addr))==0) -+ { -+ return PJ_TRUE; -+ } -+ } -+ } -+ pj_sockaddr_print(addr, addrinfo, sizeof(addrinfo), 3); -+ -+ /* Ignore IPv6 link-local address */ -+ if (stun_cfg->af == pj_AF_INET6()) { -+ const pj_in6_addr *a = &addr->ipv6.sin6_addr; -+ if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) -+ return PJ_TRUE; -+ } -+ -+ cand = &comp->cand_list[comp->cand_cnt]; -+ -+ cand->type = PJ_ICE_CAND_TYPE_HOST; -+ cand->status = PJ_SUCCESS; -+ cand->local_pref = HOST_PREF; -+ cand->transport_id = CREATE_TP_ID(TP_STUN, idx); -+ cand->comp_id = (pj_uint8_t) comp->comp_id; -+ cand->transport = transport; -+ -+ char addstr[PJ_INET6_ADDRSTRLEN+10]; -+ pj_sockaddr_print(addr, addstr, -+ sizeof(addstr), 3); -+ pj_sockaddr_cp(&cand->addr, addr); -+ pj_sockaddr_cp(&cand->base_addr, addr); -+ pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); -+ -+ /* Check if not already in list */ -+ for (j=0; j<comp->cand_cnt; j++) { -+ if (ice_cand_equals(cand, &comp->cand_list[j])) { -+ cand_duplicate = PJ_TRUE; -+ return PJ_FALSE; -+ } -+ } -+ -+ if (cand_duplicate) { -+ PJ_LOG(4, (ice_st->obj_name, -+ "Comp %d: host candidate %s (tpid=%d) is a duplicate", -+ comp->comp_id, pj_sockaddr_print(&cand->addr, addrinfo, -+ sizeof(addrinfo), 3), cand->transport_id)); -+ -+ pj_bzero(&cand->addr, sizeof(cand->addr)); -+ pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); -+ return PJ_TRUE; -+ } else { -+ comp->cand_cnt+=1; -+ } -+ -+ pj_ice_calc_foundation(ice_st->pool, &cand->foundation, -+ cand->type, &cand->base_addr); -+ -+ /* Set default candidate with the preferred default -+ * address family -+ */ -+ if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && -+ addr->addr.sa_family == comp->ice_st->cfg.af && -+ comp->cand_list[comp->default_cand].base_addr.addr.sa_family != -+ ice_st->cfg.af) -+ { -+ comp->default_cand = (unsigned)(cand - comp->cand_list); -+ } -+ -+ if (transport == PJ_CAND_TCP_ACTIVE) { -+ // Use the port 9 (DISCARD Protocol) for TCP active candidates. -+ pj_sockaddr_set_port(&cand->addr, 9); -+ } -+ -+ PJ_LOG(4,(ice_st->obj_name, -+ "Comp %d/%d: host candidate %s (tpid=%d) added", -+ comp->comp_id, comp->cand_cnt-1, -+ pj_sockaddr_print(&cand->addr, addrinfo, -+ sizeof(addrinfo), 3), -+ cand->transport_id)); -+ return PJ_TRUE; -+} -+ -+ - - static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, - pj_ice_strans_comp *comp, -@@ -504,6 +644,7 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, - cand->local_pref = SRFLX_PREF; - cand->transport_id = CREATE_TP_ID(TP_STUN, idx); - cand->comp_id = (pj_uint8_t) comp->comp_id; -+ cand->transport = stun_cfg->conn_type == PJ_STUN_TP_UDP ? PJ_CAND_UDP : PJ_CAND_TCP_PASSIVE; - - /* Allocate and initialize STUN socket data */ - data = PJ_POOL_ZALLOC_T(ice_st->pool, sock_user_data); -@@ -511,11 +652,12 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, - data->transport_id = cand->transport_id; - - /* Create the STUN transport */ -- status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, -- stun_cfg->af, &stun_sock_cb, -- sock_cfg, data, &comp->stun[idx].sock); -- if (status != PJ_SUCCESS) -- return status; -+ status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, stun_cfg->af, -+ stun_cfg->conn_type, &stun_sock_cb, sock_cfg, -+ data, &comp->stun[idx].sock); -+ if (status != PJ_SUCCESS) { -+ return status; -+ } - - /* Start STUN Binding resolution and add srflx candidate only if server - * is set. When any error occur during STUN Binding resolution, let's -@@ -581,116 +723,43 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, - break; - } - -+ - /* Add local addresses to host candidates, unless max_host_cands - * is set to zero. - */ - if (stun_cfg->max_host_cands) { -- pj_stun_sock_info stun_sock_info; -- unsigned i, cand_cnt = 0; -- -- /* Enumerate addresses */ -- status = pj_stun_sock_get_info(comp->stun[idx].sock, &stun_sock_info); -- if (status != PJ_SUCCESS) { -- PJ_PERROR(4,(ice_st->obj_name, status, -- "Failed in querying STUN socket info")); -- return status; -- } -- -- for (i = 0; i < stun_sock_info.alias_cnt && -- cand_cnt < stun_cfg->max_host_cands; ++i) -- { -- unsigned j; -- pj_bool_t cand_duplicate = PJ_FALSE; -- char addrinfo[PJ_INET6_ADDRSTRLEN+10]; -- const pj_sockaddr *addr = &stun_sock_info.aliases[i]; -- -- /* Leave one candidate for relay */ -- if (comp->cand_cnt >= PJ_ICE_ST_MAX_CAND-1) { -- PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); -- break; -- } -- -- /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ -- if (stun_cfg->loop_addr==PJ_FALSE) { -- if (stun_cfg->af == pj_AF_INET() && -- (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) -- { -- continue; -- } -- else if (stun_cfg->af == pj_AF_INET6()) { -- pj_in6_addr in6addr = {{0}}; -- in6addr.s6_addr[15] = 1; -- if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, -- sizeof(in6addr))==0) -- { -- continue; -- } -- } -- } -- -- /* Ignore IPv6 link-local address, unless it is the default -- * address (first alias). -- */ -- if (stun_cfg->af == pj_AF_INET6() && i != 0) { -- const pj_in6_addr *a = &addr->ipv6.sin6_addr; -- if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) -- continue; -- } -- -- cand = &comp->cand_list[comp->cand_cnt]; -- -- cand->type = PJ_ICE_CAND_TYPE_HOST; -- cand->status = PJ_SUCCESS; -- cand->local_pref = HOST_PREF; -- cand->transport_id = CREATE_TP_ID(TP_STUN, idx); -- cand->comp_id = (pj_uint8_t) comp->comp_id; -- pj_sockaddr_cp(&cand->addr, addr); -- pj_sockaddr_cp(&cand->base_addr, addr); -- pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); -- -- /* Check if not already in list */ -- for (j=0; j<comp->cand_cnt; j++) { -- if (ice_cand_equals(cand, &comp->cand_list[j])) { -- cand_duplicate = PJ_TRUE; -- break; -- } -- } -- -- if (cand_duplicate) { -- PJ_LOG(4, (ice_st->obj_name, -- "Comp %d: host candidate %s (tpid=%d) is a duplicate", -- comp->comp_id, pj_sockaddr_print(&cand->addr, addrinfo, -- sizeof(addrinfo), 3), cand->transport_id)); -- -- pj_bzero(&cand->addr, sizeof(cand->addr)); -- pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); -- continue; -- } else { -- comp->cand_cnt+=1; -- cand_cnt++; -- } -- -- pj_ice_calc_foundation(ice_st->pool, &cand->foundation, -- cand->type, &cand->base_addr); -- -- /* Set default candidate with the preferred default -- * address family -- */ -- if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && -- addr->addr.sa_family == comp->ice_st->cfg.af && -- comp->cand_list[comp->default_cand].base_addr.addr.sa_family != -- ice_st->cfg.af) -- { -- comp->default_cand = (unsigned)(cand - comp->cand_list); -- } -+ pj_stun_sock_info stun_sock_info; -+ unsigned i = 0; -+ pj_bool_t add_tcp_active_cand; -+ /* Enumerate addresses */ -+ status = pj_stun_sock_get_info(comp->stun[idx].sock, &stun_sock_info); -+ if (status != PJ_SUCCESS) { -+ PJ_PERROR(4,(ice_st->obj_name, status, -+ "Failed in querying STUN socket info")); -+ return status; -+ } - -- PJ_LOG(4,(ice_st->obj_name, -- "Comp %d/%d: host candidate %s (tpid=%d) added", -- comp->comp_id, comp->cand_cnt-1, -- pj_sockaddr_print(&cand->addr, addrinfo, -- sizeof(addrinfo), 3), -- cand->transport_id)); -- } -+ add_tcp_active_cand = stun_sock_info.conn_type != PJ_STUN_TP_UDP; -+ for (i = 0; i < stun_sock_info.alias_cnt && i < stun_cfg->max_host_cands; ++i) { -+ if (!add_tcp_active_cand) { -+ add_local_candidate(cand, idx, i, stun_sock_info, ice_st, comp, -+ PJ_CAND_UDP); -+ } else { -+ add_local_candidate(cand, idx, i, stun_sock_info, ice_st, comp, -+ PJ_CAND_TCP_PASSIVE); -+ /** RFC 6544, Section 4.1: -+ * First, agents SHOULD obtain host candidates as described in -+ * Section 5.1. Then, each agent SHOULD "obtain" (allocate a -+ * placeholder for) an active host candidate for each component of -+ * each TCP-capable media stream on each interface that the host -+ * has. The agent does not yet have to actually allocate a port for -+ * these candidates, but they are used for the creation of the check -+ * lists. -+ */ -+ add_local_candidate(cand, idx, i, stun_sock_info, ice_st, comp, -+ PJ_CAND_TCP_ACTIVE); -+ } -+ } - } - - return status; -@@ -803,6 +872,13 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name, - return status; - } - -+ ice_st->is_pending = PJ_FALSE; -+ if (status != PJ_SUCCESS) { -+ pj_pool_release(pool); -+ pj_log_pop_indent(); -+ return status; -+ } -+ - pj_grp_lock_add_ref(ice_st->grp_lock); - pj_grp_lock_add_handler(ice_st->grp_lock, pool, ice_st, - &ice_st_on_destroy); -@@ -1097,6 +1173,12 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, - ice_cb.on_ice_complete = &on_ice_complete; - ice_cb.on_rx_data = &ice_rx_data; - ice_cb.on_tx_pkt = &ice_tx_pkt; -+#if PJ_HAS_TCP -+ ice_cb.wait_tcp_connection = &ice_wait_tcp_connection; -+ ice_cb.select_turn_dataconn = &ice_select_turn_dataconn; -+ ice_cb.reconnect_tcp_connection = &ice_reconnect_tcp_connection; -+ ice_cb.close_tcp_connection = &ice_close_tcp_connection; -+#endif - - /* Create! */ - status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role, -@@ -1172,7 +1254,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, - &cand->foundation, &cand->addr, - &cand->base_addr, &cand->rel_addr, - pj_sockaddr_get_len(&cand->addr), -- (unsigned*)&ice_cand_id); -+ (unsigned*)&ice_cand_id, cand->transport); - if (status != PJ_SUCCESS) - goto on_error; - } -@@ -1468,110 +1550,154 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, - const pj_sockaddr_t *dst_addr, - int dst_addr_len) - { -- pj_ice_strans_comp *comp; -- pj_ice_sess_cand *def_cand; -- pj_status_t status; -- -- PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt && -- dst_addr && dst_addr_len, PJ_EINVAL); -- -- comp = ice_st->comp[comp_id-1]; -- -- /* Check that default candidate for the component exists */ -- if (comp->default_cand >= comp->cand_cnt) -- return PJ_EINVALIDOP; -+ pj_ssize_t size; -+ pj_status_t res = pj_ice_strans_sendto2(ice_st, comp_id, data, data_len, dst_addr, dst_addr_len, &size); -+ return (res==PJ_SUCCESS||res==PJ_EPENDING) ? -+ PJ_SUCCESS : res; -+} - -- /* Protect with group lock, since this may cause race condition with -- * pj_ice_strans_stop_ice(). -- * See ticket #1877. -- */ -- pj_grp_lock_acquire(ice_st->grp_lock); -+/* -+ * Application wants to send outgoing packet. -+ */ -+PJ_DEF(pj_status_t) -+pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data, -+ pj_size_t data_len, const pj_sockaddr_t *dst_addr, -+ int dst_addr_len, pj_ssize_t* size) { -+ pj_ice_strans_comp *comp; -+ pj_ice_sess_cand *def_cand; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt && -+ dst_addr && dst_addr_len, -+ PJ_EINVAL); -+ -+ if (ice_st->is_pending) { -+ return PJ_EBUSY; -+ } -+ -+ comp = ice_st->comp[comp_id - 1]; -+ -+ /* Check that default candidate for the component exists */ -+ if (comp->default_cand >= comp->cand_cnt) -+ return PJ_EINVALIDOP; -+ -+ /* Protect with group lock, since this may cause race condition with -+ * pj_ice_strans_stop_ice(). -+ * See ticket #1877. -+ */ -+ pj_grp_lock_acquire(ice_st->grp_lock); -+ -+ /* If ICE is available, send data with ICE, otherwise send with the -+ * default candidate selected during initialization. -+ * -+ * https://trac.pjsip.org/repos/ticket/1416: -+ * Once ICE has failed, also send data with the default candidate. -+ */ -+ if (ice_st->ice && ice_st->state == PJ_ICE_STRANS_STATE_RUNNING) { -+ status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len); - -- /* If ICE is available, send data with ICE, otherwise send with the -- * default candidate selected during initialization. -- * -- * https://trac.pjsip.org/repos/ticket/1416: -- * Once ICE has failed, also send data with the default candidate. -- */ -- if (ice_st->ice && ice_st->state == PJ_ICE_STRANS_STATE_RUNNING) { -- status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len); -- -- pj_grp_lock_release(ice_st->grp_lock); -- -- return status; -- } -- - pj_grp_lock_release(ice_st->grp_lock); - -- def_cand = &comp->cand_list[comp->default_cand]; -- -- if (def_cand->status == PJ_SUCCESS) { -- unsigned tp_idx = GET_TP_IDX(def_cand->transport_id); -+ if (ice_st->is_pending) { -+ return PJ_EPENDING; -+ } - -- if (def_cand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ return status; -+ } - -- enum { -- msg_disable_ind = 0xFFFF & -- ~(PJ_STUN_SESS_LOG_TX_IND| -- PJ_STUN_SESS_LOG_RX_IND) -- }; -+ pj_grp_lock_release(ice_st->grp_lock); - -- /* https://trac.pjsip.org/repos/ticket/1316 */ -- if (comp->turn[tp_idx].sock == NULL) { -- /* TURN socket error */ -- return PJ_EINVALIDOP; -- } -+ def_cand = &comp->cand_list[comp->default_cand]; - -- if (!comp->turn[tp_idx].log_off) { -- /* Disable logging for Send/Data indications */ -- PJ_LOG(5,(ice_st->obj_name, -- "Disabling STUN Indication logging for " -- "component %d", comp->comp_id)); -- pj_turn_sock_set_log(comp->turn[tp_idx].sock, -- msg_disable_ind); -- comp->turn[tp_idx].log_off = PJ_TRUE; -- } -+ pj_bool_t add_header = def_cand->transport != PJ_CAND_UDP; -+ pj_uint8_t* final_pkt = data; -+ unsigned final_len = data_len; -+ -+ if (add_header) { -+ // TCP -+ /* -+ * RFC6544 ICE requires an agent to demultiplex STUN and -+ * application-layer traffic, since they appear on the same port. This -+ * demultiplexing is described in [RFC5245] and is done using the magic -+ * cookie and other fields of the message. Stream-oriented transports -+ * introduce another wrinkle, since they require a way to frame the -+ * connection so that the application and STUN packets can be extracted -+ * in order to differentiate STUN packets from application-layer -+ * traffic. For this reason, TCP media streams utilizing ICE use the -+ * basic framing provided in RFC 4571 [RFC4571], even if the application -+ * layer protocol is not RTP. -+ */ -+ pj_uint8_t header_1 = data_len % 256; -+ pj_uint8_t header_0 = data_len >> 8; -+ final_len = 2 + data_len; -+ memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); -+ memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); -+ memcpy(&ice_st->rtp_pkt[2], (unsigned char *)data, data_len); -+ final_pkt = &ice_st->rtp_pkt; -+ } - -- status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, -- (const pj_uint8_t*)data, -- (unsigned)data_len, -- dst_addr, dst_addr_len); -- return (status==PJ_SUCCESS||status==PJ_EPENDING) ? -- PJ_SUCCESS : status; -- } else { -- const pj_sockaddr_t *dest_addr; -- unsigned dest_addr_len; -- -- if (comp->ipv4_mapped) { -- if (comp->synth_addr_len == 0 || -- pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0) -- { -- status = pj_sockaddr_synthesize(pj_AF_INET6(), -- &comp->synth_addr, -- dst_addr); -- if (status != PJ_SUCCESS) -- return status; -- -- pj_sockaddr_cp(&comp->dst_addr, dst_addr); -- comp->synth_addr_len = pj_sockaddr_get_len( -- &comp->synth_addr); -- } -- dest_addr = &comp->synth_addr; -- dest_addr_len = comp->synth_addr_len; -- } else { -- dest_addr = dst_addr; -- dest_addr_len = dst_addr_len; -- } -+ if (def_cand->status == PJ_SUCCESS) { -+ unsigned tp_idx = GET_TP_IDX(def_cand->transport_id); -+ -+ if (def_cand->type == PJ_ICE_CAND_TYPE_RELAYED) { -+ -+ enum { -+ msg_disable_ind = -+ 0xFFFF & ~(PJ_STUN_SESS_LOG_TX_IND | PJ_STUN_SESS_LOG_RX_IND) -+ }; -+ -+ /* https://trac.pjsip.org/repos/ticket/1316 */ -+ if (comp->turn[tp_idx].sock == NULL) { -+ /* TURN socket error */ -+ return PJ_EINVALIDOP; -+ } -+ -+ if (!comp->turn[tp_idx].log_off) { -+ /* Disable logging for Send/Data indications */ -+ PJ_LOG(5, (ice_st->obj_name, -+ "Disabling STUN Indication logging for " -+ "component %d", -+ comp->comp_id)); -+ pj_turn_sock_set_log(comp->turn[tp_idx].sock, msg_disable_ind); -+ comp->turn[tp_idx].log_off = PJ_TRUE; -+ } -+ -+ status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, final_pkt, -+ final_len, dst_addr, dst_addr_len); -+ ice_st->is_pending = ((status == PJ_EPENDING) && ice_st); -+ } else { -+ const pj_sockaddr_t *dest_addr; -+ unsigned dest_addr_len; -+ -+ if (comp->ipv4_mapped) { -+ if (comp->synth_addr_len == 0 || -+ pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0) { -+ status = pj_sockaddr_synthesize(pj_AF_INET6(), &comp->synth_addr, -+ dst_addr); -+ if (status != PJ_SUCCESS) -+ return status; -+ -+ pj_sockaddr_cp(&comp->dst_addr, dst_addr); -+ comp->synth_addr_len = pj_sockaddr_get_len(&comp->synth_addr); -+ } -+ dest_addr = &comp->synth_addr; -+ dest_addr_len = comp->synth_addr_len; -+ } else { -+ dest_addr = dst_addr; -+ dest_addr_len = dst_addr_len; -+ } - -- status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, data, -- (unsigned)data_len, 0, dest_addr, -- dest_addr_len); -- return (status==PJ_SUCCESS||status==PJ_EPENDING) ? -- PJ_SUCCESS : status; -- } -+ status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, final_pkt, -+ final_len, 0, dest_addr, dest_addr_len, size); -+ -+ if (add_header) *size -= sizeof(pj_uint16_t); // Do not count the header -+ ice_st->is_pending = ((status == PJ_EPENDING || *size != data_len) && ice_st); -+ } - -- } else -- return PJ_EINVALIDOP; -+ return status; -+ -+ } else -+ return PJ_EINVALIDOP; - } - - /* -@@ -1623,7 +1749,15 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) - sizeof(lip), 3); - pj_sockaddr_print(&check->rcand->addr, rip, - sizeof(rip), 3); -- -+#if PJ_HAS_TCP -+ int idx = -1; -+ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) { -+ if (ice_st->cfg.stun_tp[i].af == check->rcand->addr.addr.sa_family) { -+ idx = i; -+ break; -+ } -+ } -+#endif - if (tp_typ == TP_TURN) { - /* Activate channel binding for the remote address - * for more efficient data transfer using TURN. -@@ -1691,24 +1825,55 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, - - PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL); - -+ if (ice_st->is_pending) { -+ return PJ_EBUSY; -+ } -+ - comp = ice_st->comp[comp_id-1]; - - TRACE_PKT((comp->ice_st->obj_name, - "Component %d TX packet to %s:%d with transport %d", - comp_id, -- pj_sockaddr_print(dst_addr, daddr, sizeof(addr), 2), -+ pj_sockaddr_print(dst_addr, daddr, sizeof(daddr), 2), - pj_sockaddr_get_port(dst_addr), - tp_typ)); - -- if (tp_typ == TP_TURN) { -- if (comp->turn[tp_idx].sock) { -- status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, -- (const pj_uint8_t*)pkt, -- (unsigned)size, -- dst_addr, dst_addr_len); -- } else { -- status = PJ_EINVALIDOP; -+ pj_bool_t add_header = comp->ice_st->cfg.stun_tp->conn_type == PJ_STUN_TP_TCP; -+ pj_uint8_t* final_pkt = pkt; -+ unsigned final_len = size; -+ pj_ssize_t sent_size; -+ -+ if (add_header) { -+ // TCP -+ /* -+ * RFC6544 ICE requires an agent to demultiplex STUN and -+ * application-layer traffic, since they appear on the same port. This -+ * demultiplexing is described in [RFC5245] and is done using the magic -+ * cookie and other fields of the message. Stream-oriented transports -+ * introduce another wrinkle, since they require a way to frame the -+ * connection so that the application and STUN packets can be extracted -+ * in order to differentiate STUN packets from application-layer -+ * traffic. For this reason, TCP media streams utilizing ICE use the -+ * basic framing provided in RFC 4571 [RFC4571], even if the application -+ * layer protocol is not RTP. -+ */ -+ pj_uint8_t header_1 = size % 256; -+ pj_uint8_t header_0 = size >> 8; -+ final_len = 2 + size; -+ memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); -+ memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); -+ memcpy(&ice_st->rtp_pkt[2], (unsigned char *)pkt, size); -+ final_pkt = &ice_st->rtp_pkt; - } -+ -+ if (tp_typ == TP_TURN) { -+ if (comp->turn[tp_idx].sock) { -+ status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, -+ final_pkt, final_len, dst_addr, dst_addr_len); -+ ice_st->is_pending = status == PJ_EPENDING; -+ } else { -+ status = PJ_EINVALIDOP; -+ } - } else if (tp_typ == TP_STUN) { - const pj_sockaddr_t *dest_addr; - unsigned dest_addr_len; -@@ -1732,12 +1897,16 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, - dest_addr_len = dst_addr_len; - } - -- status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, -- pkt, (unsigned)size, 0, -- dest_addr, dest_addr_len); -+ if (comp->stun[tp_idx].sock) { -+ status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, final_pkt, -+ final_len, 0, dest_addr, dest_addr_len, &sent_size); -+ ice_st->is_pending = (status == PJ_EPENDING || sent_size != final_len); -+ } else { -+ status = PJ_EINVALIDOP; -+ } - } else { - pj_assert(!"Invalid transport ID"); -- status = PJ_EINVALIDOP; -+ status = PJ_EINVALIDOP; - } - - return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; -@@ -1763,22 +1932,241 @@ static void ice_rx_data(pj_ice_sess *ice, - } - } - -+static void on_peer_connection(pj_stun_session* sess, pj_status_t status, pj_sockaddr_t* remote_addr) { -+ -+ sock_user_data *data; -+ pj_ice_strans_comp *comp; -+ pj_ice_strans *ice_st; -+ pj_stun_sock* stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); -+ if (!stun_sock) { -+ /* We have disassociated ourselves from the STUN session */ -+ return; -+ } -+ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); -+ if (!data) { -+ /* We have disassociated ourselves from the STUN socket */ -+ return; -+ } -+ -+ comp = data->comp; -+ ice_st = comp->ice_st; -+ if (!ice_st || !ice_st->ice) { -+ // Incorrect ICE -+ return; -+ } -+ -+ ice_st->is_pending = PJ_FALSE; -+ ice_sess_on_peer_connection(ice_st->ice, data->transport_id, status, remote_addr); -+} -+ -+static void on_peer_reset_connection(pj_stun_session* sess, pj_sockaddr_t* remote_addr) { -+ -+ sock_user_data *data; -+ pj_ice_strans_comp *comp; -+ pj_ice_strans *ice_st; -+ pj_stun_sock* stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); -+ if (!stun_sock) { -+ /* We have disassociated ourselves from the STUN session */ -+ return; -+ } -+ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); -+ if (!data) { -+ /* We have disassociated ourselves from the STUN socket */ -+ return; -+ } -+ -+ comp = data->comp; -+ ice_st = comp->ice_st; -+ if (!ice_st || !ice_st->ice) { -+ // Incorrect ICE -+ return; -+ } -+ -+ ice_sess_on_peer_reset_connection(ice_st->ice, data->transport_id, remote_addr); -+} -+ -+static void on_peer_packet(pj_stun_session* sess, pj_sockaddr_t* remote_addr) { -+ -+ if (!sess || !remote_addr) return; -+ sock_user_data *data; -+ pj_ice_strans_comp *comp; -+ pj_ice_strans *ice_st; -+ pj_stun_sock* stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); -+ if (!stun_sock) { -+ /* We have disassociated ourselves from the STUN session */ -+ return; -+ } -+ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); -+ if (!data) { -+ /* We have disassociated ourselves from the STUN socket */ -+ return; -+ } -+ -+ comp = data->comp; -+ if (!comp) return; -+ ice_st = comp->ice_st; -+ if (!ice_st || !ice_st->ice) { -+ // Incorrect ICE -+ return; -+ } -+ -+ ice_sess_on_peer_packet(ice_st->ice, data->transport_id, remote_addr); -+} -+ -+#if PJ_HAS_TCP -+static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id) { -+ pj_ice_sess_check *check; -+ check = &clist->checks[check_id]; -+ const pj_ice_sess_cand *lcand; -+ lcand = check->lcand; -+ const pj_ice_sess_cand *rcand; -+ rcand = check->rcand; -+ -+ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; -+ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; -+ -+ int idx = -1; -+ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) { -+ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { -+ idx = i; -+ break; -+ } -+ } -+ if (idx == -1) { -+ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", -+ st_comp->comp_id)); -+ return PJ_EINVAL; -+ } -+ if (st_comp->stun[idx].sock) { -+ pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); -+ if (!sess) { -+ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", -+ st_comp->comp_id)); -+ return PJ_EINVAL; -+ } -+ pj_stun_session_callback(sess)->on_peer_connection = &on_peer_connection; -+ pj_stun_session_callback(sess)->on_peer_reset_connection = &on_peer_reset_connection; -+ pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; -+ return pj_stun_sock_connect_active(st_comp->stun[idx].sock, &rcand->addr, -+ rcand->addr.addr.sa_family); -+ } -+ -+ return PJ_EINVAL; -+} -+ -+static pj_status_t ice_select_turn_dataconn(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id) { -+ pj_ice_sess_check *check; -+ check = &clist->checks[check_id]; -+ const pj_ice_sess_cand *lcand; -+ lcand = check->lcand; -+ const pj_ice_sess_cand *rcand; -+ rcand = check->rcand; -+ -+ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; -+ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; -+ -+ for (int i=0; i<ice_st->cfg.turn_tp_cnt; ++i) { -+ pj_turn_session_info info; -+ pj_turn_sock_get_info(st_comp->turn[i].sock, &info); -+ if (st_comp->turn[i].sock -+ && pj_turn_sock_has_dataconn(st_comp->turn[i].sock, &rcand->addr)) { -+ ice_select_incoming_turn(ice, clist, check_id); -+ return PJ_SUCCESS; // Already connected via TURN -+ } -+ } -+ -+ return PJ_EINVAL; -+} -+ -+static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id) { -+ pj_ice_sess_check *check; -+ check = &clist->checks[check_id]; -+ const pj_ice_sess_cand *lcand; -+ lcand = check->lcand; -+ const pj_ice_sess_cand *rcand; -+ rcand = check->rcand; -+ -+ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; -+ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; -+ -+ int idx = -1; -+ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) { -+ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { -+ idx = i; -+ break; -+ } -+ } -+ if (idx == -1) { -+ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", -+ st_comp->comp_id)); -+ return PJ_EINVAL; -+ } -+ if (st_comp->stun[idx].sock) { -+ pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); -+ if (!sess) { -+ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", -+ st_comp->comp_id)); -+ return PJ_EINVAL; -+ } -+ pj_stun_session_callback(sess)->on_peer_connection = &on_peer_connection; -+ pj_stun_session_callback(sess)->on_peer_reset_connection = &on_peer_reset_connection; -+ pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; -+ return pj_stun_sock_reconnect_active(st_comp->stun[idx].sock, &rcand->addr, -+ rcand->addr.addr.sa_family); -+ } -+ -+ return PJ_EINVAL; -+} -+ -+static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, -+ pj_ice_sess_checklist *clist, -+ unsigned check_id) { -+ pj_ice_sess_check *check; -+ check = &clist->checks[check_id]; -+ const pj_ice_sess_cand *lcand; -+ lcand = check->lcand; -+ const pj_ice_sess_cand *rcand; -+ rcand = check->rcand; -+ -+ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; -+ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; -+ -+ int idx = -1; -+ for (int i=0; i<ice_st->cfg.stun_tp_cnt; ++i) { -+ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { -+ idx = i; -+ break; -+ } -+ } -+ if (idx != -1 && st_comp->stun[idx].sock) { -+ const pj_ice_sess_cand *rcand = check->rcand; -+ ice_st->is_pending = PJ_FALSE; -+ return pj_stun_sock_close(st_comp->stun[idx].sock, &rcand->addr); -+ } -+ -+ return PJ_EINVAL; -+} -+#endif -+ - /* Notification when incoming packet has been received from - * the STUN socket. - */ --static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock, -- void *pkt, -- unsigned pkt_len, -- const pj_sockaddr_t *src_addr, -- unsigned addr_len) --{ -- sock_user_data *data; -- pj_ice_strans_comp *comp; -- pj_ice_strans *ice_st; -- pj_status_t status; -- -- data = (sock_user_data*) pj_stun_sock_get_user_data(stun_sock); -- if (data == NULL) { -+ static pj_bool_t stun_on_rx_data( -+ pj_stun_sock * stun_sock, void *pkt, unsigned pkt_len, -+ const pj_sockaddr_t *src_addr, unsigned addr_len) { -+ sock_user_data *data; -+ pj_ice_strans_comp *comp; -+ pj_ice_strans *ice_st; -+ pj_status_t status; -+ -+ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); -+ if (data == NULL) { - /* We have disassociated ourselves from the STUN socket */ - return PJ_FALSE; - } -@@ -1822,9 +2210,34 @@ static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock, - pj_ioqueue_op_key_t *send_key, - pj_ssize_t sent) - { -- PJ_UNUSED_ARG(stun_sock); -- PJ_UNUSED_ARG(send_key); -- PJ_UNUSED_ARG(sent); -+ sock_user_data *data; -+ pj_ice_strans_comp *comp; -+ pj_ice_strans *ice_st; -+ -+ data = (sock_user_data*) pj_stun_sock_get_user_data(stun_sock); -+ comp = data->comp; -+ ice_st = comp->ice_st; -+ ice_st->is_pending = PJ_FALSE; -+ if (ice_st->cb.on_data_sent) { -+ (*ice_st->cb.on_data_sent)(ice_st, comp->comp_id, sent); -+ } -+ return PJ_TRUE; -+} -+ -+static pj_bool_t turn_on_data_sent(pj_turn_sock *turn_sock, pj_ssize_t sent) -+{ -+ sock_user_data *data; -+ pj_ice_strans_comp *comp; -+ pj_ice_strans *ice_st; -+ -+ data = (sock_user_data*) pj_turn_sock_get_user_data(turn_sock); -+ comp = data->comp; -+ ice_st = comp->ice_st; -+ ice_st->is_pending = PJ_FALSE; -+ -+ if (ice_st->cb.on_data_sent) { -+ (*ice_st->cb.on_data_sent)(ice_st, comp->comp_id, sent); -+ } - return PJ_TRUE; - } - -@@ -2029,6 +2442,10 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, - } - } - break; -+ case PJ_STUN_SESS_DESTROYED: -+ case PJ_STUN_TCP_CONNECT_ERROR: -+ default: -+ break; - } - - return pj_grp_lock_dec_ref(ice_st->grp_lock)? PJ_FALSE : PJ_TRUE; -@@ -2037,7 +2454,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, - /* Callback when TURN socket has received a packet */ - static void turn_on_rx_data(pj_turn_sock *turn_sock, - void *pkt, -- unsigned pkt_len, -+ unsigned size, - const pj_sockaddr_t *peer_addr, - unsigned addr_len) - { -@@ -2063,20 +2480,90 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock, - */ - if (comp->ice_st->cb.on_rx_data) { - (*comp->ice_st->cb.on_rx_data)(comp->ice_st, comp->comp_id, pkt, -- pkt_len, peer_addr, addr_len); -+ size, peer_addr, addr_len); - } - - } else { - - /* Hand over the packet to ICE */ -- status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, -- data->transport_id, pkt, pkt_len, -- peer_addr, addr_len); -- -- if (status != PJ_SUCCESS) { -- ice_st_perror(comp->ice_st, -- "Error processing packet from TURN relay", -- status); -+ if (comp->ice_st->cfg.turn_tp->conn_type == PJ_TURN_TP_TCP && size > 0) { -+ pj_uint16_t parsed = 0; -+ pj_status_t status; -+ -+ do { -+ pj_uint16_t pkt_len = size - parsed; -+ pj_uint8_t *current_packet = ((pj_uint8_t *)(pkt)) + parsed; -+ -+ /* RFC6544, the packet is wrapped into a packet following the RFC4571 */ -+ // cf stun_sock.c:parse_rx_packet -+ pj_bool_t store_remaining = PJ_TRUE; -+ if (comp->ice_st->rx_buffer_size != 0 || comp->ice_st->rx_wanted_size != 0) { -+ // We currently have a packet to complete -+ if (comp->ice_st->rx_buffer_size == 1) { -+ // We do not know the current size, parse it. -+ pkt_len = (((pj_uint8_t *)comp->ice_st->rx_buffer)[0] << 8) + -+ ((pj_uint8_t *)current_packet)[0]; -+ comp->ice_st->rx_buffer_size = 0; // We have eaten the temp packet. -+ current_packet = current_packet + 1; -+ parsed += 1; -+ if (pkt_len + parsed <= size) { -+ store_remaining = PJ_FALSE; -+ parsed += pkt_len; -+ } else { -+ comp->ice_st->rx_wanted_size = pkt_len; -+ } -+ } else if (pkt_len + comp->ice_st->rx_buffer_size >= comp->ice_st->rx_wanted_size) { -+ // We have enough pkt Build new packet to parse -+ store_remaining = PJ_FALSE; -+ pj_uint16_t eaten_bytes = comp->ice_st->rx_wanted_size - comp->ice_st->rx_buffer_size; -+ memcpy(comp->ice_st->rx_buffer + comp->ice_st->rx_buffer_size, -+ current_packet, eaten_bytes); -+ pkt_len = comp->ice_st->rx_wanted_size; -+ current_packet = comp->ice_st->rx_buffer; -+ parsed += eaten_bytes; -+ comp->ice_st->rx_buffer_size = 0; -+ comp->ice_st->rx_wanted_size = 0; -+ } -+ } else if (pkt_len > 1) { -+ pkt_len = (((pj_uint8_t *)current_packet)[0] << 8) + ((pj_uint8_t *)current_packet)[1]; -+ current_packet = current_packet + 2; -+ parsed += 2; -+ if (pkt_len + parsed <= size) { -+ store_remaining = PJ_FALSE; -+ parsed += pkt_len; -+ } else { -+ comp->ice_st->rx_wanted_size = pkt_len; -+ -+ } -+ } -+ if (store_remaining) { -+ pj_uint16_t stored_size = size - parsed; -+ memcpy(comp->ice_st->rx_buffer + comp->ice_st->rx_buffer_size, -+ current_packet, stored_size); -+ comp->ice_st->rx_buffer_size += stored_size; -+ status = PJ_SUCCESS; -+ break; -+ } -+ -+ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, -+ data->transport_id, current_packet, pkt_len, -+ peer_addr, addr_len); -+ -+ if (status != PJ_SUCCESS) { -+ ice_st_perror(comp->ice_st, -+ "Error processing packet from TURN relay", -+ status); -+ } -+ } while (parsed < size); -+ } else { -+ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, -+ data->transport_id, pkt, size, -+ peer_addr, addr_len); -+ if (status != PJ_SUCCESS) { -+ ice_st_perror(comp->ice_st, -+ "Error processing packet from TURN relay", -+ status); -+ } - } - } - -@@ -2293,5 +2780,4 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, - pj_grp_lock_dec_ref(comp->ice_st->grp_lock); - - pj_log_pop_indent(); --} -- -+} -\ No newline at end of file -diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c -index db0de10b..3013eeed 100644 ---- a/pjnath/src/pjnath/nat_detect.c -+++ b/pjnath/src/pjnath/nat_detect.c -@@ -329,7 +329,8 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, - sess_cb.on_request_complete = &on_request_complete; - sess_cb.on_send_msg = &on_send_msg; - status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb, -- PJ_FALSE, sess->grp_lock, &sess->stun_sess); -+ PJ_FALSE, sess->grp_lock, &sess->stun_sess, -+ PJ_STUN_TP_UDP); - if (status != PJ_SUCCESS) - goto on_error; - -@@ -874,10 +875,11 @@ static pj_status_t send_test(nat_detect_session *sess, - pj_sockaddr_get_port(sess->cur_server))); - - /* Send the request */ -- status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE, -- PJ_TRUE, sess->cur_server, -- pj_sockaddr_get_len(sess->cur_server), -- sess->result[test_id].tdata); -+ status = pj_stun_session_send_msg( -+ sess->stun_sess, NULL, PJ_TRUE, -+ (pj_stun_session_tp_type(sess->stun_sess) == PJ_STUN_TP_UDP), -+ sess->cur_server, pj_sockaddr_get_len(sess->cur_server), -+ sess->result[test_id].tdata); - if (status != PJ_SUCCESS) - goto on_error; - -diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c -index 7b53aba7..2b006d91 100644 ---- a/pjnath/src/pjnath/stun_session.c -+++ b/pjnath/src/pjnath/stun_session.c -@@ -49,6 +49,8 @@ struct pj_stun_session - - pj_stun_tx_data pending_request_list; - pj_stun_tx_data cached_response_list; -+ -+ pj_stun_tp_type conn_type; - }; - - #define SNAME(s_) ((s_)->pool->obj_name) -@@ -505,7 +507,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, - const pj_stun_session_cb *cb, - pj_bool_t fingerprint, - pj_grp_lock_t *grp_lock, -- pj_stun_session **p_sess) -+ pj_stun_session **p_sess, -+ pj_stun_tp_type conn_type) - { - pj_pool_t *pool; - pj_stun_session *sess; -@@ -526,6 +529,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, - pj_memcpy(&sess->cb, cb, sizeof(*cb)); - sess->use_fingerprint = fingerprint; - sess->log_flag = 0xFFFF; -+ sess->conn_type = conn_type; - - if (grp_lock) { - sess->grp_lock = grp_lock; -@@ -1511,3 +1515,9 @@ on_return: - return status; - } - -+PJ_DECL(pj_stun_session_cb *) pj_stun_session_callback(pj_stun_session *sess) { -+ return sess ? &sess->cb : NULL; -+} -+PJ_DECL(pj_stun_tp_type) pj_stun_session_tp_type(pj_stun_session *sess) { -+ return sess ? sess->conn_type : PJ_STUN_TP_UDP; -+} -diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c -index 7692e6c1..e7e876ce 100644 ---- a/pjnath/src/pjnath/stun_sock.c -+++ b/pjnath/src/pjnath/stun_sock.c -@@ -1,5 +1,5 @@ - /* $Id$ */ --/* -+/* - * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> - * -@@ -20,7 +20,6 @@ - #include <pjnath/stun_sock.h> - #include <pjnath/errno.h> - #include <pjnath/stun_transaction.h> --#include <pjnath/stun_session.h> - #include <pjlib-util/srv_resolver.h> - #include <pj/activesock.h> - #include <pj/addr_resolv.h> -@@ -40,52 +39,101 @@ - - enum { MAX_BIND_RETRY = 100 }; - --struct pj_stun_sock --{ -- char *obj_name; /* Log identification */ -- pj_pool_t *pool; /* Pool */ -- void *user_data; /* Application user data */ -- pj_bool_t is_destroying; /* Destroy already called */ -- int af; /* Address family */ -- pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/ -- pj_stun_sock_cb cb; /* Application callbacks */ -- -- int ka_interval; /* Keep alive interval */ -- pj_timer_entry ka_timer; /* Keep alive timer. */ -- -- pj_sockaddr srv_addr; /* Resolved server addr */ -- pj_sockaddr mapped_addr; /* Our public address */ -- -- pj_dns_srv_async_query *q; /* Pending DNS query */ -- pj_sock_t sock_fd; /* Socket descriptor */ -- pj_activesock_t *active_sock; /* Active socket object */ -- pj_ioqueue_op_key_t send_key; /* Default send key for app */ -- pj_ioqueue_op_key_t int_send_key; /* Send key for internal */ -- -- pj_uint16_t tsx_id[6]; /* .. to match STUN msg */ -- pj_stun_session *stun_sess; /* STUN session */ -- pj_grp_lock_t *grp_lock; /* Session group lock */ --}; -+#if PJ_HAS_TCP -+// The head of a RTP packet is stored in a 16 bits header, so the max size of a -+// packet is 65536 -+#define MAX_RTP_SIZE 65536 -+#endif -+ -+// TODO (sblin) The incoming socks are a bit HACKY for now. -+// Need a better approach -+typedef struct outgoing_sock { -+ pj_sock_t fd; -+ pj_activesock_t *sock; -+ pj_sockaddr_t *addr; -+} outgoing_sock; -+ -+typedef struct incoming_sock { -+ pj_sock_t fd; -+ pj_activesock_t *sock; -+ pj_sockaddr addr; -+ int addr_len; -+} incoming_sock; -+ -+typedef struct rx_buf { -+ pj_activesock_t *asock; -+ pj_uint8_t rx_buffer[MAX_RTP_SIZE]; -+ pj_uint16_t rx_buffer_size; -+ pj_uint16_t rx_wanted_size; -+ struct rx_buf* next; -+ struct rx_buf* prev; -+} rx_buf; -+ -+typedef struct pj_stun_sock { -+ char *obj_name; /* Log identification */ -+ pj_pool_t *pool; /* Pool */ -+ void *user_data; /* Application user data */ -+ pj_bool_t is_destroying; /* Destroy already called */ -+ int af; /* Address family */ -+ pj_stun_tp_type conn_type; -+ pj_stun_sock_cfg setting; -+ pj_stun_config cfg; /* STUN config (ioqueue etc) */ -+ pj_stun_sock_cb cb; /* Application callbacks */ -+ -+ int ka_interval; /* Keep alive interval */ -+ pj_timer_entry ka_timer; /* Keep alive timer. */ -+ -+ pj_sockaddr srv_addr; /* Resolved server addr */ -+ pj_sockaddr mapped_addr; /* Our public address */ -+ -+ pj_dns_srv_async_query *q; /* Pending DNS query */ -+ pj_sock_t main_sock_fd; /* Socket descriptor */ -+ pj_activesock_t *main_sock; /* Active socket object */ -+#if PJ_HAS_TCP -+ int outgoing_nb; -+ outgoing_sock outgoing_socks[PJ_ICE_MAX_CHECKS]; -+ int incoming_nb; -+ incoming_sock incoming_socks[PJ_ICE_MAX_CHECKS]; -+ rx_buf* rx_buffers; -+#endif -+ pj_ioqueue_op_key_t send_key; /* Default send key for app */ -+ pj_ioqueue_op_key_t int_send_key; /* Send key for internal */ -+ -+ pj_uint16_t tsx_id[6]; /* .. to match STUN msg */ -+ pj_stun_session *stun_sess; /* STUN session */ -+ pj_grp_lock_t *grp_lock; /* Session group lock */ -+} pj_stun_sock; - - /* - * Prototypes for static functions - */ - -+static pj_bool_t on_stun_sock_ready(pj_activesock_t *asock, pj_status_t status); -+ -+static pj_bool_t on_stun_sock_accept(pj_activesock_t *asock, pj_sock_t newsock, -+ const pj_sockaddr_t *src_addr, -+ int src_addr_len); -+ -+pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status); -+ - /* Destructor for group lock */ - static void stun_sock_destructor(void *obj); - - /* This callback is called by the STUN session to send packet */ --static pj_status_t sess_on_send_msg(pj_stun_session *sess, -+pj_status_t sess_on_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 pj_bool_t sess_fail(pj_stun_sock *stun_sock, pj_stun_sock_op op, -+ pj_status_t status); -+ - /* This callback is called by the STUN session when outgoing transaction - * is complete - */ --static void sess_on_request_complete(pj_stun_session *sess, -+void sess_on_request_complete(pj_stun_session *sess, - pj_status_t status, - void *token, - pj_stun_tx_data *tdata, -@@ -101,7 +149,7 @@ static void dns_srv_resolver_cb(void *user_data, - static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock); - - /* Callback from active socket when incoming packet is received */ --static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, -+pj_bool_t on_data_recvfrom(pj_activesock_t *asock, - void *data, - pj_size_t size, - const pj_sockaddr_t *src_addr, -@@ -109,15 +157,15 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, - pj_status_t status); - - /* Callback from active socket about send status */ --static pj_bool_t on_data_sent(pj_activesock_t *asock, -+pj_bool_t on_data_sent(pj_activesock_t *asock, - pj_ioqueue_op_key_t *send_key, - pj_ssize_t sent); - - /* Schedule keep-alive timer */ --static void start_ka_timer(pj_stun_sock *stun_sock); -+void start_ka_timer(pj_stun_sock *stun_sock); - - /* Keep-alive timer callback */ --static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); -+void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); - - #define INTERNAL_MSG_TOKEN (void*)(pj_ssize_t)1 - -@@ -160,206 +208,316 @@ static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg) - } - - /* -- * Create the STUN transport using the specified configuration. -+ * Initialize. - */ --PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, -- const char *name, -- int af, -- const pj_stun_sock_cb *cb, -- const pj_stun_sock_cfg *cfg, -- void *user_data, -- pj_stun_sock **p_stun_sock) --{ -- pj_pool_t *pool; -- pj_stun_sock *stun_sock; -- pj_stun_sock_cfg default_cfg; -- pj_sockaddr bound_addr; -- unsigned i; -- pj_uint16_t max_bind_retry; -- pj_status_t status; -+PJ_DEF(pj_status_t) pj_stun_sock_alloc(pj_stun_sock *stun_sock) { -+ pj_status_t status; -+ pj_sockaddr bound_addr; -+ pj_uint16_t max_bind_retry; -+ int sock_type; -+ -+ pj_grp_lock_acquire(stun_sock->grp_lock); -+ -+ if (stun_sock->conn_type == PJ_STUN_TP_UDP) -+ sock_type = pj_SOCK_DGRAM(); -+ else -+ sock_type = pj_SOCK_STREAM(); -+ -+ stun_sock->ka_interval = stun_sock->setting.ka_interval; -+ if (stun_sock->ka_interval == 0) -+ stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; -+ /* Create socket and bind socket */ -+ status = -+ pj_sock_socket(stun_sock->af, sock_type, 0, &stun_sock->main_sock_fd); -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } - -- PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); -- PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); -- PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); -- PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2( -+ stun_sock->main_sock_fd, stun_sock->setting.qos_type, -+ &stun_sock->setting.qos_params, 2, stun_sock->obj_name, NULL); -+ if (status != PJ_SUCCESS && !stun_sock->setting.qos_ignore_error) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } - -- status = pj_stun_config_check_valid(stun_cfg); -- if (status != PJ_SUCCESS) -- return status; -+ /* Apply socket buffer size */ -+ if (stun_sock->setting.so_rcvbuf_size > 0) { -+ unsigned sobuf_size = stun_sock->setting.so_rcvbuf_size; -+ status = pj_sock_setsockopt_sobuf(stun_sock->main_sock_fd, pj_SO_RCVBUF(), -+ PJ_TRUE, &sobuf_size); -+ if (status != PJ_SUCCESS) { -+ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_RCVBUF"); -+ } else { -+ if (sobuf_size < stun_sock->setting.so_rcvbuf_size) { -+ PJ_LOG(4, (stun_sock->obj_name, -+ "Warning! Cannot set SO_RCVBUF as configured, " -+ "now=%d, configured=%d", -+ sobuf_size, stun_sock->setting.so_rcvbuf_size)); -+ } else { -+ PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); -+ } -+ } -+ } -+ if (stun_sock->setting.so_sndbuf_size > 0) { -+ unsigned sobuf_size = stun_sock->setting.so_sndbuf_size; -+ status = pj_sock_setsockopt_sobuf(stun_sock->main_sock_fd, pj_SO_SNDBUF(), -+ PJ_TRUE, &sobuf_size); -+ if (status != PJ_SUCCESS) { -+ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_SNDBUF"); -+ } else { -+ if (sobuf_size < stun_sock->setting.so_sndbuf_size) { -+ PJ_LOG(4, (stun_sock->obj_name, -+ "Warning! Cannot set SO_SNDBUF as configured, " -+ "now=%d, configured=%d", -+ sobuf_size, stun_sock->setting.so_sndbuf_size)); -+ } else { -+ PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); -+ } -+ } -+ } -+ -+ /* Bind socket */ -+ max_bind_retry = MAX_BIND_RETRY; -+ if (stun_sock->setting.port_range && -+ stun_sock->setting.port_range < max_bind_retry) -+ max_bind_retry = stun_sock->setting.port_range; -+ pj_sockaddr_init(stun_sock->af, &bound_addr, NULL, 0); -+ if (stun_sock->setting.bound_addr.addr.sa_family == pj_AF_INET() || -+ stun_sock->setting.bound_addr.addr.sa_family == pj_AF_INET6()) { -+ pj_sockaddr_cp(&bound_addr, &stun_sock->setting.bound_addr); -+ } -+ status = pj_sock_bind_random(stun_sock->main_sock_fd, &bound_addr, -+ stun_sock->setting.port_range, max_bind_retry); -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+ -+ /* Init active socket configuration */ -+ { -+ pj_activesock_cfg activesock_cfg; -+ pj_activesock_cb activesock_cb; -+ -+ pj_activesock_cfg_default(&activesock_cfg); -+ activesock_cfg.grp_lock = stun_sock->grp_lock; -+ activesock_cfg.async_cnt = stun_sock->setting.async_cnt; -+ activesock_cfg.concurrency = 0; -+ -+ /* Create the active socket */ -+ pj_bzero(&activesock_cb, sizeof(activesock_cb)); -+ activesock_cb.on_data_sent = &on_data_sent; -+ activesock_cb.on_data_recvfrom = &on_data_recvfrom; -+ -+#if PJ_HAS_TCP -+ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { -+ activesock_cb.on_accept_complete = &on_stun_sock_accept; -+ // Will be ready to accept incoming connections from the external world -+ status = pj_sock_listen(stun_sock->main_sock_fd, PJ_SOMAXCONN); -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+ } else { -+ activesock_cb.on_connect_complete = &on_stun_sock_ready; -+ } -+#else -+ activesock_cb.on_connect_complete = &on_stun_sock_ready; -+#endif - -- if (name == NULL) -- name = "stuntp%p"; -+ status = -+ pj_activesock_create(stun_sock->pool, stun_sock->main_sock_fd, -+ sock_type, &activesock_cfg, stun_sock->cfg.ioqueue, -+ &activesock_cb, stun_sock, &stun_sock->main_sock); - -- if (cfg == NULL) { -- pj_stun_sock_cfg_default(&default_cfg); -- cfg = &default_cfg; -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; - } - -+#if PJ_HAS_TCP -+ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { -+ status = -+ pj_activesock_start_accept(stun_sock->main_sock, stun_sock->pool); -+ } else { -+ status = PJ_SUCCESS; -+ } -+ if (status == PJ_SUCCESS) { -+ on_stun_sock_ready(stun_sock->main_sock, PJ_SUCCESS); -+ } else if (status != PJ_EPENDING) { -+ char addrinfo[PJ_INET6_ADDRSTRLEN + 10]; -+ pj_perror(3, stun_sock->pool->obj_name, status, "Failed to connect to %s", -+ pj_sockaddr_print(&bound_addr, addrinfo, sizeof(addrinfo), 3)); -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+#else -+ on_stun_sock_ready(stun_sock->main_sock, PJ_SUCCESS); -+#endif -+ } - -- /* Create structure */ -- pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); -- stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); -- stun_sock->pool = pool; -- stun_sock->obj_name = pool->obj_name; -- stun_sock->user_data = user_data; -- stun_sock->af = af; -- stun_sock->sock_fd = PJ_INVALID_SOCKET; -- pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); -- pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+} - -- stun_sock->ka_interval = cfg->ka_interval; -- if (stun_sock->ka_interval == 0) -- stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; -+/* -+ * Create the STUN transport using the specified configuration. -+ */ -+PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *cfg, -+ const char *name, -+ int af, -+ pj_stun_tp_type conn_type, -+ const pj_stun_sock_cb *cb, -+ const pj_stun_sock_cfg *setting, -+ void *user_data, -+pj_stun_sock **p_stun_sock) -+{ -+ pj_stun_sock *stun_sock; -+ pj_stun_sock_cfg default_cfg; -+ pj_pool_t *pool; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(cfg && cb && p_stun_sock, PJ_EINVAL); -+ PJ_ASSERT_RETURN(af == pj_AF_INET() || af == pj_AF_INET6(), PJ_EAFNOTSUP); -+ PJ_ASSERT_RETURN(!setting || pj_stun_sock_cfg_is_valid(setting), PJ_EINVAL); -+ PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); -+ PJ_ASSERT_RETURN(conn_type != PJ_STUN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL); -+ -+ status = pj_stun_config_check_valid(cfg); -+ if (status != PJ_SUCCESS) -+ return status; - -- if (cfg->grp_lock) { -- stun_sock->grp_lock = cfg->grp_lock; -- } else { -- status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); -- if (status != PJ_SUCCESS) { -- pj_pool_release(pool); -- return status; -- } -+ if (!setting) { -+ pj_stun_sock_cfg_default(&default_cfg); -+ setting = &default_cfg; -+ } -+ -+ if (!name) { -+ switch (conn_type) { -+ case PJ_STUN_TP_UDP: -+ name = "udpstun%p"; -+ break; -+ case PJ_STUN_TP_TCP: -+ name = "tcpstun%p"; -+ break; -+ default: -+ PJ_ASSERT_RETURN(!"Invalid STUN conn_type", PJ_EINVAL); -+ name = "tcpstun%p"; -+ break; - } -+ } -+ -+ /* Create and init basic data structure */ -+ const int PJNATH_POOL_LEN_STUN_SOCK = 256; -+ const int PJNATH_POOL_INC_STUN_SOCK = 512; -+ pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SOCK, -+ PJNATH_POOL_INC_STUN_SOCK, NULL); -+ stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); -+ stun_sock->pool = pool; -+ stun_sock->obj_name = pool->obj_name; -+ stun_sock->user_data = user_data; -+ stun_sock->af = af; -+ stun_sock->conn_type = conn_type; -+ stun_sock->main_sock_fd = PJ_INVALID_SOCKET; -+#if PJ_HAS_TCP -+ stun_sock->outgoing_nb = -1; -+ stun_sock->incoming_nb = -1; -+#endif - -- pj_grp_lock_add_ref(stun_sock->grp_lock); -- pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, -- &stun_sock_destructor); -+ /* Copy STUN config (this contains ioqueue, timer heap, etc.) */ -+ pj_memcpy(&stun_sock->cfg, cfg, sizeof(*cfg)); - -- /* Create socket and bind socket */ -- status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); -- if (status != PJ_SUCCESS) -- goto on_error; -+ /* Copy setting (QoS parameters etc */ -+ pj_memcpy(&stun_sock->setting, setting, sizeof(*setting)); - -- /* Apply QoS, if specified */ -- status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, -- &cfg->qos_params, 2, stun_sock->obj_name, -- NULL); -- if (status != PJ_SUCCESS && !cfg->qos_ignore_error) -- goto on_error; -+ /* Set callback */ -+ pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); - -- /* Apply socket buffer size */ -- if (cfg->so_rcvbuf_size > 0) { -- unsigned sobuf_size = cfg->so_rcvbuf_size; -- status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_RCVBUF(), -- PJ_TRUE, &sobuf_size); -- if (status != PJ_SUCCESS) { -- PJ_PERROR(3, (stun_sock->obj_name, status, -- "Failed setting SO_RCVBUF")); -- } else { -- if (sobuf_size < cfg->so_rcvbuf_size) { -- PJ_LOG(4, (stun_sock->obj_name, -- "Warning! Cannot set SO_RCVBUF as configured, " -- "now=%d, configured=%d", -- sobuf_size, cfg->so_rcvbuf_size)); -- } else { -- PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", -- sobuf_size)); -- } -- } -+ if (setting->grp_lock) { -+ stun_sock->grp_lock = setting->grp_lock; -+ } else { -+ status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); -+ if (status != PJ_SUCCESS) { -+ pj_pool_release(pool); -+ return status; - } -- if (cfg->so_sndbuf_size > 0) { -- unsigned sobuf_size = cfg->so_sndbuf_size; -- status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(), -- PJ_TRUE, &sobuf_size); -- if (status != PJ_SUCCESS) { -- PJ_PERROR(3, (stun_sock->obj_name, status, -- "Failed setting SO_SNDBUF")); -- } else { -- if (sobuf_size < cfg->so_sndbuf_size) { -- PJ_LOG(4, (stun_sock->obj_name, -- "Warning! Cannot set SO_SNDBUF as configured, " -- "now=%d, configured=%d", -- sobuf_size, cfg->so_sndbuf_size)); -- } else { -- PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", -- sobuf_size)); -- } -- } -+ } -+ -+ pj_grp_lock_add_ref(stun_sock->grp_lock); -+ pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, -+ &stun_sock_destructor); -+ -+ /* Create STUN session */ -+ { -+ pj_stun_session_cb sess_cb; -+ -+ pj_bzero(&sess_cb, sizeof(sess_cb)); -+ sess_cb.on_request_complete = &sess_on_request_complete; -+ sess_cb.on_send_msg = &sess_on_send_msg; -+ status = pj_stun_session_create(&stun_sock->cfg, stun_sock->obj_name, -+ &sess_cb, PJ_FALSE, stun_sock->grp_lock, -+ &stun_sock->stun_sess, conn_type); -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); -+ return status; - } -+ } - -- /* Bind socket */ -- max_bind_retry = MAX_BIND_RETRY; -- if (cfg->port_range && cfg->port_range < max_bind_retry) -- max_bind_retry = cfg->port_range; -- pj_sockaddr_init(af, &bound_addr, NULL, 0); -- if (cfg->bound_addr.addr.sa_family == pj_AF_INET() || -- cfg->bound_addr.addr.sa_family == pj_AF_INET6()) -- { -- pj_sockaddr_cp(&bound_addr, &cfg->bound_addr); -- } -- status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, -- cfg->port_range, max_bind_retry); -- if (status != PJ_SUCCESS) -- goto on_error; -+ pj_stun_sock_alloc(stun_sock); - -- /* Create more useful information string about this transport */ --#if 0 -- { -- pj_sockaddr bound_addr; -- int addr_len = sizeof(bound_addr); -+ /* Done */ -+ *p_stun_sock = stun_sock; -+ return PJ_SUCCESS; -+} - -- status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, -- &addr_len); -- if (status != PJ_SUCCESS) -- goto on_error; - -- stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); -- pj_sockaddr_print(&bound_addr, stun_sock->info, -- PJ_INET6_ADDRSTRLEN, 3); -+/* -+ * Notification when outgoing TCP socket has been connected. -+ */ -+static pj_bool_t -+on_stun_sock_ready(pj_activesock_t *asock, pj_status_t status) -+{ -+ pj_stun_sock *stun_sock; -+ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); -+ if (!stun_sock) -+ return PJ_FALSE; -+ -+ pj_grp_lock_acquire(stun_sock->grp_lock); -+ -+ /* TURN session may have already been destroyed here. -+ * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). -+ */ -+ if (!stun_sock->stun_sess) { -+ sess_fail(stun_sock, PJ_STUN_SESS_DESTROYED, status); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return PJ_FALSE; - } --#endif - -- /* Init active socket configuration */ -- { -- pj_activesock_cfg activesock_cfg; -- pj_activesock_cb activesock_cb; -- -- pj_activesock_cfg_default(&activesock_cfg); -- activesock_cfg.grp_lock = stun_sock->grp_lock; -- activesock_cfg.async_cnt = cfg->async_cnt; -- activesock_cfg.concurrency = 0; -- -- /* Create the active socket */ -- pj_bzero(&activesock_cb, sizeof(activesock_cb)); -- activesock_cb.on_data_recvfrom = &on_data_recvfrom; -- activesock_cb.on_data_sent = &on_data_sent; -- status = pj_activesock_create(pool, stun_sock->sock_fd, -- pj_SOCK_DGRAM(), -- &activesock_cfg, stun_cfg->ioqueue, -- &activesock_cb, stun_sock, -- &stun_sock->active_sock); -- if (status != PJ_SUCCESS) -- goto on_error; -- -- /* Start asynchronous read operations */ -- status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, -- cfg->max_pkt_size, 0); -- if (status != PJ_SUCCESS) -- goto on_error; -- -- /* Init send keys */ -- pj_ioqueue_op_key_init(&stun_sock->send_key, -- sizeof(stun_sock->send_key)); -- pj_ioqueue_op_key_init(&stun_sock->int_send_key, -- sizeof(stun_sock->int_send_key)); -+ if (status != PJ_SUCCESS) { -+ sess_fail(stun_sock, PJ_STUN_TCP_CONNECT_ERROR, status); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return PJ_FALSE; - } - -- /* Create STUN session */ -- { -- pj_stun_session_cb sess_cb; -- -- pj_bzero(&sess_cb, sizeof(sess_cb)); -- sess_cb.on_request_complete = &sess_on_request_complete; -- sess_cb.on_send_msg = &sess_on_send_msg; -- status = pj_stun_session_create(&stun_sock->stun_cfg, -- stun_sock->obj_name, -- &sess_cb, PJ_FALSE, -- stun_sock->grp_lock, -- &stun_sock->stun_sess); -- if (status != PJ_SUCCESS) -- goto on_error; -+ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { -+ PJ_LOG(5,(stun_sock->obj_name, "TCP connected")); - } - -+ /* Start asynchronous read operations */ -+ pj_status_t result = pj_activesock_start_recvfrom( -+ asock, stun_sock->pool, stun_sock->setting.max_pkt_size, 0); -+ if (result != PJ_SUCCESS) { -+ return PJ_FALSE; -+ }; -+ - /* Associate us with the STUN session */ - pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); - -@@ -368,8 +526,9 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, - * STUN messages we sent with STUN messages that the application sends. - * The last 16bit value in the array is a counter. - */ -+ unsigned i; - for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) { -- stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); -+ stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); - } - stun_sock->tsx_id[5] = 0; - -@@ -378,15 +537,286 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, - stun_sock->ka_timer.cb = &ka_timer_cb; - stun_sock->ka_timer.user_data = stun_sock; - -- /* Done */ -- *p_stun_sock = stun_sock; -- return PJ_SUCCESS; -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } - --on_error: -- pj_stun_sock_destroy(stun_sock); -- return status; -+ /* Init send keys */ -+ pj_ioqueue_op_key_init(&stun_sock->send_key, -+ sizeof(stun_sock->send_key)); -+ pj_ioqueue_op_key_init(&stun_sock->int_send_key, -+ sizeof(stun_sock->int_send_key)); -+ -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return PJ_TRUE; -+} -+ -+pj_bool_t -+parse_rx_packet(pj_activesock_t *asock, void *data, pj_size_t size, -+ const pj_sockaddr_t *rx_addr, unsigned sock_addr_len) { -+ -+ pj_stun_sock *stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); -+ if (!stun_sock) -+ return PJ_FALSE; -+ -+ pj_grp_lock_acquire(stun_sock->grp_lock); -+ pj_uint16_t parsed = 0; -+ pj_status_t result = PJ_TRUE; -+ pj_status_t status; -+ -+#if PJ_HAS_TCP -+ pj_uint8_t* rx_buffer = NULL; -+ pj_uint16_t* rx_buffer_size = NULL; -+ pj_uint16_t* rx_wanted_size = NULL; -+ -+ -+ // Search current rx_buf -+ rx_buf* buf = NULL; -+ rx_buf* stun_sock_buf = stun_sock->rx_buffers; -+ while (stun_sock_buf) { -+ if (stun_sock_buf->asock == asock) { -+ buf = stun_sock_buf; -+ break; -+ } -+ stun_sock_buf = stun_sock_buf->next; -+ } -+ if (!buf) { -+ // Create rx_buf, this buf will be released when the pool is released -+ buf = (rx_buf*)pj_pool_calloc(stun_sock->pool, 1, sizeof(rx_buf)); -+ if (!buf) { -+ PJ_LOG(5, (stun_sock->obj_name, "Cannot allocate memory for rx_buf")); -+ status = pj_grp_lock_release(stun_sock->grp_lock); -+ return PJ_FALSE; -+ } -+ buf->asock = asock; -+ buf->next = stun_sock->rx_buffers; -+ if (stun_sock->rx_buffers) stun_sock->rx_buffers->prev = buf; -+ stun_sock->rx_buffers = buf; -+ } -+#endif -+ -+ do { -+ pj_uint16_t pkt_len = size - parsed; -+ pj_uint8_t *current_packet = ((pj_uint8_t *)(data)) + parsed; -+ -+#if PJ_HAS_TCP -+ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { -+ /* RFC6544, the packet is wrapped into a packet following the RFC4571 */ -+ pj_bool_t store_remaining = PJ_TRUE; -+ if (buf->rx_buffer_size != 0 || buf->rx_wanted_size != 0) { -+ // We currently have a packet to complete -+ if (buf->rx_buffer_size == 1) { -+ // We do not know the current size, parse it. -+ pkt_len = (((pj_uint8_t *)buf->rx_buffer)[0] << 8) + -+ ((pj_uint8_t *)current_packet)[0]; -+ buf->rx_buffer_size = 0; // We have eaten the temp packet. -+ current_packet = current_packet + 1; -+ parsed += 1; -+ if (pkt_len + parsed <= size) { -+ store_remaining = PJ_FALSE; -+ parsed += pkt_len; -+ } else { -+ buf->rx_wanted_size = pkt_len; -+ } -+ } else if (pkt_len + buf->rx_buffer_size >= buf->rx_wanted_size) { -+ // We have enough data Build new packet to parse -+ store_remaining = PJ_FALSE; -+ pj_uint16_t eaten_bytes = buf->rx_wanted_size - buf->rx_buffer_size; -+ memcpy(buf->rx_buffer + buf->rx_buffer_size, -+ current_packet, eaten_bytes); -+ pkt_len = buf->rx_wanted_size; -+ current_packet = buf->rx_buffer; -+ parsed += eaten_bytes; -+ buf->rx_buffer_size = 0; -+ buf->rx_wanted_size = 0; -+ } -+ } else if (pkt_len > 1) { -+ pkt_len = (((pj_uint8_t *)current_packet)[0] << 8) + ((pj_uint8_t *)current_packet)[1]; -+ current_packet = current_packet + 2; -+ parsed += 2; -+ if (pkt_len + parsed <= size) { -+ store_remaining = PJ_FALSE; -+ parsed += pkt_len; -+ } else { -+ buf->rx_wanted_size = pkt_len; -+ } -+ } -+ if (store_remaining) { -+ pj_uint16_t stored_size = size - parsed; -+ memcpy(buf->rx_buffer + buf->rx_buffer_size, -+ current_packet, stored_size); -+ buf->rx_buffer_size += stored_size; -+ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; -+ break; -+ } -+ } else { -+#endif -+ parsed = size; -+#if PJ_HAS_TCP -+ } -+#endif -+ /* Check that this is STUN message */ -+ status = pj_stun_msg_check((const pj_uint8_t *)current_packet, pkt_len, -+ PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); -+ if (status != PJ_SUCCESS) { -+ /* Not STUN -- give it to application */ -+ goto process_app_data; -+ } -+ -+ /* Treat packet as STUN header and copy the STUN message type. -+ * We don't want to access the type directly from the header -+ * since it may not be properly aligned. -+ */ -+ pj_stun_msg_hdr *hdr = (pj_stun_msg_hdr *)current_packet; -+ pj_uint16_t type; -+ pj_memcpy(&type, &hdr->type, 2); -+ type = pj_ntohs(type); -+ -+ /* If the packet is a STUN Binding response and part of the -+ * transaction ID matches our internal ID, then this is -+ * our internal STUN message (Binding request or keep alive). -+ * Give it to our STUN session. -+ */ -+ if (!PJ_STUN_IS_RESPONSE(type) || -+ PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || -+ pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) { -+ /* Not STUN Binding response, or STUN transaction ID mismatch. -+ * This is not our message too -- give it to application. -+ */ -+ goto process_app_data; -+ } -+ -+ /* This is our STUN Binding response. Give it to the STUN session */ -+ status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, current_packet, -+ pkt_len, PJ_STUN_IS_DATAGRAM, NULL, -+ NULL, rx_addr, sock_addr_len); -+ -+ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; -+ continue; -+ -+ process_app_data: -+ if (stun_sock->cb.on_rx_data) { -+ (*stun_sock->cb.on_rx_data)(stun_sock, current_packet, -+ (unsigned)pkt_len, rx_addr, sock_addr_len); -+ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; -+ continue; -+ } -+ -+ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; -+ } while (parsed < size && result); -+ -+ status = pj_grp_lock_release(stun_sock->grp_lock); -+ return result; -+} -+ -+pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, -+ pj_status_t status, pj_size_t *remainder) { -+ -+ pj_stun_sock *stun_sock; -+ -+ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); -+ if (!stun_sock) return PJ_FALSE; -+ -+ pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); -+ /* Log socket error or disconnection */ -+ if (status != PJ_SUCCESS) { -+ if (stun_sock->conn_type == PJ_STUN_TP_UDP || (status != PJ_EEOF && status != 120104 && status != 130054)) { -+ PJ_PERROR(2, (stun_sock->obj_name, status, "read() error")); -+ } else if (status == 120104 || status == 130054 /* RESET BY PEER */) { -+ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { -+ if (stun_sock->outgoing_socks[i].sock == asock) { -+ if (cb && (cb->on_peer_reset_connection)) { -+ (cb->on_peer_reset_connection)(stun_sock->stun_sess, stun_sock->outgoing_socks[i].addr); -+ } -+ } -+ } -+ } -+ return PJ_FALSE; -+ } -+#if PJ_HAS_TCP -+ pj_sockaddr_t *rx_addr = NULL; -+ unsigned sock_addr_len = 0; -+ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { -+ if (stun_sock->outgoing_socks[i].sock == asock) { -+ rx_addr = stun_sock->outgoing_socks[i].addr; -+ sock_addr_len = pj_sockaddr_get_len(rx_addr); -+ if (cb && (cb->on_peer_packet)) -+ (cb->on_peer_packet)(stun_sock->stun_sess, stun_sock->outgoing_socks[i].addr); -+ } -+ } -+ if (rx_addr == NULL && stun_sock->incoming_nb != -1) { -+ // It's an incoming message -+ for (int i = 0; i <= stun_sock->incoming_nb; ++i) { -+ if (stun_sock->incoming_socks[i].sock == asock) { -+ rx_addr = &stun_sock->incoming_socks[i].addr; -+ sock_addr_len = stun_sock->incoming_socks[i].addr_len; -+ } -+ } -+ } -+ return parse_rx_packet(asock, data, size, rx_addr, sock_addr_len); -+#else -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return PJ_FALSE; -+#endif - } - -+#if PJ_HAS_TCP -+/* -+ * Notification when incoming TCP socket has been connected. -+ * NOTE: cf https://www.pjsip.org/docs/latest/pjlib/docs/html//structpj__activesock__cb.htm if status needed -+ */ -+static pj_bool_t on_stun_sock_accept(pj_activesock_t *main_sock, pj_sock_t sock, -+ const pj_sockaddr_t *src_addr, -+ int src_addr_len) { -+ pj_status_t status; -+ pj_stun_sock *stun_sock; -+ int sock_type = pj_SOCK_STREAM(); -+ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(main_sock); -+ -+ stun_sock->incoming_nb += 1; -+ int nb_check = stun_sock->incoming_nb; -+ pj_sock_t *fd = &stun_sock->incoming_socks[nb_check].fd; -+ pj_activesock_t **asock = &stun_sock->incoming_socks[nb_check].sock; -+ pj_sockaddr_cp(&stun_sock->incoming_socks[nb_check].addr, src_addr); -+ stun_sock->incoming_socks[nb_check].addr_len = src_addr_len; -+ *fd = sock; -+ -+ pj_activesock_cfg activesock_cfg; -+ pj_activesock_cb activesock_cb; -+ -+ pj_activesock_cfg_default(&activesock_cfg); -+ activesock_cfg.grp_lock = stun_sock->grp_lock; -+ activesock_cfg.async_cnt = stun_sock->setting.async_cnt; -+ activesock_cfg.concurrency = 0; -+ -+ /* Create the active socket */ -+ pj_bzero(&activesock_cb, sizeof(activesock_cb)); -+ activesock_cb.on_data_read = &on_data_read; -+ activesock_cb.on_data_sent = &on_data_sent; -+ -+ status = pj_activesock_create(stun_sock->pool, *fd, sock_type, -+ &activesock_cfg, stun_sock->cfg.ioqueue, -+ &activesock_cb, stun_sock, asock); -+ -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+ -+ /* Start asynchronous read operations */ -+ pj_status_t result = pj_activesock_start_read(*asock, stun_sock->pool, -+ stun_sock->setting.max_pkt_size, 0); -+ if (result != PJ_SUCCESS) { -+ return PJ_FALSE; -+ }; -+ -+ return PJ_TRUE; -+} -+#endif -+ - /* Start socket. */ - PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, - const pj_str_t *domain, -@@ -504,15 +934,35 @@ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock) - } - - stun_sock->is_destroying = PJ_TRUE; -- pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap, -+ pj_timer_heap_cancel_if_active(stun_sock->cfg.timer_heap, - &stun_sock->ka_timer, 0); - -- if (stun_sock->active_sock != NULL) { -- stun_sock->sock_fd = PJ_INVALID_SOCKET; -- pj_activesock_close(stun_sock->active_sock); -- } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) { -- pj_sock_close(stun_sock->sock_fd); -- stun_sock->sock_fd = PJ_INVALID_SOCKET; -+ if (stun_sock->main_sock != NULL) { -+ stun_sock->main_sock_fd = PJ_INVALID_SOCKET; -+ pj_activesock_close(stun_sock->main_sock); -+ } else if (stun_sock->main_sock_fd != PJ_INVALID_SOCKET) { -+ pj_sock_close(stun_sock->main_sock_fd); -+ stun_sock->main_sock_fd = PJ_INVALID_SOCKET; -+ } -+ -+ for (int i = 0; i < stun_sock->incoming_nb ; ++i) { -+ if (stun_sock->incoming_socks[i].sock != NULL) { -+ stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; -+ pj_activesock_close(stun_sock->incoming_socks[i].sock); -+ } else if (stun_sock->incoming_socks[i].fd != PJ_INVALID_SOCKET) { -+ pj_sock_close(stun_sock->incoming_socks[i].fd); -+ stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; -+ } -+ } -+ -+ for (int i = 0; i < stun_sock->outgoing_nb ; ++i) { -+ if (stun_sock->outgoing_socks[i].sock != NULL) { -+ stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; -+ pj_activesock_close(stun_sock->outgoing_socks[i].sock); -+ } else if (stun_sock->outgoing_socks[i].fd != PJ_INVALID_SOCKET) { -+ pj_sock_close(stun_sock->outgoing_socks[i].fd); -+ stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; -+ } - } - - if (stun_sock->stun_sess) { -@@ -619,12 +1069,12 @@ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) - &tdata); - if (status != PJ_SUCCESS) - goto on_error; -- -+ - /* Send request */ -- status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, -- PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, -- pj_sockaddr_get_len(&stun_sock->srv_addr), -- tdata); -+ status = pj_stun_session_send_msg( -+ stun_sock->stun_sess, INTERNAL_MSG_TOKEN, PJ_FALSE, -+ (stun_sock->conn_type == PJ_STUN_TP_UDP), &stun_sock->srv_addr, -+ pj_sockaddr_get_len(&stun_sock->srv_addr), tdata); - if (status != PJ_SUCCESS && status != PJ_EPENDING) - goto on_error; - -@@ -637,7 +1087,7 @@ on_error: - - /* Get info */ - PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, -- pj_stun_sock_info *info) -+ pj_stun_sock_info *info) - { - int addr_len; - pj_status_t status; -@@ -646,73 +1096,73 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, - - pj_grp_lock_acquire(stun_sock->grp_lock); - -+ info->conn_type = stun_sock->conn_type; -+ - /* Copy STUN server address and mapped address */ - pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, -- sizeof(pj_sockaddr)); -- pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, -- sizeof(pj_sockaddr)); -+ sizeof(pj_sockaddr)); -+ pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, -+ sizeof(pj_sockaddr)); - - /* Retrieve bound address */ - addr_len = sizeof(info->bound_addr); -- status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr, -- &addr_len); -+ status = pj_sock_getsockname(stun_sock->main_sock_fd, &info->bound_addr, -+ &addr_len); - if (status != PJ_SUCCESS) { -- pj_grp_lock_release(stun_sock->grp_lock); -- return status; -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; - } - -- /* If socket is bound to a specific interface, then only put that -- * interface in the alias list. Otherwise query all the interfaces -- * in the host. -+ /* Query all the interfaces in the host, because STUN is compatible with TCP - */ - if (pj_sockaddr_has_addr(&info->bound_addr)) { -- info->alias_cnt = 1; -- pj_sockaddr_cp(&info->aliases[0], &info->bound_addr); -+ info->alias_cnt = 1; -+ pj_sockaddr_cp(&info->aliases[0], &info->bound_addr); - } else { -- pj_sockaddr def_addr; -- pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); -- unsigned i; -- -- /* Get the default address */ -- status = pj_gethostip(stun_sock->af, &def_addr); -- if (status != PJ_SUCCESS) { -- PJ_PERROR(4,(stun_sock->obj_name, status, -- "Failed in getting default address for STUN info")); -- pj_grp_lock_release(stun_sock->grp_lock); -- return status; -- } -- -- pj_sockaddr_set_port(&def_addr, port); -- -- /* Enum all IP interfaces in the host */ -- info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); -- status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, -- info->aliases); -- if (status != PJ_SUCCESS) { -- /* If enumeration fails, just return the default address */ -- PJ_PERROR(4,(stun_sock->obj_name, status, -- "Failed in enumerating interfaces for STUN info, " -- "returning default address only")); -- info->alias_cnt = 1; -- pj_sockaddr_cp(&info->aliases[0], &def_addr); -- } -- -- /* Set the port number for each address. -- */ -- for (i=0; i<info->alias_cnt; ++i) { -- pj_sockaddr_set_port(&info->aliases[i], port); -- } -- -- /* Put the default IP in the first slot */ -- for (i=0; i<info->alias_cnt; ++i) { -- if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) { -- if (i!=0) { -- pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]); -- pj_sockaddr_cp(&info->aliases[0], &def_addr); -- } -- break; -- } -- } -+ pj_sockaddr def_addr; -+ pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); -+ unsigned i; -+ -+ /* Get the default address */ -+ status = pj_gethostip(stun_sock->af, &def_addr); -+ if (status != PJ_SUCCESS) { -+ PJ_PERROR(4,(stun_sock->obj_name, status, -+ "Failed in getting default address for STUN info")); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+ -+ pj_sockaddr_set_port(&def_addr, port); -+ -+ /* Enum all IP interfaces in the host */ -+ info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); -+ status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, -+ info->aliases); -+ if (status != PJ_SUCCESS) { -+ /* If enumeration fails, just return the default address */ -+ PJ_PERROR(4,(stun_sock->obj_name, status, -+ "Failed in enumerating interfaces for STUN info, " -+ "returning default address only")); -+ info->alias_cnt = 1; -+ pj_sockaddr_cp(&info->aliases[0], &def_addr); -+ } -+ -+ /* Set the port number for each address. -+ */ -+ for (i=0; i<info->alias_cnt; ++i) { -+ pj_sockaddr_set_port(&info->aliases[i], port); -+ } -+ -+ /* Put the default IP in the first slot */ -+ for (i=0; i<info->alias_cnt; ++i) { -+ if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) { -+ if (i!=0) { -+ pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]); -+ pj_sockaddr_cp(&info->aliases[0], &def_addr); -+ } -+ break; -+ } -+ } - } - - pj_grp_lock_release(stun_sock->grp_lock); -@@ -726,36 +1176,253 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, - unsigned pkt_len, - unsigned flag, - const pj_sockaddr_t *dst_addr, -- unsigned addr_len) -+ unsigned addr_len, -+ pj_ssize_t* size) - { -- pj_ssize_t size; - pj_status_t status; - - PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL); -- -+ - pj_grp_lock_acquire(stun_sock->grp_lock); - -- if (!stun_sock->active_sock) { -- /* We have been shutdown, but this callback may still get called -- * by retransmit timer. -- */ -- pj_grp_lock_release(stun_sock->grp_lock); -- return PJ_EINVALIDOP; -+ if (!stun_sock->main_sock) { -+ /* We have been shutdown, but this callback may still get called -+ * by retransmit timer. -+ */ -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return PJ_EINVALIDOP; - } - - if (send_key==NULL) -- send_key = &stun_sock->send_key; -+ send_key = &stun_sock->send_key; - -- size = pkt_len; -- status = pj_activesock_sendto(stun_sock->active_sock, send_key, -- pkt, &size, flag, dst_addr, addr_len); -+ *size = pkt_len; -+ if (stun_sock->conn_type == PJ_STUN_TP_UDP) { -+ status = pj_activesock_sendto(stun_sock->main_sock, send_key, -+ pkt, size, flag, dst_addr, addr_len); -+ } else { -+#if PJ_HAS_TCP -+ pj_bool_t is_outgoing = PJ_FALSE; -+ pj_bool_t is_incoming = PJ_FALSE; -+ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { -+ if (pj_sockaddr_cmp(stun_sock->outgoing_socks[i].addr, dst_addr) == 0) { -+ is_outgoing = PJ_TRUE; -+ status = pj_activesock_send(stun_sock->outgoing_socks[i].sock, -+ send_key, pkt, size, flag); -+ break; -+ } -+ } -+ if (is_outgoing == PJ_FALSE) { -+ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { -+ if (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, -+ dst_addr) == 0) { -+ status = pj_activesock_send(stun_sock->incoming_socks[i].sock, -+ send_key, pkt, size, flag); -+ is_incoming = PJ_TRUE; -+ break; -+ } -+ } -+ } -+ if (is_outgoing == PJ_FALSE && is_incoming == PJ_FALSE) { -+ status = pj_activesock_send(stun_sock->main_sock, send_key, pkt, -+ size, flag); -+ } - -+#endif -+ } -+ -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+} -+ -+#if PJ_HAS_TCP -+ -+PJ_DECL(pj_status_t) -+pj_stun_sock_connect(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr, int af, int nb_check) { -+ -+ pj_grp_lock_acquire(stun_sock->grp_lock); -+ int sock_type = pj_SOCK_STREAM(); -+ -+ pj_sock_t *fd = &stun_sock->outgoing_socks[nb_check].fd; -+ pj_activesock_t **asock = &stun_sock->outgoing_socks[nb_check].sock; -+ pj_sockaddr_t **addr = &stun_sock->outgoing_socks[nb_check].addr; -+ -+ pj_status_t status = pj_sock_socket(af, sock_type, 0, fd); -+ if (status != PJ_SUCCESS) { -+ pj_stun_sock_destroy(stun_sock); - pj_grp_lock_release(stun_sock->grp_lock); - return status; -+ } -+ -+ /* Apply QoS, if specified */ -+ status = pj_sock_apply_qos2(*fd, stun_sock->setting.qos_type, -+ &stun_sock->setting.qos_params, 2, stun_sock->obj_name, NULL); -+ if (status != PJ_SUCCESS && !stun_sock->setting.qos_ignore_error) { -+ pj_stun_sock_destroy(stun_sock); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+ -+ /* Apply socket buffer size */ -+ if (stun_sock->setting.so_rcvbuf_size > 0) { -+ unsigned sobuf_size = stun_sock->setting.so_rcvbuf_size; -+ status = pj_sock_setsockopt_sobuf(*fd, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); -+ if (status != PJ_SUCCESS) { -+ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_RCVBUF"); -+ } else { -+ if (sobuf_size < stun_sock->setting.so_rcvbuf_size) { -+ PJ_LOG(4, (stun_sock->obj_name, -+ "Warning! Cannot set SO_RCVBUF as configured, " -+ "now=%d, configured=%d", -+ sobuf_size, stun_sock->setting.so_rcvbuf_size)); -+ } else { -+ PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); -+ } -+ } -+ } -+ -+ if (stun_sock->setting.so_sndbuf_size > 0) { -+ unsigned sobuf_size = stun_sock->setting.so_sndbuf_size; -+ status = pj_sock_setsockopt_sobuf(*fd, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); -+ if (status != PJ_SUCCESS) { -+ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_SNDBUF"); -+ } else { -+ if (sobuf_size < stun_sock->setting.so_sndbuf_size) { -+ PJ_LOG(4, (stun_sock->obj_name, -+ "Warning! Cannot set SO_SNDBUF as configured, " -+ "now=%d, configured=%d", -+ sobuf_size, stun_sock->setting.so_sndbuf_size)); -+ } else { -+ PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); -+ } -+ } -+ } -+ -+ /* Init active socket configuration */ -+ { -+ pj_activesock_cfg activesock_cfg; -+ pj_activesock_cb activesock_cb; -+ -+ pj_activesock_cfg_default(&activesock_cfg); -+ activesock_cfg.grp_lock = stun_sock->grp_lock; -+ activesock_cfg.async_cnt = stun_sock->setting.async_cnt; -+ activesock_cfg.concurrency = 0; -+ -+ /* Create the active socket */ -+ pj_bzero(&activesock_cb, sizeof(activesock_cb)); -+ activesock_cb.on_data_read = &on_data_read; -+ activesock_cb.on_data_sent = &on_data_sent; -+ activesock_cb.on_connect_complete = &on_connect_complete; -+ -+ status = pj_activesock_create(stun_sock->pool, *fd, -+ sock_type, &activesock_cfg, -+ stun_sock->cfg.ioqueue, &activesock_cb, -+ stun_sock, asock); -+ -+ if (status != PJ_SUCCESS) { -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+ -+ *addr = remote_addr; -+ -+ status = pj_activesock_start_connect( -+ *asock, stun_sock->pool, *addr, -+ pj_sockaddr_get_len(*addr)); -+ if (status == PJ_SUCCESS) { -+ on_connect_complete(*asock, status); -+ } else if (status != PJ_EPENDING) { -+ char addrinfo[PJ_INET6_ADDRSTRLEN+8]; -+ pj_perror(3, stun_sock->pool->obj_name, status, "Failed to connect to %s", -+ pj_sockaddr_print(*addr, addrinfo, sizeof(addrinfo), 3)); -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+ } -+ } -+ -+ pj_grp_lock_release(stun_sock->grp_lock); -+ return status; -+} -+ -+PJ_DECL(pj_status_t) -+pj_stun_sock_connect_active(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr, int af) { -+ -+ if (stun_sock->incoming_nb != -1) { -+ // Check if not incoming, if so, already connected (mainly for PRFLX candidates) -+ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { -+ if (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) == 0) { -+ pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); -+ (cb->on_peer_connection)(stun_sock->stun_sess, PJ_SUCCESS, remote_addr); -+ return PJ_SUCCESS; -+ } -+ } -+ } -+ -+ /* Create socket and bind socket */ -+ stun_sock->outgoing_nb += 1; -+ int nb_check = stun_sock->outgoing_nb; -+ return pj_stun_sock_connect(stun_sock, remote_addr, af, nb_check); -+ - } - -+PJ_DECL(pj_status_t) -+pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr, int af) { -+ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { -+ if (pj_sockaddr_cmp(stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { -+ pj_activesock_close(stun_sock->outgoing_socks[i].sock); -+ return pj_stun_sock_connect(stun_sock, remote_addr, af, i); -+ } -+ } -+ return PJ_EINVAL; -+} -+ -+PJ_DECL(pj_status_t) -+pj_stun_sock_close(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr) { -+ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { -+ if (pj_sockaddr_cmp(stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { -+ return pj_activesock_close(stun_sock->outgoing_socks[i].sock); -+ } -+ } -+ -+ for (int i = 0; i <= stun_sock->incoming_nb; ++i) { -+ if (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) == 0) { -+ return pj_activesock_close(stun_sock->incoming_socks[i].sock); -+ } -+ } -+ return PJ_EINVAL; -+} -+ -+pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) { -+ pj_stun_sock *stun_sock; -+ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); -+ -+ pj_status_t result = pj_activesock_start_read( -+ asock, stun_sock->pool, stun_sock->setting.max_pkt_size, 0); -+ if (result != PJ_SUCCESS) { -+ return PJ_FALSE; -+ }; -+ -+ pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); -+ if (!cb->on_peer_connection) { -+ return PJ_FALSE; -+ } -+ -+ // Get remote connected address -+ pj_sockaddr_t* remote_addr = NULL; -+ for (int i = 0 ; i <= stun_sock->outgoing_nb ; ++i) { -+ if (stun_sock->outgoing_socks[i].sock == asock) { -+ remote_addr = stun_sock->outgoing_socks[i].addr; -+ } -+ } -+ if (!remote_addr) return PJ_FALSE; -+ (cb->on_peer_connection)(stun_sock->stun_sess, status, remote_addr); -+ return PJ_TRUE; -+} -+ -+#endif -+ - /* This callback is called by the STUN session to send packet */ --static pj_status_t sess_on_send_msg(pj_stun_session *sess, -+pj_status_t sess_on_send_msg(pj_stun_session *sess, - void *token, - const void *pkt, - pj_size_t pkt_size, -@@ -766,7 +1433,7 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, - pj_ssize_t size; - - stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); -- if (!stun_sock || !stun_sock->active_sock) { -+ if (!stun_sock || !stun_sock->main_sock) { - /* We have been shutdown, but this callback may still get called - * by retransmit timer. - */ -@@ -777,15 +1444,32 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, - PJ_UNUSED_ARG(token); - - size = pkt_size; -- return pj_activesock_sendto(stun_sock->active_sock, -- &stun_sock->int_send_key, -- pkt, &size, 0, dst_addr, addr_len); -+ if (stun_sock->conn_type == PJ_STUN_TP_UDP) { -+ pj_status_t status = pj_activesock_sendto(stun_sock->main_sock, -+ &stun_sock->int_send_key, -+ pkt, &size, 0, dst_addr, addr_len); -+ } -+#if PJ_HAS_TCP -+ else { -+ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { -+ if (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, dst_addr) == 0) { -+ pj_status_t status = pj_activesock_send(stun_sock->incoming_socks[i].sock, -+ &stun_sock->int_send_key, pkt, &size, 0); -+ } -+ } -+ pj_status_t status = pj_activesock_send(stun_sock->main_sock, &stun_sock->int_send_key, -+ pkt, &size, 0); -+ return status; -+ } -+#else -+ return PJ_EINVAL; -+#endif - } - - /* This callback is called by the STUN session when outgoing transaction - * is complete - */ --static void sess_on_request_complete(pj_stun_session *sess, -+void sess_on_request_complete(pj_stun_session *sess, - pj_status_t status, - void *token, - pj_stun_tx_data *tdata, -@@ -869,9 +1553,9 @@ on_return: - } - - /* Schedule keep-alive timer */ --static void start_ka_timer(pj_stun_sock *stun_sock) -+void start_ka_timer(pj_stun_sock *stun_sock) - { -- pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap, -+ pj_timer_heap_cancel_if_active(stun_sock->cfg.timer_heap, - &stun_sock->ka_timer, 0); - - pj_assert(stun_sock->ka_interval != 0); -@@ -881,7 +1565,7 @@ static void start_ka_timer(pj_stun_sock *stun_sock) - delay.sec = stun_sock->ka_interval; - delay.msec = 0; - -- pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap, -+ pj_timer_heap_schedule_w_grp_lock(stun_sock->cfg.timer_heap, - &stun_sock->ka_timer, - &delay, PJ_TRUE, - stun_sock->grp_lock); -@@ -889,7 +1573,7 @@ static void start_ka_timer(pj_stun_sock *stun_sock) - } - - /* Keep-alive timer callback */ --static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) -+void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) - { - pj_stun_sock *stun_sock; - -@@ -911,7 +1595,7 @@ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) - } - - /* Callback from active socket when incoming packet is received */ --static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, -+pj_bool_t on_data_recvfrom(pj_activesock_t *asock, - void *data, - pj_size_t size, - const pj_sockaddr_t *src_addr, -@@ -919,8 +1603,6 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, - pj_status_t status) - { - pj_stun_sock *stun_sock; -- pj_stun_msg_hdr *hdr; -- pj_uint16_t type; - - stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); - if (!stun_sock) -@@ -932,62 +1614,11 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, - return PJ_TRUE; - } - -- pj_grp_lock_acquire(stun_sock->grp_lock); -- -- /* Check that this is STUN message */ -- status = pj_stun_msg_check((const pj_uint8_t*)data, size, -- PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); -- if (status != PJ_SUCCESS) { -- /* Not STUN -- give it to application */ -- goto process_app_data; -- } -- -- /* Treat packet as STUN header and copy the STUN message type. -- * We don't want to access the type directly from the header -- * since it may not be properly aligned. -- */ -- hdr = (pj_stun_msg_hdr*) data; -- pj_memcpy(&type, &hdr->type, 2); -- type = pj_ntohs(type); -- -- /* If the packet is a STUN Binding response and part of the -- * transaction ID matches our internal ID, then this is -- * our internal STUN message (Binding request or keep alive). -- * Give it to our STUN session. -- */ -- if (!PJ_STUN_IS_RESPONSE(type) || -- PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || -- pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) -- { -- /* Not STUN Binding response, or STUN transaction ID mismatch. -- * This is not our message too -- give it to application. -- */ -- goto process_app_data; -- } -- -- /* This is our STUN Binding response. Give it to the STUN session */ -- status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, -- PJ_STUN_IS_DATAGRAM, NULL, NULL, -- src_addr, addr_len); -- -- status = pj_grp_lock_release(stun_sock->grp_lock); -- -- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; -- --process_app_data: -- if (stun_sock->cb.on_rx_data) { -- (*stun_sock->cb.on_rx_data)(stun_sock, data, (unsigned)size, -- src_addr, addr_len); -- status = pj_grp_lock_release(stun_sock->grp_lock); -- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; -- } -- -- status = pj_grp_lock_release(stun_sock->grp_lock); -- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; -+ return parse_rx_packet(asock, data, size, src_addr, addr_len); - } - - /* Callback from active socket about send status */ --static pj_bool_t on_data_sent(pj_activesock_t *asock, -+pj_bool_t on_data_sent(pj_activesock_t *asock, - pj_ioqueue_op_key_t *send_key, - pj_ssize_t sent) - { -@@ -1024,3 +1655,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock, - return PJ_TRUE; - } - -+pj_stun_session* pj_stun_sock_get_session(pj_stun_sock *stun_sock) -+{ -+ return stun_sock ? stun_sock->stun_sess : NULL; -+} -diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c -index 28f62300..0c15763c 100644 ---- a/pjnath/src/pjnath/stun_transaction.c -+++ b/pjnath/src/pjnath/stun_transaction.c -@@ -394,6 +394,7 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, - PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, - pj_bool_t mod_count) - { -+ if (!tsx) return PJ_EINVAL; - if (tsx->destroy_timer.id != 0) { - return PJ_SUCCESS; - } -diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c -index 97e9435c..9f91dd34 100644 ---- a/pjnath/src/pjnath/turn_session.c -+++ b/pjnath/src/pjnath/turn_session.c -@@ -311,7 +311,8 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg, - stun_cb.on_request_complete = &stun_on_request_complete; - stun_cb.on_rx_indication = &stun_on_rx_indication; - status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb, -- PJ_FALSE, sess->grp_lock, &sess->stun); -+ PJ_FALSE, sess->grp_lock, &sess->stun, -+ conn_type); - if (status != PJ_SUCCESS) { - do_destroy(sess); - return status; -diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c -index a8d9ed00..7033fd93 100644 ---- a/pjnath/src/pjnath/turn_sock.c -+++ b/pjnath/src/pjnath/turn_sock.c -@@ -1357,3 +1357,19 @@ static void turn_on_connection_bind_status(pj_turn_session *sess, - peer_addr, addr_len); - } - } -+ -+pj_bool_t -+pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, const pj_sockaddr_t *peer) -+{ -+ if (!turn_sock) return PJ_FALSE; -+ -+ for (int i = 0; i < turn_sock->data_conn_cnt; ++i) { -+ tcp_data_conn_t* dataconn = &turn_sock->data_conn[i]; -+ if (dataconn) { -+ pj_sockaddr_t* conn_peer = &dataconn->peer_addr; -+ if (pj_sockaddr_cmp(conn_peer, peer) == 0) return PJ_TRUE; -+ } -+ } -+ -+ return PJ_FALSE; -+} -\ No newline at end of file -diff --git a/pjnath/src/pjturn-client/client_main.c b/pjnath/src/pjturn-client/client_main.c -index 1a866722..5f013631 100644 ---- a/pjnath/src/pjturn-client/client_main.c -+++ b/pjnath/src/pjturn-client/client_main.c -@@ -154,10 +154,10 @@ static int init() - #endif - - name[strlen(name)-1] = '0'+i; -- status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), -- &stun_sock_cb, &ss_cfg, -- &g.peer[i], &g.peer[i].stun_sock); -- if (status != PJ_SUCCESS) { -+ status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), -+ PJ_STUN_TP_UDP, &stun_sock_cb, &ss_cfg, -+ &g.peer[i], &g.peer[i].stun_sock); -+ if (status != PJ_SUCCESS) { - my_perror("pj_stun_sock_create()", status); - return status; - } -@@ -525,8 +525,9 @@ static void console_main(void) - } - peer = &g.peer[input[0]-'0']; - sprintf(input, "Hello from peer%d", input[0]-'0'); -+ pj_ssize_t size; - pj_stun_sock_sendto(peer->stun_sock, NULL, input, strlen(input)+1, 0, -- &g.relay_addr, pj_sockaddr_get_len(&g.relay_addr)); -+ &g.relay_addr, pj_sockaddr_get_len(&g.relay_addr), &size); - break; - case 'q': - g.quit = PJ_TRUE; -diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c -index 6c9c9ce1..eea91f01 100644 ---- a/pjnath/src/pjturn-srv/allocation.c -+++ b/pjnath/src/pjturn-srv/allocation.c -@@ -338,7 +338,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, - sess_cb.on_rx_request = &stun_on_rx_request; - sess_cb.on_rx_indication = &stun_on_rx_indication; - status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, -- &sess_cb, PJ_FALSE, NULL, &alloc->sess); -+ &sess_cb, PJ_FALSE, NULL, &alloc->sess, PJ_STUN_TP_UDP); - if (status != PJ_SUCCESS) { - goto on_error; - } -diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c -index 94dda29a..95ad1793 100644 ---- a/pjnath/src/pjturn-srv/server.c -+++ b/pjnath/src/pjturn-srv/server.c -@@ -156,7 +156,7 @@ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf, - - status = pj_stun_session_create(&srv->core.stun_cfg, srv->obj_name, - &sess_cb, PJ_FALSE, NULL, -- &srv->core.stun_sess); -+ &srv->core.stun_sess, PJ_STUN_TP_UDP); - if (status != PJ_SUCCESS) { - goto on_error; - } -diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c -index 9d811374..92826fd8 100644 ---- a/pjsip-apps/src/samples/icedemo.c -+++ b/pjsip-apps/src/samples/icedemo.c -@@ -43,7 +43,7 @@ static struct app_t - pj_bool_t regular; - pj_str_t stun_srv; - pj_str_t turn_srv; -- pj_bool_t turn_tcp; -+ pj_bool_t ice_tcp; - pj_str_t turn_username; - pj_str_t turn_password; - pj_bool_t turn_fingerprint; -@@ -341,25 +341,31 @@ static pj_status_t icedemo_init(void) - else - icedemo.ice_cfg.opt.aggressive = PJ_TRUE; - -- /* Configure STUN/srflx candidate resolution */ -- if (icedemo.opt.stun_srv.slen) { -- char *pos; -- -- /* Command line option may contain port number */ -- if ((pos=pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL) { -- icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr; -- icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr); -- -- icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos+1); -- } else { -- icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv; -- icedemo.ice_cfg.stun.port = PJ_STUN_PORT; -- } -- -- /* For this demo app, configure longer STUN keep-alive time -- * so that it does't clutter the screen output. -- */ -- icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL; -+ /* Connection type to STUN server */ -+ if (icedemo.opt.ice_tcp) -+ icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_TCP; -+ else -+ icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_UDP; -+ -+ /* Configure STUN/srflx candidate resolution */ -+ if (icedemo.opt.stun_srv.slen) { -+ char *pos; -+ -+ /* Command line option may contain port number */ -+ if ((pos = pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL) { -+ icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr; -+ icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr); -+ -+ icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos + 1); -+ } else { -+ icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv; -+ icedemo.ice_cfg.stun.port = PJ_STUN_PORT; -+ } -+ -+ /* For this demo app, configure longer STUN keep-alive time -+ * so that it does't clutter the screen output. -+ */ -+ icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL; - } - - /* Configure TURN candidate */ -@@ -384,7 +390,7 @@ static pj_status_t icedemo_init(void) - icedemo.ice_cfg.turn.auth_cred.data.static_cred.data = icedemo.opt.turn_password; - - /* Connection type to TURN server */ -- if (icedemo.opt.turn_tcp) -+ if (icedemo.opt.ice_tcp) - icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_TCP; - else - icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP; -@@ -395,8 +401,12 @@ static pj_status_t icedemo_init(void) - icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; - } - -- /* -= That's it for now, initialization is complete =- */ -- return PJ_SUCCESS; -+ if (icedemo.opt.ice_tcp) { -+ icedemo.ice_cfg.protocol = PJ_ICE_TP_TCP; -+ } -+ -+ /* -= That's it for now, initialization is complete =- */ -+ return PJ_SUCCESS; - } - - -@@ -524,26 +534,60 @@ static void icedemo_stop_session(void) - - /* Utility to create a=candidate SDP attribute */ - static int print_cand(char buffer[], unsigned maxlen, -- const pj_ice_sess_cand *cand) -+ const pj_ice_sess_cand *cand) - { - char ipaddr[PJ_INET6_ADDRSTRLEN]; - char *p = buffer; - int printed; - -- PRINT("a=candidate:%.*s %u UDP %u %s %u typ ", -- (int)cand->foundation.slen, -- cand->foundation.ptr, -- (unsigned)cand->comp_id, -- cand->prio, -- pj_sockaddr_print(&cand->addr, ipaddr, -- sizeof(ipaddr), 0), -- (unsigned)pj_sockaddr_get_port(&cand->addr)); -- -- PRINT("%s\n", -- pj_ice_get_cand_type_name(cand->type)); -+ /** Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544) -+ * candidate-attribute = "candidate" ":" foundation SP component-id SP -+ * "TCP" SP -+ * priority SP -+ * connection-address SP -+ * port SP -+ * cand-type -+ * [SP rel-addr] -+ * [SP rel-port] -+ * SP tcp-type-ext -+ * *(SP extension-att-name SP -+ * extension-att-value) -+ * -+ * tcp-type-ext = "tcptype" SP tcp-type -+ * tcp-type = "active" / "passive" / "so" -+ */ -+ PRINT("a=candidate:%.*s %u %s %u %s %u typ ", -+ (int)cand->foundation.slen, -+ cand->foundation.ptr, -+ (unsigned)cand->comp_id, -+ cand->transport == PJ_CAND_UDP? "UDP" : "TCP", -+ cand->prio, -+ pj_sockaddr_print(&cand->addr, ipaddr, -+ sizeof(ipaddr), 0), -+ (unsigned)pj_sockaddr_get_port(&cand->addr)); -+ -+ PRINT("%s", -+ pj_ice_get_cand_type_name(cand->type)); -+ -+ if (cand->transport != PJ_CAND_UDP) { -+ PRINT(" tcptype"); -+ switch (cand->transport) { -+ case PJ_CAND_TCP_ACTIVE: -+ PRINT(" active"); -+ break; -+ case PJ_CAND_TCP_PASSIVE: -+ PRINT(" passive"); -+ break; -+ case PJ_CAND_TCP_SO: -+ default: -+ PRINT(" so"); -+ break; -+ } -+ } -+ PRINT("\n"); - - if (p == buffer+maxlen) -- return -PJ_ETOOSMALL; -+ return -PJ_ETOOSMALL; - - *p = '\0'; - -@@ -608,6 +652,26 @@ static int encode_session(char buffer[], unsigned maxlen) - sizeof(ipaddr), 0)); - } - -+ if (cand[0].transport != PJ_CAND_UDP) { -+ /** RFC 6544, Section 4.5: -+ * If the default candidate is TCP-based, the agent MUST include the -+ * a=setup and a=connection attributes from RFC 4145 [RFC4145], -+ * following the procedures defined there as if ICE were not in use. -+ */ -+ PRINT("a=setup:"); -+ switch (cand[0].transport) { -+ case PJ_CAND_TCP_ACTIVE: -+ PRINT("active\n"); -+ break; -+ case PJ_CAND_TCP_PASSIVE: -+ PRINT("passive\n"); -+ break; -+ default: -+ return PJ_EINVALIDOP; -+ } -+ PRINT("a=connection:new\n"); -+ } -+ - /* Enumerate all candidates for this component */ - cand_cnt = PJ_ARRAY_SIZE(cand); - status = pj_ice_strans_enum_cands(icedemo.icest, comp+1, -@@ -702,231 +766,247 @@ static void icedemo_show_ice(void) - } - } - -- - /* -- * Input and parse SDP from the remote (containing remote's ICE information) -+ * Input and parse SDP from the remote (containing remote's ICE information) - * and save it to global variables. - */ --static void icedemo_input_remote(void) --{ -- char linebuf[80]; -- unsigned media_cnt = 0; -- unsigned comp0_port = 0; -- char comp0_addr[80]; -- pj_bool_t done = PJ_FALSE; -- -- puts("Paste SDP from remote host, end with empty line"); -- -- reset_rem_info(); -- -- comp0_addr[0] = '\0'; -- -- while (!done) { -- pj_size_t len; -- char *line; -- -- printf(">"); -- if (stdout) fflush(stdout); -- -- if (fgets(linebuf, sizeof(linebuf), stdin)==NULL) -- break; -- -- len = strlen(linebuf); -- while (len && (linebuf[len-1] == '\r' || linebuf[len-1] == '\n')) -- linebuf[--len] = '\0'; -- -- line = linebuf; -- while (len && pj_isspace(*line)) -- ++line, --len; -- -- if (len==0) -- break; -- -- /* Ignore subsequent media descriptors */ -- if (media_cnt > 1) -- continue; -- -- switch (line[0]) { -- case 'm': -- { -- int cnt; -- char media[32], portstr[32]; -- -- ++media_cnt; -- if (media_cnt > 1) { -- puts("Media line ignored"); -- break; -- } -- -- cnt = sscanf(line+2, "%s %s RTP/", media, portstr); -- if (cnt != 2) { -- PJ_LOG(1,(THIS_FILE, "Error parsing media line")); -- goto on_error; -- } -- -- comp0_port = atoi(portstr); -- -- } -- break; -- case 'c': -- { -- int cnt; -- char c[32], net[32], ip[80]; -- -- cnt = sscanf(line+2, "%s %s %s", c, net, ip); -- if (cnt != 3) { -- PJ_LOG(1,(THIS_FILE, "Error parsing connection line")); -- goto on_error; -- } -- -- strcpy(comp0_addr, ip); -- } -- break; -- case 'a': -- { -- char *attr = strtok(line+2, ": \t\r\n"); -- if (strcmp(attr, "ice-ufrag")==0) { -- strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1); -- } else if (strcmp(attr, "ice-pwd")==0) { -- strcpy(icedemo.rem.pwd, attr+strlen(attr)+1); -- } else if (strcmp(attr, "rtcp")==0) { -- char *val = attr+strlen(attr)+1; -- int af, cnt; -- int port; -- char net[32], ip[64]; -- pj_str_t tmp_addr; -- pj_status_t status; -- -- cnt = sscanf(val, "%d IN %s %s", &port, net, ip); -- if (cnt != 3) { -- PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute")); -- goto on_error; -- } -- -- if (strchr(ip, ':')) -- af = pj_AF_INET6(); -- else -- af = pj_AF_INET(); -- -- pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0); -- tmp_addr = pj_str(ip); -- status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1], -- &tmp_addr); -- if (status != PJ_SUCCESS) { -- PJ_LOG(1,(THIS_FILE, "Invalid IP address")); -- goto on_error; -- } -- pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port); -- -- } else if (strcmp(attr, "candidate")==0) { -- char *sdpcand = attr+strlen(attr)+1; -- int af, cnt; -- char foundation[32], transport[12], ipaddr[80], type[32]; -- pj_str_t tmpaddr; -- int comp_id, prio, port; -- pj_ice_sess_cand *cand; -- pj_status_t status; -- -- cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s", -- foundation, -- &comp_id, -- transport, -- &prio, -- ipaddr, -- &port, -- type); -- if (cnt != 7) { -- PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line")); -- goto on_error; -- } -- -- cand = &icedemo.rem.cand[icedemo.rem.cand_cnt]; -- pj_bzero(cand, sizeof(*cand)); -- -- if (strcmp(type, "host")==0) -- cand->type = PJ_ICE_CAND_TYPE_HOST; -- else if (strcmp(type, "srflx")==0) -- cand->type = PJ_ICE_CAND_TYPE_SRFLX; -- else if (strcmp(type, "relay")==0) -- cand->type = PJ_ICE_CAND_TYPE_RELAYED; -- else { -- PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", -- type)); -- goto on_error; -- } -- -- cand->comp_id = (pj_uint8_t)comp_id; -- pj_strdup2(icedemo.pool, &cand->foundation, foundation); -- cand->prio = prio; -- -- if (strchr(ipaddr, ':')) -- af = pj_AF_INET6(); -- else -- af = pj_AF_INET(); -- -- tmpaddr = pj_str(ipaddr); -- pj_sockaddr_init(af, &cand->addr, NULL, 0); -- status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr); -- if (status != PJ_SUCCESS) { -- PJ_LOG(1,(THIS_FILE, "Error: invalid IP address '%s'", -- ipaddr)); -- goto on_error; -- } -- -- pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port); -- -- ++icedemo.rem.cand_cnt; -- -- if (cand->comp_id > icedemo.rem.comp_cnt) -- icedemo.rem.comp_cnt = cand->comp_id; -- } -- } -- break; -- } -- } -- -- if (icedemo.rem.cand_cnt==0 || -- icedemo.rem.ufrag[0]==0 || -- icedemo.rem.pwd[0]==0 || -- icedemo.rem.comp_cnt == 0) -- { -- PJ_LOG(1, (THIS_FILE, "Error: not enough info")); -- goto on_error; -+static void icedemo_input_remote(void) { -+ char linebuf[120]; -+ unsigned media_cnt = 0; -+ unsigned comp0_port = 0; -+ char comp0_addr[80]; -+ pj_bool_t done = PJ_FALSE; -+ -+ puts("Paste SDP from remote host, end with empty line"); -+ -+ reset_rem_info(); -+ -+ comp0_addr[0] = '\0'; -+ -+ while (!done) { -+ pj_size_t len; -+ char *line; -+ -+ printf(">"); -+ if (stdout) -+ fflush(stdout); -+ -+ if (fgets(linebuf, sizeof(linebuf), stdin) == NULL) -+ break; -+ -+ len = strlen(linebuf); -+ while (len && (linebuf[len - 1] == '\r' || linebuf[len - 1] == '\n')) -+ linebuf[--len] = '\0'; -+ -+ line = linebuf; -+ while (len && pj_isspace(*line)) -+ ++line, --len; -+ -+ if (len == 0) -+ break; -+ -+ /* Ignore subsequent media descriptors */ -+ if (media_cnt > 1) -+ continue; -+ -+ switch (line[0]) { -+ case 'm': { -+ int cnt; -+ char media[32], portstr[32]; -+ -+ ++media_cnt; -+ if (media_cnt > 1) { -+ puts("Media line ignored"); -+ break; -+ } -+ -+ cnt = sscanf(line + 2, "%s %s RTP/", media, portstr); -+ if (cnt != 2) { -+ PJ_LOG(1, (THIS_FILE, "Error parsing media line")); -+ goto on_error; -+ } -+ -+ comp0_port = atoi(portstr); -+ -+ } break; -+ case 'c': { -+ int cnt; -+ char c[32], net[32], ip[80]; -+ -+ cnt = sscanf(line + 2, "%s %s %s", c, net, ip); -+ if (cnt != 3) { -+ PJ_LOG(1, (THIS_FILE, "Error parsing connection line")); -+ goto on_error; -+ } -+ -+ strcpy(comp0_addr, ip); -+ } break; -+ case 'a': { -+ char *attr = strtok(line + 2, ": \t\r\n"); -+ if (strcmp(attr, "ice-ufrag") == 0) { -+ strcpy(icedemo.rem.ufrag, attr + strlen(attr) + 1); -+ } else if (strcmp(attr, "ice-pwd") == 0) { -+ strcpy(icedemo.rem.pwd, attr + strlen(attr) + 1); -+ } else if (strcmp(attr, "rtcp") == 0) { -+ char *val = attr + strlen(attr) + 1; -+ int af, cnt; -+ int port; -+ char net[32], ip[64]; -+ pj_str_t tmp_addr; -+ pj_status_t status; -+ -+ cnt = sscanf(val, "%d IN %s %s", &port, net, ip); -+ if (cnt != 3) { -+ PJ_LOG(1, (THIS_FILE, "Error parsing rtcp attribute")); -+ goto on_error; -+ } -+ -+ if (strchr(ip, ':')) -+ af = pj_AF_INET6(); -+ else -+ af = pj_AF_INET(); -+ -+ pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0); -+ tmp_addr = pj_str(ip); -+ status = -+ pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1], &tmp_addr); -+ if (status != PJ_SUCCESS) { -+ PJ_LOG(1, (THIS_FILE, "Invalid IP address")); -+ goto on_error; -+ } -+ pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port); -+ -+ } else if (strcmp(attr, "candidate") == 0) { -+ /** Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544) -+ * candidate-attribute = "candidate" ":" foundation SP component-id -+ * SP "TCP" SP priority SP connection-address SP port SP cand-type [SP -+ * rel-addr] [SP rel-port] SP tcp-type-ext -+ * *(SP extension-att-name SP -+ * extension-att-value) -+ * -+ * tcp-type-ext = "tcptype" SP tcp-type -+ * tcp-type = "active" / "passive" / "so" -+ */ -+ char *sdpcand = attr + strlen(attr) + 1; -+ int af, cnt; -+ char foundation[32], transport[12], ipaddr[80], type[32], tcp_type[32]; -+ pj_str_t tmpaddr; -+ int comp_id, prio, port; -+ pj_ice_sess_cand *cand; -+ pj_status_t status; -+ pj_bool_t is_tcp = PJ_FALSE; -+ -+ cnt = -+ sscanf(sdpcand, "%s %d %s %d %s %d typ %s tcptype %s\n", foundation, -+ &comp_id, transport, &prio, ipaddr, &port, type, tcp_type); -+ if (cnt != 7 && cnt != 8) { -+ PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line", cnt)); -+ goto on_error; -+ } -+ -+ if (strcmp(transport, "TCP") == 0) { -+ is_tcp = PJ_TRUE; -+ } -+ -+ cand = &icedemo.rem.cand[icedemo.rem.cand_cnt]; -+ pj_bzero(cand, sizeof(*cand)); -+ -+ if (strcmp(type, "host") == 0) -+ cand->type = PJ_ICE_CAND_TYPE_HOST; -+ else if (strcmp(type, "srflx") == 0) -+ cand->type = PJ_ICE_CAND_TYPE_SRFLX; -+ else if (strcmp(type, "relay") == 0) -+ cand->type = PJ_ICE_CAND_TYPE_RELAYED; -+ else { -+ PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", type)); -+ goto on_error; -+ } -+ -+ if (is_tcp) { -+ if (strcmp(tcp_type, "active") == 0) -+ cand->transport = PJ_CAND_TCP_ACTIVE; -+ else if (strcmp(tcp_type, "passive") == 0) -+ cand->transport = PJ_CAND_TCP_PASSIVE; -+ else if (strcmp(tcp_type, "so") == 0) -+ cand->transport = PJ_CAND_TCP_SO; -+ else { -+ PJ_LOG(1, -+ (THIS_FILE, "Error: invalid transport type '%s'", tcp_type)); -+ goto on_error; -+ } -+ } else { -+ cand->transport = PJ_CAND_UDP; -+ } -+ -+ cand->comp_id = (pj_uint8_t)comp_id; -+ pj_strdup2(icedemo.pool, &cand->foundation, foundation); -+ cand->prio = prio; -+ -+ if (strchr(ipaddr, ':')) -+ af = pj_AF_INET6(); -+ else -+ af = pj_AF_INET(); -+ -+ tmpaddr = pj_str(ipaddr); -+ pj_sockaddr_init(af, &cand->addr, NULL, 0); -+ status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr); -+ if (status != PJ_SUCCESS) { -+ PJ_LOG(1, (THIS_FILE, "Error: invalid IP address '%s'", ipaddr)); -+ goto on_error; -+ } -+ -+ pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port); -+ -+ ++icedemo.rem.cand_cnt; -+ -+ if (cand->comp_id > icedemo.rem.comp_cnt) -+ icedemo.rem.comp_cnt = cand->comp_id; -+ } else if (strcmp(attr, "setup") == 0) { -+ // TODO -+ } else if (strcmp(attr, "connection") == 0) { -+ // TODO -+ } -+ } break; - } -+ } -+ -+ if (icedemo.rem.cand_cnt == 0 || icedemo.rem.ufrag[0] == 0 || -+ icedemo.rem.pwd[0] == 0 || icedemo.rem.comp_cnt == 0) { -+ PJ_LOG(1, (THIS_FILE, "Error: not enough info")); -+ goto on_error; -+ } -+ -+ if (comp0_port == 0 || comp0_addr[0] == '\0') { -+ PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found")); -+ goto on_error; -+ } else { -+ int af; -+ pj_str_t tmp_addr; -+ pj_status_t status; - -- if (comp0_port==0 || comp0_addr[0]=='\0') { -- PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found")); -- goto on_error; -- } else { -- int af; -- pj_str_t tmp_addr; -- pj_status_t status; -+ if (strchr(comp0_addr, ':')) -+ af = pj_AF_INET6(); -+ else -+ af = pj_AF_INET(); - -- if (strchr(comp0_addr, ':')) -- af = pj_AF_INET6(); -- else -- af = pj_AF_INET(); -- -- pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0); -- tmp_addr = pj_str(comp0_addr); -- status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0], -- &tmp_addr); -- if (status != PJ_SUCCESS) { -- PJ_LOG(1,(THIS_FILE, "Invalid IP address in c= line")); -- goto on_error; -- } -- pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port); -+ pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0); -+ tmp_addr = pj_str(comp0_addr); -+ status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0], &tmp_addr); -+ if (status != PJ_SUCCESS) { -+ PJ_LOG(1, (THIS_FILE, "Invalid IP address in c= line")); -+ goto on_error; - } -+ pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port); -+ } - -- PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", -- icedemo.rem.cand_cnt)); -- return; -+ PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", -+ icedemo.rem.cand_cnt)); -+ return; - - on_error: -- reset_rem_info(); -+ reset_rem_info(); - } - -- - /* - * Start ICE negotiation! This function is invoked from the menu. - */ -@@ -1213,52 +1293,53 @@ int main(int argc, char *argv[]) - icedemo.opt.max_host = -1; - - while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) { -- switch (c) { -- case 'c': -- icedemo.opt.comp_cnt = atoi(pj_optarg); -- if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) { -- puts("Invalid component count value"); -- return 1; -- } -- break; -- case 'n': -- icedemo.opt.ns = pj_str(pj_optarg); -- break; -- case 'H': -- icedemo.opt.max_host = atoi(pj_optarg); -- break; -- case 'h': -- icedemo_usage(); -- return 0; -- case 's': -- icedemo.opt.stun_srv = pj_str(pj_optarg); -- break; -- case 't': -- icedemo.opt.turn_srv = pj_str(pj_optarg); -- break; -- case 'T': -- icedemo.opt.turn_tcp = PJ_TRUE; -- break; -- case 'u': -- icedemo.opt.turn_username = pj_str(pj_optarg); -- break; -- case 'p': -- icedemo.opt.turn_password = pj_str(pj_optarg); -- break; -- case 'F': -- icedemo.opt.turn_fingerprint = PJ_TRUE; -- break; -- case 'R': -- icedemo.opt.regular = PJ_TRUE; -- break; -- case 'L': -- icedemo.opt.log_file = pj_optarg; -- break; -- default: -- printf("Argument \"%s\" is not valid. Use -h to see help", -- argv[pj_optind]); -- return 1; -- } -+ switch (c) { -+ case 'c': -+ icedemo.opt.comp_cnt = atoi(pj_optarg); -+ if (icedemo.opt.comp_cnt < 1 || -+ icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) { -+ puts("Invalid component count value"); -+ return 1; -+ } -+ break; -+ case 'n': -+ icedemo.opt.ns = pj_str(pj_optarg); -+ break; -+ case 'H': -+ icedemo.opt.max_host = atoi(pj_optarg); -+ break; -+ case 'h': -+ icedemo_usage(); -+ return 0; -+ case 's': -+ icedemo.opt.stun_srv = pj_str(pj_optarg); -+ break; -+ case 't': -+ icedemo.opt.turn_srv = pj_str(pj_optarg); -+ break; -+ case 'T': -+ icedemo.opt.ice_tcp = PJ_TRUE; -+ break; -+ case 'u': -+ icedemo.opt.turn_username = pj_str(pj_optarg); -+ break; -+ case 'p': -+ icedemo.opt.turn_password = pj_str(pj_optarg); -+ break; -+ case 'F': -+ icedemo.opt.turn_fingerprint = PJ_TRUE; -+ break; -+ case 'R': -+ icedemo.opt.regular = PJ_TRUE; -+ break; -+ case 'L': -+ icedemo.opt.log_file = pj_optarg; -+ break; -+ default: -+ printf("Argument \"%s\" is not valid. Use -h to see help", -+ argv[pj_optind]); -+ return 1; -+ } - } - - status = icedemo_init(); -diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c -index ea1e72b8..39d05d1e 100644 ---- a/pjsip/src/pjsua-lib/pjsua_core.c -+++ b/pjsip/src/pjsua-lib/pjsua_core.c -@@ -1529,10 +1529,10 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) - pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); - stun_sock_cb.on_status = &test_stun_on_status; - sess->async_wait = PJ_FALSE; -- status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve", -- sess->af, &stun_sock_cb, -- NULL, sess, &sess->stun_sock); -- if (status != PJ_SUCCESS) { -+ status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve", -+ sess->af, PJ_STUN_TP_UDP, &stun_sock_cb, -+ NULL, sess, &sess->stun_sock); -+ if (status != PJ_SUCCESS) { - char errmsg[PJ_ERR_MSG_SIZE]; - pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(4,(THIS_FILE, diff --git a/contrib/src/pjproject/rules.mak b/contrib/src/pjproject/rules.mak index 08e01c4dc48c422acdb9950c5eea0617df0400d9..85c3834315ded3d7d12b7b305d1b4f1e4315c5a6 100644 --- a/contrib/src/pjproject/rules.mak +++ b/contrib/src/pjproject/rules.mak @@ -1,10 +1,9 @@ # PJPROJECT -PJPROJECT_VERSION := 5dfa75be7d69047387f9b0436dd9492bbbf03fe4 +PJPROJECT_VERSION := 2.10 PJPROJECT_URL := https://github.com/pjsip/pjproject/archive/$(PJPROJECT_VERSION).tar.gz -PJPROJECT_OPTIONS := --disable-oss \ - --disable-sound \ - --disable-video \ +PJPROJECT_OPTIONS := --disable-sound \ + --enable-video \ --enable-ext-sound \ --disable-speex-aec \ --disable-g711-codec \ @@ -24,10 +23,6 @@ PJPROJECT_OPTIONS := --disable-oss \ --disable-libwebrtc \ --with-gnutls=$(PREFIX) -ifdef HAVE_WIN64 -PJPROJECT_EXTRA_CFLAGS += -DPJ_WIN64=1 -endif - PKGS += pjproject # FIXME: nominally 2.2.0 is enough, but it has to be patched for gnutls ifeq ($(call need_pkg,'libpjproject'),) @@ -35,10 +30,12 @@ PKGS_FOUND += pjproject endif DEPS_pjproject += gnutls -ifndef HAVE_WIN32 ifndef HAVE_MACOSX DEPS_pjproject += uuid endif + +ifdef HAVE_LINUX +PJPROJECT_OPTIONS += --enable-epoll endif $(TARBALLS)/pjproject-$(PJPROJECT_VERSION).tar.gz: @@ -48,31 +45,18 @@ $(TARBALLS)/pjproject-$(PJPROJECT_VERSION).tar.gz: pjproject: pjproject-$(PJPROJECT_VERSION).tar.gz .sum-pjproject $(UNPACK) -ifdef HAVE_WIN32 - $(APPLY) $(SRC)/pjproject/pj_win.patch -endif + $(APPLY) $(SRC)/pjproject/0001-rfc6544.patch + $(APPLY) $(SRC)/pjproject/0002-rfc2466.patch + $(APPLY) $(SRC)/pjproject/0003-add-tcp-keep-alive.patch + $(APPLY) $(SRC)/pjproject/0004-multiple_listeners.patch + $(APPLY) $(SRC)/pjproject/0005-fix_ebusy_turn.patch + $(APPLY) $(SRC)/pjproject/0006-ignore_ipv6_on_transport_check.patch + $(APPLY) $(SRC)/pjproject/0007-pj_ice_sess.patch + $(APPLY) $(SRC)/pjproject/0008-fix_ioqueue_ipv6_sendto.patch + $(APPLY) $(SRC)/pjproject/0009-add-config-site.patch ifdef HAVE_ANDROID - $(APPLY) $(SRC)/pjproject/android.patch + $(APPLY) $(SRC)/pjproject/0001-android.patch endif - $(APPLY) $(SRC)/pjproject/fix_turn_alloc_failure.patch - $(APPLY) $(SRC)/pjproject/rfc2466.patch - $(APPLY) $(SRC)/pjproject/ipv6.patch - $(APPLY) $(SRC)/pjproject/multiple_listeners.patch - $(APPLY) $(SRC)/pjproject/pj_ice_sess.patch - $(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/rfc6544.patch - $(APPLY) $(SRC)/pjproject/ice_config.patch - $(APPLY) $(SRC)/pjproject/sip_config.patch - $(APPLY) $(SRC)/pjproject/fix_first_packet_turn_tcp.patch - $(APPLY) $(SRC)/pjproject/fix_ebusy_turn.patch - $(APPLY) $(SRC)/pjproject/ignore_ipv6_on_transport_check.patch - $(APPLY) $(SRC)/pjproject/fix_turn_connection_failure.patch - $(APPLY) $(SRC)/pjproject/disable_local_resolution.patch - $(APPLY) $(SRC)/pjproject/fix_assert_on_connection_attempt.patch - $(APPLY) $(SRC)/pjproject/keep_alive.patch - $(APPLY) $(SRC)/pjproject/sip_td_timeout.patch $(UPDATE_AUTOCONFIG) $(MOVE) diff --git a/contrib/src/pjproject/sip_config.patch b/contrib/src/pjproject/sip_config.patch deleted file mode 100644 index 693a43c084adc739e26bbd91f6a8683e7579cd69..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/sip_config.patch +++ /dev/null @@ -1,23 +0,0 @@ - pjsip/include/pjsip/sip_config.h | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h -index 904df24e..f36b2d6a 100644 ---- a/pjsip/include/pjsip/sip_config.h -+++ b/pjsip/include/pjsip/sip_config.h -@@ -359,7 +359,7 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) - * containing presence information can be quite large (>1500). - */ - #ifndef PJSIP_MAX_PKT_LEN --# define PJSIP_MAX_PKT_LEN 4000 -+# define PJSIP_MAX_PKT_LEN 8000 - #endif - - -@@ -658,7 +658,7 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) - * Default: 600 - */ - #ifndef PJSIP_TRANSPORT_SERVER_IDLE_TIME --# define PJSIP_TRANSPORT_SERVER_IDLE_TIME 600 -+# define PJSIP_TRANSPORT_SERVER_IDLE_TIME 3 - #endif diff --git a/contrib/src/pjproject/uwp_vs.patch b/contrib/src/pjproject/uwp_vs.patch deleted file mode 100644 index 30c82e7d39ea325121f0459224f43aeac9d1f315..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/uwp_vs.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- a/pjlib/build/pjlib.vcxproj -+++ b/pjlib/build/pjlib.vcxproj -@@ -633,9 +633,11 @@ - </ClCompile> - <ClCompile Include="..\src\pj\ip_helper_win32.c"> - <ExcludedFromBuild Condition="'$(API_Family)'!='WinDesktop'">true</ExcludedFromBuild> -+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> - </ClCompile> - <ClCompile Include="..\src\pj\ip_helper_generic.c"> - <ExcludedFromBuild Condition="'$(API_Family)'=='WinDesktop'">true</ExcludedFromBuild> -+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild> - </ClCompile> - <ClCompile Include="..\src\pj\list.c" /> - <ClCompile Include="..\src\pj\lock.c" /> --- -2.10.2.windows.1 - diff --git a/contrib/src/pjproject/win_config.patch b/contrib/src/pjproject/win_config.patch deleted file mode 100644 index 70c790c23537c854de0941ecfa792c4f3034db68..0000000000000000000000000000000000000000 --- a/contrib/src/pjproject/win_config.patch +++ /dev/null @@ -1,9 +0,0 @@ ---- /dev/null -+++ b/pjlib/include/pj/config_site.h -@@ -0,0 +1,3 @@ -+#define THIRD_PARTY_MEDIA 0 -+#include "config_site_sample.h" -+ --- -2.10.2.windows.1 - diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp index 6e7d9b83b5df513d99acd4265712dc44faf8f111..7f7361740572be2f436bcad311fb4afb571b6a74 100644 --- a/src/ice_transport.cpp +++ b/src/ice_transport.cpp @@ -185,7 +185,7 @@ public: bool handleEvents(unsigned max_msec); // Wait data on components - std::vector<pj_ssize_t> lastReadLen_; + pj_ssize_t lastReadLen_; std::condition_variable waitDataCv_ = {}; onShutdownCb scb; @@ -307,7 +307,6 @@ IceTransport::Impl::Impl(const char* name, int component_count, bool master, } peerChannels_.resize(component_count_ + 1); - lastReadLen_.resize(component_count_); // Add local hosts (IPv4, IPv6) as stun candidates add_stun_server(config_, pj_AF_INET6()); @@ -338,13 +337,10 @@ IceTransport::Impl::Impl(const char* name, int component_count, bool master, JAMI_WARN("null IceTransport"); }; - icecb.on_data_sent = [](pj_ice_strans* ice_st, unsigned comp_id, - pj_ssize_t size) { + icecb.on_data_sent = [](pj_ice_strans* ice_st, pj_ssize_t size) { if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st))) { - if (comp_id > 0 && comp_id - 1 < tr->lastReadLen_.size()) { - tr->lastReadLen_[comp_id - 1] = size; + tr->lastReadLen_ = size; tr->waitDataCv_.notify_all(); - } } else JAMI_WARN("null IceTransport"); }; @@ -1313,15 +1309,15 @@ IceTransport::send(int comp_id, const unsigned char* buf, size_t len) return -1; } pj_ssize_t sent_size = 0; - auto status = pj_ice_strans_sendto2(pimpl_->icest_.get(), comp_id+1, buf, len, remote.pjPtr(), remote.getLength(), &sent_size); + auto status = pj_ice_strans_sendto2(pimpl_->icest_.get(), comp_id+1, buf, len, remote.pjPtr(), remote.getLength()); if (status == PJ_EPENDING && isTCPEnabled()) { auto current_size = sent_size; // NOTE; because we are in TCP, the sent size will count the header (2 // bytes length). - while (static_cast<std::size_t>(comp_id) < pimpl_->lastReadLen_.size() && current_size < static_cast<pj_ssize_t>(len)) { + while (current_size < static_cast<pj_ssize_t>(len)) { std::unique_lock<std::mutex> lk(pimpl_->iceMutex_); pimpl_->waitDataCv_.wait(lk); - current_size = pimpl_->lastReadLen_[comp_id]; + current_size = pimpl_->lastReadLen_; } } else if (status != PJ_SUCCESS && status != PJ_EPENDING) { if (status == PJ_EBUSY) {