LCOV - code coverage report
Current view: top level - xmpp - xmpp_state_machine.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 855 1008 84.8 %
Date: 2026-11-09 02:10:49 Functions: 130 133 97.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include "xmpp/xmpp_state_machine.h"
       7             : 
       8             : #include <atomic>
       9             : #include <typeinfo>
      10             : #include <boost/bind/bind.hpp>
      11             : #include <boost/date_time/posix_time/posix_time.hpp>
      12             : #include <boost/statechart/custom_reaction.hpp>
      13             : #include <boost/statechart/event.hpp>
      14             : #include <boost/statechart/simple_state.hpp>
      15             : #include <boost/statechart/state.hpp>
      16             : #include <boost/statechart/state_machine.hpp>
      17             : #include <boost/statechart/transition.hpp>
      18             : 
      19             : #include "base/logging.h"
      20             : #include "base/task_annotations.h"
      21             : #include "io/event_manager.h"
      22             : #include "io/ssl_session.h"
      23             : #include "sandesh/sandesh_trace.h"
      24             : #include "sandesh/common/vns_types.h"
      25             : #include "sandesh/common/vns_constants.h"
      26             : #include "sandesh/xmpp_client_server_sandesh_types.h"
      27             : #include "sandesh/xmpp_peer_info_types.h"
      28             : #include "sandesh/xmpp_state_machine_sandesh_types.h"
      29             : #include "sandesh/xmpp_trace_sandesh_types.h"
      30             : #include "xmpp/xmpp_connection.h"
      31             : #include "xmpp/xmpp_factory.h"
      32             : #include "xmpp/xmpp_log.h"
      33             : #include "xmpp/xmpp_server.h"
      34             : #include "xmpp/xmpp_session.h"
      35             : 
      36             : using namespace std;
      37             : using namespace boost::placeholders;
      38             : 
      39             : namespace mpl = boost::mpl;
      40             : namespace sc = boost::statechart;
      41             : 
      42             : #define SM_LOG(_sm, _msg) do {  \
      43             :     XMPP_UTDEBUG(XmppStateMachineDebug,                                        \
      44             :                  (_sm)->connection() ? (_sm)->connection()->ToUVEKey() : "",   \
      45             :                  XMPP_PEER_DIR_NA, (_sm)->ChannelType(), _msg);                \
      46             : } while (false)
      47             : 
      48             : namespace xmsm {
      49             : // events
      50             : 
      51             : struct EvStart : sc::event<EvStart> {
      52             :     static const char *Name() {
      53             :         return "EvStart";
      54             :     }
      55             : };
      56             : 
      57             : struct EvStop : sc::event<EvStop> {
      58        2866 :     static const char *Name() {
      59        2866 :         return "EvStop";
      60             :     }
      61             : };
      62             : 
      63             : struct EvAdminDown : sc::event<EvAdminDown> {
      64        3354 :     static const char *Name() {
      65        3354 :         return "EvAdminDown";
      66             :     }
      67             : };
      68             : 
      69             : struct EvConnectTimerExpired : sc::event<EvConnectTimerExpired> {
      70        7747 :     static const char *Name() {
      71        7747 :         return "EvConnectTimerExpired";
      72             :     }
      73             : };
      74             : 
      75             : struct EvOpenTimerExpired : sc::event<EvOpenTimerExpired> {
      76           1 :     static const char *Name() {
      77           1 :         return "EvOpenTimerExpired";
      78             :     }
      79             : };
      80             : 
      81             : struct EvHoldTimerExpired : sc::event<EvHoldTimerExpired> {
      82          13 :     static const char *Name() {
      83          13 :         return "EvHoldTimerExpired";
      84             :     }
      85             : };
      86             : 
      87             : struct EvTcpConnected : sc::event<EvTcpConnected> {
      88        6583 :     EvTcpConnected(XmppSession *session) : session(session) { };
      89        6583 :     static const char *Name() {
      90        6583 :         return "EvTcpConnected";
      91             :     }
      92             :     XmppSession *session;
      93             : } ;
      94             : 
      95             : struct EvTcpConnectFail : sc::event<EvTcpConnectFail> {
      96        1159 :     EvTcpConnectFail(XmppSession *session) : session(session) { };
      97        1159 :     static const char *Name() {
      98        1159 :         return "EvTcpConnectFail";
      99             :     }
     100             :     XmppSession *session;
     101             : } ;
     102             : 
     103             : struct EvTcpPassiveOpen : sc::event<EvTcpPassiveOpen> {
     104        6521 :     EvTcpPassiveOpen(XmppSession *session) : session(session) { };
     105        6520 :     static const char *Name() {
     106        6520 :         return "EvTcpPassiveOpen";
     107             :     }
     108             :     XmppSession *session;
     109             : };
     110             : 
     111             : struct EvTcpClose : sc::event<EvTcpClose> {
     112        6687 :     EvTcpClose(XmppSession *session) : session(session) { };
     113        6474 :     static const char *Name() {
     114        6474 :         return "EvTcpClose";
     115             :     }
     116             :     XmppSession *session;
     117             : };
     118             : 
     119             : struct EvTcpDeleteSession : sc::event<EvTcpDeleteSession> {
     120       12306 :     explicit EvTcpDeleteSession(TcpSession *session) : session(session) { }
     121             :     static const char *Name() {
     122             :         return "EvTcpDeleteSession";
     123             :     }
     124             :     TcpSession *session;
     125             : };
     126             : 
     127             : struct EvXmppMessage : sc::event<EvXmppMessage> {
     128     4110882 :     explicit EvXmppMessage(XmppSession *session,
     129     4110882 :         const XmppStanza::XmppMessage *msg) : session(session), msg(msg) { }
     130             :     static const char *Name() {
     131             :         return "EvXmppMessage";
     132             :     }
     133             :     XmppSession *session;
     134             :     const XmppStanza::XmppMessage *msg;
     135             : };
     136             : 
     137             : struct EvXmppOpen : public sc::event<EvXmppOpen> {
     138       15590 :     EvXmppOpen(XmppSession *session, const XmppStanza::XmppMessage *msg) :
     139       15590 :         session(session),
     140       15590 :         msg(static_cast<const XmppStanza::XmppStreamMessage *>(msg)) {
     141       15590 :     }
     142       15588 :     static const char *Name() {
     143       15588 :         return "EvXmppOpen";
     144             :     }
     145             :     XmppSession *session;
     146             :     boost::shared_ptr<const XmppStanza::XmppStreamMessage> msg;
     147             : };
     148             : 
     149             : struct EvStreamFeatureRequest : sc::event<EvStreamFeatureRequest> {
     150        1469 :     EvStreamFeatureRequest(XmppSession *session,
     151        1469 :                            const XmppStanza::XmppMessage *msg) :
     152        1469 :         session(session),
     153        1469 :         msg(static_cast<const XmppStanza::XmppStreamMessage *>(msg)) {
     154        1469 :     }
     155        1469 :     static const char *Name() {
     156        1469 :         return "EvStreamFeatureRequest";
     157             :     }
     158             :     XmppSession *session;
     159             :     boost::shared_ptr<const XmppStanza::XmppMessage> msg;
     160             : };
     161             : 
     162             : struct EvStartTls : sc::event<EvStartTls> {
     163        1464 :     EvStartTls(XmppSession *session,
     164        1464 :                const XmppStanza::XmppMessage *msg) :
     165        1464 :         session(session),
     166        1464 :         msg(static_cast<const XmppStanza::XmppStreamMessage *>(msg)) {
     167        1464 :     }
     168        1464 :     static const char *Name() {
     169        1464 :         return "EvStartTls";
     170             :     }
     171             :     XmppSession *session;
     172             :     boost::shared_ptr<const XmppStanza::XmppMessage> msg;
     173             : };
     174             : 
     175             : struct EvTlsProceed : sc::event<EvTlsProceed> {
     176        1467 :     EvTlsProceed(XmppSession *session,
     177        1467 :                  const XmppStanza::XmppMessage *msg) :
     178        1467 :         session(session),
     179        1467 :         msg(static_cast<const XmppStanza::XmppStreamMessage *>(msg)) {
     180        1467 :     }
     181        1467 :     static const char *Name() {
     182        1467 :         return "EvTlsProceed";
     183             :     }
     184             :     XmppSession *session;
     185             :     boost::shared_ptr<const XmppStanza::XmppMessage> msg;
     186             : };
     187             : 
     188             : struct EvTlsHandShakeSuccess : sc::event<EvTlsHandShakeSuccess> {
     189        2924 :     explicit EvTlsHandShakeSuccess(XmppSession *session) :
     190        2924 :         session(session) { }
     191        2924 :     static const char *Name() {
     192        2924 :         return "EvTlsHandShakeSuccess";
     193             :     }
     194             :     XmppSession *session;
     195             : };
     196             : 
     197             : struct EvTlsHandShakeFailure : sc::event<EvTlsHandShakeFailure> {
     198           2 :     explicit EvTlsHandShakeFailure(XmppSession *session) :
     199           2 :         session(session) { }
     200           2 :     static const char *Name() {
     201           2 :         return "EvTlsHandShakeFailure";
     202             :     }
     203             :     XmppSession *session;
     204             : };
     205             : 
     206             : struct EvXmppKeepalive : sc::event<EvXmppKeepalive> {
     207     2476835 :     EvXmppKeepalive(XmppSession *session,
     208     2476835 :                     const XmppStanza::XmppMessage *msg) :
     209     2476835 :         session(session), msg(msg) {
     210     2476835 :     }
     211             :     static const char *Name() {
     212             :         return "EvXmppKeepalive";
     213             :     }
     214             :     XmppSession *session;
     215             :     boost::shared_ptr<const XmppStanza::XmppMessage> msg;
     216             : };
     217             : 
     218             : struct EvXmppMessageStanza : sc::event<EvXmppMessageStanza> {
     219     1556755 :     EvXmppMessageStanza(XmppSession *session,
     220     1556755 :                          const XmppStanza::XmppMessage *msg) :
     221     1556755 :     session(session), msg(msg) {
     222     1556755 :     }
     223             :     static const char *Name() {
     224             :         return "EvXmppMessageStanza";
     225             :     }
     226             :     XmppSession *session;
     227             :     boost::shared_ptr<const XmppStanza::XmppMessage> msg;
     228             : };
     229             : 
     230             : struct EvXmppIqStanza : sc::event<EvXmppIqStanza> {
     231       56895 :     EvXmppIqStanza(XmppSession *session,
     232             :                     const XmppStanza::XmppMessage *msg)
     233       56895 :     : session(session), msg(msg) {
     234       56895 :     }
     235             :     static const char *Name() {
     236             :         return "EvXmppIqStanza";
     237             :     }
     238             :     XmppSession *session;
     239             :     boost::shared_ptr<const XmppStanza::XmppMessage> msg;
     240             : };
     241             : 
     242             : struct EvXmppOpenReceive : sc::event<EvXmppOpenReceive> {
     243             :     EvXmppOpenReceive(XmppSession *session) : session(session) {
     244             :     }
     245             :     static const char *Name() {
     246             :         return "EvXmppOpenReceive";
     247             :     }
     248             :     XmppSession *session;
     249             : };
     250             : 
     251             : struct Idle : public sc::state<Idle, XmppStateMachine> {
     252             :     typedef sc::transition<EvStart, Active,
     253             :             XmppStateMachine, &XmppStateMachine::OnStart> reactions;
     254       21773 :     Idle(my_context ctx) : my_base(ctx) {
     255       21775 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     256       21780 :         bool flap = (state_machine->get_state() == ESTABLISHED);
     257       21779 :         state_machine->set_state(IDLE);
     258       21782 :         state_machine->SendConnectionInfo("Start", "Active");
     259       21784 :         if (flap) {
     260        9482 :             state_machine->connection()->increment_flap_count();
     261             :         }
     262       21783 :     }
     263             : };
     264             : 
     265             : struct Active : public sc::state<Active, XmppStateMachine> {
     266             :     typedef mpl::list<
     267             :         sc::custom_reaction<EvAdminDown>,
     268             :         sc::custom_reaction<EvConnectTimerExpired>,
     269             :         sc::custom_reaction<EvOpenTimerExpired>,
     270             :         sc::custom_reaction<EvTcpPassiveOpen>,
     271             :         sc::custom_reaction<EvTcpClose>,
     272             :         sc::custom_reaction<EvXmppOpen>,
     273             :         sc::custom_reaction<EvStop>
     274             :     > reactions;
     275             : 
     276       15977 :     Active(my_context ctx) : my_base(ctx) {
     277       15976 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     278       15978 :         state_machine->keepalive_count_clear();
     279       15977 :         bool flap = (state_machine->get_state() == ESTABLISHED);
     280       15977 :         state_machine->set_state(ACTIVE);
     281       15978 :         if (flap) {
     282        2578 :             state_machine->connection()->increment_flap_count();
     283             :         }
     284       15978 :         if (state_machine->IsActiveChannel() ) {
     285        9181 :             if (state_machine->get_connect_attempts() >=
     286             :                 XmppStateMachine::kMaxAttempts) {
     287         285 :                 XmppConnection *connection = state_machine->connection();
     288         285 :                 if (connection) {
     289         285 :                     state_machine->SendConnectionInfo(
     290             :                         "Connect failed after retries");
     291             :                     // Notify clients if any action to be taken
     292         285 :                     connection->ChannelMux()->HandleStateEvent(xmsm::ACTIVE);
     293             :                 }
     294             :             }
     295        9181 :             state_machine->StartConnectTimer(state_machine->GetConnectTime());
     296             :         }
     297       15978 :     }
     298       31957 :     ~Active() {
     299       15979 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     300       15979 :         state_machine->CancelConnectTimer();
     301       15979 :         state_machine->CancelOpenTimer();
     302       31957 :     }
     303             : 
     304             :    //event on client only
     305        7746 :     sc::result react(const EvConnectTimerExpired &event) {
     306        7746 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     307        7746 :         if (state_machine->ConnectTimerCancelled()) {
     308           0 :             SM_LOG(state_machine, "Discard EvConnectTimerExpired in (Active) State");
     309           0 :             return discard_event();
     310             :         } else {
     311        7746 :             state_machine->SendConnectionInfo(event.Name(), "Connect");
     312        7746 :             SM_LOG(state_machine, "EvConnectTimerExpired in (Active) State");
     313        7746 :             return transit<Connect>();
     314             :         }
     315             :     }
     316             : 
     317             :     // event on server only
     318        6521 :     sc::result react(const EvTcpPassiveOpen &event) {
     319        6521 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     320        6520 :         event.session->AsyncReadStart();
     321        6521 :         assert(state_machine->session() == event.session);
     322        6521 :         event.session->set_observer(
     323             :             boost::bind(&XmppStateMachine::OnSessionEvent,
     324             :                         state_machine, _1, _2));
     325        6520 :         state_machine->StartOpenTimer(XmppStateMachine::kOpenTime);
     326        6521 :         XmppConnectionInfo info;
     327        6521 :         info.set_local_port(event.session->local_port());
     328        6519 :         info.set_remote_port(event.session->remote_port());
     329        6521 :         state_machine->SendConnectionInfo(&info, event.Name());
     330       13042 :         return discard_event();
     331        6521 :     }
     332             : 
     333             :     // event on server only
     334         182 :     sc::result react(const EvTcpClose &event) {
     335         182 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     336         182 :         if (event.session != state_machine->session()) {
     337           0 :             return discard_event();
     338             :         }
     339         182 :         state_machine->CancelOpenTimer();
     340         182 :         state_machine->ResetSession();
     341         182 :         state_machine->SendConnectionInfo(event.Name(), "Idle");
     342         182 :         return transit<Idle>();
     343             :     }
     344             : 
     345             :     //event on server only
     346           1 :     sc::result react(const EvOpenTimerExpired &event) {
     347           1 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     348           1 :         if (state_machine->OpenTimerCancelled()) {
     349           0 :             SM_LOG(state_machine,
     350             :                    "Discard EvOpenTimerExpired in (Active) State");
     351           0 :             return discard_event();
     352             :         }
     353             : 
     354             :         // At this point session on connection is not set, hence SendClose
     355             :         // using session on the state_machine.
     356           1 :         XmppSession *session = state_machine->session();
     357           1 :         XmppConnection *connection = state_machine->connection();
     358           1 :         connection->SendClose(session);
     359           1 :         state_machine->ResetSession();
     360           1 :         state_machine->SendConnectionInfo(event.Name(), "Idle");
     361           1 :         return transit<Idle>();
     362             :     }
     363             : 
     364             :     //event on server only
     365        6358 :     sc::result react(const EvXmppOpen &event) {
     366        6358 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     367        6358 :         if (event.session != state_machine->session()) {
     368           0 :             return discard_event();
     369             :         }
     370        6358 :         state_machine->AssignSession();
     371             : 
     372        6358 :         XmppConnection *connection = state_machine->connection();
     373        6358 :         if (connection->IsDeleted()) {
     374           0 :             state_machine->ResetSession();
     375           0 :             return discard_event();
     376             :         }
     377             : 
     378        6358 :         XmppSession *session = state_machine->session();
     379        6358 :         if (!state_machine->IsAuthEnabled())
     380        4846 :             state_machine->ResurrectOldConnection(connection, session);
     381             : 
     382             :         // Get the possibly updated XmppConnection information.
     383        6358 :         connection = state_machine->connection();
     384        6358 :         state_machine->CancelOpenTimer();
     385        6358 :         if (!connection->SendOpenConfirm(session)) {
     386           0 :             connection->SendClose(session);
     387           0 :             state_machine->ResetSession();
     388           0 :             state_machine->SendConnectionInfo("Send Open Confirm Failed",
     389             :                                               "Idle");
     390           0 :             return transit<Idle>();
     391             :         } else {
     392        6358 :             XmppConnectionInfo info;
     393        6358 :             info.set_identifier(event.msg->from);
     394        6358 :             if (state_machine->IsAuthEnabled()) {
     395        1512 :                 state_machine->SendConnectionInfo(&info, event.Name(),
     396             :                                                   "Open Confirm");
     397        1512 :                 return transit<OpenConfirm>();
     398             :             } else {
     399        4846 :                 connection->StartKeepAliveTimer();
     400        4846 :                 state_machine->SendConnectionInfo(&info, event.Name(),
     401             :                                                   "Established");
     402        4846 :                 return transit<XmppStreamEstablished>();
     403             :             }
     404        6358 :         }
     405             :     }
     406             : 
     407          66 :     sc::result react(const EvStop &event) {
     408          66 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     409          66 :         XmppConnection *connection = state_machine->connection();
     410          66 :         state_machine->CancelOpenTimer();
     411          66 :         state_machine->CancelConnectTimer();
     412          66 :         connection->StopKeepAliveTimer();
     413             : 
     414          66 :         if (state_machine->IsActiveChannel() ) {
     415           1 :             state_machine->set_session(NULL);
     416           1 :             state_machine->SendConnectionInfo(event.Name(), "Active");
     417           1 :             return transit<Active>();
     418             :         } else {
     419          65 :             state_machine->ResetSession();
     420          65 :             state_machine->SendConnectionInfo(event.Name(), "Idle");
     421          65 :             return transit<Idle>();
     422             :         }
     423             :     }
     424             : 
     425          99 :     sc::result react(const EvAdminDown &event) {
     426          99 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     427          99 :         XmppConnectionInfo info;
     428          99 :         info.set_close_reason("Administratively down");
     429          99 :         state_machine->connection()->set_close_reason("Administratively down");
     430          99 :         state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
     431         198 :         return transit<Idle>();
     432          99 :     }
     433             : 
     434             : };
     435             : 
     436             : //State valid only for client side, connection in Active state
     437             : struct Connect : public sc::state<Connect, XmppStateMachine> {
     438             :     typedef mpl::list<
     439             :         sc::custom_reaction<EvAdminDown>,
     440             :         sc::custom_reaction<EvConnectTimerExpired>,
     441             :         sc::custom_reaction<EvTcpConnected>,
     442             :         sc::custom_reaction<EvTcpConnectFail>,
     443             :         sc::custom_reaction<EvTcpClose>,
     444             :         sc::custom_reaction<EvStop>
     445             :     > reactions;
     446             : 
     447             :     static const int kConnectTimeout = 60;  // seconds
     448             : 
     449        7746 :     Connect(my_context ctx) : my_base(ctx) {
     450        7746 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     451        7746 :         StartSession(state_machine);
     452        7746 :         state_machine->connect_attempts_inc();
     453        7746 :         state_machine->set_state(CONNECT);
     454        7746 :         state_machine->StartConnectTimer(state_machine->GetConnectTime());
     455        7746 :         XmppSession *session = state_machine->session();
     456        7746 :         if (session != NULL) {
     457        7746 :             XmppConnectionInfo info;
     458        7746 :             info.set_local_port(session->local_port());
     459        7746 :             info.set_remote_port(session->remote_port());
     460        7746 :             state_machine->SendConnectionInfo(&info, "Connect Event");
     461        7746 :        }
     462        7746 :     }
     463       15492 :     ~Connect() {
     464        7746 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     465        7746 :         state_machine->CancelConnectTimer();
     466       15492 :     }
     467             : 
     468           1 :     sc::result react(const EvConnectTimerExpired &event) {
     469           1 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     470           1 :         if (state_machine->ConnectTimerCancelled()) {
     471           0 :             SM_LOG(state_machine,
     472             :                    "Discard EvConnectTimerExpired in (Connect) State");
     473           0 :             return discard_event();
     474             :         }
     475           1 :         CloseSession(state_machine);
     476           1 :         XmppConnectionInfo info;
     477           1 :         info.set_close_reason("Connect timer expired");
     478           1 :         state_machine->connection()->set_close_reason("Connect timer expired");
     479           1 :         state_machine->SendConnectionInfo(&info, event.Name(), "Active");
     480           1 :         return transit<Active>();
     481           1 :     }
     482             : 
     483        6583 :     sc::result react(const EvTcpConnected &event) {
     484        6583 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     485        6583 :         if (event.session != state_machine->session()) {
     486           0 :             return discard_event();
     487             :         }
     488        6583 :         XmppConnection *connection = state_machine->connection();
     489        6583 :         state_machine->CancelConnectTimer();
     490        6583 :         XmppSession *session = state_machine->session();
     491        6583 :         XmppConnectionInfo info;
     492        6583 :         info.set_local_port(session->local_port());
     493        6583 :         info.set_remote_port(session->remote_port());
     494        6583 :         if (connection->SendOpen(session)) {
     495        6583 :             state_machine->StartHoldTimer();
     496        6583 :             state_machine->SendConnectionInfo(&info, event.Name(), "OpenSent");
     497        6583 :             return transit<OpenSent>();
     498             :         } else {
     499           0 :             SM_LOG(state_machine, "SendOpen failed in (Connect) State");
     500           0 :             CloseSession(state_machine);
     501           0 :             info.set_close_reason("SendOpen failed");
     502           0 :             state_machine->connection()->set_close_reason("Send Open failed");
     503           0 :             state_machine->SendConnectionInfo(&info, "Send Open failed",
     504             :                                               "Active");
     505           0 :             return transit<Active>();
     506             :         }
     507        6583 :     }
     508             : 
     509        1159 :     sc::result react(const EvTcpConnectFail &event) {
     510             :         // delete session; restart connect timer.
     511        1159 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     512        1159 :         if (event.session != state_machine->session()) {
     513           0 :             return discard_event();
     514             :         }
     515        1159 :         state_machine->set_session(NULL);
     516        1159 :         state_machine->CancelConnectTimer();
     517        1159 :         state_machine->SendConnectionInfo(event.Name(), "Active");
     518        1159 :         return transit<Active>();
     519             :     }
     520             : 
     521           1 :     sc::result react(const EvTcpClose &event) {
     522           1 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     523           1 :         if (event.session != state_machine->session()) {
     524           0 :             return discard_event();
     525             :         }
     526             :         // close the tcp sessions.
     527           1 :         CloseSession(state_machine);
     528           1 :         state_machine->CancelConnectTimer();
     529           1 :         state_machine->SendConnectionInfo(event.Name(), "Active");
     530           1 :         return transit<Active>();
     531             :     }
     532             : 
     533           1 :     sc::result react(const EvStop &event) {
     534           1 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     535           1 :         CloseSession(state_machine);
     536           1 :         state_machine->CancelConnectTimer();
     537           1 :         XmppConnectionInfo info;
     538           1 :         info.set_close_reason("EvStop received");
     539           1 :         state_machine->connection()->set_close_reason("EvStop received");
     540           1 :         state_machine->SendConnectionInfo(&info, event.Name(), "Active");
     541           2 :         return transit<Active>();
     542           1 :     }
     543             : 
     544           0 :     sc::result react(const EvAdminDown &event) {
     545           0 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     546           0 :         CloseSession(state_machine);
     547           0 :         XmppConnectionInfo info;
     548           0 :         info.set_close_reason("Administratively down");
     549           0 :         state_machine->connection()->set_close_reason("Administratively down");
     550           0 :         state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
     551           0 :         return transit<Idle>();
     552           0 :     }
     553             : 
     554             :     // Create an active connection request.
     555        7746 :     void StartSession(XmppStateMachine *state_machine) {
     556        7746 :         XmppConnection *connection = state_machine->connection();
     557        7746 :         XmppSession *session = connection->CreateSession();
     558        7746 :         state_machine->set_session(session);
     559        7746 :         session->set_observer(boost::bind(&XmppStateMachine::OnSessionEvent,
     560             :                                           state_machine, _1, _2));
     561        7746 :         boost::system::error_code err;
     562        7746 :         session->socket()->bind(connection->local_endpoint(), err);
     563        7746 :         if (err) {
     564           0 :             LOG(WARN, "Bind failure for local address " <<
     565             :                 connection->local_endpoint() << " : " << err.message());
     566           0 :             assert(false);
     567             :         }
     568        7746 :         connection->server()->Connect(session, connection->endpoint());
     569        7746 :     }
     570             : 
     571           3 :     void CloseSession(XmppStateMachine *state_machine) {
     572           3 :         state_machine->set_session(NULL);
     573           3 :     }
     574             : };
     575             : 
     576             : // The client reaches OpenSent after sending an immediate OPEN on a active
     577             : // connection. Server should not come in this state.
     578             : struct OpenSent : public sc::state<OpenSent, XmppStateMachine> {
     579             :     typedef mpl::list<
     580             :         sc::custom_reaction<EvAdminDown>,
     581             :         sc::custom_reaction<EvTcpClose>,
     582             :         sc::custom_reaction<EvXmppOpen>,
     583             :         sc::custom_reaction<EvHoldTimerExpired>,
     584             :         sc::custom_reaction<EvStop>
     585             :     > reactions;
     586             : 
     587        6583 :     OpenSent(my_context ctx) : my_base(ctx) {
     588        6583 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     589        6583 :         state_machine->set_state(OPENSENT);
     590        6583 :     }
     591       13166 :     ~OpenSent() {
     592        6583 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     593        6583 :         state_machine->CancelHoldTimer();
     594       13166 :     }
     595             : 
     596         175 :     sc::result react(const EvTcpClose &event) {
     597         175 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     598         175 :         if (event.session != state_machine->session()) {
     599           0 :             return discard_event();
     600             :         }
     601         175 :         state_machine->CancelHoldTimer();
     602         175 :         if (event.session) {
     603         175 :             state_machine->set_session(NULL);
     604         175 :             state_machine->SendConnectionInfo(event.Name(), "Active");
     605         175 :             return transit<Active>();
     606             :         }
     607           0 :         return discard_event();
     608             :     }
     609             : 
     610        6312 :     sc::result react(const EvXmppOpen &event) {
     611        6312 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     612        6312 :         XmppConnection *connection = state_machine->connection();
     613        6312 :         if (event.session == state_machine->session()) {
     614        6312 :             state_machine->AssignSession();
     615        6312 :             XmppConnectionInfo info;
     616        6312 :             info.set_identifier(event.msg->from);
     617        6312 :             if (state_machine->IsAuthEnabled()) {
     618        1472 :                 state_machine->SendConnectionInfo(&info, event.Name(),
     619             :                                                   "Open Confirm");
     620        1472 :                 return transit<OpenConfirm>();
     621             :             } else {
     622        4840 :                 connection->SendKeepAlive();
     623        4840 :                 connection->StartKeepAliveTimer();
     624        4840 :                 state_machine->StartHoldTimer();
     625        4840 :                 state_machine->SendConnectionInfo(&info, event.Name(),
     626             :                                                   "Established");
     627        4840 :                 return transit<XmppStreamEstablished>();
     628             :             }
     629        6312 :         }
     630           0 :         return discard_event();
     631             :     }
     632             : 
     633           2 :     sc::result react(const EvHoldTimerExpired &event) {
     634           2 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     635           2 :         if (state_machine->HoldTimerCancelled()) {
     636           0 :             SM_LOG(state_machine,
     637             :                    "Discard EvHoldTimerExpired in (OpenSent) State");
     638           0 :             return discard_event();
     639             :         }
     640           2 :         CloseSession(state_machine);
     641           2 :         XmppConnectionInfo info;
     642           2 :         info.set_close_reason("Hold timer expired");
     643           2 :         state_machine->connection()->set_close_reason("Hold timer expired");
     644           2 :         state_machine->SendConnectionInfo(&info, event.Name(), "Active");
     645           2 :         return transit<Active>();
     646           2 :     }
     647             : 
     648           2 :     sc::result react(const EvStop &event) {
     649           2 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     650           2 :         state_machine->CancelHoldTimer();
     651           2 :         CloseSession(state_machine);
     652           2 :         XmppConnectionInfo info;
     653           2 :         info.set_close_reason("EvStop received");
     654           2 :         state_machine->connection()->set_close_reason("EvStop received");
     655           2 :         state_machine->SendConnectionInfo(&info, event.Name(), "Active");
     656           4 :         return transit<Active>();
     657           2 :     }
     658             : 
     659          72 :     sc::result react(const EvAdminDown &event) {
     660          72 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     661          72 :         CloseSession(state_machine);
     662          72 :         XmppConnectionInfo info;
     663          72 :         info.set_close_reason("Administratively down");
     664          72 :         state_machine->connection()->set_close_reason("Administratively down");
     665          72 :         state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
     666         144 :         return transit<Idle>();
     667          72 :     }
     668             : 
     669          76 :     void CloseSession(XmppStateMachine *state_machine) {
     670          76 :         state_machine->set_session(NULL);
     671          76 :     }
     672             : };
     673             : 
     674             : struct OpenConfirm : public sc::state<OpenConfirm, XmppStateMachine> {
     675             :     typedef mpl::list<
     676             :         sc::custom_reaction<EvAdminDown>,
     677             :         sc::custom_reaction<EvTcpClose>,
     678             :         sc::custom_reaction<EvHoldTimerExpired>,
     679             :         sc::custom_reaction<EvStreamFeatureRequest>, //received by client
     680             :         sc::custom_reaction<EvStartTls>,             //received by server
     681             :         sc::custom_reaction<EvTlsProceed>,           //received by client
     682             :         sc::custom_reaction<EvTlsHandShakeSuccess>,
     683             :         sc::custom_reaction<EvTlsHandShakeFailure>,
     684             :         sc::custom_reaction<EvXmppOpen>,             //received by server
     685             :         sc::custom_reaction<EvStop>
     686             :     > reactions;
     687             : 
     688        2984 :     OpenConfirm(my_context ctx) : my_base(ctx) {
     689        2984 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     690        2984 :         state_machine->StartHoldTimer();
     691        2984 :         XmppConnectionInfo info;
     692        2984 :         if (!state_machine->IsActiveChannel()) { //server
     693        1512 :             if (state_machine->IsAuthEnabled()) {
     694        1512 :                 XmppConnection *connection = state_machine->connection();
     695        1512 :                 XmppSession *session = state_machine->session();
     696        1512 :                 if (!connection->SendStreamFeatureRequest(session)) {
     697           0 :                     connection->SendClose(session);
     698           0 :                     SM_LOG(state_machine,
     699             :                         "Xmpp Send Stream Feature Request Failed, IDLE");
     700           0 :                     state_machine->ResetSession();
     701           0 :                     info.set_close_reason("Send Stream Feature Request Failed");
     702           0 :                     state_machine->connection()->set_close_reason(
     703             :                         "Send Stream Feature Request Failed");
     704           0 :                     state_machine->SendConnectionInfo(&info,
     705             :                         "Send Stream Feature Request failed", "Idle");
     706             :                     // cannot transition state as this is the constructor
     707             :                     // of new state
     708             :                 }
     709             :             }
     710             :         }
     711        2984 :         state_machine->set_state(OPENCONFIRM);
     712        2984 :         state_machine->set_openconfirm_state(OPENCONFIRM_INIT);
     713        2984 :     }
     714             : 
     715          46 :     sc::result react(const EvTcpClose &event) {
     716          46 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     717          46 :         state_machine->CancelHoldTimer();
     718          46 :         if (state_machine->IsActiveChannel() ) {
     719           1 :             CloseSession(state_machine);
     720           1 :             state_machine->SendConnectionInfo(event.Name(), "Active");
     721           1 :             return transit<Active>();
     722             :         } else {
     723          45 :             state_machine->ResetSession();
     724          45 :             state_machine->SendConnectionInfo(event.Name(), "Idle");
     725          45 :             return transit<Idle>();
     726             :         }
     727             :     }
     728             : 
     729           6 :     sc::result react(const EvHoldTimerExpired &event) {
     730           6 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     731           6 :         if (state_machine->HoldTimerCancelled()) {
     732           0 :             SM_LOG(state_machine,
     733             :                    "Discard EvHoldTimerExpired in (OpenConfirm) State");
     734           0 :             return discard_event();
     735             :         }
     736           6 :         XmppConnectionInfo info;
     737           6 :         info.set_close_reason("Hold timer expired");
     738           6 :         state_machine->connection()->set_close_reason("Hold timer expired");
     739           6 :         if (state_machine->IsActiveChannel() ) {
     740           4 :             CloseSession(state_machine);
     741           4 :             state_machine->SendConnectionInfo(&info, event.Name(), "Active");
     742           4 :             return transit<Active>();
     743             :         } else {
     744           2 :             state_machine->ResetSession();
     745           2 :             state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
     746           2 :             return transit<Idle>();
     747             :         }
     748           6 :     }
     749             : 
     750             :     // received by the client
     751        1469 :     sc::result react(const EvStreamFeatureRequest &event) {
     752        1469 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     753        1469 :         XmppConnection *connection = state_machine->connection();
     754        1469 :         XmppSession *session = state_machine->session();
     755             :         // TODO, we need to have a supported stream feature list
     756             :         // and compare against the requested stream feature list
     757             :         // which will enable us to send start of various features
     758        1469 :         if (!connection->SendStartTls(session)) {
     759           0 :             connection->SendClose(session);
     760           0 :             state_machine->ResetSession();
     761           0 :             XmppConnectionInfo info;
     762           0 :             info.set_close_reason("Send Start Tls Failed");
     763           0 :             state_machine->SendConnectionInfo(&info, event.Name(), "Active");
     764           0 :             return transit<Active>();
     765           0 :         } else {
     766        1469 :             state_machine->StartHoldTimer();
     767        1469 :             state_machine->SendConnectionInfo(event.Name(),
     768             :                 "Sent Start Tls, OpenConfirm Feature Negotiation");
     769        1469 :             state_machine->set_openconfirm_state(
     770             :                        OPENCONFIRM_FEATURE_NEGOTIATION);
     771        1469 :             return discard_event();
     772             :         }
     773             :     }
     774             : 
     775             :     // received by client
     776        1467 :     sc::result react(const EvTlsProceed &event) {
     777        1467 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     778        1467 :         XmppConnection *connection = state_machine->connection();
     779        1467 :         XmppSession *session = state_machine->session();
     780        1467 :         state_machine->StartHoldTimer();
     781        1467 :         XmppConnectionInfo info;
     782        1467 :         info.set_identifier(connection->GetTo());
     783             :         // Trigger Ssl Handshake on client side
     784        1467 :         session->TriggerSslHandShake(state_machine->HandShakeCbHandler());
     785        1467 :         state_machine->SendConnectionInfo( &info, event.Name(),
     786             :             "Trigger Client Ssl Handshake");
     787        2934 :         return discard_event();
     788        1467 :     }
     789             : 
     790             :     //received by server
     791        1464 :     sc::result react(const EvStartTls &event) {
     792        1464 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     793        1464 :         if (event.session != state_machine->session()) {
     794           0 :             return discard_event();
     795             :         }
     796        1464 :         XmppConnection *connection = state_machine->connection();
     797        1464 :         XmppSession *session = state_machine->session();
     798        1464 :         XmppConnectionInfo info;
     799        1464 :         info.set_identifier(connection->GetTo());
     800        1464 :         if (!connection->SendProceedTls(session)) {
     801           0 :             connection->SendClose(session);
     802           0 :             state_machine->ResetSession();
     803           0 :             info.set_close_reason("Send Proceed Tls Failed");
     804           0 :             state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
     805           0 :             return transit<Idle>();
     806             :         } else {
     807        1464 :             state_machine->StartHoldTimer();
     808             :             // Trigger Ssl Handshake on server side
     809        1464 :             session->TriggerSslHandShake(state_machine->HandShakeCbHandler());
     810        1464 :             state_machine->SendConnectionInfo(&info, event.Name(),
     811             :                 "Trigger Server Ssl Handshake");
     812        1464 :             return discard_event();
     813             :         }
     814        1464 :     }
     815             : 
     816           1 :     sc::result react(const EvStop &event) {
     817           1 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     818           1 :         state_machine->CancelHoldTimer();
     819           1 :         if (state_machine->IsActiveChannel() ) {
     820           1 :             CloseSession(state_machine);
     821           1 :             state_machine->SendConnectionInfo(event.Name(), "Active");
     822           1 :             return transit<Active>();
     823             :         } else {
     824           0 :             state_machine->ResetSession();
     825           0 :             state_machine->SendConnectionInfo(event.Name(), "Idle");
     826           0 :             return transit<Idle>();
     827             :         }
     828             :     }
     829             : 
     830        2924 :     sc::result react(const EvTlsHandShakeSuccess &event) {
     831        2924 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     832        2924 :         if (event.session != state_machine->session()) {
     833           0 :             return discard_event();
     834             :         }
     835        2924 :         XmppConnection *connection = state_machine->connection();
     836        2924 :         XmppSession *session = state_machine->session();
     837        2924 :         session->AsyncReadStart();
     838        2924 :         state_machine->set_openconfirm_state(OPENCONFIRM_FEATURE_SUCCESS);
     839        2924 :         if (state_machine->IsActiveChannel()) { //client
     840        1462 :             if (!connection->SendOpen(session)) {
     841           0 :                 connection->SendClose(session);
     842           0 :                 state_machine->ResetSession();
     843           0 :                 XmppConnectionInfo info;
     844           0 :                 info.set_close_reason("Open send failed in OpenConfirm State");
     845           0 :                 state_machine->SendConnectionInfo(event.Name(), "Active");
     846           0 :                 return transit<Active>();
     847           0 :             }
     848             :         }
     849             :         // both server and client
     850        2924 :         state_machine->StartHoldTimer();
     851        2924 :         state_machine->SendConnectionInfo(event.Name(),
     852             :             "OpenConfirm Feature Negotiation Success");
     853        2924 :         return discard_event();
     854             :     }
     855             : 
     856           2 :     sc::result react(const EvTlsHandShakeFailure &event) {
     857           2 :        XmppStateMachine *state_machine = &context<XmppStateMachine>();
     858           2 :        if (event.session != state_machine->session()) {
     859           0 :            return discard_event();
     860             :        }
     861             :        // Do not send stream close as error occured at TLS layer
     862           2 :        state_machine->ResetSession();
     863           2 :        if (state_machine->IsActiveChannel()) { // client
     864           1 :            state_machine->SendConnectionInfo(event.Name(), "Active");
     865           1 :            return transit<Active>();
     866             :        } else {
     867           1 :            state_machine->SendConnectionInfo(event.Name(), "Idle");
     868           1 :            return transit<Idle>();
     869             :        }
     870             :     }
     871             : 
     872             :     //event on server and client
     873        2919 :     sc::result react(const EvXmppOpen &event) {
     874        2919 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     875        2919 :         if (event.session != state_machine->session()) {
     876           0 :             return discard_event();
     877             :         }
     878        2919 :         XmppConnectionInfo info;
     879        2919 :         info.set_identifier(event.msg->from);
     880        2919 :         XmppConnection *connection = state_machine->connection();
     881        2919 :         XmppSession *session = state_machine->session();
     882        2919 :         if (connection->IsActiveChannel()) { //client
     883        1460 :             connection->SendKeepAlive();
     884        1460 :             connection->StartKeepAliveTimer();
     885        1460 :             state_machine->StartHoldTimer();
     886        1460 :             state_machine->SendConnectionInfo(&info, event.Name(),
     887             :                                               "Established");
     888        1460 :             return transit<XmppStreamEstablished>();
     889             :         } else { //server
     890        1459 :             if (!connection->SendOpenConfirm(session)) {
     891           0 :                 connection->SendClose(session);
     892           0 :                 SM_LOG(state_machine,
     893             :                     "Xmpp Send Open Confirm Failed, IDLE");
     894           0 :                 state_machine->ResetSession();
     895           0 :                 info.set_close_reason("Send Open Confirm Failed");
     896           0 :                 state_machine->connection()->set_close_reason(
     897             :                     "Send Open Confirm Failed");
     898           0 :                 state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
     899           0 :                 return transit<Idle>();
     900             :             } else {
     901        1459 :                 if (state_machine->IsAuthEnabled())
     902        1459 :                     state_machine->ResurrectOldConnection(connection, session);
     903        1458 :                 connection = state_machine->connection();
     904        1458 :                 connection->StartKeepAliveTimer();
     905        1459 :                 state_machine->StartHoldTimer();
     906        1459 :                 state_machine->SendConnectionInfo(&info, event.Name(),
     907             :                                                   "Established");
     908        1459 :                 return transit<XmppStreamEstablished>();
     909             :             }
     910             :         }
     911        2919 :     }
     912             : 
     913           0 :     sc::result react(const EvAdminDown &event) {
     914           0 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     915           0 :         CloseSession(state_machine);
     916           0 :         XmppConnectionInfo info;
     917           0 :         info.set_close_reason("Administratively down");
     918           0 :         state_machine->connection()->set_close_reason("Administratively down");
     919           0 :         state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
     920           0 :         return transit<Idle>();
     921           0 :     }
     922             : 
     923           6 :     void CloseSession(XmppStateMachine *state_machine) {
     924           6 :         XmppConnection *connection = state_machine->connection();
     925           6 :         if (connection != NULL) connection->StopKeepAliveTimer();
     926           6 :         state_machine->set_session(NULL);
     927           6 :     }
     928             : };
     929             : 
     930             : struct XmppStreamEstablished :
     931             :         public sc::state<XmppStreamEstablished, XmppStateMachine> {
     932             :     typedef mpl::list<
     933             :         sc::custom_reaction<EvAdminDown>,
     934             :         sc::custom_reaction<EvTcpClose>,
     935             :         sc::custom_reaction<EvXmppKeepalive>,
     936             :         sc::custom_reaction<EvXmppMessageStanza>,
     937             :         sc::custom_reaction<EvXmppIqStanza>,
     938             :         sc::custom_reaction<EvHoldTimerExpired>,
     939             :         sc::custom_reaction<EvStop>
     940             :     > reactions;
     941             : 
     942       12605 :     XmppStreamEstablished(my_context ctx) : my_base(ctx) {
     943       12605 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     944       12605 :         XmppConnection *connection = state_machine->connection();
     945       12605 :         state_machine->StartHoldTimer();
     946       12605 :         state_machine->set_state(ESTABLISHED);
     947       12604 :         connection->ChannelMux()->HandleStateEvent(xmsm::ESTABLISHED);
     948       12605 :     }
     949       25198 :     ~XmppStreamEstablished() {
     950       12599 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     951       12595 :         state_machine->CancelHoldTimer();
     952       25200 :     }
     953             : 
     954        6105 :     sc::result react(const EvTcpClose &event) {
     955        6105 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     956        6106 :         if (event.session != state_machine->session()) {
     957          36 :             return discard_event();
     958             :         }
     959        6070 :         state_machine->ResetSession();
     960        6070 :         if (state_machine->IsActiveChannel()) {
     961        2574 :             state_machine->SendConnectionInfo(event.Name(), "Active");
     962        2574 :             return transit<Active>();
     963             :         } else {
     964        3496 :             state_machine->SendConnectionInfo(event.Name(), "Idle");
     965        3496 :             return transit<Idle>();
     966             :         }
     967             :     }
     968             : 
     969     2472467 :     sc::result react(const EvXmppKeepalive &event) {
     970     2472467 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     971     2472466 :         if (event.session != state_machine->session()) {
     972           0 :             return discard_event();
     973             :         }
     974     2472466 :         state_machine->keepalive_count_inc();
     975     2472466 :         if (state_machine->get_keepalive_count() == 3) {
     976        3012 :             state_machine->connect_attempts_clear();
     977             :         }
     978     2472466 :         state_machine->StartHoldTimer();
     979     2472467 :         return discard_event();
     980             :     }
     981             : 
     982     1556755 :     sc::result react(const EvXmppMessageStanza &event) {
     983     1556755 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     984     1556755 :         if (event.session != state_machine->session()) {
     985           0 :             return discard_event();
     986             :         }
     987     1556755 :         state_machine->StartHoldTimer();
     988     3113510 :         state_machine->connection()->ProcessXmppChatMessage(
     989     1556755 :             static_cast<const XmppStanza::XmppChatMessage *>(event.msg.get()));
     990     1556755 :         return discard_event();
     991             :     }
     992             : 
     993       56895 :     sc::result react(const EvXmppIqStanza &event) {
     994       56895 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
     995       56895 :         if (event.session != state_machine->session()) {
     996           0 :             return discard_event();
     997             :         }
     998       56894 :         state_machine->StartHoldTimer();
     999      113790 :         state_machine->connection()->ProcessXmppIqMessage(
    1000       56895 :             static_cast<const XmppStanza::XmppMessage *>(event.msg.get()));
    1001       56894 :         return discard_event();
    1002             :     }
    1003             : 
    1004           5 :     sc::result react(const EvHoldTimerExpired &event) {
    1005           5 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
    1006           5 :         if (state_machine->HoldTimerCancelled()) {
    1007           0 :             SM_LOG(state_machine,
    1008             :                    "Discard EvHoldTimerExpired in (Established) State");
    1009           0 :             return discard_event();
    1010             :         }
    1011           5 :         XMPP_NOTICE(XmppStateMachineNotice,
    1012             :                     state_machine->connection()->ToUVEKey(),
    1013             :                     XMPP_PEER_DIR_NA,
    1014             :                     state_machine->ChannelType(),
    1015             :                 "EvHoldTimerExpired in (Established) State. Transit to IDLE");
    1016           5 :         state_machine->SendConnectionInfo(event.Name());
    1017           5 :         state_machine->AssertOnHoldTimeout();
    1018           5 :         state_machine->ResetSession();
    1019           5 :         if (state_machine->IsActiveChannel()) {
    1020           4 :             return transit<Active>();
    1021             :         } else {
    1022           1 :             return transit<Idle>();
    1023             :         }
    1024             :     }
    1025             : 
    1026        2796 :     sc::result react(const EvStop &event) {
    1027        2796 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
    1028        2801 :         state_machine->SendConnectionInfo(event.Name());
    1029        2799 :         state_machine->ResetSession();
    1030        2803 :         if (state_machine->IsActiveChannel()) {
    1031           0 :             return transit<Active>();
    1032             :         } else {
    1033        2803 :             return transit<Idle>();
    1034             :         }
    1035             :     }
    1036             : 
    1037        3183 :     sc::result react(const EvAdminDown &event) {
    1038        3183 :         XmppStateMachine *state_machine = &context<XmppStateMachine>();
    1039        3183 :         state_machine->ResetSession();
    1040        3183 :         XmppConnectionInfo info;
    1041        3183 :         info.set_close_reason("Administratively down");
    1042        3183 :         state_machine->connection()->set_close_reason("Administratively down");
    1043        3183 :         state_machine->SendConnectionInfo(&info, event.Name(), "Idle");
    1044        6366 :         return transit<Idle>();
    1045        3183 :     }
    1046             : };
    1047             : 
    1048             : } // namespace xmsm
    1049             : 
    1050           5 : void XmppStateMachine::AssertOnHoldTimeout() {
    1051             :     static bool init_ = false;
    1052             :     static bool assert_ = false;
    1053             : 
    1054           5 :     if (!init_) {
    1055           3 :         char *str = getenv("XMPP_ASSERT_ON_HOLD_TIMEOUT");
    1056           3 :         if (str && strtoul(str, NULL, 0) != 0) assert_ = true;
    1057           3 :         init_ = true;
    1058             :     }
    1059             : 
    1060           5 :     if (!assert_) return;
    1061             : 
    1062           0 :     if (connection()) {
    1063           0 :         connection()->LogMsg("HOLD TIMER EXPIRED: ");
    1064             :     } else {
    1065           0 :         LOG4CPLUS_DEBUG(log4cplus::Logger::getRoot(), "HOLD TIMER EXPIRED: ");
    1066             :     }
    1067             : 
    1068           0 :     assert(!assert_);
    1069             : }
    1070             : 
    1071       12351 : void XmppStateMachine::ResetSession() {
    1072       12351 :     XmppConnection *connection = this->connection();
    1073       12350 :     set_session(NULL);
    1074       12358 :     CancelHoldTimer();
    1075             : 
    1076       12357 :     if (!connection)
    1077           0 :         return;
    1078             : 
    1079             :     // Stop keepalives, transition to IDLE and notify registered entities.
    1080       12357 :     connection->StopKeepAliveTimer();
    1081       12358 :     connection->ChannelMux()->HandleStateEvent(xmsm::IDLE);
    1082       12358 :     if (IsActiveChannel())
    1083        5762 :         return;
    1084             : 
    1085             :     // Retain the connection if graceful restart is supported.
    1086        6596 :     XmppServer *server = dynamic_cast<XmppServer *>(connection->server());
    1087        6596 :     if (!server->IsPeerCloseGraceful())
    1088        6383 :         connection->ManagedDelete();
    1089             : }
    1090             : 
    1091       11606 : XmppStateMachine::XmppStateMachine(XmppConnection *connection, bool active,
    1092       11606 :     bool auth_enabled, int config_hold_time)
    1093       11606 :     : work_queue_(TaskScheduler::GetInstance()->GetTaskId("xmpp::StateMachine"),
    1094             :           connection->GetTaskInstance(),
    1095             :           boost::bind(&XmppStateMachine::DequeueEvent, this, _1)),
    1096       11606 :       connection_(connection),
    1097       11606 :       session_(NULL),
    1098       11606 :       server_(connection->server()),
    1099       11606 :       connect_timer_(
    1100       11606 :           TimerManager::CreateTimer(*server_->event_manager()->io_service(),
    1101             :           "Connect timer",
    1102             :           TaskScheduler::GetInstance()->GetTaskId("xmpp::StateMachine"),
    1103             :           connection->GetTaskInstance())),
    1104       11606 :       open_timer_(
    1105       11606 :           TimerManager::CreateTimer(*server_->event_manager()->io_service(),
    1106             :           "Open timer",
    1107             :           TaskScheduler::GetInstance()->GetTaskId("xmpp::StateMachine"),
    1108             :           connection->GetTaskInstance())),
    1109       11606 :       hold_timer_(
    1110       11606 :           TimerManager::CreateTimer(*server_->event_manager()->io_service(),
    1111             :           "Hold timer",
    1112             :           TaskScheduler::GetInstance()->GetTaskId("xmpp::StateMachine"),
    1113             :           connection->GetTaskInstance())),
    1114       11606 :       config_hold_time_(config_hold_time),
    1115       11606 :       hold_time_(GetConfiguredHoldTime()),
    1116       11606 :       attempts_(0),
    1117       11606 :       keepalive_count_(0),
    1118       11606 :       deleted_(false),
    1119       11606 :       in_dequeue_(false),
    1120       11606 :       is_active_(active),
    1121       11606 :       auth_enabled_(auth_enabled),
    1122       11606 :       state_(xmsm::IDLE),
    1123       11606 :       last_state_(xmsm::IDLE),
    1124       23212 :       openconfirm_state_(xmsm::OPENCONFIRM_INIT) {
    1125             :       handshake_cb_ = boost::bind(
    1126       11606 :           &XmppConnection::ProcessSslHandShakeResponse, connection, _1, _2);
    1127       11606 : }
    1128             : 
    1129       11701 : XmppStateMachine::~XmppStateMachine() {
    1130       11606 :     assert(!deleted_);
    1131       11606 :     deleted_ = true;
    1132       11606 :     work_queue_.Shutdown();
    1133       11606 :     set_session(NULL);
    1134             : 
    1135             :     // Explicitly call the state destructor before the state machine itself.
    1136             :     // This is needed because some of the destructors access the state machine
    1137             :     // context.
    1138       11606 :     terminate();
    1139             : 
    1140             :     // Delete timer after state machine is terminated so that there is no
    1141             :     // possible reference to the timers being deleted any more
    1142       11606 :     TimerManager::DeleteTimer(connect_timer_);
    1143       11606 :     TimerManager::DeleteTimer(open_timer_);
    1144       11606 :     TimerManager::DeleteTimer(hold_timer_);
    1145       11701 : }
    1146             : 
    1147       11836 : void XmppStateMachine::Initialize() {
    1148       11836 :     initiate();
    1149       11836 :     Enqueue(xmsm::EvStart());
    1150       11836 : }
    1151             : 
    1152             : // Note this api does not enqueue the deletion of TCP session
    1153       11606 : void XmppStateMachine::clear_session() {
    1154       11606 :     if (session_ != NULL) {
    1155        2006 :         session_->set_observer(NULL);
    1156        2006 :         session_->ClearConnection();
    1157        2006 :         session_->Close();
    1158        2006 :         connection_->clear_session();
    1159        2006 :         session_ = NULL;
    1160             :     }
    1161       11606 : }
    1162             : 
    1163       12306 : void XmppStateMachine::DeleteSession(XmppSession *session) {
    1164             :     // If there is a session assigned to this state machine, reset the observer
    1165             :     // so that tcp does not have a reference to 'this' which is going away
    1166       12306 :     if (session != NULL) {
    1167       12306 :         session->set_observer(NULL);
    1168       12301 :         session->ClearConnection();
    1169       12301 :         session->Close();
    1170       12306 :         Enqueue(xmsm::EvTcpDeleteSession(session));
    1171             :     }
    1172       12306 : }
    1173             : 
    1174       39689 : void XmppStateMachine::set_session(TcpSession *session) {
    1175       39689 :     if (session_ != NULL) {
    1176       12299 :         connection_->clear_session();
    1177       12306 :         DeleteSession(session_);
    1178             :     }
    1179       39696 :     session_ = static_cast<XmppSession *>(session);
    1180       39696 : }
    1181             : 
    1182           0 : void XmppStateMachine::TimerErrorHandler(std::string name, std::string error) {
    1183           0 : }
    1184             : 
    1185         165 : void XmppStateMachine::StartConnectTimer(int seconds) {
    1186         165 :     CancelConnectTimer();
    1187             : 
    1188             :     // Add up to +/- kJitter percentage to reduce connection collisions.
    1189         165 :     int ms = ((seconds)? seconds * 1000 : 50);
    1190         165 :     ms = (ms * (100 - kJitter)) / 100;
    1191         165 :     ms += (ms * (rand() % (kJitter * 2))) / 100;
    1192         165 :     connect_timer_->Start(ms,
    1193             :         boost::bind(&XmppStateMachine::ConnectTimerExpired, this),
    1194             :         boost::bind(&XmppStateMachine::TimerErrorHandler, this, _1, _2));
    1195         165 : }
    1196             : 
    1197       31700 : void XmppStateMachine::CancelConnectTimer() {
    1198       31700 :     connect_timer_->Cancel();
    1199       31699 : }
    1200             : 
    1201          14 : void XmppStateMachine::StartOpenTimer(int seconds) {
    1202          14 :     CancelOpenTimer();
    1203          14 :     open_timer_->Start(seconds * 1000,
    1204             :         boost::bind(&XmppStateMachine::OpenTimerExpired, this),
    1205             :         boost::bind(&XmppStateMachine::TimerErrorHandler, this, _1, _2));
    1206          14 : }
    1207             : 
    1208       22599 : void XmppStateMachine::CancelOpenTimer() {
    1209       22599 :     open_timer_->Cancel();
    1210       22599 : }
    1211             : 
    1212       12093 : int XmppStateMachine::GetConfiguredHoldTime() const {
    1213             :     static std::atomic<bool> env_checked = std::atomic<bool>();
    1214             :     static std::atomic<int> env_hold_time = std::atomic<int>();
    1215             : 
    1216             :     // For testing only - configure through environment variable.
    1217       12093 :     if (!env_checked) {
    1218          65 :         char *keepalive_time_str = getenv("XMPP_KEEPALIVE_SECONDS");
    1219          65 :         if (keepalive_time_str) {
    1220           0 :             env_hold_time = strtoul(keepalive_time_str, NULL, 0) * 3;
    1221           0 :             env_checked = true;
    1222           0 :             return env_hold_time;
    1223             :         } else {
    1224          65 :             env_checked = true;
    1225             :         }
    1226       12028 :     } else if (env_hold_time) {
    1227           0 :         return env_hold_time;
    1228       12028 :     } else if (config_hold_time_) {
    1229       12028 :         return config_hold_time_;
    1230             :     }
    1231             : 
    1232             :     // Use hard coded default.
    1233          65 :     return kHoldTime;
    1234             : }
    1235             : 
    1236     4123368 : void XmppStateMachine::StartHoldTimer() {
    1237     4123368 :     CancelHoldTimer();
    1238             : 
    1239     4123364 :     if (hold_time_ <= 0)
    1240           0 :         return;
    1241             : 
    1242     4123364 :     hold_timer_->Start(hold_time_ * 1000,
    1243             :         boost::bind(&XmppStateMachine::HoldTimerExpired, this),
    1244             :         boost::bind(&XmppStateMachine::TimerErrorHandler, this, _1, _2));
    1245             : }
    1246             : 
    1247     4155128 : void XmppStateMachine::CancelHoldTimer() {
    1248     4155128 :     hold_timer_->Cancel();
    1249     4155127 : }
    1250             : 
    1251       12051 : void XmppStateMachine::OnStart(const xmsm::EvStart &event) {
    1252       12051 : }
    1253             : 
    1254        7747 : bool XmppStateMachine::ConnectTimerExpired() {
    1255        7747 :     XMPP_UTDEBUG(XmppStateMachineTimerExpire,
    1256             :                  connection() ? connection()->ToUVEKey() : "",
    1257             :                  XMPP_PEER_DIR_NA, this->ChannelType(), "Connect", StateName());
    1258        7747 :     Enqueue(xmsm::EvConnectTimerExpired());
    1259        7747 :     return false;
    1260             : }
    1261             : 
    1262           1 : bool XmppStateMachine::OpenTimerExpired() {
    1263           1 :     XMPP_NOTICE(XmppEventLog, connection()->ToUVEKey(), XMPP_PEER_DIR_NA,
    1264             :                 this->ChannelType(),
    1265             :                  "Event: OpenTimer Expired ",
    1266             :                  connection()->endpoint().address().to_string(),
    1267             :                  connection()->GetTo());
    1268           1 :     Enqueue(xmsm::EvOpenTimerExpired());
    1269           1 :     return false;
    1270             : }
    1271             : 
    1272             : 
    1273          13 : bool XmppStateMachine::HoldTimerExpired() {
    1274          13 :     boost::system::error_code error;
    1275             : 
    1276             :     // Reset hold timer if there is data already present in the socket.
    1277          26 :     if (session() && session()->socket() &&
    1278          13 :             session()->socket()->available(error) > 0) {
    1279           0 :         return true;
    1280             :     }
    1281          13 :     XMPP_NOTICE(XmppEventLog, connection()->ToUVEKey(), XMPP_PEER_DIR_NA,
    1282             :                 this->ChannelType(), "Event: HoldTimer Expired ",
    1283             :                 connection()->endpoint().address().to_string(),
    1284             :                 connection()->GetTo());
    1285          13 :     Enqueue(xmsm::EvHoldTimerExpired());
    1286          13 :     return false;
    1287             : }
    1288             : 
    1289       20792 : void XmppStateMachine::OnSessionEvent(
    1290             :         TcpSession *session, TcpSession::Event event) {
    1291       20792 :     switch (event) {
    1292        6544 :     case TcpSession::ACCEPT:
    1293        6544 :         break;
    1294        6583 :     case TcpSession::CONNECT_COMPLETE:
    1295        6583 :         XMPP_NOTICE(XmppEventLog, session->ToUVEKey(), XMPP_PEER_DIR_IN,
    1296             :                     this->ChannelType(), "Event: Tcp Connected ",
    1297             :                     connection()->endpoint().address().to_string(),
    1298             :                     connection()->GetTo());
    1299        6583 :         Enqueue(xmsm::EvTcpConnected(static_cast<XmppSession *>(session)));
    1300        6583 :         break;
    1301        1159 :     case TcpSession::CONNECT_FAILED:
    1302        1159 :         XMPP_NOTICE(XmppEventLog, session->ToUVEKey(), XMPP_PEER_DIR_IN,
    1303             :                     this->ChannelType(), "Event: Tcp Connect Fail ",
    1304             :                     connection()->endpoint().address().to_string(),
    1305             :                     connection()->GetTo());
    1306        1159 :         Enqueue(xmsm::EvTcpConnectFail(static_cast<XmppSession *>(session)));
    1307        1159 :         connection_->inc_connect_error();
    1308        1159 :         break;
    1309        6506 :     case TcpSession::CLOSE:
    1310        6506 :         XMPP_NOTICE(XmppEventLog, session->ToUVEKey(), XMPP_PEER_DIR_IN,
    1311             :                     this->ChannelType(), "Event: Tcp Connection Closed ",
    1312             :                     connection()->endpoint().address().to_string(),
    1313             :                     connection()->GetTo());
    1314        6506 :         Enqueue(xmsm::EvTcpClose(static_cast<XmppSession *>(session)));
    1315        6506 :         connection_->inc_session_close();
    1316        6506 :         break;
    1317           0 :     default:
    1318           0 :         XMPP_WARNING(XmppUnknownEvent, session->ToUVEKey(), XMPP_PEER_DIR_IN,
    1319             :                      this->ChannelType(), event);
    1320           0 :         break;
    1321             :     }
    1322       20792 : }
    1323             : 
    1324        6521 : bool XmppStateMachine::PassiveOpen(XmppSession *session) {
    1325        6521 :     string state = "PassiveOpen in state: " + StateName();
    1326        6521 :     XMPP_NOTICE(XmppEventLog, session->ToUVEKey(), XMPP_PEER_DIR_IN,
    1327             :                 this->ChannelType(), state,
    1328             :                 session->Connection()->endpoint().address().to_string(), "");
    1329       13042 :     return Enqueue(xmsm::EvTcpPassiveOpen(session));
    1330        6521 : }
    1331             : 
    1332             : // Process XmppStream header message received over a session. Close the stream
    1333             : // if an old session is still present and undergoing graceful restart.
    1334             : //
    1335             : // Return true if msg is enqueued for further processing, false otherwise.
    1336       15770 : void XmppStateMachine::ProcessStreamHeaderMessage(XmppSession *session,
    1337             :         const XmppStanza::XmppMessage *msg) {
    1338             :     XmppConnectionManager *connection_manager =
    1339       15770 :         dynamic_cast<XmppConnectionManager *>(connection_->server());
    1340       15770 :     std::scoped_lock lock(connection_manager->mutex());
    1341             : 
    1342             :     // Update "To" information which can be used to map an older session
    1343       15771 :     session->Connection()->SetTo(msg->from);
    1344             : 
    1345       15771 :     XmppServer *xmpp_server = dynamic_cast<XmppServer *>(server_);
    1346       15771 :     XmppConnectionEndpoint *endpoint = NULL;
    1347             : 
    1348             :     // Look for an endpoint which may already exist
    1349       15771 :     if (xmpp_server) {
    1350             :         endpoint =
    1351        7999 :             xmpp_server->FindConnectionEndpoint(connection_->ToString());
    1352             : 
    1353       16006 :         if (!xmpp_server->subcluster_name().empty() &&
    1354        8007 :                xmpp_server->subcluster_name() != msg->xmlns) {
    1355           2 :             string reason = "Subcluster mismatch: Agent subcluster " +
    1356           4 :                 msg->xmlns + ", Control subcluster " +
    1357           4 :                 xmpp_server->subcluster_name();
    1358           2 :             XMPP_WARNING(XmppDeleteConnection, session->ToUVEKey(),
    1359             :                 XMPP_PEER_DIR_IN,
    1360             :                 "Drop new xmpp connection " + session->ToString() +
    1361             :                 " as " + reason);
    1362           2 :             session->Connection()->set_close_reason(reason);
    1363           2 :             ProcessEvent(xmsm::EvTcpClose(session));
    1364           2 :             delete msg;
    1365           2 :             return;
    1366           2 :         }
    1367             :     }
    1368             : 
    1369             :     // If there is no connection already associated with the end-point,
    1370             :     // process the incoming open message and move forward with the session
    1371             :     // establishment.
    1372       16105 :     if (!endpoint || !endpoint->connection() ||
    1373         336 :             connection_ == endpoint->connection()) {
    1374       15434 :         ProcessEvent(xmsm::EvXmppOpen(session, msg));
    1375       15434 :         return;
    1376             :     }
    1377             : 
    1378             :     // Check if the IP addresses match.
    1379             :     boost::asio::ip::address addr =
    1380         335 :         endpoint->connection()->endpoint().address();
    1381         335 :     if (connection_->endpoint().address() != addr) {
    1382          36 :         XMPP_WARNING(XmppDeleteConnection, session->ToUVEKey(),
    1383             :             XMPP_PEER_DIR_IN,
    1384             :             "Drop new xmpp connection " + session->ToString() +
    1385             :             " as another connection with same name " + msg->from +
    1386             :             " but with different IP address " + addr.to_string() +
    1387             :             " already exists");
    1388          36 :         ProcessEvent(xmsm::EvTcpClose(session));
    1389          36 :         delete msg;
    1390          36 :         return;
    1391             :     }
    1392             : 
    1393         299 :     XmppChannelMux *channel = endpoint->connection()->ChannelMux();
    1394             : 
    1395             :     // If GR Helper mode is not enabled, ignore the new connection request.
    1396             :     // Existing connection, if down will get cleaned up eventually. If it is
    1397             :     // up, it shall remain intact.
    1398         299 :     if (!xmpp_server->IsPeerCloseGraceful()) {
    1399         125 :         XMPP_NOTICE(XmppDeleteConnection, session->ToUVEKey(),
    1400             :                     XMPP_PEER_DIR_IN,
    1401             :                     "Drop new xmpp connection " + session->ToString() +
    1402             :                     " as another connection is alreready present");
    1403         125 :         ProcessEvent(xmsm::EvTcpClose(session));
    1404         125 :         delete msg;
    1405         125 :         return;
    1406             :     }
    1407             : 
    1408         174 :     XmppStateMachine *old_sm = endpoint->connection()->state_machine();
    1409             : 
    1410             :     // Bring down old session if connection is already up and ready. This is
    1411             :     // the scenario in which old session's TCP did not learn the session down
    1412             :     // event, possibly due to compute cold reboot. In that case, trigger
    1413             :     // closure (and possibly GR) process for the old session.
    1414         174 :     if (channel->GetPeerState() == xmps::READY) {
    1415           0 :         XMPP_NOTICE(XmppDeleteConnection, old_sm->session()->ToUVEKey(),
    1416             :             XMPP_PEER_DIR_IN, "Delete old xmpp connection " +
    1417             :             old_sm->session()->ToString() +
    1418             :             " as a new connection as been initiated (GR Helper is active)");
    1419           0 :         old_sm->Enqueue(xmsm::EvTcpClose(old_sm->session()));
    1420             : 
    1421             :         // Drop the new session until old one is deleted or marked stale.
    1422           0 :         ProcessEvent(xmsm::EvTcpClose(session));
    1423           0 :         delete msg;
    1424           0 :         return;
    1425             :     }
    1426             : 
    1427             :     // If previous closure is still in progress, drop the new connection.
    1428         174 :     if (channel->ReceiverCount()) {
    1429          18 :         XMPP_NOTICE(XmppDeleteConnection, session->ToUVEKey(),
    1430             :                     XMPP_PEER_DIR_IN,
    1431             :                     "Drop new xmpp connection " + session->ToString() +
    1432             :                     " as current connection is still under deletion");
    1433          18 :         ProcessEvent(xmsm::EvTcpClose(session));
    1434          18 :         delete msg;
    1435          18 :         return;
    1436             :     }
    1437             : 
    1438             :     // Now we reach for the classic GR case, in which existing session stale
    1439             :     // process is complete, (peer is in GR/LLGR timer wait state).
    1440             :     // In this case, we should process the open message, and later switch to
    1441             :     // the new connection and state machine, retaining the old XmppPeer,
    1442             :     // BgpXmppChannel, routes, etc.
    1443         156 :     ProcessEvent(xmsm::EvXmppOpen(session, msg));
    1444       15771 : }
    1445             : 
    1446     4110876 : void XmppStateMachine::OnMessage(XmppSession *session,
    1447             :                                  const XmppStanza::XmppMessage *msg) {
    1448     4110876 :     if (!Enqueue(xmsm::EvXmppMessage(session, msg)))
    1449           0 :         delete msg;
    1450     4110882 : }
    1451             : 
    1452     4110882 : void XmppStateMachine::ProcessMessage(XmppSession *session,
    1453             :                                       const XmppStanza::XmppMessage *msg) {
    1454             :     // Bail if session is already reset and disassociated from the connection.
    1455     4110882 :     if (!session->Connection()) {
    1456         229 :         delete msg;
    1457         232 :         return;
    1458             :     }
    1459             : 
    1460     4110655 :     const XmppStanza::XmppStreamMessage *stream_msg =
    1461             :         static_cast<const XmppStanza::XmppStreamMessage *>(msg);
    1462             : 
    1463     4110655 :     switch (msg->type) {
    1464       20170 :         case XmppStanza::STREAM_HEADER:
    1465       20170 :             if (stream_msg->strmtype ==
    1466             :                 XmppStanza::XmppStreamMessage::FEATURE_TLS) {
    1467             : 
    1468        4400 :                 switch (stream_msg->strmtlstype) {
    1469        1469 :                     case (XmppStanza::XmppStreamMessage::TLS_FEATURE_REQUEST):
    1470        1469 :                         ProcessEvent(xmsm::EvStreamFeatureRequest(session,
    1471             :                                                                   msg));
    1472        1469 :                         break;
    1473        1464 :                     case (XmppStanza::XmppStreamMessage::TLS_START):
    1474        1464 :                         ProcessEvent(xmsm::EvStartTls(session, msg));
    1475        1464 :                         break;
    1476        1467 :                     case (XmppStanza::XmppStreamMessage::TLS_PROCEED):
    1477        1467 :                         ProcessEvent(xmsm::EvTlsProceed(session, msg));
    1478        1467 :                         break;
    1479           0 :                     default:
    1480           0 :                         break;
    1481             :                 }
    1482             : 
    1483       15770 :             } else if (stream_msg->strmtype ==
    1484           0 :                     XmppStanza::XmppStreamMessage::INIT_STREAM_HEADER ||
    1485           0 :                 stream_msg->strmtype ==
    1486             :                     XmppStanza::XmppStreamMessage::INIT_STREAM_HEADER_RESP)
    1487       15770 :                 ProcessStreamHeaderMessage(session, msg);
    1488       20171 :             break;
    1489     2476835 :         case XmppStanza::WHITESPACE_MESSAGE_STANZA:
    1490     2476835 :             ProcessEvent(xmsm::EvXmppKeepalive(session, msg));
    1491     2476835 :             break;
    1492       56895 :         case XmppStanza::IQ_STANZA:
    1493       56895 :             ProcessEvent(xmsm::EvXmppIqStanza(session, msg));
    1494       56894 :             break;
    1495     1556755 :         case XmppStanza::MESSAGE_STANZA:
    1496     1556755 :             ProcessEvent(xmsm::EvXmppMessageStanza(session, msg));
    1497     1556755 :             break;
    1498           0 :         default:
    1499           0 :             if (!msg->IsValidType(msg->type)) {
    1500           0 :                 XMPP_NOTICE(XmppStateMachineUnsupportedMessage,
    1501             :                             XMPP_PEER_DIR_IN, session->ToUVEKey(),
    1502             :                             ChannelType(), (int)msg->type);
    1503             :             }
    1504           0 :             delete msg;
    1505           0 :             break;
    1506             :     }
    1507             : 
    1508             : }
    1509             : 
    1510        9131 : void XmppStateMachine::Clear() {
    1511        9131 :     Enqueue(xmsm::EvStop());
    1512        9131 : }
    1513             : 
    1514        3767 : void XmppStateMachine::SetAdminState(bool down) {
    1515        3767 :     assert(IsActiveChannel());
    1516        3767 :     if (down) {
    1517        3508 :         Enqueue(xmsm::EvAdminDown());
    1518             :     } else {
    1519         259 :         Enqueue(xmsm::EvStart());
    1520             :     }
    1521        3767 : }
    1522             : 
    1523        2926 : void  XmppStateMachine::OnEvent(SslSession *session,
    1524             :                                 xmsm::SslHandShakeResponse resp) {
    1525        2926 :     XmppSession *sess = static_cast<XmppSession *>(session);
    1526        2926 :     switch(resp) {
    1527           2 :        case xmsm::EvTLSHANDSHAKE_FAILURE:
    1528           2 :            Enqueue(xmsm::EvTlsHandShakeFailure(sess));
    1529           2 :            break;
    1530        2924 :        case xmsm::EvTLSHANDSHAKE_SUCCESS:
    1531        2924 :            Enqueue(xmsm::EvTlsHandShakeSuccess(sess));
    1532        2924 :            break;
    1533           0 :        default:
    1534           0 :            break;
    1535             :     }
    1536        2926 : }
    1537             : 
    1538       67673 : void XmppStateMachine::set_state(xmsm::XmState state) {
    1539       67673 :     last_state_ = state_;
    1540       67673 :     state_ = state;
    1541       67673 :     state_since_ = UTCTimestampUsec();
    1542             : 
    1543       67680 :     if (!logUVE()) return;
    1544             : 
    1545           0 :     XmppPeerInfoData peer_info;
    1546           0 :     peer_info.set_name(connection()->ToUVEKey());
    1547           0 :     PeerStateInfo state_info;
    1548           0 :     state_info.set_state(StateName());
    1549           0 :     state_info.set_last_state(LastStateName());
    1550           0 :     state_info.set_last_state_at(state_since_);
    1551           0 :     peer_info.set_state_info(state_info);
    1552           0 :     assert(!peer_info.get_name().empty());
    1553           0 :     XMPPPeerInfo::Send(peer_info);
    1554           0 : }
    1555             : 
    1556             : 
    1557        7377 : void XmppStateMachine::set_openconfirm_state(xmsm::XmOpenConfirmState state) {
    1558        7377 :     openconfirm_state_ = state;
    1559        7377 : }
    1560             : 
    1561             : static const char *state_names[] = {
    1562             :     "Idle",
    1563             :     "Active",
    1564             :     "Connect",
    1565             :     "OpenSent",
    1566             :     "OpenConfirm",
    1567             :     "Established" };
    1568             : 
    1569     4076034 : string XmppStateMachine::StateName() const {
    1570     4076034 :     return state_names[state_];
    1571             : }
    1572             : 
    1573         565 : string XmppStateMachine::LastStateName() const {
    1574         565 :     return state_names[last_state_];
    1575             : }
    1576             : 
    1577         559 : string XmppStateMachine::LastStateChangeAt() const {
    1578        1118 :     return integerToString(UTCUsecToPTime(state_since_));
    1579             : }
    1580             : 
    1581     7877966 : xmsm::XmState XmppStateMachine::StateType() const {
    1582     7877966 :     return state_;
    1583             : }
    1584             : 
    1585     4346598 : xmsm::XmOpenConfirmState XmppStateMachine::OpenConfirmStateType() const {
    1586     4346598 :     return openconfirm_state_;
    1587             : }
    1588             : 
    1589       12670 : void XmppStateMachine::AssignSession() {
    1590       12670 :     connection_->set_session(static_cast<XmppSession *>(session_));
    1591       12670 : }
    1592             : 
    1593             : const int XmppStateMachine::kConnectInterval;
    1594             : 
    1595       16927 : int XmppStateMachine::GetConnectTime() const {
    1596       16927 :     int backoff = attempts_ > 6 ? 6 : attempts_;
    1597       16927 :     return std::min(backoff ? 1 << (backoff - 1) : 0, kConnectInterval);
    1598             : }
    1599             : 
    1600     4072288 : bool XmppStateMachine::IsActiveChannel() {
    1601     4072288 :     return is_active_;
    1602             : }
    1603             : 
    1604     4234081 : bool XmppStateMachine::logUVE() {
    1605     4234081 :     return connection()->logUVE();
    1606             : }
    1607             : 
    1608     3999495 : const char *XmppStateMachine::ChannelType() {
    1609     3999495 :     return (IsActiveChannel() ? " Mode Client: " : " Mode Server: " );
    1610             : }
    1611             : 
    1612     4178939 : bool XmppStateMachine::DequeueEvent(
    1613             :         boost::intrusive_ptr<const sc::event_base> event) {
    1614             :     // Process message event and enqueue additional events as necessary.
    1615             :     const xmsm::EvXmppMessage *ev_xmpp_message =
    1616     4178939 :             dynamic_cast<const xmsm::EvXmppMessage *>(event.get());
    1617     4178935 :     if (ev_xmpp_message) {
    1618     4110882 :         ProcessMessage(ev_xmpp_message->session, ev_xmpp_message->msg);
    1619             :     } else {
    1620       68053 :         ProcessEvent(*event);
    1621       68045 :         event.reset();
    1622             :     }
    1623     4178952 :     return true;
    1624             : }
    1625             : 
    1626     4178689 : void XmppStateMachine::ProcessEvent(const sc::event_base &event) {
    1627     4178689 :     const xmsm::EvTcpDeleteSession *deferred_delete =
    1628     4178689 :             dynamic_cast<const xmsm::EvTcpDeleteSession *>(&event);
    1629     4178689 :     XMPP_UTDEBUG(XmppStateMachineDequeueEvent,
    1630             :                  connection() ? connection()->ToUVEKey() : "",
    1631             :                  XMPP_PEER_DIR_IN, ChannelType(), TYPE_NAME(event), StateName(),
    1632             :                  connection() ?
    1633             :                     connection()->endpoint().address().to_string() : "",
    1634             :                  connection() ? connection()->GetTo() : "");
    1635     4178715 :     if (deferred_delete) {
    1636       12304 :         TcpSession *session = deferred_delete->session;
    1637       12304 :         session->server()->DeleteSession(session);
    1638       12305 :         return;
    1639             :     }
    1640     4166411 :     if (deleted_) {
    1641           0 :         return;
    1642             :     }
    1643             : 
    1644     4166411 :     update_last_event(TYPE_NAME(event));
    1645     4166404 :     in_dequeue_ = true;
    1646     4166404 :     process_event(event);
    1647     4166389 :     in_dequeue_ = false;
    1648             : }
    1649             : 
    1650       10568 : void XmppStateMachine::unconsumed_event(const sc::event_base &event) {
    1651       10568 :     XMPP_INFO(XmppUnconsumedEvent, connection() ? connection()->ToUVEKey() : "",
    1652             :               XMPP_PEER_DIR_IN, ChannelType(), TYPE_NAME(event), StateName());
    1653       10567 : }
    1654             : 
    1655     4166408 : void XmppStateMachine::update_last_event(const std::string &event) {
    1656     4166408 :     set_last_event(event);
    1657             : 
    1658     4166413 :     if (!logUVE()) return;
    1659             : 
    1660             :     // Skip iq and message events.
    1661           1 :     if (event == "xmsm::EvXmppIqStanza" ||
    1662           0 :         event == "xmsm::EvXmppMessageStanza") {
    1663           0 :         return;
    1664             :     }
    1665             : 
    1666             :     // Skip keepalive events after we've reached established state.
    1667           0 :     if (state_ == xmsm::ESTABLISHED && event == "xmsm::EvXmppKeepalive") {
    1668           0 :         return;
    1669             :     }
    1670             : 
    1671           0 :     XmppPeerInfoData peer_info;
    1672           0 :     peer_info.set_name(connection()->ToUVEKey());
    1673           0 :     PeerEventInfo event_info;
    1674           0 :     event_info.set_last_event(last_event_);
    1675           0 :     event_info.set_last_event_at(last_event_at_);
    1676           0 :     peer_info.set_event_info(event_info);
    1677             : 
    1678           0 :     assert(!peer_info.get_name().empty());
    1679           0 :     XMPPPeerInfo::Send(peer_info);
    1680           0 : }
    1681             : 
    1682             : //
    1683             : // Enqueue an event to xmpp state machine.
    1684             : // Return false if the event is not enqueued.
    1685             : //
    1686     4179363 : bool XmppStateMachine::Enqueue(const sc::event_base &event) {
    1687     4179363 :     if (!deleted_) {
    1688     4179361 :         work_queue_.Enqueue(event.intrusive_from_this());
    1689     4179382 :         return true;
    1690             :     }
    1691             : 
    1692           2 :     return false;
    1693             : }
    1694             : 
    1695             : // Object trace routines.
    1696       44702 : void XmppStateMachine::SendConnectionInfo(const string &event,
    1697             :          const string &nextstate) {
    1698       44702 :     XmppConnectionInfo info;
    1699       44691 :     SendConnectionInfo(&info, event, nextstate);
    1700       89430 :     return;
    1701       44715 : }
    1702             : 
    1703       87422 : void XmppStateMachine::SendConnectionInfo(XmppConnectionInfo *info,
    1704             :         const string &event, const string &nextstate) {
    1705             : 
    1706       87422 :     info->set_ip_address(this->connection()->endpoint().address().to_string());
    1707       87450 :     info->set_state(StateName());
    1708       87442 :     info->set_event(event);
    1709       87444 :     if (!nextstate.empty()) {
    1710       70089 :          info->set_next_state(nextstate);
    1711             :     }
    1712       87444 :     XMPP_CONNECTION_LOG_MSG(*info);
    1713       87447 :     return;
    1714             : }
    1715             : 
    1716             : // Resurrect an old xmpp connection if present (when under GracefulRestart)
    1717             : //
    1718             : // During Graceful Restart (or otherwise), new connections are rejected in
    1719             : // ProcessStreamHeaderMessage() itself until old one's cleanup process is
    1720             : // complete and the system is ready to start a new session.
    1721             : //
    1722             : // Hence in here, when called upon receipt of OpenMessage, we can try to reuse
    1723             : // old XmppConnection if present and there by complete any pending GR process
    1724             : //
    1725             : // We do so by reusing XmppConnection, XmppChannel, etc. from the old connection
    1726             : // and only use the XmppSession and XmppStateMachine from the new session
    1727             : //
    1728             : // New connection is instead associated with the old state machine and session,
    1729             : // and their deletion is triggered
    1730        6305 : void XmppStateMachine::ResurrectOldConnection(XmppConnection *new_connection,
    1731             :                                               XmppSession *new_session) {
    1732             : 
    1733             :     // Look for an endpoint (which is a persistent data structure) across
    1734             :     // xmpp session flips
    1735             :     bool created;
    1736             :     XmppConnectionEndpoint *connection_endpoint =
    1737             :         static_cast<XmppServer *>(
    1738        6305 :             new_connection->server())->LocateConnectionEndpoint(
    1739             :                 static_cast<XmppServerConnection *>(new_connection), created);
    1740             : 
    1741             :     // If this is a new endpoint, then there is no older connection to manage.
    1742        6304 :     if (created)
    1743        6200 :         return;
    1744             : 
    1745             :     // GR Helper must be enabled when we are trying to resurrect connection.
    1746         104 :     XmppServer *server = dynamic_cast<XmppServer *>(new_connection->server());
    1747         104 :     assert(server->IsPeerCloseGraceful());
    1748             : 
    1749         104 :     XMPP_NOTICE(XmppCreateConnection, new_session->ToUVEKey(),
    1750             :                 XMPP_PEER_DIR_IN,
    1751             :                 "Resurrect xmpp connection " + new_session->ToString());
    1752             : 
    1753             :     // Retrieve old XmppConnection and XmppStateMachine (to reuse)
    1754         104 :     XmppConnection *old_xmpp_connection = connection_endpoint->connection();
    1755         104 :     assert(old_xmpp_connection);
    1756             : 
    1757         104 :     XmppStateMachine *old_state_machine = old_xmpp_connection->state_machine();
    1758         104 :     assert(old_state_machine);
    1759             : 
    1760             :     // Swap Old and New connections and state machines linkages
    1761         104 :     new_connection->SwapXmppStateMachine(old_xmpp_connection);
    1762         104 :     this->SwapXmppConnection(old_state_machine);
    1763             : 
    1764             :     // Update XmppConnection in the old session.
    1765         104 :     XmppSession *old_xmpp_session = old_state_machine->session();
    1766         104 :     if (old_xmpp_session)
    1767           0 :         old_xmpp_session->SetConnection(new_connection);
    1768         104 :     new_session->SetConnection(old_xmpp_connection);
    1769             : 
    1770             :     // Set new session with the old connection as it would be the current active
    1771             :     // connection from now on.
    1772         104 :     old_xmpp_connection->set_session(new_session);
    1773             : 
    1774             :     // Trigger deletion of the new connection which now is associated wth the
    1775             :     // the old_state_machine
    1776         104 :     new_connection->Shutdown();
    1777             : }
    1778             : 
    1779         104 : void XmppStateMachine::SwapXmppConnection(XmppStateMachine *other) {
    1780         104 :     swap(connection_, other->connection_);
    1781         104 :     connection_->SwapContents(other->connection_);
    1782         104 : }

Generated by: LCOV version 1.14