Skip to main content

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