- Added multicast support to the sockets extension (bug #40510).

This commit is contained in:
Gustavo André dos Santos Lopes 2011-03-14 00:08:29 +00:00
parent cedd0a7224
commit e4298bf0fb
10 changed files with 1591 additions and 30 deletions

View File

@ -18,7 +18,7 @@ if test "$PHP_SOCKETS" != "no"; then
AC_DEFINE(HAVE_CMSGHDR,1,[Whether you have struct cmsghdr])
fi
AC_CHECK_FUNCS([hstrerror socketpair])
AC_CHECK_FUNCS([hstrerror socketpair if_nametoindex])
AC_CHECK_HEADERS([netdb.h netinet/tcp.h sys/un.h errno.h])
AC_TRY_COMPILE([
#include <sys/types.h>
@ -28,6 +28,6 @@ if test "$PHP_SOCKETS" != "no"; then
)
AC_DEFINE([HAVE_SOCKETS], 1, [ ])
PHP_NEW_EXTENSION([sockets], [sockets.c], [$ext_shared])
PHP_NEW_EXTENSION([sockets], [sockets.c multicast.c], [$ext_shared])
PHP_INSTALL_HEADERS([ext/sockets/], [php_sockets.h])
fi

View File

@ -5,8 +5,9 @@ ARG_ENABLE("sockets", "SOCKETS support", "no");
if (PHP_SOCKETS != "no") {
if (CHECK_LIB("ws2_32.lib", "sockets", PHP_SOCKETS)
&& CHECK_LIB("Iphlpapi.lib", "sockets", PHP_SOCKETS)
&& CHECK_HEADER_ADD_INCLUDE("winsock.h", "CFLAGS_SOCKETS")) {
EXTENSION('sockets', 'sockets.c');
EXTENSION('sockets', 'sockets.c multicast.c');
AC_DEFINE('HAVE_SOCKETS', 1);
PHP_INSTALL_HEADERS("ext/sockets", "php_sockets.h");
} else {

496
ext/sockets/multicast.c Normal file
View File

@ -0,0 +1,496 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#if HAVE_SOCKETS
#include "php_network.h"
#ifdef PHP_WIN32
# include "win32/inet.h"
# include <winsock2.h>
# include <windows.h>
# include <Ws2tcpip.h>
# include <Ws2ipdef.h>
# include "php_sockets.h"
# include "win32/sockets.h"
# define NTDDI_XP NTDDI_WINXP /* bug in SDK */
# include <IPHlpApi.h>
# undef NTDDI_XP
#else
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "php_sockets.h"
#include "multicast.h"
#include "main/php_network.h"
#if defined(MCAST_JOIN_GROUP) && 1 &&\
(!defined(PHP_WIN32) || (_WIN32_WINNT >= 0x600 && SOCKETS_ENABLE_VISTA_API))
#define RFC3678_API 1
#endif
enum source_op {
JOIN_SOURCE,
LEAVE_SOURCE,
BLOCK_SOURCE,
UNBLOCK_SOURCE
};
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);
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);
#if RFC3678_API
static int _php_source_op_to_rfc3678_op(enum source_op sop);
#else
static const char *_php_source_op_to_string(enum source_op sop);
static int _php_source_op_to_ipv4_op(enum source_op sop);
#endif
int php_mcast_join(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
unsigned int if_index TSRMLS_DC)
{
return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1 TSRMLS_CC);
}
int php_mcast_leave(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
unsigned int if_index TSRMLS_DC)
{
return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0 TSRMLS_CC);
}
int php_mcast_join_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC)
{
return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE TSRMLS_CC);
}
int php_mcast_leave_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC)
{
return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE TSRMLS_CC);
}
int php_mcast_block_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC)
{
return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE TSRMLS_CC);
}
int php_mcast_unblock_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC)
{
return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE TSRMLS_CC);
}
static int _php_mcast_join_leave(
php_socket *sock,
int level,
struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */
socklen_t group_len,
unsigned int if_index,
int join TSRMLS_DC)
{
#ifdef RFC3678_API
struct group_req greq = {0};
memcpy(&greq.gr_group, group, group_len);
assert(greq.gr_group.ss_family != 0); /* the caller has set this */
greq.gr_interface = if_index;
return setsockopt(sock->bsd_socket, level,
join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char*)&greq,
sizeof(greq));
#else
if (sock->type == AF_INET) {
struct ip_mreq mreq = {0};
struct in_addr addr;
assert(group_len == sizeof(struct sockaddr_in));
if (if_index != 0) {
if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==
FAILURE)
return -2; /* failure, but notice already emitted */
mreq.imr_interface = addr;
} else {
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
}
mreq.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
return setsockopt(sock->bsd_socket, level,
join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char*)&mreq,
sizeof(mreq));
}
#if HAVE_IPV6
else if (sock->type == AF_INET6) {
struct ipv6_mreq mreq = {0};
assert(group_len == sizeof(struct sockaddr_in6));
mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)group)->sin6_addr;
mreq.ipv6mr_interface = if_index;
return setsockopt(sock->bsd_socket, level,
join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char*)&mreq,
sizeof(mreq));
}
#endif
else {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Option %s is inapplicable to this socket type",
join ? "MCAST_JOIN_GROUP" : "MCAST_LEAVE_GROUP");
return -2;
}
#endif
}
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)
{
#ifdef RFC3678_API
struct group_source_req gsreq = {0};
memcpy(&gsreq.gsr_group, group, group_len);
assert(gsreq.gsr_group.ss_family != 0);
memcpy(&gsreq.gsr_source, source, source_len);
assert(gsreq.gsr_source.ss_family != 0);
gsreq.gsr_interface = if_index;
return setsockopt(sock->bsd_socket, level,
_php_source_op_to_rfc3678_op(sop), (char*)&gsreq, sizeof(gsreq));
#else
if (sock->type == AF_INET) {
struct ip_mreq_source mreqs = {0};
struct in_addr addr;
mreqs.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;
mreqs.imr_sourceaddr = ((struct sockaddr_in*)source)->sin_addr;
assert(group_len == sizeof(struct sockaddr_in));
assert(source_len == sizeof(struct sockaddr_in));
if (if_index != 0) {
if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==
FAILURE)
return -2; /* failure, but notice already emitted */
mreqs.imr_interface = addr;
} else {
mreqs.imr_interface.s_addr = htonl(INADDR_ANY);
}
return setsockopt(sock->bsd_socket, level,
_php_source_op_to_ipv4_op(sop), (char*)&mreqs, sizeof(mreqs));
}
#if HAVE_IPV6
else if (sock->type == AF_INET6) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"This platform does not support %s for IPv6 sockets",
_php_source_op_to_string(sop));
return -2;
}
#endif
else {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Option %s is inapplicable to this socket type",
_php_source_op_to_string(sop));
return -2;
}
#endif
}
#if RFC3678_API
static int _php_source_op_to_rfc3678_op(enum source_op sop)
{
switch (sop) {
case JOIN_SOURCE:
return MCAST_JOIN_SOURCE_GROUP;
case LEAVE_SOURCE:
return MCAST_LEAVE_SOURCE_GROUP;
case BLOCK_SOURCE:
return MCAST_BLOCK_SOURCE;
case UNBLOCK_SOURCE:
return MCAST_UNBLOCK_SOURCE;
}
assert(0);
return 0;
}
#else
static const char *_php_source_op_to_string(enum source_op sop)
{
switch (sop) {
case JOIN_SOURCE:
return "MCAST_JOIN_SOURCE_GROUP";
case LEAVE_SOURCE:
return "MCAST_LEAVE_SOURCE_GROUP";
case BLOCK_SOURCE:
return "MCAST_BLOCK_SOURCE";
case UNBLOCK_SOURCE:
return "MCAST_UNBLOCK_SOURCE";
}
assert(0);
return "";
}
static int _php_source_op_to_ipv4_op(enum source_op sop)
{
switch (sop) {
case JOIN_SOURCE:
return IP_ADD_SOURCE_MEMBERSHIP;
case LEAVE_SOURCE:
return IP_DROP_SOURCE_MEMBERSHIP;
case BLOCK_SOURCE:
return IP_BLOCK_SOURCE;
case UNBLOCK_SOURCE:
return IP_UNBLOCK_SOURCE;
}
assert(0);
return 0;
}
#endif
#if PHP_WIN32
int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)
{
MIB_IPADDRTABLE *addr_table;
ULONG size;
DWORD retval;
DWORD i;
(void) php_sock; /* not necessary */
if (if_index == 0) {
out_addr->s_addr = INADDR_ANY;
return SUCCESS;
}
size = 4 * (sizeof *addr_table);
addr_table = emalloc(size);
retry:
retval = GetIpAddrTable(addr_table, &size, 0);
if (retval == ERROR_INSUFFICIENT_BUFFER) {
efree(addr_table);
addr_table = emalloc(size);
goto retry;
}
if (retval != NO_ERROR) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"GetIpAddrTable failed with error %lu", retval);
return FAILURE;
}
for (i = 0; i < addr_table->dwNumEntries; i++) {
MIB_IPADDRROW r = addr_table->table[i];
if (r.dwIndex == if_index) {
out_addr->s_addr = r.dwAddr;
return SUCCESS;
}
}
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"No interface with index %u was found", if_index);
return FAILURE;
}
int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)
{
MIB_IPADDRTABLE *addr_table;
ULONG size;
DWORD retval;
DWORD i;
(void) php_sock; /* not necessary */
if (addr->s_addr == INADDR_ANY) {
*if_index = 0;
return SUCCESS;
}
size = 4 * (sizeof *addr_table);
addr_table = emalloc(size);
retry:
retval = GetIpAddrTable(addr_table, &size, 0);
if (retval == ERROR_INSUFFICIENT_BUFFER) {
efree(addr_table);
addr_table = emalloc(size);
goto retry;
}
if (retval != NO_ERROR) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"GetIpAddrTable failed with error %lu", retval);
return FAILURE;
}
for (i = 0; i < addr_table->dwNumEntries; i++) {
MIB_IPADDRROW r = addr_table->table[i];
if (r.dwAddr == addr->s_addr) {
*if_index = r.dwIndex;
return SUCCESS;
}
}
{
char addr_str[17] = {0};
inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"The interface with IP address %s was not found", addr_str);
}
return FAILURE;
}
#else
int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)
{
struct ifreq if_req;
if (if_index == 0) {
out_addr->s_addr = INADDR_ANY;
return SUCCESS;
}
if_req.ifr_ifindex = if_index;
if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Failed obtaining address for interface %u: error %d", if_index, errno);
return FAILURE;
}
if (ioctl(php_sock->bsd_socket, SIOCGIFADDR, &if_req) == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Failed obtaining address for interface %u: error %d", if_index, errno);
return FAILURE;
}
memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr,
sizeof *out_addr);
return SUCCESS;
}
int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)
{
struct ifconf if_conf = {0};
char *buf = NULL,
*p;
int size = 0,
lastsize = 0;
size_t entry_len;
if (addr->s_addr == INADDR_ANY) {
*if_index = 0;
return SUCCESS;
}
for(;;) {
size += 5 * sizeof(struct ifreq);
buf = ecalloc(size, 1);
if_conf.ifc_len = size;
if_conf.ifc_buf = buf;
if (ioctl(php_sock->bsd_socket, SIOCGIFCONF, (char*)&if_conf) == -1 &&
(errno != EINVAL || lastsize != 0)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Failed obtaining interfaces list: error %d", errno);
goto err;
}
if (if_conf.ifc_len == lastsize)
/* not increasing anymore */
break;
else {
lastsize = if_conf.ifc_len;
efree(buf);
buf = NULL;
}
}
for (p = if_conf.ifc_buf;
p < if_conf.ifc_buf + if_conf.ifc_len;
p += entry_len) {
struct ifreq *cur_req;
/* let's hope the pointer is aligned */
cur_req = (struct ifreq*) p;
#ifdef HAVE_SOCKADDR_SA_LEN
entry_len = cur_req->ifr_addr.sa_len + sizeof(cur_req->ifr_name);
#else
/* if there's no sa_len, assume the ifr_addr field is a sockaddr */
entry_len = sizeof(struct sockaddr) + sizeof(cur_req->ifr_name);
#endif
entry_len = MAX(entry_len, sizeof(*cur_req));
if ((((struct sockaddr*)&cur_req->ifr_addr)->sa_family == AF_INET) &&
(((struct sockaddr_in*)&cur_req->ifr_addr)->sin_addr.s_addr ==
addr->s_addr)) {
if (ioctl(php_sock->bsd_socket, SIOCGIFINDEX, (char*)cur_req)
== -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Error converting interface name to index: error %d",
errno);
goto err;
} else {
*if_index = cur_req->ifr_ifindex;
efree(buf);
return SUCCESS;
}
}
}
{
char addr_str[17] = {0};
inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"The interface with IP address %s was not found", addr_str);
}
err:
if (buf != NULL)
efree(buf);
return FAILURE;
}
#endif
#endif /* HAVE_SOCKETS */

