diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 7b21d7fde6bb06086e7d32632c68a08f402ca25f..666c8c14248c80963941a85a3e1b28c106f0faca 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -7,4 +7,5 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-libc="0.2.0"
\ No newline at end of file
+libc="0.2.0"
+os_socketaddr="0.1.0"
\ No newline at end of file
diff --git a/rust/examples/dhtnode.rs b/rust/examples/dhtnode.rs
index c2eee7e159e0e2d20c5ac254c6852e4b7fab63e0..39f222e976a042d83f4eabaeb3ade065e0350831 100644
--- a/rust/examples/dhtnode.rs
+++ b/rust/examples/dhtnode.rs
@@ -49,12 +49,14 @@ fn main() {
     };
     dht.put(&InfoHash::get("bob"), Value::new("hi!"), &mut put_done_cb);
 
+
     println!("Start listening /foo");
     let mut value_cb = |v, expired| {
         println!("LISTEN: DONE CB - data: {} - v: {} - expired: {}", data, v, expired);
     };
     let token = dht.listen(&InfoHash::get("foo"), &mut value_cb);
-    let one_min = time::Duration::from_secs(60);
+    let one_min = time::Duration::from_secs(10);
     thread::sleep(one_min);
     dht.cancel_listen(&InfoHash::get("foo"), token);
+    println!("Public ips: {:#?}", dht.public_addresses());
 }
\ No newline at end of file
diff --git a/rust/src/dhtrunner.rs b/rust/src/dhtrunner.rs
index 051ca80cd00b9a04e3a6b4ad9bb46c181023ebeb..697e20a4d3f22220e4b331d190aed1d60148b9b8 100644
--- a/rust/src/dhtrunner.rs
+++ b/rust/src/dhtrunner.rs
@@ -23,6 +23,8 @@ use std::ffi::CString;
 use std::ptr;
 
 pub use crate::ffi::*;
+use std::net::SocketAddr;
+use os_socketaddr::OsSocketAddr;
 
 impl DhtRunnerConfig {
 
@@ -154,7 +156,7 @@ extern fn listen_handler(v: *mut Value, expired: bool, ptr: *mut c_void) {
 
 extern fn listen_handler_done(ptr: *mut c_void) {
     unsafe {
-        let handler = Box::from_raw(ptr as *mut ListenHandler);
+        Box::from_raw(ptr as *mut ListenHandler);
     }
 }
 
@@ -185,6 +187,18 @@ impl DhtRunner {
         }
     }
 
+    pub fn node_id(&self) -> InfoHash {
+        unsafe {
+            dht_runner_get_node_id(&*self)
+        }
+    }
+
+    pub fn id(&self) -> InfoHash {
+        unsafe {
+            dht_runner_get_id(&*self)
+        }
+    }
+
     pub fn get<'a>(&mut self, h: &InfoHash,
                 get_cb: &'a mut(dyn FnMut(Box<Value>)),
                 done_cb: &'a mut(dyn FnMut(bool))) {
@@ -209,6 +223,23 @@ impl DhtRunner {
         }
     }
 
+    pub fn permanent_put<'a>(&mut self, h: &InfoHash, v: Box<Value>,
+                done_cb: &'a mut(dyn FnMut(bool))) {
+        let handler = Box::new(PutHandler {
+            done_cb,
+        });
+        let handler = Box::into_raw(handler) as *mut c_void;
+        unsafe {
+            dht_runner_put_permanent(&mut *self, h, &*v, put_handler_done, handler)
+        }
+    }
+
+    pub fn cancel_put<'a>(&mut self, h: &InfoHash, vid: u64) {
+        unsafe {
+            dht_runner_cancel_put(&mut *self, h, vid)
+        }
+    }
+
     pub fn listen<'a>(&mut self, h: &InfoHash,
                 cb: &'a mut(dyn FnMut(Box<Value>, bool))) -> Box<OpToken> {
         let handler = Box::new(ListenHandler {
@@ -234,6 +265,21 @@ impl DhtRunner {
             dht_runner_shutdown(&mut *self, done_cb, cb_user_data)
         }
     }
+
+    pub fn public_addresses(&self) -> Vec<SocketAddr> {
+        let mut result = Vec::new();
+        unsafe {
+            let mut addresses = dht_runner_get_public_address(&*self);
+            while !addresses.is_null() && !(*addresses).is_null() {
+                let sock = (*(*addresses)).into_addr();
+                if sock.is_some() {
+                    result.push(sock.unwrap());
+                }
+                addresses = (addresses as usize + std::mem::size_of::<*mut OsSocketAddr>()) as *mut *mut OsSocketAddr;
+            }
+        }
+        result
+    }
 }
 
 impl Drop for DhtRunner {
diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs
index 1162ff31d2927b011b813f19293c61ba461246c8..7708f2c5e956733dc135f466fcc9683311964bc5 100644
--- a/rust/src/ffi.rs
+++ b/rust/src/ffi.rs
@@ -19,6 +19,7 @@
 #![allow(dead_code)]
 
 use libc::{c_char, c_int, c_uint, c_void, in_port_t, size_t};
+use os_socketaddr::OsSocketAddr;
 
 const HASH_LEN: usize = 20;
 const PKID_LEN: usize = 32;
@@ -126,6 +127,7 @@ pub struct DhtRunnerConfig
 #[link(name = "opendht-c")]
 extern {
     // dht::Value
+    pub fn dht_value_get_id(data: *const Value) -> u64;
     pub fn dht_value_get_data(data: *const Value) -> DataView;
     pub fn dht_value_new(data: *const u8, size: size_t) -> *mut Value;
     pub fn dht_value_ref(data: *const Value) -> *mut Value;
@@ -166,6 +168,8 @@ extern {
     // dht::DhtRunner
     pub fn dht_runner_config_default(config: *mut DhtRunnerConfig);
     pub fn dht_runner_new() -> *mut DhtRunner;
+    pub fn dht_runner_get_id(dht: *const DhtRunner) -> InfoHash;
+    pub fn dht_runner_get_node_id(dht: *const DhtRunner) -> InfoHash;
     pub fn dht_runner_delete(dht: *mut DhtRunner);
     pub fn dht_runner_run(dht: *mut DhtRunner, port: in_port_t);
     pub fn dht_runner_run_config(dht: *mut DhtRunner, port: in_port_t, config: *const DhtRunnerConfig);
@@ -177,6 +181,10 @@ extern {
     pub fn dht_runner_put(dht: *mut DhtRunner, h: *const InfoHash, v: *const Value,
                       done_cb: extern fn(bool, *mut c_void),
                       cb_user_data: *mut c_void);
+    pub fn dht_runner_put_permanent(dht: *mut DhtRunner, h: *const InfoHash, v: *const Value,
+                      done_cb: extern fn(bool, *mut c_void),
+                      cb_user_data: *mut c_void);
+    pub fn dht_runner_cancel_put(dht: *mut DhtRunner, h: *const InfoHash, vid: u64);
     pub fn dht_runner_listen(dht: *mut DhtRunner, h: *const InfoHash,
                       cb: extern fn(*mut Value, bool, *mut c_void),
                       done_cb: extern fn(*mut c_void),
@@ -185,4 +193,5 @@ extern {
                       token: *const OpToken);
     pub fn dht_runner_shutdown(dht: *mut DhtRunner, done_cb: extern fn(bool, *mut c_void),
                       cb_user_data: *mut c_void);
+    pub fn dht_runner_get_public_address(dht: *const DhtRunner) -> *mut *mut OsSocketAddr;
 }
\ No newline at end of file
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 4742e986ae6e0f44cc0b11bf53e38c1ac05cf5ba..53005389e841dd84b9079a5957cf3d9b6dcc9303 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -17,6 +17,7 @@
  */
 
 extern crate libc;
+extern crate os_socketaddr;
 
 mod blob;
 pub mod crypto;
diff --git a/rust/src/value.rs b/rust/src/value.rs
index 6b3c0221080938cb952bfc8b0479b2f404d49321..4ce880ab3f62c71430b83318ccada1fdaf036f8e 100644
--- a/rust/src/value.rs
+++ b/rust/src/value.rs
@@ -31,6 +31,12 @@ impl Value {
         }
     }
 
+    pub fn id(&self) -> u64 {
+        unsafe {
+            dht_value_get_id(self)
+        }
+    }
+
     fn dataview(&self) -> DataView {
         unsafe {
             dht_value_get_data(self)