summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Walker <dave@mudsite.com>2016-08-05 18:01:41 -0600
committerDavid Walker <dave@mudsite.com>2016-08-12 11:29:34 -0600
commit7373ce914914e9a434216554a7e29aa4d1775d97 (patch)
treeab8eba72d9f769688ae36c01971624c4a51e74f5
parent02544170821b91c77e0786c3b1ff8f58f916a48b (diff)
downloadphp-git-7373ce914914e9a434216554a7e29aa4d1775d97.tar.gz
Fix #72733: Expose getaddrinfo C function, and supporting connect/bind
Feature request was to expose getaddrinfo(). I accomplish this by having an array of resources that are the addrinfo structures. The resources can be used in new functions to connect/bind, and one function to examine the contents of the resource.
-rw-r--r--ext/sockets/php_sockets.h2
-rw-r--r--ext/sockets/sockets.c296
-rw-r--r--ext/sockets/tests/socket_addrinfo_bind.phpt18
-rw-r--r--ext/sockets/tests/socket_addrinfo_connect.phpt18
-rw-r--r--ext/sockets/tests/socket_addrinfo_explain.phpt34
-rw-r--r--ext/sockets/tests/socket_addrinfo_lookup.phpt18
6 files changed, 386 insertions, 0 deletions
diff --git a/ext/sockets/php_sockets.h b/ext/sockets/php_sockets.h
index ce5cdaf313..37f3453c01 100644
--- a/ext/sockets/php_sockets.h
+++ b/ext/sockets/php_sockets.h
@@ -74,8 +74,10 @@ struct sockaddr_un {
PHP_SOCKETS_API int php_sockets_le_socket(void);
PHP_SOCKETS_API php_socket *php_create_socket(void);
PHP_SOCKETS_API void php_destroy_socket(zend_resource *rsrc);
+PHP_SOCKETS_API void php_destroy_sockaddr(zend_resource *rsrc);
#define php_sockets_le_socket_name "Socket"
+#define php_sockets_le_addrinfo_name "AddressInfo"
#define PHP_SOCKET_ERROR(socket, msg, errn) \
do { \
diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c
index c23f984720..c39a6172ae 100644
--- a/ext/sockets/sockets.c
+++ b/ext/sockets/sockets.c
@@ -97,6 +97,9 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets)
static int le_socket;
#define le_socket_name php_sockets_le_socket_name
+static int le_addrinfo;
+#define le_addrinfo_name php_sockets_le_addrinfo_name
+
/* {{{ arginfo */
ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_select, 0, 0, 4)
ZEND_ARG_INFO(1, read_fds)
@@ -271,6 +274,25 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_cmsg_space, 0, 0, 2)
ZEND_ARG_INFO(0, level)
ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_addrinfo_lookup, 0, 0, 1)
+ ZEND_ARG_INFO(0, host)
+ ZEND_ARG_INFO(0, service)
+ ZEND_ARG_INFO(0, hints)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_addrinfo_connect, 0, 0, 1)
+ ZEND_ARG_INFO(0, addr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_addrinfo_bind, 0, 0, 1)
+ ZEND_ARG_INFO(0, addr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_addrinfo_explain, 0, 0, 1)
+ ZEND_ARG_INFO(0, addr)
+ZEND_END_ARG_INFO()
+
/* }}} */
static PHP_GINIT_FUNCTION(sockets);
@@ -310,6 +332,10 @@ PHP_FUNCTION(socket_last_error);
PHP_FUNCTION(socket_clear_error);
PHP_FUNCTION(socket_import_stream);
PHP_FUNCTION(socket_export_stream);
+PHP_FUNCTION(socket_addrinfo_lookup);
+PHP_FUNCTION(socket_addrinfo_connect);
+PHP_FUNCTION(socket_addrinfo_bind);
+PHP_FUNCTION(socket_addrinfo_explain);
/* {{{ sockets_functions[]
*/
@@ -348,6 +374,10 @@ const zend_function_entry sockets_functions[] = {
PHP_FE(socket_sendmsg, arginfo_socket_sendmsg)
PHP_FE(socket_recvmsg, arginfo_socket_recvmsg)
PHP_FE(socket_cmsg_space, arginfo_socket_cmsg_space)
+ PHP_FE(socket_addrinfo_lookup, arginfo_socket_addrinfo_lookup)
+ PHP_FE(socket_addrinfo_connect, arginfo_socket_addrinfo_connect)
+ PHP_FE(socket_addrinfo_bind, arginfo_socket_addrinfo_bind)
+ PHP_FE(socket_addrinfo_explain, arginfo_socket_addrinfo_explain)
/* for downwards compatibility */
PHP_FALIAS(socket_getopt, socket_get_option, arginfo_socket_get_option)
@@ -422,6 +452,17 @@ PHP_SOCKETS_API void php_destroy_socket(zend_resource *rsrc) /* {{{ */
}
/* }}} */
+PHP_SOCKETS_API void php_destroy_addrinfo(zend_resource *rsrc) /* {{{ */
+{
+ struct addrinfo *addr = rsrc->ptr;
+ efree(addr->ai_addr);
+ if (addr->ai_canonname != NULL) {
+ efree(addr->ai_canonname);
+ }
+ efree(addr);
+}
+/* }}} */
+
static int php_open_listen_sock(php_socket **php_sock, int port, int backlog) /* {{{ */
{
struct sockaddr_in la;
@@ -625,6 +666,7 @@ static PHP_MINIT_FUNCTION(sockets)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
le_socket = zend_register_list_destructors_ex(php_destroy_socket, NULL, le_socket_name, module_number);
+ le_addrinfo = zend_register_list_destructors_ex(php_destroy_addrinfo, NULL, le_addrinfo_name, module_number);
REGISTER_LONG_CONSTANT("AF_UNIX", AF_UNIX, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("AF_INET", AF_INET, CONST_CS | CONST_PERSISTENT);
@@ -744,6 +786,20 @@ static PHP_MINIT_FUNCTION(sockets)
REGISTER_LONG_CONSTANT("IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS, CONST_CS | CONST_PERSISTENT);
#endif
+ REGISTER_LONG_CONSTANT("AI_PASSIVE", AI_PASSIVE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_CANONNAME", AI_CANONNAME, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_NUMERICHOST", AI_NUMERICHOST, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_V4MAPPED", AI_V4MAPPED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_ALL", AI_ALL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_ADDRCONFIG", AI_ADDRCONFIG, CONST_CS | CONST_PERSISTENT);
+#ifdef __USE_GNU
+ REGISTER_LONG_CONSTANT("AI_IDN", AI_IDN, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_CANONIDN", AI_CANONIDN, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_IDN_ALLOW_UNASSIGNED", AI_IDN_ALLOW_UNASSIGNED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AI_IDN_USE_STD3_ASCII_RULES", AI_IDN_USE_STD3_ASCII_RULES, CONST_CS | CONST_PERSISTENT);
+#endif
+ REGISTER_LONG_CONSTANT("AI_NUMERICSERV", AI_NUMERICSERV, CONST_CS | CONST_PERSISTENT);
+
php_socket_sendrecvmsg_init(INIT_FUNC_ARGS_PASSTHRU);
return SUCCESS;
@@ -2456,6 +2512,246 @@ PHP_FUNCTION(socket_export_stream)
}
/* }}} */
+/* {{{ proto resource addrinfo socket_addrinfo_lookup(string hostname[, mixed service, array hints])
+ Gets array with contents of getaddrinfo about the given hostname. */
+PHP_FUNCTION(socket_addrinfo_lookup)
+{
+ zend_string *hostname, *key;
+ zval *hint, *service, *zhints = NULL;
+
+ struct addrinfo hints, *result, *rp, *res;
+
+ memset(&hints, 0, sizeof(hints));
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S|za", &hostname, &service, &zhints) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if (zhints) {
+ ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zhints), key, hint) {
+ if (key) {
+ if (strcmp(ZSTR_VAL(key), "ai_flags") == 0) {
+ hints.ai_flags = Z_LVAL_P(hint);
+ } else if (strcmp(ZSTR_VAL(key), "ai_socktype") == 0) {
+ hints.ai_socktype= Z_LVAL_P(hint);
+ } else if (strcmp(ZSTR_VAL(key), "ai_protocol") == 0) {
+ hints.ai_protocol = Z_LVAL_P(hint);
+ } else if (strcmp(ZSTR_VAL(key), "ai_family") == 0) {
+ hints.ai_family = Z_LVAL_P(hint);
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ convert_to_string(service);
+
+ if (getaddrinfo(ZSTR_VAL(hostname), Z_STRVAL_P(service), &hints, &result) != 0) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ if (rp->ai_family != AF_UNSPEC) {
+ res = emalloc(sizeof(struct addrinfo));
+ memcpy(res, rp, sizeof(struct addrinfo));
+
+ res->ai_addr = emalloc(sizeof(struct sockaddr));
+ memcpy(res->ai_addr, rp->ai_addr, sizeof(struct sockaddr));
+
+ if (rp->ai_canonname != NULL) {
+ res->ai_canonname = estrdup(rp->ai_canonname);
+ }
+
+ add_next_index_resource(return_value, zend_register_resource(res, le_addrinfo));
+ }
+ }
+
+ freeaddrinfo(result);
+}
+/* }}} */
+
+/* {{{ proto resource socket_addrinfo_bind(resource addrinfo)
+ Creates and binds to a socket from a given addrinfo resource */
+PHP_FUNCTION(socket_addrinfo_bind)
+{
+ zval *arg1;
+ int retval;
+ struct addrinfo *ai;
+ php_socket *php_sock = php_create_socket();
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg1) == FAILURE) {
+ efree(php_sock);
+ return;
+ }
+
+ if ((ai = (struct addrinfo *) zend_fetch_resource(Z_RES_P(arg1), le_addrinfo_name, le_addrinfo)) == NULL) {
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ php_sock->bsd_socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ php_sock->type = ai->ai_family;
+
+ if (IS_INVALID_SOCKET(php_sock)) {
+ SOCKETS_G(last_error) = errno;
+ php_error_docref(NULL, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno));
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ php_sock->error = 0;
+ php_sock->blocking = 1;
+
+ switch(php_sock->type) {
+ case AF_UNIX:
+ {
+ // AF_UNIX sockets via getaddrino are not implemented due to security problems
+ break;
+ }
+
+ case AF_INET:
+#if HAVE_IPV6
+ case AF_INET6:
+#endif
+ {
+ retval = bind(php_sock->bsd_socket, ai->ai_addr, ai->ai_addrlen);
+ break;
+ }
+ default:
+ php_error_docref(NULL, E_WARNING, "unsupported socket type '%d', must be AF_UNIX, AF_INET, or AF_INET6", php_sock->type);
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ if (retval != 0) {
+ PHP_SOCKET_ERROR(php_sock, "unable to bind address", errno);
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ RETURN_RES(zend_register_resource(php_sock, le_socket));
+}
+/* }}} */
+
+/* {{{ proto resource socket_addrinfo_connect(resource addrinfo)
+ Creates and connects to a socket from a given addrinfo resource */
+PHP_FUNCTION(socket_addrinfo_connect)
+{
+ zval *arg1;
+ int retval;
+ struct addrinfo *ai;
+ php_socket *php_sock = php_create_socket();
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg1) == FAILURE) {
+ efree(php_sock);
+ return;
+ }
+
+ if ((ai = (struct addrinfo *) zend_fetch_resource(Z_RES_P(arg1), le_addrinfo_name, le_addrinfo)) == NULL) {
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ php_sock->bsd_socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ php_sock->type = ai->ai_family;
+
+ if (IS_INVALID_SOCKET(php_sock)) {
+ SOCKETS_G(last_error) = errno;
+ php_error_docref(NULL, E_WARNING, "Unable to create socket [%d]: %s", errno, sockets_strerror(errno));
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ php_sock->error = 0;
+ php_sock->blocking = 1;
+
+ switch(php_sock->type) {
+ case AF_UNIX:
+ {
+ // AF_UNIX sockets via getaddrino are not implemented due to security problems
+ break;
+ }
+
+ case AF_INET:
+#if HAVE_IPV6
+ case AF_INET6:
+#endif
+ {
+ retval = connect(php_sock->bsd_socket, ai->ai_addr, ai->ai_addrlen);
+ break;
+ }
+ default:
+ php_error_docref(NULL, E_WARNING, "unsupported socket type '%d', must be AF_UNIX, AF_INET, or AF_INET6", php_sock->type);
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ if (retval != 0) {
+ PHP_SOCKET_ERROR(php_sock, "unable to connect address", errno);
+ efree(php_sock);
+ RETURN_FALSE;
+ }
+
+ RETURN_RES(zend_register_resource(php_sock, le_socket));
+}
+/* }}} */
+
+/* {{{ proto resource socket_addrinfo_explain(resource addrinfo)
+ Creates and connects to a socket from a given addrinfo resource */
+PHP_FUNCTION(socket_addrinfo_explain)
+{
+ zval *arg1, sockaddr;
+ struct addrinfo *ai;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg1) == FAILURE) {
+ return;
+ }
+
+ if ((ai = (struct addrinfo *) zend_fetch_resource(Z_RES_P(arg1), le_addrinfo_name, le_addrinfo)) == NULL) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+
+ add_assoc_long(return_value, "ai_flags", ai->ai_flags);
+ add_assoc_long(return_value, "ai_family", ai->ai_family);
+ add_assoc_long(return_value, "ai_socktype", ai->ai_socktype);
+ add_assoc_long(return_value, "ai_protocol", ai->ai_protocol);
+ if (ai->ai_canonname != NULL) {
+ add_assoc_string(return_value, "ai_canonname", ai->ai_canonname);
+ }
+
+ array_init(&sockaddr);
+ switch(ai->ai_addr->sa_family) {
+ case AF_INET:
+ {
+ struct sockaddr_in *sa = (struct sockaddr_in *) ai->ai_addr;
+ char addr[INET_ADDRSTRLEN];
+
+ add_assoc_long(&sockaddr, "sin_port", ntohs((unsigned short) sa->sin_port));
+ inet_ntop(ai->ai_family, &sa->sin_addr, addr, sizeof(addr));
+ add_assoc_string(&sockaddr, "sin_addr", addr);
+ break;
+ }
+#if HAVE_IPV6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *) ai->ai_addr;
+ char addr[INET6_ADDRSTRLEN];
+
+ add_assoc_long(&sockaddr, "sin6_port", ntohs((unsigned short) sa->sin6_port));
+ inet_ntop(ai->ai_family, &sa->sin6_addr, addr, sizeof(addr));
+ add_assoc_string(&sockaddr, "sin6_addr", addr);
+ break;
+ }
+#endif
+ }
+
+ add_assoc_zval(return_value, "ai_addr", &sockaddr);
+}
+/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/sockets/tests/socket_addrinfo_bind.phpt b/ext/sockets/tests/socket_addrinfo_bind.phpt
new file mode 100644
index 0000000000..eb15afee25
--- /dev/null
+++ b/ext/sockets/tests/socket_addrinfo_bind.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test socket_addrinfo_bind()
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('SKIP The sockets extension is not loaded.');
+}
+--FILE--
+<?php
+$addrinfo = socket_addrinfo_lookup('127.0.0.1', 2000, array(
+ 'ai_family' => AF_INET,
+ 'ai_socktype' => SOCK_DGRAM,
+));
+var_dump(socket_addrinfo_bind($addrinfo[0]));
+echo "Done";
+--EXPECTF--
+resource(%d) of type (Socket)
+Done
diff --git a/ext/sockets/tests/socket_addrinfo_connect.phpt b/ext/sockets/tests/socket_addrinfo_connect.phpt
new file mode 100644
index 0000000000..009d54839b
--- /dev/null
+++ b/ext/sockets/tests/socket_addrinfo_connect.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test socket_addrinfo_connect()
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('SKIP The sockets extension is not loaded.');
+}
+--FILE--
+<?php
+$addrinfo = socket_addrinfo_lookup('127.0.0.1', 2000, array(
+ 'ai_family' => AF_INET,
+ 'ai_socktype' => SOCK_DGRAM,
+));
+var_dump(socket_addrinfo_connect($addrinfo[0]));
+echo "Done";
+--EXPECTF--
+resource(%d) of type (Socket)
+Done
diff --git a/ext/sockets/tests/socket_addrinfo_explain.phpt b/ext/sockets/tests/socket_addrinfo_explain.phpt
new file mode 100644
index 0000000000..aa891039dd
--- /dev/null
+++ b/ext/sockets/tests/socket_addrinfo_explain.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Test socket_addrinfo_explain()
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('SKIP The sockets extension is not loaded.');
+}
+--FILE--
+<?php
+$addrinfo = socket_addrinfo_lookup('127.0.0.1', 2000, array(
+ 'ai_family' => AF_INET,
+ 'ai_socktype' => SOCK_DGRAM,
+));
+var_dump(socket_addrinfo_explain($addrinfo[0]));
+echo "Done";
+--EXPECT--
+array(5) {
+ ["ai_flags"]=>
+ int(0)
+ ["ai_family"]=>
+ int(2)
+ ["ai_socktype"]=>
+ int(2)
+ ["ai_protocol"]=>
+ int(17)
+ ["ai_addr"]=>
+ array(2) {
+ ["sin_port"]=>
+ int(2000)
+ ["sin_addr"]=>
+ string(9) "127.0.0.1"
+ }
+}
+Done
diff --git a/ext/sockets/tests/socket_addrinfo_lookup.phpt b/ext/sockets/tests/socket_addrinfo_lookup.phpt
new file mode 100644
index 0000000000..be35792f45
--- /dev/null
+++ b/ext/sockets/tests/socket_addrinfo_lookup.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test socket_addrinfo_lookup()
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('SKIP The sockets extension is not loaded.');
+}
+--FILE--
+<?php
+$addrinfo = socket_addrinfo_lookup('127.0.0.1', 2000, array(
+ 'ai_family' => AF_INET,
+ 'ai_socktype' => SOCK_DGRAM,
+));
+var_dump($addrinfo[0]);
+echo "Done";
+--EXPECTF--
+resource(%d) of type (AddressInfo)
+Done