1
0
mirror of https://github.com/php/php-src.git synced 2026-04-02 05:32:28 +02:00

Merge branch 'PHP-5.5'

* PHP-5.5: (40 commits)
  php_strerror in ext/sockets renamed in 5.5
  Move macro back to .c file
  Fix multicast.c not defining errno on Windows
  Fix non-Windows build
  send/recvmsg() support for Windows
  Remove some pre-vista code
  Revert "Payload of HOPLIMIT/TCLASS are 8-bit"
  Ensure memory is initialized
  Payload of HOPLIMIT/TCLASS are 8-bit
  Fix buf in string -> int conv.
  Build fixes; accept names for if_index
  Refactoring: move stuff to new conversions.c
  Support sticky IPV6_PKTINFO
  Rename some functions for consistency
  Destroy ancillary registry on shutdown
  Move some multicast stuff to multicast.c
  Fix mcast_ipv6_send test
  Check return of fstat()
  Fix build on Mac OS X
  Register extra MSG_* constants
  ...
This commit is contained in:
Gustavo Lopes
2013-02-02 18:42:01 +01:00
30 changed files with 3456 additions and 652 deletions

View File

@@ -43,6 +43,6 @@ if test "$PHP_SOCKETS" != "no"; then
AC_DEFINE(HAVE_SA_SS_FAMILY,1,[Whether you have sockaddr_storage.ss_family])
fi
PHP_NEW_EXTENSION([sockets], [sockets.c multicast.c], [$ext_shared])
PHP_NEW_EXTENSION([sockets], [sockets.c multicast.c conversions.c sockaddr_conv.c sendrecvmsg.c], [$ext_shared])
PHP_INSTALL_HEADERS([ext/sockets/], [php_sockets.h])
fi

View File

@@ -7,7 +7,7 @@ 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 multicast.c');
EXTENSION('sockets', 'sockets.c multicast.c conversions.c sockaddr_conv.c sendrecvmsg.c');
AC_DEFINE('HAVE_SOCKETS', 1);
PHP_INSTALL_HEADERS("ext/sockets", "php_sockets.h");
} else {

1528
ext/sockets/conversions.c Normal file

File diff suppressed because it is too large Load Diff

84
ext/sockets/conversions.h Normal file
View File

@@ -0,0 +1,84 @@
#ifndef PHP_SOCK_CONVERSIONS_H
#define PHP_SOCK_CONVERSIONS_H 1
#include <php.h>
#ifndef PHP_WIN32
# include <netinet/in.h>
# include <sys/socket.h>
#else
# include <Ws2tcpip.h>
#endif
#include "php_sockets.h"
/* TYPE DEFINITIONS */
struct err_s {
int has_error;
char *msg;
int level;
int should_free;
};
struct key_value {
const char *key;
unsigned key_size;
void *value;
};
/* the complete types of these two are not relevant to the outside */
typedef struct _ser_context ser_context;
typedef struct _res_context res_context;
#define KEY_RECVMSG_RET "recvmsg_ret"
typedef void (from_zval_write_field)(const zval *arr_value, char *field, ser_context *ctx);
typedef void (to_zval_read_field)(const char *data, zval *zv, res_context *ctx);
/* VARIABLE DECLARATIONS */
extern const struct key_value empty_key_value_list[];
/* AUX FUNCTIONS */
void err_msg_dispose(struct err_s *err TSRMLS_DC);
void allocations_dispose(zend_llist **allocations);
/* CONVERSION FUNCTIONS */
void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx);
void to_zval_read_int(const char *data, zval *zv, res_context *ctx);
#ifdef IPV6_PKTINFO
void from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx);
void to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx);
#endif
#ifdef SO_PASSCRED
void from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx);
void to_zval_read_ucred(const char *data, zval *zv, res_context *ctx);
#endif
#ifdef SCM_RIGHTS
size_t calculate_scm_rights_space(const zval *arr, ser_context *ctx);
void from_zval_write_fd_array(const zval *arr, char *int_arr, ser_context *ctx);
void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx);
#endif
void from_zval_write_msghdr_send(const zval *container, char *msghdr_c, ser_context *ctx);
void from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx);
void to_zval_read_msghdr(const char *msghdr_c, zval *zv, res_context *ctx);
/* ENTRY POINTS FOR CONVERSIONS */
void *from_zval_run_conversions(const zval *container,
php_socket *sock,
from_zval_write_field *writer,
size_t struct_size,
const char *top_name,
zend_llist **allocations /* out */,
struct err_s *err /* in/out */);
zval *to_zval_run_conversions(const char *structure,
to_zval_read_field *reader,
const char *top_name,
const struct key_value *key_value_pairs,
struct err_s *err);
#endif

View File

@@ -28,19 +28,7 @@
#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
# if _WIN32_WINNT >= 0x0600
# define HAVE_IF_NAMETOINDEX 1
# endif
# include "windows_common.h"
#else
#include <sys/socket.h>
#include <sys/ioctl.h>
@@ -54,6 +42,7 @@
#include "php_sockets.h"
#include "multicast.h"
#include "sockaddr_conv.h"
#include "main/php_network.h"
@@ -76,6 +65,309 @@ 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
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;
}
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);
#ifdef HAS_MCAST_EXT
int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
struct sockaddr *, socklen_t, unsigned TSRMLS_DC);
#endif
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;
}
#ifdef HAS_MCAST_EXT
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;
}
#endif
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;
}
int php_do_setsockopt_ip_mcast(php_socket *php_sock,
int level,
int optname,
zval **arg4 TSRMLS_DC)
{
unsigned int if_index;
struct in_addr if_addr;
void *opt_ptr;
socklen_t optlen;
unsigned char ipv4_mcast_ttl_lback;
int retval;
switch (optname) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
#ifdef HAS_MCAST_EXT
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
#endif
if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
return FAILURE;
} else {
return SUCCESS;
}
case IP_MULTICAST_IF:
if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
return FAILURE;
}
if (php_if_index_to_addr4(if_index, php_sock, &if_addr TSRMLS_CC) == FAILURE) {
return FAILURE;
}
opt_ptr = &if_addr;
optlen = sizeof(if_addr);
goto dosockopt;
case IP_MULTICAST_LOOP:
convert_to_boolean_ex(arg4);
goto ipv4_loop_ttl;
case IP_MULTICAST_TTL:
convert_to_long_ex(arg4);
if (Z_LVAL_PP(arg4) < 0L || Z_LVAL_PP(arg4) > 255L) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Expected a value between 0 and 255");
return FAILURE;
}
ipv4_loop_ttl:
ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_PP(arg4);
opt_ptr = &ipv4_mcast_ttl_lback;
optlen = sizeof(ipv4_mcast_ttl_lback);
goto dosockopt;
}
return 1;
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);
return FAILURE;
}
return SUCCESS;
}
int php_do_setsockopt_ipv6_mcast(php_socket *php_sock,
int level,
int optname,
zval **arg4 TSRMLS_DC)
{
unsigned int if_index;
void *opt_ptr;
socklen_t optlen;
int ov;
int retval;
switch (optname) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
#ifdef HAS_MCAST_EXT
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
#endif
if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
return FAILURE;
} else {
return SUCCESS;
}
case IPV6_MULTICAST_IF:
if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
return FAILURE;
}
opt_ptr = &if_index;
optlen = sizeof(if_index);
goto dosockopt;
case IPV6_MULTICAST_LOOP:
convert_to_boolean_ex(arg4);
goto ipv6_loop_hops;
case IPV6_MULTICAST_HOPS:
convert_to_long_ex(arg4);
if (Z_LVAL_PP(arg4) < -1L || Z_LVAL_PP(arg4) > 255L) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Expected a value between -1 and 255");
return FAILURE;
}
ipv6_loop_hops:
ov = (int) Z_LVAL_PP(arg4);
opt_ptr = &ov;
optlen = sizeof(ov);
goto dosockopt;
}
return 1; /* not handled */
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);
return FAILURE;
}
return SUCCESS;
}
int php_mcast_join(
php_socket *sock,
int level,
@@ -157,21 +449,21 @@ static int _php_mcast_join_leave(
{
#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));
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)
@@ -188,12 +480,12 @@ static int _php_mcast_join_leave(
#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));
@@ -221,26 +513,26 @@ static int _php_mcast_source_op(
{
#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)
@@ -249,7 +541,7 @@ static int _php_mcast_source_op(
} 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));
}
@@ -283,7 +575,7 @@ static int _php_source_op_to_rfc3678_op(enum source_op sop)
case UNBLOCK_SOURCE:
return MCAST_UNBLOCK_SOURCE;
}
assert(0);
return 0;
}
@@ -300,7 +592,7 @@ static const char *_php_source_op_to_string(enum source_op sop)
case UNBLOCK_SOURCE:
return "MCAST_UNBLOCK_SOURCE";
}
assert(0);
return "";
}
@@ -317,7 +609,7 @@ static int _php_source_op_to_ipv4_op(enum source_op sop)
case UNBLOCK_SOURCE:
return IP_UNBLOCK_SOURCE;
}
assert(0);
return 0;
}
@@ -416,16 +708,16 @@ retry:
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 !defined(ifr_ifindex) && defined(ifr_index)
#define ifr_ifindex ifr_index
#endif
#if defined(SIOCGIFNAME)
if_req.ifr_ifindex = if_index;
if (ioctl(php_sock->bsd_socket, SIOCGIFNAME, &if_req) == -1) {
@@ -438,13 +730,13 @@ int php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struct in_add
"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;
@@ -458,25 +750,25 @@ int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *i
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;
@@ -486,15 +778,15 @@ int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *i
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
@@ -502,7 +794,7 @@ int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *i
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)) {
@@ -537,7 +829,7 @@ int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *i
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);

