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-10-05 02:12:29 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         718 : XmppSession::XmppSession(XmppConnectionManager *manager, SslSocket *socket,
      34         718 :     bool async_ready)
      35             :     : SslSession(manager, socket, async_ready),
      36         718 :       manager_(manager),
      37         718 :       connection_(NULL),
      38         718 :       tag_known_(0),
      39         718 :       task_instance_(-1),
      40         718 :       stats_(XmppStanza::RESERVED_STANZA, XmppSession::StatsPair(0, 0)),
      41         718 :       keepalive_probes_(kSessionKeepaliveProbes) {
      42         718 :     buf_.reserve(kMaxMessageSize);
      43         718 :     offset_ = buf_.begin();
      44         718 :     stream_open_matched_ = false;
      45         718 : }
      46             : 
      47        1435 : XmppSession::~XmppSession() {
      48         718 :     set_observer(NULL);
      49         718 :     connection_ = NULL;
      50        1435 : }
      51             : 
      52         737 : void XmppSession::SetConnection(XmppConnection *connection) {
      53         737 :     assert(connection);
      54         737 :     connection_ = connection;
      55         737 :     task_instance_ = connection_->GetTaskInstance();
      56         737 : }
      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        1573 : void XmppSession::ClearConnection() {
      64        1573 :     connection_ = NULL;
      65        1573 : }
      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      604415 : void XmppSession::IncStats(unsigned int type, uint64_t bytes) {
     103      604415 :     assert (type < (unsigned int)XmppStanza::RESERVED_STANZA);
     104      604415 :     stats_[type].first++;
     105      604415 :     stats_[type].second += bytes;
     106      604415 : }
     107             : 
     108         322 : boost::system::error_code XmppSession::EnableTcpKeepalive(int hold_time) {
     109         322 :     char *keepalive_time_str = getenv("TCP_KEEPALIVE_SECONDS");
     110         322 :     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         322 :     if (hold_time <= 9) {
     117           0 :         hold_time = 9; // min hold-time in secs.
     118             :     }
     119         322 :     hold_time = ((hold_time > 18)? hold_time/2 : hold_time);
     120         322 :     keepalive_idle_time_ = hold_time/3;
     121         322 :     keepalive_interval_ =
     122         322 :         ((hold_time - keepalive_idle_time_)/keepalive_probes_);
     123         322 :     tcp_user_timeout_ = (hold_time * 1000); // msec
     124             : 
     125         322 :     return (SetSocketKeepaliveOptions(keepalive_idle_time_,
     126             :                                       keepalive_interval_,
     127             :                                       keepalive_probes_,
     128         322 :                                       tcp_user_timeout_));
     129             : }
     130             : 
     131      243923 : regex XmppSession::tag_to_pattern(const char *tag) {
     132      243923 :     std::string token("</");
     133      243923 :     token += ++tag;
     134      243923 :     token += "[\\s\\t\\r\\n]*>";
     135             : 
     136      487846 :     regex exp(token.c_str());
     137      487846 :     return exp;
     138      243923 : }
     139             : 
     140      156101 : void XmppSession::SetBuf(const std::string &str) {
     141      156101 :     if (buf_.empty()) {
     142      148542 :         ReplaceBuf(str);
     143             :     } else {
     144        7559 :         int pos = offset_ - buf_.begin();
     145        7559 :         buf_ += str;
     146        7559 :         offset_ = buf_.begin() + pos;
     147             :     }
     148      156101 : }
     149             : 
     150      615272 : void XmppSession::ReplaceBuf(const std::string &str) {
     151      615272 :     buf_ = str;
     152      615272 :     buf_.reserve(kMaxMessageSize+8);
     153      615272 :     offset_ = buf_.begin();
     154      615270 : }
     155             : 
     156      604416 : bool XmppSession::LeftOver() const {
     157      604416 :     if (buf_.empty())
     158           0 :         return false;
     159      604416 :     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      482508 : int XmppSession::MatchRegex(const regex &patt) {
     165             : 
     166      482508 :     std::string::const_iterator end = buf_.end();
     167             : 
     168      482507 :     if (regex_search(offset_, end, res_, patt,
     169      482507 :                      boost::match_default | boost::match_partial) == 0) {
     170        6698 :         return -1;
     171             :     }
     172      475809 :     if(res_[0].matched == false) {
     173             :         // partial match
     174         863 :         offset_ = res_[0].first;
     175         863 :         return 1;
     176             :     } else {
     177      474946 :         begin_tag_ = string(res_[0].first, res_[0].second);
     178      474945 :         offset_ = res_[0].second;
     179      474946 :         return 0;
     180             :     }
     181             : }
     182             : 
     183      622818 : bool XmppSession::Match(Buffer buffer, int *result, bool NewBuf) {
     184      622818 :     const XmppConnection *connection = this->Connection();
     185             : 
     186      622818 :     if (connection == NULL) {
     187           0 :         return true;
     188             :     }
     189             : 
     190      622818 :     xmsm::XmState state = connection->GetStateMcState();
     191             :     xmsm::XmOpenConfirmState oc_state =
     192      622817 :         connection->GetStateMcOpenConfirmState();
     193             : 
     194      622816 :     if (NewBuf) {
     195      156096 :         const uint8_t *cp = BufferData(buffer);
     196             :         // TODO Avoid this copy
     197      156096 :         std::string str(cp, cp + BufferSize(buffer));
     198      156098 :         XmppSession::SetBuf(str);
     199      156098 :     }
     200             : 
     201      622818 :     int m = -1;
     202      622818 :     *result = 0;
     203             :     do {
     204      860288 :         if (!tag_known_) {
     205             :             // check for whitespaces
     206      615314 :             size_t pos = buf_.find_first_not_of(sXMPP_VALIDWS);
     207      615315 :             if (pos != 0) {
     208      377793 :                 if (pos == string::npos) pos = buf_.size();
     209      377793 :                 offset_ = buf_.begin() + pos;
     210      377793 :                 return false;
     211             :             }
     212             :         }
     213             : 
     214      482496 :         if (state == xmsm::ACTIVE || state == xmsm::IDLE) {
     215         556 :             m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     216      481940 :         } else if (state == xmsm::CONNECT || state == xmsm::OPENSENT) {
     217             :             // Note, these are client only states
     218         700 :             if (!stream_open_matched_) {
     219         530 :                 m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     220         530 :                 if ((m == 0) && (tag_known_)) {
     221         265 :                     stream_open_matched_ = true;
     222             :                 }
     223             :             } else {
     224         170 :                 m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):
     225             :                                             stream_features_patt_);
     226             :             }
     227      481240 :         } else if ((state == xmsm::OPENCONFIRM) && !(IsSslDisabled())) {
     228        1100 :             if (connection->IsClient()) {
     229         592 :                 if (oc_state == xmsm::OPENCONFIRM_FEATURE_NEGOTIATION) {
     230         254 :                     m = MatchRegex(tag_known_ ? end_patt_: proceed_patt_);
     231         254 :                     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         127 :                         SetSslHandShakeInProgress(true);
     235             :                     }
     236         338 :                 } else if (oc_state == xmsm::OPENCONFIRM_FEATURE_SUCCESS) {
     237         254 :                     m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     238             :                 } else {
     239          84 :                     m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):
     240             :                                                 stream_features_patt_);
     241             :                 }
     242             :             } else {
     243         508 :                 if (oc_state == xmsm::OPENCONFIRM_FEATURE_SUCCESS) {
     244         254 :                     m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_);
     245             :                 } else {
     246         254 :                     m = MatchRegex(tag_known_ ? end_patt_:starttls_patt_);
     247         254 :                     if ((m == 0) && (tag_known_)) {
     248         127 :                         SetSslHandShakeInProgress(true);
     249             :                     }
     250             :                 }
     251             :             }
     252      480140 :         } else if (state == xmsm::OPENCONFIRM || state == xmsm::ESTABLISHED) {
     253      480140 :             m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):patt_);
     254             :         }
     255             : 
     256      482496 :         if (m == 0) { // full match
     257      474939 :             *result = 0;
     258      474939 :             tag_known_ ^= 1;
     259      474939 :             if (!tag_known_) {
     260             :                 // Found well formed xml
     261      237469 :                 return false;
     262             :             }
     263        7557 :         } else if (m == -1) { // no match
     264        6696 :             return true;
     265             :         } else {
     266         861 :             return true; // partial. read more
     267             :         }
     268      237470 :     } 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      156098 : void XmppSession::OnRead(Buffer buffer) {
     277      156098 :     if (this->Connection() == NULL || !connection_) {
     278             :         // Connection is deleted. Session is being deleted as well
     279             :         // Drop the packet.
     280           1 :         ReleaseBuffer(buffer);
     281           1 :         return;
     282             :     }
     283             : 
     284      156098 :     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      156098 :     int result = 0;
     293      156098 :     bool more = Match(buffer, &result, true);
     294             :     do {
     295      622819 :         if (more == false) {
     296      615262 :             if (result < 0) {
     297             :                 // TODO generate error, close connection.
     298       10846 :                 break;
     299             :             }
     300             : 
     301             :             // We got good match. Process the message
     302      615262 :             std::string::const_iterator st = buf_.begin();
     303      615261 :             std::string xml = string(st, offset_);
     304             :             // Ensure we have not reached the end
     305      615261 :             if (buf_.begin() == offset_) { // xml.size() == 0
     306       10846 :                 buf_.clear();
     307       10846 :                 break;
     308             :             }
     309             : 
     310      604416 :             connection_->ReceiveMsg(this, xml);
     311             : 
     312      615262 :         } 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        7557 :             break;
     317             :         }
     318             : 
     319      604416 :         if (LeftOver()) {
     320      466721 :             std::string::const_iterator st = buf_.end();
     321      466720 :             ReplaceBuf(string(offset_, st));
     322      466721 :             more = Match(buffer, &result, false);
     323             :         } else {
     324             :             // No more data in the Buffer
     325      137694 :             buf_.clear();
     326      137695 :             break;
     327             :         }
     328      466721 :     } while (true);
     329             : 
     330      156098 :     ReleaseBuffer(buffer);
     331      156098 :     return;
     332             : }

Generated by: LCOV version 1.14