mirror of
https://github.com/MeexReay/bRAC.git
synced 2025-05-06 13:38:04 +03:00
socks5 proxy and some sugoma-specified stuff
This commit is contained in:
parent
20d424b8d5
commit
998d8025f8
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -103,6 +103,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_default",
|
||||
"serde_yml",
|
||||
"socks",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1173,6 +1174,17 @@ version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socks"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@ -1360,6 +1372,28 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||
|
||||
[[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"
|
||||
version = "0.57.0"
|
||||
|
@ -15,3 +15,4 @@ serde = { version = "1.0.219", features = ["serde_derive"] }
|
||||
gtk4 = { version = "0.9.6", features = [ "v4_10" ] }
|
||||
chrono = "0.4.40"
|
||||
serde_default = "0.2.0"
|
||||
socks = "0.3.4"
|
@ -26,6 +26,7 @@ pub struct Config {
|
||||
#[serde(default = "default_true")] pub chunked_enabled: bool,
|
||||
#[serde(default = "default_true")] pub formatting_enabled: bool,
|
||||
#[serde(default = "default_true")] pub commands_enabled: bool,
|
||||
#[serde(default)] pub proxy: Option<String>,
|
||||
}
|
||||
|
||||
pub fn get_config_path() -> PathBuf {
|
||||
@ -113,12 +114,14 @@ pub struct Args {
|
||||
#[arg(long)] pub chunked_enabled: Option<bool>,
|
||||
#[arg(long)] pub formatting_enabled: Option<bool>,
|
||||
#[arg(long)] pub commands_enabled: Option<bool>,
|
||||
#[arg(long)] pub proxy: Option<String>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn patch_config(&self, config: &mut Config) {
|
||||
if let Some(v) = self.host.clone() { config.host = v }
|
||||
if let Some(v) = self.name.clone() { config.name = Some(v) }
|
||||
if let Some(v) = self.proxy.clone() { config.proxy = Some(v) }
|
||||
if let Some(v) = self.message_format.clone() { config.message_format = v }
|
||||
if let Some(v) = self.update_time.clone() { config.update_time = v }
|
||||
if let Some(v) = self.max_messages.clone() { config.max_messages = v }
|
||||
|
@ -73,5 +73,11 @@ impl Context {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! connect_rac {
|
||||
($ctx:ident) => { &mut connect(&$ctx.config(|o| o.host.clone()), $ctx.config(|o| o.ssl_enabled))? };
|
||||
($ctx:ident) => {
|
||||
&mut connect(
|
||||
&$ctx.config(|o| o.host.clone()),
|
||||
$ctx.config(|o| o.ssl_enabled),
|
||||
$ctx.config(|o| o.proxy.clone())
|
||||
)?
|
||||
};
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use std::{sync::{mpsc::{channel, Receiver}, Arc}, time::UNIX_EPOCH};
|
||||
use std::sync::{mpsc::{channel, Receiver}, Arc};
|
||||
use std::cell::RefCell;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::thread;
|
||||
@ -28,7 +28,7 @@ use gtk::{
|
||||
};
|
||||
|
||||
use super::{config::{default_max_messages, default_update_time, get_config_path, save_config, Config},
|
||||
ctx::Context, on_send_message, parse_message, print_message, recv_tick};
|
||||
ctx::Context, on_send_message, parse_message, print_message, recv_tick, sanitize_message};
|
||||
|
||||
struct UiModel {
|
||||
chat_box: GtkBox,
|
||||
@ -104,6 +104,20 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
||||
|
||||
vbox.append(&message_format_hbox);
|
||||
|
||||
let proxy_hbox = GtkBox::new(Orientation::Horizontal, 5);
|
||||
|
||||
proxy_hbox.append(&Label::builder()
|
||||
.label("Socks5 Proxy")
|
||||
.build());
|
||||
|
||||
let proxy_entry = Entry::builder()
|
||||
.text(&ctx.config(|o| o.proxy.clone()).unwrap_or_default())
|
||||
.build();
|
||||
|
||||
proxy_hbox.append(&proxy_entry);
|
||||
|
||||
vbox.append(&proxy_hbox);
|
||||
|
||||
let update_time_hbox = GtkBox::new(Orientation::Horizontal, 5);
|
||||
|
||||
update_time_hbox.append(&Label::builder()
|
||||
@ -264,6 +278,7 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
||||
#[weak] chunked_enabled_entry,
|
||||
#[weak] formatting_enabled_entry,
|
||||
#[weak] commands_enabled_entry,
|
||||
#[weak] proxy_entry,
|
||||
move |_| {
|
||||
let config = Config {
|
||||
host: host_entry.text().to_string(),
|
||||
@ -305,7 +320,16 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
||||
ssl_enabled: ssl_enabled_entry.is_active(),
|
||||
chunked_enabled: chunked_enabled_entry.is_active(),
|
||||
formatting_enabled: formatting_enabled_entry.is_active(),
|
||||
commands_enabled: commands_enabled_entry.is_active()
|
||||
commands_enabled: commands_enabled_entry.is_active(),
|
||||
proxy: {
|
||||
let proxy = proxy_entry.text().to_string();
|
||||
|
||||
if proxy.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(proxy)
|
||||
}
|
||||
}
|
||||
};
|
||||
ctx.set_config(&config);
|
||||
save_config(get_config_path(), &config);
|
||||
@ -332,12 +356,14 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
||||
#[weak] chunked_enabled_entry,
|
||||
#[weak] formatting_enabled_entry,
|
||||
#[weak] commands_enabled_entry,
|
||||
#[weak] proxy_entry,
|
||||
move |_| {
|
||||
let config = Config::default();
|
||||
ctx.set_config(&config);
|
||||
save_config(get_config_path(), &config);
|
||||
host_entry.set_text(&config.host);
|
||||
name_entry.set_text(&config.name.unwrap_or_default());
|
||||
proxy_entry.set_text(&config.proxy.unwrap_or_default());
|
||||
message_format_entry.set_text(&config.message_format);
|
||||
update_time_entry.set_text(&config.update_time.to_string());
|
||||
max_messages_entry.set_text(&config.max_messages.to_string());
|
||||
@ -718,6 +744,8 @@ fn load_css() {
|
||||
}
|
||||
|
||||
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
||||
let Some(message) = sanitize_message(message) else { return; };
|
||||
|
||||
if message.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ pub fn on_command(ctx: Arc<Context>, command: &str) -> Result<(), Box<dyn Error>
|
||||
return Ok(())
|
||||
};
|
||||
|
||||
match register_user(connect_rac!(ctx), &ctx.name(), pass) {
|
||||
match register_user(connect_rac!(ctx), &ctx.name(), pass, !ctx.config(|o| o.ssl_enabled)) {
|
||||
Ok(true) => {
|
||||
add_message(ctx.clone(), "you was registered successfully bro")?;
|
||||
*ctx.registered.write().unwrap() = Some(pass.to_string());
|
||||
@ -209,9 +209,9 @@ pub fn on_send_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn E
|
||||
);
|
||||
|
||||
if let Some(password) = ctx.registered.read().unwrap().clone() {
|
||||
send_message_auth(connect_rac!(ctx), &ctx.name(), &password, &message)?;
|
||||
send_message_auth(connect_rac!(ctx), &ctx.name(), &password, &message, !ctx.config(|o| o.ssl_enabled))?;
|
||||
} else if ctx.config(|o| o.auth_enabled) {
|
||||
send_message_spoof_auth(connect_rac!(ctx), &message)?;
|
||||
send_message_spoof_auth(connect_rac!(ctx), &message, !ctx.config(|o| o.ssl_enabled))?;
|
||||
} else {
|
||||
send_message(connect_rac!(ctx), &message)?;
|
||||
}
|
||||
@ -220,17 +220,16 @@ pub fn on_send_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn E
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// message -> (date, ip, text, (name, color))
|
||||
pub fn parse_message(message: String) -> Option<(String, Option<String>, String, Option<(String, String)>)> {
|
||||
pub fn sanitize_message(message: String) -> Option<String> {
|
||||
let message = sanitize_text(&message);
|
||||
|
||||
let message = message
|
||||
.trim_start_matches("(UNREGISTERED)")
|
||||
.trim_start_matches("(UNAUTHORIZED)")
|
||||
.trim_start_matches("(UNAUTHENTICATED)")
|
||||
.trim()
|
||||
.to_string()+" ";
|
||||
let message = message.trim().to_string();
|
||||
|
||||
Some(message)
|
||||
}
|
||||
|
||||
/// message -> (date, ip, text, (name, color))
|
||||
pub fn parse_message(message: String) -> Option<(String, Option<String>, String, Option<(String, String)>)> {
|
||||
if message.is_empty() {
|
||||
return None
|
||||
}
|
||||
@ -241,6 +240,13 @@ pub fn parse_message(message: String) -> Option<(String, Option<String>, String,
|
||||
date.get(2)?.as_str().to_string(),
|
||||
);
|
||||
|
||||
let message = message
|
||||
.trim_start_matches("(UNREGISTERED)")
|
||||
.trim_start_matches("(UNAUTHORIZED)")
|
||||
.trim_start_matches("(UNAUTHENTICATED)")
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
let (ip, message) = if let Some(message) = IP_REGEX.captures(&message) {
|
||||
(Some(message.get(1)?.as_str().to_string()), message.get(2)?.as_str().to_string())
|
||||
} else {
|
||||
|
@ -18,7 +18,7 @@ fn main() {
|
||||
let mut config = load_config(config_path);
|
||||
|
||||
if args.read_messages {
|
||||
let mut stream = connect(&config.host, config.ssl_enabled).expect("Error reading message");
|
||||
let mut stream = connect(&config.host, config.ssl_enabled, config.proxy.clone()).expect("Error reading message");
|
||||
|
||||
print!("{}", read_messages(
|
||||
&mut stream,
|
||||
@ -33,7 +33,7 @@ fn main() {
|
||||
}
|
||||
|
||||
if let Some(message) = &args.send_message {
|
||||
let mut stream = connect(&config.host, config.ssl_enabled).expect("Error sending message");
|
||||
let mut stream = connect(&config.host, config.ssl_enabled, config.proxy.clone()).expect("Error sending message");
|
||||
|
||||
send_message(
|
||||
&mut stream,
|
||||
|
151
src/proto.rs
151
src/proto.rs
@ -1,41 +1,82 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use std::{error::Error, fmt::Debug, io::{Read, Write}, net::{SocketAddr, TcpStream, ToSocketAddrs}, str::FromStr, time::Duration};
|
||||
use native_tls::TlsConnector;
|
||||
use native_tls::{TlsConnector, TlsStream};
|
||||
use socks::Socks5Stream;
|
||||
|
||||
pub trait RacStream: Read + Write + Unpin + Send + Sync + Debug {}
|
||||
impl<T: Read + Write + Unpin + Send + Sync + Debug> RacStream for T {}
|
||||
use crate::util::parse_socks5_url;
|
||||
|
||||
pub trait RacStream: Read + Write + Unpin + Send + Sync + Debug {
|
||||
fn set_read_timeout(&self, timeout: Duration);
|
||||
fn set_write_timeout(&self, timeout: Duration);
|
||||
}
|
||||
|
||||
impl RacStream for TcpStream {
|
||||
fn set_read_timeout(&self, timeout: Duration) { let _ = TcpStream::set_read_timeout(&self, Some(timeout)); }
|
||||
fn set_write_timeout(&self, timeout: Duration) { let _ = TcpStream::set_write_timeout(&self, Some(timeout)); }
|
||||
}
|
||||
|
||||
impl RacStream for Socks5Stream {
|
||||
fn set_read_timeout(&self, timeout: Duration) { let _ = TcpStream::set_read_timeout(self.get_ref(), Some(timeout)); }
|
||||
fn set_write_timeout(&self, timeout: Duration) { let _ = TcpStream::set_write_timeout(self.get_ref(), Some(timeout)); }
|
||||
}
|
||||
|
||||
impl<T: RacStream> RacStream for TlsStream<T> {
|
||||
fn set_read_timeout(&self, timeout: Duration) { self.get_ref().set_read_timeout(timeout); }
|
||||
fn set_write_timeout(&self, timeout: Duration) { self.get_ref().set_write_timeout(timeout); }
|
||||
}
|
||||
|
||||
impl RacStream for TlsStream<Box<dyn RacStream>> {
|
||||
fn set_read_timeout(&self, timeout: Duration) { self.get_ref().set_read_timeout(timeout); }
|
||||
fn set_write_timeout(&self, timeout: Duration) { self.get_ref().set_write_timeout(timeout); }
|
||||
}
|
||||
|
||||
/// Create RAC connection (also you can just TcpStream::connect)
|
||||
///
|
||||
/// host - host string, example: "example.com:12345", "example.com" (default port is 42666)
|
||||
/// ssl - wrap with ssl client, write false if you dont know what it is
|
||||
pub fn connect(host: &str, ssl: bool) -> Result<Box<dyn RacStream>, Box<dyn Error>> {
|
||||
/// proxy - socks5 proxy (host, (user, pass))
|
||||
pub fn connect(host: &str, ssl: bool, proxy: Option<String>) -> Result<Box<dyn RacStream>, Box<dyn Error>> {
|
||||
let host = if host.contains(":") {
|
||||
host.to_string()
|
||||
} else {
|
||||
format!("{host}:42666")
|
||||
};
|
||||
|
||||
if ssl {
|
||||
let stream: Box<dyn RacStream> = if let Some(proxy) = proxy {
|
||||
if let Some((proxy, auth)) = parse_socks5_url(&proxy) {
|
||||
if let Some((user, pass)) = auth {
|
||||
Box::new(Socks5Stream::connect_with_password(&proxy, host.as_str(), &user, &pass)?)
|
||||
} else {
|
||||
Box::new(Socks5Stream::connect(&proxy, host.as_str())?)
|
||||
}
|
||||
} else {
|
||||
return Err("proxy parse error".into());
|
||||
}
|
||||
} else {
|
||||
let addr = host.to_socket_addrs()?.next().ok_or::<Box<dyn Error>>("addr parse error".into())?;
|
||||
|
||||
Box::new(TcpStream::connect(&addr)?)
|
||||
};
|
||||
|
||||
let stream = if ssl {
|
||||
let ip: String = host.split_once(":")
|
||||
.map(|o| o.0.to_string())
|
||||
.unwrap_or(host.clone());
|
||||
|
||||
return Ok(Box::new(TlsConnector::builder()
|
||||
Box::new(TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.build()?
|
||||
.connect(&ip, connect(&host, false)?)?))
|
||||
}
|
||||
.connect(&ip, stream)?)
|
||||
} else {
|
||||
stream
|
||||
};
|
||||
|
||||
let addr = host.to_socket_addrs()?.next().ok_or::<Box<dyn Error>>("addr parse error".into())?;
|
||||
let stream = TcpStream::connect_timeout(&addr, Duration::from_secs(3))?;
|
||||
stream.set_read_timeout(Duration::from_secs(3));
|
||||
stream.set_write_timeout(Duration::from_secs(3));
|
||||
|
||||
stream.set_read_timeout(Some(Duration::from_secs(5)));
|
||||
stream.set_write_timeout(Some(Duration::from_secs(5)));
|
||||
|
||||
Ok(Box::new(stream))
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
/// Send message
|
||||
@ -52,10 +93,23 @@ pub fn send_message(stream: &mut impl Write, message: &str) -> Result<(), Box<dy
|
||||
/// stream - any stream that can be written to
|
||||
/// name - user name
|
||||
/// password - user password
|
||||
/// remove_null - remove null bytes on reading
|
||||
///
|
||||
/// returns whether the user was registered
|
||||
pub fn register_user(stream: &mut (impl Write + Read), name: &str, password: &str) -> Result<bool, Box<dyn Error>> {
|
||||
pub fn register_user(
|
||||
stream: &mut (impl Write + Read),
|
||||
name: &str,
|
||||
password: &str,
|
||||
remove_null: bool
|
||||
) -> Result<bool, Box<dyn Error>> {
|
||||
stream.write_all(format!("\x03{name}\n{password}").as_bytes())?;
|
||||
if remove_null {
|
||||
if let Ok(out) = skip_null(stream) {
|
||||
Ok(out[0] == 0)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
} else {
|
||||
let mut buf = vec![0];
|
||||
if let Ok(1) = stream.read(&mut buf) {
|
||||
Ok(buf[0] == 0)
|
||||
@ -63,6 +117,7 @@ pub fn register_user(stream: &mut (impl Write + Read), name: &str, password: &st
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send message with auth
|
||||
///
|
||||
@ -70,13 +125,27 @@ pub fn register_user(stream: &mut (impl Write + Read), name: &str, password: &st
|
||||
/// message - message text
|
||||
/// name - user name
|
||||
/// password - user password
|
||||
/// remove_null - remove null bytes on reading
|
||||
///
|
||||
/// returns 0 if the message was sent successfully
|
||||
/// returns 1 if the user does not exist
|
||||
/// returns 2 if the password is incorrect
|
||||
pub fn send_message_auth(stream: &mut (impl Write + Read), name: &str, password: &str, message: &str) -> Result<u8, Box<dyn Error>> {
|
||||
pub fn send_message_auth(
|
||||
stream: &mut (impl Write + Read),
|
||||
name: &str,
|
||||
password: &str,
|
||||
message: &str,
|
||||
remove_null: bool
|
||||
) -> Result<u8, Box<dyn Error>> {
|
||||
stream.write_all(format!("\x02{name}\n{password}\n{message}").as_bytes())?;
|
||||
|
||||
if remove_null {
|
||||
if let Ok(out) = skip_null(stream) {
|
||||
Ok(out[0])
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
} else {
|
||||
let mut buf = vec![0];
|
||||
if let Ok(1) = stream.read(&mut buf) {
|
||||
Ok(buf[0])
|
||||
@ -84,6 +153,7 @@ pub fn send_message_auth(stream: &mut (impl Write + Read), name: &str, password:
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send message with fake auth
|
||||
///
|
||||
@ -91,26 +161,24 @@ pub fn send_message_auth(stream: &mut (impl Write + Read), name: &str, password:
|
||||
///
|
||||
/// let (name, message) = message.split("> ") else { return send_message(stream, message) }
|
||||
/// if send_message_auth(name, name, message) != 0 {
|
||||
/// let name = "\x1f" + "name"
|
||||
/// let name = "\x1f" + name
|
||||
/// register_user(stream, name, name)
|
||||
/// send_message_spoof_auth(stream, name + "> " + message)
|
||||
/// }
|
||||
pub fn send_message_spoof_auth(stream: &mut (impl Write + Read), message: &str) -> Result<(), Box<dyn Error>> {
|
||||
pub fn send_message_spoof_auth(stream: &mut (impl Write + Read), message: &str, remove_null: bool) -> Result<(), Box<dyn Error>> {
|
||||
let Some((name, message)) = message.split_once("> ") else { return send_message(stream, message) };
|
||||
|
||||
stream.write_all(format!("\x02{name}\n{name}\n{message}").as_bytes())?;
|
||||
|
||||
let mut buf = vec![0; 1];
|
||||
if let Ok(_) = stream.read_exact(&mut buf) {
|
||||
if let Ok(f) = send_message_auth(stream, &name, &message, &message, remove_null) {
|
||||
if f != 0 {
|
||||
let name = format!("\x1f{name}");
|
||||
register_user(stream, &name, &name)?;
|
||||
let message = format!("{name}> {message}");
|
||||
send_message_spoof_auth(stream, &message)
|
||||
} else {
|
||||
Ok(())
|
||||
register_user(stream, &name, &name, remove_null);
|
||||
send_message_spoof_auth(stream, &format!("{name}> {message}"), remove_null);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Skip null bytes and return first non-null byte
|
||||
pub fn skip_null(stream: &mut impl Read) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
loop {
|
||||
@ -122,6 +190,7 @@ pub fn skip_null(stream: &mut impl Read) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// remove trailing null bytes in vector
|
||||
pub fn remove_trailing_null(vec: &mut Vec<u8>) -> Result<(), Box<dyn Error>> {
|
||||
while vec.ends_with(&[0]) {
|
||||
vec.remove(vec.len()-1);
|
||||
@ -133,7 +202,7 @@ pub fn remove_trailing_null(vec: &mut Vec<u8>) -> Result<(), Box<dyn Error>> {
|
||||
///
|
||||
/// max_messages - max messages in list
|
||||
/// last_size - last returned packet size
|
||||
/// start_null - start with skipping null bytes
|
||||
/// remove_null - start with skipping null bytes
|
||||
/// chunked - is chunked reading enabled
|
||||
///
|
||||
/// returns (messages, packet size)
|
||||
@ -141,15 +210,26 @@ pub fn read_messages(
|
||||
stream: &mut (impl Read + Write),
|
||||
max_messages: usize,
|
||||
last_size: usize,
|
||||
start_null: bool,
|
||||
remove_null: bool,
|
||||
chunked: bool
|
||||
) -> Result<Option<(Vec<String>, usize)>, Box<dyn Error>> {
|
||||
stream.write_all(&[0x00])?;
|
||||
|
||||
let packet_size = {
|
||||
let data = if remove_null {
|
||||
let mut data = skip_null(stream)?;
|
||||
let mut buf = vec![0; 10];
|
||||
let len = stream.read(&mut buf)?;
|
||||
buf.truncate(len);
|
||||
data.append(&mut buf);
|
||||
remove_trailing_null(&mut data)?;
|
||||
data
|
||||
} else {
|
||||
let mut data = vec![0; 10];
|
||||
let len = stream.read(&mut data)?;
|
||||
data.truncate(len);
|
||||
data
|
||||
};
|
||||
|
||||
String::from_utf8(data)?
|
||||
.trim_matches(char::from(0))
|
||||
@ -168,13 +248,20 @@ pub fn read_messages(
|
||||
packet_size - last_size
|
||||
};
|
||||
|
||||
let packet_data = {
|
||||
let packet_data = if remove_null {
|
||||
let mut data = skip_null(stream)?;
|
||||
let mut buf = vec![0; to_read - 1];
|
||||
stream.read_exact(&mut buf)?;
|
||||
data.append(&mut buf);
|
||||
data
|
||||
} else {
|
||||
let mut data = vec![0; to_read];
|
||||
stream.read_exact(&mut data)?;
|
||||
|
||||
String::from_utf8_lossy(&data).to_string()
|
||||
data
|
||||
};
|
||||
|
||||
let packet_data = String::from_utf8_lossy(&packet_data).to_string();
|
||||
|
||||
let lines: Vec<&str> = packet_data.split("\n").collect();
|
||||
let lines: Vec<String> = lines.clone().into_iter()
|
||||
.skip(if lines.len() >= max_messages { lines.len() - max_messages } else { 0 })
|
||||
|
16
src/util.rs
16
src/util.rs
@ -11,3 +11,19 @@ pub fn sanitize_text(input: &str) -> String {
|
||||
let cleaned_text = CONTROL_CHARS_REGEX.replace_all(&without_ansi, "");
|
||||
cleaned_text.into_owned()
|
||||
}
|
||||
|
||||
/// `socks5://user:pass@127.0.0.1:12345/path -> ("127.0.0.1:12345", ("user", "pass"))` \
|
||||
/// `socks5://127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
||||
/// `https://127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
||||
/// `127.0.0.1:12345 -> ("127.0.0.1:12345", None)` \
|
||||
/// `user:pass@127.0.0.1:12345 -> ("127.0.0.1:12345", ("user", "pass"))`
|
||||
pub fn parse_socks5_url(url: &str) -> Option<(String, Option<(String, String)>)> {
|
||||
let (_, url) = url.split_once("://").unwrap_or(("", url));
|
||||
let (url, _) = url.split_once("/").unwrap_or((url, ""));
|
||||
if let Some((auth, url)) = url.split_once("@") {
|
||||
let (user, pass) = auth.split_once(":")?;
|
||||
Some((url.to_string(), Some((user.to_string(), pass.to_string()))))
|
||||
} else {
|
||||
Some((url.to_string(), None))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user