| // Copyright 2017 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/base/network_interfaces_getifaddrs.h" | 
 |  | 
 | #include <ifaddrs.h> | 
 | #include <net/if.h> | 
 | #include <netinet/in.h> | 
 | #include <sys/types.h> | 
 |  | 
 | #include <memory> | 
 | #include <set> | 
 |  | 
 | #include "starboard/types.h" | 
 |  | 
 | #include "starboard/common/string.h" | 
 |  | 
 | #include "starboard/memory.h" | 
 |  | 
 | #include "base/files/file_path.h" | 
 | #include "base/logging.h" | 
 | #include "base/posix/eintr_wrapper.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/strings/string_tokenizer.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "build/build_config.h" | 
 | #include "net/base/ip_endpoint.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/base/network_interfaces_posix.h" | 
 |  | 
 | #if defined(OS_MACOSX) && !defined(OS_IOS) | 
 | #include <net/if_media.h> | 
 | #include <netinet/in_var.h> | 
 | #include <sys/ioctl.h> | 
 | #endif  // !OS_IOS | 
 |  | 
 | namespace net { | 
 | namespace internal { | 
 |  | 
 | #if defined(OS_MACOSX) && !defined(OS_IOS) | 
 |  | 
 | // MacOSX implementation of IPAttributesGetter which calls ioctl() on socket to | 
 | // retrieve IP attributes. | 
 | class IPAttributesGetterMac : public internal::IPAttributesGetter { | 
 |  public: | 
 |   IPAttributesGetterMac(); | 
 |   ~IPAttributesGetterMac() override; | 
 |   bool IsInitialized() const override; | 
 |   bool GetAddressAttributes(const ifaddrs* if_addr, int* attributes) override; | 
 |   NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType( | 
 |       const ifaddrs* if_addr) override; | 
 |  | 
 |  private: | 
 |   int ioctl_socket_; | 
 | }; | 
 |  | 
 | IPAttributesGetterMac::IPAttributesGetterMac() | 
 |     : ioctl_socket_(socket(AF_INET6, SOCK_DGRAM, 0)) { | 
 |   DCHECK_GE(ioctl_socket_, 0); | 
 | } | 
 |  | 
 | IPAttributesGetterMac::~IPAttributesGetterMac() { | 
 |   if (IsInitialized()) { | 
 |     PCHECK(IGNORE_EINTR(close(ioctl_socket_)) == 0); | 
 |   } | 
 | } | 
 |  | 
 | bool IPAttributesGetterMac::IsInitialized() const { | 
 |   return ioctl_socket_ >= 0; | 
 | } | 
 |  | 
 | int AddressFlagsToNetAddressAttributes(int flags) { | 
 |   int result = 0; | 
 |   if (flags & IN6_IFF_TEMPORARY) { | 
 |     result |= IP_ADDRESS_ATTRIBUTE_TEMPORARY; | 
 |   } | 
 |   if (flags & IN6_IFF_DEPRECATED) { | 
 |     result |= IP_ADDRESS_ATTRIBUTE_DEPRECATED; | 
 |   } | 
 |   if (flags & IN6_IFF_ANYCAST) { | 
 |     result |= IP_ADDRESS_ATTRIBUTE_ANYCAST; | 
 |   } | 
 |   if (flags & IN6_IFF_TENTATIVE) { | 
 |     result |= IP_ADDRESS_ATTRIBUTE_TENTATIVE; | 
 |   } | 
 |   if (flags & IN6_IFF_DUPLICATED) { | 
 |     result |= IP_ADDRESS_ATTRIBUTE_DUPLICATED; | 
 |   } | 
 |   if (flags & IN6_IFF_DETACHED) { | 
 |     result |= IP_ADDRESS_ATTRIBUTE_DETACHED; | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | bool IPAttributesGetterMac::GetAddressAttributes(const ifaddrs* if_addr, | 
 |                                                  int* attributes) { | 
 |   struct in6_ifreq ifr = {}; | 
 |   SbStringCopy(ifr.ifr_name, if_addr->ifa_name, sizeof(ifr.ifr_name) - 1); | 
 |   SbMemoryCopy(&ifr.ifr_ifru.ifru_addr, if_addr->ifa_addr, | 
 |                if_addr->ifa_addr->sa_len); | 
 |   int rv = ioctl(ioctl_socket_, SIOCGIFAFLAG_IN6, &ifr); | 
 |   if (rv >= 0) { | 
 |     *attributes = AddressFlagsToNetAddressAttributes(ifr.ifr_ifru.ifru_flags); | 
 |   } | 
 |   return (rv >= 0); | 
 | } | 
 |  | 
 | NetworkChangeNotifier::ConnectionType | 
 | IPAttributesGetterMac::GetNetworkInterfaceType(const ifaddrs* if_addr) { | 
 |   if (!IsInitialized()) | 
 |     return NetworkChangeNotifier::CONNECTION_UNKNOWN; | 
 |  | 
 |   struct ifmediareq ifmr = {}; | 
 |   SbStringCopy(ifmr.ifm_name, if_addr->ifa_name, sizeof(ifmr.ifm_name) - 1); | 
 |  | 
 |   if (ioctl(ioctl_socket_, SIOCGIFMEDIA, &ifmr) != -1) { | 
 |     if (ifmr.ifm_current & IFM_IEEE80211) { | 
 |       return NetworkChangeNotifier::CONNECTION_WIFI; | 
 |     } | 
 |     if (ifmr.ifm_current & IFM_ETHER) { | 
 |       return NetworkChangeNotifier::CONNECTION_ETHERNET; | 
 |     } | 
 |   } | 
 |  | 
 |   return NetworkChangeNotifier::CONNECTION_UNKNOWN; | 
 | } | 
 |  | 
 | #endif  // defined(OS_MACOSX) && !defined(OS_IOS) | 
 |  | 
 | bool IfaddrsToNetworkInterfaceList(int policy, | 
 |                                    const ifaddrs* interfaces, | 
 |                                    IPAttributesGetter* ip_attributes_getter, | 
 |                                    NetworkInterfaceList* networks) { | 
 |   // Enumerate the addresses assigned to network interfaces which are up. | 
 |   for (const ifaddrs* interface = interfaces; interface != NULL; | 
 |        interface = interface->ifa_next) { | 
 |     // Skip loopback interfaces, and ones which are down. | 
 |     if (!(IFF_RUNNING & interface->ifa_flags)) | 
 |       continue; | 
 |     if (IFF_LOOPBACK & interface->ifa_flags) | 
 |       continue; | 
 |     // Skip interfaces with no address configured. | 
 |     struct sockaddr* addr = interface->ifa_addr; | 
 |     if (!addr) | 
 |       continue; | 
 |  | 
 |     // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses | 
 |     // configured on non-loopback interfaces. | 
 |     if (IsLoopbackOrUnspecifiedAddress(addr)) | 
 |       continue; | 
 |  | 
 |     std::string name = interface->ifa_name; | 
 |     // Filter out VMware interfaces, typically named vmnet1 and vmnet8. | 
 |     if (ShouldIgnoreInterface(name, policy)) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     NetworkChangeNotifier::ConnectionType connection_type = | 
 |         NetworkChangeNotifier::CONNECTION_UNKNOWN; | 
 |  | 
 |     int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE; | 
 |  | 
 |     // Retrieve native ip attributes and convert to net version if a getter is | 
 |     // given. | 
 |     if (ip_attributes_getter && ip_attributes_getter->IsInitialized()) { | 
 |       if (addr->sa_family == AF_INET6 && | 
 |           ip_attributes_getter->GetAddressAttributes(interface, | 
 |                                                      &ip_attributes)) { | 
 |         // Disallow addresses with attributes ANYCASE, DUPLICATED, TENTATIVE, | 
 |         // and DETACHED as these are still progressing through duplicated | 
 |         // address detection (DAD) or are not suitable to be used in an | 
 |         // one-to-one communication and shouldn't be used by the application | 
 |         // layer. | 
 |         if (ip_attributes & | 
 |             (IP_ADDRESS_ATTRIBUTE_ANYCAST | IP_ADDRESS_ATTRIBUTE_DUPLICATED | | 
 |              IP_ADDRESS_ATTRIBUTE_TENTATIVE | IP_ADDRESS_ATTRIBUTE_DETACHED)) { | 
 |           continue; | 
 |         } | 
 |       } | 
 |  | 
 |       connection_type = | 
 |           ip_attributes_getter->GetNetworkInterfaceType(interface); | 
 |     } | 
 |  | 
 |     IPEndPoint address; | 
 |  | 
 |     int addr_size = 0; | 
 |     if (addr->sa_family == AF_INET6) { | 
 |       addr_size = sizeof(sockaddr_in6); | 
 |     } else if (addr->sa_family == AF_INET) { | 
 |       addr_size = sizeof(sockaddr_in); | 
 |     } | 
 |  | 
 |     if (address.FromSockAddr(addr, addr_size)) { | 
 |       uint8_t prefix_length = 0; | 
 |       if (interface->ifa_netmask) { | 
 |         // If not otherwise set, assume the same sa_family as ifa_addr. | 
 |         if (interface->ifa_netmask->sa_family == 0) { | 
 |           interface->ifa_netmask->sa_family = addr->sa_family; | 
 |         } | 
 |         IPEndPoint netmask; | 
 |         if (netmask.FromSockAddr(interface->ifa_netmask, addr_size)) { | 
 |           prefix_length = MaskPrefixLength(netmask.address()); | 
 |         } | 
 |       } | 
 |       networks->push_back(NetworkInterface( | 
 |           name, name, if_nametoindex(name.c_str()), connection_type, | 
 |           address.address(), prefix_length, ip_attributes)); | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace internal | 
 |  | 
 | bool GetNetworkList(NetworkInterfaceList* networks, int policy) { | 
 |   if (networks == NULL) | 
 |     return false; | 
 |  | 
 |   // getifaddrs() may require IO operations. | 
 |   base::AssertBlockingAllowed(); | 
 |  | 
 |   ifaddrs* interfaces; | 
 |   if (getifaddrs(&interfaces) < 0) { | 
 |     PLOG(ERROR) << "getifaddrs"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::unique_ptr<internal::IPAttributesGetter> ip_attributes_getter; | 
 |  | 
 | #if defined(OS_MACOSX) && !defined(OS_IOS) | 
 |   ip_attributes_getter = std::make_unique<internal::IPAttributesGetterMac>(); | 
 | #endif | 
 |  | 
 |   bool result = internal::IfaddrsToNetworkInterfaceList( | 
 |       policy, interfaces, ip_attributes_getter.get(), networks); | 
 |   freeifaddrs(interfaces); | 
 |   return result; | 
 | } | 
 |  | 
 | std::string GetWifiSSID() { | 
 |   NOTIMPLEMENTED(); | 
 |   return std::string(); | 
 | } | 
 |  | 
 | }  // namespace net |