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