59
ext/sockets/multicast.h Normal file
View File

@ -0,0 +1,59 @@
int php_if_index_to_addr4(
unsigned if_index,
php_socket *php_sock,
struct in_addr *out_addr TSRMLS_DC);
int php_add4_to_if_index(
struct in_addr *addr,
php_socket *php_sock,
unsigned *if_index TSRMLS_DC);
int php_mcast_join(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
unsigned int if_index TSRMLS_DC);
int php_mcast_leave(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
unsigned int if_index TSRMLS_DC);
int php_mcast_join_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC);
int php_mcast_leave_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC);
int php_mcast_block_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC);
int php_mcast_unblock_source(
php_socket *sock,
int level,
struct sockaddr *group,
socklen_t group_len,
struct sockaddr *source,
socklen_t source_len,
unsigned int if_index TSRMLS_DC);

View File

@ -56,6 +56,9 @@
# define h_errno WSAGetLastError()
# define set_errno(a) WSASetLastError(a)
# define close(a) closesocket(a)
# if _WIN32_WINNT >= 0x0600 && SOCKETS_ENABLE_VISTA_API
# define HAVE_IF_NAMETOINDEX 1
# endif
#else
# include <sys/types.h>
# include <sys/socket.h>
@ -73,8 +76,13 @@
# define IS_INVALID_SOCKET(a) (a->bsd_socket < 0)
# define set_errno(a) (errno = a)
# include "php_sockets.h"
# if HAVE_IF_NAMETOINDEX
# include <net/if.h>
# endif
#endif
#include "multicast.h"
ZEND_DECLARE_MODULE_GLOBALS(sockets)
static PHP_GINIT_FUNCTION(sockets);
@ -604,6 +612,111 @@ static int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *
}
/* }}} */
/* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6,
* depending on the socket) */
static int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
if (php_sock->type == AF_INET) {
struct sockaddr_in t = {0};
if (php_set_inet_addr(&t, string, php_sock TSRMLS_CC)) {
memcpy(ss, &t, sizeof t);
ss->ss_family = AF_INET;
*ss_len = sizeof(t);
return 1;
}
}
#if HAVE_IPV6
else if (php_sock->type == AF_INET6) {
struct sockaddr_in6 t = {0};
if (php_set_inet6_addr(&t, string, php_sock TSRMLS_CC)) {
memcpy(ss, &t, sizeof t);
ss->ss_family = AF_INET6;
*ss_len = sizeof(t);
return 1;
}
}
#endif
else {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"IP address used in the context of an unexpected type of socket");
}
return 0;
}
static int php_get_if_index_from_zval(zval *val, unsigned *out TSRMLS_DC)
{
int ret;
if (Z_TYPE_P(val) == IS_LONG) {
if (Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > UINT_MAX) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"the interface index cannot be negative or larger than %u;"
" given %ld", UINT_MAX, Z_LVAL_P(val));
ret = FAILURE;
} else {
*out = Z_LVAL_P(val);
ret = SUCCESS;
}
} else {
#if HAVE_IF_NAMETOINDEX
unsigned int ind;
zval_add_ref(&val);
convert_to_string_ex(&val);
ind = if_nametoindex(Z_STRVAL_P(val));
if (ind == 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"no interface with name \"%s\" could be found", Z_STRVAL_P(val));
ret = FAILURE;
} else {
*out = ind;
ret = SUCCESS;
}
zval_ptr_dtor(&val);
#else
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"this platform does not support looking up an interface by "
"name, an integer interface index must be supplied instead");
ret = FAILURE;
#endif
}
return ret;
}
static int php_get_if_index_from_array(const HashTable *ht, const char *key,
php_socket *sock, unsigned int *if_index TSRMLS_DC)
{
zval **val;
if (zend_hash_find(ht, key, strlen(key) + 1, (void **)&val) == FAILURE) {
*if_index = 0; /* default: 0 */
return SUCCESS;
}
return php_get_if_index_from_zval(*val, if_index TSRMLS_CC);
}
static int php_get_address_from_array(const HashTable *ht, const char *key,
php_socket *sock, php_sockaddr_storage *ss, socklen_t *ss_len TSRMLS_DC)
{
zval **val,
*valcp;
if (zend_hash_find(ht, key, strlen(key) + 1, (void **)&val) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", key);
return FAILURE;
}
valcp = *val;
zval_add_ref(&valcp);
convert_to_string_ex(val);
if (!php_set_inet46_addr(ss, ss_len, Z_STRVAL_P(valcp), sock TSRMLS_CC)) {
zval_ptr_dtor(&valcp);
return FAILURE;
}
zval_ptr_dtor(&valcp);
return SUCCESS;
}
/* {{{ PHP_GINIT_FUNCTION */
static PHP_GINIT_FUNCTION(sockets)
{
@ -666,12 +779,42 @@ PHP_MINIT_FUNCTION(sockets)
REGISTER_LONG_CONSTANT("PHP_NORMAL_READ", PHP_NORMAL_READ, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PHP_BINARY_READ", PHP_BINARY_READ, CONST_CS | CONST_PERSISTENT);
#ifndef MCAST_JOIN_GROUP
#define MCAST_JOIN_GROUP IP_ADD_MEMBERSHIP
#define MCAST_LEAVE_GROUP IP_DROP_MEMBERSHIP
#define MCAST_BLOCK_SOURCE IP_BLOCK_SOURCE
#define MCAST_UNBLOCK_SOURCE IP_UNBLOCK_SOURCE
#define MCAST_JOIN_SOURCE_GROUP IP_ADD_SOURCE_MEMBERSHIP
#define MCAST_LEAVE_SOURCE_GROUP IP_DROP_SOURCE_MEMBERSHIP
#endif
REGISTER_LONG_CONSTANT("MCAST_JOIN_GROUP", MCAST_JOIN_GROUP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MCAST_LEAVE_GROUP", MCAST_LEAVE_GROUP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MCAST_BLOCK_SOURCE", MCAST_BLOCK_SOURCE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MCAST_UNBLOCK_SOURCE", MCAST_UNBLOCK_SOURCE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MCAST_JOIN_SOURCE_GROUP", MCAST_JOIN_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MCAST_LEAVE_SOURCE_GROUP", MCAST_LEAVE_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IP_MULTICAST_IF", IP_MULTICAST_IF, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IP_MULTICAST_TTL", IP_MULTICAST_TTL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IP_MULTICAST_LOOP", IP_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT);
#if HAVE_IPV6
REGISTER_LONG_CONSTANT("IPV6_MULTICAST_IF", IPV6_MULTICAST_IF, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_MULTICAST_HOPS", IPV6_MULTICAST_HOPS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_MULTICAST_LOOP", IPV6_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT);
#endif
#ifndef WIN32
# include "unix_socket_constants.h"
#else
# include "win32_socket_constants.h"
#endif
REGISTER_LONG_CONSTANT("IPPROTO_IP", IPPROTO_IP, CONST_CS | CONST_PERSISTENT);
#if HAVE_IPV6
REGISTER_LONG_CONSTANT("IPPROTO_IPV6", IPPROTO_IPV6, CONST_CS | CONST_PERSISTENT);
#endif
if ((pe = getprotobyname("tcp"))) {
REGISTER_LONG_CONSTANT("SOL_TCP", pe->p_proto, CONST_CS | CONST_PERSISTENT);
}
@ -1738,6 +1881,26 @@ PHP_FUNCTION(socket_get_option)
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);
if (level == IPPROTO_IP) {
switch (optname) {
case IP_MULTICAST_IF: {
struct in_addr if_addr;
unsigned int if_index;
optlen = sizeof(if_addr);
if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&if_addr, &optlen) != 0) {
PHP_SOCKET_ERROR(php_sock, "unable to retrieve socket option", errno);
RETURN_FALSE;
}
if (php_add4_to_if_index(&if_addr, php_sock, &if_index TSRMLS_CC) == SUCCESS) {
RETURN_LONG((long) if_index);
} else {
RETURN_FALSE;
}
}
}
}
/* sol_socket options and general case */
switch(optname) {
case SO_LINGER:
optlen = sizeof(linger_val);
@ -1778,7 +1941,7 @@ PHP_FUNCTION(socket_get_option)
add_assoc_long(return_value, "sec", tv.tv_sec);
add_assoc_long(return_value, "usec", tv.tv_usec);
break;
default:
optlen = sizeof(other_val);
@ -1786,6 +1949,8 @@ PHP_FUNCTION(socket_get_option)
PHP_SOCKET_ERROR(php_sock, "unable to retrieve socket option", errno);
RETURN_FALSE;
}
if (optlen == 1)
other_val = *((unsigned char *)&other_val);
RETURN_LONG(other_val);
break;
@ -1793,30 +1958,122 @@ PHP_FUNCTION(socket_get_option)
}
/* }}} */
static int php_do_mcast_opt(php_socket *php_sock, int level, int optname, zval **arg4 TSRMLS_DC)
{
HashTable *opt_ht;
unsigned int if_index;
int retval;
int (*mcast_req_fun)(php_socket *, int, struct sockaddr *, socklen_t,
unsigned TSRMLS_DC);
int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
struct sockaddr *, socklen_t, unsigned TSRMLS_DC);
switch (optname) {
case MCAST_JOIN_GROUP:
mcast_req_fun = &php_mcast_join;
goto mcast_req_fun;
case MCAST_LEAVE_GROUP:
{
php_sockaddr_storage group = {0};
socklen_t glen;
mcast_req_fun = &php_mcast_leave;
mcast_req_fun:
convert_to_array_ex(arg4);
opt_ht = HASH_OF(*arg4);
if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
&glen TSRMLS_CC) == FAILURE) {
return FAILURE;
}
if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
&if_index TSRMLS_CC) == FAILURE) {
return FAILURE;
}
retval = mcast_req_fun(php_sock, level, (struct sockaddr*)&group,
glen, if_index TSRMLS_CC);
break;
}
case MCAST_BLOCK_SOURCE:
mcast_sreq_fun = &php_mcast_block_source;
goto mcast_sreq_fun;
case MCAST_UNBLOCK_SOURCE:
mcast_sreq_fun = &php_mcast_unblock_source;
goto mcast_sreq_fun;
case MCAST_JOIN_SOURCE_GROUP:
mcast_sreq_fun = &php_mcast_join_source;
goto mcast_sreq_fun;
case MCAST_LEAVE_SOURCE_GROUP:
{
php_sockaddr_storage group = {0},
source = {0};
socklen_t glen,
slen;
mcast_sreq_fun = &php_mcast_leave_source;
mcast_sreq_fun:
convert_to_array_ex(arg4);
opt_ht = HASH_OF(*arg4);
if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
&glen TSRMLS_CC) == FAILURE) {
return FAILURE;
}
if (php_get_address_from_array(opt_ht, "source", php_sock, &source,
&slen TSRMLS_CC) == FAILURE) {
return FAILURE;
}
if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
&if_index TSRMLS_CC) == FAILURE) {
return FAILURE;
}
retval = mcast_sreq_fun(php_sock, level, (struct sockaddr*)&group,
glen, (struct sockaddr*)&source, slen, if_index TSRMLS_CC);
break;
}
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"unexpected option in php_do_mcast_opt (level %d, option %d). "
"This is a bug.", level, optname);
return FAILURE;
}
if (retval != 0) {
if (retval != -2) { /* error, but message already emitted */
PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
}
return FAILURE;
}
return SUCCESS;
}
/* {{{ proto bool socket_set_option(resource socket, int level, int optname, int|array optval)
Sets socket options for the socket */
PHP_FUNCTION(socket_set_option)
{
zval *arg1, **arg4;
struct linger lv;
php_socket *php_sock;
int ov, optlen, retval;
zval *arg1, **arg4;
struct linger lv;
php_socket *php_sock;
int ov, optlen, retval;
#ifdef PHP_WIN32
int timeout;
int timeout;
#else
struct timeval tv;
struct timeval tv;
#endif
long level, optname;
void *opt_ptr;
HashTable *opt_ht;
zval **l_onoff, **l_linger;
zval **sec, **usec;
/* key name constants */
char *l_onoff_key = "l_onoff";
char *l_linger_key = "l_linger";
char *sec_key = "sec";
char *usec_key = "usec";
long level, optname;
void *opt_ptr;
HashTable *opt_ht;
zval **l_onoff, **l_linger;
zval **sec, **usec;
/* Multicast */
unsigned int if_index;
struct in_addr if_addr;
unsigned char ipv4_mcast_ttl_lback;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rllZ", &arg1, &level, &optname, &arg4) == FAILURE) {
return;
}
@ -1825,16 +2082,90 @@ PHP_FUNCTION(socket_set_option)
set_errno(0);
if (level == IPPROTO_IP) {
switch (optname) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
} else {
RETURN_TRUE;
}
case IP_MULTICAST_IF:
if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
if (php_if_index_to_addr4(if_index, php_sock, &if_addr TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
opt_ptr = &if_addr;
optlen = sizeof(if_addr);
goto dosockopt;
case IP_MULTICAST_LOOP:
case IP_MULTICAST_TTL:
convert_to_long_ex(arg4);
ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_PP(arg4);
opt_ptr = &ipv4_mcast_ttl_lback;
optlen = sizeof(ipv4_mcast_ttl_lback);
goto dosockopt;
}
}
#if HAVE_IPV6
else if (level == IPPROTO_IPV6) {
switch (optname) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
} else {
RETURN_TRUE;
}
case IPV6_MULTICAST_IF:
if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
opt_ptr = &if_index;
optlen = sizeof(if_index);
goto dosockopt;
case IPV6_MULTICAST_LOOP:
case IPV6_MULTICAST_HOPS:
convert_to_long_ex(arg4);
ov = (int) Z_LVAL_PP(arg4);
opt_ptr = &ov;
optlen = sizeof(ov);
goto dosockopt;
}
}
#endif
switch (optname) {
case SO_LINGER:
case SO_LINGER: {
const char l_onoff_key[] = "l_onoff";
const char l_linger_key[] = "l_linger";
convert_to_array_ex(arg4);
opt_ht = HASH_OF(*arg4);
if (zend_hash_find(opt_ht, l_onoff_key, strlen(l_onoff_key) + 1, (void **)&l_onoff) == FAILURE) {
if (zend_hash_find(opt_ht, l_onoff_key, sizeof(l_onoff_key), (void **)&l_onoff) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", l_onoff_key);
RETURN_FALSE;
}
if (zend_hash_find(opt_ht, l_linger_key, strlen(l_linger_key) + 1, (void **)&l_linger) == FAILURE) {
if (zend_hash_find(opt_ht, l_linger_key, sizeof(l_linger_key), (void **)&l_linger) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", l_linger_key);
RETURN_FALSE;
}
@ -1848,17 +2179,21 @@ PHP_FUNCTION(socket_set_option)
optlen = sizeof(lv);
opt_ptr = &lv;
break;
}
case SO_RCVTIMEO:
case SO_SNDTIMEO:
case SO_SNDTIMEO: {
const char sec_key[] = "sec";
const char usec_key[] = "usec";
convert_to_array_ex(arg4);
opt_ht = HASH_OF(*arg4);
if (zend_hash_find(opt_ht, sec_key, strlen(sec_key) + 1, (void **)&sec) == FAILURE) {
if (zend_hash_find(opt_ht, sec_key, sizeof(sec_key), (void **)&sec) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", sec_key);
RETURN_FALSE;
}
if (zend_hash_find(opt_ht, usec_key, strlen(usec_key) + 1, (void **)&usec) == FAILURE) {
if (zend_hash_find(opt_ht, usec_key, sizeof(usec_key), (void **)&usec) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", usec_key);
RETURN_FALSE;
}
@ -1876,7 +2211,8 @@ PHP_FUNCTION(socket_set_option)
opt_ptr = &timeout;
#endif
break;
}
default:
convert_to_long_ex(arg4);
ov = Z_LVAL_PP(arg4);
@ -1886,10 +2222,12 @@ PHP_FUNCTION(socket_set_option)
break;
}
dosockopt:
retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
if (retval != 0) {
PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
if (retval != -2) { /* error, but message already emitted */
PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
}
RETURN_FALSE;
}

View File

@ -0,0 +1,192 @@
--TEST--
Multicast support: IPv4 receive options
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$br = socket_bind($s, '0.0.0.0', 3000);
$so = socket_set_option($s, IPPROTO_IP, MCAST_JOIN_GROUP, array(
"group" => '224.0.0.23',
"interface" => 'lo',
));
if ($so === false) {
die('skip interface \'lo\' is unavailable.');
}
--FILE--
<?php
$domain = AF_INET;
$level = IPPROTO_IP;
$interface = "lo";
$mcastaddr = '224.0.0.23';
$sblock = "127.0.0.1";
echo "creating send socket bound to 127.0.0.1\n";
$sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP);
$br = socket_bind($sends1, '127.0.0.1');
var_dump($br);
echo "creating unbound socket and hoping the routing table causes an interface other than lo to be used for sending messages to $mcastaddr\n";
$sends2 = socket_create($domain, SOCK_DGRAM, SOL_UDP);
var_dump($br);
echo "creating receive socket\n";
$s = socket_create($domain, SOCK_DGRAM, SOL_UDP);
var_dump($s);
$br = socket_bind($s, '0.0.0.0', 3000);
var_dump($br);
$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$i = 0;
while (($str = socket_read($s, 3000)) !== FALSE) {
$i++;
echo "$i> ", $str, "\n";
if ($i == 1) {
echo "leaving group\n";
$so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);
var_dump($r);
}
if ($i == 2) {
echo "re-joining group\n";
$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends2, $m = "ignored mcast packet (different interface)", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
}
if ($i == 3) {
echo "blocking source\n";
$so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);
var_dump($r);
}
if ($i == 4) {
echo "unblocking source\n";
$so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
}
if ($i == 5) {
echo "leaving group\n";
$so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);
var_dump($r);
}
if ($i == 6) {
echo "joining source group\n";
$so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
}
if ($i == 7) {
echo "leaving source group\n";
$so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);
var_dump($r);
}
if ($i == 8) {
/* echo "rjsg\n";
$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);*/
break;
}
}
--EXPECT--
creating send socket bound to 127.0.0.1
bool(true)
creating unbound socket and hoping the routing table causes an interface other than lo to be used for sending messages to 224.0.0.23
bool(true)
creating receive socket
resource(6) of type (Socket)
bool(true)
bool(true)
int(14)
1> initial packet
leaving group
bool(true)
int(20)
int(14)
2> unicast packet
re-joining group
bool(true)
int(42)
int(12)
3> mcast packet
blocking source
bool(true)
int(31)
int(14)
4> unicast packet
unblocking source
bool(true)
int(27)
5> mcast packet from 127.0.0.1
leaving group
bool(true)
int(20)
int(14)
6> unicast packet
joining source group
bool(true)
int(27)
7> mcast packet from 127.0.0.1
leaving source group
bool(true)
int(20)
int(14)
8> unicast packet

