dbohdan 2015-03-18: Here is my solution for how to get your globally routable IP address. It is based on this helpful answer on ServerFault and uses the DNS protocol. One upside of the approach is that the DNS servers used are more likely to last and have a good uptime than your average free your-IP-over-a-REST-API service.
Download with wiki-reaper: wiki-reaper -x 44451 0 > publicip-0.2.0.tm
package require Tcl 8.5 namespace eval ::public-ip { package require dns variable services { ipv4 { google {{o-o.myaddr.l.google.com} -server ns1.google.com -type TXT} akamai {whoami.akamai.net -server ns1-1.akamaitech.net -type A} opendns {myip.opendns.com -server resolver1.opendns.com -type A} } } variable ready 0 } # Resolve the public IP address using one service. # Usage: query google|akamai|opendns proc ::public-ip::query {{service google} {protocol ipv4}} { set id [::dns::resolve \ {*}[dict get ${::public-ip::services} $protocol $service]\ -command ::public-ip::query-ready] vwait ::public-ip::ready if {[::dns::status $id] ne "ok"} { error [::dns::error $id] } set result [dict get [lindex [::dns::result $id] 0] rdata] ::dns::cleanup $id return $result } # Used internally by [query]. proc ::public-ip::query-ready {token} { set ::public-ip::ready 1 } # Resolve the public IP address using all of the available services for the # selected protocol. If one of the services returns a different address than the # others generate an error. proc ::public-ip::query-all {{protocol ipv4}} { set ip "" foreach service \ [dict keys [dict get ${::public-ip::services} $protocol]] { set resolved [query $service $protocol] if {$ip eq ""} { set ip $resolved set ipResolver $service } else { if {$ip ne $resolved} { error "got $protocol address $ip from $ipResolver but\ $resolved from $service" } } } return $ip } proc ::public-ip::test {{protocol ipv4}} { puts [query-all $protocol] } # If this is the main script... if {[info exists argv0] && ([file tail [info script]] eq [file tail $argv0])} { ::public-ip::test }
package require Tcl 8.6 namespace eval ::public-ip { package require dns variable services { ipv4 { google {{o-o.myaddr.l.google.com} -server ns1.google.com -type TXT} akamai {whoami.akamai.net -server ns1-1.akamaitech.net -type A} opendns {myip.opendns.com -server resolver1.opendns.com -type A} } } variable ready 0 # Resolve the public IP address using one service. # Usage: query google|akamai|opendns proc query {{service google} {protocol ipv4}} { variable services set id [::dns::resolve \ {*}[dict get $services $protocol $service]\ -command [namespace code query-ready]] try { vwait ::public-ip::ready if {[::dns::status $id] ne "ok"} { error [::dns::error $id] } return [dict get [lindex [::dns::result $id] 0] rdata] } finally { ::dns::cleanup $id } } # Used internally by [query]. proc query-ready {token} { variable ready 1 } # Resolve the public IP address using all of the available services for the # selected protocol. If one of the services returns a different address than the # others generate an error. proc query-all {{protocol ipv4}} { variable services set ip "" foreach service [dict keys [dict get $services $protocol]] { set resolved [query $service $protocol] if {$ip eq ""} { set ip $resolved set ipResolver $service } elseif {$ip ne $resolved} { error "got $protocol address $ip from $ipResolver but\ $resolved from $service" } } return $ip } namespace export query query-all proc test {{protocol ipv4}} { puts [query-all $protocol] } } # If this is the main script... if {[info exists argv0] && [info script] eq $argv0]} { ::public-ip::test }