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

F@st3890:aiohttp.client_exceptions.ServerDisconnectedError while get DeviceInfo #252

Closed
done7k opened this issue Oct 12, 2023 · 5 comments
Closed
Labels
bug Something isn't working

Comments

@done7k
Copy link

done7k commented Oct 12, 2023

Versions

Key Value
Package version 1.0.8
Python Version 3.11
F@st Router Model 3890V3
"InternalFirmwareVersion" sw18.76.10.13c-9

Describe the bug

I am experiencing some issues with 'Sagemcom' integration on Home Assistance. And decided to check how the sagemcom_api on my local machine. I got the python script from the example and can see the issue is the same.

The python script fails with error (in most of cases) : aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected

It happens when the script runs Device/DeviceInfo command. Right before it successfully completes Login. I have compared payloads for the post request with the one my browser makes when I run $.xmo.getValuesTree("Device/DeviceInfo") and found a difference with options value only. So, I changed a bit the sagemcom_api to populate the value and now it is totally the same but in case of browser I always get a response but the script returned some values ONLY once after 100 attempts.. usually -- Server Disconnected error this is payload of POST request

api_host=http://192.168.55.44/cgi/json-req payload={'request': {'id': 1, 'session-id': '1984664181', 'priority': False, 'actions': [{'id': 0, 'method': 'getValue', 'xpath': 'Device/DeviceInfo', 'options': {'capability-flags': {'interface': 'true'}}}], 'cnonce': 0, 'auth-key': '51c050b5adf965d7c94234589723409587230489431fd51754a2e0f3de0b165c88adfb221ffb93292ff1340819034819328413284012384e0e'}}

What else I can check / verify / etc ?

BTW, I have the F@st3890V3 router but I use SHA512 hashing, the MD5 does not work at all in my case, i can see the corresponding error msg in router log..
BTW2, version is still '1.0.1'

[bug]

@done7k
Copy link
Author

done7k commented Oct 13, 2023

Tried to simplify the test and do

  • logon, - successful. and then right after do
  • logout - fails with the Server disconnected error

the payload:
{'request': {'id': 1, 'session-id': 1888647817, 'priority': False, 'actions': [{'id': 0, 'method': 'logOut'}], 'cnonce': 3869011131, 'auth-key': '321230487120394710923847019237409128734092b93558b8929e5bef42e8dced9de1118ab1efdd92e4f166ffab7f8bac2cecac7e32459083204958239045839042850958499'}}

@iMicknl iMicknl added the bug Something isn't working label Jan 7, 2024
@bakonyiferenc
Copy link
Contributor

I had similar issue with Telekom FAST3896_MAGYAR (sw18.83.17.20e). client.logon() is successful, subsequent client.get_device_info() fails with aiohttp.client_exceptions.ClientOSError: [Errno 104] Connection reset by peer

Packet capture shows that aiohttp tries to reuse the TCP connection to send more than one HTTP request but the router don't like that and responds with RST:

16:24:15.631198 IP 192.168.0.26.42554 > 192.168.0.1.80: Flags [S], seq 3423351291, win 64240, options [mss 1460,sackOK,TS val 2923641018 ecr 0,nop,wscale 10], length 0
16:24:15.635341 IP 192.168.0.1.80 > 192.168.0.26.42554: Flags [S.], seq 1823334284, ack 3423351292, win 64240, options [mss 1460,nop,nop,sackOK,nop,wscale 5], length 0
16:24:15.635415 IP 192.168.0.26.42554 > 192.168.0.1.80: Flags [.], ack 1, win 63, length 0
16:24:15.635888 IP 192.168.0.26.42554 > 192.168.0.1.80: Flags [P.], seq 1:196, ack 1, win 63, length 195: HTTP: POST /cgi/json-req HTTP/1.1
16:24:15.636036 IP 192.168.0.26.42554 > 192.168.0.1.80: Flags [P.], seq 196:1197, ack 1, win 63, length 1001: HTTP
16:24:15.637353 IP 192.168.0.1.80 > 192.168.0.26.42554: Flags [.], ack 196, win 2003, length 0
16:24:15.637610 IP 192.168.0.1.80 > 192.168.0.26.42554: Flags [.], ack 1197, win 2002, length 0
16:24:15.647999 IP 192.168.0.1.80 > 192.168.0.26.42554: Flags [P.], seq 1:666, ack 1197, win 2003, length 665: HTTP: HTTP/1.1 200 OK
16:24:15.648009 IP 192.168.0.26.42554 > 192.168.0.1.80: Flags [.], ack 666, win 63, length 0
16:24:15.648746 IP 192.168.0.26.42554 > 192.168.0.1.80: Flags [P.], seq 1197:1391, ack 666, win 63, length 194: HTTP: POST /cgi/json-req HTTP/1.1
16:24:15.648812 IP 192.168.0.26.42554 > 192.168.0.1.80: Flags [P.], seq 1391:1812, ack 666, win 63, length 421: HTTP
16:24:15.650024 IP 192.168.0.1.80 > 192.168.0.26.42554: Flags [.], ack 1391, win 2003, length 0
16:24:15.650301 IP 192.168.0.1.80 > 192.168.0.26.42554: Flags [.], ack 1812, win 2003, length 0
16:24:15.654131 IP 192.168.0.1.80 > 192.168.0.26.42554: Flags [R.], seq 666, ack 1812, win 2003, length 0

