Esempio n. 1
0
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);
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
// 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);
	}
}