public function doExecute(Manager $args) { $email = $args->get("email"); (yield \Amp\resolve($this->checkEmail($email))); $server = \Kelunik\AcmeClient\resolveServer($args->get("server")); $keyFile = \Kelunik\AcmeClient\serverToKeyname($server); $path = "accounts/{$keyFile}.pem"; $bits = 4096; $keyStore = new KeyStore(\Kelunik\AcmeClient\normalizePath($args->get("storage"))); $this->climate->br(); try { $keyPair = (yield $keyStore->get($path)); $this->climate->whisper(" Using existing private key ..."); } catch (KeyStoreException $e) { $this->climate->whisper(" No private key found, generating new one ..."); $keyPair = (new OpenSSLKeyGenerator())->generate($bits); $keyPair = (yield $keyStore->put($path, $keyPair)); $this->climate->whisper(" Generated new private key with {$bits} bits."); } $acme = $this->acmeFactory->build($server, $keyPair); $this->climate->whisper(" Registering with " . substr($server, 8) . " ..."); /** @var Registration $registration */ $registration = (yield $acme->register($email)); $this->climate->info(" Registration successful. Contacts: " . implode(", ", $registration->getContact())); $this->climate->br(); (yield new CoroutineResult(0)); }
public function put($path, KeyPair $keyPair) { if (!is_string($path)) { throw new InvalidArgumentException(sprintf("\$root must be of type string, %s given.", gettype($path))); } return \Amp\resolve($this->doPut($path, $keyPair)); }
/** * @param $domains * @return \Generator * @throws AcmeException */ private function doIssue($domains) { //validate domains (yield \Amp\resolve($this->checkDnsRecords($domains))); //todo check avalibles aliases an applications and find each roots $keyFile = $this->serverToKeyName(); try { $keyPair = $this->getKeyStorage()->get($keyFile); } catch (FilesystemException $e) { throw new InvalidCallException("Account key not found, did you run 'yii acme/setup' or 'yii acme/quick'?", 0, $e); } $acme = $this->getAcmeService($keyPair); $promises = []; foreach ($domains as $domain) { $promises[] = \Amp\resolve($this->solveChallenge($acme, $keyPair, $domain)); } list($errors) = (yield \Amp\any($promises)); if (!empty($errors)) { foreach ($errors as $error) { echo $error->getMessage() . PHP_EOL; } throw new AcmeException("Issuance failed, not all challenges could be solved."); } $path = implode(DIRECTORY_SEPARATOR, ['certs', $this->serverToKeyName(), reset($domains)]); $keyPath = $path . DIRECTORY_SEPARATOR . 'key'; try { $keyPair = $this->getKeyStorage()->get($keyPath); } catch (FilesystemException $e) { $keyPair = (new OpenSSLKeyGenerator())->generate($this->keyLength); $keyPair = $this->getKeyStorage()->put($keyPath, $keyPair); } $location = (yield $acme->requestCertificate($keyPair, $domains)); $certificates = (yield $acme->pollForCertificate($location)); $certificateStore = $this->getCertificateStorage(); $certificateStore->setRoot($certificateStore->getRoot() . $path); $result = $certificateStore->put($certificates); (yield new CoroutineResult($result)); }
/** * Verifies a DNS-01 Challenge. * * Can be used to verify a challenge before requesting validation from a CA to catch errors early. * * @api * @param string $domain domain to verify * @param string $keyAuthorization key authorization (expected payload) * @return \Amp\Promise resolves to the DNS entry found * @throws AcmeException If the challenge could not be verified. */ public function verifyChallenge($domain, $keyAuthorization) { return \Amp\resolve($this->doVerifyChallenge($domain, $keyAuthorization)); }
public function delete($name) { return \Amp\resolve($this->doDelete($name)); }
/** * {@inheritdoc} */ public function put($path, $contents) { return \Amp\resolve($this->doPut($path, $contents), $this->reactor); }
function __init() { $state = new \StdClass(); $state->messageFactory = new MessageFactory(); $state->questionFactory = new QuestionFactory(); $state->encoder = (new EncoderFactory())->create(); $state->decoder = (new DecoderFactory())->create(); $state->arrayCache = new ArrayCache(); $state->requestIdCounter = 1; $state->pendingRequests = []; $state->serverIdMap = []; $state->serverUriMap = []; $state->serverIdTimeoutMap = []; $state->now = \time(); $state->serverTimeoutWatcher = \Amp\repeat(function ($watcherId) use($state) { $state->now = $now = \time(); foreach ($state->serverIdTimeoutMap as $id => $expiry) { if ($now > $expiry) { __unloadServer($state, $id); } } if (empty($state->serverIdMap)) { \Amp\disable($watcherId); } }, 1000, $options = ["enable" => true, "keep_alive" => false]); $state->config = (yield \Amp\resolve(__loadResolvConf())); (yield new CoroutineResult($state)); }
/** * @param string $name * @return mixed * @throws \Throwable */ public function revoke($name = '') { return \Amp\wait(\Amp\resolve($this->doRevoke($name))); }
/** * Verifies a HTTP-01 challenge. * * Can be used to verify a challenge before requesting validation from a CA to catch errors early. * * @api * @param string $domain domain to verify * @param string $token challenge token * @param string $payload expected payload * @return \Amp\Promise resolves to null * @throws AcmeException If the challenge could not be verified. */ public function verifyChallenge($domain, $token, $payload) { return \Amp\resolve($this->doVerifyChallenge($domain, $token, $payload)); }
private function doResolve($name, array $types, $options) { if (!$this->config) { $this->config = (yield \Amp\resolve($this->loadResolvConf())); } if (empty($types)) { (yield new CoroutineResult([])); return; } assert(array_reduce($types, function ($result, $val) { return $result && \is_int($val); }, true), 'The $types passed to DNS functions must all be integers (from \\Amp\\Dns\\Record class)'); $name = \strtolower($name); $result = []; // Check for cache hits if (!isset($options["cache"]) || $options["cache"]) { foreach ($types as $k => $type) { $cacheKey = "{$name}#{$type}"; $cacheValue = (yield $this->arrayCache->get($cacheKey)); if ($cacheValue !== null) { $result[$type] = $cacheValue; unset($types[$k]); } } if (empty($types)) { if (empty(array_filter($result))) { throw new NoRecordException("No records returned for {$name} (cached result)"); } else { (yield new CoroutineResult($result)); return; } } } $timeout = empty($options["timeout"]) ? $this->config["timeout"] : (int) $options["timeout"]; if (empty($options["server"])) { if (empty($this->config["nameservers"])) { throw new ResolutionException("No nameserver specified in system config"); } $uri = "udp://" . $this->config["nameservers"][0]; } else { $uri = $this->parseCustomServerUri($options["server"]); } foreach ($types as $type) { $promises[] = $this->doRequest($uri, $name, $type); } try { list(, $resultArr) = (yield \Amp\timeout(\Amp\some($promises), $timeout)); foreach ($resultArr as $value) { $result += $value; } } catch (\Amp\TimeoutException $e) { if (substr($uri, 0, 6) == "tcp://") { throw new TimeoutException("Name resolution timed out for {$name}"); } else { $options["server"] = \preg_replace("#[a-z.]+://#", "tcp://", $uri); (yield new CoroutineResult(\Amp\resolve($this->doResolve($name, $types, $options)))); return; } } catch (ResolutionException $e) { if (empty($result)) { // if we have no cached results throw $e; } } catch (CombinatorException $e) { // if all promises in Amp\some fail if (empty($result)) { // if we have no cached results foreach ($e->getExceptions() as $ex) { if ($ex instanceof NoRecordException) { throw new NoRecordException("No records returned for {$name}", 0, $e); } } throw new ResolutionException("All name resolution requests failed", 0, $e); } } (yield new CoroutineResult($result)); }
public function post($resource, array $payload) { if (!is_string($resource)) { throw new InvalidArgumentException(sprintf("\$resource must be of type string, %s given.", gettype($resource))); } return \Amp\resolve($this->doPost($resource, $payload)); }
public function execute(Manager $args) { return \Amp\resolve($this->doExecute($args)); }
public function revokeCertificate($pem) { if (!is_string($pem)) { throw new InvalidArgumentException(sprintf("\$pem must be of type string, %s given.", gettype($pem))); } return \Amp\resolve($this->doRevokeCertificate($pem)); }
/** * Revokes a certificate. * * @api * @param string $pem PEM encoded certificate * @return \Amp\Promise resolves to true * @throws AcmeException If something went wrong. */ public function revokeCertificate($pem) { return \Amp\resolve($this->doRevokeCertificate($pem)); }
private function doExecute(Manager $args) { if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { if (posix_geteuid() !== 0) { $processUser = posix_getpwnam(posix_geteuid()); $currentUsername = $processUser["name"]; $user = $args->get("user") ?: $currentUsername; if ($currentUsername !== $user) { throw new AcmeException("Running this script with --user only works as root!"); } } else { $user = $args->get("user") ?: "www-data"; } } $domains = array_map("trim", explode(":", str_replace([",", ";"], ":", $args->get("domains")))); (yield \Amp\resolve($this->checkDnsRecords($domains))); $docRoots = explode(PATH_SEPARATOR, str_replace("\\", "/", $args->get("path"))); $docRoots = array_map(function ($root) { return rtrim($root, "/"); }, $docRoots); if (count($domains) < count($docRoots)) { throw new AcmeException("Specified more document roots than domains."); } if (count($domains) > count($docRoots)) { $docRoots = array_merge($docRoots, array_fill(count($docRoots), count($domains) - count($docRoots), end($docRoots))); } $keyStore = new KeyStore(\Kelunik\AcmeClient\normalizePath($args->get("storage"))); $server = \Kelunik\AcmeClient\resolveServer($args->get("server")); $keyFile = \Kelunik\AcmeClient\serverToKeyname($server); try { $keyPair = (yield $keyStore->get("accounts/{$keyFile}.pem")); } catch (KeyStoreException $e) { throw new AcmeException("Account key not found, did you run 'bin/acme setup'?", 0, $e); } $this->climate->br(); $acme = $this->acmeFactory->build($server, $keyPair); $errors = []; $domainChunks = array_chunk($domains, 10, true); foreach ($domainChunks as $domainChunk) { $promises = []; foreach ($domainChunk as $i => $domain) { $promises[] = \Amp\resolve($this->solveChallenge($acme, $keyPair, $domain, $docRoots[$i])); } list($chunkErrors) = (yield \Amp\any($promises)); $errors += $chunkErrors; } if (!empty($errors)) { foreach ($errors as $error) { $this->climate->error($error->getMessage()); } throw new AcmeException("Issuance failed, not all challenges could be solved."); } $path = "certs/" . $keyFile . "/" . reset($domains) . "/key.pem"; $bits = $args->get("bits"); try { $keyPair = (yield $keyStore->get($path)); } catch (KeyStoreException $e) { $keyPair = (new OpenSSLKeyGenerator())->generate($bits); $keyPair = (yield $keyStore->put($path, $keyPair)); } $this->climate->br(); $this->climate->whisper(" Requesting certificate ..."); $location = (yield $acme->requestCertificate($keyPair, $domains)); $certificates = (yield $acme->pollForCertificate($location)); $path = \Kelunik\AcmeClient\normalizePath($args->get("storage")) . "/certs/" . $keyFile; $certificateStore = new CertificateStore($path); (yield $certificateStore->put($certificates)); $this->climate->info(" Successfully issued certificate."); $this->climate->info(" See {$path}/" . reset($domains)); $this->climate->br(); (yield new CoroutineResult(0)); }
/** * Retrieves a resource using a POST request. * * @api * @param string $resource resource to fetch * @param array $payload * @return \Amp\Promise resolves to the HTTP response * @throws AcmeException If the request failed. */ public function post($resource, array $payload) { return \Amp\resolve($this->doPost($resource, $payload)); }
<?php require './example_bootstrap.php'; require 'support/generic_table.php'; \Amp\run(function () { $db = new \Amp\Mysql\Pool("host=" . DB_HOST . ";user="******";pass="******";db=" . DB_NAME); /* create same table than in 003_generic_with_yield.php */ (yield \Amp\resolve(genTable($db))); /* yeah, we need a lot of yields and assigns here... With PHP 7 we finally can drop a lot of stupid parenthesis! */ $query = (yield $db->query("SELECT a * b FROM tmp")); while ((list($result) = (yield $query->fetchRow())) !== null) { var_dump($result); // outputs each row of the resultset returned by SELECT a * b FROM tmp } /* or, maybe, wait until they're all fetched (because you anyway only can continue after having full resultset */ $query = (yield $db->query("SELECT a * b FROM tmp")); $objs = (yield $query->fetchObjects()); var_dump($objs); // outputs all the rows as objects of the resultset returned by SELECT a * b FROM tmp (yield $db->query("DROP TABLE tmp")); $db->close(); });
public function delete($token) { return \Amp\resolve($this->doDelete($token)); }
/** * Verifies a DNS-01 Challenge. * * Can be used to verify a challenge before requesting validation from a CA to catch errors early. * * @api * @param string $domain domain to verify * @param string $expectedPayload expected DNS record value * @return \Amp\Promise resolves to the DNS entry found * @throws AcmeException If the challenge could not be verified. */ public function verifyChallenge($domain, $expectedPayload) { return \Amp\resolve($this->doVerifyChallenge($domain, $expectedPayload)); }
private function commandParser($client) : \Generator { $readWatcherId = yield; $buffer = ""; $length = null; do { yield; $data = @fread($client, 8192); if ($data == "" && (!is_resource($client) || @feof($client))) { \Amp\cancel($readWatcherId); return; } $buffer .= $data; do { if (!isset($length)) { if (!isset($buffer[3])) { break; } $length = unpack("Nlength", substr($buffer, 0, 4))["length"]; $buffer = substr($buffer, 4); } if (!isset($buffer[$length - 1])) { break; } $message = @\json_decode(substr($buffer, 0, $length), true); $buffer = (string) substr($buffer, $length); $length = null; if (!isset($message["action"])) { continue; } switch ($message["action"]) { case "restart": $this->restart(); break; case "stop": \Amp\resolve($this->stop())->when('Amp\\stop'); break; } } while (1); } while (1); }
/** * @param $email * @return Registration * @throws \Throwable */ public function setup($email) { return \Amp\wait(\Amp\resolve($this->doSetup($email))); }
/** * @param Manager $args * @return \Generator */ private function doExecute(Manager $args) { $configPath = $args->get("config"); try { $config = Yaml::parse((yield \Amp\File\get($configPath))); } catch (FilesystemException $e) { $this->climate->error("Config file ({$configPath}) not found."); (yield new CoroutineResult(self::EXIT_CONFIG_ERROR)); return; } catch (ParseException $e) { $this->climate->error("Config file ({$configPath}) had an invalid format and couldn't be parsed."); (yield new CoroutineResult(self::EXIT_CONFIG_ERROR)); return; } if ($args->defined("server")) { $config["server"] = $args->get("server"); } else { if (!isset($config["server"]) && $args->exists("server")) { $config["server"] = $args->get("server"); } } if ($args->defined("storage")) { $config["storage"] = $args->get("storage"); } else { if (!isset($config["storage"]) && $args->exists("storage")) { $config["storage"] = $args->get("storage"); } } if (!isset($config["server"])) { $this->climate->error("Config file ({$configPath}) didn't have a 'server' set nor was it passed as command line argument."); (yield new CoroutineResult(self::EXIT_CONFIG_ERROR)); return; } if (!isset($config["storage"])) { $this->climate->error("Config file ({$configPath}) didn't have a 'storage' set nor was it passed as command line argument."); (yield new CoroutineResult(self::EXIT_CONFIG_ERROR)); return; } if (!isset($config["email"])) { $this->climate->error("Config file ({$configPath}) didn't have a 'email' set."); (yield new CoroutineResult(self::EXIT_CONFIG_ERROR)); return; } if (!isset($config["certificates"]) || !is_array($config["certificates"])) { $this->climate->error("Config file ({$configPath}) didn't have a 'certificates' section that's an array."); (yield new CoroutineResult(self::EXIT_CONFIG_ERROR)); return; } $command = implode(" ", array_map("escapeshellarg", [PHP_BINARY, $GLOBALS["argv"][0], "setup", "--server", $config["server"], "--storage", $config["storage"], "--email", $config["email"]])); $process = new Process($command); $result = (yield $process->exec(Process::BUFFER_ALL)); if ($result->exit !== 0) { $this->climate->error("Registration failed ({$result->exit})"); $this->climate->error($command); $this->climate->br()->out($result->stdout); $this->climate->br()->error($result->stderr); (yield new CoroutineResult(self::EXIT_SETUP_ERROR)); return; } $certificateChunks = array_chunk($config["certificates"], 10, true); $errors = []; $values = []; foreach ($certificateChunks as $certificateChunk) { $promises = []; foreach ($certificateChunk as $certificate) { $promises[] = \Amp\resolve($this->checkAndIssue($certificate, $config["server"], $config["storage"])); } list($chunkErrors, $chunkValues) = (yield \Amp\any($promises)); $errors += $chunkErrors; $values += $chunkValues; } $status = ["no_change" => count(array_filter($values, function ($value) { return $value === self::STATUS_NO_CHANGE; })), "renewed" => count(array_filter($values, function ($value) { return $value === self::STATUS_RENEWED; })), "failure" => count($errors)]; if ($status["renewed"] > 0) { foreach ($values as $i => $value) { if ($value === self::STATUS_RENEWED) { $certificate = $config["certificates"][$i]; $this->climate->info("Certificate for " . implode(", ", array_keys($this->toDomainPathMap($certificate["paths"]))) . " successfully renewed."); } } } if ($status["failure"] > 0) { foreach ($errors as $i => $error) { $certificate = $config["certificates"][$i]; $this->climate->error("Issuance for the following domains failed: " . implode(", ", array_keys($this->toDomainPathMap($certificate["paths"])))); $this->climate->error("Reason: {$error}"); } $exitCode = $status["renewed"] > 0 ? self::EXIT_ISSUANCE_PARTIAL : self::EXIT_ISSUANCE_ERROR; (yield new CoroutineResult($exitCode)); return; } if ($status["renewed"] > 0) { (yield new CoroutineResult(self::EXIT_ISSUANCE_OK)); return; } }
/** * 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"; } } $options["SNI_nb_hack"] = false; 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"]); } 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; } \stream_context_set_option($socket, ["ssl" => $options]); $enable = true; return $isLegacy ? \Amp\resolve(__watchCryptoLegacy($enable, $method, $socket)) : __watchCrypto($enable, $method, $socket); }
/** * 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); }
/** * @return mixed * @throws \Throwable */ public function info() { return \Amp\wait(\Amp\resolve($this->doInfo())); }