View File

@@ -27,6 +27,16 @@
#define HAS_MCAST_EXT 1
#endif
int php_do_setsockopt_ip_mcast(php_socket *php_sock,
int level,
int optname,
zval **arg4 TSRMLS_DC);
int php_do_setsockopt_ipv6_mcast(php_socket *php_sock,
int level,
int optname,
zval **arg4 TSRMLS_DC);
int php_if_index_to_addr4(
unsigned if_index,
php_socket *php_sock,

View File

@@ -26,52 +26,19 @@
#if HAVE_SOCKETS
#include <php.h>
extern zend_module_entry sockets_module_entry;
#define phpext_sockets_ptr &sockets_module_entry
#ifdef PHP_WIN32
#include <winsock.h>
#include <Winsock2.h>
#else
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#endif
PHP_MINIT_FUNCTION(sockets);
PHP_MINFO_FUNCTION(sockets);
PHP_RSHUTDOWN_FUNCTION(sockets);
PHP_FUNCTION(socket_select);
PHP_FUNCTION(socket_create_listen);
#ifdef HAVE_SOCKETPAIR
PHP_FUNCTION(socket_create_pair);
#endif
PHP_FUNCTION(socket_accept);
PHP_FUNCTION(socket_set_nonblock);
PHP_FUNCTION(socket_set_block);
PHP_FUNCTION(socket_listen);
PHP_FUNCTION(socket_close);
PHP_FUNCTION(socket_write);
PHP_FUNCTION(socket_read);
PHP_FUNCTION(socket_getsockname);
PHP_FUNCTION(socket_getpeername);
PHP_FUNCTION(socket_create);
PHP_FUNCTION(socket_connect);
PHP_FUNCTION(socket_strerror);
PHP_FUNCTION(socket_bind);
PHP_FUNCTION(socket_recv);
PHP_FUNCTION(socket_send);
PHP_FUNCTION(socket_recvfrom);
PHP_FUNCTION(socket_sendto);
PHP_FUNCTION(socket_get_option);
PHP_FUNCTION(socket_set_option);
#ifdef HAVE_SHUTDOWN
PHP_FUNCTION(socket_shutdown);
#endif
PHP_FUNCTION(socket_last_error);
PHP_FUNCTION(socket_clear_error);
PHP_FUNCTION(socket_import_stream);
#ifndef PHP_WIN32
typedef int PHP_SOCKET;
# define PHP_SOCKETS_API PHPAPI
@@ -99,13 +66,15 @@ PHP_SOCKETS_API int php_sockets_le_socket(void);
#define php_sockets_le_socket_name "Socket"
/* Prototypes */
#ifdef ilia_0 /* not needed, only causes a compiler warning */
static int php_open_listen_sock(php_socket **php_sock, int port, int backlog TSRMLS_DC);
static int php_accept_connect(php_socket *in_sock, php_socket **new_sock, struct sockaddr *la TSRMLS_DC);
static int php_read(php_socket *sock, void *buf, size_t maxlen, int flags);
static char *php_strerror(int error TSRMLS_DC);
#endif
#define PHP_SOCKET_ERROR(socket, msg, errn) \
do { \
int _err = (errn); /* save value to avoid repeated calls to WSAGetLastError() on Windows */ \
(socket)->error = _err; \
SOCKETS_G(last_error) = _err; \
if (_err != EAGAIN && _err != EWOULDBLOCK && _err != EINPROGRESS) { \
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s [%d]: %s", msg, _err, sockets_strerror(_err TSRMLS_CC)); \
} \
} while (0)
ZEND_BEGIN_MODULE_GLOBALS(sockets)
int last_error;
@@ -118,6 +87,17 @@ ZEND_END_MODULE_GLOBALS(sockets)
#define SOCKETS_G(v) (sockets_globals.v)
#endif
ZEND_EXTERN_MODULE_GLOBALS(sockets);
enum sockopt_return {
SOCKOPT_ERROR,
SOCKOPT_CONTINUE,
SOCKOPT_SUCCESS
};
char *sockets_strerror(int error TSRMLS_DC);
php_socket *socket_import_file_descriptor(PHP_SOCKET sock TSRMLS_DC);
#else
#define phpext_sockets_ptr NULL
#endif

448
ext/sockets/sendrecvmsg.c Normal file
View File

@@ -0,0 +1,448 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2012 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> |
+----------------------------------------------------------------------+
*/
#include <php.h>
#include "php_sockets.h"
#include "sendrecvmsg.h"
#include "conversions.h"
#include <limits.h>
#include <Zend/zend_llist.h>
#ifdef ZTS
#include <TSRM/TSRM.h>
#endif
#define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
#define DEFAULT_BUFF_SIZE 8192
#define MAX_ARRAY_KEY_SIZE 128
#ifdef PHP_WIN32
#include "windows_common.h"
#include <Mswsock.h>
#define IPV6_RECVPKTINFO IPV6_PKTINFO
#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
#define msghdr _WSAMSG
static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
inline ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
{
DWORD recvd = 0,
bytesReturned;
if (WSARecvMsg == NULL) {
int res = WSAIoctl((SOCKET) sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
&WSARecvMsg, sizeof(WSARecvMsg),
&bytesReturned, NULL, NULL);
if (res != 0) {
return -1;
}
}
msg->dwFlags = (DWORD)flags;
return WSARecvMsg((SOCKET)sockfd, msg, &recvd, NULL, NULL) == 0
? (ssize_t)recvd
: -1;
}
inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
DWORD sent = 0;
return WSASendMsg((SOCKET)sockfd, (struct msghdr*)msg, (DWORD)flags, &sent, NULL, NULL) == 0
? (ssize_t)sent
: -1;
}
#endif
#define LONG_CHECK_VALID_INT(l) \
do { \
if ((l) < INT_MIN && (l) > INT_MAX) { \
php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The value %ld does not fit inside " \
"the boundaries of a native integer", (l)); \
return; \
} \
} while (0)
static struct {
int initialized;
HashTable ht;
} ancillary_registry;
#ifdef ZTS
static MUTEX_T ancillary_mutex;
#endif
static void init_ancillary_registry(void)
{
ancillary_reg_entry entry;
anc_reg_key key;
ancillary_registry.initialized = 1;
zend_hash_init(&ancillary_registry.ht, 32, NULL, NULL, 1);
#define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
entry.size = sizev; \
entry.var_el_size = var_size; \
entry.calc_space = calc; \
entry.from_array = from; \
entry.to_array = to; \
key.cmsg_level = level; \
key.cmsg_type = type; \
zend_hash_update(&ancillary_registry.ht, (char*)&key, sizeof(key), \
(void*)&entry, sizeof(entry), NULL)
#ifdef IPV6_PKTINFO
PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
#endif
#ifdef IPV6_HOPLIMIT
PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
to_zval_read_int, IPPROTO_IPV6, IPV6_HOPLIMIT);
#endif
PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
to_zval_read_int, IPPROTO_IPV6, IPV6_TCLASS);
#ifdef SO_PASSCRED
PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
#endif
#ifdef SCM_RIGHTS
PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
#endif
}
static void destroy_ancillary_registry(void)
{
if (ancillary_registry.initialized) {
zend_hash_destroy(&ancillary_registry.ht);
ancillary_registry.initialized = 0;
}
}
ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
{
anc_reg_key key = { cmsg_level, msg_type };
ancillary_reg_entry *entry;
#ifdef ZTS
tsrm_mutex_lock(ancillary_mutex);
#endif
if (!ancillary_registry.initialized) {
init_ancillary_registry();
}
#ifdef ZTS
tsrm_mutex_unlock(ancillary_mutex);
#endif
if (zend_hash_find(&ancillary_registry.ht, (char*)&key, sizeof(key),
(void**)&entry) == SUCCESS) {
return entry;
} else {
return NULL;
}
}
PHP_FUNCTION(socket_sendmsg)
{
zval *zsocket,
*zmsg;
long flags = 0;
php_socket *php_sock;
struct msghdr *msghdr;
zend_llist *allocations;
struct err_s err = {0};
ssize_t res;
/* zmsg should be passed by ref */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|l", &zsocket, &zmsg, &flags) == FAILURE) {
return;
}
LONG_CHECK_VALID_INT(flags);
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &zsocket, -1,
php_sockets_le_socket_name, php_sockets_le_socket());
msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_send,
sizeof(*msghdr), "msghdr", &allocations, &err);
if (err.has_error) {
err_msg_dispose(&err TSRMLS_CC);
RETURN_FALSE;
}
res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
if (res != -1) {
zend_llist_destroy(allocations);
efree(allocations);
RETURN_LONG((long)res);
} else {
PHP_SOCKET_ERROR(php_sock, "error in sendmsg", errno);
RETURN_FALSE;
}
}
PHP_FUNCTION(socket_recvmsg)
{
zval *zsocket,
*zmsg;
long flags = 0;
php_socket *php_sock;
ssize_t res;
struct msghdr *msghdr;
zend_llist *allocations;
struct err_s err = {0};
//ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|l",
&zsocket, &zmsg, &flags) == FAILURE) {
return;
}
LONG_CHECK_VALID_INT(flags);
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &zsocket, -1,
php_sockets_le_socket_name, php_sockets_le_socket());
msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_recv,
sizeof(*msghdr), "msghdr", &allocations, &err);
if (err.has_error) {
err_msg_dispose(&err TSRMLS_CC);
RETURN_FALSE;
}
res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
if (res != -1) {
zval *zres;
struct key_value kv[] = {
{KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET), &res},
{0}
};
zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
"msghdr", kv, &err);
/* we don;t need msghdr anymore; free it */
msghdr = NULL;
allocations_dispose(&allocations);
zval_dtor(zmsg);
if (!err.has_error) {
ZVAL_COPY_VALUE(zmsg, zres);
efree(zres); /* only shallow destruction */
} else {
err_msg_dispose(&err TSRMLS_CC);
ZVAL_FALSE(zmsg);
/* no need to destroy/free zres -- it's NULL in this circumstance */
assert(zres == NULL);
}
} else {
SOCKETS_G(last_error) = errno;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "error in recvmsg [%d]: %s",
errno, sockets_strerror(errno TSRMLS_CC));
RETURN_FALSE;
}
RETURN_LONG((long)res);
}
PHP_FUNCTION(socket_cmsg_space)
{
long level,
type,
n = 0;
ancillary_reg_entry *entry;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|l",
&level, &type, &n) == FAILURE) {
return;
}
LONG_CHECK_VALID_INT(level);
LONG_CHECK_VALID_INT(type);
LONG_CHECK_VALID_INT(n);
if (n < 0) {
php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The third argument "
"cannot be negative");
return;
}
entry = get_ancillary_reg_entry(level, type);
if (entry == NULL) {
php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The pair level %ld/type %ld is "
"not supported by PHP", level, type);
return;
}
if (entry->var_el_size > 0 && n > (LONG_MAX - (long)entry->size -
(long)CMSG_SPACE(0) - 15L) / entry->var_el_size) {
/* the -15 is to account for any padding CMSG_SPACE may add after the data */
php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The value for the "
"third argument (%ld) is too large", n);
return;
}
RETURN_LONG((long)CMSG_SPACE(entry->size + n * entry->var_el_size));
}
int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4 TSRMLS_DC)
{
struct err_s err = {0};
zend_llist *allocations = NULL;
void *opt_ptr;
socklen_t optlen;
int retval;
assert(level == IPPROTO_IPV6);
switch (optname) {
#ifdef IPV6_PKTINFO
case IPV6_PKTINFO:
#ifdef PHP_WIN32
if (Z_TYPE_PP(arg4) == IS_ARRAY) {
php_error_docref0(NULL TSRMLS_CC, E_WARNING, "Windows does not "
"support sticky IPV6_PKTINFO");
return FAILURE;
} else {
/* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
* for the same effect. We define IPV6_RECVPKTINFO to be
* IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
return 1;
}
#endif
opt_ptr = from_zval_run_conversions(*arg4, php_sock, from_zval_write_in6_pktinfo,
sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
if (err.has_error) {
err_msg_dispose(&err TSRMLS_CC);
return FAILURE;
}
optlen = sizeof(struct in6_pktinfo);
goto dosockopt;
#endif
}
/* we also support IPV6_TCLASS, but that can be handled by the default
* integer optval handling in the caller */
return 1;
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);
}
allocations_dispose(&allocations);
return retval != 0 ? FAILURE : SUCCESS;
}
int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result TSRMLS_DC)
{
struct err_s err = {0};
void *buffer;
socklen_t size;
int res;
to_zval_read_field *reader;
assert(level == IPPROTO_IPV6);
switch (optname) {
#ifdef IPV6_PKTINFO
case IPV6_PKTINFO:
size = sizeof(struct in6_pktinfo);
reader = &to_zval_read_in6_pktinfo;
break;
#endif
default:
return 1;
}
buffer = ecalloc(1, size);
res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
if (res != 0) {
PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
} else {
zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
empty_key_value_list, &err);
if (err.has_error) {
err_msg_dispose(&err TSRMLS_CC);
res = -1;
} else {
ZVAL_COPY_VALUE(result, zv);
efree(zv);
}
}
efree(buffer);
return res == 0 ? SUCCESS : FAILURE;
}
void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
{
/* IPv6 ancillary data */
#ifdef IPV6_RECVPKTINFO
REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_PKTINFO", IPV6_PKTINFO, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef IPV6_RECVHOPLIMIT
REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT", IPV6_HOPLIMIT, CONST_CS | CONST_PERSISTENT);
#endif
/* would require some effort:
REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR", IPV6_RECVRTHDR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, CONST_CS | CONST_PERSISTENT);
*/
REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS", IPV6_RECVTCLASS, CONST_CS | CONST_PERSISTENT);
/*
REGISTER_LONG_CONSTANT("IPV6_RTHDR", IPV6_RTHDR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_HOPOPTS", IPV6_HOPOPTS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IPV6_DSTOPTS", IPV6_DSTOPTS, CONST_CS | CONST_PERSISTENT);
*/
REGISTER_LONG_CONSTANT("IPV6_TCLASS", IPV6_TCLASS, CONST_CS | CONST_PERSISTENT);
#ifdef SCM_RIGHTS
REGISTER_LONG_CONSTANT("SCM_RIGHTS", SCM_RIGHTS, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef SO_PASSCRED
REGISTER_LONG_CONSTANT("SCM_CREDENTIALS", SCM_CREDENTIALS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SO_PASSCRED", SO_PASSCRED, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef ZTS
ancillary_mutex = tsrm_mutex_alloc();
#endif
}
void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
{
#ifdef ZTS
tsrm_mutex_free(ancillary_mutex);
#endif
destroy_ancillary_registry();
}

36
ext/sockets/sendrecvmsg.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef PHP_SENDRECVMSG_H
#define PHP_SENDRECVMSG_H 1
#include <php.h>
#include "conversions.h"
/* for sockets.c */
PHP_FUNCTION(socket_sendmsg);
PHP_FUNCTION(socket_recvmsg);
PHP_FUNCTION(socket_cmsg_space);
void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS);
void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS);
int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4 TSRMLS_DC);
int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result TSRMLS_DC);
/* for conversions.c */
typedef struct {
int cmsg_level; /* originating protocol */
int cmsg_type; /* protocol-specific type */
} anc_reg_key;
typedef size_t (calculate_req_space)(const zval *value, ser_context *ctx);
typedef struct {
socklen_t size; /* size of native structure */
socklen_t var_el_size; /* size of repeatable component */
calculate_req_space *calc_space;
from_zval_write_field *from_array;
to_zval_read_field *to_array;
} ancillary_reg_entry;
ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type);
#endif

119
ext/sockets/sockaddr_conv.c Normal file
View File

@@ -0,0 +1,119 @@
#include <php.h>
#include <php_network.h>
#include "php_sockets.h"
#ifdef PHP_WIN32
#include "windows_common.h"
#else
#include <netdb.h>
#include <arpa/inet.h>
#endif
#if HAVE_IPV6
/* Sets addr by hostname, or by ip in string form (AF_INET6) */
int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
struct in6_addr tmp;
#if HAVE_GETADDRINFO
struct addrinfo hints;
struct addrinfo *addrinfo = NULL;
#endif
if (inet_pton(AF_INET6, string, &tmp)) {
memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr));
} else {
#if HAVE_GETADDRINFO
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
getaddrinfo(string, NULL, &hints, &addrinfo);
if (!addrinfo) {
#ifdef PHP_WIN32
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
#else
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
#endif
return 0;
}
if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket");
freeaddrinfo(addrinfo);
return 0;
}
memcpy(&(sin6->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr));
freeaddrinfo(addrinfo);
#else
/* No IPv6 specific hostname resolution is available on this system? */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system");
return 0;
#endif
}
return 1;
}
/* }}} */
#endif
/* Sets addr by hostname, or by ip in string form (AF_INET) */
int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
struct in_addr tmp;
struct hostent *host_entry;
if (inet_aton(string, &tmp)) {
sin->sin_addr.s_addr = tmp.s_addr;
} else {
if (! (host_entry = gethostbyname(string))) {
/* Note: < -10000 indicates a host lookup error */
#ifdef PHP_WIN32
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
#else
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
#endif
return 0;
}
if (host_entry->h_addrtype != AF_INET) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET domain returned on AF_INET socket");
return 0;
}
memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length);
}
return 1;
}
/* }}} */
/* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6,
* depending on the socket) */
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;
}

View File

@@ -0,0 +1,31 @@
#ifndef PHP_SOCKADR_CONV_H
#define PHP_SOCKADR_CONV_H
#include <php_network.h>
#include "php_sockets.h" /* php_socket */
#ifndef PHP_WIN32
# include <netinet/in.h>
#else
# include <Winsock2.h>
#endif
/*
* Convert an IPv6 literal or a hostname info a sockaddr_in6.
* The IPv6 literal can be a IPv4 mapped address (like ::ffff:127.0.0.1).
* If the hostname yields no IPv6 addresses, a mapped IPv4 address may be returned (AI_V4MAPPED)
*/
int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock TSRMLS_DC);
/*
* Convert an IPv4 literal or a hostname into a sockaddr_in.
*/
int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock TSRMLS_DC);
/*
* Calls either php_set_inet6_addr() or php_set_inet_addr(), depending on the type of the socket.
*/
int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, php_socket *php_sock TSRMLS_DC);
#endif

View File

@@ -35,32 +35,12 @@
#include "ext/standard/info.h"
#include "php_ini.h"
#ifdef PHP_WIN32
# include "win32/inet.h"
# include <winsock2.h>
# include "windows_common.h"
# include <win32/inet.h>
# include <windows.h>
# include <Ws2tcpip.h>
# include "php_sockets.h"
# include "win32/sockets.h"
# define IS_INVALID_SOCKET(a) (a->bsd_socket == INVALID_SOCKET)
# ifdef EPROTONOSUPPORT
# undef EPROTONOSUPPORT
# endif
# ifdef ECONNRESET
# undef ECONNRESET
# endif
# define EPROTONOSUPPORT WSAEPROTONOSUPPORT
# define ECONNRESET WSAECONNRESET
# ifdef errno
# undef errno
# endif
# define errno WSAGetLastError()
# define h_errno WSAGetLastError()
# define set_errno(a) WSASetLastError(a)
# define close(a) closesocket(a)
# include <IPHlpApi.h>
# if _WIN32_WINNT >= 0x0600
# define HAVE_IF_NAMETOINDEX 1
# endif
# include <win32/sockets.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
@@ -86,7 +66,9 @@
# endif
#endif
#include "sockaddr_conv.h"
#include "multicast.h"
#include "sendrecvmsg.h"
ZEND_DECLARE_MODULE_GLOBALS(sockets)
static PHP_GINIT_FUNCTION(sockets);
@@ -113,15 +95,9 @@ static PHP_GINIT_FUNCTION(sockets);
#define PF_INET AF_INET
#endif
static char *php_strerror(int error TSRMLS_DC);
#define PHP_NORMAL_READ 0x0001
#define PHP_BINARY_READ 0x0002
#define PHP_SOCKET_ERROR(socket,msg,errn) socket->error = errn; \
SOCKETS_G(last_error) = errn; \
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s [%d]: %s", msg, errn, php_strerror(errn TSRMLS_CC))
static int le_socket;
#define le_socket_name php_sockets_le_socket_name
@@ -274,12 +250,64 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_clear_error, 0, 0, 0)
ZEND_ARG_INFO(0, socket)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_import_stream, 0, 0, 1)
ZEND_ARG_INFO(0, stream)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_sendmsg, 0, 0, 3)
ZEND_ARG_INFO(0, socket)
ZEND_ARG_INFO(0, msghdr)
ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_recvmsg, 0, 0, 3)
ZEND_ARG_INFO(0, socket)
ZEND_ARG_INFO(1, msghdr)
ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_cmsg_space, 0, 0, 2)
ZEND_ARG_INFO(0, level)
ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()
/* }}} */
PHP_MINIT_FUNCTION(sockets);
PHP_MINFO_FUNCTION(sockets);
PHP_RSHUTDOWN_FUNCTION(sockets);
PHP_FUNCTION(socket_select);
PHP_FUNCTION(socket_create_listen);
#ifdef HAVE_SOCKETPAIR
PHP_FUNCTION(socket_create_pair);
#endif
PHP_FUNCTION(socket_accept);
PHP_FUNCTION(socket_set_nonblock);
PHP_FUNCTION(socket_set_block);
PHP_FUNCTION(socket_listen);
PHP_FUNCTION(socket_close);
PHP_FUNCTION(socket_write);
PHP_FUNCTION(socket_read);
PHP_FUNCTION(socket_getsockname);
PHP_FUNCTION(socket_getpeername);
PHP_FUNCTION(socket_create);
PHP_FUNCTION(socket_connect);
PHP_FUNCTION(socket_strerror);
PHP_FUNCTION(socket_bind);
PHP_FUNCTION(socket_recv);
PHP_FUNCTION(socket_send);
PHP_FUNCTION(socket_recvfrom);
PHP_FUNCTION(socket_sendto);
PHP_FUNCTION(socket_get_option);
PHP_FUNCTION(socket_set_option);
#ifdef HAVE_SHUTDOWN
PHP_FUNCTION(socket_shutdown);
#endif
PHP_FUNCTION(socket_last_error);
PHP_FUNCTION(socket_clear_error);
PHP_FUNCTION(socket_import_stream);
/* {{{ sockets_functions[]
*/
const zend_function_entry sockets_functions[] = {
@@ -313,6 +341,9 @@ const zend_function_entry sockets_functions[] = {
PHP_FE(socket_last_error, arginfo_socket_last_error)
PHP_FE(socket_clear_error, arginfo_socket_clear_error)
PHP_FE(socket_import_stream, arginfo_socket_import_stream)
PHP_FE(socket_sendmsg, arginfo_socket_sendmsg)
PHP_FE(socket_recvmsg, arginfo_socket_recvmsg)
PHP_FE(socket_cmsg_space, arginfo_socket_cmsg_space)
/* for downwards compatability */
PHP_FALIAS(socket_getopt, socket_get_option, arginfo_socket_get_option)
@@ -358,13 +389,13 @@ PHP_SOCKETS_API int php_sockets_le_socket(void) /* {{{ */
static php_socket *php_create_socket(void) /* {{{ */
{
php_socket *php_sock = emalloc(sizeof *php_sock);
php_sock->bsd_socket = -1; /* invalid socket */
php_sock->type = PF_UNSPEC;
php_sock->error = 0;
php_sock->blocking = 1;
php_sock->zstream = NULL;
return php_sock;
}
/* }}} */
@@ -521,7 +552,7 @@ static int php_read(php_socket *sock, void *buf, size_t maxlen, int flags)
}
/* }}} */
static char *php_strerror(int error TSRMLS_DC) /* {{{ */
char *sockets_strerror(int error TSRMLS_DC) /* {{{ */
{
const char *buf;
@@ -568,188 +599,6 @@ static char *php_strerror(int error TSRMLS_DC) /* {{{ */
}
/* }}} */
#if HAVE_IPV6
/* Sets addr by hostname, or by ip in string form (AF_INET6) */
static int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
struct in6_addr tmp;
#if HAVE_GETADDRINFO
struct addrinfo hints;
struct addrinfo *addrinfo = NULL;
#endif
if (inet_pton(AF_INET6, string, &tmp)) {
memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr));
} else {
#if HAVE_GETADDRINFO
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = PF_INET6;
getaddrinfo(string, NULL, &hints, &addrinfo);
if (!addrinfo) {
#ifdef PHP_WIN32
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
#else
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
#endif
return 0;
}
if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket");
freeaddrinfo(addrinfo);
return 0;
}
memcpy(&(sin6->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr));
freeaddrinfo(addrinfo);
#else
/* No IPv6 specific hostname resolution is available on this system? */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system");
return 0;
#endif
}
return 1;
}
/* }}} */
#endif
/* Sets addr by hostname, or by ip in string form (AF_INET) */
static int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
struct in_addr tmp;
struct hostent *host_entry;
if (inet_aton(string, &tmp)) {
sin->sin_addr.s_addr = tmp.s_addr;
} else {
if (! (host_entry = gethostbyname(string))) {
/* Note: < -10000 indicates a host lookup error */
#ifdef PHP_WIN32
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
#else
PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
#endif
return 0;
}
if (host_entry->h_addrtype != AF_INET) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET domain returned on AF_INET socket");
return 0;
}
memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length);
}
return 1;
}
/* }}} */
/* 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)
{
@@ -774,11 +623,11 @@ PHP_MINIT_FUNCTION(sockets)
REGISTER_LONG_CONSTANT("SOCK_RAW", SOCK_RAW, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SOCK_SEQPACKET",SOCK_SEQPACKET, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SOCK_RDM", SOCK_RDM, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MSG_OOB", MSG_OOB, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MSG_WAITALL", MSG_WAITALL, CONST_CS | CONST_PERSISTENT);
#ifdef MSG_DONTWAIT
REGISTER_LONG_CONSTANT("MSG_DONTWAIT", MSG_DONTWAIT, CONST_CS | CONST_PERSISTENT);
#endif
REGISTER_LONG_CONSTANT("MSG_CTRUNC", MSG_CTRUNC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MSG_TRUNC", MSG_TRUNC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MSG_PEEK", MSG_PEEK, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MSG_DONTROUTE", MSG_DONTROUTE, CONST_CS | CONST_PERSISTENT);
#ifdef MSG_EOR
@@ -787,6 +636,29 @@ PHP_MINIT_FUNCTION(sockets)
#ifdef MSG_EOF
REGISTER_LONG_CONSTANT("MSG_EOF", MSG_EOF, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef MSG_CONFIRM
REGISTER_LONG_CONSTANT("MSG_CONFIRM", MSG_CONFIRM, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef MSG_ERRQUEUE
REGISTER_LONG_CONSTANT("MSG_ERRQUEUE", MSG_ERRQUEUE, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef MSG_NOSIGNAL
REGISTER_LONG_CONSTANT("MSG_NOSIGNAL", MSG_NOSIGNAL, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef MSG_DONTWAIT
REGISTER_LONG_CONSTANT("MSG_DONTWAIT", MSG_DONTWAIT, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef MSG_MORE
REGISTER_LONG_CONSTANT("MSG_MORE", MSG_MORE, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef MSG_WAITFORONE
REGISTER_LONG_CONSTANT("MSG_WAITFORONE",MSG_WAITFORONE, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef MSG_CMSG_CLOEXEC
REGISTER_LONG_CONSTANT("MSG_CMSG_CLOEXEC",MSG_CMSG_CLOEXEC,CONST_CS | CONST_PERSISTENT);
#endif
REGISTER_LONG_CONSTANT("SO_DEBUG", SO_DEBUG, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SO_REUSEADDR", SO_REUSEADDR, CONST_CS | CONST_PERSISTENT);
#ifdef SO_REUSEPORT
@@ -804,6 +676,9 @@ PHP_MINIT_FUNCTION(sockets)
REGISTER_LONG_CONSTANT("SO_SNDTIMEO", SO_SNDTIMEO, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SO_RCVTIMEO", SO_RCVTIMEO, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SO_TYPE", SO_TYPE, CONST_CS | CONST_PERSISTENT);
#ifdef SO_FAMILY
REGISTER_LONG_CONSTANT("SO_FAMILY", SO_FAMILY, CONST_CS | CONST_PERSISTENT);
#endif
REGISTER_LONG_CONSTANT("SO_ERROR", SO_ERROR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SOL_SOCKET", SOL_SOCKET, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SOMAXCONN", SOMAXCONN, CONST_CS | CONST_PERSISTENT);
@@ -823,7 +698,7 @@ PHP_MINIT_FUNCTION(sockets)
#define MCAST_LEAVE_SOURCE_GROUP IP_DROP_SOURCE_MEMBERSHIP
#endif
#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);
#ifdef HAS_MCAST_EXT
@@ -856,6 +731,12 @@ PHP_MINIT_FUNCTION(sockets)
REGISTER_LONG_CONSTANT("SOL_TCP", IPPROTO_TCP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SOL_UDP", IPPROTO_UDP, CONST_CS | CONST_PERSISTENT);
#if HAVE_IPV6
REGISTER_LONG_CONSTANT("IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS, CONST_CS | CONST_PERSISTENT);
#endif
php_socket_sendrecvmsg_init(INIT_FUNC_ARGS_PASSTHRU);
return SUCCESS;
}
/* }}} */
@@ -877,6 +758,7 @@ PHP_RSHUTDOWN_FUNCTION(sockets)
efree(SOCKETS_G(strerror_buf));
SOCKETS_G(strerror_buf) = NULL;
}
php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS_PASSTHRU);
return SUCCESS;
}
@@ -1018,7 +900,7 @@ PHP_FUNCTION(socket_select)
if (retval == -1) {
SOCKETS_G(last_error) = errno;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s", errno, php_strerror(errno TSRMLS_CC));
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s", errno, sockets_strerror(errno TSRMLS_CC));
RETURN_FALSE;
}
@@ -1087,7 +969,7 @@ PHP_FUNCTION(socket_set_nonblock)
}
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);
if (php_sock->zstream != NULL) {
php_stream *stream;
/* omit notice if resource doesn't exist anymore */
@@ -1096,7 +978,7 @@ PHP_FUNCTION(socket_set_nonblock)
if (stream != NULL) {
if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 0,
NULL) != -1) {
php_sock->blocking = 1;
php_sock->blocking = 0;
RETURN_TRUE;
}
}
@@ -1124,7 +1006,7 @@ PHP_FUNCTION(socket_set_block)
}
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);
/* if socket was created from a stream, give the stream a chance to take
* care of the operation itself, thereby allowing it to update its internal
* state */
@@ -1478,7 +1360,7 @@ PHP_FUNCTION(socket_create)
if (IS_INVALID_SOCKET(php_sock)) {
SOCKETS_G(last_error) = errno;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create socket [%d]: %s", errno, php_strerror(errno TSRMLS_CC));
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno TSRMLS_CC));
efree(php_sock);
RETURN_FALSE;
}
@@ -1511,7 +1393,7 @@ PHP_FUNCTION(socket_connect)
#if HAVE_IPV6
case AF_INET6: {
struct sockaddr_in6 sin6 = {0};
if (argc != 3) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Socket of type AF_INET6 requires 3 arguments");
RETURN_FALSE;
@@ -1532,7 +1414,7 @@ PHP_FUNCTION(socket_connect)
#endif
case AF_INET: {
struct sockaddr_in sin = {0};
if (argc != 3) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Socket of type AF_INET requires 3 arguments");
RETURN_FALSE;
@@ -1551,7 +1433,7 @@ PHP_FUNCTION(socket_connect)
case AF_UNIX: {
struct sockaddr_un s_un = {0};
if (addr_len >= sizeof(s_un.sun_path)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Path too long");
RETURN_FALSE;
@@ -1588,7 +1470,7 @@ PHP_FUNCTION(socket_strerror)
return;
}
RETURN_STRING(php_strerror(arg1 TSRMLS_CC), 1);
RETURN_STRING(sockets_strerror(arg1 TSRMLS_CC), 1);
}
/* }}} */
@@ -1979,8 +1861,15 @@ PHP_FUNCTION(socket_get_option)
}
}
}
} else if (level == IPPROTO_IPV6) {
int ret = php_do_getsockopt_ipv6_rfc3542(php_sock, level, optname, return_value TSRMLS_CC);
if (ret == SUCCESS) {
return;
} else if (ret == FAILURE) {
RETURN_FALSE;
} /* else continue */
}
/* sol_socket options and general case */
switch(optname) {
case SO_LINGER:
@@ -2022,7 +1911,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);
@@ -2039,102 +1928,6 @@ 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);
#ifdef HAS_MCAST_EXT
int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
struct sockaddr *, socklen_t, unsigned TSRMLS_DC);
#endif
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;
}
#ifdef HAS_MCAST_EXT
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;
}
#endif
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)
@@ -2153,12 +1946,8 @@ PHP_FUNCTION(socket_set_option)
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;
}
@@ -2167,94 +1956,26 @@ PHP_FUNCTION(socket_set_option)
set_errno(0);
#define HANDLE_SUBCALL(res) \
do { \
if (res == 1) { goto default_case; } \
else if (res == SUCCESS) { RETURN_TRUE; } \
else { RETURN_FALSE; } \
} while (0)
if (level == IPPROTO_IP) {
switch (optname) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
#ifdef HAS_MCAST_EXT
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
#endif
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:
convert_to_boolean_ex(arg4);
goto ipv4_loop_ttl;
case IP_MULTICAST_TTL:
convert_to_long_ex(arg4);
if (Z_LVAL_PP(arg4) < 0L || Z_LVAL_PP(arg4) > 255L) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Expected a value between 0 and 255");
RETURN_FALSE;
}
ipv4_loop_ttl:
ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_PP(arg4);
opt_ptr = &ipv4_mcast_ttl_lback;
optlen = sizeof(ipv4_mcast_ttl_lback);
goto dosockopt;
}
int res = php_do_setsockopt_ip_mcast(php_sock, level, optname, arg4 TSRMLS_CC);
HANDLE_SUBCALL(res);
}
#if HAVE_IPV6
else if (level == IPPROTO_IPV6) {
switch (optname) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
#ifdef HAS_MCAST_EXT
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
#endif
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:
convert_to_boolean_ex(arg4);
goto ipv6_loop_hops;
case IPV6_MULTICAST_HOPS:
convert_to_long_ex(arg4);
if (Z_LVAL_PP(arg4) < -1L || Z_LVAL_PP(arg4) > 255L) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Expected a value between -1 and 255");
RETURN_FALSE;
}
ipv6_loop_hops:
ov = (int) Z_LVAL_PP(arg4);
opt_ptr = &ov;
optlen = sizeof(ov);
goto dosockopt;
int res = php_do_setsockopt_ipv6_mcast(php_sock, level, optname, arg4 TSRMLS_CC);
if (res == 1) {
res = php_do_setsockopt_ipv6_rfc3542(php_sock, level, optname, arg4 TSRMLS_CC);
}
HANDLE_SUBCALL(res);
}
#endif
@@ -2317,8 +2038,9 @@ ipv6_loop_hops:
#endif
break;
}
default:
default_case:
convert_to_long_ex(arg4);
ov = Z_LVAL_PP(arg4);
@@ -2327,12 +2049,9 @@ ipv6_loop_hops:
break;
}
dosockopt:
retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
if (retval != 0) {
if (retval != -2) { /* error, but message already emitted */
PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
}
PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
RETURN_FALSE;
}
@@ -2373,7 +2092,7 @@ PHP_FUNCTION(socket_create_pair)
if (socketpair(domain, type, protocol, fds_array) != 0) {
SOCKETS_G(last_error) = errno;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create socket pair [%d]: %s", errno, php_strerror(errno TSRMLS_CC));
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create socket pair [%d]: %s", errno, sockets_strerror(errno TSRMLS_CC));
efree(php_sock[0]);
efree(php_sock[1]);
RETURN_FALSE;
@@ -2472,6 +2191,53 @@ PHP_FUNCTION(socket_clear_error)
}
/* }}} */
php_socket *socket_import_file_descriptor(PHP_SOCKET socket TSRMLS_DC)
{
#ifdef SO_DOMAIN
int type;
socklen_t type_len = sizeof(type);
#endif
php_socket *retsock;
php_sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
#ifndef PHP_WIN32
int t;
#endif
retsock = php_create_socket();
retsock->bsd_socket = socket;
/* determine family */
#ifdef SO_DOMAIN
if (getsockopt(socket, SOL_SOCKET, SO_DOMAIN, &type, &type_len) == 0) {
retsock->type = type;
} else
#endif
if (getsockname(socket, (struct sockaddr*)&addr, &addr_len) == 0) {
retsock->type = addr.ss_family;
} else {
PHP_SOCKET_ERROR(retsock, "unable to obtain socket family", errno);
goto error;
}
/* determine blocking mode */
#ifndef PHP_WIN32
t = fcntl(socket, F_GETFL);
if (t == -1) {
PHP_SOCKET_ERROR(retsock, "unable to obtain blocking state", errno);
goto error;
} else {
retsock->blocking = !(t & O_NONBLOCK);
}
#endif
return retsock;
error:
efree(retsock);
return NULL;
}
/* {{{ proto void socket_import_stream(resource stream)
Imports a stream that encapsulates a socket into a socket extension resource. */
PHP_FUNCTION(socket_import_stream)
@@ -2480,44 +2246,23 @@ PHP_FUNCTION(socket_import_stream)
php_stream *stream;
php_socket *retsock = NULL;
PHP_SOCKET socket; /* fd */
php_sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
#ifndef PHP_WIN32
int t;
#endif
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) == FAILURE) {
return;
}
php_stream_from_zval(stream, &zstream);
if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void**)&socket, 1)) {
/* error supposedly already shown */
RETURN_FALSE;
}
retsock = php_create_socket();
retsock->bsd_socket = socket;
/* determine family */
if (getsockname(socket, (struct sockaddr*)&addr, &addr_len) == 0) {
retsock->type = addr.ss_family;
} else {
PHP_SOCKET_ERROR(retsock, "unable to obtain socket family", errno);
goto error;
retsock = socket_import_file_descriptor(socket TSRMLS_CC);
if (retsock == NULL) {
RETURN_FALSE;
}
/* determine blocking mode */
#ifndef PHP_WIN32
t = fcntl(socket, F_GETFL);
if(t == -1) {
PHP_SOCKET_ERROR(retsock, "unable to obtain blocking state", errno);
goto error;
} else {
retsock->blocking = !(t & O_NONBLOCK);
}
#else
#ifdef PHP_WIN32
/* on windows, check if the stream is a socket stream and read its
* private data; otherwise assume it's in non-blocking mode */
if (php_stream_is(stream, PHP_STREAM_IS_SOCKET)) {
@@ -2527,7 +2272,7 @@ PHP_FUNCTION(socket_import_stream)
retsock->blocking = 1;
}
#endif
/* hold a zval reference to the stream (holding a php_stream* directly could
* also be done, but this might be slightly better if in the future we want
* to provide a socket_export_stream) */
@@ -2536,16 +2281,11 @@ PHP_FUNCTION(socket_import_stream)
zval_copy_ctor(retsock->zstream);
Z_UNSET_ISREF_P(retsock->zstream);
Z_SET_REFCOUNT_P(retsock->zstream, 1);
php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER,
PHP_STREAM_BUFFER_NONE, NULL);
ZEND_REGISTER_RESOURCE(return_value, retsock, le_socket);
return;
error:
if (retsock != NULL)
efree(retsock);
RETURN_FALSE;
}
/* }}} */

