[packages/ruby/rawhide] up to 4.0.7

mis mis at pld-linux.org
Mon Sep 28 19:48:52 CEST 2026


commit a2d943d7d36f59ca00a640f2dd0970b49e0f3165
Author: Pawel Gajda <mis at pld-linux.org>
Date:   Mon Sep 28 19:47:36 2026 +0200

    up to 4.0.7

 duplicated-paths.patch                             |   80 -
 extract-gem-versions.sh                            |   12 +-
 fix-bison-invocation.patch                         |   10 -
 mkmf-verbose.patch                                 |   25 -
 openssl3.patch                                     |   12 -
 operating_system.rb                                |   12 +
 ...pr1531-fix-mutilple-document-installation.patch |   28 +
 ...-2.1.0-Enable-configuration-of-archlibdir.patch |    7 +-
 ...dd-ruby_version_dir_name-support-for-RDoc.patch |   22 +
 ruby-2.3.0-ruby_version.patch                      |   43 +-
 ruby-2.7.0-Initialize-ABRT-hook.patch              |   32 +-
 ...build-extension-libraries-in-bundled-gems.patch |  338 --
 ruby-3.4.2-openssl-Fix-SHA-1-PSS-tests.patch       |  136 -
 ...customizable-rustc_flags-for-rustc-builds.patch |  117 +
 ...0.7-rubygems-pr9877-stop-vendoring-resolv.patch | 3579 ++++++++++++++++++++
 ruby.spec                                          |  332 +-
 strip-ccache.patch                                 |    9 +-
 17 files changed, 4003 insertions(+), 791 deletions(-)
---
diff --git a/ruby.spec b/ruby.spec
index f98cc32..a903b9d 100644
--- a/ruby.spec
+++ b/ruby.spec
@@ -1,23 +1,17 @@
-# TODO:
-#	- include ext/ in docs
-#	- replace ri with fastri
-#	- patch ri to search multiple indexes (one per package), so RPMs can install ri docs
-#	- gemify irb (?)
-#
 # Conditional build:
 %bcond_without	doc		# skip (time-consuming) docs generating; intended for speed up test builds
 %bcond_without	batteries	# Don't include rubygems, json, rake, minitest
 %bcond_without	default_ruby	# use this Ruby as default system Ruby
-%bcond_with	dtrace		# disable tracing with dtrace
+%bcond_without	dtrace		# disable tracing with dtrace
 %bcond_with	bootstrap	# build bootstrap version
 %bcond_with	tests		# build with tests
 
-%define		rel		4
-%define		ruby_version	3.4
-%define		patchlevel	11
+%define		rel		1
+%define		ruby_version	4.0
+%define		patchlevel	7
 %define		pkg_version	%{ruby_version}.%{patchlevel}
 %define		ruby_suffix	%{!?with_default_ruby:%{ruby_version}}
-%define		unicode_version	12.1.0
+%define		unicode_version	17.0.0
 %define		oname	ruby
 Summary:	Ruby - interpreted scripting language
 Summary(ja.UTF-8):	オブジェクト指向言語Rubyインタプリタ
@@ -55,10 +49,15 @@ Patch4:		%{name}-2.1.0-custom-rubygems-location.patch
 Patch6:		%{name}-2.7.0-Initialize-ABRT-hook.patch
 # Disable syntax_suggest test suite, which tries to download its dependencies.
 # https://bugs.ruby-lang.org/issues/19297
-Patch9:		%{name}-3.3.0-Disable-syntax-suggest-test-case.patch
-# Fix the tests using SHA-1 Probabilistic Signature Scheme (PSS) parameters.
-# https://github.com/ruby/openssl/pull/879
-Patch10:	%{name}-3.4.2-openssl-Fix-SHA-1-PSS-tests.patch
+Patch7:		%{name}-3.3.0-Disable-syntax-suggest-test-case.patch
+# Allow the rustc flags used for YJIT to be overridden.
+Patch10:	%{name}-4.0.1-Support-customizable-rustc_flags-for-rustc-builds.patch
+# Add ruby_version_dir_name support for RDoc (patched inside the bundled gem).
+Patch11:	%{name}-2.3.0-ruby_version-Add-ruby_version_dir_name-support-for-RDoc.patch
+# rdoc PR 1531: fix installation of multiple documents.
+Patch12:	rdoc-pr1531-fix-mutilple-document-installation.patch
+# Stop vendoring resolv for two regexps (ruby/rubygems PR 9877).
+Patch13:	%{name}-4.0.7-rubygems-pr9877-stop-vendoring-resolv.patch
 URL:		http://www.ruby-lang.org/
 BuildRequires:	autoconf >= 2.67
 BuildRequires:	automake
@@ -103,7 +102,7 @@ BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
 
 # relative symlinks
 # usage: %make_rel_symlink <target-in-install-root> [<directory> [<link-name>]]
-# e.g.  %make_rel_symlink %{gem_instdir rdoc}/lib/rdoc %{ruby_libdir}
+# e.g.  %%make_rel_symlink %%{gem_instdir rdoc}/lib/rdoc %%{ruby_libdir}
 # creates <directory>/<link-name> as a symlink holding the shortest relative
 # path to <target>, so that it stays valid after installation.  <link-name>
 # defaults to the basename of <target>.
@@ -112,54 +111,62 @@ BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
 
 # hack: skip rubygem(ipaddr)
 %define	_noautoreq	ipaddr
+# ruby(abi) replaced by ruby(release)
 %{expand:%%global _noautoreq %{_noautoreq} 'ruby\\(abi\\)'}
 %{expand:%%global _noautoprov %{?nil} 'ruby\\(abi\\)'}
 
 # Bundled libraries versions
-%define rubygems_version 3.6.9
+%define rubygems_version 4.0.20
 
 # Default gems.
-%define bundler_version 2.6.9
-%define erb_version 4.0.4.1
+%define bundler_version 4.0.20
+%define erb_version 6.0.7
 
 
-%define io_console_version 0.8.1
-%define irb_version 1.14.3
-%define json_version 2.9.1
-%define psych_version 5.2.2
-%define rdoc_version 6.14.0
+%define io_console_version 0.8.2
+%define irb_version 1.16.0
+%define json_version 2.18.0
+%define psych_version 5.3.1
+%define rdoc_version 7.0.4
 %define syntax_suggest_version 2.0.3
 
+%define benchmark_version 0.5.0
+%define fiddle_version 1.1.8
+%define logger_version 1.7.0
+%define ostruct_version 0.6.3
+%define pstore_version 0.2.1
+%define readline_version 0.0.4
+%define reline_version 0.6.3
 # Bundled gems.
 %define abbrev_version 0.1.2
-%define base64_version 0.2.0
-%define bigdecimal_version 3.1.8
-%define csv_version 3.3.2
-%define debug_version 1.11.0
-%define drb_version 2.2.1
+%define base64_version 0.3.0
+%define bigdecimal_version 4.0.1
+%define csv_version 3.3.5
+%define debug_version 1.11.1
+%define drb_version 2.2.3
 %define getoptlong_version 0.2.1
-%define matrix_version 0.4.2
-%define minitest_version 5.25.4
+%define matrix_version 0.4.3
+%define minitest_version 6.0.0
 %define mutex_m_version 0.3.0
-%define net_ftp_version 0.3.8
-%define net_imap_version 0.5.15
+%define net_ftp_version 0.3.9
+%define net_imap_version 0.6.4.1
 %define net_pop_version 0.1.2
 %define net_smtp_version 0.5.1
 %define nkf_version 0.2.0
 %define observer_version 0.1.2
-%define power_assert_version 2.0.5
-%define prime_version 0.1.3
+%define power_assert_version 3.0.1
+%define prime_version 0.1.4
 %define racc_version 1.8.1
-%define rake_version 13.2.1
-%define rbs_version 3.8.0
-%define repl_type_completor_version 0.1.9
+%define rake_version 13.3.1
+%define rbs_version 3.10.0
+%define repl_type_completor_version 0.1.12
 %define resolv_replace_version 0.1.1
 %define rexml_version 3.4.4
 %define rinda_version 0.2.0
-%define rss_version 0.3.1
-%define syslog_version 0.2.0
-%define test_unit_version 3.6.7
-%define typeprof_version 0.30.1
+%define rss_version 0.3.2
+%define syslog_version 0.3.0
+%define test_unit_version 3.7.5
+%define typeprof_version 0.31.1
 
 # Bundled nkf version
 %global bundled_nkf_version 2.1.5
@@ -726,8 +733,15 @@ these needs to be listed in Gemfile to be used by Bundler.
 %patch 3 -p1
 %patch 4 -p1
 %patch 6 -p1
-%patch 9 -p1
+%patch 7 -p1
 %patch 10 -p1
+%patch 13 -p1
+
+# The bundled rdoc is a gem, so it has to be patched in place.
+cd .bundle/gems/rdoc-%{rdoc_version}
+%patch 11 -p1
+%patch 12 -p1
+cd %{builddir}/%{buildsubdir}
 
 # must be regenerated with new bison
 %{__rm} parse.{c,h}
@@ -737,6 +751,12 @@ these needs to be listed in Gemfile to be used by Bundler.
 #%{__rm} -r ext/fiddle/libffi*
 
 # Install custom operating_system.rb.
+#
+# "make install" runs "require 'rubygems/defaults/operating_system'".  Without
+# this file that require falls through to /usr/share/ruby/%{ruby_version} and
+# happily picks up the copy from an already installed RubyGems, which then
+# points Gem.default_dir at the per-user gem directory.  Since -I../lib comes
+# first on the load path, the copy in the tree always wins.
 install -d lib/rubygems/defaults
 cp -p %{SOURCE6} lib/rubygems/defaults
 
@@ -757,7 +777,7 @@ if [ $rubygems_version != %{rubygems_version} ]; then
 	echo "Set %%define rubygems_version to $rubygems_version and re-run."
 	exit 1
 fi
-rdoc_version=$(awk '/VERSION =/ && $1 == "VERSION" {print $3}' lib/rdoc/version.rb | xargs)
+rdoc_version=$(awk '/VERSION =/ && $1 == "VERSION" {print $3}' .bundle/gems/rdoc-%{rdoc_version}/lib/rdoc/version.rb | xargs)
 if [ "$rdoc_version" != %{rdoc_version} ]; then
 	echo "Set %%define rdoc_version to $rdoc_version and re-run."
 	exit 1
@@ -806,7 +826,6 @@ chmod +x configure
 	--enable-yjit \
 %endif
 	--disable-install-doc \
-	--disable-rubygems \
 
 cd ..
 
@@ -825,6 +844,7 @@ install -d $RPM_BUILD_ROOT{%{ruby_rdocdir},%{ruby_ridir}} \
 	$RPM_BUILD_ROOT%{ruby_vendorlibdir}/data
 
 
+
 %make_install -C %{_vpath_builddir}
 %if %{with doc}
 %{__make} -C %{_vpath_builddir} install-doc DESTDIR=%{buildroot}
@@ -844,31 +864,34 @@ rm -rd ${CONFIG_TARGET_DIR}
 sed -i 's/Version: \${ruby_version}/Version: %{ruby_version}/' %{buildroot}%{_libdir}/pkgconfig/%{name}.pc
 
 # Kill bundled certificates, as they should be part of ca-certificates.
+# Newer RubyGems releases no longer ship them, hence the -f.
 for cert in GlobalSignRootCA.pem GlobalSignRootCA_R3.pem; do \
