// A program to scan emails for phishing links. // Copyright (C) 2019 Harald Eilertsen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use phisher; use clap::{clap_app, crate_name, crate_authors, crate_version, crate_description}; use std::boxed::Box; use std::error::Error; use std::fs::{ File, remove_file }; use std::io::{ BufRead, BufReader, Write }; use std::os::unix::net::{ UnixListener, UnixStream }; use std::result::Result; use std::string::ToString; use std::sync::Arc; use std::thread; use std::time::SystemTime; fn main() { let args = clap_app!(app => (name: crate_name!()) (version: crate_version!()) (author: crate_authors!()) (about: crate_description!()) (@arg PHISHTANK: --phishtank +required +takes_value "The phishtank json data file to use.") ).get_matches(); let start_time = SystemTime::now(); let filename = args.value_of("PHISHTANK").unwrap(); if let Ok(file) = File::open(filename) { let tank = Arc::new(phisher::load_phistank(BufReader::new(file)).unwrap()); println!("Loaded {} phishes in {} seconds!", tank.phishes.len(), start_time.elapsed().unwrap().as_secs()); match run_server(tank) { Ok(_) => println!("Exiting."), Err(e) => eprintln!("Error: {}, terminating.", e.to_string()) }; } else { eprintln!("Could not open file {}.\nIs the filename correct?", filename); } } fn run_server(tank: Arc) -> Result<(), Box> { let bind_address = "/tmp/phisher"; let listener = UnixListener::bind(&bind_address)?; println!("Listening for connections on {}...", &bind_address); for stream in listener.incoming() { match stream { Ok(stream) => { let t = Arc::clone(&tank); thread::spawn(move || { if let Err(e) = handle_connection(stream, t) { eprintln!("Client was disconnected: {}", e.to_string()); } }); } Err(e) => { eprintln!("Received error: {}", e.to_string()); break; } } } Ok(remove_file("/tmp/phisher")?) } // Handle a connected client. // // Once a client is connected, it can request the status of multiple // URLs before closing the connection. Each URL must be passed in on // a separate line, and the results are returned in the same order // as the URLs were passed in. // fn handle_connection(mut stream: UnixStream, tank: Arc) -> Result<(), Box> { // Clone the stream so we get another ref to the underlying socket, // then wrap it in a BufReader, so we can use read_line on it. let mut bufreader = BufReader::new(stream.try_clone()?); loop { let mut buffer = String::new(); bufreader.read_line(&mut buffer)?; let input = buffer.trim_end(); if input == "bye" { stream.write(b"bye.")?; break; } if tank.is_phish(&input) { stream.write(b"it's a phish!\n")?; } else { stream.write(b"good url.\n")?; } } Ok(()) }