mssql_browser/
browse_host.rs

1use super::error::*;
2use super::info::*;
3use super::socket::{UdpSocket, UdpSocketFactory};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
5
6/// The CLNT_UCAST_EX packet is a unicast request that is generated by clients that are trying to identify
7/// the list of database instances and their network protocol connection information installed on a single machine.
8const CLNT_UCAST_EX: u8 = 0x03;
9
10/// The server responds to all client requests with an SVR_RESP.
11const SVR_RESP: u8 = 0x05;
12
13/// Discovers any SQL Server instances running on the given host
14///
15/// # Arguments
16/// * `remote_addr` - The address of the remote host of which to retrieve information
17///                   about the instances running on it.
18#[cfg(any(feature = "tokio", feature = "async-std"))]
19pub async fn browse_host(
20    remote_addr: IpAddr,
21) -> Result<
22    InstanceIterator,
23    BrowserError<
24        <super::socket::DefaultSocketFactory as UdpSocketFactory>::Error,
25        <<super::socket::DefaultSocketFactory as UdpSocketFactory>::Socket as UdpSocket>::Error,
26    >,
27> {
28    let mut factory = super::socket::DefaultSocketFactory::new();
29    browse_host_inner(remote_addr, &mut factory).await
30}
31
32/// Discovers any SQL Server instances running on the given host
33///
34/// # Arguments
35/// * `remote_addr` - The address of the remote host of which to retrieve information
36///                   about the instances running on it.
37pub async fn browse_host_inner<SF: UdpSocketFactory>(
38    remote_addr: IpAddr,
39    socket_factory: &mut SF,
40) -> Result<InstanceIterator, BrowserError<SF::Error, <SF::Socket as UdpSocket>::Error>> {
41    let local_addr = if remote_addr.is_ipv4() {
42        IpAddr::V4(Ipv4Addr::UNSPECIFIED)
43    } else {
44        IpAddr::V6(Ipv6Addr::UNSPECIFIED)
45    };
46
47    let bind_to = SocketAddr::new(local_addr, 0);
48    let mut socket = socket_factory
49        .bind(&bind_to)
50        .await
51        .map_err(BrowserError::BindFailed)?;
52
53    let remote = SocketAddr::new(remote_addr, 1434);
54    socket
55        .connect(&remote)
56        .await
57        .map_err(|e| BrowserError::ConnectFailed(remote, e))?;
58
59    let buffer = [CLNT_UCAST_EX];
60    socket
61        .send_to(&buffer, &remote)
62        .await
63        .map_err(|e| BrowserError::SendFailed(remote, e))?;
64
65    let mut buffer = Vec::with_capacity(65535 + 3);
66
67    buffer.resize_with(buffer.capacity(), Default::default);
68
69    let bytes_received = socket
70        .recv(&mut buffer)
71        .await
72        .map_err(BrowserError::ReceiveFailed)?;
73
74    if bytes_received < 1 {
75        return Err(BrowserError::ProtocolError(
76            BrowserProtocolError::UnexpectedToken {
77                expected: BrowserProtocolToken::MessageIdentifier(SVR_RESP),
78                found: BrowserProtocolToken::EndOfMessage,
79            },
80        ));
81    }
82
83    if buffer[0] != SVR_RESP {
84        return Err(BrowserError::ProtocolError(
85            BrowserProtocolError::UnexpectedToken {
86                expected: BrowserProtocolToken::MessageIdentifier(SVR_RESP),
87                found: BrowserProtocolToken::MessageIdentifier(buffer[0]),
88            },
89        ));
90    }
91
92    if bytes_received < 3 {
93        return Err(BrowserError::ProtocolError(
94            BrowserProtocolError::UnexpectedToken {
95                expected: BrowserProtocolToken::MessageLength,
96                found: BrowserProtocolToken::EndOfMessage,
97            },
98        ));
99    }
100
101    let resp_data_len = u16::from_le_bytes([buffer[1], buffer[2]]);
102    if resp_data_len as usize != bytes_received - 3 {
103        return Err(BrowserError::ProtocolError(
104            BrowserProtocolError::LengthMismatch {
105                datagram: bytes_received,
106                header: (resp_data_len + 3) as usize,
107            },
108        ));
109    }
110
111    buffer.truncate(bytes_received);
112
113    // Validate that the buffer is valid utf-8
114    // TODO: Decode mbcs string
115    std::str::from_utf8(&buffer[3..])
116        .map_err(|e| BrowserError::ProtocolError(BrowserProtocolError::InvalidUtf8(e)))?;
117
118    Ok(InstanceIterator {
119        remote_addr,
120        buffer,
121        offset: 3,
122    })
123}
124
125/// Iterates over the instances returned by `browse_host`
126pub struct InstanceIterator {
127    remote_addr: IpAddr,
128    buffer: Vec<u8>,
129    offset: usize,
130}
131
132impl InstanceIterator {
133    /// Gets the next received instance information. You can call this method multiple
134    /// times to receive information about multiple instances until it returns Ok(None).
135    pub fn next(
136        &mut self,
137    ) -> Result<
138        Option<InstanceInfo>,
139        BrowserError<std::convert::Infallible, std::convert::Infallible>,
140    > {
141        if self.offset == self.buffer.len() {
142            return Ok(None);
143        }
144
145        // UNSAFE: Buffer is already validated to be valid utf-8 when the iterator was created
146        let as_str = unsafe { std::str::from_utf8_unchecked(&self.buffer[self.offset..]) };
147        let (instance, consumed) = parse_instance_info(self.remote_addr, as_str)
148            .map_err(|e| BrowserError::ProtocolError(e))?;
149
150        self.offset += consumed;
151        Ok(Some(instance))
152    }
153}