-    %{__rm} $RPM_BUILD_ROOT%{rubygems_dir}/rubygems/ssl_certs/*/$cert
+    %{__rm} -f $RPM_BUILD_ROOT%{rubygems_dir}/rubygems/ssl_certs/*/$cert
 done
 # Ensure there is not forgotten any certificate.
 ##test ! "$(ls -A $RPM_BUILD_ROOT%{rubygems_dir}/rubygems/ssl_certs/ 2>/dev/null)"
 
 install -d $RPM_BUILD_ROOT%{_examplesdir}/%{oname}-%{pkg_version}
 cp -Rf sample/* $RPM_BUILD_ROOT%{_examplesdir}/%{oname}-%{pkg_version}
+
+# "make install" no longer installs the top level documentation.
+install -d $RPM_BUILD_ROOT%{_docdir}/%{name}-%{pkg_version}
+cp -p NEWS.md LEGAL BSDL README.md README.EXT $RPM_BUILD_ROOT%{_docdir}/%{name}-%{pkg_version}/
 cp -p %{SOURCE4} %{buildroot}%{_mandir}/man1/rdoc.1
 cp -p %{SOURCE5} %{buildroot}%{_mandir}/man1/testrb.1
 
 %{__rm} -rf $RPM_BUILD_ROOT%{_docdir}/%{name}/html
 
-# detect this runtime, "make install" is affected by operating_system.rb what is installed in system!
-gem_dir=$( \
-  %{_vpath_builddir}/miniruby -Ilib -I. -I%{_vpath_builddir}/.ext/common \
-    tool/runruby.rb --archdir=%{_vpath_builddir} -- --disable-gems \
-    -r$(basename %{_vpath_builddir}/*-linux*-fake.rb .rb) -r rubygems \
-    -e 'puts Gem.default_dir' \
-)
-
 # Move gems root into common directory, out of Ruby directory structure.
-install -d $RPM_BUILD_ROOT%{gem_dir}
-%{__mv} $RPM_BUILD_ROOT${gem_dir}/{gems,specifications} $RPM_BUILD_ROOT%{gem_dir}
-%{__rm} $RPM_BUILD_ROOT${gem_dir}/cache/*.gem
+# "make install" already lays it out as %{gem_dir}/{gems,specifications,extensions}.
+# The bundled gem cache is not packaged.
+%{__mv} $RPM_BUILD_ROOT%{ruby_libdir}/gems $RPM_BUILD_ROOT%{gem_dir}
+%{__rm} -f $RPM_BUILD_ROOT%{gem_dir}/cache/*.gem
+
+# Install the packager overrides for RubyGems.  This has to happen after the
+# gems have been moved, so that Gem.default_dirs[:system] points at %{gem_dir}.
+install -d $RPM_BUILD_ROOT%{rubygems_dir}/rubygems/defaults
+cp -p %{SOURCE6} $RPM_BUILD_ROOT%{rubygems_dir}/rubygems/defaults
 
 # Move bundled rubygems to %gem_dir
 # make symlinks for io-console and bigdecimal, which are considered to be part of stdlib by other Gems
@@ -877,11 +900,12 @@ install -d $RPM_BUILD_ROOT%{gem_dir}
 # as there may came files from other packages as well. actually, unlikely as
 # the links to got system dir and only ruby may package there (other distro
 # packages should go to vendor dirs)%
-install -d $RPM_BUILD_ROOT%{gem_instdir rdoc}/lib
-%{__mv} $RPM_BUILD_ROOT%{ruby_libdir}/rdoc* $RPM_BUILD_ROOT%{gem_instdir rdoc}/lib
+# Since Ruby 4.0 "make install" already places the bundled gems below
+# %{gem_dir}, so nothing has to be moved out of %{ruby_libdir} here.
+[ -d $RPM_BUILD_ROOT%{gem_spec -d rdoc} ] && \
+    %{__mv} $RPM_BUILD_ROOT%{gem_spec -d rdoc} $RPM_BUILD_ROOT%{gem_dir}/specifications
 %make_rel_symlink %{gem_instdir rdoc}/lib/rdoc %{ruby_libdir}
 %make_rel_symlink %{gem_instdir rdoc}/lib/rdoc.rb %{ruby_libdir}
-%{__mv} $RPM_BUILD_ROOT%{gem_spec -d rdoc} $RPM_BUILD_ROOT%{gem_dir}/specifications
 
 %make_rel_symlink %{gem_instdir bigdecimal}/lib/bigdecimal %{ruby_libdir}
 %make_rel_symlink %{gem_instdir bigdecimal}/lib/bigdecimal.rb %{ruby_libdir}
@@ -937,9 +961,16 @@ install -d $RPM_BUILD_ROOT%{gem_extdir_mri bundler}/lib
 
 # Move the binary extensions into proper place (if no gem has binary extension,
 # the extensions directory might be empty).
-find ${RPM_BUILD_ROOT}${gem_dir}/extensions/*-%{_target_os}/%{ruby_version}.*/* -maxdepth 0 \
-  -exec mv '{}' $RPM_BUILD_ROOT%{_libdir}/gems/%{name}/ \; \
-    || echo "No gem binary extensions to move."
+for extdir in "$RPM_BUILD_ROOT"%{gem_dir}/extensions \
+              "$RPM_BUILD_ROOT"/../%{oname}-%{pkg_version}/build/.bundle/extensions; do
+    [ -d "$extdir" ] || continue
+    find "$extdir"/*-%{_target_os}/%{ruby_version}.*/* -maxdepth 0 \
+        -exec mv '{}' $RPM_BUILD_ROOT%{_libdir}/gems/%{name}/ \; \
+        || echo "No gem binary extensions to move."
+done
+# Only the leftovers in the build root; the source tree must stay intact for
+# the "gem build_complete" stamps and for subsequent extension builds.
+rm -rf "$RPM_BUILD_ROOT"%{gem_dir}/extensions
 
 # Remove the extension sources and library copies from `lib` dir.
 find $RPM_BUILD_ROOT%{gem_dir}/gems/*/ext -maxdepth 0 -exec rm -rf '{}' +
@@ -981,8 +1012,11 @@ for s in rake-%{rake_version}.gemspec rdoc-%{rdoc_version}.gemspec json-%{json_v
 	-e \"File.write '$s', spec.to_ruby\""
 done
 
-%{__sed} -i -e '1s,/usr/bin/env ruby,/usr/bin/ruby,' \
-        $(find $RPM_BUILD_ROOT -type f | xargs grep '%{_bindir}/env ruby' 2>/dev/null | cut -f1 -d:)
+# Rewrite gem shebangs.  Newer gems may use the "#!/usr/bin/env -S ruby"
+# form, which rpm's shebang check rejects.
+%{__sed} -i -e '1s,/usr/bin/env -S ruby,%{_bindir}/ruby,' \
+        -e '1s,/usr/bin/env ruby,%{_bindir}/ruby,' \
+        $(find $RPM_BUILD_ROOT -type f | xargs grep -l -E '%{_bindir}/env (-S )?ruby' 2>/dev/null)
 
 # gem non library files
 rm -rf $RPM_BUILD_ROOT%{gem_instdir debug}/{[A-Z]*,misc,exts.mk}
@@ -1021,6 +1055,7 @@ rm -f $RPM_BUILD_ROOT%{ruby_ridir}/cache.ri 2>/dev/null || :
 
 %check
 %if %{with tests}
+
 # unset these, as testsuite does "git commit" somewhere, which points to pld .spec repo. doh
 unset GIT_DIR GIT_WORK_TREE
 
@@ -1064,7 +1099,11 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 
 %files
 %defattr(644,root,root,755)
-%doc NEWS.md LEGAL BSDL README.md README.EXT
+%doc %{_docdir}/%{name}-%{pkg_version}/NEWS.md
+%doc %{_docdir}/%{name}-%{pkg_version}/LEGAL
+%doc %{_docdir}/%{name}-%{pkg_version}/BSDL
+%doc %{_docdir}/%{name}-%{pkg_version}/README.md
+%doc %{_docdir}/%{name}-%{pkg_version}/README.EXT
 %attr(755,root,root) %{_bindir}/ruby
 %{_libdir}/libruby.so.*.*.*
 %ghost %{_libdir}/libruby.so.%{ruby_version}
@@ -1079,8 +1118,8 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %dir %{ruby_vendorlibdir}
 %dir %{ruby_vendorarchdir}
 
-%dir %{dirname:%{ruby_ridir}}
-%dir %{ruby_ridir}
+# Since RDoc 7 the ri data is versioned below %%{_datadir}/ri/%%{ruby_version} and
+# is packaged by ruby-doc-ri, so the old unversioned %%{ruby_ridir} is gone.
 %dir %{ruby_rdocdir}
 
 # common dirs for ruby vendor modules
@@ -1097,23 +1136,34 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %files irb
 %defattr(644,root,root,755)
 %attr(755,root,root) %{_bindir}/irb
-%{ruby_libdir}/irb.rb
-%{ruby_libdir}/irb
-%{gem_spec -d irb}
-%{_mandir}/man1/irb.1*
+%{gem_instdir irb}
+%{gem_spec irb}
 
 %files rdoc
 %defattr(644,root,root,755)
 %attr(755,root,root) %{_bindir}/rdoc
 %attr(755,root,root) %{_bindir}/ri
 %{_mandir}/man1/rdoc.1*
+%{gem_instdir rdoc}/man/ri.1*
 %{ruby_libdir}/rdoc
 %dir %{gem_instdir rdoc}
 %{gem_instdir rdoc}/lib
 %{gem_spec rdoc}
+%{gem_instdir rdoc}/rdoc.gemspec
 %dir %{gem_instdir rdoc}/exe
 %{gem_instdir rdoc}/exe/rdoc
 %{gem_instdir rdoc}/exe/ri
+%{gem_plugin rdoc}
+%doc %{gem_instdir rdoc}/CONTRIBUTING.md
+%doc %{gem_instdir rdoc}/CVE-2013-0256.rdoc
+%doc %{gem_instdir rdoc}/ExampleMarkdown.md
+%doc %{gem_instdir rdoc}/ExampleRDoc.rdoc
+%doc %{gem_instdir rdoc}/History.rdoc
+%license %{gem_instdir rdoc}/LEGAL.rdoc
+%license %{gem_instdir rdoc}/LICENSE.rdoc
+%doc %{gem_instdir rdoc}/README.md
+%doc %{gem_instdir rdoc}/RI.md
+%doc %{gem_instdir rdoc}/TODO.rdoc
 
 %if %{with batteries}
 %files rubygems
@@ -1135,6 +1185,7 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %dir %{gem_instdir rake}
 %{gem_instdir rake}/lib
 %{gem_spec rake}
+%{gem_instdir rake}/rake.gemspec
 %dir %{gem_instdir rake}/exe
 %attr(755,root,root) %{gem_instdir rake}/exe/rake
 
@@ -1165,13 +1216,16 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 
 %files minitest
 %defattr(644,root,root,755)
+%attr(755,root,root) %{_bindir}/minitest
 %{ruby_libdir}/minitest
 %{gem_instdir minitest}
 %{gem_spec minitest}
 
 %files test-unit
 %defattr(644,root,root,755)
+%attr(755,root,root) %{_bindir}/test-unit
 %dir %{gem_instdir test-unit}
+%{gem_instdir test-unit}/bin
 %{gem_instdir test-unit}/lib
 %{gem_spec test-unit}
 %{_mandir}/man1/testrb.1*
@@ -1179,8 +1233,11 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %files racc
 %defattr(644,root,root,755)
 %dir %{gem_instdir racc}
+%dir %{gem_extdir_mri racc}
+%{gem_extdir_mri racc}/gem.build_complete
+%dir %{gem_extdir_mri racc}/racc
+%attr(755,root,root) %{gem_extdir_mri racc}/racc/cparse.so
 %attr(755,root,root) %{_bindir}/racc
-%{gem_extdir_mri racc}
 %{gem_instdir racc}/bin
 %{gem_instdir racc}/lib
 %{gem_spec racc}
@@ -1259,10 +1316,67 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 
 # base64
 %dir %{gem_instdir base64}
-%doc %{gem_instdir base64}/LICENSE.txt
+%license %{gem_instdir base64}/BSDL
+%license %{gem_instdir base64}/COPYING
+%license %{gem_instdir base64}/LEGAL
 %{gem_instdir base64}/lib
 %{gem_spec base64}
 %doc %{gem_instdir base64}/README.md
+%doc %{gem_instdir base64}/sig
+
+# benchmark
+%dir %{gem_instdir benchmark}
+%license %{gem_instdir benchmark}/BSDL
+%license %{gem_instdir benchmark}/COPYING
+%{gem_instdir benchmark}/bin
+%{gem_instdir benchmark}/lib
+%{gem_spec benchmark}
+%{gem_instdir benchmark}/Gemfile
+%doc %{gem_instdir benchmark}/README.md
+%{gem_instdir benchmark}/Rakefile
+
+# fiddle
+%dir %{gem_instdir fiddle}
+%dir %{gem_extdir_mri fiddle}
+%{gem_extdir_mri fiddle}/gem.build_complete
+%attr(755,root,root) %{gem_extdir_mri fiddle}/fiddle.so
+%{gem_instdir fiddle}/lib
+%{gem_spec fiddle}
+%{gem_instdir fiddle}/fiddle.gemspec
+%doc %{gem_instdir fiddle}/LICENSE.txt
+%doc %{gem_instdir fiddle}/README.md
+%{gem_instdir fiddle}/Rakefile
+
+# logger
+%dir %{gem_instdir logger}
+%license %{gem_instdir logger}/BSDL
+%license %{gem_instdir logger}/COPYING
+%{gem_instdir logger}/lib
+%{gem_spec logger}
+%doc %{gem_instdir logger}/README.md
+
+# ostruct
+%dir %{gem_instdir ostruct}
+%license %{gem_instdir ostruct}/BSDL
+%license %{gem_instdir ostruct}/COPYING
+%{gem_instdir ostruct}/bin
+%{gem_instdir ostruct}/lib
+%{gem_spec ostruct}
+%{gem_instdir ostruct}/ostruct.gemspec
+%{gem_instdir ostruct}/Gemfile
+%doc %{gem_instdir ostruct}/README.md
+%{gem_instdir ostruct}/Rakefile
+
+# pstore
+%dir %{gem_instdir pstore}
+%license %{gem_instdir pstore}/BSDL
+%license %{gem_instdir pstore}/COPYING
+%{gem_instdir pstore}/bin
+%{gem_instdir pstore}/lib
+%{gem_spec pstore}
+%{gem_instdir pstore}/Gemfile
+%doc %{gem_instdir pstore}/README.md
+%{gem_instdir pstore}/Rakefile
 
 # csv
 %dir %{gem_instdir csv}
@@ -1278,6 +1392,7 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %doc %{gem_instdir drb}/LICENSE.txt
 %{gem_instdir drb}/lib
 %{gem_spec drb}
+%{gem_instdir drb}/drb.gemspec
 
 # getoptlong
 %dir %{gem_instdir getoptlong}
@@ -1294,6 +1409,7 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %dir %{gem_instdir matrix}
 %{gem_instdir matrix}/lib
 %{gem_spec matrix}
+%{gem_instdir matrix}/matrix.gemspec
 
 # mutex_m
 %dir %{gem_instdir mutex_m}
@@ -1347,11 +1463,29 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %doc %{gem_instdir observer}/README.md
 %{gem_instdir observer}/Rakefile
 
+# readline
+%dir %{gem_instdir readline}
+%license %{gem_instdir readline}/BSDL
+%license %{gem_instdir readline}/COPYING
+%{gem_instdir readline}/lib
+%{gem_spec readline}
+%doc %{gem_instdir readline}/README.md
+
+# reline
+%dir %{gem_instdir reline}
+%license %{gem_instdir reline}/BSDL
+%license %{gem_instdir reline}/COPYING
+%{gem_instdir reline}/lib
+%{gem_spec reline}
+%doc %{gem_instdir reline}/README.md
+%{gem_instdir reline}/license_of_rb-readline
+
 # prime
 %dir %{gem_instdir prime}
 %{gem_instdir prime}/lib
 %{gem_instdir prime}/sig
 %{gem_spec prime}
+%{gem_instdir prime}/prime.gemspec
 
 # rdbg
 %attr(755,root,root) %{_bindir}/rdbg
@@ -1410,7 +1544,6 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %{ruby_libdir}/bundled_gems.rb
 %{ruby_libdir}/cgi
 %{ruby_libdir}/digest
-%{ruby_libdir}/fiddle
 %{ruby_libdir}/io
 %{ruby_libdir}/net
 %{ruby_libdir}/openssl
@@ -1420,7 +1553,6 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %{ruby_libdir}/uri
 %{ruby_libdir}/yaml
 %{ruby_libdir}/English.rb
-%{ruby_libdir}/benchmark.rb
 %{ruby_libdir}/bundler
 %{ruby_libdir}/bundler.rb
 %{ruby_libdir}/cgi.rb
@@ -1437,7 +1569,6 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %dir %{ruby_libdir}/erb
 %{ruby_libdir}/erb/*.rb
 %{ruby_libdir}/expect.rb
-%{ruby_libdir}/fiddle.rb
 %{ruby_libdir}/fileutils.rb
 %{ruby_libdir}/find.rb
 %{ruby_libdir}/forwardable.rb
@@ -1445,9 +1576,6 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %{ruby_libdir}/forwardable/impl.rb
 %{ruby_libdir}/ipaddr.rb
 %{ruby_libdir}/json.rb
-%{ruby_libdir}/logger.rb
-%dir %{ruby_libdir}/logger
-%{ruby_libdir}/logger/*.rb
 %{ruby_libdir}/monitor.rb
 %{ruby_libdir}/objspace.rb
 %dir %{ruby_libdir}/objspace
@@ -1459,34 +1587,19 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %{ruby_libdir}/openssl.rb
 %{ruby_libdir}/optionparser.rb
 %{ruby_libdir}/optparse.rb
-%{ruby_libdir}/ostruct.rb
 %{ruby_libdir}/pathname.rb
 %{ruby_libdir}/pp.rb
 %{ruby_libdir}/prettyprint.rb
 %{ruby_libdir}/prism*
-%{ruby_libdir}/pstore.rb
 %dir %{ruby_libdir}/random
 %{ruby_libdir}/random/*.rb
 %{ruby_libdir}/rdoc.rb
-%{ruby_libdir}/readline.rb
-%{ruby_libdir}/reline.rb
-%dir %{ruby_libdir}/reline
-%{ruby_libdir}/reline/*.rb
-%dir %{ruby_libdir}/reline/io
-%{ruby_libdir}/reline/io/*.rb
-%dir %{ruby_libdir}/reline/key_actor
-%{ruby_libdir}/reline/key_actor/*.rb
-%dir %{ruby_libdir}/reline/unicode
-%{ruby_libdir}/reline/unicode/*.rb
+
 %{ruby_libdir}/resolv.rb
 %{ruby_libdir}/ripper.rb
-%dir %{ruby_libdir}/ruby_vm
-%dir %{ruby_libdir}/ruby_vm/rjit
-%{ruby_libdir}/ruby_vm/rjit/*.rb
 %{ruby_libdir}/securerandom.rb
-%{ruby_libdir}/set.rb
 %dir %{ruby_libdir}/set
-%{ruby_libdir}/set/*.rb
+%{ruby_libdir}/set/subclass_compatible.rb
 %{ruby_libdir}/shellwords.rb
 %{ruby_libdir}/singleton.rb
 %{ruby_libdir}/socket.rb
@@ -1511,7 +1624,6 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %if %{with batteries}
 %exclude %{ruby_libdir}/rubygems.rb
 %endif
-%exclude %{ruby_libdir}/irb.rb
 %exclude %{ruby_libdir}/mkmf.rb
 
 %{ruby_archdir}/rbconfig.rb
@@ -1522,11 +1634,9 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %attr(755,root,root) %{ruby_archdir}%{_sysconfdir}.so
 %attr(755,root,root) %{ruby_archdir}/erb/escape.so
 %attr(755,root,root) %{ruby_archdir}/fcntl.so
-%attr(755,root,root) %{ruby_archdir}/fiddle.so
 %attr(755,root,root) %{ruby_archdir}/monitor.so
 %attr(755,root,root) %{ruby_archdir}/objspace.so
 %attr(755,root,root) %{ruby_archdir}/openssl.so
-%attr(755,root,root) %{ruby_archdir}/pathname.so
 %attr(755,root,root) %{ruby_archdir}/pty.so
 %attr(755,root,root) %{ruby_archdir}/ripper.so
 %attr(755,root,root) %{ruby_archdir}/socket.so
@@ -1560,28 +1670,14 @@ sed -i '/def test_ctx_server_session_cb$/,/^  end$/ s/^/#/' test/openssl/test_ss
 %{gem_dir}/specifications/default/*.gemspec
 
 %{_mandir}/man1/erb.1*
-%{_mandir}/man1/ri.1*
 
 %if %{with doc}
+# Since RDoc 7 the ri data is versioned below %%{_datadir}/ri/%%{ruby_version},
+# so take the whole tree rather than listing individual files.  The empty
+# %%{_datadir}/rdoc is already owned by the main ruby package.
 %files doc-ri
 %defattr(644,root,root,755)
-%{ruby_ridir}/[A-Z]*
-%{ruby_ridir}/contributing
-%{ruby_ridir}/date
-%{ruby_ridir}/fatal
-%{ruby_ridir}/syntax
-%{ruby_ridir}/optparse
-%lang(ja) %{ruby_ridir}/page-COPYING_ja.ri
-%lang(ja) %{ruby_ridir}/page-README_ja_md.ri
-%{ruby_ridir}/page-COPYING.ri
-%{ruby_ridir}/page-LEGAL.ri
-%{ruby_ridir}/page-NEWS_md.ri
-%{ruby_ridir}/page-README_md.ri
-%{ruby_ridir}/page-[a-z]*.ri
-%{ruby_ridir}/regexp
-%{ruby_ridir}/rjit
-%{ruby_ridir}/ruby
-%{ruby_ridir}/yjit
+%{_datadir}/ri
 %endif
 
 %files examples
diff --git a/duplicated-paths.patch b/duplicated-paths.patch
deleted file mode 100644
index bfb197f..0000000
--- a/duplicated-paths.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From e24d97c938c481450ed80ec83e5399595946c1ae Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
-Date: Fri, 8 Feb 2013 22:48:41 +0100
-Subject: [PATCH] Prevent duplicated paths when empty version string is
- configured.
-
----
- configure.ac     |  3 ++-
- loadpath.c       | 12 ++++++++++++
- tool/mkconfig.rb |  2 +-
- 3 files changed, 15 insertions(+), 2 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 999e2d6d5d..11fc237552 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -3710,7 +3710,8 @@ AS_CASE(["$ruby_version_dir_name"],
- ruby_version_dir=/'${ruby_version_dir_name}'
- 
- if test -z "${ruby_version_dir_name}"; then
--    AC_MSG_ERROR([No ruby version, No place for bundled libraries])
-+    unset ruby_version_dir
-+    AC_DEFINE(RUBY_LIB_VERSION_BLANK, 1)
- fi
- 
- rubylibdir='${rubylibprefix}'${ruby_version_dir}
-diff --git a/loadpath.c b/loadpath.c
-index 9160031..0d4d953 100644
---- a/loadpath.c
-+++ b/loadpath.c
-@@ -65,21 +65,33 @@ const char ruby_initial_load_paths[] =
-     RUBY_SEARCH_PATH "\0"
- #endif
- #ifndef NO_RUBY_SITE_LIB
-+#ifdef RUBY_LIB_VERSION_BLANK
-+    RUBY_SITE_LIB "\0"
-+#else
-     RUBY_SITE_LIB2 "\0"
-+#endif
- #ifdef RUBY_THINARCH
-     RUBY_SITE_ARCH_LIB_FOR(RUBY_THINARCH) "\0"
- #endif
-     RUBY_SITE_ARCH_LIB_FOR(RUBY_SITEARCH) "\0"
-+#ifndef RUBY_LIB_VERSION_BLANK
-     RUBY_SITE_LIB "\0"
- #endif
-+#endif
- 
- #ifndef NO_RUBY_VENDOR_LIB
-+#ifdef RUBY_LIB_VERSION_BLANK
-+    RUBY_VENDOR_LIB "\0"
-+#else
-     RUBY_VENDOR_LIB2 "\0"
-+#endif
- #ifdef RUBY_THINARCH
-     RUBY_VENDOR_ARCH_LIB_FOR(RUBY_THINARCH) "\0"
- #endif
-     RUBY_VENDOR_ARCH_LIB_FOR(RUBY_SITEARCH) "\0"
-+#ifndef RUBY_LIB_VERSION_BLANK
-     RUBY_VENDOR_LIB "\0"
-+#endif
- #endif
- 
-     RUBY_LIB "\0"
-diff --git a/tool/mkconfig.rb b/tool/mkconfig.rb
-index 07076d4..35e6c3c 100755
---- a/tool/mkconfig.rb
-+++ b/tool/mkconfig.rb
-@@ -114,7 +114,7 @@
-     val = val.gsub(/\$(?:\$|\{?(\w+)\}?)/) {$1 ? "$(#{$1})" : $&}.dump
-     case name
-     when /^prefix$/
--      val = "(TOPDIR || DESTDIR + #{val})"
-+      val = "(((TOPDIR && TOPDIR.empty?) ? nil : TOPDIR) || DESTDIR + #{val})"
-     when /^ARCH_FLAG$/
-       val = "arch_flag || #{val}" if universal
-     when /^UNIVERSAL_ARCHNAMES$/
--- 
-1.9.0
-
diff --git a/extract-gem-versions.sh b/extract-gem-versions.sh
index fcc5e18..9c10fa6 100755
--- a/extract-gem-versions.sh
+++ b/extract-gem-versions.sh
@@ -50,19 +50,19 @@ cd "$BUILD_DIR" || exit 1
 ver=$(grep -P "\w+\.version" .bundle/gems/bigdecimal-*/.bundled.bigdecimal-*.gemspec | head -1 | grep -oP '\d+\.\d+[\d\.]*')
 puts_version "bigdecimal" "$ver"
 
-ver=$(grep "VERSION = " lib/bundler/version.rb | grep -oP '\d+\.\d+[\d\.]*')
+ver=$(grep -oP 'VERSION\s*=\s*["\x27]\K[\d.]+' lib/bundler/version.rb | head -1)
 puts_version "bundler" "$ver"
 
