dnsmessage 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b0f9b39a569f631d9aee6c28f8820787ae68a3f62d24ef6d64e0b6275677dbc
4
- data.tar.gz: 6d30ad38144d9117c2991439ea274bcfc3594e787583f816638ef87af417e6dc
3
+ metadata.gz: 8443bcf9f586254aba74f3e64dec4da233e3b379f3e713dd90082de832a7b010
4
+ data.tar.gz: 8369031d2199b20dd50317fc422f9f6124fc8e313465effab31d549762fda05a
5
5
  SHA512:
6
- metadata.gz: f5a5014c67f58edd7f0ce8da1659207fc3e53ebd4167997f0d89beadf6d7c72f203f939f41c8c18f1d87c28c0506aeb6843a892109c83b8006af465b30b76935
7
- data.tar.gz: 6fc03487323152b8f51b028c6d009c3818f7a7bdd1e500d4ad4cb8fd430a8239d9c18608e4359ad5ef84b95822e6b160bdd19712735967324640547957acd414
6
+ metadata.gz: 594e07ee15de5c2cfffd76a4134e99b600fbea140584712ce4e103e28d2b97431ee5db6d55d2a7b0e04e2ab138686e35e891cac38d796730dfcb5bbcb41875f2
7
+ data.tar.gz: ea04b8007c81825812fe20f78d1b62e33046636e3a71a777725d675c07bca68af1008d6fce77d07f06806788203a56dc85aae5f6f559c111354ee04df1e319d2
@@ -9,9 +9,9 @@ name: Ruby
9
9
 
10
10
  on:
11
11
  push:
12
- branches: [ master ]
12
+ branches: [ main ]
13
13
  pull_request:
14
- branches: [ master ]
14
+ branches: [ main ]
15
15
 
16
16
  jobs:
17
17
  test:
@@ -19,7 +19,7 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['2.6', '2.7', '3.0']
22
+ ruby-version: ['2.7', '3.0']
23
23
 
24
24
  steps:
25
25
  - uses: actions/checkout@v2
data/.rubocop.yml ADDED
@@ -0,0 +1,33 @@
1
+ require:
2
+ - rubocop-rake
3
+ - rubocop-rspec
4
+
5
+ # Commonly used screens these days easily fit more than 80 characters.
6
+ Layout/LineLength:
7
+ Max: 120
8
+
9
+ # Too short methods lead to extraction of single-use methods, which can make
10
+ # the code easier to read (by naming things), but can also clutter the class
11
+ Metrics/MethodLength:
12
+ Max: 20
13
+
14
+ # The guiding principle of classes is SRP, SRP can't be accurately measured by LoC
15
+ Metrics/ClassLength:
16
+ Max: 1500
17
+
18
+ Style/StringLiterals:
19
+ Enabled: true
20
+ EnforcedStyle: double_quotes
21
+
22
+ Style/StringLiteralsInInterpolation:
23
+ Enabled: true
24
+ EnforcedStyle: double_quotes
25
+
26
+ # Most readable form.
27
+ Layout/HashAlignment:
28
+ EnforcedHashRocketStyle: table
29
+ EnforcedColonStyle: table
30
+
31
+ # Rspec kind of breaks without this
32
+ Metrics/BlockLength:
33
+ IgnoredMethods: ['describe', 'context']
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in dnsmessage.gemspec
@@ -5,3 +7,6 @@ gemspec
5
7
 
6
8
  gem "rake", "~> 12.0"
7
9
  gem "rspec", "~> 3.0"
10
+ gem "rubocop", "~> 1.0"
11
+ gem "rubocop-rake", require: false
12
+ gem "rubocop-rspec", require: false
data/README.md CHANGED
@@ -72,9 +72,9 @@ Currently implemented types are:
72
72
  * OPT
73
73
  * TXT
74
74
 
75
- Other records should be easy to add on a per-need basis as they should
76
- be based on the building blocks of already existing. Look at
77
- contributing for details on adding RR types.
75
+ Other records should be easy to add on a per-need basis as they can be
76
+ based on the building blocks of already existing parsers and builders.
77
+ Look at contributing for details on adding RR types.
78
78
 
