diff --git a/assets/local.js b/assets/local.js new file mode 100644 index 00000000..eeadd538 --- /dev/null +++ b/assets/local.js @@ -0,0 +1,342 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + + var merge, merge_sort; + + merge = function(left, right, comparison) { + var result; + result = new Array(); + while ((left.length > 0) && (right.length > 0)) { + if (comparison(left[0], right[0]) <= 0) { + result.push(left.shift()); + } else { + result.push(right.shift()); + } + } + while (left.length > 0) { + result.push(left.shift()); + } + while (right.length > 0) { + result.push(right.shift()); + } + return result; + }; + + merge_sort = function(array, comparison) { + var middle; + if (array.length < 2) { + return array; + } + middle = Math.ceil(array.length / 2); + return merge(merge_sort(array.slice(0, middle), comparison), merge_sort(array.slice(middle), comparison), comparison); + }; + + var crypto, int32Max, merge_sort; + + crypto = require("crypto"); + + int32Max = Math.pow(2, 32); + + getTable = function(key) { + var ah, al, decrypt_table, hash, i, md5sum, table; + table = new Array(256); + decrypt_table = new Array(256); + md5sum = crypto.createHash("md5"); + md5sum.update(key); + hash = new Buffer(md5sum.digest(), "binary"); + al = hash.readUInt32LE(0); + ah = hash.readUInt32LE(4); + i = 0; + while (i < 256) { + table[i] = i; + i++; + } + i = 1; + while (i < 1024) { + table = merge_sort(table, function(x, y) { + return ((ah % (x + i)) * int32Max + al) % (x + i) - ((ah % (y + i)) * int32Max + al) % (y + i); + }); + i++; + } + i = 0; + while (i < 256) { + decrypt_table[table[i]] = i; + ++i; + } + return [table, decrypt_table]; + }; + + encrypt = function(table, buf) { + var i; + i = 0; + while (i < buf.length) { + buf[i] = table[buf[i]]; + i++; + } + return buf; + }; + + parseArgs = function() { + var defination, lastKey, nextIsValue, oneArg, result, _, _ref; + defination = { + '-l': 'local_port', + '-p': 'server_port', + '-s': 'server', + '-k': 'password' + }; + result = {}; + nextIsValue = false; + lastKey = null; + _ref = process.argv; + for (_ in _ref) { + oneArg = _ref[_]; + if (nextIsValue) { + result[lastKey] = oneArg; + nextIsValue = false; + } else if (oneArg in defination) { + lastKey = defination[oneArg]; + nextIsValue = true; + } + } + return result; + }; + + var KEY, PORT, REMOTE_PORT, SERVER, config, configContent, configFromArgs, decryptTable, encrypt, encryptTable, fs, getServer, inetAton, inetNtoa, k, net, path, server, tables, timeout, v; + + inetNtoa = function(buf) { + return buf[0] + "." + buf[1] + "." + buf[2] + "." + buf[3]; + }; + + inetAton = function(ipStr) { + var buf, i, parts; + parts = ipStr.split("."); + if (parts.length !== 4) { + return null; + } else { + buf = new Buffer(4); + i = 0; + while (i < 4) { + buf[i] = +parts[i]; + i++; + } + return buf; + } + }; + + fs = require("fs"); + + path = require("path"); + + configFromArgs = parseArgs(); + + config = { + "server": "127.0.0.1", + "server_port": 8388, + "local_prot": 1080, + "password": "test", + "timeout": 60 + } + + for (k in configFromArgs) { + v = configFromArgs[k]; + config[k] = v; + } + + SERVER = config.server; + + REMOTE_PORT = config.server_port; + + PORT = config.local_port; + + KEY = config.password; + + timeout = Math.floor(config.timeout * 1000); + + getServer = function() { + if (SERVER instanceof Array) { + return SERVER[Math.floor(Math.random() * SERVER.length)]; + } else { + return SERVER; + } + }; + + net = require("net"); + + console.log("calculating ciphers"); + + tables = getTable(KEY); + + encryptTable = tables[0]; + + decryptTable = tables[1]; + + server = net.createServer(function(connection) { + var addrLen, addrToSend, cachedPieces, headerLength, remote, remoteAddr, remotePort, stage; + console.log("server connected"); + console.log("concurrent connections: " + server.connections); + stage = 0; + headerLength = 0; + remote = null; + cachedPieces = []; + addrLen = 0; + remoteAddr = null; + remotePort = null; + addrToSend = ""; + connection.on("data", function(data) { + var aServer, addrtype, buf, cmd, reply, tempBuf; + if (stage === 5) { + encrypt(encryptTable, data); + if (!remote.write(data)) { + connection.pause(); + } + return; + } + if (stage === 0) { + tempBuf = new Buffer(2); + tempBuf.write("\u0005\u0000", 0); + connection.write(tempBuf); + stage = 1; + return; + } + if (stage === 1) { + try { + cmd = data[1]; + addrtype = data[3]; + if (cmd !== 1) { + console.warn("unsupported cmd: " + cmd); + reply = new Buffer("\u0005\u0007\u0000\u0001", "binary"); + connection.end(reply); + return; + } + if (addrtype === 3) { + addrLen = data[4]; + } else if (addrtype !== 1) { + console.warn("unsupported addrtype: " + addrtype); + connection.end(); + return; + } + addrToSend = data.slice(3, 4).toString("binary"); + if (addrtype === 1) { + remoteAddr = inetNtoa(data.slice(4, 8)); + addrToSend += data.slice(4, 10).toString("binary"); + remotePort = data.readUInt16BE(8); + headerLength = 10; + } else { + remoteAddr = data.slice(5, 5 + addrLen).toString("binary"); + addrToSend += data.slice(4, 5 + addrLen + 2).toString("binary"); + remotePort = data.readUInt16BE(5 + addrLen); + headerLength = 5 + addrLen + 2; + } + buf = new Buffer(10); + buf.write("\u0005\u0000\u0000\u0001", 0, 4, "binary"); + buf.write("\u0000\u0000\u0000\u0000", 4, 4, "binary"); + buf.writeInt16BE(remotePort, 8); + connection.write(buf); + aServer = getServer(); + remote = net.connect(REMOTE_PORT, aServer, function() { + var addrToSendBuf, i, piece; + console.log("connecting " + remoteAddr + " via " + aServer); + addrToSendBuf = new Buffer(addrToSend, "binary"); + encrypt(encryptTable, addrToSendBuf); + remote.write(addrToSendBuf); + i = 0; + while (i < cachedPieces.length) { + piece = cachedPieces[i]; + encrypt(encryptTable, piece); + remote.write(piece); + i++; + } + cachedPieces = null; + return stage = 5; + }); + remote.on("data", function(data) { + encrypt(decryptTable, data); + if (!connection.write(data)) { + return remote.pause(); + } + }); + remote.on("end", function() { + console.log("remote disconnected"); + connection.end(); + return console.log("concurrent connections: " + server.connections); + }); + remote.on("error", function() { + if (stage === 4) { + console.warn("remote connection refused"); + connection.destroy(); + return; + } + console.warn("remote error"); + connection.end(); + return console.log("concurrent connections: " + server.connections); + }); + remote.on("drain", function() { + return connection.resume(); + }); + remote.setTimeout(timeout, function() { + connection.end(); + return remote.destroy(); + }); + if (data.length > headerLength) { + buf = new Buffer(data.length - headerLength); + data.copy(buf, 0, headerLength); + cachedPieces.push(buf); + buf = null; + } + return stage = 4; + } catch (e) { + console.warn(e); + connection.destroy(); + if (remote) { + return remote.destroy(); + } + } + } else { + if (stage === 4) { + return cachedPieces.push(data); + } + } + }); + connection.on("end", function() { + console.log("server disconnected"); + if (remote) { + remote.destroy(); + } + return console.log("concurrent connections: " + server.connections); + }); + connection.on("error", function() { + console.warn("server error"); + if (remote) { + remote.destroy(); + } + return console.log("concurrent connections: " + server.connections); + }); + connection.on("drain", function() { + if (remote && stage === 5) { + return remote.resume(); + } + }); + return connection.setTimeout(timeout, function() { + if (remote) { + remote.destroy(); + } + return connection.destroy(); + }); + }); + + var pid = ""+process.pid; // need to turn into a string + fs.writeFile('/data/data/com.github.shadowsocks/shadowsocks.pid', pid, function (err) { + // Ignore + }); + + server.listen(PORT, function() { + return console.log("server listening at port " + PORT); + }); + + server.on("error", function(e) { + if (e.code === "EADDRINUSE") { + return console.warn("Address in use, aborting"); + } + }); + +}).call(this); diff --git a/assets/node b/assets/node new file mode 100755 index 00000000..ac7d2669 Binary files /dev/null and b/assets/node differ diff --git a/assets/shadowsocks b/assets/shadowsocks deleted file mode 100755 index 465409d3..00000000 Binary files a/assets/shadowsocks and /dev/null differ diff --git a/src/com/github/shadowsocks/Shadowsocks.java b/src/com/github/shadowsocks/Shadowsocks.java index 13973aa2..3e6fb95a 100644 --- a/src/com/github/shadowsocks/Shadowsocks.java +++ b/src/com/github/shadowsocks/Shadowsocks.java @@ -504,9 +504,9 @@ public class Shadowsocks extends PreferenceActivity implements sb.append("kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`").append("\n"); sb.append("kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`").append("\n"); sb.append("kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`").append("\n"); - sb.append("killall pdnsd").append("\n"); - sb.append("killall redsocks").append("\n"); - sb.append("killall shadowsocks").append("\n"); + sb.append("killall -9 pdnsd").append("\n"); + sb.append("killall -9 redsocks").append("\n"); + sb.append("killall -9 node").append("\n"); Utils.runRootCommand(sb.toString()); copyAssets(""); @@ -514,7 +514,7 @@ public class Shadowsocks extends PreferenceActivity implements Utils.runCommand("chmod 755 /data/data/com.github.shadowsocks/iptables\n" + "chmod 755 /data/data/com.github.shadowsocks/redsocks\n" + "chmod 755 /data/data/com.github.shadowsocks/pdnsd\n" - + "chmod 755 /data/data/com.github.shadowsocks/shadowsocks\n" + + "chmod 755 /data/data/com.github.shadowsocks/node\n" ); } diff --git a/src/com/github/shadowsocks/ShadowsocksService.java b/src/com/github/shadowsocks/ShadowsocksService.java index 6b105964..99e2e9a2 100755 --- a/src/com/github/shadowsocks/ShadowsocksService.java +++ b/src/com/github/shadowsocks/ShadowsocksService.java @@ -79,15 +79,13 @@ public class ShadowsocksService extends Service { " port = %d;" + " type = socks5;" + "}"; + final static String SHADOWSOCKS_CONF = + "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"; final static String CMD_IPTABLES_RETURN = " -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"; - final static String CMD_IPTABLES_REDIRECT_ADD_HTTP = " -t nat -A OUTPUT -p tcp " - + "--dport 80 -j REDIRECT --to 8123\n"; - final static String CMD_IPTABLES_REDIRECT_ADD_HTTPS = " -t nat -A OUTPUT -p tcp " - + "--dport 443 -j REDIRECT --to 8124\n"; - final static String CMD_IPTABLES_DNAT_ADD_HTTP = " -t nat -A OUTPUT -p tcp " - + "--dport 80 -j DNAT --to-destination 127.0.0.1:8123\n"; - final static String CMD_IPTABLES_DNAT_ADD_HTTPS = " -t nat -A OUTPUT -p tcp " - + "--dport 443 -j DNAT --to-destination 127.0.0.1:8124\n"; + final static String CMD_IPTABLES_REDIRECT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + + "-j REDIRECT --to 8123\n"; + final static String CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + + "-j DNAT --to-destination 127.0.0.1:8123\n"; private static final int MSG_CONNECT_START = 0; private static final int MSG_CONNECT_FINISH = 1; private static final int MSG_CONNECT_SUCCESS = 2; @@ -185,8 +183,8 @@ public class ShadowsocksService extends Service { } public void startShadowsocksDaemon() { - final String cmd = String.format(BASE - + "shadowsocks \"%s\" \"%d\" \"%d\" \"%s\"", + final String cmd = String.format("nohup " + BASE + + "node " + BASE + "local.js -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" &", appHost, remotePort, port, sitekey); Utils.runRootCommand(cmd); } @@ -475,6 +473,9 @@ public class ShadowsocksService extends Service { String cmd_bypass = Utils.getIptables() + CMD_IPTABLES_RETURN; init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport " + remotePort)); + init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport " + 53)); + init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport " + 8153)); + init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport " + port)); init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + getApplicationInfo().uid)); @@ -488,11 +489,8 @@ public class ShadowsocksService extends Service { } if (isGlobalProxy || isBypassApps) { http_sb.append(hasRedirectSupport ? Utils.getIptables() - + CMD_IPTABLES_REDIRECT_ADD_HTTP : Utils.getIptables() - + CMD_IPTABLES_DNAT_ADD_HTTP); - https_sb.append(hasRedirectSupport ? Utils.getIptables() - + CMD_IPTABLES_REDIRECT_ADD_HTTPS : Utils.getIptables() - + CMD_IPTABLES_DNAT_ADD_HTTPS); + + CMD_IPTABLES_REDIRECT_ADD_SOCKS : Utils.getIptables() + + CMD_IPTABLES_DNAT_ADD_SOCKS); } if (!isGlobalProxy) { // for proxy specified apps @@ -508,12 +506,8 @@ public class ShadowsocksService extends Service { for (int uid : uidSet) { if (!isBypassApps) { http_sb.append((hasRedirectSupport ? Utils.getIptables() - + CMD_IPTABLES_REDIRECT_ADD_HTTP : Utils.getIptables() - + CMD_IPTABLES_DNAT_ADD_HTTP).replace("-t nat", - "-t nat -m owner --uid-owner " + uid)); - https_sb.append((hasRedirectSupport ? Utils.getIptables() - + CMD_IPTABLES_REDIRECT_ADD_HTTPS : Utils.getIptables() - + CMD_IPTABLES_DNAT_ADD_HTTPS).replace("-t nat", + + CMD_IPTABLES_REDIRECT_ADD_SOCKS : Utils.getIptables() + + CMD_IPTABLES_DNAT_ADD_SOCKS).replace("-t nat", "-t nat -m owner --uid-owner " + uid)); } else { init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid));