celluloid/celluloid-dns

View on GitHub
lib/celluloid/dns/system.rb

Summary

Maintainability
A
0 mins
Test Coverage
# Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

begin
    require 'win32/resolv'
rescue LoadError
    # Ignore this - we aren't running on windows.
end

module Celluloid::DNS
    # This module encapsulates system dependent name lookup functionality.
    module System
        RESOLV_CONF = "/etc/resolv.conf"
        HOSTS = "/etc/hosts"

        def self.hosts_path
            if RUBY_PLATFORM =~ /mswin32|mingw|bccwin/
                Win32::Resolv.get_hosts_path
            else
                HOSTS
            end
        end

        # This code is very experimental
        class Hosts
            def initialize
                @addresses = {}
                @names = {}
            end

            attr :addresses
            attr :names

            # This is used to match names against the list of known hosts:
            def call(name)
                @names.include?(name)
            end

            def lookup(name)
                addresses = @names[name]

                if addresses
                    addresses.last
                else
                    nil
                end
            end

            alias [] lookup

            def add(address, names)
                @addresses[address] ||= []
                @addresses[address] += names

                names.each do |name|
                    @names[name] ||= []
                    @names[name] << address
                end
            end

            def parse_hosts(io)
                io.each do |line|
                    line.sub!(/#.*/, '')
                    address, hostname, *aliases = line.split(/\s+/)

                    add(address, [hostname] + aliases)
                end
            end

            def self.local
                hosts = self.new

                path = System::hosts_path

                if path and File.exist?(path)
                    File.open(path) do |file|
                        hosts.parse_hosts(file)
                    end
                end

                return hosts
            end
        end

        def self.parse_resolv_configuration(path)
            nameservers = []
            File.open(path) do |file|
                file.each do |line|
                    # Remove any comments:
                    line.sub!(/[#;].*/, '')

                    # Extract resolv.conf command:
                    keyword, *args = line.split(/\s+/)

                    case keyword
                    when 'nameserver'
                        nameservers += args
                    end
                end
            end

            return nameservers
        end

        def self.standard_connections(nameservers)
            connections = []

            nameservers.each do |host|
                connections << [:udp, host, 53]
                connections << [:tcp, host, 53]
            end

            return connections
        end

        # Get a list of standard nameserver connections which can be used for querying any standard servers that the system has been configured with. There is no equivalent facility to use the `hosts` file at present.
        def self.nameservers
            nameservers = []

            if File.exist? RESOLV_CONF
                nameservers = parse_resolv_configuration(RESOLV_CONF)
            elsif defined?(Win32::Resolv) and RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
                search, nameservers = Win32::Resolv.get_resolv_info
            end

            return standard_connections(nameservers)
        end
    end
end