35
35
% % RFC 7553: The Uniform Resource Identifier (URI) DNS Resource Record
36
36
% % RFC 8945: Secret Key Transaction Authentication for DNS (TSIG)
37
37
38
- -export ([decode /1 , encode /1 ]).
38
+ -export ([decode /1 , decode / 2 , encode /1 , encode / 2 ]).
39
39
-export ([decode_algname /1 , encode_algname /1 ]).
40
40
41
41
-import (lists , [reverse /1 ]).
@@ -148,8 +148,10 @@ lists_member(H, [_|T]) -> lists_member(H, T).
148
148
throw (? DECODE_ERROR )
149
149
end ).
150
150
151
- decode (Buffer ) when is_binary (Buffer ) ->
152
- try do_decode (Buffer ) of
151
+ decode (Buffer ) -> decode (Buffer , true ). % Backwards compatible
152
+ % %
153
+ decode (Buffer , Mdns ) when is_binary (Buffer ), is_boolean (Mdns ) ->
154
+ try do_decode (Buffer , Mdns ) of
153
155
DnsRec ->
154
156
{ok ,DnsRec }
155
157
catch
@@ -161,11 +163,14 @@ do_decode(<<Id:16,
161
163
QR :1 ,Opcode :4 ,AA :1 ,TC :1 ,RD :1 ,
162
164
RA :1 ,PR :1 ,_ :2 ,Rcode :4 ,
163
165
QdCount :16 ,AnCount :16 ,NsCount :16 ,ArCount :16 ,
164
- QdBuf /binary >>= Buffer ) ->
165
- {AnBuf ,QdList ,QdTC } = decode_query_section (QdBuf ,QdCount ,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 ),
166
+ QdBuf /binary >>= Buffer , Mdns ) ->
167
+ {AnBuf ,QdList ,QdTC } = decode_query_section (QdBuf ,QdCount ,Buffer ,Mdns ),
168
+ {NsBuf ,AnList ,AnTC } =
169
+ decode_rr_section (AnBuf ,AnCount ,Buffer ,{Opcode ,Mdns }),
170
+ {ArBuf ,NsList ,NsTC } =
171
+ decode_rr_section (NsBuf ,NsCount ,Buffer ,{Opcode ,Mdns }),
172
+ {Rest ,ArList ,ArTC } =
173
+ decode_rr_section (ArBuf ,ArCount ,Buffer ,{Opcode ,Mdns }),
169
174
? MATCH_ELSE_DECODE_ERROR (
170
175
Rest ,
171
176
<<>>,
@@ -197,40 +202,40 @@ do_decode(<<Id:16,
197
202
arlist = ArList }
198
203
end )
199
204
end );
200
- do_decode (_ ) ->
205
+ do_decode (_ , _ ) ->
201
206
% % DNS message does not even match header
202
207
throw (? DECODE_ERROR ).
203
208
204
- decode_query_section (Bin , N , Buffer ) ->
205
- decode_query_section (Bin , N , Buffer , []).
209
+ decode_query_section (Bin , N , Buffer , Mdns ) ->
210
+ decode_query_section (Bin , N , Buffer , Mdns , []).
206
211
207
- decode_query_section (<<>>= Rest , N , _Buffer , Qs ) ->
212
+ decode_query_section (<<>>= Rest , N , _Buffer , _Mdns , Qs ) ->
208
213
{Rest ,reverse (Qs ),N =/= 0 };
209
- decode_query_section (Rest , 0 , _Buffer , Qs ) ->
214
+ decode_query_section (Rest , 0 , _Buffer , _Mdns , Qs ) ->
210
215
{Rest ,reverse (Qs ),false };
211
- decode_query_section (Bin , N , Buffer , Qs ) ->
216
+ decode_query_section (Bin , N , Buffer , Mdns , Qs ) ->
212
217
? MATCH_ELSE_DECODE_ERROR (
213
218
decode_name (Bin , Buffer ),
214
219
{<<T :16 ,C :16 ,Rest /binary >>,Name },
215
220
begin
216
- {Class ,UnicastResponse } = decode_class (C ),
221
+ {Class ,UnicastResponse } = decode_class (C , Mdns ),
217
222
DnsQuery =
218
223
# dns_query {
219
224
domain = Name ,
220
225
type = decode_type (T ),
221
226
class = Class ,
222
227
unicast_response = UnicastResponse },
223
- decode_query_section (Rest , N - 1 , Buffer , [DnsQuery |Qs ])
228
+ decode_query_section (Rest , N - 1 , Buffer , Mdns , [DnsQuery |Qs ])
224
229
end ).
225
230
226
- decode_rr_section (Opcode , Bin , N , Buffer ) ->
227
- decode_rr_section (Opcode , Bin , N , Buffer , []).
228
-
229
- decode_rr_section (_Opcode , <<>>= Rest , N , _Buffer , RRs ) ->
231
+ decode_rr_section (Bin , N , Buffer , Opts ) ->
232
+ decode_rr_section (Bin , N , Buffer , Opts , []).
233
+ % %
234
+ decode_rr_section (<<>>= Rest , N , _Buffer , _Opts , RRs ) ->
230
235
{Rest ,reverse (RRs ),N =/= 0 };
231
- decode_rr_section (_Opcode , Rest , 0 , _Buffer , RRs ) ->
236
+ decode_rr_section (Rest , 0 , _Buffer , _Opts , RRs ) ->
232
237
{Rest ,reverse (RRs ),false };
233
- decode_rr_section (Opcode , Bin , N , Buffer , RRs ) ->
238
+ decode_rr_section (Bin , N , Buffer , { Opcode , Mdns } = Opts , RRs ) ->
234
239
? MATCH_ELSE_DECODE_ERROR (
235
240
decode_name (Bin , Buffer ),
236
241
{<<T :16 /unsigned ,C :16 /unsigned ,TTL :4 /binary ,
@@ -278,14 +283,8 @@ decode_rr_section(Opcode, Bin, N, Buffer, RRs) ->
278
283
error = Error ,
279
284
other_data = OtherData });
280
285
_ ->
281
- {Class ,CacheFlush } = decode_class (C ),
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 ,
286
+ {Class ,CacheFlush } = decode_class (C , Mdns ),
287
+ Data = decode_data (D , Class , Type , Buffer , Opcode ),
289
288
<<TimeToLive :32 /signed >> = TTL ,
290
289
# dns_rr {
291
290
domain = Name ,
@@ -295,25 +294,31 @@ decode_rr_section(Opcode, Bin, N, Buffer, RRs) ->
295
294
data = Data ,
296
295
func = CacheFlush }
297
296
end ,
298
- decode_rr_section (Opcode , Rest , N - 1 , Buffer , [RR |RRs ])
297
+ decode_rr_section (Rest , N - 1 , Buffer , Opts , [RR |RRs ])
299
298
end ).
300
299
301
300
% %
302
301
% % Encode a user query
303
302
% %
304
303
305
- encode (Q ) ->
306
- QdCount = length (Q # dns_rec .qdlist ),
307
- AnCount = length (Q # dns_rec .anlist ),
308
- NsCount = length (Q # dns_rec .nslist ),
309
- ArCount = length (Q # dns_rec .arlist ),
310
- OC = Q # dns_rec .header # dns_header .opcode ,
311
- B0 = encode_header (Q # dns_rec .header , QdCount , AnCount , NsCount , ArCount ),
304
+ encode (Q ) -> encode (Q , true ). % Backwards compatible
305
+ % %
306
+ encode (
307
+ # dns_rec {
308
+ header = Header ,
309
+ qdlist = QdList , anlist = AnList , nslist = NsList , arlist = ArList },
310
+ Mdns )
311
+ when is_boolean (Mdns ) ->
312
+ B0 =
313
+ encode_header (
314
+ Header ,
315
+ length (QdList ), length (AnList ), length (NsList ), length (ArList )),
316
+ Opcode = Header # dns_header .opcode ,
312
317
C0 = gb_trees :empty (),
313
- {B1 ,C1 } = encode_query_section (B0 , C0 , Q # dns_rec . qdlist ),
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 ),
318
+ {B1 ,C1 } = encode_query_section (B0 , Mdns , C0 , QdList ),
319
+ {B2 ,C2 } = encode_res_section (B1 , { Opcode , Mdns }, C1 , AnList ),
320
+ {B3 ,C3 } = encode_res_section (B2 , { Opcode , Mdns }, C2 , NsList ),
321
+ {B ,_ } = encode_res_section (B3 , { Opcode , Mdns }, C3 , ArList ),
317
322
B .
318
323
319
324
@@ -335,19 +340,20 @@ encode_header(#dns_header{id=Id}=H, QdCount, AnCount, NsCount, ArCount) ->
335
340
336
341
% % RFC 1035: 4.1.2. Question section format
337
342
% %
338
- encode_query_section (Bin , Comp , []) -> {Bin ,Comp };
339
- encode_query_section (Bin0 , Comp0 , [# dns_query {domain = DName }= Q | Qs ]) ->
343
+ encode_query_section (Bin , _Mdns , Comp , []) -> {Bin ,Comp };
344
+ encode_query_section (Bin0 , Mdns , Comp0 , [# dns_query {domain = DName }= Q | Qs ]) ->
340
345
T = encode_type (Q # dns_query .type ),
341
- C = encode_class (Q # dns_query .class , Q # dns_query .unicast_response ),
346
+ C = encode_class (
347
+ Q # dns_query .class , Mdns andalso Q # dns_query .unicast_response ),
342
348
{Bin ,Comp } = encode_name (Bin0 , Comp0 , byte_size (Bin0 ), DName ),
343
- encode_query_section (<<Bin /binary ,T :16 ,C :16 >>, Comp , Qs ).
349
+ encode_query_section (<<Bin /binary ,T :16 ,C :16 >>, Mdns , Comp , Qs ).
344
350
345
351
% % RFC 1035: 4.1.3. Resource record format
346
352
% % RFC 6891: 6.1.2, 6.1.3, 6.2.3 Opt RR format
347
353
% %
348
- encode_res_section (_Opcode , Bin , Comp , []) -> {Bin ,Comp };
354
+ encode_res_section (Bin , _Opts , Comp , []) -> {Bin ,Comp };
349
355
encode_res_section (
350
- Opcode , Bin , Comp ,
356
+ Bin , Opts , Comp ,
351
357
[# dns_rr {
352
358
domain = DName ,
353
359
type = Type ,
@@ -356,10 +362,10 @@ encode_res_section(
356
362
ttl = TTL ,
357
363
data = Data } | Rs ]) ->
358
364
encode_res_section_rr (
359
- Opcode , Bin , Comp , Rs , DName , Type , Class , CacheFlush ,
365
+ Bin , Opts , Comp , Rs , DName , Type , Class , CacheFlush ,
360
366
<<TTL :32 /signed >>, Data );
361
367
encode_res_section (
362
- Opcode , Bin , Comp ,
368
+ Bin , Opts , Comp ,
363
369
[# dns_rr_opt {
364
370
domain = DName ,
365
371
udp_payload_size = UdpPayloadSize ,
@@ -370,10 +376,10 @@ encode_res_section(
370
376
do = DnssecOk } | Rs ]) ->
371
377
DO = case DnssecOk of true -> 1 ; false -> 0 end ,
372
378
encode_res_section_rr (
373
- Opcode , Bin , Comp , Rs , DName , ? S_OPT , UdpPayloadSize , false ,
379
+ Bin , Opts , Comp , Rs , DName , ? S_OPT , UdpPayloadSize , false ,
374
380
<<ExtRCode ,Version ,DO :1 ,Z :15 >>, Data );
375
381
encode_res_section (
376
- Opcode , Bin , Comp ,
382
+ Bin , Opts , Comp ,
377
383
[# dns_rr_tsig {
378
384
domain = DName ,
379
385
algname = AlgName ,
@@ -385,26 +391,21 @@ encode_res_section(
385
391
other_data = OtherData }]) ->
386
392
Data = {AlgName ,Now ,Fudge ,MAC ,OriginalId ,Error ,OtherData },
387
393
encode_res_section_rr (
388
- Opcode , Bin , Comp , [], DName , ? S_TSIG , ? S_ANY , false ,
394
+ Bin , Opts , Comp , [], DName , ? S_TSIG , ? S_ANY , false ,
389
395
<<0 :32 /signed >>, Data ).
390
396
391
397
encode_res_section_rr (
392
- Opcode , Bin0 , Comp0 , Rs , DName , Type , Class , CacheFlush , TTL , Data ) ->
398
+ Bin0 , {Opcode ,Mdns } = Opts , Comp0 , Rs ,
399
+ DName , Type , Class , CacheFlush , TTL , Data ) ->
393
400
T = encode_type (Type ),
394
- C = encode_class (Class , CacheFlush ),
401
+ C = encode_class (Class , Mdns and CacheFlush ),
395
402
{Bin ,Comp1 } = encode_name (Bin0 , Comp0 , byte_size (Bin0 ), DName ),
396
403
Pos = byte_size (Bin )+ 2 + 2 + byte_size (TTL )+ 2 ,
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 ,
404
+ {DataBin ,Comp } = encode_data (Comp1 , Pos , Type , Class , Data , Opcode ),
403
405
DataSize = byte_size (DataBin ),
404
406
encode_res_section (
405
- Opcode ,
406
407
<<Bin /binary ,T :16 ,C :16 ,TTL /binary ,DataSize :16 ,DataBin /binary >>,
407
- Comp , Rs ).
408
+ Opts , Comp , Rs ).
408
409
409
410
% %
410
411
% % Resource types
@@ -497,21 +498,25 @@ encode_type(Type) ->
497
498
% % Resource classes
498
499
% %
499
500
500
- decode_class (C0 ) ->
501
+ decode_class (C , false ) ->
502
+ {decode_class (C ),false };
503
+ decode_class (C0 , true ) ->
501
504
FlagBit = 16#8000 ,
502
505
C = C0 band (bnot FlagBit ),
503
- Class =
504
- case C of
505
- ? C_IN -> in ;
506
- ? C_CHAOS -> chaos ;
507
- ? C_HS -> hs ;
508
- ? C_NONE -> none ;
509
- ? C_ANY -> any ;
510
- _ -> C % % raw unknown class
511
- end ,
506
+ Class = decode_class (C ),
512
507
Flag = (C0 band FlagBit ) =/= 0 ,
513
508
{Class ,Flag }.
514
509
510
+ decode_class (C ) ->
511
+ case C of
512
+ ? C_IN -> in ;
513
+ ? C_CHAOS -> chaos ;
514
+ ? C_HS -> hs ;
515
+ ? C_NONE -> none ;
516
+ ? C_ANY -> any ;
517
+ _ -> C % % raw unknown class
518
+ end .
519
+
515
520
516
521
encode_class (Class , Flag ) ->
517
522
C = encode_class (Class ),
@@ -558,7 +563,14 @@ encode_boolean(B) when is_integer(B) -> B.
558
563
decode_boolean (0 ) -> false ;
559
564
decode_boolean (I ) when is_integer (I ) -> true .
560
565
561
-
566
+ decode_data (Data , Class , Type , Buffer , Opcode ) ->
567
+ if
568
+ % % RFC 2136: 2.4. Allow length zero data for UPDATE
569
+ Opcode == ? UPDATE , Data == <<>> ->
570
+ # dns_rr {}# dns_rr .data ;
571
+ true ->
572
+ decode_data (Data , Class , Type , Buffer )
573
+ end .
562
574
% %
563
575
% % Data field -> term() content representation
564
576
% %
@@ -771,6 +783,13 @@ decode_name_label(Label, Name, N) ->
771
783
erlang :error (badarg , [Label ,Name ,N ])
772
784
end .
773
785
786
+ encode_data (Comp , Pos , Type , Class , Data , Opcode ) ->
787
+ if
788
+ Opcode == update , Data == # dns_rr {}# dns_rr .data ->
789
+ {<<>>,Comp };
790
+ true ->
791
+ encode_data (Comp , Pos , Type , Class , Data )
792
+ end .
774
793
% %
775
794
% % Data field -> {binary(),NewCompressionTable}
776
795
% %
0 commit comments