Skip to content

Commit b1addfd

Browse files
committed
Bugfix: Fix Keychain lookup for "Connect directly"
- Keychain credentials are keyed by hostname (for example, `naspolya`) but `statfs` returns the IP. Lookup now resolves IP → hostname via mDNS discovered hosts and tries both. - Added `resolve_ip_to_hostname` helper and info-level logging for credential lookup results.
1 parent 412f0cd commit b1addfd

1 file changed

Lines changed: 56 additions & 17 deletions

File tree

apps/desktop/src-tauri/src/commands/network.rs

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,16 @@ pub async fn upgrade_to_smb_volume(volume_id: String) -> Result<String, String>
400400
info.username
401401
);
402402

403-
// Try to get credentials from Keychain (using the username/server from the mount).
404-
// If that fails, try guest access.
405-
let creds = get_keychain_password(&info.server, &info.share).await;
403+
// Try to get credentials from Keychain. The mount source has the IP, but Cmdr
404+
// stores Keychain credentials keyed by hostname (from mDNS). Try both.
405+
let hostname = resolve_ip_to_hostname(&info.server);
406+
let creds = get_keychain_password(&info.server, hostname.as_deref(), &info.share).await;
407+
408+
match &creds {
409+
Some((u, _)) => log::info!("Found Keychain credentials for user={}", u),
410+
None => log::info!("No Keychain credentials found, trying guest access"),
411+
}
412+
406413
let (username, password) = match &creds {
407414
Some((u, p)) => (Some(u.as_str()), Some(p.as_str())),
408415
None => (None, None),
@@ -422,30 +429,62 @@ pub async fn upgrade_to_smb_volume(volume_id: String) -> Result<String, String>
422429
))
423430
}
424431

432+
/// Looks up the mDNS hostname for an IP address from discovered hosts.
433+
///
434+
/// Returns the hostname (like "naspolya") without `.local` suffix.
435+
fn resolve_ip_to_hostname(ip: &str) -> Option<String> {
436+
let hosts = get_discovered_hosts();
437+
for host in &hosts {
438+
if host.ip_address.as_deref() == Some(ip) {
439+
// Return the service name (lowercased), which is what Keychain keys use
440+
return Some(host.name.to_lowercase());
441+
}
442+
}
443+
None
444+
}
445+
425446
/// Tries to retrieve SMB credentials from the Keychain.
426447
///
427-
/// Checks share-level first (more specific), then server-level.
428-
async fn get_keychain_password(server: &str, share: &str) -> Option<(String, String)> {
429-
let server = server.to_string();
448+
/// Tries multiple keys: by IP (from statfs), by hostname (from mDNS discovery),
449+
/// at both share-level and server-level.
450+
async fn get_keychain_password(
451+
server_ip: &str,
452+
hostname: Option<&str>,
453+
share: &str,
454+
) -> Option<(String, String)> {
455+
let server_ip = server_ip.to_string();
456+
let hostname = hostname.map(|s| s.to_string());
430457
let share = share.to_string();
431458

432-
// Keychain access can block, so run in a blocking task
433459
tokio::task::spawn_blocking(move || {
434460
use crate::network::keychain;
435461

436-
// Try share-level credentials first (more specific)
437-
if let Ok(creds) = keychain::get_credentials(&server, Some(&share)) {
438-
log::debug!("Found Keychain credentials for {}/{}", server, share);
439-
return Some((creds.username, creds.password));
462+
// Build a list of server names to try (hostname first, then IP)
463+
let mut servers_to_try: Vec<&str> = Vec::new();
464+
if let Some(ref h) = hostname {
465+
servers_to_try.push(h);
440466
}
441-
442-
// Try server-level credentials
443-
if let Ok(creds) = keychain::get_credentials(&server, None) {
444-
log::debug!("Found Keychain credentials for {} (server-level)", server);
445-
return Some((creds.username, creds.password));
467+
servers_to_try.push(&server_ip);
468+
469+
for server in &servers_to_try {
470+
// Try share-level credentials first (more specific)
471+
if let Ok(creds) = keychain::get_credentials(server, Some(&share)) {
472+
log::debug!("Found Keychain credentials via {}/{}", server, share);
473+
return Some((creds.username, creds.password));
474+
}
475+
// Try server-level credentials
476+
if let Ok(creds) = keychain::get_credentials(server, None) {
477+
log::debug!("Found Keychain credentials via {} (server-level)", server);
478+
return Some((creds.username, creds.password));
479+
}
446480
}
447481

448-
log::debug!("No Keychain credentials for {}/{}", server, share);
482+
log::debug!(
483+
"No Keychain credentials for {:?} / {} / {}",
484+
hostname,
485+
server_ip,
486+
share
487+
);
449488
None
450489
})
451490
.await

0 commit comments

Comments
 (0)