void for_each_address(bool get_ipv4, bool get_ipv6, F fun) {
  ifaddrs* tmp = nullptr;
  if (getifaddrs(&tmp) != 0) {
    perror("getifaddrs");
    return;
  }
  char buffer[INET6_ADDRSTRLEN];
  std::unique_ptr<ifaddrs, decltype(freeifaddrs)*> ifs{tmp, freeifaddrs};
  for (auto i = ifs.get(); i != nullptr; i = i->ifa_next) {
    auto family = fetch_addr_str(get_ipv4, get_ipv6, buffer, i->ifa_addr);
    if (family != AF_UNSPEC)
      fun(i->ifa_name, family == AF_INET ? protocol::ipv4 : protocol::ipv6,
          (i->ifa_flags & IFF_LOOPBACK) != 0,
          buffer);
  }
}
void for_each_address(bool get_ipv4, bool get_ipv6, F fun) {
  ULONG tmp_size = 16 * 1024; // try 16kb buffer first
  IP_ADAPTER_ADDRESSES* tmp = nullptr;
  constexpr size_t max_tries = 3;
  size_t try_nr = 0;
  int retval = 0;
  do {
    if (tmp)
      free(tmp);
    tmp = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(tmp_size));
    if (!tmp)
      CAF_RAISE_ERROR("malloc() failed");
    retval = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
                                  nullptr, tmp, &tmp_size);
  } while (retval == ERROR_BUFFER_OVERFLOW && ++try_nr < max_tries);
  std::unique_ptr<IP_ADAPTER_ADDRESSES, decltype(free)*> ifs{tmp, free};
  if (retval != NO_ERROR) {
    std::cerr << "Call to GetAdaptersAddresses failed with error: "
              << retval << std::endl;
    if (retval == ERROR_NO_DATA) {
      std::cerr << "No addresses were found for the requested parameters"
                << std::endl;
    } else {
      void* msgbuf = nullptr;
      if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
                        | FORMAT_MESSAGE_FROM_SYSTEM
                        | FORMAT_MESSAGE_IGNORE_INSERTS,
                        nullptr, retval,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (LPTSTR) &msgbuf, 0, nullptr)) {
        printf("Error: %s", static_cast<char*>(msgbuf));
        LocalFree(msgbuf);
      }
    }
    return;
  }
  char buffer[INET6_ADDRSTRLEN];
  for (auto i = ifs.get(); i != nullptr; i = i->Next) {
    for (auto j = i->FirstUnicastAddress; j != nullptr; j = j->Next) {
      auto addr = j->Address.lpSockaddr;
      auto family = fetch_addr_str(get_ipv4, get_ipv6, buffer, addr);
      if (family != AF_UNSPEC)
        fun(i->AdapterName, family == AF_INET ? protocol::ipv4 : protocol::ipv6,
            false, buffer);
    }
  }
}
maybe<std::pair<std::string, protocol>>
interfaces::native_address(const std::string& host,
                           maybe<protocol> preferred) {
  addrinfo hint;
  memset(&hint, 0, sizeof(hint));
  hint.ai_socktype = SOCK_STREAM;
  if (preferred)
    hint.ai_family = *preferred == protocol::ipv4 ? AF_INET : AF_INET6;
  addrinfo* tmp = nullptr;
  if (getaddrinfo(host.c_str(), nullptr, &hint, &tmp))
    return none;
  std::unique_ptr<addrinfo, decltype(freeaddrinfo)*> addrs{tmp, freeaddrinfo};
  char buffer[INET6_ADDRSTRLEN];
  for (auto i = addrs.get(); i != nullptr; i = i->ai_next) {
    auto family = fetch_addr_str(true, true, buffer, i->ai_addr);
    if (family != AF_UNSPEC)
      return {{buffer, family == AF_INET ? protocol::ipv4 : protocol::ipv6}};
  }
  return none;
}