...It should not be hard to adapt the browse displays to show both internal and external ip addresses. A good learner project if you have any desire to try your hand at modifying the webif.
...
I don't think it's entirely straightforward. There are two parts: changing the WebIf code and layout to display the external address, and actually determining that address.
The first part means modifying some HTML and the Jim code that formats and populates it (for the Media Details pop-up) in
/mod/webif/html/browse/file.jim
so as to display an external DLNA URL; it would also be useful to add the DLNA URL field to the "general file" display, but that probably means more work to create a general media file class of which the existing
ts
Humax recording class would be a specialisation.
The second part requires creating a new system information function to return the external IP address, and modifying the media file classes to use it.
In general there's no direct way for WebIf software to know the external IP address, as this is between the LAN router/gateway and the ISP router.
These techniques are suggested but can be ruled out:
- parse the output of traceroute to a known external host: too slow and unreliable;
- parse an informational web page served by the gateway that shows the address: faster, but depends on each router model (including whether it offers such a page) and its authentication data;
- use SNMP to read the address from the gateway: in theory a fast and standardised approach, but we have no SNMP client in the repository.
This leaves other techniques in which we ask some external service to send back the source address that we used to contact it.
Several web addresses will return the desired address as plain-text content; examples include icanhazip.com, ifconfig.me, ipecho.net and even some fly-by-night offerings like checkip.amazonaws.com.
This guy set up a test of various such services which I've adapted as a POSIX shell script to run in the CF environment with the
busybox package installed (it will be) and also
curl (below, the transcript shows 'a.b.c.d' for the actual IP address that was returned):
Code:
#!/bin/ sh
# tests the speed of various services for externalip:
# https://github.com/rsp/scripts/blob/master/externalip.md
# converted from the ajax-cdn-speed-test:
# https://github.com/rsp/ajax-cdn-speed-test
# by Rafał Pocztarski https://rsp.github.io/
# hosts taken on 2015-04-02 from:
# http://unix.stackexchange.com/questions/22615
# https://coderwall.com/p/lyrjsq/extract-your-external-ip-using-command-line-tools
# using only those that output IP only (with no need to parse output)
# local not yet in POSIX
type local >/dev/null || local() { true;}
# Busybox sleep only knows -N but there is usleep
! sleep 0.1s 2>/dev/null && type usleep >/dev/null &&
sleep() { # secs[s]
local zz zzs
zz=${1%s}
zzs=${zz%.*}
[ -z "$zzs" ] && zzs=0
[ "${zzs%[^0-9]}" != "$zzs" ] && return 1
zz=${zz#$zzs}; zz=${zz#.}000000
# microseconds 0<=zz<1000000
zz=${zz%${zz#??????}}
usleep $(( 1000000*zzs + zz ))
}
# default CF ping doesn't know -i
if ! ping -c 1 -i 0.2 127.0.0.1 2>/dev/null; then
myping() { # host count interval
local cnt ival cmd1 cmd2 cmd3 cmd4 cmd5
[ -n "$1" ] || exit
cmd1='BEGIN { xmt=0; rcv=0; rtn=999999; rtm=0; rtt=0; dst=0; }'
cmd2='/^PING [[:alnum:].]+ / { if (dst == 0) { print $0; dst=1; host=$2;};}'
cmd3='/^[[:digit:]]+ bytes from / { rtt += $10; if ($10 < rtn) rtn=$10; if ($10 > rtm) rtm=$10; ++rcv; print $0;}'
cmd4='/^[[:digit:]]+ packets transmitted, / { xmt += $1; }'
cmd5='END {printf "\n--- %s ping statistics ---\n", host; if (xmt > 0) printf "%d packets transmitted, %d packets received, %d%% packet loss\n", xmt, rcv, 100*(xmt-rcv)/xmt; if (rcv > 0) printf "round-trip min/avg/max = %1.2f/%1.2f/%1.2f ms\n", rtn, rtt/rcv, rtm; }'
ival=${3:-1}
cnt=${2:-1}
while [ "$cnt" -gt 0 ]; do
ping -c 1 "$1" 2>&1
cnt=$((cnt - 1))
[ $cnt -gt 0 ] && sleep "${ival}s"
done |
awk -F '[ =]' "${cmd1}; ${cmd2}; ${cmd3}; ${cmd4}; ${cmd5}"
}
else
myping() { ping -c "${2:-1}" -i "${3:-1}" "$1"; }
fi
curlout() {
awk -F '[[:space:]~]' '{if ($NF ~ /[[:digit:].]+/) {time=$NF; --NF; printf "%s %1.3fms\n", $0, time;} else print; exit;}'
}
checkurls() {
local httpl httpsl pingl url protocol err cout answer time
httpl=
httpsl=
pingl=
while read -r url; do
printf "\n%s\n" "$url"
host=${url#//}; host=${host%%/*}
# avoid weird hostname (ifcfg.me, eg) resolving to 0.0.0.0
nslookup "$host" | grep -q '0.0.0.0' && continue
for protocol in http https; do
cout="$(curl -f -m10 -L -sw "~over $protocol:\t%{time_total}" "${protocol}:${url}")"
err=$?
cout="$(echo "$cout" | tr -d '\n')"
answer="$(echo "$cout" | cut -d'~' -f1)"
if [ -z "$answer" ]; then
if [ $err -ne 0 ]; then
answer="--no response--"
else
answer="--no address--"
fi
time=999999
echo "${answer}${cout}" | curlout
continue
elif [ -n "${answer##*[0-9].*[0-9].*[0-9].*[0-9]}" ]; then
cout="$(echo "$cout" | cut -d'~' -f2-)"
answer="--not a.b.c.d address--"
time=999999
echo "${answer}${cout}" | curlout
continue
else
time=$(echo "$cout" | awk '{printf $NF; exit}')
echo "${cout}" | curlout
# stats=$(echo $cout | cut -d'~' -f1)
case $protocol in
http) httpl="${httpl}${time}s http:${url} - answer='${answer}'\n" ;;
https) httpsl="${httpsl}${time}s https:${url} - answer='${answer}'\n" ;;
esac
fi
done
avgping="$(myping "$host" "$pings" "$interval" |
awk -F = '/min\/avg\/max/ { if (1 < split($2, aa, /\//)) {print aa[2];}; exit; }')"
[ -z "$avgping" ] && avgping=999999
printf "average ping:\t%1.2fms\n" "$avgping"
pingl="${pingl}${avgping}ms ${url}\n"
done
printf "\nBest http response times:\n"
printf "${httpl}\n" | sort -n
printf "\nBest https response times:\n"
printf "${httpsl}\n" | sort -n
printf "\nBest average ping times:\n"
printf "${pingl}\n" | sort -n | grep -vE '^999999'
}
pings=5
interval=0.2
# Obsolete:
#//curlmyip.com/
#//tnx.nl/ip
#//ifcfg.me/
cat << 'END' | (checkurls)
//ifconfig.me/
//icanhazip.com/
//ip.appspot.com/
//ident.me/
//ipecho.net/plain
//whatismyip.akamai.com/
//wgetip.com/
//ip.tyk.nu/
//corz.org/ip
//bot.whatismyipaddress.com/
//checkip.amazonaws.com/
//ipof.in/txt
//l2.io/ip
//eth0.me/
END
Code:
# ./externalip-benchmark
...
Best http response times:
0.065987s http://l2.io/ip - answer='a.b.c.d'
0.066000s http://ident.me/ - answer='a.b.c.d'
0.103650s http://eth0.me/ - answer='a.b.c.d'
0.127000s http://ip.tyk.nu/ - answer='a.b.c.d'
0.131227s http://whatismyip.akamai.com/ - answer='a.b.c.d'
0.160751s http://ifconfig.me/ - answer='a.b.c.d'
0.197161s http://checkip.amazonaws.com/ - answer='a.b.c.d'
0.199395s http://icanhazip.com/ - answer='a.b.c.d'
...
Best https response times:
0.916000s https://ifconfig.me/ - answer='a.b.c.d'
0.931052s https://l2.io/ip - answer='a.b.c.d'
0.952342s https://eth0.me/ - answer='a.b.c.d'
0.966221s https://ipof.in/txt - answer='a.b.c.d'
1.022527s https://icanhazip.com/ - answer='a.b.c.d'
...
Best average ping times:
9.67ms //ifconfig.me/
17.18ms //l2.io/ip
21.51ms //ipof.in/txt
28.57ms //ident.me/
35.11ms //whatismyip.akamai.com/
36.10ms //eth0.me/
....
#
For me
http://l2.io wins easily but your mileage may vary.
Although HTTP is quite lightweight, compared to HTTPS, another approach which may be faster, and doesn't demand a client like
curl or
wget, uses a public DNS services that automatically returns the sender's IP address for some special query. The services that do this are OpenDNS, Akamai and Google, but to exercise them we need something better than Busybox
nslookup
: that thing is the
Jim DNS package provided as an example in the Jim TCL distribution -- the file has to be copied into
/mod/lib/jim
:
Code:
# jimsh
Welcome to Jim version 0.79
. package require dns
1.0
. ::dns::address [::dns::resolve myip.opendns.com -server resolver1.opendns.com]
a.b.c.d
. # also available resolver2, resolver3, resolver4 ... .opendns.com
. ::dns::address [::dns::resolve whoami.akamai.net -server ns1-1.akamaitech.net]
a.b.c.d
. dict get [split [string range [::dns::result [::dns::resolve o-o.myaddr.l.google.com -server ns1.google.com -type TXT]] 1 end-1]] "rdata"
a.b.c.d
.
These all seem to take about 100ms per query, but maybe there's some Jim overhead in that: still faster than most of the HTTP services.
As there are more HTTP services with a single API than DNS services, and HTTP is clearly faster than HTTPS, one should use
http://l2.io/ip or similar. If a bad result that might be injected by an attacker must be avoided, use HTTPS instead, but wait more than 10 times longer for the result. It might be useful to annotate the new Media Details external URL to show which service was used to determine the external IP address.