diff --git a/src/mavsdk/core/CMakeLists.txt b/src/mavsdk/core/CMakeLists.txt index 86f48f348..ff2a460de 100644 --- a/src/mavsdk/core/CMakeLists.txt +++ b/src/mavsdk/core/CMakeLists.txt @@ -24,6 +24,7 @@ target_sources(mavsdk file_cache.cpp flight_mode.cpp fs_utils.cpp + hostname_to_ip.cpp inflate_lzma.cpp math_conversions.cpp mavsdk.cpp @@ -85,8 +86,9 @@ if (NOT BUILD_WITHOUT_CURL) ) list(APPEND UNIT_TEST_SOURCES - # TODO: add this again ${PROJECT_SOURCE_DIR}/mavsdk/core/curl_test.cpp + ${PROJECT_SOURCE_DIR}/mavsdk/core/hostname_to_ip_test.cpp + # TODO: add this again #${PROJECT_SOURCE_DIR}/mavsdk/core/http_loader_test.cpp ) else() diff --git a/src/mavsdk/core/hostname_to_ip.cpp b/src/mavsdk/core/hostname_to_ip.cpp new file mode 100644 index 000000000..d008211a6 --- /dev/null +++ b/src/mavsdk/core/hostname_to_ip.cpp @@ -0,0 +1,60 @@ +#include "hostname_to_ip.h" +#include "log.h" + +#if defined(WINDOWS) +#include +#include +#pragma comment(lib, "ws2_32.lib") +#else +#include +#include +#include +#include +#endif + +namespace mavsdk { + +std::optional resolve_hostname_to_ip(const std::string& hostname) +{ +#if defined(WINDOWS) + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "WSAStartup failed" << std::endl; + return {}; + } +#endif + + addrinfo hints{}; + hints.ai_family = AF_INET; // IPv4 + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + addrinfo* result = nullptr; + int res = getaddrinfo(hostname.c_str(), nullptr, &hints, &result); + if (res != 0) { +#if defined(WINDOWS) + LogErr() << "getaddrinfo failed: " << WSAGetLastError(); + WSACleanup(); +#else + LogErr() << "getaddrinfo failed: " << gai_strerror(res); +#endif + return {}; + } + + std::string ipAddress; + for (addrinfo* ptr = result; ptr != nullptr; ptr = ptr->ai_next) { + sockaddr_in* sockaddrIpv4 = reinterpret_cast(ptr->ai_addr); + char ipStr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(sockaddrIpv4->sin_addr), ipStr, INET_ADDRSTRLEN); + ipAddress = ipStr; + break; // Take the first result + } + + freeaddrinfo(result); +#if defined(WINDOWS) + WSACleanup(); +#endif + return {ipAddress}; +} + +} // namespace mavsdk diff --git a/src/mavsdk/core/hostname_to_ip.h b/src/mavsdk/core/hostname_to_ip.h new file mode 100644 index 000000000..8bedd6425 --- /dev/null +++ b/src/mavsdk/core/hostname_to_ip.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace mavsdk { + +std::optional resolve_hostname_to_ip(const std::string& hostname); + +} // namespace mavsdk diff --git a/src/mavsdk/core/hostname_to_ip_test.cpp b/src/mavsdk/core/hostname_to_ip_test.cpp new file mode 100644 index 000000000..1f716c632 --- /dev/null +++ b/src/mavsdk/core/hostname_to_ip_test.cpp @@ -0,0 +1,34 @@ +#include "hostname_to_ip.h" +#include + +using namespace mavsdk; + +TEST(HostnameToIp, Localhost) +{ + auto host = "localhost"; + auto ip = resolve_hostname_to_ip(host); + ASSERT_TRUE(ip); + EXPECT_EQ(ip.value(), "127.0.0.1"); +} + +TEST(HostnameToIp, Ip) +{ + auto host = "127.0.0.1"; + auto ip = resolve_hostname_to_ip(host); + ASSERT_TRUE(ip); + EXPECT_EQ(ip.value(), host); +} + +TEST(HostnameToIp, Empty) +{ + auto host = ""; + auto ip = resolve_hostname_to_ip(host); + EXPECT_FALSE(ip); +} + +TEST(HostnameToIp, Something) +{ + auto host = "something"; + auto ip = resolve_hostname_to_ip(host); + EXPECT_FALSE(ip); +} diff --git a/src/mavsdk/core/mavsdk_impl.cpp b/src/mavsdk/core/mavsdk_impl.cpp index ed6ad4acd..5b5e16639 100644 --- a/src/mavsdk/core/mavsdk_impl.cpp +++ b/src/mavsdk/core/mavsdk_impl.cpp @@ -16,6 +16,7 @@ #include "plugin_base.h" #include "mavlink_channels.h" #include "callback_list.tpp" +#include "hostname_to_ip.h" namespace mavsdk { @@ -538,7 +539,15 @@ MavsdkImpl::add_udp_connection(const CliArg::Udp& udp, ForwardingOption forwardi auto handle = add_connection(new_conn); if (udp.mode == CliArg::Udp::Mode::Out) { - new_conn->add_remote(udp.host, udp.port); + // We need to add the IP rather than a hostname, otherwise we end up with two remotes: + // one for the IP, and one for a hostname. + auto remote_ip = resolve_hostname_to_ip(udp.host); + + if (!remote_ip) { + return {ConnectionResult::DestinationIpUnknown, Mavsdk::ConnectionHandle{}}; + } + + new_conn->add_remote(remote_ip.value(), udp.port); std::lock_guard lock(_systems_mutex); // With a UDP remote, we need to initiate the connection by sending heartbeats.