From bde0b6cd754c96e61e95c2224cb9d911345200f2 Mon Sep 17 00:00:00 2001 From: Jak Barnes Date: Sat, 23 Feb 2019 13:51:20 +0000 Subject: [PATCH] Comments and examples --- examples/network/network_resolve_host.c | 6 +- src/raylib.h | 58 +- src/rnet.c | 1140 ++++++++++++----------- 3 files changed, 646 insertions(+), 558 deletions(-) diff --git a/examples/network/network_resolve_host.c b/examples/network/network_resolve_host.c index e580745b0..6ad77c591 100644 --- a/examples/network/network_resolve_host.c +++ b/examples/network/network_resolve_host.c @@ -37,9 +37,9 @@ int main() InitNetwork(); AddressInformation addr; - ResolveHost(&addr, "www.google.com", NULL, SOCKET_TCP); - ResolveIP("8.8.8.8", NULL); - ResolveIP("2001:4860:4860::8888", "80"); + ResolveHost("www.google.com", "80", &addr); + // ResolveIP("8.8.8.8", NULL, NAME_INFO_DEFAULT); + // ResolveIP("2001:4860:4860::8888", "80", NAME_INFO_NUMERICSERV); // Main game loop while (!WindowShouldClose()) diff --git a/src/raylib.h b/src/raylib.h index 76061b8ae..df7073fc2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -100,13 +100,19 @@ // Network limits #define MAX_SOCKET_SET_SIZE 32 #define MAX_SOCKET_QUEUE_SIZE 16 -#define MAX_HOST_NAME_SIZE 1025 -#define MAX_SERV_NAME_SIZE 32 -#define MAX_IPV4_NAME_SIZE 22 -#define MAX_IPV6_NAME_SIZE 65 - #define MAX_SOCK_OPTS 4 +// getnameinfo() 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 */ + + + + // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. #if defined(__cplusplus) @@ -437,6 +443,7 @@ typedef struct IPAddress unsigned char* port; /* 16-bit protocol port */ } IPAddress; +// Used by the getaddrinfo function to hold host address information. typedef struct AddressInformation { int flags; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST @@ -449,31 +456,35 @@ typedef struct AddressInformation struct AddressInformation *next; // Next structure in linked list } AddressInformation; +// Used +// +// The sockaddr structure varies depending on the protocol selected. +// Except for the sin*_family parameter, sockaddr contents are expressed +// in network byte order. typedef struct SocketAddress { unsigned short family; // Address family. char data[14]; // Up to 14 bytes of direct address. } SocketAddress; -/* An option ID, value, sizeof(value) tuple for setsockopt(2). */ +// An option ID, value, sizeof(value) tuple for setsockopt(2). typedef struct SocketOpt { - int option_id; + int id; void *value; - int value_len; + int valueLen; } SocketOpt; typedef struct Socket -{ - int ready; - SocketHandle handle; - IPAddress remoteAddress; - IPAddress localAddress; - int sflag; +{ + int ready; // Is the socket ready? i.e. has information + SocketHandle handle; // The socket handle id + IPAddress host; // The host/target ip for this socket + int sflag; // Is this socket a server socket (i.e. TCP/UDP Listen Server) } Socket; -/* Configuration for a socket. Not all of these fields need to - * be set, and ones omitted from a C99-style "designated initializer" - * struct literal will be zeroed out and replaced with defaults. */ +// Configuration for a socket. Not all of these fields need to +// be set, and ones omitted from a C99-style "designated initializer" +// struct literal will be zeroed out and replaced with defaults. typedef struct SocketConfig { /* Hostname and port, for TCP or UDP sockets. */ char *host; @@ -488,7 +499,7 @@ typedef struct SocketConfig { char *IPv6; bool server; /* Listen for incoming clients? */ - bool datagram; /* UDP or datagram Unix domain? */ + bool datagram; /* UDP or datagram? */ bool nonblocking; /* non-blocking operation? */ int backlog_size; /* set a custom backlog size */ @@ -1510,12 +1521,15 @@ RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pit // Network (Module: network) //------------------------------------------------------------------------------------ +// Initialisation and cleanup RLAPI bool InitNetwork(void); RLAPI void CloseNetwork(void); -// Resolution -RLAPI void ResolveHost(AddressInformation *outaddr, const char *address, const char *port, SocketType socketType); -RLAPI char *ResolveIP(const char *host, const char *port); +// Protocol-independent name resolution from an address to an ANSI host name and from a port number to the ANSI service name. +RLAPI char* ResolveIP(const char *host, const char *port, int flags); + +// Protocol-independent translation from an ANSI host name to an address. +RLAPI char* ResolveHost(const char *address, const char *port, AddressInformation *outaddr); // IP RLAPI void GetLocalAddresses(); @@ -1530,7 +1544,7 @@ RLAPI int SocketGetError(char *buf, int buf_size, SocketResult *res); RLAPI void SocketPrintError(SocketResult *res); RLAPI void SocketSetHints(SocketConfig *cfg, AddressInformation *hints); -// Print methods +// Utility print methods RLAPI char *SocketAddressToString(SocketAddress *sockaddr, char buffer[]); RLAPI void PrintSocket(SocketAddress *addr, const int family, const int socktype, const int protocol); diff --git a/src/rnet.c b/src/rnet.c index e33b528a1..7cfd776aa 100644 --- a/src/rnet.c +++ b/src/rnet.c @@ -57,277 +57,324 @@ #if PLATFORM_WINDOWS # define errno WSAGetLastError() // Support UNIX socket error codes -#endif - -#define DEF_BACKLOG_SIZE SOMAXCONN -#define PORT_STR_BUFSZ 6 - -static bool SocketSetDefaults(SocketConfig *cfg); -static bool CreateSocket(SocketConfig *cfg, SocketResult *out); -static bool SocketSetNonBlocking(SocketResult *out); -static bool SocketSetOptions(SocketConfig *cfg, - SocketResult *out, int fd); -static const char *SocketStatusToString(enum SocketStatus s); - -/* Static network API methods */ - -static void SocketSetError(int err) -{ -#if PLATFORM == PLATFORM_WINDOWS - WSASetLastError(err); -#else - errno = err; -#endif -} - -static int SocketGetLastError() -{ -#if PLATFORM == PLATFORM_WINDOWS - return WSAGetLastError(); -#else - return errno; -#endif -} - -static bool SocketSetDefaults(SocketConfig *cfg) -{ - if (cfg->backlog_size == 0) { cfg->backlog_size = DEF_BACKLOG_SIZE; } - - /* Screen out contradictory settings */ - if (cfg->IPv6 && cfg->IPv4) { return false; } - return true; -} - -static bool SocketStatusToError(SocketResult *out, enum SocketStatus status) -{ - out->status = status; - out->saved_errno = SocketGetLastError(); - SocketSetError(0); - return false; -} - -static bool CreateSocket(SocketConfig *cfg, SocketResult *out) -{ - struct addrinfo hints; - struct addrinfo *res = NULL; - - int fd = -1; - char port_str[PORT_STR_BUFSZ]; - memset(port_str, 0, PORT_STR_BUFSZ); - - SocketSetHints(cfg, &hints); - - if (PORT_STR_BUFSZ < snprintf(port_str, PORT_STR_BUFSZ, - "%u", cfg->port)) - { - return SocketStatusToError(out, SOCKET_ERROR_SNPRINTF); - } - - struct addrinfo *ai = NULL; - int addr_res = getaddrinfo(cfg->host, port_str, &hints, &res); - if (addr_res != 0) - { - out->getaddrinfo_error = addr_res; - freeaddrinfo(res); - return SocketStatusToError(out, SOCKET_ERROR_GETADDRINFO); - } - memcpy(&out->addrinfo, res, sizeof(struct addrinfo)); - - for (ai = res; ai != NULL; ai = ai->ai_next) - { - fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (fd == -1) - { - /* Save errno, but will be clobbered if others succeed. */ - out->status = SOCKET_ERROR_SOCKET; - out->saved_errno = SocketGetLastError(); - SocketSetError(0); - continue; - } - - if (!SocketSetOptions(cfg, out, fd)) - { - freeaddrinfo(res); - return false; - } - - if (cfg->server) - { - int bind_res = bind(fd, res->ai_addr, res->ai_addrlen); - if (bind_res == -1) - { - freeaddrinfo(res); - return SocketStatusToError(out, SOCKET_ERROR_BIND); - } - - if (!cfg->datagram) - { - int listen_res = listen(fd, cfg->backlog_size); - if (listen_res == -1) - { - freeaddrinfo(res); - return SocketStatusToError(out, SOCKET_ERROR_LISTEN); - } - } - break; - } - else /* client */ - { - if (cfg->datagram) { break; } - - int connect_res = connect(fd, ai->ai_addr, ai->ai_addrlen); - if (connect_res == 0) - { - break; - } - else - { - close(fd); - fd = -1; - out->status = SOCKET_ERROR_CONNECT; - continue; - } - } - } - - if (fd == -1) - { - if (out->status == SOCKET_OK) - { - freeaddrinfo(res); - return SocketStatusToError(out, SOCKET_ERROR_UNKNOWN); - } - else - { - out->saved_errno = SocketGetLastError(); - SocketSetError(0); - freeaddrinfo(res); - return false; - } - } - - out->status = SOCKET_OK; - freeaddrinfo(res); - out->saved_errno = 0; - out->socket.handle = fd; - out->socket.ready = 0; - out->socket.remoteAddress.host = ((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr; - out->socket.remoteAddress.port = ((struct sockaddr_in*)res->ai_addr)->sin_port; - out->socket.sflag = cfg->server; - return true; -} - -static bool SocketSetNonBlocking(SocketResult *out) -{ -#if PLATFORM == PLATFORM_WINDOWS - unsigned long mode = 1; - if (ioctlsocket(out->socket.handle, FIONBIO, &mode) != 0) - { - return SocketStatusToError(out, SOCKET_ERROR_FCNTL); - } -#else - int flags = fcntl(out->socket.handle, F_GETFL, 0); - if (flags == -1) - { - return SocketStatusToError(out, SOCKET_ERROR_FCNTL); - } - if (fcntl(out->socket, F_SETFL, flags | O_NONBLOCK) < 0) - { - return SocketStatusToError(out, SOCKET_ERROR_FCNTL); - } -#endif - return true; -} - -static bool SocketSetOptions(SocketConfig *cfg, - SocketResult *out, int fd) -{ - for (int i = 0; i < MAX_SOCK_OPTS; i++) - { - SocketOpt *opt = &cfg->sockopts[i]; - if (opt->option_id == 0) { break; } - - if (setsockopt(fd, SOL_SOCKET, opt->option_id, - opt->value, opt->value_len) < 0) - { - return SocketStatusToError(out, SOCKET_ERROR_SETSOCKOPT); - } - } - - return true; -} - -static const char *SocketStatusToString(enum SocketStatus s) -{ - switch (s) - { - case SOCKET_OK: - return "ok"; - case SOCKET_ERROR_GETADDRINFO: - return "getaddrinfo"; - case SOCKET_ERROR_SOCKET: - return "socket"; - case SOCKET_ERROR_BIND: - return "bind"; - case SOCKET_ERROR_LISTEN: - return "listen"; - case SOCKET_ERROR_CONNECT: - return "connect"; - case SOCKET_ERROR_FCNTL: - return "fcntl"; - case SOCKET_ERROR_SNPRINTF: - return "snprintf"; - case SOCKET_ERROR_CONFIGURATION: - return "configuration"; - case SOCKET_ERROR_SETSOCKOPT: - return "setsockopt"; - case SOCKET_ERROR_UNKNOWN: - default: - return "unknown"; - } +#endif + +#define DEF_BACKLOG_SIZE SOMAXCONN +#define PORT_STR_BUFSZ 6 + +static bool SocketSetDefaults(SocketConfig* cfg); +static bool CreateSocket(SocketConfig* cfg, SocketResult* out); +static bool SocketSetNonBlocking(SocketResult* out); +static bool SocketSetOptions(SocketConfig* cfg, SocketResult* out, int fd); +static const char* SocketStatusToString(enum SocketStatus s); + +/* Static network API methods */ + +// Sets the error code that can be retrieved through the WSAGetLastError function. +static void SocketSetError(int err) +{ +#if PLATFORM == PLATFORM_WINDOWS + WSASetLastError(err); +#else + errno = err; +#endif +} + +// Returns the error status for the last Sockets operation that failed +static int SocketGetLastError() +{ +#if PLATFORM == PLATFORM_WINDOWS + return WSAGetLastError(); +#else + return errno; +#endif +} + +// Returns a human-readable string representing the last error message +static char* SocketGetLastErrorString() +{ + return gai_strerror(SocketGetLastError()); +} + +static bool SocketSetDefaults(SocketConfig* cfg) +{ + if (cfg->backlog_size == 0) + { + cfg->backlog_size = DEF_BACKLOG_SIZE; + } + + /* Screen out contradictory settings */ + if (cfg->IPv6 && cfg->IPv4) + { + return false; + } + return true; +} + +static bool SocketSaveError(SocketResult* out, enum SocketStatus status) +{ + out->status = status; + out->saved_errno = SocketGetLastError(); + SocketSetError(0); + return false; +} + +static bool CreateSocket(SocketConfig* cfg, SocketResult* out) +{ + struct addrinfo hints; + struct addrinfo* res = NULL; + + int fd = -1; + char port_str[PORT_STR_BUFSZ]; + memset(port_str, 0, PORT_STR_BUFSZ); + + SocketSetHints(cfg, &hints); + + if (PORT_STR_BUFSZ < snprintf(port_str, PORT_STR_BUFSZ, "%u", cfg->port)) + { + return SocketSaveError(out, SOCKET_ERROR_SNPRINTF); + } + + struct addrinfo* ai = NULL; + int addr_res = getaddrinfo(cfg->host, port_str, &hints, &res); + if (addr_res != 0) + { + out->getaddrinfo_error = addr_res; + freeaddrinfo(res); + return SocketSaveError(out, SOCKET_ERROR_GETADDRINFO); + } + memcpy(&out->addrinfo, res, sizeof(struct addrinfo)); + + for (ai = res; ai != NULL; ai = ai->ai_next) + { + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd == -1) + { + /* Save errno, but will be clobbered if others succeed. */ + out->status = SOCKET_ERROR_SOCKET; + out->saved_errno = SocketGetLastError(); + SocketSetError(0); + continue; + } + + if (!SocketSetOptions(cfg, out, fd)) + { + freeaddrinfo(res); + return false; + } + + if (cfg->server) + { + int bind_res = bind(fd, res->ai_addr, res->ai_addrlen); + if (bind_res == -1) + { + freeaddrinfo(res); + return SocketSaveError(out, SOCKET_ERROR_BIND); + } + + if (!cfg->datagram) + { + int listen_res = listen(fd, cfg->backlog_size); + if (listen_res == -1) + { + freeaddrinfo(res); + return SocketSaveError(out, SOCKET_ERROR_LISTEN); + } + } + break; + } + else /* client */ + { + if (cfg->datagram) + { + break; + } + + int connect_res = connect(fd, ai->ai_addr, ai->ai_addrlen); + if (connect_res == 0) + { + break; + } + else + { + close(fd); + fd = -1; + out->status = SOCKET_ERROR_CONNECT; + continue; + } + } + } + + if (fd == -1) + { + if (out->status == SOCKET_OK) + { + freeaddrinfo(res); + return SocketSaveError(out, SOCKET_ERROR_UNKNOWN); + } + else + { + out->saved_errno = SocketGetLastError(); + SocketSetError(0); + freeaddrinfo(res); + return false; + } + } + + out->status = SOCKET_OK; + freeaddrinfo(res); + out->saved_errno = 0; + out->socket.handle = fd; + out->socket.ready = 0; + out->socket.host.host = ((struct sockaddr_in*) res->ai_addr)->sin_addr.s_addr; + out->socket.host.port = ((struct sockaddr_in*) res->ai_addr)->sin_port; + out->socket.sflag = cfg->server; + return true; +} + +static bool SocketSetNonBlocking(SocketResult* out) +{ +#if PLATFORM == PLATFORM_WINDOWS + unsigned long mode = 1; + if (ioctlsocket(out->socket.handle, FIONBIO, &mode) != 0) + { + return SocketSaveError(out, SOCKET_ERROR_FCNTL); + } +#else + int flags = fcntl(out->socket.handle, F_GETFL, 0); + if (flags == -1) + { + return SocketSaveError(out, SOCKET_ERROR_FCNTL); + } + if (fcntl(out->socket, F_SETFL, flags | O_NONBLOCK) < 0) + { + return SocketSaveError(out, SOCKET_ERROR_FCNTL); + } +#endif + return true; +} + +static bool SocketSetOptions(SocketConfig* cfg, SocketResult* out, int fd) +{ + for (int i = 0; i < MAX_SOCK_OPTS; i++) + { + SocketOpt* opt = &cfg->sockopts[i]; + if (opt->id == 0) + { + break; + } + + if (setsockopt(fd, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) + { + return SocketSaveError(out, SOCKET_ERROR_SETSOCKOPT); + } + } + + return true; +} + +static const char* SocketStatusToString(enum SocketStatus s) +{ + switch (s) + { + case SOCKET_OK: + return "ok"; + case SOCKET_ERROR_GETADDRINFO: + return "getaddrinfo"; + case SOCKET_ERROR_SOCKET: + return "socket"; + case SOCKET_ERROR_BIND: + return "bind"; + case SOCKET_ERROR_LISTEN: + return "listen"; + case SOCKET_ERROR_CONNECT: + return "connect"; + case SOCKET_ERROR_FCNTL: + return "fcntl"; + case SOCKET_ERROR_SNPRINTF: + return "snprintf"; + case SOCKET_ERROR_CONFIGURATION: + return "configuration"; + case SOCKET_ERROR_SETSOCKOPT: + return "setsockopt"; + case SOCKET_ERROR_UNKNOWN: + default: + return "unknown"; + } } //---------------------------------------------------------------------------------- // Module implementation //---------------------------------------------------------------------------------- -// Initialise the network (requires for windows platforms only) +// Initialise the network (requires for windows platforms only) bool InitNetwork() { +#if PLATFORM == PLATFORM_WINDOWS + WORD wVersionRequested; WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR) - { - TraceLog(LOG_INFO, "WinSock initialised."); - return true; - } - else + int err; + + wVersionRequested = MAKEWORD(2, 2); + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { TraceLog(LOG_WARNING, "WinSock failed to initialise."); return false; } -} - -// Cleanup, and close the network -void CloseNetwork() -{ - if (WSACleanup() == SOCKET_ERROR) - { - if (WSAGetLastError() == WSAEINPROGRESS) - { - WSACancelBlockingCall(); - WSACleanup(); - } + 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 } -// Resolve the hostname -char* ResolveIP(const char* ip, const char* port) +// Cleanup, and close the network +void CloseNetwork() +{ +#if PLATFORM == PLATFORM_WINDOWS + if (WSACleanup() == SOCKET_ERROR) + { + if (WSAGetLastError() == WSAEINPROGRESS) + { + WSACancelBlockingCall(); + 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 */ +char* ResolveIP(const char* ip, const char* port, int flags) { // Variables - char host[MAX_HOST_NAME_SIZE]; + char host[NI_MAXHOST]; char service[NI_MAXSERV]; - int status; // Status value to return (0) is success - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) struct addrinfo* results; // A pointer to the resulting address list // Zero out the host buffer @@ -337,13 +384,12 @@ char* ResolveIP(const char* ip, const char* port) // Set the hints memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) - hints.ai_socktype = SOCKET_TCP; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM) 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 + status = getaddrinfo(ip, // e.g. "www.example.com" or IP + port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM &results // The struct to populate ); @@ -361,22 +407,22 @@ char* ResolveIP(const char* ip, const char* port) switch (results->ai_family) { case AF_INET: - status = getnameinfo(&*((struct sockaddr_in*) results->ai_addr), + status = getnameinfo(&*((struct sockaddr*) results->ai_addr), sizeof(*((struct sockaddr_in*) results->ai_addr)), host, sizeof(host), - NULL, - NULL, - 0); + service, + NI_MAXSERV, + flags); break; case AF_INET6: status = getnameinfo(&*((struct sockaddr_in6*) results->ai_addr), sizeof(*((struct sockaddr_in6*) results->ai_addr)), host, sizeof(host), - NULL, - NULL, - 0); + service, + NI_MAXSERV, + flags); break; default: break; @@ -385,7 +431,7 @@ char* ResolveIP(const char* ip, const char* port) // Did we succeed? if (status != 0) { - TraceLog(LOG_WARNING, "Failed to resolve ip %s: %ls", ip, gai_strerror(errno)); + TraceLog(LOG_WARNING, "Failed to resolve ip %s: %ls", ip, SocketGetLastErrorString()); } else { @@ -399,18 +445,22 @@ char* ResolveIP(const char* ip, const char* port) return host; } -// Get address information -void ResolveHost(AddressInformation* outaddr, const char* address, const char* port, SocketType socketType) +// 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" +// AddressInformation* outaddr - Results of host resolution (getaddrinfo) +char* ResolveHost(const char* address, const char* port, AddressInformation* outaddr) { // Variables - int status; // Status value to return (0) is success - struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) + int status; // Status value to return (0) is success + struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) struct addrinfo* results; // 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_socktype = socketType == SOCKET_TCP ? SOCK_STREAM : SOCK_DGRAM; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM) hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) // When the address is NULL, populate the IP for me @@ -421,8 +471,8 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p // Populate address information status = getaddrinfo(address, // e.g. "www.example.com" or IP - port, // e.g. "http" or port number - &hints, // e.g. SOCK_STREAM/SOCK_DGRAM + port, // e.g. "http" or port number + &hints, // e.g. SOCK_STREAM/SOCK_DGRAM &results // The struct to populate ); @@ -442,7 +492,7 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p for (iterator = outaddr; iterator != NULL; iterator = iterator->ai_next) { TraceLog(LOG_DEBUG, "GetAddressInformation"); - TraceLog(LOG_DEBUG, "Flags: 0x%x", iterator->ai_flags); + TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); PrintSocket(iterator->ai_addr, iterator->ai_family, iterator->ai_socktype, @@ -451,202 +501,226 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); } + // Free the pointer to the data returned by addrinfo freeaddrinfo(results); + + // Return the resulting hostname + return SocketAddressToString(outaddr->ai_addr); } - -/* Attempt to open a socket, according to the configuration stored in - * CFG. Returns whether the the socket opened; further details will be - * stored in RES. */ -bool SocketOpen(SocketConfig *cfg, SocketResult *res) -{ - if (cfg == NULL || res == NULL) { return false; } - memset(res, 0, sizeof(*res)); - - if (!SocketSetDefaults(cfg)) - { - res->status = SOCKET_ERROR_CONFIGURATION; - return false; - } - - if (!CreateSocket(cfg, res)) { return false; } - - if (cfg->nonblocking) - { - if (!SocketSetNonBlocking(res)) { return false; } - } - - return true; -} - -/* Close a network socket */ -void SocketClose(SocketHandle socket) -{ - if (socket) - { - closesocket(socket); - } -} - -/* Accept an incoming connection on the given server socket. - The newly created socket is returned, or NULL if there was an error. -*/ -bool SocketAccept(SocketHandle listener, SocketResult* out) -{ - struct sockaddr_in sock_addr; - socklen_t sock_alen; - sock_alen = sizeof(sock_addr); - out->socket.handle = accept(listener, (struct sockaddr *)&sock_addr, - &sock_alen); - if (out->socket.handle == INVALID_SOCKET) - { - /* Save errno, but will be clobbered if others succeed. */ - out->status = SOCKET_ERROR_ACCEPT; - out->saved_errno = SocketGetLastError(); - SocketSetError(0); - return false; - } - memcpy(&out->addrinfo, &sock_addr, sizeof(struct sockaddr)); - out->socket.remoteAddress.host = sock_addr.sin_addr.s_addr; - out->socket.remoteAddress.port = sock_addr.sin_port; - - return true; -} - -/* Send 'len' bytes of 'data' over the non-server socket 'sock' - This function returns the actual amount of data sent. If the return value - is less than the amount of data sent, then either the remote connection was - closed, or an unknown socket error occurred. -*/ -int SocketSend(Socket* socket, const void *datap, int len) -{ - const unsigned char *data = (const unsigned char *)datap; /* For pointer arithmetic */ - int sent, left; - - // /* Server sockets are for accepting connections only */ - if (socket->sflag) - { - // out->status = SOCKET_ERROR_SEND; - // out->saved_errno = SocketGetLastError(); - // SocketSetError(0); - return(-1); - } - - /* Keep sending data until it's sent or an error occurs */ - left = len; - sent = 0; - SocketSetError(0); - do - { - len = send(socket->handle, (const char *)data, left, 0); - if (len > 0) - { - sent += len; - left -= len; - data += len; - } - } - while ((left > 0) && ((len > 0) || (SocketGetLastError() == EINTR))); - - return(sent); + +// 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_cfg = { SocketConfig client_cfg = { +// .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 SocketOpen(SocketConfig* config, SocketResult* result) +{ + if (config == NULL || result == NULL) + { + return false; + } + memset(result, 0, sizeof(*result)); + + if (!SocketSetDefaults(config)) + { + result->status = SOCKET_ERROR_CONFIGURATION; + return false; + } + + if (!CreateSocket(config, result)) + { + return false; + } + + if (config->nonblocking) + { + if (!SocketSetNonBlocking(result)) + { + return false; + } + } + + return true; } - -/* 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. + +// Closes an existing socket +// +// SocketHandle socket - The id of the socket to close +void SocketClose(SocketHandle socket) +{ + if (socket) + { + closesocket(socket); + } +} + +// The accept function permits an incoming connection attempt on a socket. +// +bool SocketAccept(SocketHandle listener, SocketResult* out) +{ + struct sockaddr_in sock_addr; + socklen_t sock_alen; + sock_alen = sizeof(sock_addr); + out->socket.handle = accept(listener, (struct sockaddr*) &sock_addr, &sock_alen); + if (out->socket.handle == INVALID_SOCKET) + { + /* Save errno, but will be clobbered if others succeed. */ + out->status = SOCKET_ERROR_ACCEPT; + out->saved_errno = SocketGetLastError(); + SocketSetError(0); + return false; + } + memcpy(&out->addrinfo, &sock_addr, sizeof(struct sockaddr)); + out->socket.host.host = sock_addr.sin_addr.s_addr; + out->socket.host.port = sock_addr.sin_port; + + return true; +} + +/* Send 'len' bytes of 'data' over the non-server socket 'sock' + This function returns the actual amount of data sent. If the return value + is less than the amount of data sent, then either the remote connection was + closed, or an unknown socket error occurred. */ -int SocketReceive(Socket* socket, void *data, int maxlen) -{ - int len; - - /* Server sockets are for accepting connections only */ - if (socket->sflag) - { - // out->status = SOCKET_ERROR_RECEIVE; - // out->saved_errno = SocketGetLastError(); - // SocketSetError(0); - return(-1); - } - - SocketSetError(0); - do - { - len = recv(socket->handle, (char *)data, maxlen, 0); - } - while (SocketGetLastError() == EINTR); - - // sock->ready = 0; - return(len); -} - -/* Construct an error message in BUF, based on the status codes - * in *RES. This has the same return value and general behavior - * as snprintf -- if the return value is >= buf_size, the string - * has been truncated. Returns -1 if either BUF or RES are NULL. */ -int SocketGetError(char *buf, size_t buf_size, SocketResult *res) -{ - if (buf == NULL || res == NULL) { return 0; } - return snprintf(buf, buf_size, "%s: %ls", - SocketStatusToString(res->status), - (res->status == SOCKET_ERROR_GETADDRINFO - ? gai_strerror(res->getaddrinfo_error) - : strerror(res->saved_errno))); -} - -/* Print an error message based on the status contained in *RES. */ -void SocketPrintError(SocketResult *res) -{ - if (res == NULL) { return; } - printf("%s: %ls\n", SocketStatusToString(res->status), - (res->status == SOCKET_ERROR_GETADDRINFO - ? gai_strerror(res->getaddrinfo_error) - : strerror(res->saved_errno))); -} - -/* Set "hints" in an addrinfo struct, to be passed to getaddrinfo. */ -void SocketSetHints(SocketConfig *cfg, struct addrinfo *hints) -{ - if (cfg == NULL || hints == NULL) { return; } - memset(hints, 0, sizeof(*hints)); - - /* if .IPv4 or .IPv6 are used, set and use that instead of *host */ - if (cfg->path) - { - hints->ai_family = AF_UNIX; - } - else if (cfg->IPv6) - { - hints->ai_family = AF_INET6; - } - else if (cfg->IPv4) - { - hints->ai_family = AF_INET; - } - else - { - hints->ai_family = AF_UNSPEC; - } - - if (cfg->datagram) - { - hints->ai_socktype = SOCK_DGRAM; - } - else - { - hints->ai_socktype = SOCK_STREAM; - } - - /* Set passive unless UDP client */ - if (!cfg->datagram || cfg->server) - { - hints->ai_flags = AI_PASSIVE; - } - - if (cfg->IPv6 || cfg->IPv4) - { - hints->ai_flags |= AI_NUMERICHOST; - } +int SocketSend(Socket* socket, const void* datap, int len) +{ + const unsigned char* data = (const unsigned char*) datap; /* For pointer arithmetic */ + int sent, left; + + // /* Server sockets are for accepting connections only */ + if (socket->sflag) + { + // out->status = SOCKET_ERROR_SEND; + // out->saved_errno = SocketGetLastError(); + // SocketSetError(0); + return (-1); + } + + /* Keep sending data until it's sent or an error occurs */ + left = len; + sent = 0; + SocketSetError(0); + do + { + len = send(socket->handle, (const char*) data, left, 0); + if (len > 0) + { + sent += len; + left -= len; + data += len; + } + } while ((left > 0) && ((len > 0) || (SocketGetLastError() == EINTR))); + + return (sent); +} + +/* 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* socket, void* data, int maxlen) +{ + int len; + + /* Server sockets are for accepting connections only */ + if (socket->sflag) + { + // out->status = SOCKET_ERROR_RECEIVE; + // out->saved_errno = SocketGetLastError(); + // SocketSetError(0); + return (-1); + } + + SocketSetError(0); + do + { + len = recv(socket->handle, (char*) data, maxlen, 0); + } while (SocketGetLastError() == EINTR); + + // sock->ready = 0; + return (len); +} + +/* Construct an error message in BUF, based on the status codes + * in *RES. This has the same return value and general behavior + * as snprintf -- if the return value is >= buf_size, the string + * has been truncated. Returns -1 if either BUF or RES are NULL. */ +int SocketGetError(char* buf, size_t buf_size, SocketResult* res) +{ + if (buf == NULL || res == NULL) + { + return 0; + } + return snprintf(buf, buf_size, "%s: %ls", SocketStatusToString(res->status), (res->status == SOCKET_ERROR_GETADDRINFO ? gai_strerror(res->getaddrinfo_error) : strerror(res->saved_errno))); +} + +/* Print an error message based on the status contained in *RES. */ +void SocketPrintError(SocketResult* res) +{ + if (res == NULL) + { + return; + } + printf("%s: %ls\n", SocketStatusToString(res->status), (res->status == SOCKET_ERROR_GETADDRINFO ? gai_strerror(res->getaddrinfo_error) : strerror(res->saved_errno))); +} + +/* Set "hints" in an addrinfo struct, to be passed to getaddrinfo. */ +void SocketSetHints(SocketConfig* cfg, struct addrinfo* hints) +{ + if (cfg == NULL || hints == NULL) + { + return; + } + memset(hints, 0, sizeof(*hints)); + + /* if .IPv4 or .IPv6 are used, set and use that instead of *host */ + if (cfg->path) + { + hints->ai_family = AF_UNIX; + } + else if (cfg->IPv6) + { + hints->ai_family = AF_INET6; + } + else if (cfg->IPv4) + { + hints->ai_family = AF_INET; + } + else + { + hints->ai_family = AF_UNSPEC; + } + + if (cfg->datagram) + { + hints->ai_socktype = SOCK_DGRAM; + } + else + { + hints->ai_socktype = SOCK_STREAM; + } + + /* Set passive unless UDP client */ + if (!cfg->datagram || cfg->server) + { + hints->ai_flags = AI_PASSIVE; + } + + if (cfg->IPv6 || cfg->IPv4) + { + hints->ai_flags |= AI_NUMERICHOST; + } } // Print socket information @@ -657,72 +731,72 @@ void PrintSocket(struct SocketAddress* addr, const int family, const int socktyp switch (family) { case AF_UNSPEC: - { - TraceLog(LOG_DEBUG, "Family: Unspecified"); - } - break; + { + TraceLog(LOG_DEBUG, "\tFamily: Unspecified"); + } + break; case AF_INET: - { - TraceLog(LOG_DEBUG, "Family: AF_INET (IPv4)"); - TraceLog(LOG_INFO, "- IPv4 address %s", SocketAddressToString(addr, ip)); - } - break; + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET (IPv4)"); + TraceLog(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr, ip)); + } + break; case AF_INET6: - { - TraceLog(LOG_DEBUG, "Family: AF_INET6 (IPv6)"); - TraceLog(LOG_INFO, "- IPv6 address %s", SocketAddressToString(addr, ip)); - } - break; + { + TraceLog(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)"); + TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr, ip)); + } + break; case AF_NETBIOS: - { - TraceLog(LOG_DEBUG, "Family: AF_NETBIOS (NetBIOS)"); - } - break; + { + TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); + } + break; default: - { - TraceLog(LOG_DEBUG, "Family: Other %ld", family); - } - break; + { + TraceLog(LOG_DEBUG, "\tFamily: Other %ld", family); + } + break; } - TraceLog(LOG_DEBUG, "Socket type:"); + TraceLog(LOG_DEBUG, "\tSocket type:"); switch (socktype) { case 0: - TraceLog(LOG_DEBUG, "- Unspecified"); + TraceLog(LOG_DEBUG, "\t- Unspecified"); break; case SOCK_STREAM: - TraceLog(LOG_DEBUG, "- SOCK_STREAM (stream)"); + TraceLog(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); break; case SOCK_DGRAM: - TraceLog(LOG_DEBUG, "- SOCK_DGRAM (datagram)"); + TraceLog(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); break; case SOCK_RAW: - TraceLog(LOG_DEBUG, "- SOCK_RAW (raw)"); + TraceLog(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break; case SOCK_RDM: - TraceLog(LOG_DEBUG, "- SOCK_RDM (reliable message datagram)"); + TraceLog(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); break; case SOCK_SEQPACKET: - TraceLog(LOG_DEBUG, "- SOCK_SEQPACKET (pseudo-stream packet)"); + TraceLog(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); break; default: - TraceLog(LOG_DEBUG, "- Other %ld", socktype); + TraceLog(LOG_DEBUG, "\t- Other %ld", socktype); break; } - TraceLog(LOG_DEBUG, "Protocol:"); + TraceLog(LOG_DEBUG, "\tProtocol:"); switch (protocol) { case 0: - TraceLog(LOG_DEBUG, "- Unspecified"); + TraceLog(LOG_DEBUG, "\t- Unspecified"); break; case IPPROTO_TCP: - TraceLog(LOG_DEBUG, "- IPPROTO_TCP (TCP)"); + TraceLog(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break; case IPPROTO_UDP: - TraceLog(LOG_DEBUG, "- IPPROTO_UDP (UDP)"); + TraceLog(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break; default: - TraceLog(LOG_DEBUG, "- Other %ld", protocol); + TraceLog(LOG_DEBUG, "\t- Other %ld", protocol); break; } } @@ -733,20 +807,20 @@ char* SocketAddressToString(struct SocketAddress* sockaddr, char buffer[]) switch (sockaddr->family) { case AF_INET: - { - return inet_ntop(AF_INET, &((struct sockaddr_in*) sockaddr)->sin_addr, buffer, INET_ADDRSTRLEN); - } - break; + { + return inet_ntop(AF_INET, &((struct sockaddr_in*) sockaddr)->sin_addr, buffer, INET_ADDRSTRLEN); + } + break; case AF_INET6: - { - return inet_ntop(AF_INET6, &((struct sockaddr_in6*) sockaddr)->sin6_addr, buffer, INET6_ADDRSTRLEN); - } - break; + { + return inet_ntop(AF_INET6, &((struct sockaddr_in6*) sockaddr)->sin6_addr, buffer, INET6_ADDRSTRLEN); + } + break; default: - { - return NULL; - } - break; + { + return NULL; + } + break; } } @@ -797,13 +871,13 @@ unsigned int PackData(unsigned char* buf, char* format, ...) { case 'c': // 8-bit size += 1; - c = (signed char)va_arg(ap, int); // promoted + c = (signed char) va_arg(ap, int); // promoted *buf++ = c; break; case 'C': // 8-bit unsigned size += 1; - C = (unsigned char)va_arg(ap, unsigned int); // promoted + C = (unsigned char) va_arg(ap, unsigned int); // promoted *buf++ = C; break; @@ -851,15 +925,15 @@ unsigned int PackData(unsigned char* buf, char* format, ...) case 'f': // float-16 size += 2; - f = (float)va_arg(ap, double); // promoted - fhold = pack754_16(f); // convert to IEEE 754 + f = (float) va_arg(ap, double); // promoted + fhold = pack754_16(f); // convert to IEEE 754 packi16(buf, fhold); buf += 2; break; case 'd': // float-32 size += 4; - d = va_arg(ap, double); + d = va_arg(ap, double); fhold = pack754_32(d); // convert to IEEE 754 packi32(buf, fhold); buf += 4; @@ -867,14 +941,14 @@ unsigned int PackData(unsigned char* buf, char* format, ...) case 'g': // float-64 size += 8; - g = va_arg(ap, long double); + g = va_arg(ap, long double); fhold = pack754_64(g); // convert to IEEE 754 packi64(buf, fhold); buf += 8; break; case 's': // string - s = va_arg(ap, char*); + s = va_arg(ap, char*); len = strlen(s); size += len + 2; packi16(buf, len); @@ -942,75 +1016,75 @@ void UnpackData(unsigned char* buf, char* format, ...) } // re-sign else { - *c = -1 - (unsigned char)(0xffu - *buf); + *c = -1 - (unsigned char) (0xffu - *buf); } buf++; break; case 'C': // 8-bit unsigned - C = va_arg(ap, unsigned char*); + C = va_arg(ap, unsigned char*); *C = *buf++; break; case 'h': // 16-bit - h = va_arg(ap, int*); + h = va_arg(ap, int*); *h = unpacki16(buf); buf += 2; break; case 'H': // 16-bit unsigned - H = va_arg(ap, unsigned int*); + H = va_arg(ap, unsigned int*); *H = unpacku16(buf); buf += 2; break; case 'l': // 32-bit - l = va_arg(ap, long int*); + l = va_arg(ap, long int*); *l = unpacki32(buf); buf += 4; break; case 'L': // 32-bit unsigned - L = va_arg(ap, unsigned long int*); + L = va_arg(ap, unsigned long int*); *L = unpacku32(buf); buf += 4; break; case 'q': // 64-bit - q = va_arg(ap, long long int*); + q = va_arg(ap, long long int*); *q = unpacki64(buf); buf += 8; break; case 'Q': // 64-bit unsigned - Q = va_arg(ap, unsigned long long int*); + Q = va_arg(ap, unsigned long long int*); *Q = unpacku64(buf); buf += 8; break; case 'f': // float - f = va_arg(ap, float*); + f = va_arg(ap, float*); fhold = unpacku16(buf); - *f = unpack754_16(fhold); + *f = unpack754_16(fhold); buf += 2; break; case 'd': // float-32 - d = va_arg(ap, double*); + d = va_arg(ap, double*); fhold = unpacku32(buf); - *d = unpack754_32(fhold); + *d = unpack754_32(fhold); buf += 4; break; case 'g': // float-64 - g = va_arg(ap, long double*); + g = va_arg(ap, long double*); fhold = unpacku64(buf); - *g = unpack754_64(fhold); + *g = unpack754_64(fhold); buf += 8; break; case 's': // string - s = va_arg(ap, char*); + s = va_arg(ap, char*); len = unpacku16(buf); buf += 2; if (maxstrlen > 0 && len > maxstrlen)