tiberius/sql_browser/
tokio.rs

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