diff --git a/src/addins/ftp/Cargo.lock b/src/addins/ftp/Cargo.lock index 47cc1d624a..339c946ac6 100644 --- a/src/addins/ftp/Cargo.lock +++ b/src/addins/ftp/Cargo.lock @@ -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", diff --git a/src/addins/ftp/Cargo.toml b/src/addins/ftp/Cargo.toml index 875335a699..2f486c7076 100644 --- a/src/addins/ftp/Cargo.toml +++ b/src/addins/ftp/Cargo.toml @@ -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" \ No newline at end of file +rustls-pemfile = "2.2.0" +httparse = "1.10.1" \ No newline at end of file diff --git a/src/addins/ftp/dependencies.log b/src/addins/ftp/dependencies.log index 2012bf02ed..87d1f03cce 100644 --- a/src/addins/ftp/dependencies.log +++ b/src/addins/ftp/dependencies.log @@ -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 diff --git a/src/addins/ftp/src/component/tcp_establish.rs b/src/addins/ftp/src/component/tcp_establish.rs index c635ecddd0..740d2a2ca0 100644 --- a/src/addins/ftp/src/component/tcp_establish.rs +++ b/src/addins/ftp/src/component/tcp_establish.rs @@ -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 { - +pub fn connect_via_http_proxy(proxy_settings: &FtpProxySettings, target_addr: (&str, u16)) -> Result { 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 { diff --git a/src/en/OInt/addins/OPI_FTP.zip b/src/en/OInt/addins/OPI_FTP.zip index 842d0fc3cf..c9d9098c60 100644 Binary files a/src/en/OInt/addins/OPI_FTP.zip and b/src/en/OInt/addins/OPI_FTP.zip differ diff --git a/src/en/OPI/src/CommonTemplates/OPI_FTP/Template.addin b/src/en/OPI/src/CommonTemplates/OPI_FTP/Template.addin index 842d0fc3cf..c9d9098c60 100644 Binary files a/src/en/OPI/src/CommonTemplates/OPI_FTP/Template.addin and b/src/en/OPI/src/CommonTemplates/OPI_FTP/Template.addin differ diff --git a/src/ru/OInt/addins/OPI_FTP.zip b/src/ru/OInt/addins/OPI_FTP.zip index 842d0fc3cf..c9d9098c60 100644 Binary files a/src/ru/OInt/addins/OPI_FTP.zip and b/src/ru/OInt/addins/OPI_FTP.zip differ diff --git a/src/ru/OPI/src/CommonTemplates/OPI_FTP/Template.addin b/src/ru/OPI/src/CommonTemplates/OPI_FTP/Template.addin index 842d0fc3cf..c9d9098c60 100644 Binary files a/src/ru/OPI/src/CommonTemplates/OPI_FTP/Template.addin and b/src/ru/OPI/src/CommonTemplates/OPI_FTP/Template.addin differ