Skip to content

Commit 582cf3a

Browse files
[HttpClient] Fix option "resolve" with IPv6 addresses
1 parent 3852b38 commit 582cf3a

File tree

4 files changed

+46
-13
lines changed

4 files changed

+46
-13
lines changed

HttpClientTrait.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,14 @@ private static function mergeDefaultOptions(array $options, array $defaultOption
197197
if ($resolve = $options['resolve'] ?? false) {
198198
$options['resolve'] = [];
199199
foreach ($resolve as $k => $v) {
200-
$options['resolve'][substr(self::parseUrl('http://'.$k)['authority'], 2)] = (string) $v;
200+
if ('' === $v = (string) $v) {
201+
throw new InvalidArgumentException(sprintf('Option "resolve" for host "%s" cannot be empty.', $k));
202+
}
203+
if ('[' === $v[0] && ']' === substr($v, -1) && str_contains($v, ':')) {
204+
$v = substr($v, 1, -1);
205+
}
206+
207+
$options['resolve'][substr(self::parseUrl('http://'.$k)['authority'], 2)] = $v;
201208
}
202209
}
203210

@@ -220,7 +227,14 @@ private static function mergeDefaultOptions(array $options, array $defaultOption
220227

221228
if ($resolve = $defaultOptions['resolve'] ?? false) {
222229
foreach ($resolve as $k => $v) {
223-
$options['resolve'] += [substr(self::parseUrl('http://'.$k)['authority'], 2) => (string) $v];
230+
if ('' === $v = (string) $v) {
231+
throw new InvalidArgumentException(sprintf('Option "resolve" for host "%s" cannot be empty.', $k));
232+
}
233+
if ('[' === $v[0] && ']' === substr($v, -1) && str_contains($v, ':')) {
234+
$v = substr($v, 1, -1);
235+
}
236+
237+
$options['resolve'] += [substr(self::parseUrl('http://'.$k)['authority'], 2) => $v];
224238
}
225239
}
226240

Internal/AmpListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ public function startTlsNegotiation(Request $request): Promise
8080
public function startSendingRequest(Request $request, Stream $stream): Promise
8181
{
8282
$host = $stream->getRemoteAddress()->getHost();
83+
$this->info['primary_ip'] = $host;
8384

8485
if (false !== strpos($host, ':')) {
8586
$host = '['.$host.']';
8687
}
8788

88-
$this->info['primary_ip'] = $host;
8989
$this->info['primary_port'] = $stream->getRemoteAddress()->getPort();
9090
$this->info['pretransfer_time'] = microtime(true) - $this->info['start_time'];
9191
$this->info['debug'] .= sprintf("* Connected to %s (%s) port %d\n", $request->getUri()->getHost(), $host, $this->info['primary_port']);

Internal/AmpResolver.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,31 @@ public function __construct(array &$dnsMap)
3434

3535
public function resolve(string $name, ?int $typeRestriction = null): Promise
3636
{
37-
if (!isset($this->dnsMap[$name]) || !\in_array($typeRestriction, [Record::A, null], true)) {
37+
$recordType = Record::A;
38+
$ip = $this->dnsMap[$name] ?? null;
39+
40+
if (null !== $ip && str_contains($ip, ':')) {
41+
$recordType = Record::AAAA;
42+
}
43+
if (null === $ip || $recordType !== ($typeRestriction ?? $recordType)) {
3844
return Dns\resolver()->resolve($name, $typeRestriction);
3945
}
4046

41-
return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
47+
return new Success([new Record($ip, $recordType, null)]);
4248
}
4349

4450
public function query(string $name, int $type): Promise
4551
{
46-
if (!isset($this->dnsMap[$name]) || Record::A !== $type) {
52+
$recordType = Record::A;
53+
$ip = $this->dnsMap[$name] ?? null;
54+
55+
if (null !== $ip && str_contains($ip, ':')) {
56+
$recordType = Record::AAAA;
57+
}
58+
if (null === $ip || $recordType !== $type) {
4759
return Dns\resolver()->query($name, $type);
4860
}
4961

50-
return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
62+
return new Success([new Record($ip, $recordType, null)]);
5163
}
5264
}

NativeHttpClient.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ public function request(string $method, string $url, array $options = []): Respo
7979
if (str_starts_with($options['bindto'], 'host!')) {
8080
$options['bindto'] = substr($options['bindto'], 5);
8181
}
82+
if ((\PHP_VERSION_ID < 80223 || 80300 <= \PHP_VERSION_ID && 80311 < \PHP_VERSION_ID) && '\\' === \DIRECTORY_SEPARATOR && '[' === $options['bindto'][0]) {
83+
$options['bindto'] = preg_replace('{^\[[^\]]++\]}', '[$0]', $options['bindto']);
84+
}
8285
}
8386

8487
$hasContentLength = isset($options['normalized_headers']['content-length']);
@@ -330,31 +333,35 @@ private static function parseHostPort(array $url, array &$info): array
330333
*/
331334
private static function dnsResolve($host, NativeClientState $multi, array &$info, ?\Closure $onProgress): string
332335
{
333-
if (null === $ip = $multi->dnsCache[$host] ?? null) {
336+
$flag = '' !== $host && '[' === $host[0] && ']' === $host[-1] && str_contains($host, ':') ? \FILTER_FLAG_IPV6 : \FILTER_FLAG_IPV4;
337+
$ip = \FILTER_FLAG_IPV6 === $flag ? substr($host, 1, -1) : $host;
338+
339+
if (filter_var($ip, \FILTER_VALIDATE_IP, $flag)) {
340+
// The host is already an IP address
341+
} elseif (null === $ip = $multi->dnsCache[$host] ?? null) {
334342
$info['debug'] .= "* Hostname was NOT found in DNS cache\n";
335343
$now = microtime(true);
336344

337-
if ('[' === $host[0] && ']' === $host[-1] && filter_var(substr($host, 1, -1), \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
338-
$ip = [$host];
339-
} elseif (!$ip = gethostbynamel($host)) {
345+
if (!$ip = gethostbynamel($host)) {
340346
throw new TransportException(sprintf('Could not resolve host "%s".', $host));
341347
}
342348

343-
$info['namelookup_time'] = microtime(true) - ($info['start_time'] ?: $now);
344349
$multi->dnsCache[$host] = $ip = $ip[0];
345350
$info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
346351
} else {
347352
$info['debug'] .= "* Hostname was found in DNS cache\n";
353+
$host = str_contains($ip, ':') ? "[$ip]" : $ip;
348354
}
349355

356+
$info['namelookup_time'] = microtime(true) - ($info['start_time'] ?: $now);
350357
$info['primary_ip'] = $ip;
351358

352359
if ($onProgress) {
353360
// Notify DNS resolution
354361
$onProgress();
355362
}
356363

357-
return $ip;
364+
return $host;
358365
}
359366

360367
/**

0 commit comments

Comments
 (0)