innernet_shared/
interface_config.rs1use crate::{chmod, ensure_dirs_exist, Endpoint, Error, IoErrorContext, WrappedIoError};
2use indoc::writedoc;
3use ipnet::IpNet;
4use serde::{Deserialize, Serialize};
5use std::{
6 fs::{File, OpenOptions},
7 io::{self, Write},
8 net::SocketAddr,
9 path::{Path, PathBuf},
10};
11use wireguard_control::InterfaceName;
12
13#[derive(Clone, Deserialize, Serialize, Debug)]
14#[serde(rename_all = "kebab-case")]
15pub struct InterfaceConfig {
16 pub interface: InterfaceInfo,
18
19 pub server: ServerInfo,
21}
22
23#[derive(Clone, Deserialize, Serialize, Debug)]
24#[serde(rename_all = "kebab-case")]
25pub struct InterfaceInfo {
26 pub network_name: String,
28
29 pub address: IpNet,
32
33 pub private_key: String,
35
36 pub listen_port: Option<u16>,
38}
39
40#[derive(Clone, Deserialize, Serialize, Debug)]
41#[serde(rename_all = "kebab-case")]
42pub struct ServerInfo {
43 pub public_key: String,
45
46 pub external_endpoint: Endpoint,
48
49 pub internal_endpoint: SocketAddr,
51}
52
53impl InterfaceConfig {
54 pub fn write_to(
55 &self,
56 target_file: &mut File,
57 comments: bool,
58 mode: Option<u32>,
59 ) -> Result<(), io::Error> {
60 if let Some(val) = mode {
61 chmod(target_file, val)?;
62 }
63
64 if comments {
65 writedoc!(
66 target_file,
67 r"
68 # This is an invitation file to an innernet network.
69 #
70 # To join, you must install innernet.
71 # See https://github.com/tonarino/innernet for instructions.
72 #
73 # If you have innernet, just run:
74 #
75 # innernet install <this file>
76 #
77 # Don't edit the contents below unless you love chaos and dysfunction.
78 "
79 )?;
80 }
81 target_file.write_all(toml::to_string(self).unwrap().as_bytes())?;
82 Ok(())
83 }
84
85 pub fn write_to_path<P: AsRef<Path>>(
86 &self,
87 path: P,
88 comments: bool,
89 mode: Option<u32>,
90 ) -> Result<(), WrappedIoError> {
91 let path = path.as_ref();
92 let mut target_file = OpenOptions::new()
93 .create_new(true)
94 .write(true)
95 .open(path)
96 .with_path(path)?;
97 self.write_to(&mut target_file, comments, mode)
98 .with_path(path)
99 }
100
101 pub fn write_to_interface(
103 &self,
104 config_dir: &Path,
105 interface: &InterfaceName,
106 ) -> Result<PathBuf, Error> {
107 let path = Self::build_config_file_path(config_dir, interface)?;
108 File::create(&path)
109 .with_path(&path)?
110 .write_all(toml::to_string(self).unwrap().as_bytes())?;
111 Ok(path)
112 }
113
114 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
115 Ok(toml::from_str(
116 &std::fs::read_to_string(&path).with_path(path)?,
117 )?)
118 }
119
120 pub fn from_interface(config_dir: &Path, interface: &InterfaceName) -> Result<Self, Error> {
121 let path = Self::build_config_file_path(config_dir, interface)?;
122 crate::warn_on_dangerous_mode(&path).with_path(&path)?;
123 Self::from_file(path)
124 }
125
126 pub fn get_path(config_dir: &Path, interface: &InterfaceName) -> PathBuf {
127 config_dir
128 .join(interface.to_string())
129 .with_extension("conf")
130 }
131
132 fn build_config_file_path(
133 config_dir: &Path,
134 interface: &InterfaceName,
135 ) -> Result<PathBuf, WrappedIoError> {
136 ensure_dirs_exist(&[config_dir])?;
137 Ok(Self::get_path(config_dir, interface))
138 }
139}
140
141impl InterfaceInfo {
142 pub fn public_key(&self) -> Result<String, Error> {
143 Ok(wireguard_control::Key::from_base64(&self.private_key)?
144 .get_public()
145 .to_base64())
146 }
147}