├── .github └── workflows │ └── erlang.yml ├── .gitignore ├── .travis.yml ├── Emakefile ├── LICENSE ├── README.md ├── README.zh.md ├── ebin ├── .gitignore └── mimicsocks.app ├── include └── mimicsocks.hrl ├── priv ├── .gitignore └── mimicsocks.cfg ├── src ├── mimicsocks_app.erl ├── mimicsocks_cfg.erl ├── mimicsocks_crypt.erl ├── mimicsocks_inband_recv.erl ├── mimicsocks_inband_send.erl ├── mimicsocks_local.erl ├── mimicsocks_local_agg.erl ├── mimicsocks_mimic.erl ├── mimicsocks_remote.erl ├── mimicsocks_remote_agg.erl ├── mimicsocks_remote_ho.erl ├── mimicsocks_remote_http.erl ├── mimicsocks_remote_relay.erl ├── mimicsocks_remote_socks.erl ├── mimicsocks_sup.erl ├── mimicsocks_tcp_listener.erl ├── mimicsocks_wormhole_local.erl └── mimicsocks_wormhole_remote.erl └── test └── mimicsocks_test.erl /.github/workflows/erlang.yml: -------------------------------------------------------------------------------- 1 | name: Erlang CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | container: 19 | image: erlang:22.0.7 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Compile 24 | run: rebar3 compile 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.eunit 2 | /doc 3 | *.bak 4 | *.bak~ 5 | *.swp -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 20.0 4 | script: erl -make -------------------------------------------------------------------------------- /Emakefile: -------------------------------------------------------------------------------- 1 | {"./src/*", [{i, "./include"}, {outdir, "./ebin"}]}. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2019 foldl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mimicsocks: just another TCP proxy 2 | 3 | [![Build Status](https://travis-ci.org/foldl/mimicsocks.svg?branch=master)](https://travis-ci.org/foldl/mimicsocks) 4 | 5 | Mimicsocks is a reversable TCP forwarder, relay, tunnel or proxy, inspired by Shadowsocks and stimulated by [1]. 6 | 7 | 查看[简体中文版](README.zh.md). 8 | 9 | ## Table of Contents 10 | 11 | * [Overview](#overview) 12 | * [Features](#features) 13 | * [Scenario 1](#scenario-1) 14 | * [Scenario 2](#scenario-2) 15 | * [Get Stated](#get-started) 16 | * [Configuration Exampels](#configuration-examples) 17 | * [Scenario 1](#scenario-1-1) 18 | * [Scenario 2](#scenario-2-1) 19 | * [Inside the Wormhole](#inside-the-wormhole) 20 | 21 | 22 | ## Overview 23 | 24 | Mimicsocks is a wormhole with two ends called local & remote end respectively. 25 | Both ends are specified by an IP address and port. 26 | 27 | Data put into the local end will be transmitted to the remote end 28 | secretly and magically. Once data arrives at the remote end, it is served by 29 | a data handler. Mimicsocks has three handlers: 30 | 31 | * A simple socks4/4a/5 proxy 32 | 33 | With it, one can use mimicsocks just like Shadowsocks. 34 | 35 | Thanks to the modularity, this handler can be used as a standalone socks4/4a/5 proxy: 36 | 37 | `mimicsocks_tcp_listener:start_link([Ip, Port, mimicsocks_remote_socks, [undefined]]).` 38 | 39 | * A simple http proxy 40 | 41 | This simple proxy supports http/https and http tunnel. 42 | 43 | This handler can also be used as a standalone http proxy: 44 | 45 | `mimicsocks_tcp_listener:start_link([Ip, Port, mimicsocks_remote_http, [undefined]]).` 46 | 47 | * A relay 48 | 49 | This relay forwards data to somewhere else specified by an IP address and port. 50 | Data can be forwarded to another mimicsocks to create a chain of proxies. Data can 51 | also be forwarded to your own socks5 or http proxy. 52 | 53 | ### Features 54 | 55 | * Chainable 56 | 57 | Mimicsocks can be connected in series to create a multi-hop proxy or a likely-onion router. 58 | 59 | * Mimic 60 | 61 | Mimicsocks manipluates packages size and delay, and makes baton handover randomly. 62 | 63 | * Simple 64 | 65 | Mimicsocks is written in Erlang/OPT, no third-party dependencies. 66 | 67 | Mimicsocks can be used for different purposes. 68 | 69 | ### Scenario 1 70 | 71 | User programs connect to the local end to access services provided by different handlers. 72 | 73 | When users want extra privacy, or to access contents that are blocked by firewall, this scenario provides a solution. 74 | 75 | ``` 76 | handlers 77 | 78 | Users +--------+ 79 | + +----> http <--> 80 | | | +--------+ 81 | | | 82 | +-----v-----+ wormhole +------------+ | +--------+ 83 | | local <+ + + + + + + + > remote <--------> socks <--> 84 | +-----------+ +------------+ | +--------+ 85 | | 86 | | +--------+ 87 | +----> relay <--> 88 | +--------+ 89 | ``` 90 | 91 | ### Scenario 2 92 | 93 | User programs connect to the remote end to access services provided by different handlers. 94 | 95 | When users want to access contents located in a intranet, this scenario provides a solution. 96 | 97 | WARNING: It should be noted that in this scenario, intranet services are exposed to the 98 | outside without protection. Handlers in the intranet should have proper authentication 99 | mechanism, or chain with another mimicsocks. 100 | 101 | ``` 102 | +---------------------------------------+ 103 | | intranet | 104 | | handlers | 105 | | | 106 | | +--------+ | Users 107 | | <--> http <----+ | + 108 | | +--------+ | | | 109 | | | | | 110 | | +--------+ | +-----------+ | wormhole +-----v------+ 111 | | <--> socks <----------> local <--+ + + + + + + > remote | 112 | | +--------+ | +-----------+ | +------------+ 113 | | | | 114 | | +--------+ | | 115 | | <--> relay <----+ | 116 | | +--------+ | 117 | +---------------------------------------+ 118 | ``` 119 | 120 | ## Get Started 121 | 122 | Take Windows as an example. 123 | 124 | 1. Install Erlang/OTP 20.0 or newer (seriously). 125 | 126 | Suppose it's installed in `C:\Program files\erl9.0` 127 | 128 | 1. Download this package Erlang's lib directory: `C:\Program files\erl9.0\lib`. 129 | 130 | 1. Start werl.exe, and build mimicsocks: 131 | 132 | ```shell 133 | Eshell V9.0 (abort with ^G) 134 | 1> cd("../lib/mimicsocks"). 135 | C:/Program files/erl9.0/lib/mimicsocks 136 | ok 137 | 2> make:all(). 138 | ...... 139 | up_to_date 140 | ``` 141 | 142 | 1. Config mimicsocks. 143 | 144 | See below for exmples. Note that both ends share the same config file. 145 | 146 | Open `C:\Program files\erl9.0\lib\mimicsocks\priv\mimicsocks.cfg` and edit it: 147 | 148 | ```erlang 149 | {default, [ % name of this wormhole 150 | ... 151 | {handler, socks}, % socks, http, or relay (see below) 152 | ... 153 | ] 154 | }. 155 | ``` 156 | 157 | To use the relay handler, one can define another wormhole, then use it: 158 | ```erlang 159 | {default, [ % name of this wormhole 160 | ... 161 | {handler, {relay, another}}, 162 | ... 163 | ] 164 | }. 165 | {another, [ % name of another wormhole 166 | ... 167 | ] 168 | }. 169 | ``` 170 | 171 | Or just relay to another address: 172 | ```erlang 173 | {default, [ % name of this wormhole 174 | ... 175 | {remote_handler, {relay, {Ip, Port}}, 176 | ... 177 | ] 178 | }. 179 | ``` 180 | 181 | 1. Launch mimicsocks: 182 | 183 | On remote & local machine: 184 | ```shell 185 | erl -eval "application:ensure_all_started(mimicsocks)" -noshell -detached 186 | ``` 187 | 188 | For aggregated ones, remote end should be started ahead of local end. 189 | 190 | It's recommended to use Erlang heart (see Issue #): 191 | ```shell 192 | export HEART_COMMAND="erl -eval 'application:ensure_all_started(mimicsocks)' -noshell -detached -heart" 193 | `$HEART_COMMAND` 194 | ``` 195 | 196 | ## Configuration Examples 197 | 198 | ### Scenario 1 199 | 200 | There is a server with IP address S0.S1.S2.S3, we want to use it as a socks proxy. 201 | 202 | We are in a intranet with IP address A0.A1.A2.A3, we want to use port 8888 as the entry point. 203 | 204 | ```erlang 205 | {default, [ % name of this wormhole 206 | {server, {{A0,A1,A2,A3}, 8888}}, % local end address 207 | {wormhole_remote, {{S0,S1,S2,S3}, 9999}}, % remote end address 208 | {wormhole, aggregated}, % can be aggregated (RECOMMENDED) or distributed 209 | {handler, socks}, % socks, http, or relay 210 | {wormhole_extra_ports, [9998]}, % extra ports on remote end for handover 211 | {key, <<...>>} % possible key length: 128, 192, or 256 bits 212 | % use following code to generate a new key: 213 | % io:format("~p~n",[crypto:strong_rand_bytes(256 div 8)]). 214 | ] 215 | }. 216 | ``` 217 | 218 | After mimicsocks is successfully started, set programs' socks proxy to A0.A1.A2.A3:8888. 219 | 220 | Note: port number can be chosen randomly. 221 | 222 | ### Scenario 2 223 | 224 | We have a server with IP address S0.S1.S2.S3 and another Windows box in intranet with address A0.A1.A2.A3. 225 | We need to access Windows remote desktop from outside of this intranet. 226 | 227 | ```erlang 228 | {default, [ % name of this wormhole 229 | {reverse, true}, % reverse proxy 230 | {server, {{S0,S1,S2,S3}, 8888}}, % local end address 231 | {wormhole_remote, {{S0,S1,S2,S3}, 9999}}, % remote end address 232 | {wormhole, aggregated}, % must be aggregated 233 | {handler, {relay, {{A0,A1,A2,A3}, 3389}}}, % relay to remote desktop 234 | {wormhole_extra_ports, [9998]}, % extra ports on remote end for handover 235 | {key, <<...>>} % possible key length: 128, 192, or 256 bits 236 | ] 237 | }. 238 | ``` 239 | 240 | After mimicsocks is successfully started, connect to S0.S1.S2.S3:8888 to access the remote desktop. 241 | 242 | ## Inside the Wormhole 243 | 244 | In each end of this wormhole (a.k.a mimicsocks), there is a list of nodes. 245 | Each node receives data in message format `{recv, From, Data}`, and pass 246 | the processed data to the next node by sending `{recv, From, NewData}` to it. 247 | Generally, if there is a node A in one end, 248 | there will be a node A-1 in the other end to cancel out A's effects. 249 | 250 | There are two types of wormhole, aggregated and distributed. 251 | For a wormhole in distributed mode, when a new socket (call it local socket) 252 | is established on the local end, another socket (call it remote socket) between 253 | local & remote ends is also created. 254 | For a wormhole in aggregated mode, there is only one socket between local & remote ends, 255 | and all traffic are aggregated into this single socket. 256 | 257 | Mimicsocks has following nodes. 258 | 259 | 1. AES encryption/decryption 260 | 261 | 1. inband transceiving 262 | 263 | To support handover, mimicsocks uses these nodes for inband communication between 264 | local & remote ends. 265 | 266 | For a wormhole in distributed mode, handover means during the lifetime of local socket, new socket are 267 | dynamically created to take over the job from elder remote socket. In a tiny time 268 | frame in traffic from local to remote and traffic from remote to local occur in 269 | two separate sockets, so yes, it is baton handover. 270 | 271 | For a wormhole in aggregated mode, handover means during the lifetime of this single socket, 272 | a new socket is dynamically created to take over the job from elder one, and then 273 | the elder one is closed. 274 | 275 | 1. mimic 276 | 277 | This node learns the statistical characteristics of the ongoing traffic, and 278 | then manipluates packages size and delay to make them follow a randomly-choosen 279 | distribution. 280 | 281 | Package size may follow one of these distributions: constant, uniform or Gaussian. 282 | Package delay may follow one of these distributions: constant, uniform, Gaussian or exponential. 283 | 284 | This node does not need a A-1 in the other end. 285 | 286 | ---- 287 | [1] [The Random Forest Based Detection of Shadowsock's Traffic](http://ieeexplore.ieee.org/document/8048116/) 288 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # Mimicsocks: 又一个 TCP 代理 2 | 3 | [![Build Status](https://travis-ci.org/foldl/mimicsocks.svg?branch=master)](https://travis-ci.org/foldl/mimicsocks) 4 | 5 | Mimicsocks 是 TCP 转发器、中继、隧道、代理,在 Shadowsocks 的启发和文献 [1] 的激励下诞生。 6 | 7 | View [English version](README.md). 8 | 9 | ``` 10 | 处理模块 11 | 12 | +--------+ 13 | +----> http <--> 14 | | +--------+ 15 | | 16 | +-----------+ 虫 洞 +------------+ | +--------+ 17 | | 本端 <- - - - - - - - > 远端 <---+----> socks <--> 18 | +-----------+ +------------+ | +--------+ 19 | | 20 | | +--------+ 21 | +----> 中继 <--> 22 | +--------+ 23 | ``` 24 | 25 | ## 特性 26 | 27 | * 可串联 28 | 29 | 多个 Mimicsocks 可以串联在一起构成多跳代理或者类似洋葱路由的玩意儿. 30 | 31 | * 拟态 32 | 33 | Mimicsocks 会调整 TCP 数据包的大小、时延,工作过程中还会随机切换 Socket。 34 | 35 | * 简单 36 | 37 | Mimicsocks 纯用 Erlang/OPT 实现,不依赖其它库。 38 | 39 | ## 总体 40 | 41 | Mimicsocks 就是一个有两个端点(本端和远端)的虫洞。端点由 IP 地址和端口号指定。 42 | 43 | 进入本端的数据会被秘密地传输到远端。数据到达远端后交由数据处理模块处理。目前,Mimicsocks 44 | 有三种处理模块。 45 | 46 | * 简单的 socks4/4a/5 代理服务器 47 | 48 | 用这个东西,Mimicsocks 可以实现跟 Shadowsocks 类似的功能。 49 | 50 | 由于使用了模块化设计,这个 socks4/4a/5 代理服务器可以单独使用: 51 | 52 | `mimicsocks_tcp_listener:start_link([Ip, Port, mimicsocks_remote_socks, [undefined]).` 53 | 54 | 55 | * 简单的 http 代理服务器 56 | 57 | 支持 http/https,支持 http 隧道。 58 | 59 | 这个http 代理服务器也可以单独使用: 60 | 61 | `mimicsocks_tcp_listener:start_link([Ip, Port, mimicsocks_remote_http, [undefined]]).` 62 | 63 | * 中继 64 | 65 | 本中继可以把数据转发到指点的 IP 地址和端口。转发到另一个 Mimicsocks 的本端即可以搭建多级 66 | Mimicsocks。当然,也可用该中继将数据转发到自己的 sock5 或者 HTTP 代理服务器。 67 | 68 | ## 内部实现 69 | 70 | 在虫洞的两端各有一系列处理节点。每个节点通过 `{recv, From, Data}` 消息接收来自 71 | 前级节点的数据,处理后再通过 `{recv, From, NewData}` 消息把数据传递给后级节点。 72 | 一般而言,如果在一端有一个节点 A,那么在另一端就有一个对应的节点 A-1 73 | 来抵消 A 的作用,将数据还原。 74 | 75 | 有两种虫洞,聚合式和分布式。分布式虫洞会为每一个本端出现的连接请求建立一个单独 76 | 的到远端的连接,而聚合式虫洞在两端之间维持一个 TCP 连接,所有的数据 77 | 都聚合到该连接上传输。 78 | 79 | 目前 Mimicsocks 有以下处理节点。 80 | 81 | 1. AES 加解密 82 | 83 | 1. 带内传输 84 | 85 | 为实现切换功能,mimicsocks 通过带内传输实现本端和远端之间的控制信息传递。 86 | 87 | 1. 拟态 88 | 89 | 主动调整虫洞内传输的数据包大小、延迟,改变其统计特性。 90 | 91 | 本节点在对端不需要相应的 A-1 节点. 92 | 93 | ## Get Started 94 | 95 | Take Windows as an example. 96 | 97 | 1. Install Erlang/OTP 20.0 or newer (seriously). 98 | 99 | Suppose it's installed in `C:\Program files\erl9.0` 100 | 101 | 1. Download this package Erlang's lib directory: `C:\Program files\erl9.0\lib`. 102 | 103 | 1. Start werl.exe, and build mimicsocks: 104 | 105 | ```shell 106 | Eshell V9.0 (abort with ^G) 107 | 1> cd("../lib/mimicsocks"). 108 | C:/Program files/erl9.0/lib/mimicsocks 109 | ok 110 | 2> make:all(). 111 | ...... 112 | up_to_date 113 | ``` 114 | 115 | 1. Config mimicsocks. Note that both ends share the same config file. 116 | 117 | Open `C:\Program files\erl9.0\lib\mimicsocks\priv\mimicsocks.cfg` and edit it: 118 | 119 | ```erlang 120 | {default, [ % name of this wormhole 121 | {local, {{127,0,0,1}, 8888}}, % local end address 122 | {remote, {{127,0,0,1}, 9999}}, % remote end address 123 | {wormhole, aggregated}, % can be aggregated (RECOMMENDED) or distributed 124 | {remote_handler, socks}, % socks, http, or relay (see below) 125 | {remote_extra_ports, [9998]}, % extra ports on remote end for handover 126 | {key, <<41,186,113,221,126,106,146,106,246,112,85,183,56,79,159, 127 | 111,44,174,51,120, 240,217,55,13,205,149,176,82,120,6,61,131>>} 128 | % possible key length: 128, 192, or 256 bits 129 | % use following code to generate a new key: 130 | % io:format("~p~n",[crypto:strong_rand_bytes(256 div 8)]). 131 | ] 132 | }. 133 | ``` 134 | 135 | To use the relay handler, one can define another wormhole, then use it: 136 | ```erlang 137 | {default, [ % name of this wormhole 138 | ... 139 | {remote_handler, {relay, another}}, 140 | ... 141 | ] 142 | }. 143 | {another, [ % name of another wormhole 144 | ... 145 | ] 146 | }. 147 | ``` 148 | 149 | Or just relay to another address: 150 | ```erlang 151 | {default, [ % name of this wormhole 152 | ... 153 | {remote_handler, {relay, {Ip, Port}}, 154 | ... 155 | ] 156 | }. 157 | ``` 158 | 159 | 1. Launch mimicsocks: 160 | 161 | On remote & local machine: 162 | ```shell 163 | erl -eval "application:ensure_all_started(mimicsocks)" -noshell -detached 164 | ``` 165 | 166 | For aggregated ones, remote end should be started ahead of local end. 167 | 168 | ---- 169 | [1] [The Random Forest Based Detection of Shadowsock's Traffic](http://ieeexplore.ieee.org/document/8048116/) 170 | -------------------------------------------------------------------------------- /ebin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | !mimicsocks.app -------------------------------------------------------------------------------- /ebin/mimicsocks.app: -------------------------------------------------------------------------------- 1 | {application, mimicsocks, 2 | [{description, "just another TCP proxy"}, 3 | {vsn, "1.0.0"}, 4 | {modules, [mimicsocks_app, mimicsocks_cfg, mimicsocks_crypt, mimicsocks_inband_recv, 5 | mimicsocks_inband_send, mimicsocks_local, mimicsocks_local_agg, 6 | mimicsocks_mimic, mimicsocks_remote, mimicsocks_remote_agg, mimicsocks_remote_ho, 7 | mimicsocks_remote_relay, mimicsocks_remote_socks, mimicsocks_sup, 8 | mimicsocks_tcp_listener]}, 9 | {registered, [mimicsocks_cfg]}, 10 | {applications, [kernel, stdlib, crypto]}, 11 | {mod, {mimicsocks_app,[]}}, 12 | {env, [{log, []}]} 13 | ]}. -------------------------------------------------------------------------------- /include/mimicsocks.hrl: -------------------------------------------------------------------------------- 1 | %% logging 2 | -define(INFO(Format, L), error_logger:info_msg("~p: " ++ Format, [?MODULE | L])). 3 | -define(WARNING(Format, L), error_logger:warning_msg("~p: " ++ Format, [?MODULE | L])). 4 | -define(ERROR(Format, L), error_logger:error_msg("~p: " ++ Format, [?MODULE | L])). 5 | 6 | -define(MIMICSOCKS_HELLO_SIZE, 16). % IVEC 7 | 8 | % all TCP payload are protected by 256 AES stream cipher, including in-band 9 | % transmission, excluding IVEC 10 | 11 | -define(MIMICSOCKS_INBAND_PAYLOAD, 8). 12 | -define(MIMICSOCKS_INBAND_HMAC, 8). 13 | -define(MIMICSOCKS_INBAND_SIZE, (?MIMICSOCKS_INBAND_HMAC + ?MIMICSOCKS_INBAND_PAYLOAD)). 14 | -define(MIMICSOCKS_INBAND_NOP, 0). 15 | -define(MIMICSOCKS_INBAND_HO_L2R, 1). 16 | -define(MIMICSOCKS_INBAND_HO_R2L, 2). 17 | -define(MIMICSOCKS_INBAND_HO_COMPLETE_R2L, 3). 18 | -define(MIMICSOCKS_INBAND_HO_COMPLETE_L2R, 4). 19 | 20 | % mimicsocks use in-band packages for client-server communication: 21 | % in band packge ::= hmac(sha, key, commands, 8) | command0, ... 22 | % where, command0, command1, ... are 0-padded to 32bytes 23 | % 24 | % Command definitions: 25 | % +--------------------------+ 26 | % | 0 | variable length | 27 | % +-----+--------------------+ 28 | % | cmd | parameters | 29 | % +--------------------------+ 30 | 31 | % command 1: hand-over (local -> remote) 32 | % params ::= target port 16-bit-int/big-endian% 33 | % 34 | % command 2: hand-over (remote -> local) 35 | % command 3: hand-over complete (remote -> local) 36 | % command 4: hand-over complete (local -> remote) 37 | % params ::= Null 38 | 39 | % 40 | % a baton hand-over machanism for socket. it can be initiated by local at anytime. 41 | % 42 | % step 1: LOCAL: 43 | % connect to another port on remote (rsock2), and use ID(n) to identify itself; 44 | % start tapping rsock 45 | % 46 | % Generation of ID(n): n is the No. of handover, while No. 0 is the initial connection 47 | % Note: ID(0) is sent to remote immediately after IVec used for authentication 48 | % 49 | % ID(0) = hmac(IVec, 16) 50 | % ID(n) = hmac(ID(n-1), 16), n >= 1 51 | % 52 | % step 2: REMOTE: 53 | % after rsock2 is accepted, remote starts tapping rsock, 54 | % send HO-R2L on rsock & R2L traffic is switched to rsock2 55 | % 56 | % step 3: LOCAL: 57 | % once HO-R2L is received on rsock, switch R2L traffic to rsock2, and, 58 | % send HO-COMPLETE-L2R on sock & L2R traffic is switched to rsock2 59 | % 60 | % step 4: REMOTE: 61 | % received HO-COMPLETE-L2R is received, switch L2R traffic to rsock2 and 62 | % close rsock, HO completed. 63 | % 64 | 65 | %% defintions for aggregated transmission 66 | %doc NOP command 67 | % <> 68 | -define(AGG_CMD_NOP, 0). 69 | %doc New socket 70 | % <> 71 | -define(AGG_CMD_NEW_SOCKET, 1). 72 | %doc Close socket 73 | % <> 74 | -define(AGG_CMD_CLOSE_SOCKET, 2). 75 | %doc DATA 76 | % <> 77 | -define(AGG_CMD_DATA, 3). 78 | %doc small DATA 79 | % <> 80 | -define(AGG_CMD_SMALL_DATA, 4). 81 | 82 | -------------------------------------------------------------------------------- /priv/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | !mimicsocks.cfg -------------------------------------------------------------------------------- /priv/mimicsocks.cfg: -------------------------------------------------------------------------------- 1 | {default, % name of this wormhole 2 | [ 3 | {server, {{127,0,0,1}, 8888}}, % local end address 4 | {wormhole_remote, {{127,0,0,1}, 9999}}, % remote end address 5 | {wormhole, aggregated}, % can be aggregated or distributed 6 | {handler, socks5}, % handler 7 | {wormhole_extra_ports, [9998]}, % extra ports on remote end for handover 8 | % possible key length: 128, 192, or 256 bits 9 | % use following code to generate a new key: 10 | % io:format("~p~n",[crypto:strong_rand_bytes(256 div 8)]). 11 | {key, <<1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1>>} 12 | ] 13 | }. 14 | -------------------------------------------------------------------------------- /src/mimicsocks_app.erl: -------------------------------------------------------------------------------- 1 | %@doc a application 2 | %@author foldl 3 | -module(mimicsocks_app). 4 | 5 | -behaviour(application). 6 | 7 | -export([start/2, stop/1]). 8 | 9 | start(_Type, _Args) -> 10 | mimicsocks_sup:start_link(). 11 | 12 | stop(_State) -> 13 | ok. -------------------------------------------------------------------------------- /src/mimicsocks_cfg.erl: -------------------------------------------------------------------------------- 1 | %@doc config server 2 | %@author foldl 3 | -module(mimicsocks_cfg). 4 | 5 | -include("mimicsocks.hrl"). 6 | 7 | -behaviour(gen_server). 8 | 9 | -export([start_link/1, stop/0, get_value/2, get_server/1, list_servers/0, get_all/0]). 10 | -export([register_remote/2, get_remote/1, deregister_remote/1]). 11 | 12 | % gen-server callback functions 13 | -export([init/1, terminate/2, 14 | handle_call/3, handle_cast/2, handle_info/2, code_change/3]). 15 | 16 | -record(state, 17 | { 18 | cfg, 19 | table 20 | }). 21 | 22 | start_link(Config) when is_atom(Config) -> 23 | CfgFile = filename:join(code:priv_dir(mimicsocks), atom_to_list(Config) ++ ".cfg"), 24 | gen_server:start_link({local, ?MODULE}, ?MODULE, [CfgFile], []). 25 | 26 | stop() -> gen_server:stop(?MODULE). 27 | 28 | % api 29 | get_all() -> gen_server:call(?MODULE, get_all). 30 | list_servers() -> gen_server:call(?MODULE, list_servers). 31 | get_server(Server) -> gen_server:call(?MODULE, {get_server, Server}). 32 | get_value(Server, Key) -> gen_server:call(?MODULE, {get_value, Server, Key}). 33 | register_remote(ID, Pid) -> gen_server:call(?MODULE, {register_remote, ID, Pid}). 34 | get_remote(ID) -> gen_server:call(?MODULE, {get_remote, ID}). 35 | deregister_remote(ID) -> gen_server:call(?MODULE, {deregister_remote, ID}). 36 | 37 | % callback functions 38 | init([CfgFile]) -> 39 | {ok, Cfg} = file:consult(CfgFile), 40 | Tid = ets:new(table, []), 41 | {ok, #state{cfg = Cfg, table = Tid}}. 42 | 43 | handle_call(get_all, _From, #state{cfg = Cfg} = State) -> 44 | {reply, Cfg, State}; 45 | handle_call(list_servers, _From, #state{cfg = Cfg} = State) -> 46 | S = [element(1, X) || X <- Cfg], 47 | {reply, S, State}; 48 | handle_call({get_server, Server}, _From, #state{cfg = Cfg} = State) -> 49 | S = proplists:get_value(Server, Cfg), 50 | {reply, S, State}; 51 | handle_call({get_value, Server, Key}, _From, #state{cfg = Cfg} = State) -> 52 | R = case proplists:get_value(Server, Cfg) of 53 | L when is_list(L) -> proplists:get_value(Key, L); 54 | undefined -> undefined 55 | end, 56 | {reply, R, State}; 57 | handle_call({register_remote, ID, Pid}, _From, #state{table = Tid} = State) -> 58 | ets:insert(Tid, {ID, Pid}), 59 | {reply, ok, State}; 60 | handle_call({deregister_remote, ID}, _From, #state{table = Tid} = State) -> 61 | ets:match_delete(Tid, {ID, '_'}), 62 | {reply, ok, State}; 63 | handle_call({get_remote, ID}, _From, #state{table = Tid} = State) -> 64 | case ets:lookup(Tid, ID) of 65 | [{ID, Pid}] -> {reply, {ok, Pid}, State}; 66 | _ -> {reply, undefined, State} 67 | end. 68 | 69 | handle_cast(Request, State) -> 70 | ?ERROR("unexpected cast msg: ~p", [Request]), 71 | {noreply, State}. 72 | 73 | handle_info(Info, State) -> 74 | ?ERROR("unexpected info msg: ~p", [Info]), 75 | {noreply, State}. 76 | 77 | terminate(_Reason, #state{table = Tid} = _State) -> 78 | ets:delete(Tid), 79 | normal. 80 | 81 | code_change(_OldVsn, State, _Extra) -> {ok, State}. -------------------------------------------------------------------------------- /src/mimicsocks_crypt.erl: -------------------------------------------------------------------------------- 1 | %doc en/decrypt module 2 | %author foldl 3 | -module(mimicsocks_crypt). 4 | 5 | -export([start_link/2, stop/1, recv/2, hmac_sha/3, init_aes_ctr_enc/2, init_aes_ctr_dec/2]). 6 | 7 | -include("mimicsocks.hrl"). 8 | 9 | -record(state, { 10 | output, 11 | cipher 12 | }). 13 | 14 | start_link(encrypt, [Output, CipherState]) -> 15 | spawn_link(fun() -> loop_encrypt(#state{output = Output, cipher = CipherState}) end); 16 | start_link(decrypt, [Output, CipherState]) -> 17 | spawn_link(fun() -> loop_decrypt(#state{output = Output, cipher = CipherState}) end). 18 | 19 | stop(Pid) -> Pid ! stop. 20 | 21 | recv(Pid, Data) -> Pid ! {recv, self(), Data}. 22 | 23 | loop_encrypt(#state{output = Output, cipher = CipherState} = State) -> 24 | receive 25 | {recv, _From, Data} -> 26 | {NewCipherState, CipherText} = stream_encrypt(CipherState, Data), 27 | Output ! {recv, self(), CipherText}, 28 | loop_encrypt(State#state{cipher = NewCipherState}); 29 | Msg -> 30 | handle_msg(fun loop_encrypt/1, Msg, State) 31 | end. 32 | 33 | loop_decrypt(#state{output = Output, cipher = CipherState} = State) -> 34 | receive 35 | {recv, _From, Data} -> 36 | {NewCipherState, CipherText} = stream_decrypt(CipherState, Data), 37 | Output ! {recv, self(), CipherText}, 38 | loop_decrypt(State#state{cipher = NewCipherState}); 39 | Msg -> 40 | handle_msg(fun loop_decrypt/1, Msg, State) 41 | end. 42 | 43 | handle_msg(Loop, {flush, Ref, _From}, #state{output = Output} = State) -> 44 | Output ! {flush, Ref, self()}, 45 | Loop(State); 46 | handle_msg(_Loop, stop, #state{output = Output} = _State) -> Output ! stop; 47 | handle_msg(Loop, Msg, State) -> 48 | ?WARNING("unexpected msg: ~p", [Msg]), 49 | Loop(State). 50 | 51 | -ifndef(OTP_RELEASE). 52 | -define(OTP_RELEASE, 20). 53 | -endif. 54 | 55 | -if(?OTP_RELEASE >= 24). 56 | init_aes_ctr_enc(Key, IVec) -> crypto:crypto_init(aes_256_ctr, Key, IVec, true). 57 | init_aes_ctr_dec(Key, IVec) -> crypto:crypto_init(aes_256_ctr, Key, IVec, false). 58 | 59 | stream_encrypt(CipherState, Data) -> {CipherState, crypto:crypto_update(CipherState, Data)}. 60 | stream_decrypt(CipherState, Data) -> {CipherState, crypto:crypto_update(CipherState, Data)}. 61 | 62 | hmac_sha(Key, Data, MacLength) -> crypto:macN(hmac, sha, Key, Data, MacLength). 63 | -else. 64 | init_aes_ctr_enc(Key, IVec) -> crypto:stream_init(aes_ctr, Key, IVec). 65 | init_aes_ctr_dec(Key, IVec) -> crypto:stream_init(aes_ctr, Key, IVec). 66 | 67 | stream_encrypt(CipherState, Data) -> crypto:stream_encrypt(CipherState, Data). 68 | stream_decrypt(CipherState, Data) -> crypto:stream_decrypt(CipherState, Data). 69 | 70 | hmac_sha(Key, Data, MacLength) -> crypto:hmac(sha, Key, Data, MacLength). 71 | -endif. 72 | -------------------------------------------------------------------------------- /src/mimicsocks_inband_recv.erl: -------------------------------------------------------------------------------- 1 | %@doc inband transmission module 2 | %@author foldl 3 | -module(mimicsocks_inband_recv). 4 | 5 | -export([start_link/1, stop/1, recv/2, set_key/2, tapping/2]). 6 | -export([subtract/2]). 7 | 8 | -include("mimicsocks.hrl"). 9 | 10 | -record(state, { 11 | output, 12 | cmd_handler, 13 | key, 14 | tapping = false, 15 | buff = <<>> 16 | }). 17 | 18 | start_link([Output, Handler]) -> 19 | spawn_link(fun() -> loop(#state{output = Output, cmd_handler = Handler}) end). 20 | 21 | stop(Pid) -> Pid ! stop. 22 | 23 | recv(Pid, Data) -> Pid ! {recv, self(), Data}. 24 | set_key(Pid, Key) -> Pid ! {set_key, Key}. 25 | tapping(Pid, Flag) -> Pid ! {tapping, Flag}. 26 | 27 | search_cmds(Start, All, #state{output = Output, cmd_handler = Handler, 28 | key = Key} = State) -> 29 | case All of 30 | <> -> 32 | case mimicsocks_crypt:hmac_sha(Key, Cmds, ?MIMICSOCKS_INBAND_HMAC) of 33 | HMAC -> 34 | Output ! {recv, self(), Head}, 35 | Handler ! {inband_cmd, self(), Cmds}, 36 | search_cmds(0, Rem, State); 37 | _ -> 38 | search_cmds(Start + 1, All, State) 39 | end; 40 | _ -> 41 | NextBuff = case Start > 0 of 42 | true -> 43 | <> = All, 44 | Output ! {recv, self(), Msg}, 45 | Rem10; 46 | _ -> All 47 | end, 48 | State#state{buff = NextBuff} 49 | end. 50 | 51 | loop_data(Data, State) when is_list(Data) -> 52 | lists:foldl(fun (Bin, AState) -> loop_data(AState, Bin) end, State, Data); 53 | loop_data(Data, #state{buff = Buff, tapping = true} = State) -> 54 | search_cmds(0, <>, State); 55 | loop_data(Data, #state{output = Output, tapping = false} = State) -> 56 | Output ! {recv, self(), Data}, 57 | State. 58 | 59 | loop(State) -> 60 | receive 61 | {tapping, true} -> 62 | loop(State#state{tapping = true}); 63 | {tapping, false} -> 64 | State#state.output ! {recv, self(), State#state.buff}, 65 | loop(State#state{tapping = false, buff = <<>>}); 66 | {recv, _From, Data} -> 67 | loop(loop_data(Data, State)); 68 | {set_key, Key} -> 69 | loop(State#state{key = Key}); 70 | {flush, Ref, _From} -> 71 | State#state.output ! {flush, Ref, self()}, 72 | loop(State); 73 | stop -> State#state.output ! stop; 74 | X -> 75 | ?WARNING("unexpected msg: ~p", [X]), 76 | loop(State) 77 | end. 78 | 79 | subtract(X, Y) when is_integer(X) -> X - Y; 80 | subtract(_X, _Y) -> infinity. 81 | -------------------------------------------------------------------------------- /src/mimicsocks_inband_send.erl: -------------------------------------------------------------------------------- 1 | %@doc inband transmission module 2 | %@author foldl 3 | -module(mimicsocks_inband_send). 4 | 5 | -export([start_link/1, stop/1, recv/2, set_key/2, 6 | recv_cmd/2, recv_cmd/3, get_buff/1, continue/1]). 7 | 8 | -include("mimicsocks.hrl"). 9 | 10 | -import(mimicsocks_inband_recv, [subtract/2]). 11 | 12 | -record(state, { 13 | output, 14 | cmd_handler, 15 | key, 16 | after_cmd, % continue or hold_and_flush 17 | counter = 0, 18 | buff 19 | }). 20 | 21 | start_link([Output, Handler]) -> 22 | spawn_link(fun() -> loop(#state{output = Output, cmd_handler = Handler}) end). 23 | 24 | stop(Pid) -> Pid ! stop. 25 | 26 | recv(Pid, Data) -> Pid ! {recv, self(), Data}. 27 | 28 | set_key(Pid, Key) -> Pid ! {set_key, Key}. 29 | recv_cmd(Pid, Cmd) -> recv_cmd(Pid, Cmd, continue). 30 | continue(Pid) -> Pid ! continue. 31 | 32 | get_buff(Pid) -> 33 | This = self(), 34 | Ref = make_ref(), 35 | Pid ! {get_buff, This, Ref}, 36 | receive 37 | {get_buff, Ref, Data} -> Data 38 | end. 39 | 40 | recv_cmd(Pid, Cmd, AfterCmd) -> 41 | if size(Cmd) =< ?MIMICSOCKS_INBAND_PAYLOAD -> ok; 42 | true -> throw({size_too_large, Cmd}) 43 | end, 44 | Padding = list_to_binary(lists:duplicate(?MIMICSOCKS_INBAND_PAYLOAD - size(Cmd), 0)), 45 | Ref = make_ref(), 46 | Pid ! {send_cmd, Ref, <>, AfterCmd}, 47 | Ref. 48 | 49 | loop_data(State, Data) when is_list(Data) -> 50 | lists:foldl(fun (Bin, AState) -> loop_data(AState, Bin) end, State, Data); 51 | loop_data(#state{buff = L} = State, Data) when is_list(L) -> 52 | State#state{buff = [Data | L]}; 53 | loop_data(#state{output = Output} = State, Data) -> 54 | Output ! {recv, self(), Data}, 55 | State. 56 | 57 | loop(#state{output = Output} = State) -> 58 | receive 59 | continue -> 60 | case State#state.buff of 61 | L when is_list(L) -> 62 | loop_data(State, lists:reverse(L)); 63 | _ -> ok 64 | end, 65 | loop(State#state{buff = undefined}); 66 | {recv, _From, Data} -> 67 | loop(loop_data(State, Data)); 68 | {set_key, Key} -> 69 | loop(State#state{key = Key}); 70 | {send_cmd, Ref, Cmd, After} -> 71 | Key = State#state.key, 72 | HMAC = mimicsocks_crypt:hmac_sha(Key, Cmd, ?MIMICSOCKS_INBAND_HMAC), 73 | Output ! {recv, self(), <>}, 74 | NewState = case After of 75 | continue -> State; 76 | hold -> State#state{buff = []} 77 | end, 78 | State#state.cmd_handler ! {cmd_sent, Ref}, 79 | loop(NewState); 80 | {get_buff, Pid, Ref} -> 81 | L10 = case State#state.buff of 82 | L when is_list(L) -> lists:reverse(L); 83 | _ -> [] 84 | end, 85 | Pid ! {get_buff, Ref, L10}, 86 | loop(State); 87 | {flush, Ref, _From} -> 88 | State#state.output ! {flush, Ref, self()}, 89 | loop(State); 90 | stop -> State#state.output ! stop; 91 | X -> 92 | ?WARNING("unexpected msg: ~p", [X]), 93 | loop(State) 94 | end. 95 | -------------------------------------------------------------------------------- /src/mimicsocks_local.erl: -------------------------------------------------------------------------------- 1 | %@doc local socket forwarding node 2 | % local traffic is forwarded and handled by remote node 3 | %@author foldl 4 | -module(mimicsocks_local). 5 | 6 | -include("mimicsocks.hrl"). 7 | 8 | -behaviour(gen_statem). 9 | 10 | % api 11 | -export([start_link/1, socket_ready/2]). 12 | 13 | % callbacks 14 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 15 | 16 | % utils 17 | -import(mimicsocks_mimic, [choice/1]). 18 | 19 | % FSM States 20 | -export([ 21 | init/3, 22 | forward/3 23 | ]). 24 | 25 | -record(state, 26 | { 27 | channel, 28 | lsock 29 | } 30 | ). 31 | 32 | -define(REMOTE_TCP_OPTS, [{active, true}, {packet, raw}, binary, {reuseaddr, true}]). 33 | 34 | start_link(Args) -> 35 | gen_statem:start_link(?MODULE, Args, []). 36 | 37 | socket_ready(Pid, LSock) when is_pid(Pid), is_port(LSock) -> 38 | gen_tcp:controlling_process(LSock, Pid), 39 | gen_statem:cast(Pid, {socket_ready, LSock}). 40 | 41 | %%%=================================================================== 42 | %%% gen_fsm callbacks 43 | %%%=================================================================== 44 | init(Args) -> 45 | case mimicsocks_wormhole_local:start_link([self() | Args]) of 46 | {ok, Channel} -> 47 | {ok, init, #state{channel = Channel}}; 48 | {error, Reason} -> {stop, Reason} 49 | end. 50 | 51 | callback_mode() -> 52 | state_functions. 53 | 54 | init(cast, {socket_ready, Socket}, StateData) when is_port(Socket) -> 55 | ok = inet:setopts(Socket, [{active, true}, {packet, raw}, binary]), 56 | {next_state, forward, StateData#state{lsock = Socket}}; 57 | init(info, Msg, Data) -> handle_info(Msg, init, Data). 58 | 59 | forward(info, Msg, Data) -> handle_info(Msg, forward, Data). 60 | 61 | handle_info({recv, Channel, Data}, _StateName, #state{lsock = Socket, channel = Channel} = StateData) -> 62 | gen_tcp:send(Socket, Data), 63 | {keep_state, StateData}; 64 | handle_info({tcp, Socket, Bin}, _StateName, #state{lsock = Socket, channel = Channel} = StateData) -> 65 | mimicsocks_wormhole_local:recv(Channel, Bin), 66 | {keep_state, StateData}; 67 | handle_info({tcp_closed, Socket}, _StateName, #state{lsock = Socket} = StateData) -> 68 | {stop, normal, StateData}; 69 | handle_info(Info, StateName, StateData) -> 70 | ?ERROR("unexpected ~p in state ~p", [Info, StateName]), 71 | {keep_state, StateData}. 72 | 73 | terminate(_Reason, _StateName, #state{lsock=LSocket, 74 | channel = Channel} = _StateData) -> 75 | (catch gen_tcp:close(LSocket)), 76 | (catch mimicsocks_wormhole_local:stop(Channel)), 77 | ok. 78 | 79 | code_change(_OldVsn, StateName, State, _Extra) -> 80 | {ok, StateName, State}. -------------------------------------------------------------------------------- /src/mimicsocks_local_agg.erl: -------------------------------------------------------------------------------- 1 | %doc aggregated communication 2 | %author foldl 3 | -module(mimicsocks_local_agg). 4 | 5 | -include("mimicsocks.hrl"). 6 | 7 | -behaviour(gen_statem). 8 | 9 | %% API 10 | -export([start_link/1, stop/1, accept/2, accept2/2, socket_ready/2]). 11 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 12 | 13 | -import(mimicsocks_wormhole_local, [show_sock/1]). 14 | 15 | % FSM States 16 | -export([ 17 | init/3, 18 | forward/3 19 | ]). 20 | 21 | % utils 22 | -export([parse_full/2, send_data/3]). 23 | 24 | start_link(Args) -> 25 | gen_statem:start_link(?MODULE, Args, []). 26 | 27 | accept(Pid, Socket) -> 28 | ok = gen_tcp:controlling_process(Socket, Pid), 29 | Pid ! {accept, Socket}. 30 | 31 | accept2(Pid, Socket) -> 32 | ok = gen_tcp:controlling_process(Socket, Pid), 33 | Pid ! {accept2, Socket}. 34 | 35 | socket_ready(Pid, LSock) when is_pid(Pid), is_port(LSock) -> 36 | gen_tcp:controlling_process(LSock, Pid), 37 | gen_statem:cast(Pid, {socket_ready, LSock}). 38 | 39 | stop(Pid) -> gen_statem:stop(Pid). 40 | 41 | callback_mode() -> 42 | state_functions. 43 | 44 | -record(state, 45 | { 46 | channel, 47 | wormhole, 48 | t_s2i, 49 | t_i2s, 50 | last_id = -1, 51 | key, 52 | last_timer = undefined, 53 | 54 | buf = <<>> 55 | } 56 | ). 57 | 58 | %% callback funcitons 59 | init([Key]) -> 60 | case init_wormhole_remote(Key) of 61 | {ok, Channel} -> 62 | {ok, init, #state{wormhole = mimicsocks_wormhole_remote, 63 | t_i2s = ets:new(tablei2s, []), 64 | t_s2i = ets:new(tables2i, []), 65 | key = Key, 66 | channel = Channel}}; 67 | {error, Reason} -> {stop, Reason} 68 | end; 69 | init(Args) -> 70 | case mimicsocks_wormhole_local:start_link([self() | Args]) of 71 | {ok, Channel} -> 72 | {ok, init, #state{channel = Channel, 73 | wormhole = mimicsocks_wormhole_local, 74 | t_i2s = ets:new(tablei2s, []), 75 | t_s2i = ets:new(tables2i, []) 76 | }}; 77 | {error, Reason} -> {stop, Reason} 78 | end. 79 | 80 | init(info, {accept2, Socket}, #state{channel = Channel} = State) -> 81 | mimicsocks_wormhole_remote:socket_ready(Channel, Socket), 82 | {next_state, forward, State}; 83 | init(info, Msg, StateData) -> handle_info(Msg, init, StateData). 84 | 85 | forward(info, Info, State) -> handle_info(Info, forward, State). 86 | 87 | % here, we use monitor but not fail-restart, because on some VPS, 88 | % gen_tcp:listen fails during restart. 89 | handle_info({'DOWN', _Ref, process, Channel, _Reason}, _StateName, 90 | #state{channel = Channel, key = Key} = State) -> 91 | % clean-up 92 | clean_up(State), 93 | 94 | % re-initialize 95 | case init_wormhole_remote(Key) of 96 | {ok, NewChannel} -> 97 | {next_state, init, State#state{channel = NewChannel, buf = <<>>}}; 98 | {error, Reason} -> {stop, Reason} 99 | end; 100 | handle_info({accept2, Socket}, _StateName, #state{key = Key, 101 | wormhole = mimicsocks_wormhole_remote} = State) -> 102 | % clean-up 103 | clean_up(State), 104 | 105 | % re-initialize 106 | case init_wormhole_remote(Key) of 107 | {ok, NewChannel} -> 108 | mimicsocks_wormhole_remote:socket_ready(NewChannel, Socket), 109 | {next_state, forward, State#state{channel = NewChannel, buf = <<>>}}; 110 | {error, Reason} -> {stop, Reason} 111 | end; 112 | handle_info({accept, Socket}, _StateName, #state{t_i2s = Ti2s, t_s2i = Ts2i, 113 | channel = Channel, wormhole = W, 114 | last_id = N, 115 | last_timer = LastTimer} = State) -> 116 | State20 = case inet:setopts(Socket, [{active, true}]) of 117 | ok -> 118 | Port = make_id(Ti2s, N, N), 119 | ets:insert(Ti2s, {Port, Socket}), 120 | ets:insert(Ts2i, {Socket, Port}), 121 | W:suspend_mimic(Channel, 5000), 122 | W:recv(Channel, <>), 123 | State#state{last_id = Port, last_timer = update_ho_timer(LastTimer)}; 124 | _Error -> 125 | gen_tcp:close(Socket), 126 | State 127 | end, 128 | {keep_state, State20}; 129 | handle_info({tcp, Socket, Bin}, _StateName, #state{t_s2i = Ts2i, channel = Channel} = State) -> 130 | case ets:lookup(Ts2i, Socket) of 131 | [{Socket, Id}] -> 132 | send_data(Channel, Id, Bin); 133 | _ -> ?ERROR("socket (~p) not in db", [show_sock(Socket)]) 134 | end, 135 | {keep_state, State}; 136 | handle_info({tcp_closed, Socket}, _StateName, #state{t_s2i = Ts2i, t_i2s = Ti2s, channel = Channel} = State) -> 137 | case ets:lookup(Ts2i, Socket) of 138 | [{Socket, ID}] -> 139 | Channel ! {recv, self(), <>}, 140 | ets:match_delete(Ts2i, {Socket, '_'}), 141 | ets:match_delete(Ti2s, {ID, '_'}); 142 | _ -> ok 143 | end, 144 | {keep_state, State}; 145 | handle_info({recv, Channel, Bin}, _StateName, #state{channel = Channel, buf = Buf} = State) -> 146 | All = <>, 147 | {ok, Frames, Rem} = parse_full(All, []), 148 | State10 = lists:foldl(fun handle_cmd/2, State, Frames), 149 | NewState = State10#state{buf = Rem}, 150 | {keep_state, NewState}; 151 | handle_info({tcp_error, Socket, _Reason}, _StateName, #state{t_s2i = Ts2i, t_i2s = Ti2s, channel = Channel} = State) -> 152 | case ets:lookup(Ts2i, Socket) of 153 | [{Socket, ID}] -> 154 | Channel ! {recv, self(), <>}, 155 | ets:match_delete(Ts2i, {Socket, '_'}), 156 | ets:match_delete(Ti2s, {ID, '_'}); 157 | _ -> ok 158 | end, 159 | {keep_state, State}; 160 | handle_info(ho_timer, _StateName, #state{channel = Channel, wormhole = W} = State) -> 161 | W:handover_now(Channel), 162 | {keep_state, State#state{last_timer = undefined}}; 163 | handle_info(stop, _StateName, State) -> 164 | {stop, normal, State}; 165 | handle_info(Info, _StateName, State) -> 166 | ?WARNING("unexpected msg: ~p", [Info]), 167 | {keep_state, State}. 168 | 169 | terminate(_Reason, _StateName, #state{channel = Channel, t_i2s = T1, t_s2i = T2, wormhole = W} = 170 | _State) -> 171 | (catch W:stop(Channel)), 172 | (catch ets:delete(T1)), 173 | (catch ets:delete(T2)), 174 | normal. 175 | 176 | code_change(_OldVsn, OldStateName, OldStateData, _Extra) -> 177 | {ok, OldStateName, OldStateData}. 178 | 179 | %helpers 180 | clean_up(#state{t_i2s = Ti2s, t_s2i = Ts2i, 181 | channel = Channel, 182 | wormhole = mimicsocks_wormhole_remote} = _State) -> 183 | (catch mimicsocks_wormhole_remote:stop(Channel)), 184 | (catch ets:delete_all_objects(Ti2s)), 185 | (catch ets:delete_all_objects(Ts2i)). 186 | 187 | init_wormhole_remote(Key) -> 188 | case mimicsocks_wormhole_remote:start_link([self(), Key]) of 189 | {ok, Channel} -> 190 | unlink(Channel), 191 | erlang:monitor(process, Channel), 192 | {ok, Channel}; 193 | {error, Reason} -> {stop, Reason} 194 | end. 195 | 196 | send_data(Send, Id, List) when is_list(List) -> 197 | lists:foreach(fun (X) -> send_data(Send, Id, X) end, List); 198 | send_data(_Send, _Id, <<>>) -> ok; 199 | send_data(Send, Id, Bin) when size(Bin) < 256 -> 200 | Len = size(Bin), 201 | Send ! {recv, self(), <>}; 202 | send_data(Send, Id, Bin) when size(Bin) < 65536 -> 203 | Len = size(Bin), 204 | Send ! {recv, self(), <>}; 205 | send_data(Send, Id, Bin) -> 206 | <> = Bin, 207 | Send ! {recv, self(), <>}, 208 | send_data(Send, Id, Rem). 209 | 210 | parse_data(<<>>) -> incomplete; 211 | parse_data(<>) -> 212 | case Rem of 213 | <<_Reserved:16/big, Len, _Dummy:Len/binary, Rem10/binary>> -> 214 | {?AGG_CMD_NOP, Rem10}; 215 | _ -> incomplete 216 | end; 217 | parse_data(<>) -> 218 | case Rem of 219 | <> -> 220 | {{?AGG_CMD_NEW_SOCKET, Id}, Rem10}; 221 | _ -> incomplete 222 | end; 223 | parse_data(<>) -> 224 | case Rem of 225 | <> -> 226 | {{?AGG_CMD_CLOSE_SOCKET, Id}, Rem10}; 227 | _ -> incomplete 228 | end; 229 | parse_data(<>) -> 230 | case Rem of 231 | <> -> 232 | {{?AGG_CMD_DATA, Id, Data}, Rem10}; 233 | _ -> incomplete 234 | end; 235 | parse_data(<>) -> 236 | case Rem of 237 | <> -> 238 | {{?AGG_CMD_DATA, Id, Data}, Rem10}; 239 | _ -> incomplete 240 | end; 241 | parse_data(<>) -> 242 | {bad_command, Cmd}. 243 | 244 | parse_full(Data, Acc) -> 245 | case parse_data(Data) of 246 | incomplete -> {ok, lists:reverse(Acc), Data}; 247 | {bad_command, Value} -> {{bad_command, Value}, Acc, Data}; 248 | {Frame, Rem} -> parse_full(Rem, [Frame | Acc]) 249 | end. 250 | 251 | handle_cmd(?AGG_CMD_NOP, State) -> State; 252 | handle_cmd({?AGG_CMD_NEW_SOCKET, _Id}, State) -> 253 | ?ERROR("unexpected ?AGG_CMD_NEW_SOCKET", []), 254 | State; 255 | handle_cmd({?AGG_CMD_CLOSE_SOCKET, Id}, #state{t_s2i = Ts2i, t_i2s = Ti2s} = State) -> 256 | case ets:lookup(Ti2s, Id) of 257 | [{ID, Socket}] -> 258 | ets:match_delete(Ts2i, {Socket, '_'}), 259 | ets:match_delete(Ti2s, {ID, '_'}); 260 | _ -> ok 261 | end, 262 | State; 263 | handle_cmd({?AGG_CMD_DATA, Id, Data}, #state{t_i2s = Ti2s, last_timer = LastTimer} = State) -> 264 | case ets:lookup(Ti2s, Id) of 265 | [{Id, Socket}] -> 266 | gen_tcp:send(Socket, Data); 267 | _ -> ok 268 | end, 269 | State#state{last_timer = cancel_timer(LastTimer)}. 270 | 271 | make_id(Ti2s, N1, N0) -> 272 | N2 = (N1 + 1) rem 65536, 273 | true = (N2 =/= N0), 274 | case ets:lookup(Ti2s, N2) of 275 | [{_ID, _Socket}] -> 276 | make_id(Ti2s, N2, N0); 277 | _ -> 278 | N2 279 | end. 280 | 281 | update_ho_timer(undefined) -> 282 | {ok, TRef} = timer:send_after((rand:uniform(10) + 10) * 1000, ho_timer), 283 | TRef; 284 | update_ho_timer(Old) -> 285 | cancel_timer(Old), 286 | update_ho_timer(undefined). 287 | 288 | cancel_timer(undefined) -> undefined; 289 | cancel_timer(Old) -> 290 | timer:cancel(Old), 291 | undefined. -------------------------------------------------------------------------------- /src/mimicsocks_mimic.erl: -------------------------------------------------------------------------------- 1 | %@doc change the stat. characters of tcp packages 2 | %@author foldl 3 | -module(mimicsocks_mimic). 4 | 5 | -behaviour(gen_server). 6 | 7 | %% API 8 | -export([start_link/1, stop/1, recv/2, flush/1, change/1, change/2, suspend/2]). 9 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 10 | 11 | %helpers 12 | -export([choice/1]). 13 | 14 | -include("mimicsocks.hrl"). 15 | 16 | -define(EPSILON, 1/1000000.0). 17 | 18 | -record(state, 19 | { 20 | output, 21 | size_dist, % identity, constant, uniform, gaussian 22 | delay_dist, % identity, constant, uniform, gaussian, poission 23 | queue = queue:new(), 24 | total, 25 | create_t, 26 | last_recv_t, 27 | last_send_t, 28 | size_esti, % welford or iir 29 | delay_esti, 30 | suspended = false 31 | }). 32 | 33 | %@doc online mean & variance estimator 34 | -record(param_esti, 35 | { 36 | algo, 37 | state 38 | }). 39 | 40 | stop(Pid) -> gen_server:call(Pid, stop). 41 | 42 | flush(Pid) -> Pid ! flush. 43 | 44 | suspend(Pid, MilliSec) -> Pid ! {suspend, MilliSec}. 45 | 46 | start_link(Args) -> gen_server:start_link(?MODULE, Args, []). 47 | 48 | recv(Pid, Data) when is_binary(Data) -> 49 | Pid ! {recv, self(), Data}; 50 | recv(Pid, Data) when is_list(Data) -> 51 | [recv(Pid, X) || X <- Data]. 52 | 53 | change(Pid) -> change(Pid, {rand_size_model(), rand_delay_model()}). 54 | 55 | change(Pid, {SizeModel, DelayModel}) -> Pid ! {change, SizeModel, DelayModel}. 56 | 57 | %% callback funcitons 58 | init([Output]) -> 59 | init([Output, rand_size_model(), rand_delay_model(), iir]); 60 | init([Output, SizeModel, DelayModel, Estimator]) -> 61 | T = cur_tick(), 62 | {ok, #state{ 63 | output = Output, 64 | size_dist = SizeModel, 65 | delay_dist = DelayModel, 66 | queue = queue:new(), 67 | total = 0, 68 | create_t = T, 69 | last_recv_t = T, 70 | last_send_t = T, 71 | size_esti = esti_init(Estimator), 72 | delay_esti = esti_init(Estimator), 73 | suspended = false 74 | }}. 75 | 76 | handle_call(stop, _From, State) -> 77 | {stop, normal, stopped, State}; 78 | handle_call(_Request, _From, State) -> 79 | {reply, ok, State}. 80 | 81 | handle_cast(_Msg, State) -> 82 | {noreply, State}. 83 | 84 | handle_info({flush, Ref, _From}, #state{output = Output} = State) -> 85 | R = handle_info0(flush, State), 86 | Output ! {flush, Ref, self()}, 87 | R; 88 | handle_info(resume, State) -> 89 | {noreply, State#state{suspended = false}}; 90 | handle_info({suspend, MilliSec}, State) -> 91 | timer:send_after(MilliSec, resume), 92 | handle_info0(flush, State#state{suspended = true}); 93 | handle_info({change, SizeModel, DelayModel}, State) -> 94 | {noreply, State#state{size_dist = SizeModel, delay_dist = DelayModel}}; 95 | handle_info(stop, State) -> 96 | {stop, normal, State}; 97 | handle_info(Info, #state{suspended = Suspended} = State) -> 98 | {noreply, NewState} = handle_info0(Info, State), 99 | case Suspended of 100 | true -> 101 | handle_info0(flush, NewState); 102 | _ -> 103 | case queue:is_empty(NewState#state.queue) of 104 | false -> schedule(NewState); 105 | _ -> ok 106 | end, 107 | {noreply, NewState} 108 | end. 109 | 110 | handle_info0({recv, _From, Data}, #state{last_recv_t = LastT, delay_esti = DelayEsti, 111 | size_esti = SizeEsti, 112 | queue = Q, total = Total} = State) -> 113 | T = cur_tick(), 114 | NewDelayEsti = esti_run(T - LastT, DelayEsti), 115 | NewSizeEsti = esti_run(size(Data), SizeEsti), 116 | NewState = State#state{last_recv_t = T, 117 | delay_esti = NewDelayEsti, 118 | size_esti = NewSizeEsti, 119 | total = Total + size(Data), 120 | queue = queue:in(Data, Q)}, 121 | {noreply, NewState}; 122 | handle_info0(flush, State) -> 123 | flush0(State), 124 | {noreply, State#state{queue = queue:new(), total = 0}}; 125 | handle_info0({flush, N}, #state{output = Output, queue = Q, total = Total} = State) -> 126 | N2 = min(N, queue:len(Q)), 127 | Self = self(), 128 | {NewQ, SendTotal} = 129 | lists:foldl(fun (_, {AQueue, Acc}) -> 130 | {{value, Bin}, Q2} = queue:out(AQueue), 131 | Output ! {recv, Self, Bin}, 132 | {Q2, Acc + size(Bin)} 133 | end, {Q, 0}, 134 | lists:seq(1, N2)), 135 | {noreply, State#state{queue = NewQ, total = Total - SendTotal}}; 136 | handle_info0({schedule, Size}, State) -> 137 | NewState = schedule_send(Size, State), 138 | {noreply, NewState}. 139 | 140 | terminate(_Reason, State) -> 141 | flush0(State), 142 | normal. 143 | 144 | code_change(_OldVsn, State, _Extra) -> 145 | {ok, State}. 146 | 147 | % ---------------------- 148 | % helpers 149 | % ---------------------- 150 | 151 | flush0(#state{output = Output, queue = Q} = _State) -> 152 | Pid = self(), 153 | lists:foreach(fun (X) -> Output ! {recv, Pid, X} end, queue:to_list(Q)). 154 | 155 | cur_tick() -> erlang:monotonic_time(millisecond). 156 | 157 | schedule_send(Size, State) when Size =< 0 -> State; 158 | schedule_send(Size, #state{output = Output, queue = Q, total = Total} = State) -> 159 | case queue:out(Q) of 160 | {empty, _Q2} -> State; 161 | {{value, Bin}, Q2} -> 162 | SZ = size(Bin), 163 | case Size >= SZ of 164 | true -> 165 | Output ! {recv, self(), Bin}, 166 | schedule_send(Size - SZ, State#state{queue = Q2, total = Total - SZ}); 167 | _ -> 168 | <> = Bin, 169 | Output ! {recv, self(), Send}, 170 | State#state{queue = queue:in_r(Rem, Q2), total = Total - Size} 171 | end 172 | end. 173 | 174 | schedule_delay(#state{delay_dist = identity} = _State) -> 175 | 0; 176 | schedule_delay(#state{last_send_t = LastT, 177 | delay_esti = DelayEsti, delay_dist = constant} = _State) -> 178 | {Mean, _Var} = esti_get(DelayEsti), 179 | Mean + LastT - cur_tick(); 180 | schedule_delay(#state{last_send_t = LastT, 181 | delay_esti = DelayEsti, delay_dist = uniform} = _State) -> 182 | {Mean, Var} = esti_get(DelayEsti), 183 | X = math:sqrt(12 * Var), 184 | Mean + rand:uniform() * X + LastT - cur_tick(); 185 | schedule_delay(#state{last_send_t = LastT, 186 | delay_esti = DelayEsti, delay_dist = gaussian} = _State) -> 187 | {Mean, Var} = esti_get(DelayEsti), 188 | Z = rand:normal(Mean, Var), 189 | LastT + Z - cur_tick(); 190 | schedule_delay(#state{last_send_t = LastT, 191 | delay_esti = DelayEsti, delay_dist = poission} = _State) -> 192 | % here, the delay between two packages follows exponential distribution 193 | % pdf(x) = lambda exp(-lambda * x), for x >= 0 194 | % while lambda can be estimated as 1/Mean 195 | % let's generate exponential distribution random variable by inverse transforming 196 | {Mean, _Var} = esti_get(DelayEsti), 197 | Z = - math:log(max(rand:uniform(), ?EPSILON)) * Mean, 198 | LastT + Z - cur_tick(). 199 | 200 | schedule_size(#state{size_dist = identity, queue = Q} = _State) -> 201 | size(queue:head(Q)); 202 | schedule_size(#state{size_dist = constant, size_esti = SizeEsti} = _State) -> 203 | {Mean, _Var} = esti_get(SizeEsti), 204 | Mean; 205 | schedule_size(#state{size_dist = uniform, size_esti = SizeEsti} = _State) -> 206 | {Mean, Var} = esti_get(SizeEsti), 207 | X = math:sqrt(12 * Var), 208 | Mean + rand:uniform() * X; 209 | schedule_size(#state{size_dist = gaussian, size_esti = SizeEsti} = _State) -> 210 | {Mean, Var} = esti_get(SizeEsti), 211 | rand:normal(Mean, Var). 212 | 213 | -define(Q_LEN, 4). 214 | -define(Q_BYTES, 100 * 1024). 215 | 216 | schedule(#state{queue = Q, total = Total, create_t = CreateT} = State) -> 217 | T = cur_tick(), 218 | case {T - CreateT < 2000, queue:len(Q), Total} of 219 | {true, _, _} -> self() ! flush; 220 | {_, _L, T} when T < 100 -> self() ! flush; 221 | {_, L, _T} when L > ?Q_LEN -> self() ! {flush, queue:len(Q) - ?Q_LEN}; 222 | {_, _L, T} when T > ?Q_BYTES -> self() ! {flush, 1}; 223 | _ -> 224 | Delay = min(schedule_delay(State), 100), 225 | Size = round(schedule_size(State)), 226 | case {Delay > 10, Size > 0} of 227 | {_, false} -> self() ! flush; 228 | {true, true} -> timer:send_after(Delay, {schedule, Size}); 229 | {_, true} -> self() ! {schedule, Size} 230 | end 231 | end. 232 | 233 | %@doc init a algorithm for mean/variance estimation 234 | esti_init(welford) -> 235 | #param_esti{algo = {fun welford_run/2, fun welford_get/1}, 236 | state = welford_init()}; 237 | esti_init(iir) -> 238 | #param_esti{algo = {fun iir_run/2, fun iir_get/1}, 239 | state = iir_init()}. 240 | 241 | esti_run(X, #param_esti{algo = {RunFun, _GetFun}, state = AlgoData} = EsitState) -> 242 | EsitState#param_esti{state = RunFun(X, AlgoData)}. 243 | 244 | esti_get(#param_esti{algo = {_RunFun, GetFun}, state = AlgoData} = _EsitState) -> 245 | GetFun(AlgoData). 246 | 247 | % ----------------------- 248 | % welford algorithm 249 | % ----------------------- 250 | 251 | %@doc online mean & variance estimator 252 | -record(welford_state, 253 | { 254 | n = 0, 255 | mean = 0.0, 256 | m2 = 0.0 257 | }). 258 | 259 | welford_init() -> 260 | #welford_state{}. 261 | 262 | welford_run(X, #welford_state{n = N, mean = Mean, m2 = M2} = _State) -> 263 | NewN = N + 1, 264 | Delta = X - Mean, 265 | NewMean = Mean + Delta / NewN, 266 | Delta2 = X - NewMean, 267 | NewM2 = M2 + Delta2 * Delta2, 268 | #welford_state{n = NewN, mean = NewMean, m2 = NewM2}. 269 | 270 | welford_get(#welford_state{n = N, mean = Mean, m2 = M2} = _State) when N > 1 -> 271 | {Mean, M2 / (N - 1)}; 272 | welford_get(#welford_state{mean = Mean} = _State) -> 273 | {Mean, 0}. 274 | 275 | % ----------------------- 276 | % IIR-based estimator 277 | % ----------------------- 278 | 279 | %@doc online mean & variance estimator 280 | -record(iir_state, 281 | { 282 | mean = 0.0, 283 | var = 0.0 284 | }). 285 | 286 | iir_init() -> 287 | #iir_state{}. 288 | 289 | -define(MEAN_ALPHA, 0.1). 290 | -define(VAR_ALPHA, 0.05). 291 | 292 | iir_run(X, #iir_state{mean = Mean, var = Var} = _State) -> 293 | NewMean = ?MEAN_ALPHA * X + (1 - ?MEAN_ALPHA) * Mean, 294 | Delta = X - NewMean, 295 | NewVar = ?VAR_ALPHA * Delta * Delta + (1 - ?VAR_ALPHA) * Var, 296 | #iir_state{mean = NewMean, var = NewVar}. 297 | 298 | iir_get(#iir_state{mean = Mean, var = Var} = _State) -> 299 | {Mean, Var}. 300 | 301 | choice(L) -> 302 | Len = length(L), 303 | lists:nth(rand:uniform(Len), L). 304 | 305 | rand_size_model() -> choice([identity, constant, uniform, gaussian]). 306 | rand_delay_model() -> choice([identity, constant, uniform, gaussian, poission]). -------------------------------------------------------------------------------- /src/mimicsocks_remote.erl: -------------------------------------------------------------------------------- 1 | %@doc remote socket forwarding server 2 | % 3 | % tcp traffic is either forwarded to a socks5 handler or relayed to another mimicsocks proxy 4 | %@author foldl 5 | -module(mimicsocks_remote). 6 | 7 | -include("mimicsocks.hrl"). 8 | 9 | -behaviour(gen_statem). 10 | 11 | % api 12 | -export([start_link/1, socket_ready/2]). 13 | 14 | % callbacks 15 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 16 | 17 | -import(mimicsocks_wormhole_local, [show_sock/1, report_disconn/2]). 18 | 19 | % FSM States 20 | -export([ 21 | init/3, 22 | forward/3 23 | ]). 24 | 25 | -record(state, 26 | { 27 | channel, 28 | handler, 29 | handler_mod 30 | } 31 | ). 32 | 33 | start_link(Args) -> 34 | gen_statem:start_link(?MODULE, Args, []). 35 | 36 | socket_ready(Pid, LSock) when is_pid(Pid), is_port(LSock) -> 37 | gen_tcp:controlling_process(LSock, Pid), 38 | gen_statem:cast(Pid, {socket_ready, LSock}). 39 | 40 | %%%=================================================================== 41 | %%% gen_fsm callbacks 42 | %%%=================================================================== 43 | init([Key, HandlerMod, HandlerArgs]) -> 44 | {ok, Handler} = HandlerMod:start_link([self() | HandlerArgs]), 45 | case mimicsocks_wormhole_remote:start_link([self(), Key]) of 46 | {ok, Channel} -> 47 | {ok, init, #state{handler_mod = HandlerMod, 48 | handler = Handler, 49 | channel = Channel}}; 50 | {error, Reason} -> {stop, Reason} 51 | end. 52 | 53 | callback_mode() -> 54 | state_functions. 55 | 56 | init(cast, {socket_ready, Socket}, #state{channel = Channel} = State) when is_port(Socket) -> 57 | mimicsocks_wormhole_remote:socket_ready(Channel, Socket), 58 | {next_state, forward, State}; 59 | init(info, Msg, StateData) -> handle_info(Msg, init, StateData). 60 | 61 | forward(info, Msg, StateData) -> handle_info(Msg, forward, StateData). 62 | 63 | handle_info({recv, Channel, Data}, _StateName, #state{handler = Handler, channel = Channel} = State) -> 64 | Handler ! {recv, self(), Data}, 65 | {keep_state, State}; 66 | handle_info({recv, Handler, Data}, _StateName, #state{handler = Handler, channel = Channel} = State) -> 67 | Channel ! {recv, self(), Data}, 68 | {keep_state, State}; 69 | handle_info(Info, StateName, State) -> 70 | ?ERROR("unexpected ~p in state ~p", [Info, StateName]), 71 | {keep_state, State}. 72 | 73 | terminate(_Reason, _StateName, #state{channel = Channel, 74 | handler = Handler, 75 | handler_mod = Mod}) -> 76 | (catch mimicsocks_wormhole_remote:stop(Channel)), 77 | (catch Mod:stop(Handler)), 78 | ok. 79 | 80 | code_change(_OldVsn, StateName, State, _Extra) -> 81 | {ok, StateName, State}. 82 | -------------------------------------------------------------------------------- /src/mimicsocks_remote_agg.erl: -------------------------------------------------------------------------------- 1 | %@doc remote socket forwarding server 2 | % 3 | % tcp traffic is either forwarded to a socks5 handler or relayed to another mimicsocks proxy 4 | %@author foldl 5 | -module(mimicsocks_remote_agg). 6 | 7 | -include("mimicsocks.hrl"). 8 | 9 | -behaviour(gen_statem). 10 | 11 | % api 12 | -export([start_link/1, socket_ready/2]). 13 | 14 | % callbacks 15 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 16 | 17 | -import(mimicsocks_wormhole_local, [show_sock/1, report_disconn/2]). 18 | -import(mimicsocks_local_agg, [parse_full/2, send_data/3]). 19 | 20 | % FSM States 21 | -export([ 22 | init/3, 23 | forward/3, 24 | closing/3 25 | ]). 26 | 27 | -record(state, 28 | { 29 | channel, 30 | wormhole, 31 | 32 | handler_mod, 33 | handler_args, 34 | 35 | cmd_ref, 36 | buff = <<>>, 37 | t_p2i, 38 | t_i2p, 39 | d_buf = <<>> 40 | } 41 | ). 42 | 43 | start_link(Args) -> 44 | gen_statem:start_link(?MODULE, Args, []). 45 | 46 | socket_ready(Pid, LSock) when is_pid(Pid), is_port(LSock) -> 47 | gen_tcp:controlling_process(LSock, Pid), 48 | gen_statem:cast(Pid, {socket_ready, LSock}). 49 | 50 | %%%=================================================================== 51 | %%% gen_fsm callbacks 52 | %%%=================================================================== 53 | init([Key, HandlerMod, HandlerArgs]) -> 54 | case mimicsocks_wormhole_remote:start_link([self(), Key]) of 55 | {ok, Channel} -> 56 | {ok, init, #state{handler_mod = HandlerMod, 57 | handler_args = HandlerArgs, 58 | wormhole = mimicsocks_wormhole_remote, 59 | t_i2p = ets:new(tablei2p, []), 60 | t_p2i = ets:new(tablep2i, []), 61 | channel = Channel}}; 62 | {error, Reason} -> {stop, Reason} 63 | end; 64 | init([RemoteIp, RemotePort, OtherPorts, Key, HandlerMod, HandlerArgs]) -> 65 | case mimicsocks_wormhole_local:start_link([self(), RemoteIp, RemotePort, OtherPorts, Key]) of 66 | {ok, Channel} -> 67 | {ok, forward, #state{handler_mod = HandlerMod, 68 | handler_args = HandlerArgs, 69 | wormhole = mimicsocks_wormhole_local, 70 | t_i2p = ets:new(tablei2p, []), 71 | t_p2i = ets:new(tablep2i, []), 72 | channel = Channel}}; 73 | {error, Reason} -> {stop, Reason} 74 | end. 75 | 76 | callback_mode() -> 77 | state_functions. 78 | 79 | init(cast, {socket_ready, Socket}, #state{channel = Channel} = State) when is_port(Socket) -> 80 | mimicsocks_wormhole_remote:socket_ready(Channel, Socket), 81 | {next_state, forward, State}; 82 | init(info, Msg, StateData) -> handle_info(Msg, init, StateData). 83 | 84 | forward(info, Msg, StateData) -> handle_info(Msg, forward, StateData). 85 | 86 | closing(cast, _, State) -> {keep_state, State}; 87 | closing(info, stop, State) -> {stop, normal, State}; 88 | closing(info, _Msg, State) -> {keep_state, State}. 89 | 90 | handle_info({recv, Channel, Bin}, _StateName, #state{channel = Channel, 91 | d_buf = Buf} = State) -> 92 | All = <>, 93 | {ok, Frames, Rem} = parse_full(All, []), 94 | State10 = lists:foldl(fun handle_cmd/2, State, Frames), 95 | NewState = State10#state{d_buf = Rem}, 96 | {keep_state, NewState}; 97 | handle_info({recv, Handler, Data}, _StateName, #state{handler_mod = Mod, channel = Channel, t_p2i = Tp2i} = State) -> 98 | case ets:lookup(Tp2i, Handler) of 99 | [{Handler, ID}] -> 100 | % traffic control 101 | case proc_high(Channel) of 102 | true -> 103 | Mod:active(Handler, false), 104 | timer:send_after(20, {recv, Handler, ID, Data}); 105 | _ -> 106 | send_data(Channel, ID, Data) 107 | end; 108 | _ -> ok 109 | end, 110 | {keep_state, State}; 111 | handle_info({recv, Handler, ID, Data} = Msg, _StateName, #state{handler_mod = Mod, channel = Channel} = State) -> 112 | case proc_low(Channel) of 113 | true -> 114 | send_data(Channel, ID, Data), 115 | Mod:active(Handler, true); 116 | _ -> 117 | timer:send_after(20, Msg) 118 | end, 119 | {keep_state, State}; 120 | handle_info({'DOWN', _Ref, process, Handler, _Reason}, _StateName, 121 | #state{t_i2p = Ti2p, t_p2i = Tp2i} = State) -> 122 | case ets:lookup(Tp2i, Handler) of 123 | [{Handler, Id}] -> 124 | ets:match_delete(Tp2i, {Handler, '_'}), 125 | ets:match_delete(Ti2p, {Id, '_'}); 126 | _ -> ok 127 | end, 128 | {keep_state, State}; 129 | handle_info(Info, StateName, State) -> 130 | ?ERROR("unexpected ~p in state ~p", [Info, StateName]), 131 | {keep_state, State}. 132 | 133 | terminate(_Reason, _StateName, #state{channel = Channel, 134 | handler_mod = Mod, 135 | wormhole = W, 136 | t_i2p = Ti2p, 137 | t_p2i = Tp2i}) -> 138 | (catch W:stop(Channel)), 139 | ets:foldl(fun (Pid, _) -> (catch Mod:stop(Pid)) end, 0, Ti2p), 140 | ets:delete(Ti2p), 141 | ets:delete(Tp2i), 142 | ok. 143 | 144 | code_change(_OldVsn, StateName, State, _Extra) -> 145 | {ok, StateName, State}. 146 | 147 | %--------------- 148 | % utils 149 | %--------------- 150 | 151 | handle_cmd(?AGG_CMD_NOP, State) -> State; 152 | handle_cmd({?AGG_CMD_NEW_SOCKET, Id}, #state{t_p2i = Tp2i, t_i2p = Ti2p, 153 | handler_mod = Module, 154 | handler_args = Args, 155 | wormhole = W, 156 | channel = Channel} = State) -> 157 | case ets:lookup(Ti2p, Id) of 158 | [{Id, Pid}] -> 159 | ?ERROR("?AGG_CMD_NEW_SOCKET id already exsits, stop it", []), 160 | (catch Module:stop(Pid)); 161 | _ -> ok 162 | end, 163 | W:suspend_mimic(Channel, 5000), 164 | {ok, NewPid} = Module:start_link([self() | Args]), 165 | unlink(NewPid), 166 | erlang:monitor(process, NewPid), 167 | ets:insert(Ti2p, {Id, NewPid}), 168 | ets:insert(Tp2i, {NewPid, Id}), 169 | State; 170 | handle_cmd({?AGG_CMD_CLOSE_SOCKET, Id}, #state{t_p2i = Tp2i, t_i2p = Ti2p, 171 | handler_mod = Mod} = State) -> 172 | case ets:lookup(Ti2p, Id) of 173 | [{Id, Pid}] -> 174 | ets:match_delete(Tp2i, {Pid, '_'}), 175 | ets:match_delete(Ti2p, {Id, '_'}), 176 | (catch Mod:stop(Pid)); 177 | _ -> ok 178 | end, 179 | State; 180 | handle_cmd({?AGG_CMD_DATA, Id, Data}, #state{t_i2p = Ti2p} = State) -> 181 | case ets:lookup(Ti2p, Id) of 182 | [{Id, Pid}] -> 183 | Pid ! {recv, self(), Data}; 184 | _ -> 185 | ?WARNING("invalid port id: ~p", [Id]) 186 | end, 187 | State. 188 | 189 | proc_high(Pid) -> element(2, process_info(Pid, message_queue_len)) > 8000. 190 | proc_low(Pid) -> element(2, process_info(Pid, message_queue_len)) < 1000. 191 | -------------------------------------------------------------------------------- /src/mimicsocks_remote_ho.erl: -------------------------------------------------------------------------------- 1 | %@doc Handover management 2 | %@author foldl 3 | -module(mimicsocks_remote_ho). 4 | 5 | -export([start_link/1, socket_ready/2]). 6 | 7 | -include("mimicsocks.hrl"). 8 | 9 | start_link([]) -> 10 | {ok, spawn_link(fun f/0)}. 11 | 12 | socket_ready(Pid, Socket) -> 13 | ok = gen_tcp:controlling_process(Socket, Pid), 14 | Pid ! {socket_read, Socket}. 15 | 16 | f() -> 17 | receive 18 | {socket_read, Socket} -> 19 | {ok, ID} = gen_tcp:recv(Socket, ?MIMICSOCKS_HELLO_SIZE, 1000), 20 | case mimicsocks_cfg:get_remote(ID) of 21 | {ok, RemotePid} -> 22 | ok = gen_tcp:controlling_process(Socket, RemotePid), 23 | RemotePid ! {ho_socket, Socket}; 24 | _ -> 25 | ?ERROR("cann't find IVec remote, closing",[]), 26 | gen_tcp:close(Socket) 27 | end 28 | end. -------------------------------------------------------------------------------- /src/mimicsocks_remote_http.erl: -------------------------------------------------------------------------------- 1 | %@doc a handler of mimicsocks_remote 2 | % a super simple http proxy 3 | %@author foldl 4 | -module(mimicsocks_remote_http). 5 | 6 | -include("mimicsocks.hrl"). 7 | 8 | -behaviour(gen_statem). 9 | 10 | % api 11 | -export([start_link/1, socket_ready/2, stop/1, active/2]). 12 | 13 | % callbacks 14 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 15 | 16 | -define(TIMEOUT, 1000). 17 | 18 | -import(mimicsocks_wormhole_local, [show_sock/1, report_disconn/2]). 19 | -import(mimicsocks_remote_socks, [send_to_local/2]). 20 | 21 | % FSM States 22 | -export([ 23 | wait_req/3, 24 | data/3 25 | ]). 26 | 27 | -record(state, 28 | { 29 | local, % local data source (e.g. mimicsocks_remote or socket) 30 | rsock, % remote socket 31 | buff = <<>> 32 | } 33 | ). 34 | 35 | start_link(Args) -> 36 | gen_statem:start_link(?MODULE, Args, []). 37 | 38 | socket_ready(Pid, Sock) when is_port(Sock) -> 39 | ok = gen_tcp:controlling_process(Sock, Pid), 40 | gen_statem:cast(Pid, {socket_ready, Sock}). 41 | 42 | stop(Pid) -> gen_statem:stop(Pid). 43 | 44 | active(Pid, Option) -> Pid ! {active, Option}. 45 | 46 | %%%=================================================================== 47 | %%% gen_fsm callbacks 48 | %%%=================================================================== 49 | init([Local]) -> 50 | process_flag(trap_exit, true), 51 | {ok, wait_req, #state{local = Local}}. 52 | 53 | callback_mode() -> 54 | state_functions. 55 | 56 | %% wait_req -> data 57 | wait_req(cast, {socket_ready, Sock}, State) -> 58 | ok = inet:setopts(Sock, [{active, true}]), 59 | {keep_state, State#state{local = Sock}}; 60 | wait_req(cast, {local, Bin}, State) -> 61 | Buffer = <<(State#state.buff)/binary, Bin/binary>>, 62 | case decode_http_req(Buffer) of 63 | incomplete -> 64 | {keep_state, State#state{buff = Buffer}, ?TIMEOUT}; 65 | {{Host, Port, Method, Ver, RequestLine}, Headers} = _All -> 66 | %% connect to remote server & send first message 67 | case gen_tcp:connect(Host, Port, [{active, true}, {packet, raw}, binary, 68 | {reuseaddr, true}, {nodelay, true}]) of 69 | {ok, RSocket} -> 70 | ?INFO("Connected to remote ~p:~p for proxying", [Host, Port]), 71 | case Method of 72 | <<"CONNECT">> -> send_to_local(State#state.local, <<"HTTP/1.1 200 OK\r\n\r\n">>); 73 | _ -> gen_tcp:send(RSocket, [RequestLine, "\r\n", Headers]) 74 | end, 75 | {next_state, data, 76 | State#state{buff= <<>>, rsock = RSocket}}; 77 | {error, Reason} -> 78 | ?ERROR("wait_req can't connect to remote: ~p, ~p~n", [{Host, Port}, Reason]), 79 | send_to_local(State#state.local, [Ver, <<" 504 Gateway Time-out\r\n">>]), 80 | {stop, normal, State} 81 | end; 82 | Error -> 83 | ?ERROR("wait_req with error: ~p~n", [Error]), 84 | {stop, Error, State} 85 | end; 86 | wait_req(info, Msg, StateData) -> handle_info(Msg, wait_req, StateData). 87 | 88 | data(cast, {local, Bin}, #state{rsock = Socket} = State) -> 89 | gen_tcp:send(Socket, Bin), 90 | {next_state, data, State}; 91 | data(cast, {remote, Bin}, State) -> 92 | send_to_local(State#state.local, Bin), 93 | {next_state, data, State}; 94 | data(info, Msg, StateData) -> handle_info(Msg, data, StateData). 95 | 96 | handle_info({active, Option}, _StateName, #state{rsock = Socket} = StateData) -> 97 | case is_port(Socket) of 98 | true -> ok = inet:setopts(Socket, [{active, Option}]); 99 | _ -> ok 100 | end, 101 | {keep_state, StateData}; 102 | handle_info({recv, From, Bin}, StateName, StateData) when is_pid(From) -> 103 | ?MODULE:StateName(cast, {local, Bin}, StateData); 104 | handle_info({tcp, Socket, Bin}, StateName, #state{local=Socket} = StateData) -> 105 | ?MODULE:StateName(cast, {local, Bin}, StateData); 106 | handle_info({tcp, Socket, Bin}, StateName, #state{rsock=Socket} = StateData) -> 107 | ?MODULE:StateName(cast, {remote, Bin}, StateData); 108 | handle_info({tcp_closed, Socket}, _StateName, #state{rsock = Socket} = StateData) -> 109 | report_disconn(Socket, "Remote"), 110 | {stop, normal, StateData}; 111 | handle_info({tcp_closed, Socket}, _StateName, #state{local = Socket} = StateData) -> 112 | report_disconn(Socket, "local"), 113 | {stop, normal, StateData}; 114 | handle_info(Info, StateName, State) -> 115 | ?ERROR("unexpected ~p", [Info]), 116 | {next_state, StateName, State}. 117 | 118 | terminate(_Reason, _StateName, #state{rsock=RSocket, local = Local}) -> 119 | (catch gen_tcp:close(RSocket)), 120 | case is_port(Local) of 121 | true -> (catch gen_tcp:close(Local)); 122 | _ -> ok 123 | end. 124 | 125 | code_change(_OldVsn, StateName, State, _Extra) -> 126 | {ok, StateName, State}. 127 | 128 | decode_http_req(Req) -> 129 | case binary:split(Req, <<"\r\n">>) of 130 | [_Data] -> incomplete; 131 | [Request, Headers] -> {parse_req_line(Request), Headers} 132 | end. 133 | 134 | parse_req_line(Req) -> 135 | [Method, URL, Ver] = binary:split(Req, <<" ">>, [global]), 136 | {match, [Scheme, Host, Port, Path]} = re:run( 137 | URL, "^((?http|https)://)?(?[^:/]+):?(?\\d*)(?/?.*)", 138 | [{capture, all_names, binary}]), 139 | 140 | Port10 = get_port(Scheme, Port), 141 | Path10 = case Path of <<>> -> <<"/">>; _ -> Path end, 142 | RequestLine = erlang:iolist_to_binary([Method, " ", Path10, " ", Ver]), 143 | {binary_to_list(Host), Port10, Method, Ver, RequestLine}. 144 | 145 | get_port(<<>>, <<>>) -> 80; 146 | get_port(<<"http">>, <<>>) -> 80; 147 | get_port(<<"https">>, <<>>) -> 443; 148 | get_port(_, PortBin) -> binary_to_integer(PortBin). -------------------------------------------------------------------------------- /src/mimicsocks_remote_relay.erl: -------------------------------------------------------------------------------- 1 | %@doc a relay handler for remote node 2 | %@author foldl 3 | -module(mimicsocks_remote_relay). 4 | 5 | -export([start_link/1, stop/1, recv/2, active/2]). 6 | 7 | -include("mimicsocks.hrl"). 8 | 9 | -record(state, 10 | { 11 | local, 12 | sock 13 | }). 14 | 15 | %doc connect to mimicsocks proxy or another raw tcp server 16 | start_link([Local, Addr, Port]) -> 17 | {ok, spawn_link(fun () -> init([Addr, Port, Local]) end)}. 18 | 19 | recv(Pid, Bin) -> 20 | Pid ! {recv, self(), Bin}. 21 | 22 | stop(Pid) -> Pid ! stop. 23 | 24 | active(Pid, Option) -> Pid ! {active, Option}. 25 | 26 | init([Addr, Port, Local]) -> 27 | % connect to remote server & send first message 28 | case gen_tcp:connect(Addr, Port, [{active, true}, {packet, raw}, binary, 29 | {reuseaddr, true}]) of 30 | {ok, Socket} -> 31 | loop(#state{local = Local, sock = Socket}); 32 | {error, Reason} -> 33 | ?ERROR("failed to connect to mimicsocks: ~p, ~p\n", [{Addr, Port}, Reason]) 34 | end. 35 | 36 | loop(#state{local = Local, sock = Socket} = State) -> 37 | receive 38 | {tcp, Socket, Bin} -> 39 | Local ! {recv, self(), Bin}, 40 | loop(State); 41 | {tcp_closed, Socket} -> 42 | ok; 43 | {recv, Local, Bin} -> 44 | gen_tcp:send(Socket, Bin), 45 | loop(State); 46 | {active, Option} -> 47 | ok = inet:setops(Socket, [{active, Option}]), 48 | loop(State); 49 | stop -> 50 | ok; 51 | X -> 52 | ?WARNING("unexpected msg: ~p", [X]), 53 | loop(State) 54 | end. -------------------------------------------------------------------------------- /src/mimicsocks_remote_socks.erl: -------------------------------------------------------------------------------- 1 | %@doc a handler of mimicsocks_remote 2 | % a super simple socks5 proxy 3 | %@author foldl 4 | -module(mimicsocks_remote_socks). 5 | 6 | -include("mimicsocks.hrl"). 7 | 8 | -behaviour(gen_statem). 9 | 10 | % api 11 | -export([start_link/1, socket_ready/2, stop/1, active/2]). 12 | 13 | % callbacks 14 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 15 | 16 | -define(TIMEOUT, 1000). 17 | 18 | -import(mimicsocks_wormhole_local, [show_sock/1, report_disconn/2]). 19 | -export([send_to_local/2]). 20 | 21 | % FSM States 22 | -export([ 23 | wait_auth/3, 24 | wait_req/3, 25 | data/3 26 | ]). 27 | 28 | -record(state, 29 | { 30 | local, % local data source (e.g. mimicsocks_remote or socket) 31 | rsock, % remote socket 32 | buff = <<>> 33 | } 34 | ). 35 | 36 | start_link(Args) -> 37 | gen_statem:start_link(?MODULE, Args, []). 38 | 39 | socket_ready(Pid, Sock) when is_port(Sock) -> 40 | ok = gen_tcp:controlling_process(Sock, Pid), 41 | gen_statem:cast(Pid, {socket_ready, Sock}). 42 | 43 | active(Pid, Option) -> Pid ! {active, Option}. 44 | 45 | stop(Pid) -> gen_statem:stop(Pid). 46 | 47 | %% definitions for socksv5 48 | %% https://tools.ietf.org/html/rfc1928 49 | -define(SOCKS5_VER, 16#05). 50 | 51 | -define(SOCKS5_AUTH_NONE, 16#00). 52 | -define(SOCKS5_AUTH_GSSAPI, 16#01). 53 | -define(SOCKS5_AUTH_USER, 16#02). 54 | -define(SOCKS5_AUTH_ERR, 16#ff). 55 | 56 | -define(SOCKS5_REQ_CONNECT, 16#01). 57 | -define(SOCKS5_REQ_BIND, 16#02). 58 | -define(SOCKS5_REQ_UDP_ASSOC,16#03). 59 | 60 | -define(SOCKS5_ATYP_V4, 16#01). 61 | -define(SOCKS5_ATYP_DOM, 16#03). 62 | -define(SOCKS5_ATYP_V6, 16#04). 63 | 64 | -define(SOCKS5_REP_OK, 16#00). 65 | -define(SOCKS5_REP_FAIL, 16#01). 66 | -define(SOCKS5_REP_NOT_ALLOWED, 16#02). 67 | -define(SOCKS5_REP_NET_UNREACHABLE, 16#03). 68 | -define(SOCKS5_REP_HOST_UNREACHABLE, 16#04). 69 | -define(SOCKS5_REP_REFUSED, 16#05). 70 | -define(SOCKS5_REP_TTL_EXPIRED, 16#06). 71 | -define(SOCKS5_REP_CMD_NOT_SUPPORTED, 16#07). 72 | -define(SOCKS5_REP_ATYP_NOT_SUPPORTED, 16#08). 73 | 74 | -define(SOCKS5_RESERVED_FIELD, 16#00). 75 | 76 | %% definitions for socksv4 77 | %% http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol 78 | -define(SOCKS4_VER, 16#04). 79 | -define(SOCKS4_CMD_CONNECT, 16#01). 80 | -define(SOCKS4_CMD_BIND, 16#02). 81 | 82 | -define(SOCKS4_RES_GRANTED, 90). 83 | -define(SOCKS4_RES_REJECTED, 91). 84 | -define(SOCKS4_RES_REJECTED_CONN, 92). 85 | -define(SOCKS4_RES_REJECTED_USERID, 93). 86 | 87 | %%%=================================================================== 88 | %%% gen_fsm callbacks 89 | %%%=================================================================== 90 | init([Local]) -> 91 | process_flag(trap_exit, true), 92 | {ok, wait_auth, #state{local = Local}}. 93 | 94 | callback_mode() -> 95 | state_functions. 96 | 97 | %% wait_auth -> wait_req -> data 98 | wait_auth(cast, {socket_ready, Sock}, State) -> 99 | ok = inet:setopts(Sock, [{active, true}]), 100 | {keep_state, State#state{local = Sock}}; 101 | wait_auth(cast, {local, Bin}, State) -> 102 | Buffer = <<(State#state.buff)/binary, Bin/binary>>, 103 | case decode_socks5_auth(Buffer) of 104 | incomplete -> 105 | {next_state, wait_auth, State#state{buff = Buffer}, ?TIMEOUT}; 106 | {?SOCKS5_VER, _, _, Rest} -> 107 | send_to_local(State#state.local, <>), 108 | {next_state, wait_req, State#state{buff = Rest}, ?TIMEOUT}; 109 | {error, {not_supported_version, ?SOCKS4_VER}} -> 110 | do_socks4(Buffer, State); 111 | Error -> 112 | ?ERROR("socks5_auth with error: ~p~n", [Error]), 113 | {stop, Error, State} 114 | end; 115 | wait_auth(timeout, _, State) -> 116 | ?ERROR("Client connection timeout: wait_req~n", []), 117 | {stop, normal, State}; 118 | wait_auth(info, Msg, StateData) -> handle_info(Msg, wait_auth, StateData). 119 | 120 | do_socks4(Buffer, State) -> 121 | case decode_socks4_req(Buffer) of 122 | incomplete -> 123 | {next_state, wait_auth, State#state{buff = Buffer}, ?TIMEOUT}; 124 | {?SOCKS4_VER, DestAddr, Host, Port, Rest} = _Socks4Req -> 125 | case gen_tcp:connect(Host, Port, 126 | [{active, true}, {packet, raw}, binary, 127 | {reuseaddr, true}, 128 | {nodelay, true}]) of 129 | {ok, RSocket} -> 130 | Addr = case inet:peername(RSocket) of 131 | {ok, {Addr10, Port}} -> Addr10; 132 | _ -> {8,8,8,8} 133 | end, 134 | ?INFO("Connected to remote ~p:~p for proxying", [Addr, Port]), 135 | gen_tcp:send(RSocket, Rest), 136 | BinAddr = list_to_binary(tuple_to_list(Addr)), 137 | Socks4Rsp = <<0, ?SOCKS4_RES_GRANTED, Port:16/big, BinAddr/binary>>, 138 | send_to_local(State#state.local, Socks4Rsp), 139 | {next_state, data, State#state{buff= <<>>, rsock = RSocket}}; 140 | {error, _Reason} -> 141 | Socks4Rsp = <<0, ?SOCKS4_RES_REJECTED_CONN, Port:16/big, DestAddr/binary>>, 142 | send_to_local(State#state.local, Socks4Rsp), 143 | {stop, normal, State} 144 | end; 145 | Error -> 146 | ?ERROR("socks4 with error: ~p~n", [Error]), 147 | {stop, Error, State} 148 | end. 149 | 150 | wait_req(cast, {local, Bin}, State) -> 151 | Buffer = <<(State#state.buff)/binary, Bin/binary>>, 152 | case decode_socks5_req(Buffer) of 153 | incomplete -> 154 | {next_state, wait_req, State#state{buff = Buffer}, ?TIMEOUT}; 155 | {?SOCKS5_VER, AddrType, BinAddr, Addr, Port, Rest}-> 156 | Target = case AddrType of 157 | ?SOCKS5_ATYP_DOM -> 158 | AddrSize = size(BinAddr), 159 | <>; 160 | _ -> 161 | <> 162 | end, 163 | 164 | %% connect to remote server & send first message 165 | case gen_tcp:connect(Addr, Port, [{active, true}, {packet, raw}, binary, 166 | {reuseaddr, true}, 167 | {nodelay, true}]) of 168 | {ok, RSocket} -> 169 | ?INFO("Connected to remote ~p:~p for proxying", 170 | [Addr, Port]), 171 | gen_tcp:send(RSocket, Rest), 172 | Socks5Rsp = <>, 173 | send_to_local(State#state.local, [Socks5Rsp, Target]), 174 | {next_state, data, 175 | State#state{buff= <<>>, rsock = RSocket}}; 176 | {error, Reason} -> 177 | ?ERROR("wait_req can't connect to remote: ~p, ~p~n", [{Addr, Port}, Reason]), 178 | Socks5Rsp = <>, 179 | send_to_local(State#state.local, [Socks5Rsp, Target]), 180 | {stop, normal, State} 181 | end; 182 | Error -> 183 | ?ERROR("wait_req with error: ~p~n", [Error]), 184 | {stop, Error, State} 185 | end; 186 | wait_req(timeout, _, State) -> 187 | ?ERROR("Client connection timeout: wait_req~n", []), 188 | {stop, normal, State}; 189 | wait_req(info, Msg, StateData) -> handle_info(Msg, wait_req, StateData). 190 | 191 | data(cast, {local, Bin}, #state{rsock = Socket} = State) -> 192 | gen_tcp:send(Socket, Bin), 193 | {next_state, data, State}; 194 | data(cast, {remote, Bin}, State) -> 195 | send_to_local(State#state.local, Bin), 196 | {next_state, data, State}; 197 | data(info, Msg, StateData) -> handle_info(Msg, data, StateData). 198 | 199 | handle_info({active, Option}, _StateName, #state{rsock = Socket} = StateData) -> 200 | case is_port(Socket) of 201 | true -> ok = inet:setopts(Socket, [{active, Option}]); 202 | _ -> ok 203 | end, 204 | {keep_state, StateData}; 205 | handle_info({recv, From, Bin}, StateName, StateData) when is_pid(From) -> 206 | ?MODULE:StateName(cast, {local, Bin}, StateData); 207 | handle_info({tcp, Socket, Bin}, StateName, #state{local=Socket} = StateData) -> 208 | ?MODULE:StateName(cast, {local, Bin}, StateData); 209 | handle_info({tcp, Socket, Bin}, StateName, #state{rsock=Socket} = StateData) -> 210 | ?MODULE:StateName(cast, {remote, Bin}, StateData); 211 | handle_info({tcp_closed, Socket}, _StateName, #state{rsock = Socket} = StateData) -> 212 | report_disconn(Socket, "Remote"), 213 | {stop, normal, StateData}; 214 | handle_info({tcp_closed, Socket}, _StateName, #state{local = Socket} = StateData) -> 215 | report_disconn(Socket, "local"), 216 | {stop, normal, StateData}; 217 | handle_info(Info, StateName, State) -> 218 | ?ERROR("unexpected ~p", [Info]), 219 | {next_state, StateName, State}. 220 | 221 | terminate(_Reason, _StateName, #state{rsock=RSocket}) -> 222 | (catch gen_tcp:close(RSocket)), 223 | ok. 224 | 225 | code_change(_OldVsn, StateName, State, _Extra) -> 226 | {ok, StateName, State}. 227 | 228 | % helper functions 229 | send_to_local(Local, IoData) when is_pid(Local) -> 230 | Local ! {recv, self(), IoData}; 231 | send_to_local(LSock, IoData) when is_port(LSock) -> 232 | gen_tcp:send(LSock, IoData). 233 | 234 | decode_socks5_auth(<>) when Ver =/= ?SOCKS5_VER -> 235 | {error, {not_supported_version, Ver}}; 236 | decode_socks5_auth(<>) -> 238 | {?SOCKS5_VER, NMethods, Methods, Rest}; 239 | decode_socks5_auth(_) -> 240 | incomplete. 241 | 242 | decode_socks4_req(<<>>) -> incomplete; 243 | decode_socks4_req(<>) -> decode_socks4_req0(Rem); 244 | decode_socks4_req(<>) -> {error, {not_supported_version, Ver}}. 245 | 246 | decode_socks4_req0(<>) -> 247 | case binary:split(Rem, <<0>>) of 248 | [_USERID, More] -> 249 | case DestAddr of 250 | % socks4a 251 | <<0, 0, 0, X>> when X /= 0 -> 252 | % socks4a 253 | case binary:split(More, <<0>>) of 254 | [Host, Rest] -> 255 | {?SOCKS4_VER, DestAddr, binary_to_list(Host), DestPort, Rest}; 256 | _ -> incomplete 257 | end; 258 | _ -> 259 | {?SOCKS4_VER, DestAddr, list_to_tuple(binary_to_list(DestAddr)), DestPort, More} 260 | end; 261 | _ -> incomplete 262 | end; 263 | decode_socks4_req0(<>) -> incomplete; 264 | decode_socks4_req0(<>) -> {error, {not_supported_command, Cmd}}; 265 | decode_socks4_req0(<<>>) -> incomplete. 266 | 267 | decode_socks5_req(<<>>) -> incomplete; 268 | decode_socks5_req(<>) -> decode_socks5_req0(Rem); 269 | decode_socks5_req(<>) -> {error, {not_supported_version, Ver}}. 270 | 271 | decode_socks5_req0(<<>>) -> incomplete; 272 | decode_socks5_req0(<>) -> incomplete; 273 | decode_socks5_req0(<>) -> incomplete; 274 | decode_socks5_req0(<> = _Req) -> decode_socks5_req_conn(Rem); 275 | decode_socks5_req0(<> = _Req) -> {error, {not_supported_command, Cmd}}. 276 | 277 | decode_socks5_req_conn(<>) -> 278 | {?SOCKS5_VER, ?SOCKS5_ATYP_V4, DestAddr, list_to_tuple(binary_to_list(DestAddr)), DestPort, Rem}; 279 | decode_socks5_req_conn(<>) -> 280 | <> = DestAddr, 281 | {?SOCKS5_VER, ?SOCKS5_ATYP_V6, DestAddr, {A1,A2,A3,A4,A5,A6,A7,A8}, DestPort, Rem}; 282 | decode_socks5_req_conn(<>) -> 283 | {?SOCKS5_VER, ?SOCKS5_ATYP_DOM, Domain, binary_to_list(Domain), DestPort, Rem}; 284 | decode_socks5_req_conn(<>) -> incomplete; 285 | decode_socks5_req_conn(<>) -> incomplete; 286 | decode_socks5_req_conn(<>) -> incomplete; 287 | decode_socks5_req_conn(<>) -> {error, {bad_atype, AType}}. -------------------------------------------------------------------------------- /src/mimicsocks_sup.erl: -------------------------------------------------------------------------------- 1 | %@doc the supervisor 2 | %@author foldl 3 | -module(mimicsocks_sup). 4 | -behaviour(supervisor). 5 | 6 | -export([start_link/0, start_link/1, start_link/2]). 7 | -export([init/1]). 8 | 9 | start_link() -> 10 | Config = case application:get_env(mimicsocks, config) of 11 | {ok, X} when is_atom(X) -> X; 12 | _ -> mimicsocks 13 | end, 14 | start_link(Config, both). 15 | 16 | start_link(local) -> 17 | start_link(mimicsocks, local); 18 | start_link(remote) -> 19 | start_link(mimicsocks, remote); 20 | start_link(both) -> 21 | start_link(mimicsocks, both); 22 | start_link(Config) -> start_link(Config, both). 23 | 24 | start_link(Config, Type) -> 25 | mimicsocks_cfg:start_link(Config), 26 | Set = sets:from_list( 27 | case application:get_env(mimicsocks, log) of 28 | {ok, L} when is_list(L) -> L; 29 | _ -> [] 30 | end), 31 | case sets:is_element(file, Set) of 32 | true -> 33 | LogFile = filename:join(code:priv_dir(mimicsocks), "log"), 34 | ok = error_logger:logfile({open, LogFile}); 35 | _ -> ok 36 | end, 37 | error_logger:tty(true), % sets:is_element(tty, Set)), 38 | supervisor:start_link({local, ?MODULE}, ?MODULE, [Type]). 39 | 40 | init([Type]) -> 41 | Servers = mimicsocks_cfg:list_servers(), 42 | LocalAddresses = sets:from_list(list_addrs()), 43 | 44 | ChildLocal = case (Type == both) or (Type == local) of 45 | true -> [create_local_child(LocalAddresses, X) || X <- Servers]; 46 | _ -> [] 47 | end, 48 | ChildRemote = case (Type == both) or (Type == remote) of 49 | true -> [create_remote_child(LocalAddresses, X) || X <- Servers]; 50 | _ -> [] 51 | end, 52 | Children = lists:flatten(ChildRemote ++ ChildLocal), 53 | 54 | SupFlags = #{strategy => one_for_one, intensity => 10, period => 1}, 55 | ChildSpecs = lists:zipwith( 56 | fun (Child, Id) -> maps:put(id, Id, Child) end, 57 | Children, lists:seq(1, length(Children))), 58 | {ok, {SupFlags, ChildSpecs}}. 59 | 60 | % helper function 61 | 62 | %@doc list all addresses 63 | list_addrs() -> 64 | {ok, L} = inet:getifaddrs(), 65 | lists:flatten([proplists:get_all_values(addr, X) || {_Dev, X} <- L]). 66 | 67 | %@doc create child spec for local node 68 | create_local_child(LocalAddresses, Server) -> 69 | case mimicsocks_cfg:get_value(Server, reverse) of 70 | true -> 71 | aggregated = mimicsocks_cfg:get_value(Server, wormhole), 72 | create_local_child1(LocalAddresses, Server); 73 | _ -> create_local_child0(LocalAddresses, Server) 74 | end. 75 | 76 | create_remote_child(LocalAddresses, Server) -> 77 | case mimicsocks_cfg:get_value(Server, reverse) of 78 | true -> 79 | aggregated = mimicsocks_cfg:get_value(Server, wormhole), 80 | create_remote_child1(LocalAddresses, Server); 81 | _ -> create_remote_child0(LocalAddresses, Server) 82 | end. 83 | 84 | create_local_child0(LocalAddresses, Server) -> 85 | {Ip, Port} = mimicsocks_cfg:get_value(Server, server), 86 | {RemoteIp, RemotePort} = mimicsocks_cfg:get_value(Server, wormhole_remote), 87 | Key = mimicsocks_cfg:get_value(Server, key), 88 | OtherPorts = mimicsocks_cfg:get_value(Server, wormhole_extra_ports), 89 | LocalProxy = mimicsocks_cfg:get_value(Server, local_proxy), 90 | LocalArgs = [RemoteIp, RemotePort, OtherPorts, Key, LocalProxy], 91 | case sets:is_element(Ip, LocalAddresses) of 92 | true -> 93 | case mimicsocks_cfg:get_value(Server, wormhole) of 94 | aggregated -> 95 | LocalServerArgs = [Ip, Port, mimicsocks_local_agg, {agg, LocalArgs}], 96 | #{ 97 | start => {mimicsocks_tcp_listener, start_link, [LocalServerArgs]}, 98 | restart => permanent, 99 | shutdown => brutal_kill 100 | }; 101 | _ -> 102 | LocalServerArgs = [Ip, Port, mimicsocks_local, LocalArgs], 103 | #{ 104 | start => {mimicsocks_tcp_listener, start_link, [LocalServerArgs]}, 105 | restart => permanent, 106 | shutdown => brutal_kill 107 | } 108 | end; 109 | _ -> [] 110 | end. 111 | 112 | create_local_child1(LocalAddresses, Server) -> 113 | {RemoteIp, RemotePort} = mimicsocks_cfg:get_value(Server, wormhole_remote), 114 | Key = mimicsocks_cfg:get_value(Server, key), 115 | OtherPorts = mimicsocks_cfg:get_value(Server, wormhole_extra_ports), 116 | {Handler, HandlerArgs} = get_handler_cfg(Server), 117 | 118 | case (not sets:is_element(RemoteIp, LocalAddresses)) 119 | or (RemoteIp == {127,0,0,1}) of 120 | true -> 121 | RemoteArgs = [RemoteIp, RemotePort, OtherPorts, Key, Handler, HandlerArgs], 122 | #{ 123 | start => {mimicsocks_remote_agg, start_link, [RemoteArgs]}, 124 | restart => permanent, 125 | shutdown => brutal_kill 126 | }; 127 | _ -> [] 128 | end. 129 | 130 | %@doc create child spec for remote node 131 | create_remote_child0(LocalAddresses, Server) -> 132 | {RemoteIp, RemotePort} = mimicsocks_cfg:get_value(Server, wormhole_remote), 133 | Key = mimicsocks_cfg:get_value(Server, key), 134 | {Handler, HandlerArgs} = get_handler_cfg(Server), 135 | ExtraPorts = mimicsocks_cfg:get_value(Server, wormhole_extra_ports), 136 | 137 | Mod = case mimicsocks_cfg:get_value(Server, wormhole) of 138 | aggregated -> mimicsocks_remote_agg; 139 | _ -> mimicsocks_remote 140 | end, 141 | 142 | case sets:is_element(RemoteIp, LocalAddresses) of 143 | true -> 144 | RemoteArgs = [Key, Handler, HandlerArgs], 145 | RemoteServerArgs = [RemoteIp, RemotePort, Mod, RemoteArgs], 146 | RemoteMain = #{ 147 | start => {mimicsocks_tcp_listener, start_link, [RemoteServerArgs]}, 148 | restart => permanent, 149 | shutdown => brutal_kill 150 | }, 151 | HoWorkers = [#{ 152 | start => {mimicsocks_tcp_listener, start_link, 153 | [[RemoteIp, APort, mimicsocks_remote_ho, []]]}, 154 | restart => permanent, 155 | shutdown => brutal_kill 156 | } || APort <- ExtraPorts], 157 | [RemoteMain | HoWorkers]; 158 | _ -> [] 159 | end. 160 | 161 | create_remote_child1(LocalAddresses, Server) -> 162 | {Ip, Port} = mimicsocks_cfg:get_value(Server, server), 163 | {RemoteIp, RemotePort} = mimicsocks_cfg:get_value(Server, wormhole_remote), 164 | Key = mimicsocks_cfg:get_value(Server, key), 165 | ExtraPorts = mimicsocks_cfg:get_value(Server, wormhole_extra_ports), 166 | LocalArgs = [Key], 167 | 168 | case sets:is_element(RemoteIp, LocalAddresses) of 169 | true -> 170 | RemoteServerArgs = {[{Ip, Port}, {RemoteIp, RemotePort}], 171 | mimicsocks_local_agg, [accept, accept2], LocalArgs}, 172 | ServerCfg = #{ 173 | start => {mimicsocks_tcp_listener, start_link, [RemoteServerArgs]}, 174 | restart => permanent, 175 | shutdown => brutal_kill 176 | }, 177 | HoWorkers = [#{ 178 | start => {mimicsocks_tcp_listener, start_link, 179 | [[RemoteIp, APort, mimicsocks_remote_ho, []]]}, 180 | restart => permanent, 181 | shutdown => brutal_kill 182 | } || APort <- ExtraPorts], 183 | [ServerCfg | HoWorkers]; 184 | _ -> [] 185 | end. 186 | 187 | get_handler_cfg(Server) -> 188 | case mimicsocks_cfg:get_value(Server, handler) of 189 | socks5 -> {mimicsocks_remote_socks, []}; 190 | socks4 -> {mimicsocks_remote_socks, []}; 191 | socks -> {mimicsocks_remote_socks, []}; 192 | http -> {mimicsocks_remote_http, []}; 193 | {relay, {RelayIp, RelayPort}} -> 194 | {mimicsocks_remote_relay, [RelayIp, RelayPort]}; 195 | {relay, ProxyName} when is_atom(ProxyName) -> 196 | {RelayIp, RelayPort} = mimicsocks_cfg:get_value(ProxyName, server), 197 | {mimicsocks_remote_relay, [RelayIp, RelayPort]} 198 | end. -------------------------------------------------------------------------------- /src/mimicsocks_tcp_listener.erl: -------------------------------------------------------------------------------- 1 | %@doc a simple tcp server 2 | %@author foldl 3 | -module(mimicsocks_tcp_listener). 4 | 5 | -export([start_link/1]). 6 | 7 | start_link([_Ip, Port, Module | _T] = Args) when is_integer(Port), is_atom(Module)-> 8 | {ok, spawn_link(fun () -> init(Args) end)}; 9 | start_link({IpPortList, Module, FList, Args10} = _Args) when is_list(IpPortList) -> 10 | {ok, spawn_link(fun () -> 11 | {ok, Pid} = Module:start_link(Args10), 12 | lists:zipwith(fun ({Ip, Port}, F) -> 13 | spawn_link(fun () -> init([Ip, Port, Module, {pid, F, Pid}]) end) 14 | end, IpPortList, FList), 15 | loop() 16 | end)}. 17 | 18 | %% callbacks 19 | init([Ip, Port, Module, Args]) -> 20 | Opts = [binary, {packet, raw}, {ip, Ip}, {backlog, 128}, {active, false}, {reuseaddr, true}], 21 | case gen_tcp:listen(Port, Opts) of 22 | {ok, Listen_socket} -> 23 | case Args of 24 | {pid, F, Pid} when is_pid(Pid) -> 25 | link(Pid), 26 | accept_loop3(Listen_socket, [Module, F, Pid]); 27 | {agg, Pid} when is_pid(Pid) -> accept_loop3(Listen_socket, [Module, Pid]); 28 | {agg, Args10} -> 29 | {ok, Pid} = Module:start_link(Args10), 30 | accept_loop2(Listen_socket, [Module, Pid]); 31 | _ -> accept_loop1(Listen_socket, [Module, Args]) 32 | end; 33 | {error, Reason} -> 34 | {stop, Reason} 35 | end. 36 | 37 | accept_loop1(LSock, [Module, Args]) -> 38 | case gen_tcp:accept(LSock) of 39 | {ok, Socket} -> 40 | ok = inet:setopts(Socket, [{linger, {true, 10}}]), 41 | {ok, Pid} = Module:start_link(Args), 42 | unlink(Pid), 43 | Module:socket_ready(Pid, Socket), 44 | accept_loop1(LSock, [Module, Args]); 45 | {error, Reason} -> 46 | {error, Reason} 47 | end. 48 | 49 | accept_loop2(LSock, [Module, Pid]) -> 50 | case gen_tcp:accept(LSock) of 51 | {ok, Socket} -> 52 | ok = inet:setopts(Socket, [{linger, {true, 10}}]), 53 | Module:accept(Pid, Socket), 54 | accept_loop2(LSock, [Module, Pid]); 55 | {error, Reason} -> 56 | {error, Reason} 57 | end. 58 | 59 | accept_loop3(LSock, [Module, F, Pid] = Args) -> 60 | case gen_tcp:accept(LSock) of 61 | {ok, Socket} -> 62 | ok = inet:setopts(Socket, [{linger, {true, 10}}]), 63 | Module:F(Pid, Socket), 64 | accept_loop3(LSock, Args); 65 | {error, Reason} -> 66 | {error, Reason} 67 | end. 68 | 69 | loop() -> 70 | receive 71 | _ -> loop() 72 | end. -------------------------------------------------------------------------------- /src/mimicsocks_wormhole_local.erl: -------------------------------------------------------------------------------- 1 | %@doc generalized communication channel 2 | %@author foldl 3 | -module(mimicsocks_wormhole_local). 4 | 5 | -include("mimicsocks.hrl"). 6 | 7 | -behaviour(gen_statem). 8 | 9 | %% API 10 | -export([start_link/1, stop/1, recv/2, suspend_mimic/2, handover_now/1]). 11 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 12 | 13 | -export([report_disconn/2, show_sock/1, next_id/2]). 14 | 15 | % FSM States 16 | -export([ 17 | forward/3, 18 | ho_initiated/3, 19 | ho_wait_r2l/3, 20 | ho_sending_complete/3, 21 | ho_wait_close/3 22 | ]). 23 | 24 | % utils 25 | -import(mimicsocks_mimic, [choice/1]). 26 | 27 | start_link(Args) -> 28 | gen_statem:start_link(?MODULE, Args, []). 29 | 30 | stop(Pid) -> gen_statem:stop(Pid). 31 | 32 | recv(Pid, Data) -> Pid ! {recv, self(), Data}. 33 | 34 | suspend_mimic(Pid, Duration) -> Pid ! {suspend_mimic, Duration}. 35 | 36 | handover_now(Pid) -> Pid ! handover_now. 37 | 38 | callback_mode() -> 39 | state_functions. 40 | 41 | -record(state, 42 | { 43 | up_stream, % data receiver 44 | 45 | addr, % remote addr & port 46 | port, 47 | other_ports = [], % ports for handover 48 | extra_args, 49 | 50 | send, % process chain 51 | send_sink, 52 | recv, 53 | recv_sink, 54 | recv_inband, 55 | send_inband, 56 | 57 | ho_id, 58 | 59 | rsock, % remote socket 60 | rsock2, % handover socket 61 | key, 62 | ivec, 63 | 64 | cmd_ref, 65 | ho_timer, 66 | ho_buf = <<>>, 67 | 68 | ping_timer 69 | } 70 | ). 71 | 72 | %% callback funcitons 73 | init([UpStream, ServerAddr, ServerPort, OtherPorts, Key | T]) -> 74 | process_flag(trap_exit, true), 75 | IVec = gen_ivec(), 76 | ID0 = next_id(Key, IVec), 77 | HOID = next_id(Key, ID0), 78 | 79 | RecvSink = mimicsocks_inband_recv:start_link([self(), self()]), 80 | mimicsocks_inband_recv:set_key(RecvSink, Key), 81 | Recv = mimicsocks_crypt:start_link(decrypt, [RecvSink, mimicsocks_crypt:init_aes_ctr_dec(Key, IVec)]), 82 | 83 | {ok, SendSink} = mimicsocks_mimic:start_link([self(), identity, identity, iir]), 84 | SendEncrypt = mimicsocks_crypt:start_link(encrypt, [SendSink, mimicsocks_crypt:init_aes_ctr_enc(Key, IVec)]), 85 | Send = mimicsocks_inband_send:start_link([SendEncrypt, self()]), 86 | mimicsocks_inband_send:set_key(Send, Key), 87 | 88 | case connect(ServerAddr, ServerPort, T) of 89 | {ok, RSocket} -> 90 | ?INFO("Connected to remote ~p:~p\n", [ServerAddr, ServerPort]), 91 | gen_tcp:send(RSocket, <>), 92 | {ok, HOTimer} = create_ho_timer(OtherPorts), 93 | {ok, forward, #state{ 94 | up_stream = UpStream, 95 | addr = ServerAddr, 96 | port = ServerPort, 97 | extra_args = T, 98 | other_ports = OtherPorts, 99 | key = Key, 100 | rsock = RSocket, 101 | ivec = IVec, 102 | recv = Recv, 103 | recv_sink = RecvSink, 104 | send = Send, 105 | send_sink = SendSink, 106 | send_inband = Send, 107 | recv_inband = RecvSink, 108 | ho_timer = HOTimer, 109 | ho_id = HOID 110 | }}; 111 | {error, Reason} -> 112 | ?ERROR("can't connect to remote: ~p~n", [Reason]), 113 | {stop, Reason} 114 | end. 115 | 116 | forward(info, Info, State) -> handle_info(Info, forward, State). 117 | 118 | ho_initiated(info, {ho_socket, ok, RSock2}, #state{ 119 | ho_id = Id, recv_inband = RecvInband, key = Key} = StateData) -> 120 | ?INFO("ho_initiated", []), 121 | mimicsocks_inband_recv:tapping(RecvInband, true), 122 | gen_tcp:send(RSock2, Id), 123 | {next_state, ho_wait_r2l, 124 | StateData#state{rsock2 = RSock2, ho_buf = <<>>, ho_id = next_id(Key, Id)}, 125 | [{state_timeout, 3000, ho_wait_r2l}]}; 126 | ho_initiated(info, {ho_socket, error, Reason}, #state{recv_inband = RecvInband} = StateData) -> 127 | ?WARNING("ho_initiated failed with reason ~p", [Reason]), 128 | mimicsocks_inband_recv:tapping(RecvInband, false), 129 | {next_state, forward, StateData}; 130 | ho_initiated(info, Msg, Data) -> handle_info(Msg, ho_initiated, Data). 131 | 132 | ho_wait_r2l(state_timeout, _, StateData) -> 133 | {stop, {ho_wait_r2l, state_timeout}, StateData#state{ho_buf = <<"... truncated ...">>}}; 134 | ho_wait_r2l(info, {inband, ho_r2l}, #state{recv = Recv, ho_buf = Buf, 135 | send_inband = SendInband, 136 | recv_inband = RecvInband} = StateData) -> 137 | Ref = mimicsocks_inband_send:recv_cmd(SendInband, <>, hold), 138 | mimicsocks_inband_recv:tapping(RecvInband, false), 139 | Recv ! {recv, self(), Buf}, 140 | {next_state, ho_sending_complete, StateData#state{cmd_ref = Ref, ho_buf = <<>>}}; 141 | ho_wait_r2l(info, {tcp, Socket, Bin}, #state{rsock2 = Socket, ho_buf = Buf} = StateData) -> 142 | {keep_state, StateData#state{ho_buf = <>}}; 143 | ho_wait_r2l(info, Msg, Data) -> handle_info(Msg, ho_wait_r2l, Data). 144 | 145 | ho_sending_complete(info, {cmd_sent, Ref}, #state{send = Send} = StateData) -> 146 | Send ! {flush, Ref, self()}, 147 | {next_state, ho_sending_complete, StateData}; 148 | ho_sending_complete(info, {flush, Ref, SendSink}, #state{rsock2 = Socket2, send_sink = SendSink, 149 | rsock = Socket1, cmd_ref = Ref, 150 | send_inband = SendInband} = StateData) -> 151 | mimicsocks_inband_send:continue(SendInband), 152 | {next_state, ho_wait_close, StateData#state{rsock = Socket2, rsock2 = Socket1}}; 153 | ho_sending_complete(info, {tcp, Socket, Bin}, #state{rsock2 = Socket, recv = Recv} = StateData) -> 154 | Recv ! {recv, self(), Bin}, 155 | {keep_state, StateData}; 156 | ho_sending_complete(info, Msg, Data) -> handle_info(Msg, ho_sending_complete, Data). 157 | 158 | ho_wait_close(info, {tcp_closed, Socket}, #state{rsock2 = Socket, other_ports = OtherPorts, 159 | send_sink = SendSink} = StateData) -> 160 | ?INFO("ho complete", []), 161 | mimicsocks_mimic:change(SendSink), 162 | {ok, HOTimer} = create_ho_timer(OtherPorts), 163 | {next_state, forward, StateData#state{rsock2 = undefined, ho_timer = HOTimer}}; 164 | ho_wait_close(info, {recv, SendSink, Bin}, #state{rsock2 = Socket, send_sink = SendSink} = StateData) -> 165 | gen_tcp:send(Socket, Bin), 166 | {keep_state, StateData}; 167 | ho_wait_close(info, {tcp, Socket, Bin}, #state{rsock2 = Socket, recv = Recv} = StateData) -> 168 | Recv ! {recv, self(), Bin}, 169 | {keep_state, StateData}; 170 | ho_wait_close(info, Msg, Data) -> handle_info(Msg, ho_wait_close, Data). 171 | 172 | handle_info(handover_now, _StateName, #state{ho_timer = HOTimer} = _StateData) -> 173 | ?INFO("handover_now", []), 174 | case HOTimer of 175 | undefined -> ok; 176 | _ -> timer:cancel(HOTimer) 177 | end, 178 | self() ! ho_timer, 179 | keep_state_and_data; 180 | handle_info(ho_timer, _StateName, #state{addr = Addr, other_ports = OtherPorts, extra_args = Extra} = StateData) -> 181 | ?INFO("ho_timer", []), 182 | Port = choice(OtherPorts), 183 | Pid = self(), 184 | spawn(fun () -> 185 | case connect(Addr, Port, Extra) of 186 | {ok, RSocket} -> 187 | gen_tcp:controlling_process(RSocket, Pid), 188 | Pid ! {ho_socket, ok, RSocket}; 189 | {error, Reason} -> 190 | Pid ! {ho_socket, error, Reason} 191 | end end), 192 | {next_state, ho_initiated, StateData#state{ho_timer = undefined}}; 193 | handle_info({inband_cmd, Pid, Cmds}, _StateName, #state{recv_inband = Pid} = StateData) -> 194 | parse_cmds(Cmds, self()), 195 | {keep_state, StateData}; 196 | handle_info({tcp, RSocket, Bin}, _StateName, #state{rsock = RSocket, recv = Recv} = State) -> 197 | Recv ! {recv, self(), Bin}, 198 | {keep_state, State}; 199 | handle_info({tcp_closed, RSocket}, _StateName, #state{rsock = RSocket} = State) -> 200 | {stop, remote_down, State}; 201 | handle_info({recv, SendSink, Bin}, _StateName, #state{send_sink = SendSink, rsock = Socket} = State) -> 202 | gen_tcp:send(Socket, Bin), 203 | {keep_state, State}; 204 | handle_info({recv, Output, Bin}, _StateName, #state{send = Send, up_stream = Output} = State) -> 205 | Send ! {recv, self(), Bin}, 206 | {keep_state, State}; 207 | handle_info({recv, RecvSink, Bin}, _StateName, #state{recv_sink = RecvSink, up_stream = Output} = State) -> 208 | Output ! {recv, self(), Bin}, 209 | {keep_state, State}; 210 | handle_info({suspend_mimic, Duration}, _StateName, #state{send_sink = SendSink} = State) -> 211 | mimicsocks_mimic:suspend(SendSink, Duration), 212 | {keep_state, State}; 213 | handle_info(stop, _StateName, State) -> 214 | {stop, normal, State}; 215 | handle_info(Info, _StateName, State) -> 216 | ?WARNING("unexpected msg: ~p", [Info]), 217 | {keep_state, State}. 218 | 219 | terminate(_Reason, _StateName, #state{rsock = Sock1, rsock2 = Sock2} = 220 | _State) -> 221 | (catch gen_tcp:close(Sock1)), 222 | (catch gen_tcp:close(Sock2)), 223 | normal. 224 | 225 | code_change(_OldVsn, OldStateName, OldStateData, _Extra) -> 226 | {ok, OldStateName, OldStateData}. 227 | 228 | %--------------- 229 | % utils 230 | %--------------- 231 | 232 | -ifdef(debug). 233 | create_ho_timer(Ports) -> 234 | case length(Ports) > 0 of 235 | true -> timer:send_after(5 * 1000, ho_timer); 236 | _ -> {ok, undefined} 237 | end. 238 | -else. 239 | create_ho_timer(Ports) -> 240 | case length(Ports) > 0 of 241 | true -> timer:send_after((rand:uniform(10) + 20) * 60 * 1000, ho_timer); 242 | _ -> {ok, undefined} 243 | end. 244 | -endif. 245 | 246 | report_disconn(Socket, Type) -> 247 | case inet:peername(Socket) of 248 | {ok, {Addr, Port}} -> 249 | ?INFO("~p ~p disconnected (port ~p).", [Type, Addr, Port]); 250 | {error, _} -> 251 | ?INFO("~p disconnected", [Type]) 252 | end. 253 | 254 | show_sock(Socket) -> 255 | {ok, {Addr, Port}} = inet:peername(Socket), 256 | {Addr, Port}. 257 | 258 | parse_cmds(<> = _Cmds, _Pid) -> ok; 259 | parse_cmds(<> = _Cmds, Pid) -> 260 | Pid ! {inband, ho_r2l}, 261 | parse_cmds(Rem, Pid); 262 | parse_cmds(<> = _Cmds, Pid) -> 263 | Pid ! {inband, ho_complete}, 264 | parse_cmds(Rem, Pid). 265 | 266 | -define(REMOTE_TCP_OPTS, [{packet, raw}, binary, {reuseaddr, true}, {keepalive, true}, 267 | {send_timeout, 3000}, {send_timeout_close, true}]). 268 | 269 | connect(ServerAddr, ServerPort, [{http_proxy, ProxyAddr, ProxyPort}]) -> 270 | case gen_tcp:connect(ProxyAddr, ProxyPort, [{active, false} | ?REMOTE_TCP_OPTS]) of 271 | {ok, Socket} -> 272 | Req = ["CONNECT ", ip_to_list(ServerAddr), ":", integer_to_list(ServerPort), " HTTP/1.1\r\n\r\n"], 273 | gen_tcp:send(Socket, Req), 274 | case wait_result(Socket) of 275 | {ok, <<>>} -> 276 | inet:setopts(Socket, [{active, true}]), 277 | {ok, Socket}; 278 | {ok, Remain} -> 279 | self() ! {tcp, Socket, Remain}, 280 | inet:setopts(Socket, [{active, true}]), 281 | {ok, Socket}; 282 | OtherError -> 283 | ?ERROR("~p~n", [OtherError]), 284 | gen_tcp:close(Socket), 285 | OtherError 286 | end; 287 | Result -> Result 288 | end; 289 | connect(ServerAddr, ServerPort, _) -> 290 | gen_tcp:connect(ServerAddr, ServerPort, [{active, true} | ?REMOTE_TCP_OPTS]). 291 | 292 | ip_to_list(X) when is_list(X) -> X; 293 | ip_to_list({A,B,C,D}) -> 294 | io_lib:format("~B.~B.~B.~B",[A,B,C,D]); 295 | ip_to_list({A,B,C,D,E,F,G,H}) -> 296 | io_lib:format("~.16B.~.16B.~.16B.~.16B.~.16B.~.16B.~.16B.~.16B",[A,B,C,D,E,F,G,H]). 297 | 298 | wait_line(_Socket, _Acc, N) when N < 0 -> {error, timeout}; 299 | wait_line(Socket, Acc, N) -> 300 | receive 301 | after 50 -> ok 302 | end, 303 | case gen_tcp:recv(Socket, 0) of 304 | {ok, Data} -> 305 | All = <>, 306 | case binary:split(All, <<"\r\n\r\n">>) of 307 | [_, Remain] -> {ok, Remain}; 308 | [_] -> wait_line(Socket, All, N - 1) 309 | end; 310 | X -> X 311 | end. 312 | 313 | wait_result(Socket) -> 314 | Expected = <<"HTTP/1.1 200">>, 315 | case gen_tcp:recv(Socket, size(Expected), 2000) of 316 | {ok, Expected} -> wait_line(Socket, <<>>, 40); 317 | {ok, <<"HTTP/1.0 200">>} -> wait_line(Socket, <<>>, 40); 318 | {ok, Other} -> {error, Other}; 319 | X -> X 320 | end. 321 | 322 | next_id(Key, ID) -> mimicsocks_crypt:hmac_sha(Key, ID, ?MIMICSOCKS_HELLO_SIZE). 323 | 324 | %@doc generate a IVEC using random algo in order to randomized entropy on IVEC 325 | gen_ivec() -> 326 | L = lists:seq(1, ?MIMICSOCKS_HELLO_SIZE), 327 | T = rand:uniform(256) - 1, 328 | case rand:uniform(3) of 329 | 1 -> crypto:strong_rand_bytes(?MIMICSOCKS_HELLO_SIZE); 330 | _ -> 331 | Q = 256 div rand:uniform(10), 332 | list_to_binary([(rand:uniform(Q) + T) rem 256 || _ <- L]) 333 | end. 334 | -------------------------------------------------------------------------------- /src/mimicsocks_wormhole_remote.erl: -------------------------------------------------------------------------------- 1 | %@doc generalized communication channel 2 | %@author foldl 3 | -module(mimicsocks_wormhole_remote). 4 | 5 | -include("mimicsocks.hrl"). 6 | 7 | -behaviour(gen_statem). 8 | 9 | % api 10 | -export([start_link/1, stop/1, recv/2, socket_ready/2, suspend_mimic/2]). 11 | 12 | % callbacks 13 | -export([init/1, callback_mode/0, terminate/3, code_change/4]). 14 | 15 | -import(mimicsocks_wormhole_local, [show_sock/1, report_disconn/2, next_id/2]). 16 | 17 | % FSM States 18 | -export([ 19 | init/3, 20 | wait_ivec/3, 21 | forward/3, 22 | wait_sending_cmd/3, 23 | wait_ho_complete/3, 24 | bad_key/3 25 | ]). 26 | 27 | -record(state, 28 | { 29 | send, % process chain 30 | send_sink, 31 | recv, 32 | recv_sink, 33 | recv_inband, 34 | send_inband, 35 | 36 | rsock, % remote socket 37 | rsock2, % handover socket 38 | key, 39 | ivec, 40 | ho_id, 41 | 42 | up_stream, 43 | 44 | cmd_ref, 45 | buff = <<>> 46 | } 47 | ). 48 | 49 | start_link(Args) -> 50 | gen_statem:start_link(?MODULE, Args, []). 51 | 52 | stop(Pid) -> gen_statem:stop(Pid). 53 | 54 | socket_ready(Pid, LSock) when is_pid(Pid), is_port(LSock) -> 55 | gen_tcp:controlling_process(LSock, Pid), 56 | gen_statem:cast(Pid, {socket_ready, LSock}). 57 | 58 | recv(Pid, Data) -> Pid ! {recv, self(), Data}. 59 | 60 | suspend_mimic(Pid, Duration) -> Pid ! {suspend_mimic, Duration}. 61 | 62 | %%%=================================================================== 63 | %%% gen_fsm callbacks 64 | %%%=================================================================== 65 | init([Upstream, Key]) -> 66 | {ok, init, #state{up_stream = Upstream, key = Key}}. 67 | 68 | callback_mode() -> 69 | state_functions. 70 | 71 | init(cast, {socket_ready, Socket}, State) when is_port(Socket) -> 72 | ok = (catch inet:setopts(Socket, [{active, true}, {packet, raw}, binary])), 73 | {next_state, wait_ivec, State#state{rsock = Socket}}; 74 | init(info, Msg, StateData) -> handle_info(Msg, init, StateData). 75 | 76 | wait_ivec(cast, {local, Bin}, #state{buff = Buff, key = Key} = State) -> 77 | All = <>, 78 | case All of 79 | <> -> 80 | case next_id(Key, IVec) of 81 | ID0 -> 82 | RecvSink = mimicsocks_inband_recv:start_link([self(), self()]), 83 | mimicsocks_inband_recv:set_key(RecvSink, Key), 84 | Recv = mimicsocks_crypt:start_link(decrypt, [RecvSink, mimicsocks_crypt:init_aes_ctr_dec(Key, IVec)]), 85 | 86 | {ok, SendSink} = mimicsocks_mimic:start_link([self()]), 87 | SendCrypt = mimicsocks_crypt:start_link(encrypt, [SendSink, mimicsocks_crypt:init_aes_ctr_enc(Key, IVec)]), 88 | Send = mimicsocks_inband_send:start_link([SendCrypt, self()]), 89 | mimicsocks_inband_send:set_key(Send, Key), 90 | Recv ! {recv, self(), Rem}, 91 | 92 | HOID = next_id(Key, ID0), 93 | mimicsocks_cfg:register_remote(HOID, self()), 94 | {next_state, forward, State#state{ 95 | ivec = IVec, 96 | recv = Recv, 97 | recv_sink = RecvSink, 98 | send = Send, 99 | send_sink = SendSink, 100 | recv_inband = RecvSink, 101 | send_inband = Send, 102 | ho_id = HOID 103 | }}; 104 | _ -> 105 | create_close_timer(), 106 | {next_state, bad_key, State} 107 | end; 108 | _ -> 109 | 110 | {next_state, wait_ivec, State#state{buff = All}} 111 | end; 112 | wait_ivec(info, {tcp, _Socket, Bin}, StateData) -> 113 | wait_ivec(cast, {local, Bin}, StateData); 114 | wait_ivec(info, Msg, StateData) -> handle_info(Msg, wait_ivec, StateData). 115 | 116 | forward(info, Msg, StateData) -> handle_info(Msg, forward, StateData). 117 | 118 | bad_key(info, {tcp, _Socket, _Bin}, StateData) -> {keep_state, StateData}; 119 | bad_key(info, close, StateData) -> {stop, bad_key, StateData}; 120 | bad_key(info, Msg, StateData) -> handle_info(Msg, bad_key, StateData). 121 | 122 | wait_sending_cmd(info,{cmd_sent, Ref}, #state{cmd_ref = Ref, send = Send} = State) -> 123 | Send ! {flush, Ref, self()}, 124 | {keep_state, State}; 125 | wait_sending_cmd(info,{flush, Ref, SendSink}, #state{cmd_ref = Ref, send_sink = SendSink, 126 | send_inband = SendInband} = State) -> 127 | mimicsocks_inband_send:continue(SendInband), 128 | {next_state, wait_ho_complete, State}; 129 | wait_sending_cmd(info, {tcp, Socket, Bin}, #state{rsock2 = Socket, buff = Buff} = State) -> 130 | {keep_state, State#state{buff = <>}}; 131 | wait_sending_cmd(info, Msg, State) -> handle_info(Msg, wait_sending_cmd, State). 132 | 133 | wait_ho_complete(info, {inband, ho_complete}, #state{rsock = Sock1, rsock2 = Sock2, 134 | recv = Recv, buff = Buff, 135 | recv_inband = RecvInband, 136 | send_sink = SendSink} = State) -> 137 | mimicsocks_inband_recv:tapping(RecvInband, false), 138 | gen_tcp:close(Sock1), 139 | mimicsocks_mimic:change(SendSink), 140 | Recv ! {recv, self(), Buff}, 141 | {next_state, forward, State#state{rsock = Sock2, rsock2 = undefined, buff = <<>>}}; 142 | wait_ho_complete(info, {tcp, Socket, Bin}, #state{rsock2 = Socket, buff = Buff} = State) -> 143 | {keep_state, State#state{buff = <>}}; 144 | wait_ho_complete(info, {recv, SendSink, Bin}, #state{send_sink = SendSink, 145 | rsock2 = Socket} = State) -> 146 | gen_tcp:send(Socket, Bin), 147 | {keep_state, State}; 148 | wait_ho_complete(info, Msg, State) -> handle_info(Msg, wait_ho_complete, State). 149 | 150 | handle_info({ho_socket, Socket}, _StateName, #state{send_inband = SendInband, recv_inband = RecvInband, 151 | ho_id = Id, key = Key} = StateData) -> 152 | ok = inet:setopts(Socket, [{active, true}]), 153 | mimicsocks_inband_recv:tapping(RecvInband, true), 154 | Ref = mimicsocks_inband_send:recv_cmd(SendInband, <>, hold), 155 | NewId = next_id(Key, Id), 156 | mimicsocks_cfg:deregister_remote(Id), 157 | mimicsocks_cfg:register_remote(NewId, self()), 158 | {next_state, wait_sending_cmd, StateData#state{rsock2 = Socket, cmd_ref = Ref, 159 | ho_id = NewId}}; 160 | handle_info({inband_cmd, Pid, Cmds}, _StateName, #state{recv_sink = Pid} = State) -> 161 | parse_cmds(Cmds, self()), 162 | {keep_state, State}; 163 | handle_info({recv, RecvSink, Data}, _StateName, #state{up_stream = Upstream, recv_sink = RecvSink} = State) -> 164 | Upstream ! {recv, self(), Data}, 165 | {keep_state, State}; 166 | handle_info({recv, SendSink, Data}, _StateName, #state{rsock = Socket, send_sink = SendSink} = State) -> 167 | gen_tcp:send(Socket, Data), 168 | {keep_state, State}; 169 | handle_info({recv, Upstream, Data}, _StateName, #state{up_stream = Upstream, send = Send} = State) -> 170 | Send ! {recv, self(), Data}, 171 | {keep_state, State}; 172 | handle_info({tcp, Socket, Bin}, _StateName, #state{rsock = Socket, recv = Recv} = State) -> 173 | Recv ! {recv, self(), Bin}, 174 | {keep_state, State}; 175 | handle_info({tcp_closed, Socket}, _StateName, #state{rsock = Socket} = StateData) -> 176 | report_disconn(Socket, "Remote"), 177 | {stop, local_down, StateData}; 178 | handle_info({tcp_closed, Socket2}, _StateName, StateData) -> 179 | report_disconn(Socket2, "Remote2"), 180 | {stop, ho_error, StateData}; 181 | handle_info({suspend_mimic, Duration}, _StateName, #state{send_sink = SendSink} = State) -> 182 | mimicsocks_mimic:suspend(SendSink, Duration), 183 | {keep_state, State}; 184 | handle_info({cmd_sent, Ref}, StateName, StateData) -> 185 | ?WARNING("cmd_sent ~p in state ~p", [Ref, StateName]), 186 | {keep_state, StateData}; 187 | handle_info(Info, StateName, State) -> 188 | ?ERROR("unexpected ~p in state ~p", [Info, StateName]), 189 | {keep_state, State}. 190 | 191 | terminate(_Reason, _StateName, #state{rsock=RSocket, 192 | rsock2 = RSocket2, 193 | recv = Recv, 194 | send = Send, 195 | ho_id = Id}) -> 196 | (catch gen_tcp:close(RSocket)), 197 | (catch gen_tcp:close(RSocket2)), 198 | (catch Recv ! stop), 199 | (catch Send ! stop), 200 | mimicsocks_cfg:deregister_remote(Id), 201 | ok. 202 | 203 | code_change(_OldVsn, StateName, State, _Extra) -> 204 | {ok, StateName, State}. 205 | 206 | %--------------- 207 | % utils 208 | %--------------- 209 | parse_cmds(<> = _Cmds, _Pid) -> ok; 210 | parse_cmds(<> = _Cmds, Pid) -> 211 | Pid ! {inband, start_ho, Port}, 212 | parse_cmds(Rem, Pid); 213 | parse_cmds(<> = _Cmds, Pid) -> 214 | Pid ! {inband, ho_complete}, 215 | parse_cmds(Rem, Pid). 216 | 217 | -ifdef(debug). 218 | create_close_timer() -> timer:send_after(100, close). 219 | -else. 220 | create_close_timer() -> timer:send_after((rand:uniform(50) + 1) * 60 * 1000, close). 221 | -endif. 222 | -------------------------------------------------------------------------------- /test/mimicsocks_test.erl: -------------------------------------------------------------------------------- 1 | %@doc some tests 2 | %@author foldl 3 | -module(mimicsocks_test). 4 | 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include("mimicsocks.hrl"). 7 | 8 | -compile([export_all]). 9 | 10 | crypt_test() -> 11 | Key = crypto:strong_rand_bytes(256 div 8), 12 | IVec = crypto:strong_rand_bytes(?MIMICSOCKS_HELLO_SIZE), 13 | Cipher = crypto:stream_init(aes_ctr, Key, IVec), 14 | Decrypt = mimicsocks_crypt:start_link(decrypt, [self(), Cipher]), 15 | Encrypt = mimicsocks_crypt:start_link(encrypt, [Decrypt, Cipher]), 16 | io:format("~p~n", [{Encrypt, Decrypt}]), 17 | test_data({Encrypt, Decrypt}, {<<1,2,3,4>>, <<1,2,3,4>>}), 18 | test_data({Encrypt, Decrypt}, {<<5, 1,2,3,4>>, <<5, 1,2,3,4>>}). 19 | 20 | test_data({Entry, Exit}, {Data, Expected}) -> 21 | Entry ! {recv, self(), Data}, 22 | receive 23 | {recv, Exit, Expected} -> ok; 24 | X -> throw(X) 25 | after 26 | 1000 -> throw(timeout) 27 | end. 28 | 29 | 30 | send_and_wait(Send, Msg) -> 31 | receive 32 | Msg -> ok 33 | after 34 | 10 -> 35 | Buf = crypto:strong_rand_bytes(rand:uniform(20) + 5), 36 | mimicsocks_inband_send:recv(Send, Buf), 37 | send_and_wait(Send, Msg) 38 | end. 39 | 40 | dump_data(Pid) -> 41 | receive 42 | {recv, Pid, <<>>} -> dump_data(Pid); 43 | {recv, Pid, Data} -> 44 | io:format("~p~n", [Data]), 45 | dump_data(Pid) 46 | after 47 | 1000 -> ok 48 | end. 49 | 50 | socks5_server() -> 51 | Server = mimicsocks_tcp_listener:start_link([{127,0,0,1}, 8888, 52 | mimicsocks_remote_socks, [undefined]]), 53 | Server. 54 | 55 | http_server() -> 56 | spawn_link(fun http_server0/0). 57 | 58 | http_server0() -> 59 | Opts = [binary, {packet, raw}, {ip, {127,0,0,1}}, 60 | {keepalive, true}, {backlog, 30}, {active, true}], 61 | case gen_tcp:listen(8001, Opts) of 62 | {ok, LSock} -> 63 | io:format("http_server on 8001\n", []), 64 | case gen_tcp:accept(LSock) of 65 | {ok, Socket} -> 66 | wait_req(Socket), 67 | gen_tcp:send(Socket, "HTTP/1.1 200 OK\r\n"), 68 | gen_tcp:send(Socket, "Date: Mon, 23 May 2005 22:38:34 GMT\r\n"), 69 | gen_tcp:send(Socket, "Content-Type: text/html; charset=UTF-8\r\n"), 70 | gen_tcp:send(Socket, "Content-Encoding: UTF-8\r\n"), 71 | gen_tcp:send(Socket, "Content-Length: 400000000\r\n"), 72 | gen_tcp:send(Socket, "Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT\r\n"), 73 | gen_tcp:send(Socket, "Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)\r\n"), 74 | gen_tcp:send(Socket, "ETag: \"3f80f-1b6-3e1cb03b\"\r\n"), 75 | gen_tcp:send(Socket, "Accept-Ranges: bytes\r\n"), 76 | gen_tcp:send(Socket, "Connection: close\r\n\r\n"), 77 | send(Socket, 0), 78 | gen_tcp:close(LSock), 79 | io:format("http_server stopped\n", []); 80 | {error, Reason} -> 81 | {error, Reason} 82 | end; 83 | {error, Reason} -> 84 | io:format("error: ~p", [Reason]), 85 | {stop, Reason} 86 | end. 87 | 88 | wait_req(Socket) -> 89 | receive 90 | {tcp, Socket, Bin} -> io:format("REQ: ~p~n", [Bin]) 91 | end. 92 | 93 | send(Socket, N) -> 94 | gen_tcp:send(Socket, [integer_to_list(N), ":abcdefghijklmnopqrstuvwxyz\r\n"]), 95 | receive 96 | {tcp_closed, Socket} -> ok 97 | after 100 -> 98 | send(Socket, N + 1) 99 | end. --------------------------------------------------------------------------------