|
@@ -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
|
|
#endif // RNET_IMPLEMENTATION
|