diff options
Diffstat (limited to 'tinyNET/src/tnet_transport.c')
-rw-r--r-- | tinyNET/src/tnet_transport.c | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/tinyNET/src/tnet_transport.c b/tinyNET/src/tnet_transport.c new file mode 100644 index 0000000..75cdf85 --- /dev/null +++ b/tinyNET/src/tnet_transport.c @@ -0,0 +1,494 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)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. + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#include "tnet_transport.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 #>) */ + +extern int tnet_transport_prepare(tnet_transport_t *transport); +extern int tnet_transport_unprepare(tnet_transport_t *transport); +extern void *tnet_transport_mainthread(void *param); +extern int tnet_transport_stop(tnet_transport_t *transport); + +static void *run(void* self); + + +tnet_transport_t* tnet_transport_create(const char* host, tnet_port_t port, tnet_socket_type_t type, const char* description) +{ + return tsk_object_new(tnet_transport_def_t, host, port, type, description); +} + +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_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, 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, tnet_nat_context_handle_t* natt_ctx) +{ + tnet_transport_t *transport = handle; + + if(transport && natt_ctx){ + TSK_OBJECT_SAFE_FREE(transport->natt_ctx); // delete old + transport->natt_ctx = tsk_object_ref(natt_ctx); + return 0; + } + else{ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } +} + +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; + tnet_nat_context_handle_t* 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 = TNET_STUN_INVALID_BINDING_ID; + // if the socket is already monitored by the transport we should pause beacuse both the transport and + // NAT binder will try to read from it + tsk_bool_t pause_socket = (TSK_RUNNABLE(transport)->running || TSK_RUNNABLE(transport)->started); + + // FIXME: change when ICE will be fully implemented + TSK_DEBUG_INFO("Getting public address"); + // Pause the soket + if(pause_socket){ + tnet_transport_pause_socket(transport, fd, tsk_true); + } + // Performs STUN binding + bind_id = tnet_nat_stun_bind(transport->natt_ctx, fd); + // Resume the socket + if(pause_socket){ + tnet_transport_pause_socket(transport, fd, tsk_false); + } + + if(TNET_STUN_IS_VALID_BINDING_ID(bind_id)){ + 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){ + return tnet_transport_get_ip_n_port(handle, fd, ip, port); + } + + return 0; +} + +tnet_socket_type_t tnet_transport_get_type(const tnet_transport_handle_t *handle) +{ + if(handle){ + const tnet_transport_t *transport = handle; + return transport->type; + } + else{ + TSK_DEBUG_ERROR("NULL transport object."); + } + return tnet_socket_type_invalid; +} + +tnet_fd_t tnet_transport_get_master_fd(const tnet_transport_handle_t *handle) +{ + if(handle){ + const tnet_transport_t *transport = handle; + return transport->master->fd; + } + else{ + TSK_DEBUG_ERROR("NULL transport object."); + } + return 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) +{ + tnet_transport_t *transport = (tnet_transport_t*)handle; + struct sockaddr_storage to; + int status = -1; + tnet_fd_t fd = TNET_INVALID_FD; + + 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(TNET_SOCKET_TYPE_IS_STREAM(type)){ + /* Create client socket descriptor. */ + if(status = tnet_sockfd_init(transport->local_ip, TNET_SOCKET_PORT_ANY, type, &fd)){ + TSK_DEBUG_ERROR("Failed to create new sockfd."); + goto bail; + } + + /* Add the socket */ + if(status = tnet_transport_add_socket(handle, fd, type, tsk_true, tsk_true)){ + TNET_PRINT_LAST_ERROR("Failed to add new socket."); + + tnet_sockfd_close(&fd); + goto bail; + } + } + else{ + fd = transport->master->fd; + } + + if((status = tnet_sockfd_connectto(fd, (const struct sockaddr_storage *)&to))){ + if(fd != transport->master->fd){ + tnet_sockfd_close(&fd); + } + goto bail; + } + else{ + if(TNET_SOCKET_TYPE_IS_TLS(type)){ + transport->tls.have_tls = tsk_true; + /*transport->connected = !*/tnet_tls_socket_connect((tnet_tls_socket_handle_t*)tnet_transport_get_tlshandle(handle, fd)); + } + else{ + //transport->connected = tsk_true; + } + } + +bail: + return 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))){ + ret = tnet_transport_unprepare(handle); + } + return ret; + } + else{ + TSK_DEBUG_ERROR("NULL transport object."); + return -1; + } +} + + + +/* +* Runnable interface implementation. +*/ +static void *run(void* self) +{ + int ret = 0; + tsk_list_item_t *curr; + tnet_transport_t *transport = self; + + TSK_DEBUG_INFO("Transport::run() - enter"); + + /* 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; + } + + 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() - exit"); + + 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){ + const char *host = va_arg(*app, const char*); + +#if defined(__GNUC__) + tnet_port_t port = (uint16_t)va_arg(*app, unsigned); +#else + tnet_port_t port = (tnet_port_t)va_arg(*app, tnet_port_t); +#endif + + tnet_socket_type_t type = va_arg(*app, tnet_socket_type_t); + const char *description = va_arg(*app, const char*); + + if(description){ + transport->description = tsk_strdup(description); + } + + transport->type = type; + + transport->master = tnet_socket_create(host, port, type); + transport->context = tnet_transport_context_create(); + + if(TNET_SOCKET_TYPE_IS_IPV46(transport->type)){ + transport->local_ip = tsk_strdup(host); /* FQDN */ + } + else{ + transport->local_ip = tsk_strdup(transport->master->ip); /* IP address */ + } + } + return self; +} + +static tsk_object_t* tnet_transport_dtor(tsk_object_t * self) +{ + tnet_transport_t *transport = self; + if(transport){ + 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->description); + TSK_FREE(transport->local_ip); + + // tls + TSK_FREE(transport->tls.ca); + TSK_FREE(transport->tls.pbk); + TSK_FREE(transport->tls.pvk); + } + + 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; + |