root/ext/sockets/multicast.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_string_to_if_index
  2. php_get_if_index_from_zval
  3. php_get_if_index_from_array
  4. php_get_address_from_array
  5. php_do_mcast_opt
  6. php_do_setsockopt_ip_mcast
  7. php_do_setsockopt_ipv6_mcast
  8. php_mcast_join
  9. php_mcast_leave
  10. php_mcast_join_source
  11. php_mcast_leave_source
  12. php_mcast_block_source
  13. php_mcast_unblock_source
  14. _php_mcast_join_leave
  15. _php_mcast_source_op
  16. _php_source_op_to_rfc3678_op
  17. _php_source_op_to_string
  18. _php_source_op_to_ipv4_op
  19. php_if_index_to_addr4
  20. php_add4_to_if_index
  21. php_if_index_to_addr4
  22. php_add4_to_if_index

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

/* [<][>][^][v][top][bottom][index][help] */