1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2013-2018, The OpenClonk Team and contributors
6 * Copyright (c) 2017-2022, 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 "C4NetIO.h"
19
20#include "C4Constants.h"
21#include "C4Config.h"
22#include "C4Network2Address.h"
23#include "Standard.h"
24
25#include <assert.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <format>
29#include <sys/stat.h>
30
31// platform specifics
32#ifdef _WIN32
33
34#include <iphlpapi.h>
35#include <winsock2.h>
36
37#else
38
39#include <sys/ioctl.h>
40#include <netinet/in.h>
41#include <netinet/tcp.h>
42#include <arpa/inet.h>
43#include <netdb.h>
44#include <ifaddrs.h>
45#include <net/if.h>
46#include <stdlib.h>
47
48#define ioctlsocket ioctl
49#define closesocket close
50#define SOCKET_ERROR (-1)
51
52#endif
53
54// These are named differently on mac.
55#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
56#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
57#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
58#endif
59
60#ifdef __linux__
61#include <linux/in6.h>
62#include <linux/if_addr.h>
63
64// Linux definitions needed for parsing /proc/if_inet6
65#define IPV6_ADDR_LOOPBACK 0x0010U
66#define IPV6_ADDR_LINKLOCAL 0x0020U
67#define IPV6_ADDR_SITELOCAL 0x0040U
68#endif
69
70#include <algorithm>
71#include <cinttypes>
72#include <functional>
73#include <utility>
74
75// simulate packet loss (loss probability in percent)
76// #define C4NETIO_SIMULATE_PACKETLOSS 10
77
78// *** helpers
79
80#ifdef _WIN32
81
82const char *GetSocketErrorMsg(int iError)
83{
84 switch (iError)
85 {
86 case WSAEACCES: return "Permission denied.";
87 case WSAEADDRINUSE: return "Address already in use.";
88 case WSAEADDRNOTAVAIL: return "Cannot assign requested address.";
89 case WSAEAFNOSUPPORT: return "Address family not supported by protocol family.";
90 case WSAEALREADY: return "Operation already in progress.";
91 case WSAECONNABORTED: return "Software caused connection abort.";
92 case WSAECONNREFUSED: return "Connection refused.";
93 case WSAECONNRESET: return "Connection reset by peer.";
94 case WSAEDESTADDRREQ: return "Destination address required.";
95 case WSAEFAULT: return "Bad address.";
96 case WSAEHOSTDOWN: return "Host is down.";
97 case WSAEHOSTUNREACH: return "No route to host.";
98 case WSAEINPROGRESS: return "Operation now in progress.";
99 case WSAEINTR: return "Interrupted function call.";
100 case WSAEINVAL: return "Invalid argument.";
101 case WSAEISCONN: return "Socket is already connected.";
102 case WSAEMFILE: return "Too many open files.";
103 case WSAEMSGSIZE: return "Message too long.";
104 case WSAENETDOWN: return "Network is down.";
105 case WSAENETRESET: return "Network dropped connection on reset.";
106 case WSAENETUNREACH: return "Network is unreachable.";
107 case WSAENOBUFS: return "No buffer space available.";
108 case WSAENOPROTOOPT: return "Bad protocol option.";
109 case WSAENOTCONN: return "Socket is not connected.";
110 case WSAENOTSOCK: return "Socket operation on non-socket.";
111 case WSAEOPNOTSUPP: return "Operation not supported.";
112 case WSAEPFNOSUPPORT: return "Protocol family not supported.";
113 case WSAEPROCLIM: return "Too many processes.";
114 case WSAEPROTONOSUPPORT: return "Protocol not supported.";
115 case WSAEPROTOTYPE: return "Protocol wrong type for socket.";
116 case WSAESHUTDOWN: return "Cannot send after socket shutdown.";
117 case WSAESOCKTNOSUPPORT: return "Socket type not supported.";
118 case WSAETIMEDOUT: return "Connection timed out.";
119 case WSATYPE_NOT_FOUND: return "Class type not found.";
120 case WSAEWOULDBLOCK: return "Resource temporarily unavailable.";
121 case WSAHOST_NOT_FOUND: return "Host not found.";
122 case WSA_INVALID_HANDLE: return "Specified event object handle is invalid.";
123 case WSA_INVALID_PARAMETER: return "One or more parameters are invalid.";
124 case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state.";
125 case WSA_IO_PENDING: return "Overlapped operations will complete later.";
126 case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available.";
127 case WSANOTINITIALISED: return "Successful WSAStartup not yet performed.";
128 case WSANO_DATA: return "Valid name, no data record of requested type.";
129 case WSANO_RECOVERY: return "This is a non-recoverable error.";
130 case WSASYSCALLFAILURE: return "System call failure.";
131 case WSASYSNOTREADY: return "Network subsystem is unavailable.";
132 case WSATRY_AGAIN: return "Non-authoritative host not found.";
133 case WSAVERNOTSUPPORTED: return "WINSOCK.DLL version out of range.";
134 case WSAEDISCON: return "Graceful shutdown in progress.";
135 case WSA_OPERATION_ABORTED: return "Overlapped operation aborted.";
136 case 0: return "no error";
137 default: return "Stupid Error.";
138 }
139}
140
141const char *GetSocketErrorMsg()
142{
143 return GetSocketErrorMsg(WSAGetLastError());
144}
145
146bool HaveSocketError()
147{
148 return !!WSAGetLastError();
149}
150
151bool HaveWouldBlockError()
152{
153 return WSAGetLastError() == WSAEWOULDBLOCK;
154}
155
156bool HaveConnResetError()
157{
158 return WSAGetLastError() == WSAECONNRESET;
159}
160
161void ResetSocketError()
162{
163 WSASetLastError(0);
164}
165
166static int iWSockUseCounter = 0;
167
168bool AcquireWinSock()
169{
170 if (!iWSockUseCounter)
171 {
172 // initialize winsock
173 WSADATA data;
174 int res = WSAStartup(0x202, &data);
175 // success? count
176 if (!res)
177 iWSockUseCounter++;
178 // return result
179 return !res;
180 }
181 // winsock already initialized
182 iWSockUseCounter++;
183 return true;
184}
185
186void ReleaseWinSock()
187{
188 iWSockUseCounter--;
189 // last use?
190 if (!iWSockUseCounter)
191 WSACleanup();
192}
193
194#else
195
196const char *GetSocketErrorMsg(int iError)
197{
198 return strerror(errnum: iError);
199}
200
201const char *GetSocketErrorMsg()
202{
203 return GetSocketErrorMsg(errno);
204}
205
206bool HaveSocketError()
207{
208 return !!errno;
209}
210
211bool HaveWouldBlockError()
212{
213 return errno == EINPROGRESS || errno == EWOULDBLOCK;
214}
215
216bool HaveConnResetError()
217{
218 return errno == ECONNRESET;
219}
220
221void ResetSocketError()
222{
223 errno = 0;
224}
225
226#endif
227
228
229
230namespace
231{
232 bool ContainsGlobalIpv6(const std::vector<C4Network2HostAddress> &addresses)
233 {
234 return std::any_of(first: addresses.cbegin(), last: addresses.cend(), pred: [](const auto &addr)
235 {
236 return addr.GetFamily() == C4Network2HostAddress::IPv6
237 && !addr.IsLocal()
238 && !addr.IsPrivate();
239 });
240 }
241
242 template<typename Address, typename GetAddr = std::identity>
243 void SortAddresses(std::vector<Address> &addrs, bool haveIPv6, GetAddr &&getAddr = {})
244 {
245 const auto rank = [haveIPv6, getAddr](const Address &Addr)
246 {
247 const auto &addr = getAddr(Addr);
248 if (addr.IsLocal())
249 {
250 return 100;
251 }
252 else if (addr.IsPrivate())
253 {
254 return 150;
255 }
256 else
257 {
258 switch (addr.GetFamily())
259 {
260 case C4Network2HostAddress::IPv6:
261 return haveIPv6 ? 300 : 0;
262 case C4Network2HostAddress::IPv4:
263 return 200;
264 case C4Network2HostAddress::UnknownFamily:
265 ; // fallthrough
266 }
267
268 assert(!"Unexpected address family");
269 return 0;
270 }
271 };
272
273 // Sort by decreasing rank. Use stable sort to allow the host to prioritize addresses within a family.
274 std::stable_sort(addrs.begin(), addrs.end(), [&rank](const auto &a, const auto &b) { return rank(a) > rank(b); });
275 }
276}
277
278std::vector<C4Network2HostAddress> C4NetIO::GetLocalAddresses(bool unsorted)
279{
280 std::vector<C4Network2HostAddress> result;
281
282#ifdef _WIN32
283 std::vector<IP_ADAPTER_ADDRESSES> addresses{32};
284 for (int i = 0; i < 10; ++i)
285 {
286 auto bufsz = static_cast<ULONG>(sizeof(decltype(result)::value_type) * addresses.size());
287 const auto &rv = GetAdaptersAddresses(AF_UNSPEC,
288 GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
289 nullptr, addresses.data(), &bufsz);
290 // Too little space, try again
291 if (rv == ERROR_BUFFER_OVERFLOW)
292 {
293 addresses.resize(addresses.size() * 2);
294 continue;
295 }
296 // Something else happened
297 if (rv != NO_ERROR) return result;
298 // All okay, add addresses
299 for (const auto *address = addresses.data(); address; address = address->Next)
300 {
301 for (const auto *unicast = address->FirstUnicastAddress; unicast; unicast = unicast->Next)
302 {
303 const C4Network2HostAddress addr{unicast->Address.lpSockaddr};
304 if (!addr.IsLoopback()) result.push_back(addr);
305 }
306 }
307 }
308#else
309 bool have_ipv6{false};
310
311#ifdef __linux__
312 struct FopenFile
313 {
314 std::FILE *const f;
315 FopenFile(const char *const name, const char *const mode) : f{std::fopen(filename: name, modes: mode)} {}
316 ~FopenFile() { if (f) std::fclose(stream: f); }
317 explicit operator bool() const { return f != nullptr; }
318 };
319 // Get IPv6 addresses on Linux from procfs which allows filtering deprecated privacy addresses.
320 if (FopenFile f{"/proc/net/if_inet6", "r"})
321 {
322 sockaddr_in6 sa6{};
323 sa6.sin6_family = AF_INET6;
324 const auto a6 = sa6.sin6_addr.s6_addr;
325 std::uint8_t if_idx, plen, scope, flags;
326 char devname[20 + 1];
327 while (std::fscanf(f.f,
328 "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx "
329 "%02" SCNx8 " %02" SCNx8 " %02" SCNx8 " %02" SCNx8 " %20s\n",
330 &a6[0], &a6[1], &a6[ 2], &a6[ 3], &a6[ 4], &a6[ 5], &a6[ 6], &a6[ 7],
331 &a6[8], &a6[9], &a6[10], &a6[11], &a6[12], &a6[13], &a6[14], &a6[15],
332 &if_idx, &plen, &scope, &flags, devname) != EOF)
333 {
334 // Skip loopback and deprecated addresses.
335 if (scope == IPV6_ADDR_LOOPBACK || flags & IFA_F_DEPRECATED)
336 continue;
337 sa6.sin6_scope_id = (scope == IPV6_ADDR_LINKLOCAL ? if_idx : 0);
338 result.emplace_back(args: reinterpret_cast<sockaddr *>(&sa6));
339 }
340 have_ipv6 = result.size() > 0;
341 }
342#endif
343
344 struct IFAddrs
345 {
346 ifaddrs *addrs;
347
348 IFAddrs()
349 {
350 if (::getifaddrs(ifap: &addrs) != 0) addrs = nullptr;
351 }
352
353 ~IFAddrs() { if (addrs) ::freeifaddrs(ifa: addrs); }
354
355 explicit operator bool() const { return addrs != nullptr; }
356 };
357
358 if (const IFAddrs ifa{})
359 {
360 for (const auto *ifaddr = ifa.addrs; ifaddr != nullptr; ifaddr = ifaddr->ifa_next)
361 {
362 const auto &ad = ifaddr->ifa_addr;
363 if (ad == nullptr) continue;
364
365 // Choose only non-loopback IPv4/6 devices
366 if ((ad->sa_family == AF_INET || (!have_ipv6 && ad->sa_family == AF_INET6)) && (~ifaddr->ifa_flags & IFF_LOOPBACK))
367 {
368 result.emplace_back(args: ad);
369 }
370 }
371 }
372#endif
373
374 if (!unsorted)
375 {
376 ::SortAddresses(addrs&: result, haveIPv6: ContainsGlobalIpv6(addresses: result));
377 }
378
379 return result;
380}
381
382// Orders connection addresses to optimize joining.
383void C4NetIO::SortAddresses(std::vector<C4Network2Address> &addrs)
384{
385 // TODO: Maybe use addresses from local client to avoid the extra system calls.
386 return ::SortAddresses(addrs, haveIPv6: ContainsGlobalIpv6(addresses: C4NetIO::GetLocalAddresses(unsorted: true)), getAddr: std::mem_fn(pm: static_cast<const addr_t &(C4Network2Address::*)() const>(&C4Network2Address::GetAddr)));
387}
388
389// *** C4NetIO
390
391// construction / destruction
392
393C4NetIO::C4NetIO()
394{
395 ResetError();
396}
397
398C4NetIO::~C4NetIO() {}
399
400bool C4NetIO::InitIPv6Socket(const SOCKET socket)
401{
402 constexpr int optV6Only{0};
403 if (::setsockopt(fd: socket, IPPROTO_IPV6, IPV6_V6ONLY,
404 optval: reinterpret_cast<const char *>(&optV6Only), optlen: sizeof(optV6Only)) == SOCKET_ERROR)
405 {
406 SetError(strnError: "could not enable dual-stack socket", fSockErr: true);
407 return false;
408 }
409
410#ifdef IPV6_ADDR_PREFERENCES
411 // Prefer stable addresses. This should prevent issues with address
412 // deprecation while a match is running. No error handling - if the call
413 // fails, we just take any address.
414 constexpr int optAddrPrefs{IPV6_PREFER_SRC_PUBLIC};
415 ::setsockopt(fd: socket, IPPROTO_IPV6, IPV6_ADDR_PREFERENCES,
416 optval: reinterpret_cast<const char *>(&optAddrPrefs), optlen: sizeof(optAddrPrefs));
417#endif
418
419 return true;
420}
421
422void C4NetIO::SetError(const char *strnError, bool fSockErr)
423{
424 fSockErr &= HaveSocketError();
425 if (fSockErr)
426 Error.Copy(pnData: std::format(fmt: "{} ({})", args&: strnError, args: GetSocketErrorMsg()).c_str());
427 else
428 Error.Copy(pnData: strnError);
429}
430
431// *** C4NetIOPacket
432
433// construction / destruction
434
435C4NetIOPacket::C4NetIOPacket() {}
436
437C4NetIOPacket::C4NetIOPacket(const void *pnData, size_t inSize, bool fCopy, const C4NetIO::addr_t &naddr)
438 : StdBuf(pnData, inSize, fCopy), addr(naddr) {}
439
440C4NetIOPacket::C4NetIOPacket(const StdBuf &Buf, const C4NetIO::addr_t &naddr)
441 : StdBuf(Buf), addr(naddr) {}
442
443C4NetIOPacket::~C4NetIOPacket()
444{
445 Clear();
446}
447
448void C4NetIOPacket::Clear()
449{
450 addr = C4NetIO::addr_t();
451 StdBuf::Clear();
452}
453
454// *** C4NetIOTCP
455
456// construction / destruction
457
458C4NetIOTCP::C4NetIOTCP()
459 : pPeerList(nullptr), fInit(false),
460 pConnectWaits(nullptr),
461#ifdef _WIN32
462 Event(nullptr),
463#endif
464 PeerListCSec(this),
465 iListenPort(~0), lsock(INVALID_SOCKET),
466 pCB(nullptr) {}
467
468C4NetIOTCP::~C4NetIOTCP()
469{
470 Close();
471}
472
473bool C4NetIOTCP::Init(uint16_t iPort)
474{
475 // already init? close first
476 if (fInit) Close();
477
478#ifdef _WIN32
479 // init winsock
480 if (!AcquireWinSock())
481 {
482 SetError("could not start winsock");
483 return false;
484 }
485
486 // create event
487 if ((Event = WSACreateEvent()) == WSA_INVALID_EVENT)
488 {
489 SetError("could not create socket event", true); // to do: more error information
490 return false;
491 }
492#else
493 // create pipe
494 if (pipe(pipedes: Pipe) != 0)
495 {
496 SetError(strnError: "could not create pipe", fSockErr: true);
497 return false;
498 }
499#endif
500
501 // create listen socket (if necessary)
502 if (iPort != addr_t::IPPORT_NONE)
503 if (!Listen(inListenPort: iPort))
504 return false;
505
506 // ok
507 fInit = true;
508 return true;
509}
510
511bool C4NetIOTCP::InitBroadcast(addr_t *pBroadcastAddr)
512{
513 // ignore
514 return true;
515}
516
517bool C4NetIOTCP::Close()
518{
519 ResetError();
520
521 // not init?
522 if (!fInit) return false;
523
524 // terminate connections
525 CStdShareLock PeerListLock(&PeerListCSec);
526 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
527 if (pPeer->Open())
528 {
529 pPeer->Close();
530 if (pCB) pCB->OnDisconn(AddrPeer: pPeer->GetAddr(), pNetIO: this, szReason: "owner class closed");
531 }
532
533 ClearConnectWaits();
534
535 // close listen socket
536 if (lsock != INVALID_SOCKET)
537 {
538 closesocket(fd: lsock);
539 lsock = INVALID_SOCKET;
540 }
541
542#ifdef _WIN32
543 // close event
544 if (Event != nullptr)
545 {
546 WSACloseEvent(Event);
547 Event = nullptr;
548 }
549
550 // release winsock
551 ReleaseWinSock();
552#else
553 // close pipe
554 close(fd: Pipe[0]);
555 close(fd: Pipe[1]);
556#endif
557
558 // ok
559 fInit = false;
560 return true;
561}
562
563bool C4NetIOTCP::CloseBroadcast()
564{
565 return true;
566}
567
568bool C4NetIOTCP::Execute(int iMaxTime) // (mt-safe)
569{
570 // security
571 if (!fInit) return false;
572
573#ifdef _WIN32
574 // wait for something to happen
575 if (WaitForSingleObject(Event, iMaxTime) == WAIT_TIMEOUT)
576 // timeout -> nothing happened
577 return true;
578 WSAResetEvent(Event);
579
580 WSANETWORKEVENTS wsaEvents;
581#else
582
583 std::vector<pollfd> fds;
584 GetFDs(fds);
585
586 // build timeout value
587 timeval to = { .tv_sec: iMaxTime / 1000, .tv_usec: (iMaxTime % 1000) * 1000 };
588
589 // wait for something to happen
590 int ret = StdSync::Poll(fds, timeout: iMaxTime);
591
592 // error
593 if (ret < 0)
594 {
595 SetError(strnError: "poll failed");
596 return false;
597 }
598
599 // nothing happened
600 if (ret == 0)
601 return true;
602
603 // flush pipe
604 if (fds[0].revents & POLLIN)
605 {
606 char c;
607 ::read(fd: Pipe[0], buf: &c, nbytes: 1);
608 }
609#endif
610
611 // check sockets for events
612
613 // first: the listen socket
614 if (lsock != INVALID_SOCKET)
615 {
616#ifdef _WIN32
617 // get event list
618 if (::WSAEnumNetworkEvents(lsock, nullptr, &wsaEvents) == SOCKET_ERROR)
619 return false;
620
621 // a connection waiting for accept?
622 if (wsaEvents.lNetworkEvents & FD_ACCEPT)
623#else
624 // a connection waiting for accept?
625 if (fds[1].revents & POLLIN)
626#endif
627 if (!Accept())
628 return false;
629 // (note: what happens if there are more connections waiting?)
630
631#ifdef _WIN32
632 // closed?
633 if (wsaEvents.lNetworkEvents & FD_CLOSE)
634 // try to recreate the listen socket
635 Listen(iListenPort);
636#endif
637 }
638
639 // second: waited-for connection
640 CStdShareLock PeerListLock(&PeerListCSec);
641 for (ConnectWait *pWait = pConnectWaits, *pNext; pWait; pWait = pNext)
642 {
643 pNext = pWait->Next;
644
645 // not closed?
646 if (pWait->sock != INVALID_SOCKET)
647 {
648#ifdef _WIN32
649 // get event list
650 if (::WSAEnumNetworkEvents(pWait->sock, nullptr, &wsaEvents) == SOCKET_ERROR)
651 return false;
652
653 if (wsaEvents.lNetworkEvents & FD_CONNECT)
654#else
655 // got connection?
656 if (const auto it = std::ranges::find(fds, pWait->sock, &pollfd::fd); it != std::ranges::end(fds) && it->revents & POLLOUT)
657#endif
658 {
659 // remove from list
660 SOCKET sock = pWait->sock; pWait->sock = INVALID_SOCKET;
661
662#ifdef _WIN32
663 // error?
664 if (wsaEvents.iErrorCode[FD_CONNECT_BIT])
665 {
666 // disconnect-callback
667 if (pCB) pCB->OnDisconn(pWait->addr, this, GetSocketErrorMsg(wsaEvents.iErrorCode[FD_CONNECT_BIT]));
668 }
669 else
670#else
671 // get error code
672 int iErrCode; socklen_t iErrCodeLen = sizeof(iErrCode);
673 if (getsockopt(fd: sock, SOL_SOCKET, SO_ERROR, optval: reinterpret_cast<char *>(&iErrCode), optlen: &iErrCodeLen) != 0)
674 {
675 close(fd: sock);
676 if (pCB) pCB->OnDisconn(AddrPeer: pWait->addr, pNetIO: this, szReason: GetSocketErrorMsg());
677 }
678 // error?
679 else if (iErrCode)
680 {
681 close(fd: sock);
682 if (pCB) pCB->OnDisconn(AddrPeer: pWait->addr, pNetIO: this, szReason: GetSocketErrorMsg(iError: iErrCode));
683 }
684 else
685#endif
686 // accept connection, do callback
687 if (!Accept(nsock: sock, ConnectAddr: pWait->addr))
688 return false;
689 }
690 }
691 }
692
693 // last: all connected sockets
694 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
695 if (pPeer->Open())
696 {
697 SOCKET sock = pPeer->GetSocket();
698
699#ifdef _WIN32
700 // get event list
701 if (::WSAEnumNetworkEvents(sock, nullptr, &wsaEvents) == SOCKET_ERROR)
702 return false;
703
704 // something to read from socket?
705 if (wsaEvents.lNetworkEvents & FD_READ)
706#else
707 // something to read from socket?
708 const auto it = std::ranges::find(fds, sock, &pollfd::fd);
709 if (it != std::ranges::end(fds) && it->revents & POLLIN)
710#endif
711 for (;;)
712 {
713 // how much?
714#ifdef _WIN32
715 DWORD iBytesToRead;
716#else
717 int iBytesToRead;
718#endif
719 if (::ioctlsocket(fd: sock, FIONREAD, &iBytesToRead) == SOCKET_ERROR)
720 {
721 pPeer->Close();
722 if (pCB) pCB->OnDisconn(AddrPeer: pPeer->GetAddr(), pNetIO: this, szReason: GetSocketErrorMsg());
723 break;
724 }
725 // The following two lines of code will make sure that if the variable
726 // "iBytesToRead" is zero, it will be increased by one.
727 // In this case, it will hold the value 1 after the operation.
728 // Note it doesn't do anything for negative values.
729 // (This comment has been sponsored by Sven2)
730 if (!iBytesToRead)
731 ++iBytesToRead;
732 // get buffer
733 void *pBuf = pPeer->GetRecvBuf(iSize: iBytesToRead);
734 // read a buffer full of data from socket
735 int iBytesRead;
736 if ((iBytesRead = ::recv(fd: sock, buf: reinterpret_cast<char *>(pBuf), n: iBytesToRead, flags: 0)) == SOCKET_ERROR)
737 {
738 // Would block? Ok, let's try this again later
739 if (HaveWouldBlockError()) { ResetSocketError(); break; }
740 // So he's serious after all...
741 pPeer->Close();
742 if (pCB) pCB->OnDisconn(AddrPeer: pPeer->GetAddr(), pNetIO: this, szReason: GetSocketErrorMsg());
743 break;
744 }
745 // nothing? this means the conection was closed, if you trust in linux manpages.
746 if (!iBytesRead)
747 {
748 pPeer->Close();
749 if (pCB) pCB->OnDisconn(AddrPeer: pPeer->GetAddr(), pNetIO: this, szReason: "connection closed");
750 break;
751 }
752 // pass to Peer::OnRecv
753 pPeer->OnRecv(iSize: iBytesRead);
754 }
755
756#ifdef _WIN32
757 // socket has become writeable?
758 if (wsaEvents.lNetworkEvents & FD_WRITE)
759#else
760 // socket has become writeable?
761 if (it != std::ranges::end(fds) && it->revents & POLLOUT)
762#endif
763 // send remaining data
764 pPeer->Send();
765
766#ifdef _WIN32
767 // socket was closed?
768 if (wsaEvents.lNetworkEvents & FD_CLOSE)
769 {
770 const char *szReason = wsaEvents.iErrorCode[FD_CLOSE_BIT] ? GetSocketErrorMsg(wsaEvents.iErrorCode[FD_CLOSE_BIT]) : "closed by peer";
771 // close socket
772 pPeer->Close();
773 // do callback
774 if (pCB) pCB->OnDisconn(pPeer->GetAddr(), this, szReason);
775 }
776#endif
777 }
778
779 // done
780 return true;
781}
782
783C4NetIOTCP::Socket::~Socket()
784{
785 if (sock != INVALID_SOCKET)
786 closesocket(fd: sock);
787}
788
789C4NetIO::addr_t C4NetIOTCP::Socket::GetAddress() const
790{
791 sockaddr_in6 addr;
792 socklen_t addrLen{sizeof(addr)};
793 C4NetIO::addr_t result;
794 if (::getsockname(fd: sock, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLen) != SOCKET_ERROR)
795 {
796 result.SetAddress(reinterpret_cast<sockaddr *>(&addr));
797 }
798 return result;
799}
800
801SOCKET C4NetIOTCP::CreateSocket(const addr_t::AddressFamily family)
802{
803 // create new socket
804 const auto &nsock = ::socket(domain: (family == C4Network2HostAddress::IPv6 ? AF_INET6 : AF_INET),
805 SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
806 if (nsock == INVALID_SOCKET)
807 {
808 SetError(strnError: "socket creation failed", fSockErr: true);
809 return INVALID_SOCKET;
810 }
811
812 if (family == C4Network2HostAddress::IPv6 && !InitIPv6Socket(socket: nsock))
813 {
814 ::closesocket(fd: nsock);
815 return INVALID_SOCKET;
816 }
817
818 return nsock;
819}
820
821std::unique_ptr<C4NetIOTCP::Socket> C4NetIOTCP::Bind(const C4NetIO::addr_t &addr) // (mt-safe)
822{
823 const auto &nsock = CreateSocket(family: addr.GetFamily());
824 if (nsock == INVALID_SOCKET) return {};
825
826 // Bind the socket to the given address
827 if (::bind(fd: nsock, addr: &addr, len: addr.GetAddrLen()) == SOCKET_ERROR)
828 {
829 SetError(strnError: "binding the socket failed", fSockErr: true);
830 ::closesocket(fd: nsock);
831 return {};
832 }
833 return std::unique_ptr<Socket>{new Socket{nsock}};
834}
835
836bool C4NetIOTCP::Connect(const addr_t &addr, const std::unique_ptr<Socket> socket) // (mt-safe)
837{
838 const auto nsock = socket->sock;
839 socket->sock = INVALID_SOCKET;
840 return Connect(addr, nsock);
841}
842
843bool C4NetIOTCP::Connect(const C4NetIO::addr_t &addr) // (mt-safe)
844{
845 // Create new socket
846 const auto &nsock = CreateSocket(family: addr.GetFamily());
847 if (nsock == INVALID_SOCKET) return false;
848
849 return Connect(addr, nsock);
850}
851
852bool C4NetIOTCP::Connect(const C4NetIO::addr_t &addr, const SOCKET nsock) // (mt-safe)
853{
854#ifdef _WIN32
855 // set event
856 if (::WSAEventSelect(nsock, Event, FD_CONNECT) == SOCKET_ERROR)
857 {
858 // set error
859 SetError("connect failed: could not set event", true);
860 closesocket(nsock);
861 return false;
862 }
863
864 // add to list
865 AddConnectWait(nsock, addr);
866#else
867 // disable blocking
868 if (::fcntl(fd: nsock, F_SETFL, fcntl(fd: nsock, F_GETFL) | O_NONBLOCK) == SOCKET_ERROR)
869 {
870 // set error
871 SetError(strnError: "connect failed: could not disable blocking", fSockErr: true);
872 close(fd: nsock);
873 return false;
874 }
875#endif
876
877 // connect (async)
878 if (::connect(fd: nsock, addr: &addr, len: addr.GetAddrLen()) == SOCKET_ERROR)
879 {
880 if (!HaveWouldBlockError()) // expected
881 {
882 SetError(strnError: "socket connection failed", fSockErr: true);
883 closesocket(fd: nsock);
884 return false;
885 }
886 }
887
888#ifndef _WIN32
889 // add to list
890 AddConnectWait(sock: nsock, addr);
891#endif
892
893 // ok
894 return true;
895}
896
897bool C4NetIOTCP::Close(const addr_t &addr) // (mt-safe)
898{
899 CStdShareLock PeerListLock(&PeerListCSec);
900 // find connect wait
901 ConnectWait *pWait = GetConnectWait(addr);
902 if (pWait)
903 {
904 // close socket, do callback
905 closesocket(fd: pWait->sock); pWait->sock = INVALID_SOCKET;
906 if (pCB) pCB->OnDisconn(AddrPeer: pWait->addr, pNetIO: this, szReason: "closed");
907 }
908 else
909 {
910 // find peer
911 Peer *pPeer = GetPeer(addr);
912 if (pPeer)
913 {
914 C4NetIO::addr_t addr = pPeer->GetAddr();
915 // close peer
916 pPeer->Close();
917 // do callback
918 if (pCB) pCB->OnDisconn(AddrPeer: addr, pNetIO: this, szReason: "closed");
919 }
920 // not found
921 else
922 return false;
923 }
924 // ok
925 return true;
926}
927
928bool C4NetIOTCP::Send(const C4NetIOPacket &rPacket) // (mt-safe)
929{
930 CStdShareLock PeerListLock(&PeerListCSec);
931 // find peer
932 Peer *pPeer = GetPeer(addr: rPacket.getAddr());
933 // not found?
934 if (!pPeer) return false;
935 // send
936 return pPeer->Send(rPacket);
937}
938
939bool C4NetIOTCP::SetBroadcast(const addr_t &addr, bool fSet) // (mt-safe)
940{
941 CStdShareLock PeerListLock(&PeerListCSec);
942 // find peer
943 Peer *pPeer = GetPeer(addr);
944 if (!pPeer) return false;
945 // set flag
946 pPeer->SetBroadcast(fSet);
947 return true;
948}
949
950bool C4NetIOTCP::Broadcast(const C4NetIOPacket &rPacket) // (mt-safe)
951{
952 CStdShareLock PeerListLock(&PeerListCSec);
953 // just send to all clients
954 bool fSuccess = true;
955 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
956 if (pPeer->Open() && pPeer->doBroadcast())
957 fSuccess &= Send(rPacket: C4NetIOPacket(rPacket.getRef(), pPeer->GetAddr()));
958 return fSuccess;
959}
960
961void C4NetIOTCP::UnBlock() // (mt-safe)
962{
963#ifdef _WIN32
964 // unblock WaitForSingleObject in C4NetIOTCP::Execute manually
965 // by setting the Event
966 WSASetEvent(Event);
967#else
968 // write one character to the pipe, this will unblock everything that
969 // waits for the FD set returned by GetFDs.
970 char c = 1;
971 write(fd: Pipe[1], buf: &c, n: 1);
972#endif
973}
974
975#ifdef _WIN32
976
977HANDLE C4NetIOTCP::GetEvent() // (mt-safe)
978{
979 return Event;
980}
981
982#else
983
984void C4NetIOTCP::GetFDs(std::vector<pollfd> &fds)
985{
986 // add pipe
987 fds.push_back(x: {.fd = Pipe[0], .events = POLLIN});
988
989 // add listener
990 if (lsock != INVALID_SOCKET)
991 {
992 fds.push_back(x: {.fd = lsock, .events = POLLIN});
993 }
994
995 // add connect waits (wait for them to become writeable)
996 CStdShareLock PeerListLock(&PeerListCSec);
997 for (ConnectWait *pWait = pConnectWaits; pWait; pWait = pWait->Next)
998 {
999 fds.push_back(x: {.fd = pWait->sock, .events = POLLOUT});
1000 }
1001 // add sockets
1002 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
1003 if (pPeer->GetSocket() != INVALID_SOCKET)
1004 {
1005 // Wait for socket to become readable
1006 // Wait for socket to become writeable, if there is data waiting
1007 fds.push_back(x: {.fd = pPeer->GetSocket(), .events = static_cast<short>(pPeer->hasWaitingData() ? POLLIN | POLLOUT : POLLIN)});
1008 }
1009}
1010
1011#endif
1012
1013bool C4NetIOTCP::GetStatistic(int *pBroadcastRate) // (mt-safe)
1014{
1015 // no broadcast
1016 if (pBroadcastRate) *pBroadcastRate = 0;
1017 return true;
1018}
1019
1020bool C4NetIOTCP::GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss) // (mt-safe)
1021{
1022 CStdShareLock PeerListLock(&PeerListCSec);
1023 // find peer
1024 Peer *pPeer = GetPeer(addr);
1025 if (!pPeer || !pPeer->Open()) return false;
1026 // return statistics
1027 if (pIRate) *pIRate = pPeer->GetIRate();
1028 if (pORate) *pORate = pPeer->GetORate();
1029 if (pLoss) *pLoss = 0;
1030 return true;
1031}
1032
1033void C4NetIOTCP::ClearStatistic()
1034{
1035 CStdShareLock PeerListLock(&PeerListCSec);
1036 // clear all peer statistics
1037 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
1038 pPeer->ClearStatistics();
1039}
1040
1041C4NetIOTCP::Peer *C4NetIOTCP::Accept(SOCKET nsock, const addr_t &ConnectAddr) // (mt-safe)
1042{
1043 addr_t caddr = ConnectAddr;
1044
1045 // accept incoming connection?
1046 C4NetIO::addr_t addr;
1047 auto addrSize{static_cast<socklen_t>(addr.GetAddrLen())};
1048 if (nsock == INVALID_SOCKET)
1049 {
1050 // accept from listener
1051 if ((nsock = ::accept(fd: lsock, addr: &addr, addr_len: &addrSize)) == INVALID_SOCKET)
1052 {
1053 // set error
1054 SetError(strnError: "socket accept failed", fSockErr: true);
1055 return nullptr;
1056 }
1057 // connect address unknown, so zero it
1058 caddr.Clear();
1059 }
1060 else
1061 {
1062 // get peer address
1063 if (::getpeername(fd: nsock, addr: &addr, len: &addrSize) == SOCKET_ERROR)
1064 {
1065#ifndef _WIN32
1066 // getpeername behaves strangely on exotic platforms. Just ignore it.
1067 if (errno != ENOTCONN)
1068 {
1069#endif
1070 // set error
1071 SetError(strnError: "could not get peer address for connected socket", fSockErr: true);
1072 return nullptr;
1073#ifndef _WIN32
1074 }
1075#endif
1076 }
1077 }
1078
1079 // check address
1080 if (addr.GetFamily() == addr_t::UnknownFamily)
1081 {
1082 // set error
1083 SetError(strnError: "socket accept failed: invalid address returned");
1084 closesocket(fd: nsock);
1085 return nullptr;
1086 }
1087
1088 // disable nagle (yep, we know what we are doing here - I think)
1089 int iNoDelay = 1;
1090 ::setsockopt(fd: nsock, IPPROTO_TCP, TCP_NODELAY, optval: reinterpret_cast<const char *>(&iNoDelay), optlen: sizeof(iNoDelay));
1091
1092#ifdef _WIN32
1093 // set event
1094 if (::WSAEventSelect(nsock, Event, FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR)
1095 {
1096 // set error
1097 SetError("connection accept failed: could not set event", true);
1098 closesocket(nsock);
1099 return nullptr;
1100 }
1101#else
1102 // disable blocking
1103 if (::fcntl(fd: nsock, F_SETFL, fcntl(fd: nsock, F_GETFL) | O_NONBLOCK) == SOCKET_ERROR)
1104 {
1105 // set error
1106 SetError(strnError: "connection accept failed: could not disable blocking", fSockErr: true);
1107 close(fd: nsock);
1108 return nullptr;
1109 }
1110#endif
1111
1112 // create new peer
1113 Peer *pnPeer = new Peer(addr, nsock, this);
1114
1115 // get required locks to add item to list
1116 CStdShareLock PeerListLock(&PeerListCSec);
1117 CStdLock PeerListAddLock(&PeerListAddCSec);
1118
1119 // add to list
1120 pnPeer->Next = pPeerList;
1121 pPeerList = pnPeer;
1122
1123 // clear add-lock
1124 PeerListAddLock.Clear();
1125
1126 // ask callback if connection should be permitted
1127 if (pCB && !pCB->OnConn(AddrPeer: addr, AddrConnect: caddr, pOwnAddr: nullptr, pNetIO: this))
1128 // close socket immediately (will be deleted later)
1129 pnPeer->Close();
1130
1131 // ok
1132 return pnPeer;
1133}
1134
1135bool C4NetIOTCP::Listen(uint16_t inListenPort)
1136{
1137 // already listening?
1138 if (lsock != INVALID_SOCKET)
1139 {
1140 // close existing socket
1141 closesocket(fd: lsock);
1142 lsock = INVALID_SOCKET;
1143 }
1144 iListenPort = addr_t::IPPORT_NONE;
1145
1146 // create socket
1147 if ((lsock = ::socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP)) == INVALID_SOCKET)
1148 {
1149 SetError(strnError: "socket creation failed", fSockErr: true);
1150 return false;
1151 }
1152
1153 if (!InitIPv6Socket(socket: lsock))
1154 return false;
1155
1156 // To be able to reuse the port after close
1157#if defined(NDEBUG) && !defined(_WIN32)
1158 int reuseaddr = 1;
1159 setsockopt(fd: lsock, SOL_SOCKET, SO_REUSEADDR, optval: reinterpret_cast<const char *>(&reuseaddr), optlen: sizeof(reuseaddr));
1160#endif
1161 // bind listen socket
1162 const addr_t addr{addr_t::Any, inListenPort};
1163 if (::bind(fd: lsock, addr: &addr, len: addr.GetAddrLen()) == SOCKET_ERROR)
1164 {
1165 SetError(strnError: "socket bind failed", fSockErr: true);
1166 closesocket(fd: lsock); lsock = INVALID_SOCKET;
1167 return false;
1168 }
1169
1170#ifdef _WIN32
1171 // set event callback
1172 if (::WSAEventSelect(lsock, Event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
1173 {
1174 SetError("could not set event for listen socket", true);
1175 closesocket(lsock); lsock = INVALID_SOCKET;
1176 return false;
1177 }
1178#endif
1179
1180 // start listening
1181 if (::listen(fd: lsock, SOMAXCONN) == SOCKET_ERROR)
1182 {
1183 SetError(strnError: "socket listen failed", fSockErr: true);
1184 closesocket(fd: lsock); lsock = INVALID_SOCKET;
1185 return false;
1186 }
1187
1188 // ok
1189 iListenPort = inListenPort;
1190 return true;
1191}
1192
1193C4NetIOTCP::Peer *C4NetIOTCP::GetPeer(const addr_t &addr) // (mt-safe)
1194{
1195 CStdShareLock PeerListLock(&PeerListCSec);
1196 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
1197 if (pPeer->Open())
1198 if (pPeer->GetAddr() == addr)
1199 return pPeer;
1200 return nullptr;
1201}
1202
1203void C4NetIOTCP::OnShareFree(CStdCSecEx *pCSec)
1204{
1205 if (pCSec == &PeerListCSec)
1206 {
1207 // clear up
1208 Peer *pPeer = pPeerList, *pLast = nullptr;
1209 while (pPeer)
1210 {
1211 // delete?
1212 if (!pPeer->Open())
1213 {
1214 // unlink
1215 Peer *pDelete = pPeer;
1216 pPeer = pPeer->Next;
1217 (pLast ? pLast->Next : pPeerList) = pPeer;
1218 // delete
1219 delete pDelete;
1220 }
1221 else
1222 {
1223 // next peer
1224 pLast = pPeer;
1225 pPeer = pPeer->Next;
1226 }
1227 }
1228 ConnectWait *pWait = pConnectWaits, *pWLast = nullptr;
1229 while (pWait)
1230 {
1231 // delete?
1232 if (pWait->sock == INVALID_SOCKET)
1233 {
1234 // unlink
1235 ConnectWait *pDelete = pWait;
1236 pWait = pWait->Next;
1237 (pWLast ? pWLast->Next : pConnectWaits) = pWait;
1238 // delete
1239 delete pDelete;
1240 }
1241 else
1242 {
1243 // next peer
1244 pWLast = pWait;
1245 pWait = pWait->Next;
1246 }
1247 }
1248 }
1249}
1250
1251void C4NetIOTCP::AddConnectWait(SOCKET sock, const addr_t &addr) // (mt-safe)
1252{
1253 CStdShareLock PeerListLock(&PeerListCSec);
1254 CStdLock PeerListAddLock(&PeerListAddCSec);
1255 // create new entry, add to list
1256 ConnectWait *pnWait = new ConnectWait;
1257 pnWait->sock = sock; pnWait->addr = addr;
1258 pnWait->Next = pConnectWaits;
1259 pConnectWaits = pnWait;
1260#ifndef _WIN32
1261 // unblock, so new FD can be realized
1262 UnBlock();
1263#endif
1264}
1265
1266C4NetIOTCP::ConnectWait *C4NetIOTCP::GetConnectWait(const addr_t &addr) // (mt-safe)
1267{
1268 CStdShareLock PeerListLock(&PeerListCSec);
1269 // search
1270 for (ConnectWait *pWait = pConnectWaits; pWait; pWait = pWait->Next)
1271 if (pWait->addr == addr)
1272 return pWait;
1273 return nullptr;
1274}
1275
1276void C4NetIOTCP::ClearConnectWaits() // (mt-safe)
1277{
1278 CStdShareLock PeerListLock(&PeerListCSec);
1279 for (ConnectWait *pWait = pConnectWaits; pWait; pWait = pWait->Next)
1280 if (pWait->sock != INVALID_SOCKET)
1281 {
1282 closesocket(fd: pWait->sock);
1283 pWait->sock = INVALID_SOCKET;
1284 }
1285}
1286
1287void C4NetIOTCP::PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf)
1288{
1289 // packet data
1290 uint8_t cFirstByte = 0xff;
1291 uint32_t iSize = rPacket.getSize();
1292 uint32_t iOASize = sizeof(cFirstByte) + sizeof(iSize) + iSize;
1293
1294 // enlarge buffer
1295 int iPos = rOutBuf.getSize();
1296 rOutBuf.Grow(iGrow: iOASize);
1297
1298 // write packet at end of outgoing buffer
1299 *rOutBuf.getMPtr<uint8_t>(pos: iPos) = cFirstByte; iPos += sizeof(uint8_t);
1300 *rOutBuf.getMPtr<uint32_t>(pos: iPos) = iSize; iPos += sizeof(uint32_t);
1301 rOutBuf.Write(Buf2: rPacket, iAt: iPos);
1302}
1303
1304size_t C4NetIOTCP::UnpackPacket(const StdBuf &IBuf, const C4NetIO::addr_t &addr)
1305{
1306 size_t iPos = 0;
1307 // check first byte (should be 0xff)
1308 if (*IBuf.getPtr<uint8_t>(pos: iPos) != 0xff)
1309 // clear buffer
1310 return IBuf.getSize();
1311 iPos += sizeof(char);
1312 // read packet size
1313 uint32_t iPacketSize;
1314 if (iPos + sizeof(uint32_t) > IBuf.getSize())
1315 return 0;
1316 iPacketSize = *IBuf.getPtr<uint32_t>(pos: iPos);
1317 iPos += sizeof(uint32_t);
1318 // packet incomplete?
1319 if (iPos + iPacketSize < iPos || iPos + iPacketSize > IBuf.getSize())
1320 return 0;
1321 // ok, call back
1322 if (pCB) pCB->OnPacket(rPacket: C4NetIOPacket(IBuf.getPart(iStart: iPos, inSize: iPacketSize), addr), pNetIO: this);
1323 // absorbed
1324 return iPos + iPacketSize;
1325}
1326
1327// * C4NetIOTCP::Peer
1328
1329const unsigned int C4NetIOTCP::Peer::iTCPHeaderSize = 28 + 24; // (bytes)
1330const unsigned int C4NetIOTCP::Peer::iMinIBufSize = 8192; // (bytes)
1331
1332// construction / destruction
1333
1334C4NetIOTCP::Peer::Peer(const C4NetIO::addr_t &naddr, SOCKET nsock, C4NetIOTCP *pnParent)
1335 : pParent(pnParent),
1336 addr(naddr), sock(nsock),
1337 Next(nullptr), iIBufUsage(0), iIRate(0), iORate(0),
1338 fOpen(true), fDoBroadcast(false) {}
1339
1340C4NetIOTCP::Peer::~Peer()
1341{
1342 // close socket
1343 Close();
1344}
1345
1346// implementation
1347
1348bool C4NetIOTCP::Peer::Send(const C4NetIOPacket &rPacket) // (mt-safe)
1349{
1350 CStdLock OLock(&OCSec);
1351
1352 // already data pending to be sent? try to sent them first (empty buffer)
1353 if (!OBuf.isNull()) Send();
1354 bool fSend = OBuf.isNull();
1355
1356 // pack packet
1357 pParent->PackPacket(rPacket, rOutBuf&: OBuf);
1358
1359 // (try to) send
1360 return fSend ? Send() : true;
1361}
1362
1363bool C4NetIOTCP::Peer::Send() // (mt-safe)
1364{
1365 CStdLock OLock(&OCSec);
1366 if (OBuf.isNull()) return true;
1367
1368 // send as much as possibile
1369 int iBytesSent;
1370 if ((iBytesSent = ::send(fd: sock, buf: OBuf.getPtr<char>(), n: OBuf.getSize(), flags: 0)) == SOCKET_ERROR)
1371 if (!HaveWouldBlockError())
1372 {
1373 pParent->SetError(strnError: "send failed", fSockErr: true);
1374 return false;
1375 }
1376
1377 // nothin sent?
1378 if (iBytesSent == SOCKET_ERROR || !iBytesSent) return true;
1379
1380 // increase output rate
1381 iORate += iBytesSent + iTCPHeaderSize;
1382
1383 // data remaining?
1384 if (unsigned(iBytesSent) < OBuf.getSize())
1385 {
1386 // Shrink buffer
1387 OBuf.Move(iFrom: iBytesSent, inSize: OBuf.getSize() - iBytesSent);
1388 OBuf.Shrink(iShrink: iBytesSent);
1389#ifndef _WIN32
1390 // Unblock parent so the FD-list can be refreshed
1391 pParent->UnBlock();
1392#endif
1393 }
1394 else
1395 // just delete buffer
1396 OBuf.Clear();
1397
1398 // ok
1399 return true;
1400}
1401
1402void *C4NetIOTCP::Peer::GetRecvBuf(int iSize) // (mt-safe)
1403{
1404 CStdLock ILock(&ICSec);
1405 // Enlarge input buffer?
1406 size_t iIBufSize = std::max<size_t>(a: iMinIBufSize, b: IBuf.getSize());
1407 while (static_cast<size_t>(iIBufUsage + iSize) > iIBufSize)
1408 iIBufSize *= 2;
1409 if (iIBufSize != IBuf.getSize())
1410 IBuf.SetSize(iIBufSize);
1411 // Return the appropriate part of the input buffer
1412 return IBuf.getMPtr(i: iIBufUsage);
1413}
1414
1415void C4NetIOTCP::Peer::OnRecv(int iSize) // (mt-safe)
1416{
1417 CStdLock ILock(&ICSec);
1418 // increase input rate and input buffer usage
1419 iIRate += iTCPHeaderSize + iSize;
1420 iIBufUsage += iSize;
1421 // a prior call to GetRecvBuf should have ensured this
1422 assert(iIBufUsage <= IBuf.getSize());
1423 // read packets
1424 size_t iPos = 0, iPacketPos;
1425 while ((iPacketPos = iPos) < static_cast<size_t>(iIBufUsage))
1426 {
1427 // Try to unpack a packet
1428 StdBuf IBufPart = IBuf.getPart(iStart: iPos, inSize: iIBufUsage - iPos);
1429 int32_t iBytes = pParent->UnpackPacket(IBuf: IBufPart, addr);
1430 // Could not unpack?
1431 if (!iBytes)
1432 break;
1433 // Advance
1434 iPos += iBytes;
1435 }
1436 // data left?
1437 if (iPacketPos < static_cast<size_t>(iIBufUsage))
1438 {
1439 // no packet read?
1440 if (!iPacketPos) return;
1441 // move data
1442 IBuf.Move(iFrom: iPacketPos, inSize: IBuf.getSize() - iPacketPos);
1443 iIBufUsage -= iPacketPos;
1444 // shrink buffer
1445 int iIBufSize = IBuf.getSize();
1446 while (iIBufUsage <= iIBufSize / 2)
1447 iIBufSize /= 2;
1448 if (iIBufSize != IBuf.getSize())
1449 IBuf.Shrink(iShrink: iPacketPos);
1450 }
1451 else
1452 {
1453 // the buffer is empty
1454 iIBufUsage = 0;
1455 // shrink buffer to minimum
1456 if (IBuf.getSize() > iMinIBufSize)
1457 IBuf.SetSize(iMinIBufSize);
1458 }
1459}
1460
1461void C4NetIOTCP::Peer::Close() // (mt-safe)
1462{
1463 CStdLock ILock(&ICSec); CStdLock OLock(&OCSec);
1464 if (!fOpen) return;
1465 // close socket
1466 closesocket(fd: sock);
1467 sock = INVALID_SOCKET;
1468 // set flag
1469 fOpen = false;
1470 // clear buffers
1471 IBuf.Clear(); OBuf.Clear();
1472 iIBufUsage = 0;
1473 // reset statistics
1474 iIRate = iORate = 0;
1475}
1476
1477void C4NetIOTCP::Peer::ClearStatistics() // (mt-safe)
1478{
1479 CStdLock ILock(&ICSec); CStdLock OLock(&OCSec);
1480 iIRate = iORate = 0;
1481}
1482
1483// *** C4NetIOSimpleUDP
1484
1485C4NetIOSimpleUDP::C4NetIOSimpleUDP()
1486 : fInit(false), fMultiCast(false), iPort(~0), sock(INVALID_SOCKET), fAllowReUse(false)
1487#ifdef _WIN32
1488 , hEvent(nullptr)
1489#endif
1490{}
1491
1492C4NetIOSimpleUDP::~C4NetIOSimpleUDP()
1493{
1494 Close();
1495}
1496
1497bool C4NetIOSimpleUDP::Init(uint16_t inPort)
1498{
1499 // reset error
1500 ResetError();
1501
1502 // already initialized? close first
1503 if (fInit) Close();
1504
1505#ifdef _WIN32
1506 // init winsock
1507 if (!AcquireWinSock())
1508 {
1509 SetError("could not start winsock");
1510 return false;
1511 }
1512#endif
1513
1514 // Create sockets
1515 sock = ::socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
1516 if (sock == INVALID_SOCKET)
1517 {
1518 SetError(strnError: "could not create socket", fSockErr: true);
1519 return false;
1520 }
1521
1522 if (!InitIPv6Socket(socket: sock))
1523 {
1524 return false;
1525 }
1526
1527 // set reuse socket option
1528 if (::setsockopt(fd: sock, SOL_SOCKET, SO_REUSEADDR, optval: reinterpret_cast<char *>(&fAllowReUse), optlen: sizeof(fAllowReUse)) == SOCKET_ERROR)
1529 {
1530 SetError(strnError: "could not set reuse options", fSockErr: true);
1531 return false;
1532 }
1533
1534 // bind socket
1535 iPort = inPort;
1536 const addr_t naddr{addr_t::Any, iPort};
1537 if (::bind(fd: sock, addr: &naddr, len: sizeof(naddr)) == SOCKET_ERROR)
1538 {
1539 SetError(strnError: "could not bind socket", fSockErr: true);
1540 return false;
1541 }
1542
1543#ifdef _WIN32
1544
1545 // create event
1546 if ((hEvent = WSACreateEvent()) == WSA_INVALID_EVENT)
1547 {
1548 SetError("could not create event", true);
1549 return false;
1550 }
1551
1552 // set event for socket
1553 if (WSAEventSelect(sock, hEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
1554 {
1555 SetError("could not select event", true);
1556 return false;
1557 }
1558
1559#else
1560
1561 // disable blocking
1562 if (::fcntl(fd: sock, F_SETFL, fcntl(fd: sock, F_GETFL) | O_NONBLOCK) == SOCKET_ERROR)
1563 {
1564 // set error
1565 SetError(strnError: "could not disable blocking", fSockErr: true);
1566 return false;
1567 }
1568
1569 // create pipe
1570 if (pipe(pipedes: Pipe) != 0)
1571 {
1572 SetError(strnError: "could not create pipe", fSockErr: true);
1573 return false;
1574 }
1575
1576#endif
1577
1578 // set flags
1579 fInit = true;
1580 fMultiCast = false;
1581
1582 // ok, that's all for know.
1583 // call InitBroadcast for more initialization fun
1584 return true;
1585}
1586
1587bool C4NetIOSimpleUDP::InitBroadcast(addr_t *pBroadcastAddr)
1588{
1589 // no error... yet
1590 ResetError();
1591
1592 // security
1593 if (!pBroadcastAddr) return false;
1594
1595 // Init() has to be called first
1596 if (!fInit) return false;
1597 // already activated?
1598 if (fMultiCast) CloseBroadcast();
1599
1600 // broadcast addr valid?
1601 if (!pBroadcastAddr->IsMulticast() || pBroadcastAddr->GetFamily() != C4Network2HostAddress::IPv6)
1602 {
1603 SetError(strnError: "invalid broadcast address (only IPv6 multicast addresses are supported)");
1604 return false;
1605 }
1606 if (pBroadcastAddr->GetPort() != iPort)
1607 {
1608 SetError(strnError: "invalid broadcast address (different port)");
1609 return false;
1610 }
1611
1612 // set mc ttl to somewhat about "same net"
1613 constexpr int ttl{16};
1614 if (setsockopt(fd: sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, optval: reinterpret_cast<const char *>(&ttl), optlen: sizeof(ttl)) == SOCKET_ERROR)
1615 {
1616 SetError(strnError: "could not set mc ttl", fSockErr: true);
1617 return false;
1618 }
1619
1620 // set up multicast group information
1621 this->MCAddr = *pBroadcastAddr;
1622 MCGrpInfo.ipv6mr_multiaddr = static_cast<sockaddr_in6>(MCAddr).sin6_addr;
1623 // TODO: do multicast on all interfaces?
1624 MCGrpInfo.ipv6mr_interface = 0; // Default interface
1625
1626 // join multicast group
1627 if (setsockopt(fd: sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
1628 optval: reinterpret_cast<const char *>(&MCGrpInfo), optlen: sizeof(MCGrpInfo)) == SOCKET_ERROR)
1629 {
1630 SetError(strnError: "could not join multicast group"); // to do: more error information
1631 return false;
1632 }
1633
1634 // (try to) disable loopback (will set fLoopback accordingly)
1635 SetMCLoopback(false);
1636
1637 // ok
1638 fMultiCast = true;
1639 return true;
1640}
1641
1642bool C4NetIOSimpleUDP::Close()
1643{
1644 // should be initialized
1645 if (!fInit) return true;
1646
1647 ResetError();
1648
1649 // deactivate multicast
1650 if (fMultiCast)
1651 CloseBroadcast();
1652
1653 // close sockets
1654 if (sock != INVALID_SOCKET)
1655 {
1656 closesocket(fd: sock);
1657 sock = INVALID_SOCKET;
1658 }
1659
1660#ifdef _WIN32
1661 // close event
1662 if (hEvent != nullptr)
1663 {
1664 WSACloseEvent(hEvent);
1665 hEvent = nullptr;
1666 }
1667
1668 // release winsock
1669 ReleaseWinSock();
1670#else
1671 // close pipes
1672 close(fd: Pipe[0]);
1673 close(fd: Pipe[1]);
1674#endif
1675
1676 // ok
1677 fInit = false;
1678 return false;
1679}
1680
1681bool C4NetIOSimpleUDP::CloseBroadcast()
1682{
1683 // multicast not active?
1684 if (!fMultiCast) return true;
1685
1686 // leave multicast group
1687 if (setsockopt(fd: sock, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP,
1688 optval: reinterpret_cast<const char *>(&MCGrpInfo), optlen: sizeof(MCGrpInfo)) == SOCKET_ERROR)
1689 {
1690 SetError(strnError: "could not leave multicast group"); // to do: more error information
1691 return false;
1692 }
1693
1694 // ok
1695 fMultiCast = false;
1696 return true;
1697}
1698
1699bool C4NetIOSimpleUDP::Execute(int iMaxTime)
1700{
1701 if (!fInit) { SetError(strnError: "not yet initialized"); return false; }
1702 ResetError();
1703
1704 // wait for socket / timeout
1705 WaitResult eWR = WaitForSocket(iTimeout: iMaxTime);
1706 if (eWR == WR_Error) return false;
1707
1708 // cancelled / timeout?
1709 if (eWR == WR_Cancelled || eWR == WR_Timeout) return true;
1710 assert(eWR == WR_Readable);
1711
1712 // read packets from socket
1713 for (;;)
1714 {
1715 // how much can be read?
1716#ifdef _WIN32
1717 u_long iMaxMsgSize;
1718#else
1719 // The FIONREAD ioctl call takes an int on unix
1720 int iMaxMsgSize;
1721#endif
1722 if (::ioctlsocket(fd: sock, FIONREAD, &iMaxMsgSize) == SOCKET_ERROR)
1723 {
1724 SetError(strnError: "Could not determine the amount of data that can be read from socket", fSockErr: true);
1725 return false;
1726 }
1727
1728 // nothing?
1729 if (!iMaxMsgSize)
1730 break;
1731 // alloc buffer
1732 C4NetIOPacket Pkt; Pkt.New(inSize: iMaxMsgSize);
1733 // read data (note: it is _not_ garantueed that iMaxMsgSize bytes are available)
1734 addr_t SrcAddr; socklen_t iSrcAddrLen{sizeof(sockaddr_in6)};
1735 int iMsgSize = ::recvfrom(fd: sock, buf: Pkt.getMPtr<char>(), n: iMaxMsgSize, flags: 0, addr: &SrcAddr, addr_len: &iSrcAddrLen);
1736 // error?
1737 if (iMsgSize == SOCKET_ERROR)
1738 if (HaveConnResetError())
1739 {
1740 // this is actually some kind of notification: an ICMP msg (unreachable)
1741 // came back, so callback and continue reading
1742 if (pCB) pCB->OnDisconn(AddrPeer: SrcAddr, pNetIO: this, szReason: GetSocketErrorMsg());
1743 continue;
1744 }
1745 else
1746 {
1747 // this is the real thing, though
1748 SetError(strnError: "could not receive data from socket", fSockErr: true);
1749 return false;
1750 }
1751 // invalid address?
1752 if ((iSrcAddrLen != sizeof(sockaddr_in) && iSrcAddrLen != sizeof(sockaddr_in6)) || SrcAddr.GetFamily() == addr_t::UnknownFamily)
1753 {
1754 SetError(strnError: "recvfrom returned an invalid address");
1755 return false;
1756 }
1757 // again: nothing?
1758 if (!iMsgSize)
1759 // docs say that the connection has been closed (whatever that means for a connectionless socket...)
1760 // let's just pretend it didn't happen, but stop reading.
1761 break;
1762 // fill in packet information
1763 Pkt.SetSize(iMsgSize);
1764 Pkt.SetAddr(SrcAddr);
1765 // callback
1766 if (pCB) pCB->OnPacket(rPacket: Pkt, pNetIO: this);
1767 }
1768
1769 // ok
1770 return true;
1771}
1772
1773bool C4NetIOSimpleUDP::Send(const C4NetIOPacket &rPacket)
1774{
1775 if (!fInit) { SetError(strnError: "not yet initialized"); return false; }
1776
1777 // send it
1778 C4NetIO::addr_t addr = rPacket.getAddr();
1779 if (::sendto(fd: sock, buf: rPacket.getPtr<char>(), n: rPacket.getSize(), flags: 0,
1780 addr: &addr, addr_len: addr.GetAddrLen())
1781 != int(rPacket.getSize()) &&
1782 !HaveWouldBlockError())
1783 {
1784 SetError(strnError: "socket sendto failed", fSockErr: true);
1785 return false;
1786 }
1787
1788 // ok
1789 ResetError();
1790 return true;
1791}
1792
1793bool C4NetIOSimpleUDP::Broadcast(const C4NetIOPacket &rPacket)
1794{
1795 // just set broadcast address and send
1796 return C4NetIOSimpleUDP::Send(rPacket: C4NetIOPacket(rPacket.getRef(), MCAddr));
1797}
1798
1799#ifdef _WIN32
1800
1801void C4NetIOSimpleUDP::UnBlock() // (mt-safe)
1802{
1803 // unblock WaitForSingleObject in C4NetIOTCP::Execute manually
1804 // by setting the Event
1805 WSASetEvent(hEvent);
1806}
1807
1808HANDLE C4NetIOSimpleUDP::GetEvent() // (mt-safe)
1809{
1810 return hEvent;
1811}
1812
1813enum C4NetIOSimpleUDP::WaitResult C4NetIOSimpleUDP::WaitForSocket(int iTimeout)
1814{
1815 // wait for anything to happen
1816 DWORD ret = WaitForSingleObject(hEvent, iTimeout);
1817 if (ret == WAIT_TIMEOUT)
1818 return WR_Timeout;
1819 if (ret == WAIT_FAILED)
1820 {
1821 SetError("Wait for Event failed"); return WR_Error;
1822 }
1823 // get socket events (and reset the event)
1824 WSANETWORKEVENTS wsaEvents;
1825 if (WSAEnumNetworkEvents(sock, hEvent, &wsaEvents) == SOCKET_ERROR)
1826 {
1827 SetError("could not enumerate network events!"); return WR_Error;
1828 }
1829 // socket readable?
1830 if (wsaEvents.lNetworkEvents | FD_READ)
1831 return WR_Readable;
1832 // in case the event was set without the socket beeing readable,
1833 // the operation has been cancelled (see Unblock())
1834 WSAResetEvent(hEvent);
1835 return WR_Cancelled;
1836}
1837
1838#else
1839
1840void C4NetIOSimpleUDP::UnBlock() // (mt-safe)
1841{
1842 // write one character to the pipe, this will unblock everything that
1843 // waits for the FD set returned by GetFDs.
1844 char c = 42;
1845 write(fd: Pipe[1], buf: &c, n: 1);
1846}
1847
1848void C4NetIOSimpleUDP::GetFDs(std::vector<pollfd> &fds)
1849{
1850 fds.push_back(x: {.fd = Pipe[0], .events = POLLIN});
1851 // add socket
1852 if (sock != INVALID_SOCKET)
1853 {
1854 fds.push_back(x: {.fd = sock, .events = POLLIN});
1855 }
1856}
1857
1858enum C4NetIOSimpleUDP::WaitResult C4NetIOSimpleUDP::WaitForSocket(int iTimeout)
1859{
1860 std::vector<pollfd> fds;
1861 GetFDs(fds);
1862
1863 // wait for anything to happen
1864 int ret = poll(fds: fds.data(), nfds: fds.size(), timeout: iTimeout);
1865 // catch simple cases
1866 if (ret < 0)
1867 {
1868 SetError(strnError: "poll failed", fSockErr: true); return WR_Error;
1869 }
1870 if (!ret)
1871 return WR_Timeout;
1872 // flush pipe, if neccessary
1873 if (fds[0].revents & POLLIN)
1874 {
1875 char c; ::read(fd: Pipe[0], buf: &c, nbytes: 1);
1876 }
1877 // socket readable?
1878 return fds.size() > 1 && fds[1].revents & POLLIN ? WR_Readable : WR_Cancelled;
1879}
1880
1881#endif
1882
1883bool C4NetIOSimpleUDP::SetMCLoopback(int fLoopback)
1884{
1885 // enable/disable MC loopback
1886 setsockopt(fd: sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, optval: reinterpret_cast<char *>(&fLoopback), optlen: sizeof(fLoopback));
1887 // read result
1888 socklen_t iSize = sizeof(fLoopback);
1889 if (getsockopt(fd: sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, optval: reinterpret_cast<char *>(&fLoopback), optlen: &iSize) == SOCKET_ERROR)
1890 return false;
1891 fMCLoopback = !!fLoopback;
1892 return true;
1893}
1894
1895void C4NetIOSimpleUDP::SetReUseAddress(bool fAllow)
1896{
1897 fAllowReUse = fAllow;
1898}
1899
1900// *** C4NetIOUDP
1901
1902// * build options / constants / structures
1903
1904// Check immediately when missing packets are detected?
1905#define C4NETIOUDP_OPT_RECV_CHECK_IMMEDIATE
1906
1907// Protocol version
1908const unsigned int C4NetIOUDP::iVersion = 2;
1909
1910// Standard timeout length
1911const unsigned int C4NetIOUDP::iStdTimeout = 1000; // (ms)
1912
1913// Time interval for connection checks
1914// Equals the maximum time that C4NetIOUDP::Execute might block
1915const unsigned int C4NetIOUDP::iCheckInterval = 1000; // (ms)
1916
1917const unsigned int C4NetIOUDP::iMaxOPacketBacklog = 10000;
1918
1919const unsigned int C4NetIOUDP::iUDPHeaderSize = 8 + 24; // (bytes)
1920
1921#pragma pack (push, 1)
1922
1923// We need to adapt C4NetIO::addr_t to put it in our UDP packages.
1924// Previously, the sockaddr_in struct was just put in directly. This is
1925// horribly non-portable though, especially as the value of AF_INET6 differs
1926// between platforms.
1927struct C4NetIOUDP::BinAddr
1928{
1929 BinAddr() : type{0}, v6{} {}
1930
1931 BinAddr(const C4NetIO::addr_t &addr) : v6{}
1932 {
1933 switch (addr.GetFamily())
1934 {
1935 case C4Network2HostAddress::IPv4:
1936 {
1937 type = 1;
1938 const auto addr4 = static_cast<const sockaddr_in *>(&addr);
1939 static_assert(sizeof(v4) == sizeof(addr4->sin_addr), "unexpected IPv4 address size");
1940 std::memcpy(dest: &v4, src: &addr4->sin_addr, n: sizeof(v4));
1941 break;
1942 }
1943 case C4Network2HostAddress::IPv6:
1944 {
1945 type = 2;
1946 const auto addr6 = static_cast<const sockaddr_in6 *>(&addr);
1947 static_assert(sizeof(v6) == sizeof(addr6->sin6_addr), "unexpected IPv6 address size");
1948 std::memcpy(dest: &v6, src: &addr6->sin6_addr, n: sizeof(v6));
1949 break;
1950 }
1951 default:
1952 assert(!"Unexpected address family");
1953 }
1954 port = addr.GetPort();
1955 }
1956
1957 operator C4NetIO::addr_t() const
1958 {
1959 C4NetIO::addr_t result;
1960 switch (type)
1961 {
1962 case 1:
1963 {
1964 sockaddr_in addr4{};
1965 addr4.sin_family = AF_INET;
1966 std::memcpy(dest: &addr4.sin_addr, src: &v4, n: sizeof(v4));
1967 result.SetAddress(reinterpret_cast<sockaddr *>(&addr4));
1968 break;
1969 }
1970 case 2:
1971 {
1972 sockaddr_in6 addr6{};
1973 addr6.sin6_family = AF_INET6;
1974 std::memcpy(dest: &addr6.sin6_addr, src: &v6, n: sizeof(v6));
1975 result.SetAddress(reinterpret_cast<sockaddr *>(&addr6));
1976 break;
1977 }
1978 default:
1979 assert(!"Invalid address type");
1980 }
1981 result.SetPort(port);
1982 return result;
1983 }
1984
1985 std::string ToString() const
1986 {
1987 return static_cast<C4NetIO::addr_t>(*this).ToString();
1988 }
1989
1990 std::uint16_t port;
1991 std::uint8_t type;
1992 union
1993 {
1994 std::uint8_t v4[4];
1995 std::uint8_t v6[16];
1996 };
1997};
1998
1999// packet structures
2000struct C4NetIOUDP::PacketHdr
2001{
2002 uint8_t StatusByte;
2003 uint32_t Nr; // packet nr
2004};
2005
2006struct C4NetIOUDP::ConnPacket : public PacketHdr
2007{
2008 uint32_t ProtocolVer;
2009 BinAddr Addr;
2010 BinAddr MCAddr;
2011};
2012
2013struct C4NetIOUDP::ConnOKPacket : public PacketHdr
2014{
2015 enum { MCM_NoMC, MCM_MC, MCM_MCOK, } MCMode;
2016 BinAddr Addr;
2017};
2018
2019struct C4NetIOUDP::AddAddrPacket : public PacketHdr
2020{
2021 BinAddr Addr;
2022 BinAddr NewAddr;
2023};
2024
2025struct C4NetIOUDP::DataPacketHdr : public PacketHdr
2026{
2027 Packet::nr_t FNr; // start fragment of this series
2028 uint32_t Size; // packet size (all fragments)
2029};
2030
2031struct C4NetIOUDP::CheckPacketHdr : public PacketHdr
2032{
2033 uint32_t AckNr, MCAckNr; // numbers of the last packets received
2034 uint32_t AskCount, MCAskCount;
2035};
2036
2037struct C4NetIOUDP::ClosePacket : public PacketHdr
2038{
2039 BinAddr Addr;
2040};
2041
2042struct C4NetIOUDP::TestPacket : public PacketHdr
2043{
2044 unsigned int TestNr;
2045};
2046
2047#pragma pack (pop)
2048
2049// construction / destruction
2050
2051C4NetIOUDP::C4NetIOUDP()
2052 : fInit(false), fMultiCast(false), iPort(~0),
2053 pPeerList(nullptr),
2054 iNextCheck(0),
2055 iOPacketCounter(0),
2056 fDelayedLoopbackTest(false),
2057 iBroadcastRate(0),
2058 PeerListCSec(this),
2059 OPackets(iMaxOPacketBacklog),
2060 fSavePacket(false) {}
2061
2062C4NetIOUDP::~C4NetIOUDP()
2063{
2064 Close();
2065}
2066
2067bool C4NetIOUDP::Init(uint16_t inPort)
2068{
2069 // already initialized? close first
2070 if (fInit) Close();
2071
2072#ifdef C4NETIO_DEBUG
2073 // open log
2074 OpenDebugLog();
2075#endif
2076
2077 // Initialize UDP
2078 if (!C4NetIOSimpleUDP::Init(inPort))
2079 return false;
2080 iPort = inPort;
2081
2082 // set callback
2083 C4NetIOSimpleUDP::SetCallback(CBProxy(this));
2084
2085 // set flags
2086 fInit = true;
2087 fMultiCast = false;
2088 iNextCheck = timeGetTime() + iCheckInterval;
2089
2090 // ok, that's all for now.
2091 // call InitBroadcast for more initialization fun
2092 return true;
2093}
2094
2095bool C4NetIOUDP::InitBroadcast(addr_t *pBroadcastAddr)
2096{
2097 // no error... yet
2098 ResetError();
2099
2100 // security
2101 if (!pBroadcastAddr) return false;
2102
2103 // Init() has to be called first
2104 if (!fInit) return false;
2105 // already activated?
2106 if (fMultiCast) CloseBroadcast();
2107
2108 // set up multicast group information
2109 C4NetIO::addr_t MCAddr = *pBroadcastAddr;
2110
2111 // broadcast addr valid?
2112 if (!MCAddr.IsMulticast())
2113 {
2114 // port is needed in order to search a mc address automatically
2115 if (!iPort)
2116 {
2117 SetError(strnError: "broadcast address is not valid");
2118 return false;
2119 }
2120
2121 // Set up address as unicast-prefix-based IPv6 multicast address (RFC 3306).
2122 sockaddr_in6 saddrgen{};
2123 saddrgen.sin6_family = AF_INET6;
2124 auto addrgen = saddrgen.sin6_addr.s6_addr;
2125
2126 // ff3e ("global multicast based on network prefix") : 64 (length of network prefix)
2127 static constexpr unsigned char mcastPrefix[]{ 0xff, 0x3e, 0, 64 };
2128 std::memcpy(dest: addrgen, src: mcastPrefix, n: sizeof(mcastPrefix));
2129 addrgen += sizeof(mcastPrefix);
2130
2131 // 64 bit network prefix
2132 addr_t prefixAddr;
2133 for (const auto &addr : GetLocalAddresses())
2134 {
2135 if (addr.GetFamily() == C4Network2HostAddress::IPv6 && !addr.IsLocal())
2136 {
2137 prefixAddr.SetAddress(host: addr);
2138 break;
2139 }
2140 }
2141 if (prefixAddr.IsNull())
2142 {
2143 SetError(strnError: "no IPv6 unicast address available");
2144 return false;
2145 }
2146
2147 static constexpr std::size_t networkPrefixSize{8};
2148 std::memcpy(dest: addrgen, src: &static_cast<sockaddr_in6 *>(&prefixAddr)->sin6_addr, n: networkPrefixSize);
2149 addrgen += networkPrefixSize;
2150
2151 // 32 bit group id: search for a free one
2152 for (int iRetries = 1000; iRetries; iRetries--)
2153 {
2154 const auto rnd = static_cast<std::uint32_t>(std::rand()); // FIXME: better replacement for UnsyncedRandom()?
2155 std::memcpy(dest: addrgen, src: &rnd, n: sizeof(rnd));
2156
2157 // "high-order bit of the Group ID will be the same value as the T flag"
2158 addrgen[0] |= 0x80;
2159
2160 // create new - random - address
2161 MCAddr.SetAddress(reinterpret_cast<sockaddr *>(&saddrgen));
2162 MCAddr.SetPort(iPort);
2163 // init broadcast
2164 if (!C4NetIOSimpleUDP::InitBroadcast(pBroadcastAddr: &MCAddr))
2165 return false;
2166 // do the loopback test
2167 if (!DoLoopbackTest())
2168 {
2169 C4NetIOSimpleUDP::CloseBroadcast();
2170 if (!GetError()) SetError(strnError: "multicast loopback test failed");
2171 return false;
2172 }
2173 // send a ping packet
2174 const PacketHdr PingPacket = { .StatusByte: IPID_Ping | 0x80, .Nr: 0 };
2175 if (!C4NetIOSimpleUDP::Broadcast(rPacket: C4NetIOPacket(&PingPacket, sizeof(PingPacket))))
2176 {
2177 C4NetIOSimpleUDP::CloseBroadcast();
2178 return false;
2179 }
2180 bool fSuccess = false;
2181 for (;;)
2182 {
2183 fSavePacket = true; LastPacket.Clear();
2184 // wait for something to happen
2185 if (!C4NetIOSimpleUDP::Execute(iMaxTime: iStdTimeout))
2186 {
2187 fSavePacket = false;
2188 C4NetIOSimpleUDP::CloseBroadcast();
2189 return false;
2190 }
2191 fSavePacket = false;
2192 // Timeout? So expect this address to be unused
2193 if (LastPacket.isNull()) { fSuccess = true; break; }
2194 // looped back?
2195 if (C4NetIOSimpleUDP::getMCLoopback() && LastPacket.getAddr() == MCLoopbackAddr)
2196 // ignore this one
2197 continue;
2198 // otherwise: there must be someone else in this MC group
2199 C4NetIOSimpleUDP::CloseBroadcast();
2200 break;
2201 }
2202 if (fSuccess) break;
2203 // no success? try again...
2204 }
2205
2206 // return found address
2207 *pBroadcastAddr = MCAddr;
2208 }
2209 else
2210 {
2211 // check: must be same port
2212 if (MCAddr.GetPort() == iPort)
2213 {
2214 SetError(strnError: "invalid multicast address: wrong port");
2215 return false;
2216 }
2217 // init
2218 if (!C4NetIOSimpleUDP::InitBroadcast(pBroadcastAddr: &MCAddr))
2219 return false;
2220 // do loopback test (if not delayed)
2221 if (!fDelayedLoopbackTest)
2222 if (!DoLoopbackTest())
2223 {
2224 C4NetIOSimpleUDP::CloseBroadcast();
2225 if (!GetError()) SetError(strnError: "multicast loopback test failed");
2226 return false;
2227 }
2228 }
2229
2230 // (try to) disable multicast loopback
2231 C4NetIOSimpleUDP::SetMCLoopback(false);
2232
2233 // set flags
2234 fMultiCast = true;
2235 iOPacketCounter = 0;
2236 iBroadcastRate = 0;
2237
2238 // ok
2239 return true;
2240}
2241
2242bool C4NetIOUDP::Close()
2243{
2244 // should be initialized
2245 if (!fInit) return false;
2246
2247 // close all peers
2248 CStdShareLock PeerListLock(&PeerListCSec);
2249 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
2250 pPeer->Close(szReason: "owner class closed");
2251 PeerListLock.Clear();
2252
2253 // deactivate multicast
2254 if (fMultiCast)
2255 CloseBroadcast();
2256
2257 // close UDP
2258 bool fSuccess = C4NetIOSimpleUDP::Close();
2259
2260#ifdef C4NETIO_DEBUG
2261 // close log
2262 CloseDebugLog();
2263#endif
2264
2265 // ok
2266 fInit = false;
2267 return fSuccess;
2268}
2269
2270bool C4NetIOUDP::CloseBroadcast()
2271{
2272 ResetError();
2273
2274 // multicast not active?
2275 if (!fMultiCast) return true;
2276
2277 // ok
2278 fMultiCast = false;
2279 return C4NetIOSimpleUDP::CloseBroadcast();
2280}
2281
2282bool C4NetIOUDP::Execute(int iMaxTime) // (mt-safe)
2283{
2284 if (!fInit) { SetError(strnError: "not yet initialized"); return false; }
2285
2286 CStdLock ExecuteLock(&ExecuteCSec);
2287 CStdShareLock PeerListLock(&PeerListCSec);
2288
2289 ResetError();
2290
2291 // adjust maximum block time
2292 int iMaxBlock = GetTimeout();
2293 if (iMaxTime == StdSync::Infinite || iMaxTime > iMaxBlock) iMaxTime = iMaxBlock;
2294
2295 // execute subclass
2296 if (!C4NetIOSimpleUDP::Execute(iMaxTime: iMaxBlock))
2297 return false;
2298
2299 // connection check needed?
2300 if (iNextCheck <= timeGetTime())
2301 DoCheck();
2302 // client timeout?
2303 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
2304 if (!pPeer->Closed())
2305 pPeer->CheckTimeout();
2306
2307 // do a delayed loopback test once the incoming buffer is empty
2308 if (fDelayedLoopbackTest)
2309 {
2310 if (fMultiCast)
2311 fMultiCast = DoLoopbackTest();
2312 fDelayedLoopbackTest = false;
2313 }
2314
2315 // ok
2316 return true;
2317}
2318
2319bool C4NetIOUDP::Connect(const addr_t &addr) // (mt-safe)
2320{
2321 // connect
2322 return !!ConnectPeer(PeerAddr: addr, fFailCallback: true);
2323}
2324
2325bool C4NetIOUDP::Close(const addr_t &addr) // (mt-safe)
2326{
2327 CStdShareLock PeerListLock(&PeerListCSec);
2328 // find peer
2329 Peer *pPeer = GetPeer(addr);
2330 if (!pPeer) return false;
2331 // close
2332 pPeer->Close(szReason: "closed");
2333 return true;
2334}
2335
2336bool C4NetIOUDP::Send(const C4NetIOPacket &rPacket) // (mt-safe)
2337{
2338 // find Peer class for given address
2339 CStdShareLock PeerListLock(&PeerListCSec);
2340 Peer *pPeer = GetPeer(addr: rPacket.getAddr());
2341 // not found?
2342 if (!pPeer) return false;
2343 // send the packet
2344 return pPeer->Send(rPacket);
2345}
2346
2347bool C4NetIOUDP::Broadcast(const C4NetIOPacket &rPacket) // (mt-safe)
2348{
2349 CStdShareLock PeerListLock(&PeerListCSec);
2350 // search: any client reachable via multicast?
2351 Peer *pPeer;
2352 for (pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
2353 if (pPeer->Open() && pPeer->MultiCast() && pPeer->doBroadcast())
2354 break;
2355 bool fSuccess = true;
2356 if (pPeer)
2357 {
2358 CStdLock OutLock(&OutCSec);
2359 // send it via multicast: encapsulate packet
2360 Packet *pPkt = new Packet(rPacket.Duplicate(), iOPacketCounter);
2361 iOPacketCounter += pPkt->FragmentCnt();
2362 // add to list
2363 OPackets.AddPacket(pPacket: pPkt);
2364 // send it
2365 fSuccess &= BroadcastDirect(rPacket: *pPkt);
2366 }
2367 // send to all clients connected via du, too
2368 for (pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
2369 if (pPeer->Open() && !pPeer->MultiCast() && pPeer->doBroadcast())
2370 pPeer->Send(rPacket);
2371 return true;
2372}
2373
2374bool C4NetIOUDP::SetBroadcast(const addr_t &addr, bool fSet) // (mt-safe)
2375{
2376 CStdShareLock PeerListLock(&PeerListCSec);
2377 // find peer
2378 Peer *pPeer = GetPeer(addr);
2379 if (!pPeer) return false;
2380 // set flag
2381 pPeer->SetBroadcast(fSet);
2382 return true;
2383}
2384
2385int C4NetIOUDP::GetTimeout() // (mt-safe)
2386{
2387 // maximum time: check interval
2388 int iTiming = iCheckInterval;
2389 // check timeout
2390 if (iNextCheck)
2391 iTiming = std::max<int>(a: int(iNextCheck) - timeGetTime(), b: 0);
2392 // client timeouts (e.g. connection timeout)
2393 CStdShareLock PeerListLock(&PeerListCSec);
2394 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
2395 if (!pPeer->Closed())
2396 if (pPeer->GetTimeout())
2397 iTiming = (std::max)(a: std::min<int>(a: iTiming, b: int(pPeer->GetTimeout() - timeGetTime())), b: 0);
2398 // return timing value
2399 return iTiming;
2400}
2401
2402bool C4NetIOUDP::GetStatistic(int *pBroadcastRate) // (mt-safe)
2403{
2404 CStdLock StatLock(&StatCSec);
2405 if (pBroadcastRate) *pBroadcastRate = iBroadcastRate;
2406 return true;
2407}
2408
2409bool C4NetIOUDP::GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss) // (mt-safe)
2410{
2411 CStdShareLock PeerListLock(&PeerListCSec);
2412 // find peer
2413 Peer *pPeer = GetPeer(addr);
2414 if (!pPeer || !pPeer->Open()) return false;
2415 // return statistics
2416 if (pIRate) *pIRate = pPeer->GetIRate();
2417 if (pORate) *pORate = pPeer->GetORate();
2418 if (pLoss) *pLoss = 0;
2419 return true;
2420}
2421
2422void C4NetIOUDP::ClearStatistic()
2423{
2424 CStdShareLock PeerListLock(&PeerListCSec);
2425 // clear all peer statistics
2426 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
2427 pPeer->ClearStatistics();
2428 // broadcast statistics
2429 CStdLock StatLock(&StatCSec);
2430 iBroadcastRate = 0;
2431}
2432
2433void C4NetIOUDP::OnPacket(const C4NetIOPacket &Packet, C4NetIO *pNetIO)
2434{
2435 assert(pNetIO == this);
2436#ifdef C4NETIO_DEBUG
2437 // log it
2438 DebugLogPkt(false, Packet);
2439#endif
2440 // save packet?
2441 if (fSavePacket)
2442 {
2443 LastPacket.Copy(Buf2: Packet);
2444 return;
2445 }
2446 // looped back?
2447 if (fMultiCast && !fDelayedLoopbackTest)
2448 if (Packet.getAddr() == MCLoopbackAddr)
2449 return;
2450 // loopback test packet? ignore
2451 if ((Packet.getStatus() & 0x7F) == IPID_Test) return;
2452 // address add? process directly
2453
2454 // find out who's responsible
2455 Peer *pPeer = GetPeer(addr: Packet.getAddr());
2456 // new connection?
2457 if (!pPeer)
2458 {
2459 // ping? answer without creating a connection
2460 if ((Packet.getStatus() & 0x7F) == IPID_Ping)
2461 {
2462 PacketHdr PingPacket = { .StatusByte: static_cast<uint8_t>(IPID_Ping | (Packet.getStatus() & 0x80)), .Nr: 0 };
2463 SendDirect(packet: C4NetIOPacket(&PingPacket, sizeof(PingPacket), false, Packet.getAddr()));
2464 return;
2465 }
2466 // conn? create connection (du only!)
2467 else if (Packet.getStatus() == IPID_Conn)
2468 {
2469 pPeer = ConnectPeer(PeerAddr: Packet.getAddr(), fFailCallback: false);
2470 if (!pPeer) return;
2471 }
2472 // ignore all other packets
2473 }
2474 else
2475 {
2476 // address add?
2477 if (Packet.getStatus() == IPID_AddAddr)
2478 {
2479 OnAddAddress(FromAddr: Packet.getAddr(), Packet: *Packet.getPtr<AddAddrPacket>()); return;
2480 }
2481
2482 // forward to Peer object
2483 pPeer->OnRecv(Packet);
2484 }
2485}
2486
2487bool C4NetIOUDP::OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO)
2488{
2489 // ignore
2490 return true;
2491}
2492
2493void C4NetIOUDP::OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason)
2494{
2495 assert(pNetIO == this);
2496
2497 // C4NetIOSimple thinks the given address is no-good and we shouldn't consider
2498 // any connection to this address valid.
2499
2500 // So let's check wether we have some peer there
2501 Peer *pPeer = GetPeer(addr: AddrPeer);
2502 if (!pPeer) return;
2503
2504 // And close him (this will issue another callback)
2505 pPeer->Close(szReason);
2506}
2507
2508void C4NetIOUDP::OnAddAddress(const addr_t &FromAddr, const AddAddrPacket &Packet)
2509{
2510 // Security (this would be strange behavior indeed...)
2511 if (FromAddr != Packet.Addr && FromAddr != Packet.NewAddr) return;
2512 // Search peer(s)
2513 Peer *pPeer = GetPeer(addr: Packet.Addr);
2514 Peer *pPeer2 = GetPeer(addr: Packet.NewAddr);
2515 // Equal or not found? Nothing to do...
2516 if (!pPeer || pPeer == pPeer2) return;
2517 // Save alternate address
2518 pPeer->SetAltAddr(Packet.NewAddr);
2519 // Close superflous connection
2520 // (this will generate a close-packet, which will be ignored by the peer)
2521 pPeer2->Close(szReason: "address equivalence detected");
2522}
2523
2524// * C4NetIOUDP::Packet
2525
2526// construction / destruction
2527
2528C4NetIOUDP::Packet::Packet()
2529 : iNr(~0),
2530 Data(),
2531 pFragmentGot(nullptr) {}
2532
2533C4NetIOUDP::Packet::Packet(C4NetIOPacket &&rnData, nr_t inNr)
2534 : iNr(inNr),
2535 Data(rnData),
2536 pFragmentGot(nullptr) {}
2537
2538C4NetIOUDP::Packet::~Packet()
2539{
2540 delete[] pFragmentGot; pFragmentGot = nullptr;
2541}
2542
2543// implementation
2544
2545const size_t C4NetIOUDP::Packet::MaxSize = 512;
2546const size_t C4NetIOUDP::Packet::MaxDataSize = MaxSize - sizeof(DataPacketHdr);
2547
2548C4NetIOUDP::Packet::nr_t C4NetIOUDP::Packet::FragmentCnt() const
2549{
2550 return Data.getSize() ? (Data.getSize() - 1) / MaxDataSize + 1 : 1;
2551}
2552
2553C4NetIOPacket C4NetIOUDP::Packet::GetFragment(nr_t iFNr, bool fBroadcastFlag) const
2554{
2555 assert(iFNr < FragmentCnt());
2556 // create buffer
2557 const auto iFragmentSize = FragmentSize(iFNr);
2558 StdBuf Packet; Packet.New(inSize: sizeof(DataPacketHdr) + iFragmentSize);
2559 // set up header
2560 DataPacketHdr *pnHdr = Packet.getMPtr<DataPacketHdr>();
2561 pnHdr->StatusByte = IPID_Data | (fBroadcastFlag ? 0x80 : 0x00);
2562 pnHdr->Nr = iNr + iFNr;
2563 pnHdr->FNr = iNr;
2564 pnHdr->Size = Data.getSize();
2565 // copy data
2566 Packet.Write(Buf2: Data.getPart(iStart: iFNr * MaxDataSize, inSize: iFragmentSize),
2567 iAt: sizeof(DataPacketHdr));
2568 // return
2569 return C4NetIOPacket(Packet, Data.getAddr());
2570}
2571
2572bool C4NetIOUDP::Packet::Complete() const
2573{
2574 if (Empty()) return false;
2575 for (unsigned int i = 0; i < FragmentCnt(); i++)
2576 if (!FragmentPresent(iFNr: i))
2577 return false;
2578 return true;
2579}
2580
2581bool C4NetIOUDP::Packet::FragmentPresent(uint32_t iFNr) const
2582{
2583 return !Empty() && iFNr < FragmentCnt() && (!pFragmentGot || pFragmentGot[iFNr]);
2584}
2585
2586bool C4NetIOUDP::Packet::AddFragment(const C4NetIOPacket &Packet, const C4NetIO::addr_t &addr)
2587{
2588 // ensure the packet is big enough
2589 if (Packet.getSize() < sizeof(DataPacketHdr)) return false;
2590 size_t iPacketDataSize = Packet.getSize() - sizeof(DataPacketHdr);
2591 // get header
2592 const DataPacketHdr *pHdr = Packet.getPtr<DataPacketHdr>();
2593 // first fragment got?
2594 bool fFirstFragment = Empty();
2595 if (fFirstFragment)
2596 {
2597 // init
2598 iNr = pHdr->FNr;
2599 Data.New(inSize: pHdr->Size); Data.SetAddr(addr);
2600 // fragmented? create fragment list
2601 if (FragmentCnt() > 1)
2602 memset(s: pFragmentGot = new bool[FragmentCnt()], c: false, n: FragmentCnt());
2603 // check header
2604 if (pHdr->Nr < iNr || pHdr->Nr >= iNr + FragmentCnt()) { Data.Clear(); return false; }
2605 }
2606 else
2607 {
2608 // check header
2609 if (pHdr->FNr != iNr) return false;
2610 if (pHdr->Size != Data.getSize()) return false;
2611 if (pHdr->Nr < iNr || pHdr->Nr >= iNr + FragmentCnt()) return false;
2612 }
2613 // check packet size
2614 nr_t iFNr = pHdr->Nr - iNr;
2615 if (iPacketDataSize != FragmentSize(iFNr)) return false;
2616 // already got this fragment? (needs check for first packet as FragmentPresent always assumes true if pFragmentGot is nullptr)
2617 StdBuf PacketData = Packet.getPart(iStart: sizeof(DataPacketHdr), inSize: iPacketDataSize);
2618 if (!fFirstFragment && FragmentPresent(iFNr))
2619 {
2620 // compare
2621 if (Data.Compare(Buf2: PacketData, iAt: iFNr * MaxDataSize))
2622 return false;
2623 }
2624 else
2625 {
2626 // otherwise: copy data
2627 Data.Write(Buf2: PacketData, iAt: iFNr * MaxDataSize);
2628 // set flag (if fragmented)
2629 if (pFragmentGot)
2630 pFragmentGot[iFNr] = true;
2631 // shouldn't happen
2632 else
2633 assert(Complete());
2634 }
2635 // ok
2636 return true;
2637}
2638
2639size_t C4NetIOUDP::Packet::FragmentSize(nr_t iFNr) const
2640{
2641 assert(iFNr < FragmentCnt());
2642 return (std::min)(a: MaxDataSize, b: Data.getSize() - iFNr * MaxDataSize);
2643}
2644
2645// * C4NetIOUDP::PacketList
2646
2647// construction / destruction
2648
2649C4NetIOUDP::PacketList::PacketList(unsigned int inMaxPacketCnt)
2650 : pFront(nullptr),
2651 iMaxPacketCnt(inMaxPacketCnt),
2652 iPacketCnt(0),
2653 pBack(nullptr) {}
2654
2655C4NetIOUDP::PacketList::~PacketList()
2656{
2657 Clear();
2658}
2659
2660C4NetIOUDP::Packet *C4NetIOUDP::PacketList::GetPacket(unsigned int iNr)
2661{
2662 CStdShareLock ListLock(&ListCSec);
2663 for (Packet *pPkt = pBack; pPkt; pPkt = pPkt->Prev)
2664 if (pPkt->GetNr() == iNr)
2665 return pPkt;
2666 else if (pPkt->GetNr() < iNr)
2667 return nullptr;
2668 return nullptr;
2669}
2670
2671C4NetIOUDP::Packet *C4NetIOUDP::PacketList::GetPacketFrgm(unsigned int iNr)
2672{
2673 CStdShareLock ListLock(&ListCSec);
2674 for (Packet *pPkt = pBack; pPkt; pPkt = pPkt->Prev)
2675 if (pPkt->GetNr() <= iNr && pPkt->GetNr() + pPkt->FragmentCnt() > iNr)
2676 return pPkt;
2677 else if (pPkt->GetNr() < iNr)
2678 return nullptr;
2679 return nullptr;
2680}
2681
2682C4NetIOUDP::Packet *C4NetIOUDP::PacketList::GetFirstPacketComplete()
2683{
2684 CStdShareLock ListLock(&ListCSec);
2685 return pFront && pFront->Complete() ? pFront : nullptr;
2686}
2687
2688bool C4NetIOUDP::PacketList::FragmentPresent(unsigned int iNr)
2689{
2690 CStdShareLock ListLock(&ListCSec);
2691 Packet *pPkt = GetPacketFrgm(iNr);
2692 return pPkt ? pPkt->FragmentPresent(iFNr: iNr - pPkt->GetNr()) : false;
2693}
2694
2695bool C4NetIOUDP::PacketList::AddPacket(Packet *pPacket)
2696{
2697 CStdLock ListLock(&ListCSec);
2698 // find insert location
2699 Packet *pInsertAfter = pBack, *pInsertBefore = nullptr;
2700 for (; pInsertAfter; pInsertBefore = pInsertAfter, pInsertAfter = pInsertAfter->Prev)
2701 if (pInsertAfter->GetNr() + pInsertAfter->FragmentCnt() <= pPacket->GetNr())
2702 break;
2703 // check: enough space?
2704 if (pInsertBefore && pInsertBefore->GetNr() < pPacket->GetNr() + pPacket->FragmentCnt())
2705 return false;
2706 // insert
2707 (pInsertAfter ? pInsertAfter->Next : pFront) = pPacket;
2708 (pInsertBefore ? pInsertBefore->Prev : pBack) = pPacket;
2709 pPacket->Next = pInsertBefore;
2710 pPacket->Prev = pInsertAfter;
2711 // count packets, check limit
2712 ++iPacketCnt;
2713 while (iPacketCnt > iMaxPacketCnt)
2714 DeletePacket(pPacket: pFront);
2715 // ok
2716 return true;
2717}
2718
2719bool C4NetIOUDP::PacketList::DeletePacket(Packet *pPacket)
2720{
2721 CStdLock ListLock(&ListCSec);
2722#ifndef NDEBUG
2723 // check: this list?
2724 Packet *pPos = pPacket;
2725 while (pPos && pPos != pFront) pPos = pPos->Prev;
2726 assert(pPos);
2727#endif
2728 // unlink packet
2729 (pPacket->Prev ? pPacket->Prev->Next : pFront) = pPacket->Next;
2730 (pPacket->Next ? pPacket->Next->Prev : pBack) = pPacket->Prev;
2731 // delete packet
2732 delete pPacket;
2733 // decrease count
2734 --iPacketCnt;
2735 // ok
2736 return true;
2737}
2738
2739void C4NetIOUDP::PacketList::ClearPackets(unsigned int iUntil)
2740{
2741 CStdLock ListLock(&ListCSec);
2742 while (pFront && pFront->GetNr() < iUntil)
2743 DeletePacket(pPacket: pFront);
2744}
2745
2746void C4NetIOUDP::PacketList::Clear()
2747{
2748 CStdLock ListLock(&ListCSec);
2749 while (iPacketCnt)
2750 DeletePacket(pPacket: pFront);
2751}
2752
2753// * C4NetIOUDP::Peer
2754
2755// constants
2756
2757const unsigned int C4NetIOUDP::Peer::iConnectRetries = 5;
2758const unsigned int C4NetIOUDP::Peer::iReCheckInterval = 1000; // (ms)
2759
2760// construction / destruction
2761
2762C4NetIOUDP::Peer::Peer(const addr_t &naddr, C4NetIOUDP *pnParent)
2763 : pParent(pnParent), addr(naddr),
2764 eStatus(CS_None),
2765 fMultiCast(false), fDoBroadcast(false),
2766 iOPacketCounter(0),
2767 iIPacketCounter(0), iRIPacketCounter(0),
2768 iIMCPacketCounter(0), iRIMCPacketCounter(0),
2769 OPackets(iMaxOPacketBacklog),
2770 iMCAckPacketCounter(0),
2771 iNextReCheck(0),
2772 iIRate(0), iORate(0), iLoss(0)
2773{
2774}
2775
2776C4NetIOUDP::Peer::~Peer()
2777{
2778 // send close-packet
2779 Close(szReason: "deleted");
2780}
2781
2782bool C4NetIOUDP::Peer::Connect(bool fFailCallback) // (mt-safe)
2783{
2784 // initiate connection (DoConn will set status CS_Conn)
2785 fMultiCast = false; fConnFailCallback = fFailCallback;
2786 return DoConn(fMC: false);
2787}
2788
2789bool C4NetIOUDP::Peer::Send(const C4NetIOPacket &rPacket) // (mt-safe)
2790{
2791 CStdLock OutLock(&OutCSec);
2792 // encapsulate packet
2793 Packet *pnPacket = new Packet(rPacket.Duplicate(), iOPacketCounter);
2794 iOPacketCounter += pnPacket->FragmentCnt();
2795 pnPacket->GetData().SetAddr(addr);
2796 // add it to outgoing packet stack
2797 if (!OPackets.AddPacket(pPacket: pnPacket))
2798 return false;
2799 // This should be ensured by calling function anyway.
2800 // It is not secure to send packets before the connection
2801 // is etablished completly.
2802 if (eStatus != CS_Works) return true;
2803 // send it
2804 if (!SendDirect(rPacket: *pnPacket))
2805 {
2806 Close(szReason: "failed to send packet");
2807 return false;
2808 }
2809 return true;
2810}
2811
2812bool C4NetIOUDP::Peer::Check(bool fForceCheck)
2813{
2814 // only on working connections
2815 if (eStatus != CS_Works) return true;
2816 // prevent re-check (check floods)
2817 // instead, ask for other packets that are missing until recheck is allowed
2818 bool fNoReCheck = !!iNextReCheck && iNextReCheck > timeGetTime();
2819 if (!fNoReCheck) iLastPacketAsked = iLastMCPacketAsked = 0;
2820 unsigned int iStartAt = fNoReCheck ? (std::max)(a: iLastPacketAsked + 1, b: iIPacketCounter) : iIPacketCounter;
2821 unsigned int iStartAtMC = fNoReCheck ? (std::max)(a: iLastMCPacketAsked + 1, b: iIMCPacketCounter) : iIMCPacketCounter;
2822 // check if we have something to ask for
2823 const unsigned int iMaxAskCnt = 10;
2824 unsigned int i, iAskList[iMaxAskCnt], iAskCnt = 0, iMCAskCnt = 0;
2825 for (i = iStartAt; i < iRIPacketCounter; i++)
2826 if (!IPackets.FragmentPresent(iNr: i))
2827 if (iAskCnt < iMaxAskCnt)
2828 iLastPacketAsked = iAskList[iAskCnt++] = i;
2829 for (i = iStartAtMC; i < iRIMCPacketCounter; i++)
2830 if (!IMCPackets.FragmentPresent(iNr: i))
2831 if (iAskCnt + iMCAskCnt < iMaxAskCnt)
2832 iLastMCPacketAsked = iAskList[iAskCnt + iMCAskCnt++] = i;
2833 int iEAskCnt = iAskCnt + iMCAskCnt;
2834 // no re-check limit? set it
2835 if (!fNoReCheck)
2836 iNextReCheck = iEAskCnt ? timeGetTime() + iReCheckInterval : 0;
2837 // something to ask for? (or check forced?)
2838 if (iEAskCnt || fForceCheck)
2839 return DoCheck(iAskCnt, iMCAskCnt, pAskList: iAskList);
2840 return true;
2841}
2842
2843void C4NetIOUDP::Peer::OnRecv(const C4NetIOPacket &rPacket) // (mt-safe)
2844{
2845 // statistics
2846 { CStdLock StatLock(&StatCSec); iIRate += rPacket.getSize() + iUDPHeaderSize; }
2847 // get packet header
2848 if (rPacket.getSize() < sizeof(PacketHdr)) return;
2849 const PacketHdr *pHdr = rPacket.getPtr<PacketHdr>();
2850 bool fBroadcasted = !!(pHdr->StatusByte & 0x80);
2851 // save packet nr
2852 (fBroadcasted ? iRIMCPacketCounter : iRIPacketCounter) = std::max<unsigned int>(a: (fBroadcasted ? iRIMCPacketCounter : iRIPacketCounter), b: pHdr->Nr);
2853#ifdef C4NETIOUDP_OPT_RECV_CHECK_IMMEDIATE
2854 // do check
2855 if (eStatus == CS_Works)
2856 Check(fForceCheck: false);
2857#endif
2858 // what type of packet is it?
2859 switch (pHdr->StatusByte & 0x7f)
2860 {
2861 case IPID_Conn:
2862 {
2863 // check size
2864 if (rPacket.getSize() != sizeof(ConnPacket)) break;
2865 const ConnPacket *pPkt = rPacket.getPtr<ConnPacket>();
2866 // right version?
2867 if (pPkt->ProtocolVer != pParent->iVersion) break;
2868 if (!fBroadcasted)
2869 {
2870 // Second connection attempt using different address?
2871 if (!PeerAddr.IsNull() && PeerAddr != pPkt->Addr)
2872 {
2873 // Notify peer that he has two addresses to reach this connection.
2874 AddAddrPacket Pkt;
2875 Pkt.StatusByte = IPID_AddAddr;
2876 Pkt.Nr = iOPacketCounter;
2877 Pkt.Addr = PeerAddr;
2878 Pkt.NewAddr = pPkt->Addr;
2879 SendDirect(rPacket: C4NetIOPacket(&Pkt, sizeof(Pkt), false, addr));
2880 // But do nothing else - don't interfere with this connection
2881 break;
2882 }
2883 // reinit?
2884 else if (eStatus == CS_Works && iIPacketCounter != pPkt->Nr)
2885 {
2886 // close (callback!) ...
2887 OnClose(szReason: "reconnect"); eStatus = CS_Closed;
2888 // ... and reconnect
2889 Connect(fFailCallback: false);
2890 }
2891 // save back the address the peer is using
2892 PeerAddr = pPkt->Addr;
2893 }
2894 // set packet counter
2895 if (fBroadcasted)
2896 iRIMCPacketCounter = iRIMCPacketCounter = pPkt->Nr;
2897 else
2898 iRIPacketCounter = iIPacketCounter = pPkt->Nr;
2899 // clear incoming packets
2900 IPackets.Clear(); IMCPackets.Clear(); iNextReCheck = 0;
2901 iLastPacketAsked = iLastMCPacketAsked = 0;
2902 // Activate Multicast?
2903 if (!pParent->fMultiCast)
2904 {
2905 addr_t MCAddr{pPkt->MCAddr};
2906 if (!MCAddr.IsNull())
2907 {
2908 // Init Broadcast (with delayed loopback test)
2909 pParent->fDelayedLoopbackTest = true;
2910 if (!pParent->InitBroadcast(pBroadcastAddr: &MCAddr))
2911 pParent->fDelayedLoopbackTest = false;
2912 }
2913 }
2914 // build ConnOk Packet
2915 ConnOKPacket nPack;
2916 bool fullyConnected = false;
2917
2918 nPack.StatusByte = IPID_ConnOK; // (always du, no mc experiments here)
2919 nPack.Nr = fBroadcasted ? pParent->iOPacketCounter : iOPacketCounter;
2920 nPack.Addr = addr;
2921 if (fBroadcasted)
2922 nPack.MCMode = ConnOKPacket::MCM_MCOK; // multicast send ok
2923 else if (pParent->fMultiCast && addr.GetPort() == pParent->iPort)
2924 nPack.MCMode = ConnOKPacket::MCM_MC; // du ok, try multicast next
2925 else
2926 {
2927 nPack.MCMode = ConnOKPacket::MCM_NoMC; // du ok
2928 // No multicast => we're fully connected now
2929 fullyConnected = true;
2930 }
2931 // send it
2932 SendDirect(rPacket: C4NetIOPacket(&nPack, sizeof(nPack), false, addr));
2933
2934 // Clients will try sending data from OnConn, so send ConnOK before that.
2935 if (fullyConnected) OnConn();
2936 }
2937 break;
2938
2939 case IPID_ConnOK:
2940 {
2941 if (eStatus != CS_Conn) break;
2942 // check size
2943 if (rPacket.getSize() != sizeof(ConnOKPacket)) break;
2944 const ConnOKPacket *pPkt = rPacket.getPtr<ConnOKPacket>();
2945 // save port
2946 PeerAddr = pPkt->Addr;
2947 // Needs another Conn/ConnOK-sequence?
2948 switch (pPkt->MCMode)
2949 {
2950 case ConnOKPacket::MCM_MC:
2951 // multicast has to be active
2952 if (pParent->fMultiCast)
2953 {
2954 // already trying to connect via multicast?
2955 if (fMultiCast) break;
2956 // Send another Conn packet back (this time broadcasted to check if multicast works)
2957 fMultiCast = true; DoConn(fMC: true);
2958 break;
2959 }
2960 // fallthru
2961 case ConnOKPacket::MCM_NoMC:
2962 // Connection is established (no multicast support)
2963 fMultiCast = false; OnConn();
2964 break;
2965 case ConnOKPacket::MCM_MCOK:
2966 // Connection is established (multicast support)
2967 fMultiCast = true; OnConn();
2968 break;
2969 }
2970 }
2971 break;
2972
2973 case IPID_Data:
2974 {
2975 // get the packet header
2976 if (rPacket.getSize() < sizeof(DataPacketHdr)) return;
2977 const DataPacketHdr *pHdr = rPacket.getPtr<DataPacketHdr>();
2978 // already complet?
2979 if (pHdr->Nr < (fBroadcasted ? iIMCPacketCounter : iIPacketCounter)) break;
2980 // find or create packet
2981 bool fAddPacket = false;
2982 PacketList *pPacketList = fBroadcasted ? &IMCPackets : &IPackets;
2983 Packet *pPkt = pPacketList->GetPacket(iNr: pHdr->FNr);
2984 if (!pPkt) { pPkt = new Packet(); fAddPacket = true; }
2985 // add the fragment
2986 if (pPkt->AddFragment(Packet: rPacket, addr))
2987 {
2988 // add the packet to list
2989 if (fAddPacket) if (!pPacketList->AddPacket(pPacket: pPkt)) { delete pPkt; break; }
2990 // check for complete packets
2991 CheckCompleteIPackets();
2992 }
2993 else
2994 // delete the packet
2995 if (fAddPacket) delete pPkt;
2996 }
2997 break;
2998
2999 case IPID_Check:
3000 {
3001 // get the packet header
3002 if (rPacket.getSize() < sizeof(CheckPacketHdr)) break;
3003 const CheckPacketHdr *pPkt = rPacket.getPtr<CheckPacketHdr>();
3004 // check packet size
3005 if (rPacket.getSize() < sizeof(CheckPacketHdr) + (pPkt->AskCount + pPkt->MCAskCount) * sizeof(int)) break;
3006 // clear all acknowledged packets
3007 CStdLock OutLock(&OutCSec);
3008 OPackets.ClearPackets(iUntil: pPkt->AckNr);
3009 if (pPkt->MCAckNr > iMCAckPacketCounter)
3010 {
3011 iMCAckPacketCounter = pPkt->MCAckNr;
3012 pParent->ClearMCPackets();
3013 }
3014 OutLock.Clear();
3015 // read ask list
3016 const int *pAskList = rPacket.getPtr<int>(pos: sizeof(CheckPacketHdr));
3017 // send the packets he asks for
3018 unsigned int i;
3019 for (i = 0; i < pPkt->AskCount + pPkt->MCAskCount; i++)
3020 {
3021 // packet available?
3022 bool fMCPacket = i >= pPkt->AskCount;
3023 CStdLock OutLock(fMCPacket ? &pParent->OutCSec : &OutCSec);
3024 Packet *pPkt2Send = (fMCPacket ? pParent->OPackets : OPackets).GetPacketFrgm(iNr: pAskList[i]);
3025 if (!pPkt2Send) { Close(szReason: "starvation"); break; }
3026 // send the fragment
3027 if (fMCPacket)
3028 pParent->BroadcastDirect(rPacket: *pPkt2Send, iNr: pAskList[i]);
3029 else
3030 SendDirect(rPacket: *pPkt2Send, iNr: pAskList[i]);
3031 }
3032 }
3033 break;
3034
3035 case IPID_Close:
3036 {
3037 // check packet size
3038 if (rPacket.getSize() < sizeof(ClosePacket)) break;
3039 const ClosePacket *pPkt = rPacket.getPtr<ClosePacket>();
3040 // ignore if it's for another address
3041 if (!PeerAddr.IsNull() && PeerAddr != pPkt->Addr)
3042 break;
3043 // close
3044 OnClose(szReason: "connection closed by peer");
3045 }
3046 break;
3047 }
3048}
3049
3050void C4NetIOUDP::Peer::Close(const char *szReason) // (mt-safe)
3051{
3052 // already closed?
3053 if (eStatus == CS_Closed)
3054 return;
3055 // send close-packet
3056 ClosePacket Pkt;
3057 Pkt.StatusByte = IPID_Close;
3058 Pkt.Nr = 0;
3059 Pkt.Addr = addr;
3060 SendDirect(rPacket: C4NetIOPacket(&Pkt, sizeof(Pkt), false, addr));
3061 // callback
3062 OnClose(szReason);
3063}
3064
3065void C4NetIOUDP::Peer::CheckTimeout()
3066{
3067 // timeout set?
3068 if (!iTimeout) return;
3069 // check
3070 if (timeGetTime() > iTimeout)
3071 OnTimeout();
3072}
3073
3074void C4NetIOUDP::Peer::ClearStatistics()
3075{
3076 CStdLock StatLock(&StatCSec);
3077 iIRate = iORate = 0;
3078 iLoss = 0;
3079}
3080
3081bool C4NetIOUDP::Peer::DoConn(bool fMC) // (mt-safe)
3082{
3083 // set status
3084 eStatus = CS_Conn;
3085 // set timeout
3086 SetTimeout(iLength: iStdTimeout, iRetryCnt: iConnectRetries);
3087 // send packet (include current outgoing packet counter and mc addr)
3088 ConnPacket Pkt;
3089 Pkt.StatusByte = IPID_Conn | (fMC ? 0x80 : 0x00);
3090 Pkt.ProtocolVer = pParent->iVersion;
3091 Pkt.Nr = fMC ? pParent->iOPacketCounter : iOPacketCounter;
3092 Pkt.Addr = addr;
3093 if (pParent->fMultiCast)
3094 Pkt.MCAddr = pParent->C4NetIOSimpleUDP::getMCAddr();
3095 else
3096 Pkt.MCAddr = C4NetIO::addr_t{};
3097 return SendDirect(rPacket: C4NetIOPacket(&Pkt, sizeof(Pkt), false, addr));
3098}
3099
3100bool C4NetIOUDP::Peer::DoCheck(int iAskCnt, int iMCAskCnt, unsigned int *pAskList)
3101{
3102 // security
3103 if (!pAskList) iAskCnt = iMCAskCnt = 0;
3104 // statistics
3105 { CStdLock StatLock(&StatCSec); iLoss += iAskCnt + iMCAskCnt; }
3106 // alloc data
3107 int iAskListSize = (iAskCnt + iMCAskCnt) * sizeof(*pAskList);
3108 StdBuf Packet; Packet.New(inSize: sizeof(CheckPacketHdr) + iAskListSize);
3109 CheckPacketHdr *pChkPkt = Packet.getMPtr<CheckPacketHdr>();
3110 // set up header
3111 pChkPkt->StatusByte = IPID_Check; // (note: always du here, see C4NetIOUDP::DoCheck)
3112 pChkPkt->Nr = iOPacketCounter;
3113 pChkPkt->AckNr = iIPacketCounter;
3114 pChkPkt->MCAckNr = iIMCPacketCounter;
3115 // copy ask list
3116 pChkPkt->AskCount = iAskCnt;
3117 pChkPkt->MCAskCount = iMCAskCnt;
3118 if (pAskList)
3119 Packet.Write(pnData: pAskList, inSize: iAskListSize, iAt: sizeof(CheckPacketHdr));
3120 // send packet
3121 return SendDirect(rPacket: C4NetIOPacket(Packet, addr));
3122}
3123
3124bool C4NetIOUDP::Peer::SendDirect(const Packet &rPacket, unsigned int iNr)
3125{
3126 // send one fragment only?
3127 if (iNr + 1)
3128 return SendDirect(rPacket: rPacket.GetFragment(iFNr: iNr - rPacket.GetNr()));
3129 // otherwise: send all fragments
3130 bool fSuccess = true;
3131 for (unsigned int i = 0; i < rPacket.FragmentCnt(); i++)
3132 fSuccess &= SendDirect(rPacket: rPacket.GetFragment(iFNr: i));
3133 return fSuccess;
3134}
3135
3136bool C4NetIOUDP::Peer::SendDirect(C4NetIOPacket &&rPacket) // (mt-safe)
3137{
3138 // insert correct addr
3139 const C4NetIO::addr_t v6Addr{addr.AsIPv6()};
3140 if (!(rPacket.getStatus() & 0x80)) rPacket.SetAddr(v6Addr);
3141 // count outgoing
3142 { CStdLock StatLock(&StatCSec); iORate += rPacket.getSize() + iUDPHeaderSize; }
3143 // forward call
3144 return pParent->SendDirect(packet: std::move(rPacket));
3145}
3146
3147void C4NetIOUDP::Peer::OnConn()
3148{
3149 // reset timeout
3150 SetTimeout(iLength: StdSync::Infinite);
3151 // set status
3152 eStatus = CS_Works;
3153 // do callback
3154 C4NetIO::CBClass *pCB = pParent->pCB;
3155 if (pCB && !pCB->OnConn(AddrPeer: addr, AddrConnect: addr, pOwnAddr: &PeerAddr, pNetIO: pParent))
3156 {
3157 Close(szReason: "closed");
3158 return;
3159 }
3160 // do packet callback (in case the peer sent data while the connection was in progress)
3161 CheckCompleteIPackets();
3162}
3163
3164void C4NetIOUDP::Peer::OnClose(const char *szReason) // (mt-safe)
3165{
3166 // do callback
3167 C4NetIO::CBClass *pCB = pParent->pCB;
3168 if (eStatus == CS_Works || (eStatus == CS_Conn && fConnFailCallback))
3169 if (pCB)
3170 pCB->OnDisconn(AddrPeer: addr, pNetIO: pParent, szReason);
3171 // set status (this will schedule this peer for deletion)
3172 eStatus = CS_Closed;
3173}
3174
3175void C4NetIOUDP::Peer::CheckCompleteIPackets()
3176{
3177 // only status CS_Works
3178 if (eStatus != CS_Works) return;
3179 // (If the status is CS_Conn, we'll have to wait until the connection in the
3180 // opposite direction is etablished. There is no problem in checking for
3181 // complete packets here, but the one using the interface may get very confused
3182 // if he gets a callback for a connection that hasn't been announced to him
3183 // yet)
3184
3185 // check for complete incoming packets
3186 Packet *pPkt;
3187 while (pPkt = IPackets.GetFirstPacketComplete())
3188 {
3189 // missing packet?
3190 if (pPkt->GetNr() != iIPacketCounter) break;
3191 // do callback
3192 if (pParent->pCB)
3193 pParent->pCB->OnPacket(rPacket: pPkt->GetData(), pNetIO: pParent);
3194 // advance packet counter
3195 iIPacketCounter = pPkt->GetNr() + pPkt->FragmentCnt();
3196 // remove packet from queue
3197 [[maybe_unused]] int iNr = pPkt->GetNr();
3198 IPackets.DeletePacket(pPacket: pPkt);
3199 assert(!IPackets.GetPacketFrgm(iNr));
3200 }
3201 while (pPkt = IMCPackets.GetFirstPacketComplete())
3202 {
3203 // missing packet?
3204 if (pPkt->GetNr() != iIMCPacketCounter) break;
3205 // do callback
3206 if (pParent->pCB)
3207 pParent->pCB->OnPacket(rPacket: pPkt->GetData(), pNetIO: pParent);
3208 // advance packet counter
3209 iIMCPacketCounter = pPkt->GetNr() + pPkt->FragmentCnt();
3210 // remove packet from queue
3211 [[maybe_unused]] int iNr = pPkt->GetNr();
3212 IMCPackets.DeletePacket(pPacket: pPkt);
3213 assert(!IMCPackets.GetPacketFrgm(iNr));
3214 }
3215}
3216
3217void C4NetIOUDP::Peer::SetTimeout(int iLength, int iRetryCnt) // (mt-safe)
3218{
3219 if (iLength != StdSync::Infinite)
3220 iTimeout = timeGetTime() + iLength;
3221 else
3222 iTimeout = 0;
3223 iRetries = iRetryCnt;
3224}
3225
3226void C4NetIOUDP::Peer::OnTimeout()
3227{
3228 if (eStatus == CS_Conn)
3229 {
3230 // retries left?
3231 if (iRetries)
3232 {
3233 int iRetryCnt = iRetries - 1;
3234 // call DoConn (will set timeout)
3235 DoConn(fMC: fMultiCast);
3236 // set retry count
3237 iRetries = iRetryCnt;
3238 return;
3239 }
3240 // connection timeout: close
3241 Close(szReason: "connection timeout");
3242 }
3243 // reset timeout
3244 SetTimeout(iLength: StdSync::Infinite);
3245}
3246
3247// * C4NetIOUDP: implementation
3248
3249bool C4NetIOUDP::BroadcastDirect(const Packet &rPacket, unsigned int iNr) // (mt-safe)
3250{
3251 // only one fragment?
3252 if (iNr + 1)
3253 return SendDirect(packet: rPacket.GetFragment(iFNr: iNr - rPacket.GetNr(), fBroadcastFlag: true));
3254 // send all fragments
3255 bool fSuccess = true;
3256 for (unsigned int iFrgm = 0; iFrgm < rPacket.FragmentCnt(); iFrgm++)
3257 fSuccess &= SendDirect(packet: rPacket.GetFragment(iFNr: iFrgm, fBroadcastFlag: true));
3258 return fSuccess;
3259}
3260
3261bool C4NetIOUDP::SendDirect(C4NetIOPacket &&rPacket) // (mt-safe)
3262{
3263 addr_t toaddr = rPacket.getAddr();
3264 // packet meant to be broadcasted?
3265 if (rPacket.getStatus() & 0x80)
3266 {
3267 // set addr
3268 toaddr = C4NetIOSimpleUDP::getMCAddr();
3269 // statistics
3270 CStdLock StatLock(&StatCSec);
3271 iBroadcastRate += rPacket.getSize() + iUDPHeaderSize;
3272 }
3273
3274 // debug
3275#ifdef C4NETIO_DEBUG
3276 { C4NetIOPacket Pkt2 = rPacket; Pkt2.SetAddr(toaddr); DebugLogPkt(true, Pkt2); }
3277#endif
3278
3279#ifdef C4NETIO_SIMULATE_PACKETLOSS
3280 if ((rPacket.getStatus() & 0x7F) != IPID_Test)
3281 if (SafeRandom(100) < C4NETIO_SIMULATE_PACKETLOSS) return true;
3282#endif
3283
3284 // send it
3285 return C4NetIOSimpleUDP::Send(rPacket: C4NetIOPacket(rPacket.getRef(), toaddr));
3286}
3287
3288bool C4NetIOUDP::DoLoopbackTest()
3289{
3290 // (try to) enable loopback
3291 C4NetIOSimpleUDP::SetMCLoopback(true);
3292 // ensure loopback is activate
3293 if (!C4NetIOSimpleUDP::getMCLoopback()) return false;
3294
3295 // send test packet
3296 const PacketHdr TestPacket = { .StatusByte: IPID_Test | 0x80, .Nr: static_cast<uint32_t>(rand()) };
3297 if (!C4NetIOSimpleUDP::Broadcast(rPacket: C4NetIOPacket(&TestPacket, sizeof(TestPacket))))
3298 return false;
3299
3300 // wait for socket to become readable (should happen immediatly, do not expect packet loss)
3301 fSavePacket = true;
3302 if (!C4NetIOSimpleUDP::Execute(iMaxTime: iStdTimeout))
3303 {
3304 fSavePacket = false;
3305 if (!GetError()) SetError(strnError: "Multicast disabled: loopback test failed");
3306 return false;
3307 }
3308 fSavePacket = false;
3309
3310 // compare it to the packet that was sent
3311 if (LastPacket.getSize() != sizeof(TestPacket) ||
3312 LastPacket.Compare(pCData: &TestPacket, iCSize: sizeof(TestPacket)))
3313 {
3314 SetError(strnError: "Multicast disabled: loopback test failed");
3315 return false;
3316 }
3317
3318 // save the loopback addr back
3319 MCLoopbackAddr = LastPacket.getAddr();
3320
3321 // disable loopback
3322 C4NetIOSimpleUDP::SetMCLoopback(false);
3323 // ok
3324 return true;
3325}
3326
3327void C4NetIOUDP::ClearMCPackets()
3328{
3329 CStdShareLock PeerListLock(&PeerListCSec);
3330 CStdLock OutLock(&OutCSec);
3331 // clear packets if no client is present
3332 if (!pPeerList)
3333 OPackets.Clear();
3334 else
3335 {
3336 // find minimum acknowledged packet number
3337 unsigned int iAckNr = pPeerList->GetMCAckPacketCounter();
3338 for (Peer *pPeer = pPeerList->Next; pPeer; pPeer = pPeer->Next)
3339 iAckNr = (std::min)(a: iAckNr, b: pPeerList->GetMCAckPacketCounter());
3340 // clear packets
3341 OPackets.ClearPackets(iUntil: iAckNr);
3342 }
3343}
3344
3345void C4NetIOUDP::AddPeer(Peer *pPeer)
3346{
3347 // get locks
3348 CStdShareLock PeerListLock(&PeerListCSec);
3349 CStdLock PeerListAddLock(&PeerListAddCSec);
3350 // add
3351 pPeer->Next = pPeerList;
3352 pPeerList = pPeer;
3353}
3354
3355void C4NetIOUDP::OnShareFree(CStdCSecEx *pCSec)
3356{
3357 if (pCSec == &PeerListCSec)
3358 {
3359 Peer *pPeer = pPeerList, *pLast = nullptr;
3360 while (pPeer)
3361 {
3362 // delete?
3363 if (pPeer->Closed())
3364 {
3365 // unlink
3366 Peer *pDelete = pPeer;
3367 (pLast ? pLast->Next : pPeerList) = pPeer = pPeer->Next;
3368 // delete
3369 delete pDelete;
3370 }
3371 else
3372 {
3373 // next peer
3374 pLast = pPeer;
3375 pPeer = pPeer->Next;
3376 }
3377 }
3378 }
3379}
3380
3381C4NetIOUDP::Peer *C4NetIOUDP::GetPeer(const addr_t &addr)
3382{
3383 CStdShareLock PeerListLock(&PeerListCSec);
3384 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
3385 if (!pPeer->Closed())
3386 if (pPeer->GetAddr() == addr || pPeer->GetAltAddr() == addr)
3387 return pPeer;
3388 return nullptr;
3389}
3390
3391C4NetIOUDP::Peer *C4NetIOUDP::ConnectPeer(const addr_t &PeerAddr, bool fFailCallback) // (mt-safe)
3392{
3393 CStdShareLock PeerListLock(&PeerListCSec);
3394 // lock so no new peer can be added after this point
3395 CStdLock PeerListAddLock(&PeerListAddCSec);
3396 // recheck: address already known?
3397 Peer *pnPeer = GetPeer(addr: PeerAddr);
3398 if (pnPeer) return pnPeer;
3399 // create new Peer class
3400 pnPeer = new Peer(PeerAddr, this);
3401 if (!pnPeer) return nullptr;
3402 // add peer to list
3403 AddPeer(pPeer: pnPeer);
3404 PeerListAddLock.Clear();
3405 // send connection request
3406 if (!pnPeer->Connect(fFailCallback)) { pnPeer->Close(szReason: "connect failed"); return nullptr; }
3407 // ok (do not wait for peer)
3408 return pnPeer;
3409}
3410
3411void C4NetIOUDP::DoCheck() // (mt-safe)
3412{
3413 CStdShareLock PeerListLock(&PeerListCSec);
3414 // mc connection check?
3415 if (fMultiCast)
3416 {
3417 // only if a peer is connected via multicast
3418 Peer *pPeer;
3419 for (pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
3420 if (pPeer->Open() && pPeer->MultiCast())
3421 break;
3422 if (pPeer)
3423 {
3424 // set up packet
3425 CheckPacketHdr Pkt;
3426 Pkt.StatusByte = IPID_Check | 0x80;
3427 Pkt.Nr = iOPacketCounter;
3428 Pkt.AskCount = Pkt.MCAskCount = 0;
3429 // send it
3430 SendDirect(rPacket: C4NetIOPacket(&Pkt, sizeof(Pkt)));
3431 }
3432 }
3433 // peer connection checks
3434 for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next)
3435 if (pPeer->Open())
3436 pPeer->Check();
3437 // set time for next check
3438 iNextCheck = timeGetTime() + iCheckInterval;
3439}
3440
3441// debug
3442#ifdef C4NETIO_DEBUG
3443
3444#ifndef _WIN32
3445#define _O_SEQUENTIAL 0
3446#define _O_TEXT 0
3447#endif
3448
3449void C4NetIOUDP::OpenDebugLog()
3450{
3451 const std::string fileBase{Config.AtExePath("NetIOUDP")};
3452 for (int i = 0; i < 1000; i++)
3453 {
3454 const std::string filePath{fileBase + std::format("{}.log", i)};
3455 hDebugLog = open(filePath.c_str(), O_CREAT | O_EXCL | O_TRUNC | _O_SEQUENTIAL | _O_TEXT | O_WRONLY, S_IREAD | S_IWRITE);
3456 if (hDebugLog != -1) break;
3457 }
3458 // initial timestamp
3459 if (hDebugLog != -1)
3460 {
3461 std::string output;
3462 time_t tTime; time(&tTime);
3463 struct tm *pLocalTime;
3464 pLocalTime = localtime(&tTime);
3465 if (pLocalTime)
3466 output = std::format("C4NetIOUDP debuglog starting at {}/{}/{} {}:{:2}:{:2} - (Daylight {})\n",
3467 pLocalTime->tm_mon + 1,
3468 pLocalTime->tm_mday,
3469 pLocalTime->tm_year + 1900,
3470 pLocalTime->tm_hour,
3471 pLocalTime->tm_min,
3472 pLocalTime->tm_sec,
3473 pLocalTime->tm_isdst);
3474 else output = "C4NetIOUDP debuglog; time not available\n";
3475 write(hDebugLog, output.c_str(), output.size());
3476 }
3477}
3478
3479void C4NetIOUDP::CloseDebugLog()
3480{
3481 close(hDebugLog);
3482}
3483
3484void C4NetIOUDP::DebugLogPkt(bool fOut, const C4NetIOPacket &Pkt)
3485{
3486 StdStrBuf O;
3487 unsigned int iTime = timeGetTime();
3488 std::string output{std::format("{} {}:{:02}:{:02}:{:03} {}", fOut ? "out" : "in ",
3489 (iTime / 1000 / 60 / 60), (iTime / 1000 / 60) % 60, (iTime / 1000) % 60, iTime % 1000,
3490 Pkt.getAddr().ToString())};
3491
3492 // header?
3493 if (Pkt.getSize() >= sizeof(PacketHdr))
3494 {
3495 const PacketHdr &Hdr = *Pkt.getPtr<PacketHdr>();
3496
3497 switch (Hdr.StatusByte & 0x07f)
3498 {
3499 case IPID_Ping: output += " PING"; break;
3500 case IPID_Test: output += " TEST"; break;
3501 case IPID_Conn: output += " CONN"; break;
3502 case IPID_ConnOK: output += " CONO"; break;
3503 case IPID_Data: output += " DATA"; break;
3504 case IPID_Check: output += " CHCK"; break;
3505 case IPID_Close: output += " CLSE"; break;
3506 default: output += " UNKN"; break;
3507 }
3508 output += std::format(" {} {:04}", (Hdr.StatusByte & 0x80) ? "MC" : "DU", Hdr.Nr);
3509
3510 #define UPACK(type) \
3511 const type &P = *Pkt.getPtr<type>();
3512
3513 switch (Hdr.StatusByte)
3514 {
3515 case IPID_Test: { UPACK(TestPacket); output += std::format(" ({})", P.TestNr); break; }
3516 case IPID_Conn: { UPACK(ConnPacket); output += std::format(" (Ver {}, MC: {})", P.ProtocolVer, P.MCAddr.ToString()); break; }
3517 case IPID_ConnOK:
3518 {
3519 UPACK(ConnOKPacket);
3520 switch (P.MCMode)
3521 {
3522 case ConnOKPacket::MCM_NoMC: O.Append(" (NoMC)"); break;
3523 case ConnOKPacket::MCM_MC: O.Append(" (MC)"); break;
3524 case ConnOKPacket::MCM_MCOK: O.Append(" (MCOK)"); break;
3525 default: O.Append(" (??""?)");
3526 }
3527 break;
3528 }
3529 case IPID_Data:
3530 {
3531 UPACK(DataPacketHdr); output += std::format(" (f: {} s: {})", P.FNr, P.Size);
3532 for (int iPos = sizeof(DataPacketHdr); iPos < std::min<int>(Pkt.getSize(), sizeof(DataPacketHdr) + 16); iPos++)
3533 output += std::format(" {:02x}", *Pkt.getPtr<unsigned char>(iPos));
3534 break;
3535 }
3536 case IPID_Check:
3537 {
3538 UPACK(CheckPacketHdr);
3539 output += std::format(" (ack: {}, mcack: {}, ask: {} mcask: {}, ", P.AckNr, P.MCAckNr, P.AskCount, P.MCAskCount);
3540 if (Pkt.getSize() < sizeof(CheckPacketHdr) + sizeof(unsigned int) * (P.AskCount + P.MCAskCount))
3541 output += "too small)";
3542 else
3543 {
3544 output += '[';
3545 for (unsigned int i = 0; i < P.AskCount + P.MCAskCount; i++)
3546 output += std::format("{}{}", i ? ", " : "", *Pkt.getPtr<unsigned int>(sizeof(CheckPacketHdr) + i * sizeof(unsigned int)));
3547 output += "])";
3548 }
3549 break;
3550 }
3551 }
3552 }
3553 output += std::format(" ({} bytes)\n", Pkt.getSize());
3554 write(hDebugLog, output.c_str(), output.size());
3555}
3556
3557#endif
3558