-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
898 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# S5light | ||
A lightweight socks5 proxy server and install script. | ||
|
||
This software supports Windows/MacOS/Centos/Debian/Ubuntu and, in theory, all Linux. | ||
|
||
- Support Multiple Validation | ||
- Support for IP address whitelisting | ||
|
||
The configuration file using YAML language. | ||
|
||
Please fill in the template according to "config.yaml.example", and save it as "config.yaml". | ||
|
||
You can also use the "--config" flag to customize the path to the config file. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"os" | ||
|
||
"github.com/hang666/s5light" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
func main() { | ||
var configPath string | ||
|
||
app := &cli.App{ | ||
Name: "s5light", | ||
Usage: "A lightweight socks5 proxy server.", | ||
Version: "v0.1.0", | ||
Flags: []cli.Flag{ | ||
&cli.StringFlag{ | ||
Name: "config", | ||
Aliases: []string{"c"}, | ||
Value: "", | ||
Usage: "config file path", | ||
Destination: &configPath, | ||
}, | ||
}, | ||
Action: func(*cli.Context) error { | ||
s5light.SetConfigPath(configPath) | ||
s5light.ReadConfig() | ||
s5light.Server() | ||
return nil | ||
}, | ||
} | ||
|
||
if err := app.Run(os.Args); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package s5light | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/spf13/viper" | ||
) | ||
|
||
var Accounts []*AccountStruct | ||
|
||
var tcp_timeout_default = 60 | ||
var udp_timeout_default = 60 | ||
|
||
type AccountStruct struct { | ||
Username string `yaml:"username" mapstructure:"username"` | ||
Password string `yaml:"password" mapstructure:"password"` | ||
BindAddress string `yaml:"bind_address" mapstructure:"bind_address"` | ||
ReqAddress string `yaml:"req_address" mapstructure:"req_address"` | ||
Whitelist []string `yaml:"whitelist" mapstructure:"whitelist"` | ||
TCPTimeout int `yaml:"tcp_timeout" mapstructure:"tcp_timeout"` | ||
UDPTimeout int `yaml:"udp_timeout" mapstructure:"udp_timeout"` | ||
WhitelistMap WhitelistMapType | ||
} | ||
|
||
type WhitelistMapType map[string]bool | ||
|
||
var customConfigPath string = "" | ||
|
||
func SetConfigPath(path string) { | ||
customConfigPath = path | ||
} | ||
|
||
func ReadConfig() { | ||
viper.SetConfigName("config") | ||
viper.SetConfigType("yaml") | ||
if customConfigPath == "" { | ||
viper.AddConfigPath("/etc/s5light/") | ||
viper.AddConfigPath("$HOME/.s5light") | ||
viper.AddConfigPath(".") | ||
} else { | ||
viper.SetConfigFile(customConfigPath) | ||
} | ||
err := viper.ReadInConfig() | ||
if err != nil { | ||
panic(fmt.Errorf("fatal error config file: %w", err)) | ||
} | ||
viper.UnmarshalKey("accounts", &Accounts) | ||
|
||
for _, acc := range Accounts { | ||
if acc.TCPTimeout == 0 { | ||
acc.TCPTimeout = tcp_timeout_default | ||
} | ||
if acc.UDPTimeout == 0 { | ||
acc.UDPTimeout = udp_timeout_default | ||
} | ||
wMap := make(map[string]bool) | ||
for _, w := range acc.Whitelist { | ||
if w != "" { | ||
wMap[w] = true | ||
} | ||
} | ||
acc.WhitelistMap = wMap | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
accounts: | ||
- username: "test" | ||
password: "test123456" | ||
bind_address: "0.0.0.0:8080" | ||
req_address: "" | ||
tcp_timeout: 60 | ||
udp_timeout: 60 | ||
whitelist: | ||
- "192.168.100.1" | ||
- "127.0.0.1" | ||
- bind_address: "0.0.0.0:8081" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
module github.com/hang666/s5light | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/spf13/viper v1.14.0 | ||
github.com/txthinking/socks5 v0.0.0-20220615051428-39268faee3e6 | ||
github.com/urfave/cli/v2 v2.23.5 | ||
) | ||
|
||
require ( | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect | ||
github.com/fsnotify/fsnotify v1.6.0 // indirect | ||
github.com/hashicorp/hcl v1.0.0 // indirect | ||
github.com/magiconair/properties v1.8.6 // indirect | ||
github.com/mitchellh/mapstructure v1.5.0 // indirect | ||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect | ||
github.com/pelletier/go-toml v1.9.5 // indirect | ||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect | ||
github.com/russross/blackfriday/v2 v2.1.0 // indirect | ||
github.com/spf13/afero v1.9.2 // indirect | ||
github.com/spf13/cast v1.5.0 // indirect | ||
github.com/spf13/jwalterweatherman v1.1.0 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
github.com/subosito/gotenv v1.4.1 // indirect | ||
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect | ||
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect | ||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect | ||
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect | ||
golang.org/x/text v0.4.0 // indirect | ||
gopkg.in/ini.v1 v1.67.0 // indirect | ||
gopkg.in/yaml.v2 v2.4.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
package s5light | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"net" | ||
"strings" | ||
"time" | ||
|
||
"github.com/txthinking/socks5" | ||
) | ||
|
||
type DefaultHandle struct { | ||
whitelistMap WhitelistMapType | ||
} | ||
|
||
func checkIsWhitelisted(address string, whitelistMap WhitelistMapType) bool { | ||
//log.Printf("client come in: %s", address) | ||
w_map := whitelistMap | ||
if len(w_map) == 0 { | ||
return true | ||
} | ||
var ok bool | ||
if strings.Contains(address, ":") { | ||
_, ok = w_map[strings.Split(address, ":")[0]] | ||
} else { | ||
_, ok = w_map[address] | ||
} | ||
return ok | ||
} | ||
|
||
func (h *DefaultHandle) TCPHandle(s *socks5.Server, c *net.TCPConn, r *socks5.Request) error { | ||
if !checkIsWhitelisted(c.RemoteAddr().String(), h.whitelistMap) { | ||
return fmt.Errorf("%s is not whitelisted", c.RemoteAddr().String()) | ||
} | ||
if r.Cmd == socks5.CmdConnect { | ||
rc, err := r.Connect(c) | ||
if err != nil { | ||
return err | ||
} | ||
defer rc.Close() | ||
go func() { | ||
var bf [1024 * 2]byte | ||
for { | ||
if s.TCPTimeout != 0 { | ||
if err := rc.SetDeadline(time.Now().Add(time.Duration(s.TCPTimeout) * time.Second)); err != nil { | ||
return | ||
} | ||
} | ||
i, err := rc.Read(bf[:]) | ||
if err != nil { | ||
return | ||
} | ||
if _, err := c.Write(bf[0:i]); err != nil { | ||
return | ||
} | ||
} | ||
}() | ||
var bf [1024 * 2]byte | ||
for { | ||
if s.TCPTimeout != 0 { | ||
if err := c.SetDeadline(time.Now().Add(time.Duration(s.TCPTimeout) * time.Second)); err != nil { | ||
return nil | ||
} | ||
} | ||
i, err := c.Read(bf[:]) | ||
if err != nil { | ||
return nil | ||
} | ||
if _, err := rc.Write(bf[0:i]); err != nil { | ||
return nil | ||
} | ||
} | ||
return nil | ||
} | ||
if r.Cmd == socks5.CmdUDP { | ||
caddr, err := r.UDP(c, s.ServerAddr) | ||
if err != nil { | ||
return err | ||
} | ||
ch := make(chan byte) | ||
defer close(ch) | ||
s.AssociatedUDP.Set(caddr.String(), ch, -1) | ||
defer s.AssociatedUDP.Delete(caddr.String()) | ||
io.Copy(io.Discard, c) | ||
if socks5.Debug { | ||
log.Printf("A tcp connection that udp %#v associated closed\n", caddr.String()) | ||
} | ||
return nil | ||
} | ||
return socks5.ErrUnsupportCmd | ||
} | ||
|
||
func (h *DefaultHandle) UDPHandle(s *socks5.Server, addr *net.UDPAddr, d *socks5.Datagram) error { | ||
if !checkIsWhitelisted(string(addr.IP), h.whitelistMap) { | ||
return fmt.Errorf("%s is not whitelisted", string(addr.IP)) | ||
} | ||
src := addr.String() | ||
var ch chan byte | ||
if s.LimitUDP { | ||
any, ok := s.AssociatedUDP.Get(src) | ||
if !ok { | ||
return fmt.Errorf("This udp address %s is not associated with tcp", src) | ||
} | ||
ch = any.(chan byte) | ||
} | ||
send := func(ue *socks5.UDPExchange, data []byte) error { | ||
select { | ||
case <-ch: | ||
return fmt.Errorf("This udp address %s is not associated with tcp", src) | ||
default: | ||
_, err := ue.RemoteConn.Write(data) | ||
if err != nil { | ||
return err | ||
} | ||
if socks5.Debug { | ||
log.Printf("Sent UDP data to remote. client: %#v server: %#v remote: %#v data: %#v\n", ue.ClientAddr.String(), ue.RemoteConn.LocalAddr().String(), ue.RemoteConn.RemoteAddr().String(), data) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
dst := d.Address() | ||
var ue *socks5.UDPExchange | ||
iue, ok := s.UDPExchanges.Get(src + dst) | ||
if ok { | ||
ue = iue.(*socks5.UDPExchange) | ||
return send(ue, d.Data) | ||
} | ||
|
||
if socks5.Debug { | ||
log.Printf("Call udp: %#v\n", dst) | ||
} | ||
var laddr *net.UDPAddr | ||
any, ok := s.UDPSrc.Get(src + dst) | ||
if ok { | ||
laddr = any.(*net.UDPAddr) | ||
} | ||
raddr, err := net.ResolveUDPAddr("udp", dst) | ||
if err != nil { | ||
return err | ||
} | ||
rc, err := socks5.Dial.DialUDP("udp", laddr, raddr) | ||
if err != nil { | ||
if !strings.Contains(err.Error(), "address already in use") { | ||
return err | ||
} | ||
rc, err = socks5.Dial.DialUDP("udp", nil, raddr) | ||
if err != nil { | ||
return err | ||
} | ||
laddr = nil | ||
} | ||
if laddr == nil { | ||
s.UDPSrc.Set(src+dst, rc.LocalAddr().(*net.UDPAddr), -1) | ||
} | ||
ue = &socks5.UDPExchange{ | ||
ClientAddr: addr, | ||
RemoteConn: rc, | ||
} | ||
if socks5.Debug { | ||
log.Printf("Created remote UDP conn for client. client: %#v server: %#v remote: %#v\n", addr.String(), ue.RemoteConn.LocalAddr().String(), d.Address()) | ||
} | ||
if err := send(ue, d.Data); err != nil { | ||
ue.RemoteConn.Close() | ||
return err | ||
} | ||
s.UDPExchanges.Set(src+dst, ue, -1) | ||
go func(ue *socks5.UDPExchange, dst string) { | ||
defer func() { | ||
ue.RemoteConn.Close() | ||
s.UDPExchanges.Delete(ue.ClientAddr.String() + dst) | ||
}() | ||
var b [65507]byte | ||
for { | ||
select { | ||
case <-ch: | ||
if socks5.Debug { | ||
log.Printf("The tcp that udp address %s associated closed\n", ue.ClientAddr.String()) | ||
} | ||
return | ||
default: | ||
if s.UDPTimeout != 0 { | ||
if err := ue.RemoteConn.SetDeadline(time.Now().Add(time.Duration(s.UDPTimeout) * time.Second)); err != nil { | ||
log.Println(err) | ||
return | ||
} | ||
} | ||
n, err := ue.RemoteConn.Read(b[:]) | ||
if err != nil { | ||
return | ||
} | ||
if socks5.Debug { | ||
log.Printf("Got UDP data from remote. client: %#v server: %#v remote: %#v data: %#v\n", ue.ClientAddr.String(), ue.RemoteConn.LocalAddr().String(), ue.RemoteConn.RemoteAddr().String(), b[0:n]) | ||
} | ||
a, addr, port, err := socks5.ParseAddress(dst) | ||
if err != nil { | ||
log.Println(err) | ||
return | ||
} | ||
d1 := socks5.NewDatagram(a, addr, port, b[0:n]) | ||
if _, err := s.UDPConn.WriteToUDP(d1.Bytes(), ue.ClientAddr); err != nil { | ||
return | ||
} | ||
if socks5.Debug { | ||
log.Printf("Sent Datagram. client: %#v server: %#v remote: %#v data: %#v %#v %#v %#v %#v %#v datagram address: %#v\n", ue.ClientAddr.String(), ue.RemoteConn.LocalAddr().String(), ue.RemoteConn.RemoteAddr().String(), d1.Rsv, d1.Frag, d1.Atyp, d1.DstAddr, d1.DstPort, d1.Data, d1.Address()) | ||
} | ||
} | ||
} | ||
}(ue, dst) | ||
return nil | ||
} |
Oops, something went wrong.