sherlock 5.0.5

🔍 Hunt down social media accounts by username across social networks
Documentation
use rand::seq::SliceRandom;
use std::{collections::HashMap, sync::Arc, time::Duration};

use reqwest::{
    Client, Response,
    header::{HeaderMap, HeaderName, HeaderValue},
};

use crate::{
    query::QueryError,
    sherlock_target_manifest::{RequestMethod, TargetInfo},
};

const USER_AGENTS: [&str; 8] = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 12.0; rv:115.0) Gecko/20100101 Firefox/115.0",
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0",
];

#[derive(Debug)]
pub struct RequestResult {
    pub username: Arc<str>,
    pub site: Arc<str>,
    pub info: Arc<TargetInfo>,
    pub url: String,
    pub url_probe: String,
    pub response: Result<Response, QueryError>,
    pub query_time: Duration,
}

pub struct RequestParams {
    pub url: String,
    pub headers: Option<HashMap<String, String>>,
    pub timeout: Duration,
    pub method: RequestMethod,
    pub request_payload: Option<String>,
    pub user_agent: Option<String>,
}

pub async fn make_request(client: &Client, params: RequestParams) -> color_eyre::Result<Response> {
    let RequestParams {
        url,
        headers,
        timeout,
        method,
        request_payload,
        user_agent,
    } = params;

    let headers_map = headers
        .unwrap_or_default()
        .into_iter()
        .map(|(key, value)| {
            let header_name = key.parse::<HeaderName>().unwrap();
            let header_value = value.parse::<HeaderValue>().unwrap();
            (header_name, header_value)
        })
        .collect::<HeaderMap>();

    let req_method = match method {
        RequestMethod::Get => reqwest::Method::GET,
        RequestMethod::Post => reqwest::Method::POST,
        RequestMethod::Put => reqwest::Method::PUT,
        RequestMethod::Head => reqwest::Method::HEAD,
    };

    let random_agent = USER_AGENTS
        .choose(&mut rand::thread_rng())
        .unwrap()
        .to_owned();

    let req_user_agent = user_agent.unwrap_or(random_agent.into());

    // Use the shared client and configure per-request settings
    let resp = client
        .request(req_method, &url)
        .headers(headers_map)
        .header("User-Agent", req_user_agent)
        .timeout(timeout)
        .json(&request_payload)
        .send()
        .await?;

    Ok(resp)
}