Updated 2016-07-09 11:22:36 by dkf

This page describes some ways to find the IP address of the machine you are running Tcl on. They are organized by the method used.

Using TCP sockets edit

Usually when people ask the question in the title they mean "the IP address of the primary network interface". This is not always easy to discover. However, if you happen to have a socket connected to an external host handy, you can use
lindex [fconfigure $sock -sockname] 0

to get (one of) the IP address(es) of (one of) your network interfaces.
proc ip:address {} {
    # find out localhost's IP address
    # courtesy David Gravereaux, Heribert Dahms
    set TheServer [socket -server none -myaddr [info hostname] 0]
    set MyIP [lindex [fconfigure $TheServer -sockname] 0]
    close $TheServer
    return $MyIP
}

(Note that the above procedure can go wrong in several different ways.)

For example, depending on your network, your hostname may be something that has nothing to do with your external IP address. Connecting to an external host is probably the only way to know that that IP is used to connect to that host. When writing server applications your should really be aware that quite a few of them have multiple interfaces for multiple network domains (internet/intranet). This has become quite common: almost all company and home networks use some kind of gateway server with network address translation: One IP for internal traffic, another for external.

An important question you should ask yourself is: do I really care about my IP address? Remember that for most applications you do not need to know your IP address, and in a lot of cases you do, you may be better off asking the user. Most server applications don't need to know their IP address, they simply accept any incoming connection on the specified port. And client applications tend to only need to know the server address, as the server you connect to can immediately see where the connection is coming from (which seems to be the whole point).

One reason to care about your own IP address is if you want to build up a string representing a URL to pass to a client. It is easier to know your own IP than to know your own FQDN. And of course, I am talking about host code that does not require configuration; that is, where you want the software to come up usable on a system other than the one on which it was developed...

I actually use this:
proc myIP {} {
    if { [ catch {
        set ip 127.0.0.1
        set sid [ socket -async [ info hostname ] 22 ]
        set ip  [ lindex [ fconfigure $sid -sockname ] 0 ]
        ::close $sid
    } err ] } {
        catch { ::close $sid }
        puts stderr "myIP error: '$err' on port 22 (sshd). using 127.0.0.1"
    }
    return $ip
}

For my systems, because if they are not running ssh they are as good as dead anyway. Of course, this does not meet my criterion for general usability.

TV (23 juni 2003) This not so quick not so hack worked for me for years on various machines, os-es, nets, modems etc, didn't realy fail, I seem to remember, and isn't too slow:
proc myip {{port 302}} {    # Figure out what this machine's IP address is.
    global myip
    set tss [socket -server tserv $port]
    set ts2 [socket [info hostname] $port]
    set myip [lindex [fconfigure $ts2 -sockname] 0]
    close $tss
    close $ts2
    return $myip
}

It is taken from pcom with one line removed.

CL notes that the case this does not handle nicely is the common corporate one in which /etc/hosts translates hostname as 127.0.0.1. People often do this with laptops or other machines that move around a network.

TV I wouldn't know, I failed to mention the majority of tests have been on some 95+ version of dreaded and used Windows OS, though I used Macs at some point, and tested on early linux. I'll probably be on redhat 2.4.20 now to test.. Sorry for those who got the idea I included linux and was sure that would work, I wasn't.

(after a little while) I happen to be on Linux now, on a modern PC like arch, with upgraded Redhat 8.0, where the above at least seems to generate a sensible IP address, too. It's on a general use university net for the moment, is it possible to have a corporate net where you'd have no tcl fundable IP address equivalent? Maybe you'd just get your proxy's or gateways' address? Maybe when no tcp/ip is used?

CL has been one of those who most frequently questions, "Do you really want that?" As of mid-2003, I'm more sympathetic to those who've answered, "yes".

Several of the methods above are prone to reporting 127.0.0.1 or other undesired answers. The real platform-independent answer is that the address can only be truly known once it's an endpoint of a TCP/IP conversation. At that level, therefore, the most reliable answer is to run
    # This is reporter.tcl.
    socket -server accept 6584
    proc accept {handle origination other} {
        set message "Origination address is $origination."
        puts $message
        puts $handle $message
        close $handle
    }
    vwait forever

