LCOV - code coverage report
Current view: top level - xmpp - xmpp_session.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 154 171 90.1 %
Date: 2026-06-04 02:06:09 Functions: 15 17 88.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "base/regex.h"
       6             : #include "xmpp/xmpp_session.h"
       7             : 
       8             : #include "xmpp/xmpp_connection.h"
       9             : #include "xmpp/xmpp_log.h"
      10             : #include "xmpp/xmpp_proto.h"
      11             : #include "xmpp/xmpp_server.h"
      12             : #include "xmpp/xmpp_state_machine.h"
      13             : 
      14             : #include "sandesh/sandesh_trace.h"
      15             : #include "sandesh/xmpp_trace_sandesh_types.h"
      16             : 
      17             : using namespace std;
      18             : using contrail::regex;
      19             : using contrail::regex_match;
      20             : using contrail::regex_search;
      21             : 
      22             : using boost::asio::mutable_buffer;
      23             : 
      24             : const regex XmppSession::patt_(rXMPP_MESSAGE);
      25             : const regex XmppSession::stream_patt_(rXMPP_STREAM_START);
      26             : const regex XmppSession::stream_res_end_(rXMPP_STREAM_END);
      27             : const regex XmppSession::whitespace_(sXMPP_WHITESPACE);
      28             : const regex XmppSession::stream_features_patt_(rXMPP_STREAM_FEATURES);
      29             : const regex XmppSession::starttls_patt_(rXMPP_STREAM_STARTTLS);
      30             : const regex XmppSession::proceed_patt_(rXMPP_STREAM_PROCEED);
      31             : const regex XmppSession::end_patt_(rXMPP_STREAM_STANZA_END);
      32             : 
      33       14082 : XmppSession::XmppSession(XmppConnectionManager *manager, SslSocket *socket,
      34       14082 :     bool async_ready)
      35             :     : SslSession(manager, socket, async_ready),
      36       14082 :       manager_(manager),
      37       14082 :       connection_(NULL),
      38       14081 :       tag_known_(0),
      39       14081 :       task_instance_(-1),
      40       14081 :       stats_(XmppStanza::RESERVED_STANZA, XmppSession::StatsPair(0, 0)),
      41       14082 :       keepalive_probes_(kSessionKeepaliveProbes) {
      42       14081 :     buf_.reserve(kMaxMessageSize);
      43       14082 :     offset_ = buf_.begin();
      44       14082 :     stream_open_matched_ = false;
      45       14082 : }
      46             : 
      47       28163 : XmppSession::~XmppSession() {
      48       14082 :     set_observer(NULL);
      49       14082 :     connection_ = NULL;
      50       28163 : }
      51             : 
      52       14161 : void XmppSession::SetConnection(XmppConnection *connection) {
      53       14161 :     assert(connection);
      54       14161 :     connection_ = connection;
      55       14161 :     task_instance_ = connection_->GetTaskInstance();
      56       14161 : }
      57             : 
      58             : //
      59             : // Dissociate the connection from the this XmppSession.
      60             : // Do not invalidate the task_instance since it can be used to spawn an
      61             : // io::ReaderTask while this method is being executed.
      62             : //
      63       33308 : void XmppSession::ClearConnection() {
      64       33308 :     connection_ = NULL;
      65       33308 : }
      66             : 
      67             : //
      68             : // Concurrency: called in the context of bgp::Config task.
      69             : //
      70             : // Process write ready callback.
      71             : //
      72           0 : void XmppSession::ProcessWriteReady() {
      73           0 :     if (!connection_)
      74           0 :         return;
      75           0 :     connection_->WriteReady();
      76             : }
      77             : 
      78             : //
      79             : // Concurrency: called in the context of io thread.
      80             : //
      81             : // Handle write ready callback.
      82             : //
      83             : // Enqueue session to the XmppConnectionManager. The session is added to a
      84             : // WorkQueue gets processed in the context of bgp::Config task. Doing this
      85             : // ensures that we don't access the XmppConnection while the XmppConnection
      86             : // is trying to clear our back pointer to it.
      87             : //
      88             : // We can ignore any errors since the StateMachine will get informed of the
      89             : // TcpSession close independently and react to it.
      90             : //
      91           0 : void XmppSession::WriteReady(const boost::system::error_code &error) {
      92           0 :     if (error)
      93           0 :         return;
      94           0 :     manager_->EnqueueSession(this);
      95             : }
      96             : 
      97          30 : XmppSession::StatsPair XmppSession::Stats(unsigned int type) const {
      98          30 :     assert (type < (unsigned int)XmppStanza::RESERVED_STANZA);
      99          30 :     return stats_[type];
     100             : }
     101             : 
     102     4243140 : void XmppSession::IncStats(unsigned int type, uint64_t bytes) {
     103     4243140 :     assert (type < (unsigned int)XmppStanza::RESERVED_STANZA);
     104     4243140 :     stats_[type].first++;
     105     4243143 :     stats_[type].second += bytes;
     106     4243141 : }
     107             : 
     108        6589 : boost::system::error_code XmppSession::EnableTcpKeepalive(int hold_time) {
     109        6589 :     char *keepalive_time_str = getenv("TCP_KEEPALIVE_SECONDS");
     110        6589 :     if (keepalive_time_str) {
     111           0 :         hold_time = strtoul(keepalive_time_str, NULL, 0) * 3;
     112           0 :         if (!hold_time)
     113           0 :             return boost::system::error_code();
     114             :     }
     115             : 
     116        6589 :     if (hold_time <= 9) {
     117           0 :         hold_time = 9; // min hold-time in secs.
     118             :     }
     119        6589 :     hold_time = ((hold_time > 18)? hold_time/2 : hold_time);
     120        6589 :     keepalive_idle_time_ = hold_time/3;
     121        6589 :     keepalive_interval_ =
     122        6589 :         ((hold_time - keepalive_idle_time_)/keepalive_probes_);
     123        6589 :     tcp_user_timeout_ = (hold_time * 1000); // msec
     124             : 
     125        6589 :     return (SetSocketKeepaliveOptions(keepalive_idle_time_,
     126             :                                       keepalive_interval_,
     127             :                                       keepalive_probes_,
     128        6589 :                                       tcp_user_timeout_));
     129             : }
     130             : 
     131     1738244 : regex XmppSession::tag_to_pattern(const char *tag) {
     132     1738244 :     std::string token("</");
     133     1738240 :     token += ++tag;
     134     1738239 :     token += "[\\s\\t\\r\\n]*>";
     135             : 
     136     3476501 :     regex exp(token.c_str());
     137     3476502 :     return exp;
     138     1738253 : }
     139             : 
     140     1201579 : void XmppSession::SetBuf(const std::string &str) {
     141     1201579 :     if (buf_.empty()) {
     142     1149068 :         ReplaceBuf(str);
     143             :     } else {
     144       52512 :         int pos = offset_ - buf_.begin();
     145       52512 :         buf_ += str;
     146       52512 :         offset_ = buf_.begin() + pos;
     147             :     }
     148     1201581 : }
     149             : 
     150     4389066 : void XmppSession::ReplaceBuf(const std::string &str) {
     151     4389066 :     buf_ = str;
     152     4389062 :     buf_.reserve(kMaxMessageSize+8);
     153     4389064 :     offset_ = buf_.begin();
     154     4389068 : }
     155             : 
     156     4243174 : bool XmppSession::LeftOver() const {
     157     4243174 :     if (buf_.empty())
     158           0 :         return false;
     159     4243175 :     return (buf_.end() != offset_);
     160             : }
     161             : 
     162             : // Match a pattern in the buffer. Partially matched string is
     163             : // kept in buf_ for use in conjucntion with next buffer read.
     164     3461696 : int XmppSession::MatchRegex(const regex &patt) {
     165             : 
     166     3461696 :     std::string::const_iterator end = buf_.end();
     167             : 
     168     3461682 :     if (regex_search(offset_, end, res_, patt,
     169     3461679 :                      boost::match_default | boost::match_partial) == 0) {
     170       46675 :         return -1;
     171             :     }
     172     3415004 :     if(res_[0].matched == false) {
     173             :         // partial match
     174        5852 :         offset_ = res_[0].first;
     175        5852 :         return 1;
     176             :     } else {
     177     3409143 :         begin_tag_ = string(res_[0].first, res_[0].second);
     178     3409150 :         offset_ = res_[0].second;
     179     3409141 :         return 0;
     180             :     }
     181             : }
     182             : 
     183     4441525 : bool XmppSession::Match(Buffer buffer, int *result, bool NewBuf) {
     184     4441525 :     const XmppConnection *connection = this->Connection();
     185             : 
     186     4441519 :     if (connection == NULL) {
     187           0 :         return true;
     188             :     }
     189             : 
     190     4441519 :     xmsm::XmState state = connection->GetStateMcState();
     191             :     xmsm::XmOpenConfirmState oc_state =
     192     4441507 :         connection->GetStateMcOpenConfirmState();
     193             : 
     194     4441500 :     if (NewBuf) {
     195     1201511 :         const uint8_t *cp = BufferData(buffer);
     196             :         // TODO Avoid this copy
     197     1201501 :         std::string str(cp, cp + BufferSize(buffer));
     198     1201576 :         XmppSession::SetBuf(str);
     199     1201575 :     }
     200             : 
     201     4441568 :     int m = -1;
     202     4441568 :     *result = 0;
     203             :     do {
     204     6146152 :         if (!tag_known_) {
     205             :             // check for whitespaces
     206     4389285 :             size_t pos = buf_.find_first_not_of(sXMPP_VALIDWS);
     207     4389292 :             if (pos != 0) {
     208     2684478 :                 if (pos == string::npos) pos = buf_.size();
     209     2684480 :                 offset_ = buf_.begin() + pos;
     210     2684493 :                 return false;
     211             :             }
     212             :         }
     213             : 
     214     3461681 :         if (state == xmsm::ACTIVE || state == xmsm::IDLE) {
     215       13039 :             m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     216     3448642 :         } else if (state == xmsm::CONNECT || state == xmsm::OPENSENT) {
     217             :             // Note, these are client only states
     218       14928 :             if (!stream_open_matched_) {
     219       12574 :                 m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     220       12574 :                 if ((m == 0) && (tag_known_)) {
     221        6287 :                     stream_open_matched_ = true;
     222             :                 }
     223             :             } else {
     224        2354 :                 m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):
     225             :                                             stream_features_patt_);
     226             :             }
     227     3433714 :         } else if ((state == xmsm::OPENCONFIRM) && !(IsSslDisabled())) {
     228       12179 :             if (connection->IsClient()) {
     229        6374 :                 if (oc_state == xmsm::OPENCONFIRM_FEATURE_NEGOTIATION) {
     230        2910 :                     m = MatchRegex(tag_known_ ? end_patt_: proceed_patt_);
     231        2910 :                     if ((m == 0) && (tag_known_)) {
     232             :                         // set the flag, as we do not want OnRead function to
     233             :                         // read any more data from basic socket.
     234        1455 :                         SetSslHandShakeInProgress(true);
     235             :                     }
     236        3464 :                 } else if (oc_state == xmsm::OPENCONFIRM_FEATURE_SUCCESS) {
     237        2910 :                     m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     238             :                 } else {
     239         554 :                     m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):
     240             :                                                 stream_features_patt_);
     241             :                 }
     242             :             } else {
     243        5818 :                 if (oc_state == xmsm::OPENCONFIRM_FEATURE_SUCCESS) {
     244        2910 :                     m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     245             :                 } else {
     246        2908 :                     m = MatchRegex(tag_known_ ? end_patt_:starttls_patt_);
     247        2910 :                     if ((m == 0) && (tag_known_)) {
     248        1455 :                         SetSslHandShakeInProgress(true);
     249             :                     }
     250             :                 }
     251             :             }
     252     3421535 :         } else if (state == xmsm::OPENCONFIRM || state == xmsm::ESTABLISHED) {
     253     3421526 :             m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):patt_);
     254             :         }
     255             : 
     256     3461689 :         if (m == 0) { // full match
     257     3409166 :             *result = 0;
     258     3409166 :             tag_known_ ^= 1;
     259     3409166 :             if (!tag_known_) {
     260             :                 // Found well formed xml
     261     1704582 :                 return false;
     262             :             }
     263       52523 :         } else if (m == -1) { // no match
     264       46673 :             return true;
     265             :         } else {
     266        5850 :             return true; // partial. read more
     267             :         }
     268     1704584 :     } while (true);
     269             : 
     270             :     return true;
     271             : }
     272             : 
     273             : // Read the socket stream and send messages to the connection object.
     274             : // The buffer is copied to local string for regex match.
     275             : // TODO Code need to change st Match() is done on buffer itself.
     276     1201644 : void XmppSession::OnRead(Buffer buffer) {
     277     1201644 :     if (this->Connection() == NULL || !connection_) {
     278             :         // Connection is deleted. Session is being deleted as well
     279             :         // Drop the packet.
     280         108 :         ReleaseBuffer(buffer);
     281         108 :         return;
     282             :     }
     283             : 
     284     1201529 :     if (connection_->disable_read()) {
     285           0 :         ReleaseBuffer(buffer);
     286             : 
     287             :         // Reset the hold timer as we did receive some thing from the peer
     288           0 :         connection_->state_machine()->StartHoldTimer();
     289           0 :         return;
     290             :     }
     291             : 
     292     1201526 :     int result = 0;
     293     1201526 :     bool more = Match(buffer, &result, true);
     294             :     do {
     295     4441582 :         if (more == false) {
     296     4389059 :             if (result < 0) {
     297             :                 // TODO generate error, close connection.
     298      145884 :                 break;
     299             :             }
     300             : 
     301             :             // We got good match. Process the message
     302     4389059 :             std::string::const_iterator st = buf_.begin();
     303     4389060 :             std::string xml = string(st, offset_);
     304             :             // Ensure we have not reached the end
     305     4389029 :             if (buf_.begin() == offset_) { // xml.size() == 0
     306      145889 :                 buf_.clear();
     307      145891 :                 break;
     308             :             }
     309             : 
     310     4243145 :             connection_->ReceiveMsg(this, xml);
     311             : 
     312     4389064 :         } else {
     313             :             // Read more data. Either we have partial match
     314             :             // or no match but in this state we need to keep
     315             :             // reading data.
     316       52523 :             break;
     317             :         }
     318             : 
     319     4243178 :         if (LeftOver()) {
     320     3240004 :             std::string::const_iterator st = buf_.end();
     321     3240001 :             ReplaceBuf(string(offset_, st));
     322     3240000 :             more = Match(buffer, &result, false);
     323             :         } else {
     324             :             // No more data in the Buffer
     325     1003163 :             buf_.clear();
     326     1003168 :             break;
     327             :         }
     328     3240002 :     } while (true);
     329             : 
     330     1201575 :     ReleaseBuffer(buffer);
     331     1201590 :     return;
     332             : }

Generated by: LCOV version 1.14