View File

@@ -2,7 +2,5 @@
if (!defined("AF_INET6")) {
die('skip no IPv6 support');
}
/* If IPv6 is supported on the platform this will error out with code 111 - Connection refused.
If IPv6 is NOT supported, $errno will be set to something else (indicating parse/getaddrinfo error) */
@stream_socket_client('tcp://[::1]:0', $errno);
if ($errno != 111) die('skip no IPv6 support');
if (@stream_socket_client('udp://[::1]:8888') === false)
die('skip no IPv6 support');

View File

@@ -9,8 +9,8 @@ if (!defined('IPPROTO_IPV6')) {
die('skip IPv6 not available.');
}
$level = IPPROTO_IPV6;
$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die("skip Can not create socket");
if (socket_set_option($s, $level, IP_MULTICAST_IF, 1) === false) {
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("skip Can not create socket");
if (socket_set_option($s, $level, IPV6_MULTICAST_IF, 1) === false) {
die("skip interface 1 either doesn't exist or has no ipv6 address");
}
--FILE--

View File

@@ -0,0 +1,89 @@
--TEST--
recvmsg(): receive SCM_CREDENTIALS messages
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
die('skip not for Microsoft Windows');
}
--CLEAN--
<?php
$path = __DIR__ . "/unix_sock";
@unlink($path);
--FILE--
<?php
include __DIR__."/mcast_helpers.php.inc";
$path = __DIR__ . "/unix_sock";
@unlink($path);
echo "creating send socket\n";
$sends1 = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
var_dump($sends1);
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
echo "creating receive socket\n";
$s = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
var_dump($s);
$br = socket_bind($s, $path) or die("err");
var_dump($br);
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
socket_set_option($s, SOL_SOCKET, SO_PASSCRED, 1) or die("could not set SO_PASSCRED");
//$r = socket_sendmsg($sends1, [
// "iov" => ["test ", "thing", "\n"],
//], 0);
$r = socket_sendto($sends1, $msg = "dread", strlen($msg), 0, $path);
var_dump($r);
checktimeout($s, 500);
$data = [
"name" => [],
"buffer_size" => 2000,
"controllen" => socket_cmsg_space(SOL_SOCKET, SCM_CREDENTIALS)
];
if (!socket_recvmsg($s, $data, 0)) die("recvmsg");
print_r($data);
$pid = getmypid();
var_dump($data['control'][0]['data']['pid'] === $pid);
--EXPECTF--
creating send socket
resource(%d) of type (Socket)
creating receive socket
resource(%d) of type (Socket)
bool(true)
int(5)
Array
(
[name] =>
[control] => Array
(
[0] => Array
(
[level] => %d
[type] => %d
[data] => Array
(
[pid] => %d
[uid] => %d
[gid] => %d
)
)
)
[iov] => Array
(
[0] => dread
)
[flags] => 0
)
bool(true)

