cataphract                               Tue, 22 Mar 2011 00:44:23 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=309516

Log:
- 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.

Changed paths:
    U   php/php-src/trunk/ext/sockets/php_sockets.h
    U   php/php-src/trunk/ext/sockets/sockets.c
    A   php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt
    A   php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt
    A   php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt
    A   php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt
    A   php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt
    U   php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt
    U   php/php-src/trunk/main/network.c

Modified: php/php-src/trunk/ext/sockets/php_sockets.h
===================================================================
--- php/php-src/trunk/ext/sockets/php_sockets.h	2011-03-22 00:32:33 UTC (rev 309515)
+++ php/php-src/trunk/ext/sockets/php_sockets.h	2011-03-22 00:44:23 UTC (rev 309516)
@@ -70,6 +70,7 @@
 #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 @@
 #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

Modified: php/php-src/trunk/ext/sockets/sockets.c
===================================================================
--- php/php-src/trunk/ext/sockets/sockets.c	2011-03-22 00:32:33 UTC (rev 309515)
+++ php/php-src/trunk/ext/sockets/sockets.c	2011-03-22 00:44:23 UTC (rev 309516)
@@ -270,6 +270,10 @@
 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 @@
 #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 @@
 }
 /* }}} */

+/* 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 @@
 {
 	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_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 @@
  */
 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 @@
 	}

 	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 @@
 	}

 	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 @@
 	}

 	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_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 @@
 		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,8 +2431,82 @@
 }
 /* }}} */

+/* {{{ 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
+
 /*
  * Local variables:
  * tab-width: 4

Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt
===================================================================
--- php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt	2011-03-22 00:44:23 UTC (rev 309516)
@@ -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"

Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt
===================================================================
--- php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt	2011-03-22 00:44:23 UTC (rev 309516)
@@ -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.
+

Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt
===================================================================
--- php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt	2011-03-22 00:44:23 UTC (rev 309516)
@@ -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.
+

Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt
===================================================================
--- php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt	2011-03-22 00:44:23 UTC (rev 309516)
@@ -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.

Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt
===================================================================
--- php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt	2011-03-22 00:44:23 UTC (rev 309516)
@@ -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.

Modified: php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt
===================================================================
--- php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt	2011-03-22 00:32:33 UTC (rev 309515)
+++ php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt	2011-03-22 00:44:23 UTC (rev 309516)
@@ -154,4 +154,4 @@
 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"

Modified: php/php-src/trunk/main/network.c
===================================================================
--- php/php-src/trunk/main/network.c	2011-03-22 00:32:33 UTC (rev 309515)
+++ php/php-src/trunk/main/network.c	2011-03-22 00:44:23 UTC (rev 309516)
@@ -1095,7 +1095,9 @@
 	} else {
 		flags &= ~myflag;
 	}
-	fcntl(socketd, F_SETFL, flags);
+	if (fcntl(socketd, F_SETFL, flags) == -1) {
+		ret = FAILURE;
+	}
 #endif
 	return ret;
 }
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to