[pypy-commit] pypy py3.5-ssl: passing all tests in test_ssl.py

plan_rich pypy.commits at gmail.com
Thu Nov 17 07:34:54 EST 2016


Author: Richard Plangger <planrichi at gmail.com>
Branch: py3.5-ssl
Changeset: r88428:d9f42756c388
Date: 2016-11-17 13:34 +0100
http://bitbucket.org/pypy/pypy/changeset/d9f42756c388/

Log:	passing all tests in test_ssl.py

diff --git a/lib_pypy/openssl/_cffi_src/openssl/bio.py b/lib_pypy/openssl/_cffi_src/openssl/bio.py
--- a/lib_pypy/openssl/_cffi_src/openssl/bio.py
+++ b/lib_pypy/openssl/_cffi_src/openssl/bio.py
@@ -82,6 +82,8 @@
 int BIO_write(BIO *, const void *, int);
 int BIO_puts(BIO *, const char *);
 int BIO_method_type(const BIO *);
+
+int * Cryptography_bio_references(const BIO *);
 """
 
 MACROS = """
@@ -132,7 +134,12 @@
 long BIO_set_nbio(BIO *, long);
 void BIO_set_retry_read(BIO *);
 void BIO_clear_retry_flags(BIO *);
+
+#define CRYPTO_LOCK_BIO ...
 """
 
 CUSTOMIZATIONS = """
+int * Cryptography_bio_references(const BIO * b) {
+    return &b->references;
+}
 """
diff --git a/lib_pypy/openssl/_cffi_src/openssl/crypto.py b/lib_pypy/openssl/_cffi_src/openssl/crypto.py
--- a/lib_pypy/openssl/_cffi_src/openssl/crypto.py
+++ b/lib_pypy/openssl/_cffi_src/openssl/crypto.py
@@ -57,6 +57,8 @@
 
 /* This was removed in 1.1.0 */
 void CRYPTO_lock(int, int, const char *, int);
+
+void CRYPTO_add(void*,int,int);
 """
 
 CUSTOMIZATIONS = """
diff --git a/lib_pypy/openssl/_stdssl/__init__.py b/lib_pypy/openssl/_stdssl/__init__.py
--- a/lib_pypy/openssl/_stdssl/__init__.py
+++ b/lib_pypy/openssl/_stdssl/__init__.py
@@ -98,6 +98,10 @@
 # TODO threads?
 lib.OpenSSL_add_all_algorithms()
 
+def check_signals():
+    # TODO PyErr_CheckSignal equivalent for pypy?
+    pass
+
 def _socket_timeout(s):
     if s is None:
         return 0.0
@@ -218,13 +222,14 @@
         if sock:
             lib.SSL_set_fd(ssl, sock.fileno())
         else:
-            raise NotImplementedError("implement _SSLSocket inbio, outbio params")
-            # /* BIOs are reference counted and SSL_set_bio borrows our reference.
-            #  * To prevent a double free in memory_bio_dealloc() we need to take an
-            #  * extra reference here. */
-            # CRYPTO_add(&inbio->bio->references, 1, CRYPTO_LOCK_BIO);
-            # CRYPTO_add(&outbio->bio->references, 1, CRYPTO_LOCK_BIO);
-            # SSL_set_bio(self->ssl, inbio->bio, outbio->bio);
+            # BIOs are reference counted and SSL_set_bio borrows our reference.
+            # To prevent a double free in memory_bio_dealloc() we need to take an
+            # extra reference here.
+            irefaddr = lib.Cryptography_bio_references(inbio.bio);
+            orefaddr = lib.Cryptography_bio_references(outbio.bio);
+            lib.CRYPTO_add(irefaddr, 1, lib.CRYPTO_LOCK_BIO)
+            lib.CRYPTO_add(orefaddr, 1, lib.CRYPTO_LOCK_BIO)
+            lib.SSL_set_bio(self.ssl, inbio.bio, outbio.bio)
 
         mode = lib.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
         if lib.SSL_MODE_AUTO_RETRY:
@@ -287,12 +292,17 @@
 
     @context.setter
     def context(self, value):
-        self.ctx = value
+        if isinstance(value, _SSLContext):
+            if not HAS_SNI:
+                raise NotImplementedError("setting a socket's "
+                        "context is not supported by your OpenSSL library")
+            self.ctx = value
+            lib.SSL_set_SSL_CTX(self.ssl, self.ctx.ctx);
+        else:
+            raise TypeError("The value must be a SSLContext")
 
     def do_handshake(self):
-        sock = self.get_socket_or_None()
-        if sock is None:
-            raise ssl_error("Underlying socket connection gone", SSL_ERROR_NO_SOCKET)
+        sock = self.get_socket_or_connection_gone()
         ssl = self.ssl
         timeout = _socket_timeout(sock)
         if sock:
@@ -313,8 +323,7 @@
             err = lib.SSL_get_error(ssl, ret)
             # end allow threads
 
-            #if (PyErr_CheckSignals())
-            #    goto error;
+            check_signals()
 
             if has_timeout:
                 # REIVIEW monotonic clock?
@@ -399,8 +408,7 @@
             err = lib.SSL_get_error(self.ssl, length)
             #PySSL_END_ALLOW_THREADS
 
-            # TODO if (PyErr_CheckSignals())
-            # TODO     goto error;
+            check_signals()
 
             if has_timeout:
                 # TODO monotonic clock
@@ -428,14 +436,12 @@
             raise pyssl_error(self, length)
 
     def read(self, length, buffer_into=None):
-        sock = self.get_socket_or_None()
         ssl = self.ssl
 
         if length < 0 and buffer_into is None:
             raise ValueError("size should not be negative")
 
-        if sock is None:
-            raise ssl_error("Underlying socket connection gone", SSL_ERROR_NO_SOCKET)
+        sock = self.get_socket_or_connection_gone()
 
         if not buffer_into:
             dest = _buffer_new(length)
@@ -467,9 +473,7 @@
             err = lib.SSL_get_error(self.ssl, count);
             #PySSL_END_ALLOW_THREADS
 
-            # TODO
-            #if (PyErr_CheckSignals())
-            #    goto error;
+            check_signals()
 
             if has_timeout:
                 timeout = deadline - time.time() # TODO ? _PyTime_GetMonotonicClock();
@@ -512,9 +516,10 @@
 
     def shared_ciphers(self):
         sess = lib.SSL_get_session(self.ssl)
-
+        if sess == ffi.NULL:
+            return None
         ciphers = lib.Cryptography_get_ssl_session_ciphers(sess)
-        if sess is None or ciphers == ffi.NULL:
+        if ciphers == ffi.NULL:
             return None
         res = []
         count = lib.sk_SSL_CIPHER_num(ciphers)
@@ -559,18 +564,28 @@
         return self.socket()
 
     def get_socket_or_connection_gone(self):
+        """ There are three states:
+            1) self.socket is None (In C that would mean: self->Socket == NULL)
+            2) self.socket() is None (-> The socket is gone)
+            3) self.socket() is not None
+            This method returns True if there is not weakref object allocated
+        """
         if self.socket is None:
+            return None
+        sock = self.socket()
+        if not sock:
             raise ssl_error("Underlying socket connection gone", SSL_ERROR_NO_SOCKET)
-        return self.socket()
+        return sock
 
     def shutdown(self):
         sock = self.get_socket_or_None()
         nonblocking = False
         ssl = self.ssl
 
-        if sock is not None:
+        if self.socket is not None:
             # Guard against closed socket
-            if sock.fileno() < 0:
+            sock = self.socket()
+            if sock is None or sock.fileno() < 0:
                 raise ssl_error("Underlying socket connection gone", SSL_ERROR_NO_SOCKET)
 
             timeout = _socket_timeout(sock)
@@ -1129,7 +1144,6 @@
                     "is not in the current OpenSSL library.")
         if callback is None:
             lib.SSL_CTX_set_tlsext_servername_callback(self.ctx, ffi.NULL)
-            self.set_hostname = None
             self._set_hostname_handle = None
             return
         if not callable(callback):
@@ -1161,6 +1175,16 @@
         else:
             raise NotImplementedError("The NPN extension requires OpenSSL 1.0.1 or later.")
 
+    def _wrap_bio(self, incoming, outgoing, server_side, server_hostname):
+        # server_hostname is either None (or absent), or to be encoded
+        # using the idna encoding.
+        hostname = None
+        if server_hostname is not None:
+            hostname = server_hostname.encode("idna")
+
+        sock = _SSLSocket._new__ssl_socket(self, None, server_side, hostname, incoming, outgoing)
+        return sock
+
 
 
 if HAS_SNI and not lib.Cryptography_NO_TLSEXT:
@@ -1360,15 +1384,16 @@
 def _RAND_bytes(count, pseudo):
     if count < 0:
         raise ValueError("num must be positive")
-    buf = ffi.new("unsigned char[]", b"\x00"*count)
+    buf = ffi.new("unsigned char[%d]" % count)
     if pseudo:
         ok = lib.RAND_pseudo_bytes(buf, count)
         if ok == 1 or ok == 0:
-            return (ffi.string(buf), ok == 1)
+            _bytes = _bytes_with_len(buf, count)
+            return (_bytes, ok == 1)
     else:
         ok = lib.RAND_bytes(buf, count)
         if ok == 1:
-            return ffi.string(buf)
+            return _bytes_with_len(buf, count)
     raise ssl_error(None, errcode=lib.ERR_get_error())
 
 def RAND_pseudo_bytes(count):
diff --git a/lib_pypy/openssl/_stdssl/error.py b/lib_pypy/openssl/_stdssl/error.py
--- a/lib_pypy/openssl/_stdssl/error.py
+++ b/lib_pypy/openssl/_stdssl/error.py
@@ -101,14 +101,18 @@
             errval = SSL_ERROR_WANT_CONNECT
         elif err == SSL_ERROR_SYSCALL:
             if e == 0:
-                if ret == 0 or obj.get_socket_or_None() is None:
-                    errtype = EOFError
+                if ret == 0 or obj.socket is not None:
+                    errtype = SSLEOFError
                     errstr = "EOF occurred in violation of protocol"
                     errval = SSL_ERROR_EOF
-                elif ret == -1:
+                elif ret == -1 and obj.socket is not None:
                     # the underlying BIO reported an I/0 error
-                    errno = ffi.errno
-                    return IOError(errno)
+                    lib.ERR_clear_error()
+                    s = obj.get_socket_or_None()
+                    s.errorhandler()
+                    assert 0, "must not get here"
+                    #errno = ffi.errno
+                    #return IOError(errno)
                 else:
                     errtype = SSLSyscallError
                     errstr = "Some I/O error occurred"


More information about the pypy-commit mailing list