|
|
|
|
@ -34,8 +34,10 @@ use GuzzleHttp\Client as GuzzleClient; |
|
|
|
|
use GuzzleHttp\RequestOptions; |
|
|
|
|
use OCP\Http\Client\IClient; |
|
|
|
|
use OCP\Http\Client\IResponse; |
|
|
|
|
use OCP\Http\Client\LocalServerException; |
|
|
|
|
use OCP\ICertificateManager; |
|
|
|
|
use OCP\IConfig; |
|
|
|
|
use OCP\ILogger; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Class Client |
|
|
|
|
@ -47,20 +49,19 @@ class Client implements IClient { |
|
|
|
|
private $client; |
|
|
|
|
/** @var IConfig */ |
|
|
|
|
private $config; |
|
|
|
|
/** @var ILogger */ |
|
|
|
|
private $logger; |
|
|
|
|
/** @var ICertificateManager */ |
|
|
|
|
private $certificateManager; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param IConfig $config |
|
|
|
|
* @param ICertificateManager $certificateManager |
|
|
|
|
* @param GuzzleClient $client |
|
|
|
|
*/ |
|
|
|
|
public function __construct( |
|
|
|
|
IConfig $config, |
|
|
|
|
ILogger $logger, |
|
|
|
|
ICertificateManager $certificateManager, |
|
|
|
|
GuzzleClient $client |
|
|
|
|
) { |
|
|
|
|
$this->config = $config; |
|
|
|
|
$this->logger = $logger; |
|
|
|
|
$this->client = $client; |
|
|
|
|
$this->certificateManager = $certificateManager; |
|
|
|
|
} |
|
|
|
|
@ -144,6 +145,53 @@ class Client implements IClient { |
|
|
|
|
return $proxy; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected function preventLocalAddress(string $uri, array $options): void { |
|
|
|
|
if (($options['nextcloud']['allow_local_address'] ?? false) || |
|
|
|
|
$this->config->getSystemValueBool('allow_local_remote_servers', false)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$host = parse_url($uri, PHP_URL_HOST); |
|
|
|
|
if ($host === false) { |
|
|
|
|
$this->logger->warning("Could not detect any host in $uri"); |
|
|
|
|
throw new LocalServerException('Could not detect any host'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$host = strtolower($host); |
|
|
|
|
// remove brackets from IPv6 addresses |
|
|
|
|
if (strpos($host, '[') === 0 && substr($host, -1) === ']') { |
|
|
|
|
$host = substr($host, 1, -1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Disallow localhost and local network |
|
|
|
|
if ($host === 'localhost' || substr($host, -6) === '.local' || substr($host, -10) === '.localhost') { |
|
|
|
|
$this->logger->warning("Host $host was not connected to because it violates local access rules"); |
|
|
|
|
throw new LocalServerException('Host violates local access rules'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Disallow hostname only |
|
|
|
|
if (substr_count($host, '.') === 0) { |
|
|
|
|
$this->logger->warning("Host $host was not connected to because it violates local access rules"); |
|
|
|
|
throw new LocalServerException('Host violates local access rules'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ((bool)filter_var($host, FILTER_VALIDATE_IP) && !filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { |
|
|
|
|
$this->logger->warning("Host $host was not connected to because it violates local access rules"); |
|
|
|
|
throw new LocalServerException('Host violates local access rules'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Also check for IPv6 IPv4 nesting, because that's not covered by filter_var |
|
|
|
|
if ((bool)filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && substr_count($host, '.') > 0) { |
|
|
|
|
$delimiter = strrpos($host, ':'); // Get last colon |
|
|
|
|
$ipv4Address = substr($host, $delimiter + 1); |
|
|
|
|
|
|
|
|
|
if (!filter_var($ipv4Address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { |
|
|
|
|
$this->logger->warning("Host $host was not connected to because it violates local access rules"); |
|
|
|
|
throw new LocalServerException('Host violates local access rules'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Sends a GET request |
|
|
|
|
* |
|
|
|
|
@ -174,6 +222,7 @@ class Client implements IClient { |
|
|
|
|
* @throws \Exception If the request could not get completed |
|
|
|
|
*/ |
|
|
|
|
public function get(string $uri, array $options = []): IResponse { |
|
|
|
|
$this->preventLocalAddress($uri, $options); |
|
|
|
|
$response = $this->client->request('get', $uri, $this->buildRequestOptions($options)); |
|
|
|
|
$isStream = isset($options['stream']) && $options['stream']; |
|
|
|
|
return new Response($response, $isStream); |
|
|
|
|
@ -204,6 +253,7 @@ class Client implements IClient { |
|
|
|
|
* @throws \Exception If the request could not get completed |
|
|
|
|
*/ |
|
|
|
|
public function head(string $uri, array $options = []): IResponse { |
|
|
|
|
$this->preventLocalAddress($uri, $options); |
|
|
|
|
$response = $this->client->request('head', $uri, $this->buildRequestOptions($options)); |
|
|
|
|
return new Response($response); |
|
|
|
|
} |
|
|
|
|
@ -238,6 +288,8 @@ class Client implements IClient { |
|
|
|
|
* @throws \Exception If the request could not get completed |
|
|
|
|
*/ |
|
|
|
|
public function post(string $uri, array $options = []): IResponse { |
|
|
|
|
$this->preventLocalAddress($uri, $options); |
|
|
|
|
|
|
|
|
|
if (isset($options['body']) && is_array($options['body'])) { |
|
|
|
|
$options['form_params'] = $options['body']; |
|
|
|
|
unset($options['body']); |
|
|
|
|
@ -276,6 +328,7 @@ class Client implements IClient { |
|
|
|
|
* @throws \Exception If the request could not get completed |
|
|
|
|
*/ |
|
|
|
|
public function put(string $uri, array $options = []): IResponse { |
|
|
|
|
$this->preventLocalAddress($uri, $options); |
|
|
|
|
$response = $this->client->request('put', $uri, $this->buildRequestOptions($options)); |
|
|
|
|
return new Response($response); |
|
|
|
|
} |
|
|
|
|
@ -310,6 +363,7 @@ class Client implements IClient { |
|
|
|
|
* @throws \Exception If the request could not get completed |
|
|
|
|
*/ |
|
|
|
|
public function delete(string $uri, array $options = []): IResponse { |
|
|
|
|
$this->preventLocalAddress($uri, $options); |
|
|
|
|
$response = $this->client->request('delete', $uri, $this->buildRequestOptions($options)); |
|
|
|
|
return new Response($response); |
|
|
|
|
} |
|
|
|
|
@ -344,6 +398,7 @@ class Client implements IClient { |
|
|
|
|
* @throws \Exception If the request could not get completed |
|
|
|
|
*/ |
|
|
|
|
public function options(string $uri, array $options = []): IResponse { |
|
|
|
|
$this->preventLocalAddress($uri, $options); |
|
|
|
|
$response = $this->client->request('options', $uri, $this->buildRequestOptions($options)); |
|
|
|
|
return new Response($response); |
|
|
|
|
} |
|
|
|
|
|