Inital commit
This commit is contained in:
commit
03e18b00b1
1
.clippy.toml
Normal file
1
.clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
single-char-binding-names-threshold = 9
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
130
Cargo.lock
generated
Normal file
130
Cargo.lock
generated
Normal file
@ -0,0 +1,130 @@
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rip"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
|
||||
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
|
||||
"checksum redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "679da7508e9a6390aeaf7fbd02a800fdc64b73fe2204dd2c8ae66d22d9d5ad5d"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "rip"
|
||||
version = "0.1.0"
|
||||
authors = ["Kevin Cotugno <kevin@kevincotugno.com>"]
|
||||
|
||||
[[bin]]
|
||||
name = "rip"
|
||||
path = "src/bin/client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "rip-server"
|
||||
path = "src/bin/server.rs"
|
||||
|
||||
[lib]
|
||||
name = "rip"
|
||||
path = "src/rip/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "~2.32"
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright 2018 Kevin Cotugno
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
51
src/bin/client.rs
Normal file
51
src/bin/client.rs
Normal file
@ -0,0 +1,51 @@
|
||||
extern crate clap;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use std::io::Write;
|
||||
use std::net::IpAddr;
|
||||
|
||||
fn main() {
|
||||
let port = rip::DEFAULT_PORT.to_string();
|
||||
let args = App::new(rip::NAME)
|
||||
.version(rip::VERSION)
|
||||
.author(rip::AUTHOR)
|
||||
.about(rip::ABOUT)
|
||||
.arg(
|
||||
Arg::with_name("port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.help("Set a different port from the default of 44353")
|
||||
.default_value(&port)
|
||||
.takes_value(true),
|
||||
).arg(
|
||||
Arg::with_name("host")
|
||||
.help("Sets the Rip server host")
|
||||
.required(true)
|
||||
.index(1),
|
||||
).get_matches();
|
||||
|
||||
match run(&args) {
|
||||
Ok(ip) => {
|
||||
print!("{}", ip);
|
||||
std::io::stdout().flush().unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(args: &ArgMatches) -> Result<IpAddr, String> {
|
||||
let port_str = args.value_of("port").unwrap();
|
||||
let port: u16 = match port_str.parse() {
|
||||
Ok(v) => v,
|
||||
Err(_) => return Err("Port must be a valid unsigned 16bit integer".to_string()),
|
||||
};
|
||||
|
||||
let dest = match rip::parse_socket_addr(args.value_of("host").unwrap(), port) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
rip::run_client(dest)
|
||||
}
|
36
src/bin/server.rs
Normal file
36
src/bin/server.rs
Normal file
@ -0,0 +1,36 @@
|
||||
extern crate clap;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
|
||||
const NAME: &str = "rip-server";
|
||||
|
||||
fn main() {
|
||||
let port = rip::DEFAULT_PORT.to_string();
|
||||
let args = App::new(NAME)
|
||||
.version(rip::VERSION)
|
||||
.author(rip::AUTHOR)
|
||||
.about(rip::ABOUT)
|
||||
.arg(
|
||||
Arg::with_name("port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.help("Set a different port from the default of 44353")
|
||||
.default_value(&port)
|
||||
.takes_value(true),
|
||||
).get_matches();
|
||||
|
||||
let result = run(&args);
|
||||
if result.is_err() {
|
||||
println!("{}", result.err().unwrap());
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run(args: &ArgMatches) -> Result<(), String> {
|
||||
let port_str = args.value_of("port").unwrap();
|
||||
let port: u16 = match port_str.parse() {
|
||||
Ok(v) => v,
|
||||
Err(_) => return Err("Port must be a valid unsigned 16bit integer".to_string()),
|
||||
};
|
||||
|
||||
rip::run_server(port)
|
||||
}
|
255
src/rip/lib.rs
Normal file
255
src/rip/lib.rs
Normal file
@ -0,0 +1,255 @@
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket};
|
||||
use std::time::Duration;
|
||||
|
||||
pub const NAME: &str = "rip";
|
||||
pub const VERSION: &str = "0.1.0";
|
||||
pub const ABOUT: &str = "Rip is a simple client/server to retrieving a public IP via UDP.";
|
||||
pub const AUTHOR: &str = "Kevin Cotugno <kevin@kevincotugno.com>";
|
||||
|
||||
pub const DEFAULT_PORT: u16 = 44353;
|
||||
|
||||
const MAPPED_IPV4_KEY: u16 = 0xFFFF;
|
||||
|
||||
pub fn run_client(dest: SocketAddr) -> Result<IpAddr, String> {
|
||||
let socket = match open_socket(dest.is_ipv6(), 0) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return Err(format!("{}", err)),
|
||||
};
|
||||
|
||||
socket
|
||||
.set_read_timeout(Some(Duration::from_secs(10)))
|
||||
.unwrap();
|
||||
|
||||
do_request(dest, &socket)
|
||||
}
|
||||
|
||||
pub fn run_server(port: u16) -> Result<(), String> {
|
||||
let socket = match open_socket(true, port) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return Err(format!("{}", err)),
|
||||
};
|
||||
eprintln!("Listening on: {:?}", socket);
|
||||
|
||||
let mut unused = [0; 1024];
|
||||
loop {
|
||||
let (_, src) = match socket.recv_from(&mut unused) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return Err(format!("Failed to read message: {}", err)),
|
||||
};
|
||||
eprintln!("Request from: {}", format!("{}", src.ip()));
|
||||
|
||||
let (msg, size) = build_msg(src.ip());
|
||||
let res = socket.send_to(&msg[..size], src);
|
||||
if res.is_err() {
|
||||
eprintln!(
|
||||
"error sending response to {}, with err: ",
|
||||
res.err().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_socket_addr(host: &str, port: u16) -> Result<SocketAddr, String> {
|
||||
match parse_ip(host) {
|
||||
Ok(ip) => Ok(SocketAddr::new(ip, port)),
|
||||
Err(v) => Err(v),
|
||||
}
|
||||
}
|
||||
|
||||
fn open_socket(ipv6: bool, port: u16) -> io::Result<UdpSocket> {
|
||||
UdpSocket::bind(if ipv6 {
|
||||
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
|
||||
} else {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_ip(host: &str) -> Result<IpAddr, String> {
|
||||
match host.parse() {
|
||||
Ok(ip) => Ok(ip),
|
||||
Err(err) => Err(format!(
|
||||
"Unable to parse host: {}, with error: {}",
|
||||
host, err
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn do_request(dest: SocketAddr, socket: &UdpSocket) -> Result<IpAddr, String> {
|
||||
if socket.connect(dest).is_err() {
|
||||
return Err(format!(
|
||||
"Unable to connect: {}",
|
||||
socket.take_error().unwrap().unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
if socket.send(&[]).is_err() {
|
||||
return Err(format!(
|
||||
"Failed to send request: {}",
|
||||
socket.take_error().unwrap().unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
let count = match socket.recv(&mut buf) {
|
||||
Ok(size) => size,
|
||||
Err(error) => return Err(format!("Error reading response: {}", error)),
|
||||
};
|
||||
|
||||
parse_raw_msg(&buf[..count])
|
||||
}
|
||||
|
||||
fn parse_raw_msg(data: &[u8]) -> Result<IpAddr, String> {
|
||||
if data.is_empty() {
|
||||
return Err(String::from("empty message"));
|
||||
}
|
||||
|
||||
match data[0] {
|
||||
4 => {
|
||||
if data.len() > 4 {
|
||||
Ok(IpAddr::V4(Ipv4Addr::new(
|
||||
data[1], data[2], data[3], data[4],
|
||||
)))
|
||||
} else {
|
||||
println!("{}", data.len());
|
||||
Err(String::from("wrong number of octets for IPv4 address"))
|
||||
}
|
||||
}
|
||||
6 => {
|
||||
if data.len() > 16 {
|
||||
Ok(IpAddr::V6(Ipv6Addr::new(
|
||||
rebuild_seg(data[1], data[2]),
|
||||
rebuild_seg(data[3], data[4]),
|
||||
rebuild_seg(data[5], data[6]),
|
||||
rebuild_seg(data[7], data[8]),
|
||||
rebuild_seg(data[9], data[10]),
|
||||
rebuild_seg(data[11], data[12]),
|
||||
rebuild_seg(data[13], data[14]),
|
||||
rebuild_seg(data[15], data[16]),
|
||||
)))
|
||||
} else {
|
||||
Err(String::from("wrong number of segments for IPv6 address"))
|
||||
}
|
||||
}
|
||||
_ => Err(String::from("invalid IP type")),
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild_seg(i: u8, j: u8) -> u16 {
|
||||
let mut x: u16 = u16::from(i);
|
||||
x <<= 8;
|
||||
x |= u16::from(j);
|
||||
x
|
||||
}
|
||||
|
||||
fn build_msg(ip: IpAddr) -> ([u8; 17], usize) {
|
||||
let mut msg = [0; 17];
|
||||
|
||||
let populate = |octets: &[u8], dest: &mut [u8]| {
|
||||
if octets.len() == 4 {
|
||||
dest[0] = 4;
|
||||
} else {
|
||||
dest[0] = 6;
|
||||
}
|
||||
|
||||
for (i, oct) in octets.iter().enumerate() {
|
||||
dest[i + 1] = *oct;
|
||||
}
|
||||
};
|
||||
|
||||
let size = match ip {
|
||||
IpAddr::V4(v4) => {
|
||||
populate(&v4.octets()[..], &mut msg);
|
||||
5
|
||||
}
|
||||
IpAddr::V6(v6) => {
|
||||
let v4 = v6.to_ipv4();
|
||||
|
||||
if v4.is_some() && v6.segments()[5] == MAPPED_IPV4_KEY {
|
||||
populate(&v4.unwrap().octets()[..], &mut msg);
|
||||
5
|
||||
} else {
|
||||
populate(&v6.octets()[..], &mut msg);
|
||||
17
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(msg, size)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parses_valid_ipv4() -> Result<(), String> {
|
||||
match parse_ip("1.2.3.4") {
|
||||
Ok(v) => ipv4_with_octets(v, 1, 2, 3, 4),
|
||||
Err(error) => Err(String::from(format!("Should be ok, {}", error))),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_valid_ipv6() -> Result<(), String> {
|
||||
match parse_ip("2423:33:dfe3::1") {
|
||||
Ok(v) => ipv6_with_segments(v, 0x2423, 0x33, 0xdfe3, 0, 0, 0, 0, 1),
|
||||
Err(error) => Err(String::from(format!("Should be ok, {}", error))),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_invalid_ips() -> Result<(), String> {
|
||||
let host = "2423:33:dfe3:invalid::1";
|
||||
match parse_ip(host) {
|
||||
Ok(v) => Err(String::from(format!("Should have returned error, {}", v))),
|
||||
Err(err) => {
|
||||
let expected = format!(
|
||||
"Unable to parse host: {}, with error: invalid IP address syntax",
|
||||
host
|
||||
);
|
||||
if err != expected {
|
||||
Err(String::from(expected))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ipv4_with_octets(ip: IpAddr, a: u8, b: u8, c: u8, d: u8) -> Result<(), String> {
|
||||
match ip {
|
||||
IpAddr::V4(addr) => {
|
||||
if addr.octets() == [a, b, c, d] {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(String::from(format!("octets do not match: {}", addr)))
|
||||
}
|
||||
}
|
||||
IpAddr::V6(_) => Err(String::from("is a ipv6 address")),
|
||||
}
|
||||
}
|
||||
|
||||
fn ipv6_with_segments(
|
||||
ip: IpAddr,
|
||||
a: u16,
|
||||
b: u16,
|
||||
c: u16,
|
||||
d: u16,
|
||||
e: u16,
|
||||
f: u16,
|
||||
g: u16,
|
||||
h: u16,
|
||||
) -> Result<(), String> {
|
||||
match ip {
|
||||
IpAddr::V4(_) => Err(String::from("is a ipv4 address")),
|
||||
IpAddr::V6(addr) => {
|
||||
if addr.segments() == [a, b, c, d, e, f, g, h] {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(String::from(format!("segments do not match: {}", addr)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user