Sponge
CS144's user-space TCP library
address.cc
Go to the documentation of this file.
1 #include "address.hh"
2 
3 #include "util.hh"
4 
5 #include <arpa/inet.h>
6 #include <cstring>
7 #include <memory>
8 #include <netdb.h>
9 #include <stdexcept>
10 #include <system_error>
11 
12 using namespace std;
13 
15 Address::Raw::operator sockaddr *() { return reinterpret_cast<sockaddr *>(&storage); }
16 
18 Address::Raw::operator const sockaddr *() const { return reinterpret_cast<const sockaddr *>(&storage); }
19 
22 Address::Address(const sockaddr *addr, const size_t size) : _size(size) {
23  // make sure proposed sockaddr can fit
24  if (size > sizeof(_address.storage)) {
25  throw runtime_error("invalid sockaddr size");
26  }
27 
28  memcpy(&_address.storage, addr, size);
29 }
30 
33  public:
35  const char *name() const noexcept override { return "gai_error_category"; }
39  string message(const int return_value) const noexcept override { return gai_strerror(return_value); }
40 };
41 
45 Address::Address(const string &node, const string &service, const addrinfo &hints) : _size() {
46  // prepare for the answer
47  addrinfo *resolved_address = nullptr;
48 
49  // look up the name or names
50  const int gai_ret = getaddrinfo(node.c_str(), service.c_str(), &hints, &resolved_address);
51  if (gai_ret != 0) {
52  throw tagged_error(gai_error_category(), "getaddrinfo(" + node + ", " + service + ")", gai_ret);
53  }
54 
55  // if success, should always have at least one entry
56  if (resolved_address == nullptr) {
57  throw runtime_error("getaddrinfo returned successfully but with no results");
58  }
59 
60  // put resolved_address in a wrapper so it will get freed if we have to throw an exception
61  auto addrinfo_deleter = [](addrinfo *const x) { freeaddrinfo(x); };
62  unique_ptr<addrinfo, decltype(addrinfo_deleter)> wrapped_address(resolved_address, move(addrinfo_deleter));
63 
64  // assign to our private members (making sure size fits)
65  *this = Address(wrapped_address->ai_addr, wrapped_address->ai_addrlen);
66 }
67 
71 static inline addrinfo make_hints(const int ai_flags, const int ai_family) {
72  addrinfo hints{}; // value initialized to all zeros
73  hints.ai_flags = ai_flags;
74  hints.ai_family = ai_family;
75  return hints;
76 }
77 
80 Address::Address(const string &hostname, const string &service)
81  : Address(hostname, service, make_hints(AI_ALL, AF_INET)) {}
82 
85 Address::Address(const string &ip, const uint16_t port)
86  // tell getaddrinfo that we don't want to resolve anything
87  : Address(ip, ::to_string(port), make_hints(AI_NUMERICHOST | AI_NUMERICSERV, AF_INET)) {}
88 
89 // accessors
93 
94  const int gni_ret =
95  getnameinfo(_address, _size, ip.data(), ip.size(), port.data(), port.size(), NI_NUMERICHOST | NI_NUMERICSERV);
96  if (gni_ret != 0) {
97  throw tagged_error(gai_error_category(), "getnameinfo", gni_ret);
98  }
99 
100  return {ip.data(), stoi(port.data())};
101 }
102 
103 string Address::to_string() const {
104  const auto ip_and_port = ip_port();
105  return ip_and_port.first + ":" + ::to_string(ip_and_port.second);
106 }
107 
109  if (_address.storage.ss_family != AF_INET or _size != sizeof(sockaddr_in)) {
110  throw runtime_error("ipv4_numeric called on non-IPV4 address");
111  }
112 
113  sockaddr_in ipv4_addr{};
114  memcpy(&ipv4_addr, &_address.storage, _size);
115 
116  return be32toh(ipv4_addr.sin_addr.s_addr);
117 }
118 
120  sockaddr_in ipv4_addr{};
121  ipv4_addr.sin_family = AF_INET;
122  ipv4_addr.sin_addr.s_addr = htobe32(ip_address);
123 
124  return {reinterpret_cast<sockaddr *>(&ipv4_addr), sizeof(ipv4_addr)};
125 }
126 
127 // equality
128 bool Address::operator==(const Address &other) const {
129  if (_size != other._size) {
130  return false;
131  }
132 
133  return 0 == memcmp(&_address, &other._address, _size);
134 }
static addrinfo make_hints(const int ai_flags, const int ai_family)
Build a struct addrinfo containing hints for getaddrinfo(3).
Definition: address.cc:71
T c_str(T... args)
sockaddr_storage storage
The wrapped struct itself.
Definition: address.hh:19
Wrapper around IPv4 addresses and DNS operations.
Definition: address.hh:13
std::pair< std::string, uint16_t > ip_port() const
Dotted-quad IP address string ("18.243.0.1") and numeric port.
Definition: address.cc:90
socklen_t size() const
Size of the underlying address storage.
Definition: address.hh:66
bool operator==(const Address &other) const
Equality comparison.
Definition: address.cc:128
Address(const std::string &node, const std::string &service, const addrinfo &hints)
Constructor from ip/host, service/port, and hints to the resolver.
Definition: address.cc:45
std::string to_string() const
Human-readable string, e.g., "8.8.8.8:53".
Definition: address.cc:103
uint32_t ipv4_numeric() const
Numeric IP address as an integer (i.e., in host byte order).
Definition: address.cc:108
static Address from_ipv4_numeric(const uint32_t ip_address)
Create an Address from a 32-bit raw numeric IP address.
Definition: address.cc:119
socklen_t _size
Size of the wrapped address.
Definition: address.hh:25
uint16_t port() const
Numeric port (host byte order).
Definition: address.hh:53
std::string ip() const
Dotted-quad IP address string ("18.243.0.1").
Definition: address.hh:51
Raw _address
A wrapped sockaddr_storage containing the address.
Definition: address.hh:26
Error category for getaddrinfo and getnameinfo failures.
Definition: address.cc:32
string message(const int return_value) const noexcept override
An error message.
Definition: address.cc:39
const char * name() const noexcept override
The name of the wrapped error.
Definition: address.cc:35
std::system_error plus the name of what was being attempted
Definition: util.hh:16
T data(T... args)
T memcmp(T... args)
T memcpy(T... args)
T move(T... args)
T size(T... args)
T stoi(T... args)
T to_string(T... args)