5. การสื่อสารกับแซนด์บ็อกซ์
โดยค่าเริ่มต้น ผู้ดำเนินการจะสื่อสารกับแซนด์บ็อกซ์ผ่านคำอธิบายไฟล์ได้ คุณอาจใช้เพียงเท่านี้ก็ได้ หากต้องการแชร์ไฟล์กับแซนด์บ็อกซ์หรืออ่านเอาต์พุตมาตรฐานของแซนด์บ็อกซ์
แต่ส่วนใหญ่แล้วคุณย่อมต้องการตรรกะการสื่อสารที่ซับซ้อนมากขึ้นระหว่างผู้ดำเนินการและแซนด์บ็อกซ์ comms API (ดูไฟล์ส่วนหัว comms.h) สามารถใช้เพื่อส่งจำนวนเต็ม สตริง บัฟเฟอร์ไบต์ โปรโตคอล หรือตัวอธิบายไฟล์
การแชร์ตัวอธิบายไฟล์
การใช้ Inter-Process Communication API (ดูที่ ipc.h) คุณจะใช้ MapFd()
หรือ ReceiveFd()
ได้ดังนี้
ใช้
MapFd()
เพื่อจับคู่ข้อบ่งชี้ไฟล์จากผู้ดำเนินการไปยังแซนด์บ็อกซ์ ซึ่งอาจใช้ในการแชร์ไฟล์ที่เปิดจากผู้ดำเนินการเพื่อใช้ในแซนด์บ็อกซ์ ตัวอย่างการใช้งานสามารถดูได้ในแบบคงที่// The executor opened /proc/version and passes it to the sandboxee as stdin executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
ใช้
ReceiveFd()
เพื่อสร้างปลายทาง Socketpair สามารถใช้เพื่ออ่านเอาต์พุตมาตรฐานหรือข้อผิดพลาดมาตรฐานของแซนด์บ็อกซ์ ดูตัวอย่างการใช้งานได้ในเครื่องมือ// The executor receives a file descriptor of the sandboxee stdout int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
การใช้ Comms API
Sandbox2 มี Comms API ที่ใช้งานสะดวก นี่เป็นวิธีง่ายๆ ในการแชร์จำนวนเต็ม สตริง หรือบัฟเฟอร์ไบต์ระหว่างตัวดำเนินการและแซนด์บ็อกซ์ ด้านล่างคือข้อมูลโค้ดบางส่วนที่คุณจะพบในตัวอย่าง crc4
หากต้องการเริ่มต้นใช้งาน comms API ก่อนอื่นคุณต้องรับออบเจ็กต์ comms จากออบเจ็กต์ Sandbox2
sandbox2::Comms* comms = s2.comms();
เมื่อออบเจ็กต์การสื่อสารพร้อมใช้งาน ระบบจะส่งข้อมูลไปยังแซนด์บ็อกซ์โดยใช้ฟังก์ชันในตระกูล Send* ได้ ดูตัวอย่างการใช้ comms API ได้ในตัวอย่าง crc4 ข้อมูลโค้ดด้านล่างแสดงตัวอย่างบางส่วน ผู้ดำเนินการส่ง unsigned char buf[size]
พร้อม SendBytes(buf, size)
:
if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
/* handle error */
}
หากต้องการรับข้อมูลจากแซนด์บ็อกซ์ ให้ใช้ฟังก์ชัน Recv*
อย่างใดอย่างหนึ่ง ข้อมูลโค้ดด้านล่างเป็นข้อความที่ตัดตอนมาจากตัวอย่าง crc4 ผู้ดำเนินการจะได้รับการตรวจสอบข้อผิดพลาดในจำนวนเต็มที่ไม่มีเครื่องหมาย 32 บิต:
uint32_t crc4;
if (!(comms->RecvUint32(&crc4))) {
/* handle error */
}
การแชร์ข้อมูลกับบัฟเฟอร์
ฟังก์ชันการแชร์ข้อมูลอีกอย่างคือการใช้ API บัฟเฟอร์เพื่อแชร์ข้อมูลจำนวนมากและเพื่อหลีกเลี่ยงสำเนาที่มีค่าใช้จ่ายสูงที่ส่งกลับไปกลับมาระหว่างผู้ดำเนินการกับแซนด์บ็อกซ์
ผู้ดำเนินการสร้างบัฟเฟอร์ ไม่ว่าจะตามขนาดและข้อมูลที่จะส่ง หรือโดยตรงจากคำอธิบายไฟล์ และส่งไปยังแซนด์บ็อกซ์โดยใช้ comms->SendFD()
ในตัวดำเนินการและ comms->RecvFD()
ในแซนด์บ็อกซ์
คุณจะเห็นด้านของผู้ดำเนินการในข้อมูลโค้ดด้านล่าง แซนด์บ็อกซ์ทำงานแบบไม่พร้อมกันและแชร์ข้อมูลผ่านบัฟเฟอร์กับแซนด์บ็อกซ์:
// 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());
ในฝั่งของแซนด์บ็อกซ์ คุณจะต้องสร้างออบเจ็กต์บัฟเฟอร์และอ่านข้อมูลจากตัวบอกไฟล์จากผู้ปฏิบัติการด้วย โดยทำดังนี้
// 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 */