@@ -173,6 +173,7 @@ decode_reply(Buffer, #dns_rec{} = Q, Mdns)
173173 {error , Reason }
174174 end .
175175
176+ -define (MSG_HDR_SIZE , 12 ). % Must align with the following pattern
176177do_decode (
177178 <<Id :16 ,
178179 QR :1 ,Opcode :4 ,AA :1 ,TC :1 ,RD :1 ,
@@ -196,14 +197,19 @@ do_decode(
196197 pr = decode_boolean (PR ),
197198 rcode = Rcode },
198199 do_decode (
199- Buffer , DnsHdr , QdList , AnBuf , AnCount , NsCount , ArCount , {Opcode ,Mdns }).
200+ Buffer , DnsHdr , QdList , AnBuf , AnCount , NsCount , ArCount , {Opcode ,Mdns });
201+ do_decode (<<_ /binary >>, _Mdns ) ->
202+ throw (? DECODE_ERROR ).
203+
200204
201205do_decode_reply (
202206 <<Id :16 , _ /binary >> = Buffer ,
203207 # dns_rec { header = Q_H , qdlist = [Q_RR ] },
204208 Mdns ) ->
205209 Id =:= Q_H # dns_header .id orelse throw (badid ),
206- do_decode_reply (Buffer , Q_H , Q_RR , Id , Mdns ).
210+ do_decode_reply (Buffer , Q_H , Q_RR , Id , Mdns );
211+ do_decode_reply (<<_ /binary >>, _Q , _Mdns ) ->
212+ throw (? DECODE_ERROR ).
207213
208214do_decode_reply (
209215 <<_ :16 ,
@@ -223,27 +229,33 @@ do_decode_reply(
223229 % %
224230 QdCount == 1
225231 orelse throw (noquery ),
226- {AnBuf , [RR ], QdTC } = decode_query_section (QdBuf , QdCount , Buffer , Mdns ),
227- RR # dns_query .class =:= Q_RR # dns_query .class andalso
228- RR # dns_query .type =:= Q_RR # dns_query .type andalso
229- inet_db :eq_domains (RR # dns_query .domain , Q_RR # dns_query .domain )
230- orelse throw (noquery ),
231- H_TC = decode_boolean (TC ),
232- QdTC andalso not H_TC
233- andalso throw (? DECODE_ERROR ),
234- DnsHdr =
235- # dns_header {
236- id = Id ,
237- qr = H_QR ,
238- opcode = H_Opcode ,
239- aa = decode_boolean (AA ),
240- tc = H_TC ,
241- rd = H_RD ,
242- ra = decode_boolean (RA ),
243- pr = decode_boolean (PR ),
244- rcode = Rcode },
245- do_decode (
246- Buffer , DnsHdr , [RR ], AnBuf , AnCount , NsCount , ArCount , {Opcode ,Mdns });
232+ {AnBuf , RRs , QdTC } = decode_query_section (QdBuf , QdCount , Buffer , Mdns ),
233+ case RRs of
234+ [RR ] ->
235+ RR # dns_query .class =:= Q_RR # dns_query .class andalso
236+ RR # dns_query .type =:= Q_RR # dns_query .type andalso
237+ inet_db :eq_domains (RR # dns_query .domain , Q_RR # dns_query .domain )
238+ orelse throw (noquery ),
239+ H_TC = decode_boolean (TC ),
240+ QdTC andalso not H_TC
241+ andalso throw (? DECODE_ERROR ),
242+ DnsHdr =
243+ # dns_header {
244+ id = Id ,
245+ qr = H_QR ,
246+ opcode = H_Opcode ,
247+ aa = decode_boolean (AA ),
248+ tc = H_TC ,
249+ rd = H_RD ,
250+ ra = decode_boolean (RA ),
251+ pr = decode_boolean (PR ),
252+ rcode = Rcode },
253+ do_decode (
254+ Buffer , DnsHdr , [RR ], AnBuf , AnCount , NsCount , ArCount ,
255+ {Opcode ,Mdns });
256+ _ ->
257+ throw (? DECODE_ERROR )
258+ end ;
247259do_decode_reply (<<_ /binary >>, _Q_H , _Q_RR , _Id , _Mdns ) ->
248260 throw (unknown ).
249261
@@ -776,7 +788,11 @@ decode_characters(Data, Encoding) ->
776788 ? MATCH_ELSE_DECODE_ERROR (
777789 Data ,
778790 <<Len ,Bin :Len /binary ,Rest /binary >>,
779- {Rest ,unicode :characters_to_list (Bin , Encoding )}).
791+ ? MATCH_ELSE_DECODE_ERROR (
792+ unicode :characters_to_list (Bin , Encoding ),
793+ String ,
794+ is_list (String ),
795+ {Rest ,String })).
780796
781797% % One domain name only, there must be nothing after
782798% %
@@ -786,32 +802,55 @@ decode_domain(Bin, Buffer) ->
786802% % Domain name -> {RestBin,Name}
787803% %
788804decode_name (Bin , Buffer ) ->
789- decode_name (Bin , Buffer , [], Bin , 0 ).
790-
791- % % Tail advances with Rest until the first indirection is followed
792- % % then it stays put at that Rest.
793- decode_name (_ , Buffer , _Labels , _Tail , Cnt ) when Cnt > byte_size (Buffer ) ->
794- throw (? DECODE_ERROR ); % % Insanity bailout - this must be a decode loop
795- decode_name (<<0 ,Rest /binary >>, _Buffer , Labels , Tail , Cnt ) ->
796- % % Root domain, we have all labels for the domain name
797- {if Cnt =/= 0 -> Tail ; true -> Rest end ,
805+ decode_name (Bin , Buffer , [], Bin , 0 , 0 ).
806+
807+ decode_name (_Bin , _Buffer , _Labels , _Cont , NameLen , _PtrCnt )
808+ when NameLen >= 255 ->
809+ % % There must also be room for the root label in 255 octets
810+ % %
811+ % % One might also cap PtrCnt heuristicly at 20..50 but there is no
812+ % % support for that in RFC 1035, although almost certainly not a problem,
813+ % % and not an uncommon defensive practice.
814+ % %
815+ % % Now it is possible to craft a message that will have long
816+ % % backwards pointer chains causing high, but not catastrophically high,
817+ % % decode work.
818+ throw (? DECODE_ERROR );
819+ decode_name (<<0 ,Rest /binary >>, _Buffer , Labels , Cont , _NameLen , PtrCnt ) ->
820+ % % Root domain; we have all labels for the domain name
821+ {decode_name_rest (Rest , Cont , PtrCnt ),
798822 decode_name_labels (Labels )};
799- decode_name (<<0 :2 ,Len :6 ,Label :Len /binary ,Rest /binary >>,
800- Buffer , Labels , Tail , Cnt ) ->
823+ decode_name (
824+ <<0 :2 ,Len :6 ,Label :Len /binary ,Rest /binary >>,
825+ Buffer , Labels , Cont , NameLen , PtrCnt ) ->
801826 % % One plain label here
802- decode_name (Rest , Buffer , [Label |Labels ],
803- if Cnt =/= 0 -> Tail ; true -> Rest end ,
804- Cnt );
805- decode_name (<<3 :2 ,Ptr :14 ,Rest /binary >>, Buffer , Labels , Tail , Cnt ) ->
806- % % Indirection - reposition in buffer and recurse
827+ decode_name (
828+ Rest , Buffer , [Label |Labels ], decode_name_rest (Rest , Cont , PtrCnt ),
829+ NameLen + 1 + Len , PtrCnt );
830+ decode_name (
831+ <<3 :2 ,Ptr :14 ,Rest /binary >>, Buffer , Labels , Cont , NameLen , PtrCnt )
832+ when
833+ % % Indirection *should* point to lower offset
834+ % % (stricter than RFC1035, but commonly used common sense),
835+ % % and *must* not point into the header.
836+ % %
837+ % % This forces a pointer loop to either end when clashing
838+ % % into the header, or get content and end on max NameLen.
839+ Ptr < byte_size (Buffer ) - (byte_size (Rest ) + 2 ),
840+ Ptr >= ? MSG_HDR_SIZE ->
841+ % % Indirection - reposition in buffer
807842 ? MATCH_ELSE_DECODE_ERROR (
808843 Buffer ,
809844 <<_ :Ptr /binary ,Bin /binary >>,
810845 decode_name (
811- Bin , Buffer , Labels ,
812- if Cnt =/= 0 -> Tail ; true -> Rest end ,
813- Cnt + 2 )); % size of indirection pointer
814- decode_name (_ , _ , _ , _ , _ ) -> throw (? DECODE_ERROR ).
846+ Bin , Buffer , Labels , decode_name_rest (Rest , Cont , PtrCnt ),
847+ NameLen , PtrCnt + 1 ));
848+ decode_name (_Bin , _Buffer , _Labels , _Cont , _NameLen , _PtrCnt ) ->
849+ throw (? DECODE_ERROR ).
850+
851+ decode_name_rest (Rest , _Cont , 0 ) -> Rest ;
852+ decode_name_rest (_Rest , Cont , _PtrCnt ) -> Cont .
853+
815854
816855% % Reverse list of labels (binaries) -> domain name (string)
817856decode_name_labels ([]) -> " ." ;
@@ -940,11 +979,15 @@ encode_data(Comp, Pos, ?S_NAPTR, Data) ->
940979 B0 = <<Order :16 ,Preference :16 >>,
941980 B1 = encode_string (B0 , iolist_to_binary (Flags )),
942981 B2 = encode_string (B1 , iolist_to_binary (Services )),
943- B3 = encode_string (B2 , unicode :characters_to_binary (Regexp ,
944- unicode , utf8 )),
945- % % Bypass name compression (RFC 2915: section 2)
946- {B ,_ } = encode_name (B3 , gb_trees :empty (), Pos + byte_size (B3 ), Replacement ),
947- {B ,Comp };
982+ case unicode :characters_to_binary (Regexp , unicode , utf8 ) of
983+ EncRegexp when is_binary (EncRegexp ) ->
984+ B3 = encode_string (B2 , EncRegexp ),
985+ % % Bypass name compression (RFC 2915: section 2)
986+ {B ,_ } =
987+ encode_name (
988+ B3 , gb_trees :empty (), Pos + byte_size (B3 ), Replacement ),
989+ {B ,Comp }
990+ end ;
948991encode_data (Comp , _ , ? S_TXT , Data ) -> {encode_txt (Data ),Comp };
949992encode_data (Comp , _ , ? S_SPF , Data ) -> {encode_txt (Data ),Comp };
950993encode_data (Comp , _ , ? S_URI , Data ) ->
0 commit comments