mirror of
https://github.com/cjdelisle/cjdns
synced 2025-10-06 00:32:50 +02:00
Avoid FFI structures and functions going from Rust to C and back to Rust again by adopting a more fine-grained selection of CFFI functions. Also beginnings of Seeder structures
This commit is contained in:
@@ -232,6 +232,10 @@ void Random_bytes(struct Random* rand, uint8_t* location, uint64_t count)
|
||||
stir(rand);
|
||||
}
|
||||
}
|
||||
void Random_bytes_fromRust(Random_t* rand, uint8_t* location, uint64_t count)
|
||||
{
|
||||
Random_bytes(rand, location, count);
|
||||
}
|
||||
|
||||
void Random_base32(struct Random* rand, uint8_t* output, uint32_t length)
|
||||
{
|
||||
|
@@ -27,6 +27,7 @@ Linker_require("crypto/random/Random.c")
|
||||
typedef struct Random Random_t;
|
||||
|
||||
void Random_bytes(Random_t* rand, uint8_t* location, uint64_t count);
|
||||
void Random_bytes_fromRust(Random_t* rand, uint8_t* location, uint64_t count);
|
||||
|
||||
/**
|
||||
* Get random Base32 text, great for password material.
|
||||
|
@@ -28,7 +28,7 @@ void Iface_checkIdentity(struct Iface* iface)
|
||||
}
|
||||
|
||||
// This needs to be in a C file in order to be accessible from Rust
|
||||
Iface_DEFUN Iface_incomingFromRust(Message_t* message, struct Iface* thisInterface)
|
||||
Iface_DEFUN Iface_incoming_fromRust(Message_t* message, struct Iface* thisInterface)
|
||||
{
|
||||
if (!thisInterface->connectedIf) {
|
||||
return Error(message, "No connected interface");
|
||||
|
@@ -51,7 +51,7 @@ struct Iface
|
||||
} /* Iface_t defined in RffiPrefix.h */;
|
||||
|
||||
// This needs to be in a C file in order to be accessible from Rust
|
||||
Iface_DEFUN Iface_incomingFromRust(Message_t* message, struct Iface* thisInterface);
|
||||
Iface_DEFUN Iface_incoming_fromRust(Message_t* message, struct Iface* thisInterface);
|
||||
|
||||
void Iface_setIdentity(struct Iface* iface);
|
||||
void Iface_checkIdentity(struct Iface* iface);
|
||||
|
@@ -35,17 +35,17 @@ static int incomingCount = 0;
|
||||
static int outgoingCount = 0;
|
||||
static int dropped = 0;
|
||||
|
||||
void RustIface_gotIncoming()
|
||||
void RustIface_gotIncoming_fromRust()
|
||||
{
|
||||
incomingCount++;
|
||||
}
|
||||
|
||||
void RustIface_gotOutgoing()
|
||||
void RustIface_gotOutgoing_fromRust()
|
||||
{
|
||||
outgoingCount++;
|
||||
}
|
||||
|
||||
void RustIface_dropped()
|
||||
void RustIface_dropped_fromRust()
|
||||
{
|
||||
dropped++;
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
#ifndef RUSTIFACE_H
|
||||
#define RUSTIFACE_H
|
||||
void RustIface_gotIncoming(void);
|
||||
void RustIface_gotOutgoing(void);
|
||||
void RustIface_dropped(void);
|
||||
void RustIface_gotIncoming_fromRust(void);
|
||||
void RustIface_gotOutgoing_fromRust(void);
|
||||
void RustIface_dropped_fromRust(void);
|
||||
#endif
|
@@ -119,13 +119,15 @@ static bool PFChan_Pathfinder_sizeOk(enum PFChan_Pathfinder ev, int size)
|
||||
return (size == 8);
|
||||
case PFChan_Pathfinder_CTRL_SENDMSG:
|
||||
return (size >= 8 + PFChan_CtrlMsg_MIN_SIZE);
|
||||
case PFChan_Pathfinder_CONNECT_PEER:
|
||||
return (size == 8 + sizeof(PFChan_Pathfinder_ConnectPeer_t));
|
||||
default:;
|
||||
}
|
||||
Assert_failure("invalid event [%d]", ev);
|
||||
}
|
||||
// Forget to add the event here? :)
|
||||
Assert_compileTime(PFChan_Pathfinder__TOO_LOW == 511);
|
||||
Assert_compileTime(PFChan_Pathfinder__TOO_HIGH == 523);
|
||||
Assert_compileTime(PFChan_Pathfinder__TOO_HIGH == 524);
|
||||
|
||||
static bool PFChan_Core_sizeOk(enum PFChan_Core ev, int size)
|
||||
{
|
||||
|
@@ -114,7 +114,7 @@ fn main() -> Result<()> {
|
||||
.collect::<Vec<PathBuf>>();
|
||||
paths.sort();
|
||||
for path in paths {
|
||||
if !path.is_dir() && path.extension().unwrap() == "o" {
|
||||
if !path.is_dir() && path.extension().is_some() && path.extension().unwrap() == "o" {
|
||||
build.object(path);
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,7 @@ fn main() -> Result<()> {
|
||||
// Generate rust bindings from C
|
||||
#[cfg(feature = "generate-cffi")]
|
||||
{
|
||||
println!("cargo:warn=Generating-cffi");
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindgen::Builder::default()
|
||||
.header(out_path.join("rust_cjdns_sys_cffi_h.i").to_str().unwrap())
|
||||
@@ -135,8 +136,13 @@ fn main() -> Result<()> {
|
||||
.raw_line("#![allow(dead_code)]")
|
||||
.raw_line("#![allow(non_camel_case_types)]")
|
||||
.raw_line("#![allow(clippy::enum_variant_names)]")
|
||||
.whitelist_function(".*")
|
||||
.raw_line("use crate::rtypes::*;")
|
||||
// .whitelist_function(".*")
|
||||
// .blacklist_function("Rffi_.*")
|
||||
.whitelist_function(".*_fromRust")
|
||||
.whitelist_type("RBindings_Whitelist")
|
||||
.blacklist_type("Rffi_.*")
|
||||
.blacklist_type("RTypes_.*")
|
||||
.whitelist_var("Sockaddr_AF_INET")
|
||||
.whitelist_var("Sockaddr_AF_INET6")
|
||||
.generate()
|
||||
|
@@ -23,14 +23,14 @@
|
||||
#include "crypto/random/test/DeterminentRandomSeed.h"
|
||||
#include "util/platform/Sockaddr.h"
|
||||
#include "util/version/Version.h"
|
||||
#include "util/events/EventBase.h"
|
||||
// #include "util/events/EventBase.h"
|
||||
#include "wire/PFChan.h"
|
||||
|
||||
enum RBindings_Version {
|
||||
RBindings_Version_CurrentProtocol = Version_CURRENT_PROTOCOL,
|
||||
};
|
||||
|
||||
// This structure is guaranteed to be present in the generated rust code
|
||||
// Also all functions in the above headers will be present.
|
||||
// Any types which are not transitively included in either this structure
|
||||
// or in one of the functions will not be generated. This prevents generating
|
||||
// a bunch of platform-specific trash like uint_fast8_t etc.
|
||||
@@ -42,4 +42,10 @@ struct RBindings_Whitelist {
|
||||
Log_t* f;
|
||||
enum RBindings_Version g;
|
||||
Sockaddr_t h;
|
||||
PFChan_FromPathfinder_t i;
|
||||
enum Log_Level j;
|
||||
Allocator_t* k;
|
||||
struct Sockaddr_storage l;
|
||||
struct Sockaddr m;
|
||||
Random_t* n;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@@ -90,7 +90,7 @@ impl log::Log for CjdnsLog {
|
||||
}
|
||||
let cmsg = CString::new(msg).unwrap();
|
||||
unsafe {
|
||||
cffi::Log_print0(
|
||||
cffi::Log_print_fromRust(
|
||||
*log,
|
||||
lvl,
|
||||
filebuf.as_ptr() as *const c_char,
|
||||
|
@@ -1693,59 +1693,10 @@ mod tests {
|
||||
|
||||
fn mk_msg(padding: usize, alloc: &mut Allocator) -> Message {
|
||||
unsafe {
|
||||
Message::from_c_message(cffi::Message_new(0, padding as u32, alloc.c()))
|
||||
Message::from_c_message(cffi::Message_new_fromRust(0, padding as u32, alloc.c()))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_encrypt_decrypt_rnd_nonce() {
|
||||
// The message
|
||||
const TEST_STRING: &[u8] = b"Hello World";
|
||||
const LEN: usize = TEST_STRING.len();
|
||||
let mut alloc = allocator::new!();
|
||||
let mut msg1 = mk_msg(128, &mut alloc);
|
||||
let mut msg2 = mk_msg(128, &mut alloc);
|
||||
msg1.push_bytes(TEST_STRING).unwrap();
|
||||
msg2.push_bytes(TEST_STRING).unwrap();
|
||||
|
||||
let nonce = [42_u8; 24];
|
||||
let secret = [142_u8; 32];
|
||||
|
||||
// Encrypt
|
||||
super::encrypt_rnd_nonce(nonce, &mut msg1, secret);
|
||||
unsafe {
|
||||
cffi::CryptoAuth_encryptRndNonce(
|
||||
nonce[..].as_ptr(),
|
||||
msg2.as_c_message(),
|
||||
secret[..].as_ptr(),
|
||||
);
|
||||
}
|
||||
//println!("Rust: {}", hex::encode(msg1.bytes()));
|
||||
//println!("C: {}", hex::encode(msg2.bytes()));
|
||||
assert_eq!(msg1.bytes(), msg2.bytes(), "Encrypt results are different");
|
||||
|
||||
// Decrypt
|
||||
let res = super::decrypt_rnd_nonce(nonce, &mut msg1, secret);
|
||||
assert!(res.is_ok(), "Decrypt (Rust) failed");
|
||||
let res = unsafe {
|
||||
cffi::CryptoAuth_decryptRndNonce(
|
||||
nonce[..].as_ptr(),
|
||||
msg2.as_c_message(),
|
||||
secret[..].as_ptr(),
|
||||
)
|
||||
};
|
||||
assert_eq!(res, 0, "Decrypt (C) failed, err_code = {}", res);
|
||||
//println!("Rust: {}", hex::encode(msg1.bytes()));
|
||||
//println!("C: {}", hex::encode(msg2.bytes()));
|
||||
assert_eq!(msg1.bytes(), msg2.bytes(), "Results are different");
|
||||
|
||||
// Ensure the message is the same
|
||||
assert_eq!(msg1.len(), LEN);
|
||||
assert_eq!(msg2.len(), LEN);
|
||||
assert_eq!(msg1.pop_bytes(LEN).unwrap(), TEST_STRING);
|
||||
assert_eq!(msg2.pop_bytes(LEN).unwrap(), TEST_STRING);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_encrypt_decrypt_without_auth() {
|
||||
let keys_api = CJDNSKeysApi::new().unwrap();
|
||||
@@ -1865,177 +1816,6 @@ mod tests {
|
||||
assert_eq!(msg.bytes(), b"HelloWorld012345");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross_encrypt_decrypt_rust_to_c() {
|
||||
let keys_api = CJDNSKeysApi::new().unwrap();
|
||||
let my_keys = keys_api.key_pair();
|
||||
let her_keys = keys_api.key_pair();
|
||||
let mut alloc = allocator::new!();
|
||||
|
||||
let rust_session = {
|
||||
let priv_key = my_keys.private_key.clone();
|
||||
let pub_key = her_keys.public_key.clone();
|
||||
let name = "bob";
|
||||
|
||||
let ca =
|
||||
super::CryptoAuth::new(Some(priv_key),
|
||||
EventBase {},
|
||||
Random::Legacy(Protected::new(fake_random(&mut alloc))),
|
||||
);
|
||||
let ca = Arc::new(ca);
|
||||
|
||||
let res = ca.add_user_ipv6(
|
||||
ByteString::from(name.to_string()),
|
||||
Some(ByteString::from(name.to_string())),
|
||||
None,
|
||||
);
|
||||
assert_eq!(res.err(), None);
|
||||
|
||||
let sess = super::Session::new(
|
||||
ca,
|
||||
pub_key,
|
||||
false,
|
||||
Some(format!("{}'s session", name)),
|
||||
);
|
||||
assert!(sess.is_ok());
|
||||
sess.unwrap()
|
||||
};
|
||||
|
||||
let mut msg = mk_msg(256, &mut alloc);
|
||||
msg.push_bytes(b"HelloWorld012345").unwrap();
|
||||
let orig_length = msg.len();
|
||||
|
||||
let res = rust_session.encrypt_msg(&mut msg);
|
||||
assert!(res.is_ok());
|
||||
assert_ne!(msg.len(), orig_length);
|
||||
|
||||
let c_session = {
|
||||
let priv_key = her_keys.private_key;
|
||||
let pub_key = my_keys.public_key;
|
||||
let name = "alice";
|
||||
|
||||
let event_base = unsafe { cffi::EventBase_new(alloc.c()) };
|
||||
|
||||
let ca = unsafe {
|
||||
cffi::CryptoAuth_new(
|
||||
alloc.c(),
|
||||
priv_key.as_ptr(),
|
||||
event_base,
|
||||
std::ptr::null_mut(),
|
||||
fake_random(&mut alloc),
|
||||
)
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
let name = cffi::String_new(name.as_ptr() as *const i8, alloc.c());
|
||||
cffi::CryptoAuth_addUser_ipv6(name, name, std::ptr::null_mut(), ca)
|
||||
};
|
||||
assert_eq!(res, 0, "CryptoAuth_addUser_ipv6() failed: {}", res);
|
||||
|
||||
unsafe {
|
||||
cffi::CryptoAuth_newSession(
|
||||
ca,
|
||||
alloc.c(),
|
||||
pub_key.as_ptr(),
|
||||
false,
|
||||
format!("{}'s session", name).as_mut_ptr() as *mut i8,
|
||||
false,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let res = unsafe { cffi::CryptoAuth_decrypt(c_session, msg.as_c_message()) };
|
||||
assert_eq!(res, cffi::CryptoAuth_DecryptErr::CryptoAuth_DecryptErr_NONE);
|
||||
assert_eq!(msg.len(), orig_length);
|
||||
assert_eq!(msg.bytes(), b"HelloWorld012345");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross_encrypt_decrypt_c_to_rust() {
|
||||
let keys_api = CJDNSKeysApi::new().unwrap();
|
||||
let my_keys = keys_api.key_pair();
|
||||
let her_keys = keys_api.key_pair();
|
||||
let mut alloc = allocator::new!();
|
||||
|
||||
let c_session = {
|
||||
let priv_key = my_keys.private_key.clone();
|
||||
let pub_key = her_keys.public_key.clone();
|
||||
let name = "bob";
|
||||
|
||||
let event_base = unsafe { cffi::EventBase_new(alloc.c()) };
|
||||
|
||||
let ca = unsafe {
|
||||
cffi::CryptoAuth_new(
|
||||
alloc.c(),
|
||||
priv_key.as_ptr(),
|
||||
event_base,
|
||||
std::ptr::null_mut(),
|
||||
fake_random(&mut alloc),
|
||||
)
|
||||
};
|
||||
|
||||
let res = unsafe {
|
||||
let name = cffi::String_new(name.as_ptr() as *const i8, alloc.c());
|
||||
cffi::CryptoAuth_addUser_ipv6(name, name, std::ptr::null_mut(), ca)
|
||||
};
|
||||
assert_eq!(res, 0, "CryptoAuth_addUser_ipv6() failed: {}", res);
|
||||
|
||||
unsafe {
|
||||
cffi::CryptoAuth_newSession(
|
||||
ca,
|
||||
alloc.c(),
|
||||
pub_key.as_ptr(),
|
||||
false,
|
||||
format!("{}'s session", name).as_mut_ptr() as *mut i8,
|
||||
false,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut msg = mk_msg(256, &mut alloc);
|
||||
msg.push_bytes(b"HelloWorld012345").unwrap();
|
||||
let orig_length = msg.len();
|
||||
|
||||
let res = unsafe { cffi::CryptoAuth_encrypt(c_session, msg.as_c_message()) };
|
||||
assert_eq!(res, 0);
|
||||
assert_ne!(msg.len(), orig_length);
|
||||
|
||||
let rust_session = {
|
||||
let priv_key = her_keys.private_key;
|
||||
let pub_key = my_keys.public_key;
|
||||
let name = "alice";
|
||||
|
||||
let ca =
|
||||
super::CryptoAuth::new(
|
||||
Some(priv_key),
|
||||
EventBase {},
|
||||
Random::Legacy(Protected::new(fake_random(&mut alloc))),
|
||||
);
|
||||
let ca = Arc::new(ca);
|
||||
|
||||
let res = ca.add_user_ipv6(
|
||||
ByteString::from(name.to_string()),
|
||||
Some(ByteString::from(name.to_string())),
|
||||
None,
|
||||
);
|
||||
assert_eq!(res.err(), None);
|
||||
|
||||
let sess = super::Session::new(
|
||||
ca,
|
||||
pub_key,
|
||||
false,
|
||||
Some(format!("{}'s session", name)),
|
||||
);
|
||||
assert!(sess.is_ok());
|
||||
sess.unwrap()
|
||||
};
|
||||
|
||||
let res = rust_session.decrypt_msg(&mut msg);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(msg.len(), orig_length);
|
||||
assert_eq!(msg.bytes(), b"HelloWorld012345");
|
||||
}
|
||||
|
||||
fn fake_random(alloc: &mut Allocator) -> *mut cffi::Random_t {
|
||||
unsafe {
|
||||
let fake_seed = cffi::DeterminentRandomSeed_new(alloc.c(), std::ptr::null_mut());
|
||||
|
@@ -3,13 +3,13 @@
|
||||
pub use cjdns_crypto::random::DefaultRandom as SodiumRandom;
|
||||
pub use cjdns_crypto::random::Random as Rand;
|
||||
|
||||
use crate::cffi::Random as CRandom;
|
||||
use crate::cffi::Random_bytes;
|
||||
use crate::cffi::Random_t;
|
||||
use crate::cffi::Random_bytes_fromRust;
|
||||
use crate::gcl::Protected;
|
||||
|
||||
pub enum Random {
|
||||
Sodium(SodiumRandom),
|
||||
Legacy(Protected<*mut CRandom>),
|
||||
Legacy(Protected<*mut Random_t>),
|
||||
#[cfg(test)]
|
||||
Fake,
|
||||
}
|
||||
@@ -21,7 +21,7 @@ impl Random {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wrap_legacy(c_random: *mut CRandom) -> Self {
|
||||
pub fn wrap_legacy(c_random: *mut Random_t) -> Self {
|
||||
Random::Legacy(Protected::new(c_random))
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ impl Random {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn c_random_bytes(rand: *mut CRandom, dest: &mut [u8]) {
|
||||
fn c_random_bytes(rand: *mut Random_t, dest: &mut [u8]) {
|
||||
unsafe {
|
||||
Random_bytes(rand, dest.as_mut_ptr(), dest.len() as u64);
|
||||
Random_bytes_fromRust(rand, dest.as_mut_ptr(), dest.len() as u64);
|
||||
}
|
||||
}
|
||||
|
||||
|
6
rust/cjdns_sys/src/external/interface/cif.rs
vendored
6
rust/cjdns_sys/src/external/interface/cif.rs
vendored
@@ -22,7 +22,7 @@ impl IfRecv for CRecv {
|
||||
let c_msg = m.as_c_message();
|
||||
let cif = self.c_iface.lock();
|
||||
unsafe {
|
||||
(cffi::Iface_incomingFromRust(c_msg, *cif) as *mut RTypes_Error_t).as_mut()
|
||||
(cffi::Iface_incoming_fromRust(c_msg, *cif) as *mut RTypes_Error_t).as_mut()
|
||||
.map(|e|e.e.take())
|
||||
.flatten()
|
||||
.map(Err)
|
||||
@@ -41,7 +41,7 @@ struct CIface {
|
||||
dg: Arc<()>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn from_c(msg: *mut cffi::Message, iface_p: *mut cffi::Iface) -> *mut cffi::RTypes_Error_t {
|
||||
unsafe extern "C" fn from_c(msg: *mut cffi::Message, iface_p: *mut cffi::Iface) -> *mut RTypes_Error_t {
|
||||
let iface = from_c!(iface_p as *mut CIface);
|
||||
let alloc = (*msg)._alloc;
|
||||
let msg = Message::from_c_message(msg);
|
||||
@@ -50,7 +50,7 @@ unsafe extern "C" fn from_c(msg: *mut cffi::Message, iface_p: *mut cffi::Iface)
|
||||
Ok(_) => std::ptr::null_mut(),
|
||||
Err(e) => {
|
||||
let e: *mut RTypes_Error_t = allocator::adopt(alloc, RTypes_Error_t { e: Some(e) });
|
||||
e as *mut cffi::RTypes_Error_t
|
||||
e as *mut RTypes_Error_t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ struct TestWrapperPvt {
|
||||
impl Drop for TestWrapperPvt {
|
||||
fn drop(&mut self) {
|
||||
let _guard = GCL.lock();
|
||||
unsafe { cffi::RustIface_dropped() };
|
||||
unsafe { cffi::RustIface_dropped_fromRust() };
|
||||
println!("TestWrapperPvt dropped");
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ impl IfRecv for TestWrapperInt {
|
||||
{
|
||||
let mut pvt_l = self.pvt.lock();
|
||||
pvt_l.outgoing_count += 1;
|
||||
unsafe { cffi::RustIface_gotOutgoing() };
|
||||
unsafe { cffi::RustIface_gotOutgoing_fromRust() };
|
||||
println!(
|
||||
"Received outgoing message, total out {} in {}",
|
||||
pvt_l.outgoing_count, pvt_l.incoming_count
|
||||
@@ -46,7 +46,7 @@ impl IfRecv for TestWrapperInt {
|
||||
impl Drop for TestWrapperInt {
|
||||
fn drop(&mut self) {
|
||||
let _guard = GCL.lock();
|
||||
unsafe { cffi::RustIface_dropped() };
|
||||
unsafe { cffi::RustIface_dropped_fromRust() };
|
||||
println!("TestWrapperInt dropped");
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ impl IfRecv for TestWrapperExt {
|
||||
{
|
||||
let mut pvt_l = self.pvt.lock();
|
||||
pvt_l.incoming_count += 1;
|
||||
unsafe { cffi::RustIface_gotIncoming() };
|
||||
unsafe { cffi::RustIface_gotIncoming_fromRust() };
|
||||
println!(
|
||||
"Received incoming message, total out {} in {}",
|
||||
pvt_l.outgoing_count, pvt_l.incoming_count
|
||||
@@ -72,7 +72,7 @@ impl IfRecv for TestWrapperExt {
|
||||
impl Drop for TestWrapperExt {
|
||||
fn drop(&mut self) {
|
||||
let _guard = GCL.lock();
|
||||
unsafe { cffi::RustIface_dropped() };
|
||||
unsafe { cffi::RustIface_dropped_fromRust() };
|
||||
println!("TestWrapperExt dropped");
|
||||
}
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@ impl Message {
|
||||
}
|
||||
|
||||
pub fn anew(padding: usize, alloc: &mut Allocator) -> Self {
|
||||
unsafe { Message { msg: cffi::Message_new(0, padding as u32, alloc.c()), alloc: None } }
|
||||
unsafe { Message { msg: cffi::Message_new_fromRust(0, padding as u32, alloc.c()), alloc: None } }
|
||||
}
|
||||
|
||||
/// Create empty new message with the given amount of free space,
|
||||
@@ -73,7 +73,7 @@ impl Message {
|
||||
/// Note: this function does *NOT* clone original message,
|
||||
/// the resulting message will be empty.
|
||||
pub fn new_same_alloc(&self, padding: usize) -> Self {
|
||||
unsafe { Message { msg: cffi::Message_new(0, padding as u32, (*self.msg)._alloc), alloc: None } }
|
||||
unsafe { Message { msg: cffi::Message_new_fromRust(0, padding as u32, (*self.msg)._alloc), alloc: None } }
|
||||
}
|
||||
|
||||
/// Construct a Rust `Message` by wrapping a pointer to C `Message`.
|
||||
@@ -441,7 +441,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_message_bytes() {
|
||||
let mut alloc = allocator::new!();
|
||||
let c_msg = unsafe { cffi::Message_new(4, 5, alloc.c()) };
|
||||
let c_msg = unsafe { cffi::Message_new_fromRust(4, 5, alloc.c()) };
|
||||
let mut msg = Message::from_c_message(c_msg);
|
||||
assert_eq!(msg.len(), 4);
|
||||
assert_eq!(msg.pad(), 5);
|
||||
@@ -467,7 +467,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_message_push_pop() {
|
||||
let mut alloc = allocator::new!();
|
||||
let c_msg = unsafe { cffi::Message_new(64, 64, alloc.c()) };
|
||||
let c_msg = unsafe { cffi::Message_new_fromRust(64, 64, alloc.c()) };
|
||||
let mut msg = Message::from_c_message(c_msg);
|
||||
assert_eq!(msg.len(), 64);
|
||||
assert_eq!(msg.push(0x12345678_u32), Ok(()));
|
||||
@@ -482,7 +482,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_message_push_pop_unaligned() {
|
||||
let mut alloc = allocator::new!();
|
||||
let c_msg = unsafe { cffi::Message_new(64, 64, alloc.c()) };
|
||||
let c_msg = unsafe { cffi::Message_new_fromRust(64, 64, alloc.c()) };
|
||||
let mut msg = Message::from_c_message(c_msg);
|
||||
assert_eq!(msg.len(), 64);
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
use super::allocator::file_line;
|
||||
use super::{cstr, strc};
|
||||
use crate::bytestring::ByteString;
|
||||
use crate::cffi::{self, Allocator_t, Random_t, String_t};
|
||||
@@ -134,11 +135,7 @@ pub unsafe extern "C" fn Rffi_CryptoAuth2_tryHandshake(
|
||||
Ok((code, sess)) => {
|
||||
(*ret).code = code;
|
||||
if let Some(sess) = sess {
|
||||
let child = cffi::Allocator__child(
|
||||
alloc,
|
||||
b"rffi_tryHandshake\0".as_ptr() as *const c_char,
|
||||
163,
|
||||
);
|
||||
let child = allocator::rs(alloc).child(file_line!());
|
||||
(*ret).alloc = child;
|
||||
(*ret).sess = wrap_session(sess, child)
|
||||
}
|
||||
|
@@ -1,10 +1,10 @@
|
||||
use tokio::io::Interest;
|
||||
use tokio::io::unix::AsyncFd;
|
||||
use crate::cffi::{Allocator_t, Allocator__onFree, Allocator_OnFreeJob};
|
||||
use crate::cffi::Allocator_t;
|
||||
use crate::gcl::Protected;
|
||||
use crate::rffi::allocator;
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::{c_int, c_char};
|
||||
use crate::rffi::allocator::{self, file_line};
|
||||
use crate::util::identity::{from_c, Identity};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -13,11 +13,14 @@ struct FdReadable {
|
||||
active: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
pub struct Rffi_FdReadableTx(Arc<FdReadable>);
|
||||
pub struct Rffi_FdReadableTx{
|
||||
a: Arc<FdReadable>,
|
||||
identity: Identity<Self>,
|
||||
}
|
||||
|
||||
pub extern "C" fn fd_readable_on_free(j: *mut Allocator_OnFreeJob) {
|
||||
let timer_tx = unsafe { &*((*j).userData as *mut Rffi_FdReadableTx) };
|
||||
timer_tx.0.active.store(false, Ordering::Relaxed);
|
||||
pub extern "C" fn fd_readable_on_free(j: *mut c_void) {
|
||||
let timer_tx = unsafe { from_c!(j as *mut Rffi_FdReadableTx) };
|
||||
timer_tx.a.active.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -52,14 +55,16 @@ pub extern "C" fn Rffi_pollFdReadable(
|
||||
let active = Arc::clone(&rtx.active);
|
||||
|
||||
unsafe {
|
||||
let event_tx = allocator::adopt(alloc, Rffi_FdReadableTx(rtx));
|
||||
let event_tx = allocator::adopt(alloc,
|
||||
Rffi_FdReadableTx{ a:rtx, identity: Identity::default() }
|
||||
);
|
||||
// Note: we must close the event in the allocator onFree rather than in the drop
|
||||
// because the drop only happens later, when Rust wants to.
|
||||
Allocator__onFree(alloc,
|
||||
Some(fd_readable_on_free),
|
||||
allocator::rs(alloc).on_free(
|
||||
fd_readable_on_free,
|
||||
event_tx as *mut c_void,
|
||||
("fd_readable.rs\0").as_bytes().as_ptr() as *const ::std::os::raw::c_char,
|
||||
line!() as std::os::raw::c_int);
|
||||
file_line!()
|
||||
);
|
||||
*out = event_tx;
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use super::Rffi_EventLoop;
|
||||
use crate::cffi::{Allocator_t, Allocator__onFree, Allocator_OnFreeJob};
|
||||
use crate::cffi::Allocator_t;
|
||||
use crate::gcl::Protected;
|
||||
use crate::rffi::allocator;
|
||||
use crate::util::identity::from_c;
|
||||
use crate::rffi::allocator::{self, file_line};
|
||||
use crate::util::identity::{from_c, from_c_const, Identity};
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::{c_int, c_ulong};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -17,7 +17,10 @@ enum TimerCommand {
|
||||
}
|
||||
|
||||
/// The handle returned to C, used to talk to the timer task.
|
||||
pub struct Rffi_TimerTx(Arc<TimerTx>);
|
||||
pub struct Rffi_TimerTx{
|
||||
a: Arc<TimerTx>,
|
||||
identity: Identity<Self>,
|
||||
}
|
||||
|
||||
/// Internal struct, which we keep weak references to.
|
||||
pub struct TimerTx {
|
||||
@@ -25,10 +28,10 @@ pub struct TimerTx {
|
||||
active: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
pub extern "C" fn timeout_on_free(j: *mut Allocator_OnFreeJob) {
|
||||
let timer_tx = unsafe { &*((*j).userData as *mut Rffi_TimerTx) };
|
||||
timer_tx.0.active.store(false, Ordering::Relaxed);
|
||||
timer_tx.0.rffi_send(TimerCommand::Free);
|
||||
pub extern "C" fn timeout_on_free(j: *mut c_void) {
|
||||
let timer_tx = from_c!(j as *mut Rffi_TimerTx);
|
||||
timer_tx.a.active.store(false, Ordering::Relaxed);
|
||||
timer_tx.a.rffi_send(TimerCommand::Free);
|
||||
}
|
||||
|
||||
/// Spawn a timer task for a timeout or interval, that calls some callback whenever it triggers.
|
||||
@@ -59,14 +62,13 @@ pub extern "C" fn Rffi_setTimeout(
|
||||
let is_active = rtx.active.clone();
|
||||
|
||||
unsafe {
|
||||
let timer_tx = allocator::adopt(alloc, Rffi_TimerTx(rtx));
|
||||
let timer_tx = allocator::adopt(alloc, Rffi_TimerTx{
|
||||
a: rtx,
|
||||
identity: Default::default(),
|
||||
});
|
||||
// Note: we must close the event in the allocator onFree rather than in the drop
|
||||
// because the drop only happens later, when Rust wants to.
|
||||
Allocator__onFree(alloc,
|
||||
Some(timeout_on_free),
|
||||
timer_tx as *mut c_void,
|
||||
("timeout.rs\0").as_bytes().as_ptr() as *const ::std::os::raw::c_char,
|
||||
line!() as std::os::raw::c_int);
|
||||
allocator::rs(alloc).on_free(timeout_on_free, timer_tx as _, file_line!());
|
||||
*out_timer_tx = timer_tx;
|
||||
}
|
||||
|
||||
@@ -131,23 +133,23 @@ pub extern "C" fn Rffi_resetTimeout(
|
||||
timer_tx: *const Rffi_TimerTx,
|
||||
timeout_millis: c_ulong,
|
||||
) -> c_int {
|
||||
let timer_tx = unsafe { &*timer_tx };
|
||||
timer_tx.0.rffi_send(TimerCommand::Reset(timeout_millis))
|
||||
let timer_tx = from_c_const!(timer_tx);
|
||||
timer_tx.a.rffi_send(TimerCommand::Reset(timeout_millis))
|
||||
}
|
||||
|
||||
/// Cancel a timer task.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Rffi_clearTimeout(timer_tx: *const Rffi_TimerTx) -> c_int {
|
||||
let timer_tx = unsafe { &*timer_tx };
|
||||
timer_tx.0.active.store(false, Ordering::Relaxed);
|
||||
timer_tx.0.rffi_send(TimerCommand::Cancel)
|
||||
let timer_tx = from_c_const!(timer_tx);
|
||||
timer_tx.a.active.store(false, Ordering::Relaxed);
|
||||
timer_tx.a.rffi_send(TimerCommand::Cancel)
|
||||
}
|
||||
|
||||
/// Return 1 if a timer task is still running, 0 otherwise.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Rffi_isTimeoutActive(timer_tx: *const Rffi_TimerTx) -> c_int {
|
||||
let timer_tx = unsafe { &*timer_tx };
|
||||
timer_tx.0.active.load(Ordering::Relaxed) as _
|
||||
let timer_tx = from_c_const!(timer_tx);
|
||||
timer_tx.a.active.load(Ordering::Relaxed) as _
|
||||
}
|
||||
|
||||
/// Cancel all timer tasks.
|
||||
|
@@ -33,4 +33,18 @@ macro_rules! from_c {
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use from_c;
|
||||
pub(crate) use from_c;
|
||||
|
||||
|
||||
macro_rules! from_c_const {
|
||||
($obj:expr) => {
|
||||
{
|
||||
let o = $obj;
|
||||
unsafe {
|
||||
(*o).identity.check();
|
||||
&(*o)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use from_c_const;
|
@@ -94,18 +94,18 @@ impl From<*const cffi::Sockaddr> for Sockaddr {
|
||||
}
|
||||
}
|
||||
impl From<&SocketAddr> for Sockaddr {
|
||||
fn from(sa: &SocketAddr) -> Self {
|
||||
fn from(sa: &SocketAddr) -> Self {
|
||||
let mut out = Self{ss: unsafe { std::mem::zeroed::<cffi::Sockaddr_storage>() } };
|
||||
match sa.ip() {
|
||||
IpAddr::V4(v4) => unsafe {
|
||||
cffi::Sockaddr_initFromBytes(
|
||||
cffi::Sockaddr_initFromBytes_fromRust(
|
||||
&mut out.ss as *mut _,
|
||||
v4.octets()[..].as_ptr(),
|
||||
cffi::Sockaddr_AF_INET,
|
||||
);
|
||||
}
|
||||
IpAddr::V6(v6) => unsafe {
|
||||
cffi::Sockaddr_initFromBytes(
|
||||
cffi::Sockaddr_initFromBytes_fromRust(
|
||||
&mut out.ss as *mut _,
|
||||
v6.octets()[..].as_ptr(),
|
||||
cffi::Sockaddr_AF_INET6,
|
||||
@@ -113,7 +113,7 @@ impl From<&SocketAddr> for Sockaddr {
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
cffi::Sockaddr_setPort(&mut out.ss.addr as *mut _, sa.port());
|
||||
cffi::Sockaddr_setPort_fromRust(&mut out.ss.addr as *mut _, sa.port());
|
||||
}
|
||||
out
|
||||
}
|
||||
@@ -123,10 +123,10 @@ impl TryFrom<&Sockaddr> for SocketAddr {
|
||||
fn try_from(sa: &Sockaddr) -> anyhow::Result<Self> {
|
||||
let mut ip = [0u8; 16];
|
||||
let (port, is_ip6, is_handle) = unsafe {
|
||||
cffi::Sockaddr_asIp6(&mut ip as *mut _, &sa.ss.addr as *const _);
|
||||
cffi::Sockaddr_asIp6_fromRust(&mut ip as *mut _, &sa.ss.addr as *const _);
|
||||
(
|
||||
cffi::Sockaddr_getPort(&sa.ss.addr as *const _),
|
||||
cffi::Sockaddr_getFamily(&sa.ss.addr as *const _) == cffi::Sockaddr_AF_INET6,
|
||||
cffi::Sockaddr_getPort_fromRust(&sa.ss.addr as *const _),
|
||||
cffi::Sockaddr_getFamily_fromRust(&sa.ss.addr as *const _) == cffi::Sockaddr_AF_INET6,
|
||||
sa.ss.addr.type_ != 0
|
||||
)
|
||||
};
|
||||
|
@@ -32,7 +32,7 @@ void Log_print(struct Log* log,
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Log_print0(struct Log* log, enum Log_Level lvl, const char* file, int line, const char* msg)
|
||||
void Log_print_fromRust(struct Log* log, enum Log_Level lvl, const char* file, int line, const char* msg)
|
||||
{
|
||||
Log_print(log, lvl, file, line, "%s", msg);
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ void Log_print(struct Log* log,
|
||||
const char* format,
|
||||
...);
|
||||
|
||||
void Log_print0(struct Log* log, enum Log_Level lvl, const char* file, int line, const char* msg);
|
||||
void Log_print_fromRust(struct Log* log, enum Log_Level lvl, const char* file, int line, const char* msg);
|
||||
|
||||
#define Log_printf(log, level, ...) \
|
||||
do { \
|
||||
|
@@ -257,6 +257,10 @@ int Sockaddr_getPort(const struct Sockaddr* sockaddr)
|
||||
const uint16_t* pp = getPortPtr((struct Sockaddr*) sockaddr);
|
||||
return (pp) ? Endian_bigEndianToHost16(*pp) : -1;
|
||||
}
|
||||
int Sockaddr_getPort_fromRust(const struct Sockaddr* sockaddr)
|
||||
{
|
||||
return Sockaddr_getPort(sockaddr);
|
||||
}
|
||||
|
||||
int Sockaddr_setPort(struct Sockaddr* sockaddr, uint16_t port)
|
||||
{
|
||||
@@ -267,6 +271,10 @@ int Sockaddr_setPort(struct Sockaddr* sockaddr, uint16_t port)
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int Sockaddr_setPort_fromRust(struct Sockaddr* sockaddr, uint16_t port)
|
||||
{
|
||||
return Sockaddr_setPort(sockaddr, port);
|
||||
}
|
||||
|
||||
int Sockaddr_getAddress(struct Sockaddr* sockaddr, void* addrPtr)
|
||||
{
|
||||
@@ -299,6 +307,11 @@ int Sockaddr_getFamily(const struct Sockaddr* sockaddr)
|
||||
return sa->ss.ss_family;
|
||||
}
|
||||
|
||||
int Sockaddr_getFamily_fromRust(const struct Sockaddr* sockaddr)
|
||||
{
|
||||
return Sockaddr_getFamily(sockaddr);
|
||||
}
|
||||
|
||||
struct Sockaddr* Sockaddr_initFromBytes(struct Sockaddr_storage* out, const uint8_t* bytes, int addrFamily)
|
||||
{
|
||||
switch (addrFamily) {
|
||||
@@ -323,6 +336,14 @@ struct Sockaddr* Sockaddr_initFromBytes(struct Sockaddr_storage* out, const uint
|
||||
return &out->addr;
|
||||
}
|
||||
|
||||
Sockaddr_t* Sockaddr_initFromBytes_fromRust(
|
||||
struct Sockaddr_storage* out,
|
||||
const uint8_t* bytes,
|
||||
int addrFamily
|
||||
) {
|
||||
return Sockaddr_initFromBytes(out, bytes, addrFamily);
|
||||
}
|
||||
|
||||
struct Sockaddr* Sockaddr_fromBytes(const uint8_t* bytes, int addrFamily, struct Allocator* alloc)
|
||||
{
|
||||
struct Sockaddr_storage ss;
|
||||
@@ -383,6 +404,10 @@ void Sockaddr_asIp6(uint8_t addrOut[static 16], const struct Sockaddr* sockaddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
void Sockaddr_asIp6_fromRust(uint8_t addrOut[static 16], const struct Sockaddr* sockaddr)
|
||||
{
|
||||
Sockaddr_asIp6(addrOut, sockaddr);
|
||||
}
|
||||
|
||||
int Sockaddr_compare(const struct Sockaddr* a, const struct Sockaddr* b)
|
||||
{
|
||||
|
@@ -99,6 +99,7 @@ char* Sockaddr_print(Sockaddr_t* addr, struct Allocator* alloc);
|
||||
* @return the port number or -1 if not applicable to this sockaddr.
|
||||
*/
|
||||
int Sockaddr_getPort(const Sockaddr_t* sa);
|
||||
int Sockaddr_getPort_fromRust(const struct Sockaddr* sockaddr);
|
||||
|
||||
/**
|
||||
* Set the port for a sockaddr if applicable.
|
||||
@@ -108,6 +109,7 @@ int Sockaddr_getPort(const Sockaddr_t* sa);
|
||||
* @return 0 if all goes well, -1 if not applicable to this sockaddr.
|
||||
*/
|
||||
int Sockaddr_setPort(Sockaddr_t* sa, uint16_t port);
|
||||
int Sockaddr_setPort_fromRust(struct Sockaddr* sockaddr, uint16_t port);
|
||||
|
||||
/**
|
||||
* Get the address family for the address.
|
||||
@@ -118,6 +120,7 @@ int Sockaddr_setPort(Sockaddr_t* sa, uint16_t port);
|
||||
extern const int Sockaddr_AF_INET;
|
||||
extern const int Sockaddr_AF_INET6;
|
||||
int Sockaddr_getFamily(const Sockaddr_t* sa);
|
||||
int Sockaddr_getFamily_fromRust(const struct Sockaddr* sockaddr);
|
||||
|
||||
/**
|
||||
* Get the address stored in a sockaddr.
|
||||
@@ -137,6 +140,7 @@ int Sockaddr_getAddress(Sockaddr_t* sa, void* addrPtr);
|
||||
* Other addresses larger than 14 bytes are represented as ffff::<14 bytes of sha256 hash of address>
|
||||
*/
|
||||
void Sockaddr_asIp6(uint8_t addrOut[static 16], const Sockaddr_t* sockaddr);
|
||||
void Sockaddr_asIp6_fromRust(uint8_t addrOut[static 16], const struct Sockaddr* sockaddr);
|
||||
|
||||
/**
|
||||
* Output the native form of a sockaddr.
|
||||
@@ -155,6 +159,11 @@ static inline void* Sockaddr_asNative(Sockaddr_t* sa)
|
||||
* @return Sockaddr_t* which points to the memory of `out`
|
||||
*/
|
||||
Sockaddr_t* Sockaddr_initFromBytes(struct Sockaddr_storage* out, const uint8_t* bytes, int addrFamily);
|
||||
Sockaddr_t* Sockaddr_initFromBytes_fromRust(
|
||||
struct Sockaddr_storage* out,
|
||||
const uint8_t* bytes,
|
||||
int addrFamily
|
||||
);
|
||||
|
||||
/**
|
||||
* Sockaddr_fromBytes() takes
|
||||
|
@@ -29,6 +29,13 @@ Message_t* Message_new(uint32_t messageLength,
|
||||
return out;
|
||||
}
|
||||
|
||||
struct Message* Message_new_fromRust(uint32_t messageLength,
|
||||
uint32_t amountOfPadding,
|
||||
struct Allocator* alloc)
|
||||
{
|
||||
return Message_new(messageLength, amountOfPadding, alloc);
|
||||
}
|
||||
|
||||
void Message_setAssociatedFd(Message_t* msg, int fd)
|
||||
{
|
||||
if (fd == -1) {
|
||||
|
@@ -98,6 +98,10 @@ struct Message* Message_new(uint32_t messageLength,
|
||||
uint32_t amountOfPadding,
|
||||
struct Allocator* alloc);
|
||||
|
||||
struct Message* Message_new_fromRust(uint32_t messageLength,
|
||||
uint32_t amountOfPadding,
|
||||
struct Allocator* alloc);
|
||||
|
||||
void Message_setAssociatedFd(struct Message* msg, int fd);
|
||||
|
||||
int Message_getAssociatedFd(struct Message* msg);
|
||||
|
@@ -96,6 +96,20 @@ struct PFChan_Pathfinder_Superiority
|
||||
Assert_compileTime(
|
||||
sizeof(struct PFChan_Pathfinder_Superiority) == PFChan_Pathfinder_Superiority_SIZE);
|
||||
|
||||
typedef struct PFChan_Pathfinder_ConnectPeer {
|
||||
// If ::ffff:XXXX:XXXX then is 4 in 6
|
||||
uint8_t ip[16];
|
||||
// Public key of the peer
|
||||
uint8_t pubkey[32];
|
||||
// Null terminated, e.g. "AP_LOGIN: 65535\0"
|
||||
uint8_t login[16];
|
||||
// Null terminated, e.g. "AP_PASS: 4eqnmtOq/Mo\0"
|
||||
uint8_t password[24];
|
||||
// Protocol version of the peer
|
||||
uint32_t version;
|
||||
} PFChan_Pathfinder_ConnectPeer_t;
|
||||
Assert_compileTime(sizeof(PFChan_Pathfinder_ConnectPeer_t) == 92);
|
||||
|
||||
enum PFChan_Pathfinder
|
||||
{
|
||||
/** Below the lowest valid value. */
|
||||
@@ -173,10 +187,16 @@ enum PFChan_Pathfinder
|
||||
*/
|
||||
PFChan_Pathfinder_SNODE = 522,
|
||||
|
||||
PFChan_Pathfinder__TOO_HIGH = 523,
|
||||
/**
|
||||
* Send a peer setup request.
|
||||
* (Received by: InterfaceController.c)
|
||||
*/
|
||||
PFChan_Pathfinder_CONNECT_PEER = 523,
|
||||
|
||||
PFChan_Pathfinder__TOO_HIGH = 524,
|
||||
};
|
||||
|
||||
struct PFChan_FromPathfinder
|
||||
typedef struct PFChan_FromPathfinder
|
||||
{
|
||||
enum PFChan_Pathfinder event_be;
|
||||
|
||||
@@ -190,9 +210,10 @@ struct PFChan_FromPathfinder
|
||||
struct PFChan_Msg sendmsg;
|
||||
struct PFChan_Ping ping;
|
||||
struct PFChan_Ping pong;
|
||||
PFChan_Pathfinder_ConnectPeer_t cp;
|
||||
uint8_t bytes[1];
|
||||
} content;
|
||||
};
|
||||
} PFChan_FromPathfinder_t;
|
||||
|
||||
//// ------------------------- Core Events ------------------------- ////
|
||||
|
||||
|
Reference in New Issue
Block a user