79
79
  ## Development
80
80
 
data/Rakefile CHANGED
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "dnsmessage"
data/dnsmessage.gemspec CHANGED
@@ -1,4 +1,6 @@
1
- require_relative 'lib/dnsmessage/version'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/dnsmessage/version"
2
4
 
3
5
  Gem::Specification.new do |spec|
4
6
  spec.name = "dnsmessage"
@@ -6,13 +8,13 @@ Gem::Specification.new do |spec|
6
8
  spec.authors = ["Claus Lensbøl"]
7
9
  spec.email = ["cmol@cmol.dk"]
8
10
 
9
- spec.summary = %q{Ruby library to build and parse DNS messages}
10
- spec.description = %q{A full featured DNS parser. The library supports DNS
11
+ spec.summary = "Ruby library to build and parse DNS messages"
12
+ spec.description = 'A full featured DNS parser. The library supports DNS
11
13
  name compression and gives access to parse, build, and manipulate all aspects
12
- of the DNS queries and replies.}
14
+ of the DNS queries and replies.'
13
15
  spec.homepage = "https://github.com/cmol/dnsmessage"
14
16
  spec.license = "MIT"
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
17
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
16
18
 
17
19
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
20
 
@@ -22,10 +24,16 @@ of the DNS queries and replies.}
22
24
 
23
25
  # Specify which files should be added to the gem when it is released.
24
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
27
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
28
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
29
  end
28
30
  spec.bindir = "exe"
29
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
32
  spec.require_paths = ["lib"]
33
+
34
+ # Uncomment to register a new dependency of your gem
35
+ # spec.add_dependency "example-gem", "~> 1.0"
36
+
37
+ # For more information and examples about making a new gem, checkout our
38
+ # guide at: https://bundler.io/guides/creating_gem.html
31
39
  end
data/examples/server.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This example implements an IP discover mechanism for IPv4 and IPv6.
2
4
  # Run server with `ruby server.rb` and query with something like
3
5
  # `dig my.ip @[address_of_server]`
@@ -8,7 +10,7 @@ require "socket"
8
10
  require "dnsmessage"
9
11
 
10
12
  LISTEN_ADDR = "::"
11
- LISTEN_PORT = 12345
13
+ LISTEN_PORT = 12_345
12
14
  MSG_LENGTH = 1400
13
15
  FLAGS = 0
14
16
 
@@ -23,10 +25,10 @@ loop do
23
25
 
24
26
  # Extract client information given as array and log connection
25
27
  addr_info = Addrinfo.new(client)
26
- puts "Client connected from #{addr_info.ip_address} using " +
27
- "#{addr_info.ipv6_v4mapped? ? "IPv4" : "IPv6"}"
28
+ puts "Client connected from #{addr_info.ip_address} using " \
29
+ "#{addr_info.ipv6_v4mapped? ? "IPv4" : "IPv6"}"
28
30
 
29
- response = DNSMessage::Message::reply_to(msg)
31
+ response = DNSMessage::Message.reply_to(msg)
30
32
  opt = DNSMessage::RR.default_opt(512)
31
33
 
32
34
  # Set IPv6 defaults
@@ -39,10 +41,11 @@ loop do
39
41
  ip = addr_info.ipv6_to_ipv4.ip_address
40
42
  end
41
43
  response.answers << DNSMessage::RR.new(
42
- name: "your.ip",
43
- type: type,
44
- ttl: 10,
45
- rdata: IPAddr.new(ip))
44
+ name: "your.ip",
45
+ type: type,
46
+ ttl: 10,
47
+ rdata: IPAddr.new(ip)
48
+ )
46
49
 
47
50
  # Be nice and add an EDNS record
48
51
  response.additionals << opt
data/lib/dnsmessage.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "dnsmessage/version"
2
4
  require "dnsmessage/enums"
3
5
  require "dnsmessage/message"
@@ -1,64 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generel DNS enum definitions
1
4
  module DNSMessage
5
+ # Enums for ResourceRecord types
2
6
  module Type
