First cut at adding support for extension handling in pyOpenSSL.
diff --git a/OpenSSL/crypto/x509.c b/OpenSSL/crypto/x509.c
index 08dc567..273f557 100644
--- a/OpenSSL/crypto/x509.c
+++ b/OpenSSL/crypto/x509.c
@@ -13,6 +13,7 @@
 #include <Python.h>
 #define crypto_MODULE
 #include "crypto.h"
+#include "x509ext.h"
 
 /*
  * X.509 is a standard for digital certificates.  See e.g. the OpenSSL homepage
@@ -300,7 +301,7 @@
 
     py_pkey = crypto_PKey_New(pkey, 1);
     if (py_pkey != NULL) {
-	py_pkey->only_public = 1;
+	    py_pkey->only_public = 1;
     }
     return (PyObject *)py_pkey;
 }
@@ -685,6 +686,148 @@
     return Py_None;
 }
 
+static char crypto_X509_get_extension_count_doc[] = "\n\
+Get the number of extensions on the certificate.\n\
+\n\
+Arguments: self - X509 object\n\
+Returns:    Number of extensions as a Python integer\n\
+";
+
+static PyObject *
+crypto_X509_get_extension_count(crypto_X509Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_extension_count"))
+        return NULL;
+
+    return PyInt_FromLong((long)X509_get_ext_count(self->x509));
+}
+
+static char crypto_X509_get_extension_doc[] = "\n\
+Get a specific extension of the certificate.\n\
+\n\
+Arguments: self - X509 object\n\
+Returns:    An X509 extension object\n\
+";
+
+static PyObject *
+crypto_X509_get_extension(crypto_X509Obj *self, PyObject *args)
+{   
+    crypto_X509ExtensionObj *extobj;
+    int loc;
+    X509_EXTENSION *ext;
+
+    if (!PyArg_ParseTuple(args, "i:get_extension", &loc))
+        return NULL;
+
+    /* will return NULL if loc is outside the range of extensions,
+    not registered as an error*/
+    ext = X509_get_ext(self->x509, loc);
+    if (!ext) {
+        return NULL; /* Should be reported as an IndexError ? */
+        /*exception_from_error_queue();*/
+    }
+    
+    extobj = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type);
+    extobj->x509_extension = X509_EXTENSION_dup(ext);
+    
+    return extobj;
+}
+
+/* Copied from openssl/crypto/x509v3/v3_utl.c */
+
+static void str_free(void *str)
+{
+	OPENSSL_free(str);
+}
+static int sk_strcmp(const char * const *a, const char * const *b)
+{
+	return strcmp(*a, *b);
+}
+static int append_ia5(STACK **sk, ASN1_IA5STRING *value)
+{
+	char *tmp;
+	/* First some sanity checks */
+	if(value->type != V_ASN1_IA5STRING) return 1;
+	if(!value->data || !value->length) return 1;
+	if(!*sk) *sk = sk_new(sk_strcmp);
+	if(!*sk) return 0;
+	/* Don't add duplicates */
+	if(sk_find(*sk, (char *)value->data) != -1) return 1;
+	tmp = BUF_strdup((char *)value->data);
+	if(!tmp || !sk_push(*sk, tmp)) {
+    	sk_pop_free(*sk, str_free);
+    	*sk = NULL;
+		return 0;
+	}
+	return 1;
+}
+
+/* -------------------------------------------------*/
+/* !!! This only works for ASN1_IA5STRING values !!!*/
+/* -------------------------------------------------*/
+static STACK *get_ia5_san_value(GENERAL_NAMES *gens, int type)
+{
+	STACK *ret = NULL;
+	GENERAL_NAME *gen;
+	int i;
+
+	for(i = 0; i < sk_GENERAL_NAME_num(gens); i++)
+	{
+		gen = sk_GENERAL_NAME_value(gens, i);
+		if(gen->type != type) continue;
+		if(!append_ia5(&ret, gen->d.ia5)) return NULL;
+	}
+	return ret;
+}
+
+static char crypto_X509_get_subjectaltname_of_type_doc[] = "\n\
+Get a list of the values of some subjectaltname extensions\n\
+Presently the DNS,EMAIL and URI types are supported.\n\
+\n\
+Arguments: self - X509 object\n\
+            type - one of DNS,EMAIL or URI\n\
+Returns:    A list of values\n\
+";
+
+static PyObject *
+crypto_X509_get_subjectaltname_of_type(crypto_X509Obj *self, PyObject *args)
+{   
+	GENERAL_NAMES *gens;
+	STACK *ret;
+    char *s;
+    char *type;
+    PyObject *list;
+    int san_type;
+
+    if (!PyArg_ParseTuple(args, "s:get_subjectaltname_of_type", &type))
+        return NULL;
+
+    list = PyList_New(0);
+	gens = X509_get_ext_d2i(self->x509, NID_subject_alt_name, NULL, NULL);
+	if (gens == NULL) {
+        return list;
+    }
+    
+    /* These are the ones that are labeled/stored as ASN1_IA5STRINGs */
+    if (strcmp(type,"DNS") == 0) san_type = GEN_DNS;
+    else if(strcmp(type,"EMAIL") == 0) san_type = GEN_EMAIL;
+    else if(strcmp(type,"URI") == 0) san_type = GEN_URI;
+    else {
+        PyErr_SetString(PyExc_AttributeError, type);
+        return NULL;
+    }
+    
+	ret = get_ia5_san_value(gens, san_type);
+	if (ret != NULL) {
+        for ( ; s = sk_pop(ret) ; ){
+            PyList_Append(list, PyString_FromString(s));
+        }
+        return list;
+    }
+    else {
+        return list;
+    }
+}
 /*
  * ADD_METHOD(name) expands to a correct PyMethodDef declaration
  *   {  'name', (PyCFunction)crypto_X509_name, METH_VARARGS }
@@ -715,6 +858,9 @@
     ADD_METHOD(subject_name_hash),
     ADD_METHOD(digest),
     ADD_METHOD(add_extensions),
+    ADD_METHOD(get_extension),
+    ADD_METHOD(get_extension_count),
+    ADD_METHOD(get_subjectaltname_of_type),
     { NULL, NULL }
 };
 #undef ADD_METHOD
diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py
index f2831c7..8e96b92 100644
--- a/OpenSSL/test/test_crypto.py
+++ b/OpenSSL/test/test_crypto.py
@@ -947,6 +947,26 @@
     """
     pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
 
+    extpem = """
+-----BEGIN CERTIFICATE-----
+MIIC3jCCAkegAwIBAgIJAJHFjlcCgnQzMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNV
+BAYTAlNFMRUwEwYDVQQIEwxXZXN0ZXJib3R0b20xEjAQBgNVBAoTCUNhdGFsb2dp
+eDENMAsGA1UEAxMEUm9vdDAeFw0wODA0MjIxNDQ1MzhaFw0wOTA0MjIxNDQ1Mzha
+MFQxCzAJBgNVBAYTAlNFMQswCQYDVQQIEwJXQjEUMBIGA1UEChMLT3Blbk1ldGFk
+aXIxIjAgBgNVBAMTGW5vZGUxLm9tMi5vcGVubWV0YWRpci5vcmcwgZ8wDQYJKoZI
+hvcNAQEBBQADgY0AMIGJAoGBAPIcQMrwbk2nESF/0JKibj9i1x95XYAOwP+LarwT
+Op4EQbdlI9SY+uqYqlERhF19w7CS+S6oyqx0DRZSk4Y9dZ9j9/xgm2u/f136YS1u
+zgYFPvfUs6PqYLPSM8Bw+SjJ+7+2+TN+Tkiof9WP1cMjodQwOmdsiRbR0/J7+b1B
+hec1AgMBAAGjgcQwgcEwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
+TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFIdHsBcMVVMbAO7j6NCj
+03HgLnHaMB8GA1UdIwQYMBaAFL2h9Bf9Mre4vTdOiHTGAt7BRY/8MEYGA1UdEQQ/
+MD2CDSouZXhhbXBsZS5vcmeCESoub20yLmV4bWFwbGUuY29thwSC7wgKgRNvbTJA
+b3Blbm1ldGFkaXIub3JnMA0GCSqGSIb3DQEBBQUAA4GBALd7WdXkp2KvZ7/PuWZA
+MPlIxyjS+Ly11+BNE0xGQRp9Wz+2lABtpgNqssvU156+HkKd02rGheb2tj7MX9hG
+uZzbwDAZzJPjzDQDD7d3cWsrVcfIdqVU7epHqIadnOF+X0ghJ39pAm6VVadnSXCt
+WpOdIpB8KksUTCzV591Nr1wd
+-----END CERTIFICATE-----
+    """
     def signable(self):
         """
         Create and return a new L{X509}.
@@ -1199,6 +1219,29 @@
             b("A8:EB:07:F8:53:25:0A:F2:56:05:C5:A5:C4:C4:C7:15"))
 
 
+    def test_extension_count(self):
+        """
+        L{X509.get_extension_count} returns the number of extensions that are
+        present in the certificate
+        """
+        cert = load_certificate(FILETYPE_PEM, self.extpem)
+        self.assertEqual(cert.get_extension_count(),5)
+
+
+    def test_subjectaltname_of_type(self):
+        """
+        L{X509.get_subjectaltname_of_type} returns a list of strings
+        representing the values of the subjectAltName extensions of the
+        specified type
+        """
+        cert = load_certificate(FILETYPE_PEM, self.extpem)
+        domains = cert.get_subjectaltname_of_type("DNS")
+        domains.sort()
+        self.assertEqual(domains,['*.example.org', '*.om2.exmaple.com'])
+        self.assertEqual(cert.get_subjectaltname_of_type("EMAIL"),['om2@openmetadir.org'])
+        self.assertEqual(cert.get_subjectaltname_of_type("URI"),[])
+
+
     def test_invalid_digest_algorithm(self):
         """
         L{X509.digest} raises L{ValueError} if called with an unrecognized hash