Add Rust bindings (alpha)
This commit is contained in:
@@ -124,6 +124,16 @@ if (ZTS_ENABLE_PINVOKE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZTS_ENABLE_PINVOKE=1")
|
||||
endif()
|
||||
|
||||
# Rust language bindings
|
||||
if (ZTS_ENABLE_RUST)
|
||||
# Features
|
||||
set(BUILD_STATIC_LIB TRUE)
|
||||
set(BUILD_SHARED_LIB FALSE)
|
||||
set(BUILD_EXAMPLES FALSE)
|
||||
set(ALLOW_INSTALL_TARGET FALSE)
|
||||
set(BUILD_HOST_SELFTEST FALSE)
|
||||
endif()
|
||||
|
||||
# Python language bindings (_libzt.so)
|
||||
if (ZTS_ENABLE_PYTHON)
|
||||
# Features
|
||||
|
||||
21
build.sh
21
build.sh
@@ -368,6 +368,22 @@ host-uninstall()
|
||||
cd -
|
||||
}
|
||||
|
||||
# Build rust crate, libzt.a, link and test
|
||||
#
|
||||
host-rust()
|
||||
{
|
||||
BUILD_TYPE=${1:-debug}
|
||||
|
||||
cd pkg/crate/libzt
|
||||
cargo build # --verbose
|
||||
|
||||
# Test Rust crate
|
||||
if [[ $2 = *"test"* ]]; then
|
||||
cargo run --example libzt-test-app -- server $alice_path $testnet 0.0.0.0 $port4 &
|
||||
cargo run --example libzt-test-app -- client $bob_path $testnet $alice_ip4 $port4 &
|
||||
fi
|
||||
}
|
||||
|
||||
# Build C extension module (*.so), python module, package both into wheel
|
||||
#
|
||||
# ./build.sh host-python-wheel "release"
|
||||
@@ -467,7 +483,7 @@ host-pinvoke()
|
||||
cp $LIB_OUTPUT_DIR/* $BIN_OUTPUT_DIR
|
||||
# Start Alice as server
|
||||
MONO_THREADS_SUSPEND=preemptive; mono --debug "$BIN_OUTPUT_DIR/selftest.exe" server $alice_path $testnet $port4 &
|
||||
sleep 5
|
||||
sleep 3
|
||||
# Start Bob as client
|
||||
MONO_THREADS_SUSPEND=preemptive; mono --debug "$BIN_OUTPUT_DIR/selftest.exe" client $bob_path $testnet $alice_ip4 $port4 &
|
||||
fi
|
||||
@@ -663,6 +679,7 @@ format-code()
|
||||
then
|
||||
$PYTHON -m black src/bindings/python/libzt.py
|
||||
$PYTHON -m black src/bindings/python/node.py
|
||||
$PYTHON -m black src/bindings/python/select.py
|
||||
$PYTHON -m black src/bindings/python/sockets.py
|
||||
$PYTHON -m black examples/python/
|
||||
$PYTHON -m black test/selftest.py
|
||||
@@ -692,6 +709,8 @@ test-c()
|
||||
# Recursive deep clean
|
||||
clean()
|
||||
{
|
||||
# Clean rust crate
|
||||
(cd pkg/crate/libzt && cargo clean)
|
||||
# Finished artifacts
|
||||
rm -rf $BUILD_OUTPUT_DIR
|
||||
# CMake's build system cache
|
||||
|
||||
11
examples/rust/README.md
Normal file
11
examples/rust/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Rust example
|
||||
|
||||
```
|
||||
cargo install libzt
|
||||
```
|
||||
|
||||
Examples: [pkg/crate/libzt/src/examples](pkg/crate/libzt/src/examples)
|
||||
|
||||
## Links
|
||||
|
||||
- Getting Started: [docs.zerotier.com/sockets](https://docs.zerotier.com/sockets/tutorial.html)
|
||||
28
pkg/crate/libzt/Cargo.lock
generated
28
pkg/crate/libzt/Cargo.lock
generated
@@ -58,6 +58,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
@@ -99,6 +105,15 @@ dependencies = [
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.3"
|
||||
@@ -147,9 +162,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.94"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
||||
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -166,6 +181,7 @@ name = "libzt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cmake",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -202,9 +218,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -220,9 +236,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.3"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce5f1ceb7f74abbce32601642fcf8e8508a8a8991e0621c7d750295b9095702b"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
||||
@@ -16,12 +16,13 @@ categories = ["network-programming", "cryptography"]
|
||||
[build-dependencies]
|
||||
bindgen = "0.57"
|
||||
libc = "0.2"
|
||||
cmake = "0.1"
|
||||
|
||||
[lib]
|
||||
name = "libzt"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
[[example]]
|
||||
name = "libzt-test-app"
|
||||
path = "src/bin/libzt-test-app.rs"
|
||||
path = "src/examples/libzt-test-app.rs"
|
||||
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
[libzt](https://www.zerotier.com)
|
||||
=====
|
||||
Part of the ZeroTier SDK
|
||||
# libzt - Sockets over ZeroTier
|
||||
|
||||
`libzt` replicates the functionality of [std::net](https://doc.rust-lang.org/std/net/index.html) but uses [ZeroTier](https://www.zerotier.com) as its transport layer.
|
||||
|
||||
Securely connect application instances, physical devices, and virtual devices as if everything is on a single LAN. ZeroTier brings your network into user-space. No root, and no host configuration requirements.
|
||||
|
||||
We've paired our network hyper-visor core with a TCP/UDP/IP stack [(lwIP)](https://en.wikipedia.org/wiki/LwIP) to provide your application with an exclusive and private virtual network layer. All traffic is end-to-end encrypted between each peer and we provide an easy-to-use socket interface similar to [std::net](https://doc.rust-lang.org/std/net/index.html).
|
||||
## Usage
|
||||
|
||||
Add the following to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
libzt = "0.1.0"
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Docs: [docs.zerotier.com](https://docs.zerotier.com/sockets/tutorial.html)
|
||||
- Repo: [github.com/zerotier/libzt](https://github.com/zerotier/libzt)
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
extern crate bindgen;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=zt");
|
||||
println!("cargo:rustc-env=LLVM_CONFIG_PATH=/usr/local/opt/llvm/bin/llvm-config");
|
||||
use cmake::Config;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
//println!("cargo:rerun-if-changed=../../../include/ZeroTierSockets.h");
|
||||
//println!("cargo:include=/usr/local/include");
|
||||
fn main() {
|
||||
Config::new("src/native").build_target("zt-static").define("ZTS_ENABLE_RUST", "1").out_dir("target").build();
|
||||
|
||||
println!("cargo:rustc-link-search=target/build/lib");
|
||||
println!("cargo:rustc-link-lib=static=zt");
|
||||
|
||||
// See here for reasoning: https://flames-of-code.netlify.app/blog/rust-and-cmake-cplusplus/
|
||||
|
||||
let target = env::var("TARGET").unwrap();
|
||||
if target.contains("apple") {
|
||||
println!("cargo:rustc-link-lib=dylib=c++");
|
||||
} else if target.contains("linux") {
|
||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
} else {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
//println!("cargo:rustc-env=LLVM_CONFIG_PATH=/usr/local/opt/llvm/bin/llvm-config");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("../../../include/ZeroTierSockets.h")
|
||||
.header("src/native/include/ZeroTierSockets.h")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file("./src/libzt.rs")
|
||||
.write_to_file(out_path.join("libzt.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
|
||||
146
pkg/crate/libzt/src/examples/libzt-test-app.rs
Normal file
146
pkg/crate/libzt/src/examples/libzt-test-app.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use libzt;
|
||||
|
||||
use std::env;
|
||||
|
||||
use libzt::tcp::{TcpListener, TcpStream};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::Shutdown;
|
||||
use std::str::from_utf8;
|
||||
use std::thread;
|
||||
|
||||
// (Optional) Notify application of ZeroTier events, some with context
|
||||
fn user_event_handler(event_code: i16) -> () {
|
||||
println!("user_event {}", event_code);
|
||||
}
|
||||
|
||||
fn handle_client(mut stream: TcpStream) {
|
||||
let mut data = [0 as u8; 50]; // using 50 byte buffer
|
||||
while match stream.read(&mut data) {
|
||||
Ok(size) => {
|
||||
// echo everything!
|
||||
stream.write(&data[0..size]).unwrap();
|
||||
true
|
||||
}
|
||||
Err(_) => {
|
||||
println!(
|
||||
"An error occurred, terminating connection with {}",
|
||||
stream.peer_addr().unwrap()
|
||||
);
|
||||
stream.shutdown(Shutdown::Both).unwrap();
|
||||
false
|
||||
}
|
||||
} {}
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
println!("{:?}", args);
|
||||
|
||||
if args.len() != 5 && args.len() != 6 {
|
||||
println!("Incorrect number of arguments.");
|
||||
println!(" Usage: server <storage_path> <net_id> <local_ip> <local_port>");
|
||||
println!(" Usage: client <storage_path> <net_id> <remote_ip> <remote_port>");
|
||||
}
|
||||
|
||||
let storage_path = &args[2];
|
||||
let net_id = u64::from_str_radix(&args[3], 16).unwrap();
|
||||
|
||||
println!("path = {}", storage_path);
|
||||
println!("net_id = {:x}", net_id);
|
||||
|
||||
// SET UP ZEROTIER
|
||||
|
||||
let nn = libzt::node::ZeroTierNode {};
|
||||
// (Optional) initialization
|
||||
nn.init_set_port(0);
|
||||
nn.init_set_event_handler(user_event_handler);
|
||||
nn.init_from_storage(&storage_path);
|
||||
// Start the node
|
||||
nn.start();
|
||||
println!("Waiting for node to come online...");
|
||||
while !nn.is_online() {
|
||||
nn.delay(50);
|
||||
}
|
||||
println!("Node ID = {:#06x}", nn.id());
|
||||
println!("Joining network");
|
||||
nn.net_join(net_id);
|
||||
println!("Waiting for network to assign addresses...");
|
||||
while !nn.net_transport_is_ready(net_id) {
|
||||
nn.delay(50);
|
||||
}
|
||||
let addr = nn.addr_get(net_id).unwrap();
|
||||
println!("Assigned addr = {}", addr);
|
||||
|
||||
// Server
|
||||
|
||||
if &args[1] == "server" {
|
||||
println!("server mode");
|
||||
let mut addr_str: String = "".to_owned();
|
||||
addr_str.push_str(&args[4]);
|
||||
addr_str.push_str(":");
|
||||
addr_str.push_str(&args[5]);
|
||||
|
||||
let server: std::net::SocketAddr =
|
||||
addr_str.parse().expect("Unable to parse socket address");
|
||||
let listener = TcpListener::bind(&server).unwrap();
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => {
|
||||
println!("New connection: {}", stream.peer_addr().unwrap());
|
||||
thread::spawn(move || {
|
||||
// connection succeeded
|
||||
handle_client(stream)
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error: {}", e);
|
||||
// connection failed
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(listener);
|
||||
}
|
||||
|
||||
// Client
|
||||
|
||||
if &args[1] == "client" {
|
||||
println!("client mode");
|
||||
let mut addr_str: String = "".to_owned();
|
||||
addr_str.push_str(&args[4]);
|
||||
addr_str.push_str(":");
|
||||
addr_str.push_str(&args[5]);
|
||||
match TcpStream::connect(addr_str) {
|
||||
Ok(mut stream) => {
|
||||
println!("Successfully connected to server");
|
||||
|
||||
let msg = b"Hello!";
|
||||
|
||||
stream.write(msg).unwrap();
|
||||
println!("Sent Hello, awaiting reply...");
|
||||
|
||||
let mut data = [0 as u8; 6];
|
||||
match stream.read_exact(&mut data) {
|
||||
Ok(_) => {
|
||||
if &data == msg {
|
||||
println!("Reply is ok!");
|
||||
} else {
|
||||
let text = from_utf8(&data).unwrap();
|
||||
println!("Unexpected reply: {}", text);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to receive data: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to connect: {}", e);
|
||||
}
|
||||
}
|
||||
println!("Terminated.");
|
||||
}
|
||||
|
||||
nn.stop();
|
||||
Ok(())
|
||||
}
|
||||
@@ -15,7 +15,8 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
pub mod libzt;
|
||||
include!(concat!(env!("OUT_DIR"), "/libzt.rs"));
|
||||
|
||||
pub mod node;
|
||||
pub mod socket;
|
||||
pub mod tcp;
|
||||
|
||||
@@ -14,16 +14,17 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/libzt.rs"));
|
||||
|
||||
use crate::libzt::*;
|
||||
use std::ffi::{c_void, CStr, CString};
|
||||
use std::io;
|
||||
use std::net::{AddrParseError, IpAddr};
|
||||
use std::str::FromStr;
|
||||
|
||||
extern "C" fn native_event_handler(msg: *mut c_void) {
|
||||
let event: &mut zts_event_msg_t = unsafe { &mut *(msg as *mut zts_event_msg_t) };
|
||||
println!("event: {}", event.event_code);
|
||||
//println!("event: {}", event.event_code);
|
||||
//user_event_handler(event.event_code);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ use std::{io, mem};
|
||||
|
||||
type time_t = i64;
|
||||
|
||||
use crate::libzt::*;
|
||||
use crate::utils::*;
|
||||
|
||||
// Note: FileDesc and c_int in libc are private so we can't use that. Use i32 instead
|
||||
|
||||
@@ -19,7 +19,6 @@ use std::os::raw::c_int;
|
||||
use std::time::Duration;
|
||||
use std::{cmp, mem};
|
||||
|
||||
use crate::libzt::*;
|
||||
use crate::socket::Socket;
|
||||
use crate::utils::*;
|
||||
|
||||
|
||||
@@ -13,13 +13,12 @@
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::{c_void, CString};
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
use std::io::{self /*, Error, ErrorKind*/};
|
||||
use std::net::{/*Ipv4Addr, Ipv6Addr,*/ SocketAddr, ToSocketAddrs};
|
||||
use std::os::raw::c_int;
|
||||
use std::time::Duration;
|
||||
//use std::cmp;
|
||||
|
||||
use crate::libzt::*;
|
||||
use crate::socket::Socket;
|
||||
use crate::utils::*;
|
||||
|
||||
@@ -82,24 +81,23 @@ impl UdpSocketImpl {
|
||||
self.inner.peek_from(buf)
|
||||
}
|
||||
|
||||
pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result<usize> {
|
||||
/*
|
||||
let len = cmp::min(buf.len(), <size_t>::MAX as usize) as size_t;
|
||||
let (dstp, dstlen) = dst.into_inner();
|
||||
let ret = cvt(unsafe {
|
||||
zts_bsd_sendto(
|
||||
*self.inner.as_inner(),
|
||||
buf.as_ptr() as *const c_void,
|
||||
len,
|
||||
ZTS_MSG_NOSIGNAL,
|
||||
dstp,
|
||||
dstlen,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
*/
|
||||
Ok(0)
|
||||
}
|
||||
/*
|
||||
pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result<usize> {
|
||||
let len = cmp::min(buf.len(), <size_t>::MAX as usize) as size_t;
|
||||
let (dstp, dstlen) = dst.into_inner();
|
||||
let ret = cvt(unsafe {
|
||||
zts_bsd_sendto(
|
||||
*self.inner.as_inner(),
|
||||
buf.as_ptr() as *const c_void,
|
||||
len,
|
||||
ZTS_MSG_NOSIGNAL,
|
||||
dstp,
|
||||
dstlen,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
self.inner.set_timeout(dur, ZTS_SO_RCVTIMEO as i32)
|
||||
@@ -317,17 +315,17 @@ impl UdpSocket {
|
||||
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.0.peek_from(buf)
|
||||
}
|
||||
|
||||
pub fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], addr: A) -> io::Result<usize> {
|
||||
match addr.to_socket_addrs()?.next() {
|
||||
Some(addr) => self.0.send_to(buf, &addr),
|
||||
None => Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"No address to send data to",
|
||||
)),
|
||||
/*
|
||||
pub fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], addr: A) -> io::Result<usize> {
|
||||
match addr.to_socket_addrs()?.next() {
|
||||
Some(addr) => self.0.send_to(buf, &addr),
|
||||
None => Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"No address to send data to",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::libzt::*;
|
||||
include!(concat!(env!("OUT_DIR"), "/libzt.rs"));
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||
|
||||
Reference in New Issue
Block a user