1use bytes::BytesMut;
5use rand::Rng;
6
7use crate::reply::Reply;
8use crate::request::{increment_attempt, Request, RequestBody};
9
10use std::ffi::OsStr;
11use std::fs::Permissions;
12use std::net::Ipv6Addr;
13use std::ops::{Deref, DerefMut};
14use std::time::Duration;
15
16#[cfg(unix)]
17use std::os::unix::ffi::OsStrExt;
18#[cfg(unix)]
19use std::os::unix::fs::PermissionsExt;
20#[cfg(unix)]
21use std::os::unix::net::UnixDatagram;
22
23pub const DEFAULT_PORT: u16 = 323;
24
25#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct ClientOptions {
28 pub timeout: Duration,
30 pub n_tries: u16,
32}
33
34impl Default for ClientOptions {
35 fn default() -> Self {
36 Self {
37 n_tries: 3,
38 timeout: Duration::from_secs(1),
39 }
40 }
41}
42
43trait DgramSocket {
45 fn send(&self, buf: &[u8]) -> std::io::Result<usize>;
46 fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize>;
47}
48
49impl DgramSocket for std::net::UdpSocket {
50 fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
51 std::net::UdpSocket::send(self, buf)
52 }
53
54 fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
55 std::net::UdpSocket::recv(self, buf)
56 }
57}
58
59#[cfg(unix)]
60impl DgramSocket for UnixDatagram {
61 fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
62 UnixDatagram::send(self, buf)
63 }
64
65 fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
66 UnixDatagram::recv(self, buf)
67 }
68}
69
70#[cfg(unix)]
71#[derive(Debug)]
72struct UnixDatagramClient(UnixDatagram);
73
74#[cfg(unix)]
75impl AsRef<UnixDatagram> for UnixDatagramClient {
76 fn as_ref(&self) -> &UnixDatagram {
77 &self.0
78 }
79}
80
81#[cfg(unix)]
82impl AsMut<UnixDatagram> for UnixDatagramClient {
83 fn as_mut(&mut self) -> &mut UnixDatagram {
84 &mut self.0
85 }
86}
87
88#[cfg(unix)]
89impl Deref for UnixDatagramClient {
90 type Target = UnixDatagram;
91 fn deref(&self) -> &UnixDatagram {
92 &self.0
93 }
94}
95
96#[cfg(unix)]
97impl DerefMut for UnixDatagramClient {
98 fn deref_mut(&mut self) -> &mut UnixDatagram {
99 &mut self.0
100 }
101}
102
103#[cfg(unix)]
104impl Drop for UnixDatagramClient {
105 fn drop(&mut self) {
106 if let Ok(addr) = self.0.local_addr() {
107 if let Some(path) = addr.as_pathname() {
108 let _ = self.0.shutdown(std::net::Shutdown::Both);
109 let _ = std::fs::remove_file(path);
110 }
111 }
112 }
113}
114
115#[cfg(unix)]
116impl UnixDatagramClient {
117 fn new() -> std::io::Result<UnixDatagramClient> {
118 let id: [u8; 16] = rand::random();
119 let mut path = b"/var/run/chrony/client-000102030405060708090a0b0c0d0e0f.sock".clone();
120 hex::encode_to_slice(id, &mut path[23..55]).unwrap();
121 let path_str = OsStr::from_bytes(&path);
122 let sock = UnixDatagram::bind(path_str)?;
123 let client = UnixDatagramClient(sock);
124 std::fs::set_permissions(path_str, Permissions::from_mode(0o777))?;
125 client.connect("/var/run/chrony/chronyd.sock")?;
126 Ok(client)
127 }
128}
129
130fn blocking_query_loop<Sock: DgramSocket>(
131 sock: &Sock,
132 request_body: RequestBody,
133 options: ClientOptions,
134) -> std::io::Result<Reply> {
135 let request = Request {
136 sequence: rand::thread_rng().gen(),
137 attempt: 0,
138 body: request_body,
139 };
140
141 let mut send_buf = BytesMut::with_capacity(request.length());
142 request.serialize(&mut send_buf);
143 let mut recv_buf = [0; 1500];
144 let mut attempt = 0;
145
146 loop {
147 sock.send(send_buf.as_ref())?;
148 match sock.recv(&mut recv_buf) {
149 Ok(len) => {
150 let mut msg = &recv_buf[0..len];
151 match Reply::deserialize(&mut msg) {
152 Ok(reply) => {
153 if reply.sequence == request.sequence {
154 return Ok(reply);
155 } else {
156 return Err(std::io::Error::new(
157 std::io::ErrorKind::InvalidData,
158 "Bad sequence number",
159 ));
160 }
161 }
162 Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e)),
163 }
164 }
165 Err(e) => {
166 if e.kind() == std::io::ErrorKind::TimedOut
167 || e.kind() == std::io::ErrorKind::WouldBlock
168 {
169 attempt += 1;
170 if attempt == options.n_tries {
171 return Err(e);
172 }
173 increment_attempt(send_buf.as_mut());
174 } else {
175 return Err(e);
176 }
177 }
178 }
179 }
180}
181
182pub fn blocking_query<Server: std::net::ToSocketAddrs>(
184 request_body: RequestBody,
185 options: ClientOptions,
186 server: &Server,
187) -> std::io::Result<Reply> {
188 let sock = std::net::UdpSocket::bind((Ipv6Addr::UNSPECIFIED, 0))?;
189 sock.connect(server)?;
190 sock.set_read_timeout(Some(options.timeout))?;
191
192 blocking_query_loop(&sock, request_body, options)
193}
194
195#[cfg(unix)]
197pub fn blocking_query_uds(
198 request_body: RequestBody,
199 options: ClientOptions,
200) -> std::io::Result<Reply> {
201 let sock = UnixDatagramClient::new()?;
202 sock.set_read_timeout(Some(options.timeout))?;
203 blocking_query_loop(sock.as_ref(), request_body, options)
204}