View File

@@ -0,0 +1,100 @@
--TEST--
recvmsg(): receive SCM_CREDENTIALS messages
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
die('skip not for Microsoft Windows');
}
--CLEAN--
<?php
$path = __DIR__ . "/unix_sock";
@unlink($path);
--FILE--
<?php
include __DIR__."/mcast_helpers.php.inc";
$path = __DIR__ . "/unix_sock";
@unlink($path);
echo "creating send socket\n";
$sends1 = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
var_dump($sends1);
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
echo "creating receive socket\n";
$s = socket_create(AF_UNIX, SOCK_DGRAM, 0) or die("err");
var_dump($s);
$br = socket_bind($s, $path) or die("err");
var_dump($br);
socket_set_nonblock($s) or die("Could not put in non-blocking mode");
$r = socket_sendmsg($sends1, [
"name" => [ "path" => $path ],
"iov" => ["test ", "thing", "\n"],
"control" => [
[
"level" => SOL_SOCKET,
"type" => SCM_RIGHTS,
"data" => [$sends1, STDIN, STDOUT, STDERR],
]
]
], 0);
var_dump($r);
checktimeout($s, 500);
$data = [
"name" => [],
"buffer_size" => 2000,
"controllen" => socket_cmsg_space(SOL_SOCKET, SCM_RIGHTS, 3)
];
var_dump($data);
if (!socket_recvmsg($s, $data, 0)) die("recvmsg");
print_r($data);
--EXPECTF--
creating send socket
resource(%d) of type (Socket)
creating receive socket
resource(%d) of type (Socket)
bool(true)
int(11)
array(3) {
["name"]=>
array(0) {
}
["buffer_size"]=>
int(2000)
["controllen"]=>
int(32)
}
Array
(
[name] =>
[control] => Array
(
[0] => Array
(
[level] => %d
[type] => %d
[data] => Array
(
[0] => Resource id #%d
[1] => Resource id #%d
[2] => Resource id #%d
)
)
)
[iov] => Array
(
[0] => test thing
)
[flags] => 0
)

