use std::net::IpAddr;
use std::sync::Arc;
use crate::api::gate::{Gate, Permit};
use crate::ffi::Slice;
pub struct GateHandle(pub Arc<Gate>);
pub struct PermitHandle(pub Permit);
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Rate {
pub period: f64,
pub count: u32,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_new(max_connections: u32, max_connections_per_ip: u32, rates: *const Rate, rate_count: usize, max_connection_history: usize) -> *mut GateHandle {
let mut limits = Vec::with_capacity(rate_count);
if !rates.is_null() {
for index in 0..rate_count {
let rate = unsafe { *rates.add(index) };
limits.push((rate.period, rate.count));
}
}
Box::into_raw(Box::new(GateHandle(Gate::new(max_connections, max_connections_per_ip, limits, max_connection_history))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_free(gate: *mut GateHandle) {
if !gate.is_null() {
drop(unsafe { Box::from_raw(gate) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_count(gate: *const GateHandle) -> u32 {
unsafe { gate.as_ref() }.map_or(0, |gate| gate.0.count())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_max_connections(gate: *const GateHandle) -> u32 {
unsafe { gate.as_ref() }.map_or(0, |gate| gate.0.max_connections)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_max_connections_per_ip(gate: *const GateHandle) -> u32 {
unsafe { gate.as_ref() }.map_or(0, |gate| gate.0.max_connections_per_ip)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_max_connection_history(gate: *const GateHandle) -> usize {
unsafe { gate.as_ref() }.map_or(0, |gate| gate.0.max_connection_history)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_rate_count(gate: *const GateHandle) -> usize {
unsafe { gate.as_ref() }.map_or(0, |gate| gate.0.max_connection_rate.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_rate(gate: *const GateHandle, index: usize) -> Rate {
match unsafe { gate.as_ref() }.and_then(|gate| gate.0.max_connection_rate.get(index)) {
Some(&(period, count)) => Rate { period, count },
None => Rate { period: 0.0, count: 0 },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_window(gate: *const GateHandle) -> f64 {
unsafe { gate.as_ref() }.map_or(0.0, |gate| gate.0.window())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_count_for(gate: *const GateHandle, ip: *const u8, ip_len: usize) -> u32 {
let (Some(gate), Some(ip)) = (unsafe { gate.as_ref() }, unsafe { address(ip, ip_len) }) else {
return 0;
};
crate::helpers::sync::Lock::on(&gate.0.state).per_ip.get(&ip).copied().unwrap_or(0)
}
unsafe fn address(ip: *const u8, ip_len: usize) -> Option<IpAddr> {
unsafe { Slice::borrow_text(ip, ip_len) }?.parse().ok()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_admit(gate: *const GateHandle, ip: *const u8, ip_len: usize) -> *mut PermitHandle {
let Some(gate) = (unsafe { gate.as_ref() }) else {
return std::ptr::null_mut();
};
match gate.0.admit(unsafe { address(ip, ip_len) }, std::time::Instant::now()) {
Some(permit) => Box::into_raw(Box::new(PermitHandle(permit))),
None => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_gate_sweep(gate: *const GateHandle) {
if let Some(gate) = unsafe { gate.as_ref() } {
gate.0.sweep(std::time::Instant::now());
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_permit_free(permit: *mut PermitHandle) {
if !permit.is_null() {
drop(unsafe { Box::from_raw(permit) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_permit_address(permit: *const PermitHandle) -> crate::ffi::Buffer {
match unsafe { permit.as_ref() }.and_then(|permit| permit.0.ip) {
Some(ip) => crate::ffi::Buffer::new(ip.to_string().into_bytes()),
None => crate::ffi::Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_permit_gate(permit: *const PermitHandle) -> *mut GateHandle {
match unsafe { permit.as_ref() } {
Some(permit) => Box::into_raw(Box::new(GateHandle(Arc::clone(&permit.0.gate)))),
None => std::ptr::null_mut(),
}
}