on $TARGET machine, then, on the host whose IP one wants,
    telnet $TARGET 6584

or equivalent.

[Edit. 6584 is arbitrary. Explain circumstances in which various methods fail?]

MS: another simple-minded option, if you are connected to the internet
 proc getIp {{target www.google.com} {port 80}} {
     set s [socket $target $port]
     set res [fconfigure $s -sockname]
     close $s
     lindex $res 0
 }

RLE (2015-04-11): The above statement should say: "if you are connected to the internet with a route-able IP address. For anyone behind a NAT gateway, this gives the address of the local Ethernet interface, not the globally route-able IP.

Note, this is what the machine I'm typing this on gives:
$ rlwrap tclsh
 proc getIp {{target www.google.com} {port 80}} {
     set s [socket $target $port]
     set res [fconfigure $s -sockname]
     close $s
     lindex $res 0
 }
% getIp
10.0.0.28
% 

If the IP of interest is actually the local machine's IP, it works perfectly. But more often the IP address desired is the internet visible IP, not the local machine's IP, and those two are not always the same value.

Peter Hansen points out we haven't even started with the complexities SNAT/DNAT, firewalls, ..., might introduce.

2005/06/10 sbron: As mentioned before there is no such thing as "my IP address". Usually you want to know which IP address will be used when contacting a certain host. The next piece of code actually seems to work on linux even if the remote port is not accessible or, more amazing, if the remote host doesn't exist.
 set anyport 5555 ;# Any random port number
 set sock [socket -async $host $anyport]
 puts [fconfigure $sock -sockname]
 close $sock

Again, this is not a general solution because it doesn't work on solaris (which of course is where I needed it!). For a non-existing host or port it just returns 0.0.0.0 on solaris.

The search continues ...

DNS edit

Using DNS root servers

A modification to find the publicly addressable IP of your machine:
    proc ip:external {} {
        #this uses the TCP DNS port of the DNS root-servers. 
        #If these aren't reachable, you probably don't
        #have a working external internet connection anyway.
        set MyIP ""
        foreach a {a b c d e f g h i j k} {
            catch {
                set external [socket $a.root-servers.net 53]
                set MyIP [lindex [fconfigure $external -sockname] 0]
                close $external                                
            } 
            if { ![string equal $MyIP ""] } { break }
        }
        return $MyIP
    }

Depending on your setup, this procedure may take up to six minutes to complete (DNS timeout and route timeout for eleven servers). For me it typically takes a fraction of a second.

Using special-purpose DNS servers

See Public IP.

Parsing ifconfig (*nix) and ipconfig (Windows) edit

Likely you want to know all your IP addresses, not just the one the whole world knows about.

This works nattily on Solaris and Linux, and some poor sod might extend it to work on windoze:
 proc ifConfig { args } {
     if { [ catch {
        set interfaces [ list ]
        if { [ file executable /usr/sbin/ifconfig ] } {
           catch { ::exec /usr/sbin/ifconfig -a } data
        } elseif { [ file executable /sbin/ifconfig ] } {
           catch { ::exec /sbin/ifconfig -a } data
        } else {
           return -code error "can't find 'ifconfig' executable!"
        }
        set fid [ open /etc/hosts r ]
        set hostdata [ read $fid [ file size /etc/hosts ] ]
        ::close $fid
        foreach line [ split $hostdata "\n" ] {
           array set hosts \
              [ list [ lindex $line 0 ] [ lrange $line 1 end ] ]
        }   
        foreach line [ split $data "\n" ] {
           regexp {^[a-z]+\d+} $line if
           if { [ regexp {^\s+inet\s+(?:addr:)?(\S+)} $line -> ip ] } {
              lappend interfaces [ list $if $ip $hosts($ip) ]
           }
        }
     } err ] } {
        return -code error "ifConfig: $err"
     }
     return $interfaces
 }

DKF: That info is probably available through the ipconfig program, though I've no idea what it returns on machines with multiple network addresses.

SDW: DKF, ask and you shall recieve. This script switches between Unix and Windows, and will even detect if your address was obtained from DHCP.
switch $tcl_platform(platform) {
    unix {

     ########################
     # Parse network configuration information
     # for unix-like systems
     ####
     proc ifconfig {} { 
        variable iplist {}

        set buffer [exec /sbin/ifconfig]
        set scan "inet addr:"
        
        set addrlist {}
        foreach line [split $buffer \n] {
            if [regexp HWaddr $line] {
                lappend addrlist [lindex $line 0] [lindex $line end]
            }
            if ![regexp $scan $line] continue
            set s [expr [string first $scan $line 0] + \
                       [string length $scan]]
            set f [expr [string first " " $line $s] - 1]
            
            set addr [string range $line $s $f]        
            
            if { $addr != "127.0.0.1" } {
                lappend addrlist $addr
            }
        }

        foreach {device mac addr} $addrlist {
            set device [string tolower $device]

            if [catch {exec ps ax | grep dhcpcd | grep $device}] {
                set dhcp no
            } else {
                set dhcp yes
            }
            set dhcp [linux_dhcp_device $device]
            lappend iplist [list $device $addr $mac $dhcp]
        }
        return $iplist
      }
    }
    windows {

     #####################
     # Parse network configuration information
     # Windows NT/98/ME/2K/XP
     # (Also nabs the mac number for the network card
     # this is a quick copy and paste from my workstation 
     # inventory script)
     ####
     proc ifconfig {} {
        set iplist {}

        # Load IP info
        
        catch {
            exec ipconfig /all
        } data

        set buffer [split $data \n] 

        set mac_address {}
        foreach line $buffer {
            if [regexp -nocase {Description} $line] {
                set val [string trim [lindex [split $line :] end]]
                lappend mac_addresses $val
            }
            if [regexp -nocase {Physical Address} $line] {
                set val [string trim [lindex [split $line :] end]]
                regsub -all {\-} $val : val
                lappend mac_addresses [string tolower $val]
            }
            if [regexp -nocase {DHCP Enabled} $line] {
                set val [string trim [lindex [split $line :] end]]
                lappend mac_addresses [string tolower $val]                
            }

            if [regexp -nocase {IP Address} $line] {
                set val [string trim [lindex [split $line :] end]]
                lappend mac_addresses $val
            }
        }

        set devices -1
        set ppp 0
        foreach {desc mac dhcp ip} $mac_addresses {
            if [regexp desc "PPP"] {
                set device ppp[incr ppp]
            }  else {
                set device eth[incr devices]
            }
            lappend iplist [list $device $ip $mac $dhcp]
        }
        return $iplist
     }
    }
    default {
       proc ifconfig {} { error "Unsupported Platform" }
    }
}

2005/03/07 skm I kluged a procedure using ipconfig, and had intended to do something later with a dialog to handle a multihomed machine. Later, I didn't work on this anymore because I wanted to use a better method. The dns package looked promising. lately we're using the socket command. (never did finish all of the unit tests. btw, now that I'm using the ActiveState debugger, unit testing is much easier than what I originally did.)
 proc get_ips {} {
    # default ip_list to one element consisting of localhost.
    # If the regexp call succeeds, ip will be set a list of
    # the IPs. If the computer is not multihomed, the list will consist
    # of one element
    set ip_list {}

    # initialize status variables and warning messages
    set warning {}
    set fail 0
    set re_status 0
    # normal
    if {[catch {set ipconfig_pid [open |ipconfig]} err]} {
        # query the host for its actual IP configuration
        # get the stack trace and flatten it into one line to make it log-able
        set localInfo [split $::errorInfo \n]
        set warning "Warning: Unable to query the host for its IP\
                configuration. IP configure returned: $err\
                Stack trace: $localInfo"
    } else {
        # get the data from the ipconfig command
        set results [split [read $ipconfig_pid] \n]
        close $ipconfig_pid
        # loop over the results, searching for IP addresses.
        foreach line $results {
            if {[catch {regexp {IP Address[\. ]+: ([0-9\.]+)}\
                    $line match ip} re_status]} {
                set localInfo [split $::errorInfo \n]
                set warning "Warning: Failed to run regexp on the\
                        the ipconfig output.\
                        Result: $re_status Stack trace: $localInfo"
            } elseif {$re_status == 1} {
                lappend ip_list $ip
            }
        }
    }

    append msg "Harvested IP address(es) $ip_list" " $warning"
    # report msg here
    puts $msg
    # return ip
    if {[llength $ip_list] == 0} {
        return localhost
    } else {
        return $ip_list
    }
 }
 # unit tests
 # 0. run success case
 # 1. Change the call to ipconfig to:  catch {exec ipconfig; error foo}
 # 2. Change the regexp pattern to:  {fooIP Address[\. ]+: ([0-9\.]+)}
 # 3. Append ;error foo to the catch around the regexp

Mike Tuxford adds... Similar to the ifConfig proc above for Linux, when you know the location ifconfig and only need one interface IP, which is perhaps the most common case, the below should work. or you could edit a page on The Tcler's Wiki and your IP will appear on the Recent changes page. ;-)
proc getIP {type bin} {
   set data [exec $bin $type | grep inet]
   return [string range $data [expr [string first ":" $data]+1] \
   [expr [string first " " $data [expr [string first ":" $data]+1]]-1]]
}
puts [getIP ppp0 /sbin/ifconfig]

Using ping edit

CL has another: "ping -c 1 $known_host", and parse the output for origin.

Using web services edit

FW: Or, to see what IP your machine is seen from the outside, do some web scraping:
package require http
proc whatIP {} {
    regexp {Your IP Address:</B><BR>\n([0-9.]*)} [http::data [http::geturl whatismyipaddress.com]] {} ip
    return $ip
}

rdt says that this will probably give you the address of some router, tho :)

