5. Giao tiếp với Sandboxee

Theo mặc định, trình thực thi có thể giao tiếp với Sandboxee thông qua các bộ mô tả tệp. Đây có thể là tất cả những gì bạn cần, chẳng hạn như nếu bạn chỉ muốn chia sẻ một tệp với Sandboxee hoặc đọc đầu ra tiêu chuẩn của Sandboxee.

Tuy nhiên, rất có thể bạn cần có logic giao tiếp phức tạp hơn giữa trình thực thi và Sandboxee. Bạn có thể dùng API comms (xem tệp tiêu đề comms.h) để gửi số nguyên, chuỗi, vùng đệm byte, protobuf hoặc bộ mô tả tệp.

Chia sẻ chỉ số mô tả tệp

Khi sử dụng Inter-Process Communication API (xem ipc.h), bạn có thể sử dụng MapFd() hoặc ReceiveFd():

  • Sử dụng MapFd() để liên kết các bộ mô tả tệp từ trình thực thi đến Sandboxee. Bạn có thể dùng phương thức này để chia sẻ một tệp được mở từ trình thực thi để sử dụng trong Sandboxee. Bạn có thể xem ví dụ về cách sử dụng trong static.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • Dùng ReceiveFd() để tạo một điểm cuối socketpair. Bạn có thể dùng tuỳ chọn này để đọc đầu ra tiêu chuẩn hoặc lỗi tiêu chuẩn của Sandboxee. Bạn có thể xem ví dụ về cách sử dụng trong công cụ.

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

Sử dụng Comms API

Sandbox2 cung cấp một API comms thuận tiện. Đây là một cách đơn giản và dễ dàng để chia sẻ số nguyên, chuỗi hoặc vùng đệm byte giữa trình thực thi và Sandboxee. Dưới đây là một số đoạn mã mà bạn có thể tìm thấy trong ví dụ crc4.

Để bắt đầu sử dụng API comms, trước tiên, bạn phải lấy đối tượng comms từ đối tượng Sandbox2:

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

Sau khi có đối tượng comms, dữ liệu có thể được gửi đến Sandboxee bằng một trong các họ hàm Send*. Bạn có thể xem ví dụ về cách sử dụng API comms trong ví dụ crc4. Đoạn mã dưới đây cho thấy một đoạn trích từ ví dụ đó. Trình thực thi gửi một unsigned char buf[size] bằng SendBytes(buf, size):

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

Để nhận dữ liệu từ Sandboxee, hãy sử dụng một trong các hàm Recv*. Đoạn mã dưới đây là một phần trích dẫn từ ví dụ crc4. Trình thực thi nhận tổng kiểm trong số nguyên 32 bit chưa ký: uint32_t crc4;

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

Chia sẻ dữ liệu với các vùng đệm

Một chức năng chia sẻ dữ liệu khác là sử dụng API vùng đệm để chia sẻ lượng lớn dữ liệu và tránh các bản sao tốn kém được gửi qua lại giữa trình thực thi và Sandboxee.

Trình thực thi tạo một vùng đệm, theo kích thước và dữ liệu sẽ được truyền, hoặc trực tiếp từ một bộ mô tả tệp, rồi truyền vùng đệm đó đến Sandboxee bằng cách sử dụng comms->SendFD() trong trình thực thi và comms->RecvFD() trong Sandboxee.

Trong đoạn mã dưới đây, bạn có thể thấy phía người thực thi. Hộp cát chạy không đồng bộ và chia sẻ dữ liệu thông qua một vùng đệm với 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());

Về phía Sandboxee, bạn cũng phải tạo một đối tượng vùng đệm và đọc dữ liệu từ bộ mô tả tệp do trình thực thi gửi:

// 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 */