/* * 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"); } } /////////////////////////////////////////////////