diff options
| author | Ray <[email protected]> | 2020-02-19 18:15:59 +0100 |
|---|---|---|
| committer | Ray <[email protected]> | 2020-02-19 18:15:59 +0100 |
| commit | faff51214079955eb86dc972e1746ad728e6d5e3 (patch) | |
| tree | 24d65cbf92ab4acb1dbf4da5df2dd2437c432aab /src | |
| parent | 946a6cb2809883f9b287ac889beb835deca0f571 (diff) | |
| download | raylib-faff51214079955eb86dc972e1746ad728e6d5e3.tar.gz raylib-faff51214079955eb86dc972e1746ad728e6d5e3.zip | |
[rnet] Convert to header-only
Some functions renamed
Diffstat (limited to 'src')
| -rw-r--r-- | src/rnet.c.review | 1865 | ||||
| -rw-r--r-- | src/rnet.h | 1853 |
2 files changed, 1847 insertions, 1871 deletions
diff --git a/src/rnet.c.review b/src/rnet.c.review deleted file mode 100644 index dcec045c..00000000 --- a/src/rnet.c.review +++ /dev/null @@ -1,1865 +0,0 @@ -/********************************************************************************************* -* -* 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 -* -* CONTRIBUTORS: -* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2019-2020 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 <assert.h> // Required for: assert() -#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread() -#include <stdlib.h> // Required for: malloc(), free() -#include <string.h> // Required for: strcmp(), strncmp() - -#define NET_DEBUG_ENABLED 1 - -#if defined(SUPPORT_TRACELOG) - #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) - - #if defined(SUPPORT_TRACELOG_DEBUG) - #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__) - #else - #define TRACELOGD(...) (void)0 - #endif -#else - #define TRACELOG(level, ...) (void)0 - #define TRACELOGD(...) (void)0 -#endif - -//---------------------------------------------------------------------------------- -// 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; - -//---------------------------------------------------------------------------------- -// Local module Functions 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); - -//---------------------------------------------------------------------------------- -// Local module Functions Definition -//---------------------------------------------------------------------------------- -// 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]; - sTRACELOG(LOG_INFO, 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 *socket, struct addrinfo *address) -{ - switch (socket->type) - { - case SOCKET_TCP: - { - if (address->ai_family == AF_INET) socket->channel = socket(AF_INET, SOCK_STREAM, 0); - else socket->channel = socket(AF_INET6, SOCK_STREAM, 0); - } break; - case SOCKET_UDP: - { - if (address->ai_family == AF_INET) socket->channel = socket(AF_INET, SOCK_DGRAM, 0); - else socket->channel = socket(AF_INET6, SOCK_DGRAM, 0); - } break; - default: TRACELOG(LOG_WARNING, "Invalid socket type specified."); break; - } - - return IsSocketValid(socket); -} - -// 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 *)RNET_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 *)RNET_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; - default: 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; - - wVersionRequested = MAKEWORD(2, 2); - int 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 *)RNET_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 *)RNET_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 = 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 *)RNET_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 *)RNET_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 = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet)); - int error = 1; - - if (packet != NULL) - { - packet->maxlen = size; - packet->data = (uint8_t *)RNET_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 = (uint8_t *)RNET_MALLOC(newsize); - - if (newdata != NULL) - { - RNET_FREE(packet->data); - packet->data = newdata; - packet->maxlen = newsize; - } - - return (packet->maxlen); -} - -void FreePacket(SocketDataPacket *packet) -{ - if (packet) - { - RNET_FREE(packet->data); - RNET_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 = (SocketDataPacket **)RNET_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) - { - for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]); - RNET_FREE(packetV); - } -} - -// Send 'len' bytes of 'data' over the non-server socket 'sock' -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; - default: 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) return false; // Timeout - 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 = (struct SocketResult *)RNET_MALLOC(sizeof(*res)); - - if (res != NULL) - { - memset(res, 0, sizeof(*res)); - if ((res->socket = AllocSocket()) == NULL) - { - RNET_FREE(res); - res = NULL; - } - } - - return res; -} - -// Free an allocated SocketResult -void FreeSocketResult(SocketResult **result) -{ - if (*result != NULL) - { - if ((*result)->socket != NULL) FreeSocket(&((*result)->socket)); - - RNET_FREE(*result); - *result = NULL; - } -} - -// Allocate a Socket -Socket *AllocSocket() -{ - struct Socket *sock; - sock = (Socket *)RNET_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); - RNET_FREE(sock); - sock = NULL; - } - - return sock; -} - -// Free an allocated Socket -void FreeSocket(Socket **sock) -{ - if (*sock != NULL) - { - RNET_FREE(*sock); - *sock = NULL; - } -} - -// Allocate a SocketSet -SocketSet *AllocSocketSet(int max) -{ - struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set)); - - if (set != NULL) - { - set->numsockets = 0; - set->maxsockets = max; - set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets)); - if (set->sockets != NULL) - { - for (int i = 0; i < max; ++i) set->sockets[i] = NULL; - } - else - { - RNET_FREE(set); - set = NULL; - } - } - - return (set); -} - -// Free an allocated SocketSet -void FreeSocketSet(SocketSet *set) -{ - if (set) - { - RNET_FREE(set->sockets); - RNET_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) -{ - if (sock != NULL) - { - int i = 0; - 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) RNET_CALLOC(1, sizeof(*addressInfo)); - - if (addressInfo != NULL) - { - addressInfo->addr.ai_addr = (struct sockaddr *)RNET_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) - { - RNET_FREE((*addressInfo)->addr.ai_addr); - (*addressInfo)->addr.ai_addr = NULL; - } - - RNET_FREE(*addressInfo); - *addressInfo = NULL; - } -} - -// Allocate a list of AddressInformation -AddressInformation *AllocAddressList(int size) -{ - AddressInformation *addr; - addr = (AddressInformation *)RNET_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; - default: break; - } - - if (result == NULL) - { - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); - SocketSetLastError(0); - } - else - { - strcpy(outhost, result); - } - return result; -} - -// -void PacketSend(Packet *packet) -{ - TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketReceive(Packet *packet) -{ - TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketWrite16(Packet *packet, uint16_t value) -{ - TRACELOG(LOG_INFO, "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); - TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); -} - -// -void PacketWrite32(Packet *packet, uint32_t value) -{ - TRACELOG(LOG_INFO, "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); - - TRACELOG(LOG_INFO, "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) -{ - TRACELOG(LOG_INFO, "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); - - TRACELOG(LOG_INFO, "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]; - TRACELOG(LOG_INFO, "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]; - TRACELOG(LOG_INFO, "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]; - TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); - - return value; -} @@ -1,6 +1,9 @@ /**********************************************************************************************
*
-* rnet - Provides cross-platform network defines, macros etc
+* 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
*
* INSPIRED BY:
* SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php
@@ -15,7 +18,7 @@ *
* LICENSE: zlib/libpng
*
-* Copyright (c) 2019 Jak Barnes (github: @syphonx) and Ramon Santamaria (@raysan5)
+* Copyright (c) 2019-2020 Jak Barnes (@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.
@@ -193,7 +196,7 @@ typedef int socklen_t; #define ADDRESS_ANY (unsigned long)0x00000000
#define ADDRESS_LOOPBACK 0x7f000001
#define ADDRESS_BROADCAST (unsigned long)0xffffffff
-#define ADDRESS_NONE xffffffff
+#define ADDRESS_NONE 0xffffffff
// Network resolution related defines
#define NAME_INFO_DEFAULT 0x00 // No flags set
@@ -333,8 +336,8 @@ extern "C" { // Prevents name mangling of functions //----------------------------------------------------------------------------------
// Initialisation and cleanup
-bool InitNetwork(void);
-void CloseNetwork(void);
+bool InitNetworkDevice(void);
+void CloseNetworkDevice(void);
// Address API
void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv);
@@ -408,4 +411,1842 @@ uint64_t PacketRead64(Packet *packet); }
#endif
-#endif // RNET_H
\ No newline at end of file +#endif // RNET_H
+
+/***********************************************************************************
+*
+* RNET IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RNET_IMPLEMENTATION)
+
+#include <assert.h> // Required for: assert()
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strcmp(), strncmp()
+
+#define NET_DEBUG_ENABLED 1
+
+#if defined(SUPPORT_TRACELOG)
+ #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
+
+ #if defined(SUPPORT_TRACELOG_DEBUG)
+ #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__)
+ #else
+ #define TRACELOGD(...) (void)0
+ #endif
+#else
+ #define TRACELOG(level, ...) (void)0
+ #define TRACELOGD(...) (void)0
+#endif
+
+//----------------------------------------------------------------------------------
+// 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;
+
+//----------------------------------------------------------------------------------
+// Local module Functions 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);
+
+//----------------------------------------------------------------------------------
+// Local module Functions Definition
+//----------------------------------------------------------------------------------
+// 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];
+ TRACELOG(LOG_INFO, 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 *sckt, struct addrinfo *address)
+{
+ switch (sckt->type)
+ {
+ case SOCKET_TCP:
+ {
+ if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_STREAM, 0);
+ else sckt->channel = socket(AF_INET6, SOCK_STREAM, 0);
+ } break;
+ case SOCKET_UDP:
+ {
+ if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_DGRAM, 0);
+ else sckt->channel = socket(AF_INET6, SOCK_DGRAM, 0);
+ } break;
+ default: TRACELOG(LOG_WARNING, "Invalid socket type specified."); break;
+ }
+
+ return IsSocketValid(sckt);
+}
+
+// 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 *)RNET_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 *)RNET_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;
+ default: 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 InitNetworkDevice()
+{
+#if defined(_WIN32)
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 2);
+ int 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 CloseNetworkDevice()
+{
+#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 *)RNET_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 *)RNET_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 = 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 *)RNET_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 *)RNET_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 = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet));
+ int error = 1;
+
+ if (packet != NULL)
+ {
+ packet->maxlen = size;
+ packet->data = (uint8_t *)RNET_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 = (uint8_t *)RNET_MALLOC(newsize);
+
+ if (newdata != NULL)
+ {
+ RNET_FREE(packet->data);
+ packet->data = newdata;
+ packet->maxlen = newsize;
+ }
+
+ return (packet->maxlen);
+}
+
+void FreePacket(SocketDataPacket *packet)
+{
+ if (packet)
+ {
+ RNET_FREE(packet->data);
+ RNET_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 = (SocketDataPacket **)RNET_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)
+ {
+ for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]);
+ RNET_FREE(packetV);
+ }
+}
+
+// Send 'len' bytes of 'data' over the non-server socket 'sock'
+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;
+ default: 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) return false; // Timeout
+ 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 = (struct SocketResult *)RNET_MALLOC(sizeof(*res));
+
+ if (res != NULL)
+ {
+ memset(res, 0, sizeof(*res));
+ if ((res->socket = AllocSocket()) == NULL)
+ {
+ RNET_FREE(res);
+ res = NULL;
+ }
+ }
+
+ return res;
+}
+
+// Free an allocated SocketResult
+void FreeSocketResult(SocketResult **result)
+{
+ if (*result != NULL)
+ {
+ if ((*result)->socket != NULL) FreeSocket(&((*result)->socket));
+
+ RNET_FREE(*result);
+ *result = NULL;
+ }
+}
+
+// Allocate a Socket
+Socket *AllocSocket()
+{
+ struct Socket *sock;
+ sock = (Socket *)RNET_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);
+ RNET_FREE(sock);
+ sock = NULL;
+ }
+
+ return sock;
+}
+
+// Free an allocated Socket
+void FreeSocket(Socket **sock)
+{
+ if (*sock != NULL)
+ {
+ RNET_FREE(*sock);
+ *sock = NULL;
+ }
+}
+
+// Allocate a SocketSet
+SocketSet *AllocSocketSet(int max)
+{
+ struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set));
+
+ if (set != NULL)
+ {
+ set->numsockets = 0;
+ set->maxsockets = max;
+ set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets));
+ if (set->sockets != NULL)
+ {
+ for (int i = 0; i < max; ++i) set->sockets[i] = NULL;
+ }
+ else
+ {
+ RNET_FREE(set);
+ set = NULL;
+ }
+ }
+
+ return (set);
+}
+
+// Free an allocated SocketSet
+void FreeSocketSet(SocketSet *set)
+{
+ if (set)
+ {
+ RNET_FREE(set->sockets);
+ RNET_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)
+{
+ if (sock != NULL)
+ {
+ int i = 0;
+ 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) RNET_CALLOC(1, sizeof(*addressInfo));
+
+ if (addressInfo != NULL)
+ {
+ addressInfo->addr.ai_addr = (struct sockaddr *)RNET_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)
+ {
+ RNET_FREE((*addressInfo)->addr.ai_addr);
+ (*addressInfo)->addr.ai_addr = NULL;
+ }
+
+ RNET_FREE(*addressInfo);
+ *addressInfo = NULL;
+ }
+}
+
+// Allocate a list of AddressInformation
+AddressInformation *AllocAddressList(int size)
+{
+ AddressInformation *addr;
+ addr = (AddressInformation *)RNET_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;
+ default: break;
+ }
+
+ if (result == NULL)
+ {
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError()));
+ SocketSetLastError(0);
+ }
+ else
+ {
+ strcpy(outhost, result);
+ }
+ return result;
+}
+
+//
+void PacketSend(Packet *packet)
+{
+ TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size);
+}
+
+//
+void PacketReceive(Packet *packet)
+{
+ TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size);
+}
+
+//
+void PacketWrite16(Packet *packet, uint16_t value)
+{
+ TRACELOG(LOG_INFO, "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);
+ TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data);
+}
+
+//
+void PacketWrite32(Packet *packet, uint32_t value)
+{
+ TRACELOG(LOG_INFO, "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);
+
+ TRACELOG(LOG_INFO, "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)
+{
+ TRACELOG(LOG_INFO, "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);
+
+ TRACELOG(LOG_INFO, "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];
+ TRACELOG(LOG_INFO, "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];
+ TRACELOG(LOG_INFO, "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];
+ TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value);
+
+ return value;
+}
+
+#endif // RNET_IMPLEMENTATION
\ No newline at end of file |
