DIRECTORY Arpa USING [Address, NetNumber, nullAddress], ArpaExtras USING [MyAddress], ArpaIP USING [AllocBuffers, Buffers], BasicTime USING [Now], List USING [Nconc1, Remove], ArpaTCP USING [maxArpanetSegmentSize, Error, Port, Reason, TCPInfo], ArpaTCPLogging USING [PrintStateChange], ArpaTCPOps USING [defaultReceiveWindow, defaultRTT, maxTCPDataLength, SetDataTimeout, TCPHandle, TCPHandleRec, tcpHdrByteLength, TCPHeaderP], ArpaTCPStates, ArpaTCPTransmit USING [SendFIN, SendReset, SendSYN]; ArpaTCPStatesImpl: CEDAR MONITOR -- monitors the handleList IMPORTS Arpa, ArpaExtras, ArpaIP, BasicTime, List, ArpaTCP, ArpaTCPLogging, ArpaTCPOps, ArpaTCPTransmit EXPORTS ArpaTCPStates ~ BEGIN OPEN ArpaTCPOps, ArpaTCPStates; handleList: LIST OF REF ANY; -- list of all open TCP connections minPort: CARDINAL = 1025; -- To avoid well-known sockets and unix special sockets nextLocalPort: CARDINAL _ minPort; lastSequenceNumber: INT _ LOOPHOLE[BasicTime.Now[]]; -- used to general initial sequence numbers Close: PUBLIC PROC [handle: TCPHandle] ~ { ValidHandle[handle]; SELECT handle.state FROM listen, synSent => CloseConnection[handle, localClose]; synRcvd, established => {-- send fin after all preceeding sends ArpaTCPTransmit.SendFIN[handle]; -- send fin and ack ArpaTCPLogging.PrintStateChange[handle, finWait1]; handle.state _ finWait1; handle.reason _ localClose; }; closeWait => {-- send fin after all preceeding sends ArpaTCPTransmit.SendFIN[handle]; ArpaTCPLogging.PrintStateChange[handle, lastAck]; handle.state _ lastAck; }; closing, lastAck, timeWait => -- connection is closing NULL; -- so what? ENDCASE; }; -- Close Abort: PUBLIC PROC [handle: TCPHandle] ~ { ValidHandle[handle ! ArpaTCP.Error => GO TO GiveUp]; SELECT handle.state FROM listen, synSent => CloseConnection[handle, localAbort]; synRcvd, established, finWait1, finWait2, closeWait => { ArpaTCPTransmit.SendReset[handle, handle.localPort, handle.foreignPort, handle.foreignAddr, handle.sndNxt, handle.rcvNxt]; CloseConnection[handle, localAbort]; }; closing, lastAck, timeWait => CloseConnection[handle, localAbort]; ENDCASE; EXITS GiveUp => NULL; }; -- Abort GetInitialSequenceNumber: PUBLIC PROC RETURNS [INT] = { lastSequenceNumber _ lastSequenceNumber*1037 + 12345; RETURN [ABS[lastSequenceNumber]]; }; Open: PUBLIC ENTRY PROC [tcpInfo: ArpaTCP.TCPInfo] RETURNS [handle: TCPHandle] ~ { ENABLE UNWIND => NULL; IF FindConflictingConn[tcpInfo] # NIL THEN RETURN WITH ERROR ArpaTCP.Error[localConflict]; IF tcpInfo.active THEN IF NOT (tcpInfo.matchForeignPort AND tcpInfo.matchForeignAddr) THEN RETURN WITH ERROR ArpaTCP.Error[unspecifiedRemoteEnd] ELSE { handle _ CreateHandle[tcpInfo]; handle.iss _ GetInitialSequenceNumber[]; handle.sndUna _ handle.iss; ArpaTCPLogging.PrintStateChange[handle, synSent]; handle.state _ synSent; ArpaTCPTransmit.SendSYN[handle]; -- send SYN } ELSE { handle _ CreateHandle[tcpInfo]; ArpaTCPLogging.PrintStateChange[handle, listen]; handle.state _ listen; }; handleList _ List.Nconc1[handleList, handle]; }; CloseConnection: PUBLIC ENTRY PROC [handle: TCPHandle, reason: ArpaTCP.Reason] = { ENABLE UNWIND => NULL; Kill[handle.toNetQueue]; handle.toNetQueue _ NIL; Kill[handle.rexmitQueue]; handle.rexmitQueue _ NIL; ArpaTCPLogging.PrintStateChange[handle, closed]; handle.state _ closed; handle.reason _ reason; handleList _ List.Remove[handle, handleList]; BROADCAST handle.windowAvailable; -- make sure everyone finds out. BROADCAST handle.urgentAvailable; BROADCAST handle.dataAvailable; BROADCAST handle.notListening; }; -- CloseConnection Kill: PROC [list: LIST OF REF ANY] = { DO temp: LIST OF REF ANY _ list; IF temp = NIL THEN RETURN; list _ list.rest; temp.first _ NIL; temp.rest _ NIL; ENDLOOP; }; ValidHandle: PUBLIC ENTRY PROC [handle: TCPHandle] ~ { ENABLE UNWIND => NULL; FOR l: LIST OF REF ANY _ handleList, l.rest WHILE l # NIL DO IF l.first = handle THEN RETURN; ENDLOOP; RETURN WITH ERROR ArpaTCP.Error[IF handle = NIL THEN neverOpen ELSE handle.reason]; }; FindHandle: PUBLIC ENTRY PROC [rcvdDatagram: ArpaIP.Buffers] RETURNS [handle: TCPHandle] ~ TRUSTED { ENABLE UNWIND => NULL; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@rcvdDatagram.body]; sourceAddr: Arpa.Address; sourcePort: ArpaTCP.Port; dstnPort: ArpaTCP.Port; goodHandle: TCPHandle; sourceAddr _ rcvdDatagram.hdr1.source; sourcePort _ tcpHdrPtr^.sourcePort; dstnPort _ tcpHdrPtr^.dstnPort; goodHandle _ NIL; FOR l: LIST OF REF ANY _ handleList, l.rest WHILE l # NIL DO h: ArpaTCPOps.TCPHandle _ NARROW[l.first]; IF h.localPort = dstnPort AND (h.foreignAddr = sourceAddr OR NOT h.matchForeignAddr) AND (h.foreignPort = sourcePort OR NOT h.matchForeignPort) THEN IF h.matchForeignAddr AND h.matchForeignPort THEN RETURN [h] -- found exact match, return TCB ELSE -- match TCB with foreign address or port not specified IF h.matchForeignAddr THEN -- address match is best goodHandle _ h ELSE IF h.matchForeignPort AND goodHandle = NIL THEN goodHandle _ h ELSE IF goodHandle = NIL THEN goodHandle _ h; ENDLOOP; RETURN [goodHandle] -- best match found }; -- FindTCB CopyHandleList: PUBLIC ENTRY PROC [pool: LIST OF REF ANY] RETURNS [handleListCopy: LIST OF REF ANY _ NIL] ~ { ENABLE UNWIND => NULL; FOR from: LIST OF REF ANY _ handleList, from.rest WHILE from # NIL DO IF pool # NIL THEN { temp: LIST OF REF ANY _ pool; pool _ pool.rest; temp.rest _ handleListCopy; handleListCopy _ temp; } ELSE { handleListCopy _ CONS[NIL, handleListCopy]; }; handleListCopy.first _ from.first; ENDLOOP; WHILE pool # NIL DO next: LIST OF REF ANY _ pool.rest; pool.first _ NIL; pool.rest _ NIL; pool _ next; ENDLOOP }; FindConflictingConn: INTERNAL PROC [info: ArpaTCP.TCPInfo] RETURNS [conflictingHandle: TCPHandle] = { FOR q: LIST OF REF ANY _ handleList, q.rest WHILE q # NIL DO thisHandle: TCPHandle _ NARROW[q.first]; IF thisHandle.localPort = info.localPort AND info.matchLocalPort AND thisHandle.foreignAddr = info.foreignAddress AND info.matchForeignAddr AND thisHandle.foreignPort = info.foreignPort AND info.matchForeignPort THEN RETURN [thisHandle]; ENDLOOP; RETURN [NIL]; -- no conflict }; -- FindConflictingConn CreateHandle: PROC [info: ArpaTCP.TCPInfo] RETURNS [handle: TCPHandle] = { handle _ NEW[TCPHandleRec]; IF info.matchLocalPort THEN handle.localPort _ info.localPort ELSE DO -- assign local port automatically and ensure it doesn't match any existing one handle.localPort _ nextLocalPort; nextLocalPort _ MAX[nextLocalPort+1, minPort]; -- avoid well-known sockets FOR q: LIST OF REF ANY _ handleList, q.rest WHILE q#NIL DO IF NARROW[q.first, TCPHandle].localPort=handle.localPort THEN EXIT; -- try again REPEAT FINISHED => GOTO ok; ENDLOOP; REPEAT ok => NULL; ENDLOOP; IF info.matchForeignAddr THEN handle.foreignAddr _ info.foreignAddress ELSE handle.foreignAddr _ Arpa.nullAddress; IF info.matchForeignPort THEN handle.foreignPort _ info.foreignPort ELSE handle.foreignPort _ 0; handle.matchForeignAddr _ info.matchForeignAddr; handle.matchForeignPort _ info.matchForeignPort; handle.state _ closed; handle.reason _ neverOpen; handle.active _ info.active; SELECT TRUE FROM info.maxSegmentSize = 0 => { -- default to most appropriate maxSegmentSize IF InLocalNet[info.foreignAddress] THEN handle.maxSegmentSize _ maxTCPDataLength ELSE handle.maxSegmentSize _ ArpaTCP.maxArpanetSegmentSize; }; info.maxSegmentSize IN[1..maxTCPDataLength] => handle.maxSegmentSize _ info.maxSegmentSize; ENDCASE => handle.maxSegmentSize _ maxTCPDataLength; handle.rcvWnd _ defaultReceiveWindow; handle.fromNetQueue _ NIL; handle.readyToReadQueue _ NIL; handle.currentInputBuffer _ NIL; handle.toNetQueue _ NIL; handle.rexmitQueue _ NIL; handle.currentOutputDatagram _ ArpaIP.AllocBuffers[1]; handle.currentOutputPtr _ tcpHdrByteLength; handle.currentOutputLimit _ handle.currentOutputPtr+handle.maxSegmentSize; handle.rtt _ ArpaTCPOps.defaultRTT; ArpaTCPOps.SetDataTimeout[handle, info.timeout]; }; -- CreateHandle InLocalNet: PROC [remote: Arpa.Address] RETURNS [BOOL] = BEGIN myNetNumber: Arpa.Address _ Arpa.NetNumber[ArpaExtras.MyAddress[]]; hisNetNumber: Arpa.Address _ Arpa.NetNumber[remote]; IF hisNetNumber = [10,0,0,0] THEN RETURN[FALSE]; RETURN[myNetNumber = hisNetNumber]; END; END. ArpaTCPStatesImpl.mesa Copyright (C) 1984 by Xerox Corporation. All rights reserved. The following program was created in 1983 but has not been published within the meaning of the copyright law, is furnished under license, and may not be used, copied and/or disclosed except in accordance with the terms of said license. Last Edited by: Nichols, September 1, 1983 4:23 pm Last Edited by: Taft, January 4, 1984 10:11 am Last Edited by: HGM, March 27, 1984 3:23:42 am PST Demers, September 7, 1988 2:35:10 pm PDT Doug Terry, August 28, 1987 4:37:36 pm PDT Hal Murray May 16, 1985 3:15:32 am PDT John Larson, April 14, 1986 11:26:36 pm PST Close the TCP connection; returns an error if the connection does not exist or is already closing. Sends a FIN to the remote TCP. Note that the user may receive more data from the remote TCP after calling Close; when the connection is closed, the user will receive a message in which the data is a pointer to a ControlInfo record which contains a pointer to the TCB and the reason for closing. This is the last message that the user receives on the connection; on receipt, the user can delete all connection specific information. Abort the connection. This routine sends a TCP Reset if necessary; it calls CloseConnection to dispose of all the connection specific storage and to send a message to the user. The data in this message is a pointer to a ControlInfo record containing a pointer to the TCB and the reason the connection was closed (UserAbort). On receipt of this message, the user can dispose of all connection specific information. Generate an initial send sequence number for a connection. Attempts to open a new TCP connection. Returns a TCPHandle to use or raises ArpaTCP.OpenFailed if the Open request conflicts with an existing request or is a duplicate Open request on the same connection. The connection is open either in the LISTEN or SYNSENT state. In the LISTEN state, wait for a SYN from the remote TCP; in the SYNSENT state, send a SYN to initiate the connection. If an Open call is done to open a connection in the LISTEN state, then another Open call can be done to send the SYN and put the connection in the SYNSENT state. open listening connection Clear off the transmit and retransmit queues and remove this handle form the list. Check if handle given as argument by user is valid. Called by Abort, Close, and Send. Raises ArpaTCP.StreamClosing if bad handle, else it just returns. Find matching handle for segment received from the net. Make a copy of the handle list for the retransmit procedure to look at. Checks for conflicting handles; i.e. handles which have the same local and foreign ports; if such a handle exists, then return a pointer to it; used in user open call to determine if user connection request conflicts with existing connection. This is a gross hack to get the Max Segment Size to be the proper Arpanet size if going out to the Arpanet. What we really should be doing is properly negotiating MSS using the MSS option. This should be fixed as soon as TCP options are implemented. Κ F– "cedar" style˜head™Icode2šœ©™©J™2J™.JšœΟkœ™2Icode™(Mšœ*™*Mšœ#™&Mšœ(™+J™š ˜ Lšœœ#˜-Lšœ œ ˜Mšœœ˜%Lšœ œ˜Lšœœ˜Lšœœ7˜DLšœœ˜(Lšœ œ}˜L˜Lšœœ˜4——šΟnœœœΟc˜