poco/FSM/samples/TCP/TCP.sm
2016-07-18 14:34:53 +02:00

631 lines
14 KiB
Plaintext

// -*- tab-width: 4; -*-
%{
//
// The contents of this file are subject to the Mozilla Public
// License Version 1.1 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy
// of the License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
// implied. See the License for the specific language governing
// rights and limitations under the License.
//
// The Original Code is State Machine Compiler (SMC).
//
// The Initial Developer of the Original Code is Charles W. Rapp.
// Portions created by Charles W. Rapp are
// Copyright (C) 2000 - 2007. Charles W. Rapp.
// All Rights Reserved.
//
// Contributor(s):
//
// Name
// TCP.sm
//
// Description
// The TCP/IP state transition diagram.
//
%}
%class TcpConnection
%header TcpConnection.h
%include "TcpSegment.h"
%start TCP::CLOSED
%map TCP
%%
CLOSED
{
PassiveOpen(port: unsigned short) ServiceOpening { openServerSocket(port); }
ActiveOpen(address: const sockaddr_in*) ClientOpening { openClientSocket(address); }
AcceptOpen(segment: const TcpSegment&) SYN_RCVD { sendSynAck(segment);setNearAddress(); }
// Ignore close requests when closed.
Close nil {}
}
// Wait here until service open has either succeeded or failed.
ServiceOpening
{
ServerOpened LISTEN { openSuccess(); }
OpenFailed(reason: const char*) CLOSED { closeSocket(); openFailed(reason); }
}
// Wait here until client open has either succeeded or failed.
ClientOpening
{
ClientOpened(address: const sockaddr_in*)SYN_SENT { sendOpenSyn(address); }
OpenFailed(reason: const char*) CLOSED { closeSocket();openFailed(reason); }
}
// Server sockets just sit here, creating new client sockets when
// a SYN is received.
LISTEN
{
// Create a new client socket and send its port number in
// the SYN/ACK response.
SYN(segment: const TcpSegment&) nil { accept(segment);deleteSegment(segment); }
Close CLOSED { closeSocket();closed(""); }
// But don't reset a reset message because there is no
// connection to reset.
RST(segment: const TcpSegment&) nil { deleteSegment(segment); }
// Don't die on an undefined segment either.
UNDEF(segment: const TcpSegment&) nil { doSend(TcpSegment::RST, NULL, 0, 0, &segment);deleteSegment(segment); }
}
// An "accepted" client socket starts life in this state.
SYN_RCVD
Entry
{
startTimer("ACK_TIMER", TcpConnection::ACK_TIMEOUT);
}
Exit
{
stopTimer("ACK_TIMER");
}
{
// The connection was reset before it was established.
// Close the datagram socket but don't tell anybody.
RST(segment: const TcpSegment&) CLOSED { closeSocket();clearListener(); deleteSegment(segment); }
// The TCP state transition diagram shows the server socket
// taking an ACK transtion to the ESTABLISHED state but that
// is not quite true. The accepted client socket goes to
// the ESTABLISHED state while the server socket goes back to
// LISTEN.
ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
ESTABLISHED { accepted();deleteSegment(segment); }
Close FIN_WAIT_1 { doSend(TcpSegment::FIN, NULL, 0, 0, NULL); }
AckTimeout CLOSED { doSend(TcpSegment::RST, NULL, 0, 0, NULL);closeSocket(); }
}
// A application-created client starts life in this state.
SYN_SENT
Entry
{ startTimer("CONN_ACK_TIMER", TcpConnection::ACK_TIMEOUT); }
Exit
{ stopTimer("CONN_ACK_TIMER"); }
{
// When a TCP service accepts a new connection, it creates
// a new client to handle this connection.
SYN_ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
ESTABLISHED { setFarAddress(segment);sendSynAckAck(segment);openSuccess();deleteSegment(segment); }
Close CLOSED { closeSocket();closed(""); }
ConnAckTimeout CLOSED { closeSocket();openFailed("acknowledge timeout"); }
}
ESTABLISHED
{
FIN(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort()]
CLOSE_WAIT { doSend(TcpSegment::ACK, NULL, 0, 0, &segment);halfClosed();deleteSegment(segment); }
PSH(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort()]
nil { doSend(TcpSegment::ACK, NULL, 0, 0, &segment);receive(segment);deleteSegment(segment); }
Transmit(data: const char*, offset: int, size: int)
Transmitting
{
doSend(TcpSegment::PSH, data, offset, size, NULL);
}
Close
FIN_WAIT_1
{
doSend(TcpSegment::FIN, NULL, 0, 0, NULL); }
}
Transmitting
Entry
{
startTimer("TRANS_ACK_TIMER",
TcpConnection::ACK_TIMEOUT);
}
Exit
{
stopTimer("TRANS_ACK_TIMER");
}
{
ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
ESTABLISHED
{
transmitted();
deleteSegment(segment);
}
PSH_ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
ESTABLISHED
{
doSend(TcpSegment::ACK, NULL, 0, 0, &segment);
transmitted();
receive(segment);
deleteSegment(segment);
}
// If a push is received now, that means the far end sent
// data at exactly the same time as we sent it. Send an ack
// but don't go anywhere.
PSH(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort()]
nil
{
doSend(TcpSegment::ACK, NULL, 0, 0, &segment);
receive(segment);
deleteSegment(segment);
}
FIN(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort()]
CLOSE_WAIT
{
doSend(TcpSegment::ACK, NULL, 0, 0, &segment);
halfClosed();
deleteSegment(segment);
}
Close
FIN_WAIT_1
{
doSend(TcpSegment::FIN, NULL, 0, 0, NULL);
}
TransAckTimeout
CLOSED
{
transmitFailed("peer did not acknowledge");
closed("connection lost");
}
}
CLOSE_WAIT
{
Close
LAST_ACK
{
doSend(TcpSegment::FIN, NULL, 0, 0, NULL);
}
}
LAST_ACK
Entry
{
startTimer("CLOSE_ACK_TIMER", TcpConnection::ACK_TIMEOUT);
}
Exit
{
stopTimer("CLOSE_ACK_TIMER");
}
{
ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
CLOSED
{
doSend(TcpSegment::FIN, NULL, 0, 0, &segment);
closeSocket();
closed("");
deleteSegment(segment);
}
CloseAckTimeout
CLOSED
{
closeSocket();
closed("");
}
}
FIN_WAIT_1
Entry
{
startTimer("CLOSE_ACK_TIMER", TcpConnection::ACK_TIMEOUT);
}
Exit
{
stopTimer("CLOSE_ACK_TIMER");
}
{
ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
FIN_WAIT_2
{
deleteSegment(segment);
}
FIN(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort()]
CLOSING
{
doSend(TcpSegment::ACK, NULL, 0, 0, &segment);
deleteSegment(segment);
}
FIN_ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
TIME_WAIT
{
doSend(TcpSegment::ACK, NULL, 0, 0, &segment);
deleteSegment(segment);
}
CloseAckTimeout
FIN_WAIT_2
{
closeSocket();
closed("");
}
}
FIN_WAIT_2
Entry
{
startTimer("CLOSE_ACK_TIMER", TcpConnection::ACK_TIMEOUT);
}
Exit
{
stopTimer("CLOSE_ACK_TIMER");
}
{
FIN(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort()]
TIME_WAIT
{
doSend(TcpSegment::ACK, NULL, 0, 0, &segment);
deleteSegment(segment);
}
CloseAckTimeout
CLOSED
{
closeSocket();
}
// Ignore undefined transitions.
UNDEF(segment: const TcpSegment&)
nil
{
deleteSegment(segment);
}
}
CLOSING
Entry
{
startTimer("CLOSE_ACK_TIMER", TcpConnection::ACK_TIMEOUT);
}
Exit
{
stopTimer("CLOSE_ACK_TIMER");
}
{
ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
TIME_WAIT
{
deleteSegment(segment);
}
CloseAckTimeout
TIME_WAIT
{
closeSocket();
closed("");
}
// Ignore undefined transitions.
UNDEF(segment: const TcpSegment&)
nil
{
deleteSegment(segment);
}
}
TIME_WAIT
Entry
{
startTimer("CLOSE_TIMER", TcpConnection::CLOSE_TIMEOUT);
}
Exit
{
stopTimer("CLOSE_TIMER");
}
{
FIN_ACK(segment: const TcpSegment&)
[segment.getSrcAddress() == ctxt.getFarAddress() &&
segment.getSrcPort() == ctxt.getFarPort() &&
segment.getAcknowledgeNumber() == ctxt.getSequenceNumber()]
CLOSED
{
closeSocket();
deleteSegment(segment);
}
CloseTimeout
CLOSED
{
closeSocket();
closed("");
}
// Ignore undefined transitions.
UNDEF(segment: const TcpSegment&)
nil
{
deleteSegment(segment);
}
}
Default
{
PassiveOpen(port : unsigned short)
nil
{
openFailed("already open");
}
ActiveOpen(address: const sockaddr_in*)
nil
{
openFailed("already open");
}
Transmit(data: const char*, offset: int, size: int)
nil
{
transmitFailed("connection not established");
}
// The default action when an unknown client sends us a
// segment is to reset the unknown client but remain in
// the current state for a segment from the real client.
FIN(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
SYN(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
PSH(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
ACK(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
URG(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
FIN_ACK(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
SYN_ACK(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
PSH_ACK(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
UNDEF(segment: const TcpSegment&)
[segment.getSrcAddress() != ctxt.getFarAddress() ||
segment.getSrcPort() != ctxt.getFarPort()]
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
// Do not reset a reset. Just go to closed.
RST(segment: const TcpSegment&)
CLOSED
{
closed("connection reset by peer");
deleteSegment(segment);
}
RST_ACK(segment: const TcpSegment&)
CLOSED
{
closed("connection reset by peer");
deleteSegment(segment);
}
// The remain transitions represent segments received
// when they were not expected from the peer. The
// connection is reset.
FIN(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
SYN(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
PSH(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
ACK(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
URG(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
FIN_ACK(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
SYN_ACK(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
PSH_ACK(segment: const TcpSegment&)
CLOSED
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
UNDEF(segment: const TcpSegment&)
nil
{
doSend(TcpSegment::RST, NULL, 0, 0, &segment);
closed("connection reset");
deleteSegment(segment);
}
// Ignore timeouts which occur when not expected.
ConnAckTimeout
nil
{}
TransAckTimeout
nil
{}
CloseAckTimeout
nil
{}
CloseTimeout
nil
{}
}
%%