net.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 The PHP Group |
  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. +----------------------------------------------------------------------+
  15. | Authors: Sara Golemon <pollita@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "php.h"
  19. #include "php_network.h"
  20. #if HAVE_ARPA_INET_H
  21. # include <arpa/inet.h>
  22. #endif
  23. #if HAVE_NET_IF_H
  24. # include <net/if.h>
  25. #endif
  26. #if HAVE_GETIFADDRS
  27. # include <ifaddrs.h>
  28. #endif
  29. #ifdef PHP_WIN32
  30. # ifndef __clang__
  31. # include <intrin.h>
  32. # endif
  33. # include <winsock2.h>
  34. # include <ws2ipdef.h>
  35. # include <Ws2tcpip.h>
  36. # include <iphlpapi.h>
  37. #else
  38. # include <netdb.h>
  39. #endif
  40. PHPAPI zend_string* php_inet_ntop(const struct sockaddr *addr) {
  41. socklen_t addrlen = sizeof(struct sockaddr_in);
  42. if (!addr) { return NULL; }
  43. /* Prefer inet_ntop() as it's more task-specific and doesn't have to be demangled */
  44. #if HAVE_INET_NTOP
  45. switch (addr->sa_family) {
  46. #ifdef AF_INET6
  47. case AF_INET6: {
  48. zend_string *ret = zend_string_alloc(INET6_ADDRSTRLEN, 0);
  49. if (inet_ntop(AF_INET6, &(((struct sockaddr_in6*)addr)->sin6_addr), ZSTR_VAL(ret), INET6_ADDRSTRLEN)) {
  50. ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret));
  51. return ret;
  52. }
  53. zend_string_efree(ret);
  54. break;
  55. }
  56. #endif
  57. case AF_INET: {
  58. zend_string *ret = zend_string_alloc(INET_ADDRSTRLEN, 0);
  59. if (inet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), ZSTR_VAL(ret), INET_ADDRSTRLEN)) {
  60. ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret));
  61. return ret;
  62. }
  63. zend_string_efree(ret);
  64. break;
  65. }
  66. }
  67. #endif
  68. /* Fallback on getnameinfo() */
  69. switch (addr->sa_family) {
  70. #ifdef AF_INET6
  71. case AF_INET6:
  72. addrlen = sizeof(struct sockaddr_in6);
  73. /* fallthrough */
  74. #endif
  75. case AF_INET: {
  76. zend_string *ret = zend_string_alloc(NI_MAXHOST, 0);
  77. if (getnameinfo(addr, addrlen, ZSTR_VAL(ret), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == SUCCESS) {
  78. /* Also demangle numeric host with %name suffix */
  79. char *colon = strchr(ZSTR_VAL(ret), '%');
  80. if (colon) { *colon = 0; }
  81. ZSTR_LEN(ret) = strlen(ZSTR_VAL(ret));
  82. return ret;
  83. }
  84. zend_string_efree(ret);
  85. break;
  86. }
  87. }
  88. return NULL;
  89. }
  90. #if defined(PHP_WIN32) || HAVE_GETIFADDRS
  91. static void iface_append_unicast(zval *unicast, zend_long flags,
  92. struct sockaddr *addr, struct sockaddr *netmask,
  93. struct sockaddr *broadcast, struct sockaddr *ptp) {
  94. zend_string *host;
  95. zval u;
  96. array_init(&u);
  97. add_assoc_long(&u, "flags", flags);
  98. if (addr) {
  99. add_assoc_long(&u, "family", addr->sa_family);
  100. if ((host = php_inet_ntop(addr))) {
  101. add_assoc_str(&u, "address", host);
  102. }
  103. }
  104. if ((host = php_inet_ntop(netmask))) {
  105. add_assoc_str(&u, "netmask", host);
  106. }
  107. if ((host = php_inet_ntop(broadcast))) {
  108. add_assoc_str(&u, "broadcast", host);
  109. }
  110. if ((host = php_inet_ntop(ptp))) {
  111. add_assoc_str(&u, "ptp", host);
  112. }
  113. add_next_index_zval(unicast, &u);
  114. }
  115. #endif
  116. /* {{{ proto array|false net_get_interfaces()
  117. Returns an array in the form:
  118. array(
  119. 'ifacename' => array(
  120. 'description' => 'Awesome interface', // Win32 only
  121. 'mac' => '00:11:22:33:44:55', // Win32 only
  122. 'mtu' => 1234, // Win32 only
  123. 'unicast' => array(
  124. 0 => array(
  125. 'family' => 2, // e.g. AF_INET, AF_INET6, AF_PACKET
  126. 'address' => '127.0.0.1',
  127. 'netmnask' => '255.0.0.0',
  128. 'broadcast' => '127.255.255.255', // POSIX only
  129. 'ptp' => '127.0.0.2', // POSIX only
  130. ), // etc...
  131. ),
  132. ), // etc...
  133. )
  134. */
  135. PHP_FUNCTION(net_get_interfaces) {
  136. #ifdef PHP_WIN32
  137. # define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
  138. # define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
  139. ULONG family = AF_UNSPEC;
  140. ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
  141. PIP_ADAPTER_ADDRESSES pAddresses = NULL, p;
  142. PIP_ADAPTER_UNICAST_ADDRESS u = NULL;
  143. ULONG outBufLen = 0;
  144. DWORD dwRetVal = 0;
  145. ZEND_PARSE_PARAMETERS_NONE();
  146. // Make an initial call to GetAdaptersAddresses to get the
  147. // size needed into the outBufLen variable
  148. if (GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) {
  149. FREE(pAddresses);
  150. pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
  151. }
  152. if (pAddresses == NULL) {
  153. zend_error(E_WARNING, "Memory allocation failed for IP_ADAPTER_ADDRESSES struct");
  154. RETURN_FALSE;
  155. }
  156. dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
  157. if (NO_ERROR != dwRetVal) {
  158. /* TODO check GetLastError() */
  159. FREE(pAddresses);
  160. RETURN_FALSE;
  161. }
  162. array_init(return_value);
  163. for (p = pAddresses; p; p = p->Next) {
  164. zval iface, unicast;
  165. if ((IF_TYPE_ETHERNET_CSMACD != p->IfType) && (IF_TYPE_SOFTWARE_LOOPBACK != p->IfType)) {
  166. continue;
  167. }
  168. array_init(&iface);
  169. if (p->Description) {
  170. char tmp[256];
  171. memset(tmp, 0, sizeof(tmp));
  172. wcstombs(tmp, p->Description, sizeof(tmp));
  173. add_assoc_string(&iface, "description", tmp);
  174. }
  175. if (p->PhysicalAddressLength > 0) {
  176. zend_string *mac = zend_string_alloc(p->PhysicalAddressLength * 3, 0);
  177. char *s = ZSTR_VAL(mac);
  178. ULONG i;
  179. for (i = 0; i < p->PhysicalAddressLength; ++i) {
  180. s += snprintf(s, 4, "%02X:", p->PhysicalAddress[i]);
  181. }
  182. *(--s) = 0;
  183. ZSTR_LEN(mac) = s - ZSTR_VAL(mac);
  184. add_assoc_str(&iface, "mac", mac);
  185. }
  186. /* Flags could be placed at this level,
  187. * but we repeat it in the unicast subarray
  188. * for consistency with the POSIX version.
  189. */
  190. add_assoc_long(&iface, "mtu", p->Mtu);
  191. array_init(&unicast);
  192. for (u = p->FirstUnicastAddress; u; u = u->Next) {
  193. switch (u->Address.lpSockaddr->sa_family) {
  194. case AF_INET: {
  195. ULONG mask;
  196. struct sockaddr_in sin_mask;
  197. ConvertLengthToIpv4Mask(u->OnLinkPrefixLength, &mask);
  198. sin_mask.sin_family = AF_INET;
  199. sin_mask.sin_addr.s_addr = mask;
  200. iface_append_unicast(&unicast, p->Flags,
  201. (struct sockaddr*)u->Address.lpSockaddr,
  202. (struct sockaddr*)&sin_mask, NULL, NULL);
  203. break;
  204. }
  205. case AF_INET6: {
  206. ULONG i, j;
  207. struct sockaddr_in6 sin6_mask;
  208. memset(&sin6_mask, 0, sizeof(sin6_mask));
  209. sin6_mask.sin6_family = AF_INET6;
  210. for (i = u->OnLinkPrefixLength, j = 0; i > 0; i -= 8, ++j) {
  211. sin6_mask.sin6_addr.s6_addr[j] = (i >= 8) ? 0xff : ((ULONG)((0xffU << (8 - i)) & 0xffU));
  212. }
  213. iface_append_unicast(&unicast, p->Flags,
  214. (struct sockaddr*)u->Address.lpSockaddr,
  215. (struct sockaddr*)&sin6_mask, NULL, NULL);
  216. break;
  217. }
  218. }
  219. }
  220. add_assoc_zval(&iface, "unicast", &unicast);
  221. add_assoc_zval(return_value, p->AdapterName, &iface);
  222. }
  223. FREE(pAddresses);
  224. #undef MALLOC
  225. #undef FREE
  226. #elif HAVE_GETIFADDRS /* !PHP_WIN32 */
  227. struct ifaddrs *addrs = NULL, *p;
  228. ZEND_PARSE_PARAMETERS_NONE();
  229. if (getifaddrs(&addrs)) {
  230. php_error(E_WARNING, "getifaddrs() failed %d: %s", errno, strerror(errno));
  231. RETURN_FALSE;
  232. }
  233. array_init(return_value);
  234. for (p = addrs; p; p = p->ifa_next) {
  235. zval *iface = zend_hash_str_find(Z_ARR_P(return_value), p->ifa_name, strlen(p->ifa_name));
  236. zval *unicast;
  237. if (!iface) {
  238. zval newif;
  239. array_init(&newif);
  240. iface = zend_hash_str_add(Z_ARR_P(return_value), p->ifa_name, strlen(p->ifa_name), &newif);
  241. }
  242. unicast = zend_hash_str_find(Z_ARR_P(iface), "unicast", sizeof("unicast") - 1);
  243. if (!unicast) {
  244. zval newuni;
  245. array_init(&newuni);
  246. unicast = zend_hash_str_add(Z_ARR_P(iface), "unicast", sizeof("unicast") - 1, &newuni);
  247. }
  248. iface_append_unicast(unicast,
  249. p->ifa_flags,
  250. p->ifa_addr, p->ifa_netmask,
  251. (p->ifa_flags & IFF_BROADCAST) ? p->ifa_broadaddr : NULL,
  252. (p->ifa_flags & IFF_POINTOPOINT) ? p->ifa_dstaddr : NULL);
  253. }
  254. freeifaddrs(addrs);
  255. #else
  256. /* Should never happen as we never register the function */
  257. php_error(E_WARNING, "No support for net_get_interfaces");
  258. RETURN_FALSE;
  259. #endif
  260. }
  261. /* }}} */