text: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  #include "utils/NetworkInterfaceInfo.h"
  #include "utils/OsUtils.h"
  #include "core/logging/LoggerConfiguration.h"
  #ifdef WIN32
  #include <Windows.h>
  #include <winsock2.h>
  #include <iphlpapi.h>
  #include <WS2tcpip.h>
  #pragma comment(lib, "IPHLPAPI.lib")
  #else
  #include <unistd.h>
  #include <netinet/in.h>
  #include <sys/socket.h>
  #include <net/if.h>
  #include <arpa/inet.h>
  #include <ifaddrs.h>
  #endif
  
  namespace org {
  namespace apache {
  namespace nifi {
  namespace minifi {
  namespace utils {
  
  std::shared_ptr<core::logging::Logger> NetworkInterfaceInfo::logger_ = core::logging::LoggerFactory<NetworkInterfaceInfo>::getLogger();
  
  #ifdef WIN32
  namespace {
  std::string utf8_encode(const std::wstring& wstr) {
    if (wstr.empty())
      return std::string();
    int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    std::string result_string(size_needed, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, result_string.data(), size_needed, nullptr, nullptr);
    return result_string;
  }
  }
  
  NetworkInterfaceInfo::NetworkInterfaceInfo(const IP_ADAPTER_ADDRESSES* adapter) {
    name_ = utf8_encode(adapter->FriendlyName);
    for (auto unicast_address = adapter->FirstUnicastAddress; unicast_address != nullptr; unicast_address = unicast_address->Next) {
      if (unicast_address->Address.lpSockaddr->sa_family == AF_INET) {
        ip_v4_addresses_.push_back(OsUtils::sockaddr_ntop(unicast_address->Address.lpSockaddr));
      } else if (unicast_address->Address.lpSockaddr->sa_family == AF_INET6) {
        ip_v6_addresses_.push_back(OsUtils::sockaddr_ntop(unicast_address->Address.lpSockaddr));
      }
    }
    running_ = adapter->OperStatus == IfOperStatusUp;
    loopback_ = adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK;
  }
  #else
  NetworkInterfaceInfo::NetworkInterfaceInfo(const struct ifaddrs* ifa) {
    name_ = ifa->ifa_name;
    if (ifa->ifa_addr->sa_family == AF_INET) {
      ip_v4_addresses_.push_back(OsUtils::sockaddr_ntop(ifa->ifa_addr));
    } else if (ifa->ifa_addr->sa_family == AF_INET6) {
      ip_v6_addresses_.push_back(OsUtils::sockaddr_ntop(ifa->ifa_addr));
    }
    running_ = (ifa->ifa_flags & IFF_RUNNING);
    loopback_ = (ifa->ifa_flags & IFF_LOOPBACK);
  }
  #endif
  
  namespace {
  struct HasName {
    explicit HasName(const std::string& name) : name_(name) {}
    bool operator()(const NetworkInterfaceInfo& network_interface_info) {
      return network_interface_info.getName() == name_;
    }
    const std::string& name_;
  };
  }
  
  std::vector<NetworkInterfaceInfo> NetworkInterfaceInfo::getNetworkInterfaceInfos(std::function<bool(const NetworkInterfaceInfo&)> filter,
      const std::optional<uint32_t> max_interfaces) {
    std::vector<NetworkInterfaceInfo> network_adapters;
  #ifdef WIN32
    ULONG buffer_length = sizeof(IP_ADAPTER_ADDRESSES);
    auto get_adapters_err = GetAdaptersAddresses(0, 0, nullptr, nullptr, &buffer_length);
    if (ERROR_BUFFER_OVERFLOW != get_adapters_err) {
      logger_->log_error("GetAdaptersAddresses failed: %lu", get_adapters_err);
      return network_adapters;
    }
    std::vector<char> bytes(buffer_length, 0);
    IP_ADAPTER_ADDRESSES* adapter = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(bytes.data());
    get_adapters_err = GetAdaptersAddresses(0, 0, nullptr, adapter, &buffer_length);
    if (NO_ERROR != get_adapters_err) {
      logger_->log_error("GetAdaptersAddresses failed: %lu", get_adapters_err);
      return network_adapters;
    }
    while (adapter != nullptr) {
      NetworkInterfaceInfo interface_info(adapter);
      if (filter(interface_info)) {
        auto it = std::find_if(network_adapters.begin(), network_adapters.end(), HasName(interface_info.getName()));
        if (it == network_adapters.end()) {
          network_adapters.emplace_back(std::move(interface_info));
        } else {
          interface_info.moveAddressesInto(*it);
        }
      }
      if (max_interfaces.has_value() && network_adapters.size() >= max_interfaces.value())
        return network_adapters;
      adapter = adapter->Next;
    }
  #else
    struct ifaddrs* interface_addresses = nullptr;
    auto cleanup = gsl::finally([interface_addresses] { freeifaddrs(interface_addresses); });
    if (getifaddrs(&interface_addresses) == -1) {
      logger_->log_error("getifaddrs failed: %s", std::strerror(errno));
      return network_adapters;
    }
  
    for (struct ifaddrs* ifa = interface_addresses; ifa != nullptr; ifa = ifa->ifa_next) {
      if (!ifa->ifa_addr)
        continue;
      NetworkInterfaceInfo interface_info(ifa);
      if (filter(interface_info)) {
        auto it = std::find_if(network_adapters.begin(), network_adapters.end(), HasName(interface_info.getName()));
        if (it == network_adapters.end()) {
          network_adapters.emplace_back(std::move(interface_info));
        } else {
          interface_info.moveAddressesInto(*it);
        }
      }
      if (max_interfaces.has_value() && network_adapters.size() >= max_interfaces.value())
        return network_adapters;
    }
  #endif
    return network_adapters;
  }
  
  namespace {
  void move_append(std::vector<std::string> &&source, std::vector<std::string> &destination) {
    destination.reserve(destination.size() + source.size());
    std::move(std::begin(source), std::end(source), std::back_inserter(destination));
    source.clear();
  }
  }  // namespace
  
  void NetworkInterfaceInfo::moveAddressesInto(NetworkInterfaceInfo& destination) {
    move_append(std::move(ip_v4_addresses_), destination.ip_v4_addresses_);
    move_append(std::move(ip_v6_addresses_), destination.ip_v6_addresses_);
  }
  
  } /* namespace utils */
  } /* namespace minifi */
  } /* namespace nifi */
  } /* namespace apache */
  } /* namespace org */
  