diff --git a/.gitignore b/.gitignore
index e8223c0e..c07dcb97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
/manager/.settings/*
/driver/.settings/*
/server/.settings/*
+/manager/classes/
+/driver/classes/
+/server/classes/
.project
.classpath
.DS_Store
diff --git a/README.md b/README.md
index 6d976ed7..35b22e74 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@

[](https://www.apache.org/licenses/LICENSE-2.0.html)
-
+
## What is Cobar?
diff --git a/manager/pom.xml b/manager/pom.xml
index 63e7a481..82873ad9 100644
--- a/manager/pom.xml
+++ b/manager/pom.xml
@@ -28,6 +28,7 @@
1.0.5
GBK
+ 2.5.6.SEC03
@@ -45,52 +46,52 @@
org.springframework
spring
- 2.5.6
+ ${spring.version}
org.springframework
spring-aop
- 2.5.6
+ ${spring.version}
org.springframework
spring-beans
- 2.5.6
+ ${spring.version}
org.springframework
spring-jdbc
- 2.5.6
+ ${spring.version}
org.springframework
spring-context-support
- 2.5.6
+ ${spring.version}
org.springframework
spring-context
- 2.5.6
+ ${spring.version}
org.springframework
spring-core
- 2.5.6
+ ${spring.version}
org.springframework
spring-web
- 2.5.6
+ ${spring.version}
org.springframework
spring-webmvc
- 2.5.6
+ ${spring.version}
org.springframework
spring-test
- 2.5.6
+ ${spring.version}
test
diff --git a/server/assembly/bin/startup.bat b/server/assembly/bin/startup.bat
index ff260aa3..194e8166 100644
--- a/server/assembly/bin/startup.bat
+++ b/server/assembly/bin/startup.bat
@@ -55,7 +55,7 @@ echo ---------------------------------------------------
goto end
:okHome
-set "APP_VERSION=1.2.7"
+set "APP_VERSION=1.2.8-SNAPSHOT"
REM set COBAR_CLASSPATH
set "COBAR_CLASSPATH=%COBAR_HOME%\conf;%COBAR_HOME%\lib\classes"
diff --git a/server/assembly/logs/.gitkeep b/server/assembly/logs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/server/pom.xml b/server/pom.xml
index 1d3dd3a8..5faa8530 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -21,13 +21,13 @@
cobar-server
${app.version}
jar
- ${artifactId}
+ ${project.artifactId}
The project of cobar-server
http://code.alibabatech.com/wiki/display/Cobar
UTF-8
- 1.2.7
+ 1.2.8-SNAPSHOT
@@ -160,4 +160,4 @@
-
\ No newline at end of file
+
diff --git a/server/src/main/net/com/alibaba/cobar/net/handler/FrontendAuthenticator.java b/server/src/main/net/com/alibaba/cobar/net/handler/FrontendAuthenticator.java
index d844204b..a71ef5be 100644
--- a/server/src/main/net/com/alibaba/cobar/net/handler/FrontendAuthenticator.java
+++ b/server/src/main/net/com/alibaba/cobar/net/handler/FrontendAuthenticator.java
@@ -1,21 +1,20 @@
/*
* Copyright 1999-2012 Alibaba Group.
- *
- * 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.
+ *
+ * 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 com.alibaba.cobar.net.handler;
import java.nio.ByteBuffer;
+import java.security.DigestException;
import java.security.NoSuchAlgorithmException;
import java.util.Set;
@@ -35,131 +34,148 @@
* @author xianmao.hexm
*/
public class FrontendAuthenticator implements NIOHandler {
- private static final Logger LOGGER = Logger.getLogger(FrontendAuthenticator.class);
- private static final byte[] AUTH_OK = new byte[] { 7, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0 };
-
- protected final FrontendConnection source;
+ private static final Logger LOGGER = Logger.getLogger(FrontendAuthenticator.class);
+ private static final byte[] AUTH_OK = new byte[] {7, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0};
- public FrontendAuthenticator(FrontendConnection source) {
- this.source = source;
- }
-
- @Override
- public void handle(byte[] data) {
- // check quit packet
- if (data.length == QuitPacket.QUIT.length && data[4] == MySQLPacket.COM_QUIT) {
- source.close();
- return;
- }
+ protected final FrontendConnection source;
- AuthPacket auth = new AuthPacket();
- auth.read(data);
+ public FrontendAuthenticator(FrontendConnection source) {
+ this.source = source;
+ }
- // check user
- if (!checkUser(auth.user, source.getHost())) {
- failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "'");
- return;
- }
+ @Override
+ public void handle(byte[] data) {
+ // check quit packet
+ if (data.length == QuitPacket.QUIT.length && data[4] == MySQLPacket.COM_QUIT) {
+ source.close();
+ return;
+ }
- // check password
- if (!checkPassword(auth.password, auth.user)) {
- failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "'");
- return;
- }
+ AuthPacket auth = new AuthPacket();
+ auth.read(data);
- // check schema
- switch (checkSchema(auth.database, auth.user)) {
- case ErrorCode.ER_BAD_DB_ERROR:
- failure(ErrorCode.ER_BAD_DB_ERROR, "Unknown database '" + auth.database + "'");
- break;
- case ErrorCode.ER_DBACCESS_DENIED_ERROR:
- String s = "Access denied for user '" + auth.user + "' to database '" + auth.database + "'";
- failure(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);
- break;
- default:
- success(auth);
- }
+ // check user
+ if (!checkUser(auth.user, source.getHost())) {
+ failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "'");
+ return;
}
- protected boolean checkUser(String user, String host) {
- return source.getPrivileges().userExists(user, host);
+ // check password
+ if (!checkPassword(auth.password, auth.user)) {
+ failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "'");
+ return;
}
- protected boolean checkPassword(byte[] password, String user) {
- String pass = source.getPrivileges().getPassword(user);
+ // check schema
+ switch (checkSchema(auth.database, auth.user)) {
+ case ErrorCode.ER_BAD_DB_ERROR:
+ failure(ErrorCode.ER_BAD_DB_ERROR, "Unknown database '" + auth.database + "'");
+ break;
+ case ErrorCode.ER_DBACCESS_DENIED_ERROR:
+ String s = "Access denied for user '" + auth.user + "' to database '" + auth.database + "'";
+ failure(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);
+ break;
+ default:
+ success(auth);
+ }
+ }
- // check null
- if (pass == null || pass.length() == 0) {
- if (password == null || password.length == 0) {
- return true;
- } else {
- return false;
- }
- }
- if (password == null || password.length == 0) {
- return false;
- }
+ protected boolean checkUser(String user, String host) {
+ return source.getPrivileges().userExists(user, host);
+ }
- // encrypt
- byte[] encryptPass = null;
- try {
- encryptPass = SecurityUtil.scramble411(pass.getBytes(), source.getSeed());
- } catch (NoSuchAlgorithmException e) {
- LOGGER.warn(source.toString(), e);
- return false;
- }
- if (encryptPass != null && (encryptPass.length == password.length)) {
- int i = encryptPass.length;
- while (i-- != 0) {
- if (encryptPass[i] != password[i]) {
- return false;
- }
- }
- } else {
- return false;
- }
+ protected boolean checkPassword(byte[] password, String user) {
+ String pass = source.getPrivileges().getPassword(user);
+ // check null
+ if (pass == null || pass.length() == 0) {
+ if (password == null || password.length == 0) {
return true;
+ } else {
+ return false;
+ }
+ }
+ if (password == null || password.length == 0) {
+ return false;
}
- protected int checkSchema(String schema, String user) {
- if (schema == null) {
- return 0;
- }
- FrontendPrivileges privileges = source.getPrivileges();
- if (!privileges.schemaExists(schema)) {
- return ErrorCode.ER_BAD_DB_ERROR;
- }
- Set schemas = privileges.getUserSchemas(user);
- if (schemas == null || schemas.size() == 0 || schemas.contains(schema)) {
- return 0;
- } else {
- return ErrorCode.ER_DBACCESS_DENIED_ERROR;
- }
+ byte[] passBytes = pass.getBytes();
+ byte[] encryptPass = null;
+
+ // encrypt 1
+ try {
+ encryptPass = SecurityUtil.scramble411(passBytes, source.getSeed());
+ } catch (NoSuchAlgorithmException e) {
+ LOGGER.warn(source.toString(), e);
+ return false;
+ }
+ boolean auth = checkBytes(encryptPass, password);
+ if (auth) {
+ return true;
}
- protected void success(AuthPacket auth) {
- source.setAuthenticated(true);
- source.setUser(auth.user);
- source.setSchema(auth.database);
- source.setCharsetIndex(auth.charsetIndex);
- source.setHandler(new FrontendCommandHandler(source));
- if (LOGGER.isInfoEnabled()) {
- StringBuilder s = new StringBuilder();
- s.append(source).append('\'').append(auth.user).append("' login success");
- byte[] extra = auth.extra;
- if (extra != null && extra.length > 0) {
- s.append(",extra:").append(new String(extra));
- }
- LOGGER.info(s.toString());
+ // encrypt 2
+ try {
+ encryptPass = SecurityUtil.scrambleCachingSha2(passBytes, source.getSeed());
+ } catch (DigestException e) {
+ LOGGER.warn(source.toString(), e);
+ return false;
+ }
+ return checkBytes(encryptPass, password);
+ }
+
+ private boolean checkBytes(byte[] encryptPass, byte[] password) {
+ if (encryptPass != null && (encryptPass.length == password.length)) {
+ int i = encryptPass.length;
+ while (i-- != 0) {
+ if (encryptPass[i] != password[i]) {
+ return false;
}
- ByteBuffer buffer = source.allocate();
- source.write(source.writeToBuffer(AUTH_OK, buffer));
+ }
+ return true;
+ } else {
+ return false;
}
+ }
- protected void failure(int errno, String info) {
- LOGGER.error(source.toString() + info);
- source.writeErrMessage((byte) 2, errno, info);
+ protected int checkSchema(String schema, String user) {
+ if (schema == null) {
+ return 0;
+ }
+ FrontendPrivileges privileges = source.getPrivileges();
+ if (!privileges.schemaExists(schema)) {
+ return ErrorCode.ER_BAD_DB_ERROR;
+ }
+ Set schemas = privileges.getUserSchemas(user);
+ if (schemas == null || schemas.size() == 0 || schemas.contains(schema)) {
+ return 0;
+ } else {
+ return ErrorCode.ER_DBACCESS_DENIED_ERROR;
+ }
+ }
+
+ protected void success(AuthPacket auth) {
+ source.setAuthenticated(true);
+ source.setUser(auth.user);
+ source.setSchema(auth.database);
+ source.setCharsetIndex(auth.charsetIndex);
+ source.setHandler(new FrontendCommandHandler(source));
+ if (LOGGER.isInfoEnabled()) {
+ StringBuilder s = new StringBuilder();
+ s.append(source).append('\'').append(auth.user).append("' login success");
+ byte[] extra = auth.extra;
+ if (extra != null && extra.length > 0) {
+ s.append(",extra:").append(new String(extra));
+ }
+ LOGGER.info(s.toString());
}
+ ByteBuffer buffer = source.allocate();
+ source.write(source.writeToBuffer(AUTH_OK, buffer));
+ }
+
+ protected void failure(int errno, String info) {
+ LOGGER.error(source.toString() + info);
+ source.writeErrMessage((byte) 2, errno, info);
+ }
}
diff --git a/server/src/main/server/com/alibaba/cobar/mysql/SecurityUtil.java b/server/src/main/server/com/alibaba/cobar/mysql/SecurityUtil.java
index baf0f02a..68f493cf 100644
--- a/server/src/main/server/com/alibaba/cobar/mysql/SecurityUtil.java
+++ b/server/src/main/server/com/alibaba/cobar/mysql/SecurityUtil.java
@@ -1,20 +1,19 @@
/*
* Copyright 1999-2012 Alibaba Group.
- *
- * 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.
+ *
+ * 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 com.alibaba.cobar.mysql;
+import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -25,70 +24,115 @@
*/
public class SecurityUtil {
- public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- byte[] pass1 = md.digest(pass);
- md.reset();
- byte[] pass2 = md.digest(pass1);
- md.reset();
- md.update(seed);
- byte[] pass3 = md.digest(pass2);
- for (int i = 0; i < pass3.length; i++) {
- pass3[i] = (byte) (pass3[i] ^ pass1[i]);
- }
- return pass3;
+ private static final int CACHING_SHA2_DIGEST_LENGTH = 32;
+
+ public static byte[] scrambleCachingSha2(byte[] password, byte[] seed) throws DigestException {
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException ex) {
+ throw new DigestException(ex);
+ }
+
+ byte[] dig1 = new byte[CACHING_SHA2_DIGEST_LENGTH];
+ byte[] dig2 = new byte[CACHING_SHA2_DIGEST_LENGTH];
+ byte[] scramble1 = new byte[CACHING_SHA2_DIGEST_LENGTH];
+
+ // SHA2(src) => digest_stage1
+ md.update(password, 0, password.length);
+ md.digest(dig1, 0, CACHING_SHA2_DIGEST_LENGTH);
+ md.reset();
+
+ // SHA2(digest_stage1) => digest_stage2
+ md.update(dig1, 0, dig1.length);
+ md.digest(dig2, 0, CACHING_SHA2_DIGEST_LENGTH);
+ md.reset();
+
+ // SHA2(digest_stage2, m_rnd) => scramble_stage1
+ md.update(dig2, 0, dig1.length);
+ md.update(seed, 0, seed.length);
+ md.digest(scramble1, 0, CACHING_SHA2_DIGEST_LENGTH);
+
+ // XOR(digest_stage1, scramble_stage1) => scramble
+ byte[] mysqlScrambleBuff = new byte[CACHING_SHA2_DIGEST_LENGTH];
+ xorString(dig1, mysqlScrambleBuff, scramble1, CACHING_SHA2_DIGEST_LENGTH);
+
+ return mysqlScrambleBuff;
+ }
+
+ public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ byte[] pass1 = md.digest(pass);
+ md.reset();
+ byte[] pass2 = md.digest(pass1);
+ md.reset();
+ md.update(seed);
+ byte[] pass3 = md.digest(pass2);
+ for (int i = 0; i < pass3.length; i++) {
+ pass3[i] = (byte) (pass3[i] ^ pass1[i]);
+ }
+ return pass3;
+ }
+
+ public static final String scramble323(String pass, String seed) {
+ if ((pass == null) || (pass.length() == 0)) {
+ return pass;
+ }
+ byte b;
+ double d;
+ long[] pw = hash(seed);
+ long[] msg = hash(pass);
+ long max = 0x3fffffffL;
+ long seed1 = (pw[0] ^ msg[0]) % max;
+ long seed2 = (pw[1] ^ msg[1]) % max;
+ char[] chars = new char[seed.length()];
+ for (int i = 0; i < seed.length(); i++) {
+ seed1 = ((seed1 * 3) + seed2) % max;
+ seed2 = (seed1 + seed2 + 33) % max;
+ d = (double) seed1 / (double) max;
+ b = (byte) java.lang.Math.floor((d * 31) + 64);
+ chars[i] = (char) b;
+ }
+ seed1 = ((seed1 * 3) + seed2) % max;
+ seed2 = (seed1 + seed2 + 33) % max;
+ d = (double) seed1 / (double) max;
+ b = (byte) java.lang.Math.floor(d * 31);
+ for (int i = 0; i < seed.length(); i++) {
+ chars[i] ^= (char) b;
}
+ return new String(chars);
+ }
- public static final String scramble323(String pass, String seed) {
- if ((pass == null) || (pass.length() == 0)) {
- return pass;
- }
- byte b;
- double d;
- long[] pw = hash(seed);
- long[] msg = hash(pass);
- long max = 0x3fffffffL;
- long seed1 = (pw[0] ^ msg[0]) % max;
- long seed2 = (pw[1] ^ msg[1]) % max;
- char[] chars = new char[seed.length()];
- for (int i = 0; i < seed.length(); i++) {
- seed1 = ((seed1 * 3) + seed2) % max;
- seed2 = (seed1 + seed2 + 33) % max;
- d = (double) seed1 / (double) max;
- b = (byte) java.lang.Math.floor((d * 31) + 64);
- chars[i] = (char) b;
- }
- seed1 = ((seed1 * 3) + seed2) % max;
- seed2 = (seed1 + seed2 + 33) % max;
- d = (double) seed1 / (double) max;
- b = (byte) java.lang.Math.floor(d * 31);
- for (int i = 0; i < seed.length(); i++) {
- chars[i] ^= (char) b;
- }
- return new String(chars);
+ private static long[] hash(String src) {
+ long nr = 1345345333L;
+ long add = 7;
+ long nr2 = 0x12345671L;
+ long tmp;
+ for (int i = 0; i < src.length(); ++i) {
+ switch (src.charAt(i)) {
+ case ' ':
+ case '\t':
+ continue;
+ default:
+ tmp = (0xff & src.charAt(i));
+ nr ^= ((((nr & 63) + add) * tmp) + (nr << 8));
+ nr2 += ((nr2 << 8) ^ nr);
+ add += tmp;
+ }
}
+ long[] result = new long[2];
+ result[0] = nr & 0x7fffffffL;
+ result[1] = nr2 & 0x7fffffffL;
+ return result;
+ }
- private static long[] hash(String src) {
- long nr = 1345345333L;
- long add = 7;
- long nr2 = 0x12345671L;
- long tmp;
- for (int i = 0; i < src.length(); ++i) {
- switch (src.charAt(i)) {
- case ' ':
- case '\t':
- continue;
- default:
- tmp = (0xff & src.charAt(i));
- nr ^= ((((nr & 63) + add) * tmp) + (nr << 8));
- nr2 += ((nr2 << 8) ^ nr);
- add += tmp;
- }
- }
- long[] result = new long[2];
- result[0] = nr & 0x7fffffffL;
- result[1] = nr2 & 0x7fffffffL;
- return result;
+ private static void xorString(byte[] from, byte[] to, byte[] scramble, int length) {
+ int pos = 0;
+ int scrambleLength = scramble.length;
+ while (pos < length) {
+ to[pos] = (byte) (from[pos] ^ scramble[pos % scrambleLength]);
+ pos++;
}
+ }
}
diff --git a/server/src/main/server/com/alibaba/cobar/mysql/bio/MySQLChannel.java b/server/src/main/server/com/alibaba/cobar/mysql/bio/MySQLChannel.java
index 15093ddb..484ab487 100644
--- a/server/src/main/server/com/alibaba/cobar/mysql/bio/MySQLChannel.java
+++ b/server/src/main/server/com/alibaba/cobar/mysql/bio/MySQLChannel.java
@@ -210,8 +210,9 @@ public BinaryPacket execute(RouteResultsetNode rrn, ServerConnection sc, boolean
详细见:http://dev.mysql.com/doc/connector-j/en/connector-j-usagenotes-troubleshooting.html#qandaitem-15-1-15
https://dev.mysql.com/doc/relnotes/connector-j/en/news-5-1-13.html
*/
- if(this.charsetIndex != 45 && sc.getCharsetIndex() != 33)
+ if (!(this.charsetIndex == 45 && sc.getCharsetIndex() == 33)) {
sendCharset(sc.getCharsetIndex());
+ }
}
if (this.txIsolation != sc.getTxIsolation()) {
sendTxIsolation(sc.getTxIsolation());
@@ -224,8 +225,6 @@ public BinaryPacket execute(RouteResultsetNode rrn, ServerConnection sc, boolean
CommandPacket packet = new CommandPacket();
packet.packetId = 0;
packet.command = MySQLPacket.COM_QUERY;
-
-
packet.arg = rrn.getStatement().getBytes(charset);
// 记录执行开始时间
@@ -465,15 +464,12 @@ private void sendCharset(int ci) throws IOException {
cmd.write(out);
out.flush();
-
BinaryPacket bin = receive();
switch (bin.data[0]) {
case OkPacket.FIELD_COUNT:
-
this.charsetIndex = ci;
this.charset = CharsetUtil.getCharset(ci);
this.dbCharset = CharsetUtil.getCharset(ci);
-
break;
case ErrorPacket.FIELD_COUNT:
ErrorPacket err = new ErrorPacket();