tiberius/client/
config.rs
1mod 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) encryption: EncryptionLevel,
32 pub(crate) trust: TrustConfig,
33 pub(crate) auth: AuthMethod,
34 pub(crate) readonly: bool,
35}
36
37#[derive(Clone, Debug)]
38pub(crate) enum TrustConfig {
39 #[allow(dead_code)]
40 CaCertificateLocation(PathBuf),
41 TrustAll,
42 Default,
43}
44
45impl Default for Config {
46 fn default() -> Self {
47 Self {
48 host: None,
49 port: None,
50 database: None,
51 instance_name: None,
52 application_name: None,
53 #[cfg(any(
54 feature = "rustls",
55 feature = "native-tls",
56 feature = "vendored-openssl"
57 ))]
58 encryption: EncryptionLevel::Required,
59 #[cfg(not(any(
60 feature = "rustls",
61 feature = "native-tls",
62 feature = "vendored-openssl"
63 )))]
64 encryption: EncryptionLevel::NotSupported,
65 trust: TrustConfig::Default,
66 auth: AuthMethod::None,
67 readonly: false,
68 }
69 }
70}
71
72impl Config {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn host(&mut self, host: impl ToString) {
82 self.host = Some(host.to_string());
83 }
84
85 pub fn port(&mut self, port: u16) {
89 self.port = Some(port);
90 }
91
92 pub fn database(&mut self, database: impl ToString) {
96 self.database = Some(database.to_string())
97 }
98
99 pub fn instance_name(&mut self, name: impl ToString) {
107 self.instance_name = Some(name.to_string());
108 }
109
110 pub fn application_name(&mut self, name: impl ToString) {
115 self.application_name = Some(name.to_string());
116 }
117
118 pub fn encryption(&mut self, encryption: EncryptionLevel) {
123 self.encryption = encryption;
124 }
125
126 pub fn trust_cert(&mut self) {
137 if let TrustConfig::CaCertificateLocation(_) = &self.trust {
138 panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.")
139 }
140 self.trust = TrustConfig::TrustAll;
141 }
142
143 pub fn trust_cert_ca(&mut self, path: impl ToString) {
153 if let TrustConfig::TrustAll = &self.trust {
154 panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.")
155 } else {
156 self.trust = TrustConfig::CaCertificateLocation(PathBuf::from(path.to_string()))
157 }
158 }
159
160 pub fn authentication(&mut self, auth: AuthMethod) {
164 self.auth = auth;
165 }
166
167 pub fn readonly(&mut self, readnoly: bool) {
171 self.readonly = readnoly;
172 }
173
174 pub(crate) fn get_host(&self) -> &str {
175 self.host
176 .as_deref()
177 .filter(|v| v != &".")
178 .unwrap_or("localhost")
179 }
180
181 pub(crate) fn get_port(&self) -> u16 {
182 match (self.port, self.instance_name.as_ref()) {
183 (Some(port), _) => port,
185 (None, Some(_)) => 1434,
188 (None, None) => 1433,
190 }
191 }
192
193 pub fn get_addr(&self) -> String {
195 format!("{}:{}", self.get_host(), self.get_port())
196 }
197
198 pub fn from_ado_string(s: &str) -> crate::Result<Self> {
218 let ado: AdoNetConfig = s.parse()?;
219 Self::from_config_string(ado)
220 }
221
222 pub fn from_jdbc_string(s: &str) -> crate::Result<Self> {
229 let jdbc: JdbcConfig = s.parse()?;
230 Self::from_config_string(jdbc)
231 }
232
233 fn from_config_string(s: impl ConfigString) -> crate::Result<Self> {
234 let mut builder = Self::new();
235
236 let server = s.server()?;
237
238 if let Some(host) = server.host {
239 builder.host(host);
240 }
241
242 if let Some(port) = server.port {
243 builder.port(port);
244 }
245
246 if let Some(instance) = server.instance {
247 builder.instance_name(instance);
248 }
249
250 builder.authentication(s.authentication()?);
251
252 if let Some(database) = s.database() {
253 builder.database(database);
254 }
255
256 if let Some(name) = s.application_name() {
257 builder.application_name(name);
258 }
259
260 if s.trust_cert()? {
261 builder.trust_cert();
262 }
263
264 if let Some(ca) = s.trust_cert_ca() {
265 builder.trust_cert_ca(ca);
266 }
267
268 builder.encryption(s.encrypt()?);
269
270 builder.readonly(s.readonly());
271
272 Ok(builder)
273 }
274}
275
276pub(crate) struct ServerDefinition {
277 host: Option<String>,
278 port: Option<u16>,
279 instance: Option<String>,
280}
281
282pub(crate) trait ConfigString {
283 fn dict(&self) -> &HashMap<String, String>;
284
285 fn server(&self) -> crate::Result<ServerDefinition>;
286
287 fn authentication(&self) -> crate::Result<AuthMethod> {
288 let user = self
289 .dict()
290 .get("uid")
291 .or_else(|| self.dict().get("username"))
292 .or_else(|| self.dict().get("user"))
293 .or_else(|| self.dict().get("user id"))
294 .map(|s| s.as_str());
295
296 let pw = self
297 .dict()
298 .get("password")
299 .or_else(|| self.dict().get("pwd"))
300 .map(|s| s.as_str());
301
302 match self
303 .dict()
304 .get("integratedsecurity")
305 .or_else(|| self.dict().get("integrated security"))
306 {
307 #[cfg(all(windows, feature = "winauth"))]
308 Some(val) if val.to_lowercase() == "sspi" || Self::parse_bool(val)? => match (user, pw)
309 {
310 (None, None) => Ok(AuthMethod::Integrated),
311 _ => Ok(AuthMethod::windows(user.unwrap_or(""), pw.unwrap_or(""))),
312 },
313 #[cfg(feature = "integrated-auth-gssapi")]
314 Some(val) if val.to_lowercase() == "sspi" || Self::parse_bool(val)? => {
315 Ok(AuthMethod::Integrated)
316 }
317 _ => Ok(AuthMethod::sql_server(user.unwrap_or(""), pw.unwrap_or(""))),
318 }
319 }
320
321 fn database(&self) -> Option<String> {
322 self.dict()
323 .get("database")
324 .or_else(|| self.dict().get("initial catalog"))
325 .or_else(|| self.dict().get("databasename"))
326 .map(|db| db.to_string())
327 }
328
329 fn application_name(&self) -> Option<String> {
330 self.dict()
331 .get("application name")
332 .or_else(|| self.dict().get("applicationname"))
333 .map(|name| name.to_string())
334 }
335
336 fn trust_cert(&self) -> crate::Result<bool> {
337 self.dict()
338 .get("trustservercertificate")
339 .map(Self::parse_bool)
340 .unwrap_or(Ok(false))
341 }
342
343 fn trust_cert_ca(&self) -> Option<String> {
344 self.dict()
345 .get("trustservercertificateca")
346 .map(|ca| ca.to_string())
347 }
348
349 #[cfg(any(
350 feature = "rustls",
351 feature = "native-tls",
352 feature = "vendored-openssl"
353 ))]
354 fn encrypt(&self) -> crate::Result<EncryptionLevel> {
355 self.dict()
356 .get("encrypt")
357 .map(|val| match Self::parse_bool(val) {
358 Ok(true) => Ok(EncryptionLevel::Required),
359 Ok(false) => Ok(EncryptionLevel::Off),
360 Err(_) if val == "DANGER_PLAINTEXT" => Ok(EncryptionLevel::NotSupported),
361 Err(e) => Err(e),
362 })
363 .unwrap_or(Ok(EncryptionLevel::Off))
364 }
365
366 #[cfg(not(any(
367 feature = "rustls",
368 feature = "native-tls",
369 feature = "vendored-openssl"
370 )))]
371 fn encrypt(&self) -> crate::Result<EncryptionLevel> {
372 Ok(EncryptionLevel::NotSupported)
373 }
374
375 fn parse_bool<T: AsRef<str>>(v: T) -> crate::Result<bool> {
376 match v.as_ref().trim().to_lowercase().as_str() {
377 "true" | "yes" => Ok(true),
378 "false" | "no" => Ok(false),
379 _ => Err(crate::Error::Conversion(
380 "Connection string: Not a valid boolean".into(),
381 )),
382 }
383 }
384
385 fn readonly(&self) -> bool {
386 self.dict()
387 .get("applicationintent")
388 .filter(|val| *val == "ReadOnly")
389 .is_some()
390 }
391}