summaryrefslogtreecommitdiff
path: root/ext/sockets
diff options
context:
space:
mode:
authorChris Wright <daverandom@php.net>2014-08-06 16:22:56 +0100
committerBob Weinand <bobwei9@hotmail.com>2016-05-02 17:08:15 +0200
commite8abb70fc94247450e4016040272fc9459d18371 (patch)
tree8af059adf359d345e98230011ec27a93e6dd1ef9 /ext/sockets
parentd5a38280befda7626c2566a9a3461eebd37a5c17 (diff)
downloadphp-git-e8abb70fc94247450e4016040272fc9459d18371.tar.gz
Implement socket_export_stream()
Diffstat (limited to 'ext/sockets')
-rw-r--r--ext/sockets/sockets.c108
-rw-r--r--ext/sockets/tests/socket_export_stream-1.phpt27
-rw-r--r--ext/sockets/tests/socket_export_stream-2.phpt48
-rw-r--r--ext/sockets/tests/socket_export_stream-3.phpt47
-rw-r--r--ext/sockets/tests/socket_export_stream-4-win.phpt108
-rw-r--r--ext/sockets/tests/socket_export_stream-4.phpt105
-rw-r--r--ext/sockets/tests/socket_export_stream-5.phpt25
7 files changed, 465 insertions, 3 deletions
diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c
index 6039ac65ba..63674c50d7 100644
--- a/ext/sockets/sockets.c
+++ b/ext/sockets/sockets.c
@@ -251,6 +251,10 @@ 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_export_stream, 0, 0, 1)
+ ZEND_ARG_INFO(0, socket)
+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)
@@ -305,6 +309,7 @@ PHP_FUNCTION(socket_shutdown);
PHP_FUNCTION(socket_last_error);
PHP_FUNCTION(socket_clear_error);
PHP_FUNCTION(socket_import_stream);
+PHP_FUNCTION(socket_export_stream);
/* {{{ sockets_functions[]
*/
@@ -339,6 +344,7 @@ 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_export_stream, arginfo_socket_export_stream)
PHP_FE(socket_sendmsg, arginfo_socket_sendmsg)
PHP_FE(socket_recvmsg, arginfo_socket_recvmsg)
PHP_FE(socket_cmsg_space, arginfo_socket_cmsg_space)
@@ -2307,7 +2313,7 @@ error:
return NULL;
}
-/* {{{ proto void socket_import_stream(resource stream)
+/* {{{ proto resource socket_import_stream(resource stream)
Imports a stream that encapsulates a socket into a socket extension resource. */
PHP_FUNCTION(socket_import_stream)
{
@@ -2343,8 +2349,7 @@ PHP_FUNCTION(socket_import_stream)
#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) */
+ * also be done, but this makes socket_export_stream a bit simpler) */
ZVAL_COPY(&retsock->zstream, zstream);
php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER,
@@ -2354,6 +2359,103 @@ PHP_FUNCTION(socket_import_stream)
}
/* }}} */
+/* {{{ proto resource socket_export_stream(resource socket)
+ Exports a socket extension resource into a stream that encapsulates a socket. */
+PHP_FUNCTION(socket_export_stream)
+{
+ zval *zsocket;
+ php_socket *socket;
+ php_stream *stream = NULL;
+ php_netstream_data_t *stream_data;
+ char *protocol = NULL;
+ size_t protocollen = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsocket) == FAILURE) {
+ return;
+ }
+ if ((socket = (php_socket *) zend_fetch_resource(Z_RES_P(zsocket), le_socket_name, le_socket)) == NULL) {
+ RETURN_FALSE;
+ }
+
+ /* Either we already exported a stream or the socket came from an import,
+ * just return the existing stream */
+ if (!Z_ISUNDEF(socket->zstream)) {
+ RETURN_ZVAL(&socket->zstream, 1, 0);
+ }
+
+ /* Determine if socket is using a protocol with one of the default registered
+ * socket stream wrappers */
+ if (socket->type == PF_INET
+#if HAVE_IPV6
+ || socket->type == PF_INET6
+#endif
+ ) {
+ int protoid;
+ socklen_t protoidlen = sizeof(protoid);
+
+ getsockopt(socket->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &protoid, &protoidlen);
+
+ if (protoid == SOCK_STREAM) {
+ /* SO_PROTOCOL is not (yet?) supported on OS X, so lets assume it's TCP there */
+#ifdef SO_PROTOCOL
+ protoidlen = sizeof(protoid);
+ getsockopt(socket->bsd_socket, SOL_SOCKET, SO_PROTOCOL, (char *) &protoid, &protoidlen);
+ if (protoid == IPPROTO_TCP)
+#endif
+ {
+ protocol = "tcp";
+ protocollen = 3;
+ }
+ } else if (protoid == SOCK_DGRAM) {
+ protocol = "udp";
+ protocollen = 3;
+ }
+#ifdef PF_UNIX
+ } else if (socket->type == PF_UNIX) {
+ int type;
+ socklen_t typelen = sizeof(type);
+
+ getsockopt(socket->bsd_socket, SOL_SOCKET, SO_TYPE, (char *) &type, &typelen);
+
+ if (type == SOCK_STREAM) {
+ protocol = "unix";
+ protocollen = 4;
+ } else if (type == SOCK_DGRAM) {
+ protocol = "udg";
+ protocollen = 3;
+ }
+#endif
+ }
+
+ /* Try to get a stream with the registered sockops for the protocol in use
+ * We don't want streams to actually *do* anything though, so don't give it
+ * anything apart from the protocol */
+ if (protocol != NULL) {
+ stream = php_stream_xport_create(protocol, protocollen, 0, 0, NULL, NULL, NULL, NULL, NULL);
+ }
+
+ /* Fall back to creating a generic socket stream */
+ if (stream == NULL) {
+ stream = php_stream_sock_open_from_socket(socket->bsd_socket, 0);
+
+ if (stream == NULL) {
+ php_error_docref(NULL, E_WARNING, "failed to create stream");
+ RETURN_FALSE;
+ }
+ }
+
+ stream_data = (php_netstream_data_t *) stream->abstract;
+ stream_data->socket = socket->bsd_socket;
+ stream_data->is_blocked = socket->blocking;
+ stream_data->timeout.tv_sec = FG(default_socket_timeout);
+ stream_data->timeout.tv_usec = 0;
+
+ php_stream_to_zval(stream, &socket->zstream);
+
+ RETURN_ZVAL(&socket->zstream, 1, 0);
+}
+/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/sockets/tests/socket_export_stream-1.phpt b/ext/sockets/tests/socket_export_stream-1.phpt
new file mode 100644
index 0000000000..498e0a277c
--- /dev/null
+++ b/ext/sockets/tests/socket_export_stream-1.phpt
@@ -0,0 +1,27 @@
+--TEST--
+socket_export_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') ? AF_INET : AF_UNIX);
+socket_create_pair($domain, SOCK_STREAM, 0, $s);
+
+$s0 = reset($s);
+$s1 = next($s);
+
+$stream = socket_export_stream($s0);
+var_dump($stream);
+
+socket_write($s1, "test message");
+socket_close($s1);
+
+var_dump(stream_get_contents($stream));
+--EXPECTF--
+resource(%d) of type (stream)
+string(12) "test message"
diff --git a/ext/sockets/tests/socket_export_stream-2.phpt b/ext/sockets/tests/socket_export_stream-2.phpt
new file mode 100644
index 0000000000..98528420fa
--- /dev/null
+++ b/ext/sockets/tests/socket_export_stream-2.phpt
@@ -0,0 +1,48 @@
+--TEST--
+socket_export_stream: Bad arguments
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('SKIP sockets extension not available.');
+}
+
+--FILE--
+<?php
+
+var_dump(socket_export_stream());
+var_dump(socket_export_stream(1, 2));
+var_dump(socket_export_stream(1));
+var_dump(socket_export_stream(new stdclass));
+var_dump(socket_export_stream(fopen(__FILE__, "rb")));
+var_dump(socket_export_stream(stream_socket_server("udp://127.0.0.1:58392", $errno, $errstr, STREAM_SERVER_BIND)));
+$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+var_dump($s);
+socket_close($s);
+var_dump(socket_export_stream($s));
+
+
+echo "Done.";
+--EXPECTF--
+Warning: socket_export_stream() expects exactly 1 parameter, 0 given in %s on line %d
+NULL
+
+Warning: socket_export_stream() expects exactly 1 parameter, 2 given in %s on line %d
+NULL
+
+Warning: socket_export_stream() expects parameter 1 to be resource, integer given in %s on line %d
+NULL
+
+Warning: socket_export_stream() expects parameter 1 to be resource, object given in %s on line %d
+NULL
+
+Warning: socket_export_stream(): supplied resource is not a valid Socket resource in %s on line %d
+bool(false)
+
+Warning: socket_export_stream(): supplied resource is not a valid Socket resource in %s on line %d
+bool(false)
+resource(%d) of type (Socket)
+
+Warning: socket_export_stream(): supplied resource is not a valid Socket resource in %s on line %d
+bool(false)
+Done.
+
diff --git a/ext/sockets/tests/socket_export_stream-3.phpt b/ext/sockets/tests/socket_export_stream-3.phpt
new file mode 100644
index 0000000000..b13bb34739
--- /dev/null
+++ b/ext/sockets/tests/socket_export_stream-3.phpt
@@ -0,0 +1,47 @@
+--TEST--
+socket_export_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 ($so === false)
+ die("SKIP joining group 224.0.0.23 on interface lo failed");
+--FILE--
+<?php
+
+$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock, '0.0.0.0', 58381);
+$stream = socket_export_stream($sock);
+var_dump($stream);
+$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 (stream)
+bool(true)
+resource(%d) of type (Socket)
+int(10)
+string(10) "my message"
+Done.
+
diff --git a/ext/sockets/tests/socket_export_stream-4-win.phpt b/ext/sockets/tests/socket_export_stream-4-win.phpt
new file mode 100644
index 0000000000..e38db7bd08
--- /dev/null
+++ b/ext/sockets/tests/socket_export_stream-4-win.phpt
@@ -0,0 +1,108 @@
+--TEST--
+socket_export_stream: effects of closing
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('SKIP sockets extension not available.');
+}
+if(substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die("skip Not Valid for Linux");
+}
+
+--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";
+$sock0 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock0, '0.0.0.0', 58380);
+$stream0 = socket_export_stream($sock0);
+test($stream0, $sock0);
+
+echo "\nunset stream\n";
+$sock1 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock1, '0.0.0.0', 58381);
+$stream1 = socket_export_stream($sock1);
+unset($stream1);
+test(null, $sock1);
+
+echo "\nunset socket\n";
+$sock2 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock2, '0.0.0.0', 58382);
+$stream2 = socket_export_stream($sock2);
+unset($sock2);
+test($stream2, null);
+
+echo "\nclose stream\n";
+$sock3 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock3, '0.0.0.0', 58383);
+$stream3 = socket_export_stream($sock3);
+fclose($stream3);
+test($stream3, $sock3);
+
+echo "\nclose socket\n";
+$sock4 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock4, '0.0.0.0', 58484);
+$stream4 = socket_export_stream($sock4);
+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(): supplied resource is not a valid stream resource in %s on line %d
+
+socket_set_block
+Warning: socket_set_block(): unable to set blocking mode [%d]: An operation was attempted on something that is not a socket.
+ in %s on line %d
+
+socket_get_option
+Warning: socket_get_option(): unable to retrieve socket option [%d]: An operation was attempted on something that is not a socket.
+ in %s on line %d
+
+
+
+close socket
+stream_set_blocking
+Warning: stream_set_blocking(): supplied resource is not a valid stream resource in %s on line %d
+
+socket_set_block
+Warning: socket_set_block(): supplied resource is not a valid Socket resource in %s on line %d
+
+socket_get_option
+Warning: socket_get_option(): supplied resource is not a valid Socket resource in %s on line %d
+
+
+Done.
diff --git a/ext/sockets/tests/socket_export_stream-4.phpt b/ext/sockets/tests/socket_export_stream-4.phpt
new file mode 100644
index 0000000000..ff329ec795
--- /dev/null
+++ b/ext/sockets/tests/socket_export_stream-4.phpt
@@ -0,0 +1,105 @@
+--TEST--
+socket_export_stream: effects of closing
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('SKIP sockets extension not available.');
+}
+if(substr(PHP_OS, 0, 3) == 'WIN' ) {
+ die("skip Not Valid for Windows");
+}
+--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";
+$sock0 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock0, '0.0.0.0', 58380);
+$stream0 = socket_export_stream($sock0);
+test($stream0, $sock0);
+
+echo "\nunset stream\n";
+$sock1 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock1, '0.0.0.0', 58381);
+$stream1 = socket_export_stream($sock1);
+unset($stream1);
+test(null, $sock1);
+
+echo "\nunset socket\n";
+$sock2 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock2, '0.0.0.0', 58382);
+$stream2 = socket_export_stream($sock2);
+unset($sock2);
+test($stream2, null);
+
+echo "\nclose stream\n";
+$sock3 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock3, '0.0.0.0', 58383);
+$stream3 = socket_export_stream($sock3);
+fclose($stream3);
+test($stream3, $sock3);
+
+echo "\nclose socket\n";
+$sock4 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock4, '0.0.0.0', 58484);
+$stream4 = socket_export_stream($sock4);
+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(): supplied resource 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(): supplied resource is not a valid stream resource in %s on line %d
+
+socket_set_block
+Warning: socket_set_block(): supplied resource is not a valid Socket resource in %s on line %d
+
+socket_get_option
+Warning: socket_get_option(): supplied resource is not a valid Socket resource in %s on line %d
+
+
+Done.
diff --git a/ext/sockets/tests/socket_export_stream-5.phpt b/ext/sockets/tests/socket_export_stream-5.phpt
new file mode 100644
index 0000000000..732b2072d0
--- /dev/null
+++ b/ext/sockets/tests/socket_export_stream-5.phpt
@@ -0,0 +1,25 @@
+--TEST--
+socket_export_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
+
+$sock0 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock0, '0.0.0.0', 58380);
+$stream0 = socket_export_stream($sock0);
+leak_variable($stream0, true);
+
+$sock1 = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+socket_bind($sock1, '0.0.0.0', 58381);
+$stream1 = socket_export_stream($sock1);
+leak_variable($sock1, true);
+
+echo "Done.\n";
+--EXPECT--
+Done.