作者 钟来

初始提交

正在显示 41 个修改的文件 包含 2465 行增加0 行删除
  1 +# shadowsocks-java
  2 +
  3 +Shadowsocks-java 是一个基于SOCKS5代理的使用java开发的[shadowsocks](https://github.com/shadowsocks/shadowsocks)
  4 +代理软件。可以同时作为客户端和服务端使用目前只支持TCP协议及流加密,后续会增加UDP协议和AEAD的支持。
  5 +
  6 +
  7 +
  8 +
  9 +## Build & Install
  10 +
  11 +```bash
  12 +git clone https://github.com/zhihengjiang/shasowsocks-java
  13 +cd shadowsocks-java
  14 +mvn package
  15 +```
  16 +
  17 +## Getting Started
  18 +
  19 +创建配置文件
  20 +
  21 +```json
  22 +{
  23 + "server": "my_server_ip",
  24 + "server_port": 8388,
  25 + "local_address": "127.0.0.1",
  26 + "local_port": 1080,
  27 + "password": "mypassword",
  28 + "timeout": 300,
  29 + "method": "aes-256-cfb"
  30 +}
  31 +```
  32 +
  33 +详细的参数解释可以参考 [shadowsocks](https://github.com/shadowsocks/shadowsocks/wiki) 的文档.
  34 +
  35 +在本地使用
  36 +```bash
  37 +chmod +x ./sslocal.sh
  38 +./sslocal.sh -c config.json
  39 +
  40 +```
  41 +
  42 +在服务器端使用
  43 +```bash
  44 +chmod +x ./ssserver.sh
  45 +./ssserver.sh -c config.json
  46 +
  47 +```
  48 +
  49 +更多脚本参数请输入
  50 +```bash
  51 +./ssserver.sh -h
  52 +./sslocal.sh -h
  53 +
  54 +```
  55 +
  56 +## 支持的加密方式
  57 +
  58 +### 流加密
  59 +
  60 +* `aes-128-cfb`, `aes-192-cfb`, `aes-256-cfb`
  61 +* `aes-128-ofb`, `aes-192-ofb`, `aes-256-ofb`
  62 +* `chacha20`, `chacha20-ietf`
  63 +
  64 +## TODO
  65 +
  66 +- [ ] Documentation
  67 +- [ ] 支持UDP协议
  68 +- [ ] 支持AEAD加密
  69 +- [ ] 编写使用脚本
  1 +{
  2 + "server":"127.0.0.1",
  3 + "server_port":8000,
  4 + "local_address": "127.0.0.1",
  5 + "local_port":1080,
  6 + "password":"123456",
  7 + "timeout":300,
  8 + "method":"aes-256-cfb",
  9 + "fast_open": false,
  10 + "workers": 1
  11 +}
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <modelVersion>4.0.0</modelVersion>
  6 + <groupId>org.shadowsocks</groupId>
  7 + <artifactId>shadowsocks-java</artifactId>
  8 + <version>1.0-SNAPSHOT</version>
  9 +
  10 + <dependencies>
  11 + <!-- 支持data -->
  12 + <dependency>
  13 + <groupId>org.projectlombok</groupId>
  14 + <artifactId>lombok</artifactId>
  15 + <version>1.16.16</version>
  16 + </dependency>
  17 + <dependency>
  18 + <groupId>io.netty</groupId>
  19 + <artifactId>netty-all</artifactId>
  20 + <version>4.1.7.Final</version>
  21 + </dependency>
  22 +
  23 + <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
  24 + <dependency>
  25 + <groupId>org.bouncycastle</groupId>
  26 + <artifactId>bcprov-jdk15on</artifactId>
  27 + <version>1.56</version>
  28 + </dependency>
  29 +
  30 + <dependency>
  31 + <groupId>gnu.getopt</groupId>
  32 + <artifactId>java-getopt</artifactId>
  33 + <version>1.0.13</version>
  34 + </dependency>
  35 +
  36 + <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
  37 + <dependency>
  38 + <groupId>ch.qos.logback</groupId>
  39 + <artifactId>logback-classic</artifactId>
  40 + <version>1.1.9</version>
  41 + </dependency>
  42 +
  43 + <dependency>
  44 + <groupId>junit</groupId>
  45 + <artifactId>junit</artifactId>
  46 + <version>4.12</version>
  47 + <scope>test</scope>
  48 + </dependency>
  49 + <dependency>
  50 + <groupId>com.squareup.okhttp3</groupId>
  51 + <artifactId>okhttp</artifactId>
  52 + <version>3.11.0</version>
  53 + <scope>test</scope>
  54 + </dependency>
  55 + <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
  56 + <dependency>
  57 + <groupId>org.apache.httpcomponents</groupId>
  58 + <artifactId>httpclient</artifactId>
  59 + <scope>test</scope>
  60 + <version>4.5.6</version>
  61 + </dependency>
  62 + <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
  63 + <dependency>
  64 + <groupId>com.google.code.gson</groupId>
  65 + <artifactId>gson</artifactId>
  66 + <version>2.8.5</version>
  67 + </dependency>
  68 +
  69 +
  70 + </dependencies>
  71 +
  72 + <build>
  73 + <plugins>
  74 + <plugin>
  75 + <groupId>org.apache.maven.plugins</groupId>
  76 + <artifactId>maven-surefire-plugin</artifactId>
  77 + <configuration>
  78 + <skip>true</skip>
  79 + </configuration>
  80 + </plugin>
  81 + <plugin>
  82 + <groupId>org.apache.maven.plugins</groupId>
  83 + <artifactId>maven-compiler-plugin</artifactId>
  84 + <version>2.3.2</version>
  85 + <configuration>
  86 + <skip>true</skip>
  87 + <source>1.8</source>
  88 + <target>1.8</target>
  89 + </configuration>
  90 + </plugin>
  91 + <plugin>
  92 + <groupId>org.apache.maven.plugins</groupId>
  93 + <artifactId>maven-jar-plugin</artifactId>
  94 + <configuration>
  95 + <archive>
  96 + <manifest>
  97 + <addClasspath>true</addClasspath>
  98 + <classpathPrefix>lib/</classpathPrefix><!--指定classpath的前缀-->
  99 + <mainClass>org.shadowsocks.Main</mainClass><!--指定主类的类名-->
  100 + </manifest>
  101 + </archive>
  102 + </configuration>
  103 + </plugin>
  104 + <plugin>
  105 + <groupId>org.apache.maven.plugins</groupId>
  106 + <artifactId>maven-dependency-plugin</artifactId>
  107 + <executions>
  108 + <execution>
  109 + <id>copy-dependencies</id>
  110 + <phase>prepare-package</phase>
  111 + <goals>
  112 + <goal>copy-dependencies</goal>
  113 + </goals>
  114 + <configuration>
  115 + <!--指定outputDirectory-->
  116 + <outputDirectory>${project.build.directory}/lib</outputDirectory>
  117 + <overWriteReleases>false</overWriteReleases>
  118 + <overWriteSnapshots>false</overWriteSnapshots>
  119 + <overWriteIfNewer>true</overWriteIfNewer>
  120 + </configuration>
  121 + </execution>
  122 + </executions>
  123 + </plugin>
  124 + </plugins>
  125 + </build>
  126 +</project>
  1 +package org.shadowsocks;
  2 +import org.shadowsocks.config.Config;
  3 +import org.shadowsocks.config.JsonConfig;
  4 +import org.shadowsocks.util.CommandLineParser;
  5 +
  6 +import java.util.Arrays;
  7 +
  8 +public class Main {
  9 + public static void main( String[] args) throws Exception{
  10 + if(null == args || args.length == 0)
  11 + {
  12 + args = new String[]{"ServerMain"};
  13 + }
  14 + String path = System.getProperty("user.dir") + "/config.json";
  15 + Config config = new JsonConfig(path);
  16 +
  17 + String main = args[0];
  18 +// Config config = CommandLineParser.parse(Arrays.copyOfRange(args,1,args.length));
  19 + switch (main){
  20 + case "LocalMain":
  21 + new ShadowsocksLocal(config).start();break;
  22 + case "ServerMain":
  23 + new ShadowSocksServer(config).start();break;
  24 +
  25 + default:System.out.println("please set running mode");break;
  26 + }
  27 +
  28 +
  29 + }
  30 +}
  1 +package org.shadowsocks;
  2 +
  3 +import io.netty.bootstrap.ServerBootstrap;
  4 +import io.netty.channel.ChannelFuture;
  5 +import io.netty.channel.ChannelInitializer;
  6 +import io.netty.channel.ChannelOption;
  7 +import io.netty.channel.nio.NioEventLoopGroup;
  8 +import io.netty.channel.socket.SocketChannel;
  9 +import io.netty.channel.socket.nio.NioServerSocketChannel;
  10 +import io.netty.handler.codec.bytes.ByteArrayDecoder;
  11 +import io.netty.handler.codec.http.HttpRequestDecoder;
  12 +import io.netty.handler.codec.http.HttpServerCodec;
  13 +import org.shadowsocks.config.Config;
  14 +import org.shadowsocks.crypto.CryptoFactory;
  15 +import org.shadowsocks.handler.server.AddressHandler;
  16 +import org.shadowsocks.handler.server.StringHandler;
  17 +import org.slf4j.Logger;
  18 +import org.slf4j.LoggerFactory;
  19 +
  20 +
  21 +public class ShadowSocksServer {
  22 +
  23 + private static Logger logger = LoggerFactory.getLogger(ShadowSocksServer.class);
  24 + Config config;
  25 + public ShadowSocksServer(Config config){
  26 + this.config = config;
  27 + }
  28 +
  29 + public void start() throws InterruptedException {
  30 + NioEventLoopGroup group = new NioEventLoopGroup(2);
  31 + ServerBootstrap bootstrap = new ServerBootstrap();
  32 + try {
  33 + bootstrap.group(group)
  34 + .channel(NioServerSocketChannel.class)
  35 + .localAddress(config.getServerPort())
  36 + .option(ChannelOption.SO_TIMEOUT, config.getTimeout())
  37 + .childHandler(new ChannelInitializer<SocketChannel>() {
  38 + @Override
  39 + protected void initChannel(SocketChannel socketChannel) throws Exception {
  40 + socketChannel.pipeline().addLast(new StringHandler(CryptoFactory.create(config.getMethod(),
  41 + config.getPassword())));
  42 + }
  43 + });
  44 + ChannelFuture channelFuture = bootstrap.bind().sync();
  45 + logger.info("started and listen on " + channelFuture.channel().localAddress());
  46 + channelFuture.channel().closeFuture().sync();
  47 +
  48 + } finally {
  49 + group.shutdownGracefully();
  50 + }
  51 +
  52 + }
  53 +}
  1 +package org.shadowsocks;
  2 +
  3 +
  4 +import io.netty.bootstrap.ServerBootstrap;
  5 +import io.netty.channel.ChannelFuture;
  6 +import io.netty.channel.ChannelInitializer;
  7 +import io.netty.channel.EventLoopGroup;
  8 +import io.netty.channel.nio.NioEventLoopGroup;
  9 +import io.netty.channel.socket.SocketChannel;
  10 +import io.netty.channel.socket.nio.NioServerSocketChannel;
  11 +import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder;
  12 +import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder;
  13 +
  14 +import io.netty.handler.codec.socksx.v5.Socks5ServerEncoder;
  15 +import org.shadowsocks.config.Config;
  16 +import org.shadowsocks.handler.local.*;
  17 +import org.slf4j.Logger;
  18 +import org.slf4j.LoggerFactory;
  19 +
  20 +
  21 +public class ShadowsocksLocal {
  22 +
  23 + private static Logger logger = LoggerFactory.getLogger(ShadowSocksServer.class);
  24 + private Config config;
  25 + public ShadowsocksLocal(Config config){
  26 + this.config = config;
  27 + }
  28 + public void start() throws InterruptedException{
  29 + EventLoopGroup worker = new NioEventLoopGroup(1);
  30 + EventLoopGroup boss = new NioEventLoopGroup();
  31 + ServerBootstrap bootstrap = new ServerBootstrap();
  32 + try{
  33 + bootstrap.group(worker,boss)
  34 + .localAddress(config.getLocalPort())
  35 + .channel(NioServerSocketChannel.class)
  36 + .childHandler(new ChannelInitializer<SocketChannel>() {
  37 + @Override
  38 + protected void initChannel(SocketChannel ch) {
  39 + ch.pipeline().addLast(Socks5ServerEncoder.DEFAULT);
  40 + ch.pipeline().addLast(new Socks5InitialRequestDecoder());
  41 + ch.pipeline().addLast(new Socks5InitialRequestHandler());
  42 + ch.pipeline().addLast(new Socks5CommandRequestDecoder());
  43 + ch.pipeline().addLast(new Socks5CmdRequesthandler(config));
  44 + }
  45 + });
  46 + ChannelFuture futrue = bootstrap.bind().sync();
  47 + logger.info("connected local port:" + config.getLocalPort());
  48 + futrue.channel().closeFuture().sync();
  49 + }
  50 + finally {
  51 + worker.shutdownGracefully();
  52 + boss.shutdownGracefully();
  53 + }
  54 + }
  55 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.auth;
  17 +
  18 +public class AuthException extends Exception
  19 +{
  20 + private static final long serialVersionUID = 1L;
  21 +
  22 + public AuthException(String message){
  23 + super(message);
  24 + }
  25 + public AuthException(Exception e){
  26 + super(e);
  27 + }
  28 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.auth;
  17 +
  18 +import javax.crypto.Mac;
  19 +import javax.crypto.spec.SecretKeySpec;
  20 +import java.security.InvalidKeyException;
  21 +import java.security.NoSuchAlgorithmException;
  22 +import java.util.Arrays;
  23 +
  24 +
  25 +public class HmacSHA1 extends SSAuth{
  26 +
  27 + private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
  28 +
  29 + public static final int AUTH_LEN = 10;
  30 +
  31 + @Override
  32 +
  33 + public byte[] doAuth(byte[] key, byte [] data) throws AuthException
  34 + {
  35 + try{
  36 + SecretKeySpec signingKey = new SecretKeySpec(key, HMAC_SHA1_ALGORITHM);
  37 + Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
  38 + mac.init(signingKey);
  39 + byte [] original_result;
  40 + byte [] result = new byte[AUTH_LEN];
  41 + original_result = mac.doFinal(data);
  42 + System.arraycopy(original_result, 0, result, 0, AUTH_LEN);
  43 + return result;
  44 + }catch(NoSuchAlgorithmException | InvalidKeyException e){
  45 + throw new AuthException(e);
  46 + }
  47 + }
  48 +
  49 + @Override
  50 + public boolean doAuth(byte[] key, byte [] data, byte [] expect) throws AuthException
  51 + {
  52 + return Arrays.equals(expect, doAuth(key, data));
  53 + }
  54 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.auth;
  17 +
  18 +import java.nio.ByteBuffer;
  19 +
  20 +/**
  21 + * Auth base class
  22 + */
  23 +public abstract class SSAuth{
  24 +
  25 + public abstract byte [] doAuth(byte[] key, byte[] data) throws AuthException;
  26 + public abstract boolean doAuth(byte[] key, byte[] data, byte[] auth) throws AuthException;
  27 +
  28 + public static byte [] prepareKey(byte [] i, int c){
  29 + byte [] key = new byte[i.length + 4];
  30 + ByteBuffer b = ByteBuffer.allocate(4);
  31 + b.putInt(c);
  32 + System.arraycopy(i, 0, key, 0, i.length);
  33 + System.arraycopy(b.array(), 0, key, i.length, 4);
  34 + return key;
  35 + }
  36 +
  37 + public static byte [] prepareKey(byte [] i, byte [] k){
  38 + byte [] key = new byte[i.length + k.length];
  39 + System.arraycopy(i, 0, key, 0, i.length);
  40 + System.arraycopy(k, 0, key, i.length, k.length);
  41 + return key;
  42 + }
  43 +}
  1 +package org.shadowsocks.config;
  2 +
  3 +public abstract class BaseConfig implements Config{
  4 +
  5 + private RealConfig config;
  6 + public BaseConfig(Object source){
  7 + this.config = loadConfig(source);
  8 + }
  9 +
  10 +
  11 + @Override
  12 + public String getServerAddress() {
  13 + return config.server;
  14 + }
  15 +
  16 + @Override
  17 + public int getServerPort() {
  18 + return config.server_port;
  19 + }
  20 +
  21 + @Override
  22 + public String getLocalAddress() {
  23 + return config.local_address;
  24 + }
  25 +
  26 + @Override
  27 + public int getLocalPort() {
  28 + return config.local_port;
  29 + }
  30 +
  31 + @Override
  32 + public int getTimeout() {
  33 + return config.timeout;
  34 + }
  35 +
  36 + @Override
  37 + public String getMethod() {
  38 + return config.method;
  39 + }
  40 +
  41 + @Override
  42 + public String getPassword() {
  43 + return config.password;
  44 + }
  45 +
  46 + public RealConfig getConfig(){
  47 + return this.config;
  48 + }
  49 +
  50 +}
  1 +package org.shadowsocks.config;
  2 +
  3 +public interface Config {
  4 + String getServerAddress();
  5 + int getServerPort();
  6 + int getLocalPort();
  7 + String getLocalAddress();
  8 + String getPassword();
  9 + int getTimeout();
  10 + String getMethod();
  11 + RealConfig loadConfig(Object source);
  12 +
  13 +}
  1 +package org.shadowsocks.config;
  2 +
  3 +public class ConfigFactory {
  4 + public static Config getConfig(String path){
  5 + return new JsonConfig(path);
  6 +
  7 + }
  8 +}
  1 +package org.shadowsocks.config;
  2 +
  3 +import com.google.gson.Gson;
  4 +import com.google.gson.stream.JsonReader;
  5 +import java.io.*;
  6 +
  7 +
  8 +
  9 +public class JsonConfig extends BaseConfig {
  10 +
  11 + public JsonConfig(String path){
  12 + super(path);
  13 + }
  14 +
  15 + @Override
  16 + public RealConfig loadConfig(Object path) {
  17 + Gson gson = new Gson();
  18 + RealConfig config = new RealConfig();
  19 + try{
  20 +
  21 + JsonReader reader = new JsonReader(new FileReader((String)path));
  22 + config = gson.fromJson(reader,RealConfig.class);
  23 + }
  24 + catch (IOException e){
  25 + e.printStackTrace();
  26 + }
  27 + return config;
  28 + }
  29 +
  30 +
  31 +
  32 + public static void main(String[] args){
  33 + String path = "/home/thales/config.json";
  34 + Config config = new JsonConfig(path);
  35 + System.out.println(new Gson().toJsonTree(((JsonConfig) config).getConfig()));
  36 + }
  37 +}
  1 +package org.shadowsocks.config;
  2 +
  3 +public class PropertiesConfig extends BaseConfig{
  4 + public PropertiesConfig(String path){
  5 + super(path);
  6 + }
  7 + @Override
  8 + public RealConfig loadConfig(Object path) {
  9 + return new RealConfig();
  10 + }
  11 +}
  1 +package org.shadowsocks.config;
  2 +
  3 +public class RealConfig {
  4 + public String server;
  5 + public int server_port;
  6 + public String local_address;
  7 + public int local_port;
  8 + public int timeout;
  9 + public String method;
  10 + public String password;
  11 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.crypto;
  17 +
  18 +import org.bouncycastle.crypto.StreamBlockCipher;
  19 +import org.bouncycastle.crypto.StreamCipher;
  20 +import org.bouncycastle.crypto.engines.AESEngine;
  21 +import org.bouncycastle.crypto.modes.CFBBlockCipher;
  22 +import org.bouncycastle.crypto.modes.OFBBlockCipher;
  23 +import org.bouncycastle.crypto.params.KeyParameter;
  24 +import org.bouncycastle.crypto.params.ParametersWithIV;
  25 +
  26 +import java.io.ByteArrayOutputStream;
  27 +
  28 +/**
  29 + * AES Crypt implementation
  30 + */
  31 +public class AESCrypto extends BaseCrypto {
  32 +
  33 + public final static String CIPHER_AES_128_CFB = "aes-128-cfb";
  34 + public final static String CIPHER_AES_192_CFB = "aes-192-cfb";
  35 + public final static String CIPHER_AES_256_CFB = "aes-256-cfb";
  36 + public final static String CIPHER_AES_128_OFB = "aes-128-ofb";
  37 + public final static String CIPHER_AES_192_OFB = "aes-192-ofb";
  38 + public final static String CIPHER_AES_256_OFB = "aes-256-ofb";
  39 +
  40 + private final static int IV_LENGTH = 16;
  41 +
  42 + public AESCrypto(String name, String password) throws CryptoException {
  43 + super(name, password);
  44 + }
  45 +
  46 + @Override
  47 + public int getIVLength() {
  48 + return IV_LENGTH;
  49 + }
  50 +
  51 + @Override
  52 + public int getKeyLength() {
  53 + if(mName.equals(CIPHER_AES_128_CFB) || mName.equals(CIPHER_AES_128_OFB)) {
  54 + return 16;
  55 + }
  56 + else if (mName.equals(CIPHER_AES_192_CFB) || mName.equals(CIPHER_AES_192_OFB)) {
  57 + return 24;
  58 + }
  59 + else if (mName.equals(CIPHER_AES_256_CFB) || mName.equals(CIPHER_AES_256_OFB)) {
  60 + return 32;
  61 + }
  62 + return 0;
  63 + }
  64 +
  65 + protected StreamBlockCipher getCipher(boolean isEncrypted) throws CryptoException
  66 + {
  67 + AESEngine engine = new AESEngine();
  68 + StreamBlockCipher cipher;
  69 +
  70 + if (mName.equals(CIPHER_AES_128_CFB)) {
  71 + cipher = new CFBBlockCipher(engine, getIVLength() * 8);
  72 + }
  73 + else if (mName.equals(CIPHER_AES_192_CFB)) {
  74 + cipher = new CFBBlockCipher(engine, getIVLength() * 8);
  75 + }
  76 + else if (mName.equals(CIPHER_AES_256_CFB)) {
  77 + cipher = new CFBBlockCipher(engine, getIVLength() * 8);
  78 + }
  79 + else if (mName.equals(CIPHER_AES_128_OFB)) {
  80 + cipher = new OFBBlockCipher(engine, getIVLength() * 8);
  81 + }
  82 + else if (mName.equals(CIPHER_AES_192_OFB)) {
  83 + cipher = new OFBBlockCipher(engine, getIVLength() * 8);
  84 + }
  85 + else if (mName.equals(CIPHER_AES_256_OFB)) {
  86 + cipher = new OFBBlockCipher(engine, getIVLength() * 8);
  87 + }
  88 + else {
  89 + throw new CryptoException("Invalid AlgorithmParameter: " + mName);
  90 + }
  91 +
  92 +
  93 + return cipher;
  94 + }
  95 +
  96 + @Override
  97 + protected StreamCipher createCipher(byte[] iv, boolean encrypt) throws CryptoException
  98 + {
  99 + StreamBlockCipher c = getCipher(encrypt);
  100 + ParametersWithIV parameterIV = new ParametersWithIV(new KeyParameter(mKey), iv, 0, mIVLength);
  101 + c.init(encrypt, parameterIV);
  102 + return c;
  103 + }
  104 +
  105 + @Override
  106 + protected void process(byte[] in, ByteArrayOutputStream out, boolean encrypt){
  107 + int size;
  108 + byte[] buffer = new byte[in.length];
  109 + StreamBlockCipher cipher;
  110 + if (encrypt){
  111 + cipher = (StreamBlockCipher)mEncryptCipher;
  112 + }else{
  113 + cipher = (StreamBlockCipher)mDecryptCipher;
  114 + }
  115 + size = cipher.processBytes(in, 0, in.length, buffer, 0);
  116 + out.write(buffer, 0, size);
  117 + }
  118 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.crypto;
  17 +
  18 +import org.bouncycastle.crypto.StreamCipher;
  19 +
  20 +import java.io.ByteArrayOutputStream;
  21 +import java.io.IOException;
  22 +
  23 +/**
  24 + * Crypt base class implementation
  25 + */
  26 +public abstract class BaseCrypto implements SSCrypto
  27 +{
  28 +
  29 + protected abstract StreamCipher createCipher(byte[] iv, boolean encrypt) throws CryptoException;
  30 + protected abstract void process(byte[] in, ByteArrayOutputStream out, boolean encrypt);
  31 +
  32 + protected final String mName;
  33 + protected final byte[] mKey;
  34 + protected final int mIVLength;
  35 + protected final int mKeyLength;
  36 +
  37 + protected StreamCipher mEncryptCipher = null;
  38 + protected StreamCipher mDecryptCipher = null;
  39 +
  40 + protected byte[] mEncryptIV;
  41 + protected byte[] mDecryptIV;
  42 +
  43 + // One SSCrypto could only do one decrypt/encrypt at the same time.
  44 + protected ByteArrayOutputStream mData;
  45 +
  46 + private final byte [] mLock = new byte[0];
  47 +
  48 + public BaseCrypto(String name, String password) throws CryptoException
  49 + {
  50 + mName = name.toLowerCase();
  51 + mIVLength = getIVLength();
  52 + mKeyLength = getKeyLength();
  53 + if (mKeyLength == 0) {
  54 + throw new CryptoException("Unsupport method: " + mName);
  55 + }
  56 + mKey = Utils.getKey(password, mKeyLength, mIVLength);
  57 + mData = new ByteArrayOutputStream();
  58 + }
  59 +
  60 + public byte [] getKey(){
  61 + return mKey;
  62 + }
  63 +
  64 + public byte [] getIV(boolean encrypt){
  65 + if (encrypt){
  66 + if (mEncryptIV == null){
  67 + mEncryptIV = Utils.randomBytes(mIVLength);
  68 + }
  69 + return mEncryptIV;
  70 + }else
  71 + return mDecryptIV;
  72 + }
  73 +
  74 + private byte [] encryptLocked(byte[] in) throws CryptoException
  75 + {
  76 + mData.reset();
  77 + if (mEncryptCipher == null) {
  78 + mEncryptIV = getIV(true);
  79 + mEncryptCipher = createCipher(mEncryptIV, true);
  80 + try {
  81 + mData.write(mEncryptIV);
  82 + } catch (IOException e) {
  83 + throw new CryptoException(e);
  84 + }
  85 + }
  86 + process(in, mData, true);
  87 + return mData.toByteArray();
  88 + }
  89 +
  90 + @Override
  91 + public byte [] encrypt(byte[] in, int length) throws CryptoException
  92 + {
  93 + synchronized(mLock) {
  94 + if (length != in.length){
  95 + byte[] data = new byte[length];
  96 + System.arraycopy(in, 0, data, 0, length);
  97 + return encryptLocked(data);
  98 + }else{
  99 + return encryptLocked(in);
  100 + }
  101 + }
  102 + }
  103 +
  104 + private byte[] decryptLocked(byte[] in) throws CryptoException
  105 + {
  106 + byte[] data;
  107 + mData.reset();
  108 + if (mDecryptCipher == null) {
  109 + mDecryptCipher = createCipher(in, false);
  110 + mDecryptIV = new byte[mIVLength];
  111 + data = new byte[in.length - mIVLength];
  112 + System.arraycopy(in, 0, mDecryptIV, 0, mIVLength);
  113 + System.arraycopy(in, mIVLength, data, 0, in.length - mIVLength);
  114 + } else {
  115 + data = in;
  116 + }
  117 + process(data, mData, false);
  118 + return mData.toByteArray();
  119 + }
  120 +
  121 + @Override
  122 + public byte [] decrypt(byte[] in, int length) throws CryptoException
  123 + {
  124 + synchronized(mLock) {
  125 + if (length != in.length) {
  126 + byte[] data = new byte[length];
  127 + System.arraycopy(in, 0, data, 0, length);
  128 + return decryptLocked(data);
  129 + }else{
  130 + return decryptLocked(in);
  131 + }
  132 + }
  133 + }
  134 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.crypto;
  17 +
  18 +import org.bouncycastle.crypto.StreamCipher;
  19 +import org.bouncycastle.crypto.engines.ChaCha7539Engine;
  20 +import org.bouncycastle.crypto.engines.ChaChaEngine;
  21 +import org.bouncycastle.crypto.params.KeyParameter;
  22 +import org.bouncycastle.crypto.params.ParametersWithIV;
  23 +
  24 +import java.io.ByteArrayOutputStream;
  25 +
  26 +/**
  27 + * Chacha20 Crypt implementation
  28 + */
  29 +public class Chacha20Crypto extends BaseCrypto {
  30 +
  31 + private final static String CIPHER_CHACHA20 = "chacha20";
  32 + private final static String CIPHER_CHACHA20_IETF = "chacha20-ietf";
  33 +
  34 + private final static int IV_LENGTH = 8;
  35 + private final static int IV_IETF_LENGTH = 12;
  36 +
  37 + private final static int KEY_LENGTH = 32;
  38 +
  39 + public Chacha20Crypto(String name, String password) throws CryptoException {
  40 + super(name, password);
  41 + }
  42 +
  43 + @Override
  44 + public int getIVLength() {
  45 + if (mName.equals(CIPHER_CHACHA20_IETF)) {
  46 + return IV_IETF_LENGTH;
  47 + } else {
  48 + return IV_LENGTH;
  49 + }
  50 + }
  51 +
  52 + @Override
  53 + public int getKeyLength() {
  54 + if (mName.equals(CIPHER_CHACHA20) || mName.equals(CIPHER_CHACHA20_IETF)) {
  55 + return KEY_LENGTH;
  56 + }
  57 + return 0;
  58 + }
  59 +
  60 + @Override
  61 + protected StreamCipher createCipher(byte[] iv, boolean encrypt) throws CryptoException
  62 + {
  63 + StreamCipher c;
  64 + if (mName.equals(CIPHER_CHACHA20_IETF)) {
  65 + c = new ChaCha7539Engine();
  66 + } else {
  67 + c = new ChaChaEngine();
  68 + }
  69 + ParametersWithIV parameterIV = new ParametersWithIV(new KeyParameter(mKey), iv, 0, mIVLength);
  70 + c.init(encrypt, parameterIV);
  71 + return c;
  72 + }
  73 +
  74 + @Override
  75 + protected void process(byte[] in, ByteArrayOutputStream out, boolean encrypt){
  76 + int size;
  77 + byte[] buffer = new byte[in.length];
  78 + StreamCipher cipher;
  79 + if (encrypt){
  80 + cipher = mEncryptCipher;
  81 + }else{
  82 + cipher = mDecryptCipher;
  83 + }
  84 + size = cipher.processBytes(in, 0, in.length, buffer, 0);
  85 + out.write(buffer, 0, size);
  86 + }
  87 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.crypto;
  17 +
  18 +public class CryptoException extends Exception
  19 +{
  20 + private static final long serialVersionUID = 1L;
  21 +
  22 + public CryptoException(String message){
  23 + super(message);
  24 + }
  25 + public CryptoException(Exception e){
  26 + super(e);
  27 + }
  28 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.crypto;
  17 +
  18 +public class CryptoFactory{
  19 +
  20 + private static final String AES = "aes";
  21 + private static final String CHACHA20 = "chacha20";
  22 +
  23 + public static SSCrypto create(String name, String password) throws CryptoException
  24 + {
  25 + String cipherName = name.toLowerCase();
  26 + if (cipherName.startsWith(AES)) {
  27 + return new AESCrypto(name, password);
  28 + }else if (cipherName.startsWith(CHACHA20)) {
  29 + return new Chacha20Crypto(name, password);
  30 + }else{
  31 + throw new CryptoException("Unsupport method: " + name);
  32 + }
  33 + }
  34 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.crypto;
  17 +
  18 +/**
  19 + * Interface of crypt
  20 + */
  21 +public interface SSCrypto {
  22 + byte [] encrypt(byte[] data, int length) throws CryptoException;
  23 + byte [] decrypt(byte[] data, int length) throws CryptoException;
  24 + int getIVLength();
  25 + int getKeyLength();
  26 + byte [] getIV(boolean encrypt);
  27 + byte [] getKey();
  28 +}
  1 +/*
  2 + * Copyright 2016 Author:NU11 bestoapache@gmail.com
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.shadowsocks.crypto;
  17 +
  18 +import java.io.UnsupportedEncodingException;
  19 +import java.security.MessageDigest;
  20 +import java.security.NoSuchAlgorithmException;
  21 +import java.security.SecureRandom;
  22 +
  23 +public class Utils{
  24 +
  25 + /**
  26 + * Thanks go to Ola Bini for releasing this source on his blog.
  27 + * The source was obtained from <a href="http://olabini.com/blog/tag/evp_bytestokey/">here</a> .
  28 + */
  29 + private static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data, int count) {
  30 + byte[][] both = new byte[2][];
  31 + byte[] key = new byte[key_len];
  32 + int key_ix = 0;
  33 + byte[] iv = new byte[iv_len];
  34 + int iv_ix = 0;
  35 + both[0] = key;
  36 + both[1] = iv;
  37 + byte[] md_buf = null;
  38 + int nkey = key_len;
  39 + int niv = iv_len;
  40 + int i = 0;
  41 + if (data == null) {
  42 + return both;
  43 + }
  44 + int addmd = 0;
  45 + for (;;) {
  46 + md.reset();
  47 + if (addmd++ > 0) {
  48 + md.update(md_buf);
  49 + }
  50 + md.update(data);
  51 + if (null != salt) {
  52 + md.update(salt, 0, 8);
  53 + }
  54 + md_buf = md.digest();
  55 + for (i = 1; i < count; i++) {
  56 + md.reset();
  57 + md.update(md_buf);
  58 + md_buf = md.digest();
  59 + }
  60 + i = 0;
  61 + if (nkey > 0) {
  62 + for (;;) {
  63 + if (nkey == 0)
  64 + break;
  65 + if (i == md_buf.length)
  66 + break;
  67 + key[key_ix++] = md_buf[i];
  68 + nkey--;
  69 + i++;
  70 + }
  71 + }
  72 + if (niv > 0 && i != md_buf.length) {
  73 + for (;;) {
  74 + if (niv == 0)
  75 + break;
  76 + if (i == md_buf.length)
  77 + break;
  78 + iv[iv_ix++] = md_buf[i];
  79 + niv--;
  80 + i++;
  81 + }
  82 + }
  83 + if (nkey == 0 && niv == 0) {
  84 + break;
  85 + }
  86 + }
  87 + for (i = 0; i < md_buf.length; i++) {
  88 + md_buf[i] = 0;
  89 + }
  90 + return both;
  91 + }
  92 +
  93 +
  94 + public static byte[] getKey(String password, int keyLen, int ivLen) throws CryptoException
  95 + {
  96 + MessageDigest md = null;
  97 + byte[] passwordBytes = null;
  98 + byte[][] keyAndIV = null;
  99 + int i = 0;
  100 +
  101 + try{
  102 + md = MessageDigest.getInstance("MD5");
  103 + passwordBytes = password.getBytes("ASCII");
  104 + }catch(NoSuchAlgorithmException | UnsupportedEncodingException e){
  105 + throw new CryptoException(e);
  106 + }
  107 +
  108 + //This key should equal EVP_BytesToKey with no salt and count = 1
  109 + keyAndIV = EVP_BytesToKey(keyLen, ivLen, md, null, passwordBytes, 1);
  110 +
  111 + //Discard the iv.
  112 + return keyAndIV[0];
  113 + }
  114 +
  115 + public static byte[] randomBytes(int size) {
  116 + byte[] bytes = new byte[size];
  117 + new SecureRandom().nextBytes(bytes);
  118 + return bytes;
  119 + }
  120 +
  121 +}
  1 +package org.shadowsocks.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +@Data
  6 +public class RequestInfo {
  7 + private String host;
  8 + private int post;
  9 +
  10 + public RequestInfo(String host,int post)
  11 + {
  12 + this.host = host;
  13 + this.post = post;
  14 + }
  15 +}
  1 +package org.shadowsocks.dto;
  2 +
  3 +public enum RequestMethod {
  4 +/**
  5 + * 从服务器获取一份文档
  6 + *
  7 + * 请求实体(不支持)
  8 + *
  9 + * 响应实体(支持)
  10 + */
  11 + GET,
  12 +
  13 + /**
  14 + * 向服务器发送需要处理的数据
  15 + *
  16 + * 请求实体(支持)
  17 + *
  18 + * 响应实体(支持)
  19 + */
  20 + POST,
  21 +
  22 + /**
  23 + * 只从服务器获取文档的首部
  24 + *
  25 + * 请求实体(不支持)
  26 + *
  27 + * 响应实体(不支持)
  28 + */
  29 + HEAD,
  30 +
  31 + /**
  32 + * 将请求的主体部分存储在服务器上
  33 + *
  34 + * 请求实体(支持)
  35 + *
  36 + * 响应实体(支持)
  37 + */
  38 + PUT,
  39 +
  40 + /**
  41 + * 对可能经过代理服务器传送到服务器上去的报文进行追踪
  42 + *
  43 + * 请求实体(不支持)
  44 + *
  45 + * 响应实体(支持)
  46 + */
  47 + TRACE,
  48 +
  49 + /**
  50 + * 决定可以在服务器上执行哪些方法
  51 + *
  52 + * 请求实体(不支持)
  53 + *
  54 + * 响应实体(不支持)
  55 + */
  56 + OPTIONS,
  57 +
  58 + /**
  59 + * 从服务器上删除一份文档
  60 + *
  61 + * 请求实体(不支持)
  62 + *
  63 + * 响应实体(支持)
  64 + */
  65 + DELETE,
  66 +
  67 + /**
  68 + * 用于https
  69 + *
  70 + * 请求实体(不支持)
  71 + *
  72 + * 响应实体(支持)
  73 + */
  74 + CONNECT;
  75 +}
  1 +package org.shadowsocks.handler.local;
  2 +
  3 +
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.ByteBufUtil;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelFuture;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.channel.ChannelInboundHandlerAdapter;
  10 +import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandRequest;
  11 +import org.shadowsocks.crypto.SSCrypto;
  12 +import org.slf4j.Logger;
  13 +import org.slf4j.LoggerFactory;
  14 +
  15 +import java.net.IDN;
  16 +import java.net.Inet4Address;
  17 +import java.net.InetAddress;
  18 +import java.nio.charset.StandardCharsets;
  19 +import java.util.Arrays;
  20 +
  21 +public class Local2RemoteHandler extends ChannelInboundHandlerAdapter {
  22 + private static final Logger logger = LoggerFactory.getLogger(Local2RemoteHandler.class);
  23 +
  24 + private ChannelFuture destChannelFuture;
  25 + private SSCrypto ssCrypto;
  26 + private DefaultSocks5CommandRequest socks5CommandRequest;
  27 + private boolean isProxy = true;
  28 + private boolean addAddress = false;
  29 +
  30 + public Local2RemoteHandler(ChannelFuture destChannelFuture,SSCrypto ssCrypto, DefaultSocks5CommandRequest msg,
  31 + boolean isProxy) {
  32 + this.destChannelFuture = destChannelFuture;
  33 + this.ssCrypto = ssCrypto;
  34 + this.socks5CommandRequest = msg;
  35 + this.isProxy = isProxy;
  36 + }
  37 +
  38 + @Override
  39 + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  40 + if(isProxy){
  41 + ByteBuf buff = (ByteBuf)msg;
  42 + if(!addAddress){
  43 + ByteBuf addressInfo = parseAddress(socks5CommandRequest);
  44 + addressInfo.writeBytes(buff);
  45 + buff = addressInfo;
  46 + addAddress = true;
  47 + }
  48 + byte[] plainTxt = ByteBufUtil.getBytes(buff);
  49 + byte[] encrypt = ssCrypto.encrypt(plainTxt,plainTxt.length);
  50 + logger.info("relay message to remote server from client");
  51 + destChannelFuture.channel().writeAndFlush(Unpooled.copiedBuffer(encrypt));
  52 + }
  53 + else {
  54 + destChannelFuture.channel().writeAndFlush(msg);
  55 + }
  56 +
  57 + }
  58 +
  59 + @Override
  60 + public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  61 + logger.trace("close connection");
  62 + destChannelFuture.channel().close();
  63 + }
  64 +
  65 + private ByteBuf parseAddress (DefaultSocks5CommandRequest msg) throws Exception{
  66 + ByteBuf buff = Unpooled.buffer();
  67 + byte addressType = msg.dstAddrType().byteValue();
  68 + int port = msg.dstPort();
  69 + String host = msg.dstAddr();
  70 + if(addressType == 0x01){
  71 + buff.writeByte(0x01);
  72 + InetAddress address = Inet4Address.getByName(host);
  73 + byte[] addr = address.getAddress();
  74 + buff.writeBytes(addr);
  75 + }
  76 + if(addressType == 0x03){
  77 + buff.writeByte(0x03);
  78 + String address = IDN.toASCII(host);
  79 + byte[] addr = address.getBytes(StandardCharsets.US_ASCII);
  80 + System.out.println(address+","+host+","+addr.length+","+Arrays.toString(addr));
  81 + buff.writeByte(addr.length);
  82 + buff.writeBytes(addr);
  83 + }
  84 + else
  85 + throw new IllegalArgumentException("IP v6 not supported");
  86 + buff.writeShort(port);
  87 + return buff;
  88 + }
  89 +
  90 +}
  1 +package org.shadowsocks.handler.local;
  2 +
  3 +import io.netty.buffer.ByteBuf;
  4 +import io.netty.buffer.ByteBufUtil;
  5 +import io.netty.buffer.Unpooled;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import io.netty.channel.ChannelInboundHandlerAdapter;
  8 +import org.shadowsocks.crypto.SSCrypto;
  9 +import org.slf4j.Logger;
  10 +import org.slf4j.LoggerFactory;
  11 +
  12 +import java.nio.charset.StandardCharsets;
  13 +import java.util.Arrays;
  14 +
  15 +public class Remote2LocalHandler extends ChannelInboundHandlerAdapter {
  16 + private static final Logger logger = LoggerFactory.getLogger(Remote2LocalHandler.class);
  17 + private ChannelHandlerContext clientChannelContext;
  18 + private SSCrypto ssCrypto;
  19 + private boolean isProxy = true;
  20 +
  21 + public Remote2LocalHandler(ChannelHandlerContext clientChannelContext, SSCrypto ssCrypto, boolean isProxy) {
  22 + this.clientChannelContext = clientChannelContext;
  23 + this.ssCrypto = ssCrypto;
  24 + this.isProxy = isProxy;
  25 + }
  26 +
  27 + @Override
  28 + public void channelRead(ChannelHandlerContext ctx2, Object remoteMsg) throws Exception {
  29 + if(isProxy){
  30 + ByteBuf buff = (ByteBuf)remoteMsg;
  31 + byte[] encrypted = ByteBufUtil.getBytes(buff);
  32 + byte[] decrypted = ssCrypto.decrypt(encrypted,encrypted.length);
  33 + logger.info("relay response of target server to client");
  34 +// System.out.println("========="+Unpooled.copiedBuffer(decrypted).toString(StandardCharsets.US_ASCII));
  35 + clientChannelContext.writeAndFlush(Unpooled.copiedBuffer(decrypted));
  36 + }
  37 + else {
  38 + clientChannelContext.writeAndFlush(remoteMsg);
  39 + }
  40 + }
  41 +
  42 + @Override
  43 + public void channelInactive(ChannelHandlerContext ctx2) throws Exception {
  44 + logger.trace("close connection to target server");
  45 + clientChannelContext.channel().close();
  46 + }
  47 +}
  1 +package org.shadowsocks.handler.local;
  2 +
  3 +import io.netty.bootstrap.Bootstrap;
  4 +import io.netty.channel.*;
  5 +import io.netty.channel.nio.NioEventLoopGroup;
  6 +import io.netty.channel.socket.SocketChannel;
  7 +import io.netty.channel.socket.nio.NioSocketChannel;
  8 +import io.netty.handler.codec.socksx.v5.*;
  9 +import org.shadowsocks.config.Config;
  10 +import org.shadowsocks.crypto.CryptoFactory;
  11 +import org.shadowsocks.crypto.SSCrypto;
  12 +import org.slf4j.Logger;
  13 +import org.slf4j.LoggerFactory;
  14 +
  15 +/**
  16 + * handle socks5 request
  17 + */
  18 +public class Socks5CmdRequesthandler extends SimpleChannelInboundHandler<DefaultSocks5CommandRequest> {
  19 + private static final Logger logger = LoggerFactory.getLogger(Socks5CmdRequesthandler.class);
  20 + private Config config;
  21 + private EventLoopGroup bossGroup = new NioEventLoopGroup();
  22 + private SSCrypto ssCrypto ;
  23 + private boolean isProxy = true;
  24 +
  25 + public Socks5CmdRequesthandler(Config config){
  26 + this.config = config;
  27 + try{
  28 + ssCrypto = CryptoFactory.create(config.getMethod(),config.getPassword());
  29 + }
  30 + catch (Exception e){
  31 + e.printStackTrace();
  32 + }
  33 + }
  34 +
  35 +
  36 + @Override
  37 + protected void channelRead0(ChannelHandlerContext ctx, DefaultSocks5CommandRequest msg) {
  38 + if(msg.type().equals(Socks5CommandType.CONNECT)) {
  39 + logger.trace("connecting remote server");
  40 +
  41 + Bootstrap bootstrap = new Bootstrap();
  42 + bootstrap.group(bossGroup)
  43 + .channel(NioSocketChannel.class)
  44 + .option(ChannelOption.TCP_NODELAY, true)
  45 + .handler(new ChannelInitializer<SocketChannel>() {
  46 + @Override
  47 + protected void initChannel(SocketChannel ch) throws Exception {
  48 + ch.pipeline().addLast(new Remote2LocalHandler(ctx,ssCrypto,isProxy));
  49 + }
  50 + });
  51 + ChannelFuture future;
  52 + if(isProxy){
  53 + future = bootstrap.connect(config.getServerAddress(), config.getServerPort());
  54 + }
  55 + else {
  56 + future = bootstrap.connect(msg.dstAddr(),msg.dstPort());
  57 + }
  58 + future.addListener(new ChannelFutureListener() {
  59 +
  60 + public void operationComplete(final ChannelFuture future) throws Exception {
  61 + if(future.isSuccess()) {
  62 + logger.info("successfully connected remote server");
  63 + ctx.pipeline().addLast(new Local2RemoteHandler(future,ssCrypto,msg,isProxy));
  64 + Socks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4);
  65 + ctx.writeAndFlush(commandResponse);
  66 + } else {
  67 + Socks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, Socks5AddressType.IPv4);
  68 + ctx.writeAndFlush(commandResponse);
  69 + }
  70 + }
  71 +
  72 + });
  73 + } else {
  74 + ctx.fireChannelRead(msg);
  75 + }
  76 + }
  77 +
  78 +
  79 +}
  1 +package org.shadowsocks.handler.local;
  2 +
  3 +import io.netty.channel.ChannelHandlerContext;
  4 +import io.netty.channel.SimpleChannelInboundHandler;
  5 +import io.netty.handler.codec.socksx.SocksVersion;
  6 +import io.netty.handler.codec.socksx.v5.*;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +
  10 +public class Socks5InitialRequestHandler extends SimpleChannelInboundHandler<DefaultSocks5InitialRequest> {
  11 + private static final Logger logger = LoggerFactory.getLogger(Socks5InitialRequestHandler.class);
  12 + @Override
  13 + protected void channelRead0(ChannelHandlerContext ctx, DefaultSocks5InitialRequest msg) {
  14 + if(msg.decoderResult().isFailure()) {
  15 + logger.warn("current protocol is not socks5");
  16 + ctx.fireChannelRead(msg);
  17 + } else {
  18 + if(msg.version().equals(SocksVersion.SOCKS5)) {
  19 + Socks5InitialResponse initialResponse = new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH);
  20 + ctx.writeAndFlush(initialResponse);
  21 + System.out.println(initialResponse);
  22 + }
  23 + }
  24 +// ctx.pipeline().remove(Socks5InitialRequestDecoder.class);
  25 + }
  26 +}
  1 +package org.shadowsocks.handler.server;
  2 +
  3 +
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.ByteBufUtil;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.channel.ChannelInboundHandlerAdapter;
  9 +import org.shadowsocks.crypto.SSCrypto;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +
  13 +import java.net.InetAddress;
  14 +
  15 +public class AddressHandler extends ChannelInboundHandlerAdapter {
  16 +
  17 + private static Logger logger = LoggerFactory.getLogger(AddressHandler.class);
  18 + private final static int ADDR_TYPE_IPV4 = 1;
  19 + private final static int ADDR_TYPE_HOST = 3;
  20 +
  21 + private final ByteBuf dataQueue = Unpooled.buffer();
  22 + private final SSCrypto ssCrypto;
  23 +
  24 +
  25 + public AddressHandler(SSCrypto ssCrypto) {
  26 + this.ssCrypto = ssCrypto;
  27 + }
  28 +
  29 + @Override
  30 + public void channelActive(ChannelHandlerContext ctx) throws Exception {
  31 + logger.info("connected with {}", ctx.channel());
  32 + }
  33 +
  34 +
  35 + @Override
  36 + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  37 + ctx.close();
  38 + }
  39 +
  40 + @Override
  41 + public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  42 + logger.info("disconnected with {}", ctx.channel());
  43 + }
  44 +
  45 + @Override
  46 + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  47 + ByteBuf buff = (ByteBuf) msg;
  48 + if (buff.readableBytes() <= 0) {
  49 + return;
  50 + }
  51 + byte [] array = ByteBufUtil.getBytes(buff);
  52 + logger.info("http内容\n"+new String(array));
  53 + dataQueue.writeBytes(array);
  54 + if (dataQueue.readableBytes() < 2) {
  55 + return;
  56 + }
  57 + String host = null;
  58 + int port = 0;
  59 + int addressType = dataQueue.getUnsignedByte(0);
  60 + if (addressType == ADDR_TYPE_IPV4) {
  61 + if (dataQueue.readableBytes() < 7) {
  62 + return;
  63 + }
  64 + // addrType(1) + ipv4(4) + port(2)
  65 + dataQueue.readUnsignedByte();
  66 + byte[] ipBytes = new byte[4];
  67 + dataQueue.readBytes(ipBytes);
  68 + host = InetAddress.getByAddress(ipBytes).toString().substring(1);
  69 + port = dataQueue.readShort();
  70 + } else if (addressType == ADDR_TYPE_HOST) {
  71 + int hostLength = dataQueue.getUnsignedByte(1);
  72 + if (dataQueue.readableBytes() < hostLength + 4) {
  73 + return;
  74 + }
  75 + dataQueue.readUnsignedByte();
  76 + dataQueue.readUnsignedByte();
  77 + byte[] hostBytes = new byte[hostLength];
  78 + dataQueue.readBytes(hostBytes);
  79 + host = new String(hostBytes);
  80 + port = dataQueue.readShort();
  81 + } else {
  82 + throw new IllegalStateException("unknown address type: " + addressType);
  83 + }
  84 + ctx.channel().pipeline().addLast(new ClientDataHandler(host, port, ctx, dataQueue, ssCrypto));
  85 + ctx.channel().pipeline().remove(this);
  86 + }
  87 +
  88 +}
  1 +package org.shadowsocks.handler.server;
  2 +
  3 +import io.netty.bootstrap.Bootstrap;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.ByteBufUtil;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.*;
  8 +import io.netty.channel.socket.SocketChannel;
  9 +import io.netty.channel.socket.nio.NioSocketChannel;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.shadowsocks.crypto.SSCrypto;
  13 +
  14 +import java.net.InetAddress;
  15 +import java.nio.charset.StandardCharsets;
  16 +import java.util.Arrays;
  17 +import java.util.concurrent.atomic.AtomicReference;
  18 +
  19 +public class ClientDataHandler extends ChannelInboundHandlerAdapter {
  20 +
  21 +
  22 + private static Logger logger = LoggerFactory.getLogger(ClientDataHandler.class);
  23 + private final SSCrypto ssCrypto;
  24 + private final AtomicReference<Channel> remoteChannel = new AtomicReference<>();
  25 + private final ByteBuf clientCache;
  26 +
  27 + public ClientDataHandler(String host, int port, ChannelHandlerContext clientCtx, ByteBuf clientCache, SSCrypto ssCrypto) {
  28 + this.ssCrypto = ssCrypto;
  29 + this.clientCache = clientCache;
  30 + init(host, port, clientCtx, clientCache, ssCrypto);
  31 + }
  32 +
  33 + private void init(String host, int port, final ChannelHandlerContext clientCtx, final ByteBuf byteBuffer, final SSCrypto ssCrypto) {
  34 + Bootstrap bootstrap = new Bootstrap();
  35 + bootstrap.group(clientCtx.channel().eventLoop())
  36 + .channel(NioSocketChannel.class)
  37 + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5 * 1000)
  38 + .handler(new ChannelInitializer<SocketChannel>() {
  39 + @Override
  40 + protected void initChannel(SocketChannel ch) throws Exception {
  41 + ch.pipeline().addLast(new RemoteDataHandler(clientCtx, ssCrypto, byteBuffer));
  42 + }
  43 + });
  44 + try {
  45 + ChannelFuture channelFuture = bootstrap.connect(InetAddress.getByName(host), port);
  46 + channelFuture.addListener(new ChannelFutureListener() {
  47 + @Override
  48 + public void operationComplete(ChannelFuture future) throws Exception {
  49 + if (future.isSuccess()) {
  50 + logger.info("successfully to connect to {}:{}", host, port);
  51 + remoteChannel.set(future.channel());
  52 + } else {
  53 + logger.info("error to connect to {}:{}", host, port);
  54 + clientCtx.close();
  55 + }
  56 + }
  57 + });
  58 + } catch (Exception e) {
  59 + e.printStackTrace();
  60 + clientCtx.close();
  61 + }
  62 + }
  63 +
  64 + @Override
  65 + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  66 + ByteBuf buff = (ByteBuf) msg;
  67 + if (buff.readableBytes() <= 0) {
  68 + return;
  69 + }
  70 + byte[] bytes = ByteBufUtil.getBytes(buff);
  71 + byte[] decrypt = ssCrypto.decrypt(bytes, bytes.length);
  72 + if(remoteChannel.get() == null) {
  73 + clientCache.writeBytes(decrypt);
  74 + } else {
  75 + remoteChannel.get().writeAndFlush(Unpooled.copiedBuffer(decrypt));
  76 + }
  77 + }
  78 +
  79 + @Override
  80 + public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  81 + ctx.close();
  82 + if(remoteChannel.get() != null){
  83 + remoteChannel.get().close();
  84 + }
  85 + }
  86 +
  87 + public static class RemoteDataHandler extends SimpleChannelInboundHandler<ByteBuf> {
  88 +
  89 + private final ChannelHandlerContext clientCtx;
  90 + private final SSCrypto ssCrypto;
  91 + private final ByteBuf byteBuffer;
  92 +
  93 + public RemoteDataHandler(ChannelHandlerContext clientCtx, SSCrypto ssCrypto, ByteBuf byteBuffer) {
  94 + this.clientCtx = clientCtx;
  95 + this.ssCrypto = ssCrypto;
  96 + this.byteBuffer = byteBuffer;
  97 + }
  98 +
  99 + @Override
  100 + public void channelActive(ChannelHandlerContext ctx) throws Exception {
  101 + System.out.println("!!!!!!!!!!!!!!!!!!!!:"+new String(ByteBufUtil.getBytes(byteBuffer),StandardCharsets.UTF_8));
  102 + ctx.writeAndFlush(byteBuffer);
  103 + }
  104 +
  105 + @Override
  106 + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
  107 + byte[] bytes = ByteBufUtil.getBytes(msg);
  108 + try {
  109 +// byte[] encrypt = ssCrypto.encrypt(bytes, bytes.length);
  110 + System.out.println("++++++++++++++++:\n"+new String(bytes,StandardCharsets.UTF_8));
  111 + clientCtx.writeAndFlush(Unpooled.copiedBuffer(bytes));
  112 + } catch (Exception e) {
  113 + ctx.close();
  114 + clientCtx.close();
  115 + }
  116 + }
  117 +
  118 + @Override
  119 + public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  120 + ctx.close();
  121 + clientCtx.close();
  122 + }
  123 +
  124 + @Override
  125 + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  126 + ctx.close();
  127 + clientCtx.close();
  128 + }
  129 + }
  130 +}
  1 +package org.shadowsocks.handler.server;
  2 +
  3 +import io.netty.buffer.ByteBuf;
  4 +import io.netty.buffer.ByteBufUtil;
  5 +import io.netty.buffer.Unpooled;
  6 +import io.netty.channel.ChannelHandlerAdapter;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.channel.ChannelInboundHandlerAdapter;
  9 +import io.netty.handler.codec.http.*;
  10 +import io.netty.util.CharsetUtil;
  11 +import org.shadowsocks.crypto.SSCrypto;
  12 +import org.shadowsocks.dto.RequestInfo;
  13 +import org.shadowsocks.dto.RequestMethod;
  14 +import org.slf4j.Logger;
  15 +import org.slf4j.LoggerFactory;
  16 +
  17 +import java.net.InetAddress;
  18 +import java.net.URI;
  19 +import java.net.URISyntaxException;
  20 +import java.net.UnknownHostException;
  21 +
  22 +import static org.shadowsocks.dto.RequestMethod.CONNECT;
  23 +
  24 +public class StringHandler extends ChannelInboundHandlerAdapter {
  25 + private static Logger logger = LoggerFactory.getLogger(StringHandler.class);
  26 + private final ByteBuf dataQueue = Unpooled.buffer();
  27 + private final SSCrypto ssCrypto;
  28 + public StringHandler(SSCrypto ssCrypto) {
  29 + this.ssCrypto = ssCrypto;
  30 + }
  31 +
  32 + @Override
  33 + public void channelActive(ChannelHandlerContext ctx) throws Exception {
  34 + logger.info("connected with {}", ctx.channel());
  35 + }
  36 +
  37 +
  38 + @Override
  39 + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  40 + ctx.close();
  41 + }
  42 +
  43 + @Override
  44 + public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  45 + logger.info("disconnected with {}", ctx.channel());
  46 + }
  47 +
  48 + @Override
  49 + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  50 + ByteBuf buff = (ByteBuf) msg;
  51 + if (buff.readableBytes() <= 0) {
  52 + return;
  53 + }
  54 + byte [] array = ByteBufUtil.getBytes(buff);
  55 + logger.info("请求内容\n{}\n{}",msg.getClass(),new String(array));
  56 +
  57 + dataQueue.writeBytes(array);
  58 + if (dataQueue.readableBytes() < 2) {
  59 + return;
  60 + }
  61 + byte[] ats = ByteBufUtil.getBytes(dataQueue.slice(dataQueue.readerIndex(),dataQueue.bytesBefore((byte) ' ')));
  62 + String addressType = new String(ats);
  63 + RequestInfo requestInfo = getRequestInfo(RequestMethod.valueOf(addressType));
  64 + if(null != requestInfo)
  65 + {
  66 + ctx.channel().pipeline().addLast(new ClientDataHandler(requestInfo.getHost(), requestInfo.getPost(), ctx, buff,ssCrypto));
  67 + ctx.channel().pipeline().remove(this);
  68 + }else{
  69 + rterre(ctx,"没有解析到头部信息");
  70 + }
  71 +
  72 + }
  73 +
  74 + private void rterre(ChannelHandlerContext ctx,String message)
  75 + {
  76 + //构造内容
  77 + ByteBuf context = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8);
  78 +
  79 + //设置 response
  80 + FullHttpResponse response = new DefaultFullHttpResponse(
  81 + HttpVersion.HTTP_1_1,
  82 + HttpResponseStatus.OK,
  83 + context);
  84 +
  85 + //构建 响应头
  86 + HttpHeaders headers = response.headers();
  87 + headers.set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=utf-8");
  88 + headers.set(HttpHeaderNames.CONTENT_LENGTH, context.readableBytes());
  89 +
  90 + //将消息返回
  91 + ctx.channel().writeAndFlush(response);
  92 + }
  93 +
  94 + private RequestInfo getRequestInfo(RequestMethod requestMethod) throws UnknownHostException {
  95 + switch (requestMethod)
  96 + {
  97 + case CONNECT:
  98 + if (dataQueue.readableBytes() < 7) {
  99 + return null;
  100 + }
  101 + // addrType(7) + ipv4(4) + port(2)
  102 + dataQueue.readerIndex(7);
  103 + dataQueue.readUnsignedByte();
  104 + int index = dataQueue.bytesBefore((byte) ' ');
  105 + byte[] ats = new byte[index];
  106 + dataQueue.readBytes(ats);
  107 + String[] addresAndPost = new String(ats).split(":");
  108 + dataQueue.readUnsignedByte();
  109 + return new RequestInfo(addresAndPost[0],Integer.parseInt(addresAndPost[1]));
  110 +// case ADDR_TYPE_HOST:
  111 +// int hostLength = dataQueue.getUnsignedByte(1);
  112 +// if (dataQueue.readableBytes() < hostLength + 4) {
  113 +// return null;
  114 +// }
  115 +// dataQueue.readUnsignedByte();
  116 +// dataQueue.readUnsignedByte();
  117 +// byte[] hostBytes = new byte[hostLength];
  118 +// dataQueue.readBytes(hostBytes);
  119 +// return new RequestInfo(new String(hostBytes),dataQueue.readShort());
  120 + default:
  121 + throw new IllegalStateException("unknown address type: " + requestMethod);
  122 + }
  123 + }
  124 +}
  1 +package org.shadowsocks.util;
  2 +import org.shadowsocks.config.BaseConfig;
  3 +import org.shadowsocks.config.Config;
  4 +import org.shadowsocks.config.JsonConfig;
  5 +import org.shadowsocks.config.RealConfig;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +public class CommandLineParser {
  10 +
  11 + public static Config parse(String[] args)
  12 + {
  13 + RealConfig realConfig = new RealConfig();
  14 + for(int i=0;i<args.length;i++)
  15 + {
  16 + System.out.println(Arrays.toString(args));
  17 + String[] parts = args[i].split("=");
  18 + String key = parts[0];
  19 + String value = parts[1];
  20 +
  21 + if(key.compareTo("config")==0)
  22 + return new JsonConfig(value);
  23 +
  24 + if(key.compareTo("server")==0)
  25 + realConfig.server = value;
  26 +
  27 + if(key.compareTo("server_port")==0)
  28 + realConfig.server_port = Integer.parseInt(value);
  29 +
  30 + if(key.compareTo("local_address")==0)
  31 + realConfig.local_address = value;
  32 + if(key.compareTo("local_port")==0)
  33 + realConfig.local_port = Integer.parseInt(value);
  34 + if(key.compareTo("method")==0)
  35 + realConfig.method = value;
  36 + if(key.compareTo("password")==0)
  37 + realConfig.password = value;
  38 + if(key.compareTo("time_out")==0)
  39 + realConfig.timeout = Integer.parseInt(value);
  40 + }
  41 +
  42 + return new BaseConfig(realConfig) {
  43 + @Override
  44 + public RealConfig loadConfig(Object source) {
  45 + return (RealConfig) source;
  46 + }
  47 + };
  48 + }
  49 +}
  1 +<configuration>
  2 + <timestamp key="byDay" datePattern="yyyyMMdd'T'HHmmss"/>
  3 +
  4 + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  5 + <encoder>
  6 + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  7 + </encoder>
  8 + </appender>
  9 +
  10 + <appender name="FILE" class="ch.qos.logback.core.FileAppender">
  11 + <file>logs/org.shadowsocks-log-${byDay}.txt </file>
  12 + <append>true</append>
  13 + <encoder>
  14 + <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
  15 + </encoder>
  16 + </appender>
  17 +
  18 + <root level="info">
  19 + <appender-ref ref="FILE" />
  20 + <appender-ref ref="STDOUT" />
  21 + </root>
  22 +
  23 +</configuration>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +
  3 +<assembly>
  4 + <id>bin</id>
  5 + <!-- 最终打包成一个用于发布的zip文件 -->
  6 + <formats>
  7 + <format>zip</format>
  8 + </formats>
  9 +
  10 + <!-- Adds dependencies to zip package under lib directory -->
  11 + <dependencySets>
  12 + <dependencySet>
  13 + <!--
  14 + 不使用项目的artifact,第三方jar不要解压,打包进zip文件的lib目录
  15 + -->
  16 + <useProjectArtifact>false</useProjectArtifact>
  17 + <outputDirectory>lib</outputDirectory>
  18 + <unpack>false</unpack>
  19 + </dependencySet>
  20 + </dependencySets>
  21 +
  22 + <fileSets>
  23 + <!-- 把项目相关的说明文件,打包进zip文件的根目录 -->
  24 + <fileSet>
  25 + <directory>${project.basedir}</directory>
  26 + <outputDirectory>/</outputDirectory>
  27 + <includes>
  28 + <include>README*</include>
  29 + <include>LICENSE*</include>
  30 + <include>NOTICE*</include>
  31 + </includes>
  32 + </fileSet>
  33 +
  34 + <!-- 把项目的配置文件,打包进zip文件的config目录 -->
  35 + <fileSet>
  36 + <directory>${project.basedir}\src\main\resources\configs</directory>
  37 + <outputDirectory>../configs</outputDirectory>
  38 + <includes>
  39 + <include>*.properties</include>
  40 + </includes>
  41 + </fileSet>
  42 +
  43 + <!-- 把项目的配置文件,提出来 -->
  44 + <fileSet>
  45 + <directory>${project.basedir}\src\main\resources</directory>
  46 + <outputDirectory>/</outputDirectory>
  47 + <includes>
  48 + <include>*.properties</include>
  49 + <include>*.yml</include>
  50 + </includes>
  51 + </fileSet>
  52 +
  53 + <!-- 把项目的脚本文件目录( src/main/scripts )中的启动脚本文件,打包进zip文件的跟目录 -->
  54 + <fileSet>
  55 + <directory>${project.basedir}\bin</directory>
  56 + <outputDirectory></outputDirectory>
  57 + <includes>
  58 + <include>start.*</include>
  59 + <include>stop.*</include>
  60 + </includes>
  61 + </fileSet>
  62 +
  63 + <!-- 把项目自己编译出来的jar文件,打包进zip文件的根目录 -->
  64 + <fileSet>
  65 + <directory>${project.build.directory}</directory>
  66 + <outputDirectory></outputDirectory>
  67 + <includes>
  68 + <include>*.jar</include>
  69 + </includes>
  70 + </fileSet>
  71 + </fileSets>
  72 +</assembly>
  1 +package org.shadowsocks.Util;
  2 +
  3 +import javax.net.ssl.HttpsURLConnection;
  4 +import java.io.BufferedReader;
  5 +import java.io.DataOutputStream;
  6 +import java.io.InputStreamReader;
  7 +import java.net.HttpURLConnection;
  8 +import java.net.URL;
  9 +
  10 +public class HttpUtil {
  11 + private final String USER_AGENT = "Mozilla/5.0";
  12 +
  13 +// public static void main(String[] args) throws Exception {
  14 +//
  15 +// HttpURLConnectionExample http = new HttpURLConnectionExample();
  16 +//
  17 +// System.out.println("Testing 1 - Send Http GET request");
  18 +// http.sendGet();
  19 +//
  20 +// System.out.println("\nTesting 2 - Send Http POST request");
  21 +// http.sendPost();
  22 +//
  23 +// }
  24 +
  25 + // HTTP GET请求
  26 + public void sendGet() throws Exception {
  27 +
  28 + String url = "http://www.baidu.com/search?q=mkyong";
  29 +
  30 + URL obj = new URL(url);
  31 + HttpURLConnection con = (HttpURLConnection) obj.openConnection();
  32 +
  33 + //默认值我GET
  34 + con.setRequestMethod("GET");
  35 +
  36 + //添加请求头
  37 + con.setRequestProperty("User-Agent", USER_AGENT);
  38 +
  39 + int responseCode = con.getResponseCode();
  40 + System.out.println("\nSending 'GET' request to URL : " + url);
  41 + System.out.println("Response Code : " + responseCode);
  42 +
  43 + BufferedReader in = new BufferedReader(
  44 + new InputStreamReader(con.getInputStream()));
  45 + String inputLine;
  46 + StringBuffer response = new StringBuffer();
  47 +
  48 + while ((inputLine = in.readLine()) != null) {
  49 + response.append(inputLine);
  50 + }
  51 + in.close();
  52 +
  53 + //打印结果
  54 + System.out.println(response.toString());
  55 +
  56 + }
  57 +
  58 + // HTTP POST请求
  59 + private void sendPost() throws Exception {
  60 +
  61 + String url = "https://selfsolve.apple.com/wcResults.do";
  62 + URL obj = new URL(url);
  63 + HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
  64 +
  65 + //添加请求头
  66 + con.setRequestMethod("POST");
  67 + con.setRequestProperty("User-Agent", USER_AGENT);
  68 + con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
  69 +
  70 + String urlParameters = "sn=C02G8416DRJM&cn=&locale=&caller=&num=12345";
  71 +
  72 + //发送Post请求
  73 + con.setDoOutput(true);
  74 + DataOutputStream wr = new DataOutputStream(con.getOutputStream());
  75 + wr.writeBytes(urlParameters);
  76 + wr.flush();
  77 + wr.close();
  78 +
  79 + int responseCode = con.getResponseCode();
  80 + System.out.println("\nSending 'POST' request to URL : " + url);
  81 + System.out.println("Post parameters : " + urlParameters);
  82 + System.out.println("Response Code : " + responseCode);
  83 +
  84 + BufferedReader in = new BufferedReader(
  85 + new InputStreamReader(con.getInputStream()));
  86 + String inputLine;
  87 + StringBuffer response = new StringBuffer();
  88 +
  89 + while ((inputLine = in.readLine()) != null) {
  90 + response.append(inputLine);
  91 + }
  92 + in.close();
  93 +
  94 + //打印结果
  95 + System.out.println(response.toString());
  96 +
  97 + }
  98 +}
  1 +package org.shadowsocks.crypto;
  2 +
  3 +import org.junit.Assert;
  4 +import org.junit.Test;
  5 +import java.nio.charset.StandardCharsets;
  6 +import java.util.Arrays;
  7 +import java.util.Random;
  8 +
  9 +import static java.lang.System.arraycopy;
  10 +import static org.shadowsocks.crypto.AESCrypto.CIPHER_AES_256_CFB;
  11 +
  12 +public class AESCryptoTest {
  13 +
  14 +
  15 +
  16 +
  17 +
  18 + @Test
  19 + public void encyptTest() throws Exception {
  20 + byte[] testCase = "hello world, this is pink floyd".getBytes(StandardCharsets.UTF_8);
  21 + AESCrypto cryptoClient = new AESCrypto(CIPHER_AES_256_CFB, "abc123");
  22 + AESCrypto cryptoServer = new AESCrypto(CIPHER_AES_256_CFB, "abc123");
  23 + byte[] en = cryptoClient.encrypt(testCase, testCase.length);
  24 + byte[] de = cryptoServer.decrypt(en, en.length);
  25 + Assert.assertArrayEquals(de, testCase);
  26 + for (int i = 0; i < 100; i++) {
  27 + testCase = Utils.randomBytes(20);
  28 + en = cryptoServer.encrypt(testCase, testCase.length);
  29 + de = cryptoClient.decrypt(en, en.length);
  30 + Assert.assertArrayEquals(de, testCase);
  31 +
  32 + }
  33 +
  34 +
  35 + }
  36 +}
  1 +package org.shadowsocks.socks5;
  2 +import java.net.InetSocketAddress;
  3 +import java.net.PasswordAuthentication;
  4 +import java.net.Proxy;
  5 +
  6 +import okhttp3.OkHttpClient;
  7 +import okhttp3.Request;
  8 +import okhttp3.Response;
  9 +import org.apache.http.client.HttpClient;
  10 +import org.apache.http.client.methods.HttpGet;
  11 +import org.apache.http.impl.client.HttpClients;
  12 +
  13 +
  14 +public class HttpRequestTest {
  15 + public static void main(String[] args) throws Exception {
  16 + final String user = "t";
  17 + final String password = "test";
  18 +
  19 + Proxy proxyTest = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 10000));
  20 +
  21 +// java.net.Authenticator.setDefault(new java.net.Authenticator()
  22 +// {
  23 +// private PasswordAuthentication authentication = new PasswordAuthentication(user, password.toCharArray());
  24 +//
  25 +// @Override
  26 +// protected PasswordAuthentication getPasswordAuthentication()
  27 +// {
  28 +// return authentication;
  29 +// }
  30 +// });
  31 +
  32 +
  33 + OkHttpClient client = new OkHttpClient.Builder().proxy(proxyTest).build();
  34 + Request request = new Request.Builder().url("http://www.baidu.com").build();
  35 + Response response = client.newCall(request).execute();
  36 + System.out.println(response.code());
  37 + System.out.println(response.body());
  38 +
  39 + client.dispatcher().executorService().shutdown();
  40 + client.connectionPool().evictAll();
  41 + }
  42 +
  43 + public void socks5PortTest() throws Exception{
  44 + Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 10000));
  45 + HttpGet get = new HttpGet("http://www.baidu.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");
  46 + HttpClient httpClient = HttpClients.createDefault();
  47 + httpClient.execute(get);
  48 + }
  49 +}
  1 +package org.shadowsocks.socks5;
  2 +
  3 +import io.netty.bootstrap.ServerBootstrap;
  4 +import io.netty.channel.ChannelFuture;
  5 +import io.netty.channel.ChannelInitializer;
  6 +import io.netty.channel.ChannelOption;
  7 +import io.netty.channel.nio.NioEventLoopGroup;
  8 +import io.netty.channel.socket.SocketChannel;
  9 +import io.netty.channel.socket.nio.NioServerSocketChannel;
  10 +import org.shadowsocks.handler.server.StringHandler;
  11 +
  12 +import java.io.BufferedReader;
  13 +import java.io.IOException;
  14 +import java.io.InputStreamReader;
  15 +import java.net.ServerSocket;
  16 +import java.net.Socket;
  17 +
  18 +public class PortListenTest {
  19 + public static void main(String args[]) {
  20 +
  21 + int port = 443;
  22 + ServerSocket server_socket;
  23 + BufferedReader input;
  24 + try {
  25 + port = Integer.parseInt(args[0]);
  26 + }
  27 + catch (Exception e) {
  28 + System.out.println("port = 443 (default)");
  29 + port = 443;
  30 + }
  31 +
  32 + try {
  33 + server_socket = new ServerSocket(port);
  34 + System.out.println("Server waiting for client on port " +
  35 + server_socket.getLocalPort());
  36 + // server infinite loop
  37 + while(true) {
  38 + Socket socket = server_socket.accept();
  39 + System.out.println("New connection accepted " +
  40 + socket.getInetAddress() +
  41 + ":" + socket.getPort());
  42 + input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  43 + // print received data
  44 + try {
  45 + while(true) {
  46 + String message = input.readLine();
  47 + if (message==null) break;
  48 + System.out.println(message);
  49 + }
  50 + }
  51 + catch (IOException e) {
  52 + System.out.println(e);
  53 + }
  54 +
  55 + // connection closed by client
  56 + try {
  57 + socket.close();
  58 + System.out.println("Connection closed by client");
  59 + }
  60 + catch (IOException e) {
  61 + System.out.println(e);
  62 + }
  63 + }
  64 + }
  65 + catch (IOException e) {
  66 + System.out.println(e);
  67 + }
  68 + }
  69 +}
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<module type="JAVA_MODULE" version="4">
  3 + <component name="NewModuleRootManager" inherit-compiler-output="true">
  4 + <exclude-output />
  5 + <content url="file://$MODULE_DIR$">
  6 + <sourceFolder url="file://$MODULE_DIR$/java" isTestSource="true" />
  7 + </content>
  8 + <orderEntry type="inheritedJdk" />
  9 + <orderEntry type="sourceFolder" forTests="false" />
  10 + </component>
  11 +</module>
  1 +#!/usr/bin/env bash
  2 +
  3 +#default value for local port
  4 +local_port=1080
  5 +#default value for cipher method
  6 +method="aes-256-cfb"
  7 +#default value for server port
  8 +server_port=8388
  9 +local_address="127.0.0.1"
  10 +time_out=600
  11 +
  12 +#parse the parameters
  13 +while test $# -gt 0; do
  14 + case "$1" in
  15 + -h|--help)
  16 + echo "A fast tunnel proxy that helps you bypass firewalls."
  17 +
  18 + echo "You can supply configurations via either config file or command line arguments."
  19 +
  20 + echo "Proxy options:"
  21 + echo " -c CONFIG path to config file"
  22 + echo " -s SERVER_ADDR server address"
  23 + echo " -p SERVER_PORT server port, default: 8388"
  24 + echo " -b LOCAL_ADDR local binding address, default: 127.0.0.1"
  25 + echo " -l LOCAL_PORT local port, default: 1080"
  26 + echo " -k PASSWORD password"
  27 + echo " -m METHOD encryption method, default: aes-256-cfb"
  28 + echo " Sodium:"
  29 + echo " chacha20, chacha20-ietf."
  30 + echo " OpenSSL:"
  31 + echo " aes-{128|192|256}-cfb,aes-{128|192|256}-ofb,"
  32 + echo " -t TIMEOUT timeout in seconds, default: 600"
  33 + exit 0
  34 + ;;
  35 + -c)
  36 + shift
  37 + if test $# -gt 0; then
  38 + config=$1
  39 + fi
  40 + shift
  41 + ;;
  42 + -s)
  43 + shift
  44 + if test $# -gt 0; then
  45 + server=$1
  46 + fi
  47 + shift
  48 + ;;
  49 + -p)
  50 + shift
  51 + if test $# -gt 0; then
  52 + server_port=$1
  53 + fi
  54 + shift
  55 + ;;
  56 + -b)
  57 + shift
  58 + if test $# -gt 0; then
  59 + local_address=$1
  60 + fi
  61 + shift
  62 + ;;
  63 + -l)
  64 + shift
  65 + if test $# -gt 0; then
  66 + local_port=$1
  67 + fi
  68 + shift
  69 + ;;
  70 + -k)
  71 + shift
  72 + if test $# -gt 0; then
  73 + password=$1
  74 + fi
  75 + shift
  76 + ;;
  77 + -m)
  78 + shift
  79 + if test $# -gt 0; then
  80 + method=$1
  81 + fi
  82 + shift
  83 + ;;
  84 + -t)
  85 + shift
  86 + if test $# -gt 0; then
  87 + time_out=$1
  88 + fi
  89 + shift
  90 + ;;
  91 + *)
  92 + break
  93 + ;;
  94 + esac
  95 +done
  96 +
  97 +main="LocalMain"
  98 +
  99 +#printing some output to the users
  100 +echo "config: $config"
  101 +if [ -n "$config" ]
  102 +then
  103 + echo "Starting shadowsocks ...";
  104 + java -jar -jar ./target/shadowsocks-java-1.0-SNAPSHOT.jar ${main} config=${config}
  105 +elif [ -z "$server" ]
  106 +then
  107 + echo "please set a server address"
  108 + exit 0
  109 +elif [ -z "$password" ]
  110 +then
  111 + echo "please set a password"
  112 + exit 0
  113 +else
  114 + java -jar ./target/shadowsocks-java-1.0-SNAPSHOT.jar ${main} server=${server} server_port=${server_port}
  115 +local_address=${local_address} local_port=${local_port} method=${method} password=${password}
  116 +fi
  117 +
  118 +
  1 +#!/usr/bin/env bash
  2 +
  3 +#default value for local port
  4 +local_port=1080
  5 +#default value for cipher method
  6 +method="aes-256-cfb"
  7 +#default value for server port
  8 +server_port=8388
  9 +local_address="127.0.0.1"
  10 +time_out=600
  11 +
  12 +#parse the parameters
  13 +while test $# -gt 0; do
  14 + case "$1" in
  15 + -h|--help)
  16 + echo "A fast tunnel proxy that helps you bypass firewalls."
  17 +
  18 + echo "You can supply configurations via either config file or command line arguments."
  19 +
  20 + echo "Proxy options:"
  21 + echo " -c CONFIG path to config file"
  22 + echo " -s SERVER_ADDR server address"
  23 + echo " -p SERVER_PORT server port, default: 8388"
  24 +# echo " -b LOCAL_ADDR local binding address, default: 127.0.0.1"
  25 +# echo " -l LOCAL_PORT local port, default: 1080"
  26 + echo " -k PASSWORD password"
  27 + echo " -m METHOD encryption method, default: aes-256-cfb"
  28 + echo " Sodium:"
  29 + echo " chacha20, chacha20-ietf."
  30 + echo " OpenSSL:"
  31 + echo " aes-{128|192|256}-cfb,aes-{128|192|256}-ofb,"
  32 + echo " -t TIMEOUT timeout in seconds, default: 600"
  33 + exit 0
  34 + ;;
  35 + -c)
  36 + shift
  37 + if test $# -gt 0; then
  38 + config=$1
  39 + fi
  40 + shift
  41 + ;;
  42 + -s)
  43 + shift
  44 + if test $# -gt 0; then
  45 + server=$1
  46 + fi
  47 + shift
  48 + ;;
  49 + -p)
  50 + shift
  51 + if test $# -gt 0; then
  52 + server_port=$1
  53 + fi
  54 + shift
  55 + ;;
  56 + -b)
  57 + shift
  58 + if test $# -gt 0; then
  59 + local_address=$1
  60 + fi
  61 + shift
  62 + ;;
  63 + -l)
  64 + shift
  65 + if test $# -gt 0; then
  66 + local_port=$1
  67 + fi
  68 + shift
  69 + ;;
  70 + -k)
  71 + shift
  72 + if test $# -gt 0; then
  73 + password=$1
  74 + fi
  75 + shift
  76 + ;;
  77 + -m)
  78 + shift
  79 + if test $# -gt 0; then
  80 + method=$1
  81 + fi
  82 + shift
  83 + ;;
  84 + -t)
  85 + shift
  86 + if test $# -gt 0; then
  87 + time_out=$1
  88 + fi
  89 + shift
  90 + ;;
  91 + *)
  92 + break
  93 + ;;
  94 + esac
  95 +done
  96 +
  97 +main="ServerMain"
  98 +#printing some output to the users
  99 +echo "config: $config"
  100 +if [ -n "$config" ]
  101 +then
  102 + echo "Starting shadowsocks ...";
  103 + java -jar ./target/shadowsocks-java-1.0-SNAPSHOT.jar config=${config}
  104 +elif [ -z "$server" ]
  105 +then
  106 + echo "please set a server address"
  107 + exit 0
  108 +elif [ -z "$password" ]
  109 +then
  110 + echo "please set a password"
  111 + exit 0
  112 +else
  113 + java -jar ./target/shadowsocks-java-1.0-SNAPSHOT.jar ${main} server=${server} server_port=${server_port}
  114 +local_address=${local_address} local_port=${local_port} method=${method} password=${password}
  115 +fi
  116 +
  117 +