View File

@ -0,0 +1,65 @@
--TEST--
Multicast support: IPv4 send options
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (socket_set_option($s, $level, IP_MULTICAST_IF, 1) === false) {
die("skip interface 1 either doesn't exist or has no ipv4 address");
}
--FILE--
<?php
$domain = AF_INET;
$level = IPPROTO_IP;
$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");
echo "Setting IP_MULTICAST_TTL\n";
$r = socket_set_option($s, $level, IP_MULTICAST_TTL, 9);
var_dump($r);
$r = socket_get_option($s, $level, IP_MULTICAST_TTL);
var_dump($r);
echo "\n";
echo "Setting IP_MULTICAST_LOOP\n";
$r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 0);
var_dump($r);
$r = socket_get_option($s, $level, IP_MULTICAST_LOOP);
var_dump($r);
$r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 1);
var_dump($r);
$r = socket_get_option($s, $level, IP_MULTICAST_LOOP);
var_dump($r);
echo "\n";
echo "Setting IP_MULTICAST_IF\n";
echo "interface 0:\n";
$r = socket_set_option($s, $level, IP_MULTICAST_IF, 0);
var_dump($r);
$r = socket_get_option($s, $level, IP_MULTICAST_IF);
var_dump($r);
echo "interface 1:\n";
$r = socket_set_option($s, $level, IP_MULTICAST_IF, 1);
var_dump($r);
$r = socket_get_option($s, $level, IP_MULTICAST_IF);
var_dump($r);
echo "\n";
--EXPECT--
Setting IP_MULTICAST_TTL
bool(true)
int(9)
Setting IP_MULTICAST_LOOP
bool(true)
int(0)
bool(true)
int(1)
Setting IP_MULTICAST_IF
interface 0:
bool(true)
int(0)
interface 1:
bool(true)
int(1)

