Skip to content

Commit 3b643b8

Browse files
[HttpClient] Resolve hostnames in NoPrivateNetworkHttpClient
1 parent ebcaeea commit 3b643b8

10 files changed

+78
-15
lines changed

HttpOptions.php

+2
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ public function buffer(bool $buffer)
148148
}
149149

150150
/**
151+
* @param callable(int, int, array, \Closure|null=):void $callback
152+
*
151153
* @return $this
152154
*/
153155
public function setOnProgress(callable $callback)

NativeHttpClient.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,15 @@ public function request(string $method, string $url, array $options = []): Respo
138138
// Memoize the last progress to ease calling the callback periodically when no network transfer happens
139139
$lastProgress = [0, 0];
140140
$maxDuration = 0 < $options['max_duration'] ? $options['max_duration'] : \INF;
141-
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info, $maxDuration) {
141+
$multi = $this->multi;
142+
$resolve = static function (string $host, ?string $ip = null) use ($multi): ?string {
143+
if (null !== $ip) {
144+
$multi->dnsCache[$host] = $ip;
145+
}
146+
147+
return $multi->dnsCache[$host] ?? null;
148+
};
149+
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info, $maxDuration, $resolve) {
142150
if ($info['total_time'] >= $maxDuration) {
143151
throw new TransportException(sprintf('Max duration was reached for "%s".', implode('', $info['url'])));
144152
}
@@ -154,7 +162,7 @@ public function request(string $method, string $url, array $options = []): Respo
154162
$lastProgress = $progress ?: $lastProgress;
155163
}
156164

157-
$onProgress($lastProgress[0], $lastProgress[1], $progressInfo);
165+
$onProgress($lastProgress[0], $lastProgress[1], $progressInfo, $resolve);
158166
};
159167
} elseif (0 < $options['max_duration']) {
160168
$maxDuration = $options['max_duration'];

NoPrivateNetworkHttpClient.php

+15-2
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,24 @@ public function request(string $method, string $url, array $options = []): Respo
8080
$lastUrl = '';
8181
$lastPrimaryIp = '';
8282

83-
$options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastUrl, &$lastPrimaryIp): void {
83+
$options['on_progress'] = function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use ($onProgress, $subnets, &$lastUrl, &$lastPrimaryIp): void {
8484
if ($info['url'] !== $lastUrl) {
8585
$host = trim(parse_url($info['url'], PHP_URL_HOST) ?: '', '[]');
86+
$resolve ??= static fn () => null;
87+
88+
if (($ip = $host)
89+
&& !filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)
90+
&& !filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)
91+
&& !$ip = $resolve($host)
92+
) {
93+
if ($ip = @(dns_get_record($host, \DNS_A)[0]['ip'] ?? null)) {
94+
$resolve($host, $ip);
95+
} elseif ($ip = @(dns_get_record($host, \DNS_AAAA)[0]['ipv6'] ?? null)) {
96+
$resolve($host, '['.$ip.']');
97+
}
98+
}
8699

87-
if ($host && IpUtils::checkIp($host, $subnets ?? self::PRIVATE_SUBNETS)) {
100+
if ($ip && IpUtils::checkIp($ip, $subnets ?? self::PRIVATE_SUBNETS)) {
88101
throw new TransportException(sprintf('Host "%s" is blocked for "%s".', $host, $info['url']));
89102
}
90103

Response/AmpResponse.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,17 @@ public function __construct(AmpClientState $multi, Request $request, array $opti
8989
$info['max_duration'] = $options['max_duration'];
9090
$info['debug'] = '';
9191

92+
$resolve = static function (string $host, ?string $ip = null) use ($multi): ?string {
93+
if (null !== $ip) {
94+
$multi->dnsCache[$host] = $ip;
95+
}
96+
97+
return $multi->dnsCache[$host] ?? null;
98+
};
9299
$onProgress = $options['on_progress'] ?? static function () {};
93-
$onProgress = $this->onProgress = static function () use (&$info, $onProgress) {
100+
$onProgress = $this->onProgress = static function () use (&$info, $onProgress, $resolve) {
94101
$info['total_time'] = microtime(true) - $info['start_time'];
95-
$onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info);
102+
$onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info, $resolve);
96103
};
97104

98105
$pauseDeferred = new Deferred();

Response/AsyncContext.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ public function replaceRequest(string $method, string $url, array $options = [])
156156
$this->info['previous_info'][] = $info = $this->response->getInfo();
157157
if (null !== $onProgress = $options['on_progress'] ?? null) {
158158
$thisInfo = &$this->info;
159-
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
160-
$onProgress($dlNow, $dlSize, $thisInfo + $info);
159+
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$thisInfo, $onProgress) {
160+
$onProgress($dlNow, $dlSize, $thisInfo + $info, $resolve);
161161
};
162162
}
163163
if (0 < ($info['max_duration'] ?? 0) && 0 < ($info['total_time'] ?? 0)) {

Response/AsyncResponse.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public function __construct(HttpClientInterface $client, string $method, string
5151

5252
if (null !== $onProgress = $options['on_progress'] ?? null) {
5353
$thisInfo = &$this->info;
54-
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
55-
$onProgress($dlNow, $dlSize, $thisInfo + $info);
54+
$options['on_progress'] = static function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$thisInfo, $onProgress) {
55+
$onProgress($dlNow, $dlSize, $thisInfo + $info, $resolve);
5656
};
5757
}
5858
$this->response = $client->request($method, $url, ['buffer' => false] + $options);

Response/CurlResponse.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,20 @@ public function __construct(CurlClientState $multi, $ch, ?array $options = null,
115115
curl_pause($ch, \CURLPAUSE_CONT);
116116

117117
if ($onProgress = $options['on_progress']) {
118+
$resolve = static function (string $host, ?string $ip = null) use ($multi): ?string {
119+
if (null !== $ip) {
120+
$multi->dnsCache->hostnames[$host] = $ip;
121+
}
122+
123+
return $multi->dnsCache->hostnames[$host] ?? null;
124+
};
118125
$url = isset($info['url']) ? ['url' => $info['url']] : [];
119126
curl_setopt($ch, \CURLOPT_NOPROGRESS, false);
120-
curl_setopt($ch, \CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi, $debugBuffer) {
127+
curl_setopt($ch, \CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi, $debugBuffer, $resolve) {
121128
try {
122129
rewind($debugBuffer);
123130
$debug = ['debug' => stream_get_contents($debugBuffer)];
124-
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug);
131+
$onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug, $resolve);
125132
} catch (\Throwable $e) {
126133
$multi->handlesActivity[(int) $ch][] = null;
127134
$multi->handlesActivity[(int) $ch][] = $e;

Tests/HttpClientTestCase.php

+23
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
1717
use Symfony\Component\HttpClient\Exception\TransportException;
1818
use Symfony\Component\HttpClient\Internal\ClientState;
19+
use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;
1920
use Symfony\Component\HttpClient\Response\StreamWrapper;
2021
use Symfony\Component\Process\Exception\ProcessFailedException;
2122
use Symfony\Component\Process\Process;
@@ -466,4 +467,26 @@ public function testMisspelledScheme()
466467

467468
$httpClient->request('GET', 'http:/localhost:8057/');
468469
}
470+
471+
public function testNoPrivateNetwork()
472+
{
473+
$client = $this->getHttpClient(__FUNCTION__);
474+
$client = new NoPrivateNetworkHttpClient($client);
475+
476+
$this->expectException(TransportException::class);
477+
$this->expectExceptionMessage('Host "localhost" is blocked');
478+
479+
$client->request('GET', 'http://localhost:8888');
480+
}
481+
482+
public function testNoPrivateNetworkWithResolve()
483+
{
484+
$client = $this->getHttpClient(__FUNCTION__);
485+
$client = new NoPrivateNetworkHttpClient($client);
486+
487+
$this->expectException(TransportException::class);
488+
$this->expectExceptionMessage('Host "symfony.com" is blocked');
489+
490+
$client->request('GET', 'http://symfony.com', ['resolve' => ['symfony.com' => '127.0.0.1']]);
491+
}
469492
}

Tests/MockHttpClientTest.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -304,14 +304,17 @@ protected function getHttpClient(string $testCase): HttpClientInterface
304304

305305
switch ($testCase) {
306306
default:
307-
return new MockHttpClient(function (string $method, string $url, array $options) use ($client) {
307+
return new MockHttpClient(function (string $method, string $url, array $options) use ($client, $testCase) {
308308
try {
309309
// force the request to be completed so that we don't test side effects of the transport
310310
$response = $client->request($method, $url, ['buffer' => false] + $options);
311311
$content = $response->getContent(false);
312312

313313
return new MockResponse($content, $response->getInfo());
314314
} catch (\Throwable $e) {
315+
if (str_starts_with($testCase, 'testNoPrivateNetwork')) {
316+
throw $e;
317+
}
315318
$this->fail($e->getMessage());
316319
}
317320
});

TraceableHttpClient.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ public function request(string $method, string $url, array $options = []): Respo
5858
$content = false;
5959
}
6060

61-
$options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use (&$traceInfo, $onProgress) {
61+
$options['on_progress'] = function (int $dlNow, int $dlSize, array $info, ?\Closure $resolve = null) use (&$traceInfo, $onProgress) {
6262
$traceInfo = $info;
6363

6464
if (null !== $onProgress) {
65-
$onProgress($dlNow, $dlSize, $info);
65+
$onProgress($dlNow, $dlSize, $info, $resolve);
6666
}
6767
};
6868

0 commit comments

Comments
 (0)