nullnet_libappguard/
lib.rs1mod proto;
2
3use crate::proto::appguard_commands::{ClientMessage, FirewallPolicy, ServerMessage};
4use proto::appguard::app_guard_client::AppGuardClient;
5pub use proto::appguard::{
6 AppGuardHttpRequest, AppGuardHttpResponse, AppGuardResponse, AppGuardSmtpRequest,
7 AppGuardSmtpResponse, AppGuardTcpConnection, AppGuardTcpInfo, AppGuardTcpResponse, Log, Logs,
8};
9use std::future::Future;
10use tokio::sync::mpsc;
11pub use tonic::Streaming;
12use tonic::codegen::tokio_stream::wrappers::ReceiverStream;
13use tonic::transport::{Channel, ClientTlsConfig};
14use tonic::{Request, Response, Status};
15
16#[derive(Clone)]
17pub struct AppGuardGrpcInterface {
18 client: AppGuardClient<Channel>,
19}
20
21impl AppGuardGrpcInterface {
22 #[allow(clippy::missing_errors_doc)]
23 pub async fn new(host: &str, port: u16, tls: bool) -> Result<Self, String> {
24 let protocol = if tls { "https" } else { "http" };
25
26 let mut endpoint = Channel::from_shared(format!("{protocol}://{host}:{port}"))
27 .map_err(|e| e.to_string())?
28 .connect_timeout(std::time::Duration::from_secs(10));
29
30 if tls {
31 endpoint = endpoint
32 .tls_config(ClientTlsConfig::new().with_native_roots())
33 .map_err(|e| e.to_string())?;
34 }
35
36 let channel = endpoint.connect().await.map_err(|e| e.to_string())?;
37
38 Ok(Self {
39 client: AppGuardClient::new(channel),
40 })
41 }
42
43 #[allow(clippy::missing_errors_doc)]
44 pub async fn control_channel(
45 &self,
46 receiver: mpsc::Receiver<ClientMessage>,
47 ) -> Result<Streaming<ServerMessage>, String> {
48 let receiver = ReceiverStream::new(receiver);
49
50 Ok(self
51 .client
52 .clone()
53 .control_channel(Request::new(receiver))
54 .await
55 .map_err(|e| e.to_string())?
56 .into_inner())
57 }
58
59 #[allow(clippy::missing_errors_doc)]
60 pub async fn handle_logs(&mut self, message: Logs) -> Result<(), String> {
61 self.client
62 .handle_logs(Request::new(message))
63 .await
64 .map(|_| ())
65 .map_err(|e| e.to_string())
66 }
67
68 #[allow(clippy::missing_errors_doc)]
69 pub async fn handle_tcp_connection(
70 &mut self,
71 timeout: Option<u64>,
72 tcp_connection: AppGuardTcpConnection,
73 ) -> Result<AppGuardTcpResponse, Status> {
74 self.client
75 .handle_tcp_connection(Request::new(tcp_connection.clone()))
76 .wait_until_timeout(
77 timeout,
78 AppGuardTcpResponse {
79 tcp_info: Some(AppGuardTcpInfo {
80 connection: Some(tcp_connection),
81 ..Default::default()
82 }),
83 },
84 )
85 .await
86 }
87
88 #[allow(clippy::missing_errors_doc)]
89 pub async fn handle_http_request(
90 &mut self,
91 timeout: Option<u64>,
92 default_policy: FirewallPolicy,
93 http_request: AppGuardHttpRequest,
94 ) -> Result<AppGuardResponse, Status> {
95 self.client
96 .handle_http_request(Request::new(http_request))
97 .wait_until_timeout(
98 timeout,
99 AppGuardResponse {
100 policy: default_policy as i32,
101 },
102 )
103 .await
104 }
105
106 #[allow(clippy::missing_errors_doc)]
107 pub async fn handle_http_response(
108 &mut self,
109 timeout: Option<u64>,
110 default_policy: FirewallPolicy,
111 http_response: AppGuardHttpResponse,
112 ) -> Result<AppGuardResponse, Status> {
113 self.client
114 .handle_http_response(Request::new(http_response))
115 .wait_until_timeout(
116 timeout,
117 AppGuardResponse {
118 policy: default_policy as i32,
119 },
120 )
121 .await
122 }
123
124 #[allow(clippy::missing_errors_doc)]
125 pub async fn handle_smtp_request(
126 &mut self,
127 timeout: Option<u64>,
128 default_policy: FirewallPolicy,
129 smtp_request: AppGuardSmtpRequest,
130 ) -> Result<AppGuardResponse, Status> {
131 self.client
132 .handle_smtp_request(Request::new(smtp_request))
133 .wait_until_timeout(
134 timeout,
135 AppGuardResponse {
136 policy: default_policy as i32,
137 },
138 )
139 .await
140 }
141
142 #[allow(clippy::missing_errors_doc)]
143 pub async fn handle_smtp_response(
144 &mut self,
145 timeout: Option<u64>,
146 default_policy: FirewallPolicy,
147 smtp_response: AppGuardSmtpResponse,
148 ) -> Result<AppGuardResponse, Status> {
149 self.client
150 .handle_smtp_response(Request::new(smtp_response))
151 .wait_until_timeout(
152 timeout,
153 AppGuardResponse {
154 policy: default_policy as i32,
155 },
156 )
157 .await
158 }
159}
160
161trait WaitUntilTimeout<T> {
162 async fn wait_until_timeout(self, timeout: Option<u64>, default: T) -> Result<T, Status>;
163}
164
165impl<T, F: Future<Output = Result<Response<T>, Status>>> WaitUntilTimeout<T> for F {
166 async fn wait_until_timeout(self, timeout: Option<u64>, default: T) -> Result<T, Status> {
167 if let Some(t) = timeout {
168 if let Ok(res) = tokio::time::timeout(std::time::Duration::from_millis(t), self).await {
169 res.map(Response::into_inner)
170 } else {
171 Ok(default)
173 }
174 } else {
175 self.await.map(Response::into_inner)
176 }
177 }
178}