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 14252 : XmppSession::XmppSession(XmppConnectionManager *manager, SslSocket *socket, 34 14252 : bool async_ready) 35 : : SslSession(manager, socket, async_ready), 36 14252 : manager_(manager), 37 14252 : connection_(NULL), 38 14250 : tag_known_(0), 39 14250 : task_instance_(-1), 40 14250 : stats_(XmppStanza::RESERVED_STANZA, XmppSession::StatsPair(0, 0)), 41 14252 : keepalive_probes_(kSessionKeepaliveProbes) { 42 14251 : buf_.reserve(kMaxMessageSize); 43 14252 : offset_ = buf_.begin(); 44 14251 : stream_open_matched_ = false; 45 14251 : } 46 : 47 28501 : XmppSession::~XmppSession() { 48 14251 : set_observer(NULL); 49 14251 : connection_ = NULL; 50 28501 : } 51 : 52 14331 : void XmppSession::SetConnection(XmppConnection *connection) { 53 14331 : assert(connection); 54 14331 : connection_ = connection; 55 14331 : task_instance_ = connection_->GetTaskInstance(); 56 14331 : } 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 33479 : void XmppSession::ClearConnection() { 64 33479 : connection_ = NULL; 65 33479 : } 66 : 67 : // 68 : // Concurrency: called in the context of bgp::Config task. 69 : // 70 : // Process write ready callback. 71 : // 72 1 : void XmppSession::ProcessWriteReady() { 73 1 : if (!connection_) 74 0 : return; 75 1 : 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 1 : void XmppSession::WriteReady(const boost::system::error_code &error) { 92 1 : if (error) 93 0 : return; 94 1 : 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 4208582 : void XmppSession::IncStats(unsigned int type, uint64_t bytes) { 103 4208582 : assert (type < (unsigned int)XmppStanza::RESERVED_STANZA); 104 4208582 : stats_[type].first++; 105 4208583 : stats_[type].second += bytes; 106 4208581 : } 107 : 108 6597 : boost::system::error_code XmppSession::EnableTcpKeepalive(int hold_time) { 109 6597 : char *keepalive_time_str = getenv("TCP_KEEPALIVE_SECONDS"); 110 6597 : 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 6597 : if (hold_time <= 9) { 117 0 : hold_time = 9; // min hold-time in secs. 118 : } 119 6597 : hold_time = ((hold_time > 18)? hold_time/2 : hold_time); 120 6597 : keepalive_idle_time_ = hold_time/3; 121 6597 : keepalive_interval_ = 122 6597 : ((hold_time - keepalive_idle_time_)/keepalive_probes_); 123 6597 : tcp_user_timeout_ = (hold_time * 1000); // msec 124 : 125 6597 : return (SetSocketKeepaliveOptions(keepalive_idle_time_, 126 : keepalive_interval_, 127 : keepalive_probes_, 128 6597 : tcp_user_timeout_)); 129 : } 130 : 131 1725607 : regex XmppSession::tag_to_pattern(const char *tag) { 132 1725607 : std::string token("</"); 133 1725600 : token += ++tag; 134 1725598 : token += "[\\s\\t\\r\\n]*>"; 135 : 136 3451208 : regex exp(token.c_str()); 137 3451213 : return exp; 138 1725606 : } 139 : 140 1195106 : void XmppSession::SetBuf(const std::string &str) { 141 1195106 : if (buf_.empty()) { 142 1142474 : ReplaceBuf(str); 143 : } else { 144 52636 : int pos = offset_ - buf_.begin(); 145 52636 : buf_ += str; 146 52636 : offset_ = buf_.begin() + pos; 147 : } 148 1195112 : } 149 : 150 4356632 : void XmppSession::ReplaceBuf(const std::string &str) { 151 4356632 : buf_ = str; 152 4356619 : buf_.reserve(kMaxMessageSize+8); 153 4356625 : offset_ = buf_.begin(); 154 4356619 : } 155 : 156 4208601 : bool XmppSession::LeftOver() const { 157 4208601 : if (buf_.empty()) 158 0 : return false; 159 4208607 : 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 3436165 : int XmppSession::MatchRegex(const regex &patt) { 165 : 166 3436165 : std::string::const_iterator end = buf_.end(); 167 : 168 3436154 : if (regex_search(offset_, end, res_, patt, 169 3436134 : boost::match_default | boost::match_partial) == 0) { 170 46652 : return -1; 171 : } 172 3389482 : if(res_[0].matched == false) { 173 : // partial match 174 5997 : offset_ = res_[0].first; 175 5997 : return 1; 176 : } else { 177 3383480 : begin_tag_ = string(res_[0].first, res_[0].second); 178 3383476 : offset_ = res_[0].second; 179 3383468 : return 0; 180 : } 181 : } 182 : 183 4409223 : bool XmppSession::Match(Buffer buffer, int *result, bool NewBuf) { 184 4409223 : const XmppConnection *connection = this->Connection(); 185 : 186 4409214 : if (connection == NULL) { 187 0 : return true; 188 : } 189 : 190 4409214 : xmsm::XmState state = connection->GetStateMcState(); 191 : xmsm::XmOpenConfirmState oc_state = 192 4409204 : connection->GetStateMcOpenConfirmState(); 193 : 194 4409197 : if (NewBuf) { 195 1195054 : const uint8_t *cp = BufferData(buffer); 196 : // TODO Avoid this copy 197 1195051 : std::string str(cp, cp + BufferSize(buffer)); 198 1195107 : XmppSession::SetBuf(str); 199 1195109 : } 200 : 201 4409254 : int m = -1; 202 4409254 : *result = 0; 203 : do { 204 6101011 : if (!tag_known_) { 205 : // check for whitespaces 206 4356775 : size_t pos = buf_.find_first_not_of(sXMPP_VALIDWS); 207 4356774 : if (pos != 0) { 208 2664865 : if (pos == string::npos) pos = buf_.size(); 209 2664864 : offset_ = buf_.begin() + pos; 210 2664879 : return false; 211 : } 212 : } 213 : 214 3436145 : if (state == xmsm::ACTIVE || state == xmsm::IDLE) { 215 13058 : m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_); 216 3423087 : } else if (state == xmsm::CONNECT || state == xmsm::OPENSENT) { 217 : // Note, these are client only states 218 14851 : if (!stream_open_matched_) { 219 12570 : m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_); 220 12570 : if ((m == 0) && (tag_known_)) { 221 6285 : stream_open_matched_ = true; 222 : } 223 : } else { 224 2281 : m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()): 225 : stream_features_patt_); 226 : } 227 3408236 : } else if ((state == xmsm::OPENCONFIRM) && !(IsSslDisabled())) { 228 12266 : if (connection->IsClient()) { 229 6448 : 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 3538 : } else if (oc_state == xmsm::OPENCONFIRM_FEATURE_SUCCESS) { 237 2910 : m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_); 238 : } else { 239 628 : 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 2909 : m = MatchRegex(tag_known_ ? stream_res_end_:stream_patt_); 245 : } else { 246 2909 : m = MatchRegex(tag_known_ ? end_patt_:starttls_patt_); 247 2910 : if ((m == 0) && (tag_known_)) { 248 1455 : SetSslHandShakeInProgress(true); 249 : } 250 : } 251 : } 252 3395970 : } else if (state == xmsm::OPENCONFIRM || state == xmsm::ESTABLISHED) { 253 3395976 : m = MatchRegex(tag_known_ ? tag_to_pattern(begin_tag_.c_str()):patt_); 254 : } 255 : 256 3436151 : if (m == 0) { // full match 257 3383506 : *result = 0; 258 3383506 : tag_known_ ^= 1; 259 3383506 : if (!tag_known_) { 260 : // Found well formed xml 261 1691749 : return false; 262 : } 263 52645 : } else if (m == -1) { // no match 264 46650 : return true; 265 : } else { 266 5995 : return true; // partial. read more 267 : } 268 1691757 : } 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 1195153 : void XmppSession::OnRead(Buffer buffer) { 277 1195153 : if (this->Connection() == NULL || !connection_) { 278 : // Connection is deleted. Session is being deleted as well 279 : // Drop the packet. 280 76 : ReleaseBuffer(buffer); 281 76 : return; 282 : } 283 : 284 1195074 : 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 1195075 : int result = 0; 293 1195075 : bool more = Match(buffer, &result, true); 294 : do { 295 4409265 : if (more == false) { 296 4356620 : if (result < 0) { 297 : // TODO generate error, close connection. 298 148021 : break; 299 : } 300 : 301 : // We got good match. Process the message 302 4356620 : std::string::const_iterator st = buf_.begin(); 303 4356610 : std::string xml = string(st, offset_); 304 : // Ensure we have not reached the end 305 4356594 : if (buf_.begin() == offset_) { // xml.size() == 0 306 148025 : buf_.clear(); 307 148025 : break; 308 : } 309 : 310 4208574 : connection_->ReceiveMsg(this, xml); 311 : 312 4356631 : } 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 52645 : break; 317 : } 318 : 319 4208608 : if (LeftOver()) { 320 3214156 : std::string::const_iterator st = buf_.end(); 321 3214155 : ReplaceBuf(string(offset_, st)); 322 3214155 : more = Match(buffer, &result, false); 323 : } else { 324 : // No more data in the Buffer 325 994445 : buf_.clear(); 326 994450 : break; 327 : } 328 3214153 : } while (true); 329 : 330 1195116 : ReleaseBuffer(buffer); 331 1195119 : return; 332 : }