summaryrefslogtreecommitdiffstats
path: root/tinyNET/src/tnet_transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyNET/src/tnet_transport.c')
-rw-r--r--tinyNET/src/tnet_transport.c494
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;
+
OpenPOWER on IntegriCloud