Use iterator adapters in decl_class_parents
[hiphop-php.git] / hphp / util / network.cpp
blob5dc1a7bd7f6577eced48983266d2b0d5e28d5839
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 #ifndef _MSC_VER
19 #include <arpa/nameser.h>
20 #include <resolv.h>
21 #include <sys/utsname.h>
22 #endif
24 #include <folly/IPAddress.h>
25 #include <folly/String.h>
26 #include <folly/portability/Sockets.h>
28 namespace HPHP {
30 #ifndef _MSC_VER
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 struct ResolverLibInitializer {
36 ResolverLibInitializer() {
37 res_init();
38 // We call sethostent with stayopen = 1 to keep /etc/hosts open across calls
39 // to prevent mmap contention inside the kernel. Two calls are necessary to
40 // properly initialize the stayopen flag in glibc.
41 sethostent(1);
42 sethostent(1);
45 static ResolverLibInitializer _resolver_lib_initializer;
46 #endif
48 ///////////////////////////////////////////////////////////////////////////////
49 // thread-safe network functions
51 bool safe_gethostbyname(const char *address, HostEnt &result) {
52 #if defined(__APPLE__) || 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 return true;
64 #else
65 struct hostent *hp;
66 int res;
68 size_t hstbuflen = 1024;
69 result.tmphstbuf = (char*)malloc(hstbuflen);
70 while ((res = gethostbyname_r(address, &result.hostbuf, result.tmphstbuf,
71 hstbuflen, &hp, &result.herr)) == ERANGE) {
72 hstbuflen *= 2;
73 result.tmphstbuf = (char*)realloc(result.tmphstbuf, hstbuflen);
75 return !res && hp;
76 #endif
79 ///////////////////////////////////////////////////////////////////////////////
80 std::string GetPrimaryIPImpl(int af) {
81 const static std::string s_empty;
82 struct addrinfo hints;
83 struct addrinfo *res = nullptr;
84 int error;
86 SCOPE_EXIT {
87 if (res) {
88 freeaddrinfo(res);
92 #ifdef _MSC_VER
93 char* nodename = nullptr;
94 char nodenamebuf[65];
95 if (gethostname(nodenamebuf, 65) < 0)
96 nodename = "localhost";
97 else
98 nodename = nodenamebuf;
99 #else
100 struct utsname buf;
101 uname((struct utsname *)&buf);
102 const char* nodename = buf.nodename;
103 #endif
105 memset(&hints, 0, sizeof(struct addrinfo));
106 hints.ai_family = af;
108 error = getaddrinfo(nodename, nullptr, &hints, &res);
109 if (error) {
110 return s_empty;
113 try {
114 return folly::IPAddress(res->ai_addr).toFullyQualified();
115 } catch (folly::IPAddressFormatException& e) {
116 return s_empty;
120 std::string GetPrimaryIPv4() {
121 return GetPrimaryIPImpl(AF_INET);
124 std::string GetPrimaryIPv6() {
125 return GetPrimaryIPImpl(AF_INET6);
128 std::string GetPrimaryIP() {
129 auto ipaddress = GetPrimaryIPv4();
130 if (ipaddress.empty()) {
131 ipaddress = GetPrimaryIPv6();
133 return ipaddress;
136 static std::string normalizeIPv6Address(std::string address) {
137 // strip the scopeId
138 std::string scopeId;
139 auto it = address.find('%');
140 if (it != std::string::npos) {
141 scopeId = address.substr(it + 1);
142 address = address.substr(0, it);
145 struct in6_addr addr;
146 if (inet_pton(AF_INET6, address.c_str(), &addr) <= 0) {
147 return std::string();
150 char ipPresentation[INET6_ADDRSTRLEN];
151 if (inet_ntop(AF_INET6, &addr, ipPresentation, INET6_ADDRSTRLEN) == nullptr) {
152 return std::string();
155 // restore the scopeId if it was originally present
156 std::string res(ipPresentation);
157 if (!scopeId.empty()) {
158 res += "%" + scopeId;
161 return res;
164 HostURL::HostURL(const std::string &hosturl, int port) :
165 m_ipv6(false), m_port(port) {
167 // Find the scheme
168 auto spos = hosturl.find("://");
169 if (spos != std::string::npos) {
170 m_hosturl = m_scheme = hosturl.substr(0, spos);
171 m_hosturl += "://";
172 spos += 3;
173 } else {
174 spos = 0;
177 // strip off /EXTRA from prot://addr:port/EXTRA
178 auto extraPos = hosturl.find('/', spos);
179 auto validLen = extraPos != std::string::npos ? extraPos : hosturl.size();
181 // IPv6 address?
182 auto bpos = hosturl.find('[');
183 if (bpos != std::string::npos) {
184 // Extract out the IPAddress from [..]
185 // Look for the ending position of ']'
186 auto epos = hosturl.rfind(']', validLen - 1);
187 if (epos == std::string::npos) {
188 // This isn't a valid IPv6 address, so bail.
189 m_valid = false;
190 m_hosturl = hosturl;
191 return;
194 // IPv6 address between '[' and ']'
195 auto v6h = normalizeIPv6Address(hosturl.substr(bpos + 1, epos - bpos - 1));
196 if (v6h.empty()) {
197 m_valid = false;
198 m_hosturl = hosturl;
199 return;
201 m_host = v6h;
202 m_hosturl += '[';
203 m_hosturl += v6h;
204 m_hosturl += ']';
206 // Colon for port. Start after ']';
207 auto cpos = hosturl.find(':', epos);
208 if (cpos != std::string::npos) {
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 if (extraPos != std::string::npos) {
217 m_hosturl += hosturl.substr(extraPos);
219 m_ipv6 = true;
220 } else if (m_scheme == "unix" || m_scheme == "udg") {
221 // unix socket
222 m_host = hosturl.substr(spos);
223 m_hosturl += m_host;
224 } else {
225 // IPv4 or hostname
226 auto cpos = hosturl.find(':', spos);
227 if (cpos != std::string::npos) {
228 m_host = hosturl.substr(spos, cpos - spos);
229 m_hosturl += m_host;
230 try {
231 auto portLen = validLen - cpos - 1;
232 m_port = folly::to<uint16_t>(hosturl.substr(cpos + 1, portLen));
233 m_hosturl += hosturl.substr(cpos);
234 } catch (...) {
235 m_port = 0;
237 } else {
238 m_host = hosturl.substr(spos, validLen - spos);
239 m_hosturl += hosturl.substr(spos);
241 m_ipv6 = false;
243 m_valid = true;
246 ///////////////////////////////////////////////////////////////////////////////