xref: /openbmc/bmcweb/include/async_resolve.hpp (revision bd79bce8)
1 #pragma once
2 #include "dbus_singleton.hpp"
3 #include "logging.hpp"
4 
5 #include <boost/asio/ip/address.hpp>
6 #include <boost/asio/ip/basic_endpoint.hpp>
7 #include <boost/asio/ip/tcp.hpp>
8 #include <sdbusplus/message.hpp>
9 
10 #include <charconv>
11 #include <memory>
12 
13 namespace async_resolve
14 {
15 
endpointFromResolveTuple(const std::vector<uint8_t> & ipAddress,boost::asio::ip::tcp::endpoint & endpoint)16 inline bool endpointFromResolveTuple(const std::vector<uint8_t>& ipAddress,
17                                      boost::asio::ip::tcp::endpoint& endpoint)
18 {
19     if (ipAddress.size() == 4) // ipv4 address
20     {
21         BMCWEB_LOG_DEBUG("ipv4 address");
22         boost::asio::ip::address_v4 ipv4Addr(
23             {ipAddress[0], ipAddress[1], ipAddress[2], ipAddress[3]});
24         endpoint.address(ipv4Addr);
25     }
26     else if (ipAddress.size() == 16) // ipv6 address
27     {
28         BMCWEB_LOG_DEBUG("ipv6 address");
29         boost::asio::ip::address_v6 ipv6Addr(
30             {ipAddress[0], ipAddress[1], ipAddress[2], ipAddress[3],
31              ipAddress[4], ipAddress[5], ipAddress[6], ipAddress[7],
32              ipAddress[8], ipAddress[9], ipAddress[10], ipAddress[11],
33              ipAddress[12], ipAddress[13], ipAddress[14], ipAddress[15]});
34         endpoint.address(ipv6Addr);
35     }
36     else
37     {
38         BMCWEB_LOG_ERROR("Resolve failed to fetch the IP address");
39         return false;
40     }
41     return true;
42 }
43 
44 class Resolver
45 {
46   public:
47     // unused io param used to keep interface identical to
48     // boost::asio::tcp:::resolver
Resolver(boost::asio::io_context &)49     explicit Resolver(boost::asio::io_context& /*io*/) {}
50 
51     ~Resolver() = default;
52 
53     Resolver(const Resolver&) = delete;
54     Resolver(Resolver&&) = delete;
55     Resolver& operator=(const Resolver&) = delete;
56     Resolver& operator=(Resolver&&) = delete;
57 
58     using results_type = std::vector<boost::asio::ip::tcp::endpoint>;
59 
60     template <typename ResolveHandler>
61     // This function is kept using snake case so that it is interoperable with
62     // boost::asio::ip::tcp::resolver
63     // NOLINTNEXTLINE(readability-identifier-naming)
async_resolve(std::string_view host,std::string_view port,ResolveHandler && handler)64     void async_resolve(std::string_view host, std::string_view port,
65                        ResolveHandler&& handler)
66     {
67         BMCWEB_LOG_DEBUG("Trying to resolve: {}:{}", host, port);
68 
69         uint16_t portNum = 0;
70 
71         auto it = std::from_chars(&*port.begin(), &*port.end(), portNum);
72         if (it.ec != std::errc())
73         {
74             BMCWEB_LOG_ERROR("Failed to get the Port");
75             handler(std::make_error_code(std::errc::invalid_argument),
76                     results_type{});
77 
78             return;
79         }
80 
81         uint64_t flag = 0;
82         crow::connections::systemBus->async_method_call(
83             [host{std::string(host)}, portNum,
84              handler = std::forward<ResolveHandler>(handler)](
85                 const boost::system::error_code& ec,
86                 const std::vector<
87                     std::tuple<int32_t, int32_t, std::vector<uint8_t>>>& resp,
88                 const std::string& hostName, const uint64_t flagNum) {
89                 results_type endpointList;
90                 if (ec)
91                 {
92                     BMCWEB_LOG_ERROR("Resolve failed: {}", ec.message());
93                     handler(ec, endpointList);
94                     return;
95                 }
96                 BMCWEB_LOG_DEBUG("ResolveHostname returned: {}:{}", hostName,
97                                  flagNum);
98                 // Extract the IP address from the response
99                 for (const std::tuple<int32_t, int32_t, std::vector<uint8_t>>&
100                          resolveList : resp)
101                 {
102                     boost::asio::ip::tcp::endpoint endpoint;
103                     endpoint.port(portNum);
104                     if (!endpointFromResolveTuple(std::get<2>(resolveList),
105                                                   endpoint))
106                     {
107                         boost::system::error_code ecErr = make_error_code(
108                             boost::system::errc::address_not_available);
109                         handler(ecErr, endpointList);
110                     }
111                     BMCWEB_LOG_DEBUG("resolved endpoint is : {}",
112                                      endpoint.address().to_string());
113                     endpointList.push_back(endpoint);
114                 }
115                 // All the resolved data is filled in the endpointList
116                 handler(ec, endpointList);
117             },
118             "org.freedesktop.resolve1", "/org/freedesktop/resolve1",
119             "org.freedesktop.resolve1.Manager", "ResolveHostname", 0, host,
120             AF_UNSPEC, flag);
121     }
122 };
123 
124 } // namespace async_resolve
125