UDP settings.
This commit is contained in:
@@ -86,7 +86,7 @@ impl BufferPool {
|
||||
|
||||
Buffer {
|
||||
buffer,
|
||||
stack: self.stack.clone(),
|
||||
stack: Rc::clone(&self.stack),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ impl smoltcp::phy::Device for NetworkDevice {
|
||||
|
||||
fn transmit(&mut self, _timestamp: u64, length: usize) -> smoltcp::Result<Self::TxBuffer> {
|
||||
Ok(TxBuffer {
|
||||
network_file: self.network_file.clone(),
|
||||
network_file: Rc::clone(&self.network_file),
|
||||
buffer: vec![0; length],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ fn run() -> Result<()> {
|
||||
|
||||
trace!("opening network:");
|
||||
let network_fd = syscall::open("network:", O_RDWR | O_NONBLOCK)
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to open network:"))? as
|
||||
RawFd;
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to open network:"))?
|
||||
as RawFd;
|
||||
|
||||
trace!("opening :ip");
|
||||
let ip_fd = syscall::open(":ip", O_RDWR | O_CREAT | O_NONBLOCK)
|
||||
@@ -42,13 +42,13 @@ fn run() -> Result<()> {
|
||||
|
||||
trace!("opening :udp");
|
||||
let udp_fd = syscall::open(":udp", O_RDWR | O_CREAT | O_NONBLOCK)
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to open :udp"))? as
|
||||
RawFd;
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to open :udp"))?
|
||||
as RawFd;
|
||||
|
||||
let time_path = format!("time:{}", syscall::CLOCK_MONOTONIC);
|
||||
let time_fd = syscall::open(&time_path, syscall::O_RDWR)
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to open time:"))? as
|
||||
RawFd;
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to open time:"))?
|
||||
as RawFd;
|
||||
|
||||
let (network_file, ip_file, time_file, udp_file) = unsafe {
|
||||
(
|
||||
@@ -66,7 +66,7 @@ fn run() -> Result<()> {
|
||||
let mut event_queue = EventQueue::<(), Error>::new()
|
||||
.map_err(|e| Error::from_io_error(e, "failed to create event queue"))?;
|
||||
|
||||
let smolnetd_ = smolnetd.clone();
|
||||
let smolnetd_ = Rc::clone(&smolnetd);
|
||||
|
||||
event_queue
|
||||
.add(network_fd, move |_| {
|
||||
@@ -76,13 +76,13 @@ fn run() -> Result<()> {
|
||||
Error::from_io_error(e, "failed to listen to network events")
|
||||
})?;
|
||||
|
||||
let smolnetd_ = smolnetd.clone();
|
||||
let smolnetd_ = Rc::clone(&smolnetd);
|
||||
|
||||
event_queue
|
||||
.add(ip_fd, move |_| smolnetd_.borrow_mut().on_ip_scheme_event())
|
||||
.map_err(|e| Error::from_io_error(e, "failed to listen to ip events"))?;
|
||||
|
||||
let smolnetd_ = smolnetd.clone();
|
||||
let smolnetd_ = Rc::clone(&smolnetd);
|
||||
|
||||
event_queue
|
||||
.add(
|
||||
|
||||
@@ -35,7 +35,7 @@ impl PortSet {
|
||||
self.next = self.next.wrapping_add(1);
|
||||
};
|
||||
|
||||
return Some(port);
|
||||
Some(port)
|
||||
}
|
||||
|
||||
pub fn claim_port(&mut self, port: u16) -> bool {
|
||||
|
||||
@@ -47,7 +47,7 @@ impl IpScheme {
|
||||
}
|
||||
|
||||
pub fn notify_sockets(&mut self) -> Result<()> {
|
||||
for (&fd, ref handle) in &self.raw_sockets {
|
||||
for (&fd, handle) in &self.raw_sockets {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket: &mut RawSocket = socket_set.get_mut(handle.socket_handle).as_socket();
|
||||
if socket.can_send() {
|
||||
@@ -66,7 +66,8 @@ impl<'a> syscall::SchemeMut for IpScheme {
|
||||
return Err(syscall::Error::new(syscall::EACCES));
|
||||
}
|
||||
let path = str::from_utf8(url).or_else(|_| Err(syscall::Error::new(syscall::EINVAL)))?;
|
||||
let proto = u8::from_str_radix(path, 16).or(Err(syscall::Error::new(syscall::ENOENT)))?;
|
||||
let proto =
|
||||
u8::from_str_radix(path, 16).or_else(|_| Err(syscall::Error::new(syscall::ENOENT)))?;
|
||||
|
||||
let mut rx_packets = Vec::with_capacity(Smolnetd::SOCKET_BUFFER_SIZE);
|
||||
let mut tx_packets = Vec::with_capacity(Smolnetd::SOCKET_BUFFER_SIZE);
|
||||
|
||||
+11
-13
@@ -44,18 +44,17 @@ impl Smolnetd {
|
||||
|
||||
pub fn new(network_file: File, ip_file: File, udp_file: File, time_file: File) -> Smolnetd {
|
||||
let arp_cache = smoltcp::iface::SliceArpCache::new(vec![Default::default(); 8]);
|
||||
let hardware_addr = smoltcp::wire::EthernetAddress::from_str(
|
||||
&getcfg("mac").unwrap().trim(),
|
||||
).expect("Can't parse the 'mac' cfg");
|
||||
let local_ip = smoltcp::wire::IpAddress::from_str(&getcfg("ip").unwrap().trim())
|
||||
let hardware_addr = smoltcp::wire::EthernetAddress::from_str(getcfg("mac").unwrap().trim())
|
||||
.expect("Can't parse the 'mac' cfg");
|
||||
let local_ip = smoltcp::wire::IpAddress::from_str(getcfg("ip").unwrap().trim())
|
||||
.expect("Can't parse the 'ip' cfg.");
|
||||
let protocol_addrs = [smoltcp::wire::IpCidr::new(local_ip, 24).unwrap()];
|
||||
let default_gw = smoltcp::wire::IpAddress::from_str(&getcfg("ip_router").unwrap().trim())
|
||||
let default_gw = smoltcp::wire::IpAddress::from_str(getcfg("ip_router").unwrap().trim())
|
||||
.expect("Can't parse the 'ip_router' cfg.");
|
||||
// trace!("mac {:?} ip {}", hardware_addr, protocol_addrs);
|
||||
let input_queue = Rc::new(RefCell::new(VecDeque::new()));
|
||||
let network_file = Rc::new(RefCell::new(network_file));
|
||||
let network_device = NetworkDevice::new(network_file.clone(), input_queue.clone());
|
||||
let network_device = NetworkDevice::new(Rc::clone(&network_file), Rc::clone(&input_queue));
|
||||
let iface = smoltcp::iface::EthernetInterface::new(
|
||||
Box::new(network_device),
|
||||
Box::new(arp_cache) as Box<smoltcp::iface::ArpCache>,
|
||||
@@ -66,11 +65,11 @@ impl Smolnetd {
|
||||
let socket_set = Rc::new(RefCell::new(smoltcp::socket::SocketSet::new(vec![])));
|
||||
Smolnetd {
|
||||
iface,
|
||||
socket_set: socket_set.clone(),
|
||||
socket_set: Rc::clone(&socket_set),
|
||||
startup_time: Instant::now(),
|
||||
time_file,
|
||||
ip_scheme: IpScheme::new(socket_set.clone(), ip_file),
|
||||
udp_scheme: UdpScheme::new(socket_set.clone(), udp_file),
|
||||
ip_scheme: IpScheme::new(Rc::clone(&socket_set), ip_file),
|
||||
udp_scheme: UdpScheme::new(Rc::clone(&socket_set), udp_file),
|
||||
input_queue,
|
||||
network_file,
|
||||
input_buffer_pool: BufferPool::new(Self::INGRESS_PACKET_SIZE),
|
||||
@@ -97,10 +96,10 @@ impl Smolnetd {
|
||||
if self.time_file.read(&mut time)? < mem::size_of::<syscall::data::TimeSpec>() {
|
||||
panic!();
|
||||
}
|
||||
let mut time_ms = time.tv_sec * 1000i64 + (time.tv_nsec as i64) / 1_000_000i64;
|
||||
let mut time_ms = time.tv_sec * 1000i64 + i64::from(time.tv_nsec) / 1_000_000i64;
|
||||
time_ms += Smolnetd::CHECK_TIMEOUT_MS;
|
||||
time.tv_sec = time_ms / 1000;
|
||||
time.tv_nsec = ((time_ms % 1000) * 1_000_00) as i32;
|
||||
time.tv_nsec = ((time_ms % 1000) * 1_000_000) as i32;
|
||||
self.time_file
|
||||
.write_all(&time)
|
||||
.map_err(|e| Error::from_io_error(e, "Failed to write to time file"))?;
|
||||
@@ -140,8 +139,7 @@ impl Smolnetd {
|
||||
|
||||
fn get_timestamp(&self) -> u64 {
|
||||
let duration = Instant::now().duration_since(self.startup_time);
|
||||
let duration_ms = (duration.as_secs() * 1000) + (duration.subsec_nanos() / 1000000) as u64;
|
||||
duration_ms
|
||||
(duration.as_secs() * 1000) + u64::from(duration.subsec_nanos() / 1_000_000)
|
||||
}
|
||||
|
||||
fn network_fsync(&mut self) -> syscall::Result<usize> {
|
||||
|
||||
+166
-51
@@ -5,7 +5,11 @@ use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::str::FromStr;
|
||||
use std::str;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use syscall::SchemeMut;
|
||||
use syscall::data::TimeSpec;
|
||||
use syscall;
|
||||
|
||||
use device::NetworkDevice;
|
||||
@@ -14,6 +18,7 @@ use super::{Smolnetd, SocketSet};
|
||||
use port_set::PortSet;
|
||||
use super::post_fevent;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
enum Setting {
|
||||
Ttl,
|
||||
ReadTimeout,
|
||||
@@ -24,19 +29,27 @@ struct UdpHandle {
|
||||
flags: usize,
|
||||
events: usize,
|
||||
remote_endpoint: IpEndpoint,
|
||||
read_timeout: Option<TimeSpec>,
|
||||
write_timeout: Option<TimeSpec>,
|
||||
socket_handle: SocketHandle,
|
||||
}
|
||||
|
||||
struct SettingHandle {
|
||||
fd: usize,
|
||||
socket_handle: SocketHandle,
|
||||
setting: Setting,
|
||||
}
|
||||
|
||||
enum FdHandle {
|
||||
Setting(SocketHandle, Setting),
|
||||
Setting(SettingHandle),
|
||||
Socket(UdpHandle),
|
||||
}
|
||||
|
||||
impl FdHandle {
|
||||
fn socket_handle(&self) -> SocketHandle {
|
||||
match *self {
|
||||
FdHandle::Socket(UdpHandle { socket_handle, .. }) => socket_handle,
|
||||
FdHandle::Setting(socket_handle, _) => socket_handle,
|
||||
FdHandle::Socket(UdpHandle { socket_handle, .. }) |
|
||||
FdHandle::Setting(SettingHandle { socket_handle, .. }) => socket_handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +69,7 @@ impl UdpScheme {
|
||||
udp_fds: BTreeMap::new(),
|
||||
socket_set,
|
||||
// 49152..65535 is the suggested range for dynamic private ports
|
||||
port_set: PortSet::new(49152u16, 65535u16).expect("Wrong UDP port numbers"),
|
||||
port_set: PortSet::new(49_152u16, 65_535u16).expect("Wrong UDP port numbers"),
|
||||
udp_file,
|
||||
}
|
||||
}
|
||||
@@ -75,7 +88,7 @@ impl UdpScheme {
|
||||
|
||||
pub fn notify_sockets(&mut self) -> Result<()> {
|
||||
for (&fd, handle) in &self.udp_fds {
|
||||
if let &FdHandle::Socket(UdpHandle { socket_handle, .. }) = handle {
|
||||
if let FdHandle::Socket(UdpHandle { socket_handle, .. }) = *handle {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket: &mut UdpSocket = socket_set.get_mut(socket_handle).as_socket();
|
||||
if socket.can_send() {
|
||||
@@ -85,6 +98,83 @@ impl UdpScheme {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_setting(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
setting: Setting,
|
||||
buf: &mut [u8],
|
||||
) -> syscall::Result<usize> {
|
||||
let handle = self.udp_fds
|
||||
.get_mut(&fd)
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EBADF))?;
|
||||
let handle = match *handle {
|
||||
FdHandle::Socket(ref mut handle) => handle,
|
||||
_ => {
|
||||
return Err(syscall::Error::new(syscall::EBADF));
|
||||
}
|
||||
};
|
||||
let timespec = match (setting, handle.read_timeout, handle.write_timeout) {
|
||||
(Setting::ReadTimeout, Some(read_timeout), _) => read_timeout,
|
||||
(Setting::WriteTimeout, _, Some(write_timeout)) => write_timeout,
|
||||
_ => {
|
||||
return Ok(0);
|
||||
}
|
||||
};
|
||||
|
||||
if buf.len() < mem::size_of::<TimeSpec>() {
|
||||
Ok(0)
|
||||
} else {
|
||||
let count = timespec.deref().read(buf).map_err(|err| {
|
||||
syscall::Error::new(err.raw_os_error().unwrap_or(syscall::EIO))
|
||||
})?;
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
fn update_setting(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
setting: Setting,
|
||||
buf: &[u8],
|
||||
) -> syscall::Result<usize> {
|
||||
let handle = self.udp_fds
|
||||
.get_mut(&fd)
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EBADF))?;
|
||||
let handle = match *handle {
|
||||
FdHandle::Socket(ref mut handle) => handle,
|
||||
_ => {
|
||||
return Err(syscall::Error::new(syscall::EBADF));
|
||||
}
|
||||
};
|
||||
match setting {
|
||||
Setting::ReadTimeout | Setting::WriteTimeout => {
|
||||
let (timeout, count) = {
|
||||
if buf.len() < mem::size_of::<TimeSpec>() {
|
||||
(None, 0)
|
||||
} else {
|
||||
let mut timespec = TimeSpec::default();
|
||||
let count = timespec.deref_mut().write(buf).map_err(|err| {
|
||||
syscall::Error::new(err.raw_os_error().unwrap_or(syscall::EIO))
|
||||
})?;
|
||||
(Some(timespec), count)
|
||||
}
|
||||
};
|
||||
match setting {
|
||||
Setting::ReadTimeout => {
|
||||
handle.read_timeout = timeout;
|
||||
}
|
||||
Setting::WriteTimeout => {
|
||||
handle.write_timeout = timeout;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
return Ok(count);
|
||||
}
|
||||
Setting::Ttl => {}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl syscall::SchemeMut for UdpScheme {
|
||||
@@ -93,7 +183,7 @@ impl syscall::SchemeMut for UdpScheme {
|
||||
|
||||
trace!("Udp open {} ", path);
|
||||
|
||||
let mut parts = path.split("/");
|
||||
let mut parts = path.split('/');
|
||||
let remote_endpoint = parse_endpoint(parts.next().unwrap_or(""));
|
||||
let mut local_endpoint = parse_endpoint(parts.next().unwrap_or(""));
|
||||
|
||||
@@ -135,6 +225,8 @@ impl syscall::SchemeMut for UdpScheme {
|
||||
flags,
|
||||
events: 0,
|
||||
socket_handle,
|
||||
write_timeout: None,
|
||||
read_timeout: None,
|
||||
remote_endpoint: remote_endpoint,
|
||||
}),
|
||||
);
|
||||
@@ -169,53 +261,60 @@ impl syscall::SchemeMut for UdpScheme {
|
||||
fn write(&mut self, fd: usize, buf: &[u8]) -> syscall::Result<usize> {
|
||||
trace!("Upd write {} len {}", fd, buf.len());
|
||||
|
||||
let handle = self.udp_fds
|
||||
.get(&fd)
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EBADF))?;
|
||||
match *handle {
|
||||
FdHandle::Setting(_, _) => {
|
||||
//TODO: udp settings
|
||||
// pretend we've accepted
|
||||
}
|
||||
FdHandle::Socket(ref handle) => {
|
||||
if !handle.remote_endpoint.is_specified() {
|
||||
return Err(syscall::Error::new(syscall::EADDRNOTAVAIL));
|
||||
let (fd, setting) = {
|
||||
let handle = self.udp_fds
|
||||
.get(&fd)
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EBADF))?;
|
||||
|
||||
match *handle {
|
||||
FdHandle::Setting(ref setting_handle) => {
|
||||
(setting_handle.fd, setting_handle.setting)
|
||||
}
|
||||
FdHandle::Socket(ref handle) => {
|
||||
if !handle.remote_endpoint.is_specified() {
|
||||
return Err(syscall::Error::new(syscall::EADDRNOTAVAIL));
|
||||
}
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket: &mut UdpSocket =
|
||||
socket_set.get_mut(handle.socket_handle).as_socket();
|
||||
socket
|
||||
.send_slice(buf, handle.remote_endpoint)
|
||||
.expect("Can't send slice");
|
||||
return Ok(buf.len());
|
||||
}
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket: &mut UdpSocket = socket_set.get_mut(handle.socket_handle).as_socket();
|
||||
socket
|
||||
.send_slice(buf, handle.remote_endpoint)
|
||||
.expect("Can't send slice");
|
||||
}
|
||||
}
|
||||
Ok(buf.len())
|
||||
};
|
||||
self.update_setting(fd, setting, buf)
|
||||
}
|
||||
|
||||
fn read(&mut self, fd: usize, buf: &mut [u8]) -> syscall::Result<usize> {
|
||||
trace!("udp read {} {}", fd, buf.len());
|
||||
let handle = self.udp_fds.get(&fd).ok_or_else(|| {
|
||||
trace!("UDP read EBADF {}", fd);
|
||||
syscall::Error::new(syscall::EBADF)
|
||||
})?;
|
||||
match *handle {
|
||||
FdHandle::Setting(_, _) => {
|
||||
//TODO: udp settings
|
||||
Ok(buf.len())
|
||||
}
|
||||
FdHandle::Socket(ref handle) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket: &mut UdpSocket = socket_set.get_mut(handle.socket_handle).as_socket();
|
||||
if socket.can_recv() {
|
||||
let (length, _) = socket.recv_slice(buf).expect("Can't receive slice");
|
||||
trace!("Upd read {}", fd);
|
||||
Ok(length)
|
||||
} else if handle.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(syscall::Error::new(syscall::EWOULDBLOCK))
|
||||
let (fd, setting) = {
|
||||
let handle = self.udp_fds.get(&fd).ok_or_else(|| {
|
||||
trace!("UDP read EBADF {}", fd);
|
||||
syscall::Error::new(syscall::EBADF)
|
||||
})?;
|
||||
match *handle {
|
||||
FdHandle::Setting(ref setting_handle) => {
|
||||
(setting_handle.fd, setting_handle.setting)
|
||||
}
|
||||
FdHandle::Socket(ref handle) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket: &mut UdpSocket =
|
||||
socket_set.get_mut(handle.socket_handle).as_socket();
|
||||
return if socket.can_recv() {
|
||||
let (length, _) = socket.recv_slice(buf).expect("Can't receive slice");
|
||||
trace!("Upd read {}", fd);
|
||||
Ok(length)
|
||||
} else if handle.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(syscall::Error::new(syscall::EWOULDBLOCK))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.get_setting(fd, setting, buf)
|
||||
}
|
||||
|
||||
fn dup(&mut self, fd: usize, buf: &[u8]) -> syscall::Result<usize> {
|
||||
@@ -230,15 +329,29 @@ impl syscall::SchemeMut for UdpScheme {
|
||||
let socket_handle = handle.socket_handle();
|
||||
|
||||
match path {
|
||||
"ttl" => FdHandle::Setting(socket_handle, Setting::Ttl),
|
||||
"read_timeout" => FdHandle::Setting(socket_handle, Setting::ReadTimeout),
|
||||
"write_timeout" => FdHandle::Setting(socket_handle, Setting::WriteTimeout),
|
||||
"ttl" => FdHandle::Setting(SettingHandle {
|
||||
socket_handle,
|
||||
fd,
|
||||
setting: Setting::Ttl,
|
||||
}),
|
||||
"read_timeout" => FdHandle::Setting(SettingHandle {
|
||||
socket_handle,
|
||||
fd,
|
||||
setting: Setting::ReadTimeout,
|
||||
}),
|
||||
"write_timeout" => FdHandle::Setting(SettingHandle {
|
||||
socket_handle,
|
||||
fd,
|
||||
setting: Setting::WriteTimeout,
|
||||
}),
|
||||
_ => {
|
||||
let remote_endpoint = parse_endpoint(path);
|
||||
if let &mut FdHandle::Socket(ref udp_handle) = handle {
|
||||
if let FdHandle::Socket(ref udp_handle) = *handle {
|
||||
FdHandle::Socket(UdpHandle {
|
||||
flags: udp_handle.flags,
|
||||
events: udp_handle.events,
|
||||
read_timeout: udp_handle.read_timeout,
|
||||
write_timeout: udp_handle.write_timeout,
|
||||
remote_endpoint: if remote_endpoint.is_specified() {
|
||||
remote_endpoint
|
||||
} else {
|
||||
@@ -250,6 +363,8 @@ impl syscall::SchemeMut for UdpScheme {
|
||||
FdHandle::Socket(UdpHandle {
|
||||
flags: 0,
|
||||
events: 0,
|
||||
read_timeout: None,
|
||||
write_timeout: None,
|
||||
remote_endpoint: remote_endpoint,
|
||||
socket_handle,
|
||||
})
|
||||
@@ -279,7 +394,7 @@ impl syscall::SchemeMut for UdpScheme {
|
||||
.get_mut(&fd)
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EBADF))?;
|
||||
match *handle {
|
||||
FdHandle::Setting(_, _) => Err(syscall::Error::new(syscall::EBADF)),
|
||||
FdHandle::Setting(_) => Err(syscall::Error::new(syscall::EBADF)),
|
||||
FdHandle::Socket(ref mut handle) => {
|
||||
handle.events = events;
|
||||
Ok(fd)
|
||||
@@ -301,7 +416,7 @@ impl syscall::SchemeMut for UdpScheme {
|
||||
}
|
||||
|
||||
fn parse_endpoint(socket: &str) -> IpEndpoint {
|
||||
let mut socket_parts = socket.split(":");
|
||||
let mut socket_parts = socket.split(':');
|
||||
let host = IpAddress::Ipv4(
|
||||
Ipv4Address::from_str(socket_parts.next().unwrap_or(""))
|
||||
.unwrap_or_else(|_| Ipv4Address::new(0, 0, 0, 0)),
|
||||
|
||||
Reference in New Issue
Block a user