void Control::MOSMSController(const GSM::L3CMServiceRequest *req, UMTS::DCCHLogicalChannel *LCH) { assert(req); assert(req->serviceType().type() == GSM::L3CMServiceType::ShortMessage); assert(LCH); assert(LCH->type() != UMTS::DCCHType); LOG(INFO) << "MOSMS, req " << *req; // If we got a TMSI, find the IMSI. // Note that this is a copy, not a reference. GSM::L3MobileIdentity mobileID = req->mobileID(); resolveIMSI(mobileID,LCH); // Create a transaction record. TransactionEntry *transaction = new TransactionEntry(gConfig.getStr("SIP.Proxy.SMS").c_str(),mobileID,LCH); gTransactionTable.add(transaction); LOG(DEBUG) << "MOSMS: transaction: " << *transaction; // See GSM 04.11 Arrow Diagram A5 for the transaction // Step 1 MS->Network CP-DATA containing RP-DATA // Step 2 Network->MS CP-ACK // Step 3 Network->MS CP-DATA containing RP-ACK // 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. // """ // FIXME: check provisioning // Let the phone know we're going ahead with the transaction. LOG(INFO) << "sending CMServiceAccept"; LCH->send(GSM::L3CMServiceAccept()); // Wait for SAP3 to connect. // The first read on SAP3 is the ESTABLISH primitive. delete getFrameSMS(LCH,GSM::ESTABLISH); // Step 1 // Now get the first message. // Should be CP-DATA, containing RP-DATA. GSM::L3Frame *CM = getFrameSMS(LCH); LOG(DEBUG) << "data from MS " << *CM; if (CM->MTI()!=CPMessage::DATA) { LOG(NOTICE) << "unexpected SMS CP message with TI=" << CM->MTI(); throw UnexpectedMessage(); } unsigned L3TI = CM->TI() | 0x08; transaction->L3TI(L3TI); // Step 2 // Respond with CP-ACK. // This just means that we got the message. LOG(INFO) << "sending CPAck"; LCH->send(CPAck(L3TI),3); // Parse the message in CM and process RP part. // This is where we actually parse the message and send it out. // 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. unsigned ref; bool success = false; try { CPData data; data.parse(*CM); delete 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->send(CPData(L3TI,RPError(95,ref)),3); throw UnexpectedMessage(); } catch (GSM::L3ReadError) { LOG(WARNING) << "SMS parsing failed (in L3)"; throw UnsupportedMessage(); } // Step 3 // Send CP-DATA containing RP-ACK and message reference. if (success) { LOG(INFO) << "sending RPAck in CPData"; LCH->send(CPData(L3TI,RPAck(ref)),3); } else { LOG(INFO) << "sending RPError in CPData"; // Cause 127 is "internetworking error, unspecified". // See GSM 04.11 Table 8.4. LCH->send(CPData(L3TI,RPError(127,ref)),3); } // Step 4 // Get CP-ACK from the MS. CM = getFrameSMS(LCH); if (CM->MTI()!=CPMessage::ACK) { LOG(NOTICE) << "unexpected SMS CP message with TI=" << CM->MTI(); throw UnexpectedMessage(); } LOG(DEBUG) << "ack from MS: " << *CM; CPAck ack; ack.parse(*CM); LOG(INFO) << "CPAck " << ack; // Done. LCH->send(GSM::L3ChannelRelease()); gTransactionTable.remove(transaction); LOG(INFO) << "closing the Um channel"; }