Skip to content

Commit

Permalink
Use localhost as the default bind address (#719)
Browse files Browse the repository at this point in the history
  • Loading branch information
scaramallion authored Dec 21, 2021
1 parent ba60768 commit bc8fd99
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 27 deletions.
6 changes: 4 additions & 2 deletions docs/changelog/v2.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ The major breaking changes with the version 2.0 release are:
* SOP class variable names now use their `new DICOM UID keywords
<https://dicom.nema.org/medical/dicom/current/output/chtml/part06/chapter_A.html#table_A-1>`_
(for example, *VerificationSOPClass* becomes simply *Verification*)
* AE titles values should be :class:`str` rather than :class:`bytes` and will no
longer contain any trailing padding spaces
* AE titles values should be :class:`str` rather than :class:`bytes` and trailing
padding spaces are stripped from the raw value during decoding.


Fixes
Expand Down Expand Up @@ -86,3 +86,5 @@ Changes
:attr:`~pynetdicom.sop_class.Verification`
* All AE titles and DIMSE elements with a VR of **AE** should be set using an
ASCII :class:`str` rather than :class:`bytes`
* The default bind address used when making association requests changed to
``("localhost", 0)`` (:issue:`680`)
4 changes: 4 additions & 0 deletions pynetdicom/_globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,8 @@
STATUS_UNKNOWN: str = "Unknown"


# The default address that client sockets are bound to
BIND_ADDRESS = ("localhost", 0)


OptionalUIDType = Optional[Union[str, bytes, UID]]
14 changes: 8 additions & 6 deletions pynetdicom/ae.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
MODE_REQUESTOR,
DEFAULT_MAX_LENGTH,
DEFAULT_TRANSFER_SYNTAXES,
BIND_ADDRESS,
)


Expand Down Expand Up @@ -466,7 +467,7 @@ def associate(
ae_title: str = "ANY-SCP",
max_pdu: int = DEFAULT_MAX_LENGTH,
ext_neg: Optional[List[_UI]] = None,
bind_address: Tuple[str, int] = ("", 0),
bind_address: Tuple[str, int] = BIND_ADDRESS,
tls_args: Optional[Tuple[SSLContext, str]] = None,
evt_handlers: Optional[List[EventHandlerType]] = None,
) -> Association:
Expand All @@ -493,7 +494,8 @@ def associate(
.. versionchanged:: 2.0
`ae_title` should now be :class:`str`
* `ae_title` should now be :class:`str`
* The default `bind_address` was changed to ``("localhost", 0)``
Parameters
----------
Expand Down Expand Up @@ -524,7 +526,7 @@ def associate(
* :class:`~UserIdentityNegotiation` (0 or 1 item)
bind_address : 2-tuple, optional
The (host, port) to bind the association's communication socket
to, default ``('', 0)``.
to, default ``("localhost", 0)``.
tls_args : 2-tuple, optional
If TLS is required then this should be a 2-tuple containing a
(`ssl_context`, `server_hostname`), where `ssl_context` is the
Expand Down Expand Up @@ -1352,9 +1354,9 @@ def start_server(
Parameters
----------
address : 2-tuple
The (`host`, `port`) to use when listening for incoming association
requests.
address : Tuple[str, int]
The ``(host: str, port: int)`` to use when listening for incoming
association requests.
block : bool, optional
If ``True`` (default) then the server will be blocking, otherwise
it will start the server in a new thread and be non-blocking.
Expand Down
6 changes: 5 additions & 1 deletion pynetdicom/tests/test_ae.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,11 @@ def test_connection_timeout(self, caplog):
scu_ae.connection_timeout = 2
scu_ae.add_requested_context(Verification)
with caplog.at_level(logging.ERROR, logger="pynetdicom"):
assoc = scu_ae.associate("example.com", 11112)
assoc = scu_ae.associate(
"example.com",
11112,
bind_address=("", 0),
)
assert not assoc.is_established
assert assoc.is_aborted
msgs = [
Expand Down
2 changes: 1 addition & 1 deletion pynetdicom/tests/test_fsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def get_associate(self, assoc_type):
# Called AE Title is the destination DICOM AE title
primitive.called_ae_title = "REMOTE_AE_TITLE "
# The TCP/IP address of the source, pynetdicom includes port too
primitive.calling_presentation_address = ("", 0)
primitive.calling_presentation_address = ("localhost", 0)
# The TCP/IP address of the destination, pynetdicom includes port too
primitive.called_presentation_address = ("localhost", 11112)
# Proposed presentation contexts
Expand Down
38 changes: 21 additions & 17 deletions pynetdicom/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)

from pynetdicom import evt, _config
from pynetdicom._globals import MODE_ACCEPTOR
from pynetdicom._globals import MODE_ACCEPTOR, BIND_ADDRESS
from pynetdicom._handlers import (
standard_dimse_recv_handler,
standard_dimse_sent_handler,
Expand Down Expand Up @@ -74,10 +74,14 @@ def __init__(
self,
assoc: "Association",
client_socket: Optional[socket.socket] = None,
address: Tuple[str, int] = ("", 0),
address: Tuple[str, int] = BIND_ADDRESS,
) -> None:
"""Create a new :class:`AssociationSocket`.
.. versionchanged:: 2.0
The default for *address* was changed to ``("localhost", 0)``
Parameters
----------
assoc : association.Association
Expand All @@ -89,11 +93,11 @@ def __init__(
address : 2-tuple, optional
If *client_socket* is ``None`` then this is the ``(host, port)`` to
bind the newly created socket to, which by default will be
``('', 0)``.
``('localhost', 0)``.
"""
self._assoc = assoc

if client_socket is not None and address != ("", 0):
if client_socket is not None and address != BIND_ADDRESS:
LOGGER.warning(
"AssociationSocket instantiated with both a 'client_socket' "
"and bind 'address'. The original socket will not be rebound"
Expand Down Expand Up @@ -156,7 +160,7 @@ def connect(self, address: Tuple[str, int]) -> None:
Parameters
----------
address : 2-tuple
The ``(host, port)`` IPv4 address to connect to.
The ``(host: str, port: int)`` IPv4 address to connect to.
"""
if self.socket is None:
self.socket = self._create_socket()
Expand Down Expand Up @@ -205,7 +209,7 @@ def connect(self, address: Tuple[str, int]) -> None:
finally:
self._ready.set()

def _create_socket(self, address: Tuple[str, int] = ("", 0)) -> socket.socket:
def _create_socket(self, address: Tuple[str, int] = BIND_ADDRESS) -> socket.socket:
"""Create a new IPv4 TCP socket and set it up for use.
*Socket Options*
Expand All @@ -217,8 +221,8 @@ def _create_socket(self, address: Tuple[str, int] = ("", 0)) -> socket.socket:
Parameters
----------
address : 2-tuple, optional
The ``(host, port)`` to bind the socket to. By default the socket
is bound to ``('', 0)``, i.e. the first available port.
The ``(host: str, port: int)`` to bind the socket to. By default the socket
is bound to ``("localhost", 0)``, i.e. the first available port.
Returns
-------
Expand Down Expand Up @@ -263,16 +267,16 @@ def get_local_addr(self, host: Tuple[str, int] = ("10.255.255.255", 1)) -> str:
Parameters
----------
host : str
The host's (*addr*, *port*) when trying to determine the local
host : Tuple[str, int]
The host's ``(addr: str, port: int)`` when trying to determine the local
address.
"""
# Solution from https://stackoverflow.com/a/28950776
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as temp:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
try:
# We use `host` to allow unit testing
temp.connect(host)
addr: str = temp.getsockname()[0]
sock.connect(host)
addr: str = sock.getsockname()[0]
except:
addr = "127.0.0.1"

Expand Down Expand Up @@ -530,8 +534,8 @@ class AssociationServer(TCPServer):
The parent AE that is running the server.
request_queue_size : int
Default ``5``.
server_address : 2-tuple
The ``(host, port)`` that the server is running on.
server_address : Tuple[str, int]
The ``(host: str, port: int)`` that the server is running on.
"""

def __init__(
Expand All @@ -551,8 +555,8 @@ def __init__(
----------
ae : ae.ApplicationEntity
The parent AE that's running the server.
address : 2-tuple
The ``(host, port)`` that the server should run on.
address : Tuple[str, int]
The ``(host: str, port: int)`` that the server should run on.
ae_title : str
The AE title of the SCP.
contexts : list of presentation.PresentationContext
Expand Down

0 comments on commit bc8fd99

Please sign in to comment.