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