5- التواصل مع Sandboxee

يمكن للمنفّذ تلقائيًا التواصل مع Sandboxee من خلال واصفات الملفات. قد يكون هذا كل ما تحتاج إليه، مثلاً إذا كنت تريد مشاركة ملف مع Sandboxee أو قراءة الناتج العادي لـ Sandboxee.

ومع ذلك، من المرجّح أنّك بحاجة إلى منطق اتصال أكثر تعقيدًا بين المنفِّذ وSandboxee. يمكن استخدام واجهة برمجة التطبيقات الخاصة بالاتصالات (راجِع ملف العنوان 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);
    

استخدام واجهة برمجة التطبيقات الخاصة بالاتصالات

توفّر Sandbox2 واجهة برمجة تطبيقات للاتصالات سهلة الاستخدام. هذه طريقة بسيطة وسهلة لمشاركة الأعداد الصحيحة أو السلاسل أو مخازن مؤقتة للبايتات بين المنفِّذ وSandboxee. في ما يلي بعض مقتطفات الرموز التي يمكنك العثور عليها في مثال crc4.

للبدء باستخدام واجهة برمجة التطبيقات الخاصة بالاتصالات، عليك أولاً الحصول على عنصر الاتصالات من عنصر Sandbox2:

sandbox2::Comms* comms = s2.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. يتلقّى المنفِّذ المجموع الاختباري في عدد صحيح غير موقّع يبلغ 32 بت: uint32_t crc4;

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

مشاركة البيانات مع المخازن المؤقتة

تتمثّل إحدى وظائف مشاركة البيانات الأخرى في استخدام واجهة برمجة التطبيقات buffer ‎ لمشاركة كميات كبيرة من البيانات وتجنُّب النسخ المكلفة التي يتم إرسالها بين المنفّذ و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 */