initial commit

This commit is contained in:
MedzikUser 2022-06-04 13:58:21 +02:00
commit a91b0cb788
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
11 changed files with 606 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

262
Cargo.lock generated Normal file
View File

@ -0,0 +1,262 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
[[package]]
name = "async-trait"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libloading"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mio"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "proc-macro2"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "servers"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"libloading",
"log",
"tokio",
]
[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f392c8f16bda3456c0b00c6de39cb100449b98de55ac41c6cdd2bfcf53a1245"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"pin-project-lite",
"socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "servers"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.57"
async-trait = "0.1.56"
libloading = "0.7.3"
log = "0.4.17"
tokio = { version = "1.19.0", features = ["rt-multi-thread", "macros", "net", "io-std", "io-util"] }

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Medzik
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

34
src/client.rs Normal file
View File

@ -0,0 +1,34 @@
#![allow(clippy::unused_io_amount)]
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
};
pub struct Client {
pub stream: TcpStream,
}
impl Client {
/// Create new Client
pub fn new(stream: TcpStream) -> Self {
Self { stream }
}
/// Read message/buffer from Client
pub async fn read(&mut self) -> anyhow::Result<String> {
let mut buf = [0; 1024];
self.stream.read(&mut buf).await?;
let encoded = String::from_utf8(buf.to_vec())?.replace('\0', "");
Ok(encoded)
}
/// Send message to Client
pub async fn send(&mut self, content: &str) -> anyhow::Result<()> {
self.stream.write_all(format!("{content}\n\r").as_bytes()).await?;
Ok(())
}
}

57
src/command_handler.rs Normal file
View File

@ -0,0 +1,57 @@
use std::{any::Any, sync::Arc};
use async_trait::async_trait;
use crate::{Client, commands};
#[async_trait]
pub trait Command: Any + Send + Sync {
/// Command name
fn name(&self) -> &'static str;
/// Command help message
fn help(&self) -> &'static str;
/// Command function
async fn execute(
&self,
client: &mut Client,
args: Vec<&str>,
plugins: &CommandManagerType,
);
}
pub struct CommandManager {
/// Vector with plugins
pub commands: Vec<Box<dyn Command>>,
}
impl CommandManager {
pub fn new() -> Self {
Self {
commands: Vec::new(),
}
}
}
impl Default for CommandManager {
fn default() -> Self {
Self::new()
}
}
pub type CommandManagerType = Vec<Arc<Box<dyn Command>>>;
pub fn register_commands() -> CommandManagerType {
let mut command_manager = CommandManager::new();
for command in commands::register_commands() {
command_manager.commands.push(command)
}
// create Arc in Vector
let mut commands: CommandManagerType = Vec::new();
for command in command_manager.commands {
commands.push(Arc::new(command))
}
commands
}

30
src/commands/help.rs Normal file
View File

@ -0,0 +1,30 @@
use async_trait::async_trait;
use crate::{command_handler::Command, CommandManagerType};
pub struct CommandHelp;
#[async_trait]
impl Command for CommandHelp {
fn name(&self) -> &'static str {
"/help"
}
fn help(&self) -> &'static str {
"show help"
}
async fn execute(
&self,
client: &mut crate::Client,
_args: Vec<&str>,
commands: &CommandManagerType,
) {
for command in commands {
client
.send(&format!("{} - {}", command.name(), command.help()))
.await
.expect("send message");
}
}
}

13
src/commands/mod.rs Normal file
View File

@ -0,0 +1,13 @@
mod help;
pub use help::*;
use crate::command_handler::Command;
pub fn register_commands() -> Vec<Box<dyn Command>> {
let mut commands: Vec<Box<dyn Command>> = Vec::new();
commands.push(Box::new(CommandHelp));
commands
}

8
src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
mod client;
mod command_handler;
mod commands;
mod plugin_loader;
pub use client::*;
pub use command_handler::*;
pub use plugin_loader::*;

64
src/main.rs Normal file
View File

