Skip to content

Commit e8709fb

Browse files
committed
feat: publish pkarr self-announces through the derper
1 parent 38c9359 commit e8709fb

File tree

18 files changed

+347
-32
lines changed

18 files changed

+347
-32
lines changed

iroh-base/src/key.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ impl From<VerifyingKey> for PublicKey {
229229
}
230230
}
231231

232+
impl From<PublicKey> for VerifyingKey {
233+
fn from(value: PublicKey) -> Self {
234+
value.public()
235+
}
236+
}
237+
232238
impl Debug for PublicKey {
233239
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234240
write!(f, "PublicKey({})", base32::fmt_short(self.as_bytes()))
@@ -391,6 +397,12 @@ impl From<SigningKey> for SecretKey {
391397
}
392398
}
393399

400+
impl From<SecretKey> for SigningKey {
401+
fn from(secret: SecretKey) -> Self {
402+
secret.secret
403+
}
404+
}
405+
394406
impl From<[u8; 32]> for SecretKey {
395407
fn from(value: [u8; 32]) -> Self {
396408
Self::from_bytes(&value)

iroh-net/src/bin/derper.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use tracing::{debug, debug_span, error, info, info_span, trace, warn, Instrument
3232
use tracing_subscriber::{prelude::*, EnvFilter};
3333

3434
use metrics::StunMetrics;
35+
use url::Url;
3536

3637
type BytesBody = http_body_util::Full<hyper::body::Bytes>;
3738
type HyperError = Box<dyn std::error::Error + Send + Sync>;
@@ -201,6 +202,8 @@ struct Config {
201202
#[cfg(feature = "metrics")]
202203
/// Metrics serve address. If not set, metrics are not served.
203204
metrics_addr: Option<SocketAddr>,
205+
/// Pkarr relay to publish node announces to
206+
pkarr_relay: Option<Url>,
204207
}
205208

206209
#[derive(Serialize, Deserialize)]
@@ -261,6 +264,7 @@ impl Default for Config {
261264
mesh: None,
262265
#[cfg(feature = "metrics")]
263266
metrics_addr: None,
267+
pkarr_relay: None,
264268
}
265269
}
266270
}
@@ -481,6 +485,9 @@ async fn run(
481485
Box::new(serve_no_content_handler),
482486
);
483487
}
488+
if let Some(pkarr_relay) = cfg.pkarr_relay {
489+
builder = builder.pkarr_relay(pkarr_relay);
490+
}
484491
let derp_server = builder.spawn().await?;
485492

486493
// captive portal detections must be served over HTTP

iroh-net/src/derp.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod codec;
1717
pub mod http;
1818
mod map;
1919
mod metrics;
20+
pub(crate) mod pkarr_announce;
2021
pub(crate) mod server;
2122
pub(crate) mod types;
2223

iroh-net/src/derp/client.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use super::{
2121
types::{ClientInfo, MeshKey, RateLimiter, ServerInfo},
2222
};
2323

24+
use crate::derp::codec::PkarrWirePacket;
2425
use crate::key::{PublicKey, SecretKey};
2526
use crate::util::AbortingJoinHandle;
2627

@@ -76,6 +77,8 @@ pub struct InnerClient {
7677
reader_task: AbortingJoinHandle<()>,
7778
/// [`PublicKey`] of the server we are connected to
7879
server_public_key: PublicKey,
80+
/// Whether the server supports publishing pkarr packets for us
81+
can_pkarr_publish: bool,
7982
}
8083

