summaryrefslogtreecommitdiffstats
path: root/tinyNET/src/tnet_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyNET/src/tnet_socket.c')
-rw-r--r--tinyNET/src/tnet_socket.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/tinyNET/src/tnet_socket.c b/tinyNET/src/tnet_socket.c
new file mode 100644
index 0000000..33dc807
--- /dev/null
+++ b/tinyNET/src/tnet_socket.c
@@ -0,0 +1,326 @@
+/*
+* 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.h
+ * @brief Protocol agnostic socket.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+
+#include "tnet_socket.h"
+
+#include "tnet_utils.h"
+
+#include "tsk_string.h"
+#include "tsk_debug.h"
+
+#include <string.h>
+
+/**@defgroup tnet_socket_group Protocol agnostic socket
+*
+*
+* <h2>10.1 Sockets</h2>
+* For performance reason, all sockets created using tinyNET are non-blocking by default.
+* The newly created socket will be automatically bound to associate it with an IP address and port number. @ref tnet_socket_create() function is used to create and bind a
+* non-blocking socket. You should use @ref tnet_socket_create_2() function to control whether the socket should be bound or not. The same function is used to force the stack to create a blocking socket.<br>
+* A socket object is defined like this:<br>
+*
+* @code
+typedef struct tnet_socket_s
+{
+ TSK_DECLARE_OBJECT;
+
+ tnet_socket_type_t type;
+ tnet_fd_t fd;
+ tnet_ip_t ip;
+ uint16_t port;
+
+ tnet_tls_socket_handle_t* tlshandle;
+}
+tnet_socket_t;
+* @endcode
+* To create a socket:
+* @code
+// (create udp ipv4 or ipv6 socket)
+tnet_socket_t* socket = tnet_socket_create(
+ TNET_SOCKET_HOST_ANY, // local ip address/hostname to bind to
+ TNET_SOCKET_PORT_ANY, // local port number to bind to
+ tnet_socket_type_udp_ipv46 // the socket type (IPv4 or IPv6)
+ );
+// TNET_SOCKET_HOST_ANY --> bind to "0.0.0.0" or "::"
+// TNET_SOCKET_PORT_ANY --> bind to any available port
+* @endcode
+
+* <b>TNET_SOCKET_TYPE_IS_*()</b> macros are used to determine:
+* - The socket type (stream, dgram),
+* - The socket protocol (udp, tcp, tls, sctp, ipsec),
+* - The IP version (ipv6, ipv4),
+* - …
+* <br>
+* A socket is a well-defined object and should be destroyed using @a TSK_DECLARE_SAFE_FREE() macro.
+* A socket will be automatically closed when destroyed.
+*
+*/
+
+/**@ingroup tnet_socket_group
+* Creates a new socket.
+* To check that the returned socket is valid use @ref TNET_SOCKET_IS_VALID function.
+* @param host FQDN (e.g. www.doubango.org) or IPv4/IPv6 IP string.
+* @param port The local/remote port used to receive/send data. Set the port value to @ref TNET_SOCKET_PORT_ANY to bind to a random port.
+* @param type The type of the socket. See @ref tnet_socket_type_t.
+* @param nonblocking Indicates whether to create non-blocking socket.
+* @param bindsocket Indicates whether to bind the newly created socket or not.
+* @retval @ref tnet_socket_t object.
+* @sa @ref tnet_socket_create.
+*/
+tnet_socket_t* tnet_socket_create_2(const char*host, tnet_port_t port, tnet_socket_type_t type, tsk_bool_t nonblocking, tsk_bool_t bindsocket)
+{
+ return tsk_object_new(tnet_socket_def_t, host, port, type, nonblocking, bindsocket);
+}
+
+/**@ingroup tnet_socket_group
+* Creates a non-blocking socket and bind it.
+* To check that the returned socket is valid use @ref TNET_SOCKET_IS_VALID function.
+* @param host FQDN (e.g. www.doubango.org) or IPv4/IPv6 IP string.
+* @param port The local/remote port used to receive/send data. Set the port value to @ref TNET_SOCKET_PORT_ANY to bind to a random port.
+* @param type The type of the socket. See @ref tnet_socket_type_t.
+* @retval @ref tnet_socket_t object.
+*/
+tnet_socket_t* tnet_socket_create(const char* host, tnet_port_t port, tnet_socket_type_t type)
+{
+ return tnet_socket_create_2(host, port, type, tsk_true, tsk_true);
+}
+
+
+
+
+/**@ingroup tnet_socket_group
+ * Closes a socket.
+ * @param sock The socket to close.
+ * @retval Zero if succeed and nonzero error code otherwise.
+**/
+int tnet_socket_close(tnet_socket_t *sock)
+{
+ return tnet_sockfd_close(&(sock->fd));
+}
+
+int tnet_socket_set_tlsfiles(tnet_socket_tls_t* socket, int isClient, const char* tlsfile_ca, const char* tlsfile_pvk, const char* tlsfile_pbk)
+{
+ if(socket){
+ return -1;
+ }
+
+ if(!TNET_SOCKET_TYPE_IS_TLS(socket->type)){
+ TSK_DEBUG_ERROR("Not TLS socket.");
+ return -2;
+ }
+
+ if(socket->tlshandle){
+ TSK_DEBUG_ERROR("TLS files already set.");
+ return -3;
+ }
+
+ if((socket->tlshandle = tnet_sockfd_set_tlsfiles(socket->fd, isClient, tlsfile_ca, tlsfile_pvk, tlsfile_pbk))){
+ return 0;
+ }
+ else{
+ return -4;
+ }
+}
+
+//=================================================================================================
+// SOCKET object definition
+//
+static tsk_object_t* tnet_socket_ctor(tsk_object_t * self, va_list * app)
+{
+ tnet_socket_t *sock = self;
+ if(sock){
+ int status;
+ tsk_bool_t nonblocking;
+ tsk_bool_t bindsocket;
+ tsk_istr_t port;
+ struct addrinfo *result = 0;
+ struct addrinfo *ptr = 0;
+ struct addrinfo hints;
+ tnet_host_t local_hostname;
+
+ const char *host = va_arg(*app, const char*);
+#if defined(__GNUC__)
+ sock->port = (tnet_port_t)va_arg(*app, unsigned);
+#else
+ sock->port = va_arg(*app, tnet_port_t);
+#endif
+ tsk_itoa(sock->port, &port);
+ sock->type = va_arg(*app, tnet_socket_type_t);
+ nonblocking = va_arg(*app, tsk_bool_t);
+ bindsocket = va_arg(*app, tsk_bool_t);
+
+ memset(local_hostname, 0, sizeof(local_hostname));
+
+ /* Get the local host name */
+ if(host != TNET_SOCKET_HOST_ANY && !tsk_strempty(host)){
+ memcpy(local_hostname, host, tsk_strlen(host)>sizeof(local_hostname)-1 ? sizeof(local_hostname)-1 : tsk_strlen(host));
+ }
+ else{
+ if(TNET_SOCKET_TYPE_IS_IPV6(sock->type)){
+ memcpy(local_hostname, "::", 2);
+ }
+ else{
+ memcpy(local_hostname, "0.0.0.0", 7);
+ }
+ //if((status = tnet_gethostname(&local_hostname)))
+ //{
+ // TNET_PRINT_LAST_ERROR("gethostname have failed.");
+ // goto bail;
+ //}
+ }
+
+ /* hints address info structure */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = TNET_SOCKET_TYPE_IS_IPV46(sock->type) ? AF_UNSPEC : (TNET_SOCKET_TYPE_IS_IPV6(sock->type) ? AF_INET6 : AF_INET);
+ hints.ai_socktype = TNET_SOCKET_TYPE_IS_STREAM(sock->type) ? SOCK_STREAM : SOCK_DGRAM;
+ hints.ai_protocol = TNET_SOCKET_TYPE_IS_STREAM(sock->type) ? IPPROTO_TCP : IPPROTO_UDP;
+ hints.ai_flags = AI_PASSIVE
+#if !TNET_UNDER_WINDOWS || _WIN32_WINNT>=0x600
+ | AI_ADDRCONFIG
+#endif
+ ;
+
+ /* Performs getaddrinfo */
+ if((status = tnet_getaddrinfo(local_hostname, port, &hints, &result))){
+ TNET_PRINT_LAST_ERROR("tnet_getaddrinfo(family=%d, hostname=%s and port=%s) failed: [%s]",
+ hints.ai_family, local_hostname, port, tnet_gai_strerror(status));
+ goto bail;
+ }
+
+ /* Find our address. */
+ for(ptr = result; ptr; ptr = ptr->ai_next){
+ sock->fd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
+ if(ptr->ai_family != AF_INET6 && ptr->ai_family != AF_INET){
+ continue;
+ }
+
+ if(bindsocket){
+ /* Bind the socket */
+ if((status = bind(sock->fd, ptr->ai_addr, ptr->ai_addrlen))){
+ TNET_PRINT_LAST_ERROR("bind have failed.");
+ tnet_socket_close(sock);
+ continue;
+ }
+
+ /* Get local IP string. */
+ if(status = tnet_get_ip_n_port(sock->fd , &sock->ip, &sock->port)) /* % */
+ //if((status = tnet_getnameinfo(ptr->ai_addr, ptr->ai_addrlen, sock->ip, sizeof(sock->ip), 0, 0, NI_NUMERICHOST)))
+ {
+ TNET_PRINT_LAST_ERROR("Failed to get local IP and port.");
+ tnet_socket_close(sock);
+ continue;
+ }
+// else{
+//#if TNET_UNDER_WINDOWS
+// int index;
+// if((index = tsk_strindexOf(sock->ip, tsk_strlen(sock->ip), "%")) > 0){
+// *(sock->ip + index) = '\0';
+// }
+//#endif
+// }
+ }
+
+ /* sets the real socket type (if ipv46) */
+ if(ptr->ai_family == AF_INET6) {
+ TNET_SOCKET_TYPE_SET_IPV6Only(sock->type);
+ }
+ else{
+ TNET_SOCKET_TYPE_SET_IPV4Only(sock->type);
+ }
+ break;
+ }
+
+ /* Check socket validity. */
+ if(!TNET_SOCKET_IS_VALID(sock)) {
+ TNET_PRINT_LAST_ERROR("Invalid socket.");
+ goto bail;
+ }
+
+ /* To avoid "Address already in use" error */
+ {
+#if defined(SOLARIS)
+ char yes = '1';
+#else
+ int yes = 1;
+#endif
+ if(setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(int))){
+ TNET_PRINT_LAST_ERROR("setsockopt(SO_REUSEADDR) have failed.");
+ }
+ }
+
+ /* Sets the socket to nonblocking mode */
+ if(nonblocking){
+ if((status = tnet_sockfd_set_nonblocking(sock->fd))){
+ goto bail;
+ }
+ }
+
+bail:
+ /* Free addrinfo */
+ tnet_freeaddrinfo(result);
+
+ /* Close socket if failed. */
+ if(status && TNET_SOCKET_IS_VALID(sock)){
+ tnet_socket_close(sock);
+ }
+
+ }
+ return self;
+}
+
+static tsk_object_t* tnet_socket_dtor(tsk_object_t * self)
+{
+ tnet_socket_t *sock = self;
+
+ if(sock){
+ /* Close the socket. */
+ if(sock->fd > 0){
+ tnet_socket_close(sock);
+ }
+ /* Clean up TLS handle*/
+ if(sock->tlshandle){
+ TSK_OBJECT_SAFE_FREE(sock->tlshandle);
+ }
+ }
+
+
+ return self;
+}
+
+static const tsk_object_def_t tnet_socket_def_s =
+{
+ sizeof(tnet_socket_t),
+ tnet_socket_ctor,
+ tnet_socket_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tnet_socket_def_t = &tnet_socket_def_s;
+
+
OpenPOWER on IntegriCloud