;; 
;;=====================================================================================----- 
;; 
;;FUNCTION       GetResourceBySrv() 
;; 
;;ACTION         Returns an array of DNS SRV record data 
;; 
;;AUTHOR         Glenn Barnas 
;; 
;;VERSION        1.1  - 2009/11/11 
;; 
;;HISTORY        1.0  - 2009/01/09 - Initial Release 
;;		 1.1  - 2009/11/11 - Update to catch errors when A-records are not returned 
;; 
;;SYNTAX         GetResourceBySrv(IpAddr [, Short] [, Type] [, NoSlaves]) 
;; 
;;PARAMETERS     IpAddr - REQUIRED - String 
;;               - IP string of host to lookup. Can be null if Short is true (IP is ignored) 
;; 
;;		 Short - OPTIONAL - Integer 
;;               Flag requesting short processing. Returns the entire array instead of 
;;               the matching hostname(s). Short is forced true if a Type value is specified. 
;; 
;;		 Type - OPTIONAL - String 
;;               - Specifies the type of SRV record to return. The default type is the custom 
;;               record "_swdist._tcp" (easily changed) 
;; 
;;		 NoSlaves - OPTIONAL - Integer 
;;               A binary value that, when true, excludes slaves from the list. Useful 
;;		 when a slave needs to find a peering partner. Only valid when Short=0 
;; 
;;RETURNS        Array of Arrays containing DNS SRV record data. 
;;		    Array format is: Hostname, IP, Weight, Priority, Service_Port 
;; 
;;		 By default, the UDF returns SRV records of the type "_swdist._tcp". 
;;		 It uses the Weight value as a CIDR netmask value and only returns 
;;		 records where the IpAddr argument is in the record's subnet. It also 
;;		 sorts the data from most-specific to least specific subnet. 
;; 
;;		 If Short is True, all SRV record data is returned for external 
;;		 processing. This is useful for obtaining other SRV record types. 
;; 
;;REMARKS        In a general form, this UDF can return a set of DNS SRV records by 
;;		 specifying a Type parameter. This is useful for locating specific  
;;		 network resources, such as the PDC, all DCs, all LDAP servers, and 
;;		 so forth. In this manner, only the Type parameter is significant, 
;;		 and the UDF lends itself to many generic applications. 
;; 
;;		 In the target application, this UDF retrieves specific, custom  
;;		 SRV records ("_swdist._tcp") which identify a hierarchy of file 
;;		 server resources. There are three types of file server: 
;;		  - Master, with a priority and weight of 0 
;;		  - Secondary, with a priority of 1 and a weight of 1-31 
;;		  - Slave, with a priority of 2 and a weight of 1-31 
;;		 The slave server provides resources for a single location, the  
;;		 secondary server supports a region or scope of subnets, and the 
;;		 master is queried as a last resort when no closer resource is available. 
;;		 Data is replicated from the master to secondary and slave systems. 
;; 
;;		 The Priority value is not used by this UDF. The data returned is sorted 
;;		 by the maskbits value, so that the resource server closest to the  
;;		 client can be tried first. For example, the master site has a weight 
;;		 of 0, a secondary site has an address of 172.16.9.43 and a weight of 20, 
;;		 and the slave site has an address of 172.16.12.20 and a weight of 24. 
;;		 If a query is made with an address of 172.16.12.95, all three records 
;;		 will be returned, since that address exists in each of the three 
;;		 networks. Since the Weight values represent the maskbits, the data 
;;		 is sorted as Slave (24), Secondary (20) and then Master (0). 
;;		 The application will try to access the resource on the local Slave 
;;		 server. If it is not found, or is unavailable, the application can 
;;		 try the next resource provider (the Secondary server). Failing that, 
;;		 the Master server can be queried. 
;; 
;;		 This method of resource localization is especially useful when  
;;		 common sets of data (like software installation shares) are replicated 
;;		 from the master site out to regional offices or remote branch locations. 
;;		 With slow WAN links, the closest server is always tried first. Of 
;;		 course, you must create the SRV records that you wish to use!  
;; 
;;DEPENDENCIES   WScript 
;; 
;;TESTED WITH    W2K, WXP, W2K3, W2K8, Vista 
;; 
;;EXAMPLES       ; Get all domain controllers from local domain 
;;		 $DcList = GetResourceBySrv('', 1, '_ldap._tcp.dc._msdcs') 
;;		  
;;		 ; Get primary domain controller from local domain 
;;		 $PDCe = GetResourceBySrv('', 1, '_ldap._tcp.pdc._msdcs') 
;;		  
;;		 ; Get the list of replicated file servers closest to me 
;;		 $MyIP = Join(Split(@IPADDRESS0, ' '), '') 
;;		 $Servers = GetResourceBySrv($MyIP) 
;;		 $Tag = 0 
;;		 For Each $ServerRecord in $Servers 
;;		   If Not $Tag  
;;		     $ServerPath = '\\' + $ServerRecord[0] + '\share\folder\file.ext' 
;;		     If Exist $ServerPath 
;;		       ; open file, run command, etc.. 
;;		       If Not @ERROR 
;;		         $Tag = 1	; set Tag on successful process, prevent further attempts 
;;		       EndIf 
;;		     EndIf 
;;		   EndIf 
;;		 Next 
;;		  
;;		  
; 
Function GetResourceBySrv($_IpAddr, OPTIONAL $_Short, OPTIONAL $_Type, OPTIONAL $_NoSlaves)
 
  Dim $_, $_A			; Temp vars 
  Dim $_I, $_J, $_R		; Index pointers 