View File

@ -0,0 +1,216 @@
--TEST--
Multicast support: IPv6 receive options
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (!defined('IPPROTO_IPV6')) {
die('skip IPv6 not available.');
}
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);
$br = socket_bind($s, '::', 3000);
/* On Linux, there is no route ff00::/8 by default on lo, which makes it
* troublesome to send multicast traffic from lo, which we must since
* we're dealing with interface-local traffic... */
$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_GROUP, array(
"group" => 'ff01::114',
"interface" => 0,
));
if ($so === false) {
die('skip unable to join multicast group on any interface.');
}
$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', 3000);
if ($r === false) {
die('skip unable to send multicast packet.');
}
$so = socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array(
"group" => 'ff01::114',
"interface" => 0,
));
$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array(
"group" => 'ff01::114',
"interface" => 0,
"source" => '2001::dead:beef',
));
if ($so === false) {
die('skip protocol independent multicast API is unavailable.');
}
--FILE--
<?php
$domain = AF_INET6;
$level = IPPROTO_IPV6;
$interface = 0;
$mcastaddr = 'ff01::114';
$sblock = "?";
echo "creating send socket\n";
$sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($sends1);
echo "creating receive socket\n";
$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($s);
$br = socket_bind($s, '::0', 3000) or die("err");
var_dump($br);
$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
)) or die("err");
var_dump($so);
$r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort);
var_dump($r, $str, $from);
$sblock = $from;
$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$i = 0;
while (($str = socket_read($s, 3000)) !== FALSE) {
$i++;
echo "$i> ", $str, "\n";
if ($i == 1) {
echo "leaving group\n";
$so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);
var_dump($r);
}
if ($i == 2) {
echo "re-joining group\n";
$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
}
if ($i == 3) {
echo "blocking source\n";
$so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);
var_dump($r);
}
if ($i == 4) {
echo "unblocking source\n";
$so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
}
if ($i == 5) {
echo "leaving group\n";
$so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);
var_dump($r);
}
if ($i == 6) {
echo "joining source group\n";
$so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "mcast packet from desired source", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
}
if ($i == 7) {
echo "leaving source group\n";
$so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);
var_dump($r);
}
if ($i == 8) {
/*echo "joining source group\n";
$so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
"source" => $sblock,
));
var_dump($so);*/
break;
}
}
--EXPECTF--
creating send socket
resource(4) of type (Socket)
creating receive socket
resource(5) of type (Socket)
bool(true)
bool(true)
int(14)
int(14)
string(14) "testing packet"
string(%d) "%s"
int(14)
1> initial packet
leaving group
bool(true)
int(20)
int(14)
2> unicast packet
re-joining group
bool(true)
int(12)
3> mcast packet
blocking source
bool(true)
int(31)
int(14)
4> unicast packet
unblocking source
bool(true)
int(12)
5> mcast packet
leaving group
bool(true)
int(20)
int(14)
6> unicast packet
joining source group
bool(true)
int(32)
7> mcast packet from desired source
leaving source group
bool(true)
int(20)
int(14)
8> unicast packet

