raylib/src/rnet.h
2020-02-20 13:46:33 +01:00

2252 lines
77 KiB
C

/**********************************************************************************************
*
* 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
#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#elif !defined(__cplusplus) && !defined(bool)
typedef enum { false, true } bool;
#endif
typedef enum {
SOCKET_TCP = 0, // SOCK_STREAM
SOCKET_UDP = 1 // SOCK_DGRAM
} SocketType;
// Network typedefs
typedef uint32_t SocketChannel;
typedef struct _AddressInformation *AddressInformation;
typedef struct _SocketAddress *SocketAddress;
typedef struct _SocketAddressIPv4 *SocketAddressIPv4;
typedef struct _SocketAddressIPv6 *SocketAddressIPv6;
typedef struct _SocketAddressStorage *SocketAddressStorage;
// IPAddress definition (in network byte order)
typedef struct IPAddress {
unsigned long host; // 32-bit IPv4 host address
unsigned short port; // 16-bit protocol port
} IPAddress;
typedef struct UDPChannel {
int numbound; // The total number of addresses this channel is bound to
IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to
} UDPChannel;
// An option ID, value, sizeof(value) tuple for setsockopt(2).
typedef struct SocketOpt {
int id; // Socked option id
int valueLen; // Socked option value len
void *value; // Socked option value data
} SocketOpt;
typedef struct Socket {
int ready; // Is the socket ready? i.e. has information
int status; // The last status code to have occured using this socket
bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server)
SocketChannel channel; // The socket handle id
SocketType type; // Is this socket a TCP or UDP socket?
bool isIPv6; // Is this socket address an ipv6 address?
SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order)
SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order)
struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to
} Socket;
// Configuration for a socket
typedef struct SocketConfig {
SocketType type; // The type of socket, TCP/UDP
char *host; // The host address in xxx.xxx.xxx.xxx form
char *port; // The target port/service in the form "http" or "25565"
bool server; // Listen for incoming clients?
bool nonblocking; // non-blocking operation?
int backlog_size; // set a custom backlog size
SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS];
} SocketConfig;
typedef struct SocketDataPacket {
IPAddress address; // The source/dest address of an incoming/outgoing packet
int channel; // The src/dst channel of the packet
int maxlen; // The size of the data buffer
int status; // Packet status after sending
unsigned int len; // The length of the packet data
unsigned char *data; // The packet data
} SocketDataPacket;
// Result from calling open with a given config
typedef struct SocketResult {
int status; // Socket result state
Socket *socket; // Socket ref
} SocketResult;
typedef struct SocketSet {
int numsockets; // Socket set count
int maxsockets; // Socket set max
struct Socket **sockets; // Sockets array
} SocketSet;
// Packet type
typedef struct Packet {
uint32_t size; // The total size of bytes in data
uint32_t offs; // The offset to data access
uint32_t maxs; // The max size of data
uint8_t *data; // Data stored in network byte order
} Packet;
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
//...
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
// Initialisation and cleanup
bool InitNetworkDevice(void);
void CloseNetworkDevice(void);
// Address API
void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv);
int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr);
int GetAddressFamily(AddressInformation address);
int GetAddressSocketType(AddressInformation address);
int GetAddressProtocol(AddressInformation address);
char *GetAddressCanonName(AddressInformation address);
char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport);
// Address Memory API
AddressInformation LoadAddress(void);
void UnloadAddress(AddressInformation *addressInfo);
AddressInformation *LoadAddressList(int size);
// Socket API
bool SocketCreate(SocketConfig *config, SocketResult *result);
bool SocketBind(SocketConfig *config, SocketResult *result);
bool SocketListen(SocketConfig *config, SocketResult *result);
bool SocketConnect(SocketConfig *config, SocketResult *result);
Socket *SocketAccept(Socket *server, SocketConfig *config);
// General Socket API
int SocketSend(Socket *sock, const void *datap, int len);
int SocketReceive(Socket *sock, void *data, int maxlen);
SocketAddressStorage SocketGetPeerAddress(Socket *sock);
char *GetSocketAddressHost(SocketAddressStorage storage);
short GetSocketAddressPort(SocketAddressStorage storage);
void SocketClose(Socket *sock);
// UDP Socket API
int SocketSetChannel(Socket *socket, int channel, const IPAddress *address);
void SocketUnsetChannel(Socket *socket, int channel);
// UDP DataPacket API
SocketDataPacket *AllocPacket(int size);
int ResizePacket(SocketDataPacket *packet, int newsize);
void FreePacket(SocketDataPacket *packet);
SocketDataPacket **AllocPacketList(int count, int size);
void FreePacketList(SocketDataPacket **packets);
// Socket Memory API
Socket *LoadSocket(void);
void UnloadSocket(Socket **sock);
SocketResult *LoadSocketResult(void);
void UnloadSocketResult(SocketResult **result);
SocketSet *LoadSocketSet(int max);
void UnloadSocketSet(SocketSet *sockset);
// Socket I/O API
bool IsSocketReady(Socket *sock);
bool IsSocketConnected(Socket *sock);
int AddSocket(SocketSet *set, Socket *sock);
int RemoveSocket(SocketSet *set, Socket *sock);
int CheckSockets(SocketSet *set, unsigned int timeout);
// Packet API
void PacketSend(Packet *packet);
void PacketReceive(Packet *packet);
void PacketWrite8(Packet *packet, uint16_t value);
void PacketWrite16(Packet *packet, uint16_t value);
void PacketWrite32(Packet *packet, uint32_t value);
void PacketWrite64(Packet *packet, uint64_t value);
uint16_t PacketRead8(Packet *packet);
uint16_t PacketRead16(Packet *packet);
uint32_t PacketRead32(Packet *packet);
uint64_t PacketRead64(Packet *packet);
#ifdef __cplusplus
}
#endif
#endif // RNET_H
/***********************************************************************************
*
* RNET IMPLEMENTATION
*
************************************************************************************/
#if defined(RNET_IMPLEMENTATION)
#include <assert.h> // Required for: assert()
#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
#include <stdlib.h> // Required for: malloc(), free()
#include <string.h> // Required for: strcmp(), strncmp()
#define NET_DEBUG_ENABLED 1
#if defined(SUPPORT_TRACELOG)
#define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
#if defined(SUPPORT_TRACELOG_DEBUG)
#define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__)
#else
#define TRACELOGD(...) (void)0
#endif
#else
#define TRACELOG(level, ...) (void)0
#define TRACELOGD(...) (void)0
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct _SocketAddress
{
struct sockaddr address;
} _SocketAddress;
typedef struct _SocketAddressIPv4
{
struct sockaddr_in address;
} _SocketAddressIPv4;
typedef struct _SocketAddressIPv6
{
struct sockaddr_in6 address;
} _SocketAddressIPv6;
typedef struct _SocketAddressStorage
{
struct sockaddr_storage address;
} _SocketAddressStorage;
typedef struct _AddressInformation
{
struct addrinfo addr;
} _AddressInformation;
//----------------------------------------------------------------------------------
// Local module Functions Declarations
//----------------------------------------------------------------------------------
static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol);
static const char *SocketAddressToString(struct sockaddr_storage *sockaddr);
static bool IsIPv4Address(const char *ip);
static bool IsIPv6Address(const char *ip);
static void *GetSocketPortPtr(struct sockaddr_storage *sa);
static void *GetSocketAddressPtr(struct sockaddr_storage *sa);
static bool IsSocketValid(Socket *sock);
static void SocketSetLastError(int err);
static int SocketGetLastError();
static char *SocketGetLastErrorString();
static char *SocketErrorCodeToString(int err);
static bool SocketSetDefaults(SocketConfig *config);
static bool InitSocket(Socket *sock, struct addrinfo *addr);
static bool CreateSocket(SocketConfig *config, SocketResult *outresult);
static bool SocketSetBlocking(Socket *sock);
static bool SocketSetNonBlocking(Socket *sock);
static bool SocketSetOptions(SocketConfig *config, Socket *sock);
static void SocketSetHints(SocketConfig *config, struct addrinfo *hints);
//----------------------------------------------------------------------------------
// Local module Functions Definition
//----------------------------------------------------------------------------------
// Print socket information
static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol)
{
switch (family)
{
case AF_UNSPEC: TRACELOG(LOG_DEBUG, "\tFamily: Unspecified"); break;
case AF_INET:
{
TRACELOG(LOG_DEBUG, "\tFamily: AF_INET (IPv4)");
TRACELOG(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr));
} break;
case AF_INET6:
{
TRACELOG(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)");
TRACELOG(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr));
} break;
case AF_NETBIOS:
{
TRACELOG(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)");
} break;
default: TRACELOG(LOG_DEBUG, "\tFamily: Other %ld", family); break;
}
TRACELOG(LOG_DEBUG, "\tSocket type:");
switch (socktype)
{
case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break;
case SOCK_STREAM: TRACELOG(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); break;
case SOCK_DGRAM: TRACELOG(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); break;
case SOCK_RAW: TRACELOG(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break;
case SOCK_RDM: TRACELOG(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); break;
case SOCK_SEQPACKET: TRACELOG(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); break;
default: TRACELOG(LOG_DEBUG, "\t- Other %ld", socktype); break;
}
TRACELOG(LOG_DEBUG, "\tProtocol:");
switch (protocol)
{
case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break;
case IPPROTO_TCP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break;
case IPPROTO_UDP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break;
default: TRACELOG(LOG_DEBUG, "\t- Other %ld", protocol); break;
}
}
// Convert network ordered socket address to human readable string (127.0.0.1)
static const char *SocketAddressToString(struct sockaddr_storage *sockaddr)
{
//static const char* ipv6[INET6_ADDRSTRLEN];
assert(sockaddr != NULL);
assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6);
switch (sockaddr->ss_family)
{
case AF_INET:
{
//struct sockaddr_in *s = ((struct sockaddr_in *)sockaddr);
//return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); // TODO.
}
break;
case AF_INET6:
{
//struct sockaddr_in6 *s = ((struct sockaddr_in6 *)sockaddr);
//return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); // TODO.
}
break;
}
return NULL;
}
// Check if the null terminated string ip is a valid IPv4 address
static bool IsIPv4Address(const char *ip)
{
/*
struct sockaddr_in sa;
int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); // TODO.
return (result != 0);
*/
return false;
}
// Check if the null terminated string ip is a valid IPv6 address
static bool IsIPv6Address(const char *ip)
{
/*
struct sockaddr_in6 sa;
int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); // TODO.
return result != 0;
*/
return false;
}
// Return a pointer to the port from the correct address family (IPv4, or IPv6)
static void *GetSocketPortPtr(struct sockaddr_storage *sa)
{
if (sa->ss_family == AF_INET)
{
return &(((struct sockaddr_in *)sa)->sin_port);
}
return &(((struct sockaddr_in6 *)sa)->sin6_port);
}
// Return a pointer to the address from the correct address family (IPv4, or IPv6)
static void *GetSocketAddressPtr(struct sockaddr_storage *sa)
{
if (sa->ss_family == AF_INET)
{
return &(((struct sockaddr_in *)sa)->sin_addr);
}
return &(((struct sockaddr_in6 *)sa)->sin6_addr);
}
// Is the socket in a valid state?
static bool IsSocketValid(Socket *sock)
{
if (sock != NULL)
{
return (sock->channel != INVALID_SOCKET);
}
return false;
}
// Sets the error code that can be retrieved through the WSAGetLastError function.
static void SocketSetLastError(int err)
{
#if defined(_WIN32)
WSASetLastError(err);
#else
errno = err;
#endif
}
// Returns the error status for the last Sockets operation that failed
static int SocketGetLastError(void)
{
#if defined(_WIN32)
return WSAGetLastError();
#else
return errno;
#endif
}
// Returns a human-readable string representing the last error message
static char *SocketGetLastErrorString(void)
{
return SocketErrorCodeToString(SocketGetLastError());
}
// Returns a human-readable string representing the error message (err)
static char *SocketErrorCodeToString(int err)
{
#if defined(_WIN32)
static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE];
TRACELOG(LOG_INFO, gaiStrErrorBuffer, "%s", gai_strerror(err));
return gaiStrErrorBuffer;
#else
return gai_strerror(err);
#endif
}
// Set the defaults in the supplied SocketConfig if they're not already set
static bool SocketSetDefaults(SocketConfig *config)
{
if (config->backlog_size == 0) config->backlog_size = SOCKET_MAX_QUEUE_SIZE;
return true;
}
// Create the socket channel
static bool InitSocket(Socket *sckt, struct addrinfo *address)
{
switch (sckt->type)
{
case SOCKET_TCP:
{
if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_STREAM, 0);
else sckt->channel = socket(AF_INET6, SOCK_STREAM, 0);
} break;
case SOCKET_UDP:
{
if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_DGRAM, 0);
else sckt->channel = socket(AF_INET6, SOCK_DGRAM, 0);
} break;
default: TRACELOG(LOG_WARNING, "Invalid socket type specified."); break;
}
return IsSocketValid(sckt);
}
// CreateSocket() - Interally called by CreateSocket()
//
// This here is the bread and butter of the socket API, This function will
// attempt to open a socket, bind and listen to it based on the config passed in
//
// SocketConfig* config - Configuration for which socket to open
// SocketResult* result - The results of this function (if any, including errors)
//
// e.g.
// SocketConfig server_config = { SocketConfig client_config = {
// .host = "127.0.0.1", .host = "127.0.0.1",
// .port = 8080, .port = 8080,
// .server = true, };
// .nonblocking = true,
// };
// SocketResult server_res; SocketResult client_res;
static bool CreateSocket(SocketConfig *config, SocketResult *outresult)
{
bool success = true;
int addrstatus;
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
struct addrinfo *res; // A pointer to the resulting address list
outresult->socket->channel = INVALID_SOCKET;
outresult->status = RESULT_FAILURE;
// Set the socket type
outresult->socket->type = config->type;
// Set the hints based on information in the config
//
// AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name.
// AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6);
// Note: This causes a subsequent call to bind() to auto-fill the IP address
// of the struct sockaddr with the address of the current host.
//
SocketSetHints(config, &hints);
// Populate address information
addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set
config->port, // e.g. "http" or port number
&hints, // e.g. SOCK_STREAM/SOCK_DGRAM
&res // The struct to populate
);
// Did we succeed?
if (addrstatus != 0)
{
outresult->socket->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status));
SocketSetLastError(0);
TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", config->host, config->port, SocketGetLastErrorString());
return (success = false);
}
else
{
char hoststr[NI_MAXHOST];
char portstr[NI_MAXSERV];
//socklen_t client_len = sizeof(struct sockaddr_storage);
//int rc = getnameinfo((struct sockaddr *)res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr);
}
// Walk the address information linked-list
struct addrinfo *it;
for (it = res; it != NULL; it = it->ai_next)
{
// Initialise the socket
if (!InitSocket(outresult->socket, it))
{
outresult->socket->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status));
SocketSetLastError(0);
continue;
}
// Set socket options
if (!SocketSetOptions(config, outresult->socket))
{
outresult->socket->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status));
SocketSetLastError(0);
freeaddrinfo(res);
return (success = false);
}
}
if (!IsSocketValid(outresult->socket))
{
outresult->socket->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status));
SocketSetLastError(0);
freeaddrinfo(res);
return (success = false);
}
if (success)
{
outresult->status = RESULT_SUCCESS;
outresult->socket->ready = 0;
outresult->socket->status = 0;
if (!(config->type == SOCKET_UDP)) outresult->socket->isServer = config->server;
switch (res->ai_addr->sa_family)
{
case AF_INET:
{
outresult->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*outresult->socket->addripv4));
if (outresult->socket->addripv4 != NULL)
{
memset(outresult->socket->addripv4, 0, sizeof(*outresult->socket->addripv4));
if (outresult->socket->addripv4 != NULL)
{
memcpy(&outresult->socket->addripv4->address, (struct sockaddr_in *)res->ai_addr, sizeof(struct sockaddr_in));
outresult->socket->isIPv6 = false;
char hoststr[NI_MAXHOST];
char portstr[NI_MAXSERV];
socklen_t client_len = sizeof(struct sockaddr_storage);
getnameinfo((struct sockaddr *)&outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr);
}
}
} break;
case AF_INET6:
{
outresult->socket->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(
sizeof(*outresult->socket->addripv6));
if (outresult->socket->addripv6 != NULL)
{
memset(outresult->socket->addripv6, 0,
sizeof(*outresult->socket->addripv6));
if (outresult->socket->addripv6 != NULL)
{
memcpy(&outresult->socket->addripv6->address,
(struct sockaddr_in6 *)res->ai_addr, sizeof(struct sockaddr_in6));
outresult->socket->isIPv6 = true;
char hoststr[NI_MAXHOST];
char portstr[NI_MAXSERV];
socklen_t client_len = sizeof(struct sockaddr_storage);
getnameinfo(
(struct sockaddr *)&outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr);
}
}
} break;
default: break;
}
}
freeaddrinfo(res);
return success;
}
// Set the state of the Socket sock to blocking
static bool SocketSetBlocking(Socket *sock)
{
bool ret = true;
#if defined(_WIN32)
unsigned long mode = 0;
ret = ioctlsocket(sock->channel, FIONBIO, &mode);
#else
const int flags = fcntl(sock->channel, F_GETFL, 0);
if (!(flags & O_NONBLOCK))
{
TRACELOG(LOG_DEBUG, "Socket was already in blocking mode");
return ret;
}
ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK)));
#endif
return ret;
}
// Set the state of the Socket sock to non-blocking
static bool SocketSetNonBlocking(Socket *sock)
{
bool ret = true;
#if defined(_WIN32)
unsigned long mode = 1;
ret = ioctlsocket(sock->channel, FIONBIO, &mode);
#else
const int flags = fcntl(sock->channel, F_GETFL, 0);
if ((flags & O_NONBLOCK))
{
TRACELOG(LOG_DEBUG, "Socket was already in non-blocking mode");
return ret;
}
ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK)));
#endif
return ret;
}
// Set options specified in SocketConfig to Socket sock
static bool SocketSetOptions(SocketConfig *config, Socket *sock)
{
for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++)
{
SocketOpt *opt = &config->sockopts[i];
if (opt->id == 0) break;
if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) return false;
}
return true;
}
// Set "hints" in an addrinfo struct, to be passed to getaddrinfo.
static void SocketSetHints(SocketConfig *config, struct addrinfo *hints)
{
if (config == NULL || hints == NULL) return;
memset(hints, 0, sizeof(*hints));
// Check if the ip supplied in the config is a valid ipv4 ip ipv6 address
if (IsIPv4Address(config->host))
{
hints->ai_family = AF_INET;
hints->ai_flags |= AI_NUMERICHOST;
}
else
{
if (IsIPv6Address(config->host))
{
hints->ai_family = AF_INET6;
hints->ai_flags |= AI_NUMERICHOST;
}
else hints->ai_family = AF_UNSPEC;
}
if (config->type == SOCKET_UDP) hints->ai_socktype = SOCK_DGRAM;
else hints->ai_socktype = SOCK_STREAM;
// Set passive unless UDP client
if (!(config->type == SOCKET_UDP) || config->server) hints->ai_flags = AI_PASSIVE;
}
//----------------------------------------------------------------------------------
// Module implementation
//----------------------------------------------------------------------------------
// Initialise the network (requires for windows platforms only)
bool InitNetworkDevice(void)
{
#if defined(_WIN32)
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
TRACELOG(LOG_WARNING, "WinSock failed to initialise.");
return false;
}
else TRACELOG(LOG_INFO, "WinSock initialised.");
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
TRACELOG(LOG_WARNING, "WinSock failed to initialise.");
WSACleanup();
return false;
}
return true;
#else
return true;
#endif
}
// Cleanup, and close the network
void CloseNetworkDevice(void)
{
#if defined(_WIN32)
WSACleanup();
#endif
}
// Protocol-independent name resolution from an address to an ANSI host name
// and from a port number to the ANSI service name.
//
// The flags parameter can be used to customize processing of the getnameinfo function
//
// The following flags are available:
//
// NAME_INFO_DEFAULT 0x00 // No flags set
// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts
// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address
// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS
// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #)
// NAME_INFO_DGRAM 0x10 // Service is a datagram service
void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv)
{
// Variables
int status; // Status value to return (0) is success
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
struct addrinfo *res; // A pointer to the resulting address list
// Set the hints
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6)
hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
// Populate address information
status = getaddrinfo(ip, // e.g. "www.example.com" or IP
port, // e.g. "http" or port number
&hints, // e.g. SOCK_STREAM/SOCK_DGRAM
&res // The struct to populate
);
// Did we succeed?
if (status != 0) TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno));
else TRACELOG(LOG_DEBUG, "Resolving... %s::%s", ip, port);
// Attempt to resolve network byte order ip to hostname
switch (res->ai_family)
{
case AF_INET:
{
status = getnameinfo(&*((struct sockaddr *)res->ai_addr),
sizeof(*((struct sockaddr_in *)res->ai_addr)),
host, NI_MAXHOST, serv, NI_MAXSERV, flags);
} break;
case AF_INET6:
{
/*
status = getnameinfo(&*((struct sockaddr_in6 *)res->ai_addr), // TODO.
sizeof(*((struct sockaddr_in6 *)res->ai_addr)),
host, NI_MAXHOST, serv, NI_MAXSERV, flags);
*/
} break;
default: break;
}
if (status != 0) TRACELOG(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString());
else TRACELOG(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host);
// Free the pointer to the data returned by addrinfo
freeaddrinfo(res);
}
// Protocol-independent translation from an ANSI host name to an address
//
// e.g.
// const char* address = "127.0.0.1" (local address)
// const char* port = "80"
//
// Parameters:
// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string.
// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string.
//
// Returns:
// The total amount of addresses found, -1 on error
//
int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr)
{
// Variables
int status; // Status value to return (0) is success
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
struct addrinfo *res; // will point to the results
struct addrinfo *iterator;
assert(((address != NULL || address != 0) || (service != NULL || service != 0)));
assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC)));
// Set the hints
memset(&hints, 0, sizeof hints);
hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6)
hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
hints.ai_flags = flags;
assert((hints.ai_addrlen == 0) || (hints.ai_addrlen == 0));
assert((hints.ai_canonname == 0) || (hints.ai_canonname == 0));
assert((hints.ai_addr == 0) || (hints.ai_addr == 0));
assert((hints.ai_next == 0) || (hints.ai_next == 0));
// When the address is NULL, populate the IP for me
if (address == NULL)
{
if ((hints.ai_flags & AI_PASSIVE) == 0) hints.ai_flags |= AI_PASSIVE;
}
TRACELOG(LOG_INFO, "Resolving host...");
// Populate address information
status = getaddrinfo(address, // e.g. "www.example.com" or IP
service, // e.g. "http" or port number
&hints, // e.g. SOCK_STREAM/SOCK_DGRAM
&res // The struct to populate
);
// Did we succeed?
if (status != 0)
{
int error = SocketGetLastError();
SocketSetLastError(0);
TRACELOG(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error));
return -1;
}
else TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", address, service);
// Calculate the size of the address information list
int size = 0;
for (iterator = res; iterator != NULL; iterator = iterator->ai_next) size++;
// Validate the size is > 0, otherwise return
if (size <= 0)
{
TRACELOG(LOG_WARNING, "Error, no addresses found.");
return -1;
}
// If not address list was allocated, allocate it dynamically with the known address size
if (outAddr == NULL) outAddr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation));
// Dynamically allocate an array of address information structs
if (outAddr != NULL)
{
int i;
for (i = 0; i < size; ++i)
{
outAddr[i] = LoadAddress();
if (outAddr[i] == NULL)
{
break;
}
}
outAddr[i] = NULL;
if (i != size) outAddr = NULL;
}
else
{
TRACELOG(LOG_WARNING, "Error, failed to dynamically allocate memory for the address list");
return -1;
}
// Copy all the address information from res into outAddrList
int i = 0;
for (iterator = res; iterator != NULL; iterator = iterator->ai_next)
{
if (i < size)
{
outAddr[i]->addr.ai_flags = iterator->ai_flags;
outAddr[i]->addr.ai_family = iterator->ai_family;
outAddr[i]->addr.ai_socktype = iterator->ai_socktype;
outAddr[i]->addr.ai_protocol = iterator->ai_protocol;
outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen;
*outAddr[i]->addr.ai_addr = *iterator->ai_addr;
#if NET_DEBUG_ENABLED
TRACELOG(LOG_DEBUG, "GetAddressInformation");
TRACELOG(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags);
//PrintSocket(outAddr[i]->addr.ai_addr, outAddr[i]->addr.ai_family, outAddr[i]->addr.ai_socktype, outAddr[i]->addr.ai_protocol);
TRACELOG(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen);
TRACELOG(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname);
#endif
i++;
}
}
// Free the pointer to the data returned by addrinfo
freeaddrinfo(res);
// Return the total count of addresses found
return size;
}
// This here is the bread and butter of the socket API, This function will
// attempt to open a socket, bind and listen to it based on the config passed in
//
// SocketConfig* config - Configuration for which socket to open
// SocketResult* result - The results of this function (if any, including errors)
//
// e.g.
// SocketConfig server_config = { SocketConfig client_config = {
// .host = "127.0.0.1", .host = "127.0.0.1",
// .port = 8080, .port = 8080,
// .server = true, };
// .nonblocking = true,
// };
// SocketResult server_res; SocketResult client_res;
bool SocketCreate(SocketConfig *config, SocketResult *result)
{
// Socket creation result
bool success = true;
// Make sure we've not received a null config or result pointer
if (config == NULL || result == NULL) return (success = false);
// Set the defaults based on the config
if (!SocketSetDefaults(config))
{
TRACELOG(LOG_WARNING, "Configuration Error.");
success = false;
}
else
{
// Create the socket
if (CreateSocket(config, result))
{
if (config->nonblocking) SocketSetNonBlocking(result->socket);
else SocketSetBlocking(result->socket);
}
else success = false;
}
return success;
}
// Bind a socket to a local address
// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function.
bool SocketBind(SocketConfig *config, SocketResult *result)
{
bool success = false;
result->status = RESULT_FAILURE;
struct sockaddr_storage *sock_addr = NULL;
// Don't bind to a socket that isn't configured as a server
if (!IsSocketValid(result->socket) || !config->server)
{
TRACELOG(LOG_WARNING, Cannot bind to socket marked as \"Client\" in SocketConfig.");
success = false;
}
else
{
if (result->socket->isIPv6) sock_addr = (struct sockaddr_storage *)&result->socket->addripv6->address;
else sock_addr = (struct sockaddr_storage *)&result->socket->addripv4->address;
if (sock_addr != NULL)
{
if (bind(result->socket->channel, (struct sockaddr *)sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR)
{
TRACELOG(LOG_INFO, "Successfully bound socket.");
success = true;
}
else
{
result->socket->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
SocketSetLastError(0);
success = false;
}
}
}
// Was the bind a success?
if (success)
{
result->status = RESULT_SUCCESS;
result->socket->ready = 0;
result->socket->status = 0;
socklen_t sock_len = sizeof(*sock_addr);
if (getsockname(result->socket->channel, (struct sockaddr *)sock_addr, &sock_len) < 0)
{
TRACELOG(LOG_WARNING, "Couldn't get socket address");
}
else
{
struct sockaddr_in *s = (struct sockaddr_in *)sock_addr;
// result->socket->address.host = s->sin_addr.s_addr;
// result->socket->address.port = s->sin_port;
result->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*result->socket->addripv4));
if (result->socket->addripv4 != NULL) memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4));
memcpy(&result->socket->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in));
}
}
return success;
}
// Listens (and queues) incoming connections requests for a bound port.
bool SocketListen(SocketConfig *config, SocketResult *result)
{
bool success = false;
result->status = RESULT_FAILURE;
// Don't bind to a socket that isn't configured as a server
if (!IsSocketValid(result->socket) || !config->server)
{
TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"Client\" in SocketConfig.");
success = false;
}
else
{
// Don't listen on UDP sockets
if (!(config->type == SOCKET_UDP))
{
if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR)
{
TRACELOG(LOG_INFO, "Started listening on socket...");
success = true;
}
else
{
success = false;
result->socket->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
SocketSetLastError(0);
}
}
else
{
TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig.");
success = false;
}
}
// Was the listen a success?
if (success)
{
result->status = RESULT_SUCCESS;
result->socket->ready = 0;
result->socket->status = 0;
}
return success;
}
// Connect the socket to the destination specified by "host" and "port" in SocketConfig
bool SocketConnect(SocketConfig *config, SocketResult *result)
{
bool success = true;
result->status = RESULT_FAILURE;
// Only bind to sockets marked as server
if (config->server)
{
TRACELOG(LOG_WARNING, "Cannot connect to socket marked as \"Server\" in SocketConfig.");
success = false;
}
else
{
if (IsIPv4Address(config->host))
{
struct sockaddr_in ip4addr;
ip4addr.sin_family = AF_INET;
unsigned long hport;
hport = strtoul(config->port, NULL, 0);
ip4addr.sin_port = htons(hport);
// TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows).
//inet_pton(AF_INET, config->host, &ip4addr.sin_addr);
int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip4addr, sizeof(ip4addr));
if (connect_result == SOCKET_ERROR)
{
result->socket->status = SocketGetLastError();
SocketSetLastError(0);
switch (result->socket->status)
{
case WSAEWOULDBLOCK: success = true; break;
default:
{
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
success = false;
} break;
}
}
else
{
TRACELOG(LOG_INFO, "Successfully connected to socket.");
success = true;
}
}
else
{
if (IsIPv6Address(config->host))
{
struct sockaddr_in6 ip6addr;
ip6addr.sin6_family = AF_INET6;
unsigned long hport;
hport = strtoul(config->port, NULL, 0);
ip6addr.sin6_port = htons(hport);
//inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO.
int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip6addr, sizeof(ip6addr));
if (connect_result == SOCKET_ERROR)
{
result->socket->status = SocketGetLastError();
SocketSetLastError(0);
switch (result->socket->status)
{
case WSAEWOULDBLOCK: success = true; break;
default:
{
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
success = false;
} break;
}
}
else
{
TRACELOG(LOG_INFO, "Successfully connected to socket.");
success = true;
}
}
}
}
if (success)
{
result->status = RESULT_SUCCESS;
result->socket->ready = 0;
result->socket->status = 0;
}
return success;
}
// Closes an existing socket
//
// SocketChannel socket - The id of the socket to close
void SocketClose(Socket *sock)
{
if (sock != NULL)
{
if (sock->channel != INVALID_SOCKET) closesocket(sock->channel);
}
}
// Returns the sockaddress for a specific socket in a generic storage struct
SocketAddressStorage SocketGetPeerAddress(Socket *sock)
{
// TODO.
/*
if (sock->isServer) return NULL;
if (sock->isIPv6) return sock->addripv6;
else return sock->addripv4;
*/
return NULL;
}
// Return the address-type appropriate host portion of a socket address
char *GetSocketAddressHost(SocketAddressStorage storage)
{
assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6);
return SocketAddressToString((struct sockaddr_storage *)storage);
}
// Return the address-type appropriate port(service) portion of a socket address
short GetSocketAddressPort(SocketAddressStorage storage)
{
//return ntohs(GetSocketPortPtr(storage)); // TODO.
return 0;
}
// The accept function permits an incoming connection attempt on a socket.
//
// SocketChannel listener - The socket to listen for incoming connections on (i.e. server)
// SocketResult* out - The result of this function (if any, including errors)
//
// e.g.
//
// SocketResult connection;
// bool connected = false;
// if (!connected)
// {
// if (SocketAccept(server_res.socket.channel, &connection))
// {
// connected = true;
// }
// }
Socket *SocketAccept(Socket *server, SocketConfig *config)
{
if (!server->isServer || server->type == SOCKET_UDP) return NULL;
struct sockaddr_storage sock_addr;
socklen_t sock_alen;
Socket *sock = LoadSocket();
server->ready = 0;
sock_alen = sizeof(sock_addr);
sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr, &sock_alen);
if (sock->channel == INVALID_SOCKET)
{
sock->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status));
SocketSetLastError(0);
SocketClose(sock);
return NULL;
}
(config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock);
sock->isServer = false;
sock->ready = 0;
sock->type = server->type;
switch (sock_addr.ss_family)
{
case AF_INET:
{
struct sockaddr_in *s = ((struct sockaddr_in *)&sock_addr);
sock->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*sock->addripv4));
if (sock->addripv4 != NULL)
{
memset(sock->addripv4, 0, sizeof(*sock->addripv4));
memcpy(&sock->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in));
TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv4->address.sin_port));
}
} break;
case AF_INET6:
{
struct sockaddr_in6 *s = ((struct sockaddr_in6 *)&sock_addr);
sock->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(sizeof(*sock->addripv6));
if (sock->addripv6 != NULL)
{
memset(sock->addripv6, 0, sizeof(*sock->addripv6));
memcpy(&sock->addripv6->address, (struct sockaddr_in6 *)&s->sin6_addr, sizeof(struct sockaddr_in6));
TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv6->address.sin6_port));
}
} break;
}
return sock;
}
// Verify that the channel is in the valid range
static int ValidChannel(int channel)
{
if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS))
{
TRACELOG(LOG_WARNING, "Invalid channel");
return 0;
}
return 1;
}
// Set the socket channel
int SocketSetChannel(Socket *socket, int channel, const IPAddress *address)
{
struct UDPChannel *binding;
if (socket == NULL)
{
TRACELOG(LOG_WARNING, "Passed a NULL socket");
return (-1);
}
if (channel == -1)
{
for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel)
{
binding = &socket->binding[channel];
if (binding->numbound < SOCKET_MAX_UDPADDRESSES) break;
}
}
else
{
if (!ValidChannel(channel)) return (-1);
binding = &socket->binding[channel];
}
if (binding->numbound == SOCKET_MAX_UDPADDRESSES)
{
TRACELOG(LOG_WARNING, "No room for new addresses");
return (-1);
}
binding->address[binding->numbound++] = *address;
return (channel);
}
// Remove the socket channel
void SocketUnsetChannel(Socket *socket, int channel)
{
if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) socket->binding[channel].numbound = 0;
}
/* Allocate/free a single UDP packet 'size' bytes long.
The new packet is returned, or NULL if the function ran out of memory.
*/
SocketDataPacket *AllocPacket(int size)
{
SocketDataPacket *packet = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet));
int error = 1;
if (packet != NULL)
{
packet->maxlen = size;
packet->data = (uint8_t *)RNET_MALLOC(size);
if (packet->data != NULL)
{
error = 0;
}
}
if (error)
{
FreePacket(packet);
packet = NULL;
}
return (packet);
}
int ResizePacket(SocketDataPacket *packet, int newsize)
{
uint8_t *newdata = (uint8_t *)RNET_MALLOC(newsize);
if (newdata != NULL)
{
RNET_FREE(packet->data);
packet->data = newdata;
packet->maxlen = newsize;
}
return (packet->maxlen);
}
void FreePacket(SocketDataPacket *packet)
{
if (packet)
{
RNET_FREE(packet->data);
RNET_FREE(packet);
}
}
// Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, each 'size' bytes long.
// A pointer to the packet array is returned, or NULL if the function ran out of memory.
SocketDataPacket **AllocPacketList(int howmany, int size)
{
SocketDataPacket **packetV = (SocketDataPacket **)RNET_MALLOC((howmany + 1) * sizeof(*packetV));
if (packetV != NULL)
{
int i;
for (i = 0; i < howmany; ++i)
{
packetV[i] = AllocPacket(size);
if (packetV[i] == NULL)
{
break;
}
}
packetV[i] = NULL;
if (i != howmany)
{
FreePacketList(packetV);
packetV = NULL;
}
}
return (packetV);
}
void FreePacketList(SocketDataPacket **packetV)
{
if (packetV)
{
for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]);
RNET_FREE(packetV);
}
}
// Send 'len' bytes of 'data' over the non-server socket 'sock'
int SocketSend(Socket *sock, const void *datap, int length)
{
int sent = 0;
int left = length;
int status = -1;
int numsent = 0;
const unsigned char *data = (const unsigned char *)datap;
// Server sockets are for accepting connections only
if (sock->isServer)
{
TRACELOG(LOG_WARNING, "Cannot send information on a server socket");
return -1;
}
// Which socket are we trying to send data on
switch (sock->type)
{
case SOCKET_TCP:
{
SocketSetLastError(0);
do
{
length = send(sock->channel, (const char *)data, left, 0);
if (length > 0)
{
sent += length;
left -= length;
data += length;
}
} while ((left > 0) && // While we still have bytes left to send
((length > 0) || // The amount of bytes we actually sent is > 0
(SocketGetLastError() == WSAEINTR)) // The socket was interupted
);
if (length == SOCKET_ERROR)
{
sock->status = SocketGetLastError();
TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status));
SocketSetLastError(0);
}
else TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent);
return sent;
} break;
case SOCKET_UDP:
{
SocketSetLastError(0);
if (sock->isIPv6) status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv6->address, sizeof(sock->addripv6->address));
else status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv4->address, sizeof(sock->addripv4->address));
if (sent >= 0)
{
sock->status = 0;
++numsent;
TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status);
}
else
{
sock->status = SocketGetLastError();
TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status));
SocketSetLastError(0);
return 0;
}
return numsent;
} break;
default: break;
}
return -1;
}
// Receive up to 'maxlen' bytes of data over the non-server socket 'sock',
// and store them in the buffer pointed to by 'data'.
// This function returns the actual amount of data received. If the return
// value is less than or equal to zero, then either the remote connection was
// closed, or an unknown socket error occurred.
int SocketReceive(Socket *sock, void *data, int maxlen)
{
int len = 0;
int numrecv = 0;
int status = 0;
socklen_t sock_len;
struct sockaddr_storage sock_addr;
//char ip[INET6_ADDRSTRLEN];
// Server sockets are for accepting connections only
if (sock->isServer && (sock->type == SOCKET_TCP))
{
sock->status = SocketGetLastError();
TRACELOG(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data");
SocketSetLastError(0);
return 0;
}
// Which socket are we trying to send data on
switch (sock->type)
{
case SOCKET_TCP:
{
SocketSetLastError(0);
do
{
len = recv(sock->channel, (char *)data, maxlen, 0);
} while (SocketGetLastError() == WSAEINTR);
if (len > 0)
{
// Who sent the packet?
if (sock->type == SOCKET_UDP)
{
//TRACELOG(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *)&sock_addr), ip, sizeof(ip)));
}
((unsigned char *)data)[len] = '\0'; // Add null terminating character to the end of the stream
TRACELOG(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len);
}
sock->ready = 0;
return len;
} break;
case SOCKET_UDP:
{
SocketSetLastError(0);
sock_len = sizeof(sock_addr);
status = recvfrom(sock->channel, // The receving channel
data, // A pointer to the data buffer to fill
maxlen, // The max length of the data to fill
0, // Flags
(struct sockaddr *)&sock_addr, // The address of the recevied data
&sock_len // The length of the received data address
);
if (status >= 0) ++numrecv;
else
{
sock->status = SocketGetLastError();
switch (sock->status)
{
case WSAEWOULDBLOCK: break;
default: TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); break;
}
SocketSetLastError(0);
return 0;
}
sock->ready = 0;
return numrecv;
} break;
default: break;
}
return -1;
}
// Does the socket have it's 'ready' flag set?
bool IsSocketReady(Socket *sock)
{
return (sock != NULL) && (sock->ready);
}
// Check if the socket is considered connected
bool IsSocketConnected(Socket *sock)
{
#if defined(_WIN32)
FD_SET writefds;
FD_ZERO(&writefds);
FD_SET(sock->channel, &writefds);
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 1000000000UL;
int total = select(0, NULL, &writefds, NULL, &timeout);
if (total == -1)
{ // Error
sock->status = SocketGetLastError();
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status));
SocketSetLastError(0);
}
else if (total == 0) return false; // Timeout
else if (FD_ISSET(sock->channel, &writefds)) return true;
return false;
#else
return true;
#endif
}
// Allocate and return a SocketResult struct
SocketResult *LoadSocketResult(void)
{
struct SocketResult *res = (struct SocketResult *)RNET_MALLOC(sizeof(*res));
if (res != NULL)
{
memset(res, 0, sizeof(*res));
if ((res->socket = LoadSocket()) == NULL)
{
RNET_FREE(res);
res = NULL;
}
}
return res;
}
// Free an allocated SocketResult
void UnloadSocketResult(SocketResult **result)
{
if (*result != NULL)
{
if ((*result)->socket != NULL) UnloadSocket(&((*result)->socket));
RNET_FREE(*result);
*result = NULL;
}
}
// Allocate a Socket
Socket *LoadSocket(void)
{
struct Socket *sock;
sock = (Socket *)RNET_MALLOC(sizeof(*sock));
if (sock != NULL) memset(sock, 0, sizeof(*sock));
else
{
TRACELOG(LOG_WARNING, "Ran out of memory attempting to allocate a socket");
SocketClose(sock);
RNET_FREE(sock);
sock = NULL;
}
return sock;
}
// Free an allocated Socket
void UnloadSocket(Socket **sock)
{
if (*sock != NULL)
{
RNET_FREE(*sock);
*sock = NULL;
}
}
// Allocate a SocketSet
SocketSet *LoadSocketSet(int max)
{
struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set));
if (set != NULL)
{
set->numsockets = 0;
set->maxsockets = max;
set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets));
if (set->sockets != NULL)
{
for (int i = 0; i < max; ++i) set->sockets[i] = NULL;
}
else
{
RNET_FREE(set);
set = NULL;
}
}
return (set);
}
// Free an allocated SocketSet
void UnloadSocketSet(SocketSet *set)
{
if (set)
{
RNET_FREE(set->sockets);
RNET_FREE(set);
}
}
// Add a Socket "sock" to the SocketSet "set"
int AddSocket(SocketSet *set, Socket *sock)
{
if (sock != NULL)
{
if (set->numsockets == set->maxsockets)
{
TRACELOG(LOG_DEBUG, "Socket Error: %s", "SocketSet is full");
SocketSetLastError(0);
return (-1);
}
set->sockets[set->numsockets++] = (struct Socket *)sock;
}
else
{
TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket was null");
SocketSetLastError(0);
return (-1);
}
return (set->numsockets);
}
// Remove a Socket "sock" to the SocketSet "set"
int RemoveSocket(SocketSet *set, Socket *sock)
{
if (sock != NULL)
{
int i = 0;
for (i = 0; i < set->numsockets; ++i)
{
if (set->sockets[i] == (struct Socket *)sock) break;
}
if (i == set->numsockets)
{
TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket not found");
SocketSetLastError(0);
return (-1);
}
--set->numsockets;
for (; i < set->numsockets; ++i) set->sockets[i] = set->sockets[i + 1];
}
return (set->numsockets);
}
// Check the sockets in the socket set for pending information
int CheckSockets(SocketSet *set, unsigned int timeout)
{
int i;
SOCKET maxfd;
int retval;
struct timeval tv;
fd_set mask;
/* Find the largest file descriptor */
maxfd = 0;
for (i = set->numsockets - 1; i >= 0; --i)
{
if (set->sockets[i]->channel > maxfd)
{
maxfd = set->sockets[i]->channel;
}
}
// Check the file descriptors for available data
do
{
SocketSetLastError(0);
// Set up the mask of file descriptors
FD_ZERO(&mask);
for (i = set->numsockets - 1; i >= 0; --i)
{
FD_SET(set->sockets[i]->channel, &mask);
} // Set up the timeout
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
/* Look! */
retval = select(maxfd + 1, &mask, NULL, NULL, &tv);
} while (SocketGetLastError() == WSAEINTR);
// Mark all file descriptors ready that have data available
if (retval > 0)
{
for (i = set->numsockets - 1; i >= 0; --i)
{
if (FD_ISSET(set->sockets[i]->channel, &mask)) set->sockets[i]->ready = 1;
}
}
return retval;
}
// Allocate an AddressInformation
AddressInformation LoadAddress(void)
{
AddressInformation addressInfo = NULL;
addressInfo = (AddressInformation)RNET_CALLOC(1, sizeof(*addressInfo));
if (addressInfo != NULL)
{
addressInfo->addr.ai_addr = (struct sockaddr *)RNET_CALLOC(1, sizeof(struct sockaddr));
if (addressInfo->addr.ai_addr == NULL) TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct sockaddr\"");
}
else TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct AddressInformation\"");
return addressInfo;
}
// Free an AddressInformation struct
void UnloadAddress(AddressInformation *addressInfo)
{
if (*addressInfo != NULL)
{
if ((*addressInfo)->addr.ai_addr != NULL)
{
RNET_FREE((*addressInfo)->addr.ai_addr);
(*addressInfo)->addr.ai_addr = NULL;
}
RNET_FREE(*addressInfo);
*addressInfo = NULL;
}
}
// Allocate a list of AddressInformation
AddressInformation *LoadAddressList(int size)
{
AddressInformation *addr;
addr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation));
return addr;
}
// Opaque datatype accessor addrinfo->ai_family
int GetAddressFamily(AddressInformation address)
{
return address->addr.ai_family;
}
// Opaque datatype accessor addrinfo->ai_socktype
int GetAddressSocketType(AddressInformation address)
{
return address->addr.ai_socktype;
}
// Opaque datatype accessor addrinfo->ai_protocol
int GetAddressProtocol(AddressInformation address)
{
return address->addr.ai_protocol;
}
// Opaque datatype accessor addrinfo->ai_canonname
char *GetAddressCanonName(AddressInformation address)
{
return address->addr.ai_canonname;
}
// Opaque datatype accessor addrinfo->ai_addr
char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport)
{
//char *ip[INET6_ADDRSTRLEN];
char *result = NULL;
struct sockaddr_storage *storage = (struct sockaddr_storage *)address->addr.ai_addr;
switch (storage->ss_family)
{
case AF_INET:
{
struct sockaddr_in *s = ((struct sockaddr_in *)address->addr.ai_addr);
//result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO.
*outport = ntohs(s->sin_port);
} break;
case AF_INET6:
{
struct sockaddr_in6 *s = ((struct sockaddr_in6 *)address->addr.ai_addr);
//result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO.
*outport = ntohs(s->sin6_port);
} break;
default: break;
}
if (result == NULL)
{
TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError()));
SocketSetLastError(0);
}
else
{
strcpy(outhost, result);
}
return result;
}
//
void PacketSend(Packet *packet)
{
TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size);
}
//
void PacketReceive(Packet *packet)
{
TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size);
}
//
void PacketWrite16(Packet *packet, uint16_t value)
{
TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value);
uint8_t *data = packet->data + packet->offs;
*data++ = (uint8_t)(value >> 8);
*data++ = (uint8_t)(value);
packet->size += sizeof(uint16_t);
packet->offs += sizeof(uint16_t);
TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data);
}
//
void PacketWrite32(Packet *packet, uint32_t value)
{
TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value);
uint8_t *data = packet->data + packet->offs;
*data++ = (uint8_t)(value >> 24);
*data++ = (uint8_t)(value >> 16);
*data++ = (uint8_t)(value >> 8);
*data++ = (uint8_t)(value);
packet->size += sizeof(uint32_t);
packet->offs += sizeof(uint32_t);
TRACELOG(LOG_INFO, "Network: 0x%08" PRIX32 " - %" PRIu32 "\n",
(uint32_t)(((intptr_t) packet->data) - packet->offs),
(uint32_t)(((intptr_t) packet->data) - packet->offs));
}
//
void PacketWrite64(Packet *packet, uint64_t value)
{
TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value);
uint8_t *data = packet->data + packet->offs;
*data++ = (uint8_t)(value >> 56);
*data++ = (uint8_t)(value >> 48);
*data++ = (uint8_t)(value >> 40);
*data++ = (uint8_t)(value >> 32);
*data++ = (uint8_t)(value >> 24);
*data++ = (uint8_t)(value >> 16);
*data++ = (uint8_t)(value >> 8);
*data++ = (uint8_t)(value);
packet->size += sizeof(uint64_t);
packet->offs += sizeof(uint64_t);
TRACELOG(LOG_INFO, "Network: 0x%016" PRIX64 " - %" PRIu64 "\n", (uint64_t)(packet->data - packet->offs), (uint64_t)(packet->data - packet->offs));
}
//
uint16_t PacketRead16(Packet *packet)
{
uint8_t *data = packet->data + packet->offs;
packet->size += sizeof(uint16_t);
packet->offs += sizeof(uint16_t);
uint16_t value = ((uint16_t) data[0] << 8) | data[1];
TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value);
return value;
}
//
uint32_t PacketRead32(Packet *packet)
{
uint8_t *data = packet->data + packet->offs;
packet->size += sizeof(uint32_t);
packet->offs += sizeof(uint32_t);
uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3];
TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value);
return value;
}
//
uint64_t PacketRead64(Packet *packet)
{
uint8_t *data = packet->data + packet->offs;
packet->size += sizeof(uint64_t);
packet->offs += sizeof(uint64_t);
uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7];
TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value);
return value;
}
#endif // RNET_IMPLEMENTATION