simple-socket library 1.5.0
Loading...
Searching...
No Matches
SockAddress.cpp
1#include "SockAddress.h"
2
3#include <arpa/inet.h>
4#include <netinet/in.h>
5#include <sys/un.h>
6
7using namespace NET;
8
9namespace {
10
11void copyNumericAddress( std::string_view address, char (&buffer)[INET6_ADDRSTRLEN])
12{
13 // inet_pton() requires a NUL-terminated string.
14 // INET6_ADDRSTRLEN includes room for the terminating NUL of
15 // the longest normal textual IPv6 representation.
16 if( address.empty() ||
17 address.size() >= sizeof(buffer) ||
18 address.find('\0') != std::string_view::npos)
19 {
20 throw SocketException("Invalid IP address", false);
21 }
22
23 std::memcpy( buffer, address.data(), address.size());
24 buffer[address.size()] = '\0';
25}
26
27void checkInetPtonResult( int result, const char* invalidMessage)
28{
29 if (result == 1) return;
30 if (result == 0) throw SocketException( invalidMessage, false);
31 // inet_pton() returned -1.
32 // Unlike a malformed input string, this is a real errno-based
33 // system error (normally EAFNOSUPPORT).
34 throw SocketException("Address conversion failed (inet_pton)");
35}
36
37} // namespace
38
39SockAddress::SockAddress( AddressFamily family, std::string_view address, unsigned short port)
40{
41 char text[INET6_ADDRSTRLEN];
42
43 copyNumericAddress(address, text);
44
45 switch (nativeFamily(family))
46 {
47 case AF_INET:
48 {
49 sockaddr_in addr{};
50 addr.sin_family = AF_INET;
51 addr.sin_port = htons(port);
52 const int result = ::inet_pton(AF_INET, text, &addr.sin_addr);
53 checkInetPtonResult(result, "Invalid IPv4 address");
54 store(addr, sizeof(addr));
55 break;
56 }
57 case AF_INET6:
58 {
59 sockaddr_in6 addr{};
60 addr.sin6_family = AF_INET6;
61 addr.sin6_port = htons(port);
62 const int result = ::inet_pton(AF_INET6, text, &addr.sin6_addr);
63 checkInetPtonResult(result, "Invalid IPv6 address");
64 store(addr, sizeof(addr));
65 break;
66 }
67 default:
68 throw SocketException("Unsupported address family", false);
69 }
70}
71
72SockAddress::SockAddress( AddressFamily family, unsigned short port)
73{
74 switch (nativeFamily(family))
75 {
76 case AF_INET:
77 {
78 sockaddr_in addr{};
79 addr.sin_family = AF_INET;
80 addr.sin_port = htons(port);
81 addr.sin_addr.s_addr = htonl(INADDR_ANY);
82 store( addr, sizeof(addr));
83 break;
84 }
85 case AF_INET6:
86 {
87 sockaddr_in6 addr{};
88 addr.sin6_family = AF_INET6;
89 addr.sin6_port = htons(port);
90 addr.sin6_addr = in6addr_any;
91 store( addr, sizeof(addr));
92 break;
93 }
94 default:
95 throw SocketException("Unsupported address family", false);
96 }
97}
98
99SockAddress::SockAddress( std::string_view path)
100{
101 sockaddr_un addr{};
102 addr.sun_family = AF_UNIX;
103
104 // This constructor represents filesystem Unix-domain addresses,
105 // therefore embedded NUL characters are not meaningful here.
106 if( path.empty() ||
107 path.size() >= sizeof(addr.sun_path) ||
108 path.find('\0') != std::string_view::npos)
109 {
110 throw SocketException("Invalid Unix socket path", false);
111 }
112
113 std::memcpy( addr.sun_path, path.data(), path.size());
114 addr.sun_path[path.size()] = '\0';
115
116 const auto length = static_cast<socklen_t>( offsetof(sockaddr_un, sun_path) + path.size() + 1);
117 store( addr, length);
118}
119
120sockaddr* SockAddress::data() noexcept
121{
122 return reinterpret_cast<sockaddr*>(&m_storage);
123}
124
125const sockaddr* SockAddress::data() const noexcept
126{
127 return reinterpret_cast<const sockaddr*>(&m_storage);
128}
129
130socklen_t SockAddress::length() const noexcept
131{
132 return m_length;
133}
134
135sa_family_t SockAddress::family() const noexcept
136{
137 return m_storage.ss_family;
138}
139
140bool SockAddress::empty() const noexcept
141{
142 return m_storage.ss_family == AF_UNSPEC;
143}
144
145socklen_t* SockAddress::lengthPtr() noexcept
146{
147 m_storage = {};
148 m_length = sizeof(m_storage);
149 return &m_length;
150}
151
152unsigned short SockAddress::port() const noexcept
153{
154 switch (family())
155 {
156 case AF_INET:
157 {
158 sockaddr_in addr{};
159 std::memcpy( &addr, &m_storage, sizeof(addr));
160 return ntohs(addr.sin_port);
161 }
162 case AF_INET6:
163 {
164 sockaddr_in6 addr{};
165 std::memcpy( &addr, &m_storage, sizeof(addr));
166 return ntohs(addr.sin6_port);
167 }
168 default:
169 return 0;
170 }
171}
172
173std::string SockAddress::address() const
174{
175 char buffer[INET6_ADDRSTRLEN];
176
177 switch (family())
178 {
179 case AF_INET:
180 {
181 sockaddr_in addr{};
182 std::memcpy(&addr, &m_storage, sizeof(addr));
183
184 if (::inet_ntop( AF_INET, &addr.sin_addr, buffer, sizeof(buffer)) == nullptr)
185 throw SocketException("Address conversion failed (inet_ntop)");
186
187 return buffer;
188 }
189 case AF_INET6:
190 {
191 sockaddr_in6 addr{};
192 std::memcpy(&addr, &m_storage, sizeof(addr));
193
194 if (::inet_ntop( AF_INET6, &addr.sin6_addr, buffer, sizeof(buffer)) == nullptr)
195 throw SocketException("Address conversion failed (inet_ntop)");
196
197 return buffer;
198 }
199 case AF_UNIX:
200 {
201 sockaddr_un addr{};
202 std::memcpy(&addr, &m_storage, sizeof(addr));
203
204 constexpr std::size_t offset = offsetof(sockaddr_un, sun_path);
205 if (m_length <= offset) return {};
206
207 std::size_t length = static_cast<std::size_t>(m_length) - offset;
208
209 if (length > sizeof(addr.sun_path))
210 length = sizeof(addr.sun_path);
211
212 // Filesystem Unix socket addresses normally include
213 // the terminating NUL in m_length.
214 if (length != 0 && addr.sun_path[length - 1] == '\0')
215 --length;
216
217 return std::string(addr.sun_path, length);
218 }
219 case AF_UNSPEC:
220 return {};
221 default:
222 throw SocketException("Unsupported address family", false);
223 }
224}
225
226sa_family_t SockAddress::nativeFamily( AddressFamily family) noexcept
227{
228 switch (family)
229 {
230 case AddressFamily::IPv4: return AF_INET;
231 case AddressFamily::IPv6: return AF_INET6;
232 }
233 return AF_UNSPEC;
234}
Signals a problem with the execution of a socket call.