diff --git a/ChangeLog b/ChangeLog index 9a0fdd1a76..8687b17210 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Thu Jul 24 09:58:32 2003 NAKAMURA Usaku + + * ext/Win32API/lib/win32/resolv.rb: added. + + * lib/resolv.rb: support Win32 platforms. + Thu Jul 24 04:05:46 2003 GOTOU Yuuzou * ext/openssl/ssl.h: undef X509_NAME and PKCS7_SIGNER_INFO to diff --git a/ext/Win32API/MANIFEST b/ext/Win32API/MANIFEST index 3163f6beec..ff3a399d26 100644 --- a/ext/Win32API/MANIFEST +++ b/ext/Win32API/MANIFEST @@ -6,3 +6,4 @@ extconf.rb getch.rb point.rb lib/win32/registry.rb +lib/win32/resolv.rb diff --git a/ext/Win32API/lib/win32/resolv.rb b/ext/Win32API/lib/win32/resolv.rb new file mode 100644 index 0000000000..5a99d04d55 --- /dev/null +++ b/ext/Win32API/lib/win32/resolv.rb @@ -0,0 +1,366 @@ +=begin += Win32 DNS and DHCP I/F + +=end + +require 'win32/registry' + +module Win32 + module Resolv + API = Registry::API + + def self.get_hosts_path + path = get_hosts_dir + path = File.join(path.gsub(/\\/, File::SEPARATOR), 'hosts') + File.exist?(path) ? path : nil + end + + def self.get_resolv_info + search, nameserver = get_info + if search.empty? + search = nil + else + search.delete("") + search.uniq! + end + if nameserver.empty? + nameserver = nil + else + nameserver.delete("") + nameserver.delete("0.0.0.0") + nameserver.uniq! + end + [ search, nameserver ] + end + +getv = Win32API.new('kernel32.dll', 'GetVersionExA', 'P', 'L') +info = [ 148, 0, 0, 0, 0 ].pack('V5') + "\0" * 128 +getv.call(info) +if info.unpack('V5')[4] == 2 # VER_PLATFORM_WIN32_NT +#==================================================================== +# Windows NT +#==================================================================== + module_eval <<-'__EOS__', __FILE__, __LINE__+1 + TCPIP_NT = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' + + class << self + private + def get_hosts_dir + Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| + reg.read_s_expand('DataBasePath') + end + end + + def get_info + search = nil + nameserver = [] + Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg| + begin + slist = reg.read_s('SearchList') + search = slist.split(/,\s*/) unless slist.empty? + rescue Registry::Error + end + + if add_search = search.nil? + search = [] + begin + nvdom = reg.read_s('NV Domain') + unless nvdom.empty? + @search = [ nvdom ] + if reg.read_i('UseDomainNameDevolution') != 0 + if /^[\w\d]+\./ =~ nvdom + devo = $' + end + end + end + rescue Registry::Error + end + end + + reg.open('Interfaces') do |reg| + reg.each_key do |iface,| + reg.open(iface) do |regif| + begin + [ 'NameServer', 'DhcpNameServer' ].each do |key| + ns = regif.read_s(key) + unless ns.empty? + nameserver.concat(ns.split(/\s+/)) + break + end + end + rescue Registry::Error + end + + if add_search + begin + [ 'Domain', 'DhcpDomain' ].each do |key| + dom = regif.read_s(key) + unless dom.empty? + search.concat(dom.split(/,\s*/)) + break + end + end + rescue Registry::Error + end + end + end + end + end + search << devo if add_search and devo + end + [ search.uniq, nameserver.uniq ] + end + end + __EOS__ +else +#==================================================================== +# Windows 9x +#==================================================================== + module_eval <<-'__EOS__', __FILE__, __LINE__+1 + TCPIP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\MSTCP' + DHCP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\DHCP' + WINDOWS = 'Software\Microsoft\Windows\CurrentVersion' + + class << self + # private + + def get_hosts_dir + Registry::HKEY_LOCAL_MACHINE.open(WINDOWS) do |reg| + reg.read_s_expand('SystemRoot') + end + end + + def get_info + search = [] + nameserver = [] + begin + Registry::HKEY_LOCAL_MACHINE.open(TCPIP_9X) do |reg| + if reg.read_s("EnableDNS") == "1" + domain = reg.read_s("Domain") + ns = reg.read_s("NameServer") + slist = reg.read_s("SearchList") + search << domain unless domain.empty? + search.concat(slist.split(/,\s*/)) + nameserver.concat(ns.split(/,\s*/)) + end + end + rescue Registry::Error + end + + dhcpinfo = get_dhcpinfo + search.concat(dhcpinfo[0]) + nameserver.concat(dhcpinfo[1]) + [ search, nameserver ] + end + + def get_dhcpinfo + macaddrs = {} + ipaddrs = {} + WsControl.get_iflist.each do |index, macaddr, *ipaddr| + macaddrs[macaddr] = 1 + ipaddr.each { |ipaddr| ipaddrs[ipaddr] = 1 } + end + iflist = [ macaddrs, ipaddrs ] + + search = [] + nameserver = [] + version = -1 + Registry::HKEY_LOCAL_MACHINE.open(DHCP_9X) do |reg| + begin + version = API.unpackdw(reg.read_bin("Version")) + rescue Registry::Error + end + + reg.each_key do |key,| + catch(:not_used) do + reg.open(key) do |regdi| + dom, ns = get_dhcpinfo_key(version, regdi, iflist) + search << dom if dom + nameserver.concat(ns) if ns + end + end + end + end + [ search, nameserver ] + end + + def get_dhcpinfo_95(reg) + dhcp = reg.read_bin("DhcpInfo") + [ + API.unpackdw(dhcp[4..7]), + API.unpackdw(dhcp[8..11]), + 1, + dhcp[45..50], + reg.read_bin("OptionInfo"), + ] + end + + def get_dhcpinfo_98(reg) + [ + API.unpackdw(reg.read_bin("DhcpIPAddress")), + API.unpackdw(reg.read_bin("DhcpSubnetMask")), + API.unpackdw(reg.read_bin("HardwareType")), + reg.read_bin("HardwareAddress"), + reg.read_bin("OptionInfo"), + ] + end + + def get_dhcpinfo_key(version, reg, iflist) + info = case version + when 1 + get_dhcpinfo_95(reg) + when 2 + get_dhcpinfo_98(reg) + else + begin + get_dhcpinfo_98(reg) + rescue Registry::Error + get_dhcpinfo_95(reg) + end + end + ipaddr, netmask, hwtype, macaddr, opt = info + throw :not_used unless + ipaddr and ipaddr != 0 and + netmask and netmask != 0 and + macaddr and macaddr.size == 6 and + hwtype == 1 and + iflist[0][macaddr] and iflist[1][ipaddr] + + size = opt.size + idx = 0 + while idx <= size + opttype = opt[idx] + optsize = opt[idx + 1] + optval = opt[idx + 2, optsize] + case opttype + when 0xFF ## term + break + when 0x0F ## domain + domain = optval.chomp("\0") + when 0x06 ## dns + nameserver = optval.scan(/..../).collect { |addr| + "%d.%d.%d.%d" % addr.unpack('C4') + } + end + idx += optsize + 2 + end + [ domain, nameserver ] + rescue Registry::Error + throw :not_used + end + end + + module WsControl + WsControl = Win32API.new('wsock32.dll', 'WsControl', 'LLPPPP', 'L') + WSAGetLastError = Win32API.new('wsock32.dll', 'WSAGetLastError', 'V', 'L') + + MAX_TDI_ENTITIES = 512 + IPPROTO_TCP = 6 + WSCTL_TCP_QUERY_INFORMATION = 0 + INFO_CLASS_GENERIC = 0x100 + INFO_CLASS_PROTOCOL = 0x200 + INFO_TYPE_PROVIDER = 0x100 + ENTITY_LIST_ID = 0 + GENERIC_ENTITY = 0 + CL_NL_ENTITY = 0x301 + IF_ENTITY = 0x200 + ENTITY_TYPE_ID = 1 + CL_NL_IP = 0x303 + IF_MIB = 0x202 + IF_MIB_STATS_ID = 1 + IP_MIB_ADDRTABLE_ENTRY_ID = 0x102 + + def self.wsctl(tei_entity, tei_instance, + toi_class, toi_type, toi_id, + buffsize) + reqinfo = [ + ## TDIEntityID + tei_entity, tei_instance, + ## TDIObjectID + toi_class, toi_type, toi_id, + ## TCP_REQUEST_INFORMATION_EX + "" + ].pack('VVVVVa16') + reqsize = API.packdw(reqinfo.size) + buff = "\0" * buffsize + buffsize = API.packdw(buffsize) + result = WsControl.call( + IPPROTO_TCP, + WSCTL_TCP_QUERY_INFORMATION, + reqinfo, reqsize, + buff, buffsize) + if result != 0 + raise RuntimeError, "WsControl failed.(#{result})" + end + [ buff, API.unpackdw(buffsize) ] + end + private_class_method :wsctl + + def self.get_iflist + # Get TDI Entity List + entities, size = + wsctl(GENERIC_ENTITY, 0, + INFO_CLASS_GENERIC, + INFO_TYPE_PROVIDER, + ENTITY_LIST_ID, + MAX_TDI_ENTITIES * 8) # sizeof(TDIEntityID) + entities = entities[0, size]. + scan(/.{8}/). + collect { |e| e.unpack('VV') } + + # Get MIB Interface List + iflist = [] + ifcount = 0 + entities.each do |entity, instance| + if( (entity & IF_ENTITY)>0 ) + ifcount += 1 + etype, = wsctl(entity, instance, + INFO_CLASS_GENERIC, + INFO_TYPE_PROVIDER, + ENTITY_TYPE_ID, + 4) + if( (API.unpackdw(etype) & IF_MIB)==IF_MIB ) + ifentry, = wsctl(entity, instance, + INFO_CLASS_PROTOCOL, + INFO_TYPE_PROVIDER, + IF_MIB_STATS_ID, + 21 * 4 + 8 + 130) # sizeof(IFEntry) + iflist << [ + API.unpackdw(ifentry[0,4]), + ifentry[20, 6] + ] + end + end + end + + # Get IP Addresses + entities.each do |entity, instance| + if entity == CL_NL_ENTITY + etype, = wsctl(entity, instance, + INFO_CLASS_GENERIC, + INFO_TYPE_PROVIDER, + ENTITY_TYPE_ID, + 4) + if API.unpackdw(etype) == CL_NL_IP + ipentries, = wsctl(entity, instance, + INFO_CLASS_PROTOCOL, + INFO_TYPE_PROVIDER, + IP_MIB_ADDRTABLE_ENTRY_ID, + 24 * (ifcount+1)) # sizeof(IPAddrEntry) + ipentries.scan(/.{24}/) do |ipentry| + ipaddr, index = ipentry.unpack('VV') + if ifitem = iflist.assoc(index) + ifitem << ipaddr + end + end + end + end + end + iflist + end + end + __EOS__ +end +#==================================================================== + end +end diff --git a/lib/resolv.rb b/lib/resolv.rb index 23356a2272..6dac504747 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -235,7 +235,7 @@ class Resolv @resolvers.each {|r| r.each_address(name) {|address| yield address.to_s - yielded = true + yielded = true } return if yielded } @@ -257,7 +257,7 @@ class Resolv @resolvers.each {|r| r.each_name(address) {|name| yield name.to_s - yielded = true + yielded = true } return if yielded } @@ -270,7 +270,12 @@ class Resolv end class Hosts - DefaultFileName = '/etc/hosts' + if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM + require 'win32/resolv' + DefaultFileName = Win32::Resolv.get_hosts_path + else + DefaultFileName = '/etc/hosts' + end def initialize(filename = DefaultFileName) @filename = filename @@ -288,15 +293,15 @@ class Resolv line.sub!(/#.*/, '') addr, hostname, *aliases = line.split(/\s+/) next unless addr - addr.untaint - hostname.untaint + addr.untaint + hostname.untaint @addr2name[addr] = [] unless @addr2name.include? addr @addr2name[addr] << hostname @addr2name[addr] += aliases @name2addr[hostname] = [] unless @name2addr.include? hostname @name2addr[hostname] << addr aliases.each {|n| - n.untaint + n.untaint @name2addr[n] = [] unless @name2addr.include? n @name2addr[n] << addr } @@ -322,7 +327,7 @@ class Resolv def each_address(name, &proc) lazy_initialize if @name2addr.include?(name) - @name2addr[name].each(&proc) + @name2addr[name].each(&proc) end end @@ -358,9 +363,9 @@ class Resolv dns = new(*args) return dns unless block_given? begin - yield dns + yield dns ensure - dns.close + dns.close end end @@ -466,7 +471,7 @@ class Resolv case reply.rcode when RCode::NoError extract_resources(reply, reply_name, typeclass, &proc) - return + return when RCode::NXDomain raise Config::NXDomain.new(reply_name.to_s) else @@ -480,10 +485,10 @@ class Resolv def extract_resources(msg, name, typeclass) if typeclass < Resource::ANY - n0 = Name.create(name) + n0 = Name.create(name) msg.each_answer {|n, ttl, data| - yield data if n0 == n - } + yield data if n0 == n + } end yielded = false n0 = Name.create(name) @@ -491,8 +496,8 @@ class Resolv if n0 == n case data when typeclass - yield data - yielded = true + yield data + yielded = true when Resource::CNAME n0 = data.name end @@ -503,7 +508,7 @@ class Resolv if n0 == n case data when typeclass - yield data + yield data end end } @@ -555,11 +560,11 @@ class Resolv def initialize super() @sock = UDPSocket.new - @sock.fcntl(Fcntl::F_SETFD, 1) + @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD @id = {} @id.default = -1 @thread = Thread.new { - DNSThreadGroup.add Thread.current + DNSThreadGroup.add Thread.current loop { reply, from = @sock.recvfrom(UDPSize) msg = begin @@ -608,10 +613,10 @@ class Resolv @port = port @sock = UDPSocket.new @sock.connect(host, port) - @sock.fcntl(Fcntl::F_SETFD, 1) + @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD @id = -1 @thread = Thread.new { - DNSThreadGroup.add Thread.current + DNSThreadGroup.add Thread.current loop { reply = @sock.recv(UDPSize) msg = begin @@ -653,11 +658,11 @@ class Resolv @port = port @sock = TCPSocket.new @sock.connect(host, port) - @sock.fcntl(Fcntl::F_SETFD, 1) + @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD @id = -1 @senders = {} @thread = Thread.new { - DNSThreadGroup.add Thread.current + DNSThreadGroup.add Thread.current loop { len = @sock.read(2).unpack('n') reply = @sock.read(len) @@ -702,7 +707,7 @@ class Resolv def initialize(filename="/etc/resolv.conf") @mutex = Mutex.new @filename = filename - @initialized = nil + @initialized = nil end def lazy_initialize @@ -716,9 +721,9 @@ class Resolv f.each {|line| line.sub!(/[#;].*/, '') keyword, *args = line.split(/\s+/) - args.each { |arg| - arg.untaint - } + args.each { |arg| + arg.untaint + } next unless keyword case keyword when 'nameserver' @@ -731,6 +736,11 @@ class Resolv } } rescue Errno::ENOENT + if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM + search, nameserver = Win32::Resolv.get_resolv_info + @search = [search] if search + @nameserver = nameserver if nameserver + end end @nameserver = ['0.0.0.0'] if @nameserver.empty? @@ -758,17 +768,17 @@ class Resolv def generate_candidates(name) candidates = nil - name = Name.create(name) - if name.absolute? + name = Name.create(name) + if name.absolute? candidates = [name] - else - if @ndots <= name.length - 1 - candidates = [Name.new(name.to_a)] - else - candidates = [] - end - candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)}) - end + else + if @ndots <= name.length - 1 + candidates = [Name.new(name.to_a)] + else + candidates = [] + end + candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)}) + end return candidates end @@ -866,9 +876,9 @@ class Resolv return @string end - def inspect - return "#<#{self.class} #{self.to_s}>" - end + def inspect + return "#<#{self.class} #{self.to_s}>" + end def ==(other) return @downcase == other.downcase @@ -898,7 +908,7 @@ class Resolv def initialize(labels, absolute=true) @labels = labels - @absolute = absolute + @absolute = absolute end def absolute? @@ -1585,7 +1595,7 @@ class Resolv class IPv6 Regex_8Hex = /\A (?:[0-9A-Fa-f]{1,4}:){7} - [0-9A-Fa-f]{1,4} + [0-9A-Fa-f]{1,4} \z/x Regex_CompressedHex = /\A