io.vertx.db2client.impl.drda.DRDAConnectResponse Maven / Gradle / Ivy
/*
* 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));
}
}
}
}