Skip to content

Commit 1843057

Browse files
committed
Add inet_dns:decode/2 and inet_dns:encode/2
Add new functions that take an option to indicate if encode/decode should be for mDNS, since it affects the value range for the CLASS to 15 or 16 bits.
1 parent a829427 commit 1843057

File tree

4 files changed

+180
-80
lines changed

4 files changed

+180
-80
lines changed

lib/kernel/src/inet_dns.erl

Lines changed: 93 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
%% RFC 7553: The Uniform Resource Identifier (URI) DNS Resource Record
3636
%% RFC 8945: Secret Key Transaction Authentication for DNS (TSIG)
3737

38-
-export([decode/1, encode/1]).
38+
-export([decode/1, decode/2, encode/1, encode/2]).
3939
-export([decode_algname/1, encode_algname/1]).
4040

4141
-import(lists, [reverse/1]).
@@ -148,8 +148,10 @@ lists_member(H, [_|T]) -> lists_member(H, T).
148148
throw(?DECODE_ERROR)
149149
end).
150150

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
153155
DnsRec ->
154156
{ok,DnsRec}
155157
catch
@@ -161,11 +163,14 @@ do_decode(<<Id:16,
161163
QR:1,Opcode:4,AA:1,TC:1,RD:1,
162164
RA:1,PR:1,_:2,Rcode:4,
163165
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}),
169174
?MATCH_ELSE_DECODE_ERROR(
170175
Rest,
171176
<<>>,
@@ -197,40 +202,40 @@ do_decode(<<Id:16,
197202
arlist=ArList}
198203
end)
199204
end);
200-
do_decode(_) ->
205+
do_decode(_, _) ->
201206
%% DNS message does not even match header
202207
throw(?DECODE_ERROR).
203208

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, []).
206211

207-
decode_query_section(<<>>=Rest, N, _Buffer, Qs) ->
212+
decode_query_section(<<>>=Rest, N, _Buffer, _Mdns, Qs) ->
208213
{Rest,reverse(Qs),N =/= 0};
209-
decode_query_section(Rest, 0, _Buffer, Qs) ->
214+
decode_query_section(Rest, 0, _Buffer, _Mdns, Qs) ->
210215
{Rest,reverse(Qs),false};
211-
decode_query_section(Bin, N, Buffer, Qs) ->
216+
decode_query_section(Bin, N, Buffer, Mdns, Qs) ->
212217
?MATCH_ELSE_DECODE_ERROR(
213218
decode_name(Bin, Buffer),
214219
{<<T:16,C:16,Rest/binary>>,Name},
215220
begin
216-
{Class,UnicastResponse} = decode_class(C),
221+
{Class,UnicastResponse} = decode_class(C, Mdns),
217222
DnsQuery =
218223
#dns_query{
219224
domain = Name,
220225
type = decode_type(T),
221226
class = Class,
222227
unicast_response = UnicastResponse},
223-
decode_query_section(Rest, N-1, Buffer, [DnsQuery|Qs])
228+
decode_query_section(Rest, N-1, Buffer, Mdns, [DnsQuery|Qs])
224229
end).
225230

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) ->
230235
{Rest,reverse(RRs),N =/= 0};
231-
decode_rr_section(_Opcode, Rest, 0, _Buffer, RRs) ->
236+
decode_rr_section(Rest, 0, _Buffer, _Opts, RRs) ->
232237
{Rest,reverse(RRs),false};
233-
decode_rr_section(Opcode, Bin, N, Buffer, RRs) ->
238+
decode_rr_section(Bin, N, Buffer, {Opcode,Mdns} = Opts, RRs) ->
234239
?MATCH_ELSE_DECODE_ERROR(
235240
decode_name(Bin, Buffer),
236241
{<<T:16/unsigned,C:16/unsigned,TTL:4/binary,
@@ -278,14 +283,8 @@ decode_rr_section(Opcode, Bin, N, Buffer, RRs) ->
278283
error = Error,
279284
other_data = OtherData});
280285
_ ->
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),
289288
<<TimeToLive:32/signed>> = TTL,
290289
#dns_rr{
291290
domain = Name,
@@ -295,25 +294,31 @@ decode_rr_section(Opcode, Bin, N, Buffer, RRs) ->
295294
data = Data,
296295
func = CacheFlush}
297296
end,
298-
decode_rr_section(Opcode, Rest, N-1, Buffer, [RR|RRs])
297+
decode_rr_section(Rest, N-1, Buffer, Opts, [RR|RRs])
299298
end).
300299

301300
%%
302301
%% Encode a user query
303302
%%
304303

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,
312317
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),
317322
B.
318323

319324

@@ -335,19 +340,20 @@ encode_header(#dns_header{id=Id}=H, QdCount, AnCount, NsCount, ArCount) ->
335340

336341
%% RFC 1035: 4.1.2. Question section format
337342
%%
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]) ->
340345
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),
342348
{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).
344350

