diff --git a/include/CSFML/Network.h b/include/CSFML/Network.h index 31ce8b7..ddc55e0 100644 --- a/include/CSFML/Network.h +++ b/include/CSFML/Network.h @@ -28,6 +28,7 @@ // Headers //////////////////////////////////////////////////////////// +#include #include #include #include diff --git a/include/CSFML/Network/Dns.h b/include/CSFML/Network/Dns.h new file mode 100644 index 0000000..fe6d93a --- /dev/null +++ b/include/CSFML/Network/Dns.h @@ -0,0 +1,288 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +#include +#include + + +//////////////////////////////////////////////////////////// +/// \brief A DNS MX record +/// +//////////////////////////////////////////////////////////// +typedef struct +{ + const char* exchange; ///< Host willing to act as mail exchange + uint16_t preference; ///< Preference of this record among others, lower values are preferred +} sfDnsMxRecord; + +//////////////////////////////////////////////////////////// +/// \brief A DNS SRV record +/// +//////////////////////////////////////////////////////////// +typedef struct +{ + const char* target; ///< The domain name of the target host + uint16_t port; ///< The port on the target host of the service + uint16_t weight; ///< Server selection mechanism, larger weights should be given a proportionately higher probability of being selected + uint16_t priority; ///< The priority of the target host, a client must attempt to contact the target host with the lowest-numbered priority it can reach +} sfDnsSrvRecord; + + +//////////////////////////////////////////////////////////// +/// \brief Resolve a hostname into a list of IP addresses +/// +/// The returned array must be freed with sfFree. +/// +/// \param hostname Hostname to resolve, encoded in UTF-8 +/// \param servers The list of servers to query, NULL to use the default servers +/// \param serverCount Number of servers in \a servers +/// \param timeout Query timeout if using a provided list of servers, use 0 to wait forever +/// \param count Pointer to a variable that will be filled with the number of addresses +/// +/// \return Array of IP addresses the given hostname resolves to, NULL if name resolution fails or no address was found +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfIpAddress* sfDns_resolve( + const char* hostname, + const sfIpAddress* servers, + size_t serverCount, + sfTime timeout, + size_t* count); + +//////////////////////////////////////////////////////////// +/// \brief Query NS records for a hostname +/// +/// \param hostname Hostname to query NS records for, encoded in UTF-8 +/// \param servers The list of servers to query, NULL to use the default servers +/// \param serverCount Number of servers in \a servers +/// \param timeout Query timeout if using a provided list of servers, use 0 to wait forever +/// +/// \return New list of NS records, which can be empty +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfDnsNsRecords* sfDns_queryNs(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout); + +//////////////////////////////////////////////////////////// +/// \brief Query MX records for a hostname +/// +/// \param hostname Hostname to query MX records for, encoded in UTF-8 +/// \param servers The list of servers to query, NULL to use the default servers +/// \param serverCount Number of servers in \a servers +/// \param timeout Query timeout if using a provided list of servers, use 0 to wait forever +/// +/// \return New list of MX records, which can be empty +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfDnsMxRecords* sfDns_queryMx(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout); + +//////////////////////////////////////////////////////////// +/// \brief Query SRV records for a hostname +/// +/// \param hostname Hostname to query SRV records for, encoded in UTF-8 +/// \param servers The list of servers to query, NULL to use the default servers +/// \param serverCount Number of servers in \a servers +/// \param timeout Query timeout if using a provided list of servers, use 0 to wait forever +/// +/// \return New list of SRV records, which can be empty +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfDnsSrvRecords* sfDns_querySrv(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout); + +//////////////////////////////////////////////////////////// +/// \brief Query TXT records for a hostname +/// +/// \param hostname Hostname to query TXT records for, encoded in UTF-8 +/// \param servers The list of servers to query, NULL to use the default servers +/// \param serverCount Number of servers in \a servers +/// \param timeout Query timeout if using a provided list of servers, use 0 to wait forever +/// +/// \return New list of TXT records, which can be empty +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfDnsTxtRecords* sfDns_queryTxt(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout); + +//////////////////////////////////////////////////////////// +/// \brief Get the computer's public address via DNS +/// +/// The public address is the address of the computer from the +/// point of view of the internet, i.e. something like 89.54.1.169 +/// or 2600:1901:0:13e0::1 as opposed to a private or local address +/// like 192.168.1.56 or fe80::1234:5678:9abc. +/// +/// This function depends on both your network connection and +/// the server, and may be very slow. You should try to use it +/// as little as possible. +/// +/// \param timeout Maximum time to wait, use 0 to wait forever +/// \param type The type of public address to get +/// +/// \return Public IP address of the computer on success, sfIpAddress_None otherwise +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfIpAddress sfDns_getPublicAddress(sfTime timeout, sfIpAddressType type); + +//////////////////////////////////////////////////////////// +/// \brief Destroy a list of NS records +/// +/// \param records List of NS records to destroy +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API void sfDnsNsRecords_destroy(const sfDnsNsRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get the number of NS records in a list +/// +/// \param records List of NS records +/// +/// \return Number of NS records +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API size_t sfDnsNsRecords_getCount(const sfDnsNsRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get a NS record of a list +/// +/// \param records List of NS records +/// \param index Index of the NS record to get +/// +/// \return The NS record, encoded in UTF-8 +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API const char* sfDnsNsRecords_getRecord(const sfDnsNsRecords* records, size_t index); + +//////////////////////////////////////////////////////////// +/// \brief Destroy a list of MX records +/// +/// \param records List of MX records to destroy +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API void sfDnsMxRecords_destroy(const sfDnsMxRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get the number of MX records in a list +/// +/// \param records List of MX records +/// +/// \return Number of MX records +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API size_t sfDnsMxRecords_getCount(const sfDnsMxRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get a MX record of a list +/// +/// The strings of the record stay valid until the list +/// is destroyed. +/// +/// \param records List of MX records +/// \param index Index of the MX record to get +/// +/// \return The MX record +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfDnsMxRecord sfDnsMxRecords_getRecord(const sfDnsMxRecords* records, size_t index); + +//////////////////////////////////////////////////////////// +/// \brief Destroy a list of SRV records +/// +/// \param records List of SRV records to destroy +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API void sfDnsSrvRecords_destroy(const sfDnsSrvRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get the number of SRV records in a list +/// +/// \param records List of SRV records +/// +/// \return Number of SRV records +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API size_t sfDnsSrvRecords_getCount(const sfDnsSrvRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get a SRV record of a list +/// +/// The strings of the record stay valid until the list +/// is destroyed. +/// +/// \param records List of SRV records +/// \param index Index of the SRV record to get +/// +/// \return The SRV record +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfDnsSrvRecord sfDnsSrvRecords_getRecord(const sfDnsSrvRecords* records, size_t index); + +//////////////////////////////////////////////////////////// +/// \brief Destroy a list of TXT records +/// +/// \param records List of TXT records to destroy +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API void sfDnsTxtRecords_destroy(const sfDnsTxtRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get the number of TXT records in a list +/// +/// \param records List of TXT records +/// +/// \return Number of TXT records +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API size_t sfDnsTxtRecords_getCount(const sfDnsTxtRecords* records); + +//////////////////////////////////////////////////////////// +/// \brief Get the number of strings in a TXT record +/// +/// \param records List of TXT records +/// \param index Index of the TXT record +/// +/// \return Number of strings in the TXT record +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API size_t sfDnsTxtRecords_getStringCount(const sfDnsTxtRecords* records, size_t index); + +//////////////////////////////////////////////////////////// +/// \brief Get a string of a TXT record +/// +/// \param records List of TXT records +/// \param index Index of the TXT record +/// \param stringIndex Index of the string within the TXT record +/// +/// \return The string, encoded in UTF-8 +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API const char* sfDnsTxtRecords_getString(const sfDnsTxtRecords* records, size_t index, size_t stringIndex); diff --git a/include/CSFML/Network/IpAddress.h b/include/CSFML/Network/IpAddress.h index 4b6eefc..1bab799 100644 --- a/include/CSFML/Network/IpAddress.h +++ b/include/CSFML/Network/IpAddress.h @@ -31,17 +31,34 @@ #include +#include +#include + //////////////////////////////////////////////////////////// -/// \brief Encapsulate an IPv4 network address +/// \brief Encapsulate an IPv4 or IPv6 network address +/// +/// The address is stored as its string representation, +/// which is large enough to hold any IPv6 address. /// //////////////////////////////////////////////////////////// typedef struct { - char address[16]; + char address[46]; } sfIpAddress; +//////////////////////////////////////////////////////////// +/// \brief Type of an IP address +/// +//////////////////////////////////////////////////////////// +typedef enum +{ + sfIpAddressV4, ///< IPv4 address + sfIpAddressV6 ///< IPv6 address +} sfIpAddressType; + + //////////////////////////////////////////////////////////// /// \brief Empty object that represents invalid addresses /// @@ -49,36 +66,85 @@ typedef struct CSFML_NETWORK_API const sfIpAddress sfIpAddress_None; //////////////////////////////////////////////////////////// -/// \brief Value representing any address (0.0.0.0) +/// \brief The same as sfIpAddress_AnyV4 /// //////////////////////////////////////////////////////////// CSFML_NETWORK_API const sfIpAddress sfIpAddress_Any; //////////////////////////////////////////////////////////// -/// \brief Local host IP address (127.0.0.1, or "localhost") +/// \brief The same as sfIpAddress_LocalHostV4 /// //////////////////////////////////////////////////////////// CSFML_NETWORK_API const sfIpAddress sfIpAddress_LocalHost; //////////////////////////////////////////////////////////// -/// \brief UDP broadcast address (255.255.255.255) +/// \brief The same as sfIpAddress_BroadcastV4 /// //////////////////////////////////////////////////////////// CSFML_NETWORK_API const sfIpAddress sfIpAddress_Broadcast; //////////////////////////////////////////////////////////// -/// \brief Create an address from a string +/// \brief Value representing any IPv4 address (0.0.0.0) /// -/// Here \a address can be either a decimal address -/// (ex: "192.168.1.56") or a network name (ex: "localhost"). +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API const sfIpAddress sfIpAddress_AnyV4; + +//////////////////////////////////////////////////////////// +/// \brief Local host IPv4 address (127.0.0.1) /// -/// \param address IP address or network name +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API const sfIpAddress sfIpAddress_LocalHostV4; + +//////////////////////////////////////////////////////////// +/// \brief UDP broadcast IPv4 address (255.255.255.255) /// -/// \return Resulting address +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API const sfIpAddress sfIpAddress_BroadcastV4; + +//////////////////////////////////////////////////////////// +/// \brief Value representing any IPv6 address (::) +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API const sfIpAddress sfIpAddress_AnyV6; + +//////////////////////////////////////////////////////////// +/// \brief Local host IPv6 address (::1) +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API const sfIpAddress sfIpAddress_LocalHostV6; + +//////////////////////////////////////////////////////////// +/// \brief Create an address from a string representation +/// +/// Here \a address can be either an IPv4 address in +/// dotted-decimal notation (ex: "192.168.1.56") or an IPv6 +/// address in standard notation (ex: "2606:4700:4700::1111"). +/// Network names are not resolved, use sfDns_resolve or +/// sfIpAddress_resolve for that. +/// +/// \param address IP address string +/// +/// \return Resulting address or sfIpAddress_None if the string is not a valid address /// //////////////////////////////////////////////////////////// CSFML_NETWORK_API sfIpAddress sfIpAddress_fromString(const char* address); +//////////////////////////////////////////////////////////// +/// \brief Create an IPv4 address from a string or by resolving a network name +/// +/// Here \a address can be either a decimal address +/// (ex: "192.168.1.56") or a network name (ex: "localhost"). +/// Only IPv4 addresses are returned. +/// +/// \param address IP address or network name +/// +/// \return Resulting address or sfIpAddress_None if the address could not be resolved +/// +/// \deprecated Use sfDns_resolve instead +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API CSFML_DEPRECATED sfIpAddress sfIpAddress_resolve(const char* address); + //////////////////////////////////////////////////////////// /// \brief Create an address from 4 bytes /// @@ -112,12 +178,24 @@ CSFML_NETWORK_API sfIpAddress sfIpAddress_fromBytes(uint8_t byte0, uint8_t byte1 //////////////////////////////////////////////////////////// CSFML_NETWORK_API sfIpAddress sfIpAddress_fromInteger(uint32_t address); +//////////////////////////////////////////////////////////// +/// \brief Create an IPv6 address from 16 bytes +/// +/// \param bytes Array of 16 bytes containing the address +/// +/// \return Resulting address +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfIpAddress sfIpAddress_fromV6Bytes(const uint8_t bytes[16]); + //////////////////////////////////////////////////////////// /// \brief Get a string representation of an address /// /// The returned string is the decimal representation of the -/// IP address (like "192.168.1.56"), even if it was constructed -/// from a host name. +/// IPv4 address (like "192.168.1.56") or the standard +/// representation of the IPv6 address (like "2606:4700:4700::1111"), +/// even if it was constructed from a host name. +/// The string must be able to hold at least 46 characters. /// /// \param address Address object /// \param string String where the string representation will be stored @@ -134,6 +212,9 @@ CSFML_NETWORK_API void sfIpAddress_toString(sfIpAddress address, char* string); /// The integer produced by this function can then be converted /// back to a sfIpAddress with sfIpAddress_fromInteger. /// +/// Only IPv4 addresses can be converted to an integer, 0 is +/// returned for IPv6 addresses. +/// /// \param address Address object /// /// \return 32-bits unsigned integer representation of the address @@ -141,6 +222,50 @@ CSFML_NETWORK_API void sfIpAddress_toString(sfIpAddress address, char* string); //////////////////////////////////////////////////////////// CSFML_NETWORK_API uint32_t sfIpAddress_toInteger(sfIpAddress address); +//////////////////////////////////////////////////////////// +/// \brief Get a 16-byte representation of an IPv6 address +/// +/// The bytes produced by this function can then be converted +/// back to a sfIpAddress with sfIpAddress_fromV6Bytes. +/// +/// \param address Address object +/// \param bytes Array of 16 bytes that will be filled with the address +/// +/// \return True if the address is a valid IPv6 address and the bytes have been written, false otherwise +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API bool sfIpAddress_toV6Bytes(sfIpAddress address, uint8_t bytes[16]); + +//////////////////////////////////////////////////////////// +/// \brief Get the type of an address +/// +/// \param address Address object +/// +/// \return The type of the address, sfIpAddressV4 is returned for invalid addresses +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfIpAddressType sfIpAddress_getType(sfIpAddress address); + +//////////////////////////////////////////////////////////// +/// \brief Check if an address is a valid IPv4 address +/// +/// \param address Address object +/// +/// \return True if the address is a valid IPv4 address +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API bool sfIpAddress_isV4(sfIpAddress address); + +//////////////////////////////////////////////////////////// +/// \brief Check if an address is a valid IPv6 address +/// +/// \param address Address object +/// +/// \return True if the address is a valid IPv6 address +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API bool sfIpAddress_isV6(sfIpAddress address); + //////////////////////////////////////////////////////////// /// \brief Get the computer's local address /// @@ -150,11 +275,28 @@ CSFML_NETWORK_API uint32_t sfIpAddress_toInteger(sfIpAddress address); /// Unlike sfIpAddress_getPublicAddress, this function is fast /// and may be used safely anywhere. /// +/// This returns the local IPv4 address. +/// /// \return Local IP address of the computer /// //////////////////////////////////////////////////////////// CSFML_NETWORK_API sfIpAddress sfIpAddress_getLocalAddress(void); +//////////////////////////////////////////////////////////// +/// \brief Get the computer's local address of a given type +/// +/// The local address is the address of the computer from the +/// LAN point of view, i.e. something like 192.168.1.56 or +/// fe80::1234:5678:9abc. It is meaningful only for +/// communications over the local network. +/// +/// \param type The type of local address to get +/// +/// \return Local IP address of the computer or sfIpAddress_None if there is none +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfIpAddress sfIpAddress_getLocalAddressOfType(sfIpAddressType type); + //////////////////////////////////////////////////////////// /// \brief Get the computer's public address /// @@ -170,9 +312,31 @@ CSFML_NETWORK_API sfIpAddress sfIpAddress_getLocalAddress(void); /// to be possibly stuck waiting in case there is a problem; use /// 0 to deactivate this limit. /// +/// This returns the public IPv4 address. +/// /// \param timeout Maximum time to wait /// /// \return Public IP address of the computer /// //////////////////////////////////////////////////////////// CSFML_NETWORK_API sfIpAddress sfIpAddress_getPublicAddress(sfTime timeout); + +//////////////////////////////////////////////////////////// +/// \brief Get the computer's public address of a given type +/// +/// The public address is the address of the computer from the +/// internet point of view, i.e. something like 89.54.1.169 or +/// 2600:1901:0:13e0::1. See sfIpAddress_getPublicAddress for +/// details. +/// +/// If tamper resistance is required, setting \a secure to true +/// will make use of verified HTTPS connections to get the address. +/// +/// \param timeout Maximum time to wait, use 0 to deactivate the limit +/// \param type The type of public address to get, NULL to specify no preference +/// \param secure True to retrieve the public address via a secure HTTPS connection, false to retrieve via DNS or an insecure connection +/// +/// \return Public IP address of the computer or sfIpAddress_None on failure +/// +//////////////////////////////////////////////////////////// +CSFML_NETWORK_API sfIpAddress sfIpAddress_getPublicAddressOfType(sfTime timeout, const sfIpAddressType* type, bool secure); diff --git a/include/CSFML/Network/Types.h b/include/CSFML/Network/Types.h index e607e3f..0e6636d 100644 --- a/include/CSFML/Network/Types.h +++ b/include/CSFML/Network/Types.h @@ -25,6 +25,10 @@ #pragma once +typedef struct sfDnsMxRecords sfDnsMxRecords; +typedef struct sfDnsNsRecords sfDnsNsRecords; +typedef struct sfDnsSrvRecords sfDnsSrvRecords; +typedef struct sfDnsTxtRecords sfDnsTxtRecords; typedef struct sfFtpDirectoryResponse sfFtpDirectoryResponse; typedef struct sfFtpListingResponse sfFtpListingResponse; typedef struct sfFtpResponse sfFtpResponse; diff --git a/src/CSFML/Network/CMakeLists.txt b/src/CSFML/Network/CMakeLists.txt index c24caf4..f9fa582 100644 --- a/src/CSFML/Network/CMakeLists.txt +++ b/src/CSFML/Network/CMakeLists.txt @@ -3,6 +3,10 @@ set(SRCROOT ${PROJECT_SOURCE_DIR}/src/CSFML/Network) # all source files set(SRC + ${SRCROOT}/ConvertIpAddress.hpp + ${SRCROOT}/Dns.cpp + ${SRCROOT}/DnsStruct.hpp + ${INCROOT}/Dns.h ${INCROOT}/Export.h ${SRCROOT}/Ftp.cpp ${SRCROOT}/FtpStruct.hpp diff --git a/src/CSFML/Network/ConvertIpAddress.hpp b/src/CSFML/Network/ConvertIpAddress.hpp new file mode 100644 index 0000000..4998751 --- /dev/null +++ b/src/CSFML/Network/ConvertIpAddress.hpp @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include +#include + + +//////////////////////////////////////////////////////////// +// Convert sf::IpAddress to sfIpAddress +//////////////////////////////////////////////////////////// +[[nodiscard]] inline sfIpAddress convertIpAddress(const sf::IpAddress& address) +{ + sfIpAddress result{}; + std::strncpy(result.address, address.toString().c_str(), sizeof(result.address) - 1); + return result; +} + + +//////////////////////////////////////////////////////////// +// Convert std::optional to sfIpAddress +//////////////////////////////////////////////////////////// +[[nodiscard]] inline sfIpAddress convertIpAddress(const std::optional& address) +{ + return address ? convertIpAddress(*address) : sfIpAddress_None; +} + + +//////////////////////////////////////////////////////////// +// Convert sfIpAddress to std::optional +//////////////////////////////////////////////////////////// +[[nodiscard]] inline std::optional convertIpAddress(const sfIpAddress& address) +{ + return sf::IpAddress::fromString(address.address); +} + + +//////////////////////////////////////////////////////////// +// Convert an optional sfIpAddressType to std::optional +//////////////////////////////////////////////////////////// +[[nodiscard]] inline std::optional convertIpAddressType(const sfIpAddressType* type) +{ + if (!type) + return std::nullopt; + + return static_cast(*type); +} diff --git a/src/CSFML/Network/Dns.cpp b/src/CSFML/Network/Dns.cpp new file mode 100644 index 0000000..fb834d8 --- /dev/null +++ b/src/CSFML/Network/Dns.cpp @@ -0,0 +1,285 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include + +#include +#include +#include + + +namespace +{ +//////////////////////////////////////////////////////////// +[[nodiscard]] sf::String fromUtf8(const char* string) +{ + return sf::String::fromUtf8(string, string + std::strlen(string)); +} + + +//////////////////////////////////////////////////////////// +[[nodiscard]] std::string toUtf8(const sf::String& string) +{ + const auto utf8 = string.toUtf8(); + return {utf8.begin(), utf8.end()}; +} + + +//////////////////////////////////////////////////////////// +[[nodiscard]] std::vector convertServers(const sfIpAddress* servers, size_t serverCount) +{ + std::vector result; + + if (!servers) + return result; + + result.reserve(serverCount); + for (size_t i = 0; i < serverCount; ++i) + { + if (const auto address = convertIpAddress(servers[i])) + result.push_back(*address); + } + + return result; +} + + +//////////////////////////////////////////////////////////// +[[nodiscard]] std::optional convertTimeout(sfTime timeout) +{ + if (timeout.microseconds == 0) + return std::nullopt; + + return sf::microseconds(timeout.microseconds); +} +} // namespace + + +//////////////////////////////////////////////////////////// +sfIpAddress* sfDns_resolve(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout, size_t* count) +{ + assert(hostname); + assert(count); + + *count = 0; + + const auto addresses = sf::Dns::resolve(fromUtf8(hostname), convertServers(servers, serverCount), convertTimeout(timeout)); + if (!addresses || addresses->empty()) + return nullptr; + + auto* result = static_cast(std::malloc(addresses->size() * sizeof(sfIpAddress))); + if (!result) + return nullptr; + + for (std::size_t i = 0; i < addresses->size(); ++i) + result[i] = convertIpAddress((*addresses)[i]); + + *count = addresses->size(); + return result; +} + + +//////////////////////////////////////////////////////////// +sfDnsNsRecords* sfDns_queryNs(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout) +{ + assert(hostname); + + auto* records = new sfDnsNsRecords; + for (const auto& record : + sf::Dns::queryNs(fromUtf8(hostname), convertServers(servers, serverCount), convertTimeout(timeout))) + records->Records.push_back(toUtf8(record)); + + return records; +} + + +//////////////////////////////////////////////////////////// +sfDnsMxRecords* sfDns_queryMx(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout) +{ + assert(hostname); + + auto* records = new sfDnsMxRecords; + for (const auto& record : + sf::Dns::queryMx(fromUtf8(hostname), convertServers(servers, serverCount), convertTimeout(timeout))) + records->Records.push_back({toUtf8(record.exchange), record.preference}); + + return records; +} + + +//////////////////////////////////////////////////////////// +sfDnsSrvRecords* sfDns_querySrv(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout) +{ + assert(hostname); + + auto* records = new sfDnsSrvRecords; + for (const auto& record : + sf::Dns::querySrv(fromUtf8(hostname), convertServers(servers, serverCount), convertTimeout(timeout))) + records->Records.push_back({toUtf8(record.target), record.port, record.weight, record.priority}); + + return records; +} + + +//////////////////////////////////////////////////////////// +sfDnsTxtRecords* sfDns_queryTxt(const char* hostname, const sfIpAddress* servers, size_t serverCount, sfTime timeout) +{ + assert(hostname); + + auto* records = new sfDnsTxtRecords; + for (const auto& record : + sf::Dns::queryTxt(fromUtf8(hostname), convertServers(servers, serverCount), convertTimeout(timeout))) + { + auto& strings = records->Records.emplace_back(); + for (const auto& string : record) + strings.push_back(toUtf8(string)); + } + + return records; +} + + +//////////////////////////////////////////////////////////// +sfIpAddress sfDns_getPublicAddress(sfTime timeout, sfIpAddressType type) +{ + return convertIpAddress(sf::Dns::getPublicAddress(convertTimeout(timeout), static_cast(type))); +} + + +//////////////////////////////////////////////////////////// +void sfDnsNsRecords_destroy(const sfDnsNsRecords* records) +{ + delete records; +} + + +//////////////////////////////////////////////////////////// +size_t sfDnsNsRecords_getCount(const sfDnsNsRecords* records) +{ + assert(records); + return records->Records.size(); +} + + +//////////////////////////////////////////////////////////// +const char* sfDnsNsRecords_getRecord(const sfDnsNsRecords* records, size_t index) +{ + assert(records); + assert(index < records->Records.size()); + return records->Records[index].c_str(); +} + + +//////////////////////////////////////////////////////////// +void sfDnsMxRecords_destroy(const sfDnsMxRecords* records) +{ + delete records; +} + + +//////////////////////////////////////////////////////////// +size_t sfDnsMxRecords_getCount(const sfDnsMxRecords* records) +{ + assert(records); + return records->Records.size(); +} + + +//////////////////////////////////////////////////////////// +sfDnsMxRecord sfDnsMxRecords_getRecord(const sfDnsMxRecords* records, size_t index) +{ + assert(records); + assert(index < records->Records.size()); + + const auto& record = records->Records[index]; + return {record.exchange.c_str(), record.preference}; +} + + +//////////////////////////////////////////////////////////// +void sfDnsSrvRecords_destroy(const sfDnsSrvRecords* records) +{ + delete records; +} + + +//////////////////////////////////////////////////////////// +size_t sfDnsSrvRecords_getCount(const sfDnsSrvRecords* records) +{ + assert(records); + return records->Records.size(); +} + + +//////////////////////////////////////////////////////////// +sfDnsSrvRecord sfDnsSrvRecords_getRecord(const sfDnsSrvRecords* records, size_t index) +{ + assert(records); + assert(index < records->Records.size()); + + const auto& record = records->Records[index]; + return {record.target.c_str(), record.port, record.weight, record.priority}; +} + + +//////////////////////////////////////////////////////////// +void sfDnsTxtRecords_destroy(const sfDnsTxtRecords* records) +{ + delete records; +} + + +//////////////////////////////////////////////////////////// +size_t sfDnsTxtRecords_getCount(const sfDnsTxtRecords* records) +{ + assert(records); + return records->Records.size(); +} + + +//////////////////////////////////////////////////////////// +size_t sfDnsTxtRecords_getStringCount(const sfDnsTxtRecords* records, size_t index) +{ + assert(records); + assert(index < records->Records.size()); + return records->Records[index].size(); +} + + +//////////////////////////////////////////////////////////// +const char* sfDnsTxtRecords_getString(const sfDnsTxtRecords* records, size_t index, size_t stringIndex) +{ + assert(records); + assert(index < records->Records.size()); + assert(stringIndex < records->Records[index].size()); + return records->Records[index][stringIndex].c_str(); +} diff --git a/src/CSFML/Network/DnsStruct.hpp b/src/CSFML/Network/DnsStruct.hpp new file mode 100644 index 0000000..31977b2 --- /dev/null +++ b/src/CSFML/Network/DnsStruct.hpp @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +//////////////////////////////////////////////////////////// +// Internal structure of sfDnsNsRecords +//////////////////////////////////////////////////////////// +struct sfDnsNsRecords +{ + std::vector Records; +}; + + +//////////////////////////////////////////////////////////// +// Internal structure of sfDnsMxRecords +//////////////////////////////////////////////////////////// +struct sfDnsMxRecords +{ + struct Record + { + std::string exchange; + std::uint16_t preference{}; + }; + + std::vector Records; +}; + + +//////////////////////////////////////////////////////////// +// Internal structure of sfDnsSrvRecords +//////////////////////////////////////////////////////////// +struct sfDnsSrvRecords +{ + struct Record + { + std::string target; + std::uint16_t port{}; + std::uint16_t weight{}; + std::uint16_t priority{}; + }; + + std::vector Records; +}; + + +//////////////////////////////////////////////////////////// +// Internal structure of sfDnsTxtRecords +//////////////////////////////////////////////////////////// +struct sfDnsTxtRecords +{ + std::vector> Records; +}; diff --git a/src/CSFML/Network/Ftp.cpp b/src/CSFML/Network/Ftp.cpp index 0cf6402..c255be5 100644 --- a/src/CSFML/Network/Ftp.cpp +++ b/src/CSFML/Network/Ftp.cpp @@ -25,6 +25,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include @@ -205,7 +206,7 @@ sfFtpResponse* sfFtp_connect(sfFtp* ftp, sfIpAddress server, unsigned short port { assert(ftp); - std::optional sfmlServer = sf::IpAddress::resolve(server.address); + std::optional sfmlServer = convertIpAddress(server); if (!sfmlServer) return nullptr; diff --git a/src/CSFML/Network/IpAddress.cpp b/src/CSFML/Network/IpAddress.cpp index d48a66d..f5c80dc 100644 --- a/src/CSFML/Network/IpAddress.cpp +++ b/src/CSFML/Network/IpAddress.cpp @@ -25,63 +25,90 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include +#include +#include #include -namespace -{ -// Helper function for converting a SFML address to a CSFML one -[[nodiscard]] sfIpAddress fromSFMLAddress(std::optional address) -{ - if (!address) - return sfIpAddress_None; - - sfIpAddress result{}; - std::strncpy(result.address, address->toString().c_str(), 15); - return result; -} -} // namespace - - //////////////////////////////////////////////////////////// const sfIpAddress sfIpAddress_None = {{0}}; //////////////////////////////////////////////////////////// -const sfIpAddress sfIpAddress_Any = sfIpAddress_fromBytes(0, 0, 0, 0); +const sfIpAddress sfIpAddress_Any = {"0.0.0.0"}; //////////////////////////////////////////////////////////// -const sfIpAddress sfIpAddress_LocalHost = sfIpAddress_fromBytes(127, 0, 0, 1); +const sfIpAddress sfIpAddress_LocalHost = {"127.0.0.1"}; //////////////////////////////////////////////////////////// -const sfIpAddress sfIpAddress_Broadcast = sfIpAddress_fromBytes(255, 255, 255, 255); +const sfIpAddress sfIpAddress_Broadcast = {"255.255.255.255"}; + + +//////////////////////////////////////////////////////////// +const sfIpAddress sfIpAddress_AnyV4 = {"0.0.0.0"}; + + +//////////////////////////////////////////////////////////// +const sfIpAddress sfIpAddress_LocalHostV4 = {"127.0.0.1"}; + + +//////////////////////////////////////////////////////////// +const sfIpAddress sfIpAddress_BroadcastV4 = {"255.255.255.255"}; + + +//////////////////////////////////////////////////////////// +const sfIpAddress sfIpAddress_AnyV6 = {"::"}; + + +//////////////////////////////////////////////////////////// +const sfIpAddress sfIpAddress_LocalHostV6 = {"::1"}; //////////////////////////////////////////////////////////// sfIpAddress sfIpAddress_fromString(const char* address) { assert(address); - return fromSFMLAddress(sf::IpAddress::resolve(address)); + return convertIpAddress(sf::IpAddress::fromString(address)); +} + + +//////////////////////////////////////////////////////////// +sfIpAddress sfIpAddress_resolve(const char* address) +{ + assert(address); + return convertIpAddress(sf::IpAddress::resolve(address)); } //////////////////////////////////////////////////////////// sfIpAddress sfIpAddress_fromBytes(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3) { - return fromSFMLAddress(sf::IpAddress(byte0, byte1, byte2, byte3)); + return convertIpAddress(sf::IpAddress(byte0, byte1, byte2, byte3)); } //////////////////////////////////////////////////////////// sfIpAddress sfIpAddress_fromInteger(uint32_t address) { - return fromSFMLAddress(sf::IpAddress(address)); + return convertIpAddress(sf::IpAddress(address)); +} + + +//////////////////////////////////////////////////////////// +sfIpAddress sfIpAddress_fromV6Bytes(const uint8_t bytes[16]) +{ + assert(bytes); + + std::array array{}; + std::copy(bytes, bytes + array.size(), array.begin()); + return convertIpAddress(sf::IpAddress(array)); } @@ -96,20 +123,74 @@ void sfIpAddress_toString(sfIpAddress address, char* string) //////////////////////////////////////////////////////////// uint32_t sfIpAddress_toInteger(sfIpAddress address) { - const auto sfmlAddress = sf::IpAddress::resolve(address.address); - return sfmlAddress ? sfmlAddress->toInteger() : 0; + const auto sfmlAddress = convertIpAddress(address); + return sfmlAddress && sfmlAddress->isV4() ? sfmlAddress->toInteger() : 0; +} + + +//////////////////////////////////////////////////////////// +bool sfIpAddress_toV6Bytes(sfIpAddress address, uint8_t bytes[16]) +{ + assert(bytes); + + const auto sfmlAddress = convertIpAddress(address); + if (!sfmlAddress || !sfmlAddress->isV6()) + return false; + + const auto array = sfmlAddress->toBytes(); + std::copy(array.begin(), array.end(), bytes); + return true; +} + + +//////////////////////////////////////////////////////////// +sfIpAddressType sfIpAddress_getType(sfIpAddress address) +{ + const auto sfmlAddress = convertIpAddress(address); + return sfmlAddress ? static_cast(sfmlAddress->getType()) : sfIpAddressV4; +} + + +//////////////////////////////////////////////////////////// +bool sfIpAddress_isV4(sfIpAddress address) +{ + const auto sfmlAddress = convertIpAddress(address); + return sfmlAddress && sfmlAddress->isV4(); +} + + +//////////////////////////////////////////////////////////// +bool sfIpAddress_isV6(sfIpAddress address) +{ + const auto sfmlAddress = convertIpAddress(address); + return sfmlAddress && sfmlAddress->isV6(); } //////////////////////////////////////////////////////////// sfIpAddress sfIpAddress_getLocalAddress() { - return fromSFMLAddress(sf::IpAddress::getLocalAddress()); + return convertIpAddress(sf::IpAddress::getLocalAddress()); +} + + +//////////////////////////////////////////////////////////// +sfIpAddress sfIpAddress_getLocalAddressOfType(sfIpAddressType type) +{ + return convertIpAddress(sf::IpAddress::getLocalAddress(static_cast(type))); } //////////////////////////////////////////////////////////// sfIpAddress sfIpAddress_getPublicAddress(sfTime timeout) { - return fromSFMLAddress(sf::IpAddress::getPublicAddress(sf::microseconds(timeout.microseconds))); + return convertIpAddress(sf::IpAddress::getPublicAddress(sf::microseconds(timeout.microseconds))); +} + + +//////////////////////////////////////////////////////////// +sfIpAddress sfIpAddress_getPublicAddressOfType(sfTime timeout, const sfIpAddressType* type, bool secure) +{ + return convertIpAddress( + sf::IpAddress::getPublicAddress(sf::microseconds(timeout.microseconds), convertIpAddressType(type), secure)); } diff --git a/src/CSFML/Network/TcpListener.cpp b/src/CSFML/Network/TcpListener.cpp index a1d0318..195846a 100644 --- a/src/CSFML/Network/TcpListener.cpp +++ b/src/CSFML/Network/TcpListener.cpp @@ -25,6 +25,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include #include @@ -75,7 +76,7 @@ sfSocketStatus sfTcpListener_listen(sfTcpListener* listener, unsigned short port { assert(listener); - std::optional sfmlAddress = sf::IpAddress::resolve(address.address); + std::optional sfmlAddress = convertIpAddress(address); if (!sfmlAddress) { diff --git a/src/CSFML/Network/TcpSocket.cpp b/src/CSFML/Network/TcpSocket.cpp index 599a83b..5308533 100644 --- a/src/CSFML/Network/TcpSocket.cpp +++ b/src/CSFML/Network/TcpSocket.cpp @@ -25,6 +25,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include #include @@ -82,7 +83,7 @@ sfIpAddress sfTcpSocket_getRemoteAddress(const sfTcpSocket* socket) sfIpAddress result = sfIpAddress_None; if (address) { - std::strncpy(result.address, address->toString().c_str(), 15); + result = convertIpAddress(*address); } return result; @@ -100,7 +101,7 @@ unsigned short sfTcpSocket_getRemotePort(const sfTcpSocket* socket) //////////////////////////////////////////////////////////// sfSocketStatus sfTcpSocket_connect(sfTcpSocket* socket, sfIpAddress remoteAddress, unsigned short remotePort, sfTime timeout) { - std::optional address = sf::IpAddress::resolve(remoteAddress.address); + std::optional address = convertIpAddress(remoteAddress); if (!address) { diff --git a/src/CSFML/Network/UdpSocket.cpp b/src/CSFML/Network/UdpSocket.cpp index 05f1778..e188273 100644 --- a/src/CSFML/Network/UdpSocket.cpp +++ b/src/CSFML/Network/UdpSocket.cpp @@ -25,6 +25,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include #include @@ -76,7 +77,7 @@ sfSocketStatus sfUdpSocket_bind(sfUdpSocket* socket, unsigned short port, sfIpAd { assert(socket); - std::optional sfmlAddress = sf::IpAddress::resolve(address.address); + std::optional sfmlAddress = convertIpAddress(address); if (!sfmlAddress) { @@ -101,7 +102,7 @@ sfSocketStatus sfUdpSocket_send(sfUdpSocket* socket, const void* data, size_t si assert(socket); // Convert the address - std::optional address = sf::IpAddress::resolve(remoteAddress.address); + std::optional address = convertIpAddress(remoteAddress); if (!address) { @@ -139,7 +140,7 @@ sfSocketStatus sfUdpSocket_receive(sfUdpSocket* socket, if (address) { - std::strncpy(remoteAddress->address, address->toString().c_str(), 15); + *remoteAddress = convertIpAddress(*address); } } @@ -157,7 +158,7 @@ sfSocketStatus sfUdpSocket_sendPacket(sfUdpSocket* socket, sfPacket* packet, sfI assert(packet); // Convert the address - std::optional address = sf::IpAddress::resolve(remoteAddress.address); + std::optional address = convertIpAddress(remoteAddress); if (!address) { @@ -187,7 +188,7 @@ sfSocketStatus sfUdpSocket_receivePacket(sfUdpSocket* socket, sfPacket* packet, if (address) { - std::strncpy(remoteAddress->address, address->toString().c_str(), 15); + *remoteAddress = convertIpAddress(*address); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 332962e..c944cab 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -66,12 +66,13 @@ set_target_warnings(test-csfml-graphics) catch_discover_tests(test-csfml-graphics WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_executable(test-csfml-network + Network/Dns.test.cpp Network/Ftp.test.cpp Network/Http.test.cpp Network/IpAddress.test.cpp Network/SocketStatus.test.cpp ) -target_link_libraries(test-csfml-network PRIVATE csfml-network Catch2::Catch2WithMain SFML::Network) +target_link_libraries(test-csfml-network PRIVATE csfml-network csfml-system Catch2::Catch2WithMain SFML::Network) set_target_warnings(test-csfml-network) catch_discover_tests(test-csfml-network) diff --git a/test/Network/Dns.test.cpp b/test/Network/Dns.test.cpp new file mode 100644 index 0000000..61bde5c --- /dev/null +++ b/test/Network/Dns.test.cpp @@ -0,0 +1,25 @@ +#include +#include + +#include + +#include + +TEST_CASE("[Network] sfDns") +{ + SECTION("sfDns_resolve") + { + size_t count = 0; + sfIpAddress* addresses = sfDns_resolve("127.0.0.1", nullptr, 0, sfSeconds(1), &count); + REQUIRE(addresses != nullptr); + REQUIRE(count == 1); + CHECK(std::strcmp(addresses[0].address, "127.0.0.1") == 0); + sfFree(addresses); + + addresses = sfDns_resolve("::1", nullptr, 0, sfSeconds(1), &count); + REQUIRE(addresses != nullptr); + REQUIRE(count == 1); + CHECK(std::strcmp(addresses[0].address, "::1") == 0); + sfFree(addresses); + } +} diff --git a/test/Network/IpAddress.test.cpp b/test/Network/IpAddress.test.cpp index 1becf66..99134c2 100644 --- a/test/Network/IpAddress.test.cpp +++ b/test/Network/IpAddress.test.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include @@ -17,15 +19,62 @@ TEST_CASE("[Network] sfIpAddress") CHECK(std::strcmp(sfIpAddress_Any.address, "0.0.0.0") == 0); CHECK(std::strcmp(sfIpAddress_LocalHost.address, "127.0.0.1") == 0); CHECK(std::strcmp(sfIpAddress_Broadcast.address, "255.255.255.255") == 0); + + CHECK(std::strcmp(sfIpAddress_AnyV4.address, "0.0.0.0") == 0); + CHECK(std::strcmp(sfIpAddress_LocalHostV4.address, "127.0.0.1") == 0); + CHECK(std::strcmp(sfIpAddress_BroadcastV4.address, "255.255.255.255") == 0); + CHECK(std::strcmp(sfIpAddress_AnyV6.address, "::") == 0); + CHECK(std::strcmp(sfIpAddress_LocalHostV6.address, "::1") == 0); + } + + SECTION("sfIpAddressType") + { + STATIC_CHECK(sfIpAddressV4 == static_cast(sf::IpAddress::Type::IpV4)); + STATIC_CHECK(sfIpAddressV6 == static_cast(sf::IpAddress::Type::IpV6)); } SECTION("sfIpAddress_fromString") { CHECK(sfIpAddress_toInteger(sfIpAddress_fromString("")) == 0); CHECK(sfIpAddress_toInteger(sfIpAddress_fromString("256.256.256.256")) == 0); - CHECK(sfIpAddress_toInteger(sfIpAddress_fromString("localhost")) == 0x7F000001); + CHECK(sfIpAddress_toInteger(sfIpAddress_fromString("localhost")) == 0); CHECK(sfIpAddress_toInteger(sfIpAddress_fromString("192.168.0.1")) == 0xC0A80001); CHECK(sfIpAddress_toInteger(sfIpAddress_fromString("8.8.8.8")) == 0x08080808); + CHECK(std::strcmp(sfIpAddress_fromString("2606:4700:4700::1111").address, "2606:4700:4700::1111") == 0); + CHECK(std::strcmp(sfIpAddress_fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").address, + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") == 0); + } + + SECTION("sfIpAddress_resolve") + { + CHECK(sfIpAddress_toInteger(sfIpAddress_resolve("")) == 0); + CHECK(sfIpAddress_toInteger(sfIpAddress_resolve("localhost")) == 0x7F000001); + CHECK(sfIpAddress_toInteger(sfIpAddress_resolve("192.168.0.1")) == 0xC0A80001); + } + + SECTION("Type") + { + CHECK(sfIpAddress_isV4(sfIpAddress_LocalHostV4)); + CHECK(!sfIpAddress_isV6(sfIpAddress_LocalHostV4)); + CHECK(sfIpAddress_getType(sfIpAddress_LocalHostV4) == sfIpAddressV4); + CHECK(!sfIpAddress_isV4(sfIpAddress_LocalHostV6)); + CHECK(sfIpAddress_isV6(sfIpAddress_LocalHostV6)); + CHECK(sfIpAddress_getType(sfIpAddress_LocalHostV6) == sfIpAddressV6); + CHECK(!sfIpAddress_isV4(sfIpAddress_None)); + CHECK(!sfIpAddress_isV6(sfIpAddress_None)); + } + + SECTION("sfIpAddress_fromV6Bytes") + { + const uint8_t bytes[16] = {0x26, 0x06, 0x47, 0x00, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0x11, 0x11}; + const sfIpAddress address = sfIpAddress_fromV6Bytes(bytes); + CHECK(std::strcmp(address.address, "2606:4700:4700::1111") == 0); + CHECK(sfIpAddress_toInteger(address) == 0); + + uint8_t result[16] = {}; + CHECK(sfIpAddress_toV6Bytes(address, result)); + CHECK(std::memcmp(bytes, result, sizeof(bytes)) == 0); + CHECK(!sfIpAddress_toV6Bytes(sfIpAddress_LocalHostV4, result)); } SECTION("sfIpAddress_fromBytes")