--- embedaddon/php/ext/sockets/sockets.c	2012/02/21 23:48:01	1.1.1.1
+++ embedaddon/php/ext/sockets/sockets.c	2013/07/22 01:32:01	1.1.1.3
@@ -2,7 +2,7 @@
    +----------------------------------------------------------------------+
    | PHP Version 5                                                        |
    +----------------------------------------------------------------------+
-   | Copyright (c) 1997-2012 The PHP Group                                |
+   | Copyright (c) 1997-2013 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        |
@@ -15,11 +15,12 @@
    | Authors: Chris Vandomelen <chrisv@b0rked.dhs.org>                    |
    |          Sterling Hughes  <sterling@php.net>                         |
    |          Jason Greene     <jason@php.net>                            |
+   |          Gustavo Lopes    <cataphract@php.net>                       |
    | WinSock: Daniel Beulshausen <daniel@php4win.de>                      |
    +----------------------------------------------------------------------+
  */
 
-/* $Id: sockets.c,v 1.1.1.1 2012/02/21 23:48:01 misho Exp $ */
+/* $Id: sockets.c,v 1.1.1.3 2013/07/22 01:32:01 misho Exp $ */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -56,6 +57,9 @@
 # define h_errno		WSAGetLastError()
 # define set_errno(a)		WSASetLastError(a)
 # define close(a)		closesocket(a)
+# if _WIN32_WINNT >= 0x0600 && SOCKETS_ENABLE_VISTA_API
+#  define HAVE_IF_NAMETOINDEX 1
+# endif
 #else
 # include <sys/types.h>
 # include <sys/socket.h>
@@ -73,8 +77,16 @@
 # define IS_INVALID_SOCKET(a)	(a->bsd_socket < 0)
 # define set_errno(a) (errno = a)
 # include "php_sockets.h"
+# if defined(_AIX) && !defined(HAVE_SA_SS_FAMILY)
+# define ss_family __ss_family
+# endif
+# if HAVE_IF_NAMETOINDEX
+#  include <net/if.h>
+# endif
 #endif
 
+#include "multicast.h"
+
 ZEND_DECLARE_MODULE_GLOBALS(sockets)
 static PHP_GINIT_FUNCTION(sockets);
 
@@ -102,13 +114,17 @@ static PHP_GINIT_FUNCTION(sockets);
 
 static char *php_strerror(int error TSRMLS_DC);
 
+#define PHP_SOCKET_ERROR(socket, msg, errn) \
+		do { \
+			int _err = (errn); /* save value to avoid repeated calls to WSAGetLastError() on Windows */ \
+			(socket)->error = _err; \
+			SOCKETS_G(last_error) = _err; \
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s [%d]: %s", msg, _err, php_strerror(_err TSRMLS_CC)); \
+		} while (0)
+
 #define PHP_NORMAL_READ 0x0001
 #define PHP_BINARY_READ 0x0002
 
-#define PHP_SOCKET_ERROR(socket,msg,errn)	socket->error = errn;	\
-						SOCKETS_G(last_error) = errn; \
-						php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s [%d]: %s", msg, errn, php_strerror(errn TSRMLS_CC))
-
 static int le_socket;
 #define le_socket_name php_sockets_le_socket_name
 
@@ -261,8 +277,47 @@ 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()
 /* }}} */
 
+PHP_MINIT_FUNCTION(sockets);
+PHP_MINFO_FUNCTION(sockets);
+PHP_RSHUTDOWN_FUNCTION(sockets);
+
+PHP_FUNCTION(socket_select);
+PHP_FUNCTION(socket_create_listen);
+#ifdef HAVE_SOCKETPAIR
+PHP_FUNCTION(socket_create_pair);
+#endif
+PHP_FUNCTION(socket_accept);
+PHP_FUNCTION(socket_set_nonblock);
+PHP_FUNCTION(socket_set_block);
+PHP_FUNCTION(socket_listen);
+PHP_FUNCTION(socket_close);
+PHP_FUNCTION(socket_write);
+PHP_FUNCTION(socket_read);
+PHP_FUNCTION(socket_getsockname);
+PHP_FUNCTION(socket_getpeername);
+PHP_FUNCTION(socket_create);
+PHP_FUNCTION(socket_connect);
+PHP_FUNCTION(socket_strerror);
+PHP_FUNCTION(socket_bind);
+PHP_FUNCTION(socket_recv);
+PHP_FUNCTION(socket_send);
+PHP_FUNCTION(socket_recvfrom);
+PHP_FUNCTION(socket_sendto);
+PHP_FUNCTION(socket_get_option);
+PHP_FUNCTION(socket_set_option);
+#ifdef HAVE_SHUTDOWN
+PHP_FUNCTION(socket_shutdown);
+#endif
+PHP_FUNCTION(socket_last_error);
+PHP_FUNCTION(socket_clear_error);
+PHP_FUNCTION(socket_import_stream);
+
 /* {{{ sockets_functions[]
  */
 const zend_function_entry sockets_functions[] = {
@@ -295,6 +350,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)
@@ -335,11 +391,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);
 }
 /* }}} */
