kbr / fritzconnection

Python-Tool to communicate with the AVM Fritz!Box by the TR-064 protocol and the AHA-HTTP-Interface
MIT License
303 stars 59 forks source link

Issue with port forward when name contain quotes #111

Closed chemelli74 closed 2 years ago

chemelli74 commented 2 years ago

I have a port forward rule named "Let's Encrypt". Unfortunately when you try to disable/enable it you get a bad parameters.

I suspect quoting is not handled correctly. Will try to investigate further, but before forgetting I though to log this issue.

Simone

chemelli74 commented 2 years ago

This is the trace:

fritzconnection->call_action (log added by me):

2021-07-12 15:13:06 ERROR (SyncWorker_5) [fritzconnection] Simone: call service <WANPPPConnection1> with action <AddPortMapping>
and params <{'NewRemoteHost': '0.0.0.0', 'NewExternalPort': 80, 'NewProtocol': 'TCP', 'NewInternalPort': 80, 'NewInternalClient': '192.168.xxx.yyy', 'NewEnabled': '1', 'NewPortMappingDescription': "Let's Encrypt Renewal", 'NewLeaseDuration': 0}>

from HomeAssistant side:

2021-07-12 15:13:06 ERROR (SyncWorker_5) [homeassistant.components.fritz.switch] Connection Error: Please check the device is properly configured for remote login
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/fritz/switch.py", line 72, in service_call_action
    return fritzbox_tools.connection.call_action(  # type: ignore[no-any-return]
  File "/usr/local/lib/python3.9/site-packages/fritzconnection/core/fritzconnection.py", line 229, in call_action
    return self.soaper.execute(service, action_name, arguments)
  File "/usr/local/lib/python3.9/site-packages/fritzconnection/core/soaper.py", line 238, in execute
    return handle_response(response)
  File "/usr/local/lib/python3.9/site-packages/fritzconnection/core/soaper.py", line 222, in handle_response
    raise_fritzconnection_error(response)
  File "/usr/local/lib/python3.9/site-packages/fritzconnection/core/soaper.py", line 147, in raise_fritzconnection_error
    raise exception(message)
fritzconnection.core.exceptions.FritzArgumentError: UPnPError:
errorCode: 402
errorDescription: Invalid Args

do you have an idea on how to escape the offending part ?

 'NewPortMappingDescription': "Let's Encrypt Renewal"

Simone

chemelli74 commented 2 years ago

As a reminder, this is the part of the code to check better:

https://github.com/kbr/fritzconnection/blob/cadc128c47d82863cb77f82cddb29c0eb27fffba/fritzconnection/core/soaper.py#L228-L230

Simone

kbr commented 2 years ago

Why do you think this code is critical?

chemelli74 commented 2 years ago

Why do you think this code is critical?

Not critical, but I think this is the part of the code responsible to create the arguments for the body of the requests. Thus the place to investigate more in depth how to correctly escape descriptions with quotes.

Simone

kbr commented 2 years ago

Everything is unicode. Until line 232 where the request gets converted to an utf-8 encoded byte-string. I suppose the PortMappingDescription has the same AllowedChars set like the ones for Password and Username where a single-quote is not included. That would explain the error description reported from the box.

chemelli74 commented 2 years ago

PortMappingDescription allows the single quote at least in Fritzbox UI and I can easily get the status of that port via call_action as shown above. Only issue is with AddAction.

I cannot find a list of not allowed chars for descriptions: https://service.avm.de/help/en/FRITZ-Box-Fon-WLAN-7490/018/hilfe_zeichen_fuer_kennwoerter

Simone

chemelli74 commented 2 years ago

I may have found something useful.

From https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/x_appsetup.pdf:

image

kbr commented 2 years ago

Currently I have no idea why it doesn't work. Just keep in mind that FritzBox UI and TR064 are different interfaces. And regarding the link please fetch the document matching the used service. In your case this is https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/wanpppconnSCPD.pdf.

If you can read the description string "Let's Encrypt" via TR064 but can't send it, you may try to find out, whether there is a special escaping in the received response (I doubt this) and try to use this for sending.

chemelli74 commented 2 years ago

Debugged and found the value in the response.content:

<NewPortMappingDescription>Let&apos;s Encrypt Renewal</NewPortMappingDescription>

Simone

chemelli74 commented 2 years ago

Found the list:

image

Simone

chemelli74 commented 2 years ago

With the following code:

from xml.sax.saxutils import escape

test_string = "this is a list of strings: 1=&  2='  3=<  4=> 5=\""

# Only escapes &, <, and > by default
escaped = escape(test_string, entities={
        "'": "&apos;",
        "\"": "&quot;"
    })

print(escaped)

result is:

this is a list of strings: 1=&amp; 2=&apos; 3=&lt; 4=&gt; 5=&quot;

:-)

Simone

kbr commented 2 years ago

Ahhh, thanks – seems to be I was captured by the term escape characters, but it is about entity encodings. That's html basic stuff. No idea why I didn't get this right at the beginning. Will have a look at it :-)

chemelli74 commented 2 years ago

Ahhh, thanks – seems to be I was captured by the term escape characters, but it is about entity encodings. That's html basic stuff. No idea why I didn't get this right at the beginning. Will have a look at it :-)

Please check https://github.com/kbr/fritzconnection/pull/115. Seems to work fine for all chars but the quote :-(

I added a log line for the body and it seems ok:

2021-07-17 15:29:12 DEBUG (SyncWorker_8) [test-simone] body=b'<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:AddPortMapping xmlns:u="urn:dslforum-org:service:WANPPPConnection:1"><NewRemoteHost>0.0.0.0</NewRemoteHost><NewExternalPort>80</NewExternalPort><NewProtocol>TCP</NewProtocol><NewInternalPort>80</NewInternalPort><NewInternalClient>192.168.188.42</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>Let&apos;s Encrypt Renewal</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping></s:Body></s:Envelope>'

Simone

chemelli74 commented 2 years ago

Comparing XML response "GetGenericPortMappingEntryResponse" and XML sent for "AddPortMapping" the only difference is:

encoding="utf-8"

Simone

kbr commented 2 years ago

Please see my comment on #115

chemelli74 commented 2 years ago

Logged a ticket with AVM. Reference: Ticket-ID 4421905

Simone

chemelli74 commented 2 years ago

Logged a ticket with AVM. Reference: Ticket-ID 4421905

Simone

Official reply:

As of today the supported characters for PortMappingDescription via TR-064 are:

- a-Z  
- 0-9 
- white space 
- hyphen 
- underline 

Unfortunately the documentation does not mention this in detail.
We apologize for related inconveniences and try to improve. 

Simone