#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <iostream>

void Usage(char *progname) {
  std::cerr << "usage: " << progname << " hostname" << std::endl;
  exit(EXIT_FAILURE);
}

int main(int argc, char **argv) {
  int retval;
  struct addrinfo hints, *results, *r;

  if (argc != 2) {
    Usage(argv[0]);
  }

  // Zero out the hints data structure using memset.
  memset(&hints, 0, sizeof(hints));

  // Indicate we're happy with both AF_INET or AF_INET6 addresses.
  hints.ai_family = AF_UNSPEC;

  // Constrain the answers to SOCK_STREAM addresses.  [You can use
  // the ai_addr field in a result as an argument to connect() or
  // bind(), so if you leave ai_socketype as 0 for unconstrained,
  // you'll get multiple results for each IP address found, one for
  // each socket type.]
  hints.ai_socktype = SOCK_STREAM;

  // Do the lookup by invoking getaddrinfo().  This could take some
  // time, since the resolution may require communicating with one
  // or more DNS resolvers out on the Internet.
  if ((retval = getaddrinfo(argv[1], nullptr, &hints, &results)) != 0) {
    std::cerr << "getaddrinfo failed: ";
    // gai_strerror is defined in netdb.h and returns a
    // string describing getaddrinfo() error value.
    std::cerr << gai_strerror(retval) << std::endl;
    return EXIT_FAILURE;
  }

  // Print the results!
  std::cout << "Here are the IP addresses found for '" << argv[1];
  std::cout << "'" << std::endl;
  for (r = results; r != nullptr; r = r->ai_next) {
    // Treat the IPv4 and IPv6 cases differently.
    if (r->ai_family == AF_INET) {
      char ipstring[INET_ADDRSTRLEN];
      struct sockaddr_in *v4addr = (struct sockaddr_in *) r->ai_addr;
      inet_ntop(r->ai_family,
                &(v4addr->sin_addr),
                ipstring,
                INET_ADDRSTRLEN);
      std::cout << "  IPv4: " << ipstring << std::endl;
    } else if (r->ai_family == AF_INET6) {
      char ipstring[INET6_ADDRSTRLEN];
      struct sockaddr_in6 *v6addr = (struct sockaddr_in6 *) r->ai_addr;
      inet_ntop(r->ai_family,
                &(v6addr->sin6_addr),
                ipstring,
                INET6_ADDRSTRLEN);
      std::cout << "  IPv6: " << ipstring << std::endl;
    } else {
      std::cout << "  unknown address family " << r->ai_family << std::endl;
    }
  }

  // Clean up.
  freeaddrinfo(results);
  return 0;
}