tb 2009-08-05 I wrote a script that uses checkip.dyndns.org to wrap your current external IP from there and mail it to a given email address. Because my DDNS client is behind a NAT, it doesn't recognize when my provider hooks off, so I've put this into my [crontab] to send my external IP every 15 minutes.

See mailip.tcl

Using TWAPI edit

A problem with some of the above solutions is that they rely on parsing the output of some other program and hence will not work on localized operating systems. On Windows NT platforms only, TWAPI includes a function to provide IP address information:
package require twapi
twapi::get_ip_addresses

neb 2010/09/03: Using dyndns and other similar options isn't an option for me, as our work proxy winds up blocking things seemingly at random. Plus, it just doesn't feel clean. Parsing ipconfig is iffy, has no guarantees with multihomed machines (including those with virtual NICs). Plus, I have a thing against dependencies =]

Fortunately, on XP-class Windows boxen, it's actually much easier to do it the internal way.

Via twapi or ffidl, you can hit a couple of Windows APIs; one that will tell you the IP address of a specific interface, and another that will work out the interface for a given route. Combining those, we can grab the IP for a given side of the PC, without having to go out to the network (although it needs to be connected)
 package require twapi
 
 puts ips:[lsearch -all -inline -not -exact [::twapi::get_ip_addresses] 127.0.0.1]\n
 
 proc getiptohost hostip {
     package require twapi
         
     set idx [::twapi::get_outgoing_interface $hostip]
     array set iplist [lsearch -all -inline -not -exact [::twapi::get_netif_info $idx -ipaddresses] 127.0.0.1]
 
     if {[info exists iplist(-ipaddresses)]} {
         return [lindex $iplist(-ipaddresses) 0 0]
     } else {
         puts "error"
     }
 }
 
 puts "getting route..."
 puts [getiptohost $::argv]

