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="<<imsi; sendAuthFailMessage(400,"",""); return; } else { LOG(ERR) << "SIP REGISTER message with invalid IMSI:"<<imsi<<" Message:"<<sipmsg->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:"<<sipmsg->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:"<<LOGVAR(imsi)<<LOGVAR(dmsg->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:"<<sipmsg->msmContent; } //NewTransactionTable_ttAddMessage(mstTranId,dmsg); SipCallbacks::ttAddMessage(mstTranId,dmsg); }
static L3Message* parseSACCHMessage(const L3Frame *l3frame) { if (!l3frame) return NULL; LOG(DEBUG) << *l3frame; Primitive prim = l3frame->primitive(); if ((prim!=L3_DATA) && (prim!=L3_UNIT_DATA)) { LOG(INFO) << "non-data primitive " << prim; return NULL; } L3Message* message = parseL3(*l3frame); if (!message) { LOG(WARNING) << "SACCH received unparsable L3 frame " << *l3frame; WATCHF("SACCH received unparsable L3 frame PD=%d MTI=%d",l3frame->PD(),l3frame->MTI()); } return message; }
// see: Control::MOSMSController MachineStatus MOSMSMachine::machineRunState(int state, const GSM::L3Message *l3msg, const SIP::DialogMessage *sipmsg) { // See GSM 04.11 Arrow Diagram A5 for the transaction (pat) NO, A5 is for GPRS. Closest diagram is F1. // SIP->Network message. // Step 1 MS->Network CP-DATA containing RP-DATA with message // Step 2 Network->MS CP-ACK // 4.11 6.2.2 State wait-for-RP-ACK, timer TR1M // Step 3 Network->MS CP-DATA containing RP-ACK or RP-Error // Step 4 MS->Network CP-ACK // LAPDm operation, from GSM 04.11, Annex F: // """ // Case A: Mobile originating short message transfer, no parallel call: // The mobile station side will initiate SAPI 3 establishment by a SABM command // on the DCCH after the cipher mode has been set. If no hand over occurs, the // SAPI 3 link will stay up until the last CP-ACK is received by the MSC, and // the clearing procedure is invoked. // """ WATCHF("MOSMS state=%x\n",state); PROCLOG2(DEBUG,state)<<LOGVAR(l3msg)<<LOGVAR(sipmsg)<<LOGVAR2("imsi",tran()->subscriber()); switch (state) { case L3CASE_MM(CMServiceRequest): { timerStart(TCancel,30*1000,TimerAbortTran); // Just in case. // This is both the start state and a request to start a new MO SMS when one is already in progress, as per GSM 4.11 5.4 const L3CMServiceRequest *req = dynamic_cast<typeof(req)>(l3msg); const GSM::L3MobileIdentity &mobileID = req->mobileID(); // Reference ok - the SM is going to copy it. // FIXME: We only identify this the FIRST time. // The L3IdentifySM can check the MM state and just return. // FIXME: check provisioning return machPush(new L3IdentifyMachine(tran(),mobileID, &mIdentifyResult), stateIdentResult); } case stateIdentResult: { if (! mIdentifyResult) { //const L3CMServiceReject reject = L3CMServiceReject(L3RejectCause::Invalid_Mandatory_Information); // (pat 6-2014) I think this is wrong, based on comment below, so changing it to the main channel: // l3sendSms(L3CMServiceReject(L3RejectCause::Invalid_Mandatory_Information),SAPI0); MMRejectCause rejectCause = L3RejectCause::Invalid_Mandatory_Information; channel()->l3sendm(L3CMServiceReject(rejectCause),L3_DATA,SAPI0); return MachineStatus::QuitTran(TermCause::Local(rejectCause)); } // Let the phone know we're going ahead with the transaction. // The CMServiceReject is on SAPI 0, not SAPI 3. PROCLOG(DEBUG) << "sending CMServiceAccept"; // Update 8-6-2013: The nokia does not accept this message on SACCH SAPI 0 for in-call SMS; // so I am trying moving it to the main channel. //l3sendSms(GSM::L3CMServiceAccept(),SAPI0); channel()->l3sendm(GSM::L3CMServiceAccept(),L3_DATA,SAPI0); gReports.incr("OpenBTS.GSM.SMS.MOSMS.Start"); return MachineStatusOK; } #if FIXME case L3CASE_ERROR: { // (pat) TODO: Call this on parsel3 error... // TODO: Also send an error code to the sip side, if any. l3sendSms(CPError(getL3TI())); return MachineStatusQuitTran; } #endif case L3CASE_SMS(DATA): { timerStop(TCancel); timerStart(TR1M,TR1Mms,TimerAbortTran); // Step 0: Wait for SAP3 to connect. // The first read on SAP3 is the ESTABLISH primitive. // That was done by our caller. //delete getFrameSMS(LCH,GSM::ESTABLISH); // Step 1: This is the first message: CP-DATA, containing RP-DATA. unsigned L3TI = l3msg->TI() | 0x08; tran()->setL3TI(L3TI); const CPData *cpdata = dynamic_cast<typeof(cpdata)>(l3msg); if (cpdata == NULL) { // Currently this is impossible, but maybe someone will change the code later. l3sendSms(CPError(L3TI)); return MachineStatus::QuitTran(TermCause::Local(L3Cause::SMS_Error)); } // Step 2: Respond with CP-ACK. // This just means that we got the message and could parse it. PROCLOG(DEBUG) << "sending CPAck"; l3sendSms(CPAck(L3TI)); // (pat) The SMS message has already been through L3Message:parseL3, which called SMS::parseSMS(source), which manufactured // a CPMessage::CPData and called L3Message::parse() which called CPData::parseBody which called L3Message::parseLV, // which called CPUserData::parseV to leave the result in L3Message::CPMessage::CPData::mData. // As the mathemetician said after filling 3 blackboards with formulas: It is obvious! // FIXME -- We need to set the message ref correctly, even if the parsing fails. // The compiler gives a warning here. Let it. It will remind someone to fix it. // (pat) Update: If we cant parse far enough to get the ref we send a CPError that does not need the ref. #if 0 unsigned ref; bool success = false; try { // (pat) hierarchy is L3Message::CPMessage::CPData; L3Message::parse calls CPData::parseBody. CPData data; data.parse(*CM); LOG(INFO) << "CPData " << data; // Transfer out the RPDU -> TPDU -> delivery. ref = data.RPDU().reference(); // This handler invokes higher-layer parsers, too. success = handleRPDU(transaction,data.RPDU()); } catch (SMSReadError) { LOG(WARNING) << "SMS parsing failed (above L3)"; // Cause 95, "semantically incorrect message". LCH->l3sendf(CPData(L3TI,RPError(95,ref)),3); if you ever use this, it should call l3sendSms delete CM; throw UnexpectedMessage(); } catch (GSM::L3ReadError) { LOG(WARNING) << "SMS parsing failed (in L3)"; delete CM; throw UnsupportedMessage(); } delete CM; #endif // Step 3 // Send CP-DATA containing message ref and either RP-ACK or RP-Error. // If we cant parse the message, we send RP-Error immeidately, otherwise we wait for the dialog to finish one way or the other. const RLFrame &rpdu = cpdata->data().RPDU(); this->mRpduRef = rpdu.reference(); bool success = false; try { // This creates the outgoing SipDialog to send the message. success = handleRPDU(rpdu); } catch (...) { LOG(WARNING) << "SMS parsing failed (above L3)"; } if (! success) { PROCLOG(INFO) << "sending RPError in CPData"; // Cause 95 is "semantically incorrect message" l3sendSms(CPData(L3TI,RPError(95,mRpduRef))); } mSmsState = MoSmsWaitForAck; LOG(DEBUG) << "case DATA returning"; return MachineStatusOK; } case L3CASE_SIP(dialogBye): { // SIPDialog sends this when the MESSAGE clears. PROCLOG(INFO) << "SMS peer did not respond properly to dialog message; sending RPAck in CPData"; l3sendSms(CPData(getL3TI(),RPAck(mRpduRef))); LOG(DEBUG) << "case dialogBye returning"; } case L3CASE_SIP(dialogFail): { PROCLOG(INFO) << "sending RPError in CPData"; // TODO: Map the dialog failure state to an RPError state. // Cause 127 is "internetworking error, unspecified". // See GSM 04.11 8.2.5.4 Table 8.4. l3sendSms(CPData(getL3TI(),RPError(127,mRpduRef))); LOG(DEBUG) << "case dialogFail returning"; } case L3CASE_SMS(ACK): { timerStop(TR1M); // Step 4: Get CP-ACK from the MS. const CPAck *cpack = dynamic_cast<typeof(cpack)>(l3msg); PROCLOG(INFO) << "CPAck " << cpack; gReports.incr("OpenBTS.GSM.SMS.MOSMS.Complete"); /* MOSMS RLLP request */ //if (gConfig.getBool("Control.SMS.QueryRRLP")) { // Query for RRLP //if (!sendRRLP(mobileID, LCH)) { // LOG(INFO) << "RRLP request failed"; //} //} // Done. mSmsState = MoSmsMMConnection; // TODO: if (set) set->mmCallFinished(); LOG(DEBUG) << "case ACK returning"; // This attach causes any pending MT transactions to start now. gMMLayer.mmAttachByImsi(channel(),tran()->subscriberIMSI()); return MachineStatus::QuitTran(TermCause::Local(L3Cause::SMS_Success)); } default: LOG(DEBUG) << "unexpected state"; return unexpectedState(state,l3msg); } }