123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- class WhoisError < StandardError; end
- class SuspiciousBehavior < WhoisError; end
- class InvalidQuery < WhoisError; end
- class ObjectNotFound < WhoisError; end
- class WhoisServer
- require 'async/io/host_endpoint'
- require 'async/io/stream'
- require 'optparse'
- require_relative 'registry'
- def initialize(args)
-
- raise ArgumentError, 'Wrong argument format' unless args.is_a? Hash
- raise ArgumentError, 'Logger must be a logger' unless args[:logger].is_a? Logger
- raise ArgumentError, 'Registry must be a registry' unless args[:registry].is_a? Registry
- raise ArgumentError, 'Endpoint must be a array' unless args[:endpoint].is_a? Array
- raise ArgumentError, 'Endpoint has invalid length' unless args[:endpoint].length == 2
- raise ArgumentError, 'Host must be a string' unless args[:endpoint][0].is_a? String
- raise ArgumentError, 'Port must be a integer' unless args[:endpoint][1].is_a? Integer
- @registry = args[:registry]
- @logger = args[:logger]
- @endpoint = Async::IO::Endpoint.tcp(*args[:endpoint])
- end
- def run
- @endpoint.accept do |peer|
- @logger.debug "Incoming connection from #{peer.remote_address.ip_address} port #{peer.remote_address.ip_port}"
- stream = Async::IO::Stream.new(peer, sync: true)
- begin
- query = stream.read_partial 128
-
- raise SuspiciousBehavior, "Long request from #{peer.remote_address.ip_address}" if query.length > 64
- unless query.end_with? "\r\n"
- raise SuspiciousBehavior, "Non RFC-compliant request from #{peer.remote_address.ip_address}"
- end
-
- stream.write "% This is the dn42 whois query service.\r\n\r\n"
- query.strip!
-
- raise InvalidQuery, 'Empty query' if query.empty?
-
- pquery = query.split
- @logger.debug "Query: `#{query}`"
-
- flags = []
- opt_parser = OptionParser.new do |opts|
- opts.on('-f', '--filter') do
- flags << :filter
- end
- end
- opt_parser.parse! pquery
-
- raise InvalidQuery, 'Unknown flags' if pquery.length > 1
-
- raise InvalidQuery, 'Only flags given' if pquery.empty?
-
- rquery = pquery[-1]
-
- if flags.empty?
- ipaddr = ipaddr? rquery
- data = ipaddr ? @registry.lookup_ipaddr(ipaddr) : @registry.lookup(rquery)
- elsif flags.include? :filter
- raise InvalidQuery, 'Unknown combination of flags' if flags.length > 1
- raise InvalidQuery, 'Invalid value for used flag' if rquery !~ /^filter\d?(?:\.txt)?$/i
-
- rquery.delete_suffix! '.txt'
- rquery = "#{rquery}.txt"
- data = @registry.lookup_direct(rquery)
- end
-
- raise ObjectNotFound if data.empty?
- data.each do |path, cnt|
- stream.write "% Information related to '#{path}':\r\n"
- stream.write cnt
- stream.write "\r\n\r\n"
- end
- rescue ObjectNotFound
- stream.write "% Object Not Found\r\n\r\n"
- rescue InvalidQuery => e
- stream.write "% #{e.message}\r\n\r\n"
- ensure
- stream.close unless stream.closed?
- end
- rescue SuspiciousBehavior => e
- @logger.info "BAN: #{e.message}."
- ensure
- peer.close unless peer.closed?
- end
- end
- protected
-
-
-
- def ipaddr?(string)
- return IPAddr.new(string)
- rescue IPAddr::InvalidAddressError
- return false
- end
- end
|