Browse Source

Add net_get_interfaces()

pull/2946/head
Sara Golemon 8 years ago
parent
commit
7ca5a7d84e
No known key found for this signature in database GPG Key ID: DBDB397470D12172
  1. 1
      NEWS
  2. 11
      ext/standard/basic_functions.c
  3. 4
      ext/standard/basic_functions.h
  4. 22
      ext/standard/config.m4
  5. 4
      ext/standard/config.w32
  6. 298
      ext/standard/net.c
  7. 31
      ext/standard/php_net.h
  8. 30
      ext/standard/tests/network/net_get_interfaces_001.phpt

1
NEWS

@ -8,6 +8,7 @@ PHP NEWS
thrus making it work on Windows out of the box. (Kalle)
. Removed support for BeOS. (Kalle)
. Add PHP_VERSION to phpinfo() <title/>. (github/MattJeevas)
. Add net_get_interfaces(). (Sara, Joe, Anatol)
. Fixed bug #75031 (support append mode in temp/memory streams). (adsr)
. Fixed bug #74860 (Uncaught exceptions not being formatted properly when
error_log set to "syslog"). (Philip Prindeville)

11
ext/standard/basic_functions.c

@ -962,6 +962,9 @@ ZEND_BEGIN_ARG_INFO(arginfo_gethostname, 0)
ZEND_END_ARG_INFO()
#endif
ZEND_BEGIN_ARG_INFO(arginfo_net_get_interfaces, 0)
ZEND_END_ARG_INFO()
#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC
ZEND_BEGIN_ARG_INFO_EX(arginfo_dns_check_record, 0, 0, 1)
ZEND_ARG_INFO(0, host)
@ -2955,7 +2958,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FE(fmod, arginfo_fmod)
PHP_FE(intdiv, arginfo_intdiv)
#ifdef HAVE_INET_NTOP
PHP_RAW_NAMED_FE(inet_ntop, php_inet_ntop, arginfo_inet_ntop)
PHP_RAW_NAMED_FE(inet_ntop, zif_inet_ntop, arginfo_inet_ntop)
#endif
#ifdef HAVE_INET_PTON
PHP_RAW_NAMED_FE(inet_pton, php_inet_pton, arginfo_inet_pton)
@ -3060,6 +3063,10 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FE(gethostname, arginfo_gethostname)
#endif
#if defined(PHP_WIN32) || HAVE_GETIFADDRS
PHP_FE(net_get_interfaces, arginfo_net_get_interfaces)
#endif
#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC
PHP_FE(dns_check_record, arginfo_dns_check_record)
@ -3897,7 +3904,7 @@ PHP_FUNCTION(constant)
#ifdef HAVE_INET_NTOP
/* {{{ proto string inet_ntop(string in_addr)
Converts a packed inet address to a human readable IP address string */
PHP_NAMED_FUNCTION(php_inet_ntop)
PHP_NAMED_FUNCTION(zif_inet_ntop)
{
char *address;
size_t address_len;

4
ext/standard/basic_functions.h

@ -56,7 +56,7 @@ PHP_FUNCTION(time_sleep_until);
#endif
PHP_FUNCTION(flush);
#ifdef HAVE_INET_NTOP
PHP_NAMED_FUNCTION(php_inet_ntop);
PHP_NAMED_FUNCTION(zif_inet_ntop);
#endif
#ifdef HAVE_INET_PTON
PHP_NAMED_FUNCTION(php_inet_pton);
@ -126,6 +126,8 @@ PHP_FUNCTION(sys_getloadavg);
PHP_FUNCTION(is_uploaded_file);
PHP_FUNCTION(move_uploaded_file);
PHP_FUNCTION(net_get_interfaces);
/* From the INI parser */
PHP_FUNCTION(parse_ini_file);
PHP_FUNCTION(parse_ini_string);

22
ext/standard/config.m4

@ -448,6 +448,26 @@ if test "$PHP_PASSWORD_ARGON2" != "no"; then
])
fi
dnl
dnl net_get_interfaces
dnl
AC_CHECK_HEADERS([net/if.h netdb.h])
AC_MSG_CHECKING([for usable getifaddrs])
AC_TRY_LINK([
#include <ifaddrs.h>
],[
struct ifaddrs *interfaces;
if (!getifaddrs(&interfaces)) {
freeifaddrs(interfaces);
}
], [ac_have_getifaddrs=yes], [ac_have_getifaddrs=no])
if test "$ac_have_getifaddrs" = "yes" ; then
AC_DEFINE(HAVE_GETIFADDRS, 1, [whether getifaddrs is present and usable])
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
dnl
dnl Setup extension sources
dnl
@ -462,7 +482,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \
var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \
filters.c proc_open.c streamsfuncs.c http.c password.c \
random.c,,,
random.c net.c,,,
-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_ADD_MAKEFILE_FRAGMENT

4
ext/standard/config.w32

@ -22,13 +22,15 @@ AC_DEFINE("PHP_USE_PHP_CRYPT_R", 1);
CHECK_HEADER_ADD_INCLUDE("timelib_config.h", "CFLAGS_STANDARD", "ext/date/lib");
ADD_FLAG("LIBS_STANDARD", "iphlpapi.lib");
EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \
crc32.c crypt.c crypt_freesec.c crypt_blowfish.c crypt_sha256.c \
crypt_sha512.c php_crypt_r.c \
cyr_convert.c datetime.c dir.c dl.c dns.c dns_win32.c exec.c \
file.c filestat.c formatted_print.c fsock.c head.c html.c image.c \
info.c iptc.c lcg.c link_win32.c mail.c math.c md5.c metaphone.c microtime.c \
pack.c pageinfo.c quot_print.c rand.c mt_rand.c soundex.c \
net.c pack.c pageinfo.c quot_print.c rand.c mt_rand.c soundex.c \
string.c scanf.c syslog.c type.c uniqid.c url.c var.c \
versioning.c assert.c strnatcmp.c levenshtein.c incomplete_class.c \
url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \

298
ext/standard/net.c

@ -0,0 +1,298 @@
/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2017 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Sara Golemon <pollita@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "php_network.h"
#if HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#if HAVE_NET_IF_H
# include <net/if.h>
#endif
#if HAVE_GETIFADDRS
# include <ifaddrs.h>
# include <netdb.h>
#endif
#ifdef PHP_WIN32
# include <intrin.h>
# include <winsock2.h>
# include <ws2ipdef.h>
# include <Ws2tcpip.h>
# include <iphlpapi.h>
#endif
PHPAPI zend_string* php_inet_ntop(const struct sockaddr *addr) {
socklen_t addrlen = sizeof(struct sockaddr_in);
if (!addr) { return NULL; }
/* Prefer inet_ntop() as it's more task-specific and doesn't have to be demangled */
#if HAVE_INET_NTOP
switch (addr->sa_family) {
#ifdef AF_INET6
case AF_INET6: {
zend_string *ret = zend_string_alloc(INET6_ADDRSTRLEN, 0);
if (inet_ntop(AF_INET6, &(((struct sockaddr_in6*)addr)->sin6_addr), ZSTR_VAL(ret), INET6_ADDRSTRLEN)) {
ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret));
return ret;
}
zend_string_free(ret);
break;
}
#endif
case AF_INET: {
zend_string *ret = zend_string_alloc(INET_ADDRSTRLEN, 0);
if (inet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), ZSTR_VAL(ret), INET_ADDRSTRLEN)) {
ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret));
return ret;
}
zend_string_free(ret);
break;
}
}
#endif
/* Fallback on getnameinfo() */
switch (addr->sa_family) {
#ifdef AF_INET6
case AF_INET6:
addrlen = sizeof(struct sockaddr_in6);
/* fallthrough */
#endif
case AF_INET: {
zend_string *ret = zend_string_alloc(NI_MAXHOST, 0);
if (getnameinfo(addr, addrlen, ZSTR_VAL(ret), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == SUCCESS) {
/* Also demangle numeric host with %name suffix */
char *colon = strchr(ZSTR_VAL(ret), '%');
if (colon) { *colon = 0; }
ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret));
return ret;
}
zend_string_free(ret);
break;
}
}
return NULL;
}
static void iface_append_unicast(zval *unicast, zend_long flags,
struct sockaddr *addr, struct sockaddr *netmask,
struct sockaddr *broadcast, struct sockaddr *ptp) {
zend_string *host;
zval u;
array_init(&u);
add_assoc_long(&u, "flags", flags);
if (addr) {
add_assoc_long(&u, "family", addr->sa_family);
if ((host = php_inet_ntop(addr))) {
add_assoc_str(&u, "address", host);
}
}
if ((host = php_inet_ntop(netmask))) {
add_assoc_str(&u, "netmask", host);
}
if ((host = php_inet_ntop(broadcast))) {
add_assoc_str(&u, "broadcast", host);
}
if ((host = php_inet_ntop(ptp))) {
add_assoc_str(&u, "ptp", host);
}
add_next_index_zval(unicast, &u);
}
/* {{{ proto array|false net_get_interfaces()
Returns an array in the form:
array(
'ifacename' => array(
'description' => 'Awesome interface', // Win32 only
'mac' => '00:11:22:33:44:55', // Win32 only
'mtu' => 1234, // Win32 only
'unicast' => array(
0 => array(
'family' => 2, // e.g. AF_INET, AF_INET6, AF_PACKET
'address' => '127.0.0.1',
'netmnask' => '255.0.0.0',
'broadcast' => '127.255.255.255', // POSIX only
'ptp' => '127.0.0.2', // POSIX only
), // etc...
),
), // etc...
)
*/
PHP_FUNCTION(net_get_interfaces) {
#ifdef PHP_WIN32
# define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
# define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
ULONG family = AF_UNSPEC;
ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
PIP_ADAPTER_ADDRESSES pAddresses = NULL, p;
PIP_ADAPTER_UNICAST_ADDRESS u = NULL;
ULONG outBufLen = 0;
DWORD dwRetVal = 0;
ZEND_PARSE_PARAMETERS_NONE();
// Make an initial call to GetAdaptersAddresses to get the
// size needed into the outBufLen variable
if (GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) {
FREE(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
}
if (pAddresses == NULL) {
zend_error(E_WARNING, "Memory allocation failed for IP_ADAPTER_ADDRESSES struct");
RETURN_FALSE;
}
dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
if (NO_ERROR != dwRetVal) {
/* TODO check GetLastError() */
FREE(pAddresses);
RETURN_FALSE;
}
array_init(return_value);
for (p = pAddresses; p; p = p->Next) {
zval iface, unicast;
if ((IF_TYPE_ETHERNET_CSMACD != p->IfType) && (IF_TYPE_SOFTWARE_LOOPBACK != p->IfType)) {
continue;
}
array_init(&iface);
if (p->Description) {
char tmp[256];
memset(tmp, 0, sizeof(tmp));
wcstombs(tmp, p->Description, sizeof(tmp));
add_assoc_string(&iface, "description", tmp);
}
if (p->PhysicalAddressLength > 0) {
zend_string *mac = zend_string_alloc(p->PhysicalAddressLength * 3, 0);
char *s = ZSTR_VAL(mac);
ULONG i;
for (i = 0; i < p->PhysicalAddressLength; ++i) {
s += snprintf(s, 4, "%02X:", p->PhysicalAddress[i]);
}
*(--s) = 0;
ZSTR_LEN(mac) = s - ZSTR_VAL(mac);
add_assoc_str(&iface, "mac", mac);
}
/* Flags could be placed at this level,
* but we repeat it in the unicast subarray
* for consistency with the POSIX version.
*/
add_assoc_long(&iface, "mtu", p->Mtu);
array_init(&unicast);
for (u = p->FirstUnicastAddress; u; u = u->Next) {
switch (u->Address.lpSockaddr->sa_family) {
case AF_INET: {
ULONG mask;
struct sockaddr_in sin_mask;
ConvertLengthToIpv4Mask(u->OnLinkPrefixLength, &mask);
sin_mask.sin_family = AF_INET;
sin_mask.sin_addr.s_addr = mask;
iface_append_unicast(&unicast, p->Flags,
(struct sockaddr*)u->Address.lpSockaddr,
(struct sockaddr*)&sin_mask, NULL, NULL);
break;
}
case AF_INET6: {
ULONG i, j;
struct sockaddr_in6 sin6_mask;
memset(&sin6_mask, 0, sizeof(sin6_mask));
sin6_mask.sin6_family = AF_INET6;
for (i = u->OnLinkPrefixLength, j = 0; i > 0; i -= 8, ++j) {
sin6_mask.sin6_addr.s6_addr[j] = (i >= 8) ? 0xff : ((ULONG)((0xffU << (8 - i)) & 0xffU));
}
iface_append_unicast(&unicast, p->Flags,
(struct sockaddr*)u->Address.lpSockaddr,
(struct sockaddr*)&sin6_mask, NULL, NULL);
break;
}
}
}
add_assoc_zval(&iface, "unicast", &unicast);
add_assoc_zval(return_value, p->AdapterName, &iface);
}
FREE(pAddresses);
#undef MALLOC
#undef FREE
#elif HAVE_GETIFADDRS /* !PHP_WIN32 */
struct ifaddrs *addrs = NULL, *p;
ZEND_PARSE_PARAMETERS_NONE();
if (getifaddrs(&addrs)) {
php_error(E_WARNING, "getifaddrs() failed %d: %s", errno, strerror(errno));
RETURN_FALSE;
}
array_init(return_value);
for (p = addrs; p; p = p->ifa_next) {
zval *iface = zend_hash_str_find(Z_ARR_P(return_value), p->ifa_name, strlen(p->ifa_name));
zval *unicast;
if (!iface) {
zval newif;
array_init(&newif);
iface = zend_hash_str_add(Z_ARR_P(return_value), p->ifa_name, strlen(p->ifa_name), &newif);
}
unicast = zend_hash_str_find(Z_ARR_P(iface), "unicast", sizeof("unicast") - 1);
if (!unicast) {
zval newuni;
array_init(&newuni);
unicast = zend_hash_str_add(Z_ARR_P(iface), "unicast", sizeof("unicast") - 1, &newuni);
}
iface_append_unicast(unicast,
p->ifa_flags,
p->ifa_addr, p->ifa_netmask,
(p->ifa_flags & IFF_BROADCAST) ? p->ifa_broadaddr : NULL,
(p->ifa_flags & IFF_POINTOPOINT) ? p->ifa_dstaddr : NULL);
}
freeifaddrs(addrs);
#else
/* Should never happen as we never register the function */
php_error(E_WARNING, "No support for net_get_interfaces");
RETURN_FALSE;
#endif
}
/* }}} */

31
ext/standard/php_net.h

@ -0,0 +1,31 @@
/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2017 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Sara Golemon <pollita@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifndef PHP_NET_H
#define PHP_NET_H
#include "php.h"
#include "php_network.h"
PHPAPI zend_string* php_inet_ntop(const struct sockaddr *addr);
PHP_FUNCTION(net_get_interfaces);
#endif /* PHP_NET_H */

30
ext/standard/tests/network/net_get_interfaces_001.phpt

@ -0,0 +1,30 @@
--TEST--
net_get_interfaces IPv4 Loopback
--SKIPIF--
<?php
function_exists('net_get_interfaces') || print 'skip';
--FILE--
<?php
// Test that we have exactly one unicast address with the address 127.0.0.1
// On linux this will often be named "lo", but even that isn't guaranteed.
$ifaces = net_get_interfaces();
$found = false;
foreach ($ifaces as $iface) {
foreach ($iface['unicast'] as $unicast) {
if (($unicast['address'] ?? null) === '127.0.0.1') {
$found = true;
break 2;
}
}
}
var_dump($found);
if (!$found) {
// Extra diagnostics!
var_dump($ifaces);
}
--EXPECT--
bool(true)
Loading…
Cancel
Save