@@ -221,11 +221,13 @@ test('EDNS.ECS#encode', function() {
221221 new Packet . Resource . EDNS . ECS ( '10.11.12.13/24' ) ,
222222 ] ) ;
223223
224+ // RFC 7871 §6: ADDRESS field is only ceil(sourcePrefixLength/8) octets,
225+ // so /24 writes 3 address bytes (10.11.12), not 4.
224226 const b = Packet . Resource . encode ( query ) ;
225227 assert . deepEqual ( b , Buffer . from ( [
226228 0x00 , 0x00 , 0x29 , 0x02 , 0x00 , 0x00 , 0x00 , 0x00 ,
227- 0x00 , 0x00 , 0x0c , 0x00 , 0x08 , 0x00 , 0x08 , 0x00 ,
228- 0x01 , 0x18 , 0x00 , 0x0a , 0x0b , 0x0c , 0x0d ] ) ) ;
229+ 0x00 , 0x00 , 0x0b , 0x00 , 0x08 , 0x00 , 0x07 , 0x00 ,
230+ 0x01 , 0x18 , 0x00 , 0x0a , 0x0b , 0x0c ] ) ) ;
229231} ) ;
230232
231233test ( 'EDNS#decode' , function ( ) {
@@ -772,6 +774,115 @@ test('Packet.uuid exercises the full 16-bit range with high diversity', function
772774 }
773775} ) ;
774776
777+ test ( 'Name decode rejects a pointer cycle (no infinite loop)' , function ( ) {
778+ // Hand-built packet header (12 bytes) followed by a name that points to
779+ // itself: byte 12 = 0xC0 (pointer high), byte 13 = 0x0C (offset = 12).
780+ // Without cycle detection this would loop forever.
781+ const buf = Buffer . alloc ( 14 ) ;
782+ buf [ 12 ] = 0xC0 ;
783+ buf [ 13 ] = 0x0C ;
784+ const reader = new Packet . Reader ( buf ) ;
785+ reader . offset = 8 * 12 ;
786+ assert . throws ( ( ) => Packet . Name . decode ( reader ) , / p o i n t e r c y c l e / ) ;
787+ } ) ;
788+
789+ test ( 'Name decode rejects a two-step pointer cycle' , function ( ) {
790+ // Two pointers pointing at each other: bytes 12-13 = C0 0E, bytes 14-15 = C0 0C.
791+ const buf = Buffer . alloc ( 16 ) ;
792+ buf [ 12 ] = 0xC0 ; buf [ 13 ] = 0x0E ;
793+ buf [ 14 ] = 0xC0 ; buf [ 15 ] = 0x0C ;
794+ const reader = new Packet . Reader ( buf ) ;
795+ reader . offset = 8 * 12 ;
796+ assert . throws ( ( ) => Packet . Name . decode ( reader ) , / p o i n t e r c y c l e / ) ;
797+ } ) ;
798+
799+ test ( 'Header default constructor initializes ancount/ad/cd' , function ( ) {
800+ const header = new Packet . Header ( ) ;
801+ assert . equal ( header . ancount , 0 ) ;
802+ assert . equal ( header . ad , 0 ) ;
803+ assert . equal ( header . cd , 0 ) ;
804+ } ) ;
805+
806+ test ( 'Header#parse exposes AD and CD bits (RFC 4035)' , function ( ) {
807+ // Second header word with AD=1, CD=1, all other flags zero.
808+ // Layout: qr(1) opcode(4) aa(1) tc(1) rd(1) ra(1) z(1) ad(1) cd(1) rcode(4)
809+ // bits : 0 0000 0 0 0 0 0 1 1 0000 => 0000 0000 0011 0000 = 0x0030
810+ const buf = Buffer . from ( [
811+ 0x00 , 0x01 , // id
812+ 0x00 , 0x30 , // flags: AD=1, CD=1
813+ 0x00 , 0x00 , 0x00 , 0x00 , // counts
814+ 0x00 , 0x00 , 0x00 , 0x00 ,
815+ ] ) ;
816+ const header = Packet . Header . parse ( buf ) ;
817+ assert . equal ( header . z , 0 ) ;
818+ assert . equal ( header . ad , 1 ) ;
819+ assert . equal ( header . cd , 1 ) ;
820+ } ) ;
821+
822+ test ( 'Header#toBuffer round-trips AD and CD bits' , function ( ) {
823+ const header = new Packet . Header ( { id : 0x4242 , ad : 1 , cd : 1 } ) ;
824+ const parsed = Packet . Header . parse ( header . toBuffer ( ) ) ;
825+ assert . equal ( parsed . id , 0x4242 ) ;
826+ assert . equal ( parsed . ad , 1 ) ;
827+ assert . equal ( parsed . cd , 1 ) ;
828+ assert . equal ( parsed . z , 0 ) ;
829+ } ) ;
830+
831+ test ( 'EDNS exposes extendedRcode / version / doFlag' , function ( ) {
832+ const opt = new Packet . Resource . EDNS ( [ ] , { extendedRcode : 16 , version : 0 , doFlag : true } ) ;
833+ assert . equal ( opt . extendedRcode , 16 ) ;
834+ assert . equal ( opt . version , 0 ) ;
835+ assert . equal ( opt . doFlag , true ) ;
836+ // ttl wire encoding: ext rcode in top byte, DO at bit 15 of low half.
837+ assert . equal ( opt . ttl , ( 16 << 24 ) | 0x8000 ) ;
838+ } ) ;
839+
840+ test ( 'EDNS round-trip preserves DO bit and extended RCODE' , function ( ) {
841+ const opt = new Packet . Resource . EDNS ( [ ] , { extendedRcode : 23 , version : 0 , doFlag : true } ) ;
842+ const parsed = Packet . Resource . decode ( Packet . Resource . encode ( opt ) ) ;
843+ assert . equal ( parsed . extendedRcode , 23 ) ;
844+ assert . equal ( parsed . doFlag , true ) ;
845+ } ) ;
846+
847+ test ( 'EDNS udpPayloadSize is configurable (RFC 6891 §6.2.3)' , function ( ) {
848+ const opt = new Packet . Resource . EDNS ( [ ] , { udpPayloadSize : 4096 } ) ;
849+ assert . equal ( opt . class , 4096 ) ;
850+ const parsed = Packet . Resource . decode ( Packet . Resource . encode ( opt ) ) ;
851+ assert . equal ( parsed . class , 4096 ) ;
852+ } ) ;
853+
854+ test ( 'EDNS.ECS#encode truncates IPv4 address to prefix length (RFC 7871)' , function ( ) {
855+ // /8 → 1 octet, /17 → 3 octets (ceil)
856+ for ( const [ cidr , expectedOctets ] of [
857+ [ '10.0.0.0/8' , 1 ] ,
858+ [ '10.20.0.0/16' , 2 ] ,
859+ [ '10.20.30.0/24' , 3 ] ,
860+ [ '10.20.30.0/17' , 3 ] ,
861+ [ '10.20.30.40/32' , 4 ] ,
862+ ] ) {
863+ const query = new Packet . Resource . EDNS ( [ new Packet . Resource . EDNS . ECS ( cidr ) ] ) ;
864+ const buf = Packet . Resource . encode ( query ) ;
865+ // Layout: name(1) type(2) class(2) ttl(4) rdlength(2) optionCode(2)
866+ // optionLength(2) → optionLength sits at offset 13. Address byte count =
867+ // optionLength - 4 (family + src prefix + scope prefix headers).
868+ const optionLength = buf . readUInt16BE ( 13 ) ;
869+ assert . equal ( optionLength - 4 , expectedOctets , `cidr ${ cidr } ` ) ;
870+ }
871+ } ) ;
872+
873+ test ( 'EDNS.ECS#encode supports IPv6 family' , function ( ) {
874+ // family=2 (IPv6), /32 prefix → 4 leading octets of the address.
875+ const ecs = Packet . Resource . EDNS . ECS ( '2001:db8::/32' ) ;
876+ ecs . family = 2 ; // factory currently hard-codes family 1; opt into IPv6
877+ const opt = new Packet . Resource . EDNS ( [ ecs ] ) ;
878+ const buf = Packet . Resource . encode ( opt ) ;
879+ const parsed = Packet . Resource . decode ( buf ) ;
880+ assert . equal ( parsed . rdata [ 0 ] . family , 2 ) ;
881+ assert . equal ( parsed . rdata [ 0 ] . sourcePrefixLength , 32 ) ;
882+ // The decoder pads truncated IPv6 to 8 segments; '2001:db8' followed by 6 zero segments.
883+ assert . equal ( parsed . rdata [ 0 ] . ip , '2001:db8:0:0:0:0:0:0' ) ;
884+ } ) ;
885+
775886test ( 'Packet.parse tolerates multiple questions' , function ( ) {
776887 const request = new Packet ( ) ;
777888 request . header . id = 0x9999 ;
0 commit comments