345351
%% RFC 1035: 4.1.3. Resource record format
346352
%% RFC 6891: 6.1.2, 6.1.3, 6.2.3 Opt RR format
347353
%%
348-
encode_res_section(_Opcode, Bin, Comp, []) -> {Bin,Comp};
354+
encode_res_section(Bin, _Opts, Comp, []) -> {Bin,Comp};
349355
encode_res_section(
350-
Opcode, Bin, Comp,
356+
Bin, Opts, Comp,
351357
[#dns_rr{
352358
domain = DName,
353359
type = Type,
@@ -356,10 +362,10 @@ encode_res_section(
356362
ttl = TTL,
357363
data = Data} | Rs]) ->
358364
encode_res_section_rr(
359-
Opcode, Bin, Comp, Rs, DName, Type, Class, CacheFlush,
365+
Bin, Opts, Comp, Rs, DName, Type, Class, CacheFlush,
360366
<<TTL:32/signed>>, Data);
361367
encode_res_section(
362-
Opcode, Bin, Comp,
368+
Bin, Opts, Comp,
363369
[#dns_rr_opt{
364370
domain = DName,
365371
udp_payload_size = UdpPayloadSize,
@@ -370,10 +376,10 @@ encode_res_section(
370376
do = DnssecOk} | Rs]) ->
371377
DO = case DnssecOk of true -> 1; false -> 0 end,
372378
encode_res_section_rr(
373-
Opcode, Bin, Comp, Rs, DName, ?S_OPT, UdpPayloadSize, false,
379+
Bin, Opts, Comp, Rs, DName, ?S_OPT, UdpPayloadSize, false,
374380
<<ExtRCode,Version,DO:1,Z:15>>, Data);
375381
encode_res_section(
376-
Opcode, Bin, Comp,
382+
Bin, Opts, Comp,
377383
[#dns_rr_tsig{
378384
domain = DName,
379385
algname = AlgName,
@@ -385,26 +391,21 @@ encode_res_section(
385391
other_data = OtherData}]) ->
386392
Data = {AlgName,Now,Fudge,MAC,OriginalId,Error,OtherData},
387393
encode_res_section_rr(
388-
Opcode, Bin, Comp, [], DName, ?S_TSIG, ?S_ANY, false,
394+
Bin, Opts, Comp, [], DName, ?S_TSIG, ?S_ANY, false,
389395
<<0:32/signed>>, Data).
390396

391397
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) ->
393400
T = encode_type(Type),
394-
C = encode_class(Class, CacheFlush),
401+
C = encode_class(Class, Mdns and CacheFlush),
395402
{Bin,Comp1} = encode_name(Bin0, Comp0, byte_size(Bin0), DName),
396403
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),
403405
DataSize = byte_size(DataBin),
404406
encode_res_section(
405-
Opcode,
406407
<<Bin/binary,T:16,C:16,TTL/binary,DataSize:16,DataBin/binary>>,
407-
Comp, Rs).
408+
Opts, Comp, Rs).
408409

409410
%%
410411
%% Resource types
@@ -497,21 +498,25 @@ encode_type(Type) ->
497498
%% Resource classes
498499
%%
499500

500-
decode_class(C0) ->
501+
decode_class(C, false) ->
502+
{decode_class(C),false};
503+
decode_class(C0, true) ->
501504
FlagBit = 16#8000,
502505
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),
512507
Flag = (C0 band FlagBit) =/= 0,
513508
{Class,Flag}.
514509

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+
515520

516521
encode_class(Class, Flag) ->
517522
C = encode_class(Class),
@@ -558,7 +563,14 @@ encode_boolean(B) when is_integer(B) -> B.
558563
decode_boolean(0) -> false;
559564
decode_boolean(I) when is_integer(I) -> true.
560565

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.
562574
%%
563575
%% Data field -> term() content representation
564576
%%
@@ -771,6 +783,13 @@ decode_name_label(Label, Name, N) ->
771783
erlang:error(badarg, [Label,Name,N])
772784
end.
773785

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.
774793
%%
775794
%% Data field -> {binary(),NewCompressionTable}
776795
%%

lib/kernel/src/inet_dns_record_adts.pl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,18 @@
2424
# for internal records.
2525
#
2626
# The following defines which ADT function sets that will be generated
27-
# and which record fields that will be exponated.
27+
# and which record fields that will be exposed.
2828
#
2929
# (FunctionBaseName => [RecordName, FieldName ...], ...)
3030
my %Names = ('msg' => ['dns_rec', 'header', 'qdlist',
3131
'anlist', 'nslist', 'arlist'],
32-
'dns_rr' => ['dns_rr', 'domain', 'type', 'class', 'ttl', 'data'],
32+
'dns_rr' => ['dns_rr', 'domain', 'type', 'class', 'ttl', 'data',
33+
'func'],
3334
'dns_rr_opt' => ['dns_rr_opt', 'domain', 'type',
3435
'udp_payload_size', 'ext_rcode', 'version',
3536
'z', 'data', 'do'],
36-
'dns_query' => ['dns_query', 'domain', 'type', 'class'],
37+
'dns_query' => ['dns_query', 'domain', 'type', 'class',
38+
'unicast_response'],
3739
'header' => ['dns_header', 'id', 'qr', 'opcode', 'aa', 'tc',
3840
'rd', 'ra', 'pr', 'rcode']);
3941
# The functions are defined in the __DATA__ section at the end.

lib/kernel/src/inet_res.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ make_query(Dname, Class, Type, Options, Edns) ->
691691
class=Class}],
692692
arlist=ARList},
693693
?verbose(Options#options.verbose, "Query: ~p~n", [dns_msg(Msg)]),
694-
Buffer = inet_dns:encode(Msg),
694+
Buffer = inet_dns:encode(Msg, false),
695695
{Msg, Buffer}.
696696

697697
%% --------------------------------------------------------------------------
@@ -1091,7 +1091,7 @@ query_tcp(Timeout, Msg, Buffer, IP, Port, Verbose) ->
10911091
end.
10921092

10931093
decode_answer(Answer, Q_Msg, Verbose) ->
1094-
case inet_dns:decode(Answer) of
1094+
case inet_dns:decode(Answer, false) of
10951095
{ok, #dns_rec{header = H, arlist = ARList} = Msg} ->
10961096
?verbose(Verbose, "Got reply: ~p~n", [dns_msg(Msg)]),
10971097
T = case lists:keyfind(dns_rr_tsig, 1, ARList) of

0 commit comments

Comments
 (0)