mod firewall;
use std::{
ffi::c_void,
io::{Read, Write},
net::{IpAddr, Ipv4Addr, Ipv6Addr},
process::{ChildStdin, ChildStdout},
sync::{
Arc, Condvar, Mutex,
atomic::{AtomicBool, Ordering},
},
thread::JoinHandle,
};
use anyhow::{Context, bail};
use windows_sys::Win32::{
Devices::DeviceAndDriverInstallation::{
CM_LOCATE_DEVNODE_PHANTOM, CM_Locate_DevNodeW, CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
CM_NOTIFY_FILTER, CM_NOTIFY_FILTER_0, CM_NOTIFY_FILTER_0_1,
CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE, CM_Register_Notification, CM_Uninstall_DevNode,
CM_Unregister_Notification, CR_NO_SUCH_DEVNODE, CR_SUCCESS, HCMNOTIFICATION,
},
Foundation::{
ERROR_BUFFER_OVERFLOW, ERROR_NOT_FOUND, ERROR_OBJECT_ALREADY_EXISTS, ERROR_SUCCESS,
NO_ERROR,
},
NetworkManagement::IpHelper::{
ConvertInterfaceGuidToLuid, CreateIpForwardEntry2, CreateUnicastIpAddressEntry,
DNS_INTERFACE_SETTINGS, DNS_INTERFACE_SETTINGS_VERSION1, DNS_SETTING_IPV6,
DNS_SETTING_NAMESERVER, DeleteIpForwardEntry2, FreeMibTable, GAA_FLAG_SKIP_ANYCAST,
GAA_FLAG_SKIP_MULTICAST, GAA_FLAG_SKIP_UNICAST, GetAdaptersAddresses, GetIpForwardTable2,
IP_ADAPTER_ADDRESSES_LH, InitializeIpForwardEntry, InitializeUnicastIpAddressEntry,
MIB_IPFORWARD_ROW2, MIB_IPFORWARD_TABLE2, MIB_UNICASTIPADDRESS_ROW,
SetInterfaceDnsSettings,
},
Networking::WinSock::{
ADDRESS_FAMILY, AF_INET, AF_INET6, AF_UNSPEC, IN_ADDR, IN_ADDR_0, IN6_ADDR, IN6_ADDR_0,
SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6, SOCKADDR_INET,
},
};
use wintun::{Adapter, Session, Wintun};
use firewall::{Firewall, WfpKillSwitch};
const TUN_NAME: &str = "Geph";
const TUN_GUID: u128 = 0x6765_7068_0000_0000_0000_0000_0000_0001;
const TUN_DEVICE_INSTANCE_ID: &str = r"SWD\WINTUN\{67657068-0000-0000-0000-000000000001}";
const TUN_V4_ADDR: Ipv4Addr = Ipv4Addr::new(100, 64, 0, 1);
const TUN_V4_PREFIX: u8 = 10; const TUN_V6_ADDR: Ipv6Addr = Ipv6Addr::new(0xfd00, 0x6765, 0, 0, 0, 0, 0, 1);
const TUN_V6_PREFIX: u8 = 64;
const TUN_MTU: usize = 16384;
const RING_CAPACITY: u32 = 0x40_0000;
const V4_SPLIT: [(Ipv4Addr, u8); 2] = [
(Ipv4Addr::new(0, 0, 0, 0), 1),
(Ipv4Addr::new(128, 0, 0, 0), 1),
];
const V6_SPLIT: [(Ipv6Addr, u8); 2] = [
(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 1),
(Ipv6Addr::new(0x8000, 0, 0, 0, 0, 0, 0, 0), 1),
];
fn sentinel_dns() -> [IpAddr; 2] {
[
IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)),
]
}
#[derive(Clone, Debug)]
pub(super) struct PhysIface {
index4: u32,
index6: u32,
}
impl PhysIface {
pub(super) fn bind_indices(&self) -> (u32, u32) {
(self.index4, self.index6)
}
}
pub(super) struct VpnHandle {
#[allow(dead_code)]
wintun: Wintun,
adapter: Arc<Adapter>,
phys: PhysIface,
firewall: WfpKillSwitch,
}
struct AdapterRemovalContext {
removed: Mutex<bool>,
changed: Condvar,
}
pub(crate) struct AdapterRemovalNotification {
notification: HCMNOTIFICATION,
context: Box<AdapterRemovalContext>,
}
impl AdapterRemovalNotification {
pub(crate) fn prepare() -> anyhow::Result<Option<Self>> {
unsafe extern "system" fn callback(
_notification: HCMNOTIFICATION,
context: *const c_void,
action: i32,
_event_data: *const windows_sys::Win32::Devices::DeviceAndDriverInstallation::CM_NOTIFY_EVENT_DATA,
_event_data_size: u32,
) -> u32 {
if action == CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED {
let context = unsafe { &*(context.cast::<AdapterRemovalContext>()) };
let mut removed = context.removed.lock().unwrap();
*removed = true;
context.changed.notify_all();
}
ERROR_SUCCESS
}
let instance_id: Vec<u16> = TUN_DEVICE_INSTANCE_ID
.encode_utf16()
.chain(std::iter::once(0))
.collect();
let mut device = 0;
match unsafe { CM_Locate_DevNodeW(&mut device, instance_id.as_ptr(), 0) } {
CR_SUCCESS => {}
CR_NO_SUCH_DEVNODE => return Ok(None),
error => anyhow::bail!("locating the WinTUN device instance failed ({error})"),
}
let mut filter: CM_NOTIFY_FILTER = unsafe { std::mem::zeroed() };
filter.cbSize = std::mem::size_of::<CM_NOTIFY_FILTER>() as u32;
filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE;
let mut filter_instance_id = [0u16; 200];
filter_instance_id[..instance_id.len()].copy_from_slice(&instance_id);
filter.u = CM_NOTIFY_FILTER_0 {
DeviceInstance: CM_NOTIFY_FILTER_0_1 {
InstanceId: filter_instance_id,
},
};
let context = Box::new(AdapterRemovalContext {
removed: Mutex::new(false),
changed: Condvar::new(),
});
let mut notification = std::ptr::null_mut();
let result = unsafe {
CM_Register_Notification(
&filter,
std::ptr::from_ref(context.as_ref()).cast(),
Some(callback),
&mut notification,
)
};
if result != CR_SUCCESS {
if unsafe { CM_Locate_DevNodeW(&mut device, instance_id.as_ptr(), 0) }
== CR_NO_SUCH_DEVNODE
{
return Ok(None);
}
anyhow::bail!("registering WinTUN removal notification failed ({result})");
}
let registration = Self {
notification,
context,
};
match unsafe { CM_Locate_DevNodeW(&mut device, instance_id.as_ptr(), 0) } {
CR_SUCCESS => Ok(Some(registration)),
CR_NO_SUCH_DEVNODE => Ok(None),
error => anyhow::bail!("locating the WinTUN device instance failed ({error})"),
}
}
pub(crate) fn wait(&self) -> anyhow::Result<()> {
let mut removed = self.context.removed.lock().unwrap();
while !*removed {
removed = self.context.changed.wait(removed).unwrap();
}
drop(removed);
uninstall_orphaned_adapter()
}
}
fn uninstall_orphaned_adapter() -> anyhow::Result<()> {
let instance_id: Vec<u16> = TUN_DEVICE_INSTANCE_ID
.encode_utf16()
.chain(std::iter::once(0))
.collect();
let mut device = 0;
match unsafe {
CM_Locate_DevNodeW(
&mut device,
instance_id.as_ptr(),
CM_LOCATE_DEVNODE_PHANTOM,
)
} {
CR_SUCCESS => {}
CR_NO_SUCH_DEVNODE => return Ok(()),
error => anyhow::bail!("locating the removed WinTUN device instance failed ({error})"),
}
let result = unsafe { CM_Uninstall_DevNode(device, 0) };
if result != CR_SUCCESS {
anyhow::bail!("uninstalling the orphaned WinTUN device failed ({result})");
}
Ok(())
}
impl Drop for AdapterRemovalNotification {
fn drop(&mut self) {
unsafe {
let _ = CM_Unregister_Notification(self.notification);
}
}
}
impl VpnHandle {
pub(super) fn bind_indices(&self) -> (u32, u32) {
(self.phys.index4, self.phys.index6)
}
pub(super) fn phys_dns(&self) -> Vec<IpAddr> {
physical_dns_servers(&[self.phys.index4, self.phys.index6])
}
fn luid(&self) -> u64 {
unsafe { self.adapter.get_luid().Value }
}
pub(super) fn start_session(&self) -> anyhow::Result<Arc<Session>> {
Ok(Arc::new(
self.adapter
.start_session(RING_CAPACITY)
.context("start wintun session")?,
))
}
fn cleanup(&mut self) {
self.firewall.remove();
let _ = set_tun_dns(self.adapter.get_guid(), &[]);
delete_split_routes(self.luid());
}
}
pub(super) fn cleanup(mut handle: VpnHandle) {
handle.cleanup();
}
#[derive(Clone)]
pub(super) struct NetworkSnapshot {
bind_indices: (u32, u32),
}
pub(super) fn network_snapshot(handle: &VpnHandle) -> NetworkSnapshot {
NetworkSnapshot {
bind_indices: handle.bind_indices(),
}
}
pub(super) fn network_check(snapshot: &NetworkSnapshot) -> super::NetworkAction {
match physical_iface() {
Ok(current) if current.bind_indices() != snapshot.bind_indices => {
super::NetworkAction::Reconcile
}
_ => super::NetworkAction::Healthy,
}
}
pub(super) fn physical_iface() -> anyhow::Result<PhysIface> {
let index4 = default_route_ifindex(AF_INET)
.context("could not find the IPv4 default-route interface")?;
let index6 = default_route_ifindex(AF_INET6).unwrap_or(index4);
Ok(PhysIface { index4, index6 })
}
pub(super) fn setup(phys: PhysIface, allow_lan: bool) -> anyhow::Result<VpnHandle> {
tracing::debug!("starting Windows VPN setup");
let wintun = unsafe { wintun::load() }.map_err(|e| anyhow::anyhow!("load wintun.dll: {e}"))?;
tracing::debug!("loaded wintun.dll for VPN setup");
let rollback = scopeguard::guard((), |_| cleanup_stale());
let adapter = match Adapter::open(&wintun, TUN_NAME) {
Ok(existing) => {
tracing::debug!("opened existing WinTUN adapter");
existing
}
Err(_) => {
tracing::debug!("creating WinTUN adapter");
Adapter::create(&wintun, TUN_NAME, TUN_NAME, Some(TUN_GUID))
.map_err(|e| anyhow::anyhow!("create wintun adapter: {e}"))?
}
};
tracing::debug!("WinTUN adapter is ready");
let luid = unsafe { adapter.get_luid().Value };
set_unicast_address(luid, IpAddr::V4(TUN_V4_ADDR), TUN_V4_PREFIX)
.context("assign tun IPv4 address")?;
let _ = set_unicast_address(luid, IpAddr::V6(TUN_V6_ADDR), TUN_V6_PREFIX);
let _ = adapter.set_mtu(TUN_MTU);
let _ = set_tun_dns(adapter.get_guid(), &sentinel_dns());
tracing::debug!("configured WinTUN addressing and DNS");
let mut firewall = WfpKillSwitch::new();
firewall.preflight().context("kill switch preflight")?;
tracing::debug!("completed WFP kill-switch preflight");
firewall
.install(&geph_app_ids(), luid, allow_lan)
.context("install kill switch")?;
tracing::debug!("installed WFP kill switch");
ensure_split_routes(luid)?;
tracing::debug!("installed WinTUN capture routes");
scopeguard::ScopeGuard::into_inner(rollback);
Ok(VpnHandle {
wintun,
adapter,
phys,
firewall,
})
}
pub(super) fn reconcile(
handle: &mut VpnHandle,
phys: PhysIface,
allow_lan: bool,
) -> anyhow::Result<()> {
let luid = handle.luid();
set_unicast_address(luid, IpAddr::V4(TUN_V4_ADDR), TUN_V4_PREFIX)
.context("reassert tun IPv4 address")?;
let _ = set_unicast_address(luid, IpAddr::V6(TUN_V6_ADDR), TUN_V6_PREFIX);
let _ = handle.adapter.set_mtu(TUN_MTU);
let _ = set_tun_dns(handle.adapter.get_guid(), &sentinel_dns());
handle
.firewall
.replace(&geph_app_ids(), luid, allow_lan)
.context("reconcile kill switch")?;
ensure_split_routes(luid)?;
handle.phys = phys;
Ok(())
}
fn ensure_split_routes(luid: u64) -> anyhow::Result<()> {
for (addr, prefix) in V4_SPLIT {
add_route(luid, IpAddr::V4(addr), prefix)
.with_context(|| format!("add tun route {addr}/{prefix}"))?;
}
for (addr, prefix) in V6_SPLIT {
let _ = add_route(luid, IpAddr::V6(addr), prefix);
}
Ok(())
}
fn delete_split_routes(luid: u64) {
for (addr, prefix) in V4_SPLIT {
let _ = delete_route(luid, IpAddr::V4(addr), prefix);
}
for (addr, prefix) in V6_SPLIT {
let _ = delete_route(luid, IpAddr::V6(addr), prefix);
}
}
pub(super) fn cleanup_stale() {
match AdapterRemovalNotification::prepare() {
Ok(Some(removal)) => {
if let Err(error) = removal.wait() {
tracing::warn!(%error, "could not finish stale WinTUN device removal");
}
}
Ok(None) => {
if let Err(error) = uninstall_orphaned_adapter() {
tracing::warn!(%error, "could not remove orphaned WinTUN device");
}
}
Err(error) => {
tracing::warn!(%error, "could not observe stale WinTUN device removal");
}
}
let guid = windows_sys::core::GUID::from_u128(TUN_GUID);
let mut luid: windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH =
unsafe { std::mem::zeroed() };
let _ = set_tun_dns(TUN_GUID, &[]);
if unsafe { ConvertInterfaceGuidToLuid(&guid, &mut luid) } == NO_ERROR {
delete_split_routes(unsafe { luid.Value });
}
let _ = firewall::purge_stale();
}
fn geph_app_ids() -> Vec<std::path::PathBuf> {
let mut ids = Vec::new();
if let Ok(exe) = std::env::current_exe() {
ids.push(exe);
}
ids.push(crate::platform::engine_bin_path());
ids
}
fn set_tun_dns(guid: u128, servers: &[IpAddr]) -> anyhow::Result<()> {
let guid = windows_sys::core::GUID::from_u128(guid);
let v4: Vec<String> = servers
.iter()
.filter(|ip| ip.is_ipv4())
.map(|ip| ip.to_string())
.collect();
let v6: Vec<String> = servers
.iter()
.filter(|ip| ip.is_ipv6())
.map(|ip| ip.to_string())
.collect();
for (list, v6_flag) in [(v4, 0u64), (v6, DNS_SETTING_IPV6 as u64)] {
let joined: Vec<u16> = list
.join(",")
.encode_utf16()
.chain(std::iter::once(0))
.collect();
let settings = DNS_INTERFACE_SETTINGS {
Version: DNS_INTERFACE_SETTINGS_VERSION1,
Flags: DNS_SETTING_NAMESERVER as u64 | v6_flag,
Domain: std::ptr::null_mut(),
NameServer: joined.as_ptr() as *mut u16,
SearchList: std::ptr::null_mut(),
RegistrationEnabled: 0,
RegisterAdapterName: 0,
EnableLLMNR: 0,
QueryAdapterName: 0,
ProfileNameServer: std::ptr::null_mut(),
};
let code = unsafe { SetInterfaceDnsSettings(guid, &settings) };
if code != NO_ERROR {
bail!(
"SetInterfaceDnsSettings(ipv6={}) failed: 0x{code:08X}",
v6_flag != 0
);
}
}
Ok(())
}
fn write_sockaddr_inet(dst: &mut SOCKADDR_INET, ip: IpAddr) {
match ip {
IpAddr::V4(v4) => {
let mut sa: SOCKADDR_IN = unsafe { std::mem::zeroed() };
sa.sin_family = AF_INET;
sa.sin_addr = IN_ADDR {
S_un: IN_ADDR_0 {
S_addr: u32::from_ne_bytes(v4.octets()),
},
};
dst.Ipv4 = sa;
}
IpAddr::V6(v6) => {
let mut sa: SOCKADDR_IN6 = unsafe { std::mem::zeroed() };
sa.sin6_family = AF_INET6;
sa.sin6_addr = IN6_ADDR {
u: IN6_ADDR_0 { Byte: v6.octets() },
};
dst.Ipv6 = sa;
}
}
}
fn unspecified_like(ip: IpAddr) -> IpAddr {
match ip {
IpAddr::V4(_) => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
IpAddr::V6(_) => IpAddr::V6(Ipv6Addr::UNSPECIFIED),
}
}
fn set_unicast_address(luid: u64, ip: IpAddr, prefix: u8) -> anyhow::Result<()> {
let mut row: MIB_UNICASTIPADDRESS_ROW = unsafe { std::mem::zeroed() };
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
row.InterfaceLuid.Value = luid;
write_sockaddr_inet(&mut row.Address, ip);
row.OnLinkPrefixLength = prefix;
let code = unsafe { CreateUnicastIpAddressEntry(&row) };
if code == NO_ERROR || code == ERROR_OBJECT_ALREADY_EXISTS {
Ok(())
} else {
bail!("CreateUnicastIpAddressEntry({ip}/{prefix}) failed: 0x{code:08X}");
}
}
fn add_route(luid: u64, dest: IpAddr, prefix: u8) -> anyhow::Result<()> {
let mut row: MIB_IPFORWARD_ROW2 = unsafe { std::mem::zeroed() };
unsafe { InitializeIpForwardEntry(&mut row) };
row.InterfaceLuid.Value = luid;
write_sockaddr_inet(&mut row.DestinationPrefix.Prefix, dest);
row.DestinationPrefix.PrefixLength = prefix;
write_sockaddr_inet(&mut row.NextHop, unspecified_like(dest));
let code = unsafe { CreateIpForwardEntry2(&row) };
if code == NO_ERROR || code == ERROR_OBJECT_ALREADY_EXISTS {
Ok(())
} else {
bail!("CreateIpForwardEntry2({dest}/{prefix}) failed: 0x{code:08X}");
}
}
fn delete_route(luid: u64, dest: IpAddr, prefix: u8) -> anyhow::Result<()> {
let mut row: MIB_IPFORWARD_ROW2 = unsafe { std::mem::zeroed() };
row.InterfaceLuid.Value = luid;
write_sockaddr_inet(&mut row.DestinationPrefix.Prefix, dest);
row.DestinationPrefix.PrefixLength = prefix;
write_sockaddr_inet(&mut row.NextHop, unspecified_like(dest));
let code = unsafe { DeleteIpForwardEntry2(&row) };
if code == NO_ERROR || code == ERROR_NOT_FOUND {
Ok(())
} else {
bail!("DeleteIpForwardEntry2({dest}/{prefix}) failed: 0x{code:08X}");
}
}
fn default_route_ifindex(family: ADDRESS_FAMILY) -> Option<u32> {
let mut table: *mut MIB_IPFORWARD_TABLE2 = std::ptr::null_mut();
let code = unsafe { GetIpForwardTable2(family, &mut table) };
if code != NO_ERROR || table.is_null() {
return None;
}
let mut best: Option<(u32, u32)> = None; unsafe {
let count = (*table).NumEntries as usize;
let rows = std::ptr::addr_of!((*table).Table) as *const MIB_IPFORWARD_ROW2;
for i in 0..count {
let row = &*rows.add(i);
if row.DestinationPrefix.PrefixLength != 0 {
continue; }
if row.DestinationPrefix.Prefix.si_family != family {
continue;
}
if row.Loopback != 0 {
continue;
}
if best.map(|(m, _)| row.Metric < m).unwrap_or(true) {
best = Some((row.Metric, row.InterfaceIndex));
}
}
FreeMibTable(table as *const c_void);
}
best.map(|(_, idx)| idx)
}
fn physical_dns_servers(indices: &[u32]) -> Vec<IpAddr> {
let flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST;
let mut size: u32 = 16 * 1024;
let mut buf: Vec<u8> = Vec::new();
let mut out: Vec<IpAddr> = Vec::new();
for _ in 0..3 {
buf.resize(size as usize, 0);
let ret = unsafe {
GetAdaptersAddresses(
AF_UNSPEC as u32,
flags,
std::ptr::null(),
buf.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH,
&mut size,
)
};
if ret == ERROR_BUFFER_OVERFLOW {
continue;
}
if ret != ERROR_SUCCESS {
return out;
}
let mut adapter = buf.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH;
while !adapter.is_null() {
let a = unsafe { &*adapter };
let if4 = unsafe { a.Anonymous1.Anonymous.IfIndex };
if indices.contains(&if4) || indices.contains(&a.Ipv6IfIndex) {
let mut dns = a.FirstDnsServerAddress;
while !dns.is_null() {
let d = unsafe { &*dns };
if let Some(ip) = unsafe { sockaddr_to_ip(d.Address.lpSockaddr) }
&& !out.contains(&ip)
{
out.push(ip);
}
dns = d.Next;
}
}
adapter = a.Next;
}
return out;
}
out
}
unsafe fn sockaddr_to_ip(sa: *const SOCKADDR) -> Option<IpAddr> {
if sa.is_null() {
return None;
}
match unsafe { (*sa).sa_family } {
AF_INET => {
let sin = sa as *const SOCKADDR_IN;
let bytes = unsafe { (*sin).sin_addr.S_un.S_addr }.to_ne_bytes();
Some(IpAddr::V4(Ipv4Addr::from(bytes)))
}
AF_INET6 => {
let sin6 = sa as *const SOCKADDR_IN6;
let bytes = unsafe { (*sin6).sin6_addr.u.Byte };
Some(IpAddr::V6(Ipv6Addr::from(bytes)))
}
_ => None,
}
}
pub(super) struct Pump {
session: Arc<Session>,
stop: Arc<AtomicBool>,
threads: Vec<JoinHandle<()>>,
}
impl Pump {
pub(super) fn start(
session: Arc<Session>,
child_stdin: ChildStdin,
child_stdout: ChildStdout,
) -> Pump {
let stop = Arc::new(AtomicBool::new(false));
let up = {
let session = session.clone();
let stop = stop.clone();
std::thread::spawn(move || pump_up(session, child_stdin, stop))
};
let dn = {
let session = session.clone();
let stop = stop.clone();
std::thread::spawn(move || pump_down(session, child_stdout, stop))
};
Pump {
session,
stop,
threads: vec![up, dn],
}
}
}
impl Drop for Pump {
fn drop(&mut self) {
self.stop.store(true, Ordering::SeqCst);
let _ = self.session.shutdown();
for handle in self.threads.drain(..) {
let _ = handle.join();
}
}
}
fn pump_up(session: Arc<Session>, mut child_stdin: ChildStdin, stop: Arc<AtomicBool>) {
while !stop.load(Ordering::SeqCst) {
let packet = match session.receive_blocking() {
Ok(p) => p,
Err(_) => break,
};
let bytes = packet.bytes();
let len = std::cmp::min(bytes.len(), u16::MAX as usize);
if child_stdin.write_all(&(len as u16).to_be_bytes()).is_err()
|| child_stdin.write_all(&bytes[..len]).is_err()
|| child_stdin.flush().is_err()
{
break;
}
}
}
fn pump_down(session: Arc<Session>, mut child_stdout: ChildStdout, stop: Arc<AtomicBool>) {
let mut len_buf = [0u8; 2];
while !stop.load(Ordering::SeqCst) {
if child_stdout.read_exact(&mut len_buf).is_err() {
break;
}
let len = u16::from_be_bytes(len_buf) as usize;
if len == 0 {
continue;
}
let mut buf = vec![0u8; len];
if child_stdout.read_exact(&mut buf).is_err() {
break;
}
match session.allocate_send_packet(len as u16) {
Ok(mut packet) => {
packet.bytes_mut().copy_from_slice(&buf);
session.send_packet(packet);
}
Err(_) => break,
}
}
}