diff --git a/CMakeLists.txt b/CMakeLists.txt index 2398ccc4..5068cb67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ set(INCLUDE_FILES ) set(SOURCE_FILES + address.c callbacks.c compress.c host.c diff --git a/address.c b/address.c new file mode 100644 index 00000000..0cb9096d --- /dev/null +++ b/address.c @@ -0,0 +1,637 @@ +/** + @file list.c + @brief ENet linked list functions +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +#include +#include + +// http://rosettacode.org/wiki/Parse_an_IP_Address#C + +static unsigned int _parseDecimal ( const char** pchCursor ) +{ + unsigned int nVal = 0; + char chNow; + while ( chNow = **pchCursor, chNow >= '0' && chNow <= '9' ) + { + //shift digit in + nVal *= 10; + nVal += chNow - '0'; + + ++*pchCursor; + } + return nVal; +} + + + +static unsigned int _parseHex ( const char** pchCursor ) +{ + unsigned int nVal = 0; + char chNow; + while ( chNow = **pchCursor & 0x5f, //(collapses case, but mutilates digits) + (chNow >= ('0'&0x5f) && chNow <= ('9'&0x5f)) || + (chNow >= 'A' && chNow <= 'F') + ) + { + unsigned char nybbleValue; + chNow -= 0x10; //scootch digital values down; hex now offset by x31 + nybbleValue = ( chNow > 9 ? chNow - (0x31-0x0a) : chNow ); + //shift nybble in + nVal <<= 4; + nVal += nybbleValue; + + ++*pchCursor; + } + return nVal; +} + + + +//Parse a textual IPv4 or IPv6 address, optionally with port, into a binary +//array (for the address, in network order), and an optionally provided port. +//Also, indicate which of those forms (4 or 6) was parsed. Return true on +//success. ppszText must be a nul-terminated ASCII string. It will be +//updated to point to the character which terminated parsing (so you can carry +//on with other things. abyAddr must be 16 bytes. You can provide NULL for +//abyAddr, nPort, bIsIPv6, if you are not interested in any of those +//informations. If we request port, but there is no port part, then nPort will +//be set to 0. There may be no whitespace leading or internal (though this may +//be used to terminate a successful parse. +//Note: the binary address and integer port are in network order. +static int ParseIPv4OrIPv6 ( const char** ppszText, enet_uint8* abyAddr, enet_uint16* pnPort, int* pbIsIPv6 ) +{ + unsigned char* abyAddrLocal; + unsigned char abyDummyAddr[16]; + + //find first colon, dot, and open bracket + const char* pchColon = strchr ( *ppszText, ':' ); + const char* pchDot = strchr ( *ppszText, '.' ); + const char* pchOpenBracket = strchr ( *ppszText, '[' ); + const char* pchCloseBracket = NULL; + + + //we'll consider this to (probably) be IPv6 if we find an open + //bracket, or an absence of dots, or if there is a colon, and it + //precedes any dots that may or may not be there + int bIsIPv6local = NULL != pchOpenBracket || NULL == pchDot || + ( NULL != pchColon && ( NULL == pchDot || pchColon < pchDot ) ); + //OK, now do a little further sanity check our initial guess... + if ( bIsIPv6local ) + { + //if open bracket, then must have close bracket that follows somewhere + pchCloseBracket = strchr ( *ppszText, ']' ); + if ( NULL != pchOpenBracket && ( NULL == pchCloseBracket || + pchCloseBracket < pchOpenBracket ) ) + return 0; + } + else //probably ipv4 + { + //dots must exist, and precede any colons + if ( NULL == pchDot || ( NULL != pchColon && pchColon < pchDot ) ) + return 0; + } + + //we figured out this much so far.... + if ( NULL != pbIsIPv6 ) + *pbIsIPv6 = bIsIPv6local; + + //especially for IPv6 (where we will be decompressing and validating) + //we really need to have a working buffer even if the caller didn't + //care about the results. + abyAddrLocal = abyAddr; //prefer to use the caller's + if ( NULL == abyAddrLocal ) //but use a dummy if we must + abyAddrLocal = abyDummyAddr; + + //OK, there should be no correctly formed strings which are miscategorized, + //and now any format errors will be found out as we continue parsing + //according to plan. + if ( ! bIsIPv6local ) //try to parse as IPv4 + { + //4 dotted quad decimal; optional port if there is a colon + //since there are just 4, and because the last one can be terminated + //differently, I'm just going to unroll any potential loop. + unsigned char* pbyAddrCursor = abyAddrLocal; + unsigned int nVal; + const char* pszTextBefore = *ppszText; + nVal =_parseDecimal ( ppszText ); //get first val + if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText ) //must be in range and followed by dot and nonempty + return 0; + *(pbyAddrCursor++) = (unsigned char) nVal; //stick it in addr + ++(*ppszText); //past the dot + + pszTextBefore = *ppszText; + nVal =_parseDecimal ( ppszText ); //get second val + if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText ) + return 0; + *(pbyAddrCursor++) = (unsigned char) nVal; + ++(*ppszText); //past the dot + + pszTextBefore = *ppszText; + nVal =_parseDecimal ( ppszText ); //get third val + if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText ) + return 0; + *(pbyAddrCursor++) = (unsigned char) nVal; + ++(*ppszText); //past the dot + + pszTextBefore = *ppszText; + nVal =_parseDecimal ( ppszText ); //get fourth val + if ( nVal > 255 || pszTextBefore == *ppszText ) //(we can terminate this one in several ways) + return 0; + *(pbyAddrCursor++) = (unsigned char) nVal; + + if ( ':' == **ppszText && NULL != pnPort ) //have port part, and we want it + { + ++(*ppszText); //past the colon + pszTextBefore = *ppszText; + nVal =_parseDecimal ( ppszText ); + if ( nVal > 65535 || pszTextBefore == *ppszText ) + return 0; + + *pnPort = (enet_uint16) nVal; + return 1; + } + else //finished just with ip address + { + if ( NULL != pnPort ) + *pnPort = 0; //indicate we have no port part + return 1; + } + } + else //try to parse as IPv6 + { + unsigned char* pbyAddrCursor; + unsigned char* pbyZerosLoc; + int bIPv4Detected; + int nIdx; + //up to 8 16-bit hex quantities, separated by colons, with at most one + //empty quantity, acting as a stretchy run of zeroes. optional port + //if there are brackets followed by colon and decimal port number. + //A further form allows an ipv4 dotted quad instead of the last two + //16-bit quantities, but only if in the ipv4 space ::ffff:x:x . + if ( NULL != pchOpenBracket ) //start past the open bracket, if it exists + *ppszText = pchOpenBracket + 1; + pbyAddrCursor = abyAddrLocal; + pbyZerosLoc = NULL; //if we find a 'zero compression' location + bIPv4Detected = 0; + for ( nIdx = 0; nIdx < 8; ++nIdx ) //we've got up to 8 of these, so we will use a loop + { + const char* pszTextBefore = *ppszText; + unsigned nVal =_parseHex ( ppszText ); //get value; these are hex + if ( pszTextBefore == *ppszText ) //if empty, we are zero compressing; note the loc + { + if ( NULL != pbyZerosLoc ) //there can be only one! + { + //unless it's a terminal empty field, then this is OK, it just means we're done with the host part + if ( pbyZerosLoc == pbyAddrCursor ) + { + --nIdx; + break; + } + return 0; //otherwise, it's a format error + } + if ( ':' != **ppszText ) //empty field can only be via : + return 0; + if ( 0 == nIdx ) //leading zero compression requires an extra peek, and adjustment + { + ++(*ppszText); + if ( ':' != **ppszText ) + return 0; + } + + pbyZerosLoc = pbyAddrCursor; + ++(*ppszText); + } + else + { + if ( '.' == **ppszText ) //special case of ipv4 convenience notation + { + //who knows how to parse ipv4? we do! + const char* pszTextlocal = pszTextBefore; //back it up + unsigned char abyAddrlocal[16]; + int bIsIPv6local; + int bParseResultlocal = ParseIPv4OrIPv6 ( &pszTextlocal, abyAddrlocal, NULL, &bIsIPv6local ); + *ppszText = pszTextlocal; //success or fail, remember the terminating char + if ( ! bParseResultlocal || bIsIPv6local ) //must parse and must be ipv4 + return 0; + //transfer addrlocal into the present location + *(pbyAddrCursor++) = abyAddrlocal[0]; + *(pbyAddrCursor++) = abyAddrlocal[1]; + *(pbyAddrCursor++) = abyAddrlocal[2]; + *(pbyAddrCursor++) = abyAddrlocal[3]; + ++nIdx; //pretend like we took another short, since the ipv4 effectively is two shorts + bIPv4Detected = 1; //remember how we got here for further validation later + break; //totally done with address + } + + if ( nVal > 65535 ) //must be 16 bit quantity + return 0; + *(pbyAddrCursor++) = nVal >> 8; //transfer in network order + *(pbyAddrCursor++) = nVal & 0xff; + if ( ':' == **ppszText ) //typical case inside; carry on + { + ++(*ppszText); + } + else //some other terminating character; done with this parsing parts + { + break; + } + } + } + + //handle any zero compression we found + if ( NULL != pbyZerosLoc ) + { + int nHead = (int)( pbyZerosLoc - abyAddrLocal ); //how much before zero compression + int nTail = nIdx * 2 - (int)( pbyZerosLoc - abyAddrLocal ); //how much after zero compression + int nZeros = 16 - nTail - nHead; //how much zeros + memmove ( &abyAddrLocal[16-nTail], pbyZerosLoc, nTail ); //scootch stuff down + memset ( pbyZerosLoc, 0, nZeros ); //clear the compressed zeros + } + + //validation of ipv4 subspace ::ffff:x.x + if ( bIPv4Detected ) + { + static const unsigned char abyPfx[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; + if ( 0 != memcmp ( abyAddrLocal, abyPfx, sizeof(abyPfx) ) ) + return 0; + } + + //close bracket + if ( NULL != pchOpenBracket ) + { + if ( ']' != **ppszText ) + return 0; + ++(*ppszText); + } + + if ( ':' == **ppszText && NULL != pnPort ) //have port part, and we want it + { + const char* pszTextBefore; + unsigned int nVal; + ++(*ppszText); //past the colon + pszTextBefore = *ppszText; + pszTextBefore = *ppszText; + nVal =_parseDecimal ( ppszText ); + if ( nVal > 65535 || pszTextBefore == *ppszText ) + return 0; + + *pnPort = (enet_uint16) nVal; + return 1; + } + else //finished just with ip address + { + if ( NULL != pnPort ) + *pnPort = 0; //indicate we have no port part + return 1; + } + } + +} + +int enet_address_equal_host(const ENetAddress * firstAddress, const ENetAddress * secondAddress) +{ + if (firstAddress->type != secondAddress->type) + return 0; + + if (firstAddress->port != secondAddress->port) + return 0; + + if (firstAddress->type == ENET_ADDRESS_TYPE_IPV4) + { + if (memcmp(&firstAddress->host.v4[0], &secondAddress->host.v4[0], 4*sizeof(enet_uint8)) != 0) + return 0; + } + else if (firstAddress->type == ENET_ADDRESS_TYPE_IPV6) + { + if (memcmp(&firstAddress->host.v6[0], &secondAddress->host.v6[0], 8 * sizeof(enet_uint16)) != 0) + return 0; + } + + return 1; +} + +int enet_address_equal(const ENetAddress * firstAddress, const ENetAddress * secondAddress) +{ + if (!enet_address_equal_host(firstAddress, secondAddress)) + return 0; + + if (firstAddress->port != secondAddress->port) + return 0; + + return 1; +} + +int +enet_address_is_any(const ENetAddress * address) +{ + switch (address->type) + { + case ENET_ADDRESS_TYPE_IPV4: + { + enet_uint8 zero[4] = { 0 }; + return memcmp(&address->host.v4[0], zero, 4 * sizeof(enet_uint8)) != 0; + } + + case ENET_ADDRESS_TYPE_IPV6: + { + enet_uint16 zero[8] = { 0 }; + return memcmp(&address->host.v6[0], zero, 8 * sizeof(enet_uint16)) != 0; + } + + default: + break; + } + + return 0; +} + +int +enet_address_is_broadcast(const ENetAddress * address) +{ + /* there's no broadcast address in ipv6 */ + if (address->type == ENET_ADDRESS_TYPE_IPV4 && + address->host.v4[0] == 0xFF && + address->host.v4[1] == 0xFF && + address->host.v4[2] == 0xFF && + address->host.v4[3] == 0xFF) + { + return 1; + } + + return 0; +} + +int +enet_address_is_loopback(const ENetAddress * address) +{ + switch (address->type) + { + case ENET_ADDRESS_TYPE_IPV4: + return address->host.v4[0] == 127; + + case ENET_ADDRESS_TYPE_IPV6: + { + enet_uint16 loopback[8] = { 0, 0, 0, 0, 0, 0, 0, 1 }; + return memcmp(&address->host.v6[0], loopback, 8 * sizeof(enet_uint16)) != 0; + } + + default: + break; + } + + return 0; +} + +int +enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) +{ + enum MaxSize + { + MS_PortPart = 1 + 4, /* semicolon + 4 digits */ + + MS_IPv4 = 4 * 3 + 3 + 1, /* 4 * 3 digits plus three dots plus end of string character */ + MS_IPv4MappedIPv6 = 7 + 4 * 3 + 3 + 1, /* prefix + 4 * 3 digits plus three dots plus end of string character */ + MS_IPv6 = 8 * 4 + 7 + 1, /* 4 * 3 digits plus three dots plus end of string character */ + + MS_IPv4WithPort = MS_IPv4 + MS_PortPart, + MS_IPv4MappedIPv6WithPort = MS_IPv4MappedIPv6 + MS_PortPart + 2, /* plus two brackets */ + MS_IPv6WithPort = MS_IPv6 + MS_PortPart + 2, /* plus two brackets */ + }; + + if (address -> type == ENET_ADDRESS_TYPE_IPV4) + { + if (address->port != 0) + { + if (nameLength < MS_IPv4WithPort) + return -1; + + sprintf(name, "%u.%u.%u.%u:%u", address->host.v4[0], address->host.v4[1], address->host.v4[2], address->host.v4[3], address->port); + } + else + { + if (nameLength < MS_IPv4) + return -1; + + sprintf(name, "%u.%u.%u.%u", address->host.v4[0], address->host.v4[1], address->host.v4[2], address->host.v4[3]); + } + } + else if (address->type == ENET_ADDRESS_TYPE_IPV6) + { + /* check if ipv4-mapped address */ + if (address -> host.v6[0] == 0 && address -> host.v6[1] == 0 && + address -> host.v6[2] == 0 && address -> host.v6[3] == 0 && + address -> host.v6[4] == 0 && address -> host.v6[5] == 0xFFFF) + { + if (address->port != 0) + { + if (nameLength < MS_IPv4MappedIPv6WithPort) + return -1; + + sprintf(name, "[::ffff:%u.%u.%u.%u]:%u", + (address->host.v6[6] >> 8) & 0xFF, (address->host.v6[6] >> 0) & 0xFF, + (address->host.v6[7] >> 8) & 0xFF, (address->host.v6[7] >> 0) & 0xFF, + address->port); + } + else + { + if (nameLength < MS_IPv4MappedIPv6) + return -1; + + sprintf(name, "::ffff:%u.%u.%u.%u", + (address->host.v6[6] >> 8) & 0xFF, (address->host.v6[6] >> 0) & 0xFF, + (address->host.v6[7] >> 8) & 0xFF, (address->host.v6[7] >> 0) & 0xFF); + } + } + else + { + /* standard IPv6 + * canonical representation of an IPv6 + * https://tools.ietf.org/html/rfc5952 + */ + + const size_t maxSize = 8 * 4 + 7 + 1; /* 4 * 3 digits plus three dots plus end of string character */ + char buffer[MS_IPv6WithPort]; + + /* find the longest zero sequence */ + int f0 = -1; + int l0 = -1; + int i, j; + char* p = buffer; + int advance; + + for (i = 0; i < 8; ++i) + { + if (address -> host.v6[i] == 0) + { + for (j = i + 1; j < 8; ++j) + { + if (address -> host.v6[j] != 0) + break; + } + + if (f0 == -1 || j - i > l0 - f0) + { + f0 = i; + l0 = j; + } + } + } + + /* We need brackets around our IPv6 address if we have a port */ + if (address->port != 0) + *p++ = '['; + + for (i = 0; i < 8; ++i) + { + if (i == f0) + { + *p++ = ':'; + *p++ = ':'; + + i = l0; + if (i >= 8) + break; + } + else if (i != 0) + *p++ = ':'; + + advance = sprintf(p, "%x", address -> host.v6[i]); + p += advance; + } + + if (address->port != 0) + { + *p++ = ']'; + *p++ = ':'; + + advance = sprintf(p, "%u", address->port); + p += advance; + } + + *p++ = '\0'; + + size_t addrLen = p - buffer; + if (nameLength < addrLen) + return -1; + + memcpy(name, buffer, addrLen); + } + } + + return 0; +} + + +int +enet_address_set_host_ip (ENetAddress * address, const char * name) +{ + enet_uint8 result[16]; + int isIPv6; + int i; + enet_uint16 port; + + if (!ParseIPv4OrIPv6(&name, result, &port, &isIPv6)) + return -1; + + if (isIPv6) + { + address->type = ENET_ADDRESS_TYPE_IPV6; + + for (i = 0; i < 8; ++i) + address->host.v6[i] = (result[i * 2] << 8) | result[i * 2 + 1]; + } + else + { + address->type = ENET_ADDRESS_TYPE_IPV4; + + for (i = 0; i < 4; ++i) + address->host.v4[i] = result[i]; + } + + if (port) + address->port = port; + + return 0; +} + + +void enet_address_build_any(ENetAddress * address, ENetAddressType type) +{ + address->type = type; + + switch (type) + { + case ENET_ADDRESS_TYPE_IPV4: + { + memset(&address->host.v4, 0, sizeof(address->host.v4)); + address->port = ENET_PORT_ANY; + break; + } + + case ENET_ADDRESS_TYPE_IPV6: + { + memset(&address->host.v6, 0, sizeof(address->host.v6)); + address->port = ENET_PORT_ANY; + break; + } + + default: + break; + } +} + +void enet_address_build_loopback(ENetAddress * address, ENetAddressType type) +{ + address->type = type; + + switch (type) + { + case ENET_ADDRESS_TYPE_IPV4: + { + enet_uint8 loopbackIpV4[4] = { 127, 0, 0, 1 }; + + memcpy(&address->host.v4, loopbackIpV4, sizeof(address->host.v4)); + address->port = ENET_PORT_ANY; + break; + } + + case ENET_ADDRESS_TYPE_IPV6: + { + enet_uint16 loopbackIpV6[8] = { 0, 0, 0, 0, 0, 0, 0, 1 }; + + memcpy(&address->host.v6, loopbackIpV6, sizeof(address->host.v6)); + address->port = ENET_PORT_ANY; + break; + } + + default: + break; + } +} + +void enet_address_convert_ipv6(ENetAddress * address) +{ + enet_uint8 ipv4[4]; + + if (address->type == ENET_ADDRESS_TYPE_IPV4) + { + /* Turn into IPv4-mapped IPv6 */ + address->type = ENET_ADDRESS_TYPE_IPV6; + memcpy(&ipv4[0], &address->host.v4[0], 4 * sizeof(enet_uint8)); /* save IPv4 before modifying the IPv6 because of the union */ + + address->host.v6[0] = 0; + address->host.v6[1] = 0; + address->host.v6[2] = 0; + address->host.v6[3] = 0; + address->host.v6[4] = 0; + address->host.v6[5] = 0xFFFF; + address->host.v6[6] = ((enet_uint16) ipv4[0]) << 8 | ipv4[1]; + address->host.v6[7] = ((enet_uint16) ipv4[2]) << 8 | ipv4[3]; + } +} + + +/** @} */ diff --git a/host.c b/host.c index fff946a3..c1e90531 100644 --- a/host.c +++ b/host.c @@ -26,7 +26,7 @@ at any given time. */ ENetHost * -enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +enet_host_create (ENetAddressType type, const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) { ENetHost * host; ENetPeer * currentPeer; @@ -34,6 +34,9 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) return NULL; + if (address && address->type != type && type != ENET_ADDRESS_TYPE_ANY) + return NULL; + host = (ENetHost *) enet_malloc (sizeof (ENetHost)); if (host == NULL) return NULL; @@ -48,7 +51,11 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL } memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); - host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM); + host -> socket = enet_socket_create (type, ENET_SOCKET_TYPE_DATAGRAM); + + if (host -> socket != ENET_SOCKET_NULL && type == ENET_ADDRESS_TYPE_ANY) + enet_socket_set_option (host -> socket, ENET_SOCKOPT_IPV6ONLY, 0); + if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0)) { if (host -> socket != ENET_SOCKET_NULL) @@ -87,7 +94,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL host -> commandCount = 0; host -> bufferCount = 0; host -> checksum = NULL; - host -> receivedAddress.host = ENET_HOST_ANY; + host -> receivedAddress.type = ENET_ADDRESS_TYPE_ANY; host -> receivedAddress.port = 0; host -> receivedData = NULL; host -> receivedDataLength = 0; @@ -241,7 +248,7 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC channel -> usedReliableWindows = 0; memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); } - + command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); diff --git a/include/enet/enet.h b/include/enet/enet.h index 30010187..c65afff4 100644 --- a/include/enet/enet.h +++ b/include/enet/enet.h @@ -63,7 +63,8 @@ typedef enum _ENetSocketOption ENET_SOCKOPT_SNDTIMEO = 7, ENET_SOCKOPT_ERROR = 8, ENET_SOCKOPT_NODELAY = 9, - ENET_SOCKOPT_TTL = 10 + ENET_SOCKOPT_TTL = 10, + ENET_SOCKOPT_IPV6ONLY = 11 } ENetSocketOption; typedef enum _ENetSocketShutdown @@ -73,26 +74,32 @@ typedef enum _ENetSocketShutdown ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 } ENetSocketShutdown; -#define ENET_HOST_ANY 0 -#define ENET_HOST_BROADCAST 0xFFFFFFFFU -#define ENET_PORT_ANY 0 +typedef enum _ENetAddressType +{ + ENET_ADDRESS_TYPE_ANY = 0, + ENET_ADDRESS_TYPE_IPV4 = 1, + ENET_ADDRESS_TYPE_IPV6 = 2 +} ENetAddressType; /** * Portable internet address structure. * * The host must be specified in network byte-order, and the port must be in host - * byte-order. The constant ENET_HOST_ANY may be used to specify the default - * server host. The constant ENET_HOST_BROADCAST may be used to specify the - * broadcast address (255.255.255.255). This makes sense for enet_host_connect, - * but not for enet_host_create. Once a server responds to a broadcast, the - * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. + * byte-order. */ typedef struct _ENetAddress { - enet_uint32 host; + ENetAddressType type; enet_uint16 port; + union + { + enet_uint8 v4[4]; + enet_uint16 v6[8]; + } host; } ENetAddress; +#define ENET_PORT_ANY 0 + /** * Packet flag bit constants. * @@ -496,7 +503,7 @@ ENET_API void enet_time_set (enet_uint32); /** @defgroup socket ENet socket functions @{ */ -ENET_API ENetSocket enet_socket_create (ENetSocketType); +ENET_API ENetSocket enet_socket_create (ENetAddressType, ENetSocketType); ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *); ENET_API int enet_socket_get_address (ENetSocket, ENetAddress *); ENET_API int enet_socket_listen (ENetSocket, int); @@ -517,6 +524,45 @@ ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSock @{ */ +/** Compares two addresses (only the host part) + @param firstAddress first address to compare + @param secondAddress second address to compare + @retval 1 if addresses are equal + @retval 0 if addresses are different + @returns if the addresses are equal +*/ +ENET_API int enet_address_equal_host (const ENetAddress * firstAddress, const ENetAddress * secondAddress); + +/** Compares two addresses and their port + @param firstAddress first address to compare + @param secondAddress second address to compare + @retval 1 if addresses are equal + @retval 0 if addresses are different + @returns if the addresses are equal +*/ +ENET_API int enet_address_equal(const ENetAddress * firstAddress, const ENetAddress * secondAddress); + +/** Checks if an address is the special any address + @param address address to check + @retval 1 if address is any + @returns if the address is the any one for its family +*/ +ENET_API int enet_address_is_any(const ENetAddress * address); + +/** Checks if an address is the special broadcast address + @param address address to check + @retval 1 if address is broadcast + @returns if the address is the broadcast one for its family +*/ +ENET_API int enet_address_is_broadcast(const ENetAddress * address); + +/** Checks if an address is a loopback one + @param address address to check + @retval 1 if address is loopback + @returns if the address is a loopback one for its family +*/ +ENET_API int enet_address_is_loopback(const ENetAddress * address); + /** Attempts to parse the printable form of the IP address in the parameter hostName and sets the host field in the address parameter if successful. @param address destination to store the parsed IP address @@ -529,13 +575,16 @@ ENET_API int enet_address_set_host_ip (ENetAddress * address, const char * hostN /** Attempts to resolve the host named by the parameter hostName and sets the host field in the address parameter if successful. + @param type address type (any/ipv4/ipv6) @param address destination to store resolved address @param hostName host name to lookup @retval 0 on success @retval < 0 on failure @returns the address of the given hostName in address on success */ -ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); +ENET_API int enet_address_set_host (ENetAddress * address, ENetAddressType type, const char * hostName); + +#define ENET_ADDRESS_MAX_LENGTH 40 /*full IPv6 addresses take 39 characters + 1 null byte */ /** Gives the printable form of the IP address specified in the address parameter. @param address address printed @@ -557,14 +606,18 @@ ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostN */ ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength); +ENET_API void enet_address_build_any(ENetAddress * address, ENetAddressType type); +ENET_API void enet_address_build_loopback(ENetAddress * address, ENetAddressType type); +ENET_API void enet_address_convert_ipv6(ENetAddress * address); + /** @} */ ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32); ENET_API void enet_packet_destroy (ENetPacket *); ENET_API int enet_packet_resize (ENetPacket *, size_t); ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t); - -ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); + +ENET_API ENetHost * enet_host_create (ENetAddressType type, const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); ENET_API void enet_host_destroy (ENetHost *); ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32); ENET_API int enet_host_check_events (ENetHost *, ENetEvent *); @@ -605,7 +658,7 @@ ENET_API void * enet_range_coder_create (void); ENET_API void enet_range_coder_destroy (void *); ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t); ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t); - + extern size_t enet_protocol_command_size (enet_uint8); #ifdef __cplusplus diff --git a/protocol.c b/protocol.c index 843a719a..38b0af4e 100644 --- a/protocol.c +++ b/protocol.c @@ -318,7 +318,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet } else if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && - currentPeer -> address.host == host -> receivedAddress.host) + enet_address_equal_host(¤tPeer->address, &host->receivedAddress)) { if (currentPeer -> address.port == host -> receivedAddress.port && currentPeer -> connectID == command -> connect.connectID) @@ -1043,9 +1043,8 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || - ((host -> receivedAddress.host != peer -> address.host || - host -> receivedAddress.port != peer -> address.port) && - peer -> address.host != ENET_HOST_BROADCAST) || + (!enet_address_equal(&host->receivedAddress, &peer->address) && + !enet_address_is_broadcast(&peer->address)) || (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && sessionID != peer -> incomingSessionID)) return 0; diff --git a/unix.c b/unix.c index 66692169..1e74bab7 100644 --- a/unix.c +++ b/unix.c @@ -63,6 +63,117 @@ typedef int socklen_t; static enet_uint32 timeBase = 0; +static int addressFamily[] = { + AF_UNSPEC, //< ENET_ADDRESS_TYPE_ANY + AF_INET, //< ENET_ADDRESS_TYPE_IPV4 + AF_INET6 //< ENET_ADDRESS_TYPE_IPV6 +}; + +static int +enet_address_from_sock_addr4(ENetAddress * address, const struct sockaddr_in* sockAddr) +{ + address->type = ENET_ADDRESS_TYPE_IPV4; + address->port = ENET_NET_TO_HOST_16(sockAddr->sin_port); + + memcpy(&address->host.v4[0], &sockAddr->sin_addr.s_addr, 4 * sizeof(enet_uint8)); + + return 0; +} + +static int +enet_address_from_sock_addr6(ENetAddress * address, const struct sockaddr_in6* sockAddr) +{ + int i; + + address->type = ENET_ADDRESS_TYPE_IPV6; + address->port = ENET_NET_TO_HOST_16(sockAddr->sin6_port); + + for (i = 0; i < 8; ++i) + address->host.v6[i] = ((enet_uint16) sockAddr->sin6_addr.s6_addr[i * 2]) << 8 | sockAddr->sin6_addr.s6_addr[i * 2 + 1]; + + return 0; +} + +static int +enet_address_from_addr_info(ENetAddress * address, const struct addrinfo * info) +{ + switch (info->ai_family) + { + case AF_INET: + return enet_address_from_sock_addr4(address, (struct sockaddr_in*) info->ai_addr); + + case AF_INET6: + return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) info->ai_addr); + + default: + return -1; + } +} + +static int +enet_address_from_sock_addr(ENetAddress * address, const struct sockaddr * sockAddr) +{ + switch (sockAddr->sa_family) + { + case AF_INET: + return enet_address_from_sock_addr4(address, (struct sockaddr_in*) sockAddr); + + case AF_INET6: + return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) sockAddr); + + default: + return -1; + } +} + +static int +enet_address_to_sock_addr(const ENetAddress * address, void * sockAddr) +{ + switch (address->type) + { + case ENET_ADDRESS_TYPE_IPV4: + { + struct sockaddr_in* socketAddress = (struct sockaddr_in*) sockAddr; + int addr; + + memset(socketAddress, 0, sizeof(struct sockaddr_in)); + socketAddress->sin_family = AF_INET; + socketAddress->sin_port = ENET_HOST_TO_NET_16(address->port); + + addr = ((unsigned int) address->host.v4[0]) << 24 + | ((unsigned int) address->host.v4[1]) << 16 + | ((unsigned int) address->host.v4[2]) << 8 + | ((unsigned int) address->host.v4[3]) << 0; + + socketAddress->sin_addr.s_addr = htonl(addr); + + return sizeof(struct sockaddr_in); + } + + case ENET_ADDRESS_TYPE_IPV6: + { + struct sockaddr_in6* socketAddress = (struct sockaddr_in6*) sockAddr; + int i; + + memset(socketAddress, 0, sizeof(struct sockaddr_in6)); + socketAddress->sin6_family = AF_INET6; + socketAddress->sin6_port = ENET_HOST_TO_NET_16(address->port); + + for (i = 0; i < 8; ++i) + { + u_short addressPart = ENET_HOST_TO_NET_16(address->host.v6[i]); + socketAddress->sin6_addr.s6_addr[i * 2 + 0] = addressPart >> 0; + socketAddress->sin6_addr.s6_addr[i * 2 + 1] = addressPart >> 8; + } + + return sizeof(struct sockaddr_in6); + } + + default: + return 0; + } +} + int enet_initialize (void) { @@ -101,46 +212,61 @@ enet_time_set (enet_uint32 newTimeBase) } int -enet_address_set_host_ip (ENetAddress * address, const char * name) -{ -#ifdef HAS_INET_PTON - if (! inet_pton (AF_INET, name, & address -> host)) -#else - if (! inet_aton (name, (struct in_addr *) & address -> host)) -#endif - return -1; - - return 0; -} - -int -enet_address_set_host (ENetAddress * address, const char * name) +enet_address_set_host (ENetAddress * address, ENetAddressType type, const char * name) { #ifdef HAS_GETADDRINFO - struct addrinfo hints, * resultList = NULL, * result = NULL; + struct addrinfo hints; + struct addrinfo* result; + struct addrinfo* resultList = NULL; + enet_uint16 port; + ENetAddress tempAddress; + int bestScore = -1; - memset (& hints, 0, sizeof (hints)); - hints.ai_family = AF_INET; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; - if (getaddrinfo (name, NULL, NULL, & resultList) != 0) - return -1; + if (getaddrinfo(name, NULL, &hints, &resultList) != 0) + return -1; + + port = address->port; /* preserve port */ - for (result = resultList; result != NULL; result = result -> ai_next) + for (result = resultList; result != NULL; result = result->ai_next) { - if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in)) + if (result->ai_addr != NULL) { - struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr; - - address -> host = sin -> sin_addr.s_addr; - - freeaddrinfo (resultList); - - return 0; + if (enet_address_from_addr_info(&tempAddress, result) == 0) + { + tempAddress.port = port; /* preserve port */ + + int addressScore = 0; + if (tempAddress.type == type || (tempAddress.type == ENET_ADDRESS_TYPE_IPV6 && type == ENET_ADDRESS_TYPE_ANY)) + addressScore += 10; + else if (tempAddress.type == ENET_ADDRESS_TYPE_IPV4) + { + if (type == ENET_ADDRESS_TYPE_ANY) + addressScore += 5; /* lower score than IPv6 addresses */ + else if (type == ENET_ADDRESS_TYPE_IPV6) + { + // Convert that IPv4 to an IPv6 + enet_address_convert_ipv6(&tempAddress); + addressScore += 3; /* lower score than a real IPv6 */ + } + } + + if (addressScore > bestScore) + { + memcpy(address, &tempAddress, sizeof(ENetAddress)); + bestScore = addressScore; + } + } } } if (resultList != NULL) - freeaddrinfo (resultList); + freeaddrinfo(resultList); + + if (bestScore >= 0) + return 0; #else struct hostent * hostEntry = NULL; #ifdef HAS_GETHOSTBYNAME_R @@ -157,55 +283,40 @@ enet_address_set_host (ENetAddress * address, const char * name) hostEntry = gethostbyname (name); #endif - if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET) + /* TODO */ + /*if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET) { address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; return 0; - } + }*/ #endif - return enet_address_set_host_ip (address, name); -} - -int -enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) -{ -#ifdef HAS_INET_NTOP - if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL) -#else - char * addr = inet_ntoa (* (struct in_addr *) & address -> host); - if (addr != NULL) + if (enet_address_set_host_ip(address, name) == 0) { - size_t addrLen = strlen(addr); - if (addrLen >= nameLength) - return -1; - memcpy (name, addr, addrLen + 1); - } + if (type == ENET_ADDRESS_TYPE_ANY) + enet_address_convert_ipv6(address); + + return 0; + } else -#endif return -1; - return 0; } int enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) { + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; + int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); #ifdef HAS_GETNAMEINFO - struct sockaddr_in sin; int err; - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - - err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD); + err = getnameinfo ((struct sockaddr *) sockAddrBuf, socketAddressLen, name, nameLength, NULL, 0, NI_NAMEREQD); if (! err) { if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength)) return -1; + return 0; } if (err != EAI_NONAME) @@ -218,17 +329,13 @@ enet_address_get_host (const ENetAddress * address, char * name, size_t nameLeng char buffer [2048]; int errnum; - in.s_addr = address -> host; - #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__GNU__) - gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); + gethostbyaddr_r ((char *) sockAddrBuf, socketAddressLen, addressFamily[address->type], & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); #else - hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum); + hostEntry = gethostbyaddr_r ((char *) sockAddrBuf, socketAddressLen, addressFamily[address->type], & hostData, buffer, sizeof (buffer), & errnum); #endif #else - in.s_addr = address -> host; - - hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); + hostEntry = gethostbyaddr ((char *) sockAddrBuf, socketAddressLen, addressFamily[address->type]); #endif if (hostEntry != NULL) @@ -247,41 +354,22 @@ enet_address_get_host (const ENetAddress * address, char * name, size_t nameLeng int enet_socket_bind (ENetSocket socket, const ENetAddress * address) { - struct sockaddr_in sin; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - - if (address != NULL) - { - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - } - else - { - sin.sin_port = 0; - sin.sin_addr.s_addr = INADDR_ANY; - } + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; + int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); - return bind (socket, - (struct sockaddr *) & sin, - sizeof (struct sockaddr_in)); + return bind(socket, (struct sockaddr *) sockAddrBuf, socketAddressLen); } int enet_socket_get_address (ENetSocket socket, ENetAddress * address) { - struct sockaddr_in sin; - socklen_t sinLength = sizeof (struct sockaddr_in); - - if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) - return -1; + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; + int bufferLength; - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + if (getsockname(socket, (struct sockaddr *) sockAddrBuf, &bufferLength) == -1) + return -1; - return 0; + return enet_address_from_sock_addr(address, (struct sockaddr *) sockAddrBuf); } int @@ -291,9 +379,9 @@ enet_socket_listen (ENetSocket socket, int backlog) } ENetSocket -enet_socket_create (ENetSocketType type) +enet_socket_create (ENetAddressType addressType, ENetSocketType socketType) { - return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); + return socket(addressType == ENET_ADDRESS_TYPE_IPV4 ? PF_INET : PF_INET6, socketType == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); } int @@ -352,6 +440,10 @@ enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) result = setsockopt (socket, IPPROTO_IP, IP_TTL, (char *) & value, sizeof (int)); break; + case ENET_SOCKOPT_IPV6ONLY: + result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) & value, sizeof(int)); + break; + default: break; } @@ -384,16 +476,11 @@ enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) int enet_socket_connect (ENetSocket socket, const ENetAddress * address) { - struct sockaddr_in sin; + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; + int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); int result; - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - - result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + result = connect(socket, (struct sockaddr*) sockAddrBuf, socketAddressLen); if (result == -1 && errno == EINPROGRESS) return 0; @@ -404,20 +491,20 @@ ENetSocket enet_socket_accept (ENetSocket socket, ENetAddress * address) { int result; - struct sockaddr_in sin; - socklen_t sinLength = sizeof (struct sockaddr_in); + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; + int socketAddressLen = sizeof(sockAddrBuf); result = accept (socket, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? & sinLength : NULL); + address != NULL ? (struct sockaddr*) sockAddrBuf : NULL, + address != NULL ? & socketAddressLen : NULL); if (result == -1) return ENET_SOCKET_NULL; if (address != NULL) { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) + return ENET_SOCKET_NULL; } return result; @@ -442,22 +529,19 @@ enet_socket_send (ENetSocket socket, const ENetBuffer * buffers, size_t bufferCount) { + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; struct msghdr msgHdr; - struct sockaddr_in sin; int sentLength; memset (& msgHdr, 0, sizeof (struct msghdr)); if (address != NULL) { - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; + msgHdr.msg_namelen = enet_address_to_sock_addr(address, sockAddrBuf); + if (msgHdr.msg_namelen == 0) + return -1; - msgHdr.msg_name = & sin; - msgHdr.msg_namelen = sizeof (struct sockaddr_in); + msgHdr.msg_name = (struct sockaddr *) sockAddrBuf; } msgHdr.msg_iov = (struct iovec *) buffers; @@ -482,6 +566,8 @@ enet_socket_receive (ENetSocket socket, ENetBuffer * buffers, size_t bufferCount) { + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; + int socketAddressLen = sizeof(sockAddrBuf); struct msghdr msgHdr; struct sockaddr_in sin; int recvLength; @@ -490,8 +576,8 @@ enet_socket_receive (ENetSocket socket, if (address != NULL) { - msgHdr.msg_name = & sin; - msgHdr.msg_namelen = sizeof (struct sockaddr_in); + msgHdr.msg_name = (struct sockaddr*) &sockAddrBuf; + msgHdr.msg_namelen = socketAddressLen; } msgHdr.msg_iov = (struct iovec *) buffers; @@ -514,8 +600,8 @@ enet_socket_receive (ENetSocket socket, if (address != NULL) { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) + return -1; } return recvLength; diff --git a/win32.c b/win32.c index f395c09b..a9e54ed0 100644 --- a/win32.c +++ b/win32.c @@ -8,10 +8,128 @@ #include "enet/enet.h" #include #include +#include +#include +#include #include +#include static enet_uint32 timeBase = 0; +static int addressFamily[] = { + AF_UNSPEC, //< ENET_ADDRESS_TYPE_ANY + AF_INET, //< ENET_ADDRESS_TYPE_IPV4 + AF_INET6 //< ENET_ADDRESS_TYPE_IPV6 +}; + +static int +enet_address_from_sock_addr4(ENetAddress * address, const struct sockaddr_in* sockAddr) +{ + address->type = ENET_ADDRESS_TYPE_IPV4; + address->port = ENET_NET_TO_HOST_16(sockAddr->sin_port); + + address->host.v4[0] = sockAddr->sin_addr.S_un.S_un_b.s_b1; + address->host.v4[1] = sockAddr->sin_addr.S_un.S_un_b.s_b2; + address->host.v4[2] = sockAddr->sin_addr.S_un.S_un_b.s_b3; + address->host.v4[3] = sockAddr->sin_addr.S_un.S_un_b.s_b4; + + return 0; +} + +static int +enet_address_from_sock_addr6(ENetAddress * address, const struct sockaddr_in6* sockAddr) +{ + int i; + + address->type = ENET_ADDRESS_TYPE_IPV6; + address->port = ENET_NET_TO_HOST_16(sockAddr->sin6_port); + + for (i = 0; i < 8; ++i) + address->host.v6[i] = ((enet_uint16) sockAddr->sin6_addr.s6_addr[i * 2]) << 8 | sockAddr->sin6_addr.s6_addr[i * 2 + 1]; + + return 0; +} + +static int +enet_address_from_addr_info(ENetAddress * address, const struct addrinfo * info) +{ + switch (info->ai_family) + { + case AF_INET: + return enet_address_from_sock_addr4(address, (struct sockaddr_in*) info->ai_addr); + + case AF_INET6: + return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) info->ai_addr); + + default: + return -1; + } +} + +static int +enet_address_from_sock_addr(ENetAddress * address, const struct sockaddr * sockAddr) +{ + switch (sockAddr->sa_family) + { + case AF_INET: + return enet_address_from_sock_addr4(address, (struct sockaddr_in*) sockAddr); + + case AF_INET6: + return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) sockAddr); + + default: + return -1; + } +} + +static int +enet_address_to_sock_addr(const ENetAddress * address, void * sockAddr) +{ + switch (address->type) + { + case ENET_ADDRESS_TYPE_IPV4: + { + struct sockaddr_in* socketAddress = (struct sockaddr_in*) sockAddr; + int addr; + + memset(socketAddress, 0, sizeof(struct sockaddr_in)); + socketAddress->sin_family = AF_INET; + socketAddress->sin_port = ENET_HOST_TO_NET_16(address->port); + + addr = ((unsigned int) address->host.v4[0]) << 24 + | ((unsigned int) address->host.v4[1]) << 16 + | ((unsigned int) address->host.v4[2]) << 8 + | ((unsigned int) address->host.v4[3]) << 0; + + socketAddress->sin_addr.s_addr = htonl(addr); + + return sizeof(struct sockaddr_in); + } + + case ENET_ADDRESS_TYPE_IPV6: + { + struct sockaddr_in6* socketAddress = (struct sockaddr_in6*) sockAddr; + int i; + + memset(socketAddress, 0, sizeof(struct sockaddr_in6)); + socketAddress->sin6_family = AF_INET6; + socketAddress->sin6_port = ENET_HOST_TO_NET_16(address->port); + + for (i = 0; i < 8; ++i) + { + u_short addressPart = ENET_HOST_TO_NET_16(address->host.v6[i]); + socketAddress->sin6_addr.s6_addr[i * 2 + 0] = addressPart >> 0; + socketAddress->sin6_addr.s6_addr[i * 2 + 1] = addressPart >> 8; + } + + return sizeof(struct sockaddr_in6); + } + + default: + return 0; + } +} + int enet_initialize (void) { @@ -61,122 +179,106 @@ enet_time_set (enet_uint32 newTimeBase) } int -enet_address_set_host_ip (ENetAddress * address, const char * name) +enet_address_set_host(ENetAddress * address, ENetAddressType type, const char * name) { - enet_uint8 vals [4] = { 0, 0, 0, 0 }; - int i; + struct addrinfo hints; + struct addrinfo* result; + struct addrinfo* resultList = NULL; + enet_uint16 port; + ENetAddress tempAddress; + int bestScore = -1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; - for (i = 0; i < 4; ++ i) + if (getaddrinfo(name, NULL, &hints, &resultList) != 0) + return -1; + + port = address->port; /* preserve port */ + + for (result = resultList; result != NULL; result = result->ai_next) { - const char * next = name + 1; - if (* name != '0') + if (result->ai_addr != NULL) { - long val = strtol (name, (char **) & next, 10); - if (val < 0 || val > 255 || next == name || next - name > 3) - return -1; - vals [i] = (enet_uint8) val; + if (enet_address_from_addr_info (&tempAddress, result) == 0) + { + tempAddress.port = port; /* preserve port */ + + int addressScore = 0; + if (tempAddress.type == type || (tempAddress.type == ENET_ADDRESS_TYPE_IPV6 && type == ENET_ADDRESS_TYPE_ANY)) + addressScore += 10; + else if (tempAddress.type == ENET_ADDRESS_TYPE_IPV4) + { + if (type == ENET_ADDRESS_TYPE_ANY) + addressScore += 5; /* lower score than IPv6 addresses */ + else if (type == ENET_ADDRESS_TYPE_IPV6) + { + // Convert that IPv4 to an IPv6 + enet_address_convert_ipv6(&tempAddress); + addressScore += 3; /* lower score than a real IPv6 */ + } + } + + if (addressScore > bestScore) + { + memcpy(address, &tempAddress, sizeof(ENetAddress)); + bestScore = addressScore; + } + } } - - if (* next != (i < 3 ? '.' : '\0')) - return -1; - name = next + 1; } - memcpy (& address -> host, vals, sizeof (enet_uint32)); - return 0; -} - -int -enet_address_set_host (ENetAddress * address, const char * name) -{ - struct hostent * hostEntry; - - hostEntry = gethostbyname (name); - if (hostEntry == NULL || - hostEntry -> h_addrtype != AF_INET) - return enet_address_set_host_ip (address, name); - - address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; - - return 0; -} + if (resultList != NULL) + freeaddrinfo(resultList); -int -enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) -{ - char * addr = inet_ntoa (* (struct in_addr *) & address -> host); - if (addr == NULL) - return -1; + if (bestScore >= 0) + return 0; else { - size_t addrLen = strlen(addr); - if (addrLen >= nameLength) - return -1; - memcpy (name, addr, addrLen + 1); + if (enet_address_set_host_ip(address, name) == 0) + { + if (type == ENET_ADDRESS_TYPE_ANY) + enet_address_convert_ipv6(address); + + return 0; + } + else + return -1; } - return 0; } int enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) { - struct in_addr in; - struct hostent * hostEntry; - - in.s_addr = address -> host; - - hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); - if (hostEntry == NULL) - return enet_address_get_host_ip (address, name, nameLength); - else - { - size_t hostLen = strlen (hostEntry -> h_name); - if (hostLen >= nameLength) - return -1; - memcpy (name, hostEntry -> h_name, hostLen + 1); - } + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; + int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); - return 0; + int result = getnameinfo((struct sockaddr*) sockAddrBuf, socketAddressLen, name, nameLength, NULL, 0, NI_NAMEREQD); + if (result != 0) + return enet_address_get_host_ip (address, name, nameLength); + else + return 0; } int enet_socket_bind (ENetSocket socket, const ENetAddress * address) { - struct sockaddr_in sin; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; + int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); - if (address != NULL) - { - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - } - else - { - sin.sin_port = 0; - sin.sin_addr.s_addr = INADDR_ANY; - } - - return bind (socket, - (struct sockaddr *) & sin, - sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0; + return bind (socket, (struct sockaddr *) sockAddrBuf, socketAddressLen) == SOCKET_ERROR ? -1 : 0; } int enet_socket_get_address (ENetSocket socket, ENetAddress * address) { - struct sockaddr_in sin; - int sinLength = sizeof (struct sockaddr_in); + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; + int bufferLength; - if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) + if (getsockname (socket, (struct sockaddr *) sockAddrBuf, &bufferLength) == -1) return -1; - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); - - return 0; + return enet_address_from_sock_addr(address, (struct sockaddr *) sockAddrBuf); } int @@ -186,9 +288,9 @@ enet_socket_listen (ENetSocket socket, int backlog) } ENetSocket -enet_socket_create (ENetSocketType type) +enet_socket_create (ENetAddressType addressType, ENetSocketType socketType) { - return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); + return socket (addressType == ENET_ADDRESS_TYPE_IPV4 ? PF_INET : PF_INET6, socketType == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); } int @@ -236,6 +338,13 @@ enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) result = setsockopt (socket, IPPROTO_IP, IP_TTL, (char *) & value, sizeof (int)); break; + case ENET_SOCKOPT_IPV6ONLY: + { + DWORD option = value; + result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) & option, sizeof(option)); + break; + } + default: break; } @@ -267,16 +376,11 @@ enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) int enet_socket_connect (ENetSocket socket, const ENetAddress * address) { - struct sockaddr_in sin; + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; + int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); int result; - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - - result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + result = connect (socket, (struct sockaddr*) sockAddrBuf, socketAddressLen); if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK) return -1; @@ -286,21 +390,21 @@ enet_socket_connect (ENetSocket socket, const ENetAddress * address) ENetSocket enet_socket_accept (ENetSocket socket, ENetAddress * address) { + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; + int socketAddressLen = sizeof(sockAddrBuf); SOCKET result; - struct sockaddr_in sin; - int sinLength = sizeof (struct sockaddr_in); result = accept (socket, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? & sinLength : NULL); + address != NULL ? (struct sockaddr*) sockAddrBuf : NULL, + address != NULL ? & socketAddressLen : NULL); if (result == INVALID_SOCKET) return ENET_SOCKET_NULL; if (address != NULL) { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) + return ENET_SOCKET_NULL; } return result; @@ -325,16 +429,16 @@ enet_socket_send (ENetSocket socket, const ENetBuffer * buffers, size_t bufferCount) { - struct sockaddr_in sin; + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; + int socketAddressLen; + DWORD sentLength = 0; if (address != NULL) { - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; + socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); + if (socketAddressLen == 0) + return -1; } if (WSASendTo (socket, @@ -342,12 +446,12 @@ enet_socket_send (ENetSocket socket, (DWORD) bufferCount, & sentLength, 0, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? sizeof (struct sockaddr_in) : 0, + address != NULL ? (struct sockaddr *) sockAddrBuf : NULL, + address != NULL ? socketAddressLen : 0, NULL, NULL) == SOCKET_ERROR) { - if (WSAGetLastError () == WSAEWOULDBLOCK) + if (WSAGetLastError() == WSAEWOULDBLOCK) return 0; return -1; @@ -362,7 +466,8 @@ enet_socket_receive (ENetSocket socket, ENetBuffer * buffers, size_t bufferCount) { - INT sinLength = sizeof (struct sockaddr_in); + unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; + int socketAddressLen = sizeof(sockAddrBuf); DWORD flags = 0, recvLength = 0; struct sockaddr_in sin; @@ -372,12 +477,12 @@ enet_socket_receive (ENetSocket socket, (DWORD) bufferCount, & recvLength, & flags, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? & sinLength : NULL, + address != NULL ? (struct sockaddr *) & sockAddrBuf : NULL, + address != NULL ? & socketAddressLen : NULL, NULL, NULL) == SOCKET_ERROR) { - switch (WSAGetLastError ()) + switch (WSAGetLastError()) { case WSAEWOULDBLOCK: case WSAECONNRESET: @@ -394,8 +499,8 @@ enet_socket_receive (ENetSocket socket, if (address != NULL) { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) + return -1; } return (int) recvLength;