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