Sounds more dramatic than it is...
I'm usually creating web apps with php, js, go or python. I've played around with C, C++ and C# but never used them in a bigger project.
So why rust? I've heard a lot of people talk about it. It's supposed to be pretty fast and provides some additional features which, regarding app security, are great as well. At the end d0nutptr motivated me to participate in his giveaway/challenge.
I recently thought about what would happen if you try to race against a firewall / IDS; Are they fast enough, or could you "squeeze" in a bunch of request before the client gets blocked? Basically a one-shot port scan.
This application will scan a port range of a given target simultaneously, in an attempt to race against a firewall IDS. The result can be saved or printed as csv, json or xml.
Well the time has come to an end. I had a lot of plans and even though I failed to realize all of them, I'm still a bit proud of having realized at least the core feature (port check). This might not sound like much but for my first steps at rust, I'm pretty happy with it :)
The exports and udp / response filtering support is still missing, the code isn't cleaned up and the
Options
are never used. Oh and you can't spawn more than ~16337 threads - I still haven't figured out
why that's the case.
I spend the most time trying to understand how Response
works and how to handle errors. I'm still
not fully sure, but I feel comfortable to play around with them some more.
Rust definitely isn't like any other language I've played around with.
It is extremely strict, has a unique logic and app "life cycle".
Have I failed my quest? Yes and no, the application isn't finished but my second goal, to learn some rust was a success!
I tried to document my journey and took some notes along the way. Those are all listed below under Timeline.
cargo run
looks good:
Compiling rust_test v0.1.0 (/mnt/raid1/projects/rust_test)
Finished dev [unoptimized + debuginfo] target(s) in 0.52s
Running `target/debug/rust_test`
Hello, world!
c.wait();
lock would release
c.wait();
lock and watched the behavior
c.wait();
lock is released as soon as the "last" thread got pushed into the "Barrier Arc"Scanner
u16
seems to be the type I'm looking for
use std::sync::{Arc, Barrier};
use std::thread::JoinHandle;
use std::time::Duration;
struct Scanner {
handles: Vec<JoinHandle<()>>,
barrier: Arc
target: String, start_port: u16, max_port: u16, timeout: Duration }
- The Scanner struct now need to have some methods
- Searching for "rust struct methods"
- https://doc.rust-lang.org/book/ch05-03-method-syntax.html
- `impl StructName` is the magic syntax
- Turns out you have to use `&mut self` instead of `self` if the method "mutates?" / updates an attribute
- `pub(crate)` in front of a function makes it accessible outside the file
- The scanner might need some options, so I added a new struct called `Options` and added it to the Scanner
```rust
struct Options {
pub response: bool, // Check if the connection returns any bytes
pub udp: bool, // use udp
pub tcp: bool // use tcp
}
set_attribute_name(attr: Type)
and attribute_name() -> Type
for the getter#[derive(Builder)]
pub(crate) struct Options {
pub response: bool, // Check if the connection returns any bytes
pub udp: bool, // use udp
pub tcp: bool // use tcp
}
Putting everything together:
fn main() {
let target = String::from("somedomain.tld");
let opt = scanner::Options::default().udp(false).tcp(true).response(false).build().unwrap();
let mut s = scanner::build_scanner(opt);
s.set_target(target);
s.set_port_range(0, 65535);
s.set_timeout(Duration::from_secs(10));
}
Box<dyn std::error::Error>
many errors can be returned / handled
match
correctly used?
Builder
in this scope"
Builder
in this scope"extern crate
loading macros must be at the crate root"
extern crate derive_builder;
has to be placed within the main.rsderive_builder
dependency was missing inside the Cargo.toml file*self
which is behind a mutable reference"
*self
which is behind a mutable reference"
rustc --explain E0507
Refactoring the Options struct and the main method to implement the recent changes
#[derive(Default)]
pub(crate) struct Options {
pub response: bool,
// Check if the connection returns any bytes
pub udp: bool,
// use udp
pub tcp: bool, // use tcp
}
fn main() {
let target = String::from("somedomain.tld");
let mut opt = scanner::build_options();
*opt.udp_mut() = false;
*opt.tcp_mut() = false;
*opt.response_mut() = false;
let mut s = scanner::build_scanner(opt);
s.set_target(target);
s.set_port_range(0, 65535);
s.set_timeout(Duration::from_secs(10));
match s.start() {
Ok(()) => println!("Completed"),
Err(e) => {
match e {
ScannerError::InvalidPortRange => println!("Scanner error: {}", e),
}
}
};
}
RUST_BACKTRACE=1 cargo run
can be used to get additional information
RUST_BACKTRACE=full
provides even more detailcat /proc/sys/kernel/threads-max
255609
- So this isn't the problemfree -h
total used free shared buff/cache available
Mem: 31Gi 22Gi 1,4Gi 312Mi 7,5Gi 8,2Gi
Swap: 11Gi 46Mi 11Gi
total used free shared buff/cache available
Mem: 31Gi 14Gi 9,2Gi 266Mi 7,5Gi 15Gi
Swap: 11Gi 46Mi 11Gi
rust failed to spawn thread "Resource temporarily unavailable"
thread::sleep(Duration::from_millis(1))
TasksMax
set in my environment?
cat /etc/systemd/system.conf | grep "TasksMax"
returns a disabled config parameter "#DefaultTasksMax="cat /etc/systemd/user.conf | grep "TasksMax"
returns nothingLimiting myself to fewer threads inorder to be able to continue
std::io::Error
inside the ScannerError
?
Vec
, found struct std::io::Error
"
Vec
, found struct std::io::Error
"
time
in tokio
"
time
in tokio
"
tokio::time::timeout
provides this featureResult<std::net::TcpStream, std::io::Error>
is not a future" after implementing the timeout
Result<std::net::TcpStream, std::io::Error>
is not a future"Future
is not implemented for Result<std::net::TcpStream, std::io::Error>
"
Future
"?
Future
"
Future
is not implemented for Result<std::net::TcpStream, std::io::Error>
"match tokio::time::timeout(self.timeout, TcpStream::connect(&target)).await
inside a thread isn't working because some Future
is missing
Future
for tokio::time::Timeout<Result<std::net::TcpStream, std::io::Error>>
what ever that's supposed to meantimeout
"
rustc --explain E0277
cargo add dependency_name@version
connect_timeout
self
has an anonymous lifetime '_
but it needs to satisfy a 'static
lifetime requirement"
let timeout = self.timeout;
this fixes the issuecargo run
with arguments, you have to appended "--" and use it like this: "cargo run -- --max_port 25 --start_port 20 target.tld"