feat: dial hints on the account-bound operations for self-resolving hosts
This commit is contained in:
parent
5559fcce31
commit
28f13641a3
3 changed files with 45 additions and 23 deletions
|
|
@ -340,7 +340,7 @@ fn restore(key: &PathBuf, store: &Store, address: &str, timeout: u64) -> Result<
|
||||||
|
|
||||||
fn rotate(key: &PathBuf, store: &Store, timeout: u64) -> Result<()> {
|
fn rotate(key: &PathBuf, store: &Store, timeout: u64) -> Result<()> {
|
||||||
let account = load_account(key, store)?;
|
let account = load_account(key, store)?;
|
||||||
let rotated = client::rotate(store, &account, timeout)?;
|
let rotated = client::rotate(store, &account, None, timeout)?;
|
||||||
let addr = store.account()?.expect("rotate checked for an account");
|
let addr = store.account()?.expect("rotate checked for an account");
|
||||||
println!("rotated {}", addr.short());
|
println!("rotated {}", addr.short());
|
||||||
println!("new key: {}", b32(&rotated.new_pk));
|
println!("new key: {}", b32(&rotated.new_pk));
|
||||||
|
|
@ -509,7 +509,7 @@ fn accept(key: &PathBuf, store: &Store, address: &str, timeout: u64) -> Result<(
|
||||||
}
|
}
|
||||||
store.accept(&address, &identity)?;
|
store.accept(&address, &identity)?;
|
||||||
println!("accepted {address}; its token travels in your next message to them");
|
println!("accepted {address}; its token travels in your next message to them");
|
||||||
match client::push_tokens(store, &account, timeout)? {
|
match client::push_tokens(store, &account, None, timeout)? {
|
||||||
Pushed::NotRegistered => {
|
Pushed::NotRegistered => {
|
||||||
warn("not registered; the set will be pushed with your first fetch")
|
warn("not registered; the set will be pushed with your first fetch")
|
||||||
}
|
}
|
||||||
|
|
@ -525,7 +525,7 @@ fn block(key: &PathBuf, store: &Store, address: &str, timeout: u64) -> Result<()
|
||||||
return Err(anyhow!(format!("{address} was never accepted")));
|
return Err(anyhow!(format!("{address} was never accepted")));
|
||||||
}
|
}
|
||||||
println!("blocked {address}; their mail lands in requests from their next message on");
|
println!("blocked {address}; their mail lands in requests from their next message on");
|
||||||
match client::push_tokens(store, &account, timeout)? {
|
match client::push_tokens(store, &account, None, timeout)? {
|
||||||
Pushed::NotRegistered => {
|
Pushed::NotRegistered => {
|
||||||
warn("not registered; the set will be pushed with your first fetch")
|
warn("not registered; the set will be pushed with your first fetch")
|
||||||
}
|
}
|
||||||
|
|
@ -711,7 +711,7 @@ fn resolve_id_prefixes(store: &Store, ids: &[String]) -> Result<Vec<[u8; ID_LEN]
|
||||||
fn delete(key: &PathBuf, store: &Store, ids: &[String], timeout: u64) -> Result<()> {
|
fn delete(key: &PathBuf, store: &Store, ids: &[String], timeout: u64) -> Result<()> {
|
||||||
let account = load_account(key, store)?;
|
let account = load_account(key, store)?;
|
||||||
let full = resolve_id_prefixes(store, ids)?;
|
let full = resolve_id_prefixes(store, ids)?;
|
||||||
let removed = client::delete(store, &account, &full, timeout)?;
|
let removed = client::delete(store, &account, &full, None, timeout)?;
|
||||||
println!("removed {removed} message(s) from the server");
|
println!("removed {removed} message(s) from the server");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,7 @@ fn valid_username(name: &str) -> Result<(), Error> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::crypto::b32;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dial_hint_routes_without_renaming() {
|
fn dial_hint_routes_without_renaming() {
|
||||||
|
|
@ -227,14 +228,10 @@ mod tests {
|
||||||
// proof-of-possession and display all read `host`.
|
// proof-of-possession and display all read `host`.
|
||||||
assert_eq!(addr.dial.as_deref(), Some("192.0.2.10"));
|
assert_eq!(addr.dial.as_deref(), Some("192.0.2.10"));
|
||||||
assert_eq!(addr.host, "example.org");
|
assert_eq!(addr.host, "example.org");
|
||||||
assert_eq!(addr.short(), "alice@example.org:1961");
|
// The default port is elided in the short form, as everywhere.
|
||||||
|
assert_eq!(addr.short(), "alice@example.org");
|
||||||
assert_eq!(Address::parse("alice@example.org").unwrap().dial, None);
|
assert_eq!(Address::parse("alice@example.org").unwrap().dial, None);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::crypto::b32;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn short_form_and_default_port() {
|
fn short_form_and_default_port() {
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,20 @@ impl Session {
|
||||||
/// Opens a session, enforcing sec 4's rule about unpinned servers. TCP pins
|
/// Opens a session, enforcing sec 4's rule about unpinned servers. TCP pins
|
||||||
/// the server's Noise static key; on RNS the destination hash is the pin
|
/// the server's Noise static key; on RNS the destination hash is the pin
|
||||||
/// (RNS.md sec 13.4).
|
/// (RNS.md sec 13.4).
|
||||||
|
/// The account address with the host's dial hint applied, when the caller
|
||||||
|
/// resolved DNS itself: account-bound operations re-read the account, so
|
||||||
|
/// the hint rides along this way rather than in the stored row.
|
||||||
|
#[cfg(feature = "store")]
|
||||||
|
fn account_with_dial(store: &Store, dial: Option<&str>) -> Result<Address, Error> {
|
||||||
|
let addr = store
|
||||||
|
.account()?
|
||||||
|
.ok_or(Error::NotRegistered)?;
|
||||||
|
Ok(match dial {
|
||||||
|
Some(dial) => addr.with_dial(dial),
|
||||||
|
None => addr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "store")]
|
#[cfg(feature = "store")]
|
||||||
pub fn connect(
|
pub fn connect(
|
||||||
store: &Store,
|
store: &Store,
|
||||||
|
|
@ -286,9 +300,16 @@ pub enum Pushed {
|
||||||
/// An accept or a block only takes effect once the server holds the changed
|
/// An accept or a block only takes effect once the server holds the changed
|
||||||
/// set, so it is pushed now rather than at the next fetch.
|
/// set, so it is pushed now rather than at the next fetch.
|
||||||
#[cfg(feature = "store")]
|
#[cfg(feature = "store")]
|
||||||
pub fn push_tokens(store: &Store, account: &Account, timeout: u64) -> Result<Pushed, Error> {
|
pub fn push_tokens(
|
||||||
let Some(addr) = store.account()? else {
|
store: &Store,
|
||||||
return Ok(Pushed::NotRegistered);
|
account: &Account,
|
||||||
|
dial: Option<&str>,
|
||||||
|
timeout: u64,
|
||||||
|
) -> Result<Pushed, Error> {
|
||||||
|
let addr = match account_with_dial(store, dial) {
|
||||||
|
Ok(addr) => addr,
|
||||||
|
Err(Error::NotRegistered) => return Ok(Pushed::NotRegistered),
|
||||||
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
let mut session = connect(store, &addr, true, timeout)?;
|
let mut session = connect(store, &addr, true, timeout)?;
|
||||||
let held = authenticate(session.transport(), &addr.user, account, store)?;
|
let held = authenticate(session.transport(), &addr.user, account, store)?;
|
||||||
|
|
@ -346,10 +367,13 @@ pub struct Rotated {
|
||||||
/// is untouched; only the index moves, and the superseded key stays
|
/// is untouched; only the index moves, and the superseded key stays
|
||||||
/// derivable from it (sec 2).
|
/// derivable from it (sec 2).
|
||||||
#[cfg(feature = "store")]
|
#[cfg(feature = "store")]
|
||||||
pub fn rotate(store: &Store, account: &Account, timeout: u64) -> Result<Rotated, Error> {
|
pub fn rotate(
|
||||||
let Some(addr) = store.account()? else {
|
store: &Store,
|
||||||
return Err(Error::NotRegistered);
|
account: &Account,
|
||||||
};
|
dial: Option<&str>,
|
||||||
|
timeout: u64,
|
||||||
|
) -> Result<Rotated, Error> {
|
||||||
|
let addr = account_with_dial(store, dial)?;
|
||||||
if account.index() as usize >= MAX_CHAIN {
|
if account.index() as usize >= MAX_CHAIN {
|
||||||
return Err(Error::ChainLimit {
|
return Err(Error::ChainLimit {
|
||||||
index: account.index() + 1,
|
index: account.index() + 1,
|
||||||
|
|
@ -564,6 +588,9 @@ pub struct FetchOptions<'a> {
|
||||||
pub reset: bool,
|
pub reset: bool,
|
||||||
/// Network timeout in seconds.
|
/// Network timeout in seconds.
|
||||||
pub timeout: u64,
|
pub timeout: u64,
|
||||||
|
/// Dial this address instead of the account host, for hosts that
|
||||||
|
/// resolve DNS themselves; the host keeps its identity roles.
|
||||||
|
pub dial: Option<&'a str>,
|
||||||
/// Checked between pages and before each envelope; when raised,
|
/// Checked between pages and before each envelope; when raised,
|
||||||
/// `fetch_with` stops and returns the partial summary with `cancelled`
|
/// `fetch_with` stops and returns the partial summary with `cancelled`
|
||||||
/// set. What was processed is accounted for locally — cursor moved or
|
/// set. What was processed is accounted for locally — cursor moved or
|
||||||
|
|
@ -579,6 +606,7 @@ impl FetchOptions<'_> {
|
||||||
keep,
|
keep,
|
||||||
reset,
|
reset,
|
||||||
timeout,
|
timeout,
|
||||||
|
dial: None,
|
||||||
cancel: None,
|
cancel: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -607,9 +635,7 @@ pub fn fetch_with(
|
||||||
account: &Account,
|
account: &Account,
|
||||||
opts: FetchOptions<'_>,
|
opts: FetchOptions<'_>,
|
||||||
) -> Result<Fetched, Error> {
|
) -> Result<Fetched, Error> {
|
||||||
let Some(addr) = store.account()? else {
|
let addr = account_with_dial(store, opts.dial)?;
|
||||||
return Err(Error::NotRegistered);
|
|
||||||
};
|
|
||||||
if opts.reset {
|
if opts.reset {
|
||||||
store.set_cursor(0, &[0u8; ID_LEN])?;
|
store.set_cursor(0, &[0u8; ID_LEN])?;
|
||||||
}
|
}
|
||||||
|
|
@ -761,11 +787,10 @@ pub fn delete(
|
||||||
store: &Store,
|
store: &Store,
|
||||||
account: &Account,
|
account: &Account,
|
||||||
ids: &[[u8; ID_LEN]],
|
ids: &[[u8; ID_LEN]],
|
||||||
|
dial: Option<&str>,
|
||||||
timeout: u64,
|
timeout: u64,
|
||||||
) -> Result<u16, Error> {
|
) -> Result<u16, Error> {
|
||||||
let Some(addr) = store.account()? else {
|
let addr = account_with_dial(store, dial)?;
|
||||||
return Err(Error::NotRegistered);
|
|
||||||
};
|
|
||||||
let mut session = connect(store, &addr, true, timeout)?;
|
let mut session = connect(store, &addr, true, timeout)?;
|
||||||
let transport = session.transport();
|
let transport = session.transport();
|
||||||
authenticate(transport, &addr.user, account, store)?;
|
authenticate(transport, &addr.user, account, store)?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue