Cuberite
A lightweight, fast and extensible game server for Minecraft
UDPEndpointImpl.cpp
Go to the documentation of this file.
1 
2 // UDPEndpointImpl.cpp
3 
4 // Implements the cUDPEndpointImpl class representing an implementation of an endpoint in UDP communication
5 
6 #include "Globals.h"
7 #include "UDPEndpointImpl.h"
8 #include "NetworkSingleton.h"
9 
10 
11 
12 
13 
15 // Globals:
16 
18 {
19  static bool IsValidSocket(evutil_socket_t a_Socket)
20  {
21 #ifdef _WIN32
22  return (a_Socket != INVALID_SOCKET);
23 #else // _WIN32
24  return (a_Socket >= 0);
25 #endif // else _WIN32
26  }
27 }
28 
29 
30 
31 
32 
34 static void ConvertIPv4ToMappedIPv6(sockaddr_in & a_SrcAddr, sockaddr_in6 & a_DstAddr)
35 {
36  memset(&a_DstAddr, 0, sizeof(a_DstAddr));
37  a_DstAddr.sin6_family = AF_INET6;
38  a_DstAddr.sin6_addr.s6_addr[10] = 0xff;
39  a_DstAddr.sin6_addr.s6_addr[11] = 0xff;
40  a_DstAddr.sin6_addr.s6_addr[12] = static_cast<Byte>((a_SrcAddr.sin_addr.s_addr >> 0) & 0xff);
41  a_DstAddr.sin6_addr.s6_addr[13] = static_cast<Byte>((a_SrcAddr.sin_addr.s_addr >> 8) & 0xff);
42  a_DstAddr.sin6_addr.s6_addr[14] = static_cast<Byte>((a_SrcAddr.sin_addr.s_addr >> 16) & 0xff);
43  a_DstAddr.sin6_addr.s6_addr[15] = static_cast<Byte>((a_SrcAddr.sin_addr.s_addr >> 24) & 0xff);
44  a_DstAddr.sin6_port = a_SrcAddr.sin_port;
45 }
46 
47 
48 
49 
50 
52 // cUDPSendAfterLookup:
53 
59 {
60 public:
61  cUDPSendAfterLookup(const AString & a_Data, UInt16 a_Port, evutil_socket_t a_MainSock, evutil_socket_t a_SecondSock, bool a_IsMainSockIPv6):
62  m_Data(a_Data),
63  m_Port(a_Port),
64  m_MainSock(a_MainSock),
65  m_SecondSock(a_SecondSock),
66  m_IsMainSockIPv6(a_IsMainSockIPv6),
67  m_HasIPv4(false),
68  m_HasIPv6(false)
69  {
70  }
71 
72 protected:
75 
78 
80  evutil_socket_t m_MainSock;
81 
83  evutil_socket_t m_SecondSock;
84 
87 
89  sockaddr_in m_AddrIPv4;
90 
92  bool m_HasIPv4;
93 
95  sockaddr_in6 m_AddrIPv6;
96 
98  bool m_HasIPv6;
99 
100 
101  // cNetwork::cResolveNameCallbacks overrides:
102  virtual void OnNameResolved(const AString & a_Name, const AString & a_PI) override
103  {
104  // Not needed
105  }
106 
107  virtual bool OnNameResolvedV4(const AString & a_Name, const sockaddr_in * a_IP) override
108  {
109  if (!m_HasIPv4)
110  {
111  m_AddrIPv4 = *a_IP;
112  m_AddrIPv4.sin_port = htons(m_Port);
113  m_HasIPv4 = true;
114  }
115 
116  // Don't want OnNameResolved() callback
117  return false;
118  }
119 
120  virtual bool OnNameResolvedV6(const AString & a_Name, const sockaddr_in6 * a_IP) override
121  {
122  if (!m_HasIPv6)
123  {
124  m_AddrIPv6 = *a_IP;
125  m_AddrIPv6.sin6_port = htons(m_Port);
126  m_HasIPv6 = true;
127  }
128 
129  // Don't want OnNameResolved() callback
130  return false;
131  }
132 
133  virtual void OnFinished(void) override
134  {
135  // Send the actual data, through the correct socket and using the correct resolved address:
136  if (m_IsMainSockIPv6)
137  {
138  if (m_HasIPv6)
139  {
140  sendto(m_MainSock, m_Data.data(), m_Data.size(), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv6), static_cast<socklen_t>(sizeof(m_AddrIPv6)));
141  }
142  else if (m_HasIPv4)
143  {
144  // If the secondary socket is valid, it is an IPv4 socket, so use that:
145  if (m_SecondSock != -1)
146  {
147  sendto(m_SecondSock, m_Data.data(), m_Data.size(), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv4), static_cast<socklen_t>(sizeof(m_AddrIPv4)));
148  }
149  else
150  {
151  // Need an address conversion from IPv4 to IPv6-mapped-IPv4:
153  sendto(m_MainSock, m_Data.data(), m_Data.size(), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv6), static_cast<socklen_t>(sizeof(m_AddrIPv6)));
154  }
155  }
156  else
157  {
158  LOGD("UDP endpoint queued sendto: Name not resolved");
159  return;
160  }
161  }
162  else // m_IsMainSockIPv6
163  {
164  // Main socket is IPv4 only, only allow IPv4 dst address:
165  if (!m_HasIPv4)
166  {
167  LOGD("UDP endpoint queued sendto: Name not resolved to IPv4 for an IPv4-only socket");
168  return;
169  }
170  sendto(m_MainSock, m_Data.data(), m_Data.size(), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv4), static_cast<socklen_t>(sizeof(m_AddrIPv4)));
171  }
172  }
173 
174  virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
175  {
176  // Nothing needed
177  }
178 };
179 
180 
181 
182 
183 
185 // cUDPEndpointImpl:
186 
188  Super(a_Callbacks),
189  m_Port(0),
190  m_MainSock(-1),
191  m_IsMainSockIPv6(true),
192  m_SecondarySock(-1),
193  m_MainEvent(nullptr),
194  m_SecondaryEvent(nullptr)
195 {
196  Open(a_Port);
197 }
198 
199 
200 
201 
202 
204 {
205  Close();
206 }
207 
208 
209 
210 
211 
213 {
214  if (m_Port == 0)
215  {
216  // Already closed
217  return;
218  }
219 
220  // Close the LibEvent handles:
221  if (m_MainEvent != nullptr)
222  {
223  event_free(m_MainEvent);
224  m_MainEvent = nullptr;
225  }
226  if (m_SecondaryEvent != nullptr)
227  {
228  event_free(m_SecondaryEvent);
229  m_SecondaryEvent = nullptr;
230  }
231 
232  // Close the OS sockets:
233  evutil_closesocket(m_MainSock);
234  m_MainSock = -1;
235  evutil_closesocket(m_SecondarySock);
236  m_SecondarySock = -1;
237 
238  // Mark as closed:
239  m_Port = 0;
240 }
241 
242 
243 
244 
245 
246 bool cUDPEndpointImpl::IsOpen(void) const
247 {
248  return (m_Port != 0);
249 }
250 
251 
252 
253 
254 
256 {
257  return m_Port;
258 }
259 
260 
261 
262 
263 
264 bool cUDPEndpointImpl::Send(const AString & a_Payload, const AString & a_Host, UInt16 a_Port)
265 {
266  // If a_Host is an IP address, send the data directly:
267  sockaddr_storage sa;
268  int salen = static_cast<int>(sizeof(sa));
269  memset(&sa, 0, sizeof(sa));
270  if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) != 0)
271  {
272  // a_Host is a hostname, we need to do a lookup first:
273  auto queue = std::make_shared<cUDPSendAfterLookup>(a_Payload, a_Port, m_MainSock, m_SecondarySock, m_IsMainSockIPv6);
274  return cNetwork::HostnameToIP(a_Host, queue);
275  }
276 
277  // a_Host is an IP address and has been parsed into "sa"
278  // Insert the correct port and send data:
279  int NumSent;
280  switch (sa.ss_family)
281  {
282  case AF_INET:
283  {
284  reinterpret_cast<sockaddr_in *>(&sa)->sin_port = htons(a_Port);
285  if (m_IsMainSockIPv6)
286  {
288  {
289  // The secondary socket, which is always IPv4, is present:
290  NumSent = static_cast<int>(sendto(m_SecondarySock, a_Payload.data(), a_Payload.size(), 0, reinterpret_cast<const sockaddr *>(&sa), static_cast<socklen_t>(salen)));
291  }
292  else
293  {
294  // Need to convert IPv4 to IPv6 address before sending:
295  sockaddr_in6 IPv6;
296  ConvertIPv4ToMappedIPv6(*reinterpret_cast<sockaddr_in *>(&sa), IPv6);
297  NumSent = static_cast<int>(sendto(m_MainSock, a_Payload.data(), a_Payload.size(), 0, reinterpret_cast<const sockaddr *>(&IPv6), static_cast<socklen_t>(sizeof(IPv6))));
298  }
299  }
300  else
301  {
302  NumSent = static_cast<int>(sendto(m_MainSock, a_Payload.data(), a_Payload.size(), 0, reinterpret_cast<const sockaddr *>(&sa), static_cast<socklen_t>(salen)));
303  }
304  break;
305  }
306 
307  case AF_INET6:
308  {
309  reinterpret_cast<sockaddr_in6 *>(&sa)->sin6_port = htons(a_Port);
310  NumSent = static_cast<int>(sendto(m_MainSock, a_Payload.data(), a_Payload.size(), 0, reinterpret_cast<const sockaddr *>(&sa), static_cast<socklen_t>(salen)));
311  break;
312  }
313  default:
314  {
315  LOGD("UDP sendto: Invalid address family for address \"%s\".", a_Host.c_str());
316  return false;
317  }
318  }
319  return (NumSent > 0);
320 }
321 
322 
323 
324 
325 
327 {
328  ASSERT(IsOpen());
329 
330  // Enable broadcasts on the main socket:
331  // Some OSes use ints, others use chars, so we try both
332  int broadcastInt = 1;
333  char broadcastChar = 1;
334  // (Note that Windows uses const char * for option values, while Linux uses const void *)
335  if (setsockopt(m_MainSock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<const char *>(&broadcastInt), sizeof(broadcastInt)) == -1)
336  {
337  if (setsockopt(m_MainSock, SOL_SOCKET, SO_BROADCAST, &broadcastChar, sizeof(broadcastChar)) == -1)
338  {
339  int err = EVUTIL_SOCKET_ERROR();
340  LOGWARNING("Cannot enable broadcasts on port %d: %d (%s)", m_Port, err, evutil_socket_error_to_string(err));
341  return;
342  }
343 
344  // Enable broadcasts on the secondary socket, if opened (use char, it worked for primary):
346  {
347  if (setsockopt(m_SecondarySock, SOL_SOCKET, SO_BROADCAST, &broadcastChar, sizeof(broadcastChar)) == -1)
348  {
349  int err = EVUTIL_SOCKET_ERROR();
350  LOGWARNING("Cannot enable broadcasts on port %d (secondary): %d (%s)", m_Port, err, evutil_socket_error_to_string(err));
351  }
352  }
353  return;
354  }
355 
356  // Enable broadcasts on the secondary socket, if opened (use int, it worked for primary):
358  {
359  if (setsockopt(m_SecondarySock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<const char *>(&broadcastInt), sizeof(broadcastInt)) == -1)
360  {
361  int err = EVUTIL_SOCKET_ERROR();
362  LOGWARNING("Cannot enable broadcasts on port %d (secondary): %d (%s)", m_Port, err, evutil_socket_error_to_string(err));
363  }
364  }
365 }
366 
367 
368 
369 
370 
372 {
373  ASSERT(m_Port == 0); // Must not be already open
374 
375  // Make sure the cNetwork internals are innitialized:
377 
378  // Set up the main socket:
379  // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
380  bool NeedsTwoSockets = false;
381  m_IsMainSockIPv6 = true;
382  m_MainSock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
383 
384  int err;
386  {
387  // Failed to create IPv6 socket, create an IPv4 one instead:
388  m_IsMainSockIPv6 = false;
389  err = EVUTIL_SOCKET_ERROR();
390  LOGD("UDP: Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
391  m_MainSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
393  {
394  err = EVUTIL_SOCKET_ERROR();
395  m_Callbacks.OnError(err, fmt::format(FMT_STRING("Cannot create UDP socket for port {}: {} ({})"),
396  a_Port, err, evutil_socket_error_to_string(err))
397  );
398  return;
399  }
400 
401  // Allow the port to be reused right after the socket closes:
402  if (evutil_make_listen_socket_reuseable(m_MainSock) != 0)
403  {
404  err = EVUTIL_SOCKET_ERROR();
405  LOG("UDP Port %d cannot be made reusable: %d (%s). Restarting the server might not work.",
406  a_Port, err, evutil_socket_error_to_string(err)
407  );
408  }
409 
410  // Bind to all interfaces:
411  sockaddr_in name;
412  memset(&name, 0, sizeof(name));
413  name.sin_family = AF_INET;
414  name.sin_port = ntohs(a_Port);
415  if (bind(m_MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
416  {
417  err = EVUTIL_SOCKET_ERROR();
418  m_Callbacks.OnError(err, fmt::format(FMT_STRING("Cannot bind UDP port {}: {} ({})"),
419  a_Port, err, evutil_socket_error_to_string(err))
420  );
421  evutil_closesocket(m_MainSock);
422  return;
423  }
424  }
425  else
426  {
427  // IPv6 socket created, switch it into "dualstack" mode:
428  UInt32 Zero = 0;
429  #ifdef _WIN32
430  // WinXP doesn't support this feature, so if the setting fails, create another socket later on:
431  int res = setsockopt(m_MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
432  err = EVUTIL_SOCKET_ERROR();
433  NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
434  #else
435  setsockopt(m_MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
436  #endif
437 
438  // Allow the port to be reused right after the socket closes:
439  if (evutil_make_listen_socket_reuseable(m_MainSock) != 0)
440  {
441  err = EVUTIL_SOCKET_ERROR();
442  LOG("UDP Port %d cannot be made reusable: %d (%s). Restarting the server might not work.",
443  a_Port, err, evutil_socket_error_to_string(err)
444  );
445  }
446 
447  // Bind to all interfaces:
448  sockaddr_in6 name;
449  memset(&name, 0, sizeof(name));
450  name.sin6_family = AF_INET6;
451  name.sin6_port = ntohs(a_Port);
452  if (bind(m_MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
453  {
454  err = EVUTIL_SOCKET_ERROR();
455  m_Callbacks.OnError(err, fmt::format(FMT_STRING("Cannot bind to UDP port {}: {} ({})"),
456  a_Port, err, evutil_socket_error_to_string(err))
457  );
458  evutil_closesocket(m_MainSock);
459  return;
460  }
461  }
462  if (evutil_make_socket_nonblocking(m_MainSock) != 0)
463  {
464  err = EVUTIL_SOCKET_ERROR();
465  m_Callbacks.OnError(err, fmt::format(FMT_STRING("Cannot make socket on UDP port {} nonblocking: {} ({})"),
466  a_Port, err, evutil_socket_error_to_string(err))
467  );
468  evutil_closesocket(m_MainSock);
469  return;
470  }
471  m_MainEvent = event_new(cNetworkSingleton::Get().GetEventBase(), m_MainSock, EV_READ | EV_PERSIST, RawCallback, this);
472  event_add(m_MainEvent, nullptr);
473 
474  // Read the actual port number on which the socket is listening:
475  {
476  sockaddr_storage name;
477  socklen_t namelen = static_cast<socklen_t>(sizeof(name));
478  getsockname(m_MainSock, reinterpret_cast<sockaddr *>(&name), &namelen);
479  switch (name.ss_family)
480  {
481  case AF_INET:
482  {
483  sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(&name);
484  m_Port = ntohs(sin->sin_port);
485  break;
486  }
487  case AF_INET6:
488  {
489  sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(&name);
490  m_Port = ntohs(sin6->sin6_port);
491  break;
492  }
493  }
494  }
495 
496  // If we don't need to create another socket, bail out now:
497  if (!NeedsTwoSockets)
498  {
499  return;
500  }
501 
502  // If a secondary socket is required (WinXP dual-stack), create it here:
503  LOGD("Creating a second UDP socket for IPv4");
504  m_SecondarySock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
505 
507  {
508  // Don't report as an error, the primary socket is working
509  err = EVUTIL_SOCKET_ERROR();
510  LOGD("Socket creation failed for secondary UDP socket for port %d: %d, %s", m_Port, err, evutil_socket_error_to_string(err));
511  return;
512  }
513 
514  // Allow the port to be reused right after the socket closes:
515  if (evutil_make_listen_socket_reuseable(m_SecondarySock) != 0)
516  {
517  // Don't report as an error, the primary socket is working
518  err = EVUTIL_SOCKET_ERROR();
519  LOGD("UDP Port %d cannot be made reusable (second socket): %d (%s). Restarting the server might not work.",
520  a_Port, err, evutil_socket_error_to_string(err)
521  );
522  evutil_closesocket(m_SecondarySock);
523  m_SecondarySock = -1;
524  return;
525  }
526 
527  // Make the secondary socket nonblocking:
528  if (evutil_make_socket_nonblocking(m_SecondarySock) != 0)
529  {
530  // Don't report as an error, the primary socket is working
531  err = EVUTIL_SOCKET_ERROR();
532  LOGD("evutil_make_socket_nonblocking() failed for secondary UDP socket: %d, %s", err, evutil_socket_error_to_string(err));
533  evutil_closesocket(m_SecondarySock);
534  m_SecondarySock = -1;
535  return;
536  }
537 
538  // Bind to all IPv4 interfaces:
539  sockaddr_in name;
540  memset(&name, 0, sizeof(name));
541  name.sin_family = AF_INET;
542  name.sin_port = ntohs(m_Port);
543  if (bind(m_SecondarySock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
544  {
545  // Don't report as an error, the primary socket is working
546  err = EVUTIL_SOCKET_ERROR();
547  LOGD("Cannot bind secondary socket to UDP port %d: %d (%s)", m_Port, err, evutil_socket_error_to_string(err));
548  evutil_closesocket(m_SecondarySock);
549  m_SecondarySock = -1;
550  return;
551  }
552 
553  m_SecondaryEvent = event_new(cNetworkSingleton::Get().GetEventBase(), m_SecondarySock, EV_READ | EV_PERSIST, RawCallback, this);
554  event_add(m_SecondaryEvent, nullptr);
555 }
556 
557 
558 
559 
560 
561 void cUDPEndpointImpl::RawCallback(evutil_socket_t a_Socket, short a_What, void * a_Self)
562 {
563  cUDPEndpointImpl * Self = reinterpret_cast<cUDPEndpointImpl *>(a_Self);
564  Self->Callback(a_Socket, a_What);
565 }
566 
567 
568 
569 
570 
571 void cUDPEndpointImpl::Callback(evutil_socket_t a_Socket, short a_What)
572 {
573  if ((a_What & EV_READ) != 0)
574  {
575  // Receive datagram from the socket:
576  char buf[64 KiB];
577  sockaddr_storage sa;
578  socklen_t salen = static_cast<socklen_t>(sizeof(sa));
579  auto len = recvfrom(a_Socket, buf, sizeof(buf), 0, reinterpret_cast<sockaddr *>(&sa), &salen);
580  if (len >= 0)
581  {
582  // Convert the remote IP address to a string:
583  char RemoteHost[128];
584  UInt16 RemotePort;
585  switch (sa.ss_family)
586  {
587  case AF_INET:
588  {
589  auto sin = reinterpret_cast<sockaddr_in *>(&sa);
590  evutil_inet_ntop(sa.ss_family, &sin->sin_addr, RemoteHost, sizeof(RemoteHost));
591  RemotePort = ntohs(sin->sin_port);
592  break;
593  }
594  case AF_INET6:
595  {
596  auto sin = reinterpret_cast<sockaddr_in6 *>(&sa);
597  evutil_inet_ntop(sa.ss_family, &sin->sin6_addr, RemoteHost, sizeof(RemoteHost));
598  RemotePort = ntohs(sin->sin6_port);
599  break;
600  }
601  default:
602  {
603  return;
604  }
605  }
606 
607  // Call the callback:
608  m_Callbacks.OnReceivedData(buf, static_cast<size_t>(len), RemoteHost, RemotePort);
609  }
610  }
611 }
612 
613 
614 
615 
616 
618 // cNetwork API:
619 
621 {
622  return std::make_shared<cUDPEndpointImpl>(a_Port, a_Callbacks);
623 }
624 
625 
626 
627 
unsigned int UInt32
Definition: Globals.h:157
#define KiB
Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it,...
Definition: Globals.h:234
#define ASSERT(x)
Definition: Globals.h:276
unsigned short UInt16
Definition: Globals.h:158
unsigned char Byte
Definition: Globals.h:161
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
void LOG(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:55
#define LOGD
Definition: LoggerSimple.h:83
std::shared_ptr< cUDPEndpoint > cUDPEndpointPtr
Definition: Network.h:226
static void ConvertIPv4ToMappedIPv6(sockaddr_in &a_SrcAddr, sockaddr_in6 &a_DstAddr)
Converts a_SrcAddr in IPv4 format to a_DstAddr in IPv6 format (using IPv4-mapped IPv6).
std::string AString
Definition: StringUtils.h:11
static bool IsValidSocket(evutil_socket_t a_Socket)
Interface that provides methods available on UDP communication endpoints.
Definition: Network.h:176
cCallbacks & m_Callbacks
The callbacks used for various events on the endpoint.
Definition: Network.h:216
Interface for the callbacks for events that can happen on the endpoint.
Definition: Network.h:180
virtual void OnReceivedData(const char *a_Data, size_t a_Size, const AString &a_RemoteHost, UInt16 a_RemotePort)=0
Called when there is an incoming datagram from a remote host.
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg)=0
Called when an error occurs on the endpoint.
static cUDPEndpointPtr CreateUDPEndpoint(UInt16 a_Port, cUDPEndpoint::cCallbacks &a_Callbacks)
Opens up an UDP endpoint for sending and receiving UDP datagrams on the specified port.
static bool HostnameToIP(const AString &a_Hostname, cResolveNameCallbacksPtr a_Callbacks)
Queues a DNS query to resolve the specified hostname to IP address.
Callbacks used when resolving names to IPs.
Definition: Network.h:278
static cNetworkSingleton & Get(void)
Returns the singleton instance of this class.
A hostname-to-IP resolver callback that sends the data stored within to the resolved IP address.
cUDPSendAfterLookup(const AString &a_Data, UInt16 a_Port, evutil_socket_t a_MainSock, evutil_socket_t a_SecondSock, bool a_IsMainSockIPv6)
bool m_HasIPv4
Set to true if the name resolved to an IPv4 address.
bool m_IsMainSockIPv6
True if m_MainSock is an IPv6 socket.
virtual void OnFinished(void) override
Called when all the addresses resolved have been reported via the OnNameResolved() callback.
evutil_socket_t m_MainSock
The primary socket to use for sending.
virtual bool OnNameResolvedV4(const AString &a_Name, const sockaddr_in *a_IP) override
Called when the hostname is successfully resolved into an IPv4 address.
sockaddr_in6 m_AddrIPv6
The IPv6 address resolved, if any.
UInt16 m_Port
The port to which to send the data.
virtual void OnNameResolved(const AString &a_Name, const AString &a_PI) override
Called when the hostname is successfully resolved into an IP address.
sockaddr_in m_AddrIPv4
The IPv4 address resolved, if any.
bool m_HasIPv6
Set to true if the name resolved to an IPv6 address.
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when an error is encountered while resolving.
AString m_Data
The data to send after the hostname is resolved.
virtual bool OnNameResolvedV6(const AString &a_Name, const sockaddr_in6 *a_IP) override
Called when the hostname is successfully resolved into an IPv6 address.
evutil_socket_t m_SecondSock
The secondary socket to use for sending, if needed by the OS.
evutil_socket_t m_MainSock
The primary underlying OS socket.
virtual UInt16 GetPort(void) const override
Returns the local port to which the underlying socket is bound.
evutil_socket_t m_SecondarySock
The secondary OS socket (if primary doesn't support dualstack).
virtual void Close(void) override
Closes the underlying socket.
void Callback(evutil_socket_t a_Socket, short a_What)
The callback that is called when an event occurs on one of the sockets.
virtual ~cUDPEndpointImpl() override
virtual bool Send(const AString &a_Payload, const AString &a_Host, UInt16 a_Port) override
Sends the specified payload in a single UDP datagram to the specified host + port combination.
virtual bool IsOpen(void) const override
Returns true if the endpoint is open.
event * m_SecondaryEvent
The LibEvent handle for the secondary socket.
virtual void EnableBroadcasts(void) override
Marks the socket as capable of sending broadcast, using whatever OS API is needed.
UInt16 m_Port
The local port on which the endpoint is open.
static void RawCallback(evutil_socket_t a_Socket, short a_What, void *a_Self)
The callback that LibEvent calls when an event occurs on one of the sockets.
bool m_IsMainSockIPv6
True if m_MainSock is in the IPv6 namespace (needs IPv6 addresses for sending).
void Open(UInt16 a_Port)
Creates and opens the socket on the specified port.
event * m_MainEvent
The LibEvent handle for the primary socket.
cUDPEndpointImpl(UInt16 a_Port, cUDPEndpoint::cCallbacks &a_Callbacks)
Creates a new instance of the endpoint, with the specified callbacks.