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>
)
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
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 {
--- /dev/null
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include "php.h"\r
+\r
+#if HAVE_SOCKETS\r
+\r
+#include "php_network.h"\r
+#ifdef PHP_WIN32\r
+# include "win32/inet.h"\r
+# include <winsock2.h>\r
+# include <windows.h>\r
+# include <Ws2tcpip.h>\r
+# include <Ws2ipdef.h>\r
+# include "php_sockets.h"\r
+# include "win32/sockets.h"\r
+# define NTDDI_XP NTDDI_WINXP /* bug in SDK */\r
+# include <IPHlpApi.h>\r
+# undef NTDDI_XP\r
+#else\r
+#include <sys/socket.h>\r
+#include <sys/ioctl.h>\r
+#include <net/if.h>\r
+#include <netinet/in.h>\r
+#include <arpa/inet.h>\r
+#endif\r
+\r
+#include "php_sockets.h"\r
+#include "multicast.h"\r
+#include "main/php_network.h"\r
+\r
+#if defined(MCAST_JOIN_GROUP) && 1 &&\\r
+ (!defined(PHP_WIN32) || (_WIN32_WINNT >= 0x600 && SOCKETS_ENABLE_VISTA_API))\r
+#define RFC3678_API 1\r
+#endif\r
+\r
+\r
+enum source_op {\r
+ JOIN_SOURCE,\r
+ LEAVE_SOURCE,\r
+ BLOCK_SOURCE,\r
+ UNBLOCK_SOURCE\r
+};\r
+\r
+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);\r
+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);\r
+#if RFC3678_API\r
+static int _php_source_op_to_rfc3678_op(enum source_op sop);\r
+#else\r
+static const char *_php_source_op_to_string(enum source_op sop);\r
+static int _php_source_op_to_ipv4_op(enum source_op sop);\r
+#endif\r
+\r
+int php_mcast_join(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ unsigned int if_index TSRMLS_DC)\r
+{\r
+ return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1 TSRMLS_CC);\r
+}\r
+\r
+int php_mcast_leave(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ unsigned int if_index TSRMLS_DC)\r
+{\r
+ return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0 TSRMLS_CC);\r
+}\r
+\r
+int php_mcast_join_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC)\r
+{\r
+ return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE TSRMLS_CC);\r
+}\r
+\r
+int php_mcast_leave_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC)\r
+{\r
+ return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE TSRMLS_CC);\r
+}\r
+\r
+int php_mcast_block_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC)\r
+{\r
+ return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE TSRMLS_CC);\r
+}\r
+\r
+int php_mcast_unblock_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC)\r
+{\r
+ return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE TSRMLS_CC);\r
+}\r
+\r
+static int _php_mcast_join_leave(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */\r
+ socklen_t group_len,\r
+ unsigned int if_index,\r
+ int join TSRMLS_DC)\r
+{\r
+#ifdef RFC3678_API\r
+ struct group_req greq = {0};\r
+ \r
+ memcpy(&greq.gr_group, group, group_len);\r
+ assert(greq.gr_group.ss_family != 0); /* the caller has set this */\r
+ greq.gr_interface = if_index;\r
+\r
+ return setsockopt(sock->bsd_socket, level,\r
+ join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char*)&greq,\r
+ sizeof(greq)); \r
+#else\r
+ if (sock->type == AF_INET) {\r
+ struct ip_mreq mreq = {0};\r
+ struct in_addr addr;\r
+ \r
+ assert(group_len == sizeof(struct sockaddr_in));\r
+ \r
+ if (if_index != 0) {\r
+ if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==\r
+ FAILURE)\r
+ return -2; /* failure, but notice already emitted */\r
+ mreq.imr_interface = addr;\r
+ } else {\r
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);\r
+ }\r
+ mreq.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;\r
+ return setsockopt(sock->bsd_socket, level,\r
+ join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char*)&mreq,\r
+ sizeof(mreq));\r
+ }\r
+#if HAVE_IPV6\r
+ else if (sock->type == AF_INET6) {\r
+ struct ipv6_mreq mreq = {0};\r
+ \r
+ assert(group_len == sizeof(struct sockaddr_in6));\r
+\r
+ mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)group)->sin6_addr;\r
+ mreq.ipv6mr_interface = if_index;\r
+ \r
+ return setsockopt(sock->bsd_socket, level,\r
+ join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char*)&mreq,\r
+ sizeof(mreq));\r
+ }\r
+#endif\r
+ else {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "Option %s is inapplicable to this socket type",\r
+ join ? "MCAST_JOIN_GROUP" : "MCAST_LEAVE_GROUP");\r
+ return -2;\r
+ }\r
+#endif\r
+}\r
+\r
+static int _php_mcast_source_op(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index,\r
+ enum source_op sop TSRMLS_DC)\r
+{\r
+#ifdef RFC3678_API\r
+ struct group_source_req gsreq = {0};\r
+ \r
+ memcpy(&gsreq.gsr_group, group, group_len);\r
+ assert(gsreq.gsr_group.ss_family != 0);\r
+ memcpy(&gsreq.gsr_source, source, source_len);\r
+ assert(gsreq.gsr_source.ss_family != 0);\r
+ gsreq.gsr_interface = if_index;\r
+ \r
+ return setsockopt(sock->bsd_socket, level,\r
+ _php_source_op_to_rfc3678_op(sop), (char*)&gsreq, sizeof(gsreq));\r
+#else\r
+ if (sock->type == AF_INET) {\r
+ struct ip_mreq_source mreqs = {0};\r
+ struct in_addr addr;\r
+ \r
+ mreqs.imr_multiaddr = ((struct sockaddr_in*)group)->sin_addr;\r
+ mreqs.imr_sourceaddr = ((struct sockaddr_in*)source)->sin_addr;\r
+ \r
+ assert(group_len == sizeof(struct sockaddr_in));\r
+ assert(source_len == sizeof(struct sockaddr_in));\r
+ \r
+ if (if_index != 0) {\r
+ if (php_if_index_to_addr4(if_index, sock, &addr TSRMLS_CC) ==\r
+ FAILURE)\r
+ return -2; /* failure, but notice already emitted */\r
+ mreqs.imr_interface = addr;\r
+ } else {\r
+ mreqs.imr_interface.s_addr = htonl(INADDR_ANY);\r
+ }\r
+ \r
+ return setsockopt(sock->bsd_socket, level,\r
+ _php_source_op_to_ipv4_op(sop), (char*)&mreqs, sizeof(mreqs));\r
+ }\r
+#if HAVE_IPV6\r
+ else if (sock->type == AF_INET6) {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "This platform does not support %s for IPv6 sockets",\r
+ _php_source_op_to_string(sop));\r
+ return -2;\r
+ }\r
+#endif\r
+ else {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "Option %s is inapplicable to this socket type",\r
+ _php_source_op_to_string(sop));\r
+ return -2;\r
+ }\r
+#endif\r
+}\r
+\r
+#if RFC3678_API\r
+static int _php_source_op_to_rfc3678_op(enum source_op sop)\r
+{\r
+ switch (sop) {\r
+ case JOIN_SOURCE:\r
+ return MCAST_JOIN_SOURCE_GROUP;\r
+ case LEAVE_SOURCE:\r
+ return MCAST_LEAVE_SOURCE_GROUP;\r
+ case BLOCK_SOURCE:\r
+ return MCAST_BLOCK_SOURCE;\r
+ case UNBLOCK_SOURCE:\r
+ return MCAST_UNBLOCK_SOURCE;\r
+ }\r
+ \r
+ assert(0);\r
+ return 0;\r
+}\r
+#else\r
+static const char *_php_source_op_to_string(enum source_op sop)\r
+{\r
+ switch (sop) {\r
+ case JOIN_SOURCE:\r
+ return "MCAST_JOIN_SOURCE_GROUP";\r
+ case LEAVE_SOURCE:\r
+ return "MCAST_LEAVE_SOURCE_GROUP";\r
+ case BLOCK_SOURCE:\r
+ return "MCAST_BLOCK_SOURCE";\r
+ case UNBLOCK_SOURCE:\r
+ return "MCAST_UNBLOCK_SOURCE";\r
+ }\r
+ \r
+ assert(0);\r
+ return "";\r
+}\r
+\r
+static int _php_source_op_to_ipv4_op(enum source_op sop)\r
+{\r
+ switch (sop) {\r
+ case JOIN_SOURCE:\r
+ return IP_ADD_SOURCE_MEMBERSHIP;\r
+ case LEAVE_SOURCE:\r
+ return IP_DROP_SOURCE_MEMBERSHIP;\r
+ case BLOCK_SOURCE:\r
+ return IP_BLOCK_SOURCE;\r
+ case UNBLOCK_SOURCE:\r
+ return IP_UNBLOCK_SOURCE;\r
+ }\r
+ \r
+ assert(0);\r
+ return 0;\r
+}\r
+#endif\r
+\r
+#if PHP_WIN32\r
+int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)\r
+{\r
+ MIB_IPADDRTABLE *addr_table;\r
+ ULONG size;\r
+ DWORD retval;\r
+ DWORD i;\r
+\r
+ (void) php_sock; /* not necessary */\r
+\r
+ if (if_index == 0) {\r
+ out_addr->s_addr = INADDR_ANY;\r
+ return SUCCESS;\r
+ }\r
+\r
+ size = 4 * (sizeof *addr_table);\r
+ addr_table = emalloc(size);\r
+retry:\r
+ retval = GetIpAddrTable(addr_table, &size, 0);\r
+ if (retval == ERROR_INSUFFICIENT_BUFFER) {\r
+ efree(addr_table);\r
+ addr_table = emalloc(size);\r
+ goto retry;\r
+ }\r
+ if (retval != NO_ERROR) {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "GetIpAddrTable failed with error %lu", retval);\r
+ return FAILURE;\r
+ }\r
+ for (i = 0; i < addr_table->dwNumEntries; i++) {\r
+ MIB_IPADDRROW r = addr_table->table[i];\r
+ if (r.dwIndex == if_index) {\r
+ out_addr->s_addr = r.dwAddr;\r
+ return SUCCESS;\r
+ }\r
+ }\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "No interface with index %u was found", if_index);\r
+ return FAILURE;\r
+}\r
+\r
+int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)\r
+{\r
+ MIB_IPADDRTABLE *addr_table;\r
+ ULONG size;\r
+ DWORD retval;\r
+ DWORD i;\r
+\r
+ (void) php_sock; /* not necessary */\r
+\r
+ if (addr->s_addr == INADDR_ANY) {\r
+ *if_index = 0;\r
+ return SUCCESS;\r
+ }\r
+\r
+ size = 4 * (sizeof *addr_table);\r
+ addr_table = emalloc(size);\r
+retry:\r
+ retval = GetIpAddrTable(addr_table, &size, 0);\r
+ if (retval == ERROR_INSUFFICIENT_BUFFER) {\r
+ efree(addr_table);\r
+ addr_table = emalloc(size);\r
+ goto retry;\r
+ }\r
+ if (retval != NO_ERROR) {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "GetIpAddrTable failed with error %lu", retval);\r
+ return FAILURE;\r
+ }\r
+ for (i = 0; i < addr_table->dwNumEntries; i++) {\r
+ MIB_IPADDRROW r = addr_table->table[i];\r
+ if (r.dwAddr == addr->s_addr) {\r
+ *if_index = r.dwIndex;\r
+ return SUCCESS;\r
+ }\r
+ }\r
+\r
+ {\r
+ char addr_str[17] = {0};\r
+ inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "The interface with IP address %s was not found", addr_str);\r
+ }\r
+ return FAILURE;\r
+}\r
+\r
+#else\r
+\r
+int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_addr *out_addr TSRMLS_DC)\r
+{\r
+ struct ifreq if_req;\r
+ \r
+ if (if_index == 0) {\r
+ out_addr->s_addr = INADDR_ANY;\r
+ return SUCCESS;\r
+ }\r
+\r
+ if_req.ifr_ifindex = if_index;\r
+ if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "Failed obtaining address for interface %u: error %d", if_index, errno);\r
+ return FAILURE;\r
+ }\r
+ if (ioctl(php_sock->bsd_socket, SIOCGIFADDR, &if_req) == -1) {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "Failed obtaining address for interface %u: error %d", if_index, errno);\r
+ return FAILURE;\r
+ }\r
+ \r
+ memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr,\r
+ sizeof *out_addr);\r
+ return SUCCESS;\r
+}\r
+\r
+int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *if_index TSRMLS_DC)\r
+{\r
+ struct ifconf if_conf = {0};\r
+ char *buf = NULL,\r
+ *p;\r
+ int size = 0,\r
+ lastsize = 0;\r
+ size_t entry_len;\r
+ \r
+ if (addr->s_addr == INADDR_ANY) {\r
+ *if_index = 0;\r
+ return SUCCESS;\r
+ }\r
+ \r
+ for(;;) {\r
+ size += 5 * sizeof(struct ifreq);\r
+ buf = ecalloc(size, 1);\r
+ if_conf.ifc_len = size;\r
+ if_conf.ifc_buf = buf;\r
+ \r
+ if (ioctl(php_sock->bsd_socket, SIOCGIFCONF, (char*)&if_conf) == -1 &&\r
+ (errno != EINVAL || lastsize != 0)) {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "Failed obtaining interfaces list: error %d", errno);\r
+ goto err;\r
+ }\r
+ \r
+ if (if_conf.ifc_len == lastsize)\r
+ /* not increasing anymore */\r
+ break;\r
+ else {\r
+ lastsize = if_conf.ifc_len;\r
+ efree(buf);\r
+ buf = NULL;\r
+ }\r
+ }\r
+ \r
+ for (p = if_conf.ifc_buf;\r
+ p < if_conf.ifc_buf + if_conf.ifc_len;\r
+ p += entry_len) {\r
+ struct ifreq *cur_req;\r
+ \r
+ /* let's hope the pointer is aligned */\r
+ cur_req = (struct ifreq*) p;\r
+ \r
+#ifdef HAVE_SOCKADDR_SA_LEN\r
+ entry_len = cur_req->ifr_addr.sa_len + sizeof(cur_req->ifr_name);\r
+#else\r
+ /* if there's no sa_len, assume the ifr_addr field is a sockaddr */\r
+ entry_len = sizeof(struct sockaddr) + sizeof(cur_req->ifr_name);\r
+#endif\r
+ entry_len = MAX(entry_len, sizeof(*cur_req));\r
+ \r
+ if ((((struct sockaddr*)&cur_req->ifr_addr)->sa_family == AF_INET) &&\r
+ (((struct sockaddr_in*)&cur_req->ifr_addr)->sin_addr.s_addr ==\r
+ addr->s_addr)) {\r
+ if (ioctl(php_sock->bsd_socket, SIOCGIFINDEX, (char*)cur_req)\r
+ == -1) {\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "Error converting interface name to index: error %d",\r
+ errno);\r
+ goto err;\r
+ } else {\r
+ *if_index = cur_req->ifr_ifindex;\r
+ efree(buf);\r
+ return SUCCESS;\r
+ }\r
+ }\r
+ }\r
+\r
+ {\r
+ char addr_str[17] = {0};\r
+ inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));\r
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,\r
+ "The interface with IP address %s was not found", addr_str);\r
+ }\r
+ \r
+err:\r
+ if (buf != NULL)\r
+ efree(buf);\r
+ return FAILURE;\r
+}\r
+#endif\r
+\r
+#endif /* HAVE_SOCKETS */\r
--- /dev/null
+int php_if_index_to_addr4(\r
+ unsigned if_index,\r
+ php_socket *php_sock,\r
+ struct in_addr *out_addr TSRMLS_DC);\r
+\r
+int php_add4_to_if_index(\r
+ struct in_addr *addr,\r
+ php_socket *php_sock,\r
+ unsigned *if_index TSRMLS_DC);\r
+\r
+int php_mcast_join(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ unsigned int if_index TSRMLS_DC);\r
+\r
+int php_mcast_leave(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ unsigned int if_index TSRMLS_DC);\r
+\r
+int php_mcast_join_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC);\r
+\r
+int php_mcast_leave_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC);\r
+\r
+int php_mcast_block_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC);\r
+\r
+int php_mcast_unblock_source(\r
+ php_socket *sock,\r
+ int level,\r
+ struct sockaddr *group,\r
+ socklen_t group_len,\r
+ struct sockaddr *source,\r
+ socklen_t source_len,\r
+ unsigned int if_index TSRMLS_DC);\r
# 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>
# 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);
}
/* }}} */
+/* 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)
{
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);
}
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);
add_assoc_long(return_value, "sec", tv.tv_sec);
add_assoc_long(return_value, "usec", tv.tv_usec);
break;
-
+
default:
optlen = sizeof(other_val);
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;
}
/* }}} */
+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;
}
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;
}
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;
}
opt_ptr = &timeout;
#endif
break;
-
+ }
+
default:
convert_to_long_ex(arg4);
ov = Z_LVAL_PP(arg4);
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;
}
--- /dev/null
+--TEST--\r
+Multicast support: IPv4 receive options\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('sockets')) {\r
+ die('skip sockets extension not available.');\r
+}\r
+$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);\r
+$br = socket_bind($s, '0.0.0.0', 3000);\r
+$so = socket_set_option($s, IPPROTO_IP, MCAST_JOIN_GROUP, array(\r
+ "group" => '224.0.0.23',\r
+ "interface" => 'lo',\r
+));\r
+if ($so === false) {\r
+ die('skip interface \'lo\' is unavailable.');\r
+}\r
+--FILE--\r
+<?php\r
+$domain = AF_INET;\r
+$level = IPPROTO_IP;\r
+$interface = "lo";\r
+$mcastaddr = '224.0.0.23';\r
+$sblock = "127.0.0.1";\r
+\r
+echo "creating send socket bound to 127.0.0.1\n";\r
+$sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP);\r
+$br = socket_bind($sends1, '127.0.0.1');\r
+var_dump($br);\r
+\r
+echo "creating unbound socket and hoping the routing table causes an interface other than lo to be used for sending messages to $mcastaddr\n";\r
+$sends2 = socket_create($domain, SOCK_DGRAM, SOL_UDP);\r
+var_dump($br);\r
+\r
+echo "creating receive socket\n";\r
+$s = socket_create($domain, SOCK_DGRAM, SOL_UDP);\r
+var_dump($s);\r
+$br = socket_bind($s, '0.0.0.0', 3000);\r
+var_dump($br);\r
+\r
+$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+));\r
+var_dump($so);\r
+\r
+$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000);\r
+var_dump($r);\r
+\r
+$i = 0;\r
+while (($str = socket_read($s, 3000)) !== FALSE) {\r
+ $i++;\r
+ echo "$i> ", $str, "\n";\r
+\r
+if ($i == 1) {\r
+ echo "leaving group\n";\r
+ $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 2) {\r
+ echo "re-joining group\n";\r
+ $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends2, $m = "ignored mcast packet (different interface)", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 3) {\r
+ echo "blocking source\n";\r
+ $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 4) {\r
+ echo "unblocking source\n";\r
+ $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 5) {\r
+ echo "leaving group\n";\r
+ $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 6) {\r
+ echo "joining source group\n";\r
+ $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "mcast packet from 127.0.0.1", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 7) {\r
+ echo "leaving source group\n";\r
+ $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "127.0.0.1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 8) {\r
+/* echo "rjsg\n";\r
+ $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);*/\r
+ break;\r
+}\r
+\r
+}\r
+--EXPECT--\r
+creating send socket bound to 127.0.0.1\r
+bool(true)\r
+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\r
+bool(true)\r
+creating receive socket\r
+resource(6) of type (Socket)\r
+bool(true)\r
+bool(true)\r
+int(14)\r
+1> initial packet\r
+leaving group\r
+bool(true)\r
+int(20)\r
+int(14)\r
+2> unicast packet\r
+re-joining group\r
+bool(true)\r
+int(42)\r
+int(12)\r
+3> mcast packet\r
+blocking source\r
+bool(true)\r
+int(31)\r
+int(14)\r
+4> unicast packet\r
+unblocking source\r
+bool(true)\r
+int(27)\r
+5> mcast packet from 127.0.0.1\r
+leaving group\r
+bool(true)\r
+int(20)\r
+int(14)\r
+6> unicast packet\r
+joining source group\r
+bool(true)\r
+int(27)\r
+7> mcast packet from 127.0.0.1\r
+leaving source group\r
+bool(true)\r
+int(20)\r
+int(14)\r
+8> unicast packet\r
--- /dev/null
+--TEST--\r
+Multicast support: IPv4 send options\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('sockets')) {\r
+ die('skip sockets extension not available.');\r
+}\r
+if (socket_set_option($s, $level, IP_MULTICAST_IF, 1) === false) {\r
+ die("skip interface 1 either doesn't exist or has no ipv4 address");\r
+}\r
+--FILE--\r
+<?php\r
+$domain = AF_INET;\r
+$level = IPPROTO_IP;\r
+$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");\r
+\r
+echo "Setting IP_MULTICAST_TTL\n";\r
+$r = socket_set_option($s, $level, IP_MULTICAST_TTL, 9);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IP_MULTICAST_TTL);\r
+var_dump($r);\r
+echo "\n";\r
+\r
+echo "Setting IP_MULTICAST_LOOP\n";\r
+$r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 0);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IP_MULTICAST_LOOP);\r
+var_dump($r);\r
+$r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 1);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IP_MULTICAST_LOOP);\r
+var_dump($r);\r
+echo "\n";\r
+\r
+echo "Setting IP_MULTICAST_IF\n";\r
+echo "interface 0:\n";\r
+$r = socket_set_option($s, $level, IP_MULTICAST_IF, 0);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IP_MULTICAST_IF);\r
+var_dump($r);\r
+echo "interface 1:\n";\r
+$r = socket_set_option($s, $level, IP_MULTICAST_IF, 1);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IP_MULTICAST_IF);\r
+var_dump($r);\r
+echo "\n";\r
+\r
+--EXPECT--\r
+Setting IP_MULTICAST_TTL\r
+bool(true)\r
+int(9)\r
+\r
+Setting IP_MULTICAST_LOOP\r
+bool(true)\r
+int(0)\r
+bool(true)\r
+int(1)\r
+\r
+Setting IP_MULTICAST_IF\r
+interface 0:\r
+bool(true)\r
+int(0)\r
+interface 1:\r
+bool(true)\r
+int(1)\r
--- /dev/null
+--TEST--\r
+Multicast support: IPv6 receive options\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('sockets')) {\r
+ die('skip sockets extension not available.');\r
+}\r
+if (!defined('IPPROTO_IPV6')) {\r
+ die('skip IPv6 not available.');\r
+}\r
+$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);\r
+$br = socket_bind($s, '::', 3000);\r
+/* On Linux, there is no route ff00::/8 by default on lo, which makes it\r
+ * troublesome to send multicast traffic from lo, which we must since\r
+ * we're dealing with interface-local traffic... */\r
+$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_GROUP, array(\r
+ "group" => 'ff01::114',\r
+ "interface" => 0,\r
+));\r
+if ($so === false) {\r
+ die('skip unable to join multicast group on any interface.');\r
+}\r
+$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', 3000);\r
+if ($r === false) {\r
+ die('skip unable to send multicast packet.');\r
+}\r
+$so = socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array(\r
+ "group" => 'ff01::114',\r
+ "interface" => 0,\r
+));\r
+$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array(\r
+ "group" => 'ff01::114',\r
+ "interface" => 0,\r
+ "source" => '2001::dead:beef',\r
+));\r
+if ($so === false) {\r
+ die('skip protocol independent multicast API is unavailable.');\r
+}\r
+\r
+--FILE--\r
+<?php\r
+$domain = AF_INET6;\r
+$level = IPPROTO_IPV6;\r
+$interface = 0;\r
+$mcastaddr = 'ff01::114';\r
+$sblock = "?";\r
+\r
+echo "creating send socket\n";\r
+$sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");\r
+var_dump($sends1);\r
+\r
+echo "creating receive socket\n";\r
+$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");\r
+var_dump($s);\r
+$br = socket_bind($s, '::0', 3000) or die("err");\r
+var_dump($br);\r
+\r
+$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+)) or die("err");\r
+var_dump($so);\r
+\r
+$r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, 3000);\r
+var_dump($r);\r
+$r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort);\r
+var_dump($r, $str, $from);\r
+$sblock = $from;\r
+\r
+$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000);\r
+var_dump($r);\r
+\r
+$i = 0;\r
+while (($str = socket_read($s, 3000)) !== FALSE) {\r
+ $i++;\r
+ echo "$i> ", $str, "\n";\r
+\r
+if ($i == 1) {\r
+ echo "leaving group\n";\r
+ $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 2) {\r
+ echo "re-joining group\n";\r
+ $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 3) {\r
+ echo "blocking source\n";\r
+ $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored packet (blocked source)", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 4) {\r
+ echo "unblocking source\n";\r
+ $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 5) {\r
+ echo "leaving group\n";\r
+ $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 6) {\r
+ echo "joining source group\n";\r
+ $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "mcast packet from desired source", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 7) {\r
+ echo "leaving source group\n";\r
+ $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 8) {\r
+ /*echo "joining source group\n";\r
+ $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ "source" => $sblock,\r
+ ));\r
+ var_dump($so);*/\r
+ break;\r
+}\r
+\r
+}\r
+--EXPECTF--\r
+creating send socket\r
+resource(4) of type (Socket)\r
+creating receive socket\r
+resource(5) of type (Socket)\r
+bool(true)\r
+bool(true)\r
+int(14)\r
+int(14)\r
+string(14) "testing packet"\r
+string(%d) "%s"\r
+int(14)\r
+1> initial packet\r
+leaving group\r
+bool(true)\r
+int(20)\r
+int(14)\r
+2> unicast packet\r
+re-joining group\r
+bool(true)\r
+int(12)\r
+3> mcast packet\r
+blocking source\r
+bool(true)\r
+int(31)\r
+int(14)\r
+4> unicast packet\r
+unblocking source\r
+bool(true)\r
+int(12)\r
+5> mcast packet\r
+leaving group\r
+bool(true)\r
+int(20)\r
+int(14)\r
+6> unicast packet\r
+joining source group\r
+bool(true)\r
+int(32)\r
+7> mcast packet from desired source\r
+leaving source group\r
+bool(true)\r
+int(20)\r
+int(14)\r
+8> unicast packet\r
--- /dev/null
+--TEST--\r
+Multicast support: IPv6 receive options (limited)\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('sockets')) {\r
+ die('skip sockets extension not available.');\r
+}\r
+if (!defined('IPPROTO_IPV6')) {\r
+ die('skip IPv6 not available.');\r
+}\r
+$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);\r
+$br = socket_bind($s, '::', 3000);\r
+/* On Linux, there is no route ff00::/8 by default on lo, which makes it\r
+ * troublesome to send multicast traffic from lo, which we must since\r
+ * we're dealing with interface-local traffic... */\r
+$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_GROUP, array(\r
+ "group" => 'ff01::114',\r
+ "interface" => 0,\r
+));\r
+if ($so === false) {\r
+ die('skip unable to join multicast group on any interface.');\r
+}\r
+$r = socket_sendto($s, $m = "testing packet", strlen($m), 0, 'ff01::114', 3000);\r
+if ($r === false) {\r
+ die('skip unable to send multicast packet.');\r
+}\r
+$so = socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array(\r
+ "group" => 'ff01::114',\r
+ "interface" => 0,\r
+));\r
+$so = socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array(\r
+ "group" => 'ff01::114',\r
+ "interface" => 0,\r
+ "source" => '2001::dead:beef',\r
+));\r
+if ($so !== false) {\r
+ die('skip protocol independent multicast API is available.');\r
+}\r
+\r
+--FILE--\r
+<?php\r
+$domain = AF_INET6;\r
+$level = IPPROTO_IPV6;\r
+$interface = 0;\r
+$mcastaddr = 'ff01::114';\r
+$sblock = "?";\r
+\r
+echo "creating send socket\n";\r
+$sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");\r
+var_dump($sends1);\r
+\r
+echo "creating receive socket\n";\r
+$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");\r
+var_dump($s);\r
+$br = socket_bind($s, '::0', 3000) or die("err");\r
+var_dump($br);\r
+\r
+$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+)) or die("err");\r
+var_dump($so);\r
+\r
+$r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $mcastaddr, 3000);\r
+var_dump($r);\r
+$r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort);\r
+var_dump($r, $str, $from);\r
+$sblock = $from;\r
+\r
+$r = socket_sendto($sends1, $m = "initial packet", strlen($m), 0, $mcastaddr, 3000);\r
+var_dump($r);\r
+\r
+$i = 0;\r
+while (($str = socket_read($s, 3000)) !== FALSE) {\r
+ $i++;\r
+ echo "$i> ", $str, "\n";\r
+\r
+if ($i == 1) {\r
+ echo "leaving group\n";\r
+ $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "ignored mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+ $r = socket_sendto($sends1, $m = "unicast packet", strlen($m), 0, "::1", 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 2) {\r
+ echo "re-joining group\n";\r
+ $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\r
+ "group" => $mcastaddr,\r
+ "interface" => $interface,\r
+ ));\r
+ var_dump($so);\r
+ $r = socket_sendto($sends1, $m = "mcast packet", strlen($m), 0, $mcastaddr, 3000);\r
+ var_dump($r);\r
+}\r
+if ($i == 3) {\r
+ break;\r
+}\r
+\r
+}\r
+--EXPECTF--\r
+creating send socket\r
+resource(4) of type (Socket)\r
+creating receive socket\r
+resource(5) of type (Socket)\r
+bool(true)\r
+bool(true)\r
+int(14)\r
+int(14)\r
+string(14) "testing packet"\r
+string(%d) "%s"\r
+int(14)\r
+1> initial packet\r
+leaving group\r
+bool(true)\r
+int(20)\r
+int(14)\r
+2> unicast packet\r
+re-joining group\r
+bool(true)\r
+int(12)\r
+3> mcast packet\r
--- /dev/null
+--TEST--\r
+Multicast support: IPv6 send options\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('sockets')) {\r
+ die('skip sockets extension not available.');\r
+}\r
+if (!defined('IPPROTO_IPV6')) {\r
+ die('skip IPv6 not available.');\r
+}\r
+if (socket_set_option($s, $level, IP_MULTICAST_IF, 1) === false) {\r
+ die("skip interface 1 either doesn't exist or has no ipv6 address");\r
+}\r
+--FILE--\r
+<?php\r
+$domain = AF_INET6;\r
+$level = IPPROTO_IPV6;\r
+$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("err");\r
+\r
+echo "Setting IPV6_MULTICAST_TTL\n";\r
+$r = socket_set_option($s, $level, IPV6_MULTICAST_HOPS, 9);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IPV6_MULTICAST_HOPS);\r
+var_dump($r);\r
+echo "\n";\r
+\r
+echo "Setting IPV6_MULTICAST_LOOP\n";\r
+$r = socket_set_option($s, $level, IPV6_MULTICAST_LOOP, 0);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IPV6_MULTICAST_LOOP);\r
+var_dump($r);\r
+$r = socket_set_option($s, $level, IPV6_MULTICAST_LOOP, 1);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IPV6_MULTICAST_LOOP);\r
+var_dump($r);\r
+echo "\n";\r
+\r
+echo "Setting IPV6_MULTICAST_IF\n";\r
+echo "interface 0:\n";\r
+$r = socket_set_option($s, $level, IPV6_MULTICAST_IF, 0);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IPV6_MULTICAST_IF);\r
+var_dump($r);\r
+echo "interface 1:\n";\r
+$r = socket_set_option($s, $level, IPV6_MULTICAST_IF, 1);\r
+var_dump($r);\r
+$r = socket_get_option($s, $level, IPV6_MULTICAST_IF);\r
+var_dump($r);\r
+echo "\n";\r
+\r
+--EXPECT--\r
+Setting IPV6_MULTICAST_TTL\r
+bool(true)\r
+int(9)\r
+\r
+Setting IPV6_MULTICAST_LOOP\r
+bool(true)\r
+int(0)\r
+bool(true)\r
+int(1)\r
+\r
+Setting IPV6_MULTICAST_IF\r
+interface 0:\r
+bool(true)\r
+int(0)\r
+interface 1:\r
+bool(true)\r
+int(1)\r