View File

@ -0,0 +1,126 @@
--TEST--
Multicast support: IPv6 receive options (limited)
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (!defined('IPPROTO_IPV6')) {
die('skip IPv6 not available.');
}
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);
$br = socket_bind($s, '::', 3000);
/* On Linux, there is no route ff00::/8 by default on lo, which makes it
* troublesome to send multicast traffic from lo, which we must since
* we're dealing with interface-local traffic... */
$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_GROUP, array(
"group" => 'ff01::114',
"interface" => 0,
));
if ($so === false) {
die('skip unable to join multicast group on any interface.');
}
$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', 3000);
if ($r === false) {
die('skip unable to send multicast packet.');
}
$so = socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array(
"group" => 'ff01::114',
"interface" => 0,
));
$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array(
"group" => 'ff01::114',
"interface" => 0,
"source" => '2001::dead:beef',
));
if ($so !== false) {
die('skip protocol independent multicast API is available.');
}
--FILE--
<?php
$domain = AF_INET6;
$level = IPPROTO_IPV6;
$interface = 0;
$mcastaddr = 'ff01::114';
$sblock = "?";
echo "creating send socket\n";
$sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($sends1);
echo "creating receive socket\n";
$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($s);
$br = socket_bind($s, '::0', 3000) or die("err");
var_dump($br);
$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
)) or die("err");
var_dump($so);
$r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort);
var_dump($r, $str, $from);
$sblock = $from;
$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$i = 0;
while (($str = socket_read($s, 3000)) !== FALSE) {
$i++;
echo "$i> ", $str, "\n";
if ($i == 1) {
echo "leaving group\n";
$so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
$r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);
var_dump($r);
}
if ($i == 2) {
echo "re-joining group\n";
$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(
"group" => $mcastaddr,
"interface" => $interface,
));
var_dump($so);
$r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);
var_dump($r);
}
if ($i == 3) {
break;
}
}
--EXPECTF--
creating send socket
resource(4) of type (Socket)
creating receive socket
resource(5) of type (Socket)
bool(true)
bool(true)
int(14)
int(14)
string(14) "testing packet"
string(%d) "%s"
int(14)
1> initial packet
leaving group
bool(true)
int(20)
int(14)
2> unicast packet
re-joining group
bool(true)
int(12)
3> mcast packet

