1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "platform/impl/udp_socket_posix.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <netinet/in.h>
10 #include <netinet/ip.h>
11 #include <sys/ioctl.h>
12 #include <sys/socket.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 
16 #include <cstring>
17 #include <memory>
18 #include <sstream>
19 #include <string>
20 #include <type_traits>
21 #include <utility>
22 
23 #include "absl/types/optional.h"
24 #include "platform/api/task_runner.h"
25 #include "platform/base/error.h"
26 #include "platform/impl/udp_socket_reader_posix.h"
27 #include "util/logging.h"
28 
29 namespace openscreen {
30 namespace {
31 
IsPowerOf2(uint32_t x)32 constexpr bool IsPowerOf2(uint32_t x) {
33   return (x > 0) && ((x & (x - 1)) == 0);
34 }
35 
36 static_assert(IsPowerOf2(alignof(struct cmsghdr)),
37               "std::align requires power-of-2 alignment");
38 
39 using IPv4NetworkInterfaceIndex = decltype(ip_mreqn().imr_ifindex);
40 using IPv6NetworkInterfaceIndex = decltype(ipv6_mreq().ipv6mr_interface);
41 
CreateNonBlockingUdpSocket(int domain)42 ErrorOr<int> CreateNonBlockingUdpSocket(int domain) {
43   int fd = socket(domain, SOCK_DGRAM, 0);
44   if (fd == -1) {
45     return Error(Error::Code::kInitializationFailure, strerror(errno));
46   }
47   // On non-Linux, the SOCK_NONBLOCK option is not available, so use the
48   // more-portable method of calling fcntl() to set this behavior.
49   if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1) {
50     close(fd);
51     return Error(Error::Code::kInitializationFailure, strerror(errno));
52   }
53   return fd;
54 }
55 
56 }  // namespace
57 
UdpSocketPosix(TaskRunner * task_runner,Client * client,SocketHandle handle,const IPEndpoint & local_endpoint,PlatformClientPosix * platform_client)58 UdpSocketPosix::UdpSocketPosix(TaskRunner* task_runner,
59                                Client* client,
60                                SocketHandle handle,
61                                const IPEndpoint& local_endpoint,
62                                PlatformClientPosix* platform_client)
63     : task_runner_(task_runner),
64       client_(client),
65       handle_(handle),
66       local_endpoint_(local_endpoint),
67       platform_client_(platform_client) {
68   OSP_DCHECK(task_runner_);
69   OSP_DCHECK(local_endpoint_.address.IsV4() || local_endpoint_.address.IsV6());
70 
71   if (handle_.fd >= 0) {
72     if (platform_client_) {
73       platform_client_->udp_socket_reader()->OnCreate(this);
74     }
75   }
76 }
77 
~UdpSocketPosix()78 UdpSocketPosix::~UdpSocketPosix() {
79   Close();
80 }
81 
GetHandle() const82 const SocketHandle& UdpSocketPosix::GetHandle() const {
83   return handle_;
84 }
85 
86 // static
Create(TaskRunner * task_runner,Client * client,const IPEndpoint & endpoint)87 ErrorOr<std::unique_ptr<UdpSocket>> UdpSocket::Create(
88     TaskRunner* task_runner,
89     Client* client,
90     const IPEndpoint& endpoint) {
91   static std::atomic_bool in_create{false};
92   const bool in_create_local = in_create.exchange(true);
93   OSP_DCHECK_EQ(in_create_local, false)
94       << "Another UdpSocket::Create call is in progress. Calls to this method "
95          "must be seralized.";
96 
97   if (in_create_local) {
98     return Error::Code::kAgain;
99   }
100 
101   int domain;
102   switch (endpoint.address.version()) {
103     case Version::kV4:
104       domain = AF_INET;
105       break;
106     case Version::kV6:
107       domain = AF_INET6;
108       break;
109   }
110   const ErrorOr<int> fd = CreateNonBlockingUdpSocket(domain);
111   if (!fd) {
112     in_create = false;
113     return fd.error();
114   }
115 
116   std::unique_ptr<UdpSocket> socket = std::make_unique<UdpSocketPosix>(
117       task_runner, client, SocketHandle(fd.value()), endpoint);
118   in_create = false;
119   return socket;
120 }
121 
IsIPv4() const122 bool UdpSocketPosix::IsIPv4() const {
123   return local_endpoint_.address.IsV4();
124 }
125 
IsIPv6() const126 bool UdpSocketPosix::IsIPv6() const {
127   return local_endpoint_.address.IsV6();
128 }
129 
GetLocalEndpoint() const130 IPEndpoint UdpSocketPosix::GetLocalEndpoint() const {
131   if (local_endpoint_.port == 0) {
132     // Note: If the getsockname() call fails, just assume that's because the
133     // socket isn't bound yet. In this case, leave the original value in-place.
134     switch (local_endpoint_.address.version()) {
135       case UdpSocket::Version::kV4: {
136         struct sockaddr_in address;
137         socklen_t address_len = sizeof(address);
138         if (getsockname(handle_.fd,
139                         reinterpret_cast<struct sockaddr*>(&address),
140                         &address_len) == 0) {
141           OSP_DCHECK_EQ(address.sin_family, AF_INET);
142           local_endpoint_.address =
143               IPAddress(IPAddress::Version::kV4,
144                         reinterpret_cast<uint8_t*>(&address.sin_addr.s_addr));
145           local_endpoint_.port = ntohs(address.sin_port);
146         }
147         break;
148       }
149 
150       case UdpSocket::Version::kV6: {
151         struct sockaddr_in6 address;
152         socklen_t address_len = sizeof(address);
153         if (getsockname(handle_.fd,
154                         reinterpret_cast<struct sockaddr*>(&address),
155                         &address_len) == 0) {
156           OSP_DCHECK_EQ(address.sin6_family, AF_INET6);
157           local_endpoint_.address =
158               IPAddress(IPAddress::Version::kV6,
159                         reinterpret_cast<uint8_t*>(&address.sin6_addr));
160           local_endpoint_.port = ntohs(address.sin6_port);
161         }
162         break;
163       }
164     }
165   }
166 
167   return local_endpoint_;
168 }
169 
Bind()170 void UdpSocketPosix::Bind() {
171   if (is_closed()) {
172     OnError(Error::Code::kSocketClosedFailure);
173     return;
174   }
175 
176   // This is effectively a boolean passed to setsockopt() to allow a future
177   // bind() on the same socket to succeed, even if the address is already in
178   // use. This is pretty much universally the desired behavior.
179   const int reuse_addr = 1;
180   if (setsockopt(handle_.fd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,
181                  sizeof(reuse_addr)) == -1) {
182     OnError(Error::Code::kSocketOptionSettingFailure);
183   }
184 
185   switch (local_endpoint_.address.version()) {
186     case UdpSocket::Version::kV4: {
187       struct sockaddr_in address;
188       address.sin_family = AF_INET;
189       address.sin_port = htons(local_endpoint_.port);
190       local_endpoint_.address.CopyToV4(
191           reinterpret_cast<uint8_t*>(&address.sin_addr.s_addr));
192       if (bind(handle_.fd, reinterpret_cast<struct sockaddr*>(&address),
193                sizeof(address)) == -1) {
194         OnError(Error::Code::kSocketBindFailure);
195       }
196       return;
197     }
198 
199     case UdpSocket::Version::kV6: {
200       struct sockaddr_in6 address;
201       address.sin6_family = AF_INET6;
202       address.sin6_flowinfo = 0;
203       address.sin6_port = htons(local_endpoint_.port);
204       local_endpoint_.address.CopyToV6(
205           reinterpret_cast<uint8_t*>(&address.sin6_addr));
206       address.sin6_scope_id = 0;
207       if (bind(handle_.fd, reinterpret_cast<struct sockaddr*>(&address),
208                sizeof(address)) == -1) {
209         OnError(Error::Code::kSocketBindFailure);
210       }
211       return;
212     }
213   }
214 
215   OSP_NOTREACHED();
216 }
217 
SetMulticastOutboundInterface(NetworkInterfaceIndex ifindex)218 void UdpSocketPosix::SetMulticastOutboundInterface(
219     NetworkInterfaceIndex ifindex) {
220   if (is_closed()) {
221     OnError(Error::Code::kSocketClosedFailure);
222     return;
223   }
224 
225   switch (local_endpoint_.address.version()) {
226     case UdpSocket::Version::kV4: {
227       struct ip_mreqn multicast_properties;
228       // Appropriate address is set based on |imr_ifindex| when set.
229       multicast_properties.imr_address.s_addr = INADDR_ANY;
230       multicast_properties.imr_multiaddr.s_addr = INADDR_ANY;
231       multicast_properties.imr_ifindex =
232           static_cast<IPv4NetworkInterfaceIndex>(ifindex);
233       if (setsockopt(handle_.fd, IPPROTO_IP, IP_MULTICAST_IF,
234                      &multicast_properties,
235                      sizeof(multicast_properties)) == -1) {
236         OnError(Error::Code::kSocketOptionSettingFailure);
237       }
238       return;
239     }
240 
241     case UdpSocket::Version::kV6: {
242       const auto index = static_cast<IPv6NetworkInterfaceIndex>(ifindex);
243       if (setsockopt(handle_.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
244                      sizeof(index)) == -1) {
245         OnError(Error::Code::kSocketOptionSettingFailure);
246       }
247       return;
248     }
249   }
250 
251   OSP_NOTREACHED();
252 }
253 
JoinMulticastGroup(const IPAddress & address,NetworkInterfaceIndex ifindex)254 void UdpSocketPosix::JoinMulticastGroup(const IPAddress& address,
255                                         NetworkInterfaceIndex ifindex) {
256   if (is_closed()) {
257     OnError(Error::Code::kSocketClosedFailure);
258     return;
259   }
260 
261   switch (local_endpoint_.address.version()) {
262     case UdpSocket::Version::kV4: {
263       // Passed as data to setsockopt().  1 means return IP_PKTINFO control data
264       // in recvmsg() calls.
265       const int enable_pktinfo = 1;
266       if (setsockopt(handle_.fd, IPPROTO_IP, IP_PKTINFO, &enable_pktinfo,
267                      sizeof(enable_pktinfo)) == -1) {
268         OnError(Error::Code::kSocketOptionSettingFailure);
269         return;
270       }
271       struct ip_mreqn multicast_properties;
272       // Appropriate address is set based on |imr_ifindex| when set.
273       multicast_properties.imr_address.s_addr = INADDR_ANY;
274       multicast_properties.imr_ifindex =
275           static_cast<IPv4NetworkInterfaceIndex>(ifindex);
276       static_assert(sizeof(multicast_properties.imr_multiaddr) == 4u,
277                     "IPv4 address requires exactly 4 bytes");
278       address.CopyToV4(
279           reinterpret_cast<uint8_t*>(&multicast_properties.imr_multiaddr));
280       if (setsockopt(handle_.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
281                      &multicast_properties,
282                      sizeof(multicast_properties)) == -1) {
283         OnError(Error::Code::kSocketOptionSettingFailure);
284       }
285       return;
286     }
287 
288     case UdpSocket::Version::kV6: {
289       // Passed as data to setsockopt().  1 means return IPV6_PKTINFO control
290       // data in recvmsg() calls.
291       const int enable_pktinfo = 1;
292       if (setsockopt(handle_.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
293                      &enable_pktinfo, sizeof(enable_pktinfo)) == -1) {
294         OnError(Error::Code::kSocketOptionSettingFailure);
295         return;
296       }
297       struct ipv6_mreq multicast_properties = {
298           {/* filled-in below */},
299           static_cast<IPv6NetworkInterfaceIndex>(ifindex),
300       };
301       static_assert(sizeof(multicast_properties.ipv6mr_multiaddr) == 16u,
302                     "IPv6 address requires exactly 16 bytes");
303       address.CopyToV6(
304           reinterpret_cast<uint8_t*>(&multicast_properties.ipv6mr_multiaddr));
305       // Portability note: All platforms support IPV6_JOIN_GROUP, which is
306       // synonymous with IPV6_ADD_MEMBERSHIP.
307       if (setsockopt(handle_.fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
308                      &multicast_properties,
309                      sizeof(multicast_properties)) == -1) {
310         OnError(Error::Code::kSocketOptionSettingFailure);
311       }
312       return;
313     }
314   }
315 
316   OSP_NOTREACHED();
317 }
318 
319 namespace {
320 
321 // Examine |posix_errno| to determine whether the specific cause of a failure
322 // was transient or hard, and return the appropriate error response.
ChooseError(decltype(errno)posix_errno,Error::Code hard_error_code)323 Error ChooseError(decltype(errno) posix_errno, Error::Code hard_error_code) {
324   if (posix_errno == EAGAIN || posix_errno == EWOULDBLOCK ||
325       posix_errno == ENOBUFS) {
326     return Error(Error::Code::kAgain, strerror(errno));
327   }
328   return Error(hard_error_code, strerror(errno));
329 }
330 
GetIPAddressFromSockAddr(const sockaddr_in & sa)331 IPAddress GetIPAddressFromSockAddr(const sockaddr_in& sa) {
332   static_assert(IPAddress::kV4Size == sizeof(sa.sin_addr.s_addr),
333                 "IPv4 address size mismatch.");
334   return IPAddress(IPAddress::Version::kV4,
335                    reinterpret_cast<const uint8_t*>(&sa.sin_addr.s_addr));
336 }
337 
GetIPAddressFromPktInfo(const in_pktinfo & pktinfo)338 IPAddress GetIPAddressFromPktInfo(const in_pktinfo& pktinfo) {
339   static_assert(IPAddress::kV4Size == sizeof(pktinfo.ipi_addr),
340                 "IPv4 address size mismatch.");
341   return IPAddress(IPAddress::Version::kV4,
342                    reinterpret_cast<const uint8_t*>(&pktinfo.ipi_addr));
343 }
344 
GetPortFromFromSockAddr(const sockaddr_in & sa)345 uint16_t GetPortFromFromSockAddr(const sockaddr_in& sa) {
346   return ntohs(sa.sin_port);
347 }
348 
GetIPAddressFromSockAddr(const sockaddr_in6 & sa)349 IPAddress GetIPAddressFromSockAddr(const sockaddr_in6& sa) {
350   return IPAddress(IPAddress::Version::kV6, sa.sin6_addr.s6_addr);
351 }
352 
GetIPAddressFromPktInfo(const in6_pktinfo & pktinfo)353 IPAddress GetIPAddressFromPktInfo(const in6_pktinfo& pktinfo) {
354   return IPAddress(IPAddress::Version::kV6, pktinfo.ipi6_addr.s6_addr);
355 }
356 
GetPortFromFromSockAddr(const sockaddr_in6 & sa)357 uint16_t GetPortFromFromSockAddr(const sockaddr_in6& sa) {
358   return ntohs(sa.sin6_port);
359 }
360 
361 template <class PktInfoType>
362 bool IsPacketInfo(cmsghdr* cmh);
363 
364 template <>
IsPacketInfo(cmsghdr * cmh)365 bool IsPacketInfo<in_pktinfo>(cmsghdr* cmh) {
366   return cmh->cmsg_level == IPPROTO_IP && cmh->cmsg_type == IP_PKTINFO;
367 }
368 
369 template <>
IsPacketInfo(cmsghdr * cmh)370 bool IsPacketInfo<in6_pktinfo>(cmsghdr* cmh) {
371   return cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO;
372 }
373 
374 template <class SockAddrType, class PktInfoType>
ReceiveMessageInternal(int fd,UdpPacket * packet)375 Error ReceiveMessageInternal(int fd, UdpPacket* packet) {
376   SockAddrType sa;
377   iovec iov = {packet->data(), packet->size()};
378   alignas(alignof(cmsghdr)) uint8_t control_buffer[1024];
379   msghdr msg;
380   msg.msg_name = &sa;
381   msg.msg_namelen = sizeof(sa);
382   msg.msg_iov = &iov;
383   msg.msg_iovlen = 1;
384   msg.msg_control = control_buffer;
385   msg.msg_controllen = sizeof(control_buffer);
386   msg.msg_flags = 0;
387 
388   ssize_t bytes_received = recvmsg(fd, &msg, 0);
389   if (bytes_received == -1) {
390     return ChooseError(errno, Error::Code::kSocketReadFailure);
391   }
392 
393   OSP_DCHECK_EQ(static_cast<size_t>(bytes_received), packet->size());
394 
395   IPEndpoint source_endpoint = {.address = GetIPAddressFromSockAddr(sa),
396                                 .port = GetPortFromFromSockAddr(sa)};
397   packet->set_source(std::move(source_endpoint));
398 
399   // For multicast sockets, the packet's original destination address may be
400   // the host address (since we called bind()) but it may also be a
401   // multicast address.  This may be relevant for handling multicast data;
402   // specifically, mDNSResponder requires this information to work properly.
403 
404   socklen_t sa_len = sizeof(sa);
405   if (((msg.msg_flags & MSG_CTRUNC) != 0) ||
406       (getsockname(fd, reinterpret_cast<sockaddr*>(&sa), &sa_len) == -1)) {
407     return Error::Code::kNone;
408   }
409   for (cmsghdr* cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh)) {
410     if (IsPacketInfo<PktInfoType>(cmh)) {
411       PktInfoType* pktinfo = reinterpret_cast<PktInfoType*>(CMSG_DATA(cmh));
412       IPEndpoint destination_endpoint = {
413           .address = GetIPAddressFromPktInfo(*pktinfo),
414           .port = GetPortFromFromSockAddr(sa)};
415       packet->set_destination(std::move(destination_endpoint));
416       break;
417     }
418   }
419   return Error::Code::kNone;
420 }
421 
422 }  // namespace
423 
ReceiveMessage()424 void UdpSocketPosix::ReceiveMessage() {
425   // WARNING: This method may be called on a different thread from the thread
426   // calling into all the other methods.
427 
428   if (is_closed()) {
429     task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr()] {
430       if (auto* self = weak_this.get()) {
431         if (auto* client = self->client_) {
432           client->OnRead(self, Error::Code::kSocketClosedFailure);
433         }
434       }
435     });
436     return;
437   }
438 
439   ssize_t bytes_available = recv(handle_.fd, nullptr, 0, MSG_PEEK | MSG_TRUNC);
440   if (bytes_available == -1) {
441     task_runner_->PostTask(
442         [weak_this = weak_factory_.GetWeakPtr(),
443          error =
444              ChooseError(errno, Error::Code::kSocketReadFailure)]() mutable {
445           if (auto* self = weak_this.get()) {
446             if (auto* client = self->client_) {
447               client->OnRead(self, std::move(error));
448             }
449           }
450         });
451     return;
452   }
453   UdpPacket packet(bytes_available);
454   packet.set_socket(this);
455   Error result = Error::Code::kUnknownError;
456   switch (local_endpoint_.address.version()) {
457     case UdpSocket::Version::kV4: {
458       result =
459           ReceiveMessageInternal<sockaddr_in, in_pktinfo>(handle_.fd, &packet);
460       break;
461     }
462     case UdpSocket::Version::kV6: {
463       result = ReceiveMessageInternal<sockaddr_in6, in6_pktinfo>(handle_.fd,
464                                                                  &packet);
465       break;
466     }
467     default: {
468       OSP_NOTREACHED();
469     }
470   }
471 
472   task_runner_->PostTask(
473       [weak_this = weak_factory_.GetWeakPtr(),
474        read_result = result.ok()
475                          ? ErrorOr<UdpPacket>(std::move(packet))
476                          : ErrorOr<UdpPacket>(std::move(result))]() mutable {
477         if (auto* self = weak_this.get()) {
478           if (auto* client = self->client_) {
479             client->OnRead(self, std::move(read_result));
480           }
481         }
482       });
483 }
484 
485 // TODO(yakimakha): Consider changing the interface to accept UdpPacket as
486 // an input parameter.
SendMessage(const void * data,size_t length,const IPEndpoint & dest)487 void UdpSocketPosix::SendMessage(const void* data,
488                                  size_t length,
489                                  const IPEndpoint& dest) {
490   if (is_closed()) {
491     if (client_) {
492       client_->OnSendError(this, Error::Code::kSocketClosedFailure);
493     }
494     return;
495   }
496 
497   struct iovec iov = {const_cast<void*>(data), length};
498   struct msghdr msg;
499   msg.msg_iov = &iov;
500   msg.msg_iovlen = 1;
501   msg.msg_control = nullptr;
502   msg.msg_controllen = 0;
503   msg.msg_flags = 0;
504 
505   ssize_t num_bytes_sent = -2;
506   switch (local_endpoint_.address.version()) {
507     case UdpSocket::Version::kV4: {
508       struct sockaddr_in sa = {
509           .sin_family = AF_INET,
510           .sin_port = htons(dest.port),
511       };
512       dest.address.CopyToV4(reinterpret_cast<uint8_t*>(&sa.sin_addr.s_addr));
513       msg.msg_name = &sa;
514       msg.msg_namelen = sizeof(sa);
515       num_bytes_sent = sendmsg(handle_.fd, &msg, 0);
516       break;
517     }
518 
519     case UdpSocket::Version::kV6: {
520       struct sockaddr_in6 sa = {};
521       sa.sin6_family = AF_INET6;
522       sa.sin6_flowinfo = 0;
523       sa.sin6_scope_id = 0;
524       sa.sin6_port = htons(dest.port);
525       dest.address.CopyToV6(reinterpret_cast<uint8_t*>(&sa.sin6_addr.s6_addr));
526       msg.msg_name = &sa;
527       msg.msg_namelen = sizeof(sa);
528       num_bytes_sent = sendmsg(handle_.fd, &msg, 0);
529       break;
530     }
531   }
532 
533   if (num_bytes_sent == -1) {
534     if (client_) {
535       client_->OnSendError(this,
536                            ChooseError(errno, Error::Code::kSocketSendFailure));
537     }
538     return;
539   }
540 
541   // Sanity-check: UDP datagram sendmsg() is all or nothing.
542   OSP_DCHECK_EQ(static_cast<size_t>(num_bytes_sent), length);
543 }
544 
SetDscp(UdpSocket::DscpMode state)545 void UdpSocketPosix::SetDscp(UdpSocket::DscpMode state) {
546   if (is_closed()) {
547     OnError(Error::Code::kSocketClosedFailure);
548     return;
549   }
550 
551   constexpr auto kSettingLevel = IPPROTO_IP;
552   uint8_t code_array[1] = {static_cast<uint8_t>(state)};
553   auto code = setsockopt(handle_.fd, kSettingLevel, IP_TOS, code_array,
554                          sizeof(uint8_t));
555 
556   if (code == EBADF || code == ENOTSOCK || code == EFAULT) {
557     OSP_VLOG << "BAD SOCKET PROVIDED. CODE: " << code;
558     OnError(Error::Code::kSocketOptionSettingFailure);
559   } else if (code == EINVAL) {
560     OSP_VLOG << "INVALID DSCP INFO PROVIDED";
561     OnError(Error::Code::kSocketOptionSettingFailure);
562   } else if (code == ENOPROTOOPT) {
563     OSP_VLOG << "INVALID DSCP SETTING LEVEL PROVIDED: " << kSettingLevel;
564     OnError(Error::Code::kSocketOptionSettingFailure);
565   }
566 }
567 
OnError(Error::Code error_code)568 void UdpSocketPosix::OnError(Error::Code error_code) {
569   // The call to Close() may change |errno|, so save it here.
570   const auto original_errno = errno;
571 
572   // Close the socket unless the error code represents a transient condition.
573   if (error_code != Error::Code::kNone && error_code != Error::Code::kAgain) {
574     Close();
575   }
576 
577   if (client_) {
578     // Call the thread-safe strerror_r() to get the human-readable form of
579     // |errno|. This is a real mess: 1. Since there seems to be no constant
580     // defined for the maximum buffer size in the standard library, 1024 is
581     // used, as suggested by the man page for strerror_r(). 2. There are two
582     // possible versions of this function: The POSIX one returns int(0) on
583     // success, while the legacy GNU-specific one will provide a non-null char
584     // pointer (that may or may not be within the |buffer|).
585     char buffer[1024];
586     const auto result = strerror_r(original_errno, buffer, sizeof(buffer));
587     const char* errno_str;
588     if (std::is_convertible<decltype(result), int>::value &&
589         !result) {  // Case 1: POSIX strerror_r() success.
590       errno_str = buffer;
591     } else if (std::is_convertible<decltype(result), const char*>::value &&
592                result) {  // Case 2: GNU strerror_r() success.
593       errno_str = reinterpret_cast<const char*>(result);
594     } else {  // Case 3: strerror_r() failed (either version).
595       buffer[0] = '\0';
596       errno_str = buffer;
597     }
598 
599     std::stringstream stream;
600     stream << "endpoint: " << local_endpoint_ << ", error: " << errno_str;
601     client_->OnError(this, Error(error_code, stream.str()));
602   }
603 }
604 
Close()605 void UdpSocketPosix::Close() {
606   if (handle_.fd < 0) {
607     return;
608   }
609 
610   // Notify the UdpSocketReaderPosix that the socket handle is about to be
611   // closed.
612   if (platform_client_) {
613     platform_client_->udp_socket_reader()->OnDestroy(this);
614   }
615 
616   // It's now safe to close the socket, since no other thread (e.g., from
617   // UdpSocketReaderPosix) should be inside ReceiveMessage() at this point.
618   close(handle_.fd);
619   handle_.fd = -1;
620 }
621 
622 }  // namespace openscreen
623