2 +----------------------------------------------------------------------+
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"
19 #include <arpa/nameser.h>
21 #include <sys/utsname.h>
24 #include <folly/IPAddress.h>
25 #include <folly/String.h>
26 #include <folly/portability/Sockets.h>
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() {
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.
45 static ResolverLibInitializer _resolver_lib_initializer
;
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
);
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
) {
73 result
.tmphstbuf
= (char*)realloc(result
.tmphstbuf
, hstbuflen
);
79 ///////////////////////////////////////////////////////////////////////////////
80 std::string
GetPrimaryIPImpl(int af
) {
81 const static std::string s_empty
;
82 struct addrinfo hints
;
83 struct addrinfo
*res
= nullptr;
93 char* nodename
= nullptr;
95 if (gethostname(nodenamebuf
, 65) < 0)
96 nodename
= "localhost";
98 nodename
= nodenamebuf
;
101 uname((struct utsname
*)&buf
);
102 const char* nodename
= buf
.nodename
;
105 memset(&hints
, 0, sizeof(struct addrinfo
));
106 hints
.ai_family
= af
;
108 error
= getaddrinfo(nodename
, nullptr, &hints
, &res
);
114 return folly::IPAddress(res
->ai_addr
).toFullyQualified();
115 } catch (folly::IPAddressFormatException
& e
) {
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();
136 static std::string
normalizeIPv6Address(std::string address
) {
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
;
164 HostURL::HostURL(const std::string
&hosturl
, int port
) :
165 m_ipv6(false), m_port(port
) {
168 auto spos
= hosturl
.find("://");
169 if (spos
!= std::string::npos
) {
170 m_hosturl
= m_scheme
= hosturl
.substr(0, spos
);
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();
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.
194 // IPv6 address between '[' and ']'
195 auto v6h
= normalizeIPv6Address(hosturl
.substr(bpos
+ 1, epos
- bpos
- 1));
206 // Colon for port. Start after ']';
207 auto cpos
= hosturl
.find(':', epos
);
208 if (cpos
!= std::string::npos
) {
210 auto portLen
= validLen
- cpos
- 1;
211 m_port
= folly::to
<uint16_t>(hosturl
.substr(cpos
+ 1, portLen
));
212 m_hosturl
+= hosturl
.substr(cpos
);
216 } else if (extraPos
!= std::string::npos
) {
217 m_hosturl
+= hosturl
.substr(extraPos
);
220 } else if (m_scheme
== "unix" || m_scheme
== "udg") {
222 m_host
= hosturl
.substr(spos
);
226 auto cpos
= hosturl
.find(':', spos
);
227 if (cpos
!= std::string::npos
) {
228 m_host
= hosturl
.substr(spos
, cpos
- spos
);
231 auto portLen
= validLen
- cpos
- 1;
232 m_port
= folly::to
<uint16_t>(hosturl
.substr(cpos
+ 1, portLen
));
233 m_hosturl
+= hosturl
.substr(cpos
);
238 m_host
= hosturl
.substr(spos
, validLen
- spos
);
239 m_hosturl
+= hosturl
.substr(spos
);
246 ///////////////////////////////////////////////////////////////////////////////