8184
impl Client {
@@ -160,6 +163,21 @@ impl Client {
160163
Ok(())
161164
}
162165

166+
/// Send a pkarr packet to the derper to publish for us.
167+
///
168+
/// Must be signed by our secret key, otherwise the derper will reject it.
169+
pub async fn pkarr_publish_packet(&self, packet: pkarr::SignedPacket) -> Result<()> {
170+
if !self.inner.can_pkarr_publish {
171+
bail!("the server does not allow pkarr publishing");
172+
}
173+
// todo: check pkey
174+
self.inner
175+
.writer_channel
176+
.send(ClientWriterMessage::PkarrPublish(packet))
177+
.await?;
178+
Ok(())
179+
}
180+
163181
/// The local address that the [`Client`] is listening on.
164182
pub fn local_addr(&self) -> Result<SocketAddr> {
165183
Ok(self.inner.local_addr)
@@ -253,6 +271,8 @@ enum ClientWriterMessage {
253271
/// Asks the server to close the target's connection.
254272
/// Should only be used for mesh clients.
255273
ClosePeer(PublicKey),
274+
/// Publish a pkarr signed packet about ourselves
275+
PkarrPublish(pkarr::SignedPacket),
256276
/// Shutdown the writer
257277
Shutdown,
258278
}
@@ -305,6 +325,11 @@ impl<W: AsyncWrite + Unpin + Send + 'static> ClientWriter<W> {
305325
write_frame(&mut self.writer, Frame::ClosePeer { peer }, None).await?;
306326
self.writer.flush().await?;
307327
}
328+
ClientWriterMessage::PkarrPublish(packet) => {
329+
let packet = PkarrWirePacket::V0(packet.as_bytes());
330+
write_frame(&mut self.writer, Frame::PkarrPublish { packet }, None).await?;
331+
self.writer.flush().await?;
332+
}
308333
ClientWriterMessage::Shutdown => {
309334
return Ok(());
310335
}
@@ -368,7 +393,7 @@ impl ClientBuilder {
368393
self
369394
}
370395

371-
async fn server_handshake(&mut self) -> Result<(PublicKey, Option<RateLimiter>)> {
396+
async fn server_handshake(&mut self) -> Result<(PublicKey, Option<RateLimiter>, bool)> {
372397
debug!("server_handshake: started");
373398
let server_key = recv_server_key(&mut self.reader)
374399
.await
@@ -404,6 +429,8 @@ impl ClientBuilder {
404429
};
405430
let mut buf = encrypted_message.to_vec();
406431
shared_secret.open(&mut buf)?;
432+
433+
// TODO: Can we parse the server info from old derpers without pkarr support?
407434
let info: ServerInfo = postcard::from_bytes(&buf)?;
408435
if info.version != PROTOCOL_VERSION {
409436
bail!(
@@ -417,12 +444,12 @@ impl ClientBuilder {
417444
)?;
418445

419446
debug!("server_handshake: done");
420-
Ok((server_key, rate_limiter))
447+
Ok((server_key, rate_limiter, info.can_pkarr_publish))
421448
}
422449

423450
pub async fn build(mut self) -> Result<(Client, ClientReceiver)> {
424451
// exchange information with the server
425-
let (server_public_key, rate_limiter) = self.server_handshake().await?;
452+
let (server_public_key, rate_limiter, can_pkarr_publish) = self.server_handshake().await?;
426453

427454
// create task to handle writing to the server
428455
let (writer_sender, writer_recv) = mpsc::channel(PER_CLIENT_SEND_QUEUE_DEPTH);
@@ -485,6 +512,7 @@ impl ClientBuilder {
485512
writer_task: writer_task.into(),
486513
reader_task: reader_task.into(),
487514
server_public_key,
515+
can_pkarr_publish,
488516
}),
489517
};
490518

iroh-net/src/derp/client_conn.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{disco::looks_like_disco_wrapper, key::PublicKey};
1616

1717
use iroh_metrics::{inc, inc_by};
1818

19-
use super::codec::{DerpCodec, Frame};
19+
use super::codec::{DerpCodec, Frame, PkarrWirePacket};
2020
use super::server::MaybeTlsStream;
2121
use super::{
2222
codec::{write_frame, KEEP_ALIVE},
@@ -89,6 +89,7 @@ where
8989
pub(crate) write_timeout: Option<Duration>,
9090
pub(crate) channel_capacity: usize,
9191
pub(crate) server_channel: mpsc::Sender<ServerMessage<P>>,
92+
pub(crate) can_pkarr_publish: bool,
9293
}
9394

9495
impl<P> ClientConnBuilder<P>
@@ -106,6 +107,7 @@ where
106107
self.write_timeout,
107108
self.channel_capacity,
108109
self.server_channel,
110+
self.can_pkarr_publish,
109111
)
110112
}
111113
}
@@ -123,6 +125,7 @@ impl ClientConnManager {
123125
write_timeout: Option<Duration>,
124126
channel_capacity: usize,
125127
server_channel: mpsc::Sender<ServerMessage<P>>,
128+
can_pkarr_publish: bool,
126129
) -> ClientConnManager
127130
where
128131
P: PacketForwarder,
@@ -150,6 +153,7 @@ impl ClientConnManager {
150153
key,
151154
preferred: Arc::clone(&preferred),
152155
server_channel: server_channel.clone(),
156+
can_pkarr_publish,
153157
};
154158