3
7
  TYPE_STRINGS = {
4
- 1 => :A,
5
- 28 => :AAAA,
6
- 18 => :AFSDB,
7
- 42 => :APL,
8
- 257 => :CAA,
9
- 60 => :CDNSKEY,
10
- 59 => :CDS,
11
- 37 => :CERT,
12
- 5 => :CNAME,
13
- 62 => :CSYNC,
14
- 49 => :DHCID,
15
- 32769 => :DLV,
16
- 39 => :DNAME,
17
- 48 => :DNSKEY,
18
- 43 => :DS,
19
- 108 => :EUI48,
20
- 109 => :EUI64,
21
- 13 => :HINFO,
22
- 55 => :HIP,
23
- 45 => :IPSECKEY,
24
- 25 => :KEY,
25
- 36 => :KX,
26
- 29 => :LOC,
27
- 15 => :MX,
28
- 35 => :NAPTR,
29
- 2 => :NS,
30
- 47 => :NSE,
31
- 50 => :NSEC3,
32
- 51 => :NSEC3PARAM,
33
- 61 => :OPENPGPKEY,
34
- 12 => :PTR,
35
- 46 => :RRSIG,
36
- 17 => :RP,
37
- 24 => :SIG,
38
- 53 => :SMIMEA,
39
- 6 => :SOA,
40
- 33 => :SRV,
41
- 44 => :SSHFP,
42
- 32768 => :TA,
43
- 249 => :TKEY,
44
- 52 => :TLSA,
45
- 250 => :TSIG,
46
- 16 => :TXT,
47
- 256 => :URI,
48
- 63 => :ZONEMD,
49
- 64 => :SVCB,
50
- 65 => :HTTPS,
51
- 252 => :AXFR,
52
- 251 => :IXFR,
53
- 41 => :OPT
54
- }
8
+ 1 => :A,
9
+ 28 => :AAAA,
10
+ 18 => :AFSDB,
11
+ 42 => :APL,
12
+ 257 => :CAA,
13
+ 60 => :CDNSKEY,
14
+ 59 => :CDS,
15
+ 37 => :CERT,
16
+ 5 => :CNAME,
17
+ 62 => :CSYNC,
18
+ 49 => :DHCID,
19
+ 32_769 => :DLV,
20
+ 39 => :DNAME,
21
+ 48 => :DNSKEY,
22
+ 43 => :DS,
23
+ 108 => :EUI48,
24
+ 109 => :EUI64,
25
+ 13 => :HINFO,
26
+ 55 => :HIP,
27
+ 45 => :IPSECKEY,
28
+ 25 => :KEY,
29
+ 36 => :KX,
30
+ 29 => :LOC,
31
+ 15 => :MX,
32
+ 35 => :NAPTR,
33
+ 2 => :NS,
34
+ 47 => :NSE,
35
+ 50 => :NSEC3,
36
+ 51 => :NSEC3PARAM,
37
+ 61 => :OPENPGPKEY,
38
+ 12 => :PTR,
39
+ 46 => :RRSIG,
40
+ 17 => :RP,
41
+ 24 => :SIG,
42
+ 53 => :SMIMEA,
43
+ 6 => :SOA,
44
+ 33 => :SRV,
45
+ 44 => :SSHFP,
46
+ 32_768 => :TA,
47
+ 249 => :TKEY,
48
+ 52 => :TLSA,
49
+ 250 => :TSIG,
50
+ 16 => :TXT,
51
+ 256 => :URI,
52
+ 63 => :ZONEMD,
53
+ 64 => :SVCB,
54
+ 65 => :HTTPS,
55
+ 252 => :AXFR,
56
+ 251 => :IXFR,
57
+ 41 => :OPT
58
+ }.freeze
55
59
 
56
- TYPE_STRINGS.each do |num,name|
60
+ TYPE_STRINGS.each do |num, name|
57
61
  const_set(name, num)
58
62
  end
59
-
60
63
  end
61
64
 
65
+ # The supported DNS classes
62
66
  module Class
63
67
  IN = 1
64
68
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DNSMessage
4
+ # Message is the central class that most users of the API will use
2
5
  class Message
