-/*\r
- +----------------------------------------------------------------------+\r
- | PHP Version 5 |\r
- +----------------------------------------------------------------------+\r
- | Copyright (c) 1997-2011 The PHP Group |\r
- +----------------------------------------------------------------------+\r
- | This source file is subject to version 3.01 of the PHP license, |\r
- | that is bundled with this package in the file LICENSE, and is |\r
- | available through the world-wide-web at the following url: |\r
- | http://www.php.net/license/3_01.txt |\r
- | If you did not receive a copy of the PHP license and are unable to |\r
- | obtain it through the world-wide-web, please send a note to |\r
- | license@php.net so we can mail you a copy immediately. |\r
- +----------------------------------------------------------------------+\r
- | Authors: Gustavo Lopes <cataphract@php.net> |\r
- +----------------------------------------------------------------------+\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#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
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#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 */