ipns: Implement dnslink.
This commit is contained in:
552
Cargo.lock
generated
552
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
30
Cargo.toml
30
Cargo.toml
@ -6,12 +6,13 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "*"
|
||||
cbor = "*"
|
||||
cid = "*"
|
||||
cbor = { git = "https://github.com/dvc94ch/rust-cbor", branch = "read-data-item" }
|
||||
cid = { git = "https://github.com/multiformats/rust-cid", branch = "master" }
|
||||
domain = { path = "../domain/domain" }
|
||||
env_logger = "*"
|
||||
failure = "*"
|
||||
fnv = "*"
|
||||
futures-preview = { version = "0.3.0-alpha.13", features = ["compat"] }
|
||||
futures-preview = { git = "https://github.com/rust-lang-nursery/futures-rs", branch = "master", features = ["compat"] }
|
||||
libp2p = { version = "*", git = "https://github.com/libp2p/rust-libp2p", rev = "5655624" }
|
||||
log = "*"
|
||||
multibase = "*"
|
||||
@ -25,24 +26,5 @@ rustc-serialize = "0.3"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "0.1", features = ["async-await-preview"] }
|
||||
xdg = "*"
|
||||
|
||||
[patch.crates-io]
|
||||
cbor = { git = "https://github.com/dvc94ch/rust-cbor", branch = "read-data-item" }
|
||||
cid = { git = "https://github.com/dvc94ch/rust-cid", branch = "implement-hash" }
|
||||
tokio = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-async-await = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-codec = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-current-thread = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-executor = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-fs = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-io = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-reactor = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-sync = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-threadpool = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-timer = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-tcp = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-tls = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-udp = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio-uds = { git = "https://github.com/dvc94ch/tokio", branch = "2018-edition" }
|
||||
tokio = { version = "0.1", features = ["async-await-preview"] }
|
||||
xdg = "*"
|
@ -1,5 +1,5 @@
|
||||
#![feature(async_await, await_macro, futures_api)]
|
||||
use ipfs::{Ipfs, IpfsOptions, PeerId, TestTypes};
|
||||
use ipfs::{Ipfs, IpfsOptions, IpfsPath, PeerId, TestTypes};
|
||||
|
||||
fn main() {
|
||||
let options = IpfsOptions::<TestTypes>::default();
|
||||
@ -22,6 +22,14 @@ fn main() {
|
||||
let new_ipfs_path = await!(ipfs.resolve_ipns(&ipns_path)).unwrap();
|
||||
assert_eq!(ipfs_path, new_ipfs_path);
|
||||
|
||||
// Resolve dnslink
|
||||
let ipfs_path = IpfsPath::from_str("/ipns/ipfs.io").unwrap();
|
||||
println!("Resolving {:?}", ipfs_path.to_string());
|
||||
let ipfs_path = await!(ipfs.resolve_ipns(&ipfs_path)).unwrap();
|
||||
println!("Resolved stage 1: {:?}", ipfs_path.to_string());
|
||||
let ipfs_path = await!(ipfs.resolve_ipns(&ipfs_path)).unwrap();
|
||||
println!("Resolved stage 2: {:?}", ipfs_path.to_string());
|
||||
|
||||
ipfs.exit_daemon();
|
||||
});
|
||||
}
|
||||
|
89
src/ipns/dns.rs
Normal file
89
src/ipns/dns.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use crate::error::Error;
|
||||
use crate::path::IpfsPath;
|
||||
use domain::core::bits::{Dname, Question};
|
||||
use domain::core::iana::Rtype;
|
||||
use domain::core::rdata::Txt;
|
||||
use domain::resolv::{Resolver, StubResolver};
|
||||
use domain::resolv::stub::resolver::Query;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Poll, Waker};
|
||||
use std::str::FromStr;
|
||||
use tokio::prelude::{Async, Future as FutureOld, future::SelectOk, future::select_ok};
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
#[fail(display = "no dnslink entry")]
|
||||
pub struct DnsLinkError;
|
||||
|
||||
pub struct DnsLinkFuture {
|
||||
query: SelectOk<Query>,
|
||||
}
|
||||
|
||||
impl Future for DnsLinkFuture {
|
||||
type Output = Result<IpfsPath, Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _waker: &Waker) -> Poll<Self::Output> {
|
||||
let _self = self.get_mut();
|
||||
loop {
|
||||
let poll = _self.query.poll();
|
||||
if poll.is_err() {
|
||||
return Poll::Ready(Err(DnsLinkError.into()));
|
||||
}
|
||||
match poll.unwrap() {
|
||||
Async::Ready((answer, rest)) => {
|
||||
for record in answer.answer()?.limit_to::<Txt>() {
|
||||
let txt = record?;
|
||||
let bytes = txt.data().text();
|
||||
let string = String::from_utf8_lossy(&bytes).to_string();
|
||||
if string.starts_with("dnslink=") {
|
||||
let path = IpfsPath::from_str(&string[8..])?;
|
||||
return Poll::Ready(Ok(path));
|
||||
}
|
||||
}
|
||||
if rest.len() > 0 {
|
||||
_self.query = select_ok(rest);
|
||||
} else {
|
||||
return Poll::Ready(Err(DnsLinkError.into()))
|
||||
}
|
||||
}
|
||||
Async::NotReady => return Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(domain: &str) -> Result<DnsLinkFuture, Error> {
|
||||
let mut dnslink = "_dnslink.".to_string();
|
||||
dnslink.push_str(domain);
|
||||
let qname = Dname::from_str(&dnslink[9..])?;
|
||||
let question = Question::new_in(qname, Rtype::Txt);
|
||||
let query1 = StubResolver::new().query(question);
|
||||
|
||||
let qname = Dname::from_str(&dnslink)?;
|
||||
let question = Question::new_in(qname, Rtype::Txt);
|
||||
let query2 = StubResolver::new().query(question);
|
||||
|
||||
Ok(DnsLinkFuture {
|
||||
query: select_ok(vec![query1, query2]),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_resolve1() {
|
||||
tokio::run_async(async {
|
||||
let res = await!(resolve("ipfs.io").unwrap()).unwrap().to_string();
|
||||
assert_eq!(res, "/ipns/website.ipfs.io");
|
||||
})
|
||||
}
|
||||
|
||||
fn test_resolve2() {
|
||||
tokio::run_async(async {
|
||||
let res = await!(resolve("website.ipfs.io").unwrap()).unwrap().to_string();
|
||||
assert_eq!(res, "/ipfs/QmYfHCcUQBjyvrLfQ8Cnt2YAEiLDNRqMXAeHndM6fDW8yB");
|
||||
})
|
||||
}
|
||||
}
|
@ -5,8 +5,9 @@ use crate::repo::{Repo, RepoTypes};
|
||||
use libp2p::PeerId;
|
||||
use std::future::Future;
|
||||
|
||||
mod ipns_pb;
|
||||
mod dns;
|
||||
mod entry;
|
||||
mod ipns_pb;
|
||||
|
||||
pub struct Ipns<Types: RepoTypes> {
|
||||
repo: Repo<Types>,
|
||||
@ -26,10 +27,17 @@ impl<Types: RepoTypes> Ipns<Types> {
|
||||
let repo = self.repo.clone();
|
||||
let path = path.to_owned();
|
||||
async move {
|
||||
if path.root().is_ipns() {
|
||||
Ok(await!(repo.get_ipns(path.root().peer_id()?))??)
|
||||
} else {
|
||||
Ok(path)
|
||||
match path.root() {
|
||||
PathRoot::Ipld(_) => Ok(path),
|
||||
PathRoot::Ipns(peer_id) => {
|
||||
match await!(repo.get_ipns(peer_id))? {
|
||||
Some(path) => Ok(path),
|
||||
None => bail!("unimplemented"),
|
||||
}
|
||||
},
|
||||
PathRoot::Dns(domain) => {
|
||||
Ok(await!(dns::resolve(domain)?)?)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,12 @@ impl IpfsPath {
|
||||
let root = match (empty, root_type, key) {
|
||||
(Some(""), Some("ipfs"), Some(key)) => PathRoot::Ipld(Cid::from(key)?),
|
||||
(Some(""), Some("ipld"), Some(key)) => PathRoot::Ipld(Cid::from(key)?),
|
||||
(Some(""), Some("ipns"), Some(key)) => PathRoot::Ipns(PeerId::from_str(key).ok()?),
|
||||
(Some(""), Some("ipns"), Some(key)) => {
|
||||
match PeerId::from_str(key).ok() {
|
||||
Some(peer_id) => PathRoot::Ipns(peer_id),
|
||||
None => PathRoot::Dns(key.to_string())
|
||||
}
|
||||
},
|
||||
_ => return Err(IpfsPathError::InvalidPath(string.to_owned()).into()),
|
||||
};
|
||||
let mut path = IpfsPath::new(root);
|
||||
@ -133,6 +138,7 @@ impl TryInto<PeerId> for IpfsPath {
|
||||
pub enum PathRoot {
|
||||
Ipld(Cid),
|
||||
Ipns(PeerId),
|
||||
Dns(String),
|
||||
}
|
||||
|
||||
impl PathRoot {
|
||||
@ -168,6 +174,7 @@ impl PathRoot {
|
||||
let (prefix, key) = match self {
|
||||
PathRoot::Ipld(cid) => ("/ipfs/", cid.to_string()),
|
||||
PathRoot::Ipns(peer_id) => ("/ipns/", peer_id.to_base58()),
|
||||
PathRoot::Dns(domain) => ("/ipns/", domain.to_owned()),
|
||||
};
|
||||
let mut string = prefix.to_string();
|
||||
string.push_str(&key);
|
||||
@ -178,6 +185,7 @@ impl PathRoot {
|
||||
match self {
|
||||
PathRoot::Ipld(cid) => cid.to_bytes(),
|
||||
PathRoot::Ipns(peer_id) => peer_id.as_bytes().to_vec(),
|
||||
PathRoot::Dns(domain) => domain.as_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user