3
-
4
6
  NAME_POINTER = 0xc0
5
7
  POINTER_MASK = 0x3fff
6
8
  QUERY = 0
@@ -8,46 +10,54 @@ module DNSMessage
8
10
  HEADER_SIZE = 12
9
11
 
10
12
  attr_accessor :questions, :answers, :authority, :additionals,
11
- :id, :qr, :opcode, :aa, :tc, :rd, :ra, :z, :rcode,
12
- :qdcount, :ancount, :nscount, :arcount
13
+ :id, :qr, :opcode, :aa, :tc, :rd, :ra, :z, :rcode
14
+ attr_reader :qdcount, :ancount, :nscount, :arcount
13
15
 
14
- def initialize()
16
+ def initialize
15
17
  @questions = []
16
18
  @answers = []
17
19
  @additionals = []
18
20
  @authority = []
19
- @id = @qr = @opcode = @aa = @tc = @rd = @ra = @z = $rcode = 0
21
+ @qdcount = 0
22
+ @ancount = 0
23
+ @nscount = 0
24
+ @arcount = 0
25
+ @id = @qr = @opcode = @aa = @tc = @rd = @ra = @z = @rcode = 0
20
26
  end
21
27
 
22
28
  def parse(input)
23
- ptr = Pointer.new()
29
+ ptr = Pointer.new
24
30
  parse_header(input)
25
- idx = parse_questions(input, @qdcount, ptr)
26
- @answers, idx = parse_records(input, @ancount, idx, ptr)
27
- @authority, idx = parse_records(input, @nscount, idx, ptr)
28
- @additionals, idx = parse_records(input, @arcount, idx, ptr)
31
+ idx = parse_questions(input, @qdcount, ptr)
32
+ @answers, idx = parse_records(input, @ancount, idx, ptr)
33
+ @authority, idx = parse_records(input, @nscount, idx, ptr)
34
+ @additionals, = parse_records(input, @arcount, idx, ptr)
29
35
  end
30
36
 
31
37
  def self.parse(input)
32
- self.new().tap do | m |
38
+ new.tap do |m|
33
39
  m.parse(input)
34
40
  end
35
41
  end
36
42
 
37
- def self.reply_to(q)
38
- self.new().tap do | r |
39
- r.id = q.id
43
+ def self.reply_to(query)
44
+ new.tap do |r|
45
+ r.id = query.id
40
46
  r.qr = REPLY
41
- r.questions = q.questions
42
- r.qdcount = q.qdcount
47
+ r.questions = query.questions
43
48
  end
44
49
  end
45
50
 
46
-
47
51
  def parse_header(message)
48
52
  return nil if message.nil? || message.empty?
53
+
49
54
  @id, opts, @qdcount, @ancount, @nscount, @arcount =
50
55
  message[0...12].unpack("n6")
56
+
57
+ parse_opts(opts)
58
+ end
59
+
60
+ def parse_opts(opts)
51
61
  @qr = (opts >> 15) & 0x1
52
62
  @opcode = (opts >> 11) & 0xf
53
63
  @aa = (opts >> 10) & 0x1
@@ -61,7 +71,7 @@ module DNSMessage
61
71
  def parse_questions(message, num_questions, ptr)
62
72
  idx = HEADER_SIZE # Header takes up the first 12 bytes
63
73
  @questions = (0...num_questions).map do
64
- Question.parse(message, ptr, idx).tap do | q |
74
+ Question.parse(message, ptr, idx).tap do |q|
65
75
  idx += q.size
66
76
  end
67
77
  end
@@ -70,42 +80,71 @@ module DNSMessage
70
80
 
71
81
  def parse_records(message, num_records, idx, ptr)
72
82
  [num_records.times.map do
73
- ResourceRecord.parse(message[idx..-1],ptr).tap do | rr |
74
- ptr.add_arr(rr.add_to_hash,idx)
83
+ ResourceRecord.parse(message[idx..], ptr).tap do |rr|
84
+ ptr.add_arr(rr.add_to_hash, idx)
75
85
  idx += rr.size
76
86
  end
77
87
  end,
78
- idx]
88
+ idx]
79
89
  end
