add: ban ipv6 addresses by prefix
assumes all ipv6 addresses have 64-bit prefix (8 bytes at the start + 2 bytes for colons)
This commit is contained in:
parent
2b91422d18
commit
d7e800fcb4
6 changed files with 128 additions and 4 deletions
|
@ -1,5 +1,6 @@
|
|||
use super::*;
|
||||
use crate::cache::Cache;
|
||||
use crate::model::addr::RemoteAddr;
|
||||
use crate::model::moderation::AuditLogEntry;
|
||||
use crate::model::{Error, Result, auth::IpBan, auth::User, permissions::FinePermission};
|
||||
use crate::{auto_method, execute, get, query_row, query_rows, params};
|
||||
|
@ -26,6 +27,30 @@ impl DataManager {
|
|||
|
||||
auto_method!(get_ipban_by_ip(&str)@get_ipban_from_row -> "SELECT * FROM ipbans WHERE ip = $1" --name="ip ban" --returns=IpBan --cache-key-tmpl="atto.ipban:{}");
|
||||
|
||||
/// Get an IP ban as a [`RemoteAddr`].
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `prefix`
|
||||
pub async fn get_ipban_by_addr(&self, addr: RemoteAddr) -> Result<IpBan> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(
|
||||
&conn,
|
||||
"SELECT * FROM ipbans WHERE ip LIKE $1",
|
||||
&[&format!("{}%", addr.prefix(None))],
|
||||
|x| { Ok(Self::get_ipban_from_row(x)) }
|
||||
);
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound("ip ban".to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Get all IP bans (paginated).
|
||||
///
|
||||
/// # Arguments
|
||||
|
|
61
crates/core/src/model/addr.rs
Normal file
61
crates/core/src/model/addr.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use std::net::SocketAddr;
|
||||
|
||||
/// How many bytes should be chopped off the end of an IPV6 address to get its prefix.
|
||||
pub(crate) const IPV6_PREFIX_BYTES: usize = 8;
|
||||
|
||||
/// The protocol of a [`RemoteAddr`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum AddrProto {
|
||||
IPV4,
|
||||
IPV6,
|
||||
}
|
||||
|
||||
/// A representation of a remote IP address.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteAddr(pub SocketAddr, pub AddrProto);
|
||||
|
||||
impl From<&str> for RemoteAddr {
|
||||
fn from(value: &str) -> Self {
|
||||
if value.len() >= 16 {
|
||||
// ipv6 (16 bytes; 128 bits)
|
||||
if !(value.starts_with("[") | value.contains("]:1000")) {
|
||||
Self(format!("[{value}]:1000").parse().unwrap(), AddrProto::IPV6)
|
||||
} else {
|
||||
Self(value.parse().unwrap(), AddrProto::IPV6)
|
||||
}
|
||||
} else {
|
||||
// ipv4 (4 bytes; 32 bits)
|
||||
Self(value.parse().unwrap(), AddrProto::IPV4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteAddr {
|
||||
/// Get the address' prefix (returns the entire address for IPV4 addresses).
|
||||
///
|
||||
/// Operates on the IP address **without** colon characters.
|
||||
pub fn prefix(&self, chop: Option<usize>) -> String {
|
||||
if self.1 == AddrProto::IPV4 {
|
||||
return self.0.to_string();
|
||||
}
|
||||
|
||||
// we're adding 2 bytes to the chop bytes because we also
|
||||
// need to remove 2 colons
|
||||
let as_string = self.ipv6_inner();
|
||||
(&as_string[0..(match chop {
|
||||
Some(c) => c,
|
||||
None => IPV6_PREFIX_BYTES,
|
||||
} + (IPV6_PREFIX_BYTES / 4))])
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// [`Self::prefix`], but it returns another [`RemoteAddr`].
|
||||
pub fn to_prefix(self, chop: Option<usize>) -> Self {
|
||||
Self::from(self.prefix(chop).as_str())
|
||||
}
|
||||
|
||||
/// Get the innter content from the address (as ipv6).
|
||||
pub fn ipv6_inner(&self) -> String {
|
||||
self.0.to_string().replace("[", "").replace("]:1000", "")
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod addr;
|
||||
pub mod auth;
|
||||
pub mod communities;
|
||||
pub mod communities_permissions;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue