1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Module to provide means to transport Kerberos messages
//!

mod channel_trait;
pub use channel_trait::KrbChannel;

mod tcp_channel;
use tcp_channel::TcpChannel;

mod udp_channel;
use udp_channel::UdpChannel;

use crate::Result;
use std::collections::HashMap;
use std::net::{IpAddr, SocketAddr};
use trust_dns_resolver::config::{NameServerConfig, Protocol, ResolverConfig, ResolverOpts};
use trust_dns_resolver::Resolver;

/// Transport protocols available to send Kerberos messages
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TransportProtocol
{
	TCP,
	UDP,
}

/// Struct to package KDC's
#[derive(Debug)]
pub struct Kdcs
{
	kdcs: HashMap<String, IpAddr>,
}

impl Default for Kdcs
{
	fn default() -> Self
	{
		Self::new()
	}
}

impl Kdcs
{
	pub fn new() -> Self
	{
		Self { kdcs: HashMap::new() }
	}

	/// # Examples
	///
	/// ```
	/// let mut kdcs = Kdcs::new();
	/// let realm_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
	/// kdcs.insert("realm.com", realm_ip);
	/// ```
	pub fn insert(&mut self, realm: String, ip: IpAddr)
	{
		self.kdcs.insert(realm.to_lowercase(), ip);
	}

	pub fn get(&self, realm: &str) -> Option<&IpAddr>
	{
		self.kdcs.get(&realm.to_lowercase())
	}

	pub fn ips(&self) -> Vec<&IpAddr>
	{
		self.kdcs.values().collect()
	}

	pub fn get_clone(&self, realm: &str) -> Option<IpAddr>
	{
		self.get(realm).copied()
	}
}

/// Struct to package the KDC's and the protocol to communicate with them
pub struct KdcComm
{
	kdcs: Kdcs,
	protocol: TransportProtocol,
}

impl KdcComm
{
	/// Create a new KdcComm struct
	///
	/// # Examples
	/// ```
	/// let mut kdcs = Kdcs::new();
	/// let kdc_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
	/// kdcs.insert("DOMAIN.COM".to_string(), kdc_ip);
	///
	/// let kdccomm = KdcComm::new(kdcs, TransportProtocol::TCP);
	/// ```
	pub fn new(kdcs: Kdcs, protocol: TransportProtocol) -> Self
	{
		Self { kdcs, protocol }
	}

	/// Create a KrbChannel for the KDC specified by it's realm
	///
	/// # Examples
	///
	/// ```
	/// let mut kdcs = Kdcs::new();
	/// let kdc_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
	/// kdcs.insert("DOMAIN.COM".to_string(), kdc_ip);
	///
	/// let kdccomm = KdcComm::new(kdcs, TransportProtocol::TCP);
	/// let channel = kdccomm.create_channel("DOMAIN.COM")?;
	/// ```
	pub fn create_channel(&mut self, realm: &str) -> Result<Box<dyn KrbChannel>>
	{
		resolve_krb_channel(realm, &mut self.kdcs, self.protocol)
	}
}

const KERBEROS_PORT: u16 = 88;
/// Generates a transporter given and address and transport protocol
pub fn new_krb_channel(dst_ip: IpAddr, transport_protocol: TransportProtocol) -> Box<dyn KrbChannel>
{
	let dst_address = SocketAddr::new(dst_ip, KERBEROS_PORT);
	match transport_protocol
	{
		TransportProtocol::TCP => Box::new(TcpChannel::new(dst_address)),
		TransportProtocol::UDP => Box::new(UdpChannel::new(dst_address)),
	}
}

pub fn resolve_krb_channel(realm: &str,
                           kdcs: &mut Kdcs,
                           channel_protocol: TransportProtocol)
                           -> Result<Box<dyn KrbChannel>>
{
	let kdc_ip = resolve_kdc_ip(realm, kdcs)?;
	kdcs.insert(realm.to_string(), kdc_ip);

	Ok(new_krb_channel(kdc_ip, channel_protocol))
}

pub fn resolve_kdc_ip(realm: &str, kdcs: &Kdcs) -> Result<IpAddr>
{
	Ok(match kdcs.get_clone(realm)
	{
		Some(ip) => ip,
		None =>
		{
			let dns_servers = kdcs.ips().iter().map(|ip| SocketAddr::new(**ip, 53)).collect();
			resolve_host(realm, dns_servers)?
		},
	})
}

pub fn resolve_host(realm: &str, dns_servers: Vec<SocketAddr>) -> Result<IpAddr>
{
	let resolver = if dns_servers.is_empty()
	{
		Resolver::from_system_conf().map_err(|err| format!("Unable to use dns system configuration: {}", err))?
	}
	else
	{
		let mut resolver_config = ResolverConfig::new();
		for server in dns_servers
		{
			resolver_config.add_name_server(NameServerConfig { socket_addr: server,
			                                                   protocol: Protocol::Tcp,
			                                                   tls_dns_name: None,
			                                                   trust_nx_responses: false });
		}
		Resolver::new(resolver_config, ResolverOpts::default()).unwrap()
	};

	let ips = resolver.lookup_ip(realm).map_err(|err| format!("Error resolving '{}' : '{}'", realm, err))?;

	let ip = ips.iter().next().ok_or(format!("Error resolving '{}': No entries found", realm))?;

	Ok(ip)
}