diff --git a/build/build.tcl b/build/build.tcl
index 21df7c24..6808bfb3 100644
--- a/build/build.tcl
+++ b/build/build.tcl
@@ -502,6 +502,13 @@ respond "*" ":link device;jobdev chaos,device;jobdev cha\r"
respond "*" ":link syseng;netwrk 999999,sysnet;netwrk >\r"
+# CHAOS ARPA/NCP/TCP gateway
+respond "*" ":midas sysnet;_arpa\r"
+expect ":KILL"
+respond "*" ":link device;chaos arpa, sysnet;arpa bin\r"
+respond "*" ":link device;chaos ncp, sysnet;arpa bin\r"
+respond "*" ":link device;chaos tcp, sysnet;arpa bin\r"
+
respond "*" ":midas .;ts redrct_sysnet;redrct\r"
expect ":KILL"
diff --git a/doc/programs.md b/doc/programs.md
index b3d7d62f..d1bbf7e1 100644
--- a/doc/programs.md
+++ b/doc/programs.md
@@ -8,6 +8,7 @@
- ARCDEV, transparent file system access to archive files.
- ARCSAL, archive salvager.
- ARGUS, alerts you when specified users login or logout.
+- ARPA, gateway from Chaosnet to Arpanet and Internet.
- ATSIGN CHAOS, Chaosnet support.
- ATSIGN DEVICE, load device drivers.
- ATSIGN TARAKA, starts dragons.
diff --git a/src/sysnet/arpa.66 b/src/sysnet/arpa.66
new file mode 100755
index 00000000..feeff30a
--- /dev/null
+++ b/src/sysnet/arpa.66
@@ -0,0 +1,611 @@
+;-*-midas-*-
+title arpa
+;chaosnet to arpanet server, rfc has hostname and optional (octal) socket number
+
+;data is just forwarded from chaosnet connection to arpanet connection and vice versa,
+;except:
+; data opcode 201 from the chaosnet means do an INS on the arpanet connection
+; the rest of this packet is treated normally
+; data opcode 210 means establish auxiliary connection (for gateway FTP), the
+; the data portion of this packet contains 8 bytes of the (gensym'ed presumably)
+; contact name for the chaos end of the auxiliary connection, the server will do
+; a listen on that name; and 2 words of the arpanet socket number to connect to.
+; when a rfc is received for that contact name, a half-duplex arpanet connection
+; is established and data then forwarded from/to it to/from the chaosnet connection
+; in the same manner as the main connection.
+; For TCP it is much worse, it starts listening on TCP and chaos and waits for a
+; 211 packet before checking that TCP is open and opening chaos. This is needed
+; with the screwy way TCP FTP works.
+; Using 212 instead of 210 gensyms the local port and sends it back as
+; a 300 packet on the main data connection.
+; Made it send local internet host number in 300 packet, as four bytes
+; following the local port. (Users must check packet length since other
+; servers might not have it) -GZ 10/2/84
+;
+; hacked a little in preparation for TCP. -dcp 12/29/82
+
+f=0
+a=1
+b=2
+c=3
+d=4
+e=5
+n=6
+bp=7
+t=10
+tt=11
+s=12
+cch=13
+nch=14
+p=17
+
+frauxo==400000
+frcaxo==200000
+
+.insrt system;chsdef
+
+%cobrk==%codat+1 ;R11 net INS magic
+icpch==0
+netich==2
+netoch==3
+auxich==4
+auxoch==5
+filech==7
+chaich==10
+chaoch==11
+caxich==12
+caxoch==13
+
+call=pushj p,
+ret=popj p,
+return=popj p,
+define syscal name,args
+ .call [setz ? sixbit /name/ ? args ((setz))]
+termin
+
+$$hst3==1
+$$allnet==1 ;let them go anyplace
+$$arpa==1 ;support Arpanet, we do Chaos net ourselves
+$$icp==1 ;icp routine
+$$tcp==1
+$$connect==1
+$$hostnm==1 ;hostname lookup routines
+$$ownhst==1 ;Own hostname in standard format
+$$symlook==1
+$$hstsix==1
+$$analyze==1 ;analyze routine
+ifndef $$logging,$$logging==0
+
+.insrt syseng;netwrk >
+
+;storage
+debug: 0 ;non-zero => .value on barfage
+whoami: 0 ;sixbit of program name (ARPA, NCP or TCP)
+usrhst: 0 ;user host number
+lclhst: 0 ;local host number
+frnhst: 0 ;arpanet host number
+icpskt: 0 ;icp socket
+chapkt: block %cpmxw ;chaosnet packet goes here
+hstnam: block 10 ;stick the host name here
+npdl==100
+pdl: block npdl
+
+;interrupt handler
+tsint: loc 42
+ -tsintl,,tsint
+loc tsint
+ p
+ %piioc ? 0 ? -1 ? -1 ? iocerr ;iocerr ints highest priority
+ 0 ? 1_netich ? 0 ? 1_chaich\1_netich\1_caxich\1_auxich ? netint
+ 0 ? 1_chaich ? 0 ? 1_netich\1_chaich\1_caxich\1_auxich ? chaint
+ 0 ? 1_auxich ? 0 ? 1_chaich\1_netich\1_caxich\1_auxich ? auxint
+ 0 ? 1_caxich ? 0 ? 1_netich\1_chaich\1_caxich\1_auxich ? caxint
+tsintl==.-tsint
+
+;error handler
+die: 0
+ skipe debug
+ .value
+passon: .logout ;natural causes
+ .value
+
+sndpkt: setz ? 'pktiot ? movei 1(cch) ? setzi chapkt
+rcvpkt: setz ? 'pktiot ? movei 0(cch) ? setzi chapkt
+
+;main program
+go: .close 1, ;this can still be open from loading us
+ move p,[-npdl,,pdl-1]
+ call getme ;get my job info
+ trz f,frauxo\frcaxo ;neither auxiliary conn open yet
+ call getcha ;get the chaos connection
+ call logmein ;I know it's seven characters
+ call gethst ;get the host we are trying to send to
+ call getskt ;get the socket number to connect to
+ call getcon ;get the NCP or TCP connection
+ call getrdy ;get ready for main loop
+ jfcl
+ .hang
+
+getme: .suset [.rsname,,whoami]
+ return ;maybe error check someday
+
+getcha: movei cch,chaich
+ movei nch,netich
+ syscal chaoso,[movei chaich ? movei chaoch ? movei 5]
+ jsr die
+ move t,[.byte 8 ? %colsn ? 0 ? 0 ? 0]
+ movem t,chapkt
+ move t,[440600,,whoami] ;better not take up all six characters
+ move tt,[440800,,chapkt+%cpkdt]
+ ildb s,t
+ jumpn s,[addi s,"A-'a ;'"
+ idpb s,tt
+ movei s,1_4
+ addm s,chapkt ;increment length
+ jrst .-1]
+ .call sndpkt
+ jsr die
+ movei t,30.*60.
+ skipe debug
+ hrloi t,177777 ;wait forever if debugging
+ syscal netblk,[movei chaich ? movei %cslsn ? t ? movem t]
+ jsr die
+ caie t,%csrfc ;did we get an rfc for this?
+ jsr die
+ .call rcvpkt ;yes, read it in
+ jsr die
+ syscal rfname,[ %climm,,chaich ;Examine Chaosnet conn
+ %clout,,a ? ;(Should be CHAOS device)
+ %clout,,tt ? %clout,,tt ;Ignore index nums
+ %clout,,usrhst ] ;Find user host address
+ return
+ifn $$logging, netwrk"log CH2TCP,[usrhst]
+ return
+
+logmein: ;and tell the system other things
+ movei a,hstpag
+ movei b,filech
+ call netwrk"hstmap ;get the hosttable
+ jsr die
+ skipe debug ;debugging?
+ return
+ ldb a,[chapkt+$cpksa] ;get source host address
+ tlo a,netwrk"nw%chs_9
+ pushj p,netwrk"hstsix ;Get sixbit of it
+ .lose
+ move tt,[sixbit /000_00/] ;convert host number to sixbit
+ ldb t,[.bp ,whoami] ;get first character
+ dpb t,[.bp ,tt] ;set it
+ ldb t,[$cpksa chapkt] ;get source host address
+ dpb t,[220300,,tt]
+ lsh t,-3
+ dpb t,[300300,,tt]
+ lsh t,-3
+ dpb t,[360300,,tt]
+ .suset [.ruind,,t] ;incoroporate user index also
+ dpb t,[000300,,tt]
+ lsh t,-3
+ dpb t,[060300,,tt]
+ move t,tt ;save copy for xuname
+ .suset [.sjname,,whoami]
+ movei n,100(tt) ;loop at most 100 times
+login: cain n,(tt)
+ jsr die
+ .call [setz
+ sixbit /LOGIN/
+ tt ? a ? setz t]
+ aoja tt,login ;error, perhaps need to try other uname
+ syscal detach,[movsi 3 ? movei %jself]
+ jsr die
+ return
+
+;now look at the connection name string and find out where the guy wants to go
+gethst: move bp,[440800,,chapkt+%cpkdt] ;point to packet's data
+ ldb n,[chapkt+$cpknb] ;get number of bytes in it
+geths1: sojl n,nohost ;didnt give enough information
+ ildb t,bp
+ caie t,40 ;look for space
+ jrst geths1
+ jumpe n,nohost
+ move a,[440700,,hstnam]
+geths2: ildb t,bp ;get char from packet
+ idpb t,a ;build ascii of hostname
+ caie t,40
+ sojge n,geths2
+ movei t,0
+ dpb t,a ;make null terminated string
+ move a,[440700,,hstnam]
+ call netwrk"hstlook ;go lookup hostname
+ jrst badhst
+ movem a,frnhst
+ move a,[netwrk"nw%arp]
+ call netwrk"ownhst ;look up our name
+ setz a,
+ tlz a,740000 ;Flush any format info
+ movem a,lclhst
+ setzm netwrk"hstadr ;can now flush hosttable
+ .core hstpag
+ jsr die
+ return
+
+;now look for socket to hook up to
+getskt: setz a, ;accumulate it here
+getsk1: sojle n,getsk2
+ ildb t,bp
+ cain t,40
+ jrst getsk2
+ cail t,"0
+ caile t,"7
+ jrst badskt
+ lsh a,3
+ addi a,-"0(t)
+ jrst getsk1
+
+getsk2: skipn a
+ movei a,1 ;default to logger
+ movem a,icpskt
+ return
+
+;now we attempt the connection
+getcon: move a,whoami
+ camn a,[sixbit /ARPA/]
+ jrst gtcarp
+ camn a,[sixbit /NCP/]
+ jrst gtcncp
+ camn a,[sixbit /TCP/]
+ jrst gtctcp
+ movei a,[asciz /Unknown gateway protocol request/]
+ call sndcls
+ jsr die
+
+gtcncp: movei a,icpch ;first of 4 channels to use
+ move b,frnhst ;host
+ move c,icpskt ;icp socket
+ move d,[40+.uai,,40+.uao] ;8 bit mode
+ move nch,a ;arg to doanal
+ call netwrk"arpicp ;try to connect up
+ jrst doanal ;failed, send CLS of why
+ return
+
+gtcarp: call gtttcp ;try tcp first
+ jrst [ move t,[sixbit /NCP/] ;tcp failed
+ movem t,whoami
+ jrst gtcncp] ;else try NCP
+ move t,[sixbit /TCP/] ;tcp succeeded
+ movem t,whoami
+ return
+
+gtctcp: call gtttcp
+ jrst fail
+ return
+
+gtttcp:
+IFN 0,[ syscal tcpopn,[movei netich ? movei netoch
+ [-1] ? icpskt ;local, foreign
+ frnhst]
+ jsr die ;failed (should timeout!)
+ movei t,15.*30. ;15 seconds
+ syscal netblk,[movei netich ? movei %nsrfs ? t ? movem tt ? movem t]
+ jsr die
+ jumple t,tcptmo
+ tlz tt,-1 ;cflush left half??
+ caie tt,%nsopn
+ cain tt,%nsinp
+ jrst gtctc2 ;winning
+ cain tt,%nscli ;CLS but input?
+ jrst gtctc2 ;still winning
+ return ;failure
+];IFN 0
+ movei a,netich
+ move b,frnhst
+ move c,icpskt
+ call netwrk"tcpcon
+ return ;failure
+gtctc2: aos (p) ;skip return
+ return
+
+;set up interrupts
+getrdy: move t,[-6,,[ sixbit /OPTION/ ? tlo %opint ; new style
+ sixbit /MASK/ ? move [%piioc]
+ sixbit /MSK2/ ? movei 1_netich\1_chaich
+ ]]
+ syscal usrvar,[movei %jself ? t]
+ .lose %lssys
+
+;won, send back opn on chaosnet connection
+ movei t,%coopn
+ dpb t,[chapkt+$cpkop]
+ .call sndpkt
+ jsr die
+ return
+
+
+fail: movei a,[asciz /TCP connection to foreign host failed/]
+ jrst sndcls
+tcptmo: movei a,[asciz /Timeout while trying to TCP connect/]
+ jrst sndcls
+badhst: skipa a,[[asciz /No such host/]] ;gave a bad host name
+badskt: movei a,[asciz /Bad icp socket/]
+ jrst sndcls
+
+nohost: movei a,[asciz /No host name specified/] ;didnt give a hostname
+sndcls: hrli a,440700
+ movei n,0 ;initialize byte count
+ move bp,[440800,,chapkt+%cpkdt] ;and byte pointer for packet
+sndcl1: ildb t,a
+ jumpe t,sndcl2
+ idpb t,bp
+ aoja n,sndcl1
+sndcl2: dpb n,[chapkt+$cpknb] ;store byte count
+ movei t,%cocls
+ dpb t,[chapkt+$cpkop] ;and opcode
+ .call sndpkt
+ jsr die
+sndcl3: cain cch,chaich
+ jrst passon
+ jrst auxfls
+
+doeof: movei cch,chaich-netich(nch)
+ movsi t,(.byte 8 ? %coeof ? 0)
+ movem t,chapkt
+ .call sndpkt
+ .lose %lssys
+ .call [ setz ;wait for output to get there
+ sixbit /FINISH/
+ setzi 1(cch)]
+ jrst sndcl3 ;dont need to close if other side does
+
+;send cls of reason for arpanet connection losing
+doanal: movei n,0
+ move bp,[440800,,chapkt+%cpkdt] ;init byte count and pointer
+ movei a,(nch)
+ call netwrk"analyz
+ jsr die
+ movei cch,chaich-netich(nch)
+ cail cch,chaich
+ caile cch,caxoch
+ movei cch,chaich
+ jrst sndcl2 ;and go send that off
+
+;guys for netwrk
+popj1: aos (p)
+cpopj: ret
+
+putchr: idpb t,bp
+ aoja n,cpopj
+
+;here if get an ioc error, check the state of the arpanet connections
+iocerr: aosn iocflg
+ jsr die ;recursive IOC errors, go away
+ .call [setz ? 'whyint ? movei netich ? movem s ? setzm s]
+ jsr die
+ tlz s,400000 ;ignore network interrupt bit
+ caie s,%nsopn ;still intact?
+ jrst iocer1 ;no, go tell chaosnet connection why it lost
+ .call [setz ? 'whyint ? movei netoch ? movem s ? setzm s]
+ jsr die
+ tlz s,400000
+ caie s,%nsrfn
+ cain s,%nsopn
+ jsr die ;random ioc error, just go away
+iocer1: ;;undefer interrupts so can get another ioc ok
+ setom iocflg' ;If recursive IOC error, give up and go away, don't loop
+ move a,-2(p) ;previous DF1 word
+ .suset [.sdf1,,a] ;undefer this first so can get IOC if get I/O int
+ move a,-1(p) ;previous DF2 word
+ .suset [.sdf2,,a]
+ jrst doeof
+
+;network interrupt, get input from arpanet and send to chaosnet
+auxint: movei nch,auxich
+ movei cch,caxich
+ jrst netin0
+
+netint: movei nch,netich
+ movei cch,chaich
+netin0: .call [setz ? 'whyint ? movei 0(nch) ? movem s ? movem s ? setzm n]
+ jsr die
+ tlz s,400000
+ cain s,%nsopn ;still open?
+ jrst intx ;yes, spurious i guess
+ caie s,%nscli
+ cain s,%nsinp
+ jrst netin1 ;input waiting, get some
+ jrst doeof ;in some wedged state, flush it
+
+netin1: move t,whoami
+ came t,[sixbit /NCP/]
+ jrst ntitcp ;not NCP, go handle TCP
+ jumple n,netin2 ;no bytes waiting, forget it
+ caig n,%cpmxc ;more than a packet's worth?
+ skipa t,n
+ movei t,%cpmxc
+ subi n,(t)
+ dpb t,[chapkt+$cpknb] ;number of bytes we will send
+ movei tt,%codat
+ dpb tt,[chapkt+$cpkop]
+ move tt,[440800,,chapkt+%cpkdt]
+ .call [setz ? sixbit/siot/ ? movei 0(nch) ? tt ? setz t]
+ jsr die
+ .call sndpkt
+ jsr die
+ jrst netin1 ;go see if there is more to come
+
+ntitcp: movei t,%cpmxc ;assume we'll read in a chaos packet's worth
+ move tt,[440800,,chapkt+%cpkdt]
+ syscal siot,[movsi 10 ? movei 0(nch) ? tt ? t] ;don't hang
+ jsr die
+ cain t,%cpmxc ;read anything
+ jrst netin2 ;nope, finish up
+ subi t,%cpmxc
+ movn t,t ;now number of characters read
+ dpb t,[$cpknb chapkt]
+ movei tt,%codat
+ dpb tt,[$cpkop chapkt]
+ .call sndpkt
+ jrst die
+ jrst ntitcp
+
+netin2: cain s,%nscli ;was is closed except for that input?
+ jrst doeof ;yes, finish up then
+intx: .call [setz ? 'dismis ? setz p]
+ jsr die
+
+;chaosnet interrupt
+caxint: movei cch,caxich
+ movei nch,auxich
+ jrst chain0
+
+chaint: movei cch,chaich
+ movei nch,netich
+chain0: .call [setz ? 'whyint ? movei 0(cch) ? movem s ? movem s ? setzm n]
+ jsr die
+ cain s,%csinc ;did chaosnet go away?
+ jrst chain3 ;yes, flush it i guess
+ hlres n ;get number of receive packets waiting
+ jumple n,intx
+chain1: .call rcvpkt ;read the next packet out
+ jsr die
+ ldb t,[chapkt+$cpkop] ;get opcode
+ caie t,%cocls
+ cain t,%colos
+ jrst chain3 ;connection gone or losing, go away
+ caie t,212
+ cain t,210 ;special escape?
+ jrst opnaux ;yes, open aux conn
+ cain t,211
+ jrst tcaux2 ;open aux conn, part two
+ caige t,%codat ;data packet?
+ jrst chain2 ;no, just ignore it
+ cain t,%cobrk ;send network INS, then treat packet as data
+ .call [setz ? sixbit/netint/ ? setzi 1(nch)]
+ jfcl
+ ldb t,[chapkt+$cpknb] ;get byte count
+ move tt,[440800,,chapkt+%cpkdt] ;point to data
+ .call [setz ? sixbit/siot/ ? movei 1(nch) ? tt ? setz t]
+ jsr die
+chain2: sojg n,chain1
+ .nets netoch, ;send off the buffered output
+ jrst chain0 ;go look for more
+
+chain3: caie cch,chaich ;main connection?
+ jrst auxfls ;no, just close aux
+ .close netoch, ;close the arpanet connections
+ .close netich,
+ jrst passon ;and go away quietly
+
+auxfls: .suset [.samsk,,[1_auxich\1_caxich]]
+ .close auxoch,
+ .close auxich,
+ .close caxich,
+ .close caxoch,
+ setzm iocflg ;DOne processing ioc error I guess
+ jrst intx
+
+;special escape, open an aux connection
+opnaux: movem t,opnaxo' ;save opcode
+ move c,chapkt+%cpkdt+2 ;get the foreign socket
+ lsh c,-4 ;normalize it
+ movei cch,caxich
+ movei nch,auxich
+ .call [setz ? 'chaoso ? movei caxich ? movei caxoch ? setzi 5]
+ jsr die
+ ldb t,[chapkt+$cpknb]
+ subi t,4
+ dpb t,[chapkt+$cpknb]
+ movei t,%colsn
+ dpb t,[chapkt+$cpkop]
+ .call sndpkt
+ jsr die
+ movei t,30.*60.
+ skipe debug
+ hrloi t,177777 ;wait forever if debugging
+ .call [setz ? 'netblk ? movei caxich ? movei %cslsn ? t ? setzm t]
+ jsr die
+ caie t,%csrfc ;did we get an rfc for this?
+ jrst intx ;no, forget it
+ .call rcvpkt ;yes, read it in
+ jsr die
+ move b,frnhst
+ move t,whoami
+ camn t,[sixbit /TCP/]
+ jrst tcpaux
+ movei a,auxich
+ trnn c,1 ;is this going to be receive?
+ movei a,auxoch ;no, send
+ movei d,140+.uai
+ trnn c,1
+ movei d,140+.uao
+ call netwrk"arpcon
+ jrst doanal ;failed, send a cls for rfc
+ movei t,%coopn
+ dpb t,[chapkt+$cpkop]
+ .call sndpkt
+ jsr die
+ .suset [.simsk,,[1_caxich\1_auxich]]
+ jrst intx
+
+tcpaux: syscal rfname,[movei netich ? movem d ? movem d]
+ .lose %lssys
+ move t,opnaxo ;opcode 212?
+ cain t,212
+ seto d, ;yes, use gensym local port
+ syscal tcpopn,[movsi 100 ? movei auxich ? movei auxoch
+ d ? c ;otherwise same local port, specified foreign port
+ b]
+ jrst doanal
+ caie t,212
+ jrst intx ;dismiss now, wait when told to
+ syscal rfname,[movei auxich ? movem t ? movem t]
+ jsr die
+ ;Send back a packet containing the local port number
+ lsh t,16.+4
+ movem t,chapkt+%cpkdt
+ movei t,2
+ skipn tt,lclhst
+ jrst tcaux1
+ lsh tt,-16.+4
+ iorm tt,chapkt+%cpkdt
+ move tt,lclhst
+ lsh tt,16.+4
+ movem tt,chapkt+%cpkdt+1
+ movei t,6
+tcaux1: dpb t,[chapkt+$cpknb]
+ movei t,300 ;binary data
+ dpb t,[chapkt+$cpkop]
+ syscal pktiot,[movei chaoch ? movei chapkt]
+ jsr die
+ jrst intx
+
+;;; TCP open, part two.
+tcaux2: movei cch,caxich
+ movei nch,auxich
+ movei b,30.*30. ;only gets called from aux connection
+ movei c,%nslsn
+tcax2b: syscal netblk,[movei auxich ? c ? b ? movem c ? movem b]
+ jrst doanal
+ jumple b,doanal
+ tlz c,-1
+ cain c,%nsrfc ;transient state
+ jrst tcax2b
+ caie c,%nsopn ;open, or
+ cain c,%nsinp ;open with input
+ jrst tcax2a
+ caie c,%nscli ;also closed with input
+ jrst doanal
+tcax2a: movei t,%coopn ;open response to chaos
+ dpb t,[chapkt+$cpkop]
+ .call sndpkt
+ jsr die
+ .suset [.simsk,,[1_caxich\1_auxich]] ;enable interrupts
+ jrst auxint ;exit through interrupt code in case closed with input
+
+
+pat": patch": block 100
+
+variables
+constants
+
+hstpag==<.+1777>/2000 ;place to map in the hosts1 file.
+
+end go