-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathaddress.cc
142 lines (116 loc) · 3.83 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
#include <string>
#include <cstring>
#include <memory>
#include <netdb.h>
#include "address.hh"
#include "util.hh"
using namespace std;
/* constructors */
Address::Address()
: size_( 0 ),
addr_()
{}
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 );
}
/* error category for getaddrinfo and getnameinfo */
class gai_error_category : public error_category
{
public:
const char * name() 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 ) {
throw tagged_error( gai_error_category(), "getaddrinfo", 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 */
struct Freeaddrinfo_Deleter { void operator()( addrinfo * const x ) const { freeaddrinfo( x ); } };
unique_ptr<addrinfo, Freeaddrinfo_Deleter> wrapped_address( resolved_address );
/* 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 )
: Address()
{
addrinfo hints;
zero( hints );
hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED | AI_ALL;
*this = Address( hostname, service, &hints );
}
/* construct with numerical IP address and numeral port number */
Address::Address( const std::string & ip, const uint16_t port )
: Address()
{
/* tell getaddrinfo that we don't want to resolve anything */
addrinfo hints;
zero( hints );
hints.ai_family = AF_INET6;
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_V4MAPPED;
*this = Address( ip, ::to_string( port ), &hints );
}
/* accessors */
pair<string, uint16_t> Address::ip_port() 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 );
}
/* attempt to shorten v4-mapped address if possible */
string ip_string { ip };
if ( ip_string.size() > 7
and ip_string.substr( 0, 7 ) == "::ffff:" ) {
const string candidate_ipv4 = ip_string.substr( 7 );
Address candidate_addr( candidate_ipv4, stoi( port ) );
if ( candidate_addr == *this ) {
ip_string = candidate_ipv4;
}
}
return make_pair( ip_string, stoi( port ) );
}
string Address::to_string() const
{
const auto ip_and_port = ip_port();
return ip_and_port.first + ":" + ::to_string( ip_and_port.second );
}
const sockaddr & Address::to_sockaddr() const
{
return addr_.as_sockaddr;
}
/* equality */
bool Address::operator==( const Address & other ) const
{
return 0 == memcmp( &addr_, &other.addr_, size_ );
}