socket_proxy/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Implementation of the network socket proxy.
6//!
7//! Runs proxied versions of fuchsia.posix.socket.Provider and fuchsia.posix.socket.raw.Provider.
8//! Exposes fuchsia.net.policy.socketproxy.StarnixNetworks,
9//! fuchsia.net.policy.socketproxy.FuchsiaNetworks, and
10//! fuchsia.net.policy.socketproxy.DnsServerWatcher.
11
12use anyhow::Context as _;
13use fidl_fuchsia_posix_socket::{self as fposix_socket, OptionalUint32};
14use fuchsia_component::server::{ServiceFs, ServiceFsDir};
15use fuchsia_inspect::health::Reporter;
16use fuchsia_inspect_derive::{Inspect, WithInspect as _};
17use futures::StreamExt as _;
18use futures::channel::mpsc;
19use futures::lock::Mutex;
20use log::{error, info};
21use std::sync::Arc;
22use {
23    fidl_fuchsia_net as fnet, fidl_fuchsia_net_policy_socketproxy as fnp_socketproxy,
24    fuchsia_async as fasync,
25};
26
27mod dns_watcher;
28pub mod registry;
29mod socket_provider;
30
31pub use registry::{NetworkConversionError, NetworkExt, NetworkRegistryError};
32
33#[derive(Copy, Clone, Debug)]
34struct SocketMarks {
35    mark_1: OptionalUint32,
36    mark_2: OptionalUint32,
37}
38
39impl From<SocketMarks> for fnet::Marks {
40    fn from(SocketMarks { mark_1, mark_2 }: SocketMarks) -> Self {
41        let into_option_u32 = |opt| match opt {
42            OptionalUint32::Unset(fposix_socket::Empty) => None,
43            OptionalUint32::Value(val) => Some(val),
44        };
45        Self {
46            mark_1: into_option_u32(mark_1),
47            mark_2: into_option_u32(mark_2),
48            __source_breaking: fidl::marker::SourceBreaking,
49        }
50    }
51}
52
53impl SocketMarks {
54    fn has_value(&self) -> bool {
55        match (self.mark_1, self.mark_2) {
56            (OptionalUint32::Value(_), _) => true,
57            (_, OptionalUint32::Value(_)) => true,
58            _ => false,
59        }
60    }
61}
62
63impl Default for SocketMarks {
64    fn default() -> Self {
65        Self {
66            mark_1: OptionalUint32::Unset(fposix_socket::Empty),
67            mark_2: OptionalUint32::Unset(fposix_socket::Empty),
68        }
69    }
70}
71
72#[derive(Inspect)]
73struct SocketProxy {
74    registry: registry::Registry,
75    dns_watcher: dns_watcher::DnsServerWatcher,
76    socket_provider: socket_provider::SocketProvider,
77}
78
79impl SocketProxy {
80    fn new(
81        forwarder_tx: mpsc::Sender<crate::registry::NetworkRegistryRequest>,
82    ) -> Result<Self, anyhow::Error> {
83        let mark = Arc::new(Mutex::new(SocketMarks::default()));
84        // TODO(https://fxbug.dev/477682527): Remove this workaround and return the channel
85        // size to 1.
86        let (dns_tx, dns_rx) = mpsc::channel(10);
87        Ok(Self {
88            registry: registry::Registry::new(mark.clone(), dns_tx, forwarder_tx)
89                .context("while creating registry")?,
90            dns_watcher: dns_watcher::DnsServerWatcher::new(Arc::new(Mutex::new(dns_rx))),
91            socket_provider: socket_provider::SocketProvider::new(mark),
92        })
93    }
94}
95
96enum IncomingService {
97    DnsServerWatcher(fnp_socketproxy::DnsServerWatcherRequestStream),
98    FuchsiaNetworks(fnp_socketproxy::FuchsiaNetworksRequestStream),
99    StarnixNetworks(fnp_socketproxy::StarnixNetworksRequestStream),
100    PosixSocket(fidl_fuchsia_posix_socket::ProviderRequestStream),
101    PosixSocketRaw(fidl_fuchsia_posix_socket_raw::ProviderRequestStream),
102}
103
104/// Main entry point for the network socket proxy.
105pub async fn run() -> Result<(), anyhow::Error> {
106    fuchsia_inspect::component::health().set_starting_up();
107
108    let inspector = fuchsia_inspect::component::inspector();
109    let _inspect_server_task =
110        inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default());
111
112    // Use a generous buffer size without making it unbounded. If the
113    // server (netcfg) is not processing messages quickly enough,
114    // this indicates more significant system issues.
115    let (forwarder_tx, forwarder_rx) = mpsc::channel(50);
116    let mut request_forwarder = registry::RequestForwarder::new(forwarder_rx)?;
117    let proxy = Arc::new(SocketProxy::new(forwarder_tx)?.with_inspect(inspector.root(), "root")?);
118
119    let mut fs = ServiceFs::new_local();
120    let _: &mut ServiceFsDir<'_, _> = fs
121        .dir("svc")
122        .add_fidl_service(IncomingService::StarnixNetworks)
123        .add_fidl_service(IncomingService::FuchsiaNetworks)
124        .add_fidl_service(IncomingService::DnsServerWatcher)
125        .add_fidl_service(IncomingService::PosixSocket)
126        .add_fidl_service(IncomingService::PosixSocketRaw);
127
128    let _: &mut ServiceFs<_> = fs.take_and_serve_directory_handle()?;
129
130    fuchsia_inspect::component::health().set_ok();
131
132    let service_fut = fs.for_each_concurrent(100, move |service| {
133        let proxy = Arc::clone(&proxy);
134        async move {
135            match service {
136                IncomingService::StarnixNetworks(stream) => {
137                    proxy.registry.run_starnix(stream).await
138                }
139                IncomingService::FuchsiaNetworks(stream) => {
140                    proxy.registry.run_fuchsia(stream).await
141                }
142                IncomingService::DnsServerWatcher(stream) => proxy.dns_watcher.run(stream).await,
143                IncomingService::PosixSocket(stream) => proxy.socket_provider.run(stream).await,
144                IncomingService::PosixSocketRaw(stream) => {
145                    proxy.socket_provider.run_raw(stream).await
146                }
147            }
148            .unwrap_or_else(|e| error!("{e:?}"))
149        }
150    });
151
152    let scope = fasync::Scope::new();
153
154    let _ = scope.spawn_local(async move {
155        let res = request_forwarder.run().await;
156        info!("RequestForwarder future has terminated: {res:?}");
157    });
158
159    let _ = scope.spawn_local(async move {
160        service_fut.await;
161        error!("The main services future has terminated. It should never terminate");
162        // Abort the scope to signal that the main service loop has unexpectedly ended.
163        fasync::Scope::current().abort().await;
164    });
165
166    scope.join().await;
167
168    Ok(())
169}