5. תקשורת עם Sandboxee

כברירת מחדל, המבצע יכול לתקשר עם Sandboxee באמצעות מתארים של קבצים. יכול להיות שזה כל מה שצריך, למשל אם רוצים רק לשתף קובץ עם Sandboxee או לקרוא את הפלט הרגיל של Sandboxee.

עם זאת, סביר להניח שתצטרכו לוגיקה מורכבת יותר של תקשורת בין הרכיב שמבצע את הפעולה לבין Sandboxee. אפשר להשתמש ב-Comms API (ראו את קובץ הכותרת comms.h) כדי לשלוח מספרים שלמים, מחרוזות, מאגרי בייטים, פרוטוקולי protobuf או מתארי קבצים.

שיתוף של תיאורי קבצים

באמצעות Inter-Process Communication API (ראו ipc.h), אפשר להשתמש ב-MapFd() או ב-ReceiveFd():

  • משתמשים ב-MapFd() כדי למפות מתארים של קבצים מהמפעיל אל Sandboxee. אפשר להשתמש בזה כדי לשתף קובץ שנפתח מהמבצע לשימוש ב-Sandboxee. דוגמה לשימוש אפשר לראות ב-static.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • משתמשים ב-ReceiveFd() כדי ליצור נקודת קצה של socketpair. אפשר להשתמש באפשרות הזו כדי לקרוא את הפלט הרגיל או את השגיאות הרגילות של Sandboxee. אפשר לראות דוגמה לשימוש בכלי.

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

שימוש ב-Comms API

‫Sandbox2 מספק API נוח לתקשורת. זו דרך פשוטה וקלה לשתף מספרים שלמים, מחרוזות או מאגרי בייטים בין ה-Executor לבין Sandboxee. בהמשך מופיעים קטעי קוד שאפשר למצוא בדוגמה crc4.

כדי להתחיל להשתמש ב-API של התקשורת, קודם צריך לקבל את אובייקט התקשורת מאובייקט Sandbox2:

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

אחרי שאובייקט התקשורת יהיה זמין, אפשר יהיה לשלוח נתונים אל Sandboxee באמצעות אחת מהפונקציות של Send*. דוגמה לשימוש ב-API של התקשורת מופיעה בדוגמה crc4. קטע הקוד שבהמשך הוא קטע מתוך הדוגמה הזו. המוציא לפועל שולח unsigned char buf[size] עם SendBytes(buf, size):

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

כדי לקבל נתונים מה-Sandboxee, משתמשים באחת מהפונקציות של Recv*. קטע הקוד שבהמשך הוא קטע מתוך הדוגמה של crc4. ה-executor מקבל את סיכום הביקורת (checksum) כמספר שלם לא מסומן בן 32 ביט: uint32_t crc4;

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

שיתוף נתונים עם מאגרי נתונים זמניים

פונקציית שיתוף נתונים נוספת היא שימוש ב-buffer API כדי לשתף כמויות גדולות של נתונים ולמנוע העתקות יקרות שנשלחות הלוך ושוב בין ה-executor לבין Sandboxee.

ה-executor יוצר Buffer, לפי גודל ונתונים שיועברו, או ישירות ממתאר קובץ, ומעביר אותו אל Sandboxee באמצעות comms->SendFD() ב-executor ו-comms->RecvFD() ב-Sandboxee.

בקטע הקוד שבהמשך אפשר לראות את הצד של המפעיל. ארגז החול פועל באופן אסינכרוני ומשתף נתונים דרך מאגר עם 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());

בצד של Sandboxee, צריך גם ליצור אובייקט מאגר ולקרוא את הנתונים ממתאר הקובץ שנשלח על ידי ה-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 */