Browse Source

- Added socket_import_stream().

- Fixed socket_strerror.phpt
- Made php_set_sock_blocking return FAILURE on fcntl error.
- Made socket_set_block()/socket_set_nonblock() emit warning on error.
experimental/with_scalar_types
Gustavo André dos Santos Lopes 16 years ago
parent
commit
0de2e61cab
  1. 10
      ext/sockets/php_sockets.h
  2. 165
      ext/sockets/sockets.c
  3. 26
      ext/sockets/tests/socket_import_stream-1.phpt
  4. 49
      ext/sockets/tests/socket_import_stream-2.phpt
  5. 46
      ext/sockets/tests/socket_import_stream-3.phpt
  6. 98
      ext/sockets/tests/socket_import_stream-4.phpt
  7. 23
      ext/sockets/tests/socket_import_stream-5.phpt
  8. 2
      ext/sockets/tests/socket_strerror.phpt
  9. 4
      main/network.c

10
ext/sockets/php_sockets.h

@ -70,6 +70,7 @@ 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;
@ -80,10 +81,11 @@ typedef SOCKET PHP_SOCKET;
#endif
typedef struct {
PHP_SOCKET bsd_socket;
int type;
int error;
int blocking;
PHP_SOCKET bsd_socket;
int type;
int error;
int blocking;
zval *zstream;
} php_socket;
#ifdef PHP_WIN32

165
ext/sockets/sockets.c

