Hello all. Working on an CLI application to find directories on web applications.
I can get a recursive version working fine by cloning items repeatedly, but I want to make it more efficient with less overhead before I start attempting to make it multi-threaded (Which I know will require Arc).
Going with the philosophy I have read online:
- Make it work first, then make it better
- Make it single-threaded first, then multi-threaded.
Currently, I am trying to use RCs, since its still single-threaded, and using this as an opportunity to learn RCs and eventually Arc as I have no previous experience with pointers or parallelism. But i found that even if I Rc::Clone(PathBuf) at the beginning of my loop the code still throws ownership/move issues.
error[E0507]: cannot move out of an `Rc`
--> src/main.rs:54:25
|
54 | for line in this_reader.lines() {
| ^^^^^^^^^^^^-------
| | |
| | value moved due to this method call
| move occurs because value has type `std::io::BufReader<std::fs::File>`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
The code is small enough that I will just cp/pst it here if you would like to take a look. Really open to any criticism as I want to learn, but also learn to do it right. But at this moment also really want to understand with a good example of how to use pointers in Rust. =/
#[derive(Parser, Debug)]
#[command(author="Andrew ", version="0.1.0",
about="directory busting tool", long_about=None)]
struct Args {
/// required: The wordlist to use for the busting
#[arg(short, long, required=true, value_parser=clap::value_parser!(PathBuf))]
wordlist: PathBuf,
/// required: The target URL to go against
#[arg(short, long, required=true, value_parser=clap::value_parser!(Uri))]
target: Uri,
/// optional: Cookies to use for authenticated requests
#[arg(short, long, value_parser=clap::value_parser!(Option<String>))]
cookie: Option<String>,
/// optional: Perform the scan as a recursive scan
#[arg(short, long, default_value_t=false)]
recursive: bool,
}
fn main() {
let cli = Rc::new(Args::parse());
let mut found_dirs: Vec<String> = Vec::new();
let word_list = cli.wordlist
.to_owned();
let file = File::open(word_list)
.expect("Could not read file.");
let reader = Rc::new(BufReader::new(file));
loop {
let this_reader = Rc::clone(&reader);
let mut count = 0;
if found_dirs.is_empty() && count == 0 {
count += 1;
for line in this_reader.lines() {
let result = make_request(line.unwrap(), Rc::clone(&cli));
if result == String::from("NONE") {
continue;
} else {
found_dirs.push(result);
}
}
} else if count > 0 && found_dirs.is_empty() {
break;
} else {
let ext = found_dirs.pop().unwrap();
for line in this_reader.lines() {
let new_line = format!("{}/{}", ext, line.unwrap());
make_request(new_line, Rc::clone(&cli));
}
}
}
}
#[tokio::main]
async fn make_request(line: String, args: Rc<Args>) -> String {
let target = &args.target;
let cookie = &args.cookie;
match cookie.is_some() {
true => {
let url = format!("{}{}", &target, &line);
let client = reqwest::Client::new();
let req_resp = client.get(&url)
.header(COOKIE, cookie.as_ref().unwrap())
.send()
.await
.unwrap()
.status()
.as_u16();
if req_resp == 200 {
println!("| {} | {} | {} | => Dir found, adding to list", req_resp, url, &line);
return line;
} else {
println!("| {} | {} | {} |", req_resp, url, &line);
}
},
false => {
let url = format!("{}{}", &target, &line);
let client = reqwest::Client::new();
let req_resp = client.get(&url)
.send()
.await
.unwrap()
.status()
.as_u16();
if req_resp == 200 {
println!("| {} | {} | {} | => Dir found, adding to list", req_resp, url, &line);
return line;
} else {
println!("| {} | {} | {} |", req_resp, url, &line);
}
},
};
return String::from("NONE");
}