net.c 9.0 KB

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