Skip to content

Commit

Permalink
Merge pull request #36 from Nuhvi/rust
Browse files Browse the repository at this point in the history
Mainline support
  • Loading branch information
Nuhvi authored Nov 28, 2023
2 parents 0a18184 + 1257095 commit fedf68a
Show file tree
Hide file tree
Showing 18 changed files with 1,304 additions and 263 deletions.
498 changes: 358 additions & 140 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ Pkarr doesn't need to bootstrap anything or invent anything, instead using 15 ye
4. [x] ClI - publish and resolve resource records.
5. [x] Add optional caching and republishing of records on GET requests.
6. [x] Implement a standalone republisher and test keeping 100s of thousands of records alive.
7. [ ] Reimplement everything in Rust once the JS implementation is tested with some scale. **<< we ar here**
8. [ ] Add the DNS over HTTPS features and documentations for adding Pkarr relays in OS and browser's configurations.
7. [x] Reimplement everything in Rust once the JS implementation is tested with some scale.
8. [ ] Add the DNS over HTTPS features and documentations for adding Pkarr relays in OS and browser's configurations. **<< we ar here**

## FAQ

Expand Down
39 changes: 37 additions & 2 deletions pkarr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pkarr"
version = "0.3.0"
version = "1.0.0"
authors = ["Nuh <[email protected]>"]
edition = "2021"
description = "Public-Key Addressable Resource Records (Pkarr); publish and resolve DNS records over Mainline DHT"
Expand All @@ -12,13 +12,48 @@ categories = ["networking", "p2p", "routing"]
[dependencies]
bytes = "1.5.0"
ed25519-dalek = { version = "2.0.0", features = ["rand_core"] }
mainline = { version = "1.0.0", optional = true }
rand = "0.8.5"
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"] }
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"], optional = true}
self_cell = "1.0.2"
simple-dns = "0.5.6"
thiserror = "1.0.49"
url = "2.4.1"
z32 = "1.0.0"

[dev-dependencies]
clap = { version = "4.4.8", features = ["derive"] }
tokio = { version = "1.32.0", features = ["tokio-macros", "macros", "rt-multi-thread"] }

[features]
async= []
dht = ["mainline"]
relay = ["reqwest"]

full = ["dht", "relay", "async"]
default = ["dht"]

[[example]]
name = "relay-client"
path = "examples/relay-client.rs"
required-features = ["relay"]

[[example]]
name = "async-relay-client"
path = "examples/async/relay-client.rs"
required-features = ["relay", "async"]

[[example]]
name = "async-publish"
path = "examples/async/publish.rs"
required-features = ["async"]

[[example]]
name = "async-resolve"
path = "examples/async/resolve.rs"
required-features = ["async"]

[[example]]
name = "async-resolve-most-recent"
path = "examples/async/resolve-most-recent.rs"
required-features = ["async"]
11 changes: 11 additions & 0 deletions pkarr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Pkarr

Rust implementation of [Pkarr](pkarr.org).

Publish and resolve DNS packets over Mainline DHT.