@@ -348,7 +426,7 @@ static int php_open_listen_sock(php_socket **php_sock,
 {
 	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;
 
@@ -396,7 +474,7 @@ static int php_open_listen_sock(php_socket **php_sock,
 
 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;
 
@@ -605,6 +683,111 @@ static int php_set_inet_addr(struct sockaddr_in *sin, 
 }
 /* }}} */
 
+/* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6,
+ * depending on the socket) */
+static int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
+{
+	if (php_sock->type == AF_INET) {
+		struct sockaddr_in t = {0};
+		if (php_set_inet_addr(&t, string, php_sock TSRMLS_CC)) {
+			memcpy(ss, &t, sizeof t);
+			ss->ss_family = AF_INET;
+			*ss_len = sizeof(t);
+			return 1;
+		}
+	}
+#if HAVE_IPV6
+	else if (php_sock->type == AF_INET6) {
+		struct sockaddr_in6 t = {0};
+		if (php_set_inet6_addr(&t, string, php_sock TSRMLS_CC)) {
+			memcpy(ss, &t, sizeof t);
+			ss->ss_family = AF_INET6;
+			*ss_len = sizeof(t);
+			return 1;
+		}
+	}
+#endif
+	else {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+			"IP address used in the context of an unexpected type of socket");
+	}
+	return 0;
+}
+
+static int php_get_if_index_from_zval(zval *val, unsigned *out TSRMLS_DC)
+{
+	int ret;
+
+	if (Z_TYPE_P(val) == IS_LONG) {
+		if (Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > UINT_MAX) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"the interface index cannot be negative or larger than %u;"
+				" given %ld", UINT_MAX, Z_LVAL_P(val));
+			ret = FAILURE;
+		} else {
+			*out = Z_LVAL_P(val);
+			ret = SUCCESS;
+		}
+	} else {
+#if HAVE_IF_NAMETOINDEX
+		unsigned int ind;
+		zval_add_ref(&val);
+		convert_to_string_ex(&val);
+		ind = if_nametoindex(Z_STRVAL_P(val));
+		if (ind == 0) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"no interface with name \"%s\" could be found", Z_STRVAL_P(val));
+			ret = FAILURE;
+		} else {
+			*out = ind;
+			ret = SUCCESS;
+		}
+		zval_ptr_dtor(&val);
+#else
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"this platform does not support looking up an interface by "
+				"name, an integer interface index must be supplied instead");
+		ret = FAILURE;
+#endif
+	}
+
+	return ret;
+}
+
+static int php_get_if_index_from_array(const HashTable *ht, const char *key,
+	php_socket *sock, unsigned int *if_index TSRMLS_DC)
+{
+	zval **val;
+	
+	if (zend_hash_find(ht, key, strlen(key) + 1, (void **)&val) == FAILURE) {
+		*if_index = 0; /* default: 0 */
+		return SUCCESS;
+	}
+	
+	return php_get_if_index_from_zval(*val, if_index TSRMLS_CC);
+}
+
+static int php_get_address_from_array(const HashTable *ht, const char *key,
+	php_socket *sock, php_sockaddr_storage *ss, socklen_t *ss_len TSRMLS_DC)
+{
+	zval **val,
+		 *valcp;
+	
+	if (zend_hash_find(ht, key, strlen(key) + 1, (void **)&val) == FAILURE) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", key);
+		return FAILURE;
+	}
+	valcp = *val;
+	zval_add_ref(&valcp);
+	convert_to_string_ex(val);	
+	if (!php_set_inet46_addr(ss, ss_len, Z_STRVAL_P(valcp), sock TSRMLS_CC)) {
+		zval_ptr_dtor(&valcp);
+		return FAILURE;
+	}
+	zval_ptr_dtor(&valcp);
+	return SUCCESS;
+}
+
 /* {{{ PHP_GINIT_FUNCTION */
 static PHP_GINIT_FUNCTION(sockets)
 {
@@ -617,8 +800,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);
@@ -646,6 +827,9 @@ PHP_MINIT_FUNCTION(sockets)
 #endif
 	REGISTER_LONG_CONSTANT("SO_DEBUG",		SO_DEBUG,		CONST_CS | CONST_PERSISTENT);
 	REGISTER_LONG_CONSTANT("SO_REUSEADDR",	SO_REUSEADDR,	CONST_CS | CONST_PERSISTENT);
+#ifdef SO_REUSEPORT
+	REGISTER_LONG_CONSTANT("SO_REUSEPORT",	SO_REUSEPORT,	CONST_CS | CONST_PERSISTENT);
+#endif
 	REGISTER_LONG_CONSTANT("SO_KEEPALIVE",	SO_KEEPALIVE,	CONST_CS | CONST_PERSISTENT);
 	REGISTER_LONG_CONSTANT("SO_DONTROUTE",	SO_DONTROUTE,	CONST_CS | CONST_PERSISTENT);
 	REGISTER_LONG_CONSTANT("SO_LINGER",		SO_LINGER,		CONST_CS | CONST_PERSISTENT);
@@ -667,19 +851,48 @@ PHP_MINIT_FUNCTION(sockets)
 	REGISTER_LONG_CONSTANT("PHP_NORMAL_READ", PHP_NORMAL_READ, CONST_CS | CONST_PERSISTENT);
 	REGISTER_LONG_CONSTANT("PHP_BINARY_READ", PHP_BINARY_READ, CONST_CS | CONST_PERSISTENT);
 
+#ifndef RFC3678_API
+#define MCAST_JOIN_GROUP			IP_ADD_MEMBERSHIP
+#define MCAST_LEAVE_GROUP			IP_DROP_MEMBERSHIP
+#ifdef HAS_MCAST_EXT
+#define MCAST_BLOCK_SOURCE			IP_BLOCK_SOURCE
+#define MCAST_UNBLOCK_SOURCE		IP_UNBLOCK_SOURCE
+#define MCAST_JOIN_SOURCE_GROUP		IP_ADD_SOURCE_MEMBERSHIP
+#define MCAST_LEAVE_SOURCE_GROUP	IP_DROP_SOURCE_MEMBERSHIP
+#endif
+#endif
+	
+	REGISTER_LONG_CONSTANT("MCAST_JOIN_GROUP",			MCAST_JOIN_GROUP,			CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("MCAST_LEAVE_GROUP",			MCAST_LEAVE_GROUP,			CONST_CS | CONST_PERSISTENT);
+#ifdef HAS_MCAST_EXT
+	REGISTER_LONG_CONSTANT("MCAST_BLOCK_SOURCE",		MCAST_BLOCK_SOURCE,			CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("MCAST_UNBLOCK_SOURCE",		MCAST_UNBLOCK_SOURCE,		CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("MCAST_JOIN_SOURCE_GROUP",	MCAST_JOIN_SOURCE_GROUP,	CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("MCAST_LEAVE_SOURCE_GROUP",	MCAST_LEAVE_SOURCE_GROUP,	CONST_CS | CONST_PERSISTENT);
+#endif
+
+	REGISTER_LONG_CONSTANT("IP_MULTICAST_IF",			IP_MULTICAST_IF,		CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("IP_MULTICAST_TTL",			IP_MULTICAST_TTL,		CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("IP_MULTICAST_LOOP",			IP_MULTICAST_LOOP,		CONST_CS | CONST_PERSISTENT);
+#if HAVE_IPV6
+	REGISTER_LONG_CONSTANT("IPV6_MULTICAST_IF",			IPV6_MULTICAST_IF,		CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("IPV6_MULTICAST_HOPS",		IPV6_MULTICAST_HOPS,	CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("IPV6_MULTICAST_LOOP",		IPV6_MULTICAST_LOOP,	CONST_CS | CONST_PERSISTENT);
+#endif
+
 #ifndef WIN32
 # include "unix_socket_constants.h"
 #else
 # include "win32_socket_constants.h"
 #endif
 
-	if ((pe = getprotobyname("tcp"))) {
-		REGISTER_LONG_CONSTANT("SOL_TCP", pe->p_proto, CONST_CS | CONST_PERSISTENT);
-	}
+	REGISTER_LONG_CONSTANT("IPPROTO_IP",	IPPROTO_IP,		CONST_CS | CONST_PERSISTENT);
+#if HAVE_IPV6
+	REGISTER_LONG_CONSTANT("IPPROTO_IPV6",	IPPROTO_IPV6,	CONST_CS | CONST_PERSISTENT);
+#endif
 
-	if ((pe = getprotobyname("udp"))) {
-		REGISTER_LONG_CONSTANT("SOL_UDP", pe->p_proto, CONST_CS | CONST_PERSISTENT);
-	}
+	REGISTER_LONG_CONSTANT("SOL_TCP",		IPPROTO_TCP,	CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("SOL_UDP",		IPPROTO_UDP,	CONST_CS | CONST_PERSISTENT);
 
 	return SUCCESS;
 }
@@ -912,6 +1125,20 @@ 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 = 0;
+				RETURN_TRUE;
+			}
+		}
+	}
 
 	if (php_set_sock_blocking(php_sock->bsd_socket, 0 TSRMLS_CC) == SUCCESS) {
 		php_sock->blocking = 0;
@@ -935,6 +1162,22 @@ 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;
@@ -980,6 +1223,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));
 }
 /* }}} */
@@ -1237,7 +1490,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);
@@ -1281,11 +1534,6 @@ PHP_FUNCTION(socket_connect)
 {
 	zval				*arg1;
 	php_socket			*php_sock;
-	struct sockaddr_in	sin;
-#if HAVE_IPV6
-	struct sockaddr_in6	sin6;
-#endif
-	struct sockaddr_un	s_un;
 	char				*addr;
 	int					retval, addr_len;
 	long				port = 0;
@@ -1299,7 +1547,9 @@ PHP_FUNCTION(socket_connect)
 
 	switch(php_sock->type) {
 #if HAVE_IPV6
-		case AF_INET6:
+		case AF_INET6: {
+			struct sockaddr_in6 sin6 = {0};
+			
 			if (argc != 3) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Socket of type AF_INET6 requires 3 arguments");
 				RETURN_FALSE;
@@ -1316,15 +1566,16 @@ PHP_FUNCTION(socket_connect)
 
 			retval = connect(php_sock->bsd_socket, (struct sockaddr *)&sin6, sizeof(struct sockaddr_in6));
 			break;
+		}
 #endif
-		case AF_INET:
+		case AF_INET: {
+			struct sockaddr_in sin = {0};
+			
 			if (argc != 3) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Socket of type AF_INET requires 3 arguments");
 				RETURN_FALSE;
 			}
 
-			memset(&sin, 0, sizeof(struct sockaddr_in));
-
 			sin.sin_family = AF_INET;
 			sin.sin_port   = htons((unsigned short int)port);
 
@@ -1334,19 +1585,22 @@ PHP_FUNCTION(socket_connect)
 
 			retval = connect(php_sock->bsd_socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
 			break;
+		}
 
-		case AF_UNIX:
+		case AF_UNIX: {
+			struct sockaddr_un s_un = {0};
+			
 			if (addr_len >= sizeof(s_un.sun_path)) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Path too long");
 				RETURN_FALSE;
 			}
-				
-			memset(&s_un, 0, sizeof(struct sockaddr_un));
 
 			s_un.sun_family = AF_UNIX;
 			memcpy(&s_un.sun_path, addr, addr_len);
-			retval = connect(php_sock->bsd_socket, (struct sockaddr *) &s_un, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + addr_len);
+			retval = connect(php_sock->bsd_socket, (struct sockaddr *) &s_un,
+				(socklen_t)(XtOffsetOf(struct sockaddr_un, sun_path) + addr_len));
 			break;
+		}
 
 		default:
 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported socket type %d", php_sock->type);
@@ -1568,8 +1822,8 @@ PHP_FUNCTION(socket_recvfrom)
 			retval = recvfrom(php_sock->bsd_socket, recv_buf, arg3, arg4, (struct sockaddr *)&s_un, (socklen_t *)&slen);
 
 			if (retval < 0) {
-				efree(recv_buf);
 				PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
+				efree(recv_buf);
 				RETURN_FALSE;
 			}
 
@@ -1593,8 +1847,8 @@ PHP_FUNCTION(socket_recvfrom)
 			retval = recvfrom(php_sock->bsd_socket, recv_buf, arg3, arg4, (struct sockaddr *)&sin, (socklen_t *)&slen);
 
 			if (retval < 0) {
-				efree(recv_buf);
 				PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
+				efree(recv_buf);
 				RETURN_FALSE;
 			}
 
@@ -1622,8 +1876,8 @@ PHP_FUNCTION(socket_recvfrom)
 			retval = recvfrom(php_sock->bsd_socket, recv_buf, arg3, arg4, (struct sockaddr *)&sin6, (socklen_t *)&slen);
 
 			if (retval < 0) {
-				efree(recv_buf);
 				PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
+				efree(recv_buf);
 				RETURN_FALSE;
 			}
 
@@ -1746,6 +2000,26 @@ PHP_FUNCTION(socket_get_option)
 
 	ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);
 
+	if (level == IPPROTO_IP) {
+		switch (optname) {
+		case IP_MULTICAST_IF: {
+			struct in_addr if_addr;
+			unsigned int if_index;
+			optlen = sizeof(if_addr);
+			if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&if_addr, &optlen) != 0) {
+				PHP_SOCKET_ERROR(php_sock, "unable to retrieve socket option", errno);
+				RETURN_FALSE;
+			}
+			if (php_add4_to_if_index(&if_addr, php_sock, &if_index TSRMLS_CC) == SUCCESS) {
+				RETURN_LONG((long) if_index);
+			} else {
+				RETURN_FALSE;
+			}
+		}
+		}
+	}
+	
+	/* sol_socket options and general case */
 	switch(optname) {
 		case SO_LINGER:
 			optlen = sizeof(linger_val);
@@ -1786,7 +2060,7 @@ PHP_FUNCTION(socket_get_option)
 			add_assoc_long(return_value, "sec", tv.tv_sec);
 			add_assoc_long(return_value, "usec", tv.tv_usec);
 			break;
-
+		
 		default:
 			optlen = sizeof(other_val);
 
@@ -1794,6 +2068,8 @@ PHP_FUNCTION(socket_get_option)
 				PHP_SOCKET_ERROR(php_sock, "unable to retrieve socket option", errno);
 				RETURN_FALSE;
 			}
