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