Skip to content

Commit f12cf6e

Browse files
committed
Add handling for ACCSEC error path
1 parent e8893a2 commit f12cf6e

File tree

5 files changed

+288
-45
lines changed

5 files changed

+288
-45
lines changed

vertx-db2-client/src/main/java/io/vertx/db2client/impl/codec/InitialHandshakeCommandCodec.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import io.vertx.db2client.impl.drda.DRDAConnectResponse;
2424
import io.vertx.db2client.impl.drda.DRDAConnectResponse.RDBAccessData;
2525
import io.vertx.db2client.impl.drda.DRDAConstants;
26+
import io.vertx.db2client.impl.drda.SQLState;
27+
import io.vertx.db2client.impl.drda.SqlCode;
2628
import io.vertx.sqlclient.impl.Connection;
2729
import io.vertx.sqlclient.impl.command.CommandResponse;
2830

@@ -54,7 +56,8 @@ void encode(DB2Encoder encoder) {
5456
//Sometimes DB2 closes the connection when sending an invalid Database name.
5557
//-4499 = A fatal error occurred that resulted in a disconnect from the data source.
5658
//08001 = "The connection was unable to be established"
57-
cmd.fail(new DB2Exception("Socket closed during connection", -4499,"08001"));
59+
cmd.fail(new DB2Exception("The connection was closed by the database server.",
60+
SqlCode.CONNECTION_REFUSED, SQLState.AUTH_DATABASE_CONNECTION_REFUSED));
5861
}
5962
});
6063
sendInitialHandshake();

vertx-db2-client/src/main/java/io/vertx/db2client/impl/drda/DRDAConnectResponse.java

Lines changed: 187 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222

2323
import io.netty.buffer.ByteBuf;
24+
import io.vertx.db2client.DB2Exception;
2425