80
90
 
81
91
  def build
82
- ptr = Pointer.new()
83
- packet = build_header
92
+ ptr = Pointer.new
93
+ packet = build_header
84
94
  packet << build_questions(ptr, packet.size)
85
95
  packet << build_answers(ptr, packet.size)
86
96
  packet << build_authority(ptr, packet.size)
87
- packet << build_additionals(ptr,packet.size)
97
+ packet << build_additionals(ptr, packet.size)
88
98
  end
89
99
 
90
100
  def build_header
91
- opts = (@qr & 0x1) << 15 |
92
- (@opcode & 0xf) << 11 |
93
- (@aa & 0x1) << 10 |
94
- (@tc & 0x1) << 9 |
95
- (@rd & 0x1) << 8 |
96
- (@ra & 0x1) << 7 |
97
- (@z & 0x7) << 4 |
98
- (@z & 0xf)
99
- [@id, opts,
101
+ [@id, build_opts,
100
102
  @questions.length,
101
103
  @answers.length,
102
104
  @authority.length,
103
105
  @additionals.length].pack("n6")
104
106
  end
105
107
 
108
+ def build_opts
109
+ build_qr | build_opcode | build_aa | build_tc | build_rd | build_ra |
110
+ build_z | build_rcode
111
+ end
112
+
113
+ def build_qr
114
+ (@qr & 0x1) << 15
115
+ end
116
+
117
+ def build_opcode
118
+ (@opcode & 0xf) << 11
119
+ end
120
+
121
+ def build_aa
122
+ (@aa & 0x1) << 10
123
+ end
124
+
125
+ def build_tc
126
+ (@tc & 0x1) << 9
127
+ end
128
+
129
+ def build_rd
130
+ (@rd & 0x1) << 8
131
+ end
132
+
133
+ def build_ra
134
+ (@ra & 0x1) << 7
135
+ end
136
+
137
+ def build_z
138
+ (@z & 0x1) << 7
139
+ end
140
+
141
+ def build_rcode
142
+ (@rcode & 0xf)
143
+ end
144
+
106
145
  def build_questions(ptr, idx)
107
- @questions.map do | q |
108
- q.build(ptr,idx).tap do | bytes |
146
+ @questions.map do |q|
147
+ q.build(ptr, idx).tap do |bytes|
109
148
  idx += bytes.length
110
149
  end
111
150
  end.join("")
@@ -124,8 +163,8 @@ module DNSMessage
124
163
  end
125
164
 
126
165
  def build_record(ptr, idx, records)
127
- records.map do | rr |
128
- rr.build(ptr,idx).tap do | r |
166
+ records.map do |rr|
167
+ rr.build(ptr, idx).tap do |r|
129
168
  idx += r.length
130
169
  end
131
170
  end.join("")
@@ -136,6 +175,5 @@ module DNSMessage
136
175
  raise(StandardError, "No questions in query") if @qdcount < 1
137
176
  raise(StandardError, "Empty domain") if @domain_name.empty?
138
177
  end
139
-
140
178
  end
141
179
  end
@@ -1,39 +1,43 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DNSMessage
4
+ # Parsing DNS names directly or using pointers
2
5
  module Name
3
-
4
- NAME_POINTER = 0xc0
5
- POINTER_MASK = 0x3fff
6
+ NAME_POINTER = 0xc0
7
+ POINTER_MASK = 0x3fff
6
8
 
7
9
  def self.parse(record, ptr)
8
10
  # Read name loop
9
11
  idx = 0
10
12
  name = []
11
13
  loop do
12
- length = record[idx].unpack("c").first
14
+ length = record[idx].unpack1("c")
13
15
  idx += 1
14
- if length & NAME_POINTER == NAME_POINTER
15
- pointer = ((length << 8) | record[idx].unpack("c").first) & POINTER_MASK
16
- return [ptr.find(pointer),idx+1,false]
17
- elsif length == 0
18
- break
19
- else
20
- name << record[idx...idx+length]
21
- idx += length
22
- end
16
+ break if length.zero?
17
+
18
+ return parse_from_pointer(ptr, record, length, idx) \
19
+ if length & NAME_POINTER == NAME_POINTER
20
+
21
+ name << record[idx...idx + length]
22
+ idx += length
23
23
  end
24
24
  [name.join("."), idx, true]
25
25
  end
26
26
 
27
- def self.build(name,ptr)
27
+ def self.build(name, ptr)
28
28
  if ptr.find(name)
29
- [[(ptr.find(name) | NAME_POINTER << 8)].pack("n"),false]
29
+ [[(ptr.find(name) | NAME_POINTER << 8)].pack("n"), false]
30
30
  else
31
- [name_bytes = name.split(".").map do | section |
31
+ [name.split(".").map do |section|
32
32
  section.length.chr + section
33
- end.join("") + "\x0", # Terminate will nullptr
34
- true]
33
+ end.join("") << "\x0", # Terminate will nullptr
34
+ true]
35
35
  end
36
36
  end
37
37
 
38
+ def self.parse_from_pointer(ptr, record, length, idx)
39
+ [ptr.find(((length << 8) | record[idx].unpack1("c")) & POINTER_MASK),
40
+ idx + 1, false]
41
+ end
38
42
  end
39
43
  end
@@ -1,24 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DNSMessage
4
+ # Handle DNS pointers
2
5
  class Pointer
3
-
4
6
  def initialize(hash = {})
5
7
  @hash = hash
6
8
  end
7
9
 
8
10
  def add_arr(arr, offset)
9
11
  return unless arr
10
- arr.each do |k,v|
11
- k += offset if k.class == Integer
12
- v += offset if v.class == Integer
13
- add(k,v)
12
+
13
+ arr.each do |k, v|
14
+ k += offset if k.instance_of?(Integer)
15
+ v += offset if v.instance_of?(Integer)
16
+ add(k, v)
14
17
  end
15
18
  end
16
19
 
17
- def add(key,value)
18
- if key.class == Integer
19
- add_name(key,value)
20
+ def add(key, value)
21
+ if key.instance_of?(Integer)
22
+ add_name(key, value)
20
23
  else
21
- add_ptr(key,value)
24
+ add_ptr(key, value)
22
25
  end
23
26
  end
24
27
 
@@ -32,23 +35,24 @@ module DNSMessage
32
35
 
33
36
  private
34
37
 
35
- def add_name(key,value)
38
+ def add_name(key, value)
36
39
  value = value.split(".")
37
40
  loop do
38
41
  return unless value.length > 1
42
+
39
43
  @hash[key] = value.join(".")
40
44
  key += value.shift.length + 1
41
45
  end
42
46
  end
43
47
 
44
- def add_ptr(key,value)
48
+ def add_ptr(key, value)
45
49
  key = key.split(".")
46
50
  loop do
47
51
  return unless key.length > 1
52
+
48
53
  @hash[key.join(".")] = value
49
54
  value += key.shift.length + 1
50
55
  end
51
56
  end
52
-
53
57
  end
54
58
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DNSMessage
4
+ # Parse and build DNS questions
2
5
  class Question
3
-
4
6
  attr_accessor :name, :type, :klass
5
7
  attr_reader :size, :add_to_hash
6
8
 
@@ -11,25 +13,24 @@ module DNSMessage
11
13
  end
12
14
 
13
15
  def self.parse(question, ptr, idx)
14
- self.new().tap do | q |
16
+ new.tap do |q|
15
17
  q.parse(question, ptr, idx)
16
18
  end
17
19
  end
18
20
 
19
21
  def parse(question, ptr, idx)
20
- @name, @size, add = Name.parse(question[idx..-1], ptr)
21
- ptr.add(idx, @name) if add
22
+ @name, @size, add = Name.parse(question[idx..], ptr)
23
+ ptr.add(idx, @name) if add
22
24
 
23
- # take last four bytes
24
- @type, @klass = question[(idx+@size)..-1].unpack("n2")
25
- @size += 4
25
+ # take last four bytes
26
+ @type, @klass = question[(idx + @size)..].unpack("n2")
27
+ @size += 4
26
28
  end
27
29
 
28
- def build(ptr,idx)
29
- name_bytes, add = Name.build(@name,ptr)
30
- ptr.add(@name, idx) if add
31
- name_bytes + [type,klass].pack("n2")
30
+ def build(ptr, idx)
31
+ name_bytes, add = Name.build(@name, ptr)
32
+ ptr.add(@name, idx) if add
33
+ name_bytes + [type, klass].pack("n2")
32
34
  end
33
-
34
35
  end
35
36
  end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DNSMessage
4
+ # Parse and build Resource Records
2
5
  class ResourceRecord
3
-
4
6
  PARSERS = {
5
7
  Type::A => :parse_ip,
6
8
  Type::AAAA => :parse_ip,
7
9
  Type::CNAME => :parse_name,
8
10
  Type::OPT => :parse_opt,
9
11
  Type::TXT => :parse_text
10
- }
12
+ }.freeze
11
13
 
