diff --git a/Makefile.in b/Makefile.in index faa47299..fb217d0f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,7 +24,8 @@ SLIBS:= $(YLIB) libyate.so \ libyatescript.so.@PACKAGE_VERSION@ libyatescript.so \ libyatesig.so.@PACKAGE_VERSION@ libyatesig.so \ libyatemgcp.so.@PACKAGE_VERSION@ libyatemgcp.so \ - libyatejabber.so.@PACKAGE_VERSION@ libyatejabber.so + libyatejabber.so.@PACKAGE_VERSION@ libyatejabber.so \ + libyategsm.so.@PACKAGE_VERSION@ libyategsm.so INCS := yateclass.h yatemime.h yatengine.h yatephone.h yatecbase.h GENS := yateversn.h LIBS := diff --git a/configure.in b/configure.in index 059ac8e3..d282191f 100644 --- a/configure.in +++ b/configure.in @@ -1614,6 +1614,7 @@ AC_CONFIG_FILES([packing/rpm/yate.spec libs/yasn/Makefile libs/ysnmp/Makefile libs/miniwebrtc/Makefile + libs/ygsm/Makefile share/Makefile share/scripts/Makefile share/skins/Makefile diff --git a/libs/ygsm/Makefile.in b/libs/ygsm/Makefile.in new file mode 100644 index 00000000..8a44b1c6 --- /dev/null +++ b/libs/ygsm/Makefile.in @@ -0,0 +1,72 @@ +# Makefile +# This file holds the make rules for the libyategsm + +DEBUG := + +CXX := @CXX@ -Wall +AR := ar +DEFS := +INCLUDES := -I@top_srcdir@ -I../.. -I@srcdir@/../yxml -I@srcdir@ +CFLAGS := @CFLAGS@ @MODULE_CPPFLAGS@ @INLINE_FLAGS@ +LDFLAGS:= @LDFLAGS@ +SONAME_OPT := @SONAME_OPT@ +YATELIBS := -L../.. -lyate @LIBS@ +INCFILES := @top_srcdir@/yateclass.h @srcdir@/../yxml/yatexml.h @srcdir@/yategsm.h + +PROGS= +LIBS = libyategsm.a +OBJS = codec.o +LIBD_DEV:= libyategsm.so +LIBD_VER:= $(LIBD_DEV).@PACKAGE_VERSION@ +LIBD:= ../../$(LIBD_VER) ../../$(LIBD_DEV) + +YXML:= ../yxml/libyatexml.a + +LOCALFLAGS = +LOCALLIBS = +COMPILE = $(CXX) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS) +LINK = $(CXX) $(LDFLAGS) + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +# include optional local make rules +-include YateLocal.mak + +.PHONY: all debug ddebug xdebug +all: $(LIBS) $(LIBD) $(PROGS) + +debug: + $(MAKE) all DEBUG=-g3 MODSTRIP= + +ddebug: + $(MAKE) all DEBUG='-g3 -DDEBUG' MODSTRIP= + +xdebug: + $(MAKE) all DEBUG='-g3 -DXDEBUG' MODSTRIP= + +.PHONY: strip +strip: all + strip --strip-debug --discard-locals $(PROGS) + +.PHONY: clean +clean: + @-$(RM) $(PROGS) $(LIBS) $(LIBD) $(OBJS) core 2>/dev/null + +%.o: @srcdir@/%.cpp $(INCFILES) + $(COMPILE) -c $< + +Makefile: @srcdir@/Makefile.in ../../config.status + cd ../.. && ./config.status + +../../$(LIBD_VER): $(OBJS) $(YXML) + $(LINK) -o $@ $(SONAME_OPT)$(LIBD_VER) $^ $(YATELIBS) + +../../$(LIBD_DEV): ../../$(LIBD_VER) + cd ../.. && ln -sf $(LIBD_VER) $(LIBD_DEV) + +$(LIBS): $(OBJS) + $(AR) rcs $@ $^ + +$(YXML): + $(MAKE) -C ../yxml diff --git a/libs/ygsm/codec.cpp b/libs/ygsm/codec.cpp new file mode 100644 index 00000000..055f504d --- /dev/null +++ b/libs/ygsm/codec.cpp @@ -0,0 +1,493 @@ +/** + * codec.cpp + * This file is part of the YATE Project http://YATE.null.ro + * + * GSM Radio Layer 3 messages coder and decoder + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004-2013 Null Team + * + * 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. + */ + +#include + +using namespace TelEngine; + +struct IEParam; +struct RL3ProtoMessage; + + +struct IEParam { + RL3Codec::Type type; + RL3Codec::XmlType xmlType; + uint16_t iei; + const String name; + bool isOptional; + uint16_t length; // in bits + bool lowerBits; + unsigned int (*decoder)(const RL3Codec*,uint8_t,const IEParam*,const uint8_t*&, unsigned int&, XmlElement*&); + unsigned int (*encoder)(const RL3Codec*,uint8_t,const IEParam*,XmlElement*,DataBlock&); + const void* data; +}; + +struct RL3Message { + uint16_t value; + const String name; + const IEParam* params; +}; + + +static unsigned int decodeParams(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, + XmlElement*& out, const IEParam* param); + +static uint8_t getUINT8(const uint8_t*& in, unsigned int& len, const IEParam* param) +{ + if (!(in && len && param)) + return 0; + if (param->length == 4) { + if (param->lowerBits) + return *in && 0x0f; + len--; + return (*in++ >> 4); + } + len--; + return *in++; +} + +static void addXMLElement(XmlElement*& dst, XmlElement* what) +{ + if (!what) + return; + if (!dst) + dst = what; + else + dst->addChildSafe(what); +} + +static unsigned int decodeMsgType(const RL3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in, + unsigned int& len, XmlElement*& out) +{ + //TODO + return RL3Codec::NoError; +} + + +static unsigned int encodeMsgType(const RL3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,DataBlock& out) +{ + //TODO + return RL3Codec::NoError; +} + +static const RL3Message* findRL3Msg(uint16_t val, const RL3Message* where) +{ + if (!where) + return 0; + while (where->name) { + if (where->value == val) + return where; + where++; + } + return 0; +} + +static unsigned int decodePD(const RL3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in, + unsigned int& len, XmlElement*& out) +{ + if (!(codec && in && len && param)) + return RL3Codec::ParserErr; + DDebug(codec->dbg(),DebugAll,"decodePD(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param, + in,len,out); + XmlElement* payload = 0; + XmlElement* xml = 0; + if (codec->flags() & RL3Codec::XmlDumpMsg) { + String s; + s.hexify((void*)in,len); + payload = new XmlElement(YSTRING("message_payload"),s); + } + uint8_t val = getUINT8(in,len,param); + unsigned int status = RL3Codec::NoError; + const RL3Message* msg = static_cast(param->data); + msg = findRL3Msg(val,msg); + if (!msg) { + xml = new XmlElement(param->name ? param->name : YSTRING("ie")); + xml->setText(String(val)); + } + else { + xml = new XmlElement(msg->name); + if (msg->params) + status = decodeParams(codec,proto,in,len,xml,msg->params); + } + addXMLElement(out,xml); + if (payload) + xml->addChildSafe(payload); + return status; +} + + +static unsigned int encodePD(const RL3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,DataBlock& out) +{ + //TODO + return RL3Codec::NoError; +} + + +static const RL3Message s_mmMsgs[] = { + //TODO +}; + +static const IEParam s_mmMessage[] = { + {RL3Codec::V, RL3Codec::Skip, 0, "SkipIndicator", false, 4, false, 0, 0, 0}, + {RL3Codec::V, RL3Codec::XmlElem, 0, "MessageType", false, 8, false, decodeMsgType, encodeMsgType, s_mmMsgs}, + {RL3Codec::NoType, RL3Codec::Skip, 0, "", 0, 0, 0, 0, 0, 0 }, +}; + +static const RL3Message s_protoMsg[] = { + {RL3Codec::GCC, "GCC", 0}, + {RL3Codec::BCC, "BCC", 0}, + {RL3Codec::EPS_SM, "EPS_SM", 0}, + {RL3Codec::CC, "CC", 0}, + {RL3Codec::GTTP, "GTTP", 0}, + {RL3Codec::MM, "MM", s_mmMessage}, + {RL3Codec::RRM, "RRM", 0}, + {RL3Codec::EPS_MM, "EPS_MM", 0}, + {RL3Codec::GPRS_MM, "GPRS_MM", 0}, + {RL3Codec::SMS, "SMS", 0}, + {RL3Codec::GPRS_SM, "GPRS_SM", 0}, + {RL3Codec::SS, "SS", 0}, + {RL3Codec::LCS, "LCS", 0}, + {RL3Codec::Extension, "EXT", 0}, + {RL3Codec::Test, "TEST", 0}, + {RL3Codec::Unknown, "", 0}, +}; + +static const IEParam s_rl3Message[] = { + {RL3Codec::V, RL3Codec::XmlElem, 0, "PD", false, 4, true, decodePD, encodePD, s_protoMsg}, + {RL3Codec::NoType, RL3Codec::Skip, 0, "", 0, 0, 0, 0, 0, 0}, +}; + + + +static unsigned int skipParam(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len,const IEParam* param) +{ + if (!(codec && in && len && param)) + return RL3Codec::ParserErr; + DDebug(codec->dbg(),DebugAll,"skipParam() param=%s(%p) of type %s [%p]",param->name.c_str(),param, + lookup(param->type,RL3Codec::s_typeDict,""),codec->ptr()); + switch (param->type) { + case RL3Codec::V: + case RL3Codec::T: + if (param->length == 4) { + if (!param->lowerBits) { + len--; + in++; + } + break; + } + // intentional fall through + case RL3Codec::TV: + if (len * 8 < param->length) + return RL3Codec::MsgTooShort; + len -= param->length / 8; + in += param->length / 8; + break; + case RL3Codec::TLV: + if (len < 2) + return RL3Codec::MsgTooShort; + in++; + len--; + // intentional fall through + case RL3Codec::LV: + { + if (len < 1) + return RL3Codec::MsgTooShort; + uint8_t l = *in++; + len--; + if (len < l) + return RL3Codec::MsgTooShort; + in += l; + len -= l; + break; + } + case RL3Codec::TLVE: + if (len < 3) + return RL3Codec::MsgTooShort; + in++; + len--; + // intentional fall through + case RL3Codec::LVE: + { + if (len < 2) + return RL3Codec::MsgTooShort; + uint16_t l = in[0]; + l = (l << 8) | in[1]; + in += 2; + len -= 2; + if (len < l) + return RL3Codec::MsgTooShort; + in += l; + len -= l; + break; + } + case RL3Codec::NoType: + break; + } + return RL3Codec::NoError; +} + +static unsigned int dumpUnknownIE(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out) +{ + if (!(codec)) + return RL3Codec::ParserErr; + if (!(in && len)) + return RL3Codec::NoError; + DDebug(codec->dbg(),DebugAll,"dumpUnknownIE(in=%p,len=%u) in protocol=%s [%p]",in,len, + lookup(proto,RL3Codec::s_protoDict,"Unknown"),codec->ptr()); + uint8_t iei = *in; + // bit 8 on 1 indicates one octet length IE of type V/T/TV + unsigned int dumpOctets = len; + if (iei & 0x80 || len < 2) + dumpOctets = len; + else { + // it's either TLV or TLVE + // in EPS MM and EPS MM, if bits 7 to 4 are set on 1 => means IEI is TLVE + if ((proto == RL3Codec::EPS_MM || proto == RL3Codec::EPS_SM) && ((iei & 0x78) == 0x78)) { + if (len < 3) + dumpOctets = len; + else { + uint16_t l = in[1]; + l = (l << 8) | in[2]; + dumpOctets = (len < (l + 3) ? len : l + 3); + } + } + else + dumpOctets = (len < (in[1] + 2) ? len : in[1] + 2); + } + if (dumpOctets) { + XmlElement* xml = new XmlElement("ie"); + addXMLElement(out,xml); + String dumpStr; + dumpStr.hexify((void*)in,dumpOctets); + xml->setText(dumpStr); + in += dumpOctets; + len -= dumpOctets; + } + return RL3Codec::NoError; +} + +static unsigned int dumpParamValue(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, const IEParam* param, + XmlElement*& out) +{ + if (!(codec && in && len)) + return RL3Codec::ParserErr; + DDebug(codec->dbg(),DebugAll,"dumpParamValue(in=%p,len=%u) for %sparam%s%s(%p) [%p]",in,len, + (!param ? "unknown " : ""),(param ? "=" : ""), (param ? param->name.c_str() : ""),param,codec->ptr()); + if (param) { + String dumpStr; + uint8_t skipOctets = 0; + switch (param->type) { + case RL3Codec::T: + // there's no value to dump + break; + case RL3Codec::V: + { + uint8_t val = 0; + if (param->length == 4) { + if (!param->lowerBits) { + val |= *in & 0xf0; + len--; + in++; + } + else + val |= *in & 0x0f; + } + else { + val = *in; + len--; + in++; + } + dumpStr.hexify(&val,1); + break; + } + case RL3Codec::TV: + { + if (param->length == 8) { + uint8_t val = *in & 0x0f; + len--; + in++; + dumpStr.hexify(&val,1); + } + else + skipOctets = 1; + break; + } + case RL3Codec::TLV: + skipOctets = 2; + break; + case RL3Codec::LV: + skipOctets = 1; + break; + case RL3Codec::TLVE: + skipOctets = 3; + break; + case RL3Codec::LVE: + skipOctets = 2; + break; + case RL3Codec::NoType: + break; + } + if (skipOctets) { + const uint8_t* buff = in; + unsigned int lbuff = len; + if (int status = skipParam(codec,proto,in,len,param)) + return status; + if (len + skipOctets <= lbuff) + dumpStr.hexify((void*)(buff + skipOctets), lbuff - len - skipOctets); + } + XmlElement* xml = new XmlElement(param->name); + addXMLElement(out,xml); + if (dumpStr) + xml->setText(dumpStr); + } + else + return dumpUnknownIE(codec,proto,in,len,out); + + return RL3Codec::NoError; +} + +static unsigned int decodeV(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out, + const IEParam* param) +{ + if (!(codec && in && len && param)) + return RL3Codec::ParserErr; + if (len * 8 < param->length) + return RL3Codec::MsgTooShort; + DDebug(codec->dbg(),DebugAll,"decodeV(in=%p,len=%u,out=%p,param=%s[%p]) [%p]",in,len,out,param->name.c_str(),param,codec->ptr()); + switch (param->xmlType) { + case RL3Codec::Skip: + return skipParam(codec,proto,in,len,param); + case RL3Codec::XmlElem: + if (!(param->decoder || (param->data && param->name))) + return dumpParamValue(codec,proto,in,len,param,out); + if (param->decoder) + return param->decoder(codec,proto,param,in,len,out); + // decode an 1 byte value from a dictionary + if (param->data && param->name) { + if (param->length > 8) { + DDebug(codec->dbg(),DebugMild,"decodeV() - decoding for values longer than 1 byte not supported, dumping param=%s(%p) [%p]", + param->name.c_str(),param,codec->ptr()); + return dumpParamValue(codec,proto,in,len,param,out); + } + uint8_t val = getUINT8(in,len,param); + XmlElement* xml = new XmlElement(param->name); + addXMLElement(out,xml); + const TokenDict* dict = static_cast(param->data); + if (!dict) + xml->setText(String(val)); + else + xml->setText(lookup(val,dict,String(val))); + return RL3Codec::NoError; + } + default: + return RL3Codec::ParserErr; + } + return RL3Codec::NoError; +} + + +static unsigned int decodeParams(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out, + const IEParam* param) +{ + if (!(codec && in && len > 2 && param)) + return RL3Codec::ParserErr; + while (param) { + int status = RL3Codec::NoError; + switch (param->type) { + case RL3Codec::V: + status = decodeV(codec,proto,in,len,out,param); + break; + //TODO + case RL3Codec::T: + case RL3Codec::TV: + case RL3Codec::LV: + case RL3Codec::TLV: + case RL3Codec::LVE: + case RL3Codec::TLVE: + case RL3Codec::NoType: + break; + } + if (status) + return status; + } + return RL3Codec::NoError; +}; + +const TokenDict RL3Codec::s_typeDict[] = { + {"T", RL3Codec::T}, + {"V", RL3Codec::V}, + {"TV", RL3Codec::TV}, + {"LV", RL3Codec::LV}, + {"TLV", RL3Codec::TLV}, + {"LVE", RL3Codec::LVE}, + {"TLVE", RL3Codec::TLVE}, + {0, 0}, +}; + +const TokenDict RL3Codec::s_protoDict[] = { + {"GCC", RL3Codec::GCC}, + {"BCC", RL3Codec::BCC}, + {"EPS_SM", RL3Codec::EPS_SM}, + {"CC", RL3Codec::CC}, + {"GTTP", RL3Codec::GTTP}, + {"MM", RL3Codec::MM}, + {"RRM", RL3Codec::RRM}, + {"EPS_MM", RL3Codec::EPS_MM}, + {"GPRS_MM", RL3Codec::GPRS_MM}, + {"SMS", RL3Codec::SMS}, + {"GPRS_SM", RL3Codec::GPRS_SM}, + {"SS", RL3Codec::SS}, + {"LCS", RL3Codec::LCS}, + {"Extension", RL3Codec::Extension}, + {"Test", RL3Codec::Test}, + {"Unknown", RL3Codec::Unknown}, + {0, 0}, +}; + +RL3Codec::RL3Codec(DebugEnabler* dbg) + : m_flags(0), + m_dbg(0), + m_ptr(0) +{ + DDebug(DebugAll,"Created RL3Codec [%p]",this); + setCodecDebug(dbg); +} + +unsigned int RL3Codec::decode(const uint8_t* in, unsigned int len, XmlElement*& out) +{ + if (!in || len < 2) + return MsgTooShort; + const uint8_t* buff = in; + unsigned int l = len; + return decodeParams(this,RL3Codec::Unknown,buff,l,out,s_rl3Message); +} + +unsigned int RL3Codec::encode(XmlElement* in, DataBlock& out) +{ + //TODO + return NoError; +} + +void RL3Codec::setCodecDebug(DebugEnabler* enabler, void* ptr) +{ + m_dbg = enabler ? enabler : m_dbg; + m_ptr = ptr ? ptr : (void*)this; +} \ No newline at end of file diff --git a/libs/ygsm/yategsm.h b/libs/ygsm/yategsm.h new file mode 100644 index 00000000..f227a468 --- /dev/null +++ b/libs/ygsm/yategsm.h @@ -0,0 +1,155 @@ +/* + * yategsm.h + * GSM Radio Layer 3 library + * This file is part of the YATE Project http://YATE.null.ro + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2011-2013 Null Team + * + * 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. + */ + +#ifndef __YATEGSM_H +#define __YATEGSM_H + +#include +#include + +#ifdef _WINDOWS + +#ifdef LIBYGSM_EXPORTS +#define YGSM_API __declspec(dllexport) +#else +#ifndef LIBYGSM_STATIC +#define YGSM_API __declspec(dllimport) +#endif +#endif + +#endif /* _WINDOWS */ + +#ifndef YGSM_API +#define YGSM_API +#endif + + +namespace TelEngine { + +class YGSM_API RL3Codec +{ + YNOCOPY(RL3Codec); +public: + enum Flags { + XmlDumpMsg = 0x01, + XmlDumpIEs = 0x02, + }; + enum Status { + NoError = 0, + MsgTooShort, + UnknownProto, + ParserErr, + }; + // Protocol discriminator according to ETSI TS 124 007 V11.0.0, section 11.2.3.1.1 + enum Protocol { + GCC = 0x00, // Group Call Control + BCC = 0x01, // Broadcast Call Control + EPS_SM = 0x02, // EPS Session Management + CC = 0x03, // Call Control; Call Related SS messages + GTTP = 0x04, // GPRS Transparent Transport Protocol (GTTP) + MM = 0x05, // Mobility Management + RRM = 0x06, // Radio Resources Management + EPS_MM = 0x07, // EPS Mobility Management + GPRS_MM = 0x08, // GPRS Mobility Management + SMS = 0x09, // SMS + GPRS_SM = 0x0a, // GPRS Session Management + SS = 0x0b, // Non Call Related SS messages + LCS = 0x0c, // Location services + Extension = 0x0e, // reserved for extension of the PD to one octet length + Test = 0x0f, // used by tests procedures described in 3GPP TS 44.014, 3GPP TS 34.109 and 3GPP TS 36.509 + Unknown = 0xff, + }; + enum Type { + NoType = 0, + T, + V, + TV, + LV, + TLV, + LVE, + TLVE, + }; + enum XmlType { + Skip, + XmlElem, + }; + RL3Codec(DebugEnabler* dbg); + unsigned int decode(const uint8_t* in, unsigned int len, XmlElement*& out); + unsigned int encode(XmlElement* in, DataBlock& out); + + /** + * Set data used in debug + * @param enabler The DebugEnabler to use (0 to to use the engine) + * @param ptr Pointer to print, 0 to use the codec pointer + */ + void setCodecDebug(DebugEnabler* enabler = 0, void* ptr = 0); + + /** + * Retrieve codec flags + * @return Codec flags + */ + inline uint8_t flags() const + { return m_flags; } + + /** + * Set codec flags + * @param flgs Flags to set + * @param reset Reset flags before setting these ones + */ + inline void setFlags(uint8_t flgs, bool reset = false) + { + if (reset) + resetFlags(); + m_flags |= flgs; + } + + /** + * Reset codec flags + * @param flgs Flags to reset. If 0, all flags are reset + */ + inline void resetFlags(uint8_t flgs = 0) + { + if (flgs) + m_flags &= ~flgs; + else + m_flags = 0; + } + + inline DebugEnabler* dbg() const + { return m_dbg; } + + inline void* ptr() const + { return m_ptr; } + + static const TokenDict s_typeDict[]; + static const TokenDict s_protoDict[]; +private: + uint8_t m_flags; // Codec flags + // data used for debugging messages + DebugEnabler* m_dbg; + void* m_ptr; +}; + + +}; // namespace TelEngine + +#endif /* __YATEGSM_H */ + +/* vi: set ts=8 sw=4 sts=4 noet: */