perm filename DOMSUB.FAI[S,NET]1 blob sn#843596 filedate 1987-07-27 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00006 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	Record formats 
C00011 00003	 RRTYPE RRCLAS RRTTL RRDLEN RRNAME TY.A TY.NS TY.CNAME TY.SOA TY.MB TY.MG TY.MR TY.NULL TY.WKS TY.PTR TY.HINFO TY.MINFO TY.MX TY.AXFR TY.ALL TY.XA CL.IN CL.CH CL.ALL AUTHOR MXPREF P NET DOM NETIBF NETOBF DOMIBF DOMOBF DLKBLK DLKNME NAMBLK DATBLK DATPTR SETUDP QUERYB QDCOUNT ANCOUNT NSCOUNT ARCOUNT TEMPBP TTLMIN TTLMAX FATAL
C00016 00004	 SUCCES NICKNM SFTERR NAMERR GETDOM GETDO1 GETDO2 GETDO3 GETDO4 GETDMA GETINP GETIN1 GETIN2 GETIN3 GETIN4 GETIN5 GETIN6 GETIN7 GETIN8 CPOPJ2 CPOPJ1 CPOPJ
C00025 00005	 SQUERY SQUPTD SQUPD1 SQUPTR SQUPT1 SQUER0 SQUER1 SQUER2 UNKERR FMTERR SRVFAI NOTIMP REFUSE RDRESP SKIPQR SKIPQ1 RDANS RDAUTH RDADDL RDRRET NAMERX
C00035 00006	 READRR READR1 READR2 READR3 READR4 RDDISP RD.MAX RD.ILL RD.SOA RD.NUL RD.WKS RD.HIN RD.MIN RD.A RDOUTP RD.PTR RD.MX RD.DOM RD.DO1 RD.DO2 READC0 READCP READC1 READC2
C00041 ENDMK
C⊗;
;Record formats ;⊗

;Resource records read from or written to the system cache are stored
;in the following format (36-bit words):
;
; +-----------------------------------------------------------------------+
; |			TYPE						  |
; +-----------------------------------------------------------------------+
; |			CLASS						  |
; +-----------------------------------------------------------------------+
; |			TIME TO LIVE (in seconds)			  |
; +-----------------------------------------------------------------------+
; |			DATA LENGTH (or immediate data)			  |
; +---------------+---------------+---------------+---------------+-------+
; |								  |	  |
; |	    DOMAIN NAME, followed by DATA (in 8-bit bytes)	  |	  |
; |								  |	  |
; +---------------+---------------+---------------+---------------+-------+
;
;The domain name is a sequence of components, each having a length byte
;followed by that many bytes of characters.  A zero length byte terminates
;the string.  It is followed by data, generally in the same format.  The
;length of the data is stored in case the data has some other format.  If
;the data is known to fit in one word, it is stored in place of the data
;length.  (For Internet addresses, it is 32 bits right-aligned in the
;word.)

;Data length less than 0 means that a reply was received indicating that
;the requested data does not exist (e.g., the name is unknown).  These
;records are cached to avoid repeated queries for nonexistent information.

;Messages sent to or received from name servers follow the format in RFC
;883, as modified in RFC 973.  Each message has five sections:
;
;      +---------------------+
;      |	Header	     |
;      +---------------------+
;      |       Question      | the question for the name server
;      +---------------------+
;      |	Answer	     | answering resource records (RRs)
;      +---------------------+
;      |      Authority      | RRs pointing toward an authority
;      +---------------------+
;      |      Additional     | RRs holding pertinent information
;      +---------------------+
;
;There are no padding bytes, so after the header we cannot assume the
;alignment of any fields in such a message.
;
;The header is 12 bytes (shown here in 16-bit words; we store these two
;per word, left-aligned):
;
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		      ID		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |QR|   Opcode  |AA|TC|RD|RA|	   |   RCODE   |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		    QDCOUNT		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		    ANCOUNT		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		    NSCOUNT		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		    ARCOUNT		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;
;The question section looks as follows:
;
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |					       |
;      /		     QNAME		       /
;      /					       /
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		     QTYPE		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		     QCLASS		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;
;QNAME is in the 8-bit format described above, except that it may be
;"compressed" by including pointers to previous name components.  See RFC
;883 for details.
;
;The other sections contain 0 or more resource records (given by the
;counts in the header).  A resource record looks like
;
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |					       |
;      /					       /
;      /		      NAME		       /
;      |					       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		      TYPE		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		     CLASS		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |					       |
;      +		      TTL		       +
;      |					       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;      |		   RDLENGTH		       |
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
;      /		     RDATA		       /
;      /					       /
;      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;
;NAME is in the 8-bit compressed format, as is RDATA if it is a domain
;name.	RDLENGTH is the length (in bytes) of RDATA.

;⊗ RRTYPE RRCLAS RRTTL RRDLEN RRNAME TY.A TY.NS TY.CNAME TY.SOA TY.MB TY.MG TY.MR TY.NULL TY.WKS TY.PTR TY.HINFO TY.MINFO TY.MX TY.AXFR TY.ALL TY.XA CL.IN CL.CH CL.ALL AUTHOR MXPREF P NET DOM NETIBF NETOBF DOMIBF DOMOBF DLKBLK DLKNME NAMBLK DATBLK DATPTR SETUDP QUERYB QDCOUNT ANCOUNT NSCOUNT ARCOUNT TEMPBP TTLMIN TTLMAX FATAL

;Resource record format as stored in system cache.  Data follows name,
;unless (based on RRTYPE) it is stored in RRDLEN word.
RRTYPE←←0			;Type
RRCLAS←←1			;Class
RRTTL←←2			;Time to live (in seconds)
RRDLEN←←3			;Data length (in bytes), or immediate data
RRNAME←←4			;Start of name

;Resource record types
TY.A←←=1			;Internet address
TY.NS←←=2			;Name server
;TY.MD←←=3	(obsolete)	;Mail destination
;TY.MF←←=4	(obsolete)	;Mail forwarder
TY.CNAME←←=5			;Canonical name
TY.SOA←←=6			;Start of authority
TY.MB←←=7			;Mailbox
TY.MG←←=8			;Mail group
TY.MR←←=9			;Mail rename
TY.NULL←←=10			;Null
TY.WKS←←=11			;Well-known service
TY.PTR←←=12			;Pointer
TY.HINFO←←=13			;Host information
TY.MINFO←←=14			;Mail information
TY.MX←←=15			;Mail agent

;Query types (in addition to above)
TY.AXFR←←=252			;Zone of authority transfer
;TY.MAIB←←=253	(obsolete?)	;(MAILB) Mailbox-related records
;TY.MAIA←←=254	(obsolete)	;(MAILA) Mail agent records
TY.ALL←←=255			;All types
TY.XA←←<0,,-1>			;Inverse address kludge

;Resource record classes
CL.IN←←=1			;Internet
;CL.CS←←=2	(obsolete)	;CSNET
CL.CH←←=3			;Chaos

;Query classes (in addition to above)
CL.ALL←←=255			;All classes

AUTHOR:	0			;-1 to force authoritative answer
MXPREF:	0			;MX record preference value

BEGIN DOMSUB

P←17				;Guess

NET←←0				;Channel for network I/O
DOM←←1				;Channel for domain cache

NETIBF:	BLOCK 3			;Buffer headers
NETOBF:	BLOCK 3
DOMIBF:	BLOCK 3
DOMOBF:	BLOCK 3

DLKBLK:	BLOCK 100		;LOOKUP block
DLKNME:	BLOCK 1			;Pointer to end of name in DLKBLK
NAMBLK:	BLOCK 100		;Block for returned name
DATBLK:	BLOCK 100		;Block for returned data
DATPTR:	BLOCK 1			;Pointer used to fill DATBLK

