#! /bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # # detect wrong shell versions # if ! ( printf "\n" | read -u 0 2>/dev/null ); then echo "wrong shell interpreter" 1>&2 exit 1 fi # # file to transfer to the router # filename="$1" # # some constants to be changed, if needed # box_ip=${2:-192.168.178.1} limit_memory=${3:-1} box_port=21 box_user=adam2 box_pass=adam2 passive_ftp="P@SW" [ ${#TMP} -eq 0 ] && TMP=/tmp tmpdir=$TMP/tmp_$(date +%s)_$$ writefifo=$tmpdir/write readfifo=$tmpdir/read storefifo=$tmpdir/store outstream=7 instream=8 upstream=9 logstream=3 logfile=${EVA_LOG:-$0.log} envfile=$tmpdir/env startaddress=0x80000000 # # helper functions # read_ftp_response() { local line=" -" rc=0 instream="$1" log="$2" while read -u $instream -r line; do [ ! -z "$log" ] && echo "$line" >&$log [ "${line:3:1}" != "-" ] && break done rc=$? echo "$line" return $rc } write_ftp_command() { local outstream="$2" cmd="$1" log="$3" [ ! -z "$log" ] && echo "$cmd" >&$log echo "$cmd" >&$outstream } login_to_box() { local instream="$1" outstream="$2" log="$3" lines=0 write_ftp_command "USER $box_user" $outstream $log while [ $lines -lt 10 ]; do line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec == x331 ]; then write_ftp_command "PASS $box_pass" $outstream $log else if [ x$ec == x230 ]; then return 0 elif [ x$ec == x530 ]; then return 1 fi fi lines=$(( lines++ )) done } get_environment() { local instream="$1" outstream="$2" log="$3" lines=0 write_ftp_command "TYPE I" $outstream $log line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec != x200 ]; then return 1 fi write_ftp_command "MEDIA SDRAM" $outstream $log line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec != x200 ]; then return 1 fi write_ftp_command "$passive_ftp" $outstream $log line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec == x227 ]; then data_conn=$(echo $line | sed -n -e 's/.*(\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\)).*/data_ip=\1.\2.\3.\4 data_port=\$(( \5 * 256 + \6 ))/p') if [ ${#data_conn} -eq 0 ]; then return 1 fi eval "$data_conn" nc -d -w 60 $data_ip $data_port >$envfile & data_connection=$! sleep 1 write_ftp_command "RETR env" $outstream $log line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec == x150 ]; then line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec == x226 ]; then if [ -d /proc/$data_connection ]; then kill $data_connection 2>/dev/null & wait $data_connection 2>/dev/null fi data_connection="" echo $envfile return 0 fi else return 1 fi else return 1 fi } upload_image() { local instream="$1" outstream="$2" log="$3" file="$4" memsize="$5" startaddr="$6" endaddr="$7" eval "exec $upstream<>$storefifo" if [ $? -ne 0 ]; then return 1 fi write_ftp_command "SETENV memsize $memsize" $outstream $logstream line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec != x200 ]; then eval "exec $upstream>&-" rm $storefifo return 1 fi write_ftp_command "SETENV kernel_args_tmp mtdram1=$startaddr,$endaddr" $outstream $logstream line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec != x200 ]; then eval "exec $upstream>&-" rm $storefifo return 1 fi write_ftp_command "TYPE I" $outstream $log line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec != x200 ]; then eval "exec $upstream>&-" rm $storefifo return 1 fi write_ftp_command "MEDIA SDRAM" $outstream $log line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec != x200 ]; then eval "exec $upstream>&-" rm $storefifo return 1 fi write_ftp_command "$passive_ftp" $outstream $log line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec == x227 ]; then data_conn=$(echo $line | sed -n -e 's/.*(\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\)).*/data_ip=\1.\2.\3.\4 data_port=\$(( \5 * 256 + \6 ))/p') if [ ${#data_conn} -eq 0 ]; then eval "exec $upstream>&-" return 1 fi eval "$data_conn" nc -w 3 $data_ip $data_port <&$upstream 2>/dev/null 1>&2 & data_connection=$! sleep 1 write_ftp_command "STOR $startaddr $endaddr" $outstream $logstream line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec == x150 ]; then cat $file >$storefifo line="$(read_ftp_response $instream $log)" ec=${line:0:3} if [ x$ec == x226 ] || [ x$ec == x553 ]; then if [ -d /proc/$data_connection ]; then kill $data_connection 2>/dev/null & wait $data_connection 2>/dev/null fi data_connection="" eval "exec $upstream>&-" rm $storefifo return 0 fi eval "exec $upstream>&-" rm $storefifo return 1 else eval "exec $upstream>&-" rm $storefifo return 1 fi else eval "exec $upstream>&-" rm $storefifo return 1 fi } # # check file size # if [ x$filename == x ]; then echo "Missing file name." exit 1 fi filesize=$(wc -c <$filename) if [ $? -ne 0 ]; then echo "Missing file '$filename'" exit 1 fi # # check, if "mkfifo" and "nc" are present and usable # mkfifo 2>/dev/null if [ $? -eq 127 ]; then echo "Missing usable 'mkfifo' command." exit 1 fi command -v nc 2>/dev/null 1>&2 if [ $? -ne 0 ]; then echo "Missing any 'nc' command, it's needed for network communication and the OpenBSD version is required." exit 1 fi # there's no unique identity for the BSD version, so we check for GNU version here nc -V 2>/dev/null | grep -q GNU if [ $? -eq 0 ]; then echo "Found GNU version of 'nc' command, but the OpenBSD version is required." exit 1 fi # # build redirections for FTP with "nc" # mkdir -p $tmpdir mkfifo $writefifo rc=$? if [ $rc -ne 0 ]; then echo "Error $rc creating write fifo $writefifo." rm -r $tmpdir exit 1 fi mkfifo $readfifo rc=$? if [ $rc -ne 0 ]; then echo "Error $rc creating read fifo $readfifo." rm $writefifo rm -r $tmpdir exit 1 fi mkfifo $storefifo rc=$? if [ $rc -ne 0 ]; then echo "Error $rc creating upload fifo $storefifo." rm $writefifo rm $readfifo rm -r $tmpdir exit 1 fi eval "exec $outstream<>$writefifo" rc=$? if [ $rc -ne 0 ]; then echo "Error $rc connecting write fifo to output stream." rm $writefifo rm $readfifo rm $storefifo rm -r $tmpdir exit 1 fi eval "exec $instream<>$readfifo" rc=$? if [ $rc -ne 0 ]; then echo "Error $rc connecting read fifo to input stream." eval "exec $outstream>&-" rm $writefifo rm $readfifo rm $storefifo rm -r $tmpdir exit 1 fi eval "exec $logstream<>$logfile" rc=$? if [ $rc -ne 0 ]; then echo "Error $rc connecting log stream to log file." eval "exec $instream>&-" eval "exec $outstream>&-" rm $writefifo rm $readfifo rm $storefifo rm -r $tmpdir exit 1 fi # # now open a connection to the box # nc $box_ip $box_port <&$outstream >&$instream 2>/dev/null & control_connection=$! data_connection="" line="$(read_ftp_response $instream $logstream)" ec=${line:0:3} if [ x$ec == x220 ]; then login_to_box $instream $outstream $logstream if [ $? -eq 0 ]; then write_ftp_command "SYST" $outstream $logstream line="$(read_ftp_response $instream $logstream)" ec=${line:0:3} if [ x$ec == x215 ]; then syst=$(echo "$line" | sed -n -e "s/.*\(AVM EVA\).*/\1/p") if [ ${#syst} -ne 0 ]; then echo "Found AVM bootloader: ${line:4}" environment=$(get_environment $instream $outstream $logstream) if [ $? -eq 0 ]; then hwrev=$(sed -n -e "s/^HWRevision *\(.*\)\r\$/\1/p" $environment) echo "Found hardware revision: $hwrev" memsize=$(sed -n -e "s/^memsize *\(.*\)\r\$/\1/p" $environment) echo "Memory size is $memsize $(printf "(%u MB)" $(( $memsize / 1024 / 1024 )))" [ "$limit_memory" = "1" ] && memsize=$(( 1024 * 1024 * 128 )) && echo "Memory size limited to 128 MB" echo "Image size is $(printf "0x%06x" $filesize) $(printf "(%u MB)" $(( filesize / 1024 / 1024 )))" setmemsize=$(printf "0x%08x" $(( memsize - filesize ))) echo "Setting temporary memory size to: $setmemsize" imagestartaddr=$(printf "0x%08x" $(( startaddress + setmemsize ))) imageendaddr=$(printf "0x%08x" $(( startaddress + memsize ))) echo "Setting temporary kernel args to: mtdram1=$imagestartaddr,$imageendaddr" upload_image $instream $outstream $logstream $filename $setmemsize $imagestartaddr $imageendaddr if [ $? -eq 0 ]; then echo "Image uploaded to device." fi fi else echo "Unexpected system found: ${line:4}" fi fi else echo "Login failed." fi else echo "Error connecting to FRITZ!Box boot loader." fi if [ ${#data_connection} -ne 0 ]; then if [ -d /proc/$data_connection ]; then kill $data_connection 2>/dev/null & wait $data_connection 2>/dev/null fi fi if [ ${#control_connection} -ne 0 ]; then if [ -d /proc/$control_connection ]; then kill $control_connection 2>/dev/null & wait $control_connection 2>/dev/null fi fi eval "exec $logstream>&-" eval "exec $instream>&-" eval "exec $outstream>&-" rm $writefifo rm $readfifo rm -r $tmpdir exit 1