Commit c84026fa authored by ginuerzh's avatar ginuerzh

Merge branch '2.6'

parents fa085c56 434d34bb
...@@ -26,5 +26,5 @@ _testmain.go ...@@ -26,5 +26,5 @@ _testmain.go
*.bak *.bak
cmd/gost cmd/gost/gost
snap snap
...@@ -26,6 +26,8 @@ gost - GO Simple Tunnel ...@@ -26,6 +26,8 @@ gost - GO Simple Tunnel
* [支持SNI代理](https://docs.ginuerzh.xyz/gost/sni/) * [支持SNI代理](https://docs.ginuerzh.xyz/gost/sni/)
* [权限控制](https://docs.ginuerzh.xyz/gost/permission/) * [权限控制](https://docs.ginuerzh.xyz/gost/permission/)
* [负载均衡](https://docs.ginuerzh.xyz/gost/load-balancing/) * [负载均衡](https://docs.ginuerzh.xyz/gost/load-balancing/)
* [路由控制](https://docs.ginuerzh.xyz/gost/bypass/)
* [DNS控制](https://docs.ginuerzh.xyz/gost/dns/)
Wiki站点: <https://docs.ginuerzh.xyz/gost/> Wiki站点: <https://docs.ginuerzh.xyz/gost/>
......
package gost
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync"
"time"
glob "github.com/gobwas/glob"
)
// Matcher is a generic pattern matcher,
// it gives the match result of the given pattern for specific v.
type Matcher interface {
Match(v string) bool
String() string
}
// NewMatcher creates a Matcher for the given pattern.
// The acutal Matcher depends on the pattern:
// IP Matcher if pattern is a valid IP address.
// CIDR Matcher if pattern is a valid CIDR address.
// Domain Matcher if both of the above are not.
func NewMatcher(pattern string) Matcher {
if pattern == "" {
return nil
}
if ip := net.ParseIP(pattern); ip != nil {
return IPMatcher(ip)
}
if _, inet, err := net.ParseCIDR(pattern); err == nil {
return CIDRMatcher(inet)
}
return DomainMatcher(pattern)
}
type ipMatcher struct {
ip net.IP
}
// IPMatcher creates a Matcher for a specific IP address.
func IPMatcher(ip net.IP) Matcher {
return &ipMatcher{
ip: ip,
}
}
func (m *ipMatcher) Match(ip string) bool {
if m == nil {
return false
}
return m.ip.Equal(net.ParseIP(ip))
}
func (m *ipMatcher) String() string {
return "ip " + m.ip.String()
}
type cidrMatcher struct {
ipNet *net.IPNet
}
// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.
func CIDRMatcher(inet *net.IPNet) Matcher {
return &cidrMatcher{
ipNet: inet,
}
}
func (m *cidrMatcher) Match(ip string) bool {
if m == nil || m.ipNet == nil {
return false
}
return m.ipNet.Contains(net.ParseIP(ip))
}
func (m *cidrMatcher) String() string {
return "cidr " + m.ipNet.String()
}
type domainMatcher struct {
pattern string
glob glob.Glob
}
// DomainMatcher creates a Matcher for a specific domain pattern,
// the pattern can be a plain domain such as 'example.com',
// a wildcard such as '*.exmaple.com' or a special wildcard '.example.com'.
func DomainMatcher(pattern string) Matcher {
p := pattern
if strings.HasPrefix(pattern, ".") {
p = pattern[1:] // trim the prefix '.'
pattern = "*" + pattern
}
return &domainMatcher{
pattern: p,
glob: glob.MustCompile(pattern),
}
}
func (m *domainMatcher) Match(domain string) bool {
if m == nil || m.glob == nil {
return false
}
if domain == m.pattern {
return true
}
return m.glob.Match(domain)
}
func (m *domainMatcher) String() string {
return "domain " + m.pattern
}
// Bypass is a filter for address (IP or domain).
// It contains a list of matchers.
type Bypass struct {
matchers []Matcher
reversed bool
period time.Duration // the period for live reloading
mux sync.Mutex
}
// NewBypass creates and initializes a new Bypass using matchers as its match rules.
// The rules will be reversed if the reversed is true.
func NewBypass(reversed bool, matchers ...Matcher) *Bypass {
return &Bypass{
matchers: matchers,
reversed: reversed,
}
}
// NewBypassPatterns creates and initializes a new Bypass using matcher patterns as its match rules.
// The rules will be reversed if the reverse is true.
func NewBypassPatterns(reversed bool, patterns ...string) *Bypass {
var matchers []Matcher
for _, pattern := range patterns {
if pattern != "" {
matchers = append(matchers, NewMatcher(pattern))
}
}
return NewBypass(reversed, matchers...)
}
// Contains reports whether the bypass includes addr.
func (bp *Bypass) Contains(addr string) bool {
if bp == nil {
return false
}
// try to strip the port
if host, port, _ := net.SplitHostPort(addr); host != "" && port != "" {
if p, _ := strconv.Atoi(port); p > 0 { // port is valid
addr = host
}
}
bp.mux.Lock()
defer bp.mux.Unlock()
var matched bool
for _, matcher := range bp.matchers {
if matcher == nil {
continue
}
if matcher.Match(addr) {
matched = true
break
}
}
return !bp.reversed && matched ||
bp.reversed && !matched
}
// AddMatchers appends matchers to the bypass matcher list.
func (bp *Bypass) AddMatchers(matchers ...Matcher) {
bp.matchers = append(bp.matchers, matchers...)
}
// Matchers return the bypass matcher list.
func (bp *Bypass) Matchers() []Matcher {
return bp.matchers
}
// Reversed reports whether the rules of the bypass are reversed.
func (bp *Bypass) Reversed() bool {
return bp.reversed
}
// Reload parses config from r, then live reloads the bypass.
func (bp *Bypass) Reload(r io.Reader) error {
var matchers []Matcher
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
if n := strings.IndexByte(line, '#'); n >= 0 {
line = line[:n]
}
line = strings.Replace(line, "\t", " ", -1)
line = strings.TrimSpace(line)
if line == "" {
continue
}
// reload option
if strings.HasPrefix(line, "reload ") {
var ss []string
for _, s := range strings.Split(line, " ") {
if s = strings.TrimSpace(s); s != "" {
ss = append(ss, s)
}
}
if len(ss) == 2 {
bp.period, _ = time.ParseDuration(ss[1])
continue
}
}
// reverse option
if strings.HasPrefix(line, "reverse ") {
var ss []string
for _, s := range strings.Split(line, " ") {
if s = strings.TrimSpace(s); s != "" {
ss = append(ss, s)
}
}
if len(ss) == 2 {
bp.reversed, _ = strconv.ParseBool(ss[1])
continue
}
}
matchers = append(matchers, NewMatcher(line))
}
if err := scanner.Err(); err != nil {
return err
}
bp.mux.Lock()
defer bp.mux.Unlock()
bp.matchers = matchers
return nil
}
// Period returns the reload period
func (bp *Bypass) Period() time.Duration {
return bp.period
}
func (bp *Bypass) String() string {
b := &bytes.Buffer{}
fmt.Fprintf(b, "reversed: %v\n", bp.Reversed())
for _, m := range bp.Matchers() {
b.WriteString(m.String())
b.WriteByte('\n')
}
return b.String()
}
package gost
import "testing"
var bypassTests = []struct {
patterns []string
reversed bool
addr string
bypassed bool
}{
// IP address
{[]string{"192.168.1.1"}, false, "192.168.1.1", true},
{[]string{"192.168.1.1"}, false, "192.168.1.2", false},
{[]string{"0.0.0.0"}, false, "0.0.0.0", true},
// CIDR address
{[]string{"192.168.1.0/0"}, false, "1.2.3.4", true},
{[]string{"192.168.1.0/8"}, false, "192.1.0.255", true},
{[]string{"192.168.1.0/16"}, false, "192.168.0.255", true},
{[]string{"192.168.1.0/24"}, false, "192.168.1.255", true},
{[]string{"192.168.1.1/32"}, false, "192.168.1.1", true},
{[]string{"192.168.1.1/32"}, false, "192.168.1.2", false},
// plain domain
{[]string{"www.example.com"}, false, "www.example.com", true},
{[]string{"http://www.example.com"}, false, "http://www.example.com", true},
{[]string{"http://www.example.com"}, false, "http://example.com", false},
{[]string{"www.example.com"}, false, "example.com", false},
// domain wildcard
// sub-domain
{[]string{"*.example.com"}, false, "example.com", false},
{[]string{"*.example.com"}, false, "http://example.com", false},
{[]string{"*.example.com"}, false, "www.example.com", true},
{[]string{"*.example.com"}, false, "http://www.example.com", true},
{[]string{"*.example.com"}, false, "abc.def.example.com", true},
{[]string{"*.*.example.com"}, false, "example.com", false},
{[]string{"*.*.example.com"}, false, "www.example.com", false},
{[]string{"*.*.example.com"}, false, "abc.def.example.com", true},
{[]string{"*.*.example.com"}, false, "abc.def.ghi.example.com", true},
{[]string{"**.example.com"}, false, "example.com", false},
{[]string{"**.example.com"}, false, "www.example.com", true},
{[]string{"**.example.com"}, false, "abc.def.ghi.example.com", true},
// prefix wildcard
{[]string{"*example.com"}, false, "example.com", true},
{[]string{"*example.com"}, false, "www.example.com", true},
{[]string{"*example.com"}, false, "abc.defexample.com", true},
{[]string{"*example.com"}, false, "abc.def-example.com", true},
{[]string{"*example.com"}, false, "abc.def.example.com", true},
{[]string{"*example.com"}, false, "http://www.example.com", true},
{[]string{"*example.com"}, false, "e-xample.com", false},
{[]string{"http://*.example.com"}, false, "example.com", false},
{[]string{"http://*.example.com"}, false, "http://example.com", false},
{[]string{"http://*.example.com"}, false, "http://www.example.com", true},
{[]string{"http://*.example.com"}, false, "https://www.example.com", false},
{[]string{"http://*.example.com"}, false, "http://abc.def.example.com", true},
{[]string{"www.*.com"}, false, "www.example.com", true},
{[]string{"www.*.com"}, false, "www.abc.def.com", true},
{[]string{"www.*.*.com"}, false, "www.example.com", false},
{[]string{"www.*.*.com"}, false, "www.abc.def.com", true},
{[]string{"www.*.*.com"}, false, "www.abc.def.ghi.com", true},
{[]string{"www.*example*.com"}, false, "www.example.com", true},
{[]string{"www.*example*.com"}, false, "www.abc.example.def.com", true},
{[]string{"www.*example*.com"}, false, "www.e-xample.com", false},
{[]string{"www.example.*"}, false, "www.example.com", true},
{[]string{"www.example.*"}, false, "www.example.io", true},
{[]string{"www.example.*"}, false, "www.example.com.cn", true},
{[]string{".example.com"}, false, "www.example.com", true},
{[]string{".example.com"}, false, "example.com", true},
{[]string{".example.com"}, false, "www.example.com.cn", false},
}
func TestBypass(t *testing.T) {
for _, test := range bypassTests {
bp := NewBypassPatterns(test.reversed, test.patterns...)
if bp.Contains(test.addr) != test.bypassed {
t.Errorf("test failed: %v, %s", test.patterns, test.addr)
}
rbp := NewBypassPatterns(!test.reversed, test.patterns...)
if rbp.Contains(test.addr) == test.bypassed {
t.Errorf("reverse test failed: %v, %s", test.patterns, test.addr)
}
}
}
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"time"
"github.com/go-log/log" "github.com/go-log/log"
) )
...@@ -99,13 +100,22 @@ func (c *Chain) IsEmpty() bool { ...@@ -99,13 +100,22 @@ func (c *Chain) IsEmpty() bool {
// Dial connects to the target address addr through the chain. // Dial connects to the target address addr through the chain.
// If the chain is empty, it will use the net.Dial directly. // If the chain is empty, it will use the net.Dial directly.
func (c *Chain) Dial(addr string) (conn net.Conn, err error) { func (c *Chain) Dial(addr string, opts ...ChainOption) (conn net.Conn, err error) {
if c.IsEmpty() { options := &ChainOptions{}
return net.DialTimeout("tcp", addr, DialTimeout) for _, opt := range opts {
opt(options)
} }
for i := 0; i < c.Retries+1; i++ { retries := 1
conn, err = c.dial(addr) if c != nil && c.Retries > 0 {
retries = c.Retries
}
if options.Retries > 0 {
retries = options.Retries
}
for i := 0; i < retries; i++ {
conn, err = c.dialWithOptions(addr, options)
if err == nil { if err == nil {
break break
} }
...@@ -113,12 +123,21 @@ func (c *Chain) Dial(addr string) (conn net.Conn, err error) { ...@@ -113,12 +123,21 @@ func (c *Chain) Dial(addr string) (conn net.Conn, err error) {
return return
} }
func (c *Chain) dial(addr string) (net.Conn, error) { func (c *Chain) dialWithOptions(addr string, options *ChainOptions) (net.Conn, error) {
route, err := c.selectRoute() if options == nil {
options = &ChainOptions{}
}
route, err := c.selectRouteFor(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
addr = c.resolve(addr, options.Resolver, options.Hosts)
if route.IsEmpty() {
return net.DialTimeout("tcp", addr, options.Timeout)
}
conn, err := route.getConn() conn, err := route.getConn()
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -132,10 +151,44 @@ func (c *Chain) dial(addr string) (net.Conn, error) { ...@@ -132,10 +151,44 @@ func (c *Chain) dial(addr string) (net.Conn, error) {
return cc, nil return cc, nil
} }
func (c *Chain) resolve(addr string, resolver Resolver, hosts *Hosts) string {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return addr
}
if ip := hosts.Lookup(host); ip != nil {
return net.JoinHostPort(ip.String(), port)
}
if resolver != nil {
ips, err := resolver.Resolve(host)
if err != nil {
log.Logf("[resolver] %s: %v", host, err)
}
if len(ips) > 0 {
return net.JoinHostPort(ips[0].String(), port)
}
}
return addr
}
// Conn obtains a handshaked connection to the last node of the chain. // Conn obtains a handshaked connection to the last node of the chain.
// If the chain is empty, it returns an ErrEmptyChain error. // If the chain is empty, it returns an ErrEmptyChain error.
func (c *Chain) Conn() (conn net.Conn, err error) { func (c *Chain) Conn(opts ...ChainOption) (conn net.Conn, err error) {
for i := 0; i < c.Retries+1; i++ { options := &ChainOptions{}
for _, opt := range opts {
opt(options)
}
retries := 1
if c != nil && c.Retries > 0 {
retries = c.Retries
}
if options.Retries > 0 {
retries = options.Retries
}
for i := 0; i < retries; i++ {
var route *Chain var route *Chain
route, err = c.selectRoute() route, err = c.selectRoute()
if err != nil { if err != nil {
...@@ -143,6 +196,7 @@ func (c *Chain) Conn() (conn net.Conn, err error) { ...@@ -143,6 +196,7 @@ func (c *Chain) Conn() (conn net.Conn, err error) {
} }
conn, err = route.getConn() conn, err = route.getConn()
if err != nil { if err != nil {
log.Log(err)
continue continue
} }
...@@ -151,6 +205,7 @@ func (c *Chain) Conn() (conn net.Conn, err error) { ...@@ -151,6 +205,7 @@ func (c *Chain) Conn() (conn net.Conn, err error) {
return return
} }
// getConn obtains a connection to the last node of the chain.
func (c *Chain) getConn() (conn net.Conn, err error) { func (c *Chain) getConn() (conn net.Conn, err error) {
if c.IsEmpty() { if c.IsEmpty() {
err = ErrEmptyChain err = ErrEmptyChain
...@@ -198,13 +253,12 @@ func (c *Chain) getConn() (conn net.Conn, err error) { ...@@ -198,13 +253,12 @@ func (c *Chain) getConn() (conn net.Conn, err error) {
} }
func (c *Chain) selectRoute() (route *Chain, err error) { func (c *Chain) selectRoute() (route *Chain, err error) {
if c.isRoute { if c.IsEmpty() || c.isRoute {
return c, nil return c, nil
} }
buf := bytes.Buffer{} buf := bytes.Buffer{}
route = newRoute() route = newRoute()
route.Retries = c.Retries
for _, group := range c.nodeGroups { for _, group := range c.nodeGroups {
node, err := group.Next() node, err := group.Next()
...@@ -218,13 +272,97 @@ func (c *Chain) selectRoute() (route *Chain, err error) { ...@@ -218,13 +272,97 @@ func (c *Chain) selectRoute() (route *Chain, err error) {
ChainDialOption(route), ChainDialOption(route),
) )
route = newRoute() // cutoff the chain for multiplex. route = newRoute() // cutoff the chain for multiplex.
route.Retries = c.Retries
} }
route.AddNode(node) route.AddNode(node)
} }
route.Retries = c.Retries
if Debug { if Debug {
log.Log("select route:", buf.String()) log.Log("select route:", buf.String())
} }
return return
} }
// selectRouteFor selects route with bypass testing.
func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
if c.IsEmpty() || c.isRoute {
return c, nil
}
buf := bytes.Buffer{}
route = newRoute()
for _, group := range c.nodeGroups {
var node Node
node, err = group.Next()
if err != nil {
return
}
if node.Bypass.Contains(addr) {
if Debug {
buf.WriteString(fmt.Sprintf("[bypass]%s -> %s", node.String(), addr))
log.Log("[route]", buf.String())
}
return
}
buf.WriteString(fmt.Sprintf("%s -> ", node.String()))
if node.Client.Transporter.Multiplex() {
node.DialOptions = append(node.DialOptions,
ChainDialOption(route),
)
route = newRoute() // cutoff the chain for multiplex.
}
route.AddNode(node)
}
route.Retries = c.Retries
if Debug {
buf.WriteString(addr)
log.Log("[route]", buf.String())
}
return
}
// ChainOptions holds options for Chain.
type ChainOptions struct {
Retries int
Timeout time.Duration
Hosts *Hosts
Resolver Resolver
}
// ChainOption allows a common way to set chain options.
type ChainOption func(opts *ChainOptions)
// RetryChainOption specifies the times of retry used by Chain.Dial.
func RetryChainOption(retries int) ChainOption {
return func(opts *ChainOptions) {
opts.Retries = retries
}
}
// TimeoutChainOption specifies the timeout used by Chain.Dial.
func TimeoutChainOption(timeout time.Duration) ChainOption {
return func(opts *ChainOptions) {
opts.Timeout = timeout
}
}
// HostsChainOption specifies the hosts used by Chain.Dial.
func HostsChainOption(hosts *Hosts) ChainOption {
return func(opts *ChainOptions) {
opts.Hosts = hosts
}
}
// ResolverChainOption specifies the Resolver used by Chain.Dial.
func ResolverChainOption(resolver Resolver) ChainOption {
return func(opts *ChainOptions) {
opts.Resolver = resolver
}
}
...@@ -90,7 +90,7 @@ func (tr *tcpTransporter) Multiplex() bool { ...@@ -90,7 +90,7 @@ func (tr *tcpTransporter) Multiplex() bool {
return false return false
} }
// DialOptions describes the options for dialing. // DialOptions describes the options for Transporter.Dial.
type DialOptions struct { type DialOptions struct {
Timeout time.Duration Timeout time.Duration
Chain *Chain Chain *Chain
......
# period for live reloading
reload 10s
# matcher reversed
reverse true
10.0.0.1
192.168.0.0/24
172.1.0.0/16
192.168.100.190/32
*.example.com
.example.org
\ No newline at end of file
package main
import (
"bufio"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"strings"
"time"
"github.com/ginuerzh/gost"
)
var (
defaultCertFile = "cert.pem"
defaultKeyFile = "key.pem"
)
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid.
func tlsConfig(certFile, keyFile string) (*tls.Config, error) {
if certFile == "" {
certFile = defaultCertFile
}
if keyFile == "" {
keyFile = defaultKeyFile
}
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
}
func loadCA(caFile string) (cp *x509.CertPool, err error) {
if caFile == "" {
return
}
cp = x509.NewCertPool()
data, err := ioutil.ReadFile(caFile)
if err != nil {
return nil, err
}
if !cp.AppendCertsFromPEM(data) {
return nil, errors.New("AppendCertsFromPEM failed")
}
return
}
func loadConfigureFile(configureFile string) error {
if configureFile == "" {
return nil
}
content, err := ioutil.ReadFile(configureFile)
if err != nil {
return err
}
var cfg struct {
route
Routes []route
}
if err := json.Unmarshal(content, &cfg); err != nil {
return err
}
if len(cfg.route.ServeNodes) > 0 {
routes = append(routes, cfg.route)
}
for _, route := range cfg.Routes {
if len(route.ServeNodes) > 0 {
routes = append(routes, route)
}
}
gost.Debug = cfg.Debug
return nil
}
type stringList []string
func (l *stringList) String() string {
return fmt.Sprintf("%s", *l)
}
func (l *stringList) Set(value string) error {
*l = append(*l, value)
return nil
}
func parseKCPConfig(configFile string) (*gost.KCPConfig, error) {
if configFile == "" {
return nil, nil
}
file, err := os.Open(configFile)
if err != nil {
return nil, err
}
defer file.Close()
config := &gost.KCPConfig{}
if err = json.NewDecoder(file).Decode(config); err != nil {
return nil, err
}
return config, nil
}
func parseUsers(authFile string) (users []*url.Userinfo, err error) {
if authFile == "" {
return
}
file, err := os.Open(authFile)
if err != nil {
return
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
s := strings.SplitN(line, " ", 2)
if len(s) == 1 {
users = append(users, url.User(strings.TrimSpace(s[0])))
} else if len(s) == 2 {
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1])))
}
}
err = scanner.Err()
return
}
func parseIP(s string, port string) (ips []string) {
if s == "" {
return
}
if port == "" {
port = "8080" // default port
}
file, err := os.Open(s)
if err != nil {
ss := strings.Split(s, ",")
for _, s := range ss {
s = strings.TrimSpace(s)
if s != "" {
if !strings.Contains(s, ":") {
s = s + ":" + port
}
ips = append(ips, s)
}
}
return
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if !strings.Contains(line, ":") {
line = line + ":" + port
}
ips = append(ips, line)
}
return
}
type peerConfig struct {
Strategy string `json:"strategy"`
Filters []string `json:"filters"`
MaxFails int `json:"max_fails"`
FailTimeout int `json:"fail_timeout"`
Nodes []string `json:"nodes"`
Bypass *bypass `json:"bypass"` // global bypass
}
type bypass struct {
Reverse bool `json:"reverse"`
Patterns []string `json:"patterns"`
}
func loadPeerConfig(peer string) (config peerConfig, err error) {
if peer == "" {
return
}
content, err := ioutil.ReadFile(peer)
if err != nil {
return
}
err = json.Unmarshal(content, &config)
return
}
func (cfg *peerConfig) Validate() {
if cfg.MaxFails <= 0 {
cfg.MaxFails = 1
}
if cfg.FailTimeout <= 0 {
cfg.FailTimeout = 30 // seconds
}
}
func parseStrategy(s string) gost.Strategy {
switch s {
case "random":
return &gost.RandomStrategy{}
case "fifo":
return &gost.FIFOStrategy{}
case "round":
fallthrough
default:
return &gost.RoundStrategy{}
}
}
func parseBypass(s string) *gost.Bypass {
if s == "" {
return nil
}
var matchers []gost.Matcher
var reversed bool
if strings.HasPrefix(s, "~") {
reversed = true
s = strings.TrimLeft(s, "~")
}
f, err := os.Open(s)
if err != nil {
for _, s := range strings.Split(s, ",") {
s = strings.TrimSpace(s)
if s == "" {
continue
}
matchers = append(matchers, gost.NewMatcher(s))
}
return gost.NewBypass(reversed, matchers...)
}
f.Close()
bp := gost.NewBypass(reversed)
go gost.PeriodReload(bp, s)
return bp
}
func parseResolver(cfg string) gost.Resolver {
if cfg == "" {
return nil
}
timeout := 30 * time.Second
ttl := 60 * time.Second
var nss []gost.NameServer
f, err := os.Open(cfg)
if err != nil {
for _, s := range strings.Split(cfg, ",") {
s = strings.TrimSpace(s)
if s == "" {
continue
}
ss := strings.Split(s, "/")
if len(ss) == 1 {
nss = append(nss, gost.NameServer{
Addr: ss[0],
})
}
if len(ss) == 2 {
nss = append(nss, gost.NameServer{
Addr: ss[0],
Protocol: ss[1],
})
}
}
return gost.NewResolver(timeout, ttl, nss...)
}
f.Close()
resolver := gost.NewResolver(timeout, ttl)
go gost.PeriodReload(resolver, cfg)
return resolver
}
# resolver timeout, default 30s.
timeout 10s
# resolver cache TTL, default 60s, minus value means that cache is disabled.
ttl 300s
# period for live reloading
reload 10s
# ip[:port] [protocol] [hostname]
1.1.1.1:853 tls cloudflare-dns.com
8.8.8.8
8.8.8.8 tcp
1.1.1.1 udp
1.1.1.1:53 tcp
\ No newline at end of file
# period for live reloading
reload 10s
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This diff is collapsed.
...@@ -6,5 +6,13 @@ ...@@ -6,5 +6,13 @@
"socks5://:1081", "socks5://:1081",
"socks://:1082", "socks://:1082",
"socks4a://:1083" "socks4a://:1083"
] ],
"bypass":{
"reverse": false,
"patterns": [
"10.0.0.1",
"192.168.0.0/24",
"*.example.com"
]
}
} }
\ No newline at end of file
[//]: <> (https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309)
# Create Root CA (Done once)
## Create Root Key
**Attention:** this is the key used to sign the certificate requests, anyone holding this can sign certificates on your behalf. So keep it in a safe place!
```bash
openssl genrsa -des3 -out rootCA.key 4096
```
If you want a non password protected key just remove the `-des3` option
## Create and self sign the Root Certificate
```bash
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt
```
Here we used our root key to create the root certificate that needs to be distributed in all the computers that have to trust us.
# Create a certificate (Done for each server)
This procedure needs to be followed for each server/appliance that needs a trusted certificate from our CA
## Create the certificate key
```
openssl genrsa -out mydomain.com.key 2048
```
## Create the signing request
**Important:** Please mind that while creating the signign request is important to specify the `Common Name` providing the IP address or URL for the service, otherwise the certificate
cannot be verified
```
openssl req -new -key mydomain.com.key -out mydomain.com.csr
```
## Generate the certificate using the `mydomain` csr and key along with the CA Root key
```
openssl x509 -req -in mydomain.com.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out mydomain.com.crt -days 500 -sha256
```
-----BEGIN CERTIFICATE-----
MIIDvjCCAaYCCQC0XjV3wljvnjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTgwNzA4MDQ1MzIyWhcNMTkxMTIwMDQ1MzIyWjAuMQswCQYD
VQQGEwJDTjELMAkGA1UEBwwCU0gxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOKgzWil/KjyRy2Axb3XlLB1nMwLFJC
pC6r8yb+1Kq/ldZghJZvymuFVjn+bihvJqvZiOv4KRtnM8gD55AhaQp6Ese5M9b+
47HLB//SkfJsQREsmnrHfHxjmUQjhMy7jrpcf9OnDOXQ5zk3v6AWEIqMtAiZ99ku
AQvyJJ07+VpwZrMuzbSGfFBCKEbbqP7yKHjSUm3QDTpTiK4AnBmzlVeThUIA68oa
XZKQVXX/8U2i6H4eq5eNpyUsKSnnuK+cryHpAIK4vNMzw96vATTfEmuWASEzkHhW
3KtfXE0CIH0GsK5zueGDo9ygnO7hjtx60SWynlGf6c6edxPwNvEmTZcCAwEAATAN
BgkqhkiG9w0BAQsFAAOCAgEApLkdhnDzErgBljY6qRaR0JlouTpqJXwi5BRi7F1P
bx5ukrZAVSOsZ7ncEkZuxkIX+ktBVFBL8twkvMEl+sMQ24R+F+TrlHWN2xPR/pez
9V19hq26yMIlYLqSq3KZ0W9ZlT2ge+3sTvY+gAJhZ6nOz9WGRJ1mi+pN/ok678QX
KdOJXcePzYr5iKqMq/5cJ2sA1xYwVl+0xrvfRVTFkp4yR6wzGODtjquB+scZ9S64
GWnFTjHAJvUKYxpeoLAt9lZHsESDqGq7hA4z1uVjhNEDJHKnXW4OhXxMB8Gk2hY8
3k4zbnKsouNNW0a7jijCMpXOem/vgQF4GK5ecp0S+Ml/AunsPoi6rGgOCX8XXmti
6DfQhsxxgn1co/JKNxhgsnQftXFwKivh73JFctSh+bMLsewfXsvq+b0K3EuuV9bV
EttVCgbUaCDYdA6IDkqD2PRx9tsotne76r+cX+ah+NjnA6XN+XY2bJgV1UaiKTrP
moNHglw+xoUqOJ7FlGJcVC7uIFPhMviNkpSZh6WxX+OSS4fPO25kxxNpldql6I+3
xb5XEHLpPCEI4PyK0rYnsjk764Loqff8YBMFRQSXIUz9ot5SgGs/FY1vsQap5OeD
Hw2usWhCvkSzr7kiXI+30BvJKK2r9GOAM7mtO9dfkM9MMKKnMzd+O2XE4r6PNLrg
Rds=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE REQUEST-----
MIICczCCAVsCAQAwLjELMAkGA1UEBhMCQ04xCzAJBgNVBAcMAlNIMRIwEAYDVQQD
DAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDioM1
opfyo8kctgMW915SwdZzMCxSQqQuq/Mm/tSqv5XWYISWb8prhVY5/m4obyar2Yjr
+CkbZzPIA+eQIWkKehLHuTPW/uOxywf/0pHybEERLJp6x3x8Y5lEI4TMu466XH/T
pwzl0Oc5N7+gFhCKjLQImffZLgEL8iSdO/lacGazLs20hnxQQihG26j+8ih40lJt
0A06U4iuAJwZs5VXk4VCAOvKGl2SkFV1//FNouh+HquXjaclLCkp57ivnK8h6QCC
uLzTM8PerwE03xJrlgEhM5B4VtyrX1xNAiB9BrCuc7nhg6PcoJzu4Y7cetElsp5R
n+nOnncT8DbxJk2XAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAr+AkYRAPulBU
B5HR3pAreYrf3Y2fvGLSNo4hvsJkmXJxDgMZnGsjVzW1IZLF8szn4v050y6Qm/Ne
qupabYP5zpj0vKkACYGJ2zadnowmwlTzwlxEOv27uQykC/IuRcjdloAD7ZwhNwmO
dLNjdiXn63GUeSL/JK0UHyXTqvpmiHq+6TAOdl3vmsRFCQDChRtViK2fwSeX2y87
hLicSVQyNOe0gUx7IvE9B2QPNhdzaMVPYeN8I/cayNeUKhiWxEGKhwPAaievuSXJ
fUsz11XYBYW+kjFsTqkV1OjkG0mxvwaiq5W3CRx8365w71IMdKV5t5xhc0n0TXp7
cT27XN7cdw==
-----END CERTIFICATE REQUEST-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw4qDNaKX8qPJHLYDFvdeUsHWczAsUkKkLqvzJv7Uqr+V1mCE
lm/Ka4VWOf5uKG8mq9mI6/gpG2czyAPnkCFpCnoSx7kz1v7jscsH/9KR8mxBESya
esd8fGOZRCOEzLuOulx/06cM5dDnOTe/oBYQioy0CJn32S4BC/IknTv5WnBmsy7N
tIZ8UEIoRtuo/vIoeNJSbdANOlOIrgCcGbOVV5OFQgDryhpdkpBVdf/xTaLofh6r
l42nJSwpKee4r5yvIekAgri80zPD3q8BNN8Sa5YBITOQeFbcq19cTQIgfQawrnO5
4YOj3KCc7uGO3HrRJbKeUZ/pzp53E/A28SZNlwIDAQABAoIBAGx1pMeYMw3L2R5K
urX/aVsf1xI3My5Bdo3IpGsJx+4ZrEOnb4N96FnxMF2kiXd2B44kb/TqxepEOQ2F
VOi2D2xXP5l2WZGz+ZnBUuOL6ZX8g67B/cGCasMX/4gy51Mj6UvnSKOeMeI7GDW9
fVWPR4eB+c4XkMju4ne8zKBGBs4pN4KoxTWSnZSM4p+q/Jb/DMa+kVhFfRjkqfWc
vCpDgHs1uMcHvPBNYO9flDaC2Jgk4cvV9mX0TolXAvaNo8aN0joM7WH3fvw7NCD9
LCkqCmpjOxJIqJQT1twIkSy42q7VaFi7ApyIaMfXlmnj4UTlVTe5bBO+2AgwLYtC
cKgDMjECgYEA8JPm3Pc80EsYB6d4qp/Qmy2VrnlaxZwvaRwh63Pssqthg4SZkIp5
yjXOT4MDlJdrEzMtATRZUXTCRxGFSs0tolNY2KQ2WvYRhISlN8UBkGuMEkRGLuct
p++qpPcSZJcox25kT82CKin1nQYb48k33JAOMUOWIBJO56G35sfPj28CgYEA0BOE
pa+FYj/WxZS79YT1ZbsajeuUKlNUtAIxKJ2cKSQyfFuPM+xZG3H+iroRfR8HCVai
2+Oz9/TlxZOPR7+P+2fpS8W2tT+Qkmiyz8QJAULd+Irw5XIdatkpVm343XxMx3Pa
2qtBmgj6RINvsWTWRotMqhcuDRirxqm1IIQhkFkCgYBLNmIhyOXpVODRW8k8xrQI
H6tBHc2EJD0qRlJQczCX9z6ISIdeCfzjfAjhENuos+IU4ZX7X2thLPikEVUzuou+
yQHo0QXxUCbP4Exq8Bt6FDV5bIDonvvGGgamhlvouN1V5CxWSrCcD/wquEM15q2h
NiRJwJCJvE+Q2R1OeD9q3wKBgFWDkAJf7luAjQ3KoKy4pfnXOYSWCuCSOr94Hyfo
DmPCIpWFM4dNXRmwccIl0kYv2D54QppILJB9L2lRyZLdIZlbDUA802gN5aamLMbC
dEj2aC9bOsGxcnGVKi4BKEQub4eRD6LKuz1I70H1GpQ3MvDvEuTcfeqX9xDAclYY
t4qRAoGBAI6YSTs97DUe7Zwk7q+S3PBU5uct4Dwtmy2XWZdgHwl5aP8apSLciL5Y
PMkpcTMzkuC+QFaPZ8wFcI7GLg0bOs91hkrqscDKEg4nGB9fJkU82iOQZNL4Hv1u
wO7uIGa2kcpNtQOLNO88y45WFyrn5a+T6VhDmIuc+F+TU1ZzdYdH
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIEpDCCAowCCQDwV08QFUCcSzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTgwNzA4MDQ1MzE3WhcNMjEwNDI3MDQ1MzE3WjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDE
+71yX9vQY3l52C31e04ACvm2oNMLSCrLOXGewOTFpv9yXjinMC0Ab6Xa4yB4MPtd
ujWDSEq9gkKCkILoalVX7R4gtLDN+EdoVadBw/WHbrGB4sHnFOWwpUbjiiwwPSU5
qXjcqIqR2sA1BgoAv6c1qHq7V4bgbEjtGL71KkDoQZSIgNyJlXe1mcUKqmqeAnro
0WfwrNXyYt66L3PmCy9MIpWRxf9sa8PDsgT3SQEN4BHb70Z8hNj6RXfVGDZcpfcI
iwbrL//YnK7waRKaKD4LoLOodE0cx3fowSWYvlwUAoYx8wFKOcdtM1zaUp+QDAug
xT5g5ghU150XuqhDU6Wq1An8dLgcDU1D4cxhLk/W8OXtIk4k7yny6eUJi2zHziMm
8jfHd3M6SwohUrE3LsQI5gpvu4sAVFLMkRxaWZ95XhsVMmIsE/L5FHwDfqid0dvx
bafOKT+fI3N3BaUPJlVHCNqSzSZIW59+ufnDwBV7SmJj4KMlvixEU+EFfPFdGiCA
Lr0dSG5+Scx1aClaMUeVccCljp2f99IEa9wI+xwMPDStkOmnhVuqG1aEogggQZkD
/5yh04wrn8EwYCAiasNNUXTV7AoqIt2bgeFbGo2Qr7LdsYuUmaWEzTm0KsHogkkg
Ibd3RPBLDr/WfWI13oHMdsz8jjbXG/D1AhrcdozYDQIDAQABMA0GCSqGSIb3DQEB
CwUAA4ICAQC1EeQqn2AwrS+UVc5fKRpHzV9ZMiDpxFMRLsDWBP5kNr1nSA72yYcR
WgxvdqG/rGRds6lvRbvIaWD0zeujPkR3iCpb1V5oRXQ6lWOlY44pZEwCdnDd2M8I
yQ7BLZCHHmlCN7a51n2o0D78HeILIeeTCQlKFDc5r51qrZbZR5DZmrp9jaZ+3eCg
LQ3Onfj0WEmQFuMFGQrbJ2oaCC1GvuZWEbRh+lrxjRKOyCaRQFTY4Efe8tIwMm6J
1iyMtqK7BxminQCfizQrstB67wMljydYeUf+wwbgkiKGYc9VGopckrO3lntzKycu
9l0BmlZYkmCFt3cv23BcqAbcLdyLXh3yASwVMXaLZ4iVSaslRm4uX+gbKFCBABLa
vqu7JQHfAPOeYj7zCrN12EHejPxdCjSImBeAbe56vax4uFGAodXxDGcepRItSzax
qPKJd8U/8e3JDn+wmZNKwD9UGLZPbiuYOg7X+EWhjki0J6ZjgLc8dMleeD2rO+j2
P/Wgv1gMr6J1svUlqkNf1Ng9eSbl/nMhuOBVOGcPnK7+wCLxM7ByaR0QgeH6/9VO
4urq53/vspBC679BHsZx3gIhcg4VefmOM2cZnTRM4izPstq1JBQkbuvz+5XuT7Yj
5Fk1/xkapCUifntKYSoslkkbNHRYxAInqkc0txn3qNBI8GAQFksz5g==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAxPu9cl/b0GN5edgt9XtOAAr5tqDTC0gqyzlxnsDkxab/cl44
pzAtAG+l2uMgeDD7Xbo1g0hKvYJCgpCC6GpVV+0eILSwzfhHaFWnQcP1h26xgeLB
5xTlsKVG44osMD0lOal43KiKkdrANQYKAL+nNah6u1eG4GxI7Ri+9SpA6EGUiIDc
iZV3tZnFCqpqngJ66NFn8KzV8mLeui9z5gsvTCKVkcX/bGvDw7IE90kBDeAR2+9G
fITY+kV31Rg2XKX3CIsG6y//2Jyu8GkSmig+C6CzqHRNHMd36MElmL5cFAKGMfMB
SjnHbTNc2lKfkAwLoMU+YOYIVNedF7qoQ1OlqtQJ/HS4HA1NQ+HMYS5P1vDl7SJO
JO8p8unlCYtsx84jJvI3x3dzOksKIVKxNy7ECOYKb7uLAFRSzJEcWlmfeV4bFTJi
LBPy+RR8A36ondHb8W2nzik/nyNzdwWlDyZVRwjaks0mSFuffrn5w8AVe0piY+Cj
Jb4sRFPhBXzxXRoggC69HUhufknMdWgpWjFHlXHApY6dn/fSBGvcCPscDDw0rZDp
p4VbqhtWhKIIIEGZA/+codOMK5/BMGAgImrDTVF01ewKKiLdm4HhWxqNkK+y3bGL
lJmlhM05tCrB6IJJICG3d0TwSw6/1n1iNd6BzHbM/I421xvw9QIa3HaM2A0CAwEA
AQKCAgBXbeSH/0PxGjWwfuLnMfNM0ZJEHN2PBFj6GmTzsWnY0GZQvMEoc5mFuAhF
PsoKjrMCxsM5obyKoGYkzT9NKOT4QaY9nfVbdfc7t8ikx/USR29B1wN5LS1FWhY8
p/c08e6zySR7y9K1KgJlhmiqLGZqynyu6gpTUbyMf49CAZ8Ndw4WCBvadRzM3ZM3
SKxJtZAYBdm8WPocuwVgXe9zC0PS5wa7zMWxuaMKGNlbaGuvXOSQWYNPgSdM7chi
LHz0YjVi9VH80TEdU23SBtDa20Gup4UWH4iaXW47QH8PbG4x82zcfp7z8vEw5rsv
q7xmkvIWSXWGTJMmFQ0EmzRTray5fj38Oo2ZtwHvkJQ1WkiFNFiWFZXnoS3z6h5q
1lX6ZUhCoobUJRRlDYCwNDV6dMYKXK2NNeD1MPvzUoUIpoQnoxnNF+VYMMENax3e
YuEiT6xbBXzB/WE0bFVAtSPzf1vPVw+8MP5BhaH3lQb6XA89FiEZg+u95rNpf2SA
gFWvz0VZsGab+LwYhbYdicmKPRH+2Pzpt5MdWt8jyo066Lv0NP5xpH9IUv/u2RX7
Ycw0Bu1HWKLoEzovoH6OEa0n1A7H+PNOhABzrLvbU8GMp4kEQpXACxR43KruxE7S
QgotUAb7teCP54yEHTVAe06YIaq4JPk5xqnmMVvaeuy5rvssAQKCAQEA5d4NUONV
/An+bAf31HicZfRH6Pf1N3JUdjYz2l1y60Pf7dzlDI2fjOWlccp24+efXLM1sMeK
GXQZsAnYJevZktQxodM67CsgEFgdGhH2s5Ey3Dp5bt3uS/SaFv4sT1x9awYdYtKp
6fGovjB1Qp/eMuZNJVl9RwegFVzzrrSMxucNzUuL4v4L911ypR4wz4s1ptqI31/U
56B1VRKjwZntqJaNO2Plt/yY0s+ganhzdKBynoOKzTpYHCqZhHdqvqfc1qC0W1xI
E/b3Nf0J+GqjjT7JDbWgqNty5ipDfCdIeems96U1Gu9oeKGDzvCgtImZNFMyHzLM
MhO0v6GA6zkuVQKCAQEA22Cls2AAUuugi2tdZR/krHokrUMPaKvi9RIQpqHpoKqL
E3rKX9aWyIMhktch7VsnDF52R8CMUhgc7PfL4wsWA5cCq26x4E57aJUH5mxc5va5
n5Sxb3C99Ytr6GpCkG4Y3pzO93ihgfuW+mQREYLpFYd/c/1SH/e4Wx5nGKx6YocY
6b/AbxWcRMlihC2gK7JFgSZMqaL+wn68oKJ7j8RUN+ykZEzBXBWL2l4oKWm+qBDx
pOFSQODeQ0CQhPWovD4dVNmyrh5TcDUlJQ3+iU2hRkXe13mLTkGpSM53kwkrfVn+
4SmVLEm5YhNcHG14A1yDqs6SY//8l5xfUeZyrPBK2QKCAQEAqIsRLm8SG9RkFWge
Qk8RNfxQQbSVu0r8PRTvHjyIx5Ij/e+KjpLFGvVDQtUWKXMquTi5tF4KlzE2qIn/
T4bIKE2n+qS7vnC8eN9yryvevLlJFotVgIH/ePfnh9ZkPOhvGWsJXu1iIqPLe3Bi
ejBoJuAQTsN4BP3FVgSqtD20Px8pUo8DCbQGqCB/sCwb1AGZnDb+RvKoVBGmFnOt
WIX56TRCZ/qOdEIk9+W/FHIvDaObhziiLGqMMlLV73fz78l7Nm/s7lQSkXjyuEZJ
6jiepTEVEBVNsKH/dF4mz0CqdqFs7sPW1WIXMuQSlkh/PQDrMZ+Sz6daa5lhXWUY
9uAdZQKCAQBrDzRuYIhn7yPPRlsy0ai4X3dsstBfRZsh/Gnx2Ax64x+yJveCY+f7
/LqyvZiKDDT3PVY92ALiwW/EWX2/1JYutFCSNxhJniNtu2U6l2GTOY8HCPq6puud
XCgSKWFIuOIcKax7avxuwchBc/o8cIWtgw25HkQo46ytkx2/FdU4JjQLRw/zZjl3
/Eu+s8F58asnxvgcxTXM1yrYvdLNK4PqMutbI3YtqToyHEc/RqLLxFEZJPkOPm9Z
pLWinXx2OV35HbCsdpJDrTvuZHD2stLkx45j26YXT8X8iP4j3JLDvtq7KZ7qGSSG
b2pBWU77XPfIsL0SXkf3+VEvV+ZY7X+pAoIBABV6Mu5Yr4UxI+ZgsaKgcK8aKoyD
5GDshxkxs8R3K1i7eCF0mEjxkV25r11KX10qFvG+hPJqizKQDBOCQC2w3noqz42p
QVUeBNXpDVGoImD1/4DqUvQMivTwHWS+wSAi/wYAODJ6/bWP5Kil/7iDOwCPp0WD
mLd0ujjwkOw3Xksn2Gd01pXeiT4FZpkYnyh5ddWGf1TihRFATW5+vpi6t+6KX3LR
hwd9zi6soSwju/n986NUfGfeewBb6/fnh6hM/vfS2a0Blvk/7yM1k2P0uN+TzLYf
skhRay10UoMwtXak+q/DBzrrAbW3EwuIdV66H4dx1AV5NMq6kAAtfDXc728=
-----END RSA PRIVATE KEY-----
This diff is collapsed.
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
) )
// Version is the gost version. // Version is the gost version.
const Version = "2.5" const Version = "2.6-rc1"
// Debug is a flag that enables the debug log. // Debug is a flag that enables the debug log.
var Debug bool var Debug bool
...@@ -44,19 +44,19 @@ var ( ...@@ -44,19 +44,19 @@ var (
) )
var ( var (
// DefaultTLSConfig is a default TLS config for internal use // DefaultTLSConfig is a default TLS config for internal use.
DefaultTLSConfig *tls.Config DefaultTLSConfig *tls.Config
// DefaultUserAgent is the default HTTP User-Agent header used by HTTP and websocket // DefaultUserAgent is the default HTTP User-Agent header used by HTTP and websocket.
DefaultUserAgent = "Chrome/60.0.3112.90" DefaultUserAgent = "Chrome/60.0.3112.90"
) )
// SetLogger sets a new logger for internal log system // SetLogger sets a new logger for internal log system.
func SetLogger(logger log.Logger) { func SetLogger(logger log.Logger) {
log.DefaultLogger = logger log.DefaultLogger = logger
} }
// GenCertificate generates a random TLS certificate // GenCertificate generates a random TLS certificate.
func GenCertificate() (cert tls.Certificate, err error) { func GenCertificate() (cert tls.Certificate, err error) {
rawCert, rawKey, err := generateKeyPair() rawCert, rawKey, err := generateKeyPair()
if err != nil { if err != nil {
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"net" "net"
"net/url" "net/url"
"time"
"github.com/ginuerzh/gosocks4" "github.com/ginuerzh/gosocks4"
"github.com/ginuerzh/gosocks5" "github.com/ginuerzh/gosocks5"
...@@ -13,6 +14,7 @@ import ( ...@@ -13,6 +14,7 @@ import (
// Handler is a proxy server handler // Handler is a proxy server handler
type Handler interface { type Handler interface {
Init(options ...HandlerOption)
Handle(net.Conn) Handle(net.Conn)
} }
...@@ -24,6 +26,12 @@ type HandlerOptions struct { ...@@ -24,6 +26,12 @@ type HandlerOptions struct {
TLSConfig *tls.Config TLSConfig *tls.Config
Whitelist *Permissions Whitelist *Permissions
Blacklist *Permissions Blacklist *Permissions
Strategy Strategy
Bypass *Bypass
Retries int
Timeout time.Duration
Resolver Resolver
Hosts *Hosts
} }
// HandlerOption allows a common way to set handler options. // HandlerOption allows a common way to set handler options.
...@@ -71,18 +79,68 @@ func BlacklistHandlerOption(blacklist *Permissions) HandlerOption { ...@@ -71,18 +79,68 @@ func BlacklistHandlerOption(blacklist *Permissions) HandlerOption {
} }
} }
// BypassHandlerOption sets the bypass option of HandlerOptions.
func BypassHandlerOption(bypass *Bypass) HandlerOption {
return func(opts *HandlerOptions) {
opts.Bypass = bypass
}
}
// StrategyHandlerOption sets the strategy option of HandlerOptions.
func StrategyHandlerOption(strategy Strategy) HandlerOption {
return func(opts *HandlerOptions) {
opts.Strategy = strategy
}
}
// RetryHandlerOption sets the retry option of HandlerOptions.
func RetryHandlerOption(retries int) HandlerOption {
return func(opts *HandlerOptions) {
opts.Retries = retries
}
}
// TimeoutHandlerOption sets the timeout option of HandlerOptions.
func TimeoutHandlerOption(timeout time.Duration) HandlerOption {
return func(opts *HandlerOptions) {
opts.Timeout = timeout
}
}
// ResolverHandlerOption sets the resolver option of HandlerOptions.
func ResolverHandlerOption(resolver Resolver) HandlerOption {
return func(opts *HandlerOptions) {
opts.Resolver = resolver
}
}
// HostsHandlerOption sets the Hosts option of HandlerOptions.
func HostsHandlerOption(hosts *Hosts) HandlerOption {
return func(opts *HandlerOptions) {
opts.Hosts = hosts
}
}
type autoHandler struct { type autoHandler struct {
options []HandlerOption options *HandlerOptions
} }
// AutoHandler creates a server Handler for auto proxy server. // AutoHandler creates a server Handler for auto proxy server.
func AutoHandler(opts ...HandlerOption) Handler { func AutoHandler(opts ...HandlerOption) Handler {
h := &autoHandler{ h := &autoHandler{}
options: opts, h.Init(opts...)
}
return h return h
} }
func (h *autoHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
}
for _, opt := range options {
opt(h.options)
}
}
func (h *autoHandler) Handle(conn net.Conn) { func (h *autoHandler) Handle(conn net.Conn) {
br := bufio.NewReader(conn) br := bufio.NewReader(conn)
b, err := br.Peek(1) b, err := br.Peek(1)
...@@ -93,25 +151,23 @@ func (h *autoHandler) Handle(conn net.Conn) { ...@@ -93,25 +151,23 @@ func (h *autoHandler) Handle(conn net.Conn) {
} }
cc := &bufferdConn{Conn: conn, br: br} cc := &bufferdConn{Conn: conn, br: br}
var handler Handler
switch b[0] { switch b[0] {
case gosocks4.Ver4: case gosocks4.Ver4:
options := &HandlerOptions{}
for _, opt := range h.options {
opt(options)
}
// SOCKS4(a) does not suppport authentication method, // SOCKS4(a) does not suppport authentication method,
// so we ignore it when credentials are specified for security reason. // so we ignore it when credentials are specified for security reason.
if len(options.Users) > 0 { if len(h.options.Users) > 0 {
cc.Close() cc.Close()
return return
} }
h := &socks4Handler{options} handler = &socks4Handler{options: h.options}
h.Handle(cc) case gosocks5.Ver5: // socks5
case gosocks5.Ver5: handler = &socks5Handler{options: h.options}
SOCKS5Handler(h.options...).Handle(cc)
default: // http default: // http
HTTPHandler(h.options...).Handle(cc) handler = &httpHandler{options: h.options}
} }
handler.Init()
handler.Handle(cc)
} }
type bufferdConn struct { type bufferdConn struct {
......
package gost
import (
"bufio"
"io"
"net"
"strings"
"time"
"github.com/go-log/log"
)
// Host is a static mapping from hostname to IP.
type Host struct {
IP net.IP
Hostname string
Aliases []string
}
// Hosts is a static table lookup for hostnames.
// For each host a single line should be present with the following information:
// IP_address canonical_hostname [aliases...]
// Fields of the entry are separated by any number of blanks and/or tab characters.
// Text from a "#" character until the end of the line is a comment, and is ignored.
type Hosts struct {
hosts []Host
period time.Duration
}
// NewHosts creates a Hosts with optional list of host
func NewHosts(hosts ...Host) *Hosts {
return &Hosts{
hosts: hosts,
}
}
// AddHost adds host(s) to the host table.
func (h *Hosts) AddHost(host ...Host) {
h.hosts = append(h.hosts, host...)
}
// Lookup searches the IP address corresponds to the given host from the host table.
func (h *Hosts) Lookup(host string) (ip net.IP) {
if h == nil {
return
}
for _, h := range h.hosts {
if h.Hostname == host {
ip = h.IP
break
}
for _, alias := range h.Aliases {
if alias == host {
ip = h.IP
break
}
}
}
if ip != nil && Debug {
log.Logf("[hosts] hit: %s %s", host, ip.String())
}
return
}
// Reload parses config from r, then live reloads the hosts.
func (h *Hosts) Reload(r io.Reader) error {
var hosts []Host
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
if n := strings.IndexByte(line, '#'); n >= 0 {
line = line[:n]
}
line = strings.Replace(line, "\t", " ", -1)
line = strings.TrimSpace(line)
if line == "" {
continue
}
var ss []string
for _, s := range strings.Split(line, " ") {
if s = strings.TrimSpace(s); s != "" {
ss = append(ss, s)
}
}
if len(ss) < 2 {
continue // invalid lines are ignored
}
// reload option
if strings.ToLower(ss[0]) == "reload" {
h.period, _ = time.ParseDuration(ss[1])
continue
}
ip := net.ParseIP(ss[0])
if ip == nil {
continue // invalid IP addresses are ignored
}
host := Host{
IP: ip,
Hostname: ss[1],
}
if len(ss) > 2 {
host.Aliases = ss[2:]
}
hosts = append(hosts, host)
}
if err := scanner.Err(); err != nil {
return err
}
h.hosts = hosts
return nil
}
// Period returns the reload period
func (h *Hosts) Period() time.Duration {
return h.period
}
...@@ -75,13 +75,18 @@ type httpHandler struct { ...@@ -75,13 +75,18 @@ type httpHandler struct {
// HTTPHandler creates a server Handler for HTTP proxy server. // HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler { func HTTPHandler(opts ...HandlerOption) Handler {
h := &httpHandler{ h := &httpHandler{}
options: &HandlerOptions{}, h.Init(opts...)
return h
}
func (h *httpHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
} }
for _, opt := range opts { for _, opt := range options {
opt(h.options) opt(h.options)
} }
return h
} }
func (h *httpHandler) Handle(conn net.Conn) { func (h *httpHandler) Handle(conn net.Conn) {
...@@ -116,6 +121,13 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { ...@@ -116,6 +121,13 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
return return
} }
// try to get the actual host.
if v := req.Header.Get("Gost-Target"); v != "" {
if host, err := decodeServerName(v); err == nil {
req.Host = host
}
}
if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) { if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[http] Unauthorized to tcp connect to %s", req.Host) log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
b := []byte("HTTP/1.1 403 Forbidden\r\n" + b := []byte("HTTP/1.1 403 Forbidden\r\n" +
...@@ -127,6 +139,17 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { ...@@ -127,6 +139,17 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
return return
} }
if h.options.Bypass.Contains(req.Host) {
log.Logf("[http] [bypass] %s", req.Host)
b := []byte("HTTP/1.1 403 Forbidden\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
conn.Write(b)
if Debug {
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
}
return
}
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
if Debug && (u != "" || p != "") { if Debug && (u != "" || p != "") {
log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p) log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
...@@ -143,26 +166,50 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { ...@@ -143,26 +166,50 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
req.Header.Del("Proxy-Authorization") req.Header.Del("Proxy-Authorization")
// req.Header.Del("Proxy-Connection") // req.Header.Del("Proxy-Connection")
// try to get the actual host. host := req.Host
if v := req.Header.Get("Gost-Target"); v != "" { if _, port, _ := net.SplitHostPort(host); port == "" {
if host, err := decodeServerName(v); err == nil { host = net.JoinHostPort(req.Host, "80")
req.Host = host
}
} }
// forward http request retries := 1
lastNode := h.options.Chain.LastNode() if h.options.Chain != nil && h.options.Chain.Retries > 0 {
if req.Method != http.MethodConnect && lastNode.Protocol == "http" { retries = h.options.Chain.Retries
h.forwardRequest(conn, req) }
return if h.options.Retries > 0 {
retries = h.options.Retries
} }
host := req.Host var err error
if !strings.Contains(host, ":") { var cc net.Conn
host += ":80" var route *Chain
for i := 0; i < retries; i++ {
route, err = h.options.Chain.selectRouteFor(req.Host)
if err != nil {
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
continue
}
// forward http request
lastNode := route.LastNode()
if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
err = h.forwardRequest(conn, req, route)
if err == nil {
return
}
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
continue
}
cc, err = route.Dial(host,
RetryChainOption(1),
TimeoutChainOption(h.options.Timeout),
HostsChainOption(h.options.Hosts),
ResolverChainOption(h.options.Resolver),
)
if err == nil {
break
}
} }
cc, err := h.options.Chain.Dial(host)
if err != nil { if err != nil {
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err) log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
...@@ -192,28 +239,27 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { ...@@ -192,28 +239,27 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
} }
} }
log.Logf("[http] %s <-> %s", cc.LocalAddr(), host) var su string
if u != "" {
su = u + "@"
}
log.Logf("[http] %s%s <-> %s", su, cc.LocalAddr(), host)
transport(conn, cc) transport(conn, cc)
log.Logf("[http] %s >-< %s", cc.LocalAddr(), host) log.Logf("[http] %s%s >-< %s", su, cc.LocalAddr(), host)
} }
func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) { func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Chain) error {
if h.options.Chain.IsEmpty() { if route.IsEmpty() {
return return nil
} }
lastNode := h.options.Chain.LastNode() lastNode := route.LastNode()
cc, err := h.options.Chain.Conn() cc, err := route.Conn(
RetryChainOption(1), // we control the retry manually.
)
if err != nil { if err != nil {
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err) return err
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
if Debug {
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), lastNode.Addr, string(b))
}
conn.Write(b)
return
} }
defer cc.Close() defer cc.Close()
...@@ -232,14 +278,14 @@ func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) { ...@@ -232,14 +278,14 @@ func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
} }
if err = req.WriteProxy(cc); err != nil { if err = req.WriteProxy(cc); err != nil {
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
return return nil
} }
cc.SetWriteDeadline(time.Time{}) cc.SetWriteDeadline(time.Time{})
log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host) log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
transport(conn, cc) transport(conn, cc)
log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host) log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
return return nil
} }
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) { func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
......
...@@ -114,7 +114,7 @@ func (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn, ...@@ -114,7 +114,7 @@ func (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return wrapTLSClient(conn, cfg) return wrapTLSClient(conn, cfg, opts.Timeout)
}, },
} }
client = &http.Client{ client = &http.Client{
...@@ -182,7 +182,7 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err ...@@ -182,7 +182,7 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err
if tr.tlsConfig == nil { if tr.tlsConfig == nil {
return conn, nil return conn, nil
} }
return wrapTLSClient(conn, cfg) return wrapTLSClient(conn, cfg, opts.Timeout)
}, },
} }
client = &http.Client{ client = &http.Client{
...@@ -250,14 +250,19 @@ type http2Handler struct { ...@@ -250,14 +250,19 @@ type http2Handler struct {
// HTTP2Handler creates a server Handler for HTTP2 proxy server. // HTTP2Handler creates a server Handler for HTTP2 proxy server.
func HTTP2Handler(opts ...HandlerOption) Handler { func HTTP2Handler(opts ...HandlerOption) Handler {
h := &http2Handler{ h := &http2Handler{}
options: new(HandlerOptions), h.Init(opts...)
return h
}
func (h *http2Handler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
} }
for _, opt := range opts { for _, opt := range options {
opt(h.options) opt(h.options)
} }
return h
} }
func (h *http2Handler) Handle(conn net.Conn) { func (h *http2Handler) Handle(conn net.Conn) {
...@@ -296,6 +301,12 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) { ...@@ -296,6 +301,12 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
return return
} }
if h.options.Bypass.Contains(target) {
log.Logf("[http2] [bypass] %s", target)
w.WriteHeader(http.StatusForbidden)
return
}
u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization")) u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization"))
if Debug && (u != "" || p != "") { if Debug && (u != "" || p != "") {
log.Logf("[http] %s - %s : Authorization: '%s' '%s'", r.RemoteAddr, target, u, p) log.Logf("[http] %s - %s : Authorization: '%s' '%s'", r.RemoteAddr, target, u, p)
...@@ -310,7 +321,12 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) { ...@@ -310,7 +321,12 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
r.Header.Del("Proxy-Authorization") r.Header.Del("Proxy-Authorization")
r.Header.Del("Proxy-Connection") r.Header.Del("Proxy-Connection")
cc, err := h.options.Chain.Dial(target) cc, err := h.options.Chain.Dial(target,
RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout),
HostsChainOption(h.options.Hosts),
ResolverChainOption(h.options.Resolver),
)
if err != nil { if err != nil {
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err) log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
w.WriteHeader(http.StatusServiceUnavailable) w.WriteHeader(http.StatusServiceUnavailable)
......
...@@ -25,6 +25,7 @@ type Node struct { ...@@ -25,6 +25,7 @@ type Node struct {
group *NodeGroup group *NodeGroup
failCount uint32 failCount uint32
failTime int64 failTime int64
Bypass *Bypass
} }
// ParseNode parses the node info. // ParseNode parses the node info.
...@@ -139,6 +140,7 @@ func (node *Node) Clone() Node { ...@@ -139,6 +140,7 @@ func (node *Node) Clone() Node {
group: node.group, group: node.group,
failCount: atomic.LoadUint32(&node.failCount), failCount: atomic.LoadUint32(&node.failCount),
failTime: atomic.LoadInt64(&node.failTime), failTime: atomic.LoadInt64(&node.failTime),
Bypass: node.Bypass,
} }
} }
...@@ -186,6 +188,15 @@ func (group *NodeGroup) AddNode(node ...Node) { ...@@ -186,6 +188,15 @@ func (group *NodeGroup) AddNode(node ...Node) {
group.nodes = append(group.nodes, node...) group.nodes = append(group.nodes, node...)
} }
// SetSelector sets node selector with options for the group.
func (group *NodeGroup) SetSelector(selector NodeSelector, opts ...SelectOption) {
if group == nil {
return
}
group.Selector = selector
group.Options = opts
}
// Nodes returns node list in the group // Nodes returns node list in the group
func (group *NodeGroup) Nodes() []Node { func (group *NodeGroup) Nodes() []Node {
if group == nil { if group == nil {
......
...@@ -35,7 +35,7 @@ func TestParseNode(t *testing.T) { ...@@ -35,7 +35,7 @@ func TestParseNode(t *testing.T) {
actual, err := ParseNode(test.in) actual, err := ParseNode(test.in)
if err != nil { if err != nil {
if test.hasError { if test.hasError {
t.Logf("ParseNode(%q) got expected error: %v", test.in, err) // t.Logf("ParseNode(%q) got expected error: %v", test.in, err)
continue continue
} }
t.Errorf("ParseNode(%q) got error: %v", test.in, err) t.Errorf("ParseNode(%q) got error: %v", test.in, err)
......
...@@ -133,7 +133,7 @@ func (tr *quicTransporter) initSession(addr string, conn net.Conn, config *QUICC ...@@ -133,7 +133,7 @@ func (tr *quicTransporter) initSession(addr string, conn net.Conn, config *QUICC
} }
session, err := quic.Dial(udpConn, udpAddr, addr, config.TLSConfig, quicConfig) session, err := quic.Dial(udpConn, udpAddr, addr, config.TLSConfig, quicConfig)
if err != nil { if err != nil {
log.Log("quic dial", err) log.Log("quic dial:", err)
return nil, err return nil, err
} }
return &quicSession{conn: conn, session: session}, nil return &quicSession{conn: conn, session: session}, nil
......
...@@ -17,15 +17,20 @@ type tcpRedirectHandler struct { ...@@ -17,15 +17,20 @@ type tcpRedirectHandler struct {
// TCPRedirectHandler creates a server Handler for TCP redirect server. // TCPRedirectHandler creates a server Handler for TCP redirect server.
func TCPRedirectHandler(opts ...HandlerOption) Handler { func TCPRedirectHandler(opts ...HandlerOption) Handler {
h := &tcpRedirectHandler{ h := &tcpRedirectHandler{}
options: &HandlerOptions{ h.Init(opts...)
Chain: new(Chain),
}, return h
}
func (h *tcpRedirectHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
} }
for _, opt := range opts {
for _, opt := range options {
opt(h.options) opt(h.options)
} }
return h
} }
func (h *tcpRedirectHandler) Handle(c net.Conn) { func (h *tcpRedirectHandler) Handle(c net.Conn) {
...@@ -44,7 +49,10 @@ func (h *tcpRedirectHandler) Handle(c net.Conn) { ...@@ -44,7 +49,10 @@ func (h *tcpRedirectHandler) Handle(c net.Conn) {
log.Logf("[red-tcp] %s -> %s", srcAddr, dstAddr) log.Logf("[red-tcp] %s -> %s", srcAddr, dstAddr)
cc, err := h.options.Chain.Dial(dstAddr.String()) cc, err := h.options.Chain.Dial(dstAddr.String(),
RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout),
)
if err != nil { if err != nil {
log.Logf("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err) log.Logf("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err)
return return
......
...@@ -25,6 +25,10 @@ func TCPRedirectHandler(opts ...HandlerOption) Handler { ...@@ -25,6 +25,10 @@ func TCPRedirectHandler(opts ...HandlerOption) Handler {
return h return h
} }
func (h *tcpRedirectHandler) Init(options ...HandlerOption) {
log.Log("[red-tcp] TCP redirect is not available on the Windows platform")
}
func (h *tcpRedirectHandler) Handle(c net.Conn) { func (h *tcpRedirectHandler) Handle(c net.Conn) {
log.Log("[red-tcp] TCP redirect is not available on the Windows platform") log.Log("[red-tcp] TCP redirect is not available on the Windows platform")
c.Close() c.Close()
......
package gost
import (
"io"
"os"
"time"
"github.com/go-log/log"
)
// Reloader is the interface for objects that support live reloading.
type Reloader interface {
Reload(r io.Reader) error
Period() time.Duration
}
// PeriodReload reloads the config periodically according to the period of the reloader.
func PeriodReload(r Reloader, configFile string) error {
var lastMod time.Time
for {
f, err := os.Open(configFile)
if err != nil {
return err
}
finfo, err := f.Stat()
if err != nil {
return err
}
mt := finfo.ModTime()
if !mt.Equal(lastMod) {
if Debug {
log.Log("[reload]", configFile)
}
r.Reload(f)
lastMod = mt
}
f.Close()
period := r.Period()
if period <= 0 {
log.Log("[reload] disabled:", configFile)
return nil
}
if period < time.Second {
period = time.Second
}
<-time.After(period)
}
}
package gost
import (
"bufio"
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"net"
"strings"
"sync"
"time"
"github.com/go-log/log"
)
var (
// DefaultResolverTimeout is the default timeout for name resolution.
DefaultResolverTimeout = 30 * time.Second
// DefaultResolverTTL is the default cache TTL for name resolution.
DefaultResolverTTL = 60 * time.Second
)
// Resolver is a name resolver for domain name.
// It contains a list of name servers.
type Resolver interface {
// Resolve returns a slice of that host's IPv4 and IPv6 addresses.
Resolve(host string) ([]net.IP, error)
}
// ReloadResolver is resolover that support live reloading
type ReloadResolver interface {
Resolver
Reloader
}
// NameServer is a name server.
// Currently supported protocol: TCP, UDP and TLS.
type NameServer struct {
Addr string
Protocol string
Hostname string // for TLS handshake verification
}
func (ns NameServer) String() string {
addr := ns.Addr
prot := ns.Protocol
host := ns.Hostname
if _, port, _ := net.SplitHostPort(addr); port == "" {
addr = net.JoinHostPort(addr, "53")
}
if prot == "" {
prot = "udp"
}
return fmt.Sprintf("%s/%s %s", addr, prot, host)
}
type resolverCacheItem struct {
IPs []net.IP
ts int64
}
type resolver struct {
Resolver *net.Resolver
Servers []NameServer
mCache *sync.Map
Timeout time.Duration
TTL time.Duration
period time.Duration
}
// NewResolver create a new Resolver with the given name servers and resolution timeout.
func NewResolver(timeout, ttl time.Duration, servers ...NameServer) ReloadResolver {
r := &resolver{
Servers: servers,
Timeout: timeout,
TTL: ttl,
mCache: &sync.Map{},
}
r.init()
return r
}
func (r *resolver) init() {
if r.Timeout <= 0 {
r.Timeout = DefaultResolverTimeout
}
if r.TTL == 0 {
r.TTL = DefaultResolverTTL
}
r.Resolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
for _, ns := range r.Servers {
conn, err = r.dial(ctx, ns)
if err == nil {
break
}
log.Logf("[resolver] %s : %s", ns, err)
}
return
},
}
}
func (r *resolver) dial(ctx context.Context, ns NameServer) (net.Conn, error) {
var d net.Dialer
addr := ns.Addr
if _, port, _ := net.SplitHostPort(addr); port == "" {
addr = net.JoinHostPort(addr, "53")
}
switch strings.ToLower(ns.Protocol) {
case "tcp":
return d.DialContext(ctx, "tcp", addr)
case "tls":
conn, err := d.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, err
}
cfg := &tls.Config{
ServerName: ns.Hostname,
}
if cfg.ServerName == "" {
cfg.InsecureSkipVerify = true
}
return tls.Client(conn, cfg), nil
case "udp":
fallthrough
default:
return d.DialContext(ctx, "udp", addr)
}
}
func (r *resolver) Resolve(name string) (ips []net.IP, err error) {
if r == nil {
return
}
timeout := r.Timeout
if ip := net.ParseIP(name); ip != nil {
return []net.IP{ip}, nil
}
ips = r.loadCache(name)
if len(ips) > 0 {
if Debug {
log.Logf("[resolver] cache hit: %s %v", name, ips)
}
return
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
addrs, err := r.Resolver.LookupIPAddr(ctx, name)
for _, addr := range addrs {
ips = append(ips, addr.IP)
}
r.storeCache(name, ips)
if len(ips) > 0 && Debug {
log.Logf("[resolver] %s %v", name, ips)
}
return
}
func (r *resolver) loadCache(name string) []net.IP {
ttl := r.TTL
if ttl < 0 {
return nil
}
if v, ok := r.mCache.Load(name); ok {
item, _ := v.(*resolverCacheItem)
if item == nil || time.Since(time.Unix(item.ts, 0)) > ttl {
return nil
}
return item.IPs
}
return nil
}
func (r *resolver) storeCache(name string, ips []net.IP) {
ttl := r.TTL
if ttl < 0 || name == "" || len(ips) == 0 {
return
}
r.mCache.Store(name, &resolverCacheItem{
IPs: ips,
ts: time.Now().Unix(),
})
}
func (r *resolver) Reload(rd io.Reader) error {
var nss []NameServer
scanner := bufio.NewScanner(rd)
for scanner.Scan() {
line := scanner.Text()
if n := strings.IndexByte(line, '#'); n >= 0 {
line = line[:n]
}
line = strings.Replace(line, "\t", " ", -1)
line = strings.TrimSpace(line)
if line == "" {
continue
}
var ss []string
for _, s := range strings.Split(line, " ") {
if s = strings.TrimSpace(s); s != "" {
ss = append(ss, s)
}
}
if len(ss) == 0 {
continue
}
if len(ss) >= 2 {
// timeout option
if strings.ToLower(ss[0]) == "timeout" {
r.Timeout, _ = time.ParseDuration(ss[1])
continue
}
// ttl option
if strings.ToLower(ss[0]) == "ttl" {
r.TTL, _ = time.ParseDuration(ss[1])
continue
}
// reload option
if strings.ToLower(ss[0]) == "reload" {
r.period, _ = time.ParseDuration(ss[1])
continue
}
}
var ns NameServer
switch len(ss) {
case 1:
ns.Addr = ss[0]
case 2:
ns.Addr = ss[0]
ns.Protocol = ss[1]
default:
ns.Addr = ss[0]
ns.Protocol = ss[1]
ns.Hostname = ss[2]
}
nss = append(nss, ns)
}
if err := scanner.Err(); err != nil {
return err
}
r.Servers = nss
return nil
}
func (r *resolver) Period() time.Duration {
return r.period
}
func (r *resolver) String() string {
if r == nil {
return ""
}
b := &bytes.Buffer{}
fmt.Fprintf(b, "Timeout %v\n", r.Timeout)
fmt.Fprintf(b, "TTL %v\n", r.TTL)
fmt.Fprintf(b, "Reload %v\n", r.period)
for i := range r.Servers {
fmt.Fprintln(b, r.Servers[i])
}
return b.String()
}
...@@ -3,6 +3,7 @@ package gost ...@@ -3,6 +3,7 @@ package gost
import ( import (
"io" "io"
"net" "net"
"sync"
"time" "time"
"github.com/go-log/log" "github.com/go-log/log"
...@@ -11,6 +12,17 @@ import ( ...@@ -11,6 +12,17 @@ import (
// Server is a proxy server. // Server is a proxy server.
type Server struct { type Server struct {
Listener Listener Listener Listener
options *ServerOptions
}
// Init intializes server with given options.
func (s *Server) Init(opts ...ServerOption) {
if s.options == nil {
s.options = &ServerOptions{}
}
for _, opt := range opts {
opt(s.options)
}
} }
// Addr returns the address of the server // Addr returns the address of the server
...@@ -24,7 +36,9 @@ func (s *Server) Close() error { ...@@ -24,7 +36,9 @@ func (s *Server) Close() error {
} }
// Serve serves as a proxy server. // Serve serves as a proxy server.
func (s *Server) Serve(h Handler) error { func (s *Server) Serve(h Handler, opts ...ServerOption) error {
s.Init(opts...)
if s.Listener == nil { if s.Listener == nil {
ln, err := TCPListener("") ln, err := TCPListener("")
if err != nil { if err != nil {
...@@ -57,9 +71,30 @@ func (s *Server) Serve(h Handler) error { ...@@ -57,9 +71,30 @@ func (s *Server) Serve(h Handler) error {
return e return e
} }
tempDelay = 0 tempDelay = 0
if s.options.Bypass.Contains(conn.RemoteAddr().String()) {
log.Log("[bypass]", conn.RemoteAddr())
conn.Close()
continue
}
go h.Handle(conn) go h.Handle(conn)
} }
}
// ServerOptions holds the options for Server.
type ServerOptions struct {
Bypass *Bypass
}
// ServerOption allows a common way to set server options.
type ServerOption func(opts *ServerOptions)
// BypassServerOption sets the bypass option of ServerOptions.
func BypassServerOption(bypass *Bypass) ServerOption {
return func(opts *ServerOptions) {
opts.Bypass = bypass
}
} }
// Listener is a proxy server listener, just like a net.Listener. // Listener is a proxy server listener, just like a net.Listener.
...@@ -98,15 +133,29 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { ...@@ -98,15 +133,29 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
return tc, nil return tc, nil
} }
var (
trPool = sync.Pool{
New: func() interface{} {
return make([]byte, 32*1024)
},
}
)
func transport(rw1, rw2 io.ReadWriter) error { func transport(rw1, rw2 io.ReadWriter) error {
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { go func() {
_, err := io.Copy(rw1, rw2) buf := trPool.Get().([]byte)
defer trPool.Put(buf)
_, err := io.CopyBuffer(rw1, rw2, buf)
errc <- err errc <- err
}() }()
go func() { go func() {
_, err := io.Copy(rw2, rw1) buf := trPool.Get().([]byte)
defer trPool.Put(buf)
_, err := io.CopyBuffer(rw2, rw1, buf)
errc <- err errc <- err
}() }()
......
name: gost name: gost
version: '2.5' version: '2.6'
summary: GO Simple Tunnel summary: GO Simple Tunnel
description: | description: |
A simple tunnel written in golang A simple tunnel written in golang
......
...@@ -33,17 +33,27 @@ func (c *sniConnector) Connect(conn net.Conn, addr string) (net.Conn, error) { ...@@ -33,17 +33,27 @@ func (c *sniConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
} }
type sniHandler struct { type sniHandler struct {
options []HandlerOption options *HandlerOptions
} }
// SNIHandler creates a server Handler for SNI proxy server. // SNIHandler creates a server Handler for SNI proxy server.
func SNIHandler(opts ...HandlerOption) Handler { func SNIHandler(opts ...HandlerOption) Handler {
h := &sniHandler{ h := &sniHandler{}
options: opts, h.Init(opts...)
}
return h return h
} }
func (h *sniHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
}
for _, opt := range options {
opt(h.options)
}
}
func (h *sniHandler) Handle(conn net.Conn) { func (h *sniHandler) Handle(conn net.Conn) {
br := bufio.NewReader(conn) br := bufio.NewReader(conn)
...@@ -66,7 +76,9 @@ func (h *sniHandler) Handle(conn net.Conn) { ...@@ -66,7 +76,9 @@ func (h *sniHandler) Handle(conn net.Conn) {
if !req.URL.IsAbs() { if !req.URL.IsAbs() {
req.URL.Scheme = "http" // make sure that the URL is absolute req.URL.Scheme = "http" // make sure that the URL is absolute
} }
HTTPHandler(h.options...).(*httpHandler).handleRequest(conn, req) handler := &httpHandler{options: h.options}
handler.Init()
handler.handleRequest(conn, req)
return return
} }
...@@ -76,19 +88,25 @@ func (h *sniHandler) Handle(conn net.Conn) { ...@@ -76,19 +88,25 @@ func (h *sniHandler) Handle(conn net.Conn) {
return return
} }
options := &HandlerOptions{} addr := net.JoinHostPort(host, "443")
for _, opt := range h.options {
opt(options)
}
if !Can("tcp", host, options.Whitelist, options.Blacklist) { if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[sni] Unauthorized to tcp connect to %s", host) log.Logf("[sni] Unauthorized to tcp connect to %s", addr)
return
}
if h.options.Bypass.Contains(addr) {
log.Log("[sni] [bypass]", addr)
return return
} }
cc, err := options.Chain.Dial(host + ":443") cc, err := h.options.Chain.Dial(addr,
RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout),
HostsChainOption(h.options.Hosts),
ResolverChainOption(h.options.Resolver),
)
if err != nil { if err != nil {
log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), host, err) log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), addr, err)
return return
} }
defer cc.Close() defer cc.Close()
......
...@@ -267,7 +267,7 @@ func (c *socks4Connector) Connect(conn net.Conn, addr string) (net.Conn, error) ...@@ -267,7 +267,7 @@ func (c *socks4Connector) Connect(conn net.Conn, addr string) (net.Conn, error)
return nil, err return nil, err
} }
if len(taddr.IP) == 0 { if len(taddr.IP) == 0 {
taddr.IP = net.IPv4(0, 0, 0, 0) taddr.IP = net.IPv4zero
} }
req := gosocks4.NewRequest(gosocks4.CmdConnect, req := gosocks4.NewRequest(gosocks4.CmdConnect,
...@@ -348,30 +348,36 @@ type socks5Handler struct { ...@@ -348,30 +348,36 @@ type socks5Handler struct {
// SOCKS5Handler creates a server Handler for SOCKS5 proxy server. // SOCKS5Handler creates a server Handler for SOCKS5 proxy server.
func SOCKS5Handler(opts ...HandlerOption) Handler { func SOCKS5Handler(opts ...HandlerOption) Handler {
options := &HandlerOptions{} h := &socks5Handler{}
for _, opt := range opts { h.Init(opts...)
opt(options)
return h
}
func (h *socks5Handler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
}
for _, opt := range options {
opt(h.options)
} }
tlsConfig := options.TLSConfig tlsConfig := h.options.TLSConfig
if tlsConfig == nil { if tlsConfig == nil {
tlsConfig = DefaultTLSConfig tlsConfig = DefaultTLSConfig
} }
selector := &serverSelector{ // socks5 server selector h.selector = &serverSelector{ // socks5 server selector
Users: options.Users, Users: h.options.Users,
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
} }
// methods that socks5 server supported // methods that socks5 server supported
selector.AddMethod( h.selector.AddMethod(
gosocks5.MethodNoAuth, gosocks5.MethodNoAuth,
gosocks5.MethodUserPass, gosocks5.MethodUserPass,
MethodTLS, MethodTLS,
MethodTLSAuth, MethodTLSAuth,
) )
return &socks5Handler{
options: options,
selector: selector,
}
} }
func (h *socks5Handler) Handle(conn net.Conn) { func (h *socks5Handler) Handle(conn net.Conn) {
...@@ -419,8 +425,22 @@ func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) { ...@@ -419,8 +425,22 @@ func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) {
} }
return return
} }
if h.options.Bypass.Contains(addr) {
log.Logf("[socks5-connect] [bypass] %s", addr)
rep := gosocks5.NewReply(gosocks5.NotAllowed, nil)
rep.Write(conn)
if Debug {
log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
}
return
}
cc, err := h.options.Chain.Dial(addr) cc, err := h.options.Chain.Dial(addr,
RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout),
HostsChainOption(h.options.Hosts),
ResolverChainOption(h.options.Resolver),
)
if err != nil { if err != nil {
log.Logf("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) log.Logf("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
...@@ -716,6 +736,10 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) { ...@@ -716,6 +736,10 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
if err != nil { if err != nil {
continue // drop silently continue // drop silently
} }
if h.options.Bypass.Contains(raddr.String()) {
log.Log("[socks5-udp] [bypass] write to", raddr)
continue // bypass
}
if _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil { if _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil {
errc <- err errc <- err
return return
...@@ -738,6 +762,10 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) { ...@@ -738,6 +762,10 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
if clientAddr == nil { if clientAddr == nil {
continue continue
} }
if h.options.Bypass.Contains(raddr.String()) {
log.Log("[socks5-udp] [bypass] read from", raddr)
continue // bypass
}
buf := bytes.Buffer{} buf := bytes.Buffer{}
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n]) dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n])
dgram.Write(&buf) dgram.Write(&buf)
...@@ -785,6 +813,11 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error ...@@ -785,6 +813,11 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
if clientAddr == nil { if clientAddr == nil {
clientAddr = addr clientAddr = addr
} }
raddr := dgram.Header.Addr.String()
if h.options.Bypass.Contains(raddr) {
log.Log("[udp-tun] [bypass] write to", raddr)
continue // bypass
}
dgram.Header.Rsv = uint16(len(dgram.Data)) dgram.Header.Rsv = uint16(len(dgram.Data))
if err := dgram.Write(cc); err != nil { if err := dgram.Write(cc); err != nil {
errc <- err errc <- err
...@@ -809,6 +842,11 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error ...@@ -809,6 +842,11 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
if clientAddr == nil { if clientAddr == nil {
continue continue
} }
raddr := dgram.Header.Addr.String()
if h.options.Bypass.Contains(raddr) {
log.Log("[udp-tun] [bypass] read from", raddr)
continue // bypass
}
dgram.Header.Rsv = 0 dgram.Header.Rsv = 0
buf := bytes.Buffer{} buf := bytes.Buffer{}
...@@ -903,6 +941,10 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error ...@@ -903,6 +941,10 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
errc <- err errc <- err
return return
} }
if h.options.Bypass.Contains(addr.String()) {
log.Log("[udp-tun] [bypass] read from", addr)
continue // bypass
}
// pipe from peer to tunnel // pipe from peer to tunnel
dgram := gosocks5.NewUDPDatagram( dgram := gosocks5.NewUDPDatagram(
...@@ -932,6 +974,10 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error ...@@ -932,6 +974,10 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
if err != nil { if err != nil {
continue // drop silently continue // drop silently
} }
if h.options.Bypass.Contains(addr.String()) {
log.Log("[udp-tun] [bypass] write to", addr)
continue // bypass
}
if _, err := uc.WriteToUDP(dgram.Data, addr); err != nil { if _, err := uc.WriteToUDP(dgram.Data, addr); err != nil {
log.Logf("[udp-tun] %s -> %s : %s", cc.RemoteAddr(), addr, err) log.Logf("[udp-tun] %s -> %s : %s", cc.RemoteAddr(), addr, err)
errc <- err errc <- err
...@@ -1075,12 +1121,19 @@ type socks4Handler struct { ...@@ -1075,12 +1121,19 @@ type socks4Handler struct {
// SOCKS4Handler creates a server Handler for SOCKS4(A) proxy server. // SOCKS4Handler creates a server Handler for SOCKS4(A) proxy server.
func SOCKS4Handler(opts ...HandlerOption) Handler { func SOCKS4Handler(opts ...HandlerOption) Handler {
options := &HandlerOptions{} h := &socks4Handler{}
for _, opt := range opts { h.Init(opts...)
opt(options)
return h
}
func (h *socks4Handler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
} }
return &socks4Handler{
options: options, for _, opt := range options {
opt(h.options)
} }
} }
...@@ -1116,7 +1169,16 @@ func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) { ...@@ -1116,7 +1169,16 @@ func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) {
if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) { if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[socks4-connect] Unauthorized to tcp connect to %s", addr) log.Logf("[socks4-connect] Unauthorized to tcp connect to %s", addr)
rep := gosocks5.NewReply(gosocks4.Rejected, nil) rep := gosocks4.NewReply(gosocks4.Rejected, nil)
rep.Write(conn)
if Debug {
log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
}
return
}
if h.options.Bypass.Contains(addr) {
log.Log("[socks4-connect] [bypass]", addr)
rep := gosocks4.NewReply(gosocks4.Rejected, nil)
rep.Write(conn) rep.Write(conn)
if Debug { if Debug {
log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
...@@ -1124,7 +1186,10 @@ func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) { ...@@ -1124,7 +1186,10 @@ func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) {
return return
} }
cc, err := h.options.Chain.Dial(addr) cc, err := h.options.Chain.Dial(addr,
RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout),
)
if err != nil { if err != nil {
log.Logf("[socks4-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) log.Logf("[socks4-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
rep := gosocks4.NewReply(gosocks4.Failed, nil) rep := gosocks4.NewReply(gosocks4.Failed, nil)
......
...@@ -97,13 +97,20 @@ type shadowHandler struct { ...@@ -97,13 +97,20 @@ type shadowHandler struct {
// ShadowHandler creates a server Handler for shadowsocks proxy server. // ShadowHandler creates a server Handler for shadowsocks proxy server.
func ShadowHandler(opts ...HandlerOption) Handler { func ShadowHandler(opts ...HandlerOption) Handler {
h := &shadowHandler{ h := &shadowHandler{}
options: &HandlerOptions{}, h.Init(opts...)
return h
}
func (h *shadowHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
} }
for _, opt := range opts {
for _, opt := range options {
opt(h.options) opt(h.options)
} }
return h
} }
func (h *shadowHandler) Handle(conn net.Conn) { func (h *shadowHandler) Handle(conn net.Conn) {
...@@ -140,7 +147,17 @@ func (h *shadowHandler) Handle(conn net.Conn) { ...@@ -140,7 +147,17 @@ func (h *shadowHandler) Handle(conn net.Conn) {
return return
} }
cc, err := h.options.Chain.Dial(addr) if h.options.Bypass.Contains(addr) {
log.Logf("[ss] [bypass] %s", addr)
return
}
cc, err := h.options.Chain.Dial(addr,
RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout),
HostsChainOption(h.options.Hosts),
ResolverChainOption(h.options.Resolver),
)
if err != nil { if err != nil {
log.Logf("[ss] %s -> %s : %s", conn.RemoteAddr(), addr, err) log.Logf("[ss] %s -> %s : %s", conn.RemoteAddr(), addr, err)
return return
...@@ -321,13 +338,20 @@ type shadowUDPdHandler struct { ...@@ -321,13 +338,20 @@ type shadowUDPdHandler struct {
// ShadowUDPdHandler creates a server Handler for shadowsocks UDP relay server. // ShadowUDPdHandler creates a server Handler for shadowsocks UDP relay server.
func ShadowUDPdHandler(opts ...HandlerOption) Handler { func ShadowUDPdHandler(opts ...HandlerOption) Handler {
h := &shadowUDPdHandler{ h := &shadowUDPdHandler{}
options: &HandlerOptions{}, h.Init(opts...)
return h
}
func (h *shadowUDPdHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
} }
for _, opt := range opts {
for _, opt := range options {
opt(h.options) opt(h.options)
} }
return h
} }
func (h *shadowUDPdHandler) Handle(conn net.Conn) { func (h *shadowUDPdHandler) Handle(conn net.Conn) {
...@@ -353,11 +377,11 @@ func (h *shadowUDPdHandler) Handle(conn net.Conn) { ...@@ -353,11 +377,11 @@ func (h *shadowUDPdHandler) Handle(conn net.Conn) {
defer cc.Close() defer cc.Close()
log.Logf("[ssu] %s <-> %s", conn.RemoteAddr(), conn.LocalAddr()) log.Logf("[ssu] %s <-> %s", conn.RemoteAddr(), conn.LocalAddr())
transportUDP(conn, cc) h.transportUDP(conn, cc)
log.Logf("[ssu] %s >-< %s", conn.RemoteAddr(), conn.LocalAddr()) log.Logf("[ssu] %s >-< %s", conn.RemoteAddr(), conn.LocalAddr())
} }
func transportUDP(sc net.Conn, cc net.PacketConn) error { func (h *shadowUDPdHandler) transportUDP(sc net.Conn, cc net.PacketConn) error {
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { go func() {
for { for {
...@@ -374,14 +398,18 @@ func transportUDP(sc net.Conn, cc net.PacketConn) error { ...@@ -374,14 +398,18 @@ func transportUDP(sc net.Conn, cc net.PacketConn) error {
errc <- err errc <- err
return return
} }
//if Debug { if Debug {
// log.Logf("[ssu] %s >>> %s length: %d", sc.RemoteAddr(), dgram.Header.Addr.String(), len(dgram.Data)) log.Logf("[ssu] %s >>> %s length: %d", sc.RemoteAddr(), dgram.Header.Addr.String(), len(dgram.Data))
//} }
addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
if err != nil { if err != nil {
errc <- err errc <- err
return return
} }
if h.options.Bypass.Contains(addr.String()) {
log.Log("[ssu] [bypass] write to", addr)
continue // bypass
}
if _, err := cc.WriteTo(dgram.Data, addr); err != nil { if _, err := cc.WriteTo(dgram.Data, addr); err != nil {
errc <- err errc <- err
return return
...@@ -397,9 +425,13 @@ func transportUDP(sc net.Conn, cc net.PacketConn) error { ...@@ -397,9 +425,13 @@ func transportUDP(sc net.Conn, cc net.PacketConn) error {
errc <- err errc <- err
return return
} }
//if Debug { if Debug {
// log.Logf("[ssu] %s <<< %s length: %d", sc.RemoteAddr(), addr, n) log.Logf("[ssu] %s <<< %s length: %d", sc.RemoteAddr(), addr, n)
//} }
if h.options.Bypass.Contains(addr.String()) {
log.Log("[ssu] [bypass] read from", addr)
continue // bypass
}
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(addr)), b[:n]) dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(addr)), b[:n])
buf := bytes.Buffer{} buf := bytes.Buffer{}
dgram.Write(&buf) dgram.Write(&buf)
......
...@@ -408,13 +408,22 @@ type sshForwardHandler struct { ...@@ -408,13 +408,22 @@ type sshForwardHandler struct {
// SSHForwardHandler creates a server Handler for SSH port forwarding server. // SSHForwardHandler creates a server Handler for SSH port forwarding server.
func SSHForwardHandler(opts ...HandlerOption) Handler { func SSHForwardHandler(opts ...HandlerOption) Handler {
h := &sshForwardHandler{ h := &sshForwardHandler{}
options: new(HandlerOptions), h.Init(opts...)
config: new(ssh.ServerConfig),
return h
}
func (h *sshForwardHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
} }
for _, opt := range opts {
for _, opt := range options {
opt(h.options) opt(h.options)
} }
h.config = &ssh.ServerConfig{}
h.config.PasswordCallback = defaultSSHPasswordCallback(h.options.Users...) h.config.PasswordCallback = defaultSSHPasswordCallback(h.options.Users...)
if len(h.options.Users) == 0 { if len(h.options.Users) == 0 {
h.config.NoClientAuth = true h.config.NoClientAuth = true
...@@ -430,8 +439,6 @@ func SSHForwardHandler(opts ...HandlerOption) Handler { ...@@ -430,8 +439,6 @@ func SSHForwardHandler(opts ...HandlerOption) Handler {
} }
h.config.AddHostKey(signer) h.config.AddHostKey(signer)
} }
return h
} }
func (h *sshForwardHandler) Handle(conn net.Conn) { func (h *sshForwardHandler) Handle(conn net.Conn) {
...@@ -506,7 +513,17 @@ func (h *sshForwardHandler) directPortForwardChannel(channel ssh.Channel, raddr ...@@ -506,7 +513,17 @@ func (h *sshForwardHandler) directPortForwardChannel(channel ssh.Channel, raddr
return return
} }
conn, err := h.options.Chain.Dial(raddr) if h.options.Bypass.Contains(raddr) {
log.Logf("[ssh-tcp] [bypass] %s", raddr)
return
}
conn, err := h.options.Chain.Dial(raddr,
RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout),
HostsChainOption(h.options.Hosts),
ResolverChainOption(h.options.Resolver),
)
if err != nil { if err != nil {
log.Logf("[ssh-tcp] %s - %s : %s", h.options.Addr, raddr, err) log.Logf("[ssh-tcp] %s - %s : %s", h.options.Addr, raddr, err)
return return
......
...@@ -30,7 +30,7 @@ func (tr *tlsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) ( ...@@ -30,7 +30,7 @@ func (tr *tlsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (
if opts.TLSConfig == nil { if opts.TLSConfig == nil {
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true} opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
} }
return wrapTLSClient(conn, opts.TLSConfig) return wrapTLSClient(conn, opts.TLSConfig, opts.Timeout)
} }
type mtlsTransporter struct { type mtlsTransporter struct {
...@@ -113,7 +113,7 @@ func (tr *mtlsTransporter) initSession(addr string, conn net.Conn, opts *Handsha ...@@ -113,7 +113,7 @@ func (tr *mtlsTransporter) initSession(addr string, conn net.Conn, opts *Handsha
if opts.TLSConfig == nil { if opts.TLSConfig == nil {
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true} opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
} }
conn, err := wrapTLSClient(conn, opts.TLSConfig) conn, err := wrapTLSClient(conn, opts.TLSConfig, opts.Timeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -248,7 +248,7 @@ func (l *mtlsListener) Close() error { ...@@ -248,7 +248,7 @@ func (l *mtlsListener) Close() error {
// //
// This code is taken from consul: // This code is taken from consul:
// https://github.com/hashicorp/consul/blob/master/tlsutil/config.go // https://github.com/hashicorp/consul/blob/master/tlsutil/config.go
func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) { func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) (net.Conn, error) {
var err error var err error
var tlsConn *tls.Conn var tlsConn *tls.Conn
...@@ -264,6 +264,12 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) { ...@@ -264,6 +264,12 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
return tlsConn, nil return tlsConn, nil
} }
if timeout <= 0 {
timeout = 10 * time.Second // default timeout
}
tlsConn.SetDeadline(time.Now().Add(timeout))
// Otherwise perform handshake, but don't verify the domain // Otherwise perform handshake, but don't verify the domain
// //
// The following is lightly-modified from the doFullHandshake // The following is lightly-modified from the doFullHandshake
...@@ -273,6 +279,8 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) { ...@@ -273,6 +279,8 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
return nil, err return nil, err
} }
tlsConn.SetDeadline(time.Time{}) // clear timeout
opts := x509.VerifyOptions{ opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs, Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(), CurrentTime: time.Now(),
......
[![Godoc Reference](https://godoc.org/github.com/aead/chacha20?status.svg)](https://godoc.org/github.com/aead/chacha20)
## The ChaCha20 stream cipher
ChaCha is a stream cipher family created by Daniel J. Bernstein.
The most common ChaCha cipher is ChaCha20 (20 rounds). ChaCha20 is standardized in [RFC 7539](https://tools.ietf.org/html/rfc7539 "RFC 7539").
This package provides implementations of three ChaCha versions:
- ChaCha20 with a 64 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
- ChaCha20 with a 96 bit nonce (can en/decrypt up to 2^32 * 64 bytes ~ 256 GB for one key-nonce combination)
- XChaCha20 with a 192 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
Furthermore the chacha subpackage implements ChaCha20/12 and ChaCha20/8.
These versions use 12 or 8 rounds instead of 20.
But it's recommended to use ChaCha20 (with 20 rounds) - it will be fast enough for almost all purposes.
### Installation
Install in your GOPATH: `go get -u github.com/aead/chacha20`
### Requirements
All go versions >= 1.5.3 are supported.
Please notice, that the amd64 AVX2 asm implementation requires go1.7 or newer.
### Performance
#### AMD64
Hardware: Intel i7-6500U 2.50GHz x 2
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic
Go version: 1.8.0
```
AVX2
name speed cpb
ChaCha20_64-4 573MB/s ± 0% 4.16
ChaCha20_1K-4 2.19GB/s ± 0% 1.06
XChaCha20_64-4 261MB/s ± 0% 9.13
XChaCha20_1K-4 1.69GB/s ± 4% 1.37
XORKeyStream64-4 474MB/s ± 2% 5.02
XORKeyStream1K-4 2.09GB/s ± 1% 1.11
XChaCha20_XORKeyStream64-4 262MB/s ± 0% 9.09
XChaCha20_XORKeyStream1K-4 1.71GB/s ± 1% 1.36
SSSE3
name speed cpb
ChaCha20_64-4 583MB/s ± 0% 4.08
ChaCha20_1K-4 1.15GB/s ± 1% 2.02
XChaCha20_64-4 267MB/s ± 0% 8.92
XChaCha20_1K-4 984MB/s ± 5% 2.42
XORKeyStream64-4 492MB/s ± 1% 4.84
XORKeyStream1K-4 1.10GB/s ± 5% 2.11
XChaCha20_XORKeyStream64-4 266MB/s ± 0% 8.96
XChaCha20_XORKeyStream1K-4 1.00GB/s ± 2% 2.32
```
#### 386
Hardware: Intel i7-6500U 2.50GHz x 2
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic
Go version: 1.8.0
```
SSSE3
name                        speed cpb
ChaCha20_64-4               570MB/s ± 0% 4.18
ChaCha20_1K-4               650MB/s ± 0% 3.66
XChaCha20_64-4              223MB/s ± 0% 10.69
XChaCha20_1K-4              584MB/s ± 1% 4.08
XORKeyStream64-4            392MB/s ± 1% 6.08
XORKeyStream1K-4            629MB/s ± 1% 3.79
XChaCha20_XORKeyStream64-4  222MB/s ± 0% 10.73
XChaCha20_XORKeyStream1K-4  585MB/s ± 0% 4.07
SSE2
name speed cpb
ChaCha20_64-4 509MB/s ± 0% 4.68
ChaCha20_1K-4 553MB/s ± 2% 4.31
XChaCha20_64-4 201MB/s ± 0% 11.86
XChaCha20_1K-4 498MB/s ± 4% 4.78
XORKeyStream64-4 359MB/s ± 1% 6.64
XORKeyStream1K-4 545MB/s ± 0% 4.37
XChaCha20_XORKeyStream64-4 201MB/s ± 1% 11.86
XChaCha20_XORKeyStream1K-4 507MB/s ± 0% 4.70
```
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// Package chacha implements some low-level functions of the
// ChaCha cipher family.
package chacha // import "github.com/aead/chacha20/chacha"
import (
"encoding/binary"
"errors"
)
const (
// NonceSize is the size of the ChaCha20 nonce in bytes.
NonceSize = 8
// INonceSize is the size of the IETF-ChaCha20 nonce in bytes.
INonceSize = 12
// XNonceSize is the size of the XChaCha20 nonce in bytes.
XNonceSize = 24
// KeySize is the size of the key in bytes.
KeySize = 32
)
var (
useSSE2 bool
useSSSE3 bool
useAVX2 bool
)
var (
errKeySize = errors.New("chacha20/chacha: bad key length")
errInvalidNonce = errors.New("chacha20/chacha: bad nonce length")
)
func setup(state *[64]byte, nonce, key []byte) (err error) {
if len(key) != KeySize {
err = errKeySize
return
}
var Nonce [16]byte
switch len(nonce) {
case NonceSize:
copy(Nonce[8:], nonce)
initialize(state, key, &Nonce)
case INonceSize:
copy(Nonce[4:], nonce)
initialize(state, key, &Nonce)
case XNonceSize:
var tmpKey [32]byte
var hNonce [16]byte
copy(hNonce[:], nonce[:16])
copy(tmpKey[:], key)
hChaCha20(&tmpKey, &hNonce, &tmpKey)
copy(Nonce[8:], nonce[16:])
initialize(state, tmpKey[:], &Nonce)
// BUG(aead): A "good" compiler will remove this (optimizations)
// But using the provided key instead of tmpKey,
// will change the key (-> probably confuses users)
for i := range tmpKey {
tmpKey[i] = 0
}
default:
err = errInvalidNonce
}
return
}
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
// The length of the nonce determinds the version of ChaCha20:
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
// The rounds argument specifies the number of rounds performed for keystream
// generation - valid values are 8, 12 or 20. The src and dst may be the same slice
// but otherwise should not overlap. If len(dst) < len(src) this function panics.
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
func XORKeyStream(dst, src, nonce, key []byte, rounds int) {
if rounds != 20 && rounds != 12 && rounds != 8 {
panic("chacha20/chacha: bad number of rounds")
}
if len(dst) < len(src) {
panic("chacha20/chacha: dst buffer is to small")
}
if len(nonce) == INonceSize && uint64(len(src)) > (1<<38) {
panic("chacha20/chacha: src is too large")
}
var block, state [64]byte
if err := setup(&state, nonce, key); err != nil {
panic(err)
}
xorKeyStream(dst, src, &block, &state, rounds)
}
// Cipher implements ChaCha20/r (XChaCha20/r) for a given number of rounds r.
type Cipher struct {
state, block [64]byte
off int
rounds int // 20 for ChaCha20
noncesize int
}
// NewCipher returns a new *chacha.Cipher implementing the ChaCha20/r or XChaCha20/r
// (r = 8, 12 or 20) stream cipher. The nonce must be unique for one key for all time.
// The length of the nonce determinds the version of ChaCha20:
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
func NewCipher(nonce, key []byte, rounds int) (*Cipher, error) {
if rounds != 20 && rounds != 12 && rounds != 8 {
panic("chacha20/chacha: bad number of rounds")
}
c := new(Cipher)
if err := setup(&(c.state), nonce, key); err != nil {
return nil, err
}
c.rounds = rounds
if len(nonce) == INonceSize {
c.noncesize = INonceSize
} else {
c.noncesize = NonceSize
}
return c, nil
}
// XORKeyStream crypts bytes from src to dst. Src and dst may be the same slice
// but otherwise should not overlap. If len(dst) < len(src) the function panics.
func (c *Cipher) XORKeyStream(dst, src []byte) {
if len(dst) < len(src) {
panic("chacha20/chacha: dst buffer is to small")
}
if c.off > 0 {
n := len(c.block[c.off:])
if len(src) <= n {
for i, v := range src {
dst[i] = v ^ c.block[c.off]
c.off++
}
if c.off == 64 {
c.off = 0
}
return
}
for i, v := range c.block[c.off:] {
dst[i] = src[i] ^ v
}
src = src[n:]
dst = dst[n:]
c.off = 0
}
c.off += xorKeyStream(dst, src, &(c.block), &(c.state), c.rounds)
}
// SetCounter skips ctr * 64 byte blocks. SetCounter(0) resets the cipher.
// This function always skips the unused keystream of the current 64 byte block.
func (c *Cipher) SetCounter(ctr uint64) {
if c.noncesize == INonceSize {
binary.LittleEndian.PutUint32(c.state[48:], uint32(ctr))
} else {
binary.LittleEndian.PutUint64(c.state[48:], ctr)
}
c.off = 0
}
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build 386,!gccgo,!appengine,!nacl
package chacha
import "encoding/binary"
func init() {
useSSE2 = supportsSSE2()
useSSSE3 = supportsSSSE3()
useAVX2 = false
}
func initialize(state *[64]byte, key []byte, nonce *[16]byte) {
binary.LittleEndian.PutUint32(state[0:], sigma[0])
binary.LittleEndian.PutUint32(state[4:], sigma[1])
binary.LittleEndian.PutUint32(state[8:], sigma[2])
binary.LittleEndian.PutUint32(state[12:], sigma[3])
copy(state[16:], key[:])
copy(state[48:], nonce[:])
}
// This function is implemented in chacha_386.s
//go:noescape
func supportsSSE2() bool
// This function is implemented in chacha_386.s
//go:noescape
func supportsSSSE3() bool
// This function is implemented in chacha_386.s
//go:noescape
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_386.s
//go:noescape
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_386.s
//go:noescape
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
// This function is implemented in chacha_386.s
//go:noescape
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
if useSSSE3 {
hChaCha20SSSE3(out, nonce, key)
} else if useSSE2 {
hChaCha20SSE2(out, nonce, key)
} else {
hChaCha20Generic(out, nonce, key)
}
}
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
if useSSSE3 {
return xorKeyStreamSSSE3(dst, src, block, state, rounds)
} else if useSSE2 {
return xorKeyStreamSSE2(dst, src, block, state, rounds)
}
return xorKeyStreamGeneric(dst, src, block, state, rounds)
}
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build 386,!gccgo,!appengine,!nacl
#include "textflag.h"
DATA ·sigma<>+0x00(SB)/4, $0x61707865
DATA ·sigma<>+0x04(SB)/4, $0x3320646e
DATA ·sigma<>+0x08(SB)/4, $0x79622d32
DATA ·sigma<>+0x0C(SB)/4, $0x6b206574
GLOBL ·sigma<>(SB), (NOPTR+RODATA), $16
DATA ·one<>+0x00(SB)/8, $1
DATA ·one<>+0x08(SB)/8, $0
GLOBL ·one<>(SB), (NOPTR+RODATA), $16
DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302
DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
GLOBL ·rol16<>(SB), (NOPTR+RODATA), $16
DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003
DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B
GLOBL ·rol8<>(SB), (NOPTR+RODATA), $16
#define ROTL_SSE2(n, t, v) \
MOVO v, t; \
PSLLL $n, t; \
PSRLL $(32-n), v; \
PXOR t, v
#define CHACHA_QROUND_SSE2(v0, v1, v2, v3, t0) \
PADDL v1, v0; \
PXOR v0, v3; \
ROTL_SSE2(16, t0, v3); \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE2(12, t0, v1); \
PADDL v1, v0; \
PXOR v0, v3; \
ROTL_SSE2(8, t0, v3); \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE2(7, t0, v1)
#define CHACHA_QROUND_SSSE3(v0, v1, v2, v3, t0, r16, r8) \
PADDL v1, v0; \
PXOR v0, v3; \
PSHUFB r16, v3; \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE2(12, t0, v1); \
PADDL v1, v0; \
PXOR v0, v3; \
PSHUFB r8, v3; \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE2(7, t0, v1)
#define CHACHA_SHUFFLE(v1, v2, v3) \
PSHUFL $0x39, v1, v1; \
PSHUFL $0x4E, v2, v2; \
PSHUFL $0x93, v3, v3
#define XOR(dst, src, off, v0, v1, v2, v3, t0) \
MOVOU 0+off(src), t0; \
PXOR v0, t0; \
MOVOU t0, 0+off(dst); \
MOVOU 16+off(src), t0; \
PXOR v1, t0; \
MOVOU t0, 16+off(dst); \
MOVOU 32+off(src), t0; \
PXOR v2, t0; \
MOVOU t0, 32+off(dst); \
MOVOU 48+off(src), t0; \
PXOR v3, t0; \
MOVOU t0, 48+off(dst)
#define FINALIZE(dst, src, block, len, t0, t1) \
XORL t0, t0; \
XORL t1, t1; \
finalize: \
MOVB 0(src), t0; \
MOVB 0(block), t1; \
XORL t0, t1; \
MOVB t1, 0(dst); \
INCL src; \
INCL block; \
INCL dst; \
DECL len; \
JA finalize \
// func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
TEXT ·xorKeyStreamSSE2(SB), 4, $0-40
MOVL dst_base+0(FP), DI
MOVL src_base+12(FP), SI
MOVL src_len+16(FP), CX
MOVL state+28(FP), AX
MOVL rounds+32(FP), DX
MOVOU 0(AX), X0
MOVOU 16(AX), X1
MOVOU 32(AX), X2
MOVOU 48(AX), X3
TESTL CX, CX
JZ done
at_least_64:
MOVO X0, X4
MOVO X1, X5
MOVO X2, X6
MOVO X3, X7
MOVL DX, BX
chacha_loop:
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0)
CHACHA_SHUFFLE(X5, X6, X7)
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0)
CHACHA_SHUFFLE(X7, X6, X5)
SUBL $2, BX
JA chacha_loop
MOVOU 0(AX), X0
PADDL X0, X4
PADDL X1, X5
PADDL X2, X6
PADDL X3, X7
MOVOU ·one<>(SB), X0
PADDQ X0, X3
CMPL CX, $64
JB less_than_64
XOR(DI, SI, 0, X4, X5, X6, X7, X0)
MOVOU 0(AX), X0
ADDL $64, SI
ADDL $64, DI
SUBL $64, CX
JNZ at_least_64
less_than_64:
MOVL CX, BP
TESTL BP, BP
JZ done
MOVL block+24(FP), BX
MOVOU X4, 0(BX)
MOVOU X5, 16(BX)
MOVOU X6, 32(BX)
MOVOU X7, 48(BX)
FINALIZE(DI, SI, BX, BP, AX, DX)
done:
MOVL state+28(FP), AX
MOVOU X3, 48(AX)
MOVL CX, ret+36(FP)
RET
// func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int
TEXT ·xorKeyStreamSSSE3(SB), 4, $64-40
MOVL dst_base+0(FP), DI
MOVL src_base+12(FP), SI
MOVL src_len+16(FP), CX
MOVL state+28(FP), AX
MOVL rounds+32(FP), DX
MOVOU 48(AX), X3
TESTL CX, CX
JZ done
MOVL SP, BP
ADDL $16, SP
ANDL $-16, SP
MOVOU ·one<>(SB), X0
MOVOU 16(AX), X1
MOVOU 32(AX), X2
MOVO X0, 0(SP)
MOVO X1, 16(SP)
MOVO X2, 32(SP)
MOVOU 0(AX), X0
MOVOU ·rol16<>(SB), X1
MOVOU ·rol8<>(SB), X2
at_least_64:
MOVO X0, X4
MOVO 16(SP), X5
MOVO 32(SP), X6
MOVO X3, X7
MOVL DX, BX
chacha_loop:
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X0, X1, X2)
CHACHA_SHUFFLE(X5, X6, X7)
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X0, X1, X2)
CHACHA_SHUFFLE(X7, X6, X5)
SUBL $2, BX
JA chacha_loop
MOVOU 0(AX), X0
PADDL X0, X4
PADDL 16(SP), X5
PADDL 32(SP), X6
PADDL X3, X7
PADDQ 0(SP), X3
CMPL CX, $64
JB less_than_64
XOR(DI, SI, 0, X4, X5, X6, X7, X0)
MOVOU 0(AX), X0
ADDL $64, SI
ADDL $64, DI
SUBL $64, CX
JNZ at_least_64
less_than_64:
MOVL BP, SP
MOVL CX, BP
TESTL BP, BP
JE done
MOVL block+24(FP), BX
MOVOU X4, 0(BX)
MOVOU X5, 16(BX)
MOVOU X6, 32(BX)
MOVOU X7, 48(BX)
FINALIZE(DI, SI, BX, BP, AX, DX)
done:
MOVL state+28(FP), AX
MOVOU X3, 48(AX)
MOVL CX, ret+36(FP)
RET
// func supportsSSE2() bool
TEXT ·supportsSSE2(SB), NOSPLIT, $0-1
XORL AX, AX
INCL AX
CPUID
SHRL $26, DX
ANDL $1, DX
MOVB DX, ret+0(FP)
RET
// func supportsSSSE3() bool
TEXT ·supportsSSSE3(SB), NOSPLIT, $0-1
XORL AX, AX
INCL AX
CPUID
SHRL $9, CX
ANDL $1, CX
MOVB CX, ret+0(FP)
RET
// func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
TEXT ·hChaCha20SSE2(SB), 4, $0-12
MOVL out+0(FP), DI
MOVL nonce+4(FP), AX
MOVL key+8(FP), BX
MOVOU ·sigma<>(SB), X0
MOVOU 0(BX), X1
MOVOU 16(BX), X2
MOVOU 0(AX), X3
MOVL $20, CX
chacha_loop:
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4)
CHACHA_SHUFFLE(X1, X2, X3)
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4)
CHACHA_SHUFFLE(X3, X2, X1)
SUBL $2, CX
JNZ chacha_loop
MOVOU X0, 0(DI)
MOVOU X3, 16(DI)
RET
// func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
TEXT ·hChaCha20SSSE3(SB), 4, $0-12
MOVL out+0(FP), DI
MOVL nonce+4(FP), AX
MOVL key+8(FP), BX
MOVOU ·sigma<>(SB), X0
MOVOU 0(BX), X1
MOVOU 16(BX), X2
MOVOU 0(AX), X3
MOVOU ·rol16<>(SB), X5
MOVOU ·rol8<>(SB), X6
MOVL $20, CX
chacha_loop:
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6)
CHACHA_SHUFFLE(X1, X2, X3)
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6)
CHACHA_SHUFFLE(X3, X2, X1)
SUBL $2, CX
JNZ chacha_loop
MOVOU X0, 0(DI)
MOVOU X3, 16(DI)
RET
This diff is collapsed.
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build amd64,!gccgo,!appengine,!nacl,!go1.7
package chacha
func init() {
useSSE2 = true
useSSSE3 = supportsSSSE3()
useAVX2 = false
}
// This function is implemented in chacha_amd64.s
//go:noescape
func initialize(state *[64]byte, key []byte, nonce *[16]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func supportsSSSE3() bool
// This function is implemented in chacha_amd64.s
//go:noescape
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
// This function is implemented in chacha_amd64.s
//go:noescape
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
if useSSSE3 {
hChaCha20SSSE3(out, nonce, key)
} else if useSSE2 { // on amd64 this is always true - used to test generic on amd64
hChaCha20SSE2(out, nonce, key)
} else {
hChaCha20Generic(out, nonce, key)
}
}
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
if useSSSE3 {
return xorKeyStreamSSSE3(dst, src, block, state, rounds)
} else if useSSE2 { // on amd64 this is always true - used to test generic on amd64
return xorKeyStreamSSE2(dst, src, block, state, rounds)
}
return xorKeyStreamGeneric(dst, src, block, state, rounds)
}
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build go1.7,amd64,!gccgo,!appengine,!nacl
package chacha
func init() {
useSSE2 = true
useSSSE3 = supportsSSSE3()
useAVX2 = supportsAVX2()
}
// This function is implemented in chacha_amd64.s
//go:noescape
func initialize(state *[64]byte, key []byte, nonce *[16]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func supportsSSSE3() bool
// This function is implemented in chachaAVX2_amd64.s
//go:noescape
func supportsAVX2() bool
// This function is implemented in chacha_amd64.s
//go:noescape
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chachaAVX2_amd64.s
//go:noescape
func hChaCha20AVX(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
// This function is implemented in chacha_amd64.s
//go:noescape
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int
// This function is implemented in chachaAVX2_amd64.s
//go:noescape
func xorKeyStreamAVX2(dst, src []byte, block, state *[64]byte, rounds int) int
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
if useAVX2 {
hChaCha20AVX(out, nonce, key)
} else if useSSSE3 {
hChaCha20SSSE3(out, nonce, key)
} else if useSSE2 { // on amd64 this is always true - neccessary for testing generic on amd64
hChaCha20SSE2(out, nonce, key)
} else {
hChaCha20Generic(out, nonce, key)
}
}
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
if useAVX2 {
return xorKeyStreamAVX2(dst, src, block, state, rounds)
} else if useSSSE3 {
return xorKeyStreamSSSE3(dst, src, block, state, rounds)
} else if useSSE2 { // on amd64 this is always true - neccessary for testing generic on amd64
return xorKeyStreamSSE2(dst, src, block, state, rounds)
}
return xorKeyStreamGeneric(dst, src, block, state, rounds)
}
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build !amd64,!386 gccgo appengine nacl
package chacha
import "encoding/binary"
func initialize(state *[64]byte, key []byte, nonce *[16]byte) {
binary.LittleEndian.PutUint32(state[0:], sigma[0])
binary.LittleEndian.PutUint32(state[4:], sigma[1])
binary.LittleEndian.PutUint32(state[8:], sigma[2])
binary.LittleEndian.PutUint32(state[12:], sigma[3])
copy(state[16:], key[:])
copy(state[48:], nonce[:])
}
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
return xorKeyStreamGeneric(dst, src, block, state, rounds)
}
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
hChaCha20Generic(out, nonce, key)
}
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// Package chacha20 implements the ChaCha20 / XChaCha20 stream chipher.
// Notice that one specific key-nonce combination must be unique for all time.
//
// There are three versions of ChaCha20:
// - ChaCha20 with a 64 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
// - ChaCha20 with a 96 bit nonce (en/decrypt up to 2^32 * 64 bytes (~256 GB) for one key-nonce combination)
// - XChaCha20 with a 192 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
package chacha20 // import "github.com/aead/chacha20"
import (
"crypto/cipher"
"github.com/aead/chacha20/chacha"
)
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
// The length of the nonce determinds the version of ChaCha20:
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
// Src and dst may be the same slice but otherwise should not overlap.
// If len(dst) < len(src) this function panics.
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
func XORKeyStream(dst, src, nonce, key []byte) {
chacha.XORKeyStream(dst, src, nonce, key, 20)
}
// NewCipher returns a new cipher.Stream implementing a ChaCha20 version.
// The nonce must be unique for one key for all time.
// The length of the nonce determinds the version of ChaCha20:
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
func NewCipher(nonce, key []byte) (cipher.Stream, error) {
return chacha.NewCipher(nonce, key, 20)
}
MIT License
Copyright (c) 2017 ginuerzh
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# pht
Plain HTTP Tunnel - Tunnel over HTTP using only GET and POST requests, NO Websocket, NO CONNECT method.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -98,6 +98,9 @@ func (h *ClientHelloHandshake) ReadFrom(r io.Reader) (n int64, err error) { ...@@ -98,6 +98,9 @@ func (h *ClientHelloHandshake) ReadFrom(r io.Reader) (n int64, err error) {
// extLen := int(binary.BigEndian.Uint16(b[pos : pos+2])) // extLen := int(binary.BigEndian.Uint16(b[pos : pos+2]))
pos += 2 pos += 2
if pos >= len(b) {
return
}
br := bytes.NewReader(b[pos:]) br := bytes.NewReader(b[pos:])
for br.Len() > 0 { for br.Len() > 0 {
......
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016 Andreas Auernhammer Copyright (c) 2016 Sergey Kamardin
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
...@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ...@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment