]> gitweb.factorcode.org Git - factor.git/blob - extra/dns/dns.factor
Remove stream-peek and stream-peek1, re-implement dns vocab to not need this abstraction
[factor.git] / extra / dns / dns.factor
1 ! Copyright (C) 2010 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien.enums alien.syntax arrays calendar
4 combinators combinators.smart constructors destructors grouping
5 io io.binary io.encodings.binary io.encodings.string
6 io.encodings.utf8 io.sockets io.sockets.private
7 io.streams.byte-array io.timeouts kernel make math math.bitwise
8 math.parser namespaces nested-comments random sequences
9 slots.syntax splitting system vectors vocabs.loader ;
10 IN: dns
11
12 : with-temporary-input-seek ( n seek-type quot -- )
13     tell-input [
14         [ seek-input ] dip call
15     ] dip seek-absolute seek-input ; inline
16
17 ENUM: dns-type
18 { A 1 } { NS 2 } { MD 3 } { MF 4 }
19 { CNAME 5 } { SOA 6 } { MB 7 } { MG 8 }
20 { MR 9 } { NULL 10 } { WKS 11 } { PTR 12 }
21 { HINFO 13 } { MINFO 14 } { MX 15 } { TXT 16 }
22 { RP 17 } { AFSDB 18 } { SIG 24 } { KEY 25 }
23 { AAAA 28 } { LOC 29 } { SVR 33 } { NAPTR 35 }
24 { KX 36 } { CERT 37 } { DNAME 39 } { OPT 41 }
25 { APL 42 } { DS 43 } { SSHFP 44 } { IPSECKEY 45 }
26 { RRSIG 46 } { NSEC 47 } { DNSKEY 48 } { DHCID 49 }
27 { NSEC3 50 } { NSEC3PARAM 51 } { HIP 55 } { SPF 99 }
28 { TKEY 249 } { TSIG 250 } { IXFR 251 }
29 { TA 32768 } { DLV 32769 } ;
30
31 ENUM: dns-class { IN 1 } { CS 2 } { CH 3 } { HS 4 } ;
32
33 ENUM: dns-opcode QUERY IQUERY STATUS ;
34
35 ENUM: dns-rcode NO-ERROR FORMAT-ERROR SERVER-FAILURE
36 NAME-ERROR NOT-IMPLEMENTED REFUSED ;
37
38 SYMBOL: dns-servers
39
40 : add-dns-server ( string -- )
41     dns-servers get push ;
42
43 : remove-dns-server ( string -- )
44     dns-servers get remove! drop ;
45
46 : clear-dns-servers ( -- )
47     V{ } clone dns-servers set-global ;
48
49 : >dotted ( domain -- domain' )
50     dup "." tail? [ "." append ] unless ;
51
52 : dotted> ( string -- string' )
53     "." ?tail drop ;
54
55 TUPLE: query name type class ;
56 CONSTRUCTOR: query ( name type class -- obj )
57     [ >dotted ] change-name ;
58
59 TUPLE: rr name type class ttl rdata ;
60
61 TUPLE: hinfo cpu os ;
62
63 TUPLE: mx preference exchange ;
64
65 TUPLE: soa mname rname serial refresh retry expire minimum ;
66
67 TUPLE: a name ;
68 CONSTRUCTOR: a ( name -- obj ) ;
69
70 TUPLE: aaaa name ;
71 CONSTRUCTOR: aaaa ( name -- obj ) ;
72
73 TUPLE: cname name ;
74 CONSTRUCTOR: cname ( name -- obj ) ;
75
76 TUPLE: ptr name ;
77 CONSTRUCTOR: ptr ( name -- obj ) ;
78
79 TUPLE: ns name ;
80 CONSTRUCTOR: ns ( name -- obj ) ;
81
82 TUPLE: message id qr opcode aa tc rd ra z rcode
83 query answer-section authority-section additional-section ;
84
85 CONSTRUCTOR: message ( query -- obj )
86     16 2^ random >>id
87     0 >>qr
88     QUERY >>opcode
89     0 >>aa
90     0 >>tc
91     1 >>rd
92     0 >>ra
93     0 >>z
94     NO-ERROR >>rcode
95     [ dup sequence? [ 1array ] unless ] change-query
96     { } >>answer-section
97     { } >>authority-section
98     { } >>additional-section ;
99
100 : message>header ( message -- n )
101     [
102         {
103             [ qr>> 15 shift ]
104             [ opcode>> enum>number 11 shift ]
105             [ aa>> 10 shift ]
106             [ tc>> 9 shift ]
107             [ rd>> 8 shift ]
108             [ ra>> 7 shift ]
109             [ z>> 4 shift ]
110             [ rcode>> enum>number 0 shift ]
111         } cleave
112     ] sum-outputs ;
113
114 : header>message-parts ( n -- qr opcode aa tc rd ra z rcode )
115     {
116         [ -15 shift BIN: 1 bitand ]
117         [ -11 shift BIN: 111 bitand <dns-opcode> ]
118         [ -10 shift BIN: 1 bitand ]
119         [ -9 shift BIN: 1 bitand ]
120         [ -8 shift BIN: 1 bitand ]
121         [ -7 shift BIN: 1 bitand ]
122         [ -4 shift BIN: 111 bitand ]
123         [ BIN: 1111 bitand <dns-rcode> ]
124     } cleave ;
125
126 : byte-array>ipv4 ( byte-array -- string )
127     [ number>string ] { } map-as "." join ;
128
129 : byte-array>ipv6 ( byte-array -- string )
130     2 group [ be> >hex ] { } map-as ":" join ;
131
132 : ipv4>byte-array ( string -- byte-array )
133     "." split [ string>number ] B{ } map-as ;
134
135 : ipv6>byte-array ( string -- byte-array )
136     T{ inet6 } inet-pton ;
137
138 : expand-ipv6 ( ipv6 -- ipv6' ) ipv6>byte-array byte-array>ipv6 ;
139
140 : reverse-ipv4 ( string -- string )
141     ipv4>byte-array reverse byte-array>ipv4 ;
142
143 CONSTANT: ipv4-arpa-suffix ".in-addr.arpa"
144
145 : ipv4>arpa ( string -- string )
146     reverse-ipv4 ipv4-arpa-suffix append ;
147
148 CONSTANT: ipv6-arpa-suffix ".ip6.arpa"
149
150 : ipv6>arpa ( string -- string )
151     ipv6>byte-array
152     [ [ -4 shift 4 bits ] [ 4 bits ] bi 2array ] { } map-as
153     B{ } concat-as reverse
154     [ >hex ] { } map-as "." join ipv6-arpa-suffix append ;
155
156 : trim-ipv4-arpa ( string -- string' )
157     dotted> ipv4-arpa-suffix ?tail drop ;
158
159 : trim-ipv6-arpa ( string -- string' )
160     dotted> ipv6-arpa-suffix ?tail drop ;
161  
162 : arpa>ipv4 ( string -- ip ) trim-ipv4-arpa reverse-ipv4 ;
163
164 : arpa>ipv6 ( string -- ip )
165     trim-ipv6-arpa "." split 2 group reverse
166     [
167         first2 swap [ hex> ] bi@ [ 4 shift ] [ ] bi* bitor
168     ] B{ } map-as byte-array>ipv6 ;
169
170 : parse-length-bytes ( byte -- sequence ) read utf8 decode ;
171
172 : (parse-name) ( -- )
173     read1 [
174         dup HEX: C0 mask? [
175             8 shift read1 bitor HEX: 3fff bitand
176             seek-absolute [
177                 read1 parse-length-bytes , (parse-name)
178             ] with-temporary-input-seek
179         ] [
180             parse-length-bytes , (parse-name)
181         ] if
182     ] unless-zero ;
183
184 : parse-name ( -- sequence )
185     [ (parse-name) ] { } make "." join ;
186
187 : parse-query ( -- query )
188     parse-name
189     2 read be> <dns-type>
190     2 read be> <dns-class> <query> ;
191
192 : parse-soa ( -- soa )
193     soa new
194         parse-name >>mname
195         parse-name >>rname
196         4 read be> >>serial
197         4 read be> >>refresh
198         4 read be> >>retry
199         4 read be> >>expire
200         4 read be> >>minimum ;
201
202 : parse-mx ( -- mx )
203     mx new
204         2 read be> >>preference
205         parse-name >>exchange ;
206
207 GENERIC: parse-rdata ( n type -- obj )
208
209 M: object parse-rdata drop read ;
210 M: A parse-rdata 2drop 4 read byte-array>ipv4 <a> ;
211 M: AAAA parse-rdata 2drop 16 read byte-array>ipv6 <aaaa> ;
212 M: CNAME parse-rdata 2drop parse-name <cname> ;
213 M: MX parse-rdata 2drop parse-mx ;
214 M: NS parse-rdata 2drop parse-name <ns> ;
215 M: PTR parse-rdata 2drop parse-name <ptr> ;
216 M: SOA parse-rdata 2drop parse-soa ;
217
218 : parse-rr ( -- rr )
219     rr new
220         parse-name >>name
221         2 read be> <dns-type> >>type
222         2 read be> <dns-class> >>class
223         4 read be> >>ttl
224         2 read be> over type>> parse-rdata >>rdata ;
225
226 : parse-message ( byte-array -- message )
227     [ message new ] dip
228     binary [
229         2 read be> >>id
230         2 read be> header>message-parts set-slots[ qr opcode aa tc rd ra z rcode ]
231         2 read be> >>query
232         2 read be> >>answer-section
233         2 read be> >>authority-section
234         2 read be> >>additional-section
235         [ [ parse-query ] replicate ] change-query
236         [ [ parse-rr ] replicate ] change-answer-section
237         [ [ parse-rr ] replicate ] change-authority-section
238         [ [ parse-rr ] replicate ] change-additional-section
239     ] with-byte-reader ;
240
241 : >n/label ( string -- byte-array )
242     [ length 1array ] [ utf8 encode ] bi B{ } append-as ;
243
244 : >name ( domain -- byte-array ) "." split [ >n/label ] map concat ;
245
246 : query>byte-array ( query -- byte-array )
247     [
248         {
249             [ name>> >name ]
250             [ type>> enum>number 2 >be ]
251             [ class>> enum>number 2 >be ]
252         } cleave
253     ] B{ } append-outputs-as ;
254
255 GENERIC: rdata>byte-array ( rdata type -- obj )
256
257 M: A rdata>byte-array drop ipv4>byte-array ;
258
259 M: CNAME rdata>byte-array drop >name ;
260
261 M: HINFO rdata>byte-array
262     drop
263     [ cpu>> >name ]
264     [ os>> >name ] bi append ;
265
266 M: MX rdata>byte-array
267     drop 
268     [ preference>> 2 >be ]
269     [ exchange>> >name ] bi append ;
270
271 M: NS rdata>byte-array drop >name ;
272
273 M: PTR rdata>byte-array drop >name ;
274
275 M: SOA rdata>byte-array
276     drop
277     [
278         {
279             [ mname>> >name ]
280             [ rname>> >name ]
281             [ serial>> 4 >be ]
282             [ refresh>> 4 >be ]
283             [ retry>> 4 >be ]
284             [ expire>> 4 >be ]
285             [ minimum>> 4 >be ]
286         } cleave
287     ] B{ } append-outputs-as ;
288
289 : rr>byte-array ( rr -- byte-array )
290     [
291         {
292             [ name>> >name ]
293             [ type>> enum>number 2 >be ]
294             [ class>> enum>number 2 >be ]
295             [ ttl>> 4 >be ]
296             [
297                 [ rdata>> ] [ type>> ] bi rdata>byte-array
298                 [ length 2 >be ] [ ] bi append
299             ]
300         } cleave
301     ] B{ } append-outputs-as ;
302
303 : message>byte-array ( message -- byte-array )
304     [
305         {
306             [ id>> 2 >be ]
307             [ message>header 2 >be ]
308             [ query>> length 2 >be ]
309             [ answer-section>> length 2 >be ]
310             [ authority-section>> length 2 >be ]
311             [ additional-section>> length 2 >be ]
312             [ query>> [ query>byte-array ] map concat ]
313             [ answer-section>> [ rr>byte-array ] map concat ]
314             [ authority-section>> [ rr>byte-array ] map concat ]
315             [ additional-section>> [ rr>byte-array ] map concat ]
316         } cleave
317     ] B{ } append-outputs-as ;
318
319 : udp-query ( bytes server -- bytes' )
320     f 0 <inet4> <datagram>
321     30 seconds over set-timeout [
322         [ send ] [ receive drop ] bi
323     ] with-disposal ;
324
325 : <dns-inet4> ( -- inet4 )
326     dns-servers get random 53 <inet4> ;
327
328 : dns-query ( query -- message )
329     <message> message>byte-array
330     <dns-inet4> udp-query parse-message ;
331
332 : dns-A-query ( domain -- message ) A IN <query> dns-query ;
333 : dns-AAAA-query ( domain -- message ) AAAA IN <query> dns-query ;
334 : dns-MX-query ( domain -- message ) MX IN <query> dns-query ;
335 : dns-NS-query ( domain -- message ) NS IN <query> dns-query ;
336
337 : reverse-lookup ( reversed-ip -- message )
338     PTR IN <query> dns-query ;
339
340 : reverse-ipv4-lookup ( ip -- message )
341     ipv4>arpa reverse-lookup ;
342
343 : reverse-ipv6-lookup ( ip -- message )
344     ipv6>arpa reverse-lookup ;
345
346 : message>names ( message -- names )
347     answer-section>> [ rdata>> name>> ] map ;
348
349 : message>a-names ( message -- names )
350     answer-section>>
351     [ rdata>> ] map [ a? ] filter [ name>> ] map ;
352
353 : message>mxs ( message -- assoc )
354     answer-section>> [ rdata>> [ preference>> ] [ exchange>> ] bi 2array ] map ;
355
356 : messages>names ( messages -- names ) 
357     [ message>names ] map concat ;
358
359 : forward-confirmed-reverse-dns-ipv4? ( ipv4-string -- ? )
360     dup reverse-ipv4-lookup message>names
361     [ dns-A-query ] map messages>names member? ;
362
363 : forward-confirmed-reverse-dns-ipv6? ( ipv6-string -- ? )
364     expand-ipv6
365     dup reverse-ipv6-lookup message>names
366     [ dns-AAAA-query ] map messages>names member? ;
367
368 : message>query-name ( message -- string )
369     query>> first name>> dotted> ;
370
371 USE: nested-comments
372 (*
373 M: string resolve-host
374     dup >lower "localhost" = [
375         drop resolve-localhost
376     ] [
377         dns-A-query message>a-names [ <ipv4> ] map
378     ] if ;
379 *)
380     
381 HOOK: initial-dns-servers os ( -- sequence )
382
383 {
384     { [ os windows? ] [ "dns.windows" ] }
385     { [ os unix? ] [ "dns.unix" ] }
386 } cond require
387     
388 dns-servers [ initial-dns-servers >vector ] initialize