latte 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -3,6 +3,7 @@ PATH
3
3
  specs:
4
4
  latte (0.0.1)
5
5
  bindata
6
+ net-dns
6
7
  null_logger
7
8
  pethau (>= 0.0.2)
8
9
 
@@ -10,8 +11,9 @@ GEM
10
11
  remote: http://rubygems.org/
11
12
  specs:
12
13
  bindata (1.4.3)
14
+ net-dns (0.6.1)
13
15
  null_logger (0.0.1)
14
- pethau (0.0.2)
16
+ pethau (0.0.3)
15
17
 
16
18
  PLATFORMS
17
19
  ruby
data/latte.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  s.add_runtime_dependency "pethau", ">= 0.0.2"
22
+ s.add_runtime_dependency "net-dns"
22
23
  s.add_runtime_dependency "null_logger"
23
24
  s.add_runtime_dependency "bindata"
24
25
  end
data/lib/latte/query.rb CHANGED
@@ -1,129 +1,56 @@
1
1
  module Latte
2
2
  class Query
3
- # QTYPE codes:
4
- # A 1 a host address
5
- # NS 2 an authoritative name server
6
- # MD 3 a mail destination (Obsolete - use MX)
7
- # MF 4 a mail forwarder (Obsolete - use MX)
8
- # CNAME 5 the canonical name for an alias
9
- # SOA 6 marks the start of a zone of authority
10
- # MB 7 a mailbox domain name (EXPERIMENTAL)
11
- # MG 8 a mail group member (EXPERIMENTAL)
12
- # MR 9 a mail rename domain name (EXPERIMENTAL)
13
- # NULL 10 a null RR (EXPERIMENTAL)
14
- # WKS 11 a well known service description
15
- # PTR 12 a domain name pointer
16
- # HINFO 13 host information
17
- # MINFO 14 mailbox or mail list information
18
- # MX 15 mail exchange
19
- # TXT 16 text strings
3
+ include Enumerable
4
+ initialize_with :raw_query, :logger
20
5
 
21
- initialize_with :raw_query
6
+ class Question
7
+ initialize_with :wrapped_question
22
8
 
23
- # DNS MESSAGE FORMAT
24
- #
25
- # Header
26
- # Question
27
- # Answer
28
- # Authority
29
- # Additional
30
- #
31
- # DNS HEADER FORMAT
32
- #
33
- # OCTET 1,2 ID
34
- # OCTET 3,4 QR(1 bit) + OPCODE(4 bit)+ AA(1 bit) + TC(1 bit) +
35
- # RD(1 bit)+ RA(1 bit) + Z(3 bit) + RCODE(4 bit)
36
- # OCTET 5,6 QDCOUNT
37
- # OCTET 7,8 ANCOUNT
38
- # OCTET 9,10 NSCOUNT
39
- # OCTET 11,12 ARCOUNT
40
- #
41
- # QUESTION FORMAT
42
- #
43
- # OCTET 1,2,…n QNAME
44
- # OCTET n+1,n+2 QTYPE
45
- # OCTET n+3,n+4 QCLASS
46
- #
47
- # ANSWER, AUTHORITY, ADDITIONAL FORMAT
48
- #
49
- # OCTET 1,2,..n NAME
50
- # OCTET n+1,n+2 TYPE
51
- # OCTET n+3,n+4 CLASS
52
- # OCTET n+5,n+6,n+7,n+8 TTL
53
- # OCTET n+9,n+10 RDLENGTH
54
- # OCTET n+11,n+12,….. RDATA
55
- class QueryHeader < BinData::Record
56
- endian :big
57
- uint16 :id
58
- bit1 :qr
59
- bit4 :opcode
60
- bit1 :aa
61
- bit1 :tc
62
- bit1 :rd
63
- bit1 :ra
64
- bit3 :z
65
- bit4 :rcode
66
- uint16 :qdcount
67
- uint16 :ancount
68
- uint16 :nscount
69
- uint16 :arcount
70
- end
9
+ def qname
10
+ wrapped_question.qName
11
+ end
71
12
 
