mirror of
https://github.com/pocoproject/poco.git
synced 2025-04-18 07:32:18 +02:00
631 lines
14 KiB
Plaintext
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
|
|
{}
|
|
}
|
|
|
|
%%
|