Skip to content

Commit

Permalink
add c-ares example
Browse files Browse the repository at this point in the history
  • Loading branch information
microcai committed Oct 30, 2024
1 parent 4ab020b commit 3be250b
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 0 deletions.
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")
154 changes: 154 additions & 0 deletions tests/test_cares/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#include <iostream>
#include "ucoro/awaitable.hpp"

#include <ares.h>


////////////////////////////////////////////////////////////////////

template <typename ArgType>
struct __freeer_traits
{
typedef void (*freefunc)(ArgType*);
};

// NOTE: struct_free 不是一个类型,它就是一个函数本身!
template<typename StructType, typename __freeer_traits<StructType>::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<typename Initializer>
raii_c_ptr(Initializer&& initdata)
: _managed_resource(std::forward<Initializer>(initdata))
{
}

StructType* operator->()
{
return _managed_resource;
}

const StructType* operator->() const
{
return _managed_resource;
}

operator bool() const
{
return _managed_resource;
}
};

////////////////////////////////////////////////////////////////////
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);

std::vector<sockaddr> ret;

raii_c_ptr<ares_addrinfo, &ares_freeaddrinfo> 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<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;
}

0 comments on commit 3be250b

Please sign in to comment.