tawasuyu

tawasuyu/arje

Rust 38TOML 12Markdown 1410 Definiciones

⎇ main crates/ente-resolved-compat/src/main.rs Rust · 210 líneas · 7.5 KB Historia Quién tocó qué crudo

Persistencia setters, compat-resolved, journald→CAS, compat-polkit d88a9c579 · Sergio ·

1//! ente-resolved-compat: shim de `org.freedesktop.resolve1`.
2//!
3//! Bajo el capó usa `tokio::net::lookup_host` (que termina en getaddrinfo
4//! del libc del sistema). No reimplementamos un resolver DNS — delegamos
5//! al stack de resolución del kernel/glibc.
6//!
7//! Métodos cubiertos:
8//!   - ResolveHostname (name → addresses)
9//!   - ResolveAddress (address → name reverse)
10//!   - ResolveRecord (TXT/SRV/etc) — NotSupported (requiere DNS query directa)
11
12use ente_bus::{BusClient, BusRequest, BusResponse};
13use ente_card::Capability;
14use std::net::IpAddr;
15use tokio::signal::unix::{signal, SignalKind};
16use tracing::{info, warn};
17use tracing_subscriber::EnvFilter;
18use zbus::{fdo, interface};
19
20const BUS_NAME: &str = "org.freedesktop.resolve1";
21const OBJ_PATH: &str = "/org/freedesktop/resolve1";
22
23const AF_INET: i32 = 2;
24const AF_INET6: i32 = 10;
25
26#[tokio::main(flavor = "current_thread")]
27async fn main() -> anyhow::Result<()> {
28    init_tracing();
29    info!("ente-resolved-compat: arrancando");
30    announce_to_fractal().await;
31
32    let manager = ResolveManager;
33    let conn_result = zbus::connection::Builder::system()
34        .and_then(|b| b.name(BUS_NAME))
35        .and_then(|b| b.serve_at(OBJ_PATH, manager));
36    match conn_result {
37        Ok(builder) => match builder.build().await {
38            Ok(_conn) => {
39                info!(name = BUS_NAME, "name acquired, sirviendo");
40                wait_for_term().await
41            }
42            Err(e) => { warn!(?e, "build conn falló — modo idle"); wait_for_term().await }
43        },
44        Err(e) => { warn!(?e, "builder D-Bus falló — modo idle"); wait_for_term().await }
45    }
46}
47
48struct ResolveManager;
49
50/// Tipo del wire format de `ResolveHostname`. Por entry: (ifindex, family,
51/// address-as-bytes). systemd-resolved devuelve hasta 4 bytes para AF_INET
52/// y 16 para AF_INET6.
53type HostnameAddress = (i32, i32, Vec<u8>);
54
55#[interface(name = "org.freedesktop.resolve1.Manager")]
56impl ResolveManager {
57    /// Wire signature: `ResolveHostname(in iiusst, out a(iiay)st)` — recibe
58    /// (ifindex, name, family, flags), devuelve (addresses, canonical, flags).
59    async fn resolve_hostname(
60        &self,
61        _ifindex: i32,
62        name: String,
63        family: i32,
64        _flags: u64,
65    ) -> fdo::Result<(Vec<HostnameAddress>, String, u64)> {
66        // tokio::net::lookup_host requiere "host:port"; usamos puerto sentinel.
67        let target = format!("{name}:0");
68        let addrs = match tokio::net::lookup_host(&target).await {
69            Ok(it) => it,
70            Err(e) => return Err(fdo::Error::Failed(format!("lookup_host {name}: {e}"))),
71        };
72        let mut out = Vec::new();
73        for sa in addrs {
74            let ip = sa.ip();
75            let (af, bytes) = match ip {
76                IpAddr::V4(v4) => (AF_INET, v4.octets().to_vec()),
77                IpAddr::V6(v6) => (AF_INET6, v6.octets().to_vec()),
78            };
79            // Filtrado por family si el llamador lo pidió específico.
80            if family != 0 && family != af { continue; }
81            out.push((0i32, af, bytes));
82        }
83        if out.is_empty() {
84            return Err(fdo::Error::Failed(format!("sin resoluciones para {name} (family={family})")));
85        }
86        info!(%name, family, count = out.len(), "ResolveHostname");
87        Ok((out, name, 0))
88    }
89
90    /// Wire signature: `ResolveAddress(in iiayt, out a(is)t)` — (ifindex,
91    /// family, address, flags) → (names, flags).
92    async fn resolve_address(
93        &self,
94        _ifindex: i32,
95        family: i32,
96        address: Vec<u8>,
97        _flags: u64,
98    ) -> fdo::Result<(Vec<(i32, String)>, u64)> {
99        let ip = parse_address(family, &address)
100            .ok_or_else(|| fdo::Error::InvalidArgs(format!("address malformado family={family} bytes={}", address.len())))?;
101        // Reverse lookup vía getnameinfo. Usamos std::net::lookup_addr no existe,
102        // así que invocamos via libc directamente.
103        let name = reverse_lookup(ip)
104            .ok_or_else(|| fdo::Error::Failed(format!("sin reverse para {ip}")))?;
105        info!(%ip, %name, "ResolveAddress");
106        Ok((vec![(0, name)], 0))
107    }
108
109    async fn resolve_record(
110        &self,
111        _ifindex: i32,
112        _name: String,
113        _class: u16,
114        _type_: u16,
115        _flags: u64,
116    ) -> fdo::Result<(Vec<(i32, u16, u16, Vec<u8>)>, u64)> {
117        Err(fdo::Error::NotSupported(
118            "ResolveRecord requiere acceso DNS directo — stub no implementado".into()
119        ))
120    }
121}
122
123fn parse_address(family: i32, bytes: &[u8]) -> Option<IpAddr> {
124    match family {
125        AF_INET if bytes.len() == 4 => {
126            let mut a = [0u8; 4];
127            a.copy_from_slice(bytes);
128            Some(IpAddr::V4(std::net::Ipv4Addr::from(a)))
129        }
130        AF_INET6 if bytes.len() == 16 => {
131            let mut a = [0u8; 16];
132            a.copy_from_slice(bytes);
133            Some(IpAddr::V6(std::net::Ipv6Addr::from(a)))
134        }
135        _ => None,
136    }
137}
138
139/// getnameinfo(3) wrapper. Devuelve None si no resuelve.
140fn reverse_lookup(ip: IpAddr) -> Option<String> {
141    use std::os::raw::c_char;
142    let mut buf = [0i8; 256];
143    let r = match ip {
144        IpAddr::V4(v4) => unsafe {
145            let octets = v4.octets();
146            let mut sin = std::mem::zeroed::<libc::sockaddr_in>();
147            sin.sin_family = libc::AF_INET as u16;
148            sin.sin_addr = libc::in_addr {
149                s_addr: u32::from_ne_bytes(octets),
150            };
151            libc::getnameinfo(
152                &sin as *const _ as *const libc::sockaddr,
153                std::mem::size_of::<libc::sockaddr_in>() as u32,
154                buf.as_mut_ptr() as *mut c_char, buf.len() as u32,
155                std::ptr::null_mut(), 0,
156                libc::NI_NAMEREQD,
157            )
158        },
159        IpAddr::V6(v6) => unsafe {
160            let octets = v6.octets();
161            let mut sin6 = std::mem::zeroed::<libc::sockaddr_in6>();
162            sin6.sin6_family = libc::AF_INET6 as u16;
163            sin6.sin6_addr.s6_addr.copy_from_slice(&octets);
164            libc::getnameinfo(
165                &sin6 as *const _ as *const libc::sockaddr,
166                std::mem::size_of::<libc::sockaddr_in6>() as u32,
167                buf.as_mut_ptr() as *mut c_char, buf.len() as u32,
168                std::ptr::null_mut(), 0,
169                libc::NI_NAMEREQD,
170            )
171        },
172    };
173    if r != 0 { return None; }
174    let cs = unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) };
175    cs.to_str().ok().map(String::from)
176}
177
178extern crate libc;
179
180async fn announce_to_fractal() {
181    if let Ok(mut client) = BusClient::from_env().await {
182        let req = BusRequest::Announce {
183            capabilities: vec![Capability::Endpoint {
184                interface: ente_card::InterfaceId([0xa3; 16]),
185                version: 1,
186            }],
187        };
188        match client.call(req).await {
189            Ok(BusResponse::Ok) => info!("Announce → bus interno OK"),
190            Ok(other) => warn!(?other, "Announce respuesta inesperada"),
191            Err(e) => warn!(?e, "Announce falló"),
192        }
193    }
194}
195
196async fn wait_for_term() -> anyhow::Result<()> {
197    let mut term = signal(SignalKind::terminate())?;
198    let mut int_ = signal(SignalKind::interrupt())?;
199    tokio::select! {
200        _ = term.recv() => info!("SIGTERM"),
201        _ = int_.recv() => info!("SIGINT"),
202    }
203    Ok(())
204}
205
206fn init_tracing() {
207    let filter = EnvFilter::try_from_default_env()
208        .unwrap_or_else(|_| EnvFilter::new("ente_resolved_compat=info"));
209    tracing_subscriber::fmt().with_env_filter(filter).with_target(true).init();
210}