All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.db2client.impl.drda.DRDAConnectResponse Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2019,2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.vertx.db2client.impl.drda;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import io.netty.buffer.ByteBuf;
import io.vertx.db2client.DB2Exception;
import io.vertx.db2client.impl.DB2DatabaseMetadata;

public class DRDAConnectResponse extends DRDAResponse {

    public DRDAConnectResponse(ByteBuf buffer, ConnectionMetaData metadata) {
      super(buffer, metadata);
    }

    public void readAccessSecurity(int securityMechanism) {
        startSameIdChainParse();
        parseACCSECreply(securityMechanism);
        endOfSameIdChainData();
//      agent_.checkForChainBreakingException_();
    }

    public void readExchangeServerAttributes() {
        startSameIdChainParse();
        parseEXCSATreply();
        endOfSameIdChainData();
//        agent_.checkForChainBreakingException_();
    }

    // NET only entry point
    public void readSecurityCheck() {
        startSameIdChainParse();
        parseSECCHKreply();
        endOfSameIdChainData();
    }

    public RDBAccessData readAccessDatabase() {
        startSameIdChainParse();
        RDBAccessData accessData = parseACCRDBreply();
        endOfSameIdChainData();
        return accessData;
//        agent_.checkForChainBreakingException_();
    }

    public void readLocalCommit() {
        startSameIdChainParse();
        parseRDBCMMreply();
        endOfSameIdChainData();
    }

    // Parse the reply for the RDB Commit Unit of Work Command.
    // This method handles the parsing of all command replies and reply data
    // for the rdbcmm command.
    private void parseRDBCMMreply(/*ConnectionCallbackInterface connection*/) {
        parseTypdefsOrMgrlvlovrs();

        parseENDUOWRM();
        int peekCP = parseTypdefsOrMgrlvlovrs();

        if (peekCP == CodePoint.SQLCARD) {
            NetSqlca netSqlca = parseSQLCARD(null);
//            connection.completeSqlca(netSqlca);
        } else {
            parseCommitError();
        }
    }

    private void parseCommitError() {
        int peekCP = peekCodePoint();
        switch (peekCP) {
        case CodePoint.ABNUOWRM:
            throw new IllegalStateException("Abnormal ending to UOW");
//            NetSqlca sqlca = parseAbnormalEndUow(null);
//            connection.completeSqlca(sqlca);
//            break;
        case CodePoint.CMDCHKRM:
            parseCMDCHKRM();
            break;
        case CodePoint.RDBNACRM:
            parseRDBNACRM();
            break;
        default:
            throwUnknownCodepoint(peekCP);
//            parseCommonError(peekCP);
            break;
        }
    }

    void parseCommonError(int peekCP) {
        switch (peekCP) {
        case CodePoint.CMDNSPRM:
            parseCMDNSPRM();
            break;
        case CodePoint.PRCCNVRM:
            parsePRCCNVRM();
            break;
        case CodePoint.SYNTAXRM:
            parseSYNTAXRM();
            break;
        case CodePoint.VALNSPRM:
            parseVALNSPRM();
            break;
        default:
            throwUnknownCodepoint(peekCP);
        }
    }

    // Parameter Value Not Supported Reply Message indicates
    // that the parameter value specified is either not recognized
    // or not supported for the specified parameter.
    // The VALNSPRM can only be specified in accordance with
    // the rules specified for DDM subsetting.
    // The code point of the command parameter in error is
    // returned as a parameter in this message.
    // PROTOCOL Architects an SQLSTATE of 58017.
    //
    // if codepoint is 0x119C,0x119D, or 0x119E then SQLSTATE 58017, SQLCODE -332
    // else SQLSTATE 58017, SQLCODE -30073
    //
    // Messages
    // SQLSTATE : 58017
    //     The DDM parameter value is not supported.
    //     SQLCODE : -332
    //     There is no available conversion for the source code page
    //          to the target code page .
    //         Reason code .
    //     The reason codes are as follows:
    //     1 source and target code page combination is not supported
    //         by the database manager.
    //     2 source and target code page combination is either not
    //         supported by the database manager or by the operating
    //         system character conversion utility on the client node.
    //     3 source and target code page combination is either not
    //         supported by the database manager or by the operating
    //         system character conversion utility on the server node.
    //
    // SQLSTATE : 58017
    //     The DDM parameter value is not supported.
    //     SQLCODE : -30073
    //      Parameter value  is not supported.
    //     Some possible parameter identifiers include:
    //     002F  The target server does not support the data type
    //         requested by the application requester.
    //         The target server does not support the CCSID
    //         requested by the application requester. Ensure the CCSID
    //         used by the requester is supported by the server.
    //         119C - Verify the single-byte CCSID.
    //         119D - Verify the double-byte CCSID.
    //         119E - Verify the mixed-byte CCSID.
    //
    //     The current environment command or SQL statement
    //         cannot be processed successfully, nor can any subsequent
    //         commands or SQL statements.  The current transaction is
    //         rolled back and the application is disconnected
    //         from the remote database. The command cannot be processed.
    //
    // Returned from Server:
    // SVRCOD - required  (8 - ERROR)
    // CODPNT - required
    // RECCNT - optional (MINLVL 3, MINVAL 0) (will not be returned - should be ignored)
    // RDBNAM - optional (MINLVL 3)
    //
    private void parseVALNSPRM() {
        boolean svrcodReceived = false;
        int svrcod = CodePoint.SVRCOD_INFO;
        boolean rdbnamReceived = false;
        String rdbnam = null;
        boolean codpntReceived = false;
        int codpnt = 0;

        parseLengthAndMatchCodePoint(CodePoint.VALNSPRM);
        pushLengthOnCollectionStack();
        int peekCP = peekCodePoint();

        while (peekCP != END_OF_COLLECTION) {

            boolean foundInPass = false;

            if (peekCP == CodePoint.SVRCOD) {
                foundInPass = true;
                svrcodReceived = checkAndGetReceivedFlag(svrcodReceived);
                svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.RDBNAM) {
                foundInPass = true;
                rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
                rdbnam = parseRDBNAM(true);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.CODPNT) {
                foundInPass = true;
                codpntReceived = checkAndGetReceivedFlag(codpntReceived);
                codpnt = parseCODPNT();
                peekCP = peekCodePoint();
            }
            if (peekCP == CodePoint.SRVDGN) {
                foundInPass = true;
                String serverDiagnostics = parseSRVDGN();
                // TODO: Log this as a warning
                System.out.println("Server diagnostics: " + serverDiagnostics);
                peekCP = peekCodePoint();
            }

            // RECCNT will be skipped

            if (!foundInPass) {
                throwUnknownCodepoint(peekCP);
            }

        }
        popCollectionStack();
        if (!svrcodReceived)
          throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD);
        if (!codpntReceived)
          throwMissingRequiredCodepoint("CODPNT", CodePoint.CODPNT);

//        netAgent_.setSvrcod(svrcod);
        doValnsprmSemantics(codpnt, "\"\"");
    }

    // Data Stream Syntax Error Reply Message indicates that the data
    // sent to the target agent does not structurally conform to the requirements
    // of the DDM architecture.  The target agent terminated paring of the DSS
    // when the condition SYNERRCD specified was detected.
    // PROTOCOL architects an SQLSTATE of 58008 or 58009.
    //
    // Messages
    // SQLSTATE : 58009
    //     Execution failed due to a distribution protocol error that caused deallocation of the conversation.
    //     SQLCODE : -30020
    //     Execution failed because of a Distributed Protocol
    //         Error that will affect the successful execution of subsequent
    //         commands and SQL statements: Reason Code .
    //      Some possible reason codes include:
    //      121C Indicates that the user is not authorized to perform the requested command.
    //      1232 The command could not be completed because of a permanent error.
    //          In most cases, the server will be in the process of an abend.
    //      220A The target server has received an invalid data description.
    //          If a user SQLDA is specified, ensure that the fields are
    //          initialized correctly. Also, ensure that the length does not
    //          exceed the maximum allowed length for the data type being used.
    //
    //      The command or statement cannot be processed.  The current
    //          transaction is rolled back and the application is disconnected
    //          from the remote database.
    //
    //
    // Returned from Server:
    // SVRCOD - required  (8 - ERROR)
    // SYNERRCD - required
    // RECCNT - optional (MINVAL 0, MINLVL 3) (will not be returned - should be ignored)
    // CODPNT - optional (MINLVL 3)
    // RDBNAM - optional (MINLVL 3)
    //
    private void parseSYNTAXRM() {
        boolean svrcodReceived = false;
        int svrcod = CodePoint.SVRCOD_INFO;
        boolean synerrcdReceived = false;
        int synerrcd = 0;
        boolean rdbnamReceived = false;
        String rdbnam = null;
        boolean codpntReceived = false;
        int codpnt = 0;

        parseLengthAndMatchCodePoint(CodePoint.SYNTAXRM);
        pushLengthOnCollectionStack();
        int peekCP = peekCodePoint();

        while (peekCP != END_OF_COLLECTION) {

            boolean foundInPass = false;

            if (peekCP == CodePoint.SVRCOD) {
                foundInPass = true;
                svrcodReceived = checkAndGetReceivedFlag(svrcodReceived);
                svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.SYNERRCD) {
                foundInPass = true;
                synerrcdReceived = checkAndGetReceivedFlag(synerrcdReceived);
                synerrcd = parseSYNERRCD();
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.SRVDGN) {
                foundInPass = true;
                String serverDiagnostics = parseSRVDGN();
                // TODO: Log this as a warning
                System.out.println("Server diagnostics: " + serverDiagnostics);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.RDBNAM) {
                foundInPass = true;
                rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
                rdbnam = parseRDBNAM(true);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.CODPNT) {
                foundInPass = true;
                codpntReceived = checkAndGetReceivedFlag(codpntReceived);
                codpnt = parseCODPNT();
                peekCP = peekCodePoint();
            }

            // RECCNT will be skipped.

            if (!foundInPass) {
                throwUnknownCodepoint(peekCP);
            }
        }
        popCollectionStack();
        if (!svrcodReceived)
          throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD);
        if (!synerrcdReceived)
          throwMissingRequiredCodepoint("SYNERRCD", CodePoint.SYNERRCD);

//        netAgent_.setSvrcod(svrcod);
        doSyntaxrmSemantics(synerrcd);
    }

    String parseSRVDGN() {
        parseLengthAndMatchCodePoint(CodePoint.SRVDGN);
        if (metadata.isZos())
          return readString(CCSIDConstants.EBCDIC);
        else
          return readString();
    }

    // Syntax Error Code String specifies the condition that caused termination
    // of data stream parsing.
    private int parseSYNERRCD() {
        parseLengthAndMatchCodePoint(CodePoint.SYNERRCD);
        int synerrcd = readUnsignedByte();
        if ((synerrcd < 0x01) || (synerrcd > 0x1D)) {
            doValnsprmSemantics(CodePoint.SYNERRCD, synerrcd);
        }
        return synerrcd;
    }

    // Conversational Protocol Error Reply Message
    // indicates that a conversational protocol error occurred.
    // PROTOCOL architects the SQLSTATE value depending on SVRCOD
    // SVRCOD 8 -> SQLSTATE of 58008 or 58009
    // SVRCOD 16,128 -> SQLSTATE of 58009
    //
    // Messages
    // SQLSTATE : 58009
    //     Execution failed due to a distribution protocol error that caused deallocation of the conversation.
    //     SQLCODE : -30020
    //     Execution failed because of a Distributed Protocol
    //         Error that will affect the successful execution of subsequent
    //         commands and SQL statements: Reason Code .
    //      Some possible reason codes include:
    //      121C Indicates that the user is not authorized to perform the requested command.
    //      1232 The command could not be completed because of a permanent error.
    //          In most cases, the server will be in the process of an abend.
    //      220A The target server has received an invalid data description.
    //          If a user SQLDA is specified, ensure that the fields are
    //          initialized correctly. Also, ensure that the length does not
    //          exceed the maximum allowed length for the data type being used.
    //
    //      The command or statement cannot be processed.  The current
    //      transaction is rolled back and the application is disconnected
    //      from the remote database.
    //
    //
    // Returned from Server:
    // SVRCOD - required  (8 - ERROR, 16 - SEVERE, 128 - SESDMG)
    // PRCCNVCD - required
    // RECCNT - optional (MINVAL 0, MINLVL 3)
    // RDBNAM - optional (NINLVL 3)
    //
    private void parsePRCCNVRM() {
        boolean svrcodReceived = false;
        int svrcod = CodePoint.SVRCOD_INFO;
        boolean rdbnamReceived = false;
        String rdbnam = null;
        boolean prccnvcdReceived = false;
        int prccnvcd = 0;

        parseLengthAndMatchCodePoint(CodePoint.PRCCNVRM);
        pushLengthOnCollectionStack();
        int peekCP = peekCodePoint();

        while (peekCP != END_OF_COLLECTION) {

            boolean foundInPass = false;

            if (peekCP == CodePoint.SVRCOD) {
                foundInPass = true;
                svrcodReceived = checkAndGetReceivedFlag(svrcodReceived);
                svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_SESDMG);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.RDBNAM) {
                foundInPass = true;
                rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
                rdbnam = parseRDBNAM(true);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.PRCCNVCD) {
                foundInPass = true;
                prccnvcdReceived = checkAndGetReceivedFlag(prccnvcdReceived);
                prccnvcd = parsePRCCNVCD();
                peekCP = peekCodePoint();
            }

            if (!foundInPass) {
                throwUnknownCodepoint(peekCP);
            }

        }
        popCollectionStack();
        if (!svrcodReceived)
          throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD);
        if (!prccnvcdReceived)
          throwMissingRequiredCodepoint("PRCCNVCD", CodePoint.PRCCNVCD);