155159
// start io loop
@@ -242,6 +246,8 @@ impl ClientConnManager {
242246
pub(crate) struct ClientConnIo<P: PacketForwarder> {
243247
/// Indicates whether this client can mesh
244248
can_mesh: bool,
249+
/// Indicates whether this client can do pkarr publish
250+
can_pkarr_publish: bool,
245251
/// Io to talk to the client
246252
io: Framed<MaybeTlsStream, DerpCodec>,
247253
/// Max time we wait to complete a write to the client
@@ -480,6 +486,9 @@ where
480486
Frame::Health { .. } => {
481487
inc!(Metrics, other_packets_recv);
482488
}
489+
Frame::PkarrPublish { packet } => {
490+
self.handle_pkarr_publish(packet).await?;
491+
}
483492
_ => {
484493
inc!(Metrics, unknown_frames);
485494
}
@@ -553,6 +562,15 @@ where
553562
Ok(())
554563
}
555564

565+
async fn handle_pkarr_publish(&self, frame: PkarrWirePacket) -> Result<()> {
566+
ensure!(self.can_pkarr_publish, "insufficient permissions");
567+
let res = frame.verify_and_decode(&self.key);
568+
let packet = res?;
569+
self.send_server(ServerMessage::PkarrPublish(packet))
570+
.await?;
571+
Ok(())
572+
}
573+
556574
/// Parse the FORWARD_PACKET frame, getting the destination, source, and
557575
/// packet content. Then sends the packet to the server, who directs it
558576
/// to the destination.
@@ -654,6 +672,7 @@ mod tests {
654672
key,
655673
server_channel: server_channel_s,
656674
preferred: Arc::clone(&preferred),
675+
can_pkarr_publish: false,
657676
};
658677

659678
let done = CancellationToken::new();
@@ -865,6 +884,7 @@ mod tests {
865884
key,
866885
server_channel: server_channel_s,
867886
preferred: Arc::clone(&preferred),
887+
can_pkarr_publish: false,
868888
};
869889

870890
let done = CancellationToken::new();

iroh-net/src/derp/clients.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ mod tests {
339339
write_timeout: None,
340340
channel_capacity: 10,
341341
server_channel,
342+
can_pkarr_publish: false,
342343
},
343344
FramedRead::new(test_io, DerpCodec),
344345
)

iroh-net/src/derp/codec.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ pub(crate) enum FrameType {
118118
Restarting = 15,
119119
/// 32B src pub key + 32B dst pub key + packet bytes
120120
ForwardPacket = 16,
121+
/// Sent from the client to the server with a pkarr [`SignedPacket`], which the server should
122+
/// publish on behalf to the client.
123+
PkarrPublish = 17,
121124
#[num_enum(default)]
122125
Unknown = 255,
123126
}
@@ -259,6 +262,54 @@ pub(crate) enum Frame {
259262
dst_key: PublicKey,
260263
packet: Bytes,
261264
},
265+
PkarrPublish {
266+
packet: PkarrWirePacket,
267+
},
268+
}
269+
270+
/// A pkarr signed packet.
271+
///
272+
/// This is wrapped in a wire encoding to allow changing the package format in the future without
273+
/// breaking the protocol.
274+
#[derive(Debug, Clone, Eq, PartialEq)]
275+
pub enum PkarrWirePacket {
276+
V0(Bytes),
277+
}
278+
impl PkarrWirePacket {
279+
pub fn len(&self) -> usize {
280+
match self {
281+
PkarrWirePacket::V0(b) => b.len() + 1,
282+
}
283+
}
284+
pub fn encode(&self, dst: &mut BytesMut) {
285+
match self {
286+
PkarrWirePacket::V0(b) => {
287+
dst.put_u8(0u8);
288+
dst.put(b.as_ref());
289+
}
290+
}
291+
}
292+
pub fn from_bytes(mut src: Bytes) -> anyhow::Result<Self> {
293+
ensure!(src.len() > 1, "packet too short");
294+
let version = src.split_to(1);
295+
match version[0] {
296+
0 => Ok(Self::V0(src)),
297+
_ => bail!("unsupported pkarr wire packet version"),
298+
}
299+
}
300+
pub fn verify_and_decode(self, public_key: &PublicKey) -> anyhow::Result<pkarr::SignedPacket> {
301+
match self {
302+
PkarrWirePacket::V0(bytes) => {
303+
ensure!(bytes.len() > 104, "invalid pkarr packet");
304+
ensure!(
305+
&bytes[..32] == public_key.as_bytes(),
306+
"pkarr packet does not match client public key"
307+
);
308+
let packet = pkarr::SignedPacket::from_bytes(bytes, true)?;
309+
Ok(packet)
310+
}
311+
}
312+
}
262313
}
263314

264315
impl Frame {
@@ -280,6 +331,7 @@ impl Frame {
280331
Frame::Health { .. } => FrameType::Health,
281332
Frame::Restarting { .. } => FrameType::Restarting,
282333
Frame::ForwardPacket { .. } => FrameType::ForwardPacket,
334+
Frame::PkarrPublish { .. } => FrameType::PkarrPublish,
283335
}
284336
}
285337

@@ -312,6 +364,7 @@ impl Frame {
312364
dst_key: _,
313365
packet,
314366
} => PUBLIC_KEY_LENGTH * 2 + packet.len(),
367+
Frame::PkarrPublish { packet } => packet.len(),
315368
}
316369
}
317370

@@ -383,6 +436,7 @@ impl Frame {
383436
dst.put(dst_key.as_ref());
384437
dst.put(packet.as_ref());
385438
}
439+
Frame::PkarrPublish { packet } => packet.encode(dst),
386440
}
387441
}
388442

@@ -535,6 +589,10 @@ impl Frame {
535589
packet,
536590
}
537591
}
592+
FrameType::PkarrPublish => {
593+
let packet = PkarrWirePacket::from_bytes(content)?;
594+
Self::PkarrPublish { packet }
595+
}
538596
_ => {
539597
anyhow::bail!("invalid frame type: {:?}", frame_type);
540598
}

0 commit comments

Comments
 (0)