TITLE	IDENTD - TCP port user identification server
	SEARCH	MONSYM,MACSYM

;;; IDENTD server for TOPS-20 (using the IDENT protocol of RFC ????), written
;;; by Anders Andersson (Anders.Andersson@DoCS.UU.SE), Update Computer Club.
;;; This code is put into the public domain.  Do whatever you like with it.
;;; I can't provide any support, but you are welcome to tell me your opinions
;;; about it.
;;;
;;; This code makes use of the unsupported STAT% and MONRD% system calls (as
;;; does SYSDPY) to obtain information about TCP ports.  The MONRD% call needs
;;; to be installed in the monitor beforehand, by having "SYSDPY E" run with
;;; WHEEL capability at system startup if by no other means.
;;;
;;; This server operates on a single local interface only.  By default,
;;; interface #0 is used.  In order to use a different interface, deposit its
;;; number in location INTNUM before starting the server.
;;;
;;; This server is single-threaded, meaning that it may easily be blocked by
;;; a slow, broken, or plain evil client that fails to provide input.  This is
;;; the next thing that needs to be fixed (perhaps NETSRV can help)...

;;; This code was last hacked May 31, 1992.
;;; 36 >> 32!

VERMAJ=	1
VERMIN=	0
VEREDT=	^D31
VERWHO=	2

IDPORT=	^D113			;according to RFC 931, ????

ECODE=	6			;error code
TCBCNT=	7			;TCB counter
LPORT=	11			;number of local port
FPORT=	12			;number of foreign port
LHOST=	13			;address of local host
FHOST=	14			;address of foreign host

TCB:	BLOCK	<TCBSIZ==200>	;storage for retrieved TCB info
NAMBUF:	BLOCK	<^D50/5+1>	;buffer for TCP: file name string
HLABUF:	BLOCK	<HLASIZ==10>	;local host address buffer
LINBUF:	BLOCK	<<LINSIZ==^D20>/5+1>	;command line buffer
ATMBUF:	BLOCK	<<ATMSIZ==^D10>/5+1>	;command atom buffer
ANSWER:	BLOCK	<^D80/5+1>	;buffer for answer text

