| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d86ab5d commit 0b65c8b
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -647,6 +647,23 @@ def socketpair(family=AF_INET, type=SOCK_STREAM, proto=0): | |||
| 647 | 647 | raise | |
| 648 | 648 | finally: | |
| 649 | 649 | lsock.close() | |
| 650 | + | ||
| 651 | + # Authenticating avoids using a connection from something else | ||
| 652 | + # able to connect to {host}:{port} instead of us. | ||
| 653 | + # We expect only AF_INET and AF_INET6 families. | ||
| 654 | + try: | ||
| 655 | + if ( | ||
| 656 | + ssock.getsockname() != csock.getpeername() | ||
| 657 | + or csock.getsockname() != ssock.getpeername() | ||
| 658 | + ): | ||
| 659 | + raise ConnectionError("Unexpected peer connection") | ||
| 660 | + except: | ||
| 661 | + # getsockname() and getpeername() can fail | ||
| 662 | + # if either socket isn't connected. | ||
| 663 | + ssock.close() | ||
| 664 | + csock.close() | ||
| 665 | + raise | ||
| 666 | + | ||
| 650 | 667 | return (ssock, csock) | |
| 651 | 668 | __all__.append("socketpair") | |
| 652 | 669 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -557,19 +557,27 @@ class SocketPairTest(unittest.TestCase, ThreadableTest): | |||
| 557 | 557 | def __init__(self, methodName='runTest'): | |
| 558 | 558 | unittest.TestCase.__init__(self, methodName=methodName) | |
| 559 | 559 | ThreadableTest.__init__(self) | |
| 560 | + self.cli = None | ||
| 561 | + self.serv = None | ||
| 562 | + | ||
| 563 | + def socketpair(self): | ||
| 564 | + # To be overridden by some child classes. | ||
| 565 | + return socket.socketpair() | ||
| 560 | 566 | ||
| 561 | 567 | def setUp(self): | |
| 562 | - self.serv, self.cli = socket.socketpair() | ||
| 568 | + self.serv, self.cli = self.socketpair() | ||
| 563 | 569 | ||
| 564 | 570 | def tearDown(self): | |
| 565 | - self.serv.close() | ||
| 571 | + if self.serv: | ||
| 572 | + self.serv.close() | ||
| 566 | 573 | self.serv = None | |
| 567 | 574 | ||
| 568 | 575 | def clientSetUp(self): | |
| 569 | 576 | pass | |
| 570 | 577 | ||
| 571 | 578 | def clientTearDown(self): | |
| 572 | - self.cli.close() | ||
| 579 | + if self.cli: | ||
| 580 | + self.cli.close() | ||
| 573 | 581 | self.cli = None | |
| 574 | 582 | ThreadableTest.clientTearDown(self) | |
| 575 | 583 | ||
@@ -4630,6 +4638,120 @@ def _testSend(self): | |||
| 4630 | 4638 | self.assertEqual(msg, MSG) | |
| 4631 | 4639 | ||
| 4632 | 4640 | ||
| 4641 | + class PurePythonSocketPairTest(SocketPairTest): | ||
| 4642 | + | ||
| 4643 | + # Explicitly use socketpair AF_INET or AF_INET6 to ensure that is the | ||
| 4644 | + # code path we're using regardless platform is the pure python one where | ||
| 4645 | + # `_socket.socketpair` does not exist. (AF_INET does not work with | ||
| 4646 | + # _socket.socketpair on many platforms). | ||
| 4647 | + def socketpair(self): | ||
| 4648 | + # called by super().setUp(). | ||
| 4649 | + try: | ||
| 4650 | + return socket.socketpair(socket.AF_INET6) | ||
| 4651 | + except OSError: | ||
| 4652 | + return socket.socketpair(socket.AF_INET) | ||
| 4653 | + | ||
| 4654 | + # Local imports in this class make for easy security fix backporting. | ||
| 4655 | + | ||
| 4656 | + def setUp(self): | ||
| 4657 | + import _socket | ||
| 4658 | + self._orig_sp = getattr(_socket, 'socketpair', None) | ||
| 4659 | + if self._orig_sp is not None: | ||
| 4660 | + # This forces the version using the non-OS provided socketpair | ||
| 4661 | + # emulation via an AF_INET socket in Lib/socket.py. | ||
| 4662 | + del _socket.socketpair | ||
| 4663 | + import importlib | ||
| 4664 | + global socket | ||
| 4665 | + socket = importlib.reload(socket) | ||
| 4666 | + else: | ||
| 4667 | + pass # This platform already uses the non-OS provided version. | ||
| 4668 | + super().setUp() | ||
| 4669 | + | ||
| 4670 | + def tearDown(self): | ||
| 4671 | + super().tearDown() | ||
| 4672 | + import _socket | ||
| 4673 | + if self._orig_sp is not None: | ||
| 4674 | + # Restore the default socket.socketpair definition. | ||
| 4675 | + _socket.socketpair = self._orig_sp | ||
| 4676 | + import importlib | ||
| 4677 | + global socket | ||
| 4678 | + socket = importlib.reload(socket) | ||
| 4679 | + | ||
| 4680 | + def test_recv(self): | ||
| 4681 | + msg = self.serv.recv(1024) | ||
| 4682 | + self.assertEqual(msg, MSG) | ||
| 4683 | + | ||
| 4684 | + def _test_recv(self): | ||
| 4685 | + self.cli.send(MSG) | ||
| 4686 | + | ||
| 4687 | + def test_send(self): | ||
| 4688 | + self.serv.send(MSG) | ||
| 4689 | + | ||
| 4690 | + def _test_send(self): | ||
| 4691 | + msg = self.cli.recv(1024) | ||
| 4692 | + self.assertEqual(msg, MSG) | ||
| 4693 | + | ||
| 4694 | + def test_ipv4(self): | ||
| 4695 | + cli, srv = socket.socketpair(socket.AF_INET) | ||
| 4696 | + cli.close() | ||
| 4697 | + srv.close() | ||
| 4698 | + | ||
| 4699 | + def _test_ipv4(self): | ||
| 4700 | + pass | ||
| 4701 | + | ||
| 4702 | + @unittest.skipIf(not hasattr(_socket, 'IPPROTO_IPV6') or | ||
| 4703 | + not hasattr(_socket, 'IPV6_V6ONLY'), | ||
| 4704 | + "IPV6_V6ONLY option not supported") | ||
| 4705 | + @unittest.skipUnless(socket_helper.IPV6_ENABLED, 'IPv6 required for this test') | ||
| 4706 | + def test_ipv6(self): | ||
| 4707 | + cli, srv = socket.socketpair(socket.AF_INET6) | ||
| 4708 | + cli.close() | ||
| 4709 | + srv.close() | ||
| 4710 | + | ||
| 4711 | + def _test_ipv6(self): | ||
| 4712 | + pass | ||
| 4713 | + | ||
| 4714 | + def test_injected_authentication_failure(self): | ||
| 4715 | + orig_getsockname = socket.socket.getsockname | ||
| 4716 | + inject_sock = None | ||
| 4717 | + | ||
| 4718 | + def inject_getsocketname(self): | ||
| 4719 | + nonlocal inject_sock | ||
| 4720 | + sockname = orig_getsockname(self) | ||
| 4721 | + # Connect to the listening socket ahead of the | ||
| 4722 | + # client socket. | ||
| 4723 | + if inject_sock is None: | ||
| 4724 | + inject_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 4725 | + inject_sock.setblocking(False) | ||
| 4726 | + try: | ||
| 4727 | + inject_sock.connect(sockname[:2]) | ||
| 4728 | + except (BlockingIOError, InterruptedError): | ||
| 4729 | + pass | ||
| 4730 | + inject_sock.setblocking(True) | ||
| 4731 | + return sockname | ||
| 4732 | + | ||
| 4733 | + sock1 = sock2 = None | ||
| 4734 | + try: | ||
| 4735 | + socket.socket.getsockname = inject_getsocketname | ||
| 4736 | + with self.assertRaises(OSError): | ||
| 4737 | + sock1, sock2 = socket.socketpair() | ||
| 4738 | + finally: | ||
| 4739 | + socket.socket.getsockname = orig_getsockname | ||
| 4740 | + if inject_sock: | ||
| 4741 | + inject_sock.close() | ||
| 4742 | + if sock1: # This cleanup isn't needed on a successful test. | ||
| 4743 | + sock1.close() | ||
| 4744 | + if sock2: | ||
| 4745 | + sock2.close() | ||
| 4746 | + | ||
| 4747 | + def _test_injected_authentication_failure(self): | ||
| 4748 | + # No-op. Exists for base class threading infrastructure to call. | ||
| 4749 | + # We could refactor this test into its own lesser class along with the | ||
| 4750 | + # setUp and tearDown code to construct an ideal; it is simpler to keep | ||
| 4751 | + # it here and live with extra overhead one this _one_ failure test. | ||
| 4752 | + pass | ||
| 4753 | + | ||
| 4754 | + | ||
| 4633 | 4755 | class NonBlockingTCPTests(ThreadedTCPSocketTest): | |
| 4634 | 4756 | ||
| 4635 | 4757 | def __init__(self, methodName='runTest'): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + Authenticate the socket connection for the ``socket.socketpair()`` fallback | ||
| 2 | + on platforms where ``AF_UNIX`` is not available like Windows. | ||
| 3 | + | ||
| 4 | + Patch by Gregory P. Smith <greg@krypto.org> and Seth Larson <seth@python.org>. Reported by Ellie | ||
| 5 | + <el@horse64.org> | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments