-
-
Notifications
You must be signed in to change notification settings - Fork 24
Add a very basic support for multi-server handling. #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 3 commits
6e35676
b879047
4d90011
2a55c92
487c190
269db95
ce21d56
dbc0d98
023e46f
e54025f
02a8ba4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| use std::collections::HashMap; | ||
| use std::net::SocketAddr; | ||
| use std::sync::Arc; | ||
|
|
||
| use clap::ArgMatches; | ||
|
|
@@ -14,20 +15,25 @@ const RCON_PASSWORD_LENGTH: usize = 32; | |
|
|
||
| /// Start lazymc. | ||
| pub fn invoke(matches: &ArgMatches) -> Result<(), ()> { | ||
| // Parse bind address | ||
| let bind_addr: SocketAddr = matches.get_one::<String>("bind").unwrap().parse().unwrap(); | ||
|
|
||
| // Load config | ||
| #[allow(unused_mut)] | ||
| let mut config = config::load(matches); | ||
| let mut configs = config::load(matches); | ||
|
|
||
| // Prepare RCON if enabled | ||
| #[cfg(feature = "rcon")] | ||
| prepare_rcon(&mut config); | ||
| for config in configs.iter_mut() { | ||
| // Prepare RCON if enabled | ||
| #[cfg(feature = "rcon")] | ||
| prepare_rcon(config); | ||
|
|
||
| // Rewrite server server.properties file | ||
| rewrite_server_properties(&config); | ||
| // Rewrite server server.properties file | ||
| rewrite_server_properties(config); | ||
| } | ||
|
|
||
| // Start server service | ||
| let config = Arc::new(config); | ||
| service::server::service(config) | ||
| let configs_arc = configs.into_iter().map(Arc::new).collect(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think I used I'll have to think about something that would be better here. I'm not sure right now. Maybe we need to create a custom type to manage these configurations for us rather than working with a hashmap directly.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a new type |
||
| service::server::service(bind_addr, configs_arc) | ||
| } | ||
|
|
||
| /// Prepare RCON. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| use std::collections::HashMap; | ||
| use std::net::SocketAddr; | ||
| use std::sync::Arc; | ||
|
|
||
|
|
@@ -8,7 +9,7 @@ use tokio::net::{TcpListener, TcpStream}; | |
| use crate::config::Config; | ||
| use crate::proto::client::Client; | ||
| use crate::proxy::{self, ProxyHeader}; | ||
| use crate::server::{self, Server}; | ||
| use crate::server::Server; | ||
| use crate::service; | ||
| use crate::status; | ||
| use crate::util::error::{quit_error, ErrorHints}; | ||
|
|
@@ -19,60 +20,68 @@ use crate::util::error::{quit_error, ErrorHints}; | |
| /// | ||
| /// Spawns a tokio runtime to complete all work on. | ||
| #[tokio::main(flavor = "multi_thread")] | ||
| pub async fn service(config: Arc<Config>) -> Result<(), ()> { | ||
| // Load server state | ||
| let server = Arc::new(Server::default()); | ||
|
|
||
| pub async fn service(bind_addr: SocketAddr, configs: Vec<Arc<Config>>) -> Result<(), ()> { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't inspected this in-depth right now. I'll try to do that later when the other changes have been made. |
||
| // Listen for new connections | ||
| let listener = TcpListener::bind(config.public.address) | ||
| .await | ||
| .map_err(|err| { | ||
| quit_error( | ||
| anyhow!(err).context("Failed to start proxy server"), | ||
| ErrorHints::default(), | ||
| ); | ||
| })?; | ||
| let listener = TcpListener::bind(bind_addr).await.map_err(|err| { | ||
| quit_error( | ||
| anyhow!(err).context("Failed to start proxy server"), | ||
| ErrorHints::default(), | ||
| ); | ||
| })?; | ||
|
|
||
| info!( | ||
| target: "lazymc", | ||
| "Proxying public {} to server {}", | ||
| config.public.address, config.server.address, | ||
| ); | ||
| let mut servers = HashMap::new(); | ||
|
|
||
| if config.lockout.enabled { | ||
| warn!( | ||
| for config in configs.iter() { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably extract this (the logic for doing something for many configuration files) into a separate function we can invoke for each configuration. This is now a bit messy (and I admit my code already was). |
||
| // Load server state | ||
| let server = Arc::new(Server::default()); | ||
| servers.insert( | ||
| config.public.address.clone(), | ||
| (config.clone(), server.clone()), | ||
| ); | ||
|
|
||
| info!( | ||
| target: "lazymc", | ||
| "Lockout mode is enabled, nobody will be able to connect through the proxy", | ||
| "Proxying public {} to server {}", | ||
| config.public.address, config.server.address, | ||
| ); | ||
| } | ||
|
|
||
| // Spawn services: monitor, signal handler | ||
| tokio::spawn(service::monitor::service(config.clone(), server.clone())); | ||
| tokio::spawn(service::signal::service(config.clone(), server.clone())); | ||
| if config.lockout.enabled { | ||
| warn!( | ||
| target: "lazymc", | ||
| "Lockout mode is enabled, nobody will be able to connect through the proxy", | ||
| ); | ||
| } | ||
|
|
||
| // Spawn services: monitor, signal handler | ||
| tokio::spawn(service::monitor::service(config.clone(), server.clone())); | ||
| tokio::spawn(service::signal::service(config.clone(), server.clone())); | ||
|
|
||
| // Initiate server start | ||
| if config.server.wake_on_start { | ||
| Server::start(config.clone(), server.clone(), None).await; | ||
| } | ||
|
|
||
| // Initiate server start | ||
| if config.server.wake_on_start { | ||
| Server::start(config.clone(), server.clone(), None).await; | ||
| // Spawn additional services: probe and ban manager | ||
| tokio::spawn(service::probe::service(config.clone(), server.clone())); | ||
| tokio::task::spawn_blocking({ | ||
| let (config, server) = (config.clone(), server.clone()); | ||
| || service::file_watcher::service(config, server) | ||
| }); | ||
| } | ||
|
|
||
| // Spawn additional services: probe and ban manager | ||
| tokio::spawn(service::probe::service(config.clone(), server.clone())); | ||
| tokio::task::spawn_blocking({ | ||
| let (config, server) = (config.clone(), server.clone()); | ||
| || service::file_watcher::service(config, server) | ||
| }); | ||
| let servers_arc = Arc::new(servers); | ||
|
tW4r marked this conversation as resolved.
Outdated
|
||
|
|
||
| // Route all incomming connections | ||
| while let Ok((inbound, _)) = listener.accept().await { | ||
| route(inbound, config.clone(), server.clone()); | ||
| route(inbound, servers_arc.clone()); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Route inbound TCP stream to correct service, spawning a new task. | ||
| #[inline] | ||
| fn route(inbound: TcpStream, config: Arc<Config>, server: Arc<Server>) { | ||
| fn route(inbound: TcpStream, servers: Arc<HashMap<String, (Arc<Config>, Arc<Server>)>>) { | ||
| // Get user peer address | ||
| let peer = match inbound.peer_addr() { | ||
| Ok(peer) => peer, | ||
|
|
@@ -82,29 +91,8 @@ fn route(inbound: TcpStream, config: Arc<Config>, server: Arc<Server>) { | |
| } | ||
| }; | ||
|
|
||
| // Check ban state, just drop connection if enabled | ||
| let banned = server.is_banned_ip_blocking(&peer.ip()); | ||
| if config.server.drop_banned_ips { | ||
| info!(target: "lazymc", "Connection from banned IP {}, dropping", peer.ip()); | ||
| return; | ||
| } | ||
|
|
||
| // Route connection through proper channel | ||
| let should_proxy = | ||
| !banned && server.state() == server::State::Started && !config.lockout.enabled; | ||
| if should_proxy { | ||
| route_proxy(inbound, config) | ||
| } else { | ||
| route_status(inbound, config, server, peer) | ||
| } | ||
| } | ||
|
|
||
| /// Route inbound TCP stream to status server, spawning a new task. | ||
| #[inline] | ||
| fn route_status(inbound: TcpStream, config: Arc<Config>, server: Arc<Server>, peer: SocketAddr) { | ||
| // When server is not online, spawn a status server | ||
| let client = Client::new(peer); | ||
| let service = status::serve(client, inbound, config, server).map(|r| { | ||
| let service = status::serve(client, inbound, servers).map(|r| { | ||
| if let Err(err) = r { | ||
| warn!(target: "lazymc", "Failed to serve status: {:?}", err); | ||
| } | ||
|
|
@@ -113,24 +101,6 @@ fn route_status(inbound: TcpStream, config: Arc<Config>, server: Arc<Server>, pe | |
| tokio::spawn(service); | ||
| } | ||
|
|
||
| /// Route inbound TCP stream to proxy, spawning a new task. | ||
| #[inline] | ||
| fn route_proxy(inbound: TcpStream, config: Arc<Config>) { | ||
|
timvisee marked this conversation as resolved.
|
||
| // When server is online, proxy all | ||
| let service = proxy::proxy( | ||
| inbound, | ||
| ProxyHeader::Proxy.not_none(config.server.send_proxy_v2), | ||
| config.server.address, | ||
| ) | ||
| .map(|r| { | ||
| if let Err(err) = r { | ||
| warn!(target: "lazymc", "Failed to proxy: {}", err); | ||
| } | ||
| }); | ||
|
|
||
| tokio::spawn(service); | ||
| } | ||
|
|
||
| /// Route inbound TCP stream to proxy with queued data, spawning a new task. | ||
| #[inline] | ||
| pub fn route_proxy_queue(inbound: TcpStream, config: Arc<Config>, queue: BytesMut) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.