]> granicus.if.org Git - php/commitdiff
Implement socket_export_stream()
authorChris Wright <daverandom@php.net>
Wed, 6 Aug 2014 15:22:56 +0000 (16:22 +0100)
committerBob Weinand <bobwei9@hotmail.com>
Mon, 2 May 2016 15:08:15 +0000 (17:08 +0200)
ext/sockets/sockets.c
ext/sockets/tests/socket_export_stream-1.phpt [new file with mode: 0644]
ext/sockets/tests/socket_export_stream-2.phpt [new file with mode: 0644]
ext/sockets/tests/socket_export_stream-3.phpt [new file with mode: 0644]
ext/sockets/tests/socket_export_stream-4-win.phpt [new file with mode: 0644]
ext/sockets/tests/socket_export_stream-4.phpt [new file with mode: 0644]
ext/sockets/tests/socket_export_stream-5.phpt [new file with mode: 0644]

index 6039ac65ba6d4fb5534c16c908c9fdea2761f261..63674c50d73152125d34d399a4b289c73f83d6ea 100644 (file)
@@ -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 (file)
index 0000000..498e0a2
--- /dev/null
@@ -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 (file)
index 0000000..9852842
--- /dev/null
@@ -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 (file)
index 0000000..b13bb34
--- /dev/null
@@ -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 (file)
index 0000000..e38db7b
--- /dev/null
@@ -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 (file)
index 0000000..ff329ec
--- /dev/null
@@ -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 (file)
index 0000000..732b207
--- /dev/null
@@ -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.