Open pccavar opened 6 months ago
I just tested one of my own scripts and did not get any errors. I would need a bit more information to help you.
Which server are you trying to connect with? Do you want to share the script that causes the error?
I actually changed something related to TLS a while ago, because the PROTOCOL_TLS
constant was deprecated in Python 3.10: https://github.com/kinnay/anynet/commit/e8efe0064726cc40a81e2f2eb2286f4b55481c70. This should not cause any issues with NEX however.
Thank you for your prompt response and willingness to help. Here is the excerpt from my script where the connection is attempted to Game Builder Garage's server:
# coding:utf-8
# https://github.com/kinnay/NintendoClients/blob/master/examples/switch/gamebuilder.py
from nintendo.switch import dauth, aauth, baas, dragons
from nintendo import switch
from nintendo.nex import backend, settings, authentication, datastore, streams, common
from anynet import http
import anyio
from anynet import tls
from pprint import pprint
import sys
class GBG:
TITLE_ID = 0x0100FA5010788000
LATEST_VERSION = 0x10000
GAME_SERVER_ID = 0x2E99DD00
ACCESS_KEY = "97b08aad"
NEX_VERSION = 40610
CLIENT_VERSION = 2
HOST = "g%08x-lp1.s.n.srv.nintendo.net" %GBG.GAME_SERVER_ID
PORT = 443
async def main():
s = settings.load("switch")
s.configure(GBG.ACCESS_KEY, GBG.NEX_VERSION, GBG.CLIENT_VERSION)
async with backend.connect(s, HOST, PORT) as be:
print("Connected")
sys.exit()
anyio.run(main)
In my environment, executing this code results in an SSLV3_ALERT_HANDSHAKE_FAILURE error.
I get the same error. One thing that's interesting is that the certificate of the GBG server is signed by Nintendo Class 2 CA - G3
instead of Amazon.
Here is a minimal reproducible example:
>>> import socket
>>> import ssl
>>> s = ssl.wrap_socket(socket.socket())
>>> s.connect(("g2e99dd00-lp1.s.n.srv.nintendo.net", 443))
Traceback (most recent call last):
File "<pyshell#33>", line 1, in <module>
s.connect(("g2e99dd00-lp1.s.n.srv.nintendo.net", 443))
File "/usr/lib/python3.10/ssl.py", line 1375, in connect
self._real_connect(addr, False)
File "/usr/lib/python3.10/ssl.py", line 1366, in _real_connect
self.do_handshake()
File "/usr/lib/python3.10/ssl.py", line 1342, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1007)
The issue seems to affect the GBG server specifically. If you replace the game server id by g22306d00
(the AC:NH server) then it works fine.
My guess is that Nintendo has put a proxy in front of the GBG server, which might have an outdated TLS implementation. I found out that it works if we manually set the cipher to AES256-GCM-SHA384
on the TLS context.
I'm not sure how we can implement this in NintendoClients properly. Maybe we should add a parameter that specifies the TLS context to backend.connect
.
For now, here is a workaround:
from nintendo.nex import backend, settings
from anynet import tls
import anyio
import contextlib
import pkg_resources
import ssl
import sys
class GBG:
TITLE_ID = 0x0100FA5010788000
LATEST_VERSION = 0x10000
GAME_SERVER_ID = 0x2E99DD00
ACCESS_KEY = "97b08aad"
NEX_VERSION = 40610
CLIENT_VERSION = 2
HOST = "g%08x-lp1.s.n.srv.nintendo.net" %GBG.GAME_SERVER_ID
PORT = 443
CA = pkg_resources.resource_filename("nintendo", "files/cert/CACERT_NINTENDO_CLASS2_CA_G3.der")
@contextlib.asynccontextmanager
async def connect_patched(host, port, context=None):
with open(CA, "rb") as f:
ca = f.read()
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations(cadata=ca)
context.set_ciphers("AES256-GCM-SHA384")
async with await anyio.connect_tcp(host, port, ssl_context=context, tls_standard_compatible=False) as stream:
yield tls.TLSClient(stream)
tls.connect = connect_patched
async def main():
s = settings.load("switch")
s.configure(GBG.ACCESS_KEY, GBG.NEX_VERSION, GBG.CLIENT_VERSION)
async with backend.connect(s, HOST, PORT) as be:
print("Connected")
anyio.run(main)
On my machine, this works.
Thank you so much for your invaluable help. Your provided workaround has successfully resolved the SSL handshake issue, and I greatly appreciate your quick and effective support.
To ensure that the changes made for the workaround don't affect other sessions, I have implemented a host-specific separation in the connection code. Here is the modified part of the code:
N_CA = pkg_resources.resource_filename("nintendo", "files/cert/CACERT_NINTENDO_CLASS2_CA_G3.der")
@contextlib.asynccontextmanager
async def connect_patched(host, port, context=None):
if host != HOST:
# Existing code for other hosts
logger.debug("Connecting TCP/TLS client to %s:%s", host, port)
if context:
context = context.get(False)
async with await anyio.connect_tcp(host, port, ssl_context=context, tls_standard_compatible=False) as stream:
yield tls.TLSClient(stream)
else:
# Workaround for the GBG server
with open(N_CA, "rb") as f:
ca = f.read()
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations(cadata=ca)
context.set_ciphers("AES256-GCM-SHA384")
async with await anyio.connect_tcp(host, port, ssl_context=context, tls_standard_compatible=False) as stream:
yield tls.TLSClient(stream)
tls.connect = connect_patched
I have encountered an issue with a script that was functioning correctly until recently. The error has started occurring both locally and on the server simultaneously. I suspect there might have been some changes on the host side that could be causing this problem. Can you provide insights into any modifications that might have been made recently?
Error Traceback:
Additionally, I would like to inquire if anyone else has experienced a similar error recently. Any information or assistance regarding this issue would be greatly appreciated.