/** * @param array $args * @param callable $transform * @return \Amp\Promise */ public function send(array $args, callable $transform = null) { $promisor = new \Amp\Deferred(); $this->promisors[] = $promisor; $this->getConnection()->send($args); return \Amp\pipe($promisor->promise(), function ($response) { return $this->parser->parse($response); }); }
private function establish() { \Amp\pipe(\Amp\file\get($this->path), 'Amp\\Socket\\connect')->when(function ($e, $sock) { if ($e) { $this->failAll(); return; } $this->sock = $sock; $this->writeWatcher = \Amp\onWritable($sock, $this->writer); }); }
private function establish() { $unix = in_array("unix", \stream_get_transports(), true); if ($unix) { $promise = \Amp\Socket\connect("unix://{$this->path}.sock"); } else { $promise = \Amp\pipe(\Amp\file\get($this->path), 'Amp\\Socket\\connect'); } $promise->when(function ($e, $sock) { if ($e) { $this->failAll(); return; } $this->sock = $sock; $this->writeWatcher = \Amp\onWritable($sock, $this->writer); }); }
private function getResourceUri($resource) { if (!is_string($resource)) { throw new InvalidArgumentException(sprintf("\$resource must be of type string, %s given.", gettype($resource))); } if (substr($resource, 0, 8) === "https://") { return new Success($resource); } if (!$this->dictionary) { return \Amp\pipe(\Amp\resolve($this->fetchDictionary()), function () use($resource) { return $this->getResourceUri($resource); }); } if (isset($this->dictionary[$resource])) { return new Success($this->dictionary[$resource]); } return new Failure(new AcmeException("Resource not found in directory: '{$resource}'.")); }
/** * Enable encryption on an existing socket stream * * @param resource $socket * @param array $options * @return \Amp\Promise */ function cryptoEnable($socket, array $options = []) { static $caBundleFiles = []; $isLegacy = PHP_VERSION_ID < 50600; if ($isLegacy) { // For pre-5.6 we always manually verify names in userland // using the captured peer certificate. $options["capture_peer_cert"] = true; $options["verify_peer"] = isset($options["verify_peer"]) ? $options["verify_peer"] : true; if (isset($options["CN_match"])) { $peerName = $options["CN_match"]; $options["peer_name"] = $peerName; unset($options["CN_match"]); } if (empty($options["cafile"])) { $options["cafile"] = __DIR__ . "/../var/ca-bundle.crt"; } } // Externalize any bundle inside a Phar, because OpenSSL doesn't support the stream wrapper. if (!empty($options["cafile"]) && strpos($options["cafile"], "phar://") === 0) { // Yes, this is blocking but way better than just an error. if (!isset($caBundleFiles[$options["cafile"]])) { $bundleContent = file_get_contents($options["cafile"]); $caBundleFile = tempnam(sys_get_temp_dir(), "openssl-ca-bundle-"); file_put_contents($caBundleFile, $bundleContent); register_shutdown_function(function () use($caBundleFile) { @unlink($caBundleFile); }); $caBundleFiles[$options["cafile"]] = $caBundleFile; } $options["cafile"] = $caBundleFiles[$options["cafile"]]; } if (empty($options["ciphers"])) { // See https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29 // DES ciphers have been explicitly removed from that list // TODO: We're using the recommended settings for servers here, we need a good resource for clients. // Then we might be able to use a more restrictive list. // The following cipher suites have been explicitly disabled, taken from previous configuration: // !aNULL:!eNULL:!EXPORT:!DES:!DSS:!3DES:!MD5:!PSK $options["ciphers"] = \implode(':', ["ECDHE-ECDSA-CHACHA20-POLY1305", "ECDHE-RSA-CHACHA20-POLY1305", "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES256-GCM-SHA384", "ECDHE-RSA-AES256-GCM-SHA384", "DHE-RSA-AES128-GCM-SHA256", "DHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES128-SHA256", "ECDHE-RSA-AES128-SHA256", "ECDHE-ECDSA-AES128-SHA", "ECDHE-RSA-AES256-SHA384", "ECDHE-RSA-AES128-SHA", "ECDHE-ECDSA-AES256-SHA384", "ECDHE-ECDSA-AES256-SHA", "ECDHE-RSA-AES256-SHA", "DHE-RSA-AES128-SHA256", "DHE-RSA-AES128-SHA", "DHE-RSA-AES256-SHA256", "DHE-RSA-AES256-SHA", "AES128-GCM-SHA256", "AES256-GCM-SHA384", "AES128-SHA256", "AES256-SHA256", "AES128-SHA", "AES256-SHA", "!aNULL", "!eNULL", "!EXPORT", "!DES", "!DSS", "!3DES", "!MD5", "!PSK"]); } $ctx = \stream_context_get_options($socket); if (!empty($ctx['ssl'])) { $ctx = $ctx['ssl']; $compare = $options; $no_SNI_nb_hack = empty($ctx['SNI_nb_hack']); unset($ctx['SNI_nb_hack'], $ctx['peer_certificate'], $ctx['SNI_server_name']); unset($compare['SNI_nb_hack'], $compare['peer_certificate'], $compare['SNI_server_name']); if ($ctx == $compare) { return new Success($socket); } elseif ($no_SNI_nb_hack) { return \Amp\pipe(cryptoDisable($socket), function ($socket) use($options) { return cryptoEnable($socket, $options); }); } } if (isset($options["crypto_method"])) { $method = $options["crypto_method"]; unset($options["crypto_method"]); } elseif (PHP_VERSION_ID >= 50600 && PHP_VERSION_ID <= 50606) { /** @link https://bugs.php.net/69195 */ $method = \STREAM_CRYPTO_METHOD_TLS_CLIENT; } else { // note that this constant actually means "Any TLS version EXCEPT SSL v2 and v3" $method = \STREAM_CRYPTO_METHOD_SSLv23_CLIENT; } $options["SNI_nb_hack"] = false; \stream_context_set_option($socket, ["ssl" => $options]); return $isLegacy ? \Amp\resolve(__watchCryptoLegacy($method, $socket)) : __watchCrypto($method, $socket); }
/** * @param array $strings * @return Promise */ public function send(array $strings) { return \Amp\pipe($this->connect(), function () use($strings) { $payload = $this->parsePayload($strings); $this->outputBuffer .= $payload; $this->outputBufferLength += strlen($payload); if ($this->reader !== null) { \Amp\enable($this->reader); } if ($this->writer !== null) { \Amp\enable($this->writer); } }); }
function __recurseWithHosts($name, array $types, $options) { // Check for hosts file matches if (!isset($options["hosts"]) || $options["hosts"]) { static $hosts = null; if ($hosts === null || !empty($options["reload_hosts"])) { return \Amp\pipe(\Amp\resolve(__loadHostsFile()), function ($value) use(&$hosts, $name, $types, $options) { unset($options["reload_hosts"]); // avoid recursion $hosts = $value; return __recurseWithHosts($name, $types, $options); }); } $result = []; if (in_array(Record::A, $types) && isset($hosts[Record::A][$name])) { $result[Record::A] = [[$hosts[Record::A][$name], Record::A, $ttl = null]]; } if (in_array(Record::AAAA, $types) && isset($hosts[Record::AAAA][$name])) { $result[Record::AAAA] = [[$hosts[Record::AAAA][$name], Record::AAAA, $ttl = null]]; } if ($result) { return new Success($result); } } return \Amp\resolve(__doRecurse($name, $types, $options)); }
private function doRequest($uri, $name, $type) { $server = $this->loadExistingServer($uri) ?: $this->loadNewServer($uri); $useTCP = substr($uri, 0, 6) == "tcp://"; if ($useTCP && isset($server->connect)) { return \Amp\pipe($server->connect, function () use($uri, $name, $type) { return $this->doRequest($uri, $name, $type); }); } // Get the next available request ID do { $requestId = $this->requestIdCounter++; if ($this->requestIdCounter >= MAX_REQUEST_ID) { $this->requestIdCounter = 1; } } while (isset($this->pendingRequests[$requestId])); // Create question record $question = $this->questionFactory->create($type); $question->setName($name); // Create request message $request = $this->messageFactory->create(MessageTypes::QUERY); $request->getQuestionRecords()->add($question); $request->isRecursionDesired(true); $request->setID($requestId); // Encode request message $requestPacket = $this->encoder->encode($request); if ($useTCP) { $requestPacket = pack("n", strlen($requestPacket)) . $requestPacket; } // Send request $bytesWritten = \fwrite($server->socket, $requestPacket); if ($bytesWritten === false || isset($packet[$bytesWritten])) { throw new ResolutionException("Request send failed"); } $promisor = new Deferred(); $server->pendingRequests[$requestId] = true; $this->pendingRequests[$requestId] = [$promisor, $name, $type, $uri]; return $promisor->promise(); }
/** * Enable encryption on an existing socket stream * * @param resource $socket * @param array $options * @return \Amp\Promise */ function cryptoEnable($socket, array $options = []) { $isLegacy = PHP_VERSION_ID < 50600; if ($isLegacy) { // For pre-5.6 we always manually verify names in userland // using the captured peer certificate. $options["capture_peer_cert"] = true; $options["verify_peer"] = isset($options["verify_peer"]) ? $options["verify_peer"] : true; if (isset($options["CN_match"])) { $peerName = $options["CN_match"]; $options["peer_name"] = $peerName; unset($options["CN_match"]); } if (empty($options["cafile"])) { $options["cafile"] = __DIR__ . "/../var/ca-bundle.crt"; } } if (empty($options["ciphers"])) { $options["ciphers"] = \implode(':', ["ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES256-GCM-SHA384", "DHE-RSA-AES128-GCM-SHA256", "DHE-DSS-AES128-GCM-SHA256", "kEDH+AESGCM", "ECDHE-RSA-AES128-SHA256", "ECDHE-ECDSA-AES128-SHA256", "ECDHE-RSA-AES128-SHA", "ECDHE-ECDSA-AES128-SHA", "ECDHE-RSA-AES256-SHA384", "ECDHE-ECDSA-AES256-SHA384", "ECDHE-RSA-AES256-SHA", "ECDHE-ECDSA-AES256-SHA", "DHE-RSA-AES128-SHA256", "DHE-RSA-AES128-SHA", "DHE-DSS-AES128-SHA256", "DHE-RSA-AES256-SHA256", "DHE-DSS-AES256-SHA", "DHE-RSA-AES256-SHA", "AES128-GCM-SHA256", "AES256-GCM-SHA384", "ECDHE-RSA-RC4-SHA", "ECDHE-ECDSA-RC4-SHA", "AES128", "AES256", "RC4-SHA", "HIGH", "!aNULL", "!eNULL", "!EXPORT", "!DES", "!3DES", "!MD5", "!PSK"]); } $ctx = \stream_context_get_options($socket); if (!empty($ctx['ssl'])) { $ctx = $ctx['ssl']; $compare = $options; $no_SNI_nb_hack = empty($ctx['SNI_nb_hack']); unset($ctx['SNI_nb_hack'], $ctx['peer_certificate'], $ctx['SNI_server_name']); unset($compare['SNI_nb_hack'], $compare['peer_certificate'], $compare['SNI_server_name']); if ($ctx == $compare) { return new Success($socket); } elseif ($no_SNI_nb_hack) { return \Amp\pipe(cryptoDisable($socket), function ($socket) use($options) { return cryptoEnable($socket, $options); }); } } if (isset($options["crypto_method"])) { $method = $options["crypto_method"]; unset($options["crypto_method"]); } elseif (PHP_VERSION_ID >= 50600 && PHP_VERSION_ID < 50606) { $method = \STREAM_CRYPTO_METHOD_TLS_CLIENT; } else { $method = \STREAM_CRYPTO_METHOD_SSLv23_CLIENT; } $options["SNI_nb_hack"] = false; \stream_context_set_option($socket, ["ssl" => $options]); return $isLegacy ? \Amp\resolve(__watchCryptoLegacy($method, $socket)) : __watchCrypto($method, $socket); }