;;;                         -*- Midas -*-

NRTVER==.IFNM2

IFNDEF $$FTP,$$FTP==0	; Do not assemble FTP routines anymore.
IFNDEF $$TCP,$$TCP==1	; Normally assemble TCP routines.
IFNDEF $$DQ,$$DQ==0	; Normally use host table, not DQ device.
IFNDEF $$450,$$450==0	; Disable special casing of SMTP reply code 450
comment|   
         This file contains the network hacking code of COMSAT.

Routines called by COMSAT:

	HANLYZ - Convert host names to host numbers.
	NETIM  - Queries a network time server.
	NETPTH - For the host in N, find a proper network address/protocol.
	NETICP - Makes network connection.
	NTDISC - Disconnects.

NETSND calls these routines:

	NTXRSQ - Find XRCP scheme that the host prefers.
	NHMLTX and NHITS see if a host is a Multics or an ITS.

Generic protocol routines called by NETSND to dispatch 
to protocol-specific handlers:

	NTMBEG - Readies server for message txt to a rcpt.
	NTXRCP - Specifies rcpt to host.
	NTMSND - Sends the message text.
	NTMEND - Wraps up message and leaves connection ready for next command.

Non-skip indicates either:
	1] An IOC error (connections broken),
	2] An interaction failure which is probably temporary (go away)
	3] An interaction failure which is probably permanent (no such user)
 Generally the following values are returned on error:
	A/ MR$xxx error code, one of:
		MR$PEH - Permanent Error for Host (this msg will never win)
		MR$TEH - Temporary Error for Host (host died or conns wedged)
		MR$PER - Permanent Error for Rcpt (this rcpt will never win)
		MR$TER - Temporary Error for Rcpt (some glitch for this rcpt)
	B/ if non-zero, SLP to an error string


 |

BVAR
	; Global XRCP-hacking vars
XRSQQ:	0	; -1 if have tried asking host its preference.
XRSQPS:	0	; Preferred scheme of host. 0 none, -1 R, +1 T.
XRSQRS:	0	; Scheme actually in effect over net channels.
XRSQS:	0	; Scheme selected by sending rtns. (NETSND only)

NSMSAR:	BLOCK $ARSIZ ; Area for storage of SMTP transaction script
		     ; Always contains results of last SMTP connection.
IFN $$DQ,{	     ; If doing domain code
DQBUF:	BLOCK 63     ; Random buffer for string hacking (ugh)
		     ; Size determined by need to hold 255. char hostnames,
		     ; per RFC 883.
DQLUZ:	0	     ; Fence to detect horrible lossage in NHOPSY.
}		     ; End of domain specific storage.
EVAR


SUBTTL	Protocol Definitions

BVAR
NDHOST:	0	; Host # we're supposedly talking to. 
		; Should be same  as ac N, or an error has happened.
NTSITE:	0	; Host # NETPTH thinks we should connect to - becomes NTHOST.
NTHOST:	0	; Host # really connected to, usually same as NDHOST.
NTTYPE:	0	; Current net transport/protocol available.
NTRTSW:	0	; -1 if relaying mail; tack "@host" onto rcpt names.

;;; Variables for forcing certain kludgey routing.

KLGFRM:	-1	; Network address from
KLGTO:	-1	; Network address to
KLGNET:	-1	; Don't use this network
KLGGAT:	-1	; Instead, use this host as gateway
TCPGAT:	0	; If nonzero, gateway all non-chaos mail through here.

;;; For a Chaosnet-only host, set only TCPGAT to Chaos addr of the relay host.
;;; If the host thinks it is on the ARPAnet, but isn't really, also set OWNHS2.
;;; You should not generally have to set the other kludge switches.

;;; PROTCL macro defines the routines appropriate for each protocol.

PR$NAM==:0		;Name of protocol
PR$ICP==:1		;Connection routine
PR$MIN==:2		;Transaction initializer
PR$BEG==:3		;Message command sender
PR$RCP==:4		;Message rcpt sender
PR$SND==:5		;Message text sender
PR$END==:6		;Message ender
PR$CHK==:7		;Error classifier
NETPR:	BLOCK 4*8.
EVAR

%%%PR$==NETPR
DEFINE	PRODEF SYM,NUM,&NAME&,ICPRTN,MINRTN,BEGRTN,RCPRTN,SNDRTN,ENDRTN,CHKRTN
   SYM==:NUM
IF1,{ PRINTX \Including protocol NAME
\ }
   %%%TLC==.
   LOC %%%PR$
	[ASCIZ NAME]
	ICPRTN ? MINRTN ? BEGRTN ? RCPRTN ? SNDRTN ? ENDRTN ? CHKRTN
   %%%PR$==%%%PR$+8.
   LOC %%%TLC
TERMIN