This proc has a couple of advantages; it doesn't need the internet (it needs a network, else there isn't an IP to return, but it doesn't actually connect to anything to retrieve the IP. It's entirely self-contained),it will ignore disconnected interfaces (so if your lan is unplugged, and you are connected via WiFi, that's the interface you get) and on a multihomed machine, it will ive you the IP for the interface that is pointing to the IP you give it. Also, the IP you give it doesn't have to exist. If you give it a random wild IP, it will just return the IP for the default NIC.
 C:\temp>getip2host 1.1.1.1
 ips:74.125.19.68 192.168.56.1
 getting route...
 74.125.19.68

 C:\temp>getip2host 192.168.56.123
 ips:74.125.19.68 192.168.56.1

 getting route...
 192.168.56.1

It's longer than it probably needs to be. Filtering out 127.0.0.1 is redundant--I just wanted to make sure it failed if it ran into something I haven't thought of.

Using the registry on Windows edit

2005/04/30 sbron: A method that should work on localized Windows too is to get the information from the registry. Based on information provided by kbk in the Tcl Chatroom I made the following code that will obtain most information you may ever want to know about the available network interfaces:
 proc regpath {args} {
     join $args "\\"
 }

 proc ipConfig {} {
     global tcl_platform
     if {$tcl_platform(os) eq "Windows NT"} {
         # Windows NT/2k/XP
         set regroot [regpath HKEY_LOCAL_MACHINE Software \
             Microsoft "Windows NT" CurrentVersion NetworkCards]
         foreach key [registry keys $regroot] {
             set net [regpath $regroot $key]
             set name [registry get $net Description]
             puts "Description=$name"
             set service [registry get $net ServiceName]
             set path [regpath HKEY_LOCAL_MACHINE System \
                 CurrentControlSet Services \
                 $service Parameters Tcpip]
             set vars [registry values $path]
             foreach var $vars {
                 set val [registry get $path $var]
                 puts $var=$val
             }
             puts ""
         }
     } else {
         # Windows 95/98/ME
         set regroot [regpath HKEY_LOCAL_MACHINE System \
             CurrentControlSet Services Class]
         set net [regpath $regroot Net]
         foreach key [registry keys $net] {
             set name [registry get [regpath $net $key] DriverDesc]
             puts "Description=$name"
             set path [regpath $regroot NetTrans $key]
             set vars [registry values $path]
             foreach var $vars {
                 set val [registry get $path $var]
                 puts $var=$val
             }
             puts ""
         }
     }
 }

Unfortunately it seems that dynamic IP addresses are not necessarily stored in the registry, so this code will not be able to find those.

LES: unable to get value "DriverDesc" from key "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Class\Net\NetTrans_0003": File not found.

2005/06/10 sbron: This method isn't the final solution I hoped it would be. I have reverted to scanning the ipconfig output again myself because too many customers reported problems when I used the above code. Maybe I should delete this entry. I was just hoping that someone could use it as a starting point to improve upon.

Using TclX on *nix edit

2005/06/29 CK: what about the following command ?
    host_info addresses [info hostname]

CL believes CK is assuming TclX, which happens to provide host_info.

Using Ceptcl edit

2009-06-06 Stu With Ceptcl >= 0.4 this can be accomplished with the cepResolve command.
% cepResolve [info hostname]
192.168.2.2

% cepResolve google.com -addresses
74.125.45.100 74.125.127.100 74.125.67.100

Using UDP sockets edit

crshults 2014/10/01: This is working for me on Windows 7 right now. Going to try a few other environments later (note: I live in tkcon) -
 package require udp
 set udp_socket [udp_open 15351 reuse]
 chan configure $udp_socket -blocking 0 -buffering none -mcastadd {224.0.0.251} -mcastloop 1 -remote {224.0.0.251 15351}
 chan puts -nonewline $::udp_socket [info hostname]
 chan event $udp_socket readable read_data
 proc read_data {} {
     if {[chan read $::udp_socket] eq [info hostname]} {
         set ::ip_address [lindex [chan configure $::udp_socket -peer] 0]
         proc get_ip_address {} {
             return $::ip_address
         }
         chan close $::udp_socket
    }
 }

Linux command line commands edit

neb 2011/07/18: I was looking for a way to do the same as my previous post but for Linux, and found what looks like a much simpler way:
[~]$ ip route get 64.64.64.64/24
64.64.64.64 via 192.168.0.1 dev wlan0  src 192.168.0.102 
    cache 

AFAICT, this gives the appropriate route to a given IP, picking the appropriate interface, and the 'src' field is the IP for that interface. Although the documentation warns that using 'get' alters the usage statistics for the route, so it may be better to first try looking something up in the route cache:
[~]$ ip route show cache 133.40.3.100
133.40.3.100 via 192.168.0.1 dev wlan0  src 192.168.0.102 
    cache 
133.40.3.100 from 192.168.0.102 via 192.168.0.1 dev wlan0 
    cache 

The 'src' and 'from' fields are the interface ip.

I was going to pull the 'inet addr' from
 ifconfig if

after pulling the if from
 netstat -i

or
 netstat -te

If I wind up having to fall-back or anything, and work that out, I will post it, too; but 'ip route' looks like a much better alternative.

ZB 2009-07-10 For Linux perhaps this will be most convenient solution:

1. Find out all present network interfaces in /proc/net/dev (file format is simple)

2. Use the code below ( found it here ) - can be easily incorporated as a module, for example using SWIG - with interface names (found at step 1) as parameters:
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
 #include <arpa/inet.h>



 int main(int argc, char *argv[]) {
    struct ifreq ifr;
    int sock, j, k;
    char *p, addr[32], mask[32], mac[32];


    if (argc<2) {
        fprintf(stderr,"missing argument, example: eth0\n");
        return 1;
    }


    sock=socket(PF_INET, SOCK_STREAM, 0);
    if (-1==sock) {
        perror("socket() ");
        return 1;
    }


    strncpy(ifr.ifr_name,argv[1],sizeof(ifr.ifr_name)-1);
    ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0';


    if (-1==ioctl(sock, SIOCGIFADDR, &ifr)) {
        perror("ioctl(SIOCGIFADDR) ");
        return 1;
    }
    p=inet_ntoa(((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr);
    strncpy(addr,p,sizeof(addr)-1);
    addr[sizeof(addr)-1]='\0';


    if (-1==ioctl(sock, SIOCGIFNETMASK, &ifr)) {
        perror("ioctl(SIOCGIFNETMASK) ");
        return 1;
    }
    p=inet_ntoa(((struct sockaddr_in *)(&ifr.ifr_netmask))->sin_addr);
    strncpy(mask,p,sizeof(mask)-1);
    mask[sizeof(mask)-1]='\0';


    if (-1==ioctl(sock, SIOCGIFHWADDR, &ifr)) {
        perror("ioctl(SIOCGIFHWADDR) ");
        return 1;
    }
    for (j=0, k=0; j<6; j++) {
        k+=snprintf(mac+k, sizeof(mac)-k-1, j ? ":%02X" : "%02X",
            (int)(unsigned int)(unsigned char)ifr.ifr_hwaddr.sa_data[j]);
    }
    mac[sizeof(mac)-1]='\0';


    printf("\n");
    printf("name:    %s\n",ifr.ifr_name);
    printf("address: %s\n",addr);
    printf("netmask: %s\n",mask);
    printf("macaddr: %s\n",mac);
    printf("\n");


    close(sock);
    return 0;
 }

Using this code one can even prepare package for tcllib, especially for IP-address recognition - but found it 5 minutes ago, so no complaints, that haven't provided it yet ready-to-use. ;)

Something similar for Windows (not tested by myself): here and there (couldn't paste it properly): http://www.codeproject.com/KB/IP/obafindingipinformation.aspx

Windows command line commands edit

Someone malformadly suggested:
 global env
 exec myip [exec ypcat -k hosts | grep $env(HOST) | awk {{print $1}}]

[Lectus] This works on windows XP:
 proc getIP {} {
     set fd [open "|nslookup" r]
     fconfigure $fd -buffering line
     gets $fd line
     gets $fd line
     return [string trim [string range $line 9 end]]        
 }

But there are better cross-platform code in this page. ;D

Editing a page on the Tcler's wiki ;-) edit

Lars H: Given that web browsers tend to be more commonly available than telnet these days, one alternative to such a method would be to open the Graffiti page :-), edit (make a trivial change and save), and then look at Recent changes to see the IP number of your computer. (I've found this handy on a couple of occasions when I've plugged my laptop into the local network where I was visiting, but had to figure out a valid IP number for that network myself. ;-)

Discussion edit

[Explain why NAT and firewalls can make things more difficult.]

2005/06/29 SRIV: Perhaps STUN is the answer here [1]

[Explain why a web proxy can make using web-services to determine your address difficult.]

HolgerJ 2015-03-18: I wonder why on Java 'getNetworkInterfaces()' works so easily. Couldn't the code behind it be put into Tcl core?

See also edit

Threads on comp.lang.tcl

  • [3]
  • [4] (link broken 2015-03-18)