#!/bin/bash
MYPWD="$(dirname $(realpath $0))"
help() {
echo "$0: Ask avm's JUIS update service for latest firmware infos like download url"
cat << 'EOX'
Parameters:
-a [Prio4] Actions, shows the URL if there is a newer version, prepended with the HWR.
-i [Prio3] Computer, shows the URL if there is a newer version.
-s [Prio2] Fritzbox, shows if a new version is available. Response is cached in: "/tmp/.juis_check"
-d [Prio1] RAW-output of the response (or no -a, -i and -s).
--nofb Do not try to read values from the local Fritzbox in juis_boxinfo.xml or older jason_boxinfo.xml files, but use random if vaiable is not set.
--dect Dect-Update: Searches for a firmware for an dect device.
--bpjm BPjM-Update: Searches for the encrypted BPjM-Modul file. To format the bpjm file and remove hashes for empty strings:
cat $RAWFILE | hexdump -s 64 -v -e '16/1 "%02x" " " 16/1 "%02x" " " 1/1 "%02x\n"' | sed 's/d41d8cd98f00b204e9800998ecf8427e 00$//'
- 1st row is the hash of protocol + domain (without subdomain), eg "http://example.com".
- 2nd row is the hash of the path + file, eg "/directory/file.extension" or empty.
To read the CRC32 checksum in the header: head -c4 $RAWFILE | xxd -p
To calculate the CRC32 checksum of the body: crc32 <( tail -c +$((1 + 4)) $RAWFILE )
--info InfoMessage: Searches for a very important info message by avm.
--down Downgrade: Searches for latest release firmware, like "Zurück zum offiziellen FRITZ!OS". "Buildtype" should not be "1".
Without --dect, --bpjm, --info and --down, a firmware for a Fritzbox is searched.
--plain Forces to use http. AVM uses this only.
--https Forces to use https. Curl is mandatory for https.
Without --plain and --https, the default is https if curl is installed. But on fritzbox only if "FREETZ_ADD_JUIS_CHECK__SSL" is set.
Variables:
On Fritzbox no variables are needed and missing will be read from the device.
On Computer the hardware-recision "HW" is mandatory and for very old (HWR<150) devices you have to set (at least the "Major" part of) "Version".
With Dect: The hardware number "DHW" is mandatory additional, the software version "DSW" and type "DTP" are optional.
Name Default if not set Comment
----------------------------------------------------------------------------------------------------------------------------------------------------------
HW [mandatory] The HWR code of the device, eg "226" for Fritzbox 7590
Version [HW].00.00-00000 The "HW" as "Major" does not work with HW<150
Name [random] There is no space in the name but a zero-width space: "printf '\342\200\212'". Or copy & paste examples below.
OEM avm Examples: "avm", "avme", "kdg", "1und1", "lgi", "otwo"
Lang de Examples: "de", "en", "es", "it", "fr", "pl", "nl"
Annex B (Kabel for docsis) Extender use "Ohne", docsis devices "Kabel".
Country 049 Examples: "049", "99", "041", "0420", "0421", "043", "044", "045", "046", "047", "048", "061", "064", "066", "0972"
Buildtype 1 (1000 for --down) Common values: "1"=retail, "1001"=labor, "1000"=inhaus
Serial [random] Example: "123456789ABC"
Nonce [random] Example: "123456789aBcDeEfGhIjkL=="
Flag [shuffle] If you do not want to set a flag use "empty", for docsis devices "cable_retail" is added.
UserAgent [shuffle] Values: "Box", "TestClient" or "BoxInternetCheck"
Provider [shuffle] Use "empty" if you want no value
ProviderName [empty] This is not added if not set
ManualRequest [shuffle] Values: "true" or "false"
UpdateConfig [shuffle] Values: "1" or "3"
DHW (dect) [mandatory] The MHW code of the device, eg "06.08" for Dect 302
DSW (dect) 00.00 The firmware version of the dect device to check, "00.00" works always.
DTP (dect) 1 Values: "1"=Dect, "2"=PLC, "3"=LTE
Examples:
env - tools/juis_check --dect Version=154.07.25-80000 Name=FRITZ\!Box 7590 HW=226 DHW=06.03 -i -s -d
env - tools/juis_check --bpjm Version=154.07.25-80000 Name=FRITZ\!Box 7590 HW=226 OEM=avm Lang=de Country=049 -i -s -d
env - tools/juis_check --info Version=84.06.87-92934 Name=FRITZ\!Box Fon WLAN 7390 HW=156 -i -s -d
env - tools/juis_check --down HW=267 -i -s -d
env - tools/juis_check Version=154.07.39-90000 Name=FRITZ\!Box 7590 HW=226 Buildtype=1001 Annex=B -i -s -d
env - tools/juis_check Version=252.07.20 Name=FRITZ\!Box 6660 Cable HW=252 Flag=cable_retail Annex=Kabel -i -s -d
env - tools/juis_check Version=29.04.87 HW=94 -i -s -d
env - tools/juis_check Version=67 HW=137 -i -s -d
env - tools/juis_check HW=156 -i -s -d
env - tools/juis_check HW=259 -i -s -d
for x in $(seq 150 300); do env - tools/juis_check HW=$x -a; done
for x in $(seq 200 300); do env - tools/juis_check HW=$x Buildtype=1001 -a; done
for x in $(seq 10 109); do [ ${#x} != 3 ] && x="0$x"; env - tools/juis_check --dect HW=259 DHW=${x::-1}.0${x:2} -a; done
Or just: tools/juis m # f
EOX
exit
}
js() { [ -e "$1" ] && sed -rn "s/^<.:$2>(.*)<.*/\1/p" "$1" ; }
main() {
local x soap avmca url host sdir docsis flags Envelope body post cache info payload day ser
local line='################################################################'
local type='text/xml; charset="utf-8"'
local zwsp="$(printf '\342\200\212')"
#soap='SOAPAction: "http://jason.avm.de/updatecheck/BoxFirmwareUpdateCheck"'
[ -z "$Nonce" ] && Nonce="$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | base64)"
[ -z "$UserAgent" ] && UserAgent="$(echo 'Box TestClient BoxInternetCheck' | sed 's/ /\n/g' | shuf | head -n1)"
[ -z "$ManualRequest" ] && ManualRequest="$(echo 'true false' | sed 's/ /\n/g' | shuf | head -n1)"
[ -z "$UpdateConfig" ] && UpdateConfig="$(echo '1 3' | sed 's/ /\n/g' | shuf | head -n1)"
[ -z "$Provider" ] && Provider="$(echo 'null oma_ipclient oma_lan vodafone2_vdsl' | sed 's/ /\n/g' | shuf | head -n1 | grep -v '^null$')"
[ "$Provider" == "empty" ] && Provider=''
[ -z "$Serial" ] && while [ "${#Serial}" != "12" ]; do Serial="$(hexdump -n6 -e '/1 "%02X"' /dev/urandom | sed 's/[^0-9A-F]//g')"; done
if [ "$BIX" != "n" ]; then
[ -e '/var/juis_boxinfo.xml' ] && info='/var/juis_boxinfo.xml'
[ -e '/var/jason_boxinfo.xml' ] && info='/var/jason_boxinfo.xml'
fi
[ -z "$Name" ] && Name="$(js $info 'Name')"
[ -z "$HW" ] && HW="$(js $info 'HW')"
case "$HW" in 175|176|182|187|213|220|231|233|252|267) docsis='y' ;; esac
[ -z "$OEM" ] && OEM="$(js $info 'OEM')"
[ -z "$OEM" ] && OEM="avm"
[ -z "$Lang" ] && Lang="$(js $info 'Lang')"
[ -z "$Lang" ] && Lang="de"
[ -z "$Annex" ] && Annex="$(js $info 'Annex')"
if [ -z "$Annex" ]; then
[ -n "$docsis" ] && Annex="Kabel" || Annex="B"
fi
[ -z "$Country" ] && Country="$(js $info 'Country')"
[ -z "$Country" ] && Country="049"
[ -z "$Flag" ] && Flag="$(js $info 'Flag')"
[ -z "$Buildtype" ] && Buildtype="$(js $info 'Buildtype')"
if [ -z "$Buildtype" ]; then
[ "$CHK" == "BoxFirmwareDowngradeCheck" ] && Buildtype="1000" || Buildtype="1"
fi
if [ -z "$Name" ]; then
while [ "${#Name}" != "4" ]; do Name="$Name$(hexdump -n1 -e '/1 "%02X"' /dev/urandom | sed -rn 's/.*([0-9]).*/\1/p')"; done
Name="FRITZ\!Box$zwsp$Name"
fi
if [ -z "$Version" ]; then
Version="$(js $info 'Version')"
if [ -n "$Version" ]; then
Buildnumber="$(js $info 'Buildnumber')"
[ -z "$Buildnumber" ] && Buildnumber="$(js $info 'Revision')"
[ -n "$Buildnumber" ] && Version="$Version-$Buildnumber"
fi
if [ -z "$Version" ]; then
if [ "$HW" -ge 150 2>/dev/null ]; then
if [ "$HW" -lt 248 2>/dev/null ]; then
Version="$(( $HW - 72 ))"
else
Version="$HW"
fi
fi
fi
fi
Buildnumber="${Version#*-}"
x="${Version%-*}"
Major="${x%%.*}"
x="${x#*.}"
Minor="${x%.*}"
Minor="${Minor#0}"
Patch="${x#*.}"
[ "$Buildnumber" == "$Version" ] && Buildnumber="00000"
[ "$Minor" == "$x" ] && Minor="00"
[ "$Patch" == "$x" ] && Patch="00"
[ -z "$HW" ] && echo "You have to provide 'HW'." 1>&2 && exit 1
[ -z "$Version" ] && echo "You have to provide 'HW' and 'Version'." 1>&2 && exit 1
if [ "$SSL" == 'y' ]; then
avmca="$MYPWD/avm-rootca.pem"
[ -e "/etc/avm_root_ca.pem" ] && avmca="/etc/avm_root_ca.pem"
[ -e "/etc/jasonii_root_ca.pem" ] && avmca="/etc/jasonii_root_ca.pem"
host="jws.avm.de" # https-cert matches hostname
else
host="$HW.jws.avm.de" # no wirldcard certificate
fi
#old: /Jason/UpdateCheck
sdir='/Jason/UpdateInfoService'
Envelope='xmlns:e="http://juis.avm.de/updateinfo" xmlns:q="http://juis.avm.de/request">'
if [ "$CHK" == "BoxMessageCheck" ]; then
sdir='/Jason/MessageInfoService'
Envelope='xmlns:e="http://juis.avm.de/messageinfo" xmlns:q="http://juis.avm.de/request" xmlns:s="http://juis.avm.de/response">'
fi
url="$host$sdir"
if [ "$CHK" == "BPjMUpdateCheck" ]; then
while [ "${#day}" != "1" ]; do day="$day$(hexdump -n4 -e '/1 "%02X"' /dev/urandom | sed -rn 's/.*([012]).*/\1/p')"; done
while [ "${#day}" != "2" ]; do day="$day$(hexdump -n1 -e '/1 "%02X"' /dev/urandom | sed -rn 's/.*([0-9]).*/\1/p')"; done
payload="$(( $(date +%Y%m) - 100 ))$day"
fi
if [ "$CHK" == "DeviceFirmwareUpdateCheck" ]; then
[ -z "$DHW" ] && echo "For --dect you have to provide 'DHW'." 1>&2 && exit 1
[ -z "$DSW" ] && DSW="00.00"
[ -z "$DTP" ] && DTP="1"
while [ "${#ser}" != "12" ]; do ser="$ser$(hexdump -n1 -e '/1 "%02X"' /dev/urandom | sed -rn 's/.*([0-9]).*/\1/p')"; done
payload="
$DTP
$DHW
$DSW
$ser
$Lang"
fi
if [ -n "$ProviderName" ]; then
ProviderName="
$ProviderName"
fi
if [ -z "${Flag// /}" ]; then
Flag="$(echo 'null mesh_master mesh_master_no_trusted mesh_repeater mesh_repeater_no_trusted' | sed 's/ /\n/g' | shuf | head -n1)"
Flag="$Flag $(echo 'null medium_lan medium_dsl' | sed 's/ /\n/g' | shuf | head -n1)"
Flag="$Flag $(echo 'null null crashreport myfritz_letsencrypt botnet_detection avm_acs prov_acs' | sed 's/ /\n/g' | shuf | head -n3)"
[ -n "$docsis" ] && Flag="$Flag cable_retail"
[ -z "${Flag// /}" ] && Flag='empty'
fi
for x in ${Flag//null/}; do flags="${x/empty/}
$flags"; done
flags="$(echo "$flags" | grep -v '^$')"
body=$(cat << EOX
$Nonce
$UserAgent
$ManualRequest
${Name//$zwsp/ }
$HW
$Major
$Minor
$Patch
$Buildnumber
$Buildtype
$Serial
$OEM
$Lang
$Country
$Annex
$flags
$UpdateConfig
$Provider$ProviderName$payload
EOX
)
post=$(cat << EOX
POST /${url#*/} HTTP/1.1
Host: ${url%%/*}:80
Content-Length: ${#body}
Content-Type: ${type}
${soap}
${body}
EOX
)
cache='/tmp/.juis_check'
[ "$DEV" == 'i' -o "$DEV" == 'a' ] && cache='/dev/null'
if [ "$SSL" == 'y' ]; then
resp="$(curl -s -X POST -H "${type}" ${soap:+-H $soap} -d "${body}" "https://${url}" --cacert "${avmca}" \
| tee $cache | sed 's,><,>\n<,g' | sed 's,$//g')"
else
resp="$(echo -e "${post}" | nc ${url%%/*} 80 2>/dev/null \
| tee $cache | sed 's,><,>\n<,g' | sed 's,$//g')"
fi
[ -z "$resp" ] && echo 'Did not received an anser.' 1>&2 && exit 1
case "$DEV" in
a)
URL="$(echo "$resp" | sed -n "s/^//p")"
[ -n "$URL" ] && ( [ "$CHK" == "DeviceFirmwareUpdateCheck" ] && echo "$DHW=$URL" || echo "$HW=$URL" )
;;
i)
URL="$(echo "$resp" | sed -n "s/^//p")"
[ -n "$URL" ] && echo "URL=$URL"
;;
s)
VER="$(echo "$resp" | sed -n "s/^//p")"
[ -n "$VER" -a "$VER" != "$Version" ] && echo "Found newer version: $VER" 1>&2 || echo "No newer version found." 1>&2
;;
*)
echo -e "Using SSL:=${SSL:-n}\n\nRequest:\n$line"
[ "$SSL" == 'y' ] && echo -ne "$body" || echo -ne "$post"
echo -e "\n$line\n\nResponse:\n$line"
if command -v xmllint &>/dev/null; then
grep '^<' -v "$cache"
grep '^<' "$cache" | xmllint --format -
else
echo "$resp"
fi
echo -e "$line\n"
rm "$cache"
;;
esac
}
DEV=''
SSL=''
BIX=''
CHK='BoxFirmwareUpdateCheck'
args() {
[ "${#*}" == '0' ] && help
local DEVA DEVI DEVS DEVD
for x in $*; do
l="${x%=*}"
r="${x#*=}"
[ "$x" == '-a' ] && DEVA='x'
[ "$x" == '-i' ] && DEVI='x'
[ "$x" == '-s' ] && DEVS='x'
[ "$x" == '-d' ] && DEVD='x'
[ "$x" == '--nofb' ] && BIX='n'
[ "$x" == '--down' ] && CHK='BoxFirmwareDowngradeCheck'
[ "$x" == '--info' ] && CHK='BoxMessageCheck'
[ "$x" == '--bpjm' ] && CHK='BPjMUpdateCheck'
[ "$x" == '--dect' ] && CHK='DeviceFirmwareUpdateCheck'
[ "$x" == '--plain' ] && SSL='n'
[ "$x" == '--https' ] && SSL='y'
[ "$x" == "$l" ] && continue
eval $l="$r"
done
[ -n "$DEVA" ] && DEV='a'
[ -n "$DEVI" ] && DEV='i'
[ -n "$DEVS" ] && DEV='s'
[ -n "$DEVD" ] && DEV='d'
[ "$SSL" == 'y' ] && [ -z "$(command -v curl)" ] && echo 'Curl is mandatory for https.' 1>&2 && exit 1
if [ -z "$SSL" ]; then
command -v curl >/dev/null && SSL='y' || SSL='n'
if [ -e '/etc/.freetz-ng-version' ]; then
grep -q "^FREETZ_ADD_JUIS_CHECK__SSL='y'" /etc/options.cfg 2>/dev/null || SSL='n'
fi
fi
}
args "$@"
main