@@ -761,3 +761,150 @@ def test_records_same_packet_share_fate():
761761 first_time = dnsin .answers [0 ].created
762762 for answer in dnsin .answers :
763763 assert answer .created == first_time
764+
765+
766+ def test_dns_compression_invalid_skips_record ():
767+ """Test our wire parser can skip records we do not know how to parse."""
768+ packet = (
769+ b"\x00 \x00 \x84 \x00 \x00 \x00 \x00 \x06 \x00 \x00 \x00 \x00 \x04 _hap\x04 _tcp\x05 local\x00 \x00 \x0c "
770+ b"\x00 \x01 \x00 \x00 \x11 \x94 \x00 \x16 \x13 eufy HomeBase2-2464\xc0 \x0c \x04 Eufy\xc0 \x16 \x00 /"
771+ b"\x80 \x01 \x00 \x00 \x00 x\x00 \x08 \xc0 \xa6 \x00 \x04 @\x00 \x00 \x08 \xc0 '\x00 /\x80 \x01 \x00 \x00 "
772+ b"\x11 \x94 \x00 \t \xc0 '\x00 \x05 \x00 \x00 \x80 \x00 @\xc0 =\x00 \x01 \x80 \x01 \x00 \x00 \x00 x\x00 \x04 "
773+ b"\xc0 \xa8 Dp\xc0 '\x00 !\x80 \x01 \x00 \x00 \x00 x\x00 \x08 \x00 \x00 \x00 \x00 \xd1 _\xc0 =\xc0 '\x00 "
774+ b"\x10 \x80 \x01 \x00 \x00 \x11 \x94 \x00 K\x04 c#=1\x04 ff=2\x14 id=38:71:4F:6B:76:00\x08 md=T8010"
775+ b"\x06 pv=1.1\x05 s#=75\x04 sf=1\x04 ci=2\x0b sh=xaQk4g=="
776+ )
777+ parsed = r .DNSIncoming (packet )
778+ answer = r .DNSNsec (
779+ 'eufy HomeBase2-2464._hap._tcp.local.' ,
780+ const ._TYPE_NSEC ,
781+ const ._CLASS_IN | const ._CLASS_UNIQUE ,
782+ const ._DNS_OTHER_TTL ,
783+ 'eufy HomeBase2-2464._hap._tcp.local.' ,
784+ [const ._TYPE_TXT , const ._TYPE_SRV ],
785+ )
786+ assert answer in parsed .answers
787+
788+
789+ def test_dns_compression_points_forward ():
790+ """Test our wire parser can unpack nsec records with compression."""
791+ packet = (
792+ b"\x00 \x00 \x84 \x00 \x00 \x00 \x00 \x07 \x00 \x00 \x00 \x00 \x0e TV Beneden (2)"
793+ b"\x10 _androidtvremote\x04 _tcp\x05 local\x00 \x00 \x10 \x80 \x01 \x00 \x00 \x11 "
794+ b"\x94 \x00 \x15 \x14 bt=D8:13:99:AC:98:F1\xc0 \x0c \x00 /\x80 \x01 \x00 \x00 \x11 "
795+ b"\x94 \x00 \t \xc0 \x0c \x00 \x05 \x00 \x00 \x80 \x00 @\t Android-3\xc0 1\x00 /\x80 "
796+ b"\x01 \x00 \x00 \x00 x\x00 \x08 \xc0 \x9c \x00 \x04 @\x00 \x00 \x08 \xc0 l\x00 \x01 \x80 "
797+ b"\x01 \x00 \x00 \x00 x\x00 \x04 \xc0 \xa8 X\x0f \xc0 \x0c \x00 !\x80 \x01 \x00 \x00 \x00 "
798+ b"x\x00 \x08 \x00 \x00 \x00 \x00 \x19 B\xc0 l\xc0 \x1b \x00 \x0c \x00 \x01 \x00 \x00 \x11 "
799+ b"\x94 \x00 \x02 \xc0 \x0c \t _services\x07 _dns-sd\x04 _udp\xc0 1\x00 \x0c \x00 \x01 "
800+ b"\x00 \x00 \x11 \x94 \x00 \x02 \xc0 \x1b "
801+ )
802+ parsed = r .DNSIncoming (packet )
803+ answer = r .DNSNsec (
804+ 'TV Beneden (2)._androidtvremote._tcp.local.' ,
805+ const ._TYPE_NSEC ,
806+ const ._CLASS_IN | const ._CLASS_UNIQUE ,
807+ const ._DNS_OTHER_TTL ,
808+ 'TV Beneden (2)._androidtvremote._tcp.local.' ,
809+ [const ._TYPE_TXT , const ._TYPE_SRV ],
810+ )
811+ assert answer in parsed .answers
812+
813+
814+ def test_dns_compression_points_to_itself ():
815+ """Test our wire parser does not loop forever when a compression pointer points to itself."""
816+ packet = (
817+ b"\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x02 \x06 domain\x05 local\x00 \x00 \x01 "
818+ b"\x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x05 \xc0 (\x00 \x01 \x80 \x01 \x00 \x00 \x00 "
819+ b"\x01 \x00 \x04 \xc0 \xa8 \xd0 \x06 "
820+ )
821+ parsed = r .DNSIncoming (packet )
822+ assert len (parsed .answers ) == 1
823+
824+
825+ def test_dns_compression_points_beyond_packet ():
826+ """Test our wire parser does not fail when the compression pointer points beyond the packet."""
827+ packet = (
828+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x02 \x06 domain\x05 local\x00 \x00 \x01 '
829+ b'\x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x05 \xe7 \x0f \x00 \x01 \x80 \x01 \x00 \x00 '
830+ b'\x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x06 '
831+ )
832+ parsed = r .DNSIncoming (packet )
833+ assert len (parsed .answers ) == 1
834+
835+
836+ def test_dns_compression_generic_failure ():
837+ """Test our wire parser does not loop forever when dns compression is corrupt."""
838+ packet = (
839+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x02 \x06 domain\x05 local\x00 \x00 \x01 '
840+ b'\x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x05 -\x0c \x00 \x01 \x80 \x01 \x00 \x00 '
841+ b'\x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x06 '
842+ )
843+ parsed = r .DNSIncoming (packet )
844+ assert len (parsed .answers ) == 1
845+
846+
847+ def test_label_length_attack ():
848+ """Test our wire parser does not loop forever when the name exceeds 253 chars."""
849+ packet = (
850+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x02 \x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
851+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
852+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
853+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
854+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
855+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
856+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
857+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d'
858+ b'\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x01 d\x00 \x00 \x01 \x80 '
859+ b'\x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x05 \xc0 \x0c \x00 \x01 \x80 \x01 \x00 \x00 \x00 '
860+ b'\x01 \x00 \x04 \xc0 \xa8 \xd0 \x06 '
861+ )
862+ parsed = r .DNSIncoming (packet )
863+ assert len (parsed .answers ) == 0
864+
865+
866+ def test_label_compression_attack ():
867+ """Test our wire parser does not loop forever when exceeding the maximum number of labels."""
868+ packet = (
869+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x02 \x03 atk\x00 \x00 \x01 \x80 '
870+ b'\x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x05 \x03 atk\x03 atk\x03 atk\x03 atk\x03 '
871+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
872+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
873+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
874+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
875+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
876+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
877+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
878+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
879+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
880+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
881+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
882+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
883+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
884+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
885+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
886+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
887+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
888+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 '
889+ b'atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\x03 atk\xc0 '
890+ b'\x0c \x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x06 '
891+ )
892+ parsed = r .DNSIncoming (packet )
893+ assert len (parsed .answers ) == 1
894+
895+
896+ def test_dns_compression_loop_attack ():
897+ """Test our wire parser does not loop forever when dns compression is in a loop."""
898+ packet = (
899+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x07 \x03 atk\x03 dns\x05 loc'
900+ b'al\xc0 \x10 \x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x05 \x04 a'
901+ b'tk2\x04 dns2\xc0 \x14 \x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 \x05 '
902+ b'\x04 atk3\xc0 \x10 \x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 \xa8 \xd0 '
903+ b'\x05 \x04 atk4\x04 dns5\xc0 \x14 \x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 \x04 \xc0 '
904+ b'\xa8 \xd0 \x05 \x04 atk5\x04 dns2\xc0 ^\x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 '
905+ b'\x04 \xc0 \xa8 \xd0 \x05 \xc0 s\x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 '
906+ b'\x04 \xc0 \xa8 \xd0 \x05 \xc0 s\x00 \x01 \x80 \x01 \x00 \x00 \x00 \x01 \x00 '
907+ b'\x04 \xc0 \xa8 \xd0 \x05 '
908+ )
909+ parsed = r .DNSIncoming (packet )
910+ assert len (parsed .answers ) == 0
0 commit comments