To avoid connection reuse I added force_close=True to TCPConnector like this:

--- a/sagemcom_api/client.py
+++ b/sagemcom_api/client.py
@@ -85,7 +85,7 @@ class SagemcomClient:
             else ClientSession(
                 headers={"User-Agent": f"{DEFAULT_USER_AGENT}"},
                 timeout=ClientTimeout(DEFAULT_TIMEOUT),
-                connector=TCPConnector(verify_ssl=verify_ssl if verify_ssl else True),
+                connector=TCPConnector(verify_ssl=verify_ssl if verify_ssl else True, force_close=True),
             )
         )
 

The result is a race between the FIN of the 1st TCP session and the SYN of the 2nd one. The router still does not like it:

16:37:49.643582 IP 192.168.0.26.46218 > 192.168.0.1.80: Flags [S], seq 1459675364, win 64240, options [mss 1460,sackOK,TS val 2924455030 ecr 0,nop,wscale 10], length 0
16:37:49.646100 IP 192.168.0.1.80 > 192.168.0.26.46218: Flags [S.], seq 525479786, ack 1459675365, win 64240, options [mss 1460,nop,nop,sackOK,nop,wscale 5], length 0
16:37:49.646116 IP 192.168.0.26.46218 > 192.168.0.1.80: Flags [.], ack 1, win 63, length 0
16:37:49.646448 IP 192.168.0.26.46218 > 192.168.0.1.80: Flags [P.], seq 1:196, ack 1, win 63, length 195: HTTP: POST /cgi/json-req HTTP/1.1
16:37:49.646518 IP 192.168.0.26.46218 > 192.168.0.1.80: Flags [P.], seq 196:1197, ack 1, win 63, length 1001: HTTP
16:37:49.648093 IP 192.168.0.1.80 > 192.168.0.26.46218: Flags [.], ack 196, win 2003, length 0
16:37:49.648211 IP 192.168.0.1.80 > 192.168.0.26.46218: Flags [.], ack 1197, win 2002, length 0
16:37:49.658093 IP 192.168.0.1.80 > 192.168.0.26.46218: Flags [P.], seq 1:667, ack 1197, win 2003, length 666: HTTP: HTTP/1.1 200 OK
16:37:49.658132 IP 192.168.0.26.46218 > 192.168.0.1.80: Flags [.], ack 667, win 63, length 0
16:37:49.659300 IP 192.168.0.26.46218 > 192.168.0.1.80: Flags [F.], seq 1197, ack 667, win 63, length 0
16:37:49.659480 IP 192.168.0.26.46226 > 192.168.0.1.80: Flags [S], seq 1856209893, win 64240, options [mss 1460,sackOK,TS val 2924455046 ecr 0,nop,wscale 10], length 0
16:37:49.662250 IP 192.168.0.1.80 > 192.168.0.26.46226: Flags [R.], seq 0, ack 1856209894, win 0, length 0
16:37:49.665467 IP 192.168.0.1.80 > 192.168.0.26.46218: Flags [F.], seq 667, ack 1198, win 2003, length 0
16:37:49.665517 IP 192.168.0.26.46218 > 192.168.0.1.80: Flags [.], ack 668, win 63, length 0

To avoid the race I added a small delay like skyjet18 suggested:

--- a/sagemcom_api/client.py
+++ b/sagemcom_api/client.py
@@ -191,6 +191,7 @@ class SagemcomClient:
 
         form_data = {"req": json.dumps(payload, separators=(",", ":"))}
 
+        await asyncio.sleep(0.1)
         async with self.session.post(api_host, data=form_data) as response:
             if response.status == 400:
                 result = await response.text()

Finally it worked:

