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