1use crate::{Error, ReadOptions};
3use std::net::{Ipv4Addr, SocketAddrV4, ToSocketAddrs, UdpSocket};
4
5pub fn send(target: impl ToSocketAddrs, message: &[u8]) -> Result<(), Error> {
7 bind()?
8 .send_to(message, target)
9 .map_err(Error::SendFailed)?;
10 Ok(())
11}
12
13pub fn send_and_receive(
15 target: impl ToSocketAddrs,
16 message: &[u8],
17 options: ReadOptions,
18) -> Result<Vec<u8>, Error> {
19 let socket = bind()?;
20
21 socket.send_to(message, target).map_err(Error::SendFailed)?;
23 socket
24 .set_read_timeout(Some(options.timeout()))
25 .map_err(|_| Error::TimeoutReached)?;
26
27 let mut buffer = vec![0; options.buffer_size()];
29 let (bytes_read, _) = socket
30 .recv_from(&mut buffer)
31 .map_err(Error::ReceiveFailed)?;
32 let response = buffer[..bytes_read].to_vec();
33 Ok(response)
34}
35
36fn bind() -> Result<UdpSocket, Error> {
37 let address = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
38 UdpSocket::bind(address).map_err(Error::BindFailed)
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use crate::ReadOptions;
45 use anyhow::Result;
46 use std::time::Duration;
47
48 #[test]
49 #[cfg_attr(feature = "ci", ignore)]
50 fn test_send() {
51 let res = send("quake.se:28501", b"\xff\xff\xff\xffstatus");
52 assert!(res.is_ok());
53 }
54
55 #[test]
56 #[cfg_attr(feature = "ci", ignore)]
57 fn test_send_and_receive() -> Result<()> {
58 let response = send_and_receive(
59 "quake.se:28501",
60 b"\xff\xff\xff\xffstatus",
61 ReadOptions::new(Duration::from_secs_f32(0.2), 32 * 1024),
62 )?;
63 assert!(String::from_utf8_lossy(&response).contains("QUAKE.SE KTX"));
64 Ok(())
65 }
66}