正在显示
38 个修改的文件
包含
2222 行增加
和
0 行删除
README.md
0 → 100644
| 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 | +- [ ] 编写使用脚本 |
config.json
0 → 100644
doc/mykeys.pem
0 → 100644
| 1 | +-----BEGIN RSA PRIVATE KEY----- | ||
| 2 | +MIIEowIBAAKCAQEAv3o0t1tZLXlxzO9y06062OElqgOp9ZNZhxBg1vAjg1QBgCFg | ||
| 3 | +j3iNqG5RdYHmEl/Py88bB7S1TM3RvjfKHlaetbwJK3wak6VjDoFusvcKhXyBLI+D | ||
| 4 | +6ocvVFLeVEeTHU3wPUGLyejUXA849yEoAOntTYCVpwcli2rKK6ntj28W5X/47HbE | ||
| 5 | +ocWrBBeDbB1NIH4ZhtCaSHb8rBo2lqzrDFQHawjoICPb7N/vKbIF0YHaAfiUypFI | ||
| 6 | +hF4Ajc8quYbuXn7Hp1zJc33RgayzB3Jk3B/FQGva4Dk5+XejvmKMAuziSy0Vci+R | ||
| 7 | +h+bIRcyLR4ruCdrR4zvrKd8dF4gxSrMZh/Uc8QIDAQABAoIBAF83mCtuA6S3db3h | ||
| 8 | +fQqCZHchTyeCduwmAClIHcAE3sQZ7D0ZW/k8i8UsRtnWZODSQHUrYlesp1OwLhMX | ||
| 9 | +jy0TCg15ml3DczvkzKOT/caFTvged+X+4CyqWQDnzKPAjAnQjh0IEUmwvJmjAEL0 | ||
| 10 | +e/4oVddqkADlbugrjKm18OqY5Zvt8JI8yayIkwS8VuLvT3s2+XhSZ//U3goBaiis | ||
| 11 | ++RrbKSGbNsQXM2+u+bd/9lgOFPQrd/zNN2/XJaopD0L4GN9je+UlEL0SovynN79D | ||
| 12 | +AfF6enPSpTYYxTa2RaonvfmLPg6ezZifATZpKTRBT6A9BLMTswqd+Gu0J5z4sd3P | ||
| 13 | +oq3AZ/kCgYEA8N5tk/8uJLlhTEcq6jbcYCl1FnnJz/nnqAuj9l0JzpmwBXAhHDw/ | ||
| 14 | +YIAZlN1yZrSF2SycPNMtFDiAcW3/0ukTD0sX9jUqboo6BfXBsSzVGEdiS1NX75bv | ||
| 15 | +SgvCa1bGK3iutQj9r0f2STfqTBMIRWgjOlkoVVvDRSIZ7Qv2IHYGwacCgYEAy4F7 | ||
| 16 | +AxifKc98IyKSsQEtjnMV+nc1zvYaFCyvfLTJVZvZzTeLSoPpjV7ZIzvykR5oeQjT | ||
| 17 | +QDgfZPJrwtS7MDNLDFvSSDCtqGFlmhZVpqgz+YrqOehMdMqKA0GQFhriRG+zRYj4 | ||
| 18 | +300B7A6iQnXwW03+XbkK351m9cinYf8ZeLisD6cCgYBCwxQSQleS3EMotk0h0ATz | ||
| 19 | +LmqCyUzztWvNOzS+E3mlZxnP2A2FWvmiEL+GcH7GEuFKmb/QaCQFh8eqKPgQiIO8 | ||
| 20 | +6G6C0qepLez5O+3s4uVJrneDM12bTfWTS4Ee6VVSNUgPa6eNDuAn2TS600emnT+u | ||
| 21 | +a4nvZtjP76zJ9FfLFYu33wKBgG1IDb++5BG2usm/Uhi9MjKRuJa0l2+ZFq4IxP/p | ||
| 22 | +sUhIlfAuyJzuM2fYLDBQi9TECIQeahrNSIaT66xHQnICyWVMlaJwiYCnNMFHBx2k | ||
| 23 | +q6xwnZEVHqGE9hIDjhVgwGrRtOo9QK/hEYwq25Vm8fiF7Hd129HzG4wXAVONgqci | ||
| 24 | +PDt3AoGBAKROcAikGLrXL/KZrnqQeJizPaV+/gUv785U20lqmhEiRfNHTsyJS4OJ | ||
| 25 | +UYgL+lWdvcK9RdZLP7B/QGRjjdvkf/V//L3NZqRFKGkUgAewJBr4Wljcjvc7ndvC | ||
| 26 | +3aSwiZS7V4E2fAtC7VH+d0Zr12BmRcTm3HNDgmXki5ZSDhCWPfpt | ||
| 27 | +-----END RSA PRIVATE KEY----- |
pom.xml
0 → 100644
| 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 | + <dependency> | ||
| 12 | + <groupId>io.netty</groupId> | ||
| 13 | + <artifactId>netty-all</artifactId> | ||
| 14 | + <version>4.1.7.Final</version> | ||
| 15 | + </dependency> | ||
| 16 | + | ||
| 17 | + <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on --> | ||
| 18 | + <dependency> | ||
| 19 | + <groupId>org.bouncycastle</groupId> | ||
| 20 | + <artifactId>bcprov-jdk15on</artifactId> | ||
| 21 | + <version>1.56</version> | ||
| 22 | + </dependency> | ||
| 23 | + | ||
| 24 | + <dependency> | ||
| 25 | + <groupId>gnu.getopt</groupId> | ||
| 26 | + <artifactId>java-getopt</artifactId> | ||
| 27 | + <version>1.0.13</version> | ||
| 28 | + </dependency> | ||
| 29 | + | ||
| 30 | + <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic --> | ||
| 31 | + <dependency> | ||
| 32 | + <groupId>ch.qos.logback</groupId> | ||
| 33 | + <artifactId>logback-classic</artifactId> | ||
| 34 | + <version>1.1.9</version> | ||
| 35 | + </dependency> | ||
| 36 | + | ||
| 37 | + <dependency> | ||
| 38 | + <groupId>junit</groupId> | ||
| 39 | + <artifactId>junit</artifactId> | ||
| 40 | + <version>4.12</version> | ||
| 41 | + <scope>test</scope> | ||
| 42 | + </dependency> | ||
| 43 | + <dependency> | ||
| 44 | + <groupId>com.squareup.okhttp3</groupId> | ||
| 45 | + <artifactId>okhttp</artifactId> | ||
| 46 | + <version>3.11.0</version> | ||
| 47 | + <scope>test</scope> | ||
| 48 | + </dependency> | ||
| 49 | + <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> | ||
| 50 | + <dependency> | ||
| 51 | + <groupId>org.apache.httpcomponents</groupId> | ||
| 52 | + <artifactId>httpclient</artifactId> | ||
| 53 | + <scope>test</scope> | ||
| 54 | + <version>4.5.6</version> | ||
| 55 | + </dependency> | ||
| 56 | + <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> | ||
| 57 | + <dependency> | ||
| 58 | + <groupId>com.google.code.gson</groupId> | ||
| 59 | + <artifactId>gson</artifactId> | ||
| 60 | + <version>2.8.5</version> | ||
| 61 | + </dependency> | ||
| 62 | + | ||
| 63 | + | ||
| 64 | + </dependencies> | ||
| 65 | + | ||
| 66 | + <build> | ||
| 67 | + <plugins> | ||
| 68 | + <plugin> | ||
| 69 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 70 | + <artifactId>maven-surefire-plugin</artifactId> | ||
| 71 | + <configuration> | ||
| 72 | + <skip>true</skip> | ||
| 73 | + </configuration> | ||
| 74 | + </plugin> | ||
| 75 | + <plugin> | ||
| 76 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 77 | + <artifactId>maven-compiler-plugin</artifactId> | ||
| 78 | + <version>2.3.2</version> | ||
| 79 | + <configuration> | ||
| 80 | + <skip>true</skip> | ||
| 81 | + <source>1.8</source> | ||
| 82 | + <target>1.8</target> | ||
| 83 | + </configuration> | ||
| 84 | + </plugin> | ||
| 85 | + <plugin> | ||
| 86 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 87 | + <artifactId>maven-jar-plugin</artifactId> | ||
| 88 | + <configuration> | ||
| 89 | + <archive> | ||
| 90 | + <manifest> | ||
| 91 | + <addClasspath>true</addClasspath> | ||
| 92 | + <classpathPrefix>lib/</classpathPrefix><!--指定classpath的前缀--> | ||
| 93 | + <mainClass>org.shadowsocks.Main</mainClass><!--指定主类的类名--> | ||
| 94 | + </manifest> | ||
| 95 | + </archive> | ||
| 96 | + </configuration> | ||
| 97 | + </plugin> | ||
| 98 | + <plugin> | ||
| 99 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 100 | + <artifactId>maven-dependency-plugin</artifactId> | ||
| 101 | + <executions> | ||
| 102 | + <execution> | ||
| 103 | + <id>copy-dependencies</id> | ||
| 104 | + <phase>prepare-package</phase> | ||
| 105 | + <goals> | ||
| 106 | + <goal>copy-dependencies</goal> | ||
| 107 | + </goals> | ||
| 108 | + <configuration> | ||
| 109 | + <!--指定outputDirectory--> | ||
| 110 | + <outputDirectory>${project.build.directory}/lib</outputDirectory> | ||
| 111 | + <overWriteReleases>false</overWriteReleases> | ||
| 112 | + <overWriteSnapshots>false</overWriteSnapshots> | ||
| 113 | + <overWriteIfNewer>true</overWriteIfNewer> | ||
| 114 | + </configuration> | ||
| 115 | + </execution> | ||
| 116 | + </executions> | ||
| 117 | + </plugin> | ||
| 118 | + </plugins> | ||
| 119 | + </build> | ||
| 120 | +</project> |
src/main/java/org/shadowsocks/Main.java
0 → 100644
| 1 | +package org.shadowsocks; | ||
| 2 | +import org.shadowsocks.config.Config; | ||
| 3 | +import org.shadowsocks.config.JsonConfig; | ||
| 4 | +import org.shadowsocks.util.CommandLineParser; | ||
| 5 | +import org.slf4j.Logger; | ||
| 6 | +import org.slf4j.LoggerFactory; | ||
| 7 | + | ||
| 8 | +import java.util.Arrays; | ||
| 9 | + | ||
| 10 | +public class Main { | ||
| 11 | + private static Logger logger = LoggerFactory.getLogger(Main.class); | ||
| 12 | + | ||
| 13 | + public static void main( String[] args) throws Exception{ | ||
| 14 | + if(null == args || args.length == 0) | ||
| 15 | + { | ||
| 16 | + args = new String[]{"ServerMain"}; | ||
| 17 | + } | ||
| 18 | + String path = System.getProperty("user.dir") + "/config.json"; | ||
| 19 | + logger.info("配置文件地址【{}】",path); | ||
| 20 | + Config config = new JsonConfig(path); | ||
| 21 | + logger.info("配置文件内容【{}】",config); | ||
| 22 | + | ||
| 23 | + String main = args[0]; | ||
| 24 | +// Config config = CommandLineParser.parse(Arrays.copyOfRange(args,1,args.length)); | ||
| 25 | + switch (main){ | ||
| 26 | + case "LocalMain": | ||
| 27 | + new ShadowsocksLocal(config).start();break; | ||
| 28 | + case "ServerMain": | ||
| 29 | + new ShadowSocksServer(config).start();break; | ||
| 30 | + | ||
| 31 | + default:System.out.println("please set running mode");break; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + } | ||
| 36 | +} |
| 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 org.shadowsocks.config.Config; | ||
| 11 | +import org.shadowsocks.crypto.CryptoFactory; | ||
| 12 | +import org.shadowsocks.handler.server.AddressHandler; | ||
| 13 | +import org.slf4j.Logger; | ||
| 14 | +import org.slf4j.LoggerFactory; | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +public class ShadowSocksServer { | ||
| 18 | + | ||
| 19 | + private static Logger logger = LoggerFactory.getLogger(ShadowSocksServer.class); | ||
| 20 | + Config config; | ||
| 21 | + public ShadowSocksServer(Config config){ | ||
| 22 | + this.config = config; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public void start() throws InterruptedException { | ||
| 26 | + NioEventLoopGroup group = new NioEventLoopGroup(2); | ||
| 27 | + ServerBootstrap bootstrap = new ServerBootstrap(); | ||
| 28 | + try { | ||
| 29 | + bootstrap.group(group) | ||
| 30 | + .channel(NioServerSocketChannel.class) | ||
| 31 | + .localAddress(config.getServerPort()) | ||
| 32 | + .option(ChannelOption.SO_TIMEOUT, config.getTimeout()) | ||
| 33 | + .childHandler(new ChannelInitializer<SocketChannel>() { | ||
| 34 | + @Override | ||
| 35 | + protected void initChannel(SocketChannel socketChannel) throws Exception { | ||
| 36 | + socketChannel.pipeline().addLast(new AddressHandler(CryptoFactory.create(config.getMethod(), | ||
| 37 | + config.getPassword()))); | ||
| 38 | + } | ||
| 39 | + }); | ||
| 40 | + ChannelFuture channelFuture = bootstrap.bind().sync(); | ||
| 41 | + logger.info("started and listen on " + channelFuture.channel().localAddress()); | ||
| 42 | + channelFuture.channel().closeFuture().sync(); | ||
| 43 | + | ||
| 44 | + } finally { | ||
| 45 | + group.shutdownGracefully(); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + } | ||
| 49 | +} |
| 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 | +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 | +/* | ||
| 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.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 | + byte[] decrypted = ssCrypto.decrypt(array, array.length); | ||
| 53 | + dataQueue.writeBytes(decrypted); | ||
| 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(ctx.channel()+Arrays.toString(ssCrypto.getIV(true))); | ||
| 111 | + | ||
| 112 | + System.out.println("++++++++++++++++:\n"+new String(bytes,StandardCharsets.UTF_8)); | ||
| 113 | + | ||
| 114 | + clientCtx.writeAndFlush(Unpooled.copiedBuffer(encrypt)); | ||
| 115 | + } catch (Exception e) { | ||
| 116 | + ctx.close(); | ||
| 117 | + clientCtx.close(); | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + @Override | ||
| 122 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { | ||
| 123 | + ctx.close(); | ||
| 124 | + clientCtx.close(); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + @Override | ||
| 128 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { | ||
| 129 | + ctx.close(); | ||
| 130 | + clientCtx.close(); | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | +} |
| 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 | +} |
src/main/resources/logback.xml
0 → 100644
| 1 | +<configuration> | ||
| 2 | + | ||
| 3 | + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
| 4 | + <encoder> | ||
| 5 | + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> | ||
| 6 | + </encoder> | ||
| 7 | + </appender> | ||
| 8 | + | ||
| 9 | + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | ||
| 10 | + | ||
| 11 | + <file>logs/info.log</file> | ||
| 12 | + | ||
| 13 | + <encoder charset="utf-8"> | ||
| 14 | + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n | ||
| 15 | + </pattern> | ||
| 16 | + </encoder> | ||
| 17 | + | ||
| 18 | + <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> | ||
| 19 | + <fileNamePattern>logs/info.log.%i.gz</fileNamePattern> | ||
| 20 | + <!-- 最多存留3个文件 --> | ||
| 21 | + <minIndex>1</minIndex> | ||
| 22 | + <maxIndex>3</maxIndex> | ||
| 23 | + </rollingPolicy> | ||
| 24 | + | ||
| 25 | + <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> | ||
| 26 | + <!-- 单文件最大50MB --> | ||
| 27 | + <maxFileSize>50MB</maxFileSize> | ||
| 28 | + </triggeringPolicy> | ||
| 29 | + | ||
| 30 | + </appender> | ||
| 31 | + | ||
| 32 | + <root level="info"> | ||
| 33 | + <appender-ref ref="FILE" /> | ||
| 34 | + <appender-ref ref="STDOUT" /> | ||
| 35 | + </root> | ||
| 36 | + | ||
| 37 | +</configuration> |
src/main/resources/package.xml
0 → 100644
| 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 | +} |
src/test/test.iml
0 → 100644
| 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> |
sslocal.sh
0 → 100644
| 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 | + |
ssserver.sh
0 → 100644
| 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 | + |
-
请 注册 或 登录 后发表评论