16:43:24.798601 IP 192.168.0.26.37224 > 192.168.0.1.80: Flags [S], seq 565251666, win 64240, options [mss 1460,sackOK,TS val 2924790185 ecr 0,nop,wscale 10], length 0
16:43:24.803766 IP 192.168.0.1.80 > 192.168.0.26.37224: Flags [S.], seq 3815191878, ack 565251667, win 64240, options [mss 1460,nop,nop,sackOK,nop,wscale 5], length 0
16:43:24.803846 IP 192.168.0.26.37224 > 192.168.0.1.80: Flags [.], ack 1, win 63, length 0
16:43:24.805034 IP 192.168.0.26.37224 > 192.168.0.1.80: Flags [P.], seq 1:196, ack 1, win 63, length 195: HTTP: POST /cgi/json-req HTTP/1.1
16:43:24.805355 IP 192.168.0.26.37224 > 192.168.0.1.80: Flags [P.], seq 196:1197, ack 1, win 63, length 1001: HTTP
16:43:24.806963 IP 192.168.0.1.80 > 192.168.0.26.37224: Flags [.], ack 196, win 2003, length 0
16:43:24.807298 IP 192.168.0.1.80 > 192.168.0.26.37224: Flags [.], ack 1197, win 2002, length 0
16:43:24.815408 IP 192.168.0.1.80 > 192.168.0.26.37224: Flags [P.], seq 1:665, ack 1197, win 2003, length 664: HTTP: HTTP/1.1 200 OK
16:43:24.815468 IP 192.168.0.26.37224 > 192.168.0.1.80: Flags [.], ack 665, win 63, length 0
16:43:24.816875 IP 192.168.0.26.37224 > 192.168.0.1.80: Flags [F.], seq 1197, ack 665, win 63, length 0
16:43:24.821555 IP 192.168.0.1.80 > 192.168.0.26.37224: Flags [F.], seq 665, ack 1198, win 2003, length 0
16:43:24.821615 IP 192.168.0.26.37224 > 192.168.0.1.80: Flags [.], ack 666, win 63, length 0
16:43:24.919267 IP 192.168.0.26.37238 > 192.168.0.1.80: Flags [S], seq 1377189220, win 64240, options [mss 1460,sackOK,TS val 2924790306 ecr 0,nop,wscale 10], length 0
16:43:24.921478 IP 192.168.0.1.80 > 192.168.0.26.37238: Flags [S.], seq 939239069, ack 1377189221, win 64240, options [mss 1460,nop,nop,sackOK,nop,wscale 5], length 0
16:43:24.921538 IP 192.168.0.26.37238 > 192.168.0.1.80: Flags [.], ack 1, win 63, length 0
16:43:24.922496 IP 192.168.0.26.37238 > 192.168.0.1.80: Flags [P.], seq 1:195, ack 1, win 63, length 194: HTTP: POST /cgi/json-req HTTP/1.1
16:43:24.922675 IP 192.168.0.26.37238 > 192.168.0.1.80: Flags [P.], seq 195:615, ack 1, win 63, length 420: HTTP
16:43:24.923713 IP 192.168.0.1.80 > 192.168.0.26.37238: Flags [.], ack 195, win 2003, length 0
... etc

@iMicknl
Copy link
Owner

iMicknl commented Mar 29, 2024

@bakonyiferenc would you be willing to do a PR to fix this? :-)

@bakonyiferenc
Copy link
Contributor

That was just a quick and dirty PoC. I'll submit a proper PR next week.

bakonyiferenc added a commit to bakonyiferenc/python-sagemcom-api that referenced this issue Apr 3, 2024
Fixes (iMicknl#252)

- Implemented improved error handling and retry mechanism to handle transient network failures such as connection resets.
- Added exponential backoff strategy for retry attempts
- Ensured graceful handling of connection-related errors like ClientConnectorError, ClientOSError, and ServerDisconnectedError
bakonyiferenc added a commit to bakonyiferenc/python-sagemcom-api that referenced this issue Apr 3, 2024
Fixes (iMicknl#252)

- Implemented improved error handling and retry mechanism to handle transient network failures such as connection resets.
- Added exponential backoff strategy for retry attempts
- Ensured graceful handling of connection-related errors like ClientConnectorError, ClientOSError, and ServerDisconnectedError
iMicknl pushed a commit that referenced this issue Jun 2, 2024
Fixes (#252)

- Implemented improved error handling and retry mechanism to handle
transient network failures such as connection resets.
- Added exponential backoff strategy for retry attempts
- Ensured graceful handling of connection-related errors like
`ClientConnectorError`, `ClientOSError`, and `ServerDisconnectedError`
@iMicknl
Copy link
Owner

iMicknl commented Jun 2, 2024

Fixed by #292

@iMicknl iMicknl closed this as completed Jun 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants