去除多余代码
This commit is contained in:
@@ -1,216 +0,0 @@
|
|||||||
use std::net::Ipv4Addr;
|
|
||||||
use std::{io, process, thread};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::mpsc::{Receiver, SyncSender, TrySendError};
|
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
|
||||||
use tun::Device;
|
|
||||||
use tun::device::IFace;
|
|
||||||
use crate::handle::callback::DeviceConfig;
|
|
||||||
use crate::protocol::NetPacket;
|
|
||||||
use crate::util::{BufBlock, BufPool, GroupSyncSender, StopManager};
|
|
||||||
|
|
||||||
pub struct TunAdapter {
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))] is_tap: bool,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] device_name: Option<String>,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] mtu: u32,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] route_record: Vec<(Ipv4Addr, Ipv4Addr)>,
|
|
||||||
device: Option<Arc<Device>>,
|
|
||||||
buf_pool: BufPool,
|
|
||||||
stop_manager: StopManager,
|
|
||||||
receiver_stage: Option<Receiver<NetPacket<BufBlock>>>,
|
|
||||||
sender_stage: Option<SyncSender<NetPacket<BufBlock>>>,
|
|
||||||
}
|
|
||||||
impl TunAdapter {
|
|
||||||
pub fn new(
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))] is_tap: bool,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] device_name: Option<String>,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] mtu: u32,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] route_record: Vec<(Ipv4Addr, Ipv4Addr)>,
|
|
||||||
buf_pool: BufPool,
|
|
||||||
stop_manager: StopManager,
|
|
||||||
receiver: Receiver<NetPacket<BufBlock>>,
|
|
||||||
sender: SyncSender<NetPacket<BufBlock>>,
|
|
||||||
)->Self{
|
|
||||||
Self{
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))] is_tap,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] device_name,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] mtu,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] route_record,
|
|
||||||
device: None,
|
|
||||||
buf_pool,
|
|
||||||
stop_manager,
|
|
||||||
receiver_stage: Some(receiver),
|
|
||||||
sender_stage: Some(sender),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TunAdapter {
|
|
||||||
pub fn device(&mut self, #[cfg(target_os = "android")] device_fd: u32) -> io::Result<()> {
|
|
||||||
if self.device.is_some() {
|
|
||||||
return Ok(());
|
|
||||||
} else {
|
|
||||||
let device = create_device(#[cfg(any(target_os = "windows", target_os = "linux"))] self.is_tap,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] self.device_name.clone(),
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] self.mtu,
|
|
||||||
#[cfg(target_os = "android")] device_fd, )?;
|
|
||||||
let device = Arc::new(device);
|
|
||||||
self.device.replace(device);
|
|
||||||
{
|
|
||||||
let device = device.clone();
|
|
||||||
let buf_pool = self.buf_pool.clone();
|
|
||||||
let stop_manager = self.stop_manager.clone();
|
|
||||||
let sender = self.sender_stage.take().unwrap();
|
|
||||||
thread::Builder::new().name("tun-read".into()).spawn(move || {
|
|
||||||
loop {
|
|
||||||
let mut buf_block = buf_pool.alloc();
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
let start = 12;
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let start = 8;
|
|
||||||
match device.read(&mut buf_block.as_mut()[start..]) {
|
|
||||||
Ok(len) => {
|
|
||||||
buf_block.as_mut()[..12].fill(0);
|
|
||||||
buf_block.set_data_len(start + len);
|
|
||||||
if let Err(e) = sender.try_send(buf_block) {
|
|
||||||
match e {
|
|
||||||
TrySendError::Full(_) => {
|
|
||||||
log::warn!("发生丢包");
|
|
||||||
}
|
|
||||||
TrySendError::Disconnected(_) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("{:?}",e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stop_manager.stop();
|
|
||||||
}).unwrap();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let device = device.clone();
|
|
||||||
let stop_manager = self.stop_manager.clone();
|
|
||||||
let receiver = self.receiver_stage.take().unwrap();
|
|
||||||
|
|
||||||
thread::Builder::new().name("tun-write".into()).spawn(move || {
|
|
||||||
while let Ok(data) = receiver.recv() {
|
|
||||||
if let Err(e) = device.write(data.as_data()) {
|
|
||||||
log::warn!("写入网卡失败:{}",e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stop_manager.stop();
|
|
||||||
}).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
|
||||||
pub fn change_ip(&mut self, info: DeviceConfig) -> io::Result<()> {
|
|
||||||
let device = if let Some(device) = &self.device {
|
|
||||||
device
|
|
||||||
} else {
|
|
||||||
return Err(io::Error::new(io::ErrorKind::NotFound, "IFace"));
|
|
||||||
};
|
|
||||||
device.set_ip(info.virtual_ip, info.virtual_netmask)?;
|
|
||||||
for (dest, mask) in self.route_record.drain(..) {
|
|
||||||
if let Err(e) = self.device.delete_route(dest, mask) {
|
|
||||||
log::warn!("删除路由失败 ={:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Err(e) = device.add_route(info.virtual_network, info.virtual_netmask, 1)
|
|
||||||
{
|
|
||||||
log::warn!("添加默认路由失败 ={:?}", e);
|
|
||||||
} else {
|
|
||||||
self.route_record.push((info.virtual_network, info.virtual_netmask));
|
|
||||||
}
|
|
||||||
if let Err(e) = device
|
|
||||||
.add_route(Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST, 1)
|
|
||||||
{
|
|
||||||
log::warn!("添加广播路由失败 ={:?}", e);
|
|
||||||
} else {
|
|
||||||
self.route_record.push((Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = device.add_route(
|
|
||||||
Ipv4Addr::from([224, 0, 0, 0]),
|
|
||||||
Ipv4Addr::from([240, 0, 0, 0]),
|
|
||||||
1,
|
|
||||||
) {
|
|
||||||
log::warn!("添加组播路由失败 ={:?}", e);
|
|
||||||
} else {
|
|
||||||
self.route_record.push((
|
|
||||||
Ipv4Addr::from([224, 0, 0, 0]),
|
|
||||||
Ipv4Addr::from([240, 0, 0, 0]),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (dest, mask) in info.external_route {
|
|
||||||
if let Err(e) = self.device.add_route(dest, mask, 1) {
|
|
||||||
log::warn!("添加路由失败 ={:?}", e);
|
|
||||||
} else {
|
|
||||||
self.route_record.push((dest, mask));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
||||||
const DEFAULT_TUN_NAME: &str = "vnt-tun";
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
||||||
const DEFAULT_TAP_NAME: &str = "vnt-tap";
|
|
||||||
|
|
||||||
pub fn create_device(#[cfg(any(target_os = "windows", target_os = "linux"))] is_tap: bool,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] device_name: Option<String>,
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] mtu: u32,
|
|
||||||
#[cfg(target_os = "android")] device_fd: u32, ) -> io::Result<Device> {
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
||||||
let default_name: &str = if is_tap {
|
|
||||||
DEFAULT_TAP_NAME
|
|
||||||
} else {
|
|
||||||
DEFAULT_TUN_NAME
|
|
||||||
};
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
let device = {
|
|
||||||
let device_name = device_name
|
|
||||||
.unwrap_or(default_name.to_string());
|
|
||||||
if &device_name == default_name {
|
|
||||||
delete_device(default_name);
|
|
||||||
}
|
|
||||||
Device::new(Some(device_name), is_tap)?
|
|
||||||
};
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let device = Device::new(device_name)?;
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
let device = Device::new(
|
|
||||||
device_name
|
|
||||||
.unwrap_or(default_name.to_string()),
|
|
||||||
is_tap,
|
|
||||||
)?;
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
let device = Device::new(device_fd as _)?;
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
device.set_mtu(mtu)?;
|
|
||||||
Ok(device)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
fn delete_device(name: &str) {
|
|
||||||
// 删除默认网卡,此操作有风险,后续可能去除
|
|
||||||
use std::process::Command;
|
|
||||||
let cmd = format!("ip link delete {}", name);
|
|
||||||
let delete_tun = Command::new("sh")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(&cmd)
|
|
||||||
.output()
|
|
||||||
.expect("sh exec error!");
|
|
||||||
if !delete_tun.status.success() {
|
|
||||||
log::info!("{},{:?}",cmd, delete_tun);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
use std::{io, thread};
|
|
||||||
use std::net::Ipv4Addr;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::mpsc::Receiver;
|
|
||||||
|
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
use packet::icmp::icmp::IcmpPacket;
|
|
||||||
use packet::icmp::Kind;
|
|
||||||
use packet::ip::ipv4;
|
|
||||||
use packet::ip::ipv4::packet::IpV4Packet;
|
|
||||||
use packet::ip::ipv4::protocol::Protocol;
|
|
||||||
|
|
||||||
use crate::channel::context::Context;
|
|
||||||
use crate::cipher::Cipher;
|
|
||||||
use crate::external_route::ExternalRoute;
|
|
||||||
use crate::handle::{check_dest, CurrentDeviceInfo, PeerDeviceInfo};
|
|
||||||
use crate::ip_proxy::{IpProxyMap, ProxyHandler};
|
|
||||||
use crate::protocol;
|
|
||||||
use crate::protocol::{ip_turn_packet, MAX_TTL, NetPacket, Version};
|
|
||||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
|
||||||
use crate::protocol::ip_turn_packet::BroadcastPacket;
|
|
||||||
use crate::util::{BufBlock, SingleU64Adder, StopManager};
|
|
||||||
pub mod adapter;
|
|
||||||
pub fn start(
|
|
||||||
receivers: Vec<Receiver<BufBlock>>,
|
|
||||||
context: Context,
|
|
||||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
|
||||||
ip_route: ExternalRoute,
|
|
||||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
|
||||||
client_cipher: Cipher,
|
|
||||||
server_cipher: Cipher,
|
|
||||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
for (index,receiver) in receivers.into_iter().enumerate() {
|
|
||||||
let context = context.clone();
|
|
||||||
let current_device = current_device.clone();
|
|
||||||
let ip_route = ip_route.clone();
|
|
||||||
#[cfg(feature = "ip_proxy")]
|
|
||||||
let ip_proxy_map = ip_proxy_map.clone();
|
|
||||||
let client_cipher = client_cipher.clone();
|
|
||||||
let server_cipher = server_cipher.clone();
|
|
||||||
let device_list = device_list.clone();
|
|
||||||
thread::Builder::new()
|
|
||||||
.name(format!("IFace-{}", index))
|
|
||||||
.spawn(move || {
|
|
||||||
while let Ok(mut data) = receiver.recv() {
|
|
||||||
let data_len = data.data_len();
|
|
||||||
let buf = data.as_start_mut();
|
|
||||||
if data_len==0{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
match handle(
|
|
||||||
&context,
|
|
||||||
buf,
|
|
||||||
data_len,
|
|
||||||
current_device.load(),
|
|
||||||
&ip_route,
|
|
||||||
#[cfg(feature = "ip_proxy")]
|
|
||||||
&ip_proxy_map,
|
|
||||||
&client_cipher,
|
|
||||||
&server_cipher,
|
|
||||||
&device_list,
|
|
||||||
) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("{:?}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// 实现一个原地发送,必须保证是如下结构
|
|
||||||
/// |12字节开头|ip报文|至少1024字节结尾|
|
|
||||||
///
|
|
||||||
pub fn handle(
|
|
||||||
context: &Context,
|
|
||||||
buf: &mut [u8],
|
|
||||||
data_len: usize, //数据总长度=12+ip包长度
|
|
||||||
current_device: CurrentDeviceInfo,
|
|
||||||
ip_route: &ExternalRoute,
|
|
||||||
#[cfg(feature = "ip_proxy")] proxy_map: &Option<IpProxyMap>,
|
|
||||||
client_cipher: &Cipher,
|
|
||||||
server_cipher: &Cipher,
|
|
||||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
let ipv4_packet = IpV4Packet::new(&buf[12..data_len])?;
|
|
||||||
let protocol = ipv4_packet.protocol();
|
|
||||||
let src_ip = ipv4_packet.source_ip();
|
|
||||||
let mut dest_ip = ipv4_packet.destination_ip();
|
|
||||||
let mut net_packet = NetPacket::new0(data_len, buf)?;
|
|
||||||
net_packet.set_version(Version::V1);
|
|
||||||
net_packet.set_protocol(protocol::Protocol::IpTurn);
|
|
||||||
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into());
|
|
||||||
net_packet.first_set_ttl(6);
|
|
||||||
net_packet.set_source(src_ip);
|
|
||||||
net_packet.set_destination(dest_ip);
|
|
||||||
if dest_ip == current_device.virtual_gateway {
|
|
||||||
// 发到网关的加密方式不一样,要单独处理
|
|
||||||
if protocol == Protocol::Icmp {
|
|
||||||
net_packet.set_gateway_flag(true);
|
|
||||||
server_cipher.encrypt_ipv4(&mut net_packet)?;
|
|
||||||
context.send_default(net_packet.buffer(), current_device.connect_server)?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if dest_ip.is_multicast() {
|
|
||||||
//当作广播处理
|
|
||||||
dest_ip = Ipv4Addr::BROADCAST;
|
|
||||||
net_packet.set_destination(Ipv4Addr::BROADCAST);
|
|
||||||
}
|
|
||||||
if dest_ip.is_broadcast() || current_device.broadcast_ip == dest_ip {
|
|
||||||
// 广播 发送到直连目标
|
|
||||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
|
||||||
broadcast(
|
|
||||||
server_cipher,
|
|
||||||
context,
|
|
||||||
&mut net_packet,
|
|
||||||
¤t_device,
|
|
||||||
device_list,
|
|
||||||
)?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if !check_dest(
|
|
||||||
dest_ip,
|
|
||||||
current_device.virtual_netmask,
|
|
||||||
current_device.virtual_network,
|
|
||||||
) {
|
|
||||||
if let Some(r_dest_ip) = ip_route.route(&dest_ip) {
|
|
||||||
//路由的目标不能是自己
|
|
||||||
if r_dest_ip == src_ip {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
//需要修改目的地址
|
|
||||||
dest_ip = r_dest_ip;
|
|
||||||
net_packet.set_destination(r_dest_ip);
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "ip_proxy")]
|
|
||||||
if let Some(proxy_map) = proxy_map {
|
|
||||||
let mut ipv4_packet = IpV4Packet::new(net_packet.payload_mut())?;
|
|
||||||
proxy_map.send_handle(&mut ipv4_packet)?;
|
|
||||||
}
|
|
||||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
|
||||||
context.send_ipv4_by_id(
|
|
||||||
net_packet.buffer(),
|
|
||||||
&dest_ip,
|
|
||||||
current_device.connect_server,
|
|
||||||
current_device.status.online(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast(
|
|
||||||
server_cipher: &Cipher,
|
|
||||||
sender: &Context,
|
|
||||||
net_packet: &mut NetPacket<&mut [u8]>,
|
|
||||||
current_device: &CurrentDeviceInfo,
|
|
||||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
let list: Vec<Ipv4Addr> = device_list
|
|
||||||
.lock()
|
|
||||||
.1
|
|
||||||
.iter()
|
|
||||||
.filter(|info| info.status.is_online())
|
|
||||||
.map(|info| info.virtual_ip)
|
|
||||||
.collect();
|
|
||||||
const MAX_COUNT: usize = 8;
|
|
||||||
let mut p2p_ips = Vec::with_capacity(8);
|
|
||||||
let mut relay_ips = Vec::with_capacity(8);
|
|
||||||
let mut overflow = false;
|
|
||||||
for (index, peer_ip) in list.into_iter().enumerate() {
|
|
||||||
if index > MAX_COUNT {
|
|
||||||
overflow = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if let Some(route) = sender.route_table.route_one_p2p(&peer_ip) {
|
|
||||||
if sender
|
|
||||||
.send_by_key(net_packet.buffer(), route.route_key())
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
p2p_ips.push(peer_ip);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relay_ips.push(peer_ip);
|
|
||||||
}
|
|
||||||
if !overflow && relay_ips.is_empty() {
|
|
||||||
//全部p2p,不需要服务器中转
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if p2p_ips.is_empty() {
|
|
||||||
//都没有p2p则直接由服务器转发
|
|
||||||
if current_device.status.online() {
|
|
||||||
sender.send_default(net_packet.buffer(), current_device.connect_server)?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if !overflow && relay_ips.len() == 2 {
|
|
||||||
// 如果转发的ip数不多就直接发
|
|
||||||
for peer_ip in relay_ips {
|
|
||||||
//非直连的广播要改变目的地址,不然服务端收到了会再次广播
|
|
||||||
net_packet.set_destination(peer_ip);
|
|
||||||
sender.send_ipv4_by_id(
|
|
||||||
net_packet.buffer(),
|
|
||||||
&peer_ip,
|
|
||||||
current_device.connect_server,
|
|
||||||
current_device.status.online(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if current_device.status.offline() {
|
|
||||||
//离线的不再转发
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let buf = vec![0u8; 12 + 1 + p2p_ips.len() * 4 + net_packet.data_len() + ENCRYPTION_RESERVED];
|
|
||||||
//剩余的发送到服务端,需要告知哪些已发送过
|
|
||||||
let mut server_packet = NetPacket::new_encrypt(buf)?;
|
|
||||||
server_packet.set_version(Version::V1);
|
|
||||||
server_packet.set_gateway_flag(true);
|
|
||||||
server_packet.first_set_ttl(MAX_TTL);
|
|
||||||
server_packet.set_source(net_packet.source());
|
|
||||||
//使用对应的目的地址
|
|
||||||
server_packet.set_destination(net_packet.destination());
|
|
||||||
server_packet.set_protocol(protocol::Protocol::IpTurn);
|
|
||||||
server_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4Broadcast.into());
|
|
||||||
|
|
||||||
let mut broadcast = BroadcastPacket::unchecked(server_packet.payload_mut());
|
|
||||||
broadcast.set_address(&p2p_ips)?;
|
|
||||||
broadcast.set_data(net_packet.buffer())?;
|
|
||||||
server_cipher.encrypt_ipv4(&mut server_packet)?;
|
|
||||||
sender.send_default(server_packet.buffer(), current_device.connect_server)
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user