* #40116: Switch to 2.3.0 libzrtpcpp version
diff --git a/jni/libzrtp/sources/src/ZrtpStateClass.cpp b/jni/libzrtp/sources/src/ZrtpStateClass.cpp
new file mode 100644
index 0000000..f77f9f9
--- /dev/null
+++ b/jni/libzrtp/sources/src/ZrtpStateClass.cpp
@@ -0,0 +1,1471 @@
+/*
+ Copyright (C) 2006-2008 Werner Dittmann
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @author Werner Dittmann <Werner.Dittmann@t-online.de>
+ */
+
+#include <iostream>
+#include <cstdlib>
+#include <ctype.h>
+
+#include <libzrtpcpp/ZRtp.h>
+#include <libzrtpcpp/ZrtpStateClass.h>
+
+using namespace std;
+using namespace GnuZrtpCodes;
+
+state_t states[numberOfStates] = {
+ {Initial, &ZrtpStateClass::evInitial },
+ {Detect, &ZrtpStateClass::evDetect },
+ {AckDetected, &ZrtpStateClass::evAckDetected },
+ {AckSent, &ZrtpStateClass::evAckSent },
+ {WaitCommit, &ZrtpStateClass::evWaitCommit },
+ {CommitSent, &ZrtpStateClass::evCommitSent },
+ {WaitDHPart2, &ZrtpStateClass::evWaitDHPart2 },
+ {WaitConfirm1, &ZrtpStateClass::evWaitConfirm1 },
+ {WaitConfirm2, &ZrtpStateClass::evWaitConfirm2 },
+ {WaitConfAck, &ZrtpStateClass::evWaitConfAck },
+ {WaitClearAck, &ZrtpStateClass::evWaitClearAck },
+ {SecureState, &ZrtpStateClass::evSecureState },
+ {WaitErrorAck, &ZrtpStateClass::evWaitErrorAck }
+};
+
+
+ZrtpStateClass::ZrtpStateClass(ZRtp *p) {
+ parent = p;
+ secSubstate = Normal;
+ engine = new ZrtpStates(states, numberOfStates, Initial);
+
+ commitPkt = NULL;
+ multiStream = false;
+
+ // Set up timers according to ZRTP spec
+ T1.start = 50;
+ T1.maxResend = 20;
+ T1.capping = 200;
+
+ T2.start = 150;
+ T2.maxResend = 10;
+ T2.capping = 600;
+}
+
+ZrtpStateClass::~ZrtpStateClass(void) {
+
+ // If not in Initial state: close the protocol engine
+ // before destroying it. This will free pending packets
+ // if necessary.
+ if (!inState(Initial)) {
+ Event_t ev;
+
+ cancelTimer();
+ ev.type = ZrtpClose;
+ event = &ev;
+ engine->processEvent(*this);
+ }
+ delete engine;
+}
+
+void ZrtpStateClass::processEvent(Event_t *ev) {
+
+ event = ev;
+ char *msg, first, middle, last;
+ uint8_t *pkt;
+
+ parent->synchEnter();
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+ first = tolower(*msg);
+ middle = tolower(*(msg+4));
+ last = tolower(*(msg+7));
+
+ // Check if this is an Error packet.
+ if (first == 'e' && middle =='r' && last == ' ') {
+ /*
+ * Process a received Error packet.
+ *
+ * In any case stop timer to prevent resending packets.
+ * Use callback method to prepare and get an ErrorAck packet.
+ * Modify event type to "ErrorPkt" and hand it over to current
+ * state for further processing.
+ */
+ cancelTimer();
+ ZrtpPacketError epkt(pkt);
+ ZrtpPacketErrorAck* eapkt = parent->prepareErrorAck(&epkt);
+ parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(eapkt));
+ event->type = ErrorPkt;
+ }
+ else if (first == 'p' && middle == ' ' && last == ' ') {
+ ZrtpPacketPing ppkt(pkt);
+ ZrtpPacketPingAck* ppktAck = parent->preparePingAck(&ppkt);
+ parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(ppktAck));
+ parent->synchLeave();
+ return;
+ }
+ else if (first == 's' && last == 'y') {
+ uint32_t errorCode = 0;
+ ZrtpPacketSASrelay* srly = new ZrtpPacketSASrelay(pkt);
+ ZrtpPacketRelayAck* rapkt = parent->prepareRelayAck(srly, &errorCode);
+ parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(rapkt));
+ parent->synchLeave();
+ return;
+ }
+
+ }
+ /*
+ * Shut down protocol state engine: cancel outstanding timer, further
+ * processing in current state.
+ */
+ else if (event->type == ZrtpClose) {
+ cancelTimer();
+ }
+ engine->processEvent(*this);
+ parent->synchLeave();
+}
+
+
+void ZrtpStateClass::evInitial(void) {
+ DEBUGOUT((cout << "Checking for match in Initial.\n"));
+
+ if (event->type == ZrtpInitial) {
+ ZrtpPacketHello* hello = parent->prepareHello();
+
+ // remember packet for easy resend in case timer triggers
+ sentPacket = static_cast<ZrtpPacketBase *>(hello);
+
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (startTimer(&T1) <= 0) {
+ timerFailed(SevereNoTimer); // returns to state Initial
+ return;
+ }
+ nextState(Detect);
+ }
+}
+
+/*
+ * Detect state.
+ *
+ * When in this state the protocol engine sent an initial Hello packet
+ * to the peer.
+ *
+ * When entering this state transition function then:
+ * - Assume Initiator mode, mode may change later on peer reaction
+ * - Instance variable sentPacket contains the sent Hello packet
+ * - Hello timer T1 may be active. This is the case if the other peer
+ * has prepared its RTP session and answers our Hello packets nearly
+ * immediately, i.e. before the Hello timeout counter expires. If the
+ * other peer does not send a Hello during this time the state engine
+ * reports "other peer does not support ZRTP" but stays
+ * in state Detect with no active timer (passiv mode). Staying in state
+ * Detect allows another peer to start its detect phase any time later.
+ *
+ * This restart capability is the reason why we use "startTimer(&T1)" in
+ * case we received a Hello packet from another peer. This effectively
+ * restarts the Hello timeout counter.
+ *
+ * In this state we also handle ZrtpInitialize event. This forces a
+ * restart of ZRTP discovery if an application calls ZrtpQueue#startZrtp
+ * again. This may happen after a previous discovery phase were not
+ * successful.
+ *
+ * Usually applications use some sort of signaling protocol, for example
+ * SIP, to negotiate the RTP parameters. Thus the RTP sessions setup is
+ * fairly sychronized and thus also the ZRTP detection phase. Applications
+ * that use some other ways to setup the RTP sessions this restart capability
+ * comes in handy because no RTP setup sychronization is necessary.
+ *
+ * Possible events in this state are:
+ * - timeout for sent Hello packet: causes a resend check and
+ * repeat sending of Hello packet
+ * - received a HelloAck: stop active timer, prepare and send Hello packet,
+ * switch to state AckDeteced.
+ * - received a Hello: stop active timer, send HelloAck, prepare Commit
+ * packet, switch to state AckSent.
+ *
+ */
+void ZrtpStateClass::evDetect(void) {
+
+ DEBUGOUT((cout << "Checking for match in Detect.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ /*
+ * First check the general event type, then discrimnate
+ * the real event.
+ */
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+ /*
+ * HelloAck:
+ * - our peer acknowledged our Hello packet, we have not seen the peer's Hello yet
+ * - cancel timer T1 to stop resending Hello
+ * - switch to state AckDetected, wait for peer's Hello (F3)
+ */
+ if (first == 'h' && last =='k') {
+ cancelTimer();
+ sentPacket = NULL;
+ nextState(AckDetected);
+ return;
+ }
+ /*
+ * Hello:
+ * - send HelloAck packet to acknowledge the received Hello packet
+ * - use received Hello packet to prepare own Commit packet. We need to
+ * do it at this point because we need the hash value computed from
+ * peer's Hello packet. Follwing states my use the prepared Commit.
+ * - switch to new state AckSent which sends own Hello packet until
+ * peer acknowledges this
+ * - Don't clear sentPacket, points to Hello
+ */
+ if (first == 'h' && last ==' ') {
+ cancelTimer();
+ ZrtpPacketHelloAck* helloAck = parent->prepareHelloAck();
+
+ if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) {
+ parent->zrtpNegotiationFailed(Severe, SevereCannotSend);
+ return;
+ }
+ // Use peer's Hello packet to create my commit packet, store it
+ // for possible later usage in state AckSent
+ ZrtpPacketHello hpkt(pkt);
+ commitPkt = parent->prepareCommit(&hpkt, &errorCode);
+
+ nextState(AckSent);
+ if (commitPkt == NULL) {
+ sendErrorPacket(errorCode); // switches to Error state
+ return;
+ }
+ if (startTimer(&T1) <= 0) { // restart own Hello timer/counter
+ timerFailed(SevereNoTimer); // returns to state Initial
+ }
+ T1.maxResend = 60; // more retries to extend time, see chap. 6
+ }
+ return; // unknown packet for this state - Just ignore it
+ }
+ // Timer event triggered - this is Timer T1 to resend Hello
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (nextTimer(&T1) <= 0) {
+ commitPkt = NULL;
+ parent->zrtpNotSuppOther();
+ nextState(Detect);
+ }
+ }
+ // If application call zrtpStart() to restart discovery
+ else if (event->type == ZrtpInitial) {
+ cancelTimer();
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (startTimer(&T1) <= 0) {
+ timerFailed(SevereNoTimer); // returns to state Initial
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+/*
+ * AckSent state.
+ *
+ * The protocol engine got a Hello packet from peer and answered with a
+ * HelloAck response. According to the protocol we must also send a
+ * Hello after HelloAck (refer to figure 1 in ZRTP RFC xxxx, message
+ * HelloACK (F2) must be followed by Hello (F3)). We use the timeout in
+ * this state to send the required Hello (F3).
+ *
+ * Our peer must acknowledge the Hello with HelloAck. In earlier versions
+ * also a Commit was a valid packet thus the code covers this.
+ * Figure 1 in the RFC shows the HelloAck, chapter 7 states that a Commit
+ * may be send to acknowledge Hello. There is one constraint when using a Commit to
+ * acknowledge Hello: refer to chapter 4.1 that requires that both parties
+ * have completed the Hello/HelloAck discovery handshake. This implies that
+ * only message F4 may be replaced by a Commit. This constraint guarantees
+ * that both peers have seen at least one Hello.
+ *
+ * When entering this transition function:
+ * - The instance variabe sentPacket contains own Hello packet
+ * - The instance variabe commitPkt points to prepared Commit packet
+ * - Timer T1 is active
+ *
+ * Possible events in this state are:
+ * - timeout for sent Hello packet: causes a resend check and repeat sending
+ * of Hello packet
+ * - HelloAck: The peer answered with HelloAck to own HelloAck/Hello. Send
+ * prepared Commit packet and try Initiator mode.
+ * - Commit: The peer answered with Commit to HelloAck/Hello, thus switch to
+ * responder mode.
+ * - Hello: If the protcol engine receives another Hello it repeats the
+ * HelloAck/Hello response until Timer T1 exceeds its maximum. This may
+ * happen if the other peer sends Hello only (maybe due to network problems)
+ */
+void ZrtpStateClass::evAckSent(void) {
+
+ DEBUGOUT((cout << "Checking for match in AckSent.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ /*
+ * First check the general event type, then discrimnate
+ * the real event.
+ */
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * HelloAck:
+ * The peer answers with HelloAck to own HelloAck/Hello. Send Commit
+ * and try Initiator mode. The requirement defined in chapter 4.1 to
+ * have a complete Hello/HelloAck is fulfilled.
+ * - stop Hello timer T1
+ * - send own Commit message
+ * - switch state to CommitSent, start Commit timer, assume Initiator
+ */
+ if (first == 'h' && last =='k') {
+ cancelTimer();
+
+ // remember packet for easy resend in case timer triggers
+ // Timer trigger received in new state CommitSend
+ sentPacket = static_cast<ZrtpPacketBase *>(commitPkt);
+ commitPkt = NULL; // now stored in sentPacket
+ nextState(CommitSent);
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (startTimer(&T2) <= 0) {
+ timerFailed(SevereNoTimer); // returns to state Initial
+ }
+ return;
+ }
+ /*
+ * Hello:
+ * - peer didn't receive our HelloAck
+ * - repeat HelloAck/Hello response:
+ * -- get HelloAck packet, send it
+ * -- The timeout trigger of T1 sends our Hello packet
+ * -- stay in state AckSent
+ *
+ * Similar to Detect state: just acknowledge the Hello, the next
+ * timeout sends the following Hello.
+ */
+
+ if (first == 'h' && last ==' ') {
+ ZrtpPacketHelloAck* helloAck = parent->prepareHelloAck();
+
+ if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) {
+ nextState(Detect);
+ parent->zrtpNegotiationFailed(Severe, SevereCannotSend);
+ }
+ return;
+ }
+ /*
+ * Commit:
+ * The peer answers with Commit to HelloAck/Hello, thus switch to
+ * responder mode.
+ * - stop timer T1
+ * - prepare and send our DHPart1
+ * - switch to state WaitDHPart2 and wait for peer's DHPart2
+ * - don't start timer, we are responder
+ */
+ if (first == 'c') {
+ cancelTimer();
+ ZrtpPacketCommit cpkt(pkt);
+
+ if (!multiStream) {
+ ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&cpkt, &errorCode);
+
+ // Something went wrong during processing of the Commit packet
+ if (dhPart1 == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ return;
+ }
+ commitPkt = NULL;
+ sentPacket = static_cast<ZrtpPacketBase *>(dhPart1);
+ nextState(WaitDHPart2);
+ }
+ else {
+ ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&cpkt, &errorCode);
+
+ // Something went wrong during processing of the Commit packet
+ if (confirm == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ return;
+ }
+ sentPacket = static_cast<ZrtpPacketBase *>(confirm);
+ nextState(WaitConfirm2);
+ }
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ }
+ }
+ }
+ /*
+ * Timer:
+ * - resend Hello packet, stay in state, restart timer until repeat
+ * counter triggers
+ * - if repeat counter triggers switch to state Detect, con't clear
+ * sentPacket, Detect requires it to point to own Hello message
+ */
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ return sendFailed(); // returns to state Initial
+ }
+ if (nextTimer(&T1) <= 0) {
+ parent->zrtpNotSuppOther();
+ commitPkt = NULL;
+ // Stay in state Detect to be prepared get an hello from
+ // other peer any time later
+ nextState(Detect);
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ commitPkt = NULL;
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+/*
+ * AckDetected state.
+ *
+ * The protocol engine received a HelloAck in state Detect, thus the peer
+ * acknowledged our the Hello. According to ZRT RFC xxxx our peer must send
+ * its Hello until our protocol engine sees it (refer also to comment for
+ * state AckSent). This protocol sequence gurantees that both peers got at
+ * least one Hello.
+ *
+ * When entering this transition function
+ * - instance variable sentPacket is NULL, Hello timer stopped
+ *
+ * Possible events in this state are:
+ * Hello: we have to choices
+ * 1) we can acknowledge the peer's Hello with a HelloAck
+ * 2) we can acknowledge the peer's Hello with a Commit
+ * Both choices are implemented and may be enabled by setting a compile
+ * time #if (see code below). Currently we use choice 1) here because
+ * it's more aligned to the ZRTP specification
+ */
+void ZrtpStateClass::evAckDetected(void) {
+
+ DEBUGOUT((cout << "Checking for match in AckDetected.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+#if 1
+ /*
+ * Implementation for choice 1)
+ * Hello:
+ * - Acknowledge peers Hello, sending HelloACK (F4)
+ * - switch to state WaitCommit, wait for peer's Commit
+ * - we are going to be in the Responder role
+ */
+
+ if (first == 'h' && last ==' ') {
+ // Parse Hello packet and build an own Commit packet even if the
+ // Commit is not send to the peer. We need to do this to check the
+ // Hello packet and prepare the shared secret stuff.
+ ZrtpPacketHello hpkt(pkt);
+ ZrtpPacketCommit* commit = parent->prepareCommit(&hpkt, &errorCode);
+
+ // Something went wrong during processing of the Hello packet, for
+ // example wrong version, duplicate ZID.
+ if (commit == NULL) {
+ sendErrorPacket(errorCode);
+ return;
+ }
+ ZrtpPacketHelloAck *helloAck = parent->prepareHelloAck();
+ nextState(WaitCommit);
+
+ // remember packet for easy resend
+ sentPacket = static_cast<ZrtpPacketBase *>(helloAck);
+ if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) {
+ sendFailed();
+ }
+ }
+#else
+ /*
+ * Implementation for choice 2)
+ * Hello:
+ * - Acknowledge peers Hello by sending Commit (F5)
+ * instead of HelloAck (F4)
+ * - switch to state CommitSent
+ * - Initiator role, thus start timer T2 to monitor timeout for Commit
+ */
+
+ if (first == 'h') {
+ // Parse peer's packet data into a Hello packet
+ ZrtpPacketHello hpkt(pkt);
+ ZrtpPacketCommit* commit = parent->prepareCommit(&hpkt, &errorCode);
+ // Something went wrong during processing of the Hello packet
+ if (commit == NULL) {
+ sendErrorPacket(errorCode);
+ return;
+ }
+ nextState(CommitSent);
+
+ // remember packet for easy resend in case timer triggers
+ // Timer trigger received in new state CommitSend
+ sentPacket = static_cast<ZrtpPacketBase *>(commit);
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed();
+ return;
+ }
+ if (startTimer(&T2) <= 0) {
+ timerFailed(SevereNoTimer);
+ }
+ }
+#endif
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ nextState(Initial);
+ }
+}
+
+/*
+ * WaitCommit state.
+ *
+ * This state is only used if we use choice 1) in AckDetected.
+ *
+ * When entering this transition function
+ * - instance variable sentPacket contains a HelloAck packet
+ *
+ * Possible events in this state are:
+ * - Hello: just resend our HelloAck
+ * - Commit: prepare and send our DHPart1 message to start first
+ * half of DH key agreement. Switch to state WaitDHPart2, don't
+ * start any timer, we a Responder.
+ */
+void ZrtpStateClass::evWaitCommit(void) {
+
+ DEBUGOUT((cout << "Checking for match in WaitCommit.\n"));
+
+ char *msg, first;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ /*
+ * Hello:
+ * - resend HelloAck
+ * - stay in WaitCommit
+ */
+ if (first == 'h') {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ }
+ return;
+ }
+ /*
+ * Commit:
+ * - prepare DH1Part packet or Confirm1 if multi stream mode
+ * - send it to peer
+ * - switch state to WaitDHPart2 or WaitConfirm2 if multi stream mode
+ * - don't start timer, we are responder
+ */
+ if (first == 'c') {
+ ZrtpPacketCommit cpkt(pkt);
+
+ if (!multiStream) {
+ ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&cpkt, &errorCode);
+
+ // Something went wrong during processing of the Commit packet
+ if (dhPart1 == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ return;
+ }
+ sentPacket = static_cast<ZrtpPacketBase *>(dhPart1);
+ nextState(WaitDHPart2);
+ }
+ else {
+ ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&cpkt, &errorCode);
+
+ // Something went wrong during processing of the Commit packet
+ if (confirm == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ return;
+ }
+ sentPacket = static_cast<ZrtpPacketBase *>(confirm);
+ nextState(WaitConfirm2);
+ }
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ }
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+/*
+ * CommitSent state.
+ *
+ * This state either handles a DH1Part1 message to start the first
+ * half of DH key agreement or it handles a Commit clash. If handling a
+ * Commit clash it may happen that we change our role from Initiator to
+ * Responder.
+ *
+ * When entering this transition function
+ * - assume Initiator mode, may change if we reveice a Commit here
+ * - sentPacket contains Commit packet
+ * - Commit timer (T2) active
+ *
+ * Possible events in this state are:
+ * - timeout for sent Commit packet: causes a resend check and repeat sending
+ * of Commit packet
+ * - Commit: This is a Commit clash. Break the tie accroding to chapter 5.2
+ * - DHPart1: start first half of DH key agreement. Perpare and send own DHPart2
+ * and switch to state WaitConfirm1.
+ */
+
+void ZrtpStateClass::evCommitSent(void) {
+
+ DEBUGOUT((cout << "Checking for match in CommitSend.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * HelloAck or Hello:
+ * - delayed "HelloAck" or "Hello", maybe due to network latency, just
+ * ignore it
+ * - no switch in state, leave timer as it is
+ */
+ if (first == 'h' && (last =='k' || last == ' ')) {
+ return;
+ }
+
+ /*
+ * Commit:
+ * We have a "Commit" clash. Resolve it.
+ *
+ * - switch off resending Commit
+ * - compare my hvi with peer's hvi
+ * - if my hvi is greater
+ * - we are Initiator, stay in state, wait for peer's DHPart1 packet
+ * - else
+ * - we are Responder, stop timer
+ * - prepare and send DH1Packt,
+ * - switch to state WaitDHPart2, implies Responder path
+ */
+ if (first == 'c' && last == ' ') {
+ ZrtpPacketCommit zpCo(pkt);
+
+ if (!parent->verifyH2(&zpCo)) {
+ return;
+ }
+ cancelTimer(); // this cancels the Commit timer T2
+
+ // if our hvi is less than peer's hvi: switch to Responder mode and
+ // send DHPart1 or Confirm1 packet. Peer (as Initiator) will retrigger if
+ // necessary
+ //
+ if (parent->compareCommit(&zpCo) < 0) {
+ if (!multiStream) {
+ ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&zpCo, &errorCode);
+
+ // Something went wrong during processing of the Commit packet
+ if (dhPart1 == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ return;
+ }
+ nextState(WaitDHPart2);
+ sentPacket = static_cast<ZrtpPacketBase *>(dhPart1);
+ }
+ else {
+ ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&zpCo, &errorCode);
+
+ // Something went wrong during processing of the Commit packet
+ if (confirm == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ return;
+ }
+ nextState(WaitConfirm2);
+ sentPacket = static_cast<ZrtpPacketBase *>(confirm);
+ }
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ }
+ }
+ // Stay in state, we are Initiator, wait for DHPart1 of Confirm1 packet from peer.
+ // Resend Commit after timeout until we get a DHPart1 or Confirm1
+ else {
+ if (startTimer(&T2) <= 0) { // restart the Commit timer, gives peer more time to react
+ timerFailed(SevereNoTimer); // returns to state Initial
+ }
+ }
+ return;
+ }
+
+ /*
+ * DHPart1:
+ * - switch off resending Commit
+ * - Prepare and send DHPart2
+ * - switch to WaitConfirm1
+ * - start timer to resend DHPart2 if necessary, we are Initiator
+ */
+ if (first == 'd') {
+ cancelTimer();
+ sentPacket = NULL;
+ ZrtpPacketDHPart dpkt(pkt);
+ ZrtpPacketDHPart* dhPart2 = parent->prepareDHPart2(&dpkt, &errorCode);
+
+ // Something went wrong during processing of the DHPart1 packet
+ if (dhPart2 == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ else {
+ if (startTimer(&T2) <= 0) {
+ timerFailed(SevereNoTimer); // switches to state Initial
+ }
+ }
+
+ return;
+ }
+ sentPacket = static_cast<ZrtpPacketBase *>(dhPart2);
+ nextState(WaitConfirm1);
+
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (startTimer(&T2) <= 0) {
+ timerFailed(SevereNoTimer); // switches to state Initial
+ }
+ return;
+ }
+
+ if (multiStream && (first == 'c' && last == '1')) {
+ cancelTimer();
+ ZrtpPacketConfirm cpkt(pkt);
+
+ ZrtpPacketConfirm* confirm = parent->prepareConfirm2MultiStream(&cpkt, &errorCode);
+
+ // Something went wrong during processing of the Confirm1 packet
+ if (confirm == NULL) {
+ sendErrorPacket(errorCode);
+ return;
+ }
+ nextState(WaitConfAck);
+ sentPacket = static_cast<ZrtpPacketBase *>(confirm);
+
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (startTimer(&T2) <= 0) {
+ timerFailed(SevereNoTimer); // returns to state Initial
+ return;
+ }
+ // according to chap 5.6: after sending Confirm2 the Initiator must
+ // be ready to receive SRTP data. SRTP sender will be enabled in WaitConfAck
+ // state.
+ if (!parent->srtpSecretsReady(ForReceiver)) {
+ parent->sendInfo(Severe, CriticalSWError);
+ sendErrorPacket(CriticalSWError);
+ return;
+ }
+ }
+ }
+ // Timer event triggered, resend the Commit packet
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (nextTimer(&T2) <= 0) {
+ timerFailed(SevereTooMuchRetries); // returns to state Initial
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+/*
+ * WaitDHPart2 state.
+ *
+ * This state handles the second part of SH key agreement. Only the Resonder
+ * can enter this state.
+ *
+ * When entering this transition function
+ * - sentPacket contains DHPart1 packet, no timer active
+ *
+ * Possible events in this state are:
+ * - Commit: Our peer didn't receive out DHPart1 thus the peer sends Commit again.
+ * Just repeat our DHPart1.
+ * - DHPart2: start second half of DH key agreement. Perpare and send own Confirm1
+ * and switch to state WaitConfirm2.
+ */
+void ZrtpStateClass::evWaitDHPart2(void) {
+
+ DEBUGOUT((cout << "Checking for match in DHPart2.\n"));
+
+ char *msg, first;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ /*
+ * Commit:
+ * - resend DHPart1
+ * - stay in state
+ */
+ if (first == 'c') {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ return sendFailed(); // returns to state Initial
+ }
+ return;
+ }
+ /*
+ * DHPart2:
+ * - prepare Confirm1 packet
+ * - switch to WaitConfirm2
+ * - No timer, we are responder
+ */
+ if (first == 'd') {
+ ZrtpPacketDHPart dpkt(pkt);
+ ZrtpPacketConfirm* confirm = parent->prepareConfirm1(&dpkt, &errorCode);
+
+ if (confirm == NULL) {
+ if (errorCode != IgnorePacket) {
+ sendErrorPacket(errorCode);
+ }
+ return;
+ }
+ nextState(WaitConfirm2);
+ sentPacket = static_cast<ZrtpPacketBase *>(confirm);
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ }
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+/*
+ * WaitConirm1 state.
+ *
+ * This state handles a received Confirm1 message and only the Initiator
+ * can enter this state.
+ *
+ * When entering this transition function in DH mode:
+ * - Initiator mode
+ * - sentPacket contains DHPart2 packet, DHPart2 timer active
+ *
+ * When entering this transition function in Multi stream mode via AckSent:
+ * - Initiator mode
+ * - sentPacket contains my Commit packet, Commit timer active
+ *
+* Possible events in this state are:
+ * - timeout for sent DHPart2 packet: causes a resend check and repeat sending
+ * of DHPart2 packet.
+ * - Confirm1: Check Confirm1 message. If it is ok then prepare and send own
+ * Confirm2 packet and switch to state WaitConfAck.
+ */
+void ZrtpStateClass::evWaitConfirm1(void) {
+
+ DEBUGOUT((cout << "Checking for match in WaitConfirm1.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * Confirm1:
+ * - Switch off resending DHPart2
+ * - prepare a Confirm2 packet
+ * - switch to state WaitConfAck
+ * - set timer to monitor Confirm2 packet, we are initiator
+ */
+ if (first == 'c' && last == '1') {
+ cancelTimer();
+ ZrtpPacketConfirm cpkt(pkt);
+
+ ZrtpPacketConfirm* confirm = parent->prepareConfirm2(&cpkt, &errorCode);
+
+ // Something went wrong during processing of the Confirm1 packet
+ if (confirm == NULL) {
+ sendErrorPacket(errorCode);
+ return;
+ }
+ nextState(WaitConfAck);
+ sentPacket = static_cast<ZrtpPacketBase *>(confirm);
+
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (startTimer(&T2) <= 0) {
+ timerFailed(SevereNoTimer); // returns to state Initial TODO check for return following this line
+ }
+ // according to chap 5.8: after sending Confirm2 the Initiator must
+ // be ready to receive SRTP data. SRTP sender will be enabled in WaitConfAck
+ // state.
+ if (!parent->srtpSecretsReady(ForReceiver)) {
+ parent->sendInfo(Severe, CriticalSWError);
+ sendErrorPacket(CriticalSWError);
+ return;
+ }
+ }
+ }
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (nextTimer(&T2) <= 0) {
+ timerFailed(SevereTooMuchRetries); // returns to state Initial
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+/*
+ * WaitConfirm2 state.
+ *
+ * Handles the Confirm2 message that closes the key agreement handshake. Only
+ * the Responder can enter this state. If the Confirm2 message is ok send a
+ * Conf2Ack to our peer. Switch to secure mode after sending Conf2Ack, our
+ * peer switches to secure mode after receiving Conf2Ack.
+ *
+ * TODO - revise documentation comments
+ *
+ * When entering this transition function
+ * - Responder mode
+ * - sentPacket contains Confirm1 packet, no timer active
+ *
+ * Possible events in this state are:
+ * - DHPart2: Our peer didn't receive our Confirm1 thus sends DHPart2 again.
+ * Just repeat our Confirm1.
+ * - Confirm2: close DH key agreement. Perpare and send own Conf2Ack
+ * and switch to state SecureState.
+ */
+void ZrtpStateClass::evWaitConfirm2(void) {
+
+ DEBUGOUT((cout << "Checking for match in WaitConfirm2.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+ uint32_t errorCode = 0;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * DHPart2 or Commit in multi stream mode:
+ * - resend Confirm1 packet
+ * - stay in state
+ */
+ if (first == 'd' || (multiStream && (first == 'c' && last == ' '))) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ }
+ return;
+ }
+ /*
+ * Confirm2:
+ * - prepare ConfAck
+ * - switch on security (SRTP)
+ * - switch to SecureState
+ */
+ if (first == 'c' && last == '2') {
+ ZrtpPacketConfirm cpkt(pkt);
+ ZrtpPacketConf2Ack* confack = parent->prepareConf2Ack(&cpkt, &errorCode);
+
+ // Something went wrong during processing of the confirm2 packet
+ if (confack == NULL) {
+ sendErrorPacket(errorCode);
+ return;
+ }
+ sentPacket = static_cast<ZrtpPacketBase *>(confack);
+
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (!parent->srtpSecretsReady(ForSender) ||
+ !parent->srtpSecretsReady(ForReceiver)) {
+ parent->sendInfo(Severe, CriticalSWError);
+ sendErrorPacket(CriticalSWError);
+ return;
+ }
+ nextState(SecureState);
+ parent->sendInfo(Info, InfoSecureStateOn);
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+/*
+ * WaitConf2Ack state.
+ *
+ * This state handles the Conf2Ack message that acknowledges the successfull
+ * processing of Confirm2. Only the Initiator can enter this state. Switch on
+ * secure mode and switch to state SecureState.
+ *
+ * When entering this transition function
+ * - Initiator mode
+ * - sentPacket contains Confirm2 packet, Confirm2 timer active
+ * - receiver security switched on
+ *
+ * Possible events in this state are:
+ * - timeout for sent Confirm2 packet: causes a resend check and repeat sending
+ * of Confirm2 packet
+ * - Conf2Ack: Key agreement was successfull, switch to secure mode.
+ */
+void ZrtpStateClass::evWaitConfAck(void) {
+
+ DEBUGOUT((cout << "Checking for match in WaitConfAck.\n"));
+
+ char *msg, first;
+ uint8_t *pkt;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ /*
+ * ConfAck:
+ * - Switch off resending Confirm2
+ * - switch to SecureState
+ */
+ if (first == 'c') {
+ cancelTimer();
+ sentPacket = NULL;
+ // Receiver was already enabled after sending Confirm2 packet
+ // see previous states.
+ if (!parent->srtpSecretsReady(ForSender)) {
+ parent->sendInfo(Severe, CriticalSWError);
+ sendErrorPacket(CriticalSWError);
+ return;
+ }
+ nextState(SecureState);
+ // TODO: call parent to clear signature data at initiator
+ parent->sendInfo(Info, InfoSecureStateOn);
+ }
+ }
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ parent->srtpSecretsOff(ForReceiver);
+ return;
+ }
+ if (nextTimer(&T2) <= 0) {
+ timerFailed(SevereTooMuchRetries); // returns to state Initial
+ parent->srtpSecretsOff(ForReceiver);
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ parent->srtpSecretsOff(ForReceiver);
+ }
+}
+
+/*
+ * When entering this transition function
+ * - sentPacket contains GoClear packet, GoClear timer active
+ */
+
+void ZrtpStateClass::evWaitClearAck(void) {
+ DEBUGOUT((cout << "Checking for match in ClearAck.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * ClearAck:
+ * - stop resending GoClear,
+ * - switch to state AckDetected, wait for peer's Hello
+ */
+ if (first == 'c' && last =='k') {
+ cancelTimer();
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+ }
+ // Timer event triggered - this is Timer T2 to resend GoClear w/o HMAC
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (nextTimer(&T2) <= 0) {
+ timerFailed(SevereTooMuchRetries); // returns to state Initial
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+
+/*
+ * WaitErrorAck state.
+ *
+ * This state belongs to the "error handling state overlay" and handle
+ * ErrorAck message. Most of the ZRTP states can send Error message for
+ * example if they detect wrong packets. After sending an Error message
+ * the protocol engine switches to WaitErrorAck state. Receiving an
+ * ErrorAck message completes the ZRTP error handling.
+ *
+ * When entering this transition function
+ * - sentPacket contains Error packet, Error timer active
+ *
+ * Possible events in this state are:
+ * - timeout for sent Error packet: causes a resend check and repeat sending
+ * of Error packet
+ * - ErrorAck: Stop timer and switch to state Initial.
+ */
+
+void ZrtpStateClass::evWaitErrorAck(void) {
+ DEBUGOUT((cout << "Checking for match in ErrorAck.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * Errorck:
+ * - stop resending Error,
+ * - switch to state Initial
+ */
+ if (first == 'e' && last =='k') {
+ cancelTimer();
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+ }
+ // Timer event triggered - this is Timer T2 to resend Error.
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return;
+ }
+ if (nextTimer(&T2) <= 0) {
+ timerFailed(SevereTooMuchRetries); // returns to state Initial
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ sentPacket = NULL;
+ nextState(Initial);
+ }
+}
+
+void ZrtpStateClass::evSecureState(void) {
+
+ DEBUGOUT((cout << "Checking for match in SecureState.\n"));
+
+ char *msg, first, last;
+ uint8_t *pkt;
+
+ /*
+ * Handle a possible substate. If substate handling was ok just return.
+ */
+ if (secSubstate == WaitSasRelayAck) {
+ if (subEvWaitRelayAck())
+ return;
+ }
+
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * Confirm2:
+ * - resend Conf2Ack packet
+ * - stay in state
+ */
+ if (first == 'c' && last == '2') {
+ if (sentPacket != NULL && !parent->sendPacketZRTP(sentPacket)) {
+ sentPacket = NULL;
+ nextState(Initial);
+ parent->srtpSecretsOff(ForSender);
+ parent->srtpSecretsOff(ForReceiver);
+ parent->zrtpNegotiationFailed(Severe, SevereCannotSend);
+ }
+ return;
+ }
+ /*
+ * GoClear received, handle it. TODO fix go clear handling
+ */
+ if (first == 'g' && last == 'r') {
+ ZrtpPacketGoClear gpkt(pkt);
+ ZrtpPacketClearAck* clearAck = parent->prepareClearAck(&gpkt);
+
+ if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(clearAck))) {
+ return;
+ }
+ // TODO Timeout to resend clear ack until user user confirmation
+ }
+ }
+ else { // unknown Event type for this state (covers Error and ZrtpClose)
+ sentPacket = NULL;
+ parent->srtpSecretsOff(ForSender);
+ parent->srtpSecretsOff(ForReceiver);
+ nextState(Initial);
+ if (event->type != ZrtpClose) {
+ parent->zrtpNegotiationFailed(Severe, SevereProtocolError);
+ }
+ parent->sendInfo(Info, InfoSecureStateOff);
+ }
+}
+
+bool ZrtpStateClass::subEvWaitRelayAck() {
+ char *msg, first, last;
+ uint8_t* pkt;
+
+ /*
+ * First check the general event type, then discrimnate the real event.
+ */
+ if (event->type == ZrtpPacket) {
+ pkt = event->packet;
+ msg = (char *)pkt + 4;
+
+ first = tolower(*msg);
+ last = tolower(*(msg+7));
+
+ /*
+ * SAS relayAck:
+ * - stop resending SASRelay,
+ * - switch to secure substate Normal
+ */
+ if (first == 'r' && last =='k') {
+ cancelTimer();
+ secSubstate = Normal;
+ sentPacket = NULL;
+ }
+ return true;
+ }
+ // Timer event triggered - this is Timer T2 to resend Error.
+ else if (event->type == Timer) {
+ if (!parent->sendPacketZRTP(sentPacket)) {
+ sendFailed(); // returns to state Initial
+ return false;
+ }
+ if (nextTimer(&T2) <= 0) {
+ // returns to state initial
+ // timerFailed(ZrtpCodes.SevereCodes.SevereTooMuchRetries);
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+int32_t ZrtpStateClass::startTimer(zrtpTimer_t *t) {
+
+ t->time = t->start;
+ t->counter = 0;
+ return parent->activateTimer(t->time);
+}
+
+int32_t ZrtpStateClass::nextTimer(zrtpTimer_t *t) {
+
+ t->time += t->time;
+ t->time = (t->time > t->capping)? t->capping : t->time;
+ t->counter++;
+ if (t->counter > t->maxResend) {
+ return -1;
+ }
+ return parent->activateTimer(t->time);
+}
+
+void ZrtpStateClass::sendErrorPacket(uint32_t errorCode) {
+ cancelTimer();
+
+ ZrtpPacketError* err = parent->prepareError(errorCode);
+ parent->zrtpNegotiationFailed(ZrtpError, errorCode);
+
+ sentPacket = static_cast<ZrtpPacketBase *>(err);
+ nextState(WaitErrorAck);
+ if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(err)) || (startTimer(&T2) <= 0)) {
+ sendFailed();
+ }
+}
+
+void ZrtpStateClass::sendSASRelay(ZrtpPacketSASrelay* relay) {
+ cancelTimer();
+ sentPacket = static_cast<ZrtpPacketBase *>(relay);
+ secSubstate = WaitSasRelayAck;
+ if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(relay)) || (startTimer(&T2) <= 0)) {
+ sendFailed();
+ }
+}
+
+void ZrtpStateClass::sendFailed() {
+ sentPacket = NULL;
+ nextState(Initial);
+ parent->zrtpNegotiationFailed(Severe, SevereCannotSend);
+}
+
+void ZrtpStateClass::timerFailed(int32_t subCode) {
+ sentPacket = NULL;
+ nextState(Initial);
+ parent->zrtpNegotiationFailed(Severe, subCode);
+}
+
+void ZrtpStateClass::setMultiStream(bool multi) {
+ multiStream = multi;
+}
+
+bool ZrtpStateClass::isMultiStream() {
+ return multiStream;
+}
+
+/** EMACS **
+ * Local variables:
+ * mode: c++
+ * c-default-style: ellemtel
+ * c-basic-offset: 4
+ * End:
+ */