aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2012-09-21 22:17:22 +0000
committerRob Austein <sra@hactrn.net>2012-09-21 22:17:22 +0000
commitb19160a1867486fb8cace6836248374bb7ed2787 (patch)
treee38c899008adc5487e0f8b90a49a9c526dc062a0
parentae09d93b4b4b0b3533032cec6a4d309d4e3899db (diff)
ROA class.
svn path=/branches/tk274/; revision=4730
-rw-r--r--rpkid/ext/POW.c466
1 files changed, 465 insertions, 1 deletions
diff --git a/rpkid/ext/POW.c b/rpkid/ext/POW.c
index 219ad957..a44c1ce8 100644
--- a/rpkid/ext/POW.c
+++ b/rpkid/ext/POW.c
@@ -157,6 +157,7 @@
#define POW_IPAddress_Check(op) PyObject_TypeCheck(op, &POW_IPAddress_Type)
#define POW_ROA_Check(op) PyObject_TypeCheck(op, &POW_ROA_Type)
#define POW_Manifest_Check(op) PyObject_TypeCheck(op, &POW_Manifest_Type)
+#define POW_ROA_Check(op) PyObject_TypeCheck(op, &POW_ROA_Type)
static char pow_module__doc__ [] =
"Python interface to RFC-3779-enabled OpenSSL. This code is intended\n"
@@ -231,7 +232,8 @@ static PyTypeObject
POW_CMS_Type,
POW_IPAddress_Type,
POW_ROA_Type,
- POW_Manifest_Type;
+ POW_Manifest_Type,
+ POW_ROA_Type;
/*
* Object internals.
@@ -6153,6 +6155,467 @@ static PyTypeObject POW_Manifest_Type = {
/*
+ * ROA object.
+ */
+
+static PyObject *
+roa_object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ roa_object *self = NULL;
+
+ if ((self = (roa_object *) type->tp_alloc(type, 0)) != NULL &&
+ (self->roa = ROA_new()) != NULL)
+ return (PyObject *) self;
+
+ Py_XDECREF(self);
+ return NULL;
+}
+
+static PyObject *
+roa_object_der_read_helper(PyTypeObject *type, BIO *bio)
+{
+ roa_object *self;
+
+ if ((self = (roa_object *) roa_object_new(type, NULL, NULL)) == NULL)
+ goto error;
+
+ if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, &self->roa))
+ lose_openssl_error("Couldn't load DER encoded roa");
+
+ return (PyObject *) self;
+
+ error:
+ Py_XDECREF(self);
+ return NULL;
+}
+
+static char roa_object_der_read__doc__[] =
+ "Class method to read a DER-encoded ROA from a string.\n"
+ ;
+
+static PyObject *
+roa_object_der_read(PyTypeObject *type, PyObject *args)
+{
+ return read_from_string_helper(roa_object_der_read_helper, type, args);
+}
+
+static char roa_object_get_version__doc__[] =
+ "This method returns the version number of this ROA.\n"
+ ;
+
+static PyObject *
+roa_object_get_version(roa_object *self)
+{
+ if (self->roa->version)
+ return Py_BuildValue("N", ASN1_INTEGER_to_PyLong(self->roa->version));
+ else
+ return PyInt_FromLong(0);
+}
+
+static char roa_object_set_version__doc__[] =
+ "This method sets the version number of this ROA.\n"
+ "\n"
+ "The \"version\" parameter should be a non-negative integer.\n"
+ "\n"
+ "As of this writing, zero is both the default and the only defined version,\n"
+ "so attempting to set any version number other than zero will fail, as we\n"
+ "don't understand how to write other versions, by definition.\n"
+ ;
+
+static PyObject *
+roa_object_set_version(roa_object *self, PyObject *args)
+{
+ int version = 0;
+
+ if (!PyArg_ParseTuple(args, "|i", &version))
+ goto error;
+
+ if (version != 0)
+ lose("RFC 6482 only defines ROA version zero");
+
+ ASN1_INTEGER_free(self->roa->version);
+ self->roa->version = NULL;
+
+ Py_RETURN_NONE;
+
+ error:
+ return NULL;
+}
+
+static char roa_object_get_asid__doc__[] =
+ "This method returns the Autonomous System ID of this ROA.\n"
+ ;
+
+static PyObject *
+roa_object_get_asid(roa_object *self)
+{
+ return Py_BuildValue("N", ASN1_INTEGER_to_PyLong(self->roa->asID));
+}
+
+static char roa_object_set_asid__doc__[] =
+ "This method sets the Autonomous System ID of this ROA.\n"
+ "\n"
+ "The \"asID\" parameter should be a non-negative integer.\n"
+ ;
+
+static PyObject *
+roa_object_set_asid(roa_object *self, PyObject *args)
+{
+ PyObject *asID = NULL;
+ PyObject *zero = NULL;
+ int ok = 0;
+
+ if (!PyArg_ParseTuple(args, "O", &asID))
+ goto error;
+
+ if ((zero = PyInt_FromLong(0)) == NULL)
+ goto error;
+
+ switch (PyObject_RichCompareBool(asID, zero, Py_GE)) {
+ case -1:
+ goto error;
+ case 0:
+ lose("Negative asID is not allowed");
+ }
+
+ ASN1_INTEGER_free(self->roa->asID);
+
+ if ((self->roa->asID = PyLong_to_ASN1_INTEGER(asID)) == NULL)
+ goto error;
+
+ ok = 1;
+
+ error:
+ Py_XDECREF(zero);
+
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
+}
+
+static char roa_object_get_prefixes__doc__[] =
+ "This method returns the ROA's prefix list. This is a two-element\n"
+ "tuple: the first element is the IPv4 prefix list, the second is the\n"
+ "IPv6 prefix list.\n"
+ "\n"
+ "[Add more description here once final format is stable]\n"
+ ;
+
+static PyObject *
+roa_object_get_prefixes(roa_object *self)
+{
+ PyObject *result = NULL;
+ PyObject *ipv4_result = NULL;
+ PyObject *ipv6_result = NULL;
+ PyObject *item = NULL;
+ ipaddress_object *addr = NULL;
+ int i, j;
+
+ for (i = 0; i < sk_ROAIPAddressFamily_num(self->roa->ipAddrBlocks); i++) {
+ ROAIPAddressFamily *fam = sk_ROAIPAddressFamily_value(self->roa->ipAddrBlocks, i);
+ const unsigned afi = (fam->addressFamily->data[0] << 8) | (fam->addressFamily->data[1]);
+ PyObject **resultp = NULL;
+
+ switch (afi) {
+ case IANA_AFI_IPV4: resultp = &ipv4_result; break;
+ case IANA_AFI_IPV6: resultp = &ipv6_result; break;
+ default: lose_type_error("Unknown AFI");
+ }
+
+ if (fam->addressFamily->length > 2)
+ lose_type_error("Unsupported SAFI");
+
+ if (*resultp != NULL)
+ lose_type_error("Duplicate ROAIPAddressFamily");
+
+ if ((*resultp = PyTuple_New(sk_ROAIPAddress_num(fam->addresses))) == NULL)
+ goto error;
+
+ for (j = 0; j < sk_ROAIPAddress_num(fam->addresses); j++) {
+ ROAIPAddress *a = sk_ROAIPAddress_value(fam->addresses, j);
+ unsigned prefixlen = ((a->IPAddress)->length * 8 - ((a->IPAddress)->flags & 7));
+
+ if ((addr = (ipaddress_object *) POW_IPAddress_Type.tp_alloc(&POW_IPAddress_Type, 0)) == NULL)
+ goto error;
+
+ switch (afi) {
+ case IANA_AFI_IPV4:
+ addr->version = 4;
+ addr->length = 4;
+ addr->af = AF_INET;
+ break;
+ case IANA_AFI_IPV6:
+ addr->version = 6;
+ addr->length = 16;
+ addr->af = AF_INET6;
+ break;
+ }
+
+ memset(addr->address, 0, sizeof(addr->address));
+
+ if (a->IPAddress->length > addr->length)
+ lose("ROAIPAddress BIT STRING too long for AFI");
+
+ if (a->IPAddress->length > 0) {
+ memcpy(addr->address, a->IPAddress->data, a->IPAddress->length);
+
+ if ((a->IPAddress->flags & 7) != 0) {
+ unsigned char mask = 0xFF >> (8 - (a->IPAddress->flags & 7));
+ addr->address[a->IPAddress->length - 1] &= ~mask;
+ }
+ }
+
+ if (a->maxLength == NULL)
+ item = Py_BuildValue("(NIO)", addr, prefixlen, Py_None);
+ else
+ item = Py_BuildValue("(NIl)", addr, prefixlen, ASN1_INTEGER_get(a->maxLength));
+
+ if (item == NULL)
+ goto error;
+
+ PyTuple_SET_ITEM(*resultp, j, item);
+ item = NULL;
+ addr = NULL;
+ }
+ }
+
+ result = Py_BuildValue("(OO)",
+ (ipv4_result == NULL ? Py_None : ipv4_result),
+ (ipv6_result == NULL ? Py_None : ipv6_result));
+
+ error: /* Fall through */
+ Py_XDECREF(addr);
+ Py_XDECREF(item);
+ Py_XDECREF(ipv4_result);
+ Py_XDECREF(ipv6_result);
+
+ return result;
+}
+
+static char roa_object_set_prefixes__doc__[] =
+ "This method sets the ROA's prefix list.\n"
+ "\n"
+ "[Add description here once argument format is stable]\n"
+ ;
+
+static PyObject *
+roa_object_set_prefixes(roa_object *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"ipv4", "ipv6", NULL};
+ STACK_OF(ROAIPAddressFamily) *prefixes = NULL;
+ ROAIPAddressFamily *fam = NULL;
+ ROAIPAddress *a = NULL;
+ PyObject *ipv4_arg = Py_None;
+ PyObject *ipv6_arg = Py_None;
+ PyObject *iterator = NULL;
+ PyObject *item = NULL;
+ int afi, ok = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &ipv4_arg, &ipv6_arg))
+ goto error;
+
+ if ((prefixes = sk_ROAIPAddressFamily_new_null()) == NULL)
+ lose_no_memory();
+
+ /*
+ * Cheap trick. Refactor once I figure out how this all works.
+ */
+
+ for (afi = 0; afi < IANA_AFI_IPV4 + IANA_AFI_IPV6; afi++) {
+ unsigned char afibuf[2];
+ PyObject **argp;
+ int len;
+
+ switch (afi) {
+ case IANA_AFI_IPV4: len = 4; argp = &ipv4_arg; break;
+ case IANA_AFI_IPV6: len = 16; argp = &ipv6_arg; break;
+ default: continue;
+ }
+
+ if (*argp == Py_None)
+ continue;
+
+ afibuf[0] = (afi >> 8) & 0xFF;
+ afibuf[1] = afi & 0xFF;
+
+ if ((iterator = PyObject_GetIter(*argp)) == NULL)
+ goto error;
+
+ while ((item = PyIter_Next(iterator)) != NULL) {
+ unsigned prefixlen, maxprefixlen, bitlen, bytelen;
+ ipaddress_object *addr = NULL;
+ PyObject *maxlenobj = Py_None;
+
+ if (!PyArg_ParseTuple(item, "OI|O", &addr, &prefixlen, &maxlenobj))
+ goto error;
+
+ if (maxlenobj == Py_None)
+ maxprefixlen = prefixlen;
+ else {
+ maxprefixlen = (unsigned) PyInt_AsLong(maxlenobj);
+ if (PyErr_Occurred())
+ goto error;
+ }
+
+ if (!POW_IPAddress_Check(addr) || addr->length != len)
+ lose_type_error("Bad ROA prefix");
+
+ if (prefixlen > addr->length * 8)
+ lose("Bad prefix length");
+
+ if (maxprefixlen > addr->length * 8 || maxprefixlen < prefixlen)
+ lose("Bad maxLength value");
+
+ bytelen = (prefixlen + 7) / 8;
+ bitlen = prefixlen % 8;
+
+ if ((a = ROAIPAddress_new()) == NULL ||
+ (a->IPAddress == NULL && (a->IPAddress = ASN1_BIT_STRING_new()) == NULL) ||
+ !ASN1_BIT_STRING_set(a->IPAddress, addr->address, bytelen))
+ lose_no_memory();
+
+ a->IPAddress->flags &= ~7;
+ a->IPAddress->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+ if (bitlen > 0) {
+ a->IPAddress->data[bytelen - 1] &= ~(0xFF >> bitlen);
+ a->IPAddress->flags |= 8 - bitlen;
+ }
+
+ if (prefixlen != maxprefixlen &&
+ ((a->maxLength = ASN1_INTEGER_new()) == NULL ||
+ !ASN1_INTEGER_set(a->maxLength, maxprefixlen)))
+ lose_no_memory();
+
+ if (fam == NULL &&
+ ((fam = ROAIPAddressFamily_new()) == NULL ||
+ !sk_ROAIPAddressFamily_push(prefixes, fam) ||
+ !ASN1_OCTET_STRING_set(fam->addressFamily, afibuf, sizeof(afibuf))))
+ lose_no_memory();
+
+ if (!sk_ROAIPAddress_push(fam->addresses, a))
+ lose_no_memory();
+
+ a = NULL;
+ Py_XDECREF(item);
+ item = NULL;
+ }
+
+ fam = NULL;
+ Py_XDECREF(iterator);
+ iterator = NULL;
+ }
+
+ sk_ROAIPAddressFamily_pop_free(self->roa->ipAddrBlocks, ROAIPAddressFamily_free);
+ self->roa->ipAddrBlocks = prefixes;
+ prefixes = NULL;
+
+ ok = 1;
+
+ error:
+ sk_ROAIPAddressFamily_pop_free(prefixes, ROAIPAddressFamily_free);
+ ROAIPAddressFamily_free(fam);
+ ROAIPAddress_free(a);
+ Py_XDECREF(iterator);
+ Py_XDECREF(item);
+
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
+}
+
+static char roa_object_der_write__doc__[] =
+ "This method returns a DER encoded roa as a string.\n"
+ ;
+
+static PyObject *
+roa_object_der_write(roa_object *self)
+{
+ PyObject *result = NULL;
+ BIO *bio = NULL;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ lose_no_memory();
+
+ if (!ASN1_item_i2d_bio(ASN1_ITEM_rptr(ROA), bio, self->roa))
+ lose_openssl_error("Unable to write ROA");
+
+ result = BIO_to_PyString_helper(bio);
+
+ error: /* Fall through */
+ BIO_free(bio);
+ return result;
+}
+
+static struct PyMethodDef roa_object_methods[] = {
+ Define_Method(getVersion, roa_object_get_version, METH_NOARGS),
+ Define_Method(setVersion, roa_object_set_version, METH_VARARGS),
+ Define_Method(getASID, roa_object_get_asid, METH_NOARGS),
+ Define_Method(setASID, roa_object_set_asid, METH_VARARGS),
+ Define_Method(getPrefixes, roa_object_get_prefixes, METH_NOARGS),
+ Define_Method(setPrefixes, roa_object_set_prefixes, METH_KEYWORDS),
+ Define_Method(derWrite, roa_object_der_write, METH_NOARGS),
+ Define_Class_Method(derRead, roa_object_der_read, METH_VARARGS),
+ {NULL}
+};
+
+static void
+roa_object_dealloc(roa_object *self)
+{
+ ROA_free(self->roa);
+ self->ob_type->tp_free((PyObject*) self);
+}
+
+static char POW_ROA_Type__doc__[] =
+ "This class provides access to RPKI roa payload.\n"
+ ;
+
+static PyTypeObject POW_ROA_Type = {
+ PyObject_HEAD_INIT(0)
+ 0, /* ob_size */
+ "POW.ROA", /* tp_name */
+ sizeof(roa_object), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)roa_object_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ POW_ROA_Type__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ roa_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ roa_object_new, /* tp_new */
+};
+
+
+
+/*
* Module functions.
*/
@@ -6360,6 +6823,7 @@ init_POW(void)
Define_Class(POW_CMS_Type);
Define_Class(POW_IPAddress_Type);
Define_Class(POW_Manifest_Type);
+ Define_Class(POW_ROA_Type);
#undef Define_Class