Updated 2017-06-08 09:38:58 by dbohdan

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.3.1.tm

See also  edit

Code  edit

#! /usr/bin/env tclsh
# Public IP library for Tcl, version 0.3.1
# Copyright (C) 2015, 2016 dbohdan
# License: MIT
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] eq {ok}} {
        set rdata [dict get [lindex [::dns::result $id] 0] rdata]
        ::dns::cleanup $id
        return $rdata
    } else {
        set error [::dns::error $id]
        ::dns::cleanup $id
        error $error

# 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])} {