Mercurial > hg > monetdb-java
changeset 359:68401c1f10fa embedded
Backported more features from default branch.
author | Pedro Ferreira <pedro.ferreira@monetdbsolutions.com> |
---|---|
date | Fri, 26 Jul 2019 12:00:49 +0200 (2019-07-26) |
parents | 2ad7f42f141d |
children | 5e6218d21951 |
files | src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java |
diffstat | 4 files changed, 104 insertions(+), 61 deletions(-) [+] |
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDataSource.java @@ -49,7 +49,8 @@ public class MonetDataSource extends Mon public MonetDataSource() {} /** - * Attempts to establish a connection with the data source that this DataSource object represents. + * Attempts to establish a connection with the data source that this + * DataSource object represents. * * @return a MonetConnection * @throws SQLException if connecting to the database fails @@ -60,7 +61,8 @@ public class MonetDataSource extends Mon } /** - * Attempts to establish a connection with the data source that this DataSource object represents. + * Attempts to establish a connection with the data source that this + * DataSource object represents. * * @param username the username to use * @param password the password to use @@ -68,7 +70,9 @@ public class MonetDataSource extends Mon * @throws SQLException if connecting to the database fails */ @Override - public Connection getConnection(String username, String password) throws SQLException { + public Connection getConnection(String username, String password) + throws SQLException + { Properties props = new Properties(); props.put("user", username); props.put("password", password); @@ -82,8 +86,10 @@ public class MonetDataSource extends Mon return driver.connect(url, props); } + /** - * Gets the maximum time in seconds that this data source can wait while attempting to connect to a database. + * Gets the maximum time in seconds that this data source can wait while + * attempting to connect to a database. * * @return login timeout default is 0 (infinite) */ @@ -93,7 +99,8 @@ public class MonetDataSource extends Mon } /** - * Sets the maximum time in seconds that this data source will wait while attempting to connect to a database. + * Sets the maximum time in seconds that this data source will wait while + * attempting to connect to a database. * * @param seconds the number of seconds to wait before aborting the connect */ @@ -113,12 +120,14 @@ public class MonetDataSource extends Mon } /** - * Sets the log writer for this DataSource object to the given java.io.PrintWriter object. + * Sets the log writer for this DataSource object to the given + * java.io.PrintWriter object. * * @param out a PrintWriter - ignored */ @Override - public void setLogWriter(PrintWriter out) {} + public void setLogWriter(PrintWriter out) { + } /** * Sets the password to use when connecting. There is no getter @@ -220,7 +229,8 @@ public class MonetDataSource extends Mon * may be the root Logger. * * @return the parent Logger for this data source - * @throws SQLFeatureNotSupportedException if the data source does not use java.util.logging + * @throws SQLFeatureNotSupportedException if the data source does + * not use java.util.logging */ @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException {
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java @@ -8,6 +8,9 @@ package nl.cwi.monetdb.mcl.connection.helpers; +import nl.cwi.monetdb.mcl.protocol.ProtocolException; + +import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -43,15 +46,11 @@ public final class ChannelSecurity { * @param toDigests The Strings to digest * @return The Strings digest as a hexadecimal string */ - public static String digestStrings(String algorithm, byte[]... toDigests) { - try { - MessageDigest md = MessageDigest.getInstance(algorithm); - for (byte[] str : toDigests) { - md.update(str); - } - return toHex(md.digest()); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError("internal error: " + e.toString()); + public static String digestStrings(String algorithm, byte[]... toDigests) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance(algorithm); + for (byte[] str : toDigests) { + md.update(str); } + return toHex(md.digest()); } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java @@ -92,6 +92,16 @@ public abstract class AbstractSocket imp } /** + * Sets the TCP keep alive feature in the underlying socket. + * + * @param on A true or false value + * @throws SocketException If an error in the underlying connection happened + */ + void setKeepAlive(boolean on) throws SocketException { + socket.setKeepAlive(on); + } + + /** * Sets the underlying socket Endianness. * * @param bo A ByteOrder order value either Little-endian or Big-endian
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java @@ -18,11 +18,13 @@ import nl.cwi.monetdb.mcl.protocol.Serve import nl.cwi.monetdb.mcl.protocol.oldmapi.OldMapiProtocol; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteOrder; +import java.security.NoSuchAlgorithmException; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.SQLException; @@ -326,12 +328,13 @@ public class MapiConnection extends Mone public List<String> connect(String host, int port, String user, String pass, boolean makeConnection) throws IOException, ProtocolException, MCLException { if (ttl-- <= 0) - throw new MCLException("Maximum number of redirects reached, aborting connection attempt. Sorry."); + throw new MCLException("Maximum number of redirects reached, aborting connection attempt."); if (makeConnection) { this.protocol = new OldMapiProtocol(new OldMapiSocket(this.hostname, this.port, this)); //set nodelay, as it greatly speeds up small messages (like we often do) ((OldMapiProtocol)this.protocol).getSocket().setTcpNoDelay(true); + ((OldMapiProtocol)this.protocol).getSocket().setKeepAlive(true); ((OldMapiProtocol)this.protocol).getSocket().setSoTimeout(this.soTimeout); } @@ -469,21 +472,17 @@ public class MapiConnection extends Mone String database, String hash) throws ProtocolException, MCLException, IOException { String response; - String algo; - // parse the challenge string, split it on ':' String[] chaltok = chalstr.split(":"); - if (chaltok.length <= 4) - throw new ProtocolException("Server challenge string unusable! Challenge contains too few tokens: " - + chalstr); + if (chaltok.length <= 5) + throw new MCLException("Server challenge string unusable! It contains too few (" + chaltok.length + ") tokens: " + chalstr); // challenge string to use as salt/key String challenge = chaltok[0]; - String servert = chaltok[1]; try { - this.version = Integer.parseInt(chaltok[2].trim()); // protocol version + version = Integer.parseInt(chaltok[2]); // protocol version } catch (NumberFormatException e) { - throw new ProtocolException("Protocol version unparseable: " + chaltok[2]); + throw new MCLException("Protocol version (" + chaltok[2] + ") unparseable as integer."); } switch (chaltok[4]) { @@ -501,47 +500,57 @@ public class MapiConnection extends Mone // handle the challenge according to the version it is switch (this.version) { case 9: - // proto 9 is like 8, but uses a hash instead of the plain password, the server tells us which hash in - // the challenge after the byte-order + // proto 9 is like 8, but uses a hash instead of the plain password + // the server tells us (in 6th token) which hash in the + // challenge after the byte-order token + + String algo; + String pwhash = chaltok[5]; /* NOTE: Java doesn't support RIPEMD160 :( */ - switch (chaltok[5]) { - case "SHA512": - algo = "SHA-512"; - break; - case "SHA384": - algo = "SHA-384"; - break; - case "SHA256": - algo = "SHA-256"; - /* NOTE: Java supports SHA-224 only on 8 */ - break; - case "SHA1": - algo = "SHA-1"; - break; - case "MD5": - algo = "MD5"; - break; - default: - throw new MCLException("Unsupported password hash: " + chaltok[5]); + if (pwhash.equals("SHA512")) { + algo = "SHA-512"; + } else if (pwhash.equals("SHA384")) { + algo = "SHA-384"; + } else if (pwhash.equals("SHA256")) { + algo = "SHA-256"; + /* NOTE: Java doesn't support SHA-224 */ + } else if (pwhash.equals("SHA1")) { + algo = "SHA-1"; + } else if (pwhash.equals("MD5")) { + algo = "MD5"; + } else { + throw new MCLException("Unsupported password hash: " + pwhash); + } + try { + password = ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + throw new MCLException("This JVM does not support password hash: " + pwhash + "\n" + e.toString()); + } catch (UnsupportedEncodingException e) { + throw new MCLException("This JVM does not support UTF-8 encoding\n" + e.toString()); } - password = ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8")); - - // proto 7 (finally) used the challenge and works with a password hash. The supported implementations - // come from the server challenge. We chose the best hash we can find, in the order SHA1, MD5, plain. - // Also, the byte-order is reported in the challenge string. proto 8 made this obsolete, but retained - // the byte-order report for future "binary" transports. In proto 8, the byte-order of the blocks is - // always little endian because most machines today are. - String hashes = (hash == null ? chaltok[3] : hash); - Set<String> hashesSet = new HashSet<>(Arrays.asList(hashes.toUpperCase().split("[, ]"))); + // proto 7 (finally) used the challenge and works with a + // password hash. The supported implementations come + // from the server challenge. We chose the best hash + // we can find, in the order SHA512, SHA1, MD5, plain. + // Also the byte-order is reported in the challenge string, + // which makes sense, since only blockmode is supported. + // proto 8 made this obsolete, but retained the + // byte-order report for future "binary" transports. + // In proto 8, the byte-order of the blocks is always little + // endian because most machines today are. + String hashes = (hash == null || hash.isEmpty()) ? chaltok[3] : hash; + HashSet<String> hashesSet = new HashSet<String>(Arrays.asList(hashes.toUpperCase().split("[, ]"))); // split on comma or space // if we deal with merovingian, mask our credentials - if (servert.equals("merovingian") && !language.equals("control")) { + if (chaltok[1].equals("merovingian") && !language.equals("control")) { username = "merovingian"; password = "merovingian"; } - String pwhash; + // reuse variables algo and pwhash + algo = null; + pwhash = null; if (hashesSet.contains("SHA512")) { algo = "SHA-512"; pwhash = "{SHA512}"; @@ -558,11 +567,26 @@ public class MapiConnection extends Mone algo = "MD5"; pwhash = "{MD5}"; } else { - throw new MCLException("No supported password hashes in " + hashes); + throw new MCLException("no supported hash algorithms found in " + hashes); } - pwhash += ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8"), - challenge.getBytes("UTF-8")); + try { + pwhash += ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8"), + challenge.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + throw new MCLException("This JVM does not support password hash: " + pwhash + "\n" + e.toString()); + } catch (UnsupportedEncodingException e) { + throw new MCLException("This JVM does not support UTF-8 encoding\n" + e.toString()); + } + + // TODO: some day when we need this, we should store this + if (chaltok[4].equals("BIG")) { + // byte-order of server is big-endian + } else if (chaltok[4].equals("LIT")) { + // byte-order of server is little-endian + } else { + throw new ProtocolException("Invalid byte-order: " + chaltok[4]); + } // generate response response = "BIG:"; // JVM byte-order is big-endian