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
9use fidl_fuchsia_net as fnet;
10use fidl_fuchsia_posix_socket::{self as fposix_socket, OptionalUint32};
11use fuchsia_async as fasync;
12use fuchsia_component::server::{ServiceFs, ServiceFsDir};
13use fuchsia_inspect::health::Reporter;
14use fuchsia_inspect_derive::{IValue, Inspect, Unit, WithInspect as _};
15use futures::StreamExt as _;
16use futures::lock::Mutex;
17use log::error;
18use std::sync::Arc;
19
20mod mark_watcher;
21mod socket_provider;
22
23#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24struct SocketMarks {
25    mark_1: OptionalUint32,
26    mark_2: OptionalUint32,
27}
28
29impl From<fnet::Marks> for SocketMarks {
30    fn from(fnet::Marks { mark_1, mark_2, __source_breaking }: fnet::Marks) -> Self {
31        let into_optional_uint32 = |opt| match opt {
32            Some(val) => OptionalUint32::Value(val),
33            None => OptionalUint32::Unset(fposix_socket::Empty),
34        };
35        Self { mark_1: into_optional_uint32(mark_1), mark_2: into_optional_uint32(mark_2) }
36    }
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
72impl Unit for SocketMarks {
73    type Data = fuchsia_inspect::Node;
74
75    fn inspect_create(&self, parent: &fuchsia_inspect::Node, name: impl AsRef<str>) -> Self::Data {
76        let mut node = parent.create_child(name.as_ref());
77        self.inspect_update(&mut node);
78        node
79    }
80
81    fn inspect_update(&self, data: &mut Self::Data) {
82        data.atomic_update(|node| {
83            node.clear_recorded();
84            let fnet::Marks { mark_1, mark_2, __source_breaking } = fnet::Marks::from(*self);
85            if let Some(mark_1) = mark_1 {
86                node.record_uint("mark_1", mark_1.into());
87            }
88            if let Some(mark_2) = mark_2 {
89                node.record_uint("mark_2", mark_2.into());
90            }
91            node.record_bool("has_mark", self.has_value());
92        });
93    }
94}
95
96impl std::fmt::Display for SocketMarks {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        match self.mark_1 {
99            fposix_socket::OptionalUint32::Value(v) => write!(f, "{v}"),
100            fposix_socket::OptionalUint32::Unset(fposix_socket::Empty) => write!(f, "None"),
101        }
102    }
103}
104
105#[derive(Inspect)]
106struct SocketProxy {
107    marks: Arc<Mutex<IValue<SocketMarks>>>,
108    socket_provider: socket_provider::SocketProvider,
109}
110
111impl SocketProxy {
112    fn new() -> Self {
113        let marks = Arc::new(Mutex::new(IValue::new(SocketMarks::default())));
114        Self { marks: marks.clone(), socket_provider: socket_provider::SocketProvider::new(marks) }
115    }
116
117    async fn set_marks(&self, marks: SocketMarks) {
118        self.marks.lock().await.iset(marks);
119    }
120}
121
122enum IncomingService {
123    PosixSocket(fidl_fuchsia_posix_socket::ProviderRequestStream),
124    PosixSocketRaw(fidl_fuchsia_posix_socket_raw::ProviderRequestStream),
125}
126
127/// Main entry point for the network socket proxy.
128pub async fn run() -> Result<(), anyhow::Error> {
129    fuchsia_inspect::component::health().set_starting_up();
130
131    let inspector = fuchsia_inspect::component::inspector();
132    let _inspect_server_task =
133        inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default());
134
135    let proxy = Arc::new(SocketProxy::new().with_inspect(inspector.root(), "root")?);
136
137    let mut fs = ServiceFs::new_local();
138    let _: &mut ServiceFsDir<'_, _> = fs
139        .dir("svc")
140        .add_fidl_service(IncomingService::PosixSocket)
141        .add_fidl_service(IncomingService::PosixSocketRaw);
142
143    let _: &mut ServiceFs<_> = fs.take_and_serve_directory_handle()?;
144
145    fuchsia_inspect::component::health().set_ok();
146
147    let proxy_for_service = Arc::clone(&proxy);
148    let service_fut = fs.for_each_concurrent(100, move |service| {
149        let proxy = Arc::clone(&proxy_for_service);
150        async move {
151            match service {
152                IncomingService::PosixSocket(stream) => proxy.socket_provider.run(stream).await,
153                IncomingService::PosixSocketRaw(stream) => {
154                    proxy.socket_provider.run_raw(stream).await
155                }
156            }
157            .unwrap_or_else(|e| error!("{e:?}"))
158        }
159    });
160
161    let scope = fasync::Scope::new();
162
163    let proxy_clone = Arc::clone(&proxy);
164    let _ = scope.spawn_local(async move {
165        mark_watcher::watch_properties(proxy_clone).await;
166    });
167
168    let _ = scope.spawn_local(async move {
169        service_fut.await;
170        error!("The main services future has terminated. It should never terminate");
171        // Abort the scope to signal that the main service loop has unexpectedly ended.
172        fasync::Scope::current().abort().await;
173    });
174
175    scope.join().await;
176
177    Ok(())
178}
179#[cfg(test)]
180mod test {
181    use super::*;
182    use diagnostics_assertions::assert_data_tree;
183
184    const TEST_MARK_1: u32 = 42;
185    const TEST_MARK_2: u32 = 99;
186
187    #[fuchsia::test]
188    async fn test_socket_proxy_set_marks() {
189        let inspector = fuchsia_inspect::Inspector::default();
190        let proxy = SocketProxy::new().with_inspect(inspector.root(), "root").expect("attach");
191
192        assert_data_tree!(inspector, root: contains {
193            marks: {
194                has_mark: false,
195            },
196        });
197
198        let marks = SocketMarks {
199            mark_1: OptionalUint32::Value(TEST_MARK_1),
200            mark_2: OptionalUint32::Unset(fposix_socket::Empty),
201        };
202        proxy.set_marks(marks).await;
203        assert_eq!(**proxy.marks.lock().await, marks);
204        assert_data_tree!(inspector, root: contains {
205            marks: {
206                mark_1: u64::from(TEST_MARK_1),
207                has_mark: true,
208            },
209        });
210
211        let both_marks = SocketMarks {
212            mark_1: OptionalUint32::Value(TEST_MARK_1),
213            mark_2: OptionalUint32::Value(TEST_MARK_2),
214        };
215        proxy.set_marks(both_marks).await;
216        assert_eq!(**proxy.marks.lock().await, both_marks);
217        assert_data_tree!(inspector, root: contains {
218            marks: {
219                mark_1: u64::from(TEST_MARK_1),
220                mark_2: u64::from(TEST_MARK_2),
221                has_mark: true,
222            },
223        });
224
225        proxy.set_marks(SocketMarks::default()).await;
226        assert_eq!(**proxy.marks.lock().await, SocketMarks::default());
227        assert_data_tree!(inspector, root: contains {
228            marks: {
229                has_mark: false,
230            },
231        });
232    }
233}