12
14
  BUILDERS = {
13
15
  Type::A => :build_ip,
@@ -15,10 +17,10 @@ module DNSMessage
15
17
  Type::CNAME => :build_name,
16
18
  Type::OPT => :build_opt,
17
19
  Type::TXT => :build_text
18
- }
20
+ }.freeze
19
21
 
20
22
  attr_accessor :name, :type, :klass, :ttl, :rdata,
21
- :opt_udp, :opt_rcode, :opt_edns0_version, :opt_z_dnssec
23
+ :opt_udp, :opt_rcode, :opt_edns0_version, :opt_z_dnssec
22
24
  attr_reader :size, :add_to_hash
23
25
 
24
26
  def initialize(name: nil, type: nil, klass: Class::IN, ttl: 0,
@@ -31,36 +33,32 @@ module DNSMessage
31
33
  @add_to_hash = []
32
34
  end
33
35
 
34
- def add_to_hash
35
- @add_to_hash
36
- end
37
-
38
36
  def self.parse(record, ptr)
39
- self.new().tap do |rr|
37
+ new.tap do |rr|
40
38
  rr.parse(record, ptr)
41
39
  end
42
40
  end
43
41
 
44
42
  def parse(record, ptr)
45
- @name, idx, add = Name.parse(record,ptr)
43
+ @name, idx, add = Name.parse(record, ptr)
46
44
  @add_to_hash << [idx, @name] if add
47
- @type, @klass, @ttl, rdata_length = record[idx...idx+10].unpack("nnNn")
48
- @rdata = send(parser(@type), record, idx+10, rdata_length, ptr)
45
+ @type, @klass, @ttl, rdata_length = record[idx...idx + 10].unpack("nnNn")
46
+ @rdata = send(parser(@type), record, idx + 10, rdata_length, ptr)
49
47
  @size = idx + 10 + rdata_length
50
48
  end
51
49
 
52
- def build(ptr,idx)
50
+ def build(ptr, idx)
53
51
  return "" unless BUILDERS[type]
54
52
 
55
53
  name_bytes, add = Name.build(@name, ptr)
56
54
  ptr.add(@name, idx) if add
57
- data = send(builder(@type),ptr, idx + name_bytes.length)
55
+ data = send(builder(@type), ptr, idx + name_bytes.length)
58
56
  @rdata_length = data.length
59
57
  name_bytes + [@type, @klass, @ttl, @rdata_length].pack("nnNn") + data
60
58
  end
61
59
 
62
60
  def self.default_opt(size)
63
- self.new().tap do | opt |
61
+ new.tap do |opt|
64
62
  opt.name = ""
65
63
  opt.type = Type::OPT
66
64
  opt.opt_udp = size
@@ -84,25 +82,25 @@ module DNSMessage
84
82
  ## Parsers
85
83
  ##
86
84
 
87
- def parse_ip(rdata, start, length, ptr)
88
- IPAddr.new_ntoh(rdata[start...start+length])
85
+ def parse_ip(rdata, start, length, _ptr)
86
+ IPAddr.new_ntoh(rdata[start...start + length])
89
87
  end
90
88
 
91
- def parse_opt(rdata, start, length, ptr)
92
- @opt_udp = @klass
89
+ def parse_opt(_rdata, _start, _length, _ptr)
90
+ @opt_udp = @klass
93
91
  @opt_rcode, @opt_edns0_version, @opt_z_dnssec =
94
92
  [@ttl].pack("N").unpack("CCn")
95
93
  @opt_z_dnssec = @opt_z_dnssec >> 15
96
94
  end
97
95
 
98
- def parse_text(rdata, start, length, ptr)
96
+ def parse_text(rdata, start, _length, _ptr)
99
97
  txt_length = rdata[start].ord
100
- rdata[start+1..start+txt_length]
98
+ rdata[start + 1..start + txt_length]
101
99
  end
102
100
 
103
101
  def parse_name(rdata, start, length, ptr)
104
102
  name, idx, add = Name.parse(rdata[0...length], ptr)
105
- @add_to_hash << [start+idx, name] if add
103
+ @add_to_hash << [start + idx, name] if add
106
104
  name
107
105
  end
108
106
 
@@ -110,29 +108,28 @@ module DNSMessage
110
108
  ## Builders
111
109
  ##
112
110
 
113
- def build_ip(ptr, _)
111
+ def build_ip(_ptr, _)
114
112
  @rdata.hton
115
113
  end
116
114
 
117
- def build_opt(ptr, _)
115
+ def build_opt(_ptr, _)
118
116
  @klass = @opt_udp
119
117
  @ttl = [@opt_rcode,
120
118
  @opt_edns0_version,
121
- @opt_z_dnssec << 15].pack("CCn").unpack("N").first
119
+ @opt_z_dnssec << 15].pack("CCn").unpack1("N")
122
120
  "" # Set RDATA to nothing
