## 5. Communicating with the Sandboxee

By default, the executor can communicate with the Sandboxee through file
descriptors. This might be all you need, for example if you just want to share a
file with the Sandboxee, or read the Sandboxee's standard output.

However, you most likely have the need for more complex communication logic
between the executor and Sandboxee. The comms API (see the
[comms.h](https://github.com/google/sandboxed-api/blob/main/sandboxed_api/sandbox2/comms.h)
header file) can be used to send integers, strings, byte buffers, protobufs, or
file descriptors.

### Sharing File Descriptors

Using the Inter-Process Communication API (see
[ipc.h](https://github.com/google/sandboxed-api/blob/master/sandboxed_api/sandbox2/ipc.h)),
you can use `MapFd()` or `ReceiveFd()`:

- Use `MapFd()` to map file descriptors from the executor to the Sandboxee.
  This can be used to share a file opened from the executor for use in the
  Sandboxee. An example use can be seen in
  [static](https://developers.google.com/code-sandboxing/sandbox2/examples#static).

      // The executor opened /proc/version and passes it to the sandboxee as stdin
      executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);

- Use`ReceiveFd()` to create a socketpair endpoint. This can be used to read
  the Sandboxee's standard output or standard errors. An example use can be
  seen in the [tool](https://developers.google.com/code-sandboxing/sandbox2/examples#tool).

      // The executor receives a file descriptor of the sandboxee stdout
      int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);

### Using the comms API

Sandbox2 provides a convenient [comms
API](https://github.com/google/sandboxed-api/blob/master/sandboxed_api/sandbox2/comms.h).
This is a simple and easy way to share integers, strings, or byte buffers
between the executor and Sandboxee. Below are some code snippets that you can
find in the [crc4](https://developers.google.com/code-sandboxing/sandbox2/examples#crc4) example.

To get started with the comms API, you first have to get the comms object from
the Sandbox2 object:

    sandbox2::Comms* comms = s2.comms();

Once the comms object is available, data can be sent to the Sandboxee using one
of the Send\* family of functions. You can find an example use of the comms API
in the [crc4](https://developers.google.com/code-sandboxing/sandbox2/examples#crc4) example. The code snippet
below shows an excerpt from that example. The executor sends an `unsigned char
buf[size]` with `SendBytes(buf, size)`:

    if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
      /* handle error */
    }

To receive data from the Sandboxee, use one of the `Recv*` functions. The code
snippet below is an excerpt from the
[crc4](https://developers.google.com/code-sandboxing/sandbox2/examples#crc4) example. The executor receives
the checksum in a 32-bit unsigned integer: uint32_t crc4;

    if (!(comms->RecvUint32(&crc4))) {
      /* handle error */
    }

### Sharing Data with Buffers

Another data sharing functionality is to use the [buffer
API](https://github.com/google/sandboxed-api/blob/master/sandboxed_api/sandbox2/buffer.h)
to share large amounts of data and to avoid expensive copies that are sent back
and forth between the executor and Sandboxee.

The executor creates a Buffer, either by size and data to be passed, or directly
from a file descriptor, and passes it to the Sandboxee using `comms->SendFD()`
in the executor and `comms->RecvFD()` in the Sandboxee.

In the code snippet below, you can see the executor's side. The sandbox runs
asynchronously and shares data via a buffer with the Sandboxee:

    // start the sandbox asynchronously
    s2.RunAsync();

    // instantiate the comms object
    sandbox2::Comms* comms = s2.comms();

    // random buffer data we want to send
    constexpr unsigned char buffer_data[] = /* random data */;
    constexpr unsigned int buffer_dataLen = 34;

    // create sandbox2 buffer
    absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
         sandbox2::Buffer::CreateWithSize(1ULL << 20 /* 1Mib */);
    std::unique_ptr<sandbox2::Buffer> buffer_ptr = std::move(buffer).value();

    // point to the sandbox2 buffer and fill with data
    uint8_t* buf = buffer_ptr‑>data();
    memcpy(buf, buffer_data, buffer_data_len);

    // send the data to the sandboxee
    comms‑>SendFd(buffer_ptr‑>fd());

On the Sandboxee's side, you also have to create a buffer object and read the
data from the file descriptor sent by the executor:

    // establish the communication with the executor
    int fd;
    comms.RecvFD(&fd);

    // create the buffer
    absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
         sandbox2::Buffer::createFromFd(fd);

    // get the data
    auto buffer_ptr = std::move(buffer).value();
    uint8_t* buf = buffer_ptr‑>data();

    /* work with the buf object */