2526
public class DRDAConnectResponse extends DRDAResponse {
2627

@@ -1141,8 +1142,7 @@ private void parseSECCHKRM() {
11411142
private void parseACCSECreply(int securityMechanism) {
11421143
int peekCP = peekCodePoint();
11431144
if (peekCP != CodePoint.ACCSECRD) {
1144-
throw new IllegalStateException(String.format("Expecting ACCSECRD codepoint (0x14AC) but got %04x", peekCP));
1145-
//parseAccessSecurityError(netConnection);
1145+
parseAccessSecurityError();
11461146
}
11471147
parseACCSECRD(securityMechanism);
11481148

@@ -1154,6 +1154,191 @@ private void parseACCSECreply(int securityMechanism) {
11541154
// }
11551155
}
11561156

1157+
private void parseAccessSecurityError() {
1158+
int peekCP = peekCodePoint();
1159+
switch (peekCP) {
1160+
case CodePoint.CMDCHKRM:
1161+
parseCMDCHKRM();
1162+
break;
1163+
case CodePoint.RDBNFNRM:
1164+
parseRDBNFNRM();
1165+
break;
1166+
case CodePoint.RDBAFLRM:
1167+
parseRdbAccessFailed();
1168+
break;
1169+
default:
1170+
parseCommonError(peekCP);
1171+
}
1172+
}
1173+
1174+
// RDB Not Found Reply Message indicates that the target
1175+
// server cannot find the specified relational database.
1176+
// PROTOCOL architects an SQLSTATE of 08004.
1177+
//
1178+
// Messages
1179+
// SQLSTATE : 8004
1180+
// The application server rejected establishment of the connection.
1181+
// SQLCODE : -30061
1182+
// The database alias or database name <name> was not found at the remote node.
1183+
// The statement cannot be processed.
1184+
//
1185+
//
1186+
// Returned from Server:
1187+
// SVRCOD - required (8 - ERROR)
1188+
// RDBNAM - required
1189+
private void parseRDBNFNRM() {
1190+
boolean svrcodReceived = false;
1191+
int svrcod = CodePoint.SVRCOD_INFO;
1192+
boolean rdbnamReceived = false;
1193+
String rdbnam = null;
1194+
1195+
parseLengthAndMatchCodePoint(CodePoint.RDBNFNRM);
1196+
pushLengthOnCollectionStack();
1197+
int peekCP = peekCodePoint();
1198+
1199+
while (peekCP != END_OF_COLLECTION) {
1200+
1201+
boolean foundInPass = false;
1202+
1203+
if (peekCP == CodePoint.SVRCOD) {
1204+
foundInPass = true;
1205+
svrcodReceived = checkAndGetReceivedFlag(svrcodReceived);
1206+
svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR);
1207+
peekCP = peekCodePoint();
1208+
}
1209+
1210+
if (peekCP == CodePoint.RDBNAM) {
1211+
foundInPass = true;
1212+
rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
1213+
rdbnam = parseRDBNAM(true);
1214+
peekCP = peekCodePoint();
1215+
}
1216+
1217+
if (peekCP == CodePoint.SRVDGN) {
1218+
foundInPass = true;
1219+
String serverDiagnostics = parseSRVDGN();
1220+
// TODO: @AGG Log the server diagnostics here
1221+
peekCP = peekCodePoint();
1222+
}
1223+
1224+
if (!foundInPass) {
1225+
throwUnknownCodepoint(peekCP);
1226+
}
1227+
1228+
}
1229+
popCollectionStack();
1230+
if (!svrcodReceived)
1231+
throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD);
1232+
if (!rdbnamReceived)
1233+
throwMissingRequiredCodepoint("RDBNAM", CodePoint.RDBNAM);
1234+
1235+
// netAgent_.setSvrcod(svrcod);
1236+
throw new DB2Exception("The requested database was not found: " + metadata.databaseName,
1237+
SqlCode.RDB_NOT_FOUND, SQLState.NET_DATABASE_NOT_FOUND);
1238+
// agent_.accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(agent_,
1239+
// new ClientMessageId(SQLState.NET_DATABASE_NOT_FOUND),
1240+
// netConnection.databaseName_));
1241+
}
1242+
1243+
private void parseRdbAccessFailed() {
1244+
parseRDBAFLRM();
1245+
1246+
// an SQLCARD is returned if an RDBALFRM is returned.
1247+
// this SQLCARD always follows the RDBALFRM.
1248+
// TYPDEFNAM and TYPDEFOVR are MTLINC
1249+
1250+
if (peekCodePoint() == CodePoint.TYPDEFNAM) {
1251+
parseTYPDEFNAM();
1252+
parseTYPDEFOVR();
1253+
} else {
1254+
parseTYPDEFOVR();
1255+
parseTYPDEFNAM();
1256+
}
1257+
1258+
NetSqlca netSqlca = parseSQLCARD(null);
1259+
1260+
//Check if the SQLCARD has null SQLException
1261+
if(netSqlca.getSqlErrmc() == null) {
1262+
// netConnection.setConnectionNull(true);
1263+
} else {
1264+
NetSqlca.complete(netSqlca);
1265+
}
1266+
}
1267+
1268+
// RDB Access Failed Reply Message specifies that the relational
1269+
// database failed the attempted connection.
1270+
// An SQLCARD object must also be returned, following the
1271+
// RDBAFLRM, to explain why the RDB failed the connection.
1272+
// In addition, the target SQLAM instance is destroyed.
1273+
// The SQLSTATE is returned in the SQLCARD.
1274+
//
1275+
// Messages
1276+
// SQLSTATE : 58009
1277+
// Execution failed due to a distribution protocol error that caused deallocation of the conversation.
1278+
// SQLCODE : -30020
1279+
// Execution failed because of a Distributed Protocol
1280+
// Error that will affect the successful execution of subsequent
1281+
// commands and SQL statements: Reason Code <reason-code>.
1282+
// Some possible reason codes include:
1283+
// 121C Indicates that the user is not authorized to perform the requested command.
1284+
// 1232 The command could not be completed because of a permanent error.
1285+
// In most cases, the server will be in the process of an abend.
1286+
// 220A The target server has received an invalid data description.
1287+
// If a user SQLDA is specified, ensure that the fields are
1288+
// initialized correctly. Also, ensure that the length does not
1289+
// exceed the maximum allowed length for the data type being used.
1290+
//
1291+
// The command or statement cannot be processed. The current
1292+
// transaction is rolled back and the application is disconnected
1293+
// from the remote database.
1294+
//
1295+
//
1296+
// Returned from Server:
1297+
// SVRCOD - required (8 - ERROR)
1298+
// RDBNAM - required
1299+
//
1300+
private void parseRDBAFLRM() {
1301+
boolean svrcodReceived = false;
1302+
int svrcod = CodePoint.SVRCOD_INFO;
1303+
boolean rdbnamReceived = false;
1304+
String rdbnam = null;
1305+
1306+
parseLengthAndMatchCodePoint(CodePoint.RDBAFLRM);
1307+
pushLengthOnCollectionStack();
1308+
int peekCP = peekCodePoint();
1309+
1310+
while (peekCP != END_OF_COLLECTION) {
1311+
1312+
boolean foundInPass = false;
1313+
1314+
if (peekCP == CodePoint.SVRCOD) {
1315+
foundInPass = true;
1316+
svrcodReceived = checkAndGetReceivedFlag(svrcodReceived);
1317+
svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR);
1318+
peekCP = peekCodePoint();
1319+
}
1320+
1321+
if (peekCP == CodePoint.RDBNAM) {
1322+
foundInPass = true;
1323+
rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
1324+
rdbnam = parseRDBNAM(true);
1325+
peekCP = peekCodePoint();
1326+
}
1327+
1328+
if (!foundInPass) {
1329+
throwUnknownCodepoint(peekCP);
1330+
}
1331+
1332+
}
1333+
popCollectionStack();
1334+
if (!svrcodReceived)
1335+
throwMissingRequiredCodepoint("SVRCOD", CodePoint.SVRCOD);
1336+
if (!rdbnamReceived)
1337+
throwMissingRequiredCodepoint("RDBNAM", CodePoint.RDBNAM);
1338+
1339+
// netAgent_.setSvrcod(svrcod);
1340+
}
1341+
11571342
// The Access Security Reply Data (ACSECRD) Collection Object contains
11581343
// the security information from a target server's security manager.
11591344
// this method returns the security check code received from the server