-ver=$(grep "VERSION = " lib/erb/version.rb | grep -oP '\d+\.\d+[\d\.]*')
+ver=$(grep -oP 'VERSION\s*=\s*["\x27]\K[\d.]+' lib/erb/version.rb | head -1)
 puts_version "erb" "$ver"
 
 ver=$(grep IO_CONSOLE_VERSION ext/io/console/console.c | grep -oP '\d+\.\d+[\d\.]*')
 puts_version "io_console" "$ver"
 
-ver=$(grep -P "\s+VERSION = " lib/irb/version.rb | grep -oP '\d+\.\d+[\d\.]*')
+ver=$(grep -oP 'VERSION\s*=\s*["\x27]\K[\d.]+' .bundle/gems/irb-*/lib/irb/version.rb | head -1)
 puts_version "irb" "$ver"
 
-ver=$(cat ext/json/lib/json/version.rb | grep -oP '\d+\.\d+[\d\.]*')
+ver=$(grep -oP 'VERSION\s*=\s*["\x27]\K[\d.]+' ext/json/lib/json/version.rb | head -1)
 puts_version "json" "$ver"
 
 for i in etc stringio zlib; do
@@ -77,10 +77,10 @@ puts_version "openssl" "$ver"
 ver=$(grep "VERSION = " ext/psych/lib/psych/versions.rb | head -1 | grep -oP '\d+\.\d+[\d\.]*')
 puts_version "psych" "$ver"
 
-ver=$(grep -P "\s+VERSION\s+=\s+" lib/rdoc/version.rb | grep -oP '\d+\.\d+[\d\.]*')
+ver=$(grep -oP 'VERSION\s*=\s*["\x27]\K[\d.]+' .bundle/gems/rdoc-*/lib/rdoc/version.rb | head -1)
 puts_version "rdoc" "$ver"
 
-ver=$(grep -P "\w+\.version\s+" lib/readline*.gemspec | head -1 | grep -oP '\d+\.\d+[\d\.]*')
+ver=$(grep -oP 's\.version\s*=\s*["\x27]\K[\d.]+' .bundle/specifications/readline-*.gemspec | head -1)
 puts_version "readline" "$ver"
 
 ver=$(grep "VERSION = " lib/rubygems.rb | head -1 | grep -oP '\d+\.\d+[\d\.]*')
diff --git a/fix-bison-invocation.patch b/fix-bison-invocation.patch
deleted file mode 100644
index 2ff5cc3..0000000
--- a/fix-bison-invocation.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- ruby-2.6.5/ext/ripper/depend~	2019-10-01 13:01:01.000000000 +0200
-+++ ruby-2.6.5/ext/ripper/depend	2019-11-25 08:47:14.265303943 +0100
-@@ -11,7 +11,7 @@
-
- .y.c:
- 	$(ECHO) compiling compiler $<
--	$(Q) $(BISON) -t -v -oy.tab.c $<
-+	$(Q) $(BISON) -d -t -v -oy.tab.c $<
- 	$(Q) sed -e "/^#/s!y\.tab\.c!$@!" -f $(top_srcdir)/tool/ytab.sed y.tab.c > $@
- 	@$(RM) y.tab.c
diff --git a/mkmf-verbose.patch b/mkmf-verbose.patch
deleted file mode 100644
index 2113bea..0000000
--- a/mkmf-verbose.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 28cc0749d6729aa2444661ee7b411e183fe220b0 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
-Date: Mon, 19 Nov 2012 15:14:51 +0100
-Subject: [PATCH] Verbose mkmf.
-
----
- lib/mkmf.rb | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/lib/mkmf.rb b/lib/mkmf.rb
-index 682eb46..e6b1445 100644
---- a/lib/mkmf.rb
-+++ b/lib/mkmf.rb
-@@ -1974,7 +1974,7 @@ def configuration(srcdir)
- SHELL = /bin/sh
- 
- # V=0 quiet, V=1 verbose.  other values don't work.
--V = 0
-+V = 1
- V0 = $(V:0=)
- Q1 = $(V:1=)
- Q = $(Q1:0=@)
--- 
-1.8.3.1
-
diff --git a/openssl3.patch b/openssl3.patch
deleted file mode 100644
index acc7d3c..0000000
--- a/openssl3.patch
+++ /dev/null
@@ -1,12 +0,0 @@
---- ruby-2.6.8/ext/openssl/ossl_pkey_rsa.c.orig	2021-07-07 12:38:58.000000000 +0200
-+++ ruby-2.6.8/ext/openssl/ossl_pkey_rsa.c	2021-10-13 12:03:34.317085257 +0200
-@@ -939,7 +939,9 @@
-     rb_define_method(cRSA, "params", ossl_rsa_get_params, 0);
- 
-     DefRSAConst(PKCS1_PADDING);
-+#if OPENSSL_VERSION_MAJOR < 3
-     DefRSAConst(SSLV23_PADDING);
-+#endif
-     DefRSAConst(NO_PADDING);
-     DefRSAConst(PKCS1_OAEP_PADDING);
- 
diff --git a/operating_system.rb b/operating_system.rb
index fd56386..a0318a9 100644
--- a/operating_system.rb
+++ b/operating_system.rb
@@ -101,6 +101,18 @@ module Gem
         Gem.default_dirs[:system][:gem_dir]
       elsif Process.uid == 0
         Gem.default_dirs[:local][:gem_dir]
+      elsif ENV['RPM_PACKAGE_NAME'] == 'ruby'
+        # PLD builds ruby with --with-rubygemsdir pointing at the Ruby
+        # library directory, so the gems have to be laid down in the Ruby
+        # tree first; the spec's %install moves them to %gem_dir afterwards.
+        # Without this the whole tree would end up below the invoking
+        # user's ~/.local, which is neither the packager's nor the end
+        # user's intent.
+        #
+        # Guarded on the package name on purpose: this file also lands in
+        # every other ruby-based package, and rerouting default_dir for
+        # those would move their gems as well.
+        File.join(RbConfig::CONFIG['rubylibdir'], 'gems')
       else
         Gem.user_dir
       end
diff --git a/rdoc-pr1531-fix-mutilple-document-installation.patch b/rdoc-pr1531-fix-mutilple-document-installation.patch
new file mode 100644
index 0000000..192863f
--- /dev/null
+++ b/rdoc-pr1531-fix-mutilple-document-installation.patch
@@ -0,0 +1,28 @@
+From 994ee4c17fb8c217ab0335df55620c6bdb5d5cbe Mon Sep 17 00:00:00 2001
+From: tompng <tomoyapenguin at gmail.com>
+Date: Fri, 26 Dec 2025 04:57:12 +0900
+Subject: [PATCH] Fix comment_location for merged ClassModule
+
+---
+ lib/rdoc/code_object/class_module.rb       | 7 ++++++-
+ test/rdoc/code_object/class_module_test.rb | 6 ++++++
+ 2 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/lib/rdoc/code_object/class_module.rb b/lib/rdoc/code_object/class_module.rb
+index b6bed352a2..d7ee36f950 100644
+--- a/lib/rdoc/code_object/class_module.rb
++++ b/lib/rdoc/code_object/class_module.rb
+@@ -477,7 +477,12 @@ def merge(class_module)
+       document = document.merge other_document
+ 
+       @comment = RDoc::Comment.from_document(document)
+-      @comment_location = document
++
++      @comment_location = if document.parts.first.is_a?(RDoc::Markup::Document)
++                            document.parts.map { |doc| [doc, doc.file] }
++                          else
++                            [[document, document.file]]
++                          end
+     end
+ 
+     cm = class_module
diff --git a/ruby-2.1.0-Enable-configuration-of-archlibdir.patch b/ruby-2.1.0-Enable-configuration-of-archlibdir.patch
index 116bb66..114ef1c 100644
--- a/ruby-2.1.0-Enable-configuration-of-archlibdir.patch
+++ b/ruby-2.1.0-Enable-configuration-of-archlibdir.patch
@@ -1,4 +1,3 @@
-From 07c666ba5c3360dd6f43605a8ac7c85c99c1721f Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
 Date: Tue, 1 Oct 2013 12:22:40 +0200
 Subject: [PATCH] Allow to configure libruby.so placement.
@@ -8,10 +7,9 @@ Subject: [PATCH] Allow to configure libruby.so placement.
  1 file changed, 5 insertions(+)
 
 diff --git a/configure.ac b/configure.ac
-index 11fc237552..b77e88fc37 100644
 --- a/configure.ac
 +++ b/configure.ac
-@@ -3085,6 +3085,11 @@ AS_IF([test ${multiarch+set}], [
+@@ -3553,6 +3553,11 @@ AS_IF([test ${multiarch+set}], [
  ])
  
  archlibdir='${libdir}/${arch}'
@@ -23,6 +21,3 @@ index 11fc237552..b77e88fc37 100644
  sitearchlibdir='${libdir}/${sitearch}'
  archincludedir='${includedir}/${arch}'
  sitearchincludedir='${includedir}/${sitearch}'
--- 
-1.8.3.1
-
diff --git a/ruby-2.3.0-ruby_version-Add-ruby_version_dir_name-support-for-RDoc.patch b/ruby-2.3.0-ruby_version-Add-ruby_version_dir_name-support-for-RDoc.patch
new file mode 100644
index 0000000..82f5963
--- /dev/null
+++ b/ruby-2.3.0-ruby_version-Add-ruby_version_dir_name-support-for-RDoc.patch
@@ -0,0 +1,22 @@
+From f833e213596b0bcfad8264a555eb5093303fb5f2 Mon Sep 17 00:00:00 2001
+From: Jarek Prokop <jprokop at redhat.com>
+Date: Thu, 25 Sep 2025 12:26:39 +0200
+Subject: [PATCH] Add ruby_version_dir_name support for RDoc.
+
+---
+ lib/rdoc/ri/paths.rb | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
+index 8e89b04e..731f9e36 100644
+--- a/lib/rdoc/ri/paths.rb
++++ b/lib/rdoc/ri/paths.rb
+@@ -10,7 +10,7 @@ module RDoc::RI::Paths
+   #:stopdoc:
+   require 'rbconfig'
+ 
+-  version = RbConfig::CONFIG['ruby_version']
++  version = RbConfig::CONFIG['ruby_version_dir_name'] || RbConfig::CONFIG['ruby_version']
+ 
+   BASE    = File.join RbConfig::CONFIG['ridir'], version
+ 
diff --git a/ruby-2.3.0-ruby_version.patch b/ruby-2.3.0-ruby_version.patch
index 96609a0..7539e6f 100644
--- a/ruby-2.3.0-ruby_version.patch
+++ b/ruby-2.3.0-ruby_version.patch
@@ -1,7 +1,7 @@
 From 4fc1be3af3f58621bb751c9e63c208b15c0e8d16 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
 Date: Tue, 31 Mar 2015 16:21:04 +0200
-Subject: [PATCH 1/4] Use ruby_version_dir_name for versioned directories.
+Subject: [PATCH 1/3] Use ruby_version_dir_name for versioned directories.
 
 This disallows changing the ruby_version constant by --with-ruby-version
 configuration options. The two places version numbers are disallowed as
@@ -144,47 +144,10 @@ index 8a2c066..c81b211 100644
  arch=@arch@
 
 
-From 518850aba6eee76de7715aae8d37330e34b01983 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
-Date: Tue, 31 Mar 2015 16:37:26 +0200
-Subject: [PATCH 2/4] Add ruby_version_dir_name support for RDoc.
-
----
- lib/rdoc/ri/paths.rb | 2 +-
- tool/rbinstall.rb    | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
-index 970cb91..5bf8230 100644
---- a/lib/rdoc/ri/paths.rb
-+++ b/lib/rdoc/ri/paths.rb
-@@ -10,7 +10,7 @@ module RDoc::RI::Paths
-   #:stopdoc:
-   require 'rbconfig'
-
--  version = RbConfig::CONFIG['ruby_version']
-+  version = RbConfig::CONFIG['ruby_version_dir_name'] || RbConfig::CONFIG['ruby_version']
-
-   BASE    = File.join RbConfig::CONFIG['ridir'], version
-
-diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb
-index d4c110e..d39c9a6 100755
---- a/tool/rbinstall.rb
-+++ b/tool/rbinstall.rb
-@@ -945,7 +945,7 @@ def (bins = []).add(name)
-
- install?(:doc, :rdoc) do
-   if $rdocdir
--    ridatadir = File.join(CONFIG['ridir'], CONFIG['ruby_version'], "system")
-+    ridatadir = File.join(CONFIG['ridir'], CONFIG['ruby_version_dir_name'] || CONFIG['ruby_version'], "system")
-     prepare "rdoc", ridatadir
-     install_recursive($rdocdir, ridatadir, :no_install => rdoc_noinst, :mode => $data_mode)
-   end
-
 From 9f0ec0233f618cbb862629816b22491c3df79578 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
 Date: Tue, 31 Mar 2015 16:37:44 +0200
-Subject: [PATCH 3/4] Add ruby_version_dir_name support for RubyGems.
+Subject: [PATCH 2/3] Add ruby_version_dir_name support for RubyGems.
 
 ---
  lib/rubygems/defaults.rb  | 7 ++++---
@@ -251,7 +214,7 @@ index b25068405d..e9fef4a311 100644
 From 88c38a030c22dbf9422ece847bdfbf87d6659313 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
 Date: Wed, 1 Apr 2015 14:55:37 +0200
-Subject: [PATCH 4/4] Let headers directories follow the configured version
+Subject: [PATCH 3/3] Let headers directories follow the configured version
  name.
 
 ---
diff --git a/ruby-2.7.0-Initialize-ABRT-hook.patch b/ruby-2.7.0-Initialize-ABRT-hook.patch
index 65d1e65..d328ebc 100644
--- a/ruby-2.7.0-Initialize-ABRT-hook.patch
+++ b/ruby-2.7.0-Initialize-ABRT-hook.patch
@@ -1,4 +1,4 @@
-From eca084e4079c77c061045df9c21b219175b05228 Mon Sep 17 00:00:00 2001
+From 03b44a86b574dc0b63fd57c5f9b52b56ad3ced37 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= <vondruch at redhat.com>
 Date: Mon, 6 Jan 2020 13:56:04 +0100
 Subject: [PATCH] Initialize ABRT hook.
@@ -16,15 +16,15 @@ To keep the things simple for now, load the ABRT hook via C.
 [5]: https://lists.fedoraproject.org/archives/list/ruby-sig@lists.fedoraproject.org/message/LH6L6YJOYQT4Y5ZNOO4SLIPTUWZ5V45Q/
 ---
  abrt.c                                | 12 ++++++++++++
- common.mk                             |  3 ++-
+ common.mk                             |  1 +
  ruby.c                                |  4 ++++
  spec/ruby/core/kernel/require_spec.rb |  2 ++
- 4 files changed, 20 insertions(+), 1 deletion(-)
+ 4 files changed, 19 insertions(+)
  create mode 100644 abrt.c
 
 diff --git a/abrt.c b/abrt.c
 new file mode 100644
-index 0000000000..74b0bd5c0f
+index 0000000000..e99cb432e6
 --- /dev/null
 +++ b/abrt.c
 @@ -0,0 +1,12 @@
@@ -41,25 +41,23 @@ index 0000000000..74b0bd5c0f
 +  );
 +}
 diff --git a/common.mk b/common.mk
-index b2e5b2b6d0..f39f81da5c 100644
+index 08fee9119a..dae7d9dc00 100644
 --- a/common.mk
 +++ b/common.mk
-@@ -110,7 +110,8 @@ PRISM_FILES = prism/api_node.$(OBJEXT) \
- 		prism/prism.$(OBJEXT) \
+@@ -116,6 +116,7 @@ PRISM_FILES = prism/api_node.$(OBJEXT) \
  		prism_init.$(OBJEXT)
  
--COMMONOBJS    = array.$(OBJEXT) \
-+COMMONOBJS    = abrt.$(OBJEXT) \
-+                array.$(OBJEXT) \
+ COMMONOBJS    = \
++		abrt.$(OBJEXT) \
+ 		array.$(OBJEXT) \
  		ast.$(OBJEXT) \
  		bignum.$(OBJEXT) \
- 		class.$(OBJEXT) \
 diff --git a/ruby.c b/ruby.c
-index 60c57d6259..1eec16f2c8 100644
+index b00fc1502d..32b88f7496 100644
 --- a/ruby.c
 +++ b/ruby.c
-@@ -1744,10 +1744,14 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
- 
+@@ -1793,10 +1793,14 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
+ VALUE rb_define_gem_modules(VALUE, VALUE);
  void Init_builtin_features(void);
  
 +/* abrt.c */
@@ -70,14 +68,14 @@ index 60c57d6259..1eec16f2c8 100644
  {
      Init_builtin_features();
 +    Init_abrt();
-     rb_const_remove(rb_cObject, rb_intern_const("TMP_RUBY_PREFIX"));
  }
  
+ void rb_call_builtin_inits(void);
 diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb
-index 60c57d6259..1eec16f2c8 100644
+index 60d17242fe..a8f93b0db4 100644
 --- a/spec/ruby/core/kernel/require_spec.rb
 +++ b/spec/ruby/core/kernel/require_spec.rb
-@@ -25,6 +25,8 @@
+@@ -26,6 +26,8 @@
      out = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems --disable-did-you-mean')
      features = out.lines.map { |line| File.basename(line.chomp, '.*') }
  
