faildns 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/bin/failnamed +203 -0
  2. data/bin/failresolve +37 -0
  3. data/lib/faildns.rb +20 -0
  4. data/lib/faildns/class.rb +96 -0
  5. data/lib/faildns/client.rb +114 -0
  6. data/lib/faildns/common.rb +52 -0
  7. data/lib/faildns/domainname.rb +223 -0
  8. data/lib/faildns/header.rb +254 -0
  9. data/lib/faildns/header/opcode.rb +95 -0
  10. data/lib/faildns/header/status.rb +123 -0
  11. data/lib/faildns/header/type.rb +72 -0
  12. data/lib/faildns/ip.rb +57 -0
  13. data/lib/faildns/message.rb +100 -0
  14. data/lib/faildns/qclass.rb +51 -0
  15. data/lib/faildns/qtype.rb +60 -0
  16. data/lib/faildns/question.rb +101 -0
  17. data/lib/faildns/resourcerecord.rb +140 -0
  18. data/lib/faildns/resourcerecord/IN.rb +29 -0
  19. data/lib/faildns/resourcerecord/IN/A.rb +75 -0
  20. data/lib/faildns/resourcerecord/IN/AAAA.rb +77 -0
  21. data/lib/faildns/resourcerecord/IN/CNAME.rb +70 -0
  22. data/lib/faildns/resourcerecord/IN/HINFO.rb +79 -0
  23. data/lib/faildns/resourcerecord/IN/MX.rb +75 -0
  24. data/lib/faildns/resourcerecord/IN/NS.rb +77 -0
  25. data/lib/faildns/resourcerecord/IN/NULL.rb +66 -0
  26. data/lib/faildns/resourcerecord/IN/PTR.rb +69 -0
  27. data/lib/faildns/resourcerecord/IN/SOA.rb +128 -0
  28. data/lib/faildns/resourcerecord/IN/TXT.rb +63 -0
  29. data/lib/faildns/resourcerecord/data.rb +41 -0
  30. data/lib/faildns/server.rb +71 -0
  31. data/lib/faildns/server/dispatcher.rb +181 -0
  32. data/lib/faildns/server/dispatcher/connectiondispatcher.rb +93 -0
  33. data/lib/faildns/server/dispatcher/event.rb +47 -0
  34. data/lib/faildns/server/dispatcher/eventdispatcher.rb +73 -0
  35. data/lib/faildns/server/dispatcher/socket.rb +93 -0
  36. data/lib/faildns/type.rb +186 -0
  37. metadata +100 -0