+			if (optlen == 1)
+				other_val = *((unsigned char *)&other_val);
 
 			RETURN_LONG(other_val);
 			break;
@@ -1801,30 +2077,126 @@ PHP_FUNCTION(socket_get_option)
 }
 /* }}} */
 
+static int php_do_mcast_opt(php_socket *php_sock, int level, int optname, zval **arg4 TSRMLS_DC)
+{
+	HashTable		 		*opt_ht;
+	unsigned int			if_index;
+	int						retval;
+	int (*mcast_req_fun)(php_socket *, int, struct sockaddr *, socklen_t,
+		unsigned TSRMLS_DC);
+#ifdef HAS_MCAST_EXT
+	int (*mcast_sreq_fun)(php_socket *, int, struct sockaddr *, socklen_t,
+		struct sockaddr *, socklen_t, unsigned TSRMLS_DC);
+#endif
+
+	switch (optname) {
+	case MCAST_JOIN_GROUP:
+		mcast_req_fun = &php_mcast_join;
+		goto mcast_req_fun;
+	case MCAST_LEAVE_GROUP:
+		{
+			php_sockaddr_storage	group = {0};
+			socklen_t				glen;
+
+			mcast_req_fun = &php_mcast_leave;
+mcast_req_fun:
+			convert_to_array_ex(arg4);
+			opt_ht = HASH_OF(*arg4);
+
+			if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
+				&glen TSRMLS_CC) == FAILURE) {
+					return FAILURE;
+			}
+			if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
+				&if_index TSRMLS_CC) == FAILURE) {
+					return FAILURE;
+			}
+
+			retval = mcast_req_fun(php_sock, level, (struct sockaddr*)&group,
+				glen, if_index TSRMLS_CC);
+			break;
+		}
+
+#ifdef HAS_MCAST_EXT
+	case MCAST_BLOCK_SOURCE:
+		mcast_sreq_fun = &php_mcast_block_source;
+		goto mcast_sreq_fun;
+	case MCAST_UNBLOCK_SOURCE:
+		mcast_sreq_fun = &php_mcast_unblock_source;
+		goto mcast_sreq_fun;
+	case MCAST_JOIN_SOURCE_GROUP:
+		mcast_sreq_fun = &php_mcast_join_source;
+		goto mcast_sreq_fun;
+	case MCAST_LEAVE_SOURCE_GROUP:
+		{
+			php_sockaddr_storage	group = {0},
+									source = {0};
+			socklen_t				glen,
+									slen;
+			
+			mcast_sreq_fun = &php_mcast_leave_source;
+		mcast_sreq_fun:
+			convert_to_array_ex(arg4);
+			opt_ht = HASH_OF(*arg4);
+			
+			if (php_get_address_from_array(opt_ht, "group", php_sock, &group,
+					&glen TSRMLS_CC) == FAILURE) {
+				return FAILURE;
+			}
+			if (php_get_address_from_array(opt_ht, "source", php_sock, &source,
+					&slen TSRMLS_CC) == FAILURE) {
+				return FAILURE;
+			}
+			if (php_get_if_index_from_array(opt_ht, "interface", php_sock,
+					&if_index TSRMLS_CC) == FAILURE) {
+				return FAILURE;
+			}
+			
+			retval = mcast_sreq_fun(php_sock, level, (struct sockaddr*)&group,
+					glen, (struct sockaddr*)&source, slen, if_index TSRMLS_CC);
+			break;
+		}
+#endif
+	default:
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+			"unexpected option in php_do_mcast_opt (level %d, option %d). "
+			"This is a bug.", level, optname);
+		return FAILURE;
+	}
+
+	if (retval != 0) {
+		if (retval != -2) { /* error, but message already emitted */
+			PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
+		}
+		return FAILURE;
+	}
+	return SUCCESS;
+}
+
 /* {{{ proto bool socket_set_option(resource socket, int level, int optname, int|array optval)
    Sets socket options for the socket */
 PHP_FUNCTION(socket_set_option)
 {
-	zval			*arg1, **arg4;
-	struct linger	lv;
-	php_socket		*php_sock;
-	int				ov, optlen, retval;
+	zval					*arg1, **arg4;
+	struct linger			lv;
+	php_socket				*php_sock;
+	int						ov, optlen, retval;
 #ifdef PHP_WIN32
-	int				timeout;
+	int						timeout;
 #else
-	struct			timeval tv;
+	struct					timeval tv;
 #endif
-	long			level, optname;
-	void 			*opt_ptr;
-	HashTable 		*opt_ht;
-	zval 			**l_onoff, **l_linger;
-	zval 			**sec, **usec;
-	/* key name constants */
-	char			*l_onoff_key = "l_onoff";
-	char			*l_linger_key = "l_linger";
-	char			*sec_key = "sec";
-	char			*usec_key = "usec";
-
+	long					level, optname;
+	void 					*opt_ptr;
+	HashTable		 		*opt_ht;
+	zval 					**l_onoff, **l_linger;
+	zval		 			**sec, **usec;
+	
+	/* Multicast */
+	unsigned int			if_index;
+	struct in_addr			if_addr;
+	unsigned char			ipv4_mcast_ttl_lback;
+	
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rllZ", &arg1, &level, &optname, &arg4) == FAILURE) {
 		return;
 	}
