| // 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_LINUX) || defined(DART_HOST_OS_ANDROID) | |
| #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_linux.h" | |
| #include "bin/thread.h" | |
| #include "platform/memory_sanitizer.h" | |
| #include "platform/signal_blocker.h" | |
| namespace dart { | |
| namespace bin { | |
| static int fstat64_fixed(int fd, struct stat64* __restrict statbuf) { | |
| int result = ::fstat64(fd, statbuf); | |
| if (result == 0) { | |
| // MSAN only intercepts the old symbol name. | |
| MSAN_UNPOISON(statbuf, sizeof(*statbuf)); | |
| } | |
| return result; | |
| } | |
| static int fstat64(int fd, struct stat64* __restrict statbuf) = delete; | |
| void SocketBase::GetError(intptr_t fd, OSError* os_error) { | |
| int len = sizeof(errno); | |
| int err = 0; | |
| VOID_NO_RETRY_EXPECTED(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, | |
| reinterpret_cast<socklen_t*>(&len))); | |
| errno = err; | |
| os_error->SetCodeAndMessage(OSError::kSystem, errno); | |
| } | |
| int SocketBase::GetType(intptr_t fd) { | |
| struct stat64 buf; | |
| int result = TEMP_FAILURE_RETRY(fstat64_fixed(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 = AI_ADDRCONFIG; | |
| 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 | |
| // Android 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 = NO_RETRY_EXPECTED(getaddrinfo(host, nullptr, &hints, &info)); | |
| if (status != 0) { | |
| // We failed, try without AI_ADDRCONFIG. This can happen when looking up | |
| // e.g. '::1', when there are no global IPv6 addresses. | |
| hints.ai_flags &= ~AI_ADDRCONFIG; | |
| status = NO_RETRY_EXPECTED(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) { | |
| 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) { | |
| socklen_t optlen = static_cast<socklen_t>(*length); | |
| auto result = NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, &optlen)); | |
| *length = static_cast<unsigned int>(optlen); | |
| return result == 0; | |
| } | |
| bool SocketBase::JoinMulticast(intptr_t fd, | |
| const RawAddr& addr, | |
| const RawAddr&, | |
| int interfaceIndex) { | |
| int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; | |
| struct group_req mreq; | |
| mreq.gr_interface = interfaceIndex; | |
| memmove(&mreq.gr_group, &addr.ss, addr.size); | |
| return NO_RETRY_EXPECTED( | |
| setsockopt(fd, proto, MCAST_JOIN_GROUP, &mreq, sizeof(mreq))) == 0; | |
| } | |
| bool SocketBase::LeaveMulticast(intptr_t fd, | |
| const RawAddr& addr, | |
| const RawAddr&, | |
| int interfaceIndex) { | |
| int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; | |
| struct group_req mreq; | |
| mreq.gr_interface = interfaceIndex; | |
| memmove(&mreq.gr_group, &addr.ss, addr.size); | |
| return NO_RETRY_EXPECTED(setsockopt(fd, proto, MCAST_LEAVE_GROUP, &mreq, | |
| sizeof(mreq))) == 0; | |
| } | |
| } // namespace bin | |
| } // namespace dart | |
| #endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) |