1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5 * Copyright (c) 2010-2016, The OpenClonk Team and contributors
6 * Copyright (c) 2019-2021, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18#include "C4Include.h"
19#ifndef _WIN32
20#include <arpa/inet.h>
21#include <ifaddrs.h>
22#include <net/if.h>
23#include <netdb.h>
24#endif
25
26#include "C4Network2Address.h"
27#include "StdAdaptors.h"
28#include "StdCompiler.h"
29
30namespace
31{
32 class AddrInfo
33 {
34 public:
35 AddrInfo(const char *const node, const char *const service,
36 const struct addrinfo *const hints)
37 {
38 if (::getaddrinfo(name: node, service: service, req: hints, pai: &addrs) != 0) addrs = nullptr;
39 }
40
41 ~AddrInfo() { if (addrs) ::freeaddrinfo(ai: addrs); }
42 explicit operator bool() const { return addrs != nullptr; }
43 addrinfo *GetAddrs() const { return addrs; }
44
45 private:
46 addrinfo *addrs;
47 };
48}
49
50// *** C4Network2HostAddress
51void C4Network2HostAddress::Clear()
52{
53 v6.sin6_family = AF_INET6;
54 v6.sin6_flowinfo = 0;
55 v6.sin6_scope_id = 0;
56 v6.sin6_addr = {};
57}
58
59// *** C4Network2EndpointAddress
60const C4Network2EndpointAddress::EndpointAddressPtr C4Network2EndpointAddress::operator&() const { return EndpointAddressPtr{const_cast<C4Network2EndpointAddress *>(this)}; }
61C4Network2EndpointAddress::EndpointAddressPtr C4Network2EndpointAddress::operator&() { return EndpointAddressPtr{this}; }
62
63void C4Network2EndpointAddress::Clear()
64{
65 C4Network2HostAddress::Clear();
66 SetPort(IPPORT_NONE);
67}
68
69void C4Network2HostAddress::SetHost(const C4Network2HostAddress &other)
70{
71 SetHost(&other.gen);
72}
73
74bool C4Network2HostAddress::IsMulticast() const
75{
76 if (gen.sa_family == AF_INET6)
77 return IN6_IS_ADDR_MULTICAST(&v6.sin6_addr) != 0;
78 if (gen.sa_family == AF_INET)
79 return (ntohl(v4.sin_addr.s_addr) >> 24) == 239;
80 return false;
81}
82
83bool C4Network2HostAddress::IsLoopback() const
84{
85 if (gen.sa_family == AF_INET6)
86 return IN6_IS_ADDR_LOOPBACK(&v6.sin6_addr) != 0;
87 if (gen.sa_family == AF_INET)
88 return (ntohl(v4.sin_addr.s_addr) >> 24) == 127;
89 return false;
90}
91
92bool C4Network2HostAddress::IsLocal() const
93{
94 if (gen.sa_family == AF_INET6)
95 return IN6_IS_ADDR_LINKLOCAL(&v6.sin6_addr) != 0;
96 if (gen.sa_family == AF_INET)
97 {
98 const std::uint32_t addr{ntohl(v4.sin_addr.s_addr)};
99 return addr >> 24 == 169 && ((addr >> 16) & 0xff) == 254;
100 }
101 return false;
102}
103
104bool C4Network2HostAddress::IsPrivate() const
105{
106 // IPv6 unique local address
107 if (gen.sa_family == AF_INET6)
108 return (v6.sin6_addr.s6_addr[0] & 0xfe) == 0xfc;
109 if (gen.sa_family == AF_INET)
110 {
111 const std::uint32_t addr{ntohl(v4.sin_addr.s_addr)};
112 const std::uint32_t s{(addr >> 16) & 0xff};
113 switch (addr >> 24)
114 {
115 case 10: return true;
116 case 172: return s >= 16 && s <= 31;
117 case 192: return s == 168;
118 }
119 }
120 return false;
121}
122
123void C4Network2HostAddress::SetScopeId(const int scopeId)
124{
125 if (gen.sa_family != AF_INET6) return;
126 if (IN6_IS_ADDR_LINKLOCAL(&v6.sin6_addr) != 0)
127 v6.sin6_scope_id = scopeId;
128}
129
130int C4Network2HostAddress::GetScopeId() const
131{
132 if (gen.sa_family == AF_INET6)
133 return v6.sin6_scope_id;
134 return 0;
135}
136
137C4Network2HostAddress C4Network2HostAddress::AsIPv6() const
138{
139 static constexpr unsigned char v6_mapped_v4_prefix[12]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
140
141 C4Network2HostAddress nrv{*this};
142 switch (gen.sa_family)
143 {
144 case AF_INET6:
145 // That was easy
146 break;
147 case AF_INET:
148 nrv.v6.sin6_family = AF_INET6;
149 std::memcpy(dest: &nrv.v6.sin6_addr.s6_addr[0], src: v6_mapped_v4_prefix, n: sizeof(v6_mapped_v4_prefix));
150 std::memcpy(dest: &nrv.v6.sin6_addr.s6_addr[sizeof(v6_mapped_v4_prefix)], src: &v4.sin_addr, n: sizeof(v4.sin_addr));
151 nrv.v6.sin6_flowinfo = 0;
152 nrv.v6.sin6_scope_id = 0;
153 break;
154 default:
155 assert(!"Shouldn't reach this");
156 break;
157 }
158 return nrv;
159}
160
161bool C4Network2HostAddress::IsIPv6MappedIPv4() const
162{
163 return gen.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&v6.sin6_addr);
164}
165
166C4Network2HostAddress C4Network2HostAddress::AsIPv4() const
167{
168 C4Network2HostAddress nrv{*this};
169 if (IsIPv6MappedIPv4())
170 {
171 nrv.v4.sin_family = AF_INET;
172 std::memcpy(dest: &nrv.v4.sin_addr, src: &v6.sin6_addr.s6_addr[12], n: sizeof(v4.sin_addr));
173 }
174 return nrv;
175}
176
177C4Network2EndpointAddress C4Network2EndpointAddress::AsIPv6() const
178{
179 return {C4Network2HostAddress::AsIPv6(), GetPort()};
180}
181
182C4Network2EndpointAddress C4Network2EndpointAddress::AsIPv4() const
183{
184 return {C4Network2HostAddress::AsIPv4(), GetPort()};
185}
186
187void C4Network2HostAddress::SetHost(const sockaddr *const addr)
188{
189 // Copy all but port number
190 if (addr->sa_family == AF_INET6)
191 {
192 const auto addr6 = reinterpret_cast<const sockaddr_in6 *>(addr);
193 v6.sin6_family = addr6->sin6_family;
194 v6.sin6_flowinfo = addr6->sin6_flowinfo;
195 v6.sin6_addr = addr6->sin6_addr;
196 v6.sin6_scope_id = addr6->sin6_scope_id;
197 }
198 else if (addr->sa_family == AF_INET)
199 {
200 const auto addr4 = reinterpret_cast<const sockaddr_in *>(addr);
201 v4.sin_family = addr4->sin_family;
202 v4.sin_addr.s_addr = addr4->sin_addr.s_addr;
203 std::memset(s: &v4.sin_zero, c: 0, n: sizeof(v4.sin_zero));
204 }
205}
206
207void C4Network2EndpointAddress::SetAddress(const sockaddr *const addr)
208{
209 switch (addr->sa_family)
210 {
211 case AF_INET: std::memcpy(dest: &v4, src: addr, n: sizeof(v4)); break;
212 case AF_INET6: std::memcpy(dest: &v6, src: addr, n: sizeof(v6)); break;
213 default:
214 assert(!"Unexpected address family");
215 std::memcpy(dest: &gen, src: addr, n: sizeof(gen));
216 break;
217 }
218}
219
220void C4Network2HostAddress::SetHost(const SpecialAddress addr)
221{
222 switch (addr)
223 {
224 case Any:
225 v6.sin6_family = AF_INET6;
226 std::memset(s: &v6.sin6_addr, c: 0, n: sizeof(v6.sin6_addr));
227 v6.sin6_flowinfo = 0;
228 v6.sin6_scope_id = 0;
229 break;
230 case AnyIPv4:
231 v4.sin_family = AF_INET;
232 v4.sin_addr.s_addr = 0;
233 std::memset(s: &v4.sin_zero, c: 0, n: sizeof(v4.sin_zero));
234 break;
235 case Loopback:
236 v6.sin6_family = AF_INET6;
237 std::memset(s: &v6.sin6_addr, c: 0, n: sizeof(v6.sin6_addr));
238 v6.sin6_addr.s6_addr[15] = 1;
239 v6.sin6_flowinfo = 0;
240 v6.sin6_scope_id = 0;
241 break;
242 }
243}
244
245void C4Network2HostAddress::SetHost(const std::uint32_t v4addr)
246{
247 v4.sin_family = AF_INET;
248 v4.sin_addr.s_addr = v4addr;
249 std::memset(s: &v4.sin_zero, c: 0, n: sizeof(v4.sin_zero));
250}
251
252void C4Network2HostAddress::SetHost(const StdStrBuf &addr, const AddressFamily family)
253{
254 addrinfo hints{};
255 hints.ai_family = family;
256
257 if (const AddrInfo ai{addr.getData(), nullptr, &hints})
258 {
259 SetHost(ai.GetAddrs()->ai_addr);
260 }
261}
262
263void C4Network2EndpointAddress::SetAddress(const StdStrBuf &addr, const AddressFamily family)
264{
265 Clear();
266
267 if (addr.isNull()) return;
268
269 const auto begin = addr.getData();
270 const auto end = begin + addr.getLength();
271
272 auto ab = begin;
273 auto ae = end;
274
275 auto pb = end;
276 const auto pe = end;
277
278 bool isIPv6{false};
279
280 // If addr begins with [, it's an IPv6 address
281 if (ab[0] == '[')
282 {
283 ++ab; // Skip bracket
284 auto cbracket = std::find(first: ab, last: ae, val: ']');
285 if (cbracket == ae)
286 // No closing bracket found: invalid
287 return;
288 ae = cbracket++;
289 if (cbracket != end && cbracket[0] == ':')
290 {
291 // Port number given
292 pb = ++cbracket;
293 if (pb == end)
294 // Trailing colon: invalid
295 return;
296 }
297 isIPv6 = true;
298 }
299 // If there's more than 1 colon in the address, it's IPv6
300 else if (std::count(first: ab, last: ae, value: ':') > 1)
301 {
302 isIPv6 = true;
303 }
304 // It's probably not IPv6, but look for a port specification
305 else
306 {
307 const auto colon = std::find(first: ab, last: ae, val: ':');
308 if (colon != ae)
309 {
310 ae = colon;
311 pb = colon + 1;
312 if (pb == end)
313 // Trailing colon: invalid
314 return;
315 }
316 }
317
318 addrinfo hints{};
319 hints.ai_family = family;
320 if (const AddrInfo ai{std::string{ab, ae}.c_str(),
321 (pb != end ? std::string{pb, pe}.c_str() : nullptr), &hints})
322 {
323 SetAddress(ai.GetAddrs()->ai_addr);
324 }
325}
326
327void C4Network2EndpointAddress::SetAddress(const std::string &addr, const AddressFamily family)
328{
329 SetAddress(addr: StdStrBuf{addr.c_str(), addr.size(), false}, family);
330}
331
332void C4Network2EndpointAddress::SetAddress(const C4Network2EndpointAddress &addr)
333{
334 SetHost(addr);
335 SetPort(addr.GetPort());
336}
337
338void C4Network2EndpointAddress::SetAddress(
339 const C4Network2HostAddress::SpecialAddress host, const std::uint16_t port)
340{
341 SetHost(host);
342 SetPort(port);
343}
344
345void C4Network2EndpointAddress::SetAddress(const C4Network2HostAddress &host, const std::uint16_t port)
346{
347 SetHost(host);
348 SetPort(port);
349}
350
351bool C4Network2EndpointAddress::IsNull() const
352{
353 return IsNullHost() && GetPort() == IPPORT_NONE;
354}
355
356bool C4Network2HostAddress::IsNull() const
357{
358 switch (gen.sa_family)
359 {
360 case AF_INET: return v4.sin_addr.s_addr == 0;
361 case AF_INET6: return IN6_IS_ADDR_UNSPECIFIED(&v6.sin6_addr);
362 }
363 assert(!"Shouldn't reach this");
364 return false;
365}
366
367C4Network2HostAddress::AddressFamily C4Network2HostAddress::GetFamily() const
368{
369 return
370 gen.sa_family == AF_INET ? IPv4 :
371 gen.sa_family == AF_INET6 ? IPv6 :
372 UnknownFamily;
373}
374
375std::size_t C4Network2HostAddress::GetAddrLen() const
376{
377 return GetFamily() == IPv4 ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
378}
379
380void C4Network2EndpointAddress::SetPort(const std::uint16_t port)
381{
382 switch (gen.sa_family)
383 {
384 case AF_INET: v4.sin_port = htons(port); break;
385 case AF_INET6: v6.sin6_port = htons(port); break;
386 default: assert(!"Shouldn't reach this"); break;
387 }
388}
389
390void C4Network2EndpointAddress::SetDefaultPort(const std::uint16_t port)
391{
392 if (GetPort() == IPPORT_NONE)
393 SetPort(port);
394}
395
396std::uint16_t C4Network2EndpointAddress::GetPort() const
397{
398 switch (gen.sa_family)
399 {
400 case AF_INET: return ntohs(v4.sin_port);
401 case AF_INET6: return ntohs(v6.sin6_port);
402 }
403 assert(!"Shouldn't reach this");
404 return IPPORT_NONE;
405}
406
407bool C4Network2HostAddress::operator==(const C4Network2HostAddress &rhs) const
408{
409 // Check for IPv4-mapped IPv6 addresses.
410 if (gen.sa_family != rhs.gen.sa_family)
411 return AsIPv6() == rhs.AsIPv6();
412 if (gen.sa_family == AF_INET)
413 return v4.sin_addr.s_addr == rhs.v4.sin_addr.s_addr;
414 if (gen.sa_family == AF_INET6)
415 return std::memcmp(s1: &v6.sin6_addr, s2: &rhs.v6.sin6_addr, n: sizeof(v6.sin6_addr)) == 0 &&
416 v6.sin6_scope_id == rhs.v6.sin6_scope_id;
417 assert(!"Shouldn't reach this");
418 return false;
419}
420
421bool C4Network2EndpointAddress::operator==(const C4Network2EndpointAddress &rhs) const
422{
423 if (!C4Network2HostAddress::operator==(rhs)) return false;
424 if (gen.sa_family == AF_INET)
425 {
426 return v4.sin_port == rhs.v4.sin_port;
427 }
428 else if (gen.sa_family == AF_INET6)
429 {
430 return v6.sin6_port == rhs.v6.sin6_port &&
431 v6.sin6_scope_id == rhs.v6.sin6_scope_id;
432 }
433 assert(!"Shouldn't reach this");
434 return false;
435}
436
437std::string C4Network2HostAddress::ToString(const int flags) const
438{
439 if (IsIPv6MappedIPv4()) return AsIPv4().ToString(flags);
440
441 if (gen.sa_family == AF_INET6 && v6.sin6_scope_id != 0 && (flags & TSF_SkipZoneId))
442 {
443 auto addr = *this;
444 addr.v6.sin6_scope_id = 0;
445 return addr.ToString(flags);
446 }
447
448 char buf[INET6_ADDRSTRLEN];
449 if (::getnameinfo(sa: &gen, salen: GetAddrLen(), host: buf, hostlen: sizeof(buf), serv: nullptr, servlen: 0, NI_NUMERICHOST) != 0)
450 return {};
451
452 return buf;
453}
454
455std::string C4Network2EndpointAddress::ToString(const int flags) const
456{
457 if (IsIPv6MappedIPv4()) return AsIPv4().ToString(flags);
458
459 if (flags & TSF_SkipPort)
460 return C4Network2HostAddress::ToString(flags);
461
462 switch (GetFamily())
463 {
464 case IPv4: return std::format(fmt: "{}:{}", args: C4Network2HostAddress::ToString(flags), args: GetPort());
465 case IPv6: return std::format(fmt: "[{}]:{}", args: C4Network2HostAddress::ToString(flags), args: GetPort());
466 case UnknownFamily: ; // fallthrough
467 }
468 assert(!"Shouldn't reach this");
469 return {};
470}
471
472void C4Network2EndpointAddress::CompileFunc(StdCompiler *const comp)
473{
474 if (!comp->isCompiler())
475 {
476 std::string val{ToString(flags: TSF_SkipZoneId)};
477 comp->Value(rString&: val);
478 }
479 else
480 {
481 std::string val;
482 comp->Value(rString&: val);
483 SetAddress(addr: val);
484 }
485}
486
487// *** C4Network2Address
488
489void C4Network2Address::CompileFunc(StdCompiler *const comp)
490{
491 // Clear
492 if (comp->isCompiler())
493 {
494 addr.Clear();
495 }
496
497 // Write protocol
498 const StdEnumEntry<C4Network2IOProtocol> Protocols[]{
499 { .Name: "UDP", .Val: P_UDP },
500 { .Name: "TCP", .Val: P_TCP }
501 };
502 comp->Value(rStruct: mkEnumAdaptT<std::uint8_t>(rVal&: protocol, pNames: Protocols));
503 comp->Separator(eSep: StdCompiler::SEP_PART2); // ':'
504
505 comp->Value(rStruct: mkDefaultAdapt(rValue&: addr, rDefault: C4Network2EndpointAddress{}));
506}
507
508std::string C4Network2Address::ToString() const
509{
510 switch (protocol)
511 {
512 case P_UDP: return std::format(fmt: "UDP:{}", args: addr.ToString());
513 case P_TCP: return std::format(fmt: "TCP:{}", args: addr.ToString());
514 case P_NONE: ; // fallthrough
515 }
516 return "INVALID";
517}
518
519bool C4Network2Address::operator==(const C4Network2Address &addr2) const
520{
521 return protocol == addr2.GetProtocol() && addr == addr2.GetAddr();
522}
523