Ruslan Osmanov avatar Ruslan Osmanov committed 44a561e

Change: allow to pass NULL in place of socket argument to EventBufferEvent::sslSocket (for further connection via, say, connectHost method)
Fix: memory leak on the ssl context object free-storage handler
Add: OpenSSL client sample

Comments (0)

Files changed (6)

classes/buffer_event.c

 }
 /* }}} */
 
-/* {{{ proto bool EventBufferEvent::connectHost(EventDnsBase dns_base, string hostname, int port[, int family = EVENT_AF_UNSPEC]);
+/* {{{ proto bool EventBufferEvent::connectHost(EventDnsBase dns_base, string hostname, int port[, int family = EventUtil::AF_UNSPEC]);
  *
  * Resolves the DNS name hostname, looking for addresses of type
  * family(EVENT_AF_* constants). If the name resolution fails, it invokes the
 	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_bevent_ce);
 	PHP_EVENT_FETCH_BEVENT(bev, return_value);
 
-	fd = php_event_zval_to_fd(ppzfd TSRMLS_CC);
-	if (fd < 0) {
-		RETURN_FALSE;
+	if (Z_TYPE_PP(ppzfd) == IS_NULL) {
+		/* User decided to set fd later via connect or connectHost etc.*/
+		fd = -1;
+	} else {
+		fd = php_event_zval_to_fd(ppzfd TSRMLS_CC);
+		if (fd < 0) {
+			RETURN_FALSE;
+		}
+		/* Make sure that the socket is in non-blocking mode(libevent's tip) */
+		/*evutil_make_socket_nonblocking(fd);*/
 	}
-	/* Make sure that the socket is in non-blocking mode(libevent's tip) */
-	/*evutil_make_socket_nonblocking(fd);*/
 
 	PHP_EVENT_ASSERT(ectx->ctx);
 	ssl = SSL_new(ectx->ctx);

classes/ssl_context.c

 	ectx->ctx = ctx;
 
 	ALLOC_HASHTABLE(ectx->ht);
-	zend_hash_init_ex(ectx->ht, zend_hash_num_elements(ht_options), NULL, NULL, 0, 1);
+	if (zend_hash_init_ex(ectx->ht, zend_hash_num_elements(ht_options), NULL, ZVAL_PTR_DTOR, 0, 0) == FAILURE) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Failed to allocate hashtable for options");
+		FREE_HASHTABLE(ectx->ht);
+		return;
+	}
 	zend_hash_copy(ectx->ht, ht_options, (copy_ctor_func_t) zval_add_ref,
 			(void *) NULL, sizeof(zval *));
 

examples/ssl-echo-server/client.php

