Use anyhow for error handling

This commit is contained in:
Rodolphe Bréard 2023-04-10 13:09:56 +02:00
parent 372a49262d
commit 97fae54252
6 changed files with 43 additions and 51 deletions

View file

@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0"
publish = false
[dependencies]
anyhow = { version = "1.0.70", default-features = false, features = ["std"] }
base64 = { version = "0.21.0", default-features = false, features = ["std"] }
clap = { version = "4.1.13", default-features = false, features = ["std", "derive"] }
ed25519-dalek = { version = "2.0.0-rc.2", default-features = false, features = ["fast", "rand_core", "std"] }

View file

@ -27,7 +27,7 @@ pub async fn new_action(action: Action<'_>) -> ActionResult {
Action::ReadLine(reader_lock) => match read_entry(reader_lock).await {
Some(r) => match r {
Ok(entry) => ActionResult::NewEntry(entry),
Err(err) => ActionResult::NewEntryError(err),
Err(err) => ActionResult::NewEntryError(err.to_string()),
},
None => ActionResult::EndOfStream,
},

View file

@ -1,4 +1,5 @@
use crate::config::Config;
use anyhow::Result;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use sqlx::{ConnectOptions, SqlitePool};
@ -49,11 +50,7 @@ WHERE
AND sdid = $2
AND algorithm = $3";
pub async fn init(cnf: &Config) -> Result<SqlitePool, String> {
do_init(cnf).await.map_err(|e| e.to_string())
}
async fn do_init(cnf: &Config) -> Result<SqlitePool, sqlx::Error> {
pub async fn init(cnf: &Config) -> Result<SqlitePool> {
let mut db_options = SqliteConnectOptions::new()
.filename(cnf.key_data_base())
.create_if_missing(true);

View file

@ -44,13 +44,7 @@ pub async fn read_entry(reader_lock: Arc<RwLock<StdinReader>>) -> Option<Result<
let line_res = reader.read_line().await;
drop(reader);
log::trace!("reader lock on stdin released");
match line_res {
Some(line) => match Entry::from_bytes(&line) {
Ok(entry) => Some(Ok(entry)),
Err(err) => Some(Err(err)),
},
None => None,
}
line_res.map(|line| Entry::from_bytes(&line))
}
fn is_eol(c: u8) -> bool {

View file

@ -1,5 +1,6 @@
use crate::config::Config;
use crate::Algorithm;
use anyhow::Result;
use sqlx::types::time::OffsetDateTime;
use sqlx::SqlitePool;
use std::path::Path;
@ -30,37 +31,34 @@ pub async fn key_rotation(db: &SqlitePool, cnf: &Config) -> Duration {
durations[durations.len() - 1]
}
async fn publish_expired_keys(db: &SqlitePool, file_path: &Path) -> Result<Duration, String> {
async fn publish_expired_keys(db: &SqlitePool, file_path: &Path) -> Result<Duration> {
let res: Vec<(String, String, String, String)> = sqlx::query_as(crate::db::SELECT_EXPIRED_KEYS)
.fetch_all(db)
.await
.map_err(|e| e.to_string())?;
.await?;
if !res.is_empty() {
let rev_file = OpenOptions::new()
.write(true)
.create(true)
.append(true)
.open(file_path)
.await
.map_err(|e| e.to_string())?;
.await?;
let mut buff = BufWriter::new(rev_file);
for (selector, sdid, algorithm, private_key) in res {
buff.write_all(algorithm.as_bytes()).await.unwrap();
buff.write_all(b" ").await.unwrap();
buff.write_all(private_key.as_bytes()).await.unwrap();
buff.write_all(b" ").await.unwrap();
buff.write_all(selector.as_bytes()).await.unwrap();
buff.write_all(b"._domainkey.").await.unwrap();
buff.write_all(sdid.as_bytes()).await.unwrap();
buff.write_all(b"\n").await.unwrap();
buff.flush().await.unwrap();
buff.write_all(algorithm.as_bytes()).await?;
buff.write_all(b" ").await?;
buff.write_all(private_key.as_bytes()).await?;
buff.write_all(b" ").await?;
buff.write_all(selector.as_bytes()).await?;
buff.write_all(b"._domainkey.").await?;
buff.write_all(sdid.as_bytes()).await?;
buff.write_all(b"\n").await?;
buff.flush().await?;
sqlx::query(crate::db::UPDATE_PUBLISHED_KEY)
.bind(&selector)
.bind(&sdid)
.bind(&algorithm)
.execute(db)
.await
.map_err(|e| e.to_string())?;
.await?;
log::info!(
"{algorithm} private key for {selector}._domainkey.{sdid} has been published"
);
@ -68,8 +66,7 @@ async fn publish_expired_keys(db: &SqlitePool, file_path: &Path) -> Result<Durat
}
let res: Option<(i64,)> = sqlx::query_as(crate::db::SELECT_NEAREST_KEY_PUBLICATION)
.fetch_optional(db)
.await
.map_err(|e| e.to_string())?;
.await?;
match res {
Some((next_pub_ts,)) => {
let now_ts = OffsetDateTime::now_utc().unix_timestamp();
@ -86,16 +83,15 @@ async fn renew_key_if_expired(
domain: &str,
algorithm: Algorithm,
expiration: Duration,
) -> Result<Duration, ()> {
) -> Result<Duration> {
let res: Option<(i64,)> = sqlx::query_as(crate::db::SELECT_LATEST_KEY)
.bind(domain)
.bind(algorithm.to_string())
.fetch_optional(db)
.await
.map_err(|_| ())?;
.await?;
match res {
Some((not_after,)) => {
let not_after = OffsetDateTime::from_unix_timestamp(not_after).map_err(|_| ())?;
let not_after = OffsetDateTime::from_unix_timestamp(not_after)?;
log::debug!("{domain}: key is valid until {not_after}");
if not_after - expiration <= OffsetDateTime::now_utc() {
generate_key(db, cnf, domain, algorithm).await?;
@ -114,7 +110,7 @@ async fn generate_key(
cnf: &Config,
domain: &str,
algorithm: Algorithm,
) -> Result<(), ()> {
) -> Result<()> {
let selector = format!("dkim-{}", Uuid::new_v4().simple());
let now = OffsetDateTime::now_utc();
let not_after = now + Duration::from_secs(cnf.cryptoperiod().get());
@ -130,8 +126,7 @@ async fn generate_key(
.bind(priv_key)
.bind(pub_key)
.execute(db)
.await
.map_err(|_| ())?;
.await?;
// TODO: dns_update_cmd
log::debug!("{domain}: new {} key generated", algorithm.to_string());
Ok(())

View file

@ -1,6 +1,7 @@
use crate::config::Config;
use crate::entry::Entry;
use crate::parsed_message::ParsedMessage;
use anyhow::Result;
use tokio::io::{AsyncWriteExt, BufWriter};
pub const RETURN_SEP: &[u8] = b"|";
@ -80,28 +81,32 @@ impl Message {
log::error!("{}: unable to parse message", self.session_id);
}
}
self.print_msg().await;
if let Err(err) = self.print_msg().await {
log::error!("unable to write message: {err}");
}
get_msg_id(&self.session_id, &self.token)
}
async fn print_msg(&self) {
async fn print_msg(&self) -> Result<()> {
let i = self.content.len() - 1;
for line in self.content[0..i].split(|&b| b == b'\n') {
self.print_line(line).await;
self.print_line(line).await?;
}
self.print_line(b".").await;
self.print_line(b".").await?;
Ok(())
}
async fn print_line(&self, line: &[u8]) {
async fn print_line(&self, line: &[u8]) -> Result<()> {
let mut stdout = BufWriter::new(tokio::io::stdout());
stdout.write_all(RETURN_START).await.unwrap();
stdout.write_all(self.session_id.as_bytes()).await.unwrap();
stdout.write_all(RETURN_SEP).await.unwrap();
stdout.write_all(self.token.as_bytes()).await.unwrap();
stdout.write_all(RETURN_SEP).await.unwrap();
stdout.write_all(line).await.unwrap();
stdout.write_all(b"\n").await.unwrap();
stdout.flush().await.unwrap();
stdout.write_all(RETURN_START).await?;
stdout.write_all(self.session_id.as_bytes()).await?;
stdout.write_all(RETURN_SEP).await?;
stdout.write_all(self.token.as_bytes()).await?;
stdout.write_all(RETURN_SEP).await?;
stdout.write_all(line).await?;
stdout.write_all(b"\n").await?;
stdout.flush().await?;
Ok(())
}
}