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