72
- class QueryRequest < QueryHeader
73
- stringz :qname
74
- uint16 :qtype
75
- uint16 :qclass
76
- end
13
+ def qclass
14
+ wrapped_question.qClass
15
+ end
77
16
 
78
- def parsed_header
79
- @parsed_header ||= build_header
17
+ def qtype
18
+ wrapped_question.qType
19
+ end
80
20
  end
81
- private :parsed_header
82
21
 
83
- def build_header
84
- QueryHeader.read raw_query
22
+ def to_s
23
+ packet.to_s
85
24
  end
86
- private :build_header
87
25
 
88
- def parsed_record
89
- @parsed_record ||= build_record
26
+ def <=> other
27
+ to_s <=> other.to_s
90
28
  end
91
- private :parsed_record
92
29
 
93
- def build_record
94
- case parsed_header.qr.value
95
- when 0
96
- QueryRequest.read raw_query
97
- else
98
- raise "Unhandled QR value: #{parsed_header.qr.value}"
30
+ def each
31
+ questions.each do |q|
32
+ question = Question.new q
33
+ yield question
99
34
  end
100
35
  end
36
+ alias_method :each_question, :each
101
37
 
102
- def method_missing *args
103
- result = parsed_record.send *args
104
- return result.value if result.respond_to? :value
105
- result
106
- end
107
-
108
- def header
109
- "ID=#{id} QR=#{qr} TC=#{tc} RD=#{rd} RA=#{ra} Z=#{z} " + \
110
- "RCODE=#{rcode} QDCOUNT=#{qdcount} ANCOUNT=#{ancount} " + \
111
- "NSCOUNT=#{nscount} ARCOUNT=#{arcount}"
38
+ def id
39
+ packet.header.id
112
40
  end
113
41
 
114
- def human_qname
115
- value = parsed_record.qname.value.dup
116
- value.gsub! /^[\x00-\x1f]/, ''
117
- value.gsub! /[\x00-\x1f]/, '.'
118
- value + '.'
42
+ def packet
43
+ @packet ||= build_packet
119
44
  end
120
45
 
121
- def query
122
- "QNAME=#{human_qname} QTYPE=#{qtype} QCLASS=#{qclass}"
46
+ def build_packet
47
+ Net::DNS::Packet.parse raw_query
123
48
  end
49
+ private :build_packet
124
50
 
125
- def to_s
126
- [ header, query ].join " "
51
+ def questions
52
+ packet.question
127
53
  end
54
+ private :questions
128
55
  end
129
56
  end
@@ -1,191 +1,29 @@
1
1
  module Latte
2
2
  class Response
3
- initialize_with :query
3
+ initialize_with :query, :logger
4
4
 
5
- class BigEndianRecord < BinData::Record
6
- endian :big
5
+ def add_answer record
6
+ rr = parse_rr_string record
7
+ packet.answer << rr
7
8
  end
8
9
 
9
- class ResponseHeader < BigEndianRecord
10
- uint16 :id
11
- bit1 :qr
12
- bit4 :opcode
13
- bit1 :aa
14
- bit1 :tc
15
- bit1 :rd
16
- bit1 :ra
17
- bit3 :z, :value => 0 # Reserved for future use
18
- bit4 :rcode
19
- uint16 :qdcount
20
- uint16 :ancount
21
- uint16 :nscount
22
- uint16 :arcount
10
+ def data
11
+ packet.data
23
12
  end
24
13
 