;  Dim $_K, $_aR2		; Index ptr & return array 
  Dim $_aR			; Return strings 
  Dim $_oExec			; WScript object 
  Dim $_Rec[4]			; Working record 
  Dim $_aRtn[0]			; array of all SRV records 
  Dim $_aAdd			; array of address values 
  Dim $_DVal[33]		; array of decimal network values 
  Dim $_Network			; network and mask decimal values 
  Dim $_Hosts			; # of hosts in the subnet 
 
  If Not $_Type
    $_Type = '_swdist._tcp'	; default SRV record type 
  Else
    $_Short = 1			; force short processing for non-default types 
  EndIf
 
  $_NoSlaves = Val($_NoSlaves)
 
  ; Use WScript to execute the command and check the result 
  $_oExec = CreateObject("WScript.Shell").Exec('nslookup -type=all ' + $_Type)
  If Not VarType($_oExec) = 9
    $GetResourceBySrv = ''
    Exit 10
  EndIf
 
  $_J = -1			; pointer for returned data 
 
  ; get the command result and extract what is needed 
  $_aR = Split($_oExec.StdOut.ReadAll, @CRLF)
  ; enumerate the resulting data and place the data into the record fields 
  For $_I = 0 To UBound($_aR)
    If InStr($_aR[$_I], 'SRV service location:')
      $_ = Trim(Split($_aR[$_I + 1], '=')[1])
      $_Rec[3] = $_
      $_ = Trim(Split($_aR[$_I + 2], '=')[1])
      $_Rec[2] = $_
      $_ = Trim(Split($_aR[$_I + 3], '=')[1])
      $_Rec[4] = $_
      $_ = Trim(Split($_aR[$_I + 4], '=')[1])
      $_Rec[0] = $_
 
     ; The original query should return the A-record lookups at the end of the command 
     ; The code below will search the in-memory data for the IP lookups. If - on the off chance 
     ; they don't exist, the IP address will be obtained via NSLookup 
 
     ; skip over the current data and locate the A record reference to get the IP Addr 
      $_A = AScan($_aR, $_Rec[0], $_I + 5, , 1)
      If $_A = -1
