diff --git a/cmake-modules/Findc-ares.cmake b/cmake-modules/Findc-ares.cmake new file mode 100644 index 0000000..24dde41 --- /dev/null +++ b/cmake-modules/Findc-ares.cmake @@ -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() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f5fc5d9..4e00f49 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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() \ No newline at end of file diff --git a/tests/test_cares/CMakeLists.txt b/tests/test_cares/CMakeLists.txt new file mode 100644 index 0000000..0079c4c --- /dev/null +++ b/tests/test_cares/CMakeLists.txt @@ -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") \ No newline at end of file diff --git a/tests/test_cares/test.cpp b/tests/test_cares/test.cpp new file mode 100644 index 0000000..5f565c8 --- /dev/null +++ b/tests/test_cares/test.cpp @@ -0,0 +1,154 @@ +#include +#include "ucoro/awaitable.hpp" + +#include + + +//////////////////////////////////////////////////////////////////// + +template +struct __freeer_traits +{ + typedef void (*freefunc)(ArgType*); +}; + +// NOTE: struct_free 不是一个类型,它就是一个函数本身! +template::freefunc struct_free> +struct raii_c_ptr +{ + raii_c_ptr(const raii_c_ptr&) = delete; + raii_c_ptr(raii_c_ptr&&) = delete; + + StructType* _managed_resource; + + ~raii_c_ptr() + { + struct_free(_managed_resource); + } + + template + raii_c_ptr(Initializer&& initdata) + : _managed_resource(std::forward(initdata)) + { + } + + StructType* operator->() + { + return _managed_resource; + } + + const StructType* operator->() const + { + return _managed_resource; + } + + operator bool() const + { + return _managed_resource; + } +}; + +//////////////////////////////////////////////////////////////////// +ucoro::awaitable> aync_cares_query(std::string host) +{ + ares_channel_t* channel = co_await ucoro::local_storage_t(); + + /* Perform an IPv4 and IPv6 request for the provided domain name */ + co_return co_await callback_awaitable>([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(arg); + + std::vector ret; + + raii_c_ptr 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); + delete continuation_handle_p; + }; + + // continuation_handle 这里被移动构造了一个 堆 上的版本。 + ares_getaddrinfo(channel, host.c_str(), NULL, &hints, addrinfo_cb, new decltype(continuation_handle){std::move(continuation_handle)}); + }); +} + + +ucoro::awaitable 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; +}