toe_beans/v4/server/config/mod.rs
1mod lease_time;
2
3use super::RESOLVED_SERVER_PORT;
4use crate::v4::MessageOptions;
5use ip_network::Ipv4Network;
6pub use lease_time::LeaseTime;
7use log::{info, warn};
8use mac_address::MacAddress;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::fs::{OpenOptions, read_to_string};
12use std::io::Write;
13use std::net::{Ipv4Addr, SocketAddrV4};
14use std::os::unix::fs::OpenOptionsExt;
15use std::path::PathBuf;
16use toml;
17
18/// Representation of a `toe-beans.toml` file. Used by the [Server](crate::v4::Server).
19///
20/// This is exported for you to use in your application to generate a `toe-beans.toml`.
21#[derive(Serialize, Deserialize, Debug)]
22pub struct Config {
23 /// The path to a directory where this config will exist on disk.
24 #[serde(skip)]
25 pub path: PathBuf,
26 /// See: [LeaseTime](lease_time::LeaseTime)
27 pub lease_time: LeaseTime,
28 /// Controls whether the server agrees to use [Rapid Commit](crate::v4::MessageOptions::RapidCommit).
29 pub rapid_commit: bool,
30 /// A range of IP addresses for the server to lease. Specified in CIDR notation. For example: `10.1.9.32/16`
31 pub network_cidr: Ipv4Network,
32 /// The server will bind to this address and port.
33 pub listen_address: SocketAddrV4,
34 /// The name of the network interface to broadcast server responses to.
35 /// Currently only takes one interface.
36 pub interface: Option<String>,
37 /// May or may not match the IP address in `listen_address`.
38 /// For example, you might be listening to `0.0.0.0`, but your server has another address it can be reached from.
39 /// This is used in both the `siaddr` field and `ServerIdentifier` option.
40 ///
41 /// If `None`, then `listen_address` is used.
42 pub server_address: Option<Ipv4Addr>,
43 /// DHCP option numbers (as a String) mapped to values that are used to respond to parameter request list options
44 /// Please note that this field, in particular, is subject to change.
45 pub parameters: HashMap<String, MessageOptions>,
46 /// This is passed to `Leases`'s `static_leases` field.
47 pub static_leases: HashMap<MacAddress, Ipv4Addr>,
48 /// Whether to read and write leases to a file to maintain state between server restarts.
49 /// Defaults to `true` (when not in a benchmark or integration test) but you might want to set to `false` for performance, lack of storage, etc.
50 pub use_leases_file: bool,
51}
52
53impl Default for Config {
54 fn default() -> Self {
55 Self {
56 path: PathBuf::new(), // empty, the current directory
57 lease_time: LeaseTime::new(86_400).unwrap(), // seconds in a day
58 rapid_commit: true,
59 network_cidr: Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 0), 16).unwrap(), // 2^(32-16) addresses
60 listen_address: SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), RESOLVED_SERVER_PORT),
61 interface: None,
62 server_address: None,
63 parameters: HashMap::with_capacity(u8::MAX as usize),
64 static_leases: HashMap::new(),
65 use_leases_file: cfg!(all(
66 not(feature = "benchmark"),
67 not(feature = "integration")
68 )),
69 }
70 }
71}
72
73impl Config {
74 /// The file name that the Config is read from and written to.
75 pub const FILE_NAME: &'static str = "toe-beans.toml";
76
77 /// Reads the `toe-beans.toml` file and deserializes it into a Config.
78 /// Returns default options if there are any issues.
79 pub fn read(from_where: PathBuf) -> Self {
80 let read_result = read_to_string(from_where.join(Self::FILE_NAME));
81 if let Ok(config_string) = read_result {
82 let toml_result = toml::from_str(&config_string);
83 match toml_result {
84 Ok(config) => {
85 return Self {
86 path: from_where, // from_where might be different if passed through cli args
87 ..config
88 };
89 }
90 Err(error) => {
91 warn!("{error}");
92 warn!(
93 "Warning: invalid {}. Using default values.",
94 Self::FILE_NAME
95 );
96 }
97 }
98 } else {
99 warn!(
100 "Warning: can't read {}. Using default values.",
101 Self::FILE_NAME
102 );
103 }
104
105 Self {
106 path: from_where,
107 ..Config::default()
108 }
109 }
110
111 /// Serializes a Config and writes it to a `toe-beans.toml` file.
112 pub fn write(&self) {
113 info!("Writing {}", Self::FILE_NAME);
114
115 let config_content = toml::to_string_pretty(self).expect("Failed to generate toml data");
116 let mode = 0o600;
117
118 let mut file = OpenOptions::new()
119 .read(false)
120 .write(true)
121 .create(true)
122 .truncate(true)
123 .mode(mode) // ensure that file permissions are set before creating file
124 .open(self.path.join(Self::FILE_NAME))
125 .unwrap_or_else(|_| panic!("Failed to open config {} for writing", Self::FILE_NAME));
126
127 file.write_all(config_content.as_bytes())
128 .unwrap_or_else(|_| panic!("Failed to write config to {}", Self::FILE_NAME));
129 }
130}