vertx-db2-client/src/main/java/io/vertx/db2client/impl/drda/NetSqlca.java

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
* If the code is == 0, nothing is wrong
2626
*/
2727
public class NetSqlca {
28+
29+
private static final String SQLERRMC_MESSAGE_DELIMITER = new String(new char[] {(char)20,(char)20,(char)20,(char)20});
2830

2931
// Indexes into sqlErrd_
3032
private static final int HIGH_ORDER_ROW_COUNT = 0;
@@ -171,46 +173,46 @@ synchronized public int getSqlCode() {
171173
// return errorCode;
172174
// }
173175

174-
// synchronized public String getSqlErrmc() {
175-
// if (sqlErrmc_ != null) {
176-
// return sqlErrmc_;
177-
// }
178-
//
179-
// // sqlErrmc string is dependent on sqlErrmcMessages_ array having
180-
// // been built
181-
// initSqlErrmcMessages();
182-
//
183-
// // sqlErrmc will be built only if sqlErrmcMessages_ has been built.
184-
// // Otherwise, a null string will be returned.
185-
// if (sqlErrmcMessages_ == null) {
186-
// return null;
187-
// }
188-
//
189-
// // create 0-length String if no tokens
190-
// if (sqlErrmcMessages_.length == 0) {
191-
// sqlErrmc_ = "";
192-
// return sqlErrmc_;
193-
// }
194-
//
195-
// // concatenate tokens with sqlErrmcDelimiter delimiters into one String
196-
// StringBuffer buffer = new StringBuffer();
197-
// int indx;
198-
// for (indx = 0; indx < sqlErrmcMessages_.length - 1; indx++) {
199-
// buffer.append(sqlErrmcMessages_[indx]);
200-
// buffer.append(MessageUtils.SQLERRMC_MESSAGE_DELIMITER);
201-
// // all but the first message should be preceded by the SQL state
202-
// // and a colon (see DRDAConnThread.buildTokenizedSqlerrmc() on the
203-
// // server)
204-
// buffer.append(sqlStates_[indx+1]);
205-
// buffer.append(":");
206-
// }
207-
// // add the last token
208-
// buffer.append(sqlErrmcMessages_[indx]);
209-
//
210-
// // save as a string
211-
// sqlErrmc_ = buffer.toString();
212-
// return sqlErrmc_;
213-
// }
176+
synchronized public String getSqlErrmc() {
177+
if (sqlErrmc_ != null) {
178+
return sqlErrmc_;
179+
}
180+
181+
// sqlErrmc string is dependent on sqlErrmcMessages_ array having
182+
// been built
183+
initSqlErrmcMessages();
184+
185+
// sqlErrmc will be built only if sqlErrmcMessages_ has been built.
186+
// Otherwise, a null string will be returned.
187+
if (sqlErrmcMessages_ == null) {
188+
return null;
189+
}
190+
191+
// create 0-length String if no tokens
192+
if (sqlErrmcMessages_.length == 0) {
193+
sqlErrmc_ = "";
194+
return sqlErrmc_;
195+
}
196+
197+
// concatenate tokens with sqlErrmcDelimiter delimiters into one String
198+
StringBuffer buffer = new StringBuffer();
199+
int indx;
200+
for (indx = 0; indx < sqlErrmcMessages_.length - 1; indx++) {
201+
buffer.append(sqlErrmcMessages_[indx]);
202+
buffer.append(SQLERRMC_MESSAGE_DELIMITER);
203+
// all but the first message should be preceded by the SQL state
204+
// and a colon (see DRDAConnThread.buildTokenizedSqlerrmc() on the
205+
// server)
206+
buffer.append(sqlStates_[indx+1]);
207+
buffer.append(":");
208+
}
209+
// add the last token
210+
buffer.append(sqlErrmcMessages_[indx]);
211+
212+
// save as a string
213+
sqlErrmc_ = buffer.toString();
214+
return sqlErrmc_;
215+
}
214216

215217
/**
216218
* Initialize and build the arrays <code>sqlErrmcMessages_</code> and
@@ -539,7 +541,7 @@ public String toString() {
539541
" sqlCode=" + sqlCode_ +
540542
" sqlState=" + sqlState_ +
541543
" sqlErrd=" + Arrays.toString(sqlErrd_) +
542-
" sqlErrmc=" + bytes2String(sqlErrmcBytes_) +
544+
" sqlErrmc=" + getSqlErrmc() +
543545
" sqlErrp=" + bytes2String(sqlErrpBytes_) +
544546
" sqlStates=" + Arrays.deepToString(sqlStates_) +
545547
" sqlWarn=" + bytes2String(sqlWarnBytes_);

vertx-db2-client/src/main/java/io/vertx/db2client/impl/drda/SqlCode.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
* DB2 SQL Codes</a>
2222
*/
2323
public class SqlCode {
24+
25+
// TODO: @AGG Convert this class to an interface with constants
26+
27+
public static final int CONNECTION_REFUSED = -4499;
28+
public static final int RDB_NOT_FOUND = -30061;
29+
2430
private int code_;
2531

2632
SqlCode(int code) {
@@ -38,7 +44,7 @@ public final int getCode() {
3844

3945
public final static SqlCode queuedXAError = new SqlCode(-4203);
4046

41-
final static SqlCode disconnectError = new SqlCode(40000);
47+
public final static SqlCode disconnectError = new SqlCode(40000);
4248

4349
/** SQL code for SQL state 02000 (end of data). DRDA does not
4450
* specify the SQL code for this SQL state, but Derby/DB2 uses 100. */

0 commit comments

Comments
 (0)