diff --git a/ruby-3.2.0-build-extension-libraries-in-bundled-gems.patch b/ruby-3.2.0-build-extension-libraries-in-bundled-gems.patch
deleted file mode 100644
index de8d4d3..0000000
--- a/ruby-3.2.0-build-extension-libraries-in-bundled-gems.patch
+++ /dev/null
@@ -1,338 +0,0 @@
-From 111f8422427d78becc9183ae149b2105a16bf327 Mon Sep 17 00:00:00 2001
-From: Nobuyoshi Nakada <nobu at ruby-lang.org>
-Date: Tue, 5 Apr 2022 23:24:00 +0900
-Subject: [PATCH 1/5] Bundled gems are expanded under `.bundle/gems` now
-
----
- ext/extmk.rb          | 13 +++++++------
- template/exts.mk.tmpl |  2 +-
- 2 files changed, 8 insertions(+), 7 deletions(-)
-
-diff --git a/ext/extmk.rb b/ext/extmk.rb
-index 4a087f294ac9..1da9e2704521 100755
---- a/ext/extmk.rb
-+++ b/ext/extmk.rb
-@@ -146,7 +146,7 @@ def extmake(target, basedir = 'ext', maybestatic = true)
-     top_srcdir = $top_srcdir
-     topdir = $topdir
-     hdrdir = $hdrdir
--    prefix = "../" * (target.count("/")+1)
-+    prefix = "../" * (basedir.count("/")+target.count("/")+1)
-     $top_srcdir = relative_from(top_srcdir, prefix)
-     $hdrdir = relative_from(hdrdir, prefix)
-     $topdir = prefix + $topdir
-@@ -460,10 +460,11 @@ def $mflags.defined?(var)
- end unless $extstatic
- 
- @gemname = nil
--if ARGV[0]
--  ext_prefix, exts = ARGV.shift.split('/', 2)
-+if exts = ARGV.shift
-+  ext_prefix = exts[%r[\A(?>\.bundle/)?[^/]+(?:/(?=(.+)?)|\z)]]
-+  exts = $1
-   $extension = [exts] if exts
--  if ext_prefix == 'gems'
-+  if ext_prefix.start_with?('.')
-     @gemname = exts
-   elsif exts
-     $static_ext.delete_if {|t, *| !File.fnmatch(t, exts)}
-@@ -515,7 +516,7 @@ def $mflags.defined?(var)
-     exts.delete_if {|d| File.fnmatch?("-*", d)}
-   end
- end
--ext_prefix = File.basename(ext_prefix)
-+ext_prefix = ext_prefix[$top_srcdir.size+1..-2]
- 
- extend Module.new {
-   def timestamp_file(name, target_prefix = nil)
-@@ -634,7 +635,7 @@ def initialize(src)
-   end
- }
- 
--Dir.chdir ".."
-+Dir.chdir dir
- unless $destdir.to_s.empty?
-   $mflags.defined?("DESTDIR") or $mflags << "DESTDIR=#{$destdir}"
- end
-diff --git a/template/exts.mk.tmpl b/template/exts.mk.tmpl
-index 2f37f4480ac5..964939e365a1 100644
---- a/template/exts.mk.tmpl
-+++ b/template/exts.mk.tmpl
-@@ -25,7 +25,7 @@ macros["old_extensions"] = []
- 
- contpat = /(?>(?>[^\\\n]|\\.)*\\\n)*(?>[^\\\n]|\\.)*/
- Dir.glob("{ext,.bundle/gems}/*/exts.mk") do |e|
--  gem = /\Agems(?=\/)/ =~ e
-+  gem = e.start_with?(".bundle/gems/")
-   s = File.read(e)
-   s.scan(/^(extensions|SUBMAKEOPTS|EXT[A-Z]+|MFLAGS|NOTE_[A-Z]+)[ \t]*=[ \t]*(#{contpat})$/o) do |n, v|
-     v.gsub!(/\\\n[ \t]*/, ' ')
-
-From 6ea34cac22131d28a9cc50e7875e854aed9bdb88 Mon Sep 17 00:00:00 2001
-From: Nobuyoshi Nakada <nobu at ruby-lang.org>
-Date: Wed, 6 Apr 2022 20:25:53 +0900
-Subject: [PATCH 2/5] Retrieve configured gems info
-
----
- template/configure-ext.mk.tmpl | 2 +-
- template/exts.mk.tmpl          | 4 ++--
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/template/configure-ext.mk.tmpl b/template/configure-ext.mk.tmpl
-index 6636a7759c54..8ba6b963e3ec 100644
---- a/template/configure-ext.mk.tmpl
-+++ b/template/configure-ext.mk.tmpl
-@@ -27,7 +27,7 @@ SCRIPT_ARGS = <%=script_args.gsub("#", "\\#")%>
- EXTMK_ARGS = $(SCRIPT_ARGS) --gnumake=$(gnumake) --extflags="$(EXTLDFLAGS)" \
- 	   --make-flags="MINIRUBY='$(MINIRUBY)'"
- 
--all: exts # gems
-+all: exts gems
- exts:
- gems:
- 
-diff --git a/template/exts.mk.tmpl b/template/exts.mk.tmpl
-index 964939e365a1..e544c4c88bd7 100644
---- a/template/exts.mk.tmpl
-+++ b/template/exts.mk.tmpl
-@@ -19,7 +19,7 @@ opt = OptionParser.new do |o|
-   o.on('--configure-exts=FILE') {|v| confexts = v}
-   o.order!(ARGV)
- end
--confexts &&= File.read(confexts).scan(/^exts: (.*\.mk)/).flatten rescue nil
-+confexts &&= File.read(confexts).scan(/^(?:ext|gem)s: (.*\.mk)/).flatten rescue nil
- confexts ||= []
- macros["old_extensions"] = []
- 
-@@ -30,7 +30,7 @@ Dir.glob("{ext,.bundle/gems}/*/exts.mk") do |e|
-   s.scan(/^(extensions|SUBMAKEOPTS|EXT[A-Z]+|MFLAGS|NOTE_[A-Z]+)[ \t]*=[ \t]*(#{contpat})$/o) do |n, v|
-     v.gsub!(/\\\n[ \t]*/, ' ')
-     next if v.empty?
--    next if gem and n != "extensions"
-+    next if n != "extensions"
-     n = "old_extensions" if n == "extensions" and !confexts.include?(e)
-     v = v.split
-     m = macros[n] ||= []
-
-From be9d00ee7c72766551ba8c3530f1538034498a6a Mon Sep 17 00:00:00 2001
-From: Nobuyoshi Nakada <nobu at ruby-lang.org>
-Date: Wed, 6 Apr 2022 20:28:00 +0900
-Subject: [PATCH 3/5] Move the target directory of bundled gems like as
- rubygems
-
----
- ext/extmk.rb | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/ext/extmk.rb b/ext/extmk.rb
-index 1da9e2704521..a440af27fc5d 100755
---- a/ext/extmk.rb
-+++ b/ext/extmk.rb
-@@ -2,6 +2,9 @@
- # -*- mode: ruby; coding: us-ascii -*-
- # frozen_string_literal: false
- 
-+module Gem; end # only needs Gem::Platform
-+require 'rubygems/platform'
-+
- # :stopdoc:
- $extension = nil
- $extstatic = nil
-@@ -535,11 +538,12 @@ def create_makefile(*args, &block)
-     super(*args) do |conf|
-       conf.find do |s|
-         s.sub!(/^(TARGET_SO_DIR *= *)\$\(RUBYARCHDIR\)/) {
--          "TARGET_GEM_DIR = $(extout)/gems/$(arch)/#{@gemname}\n"\
-+          "TARGET_GEM_DIR = $(topdir)/.bundle/extensions/$(gem_platform)/$(ruby_version)/#{@gemname}\n"\
-           "#{$1}$(TARGET_GEM_DIR)$(target_prefix)"
-         }
-       end
-       conf.any? {|s| /^TARGET *= *\S/ =~ s} and conf << %{
-+gem_platform = #{Gem::Platform.local}
- 
- # default target
- all:
-
-From c4daf8e445925695c34bab8bf5135dcd1e8575a3 Mon Sep 17 00:00:00 2001
-From: Nobuyoshi Nakada <nobu at ruby-lang.org>
-Date: Wed, 6 Apr 2022 22:57:01 +0900
-Subject: [PATCH 4/5] Obey spec file locations to rubygems
-
----
- common.mk          | 3 ++-
- defs/gmake.mk      | 2 +-
- tool/gem-unpack.rb | 5 +++--
- 3 files changed, 6 insertions(+), 4 deletions(-)
-
-diff --git a/common.mk b/common.mk
-index 7c552cba1e04..b4adb2729c0e 100644
---- a/common.mk
-+++ b/common.mk
-@@ -1359,10 +1359,11 @@ extract-gems$(gnumake:yes=-nongnumake): PHONY
- 	$(Q) $(RUNRUBY) -C "$(srcdir)" \
- 	    -Itool -rgem-unpack -answ \
- 	    -e 'BEGIN {FileUtils.mkdir_p(d = ".bundle/gems")}' \
-+	    -e 'BEGIN {FileUtils.mkdir_p(s = ".bundle/specifications")}' \
- 	    -e 'gem, ver = *$$F' \
- 	    -e 'next if !ver or /^#/=~gem' \
- 	    -e 'g = "#{gem}-#{ver}"' \
--	    -e 'File.directory?("#{d}/#{g}") or Gem.unpack("gems/#{g}.gem", d)' \
-+	    -e 'File.directory?("#{d}/#{g}") or Gem.unpack("gems/#{g}.gem", d, s)' \
- 	    gems/bundled_gems
- 
- update-bundled_gems: PHONY
-diff --git a/defs/gmake.mk b/defs/gmake.mk
-index a625379a6804..27e3e21cc4d6 100644
---- a/defs/gmake.mk
-+++ b/defs/gmake.mk
-@@ -290,7 +290,7 @@ extract-gems: | $(patsubst %,.bundle/gems/%,$(bundled-gems))
- 	$(ECHO) Extracting bundle gem $*...
- 	$(Q) $(BASERUBY) -C "$(srcdir)" \
- 	    -Itool -rgem-unpack \
--	    -e 'Gem.unpack("gems/$(@F).gem", ".bundle/gems")'
-+	    -e 'Gem.unpack("gems/$(@F).gem", ".bundle/gems", ".bundle/specifications")'
- 
- $(srcdir)/.bundle/gems:
- 	$(MAKEDIRS) $@
-diff --git a/tool/gem-unpack.rb b/tool/gem-unpack.rb
-index cb05719463f2..fe10b0e420fa 100644
---- a/tool/gem-unpack.rb
-+++ b/tool/gem-unpack.rb
-@@ -5,13 +5,14 @@
- # This library is used by "make extract-gems" to
- # unpack bundled gem files.
- 
--def Gem.unpack(file, dir = nil)
-+def Gem.unpack(file, dir = nil, spec_dir = nil)
-   pkg = Gem::Package.new(file)
-   spec = pkg.spec
-   target = spec.full_name
-   target = File.join(dir, target) if dir
-   pkg.extract_files target
--  spec_file = File.join(target, "#{spec.name}-#{spec.version}.gemspec")
-+  FileUtils.mkdir_p(spec_dir ||= target)
-+  spec_file = File.join(spec_dir, "#{spec.name}-#{spec.version}.gemspec")
-   open(spec_file, 'wb') do |f|
-     f.print spec.to_ruby
-   end
-
-From 3de652d8198be9cd2998c095903889a80e738275 Mon Sep 17 00:00:00 2001
-From: Nobuyoshi Nakada <nobu at ruby-lang.org>
-Date: Thu, 7 Apr 2022 01:44:43 +0900
-Subject: [PATCH 5/5] Install built gem extension binaries
-
----
- tool/rbinstall.rb | 56 ++++++++++++++---------------------------------
- 1 file changed, 16 insertions(+), 40 deletions(-)
-
-diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb
-index 9d9b672be472..624961b4eee6 100755
---- a/tool/rbinstall.rb
-+++ b/tool/rbinstall.rb
-@@ -858,6 +858,9 @@ class UnpackedInstaller < GemInstaller
-     def write_cache_file
-     end
- 
-+    def build_extensions
-+    end
-+
-     def shebang(bin_file_name)
-       path = File.join(gem_dir, spec.bindir, bin_file_name)
-       first_line = File.open(path, "rb") {|file| file.gets}
-@@ -940,13 +943,12 @@ def ensure_writable_dir(dir)
-   install_default_gem('ext', srcdir, bindir)
- end
- 
--def load_gemspec(file, expanded = false)
-+def load_gemspec(file, base = nil)
-   file = File.realpath(file)
-   code = File.read(file, encoding: "utf-8:-")
-   code.gsub!(/(?:`git[^\`]*`|%x\[git[^\]]*\])\.split\([^\)]*\)/m) do
-     files = []
--    if expanded
--      base = File.dirname(file)
-+    if base
-       Dir.glob("**/*", File::FNM_DOTMATCH, base: base) do |n|
-         case File.basename(n); when ".", ".."; next; end
-         next if File.directory?(File.join(base, n))
-@@ -959,7 +961,7 @@ def load_gemspec(file, expanded = false)
-   unless Gem::Specification === spec
-     raise TypeError, "[#{file}] isn't a Gem::Specification (#{spec.class} instead)."
-   end
--  spec.loaded_from = file
-+  spec.loaded_from = base ? File.join(base, File.basename(file)) : file
-   spec.files.reject! {|n| n.end_with?(".gemspec") or n.start_with?(".git")}
- 
-   spec
-@@ -1014,20 +1016,6 @@ def install_default_gem(dir, srcdir, bindir)
- end
- 
- install?(:ext, :comm, :gem, :'bundled-gems') do
--  if CONFIG['CROSS_COMPILING'] == 'yes'
--    # The following hacky steps set "$ruby = BASERUBY" in tool/fake.rb
--    $hdrdir = ''
--    $extmk = nil
--    $ruby = nil  # ...
--    ruby_path = $ruby + " -I#{Dir.pwd}" # $baseruby + " -I#{Dir.pwd}"
--  else
--    # ruby_path = File.expand_path(with_destdir(File.join(bindir, ruby_install_name)))
--    ENV['RUBYLIB'] = nil
--    ENV['RUBYOPT'] = nil
--    ruby_path = File.expand_path(with_destdir(File.join(bindir, ruby_install_name))) + " --disable=gems -I#{with_destdir(archlibdir)}"
--  end
--  Gem.instance_variable_set(:@ruby, ruby_path) if Gem.ruby != ruby_path
--
-   gem_dir = Gem.default_dir
-   install_dir = with_destdir(gem_dir)
-   prepare "bundled gems", gem_dir
-@@ -1047,40 +1035,28 @@ def install_default_gem(dir, srcdir, bindir)
-     :wrappers => true,
-     :format_executable => true,
-   }
--  gem_ext_dir = "#$extout/gems/#{CONFIG['arch']}"
--  extensions_dir = with_destdir(Gem::StubSpecification.gemspec_stub("", gem_dir, gem_dir).extensions_dir)
-+
-+  extensions_dir = Gem::StubSpecification.gemspec_stub("", gem_dir, gem_dir).extensions_dir
-+  specifications_dir = File.join(gem_dir, "specifications")
-+  build_dir = Gem::StubSpecification.gemspec_stub("", ".bundle", ".bundle").extensions_dir
- 
-   File.foreach("#{srcdir}/gems/bundled_gems") do |name|
-     next if /^\s*(?:#|$)/ =~ name
-     next unless /^(\S+)\s+(\S+).*/ =~ name
-     gem_name = "#$1-#$2"
--    path = "#{srcdir}/.bundle/gems/#{gem_name}/#{gem_name}.gemspec"
--    if File.exist?(path)
--      spec = load_gemspec(path)
--    else
--      path = "#{srcdir}/.bundle/gems/#{gem_name}/#$1.gemspec"
--      next unless File.exist?(path)
--      spec = load_gemspec(path, true)
--    end
-+    path = "#{srcdir}/.bundle/specifications/#{gem_name}.gemspec"
-+    next unless File.exist?(path)
-+    spec = load_gemspec(path, "#{srcdir}/.bundle/gems/#{gem_name}")
-     next unless spec.platform == Gem::Platform::RUBY
-     next unless spec.full_name == gem_name
--    if !spec.extensions.empty? && CONFIG["EXTSTATIC"] == "static"
--      puts "skip installation of #{spec.name} #{spec.version}; bundled gem with an extension library is not supported on --with-static-linked-ext"
--      next
--    end
-     spec.extension_dir = "#{extensions_dir}/#{spec.full_name}"
--    if File.directory?(ext = "#{gem_ext_dir}/#{spec.full_name}")
--      spec.extensions[0] ||= "-"
--    end
-     package = RbInstall::DirPackage.new spec
-     ins = RbInstall::UnpackedInstaller.new(package, options)
-     puts "#{INDENT}#{spec.name} #{spec.version}"
-     ins.install
--    unless $dryrun
--      File.chmod($data_mode, File.join(install_dir, "specifications", "#{spec.full_name}.gemspec"))
--    end
--    unless spec.extensions.empty?
--      install_recursive(ext, spec.extension_dir)
-+    install_recursive("#{build_dir}/#{gem_name}", "#{extensions_dir}/#{gem_name}") do |src, dest|
-+      # puts "#{INDENT}    #{dest[extensions_dir.size+gem_name.size+2..-1]}"
-+      install src, dest, :mode => (File.executable?(src) ? $prog_mode : $data_mode)
-     end
-     installed_gems[spec.full_name] = true
-   end
diff --git a/ruby-3.4.2-openssl-Fix-SHA-1-PSS-tests.patch b/ruby-3.4.2-openssl-Fix-SHA-1-PSS-tests.patch
deleted file mode 100644
index 26ca3ec..0000000
--- a/ruby-3.4.2-openssl-Fix-SHA-1-PSS-tests.patch
+++ /dev/null
@@ -1,136 +0,0 @@
-From e0e771b76fbede656fe51c8bf18ce810f4afeee0 Mon Sep 17 00:00:00 2001
-From: Jun Aruga <jaruga at redhat.com>
-Date: Tue, 8 Apr 2025 15:03:06 +0200
-Subject: [PATCH] Fix the tests using SHA-1 Probabilistic Signature Scheme
- (PSS) parameters.
-
-Fedora OpenSSL 3.5 on rawhide stopped accepting SHA-1 PSS[1] parameters.
-This is different from the SHA-1 signatures which Fedora OpenSSL stopped
-accepting since Fedora 41.[2]
-
-This commit fixes the following test failures related to the SHA-1 PSS
-parameters with Fedora OpenSSL 3.5.
-Note these failures are the downstream Fedora OpenSSL RPM specific. The tests
-pass without this commit with the upstream OpenSSL 3.5.
-
-```
-$ rpm -q openssl-libs openssl-devel
-openssl-libs-3.5.0-2.fc43.x86_64
-openssl-devel-3.5.0-2.fc43.x86_64
-
-$ bundle exec rake test
-...
-E
-===============================================================================================
-Error: test_sign_verify_options(OpenSSL::TestPKeyRSA): OpenSSL::PKey::PKeyError: EVP_PKEY_CTX_ctrl_str(ctx, "rsa_mgf1_md", "SHA1"): digest not allowed (digest=SHA1)
-/mnt/git/ruby/openssl/test/openssl/test_pkey_rsa.rb:113:in 'Hash#each'
-/mnt/git/ruby/openssl/test/openssl/test_pkey_rsa.rb:113:in 'OpenSSL::PKey::PKey#sign'
-/mnt/git/ruby/openssl/test/openssl/test_pkey_rsa.rb:113:in 'OpenSSL::TestPKeyRSA#test_sign_verify_options'
-     110:       "rsa_pss_saltlen" => 20,
-     111:       "rsa_mgf1_md" => "SHA1"
-     112:     }
-  => 113:     sig_pss = key.sign("SHA256", data, pssopts)
-     114:     assert_equal 256, sig_pss.bytesize
-     115:     assert_equal true, key.verify("SHA256", sig_pss, data, pssopts)
-     116:     assert_equal true, key.verify_pss("SHA256", sig_pss, data,
-===============================================================================================
-E
-===============================================================================================
-Error: test_sign_verify_pss(OpenSSL::TestPKeyRSA): OpenSSL::PKey::RSAError: digest not allowed (digest=SHA1)
-/mnt/git/ruby/openssl/test/openssl/test_pkey_rsa.rb:191:in 'OpenSSL::PKey::RSA#sign_pss'
-/mnt/git/ruby/openssl/test/openssl/test_pkey_rsa.rb:191:in 'OpenSSL::TestPKeyRSA#test_sign_verify_pss'
-     188:     data = "Sign me!"
-     189:     invalid_data = "Sign me?"
-     190:
-  => 191:     signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA1")
-     192:     assert_equal 256, signature.bytesize
-     193:     assert_equal true,
-     194:       key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1")
-===============================================================================================
-...
-577 tests, 4186 assertions, 0 failures, 2 errors, 0 pendings, 3 omissions, 0 notifications
-```
-
-[1] https://en.wikipedia.org/wiki/Probabilistic_signature_scheme
-[2] https://fedoraproject.org/wiki/Changes/OpenSSLDistrustSHA1SigVer
----
- test/openssl/test_pkey_rsa.rb | 28 ++++++++++++++--------------
- 1 file changed, 14 insertions(+), 14 deletions(-)
-
-diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
-index 360309b..850c16a 100644
---- a/test/openssl/test_pkey_rsa.rb
-+++ b/test/openssl/test_pkey_rsa.rb
-@@ -108,13 +108,13 @@ def test_sign_verify_options
-     pssopts = {
-       "rsa_padding_mode" => "pss",
-       "rsa_pss_saltlen" => 20,
--      "rsa_mgf1_md" => "SHA1"
-+      "rsa_mgf1_md" => "SHA256"
-     }
-     sig_pss = key.sign("SHA256", data, pssopts)
-     assert_equal 256, sig_pss.bytesize
-     assert_equal true, key.verify("SHA256", sig_pss, data, pssopts)
-     assert_equal true, key.verify_pss("SHA256", sig_pss, data,
--                                      salt_length: 20, mgf1_hash: "SHA1")
-+                                      salt_length: 20, mgf1_hash: "SHA256")
-     # Defaults to PKCS #1 v1.5 padding => verification failure
-     assert_equal false, key.verify("SHA256", sig_pss, data)
- 
-@@ -188,22 +188,22 @@ def test_sign_verify_pss
-     data = "Sign me!"
-     invalid_data = "Sign me?"
- 
--    signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA1")
-+    signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA256")
-     assert_equal 256, signature.bytesize
-     assert_equal true,
--      key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1")
-+      key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA256")
-     assert_equal true,
--      key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
-+      key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256")
-     assert_equal false,
--      key.verify_pss("SHA256", signature, invalid_data, salt_length: 20, mgf1_hash: "SHA1")
-+      key.verify_pss("SHA256", signature, invalid_data, salt_length: 20, mgf1_hash: "SHA256")
- 
--    signature = key.sign_pss("SHA256", data, salt_length: :digest, mgf1_hash: "SHA1")
-+    signature = key.sign_pss("SHA256", data, salt_length: :digest, mgf1_hash: "SHA256")
-     assert_equal true,
--      key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA1")
-+      key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA256")
-     assert_equal true,
--      key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
-+      key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256")
-     assert_equal false,
--      key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1")
-+      key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA256")
- 
-     # The sign_pss with `salt_length: :max` raises the "invalid salt length"
-     # error in FIPS. We need to skip the tests in FIPS.
-@@ -213,18 +213,18 @@ def test_sign_verify_pss
-     # FIPS 186-5 section 5.4 PKCS #1
-     # https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
-     unless OpenSSL.fips_mode
--      signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA1")
-+      signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
-       # Should verify on the following salt_length (sLen).
-       # sLen <= emLen (octat) - 2 - hLen (octet) = 2048 / 8 - 2 - 256 / 8 = 222
-       # https://datatracker.ietf.org/doc/html/rfc8017#section-9.1.1
-       assert_equal true,
--        key.verify_pss("SHA256", signature, data, salt_length: 222, mgf1_hash: "SHA1")
-+        key.verify_pss("SHA256", signature, data, salt_length: 222, mgf1_hash: "SHA256")
-       assert_equal true,
--        key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
-+        key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256")
-     end
- 
-     assert_raise(OpenSSL::PKey::RSAError) {
--      key.sign_pss("SHA256", data, salt_length: 223, mgf1_hash: "SHA1")
-+      key.sign_pss("SHA256", data, salt_length: 223, mgf1_hash: "SHA256")
-     }
-   end
- 
--- 
-2.48.1
-
diff --git a/ruby-4.0.1-Support-customizable-rustc_flags-for-rustc-builds.patch b/ruby-4.0.1-Support-customizable-rustc_flags-for-rustc-builds.patch
new file mode 100644
index 0000000..3d16ff2
--- /dev/null
+++ b/ruby-4.0.1-Support-customizable-rustc_flags-for-rustc-builds.patch
@@ -0,0 +1,117 @@
+From 1cfb11bc8d01e4fc1ff47807721e29b250f0f19f Mon Sep 17 00:00:00 2001
+From: Jarek Prokop <jprokop at redhat.com>
+Date: Mon, 22 Dec 2025 10:13:34 +0100
+Subject: [PATCH] Support customizable rustc_flags for rustc builds.
+
+Add `rustc_flags` option for configure that appends to RUSTC_FLAGS
+flags used when compiling with rustc for customizable build flags.
+It appends to existing defaults in RUSTC_FLAGS.
+
+Co-authored-by: Alan Wu <XrXr at users.noreply.github.com>
+---
+ common.mk            | 10 ++--------
+ configure.ac         |  8 ++++++++
+ defs/jit.mk          |  2 ++
+ template/Makefile.in |  1 +
+ 4 files changed, 13 insertions(+), 8 deletions(-)
+
+diff --git a/common.mk b/common.mk
+index 08fee9119a..9ac5ae919f 100644
+--- a/common.mk
++++ b/common.mk
+@@ -270,21 +270,15 @@ MAKE_LINK = $(MINIRUBY) -rfileutils -e "include FileUtils::Verbose" \
+ # For release builds
+ YJIT_RUSTC_ARGS = --crate-name=yjit \
+ 	$(JIT_RUST_FLAGS) \
++	$(RUSTC_FLAGS) \
+ 	--edition=2021 \
+-	-g \
+-	-C lto=thin \
+-	-C opt-level=3 \
+-	-C overflow-checks=on \
+ 	'--out-dir=$(CARGO_TARGET_DIR)/release/' \
+ 	'$(top_srcdir)/yjit/src/lib.rs'
+ 
+ ZJIT_RUSTC_ARGS = --crate-name=zjit \
+ 	$(JIT_RUST_FLAGS) \
++	$(RUSTC_FLAGS) \
+ 	--edition=2024 \
+-	-g \
+-	-C lto=thin \
+-	-C opt-level=3 \
+-	-C overflow-checks=on \
+ 	'--out-dir=$(CARGO_TARGET_DIR)/release/' \
+ 	'$(top_srcdir)/zjit/src/lib.rs'
+ 
+diff --git a/configure.ac b/configure.ac
+index 2bbce78fd0..a3aa6dc383 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -69,6 +69,7 @@ dnl 93(bright yellow) is copied from .github/workflows/mingw.yml
+ AC_ARG_VAR([cflags], [additional CFLAGS (ignored when CFLAGS is given)])dnl
+ AC_ARG_VAR([cppflags], [additional CPPFLAGS (ignored when CPPFLAGS is given)])dnl
+ AC_ARG_VAR([cxxflags], [additional CXXFLAGS (ignored when CXXFLAGS is given)])dnl
++AC_ARG_VAR([rustc_flags], [additional RUSTC_FLAGS])dnl
+ 
+ [begin]_group "environment section" && {
+ HAVE_BASERUBY=yes
+@@ -4051,6 +4054,11 @@ AS_CASE(["${ZJIT_SUPPORT}"],
+     AC_DEFINE(USE_ZJIT, 0)
+ ])
+ 
++RUSTC_FLAGS='-g -C lto=thin -C opt-level=3 -C overflow-checks=on'
++AS_IF([test -n "${rustc_flags}"], [
++    RUSTC_FLAGS="${RUSTC_FLAGS} ${rustc_flags}"
++])
++
+ JIT_RUST_FLAGS='--crate-type=staticlib --cfg feature=\"stats_allocator\"'
+ RLIB_DIR=
+ AS_CASE(["$JIT_CARGO_SUPPORT:$YJIT_SUPPORT:$ZJIT_SUPPORT"],
+@@ -4108,6 +4114,7 @@ AS_IF([test -n "$RUST_LIB"], [
+ dnl These variables end up in ::RbConfig::CONFIG
+ AC_SUBST(RUSTC)dnl Rust compiler command
+ AC_SUBST(JIT_RUST_FLAGS)dnl the common rustc flags for JIT crates such as zjit
++AC_SUBST(RUSTC_FLAGS)dnl user-configurable rustc compiler flags
+ AC_SUBST(CARGO)dnl Cargo command for Rust builds
+ AC_SUBST(CARGO_BUILD_ARGS)dnl for selecting Rust build profiles
+ AC_SUBST(YJIT_SUPPORT)dnl what flavor of YJIT the Ruby build includes
+@@ -4852,6 +4859,7 @@ config_summary "strip command"       "$STRIP"
+ config_summary "install doc"         "$DOCTARGETS"
+ config_summary "YJIT support"        "$YJIT_SUPPORT"
+ config_summary "ZJIT support"        "$ZJIT_SUPPORT"
++config_summary "RUSTC_FLAGS"         "$RUSTC_FLAGS"
+ config_summary "man page type"       "$MANTYPE"
+ config_summary "search path"         "$search_path"
+ config_summary "static-linked-ext"   ${EXTSTATIC:+"yes"}
+diff --git a/defs/jit.mk b/defs/jit.mk
+index 42b56c4cd9..27b14e7a07 100644
+--- a/defs/jit.mk
++++ b/defs/jit.mk
+@@ -40,6 +40,7 @@ else ifneq ($(strip $(RLIB_DIR)),) # combo build
+ $(RUST_LIB): $(srcdir)/ruby.rs
+ 	$(ECHO) 'building $(@F)'
+ 	$(gnumake_recursive)$(Q) $(RUSTC) --edition=2024 \
++	    $(RUSTC_FLAGS) \
+ 	    '-L$(@D)' \
+ 	    --extern=yjit \
+ 	    --extern=zjit \
+@@ -58,6 +59,7 @@ $(JIT_RLIB):
+ 	$(gnumake_recursive)$(Q) $(RUSTC) --crate-name=jit \
+ 	    --edition=2024 \
+ 	    $(JIT_RUST_FLAGS) \
++	    $(RUSTC_FLAGS) \
+ 	    '--out-dir=$(@D)' \
+ 	    '$(top_srcdir)/jit/src/lib.rs'
+ endif # ifneq ($(JIT_CARGO_SUPPORT),no)
+diff --git a/template/Makefile.in b/template/Makefile.in
+index 443c394cb4..0b7b50e3aa 100644
+--- a/template/Makefile.in
++++ b/template/Makefile.in
+@@ -115,6 +115,7 @@ CARGO_TARGET_DIR=@abs_top_builddir@/target
+ CARGO_BUILD_ARGS=@CARGO_BUILD_ARGS@
+ ZJIT_TEST_FEATURES=@ZJIT_TEST_FEATURES@
+ JIT_RUST_FLAGS=@JIT_RUST_FLAGS@
++RUSTC_FLAGS=@RUSTC_FLAGS@
+ RLIB_DIR=@RLIB_DIR@
+ RUST_LIB=@RUST_LIB@
+ RUST_LIBOBJ = $(RUST_LIB:.a=. at OBJEXT@)
diff --git a/ruby-4.0.7-rubygems-pr9877-stop-vendoring-resolv.patch b/ruby-4.0.7-rubygems-pr9877-stop-vendoring-resolv.patch
new file mode 100644
index 0000000..9c1b3f7
--- /dev/null
+++ b/ruby-4.0.7-rubygems-pr9877-stop-vendoring-resolv.patch
@@ -0,0 +1,3579 @@
+From 0b8e0b1c28bc839095e2c0e884313f0f5ef1fc53 Mon Sep 17 00:00:00 2001
+From: Hiroshi SHIBATA <hsbt at ruby-lang.org>
+Date: Wed, 16 Sep 2026 09:34:09 +0900
+Subject: [PATCH] [ruby/rubygems] Stop vendoring resolv for two regexps
+
+Vendored net/http loaded the whole 3500-line DNS resolver only to match
+Resolv::IPv4::Regex and Resolv::IPv6::Regex when deciding whether an
+address may be sent as a Server Name Indication. Inline equivalent
+regexps instead, so that a resolver RubyGems never resolves with is no
+longer shipped and no longer shows up as an outdated copy in security
+audits of the Ruby tarball.
+
+https://bugs.ruby-lang.org/issues/22317
+
+https://github.com/ruby/rubygems/commit/9f2bd5f66c
+
+Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
+---
+ lib/rubygems/vendor/net-http/lib/net/http.rb |   26 +-
+ lib/rubygems/vendor/resolv/lib/resolv.rb     | 3508 ------------------
+ 2 files changed, 24 insertions(+), 3510 deletions(-)
+ delete mode 100644 lib/rubygems/vendor/resolv/lib/resolv.rb
+
+diff --git a/lib/rubygems/vendor/net-http/lib/net/http.rb b/lib/rubygems/vendor/net-http/lib/net/http.rb
+index 4800cd25f13c21..b205e49734eebf 100644
+--- a/lib/rubygems/vendor/net-http/lib/net/http.rb
++++ b/lib/rubygems/vendor/net-http/lib/net/http.rb
+@@ -22,7 +22,6 @@
+ 
+ require_relative '../../../net-protocol/lib/net/protocol'
+ require_relative '../../../uri/lib/uri'
+-require_relative '../../../resolv/lib/resolv'
+ autoload :OpenSSL, 'openssl'
+ 
+ module Gem::Net   #:nodoc:
+@@ -1531,6 +1530,29 @@ def use_ssl=(flag)
+ 
+     SSL_IVNAMES = SSL_ATTRIBUTES.map { |a| "@#{a}".to_sym }.freeze # :nodoc:
+ 
++    # Resolv::IPv4::Regex and Resolv::IPv6::Regex, copied from resolv 0.7.1 rather
++    # than required, so that net/http does not load a DNS resolver just to keep an
++    # IP address literal out of the Server Name Indication.
++    # https://github.com/ruby/resolv/blob/v0.7.1/lib/resolv.rb
++    ipv4_octet = /0|1(?:[0-9][0-9]?)?|2(?:[0-4][0-9]?|5[0-5]?|[6-9])?|[3-9][0-9]?/
++    hex16 = /[0-9A-Fa-f]{1,4}/
++    hex16_group = /(?:#{hex16}(?::#{hex16})*)?/
++    dotted_quad = /\d+\.\d+\.\d+\.\d+/
++    zone_id = /%[-0-9A-Za-z._~]+/
++
++    IPV4_ADDRESS = /\A(?:#{ipv4_octet})\.(?:#{ipv4_octet})\.(?:#{ipv4_octet})\.(?:#{ipv4_octet})\z/ # :nodoc:
++
++    IPV6_ADDRESS = /\A(?:
++        (?:#{hex16}:){7}#{hex16}                                            # a:b:c:d:e:f:g:h
++      | #{hex16_group}::#{hex16_group}                                      # a::b
++      | (?:#{hex16}:){6}#{dotted_quad}                                      # a:b:c:d:e:f:w.x.y.z
++      | #{hex16_group}::(?:#{hex16}:)*#{dotted_quad}                        # a::b:w.x.y.z
++      | [Ff][Ee]80(?::#{hex16}){7}#{zone_id}                                # fe80:b:c:d:e:f:g:h%em1
++      | [Ff][Ee]80:(?:#{hex16_group}::#{hex16_group}|:#{hex16_group})?:#{hex16}#{zone_id} # fe80::b%em1
++    )\z/x # :nodoc:
++
++    private_constant :IPV4_ADDRESS, :IPV6_ADDRESS
++
+     # Sets or returns the path to a CA certification file in PEM format.
+     attr_accessor :ca_file
+ 
+@@ -1714,7 +1736,7 @@ def connect
+ 
+         # Server Name Indication (SNI) RFC 3546/6066
+         case @address
+-        when Gem::Resolv::IPv4::Regex, Gem::Resolv::IPv6::Regex
++        when IPV4_ADDRESS, IPV6_ADDRESS
+           # don't set SNI, as IP addresses in SNI is not valid
+           # per RFC 6066, section 3.
+ 
+diff --git a/lib/rubygems/vendor/resolv/lib/resolv.rb b/lib/rubygems/vendor/resolv/lib/resolv.rb
+deleted file mode 100644
+index f922c731b05026..00000000000000
+--- a/lib/rubygems/vendor/resolv/lib/resolv.rb
++++ /dev/null
+@@ -1,3499 +0,0 @@
+-# frozen_string_literal: true
+-
+-require 'socket'
+-require_relative '../../../vendored_timeout'
+-require 'io/wait'
+-require_relative '../../../vendored_securerandom'
+-require 'rbconfig'
+-
+-# Gem::Resolv is a thread-aware DNS resolver library written in Ruby.  Gem::Resolv can
+-# handle multiple DNS requests concurrently without blocking the entire Ruby
+-# interpreter.
+-#
+-# See also resolv-replace.rb to replace the libc resolver with Gem::Resolv.
+-#
+-# Gem::Resolv can look up various DNS resources using the DNS module directly.
+-#
+-# Examples:
+-#
+-#   p Gem::Resolv.getaddress "www.ruby-lang.org"
+-#   p Gem::Resolv.getname "210.251.121.214"
+-#
+-#   Gem::Resolv::DNS.open do |dns|
+-#     ress = dns.getresources "www.ruby-lang.org", Gem::Resolv::DNS::Resource::IN::A
+-#     p ress.map(&:address)
+-#     ress = dns.getresources "ruby-lang.org", Gem::Resolv::DNS::Resource::IN::MX
+-#     p ress.map { |r| [r.exchange.to_s, r.preference] }
+-#   end
+-#
+-#
+-# == Bugs
+-#
+-# * NIS is not supported.
+-# * /etc/nsswitch.conf is not supported.
+-
+-class Gem::Resolv
+-
+-  # The version string
+-  VERSION = "0.7.0"
+-
+-  ##
+-  # Looks up the first IP address for +name+.
+-
+-  def self.getaddress(name)
+-    DefaultResolver.getaddress(name)
+-  end
+-
+-  ##
+-  # Looks up all IP address for +name+.
+-
+-  def self.getaddresses(name)
+-    DefaultResolver.getaddresses(name)
+-  end
+-
+-  ##
+-  # Iterates over all IP addresses for +name+.
+-
+-  def self.each_address(name, &block)
+-    DefaultResolver.each_address(name, &block)
+-  end
+-
+-  ##
+-  # Looks up the hostname of +address+.
+-
+-  def self.getname(address)
+-    DefaultResolver.getname(address)
+-  end
+-
+-  ##
+-  # Looks up all hostnames for +address+.
+-
+-  def self.getnames(address)
+-    DefaultResolver.getnames(address)
+-  end
+-
+-  ##
+-  # Iterates over all hostnames for +address+.
+-
+-  def self.each_name(address, &proc)
+-    DefaultResolver.each_name(address, &proc)
+-  end
+-
+-  ##
+-  # Creates a new Gem::Resolv using +resolvers+.
+-  #
+-  # If +resolvers+ is not given, a hash, or +nil+, uses a Hosts resolver and
+-  # and a DNS resolver.  If +resolvers+ is a hash, uses the hash as
+-  # configuration for the DNS resolver.
+-
+-  def initialize(resolvers=(arg_not_set = true; nil), use_ipv6: (keyword_not_set = true; nil))
+-    if !keyword_not_set && !arg_not_set
+-      warn "Support for separate use_ipv6 keyword is deprecated, as it is ignored if an argument is provided. Do not provide a positional argument if using the use_ipv6 keyword argument.", uplevel: 1
+-    end
+-
+-    @resolvers = case resolvers
+-    when Hash, nil
+-      [Hosts.new, DNS.new(DNS::Config.default_config_hash.merge(resolvers || {}))]
+-    else
+-      resolvers
+-    end
+-  end
+-
+-  ##
+-  # Looks up the first IP address for +name+.
+-
+-  def getaddress(name)
+-    each_address(name) {|address| return address}
+-    raise ResolvError.new("no address for #{name}")
+-  end
+-
+-  ##
+-  # Looks up all IP address for +name+.
+-
+-  def getaddresses(name)
+-    ret = []
+-    each_address(name) {|address| ret << address}
+-    return ret
+-  end
+-
+-  ##
+-  # Iterates over all IP addresses for +name+.
+-
+-  def each_address(name)
+-    if AddressRegex =~ name
+-      yield name
+-      return
+-    end
+-    yielded = false
+-    @resolvers.each {|r|
+-      r.each_address(name) {|address|
+-        yield address.to_s
+-        yielded = true
+-      }
+-      return if yielded
+-    }
+-  end
+-
+-  ##
+-  # Looks up the hostname of +address+.
+-
+-  def getname(address)
+-    each_name(address) {|name| return name}
+-    raise ResolvError.new("no name for #{address}")
+-  end
+-
+-  ##
+-  # Looks up all hostnames for +address+.
+-
+-  def getnames(address)
+-    ret = []
+-    each_name(address) {|name| ret << name}
+-    return ret
+-  end
+-
+-  ##
+-  # Iterates over all hostnames for +address+.
+-
+-  def each_name(address)
+-    yielded = false
+-    @resolvers.each {|r|
+-      r.each_name(address) {|name|
+-        yield name.to_s
+-        yielded = true
+-      }
+-      return if yielded
+-    }
+-  end
+-
+-  ##
+-  # Indicates a failure to resolve a name or address.
+-
+-  class ResolvError < StandardError; end
+-
+-  ##
+-  # Indicates a timeout resolving a name or address.
+-
+-  class ResolvTimeout < Gem::Timeout::Error; end
+-
+-  ##
+-  # Gem::Resolv::Hosts is a hostname resolver that uses the system hosts file.
+-
+-  class Hosts
+-    if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::RbConfig::CONFIG['host_os'] =~ /mswin/
+-      begin
+-        require 'win32/resolv' unless defined?(Win32::Resolv)
+-        hosts = Win32::Resolv.get_hosts_path || IO::NULL
+-      rescue LoadError
+-      end
+-    end
+-    # The default file name for host names
+-    DefaultFileName = hosts || '/etc/hosts'
+-
+-    ##
+-    # Creates a new Gem::Resolv::Hosts, using +filename+ for its data source.
+-
+-    def initialize(filename = DefaultFileName)
+-      @filename = filename
+-      @mutex = Thread::Mutex.new
+-      @initialized = nil
+-    end
+-
+-    def lazy_initialize # :nodoc:
+-      @mutex.synchronize {
+-        unless @initialized
+-          @name2addr = {}
+-          @addr2name = {}
+-          File.open(@filename, 'rb') {|f|
+-            f.each {|line|
+-              line.sub!(/#.*/, '')
+-              addr, *hostnames = line.split(/\s+/)
+-              next unless addr
+-              (@addr2name[addr] ||= []).concat(hostnames)
+-              hostnames.each {|hostname| (@name2addr[hostname] ||= []) << addr}
+-            }
+-          }
+-          @name2addr.each {|name, arr| arr.reverse!}
+-          @initialized = true
+-        end
+-      }
+-      self
+-    end
+-
+-    ##
+-    # Gets the IP address of +name+ from the hosts file.
+-
+-    def getaddress(name)
+-      each_address(name) {|address| return address}
+-      raise ResolvError.new("#{@filename} has no name: #{name}")
+-    end
+-
+-    ##
+-    # Gets all IP addresses for +name+ from the hosts file.
+-
+-    def getaddresses(name)
+-      ret = []
+-      each_address(name) {|address| ret << address}
+-      return ret
+-    end
+-
+-    ##
+-    # Iterates over all IP addresses for +name+ retrieved from the hosts file.
+-
+-    def each_address(name, &proc)
+-      lazy_initialize
+-      @name2addr[name]&.each(&proc)
+-    end
+-
+-    ##
+-    # Gets the hostname of +address+ from the hosts file.
+-
+-    def getname(address)
+-      each_name(address) {|name| return name}
+-      raise ResolvError.new("#{@filename} has no address: #{address}")
+-    end
+-
+-    ##
+-    # Gets all hostnames for +address+ from the hosts file.
+-
+-    def getnames(address)
+-      ret = []
+-      each_name(address) {|name| ret << name}
+-      return ret
+-    end
+-
+-    ##
+-    # Iterates over all hostnames for +address+ retrieved from the hosts file.
+-
+-    def each_name(address, &proc)
+-      lazy_initialize
+-      @addr2name[address]&.each(&proc)
+-    end
+-  end
+-
+-  ##
+-  # Gem::Resolv::DNS is a DNS stub resolver.
+-  #
+-  # Information taken from the following places:
+-  #
+-  # * STD0013
+-  # * RFC 1035
+-  # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
+-  # * etc.
+-
+-  class DNS
+-
+-    ##
+-    # Default DNS Port
+-
+-    Port = 53
+-
+-    ##
+-    # Default DNS UDP packet size
+-
+-    UDPSize = 512
+-
+-    ##
+-    # Creates a new DNS resolver.  See Gem::Resolv::DNS.new for argument details.
+-    #
+-    # Yields the created DNS resolver to the block, if given, otherwise
+-    # returns it.
+-
+-    def self.open(*args)
+-      dns = new(*args)
+-      return dns unless block_given?
+-      begin
+-        yield dns
+-      ensure
+-        dns.close
+-      end
+-    end
+-
+-    ##
+-    # Creates a new DNS resolver.
+-    #
+-    # +config_info+ can be:
+-    #
+-    # nil:: Uses /etc/resolv.conf.
+-    # String:: Path to a file using /etc/resolv.conf's format.
+-    # Hash:: Must contain :nameserver, :search and :ndots keys.
+-    # :nameserver_port can be used to specify port number of nameserver address.
+-    # :raise_timeout_errors can be used to raise timeout errors
+-    # as exceptions instead of treating the same as an NXDOMAIN response.
+-    #
+-    # The value of :nameserver should be an address string or
+-    # an array of address strings.
+-    # - :nameserver => '8.8.8.8'
+-    # - :nameserver => ['8.8.8.8', '8.8.4.4']
+-    #
+-    # The value of :nameserver_port should be an array of
+-    # pair of nameserver address and port number.
+-    # - :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]
+-    #
+-    # Example:
+-    #
+-    #   Gem::Resolv::DNS.new(:nameserver => ['210.251.121.21'],
+-    #                   :search => ['ruby-lang.org'],
+-    #                   :ndots => 1)
+-
+-    def initialize(config_info=nil)
+-      @mutex = Thread::Mutex.new
+-      @config = Config.new(config_info)
+-      @initialized = nil
+-    end
+-
+-    # Sets the resolver timeouts.  This may be a single positive number
+-    # or an array of positive numbers representing timeouts in seconds.
+-    # If an array is specified, a DNS request will retry and wait for
+-    # each successive interval in the array until a successful response
+-    # is received.  Specifying +nil+ reverts to the default timeouts:
+-    # [ 5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second ]
+-    #
+-    # Example:
+-    #
+-    #   dns.timeouts = 3
+-    #
+-    def timeouts=(values)
+-      @config.timeouts = values
+-    end
+-
+-    def lazy_initialize # :nodoc:
+-      @mutex.synchronize {
+-        unless @initialized
+-          @config.lazy_initialize
+-          @initialized = true
+-        end
+-      }
+-      self
+-    end
+-
+-    ##
+-    # Closes the DNS resolver.
+-
+-    def close
+-      @mutex.synchronize {
+-        if @initialized
+-          @initialized = false
+-        end
+-      }
+-    end
+-
+-    ##
+-    # Gets the IP address of +name+ from the DNS resolver.
+-    #
+-    # +name+ can be a Gem::Resolv::DNS::Name or a String.  Retrieved address will
+-    # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
+-
+-    def getaddress(name)
+-      each_address(name) {|address| return address}
+-      raise ResolvError.new("DNS result has no information for #{name}")
+-    end
+-
+-    ##
+-    # Gets all IP addresses for +name+ from the DNS resolver.
+-    #
+-    # +name+ can be a Gem::Resolv::DNS::Name or a String.  Retrieved addresses will
+-    # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
+-
+-    def getaddresses(name)
+-      ret = []
+-      each_address(name) {|address| ret << address}
+-      return ret
+-    end
+-
+-    ##
+-    # Iterates over all IP addresses for +name+ retrieved from the DNS
+-    # resolver.
+-    #
+-    # +name+ can be a Gem::Resolv::DNS::Name or a String.  Retrieved addresses will
+-    # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
+-
+-    def each_address(name)
+-      if use_ipv6?
+-        each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
+-      end
+-      each_resource(name, Resource::IN::A) {|resource| yield resource.address}
+-    end
+-
+-    def use_ipv6? # :nodoc:
+-      @config.lazy_initialize unless @config.instance_variable_get(:@initialized)
+-
+-      use_ipv6 = @config.use_ipv6?
+-      unless use_ipv6.nil?
+-        return use_ipv6
+-      end
+-
+-      begin
+-        list = Socket.ip_address_list
+-      rescue NotImplementedError
+-        return true
+-      end
+-      list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
+-    end
+-    private :use_ipv6?
+-
+-    ##
+-    # Gets the hostname for +address+ from the DNS resolver.
+-    #
+-    # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String.  Retrieved
+-    # name will be a Gem::Resolv::DNS::Name.
+-
+-    def getname(address)
+-      each_name(address) {|name| return name}
+-      raise ResolvError.new("DNS result has no information for #{address}")
+-    end
+-
+-    ##
+-    # Gets all hostnames for +address+ from the DNS resolver.
+-    #
+-    # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String.  Retrieved
+-    # names will be Gem::Resolv::DNS::Name instances.
+-
+-    def getnames(address)
+-      ret = []
+-      each_name(address) {|name| ret << name}
+-      return ret
+-    end
+-
+-    ##
+-    # Iterates over all hostnames for +address+ retrieved from the DNS
+-    # resolver.
+-    #
+-    # +address+ must be a Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String.  Retrieved
+-    # names will be Gem::Resolv::DNS::Name instances.
+-
+-    def each_name(address)
+-      case address
+-      when Name
+-        ptr = address
+-      when IPv4, IPv6
+-        ptr = address.to_name
+-      when IPv4::Regex
+-        ptr = IPv4.create(address).to_name
+-      when IPv6::Regex
+-        ptr = IPv6.create(address).to_name
+-      else
+-        raise ResolvError.new("cannot interpret as address: #{address}")
+-      end
+-      each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
+-    end
+-
+-    ##
+-    # Look up the +typeclass+ DNS resource of +name+.
+-    #
+-    # +name+ must be a Gem::Resolv::DNS::Name or a String.
+-    #
+-    # +typeclass+ should be one of the following:
+-    #
+-    # * Gem::Resolv::DNS::Resource::IN::A
+-    # * Gem::Resolv::DNS::Resource::IN::AAAA
+-    # * Gem::Resolv::DNS::Resource::IN::ANY
+-    # * Gem::Resolv::DNS::Resource::IN::CNAME
+-    # * Gem::Resolv::DNS::Resource::IN::HINFO
+-    # * Gem::Resolv::DNS::Resource::IN::MINFO
+-    # * Gem::Resolv::DNS::Resource::IN::MX
+-    # * Gem::Resolv::DNS::Resource::IN::NS
+-    # * Gem::Resolv::DNS::Resource::IN::PTR
+-    # * Gem::Resolv::DNS::Resource::IN::SOA
+-    # * Gem::Resolv::DNS::Resource::IN::TXT
+-    # * Gem::Resolv::DNS::Resource::IN::WKS
+-    #
+-    # Returned resource is represented as a Gem::Resolv::DNS::Resource instance,
+-    # i.e. Gem::Resolv::DNS::Resource::IN::A.
+-
+-    def getresource(name, typeclass)
+-      each_resource(name, typeclass) {|resource| return resource}
+-      raise ResolvError.new("DNS result has no information for #{name}")
+-    end
+-
+-    ##
+-    # Looks up all +typeclass+ DNS resources for +name+.  See #getresource for
+-    # argument details.
+-
+-    def getresources(name, typeclass)
+-      ret = []
+-      each_resource(name, typeclass) {|resource| ret << resource}
+-      return ret
+-    end
+-
+-    ##
+-    # Iterates over all +typeclass+ DNS resources for +name+.  See
+-    # #getresource for argument details.
+-
+-    def each_resource(name, typeclass, &proc)
+-      fetch_resource(name, typeclass) {|reply, reply_name|
+-        extract_resources(reply, reply_name, typeclass, &proc)
+-      }
+-    end
+-
+-    # :stopdoc:
+-
+-    def fetch_resource(name, typeclass)
+-      lazy_initialize
+-      truncated = {}
+-      requesters = {}
+-      udp_requester = begin
+-        make_udp_requester
+-      rescue Errno::EACCES
+-        # fall back to TCP
+-      end
+-      senders = {}
+-
+-      begin
+-        @config.resolv(name) do |candidate, tout, nameserver, port|
+-          msg = Message.new
+-          msg.rd = 1
+-          msg.add_question(candidate, typeclass)
+-
+-          requester = requesters.fetch([nameserver, port]) do
+-            if !truncated[candidate] && udp_requester
+-              udp_requester
+-            else
+-              requesters[[nameserver, port]] = make_tcp_requester(nameserver, port)
+-            end
+-          end
+-
+-          unless sender = senders[[candidate, requester, nameserver, port]]
+-            sender = requester.sender(msg, candidate, nameserver, port)
+-            next if !sender
+-            senders[[candidate, requester, nameserver, port]] = sender
+-          end
+-          reply, reply_name = requester.request(sender, tout)
+-          case reply.rcode
+-          when RCode::NoError
+-            if reply.tc == 1 and not Requester::TCP === requester
+-              # Retry via TCP:
+-              truncated[candidate] = true
+-              redo
+-            else
+-              yield(reply, reply_name)
+-            end
+-            return
+-          when RCode::NXDomain
+-            raise Config::NXDomain.new(reply_name.to_s)
+-          else
+-            raise Config::OtherResolvError.new(reply_name.to_s)
+-          end
+-        end
+-      ensure
+-        udp_requester&.close
+-        requesters.each_value { |requester| requester&.close }
+-      end
+-    end
+-
+-    def make_udp_requester # :nodoc:
+-      nameserver_port = @config.nameserver_port
+-      if nameserver_port.length == 1
+-        Requester::ConnectedUDP.new(*nameserver_port[0])
+-      else
+-        Requester::UnconnectedUDP.new(*nameserver_port)
+-      end
+-    end
+-
+-    def make_tcp_requester(host, port) # :nodoc:
+-      return Requester::TCP.new(host, port)
+-    rescue Errno::ECONNREFUSED
+-      # Treat a refused TCP connection attempt to a nameserver like a timeout,
+-      # as Gem::Resolv::DNS::Config#resolv considers ResolvTimeout exceptions as a
+-      # hint to try the next nameserver:
+-      raise ResolvTimeout
+-    end
+-
+-    def extract_resources(msg, name, typeclass) # :nodoc:
+-      if typeclass < Resource::ANY
+-        n0 = Name.create(name)
+-        msg.each_resource {|n, ttl, data|
+-          yield data if n0 == n
+-        }
+-      end
+-      yielded = false
+-      n0 = Name.create(name)
+-      msg.each_resource {|n, ttl, data|
+-        if n0 == n
+-          case data
+-          when typeclass
+-            yield data
+-            yielded = true
+-          when Resource::CNAME
+-            n0 = data.name
+-          end
+-        end
+-      }
+-      return if yielded
+-      msg.each_resource {|n, ttl, data|
+-        if n0 == n
+-          case data
+-          when typeclass
+-            yield data
+-          end
+-        end
+-      }
+-    end
+-
+-    def self.random(arg) # :nodoc:
+-      begin
+-        Gem::SecureRandom.random_number(arg)
+-      rescue NotImplementedError
+-        rand(arg)
+-      end
+-    end
+-
+-    RequestID = {} # :nodoc:
+-    RequestIDMutex = Thread::Mutex.new # :nodoc:
+-
+-    def self.allocate_request_id(host, port) # :nodoc:
+-      id = nil
+-      RequestIDMutex.synchronize {
+-        h = (RequestID[[host, port]] ||= {})
+-        begin
+-          id = random(0x0000..0xffff)
+-        end while h[id]
+-        h[id] = true
+-      }
+-      id
+-    end
+-
+-    def self.free_request_id(host, port, id) # :nodoc:
+-      RequestIDMutex.synchronize {
+-        key = [host, port]
+-        if h = RequestID[key]
+-          h.delete id
+-          if h.empty?
+-            RequestID.delete key
+-          end
+-        end
+-      }
+-    end
+-
+-    case RUBY_PLATFORM
+-    when *[
+-      # https://www.rfc-editor.org/rfc/rfc6056.txt
+-      # Appendix A. Survey of the Algorithms in Use by Some Popular Implementations
+-      /freebsd/, /linux/, /netbsd/, /openbsd/, /solaris/,
+-      /darwin/, # the same as FreeBSD
+-    ] then
+-      def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
+-        udpsock.bind(bind_host, 0)
+-      end
+-    else
+-      # Sequential port assignment
+-      def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
+-        # Ephemeral port number range recommended by RFC 6056
+-        port = random(1024..65535)
+-        udpsock.bind(bind_host, port)
+-      rescue Errno::EADDRINUSE, # POSIX
+-             Errno::EACCES, # SunOS: See PRIV_SYS_NFS in privileges(5)
+-             Errno::EPERM # FreeBSD: security.mac.portacl.port_high is configurable.  See mac_portacl(4).
+-        retry
+-      end
+-    end
+-
+-    class Requester # :nodoc:
+-      def initialize
+-        @senders = {}
+-        @socks = nil
+-      end
+-
+-      def request(sender, tout)
+-        start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+-        timelimit = start + tout
+-        begin
+-          sender.send
+-        rescue Errno::EHOSTUNREACH, # multi-homed IPv6 may generate this
+-               Errno::ENETUNREACH
+-          raise ResolvTimeout
+-        end
+-        while true
+-          before_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+-          timeout = timelimit - before_select
+-          if timeout <= 0
+-            raise ResolvTimeout
+-          end
+-          if @socks.size == 1
+-            select_result = @socks[0].wait_readable(timeout) ? [ @socks ] : nil
+-          else
+-            select_result = IO.select(@socks, nil, nil, timeout)
+-          end
+-          if !select_result
+-            after_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+-            next if after_select < timelimit
+-            raise ResolvTimeout
+-          end
+-          begin
+-            reply, from = recv_reply(select_result[0])
+-          rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
+-                 Errno::ECONNRESET # Windows
+-            # No name server running on the server?
+-            # Don't wait anymore.
+-            raise ResolvTimeout
+-          end
+-          begin
+-            msg = Message.decode(reply)
+-          rescue DecodeError
+-            next # broken DNS message ignored
+-          end
+-          if sender == sender_for(from, msg)
+-            break
+-          else
+-            # unexpected DNS message ignored
+-          end
+-        end
+-        return msg, sender.data
+-      end
+-
+-      def sender_for(addr, msg)
+-        @senders[[addr,msg.id]]
+-      end
+-
+-      def close
+-        socks = @socks
+-        @socks = nil
+-        socks&.each(&:close)
+-      end
+-
+-      class Sender # :nodoc:
+-        def initialize(msg, data, sock)
+-          @msg = msg
+-          @data = data
+-          @sock = sock
+-        end
+-      end
+-
+-      class UnconnectedUDP < Requester # :nodoc:
+-        def initialize(*nameserver_port)
+-          super()
+-          @nameserver_port = nameserver_port
+-          @initialized = false
+-          @mutex = Thread::Mutex.new
+-        end
+-
+-        def lazy_initialize
+-          @mutex.synchronize {
+-            next if @initialized
+-            @initialized = true
+-            @socks_hash = {}
+-            @socks = []
+-            @nameserver_port.each {|host, port|
+-              if host.index(':')
+-                bind_host = "::"
+-                af = Socket::AF_INET6
+-              else
+-                bind_host = "0.0.0.0"
+-                af = Socket::AF_INET
+-              end
+-              next if @socks_hash[bind_host]
+-              begin
+-                sock = UDPSocket.new(af)
+-              rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT
+-                next # The kernel doesn't support the address family.
+-              end
+-              @socks << sock
+-              @socks_hash[bind_host] = sock
+-              sock.do_not_reverse_lookup = true
+-              DNS.bind_random_port(sock, bind_host)
+-            }
+-          }
+-          self
+-        end
+-
+-        def recv_reply(readable_socks)
+-          lazy_initialize
+-          reply, from = readable_socks[0].recvfrom(UDPSize)
+-          return reply, [from[3],from[1]]
+-        end
+-
+-        def sender(msg, data, host, port=Port)
+-          host = Addrinfo.ip(host).ip_address
+-          lazy_initialize
+-          sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
+-          return nil if !sock
+-          service = [host, port]
+-          id = DNS.allocate_request_id(host, port)
+-          request = msg.encode
+-          request[0,2] = [id].pack('n')
+-          return @senders[[service, id]] =
+-            Sender.new(request, data, sock, host, port)
+-        end
+-
+-        def close
+-          @mutex.synchronize {
+-            if @initialized
+-              super
+-              @senders.each_key {|service, id|
+-                DNS.free_request_id(service[0], service[1], id)
+-              }
+-              @initialized = false
+-            end
+-          }
+-        end
+-
+-        class Sender < Requester::Sender # :nodoc:
+-          def initialize(msg, data, sock, host, port)
+-            super(msg, data, sock)
+-            @host = host
+-            @port = port
+-          end
+-          attr_reader :data
+-
+-          def send
+-            raise "@sock is nil." if @sock.nil?
+-            @sock.send(@msg, 0, @host, @port)
+-          end
+-        end
+-      end
+-
+-      class ConnectedUDP < Requester # :nodoc:
+-        def initialize(host, port=Port)
+-          super()
+-          @host = host
+-          @port = port
+-          @mutex = Thread::Mutex.new
+-          @initialized = false
+-        end
+-
+-        def lazy_initialize
+-          @mutex.synchronize {
+-            next if @initialized
+-            @initialized = true
+-            is_ipv6 = @host.index(':')
+-            sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
+-            @socks = [sock]
+-            sock.do_not_reverse_lookup = true
+-            DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
+-            sock.connect(@host, @port)
+-          }
+-          self
+-        end
+-
+-        def recv_reply(readable_socks)
+-          lazy_initialize
+-          reply = readable_socks[0].recv(UDPSize)
+-          return reply, nil
+-        end
+-
+-        def sender(msg, data, host=@host, port=@port)
+-          lazy_initialize
+-          unless host == @host && port == @port
+-            raise RequestError.new("host/port don't match: #{host}:#{port}")
+-          end
+-          id = DNS.allocate_request_id(@host, @port)
+-          request = msg.encode
+-          request[0,2] = [id].pack('n')
+-          return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
+-        end
+-
+-        def close
+-          @mutex.synchronize do
+-            if @initialized
+-              super
+-              @senders.each_key {|from, id|
+-                DNS.free_request_id(@host, @port, id)
+-              }
+-              @initialized = false
+-            end
+-          end
+-        end
+-
+-        class Sender < Requester::Sender # :nodoc:
+-          def send
+-            raise "@sock is nil." if @sock.nil?
+-            @sock.send(@msg, 0)
+-          end
+-          attr_reader :data
+-        end
+-      end
+-
+-      class MDNSOneShot < UnconnectedUDP # :nodoc:
+-        def sender(msg, data, host, port=Port)
+-          lazy_initialize
+-          id = DNS.allocate_request_id(host, port)
+-          request = msg.encode
+-          request[0,2] = [id].pack('n')
+-          sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
+-          return @senders[id] =
+-            UnconnectedUDP::Sender.new(request, data, sock, host, port)
+-        end
+-
+-        def sender_for(addr, msg)
+-          lazy_initialize
+-          @senders[msg.id]
+-        end
+-      end
+-
+-      class TCP < Requester # :nodoc:
+-        def initialize(host, port=Port)
+-          super()
+-          @host = host
+-          @port = port
+-          sock = TCPSocket.new(@host, @port)
+-          @socks = [sock]
+-          @senders = {}
+-        end
+-
+-        def recv_reply(readable_socks)
+-          len = readable_socks[0].read(2).unpack('n')[0]
+-          reply = @socks[0].read(len)
+-          return reply, nil
+-        end
+-
+-        def sender(msg, data, host=@host, port=@port)
+-          unless host == @host && port == @port
+-            raise RequestError.new("host/port don't match: #{host}:#{port}")
+-          end
+-          id = DNS.allocate_request_id(@host, @port)
+-          request = msg.encode
+-          request[0,2] = [request.length, id].pack('nn')
+-          return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
+-        end
+-
+-        class Sender < Requester::Sender # :nodoc:
+-          def send
+-            @sock.print(@msg)
+-            @sock.flush
+-          end
+-          attr_reader :data
+-        end
+-
+-        def close
+-          super
+-          @senders.each_key {|from,id|
+-            DNS.free_request_id(@host, @port, id)
+-          }
+-        end
+-      end
+-
+-      ##
+-      # Indicates a problem with the DNS request.
+-
+-      class RequestError < StandardError
+-      end
+-    end
+-
+-    class Config # :nodoc:
+-      def initialize(config_info=nil)
+-        @mutex = Thread::Mutex.new
+-        @config_info = config_info
+-        @initialized = nil
+-        @timeouts = nil
+-      end
+-
+-      def timeouts=(values)
+-        if values
+-          values = Array(values)
+-          values.each do |t|
+-            Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric"
+-            t > 0.0 or raise ArgumentError, "timeout=#{t} must be positive"
+-          end
+-          @timeouts = values
+-        else
+-          @timeouts = nil
+-        end
+-      end
+-
+-      def Config.parse_resolv_conf(filename)
+-        nameserver = []
+-        search = nil
+-        ndots = 1
+-        File.open(filename, 'rb') {|f|
+-          f.each {|line|
+-            line.sub!(/[#;].*/, '')
+-            keyword, *args = line.split(/\s+/)
+-            next unless keyword
+-            case keyword
+-            when 'nameserver'
+-              nameserver.concat(args.each(&:freeze))
+-            when 'domain'
+-              next if args.empty?
+-              search = [args[0].freeze]
+-            when 'search'
+-              next if args.empty?
+-              search = args.each(&:freeze)
+-            when 'options'
+-              args.each {|arg|
+-                case arg
+-                when /\Andots:(\d+)\z/
+-                  ndots = $1.to_i
+-                end
+-              }
+-            end
+-          }
+-        }
+-        return { :nameserver => nameserver.freeze, :search => search.freeze, :ndots => ndots.freeze }.freeze
+-      end
+-
+-      def Config.default_config_hash(filename="/etc/resolv.conf")
+-        if File.exist? filename
+-          Config.parse_resolv_conf(filename)
+-        elsif defined?(Win32::Resolv)
+-          search, nameserver = Win32::Resolv.get_resolv_info
+-          config_hash = {}
+-          config_hash[:nameserver] = nameserver if nameserver
+-          config_hash[:search] = [search].flatten if search
+-          config_hash
+-        else
+-          {}
+-        end
+-      end
+-
+-      def lazy_initialize
+-        @mutex.synchronize {
+-          unless @initialized
+-            @nameserver_port = []
+-            @use_ipv6 = nil
+-            @search = nil
+-            @ndots = 1
+-            case @config_info
+-            when nil
+-              config_hash = Config.default_config_hash
+-            when String
+-              config_hash = Config.parse_resolv_conf(@config_info)
+-            when Hash
+-              config_hash = @config_info.dup
+-              if String === config_hash[:nameserver]
+-                config_hash[:nameserver] = [config_hash[:nameserver]]
+-              end
+-              if String === config_hash[:search]
+-                config_hash[:search] = [config_hash[:search]]
+-              end
+-            else
+-              raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
+-            end
+-            if config_hash.include? :nameserver
+-              @nameserver_port = config_hash[:nameserver].map {|ns| [ns, Port] }
+-            end
+-            if config_hash.include? :nameserver_port
+-              @nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
+-            end
+-            if config_hash.include? :use_ipv6
+-              @use_ipv6 = config_hash[:use_ipv6]
+-            end
+-            @search = config_hash[:search] if config_hash.include? :search
+-            @ndots = config_hash[:ndots] if config_hash.include? :ndots
+-            @raise_timeout_errors = config_hash[:raise_timeout_errors]
+-
+-            if @nameserver_port.empty?
+-              @nameserver_port << ['0.0.0.0', Port]
+-            end
+-            if @search
+-              @search = @search.map {|arg| Label.split(arg) }
+-            else
+-              hostname = Socket.gethostname
+-              if /\./ =~ hostname
+-                @search = [Label.split($')]
+-              else
+-                @search = [[]]
+-              end
+-            end
+-
+-            if !@nameserver_port.kind_of?(Array) ||
+-               @nameserver_port.any? {|ns_port|
+-                  !(Array === ns_port) ||
+-                  ns_port.length != 2
+-                  !(String === ns_port[0]) ||
+-                  !(Integer === ns_port[1])
+-               }
+-              raise ArgumentError.new("invalid nameserver config: #{@nameserver_port.inspect}")
+-            end
+-
+-            if !@search.kind_of?(Array) ||
+-               !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
+-              raise ArgumentError.new("invalid search config: #{@search.inspect}")
+-            end
+-
+-            if !@ndots.kind_of?(Integer)
+-              raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
+-            end
+-
+-            @initialized = true
+-          end
+-        }
+-        self
+-      end
+-
+-      def single?
+-        lazy_initialize
+-        if @nameserver_port.length == 1
+-          return @nameserver_port[0]
+-        else
+-          return nil
+-        end
+-      end
+-
+-      def nameserver_port
+-        @nameserver_port
+-      end
+-
+-      def use_ipv6?
+-        @use_ipv6
+-      end
+-
+-      def generate_candidates(name)
+-        candidates = nil
+-        name = Name.create(name)
+-        if name.absolute?
+-          candidates = [name]
+-        else
+-          if @ndots <= name.length - 1
+-            candidates = [Name.new(name.to_a)]
+-          else
+-            candidates = []
+-          end
+-          candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
+-          fname = Name.create("#{name}.")
+-          if !candidates.include?(fname)
+-            candidates << fname
+-          end
+-        end
+-        return candidates
+-      end
+-
+-      InitialTimeout = 5
+-
+-      def generate_timeouts
+-        ts = [InitialTimeout]
+-        ts << ts[-1] * 2 / @nameserver_port.length
+-        ts << ts[-1] * 2
+-        ts << ts[-1] * 2
+-        return ts
+-      end
+-
+-      def resolv(name)
+-        candidates = generate_candidates(name)
+-        timeouts = @timeouts || generate_timeouts
+-        timeout_error = false
+-        begin
+-          candidates.each {|candidate|
+-            begin
+-              timeouts.each {|tout|
+-                @nameserver_port.each {|nameserver, port|
+-                  begin
+-                    yield candidate, tout, nameserver, port
+-                  rescue ResolvTimeout
+-                  end
+-                }
+-              }
+-              timeout_error = true
+-              raise ResolvError.new("DNS resolv timeout: #{name}")
+-            rescue NXDomain
+-            end
+-          }
+-        rescue ResolvError
+-          raise if @raise_timeout_errors && timeout_error
+-        end
+-      end
+-
+-      ##
+-      # Indicates no such domain was found.
+-
+-      class NXDomain < ResolvError
+-      end
+-
+-      ##
+-      # Indicates some other unhandled resolver error was encountered.
+-
+-      class OtherResolvError < ResolvError
+-      end
+-    end
+-
+-    module OpCode # :nodoc:
+-      Query = 0
+-      IQuery = 1
+-      Status = 2
+-      Notify = 4
+-      Update = 5
+-    end
+-
+-    module RCode # :nodoc:
+-      NoError = 0
+-      FormErr = 1
+-      ServFail = 2
+-      NXDomain = 3
+-      NotImp = 4
+-      Refused = 5
+-      YXDomain = 6
+-      YXRRSet = 7
+-      NXRRSet = 8
+-      NotAuth = 9
+-      NotZone = 10
+-      BADVERS = 16
+-      BADSIG = 16
+-      BADKEY = 17
+-      BADTIME = 18
+-      BADMODE = 19
+-      BADNAME = 20
+-      BADALG = 21
+-    end
+-
+-    ##
+-    # Indicates that the DNS response was unable to be decoded.
+-
+-    class DecodeError < StandardError
+-    end
+-
+-    ##
+-    # Indicates that the DNS request was unable to be encoded.
+-
+-    class EncodeError < StandardError
+-    end
+-
+-    module Label # :nodoc:
+-      def self.split(arg)
+-        labels = []
+-        arg.scan(/[^\.]+/) {labels << Str.new($&)}
+-        return labels
+-      end
+-
+-      class Str # :nodoc:
+-        def initialize(string)
+-          @string = string
+-          # case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
+-          # This assumes @string is given in ASCII compatible encoding.
+-          @downcase = string.b.downcase
+-        end
+-        attr_reader :string, :downcase
+-
+-        def to_s
+-          return @string
+-        end
+-
+-        def inspect
+-          return "#<#{self.class} #{self}>"
+-        end
+-
+-        def ==(other)
+-          return self.class == other.class && @downcase == other.downcase
+-        end
+-
+-        def eql?(other)
+-          return self == other
+-        end
+-
+-        def hash
+-          return @downcase.hash
+-        end
+-      end
+-    end
+-
+-    ##
+-    # A representation of a DNS name.
+-
+-    class Name
+-
+-      ##
+-      # Creates a new DNS name from +arg+.  +arg+ can be:
+-      #
+-      # Name:: returns +arg+.
+-      # String:: Creates a new Name.
+-
+-      def self.create(arg)
+-        case arg
+-        when Name
+-          return arg
+-        when String
+-          return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
+-        else
+-          raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
+-        end
+-      end
+-
+-      def initialize(labels, absolute=true) # :nodoc:
+-        labels = labels.map {|label|
+-          case label
+-          when String then Label::Str.new(label)
+-          when Label::Str then label
+-          else
+-            raise ArgumentError, "unexpected label: #{label.inspect}"
+-          end
+-        }
+-        @labels = labels
+-        @absolute = absolute
+-      end
+-
+-      def inspect # :nodoc:
+-        "#<#{self.class}: #{self}#{@absolute ? '.' : ''}>"
+-      end
+-
+-      ##
+-      # True if this name is absolute.
+-
+-      def absolute?
+-        return @absolute
+-      end
+-
+-      def ==(other) # :nodoc:
+-        return false unless Name === other
+-        return false unless @absolute == other.absolute?
+-        return @labels == other.to_a
+-      end
+-
+-      alias eql? == # :nodoc:
+-
+-      ##
+-      # Returns true if +other+ is a subdomain.
+-      #
+-      # Example:
+-      #
+-      #   domain = Gem::Resolv::DNS::Name.create("y.z")
+-      #   p Gem::Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
+-      #   p Gem::Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
+-      #   p Gem::Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
+-      #   p Gem::Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
+-      #   p Gem::Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
+-      #   p Gem::Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
+-      #
+-
+-      def subdomain_of?(other)
+-        raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
+-        return false if @absolute != other.absolute?
+-        other_len = other.length
+-        return false if @labels.length <= other_len
+-        return @labels[-other_len, other_len] == other.to_a
+-      end
+-
+-      def hash # :nodoc:
+-        return @labels.hash ^ @absolute.hash
+-      end
+-
+-      def to_a # :nodoc:
+-        return @labels
+-      end
+-
+-      def length # :nodoc:
+-        return @labels.length
+-      end
+-
+-      def [](i) # :nodoc:
+-        return @labels[i]
+-      end
+-
+-      ##
+-      # returns the domain name as a string.
+-      #
+-      # The domain name doesn't have a trailing dot even if the name object is
+-      # absolute.
+-      #
+-      # Example:
+-      #
+-      #   p Gem::Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
+-      #   p Gem::Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
+-
+-      def to_s
+-        return @labels.join('.')
+-      end
+-    end
+-
+-    class Message # :nodoc:
+-      @@identifier = -1
+-
+-      def initialize(id = (@@identifier += 1) & 0xffff)
+-        @id = id
+-        @qr = 0
+-        @opcode = 0
+-        @aa = 0
+-        @tc = 0
+-        @rd = 0 # recursion desired
+-        @ra = 0 # recursion available
+-        @rcode = 0
+-        @question = []
+-        @answer = []
+-        @authority = []
+-        @additional = []
+-      end
+-
+-      attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
+-      attr_reader :question, :answer, :authority, :additional
+-
+-      def ==(other)
+-        return @id == other.id &&
+-               @qr == other.qr &&
+-               @opcode == other.opcode &&
+-               @aa == other.aa &&
+-               @tc == other.tc &&
+-               @rd == other.rd &&
+-               @ra == other.ra &&
+-               @rcode == other.rcode &&
+-               @question == other.question &&
+-               @answer == other.answer &&
+-               @authority == other.authority &&
+-               @additional == other.additional
+-      end
+-
+-      def add_question(name, typeclass)
+-        @question << [Name.create(name), typeclass]
+-      end
+-
+-      def each_question
+-        @question.each {|name, typeclass|
+-          yield name, typeclass
+-        }
+-      end
+-
+-      def add_answer(name, ttl, data)
+-        @answer << [Name.create(name), ttl, data]
+-      end
+-
+-      def each_answer
+-        @answer.each {|name, ttl, data|
+-          yield name, ttl, data
+-        }
+-      end
+-
+-      def add_authority(name, ttl, data)
+-        @authority << [Name.create(name), ttl, data]
+-      end
+-
+-      def each_authority
+-        @authority.each {|name, ttl, data|
+-          yield name, ttl, data
+-        }
+-      end
+-
+-      def add_additional(name, ttl, data)
+-        @additional << [Name.create(name), ttl, data]
+-      end
+-
+-      def each_additional
+-        @additional.each {|name, ttl, data|
+-          yield name, ttl, data
+-        }
+-      end
+-
+-      def each_resource
+-        each_answer {|name, ttl, data| yield name, ttl, data}
+-        each_authority {|name, ttl, data| yield name, ttl, data}
+-        each_additional {|name, ttl, data| yield name, ttl, data}
+-      end
+-
+-      def encode
+-        return MessageEncoder.new {|msg|
+-          msg.put_pack('nnnnnn',
+-            @id,
+-            (@qr & 1) << 15 |
+-            (@opcode & 15) << 11 |
+-            (@aa & 1) << 10 |
+-            (@tc & 1) << 9 |
+-            (@rd & 1) << 8 |
+-            (@ra & 1) << 7 |
+-            (@rcode & 15),
+-            @question.length,
+-            @answer.length,
+-            @authority.length,
+-            @additional.length)
+-          @question.each {|q|
+-            name, typeclass = q
+-            msg.put_name(name)
+-            msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
+-          }
+-          [@answer, @authority, @additional].each {|rr|
+-            rr.each {|r|
+-              name, ttl, data = r
+-              msg.put_name(name)
+-              msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
+-              msg.put_length16 {data.encode_rdata(msg)}
+-            }
+-          }
+-        }.to_s
+-      end
+-
+-      class MessageEncoder # :nodoc:
+-        def initialize
+-          @data = ''.dup
+-          @names = {}
+-          yield self
+-        end
+-
+-        def to_s
+-          return @data
+-        end
+-
+-        def put_bytes(d)
+-          @data << d
+-        end
+-
+-        def put_pack(template, *d)
+-          @data << d.pack(template)
+-        end
+-
+-        def put_length16
+-          length_index = @data.length
+-          @data << "\0\0"
+-          data_start = @data.length
+-          yield
+-          data_end = @data.length
+-          @data[length_index, 2] = [data_end - data_start].pack("n")
+-        end
+-
+-        def put_string(d)
+-          self.put_pack("C", d.length)
+-          @data << d
+-        end
+-
+-        def put_string_list(ds)
+-          ds.each {|d|
+-            self.put_string(d)
+-          }
+-        end
+-
+-        def put_name(d, compress: true)
+-          put_labels(d.to_a, compress: compress)
+-        end
+-
+-        def put_labels(d, compress: true)
+-          d.each_index {|i|
+-            domain = d[i..-1]
+-            if compress && idx = @names[domain]
+-              self.put_pack("n", 0xc000 | idx)
+-              return
+-            else
+-              if @data.length < 0x4000
+-                @names[domain] = @data.length
+-              end
+-              self.put_label(d[i])
+-            end
+-          }
+-          @data << "\0"
+-        end
+-
+-        def put_label(d)
+-          self.put_string(d.to_s)
+-        end
+-      end
+-
+-      def Message.decode(m)
+-        o = Message.new(0)
+-        MessageDecoder.new(m) {|msg|
+-          id, flag, qdcount, ancount, nscount, arcount =
+-            msg.get_unpack('nnnnnn')
+-          o.id = id
+-          o.tc = (flag >> 9) & 1
+-          o.rcode = flag & 15
+-          return o unless o.tc.zero?
+-
+-          o.qr = (flag >> 15) & 1
+-          o.opcode = (flag >> 11) & 15
+-          o.aa = (flag >> 10) & 1
+-          o.rd = (flag >> 8) & 1
+-          o.ra = (flag >> 7) & 1
+-          (1..qdcount).each {
+-            name, typeclass = msg.get_question
+-            o.add_question(name, typeclass)
+-          }
+-          (1..ancount).each {
+-            name, ttl, data = msg.get_rr
+-            o.add_answer(name, ttl, data)
+-          }
+-          (1..nscount).each {
+-            name, ttl, data = msg.get_rr
+-            o.add_authority(name, ttl, data)
+-          }
+-          (1..arcount).each {
+-            name, ttl, data = msg.get_rr
+-            o.add_additional(name, ttl, data)
+-          }
+-        }
+-        return o
+-      end
+-
+-      class MessageDecoder # :nodoc:
+-        def initialize(data)
+-          @data = data
+-          @index = 0
+-          @limit = data.bytesize
+-          yield self
+-        end
+-
+-        def inspect
+-          "\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
+-        end
+-
+-        def get_length16
+-          len, = self.get_unpack('n')
+-          save_limit = @limit
+-          @limit = @index + len
+-          d = yield(len)
+-          if @index < @limit
+-            raise DecodeError.new("junk exists")
+-          elsif @limit < @index
+-            raise DecodeError.new("limit exceeded")
+-          end
+-          @limit = save_limit
+-          return d
+-        end
+-
+-        def get_bytes(len = @limit - @index)
+-          raise DecodeError.new("limit exceeded") if @limit < @index + len
+-          d = @data.byteslice(@index, len)
+-          @index += len
+-          return d
+-        end
+-
+-        def get_unpack(template)
+-          len = 0
+-          template.each_byte {|byte|
+-            byte = "%c" % byte
+-            case byte
+-            when ?c, ?C
+-              len += 1
+-            when ?n
+-              len += 2
+-            when ?N
+-              len += 4
+-            else
+-              raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
+-            end
+-          }
+-          raise DecodeError.new("limit exceeded") if @limit < @index + len
+-          arr = @data.unpack("@#{@index}#{template}")
+-          @index += len
+-          return arr
+-        end
+-
+-        def get_string
+-          raise DecodeError.new("limit exceeded") if @limit <= @index
+-          len = @data.getbyte(@index)
+-          raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
+-          d = @data.byteslice(@index + 1, len)
+-          @index += 1 + len
+-          return d
+-        end
+-
+-        def get_string_list
+-          strings = []
+-          while @index < @limit
+-            strings << self.get_string
+-          end
+-          strings
+-        end
+-
+-        def get_list
+-          [].tap do |values|
+-            while @index < @limit
+-              values << yield
+-            end
+-          end
+-        end
+-
+-        def get_name
+-          return Name.new(self.get_labels)
+-        end
+-
+-        def get_labels
+-          prev_index = @index
+-          save_index = nil
+-          d = []
+-          size = -1
+-          while true
+-            raise DecodeError.new("limit exceeded") if @limit <= @index
+-            case @data.getbyte(@index)
+-            when 0
+-              @index += 1
+-              if save_index
+-                @index = save_index
+-              end
+-              return d
+-            when 192..255
+-              idx = self.get_unpack('n')[0] & 0x3fff
+-              if prev_index <= idx
+-                raise DecodeError.new("non-backward name pointer")
+-              end
+-              prev_index = idx
+-              if !save_index
+-                save_index = @index
+-              end
+-              @index = idx
+-            else
+-              l = self.get_label
+-              d << l
+-              size += 1 + l.string.bytesize
+-              raise DecodeError.new("name label data exceed 255 octets") if size > 255
+-            end
+-          end
+-        end
+-
+-        def get_label
+-          return Label::Str.new(self.get_string)
+-        end
+-
+-        def get_question
+-          name = self.get_name
+-          type, klass = self.get_unpack("nn")
+-          return name, Resource.get_class(type, klass)
+-        end
+-
+-        def get_rr
+-          name = self.get_name
+-          type, klass, ttl = self.get_unpack('nnN')
+-          typeclass = Resource.get_class(type, klass)
+-          res = self.get_length16 do
+-            begin
+-              typeclass.decode_rdata self
+-            rescue => e
+-              raise DecodeError, e.message, e.backtrace
+-            end
+-          end
+-          res.instance_variable_set :@ttl, ttl
+-          return name, ttl, res
+-        end
+-      end
+-    end
+-
+-    ##
+-    # SvcParams for service binding RRs. [RFC9460]
+-
+-    class SvcParams
+-      include Enumerable
+-
+-      ##
+-      # Create a list of SvcParams with the given initial content.
+-      #
+-      # +params+ has to be an enumerable of +SvcParam+s.
+-      # If its content has +SvcParam+s with the duplicate key,
+-      # the one appears last takes precedence.
+-
+-      def initialize(params = [])
+-        @params = {}
+-
+-        params.each do |param|
+-          add param
+-        end
+-      end
+-
+-      ##
+-      # Get SvcParam for the given +key+ in this list.
+-
+-      def [](key)
+-        @params[canonical_key(key)]
+-      end
+-
+-      ##
+-      # Get the number of SvcParams in this list.
+-
+-      def count
+-        @params.count
+-      end
+-
+-      ##
+-      # Get whether this list is empty.
+-
+-      def empty?
+-        @params.empty?
+-      end
+-
+-      ##
+-      # Add the SvcParam +param+ to this list, overwriting the existing one with the same key.
+-
+-      def add(param)
+-        @params[param.class.key_number] = param
+-      end
+-
+-      ##
+-      # Remove the +SvcParam+ with the given +key+ and return it.
+-
+-      def delete(key)
+-        @params.delete(canonical_key(key))
+-      end
+-
+-      ##
+-      # Enumerate the +SvcParam+s in this list.
+-
+-      def each(&block)
+-        return enum_for(:each) unless block
+-        @params.each_value(&block)
+-      end
+-
+-      def encode(msg) # :nodoc:
+-        @params.keys.sort.each do |key|
+-          msg.put_pack('n', key)
+-          msg.put_length16 do
+-            @params.fetch(key).encode(msg)
+-          end
+-        end
+-      end
+-
+-      def self.decode(msg) # :nodoc:
+-        params = msg.get_list do
+-          key, = msg.get_unpack('n')
+-          msg.get_length16 do
+-            SvcParam::ClassHash[key].decode(msg)
+-          end
+-        end
+-
+-        return self.new(params)
+-      end
+-
+-      private
+-
+-      def canonical_key(key) # :nodoc:
+-        case key
+-        when Integer
+-          key
+-        when /\Akey(\d+)\z/
+-          Integer($1)
+-        when Symbol
+-          SvcParam::ClassHash[key].key_number
+-        else
+-          raise TypeError, 'key must be either String or Symbol'
+-        end
+-      end
+-    end
+-
+-    ##
+-    # Base class for SvcParam. [RFC9460]
+-
+-    class SvcParam
+-
+-      ##
+-      # Get the presentation name of the SvcParamKey.
+-
+-      def self.key_name
+-        const_get(:KeyName)
+-      end
+-
+-      ##
+-      # Get the registered number of the SvcParamKey.
+-
+-      def self.key_number
+-        const_get(:KeyNumber)
+-      end
+-
+-      ClassHash = Hash.new do |h, key| # :nodoc:
+-        case key
+-        when Integer
+-          Generic.create(key)
+-        when /\Akey(?<key>\d+)\z/
+-          Generic.create(key.to_int)
+-        when Symbol
+-          raise KeyError, "unknown key #{key}"
+-        else
+-          raise TypeError, 'key must be either String or Symbol'
+-        end
+-      end
+-
+-      ##
+-      # Generic SvcParam abstract class.
+-
+-      class Generic < SvcParam
+-
+-        ##
+-        # SvcParamValue in wire-format byte string.
+-
+-        attr_reader :value
+-
+-        ##
+-        # Create generic SvcParam
+-
+-        def initialize(value)
+-          @value = value
+-        end
+-
+-        def encode(msg) # :nodoc:
+-          msg.put_bytes(@value)
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          return self.new(msg.get_bytes)
+-        end
+-
+-        def self.create(key_number)
+-          c = Class.new(Generic)
+-          key_name = :"key#{key_number}"
+-          c.const_set(:KeyName, key_name)
+-          c.const_set(:KeyNumber, key_number)
+-          self.const_set(:"Key#{key_number}", c)
+-          ClassHash[key_name] = ClassHash[key_number] = c
+-          return c
+-        end
+-      end
+-
+-      ##
+-      # "mandatory" SvcParam -- Mandatory keys in service binding RR
+-
+-      class Mandatory < SvcParam
+-        KeyName = :mandatory
+-        KeyNumber = 0
+-        ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+-
+-        ##
+-        # Mandatory keys.
+-
+-        attr_reader :keys
+-
+-        ##
+-        # Initialize "mandatory" ScvParam.
+-
+-        def initialize(keys)
+-          @keys = keys.map(&:to_int)
+-        end
+-
+-        def encode(msg) # :nodoc:
+-          @keys.sort.each do |key|
+-            msg.put_pack('n', key)
+-          end
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          keys = msg.get_list { msg.get_unpack('n')[0] }
+-          return self.new(keys)
+-        end
+-      end
+-
+-      ##
+-      # "alpn" SvcParam -- Additional supported protocols
+-
+-      class ALPN < SvcParam
+-        KeyName = :alpn
+-        KeyNumber = 1
+-        ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+-
+-        ##
+-        # Supported protocol IDs.
+-
+-        attr_reader :protocol_ids
+-
+-        ##
+-        # Initialize "alpn" ScvParam.
+-
+-        def initialize(protocol_ids)
+-          @protocol_ids = protocol_ids.map(&:to_str)
+-        end
+-
+-        def encode(msg) # :nodoc:
+-          msg.put_string_list(@protocol_ids)
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          return self.new(msg.get_string_list)
+-        end
+-      end
+-
+-      ##
+-      # "no-default-alpn" SvcParam -- No support for default protocol
+-
+-      class NoDefaultALPN < SvcParam
+-        KeyName = :'no-default-alpn'
+-        KeyNumber = 2
+-        ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+-
+-        def encode(msg) # :nodoc:
+-          # no payload
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          return self.new
+-        end
+-      end
+-
+-      ##
+-      # "port" SvcParam -- Port for alternative endpoint
+-
+-      class Port < SvcParam
+-        KeyName = :port
+-        KeyNumber = 3
+-        ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+-
+-        ##
+-        # Port number.
+-
+-        attr_reader :port
+-
+-        ##
+-        # Initialize "port" ScvParam.
+-
+-        def initialize(port)
+-          @port = port.to_int
+-        end
+-
+-        def encode(msg) # :nodoc:
+-          msg.put_pack('n', @port)
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          port, = msg.get_unpack('n')
+-          return self.new(port)
+-        end
+-      end
+-
+-      ##
+-      # "ipv4hint" SvcParam -- IPv4 address hints
+-
+-      class IPv4Hint < SvcParam
+-        KeyName = :ipv4hint
+-        KeyNumber = 4
+-        ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+-
+-        ##
+-        # Set of IPv4 addresses.
+-
+-        attr_reader :addresses
+-
+-        ##
+-        # Initialize "ipv4hint" ScvParam.
+-
+-        def initialize(addresses)
+-          @addresses = addresses.map {|address| IPv4.create(address) }
+-        end
+-
+-        def encode(msg) # :nodoc:
+-          @addresses.each do |address|
+-            msg.put_bytes(address.address)
+-          end
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) }
+-          return self.new(addresses)
+-        end
+-      end
+-
+-      ##
+-      # "ipv6hint" SvcParam -- IPv6 address hints
+-
+-      class IPv6Hint < SvcParam
+-        KeyName = :ipv6hint
+-        KeyNumber = 6
+-        ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+-
+-        ##
+-        # Set of IPv6 addresses.
+-
+-        attr_reader :addresses
+-
+-        ##
+-        # Initialize "ipv6hint" ScvParam.
+-
+-        def initialize(addresses)
+-          @addresses = addresses.map {|address| IPv6.create(address) }
+-        end
+-
+-        def encode(msg) # :nodoc:
+-          @addresses.each do |address|
+-            msg.put_bytes(address.address)
+-          end
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) }
+-          return self.new(addresses)
+-        end
+-      end
+-
+-      ##
+-      # "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461]
+-
+-      class DoHPath < SvcParam
+-        KeyName = :dohpath
+-        KeyNumber = 7
+-        ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
+-
+-        ##
+-        # URI template for DoH queries.
+-
+-        attr_reader :template
+-
+-        ##
+-        # Initialize "dohpath" ScvParam.
+-
+-        def initialize(template)
+-          @template = template.encode('utf-8')
+-        end
+-
+-        def encode(msg) # :nodoc:
+-          msg.put_bytes(@template)
+-        end
+-
+-        def self.decode(msg) # :nodoc:
+-          template = msg.get_bytes.force_encoding('utf-8')
+-          return self.new(template)
+-        end
+-      end
+-    end
+-
+-    ##
+-    # A DNS query abstract class.
+-
+-    class Query
+-      def encode_rdata(msg) # :nodoc:
+-        raise EncodeError.new("#{self.class} is query.")
+-      end
+-
+-      def self.decode_rdata(msg) # :nodoc:
+-        raise DecodeError.new("#{self.class} is query.")
+-      end
+-    end
+-
+-    ##
+-    # A DNS resource abstract class.
+-
+-    class Resource < Query
+-
+-      ##
+-      # Remaining Time To Live for this Resource.
+-
+-      attr_reader :ttl
+-
+-      ClassHash = Module.new do
+-        module_function
+-
+-        def []=(type_class_value, klass)
+-          type_value, class_value = type_class_value
+-          Resource.const_set(:"Type#{type_value}_Class#{class_value}", klass)
+-        end
+-      end
+-
+-      def encode_rdata(msg) # :nodoc:
+-        raise NotImplementedError.new
+-      end
+-
+-      def self.decode_rdata(msg) # :nodoc:
+-        raise NotImplementedError.new
+-      end
+-
+-      def ==(other) # :nodoc:
+-        return false unless self.class == other.class
+-        s_ivars = self.instance_variables
+-        s_ivars.sort!
+-        s_ivars.delete :@ttl
+-        o_ivars = other.instance_variables
+-        o_ivars.sort!
+-        o_ivars.delete :@ttl
<Skipped 1358 lines>
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/ruby.git/commitdiff/a2d943d7d36f59ca00a640f2dd0970b49e0f3165



More information about the pld-cvs-commit mailing list