5. תקשורת עם Sandboxee

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

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

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

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

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

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

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

שימוש ב-Comms API

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

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

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

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

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

כדי לקבל נתונים מ-Sandboxee, יש להשתמש באחת מהפונקציות Recv*. קטע הקוד הבא הוא קטע מהדוגמה של crc4. האופרטור מקבל את בדיקת הסיכום כמספר שלם ללא סימן [unsigned integer] של 32 סיביות: uint32_t crc4;

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

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

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

מבצע ההפעלה יוצר מאגר נתונים זמני לפי הגודל והנתונים שיועברו או ישירות מתיאור הקובץ, ומעביר אותו ל-Sandboxee באמצעות comms->SendFD() בהפעלה ו-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, צריך גם ליצור אובייקט מאגר נתונים זמני ולקרוא את הנתונים מתיאור הקובץ שנשלח על ידי המפעיל:

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