@ -270,6 +270,10 @@ 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()
/* }}} */
/* {{{ sockets_functions[]
@ -304,6 +308,7 @@ const zend_function_entry sockets_functions[] = {
#endif
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)
/* for downwards compatability */
PHP_FALIAS(socket_getopt, socket_get_option, arginfo_socket_get_option)
@ -344,11 +349,33 @@ PHP_SOCKETS_API int php_sockets_le_socket(void) /* {{{ */
}
/* }}} */
/* allocating function to make programming errors due to uninitialized fields
* less likely */
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;
}
/* }}} */
static void php_destroy_socket(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
{
php_socket *php_sock = (php_socket *) rsrc->ptr;
php_socket *php_sock = rsrc->ptr;
close(php_sock->bsd_socket);
if (php_sock->zstream == NULL) {
if (!IS_INVALID_SOCKET(php_sock)) {
close(php_sock->bsd_socket);
}
} else {
zval_ptr_dtor(&php_sock->zstream);
}
efree(php_sock);
}
/* }}} */
@ -357,7 +384,7 @@ static int php_open_listen_sock(php_socket **php_sock, int port, int backlog TSR
{
struct sockaddr_in la;
struct hostent *hp;
php_socket *sock = (php_socket*)emalloc(sizeof(php_socket));
php_socket *sock = php_create_socket();
*php_sock = sock;
@ -405,7 +432,7 @@ static int php_open_listen_sock(php_socket **php_sock, int port, int backlog TSR
static int php_accept_connect(php_socket *in_sock, php_socket **new_sock, struct sockaddr *la, socklen_t *la_len TSRMLS_DC) /* {{{ */
{
php_socket *out_sock = (php_socket*)emalloc(sizeof(php_socket));
php_socket *out_sock = php_create_socket();
*new_sock = out_sock;
@ -731,8 +758,6 @@ static PHP_GINIT_FUNCTION(sockets)
*/
PHP_MINIT_FUNCTION(sockets)
{
struct protoent *pe;
le_socket = zend_register_list_destructors_ex(php_destroy_socket, NULL, le_socket_name, module_number);
REGISTER_LONG_CONSTANT("AF_UNIX", AF_UNIX, CONST_CS | CONST_PERSISTENT);
@ -1051,12 +1076,28 @@ 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 */
stream = zend_fetch_resource(&php_sock->zstream TSRMLS_CC, -1,
NULL, NULL, 2, php_file_le_stream(), php_file_le_pstream());
if (stream != NULL) {
if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 0,
NULL) != -1) {
php_sock->blocking = 1;
RETURN_TRUE;
}
}
}
if (php_set_sock_blocking(php_sock->bsd_socket, 0 TSRMLS_CC) == SUCCESS) {
php_sock->blocking = 0;
RETURN_TRUE;
} else {
PHP_SOCKET_ERROR(php_sock, "unable to set nonblocking mode", errno);
RETURN_FALSE;
}
RETURN_FALSE;
}
/* }}} */
@ -1072,12 +1113,30 @@ 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 */
if (php_sock->zstream != NULL) {
php_stream *stream;
stream = zend_fetch_resource(&php_sock->zstream TSRMLS_CC, -1,
NULL, NULL, 2, php_file_le_stream(), php_file_le_pstream());
if (stream != NULL) {
if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 1,
NULL) != -1) {
php_sock->blocking = 1;
RETURN_TRUE;
}
}
}
if (php_set_sock_blocking(php_sock->bsd_socket, 1 TSRMLS_CC) == SUCCESS) {
php_sock->blocking = 1;
RETURN_TRUE;
} else {
PHP_SOCKET_ERROR(php_sock, "unable to set blocking mode", errno);
RETURN_FALSE;
}
RETURN_FALSE;
}
/* }}} */
@ -1115,6 +1174,16 @@ PHP_FUNCTION(socket_close)
}
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);
if (php_sock->zstream != NULL) {
php_stream *stream = NULL;
php_stream_from_zval_no_verify(stream, &php_sock->zstream);
if (stream != NULL) {
/* close & destroy stream, incl. removing it from the rsrc list;
* resource stored in php_sock->zstream will become invalid */
php_stream_free(stream, PHP_STREAM_FREE_CLOSE |
(stream->is_persistent?PHP_STREAM_FREE_CLOSE_PERSISTENT:0));
}
}
zend_list_delete(Z_RESVAL_P(arg1));
}
/* }}} */
@ -1372,7 +1441,7 @@ PHP_FUNCTION(socket_getpeername)
PHP_FUNCTION(socket_create)
{
long arg1, arg2, arg3;
php_socket *php_sock = (php_socket*)emalloc(sizeof(php_socket));
php_socket *php_sock = php_create_socket();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &arg1, &arg2, &arg3) == FAILURE) {
efree(php_sock);
@ -2244,8 +2313,8 @@ PHP_FUNCTION(socket_create_pair)
return;
}
php_sock[0] = (php_socket*)emalloc(sizeof(php_socket));
php_sock[1] = (php_socket*)emalloc(sizeof(php_socket));
php_sock[0] = php_create_socket();
php_sock[1] = php_create_socket();
if (domain != AF_INET
#if HAVE_IPV6
@ -2362,6 +2431,80 @@ PHP_FUNCTION(socket_clear_error)
}
/* }}} */
/* {{{ proto void socket_import_stream(resource stream)
Imports a stream that encapsulates a socket into a socket extension resource. */
PHP_FUNCTION(socket_import_stream)
{
zval *zstream;
php_stream *stream;
php_socket *retsock = NULL;;
PHP_SOCKET socket; /* fd */
php_sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
int t;
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;
}
/* 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
/* 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)) {
retsock->blocking =
((php_netstream_data_t)stream->abstract)->is_blocked;
} else {
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) */
MAKE_STD_ZVAL(retsock->zstream);
*retsock->zstream = *zstream;
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);
}
/* }}} */
#endif
/*

26
ext/sockets/tests/socket_import_stream-1.phpt

@ -0,0 +1,26 @@
--TEST--
socket_import_stream: Basic test
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('SKIP sockets extension not available.');
}
--FILE--
<?php
$domain = (strtoupper(substr(PHP_OS, 0, 3) == 'WIN') ? STREAM_PF_INET : STREAM_PF_UNIX);
$s = stream_socket_pair($domain, STREAM_SOCK_STREAM, 0);
$s0 = reset($s);
$s1 = next($s);
$sock = socket_import_stream($s0);
var_dump($sock);
socket_write($sock, "test message");
socket_close($sock);
var_dump(stream_get_contents($s1));
--EXPECTF--
resource(%d) of type (Socket)
string(12) "test message"

49
ext/sockets/tests/socket_import_stream-2.phpt

@ -0,0 +1,49 @@
--TEST--
socket_import_stream: Bad arguments
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('SKIP sockets extension not available.');
}
--FILE--
<?php
var_dump(socket_import_stream());
var_dump(socket_import_stream(1, 2));
var_dump(socket_import_stream(1));
var_dump(socket_import_stream(new stdclass));
var_dump(socket_import_stream(fopen(__FILE__, "rb")));
var_dump(socket_import_stream(socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)));
$s = stream_socket_server("udp://127.0.0.1:58392", $errno, $errstr, STREAM_SERVER_BIND);
var_dump($s);
var_dump(fclose($s));
var_dump(socket_import_stream($s));
echo "Done.";
--EXPECTF--
Warning: socket_import_stream() expects exactly 1 parameter, 0 given in %s on line %d
NULL
Warning: socket_import_stream() expects exactly 1 parameter, 2 given in %s on line %d
NULL
Warning: socket_import_stream() expects parameter 1 to be resource, integer given in %s on line %d
NULL
Warning: socket_import_stream() expects parameter 1 to be resource, object given in %s on line %d
NULL
Warning: socket_import_stream(): cannot represent a stream of type STDIO as a Socket Descriptor in %s on line %d
bool(false)
Warning: socket_import_stream(): supplied resource is not a valid stream resource in %s on line %d
bool(false)
resource(%d) of type (stream)
bool(true)
Warning: socket_import_stream(): %d is not a valid stream resource in %s on line %d
bool(false)
Done.

46
ext/sockets/tests/socket_import_stream-3.phpt

@ -0,0 +1,46 @@
--TEST--
socket_import_stream: Test with multicasting
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('SKIP sockets extension not available.');
}
$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$br = socket_bind($s, '0.0.0.0', 58381);
if ($br === false)
die("SKIP IPv4/port 58381 not available");
$so = socket_set_option($s, IPPROTO_IP, MCAST_JOIN_GROUP, array(
"group" => '224.0.0.23',
"interface" => "lo",
));
if ($br === false)
die("SKIP joining group 224.0.0.23 on interface lo failed");
--FILE--
<?php
$stream = stream_socket_server("udp://0.0.0.0:58381", $errno, $errstr, STREAM_SERVER_BIND);
$sock = socket_import_stream($stream);
var_dump($sock);
$so = socket_set_option($sock, IPPROTO_IP, MCAST_JOIN_GROUP, array(
"group" => '224.0.0.23',
"interface" => "lo",
));
var_dump($so);
$sendsock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
var_dump($sendsock);
$br = socket_bind($sendsock, '127.0.0.1');
$so = socket_sendto($sendsock, $m = "my message", strlen($m), 0, "224.0.0.23", 58381);
var_dump($so);
stream_set_blocking($stream, 0);
var_dump(fread($stream, strlen($m)));
echo "Done.\n";
--EXPECTF--
resource(%d) of type (Socket)
bool(true)
resource(%d) of type (Socket)
int(10)
string(10) "my message"
Done.

98
ext/sockets/tests/socket_import_stream-4.phpt

@ -0,0 +1,98 @@
--TEST--
socket_import_stream: effects of closing
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('SKIP sockets extension not available.');
}
--FILE--
<?php
function test($stream, $sock) {
if ($stream !== null) {
echo "stream_set_blocking ";
print_r(stream_set_blocking($stream, 0));
echo "\n";
}
if ($sock !== null) {
echo "socket_set_block ";
print_r(socket_set_block($sock));
echo "\n";
echo "socket_get_option ";
print_r(socket_get_option($sock, SOL_SOCKET, SO_TYPE));
echo "\n";
}
echo "\n";
}
echo "normal\n";
$stream0 = stream_socket_server("udp://0.0.0.0:58380", $errno, $errstr, STREAM_SERVER_BIND);
$sock0 = socket_import_stream($stream0);
test($stream0, $sock0);
echo "\nunset stream\n";
$stream1 = stream_socket_server("udp://0.0.0.0:58381", $errno, $errstr, STREAM_SERVER_BIND);
$sock1 = socket_import_stream($stream1);
unset($stream1);
test(null, $sock1);
echo "\nunset socket\n";
$stream2 = stream_socket_server("udp://0.0.0.0:58382", $errno, $errstr, STREAM_SERVER_BIND);
$sock2 = socket_import_stream($stream2);
unset($sock2);
test($stream2, null);
echo "\nclose stream\n";
$stream3 = stream_socket_server("udp://0.0.0.0:58383", $errno, $errstr, STREAM_SERVER_BIND);
$sock3 = socket_import_stream($stream3);
fclose($stream3);
test($stream3, $sock3);
echo "\nclose socket\n";
$stream4 = stream_socket_server("udp://0.0.0.0:58384", $errno, $errstr, STREAM_SERVER_BIND);
$sock4 = socket_import_stream($stream4);
socket_close($sock4);
test($stream4, $sock4);
echo "Done.\n";
--EXPECTF--
normal
stream_set_blocking 1
socket_set_block 1
socket_get_option 2
unset stream
socket_set_block 1
socket_get_option 2
unset socket
stream_set_blocking 1
close stream
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(): unable to set blocking mode [%d]: %s in %s on line %d
socket_get_option
Warning: socket_get_option(): unable to retrieve socket option [%d]: %s in %s on line %d
close socket
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(): %d is not a valid Socket resource in %s on line %d
socket_get_option
Warning: socket_get_option(): %d is not a valid Socket resource in %s on line %d
Done.

23
ext/sockets/tests/socket_import_stream-5.phpt

@ -0,0 +1,23 @@
--TEST--
socket_import_stream: effects of leaked handles
--SKIPIF--
<?php
if (!extension_loaded('sockets')) {
die('SKIP sockets extension not available.');
}
if (!function_exists('leak_variable'))
die('SKIP only for debug builds')
--FILE--
<?php
$stream0 = stream_socket_server("udp://0.0.0.0:58380", $errno, $errstr, STREAM_SERVER_BIND);
$sock0 = socket_import_stream($stream0);
leak_variable($stream0, true);
$stream1 = stream_socket_server("udp://0.0.0.0:58381", $errno, $errstr, STREAM_SERVER_BIND);
$sock1 = socket_import_stream($stream1);
leak_variable($sock1, true);
echo "Done.\n";
--EXPECT--
Done.

2
ext/sockets/tests/socket_strerror.phpt

@ -154,4 +154,4 @@ string(20) "Key has been revoked"
string(27) "Key was rejected by service"
string(10) "Owner died"
string(21) "State not recoverable"
string(17) "Unknown error 132"
string(37) "Operation not possible due to RF-kill"

4
main/network.c

@ -1095,7 +1095,9 @@ PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC)
} else {
flags &= ~myflag;
}
fcntl(socketd, F_SETFL, flags);
if (fcntl(socketd, F_SETFL, flags) == -1) {
ret = FAILURE;
}
#endif
return ret;
}

Loading…
Cancel
Save