Skip to content

Commit c5c9b53

Browse files
authored
Merge pull request #11 from cconlon/05.23.2019
Python3 fixes, feature detection, ssl module compatibility fixes
2 parents 444d871 + 7dcfb22 commit c5c9b53

5 files changed

Lines changed: 118 additions & 23 deletions

File tree

setup.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
import os
2525
import sys
26-
import pip
2726
from setuptools import setup
2827
from setuptools.command.build_ext import build_ext
2928

@@ -34,6 +33,7 @@
3433

3534
import wolfssl
3635
from wolfssl._build_wolfssl import build_wolfssl
36+
from wolfssl._build_wolfssl import wolfssl_inc_path, wolfssl_lib_path
3737

3838

3939
# long_description
@@ -44,6 +44,26 @@
4444
long_description = long_description.replace(".. include:: LICENSING.rst\n",
4545
licensing_file.read())
4646

47+
def verify_wolfssl_config():
48+
# verify wolfSSL library has been configured correctly, so that cffi
49+
# binding work correctly.
50+
51+
# open <wolfssl/options.h> header to parse for #define's
52+
# This will throw a FileNotFoundError if not able to find options.h
53+
optionsHeaderPath = wolfssl_inc_path() + "/wolfssl/options.h"
54+
optionsHeader = open(optionsHeaderPath, 'r')
55+
optionsHeaderStr = optionsHeader.read()
56+
optionsHeader.close()
57+
58+
# require HAVE_SNI (--enable-sni) in native lib
59+
if '#define HAVE_SNI' not in optionsHeaderStr:
60+
raise RuntimeError("wolfSSL needs to be compiled with --enable-sni")
61+
62+
# require OPENSSL_EXTRA (--enable-opensslextra) in native lib
63+
if '#define OPENSSL_EXTRA' not in optionsHeaderStr:
64+
raise RuntimeError("wolfSSL needs to be compiled with "
65+
"--enable-opensslextra")
66+
4767
class cffiBuilder(build_ext, object):
4868

4969
def build_extension(self, ext):
@@ -55,6 +75,8 @@ def build_extension(self, ext):
5575
if os.environ.get("USE_LOCAL_WOLFSSL") is None:
5676
build_wolfssl(wolfssl.__wolfssl_version__)
5777

78+
verify_wolfssl_config()
79+
5880
super(cffiBuilder, self).build_extension(ext)
5981

6082
setup(
@@ -71,6 +93,7 @@ def build_extension(self, ext):
7193
package_dir={"":package_dir},
7294

7395
zip_safe=False,
96+
cffi_modules=["./src/wolfssl/_build_ffi.py:ffi"],
7497

7598
keywords="wolfssl, wolfcrypt, security, cryptography",
7699
classifiers=[
@@ -87,7 +110,6 @@ def build_extension(self, ext):
87110
],
88111

89112
setup_requires=["cffi"],
90-
cffi_modules=["./src/wolfssl/_build_ffi.py:ffi"],
91113
install_requires=["cffi"],
92114
test_suite="tests",
93115
tests_require=["tox", "pytest"],

src/wolfssl/__init__.py

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,9 @@ def set_ciphers(self, ciphers):
249249
be selected (because compile-time options or other configuration
250250
forbids use of all the specified ciphers), an SSLError will be raised.
251251
"""
252+
cipherBytes = t2b(ciphers)
252253
ret = _lib.wolfSSL_CTX_set_cipher_list(self.native_object,
253-
t2b(ciphers))
254+
_ffi.new("char[]", cipherBytes))
254255

255256
if ret != _SSL_SUCCESS:
256257
raise SSLError("Unable to set cipher list")
@@ -259,8 +260,11 @@ def use_sni(self, server_hostname):
259260
"""
260261
Sets the SNI hostname, wraps native wolfSSL_CTX_UseSNI()
261262
"""
263+
264+
sni = t2b(server_hostname)
265+
262266
ret = _lib.wolfSSL_CTX_UseSNI(self.native_object, 0,
263-
server_hostname, len(server_hostname))
267+
sni, len(sni))
264268

265269
if ret != _SSL_SUCCESS:
266270
raise SSLError("Unable to set wolfSSL CTX SNI")
@@ -355,7 +359,7 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
355359
# set options
356360
self.do_handshake_on_connect = do_handshake_on_connect
357361
self.suppress_ragged_eofs = suppress_ragged_eofs
358-
self.server_side = server_side
362+
self._server_side = server_side
359363

360364
# save socket
361365
self._sock = sock
@@ -421,8 +425,10 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
421425
# match domain name / host name if set in context
422426
if server_hostname is not None:
423427
if self._context.check_hostname:
428+
429+
sni = _ffi.new("char[]", server_hostname.encode("utf-8"))
424430
_lib.wolfSSL_check_domain_name(self.native_object,
425-
server_hostname)
431+
sni)
426432

427433
if connected:
428434
try:
@@ -441,13 +447,22 @@ def _release_native_object(self):
441447
_lib.wolfSSL_free(self.native_object)
442448
self.native_object = _ffi.NULL
443449

450+
def pending(self):
451+
return _lib.wolfSSL_pending(self.native_object)
452+
444453
@property
445454
def context(self):
446455
"""
447456
Returns the context used by this object.
448457
"""
449458
return self._context
450459

460+
def server_side(self):
461+
"""
462+
Returns True for server-side socket, otherwise False.
463+
"""
464+
return self._server_side;
465+
451466
def dup(self):
452467
raise NotImplementedError("Can't dup() %s instances" %
453468
self.__class__.__name__)
@@ -468,8 +483,11 @@ def use_sni(self, server_hostname):
468483
"""
469484
Sets the SNI hostname, wraps native wolfSSL_UseSNI()
470485
"""
486+
487+
sni = t2b(server_hostname)
488+
471489
ret = _lib.wolfSSL_UseSNI(self.native_object, 0,
472-
server_hostname, len(server_hostname))
490+
sni, len(sni))
473491

474492
if ret != _SSL_SUCCESS:
475493
raise SSLError("Unable to set wolfSSL SNI")
@@ -502,9 +520,17 @@ def sendall(self, data, flags=0):
502520
sent = 0
503521

504522
while sent < length:
505-
sent += self.write(data[sent:])
523+
ret = self.write(data[sent:])
524+
if (ret < 0):
525+
err = _lib.wolfSSL_get_error(self.native_object, 0)
526+
if err == _SSL_ERROR_WANT_WRITE:
527+
raise SSLWantWriteError()
528+
else:
529+
raise SSLError("wolfSSL_write error (%d)" % err)
530+
531+
sent += ret
506532

507-
return sent
533+
return None
508534

509535
def sendto(self, data, flags_or_addr, addr=None):
510536
# Ensures not to send unencrypted data trying to use this method
@@ -606,7 +632,7 @@ def shutdown(self, how):
606632
if self.native_object != _ffi.NULL:
607633
_lib.wolfSSL_shutdown(self.native_object)
608634
self._release_native_object()
609-
socket.shutdown(self._sock, how)
635+
self._sock.shutdown(how)
610636

611637
def unwrap(self):
612638
"""
@@ -620,22 +646,20 @@ def unwrap(self):
620646
sock_type=self._sock.type,
621647
proto=self._sock.proto,
622648
fileno=self._sock.fileno())
649+
623650
sock.settimeout(self._sock.gettimeout())
624651
self._sock.detach()
625652

626653
return sock
627654

628-
def close(self):
629-
self._sock.close()
630-
631655
def do_handshake(self, block=False): # pylint: disable=unused-argument
632656
"""
633657
Perform a TLS/SSL handshake.
634658
"""
635659
self._check_closed("do_handshake")
636660
self._check_connected()
637661

638-
if self.server_side:
662+
if self._server_side:
639663
ret = _lib.wolfSSL_accept(self.native_object)
640664
else:
641665
ret = _lib.wolfSSL_connect(self.native_object)
@@ -678,7 +702,7 @@ def do_handshake(self, block=False): # pylint: disable=unused-argument
678702
(err, eStr))
679703

680704
def _real_connect(self, addr, connect_ex):
681-
if self.server_side:
705+
if self._server_side:
682706
raise ValueError("can't connect in server-side mode")
683707

684708
# Here we assume that the socket is client-side, and not
@@ -687,10 +711,10 @@ def _real_connect(self, addr, connect_ex):
687711
raise ValueError("attempt to connect already-connected SSLSocket!")
688712

689713
if connect_ex:
690-
err = socket.connect_ex(self._sock, addr)
714+
err = self._sock.connect_ex(addr)
691715
else:
692716
err = 0
693-
socket.connect(self._sock, addr)
717+
self._sock.connect(addr)
694718

695719
if err == 0:
696720
self._connected = True
@@ -719,10 +743,10 @@ def accept(self):
719743
containing that new connection wrapped with a server-side secure
720744
channel, and the address of the remote client.
721745
"""
722-
if not self.server_side:
746+
if not self._server_side:
723747
raise ValueError("can't accept in client-side mode")
724748

725-
newsock, addr = socket.accept(self._sock)
749+
newsock, addr = self._sock.accept()
726750
newsock = self.context.wrap_socket(
727751
newsock,
728752
do_handshake_on_connect=self.do_handshake_on_connect,
@@ -758,6 +782,43 @@ def getpeercert(self, binary_form=False):
758782
return {'subject': ((('commonName', x509.get_subject_cn()),),),
759783
'subjectAltName': x509.get_altnames() }
760784

785+
# The following functions expose functionality of the underlying
786+
# Socket object. These are also expsed through Python's ssl module
787+
# API and are provided here for compatibility.
788+
def close(self):
789+
self._sock.close()
790+
791+
def fileno(self):
792+
"""
793+
Return file descriptor of underlying socket.
794+
"""
795+
return self._sock.fileno()
796+
797+
def gettimeout(self):
798+
"""
799+
Return the socket timeout of the underlying wrapped socket
800+
"""
801+
return self._sock.gettimeout()
802+
803+
def settimeout(self, timeout):
804+
"""
805+
Set the timeout on the underlying wrapped socket
806+
"""
807+
self._sock.settimeout(timeout)
808+
809+
def getpeername(self):
810+
"""
811+
Return the remote address that the underlying socket is connected to
812+
"""
813+
return self._sock.getpeername()
814+
815+
def getsockname(self):
816+
"""
817+
Return the underlying socket's address
818+
"""
819+
return self._sock.getsockname()
820+
821+
761822

762823
def wrap_socket(sock, keyfile=None, certfile=None, server_side=False,
763824
cert_reqs=CERT_NONE, ssl_version=PROTOCOL_TLS, ca_certs=None,

src/wolfssl/_build_ffi.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,14 @@
114114
int wolfSSL_accept(void*);
115115
int wolfSSL_write(void*, const void*, int);
116116
int wolfSSL_read(void*, void*, int);
117+
int wolfSSL_pending(void*);
117118
int wolfSSL_shutdown(void*);
118119
void* wolfSSL_get_peer_certificate(void*);
119120
int wolfSSL_UseSNI(void*, unsigned char, const void*, unsigned short);
120121
int wolfSSL_check_domain_name(void*, const char*);
121122
int wolfSSL_get_alert_history(void*, WOLFSSL_ALERT_HISTORY*);
122-
char* wolfSSL_alert_type_string_long(int);
123-
char* wolfSSL_alert_desc_string_long(int);
123+
const char* wolfSSL_alert_type_string_long(int);
124+
const char* wolfSSL_alert_desc_string_long(int);
124125
125126
/**
126127
* WOLFSSL_X509 functions
@@ -132,4 +133,4 @@
132133
)
133134

134135
if __name__ == "__main__":
135-
ffi.compile(verbose=1)
136+
ffi.compile(verbose=True)

src/wolfssl/exceptions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
# pylint: disable=missing-docstring
2424

2525
from socket import error as socket_error
26+
from errno import EWOULDBLOCK
2627

2728

2829
class SSLError(socket_error):
@@ -52,6 +53,11 @@ class SSLWantReadError(SSLError):
5253
read or write data, but more data needs to be received on the underlying
5354
TCP transport before the request can be fulfilled.
5455
"""
56+
def __init__(self):
57+
self.code = EWOULDBLOCK
58+
self.msg = "Socket would read block"
59+
self.args = (self.code, self.msg)
60+
5561
pass
5662

5763

@@ -61,6 +67,11 @@ class SSLWantWriteError(SSLError):
6167
read or write data, but more data needs to be sent on the underlying TCP
6268
transport before the request can be fulfilled.
6369
"""
70+
def __init__(self):
71+
self.code = EWOULDBLOCK
72+
self.msg = "Socket would write block"
73+
self.args = (self.code, self.msg)
74+
6475
pass
6576

6677

src/wolfssl/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
def t2b(string):
3434
"""
35-
Converts text to bynary.
35+
Converts text to binary.
3636
"""
3737
if isinstance(string, _BINARY_TYPE):
3838
return string

0 commit comments

Comments
 (0)