View File

@ -0,0 +1,68 @@
--TEST--
Multicast support: IPv6 send options
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (!defined('IPPROTO_IPV6')) {
die('skip IPv6 not available.');
}
if (socket_set_option($s, $level, IP_MULTICAST_IF, 1) === false) {
die("skip interface 1 either doesn't exist or has no ipv6 address");
}
--FILE--
<?php
$domain = AF_INET6;
$level = IPPROTO_IPV6;
$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");
echo "Setting IPV6_MULTICAST_TTL\n";
$r = socket_set_option($s, $level, IPV6_MULTICAST_HOPS, 9);
var_dump($r);
$r = socket_get_option($s, $level, IPV6_MULTICAST_HOPS);
var_dump($r);
echo "\n";
echo "Setting IPV6_MULTICAST_LOOP\n";
$r = socket_set_option($s, $level, IPV6_MULTICAST_LOOP, 0);
var_dump($r);
$r = socket_get_option($s, $level, IPV6_MULTICAST_LOOP);
var_dump($r);
$r = socket_set_option($s, $level, IPV6_MULTICAST_LOOP, 1);
var_dump($r);
$r = socket_get_option($s, $level, IPV6_MULTICAST_LOOP);
var_dump($r);
echo "\n";
echo "Setting IPV6_MULTICAST_IF\n";
echo "interface 0:\n";
$r = socket_set_option($s, $level, IPV6_MULTICAST_IF, 0);
var_dump($r);
$r = socket_get_option($s, $level, IPV6_MULTICAST_IF);
var_dump($r);
echo "interface 1:\n";
$r = socket_set_option($s, $level, IPV6_MULTICAST_IF, 1);
var_dump($r);
$r = socket_get_option($s, $level, IPV6_MULTICAST_IF);
var_dump($r);
echo "\n";
--EXPECT--
Setting IPV6_MULTICAST_TTL
bool(true)
int(9)
Setting IPV6_MULTICAST_LOOP
bool(true)
int(0)
bool(true)
int(1)
Setting IPV6_MULTICAST_IF
interface 0:
bool(true)
int(0)
interface 1:
bool(true)
int(1)