/sources/utils/network/socket.cpp
C++ | 690 lines | 508 code | 134 blank | 48 comment | 98 complexity | 107e68f1fdb87b69501579e744074f70 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0, BSD-2-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
- #include "basic_types.h"
- #if OS==OS_WINDOWS
- #define WINVER 0x0501 // set windows xp as minimum required system
- #define _WIN32_WINNT 0x0501 // set windows xp as minimum required system
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #define snprintf _snprintf
- #else
- #include <arpa/inet.h>
- #include <sys/fcntl.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/unistd.h>
- #include <sys/time.h>
- #include <netdb.h>
- #include <sys/ioctl.h>
- #include <errno.h>
- #endif
- #include <cstdio>
- #include "socket.h"
- // local functions
- namespace
- {
- #if OS==OS_WINDOWS
- static void close(int socketHandler)
- {
- closesocket(socketHandler);
- }
- // function provided here for compatibility with WindowsXP. On Vista and above this function is already present
- static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
- {
- if (af == AF_INET)
- {
- struct sockaddr_in in;
- memset(&in, 0, sizeof(in));
- in.sin_family = AF_INET;
- memcpy(&in.sin_addr, src, sizeof(struct in_addr));
- getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
- return dst;
- }
- else if (af == AF_INET6)
- {
- struct sockaddr_in6 in;
- memset(&in, 0, sizeof(in));
- in.sin6_family = AF_INET6;
- memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
- getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
- return dst;
- }
- return NULL;
- }
- #endif
- static uint16_t get_in_port(struct sockaddr* sa)
- {
- if (sa->sa_family == AF_INET)
- return ntohs(((struct sockaddr_in*)sa)->sin_port);
- return ntohs(((struct sockaddr_in6*)sa)->sin6_port);
- }
- static uint16_t get_local_port(int socketHandler)
- {
- struct sockaddr_storage sin;
- #if OS==OS_WINDOWS
- int size;
- #else
- socklen_t size;
- #endif
- size = sizeof(sin);
- if(getsockname(socketHandler, (struct sockaddr*)&sin, &size) == 0)
- return get_in_port((struct sockaddr*)&sin);
- else
- return 0;
- }
- static bool check_read_error(void)
- {
- #if OS==OS_WINDOWS
- if(WSAGetLastError() != WSAEWOULDBLOCK)
- return true;
- #else
- if(errno != EAGAIN && errno != EWOULDBLOCK)
- return true;
- #endif
- return false;
- }
- static void *get_in_addr(struct sockaddr *sa)
- {
- if (sa->sa_family == AF_INET)
- return &(((struct sockaddr_in*)sa)->sin_addr);
- return &(((struct sockaddr_in6*)sa)->sin6_addr);
- }
- static int get_local_address(int socketHandler, char address[network::ADDR_STRLEN])
- {
- struct sockaddr_storage sin;
- #if OS==OS_WINDOWS
- int size;
- #else
- socklen_t size;
- #endif
- size = sizeof(sin);
- if(getsockname(socketHandler, (struct sockaddr*)&sin, &size) != 0)
- return -1;
- inet_ntop(sin.ss_family, get_in_addr((struct sockaddr *)&sin), address, network::ADDR_STRLEN);
-
- return 0;
- }
- } // empty namespace
- namespace network
- {
- /// Receives data and stores it in buffer until buffer_size reached.
- /// @param buffer A pointer to a buffer where received data will be stored
- /// @param buffer_size Size of the buffer (in bytes)
- /// @return Size of received data or (-1) if Socket is non-blocking and there's no data received.
- int tcp_read(Socket *sock, void* buffer, size_t buffer_size)
- {
- return recv(sock->_socket_handler, (char*)buffer, buffer_size, 0);
- }
- /// Sends the data contained in buffer. Requires the Socket to be a CLIENT socket.
- /// @pre Socket must be CLIENT
- /// @param buffer A pointer to the data we want to send
- /// @param size Length of the data to be sent (bytes)
- bool tcp_send(Socket *sock, const void* buffer, size_t size)
- {
- if(sock->_type != CLIENT)
- {
- printf("ERROR: tcp_send: Expected client socket (socket with host and port target)\n");
- return false;
- }
- if(sock->_protocol != TCP)
- {
- printf("ERROR: tcp_send: Expected TCP protocol\n");
- return false;
- }
- size_t sentData = 0;
- while (sentData < size)
- {
- int status = ::send(sock->_socket_handler, (const char*)buffer + sentData, size - sentData, 0);
- if(status==-1 )
- {
- printf("ERROR: Error sending data\n");
- return false;
- }
- sentData += status;
- }
- return true;
- }
- // same as tcp read: duplicated for naming consistency
- int udp_read(Socket *sock, void* buffer, size_t buffer_size)
- {
- return recv(sock->_socket_handler, (char*)buffer, buffer_size, 0);
- }
- /// Requires the socket to be UDP. Source host address and port are returned in hostFrom and
- /// portFrom parameters. Data recieved is written in buffer address up to buffer_size.
- /// @param buffer Pointer to a buffer where received data will be stored
- /// @param buffer_size Size of the buffer
- /// @param[out] hostFrom Here the function will store the address of the remote host
- /// @param[out] portFrom Here the function will store the remote port
- /// @return the length of the data recieved
- int udp_read_from(Socket *sock, void *buffer, size_t buffer_size, char remote_host[ADDR_STRLEN], uint16_t *remote_port)
- {
- if(sock->_protocol!=UDP)
- {
- printf("ERROR: udp_read_from: UDP socket required\n");
- return -1;
- }
- struct sockaddr_storage addr;
- socklen_t addrSize = sizeof(addr);
- int status = recvfrom(sock->_socket_handler, (char*)buffer, buffer_size, 0, (struct sockaddr *)&addr, &addrSize);
- if(status == -1)
- {
- if(remote_host)
- strcpy(remote_host,"");
- if(remote_port)
- *remote_port = 0;
- if (check_read_error())
- {
- printf("ERROR: udp_read_from\n");
- return -1;
- }
- }
- else
- {
- if(remote_port)
- *remote_port = get_in_port((struct sockaddr*)&addr);
- if(remote_host)
- inet_ntop(addr.ss_family, get_in_addr((struct sockaddr *)&addr), remote_host, ADDR_STRLEN);
- }
- return status;
- }
- /// Sends the data contained in buffer to a given host:port. Requires the socket to be an UDP socket]
- ///
- /// @param buffer A pointer to the data we want to send
- /// @param size Size of the data to send (bytes)
- /// @param hostTo Target/remote host
- /// @param portTo Target/remote port
- bool udp_send_to(Socket *sock, const void *buffer, size_t size, const char *remote_host, uint16_t remote_port)
- {
- if(sock->_protocol != UDP)
- {
- printf("ERROR: udp_send_to: UDP socket required\n");
- return false;
- }
- struct addrinfo conf, *res;
- memset(&conf, 0, sizeof(conf));
- conf.ai_socktype = SOCK_DGRAM;
- conf.ai_family = AF_INET;
- if (sock->_ip_ver==IP6)
- conf.ai_family = AF_INET6;
- char portStr[10];
- snprintf(portStr, 10, "%u", remote_port);
-
- int status = getaddrinfo(remote_host, portStr, &conf, &res);
- if(status != 0)
- {
- printf("ERROR: udp_send_to: error setting addrInfo\n");
- return false;
- }
- size_t sentBytes = 0;
- while(sentBytes < size)
- {
- int status = ::sendto(sock->_socket_handler, (const char*)buffer + sentBytes, size - sentBytes, 0, res->ai_addr, res->ai_addrlen);
- if(status == -1)
- {
- printf("ERROR: udp_send_to: could not send the data\n");
- return false;
- }
- sentBytes += status;
- }
- return true;
- }
- bool init_tcp_client_socket(Socket *sock)
- {
- struct addrinfo conf, *res = NULL;
- memset(&conf, 0, sizeof(conf));
- conf.ai_socktype = SOCK_STREAM;
- conf.ai_family = AF_UNSPEC;
- if (sock->_ip_ver==IP4)
- conf.ai_family = AF_INET;
- else if (sock->_ip_ver==IP6)
- conf.ai_family = AF_INET6;
- char portStr[10];
- snprintf(portStr, 10, "%u", sock->_remote_port);
- int status = getaddrinfo(sock->_remote_host, portStr, &conf, &res);
- if(status != 0)
- {
- printf("ERROR: init_tcp_client_socket: Error setting addrInfo\n");
- return false;
- }
- bool connected = false;
- while(!connected && res)
- {
- sock->_socket_handler = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if(sock->_socket_handler != -1)
- {
- status = connect(sock->_socket_handler, res->ai_addr, res->ai_addrlen);
- if(status != -1)
- connected = true;
- else
- close(sock->_socket_handler);
- }
- if(connected && sock->_ip_ver == ANY)
- switch(res->ai_family)
- {
- case AF_INET:
- sock->_ip_ver = IP4;
- break;
- case AF_INET6:
- sock->_ip_ver = IP6;
- break;
- }
- res = res->ai_next;
- }
-
- if(!connected)
- {
- printf("ERROR: init_tcp_client_socket: error in socket connection/bind\n");
- return false;
- }
- if(!sock->_local_port)
- sock->_local_port = get_local_port(sock->_socket_handler);
- get_local_address(sock->_socket_handler, sock->_local_host);
- return true;
- }
- bool init_tcp_server_socket(Socket *sock)
- {
- struct addrinfo conf, *res = NULL;
- memset(&conf, 0, sizeof(conf));
- conf.ai_flags = AI_PASSIVE;
- conf.ai_socktype = SOCK_STREAM;
- conf.ai_family = AF_INET;
- if (sock->_ip_ver==IP6)
- conf.ai_family = AF_INET6;
- char portStr[10];
- snprintf(portStr, 10, "%u", sock->_local_port);
- const char* host = NULL;
- if (0!=strcmp(sock->_local_host,""))
- host = sock->_local_host;
- int status = getaddrinfo(host, portStr, &conf, &res);
- if(status != 0)
- {
- printf("ERROR: init_tcp_server_socket() unable to set addrinfo\n");
- return false;
- }
- bool connected = false;
- while(!connected && res)
- {
- sock->_socket_handler = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if(sock->_socket_handler != -1)
- {
- int yes = 1;
- #if OS==OS_WINDOWS
- char *optval = (char*)&yes;
- #else
- void *optval = (void*)&yes;
- #endif
- if (setsockopt(sock->_socket_handler, SOL_SOCKET, SO_REUSEADDR, optval, sizeof(int)) == -1)
- {
- printf("ERROR: init_tcp_server_socket: Error establishing socket options\n");
- return false;
- }
- if (bind(sock->_socket_handler, res->ai_addr, res->ai_addrlen) == -1)
- close(sock->_socket_handler);
- else
- connected = true;
- if (listen(sock->_socket_handler, sock->_listen_queue_size) == -1)
- {
- printf("ERROR: init_tcp_server_socket: could not start listening\n");
- return false;
- }
- }
- res = res->ai_next;
- }
- if(!connected)
- {
- printf("ERROR: init_tcp_server_socket: error in socket connection/bind\n");
- return false;
- }
- return true;
- }
- bool init_udp_socket(Socket *sock)
- {
- struct addrinfo conf, *res = NULL;
- memset(&conf, 0, sizeof(conf));
- conf.ai_flags = AI_PASSIVE;
- conf.ai_socktype = SOCK_DGRAM;
- conf.ai_family = AF_UNSPEC;
- if (sock->_ip_ver==IP4) conf.ai_family = AF_INET;
- if (sock->_ip_ver==IP6) conf.ai_family = AF_INET6;
- char portStr[10];
- snprintf(portStr, 10, "%u", sock->_local_port);
- const char* host = NULL;
- if (0!=strcmp(sock->_local_host,""))
- host = sock->_local_host;
- int status = getaddrinfo(host, portStr, &conf, &res);
- if(status != 0)
- {
- printf("ERROR: init_udp_socket: Error setting addrInfo\n");
- return false;
- }
- bool connected = false;
- while(!connected && res)
- {
- sock->_socket_handler = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if(sock->_socket_handler != -1)
- {
- if (bind(sock->_socket_handler, res->ai_addr, res->ai_addrlen) == -1)
- close(sock->_socket_handler);
- else
- connected = true;
- }
- if(connected && sock->_ip_ver == ANY)
- switch(res->ai_family)
- {
- case AF_INET:
- sock->_ip_ver = IP4;
- break;
- case AF_INET6:
- sock->_ip_ver = IP6;
- break;
- }
- res = res->ai_next;
- }
- if(!connected)
- {
- printf("ERROR: init_udp_socket: error in socket connection/bind\n");
- return false;
- }
- if(!sock->_local_port)
- sock->_local_port = get_local_port(sock->_socket_handler);
- get_local_address(sock->_socket_handler, sock->_local_host);
- return true;
- }
- /// Sets the Socket as blocking (if blocking is true) or as non-blocking (otherwise)
- /// @param blocking true to set the Socket as blocking; false to set the Socket as non-blocking
- void socket_set_blocking(Socket *sock, bool blocking)
- {
- sock->_blocking = blocking;
- int result = -1;
- #if OS==OS_WINDOWS
- u_long non_blocking = !blocking;
- result = ioctlsocket(sock->_socket_handler, FIONBIO, &non_blocking);
- if(result!=0)
- result = -1;
- #else
- int flags = fcntl(sock->_socket_handler, F_GETFL);
- if(blocking)
- result = fcntl(sock->_socket_handler, F_SETFL, flags & ~O_NONBLOCK);
- else
- result = fcntl(sock->_socket_handler, F_SETFL, flags | O_NONBLOCK);
- #endif
- }
- /// Creates a new CLIENT socket to handle the communication (send/recieve data) of
- /// this accepted connection. Requires the socket to be a SERVER TCP socket.
- /// @return A CLIENT socket that handles the new connection
- Socket* tcp_server_accept(Socket *server)
- {
- if(server->_protocol!=TCP || server->_type!=SERVER)
- {
- printf("ERROR: tcp_server_accept: required a TCP server socket\n");
- return NULL;
- }
- struct sockaddr_storage incoming_addr;
- #if OS==OS_WINDOWS
- int addrSize = sizeof(incoming_addr);
- #else
- socklen_t addrSize = sizeof(incoming_addr);
- #endif
- int new_handler = ::accept(server->_socket_handler, (struct sockaddr *)&incoming_addr, &addrSize);
- if(new_handler == -1)
- return NULL;
- Socket* acceptSocket = new Socket();
- acceptSocket->_socket_handler = new_handler;
- inet_ntop(incoming_addr.ss_family, get_in_addr((struct sockaddr *)&incoming_addr), acceptSocket->_remote_host, ADDR_STRLEN);
- acceptSocket->_remote_port = get_in_port((struct sockaddr *)&incoming_addr);
- acceptSocket->_local_port = get_local_port(acceptSocket->_socket_handler);
- get_local_address(acceptSocket->_socket_handler, acceptSocket->_local_host);
- acceptSocket->_protocol = server->_protocol;
- acceptSocket->_ip_ver = server->_ip_ver;
- acceptSocket->_type = CLIENT;
- acceptSocket->_listen_queue_size = 0;
- socket_set_blocking(acceptSocket,server->_blocking);
- return acceptSocket;
- }
- /// disconnect (close) the socket
- void socket_disconnect(Socket *sock)
- {
- if (sock && sock->_socket_handler!=-1)
- {
- close(sock->_socket_handler);
- sock->_socket_handler = -1;
- }
- }
- /// Creates a socket, connect it (if TCP) and sets it ready to send to hostTo:portTo.
- /// The local port of the socket is choosen by OS.
- /// @param hostTo the target/remote host
- /// @param portTo the target/remote port
- /// @param protocol the protocol to be used (TCP or UDP). TCP by default.
- /// @param ipVer the IP version to be used (IP4, IP6 or ANY). ANY by default.
- Socket* create_tcp_client(const char *remote_host, uint16_t remote_port, IPVer ipVer)
- {
- Socket *socket = new Socket();
- strcpy(socket->_remote_host,remote_host);
- socket->_remote_port = remote_port;
- socket->_local_port = 0;
- socket->_protocol = TCP;
- socket->_ip_ver = ipVer;
- socket->_type = CLIENT;
- socket->_blocking = true;
- socket->_listen_queue_size = 0;
-
- if (init_tcp_client_socket(socket))
- return socket;
- else
- {
- delete socket;
- return NULL;
- }
- }
- ///Creates a socket, binds it to portFrom port and listens for connections (if TCP).
- ///@param portFrom the local port the socket will be bound to
- ///@param protocol the protocol to be used (TCP or UDP). TCP by default.
- ///@param ipVer the IP version to be used (IP4, IP6 or ANY). IP4 by default.
- ///@param hostFrom the local address to be binded to (example: "localhost" or "127.0.0.1"). Empty (by default) means all available addresses.
- ///@param listen_queue_size the size of the internal buffer of the SERVER TCP socket where the connection requests are stored until accepted
- Socket* create_tcp_server(uint16_t local_port, const char *local_host, IPVer ipVer, uint16_t listen_queue_size)
- {
- Socket *socket = new Socket();
- strcpy(socket->_local_host,local_host);
- socket->_remote_port = 0;
- socket->_local_port = local_port;
- socket->_protocol = TCP;
- socket->_ip_ver = ipVer;
- socket->_type = SERVER;
- socket->_blocking = true;
- socket->_listen_queue_size = listen_queue_size;
- if (init_tcp_server_socket(socket))
- return socket;
- else
- {
- delete socket;
- return NULL;
- }
- }
- /// This client constructor for UDP Sockets allows to expecify the local port the socket
- /// will be bound to. It sets the socket ready to send data to hostTo:portTo.
- /// @param hostTo the target/remote host
- /// @param portTo the target/remote port
- /// @param portFrom the local port the socket will be bound to
- /// @param ipVer the IP version to be used (IP4, IP6 or ANY). ANY by default.
- Socket* create_udp_client(const char *remote_host, uint16_t remote_port, uint16_t local_port, IPVer ipVer)
- {
- Socket *socket = new Socket();
- strcpy(socket->_remote_host,remote_host);
- socket->_remote_port = remote_port;
- socket->_local_port = local_port;
- socket->_protocol = UDP;
- socket->_ip_ver = ipVer;
- socket->_type = CLIENT;
- socket->_blocking = true;
- socket->_listen_queue_size = 0;
- if (init_udp_socket(socket))
- return socket;
- else
- {
- delete socket;
- return NULL;
- }
- }
- void print_socket_data(Socket *sock)
- {
- printf("Socket data: \n"
- " _local_host = %s\n"
- " _local_port = %u\n"
- " _remote_host = %s\n"
- " _remote_port = %u\n"
- " _protocol = %d\n"
- " _ip_ver = %d\n"
- " _type = %d\n"
- " _blocking = %s\n"
- " _listen_queue_size = %u\n", sock->_local_host,
- sock->_local_port,
- sock->_remote_host,
- sock->_remote_port,
- sock->_protocol,
- sock->_ip_ver,
- sock->_type,
- sock->_blocking ? "true" : "false",
- sock->_listen_queue_size);
-
- }
- }
- namespace network_globals
- {
- bool init()
- {
- #if OS==OS_WINDOWS
- WSADATA wsaData;
- if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0)
- return false;
- #endif
- return true;
- }
-
- }