diff --git a/src/rnet.c b/src/rnet.c.review similarity index 96% rename from src/rnet.c rename to src/rnet.c.review index 55d4a26ac..5f2c5306f 100644 --- a/src/rnet.c +++ b/src/rnet.c.review @@ -1,2024 +1,2024 @@ -/********************************************************************************************* -* -* rnet - A simple and easy-to-use network module for raylib -* -* FEATURES: -* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API -* -* DEPENDENCIES: -* raylib.h - TraceLog -* rnet.h - platform-specific network includes -* -* CONTRIBUTORS: -* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2019 Jak Barnes (github: @syphonx) and Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -//---------------------------------------------------------------------------------- -// Check if config flags have been externally provided on compilation line -//---------------------------------------------------------------------------------- - -#include "rnet.h" - -#include "raylib.h" - -#include // Required for: assert() -#include // Required for: FILE, fopen(), fclose(), fread() -#include // Required for: malloc(), free() -#include // Required for: strcmp(), strncmp() - -//---------------------------------------------------------------------------------- -// Module defines -//---------------------------------------------------------------------------------- - -#define NET_DEBUG_ENABLED (1) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -typedef struct _SocketAddress -{ - struct sockaddr address; -} _SocketAddress; - -typedef struct _SocketAddressIPv4 -{ - struct sockaddr_in address; -} _SocketAddressIPv4; - -typedef struct _SocketAddressIPv6 -{ - struct sockaddr_in6 address; -} _SocketAddressIPv6; - -typedef struct _SocketAddressStorage -{ - struct sockaddr_storage address; -} _SocketAddressStorage; - -typedef struct _AddressInformation -{ - struct addrinfo addr; -} _AddressInformation; - - - -//---------------------------------------------------------------------------------- -// Global module forward declarations -//---------------------------------------------------------------------------------- - -static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol); -static const char *SocketAddressToString(struct sockaddr_storage *sockaddr); -static bool IsIPv4Address(const char *ip); -static bool IsIPv6Address(const char *ip); -static void *GetSocketPortPtr(struct sockaddr_storage *sa); -static void *GetSocketAddressPtr(struct sockaddr_storage *sa); -static bool IsSocketValid(Socket *sock); -static void SocketSetLastError(int err); -static int SocketGetLastError(); -static char *SocketGetLastErrorString(); -static char *SocketErrorCodeToString(int err); -static bool SocketSetDefaults(SocketConfig *config); -static bool InitSocket(Socket *sock, struct addrinfo *addr); -static bool CreateSocket(SocketConfig *config, SocketResult *outresult); -static bool SocketSetBlocking(Socket *sock); -static bool SocketSetNonBlocking(Socket *sock); -static bool SocketSetOptions(SocketConfig *config, Socket *sock); -static void SocketSetHints(SocketConfig *config, struct addrinfo *hints); - -//---------------------------------------------------------------------------------- -// Global module implementation -//---------------------------------------------------------------------------------- - -// Print socket information -static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol) -{ - switch (family) - { - case AF_UNSPEC: { TraceLog(LOG_DEBUG, "\tFamily: Unspecified"); - } - break; - case AF_INET: - { - TraceLog(LOG_DEBUG, "\tFamily: AF_INET (IPv4)"); - TraceLog(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr)); - } - break; - case AF_INET6: - { - TraceLog(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)"); - TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr)); - } - break; - case AF_NETBIOS: - { - TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); - } - break; - default: { TraceLog(LOG_DEBUG, "\tFamily: Other %ld", family); - } - break; - } - TraceLog(LOG_DEBUG, "\tSocket type:"); - switch (socktype) - { - case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; - case SOCK_STREAM: - TraceLog(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); - break; - case SOCK_DGRAM: - TraceLog(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); - break; - case SOCK_RAW: TraceLog(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break; - case SOCK_RDM: - TraceLog(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); - break; - case SOCK_SEQPACKET: - TraceLog(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); - break; - default: TraceLog(LOG_DEBUG, "\t- Other %ld", socktype); break; - } - TraceLog(LOG_DEBUG, "\tProtocol:"); - switch (protocol) - { - case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; - case IPPROTO_TCP: TraceLog(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break; - case IPPROTO_UDP: TraceLog(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break; - default: TraceLog(LOG_DEBUG, "\t- Other %ld", protocol); break; - } -} - -// Convert network ordered socket address to human readable string (127.0.0.1) -static const char *SocketAddressToString(struct sockaddr_storage *sockaddr) -{ - //static const char* ipv6[INET6_ADDRSTRLEN]; - assert(sockaddr != NULL); - assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6); - switch (sockaddr->ss_family) - { - case AF_INET: - { - //struct sockaddr_in *s = ((struct sockaddr_in *) sockaddr); - //return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); // TODO. - } - break; - case AF_INET6: - { - //struct sockaddr_in6 *s = ((struct sockaddr_in6 *) sockaddr); - //return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); // TODO. - } - break; - } - return NULL; -} - -// Check if the null terminated string ip is a valid IPv4 address -static bool IsIPv4Address(const char *ip) -{ - /* - struct sockaddr_in sa; - int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); // TODO. - return (result != 0); - */ - return false; -} - -// Check if the null terminated string ip is a valid IPv6 address -static bool IsIPv6Address(const char *ip) -{ - /* - struct sockaddr_in6 sa; - int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); // TODO. - return result != 0; - */ - return false; -} - -// Return a pointer to the port from the correct address family (IPv4, or IPv6) -static void *GetSocketPortPtr(struct sockaddr_storage *sa) -{ - if (sa->ss_family == AF_INET) - { - return &(((struct sockaddr_in *) sa)->sin_port); - } - - return &(((struct sockaddr_in6 *) sa)->sin6_port); -} - -// Return a pointer to the address from the correct address family (IPv4, or IPv6) -static void *GetSocketAddressPtr(struct sockaddr_storage *sa) -{ - if (sa->ss_family == AF_INET) - { - return &(((struct sockaddr_in *) sa)->sin_addr); - } - - return &(((struct sockaddr_in6 *) sa)->sin6_addr); -} - -// Is the socket in a valid state? -static bool IsSocketValid(Socket *sock) -{ - if (sock != NULL) - { - return (sock->channel != INVALID_SOCKET); - } - return false; -} - -// Sets the error code that can be retrieved through the WSAGetLastError function. -static void SocketSetLastError(int err) -{ -#if defined(_WIN32) - WSASetLastError(err); -#else - errno = err; -#endif -} - -// Returns the error status for the last Sockets operation that failed -static int SocketGetLastError() -{ -#if defined(_WIN32) - return WSAGetLastError(); -#else - return errno; -#endif -} - -// Returns a human-readable string representing the last error message -static char *SocketGetLastErrorString() -{ - return SocketErrorCodeToString(SocketGetLastError()); -} - -// Returns a human-readable string representing the error message (err) -static char *SocketErrorCodeToString(int err) -{ -#if defined(_WIN32) - static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; - sprintf(gaiStrErrorBuffer, "%s", gai_strerror(err)); - return gaiStrErrorBuffer; -#else - return gai_strerror(err); -#endif -} - -// Set the defaults in the supplied SocketConfig if they're not already set -static bool SocketSetDefaults(SocketConfig *config) -{ - if (config->backlog_size == 0) - { - config->backlog_size = SOCKET_MAX_QUEUE_SIZE; - } - - return true; -} - -// Create the socket channel -static bool InitSocket(Socket *sock, struct addrinfo *addr) -{ - switch (sock->type) - { - case SOCKET_TCP: - if (addr->ai_family == AF_INET) - { - sock->channel = socket(AF_INET, SOCK_STREAM, 0); - } - else - { - sock->channel = socket(AF_INET6, SOCK_STREAM, 0); - } - break; - case SOCKET_UDP: - if (addr->ai_family == AF_INET) - { - sock->channel = socket(AF_INET, SOCK_DGRAM, 0); - } - else - { - sock->channel = socket(AF_INET6, SOCK_DGRAM, 0); - } - break; - default: - TraceLog(LOG_WARNING, "Invalid socket type specified."); - break; - } - return IsSocketValid(sock); -} - -// CreateSocket() - Interally called by CreateSocket() -// -// This here is the bread and butter of the socket API, This function will -// attempt to open a socket, bind and listen to it based on the config passed in -// -// SocketConfig* config - Configuration for which socket to open -// SocketResult* result - The results of this function (if any, including errors) -// -// e.g. -// SocketConfig server_config = { SocketConfig client_config = { -// .host = "127.0.0.1", .host = "127.0.0.1", -// .port = 8080, .port = 8080, -// .server = true, }; -// .nonblocking = true, -// }; -// SocketResult server_res; SocketResult client_res; -static bool CreateSocket(SocketConfig *config, SocketResult *outresult) -{ - bool success = true; - int addrstatus; - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) - struct addrinfo *res; // A pointer to the resulting address list - outresult->socket->channel = INVALID_SOCKET; - outresult->status = RESULT_FAILURE; - - // Set the socket type - outresult->socket->type = config->type; - - // Set the hints based on information in the config - // - // AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name. - // AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6); - // Note: This causes a subsequent call to bind() to auto-fill the IP address - // of the struct sockaddr with the address of the current host. - // - SocketSetHints(config, &hints); - - // Populate address information - addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set - config->port, // e.g. "http" or port number - &hints, // e.g. SOCK_STREAM/SOCK_DGRAM - &res // The struct to populate - ); - - // Did we succeed? - if (addrstatus != 0) - { - outresult->socket->status = SocketGetLastError(); - TraceLog(LOG_WARNING, - "Socket Error: %s", - SocketErrorCodeToString(outresult->socket->status)); - SocketSetLastError(0); - TraceLog(LOG_WARNING, - "Failed to get resolve host %s:%s: %s", - config->host, - config->port, - SocketGetLastErrorString()); - return (success = false); - } - else - { - char hoststr[NI_MAXHOST]; - char portstr[NI_MAXSERV]; - //socklen_t client_len = sizeof(struct sockaddr_storage); - //int rc = getnameinfo((struct sockaddr *) res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); - TraceLog(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr); - } - - // Walk the address information linked-list - struct addrinfo *it; - for (it = res; it != NULL; it = it->ai_next) - { - // Initialise the socket - if (!InitSocket(outresult->socket, it)) - { - outresult->socket->status = SocketGetLastError(); - TraceLog(LOG_WARNING, - "Socket Error: %s", - SocketErrorCodeToString(outresult->socket->status)); - SocketSetLastError(0); - continue; - } - - // Set socket options - if (!SocketSetOptions(config, outresult->socket)) - { - outresult->socket->status = SocketGetLastError(); - TraceLog(LOG_WARNING, - "Socket Error: %s", - SocketErrorCodeToString(outresult->socket->status)); - SocketSetLastError(0); - freeaddrinfo(res); - return (success = false); - } - } - - if (!IsSocketValid(outresult->socket)) - { - outresult->socket->status = SocketGetLastError(); - TraceLog( - LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status)); - SocketSetLastError(0); - freeaddrinfo(res); - return (success = false); - } - - if (success) - { - outresult->status = RESULT_SUCCESS; - outresult->socket->ready = 0; - outresult->socket->status = 0; - if (!(config->type == SOCKET_UDP)) - { - outresult->socket->isServer = config->server; - } - switch (res->ai_addr->sa_family) - { - case AF_INET: - { - outresult->socket->addripv4 = (struct _SocketAddressIPv4 *) malloc( - sizeof(*outresult->socket->addripv4)); - if (outresult->socket->addripv4 != NULL) - { - memset(outresult->socket->addripv4, 0, - sizeof(*outresult->socket->addripv4)); - if (outresult->socket->addripv4 != NULL) - { - memcpy(&outresult->socket->addripv4->address, - (struct sockaddr_in *) res->ai_addr, sizeof(struct sockaddr_in)); - outresult->socket->isIPv6 = false; - char hoststr[NI_MAXHOST]; - char portstr[NI_MAXSERV]; - socklen_t client_len = sizeof(struct sockaddr_storage); - getnameinfo( - (struct sockaddr *) &outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); - TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); - } - } - } - break; - case AF_INET6: - { - outresult->socket->addripv6 = (struct _SocketAddressIPv6 *) malloc( - sizeof(*outresult->socket->addripv6)); - if (outresult->socket->addripv6 != NULL) - { - memset(outresult->socket->addripv6, 0, - sizeof(*outresult->socket->addripv6)); - if (outresult->socket->addripv6 != NULL) - { - memcpy(&outresult->socket->addripv6->address, - (struct sockaddr_in6 *) res->ai_addr, sizeof(struct sockaddr_in6)); - outresult->socket->isIPv6 = true; - char hoststr[NI_MAXHOST]; - char portstr[NI_MAXSERV]; - socklen_t client_len = sizeof(struct sockaddr_storage); - getnameinfo( - (struct sockaddr *) &outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); - TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); - } - } - } - break; - } - } - freeaddrinfo(res); - return success; -} - -// Set the state of the Socket sock to blocking -static bool SocketSetBlocking(Socket *sock) -{ - bool ret = true; -#if defined(_WIN32) - unsigned long mode = 0; - ret = ioctlsocket(sock->channel, FIONBIO, &mode); -#else - const int flags = fcntl(sock->channel, F_GETFL, 0); - if (!(flags & O_NONBLOCK)) - { - TraceLog(LOG_DEBUG, "Socket was already in blocking mode"); - return ret; - } - - ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK))); -#endif - return ret; -} - -// Set the state of the Socket sock to non-blocking -static bool SocketSetNonBlocking(Socket *sock) -{ - bool ret = true; -#if defined(_WIN32) - unsigned long mode = 1; - ret = ioctlsocket(sock->channel, FIONBIO, &mode); -#else - const int flags = fcntl(sock->channel, F_GETFL, 0); - if ((flags & O_NONBLOCK)) - { - TraceLog(LOG_DEBUG, "Socket was already in non-blocking mode"); - return ret; - } - ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK))); -#endif - return ret; -} - -// Set options specified in SocketConfig to Socket sock -static bool SocketSetOptions(SocketConfig *config, Socket *sock) -{ - for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++) - { - SocketOpt *opt = &config->sockopts[i]; - if (opt->id == 0) - { - break; - } - - if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) - { - return false; - } - } - - return true; -} - -// Set "hints" in an addrinfo struct, to be passed to getaddrinfo. -static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) -{ - if (config == NULL || hints == NULL) - { - return; - } - memset(hints, 0, sizeof(*hints)); - - // Check if the ip supplied in the config is a valid ipv4 ip ipv6 address - if (IsIPv4Address(config->host)) - { - hints->ai_family = AF_INET; - hints->ai_flags |= AI_NUMERICHOST; - } - else - { - if (IsIPv6Address(config->host)) - { - hints->ai_family = AF_INET6; - hints->ai_flags |= AI_NUMERICHOST; - } - else - { - hints->ai_family = AF_UNSPEC; - } - } - - if (config->type == SOCKET_UDP) - { - hints->ai_socktype = SOCK_DGRAM; - } - else - { - hints->ai_socktype = SOCK_STREAM; - } - - // Set passive unless UDP client - if (!(config->type == SOCKET_UDP) || config->server) - { - hints->ai_flags = AI_PASSIVE; - } -} - -//---------------------------------------------------------------------------------- -// Module implementation -//---------------------------------------------------------------------------------- - -// Initialise the network (requires for windows platforms only) -bool InitNetwork() -{ -#if defined(_WIN32) - WORD wVersionRequested; - WSADATA wsaData; - int err; - - wVersionRequested = MAKEWORD(2, 2); - err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) - { - TraceLog(LOG_WARNING, "WinSock failed to initialise."); - return false; - } - else - { - TraceLog(LOG_INFO, "WinSock initialised."); - } - - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) - { - TraceLog(LOG_WARNING, "WinSock failed to initialise."); - WSACleanup(); - return false; - } - - return true; -#else - return true; -#endif -} - -// Cleanup, and close the network -void CloseNetwork() -{ -#if defined(_WIN32) - WSACleanup(); -#endif -} - -// Protocol-independent name resolution from an address to an ANSI host name -// and from a port number to the ANSI service name. -// -// The flags parameter can be used to customize processing of the getnameinfo function -// -// The following flags are available: -// -// NAME_INFO_DEFAULT 0x00 // No flags set -// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts -// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address -// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS -// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) -// NAME_INFO_DGRAM 0x10 // Service is a datagram service -void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv) -{ - // Variables - int status; // Status value to return (0) is success - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) - struct addrinfo *res; // A pointer to the resulting address list - - // Set the hints - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) - hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) - - // Populate address information - status = getaddrinfo(ip, // e.g. "www.example.com" or IP - port, // e.g. "http" or port number - &hints, // e.g. SOCK_STREAM/SOCK_DGRAM - &res // The struct to populate - ); - - // Did we succeed? - if (status != 0) - { - TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno)); - } - else - { - TraceLog(LOG_DEBUG, "Resolving... %s::%s", ip, port); - } - - // Attempt to resolve network byte order ip to hostname - switch (res->ai_family) - { - case AF_INET: - status = getnameinfo(&*((struct sockaddr *) res->ai_addr), - sizeof(*((struct sockaddr_in *) res->ai_addr)), - host, - NI_MAXHOST, - serv, - NI_MAXSERV, - flags); - break; - case AF_INET6: - /* - status = getnameinfo(&*((struct sockaddr_in6 *) res->ai_addr), // TODO. - sizeof(*((struct sockaddr_in6 *) res->ai_addr)), - host, NI_MAXHOST, serv, NI_MAXSERV, flags); - */ - break; - default: break; - } - - if (status != 0) - { - TraceLog(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString()); - } - else - { - TraceLog(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host); - } - - // Free the pointer to the data returned by addrinfo - freeaddrinfo(res); -} - -// Protocol-independent translation from an ANSI host name to an address -// -// e.g. -// const char* address = "127.0.0.1" (local address) -// const char* port = "80" -// -// Parameters: -// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string. -// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string. -// -// Returns: -// The total amount of addresses found, -1 on error -// -int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr) -{ - // Variables - int status; // Status value to return (0) is success - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) - struct addrinfo *res; // will point to the results - struct addrinfo *iterator; - assert(((address != NULL || address != 0) || (service != NULL || service != 0))); - assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC))); - - // Set the hints - memset(&hints, 0, sizeof hints); - hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6) - hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) - hints.ai_flags = flags; - assert((hints.ai_addrlen == 0) || (hints.ai_addrlen == 0)); - assert((hints.ai_canonname == 0) || (hints.ai_canonname == 0)); - assert((hints.ai_addr == 0) || (hints.ai_addr == 0)); - assert((hints.ai_next == 0) || (hints.ai_next == 0)); - - // When the address is NULL, populate the IP for me - if (address == NULL) - { - if ((hints.ai_flags & AI_PASSIVE) == 0) - { - hints.ai_flags |= AI_PASSIVE; - } - } - - TraceLog(LOG_INFO, "Resolving host..."); - - // Populate address information - status = getaddrinfo(address, // e.g. "www.example.com" or IP - service, // e.g. "http" or port number - &hints, // e.g. SOCK_STREAM/SOCK_DGRAM - &res // The struct to populate - ); - - // Did we succeed? - if (status != 0) - { - int error = SocketGetLastError(); - SocketSetLastError(0); - TraceLog(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error)); - return -1; - } - else - { - TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, service); - } - - // Calculate the size of the address information list - int size = 0; - for (iterator = res; iterator != NULL; iterator = iterator->ai_next) - { - size++; - } - - // Validate the size is > 0, otherwise return - if (size <= 0) - { - TraceLog(LOG_WARNING, "Error, no addresses found."); - return -1; - } - - // If not address list was allocated, allocate it dynamically with the known address size - if (outAddr == NULL) - { - outAddr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); - } - - // Dynamically allocate an array of address information structs - if (outAddr != NULL) - { - int i; - for (i = 0; i < size; ++i) - { - outAddr[i] = AllocAddress(); - if (outAddr[i] == NULL) - { - break; - } - } - outAddr[i] = NULL; - if (i != size) - { - outAddr = NULL; - } - } - else - { - TraceLog(LOG_WARNING, - "Error, failed to dynamically allocate memory for the address list"); - return -1; - } - - // Copy all the address information from res into outAddrList - int i = 0; - for (iterator = res; iterator != NULL; iterator = iterator->ai_next) - { - if (i < size) - { - outAddr[i]->addr.ai_flags = iterator->ai_flags; - outAddr[i]->addr.ai_family = iterator->ai_family; - outAddr[i]->addr.ai_socktype = iterator->ai_socktype; - outAddr[i]->addr.ai_protocol = iterator->ai_protocol; - outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen; - *outAddr[i]->addr.ai_addr = *iterator->ai_addr; -#if NET_DEBUG_ENABLED - TraceLog(LOG_DEBUG, "GetAddressInformation"); - TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); - //PrintSocket(outAddr[i]->addr.ai_addr, outAddr[i]->addr.ai_family, outAddr[i]->addr.ai_socktype, outAddr[i]->addr.ai_protocol); - TraceLog(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen); - TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); -#endif - i++; - } - } - - // Free the pointer to the data returned by addrinfo - freeaddrinfo(res); - - // Return the total count of addresses found - return size; -} - -// This here is the bread and butter of the socket API, This function will -// attempt to open a socket, bind and listen to it based on the config passed in -// -// SocketConfig* config - Configuration for which socket to open -// SocketResult* result - The results of this function (if any, including errors) -// -// e.g. -// SocketConfig server_config = { SocketConfig client_config = { -// .host = "127.0.0.1", .host = "127.0.0.1", -// .port = 8080, .port = 8080, -// .server = true, }; -// .nonblocking = true, -// }; -// SocketResult server_res; SocketResult client_res; -bool SocketCreate(SocketConfig *config, SocketResult *result) -{ - // Socket creation result - bool success = true; - - // Make sure we've not received a null config or result pointer - if (config == NULL || result == NULL) - { - return (success = false); - } - - // Set the defaults based on the config - if (!SocketSetDefaults(config)) - { - TraceLog(LOG_WARNING, "Configuration Error."); - success = false; - } - else - { - // Create the socket - if (CreateSocket(config, result)) - { - if (config->nonblocking) - { - SocketSetNonBlocking(result->socket); - } - else - { - SocketSetBlocking(result->socket); - } - } - else - { - success = false; - } - } - return success; -} - -// Bind a socket to a local address -// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function. -bool SocketBind(SocketConfig *config, SocketResult *result) -{ - bool success = false; - result->status = RESULT_FAILURE; - struct sockaddr_storage *sock_addr = NULL; - - // Don't bind to a socket that isn't configured as a server - if (!IsSocketValid(result->socket) || !config->server) - { - TraceLog(LOG_WARNING, - "Cannot bind to socket marked as \"Client\" in SocketConfig."); - success = false; - } - else - { - if (result->socket->isIPv6) - { - sock_addr = (struct sockaddr_storage *) &result->socket->addripv6->address; - } - else - { - sock_addr = (struct sockaddr_storage *) &result->socket->addripv4->address; - } - if (sock_addr != NULL) - { - if (bind(result->socket->channel, (struct sockaddr *) sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR) - { - TraceLog(LOG_INFO, "Successfully bound socket."); - success = true; - } - else - { - result->socket->status = SocketGetLastError(); - TraceLog(LOG_WARNING, "Socket Error: %s", - SocketErrorCodeToString(result->socket->status)); - SocketSetLastError(0); - success = false; - } - } - } - // Was the bind a success? - if (success) - { - result->status = RESULT_SUCCESS; - result->socket->ready = 0; - result->socket->status = 0; - socklen_t sock_len = sizeof(*sock_addr); - if (getsockname(result->socket->channel, (struct sockaddr *) sock_addr, &sock_len) < 0) - { - TraceLog(LOG_WARNING, "Couldn't get socket address"); - } - else - { - struct sockaddr_in *s = (struct sockaddr_in *) sock_addr; - // result->socket->address.host = s->sin_addr.s_addr; - // result->socket->address.port = s->sin_port; - - // - result->socket->addripv4 - = (struct _SocketAddressIPv4 *) malloc(sizeof(*result->socket->addripv4)); - if (result->socket->addripv4 != NULL) - { - memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4)); - } - memcpy(&result->socket->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); - // - } - } - return success; -} - -// Listens (and queues) incoming connections requests for a bound port. -bool SocketListen(SocketConfig *config, SocketResult *result) -{ - bool success = false; - result->status = RESULT_FAILURE; - - // Don't bind to a socket that isn't configured as a server - if (!IsSocketValid(result->socket) || !config->server) - { - TraceLog(LOG_WARNING, - "Cannot listen on socket marked as \"Client\" in SocketConfig."); - success = false; - } - else - { - // Don't listen on UDP sockets - if (!(config->type == SOCKET_UDP)) - { - if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR) - { - TraceLog(LOG_INFO, "Started listening on socket..."); - success = true; - } - else - { - success = false; - result->socket->status = SocketGetLastError(); - TraceLog(LOG_WARNING, "Socket Error: %s", - SocketErrorCodeToString(result->socket->status)); - SocketSetLastError(0); - } - } - else - { - TraceLog(LOG_WARNING, - "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); - success = false; - } - } - - // Was the listen a success? - if (success) - { - result->status = RESULT_SUCCESS; - result->socket->ready = 0; - result->socket->status = 0; - } - return success; -} - -// Connect the socket to the destination specified by "host" and "port" in SocketConfig -bool SocketConnect(SocketConfig *config, SocketResult *result) -{ - bool success = true; - result->status = RESULT_FAILURE; - - // Only bind to sockets marked as server - if (config->server) - { - TraceLog(LOG_WARNING, - "Cannot connect to socket marked as \"Server\" in SocketConfig."); - success = false; - } - else - { - if (IsIPv4Address(config->host)) - { - struct sockaddr_in ip4addr; - ip4addr.sin_family = AF_INET; - unsigned long hport; - hport = strtoul(config->port, NULL, 0); - ip4addr.sin_port = htons(hport); - - // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows). - - //inet_pton(AF_INET, config->host, &ip4addr.sin_addr); - int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip4addr, sizeof(ip4addr)); - if (connect_result == SOCKET_ERROR) - { - result->socket->status = SocketGetLastError(); - SocketSetLastError(0); - switch (result->socket->status) - { - case WSAEWOULDBLOCK: - { - success = true; - break; - } - default: - { - TraceLog(LOG_WARNING, "Socket Error: %s", - SocketErrorCodeToString(result->socket->status)); - success = false; - break; - } - } - } - else - { - TraceLog(LOG_INFO, "Successfully connected to socket."); - success = true; - } - } - else - { - if (IsIPv6Address(config->host)) - { - struct sockaddr_in6 ip6addr; - ip6addr.sin6_family = AF_INET6; - unsigned long hport; - hport = strtoul(config->port, NULL, 0); - ip6addr.sin6_port = htons(hport); - //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO. - int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip6addr, sizeof(ip6addr)); - if (connect_result == SOCKET_ERROR) - { - result->socket->status = SocketGetLastError(); - SocketSetLastError(0); - switch (result->socket->status) - { - case WSAEWOULDBLOCK: - { - success = true; - break; - } - default: - { - TraceLog(LOG_WARNING, "Socket Error: %s", - SocketErrorCodeToString(result->socket->status)); - success = false; - break; - } - } - } - else - { - TraceLog(LOG_INFO, "Successfully connected to socket."); - success = true; - } - } - } - } - - if (success) - { - result->status = RESULT_SUCCESS; - result->socket->ready = 0; - result->socket->status = 0; - } - - return success; -} - -// Closes an existing socket -// -// SocketChannel socket - The id of the socket to close -void SocketClose(Socket *sock) -{ - if (sock != NULL) - { - if (sock->channel != INVALID_SOCKET) - { - closesocket(sock->channel); - } - } -} - -// Returns the sockaddress for a specific socket in a generic storage struct -SocketAddressStorage SocketGetPeerAddress(Socket *sock) -{ - // TODO. - /* - if (sock->isServer) return NULL; - if (sock->isIPv6) return sock->addripv6; - else return sock->addripv4; - */ - - return NULL; -} - -// Return the address-type appropriate host portion of a socket address -char *GetSocketAddressHost(SocketAddressStorage storage) -{ - assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); - return SocketAddressToString((struct sockaddr_storage *) storage); -} - -// Return the address-type appropriate port(service) portion of a socket address -short GetSocketAddressPort(SocketAddressStorage storage) -{ - //return ntohs(GetSocketPortPtr(storage)); // TODO. - - return 0; -} - -// The accept function permits an incoming connection attempt on a socket. -// -// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) -// SocketResult* out - The result of this function (if any, including errors) -// -// e.g. -// -// SocketResult connection; -// bool connected = false; -// if (!connected) -// { -// if (SocketAccept(server_res.socket.channel, &connection)) -// { -// connected = true; -// } -// } -Socket *SocketAccept(Socket *server, SocketConfig *config) -{ - if (!server->isServer || server->type == SOCKET_UDP) - { - return NULL; - } - struct sockaddr_storage sock_addr; - socklen_t sock_alen; - Socket * sock; - sock = AllocSocket(); - server->ready = 0; - sock_alen = sizeof(sock_addr); - sock->channel = accept(server->channel, (struct sockaddr *) &sock_addr, &sock_alen); - if (sock->channel == INVALID_SOCKET) - { - sock->status = SocketGetLastError(); - TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - SocketClose(sock); - return NULL; - } - (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); - sock->isServer = false; - sock->ready = 0; - sock->type = server->type; - switch (sock_addr.ss_family) - { - case AF_INET: - { - struct sockaddr_in *s = ((struct sockaddr_in *) &sock_addr); - sock->addripv4 = (struct _SocketAddressIPv4 *) malloc(sizeof(*sock->addripv4)); - if (sock->addripv4 != NULL) - { - memset(sock->addripv4, 0, sizeof(*sock->addripv4)); - memcpy(&sock->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); - TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), - ntohs(sock->addripv4->address.sin_port)); - } - } - break; - case AF_INET6: - { - struct sockaddr_in6 *s = ((struct sockaddr_in6 *) &sock_addr); - sock->addripv6 = (struct _SocketAddressIPv6 *) malloc(sizeof(*sock->addripv6)); - if (sock->addripv6 != NULL) - { - memset(sock->addripv6, 0, sizeof(*sock->addripv6)); - memcpy(&sock->addripv6->address, (struct sockaddr_in6 *) &s->sin6_addr, sizeof(struct sockaddr_in6)); - TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), - ntohs(sock->addripv6->address.sin6_port)); - } - } - break; - } - return sock; -} - -// Verify that the channel is in the valid range -static int ValidChannel(int channel) -{ - if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) - { - TraceLog(LOG_WARNING, "Invalid channel"); - return 0; - } - return 1; -} - -// Set the socket channel -int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) -{ - struct UDPChannel *binding; - if (socket == NULL) - { - TraceLog(LOG_WARNING, "Passed a NULL socket"); - return (-1); - } - if (channel == -1) - { - for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) - { - binding = &socket->binding[channel]; - if (binding->numbound < SOCKET_MAX_UDPADDRESSES) - { - break; - } - } - } - else - { - if (!ValidChannel(channel)) - { - return (-1); - } - binding = &socket->binding[channel]; - } - if (binding->numbound == SOCKET_MAX_UDPADDRESSES) - { - TraceLog(LOG_WARNING, "No room for new addresses"); - return (-1); - } - binding->address[binding->numbound++] = *address; - return (channel); -} - -// Remove the socket channel -void SocketUnsetChannel(Socket *socket, int channel) -{ - if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) - { - socket->binding[channel].numbound = 0; - } -} - -/* Allocate/free a single UDP packet 'size' bytes long. - The new packet is returned, or NULL if the function ran out of memory. - */ -SocketDataPacket *AllocPacket(int size) -{ - SocketDataPacket *packet; - int error; - - error = 1; - packet = (SocketDataPacket *) malloc(sizeof(*packet)); - if (packet != NULL) - { - packet->maxlen = size; - packet->data = (uint8_t *) malloc(size); - if (packet->data != NULL) - { - error = 0; - } - } - if (error) - { - FreePacket(packet); - packet = NULL; - } - return (packet); -} - -int ResizePacket(SocketDataPacket *packet, int newsize) -{ - uint8_t *newdata; - - newdata = (uint8_t *) malloc(newsize); - if (newdata != NULL) - { - free(packet->data); - packet->data = newdata; - packet->maxlen = newsize; - } - return (packet->maxlen); -} - -void FreePacket(SocketDataPacket *packet) -{ - if (packet) - { - free(packet->data); - free(packet); - } -} - -/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, - each 'size' bytes long. - A pointer to the packet array is returned, or NULL if the function ran out - of memory. - */ -SocketDataPacket **AllocPacketList(int howmany, int size) -{ - SocketDataPacket **packetV; - - packetV = (SocketDataPacket **) malloc((howmany + 1) * sizeof(*packetV)); - if (packetV != NULL) - { - int i; - for (i = 0; i < howmany; ++i) - { - packetV[i] = AllocPacket(size); - if (packetV[i] == NULL) - { - break; - } - } - packetV[i] = NULL; - - if (i != howmany) - { - FreePacketList(packetV); - packetV = NULL; - } - } - return (packetV); -} - -void FreePacketList(SocketDataPacket **packetV) -{ - if (packetV) - { - int i; - for (i = 0; packetV[i]; ++i) - { - FreePacket(packetV[i]); - } - free(packetV); - } -} - -// Send 'len' bytes of 'data' over the non-server socket 'sock' -// -// Example -int SocketSend(Socket *sock, const void *datap, int length) -{ - int sent = 0; - int left = length; - int status = -1; - int numsent = 0; - const unsigned char *data = (const unsigned char *) datap; - - // Server sockets are for accepting connections only - if (sock->isServer) - { - TraceLog(LOG_WARNING, "Cannot send information on a server socket"); - return -1; - } - - // Which socket are we trying to send data on - switch (sock->type) - { - case SOCKET_TCP: - { - SocketSetLastError(0); - do - { - length = send(sock->channel, (const char *) data, left, 0); - if (length > 0) - { - sent += length; - left -= length; - data += length; - } - } while ((left > 0) && // While we still have bytes left to send - ((length > 0) || // The amount of bytes we actually sent is > 0 - (SocketGetLastError() == WSAEINTR)) // The socket was interupted - ); - - if (length == SOCKET_ERROR) - { - sock->status = SocketGetLastError(); - TraceLog(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - } - else - { - TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); - } - - return sent; - } - break; - case SOCKET_UDP: - { - SocketSetLastError(0); - if (sock->isIPv6) - { - status = sendto(sock->channel, (const char *) data, left, 0, - (struct sockaddr *) &sock->addripv6->address, - sizeof(sock->addripv6->address)); - } - else - { - status = sendto(sock->channel, (const char *) data, left, 0, - (struct sockaddr *) &sock->addripv4->address, - sizeof(sock->addripv4->address)); - } - if (sent >= 0) - { - sock->status = 0; - ++numsent; - TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); - } - else - { - sock->status = SocketGetLastError(); - TraceLog(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); - SocketSetLastError(0); - return 0; - } - return numsent; - } - break; - default: break; - } - return -1; -} - -// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', -// and store them in the buffer pointed to by 'data'. -// This function returns the actual amount of data received. If the return -// value is less than or equal to zero, then either the remote connection was -// closed, or an unknown socket error occurred. -int SocketReceive(Socket *sock, void *data, int maxlen) -{ - int len = 0; - int numrecv = 0; - int status = 0; - socklen_t sock_len; - struct sockaddr_storage sock_addr; - //char ip[INET6_ADDRSTRLEN]; - - // Server sockets are for accepting connections only - if (sock->isServer && sock->type == SOCKET_TCP) - { - sock->status = SocketGetLastError(); - TraceLog(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data"); - SocketSetLastError(0); - return 0; - } - - // Which socket are we trying to send data on - switch (sock->type) - { - case SOCKET_TCP: - { - SocketSetLastError(0); - do - { - len = recv(sock->channel, (char *) data, maxlen, 0); - } while (SocketGetLastError() == WSAEINTR); - - if (len > 0) - { - // Who sent the packet? - if (sock->type == SOCKET_UDP) - { - //TraceLog(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *) &sock_addr), ip, sizeof(ip))); - } - - ((unsigned char *) data)[len] = '\0'; // Add null terminating character to the end of the stream - TraceLog(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); - } - - sock->ready = 0; - return len; - } - break; - case SOCKET_UDP: - { - SocketSetLastError(0); - sock_len = sizeof(sock_addr); - status = recvfrom(sock->channel, // The receving channel - data, // A pointer to the data buffer to fill - maxlen, // The max length of the data to fill - 0, // Flags - (struct sockaddr *) &sock_addr, // The address of the recevied data - &sock_len // The length of the received data address - ); - if (status >= 0) - { - ++numrecv; - } - else - { - sock->status = SocketGetLastError(); - switch (sock->status) - { - case WSAEWOULDBLOCK: { break; - } - default: - { - TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - break; - } - } - SocketSetLastError(0); - return 0; - } - sock->ready = 0; - return numrecv; - } - break; - } - return -1; -} - -// Does the socket have it's 'ready' flag set? -bool IsSocketReady(Socket *sock) -{ - return (sock != NULL) && (sock->ready); -} - -// Check if the socket is considered connected -bool IsSocketConnected(Socket *sock) -{ -#if defined(_WIN32) - FD_SET writefds; - FD_ZERO(&writefds); - FD_SET(sock->channel, &writefds); - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 1000000000UL; - int total = select(0, NULL, &writefds, NULL, &timeout); - if (total == -1) - { // Error - sock->status = SocketGetLastError(); - TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - } - else if (total == 0) - { // Timeout - return false; - } - else - { - if (FD_ISSET(sock->channel, &writefds)) - { - return true; - } - } - return false; -#else - return true; -#endif -} - -// Allocate and return a SocketResult struct -SocketResult *AllocSocketResult() -{ - struct SocketResult *res; - res = (struct SocketResult *) malloc(sizeof(*res)); - if (res != NULL) - { - memset(res, 0, sizeof(*res)); - if ((res->socket = AllocSocket()) == NULL) - { - free(res); - res = NULL; - } - } - return res; -} - -// Free an allocated SocketResult -void FreeSocketResult(SocketResult **result) -{ - if (*result != NULL) - { - if ((*result)->socket != NULL) - { - FreeSocket(&((*result)->socket)); - } - free(*result); - *result = NULL; - } -} - -// Allocate a Socket -Socket *AllocSocket() -{ - // Allocate a socket if one already hasn't been - struct Socket *sock; - sock = (Socket *) malloc(sizeof(*sock)); - if (sock != NULL) - { - memset(sock, 0, sizeof(*sock)); - } - else - { - TraceLog( - LOG_WARNING, "Ran out of memory attempting to allocate a socket"); - SocketClose(sock); - free(sock); - sock = NULL; - } - return sock; -} - -// Free an allocated Socket -void FreeSocket(Socket **sock) -{ - if (*sock != NULL) - { - free(*sock); - *sock = NULL; - } -} - -// Allocate a SocketSet -SocketSet *AllocSocketSet(int max) -{ - struct SocketSet *set; - int i; - - set = (struct SocketSet *) malloc(sizeof(*set)); - if (set != NULL) - { - set->numsockets = 0; - set->maxsockets = max; - set->sockets = (struct Socket **) malloc(max * sizeof(*set->sockets)); - if (set->sockets != NULL) - { - for (i = 0; i < max; ++i) - { - set->sockets[i] = NULL; - } - } - else - { - free(set); - set = NULL; - } - } - return (set); -} - -// Free an allocated SocketSet -void FreeSocketSet(SocketSet *set) -{ - if (set) - { - free(set->sockets); - free(set); - } -} - -// Add a Socket "sock" to the SocketSet "set" -int AddSocket(SocketSet *set, Socket *sock) -{ - if (sock != NULL) - { - if (set->numsockets == set->maxsockets) - { - TraceLog(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); - SocketSetLastError(0); - return (-1); - } - set->sockets[set->numsockets++] = (struct Socket *) sock; - } - else - { - TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket was null"); - SocketSetLastError(0); - return (-1); - } - return (set->numsockets); -} - -// Remove a Socket "sock" to the SocketSet "set" -int RemoveSocket(SocketSet *set, Socket *sock) -{ - int i; - - if (sock != NULL) - { - for (i = 0; i < set->numsockets; ++i) - { - if (set->sockets[i] == (struct Socket *) sock) - { - break; - } - } - if (i == set->numsockets) - { - TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket not found"); - SocketSetLastError(0); - return (-1); - } - --set->numsockets; - for (; i < set->numsockets; ++i) - { - set->sockets[i] = set->sockets[i + 1]; - } - } - return (set->numsockets); -} - -// Check the sockets in the socket set for pending information -int CheckSockets(SocketSet *set, unsigned int timeout) -{ - int i; - SOCKET maxfd; - int retval; - struct timeval tv; - fd_set mask; - - /* Find the largest file descriptor */ - maxfd = 0; - for (i = set->numsockets - 1; i >= 0; --i) - { - if (set->sockets[i]->channel > maxfd) - { - maxfd = set->sockets[i]->channel; - } - } - - // Check the file descriptors for available data - do - { - SocketSetLastError(0); - - // Set up the mask of file descriptors - FD_ZERO(&mask); - for (i = set->numsockets - 1; i >= 0; --i) - { - FD_SET(set->sockets[i]->channel, &mask); - } // Set up the timeout - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - /* Look! */ - retval = select(maxfd + 1, &mask, NULL, NULL, &tv); - } while (SocketGetLastError() == WSAEINTR); - - // Mark all file descriptors ready that have data available - if (retval > 0) - { - for (i = set->numsockets - 1; i >= 0; --i) - { - if (FD_ISSET(set->sockets[i]->channel, &mask)) - { - set->sockets[i]->ready = 1; - } - } - } - return (retval); -} - -// Allocate an AddressInformation -AddressInformation AllocAddress() -{ - AddressInformation addressInfo = NULL; - addressInfo = (AddressInformation) calloc(1, sizeof(*addressInfo)); - if (addressInfo != NULL) - { - addressInfo->addr.ai_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr)); - if (addressInfo->addr.ai_addr == NULL) - { - TraceLog(LOG_WARNING, - "Failed to allocate memory for \"struct sockaddr\""); - } - } - else - { - TraceLog(LOG_WARNING, - "Failed to allocate memory for \"struct AddressInformation\""); - } - return addressInfo; -} - -// Free an AddressInformation struct -void FreeAddress(AddressInformation *addressInfo) -{ - if (*addressInfo != NULL) - { - if ((*addressInfo)->addr.ai_addr != NULL) - { - free((*addressInfo)->addr.ai_addr); - (*addressInfo)->addr.ai_addr = NULL; - } - free(*addressInfo); - *addressInfo = NULL; - } -} - -// Allocate a list of AddressInformation -AddressInformation *AllocAddressList(int size) -{ - AddressInformation *addr; - addr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); - return addr; -} - -// Opaque datatype accessor addrinfo->ai_family -int GetAddressFamily(AddressInformation address) -{ - return address->addr.ai_family; -} - -// Opaque datatype accessor addrinfo->ai_socktype -int GetAddressSocketType(AddressInformation address) -{ - return address->addr.ai_socktype; -} - -// Opaque datatype accessor addrinfo->ai_protocol -int GetAddressProtocol(AddressInformation address) -{ - return address->addr.ai_protocol; -} - -// Opaque datatype accessor addrinfo->ai_canonname -char *GetAddressCanonName(AddressInformation address) -{ - return address->addr.ai_canonname; -} - -// Opaque datatype accessor addrinfo->ai_addr -char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport) -{ - //char *ip[INET6_ADDRSTRLEN]; - char *result = NULL; - struct sockaddr_storage *storage = (struct sockaddr_storage *) address->addr.ai_addr; - switch (storage->ss_family) - { - case AF_INET: - { - struct sockaddr_in *s = ((struct sockaddr_in *) address->addr.ai_addr); - //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. - *outport = ntohs(s->sin_port); - } - break; - case AF_INET6: - { - struct sockaddr_in6 *s = ((struct sockaddr_in6 *) address->addr.ai_addr); - //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. - *outport = ntohs(s->sin6_port); - } - break; - } - if (result == NULL) - { - TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); - SocketSetLastError(0); - } - else - { - strcpy(outhost, result); - } - return result; -} - -// -void PacketSend(Packet *packet) -{ - printf("Sending packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketReceive(Packet *packet) -{ - printf("Receiving packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketWrite16(Packet *packet, uint16_t value) -{ - printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint16_t); - packet->offs += sizeof(uint16_t); - printf("Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); -} - -// -void PacketWrite32(Packet *packet, uint32_t value) -{ - printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 24); - *data++ = (uint8_t)(value >> 16); - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint32_t); - packet->offs += sizeof(uint32_t); - printf("Network: 0x%08" PRIX32 " - %" PRIu32 "\n", - (uint32_t)(((intptr_t) packet->data) - packet->offs), - (uint32_t)(((intptr_t) packet->data) - packet->offs)); -} - -// -void PacketWrite64(Packet *packet, uint64_t value) -{ - printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 56); - *data++ = (uint8_t)(value >> 48); - *data++ = (uint8_t)(value >> 40); - *data++ = (uint8_t)(value >> 32); - *data++ = (uint8_t)(value >> 24); - *data++ = (uint8_t)(value >> 16); - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint64_t); - packet->offs += sizeof(uint64_t); - printf("Network: 0x%016" PRIX64 " - %" PRIu64 "\n", - (uint64_t)(packet->data - packet->offs), - (uint64_t)(packet->data - packet->offs)); -} - -// -uint16_t PacketRead16(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint16_t); - packet->offs += sizeof(uint16_t); - uint16_t value = ((uint16_t) data[0] << 8) | data[1]; - printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); - return value; -} - -// -uint32_t PacketRead32(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint32_t); - packet->offs += sizeof(uint32_t); - uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; - printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); - return value; -} - -// -uint64_t PacketRead64(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint64_t); - packet->offs += sizeof(uint64_t); - uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; - printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); - return value; -} +/********************************************************************************************* +* +* rnet - A simple and easy-to-use network module for raylib +* +* FEATURES: +* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API +* +* DEPENDENCIES: +* raylib.h - TraceLog +* rnet.h - platform-specific network includes +* +* CONTRIBUTORS: +* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2019 Jak Barnes (github: @syphonx) and Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +//---------------------------------------------------------------------------------- +// Check if config flags have been externally provided on compilation line +//---------------------------------------------------------------------------------- + +#include "rnet.h" + +#include "raylib.h" + +#include // Required for: assert() +#include // Required for: FILE, fopen(), fclose(), fread() +#include // Required for: malloc(), free() +#include // Required for: strcmp(), strncmp() + +//---------------------------------------------------------------------------------- +// Module defines +//---------------------------------------------------------------------------------- + +#define NET_DEBUG_ENABLED (1) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +typedef struct _SocketAddress +{ + struct sockaddr address; +} _SocketAddress; + +typedef struct _SocketAddressIPv4 +{ + struct sockaddr_in address; +} _SocketAddressIPv4; + +typedef struct _SocketAddressIPv6 +{ + struct sockaddr_in6 address; +} _SocketAddressIPv6; + +typedef struct _SocketAddressStorage +{ + struct sockaddr_storage address; +} _SocketAddressStorage; + +typedef struct _AddressInformation +{ + struct addrinfo addr; +} _AddressInformation; + + + +//---------------------------------------------------------------------------------- +// Global module forward declarations +//---------------------------------------------------------------------------------- + +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol); +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr); +static bool IsIPv4Address(const char *ip); +static bool IsIPv6Address(const char *ip); +static void *GetSocketPortPtr(struct sockaddr_storage *sa); +static void *GetSocketAddressPtr(struct sockaddr_storage *sa); +static bool IsSocketValid(Socket *sock); +static void SocketSetLastError(int err); +static int SocketGetLastError(); +static char *SocketGetLastErrorString(); +static char *SocketErrorCodeToString(int err); +static bool SocketSetDefaults(SocketConfig *config); +static bool InitSocket(Socket *sock, struct addrinfo *addr); +static bool CreateSocket(SocketConfig *config, SocketResult *outresult); +static bool SocketSetBlocking(Socket *sock); +static bool SocketSetNonBlocking(Socket *sock); +static bool SocketSetOptions(SocketConfig *config, Socket *sock); +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints); + +//---------------------------------------------------------------------------------- +// Global module implementation +//---------------------------------------------------------------------------------- + +// Print socket information +static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol) +{ + switch (family) + { + case AF_UNSPEC: { TraceLog(LOG_DEBUG, "\tFamily: Unspecified"); + } + break; + case AF_INET: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET (IPv4)"); + TraceLog(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr)); + } + break; + case AF_INET6: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)"); + TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr)); + } + break; + case AF_NETBIOS: + { + TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); + } + break; + default: { TraceLog(LOG_DEBUG, "\tFamily: Other %ld", family); + } + break; + } + TraceLog(LOG_DEBUG, "\tSocket type:"); + switch (socktype) + { + case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; + case SOCK_STREAM: + TraceLog(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); + break; + case SOCK_DGRAM: + TraceLog(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); + break; + case SOCK_RAW: TraceLog(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break; + case SOCK_RDM: + TraceLog(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); + break; + case SOCK_SEQPACKET: + TraceLog(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); + break; + default: TraceLog(LOG_DEBUG, "\t- Other %ld", socktype); break; + } + TraceLog(LOG_DEBUG, "\tProtocol:"); + switch (protocol) + { + case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break; + case IPPROTO_TCP: TraceLog(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break; + case IPPROTO_UDP: TraceLog(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break; + default: TraceLog(LOG_DEBUG, "\t- Other %ld", protocol); break; + } +} + +// Convert network ordered socket address to human readable string (127.0.0.1) +static const char *SocketAddressToString(struct sockaddr_storage *sockaddr) +{ + //static const char* ipv6[INET6_ADDRSTRLEN]; + assert(sockaddr != NULL); + assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6); + switch (sockaddr->ss_family) + { + case AF_INET: + { + //struct sockaddr_in *s = ((struct sockaddr_in *) sockaddr); + //return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); // TODO. + } + break; + case AF_INET6: + { + //struct sockaddr_in6 *s = ((struct sockaddr_in6 *) sockaddr); + //return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); // TODO. + } + break; + } + return NULL; +} + +// Check if the null terminated string ip is a valid IPv4 address +static bool IsIPv4Address(const char *ip) +{ + /* + struct sockaddr_in sa; + int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); // TODO. + return (result != 0); + */ + return false; +} + +// Check if the null terminated string ip is a valid IPv6 address +static bool IsIPv6Address(const char *ip) +{ + /* + struct sockaddr_in6 sa; + int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); // TODO. + return result != 0; + */ + return false; +} + +// Return a pointer to the port from the correct address family (IPv4, or IPv6) +static void *GetSocketPortPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *) sa)->sin_port); + } + + return &(((struct sockaddr_in6 *) sa)->sin6_port); +} + +// Return a pointer to the address from the correct address family (IPv4, or IPv6) +static void *GetSocketAddressPtr(struct sockaddr_storage *sa) +{ + if (sa->ss_family == AF_INET) + { + return &(((struct sockaddr_in *) sa)->sin_addr); + } + + return &(((struct sockaddr_in6 *) sa)->sin6_addr); +} + +// Is the socket in a valid state? +static bool IsSocketValid(Socket *sock) +{ + if (sock != NULL) + { + return (sock->channel != INVALID_SOCKET); + } + return false; +} + +// Sets the error code that can be retrieved through the WSAGetLastError function. +static void SocketSetLastError(int err) +{ +#if defined(_WIN32) + WSASetLastError(err); +#else + errno = err; +#endif +} + +// Returns the error status for the last Sockets operation that failed +static int SocketGetLastError() +{ +#if defined(_WIN32) + return WSAGetLastError(); +#else + return errno; +#endif +} + +// Returns a human-readable string representing the last error message +static char *SocketGetLastErrorString() +{ + return SocketErrorCodeToString(SocketGetLastError()); +} + +// Returns a human-readable string representing the error message (err) +static char *SocketErrorCodeToString(int err) +{ +#if defined(_WIN32) + static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE]; + sprintf(gaiStrErrorBuffer, "%s", gai_strerror(err)); + return gaiStrErrorBuffer; +#else + return gai_strerror(err); +#endif +} + +// Set the defaults in the supplied SocketConfig if they're not already set +static bool SocketSetDefaults(SocketConfig *config) +{ + if (config->backlog_size == 0) + { + config->backlog_size = SOCKET_MAX_QUEUE_SIZE; + } + + return true; +} + +// Create the socket channel +static bool InitSocket(Socket *sock, struct addrinfo *addr) +{ + switch (sock->type) + { + case SOCKET_TCP: + if (addr->ai_family == AF_INET) + { + sock->channel = socket(AF_INET, SOCK_STREAM, 0); + } + else + { + sock->channel = socket(AF_INET6, SOCK_STREAM, 0); + } + break; + case SOCKET_UDP: + if (addr->ai_family == AF_INET) + { + sock->channel = socket(AF_INET, SOCK_DGRAM, 0); + } + else + { + sock->channel = socket(AF_INET6, SOCK_DGRAM, 0); + } + break; + default: + TraceLog(LOG_WARNING, "Invalid socket type specified."); + break; + } + return IsSocketValid(sock); +} + +// CreateSocket() - Interally called by CreateSocket() +// +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +static bool CreateSocket(SocketConfig *config, SocketResult *outresult) +{ + bool success = true; + int addrstatus; + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + outresult->socket->channel = INVALID_SOCKET; + outresult->status = RESULT_FAILURE; + + // Set the socket type + outresult->socket->type = config->type; + + // Set the hints based on information in the config + // + // AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name. + // AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6); + // Note: This causes a subsequent call to bind() to auto-fill the IP address + // of the struct sockaddr with the address of the current host. + // + SocketSetHints(config, &hints); + + // Populate address information + addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set + config->port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (addrstatus != 0) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + TraceLog(LOG_WARNING, + "Failed to get resolve host %s:%s: %s", + config->host, + config->port, + SocketGetLastErrorString()); + return (success = false); + } + else + { + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + //socklen_t client_len = sizeof(struct sockaddr_storage); + //int rc = getnameinfo((struct sockaddr *) res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr); + } + + // Walk the address information linked-list + struct addrinfo *it; + for (it = res; it != NULL; it = it->ai_next) + { + // Initialise the socket + if (!InitSocket(outresult->socket, it)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + continue; + } + + // Set socket options + if (!SocketSetOptions(config, outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, + "Socket Error: %s", + SocketErrorCodeToString(outresult->socket->status)); + SocketSetLastError(0); + freeaddrinfo(res); + return (success = false); + } + } + + if (!IsSocketValid(outresult->socket)) + { + outresult->socket->status = SocketGetLastError(); + TraceLog( + LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status)); + SocketSetLastError(0); + freeaddrinfo(res); + return (success = false); + } + + if (success) + { + outresult->status = RESULT_SUCCESS; + outresult->socket->ready = 0; + outresult->socket->status = 0; + if (!(config->type == SOCKET_UDP)) + { + outresult->socket->isServer = config->server; + } + switch (res->ai_addr->sa_family) + { + case AF_INET: + { + outresult->socket->addripv4 = (struct _SocketAddressIPv4 *) malloc( + sizeof(*outresult->socket->addripv4)); + if (outresult->socket->addripv4 != NULL) + { + memset(outresult->socket->addripv4, 0, + sizeof(*outresult->socket->addripv4)); + if (outresult->socket->addripv4 != NULL) + { + memcpy(&outresult->socket->addripv4->address, + (struct sockaddr_in *) res->ai_addr, sizeof(struct sockaddr_in)); + outresult->socket->isIPv6 = false; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo( + (struct sockaddr *) &outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } + break; + case AF_INET6: + { + outresult->socket->addripv6 = (struct _SocketAddressIPv6 *) malloc( + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memset(outresult->socket->addripv6, 0, + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) + { + memcpy(&outresult->socket->addripv6->address, + (struct sockaddr_in6 *) res->ai_addr, sizeof(struct sockaddr_in6)); + outresult->socket->isIPv6 = true; + char hoststr[NI_MAXHOST]; + char portstr[NI_MAXSERV]; + socklen_t client_len = sizeof(struct sockaddr_storage); + getnameinfo( + (struct sockaddr *) &outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); + TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr); + } + } + } + break; + } + } + freeaddrinfo(res); + return success; +} + +// Set the state of the Socket sock to blocking +static bool SocketSetBlocking(Socket *sock) +{ + bool ret = true; +#if defined(_WIN32) + unsigned long mode = 0; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + if (!(flags & O_NONBLOCK)) + { + TraceLog(LOG_DEBUG, "Socket was already in blocking mode"); + return ret; + } + + ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK))); +#endif + return ret; +} + +// Set the state of the Socket sock to non-blocking +static bool SocketSetNonBlocking(Socket *sock) +{ + bool ret = true; +#if defined(_WIN32) + unsigned long mode = 1; + ret = ioctlsocket(sock->channel, FIONBIO, &mode); +#else + const int flags = fcntl(sock->channel, F_GETFL, 0); + if ((flags & O_NONBLOCK)) + { + TraceLog(LOG_DEBUG, "Socket was already in non-blocking mode"); + return ret; + } + ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK))); +#endif + return ret; +} + +// Set options specified in SocketConfig to Socket sock +static bool SocketSetOptions(SocketConfig *config, Socket *sock) +{ + for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++) + { + SocketOpt *opt = &config->sockopts[i]; + if (opt->id == 0) + { + break; + } + + if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) + { + return false; + } + } + + return true; +} + +// Set "hints" in an addrinfo struct, to be passed to getaddrinfo. +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) +{ + if (config == NULL || hints == NULL) + { + return; + } + memset(hints, 0, sizeof(*hints)); + + // Check if the ip supplied in the config is a valid ipv4 ip ipv6 address + if (IsIPv4Address(config->host)) + { + hints->ai_family = AF_INET; + hints->ai_flags |= AI_NUMERICHOST; + } + else + { + if (IsIPv6Address(config->host)) + { + hints->ai_family = AF_INET6; + hints->ai_flags |= AI_NUMERICHOST; + } + else + { + hints->ai_family = AF_UNSPEC; + } + } + + if (config->type == SOCKET_UDP) + { + hints->ai_socktype = SOCK_DGRAM; + } + else + { + hints->ai_socktype = SOCK_STREAM; + } + + // Set passive unless UDP client + if (!(config->type == SOCKET_UDP) || config->server) + { + hints->ai_flags = AI_PASSIVE; + } +} + +//---------------------------------------------------------------------------------- +// Module implementation +//---------------------------------------------------------------------------------- + +// Initialise the network (requires for windows platforms only) +bool InitNetwork() +{ +#if defined(_WIN32) + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD(2, 2); + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) + { + TraceLog(LOG_WARNING, "WinSock failed to initialise."); + return false; + } + else + { + TraceLog(LOG_INFO, "WinSock initialised."); + } + + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) + { + TraceLog(LOG_WARNING, "WinSock failed to initialise."); + WSACleanup(); + return false; + } + + return true; +#else + return true; +#endif +} + +// Cleanup, and close the network +void CloseNetwork() +{ +#if defined(_WIN32) + WSACleanup(); +#endif +} + +// Protocol-independent name resolution from an address to an ANSI host name +// and from a port number to the ANSI service name. +// +// The flags parameter can be used to customize processing of the getnameinfo function +// +// The following flags are available: +// +// NAME_INFO_DEFAULT 0x00 // No flags set +// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts +// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address +// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS +// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #) +// NAME_INFO_DGRAM 0x10 // Service is a datagram service +void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // A pointer to the resulting address list + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + + // Populate address information + status = getaddrinfo(ip, // e.g. "www.example.com" or IP + port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) + { + TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno)); + } + else + { + TraceLog(LOG_DEBUG, "Resolving... %s::%s", ip, port); + } + + // Attempt to resolve network byte order ip to hostname + switch (res->ai_family) + { + case AF_INET: + status = getnameinfo(&*((struct sockaddr *) res->ai_addr), + sizeof(*((struct sockaddr_in *) res->ai_addr)), + host, + NI_MAXHOST, + serv, + NI_MAXSERV, + flags); + break; + case AF_INET6: + /* + status = getnameinfo(&*((struct sockaddr_in6 *) res->ai_addr), // TODO. + sizeof(*((struct sockaddr_in6 *) res->ai_addr)), + host, NI_MAXHOST, serv, NI_MAXSERV, flags); + */ + break; + default: break; + } + + if (status != 0) + { + TraceLog(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString()); + } + else + { + TraceLog(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host); + } + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); +} + +// Protocol-independent translation from an ANSI host name to an address +// +// e.g. +// const char* address = "127.0.0.1" (local address) +// const char* port = "80" +// +// Parameters: +// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string. +// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string. +// +// Returns: +// The total amount of addresses found, -1 on error +// +int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr) +{ + // Variables + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + struct addrinfo *res; // will point to the results + struct addrinfo *iterator; + assert(((address != NULL || address != 0) || (service != NULL || service != 0))); + assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC))); + + // Set the hints + memset(&hints, 0, sizeof hints); + hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6) + hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) + hints.ai_flags = flags; + assert((hints.ai_addrlen == 0) || (hints.ai_addrlen == 0)); + assert((hints.ai_canonname == 0) || (hints.ai_canonname == 0)); + assert((hints.ai_addr == 0) || (hints.ai_addr == 0)); + assert((hints.ai_next == 0) || (hints.ai_next == 0)); + + // When the address is NULL, populate the IP for me + if (address == NULL) + { + if ((hints.ai_flags & AI_PASSIVE) == 0) + { + hints.ai_flags |= AI_PASSIVE; + } + } + + TraceLog(LOG_INFO, "Resolving host..."); + + // Populate address information + status = getaddrinfo(address, // e.g. "www.example.com" or IP + service, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + &res // The struct to populate + ); + + // Did we succeed? + if (status != 0) + { + int error = SocketGetLastError(); + SocketSetLastError(0); + TraceLog(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error)); + return -1; + } + else + { + TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, service); + } + + // Calculate the size of the address information list + int size = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) + { + size++; + } + + // Validate the size is > 0, otherwise return + if (size <= 0) + { + TraceLog(LOG_WARNING, "Error, no addresses found."); + return -1; + } + + // If not address list was allocated, allocate it dynamically with the known address size + if (outAddr == NULL) + { + outAddr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); + } + + // Dynamically allocate an array of address information structs + if (outAddr != NULL) + { + int i; + for (i = 0; i < size; ++i) + { + outAddr[i] = AllocAddress(); + if (outAddr[i] == NULL) + { + break; + } + } + outAddr[i] = NULL; + if (i != size) + { + outAddr = NULL; + } + } + else + { + TraceLog(LOG_WARNING, + "Error, failed to dynamically allocate memory for the address list"); + return -1; + } + + // Copy all the address information from res into outAddrList + int i = 0; + for (iterator = res; iterator != NULL; iterator = iterator->ai_next) + { + if (i < size) + { + outAddr[i]->addr.ai_flags = iterator->ai_flags; + outAddr[i]->addr.ai_family = iterator->ai_family; + outAddr[i]->addr.ai_socktype = iterator->ai_socktype; + outAddr[i]->addr.ai_protocol = iterator->ai_protocol; + outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen; + *outAddr[i]->addr.ai_addr = *iterator->ai_addr; +#if NET_DEBUG_ENABLED + TraceLog(LOG_DEBUG, "GetAddressInformation"); + TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); + //PrintSocket(outAddr[i]->addr.ai_addr, outAddr[i]->addr.ai_family, outAddr[i]->addr.ai_socktype, outAddr[i]->addr.ai_protocol); + TraceLog(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen); + TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); +#endif + i++; + } + } + + // Free the pointer to the data returned by addrinfo + freeaddrinfo(res); + + // Return the total count of addresses found + return size; +} + +// This here is the bread and butter of the socket API, This function will +// attempt to open a socket, bind and listen to it based on the config passed in +// +// SocketConfig* config - Configuration for which socket to open +// SocketResult* result - The results of this function (if any, including errors) +// +// e.g. +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, +// .server = true, }; +// .nonblocking = true, +// }; +// SocketResult server_res; SocketResult client_res; +bool SocketCreate(SocketConfig *config, SocketResult *result) +{ + // Socket creation result + bool success = true; + + // Make sure we've not received a null config or result pointer + if (config == NULL || result == NULL) + { + return (success = false); + } + + // Set the defaults based on the config + if (!SocketSetDefaults(config)) + { + TraceLog(LOG_WARNING, "Configuration Error."); + success = false; + } + else + { + // Create the socket + if (CreateSocket(config, result)) + { + if (config->nonblocking) + { + SocketSetNonBlocking(result->socket); + } + else + { + SocketSetBlocking(result->socket); + } + } + else + { + success = false; + } + } + return success; +} + +// Bind a socket to a local address +// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function. +bool SocketBind(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + struct sockaddr_storage *sock_addr = NULL; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TraceLog(LOG_WARNING, + "Cannot bind to socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + if (result->socket->isIPv6) + { + sock_addr = (struct sockaddr_storage *) &result->socket->addripv6->address; + } + else + { + sock_addr = (struct sockaddr_storage *) &result->socket->addripv4->address; + } + if (sock_addr != NULL) + { + if (bind(result->socket->channel, (struct sockaddr *) sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR) + { + TraceLog(LOG_INFO, "Successfully bound socket."); + success = true; + } + else + { + result->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + success = false; + } + } + } + // Was the bind a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + socklen_t sock_len = sizeof(*sock_addr); + if (getsockname(result->socket->channel, (struct sockaddr *) sock_addr, &sock_len) < 0) + { + TraceLog(LOG_WARNING, "Couldn't get socket address"); + } + else + { + struct sockaddr_in *s = (struct sockaddr_in *) sock_addr; + // result->socket->address.host = s->sin_addr.s_addr; + // result->socket->address.port = s->sin_port; + + // + result->socket->addripv4 + = (struct _SocketAddressIPv4 *) malloc(sizeof(*result->socket->addripv4)); + if (result->socket->addripv4 != NULL) + { + memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4)); + } + memcpy(&result->socket->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); + // + } + } + return success; +} + +// Listens (and queues) incoming connections requests for a bound port. +bool SocketListen(SocketConfig *config, SocketResult *result) +{ + bool success = false; + result->status = RESULT_FAILURE; + + // Don't bind to a socket that isn't configured as a server + if (!IsSocketValid(result->socket) || !config->server) + { + TraceLog(LOG_WARNING, + "Cannot listen on socket marked as \"Client\" in SocketConfig."); + success = false; + } + else + { + // Don't listen on UDP sockets + if (!(config->type == SOCKET_UDP)) + { + if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR) + { + TraceLog(LOG_INFO, "Started listening on socket..."); + success = true; + } + else + { + success = false; + result->socket->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + SocketSetLastError(0); + } + } + else + { + TraceLog(LOG_WARNING, + "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); + success = false; + } + } + + // Was the listen a success? + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + return success; +} + +// Connect the socket to the destination specified by "host" and "port" in SocketConfig +bool SocketConnect(SocketConfig *config, SocketResult *result) +{ + bool success = true; + result->status = RESULT_FAILURE; + + // Only bind to sockets marked as server + if (config->server) + { + TraceLog(LOG_WARNING, + "Cannot connect to socket marked as \"Server\" in SocketConfig."); + success = false; + } + else + { + if (IsIPv4Address(config->host)) + { + struct sockaddr_in ip4addr; + ip4addr.sin_family = AF_INET; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip4addr.sin_port = htons(hport); + + // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows). + + //inet_pton(AF_INET, config->host, &ip4addr.sin_addr); + int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip4addr, sizeof(ip4addr)); + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + switch (result->socket->status) + { + case WSAEWOULDBLOCK: + { + success = true; + break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + success = false; + break; + } + } + } + else + { + TraceLog(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + else + { + if (IsIPv6Address(config->host)) + { + struct sockaddr_in6 ip6addr; + ip6addr.sin6_family = AF_INET6; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip6addr.sin6_port = htons(hport); + //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO. + int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip6addr, sizeof(ip6addr)); + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + switch (result->socket->status) + { + case WSAEWOULDBLOCK: + { + success = true; + break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", + SocketErrorCodeToString(result->socket->status)); + success = false; + break; + } + } + } + else + { + TraceLog(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + } + } + + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + + return success; +} + +// Closes an existing socket +// +// SocketChannel socket - The id of the socket to close +void SocketClose(Socket *sock) +{ + if (sock != NULL) + { + if (sock->channel != INVALID_SOCKET) + { + closesocket(sock->channel); + } + } +} + +// Returns the sockaddress for a specific socket in a generic storage struct +SocketAddressStorage SocketGetPeerAddress(Socket *sock) +{ + // TODO. + /* + if (sock->isServer) return NULL; + if (sock->isIPv6) return sock->addripv6; + else return sock->addripv4; + */ + + return NULL; +} + +// Return the address-type appropriate host portion of a socket address +char *GetSocketAddressHost(SocketAddressStorage storage) +{ + assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); + return SocketAddressToString((struct sockaddr_storage *) storage); +} + +// Return the address-type appropriate port(service) portion of a socket address +short GetSocketAddressPort(SocketAddressStorage storage) +{ + //return ntohs(GetSocketPortPtr(storage)); // TODO. + + return 0; +} + +// The accept function permits an incoming connection attempt on a socket. +// +// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) +// SocketResult* out - The result of this function (if any, including errors) +// +// e.g. +// +// SocketResult connection; +// bool connected = false; +// if (!connected) +// { +// if (SocketAccept(server_res.socket.channel, &connection)) +// { +// connected = true; +// } +// } +Socket *SocketAccept(Socket *server, SocketConfig *config) +{ + if (!server->isServer || server->type == SOCKET_UDP) + { + return NULL; + } + struct sockaddr_storage sock_addr; + socklen_t sock_alen; + Socket * sock; + sock = AllocSocket(); + server->ready = 0; + sock_alen = sizeof(sock_addr); + sock->channel = accept(server->channel, (struct sockaddr *) &sock_addr, &sock_alen); + if (sock->channel == INVALID_SOCKET) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + SocketClose(sock); + return NULL; + } + (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); + sock->isServer = false; + sock->ready = 0; + sock->type = server->type; + switch (sock_addr.ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) &sock_addr); + sock->addripv4 = (struct _SocketAddressIPv4 *) malloc(sizeof(*sock->addripv4)); + if (sock->addripv4 != NULL) + { + memset(sock->addripv4, 0, sizeof(*sock->addripv4)); + memcpy(&sock->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); + TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), + ntohs(sock->addripv4->address.sin_port)); + } + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) &sock_addr); + sock->addripv6 = (struct _SocketAddressIPv6 *) malloc(sizeof(*sock->addripv6)); + if (sock->addripv6 != NULL) + { + memset(sock->addripv6, 0, sizeof(*sock->addripv6)); + memcpy(&sock->addripv6->address, (struct sockaddr_in6 *) &s->sin6_addr, sizeof(struct sockaddr_in6)); + TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s), + ntohs(sock->addripv6->address.sin6_port)); + } + } + break; + } + return sock; +} + +// Verify that the channel is in the valid range +static int ValidChannel(int channel) +{ + if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) + { + TraceLog(LOG_WARNING, "Invalid channel"); + return 0; + } + return 1; +} + +// Set the socket channel +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) +{ + struct UDPChannel *binding; + if (socket == NULL) + { + TraceLog(LOG_WARNING, "Passed a NULL socket"); + return (-1); + } + if (channel == -1) + { + for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) + { + binding = &socket->binding[channel]; + if (binding->numbound < SOCKET_MAX_UDPADDRESSES) + { + break; + } + } + } + else + { + if (!ValidChannel(channel)) + { + return (-1); + } + binding = &socket->binding[channel]; + } + if (binding->numbound == SOCKET_MAX_UDPADDRESSES) + { + TraceLog(LOG_WARNING, "No room for new addresses"); + return (-1); + } + binding->address[binding->numbound++] = *address; + return (channel); +} + +// Remove the socket channel +void SocketUnsetChannel(Socket *socket, int channel) +{ + if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) + { + socket->binding[channel].numbound = 0; + } +} + +/* Allocate/free a single UDP packet 'size' bytes long. + The new packet is returned, or NULL if the function ran out of memory. + */ +SocketDataPacket *AllocPacket(int size) +{ + SocketDataPacket *packet; + int error; + + error = 1; + packet = (SocketDataPacket *) malloc(sizeof(*packet)); + if (packet != NULL) + { + packet->maxlen = size; + packet->data = (uint8_t *) malloc(size); + if (packet->data != NULL) + { + error = 0; + } + } + if (error) + { + FreePacket(packet); + packet = NULL; + } + return (packet); +} + +int ResizePacket(SocketDataPacket *packet, int newsize) +{ + uint8_t *newdata; + + newdata = (uint8_t *) malloc(newsize); + if (newdata != NULL) + { + free(packet->data); + packet->data = newdata; + packet->maxlen = newsize; + } + return (packet->maxlen); +} + +void FreePacket(SocketDataPacket *packet) +{ + if (packet) + { + free(packet->data); + free(packet); + } +} + +/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, + each 'size' bytes long. + A pointer to the packet array is returned, or NULL if the function ran out + of memory. + */ +SocketDataPacket **AllocPacketList(int howmany, int size) +{ + SocketDataPacket **packetV; + + packetV = (SocketDataPacket **) malloc((howmany + 1) * sizeof(*packetV)); + if (packetV != NULL) + { + int i; + for (i = 0; i < howmany; ++i) + { + packetV[i] = AllocPacket(size); + if (packetV[i] == NULL) + { + break; + } + } + packetV[i] = NULL; + + if (i != howmany) + { + FreePacketList(packetV); + packetV = NULL; + } + } + return (packetV); +} + +void FreePacketList(SocketDataPacket **packetV) +{ + if (packetV) + { + int i; + for (i = 0; packetV[i]; ++i) + { + FreePacket(packetV[i]); + } + free(packetV); + } +} + +// Send 'len' bytes of 'data' over the non-server socket 'sock' +// +// Example +int SocketSend(Socket *sock, const void *datap, int length) +{ + int sent = 0; + int left = length; + int status = -1; + int numsent = 0; + const unsigned char *data = (const unsigned char *) datap; + + // Server sockets are for accepting connections only + if (sock->isServer) + { + TraceLog(LOG_WARNING, "Cannot send information on a server socket"); + return -1; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + length = send(sock->channel, (const char *) data, left, 0); + if (length > 0) + { + sent += length; + left -= length; + data += length; + } + } while ((left > 0) && // While we still have bytes left to send + ((length > 0) || // The amount of bytes we actually sent is > 0 + (SocketGetLastError() == WSAEINTR)) // The socket was interupted + ); + + if (length == SOCKET_ERROR) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else + { + TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); + } + + return sent; + } + break; + case SOCKET_UDP: + { + SocketSetLastError(0); + if (sock->isIPv6) + { + status = sendto(sock->channel, (const char *) data, left, 0, + (struct sockaddr *) &sock->addripv6->address, + sizeof(sock->addripv6->address)); + } + else + { + status = sendto(sock->channel, (const char *) data, left, 0, + (struct sockaddr *) &sock->addripv4->address, + sizeof(sock->addripv4->address)); + } + if (sent >= 0) + { + sock->status = 0; + ++numsent; + TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); + } + else + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); + SocketSetLastError(0); + return 0; + } + return numsent; + } + break; + default: break; + } + return -1; +} + +// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', +// and store them in the buffer pointed to by 'data'. +// This function returns the actual amount of data received. If the return +// value is less than or equal to zero, then either the remote connection was +// closed, or an unknown socket error occurred. +int SocketReceive(Socket *sock, void *data, int maxlen) +{ + int len = 0; + int numrecv = 0; + int status = 0; + socklen_t sock_len; + struct sockaddr_storage sock_addr; + //char ip[INET6_ADDRSTRLEN]; + + // Server sockets are for accepting connections only + if (sock->isServer && sock->type == SOCKET_TCP) + { + sock->status = SocketGetLastError(); + TraceLog(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data"); + SocketSetLastError(0); + return 0; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + len = recv(sock->channel, (char *) data, maxlen, 0); + } while (SocketGetLastError() == WSAEINTR); + + if (len > 0) + { + // Who sent the packet? + if (sock->type == SOCKET_UDP) + { + //TraceLog(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *) &sock_addr), ip, sizeof(ip))); + } + + ((unsigned char *) data)[len] = '\0'; // Add null terminating character to the end of the stream + TraceLog(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); + } + + sock->ready = 0; + return len; + } + break; + case SOCKET_UDP: + { + SocketSetLastError(0); + sock_len = sizeof(sock_addr); + status = recvfrom(sock->channel, // The receving channel + data, // A pointer to the data buffer to fill + maxlen, // The max length of the data to fill + 0, // Flags + (struct sockaddr *) &sock_addr, // The address of the recevied data + &sock_len // The length of the received data address + ); + if (status >= 0) + { + ++numrecv; + } + else + { + sock->status = SocketGetLastError(); + switch (sock->status) + { + case WSAEWOULDBLOCK: { break; + } + default: + { + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + break; + } + } + SocketSetLastError(0); + return 0; + } + sock->ready = 0; + return numrecv; + } + break; + } + return -1; +} + +// Does the socket have it's 'ready' flag set? +bool IsSocketReady(Socket *sock) +{ + return (sock != NULL) && (sock->ready); +} + +// Check if the socket is considered connected +bool IsSocketConnected(Socket *sock) +{ +#if defined(_WIN32) + FD_SET writefds; + FD_ZERO(&writefds); + FD_SET(sock->channel, &writefds); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 1000000000UL; + int total = select(0, NULL, &writefds, NULL, &timeout); + if (total == -1) + { // Error + sock->status = SocketGetLastError(); + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else if (total == 0) + { // Timeout + return false; + } + else + { + if (FD_ISSET(sock->channel, &writefds)) + { + return true; + } + } + return false; +#else + return true; +#endif +} + +// Allocate and return a SocketResult struct +SocketResult *AllocSocketResult() +{ + struct SocketResult *res; + res = (struct SocketResult *) malloc(sizeof(*res)); + if (res != NULL) + { + memset(res, 0, sizeof(*res)); + if ((res->socket = AllocSocket()) == NULL) + { + free(res); + res = NULL; + } + } + return res; +} + +// Free an allocated SocketResult +void FreeSocketResult(SocketResult **result) +{ + if (*result != NULL) + { + if ((*result)->socket != NULL) + { + FreeSocket(&((*result)->socket)); + } + free(*result); + *result = NULL; + } +} + +// Allocate a Socket +Socket *AllocSocket() +{ + // Allocate a socket if one already hasn't been + struct Socket *sock; + sock = (Socket *) malloc(sizeof(*sock)); + if (sock != NULL) + { + memset(sock, 0, sizeof(*sock)); + } + else + { + TraceLog( + LOG_WARNING, "Ran out of memory attempting to allocate a socket"); + SocketClose(sock); + free(sock); + sock = NULL; + } + return sock; +} + +// Free an allocated Socket +void FreeSocket(Socket **sock) +{ + if (*sock != NULL) + { + free(*sock); + *sock = NULL; + } +} + +// Allocate a SocketSet +SocketSet *AllocSocketSet(int max) +{ + struct SocketSet *set; + int i; + + set = (struct SocketSet *) malloc(sizeof(*set)); + if (set != NULL) + { + set->numsockets = 0; + set->maxsockets = max; + set->sockets = (struct Socket **) malloc(max * sizeof(*set->sockets)); + if (set->sockets != NULL) + { + for (i = 0; i < max; ++i) + { + set->sockets[i] = NULL; + } + } + else + { + free(set); + set = NULL; + } + } + return (set); +} + +// Free an allocated SocketSet +void FreeSocketSet(SocketSet *set) +{ + if (set) + { + free(set->sockets); + free(set); + } +} + +// Add a Socket "sock" to the SocketSet "set" +int AddSocket(SocketSet *set, Socket *sock) +{ + if (sock != NULL) + { + if (set->numsockets == set->maxsockets) + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); + SocketSetLastError(0); + return (-1); + } + set->sockets[set->numsockets++] = (struct Socket *) sock; + } + else + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket was null"); + SocketSetLastError(0); + return (-1); + } + return (set->numsockets); +} + +// Remove a Socket "sock" to the SocketSet "set" +int RemoveSocket(SocketSet *set, Socket *sock) +{ + int i; + + if (sock != NULL) + { + for (i = 0; i < set->numsockets; ++i) + { + if (set->sockets[i] == (struct Socket *) sock) + { + break; + } + } + if (i == set->numsockets) + { + TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket not found"); + SocketSetLastError(0); + return (-1); + } + --set->numsockets; + for (; i < set->numsockets; ++i) + { + set->sockets[i] = set->sockets[i + 1]; + } + } + return (set->numsockets); +} + +// Check the sockets in the socket set for pending information +int CheckSockets(SocketSet *set, unsigned int timeout) +{ + int i; + SOCKET maxfd; + int retval; + struct timeval tv; + fd_set mask; + + /* Find the largest file descriptor */ + maxfd = 0; + for (i = set->numsockets - 1; i >= 0; --i) + { + if (set->sockets[i]->channel > maxfd) + { + maxfd = set->sockets[i]->channel; + } + } + + // Check the file descriptors for available data + do + { + SocketSetLastError(0); + + // Set up the mask of file descriptors + FD_ZERO(&mask); + for (i = set->numsockets - 1; i >= 0; --i) + { + FD_SET(set->sockets[i]->channel, &mask); + } // Set up the timeout + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + /* Look! */ + retval = select(maxfd + 1, &mask, NULL, NULL, &tv); + } while (SocketGetLastError() == WSAEINTR); + + // Mark all file descriptors ready that have data available + if (retval > 0) + { + for (i = set->numsockets - 1; i >= 0; --i) + { + if (FD_ISSET(set->sockets[i]->channel, &mask)) + { + set->sockets[i]->ready = 1; + } + } + } + return (retval); +} + +// Allocate an AddressInformation +AddressInformation AllocAddress() +{ + AddressInformation addressInfo = NULL; + addressInfo = (AddressInformation) calloc(1, sizeof(*addressInfo)); + if (addressInfo != NULL) + { + addressInfo->addr.ai_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr)); + if (addressInfo->addr.ai_addr == NULL) + { + TraceLog(LOG_WARNING, + "Failed to allocate memory for \"struct sockaddr\""); + } + } + else + { + TraceLog(LOG_WARNING, + "Failed to allocate memory for \"struct AddressInformation\""); + } + return addressInfo; +} + +// Free an AddressInformation struct +void FreeAddress(AddressInformation *addressInfo) +{ + if (*addressInfo != NULL) + { + if ((*addressInfo)->addr.ai_addr != NULL) + { + free((*addressInfo)->addr.ai_addr); + (*addressInfo)->addr.ai_addr = NULL; + } + free(*addressInfo); + *addressInfo = NULL; + } +} + +// Allocate a list of AddressInformation +AddressInformation *AllocAddressList(int size) +{ + AddressInformation *addr; + addr = (AddressInformation *) malloc(size * sizeof(AddressInformation)); + return addr; +} + +// Opaque datatype accessor addrinfo->ai_family +int GetAddressFamily(AddressInformation address) +{ + return address->addr.ai_family; +} + +// Opaque datatype accessor addrinfo->ai_socktype +int GetAddressSocketType(AddressInformation address) +{ + return address->addr.ai_socktype; +} + +// Opaque datatype accessor addrinfo->ai_protocol +int GetAddressProtocol(AddressInformation address) +{ + return address->addr.ai_protocol; +} + +// Opaque datatype accessor addrinfo->ai_canonname +char *GetAddressCanonName(AddressInformation address) +{ + return address->addr.ai_canonname; +} + +// Opaque datatype accessor addrinfo->ai_addr +char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport) +{ + //char *ip[INET6_ADDRSTRLEN]; + char *result = NULL; + struct sockaddr_storage *storage = (struct sockaddr_storage *) address->addr.ai_addr; + switch (storage->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *) address->addr.ai_addr); + //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin_port); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *) address->addr.ai_addr); + //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin6_port); + } + break; + } + if (result == NULL) + { + TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); + SocketSetLastError(0); + } + else + { + strcpy(outhost, result); + } + return result; +} + +// +void PacketSend(Packet *packet) +{ + printf("Sending packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketReceive(Packet *packet) +{ + printf("Receiving packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketWrite16(Packet *packet, uint16_t value) +{ + printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + printf("Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); +} + +// +void PacketWrite32(Packet *packet, uint32_t value) +{ + printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + printf("Network: 0x%08" PRIX32 " - %" PRIu32 "\n", + (uint32_t)(((intptr_t) packet->data) - packet->offs), + (uint32_t)(((intptr_t) packet->data) - packet->offs)); +} + +// +void PacketWrite64(Packet *packet, uint64_t value) +{ + printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 56); + *data++ = (uint8_t)(value >> 48); + *data++ = (uint8_t)(value >> 40); + *data++ = (uint8_t)(value >> 32); + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + printf("Network: 0x%016" PRIX64 " - %" PRIu64 "\n", + (uint64_t)(packet->data - packet->offs), + (uint64_t)(packet->data - packet->offs)); +} + +// +uint16_t PacketRead16(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + uint16_t value = ((uint16_t) data[0] << 8) | data[1]; + printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + return value; +} + +// +uint32_t PacketRead32(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; + printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + return value; +} + +// +uint64_t PacketRead64(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; + printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + return value; +}