View File

@@ -80,9 +80,6 @@ stream_set_blocking
Warning: stream_set_blocking(): %d is not a valid stream resource in %s on line %d
socket_set_block
Warning: socket_set_block(): An operation was attempted on something that is not a socket.
in %ssocket_import_stream-4-win.php on line %d
Warning: socket_set_block(): unable to set blocking mode [%d]: An operation was attempted on something that is not a socket.
in %ssocket_import_stream-4-win.php on line %d

View File

@@ -0,0 +1,86 @@
--TEST--
recvmsg(): basic test
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (!defined('IPPROTO_IPV6')) {
die('skip IPv6 not available.');
}
--FILE--
<?php
include __DIR__."/mcast_helpers.php.inc";
$addr = '::1';
echo "creating send socket\n";
$sends1 = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($sends1);
$br = socket_bind($sends1, '::', 7001) or die("err");
var_dump($br);
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
echo "creating receive socket\n";
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($s);
$br = socket_bind($s, '::0', 3000) or die("err");
var_dump($br);
socket_set_option($s, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1) or die("err");
$r = socket_sendto($sends1, $m = "testing packet", strlen($m), 0, $addr, 3000);
var_dump($r);
if ($r < 12) die;
checktimeout($s, 500);
$data = [
"name" => ["family" => AF_INET6, "addr" => "::1"],
"buffer_size" => 2000,
"controllen" => socket_cmsg_space(IPPROTO_IPV6, IPV6_PKTINFO),
];
if (!socket_recvmsg($s, $data, 0)) die("recvmsg");
print_r($data);
--EXPECTF--
creating send socket
resource(%d) of type (Socket)
bool(true)
creating receive socket
resource(%d) of type (Socket)
bool(true)
int(14)
Array
(
[name] => Array
(
[family] => %d
[addr] => ::1
[port] => 7001
[flowinfo] => 0
[scope_id] => 0
)
[control] => Array
(
[0] => Array
(
[level] => %d
[type] => %d
[data] => Array
(
[addr] => ::1
[ifindex] => %d
)
)
)
[iov] => Array
(
[0] => testing packet
)
[flags] => 0
)

View File

@@ -0,0 +1,110 @@
--TEST--
sendmsg()/recvmsg(): test ability to receive multiple messages (WIN32)
--SKIPIF--
<?php
if (!extension_loaded('sockets'))
die('skip sockets extension not available.');
if (!defined('IPPROTO_IPV6'))
die('skip IPv6 not available.');
if (substr(PHP_OS, 0, 3) != 'WIN')
die('skip Only for Windows!');
/* Windows supports IPV6_RECVTCLASS and is able to receive the tclass via
* WSARecvMsg (though only the top 6 bits seem to reported), but WSASendMsg
* does not accept IPV6_TCLASS messages. We still test that sendmsg() works
* corectly by sending an IPV6_PKTINFO message that will have no effect */
--FILE--
<?php
include __DIR__."/mcast_helpers.php.inc";
$addr = '::1';
echo "creating send socket\n";
$sends1 = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($sends1);
$br = socket_bind($sends1, '::', 7001) or die("err");
var_dump($br);
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
echo "creating receive socket\n";
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($s);
$br = socket_bind($s, '::0', 3000) or die("err");
var_dump($br);
socket_set_option($s, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1) or die("err");
socket_set_option($s, IPPROTO_IPV6, IPV6_RECVTCLASS, 1) or die("err");
$r = socket_sendmsg($sends1, [
"name" => [ "addr" => "::1", "port" => 3000],
"iov" => ["test ", "thing", "\n"],
"control" => [[
"level" => IPPROTO_IPV6,
"type" => IPV6_PKTINFO,
"data" => [
'addr' => '::1',
'ifindex' => 1 /* we're assuming loopback is 1. Is this a safe assumption? */
],
]]
], 0);
var_dump($r);
checktimeout($s, 500);
$data = [
"name" => ["family" => AF_INET6, "addr" => "::1"],
"buffer_size" => 2000,
"controllen" => socket_cmsg_space(IPPROTO_IPV6, IPV6_PKTINFO) +
socket_cmsg_space(IPPROTO_IPV6, IPV6_TCLASS),
];
if (!socket_recvmsg($s, $data, 0)) die("recvmsg");
print_r($data);
--EXPECTF--
creating send socket
resource(5) of type (Socket)
bool(true)
creating receive socket
resource(6) of type (Socket)
bool(true)
int(11)
Array
(
[name] => Array
(
[family] => %d
[addr] => ::1
[port] => 7001
[flowinfo] => 0
[scope_id] => 0
)
[control] => Array
(
[0] => Array
(
[level] => %d
[type] => %d
[data] => Array
(
[addr] => ::1
[ifindex] => %d
)
)
[1] => Array
(
[level] => %d
[type] => %d
[data] => 0
)
)
[iov] => Array
(
[0] => test thing
)
[flags] => 0
)

View File

@@ -0,0 +1,106 @@
--TEST--
sendmsg()/recvmsg(): test ability to receive multiple messages
--SKIPIF--
<?php
if (!extension_loaded('sockets'))
die('skip sockets extension not available.');
if (!defined('IPPROTO_IPV6'))
die('skip IPv6 not available.');
if (substr(PHP_OS, 0, 3) == 'WIN')
die('skip Not for the Windows!');
/* Windows supports IPV6_RECVTCLASS and is able to receive the tclass via
* WSARecvMsg (though only the top 6 bits seem to reported), but WSASendMsg
* does not accept IPV6_TCLASS messages */
--FILE--
<?php
include __DIR__."/mcast_helpers.php.inc";
$addr = '::1';
echo "creating send socket\n";
$sends1 = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($sends1);
$br = socket_bind($sends1, '::', 7001) or die("err");
var_dump($br);
socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
echo "creating receive socket\n";
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump($s);
$br = socket_bind($s, '::0', 3000) or die("err");
var_dump($br);
socket_set_option($s, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1) or die("err");
socket_set_option($s, IPPROTO_IPV6, IPV6_RECVTCLASS, 1) or die("err");
$r = socket_sendmsg($sends1, [
"name" => [ "addr" => "::1", "port" => 3000],
"iov" => ["test ", "thing", "\n"],
"control" => [[
"level" => IPPROTO_IPV6,
"type" => IPV6_TCLASS,
"data" => 40,
]]
], 0);
var_dump($r);
checktimeout($s, 500);
$data = [
"name" => ["family" => AF_INET6, "addr" => "::1"],
"buffer_size" => 2000,
"controllen" => socket_cmsg_space(IPPROTO_IPV6, IPV6_PKTINFO) +
socket_cmsg_space(IPPROTO_IPV6, IPV6_TCLASS),
];
if (!socket_recvmsg($s, $data, 0)) die("recvmsg");
print_r($data);
--EXPECTF--
creating send socket
resource(5) of type (Socket)
bool(true)
creating receive socket
resource(6) of type (Socket)
bool(true)
int(11)
Array
(
[name] => Array
(
[family] => %d
[addr] => ::1
[port] => 7001
[flowinfo] => 0
[scope_id] => 0
)
[control] => Array
(
[0] => Array
(
[level] => %d
[type] => %d
[data] => Array
(
[addr] => ::1
[ifindex] => %d
)
)
[1] => Array
(
[level] => %d
[type] => %d
[data] => 40
)
)
[iov] => Array
(
[0] => test thing
)
[flags] => 0
)

View File