SETUDP:	26			;MTAPE block for UDP parameters
	0			;Status bits
	0			;Local port
	0
	0
	=53			;Remote domain server port
	0			;Remote address

;Message header (12 bytes) for domain server query
QUERYB:	BYTE (8)0,0,1,0		;ID, flags (recursion desired)
	BYTE (8)0,1,0,0		;QDCOUNT, ANCOUNT
	BYTE (8)0,0,0,0		;NSCOUNT, ARCOUNT

QDCOUNT:0			;Storage for RDRESP
ANCOUNT:0
NSCOUNT:0
ARCOUNT:0
TEMPBP:	0

TTLMIN:	5			;Minimum TTL for records we give system
TTLMAX:	=5*=60	;(debugging)	;Maximum TTL for records we give system

FATAL:	OUTSTR [ASCIZ/Fatal error in domain lookup code.  Find a wizard.
/]
	JRST 4,FATAL
;⊗ SUCCES NICKNM SFTERR NAMERR GETDOM GETDO1 GETDO2 GETDO3 GETDO4 GETDMA GETINP GETIN1 GETIN2 GETIN3 GETIN4 GETIN5 GETIN6 GETIN7 GETIN8 CPOPJ2 CPOPJ1 CPOPJ

;Get domain information.  Calling sequence:
;
;	MOVE 0,[<addr or byte ptr to ASCIZ domain name>]
;	MOVE 1,[<query type>]
;	MOVE 2,[<query class>]
;	PUSHJ P,GETDOM
;	 <name error: requested information does not exist>
;	 <soft error: requested information not available>
;	 <nickname was given: AC 3 points to canonical name>
;	<success, 0 contains addr of domain name,
;		  1 contains type,
;		  2 contains class,
;		  3 contains data>
;
;Setting the flag AUTHOR non-0 will force sending a network query before
;checking the system's cache.
;
;After a successful return, to get further answers:
;
;	PUSHJ P,GETDMA
;	 <no more information>
;	<success, 0-3 contain same as above>
;
;To translate an Internet address to a domain name, call GETDOM with the
;address in AC 0 and TY.XA as the query type.  The returned data will be
;a type TY.A record for the address, i.e. AC 0 will point to the name.
;
;The returned domain name pointed to by AC 0 may differ in capitalization,
;otherwise it is expected to be the same as the query domain name.  The
;format of the data returned in AC 3 depends on the type in AC 1, as follows:
;
;  TY.A		AC 3 contains the 32-bit address.
;  TY.SOA	Not implemented
;  TY.NULL	Not implemented
;  TY.WKS	Not implemented
;  TY.HINFO	Not implemented
;  TY.MINFO	Not implemented
;  TY.MX	AC 3 points to an ASCIZ string for a host name,
;		location MXPREF contains the preference value
;  Others	AC 3 points to an ASCIZ string representing a domain name
;
;If the original name is found to be a nickname, you should call GETDOM
;again with the canonical name that was returned.  It is safe to copy AC 3
;into 0 (and reset the type in AC 1).
;
;A name error means a server told us the information does not exist for
;the requested domain.  A soft error means we could not get to a server,
;so retrying later may work.

;Return addresses restore ACs saved on entry and skip appropriately.
SUCCES:	AOS -1(P)		;Success
NICKNM:	AOS -1(P)		;Nickname
SFTERR:	AOS -1(P)		;Soft error
NAMERR:	POP P,4			;Name error
	POPJ P,

↑GETDOM:PUSH P,4		;Save some ACs
	;Prepare lookup block
	MOVEM 1,DLKBLK+RRTYPE
	MOVEM 2,DLKBLK+RRCLAS
	CAIN 1,TY.XA		;Inverse address query?
	JRST [	MOVEM 0,DLKBLK+RRDLEN	;Yes, store address
		JRST GETDO4]		;Skip over name parsing
	;Convert name argument from ASCIZ to domain format
	TLNN 0,-1		;Byte ptr?
	HRLI 0,440700		;No, make one
	MOVE 2,[POINT 8,DLKBLK+RRNAME]	;Destination byte ptr
GETDO1:	SETZ 4,			;Component length ← 0
	IBP 2			;Skip length byte
	MOVE 3,2		;Save ptr to length byte
GETDO2:	ILDB 1,0		;Get a byte
	CAIE 1,"."		;End of component?
	CAIN 1,0		;Or of string?
	JRST GETDO3		;Yes
	IDPB 1,2		;Copy to domain string
	AOJA 4,GETDO2

GETDO3:	DPB 4,3			;Store length byte
	JUMPN 1,GETDO1		;Continue unless end of string
	IDPB 1,2		;End with 0 length byte
	MOVEM 2,DLKNME		;Save ptr to end of name
GETDO4:	INIT DOM,0		;Open domain device
	 SIXBIT/DOM/
	 DOMOBF,,DOMIBF
	 PUSHJ P,FATAL
	SKIPN AUTHOR		;Force query if authority desired
	LOOKUP DOM,DLKBLK	;See if data in system
	 JRST [	PUSHJ P,SQUERY	;Not found, ask a server
		 JRST SFTERR	;No reply
		LOOKUP DOM,DLKBLK ;See if server added info for us
		 JRST SFTERR	;Nope, nothing there
		JRST .+1]
	;System has information, read it
	INBUF DOM,1		;Prepare to read
	PUSHJ P,GETINP		;Read RR, return data in ACs
	 JRST NAMERR		;Name error
	 PUSHJ P,FATAL		;First input failed (can't happen)
	CAIN 1,TY.CNAME
	JRST NICKNM		;Return a nickname
	JRST SUCCES		;Return an ordinary answer

↑GETDMA:PUSH P,4
	PUSHJ P,GETINP
	 JFCL			;Name error
	 SOS -1(P)		;No input data
	POP P,4
	JRST CPOPJ1

;GETINP reads one input record from the system.  Calling sequence:
;
;	PUSHJ P,GETINP
;	 <name error>
;	 <no data available>
;	<success, ACs 0-3 contain return data>
;
;Return data is same as for GETDOM.

GETINP:	IN DOM,			;Get the data
	 JRST .+2
	JRST CPOPJ1		;Input failed, no more data
	MOVE 1,DOMIBF		;Buffer pointer
	ADDI 1,2		;Point to resource record
	SKIPGE RRDLEN(1)	;Name error?
	POPJ P,			;Yes, return failure
	;Copy the name in the reply into NAMBLK.
	MOVE 2,[POINT 8,RRNAME(1)]
	MOVE 0,[POINT 7,NAMBLK]
GETIN1:	ILDB 3,2		;Get a length byte
	JUMPE 3,GETIN3
GETIN2:	ILDB 4,2		;Get a data byte
	IDPB 4,0		;Copy it
	SOJG 3,GETIN2
	MOVEI 4,"."		;End a name component
	IDPB 4,0
	JRST GETIN1

GETIN3:	DPB 3,0			;Replace last "." with null
	MOVE 0,RRTYPE(1)
	CAIN 0,TY.A		;Internet address?
	JRST [	MOVE 3,RRDLEN(1);Yes, get it from length field
		JRST GETIN8]	;Return it
;Assume data is a domain name, and copy it into an ASCIZ string.
GETIN4:	MOVE 0,[POINT 7,DATBLK]
GETIN5:	ILDB 3,2		;Get first length byte
	JUMPE 3,GETIN7
GETIN6:	ILDB 4,2		;Get a character
	IDPB 4,0		;Copy it
	SOJG 3,GETIN6
	MOVEI 4,"."		;End a name component
	IDPB 4,0
	JRST GETIN5

GETIN7:	DPB 3,0			;Replace last "." with null
	MOVEI 3,DATBLK
GETIN8:	MOVE 2,RRCLAS(1)	;Set up ACs 0-2
	MOVE 1,RRTYPE(1)
	MOVEI 0,NAMBLK
CPOPJ2:	AOS (P)			;Skip twice for success
CPOPJ1:	AOS (P)
CPOPJ:	POPJ P,
;⊗ SQUERY SQUPTD SQUPD1 SQUPTR SQUPT1 SQUER0 SQUER1 SQUER2 UNKERR FMTERR SRVFAI NOTIMP REFUSE RDRESP SKIPQR SKIPQ1 RDANS RDAUTH RDADDL RDRRET NAMERX

;Send query to domain server.  Returns non-skip if no response,
;skip if response received.

SQUERY:	INIT NET,0		;Open network channel
	 SIXBIT/IMP/
	 NETOBF,,NETIBF
	 PUSHJ P,FATAL
	INBUF NET,1		;Allocate I/O buffers
	OUTBUF NET,1
	MOVEI 1,=8		;Set byte size in buffer headers
	DPB 1,[POINT 6,NETIBF+1,11]
	DPB 1,[POINT 6,NETOBF+1,11]
	MTAPE NET,[17 ↔ 0,,001200]	;Set 20-second input timeout
	SETOM SETUDP+2		;Let system allocate local port
;;	MOVE 1,[4415,,200012]	;(Argus)
;;	MOVE 1,[4402,,000057]	;(Labrea)
	MOVE 1,[4405,,200024]	;(Portia)
	MOVEM 1,SETUDP+6	;Set server address
	MTAPE NET,SETUDP
	OUTPUT NET,		;Set up output buffer header
	MOVE 1,SETUDP+2		;Get the port we were assigned
	DPB 1,[POINT 16,QUERYB,15]	;Use it as the ID byte
	MOVE 1,NETOBF		;Output buffer pointer
	ADDI 1,2		;First data word
	HRLI 1,QUERYB		;Set up to copy query block
	BLT 1,2(1)		;Copy three header words
	MOVE 0,DLKBLK+RRTYPE
	CAIN 0,TY.XA		;Inverse address query?
	JRST SQUPTR		;Yes, make up a PTR query
	HRLI 1,DLKBLK+RRNAME	;Start of name to copy
	MOVE 2,DLKNME		;Ptr to last byte in source
	ADDI 2,-DLKBLK-RRNAME(1);Ptr to last byte in destination
	BLT 1,(2)		;Copy name
	MOVEM 2,NETOBF+1	;Store ptr in buffer header
	MOVE 1,DLKBLK+RRTYPE	;Copy type from lookup block
	JRST SQUER0		;Finish header and send query

;Subroutine to convert a byte to decimal ASCII and store in query,
;preceded by a length byte.
SQUPTD:	MOVEI 3,1		;Figure out the length
	CAIL 2,=10
	MOVEI 3,2
	CAIL 2,=100
	MOVEI 3,3
	IDPB 3,NETOBF+1
SQUPD1:	IDIVI 2,=10		;Do the usual decimal conversion
	PUSH P,3
	JUMPE 2,.+2
	PUSHJ P,SQUPD1
	POP P,3
	ADDI 3,"0"
	IDPB 3,NETOBF+1
	POPJ P,

;Here to make up and send a PTR query for an inverse address lookup.
SQUPTR:	HRLI 1,441000		;Update output byte ptr
	MOVEM 1,NETOBF+1
	LDB 2,[POINT 8,DLKBLK+RRDLEN,35]
	PUSHJ P,SQUPTD		;Convert 4th byte to decimal
	LDB 2,[POINT 8,DLKBLK+RRDLEN,27]
	PUSHJ P,SQUPTD		;Convert 3rd byte to decimal
	LDB 2,[POINT 8,DLKBLK+RRDLEN,19]
	PUSHJ P,SQUPTD		;Convert 2nd byte to decimal
	LDB 2,[POINT 8,DLKBLK+RRDLEN,11]
	PUSHJ P,SQUPTD		;Convert 1st byte to decimal
	MOVE 2,[POINT 7,[ASCIZ/πIN-ADDR∧ARPA/]]
SQUPT1:	ILDB 3,2		;Follow with ".IN-ADDR.ARPA"
	IDPB 3,NETOBF+1
	JUMPN 3,SQUPT1
	MOVEI 1,TY.PTR		;Query type
SQUER0:	ROT 1,-=8
	IDPB 1,NETOBF+1
	ROT 1,=8
	IDPB 1,NETOBF+1
	MOVE 1,DLKBLK+RRCLAS	;Copy class
	ROT 1,-=8
	IDPB 1,NETOBF+1
	ROT 1,=8
	IDPB 1,NETOBF+1
	OUTSTR [ASCIZ/Domain query ... /]
	OUT NET,		;Send the query
	 JRST SQUER1
	PUSHJ P,FATAL		;Some kind of error

SQUER1:	IN NET,			;Wait for response
	 JRST SQUER2
	RELEAS NET,		;Time out or other error
	POPJ P,

SQUER2:	OUTBUF DOM,1		;Prepare to give RRs to system
	OUTPUT DOM,		;Set up first output buffer
	MOVEI 1,=16		;Temporarily change byte size
	DPB 1,[POINT 6,NETIBF+1,11]
	ILDB 1,NETIBF+1		;ID field
	;Don't bother checking ID for now.  What would we do if it was wrong?
	ILDB 1,NETIBF+1		;Opcode and Flags
	ILDB 2,NETIBF+1		;Read and store other header fields
	MOVEM 2,QDCOUNT
	ILDB 2,NETIBF+1
	MOVEM 2,ANCOUNT
	ILDB 2,NETIBF+1
	MOVEM 2,NSCOUNT
	ILDB 2,NETIBF+1
	MOVEM 2,ARCOUNT
	MOVEI 2,=8		;Back to 8-bit bytes
	DPB 2,[POINT 6,NETIBF+1,11]
	;Various flags in 1 should be checked.
	ANDI 1,17		;Dispatch on RCODE field
	CAILE 1,5		;Highest reply code we know
	JRST UNKERR
	JRST @[	RDRESP		;No error, read response
		FMTERR		;Format error
		SRVFAI		;Server failure
		NAMERX		;Name error
		NOTIMP		;Not implemented
		REFUSE](1)	;Refused

UNKERR:	OUTSTR [ASCIZ/Unknown error code.
/]
	PUSHJ P,FATAL

FMTERR:	OUTSTR [ASCIZ/Format error.
/]
	PUSHJ P,FATAL

SRVFAI:	OUTSTR [ASCIZ/Server failure.
/]
	PUSHJ P,FATAL

NOTIMP:	OUTSTR [ASCIZ/Not implemented error.
/]
	PUSHJ P,FATAL

REFUSE:	OUTSTR [ASCIZ/Query refused.
/]
	PUSHJ P,FATAL

;Read response from server, and store all of the resource records in the
;response into the system cache.  These will then be used to answer the
;original query.

;Skip over question section.  (We assume it matches the query.)
;QDCOUNT should always be 1, but we don't assume it.
RDRESP:	SOSGE QDCOUNT		;Skip unless done question section
	JRST RDANS
SKIPQR:	ILDB 1,NETIBF+1		;Get a length byte
	JUMPE 1,SKIPQ1		;Jump at end of QNAME
	TRZE 1,300		;Compressed format?
	JRST [	IBP NETIBF+1	;Yes, this makes it easy to skip over!
		JRST SKIPQ1]
	IBP NETIBF+1		;Skip over that many bytes
	SOJG 1,.-1
	JRST SKIPQR

SKIPQ1:	AOS NETIBF+1		;Skip 4 bytes (type and class)
	JRST RDRESP

;Read resource records from answer section.
RDANS:	SOSGE ANCOUNT		;Skip unless done answer section
	JRST RDAUTH
	PUSHJ P,READRR		;Read a resource record
	JRST RDANS

;Read resource records from authority section.
RDAUTH:	SOSGE ANCOUNT		;Skip unless done authority section
	JRST RDADDL
	PUSHJ P,READRR		;Read a resource record
	JRST RDAUTH

;Read resource records from additional section.
RDADDL:	SOSGE ANCOUNT		;Skip unless done additional section
	JRST RDRRET
	PUSHJ P,READRR		;Read a resource record
	JRST RDADDL

RDRRET:	RELEAS NET,
	JRST CPOPJ1		;Return from SQUERY

;Name error means the name we sent it is no good.  Make up a record
;to indicate this and store it in the system cache.

NAMERX:	SETOM DLKBLK+RRDLEN	;Indicate error
	MOVE 1,TTLMAX		;Give it maximum TTL
	MOVEM 1,DLKBLK+RRTTL
	MOVE 1,DOMOBF		;Output buffer pointer
	ADDI 1,2		;First data word
	HRLI 1,DLKBLK		;Addr of our query
	MOVE 2,DLKNME		;Ptr to last byte in query
	ADDI 2,-DLKBLK(1)	;Ptr to last byte in destination
	BLT 1,(2)		;Copy the query
	MOVEM 2,DOMOBF+1	;Store final byte ptr for output
	OUTPUT DOM,		;Send record to system cache
	JRST RDRRET
;⊗ READRR READR1 READR2 READR3 READR4 RDDISP RD.MAX RD.ILL RD.SOA RD.NUL RD.WKS RD.HIN RD.MIN RD.A RDOUTP RD.PTR RD.MX RD.DOM RD.DO1 RD.DO2 READC0 READCP READC1 READC2

;Read a resource record and enter it in the system's cache.

;Macro to copy NUM bytes from input buffer to a word, right-aligned.
DEFINE COPYB(NUM,WORD)<
	SETZM WORD
	MOVE 1,[POINT 8,WORD,<35-8*NUM>]
REPEAT NUM,<
	ILDB 0,NETIBF+1
	IDPB 0,1
>;REPEAT NUM
>;DEFINE COPYB

READRR:	MOVE 3,DOMOBF		;Pointer to output buffer
	ADDI 3,2		;Start of data
	MOVE 2,3		;Make a copy for byte ptr
	ADD 2,[POINT 8,RRNAME]	;Start of name
	MOVEM 2,DOMOBF+1	;Store ptr in buffer hdr
READR1:	ILDB 1,NETIBF+1		;Get a length byte
	TRZE 1,300		;Compressed format?
	JRST READR3		;Yes
	IDPB 1,DOMOBF+1		;Copy length byte
	JUMPE 1,READR4		;Jump when done
READR2:	ILDB 0,NETIBF+1		;Get a byte
	IDPB 0,DOMOBF+1		;Copy it
	SOJG 1,READR2
	JRST READR1

READR3:	PUSHJ P,READCP		;Read compressed name
READR4:	COPYB(2,<RRTYPE(3)>)	;Copy type
	COPYB(2,<RRCLAS(3)>)	;Copy class
	COPYB(4,<RRTTL(3)>)	;Copy time to live
	MOVE 0,RRTTL(3)		;Examine TTL
	CAMGE 0,TTLMIN		;Force into desired range
	MOVE 0,TTLMIN
	CAMLE 0,TTLMAX
	MOVE 0,TTLMAX
	MOVEM 0,RRTTL(3)
	COPYB(2,<RRDLEN(3)>)	;Copy data length
	MOVE 1,RRTYPE(3)	;Examine type
	CAIL 1,RD.MAX		;Bounds check
	PUSHJ P,FATAL
	JRST @RDDISP(1)		;Dispatch based on type

RDDISP:	RD.ILL
	RD.A			;TY.A
	RD.DOM			;TY.NS
	RD.DOM			;TY.MD (obsolete)
	RD.DOM			;TY.MF (obsolete)
	RD.DOM			;TY.CNAME
	RD.SOA			;TY.SOA
	RD.DOM			;TY.MB
	RD.DOM			;TY.MG
	RD.DOM			;TY.MR
	RD.NULL			;TY.NULL
	RD.WKS			;TY.WKS
	RD.PTR			;TY.PTR
	RD.HINFO		;TY.HINFO
	RD.MINFO		;TY.MINFO
	RD.MX			;TY.MX
RD.MAX←←.-RDDISP

RD.ILL:	PUSHJ P,FATAL		;Type 0 doesn't exist

RD.SOA:	PUSHJ P,FATAL		;These need to be written
RD.NUL:	PUSHJ P,FATAL
RD.WKS:	PUSHJ P,FATAL
RD.HIN:	PUSHJ P,FATAL
RD.MIN:	PUSHJ P,FATAL

RD.A:	COPYB(4,<RRDLEN(3)>)	;Copy address into length word
RDOUTP:	OUTPUT DOM,		;Send record to system cache
	POPJ P,			;Return from READRR

RD.PTR:	MOVE 0,DLKBLK+RRTYPE
	CAIE 0,TY.XA		;Yes, was this for inv addr query?
	JRST RD.DOM		;No, treat as an ordinary record
	;Construct an address record from the PTR data.
	MOVEI 0,TY.A		;Change type to address
	MOVEM 0,RRTYPE(3)
	MOVE 0,DLKBLK+RRDLEN	;Get address from query
	MOVEM 0,RRDLEN(3)
	MOVE 2,3		;Copy data into name field
	ADD 2,[POINT 8,RRNAME]
	MOVEM 2,DOMOBF+1
	JRST RD.DOM

RD.MX:	COPYB(2,MXPREF)		;Copy preference value
	;(fall into RD.DOM)
;Copy a domain name into the output buffer.
RD.DOM:	ILDB 1,NETIBF+1		;Get a length byte
	TRZE 1,300		;Compressed format?
	JRST RD.DO2		;Yes
	IDPB 1,DOMOBF+1		;Copy length byte
	JUMPE 1,RDOUTP		;Jump when done
RD.DO1:	ILDB 0,NETIBF+1		;Get a byte
	IDPB 0,DOMOBF+1		;Copy it
	SOJG 1,RD.DO1
	JRST RD.DOM

RD.DO2:	PUSHJ P,READCP		;Read compressed name
	JRST RDOUTP

;Follow a compressed name pointer to read and copy the rest of a name.
READC0:	ILDB 0,TEMPBP		;Here when multiple pointers
	JRST .+2
READCP:	ILDB 0,NETIBF+1		;Get the rest of the pointer
	LSH 1,=8
	ADDI 0,(1)		;Compute the byte offset
	IDIVI 0,4		;0←word offset, 1←byte
	ADD 0,[	POINT 8,2	;Make a byte ptr
		POINT 8,2,7	;(2 is for data offset from NETIBF)
		POINT 8,2,15
		POINT 8,2,23](1)
	ADD 0,NETIBF		;Relocate to input message
	MOVEM 0,TEMPBP
READC1:	ILDB 1,TEMPBP		;Get a length byte
	TRZE 1,300		;Another pointer?
	JRST READC0		;Yes, what complexity!
	IDPB 1,DOMOBF+1		;Copy length byte
	JUMPE 1,CPOPJ		;Return when done
READC2:	ILDB 0,TEMPBP		;Get a byte
	IDPB 0,DOMOBF+1		;Copy it
	SOJG 1,READC2
	JRST READC1

BEND DOMSUB