diff options
Diffstat (limited to 'ext/standard/net.c')
-rw-r--r-- | ext/standard/net.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/ext/standard/net.c b/ext/standard/net.c new file mode 100644 index 0000000000..2aee77ed6e --- /dev/null +++ b/ext/standard/net.c @@ -0,0 +1,301 @@ +/* + +----------------------------------------------------------------------+ + | 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> +#endif + +#ifdef PHP_WIN32 +# include <intrin.h> +# include <winsock2.h> +# include <ws2ipdef.h> +# include <Ws2tcpip.h> +# include <iphlpapi.h> +#else +# include <netdb.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; +} + +#if defined(PHP_WIN32) || HAVE_GETIFADDRS +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); +} +#endif + +/* {{{ 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 +} +/* }}} */ |