From d663fe354b12dd314935469e7f7c946f21ea6248 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:54:18 +1100 Subject: [PATCH] Improve listen address config in Setup Wizard #1016 - Also make 127.0.0.1 the default listen address - Fallback to 0.0.0.0 if no other local address works --- .../kodion/context/xbmc/xbmc_context.py | 2 + .../youtube_plugin/kodion/network/__init__.py | 2 + .../kodion/network/http_server.py | 72 +++++++++++++------ .../youtube_plugin/kodion/script_actions.py | 27 +++---- .../youtube/helper/yt_setup_wizard.py | 17 ++++- resources/settings.xml | 2 +- 6 files changed, 78 insertions(+), 44 deletions(-) diff --git a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py index 338b156f0..02c461fd7 100644 --- a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py +++ b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py @@ -134,7 +134,9 @@ class XbmcContext(AbstractContext): 'history.remove': 15015, 'history.reset.resume_point': 30674, 'home': 10000, + 'httpd': 30628, 'httpd.not.running': 30699, + 'httpd.connect.failed': 1001, 'inputstreamhelper.is_installed': 30625, 'isa.enable.check': 30579, 'key.requirement': 30731, diff --git a/resources/lib/youtube_plugin/kodion/network/__init__.py b/resources/lib/youtube_plugin/kodion/network/__init__.py index a874ee93d..5c241deb9 100644 --- a/resources/lib/youtube_plugin/kodion/network/__init__.py +++ b/resources/lib/youtube_plugin/kodion/network/__init__.py @@ -13,6 +13,7 @@ get_client_ip_address, get_connect_address, get_http_server, + get_listen_addresses, httpd_status, ) from .ip_api import Locator @@ -23,6 +24,7 @@ 'get_client_ip_address', 'get_connect_address', 'get_http_server', + 'get_listen_addresses', 'httpd_status', 'BaseRequestsClass', 'InvalidJSONError', diff --git a/resources/lib/youtube_plugin/kodion/network/http_server.py b/resources/lib/youtube_plugin/kodion/network/http_server.py index c0fa89aa8..1bea41879 100644 --- a/resources/lib/youtube_plugin/kodion/network/http_server.py +++ b/resources/lib/youtube_plugin/kodion/network/http_server.py @@ -73,25 +73,30 @@ def __init__(self, *args, **kwargs): self.whitelist_ips = self._context.get_settings().httpd_whitelist() super(RequestHandler, self).__init__(*args, **kwargs) - def connection_allowed(self, method): - client_ip = self.client_address[0] - is_whitelisted = client_ip in self.whitelist_ips - conn_allowed = is_whitelisted + def ip_address_status(self, ip_address): + is_whitelisted = ip_address in self.whitelist_ips + ip_allowed = is_whitelisted - if not conn_allowed: - octets = validate_ip_address(client_ip) + if not ip_allowed: + octets = validate_ip_address(ip_address) for ip_range in self.local_ranges: if ((any(octets) and isinstance(ip_range, tuple) and ip_range[0] <= octets <= ip_range[1]) - or client_ip == ip_range): - in_local_range = True - conn_allowed = True + or ip_address == ip_range): + is_local = True + ip_allowed = True break else: - in_local_range = False + is_local = False else: - in_local_range = 'Undetermined' + is_local = None + + return ip_allowed, is_local, is_whitelisted + + def connection_allowed(self, method): + client_ip = self.client_address[0] + ip_allowed, is_local, is_whitelisted = self.ip_address_status(client_ip) path_parts = urlsplit(self.path) if path_parts.query: @@ -129,17 +134,19 @@ def connection_allowed(self, method): '\n\tParams: |{params}|' '\n\tAddress: |{client_ip}|' '\n\tWhitelisted: {is_whitelisted}' - '\n\tLocal range: {in_local_range}' + '\n\tLocal range: {is_local}' '\n\tStatus: {status}' .format(method=method, path=path['path'], params=path['log_params'], client_ip=client_ip, is_whitelisted=is_whitelisted, - in_local_range=in_local_range, - status='Allowed' if conn_allowed else 'Blocked')) + is_local=('Undetermined' + if is_local is None else + is_local), + status='Allowed' if ip_allowed else 'Blocked')) self._context.log_debug(msg) - return conn_allowed, path + return ip_allowed, path # noinspection PyPep8Naming def do_GET(self): @@ -644,8 +651,8 @@ def get_http_server(address, port, context): return None -def httpd_status(context): - netloc = get_connect_address(context, as_netloc=True) +def httpd_status(context, address=None): + netloc = get_connect_address(context, as_netloc=True, address=address) url = urlunsplit(( 'http', netloc, @@ -687,10 +694,13 @@ def get_client_ip_address(context): return ip_address -def get_connect_address(context, as_netloc=False): - settings = context.get_settings() - listen_address = settings.httpd_listen() - listen_port = settings.httpd_port() +def get_connect_address(context, as_netloc=False, address=None): + if address is None: + settings = context.get_settings() + listen_address = settings.httpd_listen() + listen_port = settings.httpd_port() + else: + listen_address, listen_port = address try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -734,3 +744,23 @@ def get_connect_address(context, as_netloc=False): if as_netloc: return ':'.join((connect_address, str(listen_port))) return listen_address, listen_port + + +def get_listen_addresses(): + local_ranges = ( + ((10, 0, 0, 0), (10, 255, 255, 255)), + ((172, 16, 0, 0), (172, 31, 255, 255)), + ((192, 168, 0, 0), (192, 168, 255, 255)), + ) + addresses = ['127.0.0.1', xbmc.getIPAddress()] + for interface in socket.getaddrinfo(socket.gethostname(), None): + address = interface[4][0] + if interface[0] != socket.AF_INET or address in addresses: + continue + octets = validate_ip_address(address) + if not any(octets): + continue + if any(lo <= octets <= hi for lo, hi in local_ranges): + addresses.append(address) + addresses.append('0.0.0.0') + return addresses diff --git a/resources/lib/youtube_plugin/kodion/script_actions.py b/resources/lib/youtube_plugin/kodion/script_actions.py index f1f4eecd8..122239365 100644 --- a/resources/lib/youtube_plugin/kodion/script_actions.py +++ b/resources/lib/youtube_plugin/kodion/script_actions.py @@ -10,7 +10,6 @@ from __future__ import absolute_import, division, unicode_literals import os -import socket from .compatibility import parse_qsl, urlsplit, xbmc, xbmcaddon, xbmcvfs from .constants import ( @@ -23,8 +22,13 @@ WAIT_END_FLAG, ) from .context import XbmcContext -from .network import Locator, get_client_ip_address, httpd_status -from .utils import rm_dir, validate_ip_address +from .network import ( + Locator, + get_client_ip_address, + get_listen_addresses, + httpd_status, +) +from .utils import rm_dir from ..youtube import Provider @@ -100,22 +104,7 @@ def _config_actions(context, action, *_args): settings.set_subtitle_download(result == 1) elif action == 'listen_ip': - local_ranges = ( - ((10, 0, 0, 0), (10, 255, 255, 255)), - ((172, 16, 0, 0), (172, 31, 255, 255)), - ((192, 168, 0, 0), (192, 168, 255, 255)), - ) - addresses = [xbmc.getIPAddress()] - for interface in socket.getaddrinfo(socket.gethostname(), None): - address = interface[4][0] - if interface[0] != socket.AF_INET or address in addresses: - continue - octets = validate_ip_address(address) - if not any(octets): - continue - if any(lo <= octets <= hi for lo, hi in local_ranges): - addresses.append(address) - addresses += ['127.0.0.1', '0.0.0.0'] + addresses = get_listen_addresses() selected_address = ui.on_select(localize('select.listen.ip'), addresses) if selected_address != -1: settings.httpd_listen(addresses[selected_address]) diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py b/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py index c0c1cbdfc..1530c7dfe 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py @@ -14,7 +14,7 @@ from ...kodion.compatibility import urlencode, xbmcvfs from ...kodion.constants import ADDON_ID, DATA_PATH, WAIT_END_FLAG -from ...kodion.network import httpd_status +from ...kodion.network import httpd_status, get_listen_addresses from ...kodion.sql_store import PlaybackHistory, SearchHistory from ...kodion.utils import to_unicode from ...kodion.utils.datetime_parser import strptime @@ -72,9 +72,10 @@ def process_geo_location(context, step, steps, **_kwargs): def process_default_settings(context, step, steps, **_kwargs): localize = context.localize settings = context.get_settings() + ui = context.get_ui() step += 1 - if context.get_ui().on_yes_no_input( + if ui.on_yes_no_input( '{youtube} - {setup_wizard} ({step}/{steps})'.format( youtube=localize('youtube'), setup_wizard=localize('setup_wizard'), @@ -100,7 +101,17 @@ def process_default_settings(context, step, steps, **_kwargs): if settings.cache_size() < 20: settings.cache_size(20) if not httpd_status(context): - settings.httpd_listen('0.0.0.0') + port = settings.httpd_port() + for address in get_listen_addresses(): + if httpd_status(context, (address, port)): + settings.httpd_listen(address) + break + else: + ui.show_notification( + localize('httpd.connect.failed'), + header=localize('httpd'), + ) + settings.httpd_listen('0.0.0.0') return step diff --git a/resources/settings.xml b/resources/settings.xml index 27ba61dd1..9e4d71279 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -1084,7 +1084,7 @@ 0 - 0.0.0.0 + 127.0.0.1 30643