]> granicus.if.org Git - php/commitdiff
- Added socket_import_stream().
authorGustavo André dos Santos Lopes <cataphract@php.net>
Tue, 22 Mar 2011 00:44:23 +0000 (00:44 +0000)
committerGustavo André dos Santos Lopes <cataphract@php.net>
Tue, 22 Mar 2011 00:44:23 +0000 (00:44 +0000)
- 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.

ext/sockets/php_sockets.h
ext/sockets/sockets.c
ext/sockets/tests/socket_import_stream-1.phpt [new file with mode: 0644]
ext/sockets/tests/socket_import_stream-2.phpt [new file with mode: 0644]
ext/sockets/tests/socket_import_stream-3.phpt [new file with mode: 0644]
ext/sockets/tests/socket_import_stream-4.phpt [new file with mode: 0644]
ext/sockets/tests/socket_import_stream-5.phpt [new file with mode: 0644]
ext/sockets/tests/socket_strerror.phpt
main/network.c

index bde07f5cd240f218a7197e46454eb36f25594909..a4a106f18685d14e993462cf1d768b8ced3000b3 100644 (file)
@@ -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
index 6d03c3fa0c97fe42f63cc8386290d1befe815a29..6c56f64cb619bf32f6ad3bc59e21c19f5d63afc1 100644 (file)
@@ -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
 
 /*
diff --git a/ext/sockets/tests/socket_import_stream-1.phpt b/ext/sockets/tests/socket_import_stream-1.phpt
new file mode 100644 (file)
index 0000000..222fca5
--- /dev/null
@@ -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"
diff --git a/ext/sockets/tests/socket_import_stream-2.phpt b/ext/sockets/tests/socket_import_stream-2.phpt
new file mode 100644 (file)
index 0000000..085f0e3
--- /dev/null
@@ -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.
+
diff --git a/ext/sockets/tests/socket_import_stream-3.phpt b/ext/sockets/tests/socket_import_stream-3.phpt
new file mode 100644 (file)
index 0000000..2ad715d
--- /dev/null
@@ -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.
+
diff --git a/ext/sockets/tests/socket_import_stream-4.phpt b/ext/sockets/tests/socket_import_stream-4.phpt
new file mode 100644 (file)
index 0000000..33ab104
--- /dev/null
@@ -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.
diff --git a/ext/sockets/tests/socket_import_stream-5.phpt b/ext/sockets/tests/socket_import_stream-5.phpt
new file mode 100644 (file)
index 0000000..2629512
--- /dev/null
@@ -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.
index d1759c582cba29c7ce4229c1b8f4b0c21102997a..d3abe8fb59babc31b48a950b512991ebb01da460 100644 (file)
@@ -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"
index 0cfd777e4a4ccba213f8761be7414b5371464f50..0d7a30ca8b7123bb829549a5c018f7d14ec56e41 100644 (file)
@@ -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;
 }