@@ -0,0 +1,52 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
3
+ #
4
+ # This file is part of faildns.
5
+ #
6
+ # faildns is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # faildns is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with faildns. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ module DNS
21
+ Version = '0.0.1'
22
+
23
+ def self.debug (argument, options={})
24
+ if !ENV['DEBUG']
25
+ return
26
+ end
27
+
28
+ if ENV['DEBUG'].to_i < (options[:level] || 1)
29
+ return
30
+ end
31
+
32
+ output = "From: #{caller[0, options[:deep] || 1].join("\n")}\n"
33
+
34
+ if argument.is_a?(Exception)
35
+ output << "#{argument.class}: #{argument.message}\n"
36
+ output << argument.backtrace.collect {|stack|
37
+ stack
38
+ }.join("\n")
39
+ output << "\n\n"
40
+ elsif argument.is_a?(String)
41
+ output << "#{argument}\n"
42
+ else
43
+ output << "#{argument.inspect}\n"
44
+ end
45
+
46
+ if options[:separator]
47
+ output << options[:separator]
48
+ end
49
+
50
+ puts output
51
+ end
52
+ end
@@ -0,0 +1,223 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
3
+ #
4
+ # This file is part of faildns.
5
+ #
6
+ # faildns is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # faildns is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with faildns. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ module DNS
21
+
22
+ #--
23
+ # Domain names in messages are expressed in terms of a sequence of labels.
24
+ # Each label is represented as a one octet length field followed by that
25
+ # number of octets. Since every domain name ends with the null label of
26
+ # the root, a domain name is terminated by a length byte of zero. The
27
+ # high order two bits of every length octet must be zero, and the
28
+ # remaining six bits of the length field limit the label to 63 octets or
29
+ # less.
30
+ #
31
+ # To simplify implementations, the total length of a domain name (i.e.,
32
+ # label octets and label length octets) is restricted to 255 octets or
33
+ # less.
34
+ #
35
+ # Although labels can contain any 8 bit values in octets that make up a
36
+ # label, it is strongly recommended that labels follow the preferred
37
+ # syntax described elsewhere in this memo, which is compatible with
38
+ # existing host naming conventions. Name servers and resolvers must
39
+ # compare labels in a case-insensitive manner (i.e., A=a), assuming ASCII
40
+ # with zero parity. Non-alphabetic codes must match exactly.
41
+ #
42
+ # In order to reduce the size of messages, the domain system utilizes a
43
+ # compression scheme which eliminates the repetition of domain names in a
44
+ # message. In this scheme, an entire domain name or a list of labels at
45
+ # the end of a domain name is replaced with a pointer to a prior occurance
46
+ # of the same name.
47
+ #
48
+ # The pointer takes the form of a two octet sequence:
49
+ #
50
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
51
+ # | 1 1| OFFSET |
52
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
53
+ #
54
+ # The first two bits are ones. This allows a pointer to be distinguished
55
+ # from a label, since the label must begin with two zero bits because
56
+ # labels are restricted to 63 octets or less. (The 10 and 01 combinations
57
+ # are reserved for future use.) The OFFSET field specifies an offset from
58
+ # the start of the message (i.e., the first octet of the ID field in the
59
+ # domain header). A zero offset specifies the first byte of the ID field,
60
+ # etc.
61
+ #
62
+ # The compression scheme allows a domain name in a message to be
63
+ # represented as either:
64
+ #
65
+ # - a sequence of labels ending in a zero octet
66
+ #
67
+ # - a pointer
68
+ #
69
+ # - a sequence of labels ending with a pointer
70
+ #
71
+ # Pointers can only be used for occurances of a domain name where the
72
+ # format is not class specific. If this were not the case, a name server
73
+ # or resolver would be required to know the format of all RRs it handled.
74
+ # As yet, there are no such cases, but they may occur in future RDATA
75
+ # formats.
76
+ #
77
+ # If a domain name is contained in a part of the message subject to a
78
+ # length field (such as the RDATA section of an RR), and compression is
79
+ # used, the length of the compressed name is used in the length
80
+ # calculation, rather than the length of the expanded name.
81
+ #
82
+ # Programs are free to avoid using pointers in messages they generate,
83
+ # although this will reduce datagram capacity, and may cause truncation.
84
+ # However all programs are required to understand arriving messages that
85
+ # contain pointers.
86
+ #
87
+ # For example, a datagram might need to use the domain names F.ISI.ARPA,
88
+ # FOO.F.ISI.ARPA, ARPA, and the root. Ignoring the other fields of the
89
+ # message, these domain names might be represented as:
90
+ #
91
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
92
+ # 20 | 1 | F |
93
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
94
+ # 22 | 3 | I |
95
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
96
+ # 24 | S | I |
97
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
98
+ # 26 | 4 | A |
99
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
100
+ # 28 | R | P |
101
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
102
+ # 30 | A | 0 |
103
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
104
+ #
105
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
106
+ # 40 | 3 | F |
107
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
108
+ # 42 | O | O |
109
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
110
+ # 44 | 1 1| 20 |
111
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
112
+ #
113
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
114
+ # 64 | 1 1| 26 |
115
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
116
+ #
117
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
118
+ # 92 | 0 | |
119
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
120
+ #
121
+ # The domain name for F.ISI.ARPA is shown at offset 20. The domain name
122
+ # FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
123
+ # concatenate a label for FOO to the previously defined F.ISI.ARPA. The
124
+ # domain name ARPA is defined at offset 64 using a pointer to the ARPA
125
+ # component of the name F.ISI.ARPA at 20; note that this pointer relies on
126
+ # ARPA being the last label in the string at 20. The root domain name is
127
+ # defined by a single octet of zeros at 92; the root domain name has no
128
+ # labels.
129
+ #++
130
+
131
+ class DomainName
132
+ def self.pointer (string, offset)
133
+ string.force_encoding 'BINARY'
134
+
135
+ return string[offset.unpack('n').first & 0x3FFF, 512]
136
+ end
137
+
138
+ def self.parse (string, whole)
139
+ string.force_encoding 'BINARY'
140
+
141
+ result = ''
142
+
143
+ case string.unpack('c').first & 0xC0
144
+ when 0xC0
145
+ result += DomainName.parse(DomainName.pointer(whole, string), whole)
146
+ string[0, 2] = ''
147
+
148
+ when 0x00
149
+ while (length = string.unpack('c').first) != 0 && (length & 0xC0) == 0
150
+ result += '.' + string[1, length]
151
+ string[0, length + 1] = ''
152
+ end
153
+
154
+ if length & 0xC0 == 0xC0
155
+ result += '.' + DomainName.parse(string, whole)
156
+
157
+ string[0, 2] = ''
158
+ else
159
+ string[0, 1] = ''
160
+ end
161
+
162
+ result[0, 1] = ''
163
+ end
164
+
165
+ DomainName.new result
166
+ end
167
+
168
+ def self.length (string)
169
+ string.force_encoding 'BINARY'
170
+
171
+ string = string.clone
172
+ result = 0
173
+
174
+ if string.unpack('c').first & 0xC0 == 0xC0
175
+ return 2
176
+ else
177
+ while (length = string.unpack('c').first) != 0 && (length & 0xC0) == 0
178
+ result += 1 + length
179
+ string[0, length + 1] = ''
180
+ end
181
+
182
+ if length & 0xC0 == 0xC0
183
+ result += 2
184
+ else
185
+ result += 1
186
+ end
187
+ end
188
+
189
+ return result
190
+ end
191
+
192
+ attr_accessor :domain
193
+
194
+ def initialize (domain)
195
+ if domain.is_a? DomainName
196
+ domain = domain.domain
197
+ end
198
+
199
+ if !domain.is_a? String
200
+ raise ArgumentError.new 'The passed value is not a string.'
201
+ end
202
+
203
+ @domain = domain
204
+ end
205
+
206
+ def to_s
207
+ @domain
208
+ end
209
+
210
+ alias to_str to_s
211
+
212
+ def pack (options={})
213
+ result = ''
214
+
215
+ @domain.split('.').each {|part|
216
+ result += [part.length].pack('c') + part
217
+ }
218
+
219
+ result += [0].pack('c')
220
+ end
221
+ end
222
+
223
+ end
@@ -0,0 +1,254 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.doesntexist.org | meh@paranoici.org]
3
+ #
4
+ # This file is part of faildns.
5
+ #
6
+ # faildns is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # faildns is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with faildns. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'faildns/header/type'
21
+ require 'faildns/header/opcode'
22
+ require 'faildns/header/status'
23
+
24
+ module DNS
25
+
26
+ #--
27
+ # The header contains the following fields:
28
+ #
29
+ # 1 1 1 1 1 1
30
+ # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
31
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
32
+ # | ID |
33
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
34
+ # |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
35
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
36
+ # | QDCOUNT |
37
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
38
+ # | ANCOUNT |
39
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
40
+ # | NSCOUNT |
41
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
42
+ # | ARCOUNT |
43
+ # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
44
+ #
45
+ # where:
46
+ #
47
+ # ID A 16 bit identifier assigned by the program that
48
+ # generates any kind of query. This identifier is copied
49
+ # the corresponding reply and can be used by the requester
50
+ # to match up replies to outstanding queries.
51
+ #
52
+ # QR A one bit field that specifies whether this message is a
53
+ # query (0), or a response (1).
54
+ #
55
+ # OPCODE A four bit field that specifies kind of query in this
56
+ # message. This value is set by the originator of a query
57
+ # and copied into the response. The values are:
58
+ #
59
+ # 0 a standard query (QUERY)
60
+ #
61
+ # 1 an inverse query (IQUERY)
62
+ #
63
+ # 2 a server status request (STATUS)
64
+ #
65
+ # 3-15 reserved for future use
66
+ #
67
+ # AA Authoritative Answer - this bit is valid in responses,
68
+ # and specifies that the responding name server is an
69
+ # authority for the domain name in question section.
70
+ #
71
+ # Note that the contents of the answer section may have
72
+ # multiple owner names because of aliases. The AA bit
73
+ # corresponds to the name which matches the query name, or
74
+ # the first owner name in the answer section.
75
+ #
76
+ # TC TrunCation - specifies that this message was truncated
77
+ # due to length greater than that permitted on the
78
+ # transmission channel.
79
+ #
80
+ # RD Recursion Desired - this bit may be set in a query and
81
+ # is copied into the response. If RD is set, it directs
82
+ # the name server to pursue the query recursively.
83
+ # Recursive query support is optional.
84
+ #
85
+ # RA Recursion Available - this be is set or cleared in a
86
+ # response, and denotes whether recursive query support is
87
+ # available in the name server.
88
+ #
89
+ # Z Reserved for future use. Must be zero in all queries
90
+ # and responses.
91
+ #
92
+ # RCODE Response code - this 4 bit field is set as part of
93
+ # responses. The values have the following
94
+ # interpretation:
95
+ #
96
+ # 0 No error condition
97
+ #
98
+ # 1 Format error - The name server was
99
+ # unable to interpret the query.
100
+ #
101
+ # 2 Server failure - The name server was
102
+ # unable to process this query due to a
103
+ # problem with the name server.
104
+ #
105
+ # 3 Name Error - Meaningful only for
106
+ # responses from an authoritative name
107
+ # server, this code signifies that the
108
+ # domain name referenced in the query does
109
+ # not exist.
110
+ #
111
+ # 4 Not Implemented - The name server does
112
+ # not support the requested kind of query.
113
+ #
114
+ # 5 Refused - The name server refuses to
115
+ # perform the specified operation for
116
+ # policy reasons. For example, a name
117
+ # server may not wish to provide the
118
+ # information to the particular requester,
119
+ # or a name server may not wish to perform
120
+ # a particular operation (e.g., zone
121
+ # transfer) for particular data.
122
+ #
123
+ # 6-15 Reserved for future use.
124
+ #
125
+ # QDCOUNT an unsigned 16 bit integer specifying the number of
126
+ # entries in the question section.
127
+ #
128
+ # ANCOUNT an unsigned 16 bit integer specifying the number of
129
+ # resource records in the answer section.
130
+ #
131
+ # NSCOUNT an unsigned 16 bit integer specifying the number of name
132
+ # server resource records in the authority records
133
+ # section.
134
+ #
135
+ # ARCOUNT an unsigned 16 bit integer specifying the number of
136
+ # resource records in the additional records section.
137
+ #++
138
+
139
+ class Header
140
+ @@default = {
141
+ :AA => false, :TC => false, :RD => false, :RA => false, :AD => false, :CD => true,
142
+
143
+ :RCODE => Status.new(:NOERROR),
144
+
145
+ :QDCOUNT => 0,
146
+ :ANCOUNT => 0,
147
+ :NSCOUNT => 0,
148
+ :ARCOUNT => 0
149
+ }
150
+
151
+ def self.parse (string)
152
+ data = string.unpack('nnnnnn')
153
+
154
+ string[0, Header.length] = ''
155
+
156
+ return Header.new(
157
+ :ID => data[0],
158
+
159
+ :QR => Type.new((data[1] & 0x8000) >> 15),
160
+
161
+ :OPCODE => Opcode.new((data[1] & 0x7800) >> 11),
162
+
163
+ :AA => (data[1] & 0x400 != 0),
164
+ :TC => (data[1] & 0x200 != 0),
165
+ :RD => (data[1] & 0x100 != 0),
166
+ :RA => (data[1] & 0x80 != 0),
167
+ :AD => (data[1] & 0x40 != 0),
168
+ :CD => (data[1] & 0x20 != 0),
169
+
170
+ :RCODE => Status.new(data[1] & 0xf),
171
+
172
+ :QDCOUNT => data[2],
173
+
174
+ :ANCOUNT => data[3],
175
+
176
+ :NSCOUNT => data[4],
177
+
178
+ :ARCOUNT => data[5]
179
+ )
180
+ end
181
+
182
+ def self.length (string=nil)
183
+ 12
184
+ end
185
+
186
+ def initialize (what={})
187
+ if !what.is_a? Hash
188
+ raise ArgumentError.new('You have to pass a Hash.')
189
+ end
190
+
191
+ @data = @@default.merge(what)
192
+
193
+ if block_given?
194
+ yield self
195
+ end
196
+ end
197
+
198
+ def id; @data[:ID] end
199
+ def type; @data[:QR] end
200
+ def class; @data[:OPCODE] end
201
+ def authoritative?; @data[:AA] end
202
+ def truncated?; @data[:TC] end
203
+ def recursive?; @data[:RD] end
204
+ def recursivable?; @data[:RA] end
205
+ def authentic?; @data[:AD] end
206
+ def checking?; @data[:CD] end
207
+ def status; @data[:RCODE] end
208
+ def questions; @data[:QDCOUNT] end
209
+ def answers; @data[:ANCOUNT] end
210
+ def authorities; @data[:NSCOUNT] end
211
+ def additionals; @data[:ARCOUNT] end
212
+
213
+ def id= (val); @data[:ID] = val end
214
+ def type= (val); @data[:QR] = Type.new(val) end
215
+ def class= (val); @data[:OPCODE] = Opcode.new(val) end
216
+ def authoritative!; @data[:AA] = true end
217
+ def truncated!; @data[:TC] = true end
218
+ def recursive!; @data[:RD] = true end
219
+ def recursivable!; @data[:RA] = true end
220
+ def authentic!; @data[:AD] = true end
221
+ def checking!; @data[:CD] = true end
222
+ def not_authoritative!; @data[:AA] = false end
223
+ def not_truncated!; @data[:TC] = false end
224
+ def not_recursive!; @data[:RD] = false end
225
+ def not_recursivable!; @data[:RA] = false end
226
+ def not_authentic!; @data[:AD] = false end
227
+ def not_checking!; @data[:CD] = false end
228
+ def status= (val); @data[:RCODE] = Status.new(val) end
229
+ def questions= (val); @data[:QDCOUNT] = val end
230
+ def answers= (val); @data[:ANCOUNT] = val end
231
+ def authorities= (val); @data[:NSCOUNT] = val end
232
+ def additionals= (val); @data[:ARCOUNT] = val end
233
+
234
+ def pack
235
+ [
236
+ self.id,
237
+
238
+ ( (self.type.value << 15) \
239
+ | (self.class.value << 14) \
240
+ | ((self.authoritative?) ? (1 << 10) : 0) \
241
+ | ((self.truncated?) ? (1 << 9) : 0) \
242
+ | ((self.recursive?) ? (1 << 8) : 0) \
243
+ | ((self.recursivable?) ? (1 << 7) : 0) \
244
+ | (self.status.value)),
245
+
246
+ self.questions,
247
+ self.answers,
248
+ self.authorities,
249
+ self.additionals
250
+ ].pack('nnnnnn')
251
+ end
252
+ end
253
+
254
+ end