1mod osquery_binding;
6use osquery_binding::{ExtensionResponse, TExtensionManagerSyncClient};
7use thrift;
8
9#[cfg(target_os = "windows")]
10use named_pipe::PipeClient;
11#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
12use std::os::unix::net::UnixStream;
13use std::{
14 io::Result,
15 ops::Drop,
16 process::{Child, Command, Stdio},
17 time::Duration,
18};
19use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol};
20
21pub struct OSQuery {
36 _socket: String,
37 _socket_cleanup: bool,
38 _timeout: u64,
39 osquery_instance: Option<Child>,
40}
41
42impl OSQuery {
43 pub fn new() -> Self {
44 Self {
45 #[cfg(target_os = "windows")]
46 _socket: String::from(r"\\.\pipe\osquery-rs"),
47 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
48 _socket: String::from("/tmp/osquery-rs"),
49 _socket_cleanup: false,
50 _timeout: 10,
51 osquery_instance: Option::None,
52 }
53 }
54
55 pub fn set_socket(mut self, path: &str) -> Self {
57 self._socket = String::from(path);
58 self
59 }
60
61 pub fn set_timeout(mut self, timeout: u64) -> Self {
63 self._timeout = timeout;
64 self
65 }
66
67 pub fn get_timeout(&self) -> u64 {
69 self._timeout.clone()
70 }
71
72 pub fn get_socket(&self) -> String {
74 self._socket.clone()
75 }
76
77 pub fn spawn_instance(mut self, path: &str) -> Result<Self> {
94 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
95 {
96 let osquery_instance = Command::new(path)
97 .args([
98 "--extensions_socket",
99 &self._socket,
100 "--disable_database",
101 "--disable_watchdog",
102 "--disable_logging",
103 "--ephemeral",
104 "--config_path",
105 "/dev/null",
106 ])
107 .stdout(Stdio::null())
108 .stderr(Stdio::null())
109 .spawn()?;
110
111 loop {
113 match UnixStream::connect(&self._socket) {
114 Ok(_) => break,
115 Err(_) => continue,
116 };
117 }
118 self.osquery_instance = Some(osquery_instance);
119 }
120
121 #[cfg(target_os = "windows")]
122 {
123 println!("{:#?}", &self._socket);
124 println!("{:#?}", path);
125 let osquery_instance = Command::new(path)
126 .arg("--extensions_socket")
127 .arg(&self._socket)
128 .arg("--disable_database")
129 .arg("--disable_watchdog")
130 .arg("--disable_logging")
131 .arg("--ephemeral")
132 .arg("--config_path")
133 .arg("/dev/null")
134 .stdout(Stdio::null())
135 .stderr(Stdio::null())
136 .spawn()?;
137
138 loop {
140 match PipeClient::connect(&self._socket) {
141 Ok(_) => break,
142 Err(_) => continue,
143 };
144 }
145 self.osquery_instance = Some(osquery_instance);
146 }
147 self._socket_cleanup = true;
148 Ok(self)
149 }
150
151 pub fn query(&self, sql: String) -> Result<ExtensionResponse> {
153 #[cfg(target_os = "windows")]
154 let (reader, writer) = {
155 let mut reader = PipeClient::connect(&self._socket)?;
156 reader.set_read_timeout(Some(Duration::new(self._timeout, 0)));
157 reader.set_write_timeout(Some(Duration::new(self._timeout, 0)));
158 let mut writer = PipeClient::connect(&self._socket)?;
159 writer.set_read_timeout(Some(Duration::new(self._timeout, 0)));
160 writer.set_write_timeout(Some(Duration::new(self._timeout, 0)));
161 (reader, writer)
162 };
163 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
164 let (reader, writer) = {
165 let reader = UnixStream::connect(&self._socket)?;
166 reader.set_read_timeout(Some(Duration::new(self._timeout, 0)))?;
167 reader.set_write_timeout(Some(Duration::new(self._timeout, 0)))?;
168 let writer = reader.try_clone()?;
169 (reader, writer)
170 };
171 let input_protocol = TBinaryInputProtocol::new(reader, false);
172 let output_protocol = TBinaryOutputProtocol::new(writer, false);
173 let mut extention_manager_client =
174 osquery_binding::ExtensionManagerSyncClient::new(input_protocol, output_protocol);
175 extention_manager_client.query(sql.clone()).map_err(|e| {
176 std::io::Error::new(
177 std::io::ErrorKind::InvalidInput,
178 format!("Unable to execute the query '{}', ERROR: {}", sql, e),
179 )
180 })
181 }
182}
183
184impl Drop for OSQuery {
185 fn drop(&mut self) {
186 match &mut self.osquery_instance {
187 Some(p) => {
188 p.kill()
189 .expect(&format!("Unable to kill child process {}", p.id()));
190 }
191 None => {}
192 }
193 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
194 {
195 if self._socket_cleanup {
196 std::fs::remove_file(&self._socket)
197 .expect(&format!("Unable to remove socket '{}'", &self._socket));
198 }
199 }
200 }
201}