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