mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 05:26:59 +00:00
fix: january ip redirects & domain resolver (#738)
* fix: properly block private ip ranges I'm a dunce and forgot that domains do in fact resolve to ips. * fix: reimplement max redirects * fix: remove my debug error * fix: actually check redirect urls kind of the whole point of this thing.
This commit is contained in:
@@ -10,9 +10,10 @@ use reqwest::{
|
||||
use revolt_config::{config, report_internal_error};
|
||||
use revolt_files::{create_thumbnail, decode_image, image_size_vec, is_valid_image, video_size};
|
||||
use revolt_models::v0::{Embed, Image, ImageSize, Video};
|
||||
use revolt_result::{create_error, Error, Result};
|
||||
use revolt_result::{create_error, Error, Result, ToRevoltError};
|
||||
use std::{
|
||||
io::{Cursor, Write},
|
||||
str::FromStr,
|
||||
time::Duration,
|
||||
};
|
||||
use url::{Host, Url};
|
||||
@@ -22,15 +23,7 @@ lazy_static! {
|
||||
static ref CLIENT: Client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(10)) // TODO config
|
||||
.connect_timeout(Duration::from_secs(5)) // TODO config
|
||||
.redirect(redirect::Policy::custom(|attempt| {
|
||||
if attempt.previous().len() > 5 { // TODO config
|
||||
attempt.error("too many redirects")
|
||||
} else if attempt.url().host_str() == Some("proxy.stoatusercontent.com") { // TODO config
|
||||
attempt.stop()
|
||||
} else {
|
||||
attempt.follow()
|
||||
}
|
||||
}))
|
||||
.redirect(redirect::Policy::none())
|
||||
.build()
|
||||
.expect("reqwest Client");
|
||||
|
||||
@@ -63,25 +56,14 @@ lazy_static! {
|
||||
.build();
|
||||
|
||||
static ref IP_BLOCKLIST: IpFilter = IpFilter::block(&[
|
||||
"10.0.0.0/8", // something something modern problem require modern solutions
|
||||
"10.0.0.0/8",
|
||||
"192.168.0.0/16",
|
||||
"172.16.0.0/16",
|
||||
"172.17.0.0/16",
|
||||
"172.18.0.0/16",
|
||||
"172.19.0.0/16",
|
||||
"172.20.0.0/16",
|
||||
"172.21.0.0/16",
|
||||
"172.22.0.0/16",
|
||||
"172.23.0.0/16",
|
||||
"172.24.0.0/16",
|
||||
"172.25.0.0/16",
|
||||
"172.26.0.0/16",
|
||||
"172.27.0.0/16",
|
||||
"172.28.0.0/16",
|
||||
"172.29.0.0/16",
|
||||
"172.30.0.0/16",
|
||||
"172.31.0.0/16",
|
||||
"172.32.0.0/16"]
|
||||
"127.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
"169.254.0.0/16",
|
||||
"::1",
|
||||
"fc00::/7"
|
||||
]
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
@@ -280,11 +262,14 @@ impl Request {
|
||||
|
||||
/// Send a new request to a service
|
||||
pub async fn new(url: Url) -> Result<Request> {
|
||||
let mut url = url;
|
||||
let url_host_str = url.host_str().ok_or(create_error!(ProxyError))?.to_string();
|
||||
|
||||
Request::url_is_blacklisted(&url).await?;
|
||||
let mut redirect_count = 0;
|
||||
|
||||
let response = CLIENT
|
||||
loop {
|
||||
let response = CLIENT
|
||||
.get(url)
|
||||
.header(
|
||||
"User-Agent",
|
||||
@@ -299,23 +284,42 @@ impl Request {
|
||||
.await
|
||||
.map_err(|_| create_error!(ProxyError))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
tracing::error!("{:?}", response);
|
||||
return Err(create_error!(ProxyError));
|
||||
if response.status().is_redirection() {
|
||||
redirect_count += 1;
|
||||
|
||||
if redirect_count > 5 {
|
||||
return Err(create_error!(ProxyError));
|
||||
}
|
||||
if let Some(location) = response.headers().get("location") {
|
||||
let location = location.to_str().map_err(|_| create_error!(ProxyError))?;
|
||||
url = Url::from_str(location).to_internal_error()?;
|
||||
|
||||
if !Request::url_is_blacklisted(&url).await? {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
return Err(create_error!(ProxyError));
|
||||
}
|
||||
}
|
||||
|
||||
if !response.status().is_success() {
|
||||
tracing::error!("{:?}", response);
|
||||
return Err(create_error!(ProxyError));
|
||||
}
|
||||
|
||||
let content_type = response
|
||||
.headers()
|
||||
.get(CONTENT_TYPE)
|
||||
.ok_or(create_error!(ProxyError))?
|
||||
.to_str()
|
||||
.map_err(|_| create_error!(ProxyError))?;
|
||||
|
||||
let mime: mime::Mime = content_type
|
||||
.parse()
|
||||
.map_err(|_| create_error!(ProxyError))?;
|
||||
|
||||
return Ok(Request { response, mime });
|
||||
}
|
||||
|
||||
let content_type = response
|
||||
.headers()
|
||||
.get(CONTENT_TYPE)
|
||||
.ok_or(create_error!(ProxyError))?
|
||||
.to_str()
|
||||
.map_err(|_| create_error!(ProxyError))?;
|
||||
|
||||
let mime: mime::Mime = content_type
|
||||
.parse()
|
||||
.map_err(|_| create_error!(ProxyError))?;
|
||||
|
||||
Ok(Request { response, mime })
|
||||
}
|
||||
|
||||
pub async fn new_from_str(url: &str) -> Result<Request> {
|
||||
@@ -337,7 +341,7 @@ impl Request {
|
||||
Ok(Request::exists(proper_url).await)
|
||||
}
|
||||
|
||||
pub async fn url_is_blacklisted(url: &Url) -> Result<()> {
|
||||
pub async fn url_is_blacklisted(url: &Url) -> Result<bool> {
|
||||
if let Some(host) = url.host() {
|
||||
match host {
|
||||
Host::Ipv4(ipv4) => {
|
||||
@@ -347,15 +351,38 @@ impl Request {
|
||||
}
|
||||
}
|
||||
Host::Domain(domain) => {
|
||||
let mut domain = domain.to_string();
|
||||
|
||||
let config = config().await;
|
||||
if config.january.blocked_domains.iter().any(|x| x == domain) {
|
||||
|
||||
// First step: TLDs and blocked domains
|
||||
if !domain.contains(".") // lazily block TLDs
|
||||
|| config.january.blocked_domains.iter().any(|x| x == &domain)
|
||||
{
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
|
||||
if !domain.contains(":") {
|
||||
domain += ":80";
|
||||
}
|
||||
|
||||
// Second step: resolve the IP and check the blocklist
|
||||
if let Ok(mut resolved_ip) = tokio::net::lookup_host(domain.clone()).await {
|
||||
if let Some(resolved_ip) = resolved_ip.next() {
|
||||
if !IP_BLOCKLIST.is_allowed(&resolved_ip.ip().to_string()) {
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
} else {
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
} else {
|
||||
return Err(create_error!(ProxyError));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user