/********************************************************************************************* * * 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 * * DEPENDENCIES: * raylib.h - TraceLog * rnet.h - platform-specific network includes * * CONTRIBUTORS: * Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version * * * LICENSE: zlib/libpng * * Copyright (c) 2019 Jak Barnes (github: @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. * **********************************************************************************************/ //---------------------------------------------------------------------------------- // Check if config flags have been externally provided on compilation line //---------------------------------------------------------------------------------- #include "rnet.h" #include "raylib.h" #include // Required for: assert() #include // Required for: FILE, fopen(), fclose(), fread() #include // Required for: malloc(), free() #include // Required for: strcmp(), strncmp() //---------------------------------------------------------------------------------- // Module defines //---------------------------------------------------------------------------------- #define NET_DEBUG_ENABLED (1) //---------------------------------------------------------------------------------- // 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; //---------------------------------------------------------------------------------- // Global module forward 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); //---------------------------------------------------------------------------------- // Global module implementation //---------------------------------------------------------------------------------- // 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() { #if defined(_WIN32) return WSAGetLastError(); #else return errno; #endif } // Returns a human-readable string representing the last error message static char *SocketGetLastErrorString() { 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]; sprintf(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 *sock, struct addrinfo *addr) { switch (sock->type) { case SOCKET_TCP: if (addr->ai_family == AF_INET) { sock->channel = socket(AF_INET, SOCK_STREAM, 0); } else { sock->channel = socket(AF_INET6, SOCK_STREAM, 0); } break; case SOCKET_UDP: if (addr->ai_family == AF_INET) { sock->channel = socket(AF_INET, SOCK_DGRAM, 0); } else { sock->channel = socket(AF_INET6, SOCK_DGRAM, 0); } break; default: TraceLog(LOG_WARNING, "Invalid socket type specified."); break; } return IsSocketValid(sock); } // 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; } } 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 InitNetwork() { #if defined(_WIN32) WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2); 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 CloseNetwork() { #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] = AllocAddress(); if (outAddr[i] == NULL) { break; } } outAddr[i] = NULL; if (i != size) { outAddr = NULL; } } else { TraceLog(LOG_WARNING, "Error, failed to dynamically allocate memory for the address list"); return -1; } // Copy all the address information from res into outAddrList int i = 0; for (iterator = res; iterator != NULL; iterator = iterator->ai_next) { if (i < size) { outAddr[i]->addr.ai_flags = iterator->ai_flags; outAddr[i]->addr.ai_family = iterator->ai_family; outAddr[i]->addr.ai_socktype = iterator->ai_socktype; outAddr[i]->addr.ai_protocol = iterator->ai_protocol; outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen; *outAddr[i]->addr.ai_addr = *iterator->ai_addr; #if NET_DEBUG_ENABLED TraceLog(LOG_DEBUG, "GetAddressInformation"); TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags); //PrintSocket(outAddr[i]->addr.ai_addr, outAddr[i]->addr.ai_family, outAddr[i]->addr.ai_socktype, outAddr[i]->addr.ai_protocol); TraceLog(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen); TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); #endif i++; } } // Free the pointer to the data returned by addrinfo freeaddrinfo(res); // Return the total count of addresses found return size; } // This here is the bread and butter of the socket API, This function will // attempt to open a socket, bind and listen to it based on the config passed in // // SocketConfig* config - Configuration for which socket to open // SocketResult* result - The results of this function (if any, including errors) // // e.g. // SocketConfig server_config = { SocketConfig client_config = { // .host = "127.0.0.1", .host = "127.0.0.1", // .port = 8080, .port = 8080, // .server = true, }; // .nonblocking = true, // }; // SocketResult server_res; SocketResult client_res; bool SocketCreate(SocketConfig *config, SocketResult *result) { // Socket creation result bool success = true; // Make sure we've not received a null config or result pointer if (config == NULL || result == NULL) { return (success = false); } // Set the defaults based on the config if (!SocketSetDefaults(config)) { TraceLog(LOG_WARNING, "Configuration Error."); success = false; } else { // Create the socket if (CreateSocket(config, result)) { if (config->nonblocking) { SocketSetNonBlocking(result->socket); } else { SocketSetBlocking(result->socket); } } else { success = false; } } return success; } // Bind a socket to a local address // Note: The bind function is required on an unconnected socket before subsequent calls to the listen function. bool SocketBind(SocketConfig *config, SocketResult *result) { bool success = false; result->status = RESULT_FAILURE; struct sockaddr_storage *sock_addr = NULL; // Don't bind to a socket that isn't configured as a server if (!IsSocketValid(result->socket) || !config->server) { TraceLog(LOG_WARNING, "Cannot bind to socket marked as \"Client\" in SocketConfig."); success = false; } else { if (result->socket->isIPv6) { sock_addr = (struct sockaddr_storage *) &result->socket->addripv6->address; } else { sock_addr = (struct sockaddr_storage *) &result->socket->addripv4->address; } if (sock_addr != NULL) { if (bind(result->socket->channel, (struct sockaddr *) sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR) { TraceLog(LOG_INFO, "Successfully bound socket."); success = true; } else { result->socket->status = SocketGetLastError(); TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); SocketSetLastError(0); success = false; } } } // Was the bind a success? if (success) { result->status = RESULT_SUCCESS; result->socket->ready = 0; result->socket->status = 0; socklen_t sock_len = sizeof(*sock_addr); if (getsockname(result->socket->channel, (struct sockaddr *) sock_addr, &sock_len) < 0) { TraceLog(LOG_WARNING, "Couldn't get socket address"); } else { struct sockaddr_in *s = (struct sockaddr_in *) sock_addr; // result->socket->address.host = s->sin_addr.s_addr; // result->socket->address.port = s->sin_port; // result->socket->addripv4 = (struct _SocketAddressIPv4 *) RNET_MALLOC(sizeof(*result->socket->addripv4)); if (result->socket->addripv4 != NULL) { memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4)); } memcpy(&result->socket->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in)); // } } return success; } // Listens (and queues) incoming connections requests for a bound port. bool SocketListen(SocketConfig *config, SocketResult *result) { bool success = false; result->status = RESULT_FAILURE; // Don't bind to a socket that isn't configured as a server if (!IsSocketValid(result->socket) || !config->server) { TraceLog(LOG_WARNING, "Cannot listen on socket marked as \"Client\" in SocketConfig."); success = false; } else { // Don't listen on UDP sockets if (!(config->type == SOCKET_UDP)) { if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR) { TraceLog(LOG_INFO, "Started listening on socket..."); success = true; } else { success = false; result->socket->status = SocketGetLastError(); TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); SocketSetLastError(0); } } else { TraceLog(LOG_WARNING, "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); success = false; } } // Was the listen a success? if (success) { result->status = RESULT_SUCCESS; result->socket->ready = 0; result->socket->status = 0; } return success; } // Connect the socket to the destination specified by "host" and "port" in SocketConfig bool SocketConnect(SocketConfig *config, SocketResult *result) { bool success = true; result->status = RESULT_FAILURE; // Only bind to sockets marked as server if (config->server) { TraceLog(LOG_WARNING, "Cannot connect to socket marked as \"Server\" in SocketConfig."); success = false; } else { if (IsIPv4Address(config->host)) { struct sockaddr_in ip4addr; ip4addr.sin_family = AF_INET; unsigned long hport; hport = strtoul(config->port, NULL, 0); ip4addr.sin_port = htons(hport); // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows). //inet_pton(AF_INET, config->host, &ip4addr.sin_addr); int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip4addr, sizeof(ip4addr)); if (connect_result == SOCKET_ERROR) { result->socket->status = SocketGetLastError(); SocketSetLastError(0); switch (result->socket->status) { case WSAEWOULDBLOCK: { success = true; break; } default: { TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); success = false; break; } } } else { TraceLog(LOG_INFO, "Successfully connected to socket."); success = true; } } else { if (IsIPv6Address(config->host)) { struct sockaddr_in6 ip6addr; ip6addr.sin6_family = AF_INET6; unsigned long hport; hport = strtoul(config->port, NULL, 0); ip6addr.sin6_port = htons(hport); //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO. int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip6addr, sizeof(ip6addr)); if (connect_result == SOCKET_ERROR) { result->socket->status = SocketGetLastError(); SocketSetLastError(0); switch (result->socket->status) { case WSAEWOULDBLOCK: { success = true; break; } default: { TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); success = false; break; } } } else { TraceLog(LOG_INFO, "Successfully connected to socket."); success = true; } } } } if (success) { result->status = RESULT_SUCCESS; result->socket->ready = 0; result->socket->status = 0; } return success; } // Closes an existing socket // // SocketChannel socket - The id of the socket to close void SocketClose(Socket *sock) { if (sock != NULL) { if (sock->channel != INVALID_SOCKET) { closesocket(sock->channel); } } } // Returns the sockaddress for a specific socket in a generic storage struct SocketAddressStorage SocketGetPeerAddress(Socket *sock) { // TODO. /* if (sock->isServer) return NULL; if (sock->isIPv6) return sock->addripv6; else return sock->addripv4; */ return NULL; } // Return the address-type appropriate host portion of a socket address char *GetSocketAddressHost(SocketAddressStorage storage) { assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); return SocketAddressToString((struct sockaddr_storage *) storage); } // Return the address-type appropriate port(service) portion of a socket address short GetSocketAddressPort(SocketAddressStorage storage) { //return ntohs(GetSocketPortPtr(storage)); // TODO. return 0; } // The accept function permits an incoming connection attempt on a socket. // // SocketChannel listener - The socket to listen for incoming connections on (i.e. server) // SocketResult* out - The result of this function (if any, including errors) // // e.g. // // SocketResult connection; // bool connected = false; // if (!connected) // { // if (SocketAccept(server_res.socket.channel, &connection)) // { // connected = true; // } // } Socket *SocketAccept(Socket *server, SocketConfig *config) { if (!server->isServer || server->type == SOCKET_UDP) { return NULL; } struct sockaddr_storage sock_addr; socklen_t sock_alen; Socket * sock; sock = AllocSocket(); 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; int error; error = 1; packet = (SocketDataPacket *) RNET_MALLOC(sizeof(*packet)); 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; 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; 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) { int i; for (i = 0; packetV[i]; ++i) { FreePacket(packetV[i]); } RNET_FREE(packetV); } } // Send 'len' bytes of 'data' over the non-server socket 'sock' // // Example 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; } 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) { // Timeout return false; } else { if (FD_ISSET(sock->channel, &writefds)) { return true; } } return false; #else return true; #endif } // Allocate and return a SocketResult struct SocketResult *AllocSocketResult() { struct SocketResult *res; res = (struct SocketResult *) RNET_MALLOC(sizeof(*res)); if (res != NULL) { memset(res, 0, sizeof(*res)); if ((res->socket = AllocSocket()) == NULL) { RNET_FREE(res); res = NULL; } } return res; } // Free an allocated SocketResult void FreeSocketResult(SocketResult **result) { if (*result != NULL) { if ((*result)->socket != NULL) { FreeSocket(&((*result)->socket)); } RNET_FREE(*result); *result = NULL; } } // Allocate a Socket Socket *AllocSocket() { // Allocate a socket if one already hasn't been 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 FreeSocket(Socket **sock) { if (*sock != NULL) { RNET_FREE(*sock); *sock = NULL; } } // Allocate a SocketSet SocketSet *AllocSocketSet(int max) { struct SocketSet *set; int i; 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 (i = 0; i < max; ++i) { set->sockets[i] = NULL; } } else { RNET_FREE(set); set = NULL; } } return (set); } // Free an allocated SocketSet void FreeSocketSet(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) { int i; if (sock != NULL) { 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 AllocAddress() { 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 FreeAddress(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 *AllocAddressList(int size) { AddressInformation *addr; addr = (AddressInformation *) RNET_MALLOC(size * sizeof(AddressInformation)); return addr; } // Opaque datatype accessor addrinfo->ai_family int GetAddressFamily(AddressInformation address) { return address->addr.ai_family; } // Opaque datatype accessor addrinfo->ai_socktype int GetAddressSocketType(AddressInformation address) { return address->addr.ai_socktype; } // Opaque datatype accessor addrinfo->ai_protocol int GetAddressProtocol(AddressInformation address) { return address->addr.ai_protocol; } // Opaque datatype accessor addrinfo->ai_canonname char *GetAddressCanonName(AddressInformation address) { return address->addr.ai_canonname; } // Opaque datatype accessor addrinfo->ai_addr char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport) { //char *ip[INET6_ADDRSTRLEN]; char *result = NULL; struct sockaddr_storage *storage = (struct sockaddr_storage *) address->addr.ai_addr; switch (storage->ss_family) { case AF_INET: { struct sockaddr_in *s = ((struct sockaddr_in *) address->addr.ai_addr); //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. *outport = ntohs(s->sin_port); } break; case AF_INET6: { struct sockaddr_in6 *s = ((struct sockaddr_in6 *) address->addr.ai_addr); //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. *outport = ntohs(s->sin6_port); } break; } if (result == NULL) { TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); SocketSetLastError(0); } else { strcpy(outhost, result); } return result; } // void PacketSend(Packet *packet) { printf("Sending packet (%s) with size %d\n", packet->data, packet->size); } // void PacketReceive(Packet *packet) { printf("Receiving packet (%s) with size %d\n", packet->data, packet->size); } // void PacketWrite16(Packet *packet, uint16_t value) { printf("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); printf("Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); } // void PacketWrite32(Packet *packet, uint32_t value) { printf("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); printf("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) { printf("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); printf("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]; printf("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]; printf("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]; printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); return value; }