multicast.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  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: Gustavo Lopes <cataphract@php.net> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #ifdef HAVE_CONFIG_H
  17. #include "config.h"
  18. #endif
  19. #include "php.h"
  20. #include "php_network.h"
  21. #ifdef PHP_WIN32
  22. # include "windows_common.h"
  23. #else
  24. #include <sys/socket.h>
  25. #include <sys/ioctl.h>
  26. #include <net/if.h>
  27. #ifdef HAVE_SYS_SOCKIO_H
  28. #include <sys/sockio.h>
  29. #endif
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #endif
  33. #include "php_sockets.h"
  34. #include "multicast.h"
  35. #include "sockaddr_conv.h"
  36. #include "main/php_network.h"
  37. enum source_op {
  38. JOIN_SOURCE,
  39. LEAVE_SOURCE,
  40. BLOCK_SOURCE,
  41. UNBLOCK_SOURCE
  42. };
  43. static int _php_mcast_join_leave(php_socket *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index, int join);
  44. #ifdef HAS_MCAST_EXT
  45. static int _php_mcast_source_op(php_socket *sock, int level, struct sockaddr *group, socklen_t group_len, struct sockaddr *source, socklen_t source_len, unsigned int if_index, enum source_op sop);
  46. #endif
  47. #ifdef RFC3678_API
  48. static int _php_source_op_to_rfc3678_op(enum source_op sop);
  49. #elif HAS_MCAST_EXT
  50. static const char *_php_source_op_to_string(enum source_op sop);
  51. static int _php_source_op_to_ipv4_op(enum source_op sop);
  52. #endif
  53. int php_string_to_if_index(const char *val, unsigned *out)
  54. {
  55. #if HAVE_IF_NAMETOINDEX
  56. unsigned int ind;
  57. ind = if_nametoindex(val);
  58. if (ind == 0) {
  59. php_error_docref(NULL, E_WARNING,
  60. "No interface with name \"%s\" could be found", val);
  61. return FAILURE;
  62. } else {
  63. *out = ind;
  64. return SUCCESS;
  65. }
  66. #else
  67. php_error_docref(NULL, E_WARNING,
  68. "This platform does not support looking up an interface by "
  69. "name, an integer interface index must be supplied instead");
  70. return FAILURE;
  71. #endif
  72. }
  73. static int php_get_if_index_from_zval(zval *val, unsigned *out)
  74. {
  75. int ret;
  76. if (Z_TYPE_P(val) == IS_LONG) {
  77. if (Z_LVAL_P(val) < 0 || (zend_ulong)Z_LVAL_P(val) > UINT_MAX) {
  78. zend_value_error("Index must be between 0 and %u", UINT_MAX);
  79. return FAILURE;
  80. }
  81. *out = Z_LVAL_P(val);
  82. ret = SUCCESS;
  83. } else {
  84. zend_string *tmp_str;
  85. zend_string *str = zval_get_tmp_string(val, &tmp_str);
  86. ret = php_string_to_if_index(ZSTR_VAL(str), out);
  87. zend_tmp_string_release(tmp_str);
  88. }
  89. return ret;
  90. }
  91. static int php_get_if_index_from_array(const HashTable *ht, const char *key,
  92. php_socket *sock, unsigned int *if_index)
  93. {
  94. zval *val;
  95. if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {
  96. *if_index = 0; /* default: 0 */
  97. return SUCCESS;
  98. }
  99. return php_get_if_index_from_zval(val, if_index);
  100. }
  101. static int php_get_address_from_array(const HashTable *ht, const char *key,
  102. php_socket *sock, php_sockaddr_storage *ss, socklen_t *ss_len)
  103. {
  104. zval *val;
  105. zend_string *str, *tmp_str;
  106. if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {
  107. zend_value_error("No key \"%s\" passed in optval", key);
  108. return FAILURE;
  109. }
  110. str = zval_get_tmp_string(val, &tmp_str);
  111. if (!php_set_inet46_addr(ss, ss_len, ZSTR_VAL(str), sock)) {
  112. zend_tmp_string_release(tmp_str);
  113. return FAILURE;
  114. }
  115. zend_tmp_string_release(tmp_str);
  116. return SUCCESS;
  117. }
  118. static int php_do_mcast_opt(php_socket *php_sock, int level, int optname, zval *arg4)
  119. {
  120. HashTable *opt_ht;
  121. unsigned int if_index;
  122. int retval;
  123. int (*mcast_req_fun)(php_socket *, int, struct sockaddr *, socklen_t,
  124. unsigned);
  125. #ifdef HAS_MCAST_EXT
  126. int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
  127. struct sockaddr *, socklen_t, unsigned);
  128. #endif
  129. switch (optname) {
  130. case PHP_MCAST_JOIN_GROUP:
  131. mcast_req_fun = &php_mcast_join;
  132. goto mcast_req_fun;
  133. case PHP_MCAST_LEAVE_GROUP:
  134. {
  135. mcast_req_fun = &php_mcast_leave;
  136. mcast_req_fun: ;
  137. php_sockaddr_storage group = {0};
  138. socklen_t glen;
  139. convert_to_array(arg4);
  140. opt_ht = Z_ARRVAL_P(arg4);
  141. if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
  142. &glen) == FAILURE) {
  143. return FAILURE;
  144. }
  145. if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
  146. &if_index) == FAILURE) {
  147. return FAILURE;
  148. }
  149. retval = mcast_req_fun(php_sock, level, (struct sockaddr*)&group,
  150. glen, if_index);
  151. break;
  152. }
  153. #ifdef HAS_MCAST_EXT
  154. case PHP_MCAST_BLOCK_SOURCE:
  155. mcast_sreq_fun = &php_mcast_block_source;
  156. goto mcast_sreq_fun;
  157. case PHP_MCAST_UNBLOCK_SOURCE:
  158. mcast_sreq_fun = &php_mcast_unblock_source;
  159. goto mcast_sreq_fun;
  160. case PHP_MCAST_JOIN_SOURCE_GROUP:
  161. mcast_sreq_fun = &php_mcast_join_source;
  162. goto mcast_sreq_fun;
  163. case PHP_MCAST_LEAVE_SOURCE_GROUP:
  164. {
  165. mcast_sreq_fun = &php_mcast_leave_source;
  166. mcast_sreq_fun: ;
  167. php_sockaddr_storage group = {0},
  168. source = {0};
  169. socklen_t glen,
  170. slen;
  171. convert_to_array(arg4);
  172. opt_ht = Z_ARRVAL_P(arg4);
  173. if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
  174. &glen) == FAILURE) {
  175. return FAILURE;
  176. }
  177. if (php_get_address_from_array(opt_ht, "source", php_sock, &source,
  178. &slen) == FAILURE) {
  179. return FAILURE;
  180. }
  181. if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
  182. &if_index) == FAILURE) {
  183. return FAILURE;
  184. }
  185. retval = mcast_sreq_fun(php_sock, level, (struct sockaddr*)&group,
  186. glen, (struct sockaddr*)&source, slen, if_index);
  187. break;
  188. }
  189. #endif
  190. default:
  191. php_error_docref(NULL, E_WARNING,
  192. "Unexpected option in php_do_mcast_opt (level %d, option %d). "
  193. "This is a bug.", level, optname);
  194. return FAILURE;
  195. }
  196. if (retval != 0) {
  197. if (retval != -2) { /* error, but message already emitted */
  198. PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
  199. }
  200. return FAILURE;
  201. }
  202. return SUCCESS;
  203. }
  204. int php_do_setsockopt_ip_mcast(php_socket *php_sock,
  205. int level,
  206. int optname,
  207. zval *arg4)
  208. {
  209. unsigned int if_index;
  210. struct in_addr if_addr;
  211. void *opt_ptr;
  212. socklen_t optlen;
  213. unsigned char ipv4_mcast_ttl_lback;
  214. int retval;
  215. switch (optname) {
  216. case PHP_MCAST_JOIN_GROUP:
  217. case PHP_MCAST_LEAVE_GROUP:
  218. #ifdef HAS_MCAST_EXT
  219. case PHP_MCAST_BLOCK_SOURCE:
  220. case PHP_MCAST_UNBLOCK_SOURCE:
  221. case PHP_MCAST_JOIN_SOURCE_GROUP:
  222. case PHP_MCAST_LEAVE_SOURCE_GROUP:
  223. #endif
  224. if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {
  225. return FAILURE;
  226. } else {
  227. return SUCCESS;
  228. }
  229. case IP_MULTICAST_IF:
  230. if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {
  231. return FAILURE;
  232. }
  233. if (php_if_index_to_addr4(if_index, php_sock, &if_addr) == FAILURE) {
  234. return FAILURE;
  235. }
  236. opt_ptr = &if_addr;
  237. optlen = sizeof(if_addr);
  238. goto dosockopt;
  239. case IP_MULTICAST_LOOP:
  240. convert_to_boolean(arg4);
  241. ipv4_mcast_ttl_lback = (unsigned char) (Z_TYPE_P(arg4) == IS_TRUE);
  242. goto ipv4_loop_ttl;
  243. case IP_MULTICAST_TTL:
  244. convert_to_long(arg4);
  245. if (Z_LVAL_P(arg4) < 0L || Z_LVAL_P(arg4) > 255L) {
  246. zend_argument_value_error(4, "must be between 0 and 255");
  247. return FAILURE;
  248. }
  249. ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_P(arg4);
  250. ipv4_loop_ttl:
  251. opt_ptr = &ipv4_mcast_ttl_lback;
  252. optlen = sizeof(ipv4_mcast_ttl_lback);
  253. goto dosockopt;
  254. }
  255. return 1;
  256. dosockopt:
  257. retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
  258. if (retval != 0) {
  259. PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
  260. return FAILURE;
  261. }
  262. return SUCCESS;
  263. }
  264. int php_do_setsockopt_ipv6_mcast(php_socket *php_sock,
  265. int level,
  266. int optname,
  267. zval *arg4)
  268. {
  269. unsigned int if_index;
  270. void *opt_ptr;
  271. socklen_t optlen;
  272. int ov;
  273. int retval;
  274. switch (optname) {
  275. case PHP_MCAST_JOIN_GROUP:
  276. case PHP_MCAST_LEAVE_GROUP:
  277. #ifdef HAS_MCAST_EXT
  278. case PHP_MCAST_BLOCK_SOURCE:
  279. case PHP_MCAST_UNBLOCK_SOURCE:
  280. case PHP_MCAST_JOIN_SOURCE_GROUP:
  281. case PHP_MCAST_LEAVE_SOURCE_GROUP:
  282. #endif
  283. if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {
  284. return FAILURE;
  285. } else {
  286. return SUCCESS;
  287. }
  288. case IPV6_MULTICAST_IF:
  289. if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {
  290. return FAILURE;
  291. }
  292. opt_ptr = &if_index;
  293. optlen = sizeof(if_index);
  294. goto dosockopt;
  295. case IPV6_MULTICAST_LOOP:
  296. convert_to_boolean(arg4);
  297. ov = (int) Z_TYPE_P(arg4) == IS_TRUE;
  298. goto ipv6_loop_hops;
  299. case IPV6_MULTICAST_HOPS:
  300. convert_to_long(arg4);
  301. if (Z_LVAL_P(arg4) < -1L || Z_LVAL_P(arg4) > 255L) {
  302. zend_argument_value_error(4, "must be between -1 and 255");
  303. return FAILURE;
  304. }
  305. ov = (int) Z_LVAL_P(arg4);
  306. ipv6_loop_hops:
  307. opt_ptr = &ov;
  308. optlen = sizeof(ov);
  309. goto dosockopt;
  310. }
  311. return 1; /* not handled */
  312. dosockopt:
  313. retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
  314. if (retval != 0) {
  315. PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
  316. return FAILURE;
  317. }
  318. return SUCCESS;
  319. }
  320. int php_mcast_join(
  321. php_socket *sock,
  322. int level,
  323. struct sockaddr *group,
  324. socklen_t group_len,
  325. unsigned int if_index)
  326. {
  327. return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1);
  328. }
  329. int php_mcast_leave(
  330. php_socket *sock,
  331. int level,
  332. struct sockaddr *group,
  333. socklen_t group_len,
  334. unsigned int if_index)
  335. {
  336. return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0);
  337. }
  338. #ifdef HAS_MCAST_EXT
  339. int php_mcast_join_source(
  340. php_socket *sock,
  341. int level,
  342. struct sockaddr *group,
  343. socklen_t group_len,
  344. struct sockaddr *source,
  345. socklen_t source_len,
  346. unsigned int if_index)
  347. {
  348. return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE);
  349. }
  350. int php_mcast_leave_source(
  351. php_socket *sock,
  352. int level,
  353. struct sockaddr *group,
  354. socklen_t group_len,
  355. struct sockaddr *source,
  356. socklen_t source_len,
  357. unsigned int if_index)
  358. {
  359. return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE);
  360. }
  361. int php_mcast_block_source(
  362. php_socket *sock,
  363. int level,
  364. struct sockaddr *group,
  365. socklen_t group_len,
  366. struct sockaddr *source,
  367. socklen_t source_len,
  368. unsigned int if_index)
  369. {
  370. return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE);
  371. }
  372. int php_mcast_unblock_source(
  373. php_socket *sock,
  374. int level,
  375. struct sockaddr *group,
  376. socklen_t group_len,
  377. struct sockaddr *source,
  378. socklen_t source_len,
  379. unsigned int if_index)
  380. {
  381. return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE);
  382. }
  383. #endif /* HAS_MCAST_EXT */
  384. static int _php_mcast_join_leave(
  385. php_socket *sock,
  386. int level,
  387. struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */
  388. socklen_t group_len,
  389. unsigned int if_index,
  390. int join)
  391. {
  392. #ifdef RFC3678_API
  393. struct group_req greq = {0};
  394. memcpy(&greq.gr_group, group, group_len);
  395. assert(greq.gr_group.ss_family != 0); /* the caller has set this */
  396. greq.gr_interface = if_index;
  397. return setsockopt(sock->bsd_socket, level,
  398. join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char*)&greq,
  399. sizeof(greq));
  400. #else
  401. if (sock->type == AF_INET) {
  402. struct ip_mreq mreq;
  403. struct in_addr addr;
  404. memset(&mreq, 0, sizeof(struct ip_mreq));
  405. assert(group_len == sizeof(struct sockaddr_in));
  406. if (if_index != 0) {
  407. if (php_if_index_to_addr4(if_index, sock, &addr) ==
  408. FAILURE)
  409. return -2; /* failure, but notice already emitted */
  410. mreq.imr_interface = addr;
  411. } else {
  412. mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  413. }
  414. mreq.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
  415. return setsockopt(sock->bsd_socket, level,
  416. join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char*)&mreq,
  417. sizeof(mreq));
  418. }
  419. #if HAVE_IPV6
  420. else if (sock->type == AF_INET6) {
  421. struct ipv6_mreq mreq;
  422. memset(&mreq, 0, sizeof(struct ipv6_mreq));
  423. assert(group_len == sizeof(struct sockaddr_in6));
  424. mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)group)->sin6_addr;
  425. mreq.ipv6mr_interface = if_index;
  426. return setsockopt(sock->bsd_socket, level,
  427. join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char*)&mreq,
  428. sizeof(mreq));
  429. }
  430. #endif
  431. else {
  432. zend_value_error("Option %s is inapplicable to this socket type",
  433. join ? "MCAST_JOIN_GROUP" : "MCAST_LEAVE_GROUP");
  434. return -2;
  435. }
  436. #endif
  437. }
  438. #ifdef HAS_MCAST_EXT
  439. static int _php_mcast_source_op(
  440. php_socket *sock,
  441. int level,
  442. struct sockaddr *group,
  443. socklen_t group_len,
  444. struct sockaddr *source,
  445. socklen_t source_len,
  446. unsigned int if_index,
  447. enum source_op sop)
  448. {
  449. #ifdef RFC3678_API
  450. struct group_source_req gsreq = {0};
  451. memcpy(&gsreq.gsr_group, group, group_len);
  452. assert(gsreq.gsr_group.ss_family != 0);
  453. memcpy(&gsreq.gsr_source, source, source_len);
  454. assert(gsreq.gsr_source.ss_family != 0);
  455. gsreq.gsr_interface = if_index;
  456. return setsockopt(sock->bsd_socket, level,
  457. _php_source_op_to_rfc3678_op(sop), (char*)&gsreq, sizeof(gsreq));
  458. #else
  459. if (sock->type == AF_INET) {
  460. struct ip_mreq_source mreqs = {0};
  461. struct in_addr addr;
  462. mreqs.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
  463. mreqs.imr_sourceaddr = ((struct sockaddr_in*)source)->sin_addr;
  464. assert(group_len == sizeof(struct sockaddr_in));
  465. assert(source_len == sizeof(struct sockaddr_in));
  466. if (if_index != 0) {
  467. if (php_if_index_to_addr4(if_index, sock, &addr) ==
  468. FAILURE)
  469. return -2; /* failure, but notice already emitted */
  470. mreqs.imr_interface = addr;
  471. } else {
  472. mreqs.imr_interface.s_addr = htonl(INADDR_ANY);
  473. }
  474. return setsockopt(sock->bsd_socket, level,
  475. _php_source_op_to_ipv4_op(sop), (char*)&mreqs, sizeof(mreqs));
  476. }
  477. #if HAVE_IPV6
  478. else if (sock->type == AF_INET6) {
  479. php_error_docref(NULL, E_WARNING,
  480. "This platform does not support %s for IPv6 sockets",
  481. _php_source_op_to_string(sop));
  482. return -2;
  483. }
  484. #endif
  485. else {
  486. php_error_docref(NULL, E_WARNING,
  487. "Option %s is inapplicable to this socket type",
  488. _php_source_op_to_string(sop));
  489. return -2;
  490. }
  491. #endif
  492. }
  493. #if RFC3678_API
  494. static int _php_source_op_to_rfc3678_op(enum source_op sop)
  495. {
  496. switch (sop) {
  497. case JOIN_SOURCE:
  498. return MCAST_JOIN_SOURCE_GROUP;
  499. case LEAVE_SOURCE:
  500. return MCAST_LEAVE_SOURCE_GROUP;
  501. case BLOCK_SOURCE:
  502. return MCAST_BLOCK_SOURCE;
  503. case UNBLOCK_SOURCE:
  504. return MCAST_UNBLOCK_SOURCE;
  505. }
  506. assert(0);
  507. return 0;
  508. }
  509. #else
  510. static const char *_php_source_op_to_string(enum source_op sop)
  511. {
  512. switch (sop) {
  513. case JOIN_SOURCE:
  514. return "MCAST_JOIN_SOURCE_GROUP";
  515. case LEAVE_SOURCE:
  516. return "MCAST_LEAVE_SOURCE_GROUP";
  517. case BLOCK_SOURCE:
  518. return "MCAST_BLOCK_SOURCE";
  519. case UNBLOCK_SOURCE:
  520. return "MCAST_UNBLOCK_SOURCE";
  521. }
  522. assert(0);
  523. return "";
  524. }
  525. static int _php_source_op_to_ipv4_op(enum source_op sop)
  526. {
  527. switch (sop) {
  528. case JOIN_SOURCE:
  529. return IP_ADD_SOURCE_MEMBERSHIP;
  530. case LEAVE_SOURCE:
  531. return IP_DROP_SOURCE_MEMBERSHIP;
  532. case BLOCK_SOURCE:
  533. return IP_BLOCK_SOURCE;
  534. case UNBLOCK_SOURCE:
  535. return IP_UNBLOCK_SOURCE;
  536. }
  537. assert(0);
  538. return 0;
  539. }
  540. #endif
  541. #endif /* HAS_MCAST_EXT */
  542. #ifdef PHP_WIN32
  543. int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr)
  544. {
  545. MIB_IPADDRTABLE *addr_table;
  546. ULONG size;
  547. DWORD retval;
  548. DWORD i;
  549. (void) php_sock; /* not necessary */
  550. if (if_index == 0) {
  551. out_addr->s_addr = INADDR_ANY;
  552. return SUCCESS;
  553. }
  554. size = 4 * (sizeof *addr_table);
  555. addr_table = emalloc(size);
  556. retry:
  557. retval = GetIpAddrTable(addr_table, &size, 0);
  558. if (retval == ERROR_INSUFFICIENT_BUFFER) {
  559. efree(addr_table);
  560. addr_table = emalloc(size);
  561. goto retry;
  562. }
  563. if (retval != NO_ERROR) {
  564. efree(addr_table);
  565. php_error_docref(NULL, E_WARNING,
  566. "GetIpAddrTable failed with error %lu", retval);
  567. return FAILURE;
  568. }
  569. for (i = 0; i < addr_table->dwNumEntries; i++) {
  570. MIB_IPADDRROW r = addr_table->table[i];
  571. if (r.dwIndex == if_index) {
  572. out_addr->s_addr = r.dwAddr;
  573. efree(addr_table);
  574. return SUCCESS;
  575. }
  576. }
  577. efree(addr_table);
  578. php_error_docref(NULL, E_WARNING,
  579. "No interface with index %u was found", if_index);
  580. return FAILURE;
  581. }
  582. int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index)
  583. {
  584. MIB_IPADDRTABLE *addr_table;
  585. ULONG size;
  586. DWORD retval;
  587. DWORD i;
  588. (void) php_sock; /* not necessary */
  589. if (addr->s_addr == INADDR_ANY) {
  590. *if_index = 0;
  591. return SUCCESS;
  592. }
  593. size = 4 * (sizeof *addr_table);
  594. addr_table = emalloc(size);
  595. retry:
  596. retval = GetIpAddrTable(addr_table, &size, 0);
  597. if (retval == ERROR_INSUFFICIENT_BUFFER) {
  598. efree(addr_table);
  599. addr_table = emalloc(size);
  600. goto retry;
  601. }
  602. if (retval != NO_ERROR) {
  603. efree(addr_table);
  604. php_error_docref(NULL, E_WARNING,
  605. "GetIpAddrTable failed with error %lu", retval);
  606. return FAILURE;
  607. }
  608. for (i = 0; i < addr_table->dwNumEntries; i++) {
  609. MIB_IPADDRROW r = addr_table->table[i];
  610. if (r.dwAddr == addr->s_addr) {
  611. *if_index = r.dwIndex;
  612. efree(addr_table);
  613. return SUCCESS;
  614. }
  615. }
  616. efree(addr_table);
  617. {
  618. char addr_str[17] = {0};
  619. inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
  620. php_error_docref(NULL, E_WARNING,
  621. "The interface with IP address %s was not found", addr_str);
  622. }
  623. return FAILURE;
  624. }
  625. #else
  626. int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr)
  627. {
  628. struct ifreq if_req;
  629. if (if_index == 0) {
  630. out_addr->s_addr = INADDR_ANY;
  631. return SUCCESS;
  632. }
  633. #if !defined(ifr_ifindex) && (defined(ifr_index) || defined(__HAIKU__))
  634. #define ifr_ifindex ifr_index
  635. #endif
  636. #if defined(SIOCGIFNAME)
  637. if_req.ifr_ifindex = if_index;
  638. if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {
  639. #elif defined(HAVE_IF_INDEXTONAME)
  640. if (if_indextoname(if_index, if_req.ifr_name) == NULL) {
  641. #else
  642. #error Neither SIOCGIFNAME nor if_indextoname are available
  643. #endif
  644. php_error_docref(NULL, E_WARNING,
  645. "Failed obtaining address for interface %u: error %d", if_index, errno);
  646. return FAILURE;
  647. }
  648. if (ioctl(php_sock->bsd_socket, SIOCGIFADDR, &if_req) == -1) {
  649. php_error_docref(NULL, E_WARNING,
  650. "Failed obtaining address for interface %u: error %d", if_index, errno);
  651. return FAILURE;
  652. }
  653. memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr,
  654. sizeof *out_addr);
  655. return SUCCESS;
  656. }
  657. int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index)
  658. {
  659. struct ifconf if_conf = {0};
  660. char *buf = NULL,
  661. *p;
  662. int size = 0,
  663. lastsize = 0;
  664. size_t entry_len;
  665. if (addr->s_addr == INADDR_ANY) {
  666. *if_index = 0;
  667. return SUCCESS;
  668. }
  669. for(;;) {
  670. size += 5 * sizeof(struct ifreq);
  671. buf = ecalloc(size, 1);
  672. if_conf.ifc_len = size;
  673. if_conf.ifc_buf = buf;
  674. if (ioctl(php_sock->bsd_socket, SIOCGIFCONF, (char*)&if_conf) == -1 &&
  675. (errno != EINVAL || lastsize != 0)) {
  676. php_error_docref(NULL, E_WARNING,
  677. "Failed obtaining interfaces list: error %d", errno);
  678. goto err;
  679. }
  680. if (if_conf.ifc_len == lastsize)
  681. /* not increasing anymore */
  682. break;
  683. else {
  684. lastsize = if_conf.ifc_len;
  685. efree(buf);
  686. buf = NULL;
  687. }
  688. }
  689. for (p = if_conf.ifc_buf;
  690. p < ((char *)if_conf.ifc_buf) + if_conf.ifc_len;
  691. p += entry_len) {
  692. /* p may be misaligned on macos. */
  693. struct ifreq cur_req;
  694. memcpy(&cur_req, p, sizeof(struct ifreq));
  695. #ifdef HAVE_SOCKADDR_SA_LEN
  696. entry_len = cur_req.ifr_addr.sa_len + sizeof(cur_req.ifr_name);
  697. #else
  698. /* if there's no sa_len, assume the ifr_addr field is a sockaddr */
  699. entry_len = sizeof(struct sockaddr) + sizeof(cur_req.ifr_name);
  700. #endif
  701. entry_len = MAX(entry_len, sizeof(cur_req));
  702. if ((((struct sockaddr*)&cur_req.ifr_addr)->sa_family == AF_INET) &&
  703. (((struct sockaddr_in*)&cur_req.ifr_addr)->sin_addr.s_addr ==
  704. addr->s_addr)) {
  705. #if defined(SIOCGIFINDEX)
  706. if (ioctl(php_sock->bsd_socket, SIOCGIFINDEX, (char*)&cur_req)
  707. == -1) {
  708. #elif defined(HAVE_IF_NAMETOINDEX)
  709. unsigned index_tmp;
  710. if ((index_tmp = if_nametoindex(cur_req.ifr_name)) == 0) {
  711. #else
  712. #error Neither SIOCGIFINDEX nor if_nametoindex are available
  713. #endif
  714. php_error_docref(NULL, E_WARNING,
  715. "Error converting interface name to index: error %d",
  716. errno);
  717. goto err;
  718. } else {
  719. #if defined(SIOCGIFINDEX)
  720. *if_index = cur_req.ifr_ifindex;
  721. #else
  722. *if_index = index_tmp;
  723. #endif
  724. efree(buf);
  725. return SUCCESS;
  726. }
  727. }
  728. }
  729. {
  730. char addr_str[17] = {0};
  731. inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
  732. php_error_docref(NULL, E_WARNING,
  733. "The interface with IP address %s was not found", addr_str);
  734. }
  735. err:
  736. if (buf != NULL)
  737. efree(buf);
  738. return FAILURE;
  739. }
  740. #endif