Commits

Ruslan Osmanov committed 26cd25e

Fix: memory leak when EventListener::__construct failed
Del: EventBufferEvent::connectUnix, it's function moved to the connect method
Change: family arg removed from EventBufferEvent::connect, EventListener::__construct

Comments (0)

Files changed (9)

classes/buffer_event.c

 
 /* {{{ proto bool EventBufferEvent::connect(string addr);
  *
- * Connect buffer event's socket to given address(optionally with port).  The
- * function available since libevent 2.0.2-alpha.
+ * Connect buffer event's socket to given address(optionally with port).
  *
- * This function doesn't require sockets support. If socket is not assigned to
- * the bufferevent, this function allocates a socket stream and makes it
- * non-blocking internally.
- *
- * addr parameter expected
- * to be an IP address with optional port number. Recognized formats are:
+ * addr parameter expected to be whether an IP address with optional port number,
+ * or a path to UNIX domain socket.
+ * Recognized formats are:
  *
  *    [IPv6Address]:port
  *    [IPv6Address]
  *    IPv6Address
  *    IPv4Address:port
  *    IPv4Address
- * 
- * To resolve DNS names asyncronously, use
- * bufferevent_socket_connect_hostname() function.
+ *    unix:path-to-socket-file
  */
 PHP_METHOD(EventBufferEvent, connect)
 {
-	php_event_bevent_t *bev;
-	zval               *zbevent      = getThis();
-	char               *addr;
-	int                 addr_len;
-	struct sockaddr     sa;
-	socklen_t           sa_len;
+	php_event_bevent_t      *bev;
+	zval                    *zbevent  = getThis();
+	char                    *addr;
+	int                      addr_len;
+	struct sockaddr_storage  ss;
+	int                      ss_len   = sizeof(ss);
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
 				&addr, &addr_len) == FAILURE) {
 	PHP_EVENT_FETCH_BEVENT(bev, zbevent);
 	_ret_if_invalid_bevent_ptr(bev);
 
-	/* Numeric addresses only. Don't try to resolve hostname. */
-	if (evutil_parse_sockaddr_port(addr, &sa, (int *) &sa_len)) {
+	memset(&ss, 0, sizeof(ss));
+
+	if (strncasecmp(addr, PHP_EVENT_SUN_PREFIX,
+				sizeof(PHP_EVENT_SUN_PREFIX) - 1) == 0) {
+		/* UNIX domain socket path */
+
+		struct sockaddr_un *sun;
+
+		sun             = (struct sockaddr_un *) &ss;
+		sun->sun_family = AF_UNIX;
+		ss_len          = sizeof(struct sockaddr_un);
+
+		strcpy(sun->sun_path, addr + sizeof(PHP_EVENT_SUN_PREFIX) - 1);
+
+	} else if (evutil_parse_sockaddr_port(addr, (struct sockaddr *) &ss, &ss_len)) {
+		/* Numeric addresses only. Don't try to resolve hostname. */
+
 		php_error_docref(NULL TSRMLS_CC, E_WARNING,
 				"Failed parsing address: the address is not well-formed, "
 				"or the port is out of range");
 	/* bufferevent_socket_connect() allocates a socket stream internally, if we
 	 * didn't provide the file descriptor to the bufferevent before, e.g. with
 	 * bufferevent_socket_new() */
-	if (bufferevent_socket_connect(bev->bevent, &sa, sa_len)) {
-		RETURN_FALSE;
-	}
-
-	RETVAL_TRUE;
-}
-/* }}} */
-
-/* {{{ proto bool EventBufferEvent::connectUnix(string addr);
- *
- * Connect buffer event's file descriptor to given UNIX domain socket.
- */
-PHP_METHOD(EventBufferEvent, connectUnix)
-{
-	php_event_bevent_t *bev;
-	zval               *zbevent      = getThis();
-	char               *addr;
-	int                 addr_len;
-	struct sockaddr_un  sun;
-
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
-				&addr, &addr_len) == FAILURE) {
-		return;
-	}
-
-	PHP_EVENT_FETCH_BEVENT(bev, zbevent);
-	_ret_if_invalid_bevent_ptr(bev);
-
-	sun.sun_family = AF_UNIX;
-	strcpy(sun.sun_path, addr);
-
-	if (bufferevent_socket_connect(bev->bevent, (struct sockaddr *) &sun, sizeof(struct sockaddr_un))) {
+	if (bufferevent_socket_connect(bev->bevent, (struct sockaddr *) &ss, ss_len)) {
 		RETURN_FALSE;
 	}
 
  * event_dns_base_new()(requires --with-event-extra configure option).
  * For asyncronous hostname resolving pass a valid event dns base resource.
  * Otherwise the hostname resolving will block.
+ *
  * Recognized hostname formats are:
- * www.example.com (hostname) 1.2.3.4 (ipv4address) ::1 (ipv6address) [::1] ([ipv6address])
+ * www.example.com (hostname)
+ * 1.2.3.4 (ipv4address)
+ * ::1 (ipv6address)
+ * [::1] ([ipv6address])
  */
 PHP_METHOD(EventBufferEvent, connectHost)
 {

classes/listener.c

 
 /* Private }}} */
 
-/* {{{ proto EventListener EventListener::__construct(EventBase base, callable cb, mixed data, int flags, int backlog, mixed target[, int family = EventUtil::AF_UNSPEC]);
+/* {{{ proto EventListener EventListener::__construct(EventBase base, callable cb, mixed data, int flags, int backlog, mixed target);
  *
  * Creates new connection listener associated with an event base.
  *
  * target parameter may be string, socket resource, or a stream associated with a socket.
  * In case if target is a string, the string will be parsed as network address.
+ * A path to UNIX domain socket should be prefixed with 'unix:', e.g.
+ * unix:/tmp/my.sock.
  *
  * Returns object representing the event connection listener.
  */
 	zval                  **ppztarget;
 	long                    flags;
 	long                    backlog;
-	long                    family    = AF_UNSPEC;
 	struct evconnlistener  *listener;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ofz!llZ|l",
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ofz!llZ",
 				&zbase, php_event_base_ce,
-				&fci, &fcc, &zdata, &flags, &backlog, &ppztarget, &family) == FAILURE) {
-		return;
-	}
-
-	if (family & ~(AF_UNSPEC | AF_INET | AF_INET6 | AF_UNIX)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Unsupported address family: %ld", family);
-		ZVAL_NULL(zself);
+				&fci, &fcc, &zdata, &flags, &backlog, &ppztarget) == FAILURE) {
 		return;
 	}
 
 	PHP_EVENT_FETCH_BASE(base, zbase);
 
 	if (Z_TYPE_PP(ppztarget) == IS_STRING) {
-		struct sockaddr sa;
-		struct sockaddr_un *sun;
-		socklen_t       sa_len;
+		struct sockaddr_storage *ss;
+		socklen_t ss_len = sizeof(ss);
+		memset(&ss, 0, sizeof(ss));
+
+		if (strncasecmp(Z_STRVAL_PP(ppztarget), PHP_EVENT_SUN_PREFIX,
+					sizeof(PHP_EVENT_SUN_PREFIX) - 1) == 0) {
+			struct sockaddr_un *sun;
 
-		if (family == AF_UNIX) {
-			sun             = (struct sockaddr_un *) &sa;
+			sun             = (struct sockaddr_un *) &ss;
 			sun->sun_family = AF_UNIX;
-			strcpy(sun->sun_path, Z_STRVAL_PP(ppztarget));
 
-			/*sa_len = sizeof(sun->sun_family) + Z_STRLEN_PP(ppztarget);*/
-			sa_len = sizeof(struct sockaddr_un);
-		} else if (php_network_parse_network_address_with_port(Z_STRVAL_PP(ppztarget), Z_STRLEN_PP(ppztarget),
-					&sa, &sa_len TSRMLS_CC) != SUCCESS) {
+			strcpy(sun->sun_path, Z_STRVAL_PP(ppztarget) + sizeof(PHP_EVENT_SUN_PREFIX) - 1);
+			ss_len = sizeof(struct sockaddr_un);
+		} else if (php_network_parse_network_address_with_port(Z_STRVAL_PP(ppztarget),
+					Z_STRLEN_PP(ppztarget), (struct sockaddr *) &ss, &ss_len TSRMLS_CC) != SUCCESS) {
 			ZVAL_NULL(zself);
 			return;
 		}
 		PHP_EVENT_FETCH_LISTENER(l, zself);
 
 		listener = evconnlistener_new_bind(base->base, _php_event_listener_cb,
-				(void *) l, flags, backlog, &sa, sa_len);
+				(void *) l, flags, backlog, (struct sockaddr *) &ss, ss_len);
 	} else { /* ppztarget is not string */
-		evutil_socket_t   fd    = -1;
+		evutil_socket_t fd = -1;
 
 		/* php_event_zval_to_fd reports error
 	 	 * in case if it is not a valid socket resource */

examples/unix-domain-listener-client.php

 		$this->bev = new EventBufferEvent($base, NULL, EventBufferEvent::OPT_CLOSE_ON_FREE,
 			array ($this, "read_cb"), NULL, array ($this, "event_cb"));
 
-		if (!$this->bev->connectUnix($sock_path)) {
+		if (!$this->bev->connect("unix:$sock_path")) {
 			trigger_error("Failed to connect to socket `$sock_path'", E_USER_ERROR);
 		}
 

examples/unix-domain-listener.php

 
  		$this->listener = new EventListener($this->base,
  			array($this, "acceptConnCallback"), $this->base,
- 			EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,-1,
- 			$sock_path, EventUtil::AF_UNIX);
+ 			EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1,
+ 			"unix:$sock_path");
 
 		if (!$this->listener) {
 			trigger_error("Couldn't create listener", E_USER_ERROR);
 
 	if (l->data) {
 		zval_ptr_dtor(&l->data);
+		l->data = NULL;
 	}
 
-#if 0
 	if (l->self) {
 		zval_ptr_dtor(&l->self);
 		l->self = NULL;
 	}
-#endif
 
 	PHP_EVENT_FREE_FCALL_INFO(l->fci, l->fcc);
 	PHP_EVENT_FREE_FCALL_INFO(l->fci_err, l->fcc_err);
 
 	if (l->listener) {
 		evconnlistener_free(l->listener);
+		l->listener = NULL;
 	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 
 #define PHP_EVENT_VERSION "1.5.0"
 
+#define PHP_EVENT_SUN_PREFIX "unix:"
 
 extern zend_module_entry event_module_entry;
 #define phpext_event_ptr &event_module_entry
 	ZEND_ARG_INFO(0, addr)
 ZEND_END_ARG_INFO();
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_bufferevent_socket_connect_hostname, 0, 0, 3)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_bufferevent_connecthost, 0, 0, 3)
 	ZEND_ARG_INFO(0, dns_base)
 	ZEND_ARG_INFO(0, hostname)
 	ZEND_ARG_INFO(0, port)
 	ZEND_ARG_INFO(0, flags)
 	ZEND_ARG_INFO(0, backlog)
 	ZEND_ARG_INFO(0, target)
-	ZEND_ARG_INFO(0, family)
 ZEND_END_ARG_INFO();
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_evconnlistener_new_bind, 0, 0, 5)
 /* }}} */
 
 const zend_function_entry php_event_bevent_ce_functions[] = {/* {{{ */
-	PHP_ME(EventBufferEvent, __construct,        arginfo_bufferevent__construct,              ZEND_ACC_PUBLIC  | ZEND_ACC_CTOR)
-	PHP_ME(EventBufferEvent, free,               arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, connect,            arginfo_bufferevent_connect,                 ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, connectUnix,        arginfo_bufferevent_connect_unix,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, connectHost,        arginfo_bufferevent_socket_connect_hostname, ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getDnsErrorString,  arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setCallbacks,       arginfo_bufferevent_set_callbacks,           ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, enable,             arginfo_bufferevent__events,                 ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, disable,            arginfo_bufferevent__events,                 ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getEnabled,         arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getInput,           arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getOutput,          arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setWatermark,       arginfo_bufferevent_setwatermark,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, write,              arginfo_bufferevent_write,                   ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, writeBuffer,        arginfo_bufferevent_write_buffer,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, read,               arginfo_bufferevent_read,                    ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, readBuffer,         arginfo_bufferevent_write_buffer,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, createPair,         arginfo_bufferevent_pair_new,                ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setPriority,        arginfo_bufferevent_priority_set,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setTimeouts,        arginfo_bufferevent_set_timeouts,            ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, __construct,       arginfo_bufferevent__construct,    ZEND_ACC_PUBLIC  | ZEND_ACC_CTOR)
+	PHP_ME(EventBufferEvent, free,              arginfo_event__void,               ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, connect,           arginfo_bufferevent_connect,       ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, connectHost,       arginfo_bufferevent_connecthost,   ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getDnsErrorString, arginfo_event__void,               ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setCallbacks,      arginfo_bufferevent_set_callbacks, ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, enable,            arginfo_bufferevent__events,       ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, disable,           arginfo_bufferevent__events,       ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getEnabled,        arginfo_event__void,               ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getInput,          arginfo_event__void,               ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getOutput,         arginfo_event__void,               ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setWatermark,      arginfo_bufferevent_setwatermark,  ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, write,             arginfo_bufferevent_write,         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, writeBuffer,       arginfo_bufferevent_write_buffer,  ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, read,              arginfo_bufferevent_read,          ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, readBuffer,        arginfo_bufferevent_write_buffer,  ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, createPair,        arginfo_bufferevent_pair_new,      ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setPriority,       arginfo_bufferevent_priority_set,  ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setTimeouts,       arginfo_bufferevent_set_timeouts,  ZEND_ACC_PUBLIC)
 #ifdef HAVE_EVENT_OPENSSL_LIB
-	PHP_ME(EventBufferEvent, sslFilter,         arginfo_bufferevent_ssl_filter,              ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
-	PHP_ME(EventBufferEvent, sslSocket,         arginfo_bufferevent_ssl_socket,              ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
-	PHP_ME(EventBufferEvent, sslError,          arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, sslRenegotiate,    arginfo_event__void,                         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, sslFilter,      arginfo_bufferevent_ssl_filter, ZEND_ACC_PUBLIC  | ZEND_ACC_STATIC)
+	PHP_ME(EventBufferEvent, sslSocket,      arginfo_bufferevent_ssl_socket, ZEND_ACC_PUBLIC  | ZEND_ACC_STATIC)
+	PHP_ME(EventBufferEvent, sslError,       arginfo_event__void,            ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, sslRenegotiate, arginfo_event__void,            ZEND_ACC_PUBLIC)
 #endif
 
 	PHP_FE_END
 PHP_METHOD(EventBufferEvent, free);
 PHP_METHOD(EventBufferEvent, createPair);
 PHP_METHOD(EventBufferEvent, connect);
-PHP_METHOD(EventBufferEvent, connectUnix);
 PHP_METHOD(EventBufferEvent, connectHost);
 PHP_METHOD(EventBufferEvent, setCallbacks);
 PHP_METHOD(EventBufferEvent, enable);

tests/07-listener-error.phpt

 <?php
 $base = new EventBase();
 
-// Create listener based on UNIX domain socket. Pass wrong address family.
-// The contructor should return NULL
-$listener = new EventListener($base, function() {}, NULL, 0, -1,
-	"/tmp/".mt_rand().".sock", EventUtil::AF_UNSPEC);
+$sock_paths = array (
+	"unix:/tmp/".mt_rand().".sock" => TRUE,
+	"UNIX:/tmp/".mt_rand().".sock" => TRUE,
+	":/tmp/".mt_rand().".sock"     => FALSE,
+	"/tmp/".mt_rand().".sock"      => FALSE,
+);
+
+foreach ($sock_paths as $path => $expect) {
+	$listener = @new EventListener($base, function() {}, NULL, 0, -1, $path);
+	if (file_exists($path)) unlink($path);
+
+	var_dump(is_null($listener) != $expect);
+}
 
-var_dump($listener);
 ?>
 --EXPECT--
-NULL
+bool(true)
+bool(true)
+bool(true)
+bool(true)