; NSLookup can take an extended amount of time - only return IPs for hosts in memory 
;        ; not found in-memory - run NSLookup to obtain the IP address 
;        $_oExec = CreateObject("WScript.Shell").Exec('nslookup ' + $_Rec[0]) 
;        $_aR2 = Split($_oExec.StdOut.ReadAll, @CRLF) 
;        ; skip the first two lines, which return the NS address 
;        For $_K = 2 to UBound($_aR2) 
;          If InStr($_aR2[$_K], $_Rec[0]) 
;            $_Rec[1] = Trim(Split($_aR2[$_K + 1], ':')[1]) 
;            $_K = 999999 
;          EndIf 
;        Next 
        $_Rec[1] = '0.0.0.0'
      Else
        $_Rec[1] = Trim(Split($_aR[$_A], '=')[1])
      EndIf
 
      ; update the array of SRV records 
      $_J = $_J + 1		; increment the pointer & resize the array 
      ReDim Preserve $_aRtn[$_J]
      $_aRtn[$_J] = $_Rec	; add the new data record to the array 
 
      $_I = $_I + 4		; skip the processed input data 
 
    EndIf
  Next
 
  If $_Short			; do we just return the data? 
    $GetResourceBySrv = $_aRtn
    Exit 0
  EndIf
 
 
; ===================================================================================== 
 
  ; Enumerate the SRV records, using the Weight as a CIDR netmask 
  ; Calculate the network and subnet size 
  ; order the results that match the specified network by increasing subnet size 
 
  $_R = -1			; init index pointer 
  Dim $_aWork[0]		; create working array 
  $_DVal[32] = 1.0		; init bit values 
  For $_I = 31 to 1 Step -1
    $_DVal[$_I] = $_DVal[$_I + 1] * 2.0
  Next
 
  ; Convert the supplied IP address to a double value representing the decimal address 
  $_aAdd = Split($_IpAddr, '.')
  $_IpAddr = (CDbl($_aAdd[0]) * 16777216.0) + (CDbl($_aAdd[1]) * 65536.0) + (CDbl($_aAdd[2]) * 256.0) + (CDbl($_aAdd[3]) * 1.0)
 
  ; Enumerate the SRV records in the array generated above 
  For Each $_Rec in $_aRtn
    ; Data Format: Hostname, IP, Mask, Priority, Service_Port 
 
    ; Process if we have a primary/secondary and noslaves is set, or if noslaves is not set at all 
    If ($_NoSlaves and $_Rec[3] < 2) Or Not $_NoSlaves
      ; Set the number of hosts in the defined network base on the netmask 
      $_Hosts = 1.0
      For $_J = 31 to Val($_Rec[2]) Step -1
        $_Hosts = ($_Hosts * 2.0)
      Next
      ; convert server IP address to decimal value 
      $_aAdd = Split($_Rec[1], '.')
      $_ = (CDbl($_aAdd[0]) * 16777216.0) + (CDbl($_aAdd[1]) * 65536.0) + (CDbl($_aAdd[2]) * 256.0) + (CDbl($_aAdd[3]) * 1.0)
 
      ; Convert the Server IP address to its local network address based on the supplied maskbits 
      $_Network = 0.0
      For $_J = 1 to $_Rec[2]	; process each maskbit to value if present 
        If $_ >= CDbl($_DVal[$_J])
          $_Network = $_Network + $_DVal[$_J]
          $_ = $_ - $_DVal[$_J]	; remove the processed value from the address 
        EndIf
      Next
 
      ; If the target address is between the network start and end addresses, add the record 
      ; to the array to be returned 
      If $_IPAddr >= $_Network And $_IpAddr < ($_Network + $_Hosts)
        $_R = $_R + 1
        ReDim Preserve $_aWork[$_R]
        $_aWork[$_R] = $_Rec
      EndIf
 
    EndIf
 
  Next
 
  ; all matching records have been found. Sort the data into the return array 
  ; by largest to smallest maskbit values (closest to farthest). If two servers 
  ; have identical maskbit values, they are returned in no specific order. 
  ReDim $_aRtn[UBound($_aWork)]
  $_R = 0			; output pointer 
  For $_I = 31 to 0 Step -1	; cycle through all possible maskbit values 
    For $_J = 0 to UBound($_aWork)
      If $_aWork[$_J][2] = $_I
        $_aRtn[$_R] = $_aWork[$_J]
        $_R = $_R + 1
      EndIf
    Next
  Next
 
  If UBound($_aRtn) <> -1		; see if we have some data to return 
    $GetResourceBySrv = $_aRtn
    Exit 0				; return data array and exit success 
  Else
    Exit 1				; return nothing and exit with error 
  EndIf
 
EndFunction