// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SERVICES_NETWORK_LOCAL_NETWORK_ACCESS_CHECKER_H_
#define SERVICES_NETWORK_LOCAL_NETWORK_ACCESS_CHECKER_H_

#include <stdint.h>

#include "base/component_export.h"
#include "base/memory/raw_ptr.h"
#include "net/base/ip_address.h"
#include "services/network/public/cpp/cors/cors_error_status.h"
#include "services/network/public/cpp/local_network_access_check_result.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "services/network/public/mojom/ip_address_space.mojom-forward.h"
#include "services/network/public/mojom/network_context.mojom-forward.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/origin.h"

class GURL;

namespace net {

struct TransportInfo;

}  // namespace net

namespace network {

struct ResourceRequest;

// Applies Local Network Access checks to a single fetch / URL load.
//
// Manages state used for the "Local Network Access check" algorithm from
// the Local Network Access spec:
// https://wicg.github.io/local-network-access/#local-network-access-check
//
// Helper class for `URLLoader`. Should be instantiated once per `URLLoader`.
//
// Thread-compatible.
class COMPONENT_EXPORT(NETWORK_SERVICE) LocalNetworkAccessChecker {
 public:
  // `resource_request` and `url_load_options` correspond to `URLLoader`
  // constructor arguments.
  // `factory_client_security_state` should point to the client security
  // state object coming from the factory that built the owner `URLLoader`. It
  // can be nullptr when the factory doesn't use a client security state.
  LocalNetworkAccessChecker(
      const ResourceRequest& resource_request,
      const mojom::ClientSecurityState* factory_client_security_state,
      int32_t url_load_options);

  // Instances of this class are neither copyable nor movable.
  LocalNetworkAccessChecker(const LocalNetworkAccessChecker&) = delete;
  LocalNetworkAccessChecker& operator=(const LocalNetworkAccessChecker&) =
      delete;

  ~LocalNetworkAccessChecker();

  // Checks whether the client should be allowed to use the given transport.
  //
  // Implements the following "Local Network Access check" algorithm:
  // https://wicg.github.io/local-network-access/#local-network-access-check
  LocalNetworkAccessCheckResult Check(const net::TransportInfo& transport_info);

  // Returns the IP address space derived from the `transport_info` argument
  // passed to the last call to `Check()`, if any.
  //
  // Spec:
  // https://wicg.github.io/local-network-access/#response-ip-address-space
  absl::optional<mojom::IPAddressSpace> ResponseAddressSpace() const {
    return response_address_space_;
  }

  // The target IP address space applied to subsequent checks.
  //
  // Spec:
  // https://wicg.github.io/local-network-access/#request-target-ip-address-space
  mojom::IPAddressSpace TargetAddressSpace() const {
    return target_address_space_;
  }

  // Clears state from all checks this instance has performed, and sets the
  // request URL to `new_url`.
  //
  // This instance will behave as if newly constructed once more. In addition,
  // resets this instance's target IP address space to `kUnknown`.
  //
  // This should be called upon following a redirect or after a cache result
  // blocked without preflight because we'll try fetching from the network.
  void ResetForRedirect(const GURL& new_url);

  // Clears state from all checks this instance has performed.
  //
  // This instance will behave as if newly constructed once more. In addition,
  // resets this instance's target IP address space to `kUnknown`.
  //
  // This should be called after a cache result was blocked without preflight,
  // because we'll try fetching from the network again.
  void ResetForRetry();

  // Returns the client security state that applies to the current request.
  // May return nullptr.
  //
  // Contains relevant state derived from the fetch client's policy container.
  const mojom::ClientSecurityState* client_security_state() const {
    return client_security_state_.get();
  }

  // Returns an owned clone of `client_security_state()`.
  mojom::ClientSecurityStatePtr CloneClientSecurityState() const;

  // Returns the IP address space in `client_security_state()`.
  // Returns `kUnknown` if `client_security_state()` is nullptr.
  mojom::IPAddressSpace ClientAddressSpace() const;

 private:
  // Returns whether this instance has a client security state containing a
  // policy set to `kPreflightWarn`.
  bool IsPolicyPreflightWarn() const;

  // Helper for `Check()`.
  LocalNetworkAccessCheckResult CheckInternal(
      mojom::IPAddressSpace resource_address_space);

  // Sets the current request URL (it may change after redirects).
  void SetRequestUrl(const GURL& url);

  // The client security state copied from the request's trusted params.
  // May be nullptr.
  //
  // Should not be used directly. Use `client_security_state_` instead, which
  // points to the same struct iff this client security state should be used.
  const mojom::ClientSecurityStatePtr request_client_security_state_;

  // The security state of the client of the fetch. May be nullptr.
  const raw_ptr<const mojom::ClientSecurityState> client_security_state_;

  // Whether to block all requests to non-public IP address spaces, regardless
  // of other considerations. Set based on URL load options.
  const bool should_block_local_request_;

  // Whether the request URL's scheme is `http:`.
  bool is_request_url_scheme_http_ = false;

  // If the request URL's host is a local IP literal, then this stores the
  // IP. Nullopt otherwise.
  //
  // For example:
  //
  // - request url = `http://192.168.1.1`   -> `192.168.1.1`
  // - request url = `http://[fe80::]:1234` -> `fe80::`
  // - request url = `https://10.0.0.1`     -> `10.0.0.1`
  // - request url = `http://localhost`     -> nullptr
  //
  // Used to compute metrics for https://crbug.com/1381471.
  absl::optional<net::IPAddress> request_url_local_ip_;

  // The target IP address space set on the request. Ignored if `kUnknown`.
  //
  // Copied from `ResourceRequest::target_ip_address_space`.
  //
  // Invariant: always `kUnknown` if `client_security_state_` is nullptr, or
  // if `client_security_state_->local_network_request_policy` is `kAllow`.
  //
  // https://wicg.github.io/local-network-access/#request-target-ip-address-space
  mojom::IPAddressSpace target_address_space_;

  // The IP address space derived from the `transport_info` argument passed to
  // the last call to `Check()`.
  //
  // Set by `Check()`, reset by `ResetForRedirect()`.
  absl::optional<mojom::IPAddressSpace> response_address_space_;

  // The request initiator origin.
  absl::optional<url::Origin> request_initiator_;

  // The request is from/to a potentially trustworthy and same origin.
  bool is_potentially_trustworthy_same_origin_;
};

}  // namespace network

#endif  // SERVICES_NETWORK_LOCAL_NETWORK_ACCESS_CHECKER_H_