├── inetrc ├── .gitignore ├── include ├── sserl.hrl └── shadowsocks.hrl ├── src ├── sserl.app.src ├── sserl_app.erl ├── sserl_sup.erl ├── sserl_listener_sup.erl ├── sserl_stat.erl ├── sserl_manager.erl ├── shadowsocks_crypt.erl ├── sserl_listener.erl ├── sserl_sync_flow.erl └── sserl_conn.erl ├── rebar.config ├── LICENSE ├── README.md └── priv └── sserl.schema /inetrc: -------------------------------------------------------------------------------- 1 | %% -*- mode:erlang -*- 2 | {lookup, [dns]}. 3 | {timeout, 5000}. 4 | {nameserver, {127,0,0,1}, 53}. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .rebar3 2 | _* 3 | .eunit 4 | *.o 5 | *.beam 6 | *.plt 7 | *.swp 8 | *.swo 9 | .erlang.cookie 10 | ebin 11 | log 12 | erl_crash.dump 13 | .rebar 14 | logs 15 | _build 16 | rebar.lock 17 | Mnesia*/ 18 | -------------------------------------------------------------------------------- /include/sserl.hrl: -------------------------------------------------------------------------------- 1 | %% flow event report: {report, Port, Download, Upload} 2 | %% 3 | -define(FLOW_EVENT, sserl_flow_event). 4 | 5 | %% stat event 6 | %% new listener {listener, new, Port} 7 | %% accept : {listener, accept, Addr, Port} 8 | %% open : {conn, open, Pid} 9 | %% close : {conn, close, Pid, Reason} 10 | %% connect : {conn, connect, Addr, Port} 11 | %% 12 | -define(STAT_EVENT, sserl_stat_event). 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/sserl.app.src: -------------------------------------------------------------------------------- 1 | {application, sserl, 2 | [{description, "An OTP application"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {mod, { sserl_app, []}}, 6 | {applications, 7 | [kernel, 8 | stdlib, 9 | mysql_poolboy, 10 | mnesia 11 | ]}, 12 | {env,[]}, 13 | {modules, [sserl_manager, sserl_listener, sserl_conn, sserl_listener_sup]}, 14 | 15 | {maintainers, []}, 16 | {licenses, []}, 17 | {links, []}, 18 | {beegzi_status, [ 19 | {running_ports, "running ss ports", {sserl_manager, status, []}} 20 | ]} 21 | ]}. 22 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode:erlang -*- 2 | {erl_opts, [debug_info]}. 3 | {plugins, [ 4 | {beegzi_plugin, {git, "http://git.hupaul.com/paul/beegzi_plugin.git", {branch, "master"}}} 5 | ]}. 6 | {deps, [ 7 | %% {jsx, {git, "https://github.com/talentdeficit/jsx.git", {branch, "v2.8.0"}}}, 8 | {mnesia_cluster, {git, "https://github.com/paulzql/mnesia_cluster.git", {branch, "master"}}}, 9 | {mysql_poolboy, {git, "https://github.com/mysql-otp/mysql-otp-poolboy.git", {branch, "master"}}} 10 | %% {ranch, {git, "https://github.com/ninenines/ranch.git", {branch, "master"}}} 11 | ]}. 12 | 13 | {relx, [{release, {sserl, "0.1.0"}, 14 | [ 15 | sserl, 16 | poolboy, 17 | mysql, 18 | mysql_poolboy 19 | ] }] 20 | }. 21 | -------------------------------------------------------------------------------- /src/sserl_app.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc sserl public API 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | 6 | -module(sserl_app). 7 | 8 | -behaviour(application). 9 | 10 | %% Application callbacks 11 | -export([start/2, stop/1]). 12 | 13 | %%==================================================================== 14 | %% API 15 | %%==================================================================== 16 | 17 | start(_StartType, _StartArgs) -> 18 | case sserl_sup:start_link() of 19 | {ok, Pid} -> 20 | {ok, Pid}; 21 | Error -> 22 | Error 23 | end. 24 | 25 | %%-------------------------------------------------------------------- 26 | stop(_State) -> 27 | ok. 28 | 29 | %%==================================================================== 30 | %% Internal functions 31 | %%==================================================================== 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, paul . 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | * The names of its contributors may not be used to endorse or promote 16 | products derived from this software without specific prior written 17 | permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /include/shadowsocks.hrl: -------------------------------------------------------------------------------- 1 | %% Definition of macros and constants for socksv5 2 | -define(SOCKS5_VER, 16#05). 3 | 4 | -define(SOCKS5_AUTH_NONE, 16#00). 5 | -define(SOCKS5_AUTH_GSSAPI, 16#01). 6 | -define(SOCKS5_AUTH_USER, 16#02). 7 | -define(SOCKS5_AUTH_ERR, 16#ff). 8 | 9 | -define(SOCKS5_REQ_CONNECT, 16#01). 10 | -define(SOCKS5_REQ_BIND, 16#02). 11 | -define(SOCKS5_REQ_UDP_ASSOC,16#03). 12 | 13 | -define(SOCKS5_ATYP_V4, 16#01). 14 | -define(SOCKS5_ATYP_DOM, 16#03). 15 | -define(SOCKS5_ATYP_V6, 16#04). 16 | 17 | -define(SOCKS5_REP_OK, 16#00). 18 | -define(SOCKS5_REP_FAIL, 16#01). 19 | -define(SOCKS5_REP_NOT_ALLOWED, 16#02). 20 | -define(SOCKS5_REP_NET_UNREACHABLE, 16#03). 21 | -define(SOCKS5_REP_HOST_UNREACHABLE, 16#04). 22 | -define(SOCKS5_REP_REFUSED, 16#05). 23 | -define(SOCKS5_REP_TTL_EXPIRED, 16#06). 24 | -define(SOCKS5_REP_CMD_NOT_SUPPORTED, 16#07). 25 | -define(SOCKS5_REP_ATYP_NOT_SUPPORTED, 16#08). 26 | 27 | -define(SOCKS5_RESERVED_FIELD, 16#00). 28 | 29 | -define(IS_OTA(Atyp), (Atyp band 16#10) =:= 16#10). 30 | -define(OTA_ATYP_V4, 16#11). 31 | -define(OTA_ATYP_V6, 16#14). 32 | -define(OTA_ATYP_DOM,16#13). 33 | -define(GET_ATYP(Atyp), Atyp band 16#0F). 34 | -define(OTA_FLAG, 16#10). 35 | 36 | -define(HMAC_LEN, 10). 37 | -define(OTA_HEAD_LEN, 12). 38 | 39 | %% cipher info 40 | -record(cipher_info, { 41 | method=rc4_md5, %% rc4_md5 | aes_128_cfb | des_cfb | aes_192_cfb | aes_256_cfb 42 | key, 43 | encode_iv, 44 | iv_sent = false, %% true | false 45 | decode_iv, 46 | stream_enc_state, %% used in AES CTR and RC4 mode 47 | stream_dec_state, %% used in AES CTR and RC4 mode 48 | enc_rest = <<>>, 49 | dec_rest = <<>> 50 | }). 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/sserl_sup.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc sserl top level supervisor. 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | 6 | -module(sserl_sup). 7 | 8 | -behaviour(supervisor). 9 | 10 | %% API 11 | -export([start_link/0]). 12 | 13 | %% Supervisor callbacks 14 | -export([init/1]). 15 | 16 | -include("sserl.hrl"). 17 | 18 | -define(SERVER, ?MODULE). 19 | 20 | 21 | %%==================================================================== 22 | %% API functions 23 | %%==================================================================== 24 | start_link() -> 25 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 26 | 27 | %%==================================================================== 28 | %% Supervisor callbacks 29 | %%==================================================================== 30 | 31 | %% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} 32 | init([]) -> 33 | FlowEvent = {?FLOW_EVENT, 34 | {gen_event, start_link, [{local, ?FLOW_EVENT}]}, 35 | permanent, 5000, worker, dynamic}, 36 | StatEvent = {?STAT_EVENT, 37 | {gen_event, start_link, [{local, ?STAT_EVENT}]}, 38 | permanent, 5000, worker, dynamic}, 39 | ListenerSup = {sserl_listener_sup, {sserl_listener_sup, start_link, []}, 40 | transient, brutal_kill, supervisor, [sserl_listener_sup]}, 41 | 42 | Manager = {sserl_manager, {sserl_manager, start_link, []}, 43 | transient, brutal_kill, worker, [sserl_port_manager]}, 44 | 45 | {ok, { {one_for_one, 2, 10}, 46 | [FlowEvent, StatEvent, ListenerSup, Manager]} }. 47 | 48 | %%==================================================================== 49 | %% Internal functions 50 | %%==================================================================== 51 | -------------------------------------------------------------------------------- /src/sserl_listener_sup.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc sserl listener supervisor. 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | 6 | -module(sserl_listener_sup). 7 | 8 | -behaviour(supervisor). 9 | 10 | %% API 11 | -export([start_link/0, start/1, stop/1, running_ports/0]). 12 | 13 | %% Supervisor callbacks 14 | -export([init/1]). 15 | 16 | -define(SERVER, ?MODULE). 17 | 18 | %%==================================================================== 19 | %% API functions 20 | %%==================================================================== 21 | start_link() -> 22 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 23 | 24 | start(Args) -> 25 | Port = proplists:get_value(port, Args), 26 | Children = supervisor:which_children(?SERVER), 27 | case [P || {_, P, _, _} <- Children, is_pid(P), sserl_listener:get_port(P) =:= Port] of 28 | [Pid] -> 29 | sserl_listener:update(Pid, Args); 30 | _ -> 31 | error_logger:info_msg("[~p] starting port ~p", [?MODULE, Args]), 32 | supervisor:start_child(?SERVER, [Args]) 33 | end. 34 | 35 | stop(Port) -> 36 | Children = supervisor:which_children(?SERVER), 37 | case [P || {_, P, _, _} <- Children, is_pid(P), sserl_listener:get_port(P) =:= Port] of 38 | [Pid] -> 39 | error_logger:info_msg("[~p] stopping port ~p", [?MODULE, Port]), 40 | supervisor:terminate_child(?SERVER, Pid), 41 | ok; 42 | _ -> 43 | ok 44 | end. 45 | 46 | running_ports() -> 47 | Children = supervisor:which_children(?SERVER), 48 | [sserl_listener:get_port(P) || {_, P, _, _} <- Children, is_pid(P)]. 49 | 50 | %%==================================================================== 51 | %% Supervisor callbacks 52 | %%==================================================================== 53 | 54 | %% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} 55 | init([]) -> 56 | {ok, { {simple_one_for_one, 1, 5}, 57 | [{sserl_listener, {sserl_listener, start_link, []}, 58 | transient, brutal_kill, worker, [sserl_listener]}]} }. 59 | 60 | %%==================================================================== 61 | %% Internal functions 62 | %%==================================================================== 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | shadowsocks-erlang (sserl) 2 | ===== 3 | 4 | shadowsocks-erlang is a erlang port of [shadowsocks](https://github.com/shadowsocks/shadowsocks) 5 | 6 | A fast tunnel proxy that helps you bypass firewalls. 7 | 8 | Features: 9 | - TCP support 10 | - Client support 11 | - Server support 12 | - OTA support 13 | - Mulit user support 14 | - User management API support 15 | - IP blacklist support [TODO] 16 | 17 | Encryption methods 18 | - table (deprecated) 19 | - rc4-md5 20 | - aes-128-cfb 21 | - aes-192-cfb 22 | - aes-256-cfb 23 | 24 | **Note: this is an erlang app/lib of shadowsocks, not an installable application. 25 | you can run with erlang VM or package in your erlang release** 26 | 27 | 28 | Build 29 | ----- 30 | 31 | $ rebar3 compile 32 | 33 | 34 | Usage 35 | ----- 36 | 37 | * start 38 | 39 | ```erlang 40 | application:start(sserl). 41 | ``` 42 | 43 | * stop 44 | 45 | ```erlang 46 | application:stop(sserl). 47 | ``` 48 | 49 | * start / update listener 50 | 51 | If the port already started,the method, password and expiretime will be updated. 52 | 53 | ```erlang 54 | Args = [{port, 8388}, {type, server}, {ota, false}, {method, "rc4-md5"},{password,"xx"}], 55 | sserl_listener_sup:start(Args). 56 | ``` 57 | 58 | * stop listener 59 | 60 | ```erlang 61 | sserl_listener_sup:stop(8388) 62 | ``` 63 | 64 | Configuration 65 | ----- 66 | 67 | sserl support [cuttlefish](https://github.com/basho/cuttlefish), you can easy configure with cuttlefish schema. 68 | 69 | * cuttlefish files 70 | 71 | `priv/sserl.schema` 72 | 73 | * env config 74 | 75 | ```erlang 76 | {sserl, 77 | [ 78 | {listener, [ %% a list of listener args 79 | [{ip, {0,0,0,0}}, %% listen ip [optional] 80 | {port, 1080} %% listen port 81 | {method, "rc4-md5"}, %% encryption method 82 | {password, "mypass"}, %% password 83 | {ota, true}, %% use OTA [optional] 84 | {expire_time, 1472402802}, %% expire time (unix time) [optional] 85 | {conn_limit, 1024}, %% max synchronize connections on the port [optional] 86 | {type, client}, %% listener type (client or server) 87 | {server, {"x-net.vip", 8388}} %% shadowsocks server (client only) [optional] 88 | ], 89 | [ %% other listeners 90 | ... 91 | ] 92 | ]}, 93 | %% -------------------------------------- 94 | %% shadowsocks-panel integration support 95 | %% -------------------------------------- 96 | {sync_enable, false}, %% enable synchronize users from ss-panel 97 | {sync_node_id, 1}, %% the node id on ss-panel 98 | {sync_mysql_host, "x-net.vip"}, 99 | {sync_mysql_port, 3306}, 100 | {sync_mysql_db, "ss-panel"}, 101 | {sync_mysql_user, "root"}, 102 | {sync_mysql_pass, "123456"}, 103 | {sync_report_min, 1048576} %% bytes threshold to report flow 104 | ]} 105 | ``` 106 | 107 | Cluster 108 | ----- 109 | 110 | sserl will share the flow data with joined cluster 111 | 112 | * Start cluster 113 | 114 | ```erlang 115 | mnesia_cluster:join('xx@xx.com'). 116 | ``` 117 | 118 | * Leave cluster 119 | 120 | ```erlang 121 | mensia_cluster:leave(). 122 | ``` 123 | 124 | Events 125 | ----- 126 | 127 | ```erlang 128 | %% flow event report: {report, Port, Download, Upload} 129 | %% 130 | -define(FLOW_EVENT, sserl_flow_event). 131 | 132 | %% stat event 133 | %% new listener {listener, new, Port} 134 | %% accept : {listener, accept, Addr, Port} 135 | %% open : {conn, open, Pid} 136 | %% close : {conn, close, Pid, Reason} 137 | %% connect : {conn, connect, Addr, Port} 138 | %% 139 | -define(STAT_EVENT, sserl_stat_event). 140 | ``` 141 | 142 | License 143 | ----- 144 | 145 | BSD 146 | -------------------------------------------------------------------------------- /priv/sserl.schema: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang -*- 2 | 3 | %% ================================================= 4 | %% local setting 5 | %% ================================================= 6 | {mapping, "ss.listener.$name.type", "sserl.listener", [ 7 | {datatype, {enum, [{client,client}, {server,server}]}} 8 | ]}. 9 | %% @doc enable one time auth 10 | {mapping, "ss.listener.$name.ota", "sserl.listener", [ 11 | {datatype, {flag, true, false}} 12 | ]}. 13 | 14 | %% @doc ss listen ip and port. 15 | %% eg: 0.0.0.0:8388 16 | {mapping, "ss.listener.$name.local", "sserl.listener", [ 17 | {datatype, ip} 18 | ]}. 19 | %% @doc server addr 20 | %% eg: www.ss.com:8388 21 | {mapping, "ss.listener.$name.server", "sserl.listener", [ 22 | {datatype, [ip,string]} 23 | ]}. 24 | 25 | %% @doc encrypt method. 26 | %% eg: aes-256-cfb 27 | {mapping, "ss.listener.$name.method", "sserl.listener", [ 28 | {datatype, string}, 29 | {default, "rc4-md5"} 30 | ]}. 31 | 32 | %% @doc password. 33 | {mapping, "ss.listener.$name.password", "sserl.listener", [ 34 | {datatype, string} 35 | ]}. 36 | 37 | {translation,"sserl.listener", 38 | fun(Conf) -> 39 | ConfList = cuttlefish_variable:filter_by_prefix("ss.listener", Conf), 40 | Map = lists:foldl(fun({[_,_,Name,"local"], {Addr, Port}}, Acc) -> 41 | case catch [list_to_integer(N) || N <- string:tokens(Addr, ".")] of 42 | [IP1,IP2,IP3,IP4] when is_integer(IP1),is_integer(IP2),is_integer(IP3),is_integer(IP4) -> 43 | maps:put(Name, [{ip, {IP1,IP2,IP3,IP4}},{port,Port}|maps:get(Name, Acc, [])], Acc); 44 | _ -> 45 | throw({error, bad_local_ip}) 46 | end; 47 | ({[_,_,Name,"server"], {Addr,Port}}, Acc) -> 48 | maps:put(Name, [{server, {Addr,Port}}|maps:get(Name, Acc, [])], Acc); 49 | ({[_,_,Name,"server"], Addr}, Acc) -> 50 | case string:tokens(Addr,":") of 51 | [IP, Port] -> 52 | maps:put(Name, [{server, {IP,list_to_integer(Port)}}|maps:get(Name, Acc, [])], Acc); 53 | _ -> 54 | throw({error, invalid_key_server}) 55 | end; 56 | ({[_,_,Name, Attr], Value}, Acc) -> 57 | maps:put(Name, [{list_to_atom(Attr), Value}|maps:get(Name, Acc, [])], Acc); 58 | (_, Acc) -> 59 | Acc 60 | end, #{}, ConfList), 61 | maps:values(Map) 62 | end}. 63 | 64 | %% ================================================= 65 | %% sync setting 66 | %% ================================================= 67 | %% @doc is sync enabled 68 | {mapping, "ss.sync", "sserl.sync_enabled", [ 69 | {datatype, {enum, [true, false]}}, 70 | {default, false} 71 | ]}. 72 | 73 | %% @doc node id (same with web) 74 | {mapping, "ss.node_id", "sserl.sync_node_id", [ 75 | {datatype, integer} 76 | ]}. 77 | 78 | %% @doc mysql host 79 | {mapping, "ss.mysql.host", "sserl.sync_mysql_host", [ 80 | {datatype, string} 81 | ]}. 82 | %% @doc mysql port 83 | {mapping, "ss.mysql.port", "sserl.sync_mysql_port", [ 84 | {datatype, integer}, 85 | {default, 3306} 86 | ]}. 87 | 88 | %% @doc mysql database 89 | {mapping, "ss.mysql.db", "sserl.sync_mysql_db", [ 90 | {datatype, string} 91 | ]}. 92 | 93 | %% @doc mysql user 94 | {mapping, "ss.mysql.user", "sserl.sync_mysql_user", [ 95 | {datatype, string} 96 | ]}. 97 | 98 | %% @doc mysql password 99 | {mapping, "ss.mysql.pass", "sserl.sync_mysql_pass", [ 100 | {datatype, string} 101 | ]}. 102 | 103 | %% @doc min size flow to report 104 | %% default: 1MB 105 | {mapping, "ss.report_min", "sserl.sync_report_min", [ 106 | {datatype, integer}, 107 | {default, 1048576} 108 | ]}. 109 | -------------------------------------------------------------------------------- /src/sserl_stat.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author paul 3 | %%% @copyright (C) 2016, paul 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 15 May 2016 by paul 8 | %%%------------------------------------------------------------------- 9 | -module(sserl_stat). 10 | 11 | -behaviour(gen_event). 12 | 13 | %% API 14 | -export([add_handler/0]). 15 | 16 | %% gen_event callbacks 17 | -export([init/1, handle_event/2, handle_call/2, 18 | handle_info/2, terminate/2, code_change/3]). 19 | 20 | -include("sserl.hrl"). 21 | -define(SERVER, ?MODULE). 22 | 23 | -record(state, {}). 24 | 25 | %%%=================================================================== 26 | %%% API 27 | %%%=================================================================== 28 | 29 | %%-------------------------------------------------------------------- 30 | %% @doc 31 | %% Adds an event handler 32 | %% 33 | %% @spec add_handler() -> ok | {'EXIT', Reason} | term() 34 | %% @end 35 | %%-------------------------------------------------------------------- 36 | add_handler() -> 37 | gen_event:add_handler(?STAT_EVENT, ?MODULE, []). 38 | 39 | %%%=================================================================== 40 | %%% gen_event callbacks 41 | %%%=================================================================== 42 | 43 | %%-------------------------------------------------------------------- 44 | %% @private 45 | %% @doc 46 | %% Whenever a new event handler is added to an event manager, 47 | %% this function is called to initialize the event handler. 48 | %% 49 | %% @spec init(Args) -> {ok, State} 50 | %% @end 51 | %%-------------------------------------------------------------------- 52 | init([]) -> 53 | {ok, #state{}}. 54 | 55 | %%-------------------------------------------------------------------- 56 | %% @private 57 | %% @doc 58 | %% Whenever an event manager receives an event sent using 59 | %% gen_event:notify/2 or gen_event:sync_notify/2, this function is 60 | %% called for each installed event handler to handle the event. 61 | %% 62 | %% @spec handle_event(Event, State) -> 63 | %% {ok, State} | 64 | %% {swap_handler, Args1, State1, Mod2, Args2} | 65 | %% remove_handler 66 | %% @end 67 | %%-------------------------------------------------------------------- 68 | handle_event(Event, State) -> 69 | io:format("Event:~p~n", [Event]), 70 | {ok, State}. 71 | 72 | %%-------------------------------------------------------------------- 73 | %% @private 74 | %% @doc 75 | %% Whenever an event manager receives a request sent using 76 | %% gen_event:call/3,4, this function is called for the specified 77 | %% event handler to handle the request. 78 | %% 79 | %% @spec handle_call(Request, State) -> 80 | %% {ok, Reply, State} | 81 | %% {swap_handler, Reply, Args1, State1, Mod2, Args2} | 82 | %% {remove_handler, Reply} 83 | %% @end 84 | %%-------------------------------------------------------------------- 85 | handle_call(_Request, State) -> 86 | Reply = ok, 87 | {ok, Reply, State}. 88 | 89 | %%-------------------------------------------------------------------- 90 | %% @private 91 | %% @doc 92 | %% This function is called for each installed event handler when 93 | %% an event manager receives any other message than an event or a 94 | %% synchronous request (or a system message). 95 | %% 96 | %% @spec handle_info(Info, State) -> 97 | %% {ok, State} | 98 | %% {swap_handler, Args1, State1, Mod2, Args2} | 99 | %% remove_handler 100 | %% @end 101 | %%-------------------------------------------------------------------- 102 | handle_info(_Info, State) -> 103 | {ok, State}. 104 | 105 | %%-------------------------------------------------------------------- 106 | %% @private 107 | %% @doc 108 | %% Whenever an event handler is deleted from an event manager, this 109 | %% function is called. It should be the opposite of Module:init/1 and 110 | %% do any necessary cleaning up. 111 | %% 112 | %% @spec terminate(Reason, State) -> void() 113 | %% @end 114 | %%-------------------------------------------------------------------- 115 | terminate(_Reason, _State) -> 116 | ok. 117 | 118 | %%-------------------------------------------------------------------- 119 | %% @private 120 | %% @doc 121 | %% Convert process state when code is changed 122 | %% 123 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 124 | %% @end 125 | %%-------------------------------------------------------------------- 126 | code_change(_OldVsn, State, _Extra) -> 127 | {ok, State}. 128 | 129 | %%%=================================================================== 130 | %%% Internal functions 131 | %%%=================================================================== 132 | -------------------------------------------------------------------------------- /src/sserl_manager.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author paul 3 | %%% @copyright (C) 2016, paul 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 15 Aug 2016 by paul 8 | %%%------------------------------------------------------------------- 9 | -module(sserl_manager). 10 | 11 | -behaviour(gen_server). 12 | 13 | %% API 14 | -export([start_link/0, status/0]). 15 | 16 | %% gen_server callbacks 17 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 18 | terminate/2, code_change/3]). 19 | 20 | -include("sserl.hrl"). 21 | 22 | -define(SERVER, ?MODULE). 23 | 24 | -record(state, { 25 | }). 26 | 27 | %%%=================================================================== 28 | %%% API 29 | %%%=================================================================== 30 | status() -> 31 | [ [{port, Port}, {running, true}] || Port <- sserl_listener_sup:running_ports()]. 32 | 33 | %%-------------------------------------------------------------------- 34 | %% @doc 35 | %% Starts the server 36 | %% 37 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 38 | %% @end 39 | %%-------------------------------------------------------------------- 40 | start_link() -> 41 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 42 | 43 | 44 | %%%=================================================================== 45 | %%% gen_server callbacks 46 | %%%=================================================================== 47 | 48 | %%-------------------------------------------------------------------- 49 | %% @private 50 | %% @doc 51 | %% Initializes the server 52 | %% 53 | %% @spec init(Args) -> {ok, State} | 54 | %% {ok, State, Timeout} | 55 | %% ignore | 56 | %% {stop, Reason} 57 | %% @end 58 | %%-------------------------------------------------------------------- 59 | init([]) -> 60 | load_local_ports(), 61 | case sserl_sync_flow:add_handler() of 62 | {'EXIT', Reason} -> 63 | throw(Reason); 64 | _ -> ok 65 | end, 66 | sserl_stat:add_handler(), 67 | {ok, #state{}}. 68 | 69 | %%-------------------------------------------------------------------- 70 | %% @private 71 | %% @doc 72 | %% Handling call messages 73 | %% 74 | %% @spec handle_call(Request, From, State) -> 75 | %% {reply, Reply, State} | 76 | %% {reply, Reply, State, Timeout} | 77 | %% {noreply, State} | 78 | %% {noreply, State, Timeout} | 79 | %% {stop, Reason, Reply, State} | 80 | %% {stop, Reason, State} 81 | %% @end 82 | %%-------------------------------------------------------------------- 83 | handle_call(_Request, _From, State) -> 84 | Reply = ok, 85 | {reply, Reply, State}. 86 | 87 | %%-------------------------------------------------------------------- 88 | %% @private 89 | %% @doc 90 | %% Handling cast messages 91 | %% 92 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 93 | %% {noreply, State, Timeout} | 94 | %% {stop, Reason, State} 95 | %% @end 96 | %%-------------------------------------------------------------------- 97 | handle_cast(_Msg, State) -> 98 | {noreply, State}. 99 | 100 | %%-------------------------------------------------------------------- 101 | %% @private 102 | %% @doc 103 | %% Handling all non call/cast messages 104 | %% 105 | %% @spec handle_info(Info, State) -> {noreply, State} | 106 | %% {noreply, State, Timeout} | 107 | %% {stop, Reason, State} 108 | %% @end 109 | %%-------------------------------------------------------------------- 110 | handle_info(_Info, State) -> 111 | {noreply, State}. 112 | 113 | %%-------------------------------------------------------------------- 114 | %% @private 115 | %% @doc 116 | %% This function is called by a gen_server when it is about to 117 | %% terminate. It should be the opposite of Module:init/1 and do any 118 | %% necessary cleaning up. When it returns, the gen_server terminates 119 | %% with Reason. The return value is ignored. 120 | %% 121 | %% @spec terminate(Reason, State) -> void() 122 | %% @end 123 | %%-------------------------------------------------------------------- 124 | terminate(_Reason, _State) -> 125 | ok. 126 | 127 | %%-------------------------------------------------------------------- 128 | %% @private 129 | %% @doc 130 | %% Convert process state when code is changed 131 | %% 132 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 133 | %% @end 134 | %%-------------------------------------------------------------------- 135 | code_change(_OldVsn, State, _Extra) -> 136 | {ok, State}. 137 | 138 | %%%=================================================================== 139 | %%% Internal functions 140 | %%%=================================================================== 141 | load_local_ports() -> 142 | lists:map(fun(C) -> 143 | case sserl_listener_sup:start(C) of 144 | {ok, _} -> 145 | ok; 146 | E -> 147 | throw(E) 148 | end 149 | end, application:get_env(sserl, listener, [])), 150 | ok. 151 | -------------------------------------------------------------------------------- /src/shadowsocks_crypt.erl: -------------------------------------------------------------------------------- 1 | -module(shadowsocks_crypt). 2 | 3 | %% API 4 | -export([methods/0, init_cipher_info/2, encode/2, decode/2, key_iv_len/1, stream_init/3, hmac/2]). 5 | 6 | -include("shadowsocks.hrl"). 7 | 8 | methods() -> 9 | [rc4_md5, aes_128_cfb, aes_192_cfb, aes_256_cfb, none]. 10 | 11 | %%-------------------------------------------------------------------- 12 | %% @doc 13 | %% Return the cipher information 14 | %% 15 | %% @spec cipher_info(Method, Password::string()) -> #cipher_info{}. 16 | %% 17 | %% Method := rc4_md5 | des_cfb | | aes_cfb128 18 | %% @end 19 | %%-------------------------------------------------------------------- 20 | init_cipher_info(none, _) -> 21 | #cipher_info{method=none}; 22 | 23 | init_cipher_info(Method, Password) -> 24 | {KeyLen, IvLen} = key_iv_len(Method), 25 | {Key, _NewIv} = evp_bytestokey(Password, KeyLen, IvLen), 26 | %% use another random Iv, but not the one returned from evp_bytestokey() 27 | NewIv = crypto:rand_bytes(IvLen), 28 | #cipher_info{method=Method, key=Key, encode_iv=NewIv, decode_iv=undefined, 29 | stream_enc_state = stream_init(Method, Key, NewIv)}. 30 | 31 | %%-------------------------------------------------------------------- 32 | %% @doc 33 | %% Encode function 34 | %% @spec encode(CipherInfo, Data) -> Data 35 | %% CipherInfo := cipher_info() 36 | %% Data := iolist() | binary() 37 | %% @end 38 | %%-------------------------------------------------------------------- 39 | encode(#cipher_info{method=none}, Data) -> 40 | Data; 41 | 42 | encode(#cipher_info{iv_sent = false, encode_iv=Iv}=CipherInfo, Data) -> 43 | NewCipherInfo = CipherInfo#cipher_info{iv_sent=true}, 44 | {NewCipherInfo1, EncData} = encode(NewCipherInfo, Data), 45 | {NewCipherInfo1, <>}; 46 | 47 | encode(#cipher_info{method=rc4_md5, stream_enc_state=S}=CipherInfo, Data) -> 48 | {S1, EncData} = crypto:stream_encrypt(S, Data), 49 | {CipherInfo#cipher_info{stream_enc_state=S1}, EncData}; 50 | %% aes_128_cfb | aes_192_cfb | aes_256_cfb 51 | encode(#cipher_info{method=_Method, key=Key, encode_iv=Iv, enc_rest=Rest}=CipherInfo, Data) -> 52 | DataSize = size(Data), 53 | RestSize = size(Rest), 54 | BufLen = (DataSize+RestSize) div 16 * 16, 55 | 56 | <> = <>, 57 | EncData = crypto:block_encrypt(aes_cfb128, Key, Iv, Data2), 58 | NewIv = binary:part(<>, size(EncData)+16, -16), 59 | EncRest = crypto:block_encrypt(aes_cfb128, Key, NewIv, Rest2), 60 | Result = binary:part(<>, RestSize, DataSize), 61 | {CipherInfo#cipher_info{encode_iv=NewIv, enc_rest=Rest2}, Result}. 62 | 63 | 64 | %%-------------------------------------------------------------------- 65 | %% @doc 66 | %% Decode function 67 | %% @spec decode(CipherInfo, Data) -> Data 68 | %% CipherInfo := {default, EncTable::list(), DecTable::list()} | 69 | %% {Method, Key::binary(), Iv::binary()} 70 | %% Method := default | rc4 | des_cfb 71 | %% Data := iolist() | binary() 72 | %% @end 73 | %%-------------------------------------------------------------------- 74 | decode(#cipher_info{method=none}, Data) -> 75 | Data; 76 | 77 | %% recv iv 78 | decode(CipherInfo=#cipher_info{method=M, decode_iv=undefined, dec_rest=Rest}, EncData) -> 79 | {_, IvLen} = key_iv_len(M), 80 | case <> of 81 | Rest1 when byte_size(Rest1) >= IvLen -> 82 | <> = Rest1, 83 | StreamState = shadowsocks_crypt:stream_init(M, CipherInfo#cipher_info.key, Iv), 84 | decode(CipherInfo#cipher_info{decode_iv=Iv, stream_dec_state=StreamState, dec_rest= <<>>}, Rest2); 85 | Rest1 -> 86 | {CipherInfo#cipher_info{dec_rest=Rest1}} 87 | end; 88 | 89 | decode(#cipher_info{method=rc4_md5, stream_dec_state=S}=CipherInfo, EncData) -> 90 | {S1, Data} = crypto:stream_decrypt(S, EncData), 91 | {CipherInfo#cipher_info{stream_dec_state=S1}, Data}; 92 | 93 | %% aes_128_cfb | aes_192_cfb | aes_256_cfb 94 | decode(#cipher_info{method=_Method, key=Key, decode_iv=Iv, dec_rest=Rest}=CipherInfo, EncData) -> 95 | DataSize = size(EncData), 96 | RestSize = size(Rest), 97 | BufLen = (DataSize+RestSize) div 16 * 16, 98 | <> = <>, 99 | 100 | Data = crypto:block_decrypt(aes_cfb128, Key, Iv, EncData2), 101 | NewIv = binary:part(<>, size(EncData2)+16, -16), 102 | DecRest = crypto:block_decrypt(aes_cfb128, Key, NewIv, Rest2), 103 | Result = binary:part(<>, RestSize, DataSize), 104 | 105 | {CipherInfo#cipher_info{decode_iv=NewIv, dec_rest=Rest2}, Result}. 106 | 107 | hmac(Key, Data) -> 108 | crypto:hmac(sha, Key, Data, ?HMAC_LEN). 109 | 110 | %%-------------------------------------------------------------------- 111 | %% @doc 112 | %% Creates a key and an IV for doing encryption, from a password, 113 | %% using a hashing function. 114 | %% @spec evp_bytestokey(HashMethod::hash_method(), Password::string(), 115 | %% KeyLen::integer(), IvLen::integer()) -> 116 | %% {Key::binary(), Iv::binary()} 117 | %% @end 118 | %%-------------------------------------------------------------------- 119 | evp_bytestokey(Password, KeyLen, IvLen) -> 120 | evp_bytestokey_aux(list_to_binary(Password), KeyLen, IvLen, <<>>). 121 | 122 | evp_bytestokey_aux(_, KeyLen, IvLen, Acc) when KeyLen + IvLen =< size(Acc) -> 123 | <> = Acc, 124 | {Key, Iv}; 125 | 126 | evp_bytestokey_aux(Password, KeyLen, IvLen, Acc) -> 127 | Digest = crypto:hash(md5, <>), 128 | NewAcc = <>, 129 | evp_bytestokey_aux(Password, KeyLen, IvLen, NewAcc). 130 | 131 | 132 | key_iv_len(none) -> 133 | {0, 0}; 134 | key_iv_len(rc4_md5) -> 135 | {16, 16}; 136 | key_iv_len(aes_128_cfb) -> 137 | {16, 16}; 138 | key_iv_len(aes_192_cfb) -> 139 | {24, 16}; 140 | key_iv_len(aes_256_cfb) -> 141 | {32, 16}; 142 | key_iv_len(chacha20) -> 143 | {32, 8}. 144 | 145 | 146 | stream_init(rc4_md5, Key, Iv) -> 147 | crypto:stream_init(rc4, crypto:hash(md5, <>)); 148 | stream_init(_, _, _) -> 149 | undefined. 150 | 151 | %% 测试 152 | -ifdef(TEST). 153 | -include_lib("eunit/include/eunit.hrl"). 154 | 155 | rc4_test() -> 156 | 157 | Cipher = init_cipher_info(aes_128_cfb, "xx"), 158 | Data1 = <<"hello world">>, 159 | Data2 = <<"baby">>, 160 | {Cipher1, EnData1} = encode(Cipher, Data1), 161 | {Cipher2, EnData2} = encode(Cipher1, Data2), 162 | IV = Cipher1#cipher_info.encode_iv, 163 | {Cipher3, <<_IV:16/binary, DeData1/binary>>} = decode(Cipher1#cipher_info{decode_iv=IV}, EnData1), 164 | {_, DeData2} = decode(Cipher3, EnData2), 165 | io:format("~p~n", [DeData1]), 166 | {0,11} = binary:match(Data1, [DeData1],[]), 167 | {0,4} = binary:match(Data2, [DeData2],[]), 168 | ok. 169 | -endif. 170 | -------------------------------------------------------------------------------- /src/sserl_listener.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author paul 3 | %%% @copyright (C) 2016, paul 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 15 May 2016 by paul 8 | %%%------------------------------------------------------------------- 9 | -module(sserl_listener). 10 | 11 | -behaviour(gen_server). 12 | 13 | %% API 14 | -export([start_link/1, get_port/1, update/2]). 15 | 16 | %% gen_server callbacks 17 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 18 | terminate/2, code_change/3]). 19 | 20 | -include("sserl.hrl"). 21 | 22 | -define(SERVER, ?MODULE). 23 | -define(MAX_LIMIT, 16#0FFFFFFFFFFFFFFF). 24 | 25 | -record(state, { 26 | ota, % one time auth 27 | port, % listen port 28 | lsocket, % listen socket 29 | conn_limit, % connection count limit 30 | expire_time, % expire time 31 | password, % 32 | method, % 33 | accepting, % is accepting new connection? 34 | conns = 0, % current connection count 35 | expire_timer = undefined, % expire timer 36 | type = server, 37 | server = undefined % server {address, port} 38 | }). 39 | 40 | %%%=================================================================== 41 | %%% API 42 | %%%=================================================================== 43 | 44 | %%-------------------------------------------------------------------- 45 | %% @doc 46 | %% Starts the server 47 | %% 48 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 49 | %% @end 50 | %%-------------------------------------------------------------------- 51 | start_link(Args) -> 52 | %% get configs 53 | Type = proplists:get_value(type, Args, server), 54 | IP = proplists:get_value(ip, Args), 55 | Port = proplists:get_value(port, Args), 56 | ConnLimit = proplists:get_value(conn_limit, Args, ?MAX_LIMIT), 57 | ExpireTime= proplists:get_value(expire_time, Args, max_time()), 58 | OTA = proplists:get_value(ota, Args, false), 59 | Password = proplists:get_value(password, Args), 60 | Method = parse_method(proplists:get_value(method, Args, rc4_md5)), 61 | CurrTime = os:system_time(milli_seconds), 62 | Server = proplists:get_value(server, Args), 63 | %% validate args 64 | ValidMethod = lists:any(fun(M) -> M =:= Method end, shadowsocks_crypt:methods()), 65 | if 66 | Type =/=server andalso Type =/= client -> 67 | {error, {badargs, invalid_type}}; 68 | Type =:= client andalso Server =:= undefined -> 69 | {error, {badargs, client_need_server}}; 70 | Port < 0 orelse Port > 65535 -> 71 | {error, {badargs, port_out_of_range}}; 72 | not is_integer(ConnLimit) -> 73 | {error, {badargs, conn_limit_need_integer}}; 74 | not ValidMethod -> 75 | {error, {badargs, unsupported_method}}; 76 | not is_list(Password) -> 77 | {error, {badargs, password_need_list}}; 78 | CurrTime >= ExpireTime -> 79 | {error, expired}; 80 | true -> 81 | State = #state{ota=OTA, port=Port, lsocket=undefined, 82 | type = Type, 83 | conn_limit=ConnLimit, 84 | expire_time=ExpireTime, 85 | password=Password, method=Method, 86 | server = Server, 87 | expire_timer=erlang:start_timer(max_time(ExpireTime), self(), expire, [{abs,true}])}, 88 | gen_server:start_link(?MODULE, [State,IP], []) 89 | end. 90 | 91 | get_port(Pid) -> 92 | gen_server:call(Pid, get_port). 93 | 94 | update(Pid, Args) -> 95 | gen_server:call(Pid, {update, Args}). 96 | 97 | %%%=================================================================== 98 | %%% gen_server callbacks 99 | %%%=================================================================== 100 | 101 | %%-------------------------------------------------------------------- 102 | %% @private 103 | %% @doc 104 | %% Initializes the server 105 | %% 106 | %% @spec init(Args) -> {ok, State} | 107 | %% {ok, State, Timeout} | 108 | %% ignore | 109 | %% {stop, Reason} 110 | %% @end 111 | %%-------------------------------------------------------------------- 112 | init([State,IP]) -> 113 | process_flag(trap_exit, true), 114 | 115 | Opts = [binary, {backlog, 20},{nodelay, true}, {active, false}, 116 | {packet, raw}, {reuseaddr, true},{send_timeout_close, true}], 117 | %% get the ip address 118 | Opts1 = case IP of 119 | undefined -> 120 | Opts; 121 | Addr -> 122 | Opts++[{ip, Addr}] 123 | end, 124 | %% start listen 125 | case gen_tcp:listen(State#state.port, Opts1) of 126 | {ok, LSocket} -> 127 | %% set to async accept, so we can do many things on this process 128 | case prim_inet:async_accept(LSocket, -1) of 129 | {ok, _} -> 130 | gen_event:notify(?STAT_EVENT, {listener, new, State#state.port}), 131 | {ok, State#state{lsocket=LSocket}}; 132 | {error, Error} -> 133 | {stop, Error} 134 | end; 135 | Error -> 136 | {stop, Error} 137 | end. 138 | 139 | 140 | %%-------------------------------------------------------------------- 141 | %% @private 142 | %% @doc 143 | %% Handling call messages 144 | %% 145 | %% @spec handle_call(Request, From, State) -> 146 | %% {reply, Reply, State} | 147 | %% {reply, Reply, State, Timeout} | 148 | %% {noreply, State} | 149 | %% {noreply, State, Timeout} | 150 | %% {stop, Reason, Reply, State} | 151 | %% {stop, Reason, State} 152 | %% @end 153 | %%-------------------------------------------------------------------- 154 | handle_call(get_port, _From, State=#state{port=Port}) -> 155 | {reply, Port, State}; 156 | 157 | %% update args 158 | handle_call({update, Args}, _From, State) -> 159 | ConnLimit = proplists:get_value(conn_limit, Args, State#state.conn_limit), 160 | ExpireTime = proplists:get_value(expire_time, Args, State#state.expire_time), 161 | Password = proplists:get_value(password, Args, State#state.password), 162 | Method = parse_method(proplists:get_value(method, Args, State#state.method)), 163 | %% reset expire timer 164 | erlang:cancel_timer(State#state.expire_timer, []), 165 | ExpireTimer = erlang:start_timer(max_time(ExpireTime), self(), expire, [{abs,true}]), 166 | 167 | {reply, ok, State#state{conn_limit = ConnLimit, 168 | expire_time= ExpireTime, 169 | password = Password, 170 | method = Method, 171 | expire_timer=ExpireTimer 172 | }}; 173 | 174 | handle_call(_Request, _From, State) -> 175 | Reply = ok, 176 | {reply, Reply, State}. 177 | 178 | %%-------------------------------------------------------------------- 179 | %% @private 180 | %% @doc 181 | %% Handling cast messages 182 | %% 183 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 184 | %% {noreply, State, Timeout} | 185 | %% {stop, Reason, State} 186 | %% @end 187 | %%-------------------------------------------------------------------- 188 | handle_cast(_Msg, State) -> 189 | {noreply, State}. 190 | 191 | %%-------------------------------------------------------------------- 192 | %% @private 193 | %% @doc 194 | %% Handling all non call/cast messages 195 | %% 196 | %% @spec handle_info(Info, State) -> {noreply, State} | 197 | %% {noreply, State, Timeout} | 198 | %% {stop, Reason, State} 199 | %% @end 200 | %%-------------------------------------------------------------------- 201 | %% 超过使用期,停止进程 202 | handle_info({timeout, _Ref, expire}, State) -> 203 | {stop, expire, State}; 204 | 205 | handle_info({inet_async, _LSocket, _Ref, {ok, CSocket}}, 206 | State=#state{ota=OTA, port=Port, type=Type, 207 | method=Method,password=Password, server=Server, conns=Conns}) -> 208 | true = inet_db:register_socket(CSocket, inet_tcp), 209 | {ok, {Addr, _}} = inet:peername(CSocket), 210 | gen_event:notify(?STAT_EVENT, {listener, accept, Port, Addr}), 211 | 212 | {ok, Pid} = sserl_conn:start_link(CSocket, {Port, Server, OTA, Type, {Method, Password}}), 213 | 214 | case gen_tcp:controlling_process(CSocket, Pid) of 215 | ok -> 216 | gen_event:notify(?STAT_EVENT, {conn, open, Pid}), 217 | Pid ! {shoot, CSocket}; 218 | {error, _} -> 219 | exit(Pid, kill), 220 | gen_tcp:close(CSocket) 221 | end, 222 | 223 | case prim_inet:async_accept(State#state.lsocket, -1) of 224 | {ok, _} -> 225 | {noreply, State#state{conns=Conns+1}}; 226 | {error, Ref} -> 227 | {stop, {async_accept, inet:format_error(Ref)}, State#state{conns=Conns+1}} 228 | end; 229 | 230 | handle_info({inet_async, _LSocket, _Ref, Error}, State) -> 231 | {stop, Error, State}; 232 | 233 | handle_info({'EXIT', Pid, Reason}, State = #state{conns=Conns}) -> 234 | gen_event:notify(?STAT_EVENT, {conn, close, Pid, Reason}), 235 | {noreply, State#state{conns=Conns-1}}; 236 | 237 | handle_info(_Info, State) -> 238 | {noreply, State}. 239 | 240 | %%-------------------------------------------------------------------- 241 | %% @private 242 | %% @doc 243 | %% This function is called by a gen_server when it is about to 244 | %% terminate. It should be the opposite of Module:init/1 and do any 245 | %% necessary cleaning up. When it returns, the gen_server terminates 246 | %% with Reason. The return value is ignored. 247 | %% 248 | %% @spec terminate(Reason, State) -> void() 249 | %% @end 250 | %%-------------------------------------------------------------------- 251 | terminate(_Reason, _State) -> 252 | ok. 253 | 254 | %%-------------------------------------------------------------------- 255 | %% @private 256 | %% @doc 257 | %% Convert process state when code is changed 258 | %% 259 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 260 | %% @end 261 | %%-------------------------------------------------------------------- 262 | code_change(_OldVsn, State, _Extra) -> 263 | {ok, State}. 264 | 265 | %%%=================================================================== 266 | %%% Internal functions 267 | %%%=================================================================== 268 | max_time() -> 269 | erlang:convert_time_unit(erlang:system_info(end_time), native, milli_seconds). 270 | max_time(Time) -> 271 | erlang:min(Time, max_time()). 272 | 273 | 274 | parse_method(Method) when is_list(Method); is_binary(Method) -> 275 | list_to_atom(re:replace(Method, "-", "_", [global, {return, list}])); 276 | parse_method(Method) when is_atom(Method) -> 277 | Method. 278 | -------------------------------------------------------------------------------- /src/sserl_sync_flow.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author paul 3 | %%% @copyright (C) 2016, paul 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 15 Aug 2016 by paul 8 | %%%------------------------------------------------------------------- 9 | -module(sserl_sync_flow). 10 | 11 | -behaviour(gen_event). 12 | 13 | %% API 14 | -export([add_handler/0]). 15 | 16 | %% gen_event callbacks 17 | -export([init/1, handle_event/2, handle_call/2, 18 | handle_info/2, terminate/2, code_change/3]). 19 | 20 | -include("sserl.hrl"). 21 | 22 | -define(SERVER, ?MODULE). 23 | -define(LOG_TAB, sserl_flow_log). 24 | -define(FLOW_TAB, sserl_flow). 25 | -define(MYSQL_ID, sserl_mysql). 26 | 27 | -define(FORM_TYPE, "application/x-www-form-urlencoded;charset=UTF-8"). 28 | -define(JSON_TYPE, "application/json"). 29 | -define(FORM_HEADER, {"Content-Type","application/x-www-form-urlencoded;charset=UTF-8"}). 30 | -define(HTTPC_OPTIONS, [{body_format, binary}]). 31 | -define(TIMEOUT, 10000). 32 | 33 | -define(SYNC_INTERVAL, 2 * 60*1000). 34 | -define(REPORT_INTERVAL, 5*60*1000). 35 | -define(REPORT_MIN, 1048576).% 1MB 36 | 37 | -record(state, { 38 | node_id, 39 | rate, 40 | report_min 41 | }). 42 | 43 | -record(flow, { 44 | port :: integer(), 45 | uid :: integer(), 46 | max_flow :: integer(), 47 | download :: integer(), 48 | upload :: integer(), 49 | method = undefined, 50 | password = undefined 51 | }). 52 | 53 | %%%=================================================================== 54 | %%% API 55 | %%%=================================================================== 56 | 57 | %%-------------------------------------------------------------------- 58 | %% @doc 59 | %% Adds an event handler 60 | %% 61 | %% @spec add_handler() -> ok | {'EXIT', Reason} | term() 62 | %% @end 63 | %%-------------------------------------------------------------------- 64 | add_handler() -> 65 | gen_event:add_handler(?FLOW_EVENT, ?MODULE, []). 66 | 67 | %%%=================================================================== 68 | %%% gen_event callbacks 69 | %%%=================================================================== 70 | 71 | %%-------------------------------------------------------------------- 72 | %% @private 73 | %% @doc 74 | %% Whenever a new event handler is added to an event manager, 75 | %% this function is called to initialize the event handler. 76 | %% 77 | %% @spec init(Args) -> {ok, State} 78 | %% @end 79 | %%-------------------------------------------------------------------- 80 | init([]) -> 81 | case application:get_env(sync_enabled) of 82 | {ok, true} -> 83 | init([init_mysql]); 84 | _ -> 85 | diabled 86 | end; 87 | init([init_mysql]) -> 88 | {ok, Host} = application:get_env(sync_mysql_host), 89 | {ok, User} = application:get_env(sync_mysql_user), 90 | {ok, Pass} = application:get_env(sync_mysql_pass), 91 | {ok, DB} = application:get_env(sync_mysql_db), 92 | Port = application:get_env(sserl, sync_mysql_port, 3306), 93 | 94 | %% the clumn order must match the flow record element order 95 | SQLUsers = application:get_env(sserl, sync_sql_users, "SELECT port,id,transfer_enable,d,u,method,passwd FROM user WHERE enable=1"), 96 | SQLReport = application:get_env(sserl, sync_sql_reqport, "UPDATE user SET d=d+?,u=u+?,t=unix_timestamp() WHERE id=?"), 97 | SQLLog = application:get_env(sserl, sync_sql_log, "INSERT INTO user_traffic_log values(null,?,?,?,?,?,?,unix_timestamp())"), 98 | SQLRate = application:get_env(sserl, sync_sql_rate, "SELECT traffic_rate FROM ss_node WHERE id=?"), 99 | 100 | Prepares = [{users, SQLUsers}, {report, SQLReport}, {log, SQLLog}, {rate, SQLRate}], 101 | MysqlArgs = [{host, Host},{port,Port},{user, User},{password, Pass},{database, DB}, {prepare, Prepares}], 102 | mysql_poolboy:add_pool(?MYSQL_ID, [{size, 2}, {max_overflow, 10}], MysqlArgs), 103 | init([init_mnesia]); 104 | 105 | init([init_mnesia]) -> 106 | {ok, NodeId}=application:get_env(sync_node_id), 107 | ReportMin = application:get_env(sserl, sync_report_min, ?REPORT_MIN), 108 | case init_mnesia() of 109 | ok -> 110 | ets:new(?LOG_TAB, [public, named_table]), 111 | {ok, _} = mnesia:subscribe({table, ?FLOW_TAB, detailed}), 112 | error_logger:info_msg("[sserl_sync_flow] init ok ~p", [self()]), 113 | erlang:send_after(0, self(), sync_timer), 114 | erlang:send_after(?REPORT_INTERVAL, self(), report_timer), 115 | {ok, #state{node_id=NodeId, rate=get_rate(NodeId, 1), report_min=ReportMin}}; 116 | Error -> 117 | error_logger:info_msg("[sserl_sync_flow] init failed: ~p", [Error]), 118 | {error, Error} 119 | end. 120 | 121 | %%-------------------------------------------------------------------- 122 | %% @private 123 | %% @doc 124 | %% Whenever an event manager receives an event sent using 125 | %% gen_event:notify/2 or gen_event:sync_notify/2, this function is 126 | %% called for each installed event handler to handle the event. 127 | %% 128 | %% @spec handle_event(Event, State) -> 129 | %% {ok, State} | 130 | %% {swap_handler, Args1, State1, Mod2, Args2} | 131 | %% remove_handler 132 | %% @end 133 | %%-------------------------------------------------------------------- 134 | handle_event({report, Port, Download, Upload}, State = #state{rate=Rate}) -> 135 | F = fun() -> 136 | case mnesia:wread({?FLOW_TAB, Port}) of 137 | [Flow=#flow{download=D, upload=U}] -> 138 | mnesia:write(?FLOW_TAB, Flow#flow{download=D+(Download*Rate), upload=U+(Upload*Rate)}, write); 139 | _ -> 140 | ok 141 | end 142 | end, 143 | mnesia:transaction(F), 144 | ets:update_counter(?LOG_TAB, Port, [{3, Download}, {4, Upload}]), 145 | {ok, State}; 146 | handle_event(_Event, State) -> 147 | {ok, State}. 148 | 149 | %%-------------------------------------------------------------------- 150 | %% @private 151 | %% @doc 152 | %% Whenever an event manager receives a request sent using 153 | %% gen_event:call/3,4, this function is called for the specified 154 | %% event handler to handle the request. 155 | %% 156 | %% @spec handle_call(Request, State) -> 157 | %% {ok, Reply, State} | 158 | %% {swap_handler, Reply, Args1, State1, Mod2, Args2} | 159 | %% {remove_handler, Reply} 160 | %% @end 161 | %%-------------------------------------------------------------------- 162 | handle_call(_Request, State) -> 163 | Reply = ok, 164 | {ok, Reply, State}. 165 | 166 | %%-------------------------------------------------------------------- 167 | %% @private 168 | %% @doc 169 | %% This function is called for each installed event handler when 170 | %% an event manager receives any other message than an event or a 171 | %% synchronous request (or a system message). 172 | %% 173 | %% @spec handle_info(Info, State) -> 174 | %% {ok, State} | 175 | %% {swap_handler, Args1, State1, Mod2, Args2} | 176 | %% remove_handler 177 | %% @end 178 | %%-------------------------------------------------------------------- 179 | handle_info({mnesia_table_event,{write, ?FLOW_TAB, 180 | #flow{port=P, max_flow=Max,download=D,upload=U}, _, _}}, 181 | State) when D + U > Max -> 182 | sserl_listener_sup:stop(P), 183 | {ok, State}; 184 | %% handle_info({mnesia_table_event, {write, ?FLOW_TAB, 185 | %% #flow{max_flow=F,method=M,password=P}, 186 | %% [#flow{max_flow=F,method=M,password=P}],_}}, State) -> 187 | %% %% nothing to change 188 | %% {ok, State}; 189 | handle_info({mnesia_table_event, {write, ?FLOW_TAB, NewFlow, _, _}}, State) -> 190 | %% init log element 191 | case ets:lookup(?LOG_TAB, NewFlow#flow.port) of 192 | [] -> 193 | ets:insert(?LOG_TAB, {NewFlow#flow.port, NewFlow#flow.uid, 0, 0}); 194 | _ -> 195 | ok 196 | end, 197 | sserl_listener_sup:start(flow_to_args(NewFlow)), 198 | {ok, State}; 199 | handle_info({mnesia_table_event, {delete, {?FLOW_TAB, Port}, _}}, State) -> 200 | sserl_listener_sup:stop(Port), 201 | {ok, State}; 202 | 203 | handle_info(sync_timer, State) -> 204 | spawn(fun sync_users/0), 205 | erlang:send_after(?SYNC_INTERVAL, self(), sync_timer), 206 | self() ! sync_rate, 207 | {ok, State}; 208 | 209 | handle_info(report_timer, State = #state{node_id=NodeId, rate=Rate, report_min=Min}) -> 210 | do_report(NodeId, Rate, Min), 211 | erlang:send_after(?REPORT_INTERVAL, self(), report_timer), 212 | {ok, State}; 213 | 214 | handle_info(sync_rate, State = #state{node_id=NodeId, rate=Rate}) -> 215 | {ok, State#state{rate=get_rate(NodeId, Rate)}}; 216 | 217 | handle_info(Info, State) -> 218 | io:format("info:~p", [Info]), 219 | {ok, State}. 220 | 221 | %%-------------------------------------------------------------------- 222 | %% @private 223 | %% @doc 224 | %% Whenever an event handler is deleted from an event manager, this 225 | %% function is called. It should be the opposite of Module:init/1 and 226 | %% do any necessary cleaning up. 227 | %% 228 | %% @spec terminate(Reason, State) -> void() 229 | %% @end 230 | %%-------------------------------------------------------------------- 231 | terminate(_Reason, #state{node_id=NodeId, rate=Rate}) -> 232 | do_report(NodeId, Rate, 0), 233 | ets:delete(?LOG_TAB), 234 | uninit_mnesia(), 235 | ok. 236 | 237 | %%-------------------------------------------------------------------- 238 | %% @private 239 | %% @doc 240 | %% Convert process state when code is changed 241 | %% 242 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 243 | %% @end 244 | %%-------------------------------------------------------------------- 245 | code_change(_OldVsn, State, _Extra) -> 246 | {ok, State}. 247 | 248 | %%%=================================================================== 249 | %%% Internal functions 250 | %%%=================================================================== 251 | init_mnesia() -> 252 | case mnesia:add_table_copy(?FLOW_TAB, node(), ram_copies) of 253 | {aborted, {no_exists, {?FLOW_TAB, cstruct}}} -> 254 | case mnesia:create_table(?FLOW_TAB, [{type, set}, 255 | {ram_copies, [node()]}, 256 | {record_name, flow}, 257 | {attributes, record_info(fields, flow)}]) of 258 | {atomic, ok} -> 259 | ok; 260 | {aborted,{already_exists, ?FLOW_TAB}} -> 261 | ok; 262 | Error -> 263 | Error 264 | end; 265 | _ -> 266 | ok 267 | end. 268 | 269 | uninit_mnesia() -> 270 | mnesia:del_table_copy(?FLOW_TAB, node()), 271 | ok. 272 | 273 | default_method() -> 274 | application:get_env(sserl, default_method, rc4_md5). 275 | 276 | default_ip() -> 277 | application:get_env(sserl, default_ip, undefined). 278 | 279 | insert_flow(Flow) -> 280 | case mnesia:wread({?FLOW_TAB, Flow#flow.port}) of 281 | [] -> 282 | mnesia:write(?FLOW_TAB, Flow, write); 283 | [OldFlow] -> 284 | NewFlow = OldFlow#flow{max_flow=Flow#flow.max_flow, 285 | method=Flow#flow.method, 286 | password=Flow#flow.password}, 287 | mnesia:write(?FLOW_TAB, NewFlow, write) 288 | end. 289 | 290 | %% parse flow to listener args 291 | flow_to_args(#flow{port=Port, method=Method, password=Password}) -> 292 | Method1 = case Method of 293 | "" -> 294 | default_method(); 295 | _ -> 296 | Method 297 | end, 298 | [{port, Port}, {ip, default_ip()}, {method, Method1}, {password, Password}]. 299 | 300 | sync_users() -> 301 | case mysql_poolboy:execute(?MYSQL_ID, users, []) of 302 | {ok, _, Users} -> 303 | F = fun() -> 304 | [insert_flow(list_to_tuple([flow|trip_binary(User)])) || User <- Users] 305 | end, 306 | mnesia:transaction(F), 307 | ok; 308 | _ -> 309 | ok 310 | end. 311 | 312 | do_report(NodeId, Rate, Min) -> 313 | Flows = ets:select(?LOG_TAB, [{{'_','_','$1','_'}, [{'>','$1',Min}], ['$_']}, 314 | {{'_','_','_','$1'}, [{'>','$1',Min}], ['$_']}]), 315 | F = fun(Pid) -> 316 | lists:map(fun({_Port, Uid, D, U}) -> 317 | ok = mysql:execute(Pid, report, [D, U, Uid]), 318 | mysql:execute(Pid, log, [Uid, U, D, NodeId, Rate, traffic_string((U+D)*Rate)]) 319 | end, Flows), 320 | ok 321 | end, 322 | case catch mysql_poolboy:transaction(?MYSQL_ID, F) of 323 | {atomic, _} -> 324 | [ets:update_element(?LOG_TAB, P, [{3, 0},{4,0}]) || {P, _,_,_} <- Flows], 325 | ok; 326 | Error -> 327 | Error 328 | end. 329 | 330 | get_rate(NodeId, OldRate) -> 331 | case catch mysql_poolboy:execute(?MYSQL_ID, rate, [NodeId]) of 332 | {ok, _, [[Rate]]} -> 333 | Rate; 334 | _ -> 335 | OldRate 336 | end. 337 | 338 | traffic_string(T) when T > 1047527424 -> 339 | io_lib:format("~pGB", [round(T/1073741824*100)/100]); 340 | traffic_string(T) when T > 1022976 -> 341 | io_lib:format("~pMB", [round(T/1048576*100)/100]); 342 | traffic_string(T) -> 343 | io_lib:format("~pKB", [round(T/1024*100)/100]). 344 | 345 | trip_binary(L) -> 346 | lists:map(fun(I) when is_binary(I) -> 347 | binary_to_list(I); 348 | (I) -> 349 | I 350 | end, L). 351 | 352 | -------------------------------------------------------------------------------- /src/sserl_conn.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author paul 3 | %%% @copyright (C) 2016, paul 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 15 May 2016 by paul 8 | %%%------------------------------------------------------------------- 9 | -module(sserl_conn). 10 | 11 | -behaviour(gen_server). 12 | 13 | %% API 14 | -export([start_link/2, init/2]). 15 | 16 | %% gen_server callbacks 17 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 18 | terminate/2, code_change/3]). 19 | 20 | -include("shadowsocks.hrl"). 21 | -include("sserl.hrl"). 22 | 23 | -define(SERVER, ?MODULE). 24 | -define(RECV_TIMOUT, 180000). 25 | -define(REPORT_INTERVAL, 1000). 26 | -define(REPORT_MIN, 10485760). % 10MB 27 | 28 | -define(TCP_OPTS, [binary, {packet, raw}, {active, once},{nodelay, true}]). 29 | 30 | 31 | -record(state, { 32 | csocket, 33 | ssocket, 34 | ota, 35 | port, 36 | cipher_info, 37 | down = 0, 38 | up = 0, 39 | sending = 0, 40 | ota_data = <<>>, 41 | ota_len = 2, 42 | ota_id = 0, 43 | ota_iv = <<>>, 44 | type = server, 45 | target = undefined, 46 | c2s_handler=undefined, 47 | s2c_handler=undefined 48 | }). 49 | 50 | 51 | %%%=================================================================== 52 | %%% API 53 | %%%=================================================================== 54 | 55 | %%-------------------------------------------------------------------- 56 | %% @doc 57 | %% Starts the server 58 | %% 59 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 60 | %% @end 61 | %%-------------------------------------------------------------------- 62 | start_link(Socket, Info) -> 63 | proc_lib:start_link(?MODULE, init, [Socket, Info]). 64 | %% gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 65 | 66 | init(Socket, {Port, Server, OTA, Type, {Method,Password}}) -> 67 | proc_lib:init_ack({ok, self()}), 68 | wait_socket(Socket), 69 | Cipher = shadowsocks_crypt:init_cipher_info(Method, Password), 70 | State = #state{csocket=Socket, ssocket=undefined, 71 | ota=OTA, port=Port, type=Type, 72 | target = Server, 73 | cipher_info=Cipher}, 74 | init_proto(State). 75 | 76 | 77 | init_proto(State=#state{type=server,csocket=CSocket}) -> 78 | State1 = recv_ivec(State), 79 | {Addr, Port, Data, State2} = recv_target(State1), 80 | gen_event:notify(?STAT_EVENT, {conn, connect, Addr, Port}), 81 | case gen_tcp:connect(Addr, Port, ?TCP_OPTS) of 82 | {ok, SSocket} -> 83 | self() ! {send, Data}, 84 | inet:setopts(CSocket, [{active, once}]), 85 | erlang:send_after(?REPORT_INTERVAL, self(), report_flow), 86 | gen_server:enter_loop(?MODULE, [], init_handler(State2#state{ssocket=SSocket,target={Addr,Port}})); 87 | {error, Reason} -> 88 | exit(Reason) 89 | end; 90 | 91 | init_proto(State=#state{type=client, csocket=CSocket, target={Addr,Port},ota=OTA,cipher_info=Cipher}) -> 92 | {Atype, Data} = recv_socks5(CSocket), 93 | case gen_tcp:connect(Addr, Port, ?TCP_OPTS) of 94 | {ok, SSocket} -> 95 | {NewCipher, NewData} = 96 | case OTA of 97 | true -> 98 | Hmac = shadowsocks_crypt:hmac([Cipher#cipher_info.encode_iv, Cipher#cipher_info.key], 99 | [Atype bor ?OTA_FLAG, Data]), 100 | shadowsocks_crypt:encode(Cipher, [Atype bor ?OTA_FLAG, Data, Hmac]); 101 | false -> 102 | shadowsocks_crypt:encode(Cipher, [Atype, Data]) 103 | end, 104 | ok = gen_tcp:send(SSocket, NewData), 105 | inet:setopts(CSocket, [{active, once}]), 106 | State1 = State#state{ssocket = SSocket, cipher_info=NewCipher, ota_iv=Cipher#cipher_info.encode_iv}, 107 | gen_server:enter_loop(?MODULE, [], init_handler(State1)); 108 | {error, Reason} -> 109 | exit(Reason) 110 | end. 111 | 112 | init_handler(State=#state{type=client, ota=true}) -> 113 | State#state{c2s_handler=fun handle_client_ota_c2s/2, 114 | s2c_handler=fun handle_client_s2c/2}; 115 | init_handler(State=#state{type=client, ota=false}) -> 116 | State#state{c2s_handler=fun handle_client_c2s/2, 117 | s2c_handler=fun handle_client_s2c/2}; 118 | init_handler(State=#state{type=server, ota=true}) -> 119 | State#state{c2s_handler=fun handle_server_ota_c2s/2, 120 | s2c_handler=fun handle_server_s2c/2}; 121 | init_handler(State=#state{type=server, ota=false}) -> 122 | State#state{c2s_handler=fun handle_server_c2s/2, 123 | s2c_handler=fun handle_server_s2c/2}. 124 | 125 | %%%=================================================================== 126 | %%% gen_server callbacks 127 | %%%=================================================================== 128 | 129 | %%-------------------------------------------------------------------- 130 | %% @private 131 | %% @doc 132 | %% Initializes the server 133 | %% 134 | %% @spec init(Args) -> {ok, State} | 135 | %% {ok, State, Timeout} | 136 | %% ignore | 137 | %% {stop, Reason} 138 | %% @end 139 | %%-------------------------------------------------------------------- 140 | init([]) -> 141 | {ok, #state{}}. 142 | 143 | %%-------------------------------------------------------------------- 144 | %% @private 145 | %% @doc 146 | %% Handling call messages 147 | %% 148 | %% @spec handle_call(Request, From, State) -> 149 | %% {reply, Reply, State} | 150 | %% {reply, Reply, State, Timeout} | 151 | %% {noreply, State} | 152 | %% {noreply, State, Timeout} | 153 | %% {stop, Reason, Reply, State} | 154 | %% {stop, Reason, State} 155 | %% @end 156 | %%-------------------------------------------------------------------- 157 | handle_call(_Request, _From, State) -> 158 | Reply = ok, 159 | {reply, Reply, State}. 160 | 161 | %%-------------------------------------------------------------------- 162 | %% @private 163 | %% @doc 164 | %% Handling cast messages 165 | %% 166 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 167 | %% {noreply, State, Timeout} | 168 | %% {stop, Reason, State} 169 | %% @end 170 | %%-------------------------------------------------------------------- 171 | handle_cast(_Msg, State) -> 172 | {noreply, State}. 173 | 174 | %%-------------------------------------------------------------------- 175 | %% @private 176 | %% @doc 177 | %% Handling all non call/cast messages 178 | %% 179 | %% @spec handle_info(Info, State) -> {noreply, State} | 180 | %% {noreply, State, Timeout} | 181 | %% {stop, Reason, State} 182 | %% @end 183 | %%-------------------------------------------------------------------- 184 | 185 | handle_info({tcp, CSocket, Data}, State=#state{csocket=CSocket, c2s_handler=Handler}) -> 186 | inet:setopts(CSocket, [{active, once}]), 187 | Handler(Data, State); 188 | handle_info({tcp, SSocket, Data}, State=#state{ssocket=SSocket, s2c_handler=Handler}) -> 189 | inet:setopts(SSocket, [{active, once}]), 190 | Handler(Data, State); 191 | 192 | 193 | %% socket send reply 194 | handle_info({inet_reply, _Socket, _Error}, State = #state{csocket=undefined,sending=1}) -> 195 | {stop, normal, State}; 196 | handle_info({inet_reply, _Socket, _Error}, State = #state{ssocket=undefined, sending=1}) -> 197 | {stop, normal, State}; 198 | handle_info({inet_reply, _, _}, State = #state{sending=N}) -> 199 | {noreply, State#state{sending=N-1}}; 200 | %% socket closed 201 | handle_info({tcp_closed, _Socket}, State = #state{sending=0}) -> 202 | {stop, normal, State}; 203 | handle_info({tcp_closed, CSocket}, State = #state{csocket=CSocket}) -> 204 | {noreply, State#state{csocket=undefined}}; 205 | handle_info({tcp_closed, SSocket}, State = #state{ssocket=SSocket}) -> 206 | {noreply, State#state{ssocket=undefined}}; 207 | %% report flow 208 | handle_info(report_flow, State = #state{port=Port,down=Down,up=Up}) when Down + Up >= ?REPORT_MIN -> 209 | gen_event:notify(?FLOW_EVENT, {report, Port, Down, Up}), 210 | erlang:send_after(?REPORT_INTERVAL, self(), report_flow), 211 | {noreply, State#state{down=0, up=0}}; 212 | handle_info(report_flow, State) -> 213 | erlang:send_after(?REPORT_INTERVAL, self(), report_flow), 214 | {noreply, State}; 215 | 216 | %% first send 217 | handle_info({send, Data}, State=#state{type=server,ota=false,ssocket=SSocket, up=Flow, sending=S}) -> 218 | S1 = try_send(SSocket, Data), 219 | {noreply, State#state{sending=S+S1, up=Flow+size(Data)}}; 220 | handle_info({send, Data}, State=#state{type=server,ota=true, ota_data=Rest}) -> 221 | handle_ota(State#state{ota_data= <>}); 222 | 223 | handle_info(_Info, State) -> 224 | {noreply, State}. 225 | 226 | 227 | %%-------------------------------------------------------------------- 228 | %% @private 229 | %% @doc 230 | %% This function is called by a gen_server when it is about to 231 | %% terminate. It should be the opposite of Module:init/1 and do any 232 | %% necessary cleaning up. When it returns, the gen_server terminates 233 | %% with Reason. The return value is ignored. 234 | %% 235 | %% @spec terminate(Reason, State) -> void() 236 | %% @end 237 | %%-------------------------------------------------------------------- 238 | terminate(_Reason, State) -> 239 | gen_event:notify(?FLOW_EVENT, {report, State#state.port, State#state.down, State#state.up}), 240 | ok. 241 | 242 | %%-------------------------------------------------------------------- 243 | %% @private 244 | %% @doc 245 | %% Convert process state when code is changed 246 | %% 247 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 248 | %% @end 249 | %%-------------------------------------------------------------------- 250 | code_change(_OldVsn, State, _Extra) -> 251 | {ok, State}. 252 | 253 | %%%=================================================================== 254 | %%% Internal functions 255 | %%%=================================================================== 256 | 257 | %% ---------------------------------------------------------------------------------------------------- 258 | %% Data encoding / decoding 259 | %% ----------------------------------------------------------------------------------------------------- 260 | handle_client_c2s(Data, State=#state{ssocket=SSocket, cipher_info=CipherInfo, 261 | up=Flow, sending=S}) -> 262 | {CipherInfo1, EncData} = shadowsocks_crypt:encode(CipherInfo, Data), 263 | S1 = try_send(SSocket, EncData), 264 | {noreply, State#state{cipher_info=CipherInfo1, up=Flow+size(Data), sending=S+S1}}. 265 | 266 | handle_client_ota_c2s(Data, State=#state{ssocket=SSocket, cipher_info=CipherInfo, 267 | ota_iv=Iv, ota_id=Id, up=Flow, sending=S}) -> 268 | Hmac = shadowsocks_crypt:hmac([Iv, <>], Data), 269 | Len = byte_size(Data), 270 | {CipherInfo1, EncData} = shadowsocks_crypt:encode(CipherInfo, [<>, Hmac, Data]), 271 | S1 = try_send(SSocket, EncData), 272 | {noreply, State#state{cipher_info=CipherInfo1, up=Flow+size(Data), sending=S+S1, ota_id=Id+1}}. 273 | 274 | 275 | handle_server_c2s(Data, State=#state{ssocket=SSocket, cipher_info=CipherInfo, 276 | up=Flow, sending=S}) -> 277 | {CipherInfo1, DecData} = shadowsocks_crypt:decode(CipherInfo, Data), 278 | S1 = try_send(SSocket, DecData), 279 | {noreply, State#state{cipher_info=CipherInfo1, up=Flow+size(Data), sending=S+S1}}. 280 | 281 | handle_server_ota_c2s(Data, State=#state{cipher_info=CipherInfo,ota_data=Rest}) -> 282 | {CipherInfo1, DecData} = shadowsocks_crypt:decode(CipherInfo, Data), 283 | handle_ota(State#state{ota_data= <>, cipher_info=CipherInfo1}). 284 | 285 | handle_client_s2c(Data, State=#state{csocket=CSocket, cipher_info=CipherInfo, 286 | down=Flow, sending=S}) -> 287 | {CipherInfo1, DecData} = shadowsocks_crypt:decode(CipherInfo, Data), 288 | S1 = try_send(CSocket, DecData), 289 | {noreply, State#state{cipher_info=CipherInfo1, down=Flow+size(Data), sending=S+S1}}. 290 | 291 | handle_server_s2c(Data, State=#state{csocket=CSocket, cipher_info=CipherInfo, 292 | down=Flow, sending=S}) -> 293 | {CipherInfo1, EncData} = shadowsocks_crypt:encode(CipherInfo, Data), 294 | S1 = try_send(CSocket, EncData), 295 | {noreply, State#state{cipher_info=CipherInfo1, down=Flow+size(Data), sending=S+S1}}. 296 | 297 | 298 | %% handle ota frame 299 | handle_ota(State = #state{ota_data=Data, ota_len=2}) when byte_size(Data) >= 2 -> 300 | <> = Data, 301 | handle_ota(State#state{ota_len=DataLen+?HMAC_LEN+2}); 302 | handle_ota(State = #state{ota_iv=Iv,ota_data=Data, ota_len=Len, ota_id=Id, 303 | ssocket=SSocket, up=Flow, sending=S}) when byte_size(Data) >= Len -> 304 | DataLen = Len-?HMAC_LEN - 2, 305 | <<_:16/big, Hmac:?HMAC_LEN/binary, FrameData:DataLen/binary, Rest/binary>> = Data, 306 | case shadowsocks_crypt:hmac([Iv, <>], FrameData) of 307 | Hmac -> 308 | S1 = try_send(SSocket, FrameData), 309 | handle_ota(State#state{up=Flow+size(FrameData), sending=S+S1, ota_data=Rest,ota_len=2,ota_id=Id+1}); 310 | _ -> 311 | {stop, {error, bad_ota_hmac}, State} 312 | end; 313 | handle_ota(State) -> 314 | {noreply, State}. 315 | 316 | %% --------------------------------------------------------------------------------------------------------- 317 | 318 | wait_socket(Socket) -> 319 | receive 320 | {shoot, Socket} -> 321 | ok; 322 | _ -> 323 | wait_socket(Socket) 324 | end. 325 | 326 | 327 | %% recv the iv data 328 | recv_ivec(State = #state{csocket=Socket, 329 | cipher_info=#cipher_info{method=Method,key=Key}=CipherInfo}) -> 330 | {_, IvLen} = shadowsocks_crypt:key_iv_len(Method), 331 | {ok, IvData} = gen_tcp:recv(Socket, IvLen, ?RECV_TIMOUT), 332 | StreamState = shadowsocks_crypt:stream_init(Method, Key, IvData), 333 | State#state{ 334 | ota_iv = IvData, 335 | cipher_info=CipherInfo#cipher_info{ 336 | decode_iv=IvData, stream_dec_state=StreamState 337 | } 338 | }. 339 | 340 | %% recv and decode target addr and port 341 | recv_target(State) -> 342 | {<>, State1} = recv_decode(1, <<>>, State), 343 | {IPPort, Addr, Port, Rest, NewState} = 344 | case ?GET_ATYP(AddrType) of 345 | ?SOCKS5_ATYP_V4 -> 346 | {<>, State2} = recv_decode(6, Data, State1), 347 | <> = Data1, 348 | {Data1, {IP1,IP2,IP3,IP4}, DestPort, Data2, State2}; 349 | ?SOCKS5_ATYP_V6 -> 350 | {<>, State2} = recv_decode(18, Data, State1), 351 | <> = Data1, 354 | {Data1, {IP1,IP2,IP3,IP4,IP5,IP6,IP7,IP8}, DestPort, Data2, State2}; 355 | ?SOCKS5_ATYP_DOM -> 356 | {<>, State2} = recv_decode(1, Data, State1), 357 | DPLen = DomLen+2, 358 | {<>, State3} = recv_decode(DomLen+2, Data1, State2), 359 | <> = Data2, 360 | {[DomLen,Data2], binary_to_list(Domain), DestPort, Data3, State3}; 361 | _ -> 362 | throw({error_address_type, AddrType}) 363 | end, 364 | case {?IS_OTA(AddrType), NewState#state.ota} of 365 | {true, _} -> 366 | {<>, NewState2} = recv_decode(?HMAC_LEN, Rest, NewState), 367 | #cipher_info{key=Key} = NewState2#state.cipher_info, 368 | case shadowsocks_crypt:hmac([NewState2#state.ota_iv, Key], [AddrType, IPPort]) of 369 | Hmac -> 370 | {Addr, Port, Rest2, NewState2#state{ota=true}}; 371 | _ -> 372 | throw({error, ota_bad_hmac}) 373 | end; 374 | {_, true} -> 375 | throw({error, missing_ota}); 376 | {false, false} -> 377 | {Addr, Port, Rest, NewState#state{ota=false}} 378 | end. 379 | 380 | %% recv and decode data until got intput length 381 | recv_decode(Len, Data, State) when byte_size(Data) >= Len -> 382 | {Data, State}; 383 | recv_decode(Len, Data, State = #state{csocket=Socket, cipher_info=CipherInfo}) -> 384 | {ok, Data1} = gen_tcp:recv(Socket, 0, ?RECV_TIMOUT), 385 | {CipherInfo1, Data2} = shadowsocks_crypt:decode(CipherInfo, Data1), 386 | Data3 = <>, 387 | recv_decode(Len, Data3, State#state{cipher_info=CipherInfo1}). 388 | 389 | %% recv socks5 request 390 | recv_socks5(Socket) -> 391 | %% ------ handshark -------------------------- 392 | %% exactly socks5 version otherwise boom!!! 393 | <> = exactly_recv(Socket, 2), 394 | %% don't care methods 395 | _ = exactly_recv(Socket, NMethods), 396 | %% response ok 397 | ok = gen_tcp:send(Socket, <>), 398 | 399 | %% ------ socks5 req ------------------------- 400 | %% only support socks5 connect 401 | <> = exactly_recv(Socket, 4), 402 | Ret = case Atyp of 403 | ?SOCKS5_ATYP_V4 -> 404 | exactly_recv(Socket, 6); 405 | ?SOCKS5_ATYP_V6 -> 406 | exactly_recv(Socket, 18); 407 | ?SOCKS5_ATYP_DOM -> 408 | <> = exactly_recv(Socket, 1), 409 | [DomLen,exactly_recv(Socket, DomLen+2)] 410 | end, 411 | ok = gen_tcp:send(Socket, <>), 412 | {Atyp,Ret}. 413 | 414 | 415 | exactly_recv(Socket, Size) -> 416 | {ok, Ret} = gen_tcp:recv(Socket, Size, ?RECV_TIMOUT), 417 | Ret. 418 | 419 | %% try to send package 420 | %% return 1 if success else return 0 421 | try_send(Socket, Data) -> 422 | try erlang:port_command(Socket, Data) of 423 | _ -> 1 424 | catch 425 | error:_E -> 0 426 | end. 427 | --------------------------------------------------------------------------------