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 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
// Copyright 2015-2018 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Configuration module for the server binary, `named`.
pub mod dnssec;
use std::fs::File;
use std::io::Read;
use std::net::{AddrParseError, Ipv4Addr, Ipv6Addr};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::Duration;
use cfg_if::cfg_if;
use serde::{self, Deserialize};
use toml;
use crate::proto::error::ProtoResult;
use crate::proto::rr::Name;
use crate::authority::ZoneType;
use crate::error::{ConfigError, ConfigResult};
use crate::store::StoreConfig;
static DEFAULT_PATH: &str = "/var/named"; // TODO what about windows (do I care? ;)
static DEFAULT_PORT: u16 = 53;
static DEFAULT_TLS_PORT: u16 = 853;
static DEFAULT_HTTPS_PORT: u16 = 443;
static DEFAULT_QUIC_PORT: u16 = 853; // https://www.ietf.org/archive/id/draft-ietf-dprive-dnsoquic-11.html#name-reservation-of-dedicated-po
static DEFAULT_TCP_REQUEST_TIMEOUT: u64 = 5;
/// Server configuration
#[derive(Deserialize, Debug)]
pub struct Config {
/// The list of IPv4 addresses to listen on
#[serde(default)]
listen_addrs_ipv4: Vec<String>,
/// This list of IPv6 addresses to listen on
#[serde(default)]
listen_addrs_ipv6: Vec<String>,
/// Port on which to listen (associated to all IPs)
listen_port: Option<u16>,
/// Secure port to listen on
tls_listen_port: Option<u16>,
/// HTTPS port to listen on
https_listen_port: Option<u16>,
/// QUIC port to listen on
quic_listen_port: Option<u16>,
/// Timeout associated to a request before it is closed.
tcp_request_timeout: Option<u64>,
/// Level at which to log, default is INFO
log_level: Option<String>,
/// Base configuration directory, i.e. root path for zones
directory: Option<String>,
/// List of configurations for zones
#[serde(default)]
zones: Vec<ZoneConfig>,
/// Certificate to associate to TLS connections (currently the same is used for HTTPS and TLS)
#[cfg(feature = "dnssec")]
tls_cert: Option<dnssec::TlsCertConfig>,
}
impl Config {
/// read a Config file from the file specified at path.
pub fn read_config(path: &Path) -> ConfigResult<Self> {
let mut file: File = File::open(path)?;
let mut toml: String = String::new();
file.read_to_string(&mut toml)?;
toml.parse().map_err(Into::into)
}
/// set of listening ipv4 addresses (for TCP and UDP)
pub fn get_listen_addrs_ipv4(&self) -> Result<Vec<Ipv4Addr>, AddrParseError> {
self.listen_addrs_ipv4.iter().map(|s| s.parse()).collect()
}
/// set of listening ipv6 addresses (for TCP and UDP)
pub fn get_listen_addrs_ipv6(&self) -> Result<Vec<Ipv6Addr>, AddrParseError> {
self.listen_addrs_ipv6.iter().map(|s| s.parse()).collect()
}
/// port on which to listen for connections on specified addresses
pub fn get_listen_port(&self) -> u16 {
self.listen_port.unwrap_or(DEFAULT_PORT)
}
/// port on which to listen for TLS connections
pub fn get_tls_listen_port(&self) -> u16 {
self.tls_listen_port.unwrap_or(DEFAULT_TLS_PORT)
}
/// port on which to listen for HTTPS connections
pub fn get_https_listen_port(&self) -> u16 {
self.https_listen_port.unwrap_or(DEFAULT_HTTPS_PORT)
}
/// port on which to listen for QUIC connections
pub fn get_quic_listen_port(&self) -> u16 {
self.quic_listen_port.unwrap_or(DEFAULT_QUIC_PORT)
}
/// default timeout for all TCP connections before forcibly shutdown
pub fn get_tcp_request_timeout(&self) -> Duration {
Duration::from_secs(
self.tcp_request_timeout
.unwrap_or(DEFAULT_TCP_REQUEST_TIMEOUT),
)
}
/// specify the log level which should be used, ["Trace", "Debug", "Info", "Warn", "Error"]
pub fn get_log_level(&self) -> tracing::Level {
if let Some(ref level_str) = self.log_level {
tracing::Level::from_str(level_str).unwrap_or(tracing::Level::INFO)
} else {
tracing::Level::INFO
}
}
/// the path for all zone configurations, defaults to `/var/named`
pub fn get_directory(&self) -> &Path {
self.directory
.as_ref()
.map_or(Path::new(DEFAULT_PATH), Path::new)
}
/// the set of zones which should be loaded
pub fn get_zones(&self) -> &[ZoneConfig] {
&self.zones
}
/// the tls certificate to use for accepting tls connections
pub fn get_tls_cert(&self) -> Option<&dnssec::TlsCertConfig> {
cfg_if! {
if #[cfg(feature = "dnssec")] {
self.tls_cert.as_ref()
} else {
None
}
}
}
}
impl FromStr for Config {
type Err = ConfigError;
fn from_str(toml: &str) -> ConfigResult<Self> {
toml::de::from_str(toml).map_err(Into::into)
}
}
/// Configuration for a zone
#[derive(Deserialize, PartialEq, Eq, Debug)]
pub struct ZoneConfig {
/// name of the zone
pub zone: String, // TODO: make Domain::Name decodable
/// type of the zone
pub zone_type: ZoneType,
/// location of the file (short for StoreConfig::FileConfig{zone_file_path})
pub file: Option<String>,
/// Deprecated allow_update, this is a Store option
pub allow_update: Option<bool>,
/// Allow AXFR (TODO: need auth)
pub allow_axfr: Option<bool>,
/// Enable DnsSec TODO: should this move to StoreConfig?
pub enable_dnssec: Option<bool>,
/// Keys for use by the zone
#[serde(default)]
pub keys: Vec<dnssec::KeyConfig>,
/// Store configurations, TODO: allow chained Stores
#[serde(default)]
pub stores: Option<StoreConfig>,
}
impl ZoneConfig {
/// Return a new zone configuration
///
/// # Arguments
///
/// * `zone` - name of a zone, e.g. example.com
/// * `zone_type` - Type of zone, e.g. Primary, Secondary, etc.
/// * `file` - relative to Config base path, to the zone file
/// * `allow_update` - enable dynamic updates
/// * `allow_axfr` - enable AXFR transfers
/// * `enable_dnssec` - enable signing of the zone for DNSSEC
/// * `keys` - list of private and public keys used to sign a zone
pub fn new(
zone: String,
zone_type: ZoneType,
file: String,
allow_update: Option<bool>,
allow_axfr: Option<bool>,
enable_dnssec: Option<bool>,
keys: Vec<dnssec::KeyConfig>,
) -> Self {
Self {
zone,
zone_type,
file: Some(file),
allow_update,
allow_axfr,
enable_dnssec,
keys,
stores: None,
}
}
// TODO this is a little ugly for the parse, b/c there is no terminal char
/// returns the name of the Zone, i.e. the `example.com` of `www.example.com.`
pub fn get_zone(&self) -> ProtoResult<Name> {
Name::parse(&self.zone, Some(&Name::new()))
}
/// the type of the zone
pub fn get_zone_type(&self) -> ZoneType {
self.zone_type
}
/// path to the zone file, i.e. the base set of original records in the zone
///
/// this is ony used on first load, if dynamic update is enabled for the zone, then the journal
/// file is the actual source of truth for the zone.
pub fn get_file(&self) -> PathBuf {
// TODO: Option on PathBuf
PathBuf::from(self.file.as_ref().expect("file was none"))
}
/// enable dynamic updates for the zone (see SIG0 and the registered keys)
pub fn is_update_allowed(&self) -> bool {
self.allow_update.unwrap_or(false)
}
/// enable AXFR transfers
pub fn is_axfr_allowed(&self) -> bool {
self.allow_axfr.unwrap_or(false)
}
/// declare that this zone should be signed, see keys for configuration of the keys for signing
pub fn is_dnssec_enabled(&self) -> bool {
cfg_if! {
if #[cfg(feature = "dnssec")] {
self.enable_dnssec.unwrap_or(false)
} else {
false
}
}
}
/// the configuration for the keys used for auth and/or dnssec zone signing.
#[cfg(feature = "dnssec")]
#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
pub fn get_keys(&self) -> &[dnssec::KeyConfig] {
&self.keys
}
}