#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT /* * Copyright (C) 2017, University of the Basque Country (UPV/EHU) * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com> * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom <http://doubango.org> * * This file is part of Open Source Doubango Framework. * * DOUBANGO is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DOUBANGO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DOUBANGO. * */ /**@file tnet_transport.c * @brief Network transport layer. * * <h2>10.2 Tansport</h2> * A transport layer always has a master socket which determine what kind of network traffic we expect (stream or dgram). * Stream transport can manage TCP, TLS and SCTP sockets. Datagram socket can only manage UDP sockets. <br> * A transport can hold both IPv4 and IPv6 sockets. */ #include "tnet_transport.h" #include "tls/tnet_tls.h" #include "tls/tnet_dtls.h" #include "stun/tnet_stun_types.h" #include "tsk_memory.h" #include "tsk_string.h" #include "tsk_debug.h" #include "tsk_thread.h" #include "tsk_buffer.h" #include <string.h> /* memcpy, ...(<#void * #>, <#const void * #>, <#tsk_size_t #>) */ #ifndef TNET_CIPHER_LIST # define TNET_CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" #endif extern int tnet_transport_prepare(tnet_transport_t *transport); extern int tnet_transport_unprepare(tnet_transport_t *transport); extern void* TSK_STDCALL tnet_transport_mainthread(void *param); extern int tnet_transport_stop(tnet_transport_t *transport); static void* TSK_STDCALL run(void* self); static int _tnet_transport_dtls_cb(const void* usrdata, tnet_dtls_socket_event_type_t e, const tnet_dtls_socket_handle_t* handle, const void* data, tsk_size_t size); static int _tnet_transport_ssl_init(tnet_transport_t* transport) { if (!transport){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } #if HAVE_OPENSSL { tnet_socket_type_t type = tnet_transport_get_type(transport); tsk_bool_t is_tls = (TNET_SOCKET_TYPE_IS_TLS(type) || TNET_SOCKET_TYPE_IS_WSS(type)); tsk_bool_t is_dtls = transport->dtls.enabled/* TNET_SOCKET_TYPE_IS_DTLS(type)*/; // DTLS-RTP, not raw DTLS if (is_dtls && !tnet_dtls_is_supported()){ TSK_DEBUG_ERROR("Requesting to create DTLS transport but source code not built with support for this feature"); return -1; } if (is_tls && !tnet_tls_is_supported()){ TSK_DEBUG_ERROR("Requesting to create TLS transport but source code not built with support for this feature"); return -1; } if ((transport->tls.enabled = is_tls)){ if (!transport->tls.ctx_client && !(transport->tls.ctx_client = SSL_CTX_new(SSLv23_client_method()))){ TSK_DEBUG_ERROR("Failed to create SSL client context"); return -2; } if (!transport->tls.ctx_server && !(transport->tls.ctx_server = SSL_CTX_new(SSLv23_server_method()))){ TSK_DEBUG_ERROR("Failed to create SSL server context"); return -3; } SSL_CTX_set_mode(transport->tls.ctx_client, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(transport->tls.ctx_server, SSL_MODE_AUTO_RETRY); SSL_CTX_set_verify(transport->tls.ctx_server, SSL_VERIFY_NONE, tsk_null); // to be updated by tnet_transport_tls_set_certs() SSL_CTX_set_verify(transport->tls.ctx_client, SSL_VERIFY_NONE, tsk_null); // to be updated by tnet_transport_tls_set_certs() if (SSL_CTX_set_cipher_list(transport->tls.ctx_client, TNET_CIPHER_LIST) <= 0 || SSL_CTX_set_cipher_list(transport->tls.ctx_server, TNET_CIPHER_LIST) <= 0){ TSK_DEBUG_ERROR("SSL_CTX_set_cipher_list failed [%s]", ERR_error_string(ERR_get_error(), tsk_null)); return -4; } } #if HAVE_OPENSSL_DTLS if ((transport->dtls.enabled = is_dtls)){ if (!transport->dtls.ctx && !(transport->dtls.ctx = SSL_CTX_new(DTLSv1_method()))){ TSK_DEBUG_ERROR("Failed to create DTLSv1 context"); TSK_OBJECT_SAFE_FREE(transport); return -5; } SSL_CTX_set_read_ahead(transport->dtls.ctx, 1); // SSL_CTX_set_options(transport->dtls.ctx, SSL_OP_ALL); // SSL_CTX_set_mode(transport->dtls.ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_verify(transport->dtls.ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, tsk_null); // to be updated by tnet_transport_tls_set_certs() if (SSL_CTX_set_cipher_list(transport->dtls.ctx, TNET_CIPHER_LIST) <= 0){ TSK_DEBUG_ERROR("SSL_CTX_set_cipher_list failed [%s]", ERR_error_string(ERR_get_error(), tsk_null)); return -6; } transport->dtls.activated = tsk_true; } #endif /* HAVE_OPENSSL_DTLS */ } #endif /* HAVE_OPENSSL */ return 0; } static int _tnet_transport_ssl_deinit(tnet_transport_t* transport) { if (!transport){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } #if HAVE_OPENSSL if (transport->tls.ctx_client){ SSL_CTX_free(transport->tls.ctx_client); transport->tls.ctx_client = tsk_null; } if (transport->tls.ctx_server){ SSL_CTX_free(transport->tls.ctx_server); transport->tls.ctx_server = tsk_null; } if (transport->dtls.ctx){ SSL_CTX_free(transport->dtls.ctx); transport->dtls.ctx = tsk_null; } #endif /* HAVE_OPENSSL */ return 0; } tnet_transport_t* tnet_transport_create(const char* host, tnet_port_t port, tnet_socket_type_t type, const char* description) { tnet_transport_t* transport; if ((transport = tsk_object_new(tnet_transport_def_t))){ transport->description = tsk_strdup(description); transport->local_host = tsk_strdup(host); transport->req_local_port = port; transport->type = type; transport->context = tnet_transport_context_create(); if ((transport->master = tnet_socket_create(transport->local_host, transport->req_local_port, transport->type))){ transport->local_ip = tsk_strdup(transport->master->ip); transport->bind_local_port = transport->master->port; } else{ TSK_DEBUG_ERROR("Failed to create master socket"); TSK_OBJECT_SAFE_FREE(transport); } if (_tnet_transport_ssl_init(transport) != 0){ TSK_DEBUG_ERROR("Failed to initialize TLS and/or DTLS caps"); TSK_OBJECT_SAFE_FREE(transport); } // set priority tsk_runnable_set_priority(TSK_RUNNABLE(transport), TSK_THREAD_PRIORITY_TIME_CRITICAL); } return transport; } tnet_transport_t* tnet_transport_create_2(tnet_socket_t *master, const char* description) { tnet_transport_t* transport; if (!master){ TSK_DEBUG_ERROR("Invalid parameter"); return tsk_null; } if ((transport = tsk_object_new(tnet_transport_def_t))){ transport->description = tsk_strdup(description); transport->local_host = tsk_strdup(master->ip); transport->req_local_port = master->port; transport->type = master->type; transport->master = tsk_object_ref(master); transport->local_ip = tsk_strdup(transport->master->ip); transport->bind_local_port = transport->master->port; transport->context = tnet_transport_context_create(); if (_tnet_transport_ssl_init(transport) != 0){ TSK_DEBUG_ERROR("Failed to initialize TLS and/or DTLS caps"); TSK_OBJECT_SAFE_FREE(transport); } // set priority tsk_runnable_set_priority(TSK_RUNNABLE(transport), TSK_THREAD_PRIORITY_TIME_CRITICAL); } return transport; } tnet_transport_event_t* tnet_transport_event_create(tnet_transport_event_type_t type, const void* callback_data, tnet_fd_t fd) { return tsk_object_new(tnet_transport_event_def_t, type, callback_data, fd); } int tnet_transport_tls_set_certs(tnet_transport_handle_t *handle, const char* ca, const char* pbk, const char* pvk, tsk_bool_t verify) { tnet_transport_t *transport = handle; static const char* ssl_password = tsk_null; if (!transport) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } tsk_strupdate(&transport->tls.ca, ca); tsk_strupdate(&transport->tls.pvk, pvk); tsk_strupdate(&transport->tls.pbk, pbk); transport->tls.verify = verify; #if HAVE_OPENSSL { int32_t i, ret; SSL_CTX* contexts[3] = { tsk_null }; /* init DTLS/TLS contexts */ if ((ret = _tnet_transport_ssl_init(transport))){ return ret; } if (transport->tls.enabled){ contexts[0] = transport->tls.ctx_client; contexts[1] = transport->tls.ctx_server; } if (transport->dtls.enabled){ contexts[2] = transport->dtls.ctx; /* Reset fingerprints */ memset(transport->dtls.fingerprints, 0, sizeof(transport->dtls.fingerprints)); } for (i = 0; i < sizeof(contexts) / sizeof(contexts[0]); ++i){ if (!contexts[i]){ continue; } SSL_CTX_set_verify(contexts[i], transport->tls.verify ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT) : SSL_VERIFY_NONE, tsk_null); if (!tsk_strnullORempty(transport->tls.pbk) || !tsk_strnullORempty(transport->tls.pvk) || !tsk_strnullORempty(transport->tls.ca)){ /* Sets Public key (cert) */ if (!tsk_strnullORempty(transport->tls.pbk) && (ret = SSL_CTX_use_certificate_file(contexts[i], transport->tls.pbk, SSL_FILETYPE_PEM)) != 1) { TSK_DEBUG_ERROR("SSL_CTX_use_certificate_file failed [%d,%s]", ret, ERR_error_string(ERR_get_error(), tsk_null)); return -3; } /*Sets the password of the private key*/ if (!tsk_strnullORempty(ssl_password)){ SSL_CTX_set_default_passwd_cb_userdata(contexts[i], (void*)ssl_password); } /* Sets Private key (cert) */ if (!tsk_strnullORempty(transport->tls.pvk) && (ret = SSL_CTX_use_PrivateKey_file(contexts[i], transport->tls.pvk, SSL_FILETYPE_PEM)) != 1) { TSK_DEBUG_ERROR("SSL_CTX_use_PrivateKey_file failed [%d,%s]", ret, ERR_error_string(ERR_get_error(), tsk_null)); return -4; } /* Checks private key */ if (!tsk_strnullORempty(transport->tls.pvk) && SSL_CTX_check_private_key(contexts[i]) == 0) { TSK_DEBUG_ERROR("SSL_CTX_check_private_key failed [%d,%s]", ret, ERR_error_string(ERR_get_error(), tsk_null)); return -5; } /* Sets trusted CAs and CA file */ if (!tsk_strnullORempty(transport->tls.ca) && (ret = SSL_CTX_load_verify_locations(contexts[i], transport->tls.ca, /*tlsdir_cas*/tsk_null)) != 1) { TSK_DEBUG_ERROR("SSL_CTX_load_verify_locations failed [%d, %s]", ret, ERR_error_string(ERR_get_error(), tsk_null)); return -5; } } } } #endif /* HAVE_OPENSSL */ return 0; } int tnet_transport_start(tnet_transport_handle_t* handle) { int ret = -1; if (handle){ tnet_transport_t *transport = handle; /* prepare transport */ if ((ret = tnet_transport_prepare(transport))){ TSK_DEBUG_ERROR("Failed to prepare transport."); goto bail; } /* start transport */ TSK_RUNNABLE(transport)->run = run; if ((ret = tsk_runnable_start(TSK_RUNNABLE(transport), tnet_transport_event_def_t))){ TSK_DEBUG_ERROR("Failed to start transport."); goto bail; } } else{ TSK_DEBUG_ERROR("NULL transport object."); } bail: return ret; } int tnet_transport_issecure(const tnet_transport_handle_t *handle) { if (handle) { const tnet_transport_t *transport = handle; if (transport->master){ return TNET_SOCKET_TYPE_IS_SECURE(transport->master->type); } } else{ TSK_DEBUG_ERROR("NULL transport object."); } return 0; } const char* tnet_transport_get_description(const tnet_transport_handle_t *handle) { if (handle){ const tnet_transport_t *transport = handle; return transport->description; } else{ TSK_DEBUG_ERROR("NULL transport object."); return tsk_null; } } int tnet_transport_get_ip_n_port(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_ip_t *ip, tnet_port_t *port) { if (handle){ return tnet_get_ip_n_port(fd, tsk_true/*local*/, ip, port); } else{ TSK_DEBUG_ERROR("NULL transport object."); } return -1; } int tnet_transport_get_ip_n_port_2(const tnet_transport_handle_t *handle, tnet_ip_t *ip, tnet_port_t *port) { const tnet_transport_t *transport = handle; if (transport){ // do not check the master, let the application die if "null" if (ip){ memcpy(*ip, transport->master->ip, sizeof(transport->master->ip)); } if (port){ *port = transport->master->port; } return 0; } else{ TSK_DEBUG_ERROR("NULL transport object."); return -1; } } int tnet_transport_set_natt_ctx(tnet_transport_handle_t *handle, struct tnet_nat_ctx_s* natt_ctx) { if (!handle) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } TSK_OBJECT_SAFE_FREE(((tnet_transport_t *)handle)->natt_ctx); ((tnet_transport_t *)handle)->natt_ctx = tsk_object_ref(natt_ctx); return 0; } int tnet_transport_get_public_ip_n_port(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_ip_t *ip, tnet_port_t *port) { tsk_bool_t stun_ok = tsk_false; struct tnet_nat_ctx_s* natt_ctx; const tnet_transport_t *transport = handle; if (!transport){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (TNET_SOCKET_TYPE_IS_DGRAM(transport->type) && (natt_ctx = tsk_object_ref(transport->natt_ctx))) { tnet_stun_binding_id_t bind_id = kStunBindingInvalidId; // if the socket is already monitored by the transport we should pause because both the transport and // NAT binder will try to read from it // Pause the soket tnet_transport_pause_socket(transport, fd, tsk_true); // Performs STUN binding bind_id = tnet_nat_stun_bind(transport->natt_ctx, fd); // Resume the socket tnet_transport_pause_socket(transport, fd, tsk_false); if (bind_id != kStunBindingInvalidId) { char* public_ip = tsk_null; if (tnet_nat_stun_get_reflexive_address(transport->natt_ctx, bind_id, &public_ip, port) == 0){ if (ip && public_ip){ tsk_size_t ip_len = tsk_strlen(public_ip); memcpy(ip, public_ip, ip_len > sizeof(*ip) ? sizeof(*ip) : ip_len); } stun_ok = tsk_true; } TSK_FREE(public_ip); tnet_nat_stun_unbind(transport->natt_ctx, bind_id); } tsk_object_unref(natt_ctx); } if (!stun_ok){ if (fd == TNET_INVALID_FD && transport->local_ip){ memcpy(*ip, transport->local_ip, TSK_MIN(sizeof(tnet_ip_t), tsk_strlen(transport->local_ip))); *port = transport->bind_local_port; return 0; } else{ return tnet_transport_get_ip_n_port(handle, fd, ip, port); } } return 0; } const char* tnet_transport_dtls_get_local_fingerprint(const tnet_transport_handle_t *handle, tnet_dtls_hash_type_t hash) { const tnet_transport_t *transport = handle; if (!transport){ TSK_DEBUG_ERROR("Invalid parameter"); return tsk_null; } if (!transport->dtls.enabled){ TSK_DEBUG_ERROR("DTLS not enabled on this transport"); return tsk_null; } if (hash > sizeof(transport->dtls.fingerprints) / sizeof(transport->dtls.fingerprints[0])){ TSK_DEBUG_ERROR("%d not valid for fingerprint hash", hash); return tsk_null; } if (tsk_strnullORempty(transport->tls.pbk)){ TSK_DEBUG_ERROR("No certificate for which to get fingerprint"); return tsk_null; } if (tnet_dtls_get_fingerprint(transport->tls.pbk, &((tnet_transport_t *)transport)->dtls.fingerprints[hash], hash) == 0){ return (const char*)transport->dtls.fingerprints[hash]; } return tsk_null; } /* rfc5764: 4.1. The use_srtp Extension */ int tnet_transport_dtls_use_srtp(tnet_transport_handle_t *handle, const char* srtp_profiles, struct tnet_socket_s** sockets, tsk_size_t sockets_count) { tnet_transport_t *transport = handle; if (!transport || !srtp_profiles){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (!transport->dtls.enabled){ TSK_DEBUG_ERROR("DTLS not enabled on this transport"); return -2; } #if HAVE_OPENSSL_DTLS_SRTP { tsk_size_t i; transport->dtls.use_srtp = tsk_true; SSL_CTX_set_tlsext_use_srtp(transport->dtls.ctx, srtp_profiles); if (sockets){ for (i = 0; i < sockets_count; ++i){ if (sockets[i] && sockets[i]->dtlshandle){ tnet_dtls_socket_use_srtp(sockets[i]->dtlshandle); } } } return 0; } #else TSK_DEBUG_ERROR("Your OpenSSL version do not support DTLS-SRTP"); return -2; #endif } int tnet_transport_dtls_set_remote_fingerprint(tnet_transport_handle_t *handle, const tnet_fingerprint_t* fingerprint, tnet_dtls_hash_type_t hash, struct tnet_socket_s** sockets, tsk_size_t sockets_count) { const tnet_transport_t *transport = handle; if (!transport || !fingerprint){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (!transport->dtls.enabled){ TSK_DEBUG_ERROR("DTLS not enabled on this transport"); return -2; } #if HAVE_OPENSSL_DTLS if (sockets){ tsk_size_t i; for (i = 0; i < sockets_count; ++i){ if (sockets[i] && sockets[i]->dtlshandle){ tnet_dtls_socket_set_remote_fingerprint(sockets[i]->dtlshandle, fingerprint, hash); } } } return 0; #else TSK_DEBUG_ERROR("Your OpenSSL version do not support DTLS"); return -2; #endif } tsk_bool_t tnet_transport_dtls_is_enabled(const tnet_transport_handle_t *handle) { const tnet_transport_t *transport = handle; if (!transport){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } return transport->dtls.enabled; } /* Enable or disable DTLS on the transport and all coresponding sockets *@param handle The transport for which to enable or disable DTLS *@param enabled Whether to enable or disable DTLS *@param sockets List of all sockets for which to enable or disable DLS could be null. You should include the master socket in this list. *@param sockets_count The number of sockets *@return 0 if succeed, otherwise non-zero error code */ int tnet_transport_dtls_set_enabled(tnet_transport_handle_t *handle, tsk_bool_t enabled, struct tnet_socket_s** sockets, tsk_size_t sockets_count) { tnet_transport_t *transport = handle; tnet_socket_type_t type; int ret; if (!transport){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } type = tnet_transport_get_type(transport); if (enabled & !tnet_dtls_is_supported()) { TSK_DEBUG_ERROR("Trying to enable DTLS but code source not built with this feature"); return -1; } if ((transport->dtls.enabled = enabled)) { if ((ret = _tnet_transport_ssl_init(transport))) { return ret; } } else { ret = _tnet_transport_ssl_deinit(transport); } if (sockets && sockets_count) { tsk_size_t i; for (i = 0; i < sockets_count; ++i) { if (!sockets[i]) { continue; } if (enabled) { if (!sockets[i]->dtlshandle) { if (!(sockets[i]->dtlshandle = tnet_dtls_socket_create(sockets[i], transport->dtls.ctx))) { return -4; } } if (transport->dtls.use_srtp) { tnet_dtls_socket_use_srtp(sockets[i]->dtlshandle); } tnet_dtls_socket_set_callback(sockets[i]->dtlshandle, transport, _tnet_transport_dtls_cb); } else { TSK_OBJECT_SAFE_FREE(sockets[i]->dtlshandle); } } } return ret; } int tnet_transport_dtls_set_setup(tnet_transport_handle_t* handle, tnet_dtls_setup_t setup, struct tnet_socket_s** sockets, tsk_size_t sockets_count) { tnet_transport_t *transport = handle; if (!transport) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (!transport->dtls.enabled) { TSK_DEBUG_ERROR("DTLS not enabled on this transport"); return -2; } if (sockets && sockets_count) { tsk_size_t i; for (i = 0; i < sockets_count; ++i) { if (!sockets[i] || !sockets[i]->dtlshandle) { continue; } tnet_dtls_socket_set_setup(sockets[i]->dtlshandle, setup); } } return 0; } int tnet_transport_dtls_set_store_handshakingdata(tnet_transport_handle_t* handle, tsk_bool_t handshake_storedata, struct tnet_socket_s** sockets, tsk_size_t sockets_count) { tnet_transport_t *transport = handle; if (!transport) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (!transport->dtls.enabled) { TSK_DEBUG_ERROR("DTLS not enabled on this transport"); return -2; } if (sockets && sockets_count) { tsk_size_t i; for (i = 0; i < sockets_count; ++i) { if (!sockets[i] || !sockets[i]->dtlshandle) { continue; } tnet_dtls_socket_set_store_handshakingdata(sockets[i]->dtlshandle, handshake_storedata); } } return 0; } int tnet_transport_dtls_do_handshake(tnet_transport_handle_t *handle, struct tnet_socket_s** sockets, tsk_size_t sockets_count, const struct sockaddr_storage** remote_addrs, tsk_size_t remote_addrs_count) { tnet_transport_t *transport = handle; tsk_size_t i; if (!transport || !sockets) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (!transport->dtls.enabled) { TSK_DEBUG_ERROR("DTLS not enabled on this transport"); return -2; } if (sockets) { int ret; for (i = 0; i < sockets_count; ++i) { if (sockets[i] && sockets[i]->dtlshandle) { if ((ret = tnet_dtls_socket_do_handshake(sockets[i]->dtlshandle, (remote_addrs && i < remote_addrs_count) ? remote_addrs[i] : tsk_null)) != 0){ return ret; } } } } return 0; } int tnet_transport_dtls_get_handshakingdata(tnet_transport_handle_t* handle, const struct tnet_socket_s** sockets, tsk_size_t sockets_count, const void* data[], tsk_size_t size[]) { tnet_transport_t *transport = handle; tsk_size_t i; if (!transport || !sockets) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (!transport->dtls.enabled) { TSK_DEBUG_ERROR("DTLS not enabled on this transport"); return -2; } if (sockets) { int ret; for (i = 0; i < sockets_count; ++i) { if (sockets[i] && sockets[i]->dtlshandle) { if ((ret = tnet_dtls_socket_get_handshakingdata(sockets[i]->dtlshandle, &data[i], &size[i])) != 0){ return ret; } } else { data[i] = tsk_null; size[i] = 0; } } } return 0; } tnet_socket_type_t tnet_transport_get_type(const tnet_transport_handle_t *handle) { if (!handle){ TSK_DEBUG_ERROR("Invalid parameter"); return tnet_socket_type_invalid; } return ((const tnet_transport_t *)handle)->type; } tnet_fd_t tnet_transport_get_master_fd(const tnet_transport_handle_t *handle) { if (!handle){ TSK_DEBUG_ERROR("Invalid parameter"); return TNET_INVALID_FD; } return ((const tnet_transport_t *)handle)->master ? ((const tnet_transport_t *)handle)->master->fd : TNET_INVALID_FD; } /** * Connects a socket. * @param handle The transport to use to connect() the socket. The new socket will be managed by this transport. * @param host The remote @a host to connect() to. * @param port The remote @a port to connect() to. * @param type The type of the socket to use to connect() to the remote @a host. * @retval The newly connected socket. For non-blocking sockets you should use @ref tnet_sockfd_waitUntilWritable to check * the socket for writability. * @sa tnet_sockfd_waitUntilWritable. */ tnet_fd_t tnet_transport_connectto(const tnet_transport_handle_t *handle, const char* host, tnet_port_t port, tnet_socket_type_t type) { return tnet_transport_connectto_3(handle, tsk_null/*socket*/, host, port, type); } tnet_fd_t tnet_transport_connectto_3(const tnet_transport_handle_t *handle, struct tnet_socket_s* socket, const char* host, tnet_port_t port, tnet_socket_type_t type) { tnet_transport_t *transport = (tnet_transport_t*)handle; struct sockaddr_storage to; int status = -1; tnet_fd_t fd = socket ? socket->fd : TNET_INVALID_FD; tnet_tls_socket_handle_t* tls_handle = tsk_null; tsk_bool_t owe_socket = socket ? tsk_false : tsk_true; if (!transport || !transport->master){ TSK_DEBUG_ERROR("Invalid transport handle"); goto bail; } if ((TNET_SOCKET_TYPE_IS_STREAM(transport->master->type) && !TNET_SOCKET_TYPE_IS_STREAM(type)) || (TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type) && !TNET_SOCKET_TYPE_IS_DGRAM(type))) { TSK_DEBUG_ERROR("Master/destination types mismatch [%u/%u]", transport->master->type, type); goto bail; } /* Init destination sockaddr fields */ if ((status = tnet_sockaddr_init(host, port, type, &to))) { TSK_DEBUG_ERROR("Invalid HOST/PORT [%s/%u]", host, port); goto bail; } else if (TNET_SOCKET_TYPE_IS_IPV46(type)) { /* Update the type (unambiguously) */ if (to.ss_family == AF_INET6) { TNET_SOCKET_TYPE_SET_IPV6Only(type); } else { TNET_SOCKET_TYPE_SET_IPV4Only(type); } } /* * STREAM ==> create new socket and connect it to the remote host. * DGRAM ==> connect the master to the remote host. */ if (fd == TNET_INVALID_FD) { // Create client socket descriptor. if ((status = tnet_sockfd_init(transport->local_host, TNET_SOCKET_PORT_ANY, type, &fd))) { TSK_DEBUG_ERROR("Failed to create new sockfd."); goto bail; } } if ((status = tnet_sockfd_connectto(fd, (const struct sockaddr_storage *)&to))) { if (fd != transport->master->fd) { tnet_sockfd_close(&fd); } goto bail; } else { static const tsk_bool_t __isClient = tsk_true; if (TNET_SOCKET_TYPE_IS_TLS(type) || TNET_SOCKET_TYPE_IS_WSS(type)) { #if HAVE_OPENSSL tls_handle = tnet_tls_socket_create(fd, transport->tls.ctx_client); if (socket) { TSK_OBJECT_SAFE_FREE(socket->tlshandle); socket->tlshandle = tsk_object_ref(tls_handle); } if ((status = tnet_tls_socket_connect(tls_handle))) { tnet_sockfd_close(&fd); goto bail; } #endif } /* Add the socket */ // socket must be added after connect() otherwise many Linux systems when return POLLHUP as the fd is not active yet if ((status = tnet_transport_add_socket(handle, fd, type, owe_socket, __isClient, tls_handle))) { TNET_PRINT_LAST_ERROR("Failed to add new socket"); tnet_sockfd_close(&fd); goto bail; } } bail: TSK_OBJECT_SAFE_FREE(tls_handle); return status == 0 ? fd : TNET_INVALID_FD; } int tnet_transport_set_callback(const tnet_transport_handle_t *handle, tnet_transport_cb_f callback, const void* callback_data) { tnet_transport_t *transport = (tnet_transport_t*)handle; int ret = -1; if (!transport){ TSK_DEBUG_ERROR("Invalid server handle."); return ret; } transport->callback = callback; transport->callback_data = callback_data; return 0; } int tnet_transport_shutdown(tnet_transport_handle_t* handle) { if (handle){ int ret; if ((ret = tnet_transport_stop(handle)) == 0){ ret = tnet_transport_unprepare(handle); } return ret; } else{ TSK_DEBUG_ERROR("NULL transport object."); return -1; } } static int _tnet_transport_dtls_cb(const void* usrdata, tnet_dtls_socket_event_type_t dtls_e, const tnet_dtls_socket_handle_t* handle, const void* data, tsk_size_t size) { tnet_transport_t *transport = (tnet_transport_t*)usrdata; if (transport) { tnet_transport_event_type_t t_e; const struct sockaddr_storage* remote_addr; tnet_fd_t fd; tnet_transport_event_t* e; switch (dtls_e) { case tnet_dtls_socket_event_type_handshake_started: t_e = event_dtls_handshake_started; break; case tnet_dtls_socket_event_type_handshake_succeed: t_e = event_dtls_handshake_succeed; break; case tnet_dtls_socket_event_type_handshake_failed: t_e = event_dtls_handshake_failed; break; case tnet_dtls_socket_event_type_fingerprint_mismatch: t_e = event_dtls_fingerprint_mismatch; break; case tnet_dtls_socket_event_type_dtls_srtp_profile_selected: t_e = event_dtls_srtp_profile_selected; break; case tnet_dtls_socket_event_type_dtls_srtp_data: t_e = event_dtls_srtp_data; break; case tnet_dtls_socket_event_type_error: t_e = event_dtls_error; break; default: TSK_DEBUG_ERROR("DTLS event = %d ignored", dtls_e); return -1; } remote_addr = tnet_dtls_socket_get_remote_addr(handle); fd = tnet_dtls_socket_get_fd(handle); if ((e = tnet_transport_event_create(t_e, transport->callback_data, fd))) { #if HAVE_CRT //Debug memory if (data && size && (e->data = malloc(size))) { #else if (data && size && (e->data = tsk_malloc(size))) { #endif //HAVE_CRT memcpy(e->data, data, size); e->size = size; } if (remote_addr) { e->remote_addr = *remote_addr; } if (TSK_RUNNABLE(transport)->initialized && TSK_RUNNABLE(transport)->running && TSK_RUNNABLE(transport)->started) { TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(transport), e); } else { TSK_DEBUG_INFO("Delivering network event synchronously."); // network transport not started (happens when TURN is using the sockets instead of the RTP manager) if (transport->callback) { transport->callback(e); } TSK_OBJECT_SAFE_FREE(e); } return 0; } } return -1; } /* * Runnable interface implementation. */ static void* TSK_STDCALL run(void* self) { int ret = 0; tsk_list_item_t *curr; tnet_transport_t *transport = self; TSK_DEBUG_INFO("Transport::run(%s) - enter", transport->description); /* create main thread */ if ((ret = tsk_thread_create(transport->mainThreadId, tnet_transport_mainthread, transport))){ /* More important than "tsk_runnable_start" ==> start it first. */ TSK_FREE(transport->context); /* Otherwise (tsk_thread_create is ok) will be freed when mainthread exit. */ TSK_DEBUG_FATAL("Failed to create main thread [%d]", ret); return tsk_null; } /* set thread priority iOS and OSX: no incoming pkts (STUN, rtp, dtls...) when thread priority is changed -> to be checked */ #if !TNET_UNDER_APPLE ret = tsk_thread_set_priority(transport->mainThreadId[0], TSK_THREAD_PRIORITY_TIME_CRITICAL); #endif TSK_RUNNABLE_RUN_BEGIN(transport); if ((curr = TSK_RUNNABLE_POP_FIRST_SAFE(TSK_RUNNABLE(transport)))){ const tnet_transport_event_t *e = (const tnet_transport_event_t*)curr->data; if (transport->callback) { transport->callback(e); } tsk_object_unref(curr); } TSK_RUNNABLE_RUN_END(transport); TSK_DEBUG_INFO("Transport::run(%s) - exit", transport->description); return tsk_null; } //================================================================================================= // Transport object definition // static tsk_object_t* tnet_transport_ctor(tsk_object_t * self, va_list * app) { tnet_transport_t *transport = self; if (transport){ } return self; } static tsk_object_t* tnet_transport_dtor(tsk_object_t * self) { tnet_transport_t *transport = self; if (transport){ tnet_transport_set_callback(transport, tsk_null, tsk_null); tnet_transport_shutdown(transport); TSK_OBJECT_SAFE_FREE(transport->master); TSK_OBJECT_SAFE_FREE(transport->context); TSK_OBJECT_SAFE_FREE(transport->natt_ctx); TSK_FREE(transport->local_ip); TSK_FREE(transport->local_host); // (tls and dtls) = ssl TSK_FREE(transport->tls.ca); TSK_FREE(transport->tls.pbk); TSK_FREE(transport->tls.pvk); _tnet_transport_ssl_deinit(transport); // openssl contexts TSK_DEBUG_INFO("*** Transport (%s) destroyed ***", transport->description); TSK_FREE(transport->description); } return self; } static const tsk_object_def_t tnet_transport_def_s = { sizeof(tnet_transport_t), tnet_transport_ctor, tnet_transport_dtor, tsk_null, }; const tsk_object_def_t *tnet_transport_def_t = &tnet_transport_def_s; //================================================================================================= // Transport event object definition // static tsk_object_t* tnet_transport_event_ctor(tsk_object_t * self, va_list * app) { tnet_transport_event_t *e = self; if (e){ e->type = va_arg(*app, tnet_transport_event_type_t); e->callback_data = va_arg(*app, const void*); e->local_fd = va_arg(*app, tnet_fd_t); } return self; } static tsk_object_t* tnet_transport_event_dtor(tsk_object_t * self) { tnet_transport_event_t *e = self; if (e){ TSK_FREE(e->data); } return self; } static const tsk_object_def_t tnet_transport_event_def_s = { sizeof(tnet_transport_event_t), tnet_transport_event_ctor, tnet_transport_event_dtor, 0, }; const tsk_object_def_t *tnet_transport_event_def_t = &tnet_transport_event_def_s;