Home | History | Annotate | Download | only in net_test
      1 #!/usr/bin/python
      2 #
      3 # Copyright 2015 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 # http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 # pylint: disable=g-bad-todo,g-bad-file-header,wildcard-import
     18 from errno import *  # pylint: disable=wildcard-import
     19 import os
     20 import random
     21 import re
     22 from socket import *  # pylint: disable=wildcard-import
     23 import threading
     24 import time
     25 import unittest
     26 
     27 import multinetwork_base
     28 import net_test
     29 import packets
     30 import sock_diag
     31 import tcp_test
     32 
     33 
     34 NUM_SOCKETS = 30
     35 NO_BYTECODE = ""
     36 
     37 
     38 class SockDiagBaseTest(multinetwork_base.MultiNetworkBaseTest):
     39 
     40   @staticmethod
     41   def _CreateLotsOfSockets():
     42     # Dict mapping (addr, sport, dport) tuples to socketpairs.
     43     socketpairs = {}
     44     for _ in xrange(NUM_SOCKETS):
     45       family, addr = random.choice([
     46           (AF_INET, "127.0.0.1"),
     47           (AF_INET6, "::1"),
     48           (AF_INET6, "::ffff:127.0.0.1")])
     49       socketpair = net_test.CreateSocketPair(family, SOCK_STREAM, addr)
     50       sport, dport = (socketpair[0].getsockname()[1],
     51                       socketpair[1].getsockname()[1])
     52       socketpairs[(addr, sport, dport)] = socketpair
     53     return socketpairs
     54 
     55   def assertSocketClosed(self, sock):
     56     self.assertRaisesErrno(ENOTCONN, sock.getpeername)
     57 
     58   def assertSocketConnected(self, sock):
     59     sock.getpeername()  # No errors? Socket is alive and connected.
     60 
     61   def assertSocketsClosed(self, socketpair):
     62     for sock in socketpair:
     63       self.assertSocketClosed(sock)
     64 
     65   def setUp(self):
     66     super(SockDiagBaseTest, self).setUp()
     67     self.sock_diag = sock_diag.SockDiag()
     68     self.socketpairs = {}
     69 
     70   def tearDown(self):
     71     for socketpair in self.socketpairs.values():
     72       for s in socketpair:
     73         s.close()
     74     super(SockDiagBaseTest, self).tearDown()
     75 
     76 
     77 class SockDiagTest(SockDiagBaseTest):
     78 
     79   def assertSockDiagMatchesSocket(self, s, diag_msg):
     80     family = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_DOMAIN)
     81     self.assertEqual(diag_msg.family, family)
     82 
     83     src, sport = s.getsockname()[0:2]
     84     self.assertEqual(diag_msg.id.src, self.sock_diag.PaddedAddress(src))
     85     self.assertEqual(diag_msg.id.sport, sport)
     86 
     87     if self.sock_diag.GetDestinationAddress(diag_msg) not in ["0.0.0.0", "::"]:
     88       dst, dport = s.getpeername()[0:2]
     89       self.assertEqual(diag_msg.id.dst, self.sock_diag.PaddedAddress(dst))
     90       self.assertEqual(diag_msg.id.dport, dport)
     91     else:
     92       self.assertRaisesErrno(ENOTCONN, s.getpeername)
     93 
     94   def testFindsMappedSockets(self):
     95     """Tests that inet_diag_find_one_icsk can find mapped sockets.
     96 
     97     Relevant kernel commits:
     98       android-3.10:
     99         f77e059 net: diag: support v4mapped sockets in inet_diag_find_one_icsk()
    100     """
    101     socketpair = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM,
    102                                            "::ffff:127.0.0.1")
    103     for sock in socketpair:
    104       diag_msg = self.sock_diag.FindSockDiagFromFd(sock)
    105       diag_req = self.sock_diag.DiagReqFromDiagMsg(diag_msg, IPPROTO_TCP)
    106       self.sock_diag.GetSockDiag(diag_req)
    107       # No errors? Good.
    108 
    109   def testFindsAllMySockets(self):
    110     """Tests that basic socket dumping works.
    111 
    112     Relevant commits:
    113       android-3.4:
    114         ab4a727 net: inet_diag: zero out uninitialized idiag_{src,dst} fields
    115       android-3.10
    116         3eb409b net: inet_diag: zero out uninitialized idiag_{src,dst} fields
    117     """
    118     self.socketpairs = self._CreateLotsOfSockets()
    119     sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, NO_BYTECODE)
    120     self.assertGreaterEqual(len(sockets), NUM_SOCKETS)
    121 
    122     # Find the cookies for all of our sockets.
    123     cookies = {}
    124     for diag_msg, unused_attrs in sockets:
    125       addr = self.sock_diag.GetSourceAddress(diag_msg)
    126       sport = diag_msg.id.sport
    127       dport = diag_msg.id.dport
    128       if (addr, sport, dport) in self.socketpairs:
    129         cookies[(addr, sport, dport)] = diag_msg.id.cookie
    130       elif (addr, dport, sport) in self.socketpairs:
    131         cookies[(addr, sport, dport)] = diag_msg.id.cookie
    132 
    133     # Did we find all the cookies?
    134     self.assertEquals(2 * NUM_SOCKETS, len(cookies))
    135 
    136     socketpairs = self.socketpairs.values()
    137     random.shuffle(socketpairs)
    138     for socketpair in socketpairs:
    139       for sock in socketpair:
    140         # Check that we can find a diag_msg by scanning a dump.
    141         self.assertSockDiagMatchesSocket(
    142             sock,
    143             self.sock_diag.FindSockDiagFromFd(sock))
    144         cookie = self.sock_diag.FindSockDiagFromFd(sock).id.cookie
    145 
    146         # Check that we can find a diag_msg once we know the cookie.
    147         req = self.sock_diag.DiagReqFromSocket(sock)
    148         req.id.cookie = cookie
    149         diag_msg = self.sock_diag.GetSockDiag(req)
    150         req.states = 1 << diag_msg.state
    151         self.assertSockDiagMatchesSocket(sock, diag_msg)
    152 
    153   def testBytecodeCompilation(self):
    154     # pylint: disable=bad-whitespace
    155     instructions = [
    156         (sock_diag.INET_DIAG_BC_S_GE,   1, 8, 0),                      # 0
    157         (sock_diag.INET_DIAG_BC_D_LE,   1, 7, 0xffff),                 # 8
    158         (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("::1", 128, -1)),       # 16
    159         (sock_diag.INET_DIAG_BC_JMP,    1, 3, None),                   # 44
    160         (sock_diag.INET_DIAG_BC_S_COND, 2, 4, ("127.0.0.1", 32, -1)),  # 48
    161         (sock_diag.INET_DIAG_BC_D_LE,   1, 3, 0x6665),  # not used     # 64
    162         (sock_diag.INET_DIAG_BC_NOP,    1, 1, None),                   # 72
    163                                                                        # 76 acc
    164                                                                        # 80 rej
    165     ]
    166     # pylint: enable=bad-whitespace
    167     bytecode = self.sock_diag.PackBytecode(instructions)
    168     expected = (
    169         "0208500000000000"
    170         "050848000000ffff"
    171         "071c20000a800000ffffffff00000000000000000000000000000001"
    172         "01041c00"
    173         "0718200002200000ffffffff7f000001"
    174         "0508100000006566"
    175         "00040400"
    176     )
    177     self.assertMultiLineEqual(expected, bytecode.encode("hex"))
    178     self.assertEquals(76, len(bytecode))
    179     self.socketpairs = self._CreateLotsOfSockets()
    180     filteredsockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode)
    181     allsockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, NO_BYTECODE)
    182     self.assertItemsEqual(allsockets, filteredsockets)
    183 
    184     # Pick a few sockets in hash table order, and check that the bytecode we
    185     # compiled selects them properly.
    186     for socketpair in self.socketpairs.values()[:20]:
    187       for s in socketpair:
    188         diag_msg = self.sock_diag.FindSockDiagFromFd(s)
    189         instructions = [
    190             (sock_diag.INET_DIAG_BC_S_GE, 1, 5, diag_msg.id.sport),
    191             (sock_diag.INET_DIAG_BC_S_LE, 1, 4, diag_msg.id.sport),
    192             (sock_diag.INET_DIAG_BC_D_GE, 1, 3, diag_msg.id.dport),
    193             (sock_diag.INET_DIAG_BC_D_LE, 1, 2, diag_msg.id.dport),
    194         ]
    195         bytecode = self.sock_diag.PackBytecode(instructions)
    196         self.assertEquals(32, len(bytecode))
    197         sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode)
    198         self.assertEquals(1, len(sockets))
    199 
    200         # TODO: why doesn't comparing the cstructs work?
    201         self.assertEquals(diag_msg.Pack(), sockets[0][0].Pack())
    202 
    203   def testCrossFamilyBytecode(self):
    204     """Checks for a cross-family bug in inet_diag_hostcond matching.
    205 
    206     Relevant kernel commits:
    207       android-3.4:
    208         f67caec inet_diag: avoid unsafe and nonsensical prefix matches in inet_diag_bc_run()
    209     """
    210     # TODO: this is only here because the test fails if there are any open
    211     # sockets other than the ones it creates itself. Make the bytecode more
    212     # specific and remove it.
    213     self.assertFalse(self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, ""))
    214 
    215     unused_pair4 = net_test.CreateSocketPair(AF_INET, SOCK_STREAM, "127.0.0.1")
    216     unused_pair6 = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM, "::1")
    217 
    218     bytecode4 = self.sock_diag.PackBytecode([
    219         (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("0.0.0.0", 0, -1))])
    220     bytecode6 = self.sock_diag.PackBytecode([
    221         (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("::", 0, -1))])
    222 
    223     # IPv4/v6 filters must never match IPv6/IPv4 sockets...
    224     v4sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode4)
    225     self.assertTrue(v4sockets)
    226     self.assertTrue(all(d.family == AF_INET for d, _ in v4sockets))
    227 
    228     v6sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode6)
    229     self.assertTrue(v6sockets)
    230     self.assertTrue(all(d.family == AF_INET6 for d, _ in v6sockets))
    231 
    232     # Except for mapped addresses, which match both IPv4 and IPv6.
    233     pair5 = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM,
    234                                       "::ffff:127.0.0.1")
    235     diag_msgs = [self.sock_diag.FindSockDiagFromFd(s) for s in pair5]
    236     v4sockets = [d for d, _ in self.sock_diag.DumpAllInetSockets(IPPROTO_TCP,
    237                                                                  bytecode4)]
    238     v6sockets = [d for d, _ in self.sock_diag.DumpAllInetSockets(IPPROTO_TCP,
    239                                                                  bytecode6)]
    240     self.assertTrue(all(d in v4sockets for d in diag_msgs))
    241     self.assertTrue(all(d in v6sockets for d in diag_msgs))
    242 
    243   def testPortComparisonValidation(self):
    244     """Checks for a bug in validating port comparison bytecode.
    245 
    246     Relevant kernel commits:
    247       android-3.4:
    248         5e1f542 inet_diag: validate port comparison byte code to prevent unsafe reads
    249     """
    250     bytecode = sock_diag.InetDiagBcOp((sock_diag.INET_DIAG_BC_D_GE, 4, 8))
    251     self.assertRaisesErrno(
    252         EINVAL,
    253         self.sock_diag.DumpAllInetSockets, IPPROTO_TCP, bytecode.Pack())
    254 
    255   def testNonSockDiagCommand(self):
    256     def DiagDump(code):
    257       sock_id = self.sock_diag._EmptyInetDiagSockId()
    258       req = sock_diag.InetDiagReqV2((AF_INET6, IPPROTO_TCP, 0, 0xffffffff,
    259                                      sock_id))
    260       self.sock_diag._Dump(code, req, sock_diag.InetDiagMsg, "")
    261 
    262     op = sock_diag.SOCK_DIAG_BY_FAMILY
    263     DiagDump(op)  # No errors? Good.
    264     self.assertRaisesErrno(EINVAL, DiagDump, op + 17)
    265 
    266 
    267 class SockDestroyTest(SockDiagBaseTest):
    268   """Tests that SOCK_DESTROY works correctly.
    269 
    270   Relevant kernel commits:
    271     net-next:
    272       b613f56 net: diag: split inet_diag_dump_one_icsk into two
    273       64be0ae net: diag: Add the ability to destroy a socket.
    274       6eb5d2e net: diag: Support SOCK_DESTROY for inet sockets.
    275       c1e64e2 net: diag: Support destroying TCP sockets.
    276       2010b93 net: tcp: deal with listen sockets properly in tcp_abort.
    277 
    278     android-3.4:
    279       d48ec88 net: diag: split inet_diag_dump_one_icsk into two
    280       2438189 net: diag: Add the ability to destroy a socket.
    281       7a2ddbc net: diag: Support SOCK_DESTROY for inet sockets.
    282       44047b2 net: diag: Support destroying TCP sockets.
    283       200dae7 net: tcp: deal with listen sockets properly in tcp_abort.
    284 
    285     android-3.10:
    286       9eaff90 net: diag: split inet_diag_dump_one_icsk into two
    287       d60326c net: diag: Add the ability to destroy a socket.
    288       3d4ce85 net: diag: Support SOCK_DESTROY for inet sockets.
    289       529dfc6 net: diag: Support destroying TCP sockets.
    290       9c712fe net: tcp: deal with listen sockets properly in tcp_abort.
    291 
    292     android-3.18:
    293       100263d net: diag: split inet_diag_dump_one_icsk into two
    294       194c5f3 net: diag: Add the ability to destroy a socket.
    295       8387ea2 net: diag: Support SOCK_DESTROY for inet sockets.
    296       b80585a net: diag: Support destroying TCP sockets.
    297       476c6ce net: tcp: deal with listen sockets properly in tcp_abort.
    298   """
    299 
    300   def testClosesSockets(self):
    301     self.socketpairs = self._CreateLotsOfSockets()
    302     for _, socketpair in self.socketpairs.iteritems():
    303       # Close one of the sockets.
    304       # This will send a RST that will close the other side as well.
    305       s = random.choice(socketpair)
    306       if random.randrange(0, 2) == 1:
    307         self.sock_diag.CloseSocketFromFd(s)
    308       else:
    309         diag_msg = self.sock_diag.FindSockDiagFromFd(s)
    310 
    311         # Get the cookie wrong and ensure that we get an error and the socket
    312         # is not closed.
    313         real_cookie = diag_msg.id.cookie
    314         diag_msg.id.cookie = os.urandom(len(real_cookie))
    315         req = self.sock_diag.DiagReqFromDiagMsg(diag_msg, IPPROTO_TCP)
    316         self.assertRaisesErrno(ENOENT, self.sock_diag.CloseSocket, req)
    317         self.assertSocketConnected(s)
    318 
    319         # Now close it with the correct cookie.
    320         req.id.cookie = real_cookie
    321         self.sock_diag.CloseSocket(req)
    322 
    323       # Check that both sockets in the pair are closed.
    324       self.assertSocketsClosed(socketpair)
    325 
    326   def testNonTcpSockets(self):
    327     s = socket(AF_INET6, SOCK_DGRAM, 0)
    328     s.connect(("::1", 53))
    329     self.sock_diag.FindSockDiagFromFd(s)  # No exceptions? Good.
    330     self.assertRaisesErrno(EOPNOTSUPP, self.sock_diag.CloseSocketFromFd, s)
    331 
    332   # TODO:
    333   # Test that killing unix sockets returns EOPNOTSUPP.
    334 
    335 
    336 class SocketExceptionThread(threading.Thread):
    337 
    338   def __init__(self, sock, operation):
    339     self.exception = None
    340     super(SocketExceptionThread, self).__init__()
    341     self.daemon = True
    342     self.sock = sock
    343     self.operation = operation
    344 
    345   def run(self):
    346     try:
    347       self.operation(self.sock)
    348     except IOError, e:
    349       self.exception = e
    350 
    351 
    352 class SockDiagTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest):
    353 
    354   def testIpv4MappedSynRecvSocket(self):
    355     """Tests for the absence of a bug with AF_INET6 TCP SYN-RECV sockets.
    356 
    357     Relevant kernel commits:
    358          android-3.4:
    359            457a04b inet_diag: fix oops for IPv4 AF_INET6 TCP SYN-RECV state
    360     """
    361     netid = random.choice(self.tuns.keys())
    362     self.IncomingConnection(5, tcp_test.TCP_SYN_RECV, netid)
    363     sock_id = self.sock_diag._EmptyInetDiagSockId()
    364     sock_id.sport = self.port
    365     states = 1 << tcp_test.TCP_SYN_RECV
    366     req = sock_diag.InetDiagReqV2((AF_INET6, IPPROTO_TCP, 0, states, sock_id))
    367     children = self.sock_diag.Dump(req, NO_BYTECODE)
    368 
    369     self.assertTrue(children)
    370     for child, unused_args in children:
    371       self.assertEqual(tcp_test.TCP_SYN_RECV, child.state)
    372       self.assertEqual(self.sock_diag.PaddedAddress(self.remoteaddr),
    373                        child.id.dst)
    374       self.assertEqual(self.sock_diag.PaddedAddress(self.myaddr),
    375                        child.id.src)
    376 
    377 
    378 class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest):
    379 
    380   def setUp(self):
    381     super(SockDestroyTcpTest, self).setUp()
    382     self.netid = random.choice(self.tuns.keys())
    383 
    384   def CheckRstOnClose(self, sock, req, expect_reset, msg, do_close=True):
    385     """Closes the socket and checks whether a RST is sent or not."""
    386     if sock is not None:
    387       self.assertIsNone(req, "Must specify sock or req, not both")
    388       self.sock_diag.CloseSocketFromFd(sock)
    389       self.assertRaisesErrno(EINVAL, sock.accept)
    390     else:
    391       self.assertIsNone(sock, "Must specify sock or req, not both")
    392       self.sock_diag.CloseSocket(req)
    393 
    394     if expect_reset:
    395       desc, rst = self.RstPacket()
    396       msg = "%s: expecting %s: " % (msg, desc)
    397       self.ExpectPacketOn(self.netid, msg, rst)
    398     else:
    399       msg = "%s: " % msg
    400       self.ExpectNoPacketsOn(self.netid, msg)
    401 
    402     if sock is not None and do_close:
    403       sock.close()
    404 
    405   def CheckTcpReset(self, state, statename):
    406     for version in [4, 5, 6]:
    407       msg = "Closing incoming IPv%d %s socket" % (version, statename)
    408       self.IncomingConnection(version, state, self.netid)
    409       self.CheckRstOnClose(self.s, None, False, msg)
    410       if state != tcp_test.TCP_LISTEN:
    411         msg = "Closing accepted IPv%d %s socket" % (version, statename)
    412         self.CheckRstOnClose(self.accepted, None, True, msg)
    413 
    414   def testTcpResets(self):
    415     """Checks that closing sockets in appropriate states sends a RST."""
    416     self.CheckTcpReset(tcp_test.TCP_LISTEN, "TCP_LISTEN")
    417     self.CheckTcpReset(tcp_test.TCP_ESTABLISHED, "TCP_ESTABLISHED")
    418     self.CheckTcpReset(tcp_test.TCP_CLOSE_WAIT, "TCP_CLOSE_WAIT")
    419 
    420   def FindChildSockets(self, s):
    421     """Finds the SYN_RECV child sockets of a given listening socket."""
    422     d = self.sock_diag.FindSockDiagFromFd(self.s)
    423     req = self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP)
    424     req.states = 1 << tcp_test.TCP_SYN_RECV | 1 << tcp_test.TCP_ESTABLISHED
    425     req.id.cookie = "\x00" * 8
    426     children = self.sock_diag.Dump(req, NO_BYTECODE)
    427     return [self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP)
    428             for d, _ in children]
    429 
    430   def CheckChildSocket(self, version, statename, parent_first):
    431     state = getattr(tcp_test, statename)
    432 
    433     self.IncomingConnection(version, state, self.netid)
    434 
    435     d = self.sock_diag.FindSockDiagFromFd(self.s)
    436     parent = self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP)
    437     children = self.FindChildSockets(self.s)
    438     self.assertEquals(1, len(children))
    439 
    440     is_established = (state == tcp_test.TCP_NOT_YET_ACCEPTED)
    441 
    442     # The new TCP listener code in 4.4 makes SYN_RECV sockets live in the
    443     # regular TCP hash tables, and inet_diag_find_one_icsk can find them.
    444     # Before 4.4, we can see those sockets in dumps, but we can't fetch
    445     # or close them.
    446     can_close_children = is_established or net_test.LINUX_VERSION >= (4, 4)
    447 
    448     for child in children:
    449       if can_close_children:
    450         self.sock_diag.GetSockDiag(child)  # No errors? Good, child found.
    451       else:
    452         self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child)
    453 
    454     def CloseParent(expect_reset):
    455       msg = "Closing parent IPv%d %s socket %s child" % (
    456           version, statename, "before" if parent_first else "after")
    457       self.CheckRstOnClose(self.s, None, expect_reset, msg)
    458       self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, parent)
    459 
    460     def CheckChildrenClosed():
    461       for child in children:
    462         self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child)
    463 
    464     def CloseChildren():
    465       for child in children:
    466         msg = "Closing child IPv%d %s socket %s parent" % (
    467             version, statename, "after" if parent_first else "before")
    468         self.sock_diag.GetSockDiag(child)
    469         self.CheckRstOnClose(None, child, is_established, msg)
    470         self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child)
    471       CheckChildrenClosed()
    472 
    473     if parent_first:
    474       # Closing the parent will close child sockets, which will send a RST,
    475       # iff they are already established.
    476       CloseParent(is_established)
    477       if is_established:
    478         CheckChildrenClosed()
    479       elif can_close_children:
    480         CloseChildren()
    481         CheckChildrenClosed()
    482       self.s.close()
    483     else:
    484       if can_close_children:
    485         CloseChildren()
    486       CloseParent(False)
    487       self.s.close()
    488 
    489   def testChildSockets(self):
    490     for version in [4, 5, 6]:
    491       self.CheckChildSocket(version, "TCP_SYN_RECV", False)
    492       self.CheckChildSocket(version, "TCP_SYN_RECV", True)
    493       self.CheckChildSocket(version, "TCP_NOT_YET_ACCEPTED", False)
    494       self.CheckChildSocket(version, "TCP_NOT_YET_ACCEPTED", True)
    495 
    496   def CloseDuringBlockingCall(self, sock, call, expected_errno):
    497     thread = SocketExceptionThread(sock, call)
    498     thread.start()
    499     time.sleep(0.1)
    500     self.sock_diag.CloseSocketFromFd(sock)
    501     thread.join(1)
    502     self.assertFalse(thread.is_alive())
    503     self.assertIsNotNone(thread.exception)
    504     self.assertTrue(isinstance(thread.exception, IOError),
    505                     "Expected IOError, got %s" % thread.exception)
    506     self.assertEqual(expected_errno, thread.exception.errno)
    507     self.assertSocketClosed(sock)
    508 
    509   def testAcceptInterrupted(self):
    510     """Tests that accept() is interrupted by SOCK_DESTROY."""
    511     for version in [4, 5, 6]:
    512       self.IncomingConnection(version, tcp_test.TCP_LISTEN, self.netid)
    513       self.CloseDuringBlockingCall(self.s, lambda sock: sock.accept(), EINVAL)
    514       self.assertRaisesErrno(ECONNABORTED, self.s.send, "foo")
    515       self.assertRaisesErrno(EINVAL, self.s.accept)
    516 
    517   def testReadInterrupted(self):
    518     """Tests that read() is interrupted by SOCK_DESTROY."""
    519     for version in [4, 5, 6]:
    520       self.IncomingConnection(version, tcp_test.TCP_ESTABLISHED, self.netid)
    521       self.CloseDuringBlockingCall(self.accepted, lambda sock: sock.recv(4096),
    522                                    ECONNABORTED)
    523       self.assertRaisesErrno(EPIPE, self.accepted.send, "foo")
    524 
    525   def testConnectInterrupted(self):
    526     """Tests that connect() is interrupted by SOCK_DESTROY."""
    527     for version in [4, 5, 6]:
    528       family = {4: AF_INET, 5: AF_INET6, 6: AF_INET6}[version]
    529       s = net_test.Socket(family, SOCK_STREAM, IPPROTO_TCP)
    530       self.SelectInterface(s, self.netid, "mark")
    531       if version == 5:
    532         remoteaddr = "::ffff:" + self.GetRemoteAddress(4)
    533         version = 4
    534       else:
    535         remoteaddr = self.GetRemoteAddress(version)
    536       s.bind(("", 0))
    537       _, sport = s.getsockname()[:2]
    538       self.CloseDuringBlockingCall(
    539           s, lambda sock: sock.connect((remoteaddr, 53)), ECONNABORTED)
    540       desc, syn = packets.SYN(53, version, self.MyAddress(version, self.netid),
    541                               remoteaddr, sport=sport, seq=None)
    542       self.ExpectPacketOn(self.netid, desc, syn)
    543       msg = "SOCK_DESTROY of socket in connect, expected no RST"
    544       self.ExpectNoPacketsOn(self.netid, msg)
    545 
    546 
    547 if __name__ == "__main__":
    548   unittest.main()
    549