LCOV - code coverage report
Current view: directory - src/trusted/python_bindings - python_imc.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 140 110 78.6 %
Date: 2012-02-16 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright 2010 The Native Client Authors.  All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can
       4                 :  * be found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : #if NACL_WINDOWS
       8                 : # include "io.h"
       9                 : # include "fcntl.h"
      10                 : /* The version of Python we pull in for Windows includes python26.lib
      11                 :    but not python26_d.lib, which pyconfig.h insists on linking against
      12                 :    when _DEBUG is defined.  We work around this by undefining _DEBUG. */
      13                 : # undef _DEBUG
      14                 : #endif
      15                 : 
      16                 : #include <Python.h>
      17                 : 
      18                 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
      19                 : #include "native_client/src/trusted/desc/nacl_desc_imc.h"
      20                 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
      21                 : #include "native_client/src/trusted/desc/nrd_all_modules.h"
      22                 : #include "native_client/src/trusted/desc/nrd_xfer.h"
      23                 : 
      24                 : 
      25                 : static PyTypeObject nacldesc_type;
      26                 : 
      27                 : struct PyDesc {
      28                 :   PyObject_HEAD
      29                 :   struct NaClDesc *nacldesc;
      30                 : };
      31                 : 
      32                 : 
      33                 : /* Internal helper functions */
      34                 : 
      35                 : /* Create a Python wrapper for a NaClDesc. */
      36              75 : static PyObject *NaClToPyDesc(struct NaClDesc *nacldesc) {
      37              75 :   struct PyDesc *pydesc = PyObject_New(struct PyDesc, &nacldesc_type);
      38              75 :   if (pydesc == NULL)
      39               0 :     return NULL;
      40              75 :   pydesc->nacldesc = nacldesc;
      41              75 :   return (PyObject *) pydesc;
      42                 : }
      43                 : 
      44                 : /* Unwrap the Python wrapper to get a NaClDesc.
      45                 :    This returns a *borrowed* reference. */
      46              14 : static struct NaClDesc *PyToNaClDesc(PyObject *obj) {
      47              14 :   if (PyObject_TypeCheck(obj, &nacldesc_type)) {
      48              11 :     struct PyDesc *pydesc = (struct PyDesc *) obj;
      49              11 :     return pydesc->nacldesc;
      50                 :   } else {
      51               3 :     PyErr_SetString(PyExc_TypeError, "Argument is not a NaClDesc");
      52               3 :     return NULL;
      53                 :   }
      54                 : }
      55                 : 
      56                 : 
      57                 : /* Module-level functions */
      58                 : 
      59              12 : static PyObject *PyDescImcMakeBoundSock(PyObject *self, PyObject *args) {
      60                 :   struct NaClDesc *pair[2];
      61                 :   UNREFERENCED_PARAMETER(self);
      62                 :   UNREFERENCED_PARAMETER(args);
      63              12 :   if (NaClCommonDescMakeBoundSock(pair) != 0) {
      64               0 :     PyErr_SetString(PyExc_Exception, "imc_makeboundsock() failed");
      65               0 :     return NULL;
      66                 :   }
      67              12 :   return Py_BuildValue("NN", NaClToPyDesc(pair[0]),
      68                 :                              NaClToPyDesc(pair[1]));
      69                 : }
      70                 : 
      71                 : /* Python's libraries are not consistent in how or whether they wrap
      72                 :    Windows handles.
      73                 : 
      74                 :    The modules _subprocess and win32api each have their own wrappers
      75                 :    which automagically convert to integers, but the constructors are
      76                 :    not exposed.  (_subprocess is a private, Windows-only module for
      77                 :    use by subprocess.py.)
      78                 : 
      79                 :    msvcrt.get_osfhandle() just returns an integer.  This seems like
      80                 :    the simplest approach, and it fits with how Python handles Unix
      81                 :    file descriptors, so we just use integers below.  Windows handles
      82                 :    are really just integer IDs, even if HANDLE is a typedef for
      83                 :    void*. */
      84                 : 
      85               2 : static PyObject *PyDescOsSocketpair(PyObject *self, PyObject *args) {
      86                 :   NaClHandle pair[2];
      87                 :   UNREFERENCED_PARAMETER(self);
      88                 :   UNREFERENCED_PARAMETER(args);
      89               2 :   if (NaClSocketPair(pair) != 0) {
      90               0 :     PyErr_SetString(PyExc_Exception, "NaClSocketPair() failed");
      91               0 :     return NULL;
      92                 :   }
      93               2 :   return Py_BuildValue("kk", (unsigned long) pair[0],
      94                 :                              (unsigned long) pair[1]);
      95                 : }
      96                 : 
      97               4 : static PyObject *PyDescFromOsSocket(PyObject *self, PyObject *args) {
      98                 :   unsigned long socket;
      99                 :   struct NaClDescImcDesc *nacldesc;
     100                 :   UNREFERENCED_PARAMETER(self);
     101                 : 
     102               4 :   if (!PyArg_ParseTuple(args, "k", &socket))
     103               0 :     return NULL;
     104               4 :   nacldesc = malloc(sizeof(*nacldesc));
     105               4 :   if (nacldesc == NULL) {
     106               0 :     PyErr_SetString(PyExc_MemoryError, "Failed to allocate NaClDesc");
     107               0 :     return NULL;
     108                 :   }
     109               4 :   if (!NaClDescImcDescCtor(nacldesc, (NaClHandle) socket)) {
     110               0 :     free(nacldesc);
     111               0 :     PyErr_SetString(PyExc_MemoryError, "Failed to allocate NaClDesc");
     112               0 :     return NULL;
     113                 :   }
     114               4 :   return NaClToPyDesc(&nacldesc->base.base);
     115                 : }
     116                 : 
     117               1 : static PyObject *PyDescFromOsFileDescriptor(PyObject *self, PyObject *args) {
     118                 :   unsigned long fd;
     119                 :   struct NaClDescIoDesc *nacldesc;
     120                 :   UNREFERENCED_PARAMETER(self);
     121                 : 
     122               1 :   if (!PyArg_ParseTuple(args, "k", &fd))
     123               0 :     return NULL;
     124                 : #if NACL_WINDOWS
     125                 :   /* Wrap the Windows handle to get a crt-emulated file descriptor.
     126                 :      The FD is specific to the instance of crt that we are linked to,
     127                 :      which is the same instance used by the NaClDesc library. */
     128                 :   fd = _open_osfhandle(fd, _O_RDWR | _O_BINARY);
     129                 : #endif
     130                 :   /* The "mode" argument is only used for a sanity check and is not
     131                 :      stored, so passing 0 works. */
     132               1 :   nacldesc = NaClDescIoDescMake(NaClHostDescPosixMake(fd, /* mode= */ 0));
     133               1 :   if (nacldesc == NULL) {
     134               0 :     PyErr_SetString(PyExc_MemoryError, "Failed to create NaClDesc wrapper");
     135               0 :     return NULL;
     136                 :   }
     137               1 :   return NaClToPyDesc(&nacldesc->base);
     138                 : }
     139                 : 
     140                 : 
     141                 : /* Methods */
     142                 : 
     143              75 : static void PyDescDealloc(struct PyDesc *self) {
     144              75 :   NaClDescUnref(self->nacldesc);
     145              75 : }
     146                 : 
     147              38 : static struct NaClDescVtbl *GetVtbl(struct NaClDesc *nacldesc) {
     148              38 :   return (struct NaClDescVtbl *) nacldesc->base.vtbl;
     149                 : }
     150                 : 
     151              19 : static PyObject *PyDescImcConnect(PyObject *self, PyObject *args) {
     152              19 :   struct NaClDesc *self_desc = ((struct PyDesc *) self)->nacldesc;
     153                 :   int errval;
     154                 :   struct NaClDesc *result_desc;
     155                 :   UNREFERENCED_PARAMETER(args);
     156              19 :   Py_BEGIN_ALLOW_THREADS;
     157              19 :   errval = GetVtbl(self_desc)->ConnectAddr(self_desc, &result_desc);
     158              19 :   Py_END_ALLOW_THREADS;
     159              19 :   if (errval == 0) {
     160              18 :     return NaClToPyDesc(result_desc);
     161                 :   } else {
     162                 :     /* TODO(mseaborn): Record errno value in exception. */
     163               1 :     PyErr_SetString(PyExc_Exception, "imc_connect() failed");
     164               1 :     return NULL;
     165                 :   }
     166                 : }
     167                 : 
     168              19 : static PyObject *PyDescImcAccept(PyObject *self, PyObject *args) {
     169              19 :   struct NaClDesc *self_desc = ((struct PyDesc *) self)->nacldesc;
     170                 :   int errval;
     171                 :   struct NaClDesc *result_desc;
     172                 :   UNREFERENCED_PARAMETER(args);
     173              19 :   Py_BEGIN_ALLOW_THREADS;
     174              19 :   errval = GetVtbl(self_desc)->AcceptConn(self_desc, &result_desc);
     175              19 :   Py_END_ALLOW_THREADS;
     176              19 :   if (errval == 0) {
     177              18 :     return NaClToPyDesc(result_desc);
     178                 :   } else {
     179                 :     /* TODO(mseaborn): Record errno value in exception. */
     180               1 :     PyErr_SetString(PyExc_Exception, "imc_accept() failed");
     181               1 :     return NULL;
     182                 :   }
     183                 : }
     184                 : 
     185              30 : static PyObject *PyDescImcSendmsg(PyObject *self, PyObject *args) {
     186              30 :   struct NaClDesc *self_desc = ((struct PyDesc *) self)->nacldesc;
     187                 :   char *data;
     188                 :   int data_size;
     189                 :   Py_ssize_t desc_array_len;
     190                 :   PyObject *pydesc_array;
     191                 :   struct NaClDesc *nacldesc_array[NACL_ABI_IMC_DESC_MAX];
     192                 :   struct NaClImcMsgIoVec iov;
     193                 :   struct NaClImcTypedMsgHdr message;
     194                 :   ssize_t sent;
     195                 :   int i;
     196                 : 
     197              30 :   if (!PyArg_ParseTuple(args, "s#O", &data, &data_size, &pydesc_array) ||
     198                 :       !PyTuple_Check(pydesc_array))
     199               0 :     return NULL;
     200              30 :   desc_array_len = PyTuple_GET_SIZE(pydesc_array);
     201              30 :   if (desc_array_len > NACL_ABI_IMC_DESC_MAX) {
     202                 :     /* Checking the size early saves the hassle of a dynamic allocation.
     203                 :        TODO(mseaborn): Record errno value in exception as NACL_ABI_EINVAL. */
     204               1 :     PyErr_SetString(PyExc_Exception,
     205                 :                     "imc_sendmsg() failed: Too many descriptor arguments");
     206               1 :     return NULL;
     207                 :   }
     208              40 :   for (i = 0; i < desc_array_len; i++) {
     209              14 :     PyObject *obj = PyTuple_GET_ITEM(pydesc_array, i);
     210                 :     /* PyToNaClDesc() returns a borrowed reference.  Because
     211                 :        pydesc_array is a tuple, and therefore immutable, and each of
     212                 :        its PyDesc members is also immutable, this is safe.  This saves
     213                 :        us having to increment and decrement the refcounts across the
     214                 :        NaClImcSendTypedMessage() call below. */
     215              14 :     struct NaClDesc *nacldesc = PyToNaClDesc(obj);
     216              14 :     if (nacldesc == NULL)
     217               3 :       return NULL;
     218              11 :     nacldesc_array[i] = nacldesc;
     219                 :   }
     220              26 :   iov.base = data;
     221              26 :   iov.length = data_size;
     222              26 :   message.iov = &iov;
     223              26 :   message.iov_length = 1;
     224              26 :   message.ndescv = nacldesc_array;
     225              26 :   message.ndesc_length = (nacl_abi_size_t) desc_array_len;
     226              26 :   message.flags = 0;
     227              26 :   Py_BEGIN_ALLOW_THREADS;
     228              26 :   sent = NaClImcSendTypedMessage(self_desc, &message, 0);
     229              26 :   Py_END_ALLOW_THREADS;
     230              26 :   if (sent >= 0) {
     231              26 :     return Py_BuildValue("i", sent);
     232                 :   } else {
     233                 :     /* TODO(mseaborn): Record errno value in exception. */
     234               0 :     PyErr_SetString(PyExc_Exception, "imc_sendmsg() failed");
     235               0 :     return NULL;
     236                 :   }
     237                 : }
     238                 : 
     239              27 : static PyObject *PyDescImcRecvmsg(PyObject *self, PyObject *args) {
     240              27 :   struct NaClDesc *self_desc = ((struct PyDesc *) self)->nacldesc;
     241                 :   int buffer_size;
     242                 :   void *buffer;
     243                 :   struct NaClDesc *nacldesc_array[NACL_ABI_IMC_DESC_MAX];
     244                 :   struct NaClImcMsgIoVec iov;
     245                 :   struct NaClImcTypedMsgHdr message;
     246                 :   ssize_t received;
     247                 :   unsigned int i;
     248                 :   PyObject *descs_tuple;
     249                 :   PyObject *result;
     250                 : 
     251              27 :   if (!PyArg_ParseTuple(args, "i", &buffer_size))
     252               0 :     return NULL;
     253              27 :   buffer = malloc(buffer_size);
     254              27 :   if (buffer == NULL) {
     255               0 :     PyErr_SetString(PyExc_MemoryError,
     256                 :                     "imc_recvmsg(): Failed to allocate buffer");
     257               0 :     return NULL;
     258                 :   }
     259              27 :   iov.base = buffer;
     260              27 :   iov.length = buffer_size;
     261              27 :   message.iov = &iov;
     262              27 :   message.iov_length = 1;
     263              27 :   message.ndescv = nacldesc_array;
     264              27 :   message.ndesc_length = NACL_ABI_IMC_DESC_MAX;
     265              27 :   message.flags = 0;
     266              27 :   Py_BEGIN_ALLOW_THREADS;
     267                 :   /* No quota management from these bindings. */
     268              27 :   received = NaClImcRecvTypedMessage(self_desc, &message, 0, NULL);
     269              27 :   Py_END_ALLOW_THREADS;
     270                 : 
     271              27 :   if (received < 0) {
     272               1 :     free(buffer);
     273                 :     /* TODO(mseaborn): Record errno value in exception. */
     274               1 :     PyErr_SetString(PyExc_Exception, "imc_recvmsg() failed");
     275               1 :     return NULL;
     276                 :   }
     277                 : 
     278              26 :   descs_tuple = PyTuple_New(message.ndesc_length);
     279              26 :   if (descs_tuple == NULL) {
     280               0 :     free(buffer);
     281               0 :     return NULL;
     282                 :   }
     283              36 :   for (i = 0; i < message.ndesc_length; i++) {
     284              10 :     PyObject *wrapper = NaClToPyDesc(message.ndescv[i]);
     285              10 :     if (wrapper == NULL) {
     286                 :       /* On error, free the remaining received descriptors.  The
     287                 :          already-processed descriptors are freed when unreffing
     288                 :          descs_tuple.  Unreffing a partially-initialised tuple is safe
     289                 :          because the fields are NULL-initialised. */
     290                 :       unsigned int j;
     291               0 :       for (j = i; j < message.ndesc_length; j++) {
     292               0 :         NaClDescUnref(message.ndescv[j]);
     293                 :       }
     294               0 :       free(buffer);
     295               0 :       Py_DECREF(descs_tuple);
     296               0 :       return NULL;
     297                 :     }
     298              10 :     PyTuple_SET_ITEM(descs_tuple, i, wrapper);
     299                 :   }
     300                 : 
     301              26 :   result = Py_BuildValue("s#N", buffer, received, descs_tuple);
     302              26 :   free(buffer);
     303              26 :   return result;
     304                 : }
     305                 : 
     306                 : static PyMethodDef nacldesc_methods[] = {
     307                 :   { "imc_connect", (PyCFunction) PyDescImcConnect, METH_NOARGS,
     308                 :     "imc_connect() -> socket\n\n"
     309                 :     "Make a connection to a NaCl IMC SocketAddress."
     310                 :   },
     311                 :   { "imc_accept", (PyCFunction) PyDescImcAccept, METH_NOARGS,
     312                 :     "imc_accept() -> socket\n\n"
     313                 :     "Accept a connection on a NaCl IMC BoundSocket."
     314                 :   },
     315                 :   { "imc_sendmsg", (PyCFunction) PyDescImcSendmsg, METH_VARARGS,
     316                 :     "imc_sendmsg(data, desc_tuple) -> byte_count\n\n"
     317                 :     "Send a message on a NaCl IMC connected socket.  "
     318                 :     "Returns the number of bytes sent."
     319                 :   },
     320                 :   { "imc_recvmsg", (PyCFunction) PyDescImcRecvmsg, METH_VARARGS,
     321                 :     "imc_recvmsg(buffer_size) -> (data, desc_tuple)\n\n"
     322                 :     "Receive a message on a NaCl IMC connected socket."
     323                 :   },
     324                 :   { NULL, NULL, 0, NULL }
     325                 : };
     326                 : 
     327                 : static PyTypeObject nacldesc_type = {
     328                 :   PyObject_HEAD_INIT(NULL)
     329                 :   0,                         /* ob_size */
     330                 :   "naclimc.NaClDesc",        /* tp_name */
     331                 :   sizeof(struct PyDesc),     /* tp_basicsize */
     332                 :   0,                         /* tp_itemsize */
     333                 :   (destructor) PyDescDealloc, /* tp_dealloc */
     334                 :   NULL,                      /* tp_print */
     335                 :   NULL,                      /* tp_getattr */
     336                 :   NULL,                      /* tp_setattr */
     337                 :   NULL,                      /* tp_compare */
     338                 :   NULL,                      /* tp_repr */
     339                 :   NULL,                      /* tp_as_number */
     340                 :   NULL,                      /* tp_as_sequence */
     341                 :   NULL,                      /* tp_as_mapping */
     342                 :   NULL,                      /* tp_hash  */
     343                 :   NULL,                      /* tp_call */
     344                 :   NULL,                      /* tp_str */
     345                 :   NULL,                      /* tp_getattro */
     346                 :   NULL,                      /* tp_setattro */
     347                 :   NULL,                      /* tp_as_buffer */
     348                 :   Py_TPFLAGS_DEFAULT,        /* tp_flags */
     349                 :   "NaCl descriptor",         /* tp_doc */
     350                 :   NULL,                      /* tp_traverse */
     351                 :   NULL,                      /* tp_clear */
     352                 :   NULL,                      /* tp_richcompare */
     353                 :   0,                         /* tp_weaklistoffset */
     354                 :   NULL,                      /* tp_iter */
     355                 :   NULL,                      /* tp_iternext */
     356                 :   nacldesc_methods,          /* tp_methods */
     357                 :   NULL,                      /* tp_members */
     358                 :   NULL,                      /* tp_getset */
     359                 :   NULL,                      /* tp_base */
     360                 :   NULL,                      /* tp_dict */
     361                 :   NULL,                      /* tp_descr_get */
     362                 :   NULL,                      /* tp_descr_set */
     363                 :   0,                         /* tp_dictoffset */
     364                 :   NULL,                      /* tp_init */
     365                 :   NULL,                      /* tp_alloc */
     366                 :   NULL,                      /* tp_new */
     367                 : };
     368                 : 
     369                 : static PyMethodDef module_methods[] = {
     370                 :   { "imc_makeboundsock", PyDescImcMakeBoundSock, METH_NOARGS,
     371                 :     "imc_makeboundsock() -> (boundsock, sockaddr)\n\n"
     372                 :     "Creates a new socket pair.  boundsock can be used with imc_accept(), "
     373                 :     "while sockaddr can be used with imc_connect()."
     374                 :   },
     375                 :   { "os_socketpair", PyDescOsSocketpair, METH_NOARGS,
     376                 :     "os_socketpair() -> (fd1, fd2)\n\n"
     377                 :     "Returns a pair of host-OS sockets.  "
     378                 :     "On Unix, these are file descriptors.  On Windows, these are handles.  "
     379                 :     "This is for setting up a connection to a newly-spawned subprocess."
     380                 :   },
     381                 :   { "from_os_socket", PyDescFromOsSocket, METH_VARARGS,
     382                 :     "from_os_socket(fd) -> socket\n\n"
     383                 :     "Takes a host-OS socket and returns a connected socket NaClDesc.  "
     384                 :     "This takes ownership of the host-OS socket.  "
     385                 :     "This is for setting up a connection inside or to a "
     386                 :     "newly-spawned subprocess."
     387                 :   },
     388                 :   { "from_os_file_descriptor", PyDescFromOsFileDescriptor, METH_VARARGS,
     389                 :     "from_os_file_descriptor(fd) -> nacldesc\n\n"
     390                 :     "Takes a host-OS file handle and returns a NaClDesc wrapper for it.  "
     391                 :     "This takes ownership of the host-OS file handle.\n\n"
     392                 :     "On Unix, this takes a file descriptor.\n\n"
     393                 :     "On Windows, this takes a Windows handle, not a file descriptor.  "
     394                 :     "This is because Windows file descriptors are emulated by crt (the "
     395                 :     "C runtime library), and python.exe can be linked to a different crt "
     396                 :     "instance from the Python extension DLL, and so the two can be using "
     397                 :     "separate file descriptor tables."
     398                 :   },
     399                 :   { NULL, NULL, 0, NULL }
     400                 : };
     401                 : 
     402                 : /* Note that although this does not return an error code, Python's
     403                 :    importdl.c checks PyErr_Occurred(), so our early returns should
     404                 :    raise exceptions correctly. */
     405               2 : void initnaclimc(void) {
     406                 :   PyObject *module;
     407                 : 
     408               2 :   if (PyType_Ready(&nacldesc_type) < 0)
     409               0 :     return;
     410               2 :   module = Py_InitModule3("naclimc", module_methods, "NaCl IMC");
     411               2 :   if (module == NULL)
     412               0 :     return;
     413               2 :   if (PyModule_AddIntConstant(module, "DESC_MAX", NACL_ABI_IMC_DESC_MAX) != 0)
     414               0 :     return;
     415                 : 
     416               2 :   NaClNrdAllModulesInit();
     417                 : }

Generated by: LCOV version 1.7