25
- class Question < BigEndianRecord
26
- stringz :qname
27
- uint16 :qtype
28
- uint16 :qclass
29
- end
30
-
31
- class Answer < BigEndianRecord
32
- stringz :qname
33
- uint16 :qtype
34
- uint16 :qclass
35
- uint32 :ttl
36
- uint16 :rdlength, :value => lambda { rdata.length }
37
- string :rdata
38
- end
39
-
40
- def header
41
- ResponseHeader.new.tap { |h|
42
- h.id = query.id
43
- h.qr = 1 # I'm a response
44
- h.opcode = 0 # I'm a standard query
45
- h.aa = 0 # I'm not authoritative
46
- h.tc = 0 # I wasn't truncated
47
- h.rd = 0 # Please don't recursively query
48
- h.ra = 0 # Recursion isn't welcome here
49
- h.rcode = 0 # There are no errors here
50
- h.qdcount = 1 # I'm answering one query
51
- h.ancount = answers.size # The number of answer records I'm sending
52
- h.nscount = 0 # There are 0 NS records in the authority part
53
- h.arcount = 0 # How many additional records am I sending?
54
- }
55
- end
56
-
57
- def question
58
- Question.new.tap { |q|
59
- q.qname = query.qname
60
- q.qtype = query.qtype
61
- q.qclass = query.qclass
62
- }
63
- end
64
-
65
- def answers
66
- @answers ||= [ ]
67
- end
68
-
69
- class RecordParser
70
- initialize_with :record_string
71
- private_attr_accessor :record
72
- public :record
73
-
74
- # QTYPE codes:
75
- # A 1 a host address
76
- # NS 2 an authoritative name server
77
- # MD 3 a mail destination (Obsolete - use MX)
78
- # MF 4 a mail forwarder (Obsolete - use MX)
79
- # CNAME 5 the canonical name for an alias
80
- # SOA 6 marks the start of a zone of authority
81
- # MB 7 a mailbox domain name (EXPERIMENTAL)
82
- # MG 8 a mail group member (EXPERIMENTAL)
83
- # MR 9 a mail rename domain name (EXPERIMENTAL)
84
- # NULL 10 a null RR (EXPERIMENTAL)
85
- # WKS 11 a well known service description
86
- # PTR 12 a domain name pointer
87
- # HINFO 13 host information
88
- # MINFO 14 mailbox or mail list information
89
- # MX 15 mail exchange
90
- # TXT 16 text strings
91
-
92
- def qname
93
- parts[0]
94
- end
95
-
96
- def qclass
97
- parts[1]
98
- end
99
-
100
- def encoded_qclass
101
- {
102
- 'IN' => 1,
103
- 'CH' => 3,
104
- 'HS' => 4
105
- }[qclass]
106
- end
107
-
108
- def qtype
109
- parts[2]
110
- end
111
-
112
- def encoded_qtype
113
- {
114
- 'A' => 1,
115
- 'NS' => 2,
116
- 'CNAME' => 5,
117
- 'SOA' => 6,
118
- 'PTR' => 12,
119
- 'HINFO' => 13,
120
- 'MINFO' => 14,
121
- 'MX' => 15,
122
- 'TXT' => 16
123
- }[qtype]
124
- end
125
-
126
- def ttl
127
- parts[3].to_i
128
- end
129
-
130
- def rdata
131
- parts[4]
132
- end
133
-
134
- def encode_name name
135
- parts = name.split /\./
136
- parts.map! { |p| BinData::Uint8.new(p.length).to_binary_s + p }
137
- parts << BinData::Uint8.new(0).to_binary_s
138
- parts.join ''
139
- end
140
-
141
- def encoded_qname
142
- encode_name qname
143
- end
144
-
145
- def encoded_rdata
146
- # FIXME: Extract this case statment into separate encoders
147
- case qtype
148
- when 'A', 'PTR'
149
- parts = rdata.split /\./
150
- parts.map! { |o| BinData::Uint8.new(o.to_i).to_binary_s }
151
- parts.join ''
152
- when 'NS', 'CNAME'
153
- encode_name rdata
154
- else
155
- raise "I don't know how to encode QTYPE #{qtype.inspect}"
156
- end
157
- end
158
-
159
- def parts
160
- string = record_string.dup
161
- string.strip!
162
- parts = string.split /\s+/, 5
163
- parts.map! { |p| p.strip }
164
- parts
165
- end
166
-
167
- def execute
168
- self.record = Answer.new.tap { |a|
169
- a.qname = encoded_qname
170
- a.qtype = encoded_qtype
171
- a.qclass = encoded_qclass
172
- a.ttl = ttl
173
- a.rdata = encoded_rdata
174
- }
175
- end
14
+ def to_s
15
+ packet.to_s
176
16
  end
177
17
 
178
- def add record
179
- parser = RecordParser.new record
180
- parser.execute
181
- record = parser.record
182
- answers << record
18
+ def parse_rr_string string
19
+ Net::DNS::RR.new string
183
20
  end
21
+ private :parse_rr_string
184
22
 
185
- def to_s
186
- [ header, question, *answers ].map { |part|
187
- part.to_binary_s
188
- }.join ''
23
+ def packet
24
+ query.packet
189
25
  end
26
+ private :packet
190
27
  end
191
28
  end
29
+
data/lib/latte/server.rb CHANGED
@@ -59,11 +59,16 @@ module Latte
59
59
  begin
60
60
  query = Query.new data
61
61
  client_name = client.remote_address.ip_unpack.join ':'
62
- logger.debug "#{client_name} > #{address}: #{HexPresenter.new(data)}"
63
- response = Response.new query
64
- resolver.call query, response
65
- logger.debug "#{client_name} < #{address}: #{HexPresenter.new(response)}"
66
- client.reply response.to_s
62
+ logger.info "#{client_name} > #{address}: Received query:\n#{query}"
63
+ logger.debug "#{client_name} > #{address}: Packet: #{HexPresenter.new(data)}"
64
+ response = Response.new query, logger
65
+ query.each_question do |question|
66
+ resolver.call question, response
67
+ end
68
+ answer = response.data
69
+ logger.info "#{client_name} < #{address}: Sending repsonse:\n#{response}"
70
+ logger.debug "#{client_name} < #{address}: Packet: #{HexPresenter.new(answer)}"
71
+ client.reply answer
67
72
  rescue => e
68
73
  logger.error [ e.message, e.backtrace ].flatten.join("\n")
69
74
  end
data/lib/latte/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Latte
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/latte.rb CHANGED
@@ -2,6 +2,7 @@ require 'null_logger'
2
2
  require 'socket'
3
3
  require 'bindata'
4
4
  require 'pethau'
5
+ require 'net/dns/packet'
5
6
 
6
7
  class Object
7
8
  include Pethau::InitializeWith
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: latte
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-26 00:00:00.000000000Z
12
+ date: 2011-10-30 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pethau
16
- requirement: &70282543179020 !ruby/object:Gem::Requirement
16
+ requirement: &70193546165300 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,21 @@ dependencies:
21
21
  version: 0.0.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70282543179020
24
+ version_requirements: *70193546165300
25
+ - !ruby/object:Gem::Dependency
26
+ name: net-dns
27
+ requirement: &70193546164880 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70193546164880
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: null_logger
27
- requirement: &70282539604060 !ruby/object:Gem::Requirement
38
+ requirement: &70193546164420 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ! '>='
@@ -32,10 +43,10 @@ dependencies:
32
43
  version: '0'
33
44
  type: :runtime
34
45
  prerelease: false
35
- version_requirements: *70282539604060
46
+ version_requirements: *70193546164420
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: bindata
38
- requirement: &70282539603600 !ruby/object:Gem::Requirement
49
+ requirement: &70193546164000 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ! '>='
@@ -43,7 +54,7 @@ dependencies:
43
54
  version: '0'
44
55
  type: :runtime
45
56
  prerelease: false
46
- version_requirements: *70282539603600
57
+ version_requirements: *70193546164000
47
58
  description: Talks DNS and passes queries back to a query resolver build by you that
48
59
  just talks Ruby
49
60
  email: