summaryrefslogtreecommitdiff
path: root/src/M2Crypto/SSL/Connection.py
blob: c5693c900ffac66112afb57b4d08ef3e528ce19f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
from __future__ import absolute_import

"""SSL Connection aka socket

Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved.

Portions created by Open Source Applications Foundation (OSAF) are
Copyright (C) 2004-2007 OSAF. All Rights Reserved.

Copyright 2008 Heikki Toivonen. All rights reserved.
"""

import logging
import socket
import io

from M2Crypto import BIO, Err, X509, m2, six, util  # noqa
from M2Crypto.SSL import Checker, Context, timeout  # noqa
from M2Crypto.SSL import SSLError
from M2Crypto.SSL.Cipher import Cipher, Cipher_Stack
from M2Crypto.SSL.Session import Session
from typing import Any, AnyStr, Callable, Optional, Tuple, Union  # noqa

__all__ = ['Connection',
           'timeout',  # XXX Not really, but for documentation purposes
           ]

log = logging.getLogger(__name__)


def _serverPostConnectionCheck(*args, **kw):
    # type: (*Any, **Any) -> int
    return 1


class Connection(object):
    """An SSL connection."""

    serverPostConnectionCheck = _serverPostConnectionCheck

    m2_bio_free = m2.bio_free
    m2_ssl_free = m2.ssl_free
    m2_bio_noclose = m2.bio_noclose

    def __init__(self, ctx, sock=None, family=socket.AF_INET):
        # type: (Context, socket.socket, int) -> None
        """

        :param ctx: SSL.Context
        :param sock: socket to be used
        :param family: socket family
        """
        # The Checker needs to be an instance attribute
        # and not a class attribute for thread safety reason
        self.clientPostConnectionCheck = Checker.Checker()

        self._bio_freed = False
        self.ctx = ctx
        self.ssl = m2.ssl_new(self.ctx.ctx)  # type: bytes
        if sock is not None:
            self.socket = sock
        else:
            self.socket = socket.socket(family, socket.SOCK_STREAM)
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._fileno = self.socket.fileno()

        self._timeout = self.socket.gettimeout()
        if self._timeout is None:
            self._timeout = -1.0

        self.ssl_close_flag = m2.bio_noclose

        if self.ctx.post_connection_check is not None:
            self.set_post_connection_check_callback(
                self.ctx.post_connection_check)

        self.host = None

    def _free_bio(self):
        """
           Free the sslbio and sockbio, and close the socket.
        """
        # Do not do it twice
        if not self._bio_freed:
            if getattr(self, 'sslbio', None):
                self.m2_bio_free(self.sslbio)
            if getattr(self, 'sockbio', None):
                self.m2_bio_free(self.sockbio)
            if self.ssl_close_flag == self.m2_bio_noclose and \
                    getattr(self, 'ssl', None):
                self.m2_ssl_free(self.ssl)
            self.socket.close()
            self._bio_freed = True


    def __del__(self):
        # type: () -> None
        # Notice that M2Crypto doesn't automatically shuts down the
        # connection here. You have to call self.close() in your
        # program, M2Crypto won't do it automatically for you.
        self._free_bio()

    def close(self, freeBio=False):
        """
           if freeBio is true, call _free_bio
        """
        # type: () -> None
        m2.ssl_shutdown(self.ssl)
        if freeBio:
            self._free_bio()

    def clear(self):
        # type: () -> int
        """
        If there were errors in this connection, call clear() rather
        than close() to end it, so that bad sessions will be cleared
        from cache.
        """
        return m2.ssl_clear(self.ssl)

    def set_shutdown(self, mode):
        # type: (int) -> None
        """Sets the shutdown state of the Connection to mode.

        The shutdown state of an ssl connection is a bitmask of (use
        m2.SSL_* constants):

        0   No shutdown setting, yet.

        SSL_SENT_SHUTDOWN
            A "close notify" shutdown alert was sent to the peer, the
            connection is being considered closed and the session is
            closed and correct.

        SSL_RECEIVED_SHUTDOWN
            A shutdown alert was received form the peer, either a normal
            "close notify" or a fatal error.

        SSL_SENT_SHUTDOWN and SSL_RECEIVED_SHUTDOWN can be set at the
        same time.

        :param mode: set the mode bitmask.
        """
        m2.ssl_set_shutdown1(self.ssl, mode)

    def get_shutdown(self):
        # type: () -> None
        """Get the current shutdown mode of the Connection."""
        return m2.ssl_get_shutdown(self.ssl)

    def bind(self, addr):
        # type: (util.AddrType) -> None
        self.socket.bind(addr)

    def listen(self, qlen=5):
        # type: (int) -> None
        self.socket.listen(qlen)

    def ssl_get_error(self, ret):
        # type: (int) -> int
        return m2.ssl_get_error(self.ssl, ret)

    def set_bio(self, readbio, writebio):
        # type: (BIO.BIO, BIO.BIO) -> None
        """Explicitly set read and write bios

        Connects the BIOs for the read and write operations of the
        TLS/SSL (encrypted) side of ssl.

        The SSL engine inherits the behaviour of both BIO objects,
        respectively. If a BIO is non-blocking, the Connection will also
        have non-blocking behaviour.

        If there was already a BIO connected to Connection, BIO_free()
        will be called (for both the reading and writing side, if
        different).

        :param readbio: BIO for reading
        :param writebio: BIO for writing.
        """
        m2.ssl_set_bio(self.ssl, readbio._ptr(), writebio._ptr())

    def set_client_CA_list_from_file(self, cafile):
        # type: (AnyStr) -> None
        """Set the acceptable client CA list.

        If the client returns a certificate, it must have been issued by
        one of the CAs listed in cafile.

        Makes sense only for servers.

        :param cafile: Filename from which to load the CA list.

        :return: 0 A failure while manipulating the STACK_OF(X509_NAME)
                   object occurred or the X509_NAME could not be
                   extracted from cacert. Check the error stack to find
                   out the reason.

                 1 The operation succeeded.
        """
        m2.ssl_set_client_CA_list_from_file(self.ssl, cafile)

    def set_client_CA_list_from_context(self):
        # type: () -> None
        """
        Set the acceptable client CA list. If the client
        returns a certificate, it must have been issued by
        one of the CAs listed in context.

        Makes sense only for servers.
        """
        m2.ssl_set_client_CA_list_from_context(self.ssl, self.ctx.ctx)

    def setup_addr(self, addr):
        # type: (util.AddrType) -> None
        self.addr = addr

    def set_ssl_close_flag(self, flag):
        # type: (int) -> None
        """
        By default, SSL struct will be freed in __del__. Call with
        m2.bio_close to override this default.

        :param flag: either m2.bio_close or m2.bio_noclose
        """
        if flag not in (m2.bio_close, m2.bio_noclose):
            raise ValueError("flag must be m2.bio_close or m2.bio_noclose")
        self.ssl_close_flag = flag

    def setup_ssl(self):
        # type: () -> None
        # Make a BIO_s_socket.
        self.sockbio = m2.bio_new_socket(self.socket.fileno(), 0)
        # Link SSL struct with the BIO_socket.
        m2.ssl_set_bio(self.ssl, self.sockbio, self.sockbio)
        # Make a BIO_f_ssl.
        self.sslbio = m2.bio_new(m2.bio_f_ssl())
        # Link BIO_f_ssl with the SSL struct.
        m2.bio_set_ssl(self.sslbio, self.ssl, m2.bio_noclose)

    def _setup_ssl(self, addr):
        # type: (util.AddrType) -> None
        """Deprecated"""
        self.setup_addr(addr)
        self.setup_ssl()

    def set_accept_state(self):
        # type: () -> None
        """Sets Connection to work in the server mode."""
        m2.ssl_set_accept_state(self.ssl)

    def accept_ssl(self):
        # type: () -> Optional[int]
        """Waits for a TLS/SSL client to initiate the TLS/SSL handshake.

        The communication channel must already have been set and
        assigned to the ssl by setting an underlying BIO.

        :return: 0 The TLS/SSL handshake was not successful but was shut
                   down controlled and by the specifications of the
                   TLS/SSL protocol. Call get_error() with the return
                   value ret to find out the reason.

                 1 The TLS/SSL handshake was successfully completed,
                   a TLS/SSL connection has been established.

                 <0 The TLS/SSL handshake was not successful because
                    a fatal error occurred either at the protocol level
                    or a connection failure occurred. The shutdown was
                    not clean. It can also occur of action is need to
                    continue the operation for non-blocking BIOs. Call
                    get_error() with the return value ret to find
                    out the reason.
        """
        return m2.ssl_accept(self.ssl, self._timeout)

    def accept(self):
        # type: () -> Tuple[Connection, util.AddrType]
        """Accept an SSL connection.

        The return value is a pair (ssl, addr) where ssl is a new SSL
        connection object and addr is the address bound to the other end
        of the SSL connection.

        :return: tuple of Connection and addr. Address can take very
                 various forms (see socket documentation), for IPv4 it
                 is tuple(str, int), for IPv6 a tuple of four (host,
                 port, flowinfo, scopeid), where the last two are
                 optional ints.
        """
        sock, addr = self.socket.accept()
        ssl = Connection(self.ctx, sock)
        ssl.addr = addr
        ssl.setup_ssl()
        ssl.set_accept_state()
        ssl.accept_ssl()
        check = getattr(self, 'postConnectionCheck',
                        self.serverPostConnectionCheck)
        if check is not None:
            if not check(ssl.get_peer_cert(), ssl.addr[0]):
                raise Checker.SSLVerificationError(
                    'post connection check failed')
        return ssl, addr

    def set_connect_state(self):
        # type: () -> None
        """Sets Connection to work in the client mode."""
        m2.ssl_set_connect_state(self.ssl)

    def connect_ssl(self):
        # type: () -> Optional[int]
        return m2.ssl_connect(self.ssl, self._timeout)

    def connect(self, addr):
        # type: (util.AddrType) -> int
        """Overloading socket.connect()

        :param addr: addresses have various depending on their type

        :return:status of ssl_connect()
        """
        self.socket.connect(addr)
        self.addr = addr
        self.setup_ssl()
        self.set_connect_state()
        ret = self.connect_ssl()
        check = getattr(self, 'postConnectionCheck',
                        self.clientPostConnectionCheck)
        if check is not None:
            if not check(self.get_peer_cert(),
                         self.host if self.host else self.addr[0]):
                raise Checker.SSLVerificationError(
                    'post connection check failed')
        return ret

    def shutdown(self, how):
        # type: (int) -> None
        m2.ssl_set_shutdown(self.ssl, how)

    def renegotiate(self):
        # type: () -> int
        """Renegotiate this connection's SSL parameters."""
        return m2.ssl_renegotiate(self.ssl)

    def pending(self):
        # type: () -> int
        """Return the numbers of octets that can be read from the connection."""
        return m2.ssl_pending(self.ssl)

    def _write_bio(self, data):
        # type: (bytes) -> int
        return m2.ssl_write(self.ssl, data, self._timeout)

    def _write_nbio(self, data):
        # type: (bytes) -> int
        return m2.ssl_write_nbio(self.ssl, data)

    def _read_bio(self, size=1024):
        # type: (int) -> bytes
        if size <= 0:
            raise ValueError('size <= 0')
        return m2.ssl_read(self.ssl, size, self._timeout)

    def _read_nbio(self, size=1024):
        # type: (int) -> bytes
        if size <= 0:
            raise ValueError('size <= 0')
        return m2.ssl_read_nbio(self.ssl, size)

    def write(self, data):
        # type: (bytes) -> int
        if self._timeout != 0.0:
            return self._write_bio(data)
        return self._write_nbio(data)
    sendall = send = write

    def _decref_socketios(self):
        pass

    def recv_into(self, buff, nbytes=0):
        # type: (Union[bytearray, memoryview], int) -> int
        """
        A version of recv() that stores its data into a buffer rather
        than creating a new string.  Receive up to buffersize bytes from
        the socket.  If buffersize is not specified (or 0), receive up
        to the size available in the given buffer.

        If buff is bytearray, it will have after return length of the
        actually returned number of bytes. If buff is memoryview, then
        the size of buff won't change (it cannot), but all bytes after
        the number of returned bytes will be NULL.

        :param buffer: a buffer for the received bytes
        :param nbytes: maximum number of bytes to read
        :return: number of bytes read

        See recv() for documentation about the flags.
        """
        n = len(buff) if nbytes == 0 else nbytes

        if n <= 0:
            raise ValueError('size <= 0')

        # buff_bytes are actual bytes returned
        buff_bytes = m2.ssl_read(self.ssl, n, self._timeout)
        buflen = len(buff_bytes)

        # memoryview type has been added in 2.7
        if isinstance(buff, memoryview):
            buff[:buflen] = buff_bytes
            buff[buflen:] = b'\x00' * (len(buff) - buflen)
        else:
            buff[:] = buff_bytes

        return buflen

    def read(self, size=1024):
        # type: (int) -> bytes
        if self._timeout != 0.0:
            return self._read_bio(size)
        return self._read_nbio(size)
    recv = read

    def setblocking(self, mode):
        # type: (int) -> None
        """Set this connection's underlying socket to _mode_.

        Set blocking or non-blocking mode of the socket: if flag is 0,
        the socket is set to non-blocking, else to blocking mode.
        Initially all sockets are in blocking mode. In non-blocking mode,
        if a recv() call doesn't find any data, or if a send() call can't
        immediately dispose of the data, a error exception is raised;
        in blocking mode, the calls block until they can proceed.
        s.setblocking(0) is equivalent to s.settimeout(0.0);
        s.setblocking(1) is equivalent to s.settimeout(None).

        :param mode: new mode to be set
        """
        self.socket.setblocking(mode)
        if mode:
            self._timeout = -1.0
        else:
            self._timeout = 0.0

    def settimeout(self, timeout):
        # type: (float) -> None
        """Set this connection's underlying socket's timeout to _timeout_."""
        self.socket.settimeout(timeout)
        self._timeout = timeout
        if self._timeout is None:
            self._timeout = -1.0

    def fileno(self):
        # type: () -> int
        return self.socket.fileno()

    def getsockopt(self, level, optname, buflen=None):
        # type: (int, int, Optional[int]) -> Union[int, bytes]
        """Get the value of the given socket option.

        :param level: level at which the option resides.
               To manipulate options at the sockets API level, level is
               specified as socket.SOL_SOCKET. To manipulate options at
               any other level the protocol number of the appropriate
               protocol controlling the option is supplied. For example,
               to indicate that an option is to be interpreted by the
               TCP protocol, level should be set to the protocol number
               of socket.SOL_TCP; see getprotoent(3).

        :param optname: The value of the given socket option is
               described in the Unix man page getsockopt(2)). The needed
               symbolic constants (SO_* etc.) are defined in the socket
               module.

        :param buflen: If it is absent, an integer option is assumed
               and its integer value is returned by the function. If
               buflen is present, it specifies the maximum length of the
               buffer used to receive the option in, and this buffer is
               returned as a bytes object.

        :return: Either integer or bytes value of the option. It is up
                 to the caller to decode the contents of the buffer (see
                 the optional built-in module struct for a way to decode
                 C structures encoded as byte strings).
        """
        return self.socket.getsockopt(level, optname, buflen)

    def setsockopt(self, level, optname, value=None):
        # type: (int, int, Union[int, bytes, None]) -> Optional[bytes]
        """Set the value of the given socket option.

        :param level: same as with getsockopt() above

        :param optname: same as with getsockopt() above

        :param value: an integer or a string representing a buffer. In
                      the latter case it is up to the caller to ensure
                      that the string contains the proper bits (see the
                      optional built-in module struct for a way to
                      encode C structures as strings).

        :return: None for success or the error handler for failure.
        """
        return self.socket.setsockopt(level, optname, value)

    def get_context(self):
        # type: () -> Context
        """Return the Context object associated with this connection."""
        return m2.ssl_get_ssl_ctx(self.ssl)

    def get_state(self):
        # type: () -> bytes
        """Return the SSL state of this connection.

        During its use, an SSL objects passes several states. The state
        is internally maintained. Querying the state information is not
        very informative before or when a connection has been
        established. It however can be of significant interest during
        the handshake.

        :return: 6 letter string indicating the current state of the SSL
                 object ssl.
        """
        return m2.ssl_get_state(self.ssl)

    def verify_ok(self):
        # type: () -> bool
        return (m2.ssl_get_verify_result(self.ssl) == m2.X509_V_OK)

    def get_verify_mode(self):
        # type: () -> int
        """Return the peer certificate verification mode."""
        return m2.ssl_get_verify_mode(self.ssl)

    def get_verify_depth(self):
        # type: () -> int
        """Return the peer certificate verification depth."""
        return m2.ssl_get_verify_depth(self.ssl)

    def get_verify_result(self):
        # type: () -> int
        """Return the peer certificate verification result."""
        return m2.ssl_get_verify_result(self.ssl)

    def get_peer_cert(self):
        # type: () -> X509.X509
        """Return the peer certificate.

        If the peer did not provide a certificate, return None.
        """
        c = m2.ssl_get_peer_cert(self.ssl)
        if c is None:
            return None
        # Need to free the pointer coz OpenSSL doesn't.
        return X509.X509(c, 1)

    def get_peer_cert_chain(self):
        # type: () -> Optional[X509.X509_Stack]
        """Return the peer certificate chain; if the peer did not provide
        a certificate chain, return None.

        :warning: The returned chain will be valid only for as long as the
                  connection object is alive. Once the connection object
                  gets freed, the chain will be freed as well.
        """
        c = m2.ssl_get_peer_cert_chain(self.ssl)
        if c is None:
            return None
        # No need to free the pointer coz OpenSSL does.
        return X509.X509_Stack(c)

    def get_cipher(self):
        # type: () -> Optional[Cipher]
        """Return an M2Crypto.SSL.Cipher object for this connection; if the
        connection has not been initialised with a cipher suite, return None.
        """
        c = m2.ssl_get_current_cipher(self.ssl)
        if c is None:
            return None
        return Cipher(c)

    def get_ciphers(self):
        # type: () -> Optional[Cipher_Stack]
        """Return an M2Crypto.SSL.Cipher_Stack object for this
        connection; if the connection has not been initialised with
        cipher suites, return None.
        """
        c = m2.ssl_get_ciphers(self.ssl)
        if c is None:
            return None
        return Cipher_Stack(c)

    def get_cipher_list(self, idx=0):
        # type: (int) -> str
        """Return the cipher suites for this connection as a string object."""
        return six.ensure_text(m2.ssl_get_cipher_list(self.ssl, idx))

    def set_cipher_list(self, cipher_list):
        # type: (str) -> int
        """Set the cipher suites for this connection."""
        return m2.ssl_set_cipher_list(self.ssl, cipher_list)

    def makefile(self, mode='rb', bufsize=-1):
        # type: (AnyStr, int) -> Union[io.BufferedRWPair,io.BufferedReader]
        if six.PY3:
            raw = socket.SocketIO(self, mode)
            if 'rw' in mode:
                return io.BufferedRWPair(raw, raw)
            return io.BufferedReader(raw, io.DEFAULT_BUFFER_SIZE)
        else:
            return socket._fileobject(self, mode, bufsize)

    def getsockname(self):
        # type: () -> util.AddrType
        """Return the socket's own address.

        This is useful to find out the port number of an IPv4/v6 socket,
        for instance. (The format of the address returned depends
        on the address family -- see above.)

        :return:socket's address as addr type
        """
        return self.socket.getsockname()

    def getpeername(self):
        # type: () -> util.AddrType
        """Return the remote address to which the socket is connected.

        This is useful to find out the port number of a remote IPv4/v6 socket,
        for instance.
        On some systems this function is not supported.

        :return:
        """
        return self.socket.getpeername()

    def set_session_id_ctx(self, id):
        # type: (bytes) -> int
        ret = m2.ssl_set_session_id_context(self.ssl, id)
        if not ret:
            raise SSLError(Err.get_error_message())

    def get_session(self):
        # type: () -> Session
        sess = m2.ssl_get_session(self.ssl)
        return Session(sess)

    def set_session(self, session):
        # type: (Session) -> None
        m2.ssl_set_session(self.ssl, session._ptr())

    def get_default_session_timeout(self):
        # type: () -> int
        return m2.ssl_get_default_session_timeout(self.ssl)

    def get_socket_read_timeout(self):
        # type: () -> timeout
        return timeout.struct_to_timeout(
            self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
                                   timeout.struct_size()))

    @staticmethod
    def _hexdump(s):
        assert isinstance(s, six.binary_type)
        return ":".join("{0:02x}".format(ord(c) if six.PY2 else c) for c in s)

    def get_socket_write_timeout(self):
        # type: () -> timeout
        binstr = self.socket.getsockopt(
            socket.SOL_SOCKET, socket.SO_SNDTIMEO, timeout.struct_size())
        timeo = timeout.struct_to_timeout(binstr)
        # print("Debug: get_socket_write_timeout: "
        #       "get sockopt value: %s -> ret timeout(sec=%r, microsec=%r)" %
        #       (self._hexdump(binstr), timeo.sec, timeo.microsec))
        return timeo

    def set_socket_read_timeout(self, timeo):
        # type: (timeout) -> None
        assert isinstance(timeo, timeout.timeout)
        self.socket.setsockopt(
            socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeo.pack())

    def set_socket_write_timeout(self, timeo):
        # type: (timeout) -> None
        assert isinstance(timeo, timeout.timeout)
        binstr = timeo.pack()
        # print("Debug: set_socket_write_timeout: "
        #       "input timeout(sec=%r, microsec=%r) -> set sockopt value: %s" %
        #       (timeo.sec, timeo.microsec, self._hexdump(binstr)))
        self.socket.setsockopt(
            socket.SOL_SOCKET, socket.SO_SNDTIMEO, binstr)

    def get_version(self):
        # type: () -> str
        """Return the TLS/SSL protocol version for this connection."""
        return six.ensure_text(m2.ssl_get_version(self.ssl))

    def set_post_connection_check_callback(self, postConnectionCheck):  # noqa
        # type: (Callable) -> None
        self.postConnectionCheck = postConnectionCheck

    def set_tlsext_host_name(self, name):
        # type: (bytes) -> None
        """Set the requested hostname for the SNI (Server Name Indication)
        extension.
        """
        m2.ssl_set_tlsext_host_name(self.ssl, name)

    def set1_host(self, name):
        # type: (bytes) -> None
        """Set the requested hostname to check in the server certificate."""
        self.host = name