-
Notifications
You must be signed in to change notification settings - Fork 71
/
address.cc
157 lines (128 loc) · 4.33 KB
/
address.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <string>
#include <cstring>
#include <memory>
#include <cassert>
#include <functional>
#include <netdb.h>
#include "address.hh"
#include "strict_conversions.hh"
#include "util/util.hh"
#include "util/exception.hh"
using namespace std;
template <typename T> void zero( T & x ) { memset( &x, 0, sizeof( x ) ); }
/* constructors */
Address::Address()
: Address( "0", 0 )
{}
Address::Address( const raw & addr, const size_t size )
: Address( addr.as_sockaddr, size )
{}
Address::Address( const sockaddr & addr, const size_t size )
: size_( size ),
addr_()
{
/* make sure proposed sockaddr can fit */
if ( size > sizeof( addr_ ) ) {
throw runtime_error( "invalid sockaddr size" );
}
memcpy( &addr_, &addr, size_ );
}
Address::Address( const sockaddr_in & addr )
: size_( sizeof( sockaddr_in ) ),
addr_()
{
assert( size_ <= sizeof( addr_ ) );
memcpy( &addr_, &addr, size_ );
}
/* error category for getaddrinfo and getnameinfo */
class gai_error_category : public error_category
{
public:
const char * name( void ) const noexcept override { return "gai_error_category"; }
string message( const int return_value ) const noexcept override
{
return gai_strerror( return_value );
}
};
/* private constructor given ip/host, service/port, and optional hints */
Address::Address( const string & node, const string & service, const addrinfo & hints )
: size_(),
addr_()
{
/* prepare for the answer */
addrinfo *resolved_address;
/* look up the name or names */
const int gai_ret = getaddrinfo( node.c_str(), service.c_str(), &hints, &resolved_address );
if ( gai_ret ) {
string explanation = "getaddrinfo(" + node + ":" + service;
if ( hints.ai_flags | (AI_NUMERICHOST | AI_NUMERICSERV) ) {
explanation += ", numeric";
}
explanation += ")";
throw tagged_error( gai_error_category(), explanation, gai_ret );
}
/* if success, should always have at least one entry */
if ( not resolved_address ) {
throw runtime_error( "getaddrinfo returned successfully but with no results" );
}
/* put resolved_address in a wrapper so it will get freed if we have to throw an exception */
unique_ptr<addrinfo, function<void(addrinfo*)>> wrapped_address
{ resolved_address, []( addrinfo * x ) { freeaddrinfo( x ); } };
/* assign to our private members (making sure size fits) */
*this = Address( *wrapped_address->ai_addr, wrapped_address->ai_addrlen );
}
/* construct by resolving host name and service name */
Address::Address( const std::string & hostname, const std::string & service )
: size_(),
addr_()
{
addrinfo hints;
zero( hints );
hints.ai_family = AF_INET;
*this = Address( hostname, service, hints );
}
/* construct with numerical IP address and numeral port number */
Address::Address( const std::string & ip, const uint16_t port )
: size_(),
addr_()
{
/* tell getaddrinfo that we don't want to resolve anything */
addrinfo hints;
zero( hints );
hints.ai_family = AF_INET;
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
*this = Address( ip, ::to_string( port ), hints );
}
/* accessors */
pair<string, uint16_t> Address::ip_port( void ) const
{
char ip[ NI_MAXHOST ], port[ NI_MAXSERV ];
const int gni_ret = getnameinfo( &to_sockaddr(),
size_,
ip, sizeof( ip ),
port, sizeof( port ),
NI_NUMERICHOST | NI_NUMERICSERV );
if ( gni_ret ) {
throw tagged_error( gai_error_category(), "getnameinfo", gni_ret );
}
return make_pair( ip, strict_atoi( port ) );
}
string Address::str( const string port_separator ) const
{
const auto ip_and_port = ip_port();
return ip_and_port.first + port_separator + to_string( ip_and_port.second );
}
const sockaddr & Address::to_sockaddr( void ) const
{
return addr_.as_sockaddr;
}
/* comparisons */
bool Address::operator==( const Address & other ) const
{
return 0 == memcmp( &addr_, &other.addr_, size_ );
}
bool Address::operator<( const Address & other ) const
{
return (memcmp( &addr_, &other.addr_, sizeof( addr_ ) ) < 0 );
}