**[API Docs](https://docs.rs/pkarr/latest/pkarr/)**

## Get started

Check the [Examples](https://github.com/Nuhvi/pkarr/tree/main/pkarr/examples).
49 changes: 49 additions & 0 deletions pkarr/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Examples

## Publish

```sh
cargo run --example publish
```

## Resolve Eager

```sh
cargo run --example resolve <zbase32 public key from Publish step>
```

## Resolve Most Recent

```sh
cargo run --example resolve-most-recent <zbase32 public key from Publish step>
```

## Relay

```sh
cargo run --example relay-client
```

## Async Publish

```sh
cargo run --example async-publish --features="async"
```

## Async Resolve Eager

```sh
cargo run --example async-resolve --features="async" <zbase32 public key from Publish step>
```

## Resolve Most Recent

```sh
cargo run --example async-resolve-most-recent --features="async" <zbase32 public key from Publish step>
```

## Relay

```sh
cargo run --example async-relay-client --features="relay async"
```
47 changes: 47 additions & 0 deletions pkarr/examples/async/publish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! This example shows how to publish [ResourceRecord]s directly to the DHT.
//!
//! Change the `Keypair::random()` to your own keypair to publish your own records.
//! Change the `packet.answers` to your own records.
//!
//! run this example from the project root:
//! $ cargo run --example publish
use std::time::Instant;

use pkarr::{dns, Keypair, PkarrClient, Result, SignedPacket};

#[tokio::main]
async fn main() -> Result<()> {
let client = PkarrClient::new();

let keypair = Keypair::random();

let mut packet = dns::Packet::new_reply(0);
packet.answers.push(dns::ResourceRecord::new(
dns::Name::new("_foo").unwrap(),
dns::CLASS::IN,
30,
dns::rdata::RData::TXT("bar".try_into()?),
));

let signed_packet = SignedPacket::from_packet(&keypair, &packet)?;

let instant = Instant::now();

println!("\nPublishing {} ...", keypair.to_uri_string());

match client.publish(&signed_packet).await {
Ok(metadata) => {
println!(
"\nSuccessfully published in {:?} to {} nodes",
instant.elapsed(),
metadata.stored_at().len()
);
}
Err(err) => {
println!("\nFailed to publish {} \n {}", keypair.public_key(), err);
}
};

Ok(())
}
85 changes: 85 additions & 0 deletions pkarr/examples/async/relay-client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! This example shows how to publish and resolve [ResourceRecord]s to and from a Pkarr
//! [relay](https://github.com/Nuhvi/pkarr/blob/main/design/relays.md).
//!
//! In this example, we publish both an A and CNAME records as well as a TXT record representing
//! [Iroh](https://iroh.computer/) DERP regions used by Peer with the same Ed25519 key.
//!
//! For the purpose of this example we use the default Pkarr relay for both
//! publishing and resolving, but you can override `relay1` and `relay2` to
//! with your own locally running relays.
//!
//! Publishing takes a few seconds as the relay only responds after sending the PUT
//! request to all the nearest nodes to the Pkarr key.
//!
//! Resolving on the other hand is optimistic and the relay will retun the first
//! response it gets.
//!
//! run this example from the project root:
//! $ cargo run --example relay-client
use std::time::Instant;

use pkarr::{dns, url::Url, Keypair, PkarrClient, Result, SignedPacket, DEFAULT_PKARR_RELAY};

#[tokio::main]
async fn main() -> Result<()> {
let keypair = Keypair::random();

let relay1 = DEFAULT_PKARR_RELAY;
let relay2 = DEFAULT_PKARR_RELAY;

// Publisher
{
println!("\nPublishing {} ...\n", keypair.to_uri_string());
let mut packet = dns::Packet::new_reply(0);
packet.answers.push(dns::ResourceRecord::new(
dns::Name::new("_derp_region.iroh.").unwrap(),
dns::CLASS::IN,
30,
dns::rdata::RData::A(dns::rdata::A {
address: std::net::Ipv4Addr::new(52, 30, 229, 248).into(),
}),
));
packet.answers.push(dns::ResourceRecord::new(
dns::Name::new("_derp_region.iroh.").unwrap(),
dns::CLASS::IN,
30,
dns::rdata::RData::CNAME(
dns::Name::new("eu.derp.iroh.network")
.expect("is valid name")
.into(),
),
));
packet.answers.push(dns::ResourceRecord::new(
dns::Name::new("_derp_region.iroh.").unwrap(),
dns::CLASS::IN,
30,
dns::rdata::RData::TXT("url=eu.derp.iroh.network:33000".try_into()?),
));

let signed_packet = SignedPacket::from_packet(&keypair, &packet)?;

let client = PkarrClient::new();
client
.relay_put(&Url::parse(relay1).unwrap(), signed_packet)
.await?;

println!("Published {}", keypair.to_uri_string());
}

// Resolver
{
println!("\nResolving {}...\n", keypair.to_uri_string());
let reader = PkarrClient::new();

let instant = Instant::now();

let signed_packet = reader
.relay_get(&Url::parse(relay2).unwrap(), keypair.public_key())
.await?;

println!("Resolved in {:?} \n{}", instant.elapsed(), signed_packet);
}

Ok(())
}
37 changes: 37 additions & 0 deletions pkarr/examples/async/resolve-most-recent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! This example shows how to resolve [ResourceRecord]s directly from the DHT.
//!
//! run this example from the project root:
//! $ cargo run --example resolve <zbase32 encoded key>
use std::time::Instant;

use pkarr::{PkarrClient, PublicKey};

use clap::Parser;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// z32 public_key
public_key: String,
}

#[tokio::main]
async fn main() {
let cli = Cli::parse();

let client = PkarrClient::new();

let str: &str = &cli.public_key;
let public_key: PublicKey = str.try_into().expect("Invalid zbase32 encoded key");

let instant = Instant::now();

println!("\nResolving {} ...", public_key.to_uri_string());

if let Some(signed_packet) = client.resolve_most_recent(public_key).await {
println!("\nResolved in {:?} {}", instant.elapsed(), signed_packet);
} else {
println!("\nFailed to resolve {}", str);
}
}
37 changes: 37 additions & 0 deletions pkarr/examples/async/resolve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! This example shows how to resolve [ResourceRecord]s directly from the DHT.
//!
//! run this example from the project root:
//! $ cargo run --example resolve <zbase32 encoded key>
use std::time::Instant;

use pkarr::{PkarrClient, PublicKey};

use clap::Parser;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// z32 public_key
public_key: String,
}

#[tokio::main]
async fn main() {
let cli = Cli::parse();

let client = PkarrClient::new();

let str: &str = &cli.public_key;
let public_key: PublicKey = str.try_into().expect("Invalid zbase32 encoded key");

let instant = Instant::now();

println!("\nResolving {} ...", public_key.to_uri_string());

if let Some(signed_packet) = client.resolve(public_key).await {
println!("\nResolved in {:?} {}", instant.elapsed(), signed_packet);
} else {
println!("\nFailed to resolve {}", str);
}
}
46 changes: 46 additions & 0 deletions pkarr/examples/publish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! This example shows how to publish [ResourceRecord]s directly to the DHT.
//!
//! Change the `Keypair::random()` to your own keypair to publish your own records.
//! Change the `packet.answers` to your own records.
//!
//! run this example from the project root:
//! $ cargo run --example publish
use std::time::Instant;

use pkarr::{dns, Keypair, PkarrClient, Result, SignedPacket};

fn main() -> Result<()> {
let client = PkarrClient::new();

let keypair = Keypair::random();

let mut packet = dns::Packet::new_reply(0);
packet.answers.push(dns::ResourceRecord::new(
dns::Name::new("_foo").unwrap(),
dns::CLASS::IN,
30,
dns::rdata::RData::TXT("bar".try_into()?),
));

let signed_packet = SignedPacket::from_packet(&keypair, &packet)?;

let instant = Instant::now();

println!("\nPublishing {} ...", keypair.to_uri_string());

match client.publish(&signed_packet) {
Ok(metadata) => {
println!(
"\nSuccessfully published in {:?} to {} nodes",
instant.elapsed(),
metadata.stored_at().len(),
);
}
Err(err) => {
println!("\nFailed to publish {} \n {}", keypair.public_key(), err);
}
};

Ok(())
}
Loading

0 comments on commit fedf68a

Please sign in to comment.