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<()> {
|
||||
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");
|
||||
println!("rotated {}", addr.short());
|
||||
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)?;
|
||||
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 => {
|
||||
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")));
|
||||
}
|
||||
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 => {
|
||||
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<()> {
|
||||
let account = load_account(key, store)?;
|
||||
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");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@ fn valid_username(name: &str) -> Result<(), Error> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::crypto::b32;
|
||||
|
||||
#[test]
|
||||
fn dial_hint_routes_without_renaming() {
|
||||
|
|
@ -227,14 +228,10 @@ mod tests {
|
|||
// proof-of-possession and display all read `host`.
|
||||
assert_eq!(addr.dial.as_deref(), Some("192.0.2.10"));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::crypto::b32;
|
||||
|
||||
#[test]
|
||||
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
|
||||
/// the server's Noise static key; on RNS the destination hash is the pin
|
||||
/// (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")]
|
||||
pub fn connect(
|
||||
store: &Store,
|
||||
|
|
@ -286,9 +300,16 @@ pub enum Pushed {
|
|||
/// 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.
|
||||
#[cfg(feature = "store")]
|
||||
pub fn push_tokens(store: &Store, account: &Account, timeout: u64) -> Result<Pushed, Error> {
|
||||
let Some(addr) = store.account()? else {
|
||||
return Ok(Pushed::NotRegistered);
|
||||
pub fn push_tokens(
|
||||
store: &Store,
|
||||
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 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
|
||||
/// derivable from it (sec 2).
|
||||
#[cfg(feature = "store")]
|
||||
pub fn rotate(store: &Store, account: &Account, timeout: u64) -> Result<Rotated, Error> {
|
||||
let Some(addr) = store.account()? else {
|
||||
return Err(Error::NotRegistered);
|
||||
};
|
||||
pub fn rotate(
|
||||
store: &Store,
|
||||
account: &Account,
|
||||
dial: Option<&str>,
|
||||
timeout: u64,
|
||||
) -> Result<Rotated, Error> {
|
||||
let addr = account_with_dial(store, dial)?;
|
||||
if account.index() as usize >= MAX_CHAIN {
|
||||
return Err(Error::ChainLimit {
|
||||
index: account.index() + 1,
|
||||
|
|
@ -564,6 +588,9 @@ pub struct FetchOptions<'a> {
|
|||
pub reset: bool,
|
||||
/// Network timeout in seconds.
|
||||
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,
|
||||
/// `fetch_with` stops and returns the partial summary with `cancelled`
|
||||
/// set. What was processed is accounted for locally — cursor moved or
|
||||
|
|
@ -579,6 +606,7 @@ impl FetchOptions<'_> {
|
|||
keep,
|
||||
reset,
|
||||
timeout,
|
||||
dial: None,
|
||||
cancel: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -607,9 +635,7 @@ pub fn fetch_with(
|
|||
account: &Account,
|
||||
opts: FetchOptions<'_>,
|
||||
) -> Result<Fetched, Error> {
|
||||
let Some(addr) = store.account()? else {
|
||||
return Err(Error::NotRegistered);
|
||||
};
|
||||
let addr = account_with_dial(store, opts.dial)?;
|
||||
if opts.reset {
|
||||
store.set_cursor(0, &[0u8; ID_LEN])?;
|
||||
}
|
||||
|
|
@ -761,11 +787,10 @@ pub fn delete(
|
|||
store: &Store,
|
||||
account: &Account,
|
||||
ids: &[[u8; ID_LEN]],
|
||||
dial: Option<&str>,
|
||||
timeout: u64,
|
||||
) -> Result<u16, Error> {
|
||||
let Some(addr) = store.account()? else {
|
||||
return Err(Error::NotRegistered);
|
||||
};
|
||||
let addr = account_with_dial(store, dial)?;
|
||||
let mut session = connect(store, &addr, true, timeout)?;
|
||||
let transport = session.transport();
|
||||
authenticate(transport, &addr.user, account, store)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue