Radicale
Radicale copied to clipboard
[Windows 7] Fails when using an IPv4 address directly
radicale version: 3.1.8
If I define a host using an IPv4 address like so 0.0.0.0:9999
it fails with this message:
[2022-10-28 23:32:51 +0300] [3144] [INFO] Loaded default config
[2022-10-28 23:32:51 +0300] [3144] [INFO] Loaded config file 'masked'
[2022-10-28 23:32:51 +0300] [3144] [INFO] Starting Radicale
[2022-10-28 23:32:51 +0300] [3144] [INFO] auth type is 'radicale.auth.htpasswd'
[2022-10-28 23:32:51 +0300] [3144] [INFO] storage type is 'radicale.storage.multifilesystem'
[2022-10-28 23:32:51 +0300] [3144] [INFO] rights type is 'radicale.rights.owner_only'
[2022-10-28 23:32:51 +0300] [3144] [INFO] web type is 'radicale.web.none'
[2022-10-28 23:32:51 +0300] [3144] [INFO] Listening on '[0.0.0.0]:9999' with SSL
[2022-10-28 23:32:51 +0300] [3144] [CRITICAL] An exception occurred during server startup: Failed to start server '[0.0.0.0]:9999': [Errno 11004] getaddrinfo failed
Traceback (most recent call last):
File "**masked**\site-packages\radicale\server.py", line 289, in serve
server = server_class(configuration, family, address,
File "**masked**\site-packages\radicale\server.py", line 84, in __init__
super().__init__(address, RequestHandlerClass)
File "C:\Program Files\Python38\lib\socketserver.py", line 452, in __init__
self.server_bind()
File "**masked**\site-packages\radicale\server.py", line 157, in server_bind
super().server_bind()
File "**masked**\site-packages\radicale\server.py", line 92, in server_bind
super().server_bind()
File "C:\Program Files\Python38\lib\wsgiref\simple_server.py", line 50, in server_bind
HTTPServer.server_bind(self)
File "C:\Program Files\Python38\lib\http\server.py", line 138, in server_bind
socketserver.TCPServer.server_bind(self)
File "C:\Program Files\Python38\lib\socketserver.py", line 466, in server_bind
self.socket.bind(self.server_address)
socket.gaierror: [Errno 11004] getaddrinfo failed
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "**masked**\site-packages\radicale\__main__.py", line 198, in run
server.serve(configuration, shutdown_socket_out)
File "**masked**\site-packages\radicale\server.py", line 313, in serve
raise RuntimeError("Failed to start server %r: %s" % (
RuntimeError: Failed to start server '[0.0.0.0]:9999': [Errno 11004] getaddrinfo failed
I traced this into server.py->serve()
. The logic there tries for each host to create one server for IPv4 and another one for IPv6. Obviously if the "host" is an address and not a hostname one of the two servers might fail. There is some logic in that function that tries to detect this IPv6 failure and proceed if the IPv4 server succeeded.
This error happens on Windows 10 also but it is caught by this condition https://github.com/Kozea/Radicale/blob/78c7225e8d5f805c23d90517736aa6c5b0f32860/radicale/server.py#L297
However, on Windows 7 the errno is slightly different. It is 11001
.
I spent many hours to find where this 11004
comes from on Windows 7. I couldn't find anything on python docs. I tried testing it against some other EAI_* constants but I didn't find a match.
The best result I got is that this constant matches WSANO_DATA. However, I couldn't find a way to test for this from python code.
In case you can't find a more suitable solution I would suggest introducing some logic that will detect if the host is an address and only attempt to create the server type that matches it (IPv4/IPv6).
DIRTY WORKAROUND FOR USERS
- Locate where pip has installed radicale. On Windows 7 it seems to be in this path
C:\Users\<username>\AppData\Roaming\Python\<pythonversion>\site-packages\radicale
- Open the
server.py
file in an editor. - Locate inside the
serve()
the line that readspossible_families = (socket.AF_INET, socket.AF_INET6)
- Change the line to this:
possible_families = (socket.AF_INET,) # the comma makes it a tuple
If I define a host using an IPv4 address like so
0.0.0.0:9999
it fails
That's not an IPv4 address. 0.0.0.0
is an IPv4 address, and 9999
is a port number.
That's not an IPv4 address. 0.0.0.0 is an IPv4 address, and 9999 is a port number.
You're being pedantic and it isn't very helpful. In fact I said that I define the host that way. Aka with an IPv4 address which is correct. The host setting consists of an address and a port number.
Yes I was being pedantic because sometimes that is helpful for resolving bugs.
Perhaps rename the issue title to not claim that it is about "using an IPv4 address directly" if in fact it is about something else?
No, you need not again point fingers at me for being too stupid to understand what you are saying - instead consider that others might be less stupid than you as well, and it might be beneficial for you to communicate simpler to get understood by those wanting to help solve this issue.
It is about using IPv4 address directly.
This localhost:9999
works.
This 127.0.0.1:9999
doesn't work.
according to examples it should work:
[server]
# CalDAV server hostnames separated by a comma
# IPv4 syntax: address:port
# IPv6 syntax: [address]:port
# For example: 0.0.0.0:9999, [::]:9999
can you confirm that startup is working in case using bind to IPv4 localhost, only for testing:
hosts = 127.0.0.1:9999
If also not supported, then this sounds like a common problem (btw. also in some C programs) that one has to specify for the server bind explicitly the family, which you did in your "dirty" workaround.
Haven't tested "radicale" on Linux on an IPv4-only system, but can be the case that even here it will not work, depending on sysctl settings.
some options for permanently solving that issue
- autodetect given IP address and specify protocol or add an additional option in config to control the protocols to bind to like e.g. in postfix: "inet_protocols = all|ipv4|ipv6"
- catch error and try IPv4-only or IPv6-only
run test cases on
- IPv4 only systems
- IPv6 only systems
- dualstack
It is about using IPv4 address directly. This
localhost:9999
works. This127.0.0.1:9999
doesn't work.
that is working definitly on Linux with python 3.8 ona dualstack system. Question would by why python is calling getaddrinfo for an IPv4 address at all...one has to investigate code in "socket.py" - sounds to me more a bug than a feature...potentially caused by underlying implementation in Windows.
@pbiering
I think the code below is a simplification of how radicale tries to initialize the server:
import socketserver
import socket
class MyHandler(socketserver.BaseRequestHandler):
def handle(self):
pass
class DummyServer(socketserver.TCPServer):
address_family = socket.AF_INET6
try:
with DummyServer(('127.0.0.1', 5854), MyHandler) as server:
server.serve_forever()
except OSError as e:
# On Windows 10
# e.errno == socket.EAI_NONAME == 11001
# On Windows 7
# e.errno == 11004
raise e
catch error and try IPv4-only or IPv6-only
As I mentioned in my original post radicale already has logic for this inside the serve()
function in server.py. I have a link to the relevant code section.
However, the detection isn't perfect. Judging by the long if-tests in there, different platforms seem to throw different errors.
This can be related to https://github.com/Kozea/Radicale/issues/1313, which was solved finally by reworking the server socket listen code, can you please test 3.1 "latest"?
I no longer use Windows 7.