]> granicus.if.org Git - php/commitdiff
send/recvmsg() support for Windows
authorGustavo Lopes <glopes@nebm.ist.utl.pt>
Thu, 31 Jan 2013 13:01:31 +0000 (14:01 +0100)
committerGustavo Lopes <glopes@nebm.ist.utl.pt>
Sat, 2 Feb 2013 15:38:09 +0000 (16:38 +0100)
12 files changed:
ext/sockets/config.w32
ext/sockets/conversions.c
ext/sockets/conversions.h
ext/sockets/php_sockets.h
ext/sockets/sendrecvmsg.c
ext/sockets/sockaddr_conv.c
ext/sockets/sockaddr_conv.h
ext/sockets/sockets.c
ext/sockets/tests/socket_sendrecvmsg_multi_msg-win32.phpt [new file with mode: 0644]
ext/sockets/tests/socket_sendrecvmsg_multi_msg.phpt
ext/sockets/tests/socket_set_option_in6_pktinfo.phpt
ext/sockets/windows_common.h [new file with mode: 0644]

index 9c234db8f8df8f703bea205dc0af7080b2165fb2..aeaa8ed425e68dbc5de516d1417ac9d6ba0e398c 100644 (file)
@@ -7,7 +7,7 @@ if (PHP_SOCKETS != "no") {
        if (CHECK_LIB("ws2_32.lib", "sockets", PHP_SOCKETS)
        && CHECK_LIB("Iphlpapi.lib", "sockets", PHP_SOCKETS)
        && CHECK_HEADER_ADD_INCLUDE("winsock.h", "CFLAGS_SOCKETS")) {
-               EXTENSION('sockets', 'sockets.c multicast.c');
+               EXTENSION('sockets', 'sockets.c multicast.c conversions.c sockaddr_conv.c sendrecvmsg.c');
                AC_DEFINE('HAVE_SOCKETS', 1);
                PHP_INSTALL_HEADERS("ext/sockets", "php_sockets.h");
        } else {
index fa6d94948678db75c593b665ced8c2878e39b1e4..6c79166806b5eb2521e817d9ef1cc3212fdca5f2 100644 (file)
@@ -1,24 +1,67 @@
-#include "conversions.h"
 #include "sockaddr_conv.h"
 #include "conversions.h"
 #include "sendrecvmsg.h" /* for ancillary registry */
+#include "windows_common.h"
 
 #include <Zend/zend_llist.h>
 #include <ext/standard/php_smart_str.h>
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <sys/un.h>
-
-#include <sys/ioctl.h>
-#include <net/if.h>
+#ifndef PHP_WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/un.h>
+# include <sys/ioctl.h>
+# include <net/if.h>
+#else
+# include <win32/php_stdint.h>
+#endif
 
 #include <limits.h>
 #include <stdarg.h>
 #include <stddef.h>
 
+#ifdef PHP_WIN32
+typedef unsigned short sa_family_t;
+# define msghdr                        _WSAMSG
+/*
+struct _WSAMSG {
+    LPSOCKADDR       name;                             //void *msg_name
+    INT              namelen;                  //socklen_t msg_namelen
+    LPWSABUF         lpBuffers;                        //struct iovec *msg_iov
+    ULONG            dwBufferCount;            //size_t msg_iovlen
+    WSABUF           Control;                  //void *msg_control, size_t msg_controllen
+    DWORD            dwFlags;                  //int msg_flags
+}
+struct __WSABUF {
+  u_long                       len;                            //size_t iov_len (2nd member)
+  char FAR                     *buf;                           //void *iov_base (1st member)
+}
+struct _WSACMSGHDR {
+  UINT        cmsg_len;                                        //socklen_t cmsg_len
+  INT         cmsg_level;                              //int       cmsg_level
+  INT         cmsg_type;                               //int       cmsg_type;
+  followed by UCHAR cmsg_data[]
+}
+*/
+# define msg_name              name
+# define msg_namelen   namelen
+# define msg_iov               lpBuffers
+# define msg_iovlen            dwBufferCount
+# define msg_control   Control.buf
+# define msg_controllen        Control.len
+# define msg_flags             dwFlags
+# define iov_base              buf
+# define iov_len               len
+
+# define cmsghdr               _WSACMSGHDR
+# ifdef CMSG_DATA
+#  undef CMSG_DATA
+# endif
+# define CMSG_DATA             WSA_CMSG_DATA
+#endif
+
 #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
 #define DEFAULT_BUFF_SIZE 8192
 
@@ -132,7 +175,7 @@ static void do_from_to_zval_err(struct err_s *err,
        efree(user_msg);
        smart_str_free_ex(&path, 0);
 }
-__attribute__ ((format (printf, 2, 3)))
+ZEND_ATTRIBUTE_FORMAT(printf, 2 ,3)
 static void do_from_zval_err(ser_context *ctx, const char *fmt, ...)
 {
        va_list ap;
@@ -141,7 +184,7 @@ static void do_from_zval_err(ser_context *ctx, const char *fmt, ...)
        do_from_to_zval_err(&ctx->err, &ctx->keys, "user", fmt, ap);
        va_end(ap);
 }
-__attribute__ ((format (printf, 2, 3)))
+ZEND_ATTRIBUTE_FORMAT(printf, 2 ,3)
 static void do_to_zval_err(res_context *ctx, const char *fmt, ...)
 {
        va_list ap;
@@ -958,7 +1001,7 @@ static void to_zval_read_control_array(const char *msghdr_c, zval *zv, res_conte
 
        for (cmsg = CMSG_FIRSTHDR(msg);
                        cmsg != NULL && !ctx->err.has_error;
-                       cmsg = CMSG_NXTHDR(msg,cmsg)) {
+                       cmsg = CMSG_NXTHDR(msg, cmsg)) {
                zval *elem;
 
                ALLOC_INIT_ZVAL(elem);
@@ -1149,7 +1192,7 @@ static void to_zval_read_iov(const char *msghdr_c, zval *zv, res_context *ctx)
 
        for (i = 0; bytes_left > 0 && i < (uint)iovlen; i++) {
                zval    *elem;
-               size_t  len             = MIN(msghdr->msg_iov[i].iov_len, bytes_left);
+               size_t  len             = MIN(msghdr->msg_iov[i].iov_len, (size_t)bytes_left);
                char    *buf    = safe_emalloc(1, len, 1);
 
                MAKE_STD_ZVAL(elem);
index 70f31ba676e561778c73a10b4dd83c1efc006d29..7d515246a0bc66c08e407463420d0a3bed40e9c2 100644 (file)
@@ -2,8 +2,14 @@
 #define PHP_SOCK_CONVERSIONS_H 1
 
 #include <php.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
+
+#ifndef PHP_WIN32
+# include <netinet/in.h>
+# include <sys/socket.h>
+#else
+# include <Ws2tcpip.h>
+#endif
+
 #include "php_sockets.h"
 
 /* TYPE DEFINITIONS */
index 78da0c29e68f215514b0fc2f8979bf56e25b0e20..a5699c75149fc510e524b954e0ae5d4958a59f60 100644 (file)
 
 #if HAVE_SOCKETS
 
+#include <php.h>
+
 extern zend_module_entry sockets_module_entry;
 #define phpext_sockets_ptr &sockets_module_entry
 
 #ifdef PHP_WIN32
-#include <winsock.h>
+#include <Winsock2.h>
 #else
 #if HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
index b83b3ae48237824acb6ac3db995eac2c2234a784..f75fdcdedea4cfd8198a41b36416059a5ddde067 100644 (file)
 #define DEFAULT_BUFF_SIZE 8192
 #define MAX_ARRAY_KEY_SIZE 128
 
+#ifdef PHP_WIN32
+#include "windows_common.h"
+#include <Mswsock.h>
+#define IPV6_RECVPKTINFO       IPV6_PKTINFO
+#define IPV6_RECVHOPLIMIT      IPV6_HOPLIMIT
+#define msghdr _WSAMSG
+
+static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
+static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
+inline ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
+{
+       DWORD   recvd = 0,
+                       bytesReturned;
+       
+       if (WSARecvMsg == NULL) {
+               int res = WSAIoctl((SOCKET) sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                       &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
+                       &WSARecvMsg, sizeof(WSARecvMsg),
+                       &bytesReturned, NULL, NULL);
+               if (res != 0) {
+                       return -1;
+               }
+       }
+
+       msg->dwFlags = (DWORD)flags;
+       return WSARecvMsg((SOCKET)sockfd, msg, &recvd, NULL, NULL) == 0
+               ? (ssize_t)recvd
+               : -1;
+}
+inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
+{
+       DWORD sent = 0;
+       return WSASendMsg((SOCKET)sockfd, (struct msghdr*)msg, (DWORD)flags, &sent, NULL, NULL) == 0
+               ? (ssize_t)sent
+               : -1;
+}
+#endif
+
 #define LONG_CHECK_VALID_INT(l) \
        do { \
                if ((l) < INT_MIN && (l) > INT_MAX) { \
@@ -158,9 +196,7 @@ PHP_FUNCTION(socket_sendmsg)
 
                RETURN_LONG((long)res);
        } else {
-               SOCKETS_G(last_error) = errno;
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "error in sendmsg [%d]: %s",
-                               errno, sockets_strerror(errno TSRMLS_CC));
+               PHP_SOCKET_ERROR(php_sock, "error in sendmsg", errno);
                RETURN_FALSE;
        }
 }
@@ -285,6 +321,18 @@ int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname,
        switch (optname) {
 #ifdef IPV6_PKTINFO
        case IPV6_PKTINFO:
+#ifdef PHP_WIN32
+               if (Z_TYPE_PP(arg4) == IS_ARRAY) {
+                       php_error_docref0(NULL TSRMLS_CC, E_WARNING, "Windows does not "
+                                       "support sticky IPV6_PKTINFO");
+                       return FAILURE;
+               } else {
+                       /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
+                        * for the same effect. We define IPV6_RECVPKTINFO to be
+                        * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
+                       return 1;
+               }
+#endif
                opt_ptr = from_zval_run_conversions(*arg4, php_sock, from_zval_write_in6_pktinfo,
                                sizeof(struct in6_pktinfo),     "in6_pktinfo", &allocations, &err);
                if (err.has_error) {
index 19c61740d0cf793c4d20d27535bd06c56ae3720c..a40b6b4936cbbd8c8228beff47fdc35214451632 100644 (file)
@@ -3,7 +3,7 @@
 #include "php_sockets.h"
 
 #ifdef PHP_WIN32
-#include <Ws2tcpip.h>
+#include "windows_common.h"
 #else
 #include <netdb.h>
 #include <arpa/inet.h>
index 444d749fe73973f2610342e5dc6bf8f9f9d40605..665c73913f108c0534dcb91ab754ff485e357fab 100644 (file)
@@ -1,8 +1,16 @@
 #ifndef PHP_SOCKADR_CONV_H
 #define PHP_SOCKADR_CONV_H
 
+#define HAVE_SOCKETS 1
 #include <php_network.h>
-#include "php_sockets.h"
+#include "php_sockets.h" /* php_socket */
+
+#ifndef PHP_WIN32
+# include <netinet/in.h>
+#else
+# include <Winsock2.h>
+#endif
+
 
 /*
  * Convert an IPv6 literal or a hostname info a sockaddr_in6.
index 2aeb4b0fd3327de0b2dbd659588a07379153fb2b..5636cd3cdc9d17169859cffd50b92a21bcfed43a 100644 (file)
 #include "ext/standard/info.h"
 #include "php_ini.h"
 #ifdef PHP_WIN32
-# include "win32/inet.h"
-# include <winsock2.h>
+# include "windows_common.h"
+# include <win32/inet.h>
 # include <windows.h>
 # include <Ws2tcpip.h>
 # include "php_sockets.h"
-# include "win32/sockets.h"
-# define IS_INVALID_SOCKET(a)  (a->bsd_socket == INVALID_SOCKET)
-# ifdef EPROTONOSUPPORT
-#  undef EPROTONOSUPPORT
-# endif
-# ifdef ECONNRESET
-#  undef ECONNRESET
-# endif
-# define EPROTONOSUPPORT       WSAEPROTONOSUPPORT
-# define ECONNRESET            WSAECONNRESET
-# ifdef errno
-#  undef errno
-# endif
-# define errno                 WSAGetLastError()
-# define h_errno               WSAGetLastError()
-# define set_errno(a)          WSASetLastError(a)
-# define close(a)              closesocket(a)
-# include <IPHlpApi.h>
-# if _WIN32_WINNT >= 0x0600
-#  define HAVE_IF_NAMETOINDEX 1
-# endif
+# include <win32/sockets.h>
 #else
 # include <sys/types.h>
 # include <sys/socket.h>
@@ -650,8 +630,12 @@ PHP_MINIT_FUNCTION(sockets)
        REGISTER_LONG_CONSTANT("MSG_TRUNC",             MSG_TRUNC,              CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("MSG_PEEK",              MSG_PEEK,               CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("MSG_DONTROUTE", MSG_DONTROUTE,  CONST_CS | CONST_PERSISTENT);
+#ifdef MSG_EOR
        REGISTER_LONG_CONSTANT("MSG_EOR",               MSG_EOR,                CONST_CS | CONST_PERSISTENT);
+#endif
+#ifdef MSG_EOF
        REGISTER_LONG_CONSTANT("MSG_EOF",               MSG_EOF,                CONST_CS | CONST_PERSISTENT);
+#endif
 
 #ifdef MSG_CONFIRM
        REGISTER_LONG_CONSTANT("MSG_CONFIRM",   MSG_CONFIRM,    CONST_CS | CONST_PERSISTENT);
diff --git a/ext/sockets/tests/socket_sendrecvmsg_multi_msg-win32.phpt b/ext/sockets/tests/socket_sendrecvmsg_multi_msg-win32.phpt
new file mode 100644 (file)
index 0000000..3aba012
--- /dev/null
@@ -0,0 +1,110 @@
+--TEST--
+sendmsg()/recvmsg(): test ability to receive multiple messages (WIN32)
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets'))
+       die('skip sockets extension not available.');
+if (!defined('IPPROTO_IPV6'))
+       die('skip IPv6 not available.');
+if (substr(PHP_OS, 0, 3) != 'WIN')
+       die('skip Only for Windows!');
+/* Windows supports IPV6_RECVTCLASS and is able to receive the tclass via
+ * WSARecvMsg (though only the top 6 bits seem to reported), but WSASendMsg
+ * does not accept IPV6_TCLASS messages. We still  test that sendmsg() works
+ * corectly by sending an IPV6_PKTINFO message that will have no effect */
+
+--FILE--
+<?php
+include __DIR__."/mcast_helpers.php.inc";
+$addr = '::1';
+
+echo "creating send socket\n";
+$sends1 = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
+var_dump($sends1);
+$br = socket_bind($sends1, '::', 7001) or die("err");
+var_dump($br);
+socket_set_nonblock($sends1) or die("Could not put in non-blocking mode");
+
+echo "creating receive socket\n";
+$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
+var_dump($s);
+$br = socket_bind($s, '::0', 3000) or die("err");
+var_dump($br);
+
+socket_set_option($s, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1) or die("err");
+socket_set_option($s, IPPROTO_IPV6, IPV6_RECVTCLASS, 1) or die("err");
+
+$r = socket_sendmsg($sends1, [
+       "name" => [ "addr" => "::1", "port" => 3000],
+       "iov" => ["test ", "thing", "\n"],
+       "control" => [[
+               "level" => IPPROTO_IPV6,
+               "type" => IPV6_PKTINFO,
+               "data" => [
+                       'addr' => '::1',
+            'ifindex' => 1 /* we're assuming loopback is 1. Is this a safe assumption? */
+               ],
+       ]]
+], 0);
+var_dump($r);
+checktimeout($s, 500);
+
+$data = [
+    "name" => ["family" => AF_INET6, "addr" => "::1"],
+    "buffer_size" => 2000,
+    "controllen" => socket_cmsg_space(IPPROTO_IPV6, IPV6_PKTINFO) +
+                       socket_cmsg_space(IPPROTO_IPV6, IPV6_TCLASS),
+];
+if (!socket_recvmsg($s, $data, 0)) die("recvmsg");
+print_r($data);
+
+--EXPECTF--
+creating send socket
+resource(5) of type (Socket)
+bool(true)
+creating receive socket
+resource(6) of type (Socket)
+bool(true)
+int(11)
+Array
+(
+    [name] => Array
+        (
+            [family] => %d
+            [addr] => ::1
+            [port] => 7001
+            [flowinfo] => 0
+            [scope_id] => 0
+        )
+
+    [control] => Array
+        (
+            [0] => Array
+                (
+                    [level] => %d
+                    [type] => %d
+                    [data] => Array
+                        (
+                            [addr] => ::1
+                            [ifindex] => %d
+                        )
+
+                )
+
+            [1] => Array
+                (
+                    [level] => %d
+                    [type] => %d
+                    [data] => 0
+                )
+
+        )
+
+    [iov] => Array
+        (
+            [0] => test thing
+
+        )
+
+    [flags] => 0
+)
index 055e263f724c1b85bc493fd6b098f9d107673092..212f7e186f216c11cb447f08f43279473849bd54 100644 (file)
@@ -2,12 +2,15 @@
 sendmsg()/recvmsg(): test ability to receive multiple messages
 --SKIPIF--
 <?php
-if (!extension_loaded('sockets')) {
-die('skip sockets extension not available.');
-}
-if (!defined('IPPROTO_IPV6')) {
-die('skip IPv6 not available.');
-}
+if (!extension_loaded('sockets'))
+       die('skip sockets extension not available.');
+if (!defined('IPPROTO_IPV6'))
+       die('skip IPv6 not available.');
+if (substr(PHP_OS, 0, 3) == 'WIN')
+       die('skip Not for the Windows!');
+/* Windows supports IPV6_RECVTCLASS and is able to receive the tclass via
+ * WSARecvMsg (though only the top 6 bits seem to reported), but WSASendMsg
+ * does not accept IPV6_TCLASS messages */
 
 --FILE--
 <?php
@@ -36,7 +39,7 @@ $r = socket_sendmsg($sends1, [
        "control" => [[
                "level" => IPPROTO_IPV6,
                "type" => IPV6_TCLASS,
-               "data" => 42,
+               "data" => 40,
        ]]
 ], 0);
 var_dump($r);
@@ -88,7 +91,7 @@ Array
                 (
                     [level] => %d
                     [type] => %d
-                    [data] => 42
+                    [data] => 40
                 )
 
         )
index 53320cad0c3ae99258139075e52a553e63e6fe96..27b6ae59c5d12c14466c9fe387c5969130bce6a1 100644 (file)
@@ -8,7 +8,8 @@ die('skip sockets extension not available.');
 if (!defined('IPPROTO_IPV6')) {
 die('skip IPv6 not available.');
 }
-
+if (substr(PHP_OS, 0, 3) == 'WIN')
+       die('skip Not for Windows!');
 --FILE--
 <?php
 
diff --git a/ext/sockets/windows_common.h b/ext/sockets/windows_common.h
new file mode 100644 (file)
index 0000000..1dc966a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2012 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+ */
+#ifndef WINDOWS_COMMON_H
+#define WINDOWS_COMMON_H
+
+#include <Winsock2.h>
+#include <IPHlpApi.h> /* conflicting definition of CMSG_DATA */
+
+#define HAVE_IF_NAMETOINDEX 1
+
+#define IS_INVALID_SOCKET(a)   (a->bsd_socket == INVALID_SOCKET)
+#ifdef EPROTONOSUPPORT
+# undef EPROTONOSUPPORT
+#endif
+#ifdef ECONNRESET
+# undef ECONNRESET
+#endif
+#define EPROTONOSUPPORT        WSAEPROTONOSUPPORT
+#define ECONNRESET             WSAECONNRESET
+#ifdef errno
+# undef errno
+#endif
+#define errno                  WSAGetLastError()
+#define h_errno                        WSAGetLastError()
+#define set_errno(a)   WSASetLastError(a)
+#define close(a)               closesocket(a)
+
+#endif
\ No newline at end of file