/*
 * Copyright ©2026 Soham Pardeshi. All rights reserved.
 * Permission is hereby granted to students registered for University of
 * Washington CSE 333 for use solely during Summer Quarter 2026 for
 * purposes of the course. No other use, copying, distribution, or
 * modification is permitted without prior written consent. Copyrights
 * for third-party components of this work must be honored. Instructors
 * interested in reusing these course materials should contact the author.
 */

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

#include <cstdlib>
#include <cstring>
#include <iostream>

// DNS lookup with getaddrinfo(): hostname -> list of sockaddr structs
// you can hand straight to connect() or bind().
//
//   ./dnsresolve www.washington.edu
//
// getaddrinfo() may block for a long time -- it can require talking to
// DNS servers out on the Internet. It returns a heap-allocated linked
// list, so every success path must freeaddrinfo() it.
void Usage(char* progname) {
  std::cerr << "usage: " << progname << " hostname" << std::endl;
  exit(EXIT_FAILURE);
}

int main(int argc, char** argv) {
  if (argc != 2) {
    Usage(argv[0]);
  }

  struct addrinfo hints;
  memset(&hints, 0, sizeof(hints));      // unset fields must be zeroed
  hints.ai_family = AF_UNSPEC;           // IPv4 or IPv6, we don't care
  hints.ai_socktype = SOCK_STREAM;       // TCP; without this we'd get one
                                         // result per socket type per IP

  struct addrinfo* results;
  int retval = getaddrinfo(argv[1], nullptr, &hints, &results);
  if (retval != 0) {
    // Not errno: getaddrinfo has its own error codes and gai_strerror().
    std::cerr << "getaddrinfo failed: " << gai_strerror(retval) << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "addresses for '" << argv[1] << "':" << std::endl;
  for (struct addrinfo* r = results; r != nullptr; r = r->ai_next) {
    // ai_addr is a generic sockaddr*; cast based on the family before use.
    if (r->ai_family == AF_INET) {
      char ipstring[INET_ADDRSTRLEN];
      struct sockaddr_in* v4addr = reinterpret_cast<sockaddr_in*>(r->ai_addr);
      inet_ntop(AF_INET, &(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 =
          reinterpret_cast<sockaddr_in6*>(r->ai_addr);
      inet_ntop(AF_INET6, &(v6addr->sin6_addr), ipstring, INET6_ADDRSTRLEN);
      std::cout << "  IPv6: " << ipstring << std::endl;
    } else {
      std::cout << "  unknown address family " << r->ai_family << std::endl;
    }
  }

  freeaddrinfo(results);
  return EXIT_SUCCESS;
}