You've already forked OpenIntegrations
mirror of
https://github.com/Bayselonarrend/OpenIntegrations.git
synced 2025-08-10 22:41:43 +02:00
FTP: Переработка HTTP прокси
This commit is contained in:
7
src/addins/ftp/Cargo.lock
generated
7
src/addins/ftp/Cargo.lock
generated
@@ -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",
|
||||
|
@@ -24,3 +24,4 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
webpki-roots = "1.0.1"
|
||||
rustls = { version = "0.23.28", features = ["ring"] }
|
||||
rustls-pemfile = "2.2.0"
|
||||
httparse = "1.10.1"
|
@@ -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
|
||||
|
@@ -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> {
|
||||
|
BIN
src/en/OInt/addins/OPI_FTP.zip
vendored
BIN
src/en/OInt/addins/OPI_FTP.zip
vendored
Binary file not shown.
Binary file not shown.
BIN
src/ru/OInt/addins/OPI_FTP.zip
vendored
BIN
src/ru/OInt/addins/OPI_FTP.zip
vendored
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user