Skip to content

Commit cf75110

Browse files
committed
fix string decoding, implement recv_into(), add alert history
1 parent 8ada6e4 commit cf75110

2 files changed

Lines changed: 75 additions & 43 deletions

File tree

src/wolfssl/__init__.py

Lines changed: 58 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,7 @@ def get_subject_cn(self):
9494
if cnPtr == _ffi.NULL:
9595
return ''
9696

97-
cn = _ffi.string(cnPtr)
98-
99-
if _PY3:
100-
if isinstance(cn, bytes):
101-
cn = cn.decode("utf-8")
102-
else:
103-
if isinstance(cn, unicode):
104-
cn = cn.encode("utf-8")
97+
cn = _ffi.string(cnPtr).decode("ascii")
10598

10699
return cn
107100

@@ -110,7 +103,7 @@ def get_next_altname(self):
110103
if (sanPtr == _ffi.NULL):
111104
return None
112105

113-
san = _ffi.string(sanPtr)
106+
san = _ffi.string(sanPtr).decode("ascii")
114107

115108
return san
116109

@@ -284,6 +277,11 @@ def load_cert_chain(self, certfile, keyfile=None, password=None):
284277
private key in.
285278
286279
The password parameter is not supported yet.
280+
281+
wolfSSL does not support loading a certificate file that contains
282+
both the certificate AND private key. In this case, users should
283+
split them into two separate files and load using the certfile
284+
and keyfile parameters, respectively.
287285
"""
288286

289287
if password is not None:
@@ -397,35 +395,6 @@ def __init__(self, sock=None, keyfile=None, certfile=None,
397395
if server_hostname is not None:
398396
self._context.use_sni(server_hostname)
399397

400-
# preparing socket
401-
if sock is not None:
402-
# Can't use sock.type as other flags (such as SOCK_NONBLOCK) get
403-
# mixed in.
404-
if sock.getsockopt(SOL_SOCKET, SO_TYPE) != SOCK_STREAM:
405-
raise NotImplementedError("only stream sockets are supported")
406-
407-
if _PY3:
408-
socket.__init__(self._sock,
409-
family=sock.family,
410-
type=sock.type,
411-
proto=sock.proto,
412-
fileno=sock.fileno())
413-
else:
414-
socket.__init__(
415-
self._sock, _sock=sock._sock) # pylint: disable=protected-access
416-
417-
self._sock.settimeout(sock.gettimeout())
418-
419-
if _PY3:
420-
sock.detach()
421-
422-
elif fileno is not None:
423-
socket.__init__(self._sock, fileno=fileno)
424-
425-
else:
426-
socket.__init__(self._sock, family=family, type=sock_type,
427-
proto=proto)
428-
429398
# see if we are connected
430399
try:
431400
self._sock.getpeername()
@@ -584,8 +553,36 @@ def recv(self, length=1024, flags=0):
584553
return self.read(length=length)
585554

586555
def recv_into(self, buffer, nbytes=None, flags=0):
587-
raise NotImplementedError("recv_into not allowed on instances "
588-
"of %s" % self.__class__)
556+
"""
557+
Read nbytes bytes and place into buffer. If nbytes is 0, read up
558+
to full size of buffer.
559+
"""
560+
self._check_closed("read")
561+
self._check_connected()
562+
563+
if buffer is None:
564+
raise ValueError("buffer cannot be None")
565+
566+
if nbytes is None:
567+
nbytes = len(buffer)
568+
else:
569+
nbytes = min(len(buffer), nbytes)
570+
571+
if nbytes == 0:
572+
return 0
573+
574+
data = _ffi.from_buffer(buffer)
575+
length = _lib.wolfSSL_read(self.native_object, data, nbytes)
576+
577+
if length < 0:
578+
err = _lib.wolfSSL_get_error(self.native_object, 0)
579+
if err == _SSL_ERROR_WANT_READ:
580+
raise SSLWantReadError()
581+
else:
582+
raise SSLError("wolfSSL_read error (%d)" % err)
583+
584+
return length
585+
589586

590587
def recvfrom(self, length=1024, flags=0):
591588
# Ensures not to receive encrypted data trying to use this method
@@ -651,16 +648,34 @@ def do_handshake(self, block=False): # pylint: disable=unused-argument
651648
raise SSLWantWriteError()
652649
else:
653650
eBuf = _ffi.new("char[80]")
654-
eStr = _ffi.string(_lib.wolfSSL_ERR_error_string(err, eBuf))
651+
eStr = _ffi.string(_lib.wolfSSL_ERR_error_string(err,
652+
eBuf)).decode("ascii")
655653

656654
if 'ASN no signer error to confirm' in eStr or err is -188:
657655
# Some Python ssl consumers explicitly check error message
658656
# for 'certificate verify failed'
659657
raise SSLError("do_handshake failed with error %d, "
660658
"certificate verify failed" % err)
661659

662-
raise SSLError("do_handshake failed with error %d: %s" %
663-
(err, eStr))
660+
# get alert code and string to put in exception msg
661+
alertHistoryPtr = _ffi.new("WOLFSSL_ALERT_HISTORY*")
662+
alertRet = _lib.wolfSSL_get_alert_history(self.native_object,
663+
alertHistoryPtr)
664+
if alertRet == _SSL_SUCCESS:
665+
alertHistory = alertHistoryPtr[0]
666+
code = alertHistory.last_rx.code
667+
alertDesc = _lib.wolfSSL_alert_type_string_long(code)
668+
if alertDesc != _ffi.NULL:
669+
alertStr = _ffi.string(alertDesc).decode("ascii")
670+
else:
671+
alertStr = ''
672+
673+
raise SSLError("do_handshake failed with error %d: %s. "
674+
"alert (%d): %s" %
675+
(err, eStr, code, alertStr))
676+
else:
677+
raise SSLError("do_handshake failed with error %d: %s" %
678+
(err, eStr))
664679

665680
def _real_connect(self, addr, connect_ex):
666681
if self.server_side:

src/wolfssl/_build_ffi.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,20 @@
4141

4242
ffi.cdef(
4343
"""
44+
45+
/**
46+
* Structs
47+
*/
48+
typedef struct WOLFSSL_ALERT {
49+
int code;
50+
int level;
51+
} WOLFSSL_ALERT;
52+
53+
typedef struct WOLFSSL_ALERT_HISTORY {
54+
WOLFSSL_ALERT last_rx;
55+
WOLFSSL_ALERT last_tx;
56+
} WOLFSSL_ALERT_HISTORY;
57+
4458
/**
4559
* Types
4660
*/
@@ -104,6 +118,9 @@
104118
void* wolfSSL_get_peer_certificate(void*);
105119
int wolfSSL_UseSNI(void*, unsigned char, const void*, unsigned short);
106120
int wolfSSL_check_domain_name(void*, const char*);
121+
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);
107124
108125
/**
109126
* WOLFSSL_X509 functions

0 commit comments

Comments
 (0)