mirror of
https://github.com/php/php-src.git
synced 2024-09-21 18:07:23 +00:00
- Added multicast support to the sockets extension (bug #40510).
This commit is contained in:
parent
cedd0a7224
commit
e4298bf0fb
@ -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
|
||||
|
@ -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
496
ext/sockets/multicast.c
Normal 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
59
ext/sockets/multicast.h
Normal 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);
|
@ -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;
|
||||
}
|
||||
|
||||
|
192
ext/sockets/tests/mcast_ipv4_recv.phpt
Normal file
192
ext/sockets/tests/mcast_ipv4_recv.phpt
Normal 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
|
65
ext/sockets/tests/mcast_ipv4_send.phpt
Normal file
65
ext/sockets/tests/mcast_ipv4_send.phpt
Normal 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)
|
216
ext/sockets/tests/mcast_ipv6_recv.phpt
Normal file
216
ext/sockets/tests/mcast_ipv6_recv.phpt
Normal 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
|
126
ext/sockets/tests/mcast_ipv6_recv_limited.phpt
Normal file
126
ext/sockets/tests/mcast_ipv6_recv_limited.phpt
Normal 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
|
68
ext/sockets/tests/mcast_ipv6_send.phpt
Normal file
68
ext/sockets/tests/mcast_ipv6_send.phpt
Normal 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)
|
Loading…
Reference in New Issue
Block a user