diff options
| author | Ray <[email protected]> | 2021-05-31 11:43:56 +0200 |
|---|---|---|
| committer | Ray <[email protected]> | 2021-05-31 11:43:56 +0200 |
| commit | 9bd3d78374782b45d0d7f1a41f2b545d631dab33 (patch) | |
| tree | 0f68089dd487d1b81038dd10647d7be4ca4c32d4 /src | |
| parent | 1c5de9721a8c9b9543044b461d4a9480c1d25b43 (diff) | |
| download | raylib-9bd3d78374782b45d0d7f1a41f2b545d631dab33.tar.gz raylib-9bd3d78374782b45d0d7f1a41f2b545d631dab33.zip | |
WARNING: Moved some **extra** raylib libraries to /extras/ directory
Diffstat (limited to 'src')
| -rw-r--r-- | src/extras/easings.h (renamed from src/easings.h) | 0 | ||||
| -rw-r--r-- | src/extras/physac.h (renamed from src/physac.h) | 0 | ||||
| -rw-r--r-- | src/extras/rmem.h (renamed from src/rmem.h) | 0 | ||||
| -rw-r--r-- | src/extras/rnet.h (renamed from src/rnet.h) | 4510 |
4 files changed, 2255 insertions, 2255 deletions
diff --git a/src/easings.h b/src/extras/easings.h index 3441305f..3441305f 100644 --- a/src/easings.h +++ b/src/extras/easings.h diff --git a/src/physac.h b/src/extras/physac.h index 676a9695..676a9695 100644 --- a/src/physac.h +++ b/src/extras/physac.h diff --git a/src/rmem.h b/src/extras/rmem.h index dbf417fe..dbf417fe 100644 --- a/src/rmem.h +++ b/src/extras/rmem.h diff --git a/src/rnet.h b/src/extras/rnet.h index 7ed29b15..439b105a 100644 --- a/src/rnet.h +++ b/src/extras/rnet.h @@ -1,2256 +1,2256 @@ -/**********************************************************************************************
-*
-* 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 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
-#ifdef _WIN32
- #include <stdbool.h>
-#else
-#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
- #include <stdbool.h>
-#elif !defined(__cplusplus) && !defined(bool)
- typedef enum { false, true } bool;
-#endif
-#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, unsigned short *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);
-const 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 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 = (unsigned short)(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((unsigned short)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
-const 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, unsigned short *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;
-}
-
+/********************************************************************************************** +* +* 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 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 +#ifdef _WIN32 + #include <stdbool.h> +#else +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include <stdbool.h> +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; +#endif +#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, unsigned short *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); +const 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 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 = (unsigned short)(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((unsigned short)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 +const 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, unsigned short *outport) +{ + //char *ip[INET6_ADDRSTRLEN]; + char *result = NULL; + struct sockaddr_storage *storage = (struct sockaddr_storage *)address->addr.ai_addr; + + switch (storage->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *)address->addr.ai_addr); + //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin_port); + } break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *)address->addr.ai_addr); + //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin6_port); + } break; + default: break; + } + + if (result == NULL) + { + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); + SocketSetLastError(0); + } + else + { + strcpy(outhost, result); + } + return result; +} + +// +void PacketSend(Packet *packet) +{ + TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketReceive(Packet *packet) +{ + TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketWrite16(Packet *packet, uint16_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); +} + +// +void PacketWrite32(Packet *packet, uint32_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + + TRACELOG(LOG_INFO, "Network: 0x%08" PRIX32 " - %" PRIu32 "\n", + (uint32_t)(((intptr_t) packet->data) - packet->offs), + (uint32_t)(((intptr_t) packet->data) - packet->offs)); +} + +// +void PacketWrite64(Packet *packet, uint64_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 56); + *data++ = (uint8_t)(value >> 48); + *data++ = (uint8_t)(value >> 40); + *data++ = (uint8_t)(value >> 32); + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + + TRACELOG(LOG_INFO, "Network: 0x%016" PRIX64 " - %" PRIu64 "\n", (uint64_t)(packet->data - packet->offs), (uint64_t)(packet->data - packet->offs)); +} + +// +uint16_t PacketRead16(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + uint16_t value = ((uint16_t) data[0] << 8) | data[1]; + TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + + return value; +} + +// +uint32_t PacketRead32(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; + TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + + return value; +} + +// +uint64_t PacketRead64(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; + TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + + return value; +} + #endif // RNET_IMPLEMENTATION
\ No newline at end of file |
