rrclass_python.cc 12.9 KB
Newer Older
1
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $Id$

#include <dns/rrclass.h>
using namespace isc::dns;

//
// Declaration of the custom exceptions
// Initialization and addition of these go in the initModulePart
// function at the end of this file
//
static PyObject* po_InvalidRRClass;
static PyObject* po_IncompleteRRClass;

//
// Definition of the classes
//

// For each class, we need a struct, a helper functions (init, destroy,
// and static wrappers around the methods we export), a list of methods,
// and a type description

//
// RRClass
//

40
// The s_* Class simply covers one instantiation of the object
41
42
class s_RRClass : public PyObject {
public:
43
    RRClass* rrclass;
44
};
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

//
// We declare the functions here, the definitions are below
// the type definition of the object, since both can use the other
//

// General creation and destruction
static int RRClass_init(s_RRClass* self, PyObject* args);
static void RRClass_destroy(s_RRClass* self);

// These are the functions we export
static PyObject* RRClass_toText(s_RRClass* self);
// This is a second version of toText, we need one where the argument
// is a PyObject*, for the str() function in python.
static PyObject* RRClass_str(PyObject* self);
static PyObject* RRClass_toWire(s_RRClass* self, PyObject* args);
static PyObject* RRClass_getCode(s_RRClass* self);
static PyObject* RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op);

Jelte Jansen's avatar
Jelte Jansen committed
64
65
66
67
68
69
70
71
// Static function for direct class creation
static PyObject* RRClass_IN(s_RRClass *self);
static PyObject* RRClass_CH(s_RRClass *self);
static PyObject* RRClass_HS(s_RRClass *self);
static PyObject* RRClass_NONE(s_RRClass *self);
static PyObject* RRClass_ANY(s_RRClass *self);


72
73
74
75
76
77
78
// This list contains the actual set of functions we have in
// python. Each entry has
// 1. Python method name
// 2. Our static function here
// 3. Argument type
// 4. Documentation
static PyMethodDef RRClass_methods[] = {
79
    { "to_text", reinterpret_cast<PyCFunction>(RRClass_toText), METH_NOARGS,
Jelte Jansen's avatar
Jelte Jansen committed
80
      "Returns the string representation" },
81
    { "to_wire", reinterpret_cast<PyCFunction>(RRClass_toWire), METH_VARARGS,
Jelte Jansen's avatar
Jelte Jansen committed
82
83
84
85
86
87
      "Converts the RRClass object to wire format.\n"
      "The argument can be either a MessageRenderer or an object that "
      "implements the sequence interface. If the object is mutable "
      "(for instance a bytearray()), the wire data is added in-place.\n"
      "If it is not (for instance a bytes() object), a new object is "
      "returned" },
88
    { "get_code", reinterpret_cast<PyCFunction>(RRClass_getCode), METH_NOARGS,
Jelte Jansen's avatar
Jelte Jansen committed
89
      "Returns the class code as an integer" },
90
91
92
93
94
    { "IN", reinterpret_cast<PyCFunction>(RRClass_IN), METH_NOARGS | METH_STATIC, "Creates an IN RRClass" },
    { "CH", reinterpret_cast<PyCFunction>(RRClass_CH), METH_NOARGS | METH_STATIC, "Creates a CH RRClass" },
    { "HS", reinterpret_cast<PyCFunction>(RRClass_HS), METH_NOARGS | METH_STATIC, "Creates an HS RRClass" },
    { "NONE", reinterpret_cast<PyCFunction>(RRClass_NONE), METH_NOARGS | METH_STATIC, "Creates a NONE RRClass" },
    { "ANY", reinterpret_cast<PyCFunction>(RRClass_ANY), METH_NOARGS | METH_STATIC, "Creates an ANY RRClass" },
95
96
97
98
99
100
101
102
103
    { NULL, NULL, 0, NULL }
};

// This defines the complete type for reflection in python and
// parsing of PyObject* to s_RRClass
// Most of the functions are not actually implemented and NULL here.
static PyTypeObject rrclass_type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "libdns_python.RRClass",
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    sizeof(s_RRClass),                  // tp_basicsize
    0,                                  // tp_itemsize
    (destructor)RRClass_destroy,        // tp_dealloc
    NULL,                               // tp_print
    NULL,                               // tp_getattr
    NULL,                               // tp_setattr
    NULL,                               // tp_reserved
    NULL,                               // tp_repr
    NULL,                               // tp_as_number
    NULL,                               // tp_as_sequence
    NULL,                               // tp_as_mapping
    NULL,                               // tp_hash 
    NULL,                               // tp_call
    RRClass_str,                        // tp_str
    NULL,                               // tp_getattro
    NULL,                               // tp_setattro
    NULL,                               // tp_as_buffer
    Py_TPFLAGS_DEFAULT,                 // tp_flags
Jelte Jansen's avatar
Jelte Jansen committed
122
123
124
125
    "The RRClass class encapsulates DNS resource record classes.\n"
    "This class manages the 16-bit integer class codes in quite a straightforward"
    "way.  The only non trivial task is to handle textual representations of"
    "RR classes, such as \"IN\", \"CH\", or \"CLASS65534\".",
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
    NULL,                               // tp_traverse
    NULL,                               // tp_clear
    (richcmpfunc)RRClass_richcmp,       // tp_richcompare
    0,                                  // tp_weaklistoffset
    NULL,                               // tp_iter
    NULL,                               // tp_iternext
    RRClass_methods,                    // tp_methods
    NULL,                               // tp_members
    NULL,                               // tp_getset
    NULL,                               // tp_base
    NULL,                               // tp_dict
    NULL,                               // tp_descr_get
    NULL,                               // tp_descr_set
    0,                                  // tp_dictoffset
    (initproc)RRClass_init,             // tp_init
    NULL,                               // tp_alloc
    PyType_GenericNew,                  // tp_new
    NULL,                               // tp_free
    NULL,                               // tp_is_gc
    NULL,                               // tp_bases
    NULL,                               // tp_mro
    NULL,                               // tp_cache
    NULL,                               // tp_subclasses
    NULL,                               // tp_weaklist
    NULL,                               // tp_del
    0                                   // tp_version_tag
152
153
154
};

static int
155
RRClass_init(s_RRClass* self, PyObject* args) {
156
157
158
159
160
161
162
163
164
165
166
167
168
    const char* s;
    unsigned int i;
    PyObject* bytes = NULL;
    // The constructor argument can be a string ("IN"), an integer (1),
    // or a sequence of numbers between 0 and 255 (wire code)

    // Note that PyArg_ParseType can set PyError, and we need to clear
    // that if we try several like here. Otherwise the *next* python
    // call will suddenly appear to throw an exception.
    // (the way to do exceptions is to set PyErr and return -1)
    try {
        if (PyArg_ParseTuple(args, "s", &s)) {
            self->rrclass = new RRClass(s);
Jelte Jansen's avatar
Jelte Jansen committed
169
            return (0);
170
171
172
173
        } else if (PyArg_ParseTuple(args, "I", &i)) {
            PyErr_Clear();
            if (i > 65535) {
                PyErr_SetString(po_InvalidRRClass, "Class number too high");
Jelte Jansen's avatar
Jelte Jansen committed
174
                return (-1);
175
176
            }
            self->rrclass = new RRClass(i);
Jelte Jansen's avatar
Jelte Jansen committed
177
            return (0);
178
179
180
181
        } else if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
            uint8_t data[2];
            int result = readDataFromSequence(data, 2, bytes);
            if (result != 0) {
Jelte Jansen's avatar
Jelte Jansen committed
182
                return (result);
183
184
185
186
            }
            InputBuffer ib(data, 2);
            self->rrclass = new RRClass(ib);
            PyErr_Clear();
Jelte Jansen's avatar
Jelte Jansen committed
187
            return (0);
188
        }
189
190
    // Incomplete is never thrown, a type error would have already been raised
    //when we try to read the 2 bytes above
191
192
193
    } catch (InvalidRRClass ic) {
        PyErr_Clear();
        PyErr_SetString(po_InvalidRRClass, ic.what());
Jelte Jansen's avatar
Jelte Jansen committed
194
        return (-1);
195
196
197
198
    }
    PyErr_Clear();
    PyErr_SetString(PyExc_TypeError,
                    "no valid type in constructor argument");
Jelte Jansen's avatar
Jelte Jansen committed
199
    return (-1);
200
201
202
}

static void
203
RRClass_destroy(s_RRClass* self) {
204
    delete self->rrclass;
205
206
207
208
209
    self->rrclass = NULL;
    Py_TYPE(self)->tp_free(self);
}

static PyObject*
210
RRClass_toText(s_RRClass* self) {
211
    // Py_BuildValue makes python objects from native data
Jelte Jansen's avatar
Jelte Jansen committed
212
    return (Py_BuildValue("s", self->rrclass->toText().c_str()));
213
214
215
}

static PyObject*
216
RRClass_str(PyObject* self) {
217
    // Simply call the to_text method we already defined
218
219
220
    return PyObject_CallMethod(self,
                               const_cast<char*>("to_text"),
                               const_cast<char*>(""));
221
222
223
}

static PyObject*
224
RRClass_toWire(s_RRClass* self, PyObject* args) {
225
226
227
228
229
230
231
232
    PyObject* bytes;
    s_MessageRenderer* mr;
    
    if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
        PyObject* bytes_o = bytes;
        
        OutputBuffer buffer(2);
        self->rrclass->toWire(buffer);
233
        PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
234
235
236
237
        PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
        // We need to release the object we temporarily created here
        // to prevent memory leak
        Py_DECREF(n);
Jelte Jansen's avatar
Jelte Jansen committed
238
        return (result);
239
240
241
242
243
244
245
246
247
    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
        self->rrclass->toWire(*mr->messagerenderer);
        // If we return NULL it is seen as an error, so use this for
        // None returns
        Py_RETURN_NONE;
    }
    PyErr_Clear();
    PyErr_SetString(PyExc_TypeError,
                    "toWire argument must be a sequence object or a MessageRenderer");
Jelte Jansen's avatar
Jelte Jansen committed
248
    return (NULL);
249
250
251
}

static PyObject*
252
RRClass_getCode(s_RRClass* self) {
Jelte Jansen's avatar
Jelte Jansen committed
253
    return (Py_BuildValue("I", self->rrclass->getCode()));
254
255
256
}

static PyObject* 
257
RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) {
258
259
    bool c;

260
261
    // Check for null and if the types match. If different type,
    // simply return False
Jelte Jansen's avatar
Jelte Jansen committed
262
    if (!other || (self->ob_type != other->ob_type)) {
263
264
265
        Py_RETURN_FALSE;
    }

266
267
268
269
270
271
272
273
274
    switch (op) {
    case Py_LT:
        c = *self->rrclass < *other->rrclass;
        break;
    case Py_LE:
        c = *self->rrclass < *other->rrclass ||
            *self->rrclass == *other->rrclass;
        break;
    case Py_EQ:
Jelte Jansen's avatar
Jelte Jansen committed
275
        c = *self->rrclass == *other->rrclass;
276
277
        break;
    case Py_NE:
Jelte Jansen's avatar
Jelte Jansen committed
278
        c = *self->rrclass != *other->rrclass;
279
280
281
282
283
284
285
286
287
        break;
    case Py_GT:
        c = *other->rrclass < *self->rrclass;
        break;
    case Py_GE:
        c = *other->rrclass < *self->rrclass ||
            *self->rrclass == *other->rrclass;
        break;
    default:
Jelte Jansen's avatar
Jelte Jansen committed
288
289
        PyErr_SetString(PyExc_IndexError,
                        "Unhandled rich comparison operator");
Jelte Jansen's avatar
Jelte Jansen committed
290
        return (NULL);
291
292
293
294
295
296
    }
    if (c)
        Py_RETURN_TRUE;
    else
        Py_RETURN_FALSE;
}
Jelte Jansen's avatar
Jelte Jansen committed
297

Jelte Jansen's avatar
Jelte Jansen committed
298
299
300
301
//
// Common function for RRClass_IN/CH/etc.
//
static PyObject* RRClass_createStatic(RRClass stc) {
Jelte Jansen's avatar
Jelte Jansen committed
302
303
    s_RRClass* ret = PyObject_New(s_RRClass, &rrclass_type);
    if (ret != NULL) {
Jelte Jansen's avatar
Jelte Jansen committed
304
        ret->rrclass = new RRClass(stc);
Jelte Jansen's avatar
Jelte Jansen committed
305
    }
Jelte Jansen's avatar
Jelte Jansen committed
306
    return (ret);
Jelte Jansen's avatar
Jelte Jansen committed
307
308
}

Jelte Jansen's avatar
Jelte Jansen committed
309
static PyObject* RRClass_IN(s_RRClass *self UNUSED_PARAM) {
Jelte Jansen's avatar
Jelte Jansen committed
310
    return (RRClass_createStatic(RRClass::IN()));
Jelte Jansen's avatar
Jelte Jansen committed
311
312
}

313
static PyObject* RRClass_CH(s_RRClass *self UNUSED_PARAM) {
Jelte Jansen's avatar
Jelte Jansen committed
314
    return (RRClass_createStatic(RRClass::CH()));
Jelte Jansen's avatar
Jelte Jansen committed
315
316
}

317
static PyObject* RRClass_HS(s_RRClass *self UNUSED_PARAM) {
Jelte Jansen's avatar
Jelte Jansen committed
318
    return (RRClass_createStatic(RRClass::HS()));
Jelte Jansen's avatar
Jelte Jansen committed
319
320
}

321
static PyObject* RRClass_NONE(s_RRClass *self UNUSED_PARAM) {
Jelte Jansen's avatar
Jelte Jansen committed
322
    return (RRClass_createStatic(RRClass::NONE()));
Jelte Jansen's avatar
Jelte Jansen committed
323
324
}

325
static PyObject* RRClass_ANY(s_RRClass *self UNUSED_PARAM) {
Jelte Jansen's avatar
Jelte Jansen committed
326
    return (RRClass_createStatic(RRClass::ANY()));
Jelte Jansen's avatar
Jelte Jansen committed
327
}
328
329
330
331
332
// end of RRClass


// Module Initialization, all statics are initialized here
bool
333
initModulePart_RRClass(PyObject* mod) {
334
335
336
337
338
339
340
341
342
343
344
345
    // Add the exceptions to the module
    po_InvalidRRClass = PyErr_NewException("libdns_python.InvalidRRClass", NULL, NULL);
    Py_INCREF(po_InvalidRRClass);
    PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass);
    po_IncompleteRRClass = PyErr_NewException("libdns_python.IncompleteRRClass", NULL, NULL);
    Py_INCREF(po_IncompleteRRClass);
    PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass);

    // We initialize the static description object with PyType_Ready(),
    // then add it to the module. This is not just a check! (leaving
    // this out results in segmentation faults)
    if (PyType_Ready(&rrclass_type) < 0) {
Jelte Jansen's avatar
Jelte Jansen committed
346
        return (false);
347
348
349
    }
    Py_INCREF(&rrclass_type);
    PyModule_AddObject(mod, "RRClass",
350
                       reinterpret_cast<PyObject*>(&rrclass_type));
351
    
Jelte Jansen's avatar
Jelte Jansen committed
352
    return (true);
353
}