diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 6b4756ad088220..c1e9e1f819d4a9 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1213,7 +1213,8 @@ async def create_datagram_endpoint(self, protocol_factory, if local_addr: sock.bind(local_address) if remote_addr: - await self.sock_connect(sock, remote_address) + if not allow_broadcast: + await self.sock_connect(sock, remote_address) r_addr = remote_address except OSError as exc: if sock is not None: diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 116c08d6ff7fdd..0f0c30496ad340 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -589,7 +589,10 @@ class _SelectorTransport(transports._FlowControlMixin, def __init__(self, loop, sock, protocol, extra=None, server=None): super().__init__(extra, loop) self._extra['socket'] = sock - self._extra['sockname'] = sock.getsockname() + try: + self._extra['sockname'] = sock.getsockname() + except OSError: + self._extra['sockname'] = None if 'peername' not in self._extra: try: self._extra['peername'] = sock.getpeername() @@ -979,9 +982,11 @@ def sendto(self, data, addr=None): if not data: return - if self._address and addr not in (None, self._address): - raise ValueError( - f'Invalid address: must be None or {self._address}') + if self._address: + if addr not in (None, self._address): + raise ValueError( + f'Invalid address: must be None or {self._address}') + addr = self._address if self._conn_lost and self._address: if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES: @@ -992,7 +997,7 @@ def sendto(self, data, addr=None): if not self._buffer: # Attempt to send it right away first. try: - if self._address: + if self._extra['peername']: self._sock.send(data) else: self._sock.sendto(data, addr) @@ -1015,7 +1020,7 @@ def _sendto_ready(self): while self._buffer: data, addr = self._buffer.popleft() try: - if self._address: + if self._extra['peername']: self._sock.send(data) else: self._sock.sendto(data, addr) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index e333950e1e1c81..08f4dea2ecf959 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -1485,6 +1485,23 @@ def test_create_datagram_endpoint_connect_err(self): self.assertRaises( OSError, self.loop.run_until_complete, coro) + def test_create_datagram_endpoint_allow_broadcast(self): + protocol = MyDatagramProto(create_future=True, loop=self.loop) + self.loop.sock_connect = sock_connect = mock.Mock() + sock_connect.return_value = [] + + coro = self.loop.create_datagram_endpoint( + lambda: protocol, + remote_addr=('127.0.0.1', 0), + allow_broadcast=True) + + transport, _ = self.loop.run_until_complete(coro) + self.assertFalse(sock_connect.called) + + transport.close() + self.loop.run_until_complete(protocol.done) + self.assertEqual('CLOSED', protocol.state) + @patch_socket def test_create_datagram_endpoint_socket_err(self, m_socket): m_socket.getaddrinfo = socket.getaddrinfo diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index d380aa4138f8e9..b515b71a2ee117 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -1472,6 +1472,7 @@ def setUp(self): self.sock.fileno.return_value = 7 def datagram_transport(self, address=None): + self.sock.getpeername.side_effect = None if address else OSError transport = _SelectorDatagramTransport(self.loop, self.sock, self.protocol, address=address) diff --git a/Misc/NEWS.d/next/Library/2018-05-30-01-05-50.bpo-31922.fobsXJ.rst b/Misc/NEWS.d/next/Library/2018-05-30-01-05-50.bpo-31922.fobsXJ.rst new file mode 100644 index 00000000000000..df3881bffaaa38 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-30-01-05-50.bpo-31922.fobsXJ.rst @@ -0,0 +1,3 @@ +:meth:`asyncio.AbstractEventLoop.create_datagram_endpoint`: +Do not connect UDP socket when broadcast is allowed. +This allows to receive replies after a UDP broadcast.