/src/php-src/ext/standard/net.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright (c) The PHP Group | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to version 3.01 of the PHP license, | |
6 | | | that is bundled with this package in the file LICENSE, and is | |
7 | | | available through the world-wide-web at the following url: | |
8 | | | https://www.php.net/license/3_01.txt | |
9 | | | If you did not receive a copy of the PHP license and are unable to | |
10 | | | obtain it through the world-wide-web, please send a note to | |
11 | | | license@php.net so we can mail you a copy immediately. | |
12 | | +----------------------------------------------------------------------+ |
13 | | | Authors: Sara Golemon <pollita@php.net> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | #include "php.h" |
18 | | #include "php_network.h" |
19 | | |
20 | | #ifdef HAVE_ARPA_INET_H |
21 | | # include <arpa/inet.h> |
22 | | #endif |
23 | | |
24 | | #ifdef HAVE_NET_IF_H |
25 | | # include <net/if.h> |
26 | | #endif |
27 | | |
28 | | #ifdef HAVE_GETIFADDRS |
29 | | # include <ifaddrs.h> |
30 | | #elif defined(__PASE__) |
31 | | /* IBM i implements getifaddrs, but under its own name */ |
32 | | #include <as400_protos.h> |
33 | | #define getifaddrs Qp2getifaddrs |
34 | | #define freeifaddrs Qp2freeifaddrs |
35 | | #define ifaddrs ifaddrs_pase |
36 | | #endif |
37 | | |
38 | | #ifdef PHP_WIN32 |
39 | | # ifndef __clang__ |
40 | | # include <intrin.h> |
41 | | # endif |
42 | | # include <winsock2.h> |
43 | | # include <ws2ipdef.h> |
44 | | # include <Ws2tcpip.h> |
45 | | # include <iphlpapi.h> |
46 | | #else |
47 | | # include <netdb.h> |
48 | | #endif |
49 | | |
50 | 0 | PHPAPI zend_string* php_inet_ntop(const struct sockaddr *addr) { |
51 | 0 | socklen_t addrlen = sizeof(struct sockaddr_in); |
52 | |
|
53 | 0 | if (!addr) { return NULL; } |
54 | | |
55 | 0 | switch (addr->sa_family) { |
56 | 0 | #ifdef AF_INET6 |
57 | 0 | case AF_INET6: { |
58 | 0 | zend_string *ret = zend_string_alloc(INET6_ADDRSTRLEN, 0); |
59 | 0 | if (inet_ntop(AF_INET6, &(((struct sockaddr_in6*)addr)->sin6_addr), ZSTR_VAL(ret), INET6_ADDRSTRLEN)) { |
60 | 0 | ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret)); |
61 | 0 | return ret; |
62 | 0 | } |
63 | 0 | zend_string_efree(ret); |
64 | 0 | break; |
65 | 0 | } |
66 | 0 | #endif |
67 | 0 | case AF_INET: { |
68 | 0 | zend_string *ret = zend_string_alloc(INET_ADDRSTRLEN, 0); |
69 | 0 | if (inet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), ZSTR_VAL(ret), INET_ADDRSTRLEN)) { |
70 | 0 | ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret)); |
71 | 0 | return ret; |
72 | 0 | } |
73 | 0 | zend_string_efree(ret); |
74 | 0 | break; |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | /* Fallback on getnameinfo() */ |
79 | 0 | switch (addr->sa_family) { |
80 | 0 | #ifdef AF_INET6 |
81 | 0 | case AF_INET6: |
82 | 0 | addrlen = sizeof(struct sockaddr_in6); |
83 | 0 | ZEND_FALLTHROUGH; |
84 | 0 | #endif |
85 | 0 | case AF_INET: { |
86 | 0 | zend_string *ret = zend_string_alloc(NI_MAXHOST, 0); |
87 | 0 | if (getnameinfo(addr, addrlen, ZSTR_VAL(ret), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == SUCCESS) { |
88 | | /* Also demangle numeric host with %name suffix */ |
89 | 0 | char *colon = strchr(ZSTR_VAL(ret), '%'); |
90 | 0 | if (colon) { *colon = 0; } |
91 | 0 | ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret)); |
92 | 0 | return ret; |
93 | 0 | } |
94 | 0 | zend_string_efree(ret); |
95 | 0 | break; |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | 0 | return NULL; |
100 | 0 | } |
101 | | |
102 | | #if defined(PHP_WIN32) || defined(HAVE_GETIFADDRS) || defined(__PASE__) |
103 | | static void iface_append_unicast(zval *unicast, zend_long flags, |
104 | | struct sockaddr *addr, struct sockaddr *netmask, |
105 | 0 | struct sockaddr *broadcast, struct sockaddr *ptp) { |
106 | 0 | zend_string *host; |
107 | 0 | zval u; |
108 | |
|
109 | 0 | array_init(&u); |
110 | 0 | add_assoc_long(&u, "flags", flags); |
111 | |
|
112 | 0 | if (addr) { |
113 | 0 | add_assoc_long(&u, "family", addr->sa_family); |
114 | 0 | if ((host = php_inet_ntop(addr))) { |
115 | 0 | add_assoc_str(&u, "address", host); |
116 | 0 | } |
117 | 0 | } |
118 | 0 | if ((host = php_inet_ntop(netmask))) { |
119 | 0 | add_assoc_str(&u, "netmask", host); |
120 | 0 | } |
121 | |
|
122 | 0 | if ((host = php_inet_ntop(broadcast))) { |
123 | 0 | add_assoc_str(&u, "broadcast", host); |
124 | 0 | } |
125 | |
|
126 | 0 | if ((host = php_inet_ntop(ptp))) { |
127 | 0 | add_assoc_str(&u, "ptp", host); |
128 | 0 | } |
129 | |
|
130 | 0 | add_next_index_zval(unicast, &u); |
131 | 0 | } |
132 | | |
133 | | /* {{{ Returns an array in the form: |
134 | | array( |
135 | | 'ifacename' => array( |
136 | | 'description' => 'Awesome interface', // Win32 only |
137 | | 'mac' => '00:11:22:33:44:55', // Win32 only |
138 | | 'mtu' => 1234, // Win32 only |
139 | | 'unicast' => array( |
140 | | 0 => array( |
141 | | 'family' => 2, // e.g. AF_INET, AF_INET6, AF_PACKET |
142 | | 'address' => '127.0.0.1', |
143 | | 'netmnask' => '255.0.0.0', |
144 | | 'broadcast' => '127.255.255.255', // POSIX only |
145 | | 'ptp' => '127.0.0.2', // POSIX only |
146 | | ), // etc... |
147 | | ), |
148 | | ), // etc... |
149 | | ) |
150 | | */ |
151 | 0 | PHP_FUNCTION(net_get_interfaces) { |
152 | | #ifdef PHP_WIN32 |
153 | | # define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) |
154 | | # define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) |
155 | | ULONG family = AF_UNSPEC; |
156 | | ULONG flags = GAA_FLAG_INCLUDE_PREFIX; |
157 | | PIP_ADAPTER_ADDRESSES pAddresses = NULL, p; |
158 | | PIP_ADAPTER_UNICAST_ADDRESS u = NULL; |
159 | | ULONG outBufLen = 0; |
160 | | DWORD dwRetVal = 0; |
161 | | |
162 | | ZEND_PARSE_PARAMETERS_NONE(); |
163 | | |
164 | | // Make an initial call to GetAdaptersAddresses to get the |
165 | | // size needed into the outBufLen variable |
166 | | if (GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) { |
167 | | FREE(pAddresses); |
168 | | pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen); |
169 | | } |
170 | | |
171 | | if (pAddresses == NULL) { |
172 | | zend_error(E_WARNING, "Memory allocation failed for IP_ADAPTER_ADDRESSES struct"); |
173 | | RETURN_FALSE; |
174 | | } |
175 | | |
176 | | dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); |
177 | | |
178 | | if (NO_ERROR != dwRetVal) { |
179 | | char *buf = php_win32_error_to_msg(GetLastError()); |
180 | | zend_error(E_WARNING, "GetAdaptersAddresses failed: %s", buf); |
181 | | php_win32_error_msg_free(buf); |
182 | | FREE(pAddresses); |
183 | | RETURN_FALSE; |
184 | | } |
185 | | |
186 | | array_init(return_value); |
187 | | for (p = pAddresses; p; p = p->Next) { |
188 | | zval iface, unicast; |
189 | | |
190 | | if ((IF_TYPE_ETHERNET_CSMACD != p->IfType) && (IF_TYPE_IEEE80211 != p->IfType) && (IF_TYPE_SOFTWARE_LOOPBACK != p->IfType)) { |
191 | | continue; |
192 | | } |
193 | | |
194 | | array_init(&iface); |
195 | | |
196 | | if (p->Description) { |
197 | | char tmp[256]; |
198 | | memset(tmp, 0, sizeof(tmp)); |
199 | | wcstombs(tmp, p->Description, sizeof(tmp)); |
200 | | add_assoc_string(&iface, "description", tmp); |
201 | | } |
202 | | |
203 | | if (p->PhysicalAddressLength > 0) { |
204 | | zend_string *mac = zend_string_alloc(p->PhysicalAddressLength * 3, 0); |
205 | | char *s = ZSTR_VAL(mac); |
206 | | ULONG i; |
207 | | for (i = 0; i < p->PhysicalAddressLength; ++i) { |
208 | | s += snprintf(s, 4, "%02X:", p->PhysicalAddress[i]); |
209 | | } |
210 | | *(--s) = 0; |
211 | | ZSTR_LEN(mac) = s - ZSTR_VAL(mac); |
212 | | add_assoc_str(&iface, "mac", mac); |
213 | | } |
214 | | |
215 | | /* Flags could be placed at this level, |
216 | | * but we repeat it in the unicast subarray |
217 | | * for consistency with the POSIX version. |
218 | | */ |
219 | | add_assoc_long(&iface, "mtu", p->Mtu); |
220 | | |
221 | | array_init(&unicast); |
222 | | for (u = p->FirstUnicastAddress; u; u = u->Next) { |
223 | | switch (u->Address.lpSockaddr->sa_family) { |
224 | | case AF_INET: { |
225 | | ULONG mask; |
226 | | struct sockaddr_in sin_mask; |
227 | | |
228 | | ConvertLengthToIpv4Mask(u->OnLinkPrefixLength, &mask); |
229 | | sin_mask.sin_family = AF_INET; |
230 | | sin_mask.sin_addr.s_addr = mask; |
231 | | |
232 | | iface_append_unicast(&unicast, p->Flags, |
233 | | (struct sockaddr*)u->Address.lpSockaddr, |
234 | | (struct sockaddr*)&sin_mask, NULL, NULL); |
235 | | break; |
236 | | } |
237 | | case AF_INET6: { |
238 | | ULONG i, j; |
239 | | struct sockaddr_in6 sin6_mask; |
240 | | |
241 | | memset(&sin6_mask, 0, sizeof(sin6_mask)); |
242 | | sin6_mask.sin6_family = AF_INET6; |
243 | | for (i = u->OnLinkPrefixLength, j = 0; i > 0; i -= 8, ++j) { |
244 | | sin6_mask.sin6_addr.s6_addr[j] = (i >= 8) ? 0xff : ((ULONG)((0xffU << (8 - i)) & 0xffU)); |
245 | | } |
246 | | |
247 | | iface_append_unicast(&unicast, p->Flags, |
248 | | (struct sockaddr*)u->Address.lpSockaddr, |
249 | | (struct sockaddr*)&sin6_mask, NULL, NULL); |
250 | | break; |
251 | | } |
252 | | } |
253 | | } |
254 | | add_assoc_zval(&iface, "unicast", &unicast); |
255 | | |
256 | | add_assoc_bool(&iface, "up", (p->OperStatus == IfOperStatusUp)); |
257 | | |
258 | | add_assoc_zval(return_value, p->AdapterName, &iface); |
259 | | } |
260 | | |
261 | | FREE(pAddresses); |
262 | | #undef MALLOC |
263 | | #undef FREE |
264 | | #elif defined(HAVE_GETIFADDRS) || defined(__PASE__) /* !PHP_WIN32 */ |
265 | | struct ifaddrs *addrs = NULL, *p; |
266 | |
|
267 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
268 | | |
269 | 0 | if (getifaddrs(&addrs)) { |
270 | 0 | php_error(E_WARNING, "getifaddrs() failed %d: %s", errno, strerror(errno)); |
271 | 0 | RETURN_FALSE; |
272 | 0 | } |
273 | | |
274 | 0 | array_init(return_value); |
275 | 0 | for (p = addrs; p; p = p->ifa_next) { |
276 | 0 | zval *iface = zend_hash_str_find(Z_ARR_P(return_value), p->ifa_name, strlen(p->ifa_name)); |
277 | 0 | zval *unicast, *status; |
278 | |
|
279 | 0 | if (!iface) { |
280 | 0 | zval newif; |
281 | 0 | array_init(&newif); |
282 | 0 | iface = zend_hash_str_add(Z_ARR_P(return_value), p->ifa_name, strlen(p->ifa_name), &newif); |
283 | 0 | } |
284 | |
|
285 | 0 | unicast = zend_hash_str_find(Z_ARR_P(iface), "unicast", sizeof("unicast") - 1); |
286 | 0 | if (!unicast) { |
287 | 0 | zval newuni; |
288 | 0 | array_init(&newuni); |
289 | 0 | unicast = zend_hash_str_add(Z_ARR_P(iface), "unicast", sizeof("unicast") - 1, &newuni); |
290 | 0 | } |
291 | |
|
292 | 0 | iface_append_unicast(unicast, |
293 | 0 | p->ifa_flags, |
294 | 0 | p->ifa_addr, p->ifa_netmask, |
295 | 0 | (p->ifa_flags & IFF_BROADCAST) ? p->ifa_broadaddr : NULL, |
296 | 0 | (p->ifa_flags & IFF_POINTOPOINT) ? p->ifa_dstaddr : NULL); |
297 | 0 | status = zend_hash_str_find(Z_ARR_P(iface), "up", sizeof("up") - 1); |
298 | 0 | if (!status) { |
299 | 0 | add_assoc_bool(iface, "up", ((p->ifa_flags & IFF_UP) != 0)); |
300 | 0 | } |
301 | 0 | } |
302 | |
|
303 | 0 | freeifaddrs(addrs); |
304 | | #else |
305 | | /* Should never happen as we never register the function */ |
306 | | ZEND_UNREACHABLE(); |
307 | | #endif |
308 | 0 | } |
309 | | #endif |
310 | | /* }}} */ |