access lists: filter requests in socket workers instead

This commit is contained in:
Joakim Frostegård 2021-10-16 17:26:40 +02:00
parent 33966bed57
commit 7ccd5fcbf7
18 changed files with 221 additions and 163 deletions

View file

@ -115,18 +115,17 @@ pub type TorrentMap<I> = HashMap<InfoHash, TorrentData<I>>;
pub struct TorrentMaps {
pub ipv4: TorrentMap<Ipv4Addr>,
pub ipv6: TorrentMap<Ipv6Addr>,
pub access_list: AccessList,
}
impl TorrentMaps {
pub fn clean(&mut self, config: &Config) {
Self::clean_torrent_map(config, &self.access_list, &mut self.ipv4);
Self::clean_torrent_map(config, &self.access_list, &mut self.ipv6);
pub fn clean(&mut self, config: &Config, access_list: &Arc<AccessList>) {
Self::clean_torrent_map(config, access_list, &mut self.ipv4);
Self::clean_torrent_map(config, access_list, &mut self.ipv6);
}
fn clean_torrent_map<I: Ip>(
config: &Config,
access_list: &AccessList,
access_list: &Arc<AccessList>,
torrent_map: &mut TorrentMap<I>,
) {
let now = Instant::now();
@ -166,12 +165,14 @@ impl TorrentMaps {
#[derive(Clone)]
pub struct State {
pub access_list: Arc<AccessList>,
pub torrent_maps: Arc<Mutex<TorrentMaps>>,
}
impl Default for State {
fn default() -> Self {
Self {
access_list: Arc::new(Default::default()),
torrent_maps: Arc::new(Mutex::new(TorrentMaps::default())),
}
}

View file

@ -105,77 +105,69 @@ pub fn handle_announce_requests(
let valid_until = ValidUntil::new(config.cleaning.max_peer_age);
for (meta, request) in requests {
let info_hash_allowed = torrent_maps
.access_list
.allows(config.access_list.mode, &request.info_hash.0);
let peer_ip = convert_ipv4_mapped_ipv6(meta.peer_addr.ip());
let response = if info_hash_allowed {
let peer_ip = convert_ipv4_mapped_ipv6(meta.peer_addr.ip());
::log::debug!("peer ip: {:?}", peer_ip);
::log::debug!("peer ip: {:?}", peer_ip);
let response = match peer_ip {
IpAddr::V4(peer_ip_address) => {
let torrent_data: &mut TorrentData<Ipv4Addr> =
torrent_maps.ipv4.entry(request.info_hash).or_default();
match peer_ip {
IpAddr::V4(peer_ip_address) => {
let torrent_data: &mut TorrentData<Ipv4Addr> =
torrent_maps.ipv4.entry(request.info_hash).or_default();
let peer_connection_meta = PeerConnectionMeta {
worker_index: meta.worker_index,
poll_token: meta.poll_token,
peer_ip_address,
};
let peer_connection_meta = PeerConnectionMeta {
worker_index: meta.worker_index,
poll_token: meta.poll_token,
peer_ip_address,
};
let (seeders, leechers, response_peers) = upsert_peer_and_get_response_peers(
config,
rng,
peer_connection_meta,
torrent_data,
request,
valid_until,
);
let (seeders, leechers, response_peers) = upsert_peer_and_get_response_peers(
config,
rng,
peer_connection_meta,
torrent_data,
request,
valid_until,
);
let response = AnnounceResponse {
complete: seeders,
incomplete: leechers,
announce_interval: config.protocol.peer_announce_interval,
peers: ResponsePeerListV4(response_peers),
peers6: ResponsePeerListV6(vec![]),
};
let response = AnnounceResponse {
complete: seeders,
incomplete: leechers,
announce_interval: config.protocol.peer_announce_interval,
peers: ResponsePeerListV4(response_peers),
peers6: ResponsePeerListV6(vec![]),
};
Response::Announce(response)
}
IpAddr::V6(peer_ip_address) => {
let torrent_data: &mut TorrentData<Ipv6Addr> =
torrent_maps.ipv6.entry(request.info_hash).or_default();
let peer_connection_meta = PeerConnectionMeta {
worker_index: meta.worker_index,
poll_token: meta.poll_token,
peer_ip_address,
};
let (seeders, leechers, response_peers) = upsert_peer_and_get_response_peers(
config,
rng,
peer_connection_meta,
torrent_data,
request,
valid_until,
);
let response = AnnounceResponse {
complete: seeders,
incomplete: leechers,
announce_interval: config.protocol.peer_announce_interval,
peers: ResponsePeerListV4(vec![]),
peers6: ResponsePeerListV6(response_peers),
};
Response::Announce(response)
}
Response::Announce(response)
}
IpAddr::V6(peer_ip_address) => {
let torrent_data: &mut TorrentData<Ipv6Addr> =
torrent_maps.ipv6.entry(request.info_hash).or_default();
let peer_connection_meta = PeerConnectionMeta {
worker_index: meta.worker_index,
poll_token: meta.poll_token,
peer_ip_address,
};
let (seeders, leechers, response_peers) = upsert_peer_and_get_response_peers(
config,
rng,
peer_connection_meta,
torrent_data,
request,
valid_until,
);
let response = AnnounceResponse {
complete: seeders,
incomplete: leechers,
announce_interval: config.protocol.peer_announce_interval,
peers: ResponsePeerListV4(vec![]),
peers6: ResponsePeerListV6(response_peers),
};
Response::Announce(response)
}
} else {
Response::Failure(FailureResponse::new("Info hash not allowed"))
};
response_channel_sender.send(meta, response);

View file

@ -22,18 +22,16 @@ pub const APP_NAME: &str = "aquatic_http: HTTP/TLS BitTorrent tracker";
pub fn run(config: Config) -> anyhow::Result<()> {
let state = State::default();
tasks::update_access_list(&config, &mut state.torrent_maps.lock());
tasks::update_access_list(&config, &state);
start_workers(config.clone(), state.clone())?;
loop {
::std::thread::sleep(Duration::from_secs(config.cleaning.interval));
let mut torrent_maps = state.torrent_maps.lock();
tasks::update_access_list(&config, &state);
tasks::update_access_list(&config, &mut torrent_maps);
torrent_maps.clean(&config);
state.torrent_maps.lock().clean(&config, &state.access_list);
}
}
@ -57,6 +55,7 @@ pub fn start_workers(config: Config, state: State) -> anyhow::Result<()> {
for i in 0..config.socket_workers {
let config = config.clone();
let state = state.clone();
let socket_worker_statuses = socket_worker_statuses.clone();
let request_channel_sender = request_channel_sender.clone();
let opt_tls_acceptor = opt_tls_acceptor.clone();
@ -73,6 +72,7 @@ pub fn start_workers(config: Config, state: State) -> anyhow::Result<()> {
.spawn(move || {
network::run_socket_worker(
config,
state,
i,
socket_worker_statuses,
request_channel_sender,

View file

@ -3,6 +3,7 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use std::vec::Drain;
use aquatic_http_protocol::request::Request;
use hashbrown::HashMap;
use log::{debug, error, info};
use mio::net::TcpListener;
@ -25,6 +26,7 @@ const CONNECTION_CLEAN_INTERVAL: usize = 2 ^ 22;
pub fn run_socket_worker(
config: Config,
state: State,
socket_worker_index: usize,
socket_worker_statuses: SocketWorkerStatuses,
request_channel_sender: RequestChannelSender,
@ -38,6 +40,7 @@ pub fn run_socket_worker(
run_poll_loop(
config,
&state,
socket_worker_index,
request_channel_sender,
response_channel_receiver,
@ -55,6 +58,7 @@ pub fn run_socket_worker(
pub fn run_poll_loop(
config: Config,
state: &State,
socket_worker_index: usize,
request_channel_sender: RequestChannelSender,
response_channel_receiver: ResponseChannelReceiver,
@ -100,6 +104,7 @@ pub fn run_poll_loop(
} else if token != CHANNEL_TOKEN {
handle_connection_read_event(
&config,
&state,
socket_worker_index,
&mut poll,
&request_channel_sender,
@ -179,6 +184,7 @@ fn accept_new_streams(
/// then read requests and pass on through channel.
pub fn handle_connection_read_event(
config: &Config,
state: &State,
socket_worker_index: usize,
poll: &mut Poll,
request_channel_sender: &RequestChannelSender,
@ -187,6 +193,7 @@ pub fn handle_connection_read_event(
poll_token: Token,
) {
let valid_until = ValidUntil::new(config.cleaning.max_connection_age);
let access_list_mode = config.access_list.mode;
loop {
// Get connection, updating valid_until
@ -210,10 +217,26 @@ pub fn handle_connection_read_event(
peer_addr: established.peer_addr,
};
debug!("read request, sending to handler");
let opt_allowed_request = if let Request::Announce(ref r) = request {
if state.access_list.allows(access_list_mode, &r.info_hash.0){
Some(request)
} else {
None
}
} else {
Some(request)
};
if let Err(err) = request_channel_sender.send((meta, request)) {
error!("RequestChannelSender: couldn't send message: {:?}", err);
if let Some(request) = opt_allowed_request {
debug!("read allowed request, sending on to channel");
if let Err(err) = request_channel_sender.send((meta, request)) {
error!("RequestChannelSender: couldn't send message: {:?}", err);
}
} else {
let response = FailureResponse::new("Info hash not allowed");
local_responses.push((meta, Response::Failure(response)))
}
break;

View file

@ -4,10 +4,10 @@ use aquatic_common::access_list::AccessListMode;
use crate::{common::*, config::Config};
pub fn update_access_list(config: &Config, torrent_maps: &mut TorrentMaps) {
pub fn update_access_list(config: &Config, state: &State) {
match config.access_list.mode {
AccessListMode::Require | AccessListMode::Forbid => {
if let Err(err) = torrent_maps
if let Err(err) = state
.access_list
.update_from_path(&config.access_list.path)
{