multicast.c 21 KB

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