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