/********************************************************************************************** * * 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 // Required for limits #include // 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 #include #include #define IPTOS_LOWDELAY 0x10 #else // Unix #include #include #include #include #include #include #include #include #include #include #include #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 string length #define ADDRESS_IPV6_ADDRSTRLEN 65 // IPv6 string 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 string #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 #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 // Required for: assert() #include // Required for: FILE, fopen(), fclose(), fread() #include // Required for: malloc(), free() #include // 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(void) { #if defined(_WIN32) return WSAGetLastError(); #else return errno; #endif } // Returns a human-readable string representing the last error message static char *SocketGetLastErrorString(void) { 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(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 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] = 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