Annotation of embedaddon/php/ext/sockets/multicast.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 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: #if HAVE_SOCKETS
! 28:
! 29: #include "php_network.h"
! 30: #ifdef PHP_WIN32
! 31: # include "win32/inet.h"
! 32: # include <winsock2.h>
! 33: # include <windows.h>
! 34: # include <Ws2tcpip.h>
! 35: # include <Ws2ipdef.h>
! 36: # include "php_sockets.h"
! 37: # include "win32/sockets.h"
! 38: # define NTDDI_XP NTDDI_WINXP /* bug in SDK */
! 39: # include <IPHlpApi.h>
! 40: # undef NTDDI_XP
! 41: #else
! 42: #include <sys/socket.h>
! 43: #include <sys/ioctl.h>
! 44: #include <net/if.h>
! 45: #ifdef HAVE_SYS_SOCKIO_H
! 46: #include <sys/sockio.h>
! 47: #endif
! 48: #include <netinet/in.h>
! 49: #include <arpa/inet.h>
! 50: #endif
! 51:
! 52: #include "php_sockets.h"
! 53: #include "multicast.h"
! 54: #include "main/php_network.h"
! 55:
! 56:
! 57: enum source_op {
! 58: JOIN_SOURCE,
! 59: LEAVE_SOURCE,
! 60: BLOCK_SOURCE,
! 61: UNBLOCK_SOURCE
! 62: };
! 63:
! 64: 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);
! 65: #ifdef HAS_MCAST_EXT
! 66: 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);
! 67: #endif
! 68:
! 69: #ifdef RFC3678_API
! 70: static int _php_source_op_to_rfc3678_op(enum source_op sop);
! 71: #elif HAS_MCAST_EXT
! 72: static const char *_php_source_op_to_string(enum source_op sop);
! 73: static int _php_source_op_to_ipv4_op(enum source_op sop);
! 74: #endif
! 75:
! 76: int php_mcast_join(
! 77: php_socket *sock,
! 78: int level,
! 79: struct sockaddr *group,
! 80: socklen_t group_len,
! 81: unsigned int if_index TSRMLS_DC)
! 82: {
! 83: return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1 TSRMLS_CC);
! 84: }
! 85:
! 86: int php_mcast_leave(
! 87: php_socket *sock,
! 88: int level,
! 89: struct sockaddr *group,
! 90: socklen_t group_len,
! 91: unsigned int if_index TSRMLS_DC)
! 92: {
! 93: return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0 TSRMLS_CC);
! 94: }
! 95:
! 96: #ifdef HAS_MCAST_EXT
! 97: int php_mcast_join_source(
! 98: php_socket *sock,
! 99: int level,
! 100: struct sockaddr *group,
! 101: socklen_t group_len,
! 102: struct sockaddr *source,
! 103: socklen_t source_len,
! 104: unsigned int if_index TSRMLS_DC)
! 105: {
! 106: return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE TSRMLS_CC);
! 107: }
! 108:
! 109: int php_mcast_leave_source(
! 110: php_socket *sock,
! 111: int level,
! 112: struct sockaddr *group,
! 113: socklen_t group_len,
! 114: struct sockaddr *source,
! 115: socklen_t source_len,
! 116: unsigned int if_index TSRMLS_DC)
! 117: {
! 118: return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE TSRMLS_CC);
! 119: }
! 120:
! 121: int php_mcast_block_source(
! 122: php_socket *sock,
! 123: int level,
! 124: struct sockaddr *group,
! 125: socklen_t group_len,
! 126: struct sockaddr *source,
! 127: socklen_t source_len,
! 128: unsigned int if_index TSRMLS_DC)
! 129: {
! 130: return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE TSRMLS_CC);
! 131: }
! 132:
! 133: int php_mcast_unblock_source(
! 134: php_socket *sock,
! 135: int level,
! 136: struct sockaddr *group,
! 137: socklen_t group_len,
! 138: struct sockaddr *source,
! 139: socklen_t source_len,
! 140: unsigned int if_index TSRMLS_DC)
! 141: {
! 142: return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE TSRMLS_CC);
! 143: }
! 144: #endif /* HAS_MCAST_EXT */
! 145:
! 146:
! 147: static int _php_mcast_join_leave(
! 148: php_socket *sock,
! 149: int level,
! 150: struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */
! 151: socklen_t group_len,
! 152: unsigned int if_index,
! 153: int join TSRMLS_DC)
! 154: {
! 155: #ifdef RFC3678_API
! 156: struct group_req greq = {0};
! 157:
! 158: memcpy(&greq.gr_group, group, group_len);
! 159: assert(greq.gr_group.ss_family != 0); /* the caller has set this */
! 160: greq.gr_interface = if_index;
! 161:
! 162: return setsockopt(sock->bsd_socket, level,
! 163: join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char*)&greq,
! 164: sizeof(greq));
! 165: #else
! 166: if (sock->type == AF_INET) {
! 167: struct ip_mreq mreq = {0};
! 168: struct in_addr addr;
! 169:
! 170: assert(group_len == sizeof(struct sockaddr_in));
! 171:
! 172: if (if_index != 0) {
! 173: if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==
! 174: FAILURE)
! 175: return -2; /* failure, but notice already emitted */
! 176: mreq.imr_interface = addr;
! 177: } else {
! 178: mreq.imr_interface.s_addr = htonl(INADDR_ANY);
! 179: }
! 180: mreq.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
! 181: return setsockopt(sock->bsd_socket, level,
! 182: join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char*)&mreq,
! 183: sizeof(mreq));
! 184: }
! 185: #if HAVE_IPV6
! 186: else if (sock->type == AF_INET6) {
! 187: struct ipv6_mreq mreq = {0};
! 188:
! 189: assert(group_len == sizeof(struct sockaddr_in6));
! 190:
! 191: mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)group)->sin6_addr;
! 192: mreq.ipv6mr_interface = if_index;
! 193:
! 194: return setsockopt(sock->bsd_socket, level,
! 195: join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char*)&mreq,
! 196: sizeof(mreq));
! 197: }
! 198: #endif
! 199: else {
! 200: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 201: "Option %s is inapplicable to this socket type",
! 202: join ? "MCAST_JOIN_GROUP" : "MCAST_LEAVE_GROUP");
! 203: return -2;
! 204: }
! 205: #endif
! 206: }
! 207:
! 208: #ifdef HAS_MCAST_EXT
! 209: static int _php_mcast_source_op(
! 210: php_socket *sock,
! 211: int level,
! 212: struct sockaddr *group,
! 213: socklen_t group_len,
! 214: struct sockaddr *source,
! 215: socklen_t source_len,
! 216: unsigned int if_index,
! 217: enum source_op sop TSRMLS_DC)
! 218: {
! 219: #ifdef RFC3678_API
! 220: struct group_source_req gsreq = {0};
! 221:
! 222: memcpy(&gsreq.gsr_group, group, group_len);
! 223: assert(gsreq.gsr_group.ss_family != 0);
! 224: memcpy(&gsreq.gsr_source, source, source_len);
! 225: assert(gsreq.gsr_source.ss_family != 0);
! 226: gsreq.gsr_interface = if_index;
! 227:
! 228: return setsockopt(sock->bsd_socket, level,
! 229: _php_source_op_to_rfc3678_op(sop), (char*)&gsreq, sizeof(gsreq));
! 230: #else
! 231: if (sock->type == AF_INET) {
! 232: struct ip_mreq_source mreqs = {0};
! 233: struct in_addr addr;
! 234:
! 235: mreqs.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
! 236: mreqs.imr_sourceaddr = ((struct sockaddr_in*)source)->sin_addr;
! 237:
! 238: assert(group_len == sizeof(struct sockaddr_in));
! 239: assert(source_len == sizeof(struct sockaddr_in));
! 240:
! 241: if (if_index != 0) {
! 242: if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==
! 243: FAILURE)
! 244: return -2; /* failure, but notice already emitted */
! 245: mreqs.imr_interface = addr;
! 246: } else {
! 247: mreqs.imr_interface.s_addr = htonl(INADDR_ANY);
! 248: }
! 249:
! 250: return setsockopt(sock->bsd_socket, level,
! 251: _php_source_op_to_ipv4_op(sop), (char*)&mreqs, sizeof(mreqs));
! 252: }
! 253: #if HAVE_IPV6
! 254: else if (sock->type == AF_INET6) {
! 255: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 256: "This platform does not support %s for IPv6 sockets",
! 257: _php_source_op_to_string(sop));
! 258: return -2;
! 259: }
! 260: #endif
! 261: else {
! 262: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 263: "Option %s is inapplicable to this socket type",
! 264: _php_source_op_to_string(sop));
! 265: return -2;
! 266: }
! 267: #endif
! 268: }
! 269:
! 270: #if RFC3678_API
! 271: static int _php_source_op_to_rfc3678_op(enum source_op sop)
! 272: {
! 273: switch (sop) {
! 274: case JOIN_SOURCE:
! 275: return MCAST_JOIN_SOURCE_GROUP;
! 276: case LEAVE_SOURCE:
! 277: return MCAST_LEAVE_SOURCE_GROUP;
! 278: case BLOCK_SOURCE:
! 279: return MCAST_BLOCK_SOURCE;
! 280: case UNBLOCK_SOURCE:
! 281: return MCAST_UNBLOCK_SOURCE;
! 282: }
! 283:
! 284: assert(0);
! 285: return 0;
! 286: }
! 287: #else
! 288: static const char *_php_source_op_to_string(enum source_op sop)
! 289: {
! 290: switch (sop) {
! 291: case JOIN_SOURCE:
! 292: return "MCAST_JOIN_SOURCE_GROUP";
! 293: case LEAVE_SOURCE:
! 294: return "MCAST_LEAVE_SOURCE_GROUP";
! 295: case BLOCK_SOURCE:
! 296: return "MCAST_BLOCK_SOURCE";
! 297: case UNBLOCK_SOURCE:
! 298: return "MCAST_UNBLOCK_SOURCE";
! 299: }
! 300:
! 301: assert(0);
! 302: return "";
! 303: }
! 304:
! 305: static int _php_source_op_to_ipv4_op(enum source_op sop)
! 306: {
! 307: switch (sop) {
! 308: case JOIN_SOURCE:
! 309: return IP_ADD_SOURCE_MEMBERSHIP;
! 310: case LEAVE_SOURCE:
! 311: return IP_DROP_SOURCE_MEMBERSHIP;
! 312: case BLOCK_SOURCE:
! 313: return IP_BLOCK_SOURCE;
! 314: case UNBLOCK_SOURCE:
! 315: return IP_UNBLOCK_SOURCE;
! 316: }
! 317:
! 318: assert(0);
! 319: return 0;
! 320: }
! 321: #endif
! 322:
! 323: #endif /* HAS_MCAST_EXT */
! 324:
! 325: #if PHP_WIN32
! 326: int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)
! 327: {
! 328: MIB_IPADDRTABLE *addr_table;
! 329: ULONG size;
! 330: DWORD retval;
! 331: DWORD i;
! 332:
! 333: (void) php_sock; /* not necessary */
! 334:
! 335: if (if_index == 0) {
! 336: out_addr->s_addr = INADDR_ANY;
! 337: return SUCCESS;
! 338: }
! 339:
! 340: size = 4 * (sizeof *addr_table);
! 341: addr_table = emalloc(size);
! 342: retry:
! 343: retval = GetIpAddrTable(addr_table, &size, 0);
! 344: if (retval == ERROR_INSUFFICIENT_BUFFER) {
! 345: efree(addr_table);
! 346: addr_table = emalloc(size);
! 347: goto retry;
! 348: }
! 349: if (retval != NO_ERROR) {
! 350: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 351: "GetIpAddrTable failed with error %lu", retval);
! 352: return FAILURE;
! 353: }
! 354: for (i = 0; i < addr_table->dwNumEntries; i++) {
! 355: MIB_IPADDRROW r = addr_table->table[i];
! 356: if (r.dwIndex == if_index) {
! 357: out_addr->s_addr = r.dwAddr;
! 358: return SUCCESS;
! 359: }
! 360: }
! 361: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 362: "No interface with index %u was found", if_index);
! 363: return FAILURE;
! 364: }
! 365:
! 366: int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)
! 367: {
! 368: MIB_IPADDRTABLE *addr_table;
! 369: ULONG size;
! 370: DWORD retval;
! 371: DWORD i;
! 372:
! 373: (void) php_sock; /* not necessary */
! 374:
! 375: if (addr->s_addr == INADDR_ANY) {
! 376: *if_index = 0;
! 377: return SUCCESS;
! 378: }
! 379:
! 380: size = 4 * (sizeof *addr_table);
! 381: addr_table = emalloc(size);
! 382: retry:
! 383: retval = GetIpAddrTable(addr_table, &size, 0);
! 384: if (retval == ERROR_INSUFFICIENT_BUFFER) {
! 385: efree(addr_table);
! 386: addr_table = emalloc(size);
! 387: goto retry;
! 388: }
! 389: if (retval != NO_ERROR) {
! 390: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 391: "GetIpAddrTable failed with error %lu", retval);
! 392: return FAILURE;
! 393: }
! 394: for (i = 0; i < addr_table->dwNumEntries; i++) {
! 395: MIB_IPADDRROW r = addr_table->table[i];
! 396: if (r.dwAddr == addr->s_addr) {
! 397: *if_index = r.dwIndex;
! 398: return SUCCESS;
! 399: }
! 400: }
! 401:
! 402: {
! 403: char addr_str[17] = {0};
! 404: inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
! 405: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 406: "The interface with IP address %s was not found", addr_str);
! 407: }
! 408: return FAILURE;
! 409: }
! 410:
! 411: #else
! 412:
! 413: int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)
! 414: {
! 415: struct ifreq if_req;
! 416:
! 417: if (if_index == 0) {
! 418: out_addr->s_addr = INADDR_ANY;
! 419: return SUCCESS;
! 420: }
! 421:
! 422: #if !defined(ifr_ifindex) && defined(ifr_index)
! 423: #define ifr_ifindex ifr_index
! 424: #endif
! 425:
! 426: #if defined(SIOCGIFNAME)
! 427: if_req.ifr_ifindex = if_index;
! 428: if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {
! 429: #elif defined(HAVE_IF_INDEXTONAME)
! 430: if (if_indextoname(if_index, if_req.ifr_name) == NULL) {
! 431: #else
! 432: #error Neither SIOCGIFNAME nor if_indextoname are available
! 433: #endif
! 434: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 435: "Failed obtaining address for interface %u: error %d", if_index, errno);
! 436: return FAILURE;
! 437: }
! 438:
! 439: if (ioctl(php_sock->bsd_socket, SIOCGIFADDR, &if_req) == -1) {
! 440: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 441: "Failed obtaining address for interface %u: error %d", if_index, errno);
! 442: return FAILURE;
! 443: }
! 444:
! 445: memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr,
! 446: sizeof *out_addr);
! 447: return SUCCESS;
! 448: }
! 449:
! 450: int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)
! 451: {
! 452: struct ifconf if_conf = {0};
! 453: char *buf = NULL,
! 454: *p;
! 455: int size = 0,
! 456: lastsize = 0;
! 457: size_t entry_len;
! 458:
! 459: if (addr->s_addr == INADDR_ANY) {
! 460: *if_index = 0;
! 461: return SUCCESS;
! 462: }
! 463:
! 464: for(;;) {
! 465: size += 5 * sizeof(struct ifreq);
! 466: buf = ecalloc(size, 1);
! 467: if_conf.ifc_len = size;
! 468: if_conf.ifc_buf = buf;
! 469:
! 470: if (ioctl(php_sock->bsd_socket, SIOCGIFCONF, (char*)&if_conf) == -1 &&
! 471: (errno != EINVAL || lastsize != 0)) {
! 472: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 473: "Failed obtaining interfaces list: error %d", errno);
! 474: goto err;
! 475: }
! 476:
! 477: if (if_conf.ifc_len == lastsize)
! 478: /* not increasing anymore */
! 479: break;
! 480: else {
! 481: lastsize = if_conf.ifc_len;
! 482: efree(buf);
! 483: buf = NULL;
! 484: }
! 485: }
! 486:
! 487: for (p = if_conf.ifc_buf;
! 488: p < if_conf.ifc_buf + if_conf.ifc_len;
! 489: p += entry_len) {
! 490: struct ifreq *cur_req;
! 491:
! 492: /* let's hope the pointer is aligned */
! 493: cur_req = (struct ifreq*) p;
! 494:
! 495: #ifdef HAVE_SOCKADDR_SA_LEN
! 496: entry_len = cur_req->ifr_addr.sa_len + sizeof(cur_req->ifr_name);
! 497: #else
! 498: /* if there's no sa_len, assume the ifr_addr field is a sockaddr */
! 499: entry_len = sizeof(struct sockaddr) + sizeof(cur_req->ifr_name);
! 500: #endif
! 501: entry_len = MAX(entry_len, sizeof(*cur_req));
! 502:
! 503: if ((((struct sockaddr*)&cur_req->ifr_addr)->sa_family == AF_INET) &&
! 504: (((struct sockaddr_in*)&cur_req->ifr_addr)->sin_addr.s_addr ==
! 505: addr->s_addr)) {
! 506: #if defined(SIOCGIFINDEX)
! 507: if (ioctl(php_sock->bsd_socket, SIOCGIFINDEX, (char*)cur_req)
! 508: == -1) {
! 509: #elif defined(HAVE_IF_NAMETOINDEX)
! 510: unsigned index_tmp;
! 511: if ((index_tmp = if_nametoindex(cur_req->ifr_name)) == 0) {
! 512: #else
! 513: #error Neither SIOCGIFINDEX nor if_nametoindex are available
! 514: #endif
! 515: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 516: "Error converting interface name to index: error %d",
! 517: errno);
! 518: goto err;
! 519: } else {
! 520: #if defined(SIOCGIFINDEX)
! 521: *if_index = cur_req->ifr_ifindex;
! 522: #else
! 523: *if_index = index_tmp;
! 524: #endif
! 525: efree(buf);
! 526: return SUCCESS;
! 527: }
! 528: }
! 529: }
! 530:
! 531: {
! 532: char addr_str[17] = {0};
! 533: inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
! 534: php_error_docref(NULL TSRMLS_CC, E_WARNING,
! 535: "The interface with IP address %s was not found", addr_str);
! 536: }
! 537:
! 538: err:
! 539: if (buf != NULL)
! 540: efree(buf);
! 541: return FAILURE;
! 542: }
! 543: #endif
! 544:
! 545: #endif /* HAVE_SOCKETS */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>