class GitHubPages::HealthCheck::Domain
Constants
- CURRENT_IPV6_ADDRESSES
- CURRENT_IP_ADDRESSES
- CURRENT_IP_ADDRESSES_ALL
- HASH_METHODS
- LEGACY_IP_ADDRESSES
- REQUESTED_RECORD_TYPES
Attributes
Public Class Methods
Source
# File lib/github-pages-health-check/domain.rb, line 107 def initialize(host, nameservers: :default) unless host.is_a? String raise ArgumentError, "Expected string, got #{host.class}" end @host = normalize_host(host) @nameservers = nameservers @resolver = GitHubPages::HealthCheck::Resolver.new(self.host, :nameservers => nameservers) end
Source
# File lib/github-pages-health-check/domain.rb, line 103 def self.redundant(host) GitHubPages::HealthCheck::RedundantCheck.new(host).check end
Public Instance Methods
Source
# File lib/github-pages-health-check/domain.rb, line 373 def a_record? return @is_a_record if defined?(@is_a_record) return unless dns? @is_a_record = Dnsruby::Types::A == dns.first.type end
Is this domain’s first response an A record?
Source
# File lib/github-pages-health-check/domain.rb, line 389 def a_record_present? return unless dns? dns.any? { |answer| answer.type == Dnsruby::Types::A && answer.name.to_s == host } end
Does this domain has an A record setup (not necessarily as the first record)?
Source
# File lib/github-pages-health-check/domain.rb, line 381 def aaaa_record? return @is_aaaa_record if defined?(@is_aaaa_record) return unless dns? @is_aaaa_record = Dnsruby::Types::AAAA == dns.first.type end
Is this domain’s first response an AAAA record?
Source
# File lib/github-pages-health-check/domain.rb, line 396 def aaaa_record_present? return unless dns? dns.any? { |answer| answer.type == Dnsruby::Types::AAAA && answer.name.to_s == host } end
Does this domain has an AAAA record setup (not necessarily as the first record)?
Source
# File lib/github-pages-health-check/domain.rb, line 176 def apex_domain? return @apex_domain if defined?(@apex_domain) return false unless valid_domain? return true if dns_zone_soa? && dns_zone_ns? # PublicSuffix.domain pulls out the apex-level domain name. # E.g. PublicSuffix.domain("techblog.netflix.com") # => "netflix.com" # It's aware of multi-step top-level domain names: # E.g. PublicSuffix.domain("blog.digital.gov.uk") # => "digital.gov.uk" # For apex-level domain names, DNS providers do not support CNAME records. unicode_host = Addressable::IDNA.to_unicode(host) PublicSuffix.domain(unicode_host, :default_rule => nil, :ignore_private => true) == unicode_host end
Is this domain an apex domain, meaning a CNAME would be inappropriate
Source
# File lib/github-pages-health-check/domain.rb, line 522 def caa_error return nil unless caa&.errored? caa.error.class.name end
Any errors querying CAA
records
Source
# File lib/github-pages-health-check/domain.rb, line 120 def check! raise Errors::InvalidDomainError.new :domain => self unless valid_domain? raise Errors::InvalidDNSError.new :domain => self unless dns_resolves? raise Errors::DeprecatedIPError.new :domain => self if deprecated_ip? return true if proxied? raise Errors::InvalidARecordError.new :domain => self if invalid_a_record? raise Errors::InvalidCNAMEError.new :domain => self if invalid_cname? raise Errors::InvalidAAAARecordError.new :domain => self if invalid_aaaa_record? raise Errors::NotServedByPagesError.new :domain => self unless served_by_pages? true end
Runs all checks, raises an error if invalid rubocop:disable Metrics/AbcSize
Source
# File lib/github-pages-health-check/domain.rb, line 301 def cloudflare_ip? cdn_ip?(CloudFlare) end
Does the domain resolve to a CloudFlare-owned IP
Source
# File lib/github-pages-health-check/domain.rb, line 413 def cname return unless dns? cnames = dns.take_while { |answer| answer.type == Dnsruby::Types::CNAME } return if cnames.empty? www_cname(cnames.last) @cname ||= Domain.new(cnames.last.cname.to_s) end
The domain to which this domain’s CNAME resolves Returns nil if the domain is not a CNAME
Source
# File lib/github-pages-health-check/domain.rb, line 403 def cname_record? return unless dns? return false unless cname cname.valid_domain? end
Is this domain’s first response a CNAME record?
Source
# File lib/github-pages-health-check/domain.rb, line 251 def cname_to_domain_to_pages? return false unless dns? a_record_to_pages = dns.select { |d| d.type == Dnsruby::Types::A && d.name.to_s == host }.first return false unless a_record_to_pages && cname? && !cname_to_pages_dot_github_dot_com? && @www_cname CURRENT_IP_ADDRESSES.include?(a_record_to_pages.address.to_s.downcase) end
Check if the CNAME points to a Domain
that points to pages e.g. CNAME -> Domain
-> Pages rubocop:disable Metrics/AbcSize
Source
# File lib/github-pages-health-check/domain.rb, line 271 def cname_to_fastly? cname? && !pages_domain? && cname.fastly? end
Is the given domain CNAME’d directly to our Fastly
account?
Source
# File lib/github-pages-health-check/domain.rb, line 244 def cname_to_github_user_domain? cname? && !cname_to_pages_dot_github_dot_com? && cname.pages_domain? end
Is the domain’s first response a CNAME to a pages domain?
Source
# File lib/github-pages-health-check/domain.rb, line 266 def cname_to_pages_dot_github_dot_com? cname? && cname.pages_dot_github_dot_com? end
Is the given domain a CNAME to pages.github.(io|com) instead of being CNAME’d to the user’s subdomain?
domain - the domain to check, generally the target of a cname
Source
# File lib/github-pages-health-check/domain.rb, line 134 def deprecated_ip? return @deprecated_ip if defined? @deprecated_ip @deprecated_ip = (valid_domain? && a_record? && old_ip_address?) end
rubocop:enable Metrics/AbcSize
Source
# File lib/github-pages-health-check/domain.rb, line 340 def dns return @dns if defined? @dns return unless valid_domain? @dns = Timeout.timeout(TIMEOUT) do GitHubPages::HealthCheck.without_warnings do next if host.nil? REQUESTED_RECORD_TYPES .map { |type| resolver.query(type) } .flatten.uniq end end rescue StandardError @dns = nil end
Returns an array of DNS answers
Source
# File lib/github-pages-health-check/domain.rb, line 358 def dns? !(dns.nil? || dns.empty?) end
Are we even able to get the DNS record?
Source
# File lib/github-pages-health-check/domain.rb, line 209 def dns_zone_ns? return @ns_records if defined?(@ns_records) return false unless dns? @ns_records = dns.any? do |answer| answer.type == Dnsruby::Types::NS && answer.name.to_s == host end end
Does the domain have associated NS records?
Source
# File lib/github-pages-health-check/domain.rb, line 197 def dns_zone_soa? return @soa_records if defined?(@soa_records) return false unless dns? @soa_records = dns.any? do |answer| answer.type == Dnsruby::Types::SOA && answer.name.to_s == host end end
Does the domain have an associated SOA record?
Source
# File lib/github-pages-health-check/domain.rb, line 499 def enforces_https? return false unless https? && http_response.headers["Location"] redirect = Addressable::URI.parse(http_response.headers["Location"]) redirect.scheme == "https" && redirect.host == host end
Does this domain redirect HTTP requests to HTTPS?
Source
# File lib/github-pages-health-check/domain.rb, line 296 def fastly? !!host.match(/\A#{Regexp.union(Fastly::HOSTNAMES)}\z/i) end
Is the host our Fastly
CNAME?
Source
# File lib/github-pages-health-check/domain.rb, line 306 def fastly_ip? cdn_ip?(Fastly) end
Does the domain resolve to a Fastly-owned IP
Source
# File lib/github-pages-health-check/domain.rb, line 291 def github_domain? host.downcase.eql?("github.com") || host.downcase.end_with?(".github.com") end
Is this domain owned by GitHub?
Source
# File lib/github-pages-health-check/domain.rb, line 488 def https? https_response.return_code == :ok end
Does this domain respond to HTTPS requests with a valid cert?
Source
# File lib/github-pages-health-check/domain.rb, line 507 def https_eligible? # Can't have any IP's which aren't GitHub's present. return false if non_github_pages_ip_present? # Can't have underscores in the domain name (Let's Encrypt does not allow it) return false if host.include?("_") # Must be a CNAME or point to our IPs. return true if cname_to_github_user_domain? || cname_to_domain_to_pages? # Check CAA records for the full domain and its parent domain. pointed_to_github_pages_ip? && caa.lets_encrypt_allowed? end
Can an HTTPS certificate be issued for this domain?
Source
# File lib/github-pages-health-check/domain.rb, line 494 def https_error https_response.return_code unless https? end
The response code of the HTTPS request, if it failed. Useful for diagnosing cert errors
Source
# File lib/github-pages-health-check/domain.rb, line 146 def invalid_a_record? return @invalid_a_record if defined? @invalid_a_record @invalid_a_record = (valid_domain? && a_record_present? && !should_be_a_record?) end
Source
# File lib/github-pages-health-check/domain.rb, line 140 def invalid_aaaa_record? return @invalid_aaaa_record if defined? @invalid_aaaa_record @invalid_aaaa_record = (valid_domain? && aaaa_record_present? && !should_be_a_record?) end
Source
# File lib/github-pages-health-check/domain.rb, line 152 def invalid_cname? return @invalid_cname if defined? @invalid_cname @invalid_cname = begin return false unless valid_domain? return false if github_domain? || apex_domain? return true if cname_to_pages_dot_github_dot_com? || cname_to_fastly? !cname_to_github_user_domain? && should_be_cname_record? end end
Source
# File lib/github-pages-health-check/domain.rb, line 459 def maybe_wildcard? return @maybe_wildcard if defined? @maybe_wildcard return false unless dns_resolves? return false unless parent_domain sibling_domain = SecureRandom.alphanumeric(20) + "." + parent_domain @maybe_wildcard = begin wildcard_resolver = GitHubPages::HealthCheck::Resolver.new(sibling_domain, :nameservers => nameservers) [Dnsruby::Types::A, Dnsruby::Types::AAAA].any? do |record_type| wildcard_resolver.query(record_type).any? do |record| record.respond_to?(:address) && github_pages_ip?(record.address) end end end end
Source
# File lib/github-pages-health-check/domain.rb, line 429 def mx_records_present? return unless dns? dns.any? { |answer| answer.type == Dnsruby::Types::MX } end
Source
# File lib/github-pages-health-check/domain.rb, line 235 def non_github_pages_ip_present? return unless dns? dns .select { |a| Dnsruby::Types::A == a.type || Dnsruby::Types::AAAA == a.type } .any? { |a| !github_pages_ip?(a.address.to_s) } end
Are any of the domain’s A or AAAA records pointing elsewhere?
Source
# File lib/github-pages-health-check/domain.rb, line 364 def old_ip_address? return unless dns? dns.any? do |answer| answer.type == Dnsruby::Types::A && legacy_ip?(answer.address.to_s) end end
Does this domain have any A record that points to the legacy IPs?
Source
# File lib/github-pages-health-check/domain.rb, line 281 def pages_domain? !!host.match(/\A[\w-]+\.github\.(io|com)\.?\z/i) end
Is the host a *.github.(io|com) domain?
Source
# File lib/github-pages-health-check/domain.rb, line 286 def pages_dot_github_dot_com? !!host.match(/\Apages\.github\.(io|com)\.?\z/i) end
Is the host pages.github.com or pages.github.io?
Source
# File lib/github-pages-health-check/domain.rb, line 276 def pages_io_domain? !!host.match(/\A[\w-]+\.github\.(io)\.?\z/i) end
Is the host a *.github.io domain?
Source
# File lib/github-pages-health-check/domain.rb, line 447 def parent_domain parsed = PublicSuffix.parse(host) parent = host.split(".", 2).last if parent == parsed.tld return nil end parent rescue PublicSuffix::DomainNotAllowed nil end
Source
# File lib/github-pages-health-check/domain.rb, line 228 def pointed_to_github_pages_ip? return false unless address_record? CURRENT_IP_ADDRESSES_ALL.include?(dns.first.address.to_s.downcase) end
Is the domain’s first response an A or AAAA record to a valid GitHub Pages IP?
Source
# File lib/github-pages-health-check/domain.rb, line 318 def proxied? return unless dns? return true if cloudflare_ip? return false if pointed_to_github_pages_ip? return false if cname_to_github_user_domain? return false if cname_to_domain_to_pages? return false if cname_to_pages_dot_github_dot_com? return false if cname_to_fastly? || fastly_ip? served_by_pages? end
Does this non-GitHub-pages domain proxy a GitHub Pages site?
This can be:
1. A Cloudflare-owned IP address 2. A site that returns GitHub.com server headers, but isn't CNAME'd to a GitHub domain 3. A site that returns GitHub.com server headers, but isn't CNAME'd to a GitHub IP
Source
# File lib/github-pages-health-check/domain.rb, line 435 def served_by_pages? return @served_by_pages if defined? @served_by_pages return unless dns_resolves? @served_by_pages = begin return true if response.headers["Server"] == "GitHub.com" # Typhoeus mangles the case of the header, compare insensitively response.headers.any? { |k, _v| k.downcase == "x-github-request-id" } end end
Source
# File lib/github-pages-health-check/domain.rb, line 219 def should_be_a_record? !pages_io_domain? && (apex_domain? || mx_records_present?) end
Should the domain use an A record?
Source
# File lib/github-pages-health-check/domain.rb, line 223 def should_be_cname_record? !should_be_a_record? end
Source
# File lib/github-pages-health-check/domain.rb, line 481 def uri(overrides = {}) options = { :host => host, :scheme => scheme, :path => "/" } options = options.merge(overrides) Addressable::URI.new(options).normalize.to_s end
Source
# File lib/github-pages-health-check/domain.rb, line 166 def valid_domain? return @valid if defined? @valid unicode_host = Addressable::IDNA.to_unicode(host) @valid = PublicSuffix.valid?(unicode_host, :default_rule => nil, :ignore_private => true) end
Is this a valid domain that PublicSuffix recognizes? Used as an escape hatch to prevent false positives on DNS checks
Source
# File lib/github-pages-health-check/domain.rb, line 477 def wildcard_warning Errors::WildcardRecordError.new :domain => self, :parent_domain => parent_domain if maybe_wildcard? end
Source
# File lib/github-pages-health-check/domain.rb, line 424 def www_cname(cname) @www_cname ||= cname.name.to_s.start_with?("www.") && cname.name.to_s.end_with?(cname.domainname.to_s) end
Check if we have a ‘www.’ CNAME that matches the domain
Private Instance Methods
Source
# File lib/github-pages-health-check/domain.rb, line 596 def absolute_domain host.end_with?(".") ? host : "#{host}." end
Adjust ‘domain` so that it won’t be searched for with /etc/resolv.conf
GitHubPages::HealthCheck.new("anything.io").absolute_domain => "anything.io."
Source
# File lib/github-pages-health-check/domain.rb, line 530 def address_record? a_record? || aaaa_record? end
Source
# File lib/github-pages-health-check/domain.rb, line 534 def caa @caa ||= GitHubPages::HealthCheck::CAA.new( :host => cname&.host || host, :nameservers => nameservers ) end
Source
# File lib/github-pages-health-check/domain.rb, line 605 def cdn_ip?(cdn) return unless dns? address_records = dns.select do |answer| Dnsruby::Types::A == answer.type || Dnsruby::Types::AAAA == answer.type end return false if !address_records || address_records.empty? address_records.all? do |answer| cdn.controls_ip?(answer.address) end end
Does the domain resolve to a CDN-owned IP
Source
# File lib/github-pages-health-check/domain.rb, line 622 def github_pages_ip?(ip_addr) CURRENT_IP_ADDRESSES_ALL.include?(ip_addr&.to_s&.downcase) end
Source
# File lib/github-pages-health-check/domain.rb, line 557 def http_response options = GitHubPages::HealthCheck.typhoeus_options.merge(:followlocation => false) @http_response ||= Typhoeus.head(uri(:scheme => "http"), options) end
The domain’s response to HTTP requests, without following redirects
Source
# File lib/github-pages-health-check/domain.rb, line 563 def https_response options = GitHubPages::HealthCheck.typhoeus_options.merge(:followlocation => false) @https_response ||= Typhoeus.head(uri(:scheme => "https"), options) end
The domain’s response to HTTPS requests, without following redirects
Source
# File lib/github-pages-health-check/domain.rb, line 618 def legacy_ip?(ip_addr) LEGACY_IP_ADDRESSES.include?(ip_addr) end
Source
# File lib/github-pages-health-check/domain.rb, line 583 def normalize_host(domain) domain = domain.strip.chomp(".") host = Addressable::URI.parse(domain).normalized_host host ||= Addressable::URI.parse("http://#{domain}").normalized_host host unless host.to_s.empty? rescue Addressable::URI::InvalidURIError nil end
Parse the URI. Accept either domain names or full URI’s. Used by the initializer so we can be more flexible with inputs.
domain - a URI or domain name.
Examples
normalize_host("benbalter.github.com") # => 'benbalter.github.com' normalize_host("https://benbalter.github.com") # => 'benbalter.github.com' normalize_host("benbalter.github.com/help-me-im-a-path/") # => 'benbalter.github.com'
Return the hostname.
Source
# File lib/github-pages-health-check/domain.rb, line 542 def response return @response if defined? @response @response = Typhoeus.head(uri, GitHubPages::HealthCheck.typhoeus_options) # Workaround for webmock not playing nicely with Typhoeus redirects # See https://github.com/bblimke/webmock/issues/237 if @response.mock? && @response.headers["Location"] @response = Typhoeus.head(response.headers["Location"], GitHubPages::HealthCheck.typhoeus_options) end @response end
The domain’s response to HTTP(S) requests, following redirects
Source
# File lib/github-pages-health-check/domain.rb, line 600 def scheme @scheme ||= github_domain? ? "https" : "http" end