5. Komunikacja z Sandboxee

Domyślnie wykonawcy mogą komunikować się z platformą Sandboxee za pomocą deskryptorów plików. To może być wszystko, co jest potrzebne, jeśli na przykład chcesz udostępnić plik użytkownikowi piaskownicy lub odczytać jego standardowe dane wyjściowe.

Najprawdopodobniej jednak potrzebujesz bardziej złożonej logiki komunikacji między wykonawcą a osobą w środowisku piaskownicy. Interfejs Comms API (patrz plik nagłówka comms.h) może służyć do wysyłania liczb całkowitych, ciągów tekstowych, buforów bajtów, protobufów lub deskryptorów plików.

Udostępnianie deskryptorów plików

Za pomocą interfejsu Inter-Process Communication API (patrz ipc.h) możesz użyć MapFd() lub ReceiveFd():

  • Użyj narzędzia MapFd(), aby zmapować deskryptory plików od wykonawcy na piaskownicę. Można go użyć, aby udostępnić plik otwarty przez wykonawcę do użycia w trybie piaskownicy. Przykładem użycia jest static.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • Użyj ReceiveFd(), aby utworzyć punkt końcowy parowania. Pozwala to odczytywać standardowe dane wyjściowe lub standardowe błędy Sandboxee. Przykładowe zastosowanie można zobaczyć w narzędziu.

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

Korzystanie z interfejsu Comms API

Sandbox2 zapewnia wygodny interfejs API komunikacyjny. Jest to prosty i łatwy sposób udostępniania liczb całkowitych, ciągów tekstowych lub buforów bajtów między wykonawcą a środowiskiem piaskownicy. Poniżej znajdziesz kilka fragmentów kodu, które znajdziesz w przykładzie crc4.

Aby zacząć korzystać z interfejsu Comms API, musisz najpierw pobrać obiekt Comms z obiektu Sandbox2:

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

Gdy obiekt komunikacyjny będzie dostępny, dane można wysłać do piaskownicy, korzystając z jednej z funkcji Wyślij*. Przykład użycia interfejsu comms API znajdziesz w przykładzie crc4. Poniższy fragment kodu zawiera fragment tego przykładu. Wykonawca wysyła unsigned char buf[size] za pomocą SendBytes(buf, size):

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

Aby otrzymywać dane z środowiska piaskownicy, użyj jednej z funkcji Recv*. Poniższy fragment kodu to fragment kodu z przykładu crc4. Wykonawca otrzymuje sumę kontrolną w 32-bitowej nieoznaczonej liczbie całkowitej: uint32_t crc4;

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

Udostępnianie danych buforom

Inną funkcją udostępniania danych jest korzystanie z interfejsu API buforowania w celu udostępniania dużych ilości danych i unikania kosztownych kopii przesyłanych tam i z powrotem między wykonawcą a środowiskiem piaskownicy.

Wykonawca tworzy bufor (na podstawie rozmiaru i przekazanych danych) lub bezpośrednio z deskryptora pliku i przekazuje go do piaskownicy za pomocą polecenia comms->SendFD() w wykonaniu i comms->RecvFD() w środowisku piaskownicy.

We fragmencie kodu poniżej widać stronę wykonawcy. Piaskownica działa asynchronicznie i udostępnia dane za pomocą bufora użytkownikowi piaskownicy:

// 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());

Po stronie piaskownicy musisz też utworzyć obiekt bufora i odczytać dane z deskryptora pliku wysłanego przez wykonawcę:

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