├── README.MD ├── config.json └── inner-ss ├── main.go ├── schedule.go └── whitelist.go /README.MD: -------------------------------------------------------------------------------- 1 | # inner-shadowsocks # 2 | 3 | ![img](https://i.v2ex.co/5iP665B4.png) 4 | 5 | [English](#english) | [中文](#中文) 6 | 7 | ### English 8 | 9 | This is a simple app like `ss-local` in Golang. It is designed for running on a server inside China mainland and connecting a server outside through `shadowsocks`, then you can simply set `SOCKS5` proxy in your Chrome, Telegram etc. without running a client on your laptop or cell phones. 10 | 11 | There is a python version of this app: [shadowsocks-with-socks-auth](https://github.com/ihciah/shadowsocks-with-socks-auth), which is just a small modified version of original shadowsocks adding `SOCKS5` auth support. This go version is more efficient and support multi-server balance. 12 | 13 | ----- 14 | 15 | #### Features: 16 | 17 | - `SOCKS5` auth 18 | - multi-server for load balance 19 | 20 | #### Install: 21 | 22 | Way 1: Download binary from [Releases](https://github.com/ihciah/inner-shadowsocks/releases). Golang environment is not required. 23 | 24 | Way 2: 25 | 26 | ```shell 27 | go get -u -v github.com/ihciah/inner-shadowsocks/inner-ss 28 | ``` 29 | 30 | Due to network issue in China mainland, in most situation, `go get` cannot be executed correctly. 31 | 32 | #### Usage: 33 | 34 | ```shell 35 | inner-ss -c config.json 36 | ``` 37 | 38 | - -c: configure file path(default: `config.json`) 39 | - -v: verbose mode 40 | 41 | Configure file: 42 | 43 | A json string like 44 | 45 | ```json 46 | { 47 | "listen": "0.0.0.0", 48 | "port": 23333, 49 | "auth": false, 50 | "username": "ihc", 51 | "password": "iah", 52 | "servers": ["ss://RC4-MD5:pass1@ip1:port1", "ss://RC4-MD5:pass2@ip2:port2"], 53 | "maxfail": 3, 54 | "recovertime": 600, 55 | "starttimeout": 5, 56 | "remotetimeout": 60, 57 | "insidetimeout": 60, 58 | "whitelistenable": false, 59 | "whitelistdomains": [".github.com"], 60 | "whitelistips": ["::/0", "0.0.0.0/0"] 61 | } 62 | ``` 63 | 64 | The `auth`, `username`, `password`, `maxfail`, `recovertime`, `remotetimeout`, `insidetimeout`, `starttimeout` are optional whose default value are `false`, `""`, `""`, `10`, `600`, `60`, `60`, `8` . 65 | 66 | Also, `whitelistenable` is `false` by default. If you want to limit proxy only to some range, you can use `whitelistdomains` and `whitelistips` . 67 | 68 | IP should follow CIDR format such as `100.110.120.130/32`. `::/0` means all IPv6, `0.0.0.0/0` means all IPv4. Domains will be matched by the suffix. Any domains has suffix matched in `whitelistdomains` can pass. For example, `www.github.com` will match `.github.com`. 69 | 70 | If you set `auth` to `true`, you should provide `username` and `password`. 71 | 72 | You should list your servers in order. If one of them cannot be connected for `maxfail` times, it will be marked as downed-server until `recovertime` seconds later. Please pay attention that you must ensure the password and encryption method is correct. Because we cannot distinguish users' malicious requests and the wrong password of shadowsocks servers, here we judge server status through if we can connect it. 73 | 74 | `remotetimeout` is the timeout connecting remote servers, and `insidetimeout` is the one to clients. `starttimeout` is the timeout of the first communication. A short `starttimeout` can decrease the waiting time of the false packet, long `remotetimeout` and `insidetimeout` can allow long connection, for example to decrease the reconnecting of Telegram. 75 | 76 | You can use `systemd` or `supervisor` to run the daemon. The example configure of supervisor is at the bottom. 77 | 78 | 79 | 80 | ----- 81 | 82 | ### 中文 83 | 84 | 这是一个类似 `ss-local` 的小程序,使用Golang编写。你可以在一台中国大陆的服务器上运行它,它可以使用 `shadowsocks` 协议与境外服务器通信,并打开一个 `SOCKS5` 代理提供给国内用户。此应用可以免掉运行 `shadowsocks` 客户端的麻烦,你可以直接在Chrome或者Telegram等应用内设置 `SOCKS5` 代理越过封锁。 85 | 86 | [shadowsocks-with-socks-auth](https://github.com/ihciah/shadowsocks-with-socks-auth) 是一个python版 `shadowsocks` 的修改版,添加了 `SOCKS5` 用户认证功能。本应用比这个运行更高效,并且添加了保证高可用性的负载均衡功能。 87 | 88 | #### 功能: 89 | 90 | - SOCKS5用户认证支持 91 | - 多服务器负载均衡 92 | 93 | #### 安装: 94 | 95 | 方式1(推荐):在 [Releases](https://github.com/ihciah/inner-shadowsocks/releases) 中下载可执行文件并上传直接执行。不需要安装Go语言环境。 96 | 97 | 方式2: 98 | 99 | ```shell 100 | go get -u -v github.com/ihciah/inner-shadowsocks/inner-ss 101 | ``` 102 | 103 | 在国内服务器上执行安装,由于网络原因 `go get` 大概率无法顺利执行。 104 | 105 | #### 使用: 106 | 107 | ```shell 108 | inner-ss -c config.json 109 | ``` 110 | 111 | - -c: 配置文件路径,默认为config.json 112 | - -v: 打印详细信息,默认关闭 113 | 114 | 配置文件样例(json格式): 115 | 116 | ```json 117 | { 118 | "listen": "0.0.0.0", 119 | "port": 23333, 120 | "auth": false, 121 | "username": "ihc", 122 | "password": "iah", 123 | "servers": ["ss://RC4-MD5:pass1@ip1:port1", "ss://RC4-MD5:pass2@ip2:port2"], 124 | "maxfail": 3, 125 | "recovertime": 600, 126 | "starttimeout": 5, 127 | "remotetimeout": 60, 128 | "insidetimeout": 60, 129 | "whitelistenable": false, 130 | "whitelistdomains": [".github.com"], 131 | "whitelistips": ["::/0", "0.0.0.0/0"] 132 | } 133 | ``` 134 | 135 | `auth`, `username`, `password` , `maxfail`, `recovertime`, `remotetimeout`, `insidetimeout`, `starttimeout` 这些字段是可选的,默认值为 `false`, `""`, `""`, `10`, `600`, `60`, `60`, `8`. `auth`为`true`表示开启SOCKS5认证功能,可以防止服务器被扫描器扫到并滥用,当开启认证功能时,需提供 `username` 和 `password`。 136 | 137 | `whitelistenable` 默认关闭。如果需要限制访问特定服务器,你可以开启该功能,并填写 `whitelistdomains` 和 `whitelistips` 。 138 | 139 | 其中 IP 段填写格式为 CIDR 记法,如 `100.110.120.130/32`, `::/0` 表示所有 IPv6 段, `0.0.0.0/0` 表示所有 IPv4段。域名为后缀匹配,若后缀符合 `whitelistdomains` 中任意条目则可以通过,如 `www.github.com` 会匹配 `.github.com`。 140 | 141 | `servers`为 shadowsocks 服务器配置信息,请按照排好的顺序提供。如果一个服务器无法连接,当出错次数超过`maxfail`后会被标记为不可用,该标记会在`recovertime`秒后解除。请注意 shadowsocks 的服务器加密方式和密码必须保证正确。我们无法判断服务器不回应是因为用户的恶意请求还是密码错误,所以为了避免负载均衡功能被用来恶意禁用服务器,我们只采用能否连接服务器作为判断依据。 142 | 143 | `remotetimeout` 为代理服务器超时时间,`insidetimeout` 为客户端超时时间,`starttimeout` 为客户端和服务端第一次通信超时时间。`starttimeout` 可以设置为较短时间以减少错误请求的等待时间,`remotetimeout`和`insidetimeout` 设置较长时间可以允许较长连接,如减少Telegram的重连次数。 144 | 145 | 可以使用 `systemd` 或 `supervisor` 守护进程执行。文末附 supervisor 的配置文件样例。 146 | 147 | ----- 148 | 149 | #### Supervisor Configure File Example 150 | 151 | `/etc/supervisor/conf.d/ss4tg.conf` 152 | 153 | ``` 154 | [program:ss4tg] 155 | directory = /opt/ss4tg 156 | command = /opt/ss4tg/inner-ss 157 | autostart = true 158 | autorestart = true 159 | startsecs = 5 160 | startretries = 30 161 | user = nobody 162 | ``` 163 | Then run `sudo supervisorctl reload`. You can add `minfds=50000` in `[supervisord]` section of `/etc/supervisor/supervisord.conf` to increase the system limit of file descriptor. -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen": "0.0.0.0", 3 | "port": 23333, 4 | "auth": false, 5 | "username": "ihc", 6 | "password": "iah", 7 | "servers": ["ss://RC4-MD5:pass1@ip1:port1", "ss://RC4-MD5:pass2@ip2:port2"], 8 | "maxfail": 3, 9 | "recovertime": 600, 10 | "starttimeout": 3, 11 | "remotetimeout": 120, 12 | "insidetimeout": 120, 13 | "whitelistenable": false, 14 | "whitelistdomains": [".telegram.org"], 15 | "whitelistips": ["::/0", "0.0.0.0/0", "91.108.4.0/22", "91.108.8.0/22", "91.108.56.0/22", "109.239.140.0/24", "149.154.160.0/20", "149.154.164.0/22", "2001:67c:4e8::/48"] 16 | } 17 | -------------------------------------------------------------------------------- /inner-ss/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "flag" 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "net" 12 | "net/url" 13 | "time" 14 | 15 | "github.com/ihciah/go-shadowsocks2/core" 16 | ) 17 | 18 | const ( 19 | channel_buffer_size = 128 20 | default_Maxfail = 10 21 | default_Recovertime = 600 22 | default_Listen = "0.0.0.0" 23 | default_start_timeout = 8 24 | default_remote_timeout = 60 25 | default_inside_timeout = 60 26 | ) 27 | 28 | type Server struct { 29 | server string 30 | ciph core.Cipher 31 | addr string 32 | } 33 | 34 | type Config struct { 35 | listenAddr net.TCPAddr 36 | servers []Server 37 | auth bool 38 | username []byte 39 | password []byte 40 | scheduler Scheduler 41 | verbose bool 42 | rtimeout time.Duration 43 | itimeout time.Duration 44 | stimeout time.Duration 45 | whitelist Whitelist 46 | } 47 | 48 | type userConfig struct { 49 | Listen string `json:"listen"` 50 | Port int `json:"port"` 51 | Auth bool `json:"auth"` 52 | Username string `json:"username"` 53 | Password string `json:"password"` 54 | Servers []string `json:"servers"` 55 | Maxfail int `json:"maxfail"` 56 | Recovertime int `json:"recovertime"` 57 | Starttimeout int `json:"starttimeout"` 58 | Remotetimeout int `json:"remotetimeout"` 59 | Insidetimeout int `json:"insidetimeout"` 60 | Whitelistenable bool `json:"whitelistenable"` 61 | Whitelistdomains []string `json:"whitelistdomains"` 62 | Whitelistips []string `json:"whitelistips"` 63 | } 64 | 65 | type timeoutConn struct { 66 | net.Conn 67 | timelimit time.Duration 68 | starttime time.Duration 69 | active bool 70 | } 71 | 72 | func (uc *userConfig) loadServers() []Server { 73 | servers := make([]Server, 0, len(uc.Servers)) 74 | for _, st := range uc.Servers { 75 | s, err := makeServer(st) 76 | if err != nil { 77 | continue 78 | } 79 | servers = append(servers, s) 80 | } 81 | return servers 82 | } 83 | 84 | func (uc *userConfig) loadWhitelist() (w Whitelist) { 85 | domains := make([]string, 0, len(uc.Whitelistdomains)) 86 | ips := make([]net.IPNet, 0, len(uc.Whitelistips)) 87 | if !uc.Whitelistenable { 88 | return 89 | } 90 | for _, domain := range uc.Whitelistdomains { 91 | domains = append(domains, domain) 92 | } 93 | for _, ip := range uc.Whitelistips { 94 | _, ipnet, err := net.ParseCIDR(ip) 95 | if err != nil { 96 | continue 97 | } 98 | ips = append(ips, *ipnet) 99 | } 100 | return Whitelist{enable: uc.Whitelistenable, domainlist: domains, iplist: ips} 101 | } 102 | 103 | func (config *Config) log(f string, v ...interface{}) { 104 | if config.verbose { 105 | log.Printf(f, v...) 106 | } 107 | } 108 | 109 | func (tc timeoutConn) heartbeat() { 110 | if tc.active { 111 | tc.Conn.SetDeadline(time.Now().Add(tc.timelimit)) 112 | } else { 113 | tc.Conn.SetDeadline(time.Now().Add(tc.starttime)) 114 | tc.active = true 115 | } 116 | } 117 | func (tc timeoutConn) Read(buf []byte) (int, error) { 118 | tc.heartbeat() 119 | return tc.Conn.Read(buf) 120 | } 121 | 122 | func (tc timeoutConn) Write(buf []byte) (int, error) { 123 | tc.heartbeat() 124 | return tc.Conn.Write(buf) 125 | } 126 | 127 | func (config *Config) StartServer() { 128 | listener, err := net.ListenTCP("tcp", &config.listenAddr) 129 | defer listener.Close() 130 | if err != nil { 131 | panic("[inner-ss] Cannot listen on given ip and port!") 132 | } 133 | config.log("[inner-ss] Auth: %t, WhiteList: %t, RemoteTimeout: %d sec, InsideTimeout: %d sec.", 134 | config.auth, config.whitelist.enable, config.rtimeout/time.Second, config.itimeout/time.Second) 135 | config.log("[inner-ss] Listening %s on port %d.", config.listenAddr.IP, config.listenAddr.Port) 136 | for { 137 | conn, err := listener.AcceptTCP() 138 | if err != nil { 139 | config.log("[inner-ss] Failed to accept %s", err) 140 | continue 141 | } 142 | config.log("[inner-ss] Accept connection from %s", conn.RemoteAddr()) 143 | go config.handleConnection(conn) 144 | } 145 | } 146 | 147 | func bytein(y []byte, x byte) bool { 148 | for _, b := range y { 149 | if b == x { 150 | return true 151 | } 152 | } 153 | return false 154 | } 155 | 156 | func (config *Config) handleConnection(conn *net.TCPConn) error { 157 | defer conn.Close() 158 | conn.SetKeepAlive(true) 159 | if err := config.handleSocksEncrypt(conn); err != nil { 160 | config.log("[inner-ss] Error when validating user. %s", err) 161 | return err 162 | } 163 | addr, err := getAddr(conn) 164 | if err != nil { 165 | config.log("[inner-ss] Error when getAddr. %s", err) 166 | return err 167 | } 168 | if err := config.whitelist.check(addr); err != nil { 169 | config.log("[inner-ss] Error when checking ip or domain. %s", err) 170 | return err 171 | } 172 | server_id := config.scheduler.get() 173 | server, ciph := config.servers[server_id].addr, config.servers[server_id].ciph 174 | rc, err := net.Dial("tcp", server) 175 | if err != nil { 176 | config.log("[inner-ss] Cannot connect to shadowsocks server %s\n", server) 177 | config.scheduler.report_fail(server_id) 178 | return err 179 | } 180 | config.scheduler.report_success(server_id) 181 | defer rc.Close() 182 | rc.(*net.TCPConn).SetKeepAlive(true) 183 | rc = ciph.StreamConn(rc) 184 | if _, err := rc.Write(addr); err != nil { 185 | return err 186 | } 187 | _, _, rerr, err := relay(rc, conn, config.rtimeout, config.itimeout, config.stimeout) 188 | if rerr != nil { 189 | config.log("[inner-ss] Remote connection error. %s", rerr) 190 | return rerr 191 | } 192 | return err 193 | } 194 | 195 | func (config *Config) handleSocksEncrypt(conn *net.TCPConn) error { 196 | buf := make([]byte, 256) 197 | n, err := conn.Read(buf) 198 | if err != nil { 199 | return err 200 | } 201 | methods := buf[2:n] 202 | auth := byte(0x00) 203 | if config.auth { 204 | auth = 0x02 205 | } 206 | if buf[0] != 0x05 || !bytein(methods, auth) { 207 | return errors.New("Not Socks5 or auth type incorrect.") 208 | } 209 | conn.Write([]byte{0x05, auth}) 210 | if config.auth { 211 | n, err = conn.Read(buf) 212 | if err != nil { 213 | return err 214 | } 215 | if n < 3 || n < int(buf[1])+3 { 216 | return errors.New("Data not correct.") 217 | } 218 | username_len := int(buf[1]) 219 | username := buf[2 : 2+username_len] 220 | password := buf[3+username_len : n] 221 | if bytes.Equal(username, config.username) && bytes.Equal(password, config.password) { 222 | conn.Write([]byte{0x01, 0x00}) 223 | return nil 224 | } 225 | return errors.New("Invalid username or password.") 226 | } 227 | return nil 228 | } 229 | 230 | func getAddr(conn *net.TCPConn) ([]byte, error) { 231 | buf := make([]byte, 259) 232 | n, err := conn.Read(buf) 233 | if err != nil { 234 | return nil, err 235 | } 236 | if n < 7 { 237 | return nil, errors.New("Invalid packet.") 238 | } 239 | var dstAddr []byte 240 | switch buf[3] { 241 | case 0x01: 242 | if n < 6+net.IPv4len { 243 | return nil, errors.New("Invalid packet.") 244 | } 245 | dstAddr = buf[3 : 6+net.IPv4len] 246 | case 0x03: 247 | if n < 8 || n < 6+int(buf[4]) { 248 | return nil, errors.New("Invalid packet.") 249 | } 250 | dstAddr = buf[3 : 7+int(buf[4])] 251 | case 0x04: 252 | if n < 6+net.IPv6len { 253 | return nil, errors.New("Invalid packet.") 254 | } 255 | dstAddr = buf[3 : 6+net.IPv6len] 256 | default: 257 | return nil, errors.New("Invalid packet.") 258 | } 259 | 260 | switch buf[1] { 261 | case 0x01: 262 | conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10}) 263 | default: 264 | conn.Write([]byte{0x05, 0x07}) 265 | return nil, errors.New("Unsupported command.") 266 | } 267 | return dstAddr, nil 268 | } 269 | 270 | func relay(left, right net.Conn, rtimeout, itimeout, stimeout time.Duration) (int64, int64, error, error) { 271 | tleft := timeoutConn{Conn: left, timelimit: rtimeout, starttime: stimeout} 272 | tright := timeoutConn{Conn: right, timelimit: itimeout, starttime: stimeout} 273 | type res struct { 274 | N int64 275 | Err error 276 | } 277 | ch := make(chan res) 278 | 279 | go func() { 280 | n, err := io.Copy(tright, tleft) 281 | ch <- res{n, err} 282 | }() 283 | n, err := io.Copy(tleft, tright) 284 | rs := <-ch 285 | return n, rs.N, err, rs.Err 286 | } 287 | 288 | func parseURL(s string) (addr, cipher, password string, err error) { 289 | u, err := url.Parse(s) 290 | if err != nil { 291 | return 292 | } 293 | 294 | addr = u.Host 295 | if u.User != nil { 296 | cipher = u.User.Username() 297 | password, _ = u.User.Password() 298 | } 299 | return 300 | } 301 | 302 | func LoadUserConfig(config_file string, verbose bool) (Config, error) { 303 | user_config := userConfig{Maxfail: default_Maxfail, Recovertime: default_Recovertime, Listen: default_Listen, 304 | Remotetimeout: default_remote_timeout, Insidetimeout: default_inside_timeout, Starttimeout: default_start_timeout, 305 | Whitelistenable: false} 306 | config := Config{verbose: verbose} 307 | data, err := ioutil.ReadFile(config_file) 308 | if err != nil { 309 | return config, err 310 | } 311 | if err := json.Unmarshal(data, &user_config); err != nil { 312 | return config, err 313 | } 314 | if user_config.Listen == "" || user_config.Port == 0 { 315 | return config, errors.New("Cannot load config.") 316 | } 317 | config.listenAddr = net.TCPAddr{IP: net.ParseIP(user_config.Listen), Port: user_config.Port} 318 | config.auth, config.username, config.password = user_config.Auth, []byte(user_config.Username), []byte(user_config.Password) 319 | config.servers = user_config.loadServers() 320 | config.whitelist = user_config.loadWhitelist() 321 | config.whitelist.logger = config.log 322 | config.scheduler = Scheduler{} 323 | config.scheduler.init(len(config.servers), user_config.Maxfail, channel_buffer_size, user_config.Recovertime, verbose) 324 | config.rtimeout = time.Duration(user_config.Remotetimeout) * time.Second 325 | config.itimeout = time.Duration(user_config.Insidetimeout) * time.Second 326 | config.stimeout = time.Duration(user_config.Starttimeout) * time.Second 327 | return config, nil 328 | } 329 | 330 | func makeServer(s string) (Server, error) { 331 | addr, cipher, password, err := parseURL(s) 332 | if err != nil { 333 | return Server{}, err 334 | } 335 | ciph, err := core.PickCipher(cipher, []byte{}, password) 336 | if err != nil { 337 | return Server{}, err 338 | } 339 | return Server{s, ciph, addr}, nil 340 | } 341 | 342 | func main() { 343 | var config_file string 344 | var verbose bool 345 | flag.BoolVar(&verbose, "v", false, "verbose mode") 346 | flag.StringVar(&config_file, "c", "config.json", "config file path") 347 | flag.Parse() 348 | 349 | c, err := LoadUserConfig(config_file, verbose) 350 | if err != nil { 351 | log.Println("Error!", err) 352 | return 353 | } 354 | c.StartServer() 355 | } 356 | -------------------------------------------------------------------------------- /inner-ss/schedule.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type Scheduler struct { 10 | max_fail int 11 | status []bool 12 | fail_count []int 13 | succ_chan chan int 14 | fail_chan chan int 15 | lock sync.Mutex 16 | verbose bool 17 | } 18 | 19 | func (s *Scheduler) log(f string, v ...interface{}) { 20 | if s.verbose { 21 | log.Printf(f, v...) 22 | } 23 | } 24 | 25 | func (s *Scheduler) get() int { 26 | for i, v := range s.status { 27 | if v { 28 | s.log("[Schedule] Get server %d.", i) 29 | return i 30 | } 31 | } 32 | s.lock.Lock() 33 | for i := range s.status { 34 | s.status[i] = true 35 | s.fail_count[i] = 0 36 | } 37 | s.lock.Unlock() 38 | s.log("[Schedule] All servers down. Restart all of them. Get 0") 39 | return 0 40 | } 41 | 42 | func (s *Scheduler) report_success(id int) { 43 | s.log("[Schedule] %d success.", id) 44 | s.succ_chan <- id 45 | } 46 | 47 | func (s *Scheduler) report_fail(id int) { 48 | s.log("[Schedule] %d fail.", id) 49 | s.fail_chan <- id 50 | } 51 | 52 | func (s *Scheduler) init(n, max_fail, chan_buf, recover_time int, verbose bool) { 53 | s.verbose = verbose 54 | s.max_fail = max_fail 55 | s.status = make([]bool, n) 56 | for i := range s.status { 57 | s.status[i] = true 58 | } 59 | s.fail_count = make([]int, n) 60 | s.succ_chan, s.fail_chan = make(chan int, chan_buf), make(chan int, chan_buf) 61 | go s.process(recover_time) 62 | s.log("[Schedule] Init. Maxfail=%d, Recover_time=%d sec, channel_buffer_size=%d.", max_fail, recover_time, chan_buf) 63 | } 64 | 65 | func (s *Scheduler) process(recover_time int) { 66 | for { 67 | select { 68 | case succ := <-s.succ_chan: 69 | s.lock.Lock() 70 | s.status[succ] = true 71 | s.fail_count[succ] = 0 72 | s.lock.Unlock() 73 | case fail := <-s.fail_chan: 74 | s.lock.Lock() 75 | if s.status[fail] == true { 76 | if s.fail_count[fail] >= s.max_fail { 77 | s.fail_count[fail] = 0 78 | s.status[fail] = false 79 | go func(locker *sync.Mutex, timer *time.Timer) { 80 | <-timer.C 81 | locker.Lock() 82 | s.status[fail] = true 83 | s.fail_count[fail] = 0 84 | locker.Unlock() 85 | s.log("[Schedule] Server %d up due to time exceed.", fail) 86 | }(&s.lock, time.NewTimer(time.Second*time.Duration(recover_time))) 87 | s.log("[Schedule] Server %d down.", fail) 88 | } else { 89 | s.fail_count[fail]++ 90 | s.log("[Schedule] %d fail count: %d.", fail, s.fail_count[fail]) 91 | } 92 | } 93 | s.lock.Unlock() 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /inner-ss/whitelist.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "strings" 7 | ) 8 | 9 | type Whitelist struct { 10 | enable bool 11 | domainlist []string 12 | iplist []net.IPNet 13 | logger func(string, ...interface{}) 14 | } 15 | 16 | func (w *Whitelist) check_ip(ip net.IP) bool { 17 | if !w.enable { 18 | return true 19 | } 20 | for _, ipnet := range w.iplist { 21 | if ipnet.Contains(ip) { 22 | return true 23 | } 24 | } 25 | return false 26 | } 27 | 28 | func (w *Whitelist) check_domain(d string) bool { 29 | if !w.enable { 30 | return true 31 | } 32 | for _, domain := range w.domainlist { 33 | if strings.HasSuffix(d, domain) { 34 | return true 35 | } 36 | } 37 | return false 38 | } 39 | 40 | func (w *Whitelist) check(data []byte) error { 41 | switch data[0] { 42 | case 0x01: 43 | if w.check_ip(net.IP(data[1 : 1+net.IPv4len])) { 44 | w.logger("[whitelist] Whitelist ipv4 pass.") 45 | return nil 46 | } 47 | w.logger("[whitelist] Whitelist ipv4 reject.") 48 | return errors.New("IPv4 not in whitelist.") 49 | case 0x03: 50 | if w.check_domain(string(data[2 : 2+data[1]])) { 51 | w.logger("[whitelist] Whitelist domain pass.") 52 | return nil 53 | } 54 | w.logger("[whitelist] Whitelist domain reject.") 55 | return errors.New("Domain not in whitelist.") 56 | case 0x04: 57 | if w.check_ip(net.IP(data[1 : 1+net.IPv6len])) { 58 | w.logger("[whitelist] Whitelist ipv6 pass.") 59 | return nil 60 | } 61 | w.logger("[whitelist] Whitelist ipv6 reject.") 62 | return errors.New("IPv6 not in whitelist.") 63 | } 64 | return errors.New("Unknown error.") 65 | } 66 | --------------------------------------------------------------------------------