Introduce guest side forwarder agent

Brought code from ChromiumOS under confirmation of opensource licensing
team. Modification of these files to be built would be happened in the
following change.

Bug: 340126051
Test: N/A
Change-Id: I65aba2d1383fdef39c780f3b88308782a5b09fbf
diff --git a/build/debian/forwarder_guest/src/main.rs b/build/debian/forwarder_guest/src/main.rs
new file mode 100644
index 0000000..1bcbed4
--- /dev/null
+++ b/build/debian/forwarder_guest/src/main.rs
@@ -0,0 +1,183 @@
+// Copyright 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copied from ChromiumOS with relicensing:
+// src/platform2/vm_tools/chunnel/src/bin/chunnel.rs
+
+use std::env;
+use std::fmt;
+use std::process;
+use std::result;
+
+use chunnel::forwarder::{ForwarderError, ForwarderSession};
+use chunnel::stream::{StreamSocket, StreamSocketError};
+use getopts::Options;
+use libchromeos::deprecated::{PollContext, PollToken};
+use libchromeos::panic_handler::install_memfd_handler;
+use libchromeos::signal::block_signal;
+use libchromeos::syslog;
+use log::warn;
+use nix::sys::signal::Signal;
+
+// Program name.
+const IDENT: &str = "chunnel";
+
+#[remain::sorted]
+#[derive(Debug)]
+enum Error {
+    BlockSigpipe(nix::Error),
+    ConnectSocket(StreamSocketError),
+    Forward(ForwarderError),
+    PollContextDelete(nix::Error),
+    PollContextNew(nix::Error),
+    PollWait(nix::Error),
+    Syslog(libchromeos::syslog::Error),
+}
+
+type Result<T> = result::Result<T, Error>;
+
+impl fmt::Display for Error {
+    #[remain::check]
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        #[remain::sorted]
+        match self {
+            BlockSigpipe(e) => write!(f, "failed to block SIGPIPE: {}", e),
+            ConnectSocket(e) => write!(f, "failed to connnect socket: {}", e),
+            Forward(e) => write!(f, "failed to forward traffic: {}", e),
+            PollContextDelete(e) => write!(f, "failed to delete fd from poll context: {}", e),
+            PollContextNew(e) => write!(f, "failed to create poll context: {}", e),
+            PollWait(e) => write!(f, "failed to wait for poll: {}", e),
+            Syslog(e) => write!(f, "failed to initialize syslog: {}", e),
+        }
+    }
+}
+
+fn print_usage(program: &str, opts: &Options) {
+    let brief = format!("Usage: {} [options]", program);
+    print!("{}", opts.usage(&brief));
+}
+
+fn run_forwarder(local_stream: StreamSocket, remote_stream: StreamSocket) -> Result<()> {
+    block_signal(Signal::SIGPIPE).map_err(Error::BlockSigpipe)?;
+
+    #[derive(PollToken)]
+    enum Token {
+        LocalStreamReadable,
+        RemoteStreamReadable,
+    }
+    let poll_ctx: PollContext<Token> = PollContext::build_with(&[
+        (&local_stream, Token::LocalStreamReadable),
+        (&remote_stream, Token::RemoteStreamReadable),
+    ])
+    .map_err(Error::PollContextNew)?;
+
+    let mut forwarder = ForwarderSession::new(local_stream, remote_stream);
+
+    loop {
+        let events = poll_ctx.wait().map_err(Error::PollWait)?;
+
+        for event in events.iter_readable() {
+            match event.token() {
+                Token::LocalStreamReadable => {
+                    let shutdown = forwarder.forward_from_local().map_err(Error::Forward)?;
+                    if shutdown {
+                        poll_ctx
+                            .delete(forwarder.local_stream())
+                            .map_err(Error::PollContextDelete)?;
+                    }
+                }
+                Token::RemoteStreamReadable => {
+                    let shutdown = forwarder.forward_from_remote().map_err(Error::Forward)?;
+                    if shutdown {
+                        poll_ctx
+                            .delete(forwarder.remote_stream())
+                            .map_err(Error::PollContextDelete)?;
+                    }
+                }
+            }
+        }
+        if forwarder.is_shut_down() {
+            return Ok(());
+        }
+    }
+}
+
+fn main() -> Result<()> {
+    install_memfd_handler();
+    let args: Vec<String> = env::args().collect();
+    let program = args[0].clone();
+
+    let mut opts = Options::new();
+    opts.optflag("h", "help", "print this help menu");
+    opts.reqopt("l", "local", "local socket to forward", "SOCKADDR");
+    opts.reqopt("r", "remote", "remote socket to forward to", "SOCKADDR");
+    opts.optopt("t", "type", "type of traffic to forward", "stream|datagram");
+
+    let matches = match opts.parse(&args[1..]) {
+        Ok(m) => m,
+        Err(e) => {
+            warn!("failed to parse arg: {}", e);
+            print_usage(&program, &opts);
+            process::exit(1);
+        }
+    };
+    if matches.opt_present("h") {
+        print_usage(&program, &opts);
+        return Ok(());
+    }
+
+    syslog::init(IDENT.to_string(), false /* log_to_stderr */).map_err(Error::Syslog)?;
+
+    let local_sockaddr = match matches.opt_str("l") {
+        Some(sockaddr) => sockaddr,
+        None => {
+            warn!("local socket must be defined");
+            print_usage(&program, &opts);
+            process::exit(1);
+        }
+    };
+
+    let remote_sockaddr = match matches.opt_str("r") {
+        Some(sockaddr) => sockaddr,
+        None => {
+            warn!("remote socket must be defined");
+            print_usage(&program, &opts);
+            process::exit(1);
+        }
+    };
+
+    // Default to "stream" if traffic type is not defined.
+    let traffic_type = matches.opt_str("t");
+    if let Some(t) = traffic_type {
+        match t.as_ref() {
+            "stream" => {}
+            "datagram" => {
+                warn!("datagram sockets are not yet supported");
+                process::exit(1);
+            }
+            s => {
+                warn!("not a valid type of traffic: {}", s);
+                print_usage(&program, &opts);
+                process::exit(1);
+            }
+        }
+    }
+
+    let local_stream = StreamSocket::connect(&local_sockaddr).map_err(Error::ConnectSocket)?;
+    let remote_stream = StreamSocket::connect(&remote_sockaddr).map_err(Error::ConnectSocket)?;
+
+    run_forwarder(local_stream, remote_stream)
+}