NT$LCL==-1	; Local mail "protocol" (don't change).

PRODEF	NT$ISM,0,"TCP-SMTP",NSMICP,NSMINI,NSMBEG,NSMRCP,NSMDAT,NSMDON,NTERSM
PRODEF	NT$CHM,1,"CHAOS-MAIL",NKMICP,NKMINI,NKMBEG,NKXRCP,NKSND,NKEND,NTERSM
PRODEF	NT$CHS,2,"CHAOS-SEND",POPJ1,POPJ1,NKSEND,POPJ1,NKSND,NKEND,NTERSM
PRODEF  NT$CSM,3,"CHAOS-SMTP",NKSICP,NSMINI,NSMBEG,NSMRCP,NSMDAT,NSMDON,NTERSM
IFN $$FTP,[
PRODEF NT$FTP,4,"NCP-FTP",NNFICP,POPJ1,NNFBEG,POPJ1,NNFRCP,NNFSND,NNFEND,NTERFT
];$$FTP

DEFINE PROTCL AC,PR
	MOVE AC,NTTYPE
	IMULI AC,8.
	MOVE AC,NETPR+PR(AC)
TERMIN


SUBTTL	Network Service Paths

; NETPTH - Find path to network service.
; Takes desired host in N.  SORMSW must be set up.
; Skips if re-connection needed; non-skip means already connected properly. 
; Sets up NTTYPE, NTSITE, and NTRTSW.
; (Host # zero in N means ourselves.)

NETPTH:	PUSHAE P,[A,B,N]
	SETZM NTRTSW		; Reset routing flag.
	JUMPE N,[ SETOM NTTYPE ? JRST NETP99 ]
	RESOLV"GETNET A,N	; Find which network the dest site is on.
	CAMN A,[RESOLV"NW%CHS]	; Is host on the Chaosnet?
	 JRST NETP10		;  Yes, good - we always have Chaosnet.
	SKIPE B,TCPGAT		; Internet relay required?
	 JRST [	MOVE N,B	;  Yes, will really connect thru relay.
		JRST NETP60 ]
NETP10:	CAME N,NTHOST		; Are we already connected?
	 JRST NETP20		;   No.
	SKIPN SORMSW		; If we are mailing (not sending)
	 JRST [	MOVE N,NTHOST	;  Current connection is usable.
		JRST NETP99 ]
	CAME A,[RESOLV"NW%CHS]	; Sending over Chaosnet?
	 JRST [	MOVE N,NTHOST	;  No, so protocols are fairly compatible.
		JRST NETP99 ]		
	;; New connection required.
NETP20:	CAMN N,KLGFRM		; If specially patched for this host
	 JRST [	MOVE N,KLGTO	;  use specific relay address.
		JRST NETP60 ]
	CAME A,[RESOLV"NW%CHS]	; Chaosnet is the fastest connection,
	 CAME A,KLGNET
	  JRST NETP80
	MOVE N,KLGTO		; Use specific relay address.
NETP60:	SETOM NTRTSW		; Say we are relaying.
	JRST NETP90
	;; No monkey business, just find preferred host address.
NETP80:	MOVE A,[RESOLV"NW%CHS]	; Chaosnet is fastest way to go.
	CALL NETALK		; Find address for host on Chaosnet.
	 CAIA
	  MOVE N,B		; N gets best host address.
NETP90:	MOVEI B,NT$ISM		; Default transport type is IP-TCP/SMTP.
	RESOLV"GETNET A,N	; Find network.
	CAMN A,[RESOLV"NW%CHS]	; If site IS on the Chaosnet
	 MOVEI B,NT$CSM		;  Use CHAOS/SMTP instead.
NETP96:	MOVEM B,NTTYPE		; Set up NTTYPE.
	AOS -3(P)		; Say that reconnection is needed.
NETP99:	MOVEM N,NTSITE		; Store actual address to be used.
	POPAE P,[N,B,A]
	RET

; NETALK - Network address lookup
; Look up host address for host in N on network in A.
; Skip if found with new address in B.

NETALK:
IFN $$DQ,{			; Domain version?  This is gonna hurt...
	RESOLV"GETNET B,N	; Get network for this address
	CAME A,B		; Already have right net?
	 JRST NETAL0		; No, onwards
	MOVE B,N		; Yes, just return this address
	AOS (P)			; Winningly
	RET
NETAL0:
IFN 0,{				; Questionable painkiller code
	CAME A,[RESOLV"NW%CHS]	; Trying to get a Chaosnet address?
	 RET			; Nope, forget it, waste of time.
}				; End of questionable painkiller code
	PUSH P,A		; Save network number
	MOVE A,[440700,,DQBUF]	; Stuff hostname string here
	MOVE B,N		; Host number
	CALL RESOLV"HSTSRC	; Look it up
	 JRST NETAL9		; Punt
	MOVE A,[440700,,DQBUF]	; Probably already there, so what?
	MOVE B,(P)		; A points at name, get network number
	CALL RESOLV"HSTADN	; Look up net specific address
	 JRST NETAL9		; Lost
	MOVE B,A		; Won, address in B
	AOS -1(P)		; Skip return
NETAL9:	POP P,A
	RET
}				; End of domain version
.ELSE {				; Non-domain version, still support this
	PUSHAE P,[A,C,D,E]
	MOVE B,N		; Get host #.
	MOVE E,A		; Get network #.
	CALL RESOLV"HSTSRC	; Look up in HOSTS3 database.
	 JRST NETAL9
	HRRZ C,(D)		; Get rel addr of ADDRESS entry list.
	JUMPE C,NETAL9
NETAL1:	ADD C,RESOLV"HSTADR	; Derelativize.
	RESOLV"GETNET B,(C)	; Get network for this address entry.
	CAMN B,E		; Is this the one?
	 JRST [ MOVE B,(C)	;  Yes!
		AOS -4(P)
		JRST NETAL9 ]
	HRRZ C,RESOLV"ADRCDR(C)	; CDR down ADDRESS list.
	JUMPN C,NETAL1
NETAL9:	POPAE P,[E,D,C,A]
	RET
}				; .ELSE $$DQ


SUBTTL	Error handling routines


BVAR  ; leave these as variable so we can patch COMSAT -- Gumby
ICPTMO: 30.	; Timeout ICP after 30 sec. total
FINTMO: 60.	; timeout for finish
BYETMO:	30.	; timeoout for SMTP QUIT
DEFTMO: 300.	; default timeout.
EVAR

; NETRAP - Network Error Trap.  Sets up stuff so that
;	a timeout or IOC error will restore ACs and PDL and
;	jump to a specified location.
;	A - # seconds of timeout.
; Returns .+2 after setting up.
; RETURNS .+1 IF ERROR TRAP OCCURS!!!
; BEWARE!!! THIS ROUTINE MUNGS THE PDL!!!  Call NERESET before
;	attempting a non-error return!!  
; Either error trap will call NTDISC before returning, so that
; any possible server in a screwed-up state does not get reused.

LVAR	NTRPLV:	0	; Holds ptr to trap frame on PDL.
LVAR	NTRTMO:	0	; Value of timeout in 60th's of sec.
NE$FLN==:<5+15>		; Frame length; 5 vars plus 15 ACs.

NETRAP:	PUSH P,NTRPLV		; Save any previous frame ptr.
	PUSH P,NTRTMO		; Save old timeout value
	PUSH P,NTIOCV		; Save old IOC vector
	PUSH P,RLTVEC		; and old REALT vector.
	IMULI A,60.		; Find timeout in REALT units (60ths of secs)
	MOVEM A,NTRTMO		; Set it.
IFN A-1,.ERR NETRAP loses, code assumes A=1.
	MOVEI A,1(P)
	HRLI A,2
	ADD P,[15,,15]		; Make room for ACs 2-16
	BLT A,(P)		; Move ACs 2-16 onto PDL.
	PUSH P,[SIXBIT /EFRAME/]	; Check word.
	CLKOFF			; Avoid timing errors.
	MOVEM P,NTRPLV		; Frame completed, now point to it!
	MOVEI A,NTRRLT		; Set up RLT vector.
	MOVEM A,RLTVEC
	MOVEI A,NTRIOC		; Set up IOC vector.
	MOVEM A,NTIOCV
	CLKSET NTRTMO		; Set timeout going.
	CLKON			; and enable timeout interrupt.
	MOVE A,-NE$FLN(P)	; Get back return addr
	JRST 1(A)		; Do skip return!

LVAR NTIOCV:	0	; Net IOC error vector.  Holds addr of where to go
			; when a network IOC error happens.  Set by NETRAP.

	;; Come here for any net-channel IOC error.
NTRIOC:	CALL NERZAP		; Restore world
	CALL NTDIS1		; Thoroughly break connections
	MOVSI A,NCE$IO		; Furnish code for "IOC error"
	RET			; and dispatch to NETRAP return.


NTRRLT:	CALL NERZAP		; Restore the world
	CALL NTDIS1		; Thoroughly break connections
	MOVSI A,NCE$TO		; Furnish code for "Timeout"
	RET			; and dispatch to NETRAP return.


; NERESET - Called in order to flush a trap frame from the PDL.
;	Turns off clock and restores PDL to state as of NETRAP,
;	but doesn't restore any ACs!!
;	Contrives to return "normally".
; NERZAP - similar, but DOES restore ACs, and leaves NETRAP
;	return address on stack - a POPJ will dispatch to the
;	location immediately after the NERTRAP call.
; Both clobber A.

NERESET:
	CLKOFF			; Turn off clock before messing with PDL.
	SKIPN A,NTRPLV
	 JSR AUTPSY
	MOVE A,(A)		; Get last wd on purported frame
	CAME A,[SIXBIT /EFRAME/]	; Make sure a frame is there!
	 JSR AUTPSY
	POP P,A			; Yes, so get return addr
	MOVE P,NTRPLV		; Restore PDL ptr to frame level
	SUB P,[16,,16]		; Pop off check-word plus 15 saved ACs.
	POPAE P,[RLTVEC,NTIOCV,NTRTMO,NTRPLV]	; Restore various stuff.
	SUB P,[1,,1]		; Flush old NETRAP return addr.
	JRST (A)		; Return to caller!

NERZAP:	CLKOFF			; Turn off clock before messing with PDL.
	SKIPN A,NTRPLV
	 JSR AUTPSY
	MOVE A,(A)		; Get last wd on purported frame
	CAME A,[SIXBIT /EFRAME/]	; Make sure a frame is there!
	 JSR AUTPSY
	POP P,A			; Save return addr to caller.
	MOVE P,NTRPLV		; Restore PDL ptr to frame level
	MOVSI 16,-<<15-1>+1>(P)	; Set up for restoring 15 ACs (2-16)
				; Note extra check wd at top of stack.
	HRRI 16,2
	BLT 16,16		; Move the words!
	SUB P,[16,,16]		; Flush ACs and check-word from stack.
	POPAE P,[RLTVEC,NTIOCV,NTRTMO,NTRPLV]	; Restore various stuff.
	JRST (A)		; Return to caller.
	; NOTE CAREFULLY that (P) now contains the return
	; addr for the NETRAP invocation; a POPJ will dispatch!


SUBTTL	Initial Connection Protocols

;  NETICP -  Performs ICP to foreign host's server.
;	N - Host # to connect to.
; Sets up NETI and NETO channels.
; Skips if success.
;	NTHOST - set to host # connected to. (-1 if failure).

NCETAB:	OFFSET -.
 NCE$GR::	[ASCIZ /Bad Greeting/]
 NCE$HR::	[ASCIZ /Bad init reply/]
 NCE$TO::	[ASCIZ /Timeout/]
 NCE$IO::	[ASCIZ /IOC error/]
 NCE$SY::	[ASCIZ /Syscal err/]
 NCE$CS::	[ASCIZ /Bad state/]
 NCE$I2::	[ASCIZ /NCP 2 conn state/]
 NCE$I3::	[ASCIZ /NCP 3 conn state/]
 NCE$NO::	[ASCIZ /Refused/]
	OFFSET 0

NETICP:	PUSHAE P,[A,B]
NETIC1:	MOVE U1,DEBUG
	CAMN U1,[-2]
	 JRST [	STAT (,("N="),O(N),(" NTHOST="),O(NTHOST),(" NTSITE="),O(NTSITE),(" NTTYPE="),O(NTTYPE))
		JRST .+1 ]
	SETOM NDHOST		; Zap "current host", about to perform new ICP.
	SETZM XRSQQ		; Also zap whether host has been asked re XRCP.
	SETZM NTCCON		; Zap Chaosnet reconnect flag
	SETZM NTRBUF		; Zap #$%^&&^%$ reply buffer
	MOVE A,NTSITE		; Find host we should really use.
	MOVEM A,NTHOST		; Set it to be the current host.
	STAT (,("  ICP-"),RABR,HST(N))
	PROTCL A,PR$NAM
	CSTAT (,SP,LPAR,TZ(@A),FRC)
	SKIPE NTRTSW
	 JRST [	CSTAT (,(" via "),HST(NTHOST))
		JRST .+1 ]
	MOVE A,ICPTMO		; Get # secs of timeout for ICP.
	CALL NETRAP		; Set up error traps.
	 JRST NETIC2		; If timeout or IOC trap sprung, handle error.
	PROTCL A,PR$ICP
	CALL (A)		; Call it.
	 CAIA
	 JRST [	CALL NERESET	;    Won -  flush err trap.
		CSTAT (,RPAR)	;    
		MOVEM N,NDHOST	;    Say connected to this host.
		POP P,B		;    Skip return.
		JRST POPAJ1 ]
	MOVE B,A		; ICP Lost!
	CALL NERESET		;  Reset the trap.
	MOVE A,B		;  Recover the error code.
NETIC2:	MOVEM A,NTERRC'		; Come here if error trap hit.
	CSTAT (,("="),CALL(NTICPE),RPAR,FRC)
	CALL NTDISC		; Disconnect if not already disconnected.

	;; **** Hack: Try CHAOS if Internet is losing. *****
	;; **** Also: Use MAIL if CHAOS/SMTP loses.    *****
	;; 	[I'm not sure what the first part does anymore,
	;;	 given that we prefer CHAOS addresses already].
	MOVE A,NTTYPE		; Examine current protocol type.
	CAIN A,NT$CSM		; CHAOS/SMTP?
	 JRST [	MOVEI A,NT$CHM	; Yeah, switch to CHAOS/MAIL
		SKIPE SORMSW	; or CHAOS/SEND
	 	 MOVEI A,NT$CHS	; as appropriate.
		MOVEM A,NTTYPE
		JRST NETIC1 ]	; Start over....
	CAIN A,NT$CHS		; If was CHAOS-SEND
	 JRST NETIC9		;   There is no alternate path.
	CAIN A,NT$ISM		; Did we already try the Chaosnet?
	 JRST [ MOVE A,[RESOLV"NW%CHS]
		CALL NETALK	; Try Chaosnet address.
		 JRST POPBAJ
		MOVE N,B
		MOVEM N,NTSITE
		MOVEM N,NTHOST
		MOVEI A,NT$CHM
		MOVEM A,NTTYPE
		JRST NETIC1 ]
NETIC9:	JRST POPBAJ		; Already tried everything and lost.

; NTICPE - Output error message for ICP error value in A.
; Clobbers A, B.

NTICPE:	HLRZ B,A
	OUT(,TZ$(NCETAB(B)))
	CAIE B,NCE$GR		; If problem is bad reply, show it.
	 CAIN B,NCE$HR
	  JRST [OUT(,(|="|),CALL(NTRSHO),(|"|))
		RET]
	HRRES A
	SKIPE A
	 OUTCAL(,(","))
	JUMPL A,[MOVMS A
		OUT(,D(A))
		RET]
	CAIE A,
	 OUTCAL(,ERR(A))
	RET


;;; Disconnecting

; NTDISC - closes telnet connections to remote server
; NKDISC is entry for CHAOS, which has to pretend that conns are still open.
;   (I wonder if it is a bug if this these rtns are called 
;    with NETO closed, which has caused IOC errs.)

NTDISC:	MOVE U1,NTHOST		; Check net host connected to.
	SKIPE U1		; If local
	 CAMN U1,[-1]		;  or not connected
	  JRST NTDIS1		;   just zap conn state and close channels.
	MOVE U1,NTTYPE		; Check protocol/transport.
	CAIE U1,NT$ISM		; If this is SMTP
	 CAIN U1,NT$CSM		; on any medium
	  CALL NSMBYE		; be polite and say we are going away.
	   NOP
NTDIS1:	SETOM NDHOST		; Say no longer connected to anything!
	SETOM NTHOST
	SETZM XRSQQ		; And no state here, either.
	SETZM NTCCON
NKDISC:	.CLOSE NETD,		; Close all channels, make clean sweep.
	.CLOSE NETI,
	XCTIOC [OUTCAL(NETO,CLS)]
	 NOP			; Close this way to flush any buffer.
	.CLOSE NETO,		; Above doesn't always close the ITS channel
	RET



; NTRDED - Here when unexpected disconnection is detected.
; Returns temporary host error.

NTRDED:	CSTAT (,("...net conns gone."))
	MOVEI A,MR$TEH
	SETZ B,
	RET



SUBTTL	XRSQ Hacking routines

; NTXRSQ - Find which scheme (T or R) host prefers using.
; Returns after setting XRSQ variables.
; Skips if net conns still open.

NTXRSQ:	CAME N,NDHOST
	 JRST NTRDED
IFN $$FTP,[
	MOVE U1,NTTYPE		; Find protocol/transport type in use.
	CAIN U1,NT$FTP		; If it is FTP
	 JRST NTXSR1		;   must negotiate scheme.
];$$FTP
	;; The CHAOS SEND protocol does not do XRCP.
	MOVE U1,NTTYPE
	CAIN U1,NT$CHS
	 JRST [	SETZM XRSQPS	; No XRCP.
		SETZM XRSQRS	; None in effect.
		SETZM XRSQS	; None selected.
		SETOM XRSQQ	; Say have asked host.
		RET ]

	;; The SMTP and all CHAOS MAIL protocols always use scheme "R".
	SETOM XRSQPS		; Say R scheme preferred
	SETOM XRSQRS		; and is in effect.
	SETOM XRSQQ		; and have asked host.
	SETZM XRSQS		; Clear just in case.
	RET			; All done.

IFN $$FTP,[
	;; NCP/FTP requires noegotiation.

NTXSR1:	PUSHAE P,[A,B]
	MOVE A,DEFTMO		; Allow 30 sec for XRSQ procedure.
	CALL NETRAP
	 JRST NTXRS9		; If error...
	SETZM XRSQQ		; Zap all vars.  Say haven't asked.
	SETZM XRSQS		; Say none selected.
	SETZM XRSQPS		; Say none preferred.
	SETZM XRSQRS		; Say none in actual use.

	FWRITE NETO,[[XRSQ ?
]]				; Ask question of host.
	.NETS NETO,
	MOVEI A,215.		; Look for this reply...
	CALL NTRNXX		; Look for reply.
	 JRST NTXRS8		; Lost.
	;; Win, host hacks XRSQ!  See which scheme to use.
	MOVE A,NTRCNT		; Get # chars in reply buffer
	CAIGE A,5		; Need at least this many.
	 JRST NTXRS8		; Sigh...
	LDB A,[010700,,NTRBUF]	; Gross crock.  Get 5th char...
	SETZ B,
	CAIE A,"T
	 CAIN A,"t		; Text-first scheme?
	  MOVEI B,1		; Yep, use this.
	CAIE A,"R
	 CAIN A,"r		; Rcpts-first scheme?
	  SETO B,		; Yep, use -1.
	MOVEM B,XRSQPS		; Say which scheme host prefers.
	JUMPE B,NTXRS8		; Just return now if none preferred.
	TRZ A,40		; Make sure letter is uppercase...
	FWRITE NETO,[[XRSQ ],TI,(A),[
]]
	.NETS NETO,		; Try to select that scheme!
	MOVEI A,200.		; Look for this reply exactly.
	CALL NTRNXX
	 JRST NTXRS8		; Couldn't get??  Oh well.
	MOVE A,XRSQPS		; Hurray, got it!
	MOVEM A,XRSQRS		; Mark that as scheme now in effect!!
				; Drop thru to return.

NTXRS8:	SETOM XRSQQ		; Have asked, don't do again this host.
	CALL NERESET
	AOS -2(P)
NTXRS9:	PJRST POPBAJ
];$$FTP


; NTXRCP - Send an XRCP recipient specification.
;	A - ASCNT ptr to rcpt name
; Returns .+1 if error (A and B as per usual)
; .+2 if win.

NTXRCP:	CAME N,NDHOST		; Ensure still connected.
	 JRST NTRDED
	MOVE B,A
	MOVE A,DEFTMO
	SKIPLE XRSQRS		; But if current scheme is Text-first,
	 LSH A,2		;   be more generous.
	CALL NETRAP
	 JRST [	HLRZ B,A
		CSTAT (,(" ..."),TZ$(NCETAB(B)))	; Timeout or IOC
		JRST NTXRC9 ]
	PROTCL A,PR$RCP
	CALL (A)		; Do it.
	 JRST NTXRC5		;  Ananlyze error code. 
	CALL NERESET		; Win - flush network error trap.
	SETZB A,B
	JRST POPJ1

;;; Here to analyze error resulting from recipient spec.

NTXRC5:	MOVE B,A		; Here to analyze rcpt error.
	CALL NERESET		; Flush error trap.
IFN $$450, CAIE B,450.
	 CAIL B,500.
	  SKIPA A,[MR$PER]	; Permanent error
	   MOVEI A,MR$TER	; Else assume temp err. (4xx)
	CAIA
NTXRC9:	 MOVEI A,MR$TEH		; Maybe change this later?
	SETZ B,
	CAIN A,MR$PER
	 JRST [	CALL RPLYER	; For permanent error, return message
		CSTAT (,(" ...PERM ERR="),LBRC,CALL(NTRSHO),RBRC)
		RET]
	CAIE A,MR$TER
	 RET
	CSTAT (,(" ...TEMP ERR="),LBRC,CALL(NTRSHO),RBRC)
	RET


SUBTTL	NTMINI - Dispatch to message transaction initialization

; NTMINI - A should have LP to rcpt list

NTMINI:	MOVE A,DEFTMO
	CALL NETRAP		; Set up timeout.
	 JRST [	HLRZ B,A	; If we lose, print error msg.
		CSTAT (,("..."),TZ$(NCETAB(B)))
		JRST NTMIN9 ]
	PROTCL U1,PR$MIN
	CALL (U1)
	 CAIA
	  JRST [ CALL NERESET	;  Flush errset.
		 SETZB A,B
		 JRST POPJ1 ]
	MOVE B,A		; Get reply from protocol initialization.
	CALL NERESET		; Flush trap frame, etc.
	CSTAT (,(|...init lost, R="|),CALL(NTRSHO),(|"|))
IFN $$450, CAIE B,450.
	 CAIL B,500.
	  JRST NTMIN8		; Lossage...
	SKIPA B,[MR$TER]	; Assume temp err. (4xx)
NTMIN8:	 MOVEI B,MR$PER
	SKIPA A,B
NTMIN9:	 MOVEI A,MR$TEH
	SETZ B,
	CAIN A,MR$PER
	 CALL RPLYER		; For permanent error, return message
	RET

SUBTTL	NTMBEG - Dispatch to message setup routine

; NTMBEG -
;	A - ASCNT ptr to name string
;	B - Command type (0 for MAIL, -1 for FTP/MLFL, 1 for Send)
;	    This arg is only really used by the FTP routines.
;
;	Haggles with remote server and returns when NETO channel
;	(or NETD as case may be) is ready to send message text over.
;	doesn't skip if error occurs.
;
; Returns .+1 if error:
;		A - error code (see MR$ values)
;		B - SLP to error message, or zero
;	.+2 if success - NTMSND can now be used to send msg text.

NTMBEG:	CAME N,NDHOST		; Ensure still connected.
	 JRST NTRDED
	PUSHAE P,[C]
	MOVE C,A		; Save ASCNT to rcpt name.
	MOVE A,DEFTMO		; Get timeout arg
	CALL NETRAP		; Set up error traps
	 JRST [	HLRZ B,A	; Get problem index.
		CSTAT (,(" ...NTMBEG "),TZ$(NCETAB(B)))
		JRST NTMBG9 ]
	MOVE A,C		; Put back ASCNT to rcpt name
	PROTCL C,PR$BEG
	CALL (C)		; Do it
	 JRST [	MOVEM A,NTERRC	;  If error, remember code.
		CALL NERESET	;  Flush trap frame.
		MOVE A,NTERRC
		JUMPGE A,NTMBGT	;  Error, check it out.
		MOVNS A		;  Negative error is always soft/temporary
		STAT (,("NTMBEG NET ERR="),D(A))
		JRST NTMBG9 ]
	;; Rcpt accepted, all set.
	CALL NERESET	; Turn off error trapping.
	SETZB A,B
	PJRST POPCJ1	; Win return skips.

	;; Wrong error code, see if temporary or not. 
NTMBGT:	CALL NTECHK		; Classify error
	 JRST [ STAT (,("NTMBEG TEMP ERR="),LBRC,CALL(NTRSHO),RBRC)
		JRST NTMBG9 ]	;  Go claim temporary error.
	 JRST [ STAT (,("NTMBEG PERM ERR="),LBRC,CALL(NTRSHO),RBRC)
		MOVEI A,MR$PER	;  Claim permanent error for rcpt.
		CALL RPLYER	;  Set up error message
		PJRST POPCJ ]
	;; The error is not in our table, so we don't know which it is.
	;; Assume it is temporary and blame it on the recipient's mailbox.
	STAT (,("NTMBEG RCPT ERR="),LBRC,CALL(NTRSHO),RBRC)
	MOVEI A,MR$TER		; Temp Err for Rcpt
	SETZ B,
	PJRST POPCJ

NTMBG9:	MOVEI A,MR$TEH		; Note: IOC/timeouts also claim host temp err.
	SETZ B,
	PJRST POPCJ

	;; Here to  compose error receipt.
RPLYER:	MAKELN B,[0	; Net message failed, compose error string.
		%LTSAO,,[[OUTCAL(,("Recipient name apparently rejected.
	Last reply was: "),LBRC,CALL(NTRSHO),RBRC)]] ]
	RET

SUBTTL	Network Error Classification

; NTECHK - classify network error in A (smash B)
; Returns:
;	.+1 - temporary
;	.+2 - permanent
;	.+3 - not known which

NTECHK:	JUMPL A,APOPJ		; Negative means net error, always temporary.
	PROTCL U1,PR$CHK
	CALRET (U1)

; Table of reply codes that imply permanent error (sigh).
NTCTAB:	-1	; None at moment.
       ;454.	; NBS-10, WPAFB-AFWAL (these suckers use 454 for no such user!)
NTCTBL==<.-NTCTAB>

;;; Here to process SMTP reply codes.
NTERSM:
IFN $$450,{			; Looks like plant food to me...
	SKIPE SORMSW		; If this was an interactive send
	 JRST [ CAIN A,450.	; Check for "User not online".
		 JRST POPJ1	;  which is a permanent error.
		JRST .+1 ]	; (That should work for SOML/SAML also.)
}
	CAIL A,500.		; All 5xx are permanent
	 JRST POPJ1
	CAIL A,400.		; and all 4xx are temporary
	 RET
	AOS (P)			; If neither 4xx or 5xx, say dunno.
	JRST POPJ1

;;; Here to process FTP reply codes.
NTERFT:
IFN $$450, CAIE B,450.		; No, FTP. Is it a 450?
	 CAIL B,500.		; Or a 5xx?
	  JRST POPJ1		; Yes, lose.
	MOVSI B,-NTCTBL
	CAMN A,NTCTAB(B)
	 JRST POPJ1		; Found one, so assume permanent.
	AOBJN B,.-2
	AOS (P)
	JRST POPJ1



SUBTTL	NTMSND - Dispatch to Text sending routines

; NTMSND - Takes ascnt ptr to message text in A and sends over net.
;	Uses neto or NETD channel as necessary.
;	Skips on success.  Returns standard error code in A, msg in B

NTMSND:	CAME N,NDHOST
	 JRST NTRDED
	MOVE B,A		; Save arg
	SETOM NTERRC		; Reset err val
	MOVE A,DEFTMO		; Alert lifeguard to check every once in a while
	CALL NETRAP
	 JRST [	SKIPL NTERRC	; If special IOC error (MLFL)
		 JRST NTMSN9	; then already complained.
		HLRZ B,A
		CSTAT (,(" ...NTMSND "),TZ$(NCETAB(B)))
		MOVEI A,MR$TEH
		SETZ B,
		RET ]
	MOVE A,[SETZ NXFRLT]	; Use special RLT vector
	MOVEM A,RLTVEC		; So we can check progress at interrupt level.
	PROTCL A,PR$SND
	CALL (A)
	 JRST NTMSN8		; Error, reset frame and go lose.
	CALL NERESET		; Win!
	SETZB A,B
	PJRST POPJ1

	; Non-IOC/timeout SMTP data transfer error (only FINISH fail possible)
	; All current error returns are known to provide valid MR$ codes in
	; some manner.
NTMSN8:	MOVE B,A
	CALL NERESET		; IOC type error, flush err frame
	SKIPA A,B
NTMSN9:	 MOVE A,NTERRC		; Entry pt for special IOC error
	SETZ B,
	RET


LVAR NXFRST:	0		; Holds state of transfer

	; Timeout interrupt for data xfer to net site.
	; See if still active, or .IOT hung.
	; This code runs at interrupt level!
NXFRLT:	CAMN U3,NXFRST		; Check SIOT BP with last value
	 DISMISS [NXFRL5]	; Lose, no activity since last test!
	MOVEM U3,NXFRST		; Tis different, xfer is active! Save value
	DISMISS			; for next compare, and dismiss.

	; Dispatch here from above handler if timeout on no activity.
NXFRL5:	CLKOFF
	STAT (,("Note: Net xfer lossage, no activity for 2 mins!"))
	JRST NTRRLT	; Jump to regular RLT handler from here on.

SUBTTL	NTMEND - Dispatch to message wrapup

;  NTMEND - called when message transmission is finished, terminates
;	message and gets back to normal ftp communication.  may break
;	data connection if exists, but doesn't close telnet connections.
; Non-skip return implies error, usually temporary.
;	A - Error code (see MR$ values)
;	B - 0 or error message

NTMEND:	CAME N,NDHOST
	 JRST NTRDED
	MOVE A,FINTMO		; Timeout arg
	CALL NETRAP		; Setup to trap IOC and timeout.
	 JRST [	HLRZ B,A
		STAT (,("INCOMPLETE, "),TZ$(NCETAB(B)))
		JRST NTMEN9 ]
	PROTCL A,PR$END
	CALL (A)		; Invoke routine
	 JRST [	MOVEM A,NTERRC	; Failure, must examine reply code.
		CALL NERESET
		MOVE A,NTERRC		
		JRST NTMEN3]	; Failure, go examine reply code
	CALL NERESET		; Winnage.
NTMEN6:	SETZB A,B
	PJRST POPJ1

;Wrong or no reply code after sending message 
;If we get no reply, it's a temporary recipient error
;If we get a (wrong) reply between 200 and 299, complain but accept it
;If we get a 460 reply, it's a Chaosnet temporary error
;Don't trust 4xx replies on the Arpanet to be temporary
;If we get some other reply, take as permanent error since something is really fucked
NTMEN3:	JUMPLE A,[
		MOVNS A
		STAT (,("INCOMPLETE, NET ERR="),D(A))
		JRST NTMEN9]
	STAT (,("NTMEND ERR="),D(A),(","),LBRC,CALL(NTRSHO),RBRC)
	CAIL A,200.
	 CAILE A,299.
	  CAIA
	   JRST [CSTAT (,(" - but accepted."))
		JRST NTMEN6]
	CAIGE A,500.	; Permanent error?
	 JRST NTMEN9	; No, temporary (4xx or less).

	MOVEI A,MR$PER
	MAKELN B,[0	; Net message failed, compose error string.
		%LTSAO,,[[OUTCAL(,("Funny reply from foreign host after sending message.
	Last reply was: "),LBRC,CALL(NTRSHO),RBRC)]] ]
	RET

NTMEN9:	MOVEI A,MR$TER
	SKIPG NTHOST		; Make sure net conns still alive
	 MOVEI A,MR$TEH		; Else a temp err for host.
	SETZ B,
	RET


SUBTTL	Net input stuff (Read a reply)

BVAR
NTRCMX==:250.		; Max # chars in buffer
NTRBUF:	BLOCK <NTRCMX+4>/5
	0		; Ensure fence if too-long reply truncated
NTRBOV:	0		; -1 when buffer full (reply truncated).
NTRPT:	0		; BP into NTRBUF
NTRCNT:	0		; Cnt of chars in NTRBUF
REPLYC:	0		; Holds # of last code received
NTRPKT:	0		; -1 if datagram transaction stored reply here
EVAR

; NTRLGT - Get a FTP reply line.
; Skips unless ran out of input (net error).
; Over-long replies are simply truncated, without error.

NTRLGT:	SKIPE NTRPKT		; Datagram transaction?
	 JRST NTRLG8		;  Yes, reply already in buffer.
	SKIPA A,NTRCNT
NTRLG0:	 AOS A,NTRCNT
	CAIL A,NTRCMX		; Make sure we still have room.
	 JRST NTRLG3		; Nope, go to barfage section.
NTRLG1:	CALL NTCHRI		; Get char
	 RET			; Whoops, net error!
	CAIN A,^M		; Maybe end of reply line?
	 JRST NTRLG7		; Go check.
	IDPB A,NTRPT		; Deposit char in buffer.
	JRST NTRLG0		; Test for overflow and continue.

NTRLG3:	SKIPE NTRBOV		; First-time overflow?
	 JRST NTRLG4		; Nope, just output to stats.
	SETOM NTRBOV		; First time, announce error.
	STAT (,("Foo: NTRBUF Overflow: "),LBRC,CALL(NTRSHO),RBRC)
NTRLG4:	CALL NTCHRI
	 RET			; Blug, net error.
	CAIE A,^M		; Possible EOL?
	 JRST [	CSTAT (,C((A)))	;  Just report to stats.
		JRST NTRLG4]
NTRLG7:	CALL NTCHRI		; Try next char
	 RET			;  Sigh, lost.
	CAIN A,^J		; LF follows?
NTRLG8:	 AOS (P)		;  Won!  Skip on return.
	SETZM NTRPKT		; Datagram reply has been "used".
	RET


; NTRPCD - Gobble a reply line and parse reply code.
;	Given NTRPT, NTRCNT, NTRBOV.
; Returns .+1 if net error
; Returns .+2 otherwise:
;	A - reply code (-1 if couldn't parse line)
;	B - BP to rest of line after code, or 0 if no more (!).

NTRPCD:	PUSHAE P,[C,D]
	SKIPG A,NTRCNT		; See how many chars we've got.
	 JRST NTRPC2		; Nothing, can skip CRLF addition.
	CAIL A,NTRCMX-5		; Need room for at least 5 more.
	 JRST [	SUBI A,6	; If no room, make some!
		MOVEM A,NTRCNT
		MOVNI A,6
		PTSKIP A,NTRPT
		JRST .+1]
	;; Some stuff is already in buffer, so append a CRLF first.
	MOVEI B,^M ? IDPB B,NTRPT ? AOS NTRCNT
	MOVEI B,^J ? IDPB B,NTRPT ? AOS NTRCNT

NTRPC2:	MOVE B,NTRPT		; Save current ptr into buffer
	MOVE C,NTRCNT		; and current cnt.
	CALL NTRLGT		; Gobble a new line.
	 JRST NTRPC9		; Net lossage.
	MOVE D,NTRCNT		; OK, now get new count
	SUBI D,(C)		; to see how many chars we read.
	CAIGE D,3		; Must be at least 3...
	 JRST NTRPC8		; Fooey, say parsing error.

	HRLI D,-3		; 3 digits
	SETZ A,			; Value goes here
NTRPC5:	ILDB C,B
	CAIL C,"0		; Is it a digit?
	 CAILE C,"9
	  JRST NTRPC8		; Nope, say parsing error.
	IMULI A,10.		; Yes, power up A
	ADDI A,-"0(C)		; and add in value of digit.
	AOBJN D,NTRPC5		; Get next
	CAIG D,6.		; Skip if more chars on line.
	 SETZ B,		; No more, zap B to indicate this.
	CAIA
NTRPC8:	 SETO A,		; Say parsing error.
	AOS -2(P)		; Skip on return
NTRPC9:	POPAE P,[D,C]
	RET
				 

;  "NTRPLY"-- inputs one server reply into a
;	buffer for inspection.  Returns code in A and REPLYC.

NTRPLY:	PUSH P,B
	SETOM REPLYC

NTRPL1:	MOVE A,[440700,,NTRBUF]
	MOVEM A,NTRPT		; Initialize ptr
	SETZM NTRCNT		; Zero char cnt
	SETZM NTRBOV		; and overflow flag.

NTRPL2:	CALL NTRPCD		; Get line and code.
	 JRST POPBJ		; Net error.
	JUMPL A,[CSTAT (,LBRK,("NTRPLY: Bad reply syntax="),LBRC,CALL(NTRSHO),RBRC,RBRK)
		 JRST NTRPL1 ]
	MOVEM A,REPLYC		; Save returned code
	JUMPE B,NTRPL3		; If nothing more, no continuation.
	ILDB A,B		; Get char after code
	CAIN A,"-		; Continuation mark?
	 JRST NTRPL2		; Yes, bletch, get another line.

	; I suppose there should be some code around here that checks
	; to be sure that multi-line replies all have the same reply
	; code, but it hardly seems to matter.  If you care, change
	; the preceeding two lines to JRST NTRPL3 if not continuation,
	; and put the silly check/report here.

NTRPL3:	MOVE A,REPLYC
	CAILE A,599.		; Highest poss. code
	 JRST [	MOVN A,A	; Make neg. if too large
		MOVEM A,REPLYC	; (fuck you and your 951's, BBN!!)
		JRST .+1]
	SKIPE NTRBOV		; Was reply truncated?
	 JRST [	CSTAT (,RBRC)	; Yes, terminate stats report.
		JRST .+1]
	POP P,B			; All done!
	AOS (P)
	RET


; NTSOCK - assumes reply in ftrbuf is a 255 sock nnnn and proceeds
;	to convert decimal argument into socket number in a.

NTSOCK:	PUSHAE P,[B,C,D]
	MOVE D,NTRCNT
	SUBI D,9.		; Allow for the "255 SOCK " (9 chars)
	JUMPLE D,NTSCK9		; Maybe reply not long enough?
	MOVE B,[100700,,NTRBUF+1] ; BP to 10th char in buf.
	SETZ A,			; Used for arg accumulation
NTSCK1:	ILDB C,B		; Flush leading blanks.
	CAIE C,40
	 JRST NTSCK3
	SOJG D,NTSCK1
	JRST NTSCK9

NTSCK2:	ILDB C,B		; Get char
NTSCK3:	CAIL C,"0
	 CAILE C,"9
	  JRST NTSCK8		; Not a digit, stop parsing.
	IMULI A,10.		; Power up #
	ADDI A,-"0(C)		; Cvt and add digit
	SOJG D,NTSCK2		; Continue til count out.
NTSCK8:	AOS -3(P)
NTSCK9:	POPAE P,[D,C,B]
	RET


; NTR2XX and NTR3XX - search for 2xx and 3xx codes respectively,
;	flushing any 0xx or 1xx codes.  Returns code in A
;	whether fails or succeeds.
; Returns .+1 if code not in range or error.
;	Returned "code" will be zero if failure due to error.
; Returns .+2 if code in range

NTR2XX:	CALL NTRPLY		; Get net reply
	 JRST NTRPZ		; Net lossage.
	CAIGE A,200.		; If an 0xx or 1xx code,
	 JRST NTR2XX		; flush and get another.
	CAIGE A,300.		; If outside 2xx range, fail.
	 AOS (P)
	RET

NTRPZ:	SETZ A,			; Return zero "code".
	RET

NTR3XX:	CALL NTRPLY		; Get net reply
	 JRST NTRPZ
	CAIGE A,300.
	 JRST NTR3XX
	CAIGE A,400.
	 AOS (P)
	RET

; NTRNXX - Given reply code in A to look for.  Skips if found,
;	else no skip.
;	Ignores all 0xx and 1xx messages; ditto 6-9xx.
; Returns code in A whether succeed or fail.

NTRNXX:	PUSH P,A
NTRNX0:	CALL NTRPLY		; Get a reply.
	 JRST [	POP P,A ? JRST NTRPZ]	; error, return zero.
	CAMN A,(P)		; Is it what we want?
	 JRST POPAJ1		; Yep, win.
	CAIGE A,200.		; Is code one of 3xx, 4xx or 5xx?
	 JRST NTRNX0		; Nope, get another reply.
	SUB P,[1,,1]		; Ugh, failed.
	RET

; NTCHRI - returns one 7-bit ascii char in acc. A.
;	If it sees any 8-bit telnet control chars, will flush them
;	and wait for a 7-bit char.  Hence will not win with new
;	protocol, if foreign site sends anything spontaneously!

NTCHRI:	.IOT NETI,A
	JUMPGE A,NTCHI2		; If get -1,,3 then assume input channel has closed,
	SKIPN NTIOCV		; and simulate network IOC error
	 JSR AUTPSY		; with check to prevent blind hyperspace jump.
	JRST @NTIOCV
NTCHI2:	CAIL A,200
	 JRST NTCHRI		; Discard any 8-bit codes
	AOS (P)
	MOVE U1,NTTYPE
	CAIE U1,NT$CSM
	 CAIN U1,NT$ISM
	  OUTCAL (NETD,C((A)))	; If SMTP, record char for transaction.
	 RET

; NTRSHO - Output current reply to std output

NTRSHO:	OUT(,S(NTRCNT,[440700,,NTRBUF]))
	RET




SUBTTL Network Time

NTMSOC:	45			; Standard Binary "Time-server" socket

; NETIM - Hack to pluck 32-bit Network-time word from IP-TCP site
;	specified by A.  Returns value in A and skips,
;	doesn't skip if couldn't get.

NETIM:	PUSHAE P,[B,C]
	SYSCAL TCPOPN,[CIMM NETI ? CIMM NETO ? [-1] ? NTMSOC ? A ? CERR A]
	 JRST NETIM9
	MOVEI A,NETO
	NETHANG 900.,A,%NSRFS,[%NSOPN,%NSRFN]
	 JRST NETIM9
	SETZ A,
	MOVE B,[401000,,A]	; Read in 32 bit word here.
	MOVEI C,4.		; 8-bit bytes at a time.
	SYSCAL SIOT,[MOVEI NETI ? B ? C]
	 .LOSE %LSFIL
	AOS -2(P)
NETIM9:	.CLOSE NETD,
	POPAE P,[C,B]
	RET


SUBTTL Host Database routines (Name/Address lookup, etc.)

IFE $$DQ,{

; HANLYZ - Server-oriented host-name search.
;   A/ # chars
;   B/ Bp to name
;
; Skip returns in A: host address
; If non-skip, A: -1      ==> host not found, whether server or anything else.
;	         ptr,,ptr ==> ambiguous server sites, 2 absolute ptrs to
;			      NAME table entroes are turned as examples.

HANLYZ:	PUSHAE P,[B,C,D,E]
	MOVEM B,NPTSAV'		;Save ptr to name
	MOVEM A,NPTSVC'		; and save cnt.
HANL20:	SETZM HFSAV1'		;Clear the regs used to store
	SETZM HFSAV2'		;matches in.
	SETZM HNSSAV'		;(This one is non-server slot).
	SKIPN D,RESOLV"HSTADR
	 JSR AUTPSY
	ADD D,RESOLV"NAMPTR(D)	;Address NAMES table of 1wd entries.
	MOVN E,0(D)		;Get number of entries.
	HRLZS E			;Make aobjn pointer.
	HRRI E,2(D)
HANLZ1:	HRRZ D,RESOLV"NMRNAM(E)	;Points to ASCIZ name.
	ADD D,RESOLV"HSTADR
	HRLI D,440700
	MOVE C,NPTSAV
	MOVE A,NPTSVC		;Get cnt.
	MOVEM A,NPTCNT'
HANL11:	ILDB B,D
	SOSGE NPTCNT		;Decr char cnt.
	 TDCA A,A		;Clear A and skip if none left.
	  ILDB A,C
	JUMPE B,[JUMPN A,HANLZ4	;If our string longer, no match.
		MOVEI A,(E)	;Both counted out, perfect match!
		JRST HANLZ7 ]	;use this entry and go win.
	JUMPE A,HANLZ2		;Partial match if our string counts out first.
	CAMN A,B
	 JRST HANL11
	CAIL A,"a		;If chars don't match, try converting
	 CAILE A,"z		;input string to uppercase.
	  JRST HANLZ4		;Twas uppercase already.
	SUBI A,40
	CAMN A,B
	 JRST HANL11
HANLZ4:	AOBJN E,HANLZ1
	;; All searching done, no perfect matches, see if partial matches.
	SKIPE HFSAV2		;Was an ambiguous server host found?
	 JRST [	HRLZ A,HFSAV1	;Ambiguous; two or more found. 
		HRR A,HFSAV2	;First in LH, second in RH.
		JRST HANLZ9]	;Loss return.
	SKIPE A,HFSAV1		;Was unambiguous server host found?
	 JRST HANLZ7		; Yes, only one partial match, win
	SKIPN A,HNSSAV		;Was a non-server site found? (load with value)
	 JRST HANLZ8		; Nope, go to loss return.
HANLZ7:	HLRZ E,(A)		;Get adr of SITE entry.
	ADD E,RESOLV"HSTADR
	HRRZ B,RESOLV"STRADR(E)	;Get file addr of ADDRESS table entry.
	;; Now decide which of the possible addresses to use.
	;; priority is CHAOSnet, ARPAnet, random net.
	SETOB A,C
HANLC2:	ADD B,RESOLV"HSTADR	;Make abs ptr
	MOVE D,RESOLV"ADDADR(B)	;Get net address of this entry
	CAME D,OWNHST
	 CAMN D,OWNHS2		;If this is our own host address
	  JRST [ MOVE A,OWNHST	; OWNHST is best possible address!
		 JRST HANL78 ]
	RESOLV"GETNET D		;Else get net number it's on.
	MOVEI E,4		;Search for favorite networks.
	CAME D,(E)[ RESOLV"NW%ARP	;Priority in reverse order.
		    RESOLV"NW%LCS
		    RESOLV"NW%AI
		    RESOLV"NW%CHS]-1	;Chaos preferred over Internet.
	 SOJG E,.-1
	CAIL C,(E)
	 JRST HANLC3
	MOVE A,RESOLV"ADDADR(B)	;Aha, save address
	MOVEI C,(E)		;and its priority
HANLC3:	HRRZ B,RESOLV"ADRCDR(B)	;Check out more net addrs if any
	JUMPN B,HANLC2		;Yep, check next one.
	JUMPG C,HANL78		;If found a known winner, jump!
	TLNN A,(RESOLV"NE%UNT)	;Else examine more closely...
	 JRST HANL78		; Is Internet addr, shd be OK.
	JRST HANL72		;Ugh, non-Internet (and not CHAOS), so lose.

HANL72:	MOVNI A,2
	JRST HANLZ9		;Nope, not right place after all, lose.

HANL78:	AOSA -4(P)		;Come here for winning return
HANLZ8:	 SETO A,
HANLZ9:	POPAE P,[E,D,C,B]
	RET			;Failure return

	;; Here when partial match found
HANLZ2:	SKIPN HFSAV1		;Skip if already have one partial match.
	 JRST [	HRRZM E,HFSAV1	;Save table index to first partial match
		HLRZ A,(E)	;Get file addr of SITE entry
		ADD A,RESOLV"HSTADR
		MOVE A,RESOLV"STLFLG(A)	;Get flags
		TLNE A,RESOLV"STFSRV	;Skip if not server
		 JRST HANLZ4	;Continue, to check for ambiguities	
		HRRZM E,HNSSAV	;Non-server, store entry # here.
		SETZM HFSAV1	;Rectify wrong assumption
		JRST HANLZ4]	;and continue.
	;; Not first partial match, save if server, ignore if not.
	HLRZ A,(E)		;Get file addr of SITE entry
	ADD A,RESOLV"HSTADR
	MOVE A,RESOLV"STLFLG(A)	;Get flags
	TLNN A,RESOLV"STFSRV	;Skip if server
	 JRST HANLZ4		;Ignore if non-server
	MOVEI B,(E)
	CAMN B,HFSAV1		;Test against entry of previously matched name
	 JRST HANLZ4		;Ignore if already found same host.
	CAMN B,HFSAV2		;Same as first found?
	 JRST HANLZ4		; No, ignore if this host already listed.
	MOVEM B,HFSAV2'		;Different from both - "second-found" host.
	JRST HANLZ4		;Continue looking (may find exact match).

};IFE $$DQ

IFN $$DQ,{

; HANLYZ - Host Lookup
;   A/ # chars
;   B/ Bp to name
;
; Skip returns in A: host address
; If non-skip, A: -1 ==> host not found
;		  maybe someday other codes
;
; Uses the RESOLV library to query the DQ device for host address information.
; Asks for an address in the CH class; if none, asks for class IN.

HANLYZ:	PUSHAE P,[B,C,D]
	MOVE C,[440700,,DQBUF]	; Copy string (have to ASCIZify)
	ILDB D,B
	IDPB D,C		; Copy all bytes
	SOJG A,.-2
	IDPB A,C		; Null at end
	MOVE A,[440700,,DQBUF]	; Point at ASCIZified string
	CALL RESOLV"HSTADR	; Resolve string to HOSTS3 type address
	 SKIPA A,[-1]		; Lost, foo
	  AOS -3(P)		; Won, skip return
HANL99:	POPAE P,[D,C,B]
	RET

};$$DQ

SUBTTL NCP/FTP Routines - NNFICP

IFN $$FTP,[

NETDEV:	SIXBIT /NET/
ICPSOC:	3		; Standard FTP server socket
BVAR
NRLSOC:	0		; Local Receive socket (U+2)
NSLSOC:	0		; Local Send (U+3)
NDLSOC:	0		; Local Data output skt (always output so always U+5)
NRFSOC:	0		; Foreign Receive skt (S)
NSFSOC:	0		; Foreign Send (S+1)
NDFSOC:	0		; Foreign Data skt (always input, so defaults to S+2)
EVAR



; NNFICP - Make initial connection to server FTP
;	Called by NETICP.

NNFICP:	SYSCAL OPEN,[[40050+.UII,,NETD] ? NETDEV ? CIMM -1 ? ICPSOC ? NTHOST
		CERR A]
	 JRST [HRLI A,NCE$SY ? RET]
	MOVEI A,NETD			; Arg to NETHANG
	NETHANG 900.,A,%NSRFS,[%NSOPN,%NSCLI,%NSINP]	; Wait til conn. opened or timeout
	 JRST [MOVNS A ? HRLI A,NCE$CS ? RET]
	SYSCAL RCHST,[CIMM NETD ? CRET JUNK ? CRET A]
	 JSR AUTPSY		; Get generated local socket #
	ADDI A,2		; Get # for local receive
	MOVEM A,NRLSOC
	ADDI A,1		; Get # for local transmit
	MOVEM A,NSLSOC
	ADDI A,2		; Get U+5 as local output data socket for MLFL
	MOVEM A,NDLSOC
	MOVEI A,NETD
	NETHANG 900.,A,%NSOPN,[%NSCLI,%NSINP]	; Wait til input avail
	 JRST [	MOVNS A
		HRLI A,NCE$CS ? RET]
	.IOT NETD,A		; Get foreign receive socket
	MOVEM A,NRFSOC
	ADDI A,1		; Get # for foreign transmit
	MOVEM A,NSFSOC
	; Open Net output and input channels
	SYSCAL OPEN,[[.UAO,,NETO] ? NETDEV ? NSLSOC ? NRFSOC ? NTHOST ? CERR A]
	 JRST [HRLI A,NCE$SY ? RET]
	SYSCAL OPEN,[[40+.UAI,,NETI] ? NETDEV ? NRLSOC ? NSFSOC ? NTHOST ? CERR A]
	 JRST [HRLI A,NCE$SY ? RET]
	; Wait til completely open
	MOVEI A,NETO
	NETHANG 900.,A,%NSRFS,[%NSOPN]
	 JRST [MOVNS A ? HRLI A,NCE$CS ? RET]
	MOVEI A,NETI
	NETHANG 900.,A,%NSRFS,[%NSOPN,%NSCLI,%NSINP]
	 JRST [MOVNS A ? HRLI A,NCE$CS ? RET]
	.CLOSE NETD,
	CLKOFF			; ICP completed, start another frame
	CLKSET [60.*60.]	; for initial FTP negotiation
	CLKON
	OUT(NETO,OPEN(UC$IOT))	; Open net output for UUO handling.
	MOVEI A,300.
	CALL NTRNXX		; Get the 300 initial reply
	 JRST [	HRLI A,NCE$GR	; Error or code not 300.
		RET]
	; Check for being faked-out by a loop-back plug
	FWRITE NETO,[[XLBT ],HND,NTHOST,[
]]
	.NETS NETO,
	CALL NTR2XX
	 JUMPLE A,[	HRLI A,NCE$HR	; Fail if net error or weird code.
			RET]
	CAIN A,529.	; If this specific error # is returned,
	 JRST [	HRLI A,NCE$HR		; loop-back plug must be present.
		RET]
	;; Now log in if this is a Multics
	;; It doesn't really log in, it just bludgeons its way past the answering
	;; service into more maintainable code.
	MOVE A,NTHOST
	CALL NHMLTX		; Host = multics?
	 JRST POPJ1		; No, thank goodness.
	CALL NTMLTX		; Ick, do special hack (acct and passw)
	 JRST [HRLI A,NCE$HR ? RET]	; Lost, consider it temporary soft err
	JRST POPJ1


; NTDLIS - Start listening on data connection

NTDLIS:	SYSCAL OPEN,[[60+.UAO,,NETD]	; Open for listen
		NETDEV ? NDLSOC ? NDFSOC ? NTHOST]
	 CAIA
	  AOS (P)
	RET

; NTDCON - Complete data connection given foreign socket # in A

NTDCON:	PUSHAE P,[A,B]
	MOVEM A,NDFSOC		; Store forn socket
	MOVEI B,NETD
	NETHANG 900.,B,%NSLSN,[%NSRFC]	; Hang til RFC received
	 JRST POPBAJ		; Never came or something
	.NETAC NETD,		; Accept it
	 JRST POPBAJ
	MOVEI B,NETD
	NETHANG 900.,B,%NSRFC,[%NSOPN]	; Wait til fully open
	 JRST POPBAJ
	SYSCAL RCHST,[CIMM NETD ? CRET JUNK ? CRET JUNK ? CRET B]
	 JSR AUTPSY		; Find foreign socket #
	CAMN A,B		; Socket same as 255 specified?
	 JRST POPBA1		; Yes, take winning return.
	.CLOSE NETD,		; No, lose. arg was bad, or someone else connected to us.
	STAT (,(" ="),RABR,(" Foo! Data connection mismatched!"))
	JRST POPBAJ


];$$FTP


SUBTTL NCP/FTP routines - NNFRCP, NNFBEG, NNFSND, NNFEND

IFN $$FTP,[

LVAR NT%FIL:	0	; 0 = sending msg over telnet conns,
			; -1 = over data conn (MLFL)

; NNFRCP - NCP/FTP routine to submit rcpt string to host
;	B/ ASCNT to rcpt name

NNFRCP:	OUT(NETO,("XRCP "),TC(B))
	SKIPE NTRTSW		; Doing routing thru gateway?
	 OUTCAL(NETO,("@"),HST(NDHOST))	; Yes, specify real dest.
NTXRC3:	OUT(NETO,EOL)
	.NETS NETO,
	CALL NTR2XX	; Any 2xx reply is okay.
	 RET		; Sigh... must puzzle out.
	JRST POPJ1


; NNFBEG - NCP/FTP message setup routine, called by NTMBEG.
;	A/ ASCNT of rcpt name
;	B/ cmd to use

; Now find what command to use for sending mail. B neg means MLFL.
; B zero means MAIL.  B pos means figure out Sending command.

NNFBEG:	MOVE C,A
	JUMPL B,[SETOM NT%FIL	; Indicate MLFL
		MOVE B,[ASCNT [MLFL]]
		CSTAT (,(" (MLFL)"))
		JRST NNFBG2]
NNFBG0:	SETZM NT%FIL		; Indicate non-mlfl
	SKIPG B
	 JRST [ MOVE B,[ASCNT [MAIL]]	; Zero gets default of "mail"
		JRST NNFBG2 ]
	SKIPLE SENDSW		; Positive means puzzle out Sending.
	 MOVE B,[ASCNT [XMAS]]
	SKIPGE SENDSW
	 MOVE B,[ASCNT [XSEN]]
	SKIPN SENDSW
	 MOVE B,[ASCNT [XSEM]]
	; Send out appropriate command.
NNFBG2:	OUT(NETO,TC(B))		; Out with the command...
	JUMPE C,NNFBG3
	OUT(NETO,(" "),TC(C))
	SKIPE NTRTSW		; Doing routing thru gateway?
	 OUTCAL(NETO,("@"),HST(NDHOST))	; Yes, specify real dest.
NNFBG3:	OUT(NETO,EOL)
	.NETS NETO,
	SKIPGE NT%FIL	; Now look for reply as per type
	 JRST NNFBG4	; Look for MLFL reply

	CALL NTR3XX	; Look for 3xx, ignore 0xx, 1xx, 2xx.
	 RET		; If error, return to check it out.
	CAIN A,350.	; Is code right one right off?
	 JRST POPJ1	; Yep, win instantly.
	RET		; No, should check further.
	
	; Hack MLFL reply and data conn setup.
NNFBG4:	CALL NTDLIS	; Open data conn in listen mode NOW, to avoid
	 JRST [	MOVEI A,-24.	; synch problems with some cretinous systems.
		RET ]		; Return temp error.
	MOVEI A,255.	; Look for SOCK reply...
	CALL NTRNXX	; and ignore 0xx, 1xx etc.
	 JRST [ CAIE A,504.	; Code not 2xx, maybe fail hard.
		 RET		; or maybe soft
		MOVEI B,0	; Cretinous TOPS-10
		CSTAT (,(" (Rejected, trying MAIL)"))
		JRST NNFBG0 ]
	CALL NTSOCK	; Convert 255's arg into socket # in A
	 JRST [	MOVEI A,-25.	; Blah, bad argument, soft error.
		RET ]
	CALL NTDCON	; Complete data connection to socket # in A.
	 JRST NNFBG5	; Couldn't, do something
	MOVEI A,250.	; Now wait for 250 go-ahead...
	CALL NTRNXX	; ignoring 0xx etc.
	 RET		; Funny reply, check for fail hard (CMU sends its
			; user name reject here instead of in first reply)
	OUT(NETD,OPEN(UC$IOT))	; All won, set up UUO chan for output!
	JRST POPJ1

	; Here if MLFL data connection not established
	; Normally a temporary error, but see if there is an
	; accompanying FTP reply
NNFBG5:	CALL MLFLRP		;Get reply if any
	 SETO A,		;None, net error.
	RET			;Decide whether permanent or temporary

MLFLRP:	.CALL [SETZ ? 'WHYINT ? MOVEI NETI ? MOVEM A ? MOVEM A ? SETZM A]
	 MOVEI A,0
	JUMPE A,APOPJ		;No input available
	CALL NTRPLY		;Input available, read it
	 RET			;Net error
	CAIL A,400.
	 JRST POPJ1		;Valid error code
	STAT (,(" ="),RABR,(" Funny FTP reply after MLFL data connection failure:"))
	STAT (,(" ="),RABR,(" "),LBRC,CALL(NTRSHO),RBRC)
	JRST MLFLRP		;Look for more reply


NNFSND:	SKIPE NT%FIL		; Skip unless MLFL'ing
	 JRST NNFSN2
	FWRITE NETO,[TC,B]
	.NETS NETO,
	JRST POPJ1

	;MLFL - TOPS-10's give us a hard time by opening the data connection,
	;then immediately closing it and sending an FTP reply code saying the
	;recipient was illegal.  So if we get an IOC error, check for that.
NNFSN2:	MOVEI A,NTMSF1
	MOVEM A,NTIOCV
	FWRITE NETD,[TC,B]
	.NETS NETD,
	JRST POPJ1

NTMSF1:	MOVEI A,NTRIOC		;If another IOC error occurs, trap out
	MOVEM A,NTIOCV
	CSTAT (,(" IOC error in NNFSND"))
	CALL MLFLRP		;Look for reply code
	 JRST NTRIOC		;None, treat normally
	STAT (,("MLFL reply="),LBRC,CALL(NTRSHO),RBRC)
	CALL NTECHK		;Classify error
	 JRST [	MOVEI A,MR$TER
		JRST NTMSF2 ]
	 MOVEI A,MR$PER
	MOVEI A,MR$PER
NTMSF2:	MOVEM A,NTERRC
	JRST NTRIOC		; Take normal exit


; NNFEND - NCP/FTP message transmission termination check.
; Returns
;	.+1 if failed, reply code in A (negative if net error)
;	.+2 if message sent successfully.

NNFEND:	SKIPE NT%FIL		; Now terminate message according to type.
	 JRST [	.CLOSE NETD,	; for MLFL just close data connection to end.
		JRST NNFEN2]
	OUT(NETO,("
.
"))
	.NETS NETO,

NNFEN2:	CALL NTR2XX		; Look for 256 or 252 or something like that
	 RET			; Failed
	SKIPE NT%FIL		; Check out type carefully
	 JRST [	CAIE A,252.	; If MLFL, look for 252. or 256.
		 CAIN A,256.	; aren't standards wonderful?
		  JRST POPJ1	; Win.
		JRST .+1]	; Hmm, accept but state suspicions.
	CAIN A,256.		; 256. is "correct" reply for mail.
	 JRST POPJ1		; Win
	RET

];$$FTP

SUBTTL TCP/SMTP routines - NSMICP, NSMINI

NSMSOC:	31	; Port number to use for TCP SMTP connections.

; NSMICP - Establish TCP connections with SMTP server
;	NTHOST/ host addr to connect to
; Returns .+1 if failed, with A/ error #.
; Returns .+2 if wins.

NSMICP:	SYSCAL TCPOPN,[	CIMM NETI ? CIMM NETO
			[-1] ? NSMSOC ? NTHOST
			CERR A]
	 JRST [	HRLI A,NCE$SY	; If fail, return syscal error # in RH(A)
		RET]
	;; Wait until TCP connection open.
	;; Note that timeout of the NETBLK is unlikely since specified time 
	;; is larger than default global timeout ICPTMO.
	;; [ Actually, this comment was false on 5/15/88 since ICPTMO had
	;; been adjusted upwards far enough that the NETBLK sometimes timed
	;; out.  This resulted in a confusing message in the STATS file
	;; complaining that %NSRFS was a "bad state"!  I have just set the
	;; timeout here to 5 minutes.  Lets hope that ICPTMO never gets
	;; -that- large!  -Alan ]
	MOVEI A,NETO
	NETHANG 5*60.*30.,A,%NSRFS,[%NSOPN,%NSRFN]
	 JRST [	MOVNS A		; Negate final bad state
		HRLI A,NCE$CS	; Bad State
		TRNN A,-1	; If state is closed (zero),
		 HRLI A,NCE$NO	; assume site is refusing.
		RET ]

NXSICP:				; Entry point from Chaos/SMTP code

	;; Open SMTP transaction script area on channel NETD
	UAROPN [%ARTCH+%ARTZM,,NSMSAR ? [100]]
	OUT(NETD,OPEN(UC$UAR,NSMSAR))
	CALL NTR2XX		; Get initial greeting
	 JRST [	MOVNS A
		JUMPE A,NSMICL
		HRLI A,NCE$GR	; Bad greeting?
		RET ]
	OUT(NETO,OPEN(UC$BUF,,,[8.]))	; Set up buffered 8-bit chan
	OUT(NETO,("HELO "),TZ(OWNNAM),EOL,FRC)
	.NETS NETO,
	OUT(NETD,("HELO "),TZ(OWNNAM),EOL)	; Record SMTP transaction
	CALL NTR2XX		; Just flush the response...
	 CAIA
	  JRST POPJ1
	MOVNS A
	SKIPE A
	 JRST [	HRLI A,NCE$HR
		RET ]
NSMICL:	HRLI A,NCE$IO		;Here if unexplained net I/O lossage.
	RET			;Do not skip if error.


; I think there is a bug which makes a Bad Greeting (code 0) error
; be returned before NTRPLY has been called. This looks like this:
; 
; o A conn is made and an NCE$GR happens and is correctly processed.
; o A subsequent conn attempy to another host fails completely and
;    the NTRBUF/NTRCNT string is still full.
; o An NCE$GR error is processed, using the old string.
;
;
;  -- CSTACY 6:26pm  Saturday, 12 October 1985


; NSMINI - Set up for a SMTP Mail or Send.
;	Non-skip return if fails, with bad reply code in A.

NSMINI:	CALL NSMDOP	; Get delivery option
	OUT(NETO,TC(A),(" FROM:"),LABR,CALL(NSMRTP),RABR,EOL,FRC)
	.NETS NETO,
	; Record the SMTP transaction.
	OUT(NETD,TC(A),(" FROM:"),LABR,CALL(NSMRTP),RABR,EOL)
	CALL NTR2XX	; Get reply code.
	 CAIA
	  JRST POPJ1	; Success!
	CAIGE A,500.	; Bad syntax or something?
	 RET		; No, assume temp err.
	CSTAT (,(|...error for |),LABR,CALL(NSMRTP),RABR,(|="|),CALL(NTRSHO),(|", trying <>.|),FRC)
	OUT(NETO,("RSET"),EOL,FRC)
	.NETS NETO,		; Some sites apparently want to reset
	CALL NTR2XX		; Should always win.
	 NOP			;  Eh?
	CALL NSMDOP		; Get delivery option again
	OUT(NETO,TC(A),(" FROM:<>"),EOL,FRC)	; This path is always valid.
	.NETS NETO,
	OUT(NETD,TC(A),(" FROM:<>"),EOL)	; Record SMTP transaction
	CALL NTR2XX
	 RET
	JRST POPJ1


; NSMDOP - figure out SMTP delivery option

NSMDOP:	MOVE A,[ASCNT [MAIL]]	; Assume Mailing.
	SKIPN SORMSW		; Hmmm, really sending?
	 RET			;  Nope, bye.
	SKIPLE SENDSW		; Yup, figure out what kind.
	 MOVE A,[ASCNT [SAML]]	; 1 => Send And MaiL.
	SKIPE SENDSW
	 MOVE A,[ASCNT [SEND]]	;-1 => SEND only.
	SKIPN SENDSW
	 MOVE A,[ASCNT [SOML]]	; 0 => Send Or MaiL.
	RET			; Done


; NSMRTP - output return-path on standard output (doesnt have angle
;		brackets or anything)
;	Assumes current LSE is that of message being sent!!!!

NSMRTP:	PUSHAE P,[A,B]
	FINDA A,[A$SMRP,,[$LLLST(L)]]	; Maintainer for this mail?
	 CAIA			   	;  If so, output verbatim.
	  JRST [ MOVE B,LISTAR(A)+1	;  Check length first.
		 TLNN B,-1		;  If null string,
		  JRST POPBAJ		;   don't print anything.
		 OUT(,TLS(A))		;  (Don't add ourselves either!)
		 JRST POPBAJ ]
	FINDA A,[A$SRTP,,[$LLLST(L)]]	; Return-path exists?
	 JRST NSMR20
	; Moby KLUDGE to see if return-path already has routing in it.
	; Goddam SMTP protocol!
NSMR10:	MOVE B,LISTAR(A)+1		; Get SPT to return path.
	TLNN B,-1			; See if it's null.
	 JRST POPBAJ			; Yeah, don't print anything.
	OUT(,("@"),TZ(OWNNAM))		; Aha, add ourselves.
	ADD B,$LSLOC(L)			; Make ASCNT ptr to return-path.
	HRLI B,440700
	ILDB B,B			; Get 1st char of string.
	CAIN B,"@
	 SKIPA B,[",]			; If already routing, delim is comma
	  MOVEI B,":			;  Else gotta be colon (barf barf).
	OUT(,C((B)),TLS(A))
	JRST POPBAJ
NSMR20:	FINDA A,[A$CSN,,[$LLLST(L)]]	; Claimed-from exists?
	 CAIA
	  JRST NSMR30
	FINDA A,[A$SNM,,[$LLLST(L)]]	; Nope, plain sender-name?
	 JRST POPBAJ			; Ugh, couldn't find!
NSMR30:	FINDA B,[A$SNH,,[$LLLST(L)]]	;Sender at foreign host?
	 JRST [ OUT(,TLS(A),("@"),TZ(OWNNAM))	; No.
		JRST POPBAJ ]
	MOVE B,LISTAR(B)+1		; Get host # it's from
	OUT(,("@"),TZ(OWNNAM),(":"),TLS(A),("@"),HST(B)) ; Yes, cons path
	JRST POPBAJ



; NSMRCP - Pass on a recipient name
;	B/ ASCNT to name
;
; The argument to an SMTP "RCPT TO" command can be either 
; a forward-path (which will end in a mailbox) or simply a mailbox.
; In the simpler case, our ASCNT rcpt name will be missing the @HOST
; part, so we need to put that back in.  You might think it would be
; reasonable to leave it off, but some hosts (such as XEROX) don't
; default it to the local host; they complain.

NSMRCP:	PUSHAE P,[A,B,C,D]
	MOVE A,B		   ; Copy recipient name ptr.
	OUT(NETO,("RCPT TO:"),LABR,TC(A))
	HLRZ C,A		   ; Get rcpt string length
	HRLI B,440700		   ; Look at rcpt string.
NSMRC1:	SOJL C,NSMRC2
	ILDB D,B		   ; If not a source routing forward-path
	CAIE D,"@		   ; it needs to be a mailbox.
	 JRST NSMRC1
NSMRC2:	SKIPGE C
	 OUTCAL(NETO,("@"),HST(N)) ;   Output as <Foo@BAR>.
	OUT(NETO,RABR,EOL,FRC)	   ; Finish off.
	.NETS NETO,		   ; Force out.
	;; Now record the SMTP transaction.
	OUT(NETD,("RCPT TO:"),LABR,TC(A))
	SKIPGE C
	 OUTCAL(NETD,("@"),HST(N))
	OUT(NETD,RABR,EOL)
	POPAE P,[D,C,B,A]
	CALL NTR2XX
	 RET
	JRST POPJ1


; NSMBEG - Initiate message transfer for SMTP.
;	A/ ASCNT ptr to rcpt string to use.  If nonzero,
;		set up for this (single) recipient.
; Clobbers B
; Returns .+1 for error, A/ FTP-type reply code.  -1 means temp net error.
; Returns .+2 if success
;
; Note that NSMINI has already called by NETSND to do the HELO command.

NSMBEG:	JUMPE A,NSMBG5
	MOVE B,A
	CALL NSMRCP		; Arg in B
	 RET
NSMBG5:	OUT(NETO,("DATA"),EOL,FRC)
	.NETS NETO,
	OUT(NETD,("DATA"),EOL)	; Record SMTP transaction
	CALL NTR3XX
	 RET
	CAIE A,354.
	 RET
	JRST POPJ1

; NSMDAT - Send SMTP message text.
;	Mainly must worry about transparency convention.
;	B/ ASCNT ptr to message text.
; Clobbers B

NSMDAT:	PUSH P,C
	HLRZ C,B		; Get # chars in text
	JUMPE C,NSMDT8
	HRLI B,440700
	PUSHAE P,[B,C]
NSMDT2:	ILDB A,B
NSMDT3:	CAIN A,".		; Line starts with period?
	 JRST NSMDT5
	CAIA
NSMDT4:	 ILDB A,B
	CAIN A,^M		; This is end of a line?
	 JRST [	SOJLE C,NSMDT5
		ILDB A,B	; Get next char
		CAIE A,^J
		 JRST NSMDT3	; Not LF, but assume EOL seen anyway.
		SOJG C,NSMDT2	; CR-LF, so check next char (start of line)
		JRST NSMDT5]	; EOF, send off what we've got.
	SOJG C,NSMDT4

	; Text up to this point is OK, send it off.
NSMDT5:	D7BPT B			; Back up the BP to period
	EXCH B,-1(P)		; Save it and restore old BP
	EXCH C,(P)		; Save current cnt, restore old
	SUB C,(P)		; Find # chars we went over
	OUT(NETO,S(C,B))	; Output text thus far!
	CAIN A,".		; Did we stop cuz of a period?
	 OUTCAL(NETO,("."))	; Yeah, must insert extra one.
	MOVE B,-1(P)
	MOVE C,(P)
	SOJG C,[IBP B ? JRST NSMDT4]

	POPAE P,[C,B]
NSMDT8:	POP P,C
	OUT(NETO,FRC)		; Ensure buffer forced out
	SYSCAL FINISH,[MOVEI NETO]	; Wait for transmission ACK
	 JRST [	STAT (,("FINISH call failed - "),ERR)
		MOVEI A,MR$TEH	; Temp err for host.
		RET]
	JRST POPJ1

; NSMDON - Terminate SMTP message transaction, see if it won or not.
; Returns .+1 if error, A/ reply code
; Returns .+2 if won

NSMDON:	OUT(NETO,EOL,("."),EOL,FRC)	; Force out terminating line
	.NETS NETO,
	OUT(NETD,EOL,("."),EOL)	; Record SMTP transaction
	CALL NTR2XX		; Expect a 2xx reply
	 RET			; Shit.
	JRST POPJ1


; NSMBYE - Called before disconnecting from an SMTP server.
; Sends a QUIT command and skip returns.

LVAR BYEBYE:	0		; Flag to prevent NSMBYE recursion.

NSMBYE:	PUSHAE P,[A]
	SKIPGE NDHOST		; Also, if no host to say bye to
	 JRST NSMBY9		;  punt.
	MOVE A,BYETMO		; Whatever the timeout is.
	CALL NETRAP		; Setup to trap IOC and timeout.
	 JRST [	CSTAT (,("...NSMBYE timed out"))
		JRST NSMBY9 ]
	OUT(NETO,("QUIT "),EOL,FRC)
	.NETS NETO,
	OUT(NETD,("QUIT "),EOL)	; Record transaction
	CALL NTR2XX		; "221 See Ya - Closing transmission channel."
	 NOP			;    Who cares?
;	STAT (,(|  Said SMTP goodbye, server replied "|),CALL(NTRSHO),(|"|))
	CALL NERESET		; Flush error context off stack.
NSMBY9:	POPAE P,[A]
	JRST POPJ1


SUBTTL CHAOSnet ICP - NKMICP, NKSICP, NKXICP

.INSRT SYSTEM;CHSDEF		; For chaosnet defs

LVAR	CHAPKT:	BLOCK %CPMXW	; Chaosnet packet goes here

; NKMICP - Connect to Chaosnet host for MAIL.
;	NTHOST - Host #
; Returns .+1 if failed,
;	A - error #
; Skips if won.

NKMICP:	MOVE A,[.BYTE 8 ? "M ? "A ? "I ? "L]
	MOVEM A,CHAPKT+%CPKDT	; Contact name of MAIL
	CALL NKXICP		; Try sending the RFC
	 RET			; Lost, pass back to caller
	OUT(NETO,OPEN(UC$BUF,,,[8.]))	; Set up buffered 8-bit channel.
	OUT(NETD,OPEN(UC$XCT,[CALL NKBOUT]))
	AOS (P)			; Won!
	RET			; Done.

; NKSICP - Connect to Chaosnet host for SMTP.
; Same args.  Jumps off into the guts of the TCP/SMTP open routine.
; At the moment this causes it to return the same as NKMICP.

NKSICP:	MOVE A,[.BYTE 8 ? "S ? "M ? "T ? "P]
	MOVEM A,CHAPKT+%CPKDT	; Contact name of SMTP
	CALL NKXICP		; Try sending the RFC
	 RET			; Lost, pass back to caller
	JRST NXSICP		; Won, jump into common SMTP ICP code.

; NKXICP - common worker routine.

NKXICP:	SYSCAL CHAOSO,[CIMM NETI ? CIMM NETO ? CIMM 5 ? CERR A]
	 JRST [	HRLI A,NCE$SY ? RET]
	MOVE A,[.BYTE 8 ? %CORFC ? 0 ? 0 ? 4]	; 4 is byte count
	MOVEM A,CHAPKT
	SETZM CHAPKT+%CPKD	; Clear out destination
	MOVE A,NTHOST		; Host might not be in N (internet forwarding)
	DPB A,[CHAPKT+$CPKDA]	; Stick in host number.
	SYSCAL PKTIOT,[CIMM NETO ? CIMM CHAPKT ? CERR A]	; Send out the RFC
	 JRST [	HRLI A,NCE$SY ? RET]
	MOVEI A,NETO
	.SEE NSMICP ; for the explanation of why this timeout is so large.
	NETHANG 5*60.*30.,A,%CSRFS,[%CSOPN]	; Wait for it to become open.
	 JRST [	MOVSI A,NCE$CS ? RET]
	AOS (P)				; Won, open!
	RET

SUBTTL CHAOSnet routines - NKBEG, NKXRCP, NKSND, NKEND

LVAR NTCCON:	0 	; -1 if connection is already used

; NKMINI - Initializes for a Chaosnet mail connection.
;	Necessary in order to do "invisible" reconnect if any
;	messages have already been sent during this "connect", since
;	the CHA/MAIL protocol closes the real connection after each message.

NKMINI:	AOSE NTCCON	; If reconnect is necessary
	 JRST POPJ1	; Nope, connection has had no messages yet.
	MOVE A,ICPTMO
	CALL NETRAP
	 JRST NKINI8	; IOC error or timeout.
	CALL NKDISC
	MOVE A,NTHOST
	CALL NKMICP
	 JRST NKINI7	; Net error of some kind
	CALL NERESET
	JRST POPJ1	; Won, return winningly.

NKINI7:	MOVE B,A
	CALL NERESET
	MOVE A,B
NKINI8:	CSTAT (,(" ...Re-ICP "),CALL(NTICPE),FRC)
	CALL NTDISC
	RET

; NKMBEG - Initiates mail-text transfer on Chaosnet connection.
;	A - ASCNT ptr to rcpt string to use.  If non-zero, must
;		set up for this rcpt...
; Returns .+2 if success
; Returns .+1 for either temp or perm errors,
;	A - FTP-type reply code.  If -1, some type of net error.

NKMBEG:	JUMPE A,NKBEG3
	MOVE B,A
	CALL NKXRCP	; Send rcpt name if any
	 RET		; Ugh, failed.  Return error code of NKXRCP.
NKBEG3:	OUT(NETD,EOL)	; Okay, send a null line to initiate.
	AOS (P)		; Needn't wait for any reply.
	RET

NKXRCP:	TLNN B,-1		; Ensure non-null
	 JRST [	STAT (,("NULL RCPT!  Forcing failure."))
		MOVEI A,599.	; Permanent failure code.
		SETZ B,		; No error string available.
		RET]
	OUT(NETD,TC(B))		; Output rcpt string
	SKIPE NTRTSW		; If routing thru gateway,
	 OUTCAL(NETD,("@"),HST(NDHOST))	; Specify real destination.
	OUT(NETD,EOL)
	CALRET NKREPX


; NKSEND - Tries to open a Chaosnet SEND connection.
;	A - rcpt list
;  	    Should be non-zero, must set up for this rcpt.
;	N - host we should connect to.
;
; Chaosnet SENDs are weird because the recipient is specified as part
; of the connection process, rather than after a connection is open.
; This does not quite fit in the usual transaction model of things.
; We implement the SEND protocol by pretending that ICPs always
; win, and then we really do all the work during the "begin" phase.
; If you have a better idea of how to do this, let me know.

NKSEND:	PUSHAE P,[C,D,E]
	JUMPE A,[ MOVEI A,560.
		  SETZ B,
		  JRST NKSE80 ]
	MOVE E,A		; Stash away ASCNT ptr.
	SYSCAL CHAOSO,[CIMM NETI ? CIMM NETO ? CIMM 5 ? CERR A]
	 JRST [	SETO A,
		SETZ B,
		JRST NKSE90 ]
	;; Form packet.
	MOVEI TT,%CORFC		; Opcode is Request for Connection.
	DPB TT,[$CPKOP+CHAPKT]
	SETZM CHAPKT+%CPKD	; Clear out destination
	DPB N,[CHAPKT+$CPKDA]	; Stick in host number.
	;; Stuff the contact name and recipent name down the packet.
	MOVE B,[440800,,CHAPKT+%CPKDT]	; Bp to packet.
	MOVE A,[ASCII /SEND /]	; Contact name of SEND.
	MOVE C,[440700,,A]
	MOVEI D,5.
	CALL RFCSTF		; Stuff it down.
	HRLZI C,440700
	HRR C,E			; Make Bp to ASCII rcpt string.
	HLRZ D,E		; D gets number of bytes in it.
	CALL RFCSTF		; Stuff it down.
	ADDI D,5.		; Include contact name in count.
	DPB D,[$CPKNB+CHAPKT]	; Remember how many bytes in packet.
	;; Send out the RFC
	SYSCAL PKTIOT,[CIMM NETO ? CIMM CHAPKT ? CERR A]
		 JRST [	SETO A,
			JRST NKSE90 ]
	MOVEI A,NETO		; Now wait for full connection.
	NETHANG 15.*60.,A,%CSRFS,[%CSOPN]
	 CAIA
	 JRST [	MOVEM N,NDHOST	;  Say connected to this host.
		MOVEI A,250.	;  If got this far, rcpt accepted.
		OUT(NETO,OPEN(UC$BUF,,,[8.]))
		OUT(NETD,OPEN(UC$XCT,[CALL NKBOUT]))
		MOVEM N,NDHOST	;  Say connected to this host!
		AOS -3(P)	; Skip return if conn attempted.
		JRST NKSE90 ]
NKSE70:	SETZ B,
	CALL NKBSRP		; Lost, get reply string and code.
	 MOVEI A,560.		;  Else assume random permanent error.
NKSE80:	CSTAT (,SP,("Conn="),D(A),SP)
NKSE90:	POPAE P,[E,D,C]
	RET


SUBTTL CHAOSnet Reply parsing

NKREPX:	CALL NKOFRC	; Force previous output.
NKREP2:	CALL NKIREP	; Get reply.
	 RET
	CAIGE A,400.	; If less than 4xx,
	 AOS (P)	; consider it a win.
	RET

NKIREP:	MOVE U1,NTTYPE		; Check protocol.
	CAIN U1,NT$CHS		; If SENDing
	 JRST [	MOVEI A,260.	;  since we got this far
		MOVEM A,REPLYC	;  things must have worked.
		CSTAT (,SP,LPAR,("SENT OK"),RPAR)
		JRST NKIRP8 ]
	MOVE A,[440700,,NTRBUF]	; Bp to reply buffer.
	MOVEM A,NTRPT
	SETZM NTRCNT
	.IOT NETI,A		; Get 1st char...
	CAMN A,[-1,,3]		; EOF?
	 JRST [	SETO A, ? RET]
	PUSH P,B
	SETZ B,
	CAIN A,"-		; Negative response?
	 MOVEI B,560.
	CAIN A,"%		; Temporary error?
	 MOVEI B,460.
	CAIN A,"+		; Positive response?
	 MOVEI B,260.
	JUMPE B,[SETO A,	; None of them, assume network error.
		PJRST POPBJ]
	MOVEM B,REPLYC		; Got reply char, save result.
	
NKIRP3:	AOS B,NTRCNT
	CAIL B,NTRCMX
	 JRST [	SOS NTRCNT
		JRST NKIRP4]
	IDPB A,NTRPT		; Deposit char in buffer.
NKIRP4:	.IOT NETI,A		; Get another
	CAMN A,[-1,,3]
	 JRST [	SETO A, ? PJRST POPBJ]
	CAIE A,215		; Chaosnet <NL>?
	 JRST NKIRP3		; Nope, loop to store.
	POP P,B
	MOVE A,REPLYC
NKIRP8:	AOS (P)
	RET

; NKBSRP - Process RFC failure (net reply from refusal datagram)
; A/ connection state
; Returns +1 if net lossage
;         +2 if CLOSED,
;                  error string in the net reply buffer
;                  error code in A

NKBSRP:	PUSHAE P,[B,C]
	SETZM NTRCNT
	SETZM NTRBUF		; Clear buffer.
	MOVE T,[NTRBUF,,NTRBUF+1]
	BLT T,NTRBOV
	MOVE T,[440700,,NTRBUF]
	MOVEM T,NTRPT		; Set up reply pointer.
	CAIE A,%CSCLS		; If not received CLS packet
	 JRST NKBSR9		;  lossage.
	SYSCAL PKTIOT,[	CIMM NETI ? CIMM CHAPKT ? CERR A]
	 JRST NKBSR9
	LDB T,[$CPKOP CHAPKT]	; Get packet type.
	CAIE T,%COCLS		; Should be CLS.
	 JRST NKBSR9		;  Read wrong packet??
	LDB A,[$CPKNB CHAPKT]	; Count of chars in packet.
	MOVE B,[440800,,%CPKDT+CHAPKT]	; Bp to packet data.
	MOVE C,[440700,,NTRBUF]	; Bp to reply string.
	SETZM NTRCNT		; Length of reply string.
NKBSR1:	SOJL A,NKBSR7		; Gobble it all down.
	ILDB T,B		; Get a char.
	ANDI T,177		; ASCIIfy.
	IDPB T,C		; Store.
	AOS NTRCNT		; Keep count.
	CAME T,^M		; ^M might end reply.
	 CAIA
	CAIE T,^J		; ^J might end reply.
	 JUMPN T,NKBSR1		; End of buffer ends reply.
NKBSR7:	SETOM NTRPKT		; Say reply string set up.
	LDB C,NTRPT		; Check first character.
IFN $$450, MOVEI A,450.		; Assume permanent error.
.ELSE	   MOVEI A,550.		; Assume permanent error.
	CAIN C,"%		; If temporary error
	 MOVEI A,460.		;  Say so.
NKBSR8:	AOS -2(P)		;Winskip.
NKBSR9:	POPAE P,[C,B]
	RET


SUBTTL	CHAOSnet subroutines

NKSND:	OUT(NETD,TC(B))
	CALL NKOFRC
	JRST POPJ1

NKEND:	CALL NKOFRC
	MOVE A,[.BYTE 8 ? %COEOF ? 0 ? 0 ? 0]	; Send an EOF
	MOVEM A,CHAPKT
	SYSCAL PKTIOT,[CIMM NETO ? CIMM CHAPKT ? CERR A]
	 JRST [	MOVNS A ? RET]
	SYSCAL FINISH,[CIMM NETO]
	 JFCL		;Wait for EOF acknowledged or connection closed
	SETOM NTCCON	;Server will close connection after replying
	CALRET NKREP2


; NKBOUT - Chaosnet output

NKBOUT:	CAIL U1,10	; Idiotic translations
	 CAILE U1,15
	  CAIA
	   XCT -10(U1)+[TRO U1,200	; Backspace
			TRO U1,200	; Tab
			RET		; Linefeed
			JFCL		; Vert tab
			TRO U1,200	; Formfeed
			TRO U1,200	; Car ret
			]
	PUSH P,OC
	MOVEI OC,NETO
	STDOUT		; Output translated char on NETO channel.
	POP P,OC
	RET

NKOFRC:	OUT(NETO,FRC)	; Force output,
	.NETS NETO,	; and ask ITS to do same.
	RET



; RFCSTF - Given an ASCII Bp in C and a count in D, stuff 8bit down B.
; Does not smash D.  Always returns.

RFCSTF:	MOVEI TT,0	; TT counts bytes in packet.
RFCST1:	ILDB T,C	; Get char of rcpt name.
	CAML TT,D	; When all the bytes are stuffed
	 JRST RFCST9	;   all done.
	IDPB T,B	; Stuff a byte.
	CAIGE TT,%CPMXC-1 ; Make sure we are not overstuffed!
	 AOJA TT,RFCST1	; Get another byte.
RFCST9:	RET


SUBTTL	Miscellaneous routines

IFE $$DQ,{			; Non domain version

; "NHMLTX" - routine skips if host in A is a Multics.
NHMLTX:	PUSHAE P,[A,B,D]	;save ACs clobbered by HSTSRC
	MOVE B,A
	CALL RESOLV"HSTSRC		;find out about this host
	 JRST NHMLT9		;unknown host presumed not to be a Multics
	HLRZ D,RESOLV"STLSYS(D)	;get pointer to system name
	ADD D,RESOLV"HSTADR	;relocate
	MOVE A,[ASCII/MULTI/]
	MOVE B,[ASCII/CS/]
	CAMN A,(D)
	 CAME B,1(D)
	  CAIA
	   AOS -3(P)		;It's a Multics; skip return
NHMLT9:	POPAE P,[D,B,A]
	RET

; "NHITS" - routine skips if host in A is an ITS
NHITS:	PUSHAE P,[A,B,D]	;save ACs clobbered by HSTSRC
	MOVE B,A
	CALL RESOLV"HSTSRC	;find out about this host
	 JRST NHITS9		;unknown host presumed not to be an ITS
	HLRZ D,RESOLV"STLSYS(D)	;pointer to system name
	ADD D,RESOLV"HSTADR	;relocate
	MOVE A,[ASCII/ITS/]
	CAMN A,(D)
	 AOS -3(P)		;It's ITS; skip return
NHITS9:	POPAE P,[D,B,A]
	RET

}				; Non-domain version
.ELSE {				; Domain version

; "NHMLTX" - routine skips if host in A is a Multics.
NHMLTX:	PUSHAE P,[A,B,C,D]	; Save acs
	DMOVE C,[ASCII "MULTICS"]	; What we are looking for
	JRST NHOPSY		; Join common code

; "NHITS" - routine skips if host in A is an ITS.
NHITS:	PUSHAE P,[A,B,C,D]	; Save acs
	DMOVE C,[ASCII "ITS" ? 0]	; What we are looking for
;	JRST NHOPSY		; Join common code


; Common code for above routines.  Beware pushing and popping,
; and beware of changes to QNAME consing in RESOLV"HSTINF.
NHOPSY:	PUSHAE P,[C,D]		; Save argument for a little while
	MOVE B,A		; B gets host number
	MOVE A,[440700,,DQBUF]	; A gets buffer pointer
	CALL RESOLV"HSTSRC	; Look up host name
	 JRST NHOPS8		; Lost
	MOVE A,[440700,,DQBUF]	; Use same buffer for input and output
	MOVE B,[440700,,DQBUF]	; (at no time do my fingers leave my hand)
	SETZM DQLUZ		; Paranoia (this -isn't- a 255. max hostname)
	CALL RESOLV"HSTINF	; Look up the host's opsys
	 JRST NHOPS8		; Lost
	SKIPE DQLUZ		; Have we just randomly trashed memory?
	 JSR AUTPSY		; Oh shit.
	MOVEI C,10.		; Ten chars max
	SETO D,			; Haven't found null yet
NHOPS0:	SKIPE D			; Copy second string (opsys), word aligned
	 ILDB D,B		; and null terminated.  Pointers returned
	IDPB D,A		; by HSTINF happen to do the right thing.
	SOJG C,NHOPS0		; Have interesting part of string
	POPAE P,[D,C]		; Get back argument
	CAMN C,DQBUF		; Got a match?
	 CAME D,DQBUF+1
	  JRST NHOPS9		; Nope
	AOS -4(P)		; Yup, skip return (cruft city, should be
	JRST NHOPS9		; a co-routine to handle this stuff)
NHOPS8:	ADJSP P,-2		; If the stack is intact after this it's
NHOPS9:	POPAE P,[D,C,B,A]	; a bloody miracle.
	RET

}				; .ELSE $$DQ

	;special hack to get into multics
NTMLTX:	PUSH P,A
	FWRITE NETO,[[USER NETML
]]
	.NETS NETO,
	MOVEI A,330.
	CALL NTR3XX		; First reply should be passwd request.
	 JRST POPAJ
	FWRITE NETO,[[PASS NETML
]]
	.NETS NETO,
	MOVEI A,230.		; Look for "FTP server ready"
	CALL NTRNXX
	 CAIA			; Lost...
	  AOS -1(P)
	POP P,A
	RET

CONSTANTS	;so as not to clutter up outside stuff if we can help it.
