Compare commits
66 Commits
2018ificat
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
86ddae4fc7 | ||
![]() |
8f8948b89d | ||
![]() |
0e8d8a2959 | ||
![]() |
87b140e3b4 | ||
![]() |
fbde12b629 | ||
![]() |
286b06d2c4 | ||
![]() |
1d4ce43a70 | ||
![]() |
771f7d0918 | ||
![]() |
c1bcad33b8 | ||
![]() |
9218edde07 | ||
![]() |
594f82861f | ||
![]() |
21a627af73 | ||
![]() |
675cc7d9d4 | ||
![]() |
f4eae49326 | ||
![]() |
0f3229809c | ||
![]() |
4ed87d4364 | ||
![]() |
4c468456ef | ||
![]() |
9050880415 | ||
![]() |
c88d64b542 | ||
![]() |
fa25e92839 | ||
![]() |
9c1638ae58 | ||
![]() |
962437fde3 | ||
![]() |
b7545178cc | ||
![]() |
f237600542 | ||
![]() |
a58a6dc3e6 | ||
![]() |
6e89b7205e | ||
![]() |
5f57d23847 | ||
![]() |
6b3c1e17d1 | ||
![]() |
c367f94fd4 | ||
![]() |
1d67e9898d | ||
![]() |
0f856e4693 | ||
![]() |
86f9e39a99 | ||
![]() |
b0580f4453 | ||
![]() |
3e088be97f | ||
![]() |
40d016469d | ||
![]() |
8fa702c5aa | ||
![]() |
ae6fdf68e3 | ||
![]() |
f0dd4e5b92 | ||
![]() |
526c3882ae | ||
![]() |
8dccbe45c1 | ||
![]() |
0a885d753e | ||
![]() |
85ab04992a | ||
![]() |
0b17a147b6 | ||
![]() |
1dd099d5cb | ||
![]() |
bd86da9973 | ||
![]() |
e952420fd1 | ||
![]() |
aa6122564f | ||
![]() |
223a1675bd | ||
![]() |
8c71438340 | ||
![]() |
e92f36e54d | ||
![]() |
1605fa03e1 | ||
![]() |
65c4729f8d | ||
![]() |
14d587d71d | ||
![]() |
606539db64 | ||
![]() |
5715a66e77 | ||
![]() |
4bfa880554 | ||
![]() |
4f8a6abc10 | ||
![]() |
a18dadda5e | ||
![]() |
5bc995a072 | ||
![]() |
1f5fa1793a | ||
![]() |
9f95a08e5f | ||
![]() |
c23a90ca1c | ||
![]() |
70fd0f7758 | ||
![]() |
3174fd9a6f | ||
![]() |
0c26d824ed | ||
![]() |
6bdfca69ef |
66
.github/workflows/sync.yaml
vendored
Normal file
66
.github/workflows/sync.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# GitHub Actions workflow file to sync an external repository to this GitHub mirror.
|
||||
# This file was automatically generated by go-github-sync.
|
||||
#
|
||||
# The workflow does the following:
|
||||
# - Runs on a scheduled basis (and can also be triggered manually)
|
||||
# - Clones the GitHub mirror repository
|
||||
# - Fetches changes from the primary external repository
|
||||
# - Applies those changes to the mirror repository
|
||||
# - Pushes the updated content back to the GitHub mirror
|
||||
#
|
||||
# Authentication is handled by the GITHUB_TOKEN secret provided by GitHub Actions.
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate Github Actions Environment
|
||||
run: if [ "$GITHUB_ACTIONS" != "true" ]; then echo 'This script must be run in a GitHub Actions environment.'; exit 1; fi
|
||||
- name: Checkout GitHub Mirror
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Configure Git
|
||||
run: |-
|
||||
git config user.name 'GitHub Actions'
|
||||
git config user.email 'actions@github.com'
|
||||
- env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: Sync Primary Repository
|
||||
run: |-
|
||||
# Add the primary repository as a remote
|
||||
git remote add primary https://i2pgit.org/I2P_Developers/i2p-rs.git
|
||||
|
||||
# Fetch the latest changes from the primary repository
|
||||
git fetch primary
|
||||
|
||||
# Check if the primary branch exists in the primary repository
|
||||
if git ls-remote --heads primary master | grep -q master; then
|
||||
echo "Primary branch master found in primary repository"
|
||||
else
|
||||
echo "Error: Primary branch master not found in primary repository"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if we're already on the mirror branch
|
||||
if git rev-parse --verify --quiet master; then
|
||||
git checkout master
|
||||
else
|
||||
# Create the mirror branch if it doesn't exist
|
||||
git checkout -b master
|
||||
fi
|
||||
|
||||
|
||||
# Force-apply all changes from primary, overriding any conflicts
|
||||
echo "Performing force sync from primary/master to master"
|
||||
git reset --hard primary/master
|
||||
|
||||
|
||||
# Push changes back to the mirror repository
|
||||
git push origin master
|
||||
name: Sync Primary Repository to GitHub Mirror
|
||||
"on":
|
||||
push: {}
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
workflow_dispatch: {}
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ Cargo.lock
|
||||
auto-save-list
|
||||
tramp
|
||||
.\#*
|
||||
*.swp
|
||||
|
21
Cargo.toml
21
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "i2p"
|
||||
version = "0.0.1"
|
||||
version = "0.1.1"
|
||||
authors = ["Jack Grigg <str4d@i2pmail.org>"]
|
||||
description = "I2P client library with a std::net-like API"
|
||||
homepage = "https://github.com/i2p/i2p-rs"
|
||||
@@ -11,10 +11,23 @@ keywords = ["i2p", "net", "network", "sam"]
|
||||
license = "MIT"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = ["public-conn"]
|
||||
public-conn = []
|
||||
[badges]
|
||||
travis-ci = { repository = "i2p/i2p-rs" }
|
||||
|
||||
[dependencies]
|
||||
log = "0.3.7"
|
||||
nom = "^2.2"
|
||||
rand = "0.3.15"
|
||||
data-encoding = "2.1.2"
|
||||
failure = "0.1"
|
||||
failure_derive = "0.1"
|
||||
lazy_static = "1.3.0"
|
||||
log = "0.4.6"
|
||||
nom = "^4.2"
|
||||
rand = "0.5"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
sha2 = "0.8.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.5"
|
||||
|
3
Makefile
Normal file
3
Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
find -type f -name "*.rs" -not -path "*target*" -exec rustfmt --edition 2018 {} \;
|
@@ -1,40 +0,0 @@
|
||||
use i2p::net::I2pStream;
|
||||
use std::env;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
|
||||
fn help() {
|
||||
println!("Usage: eepget <host.i2p> [port]")
|
||||
}
|
||||
|
||||
fn print_homepage(host: &str, port: u16) {
|
||||
let mut stream = I2pStream::connect(format!("{}:{}", host, port)).unwrap();
|
||||
|
||||
let msg = "GET / HTTP/1.1\r\n\r\n";
|
||||
let _ = stream.write(msg.as_bytes());
|
||||
let mut reader = BufReader::new(stream);
|
||||
let mut buffer = String::new();
|
||||
let _ = reader.read_to_string(&mut buffer);
|
||||
|
||||
println!("{}", buffer);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
match args.len() {
|
||||
2 => print_homepage(&args[1], 80),
|
||||
3 => {
|
||||
let host = &args[1];
|
||||
let port = &args[2];
|
||||
let port_num: u16 = match port.parse() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
println!("Port must be an integer");
|
||||
help();
|
||||
return;
|
||||
}
|
||||
};
|
||||
print_homepage(host, port_num)
|
||||
}
|
||||
_ => help(),
|
||||
}
|
||||
}
|
14
examples/eepget/Cargo.toml
Normal file
14
examples/eepget/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "eepget"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
i2p = {path = "../../", version = "0.1.0"}
|
||||
env_logger = "0.5"
|
||||
|
||||
[[bin]]
|
||||
name = "eepget"
|
||||
path = "src/main.rs"
|
42
examples/eepget/src/main.rs
Normal file
42
examples/eepget/src/main.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use env_logger;
|
||||
use i2p::net::I2pStream;
|
||||
use std::env;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
|
||||
fn help() {
|
||||
println!("Usage: eepget <host.i2p> [port]")
|
||||
}
|
||||
|
||||
fn print_homepage(host: &str, port: u16) {
|
||||
let mut stream = I2pStream::connect(format!("{}:{}", host, port)).unwrap();
|
||||
|
||||
let msg = "GET / HTTP/1.1\r\n\r\n";
|
||||
let _ = stream.write(msg.as_bytes());
|
||||
let mut reader = BufReader::new(stream);
|
||||
let mut buffer = String::new();
|
||||
let _ = reader.read_to_string(&mut buffer);
|
||||
|
||||
println!("{}", buffer);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
let args: Vec<String> = env::args().collect();
|
||||
match args.len() {
|
||||
2 => print_homepage(&args[1], 80),
|
||||
3 => {
|
||||
let host = &args[1];
|
||||
let port = &args[2];
|
||||
let port_num: u16 = match port.parse() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
println!("Port must be an integer");
|
||||
help();
|
||||
return;
|
||||
}
|
||||
};
|
||||
print_homepage(host, port_num)
|
||||
}
|
||||
_ => help(),
|
||||
}
|
||||
}
|
14
examples/gen_dest/Cargo.toml
Normal file
14
examples/gen_dest/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "gen_gest"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
i2p = {path = "../../", version = "0.1.0"}
|
||||
env_logger = "0.5"
|
||||
|
||||
[[bin]]
|
||||
name = "gen_dest"
|
||||
path = "src/main.rs"
|
20
examples/gen_dest/src/main.rs
Normal file
20
examples/gen_dest/src/main.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
extern crate env_logger;
|
||||
extern crate i2p;
|
||||
extern crate log;
|
||||
|
||||
use log::*;
|
||||
use std::io::{Read, Write};
|
||||
use std::str::from_utf8;
|
||||
use std::{thread, time};
|
||||
|
||||
use i2p::sam::{SamConnection, DEFAULT_API};
|
||||
|
||||
// Run with RUST_LOG=debug to see the action
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let mut sam_conn = SamConnection::connect(DEFAULT_API).unwrap();
|
||||
let (pubkey, seckey) = sam_conn.generate_destination().unwrap();
|
||||
println!("New public key: {}", pubkey);
|
||||
println!("New secret key: {}", seckey);
|
||||
}
|
18
examples/hello_world/Cargo.toml
Normal file
18
examples/hello_world/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "hello_world"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
i2p = {path = "../../", version = "0.1.0"}
|
||||
env_logger = "0.5"
|
||||
log = "0.4"
|
||||
crossbeam = "0.8"
|
||||
crossbeam-utils = "0.8"
|
||||
crossbeam-channel = "0.5"
|
||||
tokio = {version = "1.17.0", features = ["full"]}
|
||||
[[bin]]
|
||||
name = "hello_world"
|
||||
path = "src/main.rs"
|
5
examples/hello_world/README.md
Normal file
5
examples/hello_world/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Usage
|
||||
|
||||
```shell
|
||||
$> RUST_LOG=debug cargo run
|
||||
```
|
191
examples/hello_world/src/main.rs
Normal file
191
examples/hello_world/src/main.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use crossbeam::sync::WaitGroup;
|
||||
use env_logger;
|
||||
use i2p;
|
||||
|
||||
use crossbeam_channel::select;
|
||||
use i2p::net::{I2pListener, I2pStream};
|
||||
use i2p::sam_options::{
|
||||
I2CPClientOptions, I2CPOptions, I2CPRouterOptions, I2CPTunnelInboundOptions,
|
||||
I2CPTunnelOutboundOptions, SAMOptions, SignatureType,
|
||||
};
|
||||
use log::*;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::Shutdown;
|
||||
use std::str::from_utf8;
|
||||
use std::{thread, time};
|
||||
|
||||
use i2p::sam::{SamConnection, SessionStyle, DEFAULT_API};
|
||||
|
||||
// Run with RUST_LOG=debug to see the action
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::init();
|
||||
let (pubkey, seckey) = {
|
||||
let mut sam_conn = SamConnection::connect(DEFAULT_API).unwrap();
|
||||
sam_conn
|
||||
.generate_destination(SignatureType::EdDsaSha512Ed25519)
|
||||
.unwrap()
|
||||
};
|
||||
info!("New public key: {}", pubkey);
|
||||
info!("New secret key: {}", seckey);
|
||||
// thread synchronization primitive
|
||||
let wg = WaitGroup::new();
|
||||
// message channel primitive
|
||||
let (tx, rx) = crossbeam_channel::bounded::<bool>(1);
|
||||
{
|
||||
let sam_session = i2p::sam::Session::create(
|
||||
DEFAULT_API,
|
||||
seckey.as_str(),
|
||||
"hello_world",
|
||||
SessionStyle::Stream,
|
||||
SAMOptions {
|
||||
i2cp_options: Some(I2CPOptions {
|
||||
router_options: Some(I2CPRouterOptions {
|
||||
inbound: Some(I2CPTunnelInboundOptions {
|
||||
length: Some(1),
|
||||
quantity: Some(2),
|
||||
backup_quantity: Some(2),
|
||||
..Default::default()
|
||||
}),
|
||||
outbound: Some(I2CPTunnelOutboundOptions {
|
||||
length: Some(1),
|
||||
quantity: Some(2),
|
||||
backup_quantity: Some(2),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
signature_type: SignatureType::EdDsaSha512Ed25519,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let local_dest = i2p::net::I2pAddr::from_b64(&sam_session.local_dest).unwrap();
|
||||
info!("local_dest {}", local_dest);
|
||||
let rx = rx;
|
||||
let listener = match I2pListener::bind_with_session(&sam_session) {
|
||||
Ok(listener) => listener,
|
||||
Err(err) => panic!("failed to establish listener with session {:#?}", err),
|
||||
};
|
||||
// force the connection into non-blocking mode
|
||||
sam_session.sam.set_nonblocking(true).unwrap();
|
||||
// akin to Golang's sync.WaitGroup, and allows synchronizing execution across different threads
|
||||
// particularly useful for enabling graceful shutdowns within server applications
|
||||
let wg = wg.clone();
|
||||
// spawns a background task, somewhat similar to Golang's goroutines
|
||||
tokio::task::spawn_blocking(move || {
|
||||
loop {
|
||||
// check to see if we received a message through the crossbeam channel
|
||||
// if not, immediately activate default case, which will continue onto
|
||||
// the following listener.accept() statement
|
||||
select! {
|
||||
recv(rx) -> _msg => {
|
||||
warn!("server received exit signal, goodbye...");
|
||||
match sam_session.sam.conn.shutdown(Shutdown::Both) {
|
||||
Ok(_) => info!("server shutdown ok"),
|
||||
Err(err) => error!("server failed to properly shutdown {:#?}", err)
|
||||
}
|
||||
drop(wg);
|
||||
return;
|
||||
},
|
||||
default() => {}
|
||||
}
|
||||
match listener.accept() {
|
||||
Ok((mut incoming_conn, conn_addr)) => {
|
||||
info!("server accepted connection from {}", conn_addr);
|
||||
let mut buf = [0_u8; 512];
|
||||
match incoming_conn.read(&mut buf) {
|
||||
Ok(n) => {
|
||||
// dont do this outside of an example
|
||||
unsafe {
|
||||
info!(
|
||||
"server read {} bytes. msg {}",
|
||||
n,
|
||||
String::from_utf8_unchecked(buf[0..n].to_vec())
|
||||
.replace("\n", "")
|
||||
);
|
||||
}
|
||||
match incoming_conn.write(&buf[0..n]) {
|
||||
Ok(n) => {
|
||||
info!("server wrote {} bytes", n)
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"server failed to write response for {}: {:#?}",
|
||||
conn_addr, err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("server failed to read data from {}: {:#?}", conn_addr, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("server failed to accept connection {:#?}", err);
|
||||
}
|
||||
}
|
||||
// because we called spawn_blocking this wont block main thread
|
||||
std::thread::sleep(std::time::Duration::from_millis(125));
|
||||
}
|
||||
});
|
||||
}
|
||||
info!("waiting 10 seconds for tunnel things to happen");
|
||||
// because we used tokio::task::spawn_blocking, we can sleep here
|
||||
// or in the spawned task without either sleeping blocking a thread
|
||||
// and pausing execution.
|
||||
std::thread::sleep(std::time::Duration::from_secs(10));
|
||||
let mut client_conn = match I2pStream::connect(&format!("{}:0", pubkey)) {
|
||||
Ok(client_conn) => client_conn,
|
||||
Err(err) => {
|
||||
if let Err(err) = tx.send(true) {
|
||||
error!("client failed to signal server task to exit {:#?}", err);
|
||||
}
|
||||
panic!(
|
||||
"client failed to connect to destination {}, {:#?}",
|
||||
i2p::net::I2pAddr::from_b64(&pubkey).unwrap(),
|
||||
err
|
||||
);
|
||||
}
|
||||
};
|
||||
match client_conn.write(b"hello_world") {
|
||||
Ok(n) => info!("client wrote {} bytes", n),
|
||||
Err(err) => {
|
||||
if let Err(err) = tx.send(true) {
|
||||
error!("client failed to signal server task to exit {:#?}", err);
|
||||
}
|
||||
panic!("client failed to write into stream {:#?}", err);
|
||||
}
|
||||
}
|
||||
let mut buf = [0_u8; 512];
|
||||
match client_conn.read(&mut buf) {
|
||||
Ok(n) => unsafe {
|
||||
info!(
|
||||
"client read {} bytes. msg {}",
|
||||
n,
|
||||
String::from_utf8_unchecked(buf[0..n].to_vec()).replace("\n", "")
|
||||
);
|
||||
},
|
||||
Err(err) => {
|
||||
if let Err(err) = tx.send(true) {
|
||||
error!("client failed to signal server task to exit {:#?}", err);
|
||||
}
|
||||
panic!("client failed to read from stream {:#?}", err);
|
||||
}
|
||||
}
|
||||
// send a message through the channel to notify the tokio task
|
||||
// to exit
|
||||
if let Err(err) = tx.send(true) {
|
||||
info!("client failed to signal server task to exit {:#?}", err);
|
||||
}
|
||||
// wait for all references to the wait group to be dropped
|
||||
wg.wait();
|
||||
match client_conn.shutdown(Shutdown::Both) {
|
||||
Ok(_) => info!("client shutdown ok"),
|
||||
Err(err) => error!("client failed to properly shutdown {:#?}", err),
|
||||
}
|
||||
info!("all background processes exited, goodbye...");
|
||||
}
|
15
examples/selfserve/Cargo.toml
Normal file
15
examples/selfserve/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "selfserve"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
i2p = {path = "../../", version = "0.1.0"}
|
||||
env_logger = "0.5"
|
||||
log = "0.4.6"
|
||||
|
||||
[[bin]]
|
||||
name = "selfserve"
|
||||
path = "src/main.rs"
|
42
examples/selfserve/src/main.rs
Normal file
42
examples/selfserve/src/main.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use env_logger;
|
||||
use log::*;
|
||||
use std::io::{Read, Write};
|
||||
use std::str::from_utf8;
|
||||
use std::{thread, time};
|
||||
|
||||
use i2p::net::{I2pListener, I2pStream};
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
// start a TCP server that will get forwards from i2p
|
||||
let server = I2pListener::bind().unwrap();
|
||||
let our_dest = server.local_addr().unwrap();
|
||||
thread::spawn(move || {
|
||||
for stream in server.incoming() {
|
||||
match stream {
|
||||
Ok(mut stream) => {
|
||||
thread::spawn(move || {
|
||||
let mut buffer = [0; 100];
|
||||
loop {
|
||||
let n = stream.read(&mut buffer).unwrap();
|
||||
info!("< {:?}", from_utf8(&buffer[0..n]).unwrap());
|
||||
stream.write("pong".as_bytes()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => error!("Error on incoming connection: {:?}", e),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
thread::sleep(time::Duration::from_millis(1000));
|
||||
|
||||
// connect through i2p to our local destination
|
||||
let mut client = I2pStream::connect(our_dest).unwrap();
|
||||
let msg = "ping";
|
||||
client.write(msg.as_bytes()).unwrap();
|
||||
let mut buffer = [0; 100];
|
||||
let n = client.read(&mut buffer).unwrap();
|
||||
info!("> {:?}", from_utf8(&buffer[0..n]).unwrap());
|
||||
}
|
18
examples/session_watcher/Cargo.toml
Normal file
18
examples/session_watcher/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "session_watcher"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
i2p = {path = "../../", version = "0.1.0"}
|
||||
env_logger = "0.5"
|
||||
log = "0.4"
|
||||
crossbeam = "0.8"
|
||||
crossbeam-utils = "0.8"
|
||||
crossbeam-channel = "0.5"
|
||||
tokio = {version = "1.17.0", features = ["full"]}
|
||||
[[bin]]
|
||||
name = "session_watcher"
|
||||
path = "src/main.rs"
|
5
examples/session_watcher/README.md
Normal file
5
examples/session_watcher/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Usage
|
||||
|
||||
```shell
|
||||
$> RUST_LOG=debug cargo run
|
||||
```
|
49
examples/session_watcher/src/main.rs
Normal file
49
examples/session_watcher/src/main.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use crossbeam::sync::WaitGroup;
|
||||
use env_logger;
|
||||
use i2p;
|
||||
|
||||
use crossbeam_channel::select;
|
||||
use i2p::net::{I2pListener, I2pStream};
|
||||
use i2p::sam_options::{
|
||||
I2CPClientOptions, I2CPOptions, I2CPRouterOptions, I2CPTunnelInboundOptions,
|
||||
I2CPTunnelOutboundOptions, SAMOptions, SignatureType,
|
||||
};
|
||||
use log::*;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::Shutdown;
|
||||
use std::str::from_utf8;
|
||||
use std::{thread, time};
|
||||
|
||||
use i2p::sam::{SamConnection, SessionStyle, DEFAULT_API};
|
||||
|
||||
// Run with RUST_LOG=debug to see the action
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::init();
|
||||
let (pubkey, seckey) = {
|
||||
let mut sam_conn = SamConnection::connect(DEFAULT_API).unwrap();
|
||||
sam_conn
|
||||
.generate_destination(SignatureType::EdDsaSha512Ed25519)
|
||||
.unwrap()
|
||||
};
|
||||
info!("New public key: {}", pubkey);
|
||||
info!("New secret key: {}", seckey);
|
||||
let mut watcher = i2p::session_watcher::SamSessionWatcher::new(
|
||||
DEFAULT_API,
|
||||
&seckey,
|
||||
SessionStyle::Stream,
|
||||
Default::default()
|
||||
).unwrap();
|
||||
|
||||
loop {
|
||||
match watcher.accept() {
|
||||
Ok((conn, addr)) => {
|
||||
info!("receiving incoming connection {}", addr);
|
||||
let _ = conn.shutdown(Shutdown::Both).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
error!("failed to accept connection {:#?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@@ -0,0 +1 @@
|
||||
hard_tabs = true
|
117
src/error.rs
Normal file
117
src/error.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use std::fmt::{self, Display};
|
||||
use std::io;
|
||||
|
||||
use failure::{Backtrace, Context, Fail};
|
||||
use nom;
|
||||
|
||||
/// I2P/SAM error definition
|
||||
#[derive(Debug, Fail)]
|
||||
pub struct Error {
|
||||
inner: Context<ErrorKind>,
|
||||
}
|
||||
|
||||
/// Kinds of I2P/SAM errors
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
|
||||
pub enum ErrorKind {
|
||||
/// Wraps io errors
|
||||
#[fail(display = "IO error occurred (is i2p running?): {}", _0)]
|
||||
Io(String),
|
||||
/// Wraps nom parser errors
|
||||
#[fail(display = "Failed to parse an I2P/SAM message")]
|
||||
MessageParsing,
|
||||
#[fail(display = "Failed to parse an I2P/SAM message")]
|
||||
UnresolvableAddress,
|
||||
#[fail(display = "Invalid or unrecognized I2P/SAM message: {}", _0)]
|
||||
SAMInvalidMessage(String),
|
||||
#[fail(display = "Can't reach peer: {}", _0)]
|
||||
SAMCantReachPeer(String),
|
||||
#[fail(display = "Destination key not found: {}", _0)]
|
||||
SAMKeyNotFound(String),
|
||||
#[fail(display = "Peer not found: {}", _0)]
|
||||
SAMPeerNotFound(String),
|
||||
#[fail(display = "Duplicate peer destination: {}", _0)]
|
||||
SAMDuplicatedDest(String),
|
||||
#[fail(display = "Invalid destination key: {}", _0)]
|
||||
SAMInvalidKey(String),
|
||||
#[fail(display = "Invalid stream id: {}", _0)]
|
||||
SAMInvalidId(String),
|
||||
#[fail(display = "I2P/SAM Timeout: {}", _0)]
|
||||
SAMTimeout(String),
|
||||
#[fail(display = "Unknown I2P/SAM error: {}", _0)]
|
||||
SAMI2PError(String),
|
||||
#[fail(display = "I2P address isn't a valid b32 or b64 encoding: {}", _0)]
|
||||
BadAddressEncoding(String),
|
||||
#[fail(display = "Accept encountered error, and session was recreated. try operation again")]
|
||||
SessionRecreated,
|
||||
}
|
||||
|
||||
impl ErrorKind {
|
||||
pub fn to_err(self) -> Error {
|
||||
Error {
|
||||
inner: Context::new(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let cause = match self.cause() {
|
||||
Some(c) => format!("{}", c),
|
||||
None => String::from("Unknown"),
|
||||
};
|
||||
let backtrace = match self.backtrace() {
|
||||
Some(b) => format!("{}", b),
|
||||
None => String::from("Unknown"),
|
||||
};
|
||||
let output = format!(
|
||||
"{} \n Cause: {} \n Backtrace: {}",
|
||||
self.inner, cause, backtrace
|
||||
);
|
||||
Display::fmt(&output, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// get kind
|
||||
pub fn kind(&self) -> ErrorKind {
|
||||
self.inner.get_context().clone()
|
||||
}
|
||||
/// get cause
|
||||
pub fn cause(&self) -> Option<&dyn Fail> {
|
||||
self.inner.cause()
|
||||
}
|
||||
/// get backtrace
|
||||
pub fn backtrace(&self) -> Option<&Backtrace> {
|
||||
self.inner.backtrace()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ErrorKind> for Error {
|
||||
fn from(kind: ErrorKind) -> Error {
|
||||
Error {
|
||||
inner: Context::new(kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Context<ErrorKind>> for Error {
|
||||
fn from(inner: Context<ErrorKind>) -> Error {
|
||||
Error { inner: inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::Io(err.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E> From<nom::Err<I, E>> for Error {
|
||||
fn from(_err: nom::Err<I, E>) -> Error {
|
||||
Error {
|
||||
inner: Context::new(ErrorKind::MessageParsing),
|
||||
}
|
||||
}
|
||||
}
|
11
src/lib.rs
11
src/lib.rs
@@ -1,9 +1,10 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod error;
|
||||
pub mod net;
|
||||
pub mod sam;
|
||||
pub mod sam_options;
|
||||
pub mod session_watcher;
|
||||
|
||||
mod parsers;
|
||||
|
||||
pub use crate::error::{Error, ErrorKind};
|
||||
pub use crate::sam::{SamConnection, Session};
|
||||
|
419
src/net/addr.rs
419
src/net/addr.rs
@@ -1,125 +1,102 @@
|
||||
use std::fmt;
|
||||
use std::hash;
|
||||
use std::io;
|
||||
use std::iter;
|
||||
use std::option;
|
||||
use std::slice;
|
||||
use std::vec;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::net::i2p::I2pAddr;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
|
||||
pub struct I2pSocketAddr {
|
||||
port: u16,
|
||||
dest: I2pAddr,
|
||||
port: u16,
|
||||
dest: I2pAddr,
|
||||
}
|
||||
|
||||
impl I2pSocketAddr {
|
||||
/// Creates a new socket address from the (dest, port) pair.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// assert_eq!(socket.dest(), I2pAddr::new("example.i2p"));
|
||||
/// assert_eq!(socket.port(), 8080);
|
||||
/// ```
|
||||
pub fn new(dest: I2pAddr, port: u16) -> I2pSocketAddr {
|
||||
I2pSocketAddr {
|
||||
port: port,
|
||||
dest: dest,
|
||||
}
|
||||
}
|
||||
/// Creates a new socket address from the (dest, port) pair.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// assert_eq!(socket.dest(), I2pAddr::new("example.i2p"));
|
||||
/// assert_eq!(socket.port(), 8080);
|
||||
/// ```
|
||||
pub fn new(dest: I2pAddr, port: u16) -> I2pSocketAddr {
|
||||
I2pSocketAddr {
|
||||
port: port,
|
||||
dest: dest,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the I2P address associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// assert_eq!(socket.dest(), I2pAddr::new("example.i2p"));
|
||||
/// ```
|
||||
pub fn dest(&self) -> I2pAddr {
|
||||
self.dest.clone()
|
||||
}
|
||||
/// Returns the I2P address associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// assert_eq!(socket.dest(), I2pAddr::new("example.i2p"));
|
||||
/// ```
|
||||
pub fn dest(&self) -> I2pAddr {
|
||||
self.dest.clone()
|
||||
}
|
||||
|
||||
/// Change the I2P address associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let mut socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// socket.set_dest(I2pAddr::new("foobar.i2p"));
|
||||
/// assert_eq!(socket.dest(), I2pAddr::new("foobar.i2p"));
|
||||
/// ```
|
||||
pub fn set_dest(&mut self, new_dest: I2pAddr) {
|
||||
self.dest = new_dest;
|
||||
}
|
||||
/// Change the I2P address associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let mut socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// socket.set_dest(I2pAddr::new("foobar.i2p"));
|
||||
/// assert_eq!(socket.dest(), I2pAddr::new("foobar.i2p"));
|
||||
/// ```
|
||||
pub fn set_dest(&mut self, new_dest: I2pAddr) {
|
||||
self.dest = new_dest;
|
||||
}
|
||||
|
||||
/// Returns the port number associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// assert_eq!(socket.port(), 8080);
|
||||
/// ```
|
||||
pub fn port(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
/// Returns the port number associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// assert_eq!(socket.port(), 8080);
|
||||
/// ```
|
||||
pub fn port(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
|
||||
/// Change the port number associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let mut socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// socket.set_port(1025);
|
||||
/// assert_eq!(socket.port(), 1025);
|
||||
/// ```
|
||||
pub fn set_port(&mut self, new_port: u16) {
|
||||
self.port = new_port;
|
||||
}
|
||||
/// Change the port number associated with this socket address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr};
|
||||
///
|
||||
/// let mut socket = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080);
|
||||
/// socket.set_port(1025);
|
||||
/// assert_eq!(socket.port(), 1025);
|
||||
/// ```
|
||||
pub fn set_port(&mut self, new_port: u16) {
|
||||
self.port = new_port;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for I2pSocketAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.dest(), self.port())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for I2pSocketAddr {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for I2pSocketAddr {
|
||||
fn clone(&self) -> I2pSocketAddr {
|
||||
I2pSocketAddr::new(self.dest.clone(), self.port)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for I2pSocketAddr {
|
||||
fn eq(&self, other: &I2pSocketAddr) -> bool {
|
||||
self.port == other.port && self.dest == other.dest
|
||||
}
|
||||
}
|
||||
impl Eq for I2pSocketAddr {}
|
||||
|
||||
impl hash::Hash for I2pSocketAddr {
|
||||
fn hash<H: hash::Hasher>(&self, s: &mut H) {
|
||||
(self.port, &self.dest).hash(s)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.dest(), self.port())
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for objects which can be converted or resolved to one or more
|
||||
@@ -169,168 +146,168 @@ impl hash::Hash for I2pSocketAddr {
|
||||
///
|
||||
/// // I2pListener::bind(), I2pDatagramSocket::bind() and I2pDatagramSocket::send_to()
|
||||
/// // behave similarly
|
||||
/// let i2p_l = I2pListener::bind("localhost:12345");
|
||||
/// let i2p_l = I2pListener::bind();
|
||||
///
|
||||
/// let mut i2p_dg_s = I2pDatagramSocket::bind(("127.0.0.1", port)).unwrap();
|
||||
/// i2p_dg_s.send_to(&[7], (dest, 23451)).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub trait ToI2pSocketAddrs {
|
||||
/// Returned iterator over socket addresses which this type may correspond
|
||||
/// to.
|
||||
type Iter: Iterator<Item = I2pSocketAddr>;
|
||||
/// Returned iterator over socket addresses which this type may correspond
|
||||
/// to.
|
||||
type Iter: Iterator<Item = I2pSocketAddr>;
|
||||
|
||||
/// Converts this object to an iterator of resolved `I2pSocketAddr`s.
|
||||
///
|
||||
/// The returned iterator may not actually yield any values depending on the
|
||||
/// outcome of any resolution performed.
|
||||
///
|
||||
/// Note that this function may block the current thread while resolution is
|
||||
/// performed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Any errors encountered during resolution will be returned as an `Err`.
|
||||
fn to_socket_addrs(&self) -> io::Result<Self::Iter>;
|
||||
/// Converts this object to an iterator of resolved `I2pSocketAddr`s.
|
||||
///
|
||||
/// The returned iterator may not actually yield any values depending on the
|
||||
/// outcome of any resolution performed.
|
||||
///
|
||||
/// Note that this function may block the current thread while resolution is
|
||||
/// performed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Any errors encountered during resolution will be returned as an `Err`.
|
||||
fn to_socket_addrs(&self) -> io::Result<Self::Iter>;
|
||||
}
|
||||
|
||||
impl ToI2pSocketAddrs for I2pSocketAddr {
|
||||
type Iter = option::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<I2pSocketAddr>> {
|
||||
Ok(Some(self.clone()).into_iter())
|
||||
}
|
||||
type Iter = option::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<I2pSocketAddr>> {
|
||||
Ok(Some(self.clone()).into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToI2pSocketAddrs for (I2pAddr, u16) {
|
||||
type Iter = option::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<I2pSocketAddr>> {
|
||||
let (dest, port) = self.clone();
|
||||
I2pSocketAddr::new(dest, port).to_socket_addrs()
|
||||
}
|
||||
type Iter = option::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<I2pSocketAddr>> {
|
||||
let (dest, port) = self.clone();
|
||||
I2pSocketAddr::new(dest, port).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToI2pSocketAddrs for (&'a str, u16) {
|
||||
type Iter = vec::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<I2pSocketAddr>> {
|
||||
let (host, port) = *self;
|
||||
let addr = I2pSocketAddr::new(I2pAddr::new(host), port);
|
||||
Ok(vec![addr].into_iter())
|
||||
}
|
||||
type Iter = vec::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<I2pSocketAddr>> {
|
||||
let (host, port) = *self;
|
||||
let addr = I2pSocketAddr::new(I2pAddr::new(host), port);
|
||||
Ok(vec![addr].into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
// accepts strings like 'example.i2p:12345'
|
||||
impl ToI2pSocketAddrs for str {
|
||||
type Iter = vec::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<I2pSocketAddr>> {
|
||||
macro_rules! try_opt {
|
||||
($e:expr, $msg:expr) => {
|
||||
match $e {
|
||||
Some(r) => r,
|
||||
None => return Err(io::Error::new(io::ErrorKind::InvalidInput, $msg)),
|
||||
}
|
||||
};
|
||||
}
|
||||
type Iter = vec::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<I2pSocketAddr>> {
|
||||
macro_rules! try_opt {
|
||||
($e:expr, $msg:expr) => {
|
||||
match $e {
|
||||
Some(r) => r,
|
||||
None => return Err(io::Error::new(io::ErrorKind::InvalidInput, $msg)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// split the string by ':' and convert the second part to u16
|
||||
let mut parts_iter = self.rsplitn(2, ':');
|
||||
let port_str = try_opt!(parts_iter.next(), "invalid I2P socket address");
|
||||
let host = try_opt!(parts_iter.next(), "invalid I2P socket address");
|
||||
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
|
||||
(host, port).to_socket_addrs()
|
||||
}
|
||||
// split the string by ':' and convert the second part to u16
|
||||
let mut parts_iter = self.rsplitn(2, ':');
|
||||
let port_str = try_opt!(parts_iter.next(), "invalid I2P socket address");
|
||||
let host = try_opt!(parts_iter.next(), "invalid I2P socket address");
|
||||
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
|
||||
(host, port).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToI2pSocketAddrs for &'a [I2pSocketAddr] {
|
||||
type Iter = iter::Cloned<slice::Iter<'a, I2pSocketAddr>>;
|
||||
type Iter = iter::Cloned<slice::Iter<'a, I2pSocketAddr>>;
|
||||
|
||||
fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
|
||||
Ok(self.iter().cloned())
|
||||
}
|
||||
fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
|
||||
Ok(self.iter().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ToI2pSocketAddrs + ?Sized> ToI2pSocketAddrs for &'a T {
|
||||
type Iter = T::Iter;
|
||||
fn to_socket_addrs(&self) -> io::Result<T::Iter> {
|
||||
(**self).to_socket_addrs()
|
||||
}
|
||||
type Iter = T::Iter;
|
||||
fn to_socket_addrs(&self) -> io::Result<T::Iter> {
|
||||
(**self).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToI2pSocketAddrs for String {
|
||||
type Iter = vec::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<I2pSocketAddr>> {
|
||||
(&**self).to_socket_addrs()
|
||||
}
|
||||
type Iter = vec::IntoIter<I2pSocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<I2pSocketAddr>> {
|
||||
(&**self).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::net::test::{isa, tsa};
|
||||
use crate::net::*;
|
||||
use crate::net::test::{isa, tsa};
|
||||
use crate::net::*;
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_i2paddr_u16() {
|
||||
let a = I2pAddr::new("example.i2p");
|
||||
let p = 12345;
|
||||
let e = I2pSocketAddr::new(a.clone(), p);
|
||||
assert_eq!(Ok(vec![e]), tsa((a, p)));
|
||||
}
|
||||
#[test]
|
||||
fn to_socket_addr_i2paddr_u16() {
|
||||
let a = I2pAddr::new("example.i2p");
|
||||
let p = 12345;
|
||||
let e = I2pSocketAddr::new(a.clone(), p);
|
||||
assert_eq!(Ok(vec![e]), tsa((a, p)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_str_u16() {
|
||||
let a = isa(I2pAddr::new("example.i2p"), 24352);
|
||||
assert_eq!(Ok(vec![a]), tsa(("example.i2p", 24352)));
|
||||
#[test]
|
||||
fn to_socket_addr_str_u16() {
|
||||
let a = isa(I2pAddr::new("example.i2p"), 24352);
|
||||
assert_eq!(Ok(vec![a]), tsa(("example.i2p", 24352)));
|
||||
|
||||
let a = isa(I2pAddr::new("example.i2p"), 23924);
|
||||
assert!(tsa(("example.i2p", 23924)).unwrap().contains(&a));
|
||||
}
|
||||
let a = isa(I2pAddr::new("example.i2p"), 23924);
|
||||
assert!(tsa(("example.i2p", 23924)).unwrap().contains(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_str() {
|
||||
let a = isa(I2pAddr::new("example.i2p"), 24352);
|
||||
assert_eq!(Ok(vec![a]), tsa("example.i2p:24352"));
|
||||
#[test]
|
||||
fn to_socket_addr_str() {
|
||||
let a = isa(I2pAddr::new("example.i2p"), 24352);
|
||||
assert_eq!(Ok(vec![a]), tsa("example.i2p:24352"));
|
||||
|
||||
let a = isa(I2pAddr::new("example.i2p"), 23924);
|
||||
assert!(tsa("example.i2p:23924").unwrap().contains(&a));
|
||||
}
|
||||
let a = isa(I2pAddr::new("example.i2p"), 23924);
|
||||
assert!(tsa("example.i2p:23924").unwrap().contains(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_string() {
|
||||
let a = isa(I2pAddr::new("example.i2p"), 24352);
|
||||
assert_eq!(
|
||||
Ok(vec![a.clone()]),
|
||||
tsa(&*format!("{}:{}", "example.i2p", "24352"))
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(vec![a.clone()]),
|
||||
tsa(&format!("{}:{}", "example.i2p", "24352"))
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(vec![a.clone()]),
|
||||
tsa(format!("{}:{}", "example.i2p", "24352"))
|
||||
);
|
||||
#[test]
|
||||
fn to_socket_addr_string() {
|
||||
let a = isa(I2pAddr::new("example.i2p"), 24352);
|
||||
assert_eq!(
|
||||
Ok(vec![a.clone()]),
|
||||
tsa(&*format!("{}:{}", "example.i2p", "24352"))
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(vec![a.clone()]),
|
||||
tsa(&format!("{}:{}", "example.i2p", "24352"))
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(vec![a.clone()]),
|
||||
tsa(format!("{}:{}", "example.i2p", "24352"))
|
||||
);
|
||||
|
||||
let s = format!("{}:{}", "example.i2p", "24352");
|
||||
assert_eq!(Ok(vec![a]), tsa(s));
|
||||
// s has been moved into the tsa call
|
||||
}
|
||||
let s = format!("{}:{}", "example.i2p", "24352");
|
||||
assert_eq!(Ok(vec![a]), tsa(s));
|
||||
// s has been moved into the tsa call
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_dest() {
|
||||
fn i2p(low: u8) -> I2pAddr {
|
||||
I2pAddr::new(&format!("example{}.i2p", low))
|
||||
}
|
||||
#[test]
|
||||
fn set_dest() {
|
||||
fn i2p(low: u8) -> I2pAddr {
|
||||
I2pAddr::new(&format!("example{}.i2p", low))
|
||||
}
|
||||
|
||||
let mut addr = I2pSocketAddr::new(i2p(12), 80);
|
||||
assert_eq!(addr.dest(), i2p(12));
|
||||
addr.set_dest(i2p(13));
|
||||
assert_eq!(addr.dest(), i2p(13));
|
||||
}
|
||||
let mut addr = I2pSocketAddr::new(i2p(12), 80);
|
||||
assert_eq!(addr.dest(), i2p(12));
|
||||
addr.set_dest(i2p(13));
|
||||
assert_eq!(addr.dest(), i2p(13));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_port() {
|
||||
let mut addr = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 80);
|
||||
assert_eq!(addr.port(), 80);
|
||||
addr.set_port(8080);
|
||||
assert_eq!(addr.port(), 8080);
|
||||
}
|
||||
#[test]
|
||||
fn set_port() {
|
||||
let mut addr = I2pSocketAddr::new(I2pAddr::new("example.i2p"), 80);
|
||||
assert_eq!(addr.port(), 80);
|
||||
addr.set_port(8080);
|
||||
assert_eq!(addr.port(), 8080);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::net::{I2pSocketAddr, ToI2pSocketAddrs};
|
||||
use crate::sam::DEFAULT_API;
|
||||
use crate::sam_options::SAMOptions;
|
||||
|
||||
/// Unimplemented
|
||||
///
|
||||
@@ -15,8 +16,9 @@ use crate::sam::DEFAULT_API;
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
/// use i2p::Error;
|
||||
///
|
||||
/// # fn foo() -> std::io::Result<()> {
|
||||
/// # fn foo() -> Result<(), Error> {
|
||||
/// {
|
||||
/// let mut socket = I2pDatagramSocket::bind("127.0.0.1:34254")?;
|
||||
///
|
||||
@@ -35,224 +37,227 @@ use crate::sam::DEFAULT_API;
|
||||
pub struct I2pDatagramSocket {}
|
||||
|
||||
impl I2pDatagramSocket {
|
||||
/// Creates an I2P datagram socket from the given address.
|
||||
///
|
||||
/// The address type can be any implementor of [`ToI2pSocketAddrs`] trait. See
|
||||
/// its documentation for concrete examples.
|
||||
///
|
||||
/// [`ToI2pSocketAddrs`]: ../../i2p/net/trait.ToI2pSocketAddrs.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// ```
|
||||
pub fn bind<A: ToI2pSocketAddrs>(addr: A) -> io::Result<I2pDatagramSocket> {
|
||||
I2pDatagramSocket::bind_via(DEFAULT_API, addr)
|
||||
}
|
||||
/// Creates an I2P datagram socket from the given address.
|
||||
///
|
||||
/// The address type can be any implementor of [`ToI2pSocketAddrs`] trait. See
|
||||
/// its documentation for concrete examples.
|
||||
///
|
||||
/// [`ToI2pSocketAddrs`]: ../../i2p/net/trait.ToI2pSocketAddrs.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// ```
|
||||
pub fn bind<A: ToI2pSocketAddrs>(addr: A) -> Result<I2pDatagramSocket, Error> {
|
||||
I2pDatagramSocket::bind_via(DEFAULT_API, addr, SAMOptions::default())
|
||||
}
|
||||
|
||||
pub fn bind_via<A: ToSocketAddrs, B: ToI2pSocketAddrs>(
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
) -> io::Result<I2pDatagramSocket> {
|
||||
super::each_addr(sam_addr, addr, I2pDatagramSocket::bind_addr)
|
||||
}
|
||||
pub fn bind_via<A: ToSocketAddrs, B: ToI2pSocketAddrs>(
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
options: SAMOptions
|
||||
) -> Result<I2pDatagramSocket, Error> {
|
||||
super::each_i2p_addr(sam_addr, addr, options,I2pDatagramSocket::bind_addr).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn bind_addr(_sam_addr: &SocketAddr, _addr: &I2pSocketAddr) -> io::Result<I2pDatagramSocket> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn bind_addr(
|
||||
_sam_addr: &SocketAddr,
|
||||
_addr: &I2pSocketAddr,
|
||||
_options: SAMOptions
|
||||
) -> Result<I2pDatagramSocket, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Receives data from the socket. On success, returns the number of bytes
|
||||
/// read and the address from whence the data came.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf)
|
||||
/// .expect("Didn't receive data");
|
||||
/// ```
|
||||
pub fn recv_from(&self, _buf: &mut [u8]) -> io::Result<(usize, I2pSocketAddr)> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Receives data from the socket. On success, returns the number of bytes
|
||||
/// read and the address from whence the data came.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf)
|
||||
/// .expect("Didn't receive data");
|
||||
/// ```
|
||||
pub fn recv_from(&self, _buf: &mut [u8]) -> Result<(usize, I2pSocketAddr), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Receives data from the socket, without removing it from the queue.
|
||||
///
|
||||
/// Successive calls return the same data.
|
||||
///
|
||||
/// On success, returns the number of bytes peeked and the address from
|
||||
/// whence the data came.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (number_of_bytes, src_addr) = socket.peek_from(&mut buf)
|
||||
/// .expect("Didn't receive data");
|
||||
/// ```
|
||||
pub fn peek_from(&self, _buf: &mut [u8]) -> io::Result<(usize, I2pSocketAddr)> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Receives data from the socket, without removing it from the queue.
|
||||
///
|
||||
/// Successive calls return the same data.
|
||||
///
|
||||
/// On success, returns the number of bytes peeked and the address from
|
||||
/// whence the data came.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (number_of_bytes, src_addr) = socket.peek_from(&mut buf)
|
||||
/// .expect("Didn't receive data");
|
||||
/// ```
|
||||
pub fn peek_from(&self, _buf: &mut [u8]) -> Result<(usize, I2pSocketAddr), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the given address. On success, returns the
|
||||
/// number of bytes written.
|
||||
///
|
||||
/// Address type can be any implementor of [`ToI2pSocketAddrs`] trait. See
|
||||
/// its documentation for concrete examples.
|
||||
///
|
||||
/// [`ToI2pSocketAddrs`]: ../../std/net/trait.ToI2pSocketAddrs.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data");
|
||||
/// ```
|
||||
pub fn send_to<A: ToI2pSocketAddrs>(&self, _buf: &[u8], addr: A) -> io::Result<usize> {
|
||||
match addr.to_socket_addrs()?.next() {
|
||||
Some(_addr) => unimplemented!(),
|
||||
None => Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"no addresses to send data to",
|
||||
)),
|
||||
}
|
||||
}
|
||||
/// Sends data on the socket to the given address. On success, returns the
|
||||
/// number of bytes written.
|
||||
///
|
||||
/// Address type can be any implementor of [`ToI2pSocketAddrs`] trait. See
|
||||
/// its documentation for concrete examples.
|
||||
///
|
||||
/// [`ToI2pSocketAddrs`]: ../../std/net/trait.ToI2pSocketAddrs.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data");
|
||||
/// ```
|
||||
pub fn send_to<A: ToI2pSocketAddrs>(&self, _buf: &[u8], addr: A) -> Result<usize, Error> {
|
||||
match addr.to_socket_addrs()?.next() {
|
||||
Some(_addr) => unimplemented!(),
|
||||
None => Err(ErrorKind::UnresolvableAddress.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the socket address that this socket was created from.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pDatagramSocket};
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// assert_eq!(socket.local_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 34254));
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> io::Result<I2pSocketAddr> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Returns the socket address that this socket was created from.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pDatagramSocket};
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// assert_eq!(socket.local_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 34254));
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> Result<I2pSocketAddr, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Creates a new independently owned handle to the underlying socket.
|
||||
///
|
||||
/// The returned `I2pDatagramSocket` is a reference to the same socket that this
|
||||
/// object references. Both handles will read and write the same port, and
|
||||
/// options set on one socket will be propagated to the other.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let socket_clone = socket.try_clone().expect("couldn't clone the socket");
|
||||
/// ```
|
||||
pub fn try_clone(&self) -> io::Result<I2pDatagramSocket> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Creates a new independently owned handle to the underlying socket.
|
||||
///
|
||||
/// The returned `I2pDatagramSocket` is a reference to the same socket that this
|
||||
/// object references. Both handles will read and write the same port, and
|
||||
/// options set on one socket will be propagated to the other.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let socket_clone = socket.try_clone().expect("couldn't clone the socket");
|
||||
/// ```
|
||||
pub fn try_clone(&self) -> Result<I2pDatagramSocket, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Connects this datagram socket to a remote address, allowing the `send` and
|
||||
/// `recv` calls to be used to send data and also applies filters to only
|
||||
/// receive data from the specified address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// ```
|
||||
pub fn connect<A: ToI2pSocketAddrs>(&self, addr: A) -> io::Result<()> {
|
||||
self.connect_via(DEFAULT_API, addr)
|
||||
}
|
||||
/// Connects this datagram socket to a remote address, allowing the `send` and
|
||||
/// `recv` calls to be used to send data and also applies filters to only
|
||||
/// receive data from the specified address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// ```
|
||||
pub fn connect<A: ToI2pSocketAddrs>(&self, addr: A) -> Result<(), Error> {
|
||||
self.connect_via(DEFAULT_API, addr, SAMOptions::default())
|
||||
}
|
||||
|
||||
pub fn connect_via<A: ToSocketAddrs, B: ToI2pSocketAddrs>(
|
||||
&self,
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
) -> io::Result<()> {
|
||||
super::each_addr(sam_addr, addr, |_sam_addr, _addr| unimplemented!())
|
||||
}
|
||||
pub fn connect_via<A: ToSocketAddrs, B: ToI2pSocketAddrs>(
|
||||
&self,
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
options: SAMOptions,
|
||||
) -> Result<(), Error> {
|
||||
super::each_i2p_addr(sam_addr, addr, options, |_sam_addr, _addr, _opts| unimplemented!())
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the remote address to which it is connected.
|
||||
///
|
||||
/// The [`connect()`] method will connect this socket to a remote address. This
|
||||
/// method will fail if the socket is not connected.
|
||||
///
|
||||
/// [`connect()`]: #method.connect
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// socket.send(&[0, 1, 2]).expect("couldn't send message");
|
||||
/// ```
|
||||
pub fn send(&self, _buf: &[u8]) -> io::Result<usize> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Sends data on the socket to the remote address to which it is connected.
|
||||
///
|
||||
/// The [`connect()`] method will connect this socket to a remote address. This
|
||||
/// method will fail if the socket is not connected.
|
||||
///
|
||||
/// [`connect()`]: #method.connect
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// socket.send(&[0, 1, 2]).expect("couldn't send message");
|
||||
/// ```
|
||||
pub fn send(&self, _buf: &[u8]) -> Result<usize, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Receives data on the socket from the remote address to which it is
|
||||
/// connected.
|
||||
///
|
||||
/// The `connect` method will connect this socket to a remote address. This
|
||||
/// method will fail if the socket is not connected.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// let mut buf = [0; 10];
|
||||
/// match socket.recv(&mut buf) {
|
||||
/// Ok(received) => println!("received {} bytes", received),
|
||||
/// Err(e) => println!("recv function failed: {:?}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn recv(&self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Receives data on the socket from the remote address to which it is
|
||||
/// connected.
|
||||
///
|
||||
/// The `connect` method will connect this socket to a remote address. This
|
||||
/// method will fail if the socket is not connected.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// let mut buf = [0; 10];
|
||||
/// match socket.recv(&mut buf) {
|
||||
/// Ok(received) => println!("received {} bytes", received),
|
||||
/// Err(e) => println!("recv function failed: {:?}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn recv(&self, _buf: &mut [u8]) -> Result<usize, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Receives data on the socket from the remote adress to which it is
|
||||
/// connected, without removing that data from the queue. On success,
|
||||
/// returns the number of bytes peeked.
|
||||
///
|
||||
/// Successive calls return the same data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method will fail if the socket is not connected. The `connect` method
|
||||
/// will connect this socket to a remote address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// let mut buf = [0; 10];
|
||||
/// match socket.peek(&mut buf) {
|
||||
/// Ok(received) => println!("received {} bytes", received),
|
||||
/// Err(e) => println!("peek function failed: {:?}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn peek(&self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Receives data on the socket from the remote adress to which it is
|
||||
/// connected, without removing that data from the queue. On success,
|
||||
/// returns the number of bytes peeked.
|
||||
///
|
||||
/// Successive calls return the same data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method will fail if the socket is not connected. The `connect` method
|
||||
/// will connect this socket to a remote address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pDatagramSocket;
|
||||
///
|
||||
/// let socket = I2pDatagramSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// let mut buf = [0; 10];
|
||||
/// match socket.peek(&mut buf) {
|
||||
/// Ok(received) => println!("received {} bytes", received),
|
||||
/// Err(e) => println!("peek function failed: {:?}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn peek(&self, _buf: &mut [u8]) -> Result<usize, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
148
src/net/i2p.rs
148
src/net/i2p.rs
@@ -1,6 +1,34 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::hash;
|
||||
|
||||
use data_encoding::{Encoding, Specification, BASE32};
|
||||
use lazy_static::lazy_static;
|
||||
use log::error;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::error::{Error, ErrorKind};
|
||||
|
||||
pub const B32_EXT: &'static str = ".b32.i2p";
|
||||
|
||||
lazy_static! {
|
||||
static ref BASE32_I2P: Encoding = {
|
||||
let mut spec = Specification::new();
|
||||
|
||||
spec.symbols.push_str("abcdefghijklmnopqrstuvwxyz234567");
|
||||
spec.padding = None;
|
||||
spec.encoding().unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref BASE64_I2P: Encoding = {
|
||||
let mut spec = Specification::new();
|
||||
spec.symbols
|
||||
.push_str("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~");
|
||||
spec.padding = Some('=');
|
||||
spec.encoding().unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
/// An I2P address, as a Destination, B32 address or hostname.
|
||||
///
|
||||
@@ -21,81 +49,59 @@ use std::hash;
|
||||
///
|
||||
/// I2pAddr::new("abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrst.b32.i2p");
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
|
||||
pub struct I2pAddr {
|
||||
inner: String,
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl I2pAddr {
|
||||
/// Creates a new I2p address from a given string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::I2pAddr;
|
||||
///
|
||||
/// let addr = I2pAddr::new("example.i2p");
|
||||
/// ```
|
||||
pub fn new(dest: &str) -> I2pAddr {
|
||||
I2pAddr {
|
||||
inner: dest.to_string(),
|
||||
}
|
||||
}
|
||||
/// Creates a new I2p address from a given string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::I2pAddr;
|
||||
///
|
||||
/// let addr = I2pAddr::new("example.i2p");
|
||||
/// ```
|
||||
pub fn new(dest: &str) -> I2pAddr {
|
||||
I2pAddr {
|
||||
inner: dest.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the String that makes up this address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::I2pAddr;
|
||||
///
|
||||
/// let addr = I2pAddr::new("example.i2p");
|
||||
/// assert_eq!(addr.string(), "example.i2p");
|
||||
/// ```
|
||||
pub fn string(&self) -> String {
|
||||
self.inner.clone()
|
||||
}
|
||||
/// Creates a new I2P address from a full base64 destination string. This
|
||||
/// will internally convert it to a common base32 addresse, using the
|
||||
/// b32.i2p extension.
|
||||
pub fn from_b64(dest: &str) -> Result<I2pAddr, Error> {
|
||||
let bin_data = BASE64_I2P.decode(dest.as_bytes()).map_err(|e| {
|
||||
error!("Base64 decoding error: {:?}", e);
|
||||
ErrorKind::BadAddressEncoding(dest.to_string()).to_err()
|
||||
})?;
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.input(bin_data);
|
||||
let mut b32 = BASE32_I2P.encode(&hasher.result());
|
||||
b32.push_str(B32_EXT);
|
||||
Ok(I2pAddr { inner: b32 })
|
||||
}
|
||||
|
||||
/// Returns the String that makes up this address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use i2p::net::I2pAddr;
|
||||
///
|
||||
/// let addr = I2pAddr::new("example.i2p");
|
||||
/// assert_eq!(addr.string(), "example.i2p");
|
||||
/// ```
|
||||
pub fn string(&self) -> String {
|
||||
self.inner.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for I2pAddr {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for I2pAddr {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for I2pAddr {
|
||||
fn clone(&self) -> I2pAddr {
|
||||
I2pAddr::new(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for I2pAddr {
|
||||
fn eq(&self, other: &I2pAddr) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for I2pAddr {}
|
||||
|
||||
impl hash::Hash for I2pAddr {
|
||||
fn hash<H: hash::Hasher>(&self, s: &mut H) {
|
||||
self.inner.hash(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for I2pAddr {
|
||||
fn partial_cmp(&self, other: &I2pAddr) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for I2pAddr {
|
||||
fn cmp(&self, other: &I2pAddr) -> Ordering {
|
||||
self.inner.cmp(&other.inner)
|
||||
}
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,11 @@
|
||||
use std::io;
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::sam_options::SAMOptions;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
|
||||
pub use self::addr::{I2pSocketAddr, ToI2pSocketAddrs};
|
||||
pub use self::datagram::I2pDatagramSocket;
|
||||
pub use self::i2p::I2pAddr;
|
||||
pub use self::streaming::{I2pListener, I2pStream};
|
||||
pub use self::streaming::{I2pListenerBuilder, I2pListener, I2pStream};
|
||||
|
||||
mod addr;
|
||||
mod datagram;
|
||||
@@ -13,27 +14,37 @@ mod streaming;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
fn each_addr<A: ToSocketAddrs, B: ToI2pSocketAddrs, F, T>(
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
mut f: F,
|
||||
) -> io::Result<T>
|
||||
fn each_i2p_addr<A: ToSocketAddrs, B: ToI2pSocketAddrs, F, T>(
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
opts: SAMOptions,
|
||||
mut f: F,
|
||||
) -> Result<T, Error>
|
||||
where
|
||||
F: FnMut(&SocketAddr, &I2pSocketAddr) -> io::Result<T>,
|
||||
F: FnMut(&SocketAddr, &I2pSocketAddr, SAMOptions) -> Result<T, Error>,
|
||||
{
|
||||
let mut last_err = None;
|
||||
for addr in addr.to_socket_addrs()? {
|
||||
for sam_addr in sam_addr.to_socket_addrs()? {
|
||||
match f(&sam_addr, &addr) {
|
||||
Ok(l) => return Ok(l),
|
||||
Err(e) => last_err = Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(last_err.unwrap_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"could not resolve to any addresses",
|
||||
)
|
||||
}))
|
||||
let mut last_err = None;
|
||||
for addr in addr.to_socket_addrs()? {
|
||||
for sam_addr in sam_addr.to_socket_addrs()? {
|
||||
match f(&sam_addr, &addr, opts.clone()) {
|
||||
Ok(l) => return Ok(l),
|
||||
Err(e) => last_err = Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(last_err.unwrap_or(ErrorKind::UnresolvableAddress.into()))
|
||||
}
|
||||
|
||||
fn each_addr<A: ToSocketAddrs, F, T>(sam_addr: A, opts: SAMOptions, mut f: F) -> Result<T, Error>
|
||||
where
|
||||
F: FnMut(&SocketAddr, SAMOptions) -> Result<T, Error>,
|
||||
{
|
||||
let mut last_err = None;
|
||||
for sam_addr in sam_addr.to_socket_addrs()? {
|
||||
match f(&sam_addr, opts.clone()) {
|
||||
Ok(l) => return Ok(l),
|
||||
Err(e) => last_err = Some(e),
|
||||
}
|
||||
}
|
||||
Err(last_err.unwrap_or(ErrorKind::UnresolvableAddress.into()))
|
||||
}
|
||||
|
@@ -3,12 +3,12 @@ use std::io::prelude::*;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::{Shutdown, SocketAddr, ToSocketAddrs};
|
||||
use std::time::Duration;
|
||||
|
||||
use rand;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::net::{I2pAddr, I2pSocketAddr, ToI2pSocketAddrs};
|
||||
use crate::sam::{StreamConnect, DEFAULT_API};
|
||||
use crate::sam::{Session, StreamConnect, StreamForward, DEFAULT_API};
|
||||
use crate::sam_options::SAMOptions;
|
||||
|
||||
/// A structure which represents an I2P stream between a local socket and a
|
||||
/// remote socket.
|
||||
@@ -30,11 +30,215 @@ use crate::sam::{StreamConnect, DEFAULT_API};
|
||||
/// } // the stream is closed here
|
||||
/// ```
|
||||
pub struct I2pStream {
|
||||
inner: StreamConnect,
|
||||
#[cfg(feature = "public-conn")]
|
||||
pub inner: StreamConnect,
|
||||
#[cfg(not(feature = "public-conn"))]
|
||||
inner: StreamConnect,
|
||||
}
|
||||
|
||||
/// Unimplemented
|
||||
/// An infinite iterator over the connections from an `I2pListener`.
|
||||
///
|
||||
/// This iterator will infinitely yield [`Some`] of the accepted connections. It
|
||||
/// is equivalent to calling `accept` in a loop.
|
||||
///
|
||||
/// This `struct` is created by the [`incoming`] method on [`I2pListener`].
|
||||
///
|
||||
/// [`Some`]: ../../std/option/enum.Option.html#variant.Some
|
||||
/// [`incoming`]: struct.I2pListener.html#method.incoming
|
||||
/// [`I2pListener`]: struct.I2pListener.html
|
||||
pub struct Incoming<'a> {
|
||||
#[cfg(feature = "public-conn")]
|
||||
pub listener: &'a I2pListener,
|
||||
#[cfg(not(feature = "public-conn"))]
|
||||
listener: &'a I2pListener,
|
||||
}
|
||||
|
||||
impl I2pStream {
|
||||
/// Opens a TCP-like connection to a remote host.
|
||||
///
|
||||
/// `addr` is an address of the remote host. Anything which implements
|
||||
/// `ToI2pSocketAddrs` trait can be supplied for the address; see this trait
|
||||
/// documentation for concrete examples.
|
||||
/// In case `ToI2pSocketAddrs::to_socket_addrs()` returns more than one
|
||||
/// entry (which should never be the case), then the first valid and
|
||||
/// reachable address is used.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pStream;
|
||||
///
|
||||
/// if let Ok(stream) = I2pStream::connect("example.i2p:8080") {
|
||||
/// println!("Connected to the server!");
|
||||
/// } else {
|
||||
/// println!("Couldn't connect to server...");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn connect<A: ToI2pSocketAddrs>(addr: A) -> Result<I2pStream, Error> {
|
||||
I2pStream::connect_via(DEFAULT_API, addr, SAMOptions::default())
|
||||
}
|
||||
|
||||
/// Same as `connect` but reuses an existing SAM session.
|
||||
pub fn connect_with_session<A: ToI2pSocketAddrs>(
|
||||
session: &Session,
|
||||
addr: A,
|
||||
) -> Result<I2pStream, Error> {
|
||||
let addr: Result<_, Error> = addr
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or(ErrorKind::UnresolvableAddress.into());
|
||||
I2pStream::connect_addr_with_session(session, &addr?)
|
||||
}
|
||||
|
||||
pub fn connect_via<A: ToSocketAddrs, B: ToI2pSocketAddrs>(
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
options: SAMOptions,
|
||||
) -> Result<I2pStream, Error> {
|
||||
super::each_i2p_addr(sam_addr, addr, options, I2pStream::connect_addr).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn connect_addr(sam_addr: &SocketAddr, addr: &I2pSocketAddr, options: SAMOptions) -> Result<I2pStream, Error> {
|
||||
let stream = StreamConnect::new(sam_addr, &addr.dest().string(), addr.port(), options)?;
|
||||
|
||||
Ok(I2pStream { inner: stream })
|
||||
}
|
||||
|
||||
fn connect_addr_with_session(
|
||||
session: &Session,
|
||||
addr: &I2pSocketAddr,
|
||||
) -> Result<I2pStream, Error> {
|
||||
let stream = StreamConnect::with_session(session, &addr.dest().string(), addr.port())?;
|
||||
|
||||
Ok(I2pStream { inner: stream })
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote peer of this I2P connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pStream};
|
||||
///
|
||||
/// let stream = I2pStream::connect("example.i2p:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// assert_eq!(stream.peer_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080));
|
||||
/// ```
|
||||
pub fn peer_addr(&self) -> Result<I2pSocketAddr, Error> {
|
||||
self.inner
|
||||
.peer_addr()
|
||||
.map(|(d, p)| I2pSocketAddr::new(I2pAddr::new(&d), p))
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this I2P connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pStream};
|
||||
///
|
||||
/// let stream = I2pStream::connect("example.i2p:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// assert_eq!(stream.local_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080));
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> Result<I2pSocketAddr, Error> {
|
||||
self.inner
|
||||
.local_addr()
|
||||
.map(|(d, p)| I2pSocketAddr::new(I2pAddr::new(&d), p))
|
||||
}
|
||||
|
||||
/// Moves this I2P stream into or out of nonblocking mode. This will
|
||||
/// result in read, write, recv and send operations becoming nonblocking,
|
||||
/// i.e., immediately returning from their calls. If the IO operation is
|
||||
/// successful, Ok is returned and no further action is required. If the
|
||||
/// IO operation could not be completed and needs to be retried, a wrapped
|
||||
/// Io error with kind io::ErrorKind::WouldBlock is returned.
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> Result<(), Error> {
|
||||
self.inner.set_nonblocking(nonblocking)
|
||||
}
|
||||
pub fn set_read_timeout(&self, duration: Option<Duration>) -> std::io::Result<()> {
|
||||
self.inner.set_read_timeout(duration)
|
||||
}
|
||||
pub fn set_write_timeout(&self, duration: Option<Duration>) -> std::io::Result<()> {
|
||||
self.inner.set_write_timeout(duration)
|
||||
}
|
||||
|
||||
/// Shuts down the read, write, or both halves of this connection.
|
||||
///
|
||||
/// This function will cause all pending and future I/O on the specified
|
||||
/// portions to return immediately with an appropriate value (see the
|
||||
/// documentation of [`Shutdown`]).
|
||||
///
|
||||
/// [`Shutdown`]: ../../std/net/enum.Shutdown.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::Shutdown;
|
||||
/// use i2p::net::I2pStream;
|
||||
///
|
||||
/// let stream = I2pStream::connect("127.0.0.1:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// stream.shutdown(Shutdown::Both).expect("shutdown call failed");
|
||||
/// ```
|
||||
pub fn shutdown(&self, how: Shutdown) -> Result<(), Error> {
|
||||
self.inner.shutdown(how)
|
||||
}
|
||||
|
||||
/// Creates a new independently owned handle to the underlying socket.
|
||||
///
|
||||
/// The returned `I2pStream` is a reference to the same stream that this
|
||||
/// object references. Both handles will read and write the same stream of
|
||||
/// data, and options set on one stream will be propagated to the other
|
||||
/// stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pStream;
|
||||
///
|
||||
/// let stream = I2pStream::connect("example.i2p:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// let stream_clone = stream.try_clone().expect("clone failed...");
|
||||
/// ```
|
||||
pub fn try_clone(&self) -> Result<I2pStream, Error> {
|
||||
self.inner.duplicate().map(|s| I2pStream { inner: s })
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for I2pStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for I2pStream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for I2pStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut res = f.debug_struct("I2pStream");
|
||||
|
||||
if let Ok(addr) = self.local_addr() {
|
||||
res.field("addr", &addr);
|
||||
}
|
||||
|
||||
if let Ok(peer) = self.peer_addr() {
|
||||
res.field("peer", &peer);
|
||||
}
|
||||
|
||||
res.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing a socket server.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -42,7 +246,7 @@ pub struct I2pStream {
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pListener, I2pStream};
|
||||
///
|
||||
/// let listener = I2pListener::bind("127.0.0.1:80").unwrap();
|
||||
/// let listener = I2pListener::bind().unwrap();
|
||||
///
|
||||
/// fn handle_client(stream: I2pStream) {
|
||||
/// // ...
|
||||
@@ -58,300 +262,240 @@ pub struct I2pStream {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct I2pListener {}
|
||||
|
||||
/// An infinite iterator over the connections from an `I2pListener`.
|
||||
///
|
||||
/// This iterator will infinitely yield [`Some`] of the accepted connections. It
|
||||
/// is equivalent to calling `accept` in a loop.
|
||||
///
|
||||
/// This `struct` is created by the [`incoming`] method on [`I2pListener`].
|
||||
///
|
||||
/// [`Some`]: ../../std/option/enum.Option.html#variant.Some
|
||||
/// [`incoming`]: struct.I2pListener.html#method.incoming
|
||||
/// [`I2pListener`]: struct.I2pListener.html
|
||||
#[derive(Debug)]
|
||||
pub struct Incoming<'a> {
|
||||
listener: &'a I2pListener,
|
||||
}
|
||||
|
||||
impl I2pStream {
|
||||
/// Opens a TCP-like connection to a remote host.
|
||||
///
|
||||
/// `addr` is an address of the remote host. Anything which implements
|
||||
/// `ToI2pSocketAddrs` trait can be supplied for the address; see this trait
|
||||
/// documentation for concrete examples.
|
||||
/// In case `ToI2pSocketAddrs::to_socket_addrs()` returns more than one
|
||||
/// entry (which should never be the case), then the first valid and
|
||||
/// reachable address is used.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pStream;
|
||||
///
|
||||
/// if let Ok(stream) = I2pStream::connect("example.i2p:8080") {
|
||||
/// println!("Connected to the server!");
|
||||
/// } else {
|
||||
/// println!("Couldn't connect to server...");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn connect<A: ToI2pSocketAddrs>(addr: A) -> io::Result<I2pStream> {
|
||||
I2pStream::connect_via(DEFAULT_API, addr)
|
||||
}
|
||||
|
||||
pub fn connect_via<A: ToSocketAddrs, B: ToI2pSocketAddrs>(
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
) -> io::Result<I2pStream> {
|
||||
super::each_addr(sam_addr, addr, I2pStream::connect_addr)
|
||||
}
|
||||
|
||||
fn connect_addr(sam_addr: &SocketAddr, addr: &I2pSocketAddr) -> io::Result<I2pStream> {
|
||||
let suffix: String = rand::thread_rng().gen_ascii_chars().take(8).collect();
|
||||
let nickname = format!("i2prs-{}", suffix);
|
||||
|
||||
let stream = StreamConnect::new(sam_addr, &addr.dest().string(), addr.port(), &nickname)?;
|
||||
|
||||
Ok(I2pStream { inner: stream })
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote peer of this I2P connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pStream};
|
||||
///
|
||||
/// let stream = I2pStream::connect("example.i2p:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// assert_eq!(stream.peer_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080));
|
||||
/// ```
|
||||
pub fn peer_addr(&self) -> io::Result<I2pSocketAddr> {
|
||||
self.inner
|
||||
.peer_addr()
|
||||
.map(|(d, p)| I2pSocketAddr::new(I2pAddr::new(&d), p))
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this I2P connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pStream};
|
||||
///
|
||||
/// let stream = I2pStream::connect("example.i2p:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// assert_eq!(stream.local_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080));
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> io::Result<I2pSocketAddr> {
|
||||
self.inner
|
||||
.local_addr()
|
||||
.map(|(d, p)| I2pSocketAddr::new(I2pAddr::new(&d), p))
|
||||
}
|
||||
|
||||
/// Shuts down the read, write, or both halves of this connection.
|
||||
///
|
||||
/// This function will cause all pending and future I/O on the specified
|
||||
/// portions to return immediately with an appropriate value (see the
|
||||
/// documentation of [`Shutdown`]).
|
||||
///
|
||||
/// [`Shutdown`]: ../../std/net/enum.Shutdown.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::Shutdown;
|
||||
/// use i2p::net::I2pStream;
|
||||
///
|
||||
/// let stream = I2pStream::connect("127.0.0.1:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// stream.shutdown(Shutdown::Both).expect("shutdown call failed");
|
||||
/// ```
|
||||
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
|
||||
self.inner.shutdown(how)
|
||||
}
|
||||
|
||||
/// Creates a new independently owned handle to the underlying socket.
|
||||
///
|
||||
/// The returned `I2pStream` is a reference to the same stream that this
|
||||
/// object references. Both handles will read and write the same stream of
|
||||
/// data, and options set on one stream will be propagated to the other
|
||||
/// stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pStream;
|
||||
///
|
||||
/// let stream = I2pStream::connect("example.i2p:8080")
|
||||
/// .expect("Couldn't connect to the server...");
|
||||
/// let stream_clone = stream.try_clone().expect("clone failed...");
|
||||
/// ```
|
||||
pub fn try_clone(&self) -> io::Result<I2pStream> {
|
||||
self.inner.duplicate().map(|s| I2pStream { inner: s })
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for I2pStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for I2pStream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for I2pStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut res = f.debug_struct("I2pStream");
|
||||
|
||||
if let Ok(addr) = self.local_addr() {
|
||||
res.field("addr", &addr);
|
||||
}
|
||||
|
||||
if let Ok(peer) = self.peer_addr() {
|
||||
res.field("peer", &peer);
|
||||
}
|
||||
|
||||
res.finish()
|
||||
}
|
||||
pub struct I2pListener {
|
||||
#[cfg(feature = "public-conn")]
|
||||
pub forward: StreamForward,
|
||||
#[cfg(not(feature = "public-conn"))]
|
||||
forward: StreamForward,
|
||||
}
|
||||
|
||||
impl I2pListener {
|
||||
/// Creates a new `I2pListener` which will be bound to the specified
|
||||
/// address.
|
||||
///
|
||||
/// The returned listener is ready for accepting connections.
|
||||
///
|
||||
/// Binding with a port number of 0 is equivalent to binding on every port.
|
||||
///
|
||||
/// The address type can be any implementor of `ToI2pSocketAddrs` trait. See
|
||||
/// its documentation for concrete examples.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind("127.0.0.1:80").unwrap();
|
||||
/// ```
|
||||
pub fn bind<A: ToI2pSocketAddrs>(addr: A) -> io::Result<I2pListener> {
|
||||
I2pListener::bind_via(DEFAULT_API, addr)
|
||||
}
|
||||
/// Creates a new `I2pListener` which will be bound to the specified
|
||||
/// address.
|
||||
///
|
||||
/// The returned listener is ready for accepting connections.
|
||||
///
|
||||
/// Binding with a port number of 0 is equivalent to binding on every port.
|
||||
///
|
||||
/// The address type can be any implementor of `ToI2pSocketAddrs` trait. See
|
||||
/// its documentation for concrete examples.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind().unwrap();
|
||||
/// ```
|
||||
pub fn bind() -> Result<I2pListener, Error> {
|
||||
I2pListener::bind_via(DEFAULT_API)
|
||||
}
|
||||
|
||||
pub fn bind_via<A: ToSocketAddrs, B: ToI2pSocketAddrs>(
|
||||
sam_addr: A,
|
||||
addr: B,
|
||||
) -> io::Result<I2pListener> {
|
||||
super::each_addr(sam_addr, addr, I2pListener::bind_addr)
|
||||
}
|
||||
pub fn bind_with_session(session: &Session) -> Result<I2pListener, Error> {
|
||||
let forward = StreamForward::with_session(session)?;
|
||||
Ok(I2pListener { forward })
|
||||
}
|
||||
|
||||
fn bind_addr(_sam_addr: &SocketAddr, _addr: &I2pSocketAddr) -> io::Result<I2pListener> {
|
||||
unimplemented!();
|
||||
}
|
||||
pub fn bind_via<A: ToSocketAddrs>(sam_addr: A) -> Result<I2pListener, Error> {
|
||||
super::each_addr(sam_addr, SAMOptions::default(), I2pListener::bind_addr).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Returns the local socket address of this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pListener};
|
||||
///
|
||||
/// let listener = I2pListener::bind("127.0.0.1:8080").unwrap();
|
||||
/// assert_eq!(listener.local_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080));
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> io::Result<I2pSocketAddr> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn bind_addr(sam_addr: &SocketAddr, options: SAMOptions) -> Result<I2pListener, Error> {
|
||||
let forward = StreamForward::new(sam_addr, options)?;
|
||||
Ok(I2pListener { forward })
|
||||
}
|
||||
|
||||
/// Creates a new independently owned handle to the underlying socket.
|
||||
///
|
||||
/// The returned `TcpListener` is a reference to the same socket that this
|
||||
/// object references. Both handles can be used to accept incoming
|
||||
/// connections and options set on one listener will affect the other.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind("127.0.0.1:8080").unwrap();
|
||||
/// let listener_clone = listener.try_clone().unwrap();
|
||||
/// ```
|
||||
pub fn try_clone(&self) -> io::Result<I2pListener> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Returns the local socket address of this listener.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pAddr, I2pSocketAddr, I2pListener};
|
||||
///
|
||||
/// let listener = I2pListener::bind().unwrap();
|
||||
/// assert_eq!(listener.local_addr().unwrap(),
|
||||
/// I2pSocketAddr::new(I2pAddr::new("example.i2p"), 8080));
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> Result<I2pSocketAddr, Error> {
|
||||
self.forward
|
||||
.local_addr()
|
||||
.map(|(d, p)| I2pSocketAddr::new(I2pAddr::new(&d), p))
|
||||
}
|
||||
|
||||
/// Accept a new incoming connection from this listener.
|
||||
///
|
||||
/// This function will block the calling thread until a new TCP connection
|
||||
/// is established. When established, the corresponding `TcpStream` and the
|
||||
/// remote peer's address will be returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind("127.0.0.1:8080").unwrap();
|
||||
/// match listener.accept() {
|
||||
/// Ok((_socket, addr)) => println!("new client: {:?}", addr),
|
||||
/// Err(e) => println!("couldn't get client: {:?}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn accept(&self) -> io::Result<(I2pStream, I2pSocketAddr)> {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Creates a new independently owned handle to the underlying socket.
|
||||
///
|
||||
/// The returned `TcpListener` is a reference to the same socket that this
|
||||
/// object references. Both handles can be used to accept incoming
|
||||
/// connections and options set on one listener will affect the other.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind().unwrap();
|
||||
/// let listener_clone = listener.try_clone().unwrap();
|
||||
/// ```
|
||||
pub fn try_clone(&self) -> Result<I2pListener, Error> {
|
||||
let forward = self.forward.duplicate()?;
|
||||
Ok(I2pListener { forward })
|
||||
}
|
||||
|
||||
/// Returns an iterator over the connections being received on this
|
||||
/// listener.
|
||||
///
|
||||
/// The returned iterator will never return [`None`] and will also not yield
|
||||
/// the peer's [`I2pSocketAddr`] structure.
|
||||
///
|
||||
/// [`None`]: ../../std/option/enum.Option.html#variant.None
|
||||
/// [`I2pSocketAddr`]: ../../std/net/struct.I2pSocketAddr.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind("127.0.0.1:80").unwrap();
|
||||
///
|
||||
/// for stream in listener.incoming() {
|
||||
/// match stream {
|
||||
/// Ok(stream) => {
|
||||
/// println!("new client!");
|
||||
/// }
|
||||
/// Err(e) => { /* connection failed */ }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn incoming(&self) -> Incoming<'_> {
|
||||
Incoming { listener: self }
|
||||
}
|
||||
/// Accept a new incoming connection from this listener.
|
||||
///
|
||||
/// This function will block the calling thread until a new TCP connection
|
||||
/// is established. When established, the corresponding `TcpStream` and the
|
||||
/// remote peer's address will be returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind().unwrap();
|
||||
/// match listener.accept() {
|
||||
/// Ok((_socket, addr)) => println!("new client: {:?}", addr),
|
||||
/// Err(e) => println!("couldn't get client: {:?}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn accept(&self) -> Result<(I2pStream, I2pSocketAddr), Error> {
|
||||
let (i2p_stream, addr) = self.forward.accept()?;
|
||||
Ok((I2pStream { inner: i2p_stream }, addr))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the connections being received on this
|
||||
/// listener.
|
||||
///
|
||||
/// The returned iterator will never return [`None`] and will also not yield
|
||||
/// the peer's [`I2pSocketAddr`] structure.
|
||||
///
|
||||
/// [`None`]: ../../std/option/enum.Option.html#variant.None
|
||||
/// [`I2pSocketAddr`]: ../../std/net/struct.I2pSocketAddr.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::I2pListener;
|
||||
///
|
||||
/// let listener = I2pListener::bind().unwrap();
|
||||
///
|
||||
/// for stream in listener.incoming() {
|
||||
/// match stream {
|
||||
/// Ok(stream) => {
|
||||
/// println!("new client!");
|
||||
/// }
|
||||
/// Err(e) => { /* connection failed */ }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn incoming(&self) -> Incoming {
|
||||
Incoming { listener: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Incoming<'a> {
|
||||
type Item = io::Result<I2pStream>;
|
||||
fn next(&mut self) -> Option<io::Result<I2pStream>> {
|
||||
Some(self.listener.accept().map(|p| p.0))
|
||||
}
|
||||
type Item = Result<I2pStream, Error>;
|
||||
fn next(&mut self) -> Option<Result<I2pStream, Error>> {
|
||||
Some(self.listener.accept().map(|p| p.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for I2pListener {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// A helper struct for creating `I2pListener`s.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use i2p::net::{I2pListenerBuilder, I2pStream};
|
||||
///
|
||||
/// let listener = I2pListenerBuilder::new().build()?;
|
||||
///
|
||||
/// fn handle_client(stream: I2pStream) {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// // accept connections and process them serially
|
||||
/// for stream in listener.incoming() {
|
||||
/// match stream {
|
||||
/// Ok(stream) => {
|
||||
/// handle_client(stream);
|
||||
/// }
|
||||
/// Err(e) => { /* connection failed */ }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct I2pListenerBuilder {
|
||||
session: Option<Session>,
|
||||
addrs: Vec<SocketAddr>,
|
||||
options: SAMOptions,
|
||||
}
|
||||
|
||||
impl Default for I2pListenerBuilder {
|
||||
fn default() -> Self {
|
||||
I2pListenerBuilder {
|
||||
session: None,
|
||||
addrs: vec![],
|
||||
options: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl I2pListenerBuilder {
|
||||
|
||||
/// Build an `I2pListener` and bind to socket addresses using previously
|
||||
/// defined settings.
|
||||
///
|
||||
/// If none of `with_session`, `with_addr` or `with_addrs` were called, the
|
||||
/// listener will bind to `crate::sam::DEFAULT_API`.
|
||||
///
|
||||
/// If `with_options` was not called, defaults to `SAMOptions::default()`
|
||||
///
|
||||
/// If `with_session` was called, all other settings will be ignored.
|
||||
pub fn build(mut self) -> Result<I2pListener, Error> {
|
||||
if let Some(s) = self.session {
|
||||
Ok(I2pListener {
|
||||
forward: StreamForward::with_session(&s)?
|
||||
})
|
||||
}
|
||||
else {
|
||||
// Default to DEFAULT_API if no socket address has been set manually
|
||||
if self.addrs.len() == 0 {
|
||||
self.addrs.extend(ToSocketAddrs::to_socket_addrs(DEFAULT_API)?);
|
||||
}
|
||||
super::each_addr(
|
||||
self.addrs.as_slice(),
|
||||
self.options,
|
||||
I2pListener::bind_addr
|
||||
).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes this builder recreate an I2pListener from the specified session.
|
||||
/// According to the SAMv3 protocol, SAM options can only be set upon
|
||||
/// session creation. Therefore, `I2pListenerBuilder::build(...)` will
|
||||
/// ignore any options set using `with_options` if recreating a listener
|
||||
/// using a previous session.
|
||||
pub fn with_session(mut self, session: Session) -> Self {
|
||||
self.session = Some(session);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add `address` to the list of socket addresses to bind to
|
||||
pub fn with_addr(mut self, address: SocketAddr) -> Self {
|
||||
self.addrs.push(address);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add all addresses derived from `addresses` to the list of socket
|
||||
/// addresses to bind to
|
||||
pub fn with_addrs<A: ToSocketAddrs>(mut self, addresses: A) -> Result<Self, Error> {
|
||||
self.addrs.extend(addresses.to_socket_addrs()?);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Use the SAMOptions specified when building the `I2pListener`
|
||||
pub fn with_options(mut self, opts: SAMOptions) -> Self {
|
||||
self.options = opts;
|
||||
self
|
||||
}
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
use crate::net::{I2pAddr, I2pSocketAddr, ToI2pSocketAddrs};
|
||||
|
||||
pub fn isa(a: I2pAddr, p: u16) -> I2pSocketAddr {
|
||||
I2pSocketAddr::new(a, p)
|
||||
I2pSocketAddr::new(a, p)
|
||||
}
|
||||
|
||||
pub fn tsa<A: ToI2pSocketAddrs>(a: A) -> Result<Vec<I2pSocketAddr>, String> {
|
||||
match a.to_socket_addrs() {
|
||||
Ok(a) => Ok(a.collect()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
match a.to_socket_addrs() {
|
||||
Ok(a) => Ok(a.collect()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
268
src/parsers.rs
268
src/parsers.rs
@@ -1,184 +1,186 @@
|
||||
use nom::{alphanumeric, space};
|
||||
use nom::{alphanumeric, alt, do_parse, named, separated_list, space, tag, take_till};
|
||||
|
||||
fn is_space(chr: char) -> bool {
|
||||
chr == ' ' || chr == '\t'
|
||||
chr == ' ' || chr == '\t'
|
||||
}
|
||||
|
||||
fn is_next_line(chr: char) -> bool {
|
||||
chr == '\n'
|
||||
chr == '\n'
|
||||
}
|
||||
|
||||
fn is_space_or_next_line(chr: char) -> bool {
|
||||
is_space(chr) || is_next_line(chr)
|
||||
is_space(chr) || is_next_line(chr)
|
||||
}
|
||||
|
||||
fn is_double_quote(chr: char) -> bool {
|
||||
chr == '\"'
|
||||
chr == '\"'
|
||||
}
|
||||
|
||||
named!(quoted_value <&str, &str>,
|
||||
do_parse!(
|
||||
tag_s!("\"") >>
|
||||
val: take_till_s!(is_double_quote) >>
|
||||
tag_s!("\"") >>
|
||||
(val)
|
||||
)
|
||||
do_parse!(
|
||||
tag!("\"") >>
|
||||
val: take_till!(is_double_quote) >>
|
||||
tag!("\"") >>
|
||||
(val)
|
||||
)
|
||||
);
|
||||
|
||||
named!(value <&str, &str>, take_till_s!(is_space_or_next_line));
|
||||
named!(value <&str, &str>, take_till!(is_space_or_next_line));
|
||||
|
||||
named!(key_value <&str, (&str, &str)>,
|
||||
do_parse!(
|
||||
key: alphanumeric >>
|
||||
tag_s!("=") >>
|
||||
val: alt!(quoted_value | value) >>
|
||||
(key, val)
|
||||
)
|
||||
do_parse!(
|
||||
key: alphanumeric >>
|
||||
tag!("=") >>
|
||||
val: alt!(quoted_value | value) >>
|
||||
(key, val)
|
||||
)
|
||||
);
|
||||
|
||||
named!(keys_and_values<&str, Vec<(&str, &str)> >, separated_list!(space, key_value));
|
||||
|
||||
named!(pub sam_hello <&str, Vec<(&str, &str)> >,
|
||||
do_parse!(
|
||||
tag_s!("HELLO REPLY ") >>
|
||||
opts: keys_and_values >>
|
||||
tag_s!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
do_parse!(
|
||||
tag!("HELLO REPLY ") >>
|
||||
opts: keys_and_values >>
|
||||
tag!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub sam_session_status <&str, Vec<(&str, &str)> >,
|
||||
do_parse!(
|
||||
tag_s!("SESSION STATUS ") >>
|
||||
opts: keys_and_values >>
|
||||
tag_s!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
do_parse!(
|
||||
tag!("SESSION STATUS ") >>
|
||||
opts: keys_and_values >>
|
||||
tag!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub sam_stream_status <&str, Vec<(&str, &str)> >,
|
||||
do_parse!(
|
||||
tag_s!("STREAM STATUS ") >>
|
||||
opts: keys_and_values >>
|
||||
tag_s!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
do_parse!(
|
||||
tag!("STREAM STATUS ") >>
|
||||
opts: keys_and_values >>
|
||||
tag!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub sam_naming_reply <&str, Vec<(&str, &str)> >,
|
||||
do_parse!(
|
||||
tag_s!("NAMING REPLY ") >>
|
||||
opts: keys_and_values >>
|
||||
tag_s!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
do_parse!(
|
||||
tag!("NAMING REPLY ") >>
|
||||
opts: keys_and_values >>
|
||||
tag!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub sam_dest_reply <&str, Vec<(&str, &str)> >,
|
||||
do_parse!(
|
||||
tag_s!("DEST REPLY ") >>
|
||||
opts: keys_and_values >>
|
||||
tag_s!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
do_parse!(
|
||||
tag!("DEST REPLY ") >>
|
||||
opts: keys_and_values >>
|
||||
tag!("\n") >>
|
||||
(opts)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nom::ErrorKind;
|
||||
use nom::IResult::Done;
|
||||
use nom::IResult::Error;
|
||||
use nom::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn hello() {
|
||||
use crate::parsers::sam_hello;
|
||||
#[test]
|
||||
fn hello() {
|
||||
use crate::parsers::sam_hello;
|
||||
|
||||
assert_eq!(
|
||||
sam_hello("HELLO REPLY RESULT=OK VERSION=3.1\n"),
|
||||
Done("", vec![("RESULT", "OK"), ("VERSION", "3.1")])
|
||||
);
|
||||
assert_eq!(
|
||||
sam_hello("HELLO REPLY RESULT=NOVERSION\n"),
|
||||
Done("", vec![("RESULT", "NOVERSION")])
|
||||
);
|
||||
assert_eq!(
|
||||
sam_hello("HELLO REPLY RESULT=I2P_ERROR MESSAGE=\"Something failed\"\n"),
|
||||
Done(
|
||||
"",
|
||||
vec![("RESULT", "I2P_ERROR"), ("MESSAGE", "Something failed")]
|
||||
)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
sam_hello("HELLO REPLY RESULT=OK VERSION=3.1\n"),
|
||||
Ok(("", vec![("RESULT", "OK"), ("VERSION", "3.1")]))
|
||||
);
|
||||
assert_eq!(
|
||||
sam_hello("HELLO REPLY RESULT=NOVERSION\n"),
|
||||
Ok(("", vec![("RESULT", "NOVERSION")]))
|
||||
);
|
||||
assert_eq!(
|
||||
sam_hello("HELLO REPLY RESULT=I2P_ERROR MESSAGE=\"Something failed\"\n"),
|
||||
Ok((
|
||||
"",
|
||||
vec![("RESULT", "I2P_ERROR"), ("MESSAGE", "Something failed")]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_status() {
|
||||
use crate::parsers::sam_session_status;
|
||||
#[test]
|
||||
fn session_status() {
|
||||
use crate::parsers::sam_session_status;
|
||||
|
||||
assert_eq!(
|
||||
sam_session_status("SESSION STATUS RESULT=OK DESTINATION=privkey\n"),
|
||||
Done("", vec![("RESULT", "OK"), ("DESTINATION", "privkey")])
|
||||
);
|
||||
assert_eq!(
|
||||
sam_session_status("SESSION STATUS RESULT=DUPLICATED_ID\n"),
|
||||
Done("", vec![("RESULT", "DUPLICATED_ID")])
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
sam_session_status("SESSION STATUS RESULT=OK DESTINATION=privkey\n"),
|
||||
Ok(("", vec![("RESULT", "OK"), ("DESTINATION", "privkey")]))
|
||||
);
|
||||
assert_eq!(
|
||||
sam_session_status("SESSION STATUS RESULT=DUPLICATED_ID\n"),
|
||||
Ok(("", vec![("RESULT", "DUPLICATED_ID")]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_status() {
|
||||
use crate::parsers::sam_stream_status;
|
||||
#[test]
|
||||
fn stream_status() {
|
||||
use crate::parsers::sam_stream_status;
|
||||
|
||||
assert_eq!(
|
||||
sam_stream_status("STREAM STATUS RESULT=OK\n"),
|
||||
Done("", vec![("RESULT", "OK")])
|
||||
);
|
||||
assert_eq!(
|
||||
sam_stream_status(
|
||||
"STREAM STATUS RESULT=CANT_REACH_PEER MESSAGE=\"Can't reach peer\"\n"
|
||||
),
|
||||
Done(
|
||||
"",
|
||||
vec![
|
||||
("RESULT", "CANT_REACH_PEER"),
|
||||
("MESSAGE", "Can't reach peer")
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
sam_stream_status("STREAM STATUS RESULT=OK\n"),
|
||||
Ok(("", vec![("RESULT", "OK")]))
|
||||
);
|
||||
assert_eq!(
|
||||
sam_stream_status(
|
||||
"STREAM STATUS RESULT=CANT_REACH_PEER MESSAGE=\"Can't reach peer\"\n"
|
||||
),
|
||||
Ok((
|
||||
"",
|
||||
vec![
|
||||
("RESULT", "CANT_REACH_PEER"),
|
||||
("MESSAGE", "Can't reach peer")
|
||||
]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn naming_reply() {
|
||||
use crate::parsers::sam_naming_reply;
|
||||
#[test]
|
||||
fn naming_reply() {
|
||||
use crate::parsers::sam_naming_reply;
|
||||
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMING REPLY RESULT=OK NAME=name VALUE=dest\n"),
|
||||
Done(
|
||||
"",
|
||||
vec![("RESULT", "OK"), ("NAME", "name"), ("VALUE", "dest")]
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMING REPLY RESULT=KEY_NOT_FOUND\n"),
|
||||
Done("", vec![("RESULT", "KEY_NOT_FOUND")])
|
||||
);
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMING REPLY RESULT=OK NAME=name VALUE=dest\n"),
|
||||
Ok((
|
||||
"",
|
||||
vec![("RESULT", "OK"), ("NAME", "name"), ("VALUE", "dest")]
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMING REPLY RESULT=KEY_NOT_FOUND\n"),
|
||||
Ok(("", vec![("RESULT", "KEY_NOT_FOUND")]))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMINGREPLY RESULT=KEY_NOT_FOUND\n"),
|
||||
Error(ErrorKind::Tag)
|
||||
);
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMING REPLY RESULT=KEY_NOT_FOUND\n"),
|
||||
Error(ErrorKind::Tag)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMINGREPLY RESULT=KEY_NOT_FOUND\n")
|
||||
.unwrap_err()
|
||||
.into_error_kind(),
|
||||
ErrorKind::Tag
|
||||
);
|
||||
assert_eq!(
|
||||
sam_naming_reply("NAMING REPLY RESULT=KEY_NOT_FOUND\n")
|
||||
.unwrap_err()
|
||||
.into_error_kind(),
|
||||
ErrorKind::Tag
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dest_reply() {
|
||||
use crate::parsers::sam_dest_reply;
|
||||
#[test]
|
||||
fn dest_reply() {
|
||||
use crate::parsers::sam_dest_reply;
|
||||
|
||||
assert_eq!(
|
||||
sam_dest_reply("DEST REPLY PUB=foo PRIV=foobar\n"),
|
||||
Done("", vec![("PUB", "foo"), ("PRIV", "foobar")])
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
sam_dest_reply("DEST REPLY PUB=foo PRIV=foobar\n"),
|
||||
Ok(("", vec![("PUB", "foo"), ("PRIV", "foobar")]))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
521
src/sam.rs
521
src/sam.rs
@@ -2,228 +2,425 @@ use std::io::prelude::*;
|
||||
|
||||
use std::clone::Clone;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::io::{BufReader, Error, ErrorKind};
|
||||
use std::io::{self, BufReader};
|
||||
use std::net::{Shutdown, SocketAddr, TcpStream, ToSocketAddrs};
|
||||
use std::time::Duration;
|
||||
|
||||
use log::debug;
|
||||
use nom::IResult;
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::{self, Rng};
|
||||
|
||||
use crate::parsers::{sam_hello, sam_naming_reply, sam_session_status, sam_stream_status};
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::net::{I2pAddr, I2pSocketAddr};
|
||||
use crate::parsers::{
|
||||
sam_dest_reply, sam_hello, sam_naming_reply, sam_session_status, sam_stream_status,
|
||||
};
|
||||
use crate::sam_options::{SAMOptions, SignatureType};
|
||||
|
||||
pub static DEFAULT_API: &'static str = "127.0.0.1:7656";
|
||||
|
||||
static SAM_MIN: &'static str = "3.0";
|
||||
static SAM_MAX: &'static str = "3.1";
|
||||
static SAM_MAX: &'static str = "3.2";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SessionStyle {
|
||||
Datagram,
|
||||
Raw,
|
||||
Stream,
|
||||
Datagram,
|
||||
Raw,
|
||||
Stream,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SamConnection {
|
||||
conn: TcpStream,
|
||||
#[cfg(feature = "public-conn")]
|
||||
pub conn: TcpStream,
|
||||
#[cfg(not(feature = "public-conn"))]
|
||||
conn: TcpStream,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Session {
|
||||
sam: SamConnection,
|
||||
local_dest: String,
|
||||
#[cfg(feature = "public-conn")]
|
||||
pub sam: SamConnection,
|
||||
#[cfg(not(feature = "public-conn"))]
|
||||
sam: SamConnection,
|
||||
pub local_dest: String,
|
||||
pub nickname: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StreamConnect {
|
||||
sam: SamConnection,
|
||||
session: Session,
|
||||
peer_dest: String,
|
||||
peer_port: u16,
|
||||
local_port: u16,
|
||||
#[cfg(feature = "public-conn")]
|
||||
pub sam: SamConnection,
|
||||
#[cfg(not(feature = "public-conn"))]
|
||||
sam: SamConnection,
|
||||
#[cfg(feature = "public-conn")]
|
||||
pub session: Session,
|
||||
#[cfg(not(feature = "public-conn"))]
|
||||
session: Session,
|
||||
pub peer_dest: String,
|
||||
pub peer_port: u16,
|
||||
pub local_port: u16,
|
||||
}
|
||||
|
||||
impl SessionStyle {
|
||||
fn string(&self) -> &str {
|
||||
match *self {
|
||||
SessionStyle::Datagram => "DATAGRAM",
|
||||
SessionStyle::Raw => "RAW",
|
||||
SessionStyle::Stream => "STREAM",
|
||||
}
|
||||
}
|
||||
fn string(&self) -> &str {
|
||||
match *self {
|
||||
SessionStyle::Datagram => "DATAGRAM",
|
||||
SessionStyle::Raw => "RAW",
|
||||
SessionStyle::Stream => "STREAM",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_response<'a>(vec: &'a [(&str, &str)]) -> Result<HashMap<&'a str, &'a str>, Error> {
|
||||
let new_vec = vec.clone();
|
||||
let map: HashMap<&str, &str> = new_vec.iter().map(|&(k, v)| (k, v)).collect();
|
||||
let res = map.get("RESULT").unwrap_or(&"OK").clone();
|
||||
let msg = map.get("MESSAGE").unwrap_or(&"").clone();
|
||||
match res {
|
||||
"OK" => Ok(map),
|
||||
"CANT_REACH_PEER" | "KEY_NOT_FOUND" | "PEER_NOT_FOUND" => {
|
||||
Err(Error::new(ErrorKind::NotFound, msg))
|
||||
}
|
||||
"DUPLICATED_DEST" => Err(Error::new(ErrorKind::AddrInUse, msg)),
|
||||
"INVALID_KEY" | "INVALID_ID" => Err(Error::new(ErrorKind::InvalidInput, msg)),
|
||||
"TIMEOUT" => Err(Error::new(ErrorKind::TimedOut, msg)),
|
||||
"I2P_ERROR" => Err(Error::new(ErrorKind::Other, msg)),
|
||||
_ => Err(Error::new(ErrorKind::Other, msg)),
|
||||
}
|
||||
let new_vec = vec.clone();
|
||||
let map: HashMap<&str, &str> = new_vec.iter().map(|&(k, v)| (k, v)).collect();
|
||||
let res = map.get("RESULT").unwrap_or(&"OK").clone();
|
||||
let msg = map.get("MESSAGE").unwrap_or(&"").clone();
|
||||
match res {
|
||||
"OK" => Ok(map),
|
||||
"CANT_REACH_PEER" => Err(ErrorKind::SAMCantReachPeer(msg.to_string()).into()),
|
||||
"KEY_NOT_FOUND" => Err(ErrorKind::SAMKeyNotFound(msg.to_string()).into()),
|
||||
"PEER_NOT_FOUND" => Err(ErrorKind::SAMPeerNotFound(msg.to_string()).into()),
|
||||
"DUPLICATED_DEST" => Err(ErrorKind::SAMDuplicatedDest(msg.to_string()).into()),
|
||||
"INVALID_KEY" => Err(ErrorKind::SAMInvalidKey(msg.to_string()).into()),
|
||||
"INVALID_ID" => Err(ErrorKind::SAMInvalidId(msg.to_string()).into()),
|
||||
"TIMEOUT" => Err(ErrorKind::SAMTimeout(msg.to_string()).into()),
|
||||
"I2P_ERROR" => Err(ErrorKind::SAMI2PError(msg.to_string()).into()),
|
||||
_ => Err(ErrorKind::SAMInvalidMessage(msg.to_string()).into()),
|
||||
}
|
||||
}
|
||||
|
||||
impl SamConnection {
|
||||
fn send<F>(&mut self, msg: String, reply_parser: F) -> Result<HashMap<String, String>, Error>
|
||||
where
|
||||
F: Fn(&str) -> IResult<&str, Vec<(&str, &str)>>,
|
||||
{
|
||||
debug!("-> {}", &msg);
|
||||
self.conn.write_all(&msg.into_bytes())?;
|
||||
fn send<F>(&mut self, msg: String, reply_parser: F) -> Result<HashMap<String, String>, Error>
|
||||
where
|
||||
F: Fn(&str) -> IResult<&str, Vec<(&str, &str)>>,
|
||||
{
|
||||
debug!("-> {}", &msg);
|
||||
self.conn.write_all(&msg.into_bytes())?;
|
||||
|
||||
let mut reader = BufReader::new(&self.conn);
|
||||
let mut buffer = String::new();
|
||||
reader.read_line(&mut buffer)?;
|
||||
debug!("<- {}", &buffer);
|
||||
let mut reader = BufReader::new(&self.conn);
|
||||
let mut buffer = String::new();
|
||||
reader.read_line(&mut buffer)?;
|
||||
debug!("<- {}", &buffer);
|
||||
|
||||
let response = reply_parser(&buffer);
|
||||
let vec_opts = response.unwrap().1;
|
||||
verify_response(&vec_opts).map(|m| {
|
||||
m.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
let vec_opts = reply_parser(&buffer)?.1;
|
||||
verify_response(&vec_opts).map(|m| {
|
||||
m.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn handshake(&mut self) -> Result<HashMap<String, String>, Error> {
|
||||
let hello_msg = format!(
|
||||
"HELLO VERSION MIN={min} MAX={max} \n",
|
||||
min = SAM_MIN,
|
||||
max = SAM_MAX
|
||||
);
|
||||
self.send(hello_msg, sam_hello)
|
||||
}
|
||||
fn handshake(&mut self) -> Result<HashMap<String, String>, Error> {
|
||||
let hello_msg = format!(
|
||||
"HELLO VERSION MIN={min} MAX={max} \n",
|
||||
min = SAM_MIN,
|
||||
max = SAM_MAX
|
||||
);
|
||||
self.send(hello_msg, sam_hello)
|
||||
}
|
||||
|
||||
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<SamConnection, Error> {
|
||||
let tcp_stream = TcpStream::connect(addr)?;
|
||||
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<SamConnection, Error> {
|
||||
let tcp_stream = TcpStream::connect(addr)?;
|
||||
|
||||
let mut socket = SamConnection { conn: tcp_stream };
|
||||
let mut socket = SamConnection { conn: tcp_stream };
|
||||
socket.handshake()?;
|
||||
|
||||
socket.handshake()?;
|
||||
Ok(socket)
|
||||
}
|
||||
|
||||
Ok(socket)
|
||||
}
|
||||
// TODO: Implement a lookup table
|
||||
pub fn naming_lookup(&mut self, name: &str) -> Result<String, Error> {
|
||||
let naming_lookup_msg = format!("NAMING LOOKUP NAME={name} \n", name = name);
|
||||
let ret = self.send(naming_lookup_msg, sam_naming_reply)?;
|
||||
Ok(ret["VALUE"].clone())
|
||||
}
|
||||
|
||||
// TODO: Implement a lookup table
|
||||
pub fn naming_lookup(&mut self, name: &str) -> Result<String, Error> {
|
||||
let create_naming_lookup_msg = format!("NAMING LOOKUP NAME={name} \n", name = name);
|
||||
let ret = self.send(create_naming_lookup_msg, sam_naming_reply)?;
|
||||
Ok(ret["VALUE"].clone())
|
||||
}
|
||||
pub fn generate_destination(
|
||||
&mut self,
|
||||
signature_type: SignatureType,
|
||||
) -> Result<(String, String), Error> {
|
||||
let dest_gen_msg = format!(
|
||||
"DEST GENERATE SIGNATURE_TYPE={signature_type} \n",
|
||||
signature_type = signature_type.to_string(),
|
||||
);
|
||||
let ret = self.send(dest_gen_msg, sam_dest_reply)?;
|
||||
Ok((ret["PUB"].clone(), ret["PRIV"].clone()))
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<SamConnection> {
|
||||
self.conn.try_clone().map(|s| SamConnection { conn: s })
|
||||
}
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> Result<(), Error> {
|
||||
self.conn.set_nonblocking(nonblocking).map_err(|e| e.into())
|
||||
}
|
||||
pub fn set_read_timeout(&self, duration: Option<Duration>) -> std::io::Result<()> {
|
||||
self.conn.set_read_timeout(duration)
|
||||
}
|
||||
pub fn set_write_timeout(&self, duration: Option<Duration>) -> std::io::Result<()> {
|
||||
self.conn.set_write_timeout(duration)
|
||||
}
|
||||
pub fn duplicate(&self) -> Result<SamConnection, Error> {
|
||||
self.conn
|
||||
.try_clone()
|
||||
.map(|s| SamConnection { conn: s })
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
/// attempts to return a handle to the underlying socket
|
||||
pub fn try_clone(&self) -> std::io::Result<TcpStream> {
|
||||
self.conn.try_clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn create<A: ToSocketAddrs>(
|
||||
sam_addr: A,
|
||||
destination: &str,
|
||||
nickname: &str,
|
||||
style: SessionStyle,
|
||||
) -> Result<Session, Error> {
|
||||
let mut sam = SamConnection::connect(sam_addr).unwrap();
|
||||
let create_session_msg = format!(
|
||||
"SESSION CREATE STYLE={style} ID={nickname} DESTINATION={destination} \n",
|
||||
style = style.string(),
|
||||
nickname = nickname,
|
||||
destination = destination
|
||||
);
|
||||
/// Create a new session using all provided parameters
|
||||
pub fn create<A: ToSocketAddrs>(
|
||||
sam_addr: A,
|
||||
destination: &str,
|
||||
nickname: &str,
|
||||
style: SessionStyle,
|
||||
options: SAMOptions,
|
||||
) -> Result<Session, Error> {
|
||||
let mut sam = SamConnection::connect(sam_addr)?;
|
||||
let create_session_msg = format!(
|
||||
// values for SIGNATURE_TYPE and leaseSetEncType taken from
|
||||
// https://github.com/eyedeekay/goSam/blob/62cade9ebc26e48ff32a517ef94212fc90aa92cd/client.go#L169
|
||||
// https://github.com/eyedeekay/goSam/blob/62cade9ebc26e48ff32a517ef94212fc90aa92cd/client.go#L166
|
||||
"SESSION CREATE STYLE={style} ID={nickname} DESTINATION={destination} {options}\n",
|
||||
style = style.string(),
|
||||
nickname = nickname,
|
||||
destination = destination,
|
||||
options = options.options(),
|
||||
);
|
||||
|
||||
sam.send(create_session_msg, sam_session_status)?;
|
||||
sam.send(create_session_msg, sam_session_status)?;
|
||||
|
||||
let local_dest = sam.naming_lookup("ME")?;
|
||||
let local_dest = sam.naming_lookup("ME")?;
|
||||
|
||||
Ok(Session {
|
||||
sam: sam,
|
||||
local_dest: local_dest,
|
||||
})
|
||||
}
|
||||
Ok(Session {
|
||||
sam: sam,
|
||||
local_dest: local_dest,
|
||||
nickname: nickname.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sam_api(&self) -> io::Result<SocketAddr> {
|
||||
self.sam.conn.peer_addr()
|
||||
}
|
||||
/// Create a new session identified by the provided destination. Auto-generates
|
||||
/// a nickname uniquely associated with the new session.
|
||||
pub fn from_destination<A: ToSocketAddrs>(
|
||||
sam_addr: A,
|
||||
destination: &str,
|
||||
options: SAMOptions,
|
||||
) -> Result<Session, Error> {
|
||||
Self::create(
|
||||
sam_addr,
|
||||
destination,
|
||||
&nickname(),
|
||||
SessionStyle::Stream,
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn naming_lookup(&mut self, name: &str) -> io::Result<String> {
|
||||
self.sam.naming_lookup(name)
|
||||
}
|
||||
/// Convenience constructor to create a new transient session with an
|
||||
/// auto-generated nickname.
|
||||
pub fn transient<A: ToSocketAddrs>(sam_addr: A, options: SAMOptions) -> Result<Session, Error> {
|
||||
Self::create(
|
||||
sam_addr,
|
||||
"TRANSIENT",
|
||||
&nickname(),
|
||||
SessionStyle::Stream,
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<Session> {
|
||||
self.sam.duplicate().map(|s| Session {
|
||||
sam: s,
|
||||
local_dest: self.local_dest.clone(),
|
||||
})
|
||||
}
|
||||
pub fn sam_api(&self) -> Result<SocketAddr, Error> {
|
||||
self.sam.conn.peer_addr().map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn naming_lookup(&mut self, name: &str) -> Result<String, Error> {
|
||||
self.sam.naming_lookup(name)
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> Result<Session, Error> {
|
||||
self.sam
|
||||
.duplicate()
|
||||
.map(|s| Session {
|
||||
sam: s,
|
||||
local_dest: self.local_dest.clone(),
|
||||
nickname: self.nickname.clone(),
|
||||
})
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
/// attempts to return a handle to the underlying socket
|
||||
pub fn try_clone(&self) -> std::io::Result<TcpStream> {
|
||||
self.sam.try_clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamConnect {
|
||||
pub fn new<A: ToSocketAddrs>(
|
||||
sam_addr: A,
|
||||
destination: &str,
|
||||
port: u16,
|
||||
nickname: &str,
|
||||
) -> io::Result<StreamConnect> {
|
||||
let mut session = Session::create(sam_addr, "TRANSIENT", nickname, SessionStyle::Stream)?;
|
||||
/// Create a new SAM client connection to the provided destination and port.
|
||||
/// Also creates a new transient session to support the connection.
|
||||
pub fn new<A: ToSocketAddrs>(
|
||||
sam_addr: A,
|
||||
destination: &str,
|
||||
port: u16,
|
||||
options: SAMOptions,
|
||||
) -> Result<StreamConnect, Error> {
|
||||
let session = Session::transient(sam_addr, options)?;
|
||||
Self::with_session(&session, destination, port)
|
||||
}
|
||||
|
||||
let mut sam = SamConnection::connect(session.sam_api()?).unwrap();
|
||||
let create_stream_msg = format!(
|
||||
"STREAM CONNECT ID={nickname} DESTINATION={destination} SILENT=false TO_PORT={port}\n",
|
||||
nickname = nickname,
|
||||
destination = destination,
|
||||
port = port
|
||||
);
|
||||
/// Create a new SAM client connection to the provided destination and port
|
||||
/// using the provided session.
|
||||
pub fn with_session(session: &Session, dest: &str, port: u16) -> Result<StreamConnect, Error> {
|
||||
let mut sam = SamConnection::connect(session.sam_api()?).unwrap();
|
||||
let dest = sam.naming_lookup(dest)?;
|
||||
|
||||
sam.send(create_stream_msg, sam_stream_status)?;
|
||||
let mut stream_msg = format!(
|
||||
"STREAM CONNECT ID={nickname} DESTINATION={destination} SILENT=false",
|
||||
nickname = session.nickname,
|
||||
destination = dest,
|
||||
);
|
||||
if port > 0 {
|
||||
stream_msg.push_str(&format!(" TO_PORT={port}\n", port = port));
|
||||
} else {
|
||||
stream_msg.push_str("\n");
|
||||
}
|
||||
|
||||
let peer_dest = session.naming_lookup(destination)?;
|
||||
sam.send(stream_msg, sam_stream_status)?;
|
||||
|
||||
Ok(StreamConnect {
|
||||
sam: sam,
|
||||
session: session,
|
||||
peer_dest: peer_dest,
|
||||
peer_port: port,
|
||||
local_port: 0,
|
||||
})
|
||||
}
|
||||
Ok(StreamConnect {
|
||||
sam: sam,
|
||||
session: session.duplicate()?,
|
||||
peer_dest: dest,
|
||||
peer_port: port,
|
||||
local_port: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn peer_addr(&self) -> io::Result<(String, u16)> {
|
||||
Ok((self.peer_dest.clone(), self.peer_port))
|
||||
}
|
||||
pub fn peer_addr(&self) -> Result<(String, u16), Error> {
|
||||
Ok((self.peer_dest.clone(), self.peer_port))
|
||||
}
|
||||
|
||||
pub fn local_addr(&self) -> io::Result<(String, u16)> {
|
||||
Ok((self.session.local_dest.clone(), self.local_port))
|
||||
}
|
||||
pub fn local_addr(&self) -> Result<(String, u16), Error> {
|
||||
Ok((self.session.local_dest.clone(), self.local_port))
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
|
||||
self.sam.conn.shutdown(how)
|
||||
}
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> Result<(), Error> {
|
||||
self.sam.set_nonblocking(nonblocking)
|
||||
}
|
||||
pub fn set_read_timeout(&self, duration: Option<Duration>) -> std::io::Result<()> {
|
||||
self.sam.set_read_timeout(duration)
|
||||
}
|
||||
pub fn set_write_timeout(&self, duration: Option<Duration>) -> std::io::Result<()> {
|
||||
self.sam.set_write_timeout(duration)
|
||||
}
|
||||
pub fn shutdown(&self, how: Shutdown) -> Result<(), Error> {
|
||||
self.sam.conn.shutdown(how).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<StreamConnect> {
|
||||
Ok(StreamConnect {
|
||||
sam: self.sam.duplicate()?,
|
||||
session: self.session.duplicate()?,
|
||||
peer_dest: self.peer_dest.clone(),
|
||||
peer_port: self.peer_port,
|
||||
local_port: self.local_port,
|
||||
})
|
||||
}
|
||||
pub fn duplicate(&self) -> Result<StreamConnect, Error> {
|
||||
Ok(StreamConnect {
|
||||
sam: self.sam.duplicate()?,
|
||||
session: self.session.duplicate()?,
|
||||
peer_dest: self.peer_dest.clone(),
|
||||
peer_port: self.peer_port,
|
||||
local_port: self.local_port,
|
||||
})
|
||||
}
|
||||
/// calls try_clone against the Session object
|
||||
pub fn try_clone_session(&self) -> std::io::Result<TcpStream> {
|
||||
self.session.try_clone()
|
||||
}
|
||||
/// calls try_clone against the SamConnection object
|
||||
pub fn try_clone_sam(&self) -> std::io::Result<TcpStream> {
|
||||
self.sam.try_clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for StreamConnect {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.sam.conn.read(buf)
|
||||
}
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.sam.conn.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for StreamConnect {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.sam.conn.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.sam.conn.flush()
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.sam.conn.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.sam.conn.flush()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StreamForward {
|
||||
session: Session,
|
||||
}
|
||||
|
||||
impl StreamForward {
|
||||
pub fn new<A: ToSocketAddrs>(sam_addr: A, options: SAMOptions) -> Result<StreamForward, Error> {
|
||||
Ok(StreamForward {
|
||||
session: Session::transient(sam_addr, options)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new SAM client connection to the provided destination and port
|
||||
/// using the provided session.
|
||||
pub fn with_session(session: &Session) -> Result<StreamForward, Error> {
|
||||
Ok(StreamForward {
|
||||
session: session.duplicate()?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn accept(&self) -> Result<(StreamConnect, I2pSocketAddr), Error> {
|
||||
let mut sam_conn = SamConnection::connect(self.session.sam_api()?).unwrap();
|
||||
|
||||
let accept_stream_msg = format!(
|
||||
"STREAM ACCEPT ID={nickname} SILENT=false\n",
|
||||
nickname = self.session.nickname,
|
||||
);
|
||||
sam_conn.send(accept_stream_msg, sam_stream_status)?;
|
||||
|
||||
let mut stream = StreamConnect {
|
||||
sam: sam_conn,
|
||||
session: self.session.duplicate()?,
|
||||
peer_dest: "".to_string(),
|
||||
// port only provided with SAM v3.2+ (not on i2pd)
|
||||
peer_port: 0,
|
||||
local_port: 0,
|
||||
};
|
||||
|
||||
// TODO use a parser combinator, perhaps move down to sam.rs
|
||||
let destination: String = {
|
||||
let mut buf_read = io::BufReader::new(stream.duplicate()?);
|
||||
let mut dest_line = String::new();
|
||||
buf_read.read_line(&mut dest_line)?;
|
||||
dest_line.split(" ").next().unwrap_or("").trim().to_string()
|
||||
};
|
||||
if destination.is_empty() {
|
||||
return Err(
|
||||
ErrorKind::SAMKeyNotFound("No b64 destination in accept".to_string()).into(),
|
||||
);
|
||||
}
|
||||
|
||||
let addr = I2pSocketAddr::new(I2pAddr::from_b64(&destination)?, 0);
|
||||
stream.peer_dest = destination;
|
||||
|
||||
Ok((stream, addr))
|
||||
}
|
||||
|
||||
pub fn local_addr(&self) -> Result<(String, u16), Error> {
|
||||
Ok((self.session.local_dest.clone(), 0))
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> Result<StreamForward, Error> {
|
||||
Ok(StreamForward {
|
||||
session: self.session.duplicate()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nickname() -> String {
|
||||
let suffix: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(8)
|
||||
.collect();
|
||||
format!("i2prs-{}", suffix)
|
||||
}
|
||||
|
695
src/sam_options.rs
Normal file
695
src/sam_options.rs
Normal file
@@ -0,0 +1,695 @@
|
||||
//! objects used for configuration SAM sessions
|
||||
//! I2CP client and router options taken from https://geti2p.net/en/docs/protocol/i2cp
|
||||
//! SAMv3 options taken from https://geti2p.net/en/docs/api/samv3#options
|
||||
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// options used when interacting with the SAM bridge
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SAMOptions {
|
||||
pub from_port: Option<u16>,
|
||||
pub to_port: Option<u16>,
|
||||
pub i2cp_options: Option<I2CPOptions>,
|
||||
pub signature_type: SignatureType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct I2CPOptions {
|
||||
pub router_options: Option<I2CPRouterOptions>,
|
||||
pub client_options: Option<I2CPClientOptions>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct I2CPRouterOptions {
|
||||
/// The timeout (ms) for all sent messages. Unused. See the protocol specification for per-message settings.
|
||||
pub client_message_timeout: Option<u32>,
|
||||
pub crypto_options: Option<I2CPRouterCryptoOptions>,
|
||||
/// Should generally be set to true for clients and false for servers
|
||||
pub dont_publish_lease_set: Option<bool>,
|
||||
/// If true, the router just sends the MessagePayload instead of sending a MessageStatus and awaiting a ReceiveMessageBegin.
|
||||
pub fast_receive: Option<bool>,
|
||||
/// The type of authentication for encrypted LS2. 0 for no per-client authentication (the default); 1 for DH per-client authentication; 2 for PSK per-client authentication. See proposal 123.
|
||||
pub lease_set_auth_type: Option<LeaseSetAuthType>,
|
||||
/// The encryption type to be used, as of 0.9.38. Interpreted client-side, but also passed to the router in the SessionConfig, to declare intent and check support. As of 0.9.39, may be comma-separated values for multiple types. See PublicKey in common strutures spec for values. See proposals 123, 144, and 145.
|
||||
pub lease_set_enc_type: Option<LeaseSetEncType>,
|
||||
/// The expiration of the offline signature, 4 bytes, seconds since the epoch. See proposal 123.
|
||||
pub lease_set_offline_expiration: Option<LeaseSetOfflineExpiration>,
|
||||
/// The base 64 of the offline signature. See proposal 123.
|
||||
pub lease_set_offline_signature: Option<LeaseSetOfflineSignature>,
|
||||
/// A base 64 X25519 private key for the router to use to decrypt the encrypted LS2 locally, only if per-client authentication is enabled. Optionally preceded by the key type and ':'. Only "ECIES_X25519:" is supported, which is the default. See proposal 123. Do not confuse with i2cp.leaseSetPrivateKey which is for the leaseset encryption keys.
|
||||
pub lease_set_priv_key: Option<LeaseSetPrivKey>,
|
||||
/// Base 64 encoded UTF-8 secret used to blind the leaseset address. See proposal 123.
|
||||
pub lease_set_secret: Option<LeaseSetSecret>,
|
||||
/// The base 64 of the transient private key, prefixed by an optional sig type number or name, default DSA_SHA1. See proposal 123.
|
||||
pub lease_set_transient_public_key: Option<LeaseSetTransientPublicKey>,
|
||||
/// The type of leaseset to be sent in the CreateLeaseSet2 Message. Interpreted client-side, but also passed to the router in the SessionConfig, to declare intent and check support. See proposal 123.
|
||||
pub lease_set_type: Option<LeaseSetType>,
|
||||
/// Guaranteed is disabled; None implemented in 0.8.1; the streaming lib default is None as of 0.8.1, the client side default is None as of 0.9.4
|
||||
pub message_reliability: Option<MessageReliability>,
|
||||
pub username: Option<String>,
|
||||
pub password: Option<String>,
|
||||
/// inbound tunnel optoins
|
||||
pub inbound: Option<I2CPTunnelInboundOptions>,
|
||||
pub outbound: Option<I2CPTunnelOutboundOptions>,
|
||||
/// Set to false to disable ever bundling a reply LeaseSet. For clients that do not publish their LeaseSet, this option must be true for any reply to be possible. "true" is also recommended for multihomed servers with long connection times.
|
||||
///
|
||||
/// Setting to "false" may save significant outbound bandwidth, especially if the client is configured with a large number of inbound tunnels (Leases). If replies are still required, this may shift the bandwidth burden to the far-end client and the floodfill. There are several cases where "false" may be appropriate:
|
||||
///
|
||||
/// Unidirectional communication, no reply required
|
||||
/// LeaseSet is published and higher reply latency is acceptable
|
||||
/// LeaseSet is published, client is a "server", all connections are inbound so the connecting far-end destination obviously has the leaseset already. Connections are either short, or it is acceptable for latency on a long-lived connection to temporarily increase while the other end re-fetches the LeaseSet after expiration. HTTP servers may fit these requirements.
|
||||
///
|
||||
pub should_bundle_reply_info: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct I2CPClientOptions {
|
||||
/// (ms) Idle time required (default 30 minutes)
|
||||
pub close_idle_time: Option<u64>,
|
||||
/// Close I2P session when idle
|
||||
pub close_on_idle: Option<bool>,
|
||||
/// Encrypt the lease
|
||||
pub encrypt_lease_set: Option<bool>,
|
||||
/// If true, the router just sends the MessagePayload instead of sending a MessageStatus and awaiting a ReceiveMessageBegin.
|
||||
pub fast_receive: Option<bool>,
|
||||
/// Gzip outbound data
|
||||
pub gzip: Option<bool>,
|
||||
/// The type of authentication for encrypted LS2. 0 for no per-client authentication (the default); 1 for DH per-client authentication; 2 for PSK per-client authentication. See proposal 123.
|
||||
pub lease_set_auth_type: Option<LeaseSetAuthType>,
|
||||
/// The sig type of the blinded key for encrypted LS2. Default depends on the destination sig type. See proposal 123.
|
||||
pub lease_set_blinded_type: Option<LeaseSetBlindedType>,
|
||||
/// The encryption type to be used, as of 0.9.38. Interpreted client-side, but also passed to the router in the SessionConfig, to declare intent and check support. As of 0.9.39, may be comma-separated values for multiple types. See also i2cp.leaseSetPrivateKey. See PublicKey in common strutures spec for values. See proposals 123, 144, and 145.
|
||||
pub lease_set_enc_type: Option<LeaseSetEncType>,
|
||||
/// For encrypted leasesets. Base 64 SessionKey (44 characters)
|
||||
pub lease_set_key: Option<LeaseSetKey>,
|
||||
/// Base 64 private keys for encryption. Optionally preceded by the encryption type name or number and ':'. For LS1, only one key is supported, and only "0:" or "ELGAMAL_2048:" is supported, which is the default. As of 0.9.39, for LS2, multiple keys may be comma-separated, and each key must be a different encryption type. I2CP will generate the public key from the private key. Use for persistent leaseset keys across restarts. See proposals 123, 144, and 145. See also i2cp.leaseSetEncType. Do not confuse with i2cp.leaseSetPrivKey which is for encrypted LS2.
|
||||
pub lease_set_private_key: Option<LeaseSetPrivateKey>,
|
||||
/// Base 64 encoded UTF-8 secret used to blind the leaseset address. See proposal 123.
|
||||
pub lease_set_secret: Option<LeaseSetSecret>,
|
||||
/// The type of leaseset to be sent in the CreateLeaseSet2 Message. Interpreted client-side, but also passed to the router in the SessionConfig, to declare intent and check support. See proposal 123.
|
||||
pub lease_set_signing_private_key: Option<LeaseSetSigningPrivateKey>,
|
||||
/// Guaranteed is disabled; None implemented in 0.8.1; None is the default as of 0.9.4
|
||||
pub message_reliability: Option<MessageReliability>,
|
||||
/// (ms) Idle time required (default 20 minutes, minimum 5 minutes)
|
||||
pub reduce_idle_time: Option<u64>,
|
||||
/// Reduce tunnel quantity when idle
|
||||
pub reduce_on_idle: Option<bool>,
|
||||
/// Tunnel quantity when reduced (applies to both inbound and outbound)
|
||||
pub reduce_quantity: Option<u8>,
|
||||
/// Connect to the router using SSL. If the client is running in the same JVM as a router, this option is ignored, and the client connects to that router internally.
|
||||
pub ssl: Option<bool>,
|
||||
/// Router hostname. If the client is running in the same JVM as a router, this option is ignored, and the client connects to that router internally
|
||||
pub tcp_host: Option<String>,
|
||||
/// Router I2CP port. If the client is running in the same JVM as a router, this option is ignored, and the client connects to that router internally.
|
||||
pub tcp_port: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct I2CPRouterCryptoOptions {
|
||||
/// Minimum number of ElGamal/AES Session Tags before we send more. Recommended: approximately tagsToSend * 2/3
|
||||
pub low_tag_threshold: Option<u8>,
|
||||
/// Inbound tag window for ECIES-X25519-AEAD-Ratchet. Local inbound tagset size. See proposal 144.
|
||||
pub ratchet_inbound_tags: Option<u64>,
|
||||
/// Outbound tag window for ECIES-X25519-AEAD-Ratchet. Advisory to send to the far-end in the options block. See proposal 144.
|
||||
pub ratchet_outbound_tags: Option<u64>,
|
||||
/// Number of ElGamal/AES Session Tags to send at a time. For clients with relatively low bandwidth per-client-pair (IRC, some UDP apps), this may be set lower.
|
||||
pub tags_to_send: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct I2CPTunnelInboundOptions {
|
||||
/// If incoming zero hop tunnel is allowed
|
||||
pub allow_zero_hop: Option<bool>,
|
||||
/// Number of redundant fail-over for tunnels in
|
||||
pub backup_quantity: Option<u8>,
|
||||
/// Number of IP bytes to match to determine if two routers should not be in the same tunnel. 0 to disable.
|
||||
pub ip_restriction: Option<u8>,
|
||||
/// Length of tunnels in
|
||||
pub length: Option<u8>,
|
||||
/// Random amount to add or subtract to the length of tunnels in. A positive number x means add a random amount from 0 to x inclusive. A negative number -x means add a random amount from -x to x inclusive. The router will limit the total length of the tunnel to 0 to 7 inclusive. The default variance was 1 prior to release 0.7.6.
|
||||
pub length_variance: Option<i8>,
|
||||
/// Number of tunnels in. Limit was increased from 6 to 16 in release 0.9; however, numbers higher than 6 are incompatible with older releases.
|
||||
pub quantity: Option<u8>,
|
||||
/// Used for consistent peer ordering across restarts.
|
||||
pub random_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct I2CPTunnelOutboundOptions {
|
||||
/// If outgoing zero hop tunnel is allowed
|
||||
pub allow_zero_hop: Option<bool>,
|
||||
/// Number of redundant fail-over for tunnels out
|
||||
pub backup_quantity: Option<u8>,
|
||||
/// Number of IP bytes to match to determine if two routers should not be in the same tunnel. 0 to disable.
|
||||
pub ip_restriction: Option<u8>,
|
||||
/// Length of tunnels out
|
||||
pub length: Option<u8>,
|
||||
/// Random amount to add or subtract to the length of tunnels in. A positive number x means add a random amount from 0 to x inclusive. A negative number -x means add a random amount from -x to x inclusive. The router will limit the total length of the tunnel to 0 to 7 inclusive. The default variance was 1 prior to release 0.7.6.
|
||||
pub length_variance: Option<i8>,
|
||||
/// Priority adjustment for outbound messages. Higher is higher priority.
|
||||
pub priority: Option<i8>,
|
||||
/// Number of tunnels in. Limit was increased from 6 to 16 in release 0.9; however, numbers higher than 6 are incompatible with older releases.
|
||||
pub quantity: Option<u8>,
|
||||
/// Used for consistent peer ordering across restarts.
|
||||
pub random_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// The base 64 of the offline signature. See proposal 123.
|
||||
pub struct LeaseSetOfflineSignature(String);
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// The encryption type to be used, as of 0.9.38. Interpreted client-side, but also passed to the router in the SessionConfig, to declare intent and check support. As of 0.9.39, may be comma-separated values for multiple types. See PublicKey in common strutures spec for values. See proposals 123, 144, and 145.
|
||||
/// https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
|
||||
pub struct LeaseSetEncType(String);
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// A base 64 X25519 private key for the router to use to decrypt the encrypted LS2 locally, only if per-client authentication is enabled. Optionally preceded by the key type and ':'. Only "ECIES_X25519:" is supported, which is the default. See proposal 123. Do not confuse with i2cp.leaseSetPrivateKey which is for the leaseset encryption keys.
|
||||
/// https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
|
||||
pub struct LeaseSetPrivKey(String);
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// Base 64 private keys for encryption. Optionally preceded by the encryption type name or number and ':'. For LS1, only one key is supported, and only "0:" or "ELGAMAL_2048:" is supported, which is the default. As of 0.9.39, for LS2, multiple keys may be comma-separated, and each key must be a different encryption type. I2CP will generate the public key from the private key. Use for persistent leaseset keys across restarts. See proposals 123, 144, and 145. See also i2cp.leaseSetEncType. Do not confuse with i2cp.leaseSetPrivKey which is for encrypted LS2.
|
||||
pub struct LeaseSetPrivateKey(String);
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// For encrypted leasesets. Base 64 SessionKey (44 characters)
|
||||
pub struct LeaseSetKey(String);
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// Base 64 encoded UTF-8 secret used to blind the leaseset address. See proposal 123.
|
||||
/// https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
|
||||
pub struct LeaseSetSecret(String);
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// The base 64 of the transient private key, prefixed by an optional sig type number or name, default DSA_SHA1. See proposal 123.
|
||||
/// https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
|
||||
pub struct LeaseSetTransientPublicKey(String);
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// Base 64 private key for signatures. Optionally preceded by the key type and ':'. DSA_SHA1 is the default. Key type must match the signature type in the destination. I2CP will generate the public key from the private key. Use for persistent leaseset keys across restarts.
|
||||
pub struct LeaseSetSigningPrivateKey(String);
|
||||
|
||||
/// The expiration of the offline signature, 4 bytes, seconds since the epoch. See proposal 123.
|
||||
pub type LeaseSetOfflineExpiration = [u8; 4];
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// The type of leaseset to be sent in the CreateLeaseSet2 Message. Interpreted client-side, but also passed to the router in the SessionConfig, to declare intent and check support. See proposal 123.
|
||||
pub struct LeaseSetType(u8);
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
/// The sig type of the blinded key for encrypted LS2. Default depends on the destination sig type. See proposal 123.
|
||||
pub struct LeaseSetBlindedType(u16);
|
||||
|
||||
/// The type of authentication for encrypted LS2. 0 for no per-client authentication (the default); 1 for DH per-client authentication; 2 for PSK per-client authentication. See proposal 123.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[repr(u64)]
|
||||
pub enum LeaseSetAuthType {
|
||||
NoPerClient = 0_u64,
|
||||
DHPerClient = 1_u64,
|
||||
PSKPerClient = 2_u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum SignatureType {
|
||||
DsaSha1,
|
||||
EcdsaSha256P256,
|
||||
EcdsaSha384P384,
|
||||
EcdsaSha512P21,
|
||||
EdDsaSha512Ed25519,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// Guaranteed is disabled; None implemented in 0.8.1; the streaming lib default is None as of 0.8.1, the client side default is None as of 0.9.4
|
||||
pub enum MessageReliability {
|
||||
BestEffort,
|
||||
None,
|
||||
}
|
||||
|
||||
impl SignatureType {
|
||||
fn string(&self) -> &str {
|
||||
match self {
|
||||
Self::DsaSha1 => "DSA_SHA1",
|
||||
Self::EcdsaSha256P256 => "ECDSA_SHA256_P256",
|
||||
Self::EcdsaSha384P384 => "ECDSA_SHA384_P384",
|
||||
Self::EcdsaSha512P21 => "ECDSA_SHA512_P521",
|
||||
Self::EdDsaSha512Ed25519 => "EdDSA_SHA512_Ed25519",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the default settings for a connection to the
|
||||
/// SAM bridge. Intentionally the only configured values are
|
||||
/// the signature type, and lease set encryption types. All other
|
||||
/// "default" options will be handled from within the SAM bridge itself,
|
||||
/// this is needed because during testing sending no values and letting defaults
|
||||
/// be determined entirely by the router resulted in being unable to connect
|
||||
/// to destination leasesets
|
||||
impl Default for SAMOptions {
|
||||
fn default() -> Self {
|
||||
SAMOptions {
|
||||
to_port: None,
|
||||
from_port: None,
|
||||
i2cp_options: Some(I2CPOptions {
|
||||
client_options: Some(I2CPClientOptions {
|
||||
lease_set_enc_type: Some(LeaseSetEncType::default()),
|
||||
..Default::default()
|
||||
}),
|
||||
router_options: Some(I2CPRouterOptions {
|
||||
lease_set_enc_type: Some(LeaseSetEncType::default()),
|
||||
..Default::default()
|
||||
}),
|
||||
}),
|
||||
signature_type: SignatureType::EdDsaSha512Ed25519,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SAMOptions {
|
||||
/// parses the SAMOptions object returning
|
||||
/// any client-side and router-side options as defined in the
|
||||
/// i2cp specs https://geti2p.net/en/docs/protocol/i2cp
|
||||
pub fn options(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CPOptions {
|
||||
pub fn string(&self) -> String {
|
||||
let mut options = String::default();
|
||||
if let Some(router_options) = &self.router_options {
|
||||
let router_options_str = router_options.string();
|
||||
if router_options_str != "" {
|
||||
options.push_str(router_options_str.as_str());
|
||||
}
|
||||
}
|
||||
if let Some(client_options) = &self.client_options {
|
||||
let client_options_str = client_options.string();
|
||||
if client_options_str != "" {
|
||||
options.push_str(client_options_str.as_str());
|
||||
}
|
||||
}
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CPRouterOptions {
|
||||
pub fn string(&self) -> String {
|
||||
let mut options = String::default();
|
||||
if let Some(client_message_timeout) = &self.client_message_timeout {
|
||||
options.push_str(&format!("clientMessageTimeout={} ", client_message_timeout));
|
||||
}
|
||||
if let Some(crypto_options) = &self.crypto_options {
|
||||
let crypto_options_str = crypto_options.string();
|
||||
if crypto_options_str != "" {
|
||||
options.push_str(&crypto_options_str.as_str());
|
||||
}
|
||||
}
|
||||
if let Some(dont_publish_lease_set) = &self.dont_publish_lease_set {
|
||||
options.push_str(&format!(
|
||||
"i2cp.dontPublishLeaseSet={} ",
|
||||
dont_publish_lease_set
|
||||
));
|
||||
}
|
||||
if let Some(fast_receive) = &self.fast_receive {
|
||||
options.push_str(&format!("i2cp.fastReceive={} ", fast_receive));
|
||||
}
|
||||
if let Some(lease_set_auth_type) = &self.lease_set_auth_type {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetAuthType={} ",
|
||||
lease_set_auth_type.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_enc_type) = &self.lease_set_enc_type {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetEncType={} ",
|
||||
lease_set_enc_type.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_offline_expiration) = &self.lease_set_offline_expiration {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetOfflineExpiration={} ",
|
||||
String::from_utf8(lease_set_offline_expiration[..].to_vec()).unwrap()
|
||||
))
|
||||
}
|
||||
if let Some(lease_set_priv_key) = &self.lease_set_priv_key {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetPrivKey={} ",
|
||||
lease_set_priv_key.to_string()
|
||||
))
|
||||
}
|
||||
if let Some(lease_set_secret) = &self.lease_set_secret {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetSecret={} ",
|
||||
lease_set_secret.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_transient_public_key) = &self.lease_set_transient_public_key {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetTransientPublicKey={} ",
|
||||
lease_set_transient_public_key.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_type) = &self.lease_set_type {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetType={} ",
|
||||
lease_set_type.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(message_reliability) = &self.message_reliability {
|
||||
options.push_str(&format!(
|
||||
"i2cp.messageReliability={} ",
|
||||
message_reliability.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(password) = &self.password {
|
||||
options.push_str(&format!("i2cp.password={} ", password));
|
||||
}
|
||||
if let Some(username) = &self.username {
|
||||
options.push_str(&format!("i2cp.username={} ", username));
|
||||
}
|
||||
if let Some(inbound) = &self.inbound {
|
||||
let inbound_str = inbound.string();
|
||||
if inbound_str != "" {
|
||||
options.push_str(inbound_str.as_str());
|
||||
}
|
||||
}
|
||||
if let Some(outbound) = &self.outbound {
|
||||
let outbound_str = outbound.string();
|
||||
if outbound_str != "" {
|
||||
options.push_str(outbound_str.as_str());
|
||||
}
|
||||
}
|
||||
if let Some(should_bundle_reply_info) = &self.should_bundle_reply_info {
|
||||
options.push_str(&format!(
|
||||
"shouldBundleReplyInfo={} ",
|
||||
should_bundle_reply_info
|
||||
));
|
||||
}
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CPClientOptions {
|
||||
pub fn string(&self) -> String {
|
||||
let mut options = String::default();
|
||||
if let Some(close_idle_time) = &self.close_idle_time {
|
||||
options.push_str(&format!("i2cp.closeIdleTime={} ", close_idle_time));
|
||||
}
|
||||
if let Some(close_on_idle) = &self.close_on_idle {
|
||||
options.push_str(&format!("i2cp.closeOnIdle={} ", close_on_idle));
|
||||
}
|
||||
if let Some(encrypt_lease_set) = &self.encrypt_lease_set {
|
||||
options.push_str(&format!("i2cp.encryptLeaseSet={} ", encrypt_lease_set));
|
||||
}
|
||||
if let Some(fast_receive) = &self.fast_receive {
|
||||
options.push_str(&format!("i2cp.fastReceive={} ", fast_receive));
|
||||
}
|
||||
if let Some(gzip) = &self.gzip {
|
||||
options.push_str(&format!("i2cp.gzip={} ", gzip));
|
||||
}
|
||||
if let Some(lease_set_auth_type) = &self.lease_set_auth_type {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetAuthType={} ",
|
||||
lease_set_auth_type.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_blinded_type) = &self.lease_set_blinded_type {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetBlindedType={} ",
|
||||
lease_set_blinded_type.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_enc_type) = &self.lease_set_enc_type {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetEncType={} ",
|
||||
lease_set_enc_type.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_key) = &self.lease_set_key {
|
||||
options.push_str(&format!("i2cp.leaseSetKey={} ", lease_set_key.to_string()));
|
||||
}
|
||||
if let Some(lease_set_private_key) = &self.lease_set_private_key {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetPrivateKey={} ",
|
||||
lease_set_private_key.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_secret) = &self.lease_set_secret {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetSecret={} ",
|
||||
lease_set_secret.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(lease_set_signing_private_key) = &self.lease_set_signing_private_key {
|
||||
options.push_str(&format!(
|
||||
"i2cp.leaseSetSigningPrivateKey={} ",
|
||||
lease_set_signing_private_key.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(message_reliability) = &self.message_reliability {
|
||||
options.push_str(&format!(
|
||||
"i2cp.messageReliability={} ",
|
||||
message_reliability.to_string()
|
||||
));
|
||||
}
|
||||
if let Some(reduce_idle_time) = &self.reduce_idle_time {
|
||||
options.push_str(&format!("i2cp.reduceIdleTime={} ", reduce_idle_time));
|
||||
}
|
||||
if let Some(reduce_on_idle) = &self.reduce_on_idle {
|
||||
options.push_str(&format!("i2cp.reduceOnIdle={} ", reduce_on_idle));
|
||||
}
|
||||
if let Some(ssl) = &self.ssl {
|
||||
options.push_str(&format!("i2cp.ssl={} ", ssl));
|
||||
}
|
||||
if let Some(tcp_host) = &self.tcp_host {
|
||||
options.push_str(&format!("i2cp.tcp.host={} ", tcp_host));
|
||||
}
|
||||
if let Some(tcp_port) = &self.tcp_port {
|
||||
options.push_str(&format!("i2cp.tcp.port={} ", tcp_port));
|
||||
}
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CPRouterCryptoOptions {
|
||||
pub fn string(&self) -> String {
|
||||
let mut options = String::default();
|
||||
if let Some(low_tag_threshold) = &self.low_tag_threshold {
|
||||
options.push_str(&format!("crypto.lowTagThreshold={} ", low_tag_threshold));
|
||||
}
|
||||
if let Some(inbound_tags) = &self.ratchet_inbound_tags {
|
||||
options.push_str(&format!("crypto.ratchet.inboundTags={} ", inbound_tags));
|
||||
}
|
||||
if let Some(outbound_tags) = &self.ratchet_outbound_tags {
|
||||
options.push_str(&format!("crypto.ratchet.outboundTags={} ", outbound_tags));
|
||||
}
|
||||
if let Some(tags_to_send) = &self.tags_to_send {
|
||||
options.push_str(&format!("crypto.tagsToSend={} ", tags_to_send));
|
||||
}
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CPTunnelInboundOptions {
|
||||
pub fn string(&self) -> String {
|
||||
let mut options = String::default();
|
||||
if let Some(allow_zero_hop) = &self.allow_zero_hop {
|
||||
options.push_str(&format!("inbound.allowZeroHop={} ", allow_zero_hop));
|
||||
}
|
||||
if let Some(backup_quantity) = &self.backup_quantity {
|
||||
options.push_str(&format!("inbound.backupQuantity={} ", backup_quantity));
|
||||
}
|
||||
if let Some(ip_restriction) = &self.ip_restriction {
|
||||
options.push_str(&format!("inbound.IPRestriction={} ", ip_restriction));
|
||||
}
|
||||
if let Some(length) = &self.length {
|
||||
options.push_str(&format!("inbound.length={} ", length));
|
||||
}
|
||||
if let Some(length_variance) = &self.length_variance {
|
||||
options.push_str(&format!("inbound.lengthVariance{} ", length_variance));
|
||||
}
|
||||
if let Some(quantity) = &self.quantity {
|
||||
options.push_str(&format!("inbound.quantity={} ", quantity));
|
||||
}
|
||||
if let Some(random_key) = &self.random_key {
|
||||
options.push_str(&format!("inbound.randomKey={} ", random_key));
|
||||
}
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CPTunnelOutboundOptions {
|
||||
pub fn string(&self) -> String {
|
||||
let mut options = String::default();
|
||||
if let Some(allow_zero_hop) = &self.allow_zero_hop {
|
||||
options.push_str(&format!("outbound.allowZeroHop={} ", allow_zero_hop));
|
||||
}
|
||||
if let Some(backup_quantity) = &self.backup_quantity {
|
||||
options.push_str(&format!("outbound.backupQuantity={} ", backup_quantity));
|
||||
}
|
||||
if let Some(ip_restriction) = &self.ip_restriction {
|
||||
options.push_str(&format!("outbound.IPRestriction={} ", ip_restriction));
|
||||
}
|
||||
if let Some(length) = &self.length {
|
||||
options.push_str(&format!("outbound.length={} ", length));
|
||||
}
|
||||
if let Some(length_variance) = &self.length_variance {
|
||||
options.push_str(&format!("outbound.lengthVariance{} ", length_variance));
|
||||
}
|
||||
if let Some(priority) = &self.priority {
|
||||
options.push_str(&format!("outbound.priority={} ", priority));
|
||||
}
|
||||
if let Some(quantity) = &self.quantity {
|
||||
options.push_str(&format!("outbound.quantity={} ", quantity));
|
||||
}
|
||||
if let Some(random_key) = &self.random_key {
|
||||
options.push_str(&format!("outbound.randomKey={} ", random_key));
|
||||
}
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetType {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetBlindedType {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetAuthType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::NoPerClient => String::from("0"),
|
||||
Self::DHPerClient => String::from("1"),
|
||||
Self::PSKPerClient => String::from("2"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetOfflineSignature {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetEncType {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetPrivKey {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetPrivateKey {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
impl ToString for LeaseSetKey {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LeaseSetSecret {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
impl ToString for LeaseSetTransientPublicKey {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
impl ToString for LeaseSetSigningPrivateKey {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LeaseSetEncType {
|
||||
fn default() -> LeaseSetEncType {
|
||||
LeaseSetEncType::from("4,0")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for LeaseSetEncType {
|
||||
fn from(val: String) -> LeaseSetEncType {
|
||||
LeaseSetEncType(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for LeaseSetEncType {
|
||||
fn from(val: &str) -> LeaseSetEncType {
|
||||
LeaseSetEncType(val.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LeaseSetAuthType {
|
||||
fn default() -> Self {
|
||||
Self::NoPerClient
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LeaseSetType {
|
||||
fn default() -> Self {
|
||||
LeaseSetType(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MessageReliability {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for MessageReliability {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::BestEffort => String::from("BestEffort"),
|
||||
Self::None => String::from("None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for SAMOptions {
|
||||
fn to_string(&self) -> String {
|
||||
let mut options = String::default();
|
||||
if let Some(from_port) = &self.from_port {
|
||||
options.push_str(&format!("FROM_PORT={} ", from_port));
|
||||
}
|
||||
if let Some(to_port) = &self.to_port {
|
||||
options.push_str(&format!("TO_PORT={} ", to_port));
|
||||
}
|
||||
if let Some(i2cp_options) = &self.i2cp_options {
|
||||
let i2cp_options_str = i2cp_options.string();
|
||||
if i2cp_options_str != "" {
|
||||
options.push_str(i2cp_options_str.as_str());
|
||||
}
|
||||
}
|
||||
// make sure to remove duplicate options
|
||||
// todo: there is likely a better way of handling this
|
||||
let mut options_parts: Vec<_> = options.split(" ").collect();
|
||||
options_parts.sort_unstable();
|
||||
options_parts.dedup();
|
||||
let mut options_parsed = String::default();
|
||||
for option_part in options_parts.clone() {
|
||||
options_parsed.push_str(&format!("{} ", option_part));
|
||||
}
|
||||
options_parsed
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for SignatureType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::DsaSha1 => "DSA_SHA1".to_string(),
|
||||
Self::EcdsaSha256P256 => "ECDSA_SHA256_P256".to_string(),
|
||||
Self::EcdsaSha384P384 => "ECDSA_SHA384_P384".to_string(),
|
||||
Self::EcdsaSha512P21 => "ECDSA_SHA512_P521".to_string(),
|
||||
Self::EdDsaSha512Ed25519 => "EdDSA_SHA512_Ed25519".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
98
src/session_watcher.rs
Normal file
98
src/session_watcher.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
//! provides a basic session watcher which wraps [I2pListener::accept] ensuring that
|
||||
//! any errors which result in the session being terminated, such as clients improperly disconnecting
|
||||
//! or other network/transport level issues are handled gracefully.
|
||||
//!
|
||||
//! any calls to accept which result in an error will cause the existing session and i2plistener to be dropped,
|
||||
//! before they are recreated and an error is returned information the caller to try the operation again
|
||||
//!
|
||||
|
||||
|
||||
use std::net::Shutdown;
|
||||
|
||||
|
||||
use crate::{sam::{StreamConnect, SessionStyle, nickname}, net::{I2pSocketAddr, I2pListener}, Session, sam_options::SAMOptions, Error, ErrorKind};
|
||||
use log::{info, warn, error};
|
||||
|
||||
/// SamSessionWatcher provides the ability to gracefully handle
|
||||
/// runtime errors by restarting the sam session, and recreating the listener
|
||||
/// any time errors are detected.
|
||||
///
|
||||
/// note: should implement better detection of which errors cause us
|
||||
/// to recreate the connection
|
||||
pub struct SamSessionWatcher {
|
||||
opts: SAMOptions,
|
||||
session: Session,
|
||||
destination: String,
|
||||
sam_endpoint: String,
|
||||
session_style: SessionStyle,
|
||||
pub listener: I2pListener,
|
||||
}
|
||||
|
||||
impl SamSessionWatcher {
|
||||
pub fn new(
|
||||
sam_endpoint: &str,
|
||||
destination: &str,
|
||||
session_style: SessionStyle,
|
||||
opts: SAMOptions,
|
||||
) -> Result<Box<SamSessionWatcher>, Error> {
|
||||
let (session, listener) = SamSessionWatcher::__recreate(
|
||||
sam_endpoint,
|
||||
destination,
|
||||
&nickname(),
|
||||
session_style.clone(),
|
||||
opts.clone()
|
||||
)?;
|
||||
Ok(Box::new(SamSessionWatcher {
|
||||
opts,
|
||||
session,
|
||||
listener,
|
||||
session_style,
|
||||
destination: destination.to_string(),
|
||||
sam_endpoint: sam_endpoint.to_string(),
|
||||
}))
|
||||
}
|
||||
pub fn accept(self: &mut Box<Self>) -> Result<(StreamConnect, I2pSocketAddr), Error> {
|
||||
match self.listener.forward.accept() {
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => {
|
||||
error!("accept encountered error, recreating stream: {:#?}", err);
|
||||
{
|
||||
drop(&mut self.listener);
|
||||
self.session.sam.conn.shutdown(Shutdown::Both)?;
|
||||
drop(&mut self.session);
|
||||
}
|
||||
self.recreate()?;
|
||||
Err(ErrorKind::SessionRecreated.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn recreate(self: &mut Box<Self>) -> Result<(), Error> {
|
||||
let (session, listener) = SamSessionWatcher::__recreate(
|
||||
&self.sam_endpoint,
|
||||
&self.destination,
|
||||
&nickname(),
|
||||
self.session_style.clone(),
|
||||
self.opts.clone()
|
||||
)?;
|
||||
self.session = session;
|
||||
self.listener = listener;
|
||||
Ok(())
|
||||
}
|
||||
fn __recreate(
|
||||
sam_endpoint: &str,
|
||||
destination: &str,
|
||||
nickname: &str,
|
||||
session_style: SessionStyle,
|
||||
opts: SAMOptions,
|
||||
) -> Result<(Session, I2pListener), Error> {
|
||||
let session = Session::create(
|
||||
sam_endpoint,
|
||||
destination,
|
||||
nickname,
|
||||
session_style,
|
||||
opts.clone(),
|
||||
)?;
|
||||
let listener = I2pListener::bind_with_session(&session)?;
|
||||
Ok((session, listener))
|
||||
}
|
||||
}
|
@@ -1,18 +1,18 @@
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn naming_lookup() {
|
||||
use i2p::sam::SamConnection;
|
||||
let mut sam = SamConnection::connect("127.0.0.1:7656").unwrap();
|
||||
{
|
||||
let res = sam.naming_lookup("zzz.i2p");
|
||||
match res {
|
||||
Ok(d) => {
|
||||
assert_eq!(
|
||||
use i2p::sam::SamConnection;
|
||||
let mut sam = SamConnection::connect("127.0.0.1:7656").unwrap();
|
||||
{
|
||||
let res = sam.naming_lookup("zzz.i2p");
|
||||
match res {
|
||||
Ok(d) => {
|
||||
assert_eq!(
|
||||
d,
|
||||
"GKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3XWD7Pw6P8qVYF39jUIq4OiNMwPnNYzy2N4mDMQdsdHO3LUVh~DEppOy9AAmEoHDjjJxt2BFBbGxfdpZCpENkwvmZeYUyNCCzASqTOOlNzdpne8cuesn3NDXIpNnqEE6Oe5Qm5YOJykrX~Vx~cFFT3QzDGkIjjxlFBsjUJyYkFjBQAEAAcAAA=="
|
||||
)
|
||||
}
|
||||
Err(ref e) => panic!("An error occurred: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ref e) => panic!("An error occurred: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user