// 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 CHROME_SERVICES_SHARING_NEARBY_PLATFORM_WIFI_LAN_MEDIUM_H_
#define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_WIFI_LAN_MEDIUM_H_
#include <memory>
#include <optional>
#include <string>
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "chrome/services/sharing/nearby/platform/wifi_lan_server_socket.h"
#include "chrome/services/sharing/nearby/platform/wifi_lan_socket.h"
#include "chromeos/ash/services/nearby/public/mojom/firewall_hole.mojom.h"
#include "chromeos/ash/services/nearby/public/mojom/mdns.mojom.h"
#include "chromeos/ash/services/nearby/public/mojom/tcp_socket_factory.mojom.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/address_list.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "services/network/public/mojom/tcp_socket.mojom.h"
#include "third_party/nearby/src/internal/platform/implementation/wifi_lan.h"
namespace ash {
namespace nearby {
class TcpServerSocketPort;
} // namespace nearby
} // namespace ash
namespace base {
class SequencedTaskRunner;
class WaitableEvent;
} // namespace base
namespace nearby {
namespace chrome {
// An implementation of the abstract Nearby Connections's class
// api::WifiLanMedium. The implementation uses the
// ::sharing::mojom::TcpSocketFactory mojo interface to 1) connect to remote
// server sockets, and 2) open local server sockets to listen for incoming
// connection requests from remote devices. We block while 1) trying to connect,
// 2) creating a server socket, and 3) cancelling pending tasks in the
// destructor. We guarantee thread safety, and we guarantee that all blocking
// connection and listening attempts return before destruction.
class WifiLanMedium : public api::WifiLanMedium,
public ::sharing::mojom::MdnsObserver {
public:
WifiLanMedium(
const mojo::SharedRemote<::sharing::mojom::TcpSocketFactory>&
socket_factory,
const mojo::SharedRemote<
chromeos::network_config::mojom::CrosNetworkConfig>&
cros_network_config,
const mojo::SharedRemote<::sharing::mojom::FirewallHoleFactory>&
firewall_hole_factory,
const mojo::SharedRemote<::sharing::mojom::MdnsManager>& mdns_manager);
WifiLanMedium(const WifiLanMedium&) = delete;
WifiLanMedium& operator=(const WifiLanMedium&) = delete;
~WifiLanMedium() override;
// Check if a network connection to a primary router exist.
bool IsNetworkConnected() const override;
// api::WifiLanMedium:
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const NsdServiceInfo& remote_service_info,
CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const std::string& ip_address,
int port,
CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanServerSocket> ListenForService(int port) override;
std::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange()
override;
bool StartDiscovery(const std::string& service_type,
DiscoveredServiceCallback callback) override;
bool StopDiscovery(const std::string& service_type) override;
private:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ConnectResult {
kSuccess = 0,
kCanceled = 1,
kErrorFailedToCreateTcpSocket = 2,
kMaxValue = kErrorFailedToCreateTcpSocket,
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ListenResult {
kSuccess = 0,
kCanceled = 1,
kErrorInvalidPort = 2,
kErrorFetchIpFailedToGetNetworkStateList = 3,
kErrorFetchIpFailedToGetManagedProperties = 4,
kErrorFetchIpMissingIpConfigs = 5,
kErrorFetchIpNoValidLocalIpAddress = 6,
kErrorFailedToCreateTcpServerSocket = 7,
kErrorUnexpectedTcpServerSocketIpEndpoint = 8,
kErrorFailedToCreateFirewallHole = 9,
kMaxValue = kErrorFailedToCreateFirewallHole,
};
/*==========================================================================*/
// ConnectToService() helpers: Connect to remote server socket.
/*==========================================================================*/
void DoConnect(const net::AddressList& address_list,
std::optional<WifiLanSocket::ConnectedSocketParameters>*
connected_socket_parameters,
base::WaitableEvent* connect_waitable_event);
void OnConnect(std::optional<WifiLanSocket::ConnectedSocketParameters>*
connected_socket_parameters,
base::WaitableEvent* connect_waitable_event,
mojo::PendingRemote<network::mojom::TCPConnectedSocket>
tcp_connected_socket,
int32_t result,
const std::optional<net::IPEndPoint>& local_addr,
const std::optional<net::IPEndPoint>& peer_addr,
mojo::ScopedDataPipeConsumerHandle receive_stream,
mojo::ScopedDataPipeProducerHandle send_stream);
/*==========================================================================*/
/*==========================================================================*/
// ListenForService() helpers: Listen for and accept incoming connections.
/*==========================================================================*/
void DoListenForService(
std::optional<WifiLanServerSocket::ServerSocketParameters>*
server_socket_parameters,
base::WaitableEvent* listen_waitable_event,
int port);
void OnGetNetworkStateList(
std::optional<WifiLanServerSocket::ServerSocketParameters>*
server_socket_parameters,
base::WaitableEvent* listen_waitable_event,
const ash::nearby::TcpServerSocketPort& port,
std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr>
result);
void OnGetNetworkProperties(
std::optional<WifiLanServerSocket::ServerSocketParameters>*
server_socket_parameters,
base::WaitableEvent* listen_waitable_event,
const ash::nearby::TcpServerSocketPort& port,
chromeos::network_config::mojom::ManagedPropertiesPtr properties);
void OnTcpServerSocketCreated(
std::optional<WifiLanServerSocket::ServerSocketParameters>*
server_socket_parameters,
base::WaitableEvent* listen_waitable_event,
mojo::PendingRemote<network::mojom::TCPServerSocket> tcp_server_socket,
const net::IPAddress& ip_address,
const ash::nearby::TcpServerSocketPort& port,
int32_t result,
const std::optional<net::IPEndPoint>& local_addr);
void OnFirewallHoleCreated(
std::optional<WifiLanServerSocket::ServerSocketParameters>*
server_socket_parameters,
base::WaitableEvent* listen_waitable_event,
mojo::PendingRemote<network::mojom::TCPServerSocket> tcp_server_socket,
const net::IPEndPoint& local_addr,
mojo::PendingRemote<::sharing::mojom::FirewallHole> firewall_hole);
/*==========================================================================*/
/*==========================================================================*/
// api::WifiLanMedium: Not implemented.
/*==========================================================================*/
bool StartAdvertising(const NsdServiceInfo& nsd_service_info) override;
bool StopAdvertising(const NsdServiceInfo& nsd_service_info) override;
/*==========================================================================*/
// sharing::mojom::MdnsObserver
void ServiceFound(::sharing::mojom::NsdServiceInfoPtr service_info) override;
void ServiceLost(::sharing::mojom::NsdServiceInfoPtr service_info) override;
// Removes |event| from the set of pending events and signals |event|. Calls
// to these methods are sequenced on |task_runner_| and thus thread safe.
void FinishConnectAttempt(base::WaitableEvent* event, ConnectResult result);
void FinishListenAttempt(base::WaitableEvent* event, ListenResult result);
// Resets the SharedRemotes and finishes all pending connect/listen attempts
// with null results.
void Shutdown(base::WaitableEvent* shutdown_waitable_event);
scoped_refptr<base::SequencedTaskRunner> task_runner_;
mojo::SharedRemote<::sharing::mojom::TcpSocketFactory> socket_factory_;
mojo::SharedRemote<chromeos::network_config::mojom::CrosNetworkConfig>
cros_network_config_;
mojo::SharedRemote<::sharing::mojom::FirewallHoleFactory>
firewall_hole_factory_;
// Unlike other remotes, mdns_manager_ must be implicitly destructed
// instead of destructed on the task_runner_ sequence.
mojo::SharedRemote<::sharing::mojom::MdnsManager> mdns_manager_;
mojo::Receiver<::sharing::mojom::MdnsObserver> mdns_observer_{this};
// Map from service_type to discovered_cb for StartDiscovery.
std::map<std::string, DiscoveredServiceCallback> discovery_callbacks_;
// Track all pending connect/listen tasks in case Close() is called while
// waiting.
base::flat_set<raw_ptr<base::WaitableEvent, CtnExperimental>>
pending_connect_waitable_events_;
base::flat_set<raw_ptr<base::WaitableEvent, CtnExperimental>>
pending_listen_waitable_events_;
};
} // namespace chrome
} // namespace nearby
#endif // CHROME_SERVICES_SHARING_NEARBY_PLATFORM_WIFI_LAN_MEDIUM_H_