rubygems-update 3.4.21 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (564) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -2
  3. data/Manifest.txt +39 -221
  4. data/README.md +1 -3
  5. data/bundler/CHANGELOG.md +74 -0
  6. data/bundler/README.md +1 -2
  7. data/bundler/bundler.gemspec +4 -2
  8. data/bundler/exe/bundle +1 -10
  9. data/bundler/lib/bundler/build_metadata.rb +3 -3
  10. data/bundler/lib/bundler/capistrano.rb +1 -1
  11. data/bundler/lib/bundler/checksum.rb +245 -0
  12. data/bundler/lib/bundler/ci_detector.rb +75 -0
  13. data/bundler/lib/bundler/cli/add.rb +3 -3
  14. data/bundler/lib/bundler/cli/binstubs.rb +4 -4
  15. data/bundler/lib/bundler/cli/cache.rb +1 -1
  16. data/bundler/lib/bundler/cli/check.rb +1 -1
  17. data/bundler/lib/bundler/cli/common.rb +9 -1
  18. data/bundler/lib/bundler/cli/config.rb +8 -7
  19. data/bundler/lib/bundler/cli/console.rb +3 -2
  20. data/bundler/lib/bundler/cli/doctor.rb +2 -2
  21. data/bundler/lib/bundler/cli/exec.rb +1 -1
  22. data/bundler/lib/bundler/cli/gem.rb +31 -23
  23. data/bundler/lib/bundler/cli/info.rb +2 -13
  24. data/bundler/lib/bundler/cli/install.rb +5 -4
  25. data/bundler/lib/bundler/cli/issue.rb +1 -1
  26. data/bundler/lib/bundler/cli/lock.rb +4 -4
  27. data/bundler/lib/bundler/cli/open.rb +1 -1
  28. data/bundler/lib/bundler/cli/outdated.rb +6 -6
  29. data/bundler/lib/bundler/cli/plugin.rb +7 -14
  30. data/bundler/lib/bundler/cli/pristine.rb +38 -30
  31. data/bundler/lib/bundler/cli/show.rb +2 -2
  32. data/bundler/lib/bundler/cli/update.rb +5 -5
  33. data/bundler/lib/bundler/cli.rb +215 -263
  34. data/bundler/lib/bundler/compact_index_client/cache.rb +29 -9
  35. data/bundler/lib/bundler/compact_index_client/cache_file.rb +153 -0
  36. data/bundler/lib/bundler/compact_index_client/gem_parser.rb +7 -3
  37. data/bundler/lib/bundler/compact_index_client/updater.rb +79 -81
  38. data/bundler/lib/bundler/compact_index_client.rb +14 -7
  39. data/bundler/lib/bundler/constants.rb +1 -1
  40. data/bundler/lib/bundler/current_ruby.rb +5 -21
  41. data/bundler/lib/bundler/definition.rb +43 -16
  42. data/bundler/lib/bundler/dependency.rb +16 -12
  43. data/bundler/lib/bundler/digest.rb +2 -2
  44. data/bundler/lib/bundler/dsl.rb +43 -25
  45. data/bundler/lib/bundler/endpoint_specification.rb +6 -2
  46. data/bundler/lib/bundler/env.rb +1 -3
  47. data/bundler/lib/bundler/errors.rb +58 -0
  48. data/bundler/lib/bundler/fetcher/base.rb +3 -1
  49. data/bundler/lib/bundler/fetcher/compact_index.rb +4 -4
  50. data/bundler/lib/bundler/fetcher/downloader.rb +13 -11
  51. data/bundler/lib/bundler/fetcher/gem_remote_fetcher.rb +16 -0
  52. data/bundler/lib/bundler/fetcher/index.rb +1 -1
  53. data/bundler/lib/bundler/fetcher.rb +28 -25
  54. data/bundler/lib/bundler/friendly_errors.rb +5 -5
  55. data/bundler/lib/bundler/gem_helper.rb +1 -1
  56. data/bundler/lib/bundler/gem_helpers.rb +12 -2
  57. data/bundler/lib/bundler/graph.rb +9 -9
  58. data/bundler/lib/bundler/index.rb +1 -2
  59. data/bundler/lib/bundler/injector.rb +1 -1
  60. data/bundler/lib/bundler/inline.rb +3 -3
  61. data/bundler/lib/bundler/installer/gem_installer.rb +10 -10
  62. data/bundler/lib/bundler/installer/parallel_installer.rb +16 -8
  63. data/bundler/lib/bundler/installer/standalone.rb +2 -3
  64. data/bundler/lib/bundler/installer.rb +9 -9
  65. data/bundler/lib/bundler/lazy_specification.rb +28 -17
  66. data/bundler/lib/bundler/lockfile_generator.rb +9 -0
  67. data/bundler/lib/bundler/lockfile_parser.rb +81 -10
  68. data/bundler/lib/bundler/man/bundle-add.1 +3 -26
  69. data/bundler/lib/bundler/man/bundle-binstubs.1 +4 -16
  70. data/bundler/lib/bundler/man/bundle-cache.1 +3 -24
  71. data/bundler/lib/bundler/man/bundle-check.1 +3 -12
  72. data/bundler/lib/bundler/man/bundle-clean.1 +3 -10
  73. data/bundler/lib/bundler/man/bundle-config.1 +20 -211
  74. data/bundler/lib/bundler/man/bundle-config.1.ronn +6 -0
  75. data/bundler/lib/bundler/man/bundle-console.1 +4 -22
  76. data/bundler/lib/bundler/man/bundle-doctor.1 +4 -18
  77. data/bundler/lib/bundler/man/bundle-exec.1 +12 -73
  78. data/bundler/lib/bundler/man/bundle-gem.1 +13 -49
  79. data/bundler/lib/bundler/man/bundle-help.1 +3 -7
  80. data/bundler/lib/bundler/man/bundle-info.1 +3 -9
  81. data/bundler/lib/bundler/man/bundle-init.1 +3 -12
  82. data/bundler/lib/bundler/man/bundle-inject.1 +6 -19
  83. data/bundler/lib/bundler/man/bundle-install.1 +27 -125
  84. data/bundler/lib/bundler/man/bundle-install.1.ronn +1 -0
  85. data/bundler/lib/bundler/man/bundle-list.1 +4 -19
  86. data/bundler/lib/bundler/man/bundle-lock.1 +5 -29
  87. data/bundler/lib/bundler/man/bundle-open.1 +7 -27
  88. data/bundler/lib/bundler/man/bundle-outdated.1 +3 -55
  89. data/bundler/lib/bundler/man/bundle-outdated.1.ronn +1 -0
  90. data/bundler/lib/bundler/man/bundle-platform.1 +5 -27
  91. data/bundler/lib/bundler/man/bundle-plugin.1 +3 -29
  92. data/bundler/lib/bundler/man/bundle-pristine.1 +5 -16
  93. data/bundler/lib/bundler/man/bundle-remove.1 +4 -14
  94. data/bundler/lib/bundler/man/bundle-show.1 +3 -10
  95. data/bundler/lib/bundler/man/bundle-update.1 +18 -137
  96. data/bundler/lib/bundler/man/bundle-version.1 +3 -16
  97. data/bundler/lib/bundler/man/bundle-viz.1 +4 -16
  98. data/bundler/lib/bundler/man/bundle.1 +5 -44
  99. data/bundler/lib/bundler/man/gemfile.5 +24 -301
  100. data/bundler/lib/bundler/man/gemfile.5.ronn +4 -0
  101. data/bundler/lib/bundler/match_metadata.rb +4 -0
  102. data/bundler/lib/bundler/match_platform.rb +1 -1
  103. data/bundler/lib/bundler/plugin/api/source.rb +3 -2
  104. data/bundler/lib/bundler/plugin/index.rb +8 -0
  105. data/bundler/lib/bundler/plugin/installer.rb +1 -1
  106. data/bundler/lib/bundler/plugin.rb +12 -5
  107. data/bundler/lib/bundler/resolver/base.rb +1 -1
  108. data/bundler/lib/bundler/resolver/incompatibility.rb +1 -1
  109. data/bundler/lib/bundler/resolver/spec_group.rb +1 -4
  110. data/bundler/lib/bundler/resolver.rb +16 -16
  111. data/bundler/lib/bundler/ruby_dsl.rb +20 -12
  112. data/bundler/lib/bundler/ruby_version.rb +1 -1
  113. data/bundler/lib/bundler/rubygems_ext.rb +27 -54
  114. data/bundler/lib/bundler/rubygems_gem_installer.rb +23 -58
  115. data/bundler/lib/bundler/rubygems_integration.rb +25 -94
  116. data/bundler/lib/bundler/runtime.rb +2 -2
  117. data/bundler/lib/bundler/self_manager.rb +23 -7
  118. data/bundler/lib/bundler/settings.rb +27 -7
  119. data/bundler/lib/bundler/setup.rb +4 -1
  120. data/bundler/lib/bundler/shared_helpers.rb +35 -13
  121. data/bundler/lib/bundler/source/git/git_proxy.rb +22 -14
  122. data/bundler/lib/bundler/source/git.rb +4 -3
  123. data/bundler/lib/bundler/source/metadata.rb +16 -16
  124. data/bundler/lib/bundler/source/path.rb +7 -6
  125. data/bundler/lib/bundler/source/rubygems.rb +21 -14
  126. data/bundler/lib/bundler/source.rb +2 -0
  127. data/bundler/lib/bundler/spec_set.rb +43 -12
  128. data/bundler/lib/bundler/stub_specification.rb +1 -0
  129. data/bundler/lib/bundler/templates/Executable.bundler +1 -1
  130. data/bundler/lib/bundler/templates/newgem/README.md.tt +3 -3
  131. data/bundler/lib/bundler/templates/newgem/Rakefile.tt +2 -6
  132. data/bundler/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +1 -1
  133. data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  134. data/bundler/lib/bundler/templates/newgem/standard.yml.tt +1 -1
  135. data/bundler/lib/bundler/ui/shell.rb +2 -2
  136. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  137. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +53 -6
  138. data/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb +8 -20
  139. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +4 -3
  140. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +23 -11
  141. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +1 -1
  142. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +36 -36
  143. data/bundler/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb +3 -2
  144. data/bundler/lib/bundler/vendor/thor/lib/thor/actions/directory.rb +1 -1
  145. data/bundler/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb +1 -1
  146. data/bundler/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +8 -10
  147. data/bundler/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +15 -4
  148. data/bundler/lib/bundler/vendor/thor/lib/thor/actions.rb +15 -15
  149. data/bundler/lib/bundler/vendor/thor/lib/thor/base.rb +140 -14
  150. data/bundler/lib/bundler/vendor/thor/lib/thor/command.rb +13 -4
  151. data/bundler/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +4 -0
  152. data/bundler/lib/bundler/vendor/thor/lib/thor/error.rb +16 -25
  153. data/bundler/lib/bundler/vendor/thor/lib/thor/group.rb +1 -1
  154. data/bundler/lib/bundler/vendor/thor/lib/thor/invocation.rb +1 -1
  155. data/bundler/lib/bundler/vendor/thor/lib/thor/nested_context.rb +2 -2
  156. data/bundler/lib/bundler/vendor/thor/lib/thor/parser/argument.rb +20 -1
  157. data/bundler/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +33 -17
  158. data/bundler/lib/bundler/vendor/thor/lib/thor/parser/option.rb +27 -8
  159. data/bundler/lib/bundler/vendor/thor/lib/thor/parser/options.rb +44 -6
  160. data/bundler/lib/bundler/vendor/thor/lib/thor/rake_compat.rb +2 -2
  161. data/bundler/lib/bundler/vendor/thor/lib/thor/runner.rb +40 -30
  162. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +26 -150
  163. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb +4 -46
  164. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb +29 -0
  165. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/html.rb +3 -45
  166. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/lcs_diff.rb +49 -0
  167. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb +134 -0
  168. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb +42 -0
  169. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb +38 -0
  170. data/bundler/lib/bundler/vendor/thor/lib/thor/shell.rb +1 -1
  171. data/bundler/lib/bundler/vendor/thor/lib/thor/util.rb +8 -7
  172. data/bundler/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  173. data/bundler/lib/bundler/vendor/thor/lib/thor.rb +155 -8
  174. data/bundler/lib/bundler/vendor/tsort/lib/tsort.rb +3 -0
  175. data/bundler/lib/bundler/vendor/uri/lib/uri/common.rb +256 -132
  176. data/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb +1 -0
  177. data/bundler/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +95 -31
  178. data/bundler/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  179. data/bundler/lib/bundler/vendored_net_http.rb +8 -0
  180. data/bundler/lib/bundler/vendored_persistent.rb +0 -4
  181. data/bundler/lib/bundler/vendored_timeout.rb +8 -0
  182. data/bundler/lib/bundler/version.rb +1 -1
  183. data/bundler/lib/bundler/vlad.rb +1 -1
  184. data/bundler/lib/bundler/yaml_serializer.rb +9 -4
  185. data/bundler/lib/bundler.rb +38 -35
  186. data/lib/rubygems/available_set.rb +4 -4
  187. data/lib/rubygems/basic_specification.rb +35 -37
  188. data/lib/rubygems/bundler_version_finder.rb +4 -4
  189. data/lib/rubygems/ci_detector.rb +75 -0
  190. data/lib/rubygems/command.rb +15 -17
  191. data/lib/rubygems/command_manager.rb +5 -4
  192. data/lib/rubygems/commands/build_command.rb +2 -2
  193. data/lib/rubygems/commands/cert_command.rb +2 -3
  194. data/lib/rubygems/commands/check_command.rb +4 -4
  195. data/lib/rubygems/commands/cleanup_command.rb +12 -14
  196. data/lib/rubygems/commands/contents_command.rb +5 -5
  197. data/lib/rubygems/commands/dependency_command.rb +4 -5
  198. data/lib/rubygems/commands/environment_command.rb +3 -5
  199. data/lib/rubygems/commands/exec_command.rb +1 -1
  200. data/lib/rubygems/commands/fetch_command.rb +2 -2
  201. data/lib/rubygems/commands/generate_index_command.rb +39 -74
  202. data/lib/rubygems/commands/help_command.rb +4 -4
  203. data/lib/rubygems/commands/info_command.rb +2 -2
  204. data/lib/rubygems/commands/install_command.rb +8 -16
  205. data/lib/rubygems/commands/list_command.rb +2 -2
  206. data/lib/rubygems/commands/lock_command.rb +1 -1
  207. data/lib/rubygems/commands/open_command.rb +1 -1
  208. data/lib/rubygems/commands/owner_command.rb +1 -1
  209. data/lib/rubygems/commands/pristine_command.rb +13 -15
  210. data/lib/rubygems/commands/push_command.rb +2 -2
  211. data/lib/rubygems/commands/query_command.rb +4 -5
  212. data/lib/rubygems/commands/rdoc_command.rb +2 -2
  213. data/lib/rubygems/commands/search_command.rb +2 -2
  214. data/lib/rubygems/commands/setup_command.rb +33 -36
  215. data/lib/rubygems/commands/sources_command.rb +12 -12
  216. data/lib/rubygems/commands/specification_command.rb +10 -10
  217. data/lib/rubygems/commands/stale_command.rb +1 -1
  218. data/lib/rubygems/commands/uninstall_command.rb +13 -14
  219. data/lib/rubygems/commands/unpack_command.rb +7 -7
  220. data/lib/rubygems/commands/update_command.rb +11 -13
  221. data/lib/rubygems/commands/which_command.rb +1 -1
  222. data/lib/rubygems/commands/yank_command.rb +1 -1
  223. data/lib/rubygems/compatibility.rb +5 -6
  224. data/lib/rubygems/config_file.rb +7 -7
  225. data/lib/rubygems/core_ext/kernel_gem.rb +0 -2
  226. data/lib/rubygems/core_ext/kernel_require.rb +20 -49
  227. data/lib/rubygems/core_ext/kernel_warn.rb +1 -1
  228. data/lib/rubygems/core_ext/tcpsocket_init.rb +1 -1
  229. data/lib/rubygems/defaults.rb +15 -3
  230. data/lib/rubygems/dependency.rb +12 -14
  231. data/lib/rubygems/dependency_installer.rb +30 -31
  232. data/lib/rubygems/dependency_list.rb +1 -1
  233. data/lib/rubygems/deprecate.rb +16 -15
  234. data/lib/rubygems/doctor.rb +6 -6
  235. data/lib/rubygems/errors.rb +2 -6
  236. data/lib/rubygems/exceptions.rb +2 -1
  237. data/lib/rubygems/ext/builder.rb +15 -10
  238. data/lib/rubygems/ext/cargo_builder.rb +5 -5
  239. data/lib/rubygems/ext/ext_conf_builder.rb +2 -4
  240. data/lib/rubygems/ext/rake_builder.rb +1 -1
  241. data/lib/rubygems/gem_runner.rb +4 -4
  242. data/lib/rubygems/gemcutter_utilities/webauthn_listener/response.rb +3 -3
  243. data/lib/rubygems/gemcutter_utilities/webauthn_poller.rb +3 -3
  244. data/lib/rubygems/gemcutter_utilities.rb +18 -19
  245. data/lib/rubygems/install_update_options.rb +18 -19
  246. data/lib/rubygems/installer.rb +66 -45
  247. data/lib/rubygems/installer_uninstaller_utils.rb +0 -2
  248. data/lib/rubygems/local_remote_options.rb +8 -11
  249. data/lib/rubygems/name_tuple.rb +7 -9
  250. data/lib/rubygems/net/http.rb +3 -0
  251. data/lib/rubygems/net-http/LICENSE.txt +22 -0
  252. data/lib/rubygems/net-http/lib/net/http/backward.rb +40 -0
  253. data/lib/rubygems/net-http/lib/net/http/exceptions.rb +34 -0
  254. data/lib/rubygems/net-http/lib/net/http/generic_request.rb +414 -0
  255. data/lib/rubygems/net-http/lib/net/http/header.rb +981 -0
  256. data/lib/rubygems/net-http/lib/net/http/proxy_delta.rb +17 -0
  257. data/lib/rubygems/net-http/lib/net/http/request.rb +88 -0
  258. data/lib/rubygems/net-http/lib/net/http/requests.rb +425 -0
  259. data/lib/rubygems/net-http/lib/net/http/response.rb +738 -0
  260. data/lib/rubygems/net-http/lib/net/http/responses.rb +1174 -0
  261. data/lib/rubygems/net-http/lib/net/http/status.rb +84 -0
  262. data/lib/rubygems/net-http/lib/net/http.rb +2496 -0
  263. data/lib/rubygems/net-http/lib/net/https.rb +23 -0
  264. data/lib/rubygems/net-protocol/LICENSE.txt +22 -0
  265. data/lib/rubygems/net-protocol/lib/net/protocol.rb +544 -0
  266. data/lib/rubygems/optparse/lib/optparse.rb +39 -17
  267. data/lib/rubygems/package/digest_io.rb +1 -1
  268. data/lib/rubygems/package/old.rb +2 -2
  269. data/lib/rubygems/package/tar_header.rb +45 -39
  270. data/lib/rubygems/package/tar_reader/entry.rb +5 -4
  271. data/lib/rubygems/package/tar_reader.rb +14 -5
  272. data/lib/rubygems/package/tar_writer.rb +20 -18
  273. data/lib/rubygems/package.rb +28 -27
  274. data/lib/rubygems/package_task.rb +2 -2
  275. data/lib/rubygems/path_support.rb +10 -11
  276. data/lib/rubygems/platform.rb +65 -48
  277. data/lib/rubygems/query_utils.rb +7 -9
  278. data/lib/rubygems/remote_fetcher.rb +17 -17
  279. data/lib/rubygems/request/connection_pools.rb +3 -3
  280. data/lib/rubygems/request.rb +20 -17
  281. data/lib/rubygems/request_set/gem_dependency_api.rb +120 -123
  282. data/lib/rubygems/request_set/lockfile/parser.rb +9 -9
  283. data/lib/rubygems/request_set/lockfile/tokenizer.rb +20 -12
  284. data/lib/rubygems/request_set/lockfile.rb +6 -11
  285. data/lib/rubygems/request_set.rb +5 -5
  286. data/lib/rubygems/requirement.rb +7 -7
  287. data/lib/rubygems/resolv/LICENSE.txt +22 -0
  288. data/lib/rubygems/resolv/lib/resolv.rb +3387 -0
  289. data/lib/rubygems/resolver/activation_request.rb +1 -3
  290. data/lib/rubygems/resolver/api_set/gem_parser.rb +7 -3
  291. data/lib/rubygems/resolver/best_set.rb +1 -1
  292. data/lib/rubygems/resolver/composed_set.rb +1 -1
  293. data/lib/rubygems/resolver/conflict.rb +4 -12
  294. data/lib/rubygems/resolver/index_set.rb +4 -4
  295. data/lib/rubygems/resolver/index_specification.rb +2 -2
  296. data/lib/rubygems/resolver/installer_set.rb +5 -6
  297. data/lib/rubygems/resolver/lock_set.rb +1 -1
  298. data/lib/rubygems/resolver.rb +6 -13
  299. data/lib/rubygems/s3_uri_signer.rb +6 -6
  300. data/lib/rubygems/safe_marshal/elements.rb +138 -0
  301. data/lib/rubygems/safe_marshal/reader.rb +306 -0
  302. data/lib/rubygems/safe_marshal/visitors/stream_printer.rb +31 -0
  303. data/lib/rubygems/safe_marshal/visitors/to_ruby.rb +385 -0
  304. data/lib/rubygems/safe_marshal/visitors/visitor.rb +74 -0
  305. data/lib/rubygems/safe_marshal.rb +74 -0
  306. data/lib/rubygems/safe_yaml.rb +5 -28
  307. data/lib/rubygems/security/policies.rb +36 -38
  308. data/lib/rubygems/security/policy.rb +7 -11
  309. data/lib/rubygems/security/signer.rb +1 -1
  310. data/lib/rubygems/security/trust_dir.rb +4 -4
  311. data/lib/rubygems/security.rb +8 -22
  312. data/lib/rubygems/source/git.rb +1 -3
  313. data/lib/rubygems/source/installed.rb +0 -2
  314. data/lib/rubygems/source/local.rb +7 -9
  315. data/lib/rubygems/source/lock.rb +1 -3
  316. data/lib/rubygems/source/specific_file.rb +0 -1
  317. data/lib/rubygems/source/vendor.rb +0 -2
  318. data/lib/rubygems/source.rb +12 -12
  319. data/lib/rubygems/source_list.rb +5 -5
  320. data/lib/rubygems/spec_fetcher.rb +31 -31
  321. data/lib/rubygems/specification.rb +145 -150
  322. data/lib/rubygems/specification_policy.rb +61 -31
  323. data/lib/rubygems/stub_specification.rb +4 -5
  324. data/lib/rubygems/text.rb +1 -2
  325. data/lib/rubygems/timeout/LICENSE.txt +22 -0
  326. data/lib/rubygems/timeout/lib/timeout.rb +199 -0
  327. data/lib/rubygems/timeout.rb +3 -0
  328. data/lib/rubygems/tsort/lib/tsort.rb +3 -0
  329. data/lib/rubygems/uninstaller.rb +9 -11
  330. data/lib/rubygems/update_suggestion.rb +5 -18
  331. data/lib/rubygems/uri_formatter.rb +1 -1
  332. data/lib/rubygems/user_interaction.rb +17 -23
  333. data/lib/rubygems/util/licenses.rb +113 -35
  334. data/lib/rubygems/util/list.rb +3 -1
  335. data/lib/rubygems/util.rb +2 -4
  336. data/lib/rubygems/validator.rb +6 -4
  337. data/lib/rubygems/version.rb +35 -29
  338. data/lib/rubygems/version_option.rb +2 -5
  339. data/lib/rubygems/yaml_serializer.rb +9 -4
  340. data/lib/rubygems.rb +42 -42
  341. data/rubygems-update.gemspec +4 -4
  342. data/setup.rb +2 -2
  343. metadata +43 -225
  344. data/lib/rubygems/indexer.rb +0 -428
  345. data/lib/rubygems/mock_gem_ui.rb +0 -86
  346. data/test/rubygems/alternate_cert.pem +0 -19
  347. data/test/rubygems/alternate_cert_32.pem +0 -19
  348. data/test/rubygems/alternate_key.pem +0 -27
  349. data/test/rubygems/bad_rake.rb +0 -3
  350. data/test/rubygems/bundler_test_gem.rb +0 -424
  351. data/test/rubygems/ca_cert.pem +0 -77
  352. data/test/rubygems/child_cert.pem +0 -19
  353. data/test/rubygems/child_cert_32.pem +0 -19
  354. data/test/rubygems/child_key.pem +0 -27
  355. data/test/rubygems/client.pem +0 -107
  356. data/test/rubygems/data/excon-0.7.7.gemspec.rz +0 -0
  357. data/test/rubygems/data/gem-private_key.pem +0 -27
  358. data/test/rubygems/data/gem-public_cert.pem +0 -20
  359. data/test/rubygems/data/null-required-ruby-version.gemspec.rz +0 -0
  360. data/test/rubygems/data/null-required-rubygems-version.gemspec.rz +0 -0
  361. data/test/rubygems/data/pry-0.4.7.gemspec.rz +0 -0
  362. data/test/rubygems/encrypted_private_key.pem +0 -30
  363. data/test/rubygems/expired_cert.pem +0 -19
  364. data/test/rubygems/fake_certlib/openssl.rb +0 -9
  365. data/test/rubygems/foo/discover.rb +0 -1
  366. data/test/rubygems/future_cert.pem +0 -19
  367. data/test/rubygems/future_cert_32.pem +0 -19
  368. data/test/rubygems/good_rake.rb +0 -3
  369. data/test/rubygems/grandchild_cert.pem +0 -19
  370. data/test/rubygems/grandchild_cert_32.pem +0 -19
  371. data/test/rubygems/grandchild_key.pem +0 -27
  372. data/test/rubygems/helper.rb +0 -1649
  373. data/test/rubygems/installer_test_case.rb +0 -248
  374. data/test/rubygems/invalid_client.pem +0 -49
  375. data/test/rubygems/invalid_issuer_cert.pem +0 -20
  376. data/test/rubygems/invalid_issuer_cert_32.pem +0 -20
  377. data/test/rubygems/invalid_key.pem +0 -27
  378. data/test/rubygems/invalid_signer_cert.pem +0 -19
  379. data/test/rubygems/invalid_signer_cert_32.pem +0 -19
  380. data/test/rubygems/invalidchild_cert.pem +0 -19
  381. data/test/rubygems/invalidchild_cert_32.pem +0 -19
  382. data/test/rubygems/invalidchild_key.pem +0 -27
  383. data/test/rubygems/multifactor_auth_utilities.rb +0 -111
  384. data/test/rubygems/package/tar_test_case.rb +0 -175
  385. data/test/rubygems/packages/Bluebie-legs-0.6.2.gem +0 -0
  386. data/test/rubygems/packages/ascii_binder-0.1.10.1.gem +0 -0
  387. data/test/rubygems/packages/ill-formatted-platform-1.0.0.10.gem +0 -0
  388. data/test/rubygems/plugin/exception/rubygems_plugin.rb +0 -4
  389. data/test/rubygems/plugin/load/rubygems_plugin.rb +0 -5
  390. data/test/rubygems/plugin/standarderror/rubygems_plugin.rb +0 -4
  391. data/test/rubygems/private3072_key.pem +0 -40
  392. data/test/rubygems/private_ec_key.pem +0 -9
  393. data/test/rubygems/private_key.pem +0 -27
  394. data/test/rubygems/public3072_cert.pem +0 -25
  395. data/test/rubygems/public_cert.pem +0 -20
  396. data/test/rubygems/public_cert_32.pem +0 -19
  397. data/test/rubygems/public_key.pem +0 -9
  398. data/test/rubygems/rubygems/commands/crash_command.rb +0 -5
  399. data/test/rubygems/rubygems_plugin.rb +0 -24
  400. data/test/rubygems/sff/discover.rb +0 -1
  401. data/test/rubygems/simple_gem.rb +0 -68
  402. data/test/rubygems/specifications/bar-0.0.2.gemspec +0 -9
  403. data/test/rubygems/specifications/foo-0.0.1-x86-mswin32.gemspec +0 -0
  404. data/test/rubygems/specifications/rubyforge-0.0.1.gemspec +0 -14
  405. data/test/rubygems/ssl_cert.pem +0 -80
  406. data/test/rubygems/ssl_key.pem +0 -27
  407. data/test/rubygems/test_bundled_ca.rb +0 -61
  408. data/test/rubygems/test_config.rb +0 -28
  409. data/test/rubygems/test_deprecate.rb +0 -158
  410. data/test/rubygems/test_exit.rb +0 -17
  411. data/test/rubygems/test_gem.rb +0 -1799
  412. data/test/rubygems/test_gem_available_set.rb +0 -130
  413. data/test/rubygems/test_gem_bundler_version_finder.rb +0 -127
  414. data/test/rubygems/test_gem_command.rb +0 -403
  415. data/test/rubygems/test_gem_command_manager.rb +0 -400
  416. data/test/rubygems/test_gem_commands_build_command.rb +0 -739
  417. data/test/rubygems/test_gem_commands_cert_command.rb +0 -866
  418. data/test/rubygems/test_gem_commands_check_command.rb +0 -68
  419. data/test/rubygems/test_gem_commands_cleanup_command.rb +0 -292
  420. data/test/rubygems/test_gem_commands_contents_command.rb +0 -271
  421. data/test/rubygems/test_gem_commands_dependency_command.rb +0 -228
  422. data/test/rubygems/test_gem_commands_environment_command.rb +0 -169
  423. data/test/rubygems/test_gem_commands_exec_command.rb +0 -857
  424. data/test/rubygems/test_gem_commands_fetch_command.rb +0 -258
  425. data/test/rubygems/test_gem_commands_generate_index_command.rb +0 -81
  426. data/test/rubygems/test_gem_commands_help_command.rb +0 -94
  427. data/test/rubygems/test_gem_commands_info_command.rb +0 -70
  428. data/test/rubygems/test_gem_commands_install_command.rb +0 -1573
  429. data/test/rubygems/test_gem_commands_list_command.rb +0 -33
  430. data/test/rubygems/test_gem_commands_lock_command.rb +0 -67
  431. data/test/rubygems/test_gem_commands_mirror.rb +0 -20
  432. data/test/rubygems/test_gem_commands_open_command.rb +0 -101
  433. data/test/rubygems/test_gem_commands_outdated_command.rb +0 -50
  434. data/test/rubygems/test_gem_commands_owner_command.rb +0 -503
  435. data/test/rubygems/test_gem_commands_pristine_command.rb +0 -708
  436. data/test/rubygems/test_gem_commands_push_command.rb +0 -603
  437. data/test/rubygems/test_gem_commands_query_command.rb +0 -858
  438. data/test/rubygems/test_gem_commands_search_command.rb +0 -16
  439. data/test/rubygems/test_gem_commands_server_command.rb +0 -20
  440. data/test/rubygems/test_gem_commands_setup_command.rb +0 -474
  441. data/test/rubygems/test_gem_commands_signin_command.rb +0 -259
  442. data/test/rubygems/test_gem_commands_signout_command.rb +0 -30
  443. data/test/rubygems/test_gem_commands_sources_command.rb +0 -534
  444. data/test/rubygems/test_gem_commands_specification_command.rb +0 -277
  445. data/test/rubygems/test_gem_commands_stale_command.rb +0 -43
  446. data/test/rubygems/test_gem_commands_uninstall_command.rb +0 -522
  447. data/test/rubygems/test_gem_commands_unpack_command.rb +0 -224
  448. data/test/rubygems/test_gem_commands_update_command.rb +0 -836
  449. data/test/rubygems/test_gem_commands_which_command.rb +0 -85
  450. data/test/rubygems/test_gem_commands_yank_command.rb +0 -299
  451. data/test/rubygems/test_gem_config_file.rb +0 -551
  452. data/test/rubygems/test_gem_dependency.rb +0 -398
  453. data/test/rubygems/test_gem_dependency_installer.rb +0 -1190
  454. data/test/rubygems/test_gem_dependency_list.rb +0 -265
  455. data/test/rubygems/test_gem_dependency_resolution_error.rb +0 -27
  456. data/test/rubygems/test_gem_doctor.rb +0 -195
  457. data/test/rubygems/test_gem_ext_builder.rb +0 -337
  458. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/.gitignore +0 -1
  459. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/custom_name.gemspec +0 -10
  460. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock +0 -249
  461. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml +0 -10
  462. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/src/lib.rs +0 -27
  463. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/lib/custom_name.rb +0 -3
  464. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/.gitignore +0 -1
  465. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +0 -249
  466. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +0 -10
  467. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/rust_ruby_example.gemspec +0 -10
  468. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/src/lib.rs +0 -51
  469. data/test/rubygems/test_gem_ext_cargo_builder.rb +0 -167
  470. data/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb +0 -34
  471. data/test/rubygems/test_gem_ext_cargo_builder_unit.rb +0 -60
  472. data/test/rubygems/test_gem_ext_cmake_builder.rb +0 -84
  473. data/test/rubygems/test_gem_ext_configure_builder.rb +0 -80
  474. data/test/rubygems/test_gem_ext_ext_conf_builder.rb +0 -229
  475. data/test/rubygems/test_gem_ext_rake_builder.rb +0 -113
  476. data/test/rubygems/test_gem_gem_runner.rb +0 -119
  477. data/test/rubygems/test_gem_gemcutter_utilities.rb +0 -361
  478. data/test/rubygems/test_gem_impossible_dependencies_error.rb +0 -60
  479. data/test/rubygems/test_gem_indexer.rb +0 -381
  480. data/test/rubygems/test_gem_install_update_options.rb +0 -208
  481. data/test/rubygems/test_gem_installer.rb +0 -2512
  482. data/test/rubygems/test_gem_local_remote_options.rb +0 -133
  483. data/test/rubygems/test_gem_name_tuple.rb +0 -43
  484. data/test/rubygems/test_gem_package.rb +0 -1306
  485. data/test/rubygems/test_gem_package_old.rb +0 -91
  486. data/test/rubygems/test_gem_package_tar_header.rb +0 -226
  487. data/test/rubygems/test_gem_package_tar_reader.rb +0 -135
  488. data/test/rubygems/test_gem_package_tar_reader_entry.rb +0 -350
  489. data/test/rubygems/test_gem_package_tar_writer.rb +0 -331
  490. data/test/rubygems/test_gem_package_task.rb +0 -118
  491. data/test/rubygems/test_gem_path_support.rb +0 -139
  492. data/test/rubygems/test_gem_platform.rb +0 -497
  493. data/test/rubygems/test_gem_rdoc.rb +0 -137
  494. data/test/rubygems/test_gem_remote_fetcher.rb +0 -1227
  495. data/test/rubygems/test_gem_request.rb +0 -547
  496. data/test/rubygems/test_gem_request_connection_pools.rb +0 -152
  497. data/test/rubygems/test_gem_request_set.rb +0 -672
  498. data/test/rubygems/test_gem_request_set_gem_dependency_api.rb +0 -853
  499. data/test/rubygems/test_gem_request_set_lockfile.rb +0 -469
  500. data/test/rubygems/test_gem_request_set_lockfile_parser.rb +0 -544
  501. data/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb +0 -307
  502. data/test/rubygems/test_gem_requirement.rb +0 -505
  503. data/test/rubygems/test_gem_resolver.rb +0 -859
  504. data/test/rubygems/test_gem_resolver_activation_request.rb +0 -43
  505. data/test/rubygems/test_gem_resolver_api_set.rb +0 -210
  506. data/test/rubygems/test_gem_resolver_api_specification.rb +0 -167
  507. data/test/rubygems/test_gem_resolver_best_set.rb +0 -159
  508. data/test/rubygems/test_gem_resolver_composed_set.rb +0 -44
  509. data/test/rubygems/test_gem_resolver_conflict.rb +0 -82
  510. data/test/rubygems/test_gem_resolver_dependency_request.rb +0 -83
  511. data/test/rubygems/test_gem_resolver_git_set.rb +0 -188
  512. data/test/rubygems/test_gem_resolver_git_specification.rb +0 -114
  513. data/test/rubygems/test_gem_resolver_index_set.rb +0 -88
  514. data/test/rubygems/test_gem_resolver_index_specification.rb +0 -93
  515. data/test/rubygems/test_gem_resolver_installed_specification.rb +0 -47
  516. data/test/rubygems/test_gem_resolver_installer_set.rb +0 -320
  517. data/test/rubygems/test_gem_resolver_local_specification.rb +0 -44
  518. data/test/rubygems/test_gem_resolver_lock_set.rb +0 -62
  519. data/test/rubygems/test_gem_resolver_lock_specification.rb +0 -98
  520. data/test/rubygems/test_gem_resolver_requirement_list.rb +0 -19
  521. data/test/rubygems/test_gem_resolver_specification.rb +0 -63
  522. data/test/rubygems/test_gem_resolver_vendor_set.rb +0 -82
  523. data/test/rubygems/test_gem_resolver_vendor_specification.rb +0 -82
  524. data/test/rubygems/test_gem_security.rb +0 -341
  525. data/test/rubygems/test_gem_security_policy.rb +0 -535
  526. data/test/rubygems/test_gem_security_signer.rb +0 -218
  527. data/test/rubygems/test_gem_security_trust_dir.rb +0 -99
  528. data/test/rubygems/test_gem_silent_ui.rb +0 -123
  529. data/test/rubygems/test_gem_source.rb +0 -254
  530. data/test/rubygems/test_gem_source_fetch_problem.rb +0 -37
  531. data/test/rubygems/test_gem_source_git.rb +0 -310
  532. data/test/rubygems/test_gem_source_installed.rb +0 -35
  533. data/test/rubygems/test_gem_source_list.rb +0 -119
  534. data/test/rubygems/test_gem_source_local.rb +0 -107
  535. data/test/rubygems/test_gem_source_lock.rb +0 -113
  536. data/test/rubygems/test_gem_source_specific_file.rb +0 -76
  537. data/test/rubygems/test_gem_source_subpath_problem.rb +0 -50
  538. data/test/rubygems/test_gem_source_vendor.rb +0 -30
  539. data/test/rubygems/test_gem_spec_fetcher.rb +0 -338
  540. data/test/rubygems/test_gem_specification.rb +0 -3856
  541. data/test/rubygems/test_gem_stream_ui.rb +0 -255
  542. data/test/rubygems/test_gem_stub_specification.rb +0 -278
  543. data/test/rubygems/test_gem_text.rb +0 -103
  544. data/test/rubygems/test_gem_uninstaller.rb +0 -675
  545. data/test/rubygems/test_gem_unsatisfiable_dependency_error.rb +0 -31
  546. data/test/rubygems/test_gem_update_suggestion.rb +0 -209
  547. data/test/rubygems/test_gem_uri.rb +0 -41
  548. data/test/rubygems/test_gem_uri_formatter.rb +0 -27
  549. data/test/rubygems/test_gem_util.rb +0 -91
  550. data/test/rubygems/test_gem_validator.rb +0 -42
  551. data/test/rubygems/test_gem_version.rb +0 -305
  552. data/test/rubygems/test_gem_version_option.rb +0 -165
  553. data/test/rubygems/test_kernel.rb +0 -124
  554. data/test/rubygems/test_project_sanity.rb +0 -49
  555. data/test/rubygems/test_remote_fetch_error.rb +0 -20
  556. data/test/rubygems/test_require.rb +0 -732
  557. data/test/rubygems/test_rubygems.rb +0 -76
  558. data/test/rubygems/test_webauthn_listener.rb +0 -143
  559. data/test/rubygems/test_webauthn_listener_response.rb +0 -93
  560. data/test/rubygems/test_webauthn_poller.rb +0 -124
  561. data/test/rubygems/utilities.rb +0 -436
  562. data/test/rubygems/wrong_key_cert.pem +0 -19
  563. data/test/rubygems/wrong_key_cert_32.pem +0 -19
  564. data/test/test_changelog_generator.rb +0 -17
@@ -0,0 +1,3387 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+ require_relative '../../timeout/lib/timeout'
5
+ require 'io/wait'
6
+
7
+ begin
8
+ require 'securerandom'
9
+ rescue LoadError
10
+ end
11
+
12
+ # Gem::Resolv is a thread-aware DNS resolver library written in Ruby. Gem::Resolv can
13
+ # handle multiple DNS requests concurrently without blocking the entire Ruby
14
+ # interpreter.
15
+ #
16
+ # See also resolv-replace.rb to replace the libc resolver with Gem::Resolv.
17
+ #
18
+ # Gem::Resolv can look up various DNS resources using the DNS module directly.
19
+ #
20
+ # Examples:
21
+ #
22
+ # p Gem::Resolv.getaddress "www.ruby-lang.org"
23
+ # p Gem::Resolv.getname "210.251.121.214"
24
+ #
25
+ # Gem::Resolv::DNS.open do |dns|
26
+ # ress = dns.getresources "www.ruby-lang.org", Gem::Resolv::DNS::Resource::IN::A
27
+ # p ress.map(&:address)
28
+ # ress = dns.getresources "ruby-lang.org", Gem::Resolv::DNS::Resource::IN::MX
29
+ # p ress.map { |r| [r.exchange.to_s, r.preference] }
30
+ # end
31
+ #
32
+ #
33
+ # == Bugs
34
+ #
35
+ # * NIS is not supported.
36
+ # * /etc/nsswitch.conf is not supported.
37
+
38
+ class Gem::Resolv
39
+
40
+ VERSION = "0.3.0"
41
+
42
+ ##
43
+ # Looks up the first IP address for +name+.
44
+
45
+ def self.getaddress(name)
46
+ DefaultResolver.getaddress(name)
47
+ end
48
+
49
+ ##
50
+ # Looks up all IP address for +name+.
51
+
52
+ def self.getaddresses(name)
53
+ DefaultResolver.getaddresses(name)
54
+ end
55
+
56
+ ##
57
+ # Iterates over all IP addresses for +name+.
58
+
59
+ def self.each_address(name, &block)
60
+ DefaultResolver.each_address(name, &block)
61
+ end
62
+
63
+ ##
64
+ # Looks up the hostname of +address+.
65
+
66
+ def self.getname(address)
67
+ DefaultResolver.getname(address)
68
+ end
69
+
70
+ ##
71
+ # Looks up all hostnames for +address+.
72
+
73
+ def self.getnames(address)
74
+ DefaultResolver.getnames(address)
75
+ end
76
+
77
+ ##
78
+ # Iterates over all hostnames for +address+.
79
+
80
+ def self.each_name(address, &proc)
81
+ DefaultResolver.each_name(address, &proc)
82
+ end
83
+
84
+ ##
85
+ # Creates a new Gem::Resolv using +resolvers+.
86
+
87
+ def initialize(resolvers=nil, use_ipv6: nil)
88
+ @resolvers = resolvers || [Hosts.new, DNS.new(DNS::Config.default_config_hash.merge(use_ipv6: use_ipv6))]
89
+ end
90
+
91
+ ##
92
+ # Looks up the first IP address for +name+.
93
+
94
+ def getaddress(name)
95
+ each_address(name) {|address| return address}
96
+ raise ResolvError.new("no address for #{name}")
97
+ end
98
+
99
+ ##
100
+ # Looks up all IP address for +name+.
101
+
102
+ def getaddresses(name)
103
+ ret = []
104
+ each_address(name) {|address| ret << address}
105
+ return ret
106
+ end
107
+
108
+ ##
109
+ # Iterates over all IP addresses for +name+.
110
+
111
+ def each_address(name)
112
+ if AddressRegex =~ name
113
+ yield name
114
+ return
115
+ end
116
+ yielded = false
117
+ @resolvers.each {|r|
118
+ r.each_address(name) {|address|
119
+ yield address.to_s
120
+ yielded = true
121
+ }
122
+ return if yielded
123
+ }
124
+ end
125
+
126
+ ##
127
+ # Looks up the hostname of +address+.
128
+
129
+ def getname(address)
130
+ each_name(address) {|name| return name}
131
+ raise ResolvError.new("no name for #{address}")
132
+ end
133
+
134
+ ##
135
+ # Looks up all hostnames for +address+.
136
+
137
+ def getnames(address)
138
+ ret = []
139
+ each_name(address) {|name| ret << name}
140
+ return ret
141
+ end
142
+
143
+ ##
144
+ # Iterates over all hostnames for +address+.
145
+
146
+ def each_name(address)
147
+ yielded = false
148
+ @resolvers.each {|r|
149
+ r.each_name(address) {|name|
150
+ yield name.to_s
151
+ yielded = true
152
+ }
153
+ return if yielded
154
+ }
155
+ end
156
+
157
+ ##
158
+ # Indicates a failure to resolve a name or address.
159
+
160
+ class ResolvError < StandardError; end
161
+
162
+ ##
163
+ # Indicates a timeout resolving a name or address.
164
+
165
+ class ResolvTimeout < Gem::Timeout::Error; end
166
+
167
+ ##
168
+ # Gem::Resolv::Hosts is a hostname resolver that uses the system hosts file.
169
+
170
+ class Hosts
171
+ if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM and
172
+ begin
173
+ require 'win32/resolv'
174
+ DefaultFileName = Win32::Resolv.get_hosts_path || IO::NULL
175
+ rescue LoadError
176
+ end
177
+ end
178
+ DefaultFileName ||= '/etc/hosts'
179
+
180
+ ##
181
+ # Creates a new Gem::Resolv::Hosts, using +filename+ for its data source.
182
+
183
+ def initialize(filename = DefaultFileName)
184
+ @filename = filename
185
+ @mutex = Thread::Mutex.new
186
+ @initialized = nil
187
+ end
188
+
189
+ def lazy_initialize # :nodoc:
190
+ @mutex.synchronize {
191
+ unless @initialized
192
+ @name2addr = {}
193
+ @addr2name = {}
194
+ File.open(@filename, 'rb') {|f|
195
+ f.each {|line|
196
+ line.sub!(/#.*/, '')
197
+ addr, hostname, *aliases = line.split(/\s+/)
198
+ next unless addr
199
+ @addr2name[addr] = [] unless @addr2name.include? addr
200
+ @addr2name[addr] << hostname
201
+ @addr2name[addr].concat(aliases)
202
+ @name2addr[hostname] = [] unless @name2addr.include? hostname
203
+ @name2addr[hostname] << addr
204
+ aliases.each {|n|
205
+ @name2addr[n] = [] unless @name2addr.include? n
206
+ @name2addr[n] << addr
207
+ }
208
+ }
209
+ }
210
+ @name2addr.each {|name, arr| arr.reverse!}
211
+ @initialized = true
212
+ end
213
+ }
214
+ self
215
+ end
216
+
217
+ ##
218
+ # Gets the IP address of +name+ from the hosts file.
219
+
220
+ def getaddress(name)
221
+ each_address(name) {|address| return address}
222
+ raise ResolvError.new("#{@filename} has no name: #{name}")
223
+ end
224
+
225
+ ##
226
+ # Gets all IP addresses for +name+ from the hosts file.
227
+
228
+ def getaddresses(name)
229
+ ret = []
230
+ each_address(name) {|address| ret << address}
231
+ return ret
232
+ end
233
+
234
+ ##
235
+ # Iterates over all IP addresses for +name+ retrieved from the hosts file.
236
+
237
+ def each_address(name, &proc)
238
+ lazy_initialize
239
+ @name2addr[name]&.each(&proc)
240
+ end
241
+
242
+ ##
243
+ # Gets the hostname of +address+ from the hosts file.
244
+
245
+ def getname(address)
246
+ each_name(address) {|name| return name}
247
+ raise ResolvError.new("#{@filename} has no address: #{address}")
248
+ end
249
+
250
+ ##
251
+ # Gets all hostnames for +address+ from the hosts file.
252
+
253
+ def getnames(address)
254
+ ret = []
255
+ each_name(address) {|name| ret << name}
256
+ return ret
257
+ end
258
+
259
+ ##
260
+ # Iterates over all hostnames for +address+ retrieved from the hosts file.
261
+
262
+ def each_name(address, &proc)
263
+ lazy_initialize
264
+ @addr2name[address]&.each(&proc)
265
+ end
266
+ end
267
+
268
+ ##
269
+ # Gem::Resolv::DNS is a DNS stub resolver.
270
+ #
271
+ # Information taken from the following places:
272
+ #
273
+ # * STD0013
274
+ # * RFC 1035
275
+ # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
276
+ # * etc.
277
+
278
+ class DNS
279
+
280
+ ##
281
+ # Default DNS Port
282
+
283
+ Port = 53
284
+
285
+ ##
286
+ # Default DNS UDP packet size
287
+
288
+ UDPSize = 512
289
+
290
+ ##
291
+ # Creates a new DNS resolver. See Gem::Resolv::DNS.new for argument details.
292
+ #
293
+ # Yields the created DNS resolver to the block, if given, otherwise
294
+ # returns it.
295
+
296
+ def self.open(*args)
297
+ dns = new(*args)
298
+ return dns unless block_given?
299
+ begin
300
+ yield dns
301
+ ensure
302
+ dns.close
303
+ end
304
+ end
305
+
306
+ ##
307
+ # Creates a new DNS resolver.
308
+ #
309
+ # +config_info+ can be:
310
+ #
311
+ # nil:: Uses /etc/resolv.conf.
312
+ # String:: Path to a file using /etc/resolv.conf's format.
313
+ # Hash:: Must contain :nameserver, :search and :ndots keys.
314
+ # :nameserver_port can be used to specify port number of nameserver address.
315
+ # :raise_timeout_errors can be used to raise timeout errors
316
+ # as exceptions instead of treating the same as an NXDOMAIN response.
317
+ #
318
+ # The value of :nameserver should be an address string or
319
+ # an array of address strings.
320
+ # - :nameserver => '8.8.8.8'
321
+ # - :nameserver => ['8.8.8.8', '8.8.4.4']
322
+ #
323
+ # The value of :nameserver_port should be an array of
324
+ # pair of nameserver address and port number.
325
+ # - :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]
326
+ #
327
+ # Example:
328
+ #
329
+ # Gem::Resolv::DNS.new(:nameserver => ['210.251.121.21'],
330
+ # :search => ['ruby-lang.org'],
331
+ # :ndots => 1)
332
+
333
+ def initialize(config_info=nil)
334
+ @mutex = Thread::Mutex.new
335
+ @config = Config.new(config_info)
336
+ @initialized = nil
337
+ end
338
+
339
+ # Sets the resolver timeouts. This may be a single positive number
340
+ # or an array of positive numbers representing timeouts in seconds.
341
+ # If an array is specified, a DNS request will retry and wait for
342
+ # each successive interval in the array until a successful response
343
+ # is received. Specifying +nil+ reverts to the default timeouts:
344
+ # [ 5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second ]
345
+ #
346
+ # Example:
347
+ #
348
+ # dns.timeouts = 3
349
+ #
350
+ def timeouts=(values)
351
+ @config.timeouts = values
352
+ end
353
+
354
+ def lazy_initialize # :nodoc:
355
+ @mutex.synchronize {
356
+ unless @initialized
357
+ @config.lazy_initialize
358
+ @initialized = true
359
+ end
360
+ }
361
+ self
362
+ end
363
+
364
+ ##
365
+ # Closes the DNS resolver.
366
+
367
+ def close
368
+ @mutex.synchronize {
369
+ if @initialized
370
+ @initialized = false
371
+ end
372
+ }
373
+ end
374
+
375
+ ##
376
+ # Gets the IP address of +name+ from the DNS resolver.
377
+ #
378
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved address will
379
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
380
+
381
+ def getaddress(name)
382
+ each_address(name) {|address| return address}
383
+ raise ResolvError.new("DNS result has no information for #{name}")
384
+ end
385
+
386
+ ##
387
+ # Gets all IP addresses for +name+ from the DNS resolver.
388
+ #
389
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
390
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
391
+
392
+ def getaddresses(name)
393
+ ret = []
394
+ each_address(name) {|address| ret << address}
395
+ return ret
396
+ end
397
+
398
+ ##
399
+ # Iterates over all IP addresses for +name+ retrieved from the DNS
400
+ # resolver.
401
+ #
402
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
403
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
404
+
405
+ def each_address(name)
406
+ each_resource(name, Resource::IN::A) {|resource| yield resource.address}
407
+ if use_ipv6?
408
+ each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
409
+ end
410
+ end
411
+
412
+ def use_ipv6? # :nodoc:
413
+ use_ipv6 = @config.use_ipv6?
414
+ unless use_ipv6.nil?
415
+ return use_ipv6
416
+ end
417
+
418
+ begin
419
+ list = Socket.ip_address_list
420
+ rescue NotImplementedError
421
+ return true
422
+ end
423
+ list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
424
+ end
425
+ private :use_ipv6?
426
+
427
+ ##
428
+ # Gets the hostname for +address+ from the DNS resolver.
429
+ #
430
+ # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
431
+ # name will be a Gem::Resolv::DNS::Name.
432
+
433
+ def getname(address)
434
+ each_name(address) {|name| return name}
435
+ raise ResolvError.new("DNS result has no information for #{address}")
436
+ end
437
+
438
+ ##
439
+ # Gets all hostnames for +address+ from the DNS resolver.
440
+ #
441
+ # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
442
+ # names will be Gem::Resolv::DNS::Name instances.
443
+
444
+ def getnames(address)
445
+ ret = []
446
+ each_name(address) {|name| ret << name}
447
+ return ret
448
+ end
449
+
450
+ ##
451
+ # Iterates over all hostnames for +address+ retrieved from the DNS
452
+ # resolver.
453
+ #
454
+ # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
455
+ # names will be Gem::Resolv::DNS::Name instances.
456
+
457
+ def each_name(address)
458
+ case address
459
+ when Name
460
+ ptr = address
461
+ when IPv4, IPv6
462
+ ptr = address.to_name
463
+ when IPv4::Regex
464
+ ptr = IPv4.create(address).to_name
465
+ when IPv6::Regex
466
+ ptr = IPv6.create(address).to_name
467
+ else
468
+ raise ResolvError.new("cannot interpret as address: #{address}")
469
+ end
470
+ each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
471
+ end
472
+
473
+ ##
474
+ # Look up the +typeclass+ DNS resource of +name+.
475
+ #
476
+ # +name+ must be a Gem::Resolv::DNS::Name or a String.
477
+ #
478
+ # +typeclass+ should be one of the following:
479
+ #
480
+ # * Gem::Resolv::DNS::Resource::IN::A
481
+ # * Gem::Resolv::DNS::Resource::IN::AAAA
482
+ # * Gem::Resolv::DNS::Resource::IN::ANY
483
+ # * Gem::Resolv::DNS::Resource::IN::CNAME
484
+ # * Gem::Resolv::DNS::Resource::IN::HINFO
485
+ # * Gem::Resolv::DNS::Resource::IN::MINFO
486
+ # * Gem::Resolv::DNS::Resource::IN::MX
487
+ # * Gem::Resolv::DNS::Resource::IN::NS
488
+ # * Gem::Resolv::DNS::Resource::IN::PTR
489
+ # * Gem::Resolv::DNS::Resource::IN::SOA
490
+ # * Gem::Resolv::DNS::Resource::IN::TXT
491
+ # * Gem::Resolv::DNS::Resource::IN::WKS
492
+ #
493
+ # Returned resource is represented as a Gem::Resolv::DNS::Resource instance,
494
+ # i.e. Gem::Resolv::DNS::Resource::IN::A.
495
+
496
+ def getresource(name, typeclass)
497
+ each_resource(name, typeclass) {|resource| return resource}
498
+ raise ResolvError.new("DNS result has no information for #{name}")
499
+ end
500
+
501
+ ##
502
+ # Looks up all +typeclass+ DNS resources for +name+. See #getresource for
503
+ # argument details.
504
+
505
+ def getresources(name, typeclass)
506
+ ret = []
507
+ each_resource(name, typeclass) {|resource| ret << resource}
508
+ return ret
509
+ end
510
+
511
+ ##
512
+ # Iterates over all +typeclass+ DNS resources for +name+. See
513
+ # #getresource for argument details.
514
+
515
+ def each_resource(name, typeclass, &proc)
516
+ fetch_resource(name, typeclass) {|reply, reply_name|
517
+ extract_resources(reply, reply_name, typeclass, &proc)
518
+ }
519
+ end
520
+
521
+ def fetch_resource(name, typeclass)
522
+ lazy_initialize
523
+ begin
524
+ requester = make_udp_requester
525
+ rescue Errno::EACCES
526
+ # fall back to TCP
527
+ end
528
+ senders = {}
529
+ begin
530
+ @config.resolv(name) {|candidate, tout, nameserver, port|
531
+ requester ||= make_tcp_requester(nameserver, port)
532
+ msg = Message.new
533
+ msg.rd = 1
534
+ msg.add_question(candidate, typeclass)
535
+ unless sender = senders[[candidate, nameserver, port]]
536
+ sender = requester.sender(msg, candidate, nameserver, port)
537
+ next if !sender
538
+ senders[[candidate, nameserver, port]] = sender
539
+ end
540
+ reply, reply_name = requester.request(sender, tout)
541
+ case reply.rcode
542
+ when RCode::NoError
543
+ if reply.tc == 1 and not Requester::TCP === requester
544
+ requester.close
545
+ # Retry via TCP:
546
+ requester = make_tcp_requester(nameserver, port)
547
+ senders = {}
548
+ # This will use TCP for all remaining candidates (assuming the
549
+ # current candidate does not already respond successfully via
550
+ # TCP). This makes sense because we already know the full
551
+ # response will not fit in an untruncated UDP packet.
552
+ redo
553
+ else
554
+ yield(reply, reply_name)
555
+ end
556
+ return
557
+ when RCode::NXDomain
558
+ raise Config::NXDomain.new(reply_name.to_s)
559
+ else
560
+ raise Config::OtherResolvError.new(reply_name.to_s)
561
+ end
562
+ }
563
+ ensure
564
+ requester&.close
565
+ end
566
+ end
567
+
568
+ def make_udp_requester # :nodoc:
569
+ nameserver_port = @config.nameserver_port
570
+ if nameserver_port.length == 1
571
+ Requester::ConnectedUDP.new(*nameserver_port[0])
572
+ else
573
+ Requester::UnconnectedUDP.new(*nameserver_port)
574
+ end
575
+ end
576
+
577
+ def make_tcp_requester(host, port) # :nodoc:
578
+ return Requester::TCP.new(host, port)
579
+ end
580
+
581
+ def extract_resources(msg, name, typeclass) # :nodoc:
582
+ if typeclass < Resource::ANY
583
+ n0 = Name.create(name)
584
+ msg.each_resource {|n, ttl, data|
585
+ yield data if n0 == n
586
+ }
587
+ end
588
+ yielded = false
589
+ n0 = Name.create(name)
590
+ msg.each_resource {|n, ttl, data|
591
+ if n0 == n
592
+ case data
593
+ when typeclass
594
+ yield data
595
+ yielded = true
596
+ when Resource::CNAME
597
+ n0 = data.name
598
+ end
599
+ end
600
+ }
601
+ return if yielded
602
+ msg.each_resource {|n, ttl, data|
603
+ if n0 == n
604
+ case data
605
+ when typeclass
606
+ yield data
607
+ end
608
+ end
609
+ }
610
+ end
611
+
612
+ if defined? SecureRandom
613
+ def self.random(arg) # :nodoc:
614
+ begin
615
+ SecureRandom.random_number(arg)
616
+ rescue NotImplementedError
617
+ rand(arg)
618
+ end
619
+ end
620
+ else
621
+ def self.random(arg) # :nodoc:
622
+ rand(arg)
623
+ end
624
+ end
625
+
626
+ RequestID = {} # :nodoc:
627
+ RequestIDMutex = Thread::Mutex.new # :nodoc:
628
+
629
+ def self.allocate_request_id(host, port) # :nodoc:
630
+ id = nil
631
+ RequestIDMutex.synchronize {
632
+ h = (RequestID[[host, port]] ||= {})
633
+ begin
634
+ id = random(0x0000..0xffff)
635
+ end while h[id]
636
+ h[id] = true
637
+ }
638
+ id
639
+ end
640
+
641
+ def self.free_request_id(host, port, id) # :nodoc:
642
+ RequestIDMutex.synchronize {
643
+ key = [host, port]
644
+ if h = RequestID[key]
645
+ h.delete id
646
+ if h.empty?
647
+ RequestID.delete key
648
+ end
649
+ end
650
+ }
651
+ end
652
+
653
+ def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
654
+ begin
655
+ port = random(1024..65535)
656
+ udpsock.bind(bind_host, port)
657
+ rescue Errno::EADDRINUSE, # POSIX
658
+ Errno::EACCES, # SunOS: See PRIV_SYS_NFS in privileges(5)
659
+ Errno::EPERM # FreeBSD: security.mac.portacl.port_high is configurable. See mac_portacl(4).
660
+ retry
661
+ end
662
+ end
663
+
664
+ class Requester # :nodoc:
665
+ def initialize
666
+ @senders = {}
667
+ @socks = nil
668
+ end
669
+
670
+ def request(sender, tout)
671
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
672
+ timelimit = start + tout
673
+ begin
674
+ sender.send
675
+ rescue Errno::EHOSTUNREACH, # multi-homed IPv6 may generate this
676
+ Errno::ENETUNREACH
677
+ raise ResolvTimeout
678
+ end
679
+ while true
680
+ before_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
681
+ timeout = timelimit - before_select
682
+ if timeout <= 0
683
+ raise ResolvTimeout
684
+ end
685
+ if @socks.size == 1
686
+ select_result = @socks[0].wait_readable(timeout) ? [ @socks ] : nil
687
+ else
688
+ select_result = IO.select(@socks, nil, nil, timeout)
689
+ end
690
+ if !select_result
691
+ after_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
692
+ next if after_select < timelimit
693
+ raise ResolvTimeout
694
+ end
695
+ begin
696
+ reply, from = recv_reply(select_result[0])
697
+ rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
698
+ Errno::ECONNRESET # Windows
699
+ # No name server running on the server?
700
+ # Don't wait anymore.
701
+ raise ResolvTimeout
702
+ end
703
+ begin
704
+ msg = Message.decode(reply)
705
+ rescue DecodeError
706
+ next # broken DNS message ignored
707
+ end
708
+ if sender == sender_for(from, msg)
709
+ break
710
+ else
711
+ # unexpected DNS message ignored
712
+ end
713
+ end
714
+ return msg, sender.data
715
+ end
716
+
717
+ def sender_for(addr, msg)
718
+ @senders[[addr,msg.id]]
719
+ end
720
+
721
+ def close
722
+ socks = @socks
723
+ @socks = nil
724
+ socks&.each(&:close)
725
+ end
726
+
727
+ class Sender # :nodoc:
728
+ def initialize(msg, data, sock)
729
+ @msg = msg
730
+ @data = data
731
+ @sock = sock
732
+ end
733
+ end
734
+
735
+ class UnconnectedUDP < Requester # :nodoc:
736
+ def initialize(*nameserver_port)
737
+ super()
738
+ @nameserver_port = nameserver_port
739
+ @initialized = false
740
+ @mutex = Thread::Mutex.new
741
+ end
742
+
743
+ def lazy_initialize
744
+ @mutex.synchronize {
745
+ next if @initialized
746
+ @initialized = true
747
+ @socks_hash = {}
748
+ @socks = []
749
+ @nameserver_port.each {|host, port|
750
+ if host.index(':')
751
+ bind_host = "::"
752
+ af = Socket::AF_INET6
753
+ else
754
+ bind_host = "0.0.0.0"
755
+ af = Socket::AF_INET
756
+ end
757
+ next if @socks_hash[bind_host]
758
+ begin
759
+ sock = UDPSocket.new(af)
760
+ rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT
761
+ next # The kernel doesn't support the address family.
762
+ end
763
+ @socks << sock
764
+ @socks_hash[bind_host] = sock
765
+ sock.do_not_reverse_lookup = true
766
+ DNS.bind_random_port(sock, bind_host)
767
+ }
768
+ }
769
+ self
770
+ end
771
+
772
+ def recv_reply(readable_socks)
773
+ lazy_initialize
774
+ reply, from = readable_socks[0].recvfrom(UDPSize)
775
+ return reply, [from[3],from[1]]
776
+ end
777
+
778
+ def sender(msg, data, host, port=Port)
779
+ host = Addrinfo.ip(host).ip_address
780
+ lazy_initialize
781
+ sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
782
+ return nil if !sock
783
+ service = [host, port]
784
+ id = DNS.allocate_request_id(host, port)
785
+ request = msg.encode
786
+ request[0,2] = [id].pack('n')
787
+ return @senders[[service, id]] =
788
+ Sender.new(request, data, sock, host, port)
789
+ end
790
+
791
+ def close
792
+ @mutex.synchronize {
793
+ if @initialized
794
+ super
795
+ @senders.each_key {|service, id|
796
+ DNS.free_request_id(service[0], service[1], id)
797
+ }
798
+ @initialized = false
799
+ end
800
+ }
801
+ end
802
+
803
+ class Sender < Requester::Sender # :nodoc:
804
+ def initialize(msg, data, sock, host, port)
805
+ super(msg, data, sock)
806
+ @host = host
807
+ @port = port
808
+ end
809
+ attr_reader :data
810
+
811
+ def send
812
+ raise "@sock is nil." if @sock.nil?
813
+ @sock.send(@msg, 0, @host, @port)
814
+ end
815
+ end
816
+ end
817
+
818
+ class ConnectedUDP < Requester # :nodoc:
819
+ def initialize(host, port=Port)
820
+ super()
821
+ @host = host
822
+ @port = port
823
+ @mutex = Thread::Mutex.new
824
+ @initialized = false
825
+ end
826
+
827
+ def lazy_initialize
828
+ @mutex.synchronize {
829
+ next if @initialized
830
+ @initialized = true
831
+ is_ipv6 = @host.index(':')
832
+ sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
833
+ @socks = [sock]
834
+ sock.do_not_reverse_lookup = true
835
+ DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
836
+ sock.connect(@host, @port)
837
+ }
838
+ self
839
+ end
840
+
841
+ def recv_reply(readable_socks)
842
+ lazy_initialize
843
+ reply = readable_socks[0].recv(UDPSize)
844
+ return reply, nil
845
+ end
846
+
847
+ def sender(msg, data, host=@host, port=@port)
848
+ lazy_initialize
849
+ unless host == @host && port == @port
850
+ raise RequestError.new("host/port don't match: #{host}:#{port}")
851
+ end
852
+ id = DNS.allocate_request_id(@host, @port)
853
+ request = msg.encode
854
+ request[0,2] = [id].pack('n')
855
+ return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
856
+ end
857
+
858
+ def close
859
+ @mutex.synchronize do
860
+ if @initialized
861
+ super
862
+ @senders.each_key {|from, id|
863
+ DNS.free_request_id(@host, @port, id)
864
+ }
865
+ @initialized = false
866
+ end
867
+ end
868
+ end
869
+
870
+ class Sender < Requester::Sender # :nodoc:
871
+ def send
872
+ raise "@sock is nil." if @sock.nil?
873
+ @sock.send(@msg, 0)
874
+ end
875
+ attr_reader :data
876
+ end
877
+ end
878
+
879
+ class MDNSOneShot < UnconnectedUDP # :nodoc:
880
+ def sender(msg, data, host, port=Port)
881
+ lazy_initialize
882
+ id = DNS.allocate_request_id(host, port)
883
+ request = msg.encode
884
+ request[0,2] = [id].pack('n')
885
+ sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
886
+ return @senders[id] =
887
+ UnconnectedUDP::Sender.new(request, data, sock, host, port)
888
+ end
889
+
890
+ def sender_for(addr, msg)
891
+ lazy_initialize
892
+ @senders[msg.id]
893
+ end
894
+ end
895
+
896
+ class TCP < Requester # :nodoc:
897
+ def initialize(host, port=Port)
898
+ super()
899
+ @host = host
900
+ @port = port
901
+ sock = TCPSocket.new(@host, @port)
902
+ @socks = [sock]
903
+ @senders = {}
904
+ end
905
+
906
+ def recv_reply(readable_socks)
907
+ len = readable_socks[0].read(2).unpack('n')[0]
908
+ reply = @socks[0].read(len)
909
+ return reply, nil
910
+ end
911
+
912
+ def sender(msg, data, host=@host, port=@port)
913
+ unless host == @host && port == @port
914
+ raise RequestError.new("host/port don't match: #{host}:#{port}")
915
+ end
916
+ id = DNS.allocate_request_id(@host, @port)
917
+ request = msg.encode
918
+ request[0,2] = [request.length, id].pack('nn')
919
+ return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
920
+ end
921
+
922
+ class Sender < Requester::Sender # :nodoc:
923
+ def send
924
+ @sock.print(@msg)
925
+ @sock.flush
926
+ end
927
+ attr_reader :data
928
+ end
929
+
930
+ def close
931
+ super
932
+ @senders.each_key {|from,id|
933
+ DNS.free_request_id(@host, @port, id)
934
+ }
935
+ end
936
+ end
937
+
938
+ ##
939
+ # Indicates a problem with the DNS request.
940
+
941
+ class RequestError < StandardError
942
+ end
943
+ end
944
+
945
+ class Config # :nodoc:
946
+ def initialize(config_info=nil)
947
+ @mutex = Thread::Mutex.new
948
+ @config_info = config_info
949
+ @initialized = nil
950
+ @timeouts = nil
951
+ end
952
+
953
+ def timeouts=(values)
954
+ if values
955
+ values = Array(values)
956
+ values.each do |t|
957
+ Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric"
958
+ t > 0.0 or raise ArgumentError, "timeout=#{t} must be positive"
959
+ end
960
+ @timeouts = values
961
+ else
962
+ @timeouts = nil
963
+ end
964
+ end
965
+
966
+ def Config.parse_resolv_conf(filename)
967
+ nameserver = []
968
+ search = nil
969
+ ndots = 1
970
+ File.open(filename, 'rb') {|f|
971
+ f.each {|line|
972
+ line.sub!(/[#;].*/, '')
973
+ keyword, *args = line.split(/\s+/)
974
+ next unless keyword
975
+ case keyword
976
+ when 'nameserver'
977
+ nameserver.concat(args)
978
+ when 'domain'
979
+ next if args.empty?
980
+ search = [args[0]]
981
+ when 'search'
982
+ next if args.empty?
983
+ search = args
984
+ when 'options'
985
+ args.each {|arg|
986
+ case arg
987
+ when /\Andots:(\d+)\z/
988
+ ndots = $1.to_i
989
+ end
990
+ }
991
+ end
992
+ }
993
+ }
994
+ return { :nameserver => nameserver, :search => search, :ndots => ndots }
995
+ end
996
+
997
+ def Config.default_config_hash(filename="/etc/resolv.conf")
998
+ if File.exist? filename
999
+ config_hash = Config.parse_resolv_conf(filename)
1000
+ else
1001
+ if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
1002
+ require 'win32/resolv'
1003
+ search, nameserver = Win32::Resolv.get_resolv_info
1004
+ config_hash = {}
1005
+ config_hash[:nameserver] = nameserver if nameserver
1006
+ config_hash[:search] = [search].flatten if search
1007
+ end
1008
+ end
1009
+ config_hash || {}
1010
+ end
1011
+
1012
+ def lazy_initialize
1013
+ @mutex.synchronize {
1014
+ unless @initialized
1015
+ @nameserver_port = []
1016
+ @use_ipv6 = nil
1017
+ @search = nil
1018
+ @ndots = 1
1019
+ case @config_info
1020
+ when nil
1021
+ config_hash = Config.default_config_hash
1022
+ when String
1023
+ config_hash = Config.parse_resolv_conf(@config_info)
1024
+ when Hash
1025
+ config_hash = @config_info.dup
1026
+ if String === config_hash[:nameserver]
1027
+ config_hash[:nameserver] = [config_hash[:nameserver]]
1028
+ end
1029
+ if String === config_hash[:search]
1030
+ config_hash[:search] = [config_hash[:search]]
1031
+ end
1032
+ else
1033
+ raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
1034
+ end
1035
+ if config_hash.include? :nameserver
1036
+ @nameserver_port = config_hash[:nameserver].map {|ns| [ns, Port] }
1037
+ end
1038
+ if config_hash.include? :nameserver_port
1039
+ @nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
1040
+ end
1041
+ if config_hash.include? :use_ipv6
1042
+ @use_ipv6 = config_hash[:use_ipv6]
1043
+ end
1044
+ @search = config_hash[:search] if config_hash.include? :search
1045
+ @ndots = config_hash[:ndots] if config_hash.include? :ndots
1046
+ @raise_timeout_errors = config_hash[:raise_timeout_errors]
1047
+
1048
+ if @nameserver_port.empty?
1049
+ @nameserver_port << ['0.0.0.0', Port]
1050
+ end
1051
+ if @search
1052
+ @search = @search.map {|arg| Label.split(arg) }
1053
+ else
1054
+ hostname = Socket.gethostname
1055
+ if /\./ =~ hostname
1056
+ @search = [Label.split($')]
1057
+ else
1058
+ @search = [[]]
1059
+ end
1060
+ end
1061
+
1062
+ if !@nameserver_port.kind_of?(Array) ||
1063
+ @nameserver_port.any? {|ns_port|
1064
+ !(Array === ns_port) ||
1065
+ ns_port.length != 2
1066
+ !(String === ns_port[0]) ||
1067
+ !(Integer === ns_port[1])
1068
+ }
1069
+ raise ArgumentError.new("invalid nameserver config: #{@nameserver_port.inspect}")
1070
+ end
1071
+
1072
+ if !@search.kind_of?(Array) ||
1073
+ !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
1074
+ raise ArgumentError.new("invalid search config: #{@search.inspect}")
1075
+ end
1076
+
1077
+ if !@ndots.kind_of?(Integer)
1078
+ raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
1079
+ end
1080
+
1081
+ @initialized = true
1082
+ end
1083
+ }
1084
+ self
1085
+ end
1086
+
1087
+ def single?
1088
+ lazy_initialize
1089
+ if @nameserver_port.length == 1
1090
+ return @nameserver_port[0]
1091
+ else
1092
+ return nil
1093
+ end
1094
+ end
1095
+
1096
+ def nameserver_port
1097
+ @nameserver_port
1098
+ end
1099
+
1100
+ def use_ipv6?
1101
+ @use_ipv6
1102
+ end
1103
+
1104
+ def generate_candidates(name)
1105
+ candidates = nil
1106
+ name = Name.create(name)
1107
+ if name.absolute?
1108
+ candidates = [name]
1109
+ else
1110
+ if @ndots <= name.length - 1
1111
+ candidates = [Name.new(name.to_a)]
1112
+ else
1113
+ candidates = []
1114
+ end
1115
+ candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
1116
+ fname = Name.create("#{name}.")
1117
+ if !candidates.include?(fname)
1118
+ candidates << fname
1119
+ end
1120
+ end
1121
+ return candidates
1122
+ end
1123
+
1124
+ InitialTimeout = 5
1125
+
1126
+ def generate_timeouts
1127
+ ts = [InitialTimeout]
1128
+ ts << ts[-1] * 2 / @nameserver_port.length
1129
+ ts << ts[-1] * 2
1130
+ ts << ts[-1] * 2
1131
+ return ts
1132
+ end
1133
+
1134
+ def resolv(name)
1135
+ candidates = generate_candidates(name)
1136
+ timeouts = @timeouts || generate_timeouts
1137
+ timeout_error = false
1138
+ begin
1139
+ candidates.each {|candidate|
1140
+ begin
1141
+ timeouts.each {|tout|
1142
+ @nameserver_port.each {|nameserver, port|
1143
+ begin
1144
+ yield candidate, tout, nameserver, port
1145
+ rescue ResolvTimeout
1146
+ end
1147
+ }
1148
+ }
1149
+ timeout_error = true
1150
+ raise ResolvError.new("DNS resolv timeout: #{name}")
1151
+ rescue NXDomain
1152
+ end
1153
+ }
1154
+ rescue ResolvError
1155
+ raise if @raise_timeout_errors && timeout_error
1156
+ end
1157
+ end
1158
+
1159
+ ##
1160
+ # Indicates no such domain was found.
1161
+
1162
+ class NXDomain < ResolvError
1163
+ end
1164
+
1165
+ ##
1166
+ # Indicates some other unhandled resolver error was encountered.
1167
+
1168
+ class OtherResolvError < ResolvError
1169
+ end
1170
+ end
1171
+
1172
+ module OpCode # :nodoc:
1173
+ Query = 0
1174
+ IQuery = 1
1175
+ Status = 2
1176
+ Notify = 4
1177
+ Update = 5
1178
+ end
1179
+
1180
+ module RCode # :nodoc:
1181
+ NoError = 0
1182
+ FormErr = 1
1183
+ ServFail = 2
1184
+ NXDomain = 3
1185
+ NotImp = 4
1186
+ Refused = 5
1187
+ YXDomain = 6
1188
+ YXRRSet = 7
1189
+ NXRRSet = 8
1190
+ NotAuth = 9
1191
+ NotZone = 10
1192
+ BADVERS = 16
1193
+ BADSIG = 16
1194
+ BADKEY = 17
1195
+ BADTIME = 18
1196
+ BADMODE = 19
1197
+ BADNAME = 20
1198
+ BADALG = 21
1199
+ end
1200
+
1201
+ ##
1202
+ # Indicates that the DNS response was unable to be decoded.
1203
+
1204
+ class DecodeError < StandardError
1205
+ end
1206
+
1207
+ ##
1208
+ # Indicates that the DNS request was unable to be encoded.
1209
+
1210
+ class EncodeError < StandardError
1211
+ end
1212
+
1213
+ module Label # :nodoc:
1214
+ def self.split(arg)
1215
+ labels = []
1216
+ arg.scan(/[^\.]+/) {labels << Str.new($&)}
1217
+ return labels
1218
+ end
1219
+
1220
+ class Str # :nodoc:
1221
+ def initialize(string)
1222
+ @string = string
1223
+ # case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
1224
+ # This assumes @string is given in ASCII compatible encoding.
1225
+ @downcase = string.b.downcase
1226
+ end
1227
+ attr_reader :string, :downcase
1228
+
1229
+ def to_s
1230
+ return @string
1231
+ end
1232
+
1233
+ def inspect
1234
+ return "#<#{self.class} #{self}>"
1235
+ end
1236
+
1237
+ def ==(other)
1238
+ return self.class == other.class && @downcase == other.downcase
1239
+ end
1240
+
1241
+ def eql?(other)
1242
+ return self == other
1243
+ end
1244
+
1245
+ def hash
1246
+ return @downcase.hash
1247
+ end
1248
+ end
1249
+ end
1250
+
1251
+ ##
1252
+ # A representation of a DNS name.
1253
+
1254
+ class Name
1255
+
1256
+ ##
1257
+ # Creates a new DNS name from +arg+. +arg+ can be:
1258
+ #
1259
+ # Name:: returns +arg+.
1260
+ # String:: Creates a new Name.
1261
+
1262
+ def self.create(arg)
1263
+ case arg
1264
+ when Name
1265
+ return arg
1266
+ when String
1267
+ return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
1268
+ else
1269
+ raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
1270
+ end
1271
+ end
1272
+
1273
+ def initialize(labels, absolute=true) # :nodoc:
1274
+ labels = labels.map {|label|
1275
+ case label
1276
+ when String then Label::Str.new(label)
1277
+ when Label::Str then label
1278
+ else
1279
+ raise ArgumentError, "unexpected label: #{label.inspect}"
1280
+ end
1281
+ }
1282
+ @labels = labels
1283
+ @absolute = absolute
1284
+ end
1285
+
1286
+ def inspect # :nodoc:
1287
+ "#<#{self.class}: #{self}#{@absolute ? '.' : ''}>"
1288
+ end
1289
+
1290
+ ##
1291
+ # True if this name is absolute.
1292
+
1293
+ def absolute?
1294
+ return @absolute
1295
+ end
1296
+
1297
+ def ==(other) # :nodoc:
1298
+ return false unless Name === other
1299
+ return false unless @absolute == other.absolute?
1300
+ return @labels == other.to_a
1301
+ end
1302
+
1303
+ alias eql? == # :nodoc:
1304
+
1305
+ ##
1306
+ # Returns true if +other+ is a subdomain.
1307
+ #
1308
+ # Example:
1309
+ #
1310
+ # domain = Gem::Resolv::DNS::Name.create("y.z")
1311
+ # p Gem::Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
1312
+ # p Gem::Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
1313
+ # p Gem::Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
1314
+ # p Gem::Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
1315
+ # p Gem::Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
1316
+ # p Gem::Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
1317
+ #
1318
+
1319
+ def subdomain_of?(other)
1320
+ raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
1321
+ return false if @absolute != other.absolute?
1322
+ other_len = other.length
1323
+ return false if @labels.length <= other_len
1324
+ return @labels[-other_len, other_len] == other.to_a
1325
+ end
1326
+
1327
+ def hash # :nodoc:
1328
+ return @labels.hash ^ @absolute.hash
1329
+ end
1330
+
1331
+ def to_a # :nodoc:
1332
+ return @labels
1333
+ end
1334
+
1335
+ def length # :nodoc:
1336
+ return @labels.length
1337
+ end
1338
+
1339
+ def [](i) # :nodoc:
1340
+ return @labels[i]
1341
+ end
1342
+
1343
+ ##
1344
+ # returns the domain name as a string.
1345
+ #
1346
+ # The domain name doesn't have a trailing dot even if the name object is
1347
+ # absolute.
1348
+ #
1349
+ # Example:
1350
+ #
1351
+ # p Gem::Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
1352
+ # p Gem::Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
1353
+
1354
+ def to_s
1355
+ return @labels.join('.')
1356
+ end
1357
+ end
1358
+
1359
+ class Message # :nodoc:
1360
+ @@identifier = -1
1361
+
1362
+ def initialize(id = (@@identifier += 1) & 0xffff)
1363
+ @id = id
1364
+ @qr = 0
1365
+ @opcode = 0
1366
+ @aa = 0
1367
+ @tc = 0
1368
+ @rd = 0 # recursion desired
1369
+ @ra = 0 # recursion available
1370
+ @rcode = 0
1371
+ @question = []
1372
+ @answer = []
1373
+ @authority = []
1374
+ @additional = []
1375
+ end
1376
+
1377
+ attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
1378
+ attr_reader :question, :answer, :authority, :additional
1379
+
1380
+ def ==(other)
1381
+ return @id == other.id &&
1382
+ @qr == other.qr &&
1383
+ @opcode == other.opcode &&
1384
+ @aa == other.aa &&
1385
+ @tc == other.tc &&
1386
+ @rd == other.rd &&
1387
+ @ra == other.ra &&
1388
+ @rcode == other.rcode &&
1389
+ @question == other.question &&
1390
+ @answer == other.answer &&
1391
+ @authority == other.authority &&
1392
+ @additional == other.additional
1393
+ end
1394
+
1395
+ def add_question(name, typeclass)
1396
+ @question << [Name.create(name), typeclass]
1397
+ end
1398
+
1399
+ def each_question
1400
+ @question.each {|name, typeclass|
1401
+ yield name, typeclass
1402
+ }
1403
+ end
1404
+
1405
+ def add_answer(name, ttl, data)
1406
+ @answer << [Name.create(name), ttl, data]
1407
+ end
1408
+
1409
+ def each_answer
1410
+ @answer.each {|name, ttl, data|
1411
+ yield name, ttl, data
1412
+ }
1413
+ end
1414
+
1415
+ def add_authority(name, ttl, data)
1416
+ @authority << [Name.create(name), ttl, data]
1417
+ end
1418
+
1419
+ def each_authority
1420
+ @authority.each {|name, ttl, data|
1421
+ yield name, ttl, data
1422
+ }
1423
+ end
1424
+
1425
+ def add_additional(name, ttl, data)
1426
+ @additional << [Name.create(name), ttl, data]
1427
+ end
1428
+
1429
+ def each_additional
1430
+ @additional.each {|name, ttl, data|
1431
+ yield name, ttl, data
1432
+ }
1433
+ end
1434
+
1435
+ def each_resource
1436
+ each_answer {|name, ttl, data| yield name, ttl, data}
1437
+ each_authority {|name, ttl, data| yield name, ttl, data}
1438
+ each_additional {|name, ttl, data| yield name, ttl, data}
1439
+ end
1440
+
1441
+ def encode
1442
+ return MessageEncoder.new {|msg|
1443
+ msg.put_pack('nnnnnn',
1444
+ @id,
1445
+ (@qr & 1) << 15 |
1446
+ (@opcode & 15) << 11 |
1447
+ (@aa & 1) << 10 |
1448
+ (@tc & 1) << 9 |
1449
+ (@rd & 1) << 8 |
1450
+ (@ra & 1) << 7 |
1451
+ (@rcode & 15),
1452
+ @question.length,
1453
+ @answer.length,
1454
+ @authority.length,
1455
+ @additional.length)
1456
+ @question.each {|q|
1457
+ name, typeclass = q
1458
+ msg.put_name(name)
1459
+ msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
1460
+ }
1461
+ [@answer, @authority, @additional].each {|rr|
1462
+ rr.each {|r|
1463
+ name, ttl, data = r
1464
+ msg.put_name(name)
1465
+ msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
1466
+ msg.put_length16 {data.encode_rdata(msg)}
1467
+ }
1468
+ }
1469
+ }.to_s
1470
+ end
1471
+
1472
+ class MessageEncoder # :nodoc:
1473
+ def initialize
1474
+ @data = ''.dup
1475
+ @names = {}
1476
+ yield self
1477
+ end
1478
+
1479
+ def to_s
1480
+ return @data
1481
+ end
1482
+
1483
+ def put_bytes(d)
1484
+ @data << d
1485
+ end
1486
+
1487
+ def put_pack(template, *d)
1488
+ @data << d.pack(template)
1489
+ end
1490
+
1491
+ def put_length16
1492
+ length_index = @data.length
1493
+ @data << "\0\0"
1494
+ data_start = @data.length
1495
+ yield
1496
+ data_end = @data.length
1497
+ @data[length_index, 2] = [data_end - data_start].pack("n")
1498
+ end
1499
+
1500
+ def put_string(d)
1501
+ self.put_pack("C", d.length)
1502
+ @data << d
1503
+ end
1504
+
1505
+ def put_string_list(ds)
1506
+ ds.each {|d|
1507
+ self.put_string(d)
1508
+ }
1509
+ end
1510
+
1511
+ def put_name(d, compress: true)
1512
+ put_labels(d.to_a, compress: compress)
1513
+ end
1514
+
1515
+ def put_labels(d, compress: true)
1516
+ d.each_index {|i|
1517
+ domain = d[i..-1]
1518
+ if compress && idx = @names[domain]
1519
+ self.put_pack("n", 0xc000 | idx)
1520
+ return
1521
+ else
1522
+ if @data.length < 0x4000
1523
+ @names[domain] = @data.length
1524
+ end
1525
+ self.put_label(d[i])
1526
+ end
1527
+ }
1528
+ @data << "\0"
1529
+ end
1530
+
1531
+ def put_label(d)
1532
+ self.put_string(d.to_s)
1533
+ end
1534
+ end
1535
+
1536
+ def Message.decode(m)
1537
+ o = Message.new(0)
1538
+ MessageDecoder.new(m) {|msg|
1539
+ id, flag, qdcount, ancount, nscount, arcount =
1540
+ msg.get_unpack('nnnnnn')
1541
+ o.id = id
1542
+ o.tc = (flag >> 9) & 1
1543
+ o.rcode = flag & 15
1544
+ return o unless o.tc.zero?
1545
+
1546
+ o.qr = (flag >> 15) & 1
1547
+ o.opcode = (flag >> 11) & 15
1548
+ o.aa = (flag >> 10) & 1
1549
+ o.rd = (flag >> 8) & 1
1550
+ o.ra = (flag >> 7) & 1
1551
+ (1..qdcount).each {
1552
+ name, typeclass = msg.get_question
1553
+ o.add_question(name, typeclass)
1554
+ }
1555
+ (1..ancount).each {
1556
+ name, ttl, data = msg.get_rr
1557
+ o.add_answer(name, ttl, data)
1558
+ }
1559
+ (1..nscount).each {
1560
+ name, ttl, data = msg.get_rr
1561
+ o.add_authority(name, ttl, data)
1562
+ }
1563
+ (1..arcount).each {
1564
+ name, ttl, data = msg.get_rr
1565
+ o.add_additional(name, ttl, data)
1566
+ }
1567
+ }
1568
+ return o
1569
+ end
1570
+
1571
+ class MessageDecoder # :nodoc:
1572
+ def initialize(data)
1573
+ @data = data
1574
+ @index = 0
1575
+ @limit = data.bytesize
1576
+ yield self
1577
+ end
1578
+
1579
+ def inspect
1580
+ "\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
1581
+ end
1582
+
1583
+ def get_length16
1584
+ len, = self.get_unpack('n')
1585
+ save_limit = @limit
1586
+ @limit = @index + len
1587
+ d = yield(len)
1588
+ if @index < @limit
1589
+ raise DecodeError.new("junk exists")
1590
+ elsif @limit < @index
1591
+ raise DecodeError.new("limit exceeded")
1592
+ end
1593
+ @limit = save_limit
1594
+ return d
1595
+ end
1596
+
1597
+ def get_bytes(len = @limit - @index)
1598
+ raise DecodeError.new("limit exceeded") if @limit < @index + len
1599
+ d = @data.byteslice(@index, len)
1600
+ @index += len
1601
+ return d
1602
+ end
1603
+
1604
+ def get_unpack(template)
1605
+ len = 0
1606
+ template.each_byte {|byte|
1607
+ byte = "%c" % byte
1608
+ case byte
1609
+ when ?c, ?C
1610
+ len += 1
1611
+ when ?n
1612
+ len += 2
1613
+ when ?N
1614
+ len += 4
1615
+ else
1616
+ raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
1617
+ end
1618
+ }
1619
+ raise DecodeError.new("limit exceeded") if @limit < @index + len
1620
+ arr = @data.unpack("@#{@index}#{template}")
1621
+ @index += len
1622
+ return arr
1623
+ end
1624
+
1625
+ def get_string
1626
+ raise DecodeError.new("limit exceeded") if @limit <= @index
1627
+ len = @data.getbyte(@index)
1628
+ raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
1629
+ d = @data.byteslice(@index + 1, len)
1630
+ @index += 1 + len
1631
+ return d
1632
+ end
1633
+
1634
+ def get_string_list
1635
+ strings = []
1636
+ while @index < @limit
1637
+ strings << self.get_string
1638
+ end
1639
+ strings
1640
+ end
1641
+
1642
+ def get_list
1643
+ [].tap do |values|
1644
+ while @index < @limit
1645
+ values << yield
1646
+ end
1647
+ end
1648
+ end
1649
+
1650
+ def get_name
1651
+ return Name.new(self.get_labels)
1652
+ end
1653
+
1654
+ def get_labels
1655
+ prev_index = @index
1656
+ save_index = nil
1657
+ d = []
1658
+ while true
1659
+ raise DecodeError.new("limit exceeded") if @limit <= @index
1660
+ case @data.getbyte(@index)
1661
+ when 0
1662
+ @index += 1
1663
+ if save_index
1664
+ @index = save_index
1665
+ end
1666
+ return d
1667
+ when 192..255
1668
+ idx = self.get_unpack('n')[0] & 0x3fff
1669
+ if prev_index <= idx
1670
+ raise DecodeError.new("non-backward name pointer")
1671
+ end
1672
+ prev_index = idx
1673
+ if !save_index
1674
+ save_index = @index
1675
+ end
1676
+ @index = idx
1677
+ else
1678
+ d << self.get_label
1679
+ end
1680
+ end
1681
+ end
1682
+
1683
+ def get_label
1684
+ return Label::Str.new(self.get_string)
1685
+ end
1686
+
1687
+ def get_question
1688
+ name = self.get_name
1689
+ type, klass = self.get_unpack("nn")
1690
+ return name, Resource.get_class(type, klass)
1691
+ end
1692
+
1693
+ def get_rr
1694
+ name = self.get_name
1695
+ type, klass, ttl = self.get_unpack('nnN')
1696
+ typeclass = Resource.get_class(type, klass)
1697
+ res = self.get_length16 do
1698
+ begin
1699
+ typeclass.decode_rdata self
1700
+ rescue => e
1701
+ raise DecodeError, e.message, e.backtrace
1702
+ end
1703
+ end
1704
+ res.instance_variable_set :@ttl, ttl
1705
+ return name, ttl, res
1706
+ end
1707
+ end
1708
+ end
1709
+
1710
+ ##
1711
+ # SvcParams for service binding RRs. [RFC9460]
1712
+
1713
+ class SvcParams
1714
+ include Enumerable
1715
+
1716
+ ##
1717
+ # Create a list of SvcParams with the given initial content.
1718
+ #
1719
+ # +params+ has to be an enumerable of +SvcParam+s.
1720
+ # If its content has +SvcParam+s with the duplicate key,
1721
+ # the one appears last takes precedence.
1722
+
1723
+ def initialize(params = [])
1724
+ @params = {}
1725
+
1726
+ params.each do |param|
1727
+ add param
1728
+ end
1729
+ end
1730
+
1731
+ ##
1732
+ # Get SvcParam for the given +key+ in this list.
1733
+
1734
+ def [](key)
1735
+ @params[canonical_key(key)]
1736
+ end
1737
+
1738
+ ##
1739
+ # Get the number of SvcParams in this list.
1740
+
1741
+ def count
1742
+ @params.count
1743
+ end
1744
+
1745
+ ##
1746
+ # Get whether this list is empty.
1747
+
1748
+ def empty?
1749
+ @params.empty?
1750
+ end
1751
+
1752
+ ##
1753
+ # Add the SvcParam +param+ to this list, overwriting the existing one with the same key.
1754
+
1755
+ def add(param)
1756
+ @params[param.class.key_number] = param
1757
+ end
1758
+
1759
+ ##
1760
+ # Remove the +SvcParam+ with the given +key+ and return it.
1761
+
1762
+ def delete(key)
1763
+ @params.delete(canonical_key(key))
1764
+ end
1765
+
1766
+ ##
1767
+ # Enumerate the +SvcParam+s in this list.
1768
+
1769
+ def each(&block)
1770
+ return enum_for(:each) unless block
1771
+ @params.each_value(&block)
1772
+ end
1773
+
1774
+ def encode(msg) # :nodoc:
1775
+ @params.keys.sort.each do |key|
1776
+ msg.put_pack('n', key)
1777
+ msg.put_length16 do
1778
+ @params.fetch(key).encode(msg)
1779
+ end
1780
+ end
1781
+ end
1782
+
1783
+ def self.decode(msg) # :nodoc:
1784
+ params = msg.get_list do
1785
+ key, = msg.get_unpack('n')
1786
+ msg.get_length16 do
1787
+ SvcParam::ClassHash[key].decode(msg)
1788
+ end
1789
+ end
1790
+
1791
+ return self.new(params)
1792
+ end
1793
+
1794
+ private
1795
+
1796
+ def canonical_key(key) # :nodoc:
1797
+ case key
1798
+ when Integer
1799
+ key
1800
+ when /\Akey(\d+)\z/
1801
+ Integer($1)
1802
+ when Symbol
1803
+ SvcParam::ClassHash[key].key_number
1804
+ else
1805
+ raise TypeError, 'key must be either String or Symbol'
1806
+ end
1807
+ end
1808
+ end
1809
+
1810
+
1811
+ ##
1812
+ # Base class for SvcParam. [RFC9460]
1813
+
1814
+ class SvcParam
1815
+
1816
+ ##
1817
+ # Get the presentation name of the SvcParamKey.
1818
+
1819
+ def self.key_name
1820
+ const_get(:KeyName)
1821
+ end
1822
+
1823
+ ##
1824
+ # Get the registered number of the SvcParamKey.
1825
+
1826
+ def self.key_number
1827
+ const_get(:KeyNumber)
1828
+ end
1829
+
1830
+ ClassHash = Hash.new do |h, key| # :nodoc:
1831
+ case key
1832
+ when Integer
1833
+ Generic.create(key)
1834
+ when /\Akey(?<key>\d+)\z/
1835
+ Generic.create(key.to_int)
1836
+ when Symbol
1837
+ raise KeyError, "unknown key #{key}"
1838
+ else
1839
+ raise TypeError, 'key must be either String or Symbol'
1840
+ end
1841
+ end
1842
+
1843
+ ##
1844
+ # Generic SvcParam abstract class.
1845
+
1846
+ class Generic < SvcParam
1847
+
1848
+ ##
1849
+ # SvcParamValue in wire-format byte string.
1850
+
1851
+ attr_reader :value
1852
+
1853
+ ##
1854
+ # Create generic SvcParam
1855
+
1856
+ def initialize(value)
1857
+ @value = value
1858
+ end
1859
+
1860
+ def encode(msg) # :nodoc:
1861
+ msg.put_bytes(@value)
1862
+ end
1863
+
1864
+ def self.decode(msg) # :nodoc:
1865
+ return self.new(msg.get_bytes)
1866
+ end
1867
+
1868
+ def self.create(key_number)
1869
+ c = Class.new(Generic)
1870
+ key_name = :"key#{key_number}"
1871
+ c.const_set(:KeyName, key_name)
1872
+ c.const_set(:KeyNumber, key_number)
1873
+ self.const_set(:"Key#{key_number}", c)
1874
+ ClassHash[key_name] = ClassHash[key_number] = c
1875
+ return c
1876
+ end
1877
+ end
1878
+
1879
+ ##
1880
+ # "mandatory" SvcParam -- Mandatory keys in service binding RR
1881
+
1882
+ class Mandatory < SvcParam
1883
+ KeyName = :mandatory
1884
+ KeyNumber = 0
1885
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1886
+
1887
+ ##
1888
+ # Mandatory keys.
1889
+
1890
+ attr_reader :keys
1891
+
1892
+ ##
1893
+ # Initialize "mandatory" ScvParam.
1894
+
1895
+ def initialize(keys)
1896
+ @keys = keys.map(&:to_int)
1897
+ end
1898
+
1899
+ def encode(msg) # :nodoc:
1900
+ @keys.sort.each do |key|
1901
+ msg.put_pack('n', key)
1902
+ end
1903
+ end
1904
+
1905
+ def self.decode(msg) # :nodoc:
1906
+ keys = msg.get_list { msg.get_unpack('n')[0] }
1907
+ return self.new(keys)
1908
+ end
1909
+ end
1910
+
1911
+ ##
1912
+ # "alpn" SvcParam -- Additional supported protocols
1913
+
1914
+ class ALPN < SvcParam
1915
+ KeyName = :alpn
1916
+ KeyNumber = 1
1917
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1918
+
1919
+ ##
1920
+ # Supported protocol IDs.
1921
+
1922
+ attr_reader :protocol_ids
1923
+
1924
+ ##
1925
+ # Initialize "alpn" ScvParam.
1926
+
1927
+ def initialize(protocol_ids)
1928
+ @protocol_ids = protocol_ids.map(&:to_str)
1929
+ end
1930
+
1931
+ def encode(msg) # :nodoc:
1932
+ msg.put_string_list(@protocol_ids)
1933
+ end
1934
+
1935
+ def self.decode(msg) # :nodoc:
1936
+ return self.new(msg.get_string_list)
1937
+ end
1938
+ end
1939
+
1940
+ ##
1941
+ # "no-default-alpn" SvcParam -- No support for default protocol
1942
+
1943
+ class NoDefaultALPN < SvcParam
1944
+ KeyName = :'no-default-alpn'
1945
+ KeyNumber = 2
1946
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1947
+
1948
+ def encode(msg) # :nodoc:
1949
+ # no payload
1950
+ end
1951
+
1952
+ def self.decode(msg) # :nodoc:
1953
+ return self.new
1954
+ end
1955
+ end
1956
+
1957
+ ##
1958
+ # "port" SvcParam -- Port for alternative endpoint
1959
+
1960
+ class Port < SvcParam
1961
+ KeyName = :port
1962
+ KeyNumber = 3
1963
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1964
+
1965
+ ##
1966
+ # Port number.
1967
+
1968
+ attr_reader :port
1969
+
1970
+ ##
1971
+ # Initialize "port" ScvParam.
1972
+
1973
+ def initialize(port)
1974
+ @port = port.to_int
1975
+ end
1976
+
1977
+ def encode(msg) # :nodoc:
1978
+ msg.put_pack('n', @port)
1979
+ end
1980
+
1981
+ def self.decode(msg) # :nodoc:
1982
+ port, = msg.get_unpack('n')
1983
+ return self.new(port)
1984
+ end
1985
+ end
1986
+
1987
+ ##
1988
+ # "ipv4hint" SvcParam -- IPv4 address hints
1989
+
1990
+ class IPv4Hint < SvcParam
1991
+ KeyName = :ipv4hint
1992
+ KeyNumber = 4
1993
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1994
+
1995
+ ##
1996
+ # Set of IPv4 addresses.
1997
+
1998
+ attr_reader :addresses
1999
+
2000
+ ##
2001
+ # Initialize "ipv4hint" ScvParam.
2002
+
2003
+ def initialize(addresses)
2004
+ @addresses = addresses.map {|address| IPv4.create(address) }
2005
+ end
2006
+
2007
+ def encode(msg) # :nodoc:
2008
+ @addresses.each do |address|
2009
+ msg.put_bytes(address.address)
2010
+ end
2011
+ end
2012
+
2013
+ def self.decode(msg) # :nodoc:
2014
+ addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) }
2015
+ return self.new(addresses)
2016
+ end
2017
+ end
2018
+
2019
+ ##
2020
+ # "ipv6hint" SvcParam -- IPv6 address hints
2021
+
2022
+ class IPv6Hint < SvcParam
2023
+ KeyName = :ipv6hint
2024
+ KeyNumber = 6
2025
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
2026
+
2027
+ ##
2028
+ # Set of IPv6 addresses.
2029
+
2030
+ attr_reader :addresses
2031
+
2032
+ ##
2033
+ # Initialize "ipv6hint" ScvParam.
2034
+
2035
+ def initialize(addresses)
2036
+ @addresses = addresses.map {|address| IPv6.create(address) }
2037
+ end
2038
+
2039
+ def encode(msg) # :nodoc:
2040
+ @addresses.each do |address|
2041
+ msg.put_bytes(address.address)
2042
+ end
2043
+ end
2044
+
2045
+ def self.decode(msg) # :nodoc:
2046
+ addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) }
2047
+ return self.new(addresses)
2048
+ end
2049
+ end
2050
+
2051
+ ##
2052
+ # "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461]
2053
+
2054
+ class DoHPath < SvcParam
2055
+ KeyName = :dohpath
2056
+ KeyNumber = 7
2057
+ ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
2058
+
2059
+ ##
2060
+ # URI template for DoH queries.
2061
+
2062
+ attr_reader :template
2063
+
2064
+ ##
2065
+ # Initialize "dohpath" ScvParam.
2066
+
2067
+ def initialize(template)
2068
+ @template = template.encode('utf-8')
2069
+ end
2070
+
2071
+ def encode(msg) # :nodoc:
2072
+ msg.put_bytes(@template)
2073
+ end
2074
+
2075
+ def self.decode(msg) # :nodoc:
2076
+ template = msg.get_bytes.force_encoding('utf-8')
2077
+ return self.new(template)
2078
+ end
2079
+ end
2080
+ end
2081
+
2082
+ ##
2083
+ # A DNS query abstract class.
2084
+
2085
+ class Query
2086
+ def encode_rdata(msg) # :nodoc:
2087
+ raise EncodeError.new("#{self.class} is query.")
2088
+ end
2089
+
2090
+ def self.decode_rdata(msg) # :nodoc:
2091
+ raise DecodeError.new("#{self.class} is query.")
2092
+ end
2093
+ end
2094
+
2095
+ ##
2096
+ # A DNS resource abstract class.
2097
+
2098
+ class Resource < Query
2099
+
2100
+ ##
2101
+ # Remaining Time To Live for this Resource.
2102
+
2103
+ attr_reader :ttl
2104
+
2105
+ ClassHash = {} # :nodoc:
2106
+
2107
+ def encode_rdata(msg) # :nodoc:
2108
+ raise NotImplementedError.new
2109
+ end
2110
+
2111
+ def self.decode_rdata(msg) # :nodoc:
2112
+ raise NotImplementedError.new
2113
+ end
2114
+
2115
+ def ==(other) # :nodoc:
2116
+ return false unless self.class == other.class
2117
+ s_ivars = self.instance_variables
2118
+ s_ivars.sort!
2119
+ s_ivars.delete :@ttl
2120
+ o_ivars = other.instance_variables
2121
+ o_ivars.sort!
2122
+ o_ivars.delete :@ttl
2123
+ return s_ivars == o_ivars &&
2124
+ s_ivars.collect {|name| self.instance_variable_get name} ==
2125
+ o_ivars.collect {|name| other.instance_variable_get name}
2126
+ end
2127
+
2128
+ def eql?(other) # :nodoc:
2129
+ return self == other
2130
+ end
2131
+
2132
+ def hash # :nodoc:
2133
+ h = 0
2134
+ vars = self.instance_variables
2135
+ vars.delete :@ttl
2136
+ vars.each {|name|
2137
+ h ^= self.instance_variable_get(name).hash
2138
+ }
2139
+ return h
2140
+ end
2141
+
2142
+ def self.get_class(type_value, class_value) # :nodoc:
2143
+ return ClassHash[[type_value, class_value]] ||
2144
+ Generic.create(type_value, class_value)
2145
+ end
2146
+
2147
+ ##
2148
+ # A generic resource abstract class.
2149
+
2150
+ class Generic < Resource
2151
+
2152
+ ##
2153
+ # Creates a new generic resource.
2154
+
2155
+ def initialize(data)
2156
+ @data = data
2157
+ end
2158
+
2159
+ ##
2160
+ # Data for this generic resource.
2161
+
2162
+ attr_reader :data
2163
+
2164
+ def encode_rdata(msg) # :nodoc:
2165
+ msg.put_bytes(data)
2166
+ end
2167
+
2168
+ def self.decode_rdata(msg) # :nodoc:
2169
+ return self.new(msg.get_bytes)
2170
+ end
2171
+
2172
+ def self.create(type_value, class_value) # :nodoc:
2173
+ c = Class.new(Generic)
2174
+ c.const_set(:TypeValue, type_value)
2175
+ c.const_set(:ClassValue, class_value)
2176
+ Generic.const_set("Type#{type_value}_Class#{class_value}", c)
2177
+ ClassHash[[type_value, class_value]] = c
2178
+ return c
2179
+ end
2180
+ end
2181
+
2182
+ ##
2183
+ # Domain Name resource abstract class.
2184
+
2185
+ class DomainName < Resource
2186
+
2187
+ ##
2188
+ # Creates a new DomainName from +name+.
2189
+
2190
+ def initialize(name)
2191
+ @name = name
2192
+ end
2193
+
2194
+ ##
2195
+ # The name of this DomainName.
2196
+
2197
+ attr_reader :name
2198
+
2199
+ def encode_rdata(msg) # :nodoc:
2200
+ msg.put_name(@name)
2201
+ end
2202
+
2203
+ def self.decode_rdata(msg) # :nodoc:
2204
+ return self.new(msg.get_name)
2205
+ end
2206
+ end
2207
+
2208
+ # Standard (class generic) RRs
2209
+
2210
+ ClassValue = nil # :nodoc:
2211
+
2212
+ ##
2213
+ # An authoritative name server.
2214
+
2215
+ class NS < DomainName
2216
+ TypeValue = 2 # :nodoc:
2217
+ end
2218
+
2219
+ ##
2220
+ # The canonical name for an alias.
2221
+
2222
+ class CNAME < DomainName
2223
+ TypeValue = 5 # :nodoc:
2224
+ end
2225
+
2226
+ ##
2227
+ # Start Of Authority resource.
2228
+
2229
+ class SOA < Resource
2230
+
2231
+ TypeValue = 6 # :nodoc:
2232
+
2233
+ ##
2234
+ # Creates a new SOA record. See the attr documentation for the
2235
+ # details of each argument.
2236
+
2237
+ def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
2238
+ @mname = mname
2239
+ @rname = rname
2240
+ @serial = serial
2241
+ @refresh = refresh
2242
+ @retry = retry_
2243
+ @expire = expire
2244
+ @minimum = minimum
2245
+ end
2246
+
2247
+ ##
2248
+ # Name of the host where the master zone file for this zone resides.
2249
+
2250
+ attr_reader :mname
2251
+
2252
+ ##
2253
+ # The person responsible for this domain name.
2254
+
2255
+ attr_reader :rname
2256
+
2257
+ ##
2258
+ # The version number of the zone file.
2259
+
2260
+ attr_reader :serial
2261
+
2262
+ ##
2263
+ # How often, in seconds, a secondary name server is to check for
2264
+ # updates from the primary name server.
2265
+
2266
+ attr_reader :refresh
2267
+
2268
+ ##
2269
+ # How often, in seconds, a secondary name server is to retry after a
2270
+ # failure to check for a refresh.
2271
+
2272
+ attr_reader :retry
2273
+
2274
+ ##
2275
+ # Time in seconds that a secondary name server is to use the data
2276
+ # before refreshing from the primary name server.
2277
+
2278
+ attr_reader :expire
2279
+
2280
+ ##
2281
+ # The minimum number of seconds to be used for TTL values in RRs.
2282
+
2283
+ attr_reader :minimum
2284
+
2285
+ def encode_rdata(msg) # :nodoc:
2286
+ msg.put_name(@mname)
2287
+ msg.put_name(@rname)
2288
+ msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
2289
+ end
2290
+
2291
+ def self.decode_rdata(msg) # :nodoc:
2292
+ mname = msg.get_name
2293
+ rname = msg.get_name
2294
+ serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
2295
+ return self.new(
2296
+ mname, rname, serial, refresh, retry_, expire, minimum)
2297
+ end
2298
+ end
2299
+
2300
+ ##
2301
+ # A Pointer to another DNS name.
2302
+
2303
+ class PTR < DomainName
2304
+ TypeValue = 12 # :nodoc:
2305
+ end
2306
+
2307
+ ##
2308
+ # Host Information resource.
2309
+
2310
+ class HINFO < Resource
2311
+
2312
+ TypeValue = 13 # :nodoc:
2313
+
2314
+ ##
2315
+ # Creates a new HINFO running +os+ on +cpu+.
2316
+
2317
+ def initialize(cpu, os)
2318
+ @cpu = cpu
2319
+ @os = os
2320
+ end
2321
+
2322
+ ##
2323
+ # CPU architecture for this resource.
2324
+
2325
+ attr_reader :cpu
2326
+
2327
+ ##
2328
+ # Operating system for this resource.
2329
+
2330
+ attr_reader :os
2331
+
2332
+ def encode_rdata(msg) # :nodoc:
2333
+ msg.put_string(@cpu)
2334
+ msg.put_string(@os)
2335
+ end
2336
+
2337
+ def self.decode_rdata(msg) # :nodoc:
2338
+ cpu = msg.get_string
2339
+ os = msg.get_string
2340
+ return self.new(cpu, os)
2341
+ end
2342
+ end
2343
+
2344
+ ##
2345
+ # Mailing list or mailbox information.
2346
+
2347
+ class MINFO < Resource
2348
+
2349
+ TypeValue = 14 # :nodoc:
2350
+
2351
+ def initialize(rmailbx, emailbx)
2352
+ @rmailbx = rmailbx
2353
+ @emailbx = emailbx
2354
+ end
2355
+
2356
+ ##
2357
+ # Domain name responsible for this mail list or mailbox.
2358
+
2359
+ attr_reader :rmailbx
2360
+
2361
+ ##
2362
+ # Mailbox to use for error messages related to the mail list or mailbox.
2363
+
2364
+ attr_reader :emailbx
2365
+
2366
+ def encode_rdata(msg) # :nodoc:
2367
+ msg.put_name(@rmailbx)
2368
+ msg.put_name(@emailbx)
2369
+ end
2370
+
2371
+ def self.decode_rdata(msg) # :nodoc:
2372
+ rmailbx = msg.get_string
2373
+ emailbx = msg.get_string
2374
+ return self.new(rmailbx, emailbx)
2375
+ end
2376
+ end
2377
+
2378
+ ##
2379
+ # Mail Exchanger resource.
2380
+
2381
+ class MX < Resource
2382
+
2383
+ TypeValue= 15 # :nodoc:
2384
+
2385
+ ##
2386
+ # Creates a new MX record with +preference+, accepting mail at
2387
+ # +exchange+.
2388
+
2389
+ def initialize(preference, exchange)
2390
+ @preference = preference
2391
+ @exchange = exchange
2392
+ end
2393
+
2394
+ ##
2395
+ # The preference for this MX.
2396
+
2397
+ attr_reader :preference
2398
+
2399
+ ##
2400
+ # The host of this MX.
2401
+
2402
+ attr_reader :exchange
2403
+
2404
+ def encode_rdata(msg) # :nodoc:
2405
+ msg.put_pack('n', @preference)
2406
+ msg.put_name(@exchange)
2407
+ end
2408
+
2409
+ def self.decode_rdata(msg) # :nodoc:
2410
+ preference, = msg.get_unpack('n')
2411
+ exchange = msg.get_name
2412
+ return self.new(preference, exchange)
2413
+ end
2414
+ end
2415
+
2416
+ ##
2417
+ # Unstructured text resource.
2418
+
2419
+ class TXT < Resource
2420
+
2421
+ TypeValue = 16 # :nodoc:
2422
+
2423
+ def initialize(first_string, *rest_strings)
2424
+ @strings = [first_string, *rest_strings]
2425
+ end
2426
+
2427
+ ##
2428
+ # Returns an Array of Strings for this TXT record.
2429
+
2430
+ attr_reader :strings
2431
+
2432
+ ##
2433
+ # Returns the concatenated string from +strings+.
2434
+
2435
+ def data
2436
+ @strings.join("")
2437
+ end
2438
+
2439
+ def encode_rdata(msg) # :nodoc:
2440
+ msg.put_string_list(@strings)
2441
+ end
2442
+
2443
+ def self.decode_rdata(msg) # :nodoc:
2444
+ strings = msg.get_string_list
2445
+ return self.new(*strings)
2446
+ end
2447
+ end
2448
+
2449
+ ##
2450
+ # Location resource
2451
+
2452
+ class LOC < Resource
2453
+
2454
+ TypeValue = 29 # :nodoc:
2455
+
2456
+ def initialize(version, ssize, hprecision, vprecision, latitude, longitude, altitude)
2457
+ @version = version
2458
+ @ssize = Gem::Resolv::LOC::Size.create(ssize)
2459
+ @hprecision = Gem::Resolv::LOC::Size.create(hprecision)
2460
+ @vprecision = Gem::Resolv::LOC::Size.create(vprecision)
2461
+ @latitude = Gem::Resolv::LOC::Coord.create(latitude)
2462
+ @longitude = Gem::Resolv::LOC::Coord.create(longitude)
2463
+ @altitude = Gem::Resolv::LOC::Alt.create(altitude)
2464
+ end
2465
+
2466
+ ##
2467
+ # Returns the version value for this LOC record which should always be 00
2468
+
2469
+ attr_reader :version
2470
+
2471
+ ##
2472
+ # The spherical size of this LOC
2473
+ # in meters using scientific notation as 2 integers of XeY
2474
+
2475
+ attr_reader :ssize
2476
+
2477
+ ##
2478
+ # The horizontal precision using ssize type values
2479
+ # in meters using scientific notation as 2 integers of XeY
2480
+ # for precision use value/2 e.g. 2m = +/-1m
2481
+
2482
+ attr_reader :hprecision
2483
+
2484
+ ##
2485
+ # The vertical precision using ssize type values
2486
+ # in meters using scientific notation as 2 integers of XeY
2487
+ # for precision use value/2 e.g. 2m = +/-1m
2488
+
2489
+ attr_reader :vprecision
2490
+
2491
+ ##
2492
+ # The latitude for this LOC where 2**31 is the equator
2493
+ # in thousandths of an arc second as an unsigned 32bit integer
2494
+
2495
+ attr_reader :latitude
2496
+
2497
+ ##
2498
+ # The longitude for this LOC where 2**31 is the prime meridian
2499
+ # in thousandths of an arc second as an unsigned 32bit integer
2500
+
2501
+ attr_reader :longitude
2502
+
2503
+ ##
2504
+ # The altitude of the LOC above a reference sphere whose surface sits 100km below the WGS84 spheroid
2505
+ # in centimeters as an unsigned 32bit integer
2506
+
2507
+ attr_reader :altitude
2508
+
2509
+
2510
+ def encode_rdata(msg) # :nodoc:
2511
+ msg.put_bytes(@version)
2512
+ msg.put_bytes(@ssize.scalar)
2513
+ msg.put_bytes(@hprecision.scalar)
2514
+ msg.put_bytes(@vprecision.scalar)
2515
+ msg.put_bytes(@latitude.coordinates)
2516
+ msg.put_bytes(@longitude.coordinates)
2517
+ msg.put_bytes(@altitude.altitude)
2518
+ end
2519
+
2520
+ def self.decode_rdata(msg) # :nodoc:
2521
+ version = msg.get_bytes(1)
2522
+ ssize = msg.get_bytes(1)
2523
+ hprecision = msg.get_bytes(1)
2524
+ vprecision = msg.get_bytes(1)
2525
+ latitude = msg.get_bytes(4)
2526
+ longitude = msg.get_bytes(4)
2527
+ altitude = msg.get_bytes(4)
2528
+ return self.new(
2529
+ version,
2530
+ Gem::Resolv::LOC::Size.new(ssize),
2531
+ Gem::Resolv::LOC::Size.new(hprecision),
2532
+ Gem::Resolv::LOC::Size.new(vprecision),
2533
+ Gem::Resolv::LOC::Coord.new(latitude,"lat"),
2534
+ Gem::Resolv::LOC::Coord.new(longitude,"lon"),
2535
+ Gem::Resolv::LOC::Alt.new(altitude)
2536
+ )
2537
+ end
2538
+ end
2539
+
2540
+ ##
2541
+ # A Query type requesting any RR.
2542
+
2543
+ class ANY < Query
2544
+ TypeValue = 255 # :nodoc:
2545
+ end
2546
+
2547
+ ClassInsensitiveTypes = [ # :nodoc:
2548
+ NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY
2549
+ ]
2550
+
2551
+ ##
2552
+ # module IN contains ARPA Internet specific RRs.
2553
+
2554
+ module IN
2555
+
2556
+ ClassValue = 1 # :nodoc:
2557
+
2558
+ ClassInsensitiveTypes.each {|s|
2559
+ c = Class.new(s)
2560
+ c.const_set(:TypeValue, s::TypeValue)
2561
+ c.const_set(:ClassValue, ClassValue)
2562
+ ClassHash[[s::TypeValue, ClassValue]] = c
2563
+ self.const_set(s.name.sub(/.*::/, ''), c)
2564
+ }
2565
+
2566
+ ##
2567
+ # IPv4 Address resource
2568
+
2569
+ class A < Resource
2570
+ TypeValue = 1
2571
+ ClassValue = IN::ClassValue
2572
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2573
+
2574
+ ##
2575
+ # Creates a new A for +address+.
2576
+
2577
+ def initialize(address)
2578
+ @address = IPv4.create(address)
2579
+ end
2580
+
2581
+ ##
2582
+ # The Gem::Resolv::IPv4 address for this A.
2583
+
2584
+ attr_reader :address
2585
+
2586
+ def encode_rdata(msg) # :nodoc:
2587
+ msg.put_bytes(@address.address)
2588
+ end
2589
+
2590
+ def self.decode_rdata(msg) # :nodoc:
2591
+ return self.new(IPv4.new(msg.get_bytes(4)))
2592
+ end
2593
+ end
2594
+
2595
+ ##
2596
+ # Well Known Service resource.
2597
+
2598
+ class WKS < Resource
2599
+ TypeValue = 11
2600
+ ClassValue = IN::ClassValue
2601
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2602
+
2603
+ def initialize(address, protocol, bitmap)
2604
+ @address = IPv4.create(address)
2605
+ @protocol = protocol
2606
+ @bitmap = bitmap
2607
+ end
2608
+
2609
+ ##
2610
+ # The host these services run on.
2611
+
2612
+ attr_reader :address
2613
+
2614
+ ##
2615
+ # IP protocol number for these services.
2616
+
2617
+ attr_reader :protocol
2618
+
2619
+ ##
2620
+ # A bit map of enabled services on this host.
2621
+ #
2622
+ # If protocol is 6 (TCP) then the 26th bit corresponds to the SMTP
2623
+ # service (port 25). If this bit is set, then an SMTP server should
2624
+ # be listening on TCP port 25; if zero, SMTP service is not
2625
+ # supported.
2626
+
2627
+ attr_reader :bitmap
2628
+
2629
+ def encode_rdata(msg) # :nodoc:
2630
+ msg.put_bytes(@address.address)
2631
+ msg.put_pack("n", @protocol)
2632
+ msg.put_bytes(@bitmap)
2633
+ end
2634
+
2635
+ def self.decode_rdata(msg) # :nodoc:
2636
+ address = IPv4.new(msg.get_bytes(4))
2637
+ protocol, = msg.get_unpack("n")
2638
+ bitmap = msg.get_bytes
2639
+ return self.new(address, protocol, bitmap)
2640
+ end
2641
+ end
2642
+
2643
+ ##
2644
+ # An IPv6 address record.
2645
+
2646
+ class AAAA < Resource
2647
+ TypeValue = 28
2648
+ ClassValue = IN::ClassValue
2649
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2650
+
2651
+ ##
2652
+ # Creates a new AAAA for +address+.
2653
+
2654
+ def initialize(address)
2655
+ @address = IPv6.create(address)
2656
+ end
2657
+
2658
+ ##
2659
+ # The Gem::Resolv::IPv6 address for this AAAA.
2660
+
2661
+ attr_reader :address
2662
+
2663
+ def encode_rdata(msg) # :nodoc:
2664
+ msg.put_bytes(@address.address)
2665
+ end
2666
+
2667
+ def self.decode_rdata(msg) # :nodoc:
2668
+ return self.new(IPv6.new(msg.get_bytes(16)))
2669
+ end
2670
+ end
2671
+
2672
+ ##
2673
+ # SRV resource record defined in RFC 2782
2674
+ #
2675
+ # These records identify the hostname and port that a service is
2676
+ # available at.
2677
+
2678
+ class SRV < Resource
2679
+ TypeValue = 33
2680
+ ClassValue = IN::ClassValue
2681
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2682
+
2683
+ # Create a SRV resource record.
2684
+ #
2685
+ # See the documentation for #priority, #weight, #port and #target
2686
+ # for +priority+, +weight+, +port and +target+ respectively.
2687
+
2688
+ def initialize(priority, weight, port, target)
2689
+ @priority = priority.to_int
2690
+ @weight = weight.to_int
2691
+ @port = port.to_int
2692
+ @target = Name.create(target)
2693
+ end
2694
+
2695
+ # The priority of this target host.
2696
+ #
2697
+ # A client MUST attempt to contact the target host with the
2698
+ # lowest-numbered priority it can reach; target hosts with the same
2699
+ # priority SHOULD be tried in an order defined by the weight field.
2700
+ # The range is 0-65535. Note that it is not widely implemented and
2701
+ # should be set to zero.
2702
+
2703
+ attr_reader :priority
2704
+
2705
+ # A server selection mechanism.
2706
+ #
2707
+ # The weight field specifies a relative weight for entries with the
2708
+ # same priority. Larger weights SHOULD be given a proportionately
2709
+ # higher probability of being selected. The range of this number is
2710
+ # 0-65535. Domain administrators SHOULD use Weight 0 when there
2711
+ # isn't any server selection to do, to make the RR easier to read
2712
+ # for humans (less noisy). Note that it is not widely implemented
2713
+ # and should be set to zero.
2714
+
2715
+ attr_reader :weight
2716
+
2717
+ # The port on this target host of this service.
2718
+ #
2719
+ # The range is 0-65535.
2720
+
2721
+ attr_reader :port
2722
+
2723
+ # The domain name of the target host.
2724
+ #
2725
+ # A target of "." means that the service is decidedly not available
2726
+ # at this domain.
2727
+
2728
+ attr_reader :target
2729
+
2730
+ def encode_rdata(msg) # :nodoc:
2731
+ msg.put_pack("n", @priority)
2732
+ msg.put_pack("n", @weight)
2733
+ msg.put_pack("n", @port)
2734
+ msg.put_name(@target, compress: false)
2735
+ end
2736
+
2737
+ def self.decode_rdata(msg) # :nodoc:
2738
+ priority, = msg.get_unpack("n")
2739
+ weight, = msg.get_unpack("n")
2740
+ port, = msg.get_unpack("n")
2741
+ target = msg.get_name
2742
+ return self.new(priority, weight, port, target)
2743
+ end
2744
+ end
2745
+
2746
+ ##
2747
+ # Common implementation for SVCB-compatible resource records.
2748
+
2749
+ class ServiceBinding
2750
+
2751
+ ##
2752
+ # Create a service binding resource record.
2753
+
2754
+ def initialize(priority, target, params = [])
2755
+ @priority = priority.to_int
2756
+ @target = Name.create(target)
2757
+ @params = SvcParams.new(params)
2758
+ end
2759
+
2760
+ ##
2761
+ # The priority of this target host.
2762
+ #
2763
+ # The range is 0-65535.
2764
+ # If set to 0, this RR is in AliasMode. Otherwise, it is in ServiceMode.
2765
+
2766
+ attr_reader :priority
2767
+
2768
+ ##
2769
+ # The domain name of the target host.
2770
+
2771
+ attr_reader :target
2772
+
2773
+ ##
2774
+ # The service paramters for the target host.
2775
+
2776
+ attr_reader :params
2777
+
2778
+ ##
2779
+ # Whether this RR is in AliasMode.
2780
+
2781
+ def alias_mode?
2782
+ self.priority == 0
2783
+ end
2784
+
2785
+ ##
2786
+ # Whether this RR is in ServiceMode.
2787
+
2788
+ def service_mode?
2789
+ !alias_mode?
2790
+ end
2791
+
2792
+ def encode_rdata(msg) # :nodoc:
2793
+ msg.put_pack("n", @priority)
2794
+ msg.put_name(@target, compress: false)
2795
+ @params.encode(msg)
2796
+ end
2797
+
2798
+ def self.decode_rdata(msg) # :nodoc:
2799
+ priority, = msg.get_unpack("n")
2800
+ target = msg.get_name
2801
+ params = SvcParams.decode(msg)
2802
+ return self.new(priority, target, params)
2803
+ end
2804
+ end
2805
+
2806
+ ##
2807
+ # SVCB resource record [RFC9460]
2808
+
2809
+ class SVCB < ServiceBinding
2810
+ TypeValue = 64
2811
+ ClassValue = IN::ClassValue
2812
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2813
+ end
2814
+
2815
+ ##
2816
+ # HTTPS resource record [RFC9460]
2817
+
2818
+ class HTTPS < ServiceBinding
2819
+ TypeValue = 65
2820
+ ClassValue = IN::ClassValue
2821
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2822
+ end
2823
+ end
2824
+ end
2825
+ end
2826
+
2827
+ ##
2828
+ # A Gem::Resolv::DNS IPv4 address.
2829
+
2830
+ class IPv4
2831
+
2832
+ ##
2833
+ # Regular expression IPv4 addresses must match.
2834
+
2835
+ Regex256 = /0
2836
+ |1(?:[0-9][0-9]?)?
2837
+ |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
2838
+ |[3-9][0-9]?/x
2839
+ Regex = /\A(#{Regex256})\.(#{Regex256})\.(#{Regex256})\.(#{Regex256})\z/
2840
+
2841
+ def self.create(arg)
2842
+ case arg
2843
+ when IPv4
2844
+ return arg
2845
+ when Regex
2846
+ if (0..255) === (a = $1.to_i) &&
2847
+ (0..255) === (b = $2.to_i) &&
2848
+ (0..255) === (c = $3.to_i) &&
2849
+ (0..255) === (d = $4.to_i)
2850
+ return self.new([a, b, c, d].pack("CCCC"))
2851
+ else
2852
+ raise ArgumentError.new("IPv4 address with invalid value: " + arg)
2853
+ end
2854
+ else
2855
+ raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}")
2856
+ end
2857
+ end
2858
+
2859
+ def initialize(address) # :nodoc:
2860
+ unless address.kind_of?(String)
2861
+ raise ArgumentError, 'IPv4 address must be a string'
2862
+ end
2863
+ unless address.length == 4
2864
+ raise ArgumentError, "IPv4 address expects 4 bytes but #{address.length} bytes"
2865
+ end
2866
+ @address = address
2867
+ end
2868
+
2869
+ ##
2870
+ # A String representation of this IPv4 address.
2871
+
2872
+ ##
2873
+ # The raw IPv4 address as a String.
2874
+
2875
+ attr_reader :address
2876
+
2877
+ def to_s # :nodoc:
2878
+ return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
2879
+ end
2880
+
2881
+ def inspect # :nodoc:
2882
+ return "#<#{self.class} #{self}>"
2883
+ end
2884
+
2885
+ ##
2886
+ # Turns this IPv4 address into a Gem::Resolv::DNS::Name.
2887
+
2888
+ def to_name
2889
+ return DNS::Name.create(
2890
+ '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
2891
+ end
2892
+
2893
+ def ==(other) # :nodoc:
2894
+ return @address == other.address
2895
+ end
2896
+
2897
+ def eql?(other) # :nodoc:
2898
+ return self == other
2899
+ end
2900
+
2901
+ def hash # :nodoc:
2902
+ return @address.hash
2903
+ end
2904
+ end
2905
+
2906
+ ##
2907
+ # A Gem::Resolv::DNS IPv6 address.
2908
+
2909
+ class IPv6
2910
+
2911
+ ##
2912
+ # IPv6 address format a:b:c:d:e:f:g:h
2913
+ Regex_8Hex = /\A
2914
+ (?:[0-9A-Fa-f]{1,4}:){7}
2915
+ [0-9A-Fa-f]{1,4}
2916
+ \z/x
2917
+
2918
+ ##
2919
+ # Compressed IPv6 address format a::b
2920
+
2921
+ Regex_CompressedHex = /\A
2922
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
2923
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
2924
+ \z/x
2925
+
2926
+ ##
2927
+ # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z
2928
+
2929
+ Regex_6Hex4Dec = /\A
2930
+ ((?:[0-9A-Fa-f]{1,4}:){6,6})
2931
+ (\d+)\.(\d+)\.(\d+)\.(\d+)
2932
+ \z/x
2933
+
2934
+ ##
2935
+ # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z
2936
+
2937
+ Regex_CompressedHex4Dec = /\A
2938
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
2939
+ ((?:[0-9A-Fa-f]{1,4}:)*)
2940
+ (\d+)\.(\d+)\.(\d+)\.(\d+)
2941
+ \z/x
2942
+
2943
+ ##
2944
+ # IPv6 link local address format fe80:b:c:d:e:f:g:h%em1
2945
+ Regex_8HexLinkLocal = /\A
2946
+ [Ff][Ee]80
2947
+ (?::[0-9A-Fa-f]{1,4}){7}
2948
+ %[-0-9A-Za-z._~]+
2949
+ \z/x
2950
+
2951
+ ##
2952
+ # Compressed IPv6 link local address format fe80::b%em1
2953
+
2954
+ Regex_CompressedHexLinkLocal = /\A
2955
+ [Ff][Ee]80:
2956
+ (?:
2957
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
2958
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
2959
+ |
2960
+ :((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
2961
+ )?
2962
+ :[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+
2963
+ \z/x
2964
+
2965
+ ##
2966
+ # A composite IPv6 address Regexp.
2967
+
2968
+ Regex = /
2969
+ (?:#{Regex_8Hex}) |
2970
+ (?:#{Regex_CompressedHex}) |
2971
+ (?:#{Regex_6Hex4Dec}) |
2972
+ (?:#{Regex_CompressedHex4Dec}) |
2973
+ (?:#{Regex_8HexLinkLocal}) |
2974
+ (?:#{Regex_CompressedHexLinkLocal})
2975
+ /x
2976
+
2977
+ ##
2978
+ # Creates a new IPv6 address from +arg+ which may be:
2979
+ #
2980
+ # IPv6:: returns +arg+.
2981
+ # String:: +arg+ must match one of the IPv6::Regex* constants
2982
+
2983
+ def self.create(arg)
2984
+ case arg
2985
+ when IPv6
2986
+ return arg
2987
+ when String
2988
+ address = ''.b
2989
+ if Regex_8Hex =~ arg
2990
+ arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
2991
+ elsif Regex_CompressedHex =~ arg
2992
+ prefix = $1
2993
+ suffix = $2
2994
+ a1 = ''.b
2995
+ a2 = ''.b
2996
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
2997
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
2998
+ omitlen = 16 - a1.length - a2.length
2999
+ address << a1 << "\0" * omitlen << a2
3000
+ elsif Regex_6Hex4Dec =~ arg
3001
+ prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
3002
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
3003
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
3004
+ address << [a, b, c, d].pack('CCCC')
3005
+ else
3006
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
3007
+ end
3008
+ elsif Regex_CompressedHex4Dec =~ arg
3009
+ prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
3010
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
3011
+ a1 = ''.b
3012
+ a2 = ''.b
3013
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
3014
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
3015
+ omitlen = 12 - a1.length - a2.length
3016
+ address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
3017
+ else
3018
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
3019
+ end
3020
+ else
3021
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
3022
+ end
3023
+ return IPv6.new(address)
3024
+ else
3025
+ raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
3026
+ end
3027
+ end
3028
+
3029
+ def initialize(address) # :nodoc:
3030
+ unless address.kind_of?(String) && address.length == 16
3031
+ raise ArgumentError.new('IPv6 address must be 16 bytes')
3032
+ end
3033
+ @address = address
3034
+ end
3035
+
3036
+ ##
3037
+ # The raw IPv6 address as a String.
3038
+
3039
+ attr_reader :address
3040
+
3041
+ def to_s # :nodoc:
3042
+ sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")).sub(/(^|:)0(:0)+(:|$)/, '::')
3043
+ end
3044
+
3045
+ def inspect # :nodoc:
3046
+ return "#<#{self.class} #{self}>"
3047
+ end
3048
+
3049
+ ##
3050
+ # Turns this IPv6 address into a Gem::Resolv::DNS::Name.
3051
+ #--
3052
+ # ip6.arpa should be searched too. [RFC3152]
3053
+
3054
+ def to_name
3055
+ return DNS::Name.new(
3056
+ @address.unpack("H32")[0].split(//).reverse + ['ip6', 'arpa'])
3057
+ end
3058
+
3059
+ def ==(other) # :nodoc:
3060
+ return @address == other.address
3061
+ end
3062
+
3063
+ def eql?(other) # :nodoc:
3064
+ return self == other
3065
+ end
3066
+
3067
+ def hash # :nodoc:
3068
+ return @address.hash
3069
+ end
3070
+ end
3071
+
3072
+ ##
3073
+ # Gem::Resolv::MDNS is a one-shot Multicast DNS (mDNS) resolver. It blindly
3074
+ # makes queries to the mDNS addresses without understanding anything about
3075
+ # multicast ports.
3076
+ #
3077
+ # Information taken form the following places:
3078
+ #
3079
+ # * RFC 6762
3080
+
3081
+ class MDNS < DNS
3082
+
3083
+ ##
3084
+ # Default mDNS Port
3085
+
3086
+ Port = 5353
3087
+
3088
+ ##
3089
+ # Default IPv4 mDNS address
3090
+
3091
+ AddressV4 = '224.0.0.251'
3092
+
3093
+ ##
3094
+ # Default IPv6 mDNS address
3095
+
3096
+ AddressV6 = 'ff02::fb'
3097
+
3098
+ ##
3099
+ # Default mDNS addresses
3100
+
3101
+ Addresses = [
3102
+ [AddressV4, Port],
3103
+ [AddressV6, Port],
3104
+ ]
3105
+
3106
+ ##
3107
+ # Creates a new one-shot Multicast DNS (mDNS) resolver.
3108
+ #
3109
+ # +config_info+ can be:
3110
+ #
3111
+ # nil::
3112
+ # Uses the default mDNS addresses
3113
+ #
3114
+ # Hash::
3115
+ # Must contain :nameserver or :nameserver_port like
3116
+ # Gem::Resolv::DNS#initialize.
3117
+
3118
+ def initialize(config_info=nil)
3119
+ if config_info then
3120
+ super({ nameserver_port: Addresses }.merge(config_info))
3121
+ else
3122
+ super(nameserver_port: Addresses)
3123
+ end
3124
+ end
3125
+
3126
+ ##
3127
+ # Iterates over all IP addresses for +name+ retrieved from the mDNS
3128
+ # resolver, provided name ends with "local". If the name does not end in
3129
+ # "local" no records will be returned.
3130
+ #
3131
+ # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
3132
+ # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
3133
+
3134
+ def each_address(name)
3135
+ name = Gem::Resolv::DNS::Name.create(name)
3136
+
3137
+ return unless name[-1].to_s == 'local'
3138
+
3139
+ super(name)
3140
+ end
3141
+
3142
+ def make_udp_requester # :nodoc:
3143
+ nameserver_port = @config.nameserver_port
3144
+ Requester::MDNSOneShot.new(*nameserver_port)
3145
+ end
3146
+
3147
+ end
3148
+
3149
+ module LOC
3150
+
3151
+ ##
3152
+ # A Gem::Resolv::LOC::Size
3153
+
3154
+ class Size
3155
+
3156
+ Regex = /^(\d+\.*\d*)[m]$/
3157
+
3158
+ ##
3159
+ # Creates a new LOC::Size from +arg+ which may be:
3160
+ #
3161
+ # LOC::Size:: returns +arg+.
3162
+ # String:: +arg+ must match the LOC::Size::Regex constant
3163
+
3164
+ def self.create(arg)
3165
+ case arg
3166
+ when Size
3167
+ return arg
3168
+ when String
3169
+ scalar = ''
3170
+ if Regex =~ arg
3171
+ scalar = [(($1.to_f*(1e2)).to_i.to_s[0].to_i*(2**4)+(($1.to_f*(1e2)).to_i.to_s.length-1))].pack("C")
3172
+ else
3173
+ raise ArgumentError.new("not a properly formed Size string: " + arg)
3174
+ end
3175
+ return Size.new(scalar)
3176
+ else
3177
+ raise ArgumentError.new("cannot interpret as Size: #{arg.inspect}")
3178
+ end
3179
+ end
3180
+
3181
+ def initialize(scalar)
3182
+ @scalar = scalar
3183
+ end
3184
+
3185
+ ##
3186
+ # The raw size
3187
+
3188
+ attr_reader :scalar
3189
+
3190
+ def to_s # :nodoc:
3191
+ s = @scalar.unpack("H2").join.to_s
3192
+ return ((s[0].to_i)*(10**(s[1].to_i-2))).to_s << "m"
3193
+ end
3194
+
3195
+ def inspect # :nodoc:
3196
+ return "#<#{self.class} #{self}>"
3197
+ end
3198
+
3199
+ def ==(other) # :nodoc:
3200
+ return @scalar == other.scalar
3201
+ end
3202
+
3203
+ def eql?(other) # :nodoc:
3204
+ return self == other
3205
+ end
3206
+
3207
+ def hash # :nodoc:
3208
+ return @scalar.hash
3209
+ end
3210
+
3211
+ end
3212
+
3213
+ ##
3214
+ # A Gem::Resolv::LOC::Coord
3215
+
3216
+ class Coord
3217
+
3218
+ Regex = /^(\d+)\s(\d+)\s(\d+\.\d+)\s([NESW])$/
3219
+
3220
+ ##
3221
+ # Creates a new LOC::Coord from +arg+ which may be:
3222
+ #
3223
+ # LOC::Coord:: returns +arg+.
3224
+ # String:: +arg+ must match the LOC::Coord::Regex constant
3225
+
3226
+ def self.create(arg)
3227
+ case arg
3228
+ when Coord
3229
+ return arg
3230
+ when String
3231
+ coordinates = ''
3232
+ if Regex =~ arg && $1.to_f < 180
3233
+ m = $~
3234
+ hemi = (m[4][/[NE]/]) || (m[4][/[SW]/]) ? 1 : -1
3235
+ coordinates = [ ((m[1].to_i*(36e5)) + (m[2].to_i*(6e4)) +
3236
+ (m[3].to_f*(1e3))) * hemi+(2**31) ].pack("N")
3237
+ orientation = m[4][/[NS]/] ? 'lat' : 'lon'
3238
+ else
3239
+ raise ArgumentError.new("not a properly formed Coord string: " + arg)
3240
+ end
3241
+ return Coord.new(coordinates,orientation)
3242
+ else
3243
+ raise ArgumentError.new("cannot interpret as Coord: #{arg.inspect}")
3244
+ end
3245
+ end
3246
+
3247
+ def initialize(coordinates,orientation)
3248
+ unless coordinates.kind_of?(String)
3249
+ raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}")
3250
+ end
3251
+ unless orientation.kind_of?(String) && orientation[/^lon$|^lat$/]
3252
+ raise ArgumentError.new('Coord expects orientation to be a String argument of "lat" or "lon"')
3253
+ end
3254
+ @coordinates = coordinates
3255
+ @orientation = orientation
3256
+ end
3257
+
3258
+ ##
3259
+ # The raw coordinates
3260
+
3261
+ attr_reader :coordinates
3262
+
3263
+ ## The orientation of the hemisphere as 'lat' or 'lon'
3264
+
3265
+ attr_reader :orientation
3266
+
3267
+ def to_s # :nodoc:
3268
+ c = @coordinates.unpack("N").join.to_i
3269
+ val = (c - (2**31)).abs
3270
+ fracsecs = (val % 1e3).to_i.to_s
3271
+ val = val / 1e3
3272
+ secs = (val % 60).to_i.to_s
3273
+ val = val / 60
3274
+ mins = (val % 60).to_i.to_s
3275
+ degs = (val / 60).to_i.to_s
3276
+ posi = (c >= 2**31)
3277
+ case posi
3278
+ when true
3279
+ hemi = @orientation[/^lat$/] ? "N" : "E"
3280
+ else
3281
+ hemi = @orientation[/^lon$/] ? "W" : "S"
3282
+ end
3283
+ return degs << " " << mins << " " << secs << "." << fracsecs << " " << hemi
3284
+ end
3285
+
3286
+ def inspect # :nodoc:
3287
+ return "#<#{self.class} #{self}>"
3288
+ end
3289
+
3290
+ def ==(other) # :nodoc:
3291
+ return @coordinates == other.coordinates
3292
+ end
3293
+
3294
+ def eql?(other) # :nodoc:
3295
+ return self == other
3296
+ end
3297
+
3298
+ def hash # :nodoc:
3299
+ return @coordinates.hash
3300
+ end
3301
+
3302
+ end
3303
+
3304
+ ##
3305
+ # A Gem::Resolv::LOC::Alt
3306
+
3307
+ class Alt
3308
+
3309
+ Regex = /^([+-]*\d+\.*\d*)[m]$/
3310
+
3311
+ ##
3312
+ # Creates a new LOC::Alt from +arg+ which may be:
3313
+ #
3314
+ # LOC::Alt:: returns +arg+.
3315
+ # String:: +arg+ must match the LOC::Alt::Regex constant
3316
+
3317
+ def self.create(arg)
3318
+ case arg
3319
+ when Alt
3320
+ return arg
3321
+ when String
3322
+ altitude = ''
3323
+ if Regex =~ arg
3324
+ altitude = [($1.to_f*(1e2))+(1e7)].pack("N")
3325
+ else
3326
+ raise ArgumentError.new("not a properly formed Alt string: " + arg)
3327
+ end
3328
+ return Alt.new(altitude)
3329
+ else
3330
+ raise ArgumentError.new("cannot interpret as Alt: #{arg.inspect}")
3331
+ end
3332
+ end
3333
+
3334
+ def initialize(altitude)
3335
+ @altitude = altitude
3336
+ end
3337
+
3338
+ ##
3339
+ # The raw altitude
3340
+
3341
+ attr_reader :altitude
3342
+
3343
+ def to_s # :nodoc:
3344
+ a = @altitude.unpack("N").join.to_i
3345
+ return ((a.to_f/1e2)-1e5).to_s + "m"
3346
+ end
3347
+
3348
+ def inspect # :nodoc:
3349
+ return "#<#{self.class} #{self}>"
3350
+ end
3351
+
3352
+ def ==(other) # :nodoc:
3353
+ return @altitude == other.altitude
3354
+ end
3355
+
3356
+ def eql?(other) # :nodoc:
3357
+ return self == other
3358
+ end
3359
+
3360
+ def hash # :nodoc:
3361
+ return @altitude.hash
3362
+ end
3363
+
3364
+ end
3365
+
3366
+ end
3367
+
3368
+ ##
3369
+ # Default resolver to use for Gem::Resolv class methods.
3370
+
3371
+ DefaultResolver = self.new
3372
+
3373
+ ##
3374
+ # Replaces the resolvers in the default resolver with +new_resolvers+. This
3375
+ # allows resolvers to be changed for resolv-replace.
3376
+
3377
+ def DefaultResolver.replace_resolvers new_resolvers
3378
+ @resolvers = new_resolvers
3379
+ end
3380
+
3381
+ ##
3382
+ # Address Regexp to use for matching IP addresses.
3383
+
3384
+ AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
3385
+
3386
+ end
3387
+