mssql_browser/
browse_instance.rs

1use super::error::*;
2use super::info::*;
3use super::socket::{UdpSocket, UdpSocketFactory};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
5
6/// The CLNT_UCAST_INST packet is a request for information related to a specific instance.
7const CLNT_UCAST_INST: u8 = 0x04;
8
9/// The server responds to all client requests with an SVR_RESP.
10const SVR_RESP: u8 = 0x05;
11
12/// Gets information about the given instance.
13///
14/// # Arguments
15/// * `remote_addr` - The address of the remote host on which the instance is running.
16/// * `instance_name` - The name of the instance, must be less than `MAX_INSTANCE_NAME_LEN` characters.
17#[cfg(any(feature = "tokio", feature = "async-std"))]
18pub async fn browse_instance(
19    remote_addr: IpAddr,
20    instance_name: &str,
21) -> Result<
22    InstanceInfo,
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_instance_inner(remote_addr, instance_name, &mut factory).await
30}
31
32/// Gets information about the given instance.
33///
34/// # Arguments
35/// * `remote_addr` - The address of the remote host on which the instance is running.
36/// * `instance_name` - The name of the instance, must be less than `MAX_INSTANCE_NAME_LEN` characters.
37pub async fn browse_instance_inner<SF: UdpSocketFactory>(
38    remote_addr: IpAddr,
39    instance_name: &str,
40    socket_factory: &mut SF,
41) -> Result<InstanceInfo, BrowserError<SF::Error, <SF::Socket as UdpSocket>::Error>> {
42    if instance_name.len() > super::MAX_INSTANCE_NAME_LEN {
43        return Err(BrowserError::InstanceNameTooLong);
44    }
45
46    let local_addr = if remote_addr.is_ipv4() {
47        IpAddr::V4(Ipv4Addr::UNSPECIFIED)
48    } else {
49        IpAddr::V6(Ipv6Addr::UNSPECIFIED)
50    };
51
52    let bind_to = SocketAddr::new(local_addr, 0);
53    let mut socket = socket_factory
54        .bind(&bind_to)
55        .await
56        .map_err(BrowserError::BindFailed)?;
57
58    let remote = SocketAddr::new(remote_addr, 1434);
59    socket
60        .connect(&remote)
61        .await
62        .map_err(|e| BrowserError::ConnectFailed(remote, e))?;
63
64    let mut buffer = [0u8; 1 + super::MAX_INSTANCE_NAME_LEN + 1];
65    buffer[0] = CLNT_UCAST_INST;
66    buffer[1..(1 + instance_name.len())].copy_from_slice(instance_name.as_bytes()); // TODO: Encode as mbcs string
67    let buffer_len = 2 + instance_name.len();
68    socket
69        .send_to(&buffer[0..buffer_len], &remote)
70        .await
71        .map_err(|e| BrowserError::SendFailed(remote, e))?;
72
73    let mut buffer = [0u8; 3 + 1024];
74
75    let bytes_received = socket
76        .recv(&mut buffer)
77        .await
78        .map_err(BrowserError::ReceiveFailed)?;
79
80    if bytes_received < 1 {
81        return Err(BrowserError::ProtocolError(
82            BrowserProtocolError::UnexpectedToken {
83                expected: BrowserProtocolToken::MessageIdentifier(SVR_RESP),
84                found: BrowserProtocolToken::EndOfMessage,
85            },
86        ));
87    }
88
89    if buffer[0] != SVR_RESP {
90        return Err(BrowserError::ProtocolError(
91            BrowserProtocolError::UnexpectedToken {
92                expected: BrowserProtocolToken::MessageIdentifier(SVR_RESP),
93                found: BrowserProtocolToken::MessageIdentifier(buffer[0]),
94            },
95        ));
96    }
97
98    if bytes_received < 3 {
99        return Err(BrowserError::ProtocolError(
100            BrowserProtocolError::UnexpectedToken {
101                expected: BrowserProtocolToken::MessageLength,
102                found: BrowserProtocolToken::EndOfMessage,
103            },
104        ));
105    }
106
107    let resp_data_len = u16::from_le_bytes([buffer[1], buffer[2]]);
108    if resp_data_len as usize != bytes_received - 3 {
109        return Err(BrowserError::ProtocolError(
110            BrowserProtocolError::LengthMismatch {
111                datagram: bytes_received,
112                header: (resp_data_len + 3) as usize,
113            },
114        ));
115    }
116
117    // TODO: Decode mbcs string
118    let as_str = std::str::from_utf8(&buffer[3..bytes_received]).unwrap();
119    let (instance, consumed) =
120        parse_instance_info(remote_addr, &as_str).map_err(|e| BrowserError::ProtocolError(e))?;
121
122    if consumed != as_str.len() {
123        return Err(BrowserError::ProtocolError(
124            BrowserProtocolError::ExtraneousData(Vec::from(&buffer[(3 + consumed)..])),
125        ));
126    }
127
128    Ok(instance)
129}