Remove trailing junk from IP addresses for sockets
[hiphop-php.git] / hphp / util / network.cpp
blob9da940ed8ddf21995de5b7ef105d0543be658ac1
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/util/network.h"
18 #include <netinet/in.h>
19 #include <arpa/nameser.h>
20 #include <arpa/inet.h>
21 #include <resolv.h>
22 #include <sys/utsname.h>
24 #include <folly/String.h>
25 #include <folly/IPAddress.h>
27 #include "hphp/util/lock.h"
28 #include "hphp/util/process.h"
30 namespace HPHP {
31 ///////////////////////////////////////////////////////////////////////////////
32 // without calling res_init(), any call to getaddrinfo() may leak memory:
33 // http://sources.redhat.com/ml/libc-hacker/2004-02/msg00049.html
35 class ResolverLibInitializer {
36 public:
37 ResolverLibInitializer() {
38 res_init();
39 // We call sethostent with stayopen = 1 to keep /etc/hosts open across calls
40 // to prevent mmap contention inside the kernel. Two calls are necessary to
41 // properly initialize the stayopen flag in glibc.
42 sethostent(1);
43 sethostent(1);
46 static ResolverLibInitializer _resolver_lib_initializer;
47 ///////////////////////////////////////////////////////////////////////////////
48 // thread-safe network functions
50 bool safe_gethostbyname(const char *address, HostEnt &result) {
51 #if defined(__APPLE__) || defined(__CYGWIN__) || defined(__MINGW__) || \
52 defined(_MSC_VER)
53 // NOTE: on windows gethostbyname is "thread safe"
54 // the hostent is allocated once per thread by winsock2
55 // and cleaned up by winsock when the thread ends
56 struct hostent *hp = gethostbyname(address);
58 if (!hp) {
59 return false;
62 result.hostbuf = *hp;
63 #ifdef __APPLE__
64 freehostent(hp);
65 #endif
66 return true;
67 #else
68 struct hostent *hp;
69 int res;
71 size_t hstbuflen = 1024;
72 result.tmphstbuf = (char*)malloc(hstbuflen);
73 while ((res = gethostbyname_r(address, &result.hostbuf, result.tmphstbuf,
74 hstbuflen, &hp, &result.herr)) == ERANGE) {
75 hstbuflen *= 2;
76 result.tmphstbuf = (char*)realloc(result.tmphstbuf, hstbuflen);
78 return !res && hp;
79 #endif
82 ///////////////////////////////////////////////////////////////////////////////
83 std::string GetPrimaryIPImpl(int af) {
84 const static std::string s_empty;
85 struct utsname buf;
86 struct addrinfo hints;
87 struct addrinfo *res = nullptr;
88 int error;
90 SCOPE_EXIT {
91 if (res) {
92 freeaddrinfo(res);
96 uname((struct utsname *)&buf);
98 memset(&hints, 0, sizeof(struct addrinfo));
99 hints.ai_family = af;
101 error = getaddrinfo(buf.nodename, nullptr, &hints, &res);
102 if (error) {
103 return s_empty;
106 try {
107 return folly::IPAddress(res->ai_addr).toFullyQualified();
108 } catch (folly::IPAddressFormatException &e) {
109 return s_empty;
113 std::string GetPrimaryIPv4() {
114 return GetPrimaryIPImpl(AF_INET);
117 std::string GetPrimaryIPv6() {
118 return GetPrimaryIPImpl(AF_INET6);
121 std::string GetPrimaryIP() {
122 auto ipaddress = GetPrimaryIPv4();
123 if (!ipaddress.empty()) {
124 ipaddress = GetPrimaryIPv6();
126 return ipaddress;
129 static std::string normalizeIPv6Address(const std::string& address) {
130 struct in6_addr addr;
131 if (inet_pton(AF_INET6, address.c_str(), &addr) <= 0) {
132 return std::string();
135 char ipPresentation[INET6_ADDRSTRLEN];
136 if (inet_ntop(AF_INET6, &addr, ipPresentation, INET6_ADDRSTRLEN) == nullptr) {
137 return std::string();
140 return ipPresentation;
143 HostURL::HostURL(const std::string &hosturl, int port) :
144 m_ipv6(false), m_port(port) {
146 // Find the scheme
147 auto spos = hosturl.find("://");
148 if (spos != std::string::npos) {
149 m_hosturl = m_scheme = hosturl.substr(0, spos);
150 m_hosturl += "://";
151 spos += 3;
152 } else {
153 spos = 0;
156 // strip off /EXTRA from prot://addr:port/EXTRA
157 auto extraPos = hosturl.find('/', spos);
158 auto validLen = extraPos != std::string::npos ? extraPos : hosturl.size();
160 // IPv6 address?
161 auto bpos = hosturl.find('[');
162 if (bpos != std::string::npos) {
163 // Extract out the IPAddress from [..]
164 // Look for the ending position of ']'
165 auto epos = hosturl.rfind(']', validLen - 1);
166 if (epos == std::string::npos) {
167 // This isn't a valid IPv6 address, so bail.
168 m_valid = false;
169 m_hosturl = hosturl;
170 return;
173 // IPv6 address between '[' and ']'
174 auto v6h = normalizeIPv6Address(hosturl.substr(bpos + 1, epos - bpos - 1));
175 if (v6h.empty()) {
176 m_valid = false;
177 m_hosturl = hosturl;
178 return;
180 m_host = v6h;
181 m_hosturl += '[';
182 m_hosturl += v6h;
183 m_hosturl += ']';
185 // Colon for port. Start after ']';
186 auto cpos = hosturl.find(':', epos);
187 if (cpos != std::string::npos) {
188 try {
189 auto portLen = validLen - cpos - 1;
190 m_port = folly::to<uint16_t>(hosturl.substr(cpos + 1, portLen));
191 m_hosturl += hosturl.substr(cpos);
192 } catch (...) {
193 m_port = 0;
195 } else if (extraPos != std::string::npos) {
196 m_hosturl += hosturl.substr(extraPos);
198 m_ipv6 = true;
199 } else if (m_scheme == "unix") {
200 // unix socket
201 m_host = hosturl.substr(spos);
202 m_hosturl += m_host;
203 } else {
204 // IPv4 or hostname
205 auto cpos = hosturl.find(':', spos);
206 if (cpos != std::string::npos) {
207 m_host = hosturl.substr(spos, cpos - spos);
208 m_hosturl += m_host;
209 try {
210 auto portLen = validLen - cpos - 1;
211 m_port = folly::to<uint16_t>(hosturl.substr(cpos + 1, portLen));
212 m_hosturl += hosturl.substr(cpos);
213 } catch (...) {
214 m_port = 0;
216 } else {
217 m_host = hosturl.substr(spos, validLen - spos);
218 m_hosturl += hosturl.substr(spos);
220 m_ipv6 = false;
222 m_valid = true;
225 ///////////////////////////////////////////////////////////////////////////////