resolv.tcl

Go to the documentation of this file.
00001 /*  resolv.tcl - Copyright (c) 2002 Emmanuel Frecon <emmanuel@sics.se>*/
00002 /* */
00003 /*  Original Author --  Emmanuel Frecon - emmanuel@sics.se*/
00004 /*  Modified by Pat Thoyts <patthoyts@users.sourceforge.net>*/
00005 /* */
00006 /*   A super module on top of the dns module for host name resolution.*/
00007 /*   There are two services provided on top of the regular Tcl library:*/
00008 /*   Firstly, this module attempts to automatically discover the default*/
00009 /*   DNS server that is setup on the machine that it is run on.  This*/
00010 /*   server will be used in all further host resolutions.  Secondly, this*/
00011 /*   module offers a rudimentary cache.  The cache is rudimentary since it*/
00012 /*   has no expiration on host name resolutions, but this is probably*/
00013 /*   enough for short lived applications.*/
00014 /* */
00015 /*  -------------------------------------------------------------------------*/
00016 /*  See the file "license.terms" for information on usage and redistribution*/
00017 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00018 /*  -------------------------------------------------------------------------*/
00019 /* */
00020 /*  $Id: resolv.tcl,v 1.9 2004/01/25 07:29:39 andreas_kupries Exp $*/
00021 
00022 package require dns 1.0;                /*  tcllib 1.3*/
00023 
00024 namespace ::resolv {
00025     variable version 1.0.3
00026     variable rcsid {$Id: resolv.tcl,v 1.9 2004/01/25 07:29:39 andreas_kupries Exp $}
00027 
00028     namespace export resolve init ignore hostname
00029 
00030     variable R
00031     if {![info exists R]} {
00032         array  R =  {
00033             initdone   0
00034             dns        ""
00035             dnsdefault ""
00036             ourhost    ""
00037             search     {}
00038         }
00039     }
00040 }
00041 
00042 /*  -------------------------------------------------------------------------*/
00043 /*  Command Name     --  ignore*/
00044 /*  Original Author  --  Emmanuel Frecon - emmanuel@sics.se*/
00045 /* */
00046 /*  Remove a host name resolution from the cache, if present, so that the*/
00047 /*  next resolution will query the DNS server again.*/
00048 /* */
00049 /*  Arguments:*/
00050 /*     hostname - Name of host to remove from the cache.*/
00051 /* */
00052 ret  ::resolv::ignore ( type hostname ) {
00053     variable Cache
00054     catch {unset Cache($hostname)}
00055     return
00056 }
00057 
00058 /*  -------------------------------------------------------------------------*/
00059 /*  Command Name     --  init*/
00060 /*  Original Author  --  Emmanuel Frecon - emmanuel@sics.se*/
00061 /* */
00062 /*  Initialise this module with a known host name.  This host (not mandatory)*/
00063 /*  will become the default if the library was not able to find a DNS server.*/
00064 /*  This command can be called several times, its effect is double: actively*/
00065 /*  looking for the default DNS server setup on the running machine; and*/
00066 /*  emptying the host name resolution cache.*/
00067 /* */
00068 /*  Arguments:*/
00069 /*     defaultdns   - Default DNS server*/
00070 /* */
00071 ret  ::resolv::init ( optional defaultdns ="" , optional search ={)} {
00072     variable R
00073     variable Cache
00074 
00075     # Clean the resolver cache
00076     catch {unset Cache}
00077 
00078     /*  Record the default DNS server and search list.*/
00079      R = (dnsdefault) $defaultdns
00080      R = (search) $search
00081 
00082     /*  Now do some intelligent lookup.  We do this on the current*/
00083     /*  hostname to get a chance to get back some (full) information on*/
00084     /*  ourselves.  A previous version was using 127.0.0.1, not sure*/
00085     /*  what is best.*/
00086      res =  [catch [list exec nslookup [info hostname]] lkup]
00087     if { $res == 0 } {
00088      l =  [split $lkup]
00089      nl =  ""
00090     foreach e $l {
00091         if { [string length $e] > 0 } {
00092         lappend nl $e
00093         }
00094     }
00095 
00096         /*  Now, a lot of mixture to arrange so that hostname points at the*/
00097         /*  DNS server that we should use for any further request.  This*/
00098         /*  code is complex, but was actually tested behind a firewall*/
00099         /*  during the SITI Winter Conference 2003.  There, strangly,*/
00100         /*  nslookup returned an error but a DNS server was actually setup*/
00101         /*  correctly...*/
00102          hostname =  ""
00103      len =  [llength $nl]
00104     for {  i =  0 } { $i < $len } { incr i } {
00105          e =  [lindex $nl $i]
00106         if { [string match -nocase "*server*" $e] } {
00107          hostname =  [lindex $nl [expr {$i + 1}]]
00108                 if { [string match -nocase "UnKnown" $hostname] } {
00109                      hostname =  ""
00110                 }
00111         break
00112         }
00113     }
00114 
00115     if { $hostname != "" } {
00116          R = (dns) $hostname
00117     } else {
00118             for {  i =  0 } { $i < $len } { incr i } {
00119                  e =  [lindex $nl $i]
00120                 if { [string match -nocase "*address*" $e] } {
00121                      hostname =  [lindex $nl [expr {$i + 1}]]
00122                     break
00123                 }
00124             }
00125             if { $hostname != "" } {
00126                  R = (dns) $hostname
00127             }
00128     }
00129     }
00130 
00131     if {$R(dns) == ""} {
00132          R = (dns) $R(dnsdefault)
00133     }
00134 
00135 
00136     /*  Start again to find our full name*/
00137      ourhost =  ""
00138     if {$res == 0} {
00139          dot =  [string first "." [info hostname]]
00140         if { $dot < 0 } {
00141             for {  i =  0 } { $i < $len } { incr i } {
00142                  e =  [lindex $nl $i]
00143                 if { [string match -nocase "*name*" $e] } {
00144                      ourhost =  [lindex $nl [expr {$i + 1}]]
00145                     break
00146                 }
00147             }
00148             if { $ourhost == "" } {
00149                 if { ! [regexp {\d+\.\d+\.\d+\.\d+} $hostname] } {
00150                      dot =  [string first "." $hostname]
00151                      ourhost =  [format "%s%s" [info hostname] \
00152                                      [string range $hostname $dot end]]
00153                 }
00154             }
00155         } else {
00156              ourhost =  [info hostname]
00157         }
00158     }
00159 
00160     if {$ourhost == ""} {
00161          R = (ourhost) [info hostname]
00162     } else {
00163          R = (ourhost) $ourhost
00164     }
00165 
00166 
00167      R = (initdone) 1
00168 
00169     return $R(dns)
00170 }
00171 
00172 /*  -------------------------------------------------------------------------*/
00173 /*  Command Name     --  resolve*/
00174 /*  Original Author  --  Emmanuel Frecon - emmanuel@sics.se*/
00175 /* */
00176 /*  Resolve a host name to an IP address.  This is a wrapping procedure around*/
00177 /*  the basic services of the dns library.*/
00178 /* */
00179 /*  Arguments:*/
00180 /*     hostname - Name of host*/
00181 /* */
00182 ret  ::resolv::resolve ( type hostname ) {
00183     variable R
00184     variable Cache
00185 
00186     # Initialise if not already done. Auto initialisation cannot take
00187     # any known DNS server (known to the caller)
00188     if { ! $R(initdone) } { init }
00189 
00190     # Check whether this is not simply a raw IP address. What about
00191     # IPv6 ??
00192     # - We don't have sockets in Tcl for IPv6 protocols - [PT]
00193     #
00194     if { [regexp {\d+\.\d+\.\d+\.\d+} $hostname] } {
00195     return $hostname
00196     }
00197 
00198     # Look for hostname in the cache, if found return.
00199     if { [array names ::resolv::Cache $hostname] != "" } {
00200     return $::resolv::Cache($hostname)
00201     }
00202 
00203     # Scream if we don't have any DNS server setup, since we cannot do
00204     # anything in that case.
00205     if { $R(dns) == "" } {
00206     return -code error "No dns server provided"
00207     }
00208 
00209     set R(retries) 0
00210     set ip [Resolve $hostname]
00211 
00212     # And store the result of resolution in our cache for further use.
00213     set Cache($hostname) $ip
00214 
00215     return $ip
00216 }
00217 
00218 /*  Description:*/
00219 /*   Attempt to resolve hostname via DNS. If the name cannot be resolved then*/
00220 /*   iterate through the search list appending each domain in turn until we*/
00221 /*   get one that succeeds.*/
00222 /* */
00223 ret  ::resolv::Resolve (type hostname) {
00224     variable R
00225     set t [::dns::resolve $hostname -server $R(dns)]
00226     ::dns::wait $t;                       # wait with event processing
00227     set status [dns::status $t]
00228     if {$status == "ok"} {
00229         set ip [lindex [::dns::address $t] 0]
00230         ::dns::cleanup $t
00231     } elseif {$status == "error"
00232               && [::dns::errorcode $t] == 3 
00233               && $R(retries) < [llength $R(search)]} {
00234         ::dns::cleanup $t
00235         set suffix [lindex $R(search) $R(retries)]
00236         incr R(retries)
00237         set new [lindex [split $hostname .] 0].[string trim $suffix .]
00238         set ip [Resolve $new]
00239     } else {
00240         set err [dns::error $t]
00241         ::dns::cleanup $t
00242         return -code error "dns error: $err"
00243     }
00244     return $ip
00245 }
00246 
00247 /*  -------------------------------------------------------------------------*/
00248 
00249 package provide resolv $::resolv::version
00250 
00251 /*  -------------------------------------------------------------------------*/
00252 /*  Local Variables:*/
00253 /*    indent-tabs-mode: nil*/
00254 /*  End:*/
00255 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1