text: /**********************************************************************************************
  *
  *   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
  *       SDL_net - https://www.libsdl.org/projects/SDL_net/
  *       BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html
  *       BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html
  *       Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2
  *
  *   CONTRIBUTORS:
  *       Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version
  *
  *
  *   LICENSE: zlib/libpng
  *
  *   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.
  *
  *   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.
  *
  **********************************************************************************************/
  
  #ifndef RNET_H
  #define RNET_H
  
  #include <limits.h>         // Required for limits
  #include <inttypes.h>       // Required for platform type sizes
  
  //----------------------------------------------------------------------------------
  // Defines and Macros
  //----------------------------------------------------------------------------------
  
  // Undefine any conflicting windows.h symbols
  // If defined, the following flags inhibit definition of the indicated items.
  #define NOGDICAPMASKS     // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
  #define NOVIRTUALKEYCODES // VK_*
  #define NOWINMESSAGES     // WM_*, EM_*, LB_*, CB_*
  #define NOWINSTYLES       // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
  #define NOSYSMETRICS      // SM_*
  #define NOMENUS           // MF_*
  #define NOICONS           // IDI_*
  #define NOKEYSTATES       // MK_*
  #define NOSYSCOMMANDS     // SC_*
  #define NORASTEROPS       // Binary and Tertiary raster ops
  #define NOSHOWWINDOW      // SW_*
  #define OEMRESOURCE       // OEM Resource values
  #define NOATOM            // Atom Manager routines
  #define NOCLIPBOARD       // Clipboard routines
  #define NOCOLOR           // Screen colors
  #define NOCTLMGR          // Control and Dialog routines
  #define NODRAWTEXT        // DrawText() and DT_*
  #define NOGDI             // All GDI defines and routines
  #define NOKERNEL          // All KERNEL defines and routines
  #define NOUSER            // All USER defines and routines
  #define NONLS             // All NLS defines and routines
  #define NOMB              // MB_* and MessageBox()
  #define NOMEMMGR          // GMEM_*, LMEM_*, GHND, LHND, associated routines
  #define NOMETAFILE        // typedef METAFILEPICT
  #define NOMINMAX          // Macros min(a,b) and max(a,b)
  #define NOMSG             // typedef MSG and associated routines
  #define NOOPENFILE        // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
  #define NOSCROLL          // SB_* and scrolling routines
  #define NOSERVICE         // All Service Controller routines, SERVICE_ equates, etc.
  #define NOSOUND           // Sound driver routines
  #define NOTEXTMETRIC      // typedef TEXTMETRIC and associated routines
  #define NOWH              // SetWindowsHook and WH_*
  #define NOWINOFFSETS      // GWL_*, GCL_*, associated routines
  #define NOCOMM            // COMM driver routines
  #define NOKANJI           // Kanji support stuff.
  #define NOHELP            // Help engine interface.
  #define NOPROFILER        // Profiler interface.
  #define NODEFERWINDOWPOS  // DeferWindowPos routines
  #define NOMCX             // Modem Configuration Extensions
  #define MMNOSOUND
  
  // Allow custom memory allocators
  #ifndef RNET_MALLOC
      #define RNET_MALLOC(sz)       malloc(sz)
  #endif
  #ifndef RNET_CALLOC
      #define RNET_CALLOC(n,sz)     calloc(n,sz)
  #endif
  #ifndef RNET_FREE
      #define RNET_FREE(p)          free(p)
  #endif
  
  //----------------------------------------------------------------------------------
  // Platform type definitions
  // From: https://github.com/DFHack/clsocket/blob/master/src/Host.h
  //----------------------------------------------------------------------------------
  
  #ifdef WIN32
  typedef int socklen_t;
  #endif
  
  #ifndef RESULT_SUCCESS
  #    define RESULT_SUCCESS 0
  #endif // RESULT_SUCCESS
  
  #ifndef RESULT_FAILURE
  #    define RESULT_FAILURE 1
  #endif // RESULT_FAILURE
  
  #ifndef htonll
  #    ifdef _BIG_ENDIAN
  #        define htonll(x) (x)
  #        define ntohll(x) (x)
  #    else
  #        define htonll(x) ((((uint64) htonl(x)) << 32) + htonl(x >> 32))
  #        define ntohll(x) ((((uint64) ntohl(x)) << 32) + ntohl(x >> 32))
  #    endif // _BIG_ENDIAN
  #endif     // htonll
  
  //----------------------------------------------------------------------------------
  // Platform specific network includes
  // From: https://github.com/SDL-mirror/SDL_net/blob/master/SDLnetsys.h
  //----------------------------------------------------------------------------------
  
  // Include system network headers
  #if defined(_WIN32) // Windows
      #define __USE_W32_SOCKETS
      #define WIN32_LEAN_AND_MEAN
      #include <winsock2.h>
      #include <ws2tcpip.h>
      #include <io.h>
      #define IPTOS_LOWDELAY 0x10
  #else               // Unix
      #include <sys/types.h>
      #include <fcntl.h>
      #include <netinet/in.h>
      #include <sys/ioctl.h>
      #include <sys/time.h>
      #include <unistd.h>
      #include <net/if.h>
      #include <netdb.h>
      #include <netinet/tcp.h>
      #include <sys/socket.h>
      #include <arpa/inet.h>
  #endif
  
  #ifndef INVALID_SOCKET
      #define INVALID_SOCKET ~(0)
  #endif
  
  #ifndef __USE_W32_SOCKETS
      #define closesocket close
      #define SOCKET int
      #define INVALID_SOCKET -1
      #define SOCKET_ERROR -1
  #endif
  
  #ifdef __USE_W32_SOCKETS
      #ifndef EINTR
          #define EINTR WSAEINTR
      #endif
  #endif
  
  //----------------------------------------------------------------------------------
  // Module defines
  //----------------------------------------------------------------------------------
  
  // Network connection related defines
  #define SOCKET_MAX_SET_SIZE         32     // Maximum sockets in a set
  #define SOCKET_MAX_QUEUE_SIZE       16     // Maximum socket queue size
  #define SOCKET_MAX_SOCK_OPTS        4      // Maximum socket options
  #define SOCKET_MAX_UDPCHANNELS      32     // Maximum UDP channels
  #define SOCKET_MAX_UDPADDRESSES     4      // Maximum bound UDP addresses
  
  // Network address related defines
  #define ADDRESS_IPV4_ADDRSTRLEN     22     // IPv4 sds length
  #define ADDRESS_IPV6_ADDRSTRLEN     65     // IPv6 sds length
  #define ADDRESS_TYPE_ANY            0      // AF_UNSPEC
  #define ADDRESS_TYPE_IPV4           2      // AF_INET
  #define ADDRESS_TYPE_IPV6           23     // AF_INET6
  #define ADDRESS_MAXHOST             1025   // Max size of a fully-qualified domain name
  #define ADDRESS_MAXSERV             32     // Max size of a service name
  
  // Network address related defines
  #define ADDRESS_ANY                 (unsigned long)0x00000000
  #define ADDRESS_LOOPBACK            0x7f000001
  #define ADDRESS_BROADCAST           (unsigned long)0xffffffff
  #define ADDRESS_NONE                0xffffffff
  
  // Network resolution related defines
  #define NAME_INFO_DEFAULT           0x00   // No flags set
  #define NAME_INFO_NOFQDN            0x01   // Only return nodename portion for local hosts
  #define NAME_INFO_NUMERICHOST       0x02   // Return numeric form of the host's address
  #define NAME_INFO_NAMEREQD          0x04   // Error if the host's name not in DNS
  #define NAME_INFO_NUMERICSERV       0x08   // Return numeric form of the service (port #)
  #define NAME_INFO_DGRAM             0x10   // Service is a datagram service
  
  // Address resolution related defines
  #if defined(_WIN32)
      #define ADDRESS_INFO_PASSIVE                (0x00000001)  // Socket address will be used in bind() call
      #define ADDRESS_INFO_CANONNAME              (0x00000002)  // Return canonical name in first ai_canonname
      #define ADDRESS_INFO_NUMERICHOST            (0x00000004)  // Nodename must be a numeric address sds
      #define ADDRESS_INFO_NUMERICSERV            (0x00000008)  // Servicename must be a numeric port number
      #define ADDRESS_INFO_DNS_ONLY               (0x00000010)  // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.)
      #define ADDRESS_INFO_ALL                    (0x00000100)  // Query both IP6 and IP4 with AI_V4MAPPED
      #define ADDRESS_INFO_ADDRCONFIG             (0x00000400)  // Resolution only if global address configured
      #define ADDRESS_INFO_V4MAPPED               (0x00000800)  // On v6 failure, query v4 and convert to V4MAPPED format
      #define ADDRESS_INFO_NON_AUTHORITATIVE      (0x00004000)  // LUP_NON_AUTHORITATIVE
      #define ADDRESS_INFO_SECURE                 (0x00008000)  // LUP_SECURE
      #define ADDRESS_INFO_RETURN_PREFERRED_NAMES (0x00010000)  // LUP_RETURN_PREFERRED_NAMES
      #define ADDRESS_INFO_FQDN                   (0x00020000)  // Return the FQDN in ai_canonname
      #define ADDRESS_INFO_FILESERVER             (0x00040000)  // Resolving fileserver name resolution
      #define ADDRESS_INFO_DISABLE_IDN_ENCODING   (0x00080000)  // Disable Internationalized Domain Names handling
      #define ADDRESS_INFO_EXTENDED               (0x80000000)  // Indicates this is extended ADDRINFOEX(2/..) struct
      #define ADDRESS_INFO_RESOLUTION_HANDLE      (0x40000000)  // Request resolution handle
  #endif
  
  //----------------------------------------------------------------------------------
  // Types and Structures Definition
  //----------------------------------------------------------------------------------
  
  // Boolean type
  #if defined(__STDC__) && __STDC_VERSION__ >= 199901L
      #include <stdbool.h>
  #elif !defined(__cplusplus) && !defined(bool)
      typedef enum { false, true } bool;
  #endif
  
  typedef enum {
      SOCKET_TCP = 0,             // SOCK_STREAM
      SOCKET_UDP = 1              // SOCK_DGRAM
  } SocketType;
  
  // Network typedefs
  typedef uint32_t SocketChannel;
  typedef struct _AddressInformation *AddressInformation;
  typedef struct _SocketAddress *SocketAddress;
  typedef struct _SocketAddressIPv4 *SocketAddressIPv4;
  typedef struct _SocketAddressIPv6 *SocketAddressIPv6;
  typedef struct _SocketAddressStorage *SocketAddressStorage;
  
  // IPAddress definition (in network byte order)
  typedef struct IPAddress {
      unsigned long host;         // 32-bit IPv4 host address
      unsigned short port;        // 16-bit protocol port
  } IPAddress;
  
  typedef struct UDPChannel {
      int numbound;               // The total number of addresses this channel is bound to
      IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to
  } UDPChannel;
  
  // An option ID, value, sizeof(value) tuple for setsockopt(2).
  typedef struct SocketOpt {
      int id;                     // Socked option id
      int valueLen;               // Socked option value len
      void *value;                // Socked option value data
  } SocketOpt;
  
  typedef struct Socket {
      int ready;                  // Is the socket ready? i.e. has information
      int status;                 // The last status code to have occured using this socket
      bool isServer;              // Is this socket a server socket (i.e. TCP/UDP Listen Server)
      SocketChannel channel;      // The socket handle id
      SocketType type;            // Is this socket a TCP or UDP socket?
      
      bool isIPv6;                // Is this socket address an ipv6 address?
      SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order)
      SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order)
  
      struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to
  } Socket;
  
  // Configuration for a socket
  typedef struct SocketConfig {
      SocketType type;            // The type of socket, TCP/UDP
      char *host;                 // The host address in xxx.xxx.xxx.xxx form
      char *port;                 // The target port/service in the form "http" or "25565"
      bool server;                // Listen for incoming clients?
      bool nonblocking;           // non-blocking operation?
      int backlog_size;           // set a custom backlog size
      SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS];
  } SocketConfig;
  
  typedef struct SocketDataPacket {
      IPAddress address;          // The source/dest address of an incoming/outgoing packet
      int channel;                // The src/dst channel of the packet
      int maxlen;                 // The size of the data buffer
      int status;                 // Packet status after sending
      unsigned int len;           // The length of the packet data
      unsigned char *data;        // The packet data
  } SocketDataPacket;
  
  // Result from calling open with a given config
  typedef struct SocketResult {
      int status;                 // Socket result state
      Socket *socket;             // Socket ref
  } SocketResult;
  
  typedef struct SocketSet {
      int numsockets;             // Socket set count
      int maxsockets;             // Socket set max
      struct Socket **sockets;    // Sockets array
  } SocketSet;
  
  // Packet type
  typedef struct Packet {
      uint32_t size;              // The total size of bytes in data
      uint32_t offs;              // The offset to data access
      uint32_t maxs;              // The max size of data
      uint8_t *data;              // Data stored in network byte order
  } Packet;
  
  
  #ifdef __cplusplus
  extern "C" {        // Prevents name mangling of functions
  #endif
  
  //----------------------------------------------------------------------------------
  // Global Variables Definition
  //----------------------------------------------------------------------------------
  //...
  
  //----------------------------------------------------------------------------------
  // Module Functions Declaration
  //----------------------------------------------------------------------------------
  
  // Initialisation and cleanup
  bool InitNetworkDevice(void);
  void CloseNetworkDevice(void);
  
  // Address API
  void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv);
  int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr);
  int GetAddressFamily(AddressInformation address);
  int GetAddressSocketType(AddressInformation address);
  int GetAddressProtocol(AddressInformation address);
  char *GetAddressCanonName(AddressInformation address);
  char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport);
  
  // Address Memory API
  AddressInformation LoadAddress(void);
  void UnloadAddress(AddressInformation *addressInfo);
  AddressInformation *LoadAddressList(int size);
  
  // Socket API
  bool SocketCreate(SocketConfig *config, SocketResult *result);
  bool SocketBind(SocketConfig *config, SocketResult *result);
  bool SocketListen(SocketConfig *config, SocketResult *result);
  bool SocketConnect(SocketConfig *config, SocketResult *result);
  Socket *SocketAccept(Socket *server, SocketConfig *config);
  
  // General Socket API
  int SocketSend(Socket *sock, const void *datap, int len);
  int SocketReceive(Socket *sock, void *data, int maxlen);
  SocketAddressStorage SocketGetPeerAddress(Socket *sock);
  char *GetSocketAddressHost(SocketAddressStorage storage);
  short GetSocketAddressPort(SocketAddressStorage storage);
  void SocketClose(Socket *sock);
  
  // UDP Socket API
  int SocketSetChannel(Socket *socket, int channel, const IPAddress *address);
  void SocketUnsetChannel(Socket *socket, int channel);
  
  // UDP DataPacket API
  SocketDataPacket *AllocPacket(int size);
  int ResizePacket(SocketDataPacket *packet, int newsize);
  void FreePacket(SocketDataPacket *packet);
  SocketDataPacket **AllocPacketList(int count, int size);
  void FreePacketList(SocketDataPacket **packets);
  
  // Socket Memory API
  Socket *LoadSocket(void);
  void UnloadSocket(Socket **sock);
  SocketResult *LoadSocketResult(void);
  void UnloadSocketResult(SocketResult **result);
  SocketSet *LoadSocketSet(int max);
  void UnloadSocketSet(SocketSet *sockset);
  
  // Socket I/O API
  bool IsSocketReady(Socket *sock);
  bool IsSocketConnected(Socket *sock);
  int AddSocket(SocketSet *set, Socket *sock);
  int RemoveSocket(SocketSet *set, Socket *sock);
  int CheckSockets(SocketSet *set, unsigned int timeout);
  
  // Packet API
  void PacketSend(Packet *packet);
  void PacketReceive(Packet *packet);
  void PacketWrite8(Packet *packet, uint16_t value);
  void PacketWrite16(Packet *packet, uint16_t value);
  void PacketWrite32(Packet *packet, uint32_t value);
  void PacketWrite64(Packet *packet, uint64_t value);
  uint16_t PacketRead8(Packet *packet);
  uint16_t PacketRead16(Packet *packet);
  uint32_t PacketRead32(Packet *packet);
  uint64_t PacketRead64(Packet *packet);
  
  #ifdef __cplusplus
  }
  #endif
  
  #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 sds (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 sds 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 sds 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(void)
  {
  #if defined(_WIN32)
      return WSAGetLastError();
  #else
      return errno;
  #endif
  }
  
  // Returns a human-readable sds representing the last error message
  static char *SocketGetLastErrorString(void)
  {
      return SocketErrorCodeToString(SocketGetLastError());
  }
  
  // Returns a human-readable sds 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(void)
  {
  #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(void)
  {
  #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 sds that contains a host (node) name or a numeric host address sds.
  //      const char* service - A pointer to a NULL-terminated ANSI sds that contains either a service name or port number represented as a sds.
  //
  //    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] = LoadAddress();
              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 = LoadSocket();
      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 *LoadSocketResult(void)
  {
      struct SocketResult *res = (struct SocketResult *)RNET_MALLOC(sizeof(*res));
  
      if (res != NULL)
      {
          memset(res, 0, sizeof(*res));
          if ((res->socket = LoadSocket()) == NULL)
          {
              RNET_FREE(res);
              res = NULL;
          }
      }
  
      return res;
  }
  
  // Free an allocated SocketResult
  void UnloadSocketResult(SocketResult **result)
  {
      if (*result != NULL)
      {
          if ((*result)->socket != NULL) UnloadSocket(&((*result)->socket));
  
          RNET_FREE(*result);
          *result = NULL;
      }
  }
  
  // Allocate a Socket
  Socket *LoadSocket(void)
  {
      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 UnloadSocket(Socket **sock)
  {
      if (*sock != NULL)
      {
          RNET_FREE(*sock);
          *sock = NULL;
      }
  }
  
  // Allocate a SocketSet
  SocketSet *LoadSocketSet(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 UnloadSocketSet(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 LoadAddress(void)
  {
      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 UnloadAddress(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 *LoadAddressList(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