Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add c-ares example #48

Merged
merged 1 commit into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions cmake-modules/Findc-ares.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#
# This file is open source software, licensed to you under the terms
# of the Apache License, Version 2.0 (the "License"). See the NOTICE file
# distributed with this work for additional information regarding copyright
# ownership. You may not use this file except in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

#
# Copyright (C) 2018 Scylladb, Ltd.
#

find_package (PkgConfig)

if (PkgConfig_FOUND)

pkg_check_modules (c-ares_PC libcares)

find_library (c-ares_LIBRARY
NAMES cares
HINTS
${c-ares_PC_LIBDIR}
${c-ares_PC_LIBRARY_DIRS})

find_path (c-ares_INCLUDE_DIR
NAMES ares_dns.h
HINTS
${c-ares_PC_INCLUDEDIR}
${c-ares_PC_INCLUDE_DIRS})

mark_as_advanced (
c-ares_LIBRARY
c-ares_INCLUDE_DIR)

include (FindPackageHandleStandardArgs)

find_package_handle_standard_args (c-ares
REQUIRED_VARS
c-ares_LIBRARY
c-ares_INCLUDE_DIR
VERSION_VAR c-ares_PC_VERSION)

set (c-ares_LIBRARIES ${c-ares_LIBRARY})
set (c-ares_INCLUDE_DIRS ${c-ares_INCLUDE_DIR})

if (c-ares_FOUND AND NOT (TARGET c-ares::cares))
add_library (c-ares::cares UNKNOWN IMPORTED)

set_target_properties (c-ares::cares
PROPERTIES
IMPORTED_LOCATION ${c-ares_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES ${c-ares_INCLUDE_DIRS})
endif ()

endif()
6 changes: 6 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,9 @@ if (LIBEVENT_FOUND)
endif(LIBEVENT_FOUND)

add_subdirectory(test_executor)

find_package(c-ares)

if(c-ares_FOUND)
add_subdirectory(test_cares)
endif()
6 changes: 6 additions & 0 deletions tests/test_cares/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

add_executable(testc-ares test.cpp)
target_link_libraries(testc-ares ucoro c-ares::cares)

add_test(NAME testc-ares COMMAND testc-ares)
set_target_properties(testc-ares PROPERTIES FOLDER "ucoro_tests")
125 changes: 125 additions & 0 deletions tests/test_cares/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include <iostream>
#include "ucoro/awaitable.hpp"

#include <ares.h>


////////////////////////////////////////////////////////////////////
template <auto fn>
struct deleter
{
template <typename T>
constexpr void operator()(T* arg) const
{
fn(arg);
}
};

template <typename T, auto fn>
using auto_delete = std::unique_ptr<T, deleter<fn>>;
////////////////////////////////////////////////////////////////////
ucoro::awaitable<std::vector<sockaddr>> aync_cares_query(std::string host)
{
ares_channel_t* channel = co_await ucoro::local_storage_t<ares_channel_t*>();

/* Perform an IPv4 and IPv6 request for the provided domain name */
co_return co_await callback_awaitable<std::vector<sockaddr>>([channel, host](auto continuation_handle) mutable
{
ares_addrinfo_hints hints = { 0 };
hints.ai_family = AF_UNSPEC;
hints.ai_flags = ARES_AI_CANONNAME;

/* Callback that is called when DNS query is finished */
auto addrinfo_cb = [](void *arg, int status, int timeouts, struct ares_addrinfo *result)
{
// 这里的 magic 是 decltype(continuation_handle) 类型,获取到了 resume 对象的指针.
auto continuation_handle_p = reinterpret_cast<decltype(continuation_handle)*>(arg);

auto_delete<decltype(continuation_handle), [](auto p){ delete p;}>
auto_delete_continuation_handle{continuation_handle_p};

std::vector<sockaddr> ret;

auto_delete<ares_addrinfo, ares_freeaddrinfo> auto_delete_result(result);

if (result)
{
struct ares_addrinfo_node *node;
for (node = result->nodes; node != NULL; node = node->ai_next)
{
ret.push_back(*(node->ai_addr));
}
}

(*continuation_handle_p)(ret);
};

// continuation_handle 这里被移动构造了一个 堆 上的版本。
ares_getaddrinfo(channel, host.c_str(), NULL, &hints, addrinfo_cb, new decltype(continuation_handle){std::move(continuation_handle)});
});
}


ucoro::awaitable<void> coro_cares_query()
{
for (auto host : {"microcai.org", "github.com", "google.com", "baidu.com", "boost.org"})
{
auto dns_result = co_await aync_cares_query(host);

printf("===================\n");
std::cout << "query for \"" << host << "\" result:" << std::endl;

for (auto sock_address : dns_result)
{
char addr_buf[64] = "";
const void *ptr = NULL;
if (sock_address.sa_family == AF_INET)
{
const struct sockaddr_in *in_addr =
(const struct sockaddr_in *)((void *) &sock_address);
ptr = &in_addr->sin_addr;
}
else if (sock_address.sa_family == AF_INET6)
{
const struct sockaddr_in6 *in_addr =
(const struct sockaddr_in6 *)((void *) &sock_address);
ptr = &in_addr->sin6_addr;
}
else
{
continue;
}
ares_inet_ntop(sock_address.sa_family, ptr, addr_buf, sizeof(addr_buf));
printf("Addr: %s\n", addr_buf);
}
}
}

int main(int argc, char **argv)
{
ares_library_init(ARES_LIB_INIT_ALL);

ares_channel_t *channel = NULL;
ares_options options = { 0 };

/* Enable event thread so we don't have to monitor file descriptors */
options.evsys = ARES_EVSYS_DEFAULT;

/* Initialize channel to run queries, a single channel can accept unlimited
* queries */
if (ares_init_options(&channel, &options, ARES_OPT_EVENT_THREAD) != ARES_SUCCESS)
{
printf("c-ares initialization issue\n");
return 1;
}

coro_start(coro_cares_query(), channel);

/* Wait until no more requests are left to be processed */
ares_queue_wait_empty(channel, -1);

/* Cleanup */
ares_destroy(channel);
ares_library_cleanup();
return 0;
}
Loading