Skip to content

Commit c62acc5

Browse files
committed
Additional basic DB2 input validation
Signed-off-by: Greg Watts <gwatts@us.ibm.com>
1 parent ebab50a commit c62acc5

File tree

8 files changed

+242
-60
lines changed

8 files changed

+242
-60
lines changed

vertx-db2-client/src/main/java/io/vertx/db2client/DB2ConnectOptions.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import io.vertx.codegen.annotations.GenIgnore;
2525
import io.vertx.core.json.JsonObject;
2626
import io.vertx.db2client.impl.DB2ConnectionUriParser;
27+
import io.vertx.db2client.impl.drda.SQLState;
28+
import io.vertx.db2client.impl.drda.SqlCode;
2729
import io.vertx.sqlclient.SqlConnectOptions;
2830

2931
/**
@@ -47,11 +49,11 @@ public static DB2ConnectOptions fromUri(String connectionUri) throws IllegalArgu
4749
public static final String DEFAULT_HOST = "localhost";
4850
public static final int DEFAULT_PORT = 50000;
4951
public static final String DEFAULT_USER = "root";
50-
public static final String DEFAULT_PASSWORD = "";
51-
public static final String DEFAULT_SCHEMA = "";
52+
public static final String DEFAULT_PASSWORD = "INVALID";
53+
public static final String DEFAULT_SCHEMA = "INVALID";
5254
public static final String DEFAULT_CHARSET = "utf8";
5355
public static final boolean DEFAULT_USE_AFFECTED_ROWS = false;
54-
public static final int DEFAULT_PIPELINING_LIMIT = 1;//256; // TODO default to 256 once implemented properly
56+
public static final int DEFAULT_PIPELINING_LIMIT = 1; //256; // TODO default to 256 once implemented properly
5557
public static final Map<String, String> DEFAULT_CONNECTION_ATTRIBUTES;
5658

5759
static {
@@ -93,17 +95,29 @@ public DB2ConnectOptions setPort(int port) {
9395

9496
@Override
9597
public DB2ConnectOptions setUser(String user) {
96-
return (DB2ConnectOptions) super.setUser(user);
98+
if (user == null || user.trim().length() < 1) {
99+
throw new DB2Exception("The user cannot be blank or null", SqlCode.MISSING_CREDENTIALS, SQLState.CONNECT_USERID_ISNULL);
100+
} else {
101+
return (DB2ConnectOptions) super.setUser(user);
102+
}
97103
}
98104

99105
@Override
100106
public DB2ConnectOptions setPassword(String password) {
101-
return (DB2ConnectOptions) super.setPassword(password);
107+
if (password == null || password.trim().length() < 1) {
108+
throw new DB2Exception("The password cannot be blank or null", SqlCode.MISSING_CREDENTIALS, SQLState.CONNECT_PASSWORD_ISNULL);
109+
} else {
110+
return (DB2ConnectOptions) super.setPassword(password);
111+
}
102112
}
103113

104114
@Override
105115
public DB2ConnectOptions setDatabase(String database) {
106-
return (DB2ConnectOptions) super.setDatabase(database);
116+
if (database == null || database.trim().length() < 1) {
117+
throw new DB2Exception("The database name cannot be blank or null", SqlCode.DATABASE_NOT_FOUND, SQLState.DATABASE_NOT_FOUND);
118+
} else {
119+
return (DB2ConnectOptions) super.setDatabase(database);
120+
}
107121
}
108122

109123
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void write(CommandBase<?> cmd) {
9797
} else if (cmd instanceof CloseConnectionCommand) {
9898
codec = new CloseConnectionCommandCodec((CloseConnectionCommand) cmd);
9999
} else if (cmd instanceof PrepareStatementCommand) {
100-
codec = new PrepareStatementCodec((PrepareStatementCommand) cmd);
100+
codec = new PrepareStatementCodec((PrepareStatementCommand) cmd);
101101
} else if (cmd instanceof CloseStatementCommand) {
102102
codec = new CloseStatementCommandCodec((CloseStatementCommand) cmd);
103103
} else if (cmd instanceof CloseCursorCommand) {

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class InitialHandshakeCommandCodec extends AuthenticationCommandBaseCodec<Connec
3333
private static final int ST_CONNECTING = 0;
3434
private static final int ST_AUTHENTICATING = 1;
3535
private static final int ST_CONNECTED = 2;
36+
private static final int ST_CONNECT_FAILED = 3;
3637

3738
private static final int TARGET_SECURITY_MEASURE = DRDAConstants.SECMEC_USRIDPWD;
3839

@@ -69,7 +70,14 @@ void decodePayload(ByteBuf payload, int payloadLength) {
6970
switch (status) {
7071
case ST_CONNECTING:
7172
response.readExchangeServerAttributes();
72-
response.readAccessSecurity(TARGET_SECURITY_MEASURE);
73+
// readAccessSecurity can throw a DB2Exception if there are problems connecting. In that case, we want to catch that exception and
74+
// make sure to set the status to something other than ST_CONNECTING so we don't try to complete the result twice (when we hit encode)
75+
try {
76+
response.readAccessSecurity(TARGET_SECURITY_MEASURE);
77+
} catch (DB2Exception de) {
78+
status = ST_CONNECT_FAILED;
79+
throw de;
80+
}
7381
status = ST_AUTHENTICATING;
7482
ByteBuf packet = allocateBuffer();
7583
int packetStartIdx = packet.writerIndex();

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,8 @@ private String parsePRDID(boolean skip) {
10411041
private void parseSECCHKreply() {
10421042
int peekCP = peekCodePoint();
10431043
if (peekCP != CodePoint.SECCHKRM) {
1044-
throwUnknownCodepoint(peekCP);
1044+
// throwUnknownCodepoint(peekCP);
1045+
parseCommonError(peekCP);
10451046
}
10461047

10471048
parseSECCHKRM();
@@ -1058,7 +1059,7 @@ private void parseSECCHKreply() {
10581059
parseSECTKN(false);
10591060
}
10601061
}
1061-
1062+
10621063
// The Security Check (SECCHKRM) Reply Message indicates the acceptability
10631064
// of the security information.
10641065
// This method throws an exception if the connection was not established
@@ -1143,7 +1144,7 @@ private void parseSECCHKRM() {
11431144
throw new DB2Exception("Missing userid, verify a user value was supplied", SqlCode.MISSING_CREDENTIALS, SQLState.CONNECT_USERID_ISNULL);
11441145
// Missing password - TODO We should catch and handle this issue *before* the call to the DB2 server
11451146
case CodePoint.SECCHKCD_10:
1146-
// Using SQL error code and state values from similar JDBC reponse
1147+
// Using SQL error code and state values from similar JDBC response
11471148
throw new DB2Exception("Missing password, verify a password value was supplied", SqlCode.MISSING_CREDENTIALS, SQLState.CONNECT_PASSWORD_ISNULL);
11481149
// Invalid credentials
11491150
case CodePoint.SECCHKCD_0E:
@@ -1315,12 +1316,15 @@ private void parseRdbAccessFailed() {
13151316
// Returned from Server:
13161317
// SVRCOD - required (8 - ERROR)
13171318
// RDBNAM - required
1319+
// SRVDGN - optional
13181320
//
13191321
private void parseRDBAFLRM() {
13201322
boolean svrcodReceived = false;
13211323
int svrcod = CodePoint.SVRCOD_INFO;
13221324
boolean rdbnamReceived = false;
1325+
boolean srvdgnReceived = false;
13231326
String rdbnam = null;
1327+
String serverDiagnostics = null;
13241328

13251329
parseLengthAndMatchCodePoint(CodePoint.RDBAFLRM);
13261330
pushLengthOnCollectionStack();
@@ -1343,6 +1347,14 @@ private void parseRDBAFLRM() {
13431347
rdbnam = parseRDBNAM(true);
13441348
peekCP = peekCodePoint();
13451349
}
1350+
1351+
// Optional code point
1352+
if (peekCP == CodePoint.SRVDGN) {
1353+
foundInPass = true;
1354+
srvdgnReceived = checkAndGetReceivedFlag(srvdgnReceived);
1355+
serverDiagnostics = parseSRVDGN();
1356+
peekCP = peekCodePoint();
1357+
}
13461358

13471359
if (!foundInPass) {
13481360
throwUnknownCodepoint(peekCP);

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2730,6 +2730,8 @@ private int parseCODPNT() {
27302730
void parseDTAMCHRM() {
27312731
boolean svrcodReceived = false;
27322732
int svrcod = CodePoint.SVRCOD_INFO;
2733+
boolean srvdgnReceived = false;
2734+
String serverDiagnostics = null;
27332735
boolean rdbnamReceived = false;
27342736
String rdbnam = null;
27352737

@@ -2747,13 +2749,21 @@ void parseDTAMCHRM() {
27472749
svrcod = parseSVRCOD(CodePoint.SVRCOD_ERROR, CodePoint.SVRCOD_ERROR);
27482750
peekCP = peekCodePoint();
27492751
}
2750-
2752+
27512753
if (peekCP == CodePoint.RDBNAM) {
27522754
foundInPass = true;
27532755
rdbnamReceived = checkAndGetReceivedFlag(rdbnamReceived);
27542756
rdbnam = parseRDBNAM(true);
27552757
peekCP = peekCodePoint();
27562758
}
2759+
2760+
// Optional code point
2761+
if (peekCP == CodePoint.SRVDGN) {
2762+
foundInPass = true;
2763+
srvdgnReceived = checkAndGetReceivedFlag(srvdgnReceived);
2764+
serverDiagnostics = parseSRVDGN();
2765+
peekCP = peekCodePoint();
2766+
}
27572767

27582768
if (!foundInPass) {
27592769
throwUnknownCodepoint(peekCP);

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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.util.Arrays;
1919

20+
import io.vertx.db2client.DB2Exception;
21+
2022
/**
2123
* A SQLCA stands for "SQL Communication Area"
2224
* The primary purpose is for tracking the SQLCode.
@@ -92,16 +94,52 @@ public static int complete(NetSqlca sqlca, int... allowedCodes) {
9294
return 0;
9395
boolean allowed = Arrays.stream(allowedCodes).anyMatch(code -> code == sqlca.sqlCode_);
9496
if (!allowed && sqlca.sqlCode_ < 0) {
97+
throwSqlError(sqlca);
9598
// TODO: May want to go through the DB2 SQL error code doc above and provide English
96-
// messages to go along with the corresponding SQLcode to save users needing to look them up
97-
throw new IllegalStateException("ERROR sqlcode=" + sqlca.sqlCode_ + " Full Sqlca: " + sqlca.toString());
99+
// messages to go along with the corresponding SQLcode to save users needing to look them up
98100
}
99101
if (!allowed && sqlca.sqlCode_ > 0) {
100102
System.out.println("WARNING sqlcode=" + sqlca.sqlCode_);
101103
}
102104
return sqlca.sqlCode_;
103105
}
104106

107+
/**
108+
* Throws a specific error message based on the passed in SQL error code
109+
* @param sqlca
110+
*/
111+
public static void throwSqlError(NetSqlca sqlca) {
112+
if (sqlca == null || sqlca.sqlCode_ == 0) {
113+
return;
114+
}
115+
// Add additional error messages to this list
116+
switch(sqlca.sqlCode_) {
117+
// The SQL syntax is invalid
118+
case SqlCode.INVALID_SQL_STATEMENT:
119+
throw new DB2Exception("The SQL syntax provided was invalid", SqlCode.INVALID_SQL_STATEMENT, sqlca.sqlState_);
120+
// The object (table?) is not defined/available
121+
case SqlCode.OBJECT_NOT_DEFINED:
122+
if (sqlca.sqlErrmc_ != null && sqlca.sqlErrmc_.trim().length() > 0)
123+
throw new DB2Exception("The object " + sqlca.sqlErrmc_ + " provided is not defined", SqlCode.OBJECT_NOT_DEFINED, sqlca.sqlState_);
124+
else
125+
throw new DB2Exception("An object provided is not defined", SqlCode.OBJECT_NOT_DEFINED, sqlca.sqlState_);
126+
// The object (table?) is not defined/available
127+
case SqlCode.COLUMN_DOES_NOT_EXIST:
128+
if (sqlca.sqlErrmc_ != null && sqlca.sqlErrmc_.trim().length() > 0)
129+
throw new DB2Exception("The column " + sqlca.sqlErrmc_ + " provided does not exist", SqlCode.COLUMN_DOES_NOT_EXIST, sqlca.sqlState_);
130+
else
131+
throw new DB2Exception("A column provided does not exist", SqlCode.COLUMN_DOES_NOT_EXIST, sqlca.sqlState_);
132+
// Invalid database specified
133+
case SqlCode.DATABASE_NOT_FOUND:
134+
if (sqlca.sqlErrmc_ != null && sqlca.sqlErrmc_.trim().length() > 0)
135+
throw new DB2Exception("The database " + sqlca.sqlErrmc_ + " provided was not found", SqlCode.DATABASE_NOT_FOUND, sqlca.sqlState_);
136+
else
137+
throw new DB2Exception("The database provided was not found", SqlCode.DATABASE_NOT_FOUND, sqlca.sqlState_);
138+
default:
139+
throw new IllegalStateException("ERROR sqlcode=" + sqlca.sqlCode_ + " Full Sqlca: " + sqlca.toString());
140+
}
141+
}
142+
105143
void setSqlerrd(int[] sqlErrd) {
106144
sqlErrd_ = sqlErrd;
107145
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ public class SqlCode {
2828
public static final int RDB_NOT_FOUND = -30061;
2929
public static final int INVALID_CREDENTIALS = -4214;
3030
public static final int MISSING_CREDENTIALS = -4461;
31+
public static final int DATABASE_NOT_FOUND = -1001;
32+
33+
// -104 is a broad error message (illegal symbol encountered in SQL statement)
34+
// and could be further broken down by adding more specific SQL error codes and handling them separately
35+
public static final int INVALID_SQL_STATEMENT = -104;
36+
37+
// The error message for this says "Object not defined in DB2" in Wikipedia and
38+
// "<name> is an undefined name" in the IBM zOS DB2 Knowledge Center
39+
// But I see it with invalid table names specified in a query
40+
public static final int OBJECT_NOT_DEFINED = -204;
41+
public static final int COLUMN_DOES_NOT_EXIST = -206;
3142

3243
private int code_;
3344

0 commit comments

Comments
 (0)