| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "platform/globals.h" | |
| #if defined(DART_HOST_OS_MACOS) | |
| #include "bin/socket_base.h" | |
| #include <errno.h> // NOLINT | |
| #include <ifaddrs.h> // NOLINT | |
| #include <net/if.h> // NOLINT | |
| #include <netinet/tcp.h> // NOLINT | |
| #include <stdio.h> // NOLINT | |
| #include <stdlib.h> // NOLINT | |
| #include <string.h> // NOLINT | |
| #include <sys/stat.h> // NOLINT | |
| #include <unistd.h> // NOLINT | |
| #include "bin/fdutils.h" | |
| #include "bin/file.h" | |
| #include "bin/socket_base_macos.h" | |
| #include "platform/signal_blocker.h" | |
| namespace dart { | |
| namespace bin { | |
| void SocketBase::GetError(intptr_t fd, OSError* os_error) { | |
| int len = sizeof(errno); | |
| getsockopt(fd, SOL_SOCKET, SO_ERROR, &errno, | |
| reinterpret_cast<socklen_t*>(&len)); | |
| os_error->SetCodeAndMessage(OSError::kSystem, errno); | |
| } | |
| int SocketBase::GetType(intptr_t fd) { | |
| struct stat buf; | |
| int result = fstat(fd, &buf); | |
| if (result == -1) { | |
| return -1; | |
| } | |
| if (S_ISCHR(buf.st_mode)) { | |
| return File::kTerminal; | |
| } | |
| if (S_ISFIFO(buf.st_mode)) { | |
| return File::kPipe; | |
| } | |
| if (S_ISREG(buf.st_mode)) { | |
| return File::kFile; | |
| } | |
| return File::kOther; | |
| } | |
| AddressList<SocketAddress>* SocketBase::LookupAddress(const char* host, | |
| int type, | |
| OSError** os_error) { | |
| // Perform a name lookup for a host name. | |
| struct addrinfo hints; | |
| memset(&hints, 0, sizeof(hints)); | |
| hints.ai_family = SocketAddress::FromType(type); | |
| hints.ai_socktype = SOCK_STREAM; | |
| hints.ai_flags = 0; | |
| hints.ai_protocol = IPPROTO_TCP; | |
| // Check if host is valid dotted-quad IPv4 address or valid IPv6 address. | |
| struct in_addr ipv4_bin; | |
| struct in6_addr ipv6_bin; | |
| if (inet_pton(AF_INET, host, &ipv4_bin) == 1 | |
| #ifdef INET_PTON_FLAWED | |
| // MacOS accepts `0177.0.0.1` as IPv4. | |
| // Reject leading zero digits that could be intended as octal. | |
| && SocketBase::IsIPv4WithoutLeadingZeros(host) | |
| #endif | |
| ) { | |
| // If it is a valid strict IPv4, parse it safely as a numeric host. | |
| hints.ai_family = AF_INET; | |
| hints.ai_flags |= AI_NUMERICHOST; // Safe local parsing | |
| } else if (inet_pton(AF_INET6, host, &ipv6_bin) == 1) { | |
| // 2. If it is a valid strict IPv6, parse it safely as a numeric host. | |
| hints.ai_family = AF_INET6; | |
| hints.ai_flags |= AI_NUMERICHOST; // Safe local parsing | |
| } else if (inet_aton(host, &ipv4_bin) != 0) { | |
| // Reject legacy inet_aton strings (like "127.1", "0x7f.1") | |
| // If inet_aton accepts and inet_pton does not, it's a malformed IP. | |
| ASSERT(*os_error == nullptr); | |
| int status = EAI_NONAME; | |
| *os_error = | |
| new OSError(status, gai_strerror(status), OSError::kGetAddressInfo); | |
| return nullptr; | |
| } | |
| struct addrinfo* info = nullptr; | |
| int status = getaddrinfo(host, nullptr, &hints, &info); | |
| if (status != 0) { | |
| ASSERT(*os_error == nullptr); | |
| *os_error = | |
| new OSError(status, gai_strerror(status), OSError::kGetAddressInfo); | |
| return nullptr; | |
| } | |
| intptr_t count = 0; | |
| for (struct addrinfo* c = info; c != nullptr; c = c->ai_next) { | |
| if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { | |
| count++; | |
| } | |
| } | |
| intptr_t i = 0; | |
| AddressList<SocketAddress>* addresses = new AddressList<SocketAddress>(count); | |
| for (struct addrinfo* c = info; c != nullptr; c = c->ai_next) { | |
| if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { | |
| addresses->SetAt(i, new SocketAddress(RawAddr::FromInet4or6(c->ai_addr))); | |
| i++; | |
| } | |
| } | |
| freeaddrinfo(info); | |
| return addresses; | |
| } | |
| bool SocketBase::SetMulticastLoop(intptr_t fd, | |
| intptr_t protocol, | |
| bool enabled) { | |
| u_int on = enabled ? 1 : 0; | |
| int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; | |
| int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP | |
| : IPV6_MULTICAST_LOOP; | |
| return NO_RETRY_EXPECTED(setsockopt( | |
| fd, level, optname, reinterpret_cast<char*>(&on), sizeof(on))) == | |
| 0; | |
| } | |
| bool SocketBase::GetOption(intptr_t fd, | |
| int level, | |
| int option, | |
| char* data, | |
| unsigned int* length) { | |
| return NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, length)) == 0; | |
| } | |
| static bool JoinOrLeaveMulticast(intptr_t fd, | |
| const RawAddr& addr, | |
| const RawAddr& interface, | |
| int interfaceIndex, | |
| bool join) { | |
| if (addr.addr.sa_family == AF_INET) { | |
| ASSERT(interface.addr.sa_family == AF_INET); | |
| struct ip_mreq mreq; | |
| memmove(&mreq.imr_multiaddr, &addr.in.sin_addr, | |
| SocketAddress::GetInAddrLength(addr)); | |
| memmove(&mreq.imr_interface, &interface.in.sin_addr, | |
| SocketAddress::GetInAddrLength(interface)); | |
| if (join) { | |
| return NO_RETRY_EXPECTED(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, | |
| &mreq, sizeof(mreq))) == 0; | |
| } else { | |
| return NO_RETRY_EXPECTED(setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, | |
| &mreq, sizeof(mreq))) == 0; | |
| } | |
| } else { | |
| ASSERT(addr.addr.sa_family == AF_INET6); | |
| struct ipv6_mreq mreq; | |
| memmove(&mreq.ipv6mr_multiaddr, &addr.in6.sin6_addr, | |
| SocketAddress::GetInAddrLength(addr)); | |
| mreq.ipv6mr_interface = interfaceIndex; | |
| if (join) { | |
| return NO_RETRY_EXPECTED(setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, | |
| &mreq, sizeof(mreq))) == 0; | |
| } else { | |
| return NO_RETRY_EXPECTED(setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, | |
| &mreq, sizeof(mreq))) == 0; | |
| } | |
| } | |
| } | |
| bool SocketBase::JoinMulticast(intptr_t fd, | |
| const RawAddr& addr, | |
| const RawAddr& interface, | |
| int interfaceIndex) { | |
| return JoinOrLeaveMulticast(fd, addr, interface, interfaceIndex, true); | |
| } | |
| bool SocketBase::LeaveMulticast(intptr_t fd, | |
| const RawAddr& addr, | |
| const RawAddr& interface, | |
| int interfaceIndex) { | |
| return JoinOrLeaveMulticast(fd, addr, interface, interfaceIndex, false); | |
| } | |
| } // namespace bin | |
| } // namespace dart | |
| #endif // defined(DART_HOST_OS_MACOS) |