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之间的选择。