@@ -14,7 +14,7 @@ if (!extension_loaded('sockets')) {
if (!socket_set_nonblock($socket)) {
die('Unable to set nonblocking mode for socket');
}
socket_recvfrom($socket, $buf, 12, 0, $from, $port); // cause warning
var_dump(socket_recvfrom($socket, $buf, 12, 0, $from, $port)); //false (EAGAIN - no warning)
$address = '127.0.0.1';
socket_sendto($socket, '', 1, 0, $address); // cause warning
if (!socket_bind($socket, $address, 1223)) {
@@ -44,7 +44,7 @@ if (!extension_loaded('sockets')) {
socket_close($socket);
--EXPECTF--
Warning: socket_recvfrom(): unable to recvfrom [%d]: %a in %s on line %d
bool(false)
Warning: Wrong parameter count for socket_sendto() in %s on line %d

View File

@@ -0,0 +1,62 @@
--TEST--
Test if socket_recvfrom() receives data sent by socket_sendto() via IPv6 UDP (Win32)
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('SKIP The sockets extension is not loaded.');
}
if (substr(PHP_OS, 0, 3) != 'WIN') {
die('skip only for Windows');
}
require 'ipv6_skipif.inc';
--FILE--
<?php
$socket = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);
if (!$socket) {
die('Unable to create AF_INET6 socket');
}
if (!socket_set_nonblock($socket)) {
die('Unable to set nonblocking mode for socket');
}
socket_recvfrom($socket, $buf, 12, 0, $from, $port); // cause warning
$address = '::1';
socket_sendto($socket, '', 1, 0, $address); // cause warning
if (!socket_bind($socket, $address, 1223)) {
die("Unable to bind to $address:1223");
}
$msg = "Ping!";
$len = strlen($msg);
$bytes_sent = socket_sendto($socket, $msg, $len, 0, $address, 1223);
if ($bytes_sent == -1) {
die('An error occurred while sending to the socket');
} else if ($bytes_sent != $len) {
die($bytes_sent . ' bytes have been sent instead of the ' . $len . ' bytes expected');
}
$from = "";
$port = 0;
socket_recvfrom($socket, $buf, 12, 0); // cause warning
socket_recvfrom($socket, $buf, 12, 0, $from); // cause warning
$bytes_received = socket_recvfrom($socket, $buf, 12, 0, $from, $port);
if ($bytes_received == -1) {
die('An error occurred while receiving from the socket');
} else if ($bytes_received != $len) {
die($bytes_received . ' bytes have been received instead of the ' . $len . ' bytes expected');
}
echo "Received $buf from remote address $from and remote port $port" . PHP_EOL;
socket_close($socket);
--EXPECTF--
Warning: socket_recvfrom(): unable to recvfrom [10022]: An invalid argument was supplied.
in %s on line %d
Warning: Wrong parameter count for socket_sendto() in %s on line %d
Warning: socket_recvfrom() expects at least 5 parameters, 4 given in %s on line %d
Warning: Wrong parameter count for socket_recvfrom() in %s on line %d
Received Ping! from remote address ::1 and remote port 1223
--CREDITS--
Falko Menge <mail at falko-menge dot de>
PHP Testfest Berlin 2009-05-09

View File

@@ -5,6 +5,9 @@ Test if socket_recvfrom() receives data sent by socket_sendto() via IPv6 UDP
if (!extension_loaded('sockets')) {
die('SKIP The sockets extension is not loaded.');
}
if (substr(PHP_OS, 0, 3) == 'WIN') {
die('skip Not valid for Windows');
}
require 'ipv6_skipif.inc';
--FILE--
<?php
@@ -15,7 +18,7 @@ require 'ipv6_skipif.inc';
if (!socket_set_nonblock($socket)) {
die('Unable to set nonblocking mode for socket');
}
socket_recvfrom($socket, $buf, 12, 0, $from, $port); // cause warning
var_dump(socket_recvfrom($socket, $buf, 12, 0, $from, $port)); // false (EAGAIN, no warning)
$address = '::1';
socket_sendto($socket, '', 1, 0, $address); // cause warning
if (!socket_bind($socket, $address, 1223)) {
@@ -45,7 +48,7 @@ require 'ipv6_skipif.inc';
socket_close($socket);
--EXPECTF--
Warning: socket_recvfrom(): unable to recvfrom [11]: Resource temporarily unavailable in %s on line %d
bool(false)
Warning: Wrong parameter count for socket_sendto() in %s on line %d

View File

@@ -18,7 +18,7 @@ if (!extension_loaded('sockets')) {
if (!socket_set_nonblock($socket)) {
die('Unable to set nonblocking mode for socket');
}
socket_recvfrom($socket, $buf, 12, 0, $from, $port); // cause warning
var_dump(socket_recvfrom($socket, $buf, 12, 0, $from, $port)); //false (EAGAIN, no warning)
$address = sprintf("/tmp/%s.sock", uniqid());
if (!socket_bind($socket, $address)) {
die("Unable to bind to $address");
@@ -53,8 +53,7 @@ if (!extension_loaded('sockets')) {
?>
--EXPECTF--
Warning: socket_create(): Unable to create socket [%d]: Protocol not supported in %s on line %d
Warning: socket_recvfrom(): unable to recvfrom [%d]: Resource temporarily unavailable in %s on line %d
bool(false)
Warning: socket_sendto() expects at least 5 parameters, 4 given in %s on line %d
bool(false)

View File

@@ -0,0 +1,32 @@
--TEST--
socket_set_option() with IPV6_PKTINFO
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('skip sockets extension not available.');
}
if (!defined('IPPROTO_IPV6')) {
die('skip IPv6 not available.');
}
if (substr(PHP_OS, 0, 3) == 'WIN')
die('skip Not for Windows!');
--FILE--
<?php
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, []));
var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, [
"addr" => '::1',
"ifindex" => 0
]));
//Oddly, Linux does not support IPV6_PKTINFO in sockgetopt().
//See do_ipv6_getsockopt() on the kernel sources
//A work-around with is sort-of possible (with IPV6_2292PKTOPTIONS),
//but not worth it
//var_dump(socket_get_option($s, IPPROTO_IPV6, IPV6_PKTINFO));
--EXPECTF--
Warning: socket_set_option(): error converting user data (path: in6_pktinfo): The key 'addr' is required in %s on line %d
bool(false)
bool(true)

View File

@@ -0,0 +1,43 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2012 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. |
+----------------------------------------------------------------------+
*/
#ifndef WINDOWS_COMMON_H
#define WINDOWS_COMMON_H
#include <Winsock2.h>
#define NTDDI_XP NTDDI_WINXP /* bug in SDK */
#include <IPHlpApi.h> /* conflicting definition of CMSG_DATA */
#undef NTDDI_XP
#define HAVE_IF_NAMETOINDEX 1
#define IS_INVALID_SOCKET(a) (a->bsd_socket == INVALID_SOCKET)
#ifdef EPROTONOSUPPORT
# undef EPROTONOSUPPORT
#endif
#ifdef ECONNRESET
# undef ECONNRESET
#endif
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
#define ECONNRESET WSAECONNRESET
#ifdef errno
# undef errno
#endif
#define errno WSAGetLastError()
#define h_errno WSAGetLastError()
#define set_errno(a) WSASetLastError(a)
#define close(a) closesocket(a)
#endif

View File

@@ -1079,11 +1079,6 @@ PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
/* with ioctlsocket, a non-zero sets nonblocking, a zero sets blocking */
flags = !block;
if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) {
char *error_string;
error_string = php_socket_strerror(WSAGetLastError(), NULL, 0);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", error_string);
efree(error_string);
ret = FAILURE;
}
#else

View File

@@ -21,6 +21,8 @@
#ifndef _PHP_NETWORK_H
#define _PHP_NETWORK_H
#include <php.h>
#ifdef PHP_WIN32
# include "win32/inet.h"
#else

View File

@@ -1,83 +1,4 @@
#include "config.w32.h"
#include "php.h"
#include <winsock2.h>
#include <windows.h>
#include <Ws2tcpip.h>
#include "inet.h"
#if (_WIN32_WINNT < 0x0600) /* Vista/2k8 have these functions */
PHPAPI int inet_pton(int af, const char* src, void* dst)
{
int address_length;
struct sockaddr_storage sa;
struct sockaddr_in *sin = (struct sockaddr_in *)&sa;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa;
switch (af) {
case AF_INET:
address_length = sizeof (struct sockaddr_in);
break;
case AF_INET6:
address_length = sizeof (struct sockaddr_in6);
break;
default:
return -1;
}
if (WSAStringToAddress ((LPTSTR) src, af, NULL, (LPSOCKADDR) &sa, &address_length) == 0) {
switch (af) {
case AF_INET:
memcpy (dst, &sin->sin_addr, sizeof (struct in_addr));
break;
case AF_INET6:
memcpy (dst, &sin6->sin6_addr, sizeof (struct in6_addr));
break;
}
return 1;
}
return 0;
}
PHPAPI const char* inet_ntop(int af, const void* src, char* dst, size_t size)
{
int address_length;
DWORD string_length = size;
struct sockaddr_storage sa;
struct sockaddr_in *sin = (struct sockaddr_in *)&sa;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa;
memset (&sa, 0, sizeof (sa));
switch (af) {
case AF_INET:
address_length = sizeof (struct sockaddr_in);
sin->sin_family = af;
memcpy (&sin->sin_addr, src, sizeof (struct in_addr));
break;
case AF_INET6:
address_length = sizeof (struct sockaddr_in6);
sin6->sin6_family = af;
memcpy (&sin6->sin6_addr, src, sizeof (struct in6_addr));
break;
default:
return NULL;
}
if (WSAAddressToString ((LPSOCKADDR) &sa, address_length, NULL, dst, &string_length) == 0) {
return dst;
}
return NULL;
}
#endif
int inet_aton(const char *cp, struct in_addr *inp) {
inp->s_addr = inet_addr(cp);

View File

@@ -1,11 +1,4 @@
#if _MSC_VER >= 1500
# include <In6addr.h>
#endif
#include <Ws2tcpip.h>
#if (_WIN32_WINNT <= 0x502)
PHPAPI int inet_pton(int af, const char* src, void* dst);
PHPAPI const char* inet_ntop(int af, const void* src, char* dst, size_t size);
#endif
#include <php.h>
#include <Winsock2.h>
PHPAPI int inet_aton(const char *cp, struct in_addr *inp);