123
121
  end
124
122
 
125
- def build_text(ptr, _)
123
+ def build_text(_ptr, _)
126
124
  @rdata.length.chr + rdata
127
125
  end
128
126
 
129
127
  def build_name(ptr, idx)
130
- Name.build(@rdata, ptr).tap do | bytes, add |
131
- ptr.add(@rdata,idx) if add
128
+ Name.build(@rdata, ptr).tap do |bytes, add|
129
+ ptr.add(@rdata, idx) if add
132
130
  return bytes
133
131
  end
134
132
  end
135
-
136
133
  end
137
134
 
138
135
  # Add "alias"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DNSMessage
2
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
3
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dnsmessage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claus Lensbøl
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-15 00:00:00.000000000 Z
11
+ date: 2021-02-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  A full featured DNS parser. The library supports DNS
@@ -23,6 +23,7 @@ files:
23
23
  - ".github/workflows/ruby.yml"
24
24
  - ".gitignore"
25
25
  - ".rspec"
26
+ - ".rubocop.yml"
26
27
  - ".travis.yml"
27
28
  - CODE_OF_CONDUCT.md
28
29
  - Gemfile
@@ -49,7 +50,7 @@ metadata:
49
50
  homepage_uri: https://github.com/cmol/dnsmessage
50
51
  source_code_uri: https://github.com/cmol/dnsmessage
51
52
  changelog_uri: https://github.com/cmol/dnsmessage
52
- post_install_message:
53
+ post_install_message:
53
54
  rdoc_options: []
54
55
  require_paths:
55
56
  - lib
@@ -57,16 +58,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
58
  requirements:
58
59
  - - ">="
59
60
  - !ruby/object:Gem::Version
60
- version: 2.3.0
61
+ version: 2.7.0
61
62
  required_rubygems_version: !ruby/object:Gem::Requirement
62
63
  requirements:
63
64
  - - ">="
64
65
  - !ruby/object:Gem::Version
65
66
  version: '0'
66
67
  requirements: []
67
- rubyforge_project:
68
- rubygems_version: 2.7.6
69
- signing_key:
68
+ rubygems_version: 3.2.3
69
+ signing_key:
70
70
  specification_version: 4
71
71
  summary: Ruby library to build and parse DNS messages
72
72
  test_files: []