1
0
mirror of https://github.com/Bayselonarrend/OpenIntegrations.git synced 2025-08-10 22:41:43 +02:00

FTP: Переработка HTTP прокси

This commit is contained in:
Anton Titovets
2025-08-04 15:46:03 +03:00
parent 8908d212eb
commit 96e93929aa
8 changed files with 55 additions and 48 deletions

View File

@@ -276,6 +276,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "iana-time-zone"
version = "0.1.61"
@@ -441,6 +447,7 @@ dependencies = [
"addin1c",
"base64",
"chrono",
"httparse",
"rustls",
"rustls-pemfile",
"serde",

View File

@@ -23,4 +23,5 @@ base64 = "0.22.1"
chrono = { version = "0.4", features = ["serde"] }
webpki-roots = "1.0.1"
rustls = { version = "0.23.28", features = ["ring"] }
rustls-pemfile = "2.2.0"
rustls-pemfile = "2.2.0"
httparse = "1.10.1"

View File

@@ -1,9 +1,9 @@
"MAIN ---"
linux-vdso.so.1 (0x00007ffe4c948000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x0000759ff8e00000)
libc.so.6 => /lib64/libc.so.6 (0x0000759ff8a00000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000759ff8600000)
/lib64/ld-linux-x86-64.so.2 (0x0000759ff9400000)
linux-vdso.so.1 (0x00007ffc20c91000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007776c2000000)
libc.so.6 => /lib64/libc.so.6 (0x00007776c1c00000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007776c1800000)
/lib64/ld-linux-x86-64.so.2 (0x00007776c2600000)
GLIBC_2.2.5
GLIBC_2.3
GLIBC_2.3.4

View File

@@ -1,10 +1,11 @@
use std::io::{BufRead, BufReader, Write};
use std::net::{SocketAddr, TcpStream};
use std::io::{Read, Write};
use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
use std::time::Duration;
use socks::{Socks4Stream, Socks5Stream};
use base64::{Engine as _, engine::general_purpose};
use suppaftp::FtpError;
use crate::component::configuration::{FtpProxySettings, FtpSettings};
use std::fmt::Write as FmtWrite;
pub fn make_passive_proxy_stream(
ftp_settings: &FtpSettings,
@@ -122,62 +123,60 @@ fn connect_via_socks4(proxy_settings: &FtpProxySettings, target_addr: (&str, u16
.map_err(|e| format!("SOCKS4 error: {}", e))
}
fn connect_via_http_proxy(proxy_settings: &FtpProxySettings, target_addr: (&str, u16)) -> Result<TcpStream, String> {
pub fn connect_via_http_proxy(proxy_settings: &FtpProxySettings, target_addr: (&str, u16)) -> Result<TcpStream, String> {
let proxy_addr = format!("{}:{}", proxy_settings.server, proxy_settings.port);
let mut stream = TcpStream::connect(proxy_addr)
.map_err(|e| format!("Failed to connect to HTTP proxy: {}", e))?;
let mut stream = TcpStream::connect(
proxy_addr.to_socket_addrs()
.map_err(|e| format!("Failed to resolve proxy address: {}", e))?
.next()
.ok_or_else(|| "Proxy address resolution returned no results".to_string())?
).map_err(|e| format!("Failed to connect to HTTP proxy: {}", e))?;
// Установим таймауты для стабильности
stream.set_read_timeout(Some(Duration::from_secs(10))).ok();
stream.set_write_timeout(Some(Duration::from_secs(10))).ok();
// Формируем CONNECT-запрос
let host_port = format!("{}:{}", target_addr.0, target_addr.1);
let request = format!(
"CONNECT {} HTTP/1.1\r\nHost: {}\r\n",
let mut request = format!(
"CONNECT {} HTTP/1.1\r\nHost: {}\r\nConnection: keep-alive\r\n",
host_port, host_port
);
let request = if let (Some(user), Some(pass)) = (&proxy_settings.login, &proxy_settings.password) {
let auth = general_purpose::STANDARD.encode(&format!("{}:{}", user, pass));
format!("{}Proxy-Authorization: Basic {}\r\n\r\n", request, auth)
} else {
request + "\r\n"
};
// Добавляем Proxy-Authorization при необходимости
if let (Some(user), Some(pass)) = (&proxy_settings.login, &proxy_settings.password) {
let auth = general_purpose::STANDARD.encode(format!("{}:{}", user, pass));
write!(request, "Proxy-Authorization: Basic {}\r\n", auth).unwrap();
}
request.push_str("\r\n");
// Отправляем запрос
stream.write_all(request.as_bytes())
.map_err(|e| format!("Failed to send CONNECT request: {}", e))?;
let mut reader = BufReader::new(stream);
// Читаем ответ прокси
let mut buf = [0u8; 4096];
let n = stream.read(&mut buf)
.map_err(|e| format!("Failed to read proxy response: {}", e))?;
let mut status_line = String::new();
reader.read_line(&mut status_line)
.map_err(|e| format!("Failed to read status line: {}", e))?;
if !status_line.starts_with("HTTP/") {
return Err(format!("Invalid response: {}", status_line));
let mut headers = [httparse::EMPTY_HEADER; 16];
let mut response = httparse::Response::new(&mut headers);
match response.parse(&buf[..n]) {
Ok(httparse::Status::Complete(_)) => {},
Ok(httparse::Status::Partial) => return Err("Incomplete proxy response".to_string()),
Err(e) => return Err(format!("Failed to parse proxy response: {:?}", e)),
}
let status_code = status_line.split_whitespace().nth(1)
.ok_or_else(|| "Malformed status line")?;
match status_code {
"200" => {}
"407" => return Err("Proxy authentication required".to_string()),
code => return Err(format!("Proxy error: {}", code)),
}
// Пропускаем заголовки
let mut header_line = String::new();
loop {
header_line.clear();
reader.read_line(&mut header_line)
.map_err(|e| format!("Failed to read header: {}", e))?;
if header_line.trim().is_empty() {
break;
let code = response.code.ok_or_else(|| "Missing HTTP status code in proxy response".to_string())?;
match code {
200 => Ok(stream),
407 => Err("Proxy authentication required (HTTP 407)".to_string()),
_ => {
let resp_str = String::from_utf8_lossy(&buf[..n]);
Err(format!("Proxy error: HTTP {}\nFull response:\n{}", code, resp_str))
}
}
let stream = reader.into_inner();
Ok(stream)
}
fn connect_direct(addr: (&str, u16)) -> Result<TcpStream, String> {

Binary file not shown.

Binary file not shown.