use super::matchy::{
matchy_aget_value, matchy_close, matchy_entry_data_list_t, matchy_entry_data_t, matchy_entry_s,
matchy_get_entry_data_list, matchy_open, matchy_query, matchy_t, MATCHY_SUCCESS,
};
use std::ffi::{CStr, CString};
use std::mem;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
#[repr(C)]
pub struct MMDB_s {
_matchy_db: *mut matchy_t,
pub flags: u32,
pub filename: *const c_char,
pub file_size: isize,
}
#[repr(C)]
pub struct MMDB_entry_s {
pub mmdb: *const MMDB_s,
pub _matchy_entry: matchy_entry_s,
}
#[repr(C)]
pub struct MMDB_lookup_result_s {
pub found_entry: bool,
pub entry: MMDB_entry_s,
pub netmask: u16,
}
#[allow(non_camel_case_types)]
pub type MMDB_entry_data_s = matchy_entry_data_t;
#[repr(C)]
pub struct MMDB_entry_data_list_s {
pub entry_data: MMDB_entry_data_s,
pub next: *mut Self,
pub pool: *mut c_void,
}
const MMDB_SUCCESS: c_int = 0;
const MMDB_FILE_OPEN_ERROR: c_int = 1;
const MMDB_IO_ERROR: c_int = 4;
const MMDB_OUT_OF_MEMORY_ERROR: c_int = 5;
const MMDB_INVALID_DATA_ERROR: c_int = 7;
const MMDB_INVALID_LOOKUP_PATH_ERROR: c_int = 8;
const MMDB_INVALID_NODE_NUMBER_ERROR: c_int = 10;
fn map_matchy_error(matchy_error: i32) -> c_int {
match matchy_error {
MATCHY_SUCCESS => MMDB_SUCCESS,
super::matchy::MATCHY_ERROR_FILE_NOT_FOUND => MMDB_FILE_OPEN_ERROR,
super::matchy::MATCHY_ERROR_IO => MMDB_IO_ERROR,
super::matchy::MATCHY_ERROR_OUT_OF_MEMORY => MMDB_OUT_OF_MEMORY_ERROR,
super::matchy::MATCHY_ERROR_LOOKUP_PATH_INVALID => MMDB_INVALID_LOOKUP_PATH_ERROR,
_ => MMDB_INVALID_DATA_ERROR,
}
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_open(
filename: *const c_char,
flags: u32,
mmdb: *mut MMDB_s,
) -> c_int {
if filename.is_null() || mmdb.is_null() {
return MMDB_FILE_OPEN_ERROR;
}
ptr::write_bytes(mmdb, 0, 1);
let filename_str = match CStr::from_ptr(filename).to_str() {
Ok(s) => s,
Err(_) => return MMDB_FILE_OPEN_ERROR,
};
let db = matchy_open(filename_str.as_ptr().cast::<c_char>());
if db.is_null() {
return MMDB_FILE_OPEN_ERROR;
}
let filename_copy = match CString::new(filename_str) {
Ok(s) => s.into_raw(),
Err(_) => {
matchy_close(db);
return MMDB_OUT_OF_MEMORY_ERROR;
}
};
(*mmdb)._matchy_db = db;
(*mmdb).flags = flags;
(*mmdb).filename = filename_copy;
(*mmdb).file_size = 0;
MMDB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_lookup_string(
mmdb: *const MMDB_s,
ipstr: *const c_char,
gai_error: *mut c_int,
mmdb_error: *mut c_int,
) -> MMDB_lookup_result_s {
let set_error = |gai: c_int, mmdb_err: c_int| {
if !gai_error.is_null() {
*gai_error = gai;
}
if !mmdb_error.is_null() {
*mmdb_error = mmdb_err;
}
MMDB_lookup_result_s {
found_entry: false,
entry: MMDB_entry_s {
mmdb: ptr::null(),
_matchy_entry: mem::zeroed(),
},
netmask: 0,
}
};
if mmdb.is_null() || ipstr.is_null() {
return set_error(0, MMDB_INVALID_DATA_ERROR);
}
let db = (*mmdb)._matchy_db;
if db.is_null() {
return set_error(0, MMDB_INVALID_DATA_ERROR);
}
let result = matchy_query(db, ipstr);
if !result.found {
return set_error(0, MMDB_SUCCESS);
}
let lookup_result = MMDB_lookup_result_s {
found_entry: true,
entry: MMDB_entry_s {
mmdb,
_matchy_entry: matchy_entry_s {
db,
_data_offset: result._data_offset,
},
},
netmask: u16::from(result.prefix_len),
};
if !gai_error.is_null() {
*gai_error = 0;
}
if !mmdb_error.is_null() {
*mmdb_error = MMDB_SUCCESS;
}
lookup_result
}
#[cfg(unix)]
#[no_mangle]
pub unsafe extern "C" fn MMDB_lookup_sockaddr(
mmdb: *const MMDB_s,
sockaddr: *const libc::sockaddr,
mmdb_error: *mut c_int,
) -> MMDB_lookup_result_s {
let set_error = |err: c_int| {
if !mmdb_error.is_null() {
*mmdb_error = err;
}
MMDB_lookup_result_s {
found_entry: false,
entry: MMDB_entry_s {
mmdb: ptr::null(),
_matchy_entry: mem::zeroed(),
},
netmask: 0,
}
};
if mmdb.is_null() || sockaddr.is_null() {
return set_error(MMDB_INVALID_DATA_ERROR);
}
let family = i32::from((*sockaddr).sa_family);
let ip_addr = match family {
libc::AF_INET => {
let sa = sockaddr.cast::<libc::sockaddr_in>();
let mut in_addr: libc::in_addr = mem::zeroed();
ptr::copy_nonoverlapping(
&(*sa).sin_addr as *const libc::in_addr,
&mut in_addr as *mut libc::in_addr,
1,
);
let addr = u32::from_be(in_addr.s_addr);
IpAddr::V4(Ipv4Addr::from(addr))
}
libc::AF_INET6 => {
let sa = sockaddr.cast::<libc::sockaddr_in6>();
let mut in6_addr: libc::in6_addr = mem::zeroed();
ptr::copy_nonoverlapping(
&(*sa).sin6_addr as *const libc::in6_addr,
&mut in6_addr as *mut libc::in6_addr,
1,
);
IpAddr::V6(Ipv6Addr::from(in6_addr.s6_addr))
}
_ => return set_error(MMDB_INVALID_DATA_ERROR),
};
let ip_str = ip_addr.to_string();
let ip_cstr = match CString::new(ip_str) {
Ok(s) => s,
Err(_) => return set_error(MMDB_OUT_OF_MEMORY_ERROR),
};
let mut gai_error = 0;
MMDB_lookup_string(mmdb, ip_cstr.as_ptr(), &mut gai_error, mmdb_error)
}
#[cfg(windows)]
#[no_mangle]
pub unsafe extern "C" fn MMDB_lookup_sockaddr(
mmdb: *const MMDB_s,
sockaddr: *const winapi::shared::ws2def::SOCKADDR,
mmdb_error: *mut c_int,
) -> MMDB_lookup_result_s {
let set_error = |err: c_int| {
if !mmdb_error.is_null() {
*mmdb_error = err;
}
MMDB_lookup_result_s {
found_entry: false,
entry: MMDB_entry_s {
mmdb: ptr::null(),
_matchy_entry: mem::zeroed(),
},
netmask: 0,
}
};
if mmdb.is_null() || sockaddr.is_null() {
return set_error(MMDB_INVALID_DATA_ERROR);
}
use winapi::shared::ws2def::{AF_INET, AF_INET6, SOCKADDR_IN};
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH;
let ip_addr = match (*sockaddr).sa_family as i32 {
AF_INET => {
let sa = sockaddr as *const SOCKADDR_IN;
let addr = u32::from_be(*(*sa).sin_addr.S_un.S_addr());
IpAddr::V4(Ipv4Addr::from(addr))
}
AF_INET6 => {
let sa = sockaddr as *const SOCKADDR_IN6_LH;
let addr = *(*sa).sin6_addr.u.Byte();
IpAddr::V6(Ipv6Addr::from(addr))
}
_ => return set_error(MMDB_INVALID_DATA_ERROR),
};
let ip_str = ip_addr.to_string();
let ip_cstr = match CString::new(ip_str) {
Ok(s) => s,
Err(_) => return set_error(MMDB_OUT_OF_MEMORY_ERROR),
};
let mut gai_error = 0;
MMDB_lookup_string(mmdb, ip_cstr.as_ptr(), &mut gai_error, mmdb_error)
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_aget_value(
start: *mut MMDB_entry_s,
entry_data: *mut MMDB_entry_data_s,
path: *const *const c_char,
) -> c_int {
if start.is_null() || entry_data.is_null() || path.is_null() {
return MMDB_INVALID_DATA_ERROR;
}
let matchy_entry = &(*start)._matchy_entry as *const _;
let status = matchy_aget_value(matchy_entry, entry_data, path);
map_matchy_error(status)
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_get_entry_data_list(
start: *mut MMDB_entry_s,
entry_data_list: *mut *mut MMDB_entry_data_list_s,
) -> c_int {
if start.is_null() || entry_data_list.is_null() {
return MMDB_INVALID_DATA_ERROR;
}
let matchy_entry = &(*start)._matchy_entry as *const _;
let matchy_list_ptr = entry_data_list.cast::<*mut matchy_entry_data_list_t>();
let status = matchy_get_entry_data_list(matchy_entry, matchy_list_ptr);
map_matchy_error(status)
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_free_entry_data_list(entry_data_list: *mut MMDB_entry_data_list_s) {
if entry_data_list.is_null() {
return;
}
let mut current = entry_data_list;
while !current.is_null() {
let next = (*current).next;
let _ = Box::from_raw(current);
current = next;
}
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_close(mmdb: *mut MMDB_s) {
if mmdb.is_null() {
return;
}
if !(*mmdb)._matchy_db.is_null() {
matchy_close((*mmdb)._matchy_db);
(*mmdb)._matchy_db = ptr::null_mut();
}
if !(*mmdb).filename.is_null() {
let _ = CString::from_raw((*mmdb).filename as *mut c_char);
(*mmdb).filename = ptr::null();
}
}
#[no_mangle]
pub extern "C" fn MMDB_lib_version() -> *const c_char {
concat!(env!("CARGO_PKG_VERSION"), "-compat\0")
.as_ptr()
.cast::<c_char>()
}
#[no_mangle]
pub extern "C" fn MMDB_strerror(error_code: c_int) -> *const c_char {
let msg = match error_code {
MMDB_SUCCESS => "Success\0",
MMDB_FILE_OPEN_ERROR => "Error opening database file\0",
MMDB_IO_ERROR => "IO error\0",
MMDB_OUT_OF_MEMORY_ERROR => "Out of memory\0",
MMDB_INVALID_DATA_ERROR => "Invalid or corrupt data\0",
MMDB_INVALID_LOOKUP_PATH_ERROR => "Invalid lookup path\0",
MMDB_INVALID_NODE_NUMBER_ERROR => "Invalid node number\0",
_ => "Unknown error\0",
};
msg.as_ptr().cast::<c_char>()
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_read_node(
_mmdb: *const MMDB_s,
_node_number: u32,
_node: *mut c_void,
) -> c_int {
MMDB_INVALID_NODE_NUMBER_ERROR
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_dump_entry_data_list(
_stream: *mut libc::FILE,
_entry_data_list: *const MMDB_entry_data_list_s,
_indent: c_int,
) -> c_int {
MMDB_INVALID_DATA_ERROR
}
#[no_mangle]
pub unsafe extern "C" fn MMDB_get_metadata_as_entry_data_list(
_mmdb: *const MMDB_s,
_entry_data_list: *mut *mut MMDB_entry_data_list_s,
) -> c_int {
MMDB_INVALID_DATA_ERROR
}