No specific methodology other than checking the github CI output for the `clang-tidy` job, and fixing things one at a time.
1672 lines
50 KiB
C
1672 lines
50 KiB
C
/*
|
|
* Copyright (C) 2011, 2012, 2013 Citrix Systems
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the project nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "ns_turn_defs.h"
|
|
#include "ns_turn_ioalib.h" // for ioa_engine_handle
|
|
#include "ns_turn_msg.h"
|
|
#include "ns_turn_utils.h"
|
|
|
|
#include "apputils.h"
|
|
#include "session.h"
|
|
#include "startuclient.h"
|
|
#include "uclient.h"
|
|
|
|
#if defined(__MINGW32__)
|
|
#ifndef usleep
|
|
#define usleep Sleep
|
|
#endif
|
|
#endif
|
|
|
|
/////////////////////////////////////////
|
|
|
|
#define MAX_CONNECT_EFFORTS (77)
|
|
#define DTLS_MAX_CONNECT_TIMEOUT (30)
|
|
#define MAX_TLS_CYCLES (32)
|
|
#define EXTRA_CREATE_PERMS (25)
|
|
|
|
static uint64_t current_reservation_token = 0;
|
|
static int allocate_rtcp = 0;
|
|
static const int never_allocate_rtcp = 0;
|
|
|
|
static const unsigned char kALPNProtos[] = "\x08http/1.1\x09stun.turn\x12stun.nat-discovery";
|
|
static const size_t kALPNProtosLen = sizeof(kALPNProtos) - 1;
|
|
|
|
/////////////////////////////////////////
|
|
|
|
int rare_event(void) {
|
|
if (dos) {
|
|
return (((unsigned long)turn_random()) % 1000 == 777);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int not_rare_event(void) {
|
|
if (dos) {
|
|
return ((((unsigned long)turn_random()) % 1000) < 200);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int get_allocate_address_family(ioa_addr *relay_addr) {
|
|
if (relay_addr->ss.sa_family == AF_INET) {
|
|
return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_DEFAULT;
|
|
} else if (relay_addr->ss.sa_family == AF_INET6) {
|
|
return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6;
|
|
} else {
|
|
return STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_INVALID;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
|
|
static SSL *tls_connect(ioa_socket_raw fd, ioa_addr *remote_addr, bool *try_again, int connect_cycle) {
|
|
|
|
int ctxtype = (int)(((unsigned long)turn_random()) % root_tls_ctx_num);
|
|
|
|
SSL *ssl;
|
|
|
|
ssl = SSL_new(root_tls_ctx[ctxtype]);
|
|
|
|
SSL_set_alpn_protos(ssl, kALPNProtos, kALPNProtosLen);
|
|
|
|
if (use_tcp) {
|
|
SSL_set_fd(ssl, fd);
|
|
} else {
|
|
#if !DTLS_SUPPORTED
|
|
UNUSED_ARG(remote_addr);
|
|
fprintf(stderr, "ERROR: DTLS is not supported.\n");
|
|
exit(-1);
|
|
#else
|
|
/* Create BIO, connect and set to already connected */
|
|
BIO *bio = BIO_new_dgram(fd, BIO_CLOSE);
|
|
// bio = BIO_new_socket(fd, BIO_CLOSE);
|
|
|
|
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &remote_addr->ss);
|
|
|
|
SSL_set_bio(ssl, bio, bio);
|
|
|
|
{
|
|
struct timeval timeout;
|
|
/* Set and activate timeouts */
|
|
timeout.tv_sec = DTLS_MAX_CONNECT_TIMEOUT;
|
|
timeout.tv_usec = 0;
|
|
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
|
|
}
|
|
|
|
set_mtu_df(ssl, fd, remote_addr->ss.sa_family, SOSO_MTU, !use_tcp, clnet_verbose);
|
|
#endif
|
|
}
|
|
|
|
SSL_set_max_cert_list(ssl, 655350);
|
|
|
|
if (clnet_verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "call SSL_connect...\n");
|
|
}
|
|
|
|
int rc = 0;
|
|
|
|
do {
|
|
do {
|
|
rc = SSL_connect(ssl);
|
|
} while (rc < 0 && socket_eintr());
|
|
int orig_errno = socket_errno();
|
|
if (rc > 0) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: client session connected with cipher %s, method=%s\n", __FUNCTION__,
|
|
SSL_get_cipher(ssl), turn_get_ssl_method(ssl, NULL));
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
|
|
if (clnet_verbose && SSL_get1_peer_certificate(ssl)) {
|
|
#else
|
|
if (clnet_verbose && SSL_get_peer_certificate(ssl)) {
|
|
#endif
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "------------------------------------------------------------\n");
|
|
X509_NAME_print_ex_fp(stdout, X509_get_subject_name(SSL_get_peer_certificate(ssl)), 1, XN_FLAG_MULTILINE);
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n Cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n------------------------------------------------------------\n\n");
|
|
}
|
|
break;
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect: rc=%d, ctx=%d\n", __FUNCTION__, rc, ctxtype);
|
|
|
|
switch (SSL_get_error(ssl, rc)) {
|
|
case SSL_ERROR_WANT_READ:
|
|
case SSL_ERROR_WANT_WRITE:
|
|
if (!dos) {
|
|
usleep(1000);
|
|
}
|
|
continue;
|
|
default: {
|
|
char buf[1025];
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "errno=%d, err=%d, %s (%d)\n", orig_errno, (int)ERR_get_error(),
|
|
ERR_error_string(ERR_get_error(), buf), (int)SSL_get_error(ssl, rc));
|
|
if (connect_cycle < MAX_TLS_CYCLES) {
|
|
if (try_again) {
|
|
SSL_free(ssl);
|
|
*try_again = true;
|
|
return NULL;
|
|
}
|
|
}
|
|
exit(-1);
|
|
}
|
|
};
|
|
}
|
|
} while (1);
|
|
|
|
if (clnet_verbose && SSL_get_peer_certificate(ssl)) {
|
|
if (use_tcp) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "------TLS---------------------------------------------------\n");
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "------DTLS---------------------------------------------------\n");
|
|
}
|
|
X509_NAME_print_ex_fp(stdout, X509_get_subject_name(SSL_get_peer_certificate(ssl)), 1, XN_FLAG_MULTILINE);
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n Cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n------------------------------------------------------------\n\n");
|
|
}
|
|
|
|
return ssl;
|
|
}
|
|
|
|
int socket_connect(evutil_socket_t clnet_fd, ioa_addr *remote_addr, int *connect_err) {
|
|
if (addr_connect(clnet_fd, remote_addr, connect_err) < 0) {
|
|
if (*connect_err == EINPROGRESS) {
|
|
return 0;
|
|
}
|
|
if (*connect_err == EADDRINUSE) {
|
|
return +1;
|
|
}
|
|
perror("connect");
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect to remote addr: %d\n", __FUNCTION__, *connect_err);
|
|
exit(-1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clnet_connect(uint16_t clnet_remote_port, const char *remote_address, const unsigned char *ifname,
|
|
const char *local_address, bool verbose, app_ur_conn_info *clnet_info) {
|
|
|
|
ioa_addr local_addr;
|
|
int connect_cycle = 0;
|
|
|
|
ioa_addr remote_addr;
|
|
|
|
start_socket:
|
|
|
|
memset(&remote_addr, 0, sizeof(ioa_addr));
|
|
if (make_ioa_addr((const uint8_t *)remote_address, clnet_remote_port, &remote_addr) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
memset(&local_addr, 0, sizeof(ioa_addr));
|
|
|
|
evutil_socket_t clnet_fd = socket(
|
|
remote_addr.ss.sa_family,
|
|
use_sctp ? SCTP_CLIENT_STREAM_SOCKET_TYPE : (use_tcp ? CLIENT_STREAM_SOCKET_TYPE : CLIENT_DGRAM_SOCKET_TYPE),
|
|
use_sctp ? SCTP_CLIENT_STREAM_SOCKET_PROTOCOL
|
|
: (use_tcp ? CLIENT_STREAM_SOCKET_PROTOCOL : CLIENT_DGRAM_SOCKET_PROTOCOL));
|
|
if (clnet_fd < 0) {
|
|
perror("socket");
|
|
exit(-1);
|
|
}
|
|
|
|
if (sock_bind_to_device(clnet_fd, ifname) < 0) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", ifname);
|
|
}
|
|
|
|
set_sock_buf_size(clnet_fd, UR_CLIENT_SOCK_BUF_SIZE);
|
|
|
|
set_raw_socket_tos(clnet_fd, remote_addr.ss.sa_family, 0x22);
|
|
set_raw_socket_ttl(clnet_fd, remote_addr.ss.sa_family, 47);
|
|
|
|
if (clnet_info->is_peer && (*local_address == 0)) {
|
|
|
|
if (remote_addr.ss.sa_family == AF_INET6) {
|
|
if (make_ioa_addr((const uint8_t *)"::1", 0, &local_addr) < 0) {
|
|
socket_closesocket(clnet_fd);
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (make_ioa_addr((const uint8_t *)"127.0.0.1", 0, &local_addr) < 0) {
|
|
socket_closesocket(clnet_fd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
addr_bind(clnet_fd, &local_addr, 0, 1, get_socket_type());
|
|
|
|
} else if (strlen(local_address) > 0) {
|
|
|
|
if (make_ioa_addr((const uint8_t *)local_address, 0, &local_addr) < 0) {
|
|
socket_closesocket(clnet_fd);
|
|
return -1;
|
|
}
|
|
|
|
addr_bind(clnet_fd, &local_addr, 0, 1, get_socket_type());
|
|
}
|
|
|
|
int connect_err = 0;
|
|
if (clnet_info->is_peer) {
|
|
;
|
|
} else if (socket_connect(clnet_fd, &remote_addr, &connect_err) > 0) {
|
|
goto start_socket;
|
|
}
|
|
|
|
if (clnet_info) {
|
|
addr_cpy(&(clnet_info->remote_addr), &remote_addr);
|
|
addr_cpy(&(clnet_info->local_addr), &local_addr);
|
|
clnet_info->fd = clnet_fd;
|
|
addr_get_from_sock(clnet_fd, &(clnet_info->local_addr));
|
|
STRCPY(clnet_info->lsaddr, local_address);
|
|
STRCPY(clnet_info->rsaddr, remote_address);
|
|
STRCPY(clnet_info->ifname, (const char *)ifname);
|
|
}
|
|
|
|
if (use_secure) {
|
|
bool try_again = false;
|
|
clnet_info->ssl = tls_connect(clnet_info->fd, &remote_addr, &try_again, connect_cycle++);
|
|
if (!clnet_info->ssl) {
|
|
if (try_again) {
|
|
goto start_socket;
|
|
}
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot SSL connect to remote addr\n", __FUNCTION__);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
if (verbose && clnet_info) {
|
|
addr_debug_print(verbose, &(clnet_info->local_addr), "Connected from");
|
|
addr_debug_print(verbose, &remote_addr, "Connected to");
|
|
}
|
|
|
|
if (!dos) {
|
|
usleep(500);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int read_mobility_ticket(app_ur_conn_info *clnet_info, stun_buffer *message) {
|
|
int ret = 0;
|
|
if (clnet_info && message) {
|
|
stun_attr_ref s_mobile_id_sar = stun_attr_get_first_by_type(message, STUN_ATTRIBUTE_MOBILITY_TICKET);
|
|
if (s_mobile_id_sar) {
|
|
int smid_len = stun_attr_get_len(s_mobile_id_sar);
|
|
if (smid_len > 0 && (((size_t)smid_len) < sizeof(clnet_info->s_mobile_id))) {
|
|
const uint8_t *smid_val = stun_attr_get_value(s_mobile_id_sar);
|
|
if (smid_val) {
|
|
memcpy(clnet_info->s_mobile_id, smid_val, (size_t)smid_len);
|
|
clnet_info->s_mobile_id[smid_len] = 0;
|
|
if (clnet_verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: smid=%s\n", __FUNCTION__, clnet_info->s_mobile_id);
|
|
}
|
|
}
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: smid_len=%d\n", __FUNCTION__, smid_len);
|
|
ret = -1;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void add_origin(stun_buffer *message) {
|
|
if (message && origin[0]) {
|
|
const char *some_origin = "https://carleon.gov:443";
|
|
stun_attr_add(message, STUN_ATTRIBUTE_ORIGIN, some_origin, strlen(some_origin));
|
|
stun_attr_add(message, STUN_ATTRIBUTE_ORIGIN, origin, strlen(origin));
|
|
some_origin = "ftp://uffrith.net";
|
|
stun_attr_add(message, STUN_ATTRIBUTE_ORIGIN, some_origin, strlen(some_origin));
|
|
}
|
|
}
|
|
|
|
static int clnet_allocate(bool verbose, app_ur_conn_info *clnet_info, ioa_addr *relay_addr, int af, char *turn_addr,
|
|
uint16_t *turn_port) {
|
|
|
|
int af_cycle = 0;
|
|
bool reopen_socket = false;
|
|
|
|
bool allocate_finished;
|
|
|
|
stun_buffer request_message, response_message;
|
|
|
|
beg_allocate:
|
|
|
|
allocate_finished = false;
|
|
|
|
while (!allocate_finished && af_cycle++ < 32) {
|
|
|
|
bool allocate_sent = false;
|
|
|
|
if (reopen_socket && !use_tcp) {
|
|
socket_closesocket(clnet_info->fd);
|
|
clnet_info->fd = -1;
|
|
if (clnet_connect(addr_get_port(&(clnet_info->remote_addr)), clnet_info->rsaddr, (uint8_t *)clnet_info->ifname,
|
|
clnet_info->lsaddr, verbose, clnet_info) < 0) {
|
|
exit(-1);
|
|
}
|
|
reopen_socket = false;
|
|
}
|
|
|
|
int af4 = dual_allocation || (af == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4);
|
|
int af6 = dual_allocation || (af == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6);
|
|
|
|
uint64_t reservation_token = 0;
|
|
char *rt = NULL;
|
|
// TODO: Not a bool, can hold -1.
|
|
// What is this variable representing?
|
|
int ep = !no_rtcp && !dual_allocation;
|
|
|
|
if (!no_rtcp) {
|
|
if (!never_allocate_rtcp && allocate_rtcp) {
|
|
reservation_token = ioa_ntoh64(current_reservation_token);
|
|
rt = (char *)(&reservation_token);
|
|
}
|
|
}
|
|
|
|
if (is_TCP_relay()) {
|
|
ep = -1;
|
|
} else if (rt) {
|
|
ep = -1;
|
|
} else if (!ep) {
|
|
ep = (((uint8_t)turn_random()) % 2);
|
|
ep = ep - 1;
|
|
}
|
|
|
|
if (!dos) {
|
|
stun_set_allocate_request(&request_message, UCLIENT_SESSION_LIFETIME, af4, af6, relay_transport, mobility, rt,
|
|
ep);
|
|
} else {
|
|
stun_set_allocate_request(&request_message, UCLIENT_SESSION_LIFETIME / 3, af4, af6, relay_transport, mobility, rt,
|
|
ep);
|
|
}
|
|
|
|
if (bps) {
|
|
stun_attr_add_bandwidth_str(request_message.buf, &(request_message.len), bps);
|
|
}
|
|
|
|
if (dont_fragment) {
|
|
stun_attr_add(&request_message, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0);
|
|
}
|
|
|
|
add_origin(&request_message);
|
|
|
|
if (add_integrity(clnet_info, &request_message) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
stun_attr_add_fingerprint_str(request_message.buf, &(request_message.len));
|
|
|
|
while (!allocate_sent) {
|
|
|
|
int len = send_buffer(clnet_info, &request_message, 0, 0);
|
|
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "allocate sent\n");
|
|
}
|
|
allocate_sent = 1;
|
|
} else {
|
|
perror("send");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
////////////<<==allocate send
|
|
|
|
if (not_rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
////////allocate response==>>
|
|
{
|
|
bool allocate_received = false;
|
|
while (!allocate_received) {
|
|
|
|
int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message);
|
|
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "allocate response received: \n");
|
|
}
|
|
response_message.len = len;
|
|
int err_code = 0;
|
|
uint8_t err_msg[129];
|
|
if (stun_is_success_response(&response_message)) {
|
|
allocate_received = true;
|
|
allocate_finished = true;
|
|
|
|
if (clnet_info->nonce[0]) {
|
|
if (check_integrity(clnet_info, &response_message) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n");
|
|
}
|
|
{
|
|
bool found = false;
|
|
|
|
stun_attr_ref sar = stun_attr_get_first(&response_message);
|
|
while (sar) {
|
|
|
|
int attr_type = stun_attr_get_type(sar);
|
|
if (attr_type == STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS) {
|
|
|
|
if (!stun_attr_get_addr(&response_message, sar, relay_addr, NULL)) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: !!!: relay addr cannot be received (1)\n", __FUNCTION__);
|
|
return -1;
|
|
} else {
|
|
if (verbose) {
|
|
ioa_addr raddr;
|
|
memcpy(&raddr, relay_addr, sizeof(ioa_addr));
|
|
addr_debug_print(verbose, &raddr, "Received relay addr");
|
|
}
|
|
|
|
if (!addr_any(relay_addr)) {
|
|
if (relay_addr->ss.sa_family == AF_INET) {
|
|
if (default_address_family != STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) {
|
|
found = true;
|
|
addr_cpy(&(clnet_info->relay_addr), relay_addr);
|
|
break;
|
|
}
|
|
}
|
|
if (relay_addr->ss.sa_family == AF_INET6) {
|
|
if (default_address_family == STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6) {
|
|
found = true;
|
|
addr_cpy(&(clnet_info->relay_addr), relay_addr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sar = stun_attr_get_next(&response_message, sar);
|
|
}
|
|
|
|
if (!found) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: !!!: relay addr cannot be received (2)\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
stun_attr_ref rt_sar = stun_attr_get_first_by_type(&response_message, STUN_ATTRIBUTE_RESERVATION_TOKEN);
|
|
uint64_t rtv = stun_attr_get_reservation_token_value(rt_sar);
|
|
current_reservation_token = rtv;
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: rtv=%llu\n", __FUNCTION__, (long long unsigned int)rtv);
|
|
}
|
|
|
|
read_mobility_ticket(clnet_info, &response_message);
|
|
|
|
} else if (stun_is_challenge_response_str(response_message.buf, response_message.len, &err_code, err_msg,
|
|
sizeof(err_msg), clnet_info->realm, clnet_info->nonce,
|
|
clnet_info->server_name, &(clnet_info->oauth))) {
|
|
goto beg_allocate;
|
|
} else if (stun_is_error_response(&response_message, &err_code, err_msg, sizeof(err_msg))) {
|
|
|
|
allocate_received = true;
|
|
|
|
if (err_code == 300) {
|
|
|
|
if (clnet_info->nonce[0]) {
|
|
if (check_integrity(clnet_info, &response_message) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ioa_addr alternate_server;
|
|
if (!stun_attr_get_first_addr(&response_message, STUN_ATTRIBUTE_ALTERNATE_SERVER, &alternate_server,
|
|
NULL)) {
|
|
// error
|
|
} else if (turn_addr && turn_port) {
|
|
addr_to_string_no_port(&alternate_server, (uint8_t *)turn_addr);
|
|
*turn_port = (uint16_t)addr_get_port(&alternate_server);
|
|
}
|
|
}
|
|
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "error %d (%s)\n", err_code, (char *)err_msg);
|
|
if (err_code != 437) {
|
|
current_reservation_token = 0;
|
|
return -1;
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "trying allocate again %d...\n", err_code);
|
|
sleep(1);
|
|
reopen_socket = true;
|
|
}
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown allocate response\n");
|
|
/* Try again ? */
|
|
}
|
|
} else {
|
|
perror("recv");
|
|
exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
////////////<<== allocate response received
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (!allocate_finished) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot complete Allocation\n");
|
|
exit(-1);
|
|
}
|
|
|
|
allocate_rtcp = !allocate_rtcp;
|
|
|
|
if (true) {
|
|
|
|
af_cycle = 0;
|
|
|
|
if (clnet_info->s_mobile_id[0]) {
|
|
|
|
int fd = clnet_info->fd;
|
|
SSL *ssl = clnet_info->ssl;
|
|
|
|
bool close_now = turn_random() % 2;
|
|
|
|
if (close_now) {
|
|
bool close_socket = (int)(turn_random() % 2);
|
|
if (ssl && !close_socket) {
|
|
SSL_shutdown(ssl);
|
|
SSL_free(ssl);
|
|
fd = -1;
|
|
} else if (fd >= 0) {
|
|
socket_closesocket(fd);
|
|
fd = -1;
|
|
ssl = NULL;
|
|
}
|
|
}
|
|
|
|
app_ur_conn_info ci;
|
|
memcpy(&ci, clnet_info, sizeof(ci));
|
|
ci.fd = -1;
|
|
ci.ssl = NULL;
|
|
clnet_info->fd = -1;
|
|
clnet_info->ssl = NULL;
|
|
// Reopen:
|
|
if (clnet_connect(addr_get_port(&(ci.remote_addr)), ci.rsaddr, (unsigned char *)ci.ifname, ci.lsaddr,
|
|
clnet_verbose, clnet_info) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (ssl) {
|
|
SSL_shutdown(ssl);
|
|
SSL_free(ssl);
|
|
} else if (fd >= 0) {
|
|
socket_closesocket(fd);
|
|
}
|
|
}
|
|
|
|
beg_refresh:
|
|
|
|
if (af_cycle++ > 32) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot complete Refresh\n");
|
|
exit(-1);
|
|
}
|
|
|
|
//==>>refresh request, for an example only:
|
|
{
|
|
bool refresh_sent = false;
|
|
|
|
stun_init_request(STUN_METHOD_REFRESH, &request_message);
|
|
uint32_t lt = htonl(UCLIENT_SESSION_LIFETIME);
|
|
stun_attr_add(&request_message, STUN_ATTRIBUTE_LIFETIME, (const char *)<, 4);
|
|
|
|
if (clnet_info->s_mobile_id[0]) {
|
|
stun_attr_add(&request_message, STUN_ATTRIBUTE_MOBILITY_TICKET, (const char *)clnet_info->s_mobile_id,
|
|
strlen(clnet_info->s_mobile_id));
|
|
}
|
|
|
|
if (dual_allocation && !mobility) {
|
|
// TODO: This could be reworked
|
|
// it's using t as a tri-state to determine whether to add the requested address family field
|
|
// it should be two seperate bools for readability purposes though.
|
|
uint8_t t = ((uint8_t)turn_random()) % 3;
|
|
if (t) {
|
|
uint8_t field[4];
|
|
field[0] = (t == 1) ? (uint8_t)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4
|
|
: (uint8_t)STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6;
|
|
field[1] = 0;
|
|
field[2] = 0;
|
|
field[3] = 0;
|
|
stun_attr_add(&request_message, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, (const char *)field, 4);
|
|
}
|
|
}
|
|
|
|
add_origin(&request_message);
|
|
|
|
if (add_integrity(clnet_info, &request_message) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
stun_attr_add_fingerprint_str(request_message.buf, &(request_message.len));
|
|
|
|
while (!refresh_sent) {
|
|
|
|
int len = send_buffer(clnet_info, &request_message, 0, 0);
|
|
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "refresh sent\n");
|
|
}
|
|
refresh_sent = true;
|
|
|
|
if (clnet_info->s_mobile_id[0]) {
|
|
usleep(10000);
|
|
send_buffer(clnet_info, &request_message, 0, 0);
|
|
}
|
|
} else {
|
|
perror("send");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (not_rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
////////refresh response==>>
|
|
{
|
|
bool refresh_received = false;
|
|
while (!refresh_received) {
|
|
|
|
int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message);
|
|
|
|
if (clnet_info->s_mobile_id[0]) {
|
|
len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message);
|
|
}
|
|
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "refresh response received: \n");
|
|
}
|
|
response_message.len = len;
|
|
int err_code = 0;
|
|
uint8_t err_msg[129];
|
|
if (stun_is_success_response(&response_message)) {
|
|
read_mobility_ticket(clnet_info, &response_message);
|
|
refresh_received = true;
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n");
|
|
}
|
|
} else if (stun_is_challenge_response_str(response_message.buf, response_message.len, &err_code, err_msg,
|
|
sizeof(err_msg), clnet_info->realm, clnet_info->nonce,
|
|
clnet_info->server_name, &(clnet_info->oauth))) {
|
|
goto beg_refresh;
|
|
} else if (stun_is_error_response(&response_message, &err_code, err_msg, sizeof(err_msg))) {
|
|
refresh_received = true;
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "error %d (%s)\n", err_code, (char *)err_msg);
|
|
return -1;
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown refresh response\n");
|
|
/* Try again ? */
|
|
}
|
|
} else {
|
|
perror("recv");
|
|
exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int turn_channel_bind(bool verbose, uint16_t *chn, app_ur_conn_info *clnet_info, ioa_addr *peer_addr) {
|
|
|
|
stun_buffer request_message, response_message;
|
|
|
|
beg_bind:
|
|
|
|
if (negative_test) {
|
|
*chn = stun_set_channel_bind_request(&request_message, peer_addr, (uint16_t)turn_random());
|
|
} else {
|
|
*chn = stun_set_channel_bind_request(&request_message, peer_addr, *chn);
|
|
}
|
|
|
|
add_origin(&request_message);
|
|
|
|
if (add_integrity(clnet_info, &request_message) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
stun_attr_add_fingerprint_str(request_message.buf, &(request_message.len));
|
|
|
|
bool cb_sent = false;
|
|
|
|
while (!cb_sent) {
|
|
|
|
int len = send_buffer(clnet_info, &request_message, 0, 0);
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "channel bind sent\n");
|
|
}
|
|
cb_sent = true;
|
|
} else {
|
|
perror("send");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
////////////<<==channel bind send
|
|
|
|
if (not_rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
////////channel bind response==>>
|
|
|
|
{
|
|
bool cb_received = false;
|
|
while (!cb_received) {
|
|
|
|
int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message);
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "cb response received: \n");
|
|
}
|
|
int err_code = 0;
|
|
uint8_t err_msg[129];
|
|
if (stun_is_success_response(&response_message)) {
|
|
|
|
cb_received = true;
|
|
|
|
if (clnet_info->nonce[0]) {
|
|
if (check_integrity(clnet_info, &response_message) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success: 0x%x\n", (int)(*chn));
|
|
}
|
|
} else if (stun_is_challenge_response_str(response_message.buf, response_message.len, &err_code, err_msg,
|
|
sizeof(err_msg), clnet_info->realm, clnet_info->nonce,
|
|
clnet_info->server_name, &(clnet_info->oauth))) {
|
|
goto beg_bind;
|
|
} else if (stun_is_error_response(&response_message, &err_code, err_msg, sizeof(err_msg))) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "channel bind: error %d (%s)\n", err_code, (char *)err_msg);
|
|
return -1;
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown channel bind response\n");
|
|
/* Try again ? */
|
|
}
|
|
} else {
|
|
perror("recv");
|
|
exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int turn_create_permission(bool verbose, app_ur_conn_info *clnet_info, ioa_addr *peer_addr, int addrnum) {
|
|
|
|
if (no_permissions || (addrnum < 1)) {
|
|
return 0;
|
|
}
|
|
|
|
char saddr[129] = "\0";
|
|
if (verbose) {
|
|
addr_to_string(peer_addr, (uint8_t *)saddr);
|
|
}
|
|
|
|
stun_buffer request_message, response_message;
|
|
|
|
beg_cp:
|
|
|
|
stun_init_request(STUN_METHOD_CREATE_PERMISSION, &request_message);
|
|
for (int addrindex = 0; addrindex < addrnum; ++addrindex) {
|
|
stun_attr_add_addr(&request_message, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr + addrindex);
|
|
}
|
|
|
|
add_origin(&request_message);
|
|
|
|
if (add_integrity(clnet_info, &request_message) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
stun_attr_add_fingerprint_str(request_message.buf, &(request_message.len));
|
|
|
|
bool cp_sent = false;
|
|
|
|
while (!cp_sent) {
|
|
|
|
int len = send_buffer(clnet_info, &request_message, 0, 0);
|
|
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "create perm sent: %s\n", saddr);
|
|
}
|
|
cp_sent = true;
|
|
} else {
|
|
perror("send");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
////////////<<==create permission send
|
|
|
|
if (not_rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
////////create permission response==>>
|
|
|
|
{
|
|
bool cp_received = false;
|
|
while (!cp_received) {
|
|
|
|
int len = recv_buffer(clnet_info, &response_message, 1, 0, NULL, &request_message);
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "cp response received: \n");
|
|
}
|
|
int err_code = 0;
|
|
uint8_t err_msg[129];
|
|
if (stun_is_success_response(&response_message)) {
|
|
|
|
cp_received = true;
|
|
|
|
if (clnet_info->nonce[0]) {
|
|
if (check_integrity(clnet_info, &response_message) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n");
|
|
}
|
|
} else if (stun_is_challenge_response_str(response_message.buf, response_message.len, &err_code, err_msg,
|
|
sizeof(err_msg), clnet_info->realm, clnet_info->nonce,
|
|
clnet_info->server_name, &(clnet_info->oauth))) {
|
|
goto beg_cp;
|
|
} else if (stun_is_error_response(&response_message, &err_code, err_msg, sizeof(err_msg))) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "create permission error %d (%s)\n", err_code, (char *)err_msg);
|
|
return -1;
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown create permission response\n");
|
|
/* Try again ? */
|
|
}
|
|
} else {
|
|
perror("recv");
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int start_connection(uint16_t clnet_remote_port0, const char *remote_address0, const unsigned char *ifname,
|
|
const char *local_address, bool verbose, app_ur_conn_info *clnet_info_probe,
|
|
app_ur_conn_info *clnet_info, uint16_t *chn, app_ur_conn_info *clnet_info_rtcp,
|
|
uint16_t *chn_rtcp) {
|
|
|
|
ioa_addr relay_addr;
|
|
ioa_addr relay_addr_rtcp;
|
|
ioa_addr peer_addr_rtcp;
|
|
|
|
addr_cpy(&peer_addr_rtcp, &peer_addr);
|
|
addr_set_port(&peer_addr_rtcp, addr_get_port(&peer_addr_rtcp) + 1);
|
|
|
|
/* Probe: */
|
|
|
|
if (clnet_connect(clnet_remote_port0, remote_address0, ifname, local_address, verbose, clnet_info_probe) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
uint16_t clnet_remote_port = clnet_remote_port0;
|
|
char remote_address[1025];
|
|
STRCPY(remote_address, remote_address0);
|
|
|
|
clnet_allocate(verbose, clnet_info_probe, &relay_addr, default_address_family, remote_address, &clnet_remote_port);
|
|
|
|
/* Real: */
|
|
|
|
*chn = 0;
|
|
if (chn_rtcp) {
|
|
*chn_rtcp = 0;
|
|
}
|
|
|
|
if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
int af = default_address_family ? default_address_family : get_allocate_address_family(&peer_addr);
|
|
if (clnet_allocate(verbose, clnet_info, &relay_addr, af, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
af = default_address_family ? default_address_family : get_allocate_address_family(&peer_addr_rtcp);
|
|
if (clnet_allocate(verbose, clnet_info_rtcp, &relay_addr_rtcp, af, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!dos) {
|
|
if (!do_not_use_channel) {
|
|
/* These multiple "channel bind" requests are here only because
|
|
* we are playing with the TURN server trying to screw it */
|
|
if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
*chn = 0;
|
|
if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (turn_channel_bind(verbose, chn, clnet_info, &peer_addr) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (extra_requests) {
|
|
const char *sarbaddr = "164.156.178.190";
|
|
if (turn_random() % 2 == 0) {
|
|
sarbaddr = "2001::172";
|
|
}
|
|
ioa_addr arbaddr;
|
|
make_ioa_addr((const uint8_t *)sarbaddr, 333, &arbaddr);
|
|
int maxi = (unsigned short)turn_random() % EXTRA_CREATE_PERMS;
|
|
for (int i = 0; i < maxi; i++) {
|
|
uint16_t chni = 0;
|
|
int port = (unsigned short)turn_random();
|
|
if (port < 1024) {
|
|
port += 1024;
|
|
}
|
|
addr_set_port(&arbaddr, port);
|
|
uint8_t *u = (uint8_t *)&(arbaddr.s4.sin_addr);
|
|
u[(unsigned short)turn_random() % 4] = u[(unsigned short)turn_random() % 4] + 1;
|
|
// char sss[128];
|
|
// addr_to_string(&arbaddr,(uint8_t*)sss);
|
|
// printf("%s: 111.111: %s\n",__FUNCTION__,sss);
|
|
turn_channel_bind(verbose, &chni, clnet_info, &arbaddr);
|
|
}
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
if (turn_channel_bind(verbose, chn_rtcp, clnet_info_rtcp, &peer_addr_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (extra_requests) {
|
|
const char *sarbaddr = "64.56.78.90";
|
|
if (turn_random() % 2 == 0) {
|
|
sarbaddr = "2001::172";
|
|
}
|
|
ioa_addr arbaddr[EXTRA_CREATE_PERMS];
|
|
make_ioa_addr((const uint8_t *)sarbaddr, 333, &arbaddr[0]);
|
|
int maxi = (unsigned short)turn_random() % EXTRA_CREATE_PERMS;
|
|
for (int i = 0; i < maxi; i++) {
|
|
if (i > 0) {
|
|
addr_cpy(&arbaddr[i], &arbaddr[0]);
|
|
}
|
|
addr_set_port(&arbaddr[i], (unsigned short)turn_random());
|
|
uint8_t *u = (uint8_t *)&(arbaddr[i].s4.sin_addr);
|
|
u[(unsigned short)turn_random() % 4] = u[(unsigned short)turn_random() % 4] + 1;
|
|
// char sss[128];
|
|
// addr_to_string(&arbaddr[i],(uint8_t*)sss);
|
|
// printf("%s: 111.111: %s\n",__FUNCTION__,sss);
|
|
}
|
|
turn_create_permission(verbose, clnet_info, arbaddr, maxi);
|
|
}
|
|
} else {
|
|
|
|
bool const before = turn_random() % 2;
|
|
|
|
if (before) {
|
|
if (turn_create_permission(verbose, clnet_info, &peer_addr, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (turn_create_permission(verbose, clnet_info, &peer_addr_rtcp, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (extra_requests) {
|
|
const char *sarbaddr = "64.56.78.90";
|
|
if (turn_random() % 2) {
|
|
sarbaddr = "2001::172";
|
|
}
|
|
ioa_addr arbaddr[EXTRA_CREATE_PERMS];
|
|
make_ioa_addr((const uint8_t *)sarbaddr, 333, &arbaddr[0]);
|
|
int maxi = (unsigned short)turn_random() % EXTRA_CREATE_PERMS;
|
|
for (int i = 0; i < maxi; i++) {
|
|
if (i > 0) {
|
|
addr_cpy(&arbaddr[i], &arbaddr[0]);
|
|
}
|
|
addr_set_port(&arbaddr[i], (unsigned short)turn_random());
|
|
uint8_t *u = (uint8_t *)&(arbaddr[i].s4.sin_addr);
|
|
u[(unsigned short)turn_random() % 4] = u[(unsigned short)turn_random() % 4] + 1;
|
|
// char sss[128];
|
|
// addr_to_string(&arbaddr,(uint8_t*)sss);
|
|
// printf("%s: 111.111: %s\n",__FUNCTION__,sss);
|
|
}
|
|
turn_create_permission(verbose, clnet_info, arbaddr, maxi);
|
|
}
|
|
|
|
if (!before) {
|
|
if (turn_create_permission(verbose, clnet_info, &peer_addr, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (turn_create_permission(verbose, clnet_info, &peer_addr_rtcp, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
if (turn_create_permission(verbose, clnet_info_rtcp, &peer_addr_rtcp, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (turn_create_permission(verbose, clnet_info_rtcp, &peer_addr, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
addr_cpy(&(clnet_info->peer_addr), &peer_addr);
|
|
if (!no_rtcp) {
|
|
addr_cpy(&(clnet_info_rtcp->peer_addr), &peer_addr_rtcp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int start_c2c_connection(uint16_t clnet_remote_port0, const char *remote_address0, const unsigned char *ifname,
|
|
const char *local_address, bool verbose, app_ur_conn_info *clnet_info_probe,
|
|
app_ur_conn_info *clnet_info1, uint16_t *chn1, app_ur_conn_info *clnet_info1_rtcp,
|
|
uint16_t *chn1_rtcp, app_ur_conn_info *clnet_info2, uint16_t *chn2,
|
|
app_ur_conn_info *clnet_info2_rtcp, uint16_t *chn2_rtcp) {
|
|
|
|
ioa_addr relay_addr1;
|
|
ioa_addr relay_addr1_rtcp;
|
|
|
|
ioa_addr relay_addr2;
|
|
ioa_addr relay_addr2_rtcp;
|
|
|
|
*chn1 = 0;
|
|
*chn2 = 0;
|
|
if (chn1_rtcp) {
|
|
*chn1_rtcp = 0;
|
|
}
|
|
if (chn2_rtcp) {
|
|
*chn2_rtcp = 0;
|
|
}
|
|
|
|
/* Probe: */
|
|
|
|
if (clnet_connect(clnet_remote_port0, remote_address0, ifname, local_address, verbose, clnet_info_probe) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
uint16_t clnet_remote_port = clnet_remote_port0;
|
|
char remote_address[1025];
|
|
STRCPY(remote_address, remote_address0);
|
|
|
|
clnet_allocate(verbose, clnet_info_probe, &relay_addr1, default_address_family, remote_address, &clnet_remote_port);
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
/* Real: */
|
|
|
|
if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info1) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info1_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
if (passive_tcp) {
|
|
clnet_info2->is_peer = true;
|
|
}
|
|
|
|
if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info2) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
if (clnet_connect(clnet_remote_port, remote_address, ifname, local_address, verbose, clnet_info2_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
|
|
if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (clnet_allocate(verbose, clnet_info1_rtcp, &relay_addr1_rtcp, default_address_family, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (clnet_allocate(verbose, clnet_info2_rtcp, &relay_addr2_rtcp, default_address_family, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
|
|
if (clnet_allocate(verbose, clnet_info1, &relay_addr1, default_address_family, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (!(clnet_info2->is_peer)) {
|
|
if (clnet_allocate(verbose, clnet_info2, &relay_addr2, default_address_family, NULL, NULL) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
addr_cpy(&(clnet_info2->remote_addr), &relay_addr1);
|
|
addr_cpy(&relay_addr2, &(clnet_info2->local_addr));
|
|
}
|
|
}
|
|
|
|
if (!do_not_use_channel) {
|
|
if (turn_channel_bind(verbose, chn1, clnet_info1, &relay_addr2) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (extra_requests) {
|
|
const char *sarbaddr = "164.156.178.190";
|
|
if (turn_random() % 2 == 0) {
|
|
sarbaddr = "2001::172";
|
|
}
|
|
ioa_addr arbaddr;
|
|
make_ioa_addr((const uint8_t *)sarbaddr, 333, &arbaddr);
|
|
int maxi = (unsigned short)turn_random() % EXTRA_CREATE_PERMS;
|
|
for (int i = 0; i < maxi; i++) {
|
|
uint16_t chni = 0;
|
|
int port = (unsigned short)turn_random();
|
|
if (port < 1024) {
|
|
port += 1024;
|
|
}
|
|
addr_set_port(&arbaddr, port);
|
|
uint8_t *u = (uint8_t *)&(arbaddr.s4.sin_addr);
|
|
u[(unsigned short)turn_random() % 4] = u[(unsigned short)turn_random() % 4] + 1;
|
|
// char sss[128];
|
|
// addr_to_string(&arbaddr,(uint8_t*)sss);
|
|
// printf("%s: 111.111: %s\n",__FUNCTION__,sss);
|
|
turn_channel_bind(verbose, &chni, clnet_info1, &arbaddr);
|
|
}
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
if (extra_requests) {
|
|
const char *sarbaddr = "64.56.78.90";
|
|
if (turn_random() % 2 == 0) {
|
|
sarbaddr = "2001::172";
|
|
}
|
|
ioa_addr arbaddr[EXTRA_CREATE_PERMS];
|
|
make_ioa_addr((const uint8_t *)sarbaddr, 333, &arbaddr[0]);
|
|
int maxi = (unsigned short)turn_random() % EXTRA_CREATE_PERMS;
|
|
for (int i = 0; i < maxi; i++) {
|
|
if (i > 0) {
|
|
addr_cpy(&arbaddr[i], &arbaddr[0]);
|
|
}
|
|
addr_set_port(&arbaddr[i], (unsigned short)turn_random());
|
|
uint8_t *u = (uint8_t *)&(arbaddr[i].s4.sin_addr);
|
|
u[(unsigned short)turn_random() % 4] = u[(unsigned short)turn_random() % 4] + 1;
|
|
// char sss[128];
|
|
// addr_to_string(&arbaddr[i],(uint8_t*)sss);
|
|
// printf("%s: 111.111: %s\n",__FUNCTION__,sss);
|
|
}
|
|
turn_create_permission(verbose, clnet_info1, arbaddr, maxi);
|
|
}
|
|
|
|
if (!no_rtcp) {
|
|
if (turn_channel_bind(verbose, chn1_rtcp, clnet_info1_rtcp, &relay_addr2_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (turn_channel_bind(verbose, chn2, clnet_info2, &relay_addr1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (!no_rtcp) {
|
|
if (turn_channel_bind(verbose, chn2_rtcp, clnet_info2_rtcp, &relay_addr1_rtcp) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
|
|
if (turn_create_permission(verbose, clnet_info1, &relay_addr2, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (extra_requests) {
|
|
const char *sarbaddr = "64.56.78.90";
|
|
if (turn_random() % 2 == 0) {
|
|
sarbaddr = "2001::172";
|
|
}
|
|
ioa_addr arbaddr;
|
|
make_ioa_addr((const uint8_t *)sarbaddr, 333, &arbaddr);
|
|
int maxi = (unsigned short)turn_random() % EXTRA_CREATE_PERMS;
|
|
for (int i = 0; i < maxi; i++) {
|
|
addr_set_port(&arbaddr, (unsigned short)turn_random());
|
|
uint8_t *u = (uint8_t *)&(arbaddr.s4.sin_addr);
|
|
u[(unsigned short)turn_random() % 4] = u[(unsigned short)turn_random() % 4] + 1;
|
|
// char sss[128];
|
|
// addr_to_string(&arbaddr,(uint8_t*)sss);
|
|
// printf("%s: 111.111: %s\n",__FUNCTION__,sss);
|
|
turn_create_permission(verbose, clnet_info1, &arbaddr, 1);
|
|
}
|
|
}
|
|
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (!no_rtcp) {
|
|
if (turn_create_permission(verbose, clnet_info1_rtcp, &relay_addr2_rtcp, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
if (!(clnet_info2->is_peer)) {
|
|
if (turn_create_permission(verbose, clnet_info2, &relay_addr1, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (!no_rtcp) {
|
|
if (turn_create_permission(verbose, clnet_info2_rtcp, &relay_addr1_rtcp, 1) < 0) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
if (rare_event()) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
addr_cpy(&(clnet_info1->peer_addr), &relay_addr2);
|
|
if (!no_rtcp) {
|
|
addr_cpy(&(clnet_info1_rtcp->peer_addr), &relay_addr2_rtcp);
|
|
}
|
|
addr_cpy(&(clnet_info2->peer_addr), &relay_addr1);
|
|
if (!no_rtcp) {
|
|
addr_cpy(&(clnet_info2_rtcp->peer_addr), &relay_addr1_rtcp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//////////// RFC 6062 ///////////////
|
|
|
|
int turn_tcp_connect(bool verbose, app_ur_conn_info *clnet_info, ioa_addr *peer_addr) {
|
|
|
|
{
|
|
bool cp_sent = false;
|
|
|
|
stun_buffer message;
|
|
|
|
stun_init_request(STUN_METHOD_CONNECT, &message);
|
|
stun_attr_add_addr(&message, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, peer_addr);
|
|
|
|
add_origin(&message);
|
|
|
|
if (add_integrity(clnet_info, &message) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
stun_attr_add_fingerprint_str(message.buf, (size_t *)&(message.len));
|
|
|
|
while (!cp_sent) {
|
|
|
|
int len = send_buffer(clnet_info, &message, 0, 0);
|
|
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "tcp connect sent\n");
|
|
}
|
|
cp_sent = true;
|
|
} else {
|
|
perror("send");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////<<==connect send
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int turn_tcp_connection_bind(int verbose, app_ur_conn_info *clnet_info, app_tcp_conn_info *atc, int errorOK) {
|
|
|
|
stun_buffer request_message, response_message;
|
|
|
|
beg_cb:
|
|
|
|
stun_init_request(STUN_METHOD_CONNECTION_BIND, &request_message);
|
|
|
|
uint32_t cid = atc->cid;
|
|
|
|
stun_attr_add(&request_message, STUN_ATTRIBUTE_CONNECTION_ID, (const char *)&cid, 4);
|
|
|
|
add_origin(&request_message);
|
|
|
|
if (add_integrity(clnet_info, &request_message) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
stun_attr_add_fingerprint_str(request_message.buf, (size_t *)&(request_message.len));
|
|
|
|
bool cb_sent = false;
|
|
|
|
while (!cb_sent) {
|
|
|
|
int len = send_buffer(clnet_info, &request_message, 1, atc);
|
|
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connection bind sent\n");
|
|
}
|
|
cb_sent = true;
|
|
} else {
|
|
if (errorOK) {
|
|
return 0;
|
|
}
|
|
perror("send");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
////////////<<==connection bind send
|
|
|
|
if (not_rare_event()) {
|
|
return 0;
|
|
}
|
|
|
|
////////connection bind response==>>
|
|
|
|
{
|
|
bool cb_received = false;
|
|
while (!cb_received) {
|
|
|
|
int len = recv_buffer(clnet_info, &response_message, 1, 1, atc, &request_message);
|
|
if (len > 0) {
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connect bind response received: \n");
|
|
}
|
|
int err_code = 0;
|
|
uint8_t err_msg[129];
|
|
if (stun_is_success_response(&response_message)) {
|
|
|
|
if (clnet_info->nonce[0]) {
|
|
if (check_integrity(clnet_info, &response_message) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (stun_get_method(&response_message) != STUN_METHOD_CONNECTION_BIND) {
|
|
continue;
|
|
}
|
|
cb_received = true;
|
|
if (verbose) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "success\n");
|
|
}
|
|
atc->tcp_data_bound = true;
|
|
} else if (stun_is_challenge_response_str(response_message.buf, response_message.len, &err_code, err_msg,
|
|
sizeof(err_msg), clnet_info->realm, clnet_info->nonce,
|
|
clnet_info->server_name, &(clnet_info->oauth))) {
|
|
goto beg_cb;
|
|
} else if (stun_is_error_response(&response_message, &err_code, err_msg, sizeof(err_msg))) {
|
|
cb_received = true;
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "connection bind error %d (%s)\n", err_code, (char *)err_msg);
|
|
return -1;
|
|
} else {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "unknown connection bind response\n");
|
|
/* Try again ? */
|
|
}
|
|
} else {
|
|
if (errorOK) {
|
|
return 0;
|
|
}
|
|
perror("recv");
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void tcp_data_connect(app_ur_session *elem, uint32_t cid) {
|
|
int clnet_fd;
|
|
int connect_cycle = 0;
|
|
|
|
again:
|
|
|
|
clnet_fd = socket(elem->pinfo.remote_addr.ss.sa_family, CLIENT_STREAM_SOCKET_TYPE, CLIENT_STREAM_SOCKET_PROTOCOL);
|
|
if (clnet_fd < 0) {
|
|
perror("socket");
|
|
exit(-1);
|
|
}
|
|
|
|
if (sock_bind_to_device(clnet_fd, client_ifname) < 0) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", client_ifname);
|
|
}
|
|
set_sock_buf_size(clnet_fd, (UR_CLIENT_SOCK_BUF_SIZE << 2));
|
|
|
|
++elem->pinfo.tcp_conn_number;
|
|
int i = (int)(elem->pinfo.tcp_conn_number - 1);
|
|
elem->pinfo.tcp_conn =
|
|
(app_tcp_conn_info **)realloc(elem->pinfo.tcp_conn, elem->pinfo.tcp_conn_number * sizeof(app_tcp_conn_info *));
|
|
elem->pinfo.tcp_conn[i] = (app_tcp_conn_info *)calloc(sizeof(app_tcp_conn_info), 1);
|
|
|
|
elem->pinfo.tcp_conn[i]->tcp_data_fd = clnet_fd;
|
|
elem->pinfo.tcp_conn[i]->cid = cid;
|
|
|
|
addr_cpy(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), &(elem->pinfo.local_addr));
|
|
|
|
addr_set_port(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), 0);
|
|
|
|
addr_bind(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), 1, 1, TCP_SOCKET);
|
|
|
|
addr_get_from_sock(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr));
|
|
|
|
{
|
|
int cycle = 0;
|
|
while (cycle++ < 1024) {
|
|
int err = 0;
|
|
if (addr_connect(clnet_fd, &(elem->pinfo.remote_addr), &err) < 0) {
|
|
if (err == EADDRINUSE) {
|
|
socket_closesocket(clnet_fd);
|
|
clnet_fd =
|
|
socket(elem->pinfo.remote_addr.ss.sa_family, CLIENT_STREAM_SOCKET_TYPE, CLIENT_STREAM_SOCKET_PROTOCOL);
|
|
if (clnet_fd < 0) {
|
|
perror("socket");
|
|
exit(-1);
|
|
}
|
|
if (sock_bind_to_device(clnet_fd, client_ifname) < 0) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Cannot bind client socket to device %s\n", client_ifname);
|
|
}
|
|
set_sock_buf_size(clnet_fd, UR_CLIENT_SOCK_BUF_SIZE << 2);
|
|
|
|
elem->pinfo.tcp_conn[i]->tcp_data_fd = clnet_fd;
|
|
|
|
addr_cpy(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), &(elem->pinfo.local_addr));
|
|
|
|
addr_set_port(&(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), 0);
|
|
|
|
addr_bind(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr), 1, 1, TCP_SOCKET);
|
|
|
|
addr_get_from_sock(clnet_fd, &(elem->pinfo.tcp_conn[i]->tcp_data_local_addr));
|
|
|
|
continue;
|
|
|
|
} else {
|
|
perror("connect");
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot connect to remote addr\n", __FUNCTION__);
|
|
exit(-1);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (use_secure) {
|
|
bool try_again = false;
|
|
elem->pinfo.tcp_conn[i]->tcp_data_ssl =
|
|
tls_connect(elem->pinfo.tcp_conn[i]->tcp_data_fd, &(elem->pinfo.remote_addr), &try_again, connect_cycle++);
|
|
if (!(elem->pinfo.tcp_conn[i]->tcp_data_ssl)) {
|
|
if (try_again) {
|
|
--elem->pinfo.tcp_conn_number;
|
|
goto again;
|
|
}
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot SSL connect to remote addr\n", __FUNCTION__);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
if (turn_tcp_connection_bind(clnet_verbose, &(elem->pinfo), elem->pinfo.tcp_conn[i], 0) < 0) {
|
|
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: cannot BIND to tcp connection\n", __FUNCTION__);
|
|
} else {
|
|
|
|
socket_set_nonblocking(clnet_fd);
|
|
|
|
struct event *ev = event_new(client_event_base, clnet_fd, EV_READ | EV_PERSIST, client_input_handler, elem);
|
|
|
|
event_add(ev, NULL);
|
|
|
|
elem->input_tcp_data_ev = ev;
|
|
|
|
addr_debug_print(clnet_verbose, &(elem->pinfo.remote_addr), "TCP data network connected to");
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|