Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
6579-inet_dns-support-UPDATE.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 6579-inet_dns-support-UPDATE.patch of Package erlang
From 89f6c65a4af3a21da0ecf268b0982b6fe158873b Mon Sep 17 00:00:00 2001 From: Alexander Clouter <alex@digriz.org.uk> Date: Thu, 20 Apr 2023 08:22:11 +0100 Subject: [PATCH 09/10] inet_dns: support UPDATE --- lib/kernel/src/inet_dns.erl | 79 +++++++++++-------- lib/kernel/src/inet_dns.hrl | 13 ++- lib/kernel/src/inet_res.erl | 5 ++ lib/kernel/test/inet_res_SUITE.erl | 62 ++++++++++++++- .../inet_res_SUITE_data/otptest/knot_inc.conf | 6 ++ 5 files changed, 129 insertions(+), 36 deletions(-) diff --git a/lib/kernel/src/inet_dns.erl b/lib/kernel/src/inet_dns.erl index fc38e437aa..6228ca9790 100644 --- a/lib/kernel/src/inet_dns.erl +++ b/lib/kernel/src/inet_dns.erl @@ -24,6 +24,7 @@ %% RFC 1035: Domain Names - Implementation and Specification %% RFC 1995: Incremental Zone Transfer in DNS %% RFC 1996: A Mechanism for Prompt Notification of Zone Changes (DNS NOTIFY) +%% RFC 2136: Dynamic Updates in the Domain Name System (DNS UPDATE) %% RFC 2181: Clarifications to the DNS Specification %% RFC 2782: A DNS RR for specifying the location of services (DNS SRV) %% RFC 2915: The Naming Authority Pointer (NAPTR) DNS Resource Rec @@ -160,9 +161,9 @@ do_decode(<<Id:16, 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(AnBuf,AnCount,Buffer), - {ArBuf,NsList,NsTC} = decode_rr_section(NsBuf,NsCount,Buffer), - {Rest,ArList,ArTC} = decode_rr_section(ArBuf,ArCount,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), ?MATCH_ELSE_DECODE_ERROR( Rest, <<>>, @@ -220,14 +221,14 @@ decode_query_section(Bin, N, Buffer, Qs) -> decode_query_section(Rest, N-1, Buffer, [DnsQuery|Qs]) end). -decode_rr_section(Bin, N, Buffer) -> - decode_rr_section(Bin, N, Buffer, []). +decode_rr_section(Opcode, Bin, N, Buffer) -> + decode_rr_section(Opcode, Bin, N, Buffer, []). -decode_rr_section(<<>>=Rest, N, _Buffer, RRs) -> +decode_rr_section(_Opcode, <<>>=Rest, N, _Buffer, RRs) -> {Rest,reverse(RRs),N =/= 0}; -decode_rr_section(Rest, 0, _Buffer, RRs) -> +decode_rr_section(_Opcode, Rest, 0, _Buffer, RRs) -> {Rest,reverse(RRs),false}; -decode_rr_section(Bin, N, Buffer, RRs) -> +decode_rr_section(Opcode, Bin, N, Buffer, RRs) -> ?MATCH_ELSE_DECODE_ERROR( decode_name(Bin, Buffer), {<<T:16/unsigned,C:16/unsigned,TTL:4/binary, @@ -254,7 +255,13 @@ decode_rr_section(Bin, N, Buffer, RRs) -> do = DnssecOk}; _ -> {Class,CacheFlush} = decode_class(C), - Data = decode_data(D, Class, Type, Buffer), + 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, <<TimeToLive:32/signed>> = TTL, #dns_rr{ domain = Name, @@ -264,7 +271,7 @@ decode_rr_section(Bin, N, Buffer, RRs) -> data = Data, func = CacheFlush} end, - decode_rr_section(Rest, N-1, Buffer, [RR|RRs]) + decode_rr_section(Opcode, Rest, N-1, Buffer, [RR|RRs]) end). %% @@ -276,12 +283,13 @@ encode(Q) -> 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), C0 = gb_trees:empty(), {B1,C1} = encode_query_section(B0, C0, Q#dns_rec.qdlist), - {B2,C2} = encode_res_section(B1, C1, Q#dns_rec.anlist), - {B3,C3} = encode_res_section(B2, C2, Q#dns_rec.nslist), - {B,_} = encode_res_section(B3, C3, Q#dns_rec.arlist), + {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), B. @@ -313,9 +321,9 @@ encode_query_section(Bin0, Comp0, [#dns_query{domain=DName}=Q | 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(Bin, Comp, []) -> {Bin,Comp}; +encode_res_section(_Opcode, Bin, Comp, []) -> {Bin,Comp}; encode_res_section( - Bin, Comp, + Opcode, Bin, Comp, [#dns_rr{ domain = DName, type = Type, @@ -324,10 +332,10 @@ encode_res_section( ttl = TTL, data = Data} | Rs]) -> encode_res_section_rr( - Bin, Comp, Rs, DName, Type, Class, CacheFlush, + Opcode, Bin, Comp, Rs, DName, Type, Class, CacheFlush, <<TTL:32/signed>>, Data); encode_res_section( - Bin, Comp, + Opcode, Bin, Comp, [#dns_rr_opt{ domain = DName, udp_payload_size = UdpPayloadSize, @@ -338,18 +346,24 @@ encode_res_section( do = DnssecOk} | Rs]) -> DO = case DnssecOk of true -> 1; false -> 0 end, encode_res_section_rr( - Bin, Comp, Rs, DName, ?S_OPT, UdpPayloadSize, false, + Opcode, Bin, Comp, Rs, DName, ?S_OPT, UdpPayloadSize, false, <<ExtRCode,Version,DO:1,Z:15>>, Data). encode_res_section_rr( - Bin0, Comp0, Rs, DName, Type, Class, CacheFlush, TTL, Data) -> + Opcode, Bin0, Comp0, Rs, DName, Type, Class, CacheFlush, TTL, Data) -> T = encode_type(Type), C = encode_class(Class, CacheFlush), {Bin,Comp1} = encode_name(Bin0, Comp0, byte_size(Bin0), DName), Pos = byte_size(Bin)+(2+2)+byte_size(TTL)+2, - {DataBin,Comp} = encode_data(Comp1, Pos, Type, Class, Data), + {DataBin,Comp} = if + Opcode == update, Data == #dns_rr{}#dns_rr.data -> + {<<>>,Comp1}; + true -> + encode_data(Comp1, Pos, Type, Class, Data) + end, DataSize = byte_size(DataBin), encode_res_section( + Opcode, <<Bin/binary,T:16,C:16,TTL/binary,DataSize:16,DataBin/binary>>, Comp, Rs). @@ -450,6 +464,7 @@ decode_class(C0) -> ?C_IN -> in; ?C_CHAOS -> chaos; ?C_HS -> hs; + ?C_NONE -> none; ?C_ANY -> any; _ -> C %% raw unknown class end, @@ -469,6 +484,7 @@ encode_class(Class) -> in -> ?C_IN; chaos -> ?C_CHAOS; hs -> ?C_HS; + none -> ?C_NONE; any -> ?C_ANY; Class when is_integer(Class) -> Class %% raw unknown class end. @@ -479,6 +495,7 @@ decode_opcode(Opcode) -> ?IQUERY -> iquery; ?STATUS -> status; ?NOTIFY -> notify; + ?UPDATE -> update; _ when is_integer(Opcode) -> Opcode %% non-standard opcode end. @@ -488,6 +505,7 @@ encode_opcode(Opcode) -> iquery -> ?IQUERY; status -> ?STATUS; notify -> ?NOTIFY; + update -> ?UPDATE; _ when is_integer(Opcode) -> Opcode %% non-standard opcode end. @@ -715,17 +733,6 @@ decode_name_label(Label, Name, N) -> %% %% Data field -> {binary(),NewCompressionTable} %% -%% Class IN RRs -encode_data(Comp, _, ?S_A, in, Addr) -> - {A,B,C,D} = Addr, - {<<A,B,C,D>>,Comp}; -encode_data(Comp, _, ?S_AAAA, in, Addr) -> - {A,B,C,D,E,F,G,H} = Addr, - {<<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16>>,Comp}; -encode_data(Comp, _, ?S_WKS, in, Data) -> - {{A,B,C,D},Proto,BitMap} = Data, - BitMapBin = iolist_to_binary(BitMap), - {<<A,B,C,D,Proto,BitMapBin/binary>>,Comp}; %% OPT pseudo-RR (of no class) - should not take this way; %% this must be a #dns_rr{type = ?S_OPT} instead of a #dns_rr_opt{}, %% so good luck getting in particular Class and TTL right... @@ -742,6 +749,16 @@ encode_data(Comp, Pos, Type, Class, Data) -> %% %% %% Standard RRs (any class) +encode_data(Comp, _, ?S_A, Addr) -> + {A,B,C,D} = Addr, + {<<A,B,C,D>>,Comp}; +encode_data(Comp, _, ?S_AAAA, Addr) -> + {A,B,C,D,E,F,G,H} = Addr, + {<<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16>>,Comp}; +encode_data(Comp, _, ?S_WKS, Data) -> + {{A,B,C,D},Proto,BitMap} = Data, + BitMapBin = iolist_to_binary(BitMap), + {<<A,B,C,D,Proto,BitMapBin/binary>>,Comp}; encode_data(Comp, Pos, ?S_SOA, Data) -> {MName,RName,Serial,Refresh,Retry,Expiry,Minimum} = Data, {B1,Comp1} = encode_name(Comp, Pos, MName), diff --git a/lib/kernel/src/inet_dns.hrl b/lib/kernel/src/inet_dns.hrl index 39cdd4252f..17151fbb6a 100644 --- a/lib/kernel/src/inet_dns.hrl +++ b/lib/kernel/src/inet_dns.hrl @@ -28,6 +28,7 @@ -define(IQUERY, 16#1). %% inverse query -define(STATUS, 16#2). %% nameserver status query -define(NOTIFY, 16#4). %% notify +-define(UPDATE, 16#5). %% dynamic update %% %% Currently defined response codes @@ -38,6 +39,11 @@ -define(NXDOMAIN, 3). %% non existent domain -define(NOTIMP, 4). %% not implemented -define(REFUSED, 5). %% query refused +-define(YXDOMAIN, 6). %% name exists when it should not (DDNS) +-define(YXRRSET, 7). %% RR set exists when it should not (DDNS) +-define(NXRRSET, 8). %% RR set that should exist does not (DDNS) +-define(NOTAUTH, 9). %% server not authoritative for zone (DDNS) +-define(NOTZONE, 10). %% name not contained in zone (DDNS) -define(BADVERS, 16). %% bad version EDNS pseudo-rr RFC6891: 6.1.3 %% @@ -134,6 +140,7 @@ -define(C_IN, 1). %% the arpa internet -define(C_CHAOS, 3). %% for chaos net at MIT -define(C_HS, 4). %% for Hesiod name server at MIT +-define(C_NONE, 254). %% for DDNS (RFC2136, section 2.4) -define(C_ANY, 255). %% wildcard match %% @@ -161,9 +168,9 @@ -record(dns_rec, { header, %% dns_header record - qdlist = [], %% list of question entries - anlist = [], %% list of answer entries - nslist = [], %% list of authority entries + qdlist = [], %% list of question (for UPDATE 'zone') entries + anlist = [], %% list of answer (for UPDATE 'prequisites') entries + nslist = [], %% list of authority (for UPDATE 'update') entries arlist = [] %% list of resource entries }). diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 0fb46332f2..f0d2f21364 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -1106,6 +1106,11 @@ decode_answer(Answer, Q_Msg, Verbose) -> ?NXDOMAIN -> {error,{nxdomain,Msg}}; ?NOTIMP -> {error,{notimp,Msg}}; ?REFUSED -> {error,{refused,Msg}}; + ?YXDOMAIN -> {error,{yxdomain,Msg}}; + ?YXRRSET -> {error,{yxrrset,Msg}}; + ?NXRRSET -> {error,{nxrrset,Msg}}; + ?NOTAUTH -> {error,{noauth,Msg}}; + ?NOTZONE -> {error,{nozone,Msg}}; ?BADVERS -> {error,{badvers,Msg}}; _ -> {error,{unknown,Msg}} end; diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index c620e6d93e..c69cf934ce 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -35,7 +35,7 @@ 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 + label_compression_limit/1, update/1 ]). -export([ gethostbyaddr/0, gethostbyaddr/1, @@ -77,7 +77,7 @@ all() -> nxdomain_reply, last_ms_answer, intermediate_error, servfail_retry_timeout_default, servfail_retry_timeout_1000, - label_compression_limit, + label_compression_limit, update, gethostbyaddr, gethostbyaddr_v6, gethostbyname, gethostbyname_v6, getaddr, getaddr_v6, ipv4_to_ipv6, host_and_addr]. @@ -133,6 +133,7 @@ zone_dir(TC) -> files_monitor -> otptest; nxdomain_reply -> otptest; last_ms_answer -> otptest; + update -> otptest; intermediate_error -> {internal, #{rcode => ?REFUSED}}; @@ -1412,6 +1413,63 @@ incr_domain([Char | Domain]) -> [Char+1 | Domain]. +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that the data portion can only be zero bytes for UPDATEs +%% and that a real DNS server (Knot DNS) accepts our packet + +update(Config) when is_list(Config) -> + {NSIP,NSPort} = ns(Config), + Domain = "otptest", + + % test that empty data for a query fails + QueryRec = #dns_rec{ + header = #dns_header{ opcode = query }, + anlist = [ + #dns_rr{ domain = "test-update." ++ Domain, type = a } + ] + }, + true = try inet_dns:encode(QueryRec) of + _ -> + false + catch + error:{badmatch,[]}:_ -> + true + end, + + % test that empty data for an update + UpdateRec = #dns_rec{ + header = #dns_header{ opcode = update }, + % Zone + qdlist = [ + #dns_query{ domain = Domain, class = in, type = soa } + ], + % Update + nslist = [ + #dns_rr{ + domain = "update-test." ++ Domain, + ttl = 300, + class = in, + type = a, + data = {192,0,2,1} + } + ] + }, + UpdatePkt = inet_dns:encode(UpdateRec), + true = is_binary(UpdatePkt), + + % check if an actual DNS server accepts it + SockOpts = [binary,{active,false}], + {ok,Sock} = gen_udp:open(0, SockOpts), + ok = gen_udp:connect(Sock, NSIP, NSPort), + ok = gen_udp:send(Sock, UpdatePkt), + {ok,{NSIP,NSPort,ResponsePkt}} = gen_udp:recv(Sock, 0), + ok = gen_udp:close(Sock), + {ok,ResponseRec} = inet_dns:decode(ResponsePkt), + #dns_rec{ header = #dns_header{ rcode = ?NOERROR } } = ResponseRec, + + ok. + + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Compatibility tests. Call the inet_SUITE tests, but with %% lookup = [file,dns] instead of [native] diff --git a/lib/kernel/test/inet_res_SUITE_data/otptest/knot_inc.conf b/lib/kernel/test/inet_res_SUITE_data/otptest/knot_inc.conf index 6dd8c6dedf..50c9f0d977 100644 --- a/lib/kernel/test/inet_res_SUITE_data/otptest/knot_inc.conf +++ b/lib/kernel/test/inet_res_SUITE_data/otptest/knot_inc.conf @@ -1,4 +1,10 @@ +acl: + - id: update_rule + address: 127.0.0.1 + action: update + zone: - domain: otptest + acl: [ update_rule ] - domain: 0.0.127.in-addr.arpa. - domain: 0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. -- 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