Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
7351-Add-inet_dns-decode-2-and-inet_dns-encode-...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 7351-Add-inet_dns-decode-2-and-inet_dns-encode-2.patch of Package erlang
From 1843057b974e5a856024923a8405e9ce27399624 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen <raimo@erlang.org> Date: Fri, 24 Nov 2023 14:00:24 +0100 Subject: [PATCH] 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. --- lib/kernel/src/inet_dns.erl | 167 ++++++++++++++----------- lib/kernel/src/inet_dns_record_adts.pl | 8 +- lib/kernel/src/inet_res.erl | 4 +- lib/kernel/test/inet_res_SUITE.erl | 81 +++++++++++- 4 files changed, 180 insertions(+), 80 deletions(-) diff --git a/lib/kernel/src/inet_dns.erl b/lib/kernel/src/inet_dns.erl index c65546a7e5..94598905be 100644 --- a/lib/kernel/src/inet_dns.erl +++ b/lib/kernel/src/inet_dns.erl @@ -35,7 +35,7 @@ %% RFC 7553: The Uniform Resource Identifier (URI) DNS Resource Record %% RFC 8945: Secret Key Transaction Authentication for DNS (TSIG) --export([decode/1, encode/1]). +-export([decode/1, decode/2, encode/1, encode/2]). -export([decode_algname/1, encode_algname/1]). -import(lists, [reverse/1]). @@ -148,8 +148,10 @@ lists_member(H, [_|T]) -> lists_member(H, T). throw(?DECODE_ERROR) end). -decode(Buffer) when is_binary(Buffer) -> - try do_decode(Buffer) of +decode(Buffer) -> decode(Buffer, true). % Backwards compatible +%% +decode(Buffer, Mdns) when is_binary(Buffer), is_boolean(Mdns) -> + try do_decode(Buffer, Mdns) of DnsRec -> {ok,DnsRec} catch @@ -161,11 +163,14 @@ do_decode(<<Id:16, QR:1,Opcode:4,AA:1,TC:1,RD:1, RA:1,PR:1,_:2,Rcode:4, QdCount:16,AnCount:16,NsCount:16,ArCount:16, - QdBuf/binary>>=Buffer) -> - {AnBuf,QdList,QdTC} = decode_query_section(QdBuf,QdCount,Buffer), - {NsBuf,AnList,AnTC} = decode_rr_section(Opcode,AnBuf,AnCount,Buffer), - {ArBuf,NsList,NsTC} = decode_rr_section(Opcode,NsBuf,NsCount,Buffer), - {Rest,ArList,ArTC} = decode_rr_section(Opcode,ArBuf,ArCount,Buffer), + QdBuf/binary>>=Buffer, Mdns) -> + {AnBuf,QdList,QdTC} = decode_query_section(QdBuf,QdCount,Buffer,Mdns), + {NsBuf,AnList,AnTC} = + decode_rr_section(AnBuf,AnCount,Buffer,{Opcode,Mdns}), + {ArBuf,NsList,NsTC} = + decode_rr_section(NsBuf,NsCount,Buffer,{Opcode,Mdns}), + {Rest,ArList,ArTC} = + decode_rr_section(ArBuf,ArCount,Buffer,{Opcode,Mdns}), ?MATCH_ELSE_DECODE_ERROR( Rest, <<>>, @@ -197,40 +202,40 @@ do_decode(<<Id:16, arlist=ArList} end) end); -do_decode(_) -> +do_decode(_, _) -> %% DNS message does not even match header throw(?DECODE_ERROR). -decode_query_section(Bin, N, Buffer) -> - decode_query_section(Bin, N, Buffer, []). +decode_query_section(Bin, N, Buffer, Mdns) -> + decode_query_section(Bin, N, Buffer, Mdns, []). -decode_query_section(<<>>=Rest, N, _Buffer, Qs) -> +decode_query_section(<<>>=Rest, N, _Buffer, _Mdns, Qs) -> {Rest,reverse(Qs),N =/= 0}; -decode_query_section(Rest, 0, _Buffer, Qs) -> +decode_query_section(Rest, 0, _Buffer, _Mdns, Qs) -> {Rest,reverse(Qs),false}; -decode_query_section(Bin, N, Buffer, Qs) -> +decode_query_section(Bin, N, Buffer, Mdns, Qs) -> ?MATCH_ELSE_DECODE_ERROR( decode_name(Bin, Buffer), {<<T:16,C:16,Rest/binary>>,Name}, begin - {Class,UnicastResponse} = decode_class(C), + {Class,UnicastResponse} = decode_class(C, Mdns), DnsQuery = #dns_query{ domain = Name, type = decode_type(T), class = Class, unicast_response = UnicastResponse}, - decode_query_section(Rest, N-1, Buffer, [DnsQuery|Qs]) + decode_query_section(Rest, N-1, Buffer, Mdns, [DnsQuery|Qs]) end). -decode_rr_section(Opcode, Bin, N, Buffer) -> - decode_rr_section(Opcode, Bin, N, Buffer, []). - -decode_rr_section(_Opcode, <<>>=Rest, N, _Buffer, RRs) -> +decode_rr_section(Bin, N, Buffer, Opts) -> + decode_rr_section(Bin, N, Buffer, Opts, []). +%% +decode_rr_section(<<>>=Rest, N, _Buffer, _Opts, RRs) -> {Rest,reverse(RRs),N =/= 0}; -decode_rr_section(_Opcode, Rest, 0, _Buffer, RRs) -> +decode_rr_section(Rest, 0, _Buffer, _Opts, RRs) -> {Rest,reverse(RRs),false}; -decode_rr_section(Opcode, Bin, N, Buffer, RRs) -> +decode_rr_section(Bin, N, Buffer, {Opcode,Mdns} = Opts, RRs) -> ?MATCH_ELSE_DECODE_ERROR( decode_name(Bin, Buffer), {<<T:16/unsigned,C:16/unsigned,TTL:4/binary, @@ -278,14 +283,8 @@ decode_rr_section(Opcode, Bin, N, Buffer, RRs) -> error = Error, other_data = OtherData}); _ -> - {Class,CacheFlush} = decode_class(C), - Data = if - %% RFC 2136: 2.4. Allow length zero data for UPDATE - Opcode == ?UPDATE, D == <<>> -> - #dns_rr{}#dns_rr.data; - true -> - decode_data(D, Class, Type, Buffer) - end, + {Class,CacheFlush} = decode_class(C, Mdns), + Data = decode_data(D, Class, Type, Buffer, Opcode), <<TimeToLive:32/signed>> = TTL, #dns_rr{ domain = Name, @@ -295,25 +294,31 @@ decode_rr_section(Opcode, Bin, N, Buffer, RRs) -> data = Data, func = CacheFlush} end, - decode_rr_section(Opcode, Rest, N-1, Buffer, [RR|RRs]) + decode_rr_section(Rest, N-1, Buffer, Opts, [RR|RRs]) end). %% %% Encode a user query %% -encode(Q) -> - QdCount = length(Q#dns_rec.qdlist), - AnCount = length(Q#dns_rec.anlist), - NsCount = length(Q#dns_rec.nslist), - ArCount = length(Q#dns_rec.arlist), - OC = Q#dns_rec.header#dns_header.opcode, - B0 = encode_header(Q#dns_rec.header, QdCount, AnCount, NsCount, ArCount), +encode(Q) -> encode(Q, true). % Backwards compatible +%% +encode( + #dns_rec{ + header = Header, + qdlist = QdList, anlist = AnList, nslist = NsList, arlist = ArList }, + Mdns) + when is_boolean(Mdns) -> + B0 = + encode_header( + Header, + length(QdList), length(AnList), length(NsList), length(ArList)), + Opcode = Header#dns_header.opcode, C0 = gb_trees:empty(), - {B1,C1} = encode_query_section(B0, C0, Q#dns_rec.qdlist), - {B2,C2} = encode_res_section(OC, B1, C1, Q#dns_rec.anlist), - {B3,C3} = encode_res_section(OC, B2, C2, Q#dns_rec.nslist), - {B,_} = encode_res_section(OC, B3, C3, Q#dns_rec.arlist), + {B1,C1} = encode_query_section(B0, Mdns, C0, QdList), + {B2,C2} = encode_res_section(B1, {Opcode,Mdns}, C1, AnList), + {B3,C3} = encode_res_section(B2, {Opcode,Mdns}, C2, NsList), + {B,_} = encode_res_section(B3, {Opcode,Mdns}, C3, ArList), B. @@ -335,19 +340,20 @@ encode_header(#dns_header{id=Id}=H, QdCount, AnCount, NsCount, ArCount) -> %% RFC 1035: 4.1.2. Question section format %% -encode_query_section(Bin, Comp, []) -> {Bin,Comp}; -encode_query_section(Bin0, Comp0, [#dns_query{domain=DName}=Q | Qs]) -> +encode_query_section(Bin, _Mdns, Comp, []) -> {Bin,Comp}; +encode_query_section(Bin0, Mdns, Comp0, [#dns_query{domain=DName}=Q | Qs]) -> T = encode_type(Q#dns_query.type), - C = encode_class(Q#dns_query.class, Q#dns_query.unicast_response), + C = encode_class( + Q#dns_query.class, Mdns andalso Q#dns_query.unicast_response), {Bin,Comp} = encode_name(Bin0, Comp0, byte_size(Bin0), DName), - encode_query_section(<<Bin/binary,T:16,C:16>>, Comp, Qs). + encode_query_section(<<Bin/binary,T:16,C:16>>, Mdns, Comp, Qs). %% RFC 1035: 4.1.3. Resource record format %% RFC 6891: 6.1.2, 6.1.3, 6.2.3 Opt RR format %% -encode_res_section(_Opcode, Bin, Comp, []) -> {Bin,Comp}; +encode_res_section(Bin, _Opts, Comp, []) -> {Bin,Comp}; encode_res_section( - Opcode, Bin, Comp, + Bin, Opts, Comp, [#dns_rr{ domain = DName, type = Type, @@ -356,10 +362,10 @@ encode_res_section( ttl = TTL, data = Data} | Rs]) -> encode_res_section_rr( - Opcode, Bin, Comp, Rs, DName, Type, Class, CacheFlush, + Bin, Opts, Comp, Rs, DName, Type, Class, CacheFlush, <<TTL:32/signed>>, Data); encode_res_section( - Opcode, Bin, Comp, + Bin, Opts, Comp, [#dns_rr_opt{ domain = DName, udp_payload_size = UdpPayloadSize, @@ -370,10 +376,10 @@ encode_res_section( do = DnssecOk} | Rs]) -> DO = case DnssecOk of true -> 1; false -> 0 end, encode_res_section_rr( - Opcode, Bin, Comp, Rs, DName, ?S_OPT, UdpPayloadSize, false, + Bin, Opts, Comp, Rs, DName, ?S_OPT, UdpPayloadSize, false, <<ExtRCode,Version,DO:1,Z:15>>, Data); encode_res_section( - Opcode, Bin, Comp, + Bin, Opts, Comp, [#dns_rr_tsig{ domain = DName, algname = AlgName, @@ -385,26 +391,21 @@ encode_res_section( other_data = OtherData}]) -> Data = {AlgName,Now,Fudge,MAC,OriginalId,Error,OtherData}, encode_res_section_rr( - Opcode, Bin, Comp, [], DName, ?S_TSIG, ?S_ANY, false, + Bin, Opts, Comp, [], DName, ?S_TSIG, ?S_ANY, false, <<0:32/signed>>, Data). encode_res_section_rr( - Opcode, Bin0, Comp0, Rs, DName, Type, Class, CacheFlush, TTL, Data) -> + Bin0, {Opcode,Mdns} = Opts, Comp0, Rs, + DName, Type, Class, CacheFlush, TTL, Data) -> T = encode_type(Type), - C = encode_class(Class, CacheFlush), + C = encode_class(Class, Mdns and CacheFlush), {Bin,Comp1} = encode_name(Bin0, Comp0, byte_size(Bin0), DName), Pos = byte_size(Bin)+(2+2)+byte_size(TTL)+2, - {DataBin,Comp} = if - Opcode == update, Data == #dns_rr{}#dns_rr.data -> - {<<>>,Comp1}; - true -> - encode_data(Comp1, Pos, Type, Class, Data) - end, + {DataBin,Comp} = encode_data(Comp1, Pos, Type, Class, Data, Opcode), DataSize = byte_size(DataBin), encode_res_section( - Opcode, <<Bin/binary,T:16,C:16,TTL/binary,DataSize:16,DataBin/binary>>, - Comp, Rs). + Opts, Comp, Rs). %% %% Resource types @@ -497,21 +498,25 @@ encode_type(Type) -> %% Resource classes %% -decode_class(C0) -> +decode_class(C, false) -> + {decode_class(C),false}; +decode_class(C0, true) -> FlagBit = 16#8000, C = C0 band (bnot FlagBit), - Class = - case C of - ?C_IN -> in; - ?C_CHAOS -> chaos; - ?C_HS -> hs; - ?C_NONE -> none; - ?C_ANY -> any; - _ -> C %% raw unknown class - end, + Class = decode_class(C), Flag = (C0 band FlagBit) =/= 0, {Class,Flag}. +decode_class(C) -> + case C of + ?C_IN -> in; + ?C_CHAOS -> chaos; + ?C_HS -> hs; + ?C_NONE -> none; + ?C_ANY -> any; + _ -> C %% raw unknown class + end. + encode_class(Class, Flag) -> C = encode_class(Class), @@ -558,7 +563,14 @@ encode_boolean(B) when is_integer(B) -> B. decode_boolean(0) -> false; decode_boolean(I) when is_integer(I) -> true. - +decode_data(Data, Class, Type, Buffer, Opcode) -> + if + %% RFC 2136: 2.4. Allow length zero data for UPDATE + Opcode == ?UPDATE, Data == <<>> -> + #dns_rr{}#dns_rr.data; + true -> + decode_data(Data, Class, Type, Buffer) + end. %% %% Data field -> term() content representation %% @@ -771,6 +783,13 @@ decode_name_label(Label, Name, N) -> erlang:error(badarg, [Label,Name,N]) end. +encode_data(Comp, Pos, Type, Class, Data, Opcode) -> + if + Opcode == update, Data == #dns_rr{}#dns_rr.data -> + {<<>>,Comp}; + true -> + encode_data(Comp, Pos, Type, Class, Data) + end. %% %% Data field -> {binary(),NewCompressionTable} %% diff --git a/lib/kernel/src/inet_dns_record_adts.pl b/lib/kernel/src/inet_dns_record_adts.pl index f3331222fc..0adc2285b9 100644 --- a/lib/kernel/src/inet_dns_record_adts.pl +++ b/lib/kernel/src/inet_dns_record_adts.pl @@ -24,16 +24,18 @@ use strict; # for internal records. # # The following defines which ADT function sets that will be generated -# and which record fields that will be exponated. +# and which record fields that will be exposed. # # (FunctionBaseName => [RecordName, FieldName ...], ...) my %Names = ('msg' => ['dns_rec', 'header', 'qdlist', 'anlist', 'nslist', 'arlist'], - 'dns_rr' => ['dns_rr', 'domain', 'type', 'class', 'ttl', 'data'], + 'dns_rr' => ['dns_rr', 'domain', 'type', 'class', 'ttl', 'data', + 'func'], 'dns_rr_opt' => ['dns_rr_opt', 'domain', 'type', 'udp_payload_size', 'ext_rcode', 'version', 'z', 'data', 'do'], - 'dns_query' => ['dns_query', 'domain', 'type', 'class'], + 'dns_query' => ['dns_query', 'domain', 'type', 'class', + 'unicast_response'], 'header' => ['dns_header', 'id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'pr', 'rcode']); # The functions are defined in the __DATA__ section at the end. diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 091ebef91c..c2b5b3be77 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -691,7 +691,7 @@ make_query(Dname, Class, Type, Options, Edns) -> class=Class}], arlist=ARList}, ?verbose(Options#options.verbose, "Query: ~p~n", [dns_msg(Msg)]), - Buffer = inet_dns:encode(Msg), + Buffer = inet_dns:encode(Msg, false), {Msg, Buffer}. %% -------------------------------------------------------------------------- @@ -1091,7 +1091,7 @@ query_tcp(Timeout, Msg, Buffer, IP, Port, Verbose) -> end. decode_answer(Answer, Q_Msg, Verbose) -> - case inet_dns:decode(Answer) of + case inet_dns:decode(Answer, false) of {ok, #dns_rec{header = H, arlist = ARList} = Msg} -> ?verbose(Verbose, "Got reply: ~p~n", [dns_msg(Msg)]), T = case lists:keyfind(dns_rr_tsig, 1, ARList) of diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index 045fdcc83b..3a0f2bd05a 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -35,7 +35,8 @@ edns0/1, edns0_multi_formerr/1, txt_record/1, files_monitor/1, nxdomain_reply/1, last_ms_answer/1, intermediate_error/1, servfail_retry_timeout_default/1, servfail_retry_timeout_1000/1, - label_compression_limit/1, update/1, tsig_client/1, tsig_server/1 + label_compression_limit/1, update/1, tsig_client/1, tsig_server/1, + mdns_encode_decode/1 ]). -export([ gethostbyaddr/0, gethostbyaddr/1, @@ -78,6 +79,7 @@ all() -> intermediate_error, servfail_retry_timeout_default, servfail_retry_timeout_1000, label_compression_limit, update, tsig_client, tsig_server, + mdns_encode_decode, gethostbyaddr, gethostbyaddr_v6, gethostbyname, gethostbyname_v6, getaddr, getaddr_v6, ipv4_to_ipv6, host_and_addr]. @@ -1661,6 +1663,83 @@ tsig_server(Domain, TS0, Sock) -> ok = gen_tcp:send(Sock, PktR3S). +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% inet_dns encode/decode specials +%% Should maybe be a suite of its own. + +mdns_encode_decode(Config) when is_list(Config) -> + Id = 4711, + Opcode = 'query', + Class = in, + Type = txt, + Domain = "test.local", + Text = ["abc", "123"], + BinText = <<3,$a,$b,$c, 3,$1,$2,$3>>, % Wire format for Text + IN_h = 32769, % Class IN with high bit set + %% + %% Create a unicast-response to a mDNS query + %% with cache-flush bit on the RR record (func field), + %% which sets the class fields high bit when encoded + Header = + inet_dns:make_header( + [{id, Id}, {qr, true}, {opcode, Opcode}, + {aa, false}, {tc, false}, {rd, false}, {ra, false}, {pr, false}]), + Query = + inet_dns:make_dns_query( + [{class, Class}, {type, Type}, {domain, Domain}, + {unicast_response, true}]), % High bit 1 + TxtRR = + inet_dns:make_rr( + [{domain, Domain}, {class, Class}, {type, Type}, + {data, Text}, + {func, true}]), % High bit 1 + Msg = + inet_dns:make_msg( + [{header, Header}, {qdlist, [Query]}, {anlist, [TxtRR]}]), + %% + %% Encode and verify decode + Buffer = inet_dns:encode(Msg), + {{ok, Msg}, Msg} = {inet_dns:decode(Buffer), Msg}, + %% + %% Decode as if not mDNS, which exposes the high class field bit, + %% and doesn't decode the RR data + Query2 = + inet_dns:make_dns_query( + Query, + [{class,IN_h}, + {unicast_response,false}]), % High bit 0 + TxtRR2 = + inet_dns:make_rr( + TxtRR, + [{class,IN_h}, + {data,BinText}, % Raw, encoded + {func,false}]), % High bit 0 + Msg2 = inet_dns:make_msg(Msg, [{qdlist, [Query2]}, {anlist, [TxtRR2]}]), + {{ok, Msg2}, Msg2} = {inet_dns:decode(Buffer, false), Msg2}, + %% + %% Encode non-mDNS which ignores the high class bit flags + %% in #dns_query.unicast_response and #dns_rr.func + Buffer3 = inet_dns:encode(Msg, false), + %% + %% Decode non-mDNS and verify + Query3 = + inet_dns:make_dns_query(Query, unicast_response, false), % High bit 0 + TxtRR3 = inet_dns:make_rr(TxtRR, func, false), % High bit 0 + Msg3 = inet_dns:make_msg(Msg, [{qdlist, [Query3]}, {anlist, [TxtRR3]}]), + {{ok, Msg3}, Msg3} = {inet_dns:decode(Buffer3, false), Msg3}, + %% + %% Decode mDNS should give the same answer since the high bits + %% in the class fields are encoded as zero + {{ok, Msg3}, Msg3} = {inet_dns:decode(Buffer3, true), Msg3}, + %% + %% Encode non-mDNS with class >= 32768 + Buffer4 = inet_dns:encode(Msg2, false), + {{ok, Msg2}, Msg2} = {inet_dns:decode(Buffer4, false), Msg2}, + %% + %% Decoding for mDNS should set the high class field flags + {{ok, Msg}, Msg} = {inet_dns:decode(Buffer4, true), Msg}, + ok. + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Compatibility tests. Call the inet_SUITE tests, but with %% lookup = [file,dns] instead of [native] -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor