LCOV - code coverage report
Current view: top level - python/isc/acl - dns_requestcontext_python.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 77 86 89.5 %
Date: 2012-05-15 Functions: 9 10 90.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 59 116 50.9 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
       2                 :            : //
       3                 :            : // Permission to use, copy, modify, and/or distribute this software for any
       4                 :            : // purpose with or without fee is hereby granted, provided that the above
       5                 :            : // copyright notice and this permission notice appear in all copies.
       6                 :            : //
       7                 :            : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       8                 :            : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       9                 :            : // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
      10                 :            : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      11                 :            : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
      12                 :            : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      13                 :            : // PERFORMANCE OF THIS SOFTWARE.
      14                 :            : 
      15                 :            : // Enable this if you use s# variants with PyArg_ParseTuple(), see
      16                 :            : // http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
      17                 :            : #define PY_SSIZE_T_CLEAN
      18                 :            : 
      19                 :            : // Python.h needs to be placed at the head of the program file, see:
      20                 :            : // http://docs.python.org/py3k/extending/extending.html#a-simple-example
      21                 :            : #include <Python.h>
      22                 :            : 
      23                 :            : #include <sys/types.h>
      24                 :            : #include <sys/socket.h>
      25                 :            : #include <netinet/in.h>
      26                 :            : #include <netdb.h>
      27                 :            : #include <string.h>
      28                 :            : 
      29                 :            : #include <cassert>
      30                 :            : #include <memory>
      31                 :            : #include <string>
      32                 :            : #include <sstream>
      33                 :            : #include <stdexcept>
      34                 :            : 
      35                 :            : #include <boost/scoped_ptr.hpp>
      36                 :            : #include <boost/lexical_cast.hpp>
      37                 :            : 
      38                 :            : #include <exceptions/exceptions.h>
      39                 :            : 
      40                 :            : #include <util/buffer.h>
      41                 :            : #include <util/python/pycppwrapper_util.h>
      42                 :            : 
      43                 :            : #include <dns/name.h>
      44                 :            : #include <dns/rrclass.h>
      45                 :            : #include <dns/rrtype.h>
      46                 :            : #include <dns/rrttl.h>
      47                 :            : #include <dns/rdata.h>
      48                 :            : #include <dns/tsigrecord.h>
      49                 :            : 
      50                 :            : #include <acl/dns.h>
      51                 :            : #include <acl/ip_check.h>
      52                 :            : 
      53                 :            : #include "dns.h"
      54                 :            : #include "dns_requestcontext_python.h"
      55                 :            : 
      56                 :            : using namespace std;
      57                 :            : using boost::scoped_ptr;
      58                 :            : using boost::lexical_cast;
      59                 :            : using namespace isc;
      60                 :            : using namespace isc::dns;
      61                 :            : using namespace isc::dns::rdata;
      62                 :            : using namespace isc::util::python;
      63                 :            : using namespace isc::acl::dns;
      64                 :            : using namespace isc::acl::dns::python;
      65                 :            : 
      66                 :            : namespace isc {
      67                 :            : namespace acl {
      68                 :            : namespace dns {
      69                 :            : namespace python {
      70                 :            : 
      71         [ +  - ]:        144 : struct s_RequestContext::Data {
      72                 :            :     // The constructor.
      73                 :            :     Data(const char* const remote_addr, const unsigned short remote_port,
      74                 :            :          const char* tsig_data, const Py_ssize_t tsig_len)
      75                 :          3 :     {
      76         [ +  + ]:         75 :         createRemoteAddr(remote_addr, remote_port);
      77         [ -  + ]:         72 :         createTSIGRecord(tsig_data, tsig_len);
      78                 :            :     }
      79                 :            : 
      80                 :            :     // A convenient type converter from sockaddr_storage to sockaddr
      81                 :            :     const struct sockaddr& getRemoteSockaddr() const {
      82                 :         79 :         const void* p = &remote_ss;
      83                 :            :         return (*static_cast<const struct sockaddr*>(p));
      84                 :            :     }
      85                 :            : 
      86                 :            :     // The remote (source) IP address of the request.  Note that it needs
      87                 :            :     // a reference to remote_ss.  That's why the latter is stored within
      88                 :            :     // this structure.
      89                 :            :     scoped_ptr<IPAddress> remote_ipaddr;
      90                 :            : 
      91                 :            :     // The effective length of remote_ss.  It's necessary for getnameinfo()
      92                 :            :     // called from sockaddrToText (__str__ backend).
      93                 :            :     socklen_t remote_salen;
      94                 :            : 
      95                 :            :     // The TSIG record included in the request, if any.  If the request
      96                 :            :     // doesn't contain a TSIG, this will be NULL.
      97                 :            :     scoped_ptr<TSIGRecord> tsig_record;
      98                 :            : 
      99                 :            : private:
     100                 :            :     // A helper method for the constructor that is responsible for constructing
     101                 :            :     // the remote address.
     102                 :         75 :     void createRemoteAddr(const char* const remote_addr,
     103                 :            :                           const unsigned short remote_port)
     104                 :            :     {
     105                 :            :         struct addrinfo hints, *res;
     106                 :            :         memset(&hints, 0, sizeof(hints));
     107                 :         75 :         hints.ai_family = AF_UNSPEC;
     108                 :         75 :         hints.ai_socktype = SOCK_DGRAM;
     109                 :         75 :         hints.ai_protocol = IPPROTO_UDP;
     110                 :         75 :         hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
     111                 :            :         const int error(getaddrinfo(remote_addr,
     112                 :         75 :                                     lexical_cast<string>(remote_port).c_str(),
     113         [ +  - ]:         75 :                                     &hints, &res));
     114         [ +  + ]:         75 :         if (error != 0) {
     115 [ +  - ][ +  - ]:          9 :             isc_throw(InvalidParameter, "Failed to convert [" << remote_addr
         [ +  - ][ +  - ]
                 [ +  - ]
     116                 :            :                       << "]:" << remote_port << ", " << gai_strerror(error));
     117                 :            :         }
     118         [ -  + ]:         72 :         assert(sizeof(remote_ss) > res->ai_addrlen);
     119                 :         72 :         memcpy(&remote_ss, res->ai_addr, res->ai_addrlen);
     120                 :         72 :         remote_salen = res->ai_addrlen;
     121                 :         72 :         freeaddrinfo(res);
     122                 :            : 
     123         [ +  - ]:         72 :         remote_ipaddr.reset(new IPAddress(getRemoteSockaddr()));
     124                 :         72 :     }
     125                 :            : 
     126                 :            :     // A helper method for the constructor that is responsible for constructing
     127                 :            :     // the request TSIG.
     128                 :         72 :     void createTSIGRecord(const char* tsig_data, const Py_ssize_t tsig_len) {
     129         [ +  + ]:         72 :         if (tsig_len == 0) {
     130                 :         72 :             return;
     131                 :            :         }
     132                 :            : 
     133                 :            :         // Re-construct the TSIG record from the passed binary.  This should
     134                 :            :         // normally succeed because we are generally expected to be called
     135                 :            :         // from the frontend .py, which converts a valid TSIGRecord in its
     136                 :            :         // wire format.  If some evil or buggy python program directly calls
     137                 :            :         // us with bogus data, validation in libdns++ will trigger an
     138                 :            :         // exception, which will be caught and converted to a Python exception
     139                 :            :         // in RequestContext_init().
     140                 :         19 :         isc::util::InputBuffer b(tsig_data, tsig_len);
     141                 :         38 :         const Name key_name(b);
     142         [ +  - ]:         19 :         const RRType tsig_type(b.readUint16());
     143         [ +  - ]:         19 :         const RRClass tsig_class(b.readUint16());
     144                 :         19 :         const RRTTL ttl(b.readUint32());
     145         [ +  - ]:         19 :         const size_t rdlen(b.readUint16());
     146                 :            :         const ConstRdataPtr rdata = createRdata(tsig_type, tsig_class, b,
     147         [ +  - ]:         19 :                                                 rdlen);
     148                 :            :         tsig_record.reset(new TSIGRecord(key_name, tsig_class, ttl,
     149 [ +  - ][ +  - ]:         19 :                                          *rdata, 0));
     150                 :            :     }
     151                 :            : 
     152                 :            : private:
     153                 :            :     struct sockaddr_storage remote_ss;
     154                 :            : };
     155                 :            : 
     156                 :            : } // namespace python
     157                 :            : } // namespace dns
     158                 :            : } // namespace acl
     159                 :            : } // namespace isc
     160                 :            : 
     161                 :            : 
     162                 :            : //
     163                 :            : // Definition of the classes
     164                 :            : //
     165                 :            : 
     166                 :            : // For each class, we need a struct, a helper functions (init, destroy,
     167                 :            : // and static wrappers around the methods we export), a list of methods,
     168                 :            : // and a type description
     169                 :            : 
     170                 :            : //
     171                 :            : // RequestContext
     172                 :            : //
     173                 :            : 
     174                 :            : // Trivial constructor.
     175                 :          0 : s_RequestContext::s_RequestContext() : cppobj(NULL), data_(NULL) {
     176                 :          0 : }
     177                 :            : 
     178                 :            : // Import pydoc text
     179                 :            : #include "dns_requestcontext_inc.cc"
     180                 :            : 
     181                 :            : namespace {
     182                 :            : // This list contains the actual set of functions we have in
     183                 :            : // python. Each entry has
     184                 :            : // 1. Python method name
     185                 :            : // 2. Our static function here
     186                 :            : // 3. Argument type
     187                 :            : // 4. Documentation
     188                 :            : PyMethodDef RequestContext_methods[] = {
     189                 :            :     { NULL, NULL, 0, NULL }
     190                 :            : };
     191                 :            : 
     192                 :            : int
     193                 :         79 : RequestContext_init(PyObject* po_self, PyObject* args, PyObject*) {
     194                 :         79 :     s_RequestContext* const self = static_cast<s_RequestContext*>(po_self);
     195                 :            : 
     196                 :            :     try {
     197                 :            :         // In this initial implementation, the constructor is simple: It
     198                 :            :         // takes two parameters.  The first parameter should be a Python
     199                 :            :         // socket address object.
     200                 :            :         // For IPv4, it's ('address test', numeric_port); for IPv6,
     201                 :            :         // it's ('address text', num_port, num_flowid, num_zoneid).
     202                 :            :         // The second parameter is wire-format TSIG record in the form of
     203                 :            :         // Python byte data.  If the TSIG isn't included in the request,
     204                 :            :         // its length will be 0.
     205                 :            :         // Below, we parse the argument in the most straightforward way.
     206                 :            :         // As the constructor becomes more complicated, we should probably
     207                 :            :         // make it more structural (for example, we should first retrieve
     208                 :            :         // the python objects, and parse them recursively)
     209                 :            : 
     210                 :            :         const char* remote_addr;
     211                 :            :         unsigned short remote_port;
     212                 :            :         unsigned int remote_flowinfo; // IPv6 only, unused here
     213                 :            :         unsigned int remote_zoneid; // IPv6 only, unused here
     214                 :            :         const char* tsig_data;
     215                 :            :         Py_ssize_t tsig_len;
     216                 :            : 
     217 [ +  + ][ +  + ]:         93 :         if (PyArg_ParseTuple(args, "(sH)y#", &remote_addr, &remote_port,
                 [ +  + ]
     218         [ +  - ]:         79 :                              &tsig_data, &tsig_len) ||
     219                 :            :             PyArg_ParseTuple(args, "(sHII)y#", &remote_addr, &remote_port,
     220                 :            :                              &remote_flowinfo, &remote_zoneid,
     221         [ +  - ]:         14 :                              &tsig_data, &tsig_len))
     222                 :            :         {
     223                 :            :             // We need to clear the error in case the first call to ParseTuple
     224                 :            :             // fails.
     225         [ +  - ]:         75 :             PyErr_Clear();
     226                 :            : 
     227                 :            :             auto_ptr<s_RequestContext::Data> dataptr(
     228                 :            :                 new s_RequestContext::Data(remote_addr, remote_port,
     229         [ +  - ]:         78 :                                            tsig_data, tsig_len));
     230                 :         72 :             self->cppobj = new RequestContext(*dataptr->remote_ipaddr,
     231         [ +  - ]:         72 :                                               dataptr->tsig_record.get());
     232                 :         72 :             self->data_ = dataptr.release();
     233                 :         72 :             return (0);
     234                 :            :         }
     235                 :          6 :     } catch (const exception& ex) {
     236                 :            :         const string ex_what = "Failed to construct RequestContext object: " +
     237 [ -  + ][ -  + ]:          6 :             string(ex.what());
     238 [ -  + ][ -  + ]:          3 :         PyErr_SetString(getACLException("Error"), ex_what.c_str());
     239                 :            :         return (-1);
     240         [ +  - ]:          3 :     } catch (...) {
     241                 :            :         PyErr_SetString(PyExc_RuntimeError,
     242         [ #  # ]:          0 :                         "Unexpected exception in constructing RequestContext");
     243                 :            :         return (-1);
     244                 :            :     }
     245                 :            : 
     246                 :            :     PyErr_SetString(PyExc_TypeError,
     247                 :          4 :                     "Invalid arguments to RequestContext constructor");
     248                 :            : 
     249                 :         79 :     return (-1);
     250                 :            : }
     251                 :            : 
     252                 :            : void
     253                 :         81 : RequestContext_destroy(PyObject* po_self) {
     254                 :         81 :     s_RequestContext* const self = static_cast<s_RequestContext*>(po_self);
     255                 :            : 
     256                 :         81 :     delete self->cppobj;
     257         [ +  + ]:         81 :     delete self->data_;
     258                 :         81 :     Py_TYPE(self)->tp_free(self);
     259                 :         81 : }
     260                 :            : 
     261                 :            : // A helper function for __str__()
     262                 :            : string
     263                 :          7 : sockaddrToText(const struct sockaddr& sa, socklen_t sa_len) {
     264                 :            :     char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
     265         [ -  + ]:          7 :     if (getnameinfo(&sa, sa_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
     266                 :          7 :                     NI_NUMERICHOST | NI_NUMERICSERV)) {
     267                 :            :         // In this context this should never fail.
     268         [ #  # ]:          0 :         isc_throw(Unexpected, "Unexpected failure in getnameinfo");
     269                 :            :     }
     270                 :            : 
     271 [ +  - ][ +  - ]:         14 :     return ("[" + string(hbuf) + "]:" + string(sbuf));
     272                 :            : }
     273                 :            : 
     274                 :            : // for the __str__() method.  This method is provided mainly for internal
     275                 :            : // testing.
     276                 :            : PyObject*
     277                 :          7 : RequestContext_str(PyObject* po_self) {
     278                 :            :     const s_RequestContext* const self =
     279                 :          7 :         static_cast<s_RequestContext*>(po_self);
     280                 :            : 
     281                 :            :     try {
     282   [ +  -  +  - ]:         14 :         stringstream objss;
     283 [ +  - ][ +  - ]:          7 :         objss << "<" << requestcontext_type.tp_name << " object, "
                 [ +  - ]
     284         [ +  - ]:          7 :               << "remote_addr="
     285                 :         14 :               << sockaddrToText(self->data_->getRemoteSockaddr(),
     286 [ +  - ][ +  - ]:         14 :                                 self->data_->remote_salen);
     287         [ +  + ]:          7 :         if (self->data_->tsig_record) {
     288 [ +  - ][ +  - ]:          2 :             objss << ", key=" << self->data_->tsig_record->getName();
     289                 :            :         }
     290         [ +  - ]:          7 :         objss << ">";
     291         [ +  - ]:          7 :         return (Py_BuildValue("s", objss.str().c_str()));
     292                 :          0 :     } catch (const exception& ex) {
     293                 :            :         const string ex_what =
     294                 :            :             "Failed to convert RequestContext object to text: " +
     295 [ #  # ][ #  # ]:          0 :             string(ex.what());
     296         [ #  # ]:          0 :         PyErr_SetString(PyExc_RuntimeError, ex_what.c_str());
     297         [ #  # ]:          0 :     } catch (...) {
     298                 :            :         PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
     299         [ #  # ]:          0 :                         "converting RequestContext object to text");
     300                 :            :     }
     301                 :            :     return (NULL);
     302                 :            : }
     303                 :            : } // end of unnamed namespace
     304                 :            : 
     305                 :            : namespace isc {
     306                 :            : namespace acl {
     307                 :            : namespace dns {
     308                 :            : namespace python {
     309                 :            : // This defines the complete type for reflection in python and
     310                 :            : // parsing of PyObject* to s_RequestContext
     311                 :            : // Most of the functions are not actually implemented and NULL here.
     312                 :            : PyTypeObject requestcontext_type = {
     313                 :            :     PyVarObject_HEAD_INIT(NULL, 0)
     314                 :            :     "isc.acl._dns.RequestContext",
     315                 :            :     sizeof(s_RequestContext),                 // tp_basicsize
     316                 :            :     0,                                  // tp_itemsize
     317                 :            :     RequestContext_destroy,             // tp_dealloc
     318                 :            :     NULL,                               // tp_print
     319                 :            :     NULL,                               // tp_getattr
     320                 :            :     NULL,                               // tp_setattr
     321                 :            :     NULL,                               // tp_reserved
     322                 :            :     NULL,                               // tp_repr
     323                 :            :     NULL,                               // tp_as_number
     324                 :            :     NULL,                               // tp_as_sequence
     325                 :            :     NULL,                               // tp_as_mapping
     326                 :            :     NULL,                               // tp_hash
     327                 :            :     NULL,                               // tp_call
     328                 :            :     RequestContext_str,                 // tp_str
     329                 :            :     NULL,                               // tp_getattro
     330                 :            :     NULL,                               // tp_setattro
     331                 :            :     NULL,                               // tp_as_buffer
     332                 :            :     Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // tp_flags
     333                 :            :     RequestContext_doc,
     334                 :            :     NULL,                               // tp_traverse
     335                 :            :     NULL,                               // tp_clear
     336                 :            :     NULL, // tp_richcompare
     337                 :            :     0,                                  // tp_weaklistoffset
     338                 :            :     NULL,                               // tp_iter
     339                 :            :     NULL,                               // tp_iternext
     340                 :            :     RequestContext_methods,                   // tp_methods
     341                 :            :     NULL,                               // tp_members
     342                 :            :     NULL,                               // tp_getset
     343                 :            :     NULL,                               // tp_base
     344                 :            :     NULL,                               // tp_dict
     345                 :            :     NULL,                               // tp_descr_get
     346                 :            :     NULL,                               // tp_descr_set
     347                 :            :     0,                                  // tp_dictoffset
     348                 :            :     RequestContext_init,                // tp_init
     349                 :            :     NULL,                               // tp_alloc
     350                 :            :     PyType_GenericNew,                  // tp_new
     351                 :            :     NULL,                               // tp_free
     352                 :            :     NULL,                               // tp_is_gc
     353                 :            :     NULL,                               // tp_bases
     354                 :            :     NULL,                               // tp_mro
     355                 :            :     NULL,                               // tp_cache
     356                 :            :     NULL,                               // tp_subclasses
     357                 :            :     NULL,                               // tp_weaklist
     358                 :            :     NULL,                               // tp_del
     359                 :            :     0                                   // tp_version_tag
     360                 :            : };
     361                 :            : 
     362                 :            : bool
     363                 :          2 : initModulePart_RequestContext(PyObject* mod) {
     364                 :            :     // We initialize the static description object with PyType_Ready(),
     365                 :            :     // then add it to the module. This is not just a check! (leaving
     366                 :            :     // this out results in segmentation faults)
     367         [ +  - ]:          2 :     if (PyType_Ready(&requestcontext_type) < 0) {
     368                 :            :         return (false);
     369                 :            :     }
     370                 :          2 :     void* p = &requestcontext_type;
     371         [ +  - ]:          2 :     if (PyModule_AddObject(mod, "RequestContext",
     372                 :          2 :                            static_cast<PyObject*>(p)) < 0) {
     373                 :            :         return (false);
     374                 :            :     }
     375                 :          2 :     Py_INCREF(&requestcontext_type);
     376                 :            : 
     377                 :          2 :     return (true);
     378                 :            : }
     379                 :            : } // namespace python
     380                 :            : } // namespace dns
     381                 :            : } // namespace acl
     382                 :          2 : } // namespace isc

Generated by: LCOV version 1.9