CSB:	EXP	<CM%XIF!0>	;no indirect files, no reparse possible
	EXP	<0,,.NULIO>	;we don't want any output from COMND%
	EXP	<-1,,[ASCIZ //]>	;null prompt
	BLOCK	1
	BLOCK	1
	BLOCK	1
	BLOCK	1
	BLOCK	1
	BLOCK	1
	EXP	<0>		;no GTJFN% block needed

TCPJFN:	BLOCK	1		;IDENT client file handle
ANSPTR:	BLOCK	1		;pointer into answer buffer
USERNO:	BLOCK	1		;number of user owning job

INTNUM:	EXP	<0>		;interface# (change this as appropriate)

EVEC:	JRST	START
	JRST	REENT
	BYTE	(3)VERWHO(9)VERMAJ(6)VERMIN(18)VEREDT

ERRTXT:	EXP	[ASCIZ /INVALID-PORT/]
	EXP	[ASCIZ /NO-USER/]
	EXP	[ASCIZ /UNKNOWN-ERROR/]

ERRIP=	1
ERRNU=	2
ERRUE=	3

START:	RESET%
REENT:	MOVEI	1,.GTHLA
	MOVEI	3,HLABUF
	MOVEI	4,HLASIZ
	GTHST%			;obtain all local addresses
	 ERJMP	FATAL
	MOVE	1,INTNUM	;this is the selected interface #
	CAIL	1,0		;negative interface #?
	 CAML	1,4		;within the range of existing interfaces?
	  JRST	NONINT		;the specified interface does not exist
	MOVE	LHOST,HLABUF(1)	;pick the corresponding local address
	HRROI	1,NAMBUF	;create a file name string for GTJFN%
	HRROI	2,[ASCIZ /TCP:/]
	MOVEI	3,0
	SOUT%
	 ERJMP	FATAL
	MOVE	2,LHOST
	MOVE	3,[NO%MAG!NO%LFL!NO%ZRO!FLD(^D11,NO%COL)!FLD(^D8,NO%RDX)]
	NOUT%
	 ERJMP	FATAL
	MOVEI	2,"-"
	IDPB	2,1
	MOVEI	2,IDPORT
	MOVEI	3,^D10
	NOUT%
	 ERJMP	FATAL
	MOVEI	2,"V"-"@"	;insert a ^V
	IDPB	2,1		;we want this source file to be mailable
	HRROI	2,[ASCIZ /#..0;CONNECTION:PASSIVE/]
	MOVEI	3,0
	SOUT%
	 ERJMP	FATAL
LISTEN:	SETZB	LPORT,FPORT	;destroy possible garbage
	HRROI	1,LINBUF	;set up CSB pointers and counters
	MOVEM	1,CSB+.CMBFP
	MOVEM	1,CSB+.CMPTR
	MOVEI	1,LINSIZ
	MOVEM	1,CSB+.CMCNT
	MOVEM	1,CSB+.CMINC
	HRROI	1,ATMBUF
	MOVEM	1,CSB+.CMABP
	MOVEI	1,ATMSIZ
	MOVEM	1,CSB+.CMABC
	MOVE	1,[GJ%SHT!GJ%OLD!0]	;establish a server port...
	HRROI	2,NAMBUF
	GTJFN%
	 ERJMP	FATAL
	MOVE	2,[FLD(^D8,OF%BSZ)!FLD(.TCMWI,OF%MOD)!OF%RD!OF%WR]
	OPENF%			;wait for a client to connect...
	 ERJMP	FATAL
	MOVEM	1,TCPJFN
	HRLM	1,CSB+.CMIOJ	;input JFN
	GDSTS%			;get info about the client
	 ERJMP	FATAL
	MOVEM	3,FHOST		;save client host
	MOVEI	ECODE,ERRIP	;default error message: invalid port
	MOVEI	1,CSB
	MOVEI	2,[FLDDB.(.CMINI)]
	COMND%
	 ERJMP	CFATAL
	TLNE	1,(CM%NOP)
	 JRST	MREPLY
	MOVEI	2,[FLDDB.(.CMNUM,CM%SDH,^D10)]
	COMND%			;parse local port number
	 ERJMP	CFATAL
	TLNE	1,(CM%NOP)
	 JRST	MREPLY
	MOVE	LPORT,2
	MOVEI	2,[FLDDB.(.CMCMA,CM%SDH)]
	COMND%			;parse a comma
	 ERJMP	CFATAL
	TLNE	1,(CM%NOP)
	 JRST	MREPLY
	MOVEI	2,[FLDDB.(.CMNUM,CM%SDH,^D10)]
	COMND%			;parse foreign port number
	 ERJMP	CFATAL
	TLNE	1,(CM%NOP)
	 JRST	MREPLY
	MOVE	FPORT,2
	MOVEI	2,[FLDDB.(.CMCFM,CM%SDH)]
	COMND%			;parse CRLF
	 ERJMP	CFATAL
	TLNE	1,(CM%NOP)
	 JRST	MREPLY
	TDNN	LPORT,[^-177777]
	 TDNE	FPORT,[^-177777]
	  JRST	MREPLY		;check that port numbers are within range
	MOVEI	ECODE,0		;no error so far
MREPLY:	HRROI	1,ANSWER	;put some port numbers back in reply
	MOVEI	3,^D10
	MOVE	2,LPORT
	NOUT%
	 ERJMP	FATAL
	MOVEI	2,","
	IDPB	2,1
	MOVE	2,FPORT
	NOUT%
	 ERJMP	FATAL
	MOVEM	1,ANSPTR
	CAIE	ECODE,0		;was there an error?
	 JRST	EREPLY		;then make this an error reply
	HRLZI	1,(TCP%NI)
	STAT%			;get number of TCBs
	 ERJMP	FATAL
	MOVE	TCBCNT,2
TCLOOP:	HRLI	1,(TCP%IX)
	HRR	1,TCBCNT
	MOVSI	2,-200
	MOVE	3,[-200,,TCB]
	STAT%			;retrieve current TCB
	 ERJMP	FATAL
	CAMN	FHOST,TCB+7	;the wanted foreign host?
	 CAME	LHOST,TCB+10	;the wanted local host also?
	  JRST	TCNEXT		;no, ignore this one
	CAMN	FPORT,TCB+11	;the requested foreign port?
	 CAME	LPORT,TCB+12	;the requested local port also?
	  JRST	TCNEXT		;no, ignore this one
	MOVEI	1,16
	HLRZ	2,TCB+6		;get monitor local job number
	MONRD%			;convert to cluster (?) global job number
	 ERJMP	FATAL
	MOVE	1,2		;find out user of job
	MOVE	2,[-1,,USERNO]
	MOVEI	3,.JIUNO
	GETJI%
	 ERJMP	FATAL
	MOVE	1,ANSPTR
	HRROI	2,[ASCIZ /:USERID:TOPS20:/]
	MOVEI	3,0
	SOUT%
	 ERJMP	FATAL
	MOVE	2,USERNO	;translate to user name string
	DIRST%
	 ERJMP	FATAL
	JRST	TCPUSH		;go tell all to the client (ptr in AC1)

NONINT:	HRROI	1,[ASCIZ /IP interface /]
	ESOUT%
	MOVEI	1,.PRIOU
	MOVE	2,INTNUM
	MOVEI	3,^D8
	NOUT%
	 ERJMP	FATAL
	HRROI	1,[ASCIZ / does not exist -- change INTNUM and continue
/]
	PSOUT%
	HALTF%
	JRST	START

TCNEXT:	AOBJN	TCBCNT,TCLOOP
	MOVEI	ECODE,ERRNU	;no matching ports in use found
EREPLY:	MOVE	1,ANSPTR
	HRROI	2,[ASCIZ /:ERROR:/]
	MOVEI	3,0
	SOUT%
	 ERJMP	FATAL
	HRRO	2,ERRTXT-1(ECODE)
	MOVEI	3,0
	SOUT%
	 ERJMP	FATAL
TCPUSH:	MOVEI	2,"M"-"@"	;be sure buf ptr is in AC1 here
	IDPB	2,1
	MOVEI	2,"J"-"@"
	IDPB	2,1
	MOVEI	3,0		;terminate string with a NUL byte
	IDPB	3,1
	MOVE	1,TCPJFN
	SETZM	TCPJFN		;don't tell client about errors after this
	HRROI	2,ANSWER
	SOUT%
	 ERJMP	FATAL
	MOVEI	2,.TCPSH	;this is our last word
	TCOPR%
	 ERJMP	FATAL
	CLOSF%
	 ERJMP	FATAL
	JRST	LISTEN		;go wait for our next client

CFATAL:	MOVEI	1,.FHSLF	;COMND% had an error, check what it is
	GETER%
	HRRZ	2,2
	CAIE	2,COMNX2	;field too long?
	 CAIN	2,COMNX3	;command too long?
	  JRST	MREPLY		;client's fault, go tell him
	CAIN	2,COMNX9	;end of input?
	 JRST	MREPLY		;also client's fault
;	JRST	FATAL		;else fall through and tell us
FATAL:	HRROI	1,[ASCIZ /JSYS error - /]
	ESOUT%
	MOVEI	1,.PRIOU
	HRLOI	2,.FHSLF
	MOVEI	3,0
	ERSTR%
	 NOP			;never mind
	 NOP			;fat chance
	HRROI	2,[ASCIZ /
/]
	PSOUT%
	MOVEI	ECODE,ERRUE	;this is not a normal error
	SKIPE	TCPJFN		;do we have a connection?
	 JRST	EREPLY		;then try to finish it and continue
	HALTF%			;else die...
	JRST	.-1		;...and stay there

	END	<3,,EVEC>