<> <> <> <> <> <> <> <<>> DIRECTORY BasicTime USING [Now], IPDefs USING [Datagram, DatagramRec, DByte, Address, nullAddress], List USING [Append, Nconc1, Remove], Process USING [DisableTimeout, MsecToTicks, SetTimeout], TCP USING [Error, maxTimeout, Reason, TCPInfo], TCPLogging USING [PrintStateChange], TCPOps USING [defaultReceiveWindow, maxTCPDataLength, TCPHandle, TCPHandleRec, tcpHdrByteLength, TCPHeaderP], TCPStates, TCPTransmit USING [SendFIN, SendReset, SendSYN]; TCPStatesImpl: CEDAR MONITOR -- monitors the handleList IMPORTS BasicTime, List, Process, TCP, TCPLogging, TCPOps, TCPTransmit EXPORTS TCPStates ~ BEGIN OPEN TCPOps, TCPStates; handleList: LIST OF REF ANY; -- list of all open TCP connections nextLocalPort: CARDINAL _ 256; -- avoid well-known sockets 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 TCPTransmit.SendFIN[handle]; -- send fin and ack TCPLogging.PrintStateChange[handle, finWait1]; handle.state _ finWait1; handle.reason _ localClose; }; closeWait => {-- send fin after all preceeding sends TCPTransmit.SendFIN[handle]; TCPLogging.PrintStateChange[handle, lastAck]; handle.state _ lastAck; }; closing, lastAck, timeWait => -- connection is closing NULL; -- so what? ENDCASE; }; -- Close Abort: PUBLIC PROC [handle: TCPHandle] ~ { <> ValidHandle[handle ! TCP.Error => GO TO GiveUp]; SELECT handle.state FROM listen, synSent => CloseConnection[handle, localAbort]; synRcvd, established, finWait1, finWait2, closeWait => { TCPTransmit.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: TCP.TCPInfo] RETURNS [handle: TCPHandle] ~ { <> ENABLE UNWIND => NULL; IF FindConflictingConn[tcpInfo] # NIL THEN RETURN WITH ERROR TCP.Error[localConflict]; IF tcpInfo.active THEN IF NOT (tcpInfo.matchForeignPort AND tcpInfo.matchForeignAddr) THEN RETURN WITH ERROR TCP.Error[unspecifiedRemoteEnd] ELSE { handle _ CreateHandle[tcpInfo]; handle.iss _ GetInitialSequenceNumber[]; handle.sndUna _ handle.iss; TCPLogging.PrintStateChange[handle, synSent]; handle.state _ synSent; TCPTransmit.SendSYN[handle]; -- send SYN } ELSE { <> handle _ CreateHandle[tcpInfo]; TCPLogging.PrintStateChange[handle, listen]; handle.state _ listen; }; handleList _ List.Nconc1[handleList, handle]; }; CloseConnection: PUBLIC ENTRY PROC [handle: TCPHandle, reason: TCP.Reason] = { <> ENABLE UNWIND => NULL; Kill[handle.toNetQueue]; handle.toNetQueue _ NIL; Kill[handle.rexmitQueue]; handle.rexmitQueue _ NIL; TCPLogging.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 TCP.Error[IF handle = NIL THEN neverOpen ELSE handle.reason]; }; FindHandle: PUBLIC ENTRY PROC [rcvdDatagram: IPDefs.Datagram] RETURNS [handle: TCPHandle] ~ TRUSTED { <> ENABLE UNWIND => NULL; tcpHdrPtr: TCPHeaderP _ LOOPHOLE[@rcvdDatagram.data]; sourceAddr: IPDefs.Address; sourcePort: IPDefs.DByte; dstnPort: IPDefs.DByte; goodHandle: TCPHandle; sourceAddr _ rcvdDatagram.inHdr.source; sourcePort _ tcpHdrPtr^.sourcePort; dstnPort _ tcpHdrPtr^.dstnPort; goodHandle _ NIL; FOR l: LIST OF REF ANY _ handleList, l.rest WHILE l # NIL DO h: TCPOps.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 RETURNS [handleListCopy: LIST OF REF ANY] ~ { <> ENABLE UNWIND => NULL; RETURN [List.Append[handleList, NIL]]; }; FindConflictingConn: INTERNAL PROC [info: TCP.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: TCP.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, 256]; -- 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 _ IPDefs.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; handle.maxSegmentSize _ maxTCPDataLength; handle.rcvWnd _ defaultReceiveWindow; handle.fromNetQueue _ NIL; handle.readyToReadQueue _ NIL; handle.currentInputBuffer _ NIL; handle.toNetQueue _ NIL; handle.rexmitQueue _ NIL; handle.currentOutputDatagram _ NEW[IPDefs.DatagramRec]; handle.currentOutputPtr _ tcpHdrByteLength; handle.currentOutputLimit _ handle.currentOutputPtr+handle.maxSegmentSize; handle.dataTimeout _ MIN[info.timeout, TCP.maxTimeout]; TRUSTED { IF handle.dataTimeout>=0 THEN { Process.SetTimeout[@handle.dataAvailable, Process.MsecToTicks[MIN[handle.dataTimeout, 10000]]]; Process.SetTimeout[@handle.windowAvailable, Process.MsecToTicks[MIN[handle.dataTimeout, 10000]]]} ELSE Process.DisableTimeout[@handle.dataAvailable]; }; }; -- CreateHandle END.