PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/whois/server.rb

https://github.com/deasmi/whois
Ruby | 292 lines | 99 code | 30 blank | 163 comment | 20 complexity | d9f4c9647415405e1b5fd95b745b6803 MD5 | raw file
Possible License(s): MIT
  1. #
  2. # = Ruby Whois
  3. #
  4. # An intelligent pure Ruby WHOIS client and parser.
  5. #
  6. #
  7. # Category:: Net
  8. # Package:: Whois
  9. # Author:: Simone Carletti <weppos@weppos.net>
  10. # License:: MIT License
  11. #
  12. #--
  13. #
  14. #++
  15. require 'ipaddr'
  16. module Whois
  17. class Server
  18. module Adapters
  19. autoload :Base, "whois/server/adapters/base"
  20. autoload :Arpa, "whois/server/adapters/arpa"
  21. autoload :Afilias, "whois/server/adapters/afilias"
  22. autoload :Formatted, "whois/server/adapters/formatted"
  23. autoload :None, "whois/server/adapters/none"
  24. autoload :NotImplemented, "whois/server/adapters/not_implemented"
  25. autoload :Pir, "whois/server/adapters/pir"
  26. autoload :Standard, "whois/server/adapters/standard"
  27. autoload :Verisign, "whois/server/adapters/verisign"
  28. autoload :Web, "whois/server/adapters/web"
  29. end
  30. @@definitions = {}
  31. # Searches the /definitions folder for definition files and loads them.
  32. # This method is automatically invoked when this file is parsed
  33. # by the Ruby interpreter (scroll down to the bottom of this file).
  34. def self.load_definitions
  35. Dir[File.dirname(__FILE__) + '/definitions/*.rb'].each { |file| load(file) }
  36. end
  37. # Lookup and returns the definition list for given <tt>type</tt>,
  38. # or all definitions if <tt>type</tt> is <tt>nil</tt>.
  39. #
  40. # ==== Parameters
  41. #
  42. # type:: The type of WHOIS server to lookup.
  43. # Known values are :tld, :ipv4, :ipv6.
  44. #
  45. # ==== Returns
  46. #
  47. # Hash:: If the method is called without specifying a <tt>type</tt>.
  48. # Array:: If the method is called specifying a <tt>type</tt>,
  49. # and the <tt>type</tt> exists.
  50. # nil:: If the method is called specifying a <tt>type</tt>,
  51. # and the <tt>type</tt> doesn't exist.
  52. #
  53. # ==== Examples
  54. #
  55. # Whois::Server.definitions
  56. # # => { :tld => [...], :ipv4 => [], ... }
  57. #
  58. # Whois::Server.definitions(:tld)
  59. # # => [...]
  60. #
  61. # Whois::Server.definitions(:invalid)
  62. # # => nil
  63. #
  64. def self.definitions(type = nil)
  65. if type.nil?
  66. @@definitions
  67. else
  68. @@definitions[type]
  69. end
  70. end
  71. # Defines a new server for <tt>:type</tt> queries.
  72. #
  73. # ==== Parameters
  74. #
  75. # type:: The type of WHOIS server to define.
  76. # Known values are :tld, :ipv4, :ipv6.
  77. # allocation:: The allocation, range or hostname
  78. # this server is responsible for.
  79. # host:: The server hostname.
  80. # Use nil if unknown or not available.
  81. # options:: Hash of options (default: {}).
  82. # The <tt>:adapter</tt> option has a special meaning
  83. # and determines the adapter Class to use.
  84. # Defaults to </tt>Whois::Server::Adapters::Standard</tt>
  85. # unless specified. All the other options are passed
  86. # directly to the adapter which can decide how to use them.
  87. #
  88. # ==== Returns
  89. #
  90. # Nothing.
  91. #
  92. # ==== Examples
  93. #
  94. # # Define a server for the .it extension
  95. # Whois::Server.define :tld, ".it", "whois.nic.it"
  96. #
  97. # # Define a new server for an range of IPv4 addresses
  98. # Whois::Server.define :ipv4, "61.192.0.0/12", "whois.nic.ad.jp"
  99. #
  100. # # Define a new server for an range of IPv6 addresses
  101. # Whois::Server.define :ipv6, "2001:2000::/19", "whois.ripe.net"
  102. #
  103. # # Define a new server with a custom adapter
  104. # Whois::Server.define :tld, ".test", nil,
  105. # :adapter => Whois::Server::Adapter::None
  106. #
  107. # # Define a new server with a custom adapter and options
  108. # Whois::Server.define :tld, ".ar", nil,
  109. # :adapter => Whois::Server::Adapters::Web,
  110. # :web => "http://www.nic.ar/"
  111. #
  112. def self.define(type, allocation, host, options = {})
  113. @@definitions[type] ||= []
  114. @@definitions[type] << [allocation, host, options]
  115. end
  116. # Creates a new server adapter from given arguments
  117. # and returns the server instance.
  118. #
  119. # By default, returns a new Whois::Servers::Adapter::Standard instance.
  120. # You can customize the behavior passing a custom adapter class
  121. # as <tt>:adapter</tt> option.
  122. #
  123. # Whois::Server.factory :tld, ".it", "whois.nic.it"
  124. # # => #<Whois::Servers::Adapter::Standard>
  125. #
  126. # Whois::Server.factory :tld, ".it", "whois.nic.it",
  127. # :option => Whois::Servers::Adapter::Custom
  128. # # => #<Whois::Servers::Adapter::Custom>
  129. #
  130. # Please note that any adapter is responsible for a limited set
  131. # of queries, which should be included in the range
  132. # of the <tt>allocation</tt> parameter.
  133. # Use <tt>Whois::Server#guess</tt> if you are not sure which adapter
  134. # is the right one for a specific string.
  135. #
  136. # ==== Parameters
  137. #
  138. # type:: The type of WHOIS server to define.
  139. # Known values are :tld, :ipv4, :ipv6.
  140. # allocation:: The allocation, range or hostname
  141. # this server is responsible for.
  142. # host:: The server hostname.
  143. # Use nil if unknown or not available.
  144. # options:: Hash of options (default: {}).
  145. # The <tt>:adapter</tt> option has a special meaning
  146. # and determines the adapter Class to use.
  147. # Defaults to </tt>Whois::Server::Adapters::Standard</tt>
  148. # unless specified. All the other options are passed
  149. # directly to the adapter which can decide how to use them.
  150. #
  151. # ==== Returns
  152. #
  153. # Whois::Server::Adapters::Standard::
  154. # An adapter that can be used to perform queries.
  155. #
  156. def self.factory(type, allocation, host, options = {})
  157. options = options.dup
  158. (options.delete(:adapter) || Adapters::Standard).new(type, allocation, host, options)
  159. end
  160. # Parses <tt>qstring</tt> and tries to guess the right server.
  161. #
  162. # It successfully detects the following query types:
  163. # * ipv6
  164. # * ipv4
  165. # * top level domains (e.g. .com, .net, .it)
  166. # * domain names (e.g. google.com, google.net, google.it)
  167. # * emails
  168. #
  169. # Note that not all query types actually have a corresponding adapter.
  170. # For instance, the following request will result in a
  171. # <tt>Whois::ServerNotSupported</tt> exception.
  172. #
  173. # Whois::Server.guess "mail@example.com"
  174. #
  175. # ==== Returns
  176. #
  177. # Whois::Server::Adapters::Base::
  178. # The adapter that can be used to perform
  179. # WHOIS queries for <tt>qstring</tt>.
  180. #
  181. # ==== Raises
  182. #
  183. # Whois::ServerNotFound::
  184. # When unable to find an appropriate WHOIS adapter
  185. # for <tt>qstring</tt>. Most of the cases, the <tt>qstring</tt>
  186. # haven't been recognised as one of the supported query types.
  187. # Whois::ServerNotSupported::
  188. # When the <tt>qstring</tt> type is detected,
  189. # but the object type doesn't have any supported WHOIS adapter associated.
  190. #
  191. def self.guess(qstring)
  192. # Top Level Domain match
  193. if matches_tld?(qstring)
  194. return factory(:tld, ".", "whois.iana.org")
  195. end
  196. # IP address (secure match)
  197. if matches_ip?(qstring)
  198. return find_for_ip(qstring)
  199. end
  200. # Email Address (secure match)
  201. if matches_email?(qstring)
  202. return find_for_email(qstring)
  203. end
  204. # Domain Name match
  205. if server = find_for_domain(qstring)
  206. return server
  207. end
  208. # Gave Over
  209. raise ServerNotFound, "Unable to find a whois server for `#{qstring}'"
  210. end
  211. private
  212. def self.matches_tld?(string)
  213. string =~ /^\.(xn--)?[a-z0-9]+$/
  214. end
  215. def self.matches_ip?(string)
  216. valid_ipv4?(string) || valid_ipv6?(string)
  217. end
  218. def self.matches_email?(string)
  219. string =~ /@/
  220. end
  221. def self.find_for_ip(qstring)
  222. ip = IPAddr.new(qstring)
  223. type = ip.ipv4? ? :ipv4 : :ipv6
  224. definitions(type).each do |definition|
  225. if IPAddr.new(definition.first).include?(ip)
  226. return factory(type, *definition)
  227. end
  228. end
  229. raise AllocationUnknown, "IP Allocation for `#{qstring}' unknown. Server definitions might be outdated."
  230. end
  231. def self.find_for_email(qstring)
  232. raise ServerNotSupported, "No WHOIS server is known for email objects"
  233. end
  234. def self.find_for_domain(qstring)
  235. definitions(:tld).each do |definition|
  236. return factory(:tld, *definition) if /#{Regexp.escape(definition.first)}$/ =~ qstring
  237. end
  238. nil
  239. end
  240. def self.valid_ipv4?(addr)
  241. if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
  242. return $~.captures.all? {|i| i.to_i < 256}
  243. end
  244. false
  245. end
  246. def self.valid_ipv6?(addr)
  247. # IPv6 (normal)
  248. return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
  249. return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
  250. return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
  251. # IPv6 (IPv4 compat)
  252. return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($')
  253. 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?($')
  254. return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
  255. false
  256. end
  257. end
  258. end
  259. Whois::Server.load_definitions