#!/bin/sh # needs: curl, yq (xq) # usage: fb-getip.sh [ip address] # # Endpoints & Services: # http://fritz.box:49000/tr64desc.xml # AVM TR064 Documentation: # https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AVM_TR-064_first_steps.pdf # https://www.broadband-forum.org/technical/download/TR-064.pdf FBHOST="fritz.box" [ ! -z "$1" ] && FBHOST="$1" # Variable parts, which can be looked up in the TR-064 documentation SERVICE=WANIPConnection ACTION=GetExternalIPAddress # Endpoints supported by the fritzbox, can be looked up at the # "Endpoint & Services" URL. ENDPOINT=WANIPConn1 # SOAP protocol payload (https://en.wikipedia.org/wiki/SOAP) # This is the language the fritzbox is "speaking". So we create this # payload with the appropriage Service, Action variables. PAYLOAD=\ ' ' # We use curl to send the payload with the appropriate soap+upnp header # to the fritzbox and receive the SOAP response. # # Header explanation: # # - Header: Content-Type - describes that we send format text/xml with utf8 encoding. # See HTTP Specification: # https://www.rfc-editor.org/rfc/rfc9110.html#name-content-type # (Chapter 8.3) # # - Header: SoapAction... - it's redundant with the payload information. # See SoapAction specification: # https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528 # (Chapter 6.1.1) # # - SoapAction:... - This is "urn:schemas-upnp-org: # Each has it's own specifcation. # WANIPConnection Service Specification: # https://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v1-Service.pdf # (Chapter 2.1) RESPONSE="$(curl -s "http://$FBHOST:49000/igdupnp/control/$ENDPOINT" \ -H "Content-Type: text/xml; charset=utf-8" \ -H "SoapAction:urn:schemas-upnp-org:service:$SERVICE:1#$ACTION" \ -d "$PAYLOAD")" # The SOAP response is ugly XML (just like the requst), so we use "xq", # which converts XML to JSON and allows us to operate on it with "jq" # syntax. # yq/xq documentation: https://kislyuk.github.io/yq # jq documentation: https://jqlang.github.io/jq/manual # Tutorial: https://www.ashbyhq.com/blog/engineering/jq-and-yq # # (once understood, these tools are _very_ powerful! - worth to learn) # This "case" is only there so we can do a different kind parsing for # other endpoints. Like when the ACTION variable is changed the parsing # needs to be adapted. case "$ACTION" in GetExternalIPAddress) printf '%s' "$RESPONSE" \ | xq -r '."s:Envelope"."s:Body"."u:GetExternalIPAddressResponse".NewExternalIPAddress' ;; *) printf '%s' "$RESPONSE" ;; esac