+<?php
+/*
+ * Sample OpenSSL client.
+ *
+ * Usage:
+ * 1) Launch a server, e.g.:
+ * $ php examples/ssl-echo-server/server.php 9800
+ *
+ * 2) Launch the client in another terminal:
+ * $ php examples/ssl-echo-server/client.php 9988
+ *
+ * Both client and server should output something similar to the following:
+ * Received 5 bytes
+ * ----- data ----
+ * 5:	test
+ */
+
+// Allow to override the port
+$port = 9999;
+if ($argc > 1) {
+	$port = (int) $argv[1];
+}
+if ($port <= 0 || $port > 65535) {
+	exit("Invalid port\n");
+}
+
+class MySslEchoServerClient {
+	public $port,
+		$base,
+		$bev,
+		$ctx;
+
+	function __construct ($port, $host = "127.0.0.1") {
+		$this->port = $port;
+		$this->ctx = $this->init_ssl();
+		if (!$this->ctx) {
+			trigger_error("Failed creating SSL context", E_USER_ERROR);
+		}
+
+		$this->base = new EventBase();
+		if (!$this->base) {
+			trigger_error("Failed to initialize event base", E_USER_ERROR);
+		}
+
+		$this->bev = EventBufferEvent::sslSocket($this->base, NULL, $this->ctx,
+			EventBufferEvent::SSL_CONNECTING, EventBufferEvent::OPT_CLOSE_ON_FREE);
+		if (!$this->bev) {
+			trigger_error("Failedi to initialize buffer event", E_USER_ERROR);
+		}
+		$this->bev->setCallbacks(array($this, "ssl_read_cb"), NULL, array($this, "ssl_event_cb"));
+		if (!$this->bev->connectHost(NULL, $host, $port, EventUtil::AF_INET)) {
+			trigger_error("connectHost failed", E_USER_ERROR);
+		}
+		$this->bev->enable(Event::READ);
+	}
+
+	function __destruct() {
+		if ($this->bev) {
+			$this->bev->free();
+		}
+	}
+
+	function dispatch() {
+		$this->base->dispatch();
+	}
+
+	function init_ssl() {
+		$local_cert = __DIR__."/cert.pem";
+		$local_pk   = __DIR__."/privkey.pem";
+
+		$ctx = new EventSslContext(EventSslContext::SSLv3_CLIENT_METHOD, array (
+ 			EventSslContext::OPT_LOCAL_CERT  => $local_cert,
+ 			EventSslContext::OPT_LOCAL_PK    => $local_pk,
+ 			//EventSslContext::OPT_PASSPHRASE  => "echo server",
+ 			EventSslContext::OPT_ALLOW_SELF_SIGNED => true,
+		));
+
+		return $ctx;
+	}
+
+	// This callback is invoked when there is data to read on $bev.
+	function ssl_read_cb($bev, $ctx) {
+		$in = $bev->input; //$bev->getInput();
+
+		printf("Received %ld bytes\n", $in->length);
+    	printf("----- data ----\n");
+    	printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));
+
+		$bev->writeBuffer($in);
+
+		$this->bev->free();
+		$this->bev = NULL;
+		$this->base->exit();
+	}
+
+	// This callback is invoked when some even occurs on the event listener,
+	// e.g. connection closed, or an error occured
+	function ssl_event_cb($bev, $events, $ctx) {
+		if ($events & EventBufferEvent::ERROR) {
+			// Fetch errors from the SSL error stack
+			while ($err = $bev->sslError()) {
+				fprintf(STDERR, "Bufferevent error %s.\n", $err);
+			}
+		}
+
+		if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
+			$bev->free();
+			$bev = NULL;
+		} elseif ($events & EventBufferEvent::CONNECTED) {
+			$bev->output->add("test\n");
+		}
+	}
+}
+
+$cl = new MySslEchoServerClient($port);
+$cl->dispatch();
+?>

examples/ssl-echo-server/server.php

 	function ssl_read_cb($bev, $ctx) {
 		$in = $bev->input; //$bev->getInput();
 
-		printf("Received %zu bytes\n", $in->length);
+		printf("Received %ld bytes\n", $in->length);
     	printf("----- data ----\n");
     	printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));
 
  			EventSslContext::OPT_LOCAL_PK    => $local_pk,
  			//EventSslContext::OPT_PASSPHRASE  => "echo server",
  			EventSslContext::OPT_VERIFY_PEER => true,
- 			EventSslContext::OPT_ALLOW_SELF_SIGNED => false,
+ 			EventSslContext::OPT_ALLOW_SELF_SIGNED => true,
 		));
 
 		return $ctx;
   Add: EventBuffer::searchEol method
   Add: EventUtil::getSocketName method
   Add: EventListener::getSocketName method
+  Fix: memory leak due to lack of zend_hash_destroy on the ssl context options
   ]]></notes>
   <!--}}}-->
   <!--{{{ Contents -->
         <file role="doc" name="buffer_proxy.php"/>
         <dir name="ssl-echo-server">
           <file role="doc" name="server.php"/>
+          <file role="doc" name="client.php"/>
         </dir>
         <file role="doc" name="eio.php"/>
         <file role="doc" name="fibonacci_buffer.php"/>
         Add: EventBuffer::searchEol method
         Add: EventUtil::getSocketName method
         Add: EventListener::getSocketName method
+  Fix: memory leak due to lack of zend_hash_destroy on the ssl context options
         ]]></notes>
     </release>
     <!--}}}-->
 	}
 
 	if (ectx->ht) {
-		/*zend_hash_destroy(ectx->ht);*/
+		zend_hash_destroy(ectx->ht);
 		FREE_HASHTABLE(ectx->ht);
 		ectx->ht = NULL;
 	}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.