mirror of
https://github.com/Hopiu/linkchecker.git
synced 2026-03-22 00:40:30 +00:00
git-svn-id: https://linkchecker.svn.sourceforge.net/svnroot/linkchecker/trunk/linkchecker@192 e7d03fd6-7b0d-0410-9947-9c21f3af8025
415 lines
10 KiB
C
415 lines
10 KiB
C
/* @(#)ssl.c 1.1 VMS-99/01/30 python wrapper for SSLeay https
|
|
*/
|
|
|
|
#include "Python.h"
|
|
#if defined(WITH_THREAD) && !defined(HAVE_GETHOSTBYNAME_R) &&\
|
|
!defined(MS_WINDOWS)
|
|
#include "thread.h"
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#ifndef MS_WINDOWS
|
|
#include <sys/socket.h>
|
|
#else
|
|
#include <winsock.h>
|
|
#endif
|
|
|
|
#if defined(PYOS_OS2)
|
|
#define INCL_DOS
|
|
#define INCL_DOSERRORS
|
|
#define INCL_NOPMAPI
|
|
#include <os2.h>
|
|
#endif
|
|
|
|
#include "ssl.h"
|
|
#include "err.h"
|
|
|
|
/*
|
|
some hacks to choose between K&R or ANSI style function
|
|
definitions. For NT to build this as an extension module (ie, DLL)
|
|
it must be compiled by the C++ compiler, as it takes the address of
|
|
a static data item exported from the main Python DLL.
|
|
*/
|
|
#ifdef MS_WINDOWS
|
|
#define FORCE_ANSI_FUNC_DEFS
|
|
#endif
|
|
|
|
#if defined(PYOS_OS2)
|
|
#define FORCE_ANSI_FUNC_DEFS
|
|
#endif
|
|
|
|
#ifdef FORCE_ANSI_FUNC_DEFS
|
|
#define BUILD_FUNC_DEF_1( fnname, arg1type, arg1name ) \
|
|
fnname( arg1type arg1name )
|
|
|
|
#define BUILD_FUNC_DEF_2( fnname, arg1type, arg1name, arg2type, arg2name ) \
|
|
fnname( arg1type arg1name, arg2type arg2name )
|
|
|
|
#else /* !FORCE_ANSI_FN_DEFS */
|
|
#define BUILD_FUNC_DEF_1( fnname, arg1type, arg1name ) \
|
|
fnname( arg1name ) \
|
|
arg1type arg1name;
|
|
|
|
#define BUILD_FUNC_DEF_2( fnname, arg1type, arg1name, arg2type, arg2name ) \
|
|
fnname( arg1name, arg2name ) \
|
|
arg1type arg1name; \
|
|
arg2type arg2name;
|
|
#endif /* !FORCE_ANSI_FN_DEFS */
|
|
|
|
/* Global variable holding the exception type for errors detected
|
|
by this module (but not argument type or memory errors, etc.). */
|
|
|
|
static PyObject *PySslError;
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
int sock_fd;
|
|
PyObject *x_attr; /* attributes dictionary */
|
|
SSL_CTX *ctx;
|
|
SSL *ssl;
|
|
X509 *server_cert;
|
|
BIO *sbio;
|
|
char server[256];
|
|
char issuer[256];
|
|
} PySslObject;
|
|
|
|
staticforward PyTypeObject SSL_Type;
|
|
#define PySslObject_Check(v) ((v)->ob_type == &SSL_Type)
|
|
|
|
/*
|
|
* raise an error according to errno, return NULL
|
|
*/
|
|
static PyObject *
|
|
PySsl_errno ()
|
|
{
|
|
#ifdef MS_WINDOWS
|
|
if (WSAGetLastError()) {
|
|
PyObject *v = Py_BuildValue("(is)",WSAGetLastError(),"winsock error");
|
|
|
|
if (v) {
|
|
PyErr_SetObject(PySslError,v);
|
|
Py_DECREF(v);
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
return PyErr_SetFromErrno(PySslError);
|
|
}
|
|
|
|
/*
|
|
* format SSl error string
|
|
*/
|
|
static int
|
|
BUILD_FUNC_DEF_2 (PySsl_err_str, unsigned long, e, char *, buf)
|
|
{
|
|
unsigned long l = ERR_GET_LIB(e);
|
|
unsigned long f = ERR_GET_FUNC(e);
|
|
unsigned long r = ERR_GET_REASON(e);
|
|
char* ls = (char*)ERR_lib_error_string(e);
|
|
char* fs = (char*)ERR_func_error_string(e);
|
|
char* rs = (char*)ERR_reason_error_string(e);
|
|
char* bp = buf + 2; /* skip two initial blanks */
|
|
|
|
(void)strcpy(buf," none:"); /* initialize buffer */
|
|
bp += (ls) ? sprintf(bp,"%s:",ls) :
|
|
((l) ? sprintf(bp,"lib %lu:",l) : 0);
|
|
bp += (fs) ? sprintf(bp,"%s ",fs) :
|
|
((f) ? sprintf(bp,"func %lu:",f) : 0);
|
|
bp += (rs) ? sprintf(bp,"%s:",rs) :
|
|
((r) ? sprintf(bp,"reason(%lu):",r) : 0);
|
|
*bp-- = 0; /* suppress last divider (:) */
|
|
return (bp - buf);
|
|
}
|
|
|
|
/*
|
|
* report SSL core errors
|
|
*/
|
|
static PySslObject *
|
|
PySsl_errors ()
|
|
{
|
|
#define PY_SSL_ERR_MAX 256
|
|
|
|
unsigned long e;
|
|
char buf[2 * PY_SSL_ERR_MAX];
|
|
char *bf = buf;
|
|
|
|
while (((bf - buf) < PY_SSL_ERR_MAX) && (e = ERR_get_error()))
|
|
bf += PySsl_err_str(e,bf);
|
|
{
|
|
PyObject *v = Py_BuildValue("(sss)", "ssl","core",buf+2);
|
|
if (v != NULL) {
|
|
PyErr_SetObject(PySslError,v);
|
|
Py_DECREF(v);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* report SSL application layer errors
|
|
*/
|
|
static PySslObject *
|
|
BUILD_FUNC_DEF_2 (PySsl_app_errors, SSL *, s, int, ret)
|
|
{
|
|
int err = SSL_get_error(s,ret);
|
|
char *str;
|
|
|
|
switch (err) {
|
|
case SSL_ERROR_SSL:
|
|
return (PySsl_errors());
|
|
case SSL_ERROR_SYSCALL:
|
|
return ((PySslObject *)PySsl_errno());
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
str = "End of data";
|
|
break;
|
|
case SSL_ERROR_WANT_READ:
|
|
str = "Want read";
|
|
break;
|
|
case SSL_ERROR_WANT_WRITE:
|
|
str = "Want write";
|
|
break;
|
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
str = "Want x509 lookup";
|
|
break;
|
|
case SSL_ERROR_WANT_CONNECT:
|
|
str = "Want connect";
|
|
break;
|
|
default:
|
|
str = "Unknown";
|
|
break;
|
|
}
|
|
{
|
|
PyObject *v = Py_BuildValue("(sis)", "ssl",err, str);
|
|
if (v != NULL) {
|
|
PyErr_SetObject(PySslError,v);
|
|
Py_DECREF(v);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/* ssl.read(len) method */
|
|
|
|
static PyObject *
|
|
BUILD_FUNC_DEF_2 (PySslObj_read, PySslObject *, self, PyObject *, args)
|
|
{
|
|
int len, n;
|
|
PyObject *buf;
|
|
|
|
if (!PyArg_ParseTuple(args,"i",&len))
|
|
return (NULL);
|
|
if (!(buf = PyString_FromStringAndSize((char *)0,len)))
|
|
return (NULL);
|
|
Py_BEGIN_ALLOW_THREADS
|
|
|
|
n = SSL_read(self->ssl,PyString_AsString(buf),len);
|
|
|
|
Py_END_ALLOW_THREADS
|
|
|
|
switch (SSL_get_error(self->ssl,n)) {
|
|
case SSL_ERROR_NONE: /* good return value */
|
|
break;
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
case SSL_ERROR_SYSCALL:
|
|
if (!n) /* fix SSL_ERROR_SYCSALL errno=0 case */
|
|
break;
|
|
/* fall thru here */
|
|
default:
|
|
Py_DECREF(buf);
|
|
(void)PySsl_app_errors(self->ssl,n);
|
|
return (NULL);
|
|
}
|
|
if ((n != len) && (_PyString_Resize(&buf,n) < 0))
|
|
return (NULL);
|
|
return (buf);
|
|
}
|
|
|
|
/* ssl.write(data,len) method */
|
|
|
|
static PyObject *
|
|
BUILD_FUNC_DEF_2 (PySslObj_write, PySslObject *, self, PyObject *, args)
|
|
{
|
|
char *buf;
|
|
int len, n;
|
|
if (!PyArg_ParseTuple(args, "si", &buf, &len))
|
|
return NULL;
|
|
|
|
/* Note: flags are ignored */
|
|
|
|
Py_BEGIN_ALLOW_THREADS
|
|
|
|
n = SSL_write(self->ssl,buf,len);
|
|
|
|
Py_END_ALLOW_THREADS
|
|
if (n < 0)
|
|
return (PySsl_errno());
|
|
return (PyInt_FromLong((long)n));
|
|
}
|
|
|
|
/* ssl.server() method */
|
|
|
|
static PyObject *
|
|
BUILD_FUNC_DEF_2 (PySslObj_server, PySslObject *, self, PyObject *, args)
|
|
{
|
|
if (!PyArg_NoArgs(args))
|
|
return (NULL);
|
|
return (PyString_FromString(self->server));
|
|
}
|
|
|
|
/* ssl.issuer() method */
|
|
|
|
static PyObject *
|
|
BUILD_FUNC_DEF_2 (PySslObj_issuer, PySslObject *, self, PyObject *, args)
|
|
{
|
|
if (!PyArg_NoArgs(args))
|
|
return (NULL);
|
|
return (PyString_FromString(self->issuer));
|
|
}
|
|
|
|
/* SSL object methods */
|
|
|
|
static PyMethodDef PySslObj_methods[] = {
|
|
{"read", (PyCFunction)PySslObj_read,1},
|
|
{"write", (PyCFunction)PySslObj_write,1},
|
|
{"server", (PyCFunction)PySslObj_server},
|
|
{"issuer", (PyCFunction)PySslObj_issuer},
|
|
{ NULL, NULL}
|
|
};
|
|
|
|
static void
|
|
BUILD_FUNC_DEF_1 (PySsl_dealloc, PySslObject *, self)
|
|
{
|
|
if (self->server_cert) /* possible not to have one? */
|
|
X509_free(self->server_cert);
|
|
SSL_CTX_free(self->ctx);
|
|
SSL_free(self->ssl);
|
|
Py_XDECREF(self->x_attr);
|
|
PyMem_DEL(self);
|
|
}
|
|
|
|
static PyObject *
|
|
BUILD_FUNC_DEF_2 (PySsl_getattr, PySslObject *, self, char *, name)
|
|
{
|
|
return (Py_FindMethod(PySslObj_methods,(PyObject *)self,name));
|
|
}
|
|
|
|
staticforward PyTypeObject SSL_Type = {
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
0, /*ob_size*/
|
|
"SSL", /*tp_name*/
|
|
sizeof(PySslObject), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
/* methods */
|
|
(destructor)PySsl_dealloc, /*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
(getattrfunc)PySsl_getattr, /*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*/
|
|
};
|
|
|
|
/*
|
|
* C function called for new object initialization
|
|
* Note: SSL protocol version 2, 3, or 2+3 set at compile time
|
|
*/
|
|
static PySslObject *
|
|
BUILD_FUNC_DEF_1 (newPySslObject, int, sock_fd)
|
|
{
|
|
PySslObject *self;
|
|
SSL_METHOD *meth;
|
|
int ret;
|
|
|
|
#if 0
|
|
meth=SSLv3_client_method();
|
|
meth=SSLv23_client_method();
|
|
#endif
|
|
|
|
meth=SSLv2_client_method();
|
|
|
|
if (!(self = PyObject_NEW(PySslObject,&SSL_Type))) /* create new object */
|
|
return (NULL);
|
|
(void)memset(self->server,0,sizeof(self->server));
|
|
(void)memset(self->issuer,0,sizeof(self->issuer));
|
|
|
|
self->x_attr = PyDict_New();
|
|
if (!(self->ctx = SSL_CTX_new(meth))) { /* set up context */
|
|
PyMem_DEL(self);
|
|
return (PySsl_errors());
|
|
}
|
|
#if 0 /* Note: set this for v23, Netscape server */
|
|
SSL_CTX_set_options(self->ctx,SSL_OP_ALL);
|
|
#endif
|
|
self->ssl = SSL_new(self->ctx); /* new ssl struct */
|
|
if (!(ret = SSL_set_fd(self->ssl,sock_fd))) { /* set the socket for SSL */
|
|
PyMem_DEL(self);
|
|
return (PySsl_app_errors(self->ssl,ret));
|
|
}
|
|
SSL_CTX_set_verify(self->ctx,SSL_VERIFY_NONE,NULL); /* set verify lvl */
|
|
SSL_set_connect_state(self->ssl);
|
|
|
|
if ((ret = SSL_connect(self->ssl)) < 0) { /* negotiate SSL connection */
|
|
PyMem_DEL(self);
|
|
return (PySsl_app_errors(self->ssl,ret));
|
|
}
|
|
self->ssl->debug = 1;
|
|
|
|
if ((self->server_cert = SSL_get_peer_certificate(self->ssl))) {
|
|
X509_NAME_oneline(X509_get_subject_name(self->server_cert),
|
|
self->server,sizeof(self->server));
|
|
X509_NAME_oneline(X509_get_issuer_name(self->server_cert),
|
|
self->issuer, sizeof(self->issuer));
|
|
}
|
|
self->x_attr = NULL;
|
|
self->sock_fd = sock_fd;
|
|
return (self);
|
|
}
|
|
|
|
/*
|
|
* Python function called for new object initialization
|
|
*/
|
|
static PyObject *
|
|
BUILD_FUNC_DEF_2 (PySsl_ssl_new, PyObject *, self, PyObject *, args)
|
|
{
|
|
int sock_fd;
|
|
if (!PyArg_ParseTuple(args, "i", &sock_fd))
|
|
return (NULL);
|
|
return ((PyObject *)newPySslObject(sock_fd));
|
|
}
|
|
|
|
/* List of functions exported by this module. */
|
|
|
|
static PyMethodDef PySsl_methods[] = {
|
|
{"ssl", (PyCFunction)PySsl_ssl_new, 1},
|
|
{NULL, NULL} /* sentinel */
|
|
|
|
};
|
|
|
|
/*
|
|
* Initialize this module, called when the first 'import ssl' is done
|
|
*/
|
|
void
|
|
initssl ()
|
|
{
|
|
PyObject *m, *d;
|
|
m = Py_InitModule("ssl", PySsl_methods);
|
|
d = PyModule_GetDict(m);
|
|
|
|
SSL_load_error_strings();
|
|
SSLeay_add_ssl_algorithms();
|
|
|
|
/* *** Python 1.5 ***
|
|
if (!(PySssl_Error = PyErr_NewException("ssl.error",NULL,NULL)))
|
|
return;
|
|
*/
|
|
|
|
if (!(PySslError = PyString_FromString("ssl.error")) ||
|
|
PyDict_SetItemString(d,"error",PySslError))
|
|
Py_FatalError("can't define ssl.error");
|
|
if (PyDict_SetItemString(d,"SSLType",(PyObject *)&SSL_Type))
|
|
return;
|
|
}
|
|
|