1 /*******************************************************************************
2 * thrill/net/tcp/socket_address.cpp
3 *
4 * Implements lookups and conversions to low-level socket address structs.
5 *
6 * Part of Project Thrill - http://project-thrill.org
7 *
8 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
9 *
10 * All rights reserved. Published under the BSD-2 license in the LICENSE file.
11 ******************************************************************************/
12
13 #include <thrill/common/logger.hpp>
14 #include <thrill/net/tcp/socket_address.hpp>
15
16 #include <arpa/inet.h>
17 #include <netdb.h>
18
19 #include <algorithm>
20 #include <cerrno>
21 #include <string>
22 #include <vector>
23
24 namespace thrill {
25 namespace net {
26 namespace tcp {
27
28 /******************************************************************************/
29
SocketAddress(struct sockaddr * sa,socklen_t salen)30 SocketAddress::SocketAddress(struct sockaddr* sa, socklen_t salen)
31 : resolve_error_(0) {
32 memcpy(&sockaddr_, sa, std::min<socklen_t>(salen, sizeof(sockaddr_)));
33 }
34
SocketAddress(const std::string & hostport)35 SocketAddress::SocketAddress(const std::string& hostport) {
36 std::string host = hostport;
37 size_t colonpos = host.rfind(':');
38 if (colonpos == std::string::npos)
39 {
40 Resolve(hostport.c_str());
41 }
42 else
43 {
44 std::string port = host.substr(colonpos + 1);
45 host.erase(colonpos);
46 Resolve(host.c_str(), port.c_str());
47 }
48 }
49
SocketAddress(const char * hostname,const char * servicename)50 SocketAddress::SocketAddress(const char* hostname, const char* servicename) {
51 Resolve(hostname, servicename);
52 }
53
ToStringHost() const54 std::string SocketAddress::ToStringHost() const {
55 char str[64];
56 if (sockaddr()->sa_family == AF_INET)
57 {
58 if (inet_ntop(AF_INET,
59 &sockaddr_in()->sin_addr, str, sizeof(str)) == nullptr)
60 {
61 sLOG << "Error in inet_ntop: " << strerror(errno);
62 return "<error>";
63 }
64 return str;
65 }
66 else if (sockaddr()->sa_family == AF_INET6)
67 {
68 if (inet_ntop(AF_INET6,
69 &sockaddr_in6()->sin6_addr, str, sizeof(str)) == nullptr)
70 {
71 sLOG << "Error in inet_ntop: " << strerror(errno);
72 return "<error>";
73 }
74 return str;
75 }
76 else
77 return "<invalid>";
78 }
79
ToStringHostPort() const80 std::string SocketAddress::ToStringHostPort() const {
81 return ToStringHost() + ":" + std::to_string(GetPort());
82 }
83
operator <<(std::ostream & os,const SocketAddress & sa)84 std::ostream& operator << (std::ostream& os, const SocketAddress& sa) {
85 return os << sa.ToStringHostPort();
86 }
87
Resolve(const char * hostname,const char * servicename)88 bool SocketAddress::Resolve(const char* hostname, const char* servicename) {
89 struct addrinfo* result;
90 struct addrinfo hints;
91
92 memset(&hints, 0, sizeof(struct addrinfo));
93 hints.ai_family = AF_INET; /* Allow IPv4 or IPv6 */
94 hints.ai_socktype = SOCK_STREAM;
95 hints.ai_flags = 0;
96 hints.ai_protocol = 0;
97
98 int gai = getaddrinfo(hostname, servicename, &hints, &result);
99 if (gai != 0)
100 {
101 memset(&sockaddr_, 0, sizeof(sockaddr_));
102 resolve_error_ = gai;
103 return false;
104 }
105 else
106 {
107 *this = SocketAddress(result->ai_addr, result->ai_addrlen);
108 freeaddrinfo(result);
109 return IsValid();
110 }
111 }
112
GetResolveError() const113 const char* SocketAddress::GetResolveError() const {
114 return gai_strerror(resolve_error_);
115 }
116
117 SocketAddress
ResolveOne(const char * hostname,const char * servicename)118 SocketAddress::ResolveOne(const char* hostname, const char* servicename) {
119 struct addrinfo* result;
120 struct addrinfo hints;
121
122 memset(&hints, 0, sizeof(struct addrinfo));
123 hints.ai_family = AF_INET; /* Allow IPv4 or IPv6 */
124 hints.ai_socktype = SOCK_STREAM;
125 hints.ai_flags = 0;
126 hints.ai_protocol = 0;
127
128 int s = getaddrinfo(hostname, servicename, &hints, &result);
129 if (s != 0) {
130 return SocketAddress();
131 }
132
133 SocketAddress sa(result->ai_addr, result->ai_addrlen);
134
135 freeaddrinfo(result);
136
137 return sa;
138 }
139
140 SocketAddress
ResolveWithPort(const char * hostname,const char * defaultservice)141 SocketAddress::ResolveWithPort(const char* hostname,
142 const char* defaultservice) {
143 std::string host = hostname;
144
145 std::string::size_type colonpos = host.rfind(':');
146 if (colonpos == std::string::npos)
147 return ResolveOne(hostname, defaultservice);
148
149 std::string servicename(host, colonpos + 1);
150 host.erase(colonpos);
151
152 return ResolveOne(host.c_str(), servicename.c_str());
153 }
154
155 std::vector<SocketAddress>
ResolveAll(const char * hostname,const char * servicename)156 SocketAddress::ResolveAll(const char* hostname, const char* servicename) {
157 std::vector<SocketAddress> salist;
158
159 struct addrinfo* result;
160 struct addrinfo hints;
161
162 memset(&hints, 0, sizeof(struct addrinfo));
163 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
164 hints.ai_socktype = SOCK_STREAM;
165 hints.ai_flags = 0;
166 hints.ai_protocol = 0;
167
168 int s = getaddrinfo(hostname, servicename, &hints, &result);
169 if (s != 0) {
170 return salist;
171 }
172
173 for (struct addrinfo* ap = result; ap != nullptr; ap = ap->ai_next)
174 {
175 salist.push_back(SocketAddress(ap->ai_addr, ap->ai_addrlen));
176 }
177
178 freeaddrinfo(result);
179
180 return salist;
181 }
182
183 /******************************************************************************/
184
IPv4Address(const char * ipstring,uint16_t port)185 IPv4Address::IPv4Address(const char* ipstring, uint16_t port)
186 : SocketAddress() {
187 struct sockaddr_in* sin = sockaddr_in();
188 sin->sin_family = AF_INET;
189 if (inet_pton(AF_INET, ipstring, &sin->sin_addr) <= 0) {
190 sin->sin_family = 0;
191 return;
192 }
193 sin->sin_port = htons(port);
194 }
195
196 /******************************************************************************/
197
IPv6Address(const char * ipstring,uint16_t port)198 IPv6Address::IPv6Address(const char* ipstring, uint16_t port)
199 : SocketAddress() {
200 struct sockaddr_in6* sin6 = sockaddr_in6();
201 sin6->sin6_family = AF_INET6;
202 if (inet_pton(AF_INET6, ipstring, &sin6->sin6_addr) <= 0) {
203 sin6->sin6_family = 0;
204 return;
205 }
206 sin6->sin6_port = htons(port);
207 }
208
209 } // namespace tcp
210 } // namespace net
211 } // namespace thrill
212
213 /******************************************************************************/
214