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)
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);
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[]
*/
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)
}
/* }}} */
+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;
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);
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;
}
/* }}} */
+/* {{{ 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