Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for open ports without jumping to the executor #4678

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions supervisor/addons/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
from ..jobs.const import JobExecutionLimit
from ..jobs.decorator import Job
from ..store.addon import AddonStore
from ..utils import check_port
from ..utils import async_check_port
from ..utils.apparmor import adjust_profile
from ..utils.json import read_json_file, write_json_file
from ..utils.sentry import capture_exception
Expand Down Expand Up @@ -539,7 +539,7 @@ async def watchdog_application(self) -> bool:

# TCP monitoring
if s_prefix == "tcp":
return await self.sys_run_in_executor(check_port, self.ip_address, port)
return await async_check_port(self.coresys.loop, self.ip_address, port)

# lookup the correct protocol from config
if t_proto:
Expand Down
1 change: 1 addition & 0 deletions supervisor/coresys.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(self):

# External objects
self._loop: asyncio.BaseEventLoop = asyncio.get_running_loop()
self.loop = self._loop
self._websession: aiohttp.ClientSession = aiohttp.ClientSession()

# Global objects
Expand Down
6 changes: 3 additions & 3 deletions supervisor/homeassistant/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from ..exceptions import HomeAssistantAPIError, HomeAssistantAuthError
from ..jobs.const import JobExecutionLimit
from ..jobs.decorator import Job
from ..utils import check_port, version_is_new_enough
from ..utils import async_check_port, version_is_new_enough
from .const import LANDINGPAGE

_LOGGER: logging.Logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -140,8 +140,8 @@ async def get_api_state(self) -> str | None:
return None

# Check if port is up
if not await self.sys_run_in_executor(
check_port,
if not await async_check_port(
self.coresys.loop,
self.sys_homeassistant.ip_address,
self.sys_homeassistant.api_port,
):
Expand Down
17 changes: 17 additions & 0 deletions supervisor/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ def check_port(address: IPv4Address, port: int) -> bool:
return False


async def async_check_port(
loop: asyncio.AbstractEventLoop, address: IPv4Address, port: int
) -> bool:
"""Check if port is mapped."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
try:
async with asyncio.timeout(0.5):
await loop.sock_connect(sock, (str(address), port))
except (OSError, TimeoutError):
return False
finally:
if sock is not None:
sock.close()
return True


def check_exception_chain(err: Exception, object_type: Any) -> bool:
"""Check if exception chain include sub exception.

Expand Down
15 changes: 14 additions & 1 deletion tests/utils/test_check_port.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Check ports."""
import asyncio
from ipaddress import ip_address

from supervisor.utils import check_port
from supervisor.utils import async_check_port, check_port


def test_exists_open_port():
Expand All @@ -12,3 +13,15 @@ def test_exists_open_port():
def test_not_exists_port():
"""Test a not exists network service."""
assert not check_port(ip_address("192.0.2.1"), 53)


async def test_async_exists_open_port():
"""Test a exists network port."""
assert await async_check_port(asyncio.get_running_loop(), ip_address("8.8.8.8"), 53)


async def test_async_not_exists_port():
"""Test a not exists network service."""
assert not await async_check_port(
asyncio.get_running_loop(), ip_address("192.0.2.1"), 53
)