22 return (a_Socket != INVALID_SOCKET);
24 return (a_Socket >= 0);
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;
158 LOGD(
"UDP endpoint queued sendto: Name not resolved");
167 LOGD(
"UDP endpoint queued sendto: Name not resolved to IPv4 for an IPv4-only socket");
191 m_IsMainSockIPv6(true),
193 m_MainEvent(nullptr),
194 m_SecondaryEvent(nullptr)
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)
280 switch (sa.ss_family)
284 reinterpret_cast<sockaddr_in *
>(&sa)->sin_port = htons(a_Port);
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)));
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))));
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)));
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)));
315 LOGD(
"UDP sendto: Invalid address family for address \"%s\".", a_Host.c_str());
319 return (NumSent > 0);
332 int broadcastInt = 1;
333 char broadcastChar = 1;
335 if (setsockopt(
m_MainSock, SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<const char *
>(&broadcastInt),
sizeof(broadcastInt)) == -1)
337 if (setsockopt(
m_MainSock, SOL_SOCKET, SO_BROADCAST, &broadcastChar,
sizeof(broadcastChar)) == -1)
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));
347 if (setsockopt(
m_SecondarySock, SOL_SOCKET, SO_BROADCAST, &broadcastChar,
sizeof(broadcastChar)) == -1)
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));
359 if (setsockopt(
m_SecondarySock, SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<const char *
>(&broadcastInt),
sizeof(broadcastInt)) == -1)
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));
380 bool NeedsTwoSockets =
false;
382 m_MainSock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
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);
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))
402 if (evutil_make_listen_socket_reuseable(
m_MainSock) != 0)
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)
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)
417 err = EVUTIL_SOCKET_ERROR();
419 a_Port, err, evutil_socket_error_to_string(err))
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));
435 setsockopt(
m_MainSock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char *
>(&Zero),
sizeof(Zero));
439 if (evutil_make_listen_socket_reuseable(
m_MainSock) != 0)
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)
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)
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))
462 if (evutil_make_socket_nonblocking(
m_MainSock) != 0)
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))
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)
483 sockaddr_in * sin =
reinterpret_cast<sockaddr_in *
>(&name);
484 m_Port = ntohs(sin->sin_port);
489 sockaddr_in6 * sin6 =
reinterpret_cast<sockaddr_in6 *
>(&name);
490 m_Port = ntohs(sin6->sin6_port);
497 if (!NeedsTwoSockets)
503 LOGD(
"Creating a second UDP socket for IPv4");
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));
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)
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));
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)
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));
573 if ((a_What & EV_READ) != 0)
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);
583 char RemoteHost[128];
585 switch (sa.ss_family)
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);
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);
622 return std::make_shared<cUDPEndpointImpl>(a_Port, a_Callbacks);
#define KiB
Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it,...
void LOGWARNING(std::string_view a_Format, const Args &... args)
void LOG(std::string_view a_Format, const Args &... args)
std::shared_ptr< cUDPEndpoint > cUDPEndpointPtr
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).
static bool IsValidSocket(evutil_socket_t a_Socket)
Interface that provides methods available on UDP communication endpoints.
cCallbacks & m_Callbacks
The callbacks used for various events on the endpoint.
Interface for the callbacks for events that can happen on the endpoint.
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.
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.