registry.rb 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. # Class, which provides possibilities to act directly with the data in the registry.
  2. class Registry
  3. require 'logger'
  4. require 'ipaddr'
  5. MAX_LIMIT = 40_000
  6. IP_PATHS = %w[route route6 inetnum inet6num].freeze
  7. SEARCH_PATHS = %w[as-set aut-num dns key-cert mntner organisation person registry role route-set schema tinc-key].freeze
  8. DIRECT_NAME = 'direct'.freeze
  9. def initialize(args)
  10. # Ensure data types of arguments
  11. raise ArgumentError, 'Wrong argument format' unless args.is_a? Hash
  12. raise ArgumentError, 'Directory must be a string' unless args[:dir].is_a? String
  13. raise ArgumentError, 'Logger must be a logger' unless args[:logger].is_a? Logger
  14. # Process and save the arguments
  15. @data_dir = "#{args[:dir]}#{File::SEPARATOR}data#{File::SEPARATOR}"
  16. @logger = args[:logger]
  17. @registry_data = {}
  18. # Check if the registry is available
  19. raise ArgumentError, 'Path does not exist' unless File.exist? @data_dir
  20. end
  21. # Reads the registry again
  22. def update
  23. limit_counter = 0
  24. registry_data = {}
  25. Dir["#{@data_dir}#{File::SEPARATOR}**#{File::SEPARATOR}*"].each do |file|
  26. if File.file? file
  27. if File.readable? file
  28. # Read file and save the lines into a array
  29. cnt = File.readlines file
  30. # Remove line endings
  31. cnt.map!(&:chomp)
  32. # Remove empty lines
  33. cnt.reject!(&:empty?)
  34. # Concat lines with CRLF
  35. val = cnt.join "\r\n"
  36. # Determine the path (something like aut-num or inetnum)
  37. splited_dir = File.dirname(file).split(File::SEPARATOR)
  38. if splited_dir.empty?
  39. @logger.error "Invalid path for file #{file}"
  40. next
  41. elsif splited_dir[-1] == 'data'
  42. # file is direct in data path, e. g. filter files
  43. path = DIRECT_NAME
  44. else
  45. path = splited_dir[-1]
  46. end
  47. # Determine the object something like AS1 or ::/0
  48. object_name = File.basename file
  49. # Create the path in the registry data if needed
  50. registry_data[path] = {} unless registry_data.key? path
  51. if IP_PATHS.include? path
  52. # Extract the IP out of the filename
  53. ip_address_str = object_name.gsub('_', '/')
  54. begin
  55. # Parse the IP
  56. ip_address = IPAddr.new ip_address_str
  57. # Save the parsed ip and the file content
  58. registry_data[path][object_name] = [ip_address, val]
  59. rescue IPAddr::InvalidAddressError
  60. @logger.error "Failed to parse #{ip_address_str}"
  61. end
  62. else
  63. # Save the file content
  64. registry_data[path][object_name] = val
  65. end
  66. else
  67. @logger.error "File #{file} is not readable."
  68. end
  69. end
  70. # Avoid crashing the program by adding
  71. # a lot of objects to the registry
  72. limit_counter += 1
  73. if limit_counter > MAX_LIMIT
  74. @logger.error 'Reached file limit.'
  75. break
  76. end
  77. end
  78. @logger.info 'Save new registry data.'
  79. @registry_data = registry_data
  80. end
  81. # Searches for an IP address in the registry
  82. #
  83. # @return [Array]
  84. def lookup_ipaddr(query)
  85. raise SuspiciousBehavior, 'Tried to lookup a IP address which is not a IP address' unless query.is_a? IPAddr
  86. if query.ipv4?
  87. route_path = 'route'
  88. inetnum_path = 'inetnum'
  89. elsif query.ipv6?
  90. route_path = 'route6'
  91. inetnum_path = 'inet6num'
  92. else
  93. raise SuspiciousBehavior, "Valid IP address is not IPv4 or IPv6: #{query}"
  94. end
  95. results = []
  96. results << lookup_ip_in_path(query, route_path)
  97. results[-1].map! do |value|
  98. [value[0], 2, value[1], value[2]]
  99. end
  100. results << lookup_ip_in_path(query, inetnum_path)
  101. results[-1].map! do |value|
  102. [value[0], 1, value[1], value[2]]
  103. end
  104. results.flatten!(1)
  105. results.reject!(&:empty?)
  106. results.sort! do |ip_a, ip_b|
  107. (ip_a[0] == ip_b[0] ? ip_a[1] <=> ip_b[1] : ip_a[0] <=> ip_b[0])
  108. end
  109. # Remove sorting information
  110. results.map! do |val|
  111. [val[2], val[3]]
  112. end
  113. return results
  114. end
  115. # Searches for an IP address in a specific path in the registry
  116. #
  117. # @return [Array]
  118. def lookup_ip_in_path(query, path)
  119. raise ArgumentError, 'Invalid IP path' unless IP_PATHS.include? path
  120. raise ArgumentError, 'IP address must be of type IP address' unless query.is_a? IPAddr
  121. results = []
  122. @registry_data[path].each do |key, value|
  123. ipaddr = value[0]
  124. data = value[1]
  125. if ipaddr.include? query
  126. # Found match
  127. results << [ipaddr, "#{path}/#{key}", data]
  128. end
  129. end
  130. return results
  131. end
  132. # Searches for files which are directly in the data directory.
  133. #
  134. # @return [Array]
  135. def lookup_direct(query)
  136. raise SuspiciousBehavior, 'Tried to lookup a string which is not a string' unless query.is_a? String
  137. query = query.sanitize.downcase
  138. return lookup_in_path(query, DIRECT_NAME)
  139. end
  140. # Searches for an object in a specific path in the registry
  141. #
  142. # @return [Array]
  143. def lookup_in_path(query, path)
  144. raise ArgumentError, 'Query must be a string' unless query.is_a? String
  145. raise ArgumentError, 'Path must be a string' unless path.is_a? String
  146. results = []
  147. @registry_data[path].each do |key, data|
  148. if key.downcase == query
  149. # Match found
  150. results << ["#{path}/#{key}", data]
  151. end
  152. end
  153. return results
  154. end
  155. # Searches for an object in the registry
  156. #
  157. # @return [Array]
  158. def lookup(query)
  159. raise SuspiciousBehavior, 'Tried to lookup a string which is not a string' unless query.is_a? String
  160. query = query.sanitize.downcase
  161. results = []
  162. SEARCH_PATHS.each do |search_path|
  163. results << lookup_in_path(query, search_path)
  164. end
  165. results.flatten!(1)
  166. return results
  167. end
  168. end