jSS7 中的 SendParameters 奇怪行为
SendParameters weird behavior in jSS7
所以我有一个 restcomm/jss7 where i implemented the SendParameters SS7 command that is defined for MAP v1, since the version i forked didn't have it implemented at the time (don't think it's implemented still). This is not the first command that i implemented in JSS7. The issue is that I'm pretty sure my encoding/decoding follows the 09.02 规范的分叉版本,但是当我在 subscriberId 部分给定 TMSI 而不是 IMSI 构建调用 SendParameters 消息时,它在 wireshark 中显示为 msisdn 并且未正确解析。这是 wireshark 的问题还是我的实现有问题?我的 SendParametersRequestImpl 和 SubscriberIdImpl 供参考:
public class SendParameterRequestImp extends MobilityMessageImpl implements SendParametersRequest {
private static final int _TAG_SUBSCRIBER_ID = 0;
private static final int _TAG_REQUESTED_PARAMETER = 1;
private SubscriberId subscriberId;
private static final String _PrimitiveName = "SendParameterRequestImp";
private ArrayList<RequestParameter> requestParameterList;
public SendParameterRequestImp(SubscriberId subscriberId, ArrayList<RequestParameter> requestParameters) {
this.subscriberId = subscriberId;
this.requestParameterList = requestParameters;
}
public SendParameterRequestImp() {
}
@Override
public ArrayList<RequestParameter> getRequestParameterList() {
return requestParameterList;
}
@Override
public SubscriberId getSubscriberId() {
return subscriberId;
}
public void setSubscriberId(SubscriberId subscriberId) {
this.subscriberId = subscriberId;
}
@Override
public MAPMessageType getMessageType() {
return MAPMessageType.sendParameter_Request;
}
@Override
public int getOperationCode() {
return MAPOperationCode.sendParameters;
}
@Override
public int getTag() throws MAPException {
return Tag.SEQUENCE;
}
@Override
public int getTagClass() {
return Tag.CLASS_UNIVERSAL;
}
@Override
public boolean getIsPrimitive() {
return false;
}
@Override
public void decodeAll(AsnInputStream ansIS) throws MAPParsingComponentException {
try {
int length = ansIS.readLength();
this._decode(ansIS, length);
} catch (IOException e) {
throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
} catch (AsnException e) {
throw new MAPParsingComponentException("AsnException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
private void _decode(AsnInputStream ansIS, int length) throws IOException, AsnException, MAPParsingComponentException {
AsnInputStream ais = ansIS.readSequenceStreamData(length);
while (true) {
if (ais.available() == 0)
break;
int tag = ais.readTag();
switch (ais.getTagClass()) {
case Tag.CLASS_CONTEXT_SPECIFIC:
this.subscriberId = new SubscriberIdImpl();
((SubscriberIdImpl) this.subscriberId).decodeAll(ais);
break;
case Tag.CLASS_UNIVERSAL:
switch (tag) {
case Tag.SEQUENCE:
this.requestParameterList = new ArrayList<RequestParameter>();
AsnInputStream ais3 = ais.readSequenceStream();
while (true) {
if (ais3.available() == 0)
break;
int tag2 = ais3.readTag();
if (tag2 != Tag.ENUMERATED)
throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
+ ": bad tag or tagClass or is not primitive when decoding plmnClientList",
MAPParsingComponentExceptionReason.MistypedParameter);
int code = (int) ais3.readInteger();
RequestParameter elem = RequestParameter.getInstance(code);
this.requestParameterList.add(elem);
}
break;
default:
ais.advanceElement();
break;
}
break;
default:
ais.advanceElement();
break;
}
}
}
@Override
public void decodeData(AsnInputStream ansIS, int length) throws MAPParsingComponentException {
try {
this._decode(ansIS, length);
} catch (IOException e) {
throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
} catch (AsnException e) {
throw new MAPParsingComponentException("AsnException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
@Override
public void encodeAll(AsnOutputStream asnOs) throws MAPException {
this.encodeAll(asnOs, this.getTagClass(), this.getTag());
}
@Override
public void encodeAll(AsnOutputStream asnOs, int tagClass, int tag) throws MAPException {
try {
asnOs.writeTag(tagClass, this.getIsPrimitive(), tag);
int pos = asnOs.StartContentDefiniteLength();
this.encodeData(asnOs);
asnOs.FinalizeContent(pos);
} catch (AsnException e) {
throw new MAPException("AsnException when encoding " + _PrimitiveName + ": " + e.getMessage(), e);
}
}
@Override
public void encodeData(AsnOutputStream asnOs) throws MAPException {
if (this.subscriberId == null || this.requestParameterList == null) {
throw new MAPException("Error while encoding " + _PrimitiveName
+ " the mandatory parameter subscriberId and requestParameterList is not defined");
}
((SubscriberIdImpl) this.subscriberId).encodeAll(asnOs);
try {
asnOs.writeTag(Tag.CLASS_UNIVERSAL, false, Tag.SEQUENCE);
int pos = asnOs.StartContentDefiniteLength();
for (RequestParameter requestParameter : this.requestParameterList)
asnOs.writeInteger(Tag.CLASS_UNIVERSAL, Tag.ENUMERATED, requestParameter.getCode());
asnOs.FinalizeContent(pos);
} catch (IOException e) {
e.printStackTrace();
} catch (AsnException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "SendParameterRequestImp{" +
"subscriberId=" + subscriberId +
", requestParameterList=" + requestParameterList +
'}';
}
}
public class SubscriberIdImpl implements SubscriberId, MAPAsnPrimitive {
private TMSI tmsi;
private IMSI imsi;
private static final int _TAG_IMSI = 0;
private static final int _TAG_TMSI = 1;
private static final String _PrimitiveName = "SubscriberId";
public SubscriberIdImpl() {
}
public SubscriberIdImpl(TMSI tmsi) {
this.tmsi = tmsi;
}
public SubscriberIdImpl(IMSI imsi) {
this.imsi = imsi;
}
@Override
public TMSI getTmsi() {
return tmsi;
}
@Override
public IMSI getImsi() {
return imsi;
}
@Override
public int getTag() throws MAPException {
if (this.imsi != null) {
return _TAG_IMSI;
} else {
return _TAG_TMSI;
}
}
@Override
public int getTagClass() {
return Tag.CLASS_CONTEXT_SPECIFIC;
}
@Override
public boolean getIsPrimitive() {
return true;
}
@Override
public void decodeAll(AsnInputStream ansIS) throws MAPParsingComponentException {
try {
int length = ansIS.readLength();
this._decode(ansIS, length);
} catch (IOException e) {
throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": ", e,
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
private void _decode(AsnInputStream asnIS, int length) throws MAPParsingComponentException {
if (asnIS.getTagClass() != Tag.CLASS_CONTEXT_SPECIFIC || !asnIS.isTagPrimitive())
throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
+ ": bad tag class or is not primitive: TagClass=" + asnIS.getTagClass(),
MAPParsingComponentExceptionReason.MistypedParameter);
switch (asnIS.getTag()) {
case _TAG_IMSI:
this.imsi = new IMSIImpl();
((IMSIImpl) this.imsi).decodeData(asnIS, length);
break;
case _TAG_TMSI:
this.tmsi = new TMSIImpl();
((TMSIImpl) this.tmsi).decodeData(asnIS, length);
break;
default:
throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
+ ": Expexted imsi [0] IMSI or msisdn [1] ISDN-AddressString, but found " + asnIS.getTag(),
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
@Override
public void decodeData(AsnInputStream ansIS, int length) throws MAPParsingComponentException {
this._decode(ansIS, length);
}
@Override
public void encodeAll(AsnOutputStream asnOs) throws MAPException {
this.encodeAll(asnOs, this.getTagClass(), this.getTag());
}
@Override
public void encodeAll(AsnOutputStream asnOs, int tagClass, int tag) throws MAPException {
try {
asnOs.writeTag(tagClass, this.getIsPrimitive(), tag);
int pos = asnOs.StartContentDefiniteLength();
this.encodeData(asnOs);
asnOs.FinalizeContent(pos);
} catch (AsnException e) {
throw new MAPException("AsnException when encoding " + _PrimitiveName + ": " + e.getMessage(), e);
}
}
@Override
public void encodeData(AsnOutputStream asnOs) throws MAPException {
if (this.imsi == null && this.tmsi == null)
throw new MAPException("Error while encoding " + _PrimitiveName + ": all choices must not be null");
if (this.imsi != null && this.tmsi != null)
throw new MAPException("Error while encoding " + _PrimitiveName + ": all choices must not be not null");
if (this.imsi != null) {
((IMSIImpl) this.imsi).encodeData(asnOs);
} else {
((TMSIImpl) this.tmsi).encodeData(asnOs);
}
}
@Override
public String toString() {
return "SubscriberIdImpl{" +
"tmsi=" + tmsi +
", imsi=" + imsi +
'}';
}
}
ASN.1 规范如下所示:
MAP V1: SendParametersArg ::= SEQUENCE { subscriberId SubscriberId, requestParameterList RequestParameterList}
requestParameterList SEQUENCE SIZE (1..2) OF RequestParameter
SubscriberId ::= CHOICE { imsi [0] IMSI, tmsi [1] TMSI}
Wireshark 截图:
在查看了 asn.1 的 wireshark 源代码解析器之后,他们似乎将 SubscriberIdentity 用于 SendParametersArg 而不是 SubscriberId。区别在于前者是IMSI和MSISDN之间的选择,后者是IMSI和TMSI之间的选择。
所以我有一个 restcomm/jss7 where i implemented the SendParameters SS7 command that is defined for MAP v1, since the version i forked didn't have it implemented at the time (don't think it's implemented still). This is not the first command that i implemented in JSS7. The issue is that I'm pretty sure my encoding/decoding follows the 09.02 规范的分叉版本,但是当我在 subscriberId 部分给定 TMSI 而不是 IMSI 构建调用 SendParameters 消息时,它在 wireshark 中显示为 msisdn 并且未正确解析。这是 wireshark 的问题还是我的实现有问题?我的 SendParametersRequestImpl 和 SubscriberIdImpl 供参考:
public class SendParameterRequestImp extends MobilityMessageImpl implements SendParametersRequest {
private static final int _TAG_SUBSCRIBER_ID = 0;
private static final int _TAG_REQUESTED_PARAMETER = 1;
private SubscriberId subscriberId;
private static final String _PrimitiveName = "SendParameterRequestImp";
private ArrayList<RequestParameter> requestParameterList;
public SendParameterRequestImp(SubscriberId subscriberId, ArrayList<RequestParameter> requestParameters) {
this.subscriberId = subscriberId;
this.requestParameterList = requestParameters;
}
public SendParameterRequestImp() {
}
@Override
public ArrayList<RequestParameter> getRequestParameterList() {
return requestParameterList;
}
@Override
public SubscriberId getSubscriberId() {
return subscriberId;
}
public void setSubscriberId(SubscriberId subscriberId) {
this.subscriberId = subscriberId;
}
@Override
public MAPMessageType getMessageType() {
return MAPMessageType.sendParameter_Request;
}
@Override
public int getOperationCode() {
return MAPOperationCode.sendParameters;
}
@Override
public int getTag() throws MAPException {
return Tag.SEQUENCE;
}
@Override
public int getTagClass() {
return Tag.CLASS_UNIVERSAL;
}
@Override
public boolean getIsPrimitive() {
return false;
}
@Override
public void decodeAll(AsnInputStream ansIS) throws MAPParsingComponentException {
try {
int length = ansIS.readLength();
this._decode(ansIS, length);
} catch (IOException e) {
throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
} catch (AsnException e) {
throw new MAPParsingComponentException("AsnException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
private void _decode(AsnInputStream ansIS, int length) throws IOException, AsnException, MAPParsingComponentException {
AsnInputStream ais = ansIS.readSequenceStreamData(length);
while (true) {
if (ais.available() == 0)
break;
int tag = ais.readTag();
switch (ais.getTagClass()) {
case Tag.CLASS_CONTEXT_SPECIFIC:
this.subscriberId = new SubscriberIdImpl();
((SubscriberIdImpl) this.subscriberId).decodeAll(ais);
break;
case Tag.CLASS_UNIVERSAL:
switch (tag) {
case Tag.SEQUENCE:
this.requestParameterList = new ArrayList<RequestParameter>();
AsnInputStream ais3 = ais.readSequenceStream();
while (true) {
if (ais3.available() == 0)
break;
int tag2 = ais3.readTag();
if (tag2 != Tag.ENUMERATED)
throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
+ ": bad tag or tagClass or is not primitive when decoding plmnClientList",
MAPParsingComponentExceptionReason.MistypedParameter);
int code = (int) ais3.readInteger();
RequestParameter elem = RequestParameter.getInstance(code);
this.requestParameterList.add(elem);
}
break;
default:
ais.advanceElement();
break;
}
break;
default:
ais.advanceElement();
break;
}
}
}
@Override
public void decodeData(AsnInputStream ansIS, int length) throws MAPParsingComponentException {
try {
this._decode(ansIS, length);
} catch (IOException e) {
throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
} catch (AsnException e) {
throw new MAPParsingComponentException("AsnException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
@Override
public void encodeAll(AsnOutputStream asnOs) throws MAPException {
this.encodeAll(asnOs, this.getTagClass(), this.getTag());
}
@Override
public void encodeAll(AsnOutputStream asnOs, int tagClass, int tag) throws MAPException {
try {
asnOs.writeTag(tagClass, this.getIsPrimitive(), tag);
int pos = asnOs.StartContentDefiniteLength();
this.encodeData(asnOs);
asnOs.FinalizeContent(pos);
} catch (AsnException e) {
throw new MAPException("AsnException when encoding " + _PrimitiveName + ": " + e.getMessage(), e);
}
}
@Override
public void encodeData(AsnOutputStream asnOs) throws MAPException {
if (this.subscriberId == null || this.requestParameterList == null) {
throw new MAPException("Error while encoding " + _PrimitiveName
+ " the mandatory parameter subscriberId and requestParameterList is not defined");
}
((SubscriberIdImpl) this.subscriberId).encodeAll(asnOs);
try {
asnOs.writeTag(Tag.CLASS_UNIVERSAL, false, Tag.SEQUENCE);
int pos = asnOs.StartContentDefiniteLength();
for (RequestParameter requestParameter : this.requestParameterList)
asnOs.writeInteger(Tag.CLASS_UNIVERSAL, Tag.ENUMERATED, requestParameter.getCode());
asnOs.FinalizeContent(pos);
} catch (IOException e) {
e.printStackTrace();
} catch (AsnException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "SendParameterRequestImp{" +
"subscriberId=" + subscriberId +
", requestParameterList=" + requestParameterList +
'}';
}
}
public class SubscriberIdImpl implements SubscriberId, MAPAsnPrimitive {
private TMSI tmsi;
private IMSI imsi;
private static final int _TAG_IMSI = 0;
private static final int _TAG_TMSI = 1;
private static final String _PrimitiveName = "SubscriberId";
public SubscriberIdImpl() {
}
public SubscriberIdImpl(TMSI tmsi) {
this.tmsi = tmsi;
}
public SubscriberIdImpl(IMSI imsi) {
this.imsi = imsi;
}
@Override
public TMSI getTmsi() {
return tmsi;
}
@Override
public IMSI getImsi() {
return imsi;
}
@Override
public int getTag() throws MAPException {
if (this.imsi != null) {
return _TAG_IMSI;
} else {
return _TAG_TMSI;
}
}
@Override
public int getTagClass() {
return Tag.CLASS_CONTEXT_SPECIFIC;
}
@Override
public boolean getIsPrimitive() {
return true;
}
@Override
public void decodeAll(AsnInputStream ansIS) throws MAPParsingComponentException {
try {
int length = ansIS.readLength();
this._decode(ansIS, length);
} catch (IOException e) {
throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": ", e,
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
private void _decode(AsnInputStream asnIS, int length) throws MAPParsingComponentException {
if (asnIS.getTagClass() != Tag.CLASS_CONTEXT_SPECIFIC || !asnIS.isTagPrimitive())
throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
+ ": bad tag class or is not primitive: TagClass=" + asnIS.getTagClass(),
MAPParsingComponentExceptionReason.MistypedParameter);
switch (asnIS.getTag()) {
case _TAG_IMSI:
this.imsi = new IMSIImpl();
((IMSIImpl) this.imsi).decodeData(asnIS, length);
break;
case _TAG_TMSI:
this.tmsi = new TMSIImpl();
((TMSIImpl) this.tmsi).decodeData(asnIS, length);
break;
default:
throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
+ ": Expexted imsi [0] IMSI or msisdn [1] ISDN-AddressString, but found " + asnIS.getTag(),
MAPParsingComponentExceptionReason.MistypedParameter);
}
}
@Override
public void decodeData(AsnInputStream ansIS, int length) throws MAPParsingComponentException {
this._decode(ansIS, length);
}
@Override
public void encodeAll(AsnOutputStream asnOs) throws MAPException {
this.encodeAll(asnOs, this.getTagClass(), this.getTag());
}
@Override
public void encodeAll(AsnOutputStream asnOs, int tagClass, int tag) throws MAPException {
try {
asnOs.writeTag(tagClass, this.getIsPrimitive(), tag);
int pos = asnOs.StartContentDefiniteLength();
this.encodeData(asnOs);
asnOs.FinalizeContent(pos);
} catch (AsnException e) {
throw new MAPException("AsnException when encoding " + _PrimitiveName + ": " + e.getMessage(), e);
}
}
@Override
public void encodeData(AsnOutputStream asnOs) throws MAPException {
if (this.imsi == null && this.tmsi == null)
throw new MAPException("Error while encoding " + _PrimitiveName + ": all choices must not be null");
if (this.imsi != null && this.tmsi != null)
throw new MAPException("Error while encoding " + _PrimitiveName + ": all choices must not be not null");
if (this.imsi != null) {
((IMSIImpl) this.imsi).encodeData(asnOs);
} else {
((TMSIImpl) this.tmsi).encodeData(asnOs);
}
}
@Override
public String toString() {
return "SubscriberIdImpl{" +
"tmsi=" + tmsi +
", imsi=" + imsi +
'}';
}
}
ASN.1 规范如下所示:
MAP V1: SendParametersArg ::= SEQUENCE { subscriberId SubscriberId, requestParameterList RequestParameterList}
requestParameterList SEQUENCE SIZE (1..2) OF RequestParameter
SubscriberId ::= CHOICE { imsi [0] IMSI, tmsi [1] TMSI}
Wireshark 截图:
在查看了 asn.1 的 wireshark 源代码解析器之后,他们似乎将 SubscriberIdentity 用于 SendParametersArg 而不是 SubscriberId。区别在于前者是IMSI和MSISDN之间的选择,后者是IMSI和TMSI之间的选择。