veloren_server/sys/msg/
network_events.rs

1use crate::{EditableSettings, client::Client, settings::banlist::NormalizedIpAddr};
2use common::{
3    comp::Player,
4    event::{ClientDisconnectEvent, EventBus},
5};
6use common_ecs::{Job, Origin, Phase, System};
7use common_net::msg::{DisconnectReason, ServerGeneral};
8use network::ParticipantEvent;
9use specs::{Entities, Join, Read, ReadExpect, ReadStorage, WriteStorage};
10
11/// This system consumes events from the `Participant::try_fetch_event`. These
12/// currently indicate channels being created and destroyed which potentially
13/// corresponds to the client using new addresses.
14///
15/// New addresses are checked against the existing IP bans. If a match is found
16/// that client will be kicked. Otherwise, the IP is added to the set of IPs
17/// that client has used. When a new IP ban is created, the set of IP addrs used
18/// by each client is scanned and any clients with matches are kicked.
19///
20/// We could retain addresses of removed channels and use them when banning but
21/// that would use a potentially unknown amount of memory (so they are removed).
22#[derive(Default)]
23pub struct Sys;
24impl<'a> System<'a> for Sys {
25    type SystemData = (
26        Entities<'a>,
27        ReadStorage<'a, Player>,
28        WriteStorage<'a, Client>,
29        Read<'a, EventBus<ClientDisconnectEvent>>,
30        ReadExpect<'a, EditableSettings>,
31    );
32
33    const NAME: &'static str = "msg::network_events";
34    const ORIGIN: Origin = Origin::Server;
35    const PHASE: Phase = Phase::Create;
36
37    fn run(
38        _job: &mut Job<Self>,
39        (entities, players, mut clients, client_disconnect_event_bus, editable_settings): Self::SystemData,
40    ) {
41        let now = chrono::Utc::now();
42        let mut client_disconnect_emitter = client_disconnect_event_bus.emitter();
43
44        for (entity, client) in (&entities, &mut clients).join() {
45            while let Some(Ok(Some(event))) = client
46                .participant
47                .as_mut()
48                .map(|participant| participant.try_fetch_event())
49            {
50                match event {
51                    ParticipantEvent::ChannelCreated(connect_addr) => {
52                        // Ignore mpsc connections
53                        if let Some(addr) = connect_addr.socket_addr() {
54                            client.current_ip_addrs.push(addr);
55
56                            let banned = editable_settings
57                                .banlist
58                                .ip_bans()
59                                .get(&NormalizedIpAddr::from(addr.ip()))
60                                .and_then(|ban_entry| ban_entry.current.action.ban())
61                                .and_then(|ban| {
62                                    // Hardcoded admins can always log in.
63                                    let admin = players.get(entity).and_then(|player| {
64                                        editable_settings.admins.get(&player.uuid())
65                                    });
66                                    crate::login_provider::ban_applies(ban, admin, now)
67                                        .then(|| ban.info())
68                                });
69
70                            if let Some(ban_info) = banned {
71                                // Kick client
72                                client_disconnect_emitter.emit(ClientDisconnectEvent(
73                                    entity,
74                                    common::comp::DisconnectReason::Kicked,
75                                ));
76                                let _ = client.send(ServerGeneral::Disconnect(
77                                    DisconnectReason::Banned(ban_info),
78                                ));
79                            }
80                        }
81                    },
82                    ParticipantEvent::ChannelDeleted(connect_addr) => {
83                        // Ignore mpsc connections
84                        if let Some(addr) = connect_addr.socket_addr() {
85                            if let Some(i) = client
86                                .current_ip_addrs
87                                .iter()
88                                .rev()
89                                .position(|a| *a == addr)
90                            {
91                                client.current_ip_addrs.remove(i);
92                            } else {
93                                tracing::error!(
94                                    "Channel deleted but its address isn't present in \
95                                     client.current_ip_addrs!"
96                                );
97                            }
98                        }
99                    },
100                }
101            }
102        }
103    }
104}