22
22
% % Dns record encode/decode
23
23
% %
24
24
% % RFC 1035: Domain Names - Implementation and Specification
25
+ % % RFC 1995: Incremental Zone Transfer in DNS
26
+ % % RFC 1996: A Mechanism for Prompt Notification of Zone Changes (DNS NOTIFY)
27
+ % % RFC 2136: Dynamic Updates in the Domain Name System (DNS UPDATE)
25
28
% % RFC 2181: Clarifications to the DNS Specification
26
- % % RFC 6891: Extension Mechanisms for DNS (EDNS0)
27
29
% % RFC 2782: A DNS RR for specifying the location of services (DNS SRV)
28
30
% % RFC 2915: The Naming Authority Pointer (NAPTR) DNS Resource Rec
31
+ % % RFC 5936: DNS Zone Transfer Protocol (AXFR)
29
32
% % RFC 6488: DNS Certification Authority Authorization (CAA) Resource Record
30
- % % RFC 7553: The Uniform Resource Identifier (URI) DNS Resource Record
31
33
% % RFC 6762: Multicast DNS
34
+ % % RFC 6891: Extension Mechanisms for DNS (EDNS0)
35
+ % % RFC 7553: The Uniform Resource Identifier (URI) DNS Resource Record
36
+ % % RFC 8945: Secret Key Transaction Authentication for DNS (TSIG)
32
37
33
38
-export ([decode /1 , encode /1 ]).
39
+ -export ([decode_algname /1 , encode_algname /1 ]).
34
40
35
41
-import (lists , [reverse /1 ]).
36
42
@@ -157,9 +163,9 @@ do_decode(<<Id:16,
157
163
QdCount :16 ,AnCount :16 ,NsCount :16 ,ArCount :16 ,
158
164
QdBuf /binary >>= Buffer ) ->
159
165
{AnBuf ,QdList ,QdTC } = decode_query_section (QdBuf ,QdCount ,Buffer ),
160
- {NsBuf ,AnList ,AnTC } = decode_rr_section (AnBuf ,AnCount ,Buffer ),
161
- {ArBuf ,NsList ,NsTC } = decode_rr_section (NsBuf ,NsCount ,Buffer ),
162
- {Rest ,ArList ,ArTC } = decode_rr_section (ArBuf ,ArCount ,Buffer ),
166
+ {NsBuf ,AnList ,AnTC } = decode_rr_section (Opcode , AnBuf ,AnCount ,Buffer ),
167
+ {ArBuf ,NsList ,NsTC } = decode_rr_section (Opcode , NsBuf ,NsCount ,Buffer ),
168
+ {Rest ,ArList ,ArTC } = decode_rr_section (Opcode , ArBuf ,ArCount ,Buffer ),
163
169
? MATCH_ELSE_DECODE_ERROR (
164
170
Rest ,
165
171
<<>>,
@@ -217,14 +223,14 @@ decode_query_section(Bin, N, Buffer, Qs) ->
217
223
decode_query_section (Rest , N - 1 , Buffer , [DnsQuery |Qs ])
218
224
end ).
219
225
220
- decode_rr_section (Bin , N , Buffer ) ->
221
- decode_rr_section (Bin , N , Buffer , []).
226
+ decode_rr_section (Opcode , Bin , N , Buffer ) ->
227
+ decode_rr_section (Opcode , Bin , N , Buffer , []).
222
228
223
- decode_rr_section (<<>>= Rest , N , _Buffer , RRs ) ->
229
+ decode_rr_section (_Opcode , <<>>= Rest , N , _Buffer , RRs ) ->
224
230
{Rest ,reverse (RRs ),N =/= 0 };
225
- decode_rr_section (Rest , 0 , _Buffer , RRs ) ->
231
+ decode_rr_section (_Opcode , Rest , 0 , _Buffer , RRs ) ->
226
232
{Rest ,reverse (RRs ),false };
227
- decode_rr_section (Bin , N , Buffer , RRs ) ->
233
+ decode_rr_section (Opcode , Bin , N , Buffer , RRs ) ->
228
234
? MATCH_ELSE_DECODE_ERROR (
229
235
decode_name (Bin , Buffer ),
230
236
{<<T :16 /unsigned ,C :16 /unsigned ,TTL :4 /binary ,
@@ -235,6 +241,9 @@ decode_rr_section(Bin, N, Buffer, RRs) ->
235
241
RR =
236
242
case Type of
237
243
? S_OPT ->
244
+ % % RFC 6891: 6.1.1. FORMERR if more than one dns_rr_opt
245
+ lists :keymember (dns_rr_opt , 1 , RRs ) andalso
246
+ throw (? DECODE_ERROR ),
238
247
<<ExtRcode ,Version ,DO :1 ,Z :15 >> = TTL ,
239
248
DnssecOk = (DO =/= 0 ),
240
249
# dns_rr_opt {
@@ -246,9 +255,37 @@ decode_rr_section(Bin, N, Buffer, RRs) ->
246
255
z = Z ,
247
256
data = D ,
248
257
do = DnssecOk };
258
+ ? S_TSIG ->
259
+ % % RFC 8945: 5.2. FORMERR if not last
260
+ % % RFC 8945: 5.2. FORMERR if more than one dns_rr_tsig
261
+ % % (...covered by being last)
262
+ Rest =/= <<>> andalso throw (? DECODE_ERROR ),
263
+ {DR ,AlgName } = decode_name (D , Buffer ),
264
+ ? MATCH_ELSE_DECODE_ERROR (
265
+ DR ,
266
+ <<Now :48 , Fudge :16 , MACSize :16 , MAC :MACSize /binary ,
267
+ OriginalId :16 , Error :16 ,
268
+ OtherLen :16 , OtherData :OtherLen /binary >>,
269
+ # dns_rr_tsig {
270
+ domain = Name ,
271
+ type = Type ,
272
+ offset = byte_size (Buffer ) - byte_size (Bin ),
273
+ algname = AlgName ,
274
+ now = Now ,
275
+ fudge = Fudge ,
276
+ mac = MAC ,
277
+ original_id = OriginalId ,
278
+ error = Error ,
279
+ other_data = OtherData });
249
280
_ ->
250
281
{Class ,CacheFlush } = decode_class (C ),
251
- Data = decode_data (D , Class , Type , Buffer ),
282
+ Data = if
283
+ % % RFC 2136: 2.4. Allow length zero data for UPDATE
284
+ Opcode == ? UPDATE , D == <<>> ->
285
+ # dns_rr {}# dns_rr .data ;
286
+ true ->
287
+ decode_data (D , Class , Type , Buffer )
288
+ end ,
252
289
<<TimeToLive :32 /signed >> = TTL ,
253
290
# dns_rr {
254
291
domain = Name ,
@@ -258,7 +295,7 @@ decode_rr_section(Bin, N, Buffer, RRs) ->
258
295
data = Data ,
259
296
func = CacheFlush }
260
297
end ,
261
- decode_rr_section (Rest , N - 1 , Buffer , [RR |RRs ])
298
+ decode_rr_section (Opcode , Rest , N - 1 , Buffer , [RR |RRs ])
262
299
end ).
263
300
264
301
% %
@@ -270,12 +307,13 @@ encode(Q) ->
270
307
AnCount = length (Q # dns_rec .anlist ),
271
308
NsCount = length (Q # dns_rec .nslist ),
272
309
ArCount = length (Q # dns_rec .arlist ),
310
+ OC = Q # dns_rec .header # dns_header .opcode ,
273
311
B0 = encode_header (Q # dns_rec .header , QdCount , AnCount , NsCount , ArCount ),
274
312
C0 = gb_trees :empty (),
275
313
{B1 ,C1 } = encode_query_section (B0 , C0 , Q # dns_rec .qdlist ),
276
- {B2 ,C2 } = encode_res_section (B1 , C1 , Q # dns_rec .anlist ),
277
- {B3 ,C3 } = encode_res_section (B2 , C2 , Q # dns_rec .nslist ),
278
- {B ,_ } = encode_res_section (B3 , C3 , Q # dns_rec .arlist ),
314
+ {B2 ,C2 } = encode_res_section (OC , B1 , C1 , Q # dns_rec .anlist ),
315
+ {B3 ,C3 } = encode_res_section (OC , B2 , C2 , Q # dns_rec .nslist ),
316
+ {B ,_ } = encode_res_section (OC , B3 , C3 , Q # dns_rec .arlist ),
279
317
B .
280
318
281
319
@@ -307,9 +345,9 @@ encode_query_section(Bin0, Comp0, [#dns_query{domain=DName}=Q | Qs]) ->
307
345
% % RFC 1035: 4.1.3. Resource record format
308
346
% % RFC 6891: 6.1.2, 6.1.3, 6.2.3 Opt RR format
309
347
% %
310
- encode_res_section (Bin , Comp , []) -> {Bin ,Comp };
348
+ encode_res_section (_Opcode , Bin , Comp , []) -> {Bin ,Comp };
311
349
encode_res_section (
312
- Bin , Comp ,
350
+ Opcode , Bin , Comp ,
313
351
[# dns_rr {
314
352
domain = DName ,
315
353
type = Type ,
@@ -318,10 +356,10 @@ encode_res_section(
318
356
ttl = TTL ,
319
357
data = Data } | Rs ]) ->
320
358
encode_res_section_rr (
321
- Bin , Comp , Rs , DName , Type , Class , CacheFlush ,
359
+ Opcode , Bin , Comp , Rs , DName , Type , Class , CacheFlush ,
322
360
<<TTL :32 /signed >>, Data );
323
361
encode_res_section (
324
- Bin , Comp ,
362
+ Opcode , Bin , Comp ,
325
363
[# dns_rr_opt {
326
364
domain = DName ,
327
365
udp_payload_size = UdpPayloadSize ,
@@ -332,18 +370,39 @@ encode_res_section(
332
370
do = DnssecOk } | Rs ]) ->
333
371
DO = case DnssecOk of true -> 1 ; false -> 0 end ,
334
372
encode_res_section_rr (
335
- Bin , Comp , Rs , DName , ? S_OPT , UdpPayloadSize , false ,
336
- <<ExtRCode ,Version ,DO :1 ,Z :15 >>, Data ).
373
+ Opcode , Bin , Comp , Rs , DName , ? S_OPT , UdpPayloadSize , false ,
374
+ <<ExtRCode ,Version ,DO :1 ,Z :15 >>, Data );
375
+ encode_res_section (
376
+ Opcode , Bin , Comp ,
377
+ [# dns_rr_tsig {
378
+ domain = DName ,
379
+ algname = AlgName ,
380
+ now = Now ,
381
+ fudge = Fudge ,
382
+ mac = MAC ,
383
+ original_id = OriginalId ,
384
+ error = Error ,
385
+ other_data = OtherData }]) ->
386
+ Data = {AlgName ,Now ,Fudge ,MAC ,OriginalId ,Error ,OtherData },
387
+ encode_res_section_rr (
388
+ Opcode , Bin , Comp , [], DName , ? S_TSIG , ? S_ANY , false ,
389
+ <<0 :32 /signed >>, Data ).
337
390
338
391
encode_res_section_rr (
339
- Bin0 , Comp0 , Rs , DName , Type , Class , CacheFlush , TTL , Data ) ->
392
+ Opcode , Bin0 , Comp0 , Rs , DName , Type , Class , CacheFlush , TTL , Data ) ->
340
393
T = encode_type (Type ),
341
394
C = encode_class (Class , CacheFlush ),
342
395
{Bin ,Comp1 } = encode_name (Bin0 , Comp0 , byte_size (Bin0 ), DName ),
343
396
Pos = byte_size (Bin )+ 2 + 2 + byte_size (TTL )+ 2 ,
344
- {DataBin ,Comp } = encode_data (Comp1 , Pos , Type , Class , Data ),
397
+ {DataBin ,Comp } = if
398
+ Opcode == update , Data == # dns_rr {}# dns_rr .data ->
399
+ {<<>>,Comp1 };
400
+ true ->
401
+ encode_data (Comp1 , Pos , Type , Class , Data )
402
+ end ,
345
403
DataSize = byte_size (DataBin ),
346
404
encode_res_section (
405
+ Opcode ,
347
406
<<Bin /binary ,T :16 ,C :16 ,TTL /binary ,DataSize :16 ,DataBin /binary >>,
348
407
Comp , Rs ).
349
408
@@ -379,7 +438,8 @@ decode_type(Type) ->
379
438
? T_UID -> ? S_UID ;
380
439
? T_GID -> ? S_GID ;
381
440
? T_UNSPEC -> ? S_UNSPEC ;
382
- % % Query type values which do not appear in resource records
441
+ ? T_TSIG -> ? S_TSIG ;
442
+ ? T_IXFR -> ? S_IXFR ;
383
443
? T_AXFR -> ? S_AXFR ;
384
444
? T_MAILB -> ? S_MAILB ;
385
445
? T_MAILA -> ? S_MAILA ;
@@ -421,7 +481,8 @@ encode_type(Type) ->
421
481
? S_UID -> ? T_UID ;
422
482
? S_GID -> ? T_GID ;
423
483
? S_UNSPEC -> ? T_UNSPEC ;
424
- % % Query type values which do not appear in resource records
484
+ ? S_TSIG -> ? T_TSIG ;
485
+ ? S_IXFR -> ? T_IXFR ;
425
486
? S_AXFR -> ? T_AXFR ;
426
487
? S_MAILB -> ? T_MAILB ;
427
488
? S_MAILA -> ? T_MAILA ;
@@ -444,6 +505,7 @@ decode_class(C0) ->
444
505
? C_IN -> in ;
445
506
? C_CHAOS -> chaos ;
446
507
? C_HS -> hs ;
508
+ ? C_NONE -> none ;
447
509
? C_ANY -> any ;
448
510
_ -> C % % raw unknown class
449
511
end ,
@@ -463,6 +525,7 @@ encode_class(Class) ->
463
525
in -> ? C_IN ;
464
526
chaos -> ? C_CHAOS ;
465
527
hs -> ? C_HS ;
528
+ none -> ? C_NONE ;
466
529
any -> ? C_ANY ;
467
530
Class when is_integer (Class ) -> Class % % raw unknown class
468
531
end .
@@ -472,6 +535,8 @@ decode_opcode(Opcode) ->
472
535
? QUERY -> 'query' ;
473
536
? IQUERY -> iquery ;
474
537
? STATUS -> status ;
538
+ ? NOTIFY -> notify ;
539
+ ? UPDATE -> update ;
475
540
_ when is_integer (Opcode ) -> Opcode % % non-standard opcode
476
541
end .
477
542
@@ -480,9 +545,11 @@ encode_opcode(Opcode) ->
480
545
'query' -> ? QUERY ;
481
546
iquery -> ? IQUERY ;
482
547
status -> ? STATUS ;
548
+ notify -> ? NOTIFY ;
549
+ update -> ? UPDATE ;
483
550
_ when is_integer (Opcode ) -> Opcode % % non-standard opcode
484
551
end .
485
-
552
+
486
553
487
554
encode_boolean (true ) -> 1 ;
488
555
encode_boolean (false ) -> 0 ;
@@ -707,17 +774,6 @@ decode_name_label(Label, Name, N) ->
707
774
% %
708
775
% % Data field -> {binary(),NewCompressionTable}
709
776
% %
710
- % % Class IN RRs
711
- encode_data (Comp , _ , ? S_A , in , Addr ) ->
712
- {A ,B ,C ,D } = Addr ,
713
- {<<A ,B ,C ,D >>,Comp };
714
- encode_data (Comp , _ , ? S_AAAA , in , Addr ) ->
715
- {A ,B ,C ,D ,E ,F ,G ,H } = Addr ,
716
- {<<A :16 ,B :16 ,C :16 ,D :16 ,E :16 ,F :16 ,G :16 ,H :16 >>,Comp };
717
- encode_data (Comp , _ , ? S_WKS , in , Data ) ->
718
- {{A ,B ,C ,D },Proto ,BitMap } = Data ,
719
- BitMapBin = iolist_to_binary (BitMap ),
720
- {<<A ,B ,C ,D ,Proto ,BitMapBin /binary >>,Comp };
721
777
% % OPT pseudo-RR (of no class) - should not take this way;
722
778
% % this must be a #dns_rr{type = ?S_OPT} instead of a #dns_rr_opt{},
723
779
% % so good luck getting in particular Class and TTL right...
@@ -734,6 +790,16 @@ encode_data(Comp, Pos, Type, Class, Data) ->
734
790
% %
735
791
% %
736
792
% % Standard RRs (any class)
793
+ encode_data (Comp , _ , ? S_A , Addr ) ->
794
+ {A ,B ,C ,D } = Addr ,
795
+ {<<A ,B ,C ,D >>,Comp };
796
+ encode_data (Comp , _ , ? S_AAAA , Addr ) ->
797
+ {A ,B ,C ,D ,E ,F ,G ,H } = Addr ,
798
+ {<<A :16 ,B :16 ,C :16 ,D :16 ,E :16 ,F :16 ,G :16 ,H :16 >>,Comp };
799
+ encode_data (Comp , _ , ? S_WKS , Data ) ->
800
+ {{A ,B ,C ,D },Proto ,BitMap } = Data ,
801
+ BitMapBin = iolist_to_binary (BitMap ),
802
+ {<<A ,B ,C ,D ,Proto ,BitMapBin /binary >>,Comp };
737
803
encode_data (Comp , Pos , ? S_SOA , Data ) ->
738
804
{MName ,RName ,Serial ,Refresh ,Retry ,Expiry ,Minimum } = Data ,
739
805
{B1 ,Comp1 } = encode_name (Comp , Pos , MName ),
@@ -808,6 +874,17 @@ encode_data(Comp, _, ?S_CAA, Data)->
808
874
_ ->
809
875
{encode_txt (Data ),Comp }
810
876
end ;
877
+ encode_data (Comp , _ , ? S_TSIG , Data )->
878
+ {AlgName ,Now ,Fudge ,MAC ,OriginalId ,Error ,OtherData } = Data ,
879
+ % % Bypass name compression (RFC 8945, section 4.2)
880
+ {AlgNameEncoded ,_ } = encode_name (gb_trees :empty (), 0 , AlgName ),
881
+ MACSize = byte_size (MAC ),
882
+ OtherLen = byte_size (OtherData ),
883
+ DataB = <<AlgNameEncoded /binary ,
884
+ Now :48 , Fudge :16 , MACSize :16 , MAC :MACSize /binary ,
885
+ OriginalId :16 , Error :16 ,
886
+ OtherLen :16 , OtherData :OtherLen /binary >>,
887
+ {DataB ,Comp };
811
888
% %
812
889
% % sofar unknown or non standard
813
890
encode_data (Comp , _Pos , Type , Data ) when is_integer (Type ) ->
@@ -947,3 +1024,35 @@ encode_loc_size(X)
947
1024
Multiplier = round (math :pow (10 , Exponent )),
948
1025
Base = (X + Multiplier - 1 ) div Multiplier ,
949
1026
<<Base :4 , Exponent :4 >>.
1027
+
1028
+ decode_algname (AlgName ) ->
1029
+ case AlgName of
1030
+ ? T_TSIG_HMAC_MD5 -> ? S_TSIG_HMAC_MD5 ;
1031
+ ? T_TSIG_GSS_TSIG -> ? S_TSIG_GSS_TSIG ;
1032
+ ? T_TSIG_HMAC_SHA1 -> ? S_TSIG_HMAC_SHA1 ;
1033
+ ? T_TSIG_HMAC_SHA1_96 -> ? S_TSIG_HMAC_SHA1_96 ;
1034
+ ? T_TSIG_HMAC_SHA224 -> ? S_TSIG_HMAC_SHA224 ;
1035
+ ? T_TSIG_HMAC_SHA256 -> ? S_TSIG_HMAC_SHA256 ;
1036
+ ? T_TSIG_HMAC_SHA256_128 -> ? S_TSIG_HMAC_SHA256_128 ;
1037
+ ? T_TSIG_HMAC_SHA384 -> ? S_TSIG_HMAC_SHA384 ;
1038
+ ? T_TSIG_HMAC_SHA384_192 -> ? S_TSIG_HMAC_SHA384_192 ;
1039
+ ? T_TSIG_HMAC_SHA512 -> ? S_TSIG_HMAC_SHA512 ;
1040
+ ? T_TSIG_HMAC_SHA512_256 -> ? S_TSIG_HMAC_SHA512_256 ;
1041
+ _ -> AlgName % raw unknown algname
1042
+ end .
1043
+
1044
+ encode_algname (Alg ) ->
1045
+ case Alg of
1046
+ ? S_TSIG_HMAC_MD5 -> ? T_TSIG_HMAC_MD5 ;
1047
+ ? S_TSIG_GSS_TSIG -> ? T_TSIG_GSS_TSIG ;
1048
+ ? S_TSIG_HMAC_SHA1 -> ? T_TSIG_HMAC_SHA1 ;
1049
+ ? S_TSIG_HMAC_SHA1_96 -> ? T_TSIG_HMAC_SHA1_96 ;
1050
+ ? S_TSIG_HMAC_SHA224 -> ? T_TSIG_HMAC_SHA224 ;
1051
+ ? S_TSIG_HMAC_SHA256 -> ? T_TSIG_HMAC_SHA256 ;
1052
+ ? S_TSIG_HMAC_SHA256_128 -> ? T_TSIG_HMAC_SHA256_128 ;
1053
+ ? S_TSIG_HMAC_SHA384 -> ? T_TSIG_HMAC_SHA384 ;
1054
+ ? S_TSIG_HMAC_SHA384_192 -> ? T_TSIG_HMAC_SHA384_192 ;
1055
+ ? S_TSIG_HMAC_SHA512 -> ? T_TSIG_HMAC_SHA512 ;
1056
+ ? S_TSIG_HMAC_SHA512_256 -> ? T_TSIG_HMAC_SHA512_256 ;
1057
+ Alg when is_list (Alg ) -> Alg % raw unknown algname
1058
+ end .
0 commit comments