Changeset: b58c1b245ede for monetdb-java
URL: https://dev.monetdb.org/hg/monetdb-java?cmd=changeset;node=b58c1b245ede
Modified Files:
src/main/java/nl/cwi/monetdb/mcl/net/MapiSocket.java
Branch: default
Log Message:
Correct and improve implementation of getChallengeResponse() for protocol 9
Add method getLogWriter() so when debug is enabled it can be used from other
places (e.g. MonetConnection)
Disabled unused method: debug(PrintStream out)
Improved comments and documentation
diffs (truncated from 413 to 300 lines):
diff --git a/src/main/java/nl/cwi/monetdb/mcl/net/MapiSocket.java
b/src/main/java/nl/cwi/monetdb/mcl/net/MapiSocket.java
--- a/src/main/java/nl/cwi/monetdb/mcl/net/MapiSocket.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/net/MapiSocket.java
@@ -10,15 +10,12 @@ package nl.cwi.monetdb.mcl.net;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
-import java.io.EOFException;
import java.io.FileWriter;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.Socket;
@@ -32,7 +29,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import nl.cwi.monetdb.mcl.MCLException;
import nl.cwi.monetdb.mcl.io.BufferedMCLReader;
@@ -111,16 +107,18 @@ public final class MapiSocket {
private String language = "sql";
/** The hash methods to use (null = default) */
private String hash = null;
+
/** Whether we should follow redirects */
private boolean followRedirects = true;
/** How many redirections do we follow until we're fed up with it? */
private int ttl = 10;
+
/** Whether we are debugging or not */
private boolean debug = false;
/** The Writer for the debug log-file */
private Writer log;
- /** The blocksize (hardcoded in compliance with stream.mx) */
+ /** The blocksize (hardcoded in compliance with MonetDB
common/stream/stream.h) */
public final static int BLOCK = 8 * 1024 - 2;
/** A short in two bytes for holding the block size in bytes */
@@ -201,7 +199,7 @@ public final class MapiSocket {
* operation to have effect.
*
* @param s The specified timeout, in milliseconds. A timeout
- * of zero is interpreted as an infinite timeout.
+ * of zero will disable timeout (i.e., timeout of infinity).
* @throws SocketException Issue with the socket
*/
public void setSoTimeout(int s) throws SocketException {
@@ -209,7 +207,7 @@ public final class MapiSocket {
throw new IllegalArgumentException("timeout can't be
negative");
}
this.soTimeout = s;
- // limit time to wait on blocking operations (0 = indefinite)
+ // limit time to wait on blocking operations
if (con != null) {
con.setSoTimeout(s);
}
@@ -229,7 +227,7 @@ public final class MapiSocket {
}
/**
- * Enables/disables debug
+ * Enables/disables debug mode with logging to file
*
* @param debug Value to set
*/
@@ -266,7 +264,7 @@ public final class MapiSocket {
throws IOException, UnknownHostException, SocketException,
MCLParseException, 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) {
con = new Socket(host, port);
@@ -288,28 +286,20 @@ public final class MapiSocket {
String c = reader.readLine();
reader.waitForPrompt();
- writer.writeLine(
- getChallengeResponse(
- c,
- user,
- pass,
- language,
- database,
- hash
- )
- );
-
- // read monet response till prompt
+ writer.writeLine(getChallengeResponse(c, user, pass, language,
database, hash));
+ // read monetdb mserver response till prompt
List<String> redirects = new ArrayList<String>();
List<String> warns = new ArrayList<String>();
String err = "", tmp;
int lineType;
do {
- if ((tmp = reader.readLine()) == null)
+ tmp = reader.readLine();
+ if (tmp == null)
throw new IOException("Read from " +
con.getInetAddress().getHostName() + ":" +
con.getPort() + ": End of
stream reached");
- if ((lineType = reader.getLineType()) ==
BufferedMCLReader.ERROR) {
+ lineType = reader.getLineType();
+ if (lineType == BufferedMCLReader.ERROR) {
err += "\n" + tmp.substring(7);
} else if (lineType == BufferedMCLReader.INFO) {
warns.add(tmp.substring(1));
@@ -317,10 +307,12 @@ public final class MapiSocket {
redirects.add(tmp.substring(1));
}
} while (lineType != BufferedMCLReader.PROMPT);
+
if (err.length() > 0) {
close();
- throw new MCLException(err.trim());
+ throw new MCLException(err);
}
+
if (!redirects.isEmpty()) {
if (followRedirects) {
// Ok, server wants us to go somewhere else.
The list
@@ -353,8 +345,7 @@ public final class MapiSocket {
if
(tmp.equals("database")) {
tmp =
args[i].substring(pos + 1);
if
(!tmp.equals(database)) {
-
warns.add("redirect points to different " +
-
"database: " + tmp);
+
warns.add("redirect points to different database: " + tmp);
setDatabase(tmp);
}
} else if
(tmp.equals("language")) {
@@ -393,7 +384,7 @@ public final class MapiSocket {
if (tmp != null && tmp.length() > 0) {
tmp = tmp.substring(1).trim();
if (!tmp.isEmpty() &&
!tmp.equals(database)) {
- warns.add("redirect
points to different " + "database: " + tmp);
+ warns.add("redirect
points to different database: " + tmp);
setDatabase(tmp);
}
}
@@ -424,6 +415,7 @@ public final class MapiSocket {
* string is null, a challengeless response is returned.
*
* @param chalstr the challenge string
+ * for example:
H8sRMhtevGd:mserver:9:PROT10,RIPEMD160,SHA256,SHA1,MD5,COMPRESSION_SNAPPY,COMPRESSION_LZ4:LIT:SHA512:
* @param username the username to use
* @param password the password to use
* @param language the language to use
@@ -438,80 +430,76 @@ public final class MapiSocket {
String database,
String hash
) throws MCLParseException, MCLException, IOException {
- String response;
- String algo;
-
// parse the challenge string, split it on ':'
String[] chaltok = chalstr.split(":");
- if (chaltok.length <= 4)
- throw new MCLParseException("Server challenge string
unusable! Challenge contains too few tokens: " + chalstr);
+ if (chaltok.length <= 5)
+ throw new MCLParseException("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 {
- version = Integer.parseInt(chaltok[2].trim()); //
protocol version
+ version = Integer.parseInt(chaltok[2]); // protocol
version
} catch (NumberFormatException e) {
- throw new MCLParseException("Protocol version
unparseable: " + chaltok[3]);
+ throw new MCLParseException("Protocol version (" +
chaltok[2] + ") unparseable as integer.");
}
// handle the challenge according to the version it is
switch (version) {
- default:
- throw new MCLException("Unsupported protocol
version: " + 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 :( */
- if (chaltok[5].equals("SHA512")) {
+ if (pwhash.equals("SHA512")) {
algo = "SHA-512";
- } else if (chaltok[5].equals("SHA384")) {
+ } else if (pwhash.equals("SHA384")) {
algo = "SHA-384";
- } else if (chaltok[5].equals("SHA256")) {
+ } else if (pwhash.equals("SHA256")) {
algo = "SHA-256";
/* NOTE: Java doesn't support SHA-224 */
- } else if (chaltok[5].equals("SHA1")) {
+ } else if (pwhash.equals("SHA1")) {
algo = "SHA-1";
- } else if (chaltok[5].equals("MD5")) {
+ } else if (pwhash.equals("MD5")) {
algo = "MD5";
} else {
- throw new MCLException("Unsupported
password hash: " + chaltok[5]);
+ throw new MCLException("Unsupported
password hash: " + pwhash);
}
-
try {
MessageDigest md =
MessageDigest.getInstance(algo);
md.update(password.getBytes("UTF-8"));
byte[] digest = md.digest();
password = toHex(digest);
} catch (NoSuchAlgorithmException e) {
- throw new AssertionError("internal
error: " + e.toString());
+ throw new MCLException("This JVM does
not support password hash: " + pwhash + "\n" + e.toString());
} catch (UnsupportedEncodingException e) {
- throw new AssertionError("internal
error: " + e.toString());
+ throw new MCLException("This JVM does
not support UTF-8 encoding\n" + e.toString());
}
// 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,
+ // 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
+ // 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<String>(Arrays.asList(hashes.toUpperCase().split("[, ]")));
+ 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}";
@@ -528,46 +516,41 @@ public final class MapiSocket {
algo = "MD5";
pwhash = "{MD5}";
} else {
- throw new MCLException("no supported
password hashes in " + hashes);
+ throw new MCLException("no supported
hash algorithms found in " + hashes);
}
- if (algo != null) {
- try {
- MessageDigest md =
MessageDigest.getInstance(algo);
-
md.update(password.getBytes("UTF-8"));
-
md.update(challenge.getBytes("UTF-8"));
- byte[] digest = md.digest();
- pwhash += toHex(digest);
- } catch (NoSuchAlgorithmException e) {
- throw new
AssertionError("internal error: " + e.toString());
- } catch (UnsupportedEncodingException
e) {
- throw new
AssertionError("internal error: " + e.toString());
- }
+ try {
+ MessageDigest md =
MessageDigest.getInstance(algo);
+ md.update(password.getBytes("UTF-8"));
+ md.update(challenge.getBytes("UTF-8"));
+ byte[] digest = md.digest();
+ pwhash += toHex(digest);
+ } 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
+
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list