//        netAgent_.setSvrcod(svrcod);
        doPrccnvrmSemantics(CodePoint.PRCCNVRM);
    }

    // The client can detect that a conversational protocol error has occurred.
    // This can also be detected at the server in which case a PRCCNVRM is returned.
    // The Conversation Protocol Error Code, PRCCNVRM, describes the various errors.
    //
    // Note: Not all of these may be valid at the client.  See descriptions for
    // which ones make sense for client side errors/checks.
    // Conversation Error Code                  Description of Error
    // -----------------------                  --------------------
    // 0x01                                     RPYDSS received by target communications manager.
    // 0x02                                     Multiple DSSs sent without chaining or multiple
    //                                          DSS chains sent.
    // 0x03                                     OBJDSS sent when not allowed.
    // 0x04                                     Request correlation identifier of an RQSDSS
    //                                          is less than or equal to the previous
    //                                          RQSDSS's request correlatio identifier in the chain.
    // 0x05                                     Request correlation identifier of an OBJDSS
    //                                          does not equal the request correlation identifier
    //                                          of the preceding RQSDSS.
    // 0x06                                     EXCSAT was not the first command after the connection
    //                                          was established.
    // 0x10                                     ACCSEC or SECCHK command sent in wrong state.
    // 0x11                                     SYNCCTL or SYNCRSY command is used incorrectly.
    // 0x12                                     RDBNAM mismatch between ACCSEC, SECCHK, and ACCRDB.
    // 0x13                                     A command follows one that returned EXTDTAs as reply object.
    //
    // When the client detects these errors, it will be handled as if a PRCCNVRM is returned
    // from the server.  In this PRCCNVRM case, PROTOCOL architects an SQLSTATE of 58008 or 58009
    // depening of the SVRCOD.  In this case, a 58009 will always be returned.
    // Messages
    // SQLSTATE : 58009
    //     Execution failed due to a distribution protocol error that caused deallocation of the conversation.
    //     SQLCODE : -30020
    //     Execution failed because of a Distributed Protocol
    //         Error that will affect the successful execution of subsequent
    //         commands and SQL statements: Reason Code .
    //      Some possible reason codes include:
    //      121C Indicates that the user is not authorized to perform the requested command.
    //      1232 The command could not be completed because of a permanent error.
    //          In most cases, the server will be in the process of an abend.
    //      220A The target server has received an invalid data description.
    //          If a user SQLDA is specified, ensure that the fields are
    //          initialized correctly. Also, ensure that the length does not
    //          exceed the maximum allowed length for the data type being used.
    //
    //      The command or statement cannot be processed.  The current
    //          transaction is rolled back and the application is disconnected
    //          from the remote database.
    private void doPrccnvrmSemantics(int conversationProtocolErrorCode) {
        throw new IllegalStateException("DRDA_CONNECTION_TERMINATED CONN_DRDA_PRCCNVRM " + Integer.toHexString(conversationProtocolErrorCode));
        // we may need to map the conversation protocol error code, prccnvcd, to some kind
        // of reason code.  For now just return the prccnvcd as the reason code
//        agent_.accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(agent_,
//            new ClientMessageId(SQLState.DRDA_CONNECTION_TERMINATED),
//                msgutil_.getTextMessage(MessageId.CONN_DRDA_PRCCNVRM,
//                    Integer.toHexString(conversationProtocolErrorCode))));
    }

    // Conversational Protocol Error Code specifies the condition
    // for which the PRCCNVRm was returned.
    private int parsePRCCNVCD() {
        parseLengthAndMatchCodePoint(CodePoint.PRCCNVCD);
        int prccnvcd = readUnsignedByte();
        if ((prccnvcd != 0x01) && (prccnvcd != 0x02) && (prccnvcd != 0x03) &&
                (prccnvcd != 0x04) && (prccnvcd != 0x05) && (prccnvcd != 0x06) &&
                (prccnvcd != 0x10) && (prccnvcd != 0x11) && (prccnvcd != 0x12) &&
                (prccnvcd != 0x13) && (prccnvcd != 0x15)) {
            doValnsprmSemantics(CodePoint.PRCCNVCD, prccnvcd);
        }
        return prccnvcd;
    }

    // RDB Not Accessed Reply Message indicates that the access relational
    // database command (ACCRDB) was not issued prior to a command
    // requesting the RDB Services.
    // PROTOCOL Architects an SQLSTATE of 58008 or 58009.
    //
    // Messages
    // SQLSTATE : 58009
    //     Execution failed due to a distribution protocol error that caused deallocation of the conversation.
    //     SQLCODE : -30020
    //     Execution failed because of a Distributed Protocol
    //         Error that will affect the successful execution of subsequent
    //         commands and SQL statements: Reason Code .
    //      Some possible reason codes include:
    //      121C Indicates that the user is not authorized to perform the requested command.
    //      1232 The command could not be completed because of a permanent error.
    //          In most cases, the server will be in the process of an abend.
    //      220A The target server has received an invalid data description.
    //          If a user SQLDA is specified, ensure that the fields are
    //          initialized correctly. Also, ensure that the length does not
    //          exceed the maximum allowed length for the data type being used.
    //
    //      The command or statement cannot be processed.  The current
    //      transaction is rolled back and the application is disconnected
    //      from the remote database.
    //
    //
    // Returned from Server:
    // SVRCOD - required  (8 - ERROR)
    // RDBNAM - required
    //
    // Called by all the NET*Reply classes.
    void parseRDBNACRM() {
        boolean svrcodReceived = false;
        int svrcod = CodePoint.SVRCOD_INFO;
        boolean rdbnamReceived = false;
        String rdbnam = null;

        parseLengthAndMatchCodePoint(CodePoint.RDBNACRM);
        pushLengthOnCollectionStack();
        int peekCP = peekCodePoint();

        while (peekCP != END_OF_COLLECTION) {

            boolean foundInPass = false;

            if (peekCP == CodePoint.SVRCOD) {
                foundInPass = true;
                svrcodReceived = checkAndGetReceivedFlag(svrcodReceived);
                svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.RDBNAM) {
                foundInPass = true;
                rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
                rdbnam = parseRDBNAM(true);
                peekCP = peekCodePoint();
            }

            if (!foundInPass) {
                throwUnknownCodepoint(peekCP);
            }

        }
        popCollectionStack();
        if (!svrcodReceived)
            throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD);
        if (!rdbnamReceived)
            throwMissingRequiredCodepoint("RDBNAM", CodePoint.RDBNAM);

//        netAgent_.setSvrcod(svrcod);
        throw new IllegalStateException("SQLState.DRDA_CONNECTION_TERMINATED");
//        agent_.accumulateChainBreakingReadExceptionAndThrow(
//            new DisconnectException(agent_,
//                new ClientMessageId(SQLState.DRDA_CONNECTION_TERMINATED),
//                msgutil_.getTextMessage(MessageId.CONN_DRDA_RDBNACRM)));
    }


    // Command Check Reply Message indicates that the requested
    // command encountered an unarchitected and implementation-specific
    // condition for which there is no architected message.  If the severity
    // code value is ERROR or greater, the command has failed.  The
    // message can be accompanied by other messages that help to identify
    // the specific condition.
    // The CMDCHKRM should not be used as a general catch-all in place of
    // product-defined messages when using product extensions to DDM.
    // PROTOCOL architects the SQLSTATE value depending on SVRCOD
    // SVRCOD 0 -> SQLSTATE is not returned
    // SVRCOD 8 -> SQLSTATE of 58008 or 58009
    // SVRCOD 16,32,64,128 -> SQLSTATE of 58009
    //
    // Messages
    //   SQLSTATE : 58009
    //     Execution failed due to a distribution protocol error that caused deallocation of the conversation.
    //     SQLCODE : -30020
    //     Execution failed because of a Distributed Protocol
    //       Error that will affect the successful execution of subsequent
    //       commands and SQL statements: Reason Code .
    //     Some possible reason codes include:
    //       121C Indicates that the user is not authorized to perform the requested command.
    //       1232 The command could not be completed because of a permanent error.
    //         In most cases, the server will be in the process of an abend.
    //       220A The target server has received an invalid data description.
    //         If a user SQLDA is specified, ensure that the fields are
    //         initialized correctly. Also, ensure that the length does not
    //         exceed the maximum allowed length for the data type being used.
    //
    // The command or statement cannot be processed.  The current
    // transaction is rolled back and the application is disconnected
    //  from the remote database.
    //
    //
    // Returned from Server:
    //   SVRCOD - required  (0 - INFO, 4 - WARNING, 8 - ERROR, 16 - SEVERE,
    //                       32 - ACCDMG, 64 - PRMDMG, 128 - SESDMG))
    //   RDBNAM - optional (MINLVL 3)
    //   RECCNT - optional (MINVAL 0, MINLVL 3)
    //
    // Called by all the Reply classesCMDCHKRM
    void parseCMDCHKRM() {
        boolean svrcodReceived = false;
        int svrcod = CodePoint.SVRCOD_INFO;
        boolean rdbnamReceived = false;
        String rdbnam = null;
        parseLengthAndMatchCodePoint(CodePoint.CMDCHKRM);
        pushLengthOnCollectionStack();
        int peekCP = peekCodePoint();

        while (peekCP != END_OF_COLLECTION) {

            boolean foundInPass = false;

            if (peekCP == CodePoint.SVRCOD) {
                foundInPass = true;
                svrcodReceived = checkAndGetReceivedFlag(svrcodReceived);
                svrcod = parseSVRCOD(CodePoint.SVRCOD_INFO, CodePoint.SVRCOD_SESDMG);
                peekCP = peekCodePoint();
            }

            if (peekCP == CodePoint.RDBNAM) {
                foundInPass = true;
                rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
                rdbnam = parseRDBNAM(true);
                peekCP = peekCodePoint();
            }
            // skip over the RECCNT since it can't be found in the DDM book.

            if (peekCP == 0x115C) {
                foundInPass = true;
                parseLengthAndMatchCodePoint(0x115C);
                skipBytes();
                peekCP = peekCodePoint();
            }

            if (!foundInPass) {
                throwUnknownCodepoint(peekCP);
            }

        }
        popCollectionStack();
        if (!svrcodReceived)
            throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD);

//        netAgent_.setSvrcod(svrcod);
        NetSqlca netSqlca = parseSQLCARD(null);
//        netAgent_.netConnection_.completeSqlca(netSqlca);

        throw new IllegalStateException("SQLState.DRDA_CONNECTION_TERMINATED");
//        agent_.accumulateChainBreakingReadExceptionAndThrow(
//            new DisconnectException(
//                agent_,
//                new ClientMessageId(SQLState.DRDA_CONNECTION_TERMINATED),
//                new SqlException(agent_.logWriter_, netSqlca),
//                msgutil_.getTextMessage(MessageId.CONN_DRDA_CMDCHKRM)));
    }

    // Parse the reply for the Access RDB Command.
    // This method handles the parsing of all command replies and reply data
    // for the accrdb command.
    private RDBAccessData parseACCRDBreply() {
        int peekCP = peekCodePoint();
        if (peekCP != CodePoint.ACCRDBRM) {
            throw new IllegalStateException("Expected state ACCRDBRM but got " + Integer.toHexString(peekCP));
        }

        RDBAccessData accessData = parseACCRDBRM();
        parseInitialPBSD();
        peekCP = peekCodePoint();
        if (peekCP == END_OF_SAME_ID_CHAIN) {
            return accessData;
        }

        parseTypdefsOrMgrlvlovrs();
        NetSqlca netSqlca = parseSQLCARD(null);
        NetSqlca.complete(netSqlca);
        return accessData;
    }



    /**
    * Parse the initial PBSD - PiggyBackedSessionData code point.
    * 

* If sent by the server, it contains a PBSD_ISO code point followed by a * byte representing the JDBC isolation level, and a PBSD_SCHEMA code point * followed by the name of the current schema as an UTF-8 String. */ private void parseInitialPBSD() { if (peekCodePoint() != CodePoint.PBSD) { return; } parseLengthAndMatchCodePoint(CodePoint.PBSD); int peekCP = peekCodePoint(); while (peekCP != END_OF_SAME_ID_CHAIN) { parseLengthAndMatchCodePoint(peekCP); switch (peekCP) { case CodePoint.PBSD_ISO: int isolationLevel = readUnsignedByte(); if (isolationLevel != Connection.TRANSACTION_READ_COMMITTED) throw new IllegalStateException("Database using unsupported transaction isolation level: " + isolationLevel); // netAgent_.netConnection_. // completeInitialPiggyBackIsolation(readUnsignedByte()); break; case CodePoint.PBSD_SCHEMA: String pbSchema = readString(getDdmLength(), CCSIDConstants.UTF8); // netAgent_.netConnection_. // completeInitialPiggyBackSchema // (readString(getDdmLength(), Typdef.UTF8ENCODING)); break; default: throw new IllegalStateException("Found unknown codepoint: " + Integer.toHexString(peekCP)); } peekCP = peekCodePoint(); } } // Access to RDB Completed (ACRDBRM) Reply Message specifies that an // instance of the SQL application manager has been created and is bound // to the specified relation database (RDB). // // Returned from Server: // SVRCOD - required (0 - INFO, 4 - WARNING) // PRDID - required // TYPDEFNAM - required (MINLVL 4) (QTDSQLJVM) // TYPDEFOVR - required // RDBINTTKN - optional // CRRTKN - optional // USRID - optional // SRVLST - optional (MINLVL 5) private RDBAccessData parseACCRDBRM() { boolean svrcodReceived = false; int svrcod = CodePoint.SVRCOD_INFO; boolean prdidReceived = false; String prdid = null; boolean typdefnamReceived = false; boolean typdefovrReceived = false; boolean rdbinttknReceived = false; boolean crrtknReceived = false; byte[] crrtkn = null; boolean usridReceived = false; String usrid = null; parseLengthAndMatchCodePoint(CodePoint.ACCRDBRM); pushLengthOnCollectionStack(); int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { boolean foundInPass = false; if (peekCP == CodePoint.SVRCOD) { // severity code. If the target SQLAM cannot support the typdefovr // parameter values specified for the double-byte and mixed-byte CCSIDs // on the corresponding ACCRDB command, then the severity code WARNING // is specified on the ACCRDBRM. foundInPass = true; svrcodReceived = checkAndGetReceivedFlag(svrcodReceived); svrcod = parseSVRCOD(CodePoint.SVRCOD_INFO, CodePoint.SVRCOD_WARNING); peekCP = peekCodePoint(); } // this is the product release level of the target RDB server. if (peekCP == CodePoint.PRDID) { foundInPass = true; prdidReceived = checkAndGetReceivedFlag(prdidReceived); prdid = parsePRDID(false); // false means do not skip the bytes peekCP = peekCodePoint(); } if (peekCP == CodePoint.TYPDEFNAM) { // this is the name of the data type to the data representation mapping // definitions tha the target SQLAM uses when sending reply data objects. foundInPass = true; typdefnamReceived = checkAndGetReceivedFlag(typdefnamReceived); parseTYPDEFNAM(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.TYPDEFOVR) { // this is the single-byte, double-byte, and mixed-byte CCSIDs of the // scalar data arrays (SDA) in the identified data type to data representation // mapping definitions. foundInPass = true; typdefovrReceived = checkAndGetReceivedFlag(typdefovrReceived); parseTYPDEFOVR(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.RDBINTTKN) { // @AGG added manually foundInPass = true; rdbinttknReceived = checkAndGetReceivedFlag(rdbinttknReceived); parseRDBINTTKN(false); peekCP = peekCodePoint(); } if (peekCP == CodePoint.USRID) { // specifies the target defined user ID. It is returned if the value of // TRGDFTRT is TRUE in ACCRDB. Right now this driver always sets this // value to false so this should never get returned here. // if it is returned, it could be considered an error but for now // this driver will just skip the bytes. foundInPass = true; usridReceived = checkAndGetReceivedFlag(usridReceived); usrid = parseUSRID(true); peekCP = peekCodePoint(); } if (peekCP == CodePoint.CRRTKN) { // carries information to correlate with the work being done on bahalf // of an application at the source and at the target server. // defualt value is ''. // this parameter is only retunred if an only if the CRRTKN parameter // is not received on ACCRDB. We will rely on server to send us this // in ACCRDBRM foundInPass = true; crrtknReceived = checkAndGetReceivedFlag(crrtknReceived); crrtkn = parseCRRTKN(false); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SRVLST) { foundInPass = true; parseSRVLST(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.IPADDR) { foundInPass = true; parseIPADDR(); peekCP = peekCodePoint(); } if (!foundInPass) { throwUnknownCodepoint(peekCP); //doPrmnsprmSemantics(peekCP); } } popCollectionStack(); // check for the required instance variables. if (!svrcodReceived) throw new IllegalStateException("Did not find codepoint SVRCOD in reply data"); if (!prdidReceived) throw new IllegalStateException("Did not find codepoint PRDID in reply data"); if (!typdefnamReceived) throw new IllegalStateException("Did not find codepoint TYPDEFNAM in reply data"); if (!typdefovrReceived) throw new IllegalStateException("Did not find codepoint TYPDEFOVR in reply data"); // checkRequiredObjects(svrcodReceived, // prdidReceived, // typdefnamReceived, // typdefovrReceived); // rdbAccessed(svrcod, // prdid, // crrtknReceived, // crrtkn); return new RDBAccessData(svrcod, prdid, crrtknReceived, crrtkn); } public static class RDBAccessData { public final int svrcod; public final String prdid; public final byte[] correlationToken; public RDBAccessData(int svrcod, String prdid, boolean crrtknReceived, byte[] crrtkn) { this.svrcod = svrcod; this.prdid = prdid; correlationToken = crrtknReceived ? crrtkn : null; } } private byte[] parseRDBINTTKN(boolean skip) { parseLengthAndMatchCodePoint(CodePoint.RDBINTTKN); if (skip) { skipBytes(); return null; } return readBytes(); } // Correlation Token specifies a token that is conveyed between source // and target servers for correlating the processing between servers. private byte[] parseCRRTKN(boolean skip) { parseLengthAndMatchCodePoint(CodePoint.CRRTKN); if (skip) { skipBytes(); return null; } return readBytes(); } private void parseSRVLST() { parseLengthAndMatchCodePoint(CodePoint.SRVLST); pushLengthOnCollectionStack(); boolean foundInPass = false; boolean foundServerListCount = false; boolean foundServerList = false; int serverListCount = 0; int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { if (peekCP == CodePoint.SRVLSTCNT) { foundInPass = true; foundServerListCount = true; serverListCount = parseSRVLSTCNT(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SRVLSRV) { foundInPass = true; foundServerList = true; // TODO: utilize returned server list for failover/client reroute feature parseSRVLSRV(serverListCount); peekCP = peekCodePoint(); } if (!foundInPass) throwUnknownCodepoint(peekCP); } popCollectionStack(); if (!foundServerListCount) throwMissingRequiredCodepoint("SRVLSTCNT", CodePoint.SRVLSTCNT); if (!foundServerList) throwMissingRequiredCodepoint("SRVLSRV", CodePoint.SRVLSRV); } private int parseSRVLSTCNT() { parseLengthAndMatchCodePoint(CodePoint.SRVLSTCNT); return readUnsignedShort(); } private List parseSRVLSRV(int serverListCount) { parseLengthAndMatchCodePoint(CodePoint.SRVLSRV); List serverList = new ArrayList<>(serverListCount); for (int i = 0; i < serverListCount; i++) { int priority = 0; boolean foundServerPriority = false; int peekCP = peekCodePoint(); if (peekCP == CodePoint.SRVPRTY) { foundServerPriority = true; priority = parseSRVPRTY(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.TCPPORTHOST) { parseTCPPORTHOST(false); } else if (peekCP == CodePoint.IPADDR) { parseIPADDR(); } else { throwUnknownCodepoint(peekCP); } if (!foundServerPriority) throwMissingRequiredCodepoint("SRVPRTY", CodePoint.SRVPRTY); } return serverList; } private byte[] parseIPADDR() { parseLengthAndMatchCodePoint(CodePoint.IPADDR); return readBytes(); } private Object[] parseTCPPORTHOST(boolean skip) { parseLengthAndMatchCodePoint(CodePoint.TCPPORTHOST); if (skip) { skipBytes(); return null; } Object[] hostPort = new Object[2]; hostPort[0] = readUnsignedShort(); hostPort[1] = readString(); return hostPort; } private int parseSRVPRTY() { parseLengthAndMatchCodePoint(CodePoint.SRVPRTY); return readUnsignedShort(); } // The User Id specifies an end-user name. private String parseUSRID(boolean skip) { parseLengthAndMatchCodePoint(CodePoint.USRID); if (skip) { skipBytes(); return null; } return readString(); }; // Product specific Identifier specifies the product release level // of a DDM server. private String parsePRDID(boolean skip) { parseLengthAndMatchCodePoint(CodePoint.PRDID); if (skip) { skipBytes(); return null; } else { return readString(); } } // Parse the reply for the Security Check Command. // This method handles the parsing of all command replies and reply data // for the secchk command. private void parseSECCHKreply() { int peekCP = peekCodePoint(); if (peekCP != CodePoint.SECCHKRM) { // throwUnknownCodepoint(peekCP); parseCommonError(peekCP); } parseSECCHKRM(); if (peekCodePoint() == CodePoint.SECTKN) { // rpydta used only if the security mechanism returns // a security token that must be sent back to the source system. // this is only used for DCSSEC. In the case of DCESEC, // the sectkn must be returned as reply data if DCE is using // mutual authentication. // Need to double check what to map this to. This is probably // incorrect but consider it a conversation protocol error // 0x03 - OBJDSS sent when not allowed. //parseSECTKN (true); parseSECTKN(false); } } // The Security Check (SECCHKRM) Reply Message indicates the acceptability // of the security information. // This method throws an exception if the connection was not established // It is up to the caller to catch this exception and take the appropriate action. // // Returned from Server: // SVRCOD - required (0 - INFO, 8 - ERROR, 16 -SEVERE) // SECCHKCD - required // SECTKN - optional, ignorable // SVCERRNO - optional private void parseSECCHKRM() { boolean svrcodReceived = false; int svrcod = CodePoint.SVRCOD_INFO; boolean secchkcdReceived = false; int secchkcd = CodePoint.SECCHKCD_00; boolean sectknReceived = false; byte[] sectkn = null; parseLengthAndMatchCodePoint(CodePoint.SECCHKRM); pushLengthOnCollectionStack(); int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { boolean foundInPass = false; if (peekCP == CodePoint.SVRCOD) { // severity code. it's value is dictated by the SECCHKCD. // right now it will not be checked that it is the correct value // for the SECCHKCD. maybe this will be done in the future. foundInPass = true; svrcodReceived = checkAndGetReceivedFlag(svrcodReceived); svrcod = parseSVRCOD(CodePoint.SVRCOD_INFO, CodePoint.SVRCOD_SEVERE); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SECCHKCD) { // security check code. this specifies the state of the security information. // there is a relationship between this value and the SVRCOD value. // right now this driver will not check these values against each other. foundInPass = true; secchkcdReceived = checkAndGetReceivedFlag(secchkcdReceived); secchkcd = parseSECCHKCD(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SECTKN) { // security token. // used when mutual authentication of the source and target servers // is requested. The architecture lists this as an instance variable // and also says that the SECTKN flows as reply data to the secchk cmd and // it must flow after the secchkrm message. Right now this driver doesn't // support ay mutual authentication so it will be ignored (it is listed // as an ignorable instance variable in the ddm manual). foundInPass = true; sectknReceived = checkAndGetReceivedFlag(sectknReceived); sectkn = parseSECTKN(true); peekCP = peekCodePoint(); } if (!foundInPass) { throw new IllegalStateException("Found unexpected codepoint: " + peekCP); } } popCollectionStack(); // check for the required instance variables. if (!svrcodReceived) throw new IllegalStateException("Did not receive SVRCOD codepoint"); if (!secchkcdReceived) throw new IllegalStateException("Did not receive SECCHKCD codepoint"); // checkRequiredObjects(svrcodReceived, secchkcdReceived); // netConnection.securityCheckComplete(svrcod, secchkcd); switch (secchkcd) { // Security information accepted case CodePoint.SECCHKCD_00: break; // Missing userid - TODO We should catch and handle this issue *before* the call to the DB2 server case CodePoint.SECCHKCD_12: // Using SQL error code and state values from JDBC throw new DB2Exception("Missing userid, verify a user value was supplied", SqlCode.MISSING_CREDENTIALS, SQLState.CONNECT_USERID_ISNULL); // Missing password - TODO We should catch and handle this issue *before* the call to the DB2 server case CodePoint.SECCHKCD_10: // Using SQL error code and state values from similar JDBC response throw new DB2Exception("Missing password, verify a password value was supplied", SqlCode.MISSING_CREDENTIALS, SQLState.CONNECT_PASSWORD_ISNULL); // Invalid credentials case CodePoint.SECCHKCD_0E: case CodePoint.SECCHKCD_0F: case CodePoint.SECCHKCD_13: case CodePoint.SECCHKCD_14: case CodePoint.SECCHKCD_15: // Using SQL error code and state values from similar JDBC response for consistency throw new DB2Exception("Invalid credentials, verify the user and password values supplied are correct", SqlCode.INVALID_CREDENTIALS, SQLState.NET_CONNECT_AUTH_FAILED); default: throw new IllegalArgumentException("Authentication failed"); } } private void parseACCSECreply(int securityMechanism) { int peekCP = peekCodePoint(); if (peekCP != CodePoint.ACCSECRD) { parseAccessSecurityError(); } parseACCSECRD(securityMechanism); // peekCP = peekCodePoint(); // if (SanityManager.DEBUG) { // if (peekCP != Reply.END_OF_SAME_ID_CHAIN) { // SanityManager.THROWASSERT("expected END_OF_SAME_ID_CHAIN"); // } // } } private void parseAccessSecurityError() { int peekCP = peekCodePoint(); switch (peekCP) { case CodePoint.CMDCHKRM: parseCMDCHKRM(); break; case CodePoint.RDBNFNRM: parseRDBNFNRM(); break; case CodePoint.RDBAFLRM: parseRdbAccessFailed(); break; default: parseCommonError(peekCP); } } // RDB Not Found Reply Message indicates that the target // server cannot find the specified relational database. // PROTOCOL architects an SQLSTATE of 08004. // // Messages // SQLSTATE : 8004 // The application server rejected establishment of the connection. // SQLCODE : -30061 // The database alias or database name was not found at the remote node. // The statement cannot be processed. // // // Returned from Server: // SVRCOD - required (8 - ERROR) // RDBNAM - required private void parseRDBNFNRM() { boolean svrcodReceived = false; int svrcod = CodePoint.SVRCOD_INFO; boolean rdbnamReceived = false; String rdbnam = null; parseLengthAndMatchCodePoint(CodePoint.RDBNFNRM); pushLengthOnCollectionStack(); int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { boolean foundInPass = false; if (peekCP == CodePoint.SVRCOD) { foundInPass = true; svrcodReceived = checkAndGetReceivedFlag(svrcodReceived); svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR); peekCP = peekCodePoint(); } if (peekCP == CodePoint.RDBNAM) { foundInPass = true; rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived); rdbnam = parseRDBNAM(true); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SRVDGN) { foundInPass = true; String serverDiagnostics = parseSRVDGN(); // TODO: @AGG Log the server diagnostics here peekCP = peekCodePoint(); } if (!foundInPass) { throwUnknownCodepoint(peekCP); } } popCollectionStack(); if (!svrcodReceived) throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD); if (!rdbnamReceived) throwMissingRequiredCodepoint("RDBNAM", CodePoint.RDBNAM); // netAgent_.setSvrcod(svrcod); throw new DB2Exception("The requested database was not found: " + metadata.databaseName, SqlCode.RDB_NOT_FOUND, SQLState.NET_DATABASE_NOT_FOUND); // agent_.accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(agent_, // new ClientMessageId(SQLState.NET_DATABASE_NOT_FOUND), // netConnection.databaseName_)); } private void parseRdbAccessFailed() { parseRDBAFLRM(); // an SQLCARD is returned if an RDBALFRM is returned. // this SQLCARD always follows the RDBALFRM. // TYPDEFNAM and TYPDEFOVR are MTLINC if (peekCodePoint() == CodePoint.TYPDEFNAM) { parseTYPDEFNAM(); parseTYPDEFOVR(); } else { parseTYPDEFOVR(); parseTYPDEFNAM(); } NetSqlca netSqlca = parseSQLCARD(null); //Check if the SQLCARD has null SQLException if(netSqlca.getSqlErrmc() == null) { // netConnection.setConnectionNull(true); } else { NetSqlca.complete(netSqlca); } } // RDB Access Failed Reply Message specifies that the relational // database failed the attempted connection. // An SQLCARD object must also be returned, following the // RDBAFLRM, to explain why the RDB failed the connection. // In addition, the target SQLAM instance is destroyed. // The SQLSTATE is returned in the SQLCARD. // // Messages // SQLSTATE : 58009 // Execution failed due to a distribution protocol error that caused deallocation of the conversation. // SQLCODE : -30020 // Execution failed because of a Distributed Protocol // Error that will affect the successful execution of subsequent // commands and SQL statements: Reason Code . // Some possible reason codes include: // 121C Indicates that the user is not authorized to perform the requested command. // 1232 The command could not be completed because of a permanent error. // In most cases, the server will be in the process of an abend. // 220A The target server has received an invalid data description. // If a user SQLDA is specified, ensure that the fields are // initialized correctly. Also, ensure that the length does not // exceed the maximum allowed length for the data type being used. // // The command or statement cannot be processed. The current // transaction is rolled back and the application is disconnected // from the remote database. // // // Returned from Server: // SVRCOD - required (8 - ERROR) // RDBNAM - required // SRVDGN - optional // private void parseRDBAFLRM() { boolean svrcodReceived = false; int svrcod = CodePoint.SVRCOD_INFO; boolean rdbnamReceived = false; boolean srvdgnReceived = false; String rdbnam = null; String serverDiagnostics = null; parseLengthAndMatchCodePoint(CodePoint.RDBAFLRM); pushLengthOnCollectionStack(); int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { boolean foundInPass = false; if (peekCP == CodePoint.SVRCOD) { foundInPass = true; svrcodReceived = checkAndGetReceivedFlag(svrcodReceived); svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR); peekCP = peekCodePoint(); } if (peekCP == CodePoint.RDBNAM) { foundInPass = true; rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived); rdbnam = parseRDBNAM(true); peekCP = peekCodePoint(); } // Optional code point if (peekCP == CodePoint.SRVDGN) { foundInPass = true; srvdgnReceived = checkAndGetReceivedFlag(srvdgnReceived); serverDiagnostics = parseSRVDGN(); peekCP = peekCodePoint(); } if (!foundInPass) { throwUnknownCodepoint(peekCP); } } popCollectionStack(); if (!svrcodReceived) throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD); if (!rdbnamReceived) throwMissingRequiredCodepoint("RDBNAM", CodePoint.RDBNAM); // netAgent_.setSvrcod(svrcod); } // The Access Security Reply Data (ACSECRD) Collection Object contains // the security information from a target server's security manager. // this method returns the security check code received from the server // (if the server does not return a security check code, this method // will return 0). it is up to the caller to check // the value of this return code and take the appropriate action. // // Returned from Server: // SECMEC - required // SECTKN - optional (MINLVL 6) // SECCHKCD - optional private void parseACCSECRD(int securityMechanism) { boolean secmecReceived = false; int[] secmecList = null; boolean sectknReceived = false; byte[] sectkn = null; boolean secchkcdReceived = false; int secchkcd = 0; parseLengthAndMatchCodePoint(CodePoint.ACCSECRD); pushLengthOnCollectionStack(); int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { boolean foundInPass = false; if (peekCP == CodePoint.SECMEC) { // security mechanism. // this value must either reflect the value sent in the ACCSEC command // if the target server supports it; or the values the target server // does support when it does not support or accept the value // requested by the source server. // the secmecs returned are treated as a list and stored in // targetSecmec_List. // if the target server supports the source's secmec, it // will be saved in the variable targetSecmec_ (NOTE: so // after calling this method, if targetSecmec_'s value is zero, // then the target did NOT support the source secmec. any alternate // secmecs would be contained in targetSecmec_List). foundInPass = true; secmecReceived = checkAndGetReceivedFlag(secmecReceived); secmecList = parseSECMEC(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SECTKN) { // security token foundInPass = true; sectknReceived = checkAndGetReceivedFlag(sectknReceived); sectkn = parseSECTKN(false); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SECCHKCD) { // security check code. // included if and only if an error is detected when processing // the ACCSEC command. this has an implied severity code // of ERROR. foundInPass = true; secchkcdReceived = checkAndGetReceivedFlag(secchkcdReceived); secchkcd = parseSECCHKCD(); peekCP = peekCodePoint(); } if (!foundInPass) { throw new IllegalStateException("Found invalid codepoint: " + Integer.toHexString(peekCP)); //doPrmnsprmSemantics(peekCP); } } popCollectionStack(); if (!secmecReceived) throw new IllegalStateException("Did not receive SECMEC flag in response"); // checkRequiredObjects(secmecReceived); // TODO: port this method to validate secMec received is same as requested one setAccessSecurityData(secchkcd, securityMechanism, secmecList, sectknReceived, sectkn); /* Switch to UTF-8 or EBCDIC managers depending on what's supported */ // if (netConnection.serverSupportsUtf8Ccsid()) { // netConnection.netAgent_.switchToUtf8CcsidMgr(); // } else { // netConnection.netAgent_.switchToEbcdicMgr(); // } // ccsidManager.setCCSID(CCSIDManager.UTF8); // TODO @AGG should be switching to UTF8 here? } // secmecList is always required and will not be null. // secchkcd has an implied severity of error. // it will be returned if an error is detected. // if no errors and security mechanism requires a sectkn, then void setAccessSecurityData(int secchkcd, int desiredSecmec, int[] secmecList, boolean sectknReceived, byte[] sectkn) { // @AGG this method was originally on NetConnection // - if the secchkcd is not 0, then map to an exception. if (secchkcd != CodePoint.SECCHKCD_00) { // the implied severity code is error throw new IllegalStateException("Got nonzero SECCHKCD codepoint: " + secchkcd); // netAgent_.setSvrcod(CodePoint.SVRCOD_ERROR); // agent_.accumulateReadException(mapSecchkcd(secchkcd)); } else { // - verify that the secmec parameter reflects the value sent // in the ACCSEC command. // should we check for null list if ((secmecList.length == 1) && (secmecList[0] == desiredSecmec)) { // the security mechanism returned from the server matches // the mechanism requested by the client. // targetSecmec_ = secmecList[0]; if ((desiredSecmec == DRDAConstants.SECMEC_USRENCPWD) || (desiredSecmec == DRDAConstants.SECMEC_EUSRIDPWD) || (desiredSecmec == DRDAConstants.SECMEC_USRSSBPWD) || (desiredSecmec == DRDAConstants.SECMEC_EUSRIDDTA) || (desiredSecmec == DRDAConstants.SECMEC_EUSRPWDDTA)) { // a security token is required for USRENCPWD, or EUSRIDPWD. if (!sectknReceived) { throw new IllegalStateException("SQLState.NET_SECTKN_NOT_RETURNED"); // agent_.accumulateChainBreakingReadExceptionAndThrow( // new DisconnectException(agent_, // new ClientMessageId(SQLState.NET_SECTKN_NOT_RETURNED))); } else { throw new UnsupportedOperationException(); // if (desiredSecmec == NetConfiguration.SECMEC_USRSSBPWD) // targetSeed_ = sectkn; // else // targetPublicKey_ = sectkn; // if (encryptionManager_ != null) { // encryptionManager_.resetSecurityKeys(); // } } } } else { // accumulate an SqlException and don't disconnect yet // if a SECCHK was chained after this it would receive a secchk code // indicating the security mechanism wasn't supported and that would be a // chain breaking exception. if no SECCHK is chained this exception // will be surfaced by endReadChain // agent_.accumulateChainBreakingReadExceptionAndThrow ( // new DisconnectException (agent_,"secmec not supported ","0000", -999)); throw new IllegalStateException("SQLState.NET_SECKTKN_NOT_RETURNED"); // agent_.accumulateReadException(new SqlException(agent_.logWriter_, // new ClientMessageId(SQLState.NET_SECKTKN_NOT_RETURNED))); } } } // The Security Check Code String codifies the security information // and condition for the SECCHKRM. private int parseSECCHKCD() { parseLengthAndMatchCodePoint(CodePoint.SECCHKCD); int secchkcd = readUnsignedByte(); if ((secchkcd < CodePoint.SECCHKCD_00) || (secchkcd > CodePoint.SECCHKCD_15)) { doValnsprmSemantics(CodePoint.SECCHKCD, secchkcd); } // @AGG remove this after fixing PeekCP() ? //adjustLengths(1); // @AGG added this after some debugging return secchkcd; } // The Security Token Byte String is information provided and used // by the various security mechanisms. private byte[] parseSECTKN(boolean skip) { parseLengthAndMatchCodePoint(CodePoint.SECTKN); if (skip) { skipBytes(); return null; } return readBytes(); } // Security Mechanims. private int[] parseSECMEC() { parseLengthAndMatchCodePoint(CodePoint.SECMEC); return readUnsignedShortList(); } // Parse the reply for the Exchange Server Attributes Command. // This method handles the parsing of all command replies and reply data // for the excsat command. private void parseEXCSATreply() { if (peekCodePoint() != CodePoint.EXCSATRD) { parseExchangeServerAttributesError(); return; } parseEXCSATRD(); } private void parseExchangeServerAttributesError() { int peekCP = peekCodePoint(); throw new IllegalStateException(String.format("Invalid codepoint: %02x", peekCP)); // switch (peekCP) { // case CodePoint.CMDCHKRM: // parseCMDCHKRM(); // break; // case CodePoint.MGRLVLRM: // parseMGRLVLRM(); // break; // default: // parseCommonError(peekCP); // } } // Command Not Supported Reply Message indicates that the specified // command is not recognized or not supported for the // specified target. The reply message can be returned // only in accordance with the architected rules for DDM subsetting. // PROTOCOL architects an SQLSTATE of 58014. // // Messages // SQLSTATE : 58014 // The DDM command is not supported. // SQLCODE : -30070 // Command is not supported. // The current transaction is rolled back and the application is // disconnected from the remote database. The statement cannot be processed. // // // Returned from Server: // SVRCOD - required (4 - WARNING, 8 - ERROR) (MINLVL 2) // CODPNT - required // RDBNAM - optional (MINLVL 3) // void parseCMDNSPRM() { boolean svrcodReceived = false; int svrcod = CodePoint.SVRCOD_INFO; boolean rdbnamReceived = false; String rdbnam = null; boolean codpntReceived = false; int codpnt = 0; parseLengthAndMatchCodePoint(CodePoint.CMDNSPRM); pushLengthOnCollectionStack(); int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { boolean foundInPass = false; if (peekCP == CodePoint.SVRCOD) { foundInPass = true; svrcodReceived = checkAndGetReceivedFlag(svrcodReceived); svrcod = parseSVRCOD(CodePoint.SVRCOD_WARNING, CodePoint.SVRCOD_ERROR); peekCP = peekCodePoint(); } if (peekCP == CodePoint.RDBNAM) { foundInPass = true; rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived); rdbnam = parseRDBNAM(true); peekCP = peekCodePoint(); } if (peekCP == CodePoint.CODPNT) { foundInPass = true; codpntReceived = checkAndGetReceivedFlag(codpntReceived); codpnt = parseCODPNT(); peekCP = peekCodePoint(); } if (!foundInPass) { throwUnknownCodepoint(peekCP); } } popCollectionStack(); if (!svrcodReceived) throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD); if (!codpntReceived) throwMissingRequiredCodepoint("CODPNT", CodePoint.CODPNT); // netAgent_.setSvrcod(svrcod); // agent_.accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(agent_, // new ClientMessageId(SQLState.DRDA_DDM_COMMAND_NOT_SUPPORTED), // Integer.toHexString(codpnt))); throw new IllegalStateException("DRDA_DDM_COMMAND_NOT_SUPPORTED: " + Integer.toHexString(codpnt)); } // The Code Point Data specifies a scalar value that is an architected code point. private int parseCODPNT() { parseLengthAndMatchCodePoint(CodePoint.CODPNT); return parseCODPNTDR(); } // Code Point Data Representation specifies the data representation // of a dictionary codepoint. Code points are hexadecimal aliases for DDM // named terms. private int parseCODPNTDR() { return readUnsignedShort(); } // The Server Attributes Reply Data (EXCSATRD) returns the following // information in response to an EXCSAT command: // - the target server's class name // - the target server's support level for each class of manager // the source requests // - the target server's product release level // - the target server's external name // - the target server's name // // Returned from Server: // EXTNAM - optional // MGRLVLLS - optional // SRVCLSNM - optional // SRVNAM - optional // SRVRLSLV - optional private void parseEXCSATRD() { boolean extnamReceived = false; boolean mgrlvllsReceived = false; boolean srvclsnmReceived = false; String srvclsnm = null; boolean srvnamReceived = false; boolean srvrlslvReceived = false; parseLengthAndMatchCodePoint(CodePoint.EXCSATRD); pushLengthOnCollectionStack(); int peekCP = peekCodePoint(); while (peekCP != END_OF_COLLECTION) { boolean foundInPass = false; if (peekCP == CodePoint.EXTNAM) { // External Name is the name of the job, task, or process // on a system for which a DDM server is active. For a target // DDM server, the external name is the name of the job the system creates // or activates to run the DDM server. // No semantic meaning is assigned to external names in DDM. // External names are transmitted to aid in problem determination. foundInPass = true; extnamReceived = checkAndGetReceivedFlag(extnamReceived); parseLengthAndMatchCodePoint(CodePoint.EXTNAM); String extnam = readString(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.MGRLVLLS) { // Manager-Level List // specifies a list of code points and support levels for the // classes of managers a server supports foundInPass = true; mgrlvllsReceived = checkAndGetReceivedFlag(mgrlvllsReceived); parseMGRLVLLS(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SRVCLSNM) { // Server Class Name // specifies the name of a class of ddm servers. foundInPass = true; srvclsnmReceived = checkAndGetReceivedFlag(srvclsnmReceived); parseLengthAndMatchCodePoint(CodePoint.SRVCLSNM); srvclsnm = readString(); // parseSRVCLSNM(); peekCP = peekCodePoint(); } if (peekCP == CodePoint.SRVNAM) { // Server Name // no semantic meaning is assigned to server names in DDM, // it is recommended (by the DDM manual) that the server's // physical or logical location identifier be used as a server name. // server names are transmitted for problem determination purposes. // this driver will save this name and in the future may use it // for logging errors. foundInPass = true; srvnamReceived = checkAndGetReceivedFlag(srvnamReceived); parseLengthAndMatchCodePoint(CodePoint.SRVNAM); readString(); //parseSRVNAM(); // not used yet peekCP = peekCodePoint(); } if (peekCP == CodePoint.SRVRLSLV) { // Server Product Release Level // specifies the procuct release level of a ddm server. foundInPass = true; srvrlslvReceived = checkAndGetReceivedFlag(srvrlslvReceived); parseLengthAndMatchCodePoint(CodePoint.SRVRLSLV); String serverReleaseLevel = readString(); // parseSRVRLSLV(); metadata.setDbMetadata(new DB2DatabaseMetadata(serverReleaseLevel)); peekCP = peekCodePoint(); } if (!foundInPass) { throwUnknownCodepoint(peekCP); } if (!srvrlslvReceived) throwMissingRequiredCodepoint("SRVRLSLV", CodePoint.SRVRLSLV); } ddmCollectionLenStack.pop(); // according the the DDM book, all these instance variables are optional //netConnection.setServerAttributeData(srvclsnm, srvrlslv); } // Manager-Level List. // Specifies a list of code points and support levels for the // classes of managers a server supports. // The target server must not provide information for any target // managers unless the source explicitly requests it. // For each manager class, if the target server's support level // is greater than or equal to the source server's level, then the source // server's level is returned for that class if the target server can operate // at the source's level; otherwise a level 0 is returned. If the target // server's support level is less than the source server's level, the // target server's level is returned for that class. If the target server // does not recognize the code point of a manager class or does not support // that class, it returns a level of 0. The target server then waits // for the next command or for the source server to terminate communications. // When the source server receives EXCSATRD, it must compare each of the entries // in the mgrlvlls parameter it received to the corresponding entries in the mgrlvlls // parameter it sent. If any level mismatches, the source server must decide // whether it can use or adjust to the lower level of target support for that manager // class. There are no architectural criteria for making this decision. // The source server can terminate communications or continue at the target // servers level of support. It can also attempt to use whatever // commands its user requests while receiving eror reply messages for real // functional mismatches. // The manager levels the source server specifies or the target server // returns must be compatible with the manager-level dependencies of the specified // manangers. Incompatible manager levels cannot be specified. // After this method successfully returns, the targetXXXX values (where XXXX // represents a manager name. example targetAgent) contain the negotiated // manager levels for this particular connection. private void parseMGRLVLLS() { parseLengthAndMatchCodePoint(CodePoint.MGRLVLLS); // each manager class and level is 4 bytes long. // get the length of the mgrlvls bytes, make sure it contains // the correct number of bytes for a mgrlvlls object, and calculate // the number of manager's returned from the server. int managerListLength = getDdmLength(); if ((managerListLength == 0) || ((managerListLength % 4) != 0)) { doSyntaxrmSemantics(CodePoint.SYNERRCD_OBJ_LEN_NOT_ALLOWED); } int managerCount = managerListLength / 4; // the managerCount should be equal to the same number of // managers sent on the excsat. // System.out.println("Database server attributes:"); // read each of the manager levels returned from the server. for (int i = 0; i < managerCount; i++) { // first two byte are the manager's codepoint, next two bytes are the level. int managerCodePoint = readUnsignedShort(); //buffer.readUnsignedShort(); //parseCODPNTDR(); int managerLevel = readUnsignedShort(); //buffer.readUnsignedShort(); // parseMGRLVLN(); // TODO @AGG: decide which manager levels we should support // check each manager to make sure levels are within proper limits // for this driver. Also make sure unexpected managers are not returned. switch (managerCodePoint) { case CodePoint.AGENT: // System.out.println(" AGENT=" + managerLevel); // break; case CodePoint.SQLAM: // System.out.println(" SQLAM=" + managerLevel); // break; case CodePoint.UNICODEMGR: // System.out.println(" UNICODEMGR=" + managerLevel); // break; case CodePoint.RDB: // System.out.println(" RDB=" + managerLevel); // break; case CodePoint.SECMGR: // System.out.println(" SECMGR=" + managerLevel); // break; case CodePoint.CMNTCPIP: // System.out.println(" CMNTCPIP=" + managerLevel); break; default: System.out.println(" WARN: Unknown manager codepoint: 0x" + Integer.toHexString(managerCodePoint)); } } } }