/* * Copyright 2013, 2014 Range Networks, Inc. * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ // Written by Pat Thompson. #define LOG_GROUP LogGroup::SIP // Can set Log.Level.SIP for debugging #include "SIPUtility.h" #include "SIPBase.h" #include "SIP2Interface.h" #include "SIPMessage.h" #include "SIPTransaction.h" #include "SIPExport.h" namespace SIP { static const string cINVITEstr("INVITE"); static const string cREGISTERstr("REGISTER"); // Note: Most of the documentation is in SIPMessage.cpp // TERMS: // URI = Uniform Resource Identifier. 19.1: In SIP it includes sip:user:password@host:port;parameters?headers // userinfo part of a URI is: The name:password@ The userinfo is optional. // UAC = User Agent Client (request initiator) // UAS = User Agent Server (request recipient) // UA = User Agent, either client or server. // Session = the SDP session for media created in a dialog. // SIP Layers: // 1. Message syntax/encoding // 2. Transport Layer sec 18. // 3. Transaction Layer: handles matching responses to requests, retransmissions, timeouts. // (Transaction Layer is kind of a crappy name since the whole thing is called a Transaction) // 4. TU = Trasaction User. // The From and To in a response match a request, ie, in a response they are the reverse of the message direction. // Tags 8.1.1: // The from-tag identifies the client and must be in a request. // The to-tag identifies the peer. A request must not have a To-tag. To-tag is set in the response. // Requests: // Request-Line - first line in SIP request, contains: method Request-URI SIP-Version // The request-URI is sip or sips uri indicating user or service to which this request is addressed. // Responses: // Status-Line - first line in SIP response, contains: SIP-Version Status-Code Reason-Phrase void SipTransaction::_define_vtable() {} void SipTransaction::TUTimeoutV() { LOG(DEBUG); } void SipTransaction::stInitNonDialogTransaction(TranEntryId tranid, string wBranch, SipMessage *request, const IPAddressSpec *wPeer) { mstTranId = tranid; mstBranch = wBranch; stSaveRequestId(request); mstPeer = *wPeer; } void SipTransaction::stInitNonDialogTransaction(TranEntryId tranid, string wBranch, SipMessage *request, string wProxy, const char *wProxyProvenance) { mstTranId = tranid; mstBranch = wBranch; stSaveRequestId(request); mstPeer.ipSet(wProxy, wProxyProvenance); } void SipTransaction::stInitInDialogTransaction(SipDialog *wDialog, string wBranch, SipMessage *request) { mstDialog = wDialog; mstTranId = wDialog->mTranId; mstPeer = *wDialog->dsPeer(); mstBranch = wBranch; stSaveRequestId(request); } void SipTransaction::stWrite(SipMessage *sipmsg) { if (!mstPeer.mipValid) { LOG(ERR) << "Attempt to write to invalid peer address:"<dmRand = rand; dmsg->dmRejectCause = atoi(gsmRejectCode.c_str()); devassert(mstTranId); // The tran id is 0 for unregister messages. If one of those gets this far it is a bug. if (mstTranId) { SipCallbacks::ttAddMessage(mstTranId,dmsg); } } void SipTransaction::sendAuthOKMessage(SipMessage *sipmsg) { devassert(mstTranId); // The tran id is 0 for unregister messages. If one of those gets this far it is a bug. if (! mstTranId) { return; } string imsi = sipmsg->msmTo.uriUsername(); // The To: and From: have the same value for a REGISTER message. // Validate the imsi: if (imsi.empty()) { LOG(ERR) << "can't find imsi to store kc"; sendAuthFailMessage(400,"",""); return; } if (0 == strncasecmp(imsi.c_str(),"imsi",4)) { // happiness } else if (0 == strncasecmp(imsi.c_str(),"tmsi",4)) { // TODO: In future the user name could be a TMSI. LOG(ERR) << "SIP REGISTER message with TMSI not supported, userid="<msmContent; sendAuthFailMessage(400,"",""); return; } DialogAuthMessage *dmsg = new DialogAuthMessage(mstTranId, DialogState::dialogActive,200); dmsg->dmPAssociatedUri = sipmsg->msmHeaders.paramFind("P-Associated-URI"); // case doesnt matter dmsg->dmPAssertedIdentity = sipmsg->msmHeaders.paramFind("P-Asserted-Identity"); string authinfo = sipmsg->msmHeaders.paramFind("Authentication-Info"); if (! authinfo.empty()) { SipParamList params; parseToParams(authinfo, params); dmsg->dmKc = params.paramFind("cnonce"); // This is the way SR passes the Kc. } else { // There will not be a cnonce if the Registrar does not know it. Dont worry about it. //LOG(INFO) << "No Authenticate-Info header in SIP REGISTER response:"<msmContent; } if (dmsg->dmKc.empty()) { dmsg->dmKc = sipmsg->msmHeaders.paramFind("P-GSM-Kc"); // This is the way Yate passes the Kc. } WATCHF("CNONCE: imsi=%s Kc=%s\n",imsi.c_str(),dmsg->dmKc.c_str()); if (! dmsg->dmKc.empty()) { LOG(DEBUG) << "Storing Kc:"<dmKc.size()); // We dont put Kc itself in the log. //gTMSITable.putKc(imsi.c_str()+4, kc, pAssociatedUri, pAssertedIdentity); } else { LOG(NOTICE) << "No Kc in SIP REGISTER response:"<msmContent; } //NewTransactionTable_ttAddMessage(mstTranId,dmsg); SipCallbacks::ttAddMessage(mstTranId,dmsg); } // The cause is not currently used. void SipTransaction::stSetDialogState(SipState newState, int code, char timer) const { if (SipDialog *dialog = mstDialog.self()) { dialog->dialogPushState(newState,code,timer); } } // This is the default. The INVITE ones are static and live in the dialog so they are not destroyed. //void SipTransaction::stDestroyV() //{ // if (mstDialog) { // // Do what? // //mstDialog->removeTransaction(this); // } // gSipInterface.tuMapRemove(this,true); // delete this; //} void SipTransaction::stFail(int code) { stSetDialogState(SSFail,code,0); // Dont need more timer, this is invoked after timer expiry. stDestroyV(); } ostream& operator<<(ostream& os, const SipTransaction&st) { os << " SipTransaction("<smViaBranch() == mstOutRequest.smViaBranch() && msg->smCSeqMethod() == mstOutRequest.smCSeqMethod()); return false; } // 17.2.3: Matching requests to Server [inbound] Transactions: // If branch has the magic cookie z9hG4bK: // 1. branch param in request equals the top via header branch of the request that created the transaction // 2. and the sent-by value in in top via of request is equal // 3. method matches, exceptfor ACK, in which case the method of the requested that created the transaction is INVITE. // bool SipServerTrLayer::stMatchesMessageV(SipMessage *msg) // { // SipVia msgvia(msg->msmVias), requestvia(mstInRequest.msmVias); // if (msgvia.mViaBranch == requestvia.mViaBranch && msgvia.mSentBy == requestvia.mSentBy) { // if (msg->isACK()) { // return mstInRequest.msmReqMethod == "INVITE"; // } else { // return msg->msmReqMethod == mstInRequest.msmReqMethod; // } // } // return false; // } // The transactions methods we support are: // INVITE, ACK, MESSAGE, BYE, CANCEL, INFO. bool SipClientTrLayer::TLWriteHighSideV(SipMessage *sipmsg) { int code = sipmsg->smGetCode(); LOG(DEBUG) <= 300) { mstDialog->MOCSendACK(); } else { stDestroyV(); return true; } // The TU is responssible for sending the ACK, after connection setup. } if (stIsReliableTransport()) { stDestroyV(); return true; } else { mTimerDK.set(32*1000); } return true; } case stCompleted: switch ((code / 100) * 100) { case 100: case 200: return false; // suppress duplicate messages. default: if (stIsInvite()) { mstDialog->MOCSendACK(); } return false; // suppress duplicate messages. } case stTerminated: // Now what? LOG(ERR) << "SIP Message received on terminated client transaction."<isACK()); } mstOutRequest = *request; devassert(mstState == stInitializing); stWrite(request); mstState = stCallingOrTrying; } void SipClientTrLayer::sctInitRegisterClientTransaction(SipDialog *wRegistrar, TranEntryId tid, SipMessage *request, string branch) { //stInitInDialogTransaction(wDialog, branch, request); // Do this first. stInitNonDialogTransaction(tid, branch,request,wRegistrar->dsPeer()); mstOutRequest = *request; } void SipClientTrLayer::sctInitInDialogClientTransaction(SipDialog *wDialog, SipMessage *request, string branch) { stInitInDialogTransaction(wDialog, branch, request); // Do this first. mTimerBF.setOnce(64*T1); // this is init time so we could have used just set. if (!stIsReliableTransport()) { mTimerAE.set(T1); } mstOutRequest = *request; } void SipClientTrLayer::sctStart() { LOG(DEBUG); ScopedLock lock(mstLock); // Must add to the tu map before sending the message because the reply can be fast enough to cause a race. gSipInterface.tuMapAdd(this); devassert(mstState == stInitializing); stWrite(&mstOutRequest); // Send the initial message. mstState = stCallingOrTrying; } // Return TRUE to delete it. bool SipClientTrLayer::TLPeriodicServiceV() { LOG(DEBUG) << LOGVAR(mTimerDK) <| |<-------+ // | | Transport Err. // | | Inform TU // | |--------------->+ // +-----------+ | //300-699 from TU | |2xx from TU | //send response | |send response | // | +------------------>+ // | | //INVITE V Timer G fires | //send response+-----------+ send response | // +--------| |--------+ | // | | Completed | | | // +------->| |<-------+ | // +-----------+ | // | | | // ACK | | | // - | +------------------>+ // | Timer H fires | // V or Transport Err.| // +-----------+ Inform TU | // | | | // | Confirmed | | // | | | // +-----------+ | // | | // |Timer I fires | // |- | // | | // V | // +-----------+ | // | | | // | Terminated|<---------------+ // | | // +-----------+ //Figure 7: INVITE server transaction // // The request should be INVITE or ACK. // // Return TRUE if the message should be sent to the TU. // bool SipServerTrLayer::TLWriteHighSideV(SipMessage *wRequest) // { // LOG(DEBUG); // switch (mstState) { // case stTrying: // // Discard retransmissions until the TU server responds. // // For invites, if this takes longer than 200ms we are supposed to manufacture a 100 Trying response, // // but we're just going to hope that does not happen and treat it the same. // if (sameMsg(wRequest,&mstInRequest)) { // // discard duplicate. // // TODO: Arent we supposed to send 100 Trying? // return false; // } // TUWriteHighSideV(wRequest); // return true; // case stProceeding: // case stCompleted: // // The TU already sent something so resend it. // // As specified in RFC3261 on receipt of a request we are required to resend the last response, // // even if we just did so, which is a waste of resources. // // I am modifying this slightly so we do not send two duplicate responses immediately after eaach other. // if (mLastSent.elapsed() >= 100) { // stWrite(&mstResponse); // resend most recent response. // } // return false; // This is a repeat message. // case stConfirmed: // // This Confirmed state exists simply to ignore this duplicate ACK. // // TODO: print an error if it is not an ACK. // return false; // This is a repeat ACK. // case stTerminated: // // Now what? // LOG(ERR) << "SIP Message received on terminated server transaction."<isACK()) { // mstState = stConfirmed; // if (stIsReliableTransport()) { stDestroyV(); } else { mTimerI.set(T4); } // } // TUWriteHighSideV(wRequest); // return true; // } // // // The transaction was established by some incoming request, and now the TU is sending a response. // void SipInviteServerTrLayer::TLWriteLowSideV(SipMessage *wResponse) // { // LOG(DEBUG); // ScopedLock lock(mstLock); // mstResponse = *wResponse; // stWrite(&mstResponse); mLastSent.now(); // assert(stIsInvite()); // // switch (mstState) { // case stTrying: // case stProceeding: // switch ((wResponse->msmCode / 100) * 100) { // case 100: // sending provisional response. // //assert(mstState == stTrying || mstState == stProceeding); // mstState = stProceeding; // stSetDialogState(Proceeding,wResponse->msmCode); // return; // case 200: // // The spec indicates that the TL layer is terminated when 200 is sent, // // and I quote: "retransmissions of the 2xx responses are handled by the TU" // // maybe because they have SDP content that may change? // mstState = stTerminated; // // stDestroyV(); // return; // default: // //assert(mstState != stConfirmed); // mstState = stCompleted; // mTimerHJ.set(64*T1); // if (!stIsReliableTransport()) { // mTimerG.set(min(T2,mGCount * T1)); // mGCount *= 2; // } // return; // } // case stCompleted: // case stConfirmed: // case stTerminated: // TODO: Is this right? // assert(0); // } // } // // void SipNonInviteServerTrLayer::TLWriteLowSideV(SipMessage *wResponse) // { // LOG(DEBUG); // ScopedLock lock(mstLock); // mstResponse = *wResponse; // stWrite(&mstResponse); mLastSent.now(); // assert(! stIsInvite()); // switch (mstState) { // case stTrying: // case stProceeding: // switch ((wResponse->msmCode / 100) * 100) { // case 100: // sending provisional response. // mstState = stProceeding; // stSetDialogState(Proceeding,wResponse->msmCode); // return; // default: // mstState = stCompleted; // // We have to rely on the TU layer to set the dialog state. // //setDialogState(?); // if (stIsReliableTransport()) { vstDestroy(); } else { mTimerHJ.set(64*T1); } // return; // } // case stCompleted: // case stConfirmed: // case stTerminated: // assert(0); // } // } // // bool SipServerTrLayer::TLPeriodicServiceV() // { // LOG(DEBUG) << LOGVAR(mTimerHJ) <msmCode; static const char *pRejectCauseHeader = "P-GSM-Reject-Cause"; static const char *whatami = stKind == KindRegister ? "SIP Register " : "SIP UnRegister "; LOG(DEBUG) <smGetRand401(),sipmsg->msmHeaders.paramFind(pRejectCauseHeader)); } setTransactionState(stCompleted); } else if (code == 200) { // (pat) We can no longer stash Kc directly in the TMSI table because the entry is not created until the MS is validated. if (stKind == KindRegister) { sendAuthOKMessage(sipmsg); } } else switch ((code/100) * 100) { case 100: return; // we dont care. case 200: LOG(ERR) <msmHeaders.paramFind(pRejectCauseHeader)); } return; } } SipRegisterTU::SipRegisterTU(SipRegisterTU::Kind wKind, SipDialog *registrar, TranEntryId tid, SipMessage *request) { // Kinda dumb to fish out the branch from the request, but its ok. //SipDialog *registrar = getRegistrar(); //SipMessage *request = registrar->makeRegisterMsg(SIPDTRegister,chan,rand,msid,sres.c_str()); // It is in the dummy dialog established for the registrar. stKind = wKind; sctInitRegisterClientTransaction(registrar, tid, request, request->smGetBranch()); } void startRegister(TranEntryId tid, const FullMobileId &msid, const string rand, const string sres, L3LogicalChannel *chan) // msid is imsi and/or tmsi { LOG(DEBUG) <makeRegisterMsg(SIPDTRegister,chan,rand,msid,sres.c_str()); SipRegisterTU *reg = new SipRegisterTU(SipRegisterTU::KindRegister,registrar,tid,request); delete request; // sctInitRegisterTransaction made a copy. Kind of wasteful. reg->sctStart(); } void startUnregister(const FullMobileId &msid, L3LogicalChannel *chan) { LOG(DEBUG) <makeRegisterMsg(SIPDTUnregister,chan,"",msid,NULL); SipRegisterTU *reg = new SipRegisterTU(SipRegisterTU::KindUnRegister,registrar,(TranEntryId)0,request); delete request; // sctInitRegisterTransaction made a copy. Kind of wasteful. reg->sctStart(); } // =========================================================================================== void SipMOByeTU::TUWriteHighSideV(SipMessage *sipmsg) { LOG(INFO) << "SIP term info msmCode: " << sipmsg->msmCode; if (sipmsg->msmCode >= 200) { stSetDialogState(Cleared,sipmsg->msmCode,'K'); } } //void SipMOByeTU::TUTimeoutV() { stSetDialogState(Cleared,0); } SipMOByeTU::SipMOByeTU(SipDialog *wDialog, string wReasonHeader) // : SipClientTrLayer(wDialog->dsPeer(), make_branch(),wDialog) { LOG(INFO) << "SIP term info SipMOByeTU"; // SVGDBG string branch = make_branch(); SipMessage *bye = new SipMessageRequestWithinDialog(stGetMethodNameV(),wDialog,branch); bye->msmReasonHeader = wReasonHeader; sctInitInDialogClientTransaction(wDialog, bye, branch); delete bye; } void SipMOCancelTU::TUWriteHighSideV(SipMessage *sipmsg) { if (sipmsg->msmCode >= 200) { stSetDialogState(Canceled,sipmsg->msmCode,'K'); } } //void SipMOCancelTU::TUTimeoutV() { stSetDialogState(Canceled,0); } SipMOCancelTU::SipMOCancelTU(SipDialog *wDialog,string wReasonHeader) { // : SipClientTrLayer(wDialog->dsPeer(), make_branch(),wDialog LOG(INFO) << "SIP term info SipMOCancelTU"; // Mobile originate string branch = make_branch(); SipMessage *cancelMsg = new SipMessageRequestWithinDialog(this->stGetMethodNameV(),wDialog,branch); cancelMsg->msmReasonHeader = wReasonHeader; //wDialog->getTermList().copyEntireList(cancelMsg->getTermList()); // SVGDBG SipMOCancelTU this->sctInitInDialogClientTransaction(wDialog, cancelMsg, branch); // Message gets copied in here delete cancelMsg; } SipDtmfTU::SipDtmfTU(SipDialog *wDialog, unsigned wInfo) //: SipClientTrLayer(wDialog->dsPeer(), make_branch(),wDialog) { string branch = make_branch(); SipMessage *msg = new SipMessageRequestWithinDialog(stGetMethodNameV(),wDialog,branch); static const string applicationDtmf("application/dtmf-relay"); string body; switch (wInfo) { case 11: body = string("Signal=*\nDuration=200"); break; case 12: body = string("Signal=#\nDuration=200"); break; default: body = format("Signal=%i\nDuration=200",wInfo); break; } msg->smAddBody(applicationDtmf,body); sctInitInDialogClientTransaction(wDialog, msg, branch); delete msg; } void SipDtmfTU::TUWriteHighSideV(SipMessage *sipmsg) { switch ((sipmsg->msmCode*100)/100) { case 100: return; // keep going. default: sendSimpleMessage(DialogState::dialogDtmf,sipmsg->msmCode); return; } } }; // namespace SIP