Skip to content

Commit

Permalink
Merge pull request #29 from Bestoa/dev
Browse files Browse the repository at this point in the history
Merge dev to master.
  • Loading branch information
Bestoa authored Jun 23, 2016
2 parents c6f5ddd + a2a8692 commit 862dee0
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 27 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Support these args:
7. -S server mode
8. -L Local mode(client, default)
9. -c config file
10. -t timeout(unit is second)

Crypto method:

Expand All @@ -33,7 +34,7 @@ Crypto method:

One time auth feature done.

Support JSON config file.(local\_address/timeout/fast\_open/workers is not support)
Support JSON config file.(local\_address/fast\_open/workers is not support)

You could refer to demo config etc/demo.json.

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/shadowsocks/nio/tcp/BufferHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ public static ByteBuffer create(int size)
return ByteBuffer.allocate(size);
}

// This logic comes from Grizzly.
// Block current thread, avoid 100% cpu loading.
public static void send(SocketChannel remote, byte [] newData) throws IOException
{
SelectionKey key = null;
Selector writeSelector = null;
int attempts = 0;
// 15s for each write timeout.
int writeTimeout = 15 * 1000;
ByteBuffer bb = ByteBuffer.wrap(newData);
try {
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/shadowsocks/nio/tcp/LocalTcpWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ private void parseHeader() throws IOException
BufferHelper.prepare(bb, 257);
local.read(bb);

//Check socks version.
//TODO: check method list.
bb.flip();
if (bb.get() != 5) {
throw new IOException("INVALID CONNECTION");
}

//reply 0x05(Socks version) 0x00 (no password)
byte [] msg = {0x05, 0x00};
replyToProxyProgram(msg);
Expand Down Expand Up @@ -246,7 +253,7 @@ private void init() throws IOException{

mOneTimeAuth = mConfig.oneTimeAuth;

mConfig.remoteAddress = new InetSocketAddress(InetAddress.getByName(mConfig.server), mConfig.port);
mConfig.remoteAddress = new InetSocketAddress(InetAddress.getByName(mConfig.server), mConfig.serverPort);
}

public LocalTcpWorker(SocketChannel sc, LocalConfig lc){
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/shadowsocks/nio/tcp/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package shadowsocks.nio.tcp;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.ByteArrayOutputStream;
Expand All @@ -23,6 +24,8 @@

public class Session {

private static Logger log = LogManager.getLogger(Session.class.getName());

final static public int ADDR_TYPE_IPV4 = 0x01;
final static public int ADDR_TYPE_HOST = 0x03;

Expand Down Expand Up @@ -52,14 +55,20 @@ private static void dec(){

private int mSessionID;

private long mTimeout = 30 * 1000;
// Default timeout is 300s.
private long mTimeout = 300 * 1000;

private long mLastActiveTime;

public void updateActiveTime(){
mLastActiveTime = System.currentTimeMillis();
}

public void setTimeout(int t)
{
mTimeout = t*1000;
}

public boolean isTimeout() {
return System.currentTimeMillis() - mLastActiveTime > mTimeout;
}
Expand All @@ -84,7 +93,7 @@ public void record(int size, int direct) {
mR2LSize += size;
}

public void dump(Logger log, Exception e) {
public void dump(Exception e) {
log.error("Remote: " + mRemote + ", local: " + mLocal + ". Stream down size: " + mR2LSize + ", stream up size: " + mL2RSize + ".", e);
}
public int getID(){
Expand Down
31 changes: 19 additions & 12 deletions src/main/java/shadowsocks/nio/tcp/TcpWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public abstract class TcpWorker implements Runnable {
protected int mChunkCount = 0;


protected void mainLoop(Selector selector, SocketChannel local, SocketChannel remote) throws IOException,InterruptedException,CryptoException,AuthException
private void mainLoop(Selector selector, SocketChannel local, SocketChannel remote) throws IOException,InterruptedException,CryptoException,AuthException
{
local.configureBlocking(false);
remote.configureBlocking(false);
Expand Down Expand Up @@ -118,19 +118,18 @@ protected void mainLoop(Selector selector, SocketChannel local, SocketChannel re
}
}

protected void TcpRelay(SocketChannel local)
protected void TcpRelay()
{
int CONNECT_TIMEOUT = 5000;
SocketChannel remote = mSession.get(false);
SocketChannel local = mSession.get(true);

try(SocketChannel remote = SocketChannel.open();
Selector selector = Selector.open();)
try(Selector selector = Selector.open())
{
mSession.set(local, true);
mSession.set(remote, false);
//Init subclass special field.
handleStage(INIT);
handleStage(PARSE_HEADER);
log.info(mSession.getID() + ": connecting " + mConfig.target + " from " + local.socket().getLocalSocketAddress());
log.info(mSession.getID() + ": connecting " + mConfig.target + " from " + local.getRemoteAddress());
remote.socket().setTcpNoDelay(true);
remote.socket().connect(mConfig.remoteAddress, CONNECT_TIMEOUT);
handleStage(BEFORE_TCP_RELAY);
Expand All @@ -141,25 +140,33 @@ protected void TcpRelay(SocketChannel local)
}catch(InterruptedException e){
//ignore
}catch(IOException | CryptoException | AuthException e){
mSession.dump(log, e);
if (e.getMessage().equals("INVALID CONNECTION")) {
log.debug("Invalid connection");
return;
}
mSession.dump(e);
}

}


@Override
public void run(){
//make sure this channel could be closed
try(SocketChannel local = mLocal){
//make sure the 2 channels could be closed
try(SocketChannel local = mLocal; SocketChannel remote = SocketChannel.open())
{
mSession = new Session();
mSession.set(local, true);
mSession.set(remote, false);
mSession.setTimeout(mConfig.timeout);
// for decrypt/encrypt
mCryptor = CryptoFactory.create(mConfig.method, mConfig.password);
// for one time auth
mAuthor = new HmacSHA1();
mStreamUpData = new ByteArrayOutputStream();
TcpRelay(local);
TcpRelay();
}catch(Exception e){
mSession.dump(log, e);
log.error(e);
}finally{
mSession.destory();
}
Expand Down
34 changes: 31 additions & 3 deletions src/main/java/shadowsocks/util/GlobalConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,24 @@ public class GlobalConfig{
private int mLocalPort;
private boolean mOneTimeAuth;
private boolean mIsServerMode;
/* UNIT second */
private int mTimeout;

final private static String DEFAULT_METHOD = "aes-256-cfb";
final private static String DEFAULT_PASSWORD = "123456";
final private static String DEFAULT_SERVER = "127.0.0.1";
final private static int DEFAULT_PORT = 8388;
final private static int DEFAULT_LOCAL_PORT = 9999;
final private static int DEFAULT_TIMEOUT = 300;

public void setTimeout(int t)
{
mTimeout = t;
}
public int getTimeout()
{
return mTimeout;
}

public void setPassowrd(String p)
{
Expand Down Expand Up @@ -115,7 +127,6 @@ public String getConfigFile(){
return mConfigFile;
}


public synchronized static GlobalConfig get()
{
if (mConfig == null)
Expand All @@ -135,6 +146,7 @@ public GlobalConfig()
mOneTimeAuth = false;
mIsServerMode = false;
mConfigFile = null;
mTimeout = DEFAULT_TIMEOUT;
}

public void printConfig(){
Expand All @@ -150,6 +162,7 @@ public void printConfig(){
log.info("Server port [" + getPort() + "]");
log.info("Local port [" + getLocalPort() + "]");
}
log.info("Timeout [" + getTimeout() + "]");
}

public static String readConfigFile(String name){
Expand Down Expand Up @@ -215,11 +228,18 @@ public static void getConfigFromFile(){
}catch(JSONException e){
//No this config, ignore;
}
try{
int timeout = jsonobj.getInt("timeout");
log.debug("CFG:timeout: " + timeout);
GlobalConfig.get().setTimeout(timeout);
}catch(JSONException e){
//No this config, ignore;
}
}
public static void getConfigFromArgv(String argv[])
{

Getopt g = new Getopt("shadowsocks", argv, "SLm:k:p:as:l:c:");
Getopt g = new Getopt("shadowsocks", argv, "SLm:k:p:as:l:c:t:");
int c;
String arg;
while ((c = g.getopt()) != -1)
Expand Down Expand Up @@ -270,6 +290,12 @@ public static void getConfigFromArgv(String argv[])
log.debug("CMD:Config file: " + arg);
GlobalConfig.get().setConfigFile(arg);
break;
case 't':
arg = g.getOptarg();
int timeout = Integer.parseInt(arg);
log.debug("CMD:timeout: " + timeout);
GlobalConfig.get().setTimeout(timeout);
break;
case '?':
default:
help();
Expand All @@ -284,7 +310,9 @@ public static LocalConfig createLocalConfig() {
GlobalConfig.get().getServer(),
GlobalConfig.get().getPort(),
GlobalConfig.get().getLocalPort(),
GlobalConfig.get().isOTAEnabled());
GlobalConfig.get().isOTAEnabled(),
GlobalConfig.get().getTimeout()
);
}

private static void help()
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/shadowsocks/util/LocalConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,22 @@ public class LocalConfig{
public String password;
public String method;
public String server;
public int port;
public int serverPort;
public int localPort;
public boolean oneTimeAuth;
public int timeout;
//For server is target, for local is server.
public InetSocketAddress remoteAddress;

public String target;

public LocalConfig(String k, String m, String s, int p, int lp, boolean ota){
public LocalConfig(String k, String m, String s, int p, int lp, boolean ota, int t){
password = k;
method = m;
server = s;
port = p;
serverPort = p;
localPort = lp;
oneTimeAuth = ota;
timeout = t;
}
}
10 changes: 5 additions & 5 deletions src/test/java/shadowsocks/SystemTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ private void testSimpleHttp(boolean ota) {
URL url = new URL("http://example.com");
conn = (HttpURLConnection)url.openConnection(proxy);
conn.setRequestMethod("GET");
DataInputStream in1 = new DataInputStream(conn.getInputStream());
byte [] result = new byte[8192];
in1.read(result);
DataInputStream in2 = new DataInputStream(this.getClass().getClassLoader().getResourceAsStream("result-example-com"));
DataInputStream in1 = new DataInputStream(this.getClass().getClassLoader().getResourceAsStream("result-example-com"));
byte [] expect = new byte[8192];
in2.read(expect);
int dataLen = in1.read(expect);
DataInputStream in2 = new DataInputStream(conn.getInputStream());
byte [] result = new byte[8192];
in2.readFully(result, 0, dataLen);
boolean compareResult = Arrays.equals(result, expect);
if (!compareResult) {
log.debug("====================");
Expand Down

0 comments on commit 862dee0

Please sign in to comment.