This commit is contained in:
Kovid Goyal 2023-12-04 14:33:18 +05:30
commit 725adf696f
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 55 additions and 12 deletions

View File

@ -17,15 +17,23 @@ from calibre.utils.smtp import config as email_config
def local_url_for_content_server():
from calibre.srv.opts import server_config
from calibre.utils.network import is_ipv6_addr, get_fallback_server_addr
opts = server_config()
interface = opts.listen_on or '0.0.0.0'
interface = {'0.0.0.0': '127.0.0.1', '::':'::1'}.get(interface)
interface = opts.listen_on or get_fallback_server_addr()
addr_map = {'0.0.0.0': '127.0.0.1',
'::': '::1'}
if interface in addr_map:
interface = addr_map[interface]
protocol = 'https' if opts.ssl_certfile and opts.ssl_keyfile else 'http'
prefix = opts.url_prefix or ''
port = opts.port
if ':' in interface:
interface = f'[{interface}]'
return f'{protocol}://{interface}:{port}{prefix}'
addr = f'[{interface}]' if is_ipv6_addr(interface) else f'{interface}'
return f'{protocol}://{addr}:{port}{prefix}'
def open_in_browser():

View File

@ -1299,10 +1299,20 @@ class ConfigWidget(ConfigWidgetBase):
self.stopping_msg.accept()
def test_server(self):
from calibre.utils.network import is_ipv6_addr, get_fallback_server_addr
prefix = self.advanced_tab.get('url_prefix') or ''
protocol = 'https' if self.advanced_tab.has_ssl else 'http'
lo = self.advanced_tab.get('listen_on') or '0.0.0.0'
lo = {'0.0.0.0': '127.0.0.1', '::':'::1'}.get(lo)
lo = self.advanced_tab.get('listen_on') or get_fallback_server_addr()
addr_map = {'0.0.0.0': '127.0.0.1',
'::': '::1'}
if lo in addr_map:
lo = addr_map[lo]
if is_ipv6_addr(lo):
lo = f'[{lo}]'
url = '{protocol}://{interface}:{port}{prefix}'.format(
protocol=protocol, interface=lo,
port=self.main_tab.opt_port.value(), prefix=prefix)

View File

@ -29,6 +29,7 @@ from calibre.utils.localization import _
from calibre.utils.logging import ThreadSafeLog
from calibre.utils.mdns import get_external_ip
from calibre.utils.monotonic import monotonic
from calibre.utils.network import get_fallback_server_addr
from calibre.utils.socket_inheritance import set_socket_inherit
from polyglot.builtins import iteritems
from polyglot.queue import Empty, Full
@ -399,7 +400,7 @@ class ServerLoop:
ba = (self.opts.listen_on, int(self.opts.port))
if not ba[0]:
# AI_PASSIVE does not work with host of '' or None
ba = ('0.0.0.0', ba[1])
ba = (get_fallback_server_addr(), ba[1])
self.bind_address = ba
self.bound_address = None
self.connection_map = {}
@ -508,17 +509,23 @@ class ServerLoop:
self.setup_socket()
def serve(self):
from calibre.utils.network import is_ipv6_addr
self.connection_map = {}
if not self.socket_was_preactivated:
self.socket.listen(min(socket.SOMAXCONN, 128))
self.bound_address = ba = self.socket.getsockname()
if isinstance(ba, tuple):
ba = ':'.join(map(str, ba))
if is_ipv6_addr(ba[0]):
addr = f'[{ba[0]}]'
else:
addr = f'{ba[0]}'
ba_str = f'{addr}:' + ':'.join(map(str, ba[1:]))
self.pool.start()
with TemporaryDirectory(prefix='srv-') as tdir:
self.tdir = tdir
if self.LISTENING_MSG:
self.log(self.LISTENING_MSG, ba)
self.log(self.LISTENING_MSG, ba_str)
self.plugin_pool.start()
self.ready = True

View File

@ -110,8 +110,8 @@ raw_options = (
' there are more than this number of items. Set to zero to disable.'),
_('The interface on which to listen for connections'),
'listen_on', '0.0.0.0',
_('The default is to listen on all available IPv4 interfaces. You can change this to, for'
'listen_on', None,
_('The default is to listen on all available IPv6 and IPv4 interfaces. You can change this to, for'
' example, "127.0.0.1" to only listen for connections from the local machine, or'
' to "::" to listen to all incoming IPv6 and IPv4 connections.'),

View File

@ -77,6 +77,9 @@ def _get_external_ip():
def verify_ipV4_address(ip_address):
if ip_address == None:
return None
result = None
if ip_address != '0.0.0.0' and ip_address != '::':
# do some more sanity checks on the address

View File

@ -108,3 +108,18 @@ def internet_connected():
DummyNetworkStatus()
return internet_connected.checker()
def is_ipv6_addr(addr):
import socket
try:
socket.inet_pton(socket.AF_INET6, addr)
return True
except OSError:
return False
def get_fallback_server_addr():
from socket import has_dualstack_ipv6
if has_dualstack_ipv6():
return '::'
else:
return '0.0.0.0'