pax_global_header00006660000000000000000000000064135503703600014514gustar00rootroot0000000000000052 comment=c7fbb0e803290be4efe3100a3513ef7218fba4c2 gitlab-net-dns-v0.9.1/000077500000000000000000000000001355037036000145215ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/.gitignore000066400000000000000000000000751355037036000165130ustar00rootroot00000000000000# Bundler .bundle pkg/* Gemfile.lock # YARD .yardoc yardoc/ gitlab-net-dns-v0.9.1/.gitlab-ci.yml000066400000000000000000000004411355037036000171540ustar00rootroot00000000000000image: "ruby:2.6-alpine" cache: paths: - vendor/ruby before_script: - apk add --update git make build-base - gem install bundler - bundle install --path vendor/ruby rubocop: script: - bundle exec rubocop allow_failure: true rspec: script: - bundle exec rake gitlab-net-dns-v0.9.1/.rspec000066400000000000000000000000101355037036000156250ustar00rootroot00000000000000--colourgitlab-net-dns-v0.9.1/.rubocop.yml000066400000000000000000000000761355037036000167760ustar00rootroot00000000000000inherit_from: - .rubocop_defaults.yml - .rubocop_todo.yml gitlab-net-dns-v0.9.1/.rubocop_defaults.yml000066400000000000000000000226621355037036000206720ustar00rootroot00000000000000# This cop requires odd code indentations (as of rubocop 0.57.0) # https://github.com/rubocop-hq/rubocop/issues/5956 Layout/AccessModifierIndentation: Enabled: false # It causes weird aligments, especially for specs. Layout/BlockEndNewline: Enabled: false # Generally, the keyword style uses a lot of space. This is particularly true when # you use case/if statements, in combination with a long-name variable. # # invoice_error_message = case error # when 1 == 1 # do_something # else # do_else # end # Layout/EndAlignment: EnforcedStyleAlignWith: variable # [codesmell] Lint/HandleExceptions: Enabled: false # [codesmell] Metrics/AbcSize: Enabled: false Exclude: - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' # [codesmell] Metrics/BlockLength: Enabled: false # [codesmell] Metrics/CyclomaticComplexity: Enabled: false Exclude: - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' # [codesmell] Metrics/ClassLength: Enabled: false Exclude: - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' # [codesmell] Metrics/LineLength: Enabled: false Exclude: - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' Max: 100 # [codesmell] Metrics/MethodLength: Enabled: false Exclude: - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' Max: 10 # [codesmell] Metrics/ModuleLength: Enabled: false Exclude: - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' # [codesmell] Metrics/ParameterLists: Enabled: false Max: 5 # [codesmell] Metrics/PerceivedComplexity: Enabled: false # We tend to use @_name to represent a variable that is memoized, # but it should not be accessed directly and kept as private. Naming/MemoizedInstanceVariableName: Enabled: false # We use it from time to time, as it's not always possible (or maintainable) # to use simple ? methods. # Moreover, it's actually more efficient to not-use predicates: # https://github.com/bbatsov/rubocop/issues/3633 Naming/PredicateName: Enabled: false # This cop triggers several false positives that make sense in our domain model. # For instance, ip is considered an uncommunicative parameter name: # # ipv4_to_arpa_name(ip) # Naming/UncommunicativeMethodParamName: Enabled: false # This cop returns false positive violations (as of rubocop 0.57.0) # https://github.com/rubocop-hq/rubocop/issues/5953 Style/AccessModifierDeclarations: Enabled: false # Do not use "and" or "or" in conditionals, but for readability we can use it # to chain executions. Just beware of operator order. Style/AndOr: EnforcedStyle: conditionals # No specific reason, except that %q() is easier to grep than %() Style/BarePercentLiterals: EnforcedStyle: percent_q # braces_for_chaining seems a good fit of what we've been doing so far. Style/BlockDelimiters: EnforcedStyle: braces_for_chaining IgnoredMethods: - expect # I'm not sure we should enforce a style, # but if we do, context_dependent offers a good compromise on readability. Style/BracesAroundHashParameters: Enabled: false EnforcedStyle: context_dependent # Warn on empty else. Style/EmptyElse: EnforcedStyle: empty # There is no specific preference for empty methods. # One-line methods are not exceptionally nice in Ruby. Just ignore this cop for now. Style/EmptyMethod: Enabled: false # We don't care about the format style. # In most cases we use %, but not at the point we want to enforce it # as a convention in the entire code. Style/FormatString: Enabled: false # Annotated tokens (like %s) are a good thing, but in most cases we don't need them. # %s is a simpler and straightforward version that works in almost all cases. So don't complain. Style/FormatStringToken: Enabled: false # We don't support frozen strings. # This is an experimental feature and we don't know if it will be shipped with # Ruby 3.0 or not. Style/FrozenStringLiteralComment: Enabled: false # Prefer the latest Hash syntax Style/HashSyntax: Exclude: # But Rakefiles generally have some definition like # :default => :test # that looks nicer with the old rocket syntax. - 'Rakefile' # We want to be able to decide when to use one-line if/unless modifiers. Style/IfUnlessModifier: Enabled: false # [codesmell] # It's not always that bad. Style/IfInsideElse: Enabled: false # module_function doesn't respect the visibility of the methods, # and doesn't work well when the module contain both public/private methods. Style/ModuleFunction: Enabled: false Style/MultilineBlockChain: Exclude: # RSpec uses multi-line blocks for certain features - 'spec/**/*_spec.rb' # unless is not always cool. Style/NegatedIf: Enabled: false # Magic numbers are not welcomed Style/NumericLiterals: Exclude: # however tests can use numeric literals for method calls, # without the need to define a variable just for that. - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' # For years, %w() has been the de-facto standard. A lot of libraries are using (). # Switching to [] would be a nightmare. Style/PercentLiteralDelimiters: Enabled: false # Enable but only for multiple returns value. # # return foo, bar # # reads much better than # # [foo, bar] # Style/RedundantReturn: AllowMultipleReturnValues: true # Do we care? Style/RegexpLiteral: Enabled: false # There are cases were the inline rescue is ok. We can either downgrade the severity, # or rely on the developer judgement on a case-by-case basis. Style/RescueModifier: Enabled: false # This is quite annoying, especially in cases where we don't control it (e.g. schema.rb). Style/SymbolArray: Enabled: false # We don't have a preference. Style/SpecialGlobalVars: Enabled: false EnforcedStyle: use_perl_names # We generally use double quotes, sometimes single quotes. # Should we enforce it at code level? Style/StringLiterals: Enabled: false EnforcedStyle: double_quotes # As before. Style/StringLiteralsInInterpolation: Enabled: false EnforcedStyle: double_quotes # It's nice to be consistent. The trailing comma also allows easy reordering, # and doesn't cause a diff in Git when you add a line to the bottom. Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: consistent_comma # It's nice to be consistent. The trailing comma also allows easy reordering, # and doesn't cause a diff in Git when you add a line to the bottom. Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: consistent_comma Style/TrivialAccessors: # IgnoreClassMethods because I want to be able to define class-level accessors # that sets an instance variable on the metaclass, such as: # # def self.default=(value) # @default = value # end # IgnoreClassMethods: true Style/WordArray: EnforcedStyle: percent MinSize: 3 # Forces the order of comparison arguments. # # According to this cop, the following statement is bad: # # "https" == uri.scheme # # Whereas the following is considered good: # # uri.scheme == "https" Style/YodaCondition: Enabled: false # For the same reason of EndAlignment, aligning with the case may have a bad impact # on a case after a very long variable. # # invoice_error_message = case error # when 1 == 1 # do_something # else # do_else # end # Layout/CaseIndentation: EnforcedStyle: end # I was a big fan of leading, but trailing seems to be more commonly adopted. # At least at the time being. Layout/DotPosition: EnforcedStyle: leading # Double empty lines are useful to separate conceptually different methods # in the same class or module. Layout/EmptyLines: Enabled: false # This is buggy. It detects as a style violation a few `class` and `module` definitions Layout/EmptyLinesAroundArguments: Enabled: false Layout/EmptyLinesAroundBlockBody: Exclude: # RSpec is all made of blocks. Disable this config in RSpec # to be consistent with EmptyLinesAroundClassBody and EmptyLinesAroundModuleBody - 'spec/**/*_spec.rb' - 'test/**/*_test.rb' # In most cases, a space is nice. Sometimes, it's not. # Just be consistent with the rest of the surrounding code. Layout/EmptyLinesAroundClassBody: Enabled: false # We're ok with it. We use it quite often for method-level rescue statements. Layout/EmptyLinesAroundExceptionHandlingKeywords: Enabled: false # In most cases, a space is nice. Sometimes, it's not. # Just be consistent with the rest of the surrounding code. Layout/EmptyLinesAroundModuleBody: Enabled: false # This is quite buggy, as it doesn't recognize double lines. Layout/EmptyLineBetweenDefs: Enabled: false # Multi-line differs from standard indentation, they are indented twice. Layout/IndentFirstArgument: IndentationWidth: 4 # Array indentation should be consistent with method/variable definition. Layout/IndentFirstArrayElement: EnforcedStyle: consistent # Hash indentation should be consistent with method/variable definition. Layout/IndentFirstHashElement: EnforcedStyle: consistent # Multi-line differs from standard indentation, they are indented twice. Layout/MultilineMethodCallIndentation: EnforcedStyle: indented IndentationWidth: 4 # Multi-line differs from standard indentation, they are indented twice. Layout/MultilineOperationIndentation: EnforcedStyle: indented IndentationWidth: 4 # Sorry, but using trailing spaces helps readability. # # %w( foo bar ) # # looks better than: # # %w(foo bar) # Layout/SpaceInsidePercentLiteralDelimiters: Enabled: false gitlab-net-dns-v0.9.1/.rubocop_todo.yml000066400000000000000000000120631355037036000200220ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2018-10-29 22:14:14 +0100 using RuboCop version 0.60.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 1 # Configuration parameters: Include. # Include: **/*.gemspec Gemspec/RequiredRubyVersion: Exclude: - 'net-dns.gemspec' # Offense count: 2 Lint/AmbiguousRegexpLiteral: Exclude: - 'test/unit/rr/mr_test.rb' - 'test/unit/rr/types_test.rb' # Offense count: 1 Lint/DuplicateMethods: Exclude: - 'lib/net/dns/resolver.rb' # Offense count: 1 Lint/EmptyWhen: Exclude: - 'lib/net/dns/rr/types.rb' # Offense count: 1 Lint/IneffectiveAccessModifier: Exclude: - 'lib/net/dns/rr.rb' # Offense count: 1 Lint/LiteralAsCondition: Exclude: - 'lib/net/dns/header.rb' # Offense count: 1 Lint/NonLocalExitFromIterator: Exclude: - 'lib/net/dns/resolver.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'lib/net/dns/core_ext.rb' - 'lib/net/dns/header.rb' - 'lib/net/dns/resolver.rb' - 'test/unit/rr/types_test.rb' # Offense count: 9 # Cop supports --auto-correct. # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. Lint/UnusedMethodArgument: Exclude: - 'lib/net/dns/packet.rb' - 'lib/net/dns/resolver.rb' - 'test/unit/resolver_test.rb' # Offense count: 5 # Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'lib/net/dns/rr/null.rb' - 'lib/net/dns/rr/ptr.rb' - 'lib/net/dns/rr/soa.rb' - 'lib/net/dns/rr/srv.rb' - 'lib/net/dns/rr/txt.rb' # Offense count: 13 Lint/UselessAssignment: Exclude: - 'lib/net/dns/packet.rb' - 'lib/net/dns/question.rb' - 'lib/net/dns/resolver.rb' - 'lib/net/dns/rr.rb' - 'lib/net/dns/rr/hinfo.rb' - 'lib/net/dns/rr/srv.rb' - 'test/unit/resolver_test.rb' # Offense count: 3 # Configuration parameters: CheckForMethodsWithNoSideEffects. Lint/Void: Exclude: - 'lib/net/dns/rr.rb' - 'lib/net/dns/rr/a.rb' - 'lib/net/dns/rr/aaaa.rb' # Offense count: 23 Naming/AccessorMethodName: Exclude: - 'lib/net/dns/rr.rb' - 'lib/net/dns/rr/a.rb' - 'lib/net/dns/rr/aaaa.rb' - 'lib/net/dns/rr/cname.rb' - 'lib/net/dns/rr/hinfo.rb' - 'lib/net/dns/rr/mr.rb' - 'lib/net/dns/rr/mx.rb' - 'lib/net/dns/rr/ns.rb' - 'lib/net/dns/rr/null.rb' - 'lib/net/dns/rr/ptr.rb' - 'lib/net/dns/rr/soa.rb' - 'lib/net/dns/rr/txt.rb' # Offense count: 12 Naming/ConstantName: Exclude: - 'lib/net/dns/header.rb' - 'lib/net/dns/resolver.rb' - 'test/unit/resolver_test.rb' - 'test/unit/rr/a_test.rb' - 'test/unit/rr/aaaa_test.rb' - 'test/unit/rr/classes_test.rb' - 'test/unit/rr/cname_test.rb' - 'test/unit/rr/hinfo_test.rb' - 'test/unit/rr/mr_test.rb' - 'test/unit/rr/mx_test.rb' - 'test/unit/rr/ns_test.rb' # Offense count: 12 # Configuration parameters: EnforcedStyle. # SupportedStyles: snake_case, camelCase Naming/MethodName: Exclude: - 'lib/net/dns/header.rb' - 'lib/net/dns/question.rb' - 'lib/net/dns/resolver.rb' - 'lib/net/dns/resolver/socks.rb' # Offense count: 24 # Configuration parameters: EnforcedStyle. # SupportedStyles: snake_case, camelCase Naming/VariableName: Exclude: - 'lib/net/dns/header.rb' - 'lib/net/dns/question.rb' # Offense count: 5 Security/Eval: Exclude: - 'lib/net/dns/header.rb' - 'lib/net/dns/resolver.rb' - 'lib/net/dns/rr.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - 'test/test_helper.rb' - 'test/unit/resolver_test.rb' # Offense count: 5 Style/ClassVars: Exclude: - 'lib/net/dns/resolver/socks.rb' - 'lib/net/dns/rr/classes.rb' - 'lib/net/dns/rr/types.rb' # Offense count: 6 Style/Documentation: Exclude: - 'spec/**/*' - 'test/**/*' - 'lib/net/dns.rb' - 'lib/net/dns/names.rb' - 'lib/net/dns/resolver.rb' - 'lib/net/dns/resolver/timeouts.rb' # Offense count: 2 Style/DoubleNegation: Exclude: - 'Rakefile' - 'lib/net/dns/resolver.rb' # Offense count: 4 Style/EvalWithLocation: Exclude: - 'lib/net/dns/header.rb' - 'lib/net/dns/resolver.rb' - 'lib/net/dns/rr.rb' # Offense count: 41 # Configuration parameters: MinBodyLength. Style/GuardClause: Enabled: false # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - 'spec/**/*' - 'lib/net/dns/header.rb' - 'lib/net/dns/names.rb' - 'lib/net/dns/question.rb' - 'lib/net/dns/resolver.rb' - 'lib/net/dns/resolver/timeouts.rb' gitlab-net-dns-v0.9.1/.travis.yml000066400000000000000000000001611355037036000166300ustar00rootroot00000000000000language: ruby rvm: - 2.1 - 2.2 - 2.3 - 2.4 - 2.5 - jruby-9.1.5.0 - ruby-head cache: - bundler gitlab-net-dns-v0.9.1/CHANGELOG.md000066400000000000000000000070501355037036000163340ustar00rootroot00000000000000# Changelog ## Release 0.9.1 - FIXED: Fixed undefined 'size' errors (https://gitlab.com/gitlab-org/gitlab-net-dns/merge_requests/2) - CHANGED: Test with GitLab CI (https://gitlab.com/gitlab-org/gitlab-net-dns/merge_requests/1) ## Release 0.9.0 - FIXED: Fixed CI run (GH-77). - FIXED: Fixed deprecated references to Fixnum. - CHANGED: Major code cleanup. - CHANGED: Minimum Ruby version is now 2.1 ## Release 0.8.0 - FIXED: undefined local variable or method `source_address_inet6' (GH-40). [Thanks @simsicon] - FIXED: Fixed bug on parsing multiple nameservers on different lines (GH-45). [Thanks @nicholasren] - CHANGED: Dropped duplicate query ID filter. Query ID is now randomically generated but it's not guaranteed to be unique (GH-39). [Thanks @ebroder] - CHANGED: require 'net/dns' is now the preferred way to load the library (GH-37). [Thanks @johnroa] - CHANGED: Removed setup.rb installation script. ## Release 0.7.1 - FIXED: Invalid file permissions on several files (GH-35) [Thanks @jamespharaoh] ## Release 0.7.0 - ADDED: Added (experimental) Support for HINFO record. - FIXED: Use Net::DNS::Resolver::Error class (not ResolverError, which does not exist). - FIXED: Cleaned up require dependency and recursive require statements. - FIXED: Use RbConfig instead of obsolete and deprecated Config (GH-28, GH-33) [Thanks @shadowbq, @eik3] - FIXED: SRV record not required by Net::DNS::RR (GH-27) [Thanks @sebastian] - FIXED: Resolver now supports IPv6 (GH-32) [Thanks @jamesotron] - FIXED: Net::DNS::RR::PTR references an invalid parameter (GH-19) [Thanks @dd23] - FIXED: Net::DNS::Question changes input arguments (GH-7) [Thanks @gfarfl] - CHANGED: Refactoring unit test to follow most used Ruby conventions. - CHANGED: Rewritten and simplified Net::DNS::Classes. Improved test harness. - CHANGED: Removed Jeweler development dependency. - CHANGED: The library is now compatible with Bundler. - CHANGED: Minimum supported Ruby version changed to Ruby 1.8.7. - CHANGED: Rescue NameError so unsupported record types only result in a warning. - CHANGED: Renamed Net::DNS::Resolver#send to Net::DNS::Resolver#query to avoid overriding default meaning of send method. ## Release 0.6.1 - ADDED: Net::DNS::Packet#to_s method (alias of #inspect) - FIXED: typo in lib/net/dns/rr/ptr.rb [Thanks Chris Lundquist] - FIXED: warning: method redefined; discarding old inspect (GH-3) [Thanks Kevin Baker] - FIXED: issue with rescue ArgumentError (GH-5) and with IPAddr handling (GH-6) ## Release 0.6.0 *WARNING:- If you are upgrading from a previous minor release, check out the Compatibility issue list below. - FIXED: Added missing #to_s method to Net::DNS::Question. - FIXED: Compatibility with Ruby 1.9 - FIXED: Types regexp order issue - CHANGED: Refactoring unit test to follow most used Ruby conventions - CHANGED: default timeout is now 5 seconds for both UDP and TCP - CHANGED: Moved main dns.rb file to lib/net folder as default for GEMs. In this way it can be autoloaded when the gem is required. ### Compatibility issues - CHANGED: RR#set_stype scope is now private to prevent invalid usage. - CHANGED: DnsTimeout#timeout now raises LocalJumpError instead of DnsTimeoutArgumentError when block is missing. - CHANGED: Renamed Net::DNS::RR::Types::Types to Net::DNS::RR::Types::TYPES to follow Ruby coding standards. ## Release 0.4 - many bug fixes (thanks guys!) - a whole new class Net::DNS::Header::RCode - new methods in Net::DNS::Resolver class to do AXFR queries - a new SRV resource record written by Dan Janowski - more documentation written and corrected gitlab-net-dns-v0.9.1/Gemfile000066400000000000000000000002441355037036000160140ustar00rootroot00000000000000source "https://rubygems.org" gemspec gem "minitest" gem "minitest-reporters" gem "rspec" gem "rubocop", require: false gem "rubocop-performance", require: false gitlab-net-dns-v0.9.1/LICENSE.txt000066400000000000000000000047361355037036000163560ustar00rootroot00000000000000net-dns is copyrighted free software by Marco Ceresa (@bluemonk) and Simone Carletti (@weppos). You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. gitlab-net-dns-v0.9.1/README.md000066400000000000000000000104451355037036000160040ustar00rootroot00000000000000# Net::DNS Net::DNS is a DNS library written in pure Ruby. It started as a port of Perl Net::DNS module, but it evolved in time into a full Ruby library. ## Features - Complete OO interface - Clean and intuitive API - Modular and flexible ## Requirements Ruby >= 2.1 ## Installation The best way to install this library is via [RubyGems](https://rubygems.org/). ``` gem install net-dns ``` ## API Documentation Visit the page http://rdoc.info/gems/net-dns ## Trivial resolver The simplest way to use the library is to invoke the Resolver() method: ```ruby require 'rubygems' require 'net/dns' p Resolver("www.google.com") ``` The output is compatible with BIND zone files and it's the same you would get with the dig utility. ``` ;; Answer received from localhost:53 (212 bytes) ;; ;; HEADER SECTION ;; id = 64075 ;; qr = 1 opCode: QUERY aa = 0 tc = 0 rd = 1 ;; ra = 1 ad = 0 cd = 0 rcode = NoError ;; qdCount = 1 anCount = 3 nsCount = 4 arCount = 4 ;; QUESTION SECTION (1 record): ;; google.com. IN A ;; ANSWER SECTION (3 records): google.com. 212 IN A 74.125.45.100 google.com. 212 IN A 74.125.67.100 google.com. 212 IN A 209.85.171.100 ;; AUTHORITY SECTION (4 records): google.com. 345512 IN NS ns1.google.com. google.com. 345512 IN NS ns4.google.com. google.com. 345512 IN NS ns2.google.com. google.com. 345512 IN NS ns3.google.com. ;; ADDITIONAL SECTION (4 records): ns1.google.com. 170275 IN A 216.239.32.10 ns2.google.com. 170275 IN A 216.239.34.10 ns3.google.com. 170275 IN A 216.239.36.10 ns4.google.com. 170275 IN A 216.239.38.10 ``` An optional block can be passed yielding the Net::DNS::Packet object ```ruby Resolver("www.google.com") { |packet| puts packet.size + " bytes" } # => 484 bytes ``` Same for Net::DNS::Resolver.start(): ```ruby Net::DNS::Resolver.start("google.com").answer.size # => 5 ``` As optional parameters, +TYPE+ and +CLASS+ can be specified. ```ruby p Net::DNS::Resolver.start("google.com", Net::DNS::MX) ;; Answer received from localhost:53 (316 bytes) ;; ;; HEADER SECTION ;; id = 59980 ;; qr = 1 opCode: QUERY aa = 0 tc = 0 rd = 1 ;; ra = 1 ad = 0 cd = 0 rcode = NoError ;; qdCount = 1 anCount = 4 nsCount = 4 arCount = 8 ;; QUESTION SECTION (1 record): ;; google.com. IN MX ;; ANSWER SECTION (4 records): google.com. 10800 IN MX 10 smtp2.google.com. google.com. 10800 IN MX 10 smtp3.google.com. google.com. 10800 IN MX 10 smtp4.google.com. google.com. 10800 IN MX 10 smtp1.google.com. ``` ## Handling the response packet The method Net::DNS::Resolver.start is a wrapper around Resolver.new. It returns a new Net::DNS::Packet object. A DNS packet is divided into 5 sections: - header section # => a Net::DNS::Header object - question section # => a Net::DNS::Question object - answer section # => an Array of Net::DNS::RR objects - authority section # => an Array of Net::DNS::RR objects - additional section # => an Array of Net::DNS::RR objects You can access each section by calling the attribute with the same name on a Packet object: ```ruby packet = Net::DNS::Resolver.start("google.com") header = packet.header answer = packet.answer puts "The packet is #{packet.data.size} bytes" puts "It contains #{header.anCount} answer entries" answer.any? {|ans| p ans} ``` The output is ``` The packet is 378 bytes It contains 3 answer entries google.com. 244 IN A 74.125.45.100 google.com. 244 IN A 74.125.67.100 google.com. 244 IN A 209.85.171.100 ``` A better way to handle the answer section is to use the iterators directly on a Packet object: ```ruby packet.each_address do |ip| puts "#{ip} is alive" if Ping.pingecho(ip.to_s, 10, 80) end ``` Gives: ``` 74.125.45.100 is alive 74.125.67.100 is alive 209.85.171.100 is alive ``` ## License Net::DNS is distributed under the same license Ruby is. ## Authors - Marco Ceresa (@bluemonk) - Simone Carletti (@weppos) gitlab-net-dns-v0.9.1/Rakefile000066400000000000000000000013241355037036000161660ustar00rootroot00000000000000require "bundler/gem_tasks" # Run test by default. task default: :test task test: %i[testunit spec] require 'rake/testtask' Rake::TestTask.new(:testunit) do |t| t.libs = %w[lib test] t.pattern = "test/**/*_test.rb" t.verbose = !ENV["VERBOSE"].nil? t.warning = !ENV["WARNING"].nil? end require "rubocop/rake_task" RuboCop::RakeTask.new require 'rspec/core/rake_task' begin require 'fuubar' rescue LoadError end RSpec::Core::RakeTask.new do |t| t.verbose = !!ENV["VERBOSE"] t.rspec_opts = [] t.rspec_opts << ['--format', 'Fuubar'] if defined?(Fuubar) end require 'yard/rake/yardoc_task' YARD::Rake::YardocTask.new(:yardoc) do |y| y.options = ["--output-dir", "yardoc"] end CLOBBER.include "yardoc" gitlab-net-dns-v0.9.1/THANKS.rdoc000066400000000000000000000007511355037036000163650ustar00rootroot00000000000000Many thanks to these wonderful people: - Paul Barry for his excellent article on May07 issue of LinuxJournal, for the kind words and for bug reports - Dan Janowski (and his SRV RR) - Joshua Abraham - Ben April - Gene Rogers - Nicholas Veeser - Gildas Cherruel - Alex Dalitz - Cory Wright - Nicolas Pugnant - Andrea Peltrin - Giovanni Corriga - Luca Russo - Pierguido Lambri - Andrea Pampuri - _koo_ - Oyku Gencay - Eric Liedtke - Justin Mercier - Daniel Berger and all #ruby-lang people gitlab-net-dns-v0.9.1/bin/000077500000000000000000000000001355037036000152715ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/bin/console000077500000000000000000000005141355037036000166610ustar00rootroot00000000000000#!/usr/bin/env ruby require "bundler/setup" require "net/dns" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require "irb" IRB.start gitlab-net-dns-v0.9.1/demo/000077500000000000000000000000001355037036000154455ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/demo/check_soa.rb000077500000000000000000000067111355037036000177210ustar00rootroot00000000000000#!/usr/bin/env ruby require 'rubygems' if RUBY_VERSION.to_s < "1.9.0" require 'net/dns' #------------------------------------------------------------------------------ # Get the domain from the command line. #------------------------------------------------------------------------------ raise ArgumentError, "Usage: check_soa.rb domain\n" unless ARGV.size == 1 domain = ARGV[0] #------------------------------------------------------------------------------ # Find all the nameservers for the domain. #------------------------------------------------------------------------------ res = Net::DNS::Resolver.new(defname: false, retry: 2) ns_req = res.query(domain, Net::DNS::NS) unless ns_req && (ns_req.header.anCount > 0) raise ArgumentError, "No nameservers found for domain: #{res.errorstring}" end # Send out non-recursive queries res.recurse = false # Do not buffer standard out # | = 1; #------------------------------------------------------------------------------ # Check the SOA record on each nameserver. #------------------------------------------------------------------------------ ns_req.each_nameserver do |ns| #---------------------------------------------------------------------- # Set the resolver to query this nameserver. #---------------------------------------------------------------------- # In order to lookup the IP(s) of the nameserver, we need a Resolver # object that is set to our local, recursive nameserver. So we create # a new object just to do that. local_res = Net::DNS::Resolver.new a_req = local_res.query(ns, Net::DNS::A) unless a_req puts "Can not find address for ns: " + res.errorstring + "\n" next end a_req.each_address do |ip| #---------------------------------------------------------------------- # Ask this IP. #---------------------------------------------------------------------- res.nameservers = ip print "#{ns} (#{ip}): " #---------------------------------------------------------------------- # Get the SOA record. #---------------------------------------------------------------------- soa_req = res.send(domain, Net::DNS::SOA, Net::DNS::IN) if soa_req.nil? puts res.errorstring, "\n" next end #---------------------------------------------------------------------- # Is this nameserver authoritative for the domain? #---------------------------------------------------------------------- unless soa_req.header.auth? print "isn't authoritative for domain\n" next end #---------------------------------------------------------------------- # We should have received exactly one answer. #---------------------------------------------------------------------- unless soa_req.header.anCount == 1 print "expected 1 answer, got " + soa_req.header.anCount.to_s + "\n" next end #---------------------------------------------------------------------- # Did we receive an SOA record? #---------------------------------------------------------------------- unless soa_req.answer[0].class == Net::DNS::RR::SOA print "expected SOA, got " + Net::DNS::RR::RRTypes.to_str(soa_req.answer[0].type) + "\n" next end #---------------------------------------------------------------------- # Print the serial number. #---------------------------------------------------------------------- print "has serial number " + soa_req.answer[0].serial.to_s + "\n" end end gitlab-net-dns-v0.9.1/demo/threads.rb000066400000000000000000000005721355037036000174300ustar00rootroot00000000000000require 'rubygems' if RUBY_VERSION.to_s < "1.9.0" require 'net/dns' a = ["ibm.com", "sun.com", "redhat.com"] threads = [] a.each do |dom| threads << Thread.new(dom) do |domain| res = Net::DNS::Resolver.new res.query(domain, Net::DNS::NS).each_nameserver do |ns| puts "Domain #{domain} has nameserver #{ns}" end puts "" end end threads.each(&:join) gitlab-net-dns-v0.9.1/gitlab-net-dns.gemspec000066400000000000000000000015671355037036000207070ustar00rootroot00000000000000$LOAD_PATH.push File.expand_path('lib', __dir__) require "net/dns/version" Gem::Specification.new do |s| s.name = "gitlab-net-dns" s.version = Net::DNS::VERSION s.authors = ["Marco Ceresa", "Simone Carletti"] s.email = ["ceresa@gmail.com", "weppos@weppos.net"] s.homepage = "https://gitlab.com/gitlab-org/gitlab-net-dns" s.summary = "Pure Ruby DNS library." s.description = "Net::DNS is a pure Ruby DNS library, with a clean OO interface and an extensible API." s.licenses = ["Ruby"] s.required_ruby_version = ">= 2.1" s.require_paths = ["lib"] s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.extra_rdoc_files = %w[LICENSE.txt] s.add_development_dependency "mocha" s.add_development_dependency "rake" s.add_development_dependency "yard" end gitlab-net-dns-v0.9.1/lib/000077500000000000000000000000001355037036000152675ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/lib/net/000077500000000000000000000000001355037036000160555ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/lib/net/dns.rb000066400000000000000000000036421355037036000171730ustar00rootroot00000000000000require_relative 'dns/version' require_relative 'dns/resolver' require_relative 'dns/rr' module Net module DNS # Packet size in bytes PACKETSZ = 512 # Size of the header HFIXEDSZ = 12 # Size of the question portion (type and class) QFIXEDSZ = 4 # Size of an RR portion (type,class,lenght and ttl) RRFIXEDSZ = 10 # Size of an int 32 bit INT32SZ = 4 # Size of a short int INT16SZ = 2 module QueryTypes SIGZERO = 0 A = 1 NS = 2 MD = 3 MF = 4 CNAME = 5 SOA = 6 MB = 7 MG = 8 MR = 9 NULL = 10 WKS = 11 PTR = 12 HINFO = 13 MINFO = 14 MX = 15 TXT = 16 RP = 17 AFSDB = 18 X25 = 19 ISDN = 20 RT = 21 NSAP = 22 NSAPPTR = 23 SIG = 24 KEY = 25 PX = 26 GPOS = 27 AAAA = 28 LOC = 29 NXT = 30 EID = 31 NIMLOC = 32 SRV = 33 ATMA = 34 NAPTR = 35 KX = 36 CERT = 37 DNAME = 39 OPT = 41 DS = 43 SSHFP = 44 RRSIG = 46 NSEC = 47 DNSKEY = 48 UINFO = 100 UID = 101 GID = 102 UNSPEC = 103 TKEY = 249 TSIG = 250 IXFR = 251 AXFR = 252 MAILB = 253 MAILA = 254 ANY = 255 end module QueryClasses # Internet class IN = 1 # Chaos class CH = 3 # Hesiod class HS = 4 # None class NONE = 254 # Any class ANY = 255 end include QueryTypes include QueryClasses end end gitlab-net-dns-v0.9.1/lib/net/dns/000077500000000000000000000000001355037036000166415ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/lib/net/dns/header.rb000066400000000000000000000534211355037036000204230ustar00rootroot00000000000000module Net module DNS # DNS packet header class # # The Net::DNS::Header class represents the header portion of a # DNS packet. An Header object is created whenever a new packet # is parsed or as user request. # # header = Net::DNS::Header.new # # ;; id = 18123 # # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1 # # ;; ra = 0 ad = 0 cd = 0 rcode = 0 # # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0 # # header.format # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 18123 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # |0| 0 |0|0|1|0|0| 0 | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 1 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # # packet is an instance of Net::DNS::Packet # header = packet.header # puts "Answer is #{header.auth? ? '' : 'non'} authoritative" # # A lot of methods were written to keep a compatibility layer with # the Perl version of the library, as long as methods name which are # more or less the same. # class Header # A wrong +count+ parameter has been passed. class WrongCountError < ArgumentError end # A wrong +recursive+ parameter has been passed. class WrongRecursiveError < ArgumentError end # An invalid +opCode+ has been specified. class WrongOpcodeError < ArgumentError end # Base error class. class Error < StandardError end # DNS Header RCode handling class # # It should be used internally by Net::DNS::Header class. However, it's still # possible to instantiate it directly. # # require 'net/dns/header' # rcode = Net::DNS::Header::RCode.new 0 # # The RCode class represents the RCode field in the Header portion of a # DNS packet. This field (called Response Code) is used to get informations # about the status of a DNS operation, such as a query or an update. These # are the values in the original Mockapetris's standard (RFC1035): # # * 0 No error condition # * 1 Format error - The name server was unable to interpret # the query. # * 2 Server failure - The name server was # unable to process this query due to a # problem with the name server. # * 3 Name Error - Meaningful only for # responses from an authoritative name # server, this code means that the # domain name referenced in the query does # not exist. # * 4 Not Implemented - The name server does # not support the requested kind of query. # * 5 Refused - The name server refuses to # perform the specified operation for # policy reasons. For example, a name # server may not wish to provide the # information to the particular requester, # or a name server may not wish to perform # a particular operation (e.g., zone # transfer) for particular data. # * 6-15 Reserved for future use. # # In the next DNS RFCs, codes 6-15 has been assigned to the following # errors: # # * 6 YXDomain # * 7 YXRRSet # * 8 NXRRSet # * 9 NotAuth # * 10 NotZone # # More RCodes has to come for TSIGs and other operations. # class RCode # Constant for +rcode+ Response Code No Error NOERROR = 0 # Constant for +rcode+ Response Code Format Error FORMAT = 1 # Constant for +rcode+ Response Code Server Format Error SERVER = 2 # Constant for +rcode+ Response Code Name Error NAME = 3 # Constant for +rcode+ Response Code Not Implemented Error NOTIMPLEMENTED = 4 # Constant for +rcode+ Response Code Refused Error REFUSED = 5 RCodeType = %w[NoError FormErr ServFail NXDomain NotImp Refused YXDomain YXRRSet NXRRSet NotAuth NotZone].freeze RCodeErrorString = ["No errors", "The name server was unable to interpret the query", "The name server was unable to process this query due to problem with the name server", "Domain name referenced in the query does not exists", "The name server does not support the requested kind of query", "The name server refuses to perform the specified operation for policy reasons", "", "", "", "", "",].freeze attr_reader :code, :type, :explanation def initialize(code) if (0..10).cover? code @code = code @type = RCodeType[code] @explanation = RCodeErrorString[code] else raise ArgumentError, "RCode `#{code}' out of range" end end def to_s @code.to_s end end # Constant for +opCode+ query QUERY = 0 # Constant for +opCode+ iquery IQUERY = 1 # Constant for +opCode+ status STATUS = 2 # Array with given strings OPARR = %w[QUERY IQUERY STATUS].freeze # Reader for +id+ attribute attr_reader :id # Reader for the operational code attr_reader :opCode # Reader for the rCode instance attr_reader :rCode # Reader for question section entries number attr_reader :qdCount # Reader for answer section entries number attr_reader :anCount # Reader for authority section entries number attr_reader :nsCount # Reader for addictional section entries number attr_reader :arCount # Creates a new Net::DNS::Header object with the desired values, # which can be specified as an Hash argument. When called without # arguments, defaults are used. If a data string is passed, values # are taken from parsing the string. # # Examples: # # # Create a new Net::DNS::Header object # header = Net::DNS::Header.new # # # Create a new Net::DNS::Header object passing values # header = Net::DNS::Header.new(:opCode => 1, :rd => 0) # # # Create a new Net::DNS::Header object with binary data # header = Net::DNS::Header.new(data) # # Default values are: # # :id => auto generated # :qr => 0 # Query response flag # :aa => 0 # Authoritative answer flag # :tc => 0 # Truncated packet flag # :ra => 0 # Recursiond available flag # :rCode => 0 # Response code (status of the query) # :opCode => 0 # Operational code (purpose of the query) # :cd => 0 # Checking disable flag # :ad => 0 # Only relevant in DNSSEC context # :rd => 1 # Recursion desired flag # :qdCount => 1 # Number of questions in the dns packet # :anCount => 0 # Number of answer RRs in the dns packet # :nsCount => 0 # Number of authoritative RRs in the dns packet # :arCount => 0 # Number of additional RRs in the dns packet # # See also each option for a detailed explanation of usage. # def initialize(arg = {}) if arg.is_a? Hash new_from_hash(arg) else raise ArgumentError, "Wrong argument class `#{arg.class}'" end end # Creates a new Net::DNS::Header object from binary data, which is # passed as a string object as argument. # The configurations parameters are taken from parsing the string. # # Example: # # # Create a new Net::DNS::Header object with binary data # header = Net::DNS::Header.new(data) # # header.auth? # #=> "true" if it comes from authoritative name server # def self.parse(arg) if arg.is_a? String o = allocate o.send(:new_from_binary, arg) o else raise ArgumentError, "Wrong argument class `#{arg.class}'" end end # Inspect method, prints out all the options and relative values. # # p Net::DNS::Header.new # # ;; id = 18123 # # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1 # # ;; ra = 0 ad = 0 cd = 0 rcode = 0 # # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0 # # This method will maybe be changed in the future to a more pretty # way of display output. # def inspect ";; id = #{@id}\n" + if false # @opCode == "UPDATE" # do stuff else ";; qr = #{@qr}\t" \ "opCode: #{opCode_str}\t" \ "aa = #{@aa}\t" \ "tc = #{@tc}\t" \ "rd = #{@rd}\n" \ ";; ra = #{@ra}\t" \ "ad = #{@ad}\t" \ "cd = #{@cd}\t" \ "rcode = #{@rCode.type}\n" \ ";; qdCount = #{@qdCount}\t" \ "anCount = #{@anCount}\t" \ "nsCount = #{@nsCount}\t" \ "arCount = #{@arCount}\n" end end # The Net::DNS::Header#format method prints out the header # in a special ascii representation of data, in a way # similar to those often found on RFCs. # # p Net::DNS::Header.new.format # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 18123 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # |0| 0 |0|0|1|0|0| 0 | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 1 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # | 0 | # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # This can be very usefull for didactical purpouses :) # def format del = ("+-" * 16) + "+\n" len = del.length str = del + "|" + @id.to_s.center(len - 3) + "|\n" str += del + "|" + @qr.to_s str += "|" + @opCode.to_s.center(7) str += "|" + @aa.to_s str += "|" + @tc.to_s str += "|" + @rd.to_s str += "|" + @ra.to_s str += "|" + @ad.to_s str += "|" + @cd.to_s.center(3) str += "|" + @rCode.to_s.center(7) + "|\n" str += del + "|" + @qdCount.to_s.center(len - 3) + "|\n" str += del + "|" + @anCount.to_s.center(len - 3) + "|\n" str += del + "|" + @nsCount.to_s.center(len - 3) + "|\n" str += del + "|" + @arCount.to_s.center(len - 3) + "|\n" + del str end # Returns the header data in binary format, appropriate # for use in a DNS query packet. # # hdata = header.data # puts "Header is #{hdata.size} bytes" # def data arr = [] arr.push(@id) arr.push((@qr << 7) | (@opCode << 3) | (@aa << 2) | (@tc << 1) | @rd) arr.push((@ra << 7) | (@ad << 5) | (@cd << 4) | @rCode.code) arr.push(@qdCount) arr.push(@anCount) arr.push(@nsCount) arr.push(@arCount) arr.pack("n C2 n4") end # Set the ID for the current header. Useful when # performing security tests. # def id=(val) if (0..65_535).cover? val @id = val else raise ArgumentError, "ID `#{val}' out of range" end end # Checks whether the header is a query (+qr+ bit set to 0) # def query? @qr == 0 end # Set the +qr+ query response flag to be either +true+ or # +false+. You can also use the values 0 and 1. This flag # indicates if the DNS packet contains a query or an answer, # so it should be set to +true+ in DNS answer packets. # If +qr+ is +true+, the packet is a response. # def qr=(val) case val when true @qr = 1 when false @qr = 0 when 0, 1 @qr = val else raise ArgumentError, ":qr must be true(or 1) or false(or 0)" end end # Checks whether the header is a response # (+qr+ bit set to 1) # def response? @qr == 1 end # Returns a string representation of the +opCode+ # # puts "Packet is a #{header.opCode_str}" # #=> Packet is a QUERY # def opCode_str OPARR[@opCode] end # Set the +opCode+ variable to a new value. This fields indicates # the type of the question present in the DNS packet; +val+ can be # one of the values QUERY, IQUERY or STATUS. # # * QUERY is the standard DNS query # * IQUERY is the inverse query # * STATUS is used to query the nameserver for its status # # Example: # # include Net::DNS # header = Header.new # header.opCode = Header::STATUS # def opCode=(val) if (0..2).cover? val @opCode = val else raise WrongOpcodeError, "Wrong opCode value (#{val}), must be QUERY, IQUERY or STATUS" end end # Checks whether the response is authoritative # # if header.auth? # puts "Response is authoritative" # else # puts "Answer is NOT authoritative" # end # def auth? @aa == 1 end # Set the +aa+ flag (authoritative answer) to either +true+ # or +false+. You can also use 0 or 1. # # This flag indicates whether a DNS answer packet contains # authoritative data, meaning that is was generated by a # nameserver authoritative for the domain of the question. # # Must only be set to +true+ in DNS answer packets. # def aa=(val) case val when true @aa = 1 when false @aa = 0 when 0, 1 @aa = val else raise ArgumentError, ":aa must be true(or 1) or false(or 0)" end end # Checks whether the packet was truncated # # # Sending packet using UDP # if header.truncated? # puts "Warning, packet has been truncated!" # # Sending packet using TCP # end # # Do something with the answer # def truncated? @tc == 1 end # Set the +tc+ flag (truncated packet) to either +true+ # ot +false+. You can also use 0 or 1. # # The truncated flag is used in response packets to indicate # that the amount of data to be trasmitted exceedes the # maximum allowed by the protocol in use, tipically UDP, and # that the data present in the packet has been truncated. # A different protocol (such has TCP) need to be used to # retrieve full data. # # Must only be set in DNS answer packets. # def tc=(val) case val when true @tc = 1 when false @tc = 0 when 0, 1 @tc = val else raise ArgumentError, ":tc must be true(or 1) or false(or 0)" end end # Checks whether the packet has a recursion bit # set, meaning that recursion is desired # def recursive? @rd == 1 end # Sets the recursion desidered bit. # Remember that recursion query support is # optional. # # header.recursive = true # hdata = header.data # suitable for sending # # Consult RFC1034 and RFC1035 for a detailed explanation # of how recursion works. # def recursive=(val) case val when true @rd = 1 when false @rd = 0 when 1 @rd = 1 when 0 @rd = 0 else raise WrongRecursiveError, "Wrong value (#{val}), please specify true (1) or false (0)" end end # Alias for Header#recursive= to keep compatibility # with the Perl version. # def rd=(val) self.recursive = val end # Checks whether recursion is available. # This flag is usually set by nameservers to indicate # that they support recursive-type queries. # def r_available? @ra == 1 end # Set the +ra+ flag (recursion available) to either +true+ or # +false+. You can also use 0 and 1. # # This flag must only be set in DNS answer packets. # def ra=(val) case val when true @ra = 1 when false @ra = 0 when 0, 1 @ra = val else raise ArgumentError, ":ra must be true(or 1) or false(or 0)" end end # Checks whether checking is enabled or disabled. # # Checking is enabled by default. # def checking? @cd == 0 end # Set the +cd+ flag (checking disabled) to either +true+ # ot +false+. You can also use 0 or 1. # def cd=(val) case val when true @cd = 1 when false @cd = 0 when 0, 1 @cd = val else raise ArgumentError, ":cd must be true(or 1) or false(or 0)" end end # Checks whether +ad+ flag has been set. # # This flag is only relevant in DNSSEC context. # def verified? @ad == 1 end # Set the +ad+ flag to either +true+ # ot +false+. You can also use 0 or 1. # # The AD bit is only set on answers where signatures have # been cryptographically verified or the server is # authoritative for the data and is allowed to set the bit by policy. # def ad=(val) case val when true @ad = 1 when false @ad = 0 when 0, 1 @ad = val else raise ArgumentError, ":ad must be true(or 1) or false(or 0)" end end # Returns an error array for the header response code, or # +nil+ if no error is generated. # # error, cause = header.rCode_str # puts "Error #{error} cause by: #{cause}" if error # #=> Error ForErr caused by: The name server # #=> was unable to interpret the query # def rCode_str [rCode.type, rCode.explanation] end # Checks for errors in the DNS packet # # unless header.error? # puts "No errors in DNS answer packet" # end # def error? @rCode.code > 0 end # Set the rCode value. This should only be done in DNS # answer packets. # def rCode=(val) @rCode = RCode.new(val) end # Sets the number of entries in a question section # def qdCount=(val) if (0..65_535).cover? val @qdCount = val else raise WrongCountError, "Wrong number of count (#{val}), must be 0-65535" end end # Sets the number of RRs in an answer section # def anCount=(val) if (0..65_535).cover? val @anCount = val else raise WrongCountError, "Wrong number of count (#{val}), must be 0-65535" end end # Sets the number of RRs in an authority section # def nsCount=(val) if (0..65_535).cover? val @nsCount = val else raise WrongCountError, "Wrong number of count (#{val}), must be 0-65535" end end # Sets the number of RRs in an addictional section # def arCount=(val) if (0..65_535).cover? val @arCount = val else raise WrongCountError, "Wrong number of count: `#{val}' must be 0-65535" end end private def new_from_scratch @id = genID # generate ad unique id @qr = @aa = @tc = @ra = @ad = @cd = 0 @rCode = RCode.new(0) # no error @anCount = @nsCount = @arCount = 0 @rd = @qdCount = 1 @opCode = QUERY # standard query, default message end def new_from_binary(str) unless str.size == Net::DNS::HFIXEDSZ raise ArgumentError, "Header binary data has wrong size: `#{str.size}' bytes" end arr = str.unpack("n C2 n4") @id = arr[0] @qr = (arr[1] >> 7) & 0x01 @opCode = (arr[1] >> 3) & 0x0F @aa = (arr[1] >> 2) & 0x01 @tc = (arr[1] >> 1) & 0x01 @rd = arr[1] & 0x1 @ra = (arr[2] >> 7) & 0x01 @ad = (arr[2] >> 5) & 0x01 @cd = (arr[2] >> 4) & 0x01 @rCode = RCode.new(arr[2] & 0xf) @qdCount = arr[3] @anCount = arr[4] @nsCount = arr[5] @arCount = arr[6] end def new_from_hash(hash) new_from_scratch hash.each do |key, val| eval "self.#{key} = val" end end def genID rand(65_535) end end end end gitlab-net-dns-v0.9.1/lib/net/dns/names.rb000066400000000000000000000071341355037036000202760ustar00rootroot00000000000000module Net # :nodoc: module DNS module Names # Base error class. class Error < StandardError end # Generic Names Error. class ExpandError < Error end INT16SZ = 2 # Expand a compressed name in a DNS Packet object. Please # see RFC1035 for an explanation of how the compression # in DNS packets works, how may it be useful and how should # be handled. # # This method accept two parameters: a raw packet data and an # offset, which indicates the point in the packet in which the # parsing has arrived. # def dn_expand(packet, offset) name = "" packetlen = packet.size loop do raise ExpandError, "Offset is greater than packet length!" if packetlen < (offset + 1) len = packet.unpack("@#{offset} C")[0] if len == 0 offset += 1 break elsif (len & 0xC0) == 0xC0 raise ExpandError, "Packet ended before offset expand" if packetlen < (offset + INT16SZ) ptr = packet.unpack("@#{offset} n")[0] ptr &= 0x3FFF name2 = dn_expand(packet, ptr)[0] raise ExpandError, "Packet is malformed!" if name2.nil? name += name2 offset += INT16SZ break else offset += 1 raise ExpandError, "No expansion found" if packetlen < (offset + len) elem = packet[offset..offset + len - 1] name += "#{elem}." offset += len end end [name, offset] # name.chomp(".") if trailing dot has to be omitted end def pack_name(name) if name.size > 255 raise ArgumentError, "Name may not exceed 255 chars" end arr = name.split(".") str = "" arr.each do |elem| if elem.size > 63 raise ArgumentError, "Label may not exceed 63 chars" end str += [elem.size, elem].pack("Ca*") end str += [0].pack("C") str end def names_array(name) arr = name.split(".") ar = [] string = "" arr.size.times do |i| x = i + 1 elem = arr[-x] len = elem.size string = (string.reverse + [len, elem].pack("Ca*").reverse).reverse ar.unshift(string) end ar end def dn_comp(name, offset, compnames) names = {} ptr = 0 str = "" arr = names_array(name) arr.each do |entry| if compnames.key?(entry) ptr = 0xC000 | compnames[entry] str += [ptr].pack("n") offset += INT16SZ break else len = entry.unpack("C")[0] elem = entry[1..len] str += [len, elem].pack("Ca*") names.update(entry.to_s => offset) offset += len end end [str, offset, names] end def valid?(name) if name =~ /^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+((a[cdefgilmnoqrstuwxz]|aero|arpa)|(b[abdefghijmnorstvwyz]|biz)|(c[acdfghiklmnorsuvxyz]|cat|com|coop)|d[ejkmoz]|(e[ceghrstu]|edu)|f[ijkmor]|(g[abdefghilmnpqrstuwy]|gov)|h[kmnrtu]|(i[delmnoqrst]|info|int)|(j[emop]|jobs)|k[eghimnprwyz]|l[abcikrstuvy]|(m[acdghklmnopqrstuvwxyz]|mil|mobi|museum)|(n[acefgilopruz]|name|net)|(om|org)|(p[aefghklmnrstwy]|pro)|qa|r[eouw]|s[abcdeghijklmnortvyz]|(t[cdfghjklmnoprtvwz]|travel)|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])$/i name else raise ArgumentError, "Invalid FQDN: #{name}" end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/packet.rb000066400000000000000000000437521355037036000204500ustar00rootroot00000000000000require 'logger' require 'net/dns/names' require 'net/dns/header' require 'net/dns/question' require 'net/dns/rr' module Net module DNS # # = Net::DNS::Packet # # The Net::DNS::Packet class represents an entire DNS packet, # divided in his main section: # # * Header (instance of Net::DNS::Header) # * Question (array of Net::DNS::Question objects) # * Answer, Authority, Additional (each formed by an array of Net::DNS::RR # objects) # # You can use this class whenever you need to create a DNS packet, whether # in an user application, in a resolver instance (have a look, for instance, # at the Net::DNS::Resolver#send method) or for a nameserver. # # For example: # # # Create a packet # packet = Net::DNS::Packet.new("www.example.com") # mx = Net::DNS::Packet.new("example.com", Net::DNS::MX) # # # Getting packet binary data, suitable for network transmission # data = packet.data # # A packet object can be created from binary data too, like an # answer packet just received from a network stream: # # packet = Net::DNS::Packet::parse(data) # # Each part of a packet can be gotten by the right accessors: # # header = packet.header # Instance of Net::DNS::Header class # question = packet.question # Instance of Net::DNS::Question class # # # Iterate over additional RRs # packet.additional.each do |rr| # puts "Got an #{rr.type} record" # end # # Some iterators have been written to easy the access of those RRs, # which are often the most important. So instead of doing: # # packet.answer.each do |rr| # if rr.type == Net::DNS::RR::Types::A # # do something with +rr.address+ # end # end # # we can do: # # packet.each_address do |ip| # # do something with +ip+ # end # # Be sure you don't miss all the iterators in the class documentation. # # == Logging facility # # As Net::DNS::Resolver class, Net::DNS::Packet class has its own logging # facility too. It work in the same way the other one do, so you can # maybe want to override it or change the file descriptor. # # packet = Net::DNS::Packet.new("www.example.com") # packet.logger = $stderr # # # or even # packet.logger = Logger.new("/tmp/packet.log") # # If the Net::DNS::Packet class is directly instantiated by the Net::DNS::Resolver # class, like the great majority of the time, it will use the same logger facility. # # Logger level will be set to Logger::Debug if $DEBUG variable is set. # class Packet include Names # Base error class. class Error < StandardError end # Generic Packet Error. class PacketError < Error end attr_reader :header, :question, :answer, :authority, :additional attr_reader :answerfrom, :answersize # Creates a new instance of Net::DNS::Packet class. Arguments are the # canonical name of the resource, an optional type field and an optional # class field. The record type and class can be omitted; they default # to +A+ and +IN+. # # packet = Net::DNS::Packet.new("www.example.com") # packet = Net::DNS::Packet.new("example.com", Net::DNS::MX) # packet = Net::DNS::Packet.new("example.com", Net::DNS::TXT, Net::DNS::CH) # # This class no longer instantiate object from binary data coming from # network streams. Please use Net::DNS::Packet.parse instead. def initialize(name, type = Net::DNS::A, cls = Net::DNS::IN) @header = Net::DNS::Header.new(qdCount: 1) @question = [Net::DNS::Question.new(name, type, cls)] @answer = [] @authority = [] @additional = [] @logger = Logger.new $stdout @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN end # Checks if the packet is a QUERY packet def query? @header.opCode == Net::DNS::Header::QUERY end # Returns the packet object in binary data, suitable # for sending across a network stream. # # packet_data = packet.data # puts "Packet is #{packet_data.size} bytes long" # def data qdcount = ancount = nscount = arcount = 0 data = @header.data headerlength = data.length @question.each do |question| data += question.data qdcount += 1 end @answer.each do |rr| data += rr.data # (data.length) ancount += 1 end @authority.each do |rr| data += rr.data # (data.length) nscount += 1 end @additional.each do |rr| data += rr.data # (data.length) arcount += 1 end @header.qdCount = qdcount @header.anCount = ancount @header.nsCount = nscount @header.arCount = arcount @header.data + data[Net::DNS::HFIXEDSZ..data.size] end # Same as Net::DNS::Packet#data, but implements name compression # (see RFC1025) for a considerable save of bytes. # # packet = Net::DNS::Packet.new("www.example.com") # puts "Size normal is #{packet.data.size} bytes" # puts "Size compressed is #{packet.data_comp.size} bytes" # def data_comp offset = 0 compnames = {} qdcount = ancount = nscount = arcount = 0 data = @header.data headerlength = data.length @question.each do |question| str, offset, names = question.data data += str compnames.update(names) qdcount += 1 end @answer.each do |rr| str, offset, names = rr.data(offset, compnames) data += str compnames.update(names) ancount += 1 end @authority.each do |rr| str, offset, names = rr.data(offset, compnames) data += str compnames.update(names) nscount += 1 end @additional.each do |rr| str, offset, names = rr.data(offset, compnames) data += str compnames.update(names) arcount += 1 end @header.qdCount = qdcount @header.anCount = ancount @header.nsCount = nscount @header.arCount = arcount @header.data + data[Net::DNS::HFIXEDSZ..data.size] end # Returns a string containing a human-readable representation # of this Net::DNS::Packet instance. def inspect retval = "" if (@answerfrom != "0.0.0.0:0") && @answerfrom retval += ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n" end retval += ";; HEADER SECTION\n" retval += @header.inspect retval += "\n" section = @header.opCode == "UPDATE" ? "ZONE" : "QUESTION" retval += ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '' : 's'}):\n" @question.each do |qr| retval += ";; " + qr.inspect + "\n" end unless @answer.empty? retval += "\n" section = @header.opCode == "UPDATE" ? "PREREQUISITE" : "ANSWER" retval += ";; #{section} SECTION (#{@header.anCount} record#{@header.anCount == 1 ? '' : 's'}):\n" @answer.each do |rr| retval += rr.inspect + "\n" end end unless @authority.empty? retval += "\n" section = @header.opCode == "UPDATE" ? "UPDATE" : "AUTHORITY" retval += ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '' : 's'}):\n" @authority.each do |rr| retval += rr.inspect + "\n" end end unless @additional.empty? retval += "\n" retval += ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '' : 's'}):\n" @additional.each do |rr| retval += rr.inspect + "\n" end end retval end alias to_s inspect # Delegates to Net::DNS::Header#truncated?. def truncated? @header.truncated? end # Assigns a Net::DNS::Header object # to this Net::DNS::Packet instance. def header=(object) if object.is_a? Net::DNS::Header @header = object else raise ArgumentError, "Argument must be a Net::DNS::Header object" end end # Assigns a Net::DNS::Question object # to this Net::DNS::Packet instance. def question=(object) case object when Array if object.all? { |x| x.is_a? Net::DNS::Question } @question = object else raise ArgumentError, "Some of the elements is not an Net::DNS::Question object" end when Net::DNS::Question @question = [object] else raise ArgumentError, "Invalid argument, not a Question object nor an array of objects" end end # Assigns one or an array of Net::DNS::RR objects # to the answer section of this Net::DNS::Packet instance. def answer=(object) case object when Array if object.all? { |x| x.is_a? Net::DNS::RR } @answer = object else raise ArgumentError, "Some of the elements is not an Net::DNS::RR object" end when Net::DNS::RR @answer = [object] else raise ArgumentError, "Invalid argument, not a RR object nor an array of objects" end end # Assigns one or an array of Net::DNS::RR objects # to the additional section of this Net::DNS::Packet instance. def additional=(object) case object when Array if object.all? { |x| x.is_a? Net::DNS::RR } @additional = object else raise ArgumentError, "Some of the elements is not an Net::DNS::RR object" end when Net::DNS::RR @additional = [object] else raise ArgumentError, "Invalid argument, not a RR object nor an array of objects" end end # Assigns one or an array of Net::DNS::RR objects # to the authority section of this Net::DNS::Packet instance. def authority=(object) case object when Array if object.all? { |x| x.is_a? Net::DNS::RR } @authority = object else raise ArgumentError, "Some of the elements is not an Net::DNS::RR object" end when Net::DNS::RR @authority = [object] else raise ArgumentError, "Invalid argument, not a RR object nor an array of objects" end end # Iterates every address in the +answer+ section # of this Net::DNS::Packet instance. # # packet.each_address do |ip| # ping ip.to_s # end # # As you can see in the documentation for the Net::DNS::RR::A class, # the address returned is an instance of IPAddr class. def each_address(&block) @answer.each do |elem| next unless elem.class == Net::DNS::RR::A yield elem.address end end # Iterates every nameserver in the +answer+ section # of this Net::DNS::Packet instance. # # packet.each_nameserver do |ns| # puts "Nameserver found: #{ns}" # end # def each_nameserver(&block) @answer.each do |elem| next unless elem.class == Net::DNS::RR::NS yield elem.nsdname end end # Iterates every exchange record in the +answer+ section # of this Net::DNS::Packet instance. # # packet.each_mx do |pref,name| # puts "Mail exchange #{name} has preference #{pref}" # end # def each_mx(&block) @answer.each do |elem| next unless elem.class == Net::DNS::RR::MX yield elem.preference, elem.exchange end end # Iterates every canonical name in the +answer+ section # of this Net::DNS::Packet instance. # # packet.each_cname do |cname| # puts "Canonical name: #{cname}" # end # def each_cname(&block) @answer.each do |elem| next unless elem.class == Net::DNS::RR::CNAME yield elem.cname end end # Iterates every pointer in the +answer+ section # of this Net::DNS::Packet instance. # # packet.each_ptr do |ptr| # puts "Pointer for resource: #{ptr}" # end # def each_ptr(&block) @answer.each do |elem| next unless elem.class == Net::DNS::RR::PTR yield elem.ptrdname end end # Returns the packet size in bytes. # # Resolver("www.google.com") do |packet| # puts packet.size + " bytes"} # end # # => 484 bytes # def size data.size end # Checks whether the query returned a NXDOMAIN error, # meaning the queried domain name doesn't exist. # # %w[a.com google.com ibm.com d.com].each do |domain| # response = Net::DNS::Resolver.new.send(domain) # puts "#{domain} doesn't exist" if response.nxdomain? # end # # => a.com doesn't exist # # => d.com doesn't exist # def nxdomain? header.rCode.code == Net::DNS::Header::RCode::NAME end # Creates a new instance of Net::DNS::Packet class from binary data, # taken out from a network stream. For example: # # # udp_socket is an UDPSocket waiting for a response # ans = udp_socket.recvfrom(1500) # packet = Net::DNS::Packet::parse(ans) # # An optional +from+ argument can be used to specify the information # of the sender. If data is passed as is from a Socket#recvfrom call, # the method will accept it. # # Be sure that your network data is clean from any UDP/TCP header, # especially when using RAW sockets. # def self.parse(*args) o = allocate o.send(:new_from_data, *args) o end private # New packet from binary data def new_from_data(data, from = nil) unless from if data.is_a? Array data, from = data else from = [0, 0, "0.0.0.0", "unknown"] end end @answerfrom = from[2] + ":" + from[1].to_s @answersize = data.size @logger = Logger.new $stdout @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN #------------------------------------------------------------ # Header section #------------------------------------------------------------ offset = Net::DNS::HFIXEDSZ @header = Net::DNS::Header.parse(data[0..offset - 1]) @logger.debug ";; HEADER SECTION" @logger.debug @header.inspect #------------------------------------------------------------ # Question section #------------------------------------------------------------ section = @header.opCode == "UPDATE" ? "ZONE" : "QUESTION" @logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '' : 's'})" @question = [] @header.qdCount.times do qobj, offset = parse_question(data, offset) @question << qobj @logger.debug ";; #{qobj.inspect}" end #------------------------------------------------------------ # Answer/prerequisite section #------------------------------------------------------------ section = @header.opCode == "UPDATE" ? "PREREQUISITE" : "ANSWER" @logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '' : 's'})" @answer = [] @header.anCount.times do begin rrobj, offset = Net::DNS::RR.parse_packet(data, offset) @answer << rrobj @logger.debug rrobj.inspect rescue NameError => e warn "Net::DNS unsupported record type: #{e.message}" end end #------------------------------------------------------------ # Authority/update section #------------------------------------------------------------ section = @header.opCode == "UPDATE" ? "UPDATE" : "AUTHORITY" @logger.debug ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '' : 's'})" @authority = [] @header.nsCount.times do begin rrobj, offset = Net::DNS::RR.parse_packet(data, offset) @authority << rrobj @logger.debug rrobj.inspect rescue NameError => e warn "Net::DNS unsupported record type: #{e.message}" end end #------------------------------------------------------------ # Additional section #------------------------------------------------------------ @logger.debug ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '' : 's'})" @additional = [] @header.arCount.times do begin rrobj, offset = Net::DNS::RR.parse_packet(data, offset) @additional << rrobj @logger.debug rrobj.inspect rescue NameError => e warn "Net::DNS unsupported record type: #{e.message}" end end end # Parse question section def parse_question(data, offset) size = (dn_expand(data, offset)[1] - offset) + (2 * Net::DNS::INT16SZ) [Net::DNS::Question.parse(data[offset, size]), offset + size] rescue StandardError => e raise PacketError, "Caught exception, maybe packet malformed => #{e.message}" end end end end gitlab-net-dns-v0.9.1/lib/net/dns/question.rb000066400000000000000000000125261355037036000210430ustar00rootroot00000000000000module Net module DNS # # =Name # # Net::DNS::Question - DNS packet question class # # =Synopsis # # require 'net/dns/question' # # =Description # # This class represent the Question portion of a DNS packet. The number # of question entries is stored in the +qdCount+ variable of an Header # object. # # A new object can be created passing the name of the query and the type # of answer desired, plus an optional argument containing the class: # # question = Net::DNS::Question.new("google.com.", Net::DNS::A) # #=> "google.com. A IN" # # Alternatevly, a new object is created when processing a binary # packet, as when an answer is received. # To obtain the binary data from a question object you can use # the method Question#data: # # question.data # #=> "\006google\003com\000\000\001\000\001" # # A lot of methods were written to keep a compatibility layer with # the Perl version of the library, as long as methods name which are # more or less the same. # class Question include Names # Base error class. class Error < StandardError end # An error in the +name+ part of a Question entry class NameInvalid < Error end # +name+ part of a Question entry attr_reader :qName # +type+ part of a Question entry attr_reader :qType # +class+ part of a Question entry attr_reader :qClass # Creates a new Net::DNS::Question object: # # question = Net::DNS::Question.new("example.com") # #=> "example.com A IN" # question = Net::DNS::Question.new("example.com", Net::DNS::MX) # #=> "example.com MX IN" # question = Net::DNS::Question.new("example.com", Net::DNS::TXT, Net::DNS::HS) # #=> "example.com TXT HS" # If not specified, +type+ and +cls+ arguments defaults # to Net::DNS::A and Net::DNS::IN respectively. # def initialize(name, type = Net::DNS::A, cls = Net::DNS::IN) @qName = check_name name @qType = Net::DNS::RR::Types.new(type) @qClass = Net::DNS::RR::Classes.new(cls) end # Return a new Net::DNS::Question object created by # parsing binary data, such as an answer from the # nameserver. # # question = Net::DNS::Question.parse(data) # puts "Queried for #{question.qName} type #{question.qType.to_s}" # #=> Queried for example.com type A # def self.parse(arg) o = allocate o.send(:new_from_binary, arg.to_s) o end # Outputs binary data from a Question object # # question.data # #=> "\006google\003com\000\000\001\000\001" # def data [pack_name(@qName), @qType.to_i, @qClass.to_i].pack("a*nn") end # Return the binary data of the objects, plus an offset # and an Hash with references to compressed names. For use in # Net::DNS::Packet compressed packet creation. def comp_data arr = @qName.split(".") str = pack_name(@qName) string = "" names = {} offset = Net::DNS::HFIXEDSZ arr.size.times do |i| x = i + 1 elem = arr[-x] len = elem.size string = (string.reverse + [len, elem].pack("Ca*").reverse).reverse names[string] = offset offset += len end offset += 2 * Net::DNS::INT16SZ str += "\000" [[str, @qType.to_i, @qClass.to_i].pack("a*nn"), offset, names] end # # call-seq: # question.inspect -> string # # Returns a printable version of question with nice formatting. # # q = Net::DNS::Question.new("google.com.", Net::DNS::A) # q.inspect # => "google.com. IN A " # def inspect len = if @qName.size > 29 @qName.size + 1 else 29 end [@qName, @qClass.to_s, @qType.to_s].pack("A#{len} A8 A8") end # # call-seq: # question.to_s -> string # # Returns a string representation of question. # It is the same as inspect. # # q = Net::DNS::Question.new("google.com.", Net::DNS::A) # q.inspect # => "google.com. IN A " # def to_s inspect.to_s end private def build_qName(str) result = "" offset = 0 loop do len = str.unpack("@#{offset} C")[0] break if len == 0 offset += 1 result += str[offset..offset + len - 1] result += "." offset += len end result end def check_name(input) name = input.to_s.strip if name =~ /[^\w\.\-_]/ raise NameInvalid, "Invalid Question Name `#{name}'" end name end def new_from_binary(data) str, type, cls = data.unpack("a#{data.size - 4}nn") @qName = build_qName(str) @qType = Net::DNS::RR::Types.new type @qClass = Net::DNS::RR::Classes.new cls rescue StandardError => e raise ArgumentError, "Invalid data: #{data.inspect}" end end end end gitlab-net-dns-v0.9.1/lib/net/dns/resolver.rb000066400000000000000000001177131355037036000210410ustar00rootroot00000000000000require 'rbconfig' require 'socket' require 'timeout' require 'net/dns/packet' require 'net/dns/resolver/timeouts' # Resolver helper method. # # Calling the resolver directly: # # puts Resolver("www.google.com").answer.size # # => 5 # # An optional block can be passed yielding the Net::DNS::Packet object. # # Resolver("www.google.com") { |packet| puts packet.size + " bytes" } # # => 484 bytes # def Resolver(name, type = Net::DNS::A, cls = Net::DNS::IN, &block) resolver = Net::DNS::Resolver.start(name, type, cls) if block_given? yield resolver else resolver end end module Net module DNS include Logger::Severity # = Net::DNS::Resolver - DNS resolver class # # The Net::DNS::Resolver class implements a complete DNS resolver written # in pure Ruby, without a single C line of code. It has all of the # tipical properties of an evoluted resolver, and a bit of OO which # comes from having used Ruby. # # This project started as a porting of the Net::DNS Perl module, # written by Martin Fuhr, but turned out (in the last months) to be # an almost complete rewriting. Well, maybe some of the features of # the Perl version are still missing, but guys, at least this is # readable code! # # == Environment # # The Following Environment variables can also be used to configure # the resolver: # # * +RES_NAMESERVERS+: A space-separated list of nameservers to query. # # # Bourne Shell # $ RES_NAMESERVERS="192.168.1.1 192.168.2.2 192.168.3.3" # $ export RES_NAMESERVERS # # # C Shell # % setenv RES_NAMESERVERS "192.168.1.1 192.168.2.2 192.168.3.3" # # * +RES_SEARCHLIST+: A space-separated list of domains to put in the # search list. # # # Bourne Shell # $ RES_SEARCHLIST="example.com sub1.example.com sub2.example.com" # $ export RES_SEARCHLIST # # # C Shell # % setenv RES_SEARCHLIST "example.com sub1.example.com sub2.example.com" # # * +LOCALDOMAIN+: The default domain. # # # Bourne Shell # $ LOCALDOMAIN=example.com # $ export LOCALDOMAIN # # # C Shell # % setenv LOCALDOMAIN example.com # # * +RES_OPTIONS+: A space-separated list of resolver options to set. # Options that take values are specified as option:value. # # # Bourne Shell # $ RES_OPTIONS="retrans:3 retry:2 debug" # $ export RES_OPTIONS # # # C Shell # % setenv RES_OPTIONS "retrans:3 retry:2 debug" # class Resolver class Error < StandardError end class NoResponseError < Error end # An hash with the defaults values of almost all the # configuration parameters of a resolver object. See # the description for each parameter to have an # explanation of its usage. Defaults = { config_file: "/etc/resolv.conf", log_file: $stdout, port: 53, searchlist: [], nameservers: [IPAddr.new("127.0.0.1")], domain: "", source_port: 0, source_address: IPAddr.new("0.0.0.0"), source_address_inet6: IPAddr.new('::'), retry_interval: 5, retry_number: 4, recursive: true, defname: true, dns_search: true, use_tcp: false, ignore_truncated: false, packet_size: 512, tcp_timeout: TcpTimeout.new(5), udp_timeout: UdpTimeout.new(5), }.freeze class << self C = Object.const_get(defined?(RbConfig) ? :RbConfig : :Config)::CONFIG # Quick resolver method. Bypass the configuration using # the defaults. # # Net::DNS::Resolver.start "www.google.com" # def start(*params) new.search(*params) end # Returns true if running on a Windows platform. # # Note. This method doesn't rely on the RUBY_PLATFORM constant # because the comparison will fail when running on JRuby. # On JRuby RUBY_PLATFORM == 'java'. def platform_windows? !!(C["host_os"] =~ /msdos|mswin|djgpp|mingw/i) end end # Creates a new resolver object. # # Argument +config+ can either be empty or be an hash with # some configuration parameters. To know what each parameter # do, look at the description of each. # Some example: # # # Use the sistem defaults # res = Net::DNS::Resolver.new # # # Specify a configuration file # res = Net::DNS::Resolver.new(:config_file => '/my/dns.conf') # # # Set some option # res = Net::DNS::Resolver.new(:nameservers => "172.16.1.1", # :recursive => false, # :retry => 10) # # == Config file # # Net::DNS::Resolver uses a config file to read the usual # values a resolver needs, such as nameserver list and # domain names. On UNIX systems the defaults are read from the # following files, in the order indicated: # # * /etc/resolv.conf # * $HOME/.resolv.conf # * ./.resolv.conf # # The following keywords are recognized in resolver configuration files: # # * domain: the default domain. # * search: a space-separated list of domains to put in the search list. # * nameserver: a space-separated list of nameservers to query. # # Files except for /etc/resolv.conf must be owned by the effective userid # running the program or they won't be read. In addition, several environment # variables can also contain configuration information; see Environment # in the main description for Resolver class. # # On Windows Systems, an attempt is made to determine the system defaults # using the registry. This is still a work in progress; systems with many # dynamically configured network interfaces may confuse Net::DNS. # # You can include a configuration file of your own when creating a resolver # object: # # # Use my own configuration file # my $res = Net::DNS::Resolver->new(config_file => '/my/dns.conf'); # # This is supported on both UNIX and Windows. Values pulled from a custom # configuration file override the the system's defaults, but can still be # overridden by the other arguments to Resolver::new. # # Explicit arguments to Resolver::new override both the system's defaults # and the values of the custom configuration file, if any. # # == Parameters # # The following arguments to Resolver::new are supported: # # * nameservers: an array reference of nameservers to query. # * searchlist: an array reference of domains. # * recurse # * debug # * domain # * port # * srcaddr # * srcport # * tcp_timeout # * udp_timeout # * retrans # * retry # * usevc # * stayopen # * igntc # * defnames # * dnsrch # * persistent_tcp # * persistent_udp # * dnssec # # For more information on any of these options, please consult the # method of the same name. # # == Disclaimer # # Part of the above documentation is taken from the one in the # Net::DNS::Resolver Perl module. # def initialize(config = {}) config.is_a?(Hash) or raise(ArgumentError, "Expected `config' to be a Hash") @config = Defaults.merge config @raw = false # New logger facility @logger = Logger.new(@config[:log_file]) @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN #------------------------------------------------------------ # Resolver configuration will be set in order from: # 1) initialize arguments # 2) ENV variables # 3) config file # 4) defaults (and /etc/resolv.conf for config) #------------------------------------------------------------ #------------------------------------------------------------ # Parsing config file #------------------------------------------------------------ parse_config_file #------------------------------------------------------------ # Parsing ENV variables #------------------------------------------------------------ parse_environment_variables #------------------------------------------------------------ # Parsing arguments #------------------------------------------------------------ config.each do |key, val| next if (key == :log_file) || (key == :config_file) begin eval "self.#{key} = val" rescue NoMethodError raise ArgumentError, "Option #{key} not valid" end end end # Get the resolver search list, returned as an array of entries. # # res.searchlist # #=> ["example.com","a.example.com","b.example.com"] # def searchlist @config[:searchlist].inspect end # Set the resolver searchlist. # +arg+ can be a single string or an array of strings. # # res.searchstring = "example.com" # res.searchstring = ["example.com","a.example.com","b.example.com"] # # Note that you can also append a new name to the searchlist. # # res.searchlist << "c.example.com" # res.searchlist # #=> ["example.com","a.example.com","b.example.com","c.example.com"] # # The default is an empty array. # def searchlist=(arg) case arg when String @config[:searchlist] = [arg] if valid? arg @logger.info "Searchlist changed to value #{@config[:searchlist].inspect}" when Array @config[:searchlist] = arg if arg.all? { |x| valid? x } @logger.info "Searchlist changed to value #{@config[:searchlist].inspect}" else raise ArgumentError, "Wrong argument format, neither String nor Array" end end # Get the list of resolver nameservers, in a dotted decimal format- # # res.nameservers # #=> ["192.168.0.1","192.168.0.2"] # def nameservers @config[:nameservers].map(&:to_s) end alias nameserver nameservers # Set the list of resolver nameservers. # +arg+ can be a single ip address or an array of addresses. # # res.nameservers = "192.168.0.1" # res.nameservers = ["192.168.0.1","192.168.0.2"] # # If you want you can specify the addresses as IPAddr instances. # # ip = IPAddr.new("192.168.0.3") # res.nameservers << ip # #=> ["192.168.0.1","192.168.0.2","192.168.0.3"] # # The default is 127.0.0.1 (localhost) # def nameservers=(arg) case arg when String begin @config[:nameservers] = [IPAddr.new(arg)] @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" rescue ArgumentError # arg is in the name form, not IP nameservers_from_name(arg) end when IPAddr @config[:nameservers] = [arg] @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" when Array @config[:nameservers] = [] arg.each do |x| val = case x when String begin IPAddr.new(x) rescue ArgumentError nameservers_from_name(arg) return end when IPAddr x else raise ArgumentError, "Wrong argument format" end @config[:nameservers] << val end @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" else raise ArgumentError, "Wrong argument format, neither String, Array nor IPAddr" end end alias_method("nameserver=", "nameservers=") # Return a string with the default domain. def domain @config[:domain].inspect end # Set the domain for the query. def domain=(name) @config[:domain] = name if valid? name end # Return the defined size of the packet. def packet_size @config[:packet_size] end # Get the port number to which the resolver sends queries. # # puts "Sending queries to port #{res.port}" # def port @config[:port] end # Set the port number to which the resolver sends queries. This can be useful # for testing a nameserver running on a non-standard port. # # res.port = 10053 # # The default is port 53. # def port=(num) (0..65_535).cover?(num) or raise(ArgumentError, "Wrong port number #{num}") @config[:port] = num @logger.info "Port number changed to #{num}" end # Get the value of the source port number. # # puts "Sending queries using port #{res.source_port}" # def source_port @config[:source_port] end alias srcport source_port # Set the local source port from which the resolver sends its queries. # # res.source_port = 40000 # # Note that if you want to set a port you need root priviledges, as # raw sockets will be used to generate packets. The class will then # generate the exception ResolverPermissionError if you're not root. # # The default is 0, which means that the port will be chosen by the # underlaying layers. # def source_port=(num) root? or raise(ResolverPermissionError, "Are you root?") (0..65_535).cover?(num) or raise(ArgumentError, "Wrong port number #{num}") @config[:source_port] = num end alias srcport= source_port= # Get the local address from which the resolver sends queries # # puts "Sending queries using source address #{res.source_address}" # def source_address @config[:source_address].to_s end alias srcaddr source_address # Get the local ipv6 address from which the resolver sends queries # def source_address_inet6 @config[:source_address_inet6].to_s end # Set the local source address from which the resolver sends its queries. # # res.source_address = "172.16.100.1" # res.source_address = IPAddr.new("172.16.100.1") # # You can specify +arg+ as either a string containing the ip address # or an instance of IPAddr class. # # Normally this can be used to force queries out a specific interface # on a multi-homed host. In this case, you should of course need to # know the addresses of the interfaces. # # Another way to use this option is for some kind of spoofing attacks # towards weak nameservers, to probe the security of your network. # This includes specifing ranged attacks such as DoS and others. For # a paper on DNS security, checks http://www.marcoceresa.com/security/ # # Note that if you want to set a non-binded source address you need # root priviledges, as raw sockets will be used to generate packets. # The class will then generate an exception if you're not root. # # The default is 0.0.0.0, meaning any local address (chosen on routing needs). # def source_address=(addr) addr.respond_to?(:to_s) or raise(ArgumentError, "Wrong address argument #{addr}") begin port = rand(1024..65_023) @logger.info "Try to determine state of source address #{addr} with port #{port}" a = TCPServer.new(addr.to_s, port) rescue SystemCallError => e case e.errno when 98 # Port already in use! @logger.warn "Port already in use" retry when 99 # Address is not valid: raw socket @raw = true @logger.warn "Using raw sockets" else raise SystemCallError, e end ensure a.close end case addr when String @config[:source_address] = IPAddr.new(string) @logger.info "Using new source address: #{@config[:source_address]}" when IPAddr @config[:source_address] = addr @logger.info "Using new source address: #{@config[:source_address]}" else raise ArgumentError, "Unknown dest_address format" end end alias srcaddr= source_address= # Return the retrasmission interval (in seconds) the resolvers has # been set on. def retry_interval @config[:retry_interval] end alias retrans retry_interval # Set the retrasmission interval in seconds. Default 5 seconds. def retry_interval=(num) num.positive? or raise(ArgumentError, "Interval must be positive") @config[:retry_interval] = num @logger.info "Retransmission interval changed to #{num} seconds" end alias retrans= retry_interval= # The number of times the resolver will try a query. # # puts "Will try a max of #{res.retry_number} queries" # def retry_number @config[:retry_number] end # Set the number of times the resolver will try a query. # Default 4 times. def retry_number=(num) num.is_a?(Integer) && (num > 0) or raise(ArgumentError, "Retry value must be a positive integer") @config[:retry_number] = num @logger.info "Retrasmissions number changed to #{num}" end alias_method('retry=', 'retry_number=') # This method will return true if the resolver is configured to # perform recursive queries. # # print "The resolver will perform a " # print res.recursive? ? "" : "not " # puts "recursive query" # def recursive? @config[:recursive] end alias recurse recursive? alias recursive recursive? # Sets whether or not the resolver should perform recursive # queries. Default is true. # # res.recursive = false # perform non-recursive query # def recursive=(bool) case bool when TrueClass, FalseClass @config[:recursive] = bool @logger.info("Recursive state changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end end alias recurse= recursive= # Return a string representing the resolver state, suitable # for printing on the screen. # # puts "Resolver state:" # puts res.state # def state str = ";; RESOLVER state:\n;; " i = 1 @config.each do |key, val| str << if (key == :log_file) || (key == :config_file) "#{key}: #{val} \t" else "#{key}: #{eval(key.to_s)} \t" end str << "\n;; " if i.even? i += 1 end str end alias print state alias inspect state # Checks whether the +defname+ flag has been activate. def defname? @config[:defname] end alias defname defname? # Set the flag +defname+ in a boolean state. if +defname+ is true, # calls to Resolver#query will append the default domain to names # that contain no dots. # Example: # # # Domain example.com # res.defname = true # res.query("machine1") # #=> This will perform a query for machine1.example.com # # Default is true. # def defname=(bool) case bool when TrueClass, FalseClass @config[:defname] = bool @logger.info("Defname state changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end end # Get the state of the dns_search flag. def dns_search @config[:dns_search] end alias dnsrch dns_search # Set the flag +dns_search+ in a boolean state. If +dns_search+ # is true, when using the Resolver#search method will be applied # the search list. Default is true. def dns_search=(bool) case bool when TrueClass, FalseClass @config[:dns_search] = bool @logger.info("DNS search state changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end end alias_method("dnsrch=", "dns_search=") # Get the state of the use_tcp flag. # def use_tcp? @config[:use_tcp] end alias usevc use_tcp? alias use_tcp use_tcp? # If +use_tcp+ is true, the resolver will perform all queries # using TCP virtual circuits instead of UDP datagrams, which # is the default for the DNS protocol. # # res.use_tcp = true # res.query "host.example.com" # #=> Sending TCP segments... # # Default is false. # def use_tcp=(bool) case bool when TrueClass, FalseClass @config[:use_tcp] = bool @logger.info("Use tcp flag changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end end alias usevc= use_tcp= def ignore_truncated? @config[:ignore_truncated] end alias ignore_truncated ignore_truncated? def ignore_truncated=(bool) case bool when TrueClass, FalseClass @config[:ignore_truncated] = bool @logger.info("Ignore truncated flag changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end end # Return an object representing the value of the stored TCP # timeout the resolver will use in is queries. This object # is an instance of the class +TcpTimeout+, and two methods # are available for printing informations: TcpTimeout#to_s # and TcpTimeout#pretty_to_s. # # Here's some example: # # puts "Timeout of #{res.tcp_timeout} seconds" # implicit to_s # #=> Timeout of 150 seconds # # puts "You set a timeout of " + res.tcp_timeout.pretty_to_s # #=> You set a timeout of 2 minutes and 30 seconds # # If the timeout is infinite, a string "infinite" will be returned. # def tcp_timeout @config[:tcp_timeout].to_s end # Set the value of TCP timeout for resolver queries that # will be performed using TCP. A value of 0 means that # the timeout will be infinite. # The value is stored internally as a +TcpTimeout+ object, see # the description for Resolver#tcp_timeout # # Default is 5 seconds. # def tcp_timeout=(secs) @config[:tcp_timeout] = TcpTimeout.new(secs) @logger.info("New TCP timeout value: #{@config[:tcp_timeout]} seconds") end # Return an object representing the value of the stored UDP # timeout the resolver will use in is queries. This object # is an instance of the class +UdpTimeout+, and two methods # are available for printing information: UdpTimeout#to_s # and UdpTimeout#pretty_to_s. # # Here's some example: # # puts "Timeout of #{res.udp_timeout} seconds" # implicit to_s # #=> Timeout of 150 seconds # # puts "You set a timeout of " + res.udp_timeout.pretty_to_s # #=> You set a timeout of 2 minutes and 30 seconds # # If the timeout is zero, a string "not defined" will # be returned. # def udp_timeout @config[:udp_timeout].to_s end # Set the value of UDP timeout for resolver queries that # will be performed using UDP. A value of 0 means that # the timeout will not be used, and the resolver will use # only +retry_number+ and +retry_interval+ parameters. # # Default is 5 seconds. # # The value is stored internally as a +UdpTimeout+ object, see # the description for Resolver#udp_timeout. # def udp_timeout=(secs) @config[:udp_timeout] = UdpTimeout.new(secs) @logger.info("New UDP timeout value: #{@config[:udp_timeout]} seconds") end # Set a new log file for the logger facility of the resolver # class. Could be a file descriptor too: # # res.log_file = $stderr # # Note that a new logging facility will be create, destroing # the old one, which will then be impossibile to recover. # def log_file=(log) @config[:log_file] = log @logger = Logger.new(@config[:log_file]) @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN end # This one permits to have a personal logger facility to handle # resolver messages, instead of new built-in one, which is set up # for a +$stdout+ (or +$stderr+) use. # # If you want your own logging facility you can create a new instance # of the +Logger+ class: # # log = Logger.new("/tmp/resolver.log","weekly",2*1024*1024) # log.level = Logger::DEBUG # log.progname = "ruby_resolver" # # and then pass it to the resolver: # # res.logger = log # # Note that this will destroy the precedent logger. # def logger=(logger) logger.is_a?(Logger) or raise(ArgumentError, "Argument must be an instance of Logger class") @logger = logger end # Set the log level for the built-in logging facility. # # The log level can be one of the following: # # - +Net::DNS::DEBUG+ # - +Net::DNS::INFO+ # - +Net::DNS::WARN+ # - +Net::DNS::ERROR+ # - +Net::DNS::FATAL+ # # Note that if the global variable $DEBUG is set (like when the # -d switch is used at the command line) the logger level is # automatically set at DEGUB. # # For further informations, see Logger documentation in the # Ruby standard library. # def log_level=(level) @logger.level = level end # Performs a DNS query for the given name, applying the searchlist if # appropriate. The search algorithm is as follows: # # 1. If the name contains at least one dot, try it as is. # 2. If the name doesn't end in a dot then append each item in the search # list to the name. This is only done if +dns_search+ is true. # 3. If the name doesn't contain any dots, try it as is. # # The record type and class can be omitted; they default to +A+ and +IN+. # # packet = res.search('mailhost') # packet = res.search('mailhost.example.com') # packet = res.search('example.com', Net::DNS::MX) # packet = res.search('user.passwd.example.com', Net::DNS::TXT, Net::DNS::HS) # # If the name is an IP address (Ipv4 or IPv6), in the form of a string # or a +IPAddr+ object, then an appropriate PTR query will be performed: # # ip = IPAddr.new("172.16.100.2") # packet = res.search(ip) # packet = res.search("192.168.10.254") # # Returns a Net::DNS::Packet object. If you need to examine the response packet # whether it contains any answers or not, use the Resolver#query method instead. # def search(name, type = Net::DNS::A, cls = Net::DNS::IN) return query(name, type, cls) if name.class == IPAddr # If the name contains at least one dot then try it as is first. if name.include? "." @logger.debug "Search(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" ans = query(name, type, cls) return ans if ans.header.anCount > 0 end # If the name doesn't end in a dot then apply the search list. if name !~ /\.$/ && @config[:dns_search] @config[:searchlist].each do |domain| newname = name + "." + domain @logger.debug "Search(#{newname},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" ans = query(newname, type, cls) return ans if ans.header.anCount > 0 end end # Finally, if the name has no dots then try it as is. @logger.debug "Search(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" query(name + ".", type, cls) end # Performs a DNS query for the given name; the search list # is not applied. If the name doesn't contain any dots and # +defname+ is true then the default domain will be appended. # # The record type and class can be omitted; they default to +A+ # and +IN+. If the name looks like an IP address (IPv4 or IPv6), # then an appropriate PTR query will be performed. # # packet = res.query('mailhost') # packet = res.query('mailhost.example.com') # packet = res.query('example.com', Net::DNS::MX) # packet = res.query('user.passwd.example.com', Net::DNS::TXT, Net::DNS::HS) # # If the name is an IP address (Ipv4 or IPv6), in the form of a string # or a +IPAddr+ object, then an appropriate PTR query will be performed: # # ip = IPAddr.new("172.16.100.2") # packet = res.query(ip) # packet = res.query("192.168.10.254") # # Returns a Net::DNS::Packet object. If you need to examine the response # packet whether it contains any answers or not, use the Resolver#query # method instead. # def query(name, type = Net::DNS::A, cls = Net::DNS::IN) return send(name, type, cls) if name.class == IPAddr # If the name doesn't contain any dots then append the default domain. if name !~ /\./ && name !~ /:/ && @config[:defname] name += "." + @config[:domain] end @logger.debug "Query(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" send(name, type, cls) end # Performs a DNS query for the given name. Neither the # searchlist nor the default domain will be appended. # # The argument list can be either a Net::DNS::Packet object # or a name string plus optional type and class, which if # omitted default to +A+ and +IN+. # # Returns a Net::DNS::Packet object. # # # Executes the query with a +Packet+ object # send_packet = Net::DNS::Packet.new("host.example.com", Net::DNS::NS, Net::DNS::HS) # packet = res.query(send_packet) # # # Executes the query with a host, type and cls # packet = res.query("host.example.com") # packet = res.query("host.example.com", Net::DNS::NS) # packet = res.query("host.example.com", Net::DNS::NS, Net::DNS::HS) # # If the name is an IP address (Ipv4 or IPv6), in the form of a string # or a IPAddr object, then an appropriate PTR query will be performed: # # ip = IPAddr.new("172.16.100.2") # packet = res.query(ip) # # packet = res.query("172.16.100.2") # # Use +packet.header.ancount+ or +packet.answer+ to find out if there # were any records in the answer section. # def query(argument, type = Net::DNS::A, cls = Net::DNS::IN) !@config[:nameservers].empty? or raise(Resolver::Error, "No nameservers specified!") method = :query_udp packet = if argument.is_a? Net::DNS::Packet argument else make_query_packet(argument, type, cls) end # Store packet_data for performance improvements, # so methods don't keep on calling Packet#data packet_data = packet.data packet_size = packet_data.size # Choose whether use TCP, UDP or RAW if packet_size > @config[:packet_size] # Must use TCP, either plain or raw if @raw # Use raw sockets? @logger.info "Sending #{packet_size} bytes using TCP over RAW socket" method = :send_raw_tcp else @logger.info "Sending #{packet_size} bytes using TCP" method = :query_tcp end else # Packet size is inside the boundaries if @raw # Use raw sockets? @logger.info "Sending #{packet_size} bytes using UDP over RAW socket" method = :send_raw_udp elsif use_tcp? # User requested TCP @logger.info "Sending #{packet_size} bytes using TCP" method = :query_tcp else # Finally use UDP @logger.info "Sending #{packet_size} bytes using UDP" end end if type == Net::DNS::AXFR if @raw @logger.info "AXFR query, switching to TCP over RAW socket" method = :send_raw_tcp else @logger.info "AXFR query, switching to TCP" method = :query_tcp end end ans = send(method, packet, packet_data) unless ans message = "No response from nameservers list" @logger.fatal(message) raise NoResponseError, message end @logger.info "Received #{ans[0].size} bytes from #{ans[1][2] + ':' + ans[1][1].to_s}" response = Net::DNS::Packet.parse(ans[0], ans[1]) if response.header.truncated? && !ignore_truncated? @logger.warn "Packet truncated, retrying using TCP" self.use_tcp = true begin return query(argument, type, cls) ensure self.use_tcp = false end end response end # Performs a zone transfer for the zone passed as a parameter. # # It is actually only a wrapper to a send with type set as Net::DNS::AXFR, # since it is using the same infrastucture. # def axfr(name, cls = Net::DNS::IN) @logger.info "Requested AXFR transfer, zone #{name} class #{cls}" query(name, Net::DNS::AXFR, cls) end # Performs an MX query for the domain name passed as parameter. # # It actually uses the same methods a normal Resolver query would # use, but automatically sort the results based on preferences # and returns an ordered array. # # res = Net::DNS::Resolver.new # res.mx("google.com") # def mx(name, cls = Net::DNS::IN) arr = [] query(name, Net::DNS::MX, cls).answer.each do |entry| arr << entry if entry.type == 'MX' end arr.sort_by(&:preference) end private # Parses a configuration file specified as the argument. def parse_config_file if self.class.platform_windows? require 'win32/resolv' arr = Win32::Resolv.get_resolv_info self.domain = arr[0][0] self.nameservers = arr[1] else nameservers = [] IO.foreach(@config[:config_file]) do |line| line.gsub!(/\s*[;#].*/, "") next unless line =~ /\S/ case line when /^\s*domain\s+(\S+)/ self.domain = Regexp.last_match(1) when /^\s*search\s+(.*)/ self.searchlist = Regexp.last_match(1).split(" ") when /^\s*nameserver\s+(.*)/ nameservers << Regexp.last_match(1).split(" ") end end self.nameservers = nameservers.flatten end end # Parses environment variables. def parse_environment_variables if ENV['RES_NAMESERVERS'] self.nameservers = ENV['RES_NAMESERVERS'].split(" ") end if ENV['RES_SEARCHLIST'] self.searchlist = ENV['RES_SEARCHLIST'].split(" ") end if ENV['LOCALDOMAIN'] self.domain = ENV['LOCALDOMAIN'] end if ENV['RES_OPTIONS'] ENV['RES_OPTIONS'].split(" ").each do |opt| name, val = opt.split(":") begin eval("self.#{name} = #{val}") rescue NoMethodError raise ArgumentError, "Invalid ENV option #{name}" end end end end def nameservers_from_name(arg) arr = [] arg.split(" ").each do |name| Resolver.new.search(name).each_address do |ip| arr << ip end end @config[:nameservers] << arr end def make_query_packet(string, type, cls) case string when IPAddr name = string.reverse type = Net::DNS::PTR @logger.warn "PTR query required for address #{string}, changing type to PTR" when /\d/ # Contains a number, try to see if it's an IP or IPv6 address begin name = IPAddr.new(string.chomp(".")).reverse type = Net::DNS::PTR rescue ArgumentError name = string if valid? string end else name = string if valid? string end # Create the packet packet = Net::DNS::Packet.new(name, type, cls) if packet.query? packet.header.recursive = @config[:recursive] ? 1 : 0 end # DNSSEC and TSIG stuff to be inserted here packet end def query_tcp(packet, packet_data) ans = nil length = [packet_data.size].pack("n") @config[:nameservers].each do |ns| begin buffer = "" socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) socket.bind(Socket.pack_sockaddr_in(@config[:source_port], @config[:source_address].to_s)) sockaddr = Socket.pack_sockaddr_in(@config[:port], ns.to_s) @config[:tcp_timeout].timeout do socket.connect(sockaddr) @logger.info "Contacting nameserver #{ns} port #{@config[:port]}" socket.write(length + packet_data) ans = socket.recv(Net::DNS::INT16SZ) len = ans.unpack("n")[0] @logger.info "Receiving #{len} bytes..." if len == 0 @logger.warn "Receiving 0 length packet from nameserver #{ns}, trying next." next end while buffer.size < len left = len - buffer.size temp, from = socket.recvfrom(left) buffer += temp end unless buffer.size == len @logger.warn "Malformed packet from nameserver #{ns}, trying next." next end end return [buffer, ["", @config[:port], ns.to_s, ns.to_s]] rescue TimeoutError @logger.warn "Nameserver #{ns} not responding within TCP timeout, trying next one" next ensure socket.close end end ans end def query_udp(packet, packet_data) socket4 = UDPSocket.new socket4.bind(@config[:source_address].to_s, @config[:source_port]) socket6 = UDPSocket.new(Socket::AF_INET6) socket6.bind(@config[:source_address_inet6].to_s, @config[:source_port]) ans = nil response = "" @config[:nameservers].each do |ns| begin @config[:udp_timeout].timeout do @logger.info "Contacting nameserver #{ns} port #{@config[:port]}" ans = if ns.ipv6? socket6.send(packet_data, 0, ns.to_s, @config[:port]) socket6.recvfrom(@config[:packet_size]) else socket4.send(packet_data, 0, ns.to_s, @config[:port]) socket4.recvfrom(@config[:packet_size]) end end break if ans rescue TimeoutError @logger.warn "Nameserver #{ns} not responding within UDP timeout, trying next one" next end end ans end # FIXME: a ? method should never raise. def valid?(name) name !~ /[^-\w\.]/ or raise(ArgumentError, "Invalid domain name #{name}") true end end end end gitlab-net-dns-v0.9.1/lib/net/dns/resolver/000077500000000000000000000000001355037036000205025ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/lib/net/dns/resolver/socks.rb000066400000000000000000000067121355037036000221570ustar00rootroot00000000000000require 'socket' require 'ipaddr' class RawSocket # :nodoc: @@id_arr = [] def initialize(src_addr, dest_addr) # Define socket begin @socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW rescue SystemCallError => e raise SystemCallError, "You must be root to use raw sockets! #{e}" end @socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1 # Checks addresses @src_addr = check_addr src_addr @dest_addr = check_addr dest_addr # Source and destination port are zero @src_port = 0 @dest_port = 0 # Set correct protocol version in the header @version = @dest_addr.ipv4? ? "0100" : "0110" # Total lenght: must be overridden by subclasses @tot_lenght = 20 # Protocol: must be overridden by subclasses @protocol = 1 # ICMP by default # Generate a new id # @id = genID @id = 1234 # Generate peer sockaddr @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end def send(payload = '') packet = make_ip_header([ [@version + '0101', 'B8'], # version, hlen [0, 'C'], # tos [@tot_lenght + payload.size, 'n'], # total len [@id, 'n'], # id [0, 'n'], # flags, offset [64, 'C'], # ttl [@protocol, 'C'], # protocol [0, 'n'], # checksum [@src_addr.to_i, 'N'], # source [@dest_addr.to_i, 'N'], # destination ]) packet << make_transport_header(payload.size) packet << [payload].pack("a*") @socket.send(packet, 0, @to) end private def check_addr(addr) case addr when String IPAddr.new(addr) when IPAddr addr else raise ArgumentError, "Wrong address format: #{addr}" end end def check_port(port) if (1..65_535).cover?(port) && port.is_a?(Integer) port else raise ArgumentError, "Port #{port} not valid" end end def genID while @@id_arr.include?(q = rand(65_535)) end @@id_arr.push(q) q end def ipchecksum(data) checksum = data.unpack("n*").inject(0) { |s, x| s + x } ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff end def make_ip_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data_str = data.pack(template) checksum = ipchecksum(data_str) data[-3] = checksum data.pack(template) end def make_transport_header "" end end class UdpRawSocket < RawSocket # :nodoc: def initialize(src_addr, src_port, dest_addr, dest_port) super(src_addr, dest_addr) # Check ports @src_port = check_port src_port @dest_port = check_port dest_port # Total lenght: must be overridden by subclasses @tot_lenght = 20 + 8 # 8 bytes => UDP Header # Protocol: must be overridden by subclasses @protocol = 17 # UDP protocol @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end private def make_udp_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data.pack(template) end def make_transport_header(pay_size) make_udp_header([ [@src_port, 'n'], # source port [@dest_port, 'n'], # destination port [8 + pay_size, 'n'], # len [0, 'n'] # checksum (mandatory) ]) end end gitlab-net-dns-v0.9.1/lib/net/dns/resolver/timeouts.rb000066400000000000000000000033341355037036000227030ustar00rootroot00000000000000require 'timeout' module Net # :nodoc: module DNS class Resolver class DnsTimeout attr_reader :seconds def initialize(seconds) if seconds.is_a?(Numeric) && seconds >= 0 @seconds = seconds else raise ArgumentError, "Invalid value for tcp timeout" end end # Returns a string representation of the timeout corresponding # to the number of @seconds. def to_s @seconds == 0 ? @output.to_s : @seconds.to_s end def pretty_to_s transform(@seconds) end # Executes the method's block. If the block execution terminates before +sec+ # seconds has passed, it returns true. If not, it terminates the execution # and raises Timeout::Error. # If @seconds is 0 or nil, no timeout is set. def timeout(&block) raise LocalJumpError, "no block given" unless block_given? Timeout.timeout(@seconds, &block) end private def transform(secs) case secs when 0 to_s when 1..59 "#{secs} seconds" when 60..3559 "#{secs / 60} minutes and #{secs % 60} seconds" else hours = secs / 3600 secs -= (hours * 3600) "#{hours} hours, #{secs / 60} minutes and #{secs % 60} seconds" end end end class TcpTimeout < DnsTimeout def initialize(seconds) @output = "infinite" super end end class UdpTimeout < DnsTimeout def initialize(seconds) @output = "not defined" super end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr.rb000066400000000000000000000260151355037036000176150ustar00rootroot00000000000000require 'ipaddr' require_relative 'names' require_relative 'rr/types' require_relative 'rr/classes' %w[a aaaa cname hinfo mr mx ns ptr soa srv txt].each do |file| require_relative "rr/#{file}" end module Net module DNS # # = Net::DNS::RR - DNS Resource Record class # # The Net::DNS::RR is the base class for DNS Resource # Record (RR) objects. A RR is a pack of data that represents # resources for a DNS zone. The form in which this data is # shows can be drawed as follow: # # "name ttl class type data" # # The +name+ is the name of the resource, like an canonical # name for an +A+ record (internet ip address). The +ttl+ is the # time to live, expressed in seconds. +type+ and +class+ are # respectively the type of resource (+A+ for ip addresses, +NS+ # for nameservers, and so on) and the class, which is almost # always +IN+, the Internet class. At the end, +data+ is the # value associated to the name for that particular type of # resource record. An example: # # # A record for IP address # "www.example.com 86400 IN A 172.16.100.1" # # # NS record for name server # "www.example.com 86400 IN NS ns.example.com" # # A new RR object can be created in 2 ways: passing a string # such the ones above, or specifying each field as the pair # of an hash. See the Net::DNS::RR.new method for details. # class RR include Names # Base error class. class Error < StandardError end # Error in parsing binary data, maybe from a malformed packet. class DataError < Error end # Regexp matching an RR string RR_REGEXP = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s+(" + Net::DNS::RR::Classes.regexp + "|CLASS\\d+)?\\s*(" + Net::DNS::RR::Types.regexp + "|TYPE\\d+)?\\s*(.*)$", Regexp::IGNORECASE) # Dimension of the sum of class, type, TTL and rdlength fields in a # RR portion of the packet, in bytes RRFIXEDSZ = 10 # Create a new instance of Net::DNS::RR class, or an instance of # any of the subclass of the appropriate type. # # Argument can be a string or an hash. With a sting, we can pass # a RR resource record in the canonical format: # # a = Net::DNS::RR.new("foo.example.com. 86400 A 10.1.2.3") # mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.") # cname = Net::DNS::RR.new("www.example.com 300 IN CNAME www1.example.com") # txt = Net::DNS::RR.new('baz.example.com 3600 HS TXT "text record"') # # Incidentally, +a+, +mx+, +cname+ and +txt+ objects will be instances of # respectively Net::DNS::RR::A, Net::DNS::RR::MX, Net::DNS::RR::CNAME and # Net::DNS::RR::TXT classes. # # The name and RR data are required; all other informations are optional. # If omitted, the +TTL+ defaults to 10800, +type+ default to +A+ and the RR class # defaults to +IN+. Omitting the optional fields is useful for creating the # empty RDATA sections required for certain dynamic update operations. # All names must be fully qualified. The trailing dot (.) is optional. # # The preferred method is however passing an hash with keys and values: # # rr = Net::DNS::RR.new( # :name => "foo.example.com", # :ttl => 86400, # :cls => "IN", # :type => "A", # :address => "10.1.2.3" # ) # # rr = Net::DNS::RR.new( # :name => "foo.example.com", # :rdata => "10.1.2.3" # ) # # Name and data are required; all the others fields are optionals like # we've seen before. The data field can be specified either with the # right name of the resource (+:address+ in the example above) or with # the generic key +:rdata+. Consult documentation to find the exact name # for the resource in each subclass. # def initialize(arg) instance = case arg when String new_from_string(arg) when Hash new_from_hash(arg) else raise ArgumentError, "Invalid argument, must be a RR string or an hash of values" end if @type.to_s == "ANY" @cls = Net::DNS::RR::Classes.new("IN") end build_pack set_type instance end # Return a new RR object of the correct type (like Net::DNS::RR::A # if the type is A) from a binary string, usually obtained from # network stream. # # This method is used when parsing a binary packet by the Packet # class. # def self.parse(data) o = allocate obj, offset = o.send(:new_from_binary, data, 0) obj end # Same as RR.parse, but takes an entire packet binary data to # perform name expansion. Default when analizing a packet # just received from a network stream. # # Return an instance of appropriate class and the offset # pointing at the end of the data parsed. # def self.parse_packet(data, offset) o = allocate o.send(:new_from_binary, data, offset) end attr_reader :name attr_reader :ttl # Type accessor def type @type.to_s end # Class accessor def cls @cls.to_s end def value get_inspect end # Data belonging to that appropriate class, # not to be used (use real accessors instead) attr_reader :rdata # Return the RR object in binary data format, suitable # for using in network streams. # # raw_data = rr.data # puts "RR is #{raw_data.size} bytes long" # def data str = pack_name(@name) str + [@type.to_i, @cls.to_i, ttl, @rdlength].pack("n2 N n") + get_data end # Return the RR object in binary data format, suitable # for using in network streams, with names compressed. # Must pass as arguments the offset inside the packet # and an hash of compressed names. # # This method is to be used in other classes and is # not intended for user space programs. # # TO FIX in one of the future releases # def comp_data(offset, compnames) str, offset, names = dn_comp(@name, offset, compnames) str += [@type.to_i, @cls.to_i, ttl, @rdlength].pack("n2 N n") offset += Net::DNS::RRFIXEDSZ [str, offset, names] end # Returns a human readable representation of this record. # The value is always a String. # # mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.") # #=> example.com. 7200 IN MX 10 mailhost.example.com. # def inspect to_s end # Returns a String representation of this record. # # mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.") # mx.to_s # #=> "example.com. 7200 IN MX 10 mailhost.example.com." # def to_s items = to_a.map(&:to_s) if @name.size < 24 items.pack("A24 A8 A8 A8 A*") else items.join(" ") end.to_s end # Returns an Array with all the attributes for this record. # # mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.") # mx.to_a # #=> ["example.com.", 7200, "IN", "MX", "10 mailhost.example.com."] # def to_a [name, ttl, cls.to_s, type.to_s, value] end private def new_from_string(rrstring) unless rrstring =~ RR_REGEXP raise ArgumentError, "Format error for RR string (maybe CLASS and TYPE not valid?)" end # Name of RR - mandatory begin @name = Regexp.last_match(1).downcase rescue NoMethodError raise ArgumentError, "Missing name field in RR string #{rrstring}" end # Time to live for RR, default 3 hours @ttl = Regexp.last_match(2) ? Regexp.last_match(2).to_i : 10_800 # RR class, default to IN @cls = Net::DNS::RR::Classes.new Regexp.last_match(3) # RR type, default to A @type = Net::DNS::RR::Types.new Regexp.last_match(4) # All the rest is data @rdata = Regexp.last_match(5) ? Regexp.last_match(5).strip : "" if self.class == Net::DNS::RR Net::DNS::RR.const_get(@type.to_s).new(rrstring) else subclass_new_from_string(@rdata) self.class end end def new_from_hash(args) # Name field is mandatory unless args.key? :name raise ArgumentError, ":name field is mandatory" end @name = args[:name].downcase @ttl = args[:ttl] ? args[:ttl].to_i : 10_800 # Default 3 hours @type = Net::DNS::RR::Types.new args[:type] @cls = Net::DNS::RR::Classes.new args[:cls] @rdata = args[:rdata] ? args[:rdata].strip : "" @rdlength = args[:rdlength] || @rdata.size if self.class == Net::DNS::RR Net::DNS::RR.const_get(@type.to_s).new(args) else hash = args.delete_if { |k, _| %i[name ttl type cls].include?(k) } if hash.key? :rdata subclass_new_from_string(hash[:rdata]) else subclass_new_from_hash(hash) end self.class end end def new_from_binary(data, offset) if self.class == Net::DNS::RR temp = dn_expand(data, offset)[1] type = Net::DNS::RR::Types.new data.unpack("@#{temp} n")[0] (eval "Net::DNS::RR::#{type}").parse_packet(data, offset) else @name, offset = dn_expand(data, offset) rrtype, cls, @ttl, @rdlength = data.unpack("@#{offset} n2 N n") @type = Net::DNS::RR::Types.new rrtype @cls = Net::DNS::RR::Classes.new cls offset += RRFIXEDSZ offset = subclass_new_from_binary(data, offset) build_pack set_type [self, offset] end end # Methods to be overridden by subclasses def subclass_new_from_array(arr) end def subclass_new_from_string(str) end def subclass_new_from_hash(hash) end def subclass_new_from_binary(data, offset) end def build_pack end def get_inspect @rdata end def get_data @rdata end def set_type # TODO: Here we should probably # raise NotImplementedError # if we want the method to be implemented in any subclass. end def self.new(*args) o = allocate obj = o.send(:initialize, *args) if self == Net::DNS::RR obj else o end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/000077500000000000000000000000001355037036000172645ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/lib/net/dns/rr/a.rb000066400000000000000000000062221355037036000200330ustar00rootroot00000000000000module Net module DNS class RR # # = IPv4 Address Record (A) # # Class for DNS IPv4 Address (A) resource records. # # The resource data is an IPv4 (i.e. 32 bit long) address, # hold in the instance variable +address+. # # a = Net::DNS::RR::A.new("localhost.movie.edu. 360 IN A 127.0.0.1") # # a = Net::DNS::RR::A.new(:name => "localhost.movie.edu.", # :ttl => 360, # :cls => Net::DNS::IN, # :type => Net::DNS::A, # :address => "127.0.0.1" ) # # When computing binary data to transmit the RR, the RDATA section is an # Internet address expressed as four decimal numbers separated by dots # without any embedded space (e.g. "10.2.0.52" or "192.0.5.6"). # class A < RR # Gets the current IPv4 address for this record. # # Returns an instance of IPAddr. attr_reader :address # Assigns a new IPv4 address to this record, which can be in the # form of a String or an IPAddr object. # # Examples # # a.address = "192.168.0.1" # a.address = IPAddr.new("10.0.0.1") # # Returns the new allocated instance of IPAddr. def address=(string_or_ipaddr) @address = check_address(string_or_ipaddr) build_pack @address end # Gets the standardized value for this record, # represented by the value of address. # # Returns a String. def value address.to_s end private def subclass_new_from_hash(options) if options.key?(:address) @address = check_address(options[:address]) elsif options.key?(:rdata) @address = check_address(options[:rdata]) else raise ArgumentError, ":address or :rdata field is mandatory" end end def subclass_new_from_string(str) @address = check_address(str) end def subclass_new_from_binary(data, offset) a, b, c, d = data.unpack("@#{offset} CCCC") @address = IPAddr.new("#{a}.#{b}.#{c}.#{d}") offset + 4 end def set_type @type = Net::DNS::RR::Types.new("A") end def get_inspect value end def check_address(input) address = case input when IPAddr input # Address in numeric form when Integer IPAddr.new(input, Socket::AF_INET) when String IPAddr.new(input) else raise ArgumentError, "Invalid IP address `#{input}'" end unless address.ipv4? raise(ArgumentError, "Must specify an IPv4 address") end address end def build_pack @address_pack = @address.hton @rdlength = @address_pack.size end def get_data @address_pack end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/aaaa.rb000066400000000000000000000043621355037036000205010ustar00rootroot00000000000000module Net module DNS class RR # # = IPv6 Address Record (AAAA) # # Class for DNS IPv6 Address (AAAA) resource records. # class AAAA < RR # Gets the current IPv6 address for this record. # # Returns an instance of IPAddr. attr_reader :address # Assigns a new IPv6 address to this record, which can be in the # form of a String or an IPAddr object. # # Examples # # a.address = "192.168.0.1" # a.address = IPAddr.new("10.0.0.1") # # Returns the new allocated instance of IPAddr. def address=(string_or_ipaddr) @address = check_address(string_or_ipaddr) build_pack @address end # Gets the standardized value for this record, # represented by the value of address. # # Returns a String. def value address.to_s end private def subclass_new_from_hash(options) if options.key?(:address) @address = check_address(options[:address]) else raise ArgumentError, ":address field is mandatory" end end def subclass_new_from_string(str) @address = check_address(str) end def subclass_new_from_binary(data, offset) tokens = data.unpack("@#{offset} n8") @address = IPAddr.new(format("%x:%x:%x:%x:%x:%x:%x:%x", *tokens)) offset + 16 end def set_type @type = Net::DNS::RR::Types.new("AAAA") end def get_inspect value end def check_address(input) address = case input when IPAddr input when String IPAddr.new(input) else raise ArgumentError, "Invalid IP address `#{input}'" end unless address.ipv6? raise(ArgumentError, "Must specify an IPv6 address") end address end def build_pack @address_pack = @address.hton @rdlength = @address_pack.size end def get_data @address_pack end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/classes.rb000066400000000000000000000067211355037036000212540ustar00rootroot00000000000000module Net module DNS class RR # # = Net::DNS::Classes # # This is an auxiliary class to handle Net::DNS::RR # class field in a DNS packet. # class Classes # Hash with the values of each RR class stored with the # respective id number. CLASSES = { 'IN' => 1, # RFC 1035 'CH' => 3, # RFC 1035 'HS' => 4, # RFC 1035 'NONE' => 254, # RFC 2136 'ANY' => 255, # RFC 1035 }.freeze # The default value when class is nil in Resource Records @@default = CLASSES["IN"] # Creates a new object representing an RR class. Performs some # checks on the argument validity too. Il +cls+ is +nil+, the # default value is +ANY+ or the one set with Classes.default= def initialize(cls) case cls when String initialize_from_str(cls) when Integer initialize_from_num(cls) when nil initialize_from_num(@@default) end if @str.nil? || @num.nil? raise ArgumentError, "Unable to create a `Classes' from `#{cls}'" end end # Returns the class in number format # (default for normal use) # # FIXME: inspect must return a String. # def inspect @num end # Returns the class in string format, # ex. "IN" or "CH" or such a string. def to_s @str.to_s end # Returns the class in numeric format, # usable by the pack methods for data transfers. def to_i @num.to_i end def self.default @@default end # Be able to control the default class to assign when # cls argument is +nil+. Default to +IN+ def self.default=(str) if CLASSES[str] @@default = CLASSES[str] else raise ArgumentError, "Unknown class `#{str}'" end end # Returns whether cls is a valid RR class. # # Net::DNS::RR::Classes.valid?("IN") # # => true # Net::DNS::RR::Classes.valid?(1) # # => true # Net::DNS::RR::Classes.valid?("Q") # # => false # Net::DNS::RR::Classes.valid?(256) # # => false # Net::DNS::RR::Classes.valid?(Hash.new) # # => ArgumentError # # FIXME: valid? should never raise. # # ==== Raises # ArgumentError:: if cls isn't either a String or a Fixnum # def self.valid?(cls) case cls when String CLASSES.key?(cls) when Integer CLASSES.invert.key?(cls) else raise ArgumentError, "Wrong cls class: #{cls.class}" end end # Gives in output the keys from the +Classes+ hash # in a format suited for regexps def self.regexp CLASSES.keys.sort.join("|") end private # Initialize a new instance from a Class name. def initialize_from_str(str) key = str.to_s.upcase @num = CLASSES[key] @str = key end # Initialize a new instance from the Class value. def initialize_from_num(num) key = num.to_i @num = key @str = CLASSES.invert[key] end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/cname.rb000066400000000000000000000032521355037036000206760ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR # # = Canonical Name Record (CNAME) # # Class for DNS CNAME resource records. # # A CNAME record maps an alias or nickname to the real or Canonical name # which may lie outside the current zone. # Canonical means expected or real name. # class CNAME < RR # Gets the canonical name value. # # Returns a String. attr_reader :cname # Gets the standardized value for this record, # represented by the value of cname. # # Returns a String. def value cname.to_s end private def subclass_new_from_hash(options) if options.key?(:cname) @cname = check_name(options[:cname]) else raise ArgumentError, ":cname field is mandatory" end end def subclass_new_from_string(str) @cname = check_name(str) end def subclass_new_from_binary(data, offset) @cname, offset = dn_expand(data, offset) offset end def set_type @type = Net::DNS::RR::Types.new("CNAME") end def get_inspect value end def check_name(input) name = input.to_s unless name =~ /(\w\.?)+\s*$/ && name =~ /[a-zA-Z]/ raise ArgumentError, "Invalid Canonical Name `#{name}'" end name end def build_pack @cname_pack = pack_name(@cname) @rdlength = @cname_pack.size end def get_data @cname_pack end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/hinfo.rb000066400000000000000000000047231355037036000207220ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR # # = System Information Record (HINFO) # # Class for DNS HINFO resource records. # # Allows definition of the Hardware type and Operating System (OS) in use at a host. # For security reasons these records are rarely used on public servers. # If a space exists in the field it must be enclosed in quotes. # Single space between CPU and OS parameters. # class HINFO < RR # Gets the CPU value. # # Returns a String. attr_reader :cpu # Gets the OS value. # # Returns a String. attr_reader :os # Gets the standardized value for this record, # represented by the value of cpu and os. # # Returns a String. def value %Q("#{cpu}" "#{os}") end # Gets a list of all the attributes for this record. # # Returns an Array of values. def to_a [nil, nil, cls.to_s, type.to_s, value] end private def subclass_new_from_hash(options) if options.key?(:cpu) && options.key?(:os) @cpu = options[:cpu] @os = options[:os] else raise ArgumentError, ":cpu and :os fields are mandatory" end end def subclass_new_from_string(str) @cpu, @os = check_hinfo(str) end def subclass_new_from_binary(data, offset) len = data.unpack("@#{offset} C").first offset += 1 @cpu = data[offset..(offset + len)] offset += len len = data.unpack("@#{offset} C").first offset += 1 @os = data[offset..(offset + len)] offset += len end def set_type @type = Net::DNS::RR::Types.new("HINFO") end def get_inspect value end def check_hinfo(input) if input.to_s.strip =~ /^(?:["']?(.*?)["']?)\s+(?:["']?(.*?)["']?)$/ [Regexp.last_match(1), Regexp.last_match(2)] else raise ArgumentError, "Invalid HINFO Section `#{input}'" end end def build_pack @hinfo_pack = "" @hinfo_pack += [cpu.size].pack("C") + cpu @hinfo_pack += [os.size].pack("C") + os @rdlength = @hinfo_pack.size end def get_data @hinfo_pack end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/mr.rb000066400000000000000000000027271355037036000202370ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR # # = Mail Rename Record (MR) # # Class for DNS MR resource records. # class MR < RR # Gets the newname value. # # Returns a String. attr_reader :newname # Gets the standardized value for this record, # represented by the value of newname. # # Returns a String. def value newname.to_s end private def subclass_new_from_hash(options) if options.key?(:newname) @newname = check_name(options[:newname]) else raise ArgumentError, ":newname field is mandatory" end end def subclass_new_from_string(str) @newname = check_name(str) end def subclass_new_from_binary(data, offset) @newname = dn_expand(data, offset) offset end def set_type @type = Net::DNS::RR::Types.new("MR") end def get_inspect value end def check_name(input) name = input.to_s unless name =~ /(\w\.?)+\s*$/ raise ArgumentError, "Invalid Domain Name `#{name}'" end name end def build_pack @newname_pack = pack_name(@newname) @rdlength = @newname_pack.size end def get_data @newname_pack end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/mx.rb000066400000000000000000000041111355037036000202320ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR # # = Mail Exchange Record (MX) # # Class for DNS MX resource records. # # A MX record specifies the name and relative preference of mail servers # (mail exchangers in the DNS jargon) for the zone. # The MX RR is used by SMTP (Mail) Agents to route mail for the domain. # class MX < RR # Gets the preference value. # # Returns an Integer. attr_reader :preference # Gets the exchange value. # # Returns a String. attr_reader :exchange # Gets the standardized value for this record, # represented by the value of preference and exchange. # # Returns a String. def value "#{preference} #{exchange}" end private def subclass_new_from_hash(options) if options.key?(:preference) && options.key?(:exchange) @preference = options[:preference].to_i @exchange = options[:exchange] else raise ArgumentError, ":preference and :exchange fields are mandatory" end end def subclass_new_from_string(str) @preference, @exchange = check_mx(str) end def subclass_new_from_binary(data, offset) @preference = data.unpack("@#{offset} n")[0] offset += 2 @exchange, offset = dn_expand(data, offset) offset end def set_type @type = Net::DNS::RR::Types.new("MX") end def get_inspect value end def check_mx(input) str = input.to_s unless str.strip =~ /^(\d+)\s+(\S+)$/ raise ArgumentError, "Invalid MX section `#{str}'" end [Regexp.last_match(1).to_i, Regexp.last_match(2)] end def build_pack @mx_pack = [@preference].pack("n") + pack_name(@exchange) @rdlength = @mx_pack.size end def get_data @mx_pack end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/ns.rb000066400000000000000000000027711355037036000202400ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR # # = Name Server Record (NS) # # Class for DNS NS resource records. # class NS < RR # Gets the name server value. # # Returns a String. attr_reader :nsdname # Gets the standardized value for this record, # represented by the value of nsdname. # # Returns a String. def value nsdname.to_s end private def subclass_new_from_hash(options) if options.key?(:nsdname) @nsdname = check_name(options[:nsdname]) else raise ArgumentError, ":nsdname field is mandatory" end end def subclass_new_from_string(str) @nsdname = check_name(str) end def subclass_new_from_binary(data, offset) @nsdname, offset = dn_expand(data, offset) offset end def set_type @type = Net::DNS::RR::Types.new("NS") end def get_inspect value end def check_name(input) name = input.to_s unless name =~ /(\w\.?)+\s*$/ && name =~ /[a-zA-Z]/ raise ArgumentError, "Invalid Name Server `#{name}'" end name end def build_pack @nsdname_pack = pack_name(@nsdname) @rdlength = @nsdname_pack.size end def get_data @nsdname_pack end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/null.rb000066400000000000000000000020411355037036000205600ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR #------------------------------------------------------------ # RR type NULL #------------------------------------------------------------ class NULL < RR attr_reader :null private def build_pack @null_pack = @null @rdlength = @null_pack.size end def get_data @null_pack end def get_inspect @null.to_s end def subclass_new_from_hash(args) if args.key? :null @null = args[:null] else raise ArgumentError, ":null field is mandatory but missing" end end def subclass_new_from_string(str) @null = str.strip end def subclass_new_from_binary(data, offset) @null = data[offset..offset + @rdlength] offset + @rdlength end private def set_type @type = Net::DNS::RR::Types.new("NULL") end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/ptr.rb000066400000000000000000000032711355037036000204210ustar00rootroot00000000000000module Net module DNS class RR # # = Pointer Record (PTR) # # Class for DNS Pointer (PTR) resource records. # # Pointer records are the opposite of A and AAAA RRs # and are used in Reverse Map zone files to map # an IP address (IPv4 or IPv6) to a host name. # class PTR < RR # Gets the PTR value. # # Returns a String. def ptrdname @ptrdname.to_s end alias ptr ptrdname # Gets the standardized value for this record, # represented by the value of ptrdname. # # Returns a String. def value ptrdname.to_s end private def build_pack @ptrdname_pack = pack_name(@ptrdname) @rdlength = @ptrdname_pack.size end def get_data @ptrdname_pack end def subclass_new_from_hash(args) if args.key?(:ptrdname) || args.key?(:ptr) @ptrdname = args[:ptrdname] else raise ArgumentError, ":ptrdname or :ptr field is mandatory" end end def subclass_new_from_string(str) @ptrdname = check_name(str) end def subclass_new_from_binary(data, offset) @ptrdname, offset = dn_expand(data, offset) offset end private def set_type @type = Net::DNS::RR::Types.new("PTR") end def get_inspect value end def check_name(input) IPAddr.new(str) rescue StandardError raise ArgumentError, "Invalid PTR Section `#{input}'" end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/soa.rb000066400000000000000000000046661355037036000204070ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR #------------------------------------------------------------ # RR type SOA #------------------------------------------------------------ class SOA < RR attr_reader :mname, :rname, :serial, :refresh, :retry, :expire, :minimum private def build_pack @soa_pack = pack_name(@mname) @soa_pack += pack_name(@rname) @soa_pack += [@serial, @refresh, @retry, @expire, @minimum].pack("N5") end def get_data @soa_pack end def get_inspect "#{@mname} #{@rname} #{@serial} #{@refresh} #{@retry} #{@expire} #{@minimum}" end def subclass_new_from_hash(args) if args.key? :rdata subclass_new_from_string(args[:rdata]) else %i[mname rname serial refresh retry expire minimum].each do |key| raise ArgumentError, "Missing field :#{key}" unless args.key? key end @mname = args[:mname] if valid? args[:mname] @rname = args[:rname] if valid? args[:rname] @serial = args[:serial] if number? args[:serial] @refresh = args[:refresh] if number? args[:refresh] @retry = args[:retry] if number? args[:retry] @expire = args[:expire] if number? args[:expire] @minimum = args[:minimum] if number? args[:minimum] end end def number?(num) if num.is_a?(Integer) && (num > 0) true else raise ArgumentError, "Wrong format field: #{num} not a number or less than zero" end end def subclass_new_from_string(str) mname, rname, serial, refresh, ret, expire, minimum = str.strip.split(" ") @mname = mname if valid? mname @rname = rname if valid? rname @serial, @refresh, @retry, @expire, @minimum = [serial, refresh, ret, expire, minimum].collect do |i| i.to_i if valid? i.to_i end end def subclass_new_from_binary(data, offset) @mname, offset = dn_expand(data, offset) @rname, offset = dn_expand(data, offset) @serial, @refresh, @retry, @expire, @minimum = data.unpack("@#{offset} N5") offset + 5 * Net::DNS::INT32SZ end private def set_type @type = Net::DNS::RR::Types.new("SOA") end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/srv.rb000066400000000000000000000017051355037036000204260ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR #------------------------------------------------------------ # RR type SRV #------------------------------------------------------------ class SRV < RR attr_reader :priority, :weight, :port, :host private def build_pack str = "" end def subclass_new_from_binary(data, offset) off_end = offset + @rdlength @priority, @weight, @port = data.unpack("@#{offset} n n n") offset += 6 @host = [] while offset < off_end len = data.unpack("@#{offset} C")[0] offset += 1 str = data[offset..offset + len - 1] offset += len @host << str end @host = @host.join(".") offset end private def set_type @type = Net::DNS::RR::Types.new("SRV") end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/txt.rb000066400000000000000000000024701355037036000204330ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR #------------------------------------------------------------ # RR type TXT #------------------------------------------------------------ class TXT < RR attr_reader :txt private def build_pack str = "" @txt.split(" ").each do |txt| str += [txt.length, txt].pack("C a*") end @txt_pack = str @rdlength = @txt_pack.size end def get_data @txt_pack end def subclass_new_from_hash(args) if args.key? :txt @txt = args[:txt].strip else raise ArgumentError, ":txt field is mandatory but missing" end end def subclass_new_from_string(str) @txt = str.strip end def subclass_new_from_binary(data, offset) off_end = offset + @rdlength @txt = "" while offset < off_end len = data.unpack("@#{offset} C")[0] offset += 1 str = data[offset..offset + len - 1] offset += len @txt << str << " " end offset end private def set_type @type = Net::DNS::RR::Types.new("TXT") end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/rr/types.rb000066400000000000000000000146631355037036000207670ustar00rootroot00000000000000module Net # :nodoc: module DNS class RR # This is an auxiliary class to handle RR type field in a DNS packet. class Types TYPES = { 'SIGZERO' => 0, # RFC2931 consider this a pseudo type 'A' => 1, # RFC 1035, Section 3.4.1 'NS' => 2, # RFC 1035, Section 3.3.11 'MD' => 3, # RFC 1035, Section 3.3.4 (obsolete) 'MF' => 4, # RFC 1035, Section 3.3.5 (obsolete) 'CNAME' => 5, # RFC 1035, Section 3.3.1 'SOA' => 6, # RFC 1035, Section 3.3.13 'MB' => 7, # RFC 1035, Section 3.3.3 'MG' => 8, # RFC 1035, Section 3.3.6 'MR' => 9, # RFC 1035, Section 3.3.8 'NULL' => 10, # RFC 1035, Section 3.3.10 'WKS' => 11, # RFC 1035, Section 3.4.2 (deprecated) 'PTR' => 12, # RFC 1035, Section 3.3.12 'HINFO' => 13, # RFC 1035, Section 3.3.2 'MINFO' => 14, # RFC 1035, Section 3.3.7 'MX' => 15, # RFC 1035, Section 3.3.9 'TXT' => 16, # RFC 1035, Section 3.3.14 'RP' => 17, # RFC 1183, Section 2.2 'AFSDB' => 18, # RFC 1183, Section 1 'X25' => 19, # RFC 1183, Section 3.1 'ISDN' => 20, # RFC 1183, Section 3.2 'RT' => 21, # RFC 1183, Section 3.3 'NSAP' => 22, # RFC 1706, Section 5 'NSAP_PTR' => 23, # RFC 1348 (obsolete) # The following 2 RRs are impemented in Net::DNS::SEC, TODO 'SIG' => 24, # RFC 2535, Section 4.1 'KEY' => 25, # RFC 2535, Section 3.1 'PX' => 26, # RFC 2163, 'GPOS' => 27, # RFC 1712 (obsolete) 'AAAA' => 28, # RFC 1886, Section 2.1 'LOC' => 29, # RFC 1876 # The following RR is implemented in Net::DNS::SEC, TODO 'NXT' => 30, # RFC 2535, Section 5.2 'EID' => 31, # draft-ietf-nimrod-dns-xx.txt 'NIMLOC' => 32, # draft-ietf-nimrod-dns-xx.txt 'SRV' => 33, # RFC 2052 'ATMA' => 34, # ??? 'NAPTR' => 35, # RFC 2168 'KX' => 36, # RFC 2230 'CERT' => 37, # RFC 2538 'DNAME' => 39, # RFC 2672 'OPT' => 41, # RFC 2671 # The following 4 RRs are implemented in Net::DNS::SEC TODO 'DS' => 43, # draft-ietf-dnsext-delegation-signer 'SSHFP' => 44, # draft-ietf-secsh-dns (No RFC # yet at time of coding) 'RRSIG' => 46, # draft-ietf-dnsext-dnssec-2535typecode-change 'NSEC' => 47, # draft-ietf-dnsext-dnssec-2535typecode-change 'DNSKEY' => 48, # draft-ietf-dnsext-dnssec-2535typecode-change 'UINFO' => 100, # non-standard 'UID' => 101, # non-standard 'GID' => 102, # non-standard 'UNSPEC' => 103, # non-standard 'TKEY' => 249, # RFC 2930 'TSIG' => 250, # RFC 2931 'IXFR' => 251, # RFC 1995 'AXFR' => 252, # RFC 1035 'MAILB' => 253, # RFC 1035 (MB, MG, MR) 'MAILA' => 254, # RFC 1035 (obsolete - see MX) 'ANY' => 255, # RFC 1035 }.freeze # The default value when type is nil in Resource Records @@default = TYPES["A"] def self.default @@default end # Be able to control the default type to assign when # type is +nil+. Default to +A+ def self.default=(str) if TYPES.key? str @@default = TYPES[str] else raise ArgumentError, "Unknown type #{str}" end end # Checks whether +type+ is a valid RR type. def self.valid?(type) case type when String TYPES.key?(type) when Integer TYPES.invert.key?(type) else raise ArgumentError, "Wrong type class: #{type.class}" end end # Returns the type in string format, as "A" or "NS", # given the numeric value def self.to_str(type) case type when Integer if TYPES.invert.key? type TYPES.invert[type] else raise ArgumentError, "Unknown type number #{type}" end else raise ArgumentError, "Wrong type class: #{type.class}" end end # Gives in output the keys from the +Types+ hash # in a format suited for regexps def self.regexp # Longest ones go first, so the regex engine will match AAAA before A. TYPES.keys.sort { |a, b| b.length <=> a.length }.join("|") end # Creates a new object representing an RR type. Performs some # checks on the argument validity too. Il +type+ is +nil+, the # default value is +ANY+ or the one set with Types.default= def initialize(type) case type when String # type in the form "A" or "NS" new_from_string(type.upcase) when Integer # type in numeric form new_from_num(type) when nil # default type, control with Types.default= @str = TYPES.invert[@@default] @num = @@default else raise ArgumentError, "Wrong type class: #{type.class}" end end # Returns the type in number format # (default for normal use) def inspect @num end # Returns the type in string format, # i.d. "A" or "NS" or such a string. def to_s @str end # Returns the type in numeric format, # usable by the pack methods for data transfers def to_i @num.to_i end def to_str @num.to_s end private # Constructor for string data type. def new_from_string(type) case type when /^TYPE\\d+/ # TODO!!! else # String with name of type if TYPES.key? type @str = type @num = TYPES[type] else raise ArgumentError, "Unknown type #{type}" end end end # Contructor for numeric data type. def new_from_num(type) if TYPES.invert.key? type @num = type @str = TYPES.invert[type] else raise ArgumentError, "Unkown type number #{type}" end end end end end end gitlab-net-dns-v0.9.1/lib/net/dns/version.rb000066400000000000000000000002011355037036000206440ustar00rootroot00000000000000# frozen_string_literal: true module Net module DNS # The current library version. VERSION = "0.9.1".freeze end end gitlab-net-dns-v0.9.1/spec/000077500000000000000000000000001355037036000154535ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/spec/fixtures/000077500000000000000000000000001355037036000173245ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/spec/fixtures/resolv.conf000066400000000000000000000001631355037036000215050ustar00rootroot00000000000000search corporate.thoughtworks.com nameserver 192.168.1.1 nameserver 192.168.1.2 nameserver 192.168.1.3 192.168.1.4 gitlab-net-dns-v0.9.1/spec/spec_helper.rb000066400000000000000000000005341355037036000202730ustar00rootroot00000000000000require 'rspec' require 'net/dns' unless defined?(SPEC_ROOT) SPEC_ROOT = File.expand_path(__dir__) end # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[File.join(SPEC_ROOT, "support/**/*.rb")].each { |f| require f } RSpec.configure do |config| config.mock_with :rspec end gitlab-net-dns-v0.9.1/spec/unit/000077500000000000000000000000001355037036000164325ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/spec/unit/resolver/000077500000000000000000000000001355037036000202735ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/spec/unit/resolver/dns_timeout_spec.rb000066400000000000000000000020071355037036000241630ustar00rootroot00000000000000require 'spec_helper' require 'net/dns/resolver/timeouts' describe Net::DNS::Resolver::DnsTimeout do subject { described_class.new(10) } describe "#initialize" do it "returns an instance of DnsTimeout" do expect(subject.class).to be(described_class) end it "sets timeout" do expect(described_class.new(0).seconds).to eq(0) expect(described_class.new(10).seconds).to eq(10) end it "raises ArgumentError when timeout is invalid" do expect { described_class.new(nil) }.to raise_error(ArgumentError) expect { described_class.new("") }.to raise_error(ArgumentError) expect { described_class.new("foo") }.to raise_error(ArgumentError) expect { described_class.new(-1) }.to raise_error(ArgumentError) end end describe "#to_s" do it "returns the seconds" do expect(subject.to_s).to eq("10") end end describe "#timeout" do it "requires a block" do expect { subject.timeout }.to raise_error(LocalJumpError) end end end gitlab-net-dns-v0.9.1/spec/unit/resolver/tcp_timeout_spec.rb000066400000000000000000000027011355037036000241660ustar00rootroot00000000000000require 'spec_helper' require 'net/dns/resolver/timeouts' describe Net::DNS::Resolver::TcpTimeout do subject { described_class.new(10) } it "inherits from DnsTimeout" do expect(described_class.ancestors).to include(Net::DNS::Resolver::DnsTimeout) end describe "#initialize" do it "returns an instance of TcpTimeout" do expect(subject.class).to be(described_class) end it "sets timeout" do expect(described_class.new(0).seconds).to eq(0) expect(described_class.new(10).seconds).to eq(10) end it "raises ArgumentError when timeout is invalid" do expect { described_class.new(nil) }.to raise_error(ArgumentError) expect { described_class.new("") }.to raise_error(ArgumentError) expect { described_class.new("foo") }.to raise_error(ArgumentError) expect { described_class.new(-1) }.to raise_error(ArgumentError) end end describe "#to_s" do it "returns infinite when seconds is 0" do expect(described_class.new(0).to_s).to eq("infinite") end it "returns the seconds" do expect(subject.to_s).to eq("10") end end describe "#pretty_to_s" do it "returns a more verbose version" do expect(described_class.new(30).pretty_to_s).to eq("30 seconds") expect(described_class.new(90).pretty_to_s).to eq("1 minutes and 30 seconds") expect(described_class.new(3690).pretty_to_s).to eq("1 hours, 1 minutes and 30 seconds") end end end gitlab-net-dns-v0.9.1/spec/unit/resolver/udp_timeout_spec.rb000066400000000000000000000027041355037036000241730ustar00rootroot00000000000000require 'spec_helper' require 'net/dns/resolver/timeouts' describe Net::DNS::Resolver::UdpTimeout do subject { described_class.new(10) } it "inherits from DnsTimeout" do expect(described_class.ancestors).to include(Net::DNS::Resolver::DnsTimeout) end describe "#initialize" do it "returns an instance of TcpTimeout" do expect(subject.class).to be(described_class) end it "sets timeout" do expect(described_class.new(0).seconds).to eq(0) expect(described_class.new(10).seconds).to eq(10) end it "raises ArgumentError when timeout is invalid" do expect { described_class.new(nil) }.to raise_error(ArgumentError) expect { described_class.new("") }.to raise_error(ArgumentError) expect { described_class.new("foo") }.to raise_error(ArgumentError) expect { described_class.new(-1) }.to raise_error(ArgumentError) end end describe "#to_s" do it "returns infinite when seconds is 0" do expect(described_class.new(0).to_s).to eq("not defined") end it "returns the seconds" do expect(subject.to_s).to eq("10") end end describe "#pretty_to_s" do it "returns a more verbose version" do expect(described_class.new(30).pretty_to_s).to eq("30 seconds") expect(described_class.new(90).pretty_to_s).to eq("1 minutes and 30 seconds") expect(described_class.new(3690).pretty_to_s).to eq("1 hours, 1 minutes and 30 seconds") end end end gitlab-net-dns-v0.9.1/test/000077500000000000000000000000001355037036000155005ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/test/test_helper.rb000066400000000000000000000004421355037036000203430ustar00rootroot00000000000000require "minitest/autorun" require "minitest/reporters" Minitest::Reporters.use! Minitest::Reporters::DefaultReporter.new(color: true) $LOAD_PATH.unshift File.expand_path("../lib", __dir__) require "net/dns" module Minitest::Assertions def assert_nothing_raised(*) yield end end gitlab-net-dns-v0.9.1/test/unit/000077500000000000000000000000001355037036000164575ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/test/unit/header_test.rb000066400000000000000000000115751355037036000213040ustar00rootroot00000000000000require 'test_helper' require 'net/dns/header' class HeaderTest < Minitest::Test include Net::DNS def setup @default = Header.new @hash = Header.new(id: 441, qr: 1, opCode: Header::IQUERY, aa: 1, tc: 1, rd: 0, cd: 0, ad: 0, ra: 1, rCode: Header::RCode::FORMAT, qdCount: 1, anCount: 2, nsCount: 3, arCount: 3) @modified = Header.new @modified.id = 442 @modified.qr = true @modified.opCode = Header::IQUERY @modified.aa = true @modified.tc = true @modified.rd = false @modified.cd = false @modified.ra = true @modified.rCode = Header::RCode::FORMAT @modified.qdCount = 1 @modified.anCount = 2 @modified.nsCount = 3 @modified.arCount = 3 @data = @modified.data num = [(@data.unpack("n")[0] + 1)].pack("n") @data[0] = num[0] @data[1] = num[1] @binary = Header.parse(@data) end def test_simple assert_equal(@default.query?, true) assert_equal(@default.response?, false) assert_equal(@default.opCode, Header::QUERY) assert_equal(@default.auth?, false) assert_equal(@default.truncated?, false) assert_equal(@default.recursive?, true) assert_equal(@default.checking?, true) assert_equal(@default.verified?, false) assert_equal(@default.r_available?, false) assert_equal(@default.rCode.code, Header::RCode::NOERROR) assert_equal(@default.qdCount, 1) assert_equal(@default.anCount, 0) assert_equal(@default.nsCount, 0) assert_equal(@default.arCount, 0) assert_equal(@hash.id, 441) assert_equal(@hash.query?, false) assert_equal(@hash.response?, true) assert_equal(@hash.opCode, Header::IQUERY) assert_equal(@hash.auth?, true) assert_equal(@hash.truncated?, true) assert_equal(@hash.recursive?, false) assert_equal(@hash.checking?, true) assert_equal(@hash.verified?, false) assert_equal(@hash.r_available?, true) assert_equal(@hash.rCode.code, Header::RCode::FORMAT) assert_equal(@hash.qdCount, 1) assert_equal(@hash.anCount, 2) assert_equal(@hash.nsCount, 3) assert_equal(@hash.arCount, 3) assert_equal(@modified.id, 442) assert_equal(@modified.query?, false) assert_equal(@modified.response?, true) assert_equal(@modified.opCode, Header::IQUERY) assert_equal(@modified.auth?, true) assert_equal(@modified.truncated?, true) assert_equal(@modified.recursive?, false) assert_equal(@modified.checking?, true) assert_equal(@modified.verified?, false) assert_equal(@modified.r_available?, true) assert_equal(@modified.rCode.code, Header::RCode::FORMAT) assert_equal(@modified.qdCount, 1) assert_equal(@modified.anCount, 2) assert_equal(@modified.nsCount, 3) assert_equal(@modified.arCount, 3) assert_equal(@binary.data, @data) assert_equal(@binary.id, 443) assert_equal(@binary.query?, false) assert_equal(@binary.response?, true) assert_equal(@binary.opCode, Header::IQUERY) assert_equal(@binary.auth?, true) assert_equal(@binary.truncated?, true) assert_equal(@binary.recursive?, false) assert_equal(@binary.checking?, true) assert_equal(@binary.verified?, false) assert_equal(@binary.r_available?, true) assert_equal(@binary.rCode.code, Header::RCode::FORMAT) assert_equal(@binary.qdCount, 1) assert_equal(@binary.anCount, 2) assert_equal(@binary.nsCount, 3) assert_equal(@binary.arCount, 3) assert_raises(ArgumentError) do Header.new([]) end assert_raises(ArgumentError) do Header.parse([]) end assert_raises(ArgumentError) do Header.parse("aa") end assert_raises(ArgumentError) do @default.id = 1_000_000 end assert_raises(ArgumentError) do @default.qr = 2 end assert_raises(Header::WrongOpcodeError) do @default.opCode = 4 end assert_raises(ArgumentError) do @default.aa = 2 end assert_raises(ArgumentError) do @default.tc = 2 end assert_raises(Header::WrongRecursiveError) do @default.recursive = 2 end assert_raises(ArgumentError) do @default.ra = 2 end assert_raises(ArgumentError) do @default.cd = 2 end assert_raises(ArgumentError) do @default.ad = 2 end assert_raises(ArgumentError) do @default.rCode = 46 end assert_raises(Header::WrongCountError) do @default.qdCount = 100_000 end assert_raises(Header::WrongCountError) do @default.anCount = 100_000 end assert_raises(Header::WrongCountError) do @default.nsCount = 100_000 end assert_raises(Header::WrongCountError) do @default.arCount = 100_000 end end end gitlab-net-dns-v0.9.1/test/unit/names_test.rb000066400000000000000000000007541355037036000211540ustar00rootroot00000000000000require 'test_helper' require 'net/dns/names' class NamesTest < Minitest::Test include Net::DNS::Names def test_long_names assert_nothing_raised do pack_name('a' * 63) end assert_raises ArgumentError do pack_name('a' * 64) end assert_nothing_raised do pack_name(['a' * 63, 'b' * 63, 'c' * 63, 'd' * 63].join('.')) end assert_raises ArgumentError do pack_name(['a' * 63, 'b' * 63, 'c' * 63, 'd' * 63, 'e'].join('.')) end end end gitlab-net-dns-v0.9.1/test/unit/packet_test.rb000066400000000000000000000051741355037036000213210ustar00rootroot00000000000000require 'test_helper' require 'net/dns/packet' class PacketTest < Minitest::Test def setup @klass = Net::DNS::Packet @domain = 'example.com' end def test_initialize @record = @klass.new(@domain, Net::DNS::MX, Net::DNS::HS) assert_instance_of @klass, @record assert_instance_of Net::DNS::Header, @record.header assert_instance_of Array, @record.question assert_instance_of Net::DNS::Question, @record.question.first assert_instance_of Array, @record.answer assert_instance_of Array, @record.authority assert_instance_of Array, @record.additional end def test_initialize_should_set_question @question = @klass.new(@domain).question.first assert_equal @domain, @question.qName assert_equal Net::DNS::RR::Types.new(Net::DNS::A).to_s, @question.qType.to_s assert_equal Net::DNS::RR::Classes.new(Net::DNS::IN).to_s, @question.qClass.to_s @question = @klass.new(@domain, Net::DNS::MX, Net::DNS::HS).question.first assert_equal @domain, @question.qName assert_equal Net::DNS::RR::Types.new(Net::DNS::MX).to_s, @question.qType.to_s assert_equal Net::DNS::RR::Classes.new(Net::DNS::HS).to_s, @question.qClass.to_s end def test_self_parse packet = "\337M\201\200\000\001\000\003\000\004\000\004\006google\003com\000\000\001\000\001\300\f\000\001\000\001\000\000\001,\000\004@\351\273c\300\f\000\001\000\001\000\000\001,\000\004H\016\317c\300\f\000\001\000\001\000\000\001,\000\004@\351\247c\300\f\000\002\000\001\000\003\364\200\000\006\003ns1\300\f\300\f\000\002\000\001\000\003\364\200\000\006\003ns2\300\f\300\f\000\002\000\001\000\003\364\200\000\006\003ns3\300\f\300\f\000\002\000\001\000\003\364\200\000\006\003ns4\300\f\300X\000\001\000\001\000\003\307\273\000\004\330\357 \n\300j\000\001\000\001\000\003\307\273\000\004\330\357\"\n\300|\000\001\000\001\000\003\307\273\000\004\330\357$\n\300\216\000\001\000\001\000\003\307\273\000\004\330\357&\n" @record = @klass.parse(packet) assert_instance_of @klass, @record assert_instance_of Net::DNS::Header, @record.header assert_instance_of Array, @record.question assert_instance_of Net::DNS::Question, @record.question.first assert_instance_of Array, @record.answer assert_instance_of Net::DNS::RR::A, @record.answer.first assert_instance_of Array, @record.authority assert_instance_of Net::DNS::RR::NS, @record.authority.first assert_instance_of Array, @record.additional assert_instance_of Net::DNS::RR::A, @record.additional.first end end gitlab-net-dns-v0.9.1/test/unit/question_test.rb000066400000000000000000000056371355037036000217250ustar00rootroot00000000000000require 'test_helper' require 'net/dns/question' class QuestionTest < Minitest::Test def setup @domain = 'example.com.' @type = 'MX' @cls = 'HS' @data = "\006google\003com\000\000\001\000\001" @default = Net::DNS::Question.new(@domain) @string = Net::DNS::Question.new(@domain, @type, @cls) @binary = Net::DNS::Question.parse(@data) @binary2 = Net::DNS::Question.parse(@string.data) end def test_simple assert_equal(@default.qName, @domain) assert_equal(@default.qType.to_s, "A") assert_equal(@default.qClass.to_s, "IN") assert_equal(@string.qName, @domain) assert_equal(@string.qType.to_s, "MX") assert_equal(@string.qClass.to_s, "HS") assert_equal(@binary.qName, "google.com.") assert_equal(@binary.qType.to_s, "A") assert_equal(@binary.qClass.to_s, "IN") assert_equal(@binary2.qName, @domain) assert_equal(@binary2.qType.to_s, "MX") assert_equal(@binary2.qClass.to_s, "HS") end def test_raise # assert_raises(Net::DNS::Question::NameInvalid) do # Net::DNS::Question.new(1) # end assert_raises(Net::DNS::Question::NameInvalid) do Net::DNS::Question.new("test{") end assert_raises(ArgumentError) do Net::DNS::Question.parse([]) end assert_raises(ArgumentError) do Net::DNS::Question.parse("test") end end def test_inspect assert_equal "google.com. IN A ", Net::DNS::Question.new("google.com.").inspect assert_equal "google.com. IN A ", Net::DNS::Question.new("google.com.", Net::DNS::A).inspect assert_equal "google.com. IN NS ", Net::DNS::Question.new("google.com.", Net::DNS::NS).inspect assert_equal "google.com. IN NS ", Net::DNS::Question.new("google.com.", Net::DNS::NS).inspect end def test_inspect_with_name_longer_than_29_chrs assert_equal "supercalifragilistichespiralidoso.com IN A ", Net::DNS::Question.new("supercalifragilistichespiralidoso.com").inspect end def test_to_s assert_equal "google.com. IN A ", Net::DNS::Question.new("google.com.").to_s assert_equal "google.com. IN A ", Net::DNS::Question.new("google.com.", Net::DNS::A).to_s assert_equal "google.com. IN NS ", Net::DNS::Question.new("google.com.", Net::DNS::NS).to_s assert_equal "google.com. IN NS ", Net::DNS::Question.new("google.com.", Net::DNS::NS).to_s end def test_to_s_with_name_longer_than_29_chrs assert_equal "supercalifragilistichespiralidoso.com IN A ", Net::DNS::Question.new("supercalifragilistichespiralidoso.com").to_s end end gitlab-net-dns-v0.9.1/test/unit/resolver_test.rb000066400000000000000000000107661355037036000217160ustar00rootroot00000000000000require 'test_helper' require 'net/dns/resolver' class Net::DNS::Resolver attr_reader :config end class ResolverTest < Minitest::Test def test_initialize assert_nothing_raised { Net::DNS::Resolver.new } end def test_initialize_with_config assert_nothing_raised { Net::DNS::Resolver.new({}) } end def test_initialize_with_multi_name_servers resolver = Net::DNS::Resolver.new(config_file: File.expand_path('../../spec/fixtures/resolv.conf', __dir__)) assert_equal ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'], resolver.nameservers end def test_initialize_with_invalid_config_should_raise_argumenterror assert_raises(ArgumentError) { Net::DNS::Resolver.new("") } assert_raises(ArgumentError) { Net::DNS::Resolver.new(0) } assert_raises(ArgumentError) { Net::DNS::Resolver.new(:foo) } end def test_query_with_no_nameservers_should_raise_resolvererror assert_raises(Net::DNS::Resolver::Error) { Net::DNS::Resolver.new(nameservers: []).query("example.com") } end # def test_send_to_ipv6_nameserver_should_not_raise_einval # assert_nothing_raised { Net::DNS::Resolver.new(:nameservers => ['2001:4860:4860::8888', '2001:4860:4860::8844']).send('example.com')} # end # I know private methods are supposed to not be tested directly # but since this library lacks unit tests, for now let me test them in this way. def _make_query_packet(*args) Net::DNS::Resolver.new.send(:make_query_packet, *args) end def test_make_query_packet_from_ipaddr packet = _make_query_packet(IPAddr.new("192.168.1.1"), Net::DNS::A, cls = Net::DNS::IN) assert_equal "1.1.168.192.in-addr.arpa", packet.question.first.qName assert_equal Net::DNS::PTR.to_i, packet.question.first.qType.to_i assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i end def test_make_query_packet_from_string_like_ipv4 packet = _make_query_packet("192.168.1.1", Net::DNS::A, cls = Net::DNS::IN) assert_equal "1.1.168.192.in-addr.arpa", packet.question.first.qName assert_equal Net::DNS::PTR.to_i, packet.question.first.qType.to_i assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i end def test_make_query_packet_from_string_like_ipv6 packet = _make_query_packet("2001:1ac0::200:0:a5d1:6004:2", Net::DNS::A, cls = Net::DNS::IN) assert_equal "2.0.0.0.4.0.0.6.1.d.5.a.0.0.0.0.0.0.2.0.0.0.0.0.0.c.a.1.1.0.0.2.ip6.arpa", packet.question.first.qName assert_equal Net::DNS::PTR.to_i, packet.question.first.qType.to_i assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i end def test_make_query_packet_from_string_like_hostname packet = _make_query_packet("ns2.google.com", Net::DNS::A, cls = Net::DNS::IN) assert_equal "ns2.google.com", packet.question.first.qName assert_equal Net::DNS::A.to_i, packet.question.first.qType.to_i assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i end def test_make_query_packet_from_string_like_hostname_with_number packet = _make_query_packet("ns.google.com", Net::DNS::A, cls = Net::DNS::IN) assert_equal "ns.google.com", packet.question.first.qName assert_equal Net::DNS::A.to_i, packet.question.first.qType.to_i assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i end def test_should_return_state_without_exception res = Net::DNS::Resolver.new assert_nothing_raised { res.state } end RubyPlatforms = [ ["darwin9.0", false], # Mac OS X ["darwin", false], # JRuby on Mac OS X ["linux-gnu", false], ["mingw32", true], # ruby 1.8.6 (2008-03-03 patchlevel 114) [i386-mingw32] ["mswin32", true], # ruby 1.8.6 (2008-03-03 patchlevel 114) [i386-mswin32] ["mswin32", true], # ruby 1.8.6 (2008-04-22 rev 6555) [x86-jruby1.1.1] ].freeze C = Object.const_get(defined?(RbConfig) ? :RbConfig : :Config)::CONFIG def test_self_platform_windows_question RubyPlatforms.each do |platform, is_windows| assert_equal is_windows, override_platform(platform) { Net::DNS::Resolver.platform_windows? }, "Expected `#{is_windows}' with platform `#{platform}'" end end private def override_platform(new_platform, &block) raise LocalJumpError, "no block given" unless block_given? old_platform = C["host_os"] C["host_os"] = new_platform result = yield ensure C["host_os"] = old_platform result end end gitlab-net-dns-v0.9.1/test/unit/rr/000077500000000000000000000000001355037036000171025ustar00rootroot00000000000000gitlab-net-dns-v0.9.1/test/unit/rr/a_test.rb000066400000000000000000000062631355037036000207150ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRATest < Minitest::Test def setup @rr_name = "google.com." @rr_type = "A" @rr_cls = "IN" @rr_ttl = 10_000 @rr_value = "64.233.187.99" @rr_address = IPAddr.new(@rr_value) @rr_output = "google.com. 10000 IN A 64.233.187.99" @rr = Net::DNS::RR::A.new(name: @rr_name, address: @rr_address, ttl: @rr_ttl) end def test_initialize_from_hash @record = Net::DNS::RR::A.new(name: @rr_name, address: @rr_value, ttl: @rr_ttl) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_address, @record.address assert_equal @rr_value, @record.value end def test_initialize_from_string @record = Net::DNS::RR::A.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}") assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_address, @record.address assert_equal @rr_value, @record.value end def test_parse data = "\006google\003com\000\000\001\000\001\000\000'\020\000\004@\351\273c" @record = Net::DNS::RR.parse(data) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_address, @record.address assert_equal @rr_value, @record.value end InvalidArguments = [ { name: "google.com", address: "255.256" }, { name: "google.com" }, Object.new, Array.new(7), "10800 IN A", "google.com. 10800 IN B", "google.com. 10800 IM A", ].freeze InvalidArguments.each_with_index do |arguments, index| define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do assert_raises(ArgumentError) { Net::DNS::RR::A.new(arguments) } end end def test_address_getter assert_equal @rr_address, @rr.address end def test_address_setter assert_raises(ArgumentError) { @rr.address = nil } expected = IPAddr.new("64.233.187.99") assert_equal expected, @rr.address = "64.233.187.99" assert_equal expected, @rr.address expected = IPAddr.new("64.233.187.90") assert_equal expected, @rr.address = expected.to_i assert_equal expected, @rr.address expected = IPAddr.new("64.233.187.80") assert_equal expected, @rr.address = IPAddr.new("64.233.187.80") assert_equal expected, @rr.address end def test_value assert_equal @rr_value, @rr.value end def test_inspect assert_equal "google.com. 10000 IN A 64.233.187.99", @rr.inspect end def test_to_s assert_equal "google.com. 10000 IN A 64.233.187.99", @rr.to_s end def test_to_a assert_equal ["google.com.", 10_000, "IN", "A", "64.233.187.99"], @rr.to_a end end gitlab-net-dns-v0.9.1/test/unit/rr/aaaa_test.rb000066400000000000000000000062451355037036000213600ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRAAAATest < Minitest::Test def setup @rr_name = "www.nic.it." @rr_type = "AAAA" @rr_cls = "IN" @rr_ttl = 60 @rr_value = "2a00:d40:1:1::239" @rr_address = IPAddr.new(@rr_value) @rr_output = "www.nic.it. 60 IN AAAA 2a00:d40:1:1::239" @rr = Net::DNS::RR::AAAA.new(name: @rr_name, address: @rr_address, ttl: @rr_ttl) end def test_initialize_from_hash @record = Net::DNS::RR::AAAA.new(name: @rr_name, address: @rr_value, ttl: @rr_ttl) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_address, @record.address assert_equal @rr_value, @record.value end def test_initialize_from_string @record = Net::DNS::RR::AAAA.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}") assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_address, @record.address assert_equal @rr_value, @record.value end def test_parse data = "\003www\003nic\002it\000\000\034\000\001\000\000\000<\000\020*\000\r@\000\001\000\001\000\000\000\000\000\000\0029" @record = Net::DNS::RR.parse(data) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_address, @record.address assert_equal @rr_value, @record.value end InvalidArguments = [ { name: "google.com", address: "2a00" }, { name: "google.com" }, Object.new, Array.new(7), "10800 IN AAAA", # FIXME: "google.com. 10800 IN B", # FIXME: "google.com. 10800 IM AAAA", ].freeze InvalidArguments.each_with_index do |arguments, index| define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do assert_raises(ArgumentError) { Net::DNS::RR::AAAA.new(arguments) } end end def test_address_getter assert_equal @rr_address, @rr.address end def test_address_setter assert_raises(ArgumentError) { @rr.address = nil } expected = IPAddr.new("2a00:d40:1:1::239") assert_equal expected, @rr.address = "2a00:d40:1:1::239" assert_equal expected, @rr.address expected = IPAddr.new("2a00:d40:1:1::240") assert_equal expected, @rr.address = IPAddr.new("2a00:d40:1:1::240") assert_equal expected, @rr.address end def test_value assert_equal @rr_value, @rr.value end def test_inspect assert_equal "www.nic.it. 60 IN AAAA 2a00:d40:1:1::239", @rr.inspect end def test_to_s assert_equal "www.nic.it. 60 IN AAAA 2a00:d40:1:1::239", @rr.to_s end def test_to_a assert_equal ["www.nic.it.", 60, "IN", "AAAA", "2a00:d40:1:1::239"], @rr.to_a end end gitlab-net-dns-v0.9.1/test/unit/rr/classes_test.rb000066400000000000000000000043751355037036000221340ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRClassesTest < Minitest::Test def setup @classes = {} @regexp_string = "ANY|CH|HS|IN|NONE" end StrAndNum = [ ['IN', 1], ['CH', 3], ['HS', 4], ['NONE', 254], ['ANY', 255], ].freeze StrAndNum.each do |str, num| define_method "test_initialize_from_str_#{str}" do instance = Net::DNS::RR::Classes.new(str) assert_equal str, instance.to_s assert_equal num, instance.to_i end define_method "test_initialize_from_num_#{num}" do instance = Net::DNS::RR::Classes.new(num) assert_equal str, instance.to_s assert_equal num, instance.to_i end end def test_initialize_should_raise_with_invalid_class assert_raises(ArgumentError) { Net::DNS::RR::Classes.new({}) } end def test_inspect assert_equal 1, Net::DNS::RR::Classes.new(1).inspect assert_equal 1, Net::DNS::RR::Classes.new("IN").inspect end def test_to_s assert_equal "IN", Net::DNS::RR::Classes.new(1).to_s assert_equal "IN", Net::DNS::RR::Classes.new("IN").to_s end def test_to_i assert_equal 1, Net::DNS::RR::Classes.new(1).to_i assert_equal 1, Net::DNS::RR::Classes.new("IN").to_i end def test_self_default @_default = Net::DNS::RR::Classes.default # Default type should be ANY => 255 instance = Net::DNS::RR::Classes.new(nil) assert_equal 1, instance.to_i assert_equal "IN", instance.to_s # Let's change default behaviour Net::DNS::RR::Classes.default = "CH" instance = Net::DNS::RR::Classes.new(nil) assert_equal 3, instance.to_i assert_equal "CH", instance.to_s Net::DNS::RR::Classes.default = "IN" instance = Net::DNS::RR::Classes.new(nil) assert_equal 1, instance.to_i assert_equal "IN", instance.to_s ensure Net::DNS::RR::Classes.default = Net::DNS::RR::Classes::CLASSES.invert[@_default] end def test_self_valid? assert Net::DNS::RR::Classes.valid?("IN") assert Net::DNS::RR::Classes.valid?(1) assert !Net::DNS::RR::Classes.valid?("Q") assert !Net::DNS::RR::Classes.valid?(256) assert_raises(ArgumentError) { Net::DNS::RR::Classes.valid?({}) } end def test_self_regexp assert_equal @regexp_string, Net::DNS::RR::Classes.regexp end end gitlab-net-dns-v0.9.1/test/unit/rr/cname_test.rb000066400000000000000000000054461355037036000215620ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRCNAMETest < Minitest::Test def setup @rr_name = "www.google.com." @rr_type = "CNAME" @rr_cls = "IN" @rr_ttl = 550_317 @rr_value = "www.l.google.com." @rr_cname = @rr_value @rr_output = "www.google.com. 550317 IN CNAME www.l.google.com." @rr = Net::DNS::RR::CNAME.new(name: @rr_name, cname: @rr_cname, ttl: @rr_ttl) end def test_initialize_from_hash @record = Net::DNS::RR::CNAME.new(name: @rr_name, cname: @rr_value, ttl: @rr_ttl) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_cname, @record.cname assert_equal @rr_value, @record.value end def test_initialize_from_string @record = Net::DNS::RR::CNAME.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}") assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_cname, @record.cname assert_equal @rr_value, @record.value end def test_parse data = "\003www\006google\003com\000\000\005\000\001\000\be\255\000\022\003www\001l\006google\003com\000" @record = Net::DNS::RR.parse(data) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_cname, @record.cname assert_equal @rr_value, @record.value end InvalidArguments = [ # FIXME: { :name => "google.com", :cname => "foo___bar" }, # FIXME: { :name => "google.com", :cname => "foo$bar" }, { name: "google.com" }, Object.new, Array.new(7), "10800 IN CNAME", "google.com. 10800 IN CNAME", ].freeze InvalidArguments.each_with_index do |arguments, index| define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do assert_raises(ArgumentError) { p Net::DNS::RR::CNAME.new(arguments) } end end def test_cname_getter assert_equal @rr_cname, @rr.cname end def test_value assert_equal @rr_value, @rr.value end def test_inspect assert_equal "www.google.com. 550317 IN CNAME www.l.google.com.", @rr.inspect end def test_to_s assert_equal "www.google.com. 550317 IN CNAME www.l.google.com.", @rr.to_s end def test_to_a assert_equal ["www.google.com.", 550_317, "IN", "CNAME", "www.l.google.com."], @rr.to_a end end gitlab-net-dns-v0.9.1/test/unit/rr/hinfo_test.rb000066400000000000000000000065271355037036000216030ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' require 'net/dns/rr/hinfo' class RRHINFOTest < Minitest::Test def setup @rr_name = "" @rr_type = "HINFO" @rr_cls = "IN" @rr_ttl = nil @rr_value = '"PC-Intel-700mhz" "Redhat Linux 7.1"' @rr_output = ' IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"' @rr_cpu = "PC-Intel-700mhz" @rr_os = "Redhat Linux 7.1" @rr = Net::DNS::RR::HINFO.new(name: @rr_name, cpu: @rr_cpu, os: @rr_os) end def test_initialize_from_hash @record = Net::DNS::RR::HINFO.new(name: @rr_name, cpu: @rr_cpu, os: @rr_os) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal 10_800, @record.ttl assert_equal @rr_value, @record.value assert_equal @rr_cpu, @record.cpu assert_equal @rr_os, @record.os end def test_initialize_from_string @record = Net::DNS::RR::HINFO.new(%Q(#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} PC-Intel-700mhz "Redhat Linux 7.1")) assert_equal @rr_output, @record.to_s assert_equal @rr_value, @record.value assert_equal @rr_cpu, @record.cpu assert_equal @rr_os, @record.os end def test_initialize_from_string_without_quotes @record = Net::DNS::RR::HINFO.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}") assert_equal @rr_output, @record.to_s # FIXME: assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal 10_800, @record.ttl assert_equal @rr_value, @record.value assert_equal @rr_cpu, @record.cpu assert_equal @rr_os, @record.os end # FIXME: Can't get valid data # # def test_parse # data = "\002in\000\000\r\000\001\000\000*0\000!\017PC-Intel-700mhz\020Redhat Linux 7.1" # @record = Net::DNS::RR.parse(data) # assert_equal @rr_output, @record.to_s # assert_equal @rr_name, @record.name # assert_equal @rr_type, @record.type # assert_equal @rr_cls, @record.cls # assert_equal @rr_ttl, @record.ttl # assert_equal @rr_value, @record.value # # assert_equal @rr_cpu, @record.cpu # assert_equal @rr_os, @record.os # end InvalidArguments = [ { name: "google.com" }, Object.new, Array.new(7), "10800 IN HINFO", "IN HINFO", ].freeze InvalidArguments.each_with_index do |arguments, index| define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do assert_raises(ArgumentError) { p Net::DNS::RR::HINFO.new(arguments) } end end def test_cpu assert_equal @rr_cpu, @rr.cpu end def test_os assert_equal @rr_os, @rr.os end def test_value assert_equal '"PC-Intel-700mhz" "Redhat Linux 7.1"', @rr.value end def test_inspect assert_equal ' IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"', @rr.inspect end def test_to_s assert_equal ' IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"', @rr.to_s end def test_to_a assert_equal [nil, nil, "IN", "HINFO", '"PC-Intel-700mhz" "Redhat Linux 7.1"'], @rr.to_a end end gitlab-net-dns-v0.9.1/test/unit/rr/mr_test.rb000066400000000000000000000065651355037036000211200ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRMRTest < Minitest::Test def setup @klass = Net::DNS::RR::MR @rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.bornagain.edu.", ttl: 9000) @rr_name = "eddie.movie.edu." @rr_type = "MR" @rr_cls = "IN" @rr_ttl = 9000 @rr_newname = "eddie.bornagain.edu." @rr_value = "eddie.bornagain.edu." @rr_output = "eddie.movie.edu. 9000 IN MR eddie.bornagain.edu." end def test_initialize_from_hash @record = @klass.new(name: "eddie.movie.edu.", newname: "eddie.bornagain.edu.", ttl: 9000) assert_equal @rr_output, @record.inspect assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_newname, @record.newname end def test_initialize_from_string @record = @klass.new("eddie.movie.edu. 9000 IN MR eddie.bornagain.edu.") assert_equal @rr_output, @record.inspect assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_newname, @record.newname end # def test_parse # data = "\005eddie\005movie\003edu\000\000\t\000\001\000\000#(\000\025\005eddie\tbornagain\003edu\000" # @record = Net::DNS::RR.parse(data) # assert_equal @rr_output, @record.inspect # assert_equal @rr_name, @record.name # assert_equal @rr_type, @record.type # assert_equal @rr_cls, @record.cls # assert_equal @rr_ttl, @record.ttl # assert_equal @rr_newname, @record.newname # end InvalidArguments = [ # FIXME: { :name => "eddie.movie.edu.", :newname => "foo___bar" }, # FIXME: { :name => "eddie.movie.edu.", :newname => "foo$bar" }, # FIXME: { :name => "eddie.movie.edu", :newname => "eddie.newname.edu." }, Object.new, Array.new(7), "9000 IN MR", ].freeze InvalidArguments.each_with_index do |arguments, index| define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do assert_raises(ArgumentError) { @klass.new(arguments) } end end def test_initialize_should_raise_with_missing_newname error = assert_raises(ArgumentError) { @klass.new(name: "eddie.movie.edu.") } assert_match /:newname/, error.message end def test_value @rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.newname.edu.") assert_equal "eddie.newname.edu.", @rr.value @rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.othername.edu.") assert_equal "eddie.othername.edu.", @rr.value end def test_newname @rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.newname.edu.") assert_equal "eddie.newname.edu.", @rr.newname @rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.othername.edu.") assert_equal "eddie.othername.edu.", @rr.newname end def test_inspect assert_equal "eddie.movie.edu. 9000 IN MR eddie.bornagain.edu.", @rr.inspect end def test_to_s assert_equal "eddie.movie.edu. 9000 IN MR eddie.bornagain.edu.", @rr.to_s end def test_to_a assert_equal ["eddie.movie.edu.", 9000, "IN", "MR", "eddie.bornagain.edu."], @rr.to_a end end gitlab-net-dns-v0.9.1/test/unit/rr/mx_test.rb000066400000000000000000000073401355037036000211160ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRMXTest < Minitest::Test def setup @rr_name = "example.com." @rr_type = "MX" @rr_cls = "IN" @rr_ttl = 10_000 @rr_preference = 10 @rr_exchange = "mail.example.com." @rr_value = "#{@rr_preference} #{@rr_exchange}" @rr_output = "example.com. 10000 IN MX 10 mail.example.com." @rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.", ttl: 10_000) end def test_initialize_from_hash @record = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.", ttl: 10_000) assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_preference, @record.preference assert_equal @rr_exchange, @record.exchange end def test_initialize_from_string @record = Net::DNS::RR::MX.new("example.com. 10000 IN MX 10 mail.example.com.") assert_equal @rr_output, @record.to_s assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_preference, @record.preference assert_equal @rr_exchange, @record.exchange end # FIXME: can't get it working with canned data # def test_parse # data = "\001\220\006google\003com\004s9b2\005psmtp\003com\000" # @record = Net::DNS::RR.parse(data) # assert_equal @rr_output, @record.to_s # assert_equal @rr_name, @record.name # assert_equal @rr_type, @record.type # assert_equal @rr_cls, @record.cls # assert_equal @rr_ttl, @record.ttl # assert_equal @rr_preference, @record.preference # assert_equal @rr_exchange, @record.exchange # end InvalidArguments = [ { name: "google.com" }, Object.new, Array.new(7), "10800 IN NS", "google.com. 10800 IN NS", ].freeze InvalidArguments.each_with_index do |arguments, index| define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do assert_raises(ArgumentError) { p Net::DNS::RR::MX.new(arguments) } end end def test_preference @rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.") assert_equal 10, @rr.preference @rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 100, exchange: "mail.example.com.") assert_equal 100, @rr.preference end def test_exchange @rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.") assert_equal "mail.example.com.", @rr.exchange @rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail2.example.com.") assert_equal "mail2.example.com.", @rr.exchange end def test_value @rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.") assert_equal "10 mail.example.com.", @rr.value @rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 100, exchange: "mail2.example.com.") assert_equal "100 mail2.example.com.", @rr.value end def test_inspect assert_equal "example.com. 10000 IN MX 10 mail.example.com.", @rr.inspect end def test_to_s assert_equal "example.com. 10000 IN MX 10 mail.example.com.", @rr.to_s end def test_to_a assert_equal ["example.com.", 10_000, "IN", "MX", "10 mail.example.com."], @rr.to_a end end gitlab-net-dns-v0.9.1/test/unit/rr/ns_test.rb000066400000000000000000000046521355037036000211150ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRNSTest < Minitest::Test def setup @rr_name = "google.com." @rr_type = "NS" @rr_cls = "IN" @rr_ttl = 10_800 @rr_nsdname = "ns1.google.com." @rr_output = "google.com. 10800 IN NS ns1.google.com." @rr = Net::DNS::RR::NS.new(name: "google.com.", nsdname: "ns1.google.com.", ttl: @rr_ttl) end def test_initialize_from_hash @record = Net::DNS::RR::NS.new(name: "google.com.", nsdname: "ns1.google.com.") assert_equal @rr_output, @record.inspect assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_nsdname, @record.nsdname end def test_initialize_from_string @record = Net::DNS::RR::NS.new("google.com. 10800 IN NS ns1.google.com.") assert_equal @rr_output, @record.inspect assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_nsdname, @record.nsdname end def test_parse data = "\006google\003com\000\000\002\000\001\000\000*0\000\020\003ns1\006google\003com\000" @record = Net::DNS::RR.parse(data) assert_equal @rr_output, @record.inspect assert_equal @rr_name, @record.name assert_equal @rr_type, @record.type assert_equal @rr_cls, @record.cls assert_equal @rr_ttl, @record.ttl assert_equal @rr_nsdname, @record.nsdname end InvalidArguments = [ { name: "google.com", nsdname: "255.256" }, { name: "google.com" }, Object.new, Array.new(7), "10800 IN A", ].freeze InvalidArguments.each_with_index do |arguments, index| define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do assert_raises(ArgumentError) { Net::DNS::RR::NS.new(arguments) } end end def test_value assert_equal "ns1.google.com.", @rr.value end def test_inspect assert_equal "google.com. 10800 IN NS ns1.google.com.", @rr.inspect end def test_to_s assert_equal "google.com. 10800 IN NS ns1.google.com.", @rr.to_s end def test_to_a assert_equal ["google.com.", 10_800, "IN", "NS", "ns1.google.com."], @rr.to_a end end gitlab-net-dns-v0.9.1/test/unit/rr/types_test.rb000066400000000000000000000040031355037036000216270ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRTypesTest < Minitest::Test def setup end def test_default @_default = Net::DNS::RR::Types.default # Default type should be ANY => 255 instance = Net::DNS::RR::Types.new(nil) assert_equal("1", instance.to_str) assert_equal("A", instance.to_s) # Let's change default behaviour Net::DNS::RR::Types.default = "A" instance = Net::DNS::RR::Types.new(nil) assert_equal("1", instance.to_str) assert_equal("A", instance.to_s) Net::DNS::RR::Types.default = "ANY" instance = Net::DNS::RR::Types.new(nil) assert_equal("255", instance.to_str) assert_equal("ANY", instance.to_s) ensure Net::DNS::RR::Types.default = Net::DNS::RR::Types::TYPES.invert[@_default] end def test_types Net::DNS::RR::Types::TYPES.each do |key, num| instance_from_string = Net::DNS::RR::Types.new(key) instance_from_num = Net::DNS::RR::Types.new(num) assert_equal(key, instance_from_string.to_s) assert_equal(num.to_s, instance_from_string.to_str) assert_equal(key, instance_from_num.to_s) assert_equal(num.to_s, instance_from_num.to_str) end assert_raises(ArgumentError) do Net::DNS::RR::Types.new({}) end end def test_regexp pattern = Net::DNS::RR::Types.regexp assert_instance_of String, pattern Net::DNS::RR::Types::TYPES.each do |key, num| assert_match /\|?#{key}\|?/, pattern end end def test_valid? assert_equal(true, Net::DNS::RR::Types.valid?("A")) assert_equal(true, Net::DNS::RR::Types.valid?(1)) assert_equal(false, Net::DNS::RR::Types.valid?("Q")) assert_equal(false, Net::DNS::RR::Types.valid?(256)) assert_raises(ArgumentError) do Net::DNS::RR::Types.valid?({}) end end def test_to_str assert_equal("A", Net::DNS::RR::Types.to_str(1)) assert_raises(ArgumentError) do Net::DNS::RR::Types.to_str(256) end assert_raises(ArgumentError) do Net::DNS::RR::Types.to_str("string") end end end gitlab-net-dns-v0.9.1/test/unit/rr_test.rb000066400000000000000000000074561355037036000205020ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRTest < Minitest::Test def setup @rr_name = "example.com." @type = "A" @cls = "IN" @ttl = 10_800 @rdata = "64.233.187.99" @defaults = Net::DNS::RR.new(name: @rr_name, rdata: @rdata) @a_hash = Net::DNS::RR.new(name: @rr_name, ttl: @ttl, cls: @cls, type: @type, address: @rdata) @a_string = Net::DNS::RR::A.new("example.com. 10800 IN A 64.233.187.99") @str = "example.com. 10800 IN A 64.233.187.99" @a = Net::DNS::RR.new("foo.example.com. 86400 A 10.1.2.3") @mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.") @cname = Net::DNS::RR.new("www.example.com IN CNAME www1.example.com") @txt = Net::DNS::RR.new('baz.example.com 3600 HS TXT "text record"') @a_data = @a.data @a_binary = Net::DNS::RR.parse(@a_data) @mx_data = @mx.data @mx_binary = Net::DNS::RR.parse(@mx_data) @array = [@rr_name, @ttl, @cls, @type, @rdata] end def test_classes assert_instance_of Net::DNS::RR::A, @a assert_instance_of Net::DNS::RR::MX, @mx assert_instance_of Net::DNS::RR::CNAME, @cname assert_instance_of Net::DNS::RR::TXT, @txt assert_instance_of Net::DNS::RR::A, @a_binary assert_instance_of Net::DNS::RR::MX, @mx_binary end def test_ttl assert_equal @a.ttl, 86_400 assert_equal @mx.ttl, 7200 assert_equal @cname.ttl, 10_800 assert_equal @txt.ttl, 3600 assert_equal @a_binary.ttl, 86_400 assert_equal @mx_binary.ttl, 7200 end def test_types assert_equal @a.type, "A" assert_equal @mx.type, "MX" assert_equal @cname.type, "CNAME" assert_equal @txt.type, "TXT" assert_equal @a_binary.type, "A" assert_equal @mx_binary.type, "MX" end def test_cls assert_equal @a.cls, "IN" assert_equal @mx.cls, "IN" assert_equal @cname.cls, "IN" assert_equal @txt.cls, "HS" assert_equal @a_binary.cls, "IN" assert_equal @mx_binary.cls, "IN" end def test_name assert_equal @a.name, "foo.example.com." assert_equal @mx.name, "example.com." assert_equal @cname.name, "www.example.com" assert_equal @txt.name, "baz.example.com" assert_equal @a_binary.name, "foo.example.com." assert_equal @mx_binary.name, "example.com." end def test_rdata assert_equal @a.address.to_s, "10.1.2.3" assert_equal @mx.preference, 10 assert_equal @mx.exchange, "mailhost.example.com." assert_equal @cname.cname, "www1.example.com" assert_equal @txt.txt, '"text record"' assert_equal @a_binary.address.to_s, "10.1.2.3" assert_equal @mx_binary.preference, 10 assert_equal @mx_binary.exchange, "mailhost.example.com." end def test_simple assert_equal @rr_name, @defaults.name assert_equal @type, @defaults.type assert_equal @cls, @defaults.cls assert_equal @ttl, @defaults.ttl assert_equal @rdata, @defaults.rdata.to_s assert_equal(@str, @a_hash.inspect) assert_equal(@rr_name, @a_hash.name) assert_equal(@type, @a_hash.type) assert_equal(@cls, @a_hash.cls) assert_equal(@ttl, @a_hash.ttl) assert_equal(@rdata, @a_hash.address.to_s) assert_equal(@str, @a_string.inspect) assert_equal(@rr_name, @a_string.name) assert_equal(@type, @a_string.type) assert_equal(@cls, @a_string.cls) assert_equal(@ttl, @a_string.ttl) assert_equal(@rdata, @a_string.address.to_s) assert_equal(@a_data, @a_binary.data) assert_equal(@mx_data, @mx_binary.data) assert_equal(@str, @a_hash.to_s) assert_equal(@array, @a_hash.to_a) end def test_range assert_raises(ArgumentError) do Net::DNS::RR.new("google.com. 10800 IM A") end end end