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 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 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}