tiberius_rustls/sql_browser/
smol.rs

1use super::SqlBrowser;
2use crate::client::Config;
3use async_io::Timer;
4use async_net::{resolve, TcpStream, UdpSocket};
5use async_trait::async_trait;
6use futures_lite::FutureExt;
7use futures_util::future::TryFutureExt;
8use std::io;
9use std::time::Duration;
10use tracing::Level;
11
12#[async_trait]
13impl SqlBrowser for TcpStream {
14    /// This method can be used to connect to SQL Server named instances
15    /// when on a Windows paltform with the `sql-browser-tokio` feature
16    /// enabled. Please see the crate examples for more detailed examples.
17    async fn connect_named(builder: &Config) -> crate::Result<Self> {
18        let addrs = resolve(builder.get_addr()).await?;
19
20        for mut addr in addrs {
21            if let Some(ref instance_name) = builder.instance_name {
22                // First resolve the instance to a port via the
23                // SSRP protocol/MS-SQLR protocol [1]
24                // [1] https://msdn.microsoft.com/en-us/library/cc219703.aspx
25
26                let local_bind: std::net::SocketAddr = if addr.is_ipv4() {
27                    "0.0.0.0:0".parse().unwrap()
28                } else {
29                    "[::]:0".parse().unwrap()
30                };
31
32                tracing::event!(
33                    Level::TRACE,
34                    "Connecting to instance `{}` using SQL Browser in port `{}`",
35                    instance_name,
36                    builder.get_port()
37                );
38
39                let msg = [&[4u8], instance_name.as_bytes()].concat();
40                let mut buf = vec![0u8; 4096];
41
42                let socket = UdpSocket::bind(&local_bind).await?;
43                socket.send_to(&msg, &addr).await?;
44
45                let timeout = Duration::from_millis(1000);
46
47                let len = socket.recv(&mut buf).or(async {
48                    Timer::after(timeout).await;
49                    Err(std::io::ErrorKind::TimedOut.into())
50                })
51                    .map_err(|e| {
52                        if e.kind() == std::io::ErrorKind::TimedOut {
53                            crate::error::Error::Conversion(
54                                format!(
55                                    "SQL browser timeout during resolving instance {}. Please check if browser is running in port {} and does the instance exist.",
56                                    instance_name,
57                                    builder.get_port(),
58                                )
59                                .into(),
60                            )
61                        } else {
62                            e.into()
63                        }
64                    }).await?;
65
66                let port = super::get_port_from_sql_browser_reply(buf, len, instance_name)?;
67                tracing::event!(Level::TRACE, "Found port `{}` from SQL Browser", port);
68                addr.set_port(port);
69            };
70
71            if let Ok(stream) = TcpStream::connect(addr).await {
72                stream.set_nodelay(true)?;
73                return Ok(stream);
74            }
75        }
76
77        Err(io::Error::new(io::ErrorKind::NotFound, "Could not resolve server host").into())
78    }
79}