'  Author:  Steven Manross
'  Purpose: Identify networking settings including speed and duplex for Windows based systems through the registry, and WMI.
'  Create Date: ~8/15/2007 (made into a seperate script for public consumption on 12/7/2010)
'  Version: 2.2
'  Modified:
'    2010/12/10 -- Simplified Logic in Enumeration routines and added BRCMndi\params for older Broadcom Drivers
'  
'  Intel and Broadcom cards were mostly tested in Proliant servers, and a few Dell Optiplex models, along with a few HP Business Client Desktop models
'  Please report inconsistencies with any network cards you encounter to Steven at Manross dot net (additions to the detection algorhythms are sometimes necessary because Driver vendors are allowed to make certain registry keys named whatever they want)   :(
'  --> example data from the Windows registry may be required to troublsehoot your issues:
'
'  If you are having issues, Export these 2 Registry Keys and send them to the email address above:
'
'  The Network Devices Class key (mostly driver data -- contains no personally identifiable data):
'  HKLM\System\Currentcontrolset\Control\Class\{4D36E972-E325-11CE-BFC1-08002be10318}
'
'  and
'  
'  PNP Key to enumerate found devices (less likely to be needed -- lists hardware in your system)
'  HKLM\System\CurrentControlSet\Control\DeviceClasses\{ad498944-762f-11d0-8dcb-00c04fc3358c}
'
'  Tested with Intel, Broadcom, and a few Realtek cards
'
'  Comments, Kudos and criticism are welcome.
'
'  Syntax:
'
'  cscript getspeedduplex.vbs
'
'  cscript getspeedduplex.vbs COMPUTERNAME1 COMPUTERNAME2 COMPUTERNAME3
'
'  Sample Output:
'
' COMPUTER1
'   Intel(R) 82567V-2 Gigabit Network Connection
'     808610CE -- 00:1C:XX:XX:XX:XX = Auto-negotiate 1000Mbps
'       10.2.1.30 - 255.255.255.0 - 10.2.1.1
'       10.2.1.31 - 255.255.255.0 - 0.0.0.0
'       10.2.1.32 - 255.255.255.0 - 0.0.0.0
'       10.2.1.33 - 255.255.255.0 - 0.0.0.0
'       10.2.1.34 - 255.255.255.0 - 0.0.0.0
' COMPUTER2
'   Intel(R) 82567LM-2 Gigabit Network Connection
'     808610CC -- 00:1C:XX:XX:XX:XX = Auto-negotiate 1000Mbps
'       10.2.1.35 - 255.255.255.0 - 10.2.1.1
'       10.2.1.29 - 255.255.255.0 - 0.0.0.0
' COMPUTER3
'   Intel(R) 82567V-2 Gigabit Network Connection
'     808610CE -- 00:1C:XX:XX:XX:XX = Auto Detect
'       10.2.1.15 - 255.255.255.0 - 10.2.1.1
'       10.2.1.16 - 255.255.255.0 - 0.0.0.0
'       10.2.1.17 - 255.255.255.0 - 0.0.0.0
'       10.2.1.18 - 255.255.255.0 - 0.0.0.0

On Error Resume Next
Const HKLM = &H80000002
if WScript.Arguments.Count > 0 Then
  For Each computer in WScript.Arguments
    GetSpeedDuplex(computer)
  Next
Else
  Set WshShell = CreateObject("WScript.Shell")
  GetSpeedDuplex(WshShell.ExpandEnvironmentStrings("%COMPUTERNAME%"))
End If
WScript.Quit(0)

Function GetSpeedDuplex(pcname)
  On Error Resume Next
  WScript.Echo pcname


  Set objRegistry = GetObject("winmgmts:\\" & pcname & "\root\default:StdRegProv")  
  if Err.Number <> 0 Then
    WScript.Echo "  Error connecting to WMI for " & pcname  & " -- " & Err.Description
    GetSpeedDuplex = 0
    Exit Function
  End If

  objRegistry.EnumKey HKLM, "System\CurrentControlSet\Control\DeviceClasses\{ad498944-762f-11d0-8dcb-00c04fc3358c}", arrPNPNicInstanceKeys
  if Err.Number <> 0 Then
    WScript.Echo "Error enuming PNPDevice subkey for speed and duplex check: " & pcname  & " -- " & Err.Description
    GetSpeedDuplex = 0
    Exit Function
  End If

  xx = 0

  'check DeviceClasses registry keys
  'prune out ghost entries (seriously --  Symantec Ghost leaves around drivers for NICs that don't exist in PNP -- but look like they are there if you are just checking the drivers and registry)
  pnp_linked = Array()

  for each pnpnicinstance in arrPNPNicInstanceKeys
    objRegistry.EnumKey HKLM, "System\CurrentControlSet\Control\DeviceClasses\{ad498944-762f-11d0-8dcb-00c04fc3358c}\" & pnpnicinstance, arrPNPNicDeviceKeys
    if Err.Number <> 0 Then
      Wcript.Echo "System\CurrentControlSet\Control\DeviceClasses\{ad498944-762f-11d0-8dcb-00c04fc3358c}\" & pnpnicinstance
      WScript.Echo "  Error connecting to PNP Instance key of Network Class -- " & Err.Description
      GetSpeedDuplex = 0
      Exit Function
    End If
    'WScript.Echo "pnpinst: " &pnpnicinstance
    for each  pnpnicdev in arrPNPNicDeviceKeys
      if pnpnicdev <> "Control" Then 
        objRegistry.GetDWORDvalue HKLM, "System\Currentcontrolset\Control\DeviceClasses\{ad498944-762f-11d0-8dcb-00c04fc3358c}\" & pnpnicinstance & "\" & pnpnicdev & "\Control", "Linked",is_pnp_linked
        if is_pnp_linked = 1 Then
          nicguid =  Right(pnpnicdev,Len(pnpnicdev)-1)
          'WScript.Echo "pnpdevice: " & pnpnicdev
          'WScript.Echo "nicguid: " & nicguid
          '' this is kind of interesting because certain broadcom based NICs in Proliant Servers actually have the Device ID and Manufacturer ID reversed in the PNP String (If I recall correctly this was in G5s)
          ReDim Preserve pnp_linked(xx)
          pnp_linked(xx) = nicguid
          xx = xx + 1
        End If
        is_pnp_linked = 0                        
      End If
    Next
  Next
  objRegistry.EnumKey HKLM, "System\Currentcontrolset\Control\Class\{4D36E972-E325-11CE-BFC1-08002be10318}", arrSubKeys  
  if Err.Number <> 0 Then
    WScript.Echo "Error enumerating network adapter sub keys for " & pcname  & " -- " & Err.Description
    GetSpeedDuplex = 0
    Exit Function
  End If
  
  rtnval = nic_status(pcname,nicnums) 'do this once 'chenged 20080204 to traverse all nics and then report back a data structure of all nic info.
                                      '  then this sub can see all the data, and nic_status can prune old data (nics that have changed and are reporting multiple IPs, etc)
                                      'plus allow for speed improvement in nic data retrieval (roughly halve the time it takes to do NIC lookup).

  base_network_classes_key = "System\Currentcontrolset\Control\Class\{4D36E972-E325-11CE-BFC1-08002be10318}\"
  'check Network Classes Key
  found_a_nic = 0
  For Each subkey In arrSubKeys  
    Err.Clear 'clear spurrious errors
    nicnumb = CStr(CLng(subkey))
    'WScript.Echo "  nicnumb: " & nicnumb
    if nicnums.Exists(nicnumb) Then 'only look at valid macaddressed nics (speed improvement for remote sites)
      'WScript.Echo "subkey = " & subkey
      objRegistry.GetStringvalue HKLM, base_network_classes_key & subkey, "NetCfgInstanceId",InstanceName
      found_valid_instance = 0
      'WScript.Echo "  " & subkey & " - " & InstanceName
      For Each nicinstance In pnp_linked
        'I'm finding that some nics are either in the registry and WMI without an associated nic, or that nic's driver isn't installed.
        '  this code is to get rid of the info for those nics that aren't totally installed.
        'as well, some nics may have been installed at some time and now are not, so I changed the old way to query what I think is the PNP registration of the currently installed NICs
        ' I.E. Symantec Ghost
        If nicinstance = InstanceName Then
          'WScript.Echo "    Found Valid Instance!"
          found_valid_instance = 1
        End If
      Next

      if found_valid_instance = 1 Then  
        objRegistry.GetStringvalue HKLM, base_network_classes_key & subkey, "ComponentId",strValue  
        if Err.Number <> 0  and Err.Number <> 13 Then
          WScript.Echo "  Error extracting key value for the 'ComponentID' in " & subkey & " on " & pcname  & " -- " & Hex(Err.Number)
          GetSpeedDuplex = 0
          Exit Function
        Else
          Err.Clear
        End If
        if not isnull(strValue) then  
          strValue = lcase(strValue)
          'WScript.Echo "    " & strValue  
          if Left(strValue,8) = "pci\ven_" Then
            section = UCase(left(replace(replace(strValue, "pci\ven_", ""), "&dev_", ""), 8))  
          ElseIf Left(strValue,8) = "b06bdrv\" Then
            section = UCase(left(replace(strValue, "b06bdrv\l2nd&pci_", ""), 8))  
            'WScript.Echo "section = " & section
          Else
            'Wscript.Echo "Error getting NIC Number from ComponentID (which is normal for non-NICs like the MS L2TP transport, etc)"
          End If

          found_vendor = 0
          ndiparamsrtn = 0
          
          '20101210 -- redesigned the ndiparams stuff to allow array of ndi_params_key_names and simplify code, added BRCMndi\params for older NetExtreme II card drivers
          ndi_params_key_names = Array("ndi\Params","ndi\SavedParams","BRCMndi\Params")
          
          for each ndiparamskey in ndi_params_key_names 
            'WScript.Echo ndiparamskey
            rtn = objRegistry.EnumValues (HKLM, base_network_classes_key & subkey & "\" & ndiparamskey,"",ndiparamnames,ndiparamtypes)
            if Err.Number = 0 and rtn = 0 Then
              'WScript.Echo "enum of " & ndiparamskey & " succeeded"
              '20101103 - Duplex Mode added for RealTek GBE (in Pavilion s5570t)
              speed_duplex_key_names = Array("SpeedDuplex","*SpeedDuplex","RequestedMediaType","req_medium","ConnectionType","DuplexMode")
              sd_key_name = ""
              for each sd_key_name_try in speed_duplex_key_names
                rtn = objRegistry.EnumValues (HKLM, "System\Currentcontrolset\Control\Class\{4D36E972-E325-11CE-BFC1-08002be10318}\" & subkey & "\" & ndiparamskey & "\" & sd_key_name_try,"",ndiparamnames,ndiparamtypes)
                if Err.Number = 0 and rtn = 0 Then
                  'WScript.Echo "  Found: " & sd_key_name_try


                  'WScript.Echo "found vendor!  " & "(" & section & ")" & subkey
                  objRegistry.GetStringValue HKLM, base_network_classes_key & subkey,"DriverDesc",nic_name
                  WScript.Echo "  " & nic_name
                  objRegistry.GetStringValue HKLM, base_network_classes_key & subkey,"ProviderName",vendorname
                  'WScript.Echo "vendorname: " & vendorname

                  objRegistry.GetStringvalue HKLM, base_network_classes_key & subkey, sd_key_name_try,speedduplex
     
                  objRegistry.EnumValues HKLM, base_network_classes_key & subkey & "\" & ndiparamskey & "\" & sd_key_name_try & "\enum",sdenumarray,sdenumtypes
                  For Each sdenumvalname in sdenumarray
                    'WScript.Echo sdenumvalname
                    if sdenumtypes(0) = 1 Then
                      objRegistry.GetStringValue HKLM, base_network_classes_key & subkey & "\" & ndiparamskey & "\" & sd_key_name_try & "\enum",sdenumvalname,sdval
                      if sdval <> "" Then
                        if speedduplex = sdenumvalname Then
                          speedduplex_desc = sdval
                          found_a_nic = 1
                          'WScript.Echo "sdval: " & sdval
                        End If
                      End If
                   End If
                  Next
        
                  if speedduplex <> "" Then 
                    WScript.Echo "    " & section & " -- " & nicnums.Item(nicnumb).Item("macaddr") & " = " & speedduplex_desc
                    speedduplex_desc = ""
                  End If
                  For Each ipaddr in nicnums.Item(nicnumb).Item("ipaddresses").Keys
                    WScript.Echo "      " & ipaddr & " - " & nicnums.Item(nicnumb).Item("ipaddresses").Item(ipaddr).Item("subnetmask") & " - " & nicnums.Item(nicnumb).Item("ipaddresses").Item(ipaddr).Item("ipgateway")
                  Next
                  sdenumarray = Nothing
                  sdenumtypes = Nothing
                  Exit For
                Else
                  Err.Clear
                End If
              Next
              Exit For
            Else
              Err.Clear
            End If
          Next
          If found_a_nic = 0 Then
            WScript.Echo "  no data for NIC: " & section
          End If
        End if  
      End if  
    End If
  Next  
  Set nicnums = Nothing
  if found_a_nic = 0 Then
    WScript.Echo "Error finding NIC (No Network Adapters found)!"
  End If
  GetSpeedDuplex = 1
End Function  

Function nic_status (pcname,nicdict)
  On Error Resume Next
  Set Wmi = GetObject("WinMgmts://" & pcname)
  if Err.Number = 0 Then
    Set Computers = Wmi.ExecQuery("SELECT * FROM Win32_NetworkAdapterConfiguration",,48)
    if Err.Number <> 0 Then
      WScript.Echo "  Error in WMI Query: " & Err.Number
      nic_status = 0 
      Err.Clear
      Exit Function
    End If

    Set nicdict = CreateObject("Scripting.Dictionary")

    For each nic in Computers
      If nic.IPEnabled = True Then
        ip_enabled = 1
      Else
        ip_enabled = 0
      End If
      mac_addr = nic.MacAddress
      ipnum = 0
      If mac_addr <> "" and ip_enabled = 1 Then

        nicdict.Add CStr(CLng(nic.Index)),CreateObject("Scripting.Dictionary")
        nicdict.Item(CStr(CLng(nic.Index))).Add "macaddr",mac_addr
        nicdict.Item(CStr(CLng(nic.Index))).Add "ipenabled",ip_enabled
        'WScript.Echo "  " & nic.Index & " - " & nic.MacAddress

        nicdict.Item(CStr(CLng(nic.Index))).Add "ipaddresses",CreateObject("Scripting.Dictionary")
        For each ip in nic.IPAddress
          If ipnum = 0 Then
            ipgw = nic.DefaultIPGateway(ipnum)
          Else
            ipgw = "0.0.0.0"
          End If
          ip = nic.IPAddress(ipnum)
          sn = nic.IPSubnet(ipnum)
  
          If ipaddrs.Exists(ip) Then
            'WScript.Echo "  removing: " & ip 
            ipaddrs.Remove(ip) ' removing the name from the ipaddrs collection will make sure it is not deleted from the database in the delete code below.
          End If
          nicdict.Item(CStr(CLng(nic.Index))).Item("ipaddresses").Add ip , CreateObject("Scripting.Dictionary")

          nicdict.Item(CStr(CLng(nic.Index))).Item("ipaddresses").Item(ip).Add "macaddress", nic.MacAddress
          nicdict.Item(CStr(CLng(nic.Index))).Item("ipaddresses").Item(ip).Add "subnetmask", sn
          nicdict.Item(CStr(CLng(nic.Index))).Item("ipaddresses").Item(ip).Add "ipgateway", ipgw
          ipnum = ipnum + 1
        Next
      End If
    Next
    Err.Clear

    nic_status = 1
    Exit Function
  Else
    WScript.Echo "  Error in WMI: " & Err.Number
    nic_status = 0 
    Err.Clear
    Exit Function
  End If
End Function