@ -0,0 +1,64 @@
use servers::{loader, Client, PluginManagerType, register_commands, CommandManagerType};
use tokio::{io::AsyncWriteExt, net::TcpListener};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// listen Tcp server
let listener = TcpListener::bind("0.0.0.0:9999").await?;
// load plugins
let plugin_manager = loader()?;
// load command
let commands_manager = register_commands();
// Accepts a new incoming connection from this listener.
while let Ok((stream, _address)) = listener.accept().await {
let client = Client::new(stream);
// handle client connection in new thread
tokio::spawn(handle_connection(client, (*plugin_manager).to_vec(), (*commands_manager).to_vec()));
}
Ok(())
}
async fn handle_connection(mut client: Client, plugins: PluginManagerType, commands: CommandManagerType) -> anyhow::Result<()> {
println!("New Client: {:?}", client.stream.peer_addr()?);
loop {
// read client message/buffer
let buf = client.read().await?;
// split message by whitespace
let args: &Vec<&str> = &buf.split_ascii_whitespace().collect();
// get command from args
let cmd = args[0];
println!("{:?}", &args);
for command in &commands {
if cmd == command.name() {
println!("s");
command.execute(&mut client, args[1..args.len()].to_vec(), &commands).await;
break
}
}
// search command in plugins
for plugin in &plugins {
// if command found execute plugin
if cmd == plugin.command() {
plugin.execute(&mut client, args[1..args.len()].to_vec())
}
}
// if an I/O or EOF error, abort the connection
if client.stream.flush().await.is_err() {
break;
}
}
Ok(())
}

105
src/plugin_loader.rs Normal file
View File

@ -0,0 +1,105 @@
use std::{any::Any, fs, sync::Arc};
use libloading::{Library, Symbol};
use log::{debug, trace};
use crate::Client;
/// A plugin which allows you to add extra functionality.
pub trait Plugin: Any + Send + Sync {
/// Get a name describing the `Plugin`.
fn name(&self) -> &'static str;
/// A function that runs immediately after plugin loading.
/// Usually used for initialization.
fn on_plugin_load(&self);
/// A function that runs immediately before the plugin is unloaded.
/// Use this if you want to do any cleanup.
fn on_plugin_unload(&self);
/// Plugin command.
/// For example: `/command`
fn command(&self) -> &'static str;
/// Help message of this command.
fn help(&self) -> &'static str;
/// The function will be executed, when sending plugin command.
fn execute(&self, client: &mut Client, args: Vec<&str>);
}
pub struct PluginManager {
/// Vector with loaded plugins
pub plugins: Vec<Box<dyn Plugin>>,
}
impl PluginManager {
/// Create empty `PluginManager`
pub fn new() -> Self {
Self {
plugins: Vec::new(),
}
}
/// Unload all plugins and loaded plugin libraries, making sure to fire
/// their `on_plugin_unload()` methods so they can do any necessary cleanup.
pub fn unload(&mut self) {
debug!("Unloading plugins");
for plugin in self.plugins.drain(..) {
trace!("Firing on_plugin_unload for {:?}", plugin.name());
plugin.on_plugin_unload();
}
}
}
impl Default for PluginManager {
fn default() -> Self {
Self::new()
}
}
pub trait PluginRegistrar {
fn register_plugin(&mut self, plugin: Box<dyn Plugin>);
}
impl PluginRegistrar for PluginManager {
fn register_plugin(&mut self, plugin: Box<dyn Plugin>) {
self.plugins.push(plugin)
}
}
pub type PluginManagerType = Vec<Arc<Box<dyn Plugin>>>;
pub fn loader() -> anyhow::Result<PluginManagerType> {
// get path to .so lib from command argument
let config_dir = "./plugins";
let paths = fs::read_dir(config_dir)?;
// create a plugin manager where all loaded plugins will be located
let mut plugin_manager = PluginManager::new();
// for all plugin in directory
for path in paths {
// get library file path
let path = path?.path();
// loading library with .so is unsafe
unsafe {
// load library
// Box::new and Box::leak must be there because if it isn't there it throws a segmentation fault
let lib = Box::leak(Box::new(Library::new(path)?));
// get `plugin_entry` from library
let func: Symbol<unsafe extern "C" fn(&mut dyn PluginRegistrar) -> ()> =
lib.get(b"plugin_entry")?;
// execute initial function
func(&mut plugin_manager);
}
}
// create Arc in Vector
let mut plugins: PluginManagerType = Vec::new();
for plugin in plugin_manager.plugins {
plugins.push(Arc::new(plugin))
}
Ok(plugins)
}