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-18 01:51:13 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       14100 : XmppSession::XmppSession(XmppConnectionManager *manager, SslSocket *socket,
      34       14100 :     bool async_ready)
      35             :     : SslSession(manager, socket, async_ready),
      36       14100 :       manager_(manager),
      37       14100 :       connection_(NULL),
      38       14099 :       tag_known_(0),
      39       14099 :       task_instance_(-1),
      40       14099 :       stats_(XmppStanza::RESERVED_STANZA, XmppSession::StatsPair(0, 0)),
      41       14100 :       keepalive_probes_(kSessionKeepaliveProbes) {
      42       14099 :     buf_.reserve(kMaxMessageSize);
      43       14100 :     offset_ = buf_.begin();
      44       14100 :     stream_open_matched_ = false;
      45       14100 : }
      46             : 
      47       28197 : XmppSession::~XmppSession() {
      48       14099 :     set_observer(NULL);
      49       14099 :     connection_ = NULL;
      50       28197 : }
      51             : 
      52       14179 : void XmppSession::SetConnection(XmppConnection *connection) {
      53       14179 :     assert(connection);
      54       14179 :     connection_ = connection;
      55       14179 :     task_instance_ = connection_->GetTaskInstance();
      56       14179 : }
      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     4169161 : void XmppSession::IncStats(unsigned int type, uint64_t bytes) {
     103     4169161 :     assert (type < (unsigned int)XmppStanza::RESERVED_STANZA);
     104     4169161 :     stats_[type].first++;
     105     4169164 :     stats_[type].second += bytes;
     106     4169164 : }
     107             : 
     108        6590 : boost::system::error_code XmppSession::EnableTcpKeepalive(int hold_time) {
     109        6590 :     char *keepalive_time_str = getenv("TCP_KEEPALIVE_SECONDS");
     110        6590 :     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        6590 :     if (hold_time <= 9) {
     117           0 :         hold_time = 9; // min hold-time in secs.
     118             :     }
     119        6590 :     hold_time = ((hold_time > 18)? hold_time/2 : hold_time);
     120        6590 :     keepalive_idle_time_ = hold_time/3;
     121        6590 :     keepalive_interval_ =
     122        6590 :         ((hold_time - keepalive_idle_time_)/keepalive_probes_);
     123        6590 :     tcp_user_timeout_ = (hold_time * 1000); // msec
     124             : 
     125        6590 :     return (SetSocketKeepaliveOptions(keepalive_idle_time_,
     126             :                                       keepalive_interval_,
     127             :                                       keepalive_probes_,
     128        6590 :                                       tcp_user_timeout_));
     129             : }
     130             : 
     131     1716153 : regex XmppSession::tag_to_pattern(const char *tag) {
     132     1716153 :     std::string token("</");
     133     1716148 :     token += ++tag;
     134     1716148 :     token += "[\\s\\t\\r\\n]*>";
     135             : 
     136     3432316 :     regex exp(token.c_str());
     137     3432318 :     return exp;
     138     1716160 : }
     139             : 
     140     1174504 : void XmppSession::SetBuf(const std::string &str) {
     141     1174504 :     if (buf_.empty()) {
     142     1122079 :         ReplaceBuf(str);
     143             :     } else {
     144       52426 :         int pos = offset_ - buf_.begin();
     145       52427 :         buf_ += str;
     146       52427 :         offset_ = buf_.begin() + pos;
     147             :     }
     148     1174514 : }
     149             : 
     150     4317257 : void XmppSession::ReplaceBuf(const std::string &str) {
     151     4317257 :     buf_ = str;
     152     4317262 :     buf_.reserve(kMaxMessageSize+8);
     153     4317267 :     offset_ = buf_.begin();
     154     4317267 : }
     155             : 
     156     4169194 : bool XmppSession::LeftOver() const {
     157     4169194 :     if (buf_.empty())
     158           0 :         return false;
     159     4169198 :     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     3417625 : int XmppSession::MatchRegex(const regex &patt) {
     165             : 
     166     3417625 :     std::string::const_iterator end = buf_.end();
     167             : 
     168     3417618 :     if (regex_search(offset_, end, res_, patt,
     169     3417606 :                      boost::match_default | boost::match_partial) == 0) {
     170       46050 :         return -1;
     171             :     }
     172     3371556 :     if(res_[0].matched == false) {
     173             :         // partial match
     174        6391 :         offset_ = res_[0].first;
     175        6391 :         return 1;
     176             :     } else {
     177     3365163 :         begin_tag_ = string(res_[0].first, res_[0].second);
     178     3365160 :         offset_ = res_[0].second;
     179     3365154 :         return 0;
     180             :     }
     181             : }
     182             : 
     183     4369658 : bool XmppSession::Match(Buffer buffer, int *result, bool NewBuf) {
     184     4369658 :     const XmppConnection *connection = this->Connection();
     185             : 
     186     4369663 :     if (connection == NULL) {
     187           0 :         return true;
     188             :     }
     189             : 
     190     4369663 :     xmsm::XmState state = connection->GetStateMcState();
     191             :     xmsm::XmOpenConfirmState oc_state =
     192     4369647 :         connection->GetStateMcOpenConfirmState();
     193             : 
     194     4369641 :     if (NewBuf) {
     195     1174465 :         const uint8_t *cp = BufferData(buffer);
     196             :         // TODO Avoid this copy
     197     1174462 :         std::string str(cp, cp + BufferSize(buffer));
     198     1174502 :         XmppSession::SetBuf(str);
     199     1174510 :     }
     200             : 
     201     4369688 :     int m = -1;
     202     4369688 :     *result = 0;
     203             :     do {
     204     6052277 :         if (!tag_known_) {
     205             :             // check for whitespaces
     206     4317506 :             size_t pos = buf_.find_first_not_of(sXMPP_VALIDWS);
     207     4317514 :             if (pos != 0) {
     208     2634668 :                 if (pos == string::npos) pos = buf_.size();
     209     2634667 :                 offset_ = buf_.begin() + pos;
     210     2634684 :                 return false;
     211             :             }
     212             :         }
     213             : 
     214     3417617 :         if (state == xmsm::ACTIVE || state == xmsm::IDLE) {
     215       13043 :             m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     216     3404574 :         } else if (state == xmsm::CONNECT || state == xmsm::OPENSENT) {
     217             :             // Note, these are client only states
     218       14810 :             if (!stream_open_matched_) {
     219       12562 :                 m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     220       12562 :                 if ((m == 0) && (tag_known_)) {
     221        6281 :                     stream_open_matched_ = true;
     222             :                 }
     223             :             } else {
     224        2248 :                 m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):
     225             :                                             stream_features_patt_);
     226             :             }
     227     3389764 :         } else if ((state == xmsm::OPENCONFIRM) && !(IsSslDisabled())) {
     228       12302 :             if (connection->IsClient()) {
     229        6482 :                 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        3572 :                 } else if (oc_state == xmsm::OPENCONFIRM_FEATURE_SUCCESS) {
     237        2910 :                     m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     238             :                 } else {
     239         662 :                     m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):
     240             :                                                 stream_features_patt_);
     241             :                 }
     242             :             } else {
     243        5820 :                 if (oc_state == xmsm::OPENCONFIRM_FEATURE_SUCCESS) {
     244        2910 :                     m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     245             :                 } else {
     246        2910 :                     m = MatchRegex(tag_known_ ? end_patt_:starttls_patt_);
     247        2909 :                     if ((m == 0) && (tag_known_)) {
     248        1455 :                         SetSslHandShakeInProgress(true);
     249             :                     }
     250             :                 }
     251             :             }
     252     3377462 :         } else if (state == xmsm::OPENCONFIRM || state == xmsm::ESTABLISHED) {
     253     3377466 :             m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):patt_);
     254             :         }
     255             : 
     256     3417613 :         if (m == 0) { // full match
     257     3365176 :             *result = 0;
     258     3365176 :             tag_known_ ^= 1;
     259     3365176 :             if (!tag_known_) {
     260             :                 // Found well formed xml
     261     1682587 :                 return false;
     262             :             }
     263       52437 :         } else if (m == -1) { // no match
     264       46048 :             return true;
     265             :         } else {
     266        6389 :             return true; // partial. read more
     267             :         }
     268     1682589 :     } 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     1174586 : void XmppSession::OnRead(Buffer buffer) {
     277     1174586 :     if (this->Connection() == NULL || !connection_) {
     278             :         // Connection is deleted. Session is being deleted as well
     279             :         // Drop the packet.
     280          96 :         ReleaseBuffer(buffer);
     281          96 :         return;
     282             :     }
     283             : 
     284     1174488 :     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     1174487 :     int result = 0;
     293     1174487 :     bool more = Match(buffer, &result, true);
     294             :     do {
     295     4369697 :         if (more == false) {
     296     4317260 :             if (result < 0) {
     297             :                 // TODO generate error, close connection.
     298      148069 :                 break;
     299             :             }
     300             : 
     301             :             // We got good match. Process the message
     302     4317260 :             std::string::const_iterator st = buf_.begin();
     303     4317248 :             std::string xml = string(st, offset_);
     304             :             // Ensure we have not reached the end
     305     4317232 :             if (buf_.begin() == offset_) { // xml.size() == 0
     306      148073 :                 buf_.clear();
     307      148073 :                 break;
     308             :             }
     309             : 
     310     4169174 :             connection_->ReceiveMsg(this, xml);
     311             : 
     312     4317274 :         } 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       52437 :             break;
     317             :         }
     318             : 
     319     4169198 :         if (LeftOver()) {
     320     3195187 :             std::string::const_iterator st = buf_.end();
     321     3195182 :             ReplaceBuf(string(offset_, st));
     322     3195184 :             more = Match(buffer, &result, false);
     323             :         } else {
     324             :             // No more data in the Buffer
     325      974006 :             buf_.clear();
     326      974010 :             break;
     327             :         }
     328     3195184 :     } while (true);
     329             : 
     330     1174516 :     ReleaseBuffer(buffer);
     331     1174524 :     return;
     332             : }

Generated by: LCOV version 1.14