UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
addrinfo.cpp
Go to the documentation of this file.
1// Copyright (C) 2023+ GPL 3 and higher by Ingo Höft, <Ingo@Hoeft-online.de>
2// Redistribution only with this Copyright remark. Last modified: 2025-03-20
9#include <UPnPsdk/synclog.hpp>
10#include <UPnPsdk/sockaddr.hpp>
11
12#include <umock/netdb.hpp>
14#include <cstring>
16
17
18namespace UPnPsdk {
19
20// CAddrinfo class to wrap ::addrinfo() system calls
21// =================================================
22// On Microsoft Windows this needs to have Windows Sockets initialized with
23// WSAStartup().
24
25// Constructor for getting an address information with service name that can
26// also be a port number string.
27// -------------------------------------------------------------------------
28CAddrinfo::CAddrinfo(std::string_view a_node, //
29 std::string_view a_service, //
30 const int a_flags, //
31 const int a_socktype)
32 : m_node(a_node), m_service(a_service) {
33 TRACE2(this, " Construct CAddrinfo() with extra service")
34 // I cannot use the initialization list of the constructor because the
35 // member order in the structure addrinfo is different on Linux, MacOS and
36 // win32. I have to use the member names to initialize them, what's not
37 // possible for structures in the constructors initialization list.
38 m_hints.ai_flags = a_flags;
39 m_hints.ai_socktype = a_socktype;
40}
41
42// Constructor for getting an address information from only a netaddress.
43// ----------------------------------------------------------------------
44CAddrinfo::CAddrinfo(std::string_view a_node, //
45 const int a_flags, //
46 const int a_socktype)
47 : m_node(a_node), m_service(a_node == "" ? "0" : "") {
48 TRACE2(this, " Construct CAddrinfo() with netaddress")
49 // I cannot use the initialization list of the constructor because the
50 // member order in the structure addrinfo is different on Linux, MacOS and
51 // win32. I have to use the member names to initialize them, what's not
52 // possible for structures in the constructors initialization list.
53 m_hints.ai_flags = a_flags;
54 m_hints.ai_socktype = a_socktype;
55}
56
57
58// Destructor
59// ----------
61CAddrinfo::~CAddrinfo() {
62 TRACE2(this, " Destruct CAddrinfo()")
63 this->free_addrinfo();
64}
66
67// Private method to free allocated memory for address information
68// ---------------------------------------------------------------
69void CAddrinfo::free_addrinfo() noexcept {
70 if (m_res != &m_hints) {
71 TRACE2("syscall ::freeaddrinfo() with m_res = ", m_res)
72 umock::netdb_h.freeaddrinfo(m_res);
73 m_res = &m_hints;
74 m_res_current = &m_hints;
75 }
76}
77
78
79// Member access operator ->
80// -------------------------
81const ::addrinfo* CAddrinfo::operator->() const noexcept {
82 return m_res_current;
83}
84
85
86// Getter for the first entry of an address info from the operating system
87// -----------------------------------------------------------------------
88// Get address information with cached hints.
90 TRACE2(this, " Executing CAddrinfo::get_first()")
91
92 // Prepare input for ::getaddrinfo()
93 std::string node, service;
94 try {
95 if (m_service.empty())
96 split_addr_port(m_node, node, service);
97 else
98 split_addr_port(m_node + ":" + m_service, node, service);
99 } catch (const std::range_error& ex) {
100 m_error_msg =
101 UPnPsdk_LOGWHAT "MSG1128: catched next line ...\n" + ex.what();
102
103 return false;
104 }
105
106 // syscall ::getaddrinfo() with prepared arguments
107 // -----------------------------------------------
108 ::addrinfo* new_res{nullptr}; // Result from ::getaddrinfo()
109 const int ret = umock::netdb_h.getaddrinfo(
110 (node.empty() ? nullptr : node.c_str()),
111 (service.empty() ? nullptr : service.c_str()), &m_hints, &new_res);
112#ifndef __APPLE__
113 // std::format() since c++20 isn't supported on AppleClang 15, even on c++23
114 TRACE2(this, " syscall ::getaddrinfo(" + node + ", " + service +
115 ") with new_res = " +
116 std::format("{:#x}", reinterpret_cast<uintptr_t>(new_res)))
117#endif
118 if (g_dbug) {
119 // Very helpful for debugging to see what is given to ::getaddrinfo()
120 char addrStr[INET6_ADDRSTRLEN]{};
121 char servStr[NI_MAXSERV]{};
122 if (ret == 0)
123 ::getnameinfo(new_res->ai_addr,
124 static_cast<socklen_t>(new_res->ai_addrlen), addrStr,
125 sizeof(addrStr), servStr, sizeof(servStr),
126 NI_NUMERICHOST | NI_NUMERICSERV);
127 // clang-format off
128 UPnPsdk_LOGINFO("MSG1111") "syscall ::getaddrinfo("
129 << (node.empty() ? "nullptr, " : "\"" + node + "\", ")
130 << (service.empty() ? "nullptr, " : "\"" + service + "\", ")
131 << &m_hints << ", " << &new_res
132 << ") node=\"" << m_node << "\", "
133 << (m_hints.ai_flags & AI_NUMERICHOST ? "AI_NUMERICHOST, " : "")
134 << (m_hints.ai_flags & AI_NUMERICSERV ? "AI_NUMERICSERV, " : "")
135 << (m_hints.ai_flags & AI_PASSIVE ? "AI_PASSIVE, " : "")
136 << (m_hints.ai_family == AF_INET6 ? "AF_INET6" :
137 (m_hints.ai_family == AF_INET ? "AF_INET" :
138 (m_hints.ai_family == AF_UNSPEC ? "AF_UNSPEC" :
139 "m_hints.ai_family=" + std::to_string(m_hints.ai_family))))
140 << ", "
141 << (m_hints.ai_socktype == SOCK_STREAM ? "SOCK_STREAM" :
142 (m_hints.ai_socktype == SOCK_DGRAM ? "SOCK_DGRAM" :
143 (m_hints.ai_socktype == SOCK_RAW ? "SOCK_RAW" :
144 "socktype=" + std::to_string(m_hints.ai_socktype))))
145 << (ret != 0
146 ? ". Get EAI_ERROR(" + std::to_string(ret) + ")"
147 : ". Get first \"" + std::string(addrStr) + "\" port "
148 + std::string(servStr)
149 + (new_res->ai_next == nullptr ? ", no more entries." : ", more entries..."))
150 << '\n';
151 }
152
153 if (ret != 0) {
155 // Error numbers definded in netdb.h.
156 // Maybe an alphanumeric node name that cannot be resolved (e.g. by
157 // DNS)? Anyway, the user has to decide what to do. Because this
158 // depends on extern available DNS server the error can occur
159 // unexpectedly at any time. We have no influence on it but I will give
160 // an extended error message.
161 m_error_msg = UPnPsdk_LOGWHAT + "MSG1112: errid(" +
162 std::to_string(ret) + ")=\"" + ::gai_strerror(ret) + "\", " +
163 ((m_hints.ai_family == AF_UNSPEC) ? "IPv?_" :
164 ((m_hints.ai_family == AF_INET6) ? "IPv6_" : "IPv4_")) +
165 ((m_hints.ai_flags & AI_NUMERICHOST) ? "numeric_host=\"" : "alphanum_name=\"") +
166 m_node + "\", service=\"" +
167 m_service + "\"" +
168 ((m_hints.ai_flags & AI_PASSIVE) ? ", passive_listen" : "") +
169 ((m_hints.ai_flags & AI_NUMERICHOST) ? "" : ", (maybe DNS query temporary failed?)") +
170 ", ai_socktype=" + std::to_string(m_hints.ai_socktype);
171 UPnPsdk_LOGERR("?????") << m_error_msg << '\n';
172
173 return false;
174 }
175 // clang-format on
176
177 for (::addrinfo* res{new_res}; res != nullptr; res = res->ai_next) {
178 // Different on platforms: Ubuntu & MacOS return protocol number, win32
179 // returns 0. I just return what was used to call ::getaddrinfo().
180 res->ai_protocol = m_hints.ai_protocol;
181 //
182 // Different on platforms: Ubuntu returns set flags, MacOS & win32
183 // return 0. I just return what was used to call ::getaddrinfo().
184 res->ai_flags = m_hints.ai_flags;
185 }
186
187 // If get_first() is called the second time then m_res still points to the
188 // previous allocated memory. To avoid a memory leak it must be freed before
189 // pointing to the new allocated memory.
190 this->free_addrinfo();
191 // finaly point to the new address information from the operating
192 // system.
193 m_res = new_res;
194 m_res_current = new_res;
195
196 return true;
197}
198
199
200// Getter for the next available address information
201// -------------------------------------------------
202bool CAddrinfo::get_next() noexcept {
203 if (m_res_current->ai_next == nullptr) {
204 // It doesn't matter if already pointing to m_hints. m_hints->ai_next is
205 // also nullptr.
206 m_res_current = &m_hints;
207 return false;
208 }
209 m_res_current = m_res_current->ai_next;
210 return true;
211}
212
213
214// Get the socket address from current selcted address information
215// ---------------------------------------------------------------
217 if (m_res == &m_hints)
218 a_saddr = "";
219 else
220 a_saddr = reinterpret_cast<sockaddr_storage&>(*m_res_current->ai_addr);
221}
222
223
224// Get cached error message
225// ------------------------
226const std::string& CAddrinfo::what() const { return m_error_msg; }
227
228} // namespace UPnPsdk
Declaration of the Addrinfo class.
Get information from the operating system about an internet address.
Definition addrinfo.hpp:55
const ::addrinfo * operator->() const noexcept
Read access to members of the addrinfo structure
Definition addrinfo.cpp:81
bool get_next() noexcept
Get next available address information.
Definition addrinfo.cpp:202
bool get_first()
Get the first entry of an address info from the operating system.
Definition addrinfo.cpp:89
const std::string & what() const
Get cached error message.
Definition addrinfo.cpp:226
CAddrinfo(std::string_view a_node, std::string_view a_service, const int a_flags=0, const int a_socktype=SOCK_STREAM)
Constructor for getting an address information with service name.
Definition addrinfo.cpp:28
void sockaddr(SSockaddr &a_saddr)
Get the socket address from current selcted address information.
Definition addrinfo.cpp:216
void split_addr_port(const std::string &a_addr_str, std::string &a_addr, std::string &a_serv)
Free function to split inet address and port(service)
Definition sockaddr.cpp:210
Reengineered Object Oriented UPnP+ program code.
UPnPsdk_EXTERN bool g_dbug
Switch to enable verbose (debug) output.
Definition global.hpp:26
Declaration of the Sockaddr class and some free helper functions.
Trivial ::sockaddr structures enhanced with methods.
Definition sockaddr.hpp:94
Define macro for synced logging to the console for detailed info and debug.