PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/whois/server.rb

http://github.com/weppos/whois
Ruby | 410 lines | 175 code | 51 blank | 184 comment | 34 complexity | 411f8c0c23ed35db62d0a0640a29c5c6 MD5 | raw file
Possible License(s): MIT
  1. # frozen_string_literal: true
  2. #--
  3. # Ruby Whois
  4. #
  5. # An intelligent pure Ruby WHOIS client and parser.
  6. #
  7. # Copyright (c) 2009-2021 Simone Carletti <weppos@weppos.net>
  8. #++
  9. require 'ipaddr'
  10. require 'json'
  11. require 'whois/server/adapters/base'
  12. module Whois
  13. # The {Whois::Server} class has two important roles:
  14. #
  15. # 1. it acts as a database for the WHOIS server definitions
  16. # 2. it is responsible for selecting the right adapter used to handle the query to the WHOIS server(s).
  17. #
  18. class Server
  19. # The {Whois::Server::Adapters} module is a namespace for all
  20. # WHOIS server adapters. Each adapter is a subclass of {Whois::Server::Adapters::Base},
  21. # customized to handle WHOIS queries for a type or a group of servers.
  22. module Adapters
  23. autoload :Base, "whois/server/adapters/base"
  24. autoload :Arin, "whois/server/adapters/arin"
  25. autoload :Arpa, "whois/server/adapters/arpa"
  26. autoload :Afilias, "whois/server/adapters/afilias"
  27. autoload :Formatted, "whois/server/adapters/formatted"
  28. autoload :None, "whois/server/adapters/none"
  29. autoload :NotImplemented, "whois/server/adapters/not_implemented"
  30. autoload :Standard, "whois/server/adapters/standard"
  31. autoload :Verisign, "whois/server/adapters/verisign"
  32. autoload :Web, "whois/server/adapters/web"
  33. end
  34. # @return [Array<Symbol>] the definition types
  35. TYPES = [
  36. TYPE_TLD = :tld,
  37. TYPE_IPV4 = :ipv4,
  38. TYPE_IPV6 = :ipv6,
  39. TYPE_ASN16 = :asn16,
  40. TYPE_ASN32 = :asn32,
  41. ].freeze
  42. # Empty hash constant used to save allocation for definitions with empty settings.
  43. EMPTY_HASH = {}.freeze
  44. private_constant :EMPTY_HASH
  45. class << self
  46. # Clears the definition and reset them to an empty list.
  47. #
  48. # @return [void]
  49. def clear_definitions
  50. @definitions = {}
  51. end
  52. # Searches the +/definitions+ folder for definition files and loads them.
  53. # This method is automatically invoked when this file is parsed
  54. # by the Ruby interpreter (scroll down to the bottom of this file).
  55. #
  56. # @return [void]
  57. def load_definitions
  58. clear_definitions
  59. Dir[File.expand_path('../../data/*.json', __dir__)].each { |f| load_json(f) }
  60. end
  61. # Loads the definitions from a JSON file.
  62. #
  63. # @param [String] file The path to the definition file.
  64. #
  65. # @return [void]
  66. def load_json(file)
  67. type = File.basename(file, File.extname(file)).to_sym
  68. JSON.parse(File.read(file)).each do |allocation, settings|
  69. next if allocation == "_"
  70. settings.reject! { |k, _| k.start_with?("_") }
  71. host = settings.delete("host")
  72. host = intern_string(host) if host
  73. options = if settings.empty?
  74. EMPTY_HASH
  75. else
  76. settings.map { |k, v| [k.to_sym, v.is_a?(String) ? intern_string(v) : v] }.to_h.freeze
  77. end
  78. define(type, allocation, host, options)
  79. end
  80. end
  81. # Lookup and returns the definition list for given `type`.
  82. #
  83. # @param [Symbol] type The type of WHOIS server to lookup.
  84. # See Whois::Server::TYPES for valid types.
  85. #
  86. # @return [{ Symbol => Array }]
  87. # The definition Hash if +type+ is +nil+.
  88. # @return [Array<Hash>]
  89. # The definitions for given +type+ if +type+ is not +nil+ and +type+ exists.
  90. #
  91. # @example Return the definitions for given key.
  92. #
  93. # Whois::Server.definitions(:tld)
  94. # # => [...]
  95. #
  96. # Whois::Server.definitions(:invalid)
  97. # # => nil
  98. #
  99. def definitions(type)
  100. TYPES.include?(type) or
  101. raise(ArgumentError, "`#{type}` is not a valid definition type")
  102. _definitions(type).values
  103. end
  104. # Defines a new server for <tt>:type</tt> queries.
  105. #
  106. # @param [Symbol] type
  107. # The type of WHOIS server to define.
  108. # Known values are :tld, :ipv4, :ipv6.
  109. # @param [String] allocation
  110. # The allocation, range or hostname, this server is responsible for.
  111. # @param [String, nil] host
  112. # The server hostname. Use nil if unknown or not available.
  113. # @param [Hash] options Optional definition properties.
  114. # @option options [Class] :adapter (Whois::Server::Adapters::Standard)
  115. # This option has a special meaning and determines the adapter Class to use.
  116. # Defaults to {Whois::Server::Adapters::Standard} unless specified.
  117. # All the other options are passed directly to the adapter which can decide how to use them.
  118. #
  119. # @return [void]
  120. #
  121. # @example
  122. #
  123. # # Define a server for the .it extension
  124. # Whois::Server.define :tld, "it", "whois.nic.it"
  125. #
  126. # # Define a new server for an range of IPv4 addresses
  127. # Whois::Server.define :ipv4, "61.192.0.0/12", "whois.nic.ad.jp"
  128. #
  129. # # Define a new server for an range of IPv6 addresses
  130. # Whois::Server.define :ipv6, "2001:2000::/19", "whois.ripe.net"
  131. #
  132. # # Define a new server with a custom adapter
  133. # Whois::Server.define :tld, "test", nil,
  134. # :adapter => Whois::Server::Adapter::None
  135. #
  136. # # Define a new server with a custom adapter and options
  137. # Whois::Server.define :tld, "ar", nil,
  138. # :adapter => Whois::Server::Adapters::Web,
  139. # :url => "http://www.nic.ar/"
  140. #
  141. def define(type, allocation, host, options = EMPTY_HASH)
  142. TYPES.include?(type) or
  143. raise(ArgumentError, "`#{type}` is not a valid definition type")
  144. _definitions(type)[allocation] = [allocation, host, options.freeze]
  145. end
  146. # Creates a new server adapter from given arguments
  147. # and returns the server instance.
  148. #
  149. # By default, returns a new {Whois::Server::Adapters::Standard} instance.
  150. # You can customize the behavior passing a custom adapter class
  151. # as <tt>:adapter</tt> option.
  152. #
  153. # Whois::Server.factory :tld, "it", "whois.nic.it"
  154. # # => #<Whois::Servers::Adapter::Standard>
  155. #
  156. # Whois::Server.factory :tld, "it", "whois.nic.it",
  157. # :option => Whois::Servers::Adapter::Custom
  158. # # => #<Whois::Servers::Adapter::Custom>
  159. #
  160. # Please note that any adapter is responsible for a limited set
  161. # of queries, which should be included in the range of the <tt>allocation</tt> parameter.
  162. # Use {Whois::Server.guess} if you are not sure which adapter
  163. # is the right one for a specific string.
  164. #
  165. # @param [Symbol] type
  166. # The type of WHOIS server to define.
  167. # Known values are :tld, :ipv4, :ipv6.
  168. # @param [String] allocation
  169. # The allocation, range or hostname, this server is responsible for.
  170. # @param [String, nil] host
  171. # The server hostname. Use nil if unknown or not available.
  172. # @param [Hash] options Optional definition properties.
  173. # @option options [Class] :adapter (Whois::Server::Adapters::Standard)
  174. # This option has a special meaning and determines the adapter Class to use.
  175. # Defaults to {Whois::Server::Adapters::Standard} unless specified.
  176. # All the other options are passed directly to the adapter which can decide how to use them.
  177. #
  178. # @return [Whois::Server::Adapters::Base]
  179. # a server adapter that can be used to perform queries.
  180. def factory(type, allocation, host, options = {})
  181. options = options.dup
  182. adapter = options.delete(:adapter) || Adapters::Standard
  183. adapter = Adapters.const_get(camelize(adapter)) unless adapter.respond_to?(:new)
  184. adapter.new(type, allocation, host, options)
  185. end
  186. # Parses <tt>string</tt> and tries to guess the right server.
  187. #
  188. # It successfully detects the following query types:
  189. # * ipv6
  190. # * ipv4
  191. # * top level domains (e.g. .com, .net, .it)
  192. # * domain names (e.g. google.com, google.net, google.it)
  193. # * emails
  194. #
  195. # Note that not all query types actually have a corresponding adapter.
  196. # For instance, the following request will result in a
  197. # {Whois::ServerNotSupported} exception.
  198. #
  199. # Whois::Server.guess "mail@example.com"
  200. #
  201. #
  202. # @param string [String]
  203. # @return [Whois::Server::Adapters::Base]
  204. # a server adapter that can be used to perform queries.
  205. #
  206. # @raise [Whois::AllocationUnknown]
  207. # when the input is an IP, but the IP doesn't have a specific known allocation
  208. # that matches one of the existing server definitions.
  209. # @raise [Whois::ServerNotFound]
  210. # when unable to find an appropriate WHOIS adapter. In most of the cases, the input
  211. # is not recognised as one of the supported query types.
  212. # @raise [Whois::ServerNotSupported]
  213. # when the string type is detected,
  214. # but the object type doesn't have any supported WHOIS adapter associated.
  215. def guess(string)
  216. # Top Level Domain match
  217. if matches_tld?(string)
  218. return factory(:tld, ".", "whois.iana.org")
  219. end
  220. # IP address (secure match)
  221. if matches_ip?(string)
  222. return find_for_ip(string)
  223. end
  224. # Email Address (secure match)
  225. if matches_email?(string)
  226. return find_for_email(string)
  227. end
  228. # Domain Name match
  229. if (server = find_for_domain(string))
  230. return server
  231. end
  232. # ASN match
  233. if matches_asn?(string)
  234. return find_for_asn(string)
  235. end
  236. # Game Over
  237. raise ServerNotFound, "Unable to find a WHOIS server for `#{string}'"
  238. end
  239. # Searches for definition that matches given IP.
  240. #
  241. # @param string [String]
  242. # @return [Whois::Server::Adapters::Base, nil]
  243. # a server adapter that can be used to perform queries.
  244. # @raise [Whois::AllocationUnknown]
  245. # when the IP doesn't have a specific known allocation
  246. # that matches one of the existing server definitions.
  247. def find_for_ip(string)
  248. begin
  249. ip = IPAddr.new(string)
  250. type = ip.ipv4? ? TYPE_IPV4 : TYPE_IPV6
  251. _definitions(type).each do |_, definition|
  252. if IPAddr.new(definition.first).include?(ip)
  253. return factory(type, *definition)
  254. end
  255. end
  256. rescue ArgumentError
  257. # continue
  258. nil
  259. end
  260. raise AllocationUnknown, "IP Allocation for `#{string}' unknown"
  261. end
  262. # Searches for definition that matches given email.
  263. #
  264. # @param string [String]
  265. # @raise [Whois::ServerNotSupported]
  266. # emails are not supported.
  267. def find_for_email(string)
  268. raise ServerNotSupported, "No WHOIS server is known for email objects"
  269. end
  270. # Searches for definition that matches given domain.
  271. #
  272. # @param string [String]
  273. # @return [Whois::Server::Adapters::Base, nil]
  274. # a server adapter that can be used to perform queries.
  275. def find_for_domain(string)
  276. token = string
  277. defs = _definitions(TYPE_TLD)
  278. while token != ""
  279. if (found = defs[token])
  280. return factory(:tld, *found)
  281. else
  282. index = token.index(".")
  283. break if index.nil?
  284. token = token[(index + 1)..-1]
  285. end
  286. end
  287. nil
  288. end
  289. # Searches for definition that matches given ASN string.
  290. #
  291. # @param string [String]
  292. # @return [Whois::Server::Adapters::Base, nil]
  293. # a server adapter that can be used to perform queries.
  294. # @raise [Whois::AllocationUnknown]
  295. # when the IP doesn't have a specific known allocation
  296. # that matches one of the existing server definitions.
  297. def find_for_asn(string)
  298. asn = string[/\d+/].to_i
  299. asn_type = asn <= 65_535 ? TYPE_ASN16 : TYPE_ASN32
  300. _definitions(asn_type).each do |_, definition|
  301. if (range = definition.first.split.map(&:to_i)) && asn >= range.first && asn <= range.last
  302. return factory(asn_type, *definition)
  303. end
  304. end
  305. raise AllocationUnknown, "Unknown AS number - `#{asn}'."
  306. end
  307. private
  308. def _definitions(type = nil)
  309. if type.nil?
  310. @definitions
  311. else
  312. @definitions[type] ||= {}
  313. end
  314. end
  315. if String.method_defined?(:-@)
  316. def intern_string(string)
  317. -string
  318. end
  319. else
  320. def intern_string(string)
  321. string.freeze
  322. end
  323. end
  324. def camelize(string)
  325. string.to_s.split("_").collect(&:capitalize).join
  326. end
  327. def matches_tld?(string)
  328. string.match?(/^\.(xn--)?[a-z0-9]+$/)
  329. end
  330. def matches_ip?(string)
  331. valid_ipv4?(string) || valid_ipv6?(string)
  332. end
  333. def matches_email?(string)
  334. string.include?('@')
  335. end
  336. def matches_asn?(string)
  337. string.match?(/^as\d+$/i)
  338. end
  339. def valid_ipv4?(addr)
  340. if (m = /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/.match(addr))
  341. return m.captures.all? { |i| i.to_i < 256 }
  342. end
  343. false
  344. end
  345. def valid_ipv6?(addr)
  346. # IPv6 (normal)
  347. return true if addr.match?(/\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/)
  348. return true if addr.match?(/\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/)
  349. return true if addr.match?(/\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/)
  350. # IPv6 (IPv4 compat)
  351. return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?(Regexp.last_match.post_match)
  352. return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?(Regexp.last_match.post_match)
  353. return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?(Regexp.last_match.post_match)
  354. false
  355. end
  356. end
  357. end
  358. end
  359. Whois::Server.load_definitions