mssql_browser/
info.rs

1use super::error::{BrowserProtocolError, BrowserProtocolField, BrowserProtocolToken};
2use std::net::IpAddr;
3
4/// Information send in a browser protocol response
5/// See [SVR_RESP](https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/2e1560c9-5097-4023-9f5e-72b9ff1ec3b1)
6#[derive(Debug)]
7pub struct InstanceInfo {
8    /// The address of the instance
9    pub addr: IpAddr,
10
11    /// The name of the server. The SERVERNAME MUST be no greater than 255 bytes.
12    pub server_name: String,
13
14    /// A text string that represents the name of the server instance being described.
15    /// The INSTANCENAME MUST be no greater than 255 bytes but SHOULD be no greater than 16 MBCS characters.
16    pub instance_name: String,
17
18    pub is_clustered: bool,
19
20    /// A text string that conveys the version of the server instance. The VERSION_STRING MUST be no greater than 16 bytes.
21    /// VERSION_STRING MUST NOT be empty and MUST appear as follows: VERSION_STRING=1*[0-9"."]
22    pub version: String,
23
24    pub np_info: Option<NamedPipeInfo>,
25    pub tcp_info: Option<TcpInfo>,
26    pub via_info: Option<ViaInfo>,
27    pub rpc_info: Option<RpcInfo>,
28    pub spx_info: Option<SpxInfo>,
29    pub adsp_info: Option<AdspInfo>,
30    pub bv_info: Option<BvInfo>,
31}
32
33/// Information about the named pipe endpoint
34#[derive(Debug)]
35pub struct NamedPipeInfo {
36    /// A text string that represents the pipe name.
37    pub name: String,
38}
39
40/// Information about the Tcp endpoint
41#[derive(Debug)]
42pub struct TcpInfo {
43    /// A text string that represents the decimal value of the TCP port that is used to connect to the requested server instance.
44    /// TCP_PORT SHOULD be a valid TCP port as specified in \[RFC793\]
45    pub port: u16,
46}
47
48/// Information about the Virtual Interface Architecture endpoint
49#[derive(Debug)]
50pub struct ViaInfo {
51    /// A text string that MUST be no greater than 15 bytes and that represents the NetBIOS name of a machine where the server resides.
52    pub machine_name: String,
53
54    /// The VIA addresses specified
55    pub addresses: Vec<ViaAddress>,
56}
57
58/// A combination of NIC name and port.
59#[derive(Debug)]
60pub struct ViaAddress {
61    /// A text string that represents the VIA network interface card (NIC) identifier.
62    /// VIANIC SHOULD be a valid VIA Adapter NIC number \[VIA2002\].
63    pub nic: String,
64
65    /// A text string that represents the decimal value of the VIA NIC's port.
66    /// VIAPORT SHOULD be a valid VIA Adapter port number \[VIA2002\].
67    pub port: String,
68}
69
70/// Contains information about an RPC endpoint
71#[derive(Debug)]
72pub struct RpcInfo {
73    /// The name of the computer to connect to. SHOULD be no more than 127 MBCS characters.
74    pub computer_name: String,
75}
76
77/// Contains information about an SPX service endpoint
78#[derive(Debug)]
79pub struct SpxInfo {
80    /// The SPX service name of the server.
81    /// MUST NOT be greater than 1,024 bytes and SHOULD be no more than 127 MBCS characters.
82    pub service_name: String,
83}
84
85/// Contains information about an AppleTalk endpoint
86#[derive(Debug)]
87pub struct AdspInfo {
88    /// The AppleTalk service object name. SHOULD be no more than 127 MBCS characters.
89    pub object_name: String,
90}
91
92/// Contains information about an Banyan VINES endpoint
93#[derive(Debug)]
94pub struct BvInfo {
95    /// The Banyan VINES item name. SHOULD be no more than 127 MBCS characters.
96    pub item_name: String,
97
98    /// The Banyan VINES group name. SHOULD be no more than 127 MBCS characters.
99    pub group_name: String,
100
101    /// The Banyan VINES organization name. SHOULD be no more than 127 MBCS characters.
102    pub org_name: String,
103}
104
105/// Contains information about the DAC endpoint of an instance
106#[derive(Debug)]
107pub struct DacInfo {
108    pub port: u16,
109}
110
111struct SplitIteratorWithPosition<'a> {
112    inner: std::str::Split<'a, char>,
113    position: usize,
114}
115
116impl<'a> SplitIteratorWithPosition<'a> {
117    fn new(inner: std::str::Split<'a, char>) -> SplitIteratorWithPosition<'a> {
118        SplitIteratorWithPosition {
119            inner: inner,
120            position: 0,
121        }
122    }
123
124    fn string_position(&self) -> usize {
125        self.position
126    }
127}
128
129impl<'a> Iterator for SplitIteratorWithPosition<'a> {
130    type Item = &'a str;
131
132    fn next(&mut self) -> Option<&'a str> {
133        match self.inner.next() {
134            Some(x) => {
135                self.position += x.len() + 1;
136                Some(x)
137            }
138            None => None,
139        }
140    }
141}
142
143pub(crate) fn parse_instance_info(
144    addr: IpAddr,
145    string: &str,
146) -> Result<(InstanceInfo, usize), BrowserProtocolError> {
147    #[inline]
148    fn expect_next<'a, T: Iterator<Item = &'a str>>(
149        iterator: &mut T,
150        identifier: &str,
151        field: BrowserProtocolField,
152    ) -> Result<(), BrowserProtocolError> {
153        iterator
154            .next()
155            .ok_or_else(|| BrowserProtocolError::UnexpectedToken {
156                expected: BrowserProtocolToken::Identifier(field),
157                found: BrowserProtocolToken::EndOfMessage,
158            })
159            .and_then(|x| {
160                if x == identifier {
161                    Ok(())
162                } else {
163                    Err(BrowserProtocolError::UnexpectedToken {
164                        expected: BrowserProtocolToken::Identifier(field),
165                        found: BrowserProtocolToken::Literal(x.to_string()),
166                    })
167                }
168            })
169    }
170
171    fn consume_next<'a, T: Iterator<Item = &'a str>>(
172        iterator: &mut T,
173        value_name: BrowserProtocolField,
174    ) -> Result<&'a str, BrowserProtocolError> {
175        iterator
176            .next()
177            .ok_or_else(|| BrowserProtocolError::UnexpectedToken {
178                expected: BrowserProtocolToken::ValueOf(value_name),
179                found: BrowserProtocolToken::EndOfMessage,
180            })
181    }
182
183    let mut iterator = SplitIteratorWithPosition::new(string.split(';'));
184
185    // Instance information
186    expect_next(
187        &mut iterator,
188        "ServerName",
189        BrowserProtocolField::ServerName,
190    )?;
191    let server_name = consume_next(&mut iterator, BrowserProtocolField::ServerName)?;
192    expect_next(
193        &mut iterator,
194        "InstanceName",
195        BrowserProtocolField::InstanceName,
196    )?;
197    let instance_name = consume_next(&mut iterator, BrowserProtocolField::InstanceName)?;
198    expect_next(
199        &mut iterator,
200        "IsClustered",
201        BrowserProtocolField::IsClustered,
202    )?;
203    let is_clustered_str = consume_next(&mut iterator, BrowserProtocolField::IsClustered)?;
204    let is_clustered = match is_clustered_str {
205        "Yes" => true,
206        "No" => false,
207        v => {
208            return Err(BrowserProtocolError::UnexpectedToken {
209                expected: BrowserProtocolToken::ValueOf(BrowserProtocolField::IsClustered),
210                found: BrowserProtocolToken::Literal(v.to_string()),
211            })
212        }
213    };
214    expect_next(&mut iterator, "Version", BrowserProtocolField::Version)?;
215    let version = consume_next(&mut iterator, BrowserProtocolField::Version)?;
216
217    // Supported protocols
218    let mut np_info: Option<NamedPipeInfo> = None;
219    let mut tcp_info: Option<TcpInfo> = None;
220    let mut via_info: Option<ViaInfo> = None;
221    let mut rpc_info: Option<RpcInfo> = None;
222    let mut spx_info: Option<SpxInfo> = None;
223    let mut adsp_info: Option<AdspInfo> = None;
224    let mut bv_info: Option<BvInfo> = None;
225
226    loop {
227        match iterator.next() {
228            Some("np") => {
229                let pipe_name = consume_next(&mut iterator, BrowserProtocolField::NamedPipeName)?;
230                np_info = Some(NamedPipeInfo {
231                    name: pipe_name.to_owned(),
232                });
233            }
234            Some("tcp") => {
235                let port_str = consume_next(&mut iterator, BrowserProtocolField::TcpPort)?;
236                let port: u16 =
237                    port_str
238                        .parse()
239                        .map_err(|_| BrowserProtocolError::UnexpectedToken {
240                            expected: BrowserProtocolToken::TcpPort,
241                            found: BrowserProtocolToken::Literal(port_str.to_string()),
242                        })?;
243                tcp_info = Some(TcpInfo { port });
244            }
245            Some("via") => {
246                let parameters = consume_next(&mut iterator, BrowserProtocolField::ViaMachineName)?;
247                let comma_idx =
248                    parameters
249                        .find(',')
250                        .ok_or_else(|| BrowserProtocolError::UnexpectedToken {
251                            expected: BrowserProtocolToken::ViaParameters,
252                            found: BrowserProtocolToken::Literal(parameters.to_string()),
253                        })?;
254                let machine_name = &parameters[0..comma_idx];
255                let mut nic_port_parts = (&parameters[(comma_idx + 1)..]).split(&[',', ':'][..]);
256                let mut addresses = Vec::new();
257                while let Some(nic) = nic_port_parts.next() {
258                    let port = nic_port_parts.next().ok_or_else(|| {
259                        BrowserProtocolError::UnexpectedToken {
260                            expected: BrowserProtocolToken::ViaParameters,
261                            found: BrowserProtocolToken::Literal(parameters.to_string()),
262                        }
263                    })?;
264                    addresses.push(ViaAddress {
265                        nic: nic.to_owned(),
266                        port: port.to_owned(),
267                    });
268                }
269                via_info = Some(ViaInfo {
270                    machine_name: machine_name.to_owned(),
271                    addresses,
272                });
273            }
274            Some("rpc") => {
275                let computer_name =
276                    consume_next(&mut iterator, BrowserProtocolField::RpcComputerName)?;
277                rpc_info = Some(RpcInfo {
278                    computer_name: computer_name.to_owned(),
279                });
280            }
281            Some("spx") => {
282                let service_name =
283                    consume_next(&mut iterator, BrowserProtocolField::SpxServiceName)?;
284                spx_info = Some(SpxInfo {
285                    service_name: service_name.to_owned(),
286                });
287            }
288            Some("adsp") => {
289                let object_name =
290                    consume_next(&mut iterator, BrowserProtocolField::AppleTalkObjectName)?;
291                adsp_info = Some(AdspInfo {
292                    object_name: object_name.to_owned(),
293                });
294            }
295            Some("bv") => {
296                let item_name = consume_next(&mut iterator, BrowserProtocolField::BvItemName)?;
297                let group_name = consume_next(&mut iterator, BrowserProtocolField::BvGroupName)?;
298                let org_name = consume_next(&mut iterator, BrowserProtocolField::BvOrgName)?;
299                bv_info = Some(BvInfo {
300                    item_name: item_name.to_owned(),
301                    group_name: group_name.to_owned(),
302                    org_name: org_name.to_owned(),
303                });
304            }
305            Some("") => break,
306            Some(x) => {
307                return Err(BrowserProtocolError::UnexpectedToken {
308                    expected: BrowserProtocolToken::EndpointIdentifierOrSemicolon,
309                    found: BrowserProtocolToken::Literal(x.to_string()),
310                })
311            }
312            None => {
313                return Err(BrowserProtocolError::UnexpectedToken {
314                    expected: BrowserProtocolToken::EndpointIdentifierOrSemicolon,
315                    found: BrowserProtocolToken::EndOfMessage,
316                })
317            }
318        };
319    }
320
321    let consumed = iterator.string_position();
322
323    Ok((
324        InstanceInfo {
325            addr,
326            server_name: server_name.to_owned(),
327            instance_name: instance_name.to_owned(),
328            is_clustered,
329            version: version.to_owned(),
330            np_info,
331            tcp_info,
332            via_info,
333            rpc_info,
334            spx_info,
335            adsp_info,
336            bv_info,
337        },
338        consumed,
339    ))
340}