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