904 lines
19 KiB
C++
904 lines
19 KiB
C++
/*
|
|
* OpenBTS provides an open source alternative to legacy telco protocols and
|
|
* traditionally complex, proprietary hardware systems.
|
|
*
|
|
* Copyright 2008 Free Software Foundation, Inc.
|
|
* Copyright 2014 Range Networks, Inc.
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero General
|
|
* Public License version 3. See the COPYING and NOTICE files in the main
|
|
* directory for licensing information.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
*/
|
|
|
|
/*
|
|
As a simplification, we are supporting only the default 7-bit alphabet.
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef SMS_MESSAGE_H
|
|
#define SMS_MESSAGE_H
|
|
|
|
#include <stdio.h>
|
|
#include "SMSTransfer.h"
|
|
#include <GSML3Message.h>
|
|
#include <GSML3CCElements.h>
|
|
#include <GSML3MMElements.h>
|
|
|
|
|
|
namespace SMS {
|
|
|
|
|
|
class SMSReadError : public GSM::GSMError {
|
|
public:
|
|
SMSReadError():GSMError() {}
|
|
};
|
|
#define SMS_READ_ERROR {throw SMSReadError();}
|
|
|
|
|
|
|
|
|
|
/**@name SMS Transport Layer (TL) */
|
|
//@{
|
|
|
|
// FIXME -- All parsers for TL messages and elements should return a success/fail indication.
|
|
|
|
/**@name Elements for SMS Transport Layer (TL) */
|
|
//@{
|
|
|
|
/** A base class for elements of GSM 03.40 9.1.2 and 9.2.3 */
|
|
class TLElement {
|
|
public:
|
|
virtual ~TLElement() {}
|
|
virtual size_t length() const =0;
|
|
virtual void parse(const TLFrame&, size_t&) =0;
|
|
virtual void write(TLFrame&, size_t&) const =0;
|
|
virtual void text(std::ostream&) const {}
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const TLElement& msg);
|
|
|
|
/**
|
|
GSM 03.40 9.1.2.5
|
|
This is very similar to a Q.931-style BCD number.
|
|
Especially since we don't support non-numeric addresses.
|
|
*/
|
|
class TLAddress : public TLElement {
|
|
|
|
private:
|
|
|
|
GSM::TypeOfNumber mType;
|
|
GSM::NumberingPlan mPlan;
|
|
GSM::L3BCDDigits mDigits;
|
|
|
|
public:
|
|
|
|
TLAddress():TLElement() {}
|
|
|
|
TLAddress(GSM::TypeOfNumber wType, GSM::NumberingPlan wPlan, const char* wDigits)
|
|
:TLElement(),
|
|
mType(wType),mPlan(wPlan),mDigits(wDigits)
|
|
{ }
|
|
|
|
TLAddress(const char* wDigits)
|
|
:TLElement(),
|
|
mType(GSM::NationalNumber),mPlan(GSM::E164Plan),mDigits(wDigits)
|
|
{ }
|
|
|
|
const char *digits() const { return mDigits.digits(); }
|
|
GSM::TypeOfNumber type() const { return mType; }
|
|
GSM::NumberingPlan plan() const { return mPlan; }
|
|
|
|
size_t length() const { return 2 + mDigits.lengthV(); }
|
|
void parse(const TLFrame&, size_t&);
|
|
void write(TLFrame&, size_t&) const;
|
|
void text(std::ostream&) const;
|
|
};
|
|
|
|
|
|
|
|
/** GSM 03.40 9.2.3.12 */
|
|
class TLValidityPeriod : public TLElement {
|
|
|
|
private:
|
|
|
|
unsigned mVPF;
|
|
Timeval mExpiration;
|
|
|
|
public:
|
|
|
|
|
|
/** Default validity period of one week, no format specified. */
|
|
TLValidityPeriod(unsigned wVPF=0xFF)
|
|
:TLElement(),
|
|
mVPF(wVPF),
|
|
mExpiration(7*24*60*60*1000)
|
|
{ }
|
|
|
|
void VPF(unsigned wVPF) { mVPF=wVPF; }
|
|
|
|
size_t length() const;
|
|
void parse(const TLFrame&, size_t&);
|
|
void write(TLFrame&, size_t&) const;
|
|
void text(std::ostream&) const;
|
|
};
|
|
|
|
|
|
|
|
class TLTimestamp : public TLElement {
|
|
|
|
private:
|
|
|
|
GSM::L3TimeZoneAndTime mTime;
|
|
|
|
public:
|
|
|
|
const Timeval& time() const { return mTime.time(); }
|
|
void time(const Timeval& wTime) { mTime.time(wTime); }
|
|
|
|
size_t length() const { return mTime.lengthV(); }
|
|
void write(TLFrame& dest, size_t& wp) const { mTime.writeV((GSM::L3Frame&)(BitVector&)dest, wp); }
|
|
void parse(const TLFrame& src, size_t& rp) { mTime.parseV((GSM::L3Frame&)(BitVector&)src, rp); }
|
|
};
|
|
|
|
|
|
|
|
/** GSM 03.40 9.2.3.24 */
|
|
class TLUserData : public TLElement {
|
|
|
|
private:
|
|
|
|
unsigned mDCS; ///< data coding scheme
|
|
bool mUDHI; ///< header indicator
|
|
unsigned mLength; ///< TP-User-Data-Length, see GSM 03.40 Fig. 9.2.3.24(a),
|
|
///< GSM 03.40 Fig. 9.2.3.24(b) and GSM 03.40 9.2.3.16.
|
|
BitVector mRawData; ///< raw packed data
|
|
|
|
public:
|
|
|
|
/** Initialize the DCS with a non-valid value. */
|
|
TLUserData(unsigned wDCS=0x100, bool wUDHI=false)
|
|
:TLElement(),
|
|
mDCS(wDCS),
|
|
mUDHI(wUDHI),
|
|
mLength(0)
|
|
{
|
|
}
|
|
|
|
/** Initialize from a raw encoded data. */
|
|
TLUserData(unsigned wDCS, const BitVector wRawData, unsigned wLength,
|
|
bool wUDHI=false)
|
|
:TLElement(),
|
|
mDCS(wDCS),
|
|
mUDHI(wUDHI),
|
|
mLength(wLength)
|
|
{
|
|
mRawData.clone(wRawData);
|
|
}
|
|
|
|
/** Initialize from a simple C string. */
|
|
TLUserData(const char* text, GSM::GSMAlphabet alphabet=GSM::ALPHABET_7BIT, bool wUDHI=false)
|
|
:TLElement(),
|
|
mDCS(0),
|
|
mUDHI(wUDHI),
|
|
mLength(0)
|
|
{
|
|
switch(alphabet) {
|
|
case GSM::ALPHABET_7BIT:
|
|
encode7bit(text);
|
|
break;
|
|
case GSM::ALPHABET_8BIT:
|
|
case GSM::ALPHABET_UCS2:
|
|
default:
|
|
//LOG(WARNING) << "Unsupported alphabet: " << alphabet;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DCS(unsigned wDCS) { mDCS=wDCS; }
|
|
unsigned DCS() const { return mDCS; }
|
|
void UDHI(unsigned wUDHI) { mUDHI=wUDHI; }
|
|
unsigned UDHI() const { return mUDHI; }
|
|
/** Encode text into this element, using 7-bit alphabet */
|
|
void encode7bit(const char *text);
|
|
/** Decode text from this element, using 7-bit alphabet */
|
|
std::string decode() const;
|
|
|
|
/** This length includes a byte for the length field. */
|
|
size_t length() const;
|
|
|
|
/** Parse, including the initial length byte. */
|
|
void parse(const TLFrame&, size_t&);
|
|
|
|
void write(TLFrame&, size_t&) const;
|
|
void text(std::ostream&) const;
|
|
};
|
|
|
|
|
|
|
|
//@} // SMS TL Elements
|
|
|
|
/**@name Messages for SMS Transport Layer (TL) */
|
|
//@{
|
|
|
|
/** GSM 03.40 9.2 */
|
|
class TLMessage {
|
|
|
|
protected:
|
|
|
|
/**@name Standard TLheader bits from GSM 03.40 9.2.3.
|
|
- 0 MTI (9.2.3.1)
|
|
- 1 MTI
|
|
- 2 MMS (9.2.3.2), RD (9.2.3.25)
|
|
- 3 VPF (9.2.3.3)
|
|
- 4 VPF
|
|
- 5 SRI (9.2.3.4), SRR (9.2.3.5), SRQ (9.2.3.26)
|
|
- 6 UDHI (9.2.3.23)
|
|
- 7 RP (9.2.3.17)
|
|
*/
|
|
//@{
|
|
bool mMMS; ///< more messages to send
|
|
bool mRD; ///< reject duplicates
|
|
unsigned mVPF; ///< validity period format
|
|
bool mSRR; ///< status report request
|
|
bool mSRI; ///< status report indication
|
|
bool mSRQ; ///< status report qualifier
|
|
bool mUDHI; ///< user-data header-indicator
|
|
bool mRP; ///< reply path
|
|
//@}
|
|
|
|
public:
|
|
|
|
/** Maximum size of user data field. */
|
|
static const unsigned maxData = 160;
|
|
|
|
/** GSM 03.40 9.2.3.1 */
|
|
enum MessageType {
|
|
DELIVER = 0x0, // SC -> MS
|
|
DELIVER_REPORT = 0x0, // MS -> SC
|
|
STATUS_REPORT = 0x2, // SC -> MS
|
|
COMMAND = 0x02, // MS -> SC
|
|
SUBMIT = 0x1, // MS -> SC
|
|
SUBMIT_REPORT = 0x1 // SC -> MS
|
|
};
|
|
|
|
TLMessage()
|
|
:mMMS(false),mSRI(false),mRP(false)
|
|
{}
|
|
|
|
virtual ~TLMessage(){}
|
|
|
|
virtual int MTI() const=0;
|
|
|
|
/** The bodtLength is everything beyond the header byte. */
|
|
virtual size_t l2BodyLength() const = 0;
|
|
|
|
virtual size_t length() const { return 1+l2BodyLength(); }
|
|
|
|
size_t bitsNeeded() const { return length()*8; }
|
|
|
|
virtual void parse( const TLFrame& frame );
|
|
virtual void parseBody( const TLFrame& frame, size_t &rp) =0;
|
|
|
|
virtual void write( TLFrame& frame ) const;
|
|
virtual void writeBody( TLFrame& frame, size_t &rp) const =0;
|
|
|
|
virtual void text(std::ostream& os) const
|
|
{ os << MTI(); }
|
|
|
|
protected:
|
|
|
|
/**@name Readers and writers for standard header bits. */
|
|
//@{
|
|
// Note that offset is reversed, i'=7-i.
|
|
void writeMTI(TLFrame& fm) const { fm.fillField(6,MTI(),2); }
|
|
void writeMMS(TLFrame& fm) const { fm[5]=mMMS; }
|
|
void parseMMS(const TLFrame& fm) { mMMS=fm[5]; }
|
|
void writeRD(TLFrame& fm) const { fm[5]=mRD; }
|
|
void parseRD(const TLFrame& fm) { mRD=fm[5]; }
|
|
void writeVPF(TLFrame& fm) const { fm.fillField(3,mVPF,2); }
|
|
void parseVPF(const TLFrame& fm) { mVPF = fm.peekField(3,2); }
|
|
void writeSRR(TLFrame& fm) const { fm[2]=mSRR; }
|
|
void parseSRR(const TLFrame& fm) { mSRR=fm[2]; }
|
|
void writeSRI(TLFrame& fm) const { fm[2]=mSRI; }
|
|
void parseSRI(const TLFrame& fm) { mSRI=fm[2]; }
|
|
void writeSRQ(TLFrame& fm) const { fm[2]=mSRQ; }
|
|
void parseSRQ(const TLFrame& fm) { mSRQ=fm[2]; }
|
|
void writeUDHI(TLFrame& fm, bool udhi) const { fm[1]=udhi; }
|
|
bool parseUDHI(const TLFrame& fm) { return fm[1]; }
|
|
void writeRP(TLFrame& fm) const { fm[0]=mRP; }
|
|
void parseRP(const TLFrame& fm) { mRP=fm[0]; }
|
|
void writeUnused(TLFrame& fm) const { fm.fill(0,3,2); } ///< Fill unused bits with 0s
|
|
//@}
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, TLMessage::MessageType val);
|
|
std::ostream& operator<<(std::ostream& os, const TLMessage& msg);
|
|
|
|
|
|
|
|
/** GSM 03.40 9.2.2.2, uplink */
|
|
class TLSubmit : public TLMessage {
|
|
|
|
private:
|
|
|
|
unsigned mMR; ///< message reference
|
|
TLAddress mDA; ///< destination address
|
|
unsigned mPI; ///< protocol identifier
|
|
unsigned mDCS; ///< data coding scheme
|
|
TLValidityPeriod mVP; ///< validity period
|
|
TLUserData mUD; ///< user data
|
|
|
|
public:
|
|
|
|
int MTI() const { return SUBMIT; }
|
|
|
|
const TLAddress& DA() const { return mDA; }
|
|
const TLUserData& UD() const { return mUD; }
|
|
|
|
size_t l2BodyLength() const;
|
|
void writeBody(TLFrame&, size_t&) const { assert(0); }
|
|
void parseBody(const TLFrame&, size_t&);
|
|
virtual void text(std::ostream&) const;
|
|
};
|
|
|
|
|
|
|
|
/** GMS 03.40 9.2.2.2a, downlink */
|
|
class TLSubmitReport : public TLMessage
|
|
{
|
|
|
|
private:
|
|
// We are leaving out the optional fields.
|
|
unsigned mFC; ///< failure cause
|
|
unsigned mPI; ///< parameter indicator
|
|
TLTimestamp mSCTS; ///< service center timestamp
|
|
|
|
public:
|
|
|
|
size_t l2BodyLength() const { return 1 + 1 + 7; }
|
|
void writeBody(TLFrame& frame, size_t& wp ) const;
|
|
void parseBody(const TLFrame&, size_t&) { assert(0); }
|
|
virtual void text( std::ostream& os ) const;
|
|
};
|
|
|
|
|
|
/** GSM 03.40 9.2.2.1, downlink */
|
|
class TLDeliver : public TLMessage {
|
|
|
|
private:
|
|
|
|
TLAddress mOA; ///< origination address, GSM 03.40 9.3.2.7
|
|
unsigned mPID; ///< TL-PID, GSM 03.40 9.2.3.9
|
|
// DCS is taken from mUD.
|
|
TLTimestamp mSCTS; ///< service center timestamp, GSM 03.40 9.2.3.11
|
|
TLUserData mUD; ///< user data
|
|
|
|
public:
|
|
|
|
TLDeliver(const TLAddress& wOA, const TLUserData& wUD, unsigned wPID=0)
|
|
:TLMessage(),
|
|
mOA(wOA),mPID(wPID),mUD(wUD)
|
|
{ }
|
|
|
|
TLDeliver(const TLUserData& wUD)
|
|
:TLMessage(),
|
|
mUD(wUD)
|
|
{}
|
|
|
|
int MTI() const { return DELIVER; }
|
|
|
|
size_t l2BodyLength() const;
|
|
void writeBody( TLFrame& frame, size_t& wp ) const;
|
|
void parseBody(const TLFrame&, size_t&) { assert(0); }
|
|
virtual void text( std::ostream& os ) const;
|
|
};
|
|
|
|
|
|
TLMessage * parseTL( const TLFrame& frame );
|
|
|
|
|
|
//@} // SMS TL Messages
|
|
|
|
|
|
//@} // SMS TL
|
|
|
|
|
|
|
|
/**@name Elements and Messages for SMS RP (RL Layer) */
|
|
//@{
|
|
|
|
/**@name Elements for SMS RP (RL Layer) */
|
|
//@{
|
|
|
|
/** A common class for RP addresses, GSM 04.11 8.2.5 */
|
|
class RPAddress : public GSM::L3CalledPartyBCDNumber {
|
|
|
|
public:
|
|
|
|
RPAddress():L3CalledPartyBCDNumber() {}
|
|
|
|
RPAddress(const char* wDigits)
|
|
:L3CalledPartyBCDNumber(wDigits)
|
|
{}
|
|
|
|
RPAddress(const GSM::L3CallingPartyBCDNumber& other)
|
|
:L3CalledPartyBCDNumber(other)
|
|
{}
|
|
|
|
};
|
|
|
|
/** GSM 04.11 8.2.5.3 */
|
|
class RPUserData : public GSM::L3ProtocolElement {
|
|
|
|
private:
|
|
|
|
// The BitVector is a placeholder for a higher-layer object.
|
|
TLFrame mTPDU;
|
|
|
|
public:
|
|
|
|
RPUserData()
|
|
:L3ProtocolElement(),
|
|
mTPDU()
|
|
{}
|
|
|
|
RPUserData(const TLFrame& wTPDU)
|
|
:L3ProtocolElement(),
|
|
mTPDU(wTPDU)
|
|
{}
|
|
|
|
RPUserData(const TLMessage& TLM)
|
|
:L3ProtocolElement()
|
|
{
|
|
TLM.write(mTPDU);
|
|
}
|
|
|
|
|
|
const TLFrame& TPDU() const { return mTPDU; }
|
|
|
|
size_t lengthV() const
|
|
{
|
|
size_t len = mTPDU.size()/8;
|
|
if (mTPDU.size()%8) len++;
|
|
return len;
|
|
}
|
|
|
|
void writeV(GSM::L3Frame& dest, size_t &wp) const;
|
|
void parseV(const GSM::L3Frame& src, size_t &rp) { assert(0); }
|
|
void parseV(const GSM::L3Frame& src, size_t &rp, size_t expectedLength);
|
|
|
|
void text(std::ostream& os) const { mTPDU.hex(os); }
|
|
};
|
|
|
|
|
|
/**
|
|
GSM 04.11 8.2.5.4.
|
|
We ignore the diagnostics field.
|
|
*/
|
|
class RPCause : public GSM::L3ProtocolElement {
|
|
|
|
|
|
private:
|
|
|
|
unsigned mValue;
|
|
|
|
public:
|
|
|
|
RPCause(unsigned wValue)
|
|
:L3ProtocolElement(),
|
|
mValue(wValue)
|
|
{}
|
|
|
|
size_t lengthV() const { return 1; }
|
|
|
|
|
|
void writeV(GSM::L3Frame& dest, size_t &wp) const
|
|
{ dest.writeField(wp,mValue,8); }
|
|
|
|
void parseV(const GSM::L3Frame& src, size_t &rp)
|
|
{ mValue = src.readField(rp,8); }
|
|
|
|
void parseV(const GSM::L3Frame& src, size_t &rp, size_t expectedLength)
|
|
{ mValue = src.peekField(rp,8); rp += 8*expectedLength; }
|
|
|
|
void text(std::ostream& os) const
|
|
{ os << std::hex << "0x" << mValue << std::dec; }
|
|
};
|
|
|
|
//@}
|
|
|
|
|
|
|
|
|
|
|
|
/**@name Messages for SMS RP (RL Layer) */
|
|
//@{
|
|
|
|
/** The L4 RP message, GSM 04.11 7.3, 8.2. */
|
|
class RPMessage {
|
|
|
|
public:
|
|
|
|
unsigned mReference;
|
|
|
|
/** Table 8.3 GSM 04.11, add 1 for downlink */
|
|
enum MessageType {
|
|
Data=0x0,
|
|
Ack=0x2,
|
|
Error=0x4,
|
|
SMMA=0x6
|
|
};
|
|
|
|
RPMessage(unsigned wReference=0)
|
|
:mReference(wReference)
|
|
{}
|
|
|
|
virtual ~RPMessage(){}
|
|
|
|
unsigned reference() const { return mReference; }
|
|
|
|
virtual int MTI() const=0;
|
|
|
|
virtual size_t l2BodyLength() const = 0;
|
|
|
|
size_t length() const { return l2BodyLength()+2; }
|
|
|
|
size_t bitsNeeded() const { return length()*8; }
|
|
|
|
void parse( const RLFrame& frame );
|
|
|
|
virtual void parseBody(const RLFrame& frame, size_t &rp) =0;
|
|
|
|
/** For the network side, the write method adds 1 to the MTI. */
|
|
void write( RLFrame& frame ) const;
|
|
|
|
virtual void writeBody(RLFrame& frame, size_t &wp) const =0;
|
|
|
|
virtual void text(std::ostream& os) const;
|
|
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream&, RPMessage::MessageType);
|
|
std::ostream& operator<<(std::ostream& os, const RPMessage& msg);
|
|
|
|
|
|
|
|
|
|
/** GSM 04.11 7.3.1 */
|
|
class RPData : public RPMessage {
|
|
|
|
private:
|
|
|
|
RPAddress mOriginator; ///< originating SMSC
|
|
RPAddress mDestination; ///< destination SMSC
|
|
RPUserData mUserData; ///< payload
|
|
|
|
public:
|
|
|
|
RPData():RPMessage() {}
|
|
|
|
/** This is a constructor for the downlink version of the message. */
|
|
RPData(unsigned ref, const RPAddress& wOriginator, const TLMessage& TLM)
|
|
:RPMessage(ref),
|
|
mOriginator(wOriginator),mUserData(TLM)
|
|
{}
|
|
|
|
const TLFrame& TPDU() const { return mUserData.TPDU(); }
|
|
|
|
int MTI() const { return Data; }
|
|
void parseBody( const RLFrame& frame, size_t &rp);
|
|
void writeBody( RLFrame & frame, size_t &wp ) const;
|
|
|
|
size_t l2BodyLength() const
|
|
{ return mOriginator.lengthLV() + mDestination.lengthLV() + mUserData.lengthLV(); }
|
|
|
|
void text(std::ostream&) const;
|
|
};
|
|
|
|
|
|
/** GSM 04.11 7.3.2 */
|
|
class RPSMMA : public RPMessage {
|
|
|
|
public:
|
|
|
|
int MTI() const { return SMMA; }
|
|
//void parseBody( const RLFrame& frame, size_t &rp);
|
|
//void writeBody( RLFrame & frame, size_t &wp ) const;
|
|
|
|
size_t l2BodyLength() const { return 0; }
|
|
};
|
|
|
|
|
|
/** GSM 04.11 7.3.3 */
|
|
class RPAck : public RPMessage {
|
|
|
|
// We're ignoring the user data field.
|
|
|
|
public:
|
|
|
|
RPAck(unsigned mReference)
|
|
:RPMessage(mReference)
|
|
{}
|
|
|
|
int MTI() const { return Ack; }
|
|
|
|
void writeBody(RLFrame& frame, size_t &wp) const {}
|
|
void parseBody( const RLFrame& frame, size_t &rp) {}
|
|
|
|
size_t l2BodyLength() const { return 0; }
|
|
};
|
|
|
|
|
|
/** GSM 04.11 7.3.4 */
|
|
class RPError : public RPMessage {
|
|
|
|
private:
|
|
|
|
// Ignore user data for now.
|
|
RPCause mCause;
|
|
|
|
public:
|
|
|
|
RPError(const RPCause& wCause, unsigned mReference)
|
|
:RPMessage(mReference),
|
|
mCause(wCause)
|
|
{}
|
|
|
|
int MTI() const { return Error; }
|
|
|
|
void writeBody(RLFrame& frame, size_t &wp) const;
|
|
void parseBody(const RLFrame& frame, size_t &rp);
|
|
|
|
size_t l2BodyLength() const { return mCause.lengthLV(); }
|
|
|
|
void text(std::ostream&) const;
|
|
};
|
|
|
|
|
|
//@}
|
|
|
|
//@} // SMS RL
|
|
|
|
|
|
|
|
|
|
|
|
/**@name Elements for SMS CP (CM Layer) */
|
|
//@{
|
|
|
|
/**@name Elements for SMS CP (CM Layer) */
|
|
//@{
|
|
|
|
/** GSM 04.11 8.1.4.2 */
|
|
class CPCause : public GSM::L3ProtocolElement {
|
|
|
|
private:
|
|
|
|
unsigned mValue;
|
|
|
|
public:
|
|
|
|
CPCause(unsigned wValue)
|
|
:L3ProtocolElement(),
|
|
mValue(wValue)
|
|
{}
|
|
|
|
size_t lengthV() const { return 1; }
|
|
|
|
void writeV(GSM::L3Frame& dest, size_t &wp) const
|
|
{ dest.writeField(wp,mValue,8); }
|
|
|
|
void parseV(const GSM::L3Frame& src, size_t &rp, size_t) { assert(0); }
|
|
|
|
void parseV(const GSM::L3Frame& src, size_t &rp)
|
|
{ mValue = src.readField(rp,8); }
|
|
|
|
void text(std::ostream& os) const
|
|
{ os << std::hex << "0x" << mValue << std::dec; }
|
|
};
|
|
|
|
|
|
/** GSM 04.11 8.1.4.1 */
|
|
class CPUserData : public GSM::L3ProtocolElement {
|
|
|
|
private:
|
|
|
|
RLFrame mRPDU; ///< relay-layer protocol data unit
|
|
|
|
public:
|
|
|
|
CPUserData()
|
|
:L3ProtocolElement()
|
|
{}
|
|
|
|
CPUserData(const RPMessage& RPM)
|
|
:L3ProtocolElement(),
|
|
mRPDU(RPM.bitsNeeded())
|
|
{
|
|
RPM.write(mRPDU);
|
|
}
|
|
|
|
const RLFrame& RPDU() const { return mRPDU; }
|
|
|
|
size_t lengthV() const { return mRPDU.size()/8; }
|
|
void writeV(GSM::L3Frame& dest, size_t &wp) const;
|
|
void parseV(const GSM::L3Frame& src, size_t &rp, size_t expectedLength);
|
|
void parseV(const GSM::L3Frame& src, size_t &rp) { assert(0); }
|
|
void text(std::ostream& os) const { mRPDU.hex(os); }
|
|
};
|
|
|
|
//@} // CP Elements
|
|
|
|
|
|
|
|
/**@name Messages for SMS CP (SMS CM layer) */
|
|
//@{
|
|
|
|
/**
|
|
A virtual class for SMS CM-layer messages.
|
|
See GSM 04.11 7.
|
|
This is probably nearly the same as GSM::L3CCMessage,
|
|
but with different message types.
|
|
*/
|
|
|
|
class CPMessage : public GSM::L3Message
|
|
{
|
|
private:
|
|
|
|
/**@name Header information ref. Figure 8. GSM 04.11 */
|
|
//@{
|
|
unsigned mTI; ///< short transaction ID, GSM 04.07 11.2.3.1.3
|
|
//@}
|
|
|
|
|
|
public:
|
|
|
|
/** Message type defined in GSM 04.11 8.1.3 Table 8.1 */
|
|
enum MessageType {
|
|
DATA=0x01,
|
|
ACK=0x04,
|
|
ERROR=0x10
|
|
};
|
|
|
|
|
|
CPMessage(unsigned wTI)
|
|
:L3Message(),
|
|
mTI(wTI)
|
|
{}
|
|
|
|
size_t fullBodyLength() const { return l2BodyLength(); }
|
|
|
|
/** Override the write method to include transaction identifiers in header. */
|
|
void write(GSM::L3Frame& dest) const;
|
|
|
|
GSM::L3PD PD() const { return GSM::L3SMSPD; }
|
|
|
|
unsigned TI() const { return mTI; }
|
|
void TI(unsigned wTI){ mTI=wTI; }
|
|
|
|
void text(std::ostream&) const;
|
|
|
|
};
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, CPMessage::MessageType MTI);
|
|
|
|
/**
|
|
Parse a complete SMS L3 (CM) message.
|
|
This is the top-level SMS parser, called along side other L3 parsers.
|
|
*/
|
|
CPMessage * parseSMS( const GSM::L3Frame& frame );
|
|
|
|
/**
|
|
Parse msgtext from a hex string to RPData struct.
|
|
@param hexstring RPData encoded into hex-string.
|
|
@return Pointer to parsed RPData or NULL on error.
|
|
*/
|
|
RPData *hex2rpdata(const char *hexstring);
|
|
|
|
/**
|
|
Parse a TPDU.
|
|
Currently only SMS-SUBMIT is supported.
|
|
@param TPDU The TPDU.
|
|
@return Pointer to parsed TLMessage or NULL on error.
|
|
*/
|
|
TLMessage *parseTPDU(const TLFrame& TPDU);
|
|
|
|
/** A factory method for SMS L3 (CM) messages. */
|
|
CPMessage * CPFactory( CPMessage::MessageType MTI );
|
|
|
|
|
|
|
|
/** GSM 04.11 7.2.1 */
|
|
class CPAck : public CPMessage
|
|
{
|
|
public:
|
|
|
|
CPAck( unsigned wTI=7)
|
|
:CPMessage(wTI)
|
|
{ }
|
|
|
|
int MTI() const { return ACK; }
|
|
|
|
size_t l2BodyLength() const { return 0; }
|
|
void parseBody(const GSM::L3Frame& dest, size_t &rp) {};
|
|
void writeBody(GSM::L3Frame& dest, size_t &wp) const {};
|
|
|
|
void text(std::ostream& os) const { CPMessage::text(os); }
|
|
};
|
|
|
|
|
|
|
|
/** GSM 04.11 7.2.3 */
|
|
class CPError : public CPMessage
|
|
{
|
|
private:
|
|
|
|
CPCause mCause;
|
|
|
|
public:
|
|
|
|
CPError(unsigned wTI=7)
|
|
:CPMessage(wTI),
|
|
mCause(0x7f)
|
|
{ }
|
|
|
|
CPError(unsigned wTI, const CPCause& wCause)
|
|
:CPMessage(wTI),
|
|
mCause(wCause)
|
|
{ }
|
|
|
|
int MTI() const { return ERROR; }
|
|
size_t l2BodyLength() const { return mCause.lengthV(); }
|
|
void writeBody(GSM::L3Frame& dest, size_t &wp) const;
|
|
void parseBody(const GSM::L3Frame&, size_t&) { assert(0); }
|
|
};
|
|
|
|
|
|
|
|
/** GSM 04.11 7.2.1 */
|
|
class CPData : public CPMessage
|
|
{
|
|
private:
|
|
|
|
CPUserData mData;
|
|
|
|
public:
|
|
|
|
CPData(unsigned wTI=7)
|
|
:CPMessage(wTI)
|
|
{ }
|
|
|
|
CPData(unsigned wTI, const RPMessage& RPM)
|
|
:CPMessage(wTI),
|
|
mData(RPM)
|
|
{ }
|
|
|
|
const CPUserData& data() const { return mData; }
|
|
const RLFrame& RPDU() const { return mData.RPDU(); }
|
|
|
|
int MTI() const { return DATA; }
|
|
size_t l2BodyLength() const { return mData.lengthLV(); }
|
|
void parseBody(const GSM::L3Frame& dest, size_t &rp);
|
|
void writeBody(GSM::L3Frame& dest, size_t &wp) const;
|
|
void text(std::ostream&) const;
|
|
};
|
|
|
|
|
|
//@} // CP messages
|
|
|
|
//@} // SMS CP
|
|
|
|
|
|
|
|
}; // namespace SMS
|
|
|
|
#endif
|
|
|
|
// vim: ts=4 sw=4
|