@@ -1833,16 +2205,110 @@ PHP_FUNCTION(socket_set_option)
 
 	set_errno(0);
 
+	if (level == IPPROTO_IP) {
+		switch (optname) {
+		case MCAST_JOIN_GROUP:
+		case MCAST_LEAVE_GROUP:
+#ifdef HAS_MCAST_EXT
+		case MCAST_BLOCK_SOURCE:
+		case MCAST_UNBLOCK_SOURCE:
+		case MCAST_JOIN_SOURCE_GROUP:
+		case MCAST_LEAVE_SOURCE_GROUP:
+#endif
+			if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
+				RETURN_FALSE;
+			} else {
+				RETURN_TRUE;
+			}
+
+		case IP_MULTICAST_IF:
+			if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
+				RETURN_FALSE;
+			}
+
+			if (php_if_index_to_addr4(if_index, php_sock, &if_addr TSRMLS_CC) == FAILURE) {
+				RETURN_FALSE;
+			}
+			opt_ptr = &if_addr;
+			optlen	= sizeof(if_addr);
+			goto dosockopt;
+
+		case IP_MULTICAST_LOOP:
+			convert_to_boolean_ex(arg4);
+			goto ipv4_loop_ttl;
+		case IP_MULTICAST_TTL:
+			convert_to_long_ex(arg4);
+			if (Z_LVAL_PP(arg4) < 0L || Z_LVAL_PP(arg4) > 255L) {
+				php_error_docref(NULL TSRMLS_CC, E_WARNING,
+						"Expected a value between 0 and 255");
+				RETURN_FALSE;
+			}
+ipv4_loop_ttl:
+			ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_PP(arg4);
+			opt_ptr = &ipv4_mcast_ttl_lback;
+			optlen	= sizeof(ipv4_mcast_ttl_lback);
+			goto dosockopt;
+		}
+	}
+
+#if HAVE_IPV6
+	else if (level == IPPROTO_IPV6) {
+		switch (optname) {
+		case MCAST_JOIN_GROUP:
+		case MCAST_LEAVE_GROUP:
+#ifdef HAS_MCAST_EXT
+		case MCAST_BLOCK_SOURCE:
+		case MCAST_UNBLOCK_SOURCE:
+		case MCAST_JOIN_SOURCE_GROUP:
+		case MCAST_LEAVE_SOURCE_GROUP:
+#endif
+			if (php_do_mcast_opt(php_sock, level, optname, arg4 TSRMLS_CC) == FAILURE) {
+				RETURN_FALSE;
+			} else {
+				RETURN_TRUE;
+			}
+
+		case IPV6_MULTICAST_IF:
+			if (php_get_if_index_from_zval(*arg4, &if_index TSRMLS_CC) == FAILURE) {
+				RETURN_FALSE;
+			}
+			
+			opt_ptr = &if_index;
+			optlen	= sizeof(if_index);
+			goto dosockopt;
+
+		case IPV6_MULTICAST_LOOP:
+			convert_to_boolean_ex(arg4);
+			goto ipv6_loop_hops;
+		case IPV6_MULTICAST_HOPS:
+			convert_to_long_ex(arg4);
+			if (Z_LVAL_PP(arg4) < -1L || Z_LVAL_PP(arg4) > 255L) {
+				php_error_docref(NULL TSRMLS_CC, E_WARNING,
+						"Expected a value between -1 and 255");
+				RETURN_FALSE;
+			}
+ipv6_loop_hops:
+			ov = (int) Z_LVAL_PP(arg4);
+			opt_ptr = &ov;
+			optlen	= sizeof(ov);
+			goto dosockopt;
+		}
+	}
+#endif
+
 	switch (optname) {
-		case SO_LINGER:
+		case SO_LINGER: {
+			const char l_onoff_key[] = "l_onoff";
+			const char l_linger_key[] = "l_linger";
+
 			convert_to_array_ex(arg4);
 			opt_ht = HASH_OF(*arg4);
 
-			if (zend_hash_find(opt_ht, l_onoff_key, strlen(l_onoff_key) + 1, (void **)&l_onoff) == FAILURE) {
+			if (zend_hash_find(opt_ht, l_onoff_key, sizeof(l_onoff_key), (void **)&l_onoff) == FAILURE) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", l_onoff_key);
 				RETURN_FALSE;
 			}
-			if (zend_hash_find(opt_ht, l_linger_key, strlen(l_linger_key) + 1, (void **)&l_linger) == FAILURE) {
+			if (zend_hash_find(opt_ht, l_linger_key, sizeof(l_linger_key), (void **)&l_linger) == FAILURE) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", l_linger_key);
 				RETURN_FALSE;
 			}
@@ -1856,17 +2322,21 @@ PHP_FUNCTION(socket_set_option)
 			optlen = sizeof(lv);
 			opt_ptr = &lv;
 			break;
+		}
 
 		case SO_RCVTIMEO:
-		case SO_SNDTIMEO:
+		case SO_SNDTIMEO: {
+			const char sec_key[] = "sec";
+			const char usec_key[] = "usec";
+
 			convert_to_array_ex(arg4);
 			opt_ht = HASH_OF(*arg4);
 
-			if (zend_hash_find(opt_ht, sec_key, strlen(sec_key) + 1, (void **)&sec) == FAILURE) {
+			if (zend_hash_find(opt_ht, sec_key, sizeof(sec_key), (void **)&sec) == FAILURE) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", sec_key);
 				RETURN_FALSE;
 			}
-			if (zend_hash_find(opt_ht, usec_key, strlen(usec_key) + 1, (void **)&usec) == FAILURE) {
+			if (zend_hash_find(opt_ht, usec_key, sizeof(usec_key), (void **)&usec) == FAILURE) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "no key \"%s\" passed in optval", usec_key);
 				RETURN_FALSE;
 			}
@@ -1884,7 +2354,8 @@ PHP_FUNCTION(socket_set_option)
 			opt_ptr = &timeout;
 #endif
 			break;
-
+		}
+		
 		default:
 			convert_to_long_ex(arg4);
 			ov = Z_LVAL_PP(arg4);
@@ -1894,10 +2365,12 @@ PHP_FUNCTION(socket_set_option)
 			break;
 	}
 
+dosockopt:
 	retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
-
 	if (retval != 0) {
-		PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
+		if (retval != -2) { /* error, but message already emitted */
+			PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
+		}
 		RETURN_FALSE;
 	}
 
@@ -1919,8 +2392,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
@@ -2034,6 +2507,83 @@ PHP_FUNCTION(socket_clear_error)
 	}
 
 	return;
+}
+/* }}} */
+
+/* {{{ 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);
+#ifndef PHP_WIN32
+	int					 t;
+#endif
+
+	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);
+	RETURN_FALSE;
 }
 /* }}} */