tiberius/client/
config.rs1mod ado_net;
2mod jdbc;
3
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7use super::AuthMethod;
8use crate::EncryptionLevel;
9use ado_net::*;
10use jdbc::*;
11
12#[derive(Clone, Debug)]
13pub struct Config {
26 pub(crate) host: Option<String>,
27 pub(crate) port: Option<u16>,
28 pub(crate) database: Option<String>,
29 pub(crate) instance_name: Option<String>,
30 pub(crate) application_name: Option<String>,
31 pub(crate) packet_size: Option<u32>,
32 pub(crate) encryption: EncryptionLevel,
33 pub(crate) trust: TrustConfig,
34 pub(crate) auth: AuthMethod,
35 pub(crate) readonly: bool,
36}
37
38#[derive(Clone, Debug)]
39pub(crate) enum TrustConfig {
40 #[allow(dead_code)]
41 CaCertificateLocation(PathBuf),
42 TrustAll,
43 Default,
44}
45
46impl Default for Config {
47 fn default() -> Self {
48 Self {
49 host: None,
50 port: None,
51 database: None,
52 instance_name: None,
53 application_name: None,
54 packet_size: None,
55 #[cfg(any(
56 feature = "rustls",
57 feature = "native-tls",
58 feature = "vendored-openssl"
59 ))]
60 encryption: EncryptionLevel::Required,
61 #[cfg(not(any(
62 feature = "rustls",
63 feature = "native-tls",
64 feature = "vendored-openssl"
65 )))]
66 encryption: EncryptionLevel::NotSupported,
67 trust: TrustConfig::Default,
68 auth: AuthMethod::None,
69 readonly: false,
70 }
71 }
72}
73
74impl Config {
75 pub fn new() -> Self {
77 Self::default()
78 }
79
80 pub fn host(&mut self, host: impl ToString) {
84 self.host = Some(host.to_string());
85 }
86
87 pub fn port(&mut self, port: u16) {
91 self.port = Some(port);
92 }
93
94 pub fn database(&mut self, database: impl ToString) {
98 self.database = Some(database.to_string())
99 }
100
101 pub fn instance_name(&mut self, name: impl ToString) {
109 self.instance_name = Some(name.to_string());
110 }
111
112 pub fn application_name(&mut self, name: impl ToString) {
117 self.application_name = Some(name.to_string());
118 }
119
120 pub fn packet_size(&mut self, packet_size: u32) {
127 self.packet_size = Some(packet_size);
128 }
129
130 pub fn encryption(&mut self, encryption: EncryptionLevel) {
135 self.encryption = encryption;
136 }
137
138 pub fn trust_cert(&mut self) {
149 if let TrustConfig::CaCertificateLocation(_) = &self.trust {
150 panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.")
151 }
152 self.trust = TrustConfig::TrustAll;
153 }
154
155 pub fn trust_cert_ca(&mut self, path: impl ToString) {
165 if let TrustConfig::TrustAll = &self.trust {
166 panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.")
167 } else {
168 self.trust = TrustConfig::CaCertificateLocation(PathBuf::from(path.to_string()))
169 }
170 }
171
172 pub fn authentication(&mut self, auth: AuthMethod) {
176 self.auth = auth;
177 }
178
179 pub fn readonly(&mut self, readnoly: bool) {
183 self.readonly = readnoly;
184 }
185
186 pub(crate) fn get_host(&self) -> &str {
187 self.host
188 .as_deref()
189 .filter(|v| v != &".")
190 .unwrap_or("localhost")
191 }
192
193 pub(crate) fn get_port(&self) -> u16 {
194 match (self.port, self.instance_name.as_ref()) {
195 (Some(port), _) => port,
197 (None, Some(_)) => 1434,
200 (None, None) => 1433,
202 }
203 }
204
205 pub fn get_addr(&self) -> String {
207 format!("{}:{}", self.get_host(), self.get_port())
208 }
209
210 pub fn from_ado_string(s: &str) -> crate::Result<Self> {
231 let ado: AdoNetConfig = s.parse()?;
232 Self::from_config_string(ado)
233 }
234
235 pub fn from_jdbc_string(s: &str) -> crate::Result<Self> {
242 let jdbc: JdbcConfig = s.parse()?;
243 Self::from_config_string(jdbc)
244 }
245
246 fn from_config_string(s: impl ConfigString) -> crate::Result<Self> {
247 let mut builder = Self::new();
248
249 let server = s.server()?;
250
251 if let Some(host) = server.host {
252 builder.host(host);
253 }
254
255 if let Some(port) = server.port {
256 builder.port(port);
257 }
258
259 if let Some(instance) = server.instance {
260 builder.instance_name(instance);
261 }
262
263 builder.authentication(s.authentication()?);
264
265 if let Some(database) = s.database() {
266 builder.database(database);
267 }
268
269 if let Some(name) = s.application_name() {
270 builder.application_name(name);
271 }
272
273 if let Some(packet_size) = s.packet_size()? {
274 builder.packet_size(packet_size);
275 }
276
277 if s.trust_cert()? {
278 builder.trust_cert();
279 }
280
281 if let Some(ca) = s.trust_cert_ca() {
282 builder.trust_cert_ca(ca);
283 }
284
285 builder.encryption(s.encrypt()?);
286
287 builder.readonly(s.readonly());
288
289 Ok(builder)
290 }
291}
292
293pub(crate) struct ServerDefinition {
294 host: Option<String>,
295 port: Option<u16>,
296 instance: Option<String>,
297}
298
299pub(crate) trait ConfigString {
300 fn dict(&self) -> &HashMap<String, String>;
301
302 fn server(&self) -> crate::Result<ServerDefinition>;
303
304 fn authentication(&self) -> crate::Result<AuthMethod> {
305 let user = self
306 .dict()
307 .get("uid")
308 .or_else(|| self.dict().get("username"))
309 .or_else(|| self.dict().get("user"))
310 .or_else(|| self.dict().get("user id"))
311 .map(|s| s.as_str());
312
313 let pw = self
314 .dict()
315 .get("password")
316 .or_else(|| self.dict().get("pwd"))
317 .map(|s| s.as_str());
318
319 match self
320 .dict()
321 .get("integratedsecurity")
322 .or_else(|| self.dict().get("integrated security"))
323 {
324 #[cfg(all(windows, feature = "winauth"))]
325 Some(val) if val.to_lowercase() == "sspi" || Self::parse_bool(val)? => match (user, pw)
326 {
327 (None, None) => Ok(AuthMethod::Integrated),
328 _ => Ok(AuthMethod::windows(user.unwrap_or(""), pw.unwrap_or(""))),
329 },
330 #[cfg(feature = "integrated-auth-gssapi")]
331 Some(val) if val.to_lowercase() == "sspi" || Self::parse_bool(val)? => {
332 Ok(AuthMethod::Integrated)
333 }
334 _ => Ok(AuthMethod::sql_server(user.unwrap_or(""), pw.unwrap_or(""))),
335 }
336 }
337
338 fn database(&self) -> Option<String> {
339 self.dict()
340 .get("database")
341 .or_else(|| self.dict().get("initial catalog"))
342 .or_else(|| self.dict().get("databasename"))
343 .map(|db| db.to_string())
344 }
345
346 fn application_name(&self) -> Option<String> {
347 self.dict()
348 .get("application name")
349 .or_else(|| self.dict().get("applicationname"))
350 .map(|name| name.to_string())
351 }
352
353 fn packet_size(&self) -> crate::Result<Option<u32>> {
354 self.dict()
355 .get("packet size")
356 .or_else(|| self.dict().get("packetsize"))
357 .or_else(|| self.dict().get("packet_size"))
358 .map(|value| value.parse())
359 .transpose()
360 .map_err(Into::into)
361 }
362
363 fn trust_cert(&self) -> crate::Result<bool> {
364 self.dict()
365 .get("trustservercertificate")
366 .map(Self::parse_bool)
367 .unwrap_or(Ok(false))
368 }
369
370 fn trust_cert_ca(&self) -> Option<String> {
371 self.dict()
372 .get("trustservercertificateca")
373 .map(|ca| ca.to_string())
374 }
375
376 #[cfg(any(
377 feature = "rustls",
378 feature = "native-tls",
379 feature = "vendored-openssl"
380 ))]
381 fn encrypt(&self) -> crate::Result<EncryptionLevel> {
382 self.dict()
383 .get("encrypt")
384 .map(|val| match Self::parse_bool(val) {
385 Ok(true) => Ok(EncryptionLevel::Required),
386 Ok(false) => Ok(EncryptionLevel::Off),
387 Err(_) if val == "DANGER_PLAINTEXT" => Ok(EncryptionLevel::NotSupported),
388 Err(e) => Err(e),
389 })
390 .unwrap_or(Ok(EncryptionLevel::Off))
391 }
392
393 #[cfg(not(any(
394 feature = "rustls",
395 feature = "native-tls",
396 feature = "vendored-openssl"
397 )))]
398 fn encrypt(&self) -> crate::Result<EncryptionLevel> {
399 Ok(EncryptionLevel::NotSupported)
400 }
401
402 fn parse_bool<T: AsRef<str>>(v: T) -> crate::Result<bool> {
403 match v.as_ref().trim().to_lowercase().as_str() {
404 "true" | "yes" => Ok(true),
405 "false" | "no" => Ok(false),
406 _ => Err(crate::Error::Conversion(
407 "Connection string: Not a valid boolean".into(),
408 )),
409 }
410 }
411
412 fn readonly(&self) -> bool {
413 self.dict()
414 .get("applicationintent")
415 .filter(|val| *val == "ReadOnly")
416 .is_some()
417 }
418}