Merge branch '2.6'
This commit is contained in:
commit
c84026fa30
2
.gitignore
vendored
2
.gitignore
vendored
@ -26,5 +26,5 @@ _testmain.go
|
||||
|
||||
*.bak
|
||||
|
||||
cmd/gost
|
||||
cmd/gost/gost
|
||||
snap
|
||||
|
@ -26,6 +26,8 @@ gost - GO Simple Tunnel
|
||||
* [支持SNI代理](https://docs.ginuerzh.xyz/gost/sni/)
|
||||
* [权限控制](https://docs.ginuerzh.xyz/gost/permission/)
|
||||
* [负载均衡](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/>
|
||||
|
||||
|
267
bypass.go
Normal file
267
bypass.go
Normal file
@ -0,0 +1,267 @@
|
||||
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()
|
||||
}
|
95
bypass_test.go
Normal file
95
bypass_test.go
Normal file
@ -0,0 +1,95 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
162
chain.go
162
chain.go
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-log/log"
|
||||
)
|
||||
@ -99,13 +100,22 @@ func (c *Chain) IsEmpty() bool {
|
||||
|
||||
// Dial connects to the target address addr through the chain.
|
||||
// If the chain is empty, it will use the net.Dial directly.
|
||||
func (c *Chain) Dial(addr string) (conn net.Conn, err error) {
|
||||
if c.IsEmpty() {
|
||||
return net.DialTimeout("tcp", addr, DialTimeout)
|
||||
func (c *Chain) Dial(addr string, opts ...ChainOption) (conn net.Conn, err error) {
|
||||
options := &ChainOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
for i := 0; i < c.Retries+1; i++ {
|
||||
conn, err = c.dial(addr)
|
||||
retries := 1
|
||||
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 {
|
||||
break
|
||||
}
|
||||
@ -113,12 +123,21 @@ func (c *Chain) Dial(addr string) (conn net.Conn, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Chain) dial(addr string) (net.Conn, error) {
|
||||
route, err := c.selectRoute()
|
||||
func (c *Chain) dialWithOptions(addr string, options *ChainOptions) (net.Conn, error) {
|
||||
if options == nil {
|
||||
options = &ChainOptions{}
|
||||
}
|
||||
route, err := c.selectRouteFor(addr)
|
||||
if err != nil {
|
||||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -132,10 +151,44 @@ func (c *Chain) dial(addr string) (net.Conn, error) {
|
||||
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.
|
||||
// If the chain is empty, it returns an ErrEmptyChain error.
|
||||
func (c *Chain) Conn() (conn net.Conn, err error) {
|
||||
for i := 0; i < c.Retries+1; i++ {
|
||||
func (c *Chain) Conn(opts ...ChainOption) (conn net.Conn, err error) {
|
||||
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
|
||||
route, err = c.selectRoute()
|
||||
if err != nil {
|
||||
@ -143,6 +196,7 @@ func (c *Chain) Conn() (conn net.Conn, err error) {
|
||||
}
|
||||
conn, err = route.getConn()
|
||||
if err != nil {
|
||||
log.Log(err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -151,6 +205,7 @@ func (c *Chain) Conn() (conn net.Conn, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// getConn obtains a connection to the last node of the chain.
|
||||
func (c *Chain) getConn() (conn net.Conn, err error) {
|
||||
if c.IsEmpty() {
|
||||
err = ErrEmptyChain
|
||||
@ -198,13 +253,12 @@ func (c *Chain) getConn() (conn net.Conn, err error) {
|
||||
}
|
||||
|
||||
func (c *Chain) selectRoute() (route *Chain, err error) {
|
||||
if c.isRoute {
|
||||
if c.IsEmpty() || c.isRoute {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
route = newRoute()
|
||||
route.Retries = c.Retries
|
||||
|
||||
for _, group := range c.nodeGroups {
|
||||
node, err := group.Next()
|
||||
@ -218,13 +272,97 @@ func (c *Chain) selectRoute() (route *Chain, err error) {
|
||||
ChainDialOption(route),
|
||||
)
|
||||
route = newRoute() // cutoff the chain for multiplex.
|
||||
route.Retries = c.Retries
|
||||
}
|
||||
|
||||
route.AddNode(node)
|
||||
}
|
||||
route.Retries = c.Retries
|
||||
|
||||
if Debug {
|
||||
log.Log("select route:", buf.String())
|
||||
}
|
||||
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 {
|
||||
return false
|
||||
}
|
||||
|
||||
// DialOptions describes the options for dialing.
|
||||
// DialOptions describes the options for Transporter.Dial.
|
||||
type DialOptions struct {
|
||||
Timeout time.Duration
|
||||
Chain *Chain
|
||||
|
12
cmd/gost/bypass.txt
Normal file
12
cmd/gost/bypass.txt
Normal file
@ -0,0 +1,12 @@
|
||||
# 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
|
291
cmd/gost/cfg.go
Normal file
291
cmd/gost/cfg.go
Normal file
@ -0,0 +1,291 @@
|
||||
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
|
||||
}
|
16
cmd/gost/dns.txt
Normal file
16
cmd/gost/dns.txt
Normal file
@ -0,0 +1,16 @@
|
||||
# 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
|
17
cmd/gost/hosts.txt
Normal file
17
cmd/gost/hosts.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# 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
|
389
cmd/gost/main.go
389
cmd/gost/main.go
@ -1,20 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
// _ "net/http/pprof"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gost"
|
||||
@ -64,15 +58,23 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// generate random self-signed certificate.
|
||||
cert, err := gost.GenCertificate()
|
||||
// go func() {
|
||||
// log.Log(http.ListenAndServe("localhost:6060", nil))
|
||||
// }()
|
||||
// NOTE: as of 2.6, you can use custom cert/key files to initialize the default certificate.
|
||||
config, err := tlsConfig(defaultCertFile, defaultKeyFile)
|
||||
if err != nil {
|
||||
log.Log(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
gost.DefaultTLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
// generate random self-signed certificate.
|
||||
cert, err := gost.GenCertificate()
|
||||
if err != nil {
|
||||
log.Log(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
config = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
}
|
||||
gost.DefaultTLSConfig = config
|
||||
|
||||
for _, route := range routes {
|
||||
if err := route.serve(); err != nil {
|
||||
@ -93,7 +95,6 @@ type route struct {
|
||||
func (r *route) initChain() (*gost.Chain, error) {
|
||||
chain := gost.NewChain()
|
||||
chain.Retries = r.Retries
|
||||
|
||||
gid := 1 // group ID
|
||||
|
||||
for _, ns := range r.ChainNodes {
|
||||
@ -121,12 +122,18 @@ func (r *route) initChain() (*gost.Chain, error) {
|
||||
log.Log(err)
|
||||
}
|
||||
peerCfg.Validate()
|
||||
|
||||
strategy := peerCfg.Strategy
|
||||
// overwrite the strategry in the peer config if `strategy` param exists.
|
||||
if s := nodes[0].Get("strategy"); s != "" {
|
||||
strategy = s
|
||||
}
|
||||
ngroup.Options = append(ngroup.Options,
|
||||
gost.WithFilter(&gost.FailFilter{
|
||||
MaxFails: peerCfg.MaxFails,
|
||||
FailTimeout: time.Duration(peerCfg.FailTimeout) * time.Second,
|
||||
}),
|
||||
gost.WithStrategy(parseStrategy(peerCfg.Strategy)),
|
||||
gost.WithStrategy(parseStrategy(strategy)),
|
||||
)
|
||||
|
||||
for _, s := range peerCfg.Nodes {
|
||||
@ -143,6 +150,18 @@ func (r *route) initChain() (*gost.Chain, error) {
|
||||
ngroup.AddNode(nodes...)
|
||||
}
|
||||
|
||||
var bypass *gost.Bypass
|
||||
// global bypass
|
||||
if peerCfg.Bypass != nil {
|
||||
bypass = gost.NewBypassPatterns(peerCfg.Bypass.Reverse, peerCfg.Bypass.Patterns...)
|
||||
}
|
||||
nodes = ngroup.Nodes()
|
||||
for i := range nodes {
|
||||
if nodes[i].Bypass == nil {
|
||||
nodes[i].Bypass = bypass // use global bypass if local bypass does not exist.
|
||||
}
|
||||
}
|
||||
|
||||
chain.AddNodeGroup(ngroup)
|
||||
}
|
||||
|
||||
@ -197,11 +216,6 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
|
||||
case "mwss":
|
||||
tr = gost.MWSSTransporter(wsOpts)
|
||||
case "kcp":
|
||||
/*
|
||||
if !chain.IsEmpty() {
|
||||
return nil, errors.New("KCP must be the first node in the proxy chain")
|
||||
}
|
||||
*/
|
||||
config, err := parseKCPConfig(node.Get("c"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -214,21 +228,15 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
|
||||
tr = gost.SSHTunnelTransporter()
|
||||
}
|
||||
case "quic":
|
||||
/*
|
||||
if !chain.IsEmpty() {
|
||||
return nil, errors.New("QUIC must be the first node in the proxy chain")
|
||||
}
|
||||
*/
|
||||
config := &gost.QUICConfig{
|
||||
TLSConfig: tlsCfg,
|
||||
KeepAlive: node.GetBool("keepalive"),
|
||||
TLSConfig: tlsCfg,
|
||||
KeepAlive: node.GetBool("keepalive"),
|
||||
Timeout: time.Duration(node.GetInt("timeout")) * time.Second,
|
||||
IdleTimeout: time.Duration(node.GetInt("idle")) * time.Second,
|
||||
}
|
||||
|
||||
config.Timeout = time.Duration(node.GetInt("timeout")) * time.Second
|
||||
config.IdleTimeout = time.Duration(node.GetInt("idle")) * time.Second
|
||||
|
||||
if key := node.Get("key"); key != "" {
|
||||
sum := sha256.Sum256([]byte(key))
|
||||
if cipher := node.Get("cipher"); cipher != "" {
|
||||
sum := sha256.Sum256([]byte(cipher))
|
||||
config.Key = sum[:]
|
||||
}
|
||||
|
||||
@ -241,9 +249,6 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
|
||||
tr = gost.H2CTransporter()
|
||||
|
||||
case "obfs4":
|
||||
if err := gost.Obfs4Init(node, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr = gost.Obfs4Transporter()
|
||||
case "ohttp":
|
||||
tr = gost.ObfsHTTPTransporter()
|
||||
@ -297,10 +302,14 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
|
||||
Transporter: tr,
|
||||
}
|
||||
|
||||
node.Bypass = parseBypass(node.Get("bypass"))
|
||||
|
||||
ips := parseIP(node.Get("ip"), sport)
|
||||
for _, ip := range ips {
|
||||
node.Addr = ip
|
||||
// override the default node address
|
||||
node.HandshakeOptions = append(handshakeOptions, gost.AddrHandshakeOption(ip))
|
||||
// One node per IP
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
if len(ips) == 0 {
|
||||
@ -308,6 +317,14 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
|
||||
nodes = []gost.Node{node}
|
||||
}
|
||||
|
||||
if node.Transport == "obfs4" {
|
||||
for i := range nodes {
|
||||
if err := gost.Obfs4Init(nodes[i], false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -373,14 +390,13 @@ func (r *route) serve() error {
|
||||
}
|
||||
case "quic":
|
||||
config := &gost.QUICConfig{
|
||||
TLSConfig: tlsCfg,
|
||||
KeepAlive: node.GetBool("keepalive"),
|
||||
TLSConfig: tlsCfg,
|
||||
KeepAlive: node.GetBool("keepalive"),
|
||||
Timeout: time.Duration(node.GetInt("timeout")) * time.Second,
|
||||
IdleTimeout: time.Duration(node.GetInt("idle")) * time.Second,
|
||||
}
|
||||
config.Timeout = time.Duration(node.GetInt("timeout")) * time.Second
|
||||
config.IdleTimeout = time.Duration(node.GetInt("idle")) * time.Second
|
||||
|
||||
if key := node.Get("key"); key != "" {
|
||||
sum := sha256.Sum256([]byte(key))
|
||||
if cipher := node.Get("cipher"); cipher != "" {
|
||||
sum := sha256.Sum256([]byte(cipher))
|
||||
config.Key = sum[:]
|
||||
}
|
||||
|
||||
@ -425,64 +441,76 @@ func (r *route) serve() error {
|
||||
return err
|
||||
}
|
||||
|
||||
var handler gost.Handler
|
||||
switch node.Protocol {
|
||||
case "http2":
|
||||
handler = gost.HTTP2Handler()
|
||||
case "socks", "socks5":
|
||||
handler = gost.SOCKS5Handler()
|
||||
case "socks4", "socks4a":
|
||||
handler = gost.SOCKS4Handler()
|
||||
case "ss":
|
||||
handler = gost.ShadowHandler()
|
||||
case "http":
|
||||
handler = gost.HTTPHandler()
|
||||
case "tcp":
|
||||
handler = gost.TCPDirectForwardHandler(node.Remote)
|
||||
case "rtcp":
|
||||
handler = gost.TCPRemoteForwardHandler(node.Remote)
|
||||
case "udp":
|
||||
handler = gost.UDPDirectForwardHandler(node.Remote)
|
||||
case "rudp":
|
||||
handler = gost.UDPRemoteForwardHandler(node.Remote)
|
||||
case "forward":
|
||||
handler = gost.SSHForwardHandler()
|
||||
case "redirect":
|
||||
handler = gost.TCPRedirectHandler()
|
||||
case "ssu":
|
||||
handler = gost.ShadowUDPdHandler()
|
||||
case "sni":
|
||||
handler = gost.SNIHandler()
|
||||
default:
|
||||
// start from 2.5, if remote is not empty, then we assume that it is a forward tunnel.
|
||||
if node.Remote != "" {
|
||||
handler = gost.TCPDirectForwardHandler(node.Remote)
|
||||
} else {
|
||||
handler = gost.AutoHandler()
|
||||
}
|
||||
}
|
||||
|
||||
var whitelist, blacklist *gost.Permissions
|
||||
if node.Values.Get("whitelist") != "" {
|
||||
if whitelist, err = gost.ParsePermissions(node.Values.Get("whitelist")); err != nil {
|
||||
if whitelist, err = gost.ParsePermissions(node.Get("whitelist")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if node.Values.Get("blacklist") != "" {
|
||||
if blacklist, err = gost.ParsePermissions(node.Values.Get("blacklist")); err != nil {
|
||||
if blacklist, err = gost.ParsePermissions(node.Get("blacklist")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var handlerOptions []gost.HandlerOption
|
||||
var hosts *gost.Hosts
|
||||
if f, _ := os.Open(node.Get("hosts")); f != nil {
|
||||
f.Close()
|
||||
hosts = gost.NewHosts()
|
||||
go gost.PeriodReload(hosts, node.Get("hosts"))
|
||||
}
|
||||
|
||||
handlerOptions = append(handlerOptions,
|
||||
handler.Init(
|
||||
gost.AddrHandlerOption(node.Addr),
|
||||
gost.ChainHandlerOption(chain),
|
||||
gost.UsersHandlerOption(users...),
|
||||
gost.TLSConfigHandlerOption(tlsCfg),
|
||||
gost.WhitelistHandlerOption(whitelist),
|
||||
gost.BlacklistHandlerOption(blacklist),
|
||||
gost.BypassHandlerOption(parseBypass(node.Get("bypass"))),
|
||||
gost.StrategyHandlerOption(parseStrategy(node.Get("strategy"))),
|
||||
gost.ResolverHandlerOption(parseResolver(node.Get("dns"))),
|
||||
gost.HostsHandlerOption(hosts),
|
||||
gost.RetryHandlerOption(node.GetInt("retry")),
|
||||
gost.TimeoutHandlerOption(time.Duration(node.GetInt("timeout"))*time.Second),
|
||||
)
|
||||
var handler gost.Handler
|
||||
switch node.Protocol {
|
||||
case "http2":
|
||||
handler = gost.HTTP2Handler(handlerOptions...)
|
||||
case "socks", "socks5":
|
||||
handler = gost.SOCKS5Handler(handlerOptions...)
|
||||
case "socks4", "socks4a":
|
||||
handler = gost.SOCKS4Handler(handlerOptions...)
|
||||
case "ss":
|
||||
handler = gost.ShadowHandler(handlerOptions...)
|
||||
case "http":
|
||||
handler = gost.HTTPHandler(handlerOptions...)
|
||||
case "tcp":
|
||||
handler = gost.TCPDirectForwardHandler(node.Remote, handlerOptions...)
|
||||
case "rtcp":
|
||||
handler = gost.TCPRemoteForwardHandler(node.Remote, handlerOptions...)
|
||||
case "udp":
|
||||
handler = gost.UDPDirectForwardHandler(node.Remote, handlerOptions...)
|
||||
case "rudp":
|
||||
handler = gost.UDPRemoteForwardHandler(node.Remote, handlerOptions...)
|
||||
case "forward":
|
||||
handler = gost.SSHForwardHandler(handlerOptions...)
|
||||
case "redirect":
|
||||
handler = gost.TCPRedirectHandler(handlerOptions...)
|
||||
case "ssu":
|
||||
handler = gost.ShadowUDPdHandler(handlerOptions...)
|
||||
case "sni":
|
||||
handler = gost.SNIHandler(handlerOptions...)
|
||||
default:
|
||||
// start from 2.5, if remote is not empty, then we assume that it is a forward tunnel.
|
||||
if node.Remote != "" {
|
||||
handler = gost.TCPDirectForwardHandler(node.Remote, handlerOptions...)
|
||||
} else {
|
||||
handler = gost.AutoHandler(handlerOptions...)
|
||||
}
|
||||
}
|
||||
|
||||
srv := &gost.Server{Listener: ln}
|
||||
go srv.Serve(handler)
|
||||
@ -490,198 +518,3 @@ func (r *route) serve() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 = "cert.pem"
|
||||
}
|
||||
if keyFile == "" {
|
||||
keyFile = "key.pem"
|
||||
}
|
||||
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"`
|
||||
}
|
||||
|
||||
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{}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -6,5 +6,13 @@
|
||||
"socks5://:1081",
|
||||
"socks://:1082",
|
||||
"socks4a://:1083"
|
||||
]
|
||||
],
|
||||
"bypass":{
|
||||
"reverse": false,
|
||||
"patterns": [
|
||||
"10.0.0.1",
|
||||
"192.168.0.0/24",
|
||||
"*.example.com"
|
||||
]
|
||||
}
|
||||
}
|
48
cmd/gost/ssl/README.md
Normal file
48
cmd/gost/ssl/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
[//]: <> (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
|
||||
```
|
23
cmd/gost/ssl/localhost.crt
Normal file
23
cmd/gost/ssl/localhost.crt
Normal file
@ -0,0 +1,23 @@
|
||||
-----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-----
|
16
cmd/gost/ssl/localhost.csr
Normal file
16
cmd/gost/ssl/localhost.csr
Normal file
@ -0,0 +1,16 @@
|
||||
-----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-----
|
27
cmd/gost/ssl/localhost.key
Normal file
27
cmd/gost/ssl/localhost.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
27
cmd/gost/ssl/rootCA.crt
Normal file
27
cmd/gost/ssl/rootCA.crt
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
51
cmd/gost/ssl/rootCA.key
Normal file
51
cmd/gost/ssl/rootCA.key
Normal file
@ -0,0 +1,51 @@
|
||||
-----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-----
|
1
cmd/gost/ssl/rootCA.srl
Normal file
1
cmd/gost/ssl/rootCA.srl
Normal file
@ -0,0 +1 @@
|
||||
B45E3577C258EF9E
|
287
forward.go
287
forward.go
@ -3,6 +3,7 @@ package gost
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -27,159 +28,357 @@ func (c *forwardConnector) Connect(conn net.Conn, addr string) (net.Conn, error)
|
||||
|
||||
type tcpDirectForwardHandler struct {
|
||||
raddr string
|
||||
group *NodeGroup
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// TCPDirectForwardHandler creates a server Handler for TCP port forwarding server.
|
||||
// The raddr is the remote address that the server will forward to.
|
||||
// NOTE: as of 2.6, remote address can be a comma-separated address list.
|
||||
func TCPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
||||
if raddr == "" {
|
||||
raddr = "0.0.0.0:0"
|
||||
}
|
||||
h := &tcpDirectForwardHandler{
|
||||
raddr: raddr,
|
||||
options: &HandlerOptions{},
|
||||
raddr: raddr,
|
||||
group: NewNodeGroup(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
|
||||
for i, addr := range strings.Split(raddr, ",") {
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
// We treat the remote target server as a node, so we can put them in a group,
|
||||
// and perform the node selection for load balancing.
|
||||
h.group.AddNode(Node{
|
||||
ID: i + 1,
|
||||
Addr: addr,
|
||||
Host: addr,
|
||||
})
|
||||
}
|
||||
h.Init(opts...)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *tcpDirectForwardHandler) Init(options ...HandlerOption) {
|
||||
if h.options == nil {
|
||||
h.options = &HandlerOptions{}
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(h.options)
|
||||
}
|
||||
return h
|
||||
|
||||
h.group.SetSelector(&defaultSelector{},
|
||||
WithStrategy(h.options.Strategy),
|
||||
WithFilter(&FailFilter{
|
||||
MaxFails: 1,
|
||||
FailTimeout: 30 * time.Second,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (h *tcpDirectForwardHandler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
log.Logf("[tcp] %s - %s", conn.RemoteAddr(), h.raddr)
|
||||
cc, err := h.options.Chain.Dial(h.raddr)
|
||||
retries := 1
|
||||
if h.options.Chain != nil && h.options.Chain.Retries > 0 {
|
||||
retries = h.options.Chain.Retries
|
||||
}
|
||||
if h.options.Retries > 0 {
|
||||
retries = h.options.Retries
|
||||
}
|
||||
|
||||
var cc net.Conn
|
||||
var node Node
|
||||
var err error
|
||||
for i := 0; i < retries; i++ {
|
||||
node, err = h.group.Next()
|
||||
if err != nil {
|
||||
log.Logf("[tcp] %s - %s : %s", conn.RemoteAddr(), h.raddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Logf("[tcp] %s - %s", conn.RemoteAddr(), node.Addr)
|
||||
cc, err = h.options.Chain.Dial(node.Addr,
|
||||
RetryChainOption(h.options.Retries),
|
||||
TimeoutChainOption(h.options.Timeout),
|
||||
)
|
||||
if err != nil {
|
||||
log.Logf("[tcp] %s -> %s : %s", conn.RemoteAddr(), node.Addr, err)
|
||||
node.MarkDead()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Logf("[tcp] %s -> %s : %s", conn.RemoteAddr(), h.raddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
node.ResetDead()
|
||||
defer cc.Close()
|
||||
|
||||
log.Logf("[tcp] %s <-> %s", conn.RemoteAddr(), h.raddr)
|
||||
log.Logf("[tcp] %s <-> %s", conn.RemoteAddr(), node.Addr)
|
||||
transport(conn, cc)
|
||||
log.Logf("[tcp] %s >-< %s", conn.RemoteAddr(), h.raddr)
|
||||
log.Logf("[tcp] %s >-< %s", conn.RemoteAddr(), node.Addr)
|
||||
}
|
||||
|
||||
type udpDirectForwardHandler struct {
|
||||
raddr string
|
||||
group *NodeGroup
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// UDPDirectForwardHandler creates a server Handler for UDP port forwarding server.
|
||||
// The raddr is the remote address that the server will forward to.
|
||||
// NOTE: as of 2.6, remote address can be a comma-separated address list.
|
||||
func UDPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
||||
h := &udpDirectForwardHandler{
|
||||
raddr: raddr,
|
||||
options: &HandlerOptions{},
|
||||
raddr: raddr,
|
||||
group: NewNodeGroup(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
|
||||
for i, addr := range strings.Split(raddr, ",") {
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
// We treat the remote target server as a node, so we can put them in a group,
|
||||
// and perform the node selection for load balancing.
|
||||
h.group.AddNode(Node{
|
||||
ID: i + 1,
|
||||
Addr: addr,
|
||||
Host: addr,
|
||||
})
|
||||
}
|
||||
|
||||
h.Init(opts...)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *udpDirectForwardHandler) Init(options ...HandlerOption) {
|
||||
if h.options == nil {
|
||||
h.options = &HandlerOptions{}
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(h.options)
|
||||
}
|
||||
return h
|
||||
|
||||
h.group.SetSelector(&defaultSelector{},
|
||||
WithStrategy(h.options.Strategy),
|
||||
WithFilter(&FailFilter{
|
||||
MaxFails: 1,
|
||||
FailTimeout: 30 * time.Second,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (h *udpDirectForwardHandler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
node, err := h.group.Next()
|
||||
if err != nil {
|
||||
log.Logf("[udp] %s - %s : %s", conn.RemoteAddr(), h.raddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
var cc net.Conn
|
||||
if h.options.Chain.IsEmpty() {
|
||||
raddr, err := net.ResolveUDPAddr("udp", h.raddr)
|
||||
raddr, err := net.ResolveUDPAddr("udp", node.Addr)
|
||||
if err != nil {
|
||||
log.Logf("[udp] %s - %s : %s", conn.LocalAddr(), h.raddr, err)
|
||||
node.MarkDead()
|
||||
log.Logf("[udp] %s - %s : %s", conn.LocalAddr(), node.Addr, err)
|
||||
return
|
||||
}
|
||||
cc, err = net.DialUDP("udp", nil, raddr)
|
||||
if err != nil {
|
||||
log.Logf("[udp] %s - %s : %s", conn.LocalAddr(), h.raddr, err)
|
||||
node.MarkDead()
|
||||
log.Logf("[udp] %s - %s : %s", conn.LocalAddr(), node.Addr, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
cc, err = getSOCKS5UDPTunnel(h.options.Chain, nil)
|
||||
if err != nil {
|
||||
log.Logf("[udp] %s - %s : %s", conn.LocalAddr(), h.raddr, err)
|
||||
log.Logf("[udp] %s - %s : %s", conn.LocalAddr(), node.Addr, err)
|
||||
return
|
||||
}
|
||||
cc = &udpTunnelConn{Conn: cc, raddr: h.raddr}
|
||||
cc = &udpTunnelConn{Conn: cc, raddr: node.Addr}
|
||||
}
|
||||
|
||||
defer cc.Close()
|
||||
node.ResetDead()
|
||||
|
||||
log.Logf("[udp] %s <-> %s", conn.RemoteAddr(), h.raddr)
|
||||
log.Logf("[udp] %s <-> %s", conn.RemoteAddr(), node.Addr)
|
||||
transport(conn, cc)
|
||||
log.Logf("[udp] %s >-< %s", conn.RemoteAddr(), h.raddr)
|
||||
log.Logf("[udp] %s >-< %s", conn.RemoteAddr(), node.Addr)
|
||||
}
|
||||
|
||||
type tcpRemoteForwardHandler struct {
|
||||
raddr string
|
||||
group *NodeGroup
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// TCPRemoteForwardHandler creates a server Handler for TCP remote port forwarding server.
|
||||
// The raddr is the remote address that the server will forward to.
|
||||
// NOTE: as of 2.6, remote address can be a comma-separated address list.
|
||||
func TCPRemoteForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
||||
h := &tcpRemoteForwardHandler{
|
||||
raddr: raddr,
|
||||
options: &HandlerOptions{},
|
||||
raddr: raddr,
|
||||
group: NewNodeGroup(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
|
||||
for i, addr := range strings.Split(raddr, ",") {
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
// We treat the remote target server as a node, so we can put them in a group,
|
||||
// and perform the node selection for load balancing.
|
||||
h.group.AddNode(Node{
|
||||
ID: i + 1,
|
||||
Addr: addr,
|
||||
Host: addr,
|
||||
})
|
||||
}
|
||||
h.Init(opts...)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *tcpRemoteForwardHandler) Init(options ...HandlerOption) {
|
||||
if h.options == nil {
|
||||
h.options = &HandlerOptions{}
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(h.options)
|
||||
}
|
||||
return h
|
||||
|
||||
h.group.SetSelector(&defaultSelector{},
|
||||
WithStrategy(h.options.Strategy),
|
||||
WithFilter(&FailFilter{
|
||||
MaxFails: 1,
|
||||
FailTimeout: 30 * time.Second,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (h *tcpRemoteForwardHandler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
cc, err := net.DialTimeout("tcp", h.raddr, DialTimeout)
|
||||
retries := 1
|
||||
if h.options.Chain != nil && h.options.Chain.Retries > 0 {
|
||||
retries = h.options.Chain.Retries
|
||||
}
|
||||
if h.options.Retries > 0 {
|
||||
retries = h.options.Retries
|
||||
}
|
||||
|
||||
var cc net.Conn
|
||||
var node Node
|
||||
var err error
|
||||
for i := 0; i < retries; i++ {
|
||||
node, err = h.group.Next()
|
||||
if err != nil {
|
||||
log.Logf("[rtcp] %s - %s : %s", conn.LocalAddr(), h.raddr, err)
|
||||
return
|
||||
}
|
||||
cc, err = net.DialTimeout("tcp", node.Addr, h.options.Timeout)
|
||||
if err != nil {
|
||||
log.Logf("[rtcp] %s -> %s : %s", conn.LocalAddr(), node.Addr, err)
|
||||
node.MarkDead()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Logf("[rtcp] %s -> %s : %s", conn.LocalAddr(), h.raddr, err)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
log.Logf("[rtcp] %s <-> %s", conn.LocalAddr(), h.raddr)
|
||||
defer cc.Close()
|
||||
node.ResetDead()
|
||||
|
||||
log.Logf("[rtcp] %s <-> %s", conn.LocalAddr(), node.Addr)
|
||||
transport(cc, conn)
|
||||
log.Logf("[rtcp] %s >-< %s", conn.LocalAddr(), h.raddr)
|
||||
log.Logf("[rtcp] %s >-< %s", conn.LocalAddr(), node.Addr)
|
||||
}
|
||||
|
||||
type udpRemoteForwardHandler struct {
|
||||
raddr string
|
||||
group *NodeGroup
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// UDPRemoteForwardHandler creates a server Handler for UDP remote port forwarding server.
|
||||
// The raddr is the remote address that the server will forward to.
|
||||
// NOTE: as of 2.6, remote address can be a comma-separated address list.
|
||||
func UDPRemoteForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
||||
h := &udpRemoteForwardHandler{
|
||||
raddr: raddr,
|
||||
options: &HandlerOptions{},
|
||||
raddr: raddr,
|
||||
group: NewNodeGroup(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
|
||||
for i, addr := range strings.Split(raddr, ",") {
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
// We treat the remote target server as a node, so we can put them in a group,
|
||||
// and perform the node selection for load balancing.
|
||||
h.group.AddNode(Node{
|
||||
ID: i + 1,
|
||||
Addr: addr,
|
||||
Host: addr,
|
||||
})
|
||||
}
|
||||
|
||||
h.Init(opts...)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *udpRemoteForwardHandler) Init(options ...HandlerOption) {
|
||||
if h.options == nil {
|
||||
h.options = &HandlerOptions{}
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(h.options)
|
||||
}
|
||||
return h
|
||||
h.group.SetSelector(&defaultSelector{},
|
||||
WithStrategy(h.options.Strategy),
|
||||
WithFilter(&FailFilter{
|
||||
MaxFails: 1,
|
||||
FailTimeout: 30 * time.Second,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (h *udpRemoteForwardHandler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp", h.raddr)
|
||||
if err != nil {
|
||||
log.Logf("[rudp] %s - %s : %s", conn.RemoteAddr(), h.raddr, err)
|
||||
return
|
||||
}
|
||||
cc, err := net.DialUDP("udp", nil, raddr)
|
||||
node, err := h.group.Next()
|
||||
if err != nil {
|
||||
log.Logf("[rudp] %s - %s : %s", conn.RemoteAddr(), h.raddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Logf("[rudp] %s <-> %s", conn.RemoteAddr(), h.raddr)
|
||||
raddr, err := net.ResolveUDPAddr("udp", node.Addr)
|
||||
if err != nil {
|
||||
node.MarkDead()
|
||||
log.Logf("[rudp] %s - %s : %s", conn.RemoteAddr(), node.Addr, err)
|
||||
return
|
||||
}
|
||||
cc, err := net.DialUDP("udp", nil, raddr)
|
||||
if err != nil {
|
||||
node.MarkDead()
|
||||
log.Logf("[rudp] %s - %s : %s", conn.RemoteAddr(), node.Addr, err)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
node.ResetDead()
|
||||
|
||||
log.Logf("[rudp] %s <-> %s", conn.RemoteAddr(), node.Addr)
|
||||
transport(conn, cc)
|
||||
log.Logf("[rudp] %s >-< %s", conn.RemoteAddr(), h.raddr)
|
||||
log.Logf("[rudp] %s >-< %s", conn.RemoteAddr(), node.Addr)
|
||||
}
|
||||
|
||||
type udpDirectForwardListener struct {
|
||||
|
10
gost.go
10
gost.go
@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// Version is the gost version.
|
||||
const Version = "2.5"
|
||||
const Version = "2.6-rc1"
|
||||
|
||||
// Debug is a flag that enables the debug log.
|
||||
var Debug bool
|
||||
@ -44,19 +44,19 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTLSConfig is a default TLS config for internal use
|
||||
// DefaultTLSConfig is a default TLS config for internal use.
|
||||
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"
|
||||
)
|
||||
|
||||
// SetLogger sets a new logger for internal log system
|
||||
// SetLogger sets a new logger for internal log system.
|
||||
func SetLogger(logger log.Logger) {
|
||||
log.DefaultLogger = logger
|
||||
}
|
||||
|
||||
// GenCertificate generates a random TLS certificate
|
||||
// GenCertificate generates a random TLS certificate.
|
||||
func GenCertificate() (cert tls.Certificate, err error) {
|
||||
rawCert, rawKey, err := generateKeyPair()
|
||||
if err != nil {
|
||||
|
84
handler.go
84
handler.go
@ -5,6 +5,7 @@ import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
@ -13,6 +14,7 @@ import (
|
||||
|
||||
// Handler is a proxy server handler
|
||||
type Handler interface {
|
||||
Init(options ...HandlerOption)
|
||||
Handle(net.Conn)
|
||||
}
|
||||
|
||||
@ -24,6 +26,12 @@ type HandlerOptions struct {
|
||||
TLSConfig *tls.Config
|
||||
Whitelist *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.
|
||||
@ -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 {
|
||||
options []HandlerOption
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// AutoHandler creates a server Handler for auto proxy server.
|
||||
func AutoHandler(opts ...HandlerOption) Handler {
|
||||
h := &autoHandler{
|
||||
options: opts,
|
||||
}
|
||||
h := &autoHandler{}
|
||||
h.Init(opts...)
|
||||
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) {
|
||||
br := bufio.NewReader(conn)
|
||||
b, err := br.Peek(1)
|
||||
@ -93,25 +151,23 @@ func (h *autoHandler) Handle(conn net.Conn) {
|
||||
}
|
||||
|
||||
cc := &bufferdConn{Conn: conn, br: br}
|
||||
var handler Handler
|
||||
switch b[0] {
|
||||
case gosocks4.Ver4:
|
||||
options := &HandlerOptions{}
|
||||
for _, opt := range h.options {
|
||||
opt(options)
|
||||
}
|
||||
// SOCKS4(a) does not suppport authentication method,
|
||||
// so we ignore it when credentials are specified for security reason.
|
||||
if len(options.Users) > 0 {
|
||||
if len(h.options.Users) > 0 {
|
||||
cc.Close()
|
||||
return
|
||||
}
|
||||
h := &socks4Handler{options}
|
||||
h.Handle(cc)
|
||||
case gosocks5.Ver5:
|
||||
SOCKS5Handler(h.options...).Handle(cc)
|
||||
handler = &socks4Handler{options: h.options}
|
||||
case gosocks5.Ver5: // socks5
|
||||
handler = &socks5Handler{options: h.options}
|
||||
default: // http
|
||||
HTTPHandler(h.options...).Handle(cc)
|
||||
handler = &httpHandler{options: h.options}
|
||||
}
|
||||
handler.Init()
|
||||
handler.Handle(cc)
|
||||
}
|
||||
|
||||
type bufferdConn struct {
|
||||
|
120
hosts.go
Normal file
120
hosts.go
Normal file
@ -0,0 +1,120 @@
|
||||
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
|
||||
}
|
124
http.go
124
http.go
@ -75,13 +75,18 @@ type httpHandler struct {
|
||||
|
||||
// HTTPHandler creates a server Handler for HTTP proxy server.
|
||||
func HTTPHandler(opts ...HandlerOption) Handler {
|
||||
h := &httpHandler{
|
||||
options: &HandlerOptions{},
|
||||
h := &httpHandler{}
|
||||
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)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *httpHandler) Handle(conn net.Conn) {
|
||||
@ -116,6 +121,13 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
||||
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) {
|
||||
log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
|
||||
b := []byte("HTTP/1.1 403 Forbidden\r\n" +
|
||||
@ -127,6 +139,17 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
||||
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"))
|
||||
if Debug && (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) {
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
// req.Header.Del("Proxy-Connection")
|
||||
|
||||
// try to get the actual host.
|
||||
if v := req.Header.Get("Gost-Target"); v != "" {
|
||||
if host, err := decodeServerName(v); err == nil {
|
||||
req.Host = host
|
||||
host := req.Host
|
||||
if _, port, _ := net.SplitHostPort(host); port == "" {
|
||||
host = net.JoinHostPort(req.Host, "80")
|
||||
}
|
||||
|
||||
retries := 1
|
||||
if h.options.Chain != nil && h.options.Chain.Retries > 0 {
|
||||
retries = h.options.Chain.Retries
|
||||
}
|
||||
if h.options.Retries > 0 {
|
||||
retries = h.options.Retries
|
||||
}
|
||||
|
||||
var err error
|
||||
var cc net.Conn
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// forward http request
|
||||
lastNode := h.options.Chain.LastNode()
|
||||
if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
|
||||
h.forwardRequest(conn, req)
|
||||
return
|
||||
}
|
||||
|
||||
host := req.Host
|
||||
if !strings.Contains(host, ":") {
|
||||
host += ":80"
|
||||
}
|
||||
|
||||
cc, err := h.options.Chain.Dial(host)
|
||||
if err != nil {
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
|
||||
|
||||
@ -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)
|
||||
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) {
|
||||
if h.options.Chain.IsEmpty() {
|
||||
return
|
||||
func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Chain) error {
|
||||
if route.IsEmpty() {
|
||||
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 {
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, 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
|
||||
return err
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
@ -232,14 +278,14 @@ func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
|
||||
}
|
||||
if err = req.WriteProxy(cc); err != nil {
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
cc.SetWriteDeadline(time.Time{})
|
||||
|
||||
log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
|
||||
transport(conn, cc)
|
||||
log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
|
||||
|
34
http2.go
34
http2.go
@ -114,7 +114,7 @@ func (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wrapTLSClient(conn, cfg)
|
||||
return wrapTLSClient(conn, cfg, opts.Timeout)
|
||||
},
|
||||
}
|
||||
client = &http.Client{
|
||||
@ -182,7 +182,7 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err
|
||||
if tr.tlsConfig == nil {
|
||||
return conn, nil
|
||||
}
|
||||
return wrapTLSClient(conn, cfg)
|
||||
return wrapTLSClient(conn, cfg, opts.Timeout)
|
||||
},
|
||||
}
|
||||
client = &http.Client{
|
||||
@ -250,16 +250,21 @@ type http2Handler struct {
|
||||
|
||||
// HTTP2Handler creates a server Handler for HTTP2 proxy server.
|
||||
func HTTP2Handler(opts ...HandlerOption) Handler {
|
||||
h := &http2Handler{
|
||||
options: new(HandlerOptions),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(h.options)
|
||||
}
|
||||
h := &http2Handler{}
|
||||
h.Init(opts...)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *http2Handler) Init(options ...HandlerOption) {
|
||||
if h.options == nil {
|
||||
h.options = &HandlerOptions{}
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(h.options)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *http2Handler) Handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
@ -296,6 +301,12 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
|
||||
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"))
|
||||
if Debug && (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) {
|
||||
r.Header.Del("Proxy-Authorization")
|
||||
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 {
|
||||
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
11
node.go
11
node.go
@ -25,6 +25,7 @@ type Node struct {
|
||||
group *NodeGroup
|
||||
failCount uint32
|
||||
failTime int64
|
||||
Bypass *Bypass
|
||||
}
|
||||
|
||||
// ParseNode parses the node info.
|
||||
@ -139,6 +140,7 @@ func (node *Node) Clone() Node {
|
||||
group: node.group,
|
||||
failCount: atomic.LoadUint32(&node.failCount),
|
||||
failTime: atomic.LoadInt64(&node.failTime),
|
||||
Bypass: node.Bypass,
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,6 +188,15 @@ func (group *NodeGroup) AddNode(node ...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
|
||||
func (group *NodeGroup) Nodes() []Node {
|
||||
if group == nil {
|
||||
|
@ -35,7 +35,7 @@ func TestParseNode(t *testing.T) {
|
||||
actual, err := ParseNode(test.in)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
t.Errorf("ParseNode(%q) got error: %v", test.in, err)
|
||||
|
2
quic.go
2
quic.go
@ -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)
|
||||
if err != nil {
|
||||
log.Log("quic dial", err)
|
||||
log.Log("quic dial:", err)
|
||||
return nil, err
|
||||
}
|
||||
return &quicSession{conn: conn, session: session}, nil
|
||||
|
22
redirect.go
22
redirect.go
@ -17,15 +17,20 @@ type tcpRedirectHandler struct {
|
||||
|
||||
// TCPRedirectHandler creates a server Handler for TCP redirect server.
|
||||
func TCPRedirectHandler(opts ...HandlerOption) Handler {
|
||||
h := &tcpRedirectHandler{
|
||||
options: &HandlerOptions{
|
||||
Chain: new(Chain),
|
||||
},
|
||||
h := &tcpRedirectHandler{}
|
||||
h.Init(opts...)
|
||||
|
||||
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)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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 {
|
||||
log.Logf("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err)
|
||||
return
|
||||
|
@ -25,6 +25,10 @@ func TCPRedirectHandler(opts ...HandlerOption) Handler {
|
||||
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) {
|
||||
log.Log("[red-tcp] TCP redirect is not available on the Windows platform")
|
||||
c.Close()
|
||||
|
52
reload.go
Normal file
52
reload.go
Normal file
@ -0,0 +1,52 @@
|
||||
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)
|
||||
}
|
||||
}
|
280
resolver.go
Normal file
280
resolver.go
Normal file
@ -0,0 +1,280 @@
|
||||
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()
|
||||
}
|
55
server.go
55
server.go
@ -3,6 +3,7 @@ package gost
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-log/log"
|
||||
@ -11,6 +12,17 @@ import (
|
||||
// Server is a proxy server.
|
||||
type Server struct {
|
||||
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
|
||||
@ -24,7 +36,9 @@ func (s *Server) Close() error {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ln, err := TCPListener("")
|
||||
if err != nil {
|
||||
@ -57,9 +71,30 @@ func (s *Server) Serve(h Handler) error {
|
||||
return e
|
||||
}
|
||||
tempDelay = 0
|
||||
|
||||
if s.options.Bypass.Contains(conn.RemoteAddr().String()) {
|
||||
log.Log("[bypass]", conn.RemoteAddr())
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
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.
|
||||
@ -98,15 +133,29 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
var (
|
||||
trPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 32*1024)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func transport(rw1, rw2 io.ReadWriter) error {
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := io.Copy(rw1, rw2)
|
||||
buf := trPool.Get().([]byte)
|
||||
defer trPool.Put(buf)
|
||||
|
||||
_, err := io.CopyBuffer(rw1, rw2, buf)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(rw2, rw1)
|
||||
buf := trPool.Get().([]byte)
|
||||
defer trPool.Put(buf)
|
||||
|
||||
_, err := io.CopyBuffer(rw2, rw1, buf)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: gost
|
||||
version: '2.5'
|
||||
version: '2.6'
|
||||
summary: GO Simple Tunnel
|
||||
description: |
|
||||
A simple tunnel written in golang
|
||||
|
44
sni.go
44
sni.go
@ -33,17 +33,27 @@ func (c *sniConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
|
||||
}
|
||||
|
||||
type sniHandler struct {
|
||||
options []HandlerOption
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
||||
// SNIHandler creates a server Handler for SNI proxy server.
|
||||
func SNIHandler(opts ...HandlerOption) Handler {
|
||||
h := &sniHandler{
|
||||
options: opts,
|
||||
}
|
||||
h := &sniHandler{}
|
||||
h.Init(opts...)
|
||||
|
||||
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) {
|
||||
br := bufio.NewReader(conn)
|
||||
|
||||
@ -66,7 +76,9 @@ func (h *sniHandler) Handle(conn net.Conn) {
|
||||
if !req.URL.IsAbs() {
|
||||
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
|
||||
}
|
||||
|
||||
@ -76,19 +88,25 @@ func (h *sniHandler) Handle(conn net.Conn) {
|
||||
return
|
||||
}
|
||||
|
||||
options := &HandlerOptions{}
|
||||
for _, opt := range h.options {
|
||||
opt(options)
|
||||
}
|
||||
addr := net.JoinHostPort(host, "443")
|
||||
|
||||
if !Can("tcp", host, options.Whitelist, options.Blacklist) {
|
||||
log.Logf("[sni] Unauthorized to tcp connect to %s", host)
|
||||
if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) {
|
||||
log.Logf("[sni] Unauthorized to tcp connect to %s", addr)
|
||||
return
|
||||
}
|
||||
if h.options.Bypass.Contains(addr) {
|
||||
log.Log("[sni] [bypass]", addr)
|
||||
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 {
|
||||
log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), host, err)
|
||||
log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), addr, err)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
105
socks.go
105
socks.go
@ -267,7 +267,7 @@ func (c *socks4Connector) Connect(conn net.Conn, addr string) (net.Conn, error)
|
||||
return nil, err
|
||||
}
|
||||
if len(taddr.IP) == 0 {
|
||||
taddr.IP = net.IPv4(0, 0, 0, 0)
|
||||
taddr.IP = net.IPv4zero
|
||||
}
|
||||
|
||||
req := gosocks4.NewRequest(gosocks4.CmdConnect,
|
||||
@ -348,30 +348,36 @@ type socks5Handler struct {
|
||||
|
||||
// SOCKS5Handler creates a server Handler for SOCKS5 proxy server.
|
||||
func SOCKS5Handler(opts ...HandlerOption) Handler {
|
||||
options := &HandlerOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
h := &socks5Handler{}
|
||||
h.Init(opts...)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *socks5Handler) Init(options ...HandlerOption) {
|
||||
if h.options == nil {
|
||||
h.options = &HandlerOptions{}
|
||||
}
|
||||
|
||||
tlsConfig := options.TLSConfig
|
||||
for _, opt := range options {
|
||||
opt(h.options)
|
||||
}
|
||||
|
||||
tlsConfig := h.options.TLSConfig
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = DefaultTLSConfig
|
||||
}
|
||||
selector := &serverSelector{ // socks5 server selector
|
||||
Users: options.Users,
|
||||
h.selector = &serverSelector{ // socks5 server selector
|
||||
Users: h.options.Users,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
// methods that socks5 server supported
|
||||
selector.AddMethod(
|
||||
h.selector.AddMethod(
|
||||
gosocks5.MethodNoAuth,
|
||||
gosocks5.MethodUserPass,
|
||||
MethodTLS,
|
||||
MethodTLSAuth,
|
||||
)
|
||||
return &socks5Handler{
|
||||
options: options,
|
||||
selector: selector,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *socks5Handler) Handle(conn net.Conn) {
|
||||
@ -419,8 +425,22 @@ func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) {
|
||||
}
|
||||
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 {
|
||||
log.Logf("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
|
||||
@ -716,6 +736,10 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
|
||||
if err != nil {
|
||||
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 {
|
||||
errc <- err
|
||||
return
|
||||
@ -738,6 +762,10 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
|
||||
if clientAddr == nil {
|
||||
continue
|
||||
}
|
||||
if h.options.Bypass.Contains(raddr.String()) {
|
||||
log.Log("[socks5-udp] [bypass] read from", raddr)
|
||||
continue // bypass
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n])
|
||||
dgram.Write(&buf)
|
||||
@ -785,6 +813,11 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
|
||||
if clientAddr == nil {
|
||||
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))
|
||||
if err := dgram.Write(cc); err != nil {
|
||||
errc <- err
|
||||
@ -809,6 +842,11 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
|
||||
if clientAddr == nil {
|
||||
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
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
@ -903,6 +941,10 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
if h.options.Bypass.Contains(addr.String()) {
|
||||
log.Log("[udp-tun] [bypass] read from", addr)
|
||||
continue // bypass
|
||||
}
|
||||
|
||||
// pipe from peer to tunnel
|
||||
dgram := gosocks5.NewUDPDatagram(
|
||||
@ -932,6 +974,10 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
|
||||
if err != nil {
|
||||
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 {
|
||||
log.Logf("[udp-tun] %s -> %s : %s", cc.RemoteAddr(), addr, err)
|
||||
errc <- err
|
||||
@ -1075,12 +1121,19 @@ type socks4Handler struct {
|
||||
|
||||
// SOCKS4Handler creates a server Handler for SOCKS4(A) proxy server.
|
||||
func SOCKS4Handler(opts ...HandlerOption) Handler {
|
||||
options := &HandlerOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
h := &socks4Handler{}
|
||||
h.Init(opts...)
|
||||
|
||||
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) {
|
||||
|
||||
if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) {
|
||||
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)
|
||||
if Debug {
|
||||
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) {
|
||||
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 {
|
||||
log.Logf("[socks4-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||
rep := gosocks4.NewReply(gosocks4.Failed, nil)
|
||||
|
66
ss.go
66
ss.go
@ -97,13 +97,20 @@ type shadowHandler struct {
|
||||
|
||||
// ShadowHandler creates a server Handler for shadowsocks proxy server.
|
||||
func ShadowHandler(opts ...HandlerOption) Handler {
|
||||
h := &shadowHandler{
|
||||
options: &HandlerOptions{},
|
||||
h := &shadowHandler{}
|
||||
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)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *shadowHandler) Handle(conn net.Conn) {
|
||||
@ -140,7 +147,17 @@ func (h *shadowHandler) Handle(conn net.Conn) {
|
||||
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 {
|
||||
log.Logf("[ss] %s -> %s : %s", conn.RemoteAddr(), addr, err)
|
||||
return
|
||||
@ -321,13 +338,20 @@ type shadowUDPdHandler struct {
|
||||
|
||||
// ShadowUDPdHandler creates a server Handler for shadowsocks UDP relay server.
|
||||
func ShadowUDPdHandler(opts ...HandlerOption) Handler {
|
||||
h := &shadowUDPdHandler{
|
||||
options: &HandlerOptions{},
|
||||
h := &shadowUDPdHandler{}
|
||||
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)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *shadowUDPdHandler) Handle(conn net.Conn) {
|
||||
@ -353,11 +377,11 @@ func (h *shadowUDPdHandler) Handle(conn net.Conn) {
|
||||
defer cc.Close()
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
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)
|
||||
go func() {
|
||||
for {
|
||||
@ -374,14 +398,18 @@ func transportUDP(sc net.Conn, cc net.PacketConn) error {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
//if Debug {
|
||||
// log.Logf("[ssu] %s >>> %s length: %d", sc.RemoteAddr(), dgram.Header.Addr.String(), len(dgram.Data))
|
||||
//}
|
||||
if Debug {
|
||||
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())
|
||||
if err != nil {
|
||||
errc <- err
|
||||
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 {
|
||||
errc <- err
|
||||
return
|
||||
@ -397,9 +425,13 @@ func transportUDP(sc net.Conn, cc net.PacketConn) error {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
//if Debug {
|
||||
// log.Logf("[ssu] %s <<< %s length: %d", sc.RemoteAddr(), addr, n)
|
||||
//}
|
||||
if Debug {
|
||||
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])
|
||||
buf := bytes.Buffer{}
|
||||
dgram.Write(&buf)
|
||||
|
31
ssh.go
31
ssh.go
@ -408,13 +408,22 @@ type sshForwardHandler struct {
|
||||
|
||||
// SSHForwardHandler creates a server Handler for SSH port forwarding server.
|
||||
func SSHForwardHandler(opts ...HandlerOption) Handler {
|
||||
h := &sshForwardHandler{
|
||||
options: new(HandlerOptions),
|
||||
config: new(ssh.ServerConfig),
|
||||
h := &sshForwardHandler{}
|
||||
h.Init(opts...)
|
||||
|
||||
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)
|
||||
}
|
||||
h.config = &ssh.ServerConfig{}
|
||||
|
||||
h.config.PasswordCallback = defaultSSHPasswordCallback(h.options.Users...)
|
||||
if len(h.options.Users) == 0 {
|
||||
h.config.NoClientAuth = true
|
||||
@ -430,8 +439,6 @@ func SSHForwardHandler(opts ...HandlerOption) Handler {
|
||||
}
|
||||
h.config.AddHostKey(signer)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *sshForwardHandler) Handle(conn net.Conn) {
|
||||
@ -506,7 +513,17 @@ func (h *sshForwardHandler) directPortForwardChannel(channel ssh.Channel, raddr
|
||||
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 {
|
||||
log.Logf("[ssh-tcp] %s - %s : %s", h.options.Addr, raddr, err)
|
||||
return
|
||||
|
14
tls.go
14
tls.go
@ -30,7 +30,7 @@ func (tr *tlsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (
|
||||
if opts.TLSConfig == nil {
|
||||
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
return wrapTLSClient(conn, opts.TLSConfig)
|
||||
return wrapTLSClient(conn, opts.TLSConfig, opts.Timeout)
|
||||
}
|
||||
|
||||
type mtlsTransporter struct {
|
||||
@ -113,7 +113,7 @@ func (tr *mtlsTransporter) initSession(addr string, conn net.Conn, opts *Handsha
|
||||
if opts.TLSConfig == nil {
|
||||
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
conn, err := wrapTLSClient(conn, opts.TLSConfig)
|
||||
conn, err := wrapTLSClient(conn, opts.TLSConfig, opts.Timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -248,7 +248,7 @@ func (l *mtlsListener) Close() error {
|
||||
//
|
||||
// This code is taken from consul:
|
||||
// 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 tlsConn *tls.Conn
|
||||
|
||||
@ -264,6 +264,12 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
|
||||
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
|
||||
//
|
||||
// The following is lightly-modified from the doFullHandshake
|
||||
@ -273,6 +279,8 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConn.SetDeadline(time.Time{}) // clear timeout
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: tlsConfig.RootCAs,
|
||||
CurrentTime: time.Now(),
|
||||
|
79
vendor/github.com/aead/chacha20/README.md
generated
vendored
79
vendor/github.com/aead/chacha20/README.md
generated
vendored
@ -1,79 +0,0 @@
|
||||
[](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
|
||||
```
|
176
vendor/github.com/aead/chacha20/chacha/chacha.go
generated
vendored
176
vendor/github.com/aead/chacha20/chacha/chacha.go
generated
vendored
@ -1,176 +0,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.
|
||||
|
||||
// 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
|
||||
}
|
542
vendor/github.com/aead/chacha20/chacha/chachaAVX2_amd64.s
generated
vendored
542
vendor/github.com/aead/chacha20/chacha/chachaAVX2_amd64.s
generated
vendored
@ -1,542 +0,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 go1.7,amd64,!gccgo,!appengine,!nacl
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·sigma_AVX<>+0x00(SB)/4, $0x61707865
|
||||
DATA ·sigma_AVX<>+0x04(SB)/4, $0x3320646e
|
||||
DATA ·sigma_AVX<>+0x08(SB)/4, $0x79622d32
|
||||
DATA ·sigma_AVX<>+0x0C(SB)/4, $0x6b206574
|
||||
GLOBL ·sigma_AVX<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·one_AVX<>+0x00(SB)/8, $1
|
||||
DATA ·one_AVX<>+0x08(SB)/8, $0
|
||||
GLOBL ·one_AVX<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·one_AVX2<>+0x00(SB)/8, $0
|
||||
DATA ·one_AVX2<>+0x08(SB)/8, $0
|
||||
DATA ·one_AVX2<>+0x10(SB)/8, $1
|
||||
DATA ·one_AVX2<>+0x18(SB)/8, $0
|
||||
GLOBL ·one_AVX2<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·two_AVX2<>+0x00(SB)/8, $2
|
||||
DATA ·two_AVX2<>+0x08(SB)/8, $0
|
||||
DATA ·two_AVX2<>+0x10(SB)/8, $2
|
||||
DATA ·two_AVX2<>+0x18(SB)/8, $0
|
||||
GLOBL ·two_AVX2<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·rol16_AVX2<>+0x00(SB)/8, $0x0504070601000302
|
||||
DATA ·rol16_AVX2<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
|
||||
DATA ·rol16_AVX2<>+0x10(SB)/8, $0x0504070601000302
|
||||
DATA ·rol16_AVX2<>+0x18(SB)/8, $0x0D0C0F0E09080B0A
|
||||
GLOBL ·rol16_AVX2<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·rol8_AVX2<>+0x00(SB)/8, $0x0605040702010003
|
||||
DATA ·rol8_AVX2<>+0x08(SB)/8, $0x0E0D0C0F0A09080B
|
||||
DATA ·rol8_AVX2<>+0x10(SB)/8, $0x0605040702010003
|
||||
DATA ·rol8_AVX2<>+0x18(SB)/8, $0x0E0D0C0F0A09080B
|
||||
GLOBL ·rol8_AVX2<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
#define ROTL(n, t, v) \
|
||||
VPSLLD $n, v, t; \
|
||||
VPSRLD $(32-n), v, v; \
|
||||
VPXOR v, t, v
|
||||
|
||||
#define CHACHA_QROUND(v0, v1, v2, v3, t, c16, c8) \
|
||||
VPADDD v0, v1, v0; \
|
||||
VPXOR v3, v0, v3; \
|
||||
VPSHUFB c16, v3, v3; \
|
||||
VPADDD v2, v3, v2; \
|
||||
VPXOR v1, v2, v1; \
|
||||
ROTL(12, t, v1); \
|
||||
VPADDD v0, v1, v0; \
|
||||
VPXOR v3, v0, v3; \
|
||||
VPSHUFB c8, v3, v3; \
|
||||
VPADDD v2, v3, v2; \
|
||||
VPXOR v1, v2, v1; \
|
||||
ROTL(7, t, v1)
|
||||
|
||||
#define CHACHA_SHUFFLE(v1, v2, v3) \
|
||||
VPSHUFD $0x39, v1, v1; \
|
||||
VPSHUFD $0x4E, v2, v2; \
|
||||
VPSHUFD $-109, v3, v3
|
||||
|
||||
#define XOR_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \
|
||||
VMOVDQU (0+off)(src), t0; \
|
||||
VPERM2I128 $32, v1, v0, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (0+off)(dst); \
|
||||
VMOVDQU (32+off)(src), t0; \
|
||||
VPERM2I128 $32, v3, v2, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (32+off)(dst); \
|
||||
VMOVDQU (64+off)(src), t0; \
|
||||
VPERM2I128 $49, v1, v0, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (64+off)(dst); \
|
||||
VMOVDQU (96+off)(src), t0; \
|
||||
VPERM2I128 $49, v3, v2, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (96+off)(dst)
|
||||
|
||||
#define XOR_UPPER_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \
|
||||
VMOVDQU (0+off)(src), t0; \
|
||||
VPERM2I128 $32, v1, v0, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (0+off)(dst); \
|
||||
VMOVDQU (32+off)(src), t0; \
|
||||
VPERM2I128 $32, v3, v2, t1; \
|
||||
VPXOR t0, t1, t0; \
|
||||
VMOVDQU t0, (32+off)(dst); \
|
||||
|
||||
#define EXTRACT_LOWER(dst, v0, v1, v2, v3, t0) \
|
||||
VPERM2I128 $49, v1, v0, t0; \
|
||||
VMOVDQU t0, 0(dst); \
|
||||
VPERM2I128 $49, v3, v2, t0; \
|
||||
VMOVDQU t0, 32(dst)
|
||||
|
||||
#define XOR_AVX(dst, src, off, v0, v1, v2, v3, t0) \
|
||||
VPXOR 0+off(src), v0, t0; \
|
||||
VMOVDQU t0, 0+off(dst); \
|
||||
VPXOR 16+off(src), v1, t0; \
|
||||
VMOVDQU t0, 16+off(dst); \
|
||||
VPXOR 32+off(src), v2, t0; \
|
||||
VMOVDQU t0, 32+off(dst); \
|
||||
VPXOR 48+off(src), v3, t0; \
|
||||
VMOVDQU t0, 48+off(dst)
|
||||
|
||||
#define TWO 0(SP)
|
||||
#define C16 32(SP)
|
||||
#define C8 64(SP)
|
||||
#define STATE_0 96(SP)
|
||||
#define STATE_1 128(SP)
|
||||
#define STATE_2 160(SP)
|
||||
#define STATE_3 192(SP)
|
||||
#define TMP_0 224(SP)
|
||||
#define TMP_1 256(SP)
|
||||
|
||||
// func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
TEXT ·xorKeyStreamAVX2(SB), 4, $320-80
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ src_len+32(FP), CX
|
||||
MOVQ block+48(FP), BX
|
||||
MOVQ state+56(FP), AX
|
||||
MOVQ rounds+64(FP), DX
|
||||
|
||||
MOVQ SP, R8
|
||||
ADDQ $32, SP
|
||||
ANDQ $-32, SP
|
||||
|
||||
VMOVDQU 0(AX), Y2
|
||||
VMOVDQU 32(AX), Y3
|
||||
VPERM2I128 $0x22, Y2, Y0, Y0
|
||||
VPERM2I128 $0x33, Y2, Y1, Y1
|
||||
VPERM2I128 $0x22, Y3, Y2, Y2
|
||||
VPERM2I128 $0x33, Y3, Y3, Y3
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
|
||||
VMOVDQU ·one_AVX2<>(SB), Y4
|
||||
VPADDD Y4, Y3, Y3
|
||||
|
||||
VMOVDQA Y0, STATE_0
|
||||
VMOVDQA Y1, STATE_1
|
||||
VMOVDQA Y2, STATE_2
|
||||
VMOVDQA Y3, STATE_3
|
||||
|
||||
VMOVDQU ·rol16_AVX2<>(SB), Y4
|
||||
VMOVDQU ·rol8_AVX2<>(SB), Y5
|
||||
VMOVDQU ·two_AVX2<>(SB), Y6
|
||||
VMOVDQA Y4, Y14
|
||||
VMOVDQA Y5, Y15
|
||||
VMOVDQA Y4, C16
|
||||
VMOVDQA Y5, C8
|
||||
VMOVDQA Y6, TWO
|
||||
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
CMPQ CX, $192
|
||||
JBE between_64_and_192
|
||||
CMPQ CX, $320
|
||||
JBE between_192_and_320
|
||||
CMPQ CX, $448
|
||||
JBE between_320_and_448
|
||||
|
||||
at_least_512:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VPADDQ TWO, Y3, Y7
|
||||
VMOVDQA Y0, Y8
|
||||
VMOVDQA Y1, Y9
|
||||
VMOVDQA Y2, Y10
|
||||
VPADDQ TWO, Y7, Y11
|
||||
VMOVDQA Y0, Y12
|
||||
VMOVDQA Y1, Y13
|
||||
VMOVDQA Y2, Y14
|
||||
VPADDQ TWO, Y11, Y15
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_512:
|
||||
VMOVDQA Y8, TMP_0
|
||||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y8, C16, C8)
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y8, C16, C8)
|
||||
VMOVDQA TMP_0, Y8
|
||||
VMOVDQA Y0, TMP_0
|
||||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y0, C16, C8)
|
||||
CHACHA_QROUND(Y12, Y13, Y14, Y15, Y0, C16, C8)
|
||||
CHACHA_SHUFFLE(Y1, Y2, Y3)
|
||||
CHACHA_SHUFFLE(Y5, Y6, Y7)
|
||||
CHACHA_SHUFFLE(Y9, Y10, Y11)
|
||||
CHACHA_SHUFFLE(Y13, Y14, Y15)
|
||||
|
||||
CHACHA_QROUND(Y12, Y13, Y14, Y15, Y0, C16, C8)
|
||||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y0, C16, C8)
|
||||
VMOVDQA TMP_0, Y0
|
||||
VMOVDQA Y8, TMP_0
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y8, C16, C8)
|
||||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y8, C16, C8)
|
||||
VMOVDQA TMP_0, Y8
|
||||
CHACHA_SHUFFLE(Y3, Y2, Y1)
|
||||
CHACHA_SHUFFLE(Y7, Y6, Y5)
|
||||
CHACHA_SHUFFLE(Y11, Y10, Y9)
|
||||
CHACHA_SHUFFLE(Y15, Y14, Y13)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_512
|
||||
|
||||
VMOVDQA Y12, TMP_0
|
||||
VMOVDQA Y13, TMP_1
|
||||
VPADDD STATE_0, Y0, Y0
|
||||
VPADDD STATE_1, Y1, Y1
|
||||
VPADDD STATE_2, Y2, Y2
|
||||
VPADDD STATE_3, Y3, Y3
|
||||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13)
|
||||
VMOVDQA STATE_0, Y0
|
||||
VMOVDQA STATE_1, Y1
|
||||
VMOVDQA STATE_2, Y2
|
||||
VMOVDQA STATE_3, Y3
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y8, Y8
|
||||
VPADDD Y1, Y9, Y9
|
||||
VPADDD Y2, Y10, Y10
|
||||
VPADDD Y3, Y11, Y11
|
||||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD TMP_0, Y0, Y12
|
||||
VPADDD TMP_1, Y1, Y13
|
||||
VPADDD Y2, Y14, Y14
|
||||
VPADDD Y3, Y15, Y15
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $512
|
||||
JB less_than_512
|
||||
|
||||
XOR_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5)
|
||||
VMOVDQA Y3, STATE_3
|
||||
ADDQ $512, SI
|
||||
ADDQ $512, DI
|
||||
SUBQ $512, CX
|
||||
CMPQ CX, $448
|
||||
JA at_least_512
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
|
||||
VMOVDQA C16, Y14
|
||||
VMOVDQA C8, Y15
|
||||
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
CMPQ CX, $192
|
||||
JBE between_64_and_192
|
||||
CMPQ CX, $320
|
||||
JBE between_192_and_320
|
||||
JMP between_320_and_448
|
||||
|
||||
less_than_512:
|
||||
XOR_UPPER_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5)
|
||||
EXTRACT_LOWER(BX, Y12, Y13, Y14, Y15, Y4)
|
||||
ADDQ $448, SI
|
||||
ADDQ $448, DI
|
||||
SUBQ $448, CX
|
||||
JMP finalize
|
||||
|
||||
between_320_and_448:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VPADDQ TWO, Y3, Y7
|
||||
VMOVDQA Y0, Y8
|
||||
VMOVDQA Y1, Y9
|
||||
VMOVDQA Y2, Y10
|
||||
VPADDQ TWO, Y7, Y11
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_384:
|
||||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y13, Y14, Y15)
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE(Y1, Y2, Y3)
|
||||
CHACHA_SHUFFLE(Y5, Y6, Y7)
|
||||
CHACHA_SHUFFLE(Y9, Y10, Y11)
|
||||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y13, Y14, Y15)
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE(Y3, Y2, Y1)
|
||||
CHACHA_SHUFFLE(Y7, Y6, Y5)
|
||||
CHACHA_SHUFFLE(Y11, Y10, Y9)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_384
|
||||
|
||||
VPADDD STATE_0, Y0, Y0
|
||||
VPADDD STATE_1, Y1, Y1
|
||||
VPADDD STATE_2, Y2, Y2
|
||||
VPADDD STATE_3, Y3, Y3
|
||||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13)
|
||||
VMOVDQA STATE_0, Y0
|
||||
VMOVDQA STATE_1, Y1
|
||||
VMOVDQA STATE_2, Y2
|
||||
VMOVDQA STATE_3, Y3
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
VPADDD Y0, Y8, Y8
|
||||
VPADDD Y1, Y9, Y9
|
||||
VPADDD Y2, Y10, Y10
|
||||
VPADDD Y3, Y11, Y11
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $384
|
||||
JB less_than_384
|
||||
|
||||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
SUBQ $384, CX
|
||||
TESTQ CX, CX
|
||||
JE done
|
||||
|
||||
ADDQ $384, SI
|
||||
ADDQ $384, DI
|
||||
JMP between_0_and_64
|
||||
|
||||
less_than_384:
|
||||
XOR_UPPER_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12)
|
||||
ADDQ $320, SI
|
||||
ADDQ $320, DI
|
||||
SUBQ $320, CX
|
||||
JMP finalize
|
||||
|
||||
between_192_and_320:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VMOVDQA Y3, Y7
|
||||
VMOVDQA Y0, Y8
|
||||
VMOVDQA Y1, Y9
|
||||
VMOVDQA Y2, Y10
|
||||
VPADDQ TWO, Y3, Y11
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_256:
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE(Y5, Y6, Y7)
|
||||
CHACHA_SHUFFLE(Y9, Y10, Y11)
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE(Y7, Y6, Y5)
|
||||
CHACHA_SHUFFLE(Y11, Y10, Y9)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_256
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
VPADDQ TWO, Y3, Y3
|
||||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
VPADDD Y0, Y8, Y8
|
||||
VPADDD Y1, Y9, Y9
|
||||
VPADDD Y2, Y10, Y10
|
||||
VPADDD Y3, Y11, Y11
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $256
|
||||
JB less_than_256
|
||||
|
||||
XOR_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
SUBQ $256, CX
|
||||
TESTQ CX, CX
|
||||
JE done
|
||||
|
||||
ADDQ $256, SI
|
||||
ADDQ $256, DI
|
||||
JMP between_0_and_64
|
||||
|
||||
less_than_256:
|
||||
XOR_UPPER_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13)
|
||||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12)
|
||||
ADDQ $192, SI
|
||||
ADDQ $192, DI
|
||||
SUBQ $192, CX
|
||||
JMP finalize
|
||||
|
||||
between_64_and_192:
|
||||
VMOVDQA Y0, Y4
|
||||
VMOVDQA Y1, Y5
|
||||
VMOVDQA Y2, Y6
|
||||
VMOVDQA Y3, Y7
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_128:
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE(Y5, Y6, Y7)
|
||||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
|
||||
CHACHA_SHUFFLE(Y7, Y6, Y5)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_128
|
||||
|
||||
VPADDD Y0, Y4, Y4
|
||||
VPADDD Y1, Y5, Y5
|
||||
VPADDD Y2, Y6, Y6
|
||||
VPADDD Y3, Y7, Y7
|
||||
VPADDQ TWO, Y3, Y3
|
||||
|
||||
CMPQ CX, $128
|
||||
JB less_than_128
|
||||
|
||||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
SUBQ $128, CX
|
||||
TESTQ CX, CX
|
||||
JE done
|
||||
|
||||
ADDQ $128, SI
|
||||
ADDQ $128, DI
|
||||
JMP between_0_and_64
|
||||
|
||||
less_than_128:
|
||||
XOR_UPPER_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
|
||||
EXTRACT_LOWER(BX, Y4, Y5, Y6, Y7, Y13)
|
||||
ADDQ $64, SI
|
||||
ADDQ $64, DI
|
||||
SUBQ $64, CX
|
||||
JMP finalize
|
||||
|
||||
between_0_and_64:
|
||||
VMOVDQA X0, X4
|
||||
VMOVDQA X1, X5
|
||||
VMOVDQA X2, X6
|
||||
VMOVDQA X3, X7
|
||||
|
||||
MOVQ DX, R9
|
||||
|
||||
chacha_loop_64:
|
||||
CHACHA_QROUND(X4, X5, X6, X7, X13, X14, X15)
|
||||
CHACHA_SHUFFLE(X5, X6, X7)
|
||||
CHACHA_QROUND(X4, X5, X6, X7, X13, X14, X15)
|
||||
CHACHA_SHUFFLE(X7, X6, X5)
|
||||
SUBQ $2, R9
|
||||
JA chacha_loop_64
|
||||
|
||||
VPADDD X0, X4, X4
|
||||
VPADDD X1, X5, X5
|
||||
VPADDD X2, X6, X6
|
||||
VPADDD X3, X7, X7
|
||||
VMOVDQU ·one_AVX<>(SB), X0
|
||||
VPADDQ X0, X3, X3
|
||||
|
||||
CMPQ CX, $64
|
||||
JB less_than_64
|
||||
|
||||
XOR_AVX(DI, SI, 0, X4, X5, X6, X7, X13)
|
||||
SUBQ $64, CX
|
||||
JMP done
|
||||
|
||||
less_than_64:
|
||||
VMOVDQU X4, 0(BX)
|
||||
VMOVDQU X5, 16(BX)
|
||||
VMOVDQU X6, 32(BX)
|
||||
VMOVDQU X7, 48(BX)
|
||||
|
||||
finalize:
|
||||
XORQ R11, R11
|
||||
XORQ R12, R12
|
||||
MOVQ CX, BP
|
||||
|
||||
xor_loop:
|
||||
MOVB 0(SI), R11
|
||||
MOVB 0(BX), R12
|
||||
XORQ R11, R12
|
||||
MOVB R12, 0(DI)
|
||||
INCQ SI
|
||||
INCQ BX
|
||||
INCQ DI
|
||||
DECQ BP
|
||||
JA xor_loop
|
||||
|
||||
done:
|
||||
VMOVDQU X3, 48(AX)
|
||||
VZEROUPPER
|
||||
MOVQ R8, SP
|
||||
MOVQ CX, ret+72(FP)
|
||||
RET
|
||||
|
||||
// func hChaCha20AVX(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
TEXT ·hChaCha20AVX(SB), 4, $0-24
|
||||
MOVQ out+0(FP), DI
|
||||
MOVQ nonce+8(FP), AX
|
||||
MOVQ key+16(FP), BX
|
||||
|
||||
VMOVDQU ·sigma_AVX<>(SB), X0
|
||||
VMOVDQU 0(BX), X1
|
||||
VMOVDQU 16(BX), X2
|
||||
VMOVDQU 0(AX), X3
|
||||
VMOVDQU ·rol16_AVX2<>(SB), X5
|
||||
VMOVDQU ·rol8_AVX2<>(SB), X6
|
||||
|
||||
MOVQ $20, CX
|
||||
|
||||
chacha_loop:
|
||||
CHACHA_QROUND(X0, X1, X2, X3, X4, X5, X6)
|
||||
CHACHA_SHUFFLE(X1, X2, X3)
|
||||
CHACHA_QROUND(X0, X1, X2, X3, X4, X5, X6)
|
||||
CHACHA_SHUFFLE(X3, X2, X1)
|
||||
SUBQ $2, CX
|
||||
JNZ chacha_loop
|
||||
|
||||
VMOVDQU X0, 0(DI)
|
||||
VMOVDQU X3, 16(DI)
|
||||
VZEROUPPER
|
||||
RET
|
||||
|
||||
// func supportsAVX2() bool
|
||||
TEXT ·supportsAVX2(SB), 4, $0-1
|
||||
MOVQ runtime·support_avx(SB), AX
|
||||
MOVQ runtime·support_avx2(SB), BX
|
||||
ANDQ AX, BX
|
||||
MOVB BX, ret+0(FP)
|
||||
RET
|
67
vendor/github.com/aead/chacha20/chacha/chacha_386.go
generated
vendored
67
vendor/github.com/aead/chacha20/chacha/chacha_386.go
generated
vendored
@ -1,67 +0,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)
|
||||
}
|
311
vendor/github.com/aead/chacha20/chacha/chacha_386.s
generated
vendored
311
vendor/github.com/aead/chacha20/chacha/chacha_386.s
generated
vendored
@ -1,311 +0,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
|
||||
|
||||
#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
|
788
vendor/github.com/aead/chacha20/chacha/chacha_amd64.s
generated
vendored
788
vendor/github.com/aead/chacha20/chacha/chacha_amd64.s
generated
vendored
@ -1,788 +0,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 amd64,!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)
|
||||
|
||||
// func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
TEXT ·xorKeyStreamSSE2(SB), 4, $112-80
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ src_len+32(FP), CX
|
||||
MOVQ block+48(FP), BX
|
||||
MOVQ state+56(FP), AX
|
||||
MOVQ rounds+64(FP), DX
|
||||
|
||||
MOVQ SP, R9
|
||||
ADDQ $16, SP
|
||||
ANDQ $-16, SP
|
||||
|
||||
MOVOU 0(AX), X0
|
||||
MOVOU 16(AX), X1
|
||||
MOVOU 32(AX), X2
|
||||
MOVOU 48(AX), X3
|
||||
MOVOU ·one<>(SB), X15
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
|
||||
CMPQ CX, $128
|
||||
JBE between_64_and_128
|
||||
|
||||
MOVO X0, 0(SP)
|
||||
MOVO X1, 16(SP)
|
||||
MOVO X2, 32(SP)
|
||||
MOVO X3, 48(SP)
|
||||
MOVO X15, 64(SP)
|
||||
|
||||
CMPQ CX, $192
|
||||
JBE between_128_and_192
|
||||
|
||||
MOVQ $192, R14
|
||||
|
||||
at_least_256:
|
||||
MOVO X0, X4
|
||||
MOVO X1, X5
|
||||
MOVO X2, X6
|
||||
MOVO X3, X7
|
||||
PADDQ 64(SP), X7
|
||||
MOVO X0, X12
|
||||
MOVO X1, X13
|
||||
MOVO X2, X14
|
||||
MOVO X7, X15
|
||||
PADDQ 64(SP), X15
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X15, X11
|
||||
PADDQ 64(SP), X11
|
||||
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_256:
|
||||
MOVO X8, 80(SP)
|
||||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X8)
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X8)
|
||||
MOVO 80(SP), X8
|
||||
|
||||
MOVO X0, 80(SP)
|
||||
CHACHA_QROUND_SSE2(X12, X13, X14, X15, X0)
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X0)
|
||||
MOVO 80(SP), X0
|
||||
|
||||
CHACHA_SHUFFLE(X1, X2, X3)
|
||||
CHACHA_SHUFFLE(X5, X6, X7)
|
||||
CHACHA_SHUFFLE(X13, X14, X15)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
|
||||
MOVO X8, 80(SP)
|
||||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X8)
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X8)
|
||||
MOVO 80(SP), X8
|
||||
|
||||
MOVO X0, 80(SP)
|
||||
CHACHA_QROUND_SSE2(X12, X13, X14, X15, X0)
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X0)
|
||||
MOVO 80(SP), X0
|
||||
|
||||
CHACHA_SHUFFLE(X3, X2, X1)
|
||||
CHACHA_SHUFFLE(X7, X6, X5)
|
||||
CHACHA_SHUFFLE(X15, X14, X13)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_256
|
||||
|
||||
MOVO X8, 80(SP)
|
||||
|
||||
PADDL 0(SP), X0
|
||||
PADDL 16(SP), X1
|
||||
PADDL 32(SP), X2
|
||||
PADDL 48(SP), X3
|
||||
XOR(DI, SI, 0, X0, X1, X2, X3, X8)
|
||||
|
||||
MOVO 0(SP), X0
|
||||
MOVO 16(SP), X1
|
||||
MOVO 32(SP), X2
|
||||
MOVO 48(SP), X3
|
||||
PADDQ 64(SP), X3
|
||||
|
||||
PADDL X0, X4
|
||||
PADDL X1, X5
|
||||
PADDL X2, X6
|
||||
PADDL X3, X7
|
||||
PADDQ 64(SP), X3
|
||||
XOR(DI, SI, 64, X4, X5, X6, X7, X8)
|
||||
|
||||
MOVO 64(SP), X5
|
||||
MOVO 80(SP), X8
|
||||
|
||||
PADDL X0, X12
|
||||
PADDL X1, X13
|
||||
PADDL X2, X14
|
||||
PADDL X3, X15
|
||||
PADDQ X5, X3
|
||||
XOR(DI, SI, 128, X12, X13, X14, X15, X4)
|
||||
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X5, X3
|
||||
|
||||
CMPQ CX, $256
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 192, X8, X9, X10, X11, X4)
|
||||
MOVO X3, 48(SP)
|
||||
ADDQ $256, SI
|
||||
ADDQ $256, DI
|
||||
SUBQ $256, CX
|
||||
CMPQ CX, $192
|
||||
JA at_least_256
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
MOVO 64(SP), X15
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
CMPQ CX, $128
|
||||
JBE between_64_and_128
|
||||
|
||||
between_128_and_192:
|
||||
MOVQ $128, R14
|
||||
MOVO X0, X4
|
||||
MOVO X1, X5
|
||||
MOVO X2, X6
|
||||
MOVO X3, X7
|
||||
PADDQ X15, X7
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X7, X11
|
||||
PADDQ X15, X11
|
||||
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_192:
|
||||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X12)
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12)
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12)
|
||||
CHACHA_SHUFFLE(X1, X2, X3)
|
||||
CHACHA_SHUFFLE(X5, X6, X7)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X12)
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12)
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12)
|
||||
CHACHA_SHUFFLE(X3, X2, X1)
|
||||
CHACHA_SHUFFLE(X7, X6, X5)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_192
|
||||
|
||||
PADDL 0(SP), X0
|
||||
PADDL 16(SP), X1
|
||||
PADDL 32(SP), X2
|
||||
PADDL 48(SP), X3
|
||||
XOR(DI, SI, 0, X0, X1, X2, X3, X12)
|
||||
|
||||
MOVO 0(SP), X0
|
||||
MOVO 16(SP), X1
|
||||
MOVO 32(SP), X2
|
||||
MOVO 48(SP), X3
|
||||
PADDQ X15, X3
|
||||
|
||||
PADDL X0, X4
|
||||
PADDL X1, X5
|
||||
PADDL X2, X6
|
||||
PADDL X3, X7
|
||||
PADDQ X15, X3
|
||||
XOR(DI, SI, 64, X4, X5, X6, X7, X12)
|
||||
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X15, X3
|
||||
|
||||
CMPQ CX, $192
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 128, X8, X9, X10, X11, X12)
|
||||
SUBQ $192, CX
|
||||
JMP done
|
||||
|
||||
between_64_and_128:
|
||||
MOVQ $64, R14
|
||||
MOVO X0, X4
|
||||
MOVO X1, X5
|
||||
MOVO X2, X6
|
||||
MOVO X3, X7
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X3, X11
|
||||
PADDQ X15, X11
|
||||
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_128:
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12)
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12)
|
||||
CHACHA_SHUFFLE(X5, X6, X7)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12)
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12)
|
||||
CHACHA_SHUFFLE(X7, X6, X5)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_128
|
||||
|
||||
PADDL X0, X4
|
||||
PADDL X1, X5
|
||||
PADDL X2, X6
|
||||
PADDL X3, X7
|
||||
PADDQ X15, X3
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X15, X3
|
||||
XOR(DI, SI, 0, X4, X5, X6, X7, X12)
|
||||
|
||||
CMPQ CX, $128
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 64, X8, X9, X10, X11, X12)
|
||||
SUBQ $128, CX
|
||||
JMP done
|
||||
|
||||
between_0_and_64:
|
||||
MOVQ $0, R14
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X3, X11
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_64:
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_64
|
||||
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X15, X3
|
||||
CMPQ CX, $64
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 0, X8, X9, X10, X11, X12)
|
||||
SUBQ $64, CX
|
||||
JMP done
|
||||
|
||||
less_than_64:
|
||||
// R14 contains the num of bytes already xor'd
|
||||
ADDQ R14, SI
|
||||
ADDQ R14, DI
|
||||
SUBQ R14, CX
|
||||
MOVOU X8, 0(BX)
|
||||
MOVOU X9, 16(BX)
|
||||
MOVOU X10, 32(BX)
|
||||
MOVOU X11, 48(BX)
|
||||
XORQ R11, R11
|
||||
XORQ R12, R12
|
||||
MOVQ CX, BP
|
||||
|
||||
xor_loop:
|
||||
MOVB 0(SI), R11
|
||||
MOVB 0(BX), R12
|
||||
XORQ R11, R12
|
||||
MOVB R12, 0(DI)
|
||||
INCQ SI
|
||||
INCQ BX
|
||||
INCQ DI
|
||||
DECQ BP
|
||||
JA xor_loop
|
||||
|
||||
done:
|
||||
MOVOU X3, 48(AX)
|
||||
MOVQ R9, SP
|
||||
MOVQ CX, ret+72(FP)
|
||||
RET
|
||||
|
||||
// func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int
|
||||
TEXT ·xorKeyStreamSSSE3(SB), 4, $144-80
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ src_len+32(FP), CX
|
||||
MOVQ block+48(FP), BX
|
||||
MOVQ state+56(FP), AX
|
||||
MOVQ rounds+64(FP), DX
|
||||
|
||||
MOVQ SP, R9
|
||||
ADDQ $16, SP
|
||||
ANDQ $-16, SP
|
||||
|
||||
MOVOU 0(AX), X0
|
||||
MOVOU 16(AX), X1
|
||||
MOVOU 32(AX), X2
|
||||
MOVOU 48(AX), X3
|
||||
MOVOU ·rol16<>(SB), X13
|
||||
MOVOU ·rol8<>(SB), X14
|
||||
MOVOU ·one<>(SB), X15
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
|
||||
CMPQ CX, $128
|
||||
JBE between_64_and_128
|
||||
|
||||
MOVO X0, 0(SP)
|
||||
MOVO X1, 16(SP)
|
||||
MOVO X2, 32(SP)
|
||||
MOVO X3, 48(SP)
|
||||
MOVO X15, 64(SP)
|
||||
|
||||
CMPQ CX, $192
|
||||
JBE between_128_and_192
|
||||
|
||||
MOVO X13, 96(SP)
|
||||
MOVO X14, 112(SP)
|
||||
MOVQ $192, R14
|
||||
|
||||
at_least_256:
|
||||
MOVO X0, X4
|
||||
MOVO X1, X5
|
||||
MOVO X2, X6
|
||||
MOVO X3, X7
|
||||
PADDQ 64(SP), X7
|
||||
MOVO X0, X12
|
||||
MOVO X1, X13
|
||||
MOVO X2, X14
|
||||
MOVO X7, X15
|
||||
PADDQ 64(SP), X15
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X15, X11
|
||||
PADDQ 64(SP), X11
|
||||
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_256:
|
||||
MOVO X8, 80(SP)
|
||||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X8, 96(SP), 112(SP))
|
||||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X8, 96(SP), 112(SP))
|
||||
MOVO 80(SP), X8
|
||||
|
||||
MOVO X0, 80(SP)
|
||||
CHACHA_QROUND_SSSE3(X12, X13, X14, X15, X0, 96(SP), 112(SP))
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X0, 96(SP), 112(SP))
|
||||
MOVO 80(SP), X0
|
||||
|
||||
CHACHA_SHUFFLE(X1, X2, X3)
|
||||
CHACHA_SHUFFLE(X5, X6, X7)
|
||||
CHACHA_SHUFFLE(X13, X14, X15)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
|
||||
MOVO X8, 80(SP)
|
||||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X8, 96(SP), 112(SP))
|
||||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X8, 96(SP), 112(SP))
|
||||
MOVO 80(SP), X8
|
||||
|
||||
MOVO X0, 80(SP)
|
||||
CHACHA_QROUND_SSSE3(X12, X13, X14, X15, X0, 96(SP), 112(SP))
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X0, 96(SP), 112(SP))
|
||||
MOVO 80(SP), X0
|
||||
|
||||
CHACHA_SHUFFLE(X3, X2, X1)
|
||||
CHACHA_SHUFFLE(X7, X6, X5)
|
||||
CHACHA_SHUFFLE(X15, X14, X13)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_256
|
||||
|
||||
MOVO X8, 80(SP)
|
||||
|
||||
PADDL 0(SP), X0
|
||||
PADDL 16(SP), X1
|
||||
PADDL 32(SP), X2
|
||||
PADDL 48(SP), X3
|
||||
XOR(DI, SI, 0, X0, X1, X2, X3, X8)
|
||||
MOVO 0(SP), X0
|
||||
MOVO 16(SP), X1
|
||||
MOVO 32(SP), X2
|
||||
MOVO 48(SP), X3
|
||||
PADDQ 64(SP), X3
|
||||
|
||||
PADDL X0, X4
|
||||
PADDL X1, X5
|
||||
PADDL X2, X6
|
||||
PADDL X3, X7
|
||||
PADDQ 64(SP), X3
|
||||
XOR(DI, SI, 64, X4, X5, X6, X7, X8)
|
||||
|
||||
MOVO 64(SP), X5
|
||||
MOVO 80(SP), X8
|
||||
|
||||
PADDL X0, X12
|
||||
PADDL X1, X13
|
||||
PADDL X2, X14
|
||||
PADDL X3, X15
|
||||
PADDQ X5, X3
|
||||
XOR(DI, SI, 128, X12, X13, X14, X15, X4)
|
||||
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X5, X3
|
||||
|
||||
CMPQ CX, $256
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 192, X8, X9, X10, X11, X4)
|
||||
MOVO X3, 48(SP)
|
||||
ADDQ $256, SI
|
||||
ADDQ $256, DI
|
||||
SUBQ $256, CX
|
||||
CMPQ CX, $192
|
||||
JA at_least_256
|
||||
|
||||
TESTQ CX, CX
|
||||
JZ done
|
||||
MOVOU ·rol16<>(SB), X13
|
||||
MOVOU ·rol8<>(SB), X14
|
||||
MOVO 64(SP), X15
|
||||
CMPQ CX, $64
|
||||
JBE between_0_and_64
|
||||
CMPQ CX, $128
|
||||
JBE between_64_and_128
|
||||
|
||||
between_128_and_192:
|
||||
MOVQ $128, R14
|
||||
MOVO X0, X4
|
||||
MOVO X1, X5
|
||||
MOVO X2, X6
|
||||
MOVO X3, X7
|
||||
PADDQ X15, X7
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X7, X11
|
||||
PADDQ X15, X11
|
||||
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_192:
|
||||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X12, X13, X14)
|
||||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14)
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14)
|
||||
CHACHA_SHUFFLE(X1, X2, X3)
|
||||
CHACHA_SHUFFLE(X5, X6, X7)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X12, X13, X14)
|
||||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14)
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14)
|
||||
CHACHA_SHUFFLE(X3, X2, X1)
|
||||
CHACHA_SHUFFLE(X7, X6, X5)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_192
|
||||
|
||||
PADDL 0(SP), X0
|
||||
PADDL 16(SP), X1
|
||||
PADDL 32(SP), X2
|
||||
PADDL 48(SP), X3
|
||||
XOR(DI, SI, 0, X0, X1, X2, X3, X12)
|
||||
|
||||
MOVO 0(SP), X0
|
||||
MOVO 16(SP), X1
|
||||
MOVO 32(SP), X2
|
||||
MOVO 48(SP), X3
|
||||
PADDQ X15, X3
|
||||
|
||||
PADDL X0, X4
|
||||
PADDL X1, X5
|
||||
PADDL X2, X6
|
||||
PADDL X3, X7
|
||||
PADDQ X15, X3
|
||||
XOR(DI, SI, 64, X4, X5, X6, X7, X12)
|
||||
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X15, X3
|
||||
|
||||
CMPQ CX, $192
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 128, X8, X9, X10, X11, X12)
|
||||
SUBQ $192, CX
|
||||
JMP done
|
||||
|
||||
between_64_and_128:
|
||||
MOVQ $64, R14
|
||||
MOVO X0, X4
|
||||
MOVO X1, X5
|
||||
MOVO X2, X6
|
||||
MOVO X3, X7
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X3, X11
|
||||
PADDQ X15, X11
|
||||
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_128:
|
||||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14)
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14)
|
||||
CHACHA_SHUFFLE(X5, X6, X7)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14)
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14)
|
||||
CHACHA_SHUFFLE(X7, X6, X5)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_128
|
||||
|
||||
PADDL X0, X4
|
||||
PADDL X1, X5
|
||||
PADDL X2, X6
|
||||
PADDL X3, X7
|
||||
PADDQ X15, X3
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X15, X3
|
||||
XOR(DI, SI, 0, X4, X5, X6, X7, X12)
|
||||
|
||||
CMPQ CX, $128
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 64, X8, X9, X10, X11, X12)
|
||||
SUBQ $128, CX
|
||||
JMP done
|
||||
|
||||
between_0_and_64:
|
||||
MOVQ $0, R14
|
||||
MOVO X0, X8
|
||||
MOVO X1, X9
|
||||
MOVO X2, X10
|
||||
MOVO X3, X11
|
||||
MOVQ DX, R8
|
||||
|
||||
chacha_loop_64:
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14)
|
||||
CHACHA_SHUFFLE(X9, X10, X11)
|
||||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14)
|
||||
CHACHA_SHUFFLE(X11, X10, X9)
|
||||
SUBQ $2, R8
|
||||
JA chacha_loop_64
|
||||
|
||||
PADDL X0, X8
|
||||
PADDL X1, X9
|
||||
PADDL X2, X10
|
||||
PADDL X3, X11
|
||||
PADDQ X15, X3
|
||||
CMPQ CX, $64
|
||||
JB less_than_64
|
||||
|
||||
XOR(DI, SI, 0, X8, X9, X10, X11, X12)
|
||||
SUBQ $64, CX
|
||||
JMP done
|
||||
|
||||
less_than_64:
|
||||
// R14 contains the num of bytes already xor'd
|
||||
ADDQ R14, SI
|
||||
ADDQ R14, DI
|
||||
SUBQ R14, CX
|
||||
MOVOU X8, 0(BX)
|
||||
MOVOU X9, 16(BX)
|
||||
MOVOU X10, 32(BX)
|
||||
MOVOU X11, 48(BX)
|
||||
XORQ R11, R11
|
||||
XORQ R12, R12
|
||||
MOVQ CX, BP
|
||||
|
||||
xor_loop:
|
||||
MOVB 0(SI), R11
|
||||
MOVB 0(BX), R12
|
||||
XORQ R11, R12
|
||||
MOVB R12, 0(DI)
|
||||
INCQ SI
|
||||
INCQ BX
|
||||
INCQ DI
|
||||
DECQ BP
|
||||
JA xor_loop
|
||||
|
||||
done:
|
||||
MOVQ R9, SP
|
||||
MOVOU X3, 48(AX)
|
||||
MOVQ CX, ret+72(FP)
|
||||
RET
|
||||
|
||||
// func supportsSSSE3() bool
|
||||
TEXT ·supportsSSSE3(SB), NOSPLIT, $0-1
|
||||
XORQ AX, AX
|
||||
INCQ AX
|
||||
CPUID
|
||||
SHRQ $9, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
||||
// func initialize(state *[64]byte, key []byte, nonce *[16]byte)
|
||||
TEXT ·initialize(SB), 4, $0-40
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ key+8(FP), AX
|
||||
MOVQ nonce+32(FP), BX
|
||||
|
||||
MOVOU ·sigma<>(SB), X0
|
||||
MOVOU 0(AX), X1
|
||||
MOVOU 16(AX), X2
|
||||
MOVOU 0(BX), X3
|
||||
|
||||
MOVOU X0, 0(DI)
|
||||
MOVOU X1, 16(DI)
|
||||
MOVOU X2, 32(DI)
|
||||
MOVOU X3, 48(DI)
|
||||
RET
|
||||
|
||||
// func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
|
||||
TEXT ·hChaCha20SSE2(SB), 4, $0-24
|
||||
MOVQ out+0(FP), DI
|
||||
MOVQ nonce+8(FP), AX
|
||||
MOVQ key+16(FP), BX
|
||||
|
||||
MOVOU ·sigma<>(SB), X0
|
||||
MOVOU 0(BX), X1
|
||||
MOVOU 16(BX), X2
|
||||
MOVOU 0(AX), X3
|
||||
|
||||
MOVQ $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)
|
||||
SUBQ $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-24
|
||||
MOVQ out+0(FP), DI
|
||||
MOVQ nonce+8(FP), AX
|
||||
MOVQ key+16(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
|
||||
|
||||
MOVQ $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)
|
||||
SUBQ $2, CX
|
||||
JNZ chacha_loop
|
||||
|
||||
MOVOU X0, 0(DI)
|
||||
MOVOU X3, 16(DI)
|
||||
RET
|
319
vendor/github.com/aead/chacha20/chacha/chacha_generic.go
generated
vendored
319
vendor/github.com/aead/chacha20/chacha/chacha_generic.go
generated
vendored
@ -1,319 +0,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.
|
||||
|
||||
package chacha
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
var sigma = [4]uint32{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574}
|
||||
|
||||
func xorKeyStreamGeneric(dst, src []byte, block, state *[64]byte, rounds int) int {
|
||||
for len(src) >= 64 {
|
||||
chachaGeneric(block, state, rounds)
|
||||
|
||||
for i, v := range block {
|
||||
dst[i] = src[i] ^ v
|
||||
}
|
||||
src = src[64:]
|
||||
dst = dst[64:]
|
||||
}
|
||||
|
||||
n := len(src)
|
||||
if n > 0 {
|
||||
chachaGeneric(block, state, rounds)
|
||||
for i, v := range src {
|
||||
dst[i] = v ^ block[i]
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func chachaGeneric(dst *[64]byte, state *[64]byte, rounds int) {
|
||||
v00 := binary.LittleEndian.Uint32(state[0:])
|
||||
v01 := binary.LittleEndian.Uint32(state[4:])
|
||||
v02 := binary.LittleEndian.Uint32(state[8:])
|
||||
v03 := binary.LittleEndian.Uint32(state[12:])
|
||||
v04 := binary.LittleEndian.Uint32(state[16:])
|
||||
v05 := binary.LittleEndian.Uint32(state[20:])
|
||||
v06 := binary.LittleEndian.Uint32(state[24:])
|
||||
v07 := binary.LittleEndian.Uint32(state[28:])
|
||||
v08 := binary.LittleEndian.Uint32(state[32:])
|
||||
v09 := binary.LittleEndian.Uint32(state[36:])
|
||||
v10 := binary.LittleEndian.Uint32(state[40:])
|
||||
v11 := binary.LittleEndian.Uint32(state[44:])
|
||||
v12 := binary.LittleEndian.Uint32(state[48:])
|
||||
v13 := binary.LittleEndian.Uint32(state[52:])
|
||||
v14 := binary.LittleEndian.Uint32(state[56:])
|
||||
v15 := binary.LittleEndian.Uint32(state[60:])
|
||||
|
||||
s00, s01, s02, s03, s04, s05, s06, s07 := v00, v01, v02, v03, v04, v05, v06, v07
|
||||
s08, s09, s10, s11, s12, s13, s14, s15 := v08, v09, v10, v11, v12, v13, v14, v15
|
||||
|
||||
for i := 0; i < rounds; i += 2 {
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
}
|
||||
|
||||
v00 += s00
|
||||
v01 += s01
|
||||
v02 += s02
|
||||
v03 += s03
|
||||
v04 += s04
|
||||
v05 += s05
|
||||
v06 += s06
|
||||
v07 += s07
|
||||
v08 += s08
|
||||
v09 += s09
|
||||
v10 += s10
|
||||
v11 += s11
|
||||
v12 += s12
|
||||
v13 += s13
|
||||
v14 += s14
|
||||
v15 += s15
|
||||
|
||||
s12++
|
||||
binary.LittleEndian.PutUint32(state[48:], s12)
|
||||
if s12 == 0 { // indicates overflow
|
||||
s13++
|
||||
binary.LittleEndian.PutUint32(state[52:], s13)
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(dst[0:], v00)
|
||||
binary.LittleEndian.PutUint32(dst[4:], v01)
|
||||
binary.LittleEndian.PutUint32(dst[8:], v02)
|
||||
binary.LittleEndian.PutUint32(dst[12:], v03)
|
||||
binary.LittleEndian.PutUint32(dst[16:], v04)
|
||||
binary.LittleEndian.PutUint32(dst[20:], v05)
|
||||
binary.LittleEndian.PutUint32(dst[24:], v06)
|
||||
binary.LittleEndian.PutUint32(dst[28:], v07)
|
||||
binary.LittleEndian.PutUint32(dst[32:], v08)
|
||||
binary.LittleEndian.PutUint32(dst[36:], v09)
|
||||
binary.LittleEndian.PutUint32(dst[40:], v10)
|
||||
binary.LittleEndian.PutUint32(dst[44:], v11)
|
||||
binary.LittleEndian.PutUint32(dst[48:], v12)
|
||||
binary.LittleEndian.PutUint32(dst[52:], v13)
|
||||
binary.LittleEndian.PutUint32(dst[56:], v14)
|
||||
binary.LittleEndian.PutUint32(dst[60:], v15)
|
||||
}
|
||||
|
||||
func hChaCha20Generic(out *[32]byte, nonce *[16]byte, key *[32]byte) {
|
||||
v00 := sigma[0]
|
||||
v01 := sigma[1]
|
||||
v02 := sigma[2]
|
||||
v03 := sigma[3]
|
||||
v04 := binary.LittleEndian.Uint32(key[0:])
|
||||
v05 := binary.LittleEndian.Uint32(key[4:])
|
||||
v06 := binary.LittleEndian.Uint32(key[8:])
|
||||
v07 := binary.LittleEndian.Uint32(key[12:])
|
||||
v08 := binary.LittleEndian.Uint32(key[16:])
|
||||
v09 := binary.LittleEndian.Uint32(key[20:])
|
||||
v10 := binary.LittleEndian.Uint32(key[24:])
|
||||
v11 := binary.LittleEndian.Uint32(key[28:])
|
||||
v12 := binary.LittleEndian.Uint32(nonce[0:])
|
||||
v13 := binary.LittleEndian.Uint32(nonce[4:])
|
||||
v14 := binary.LittleEndian.Uint32(nonce[8:])
|
||||
v15 := binary.LittleEndian.Uint32(nonce[12:])
|
||||
|
||||
for i := 0; i < 20; i += 2 {
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], v00)
|
||||
binary.LittleEndian.PutUint32(out[4:], v01)
|
||||
binary.LittleEndian.PutUint32(out[8:], v02)
|
||||
binary.LittleEndian.PutUint32(out[12:], v03)
|
||||
binary.LittleEndian.PutUint32(out[16:], v12)
|
||||
binary.LittleEndian.PutUint32(out[20:], v13)
|
||||
binary.LittleEndian.PutUint32(out[24:], v14)
|
||||
binary.LittleEndian.PutUint32(out[28:], v15)
|
||||
}
|
56
vendor/github.com/aead/chacha20/chacha/chacha_go16_amd64.go
generated
vendored
56
vendor/github.com/aead/chacha20/chacha/chacha_go16_amd64.go
generated
vendored
@ -1,56 +0,0 @@
|
||||
// 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)
|
||||
}
|
72
vendor/github.com/aead/chacha20/chacha/chacha_go17_amd64.go
generated
vendored
72
vendor/github.com/aead/chacha20/chacha/chacha_go17_amd64.go
generated
vendored
@ -1,72 +0,0 @@
|
||||
// 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)
|
||||
}
|
26
vendor/github.com/aead/chacha20/chacha/chacha_ref.go
generated
vendored
26
vendor/github.com/aead/chacha20/chacha/chacha_ref.go
generated
vendored
@ -1,26 +0,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 !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)
|
||||
}
|
41
vendor/github.com/aead/chacha20/chacha20.go
generated
vendored
41
vendor/github.com/aead/chacha20/chacha20.go
generated
vendored
@ -1,41 +0,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.
|
||||
|
||||
// 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)
|
||||
}
|
21
vendor/github.com/ginuerzh/pht/LICENSE
generated
vendored
21
vendor/github.com/ginuerzh/pht/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
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.
|
2
vendor/github.com/ginuerzh/pht/README.md
generated
vendored
2
vendor/github.com/ginuerzh/pht/README.md
generated
vendored
@ -1,2 +0,0 @@
|
||||
# pht
|
||||
Plain HTTP Tunnel - Tunnel over HTTP using only GET and POST requests, NO Websocket, NO CONNECT method.
|
162
vendor/github.com/ginuerzh/pht/client.go
generated
vendored
162
vendor/github.com/ginuerzh/pht/client.go
generated
vendored
@ -1,162 +0,0 @@
|
||||
package pht
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Host string
|
||||
Key string
|
||||
httpClient *http.Client
|
||||
manager *sessionManager
|
||||
}
|
||||
|
||||
func NewClient(host, key string) *Client {
|
||||
return &Client{
|
||||
Host: host,
|
||||
Key: key,
|
||||
httpClient: &http.Client{},
|
||||
manager: newSessionManager(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Dial() (net.Conn, error) {
|
||||
r, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s%s", c.Host, tokenURI), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Header.Set("Authorization", "key="+c.Key)
|
||||
resp, err := c.httpClient.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token := strings.TrimPrefix(string(data), "token=")
|
||||
if token == "" {
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
|
||||
session := newSession(0, 0)
|
||||
c.manager.SetSession(token, session)
|
||||
|
||||
go c.sendDataLoop(token)
|
||||
go c.recvDataLoop(token)
|
||||
|
||||
return newConn(session), nil
|
||||
}
|
||||
|
||||
func (c *Client) sendDataLoop(token string) error {
|
||||
session := c.manager.GetSession(token)
|
||||
if session == nil {
|
||||
return errors.New("invalid token")
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case b, ok := <-session.wchan:
|
||||
var data string
|
||||
if len(b) > 0 {
|
||||
data = base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
r, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s%s", c.Host, pushURI), bytes.NewBufferString(data+"\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Header.Set("Authorization", fmt.Sprintf("key=%s; token=%s", c.Key, token))
|
||||
if !ok {
|
||||
c.manager.DelSession(token)
|
||||
resp, err := c.httpClient.Do(r)
|
||||
if err != nil { // TODO: retry
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil // session is closed
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(r)
|
||||
if err != nil { // TODO: retry
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) recvDataLoop(token string) error {
|
||||
session := c.manager.GetSession(token)
|
||||
if session == nil {
|
||||
return errors.New("invalid token")
|
||||
}
|
||||
|
||||
for {
|
||||
err := c.recvData(token, session)
|
||||
if err != nil {
|
||||
close(session.rchan)
|
||||
c.manager.DelSession(token)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) recvData(token string, s *session) error {
|
||||
r, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s%s", c.Host, pollURI), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Header.Set("Authorization", fmt.Sprintf("key=%s; token=%s", c.Key, token))
|
||||
resp, err := c.httpClient.Do(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for scanner.Scan() {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return errors.New("session closed")
|
||||
default:
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(scanner.Text())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case s.rchan <- b:
|
||||
case <-s.closed:
|
||||
return errors.New("session closed")
|
||||
case <-time.After(time.Second * 90):
|
||||
return errors.New("timeout")
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
133
vendor/github.com/ginuerzh/pht/conn.go
generated
vendored
133
vendor/github.com/ginuerzh/pht/conn.go
generated
vendored
@ -1,133 +0,0 @@
|
||||
package pht
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type conn struct {
|
||||
session *session
|
||||
rb []byte // read buffer
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
rTimer, wTimer *time.Timer
|
||||
closed chan interface{}
|
||||
}
|
||||
|
||||
func newConn(session *session) *conn {
|
||||
conn := &conn{
|
||||
session: session,
|
||||
rTimer: time.NewTimer(time.Hour * 65535),
|
||||
wTimer: time.NewTimer(time.Hour * 65535),
|
||||
closed: make(chan interface{}),
|
||||
}
|
||||
conn.rTimer.Stop()
|
||||
conn.wTimer.Stop()
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func (conn *conn) Read(b []byte) (n int, err error) {
|
||||
select {
|
||||
case <-conn.closed:
|
||||
err = errors.New("read: use of closed network connection")
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
if len(conn.rb) > 0 {
|
||||
n = copy(b, conn.rb)
|
||||
conn.rb = conn.rb[n:]
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case data, ok := <-conn.session.rchan:
|
||||
if !ok {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
n = copy(b, data)
|
||||
conn.rb = data[n:]
|
||||
case <-conn.rTimer.C:
|
||||
err = errors.New("read timeout")
|
||||
case <-conn.closed:
|
||||
err = io.EOF
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *conn) Write(b []byte) (n int, err error) {
|
||||
select {
|
||||
case <-conn.closed:
|
||||
err = errors.New("write: use of closed network connection")
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, len(b))
|
||||
copy(data, b)
|
||||
|
||||
select {
|
||||
case conn.session.wchan <- data:
|
||||
n = len(b)
|
||||
case <-conn.wTimer.C:
|
||||
err = errors.New("write timeout")
|
||||
case <-conn.closed:
|
||||
err = errors.New("connection is closed")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *conn) Close() error {
|
||||
close(conn.closed)
|
||||
close(conn.session.closed)
|
||||
close(conn.session.wchan)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *conn) LocalAddr() net.Addr {
|
||||
return conn.localAddr
|
||||
}
|
||||
|
||||
func (conn *conn) RemoteAddr() net.Addr {
|
||||
return conn.remoteAddr
|
||||
}
|
||||
|
||||
func (conn *conn) SetReadDeadline(t time.Time) error {
|
||||
if t.IsZero() {
|
||||
conn.rTimer.Stop()
|
||||
return nil
|
||||
}
|
||||
conn.rTimer.Reset(t.Sub(time.Now()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *conn) SetWriteDeadline(t time.Time) error {
|
||||
if t.IsZero() {
|
||||
conn.wTimer.Stop()
|
||||
return nil
|
||||
}
|
||||
conn.wTimer.Reset(t.Sub(time.Now()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *conn) SetDeadline(t time.Time) error {
|
||||
if t.IsZero() {
|
||||
conn.rTimer.Stop()
|
||||
conn.wTimer.Stop()
|
||||
return nil
|
||||
}
|
||||
d := t.Sub(time.Now())
|
||||
conn.rTimer.Reset(d)
|
||||
conn.wTimer.Reset(d)
|
||||
return nil
|
||||
}
|
199
vendor/github.com/ginuerzh/pht/server.go
generated
vendored
199
vendor/github.com/ginuerzh/pht/server.go
generated
vendored
@ -1,199 +0,0 @@
|
||||
package pht
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
tokenURI = "/token"
|
||||
pushURI = "/push"
|
||||
pollURI = "/poll"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Addr string
|
||||
Key string
|
||||
Handler func(net.Conn)
|
||||
manager *sessionManager
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe() error {
|
||||
s.manager = newSessionManager()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle(tokenURI, http.HandlerFunc(s.tokenHandler))
|
||||
mux.Handle(pushURI, http.HandlerFunc(s.pushHandler))
|
||||
mux.Handle(pollURI, http.HandlerFunc(s.pollHandler))
|
||||
|
||||
return http.ListenAndServe(s.Addr, mux)
|
||||
}
|
||||
|
||||
func (s *Server) tokenHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
m := parseAuth(r.Header.Get("Authorization"))
|
||||
if m["key"] != s.Key {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
token, session, err := s.manager.NewSession(0, 0)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := s.upgrade(session, r)
|
||||
if err != nil {
|
||||
s.manager.DelSession(token)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if s.Handler != nil {
|
||||
go s.Handler(conn)
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("token=%s", token)))
|
||||
}
|
||||
|
||||
func (s *Server) pushHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
m := parseAuth(r.Header.Get("Authorization"))
|
||||
if m["key"] != s.Key {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
token := m["token"]
|
||||
session := s.manager.GetSession(token)
|
||||
if session == nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
br := bufio.NewReader(r.Body)
|
||||
data, err := br.ReadString('\n')
|
||||
if err != nil {
|
||||
s.manager.DelSession(token)
|
||||
close(session.rchan)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data = strings.TrimSuffix(data, "\n")
|
||||
if len(data) == 0 {
|
||||
s.manager.DelSession(token)
|
||||
close(session.rchan)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
s.manager.DelSession(token)
|
||||
close(session.rchan)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.closed:
|
||||
s.manager.DelSession(token)
|
||||
return
|
||||
case session.rchan <- b:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
case <-time.After(time.Second * 90):
|
||||
s.manager.DelSession(token)
|
||||
w.WriteHeader(http.StatusRequestTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) pollHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
m := parseAuth(r.Header.Get("Authorization"))
|
||||
if m["key"] != s.Key {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
token := m["token"]
|
||||
session := s.manager.GetSession(token)
|
||||
if session == nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case data, ok := <-session.wchan:
|
||||
if !ok {
|
||||
s.manager.DelSession(token)
|
||||
return // session is closed
|
||||
}
|
||||
bw := bufio.NewWriter(w)
|
||||
bw.WriteString(base64.StdEncoding.EncodeToString(data))
|
||||
bw.WriteString("\n")
|
||||
if err := bw.Flush(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
}
|
||||
case <-time.After(time.Second * 25):
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) upgrade(sess *session, r *http.Request) (net.Conn, error) {
|
||||
conn := newConn(sess)
|
||||
raddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
||||
if err != nil {
|
||||
raddr = &net.TCPAddr{}
|
||||
}
|
||||
conn.remoteAddr = raddr
|
||||
|
||||
laddr, err := net.ResolveTCPAddr("tcp", s.Addr)
|
||||
if err != nil {
|
||||
laddr = &net.TCPAddr{}
|
||||
}
|
||||
conn.localAddr = laddr
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func parseAuth(auth string) map[string]string {
|
||||
mkv := make(map[string]string)
|
||||
|
||||
for _, s := range strings.Split(auth, ";") {
|
||||
n := strings.Index(s, "=")
|
||||
if n < 0 {
|
||||
continue
|
||||
}
|
||||
mkv[strings.TrimSpace(s[:n])] = strings.TrimSpace(s[n+1:])
|
||||
}
|
||||
|
||||
return mkv
|
||||
}
|
80
vendor/github.com/ginuerzh/pht/session.go
generated
vendored
80
vendor/github.com/ginuerzh/pht/session.go
generated
vendored
@ -1,80 +0,0 @@
|
||||
package pht
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRChanLen = 64
|
||||
defaultWChanLen = 64
|
||||
)
|
||||
|
||||
type session struct {
|
||||
rchan chan []byte
|
||||
wchan chan []byte
|
||||
closed chan interface{}
|
||||
}
|
||||
|
||||
func newSession(rlen, wlen int) *session {
|
||||
if rlen <= 0 {
|
||||
rlen = defaultRChanLen
|
||||
}
|
||||
if wlen <= 0 {
|
||||
wlen = defaultWChanLen
|
||||
}
|
||||
|
||||
return &session{
|
||||
rchan: make(chan []byte, rlen),
|
||||
wchan: make(chan []byte, wlen),
|
||||
closed: make(chan interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
type sessionManager struct {
|
||||
sessions map[string]*session
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
func newSessionManager() *sessionManager {
|
||||
return &sessionManager{
|
||||
sessions: make(map[string]*session),
|
||||
mux: sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *sessionManager) NewSession(rlen, wlen int) (token string, s *session, err error) {
|
||||
var nonce [16]byte
|
||||
if _, err = rand.Read(nonce[:]); err != nil {
|
||||
return
|
||||
}
|
||||
token = hex.EncodeToString(nonce[:])
|
||||
s = newSession(rlen, wlen)
|
||||
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
m.sessions[token] = s
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *sessionManager) SetSession(token string, session *session) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
m.sessions[token] = session
|
||||
}
|
||||
|
||||
func (m *sessionManager) GetSession(token string) *session {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
|
||||
return m.sessions[token]
|
||||
}
|
||||
|
||||
func (m *sessionManager) DelSession(token string) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
|
||||
delete(m.sessions, token)
|
||||
}
|
3
vendor/github.com/ginuerzh/tls-dissector/handshake.go
generated
vendored
3
vendor/github.com/ginuerzh/tls-dissector/handshake.go
generated
vendored
@ -98,6 +98,9 @@ func (h *ClientHelloHandshake) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
|
||||
// extLen := int(binary.BigEndian.Uint16(b[pos : pos+2]))
|
||||
pos += 2
|
||||
if pos >= len(b) {
|
||||
return
|
||||
}
|
||||
|
||||
br := bytes.NewReader(b[pos:])
|
||||
for br.Len() > 0 {
|
||||
|
2
vendor/github.com/aead/chacha20/LICENSE → vendor/github.com/gobwas/glob/LICENSE
generated
vendored
2
vendor/github.com/aead/chacha20/LICENSE → vendor/github.com/gobwas/glob/LICENSE
generated
vendored
@ -1,6 +1,6 @@
|
||||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
26
vendor/github.com/gobwas/glob/bench.sh
generated
vendored
Executable file
26
vendor/github.com/gobwas/glob/bench.sh
generated
vendored
Executable file
@ -0,0 +1,26 @@
|
||||
#! /bin/bash
|
||||
|
||||
bench() {
|
||||
filename="/tmp/$1-$2.bench"
|
||||
if test -e "${filename}";
|
||||
then
|
||||
echo "Already exists ${filename}"
|
||||
else
|
||||
backup=`git rev-parse --abbrev-ref HEAD`
|
||||
git checkout $1
|
||||
echo -n "Creating ${filename}... "
|
||||
go test ./... -run=NONE -bench=$2 > "${filename}" -benchmem
|
||||
echo "OK"
|
||||
git checkout ${backup}
|
||||
sleep 5
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
to=$1
|
||||
current=`git rev-parse --abbrev-ref HEAD`
|
||||
|
||||
bench ${to} $2
|
||||
bench ${current} $2
|
||||
|
||||
benchcmp $3 "/tmp/${to}-$2.bench" "/tmp/${current}-$2.bench"
|
525
vendor/github.com/gobwas/glob/compiler/compiler.go
generated
vendored
Normal file
525
vendor/github.com/gobwas/glob/compiler/compiler.go
generated
vendored
Normal file
@ -0,0 +1,525 @@
|
||||
package compiler
|
||||
|
||||
// TODO use constructor with all matchers, and to their structs private
|
||||
// TODO glue multiple Text nodes (like after QuoteMeta)
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gobwas/glob/match"
|
||||
"github.com/gobwas/glob/syntax/ast"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
)
|
||||
|
||||
func optimizeMatcher(matcher match.Matcher) match.Matcher {
|
||||
switch m := matcher.(type) {
|
||||
|
||||
case match.Any:
|
||||
if len(m.Separators) == 0 {
|
||||
return match.NewSuper()
|
||||
}
|
||||
|
||||
case match.AnyOf:
|
||||
if len(m.Matchers) == 1 {
|
||||
return m.Matchers[0]
|
||||
}
|
||||
|
||||
return m
|
||||
|
||||
case match.List:
|
||||
if m.Not == false && len(m.List) == 1 {
|
||||
return match.NewText(string(m.List))
|
||||
}
|
||||
|
||||
return m
|
||||
|
||||
case match.BTree:
|
||||
m.Left = optimizeMatcher(m.Left)
|
||||
m.Right = optimizeMatcher(m.Right)
|
||||
|
||||
r, ok := m.Value.(match.Text)
|
||||
if !ok {
|
||||
return m
|
||||
}
|
||||
|
||||
var (
|
||||
leftNil = m.Left == nil
|
||||
rightNil = m.Right == nil
|
||||
)
|
||||
if leftNil && rightNil {
|
||||
return match.NewText(r.Str)
|
||||
}
|
||||
|
||||
_, leftSuper := m.Left.(match.Super)
|
||||
lp, leftPrefix := m.Left.(match.Prefix)
|
||||
la, leftAny := m.Left.(match.Any)
|
||||
|
||||
_, rightSuper := m.Right.(match.Super)
|
||||
rs, rightSuffix := m.Right.(match.Suffix)
|
||||
ra, rightAny := m.Right.(match.Any)
|
||||
|
||||
switch {
|
||||
case leftSuper && rightSuper:
|
||||
return match.NewContains(r.Str, false)
|
||||
|
||||
case leftSuper && rightNil:
|
||||
return match.NewSuffix(r.Str)
|
||||
|
||||
case rightSuper && leftNil:
|
||||
return match.NewPrefix(r.Str)
|
||||
|
||||
case leftNil && rightSuffix:
|
||||
return match.NewPrefixSuffix(r.Str, rs.Suffix)
|
||||
|
||||
case rightNil && leftPrefix:
|
||||
return match.NewPrefixSuffix(lp.Prefix, r.Str)
|
||||
|
||||
case rightNil && leftAny:
|
||||
return match.NewSuffixAny(r.Str, la.Separators)
|
||||
|
||||
case leftNil && rightAny:
|
||||
return match.NewPrefixAny(r.Str, ra.Separators)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
return matcher
|
||||
}
|
||||
|
||||
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
|
||||
if len(matchers) == 0 {
|
||||
return nil, fmt.Errorf("compile error: need at least one matcher")
|
||||
}
|
||||
if len(matchers) == 1 {
|
||||
return matchers[0], nil
|
||||
}
|
||||
if m := glueMatchers(matchers); m != nil {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
idx := -1
|
||||
maxLen := -1
|
||||
var val match.Matcher
|
||||
for i, matcher := range matchers {
|
||||
if l := matcher.Len(); l != -1 && l >= maxLen {
|
||||
maxLen = l
|
||||
idx = i
|
||||
val = matcher
|
||||
}
|
||||
}
|
||||
|
||||
if val == nil { // not found matcher with static length
|
||||
r, err := compileMatchers(matchers[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return match.NewBTree(matchers[0], nil, r), nil
|
||||
}
|
||||
|
||||
left := matchers[:idx]
|
||||
var right []match.Matcher
|
||||
if len(matchers) > idx+1 {
|
||||
right = matchers[idx+1:]
|
||||
}
|
||||
|
||||
var l, r match.Matcher
|
||||
var err error
|
||||
if len(left) > 0 {
|
||||
l, err = compileMatchers(left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(right) > 0 {
|
||||
r, err = compileMatchers(right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return match.NewBTree(val, l, r), nil
|
||||
}
|
||||
|
||||
func glueMatchers(matchers []match.Matcher) match.Matcher {
|
||||
if m := glueMatchersAsEvery(matchers); m != nil {
|
||||
return m
|
||||
}
|
||||
if m := glueMatchersAsRow(matchers); m != nil {
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func glueMatchersAsRow(matchers []match.Matcher) match.Matcher {
|
||||
if len(matchers) <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
c []match.Matcher
|
||||
l int
|
||||
)
|
||||
for _, matcher := range matchers {
|
||||
if ml := matcher.Len(); ml == -1 {
|
||||
return nil
|
||||
} else {
|
||||
c = append(c, matcher)
|
||||
l += ml
|
||||
}
|
||||
}
|
||||
return match.NewRow(l, c...)
|
||||
}
|
||||
|
||||
func glueMatchersAsEvery(matchers []match.Matcher) match.Matcher {
|
||||
if len(matchers) <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
hasAny bool
|
||||
hasSuper bool
|
||||
hasSingle bool
|
||||
min int
|
||||
separator []rune
|
||||
)
|
||||
|
||||
for i, matcher := range matchers {
|
||||
var sep []rune
|
||||
|
||||
switch m := matcher.(type) {
|
||||
case match.Super:
|
||||
sep = []rune{}
|
||||
hasSuper = true
|
||||
|
||||
case match.Any:
|
||||
sep = m.Separators
|
||||
hasAny = true
|
||||
|
||||
case match.Single:
|
||||
sep = m.Separators
|
||||
hasSingle = true
|
||||
min++
|
||||
|
||||
case match.List:
|
||||
if !m.Not {
|
||||
return nil
|
||||
}
|
||||
sep = m.List
|
||||
hasSingle = true
|
||||
min++
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
// initialize
|
||||
if i == 0 {
|
||||
separator = sep
|
||||
}
|
||||
|
||||
if runes.Equal(sep, separator) {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if hasSuper && !hasAny && !hasSingle {
|
||||
return match.NewSuper()
|
||||
}
|
||||
|
||||
if hasAny && !hasSuper && !hasSingle {
|
||||
return match.NewAny(separator)
|
||||
}
|
||||
|
||||
if (hasAny || hasSuper) && min > 0 && len(separator) == 0 {
|
||||
return match.NewMin(min)
|
||||
}
|
||||
|
||||
every := match.NewEveryOf()
|
||||
|
||||
if min > 0 {
|
||||
every.Add(match.NewMin(min))
|
||||
|
||||
if !hasAny && !hasSuper {
|
||||
every.Add(match.NewMax(min))
|
||||
}
|
||||
}
|
||||
|
||||
if len(separator) > 0 {
|
||||
every.Add(match.NewContains(string(separator), true))
|
||||
}
|
||||
|
||||
return every
|
||||
}
|
||||
|
||||
func minimizeMatchers(matchers []match.Matcher) []match.Matcher {
|
||||
var done match.Matcher
|
||||
var left, right, count int
|
||||
|
||||
for l := 0; l < len(matchers); l++ {
|
||||
for r := len(matchers); r > l; r-- {
|
||||
if glued := glueMatchers(matchers[l:r]); glued != nil {
|
||||
var swap bool
|
||||
|
||||
if done == nil {
|
||||
swap = true
|
||||
} else {
|
||||
cl, gl := done.Len(), glued.Len()
|
||||
swap = cl > -1 && gl > -1 && gl > cl
|
||||
swap = swap || count < r-l
|
||||
}
|
||||
|
||||
if swap {
|
||||
done = glued
|
||||
left = l
|
||||
right = r
|
||||
count = r - l
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if done == nil {
|
||||
return matchers
|
||||
}
|
||||
|
||||
next := append(append([]match.Matcher{}, matchers[:left]...), done)
|
||||
if right < len(matchers) {
|
||||
next = append(next, matchers[right:]...)
|
||||
}
|
||||
|
||||
if len(next) == len(matchers) {
|
||||
return next
|
||||
}
|
||||
|
||||
return minimizeMatchers(next)
|
||||
}
|
||||
|
||||
// minimizeAnyOf tries to apply some heuristics to minimize number of nodes in given tree
|
||||
func minimizeTree(tree *ast.Node) *ast.Node {
|
||||
switch tree.Kind {
|
||||
case ast.KindAnyOf:
|
||||
return minimizeTreeAnyOf(tree)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// minimizeAnyOf tries to find common children of given node of AnyOf pattern
|
||||
// it searches for common children from left and from right
|
||||
// if any common children are found – then it returns new optimized ast tree
|
||||
// else it returns nil
|
||||
func minimizeTreeAnyOf(tree *ast.Node) *ast.Node {
|
||||
if !areOfSameKind(tree.Children, ast.KindPattern) {
|
||||
return nil
|
||||
}
|
||||
|
||||
commonLeft, commonRight := commonChildren(tree.Children)
|
||||
commonLeftCount, commonRightCount := len(commonLeft), len(commonRight)
|
||||
if commonLeftCount == 0 && commonRightCount == 0 { // there are no common parts
|
||||
return nil
|
||||
}
|
||||
|
||||
var result []*ast.Node
|
||||
if commonLeftCount > 0 {
|
||||
result = append(result, ast.NewNode(ast.KindPattern, nil, commonLeft...))
|
||||
}
|
||||
|
||||
var anyOf []*ast.Node
|
||||
for _, child := range tree.Children {
|
||||
reuse := child.Children[commonLeftCount : len(child.Children)-commonRightCount]
|
||||
var node *ast.Node
|
||||
if len(reuse) == 0 {
|
||||
// this pattern is completely reduced by commonLeft and commonRight patterns
|
||||
// so it become nothing
|
||||
node = ast.NewNode(ast.KindNothing, nil)
|
||||
} else {
|
||||
node = ast.NewNode(ast.KindPattern, nil, reuse...)
|
||||
}
|
||||
anyOf = appendIfUnique(anyOf, node)
|
||||
}
|
||||
switch {
|
||||
case len(anyOf) == 1 && anyOf[0].Kind != ast.KindNothing:
|
||||
result = append(result, anyOf[0])
|
||||
case len(anyOf) > 1:
|
||||
result = append(result, ast.NewNode(ast.KindAnyOf, nil, anyOf...))
|
||||
}
|
||||
|
||||
if commonRightCount > 0 {
|
||||
result = append(result, ast.NewNode(ast.KindPattern, nil, commonRight...))
|
||||
}
|
||||
|
||||
return ast.NewNode(ast.KindPattern, nil, result...)
|
||||
}
|
||||
|
||||
func commonChildren(nodes []*ast.Node) (commonLeft, commonRight []*ast.Node) {
|
||||
if len(nodes) <= 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// find node that has least number of children
|
||||
idx := leastChildren(nodes)
|
||||
if idx == -1 {
|
||||
return
|
||||
}
|
||||
tree := nodes[idx]
|
||||
treeLength := len(tree.Children)
|
||||
|
||||
// allocate max able size for rightCommon slice
|
||||
// to get ability insert elements in reverse order (from end to start)
|
||||
// without sorting
|
||||
commonRight = make([]*ast.Node, treeLength)
|
||||
lastRight := treeLength // will use this to get results as commonRight[lastRight:]
|
||||
|
||||
var (
|
||||
breakLeft bool
|
||||
breakRight bool
|
||||
commonTotal int
|
||||
)
|
||||
for i, j := 0, treeLength-1; commonTotal < treeLength && j >= 0 && !(breakLeft && breakRight); i, j = i+1, j-1 {
|
||||
treeLeft := tree.Children[i]
|
||||
treeRight := tree.Children[j]
|
||||
|
||||
for k := 0; k < len(nodes) && !(breakLeft && breakRight); k++ {
|
||||
// skip least children node
|
||||
if k == idx {
|
||||
continue
|
||||
}
|
||||
|
||||
restLeft := nodes[k].Children[i]
|
||||
restRight := nodes[k].Children[j+len(nodes[k].Children)-treeLength]
|
||||
|
||||
breakLeft = breakLeft || !treeLeft.Equal(restLeft)
|
||||
|
||||
// disable searching for right common parts, if left part is already overlapping
|
||||
breakRight = breakRight || (!breakLeft && j <= i)
|
||||
breakRight = breakRight || !treeRight.Equal(restRight)
|
||||
}
|
||||
|
||||
if !breakLeft {
|
||||
commonTotal++
|
||||
commonLeft = append(commonLeft, treeLeft)
|
||||
}
|
||||
if !breakRight {
|
||||
commonTotal++
|
||||
lastRight = j
|
||||
commonRight[j] = treeRight
|
||||
}
|
||||
}
|
||||
|
||||
commonRight = commonRight[lastRight:]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func appendIfUnique(target []*ast.Node, val *ast.Node) []*ast.Node {
|
||||
for _, n := range target {
|
||||
if reflect.DeepEqual(n, val) {
|
||||
return target
|
||||
}
|
||||
}
|
||||
return append(target, val)
|
||||
}
|
||||
|
||||
func areOfSameKind(nodes []*ast.Node, kind ast.Kind) bool {
|
||||
for _, n := range nodes {
|
||||
if n.Kind != kind {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func leastChildren(nodes []*ast.Node) int {
|
||||
min := -1
|
||||
idx := -1
|
||||
for i, n := range nodes {
|
||||
if idx == -1 || (len(n.Children) < min) {
|
||||
min = len(n.Children)
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
func compileTreeChildren(tree *ast.Node, sep []rune) ([]match.Matcher, error) {
|
||||
var matchers []match.Matcher
|
||||
for _, desc := range tree.Children {
|
||||
m, err := compile(desc, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, optimizeMatcher(m))
|
||||
}
|
||||
return matchers, nil
|
||||
}
|
||||
|
||||
func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) {
|
||||
switch tree.Kind {
|
||||
case ast.KindAnyOf:
|
||||
// todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go)
|
||||
if n := minimizeTree(tree); n != nil {
|
||||
return compile(n, sep)
|
||||
}
|
||||
matchers, err := compileTreeChildren(tree, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return match.NewAnyOf(matchers...), nil
|
||||
|
||||
case ast.KindPattern:
|
||||
if len(tree.Children) == 0 {
|
||||
return match.NewNothing(), nil
|
||||
}
|
||||
matchers, err := compileTreeChildren(tree, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, err = compileMatchers(minimizeMatchers(matchers))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case ast.KindAny:
|
||||
m = match.NewAny(sep)
|
||||
|
||||
case ast.KindSuper:
|
||||
m = match.NewSuper()
|
||||
|
||||
case ast.KindSingle:
|
||||
m = match.NewSingle(sep)
|
||||
|
||||
case ast.KindNothing:
|
||||
m = match.NewNothing()
|
||||
|
||||
case ast.KindList:
|
||||
l := tree.Value.(ast.List)
|
||||
m = match.NewList([]rune(l.Chars), l.Not)
|
||||
|
||||
case ast.KindRange:
|
||||
r := tree.Value.(ast.Range)
|
||||
m = match.NewRange(r.Lo, r.Hi, r.Not)
|
||||
|
||||
case ast.KindText:
|
||||
t := tree.Value.(ast.Text)
|
||||
m = match.NewText(t.Text)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("could not compile tree: unknown node type")
|
||||
}
|
||||
|
||||
return optimizeMatcher(m), nil
|
||||
}
|
||||
|
||||
func Compile(tree *ast.Node, sep []rune) (match.Matcher, error) {
|
||||
m, err := compile(tree, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
80
vendor/github.com/gobwas/glob/glob.go
generated
vendored
Normal file
80
vendor/github.com/gobwas/glob/glob.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
package glob
|
||||
|
||||
import (
|
||||
"github.com/gobwas/glob/compiler"
|
||||
"github.com/gobwas/glob/syntax"
|
||||
)
|
||||
|
||||
// Glob represents compiled glob pattern.
|
||||
type Glob interface {
|
||||
Match(string) bool
|
||||
}
|
||||
|
||||
// Compile creates Glob for given pattern and strings (if any present after pattern) as separators.
|
||||
// The pattern syntax is:
|
||||
//
|
||||
// pattern:
|
||||
// { term }
|
||||
//
|
||||
// term:
|
||||
// `*` matches any sequence of non-separator characters
|
||||
// `**` matches any sequence of characters
|
||||
// `?` matches any single non-separator character
|
||||
// `[` [ `!` ] { character-range } `]`
|
||||
// character class (must be non-empty)
|
||||
// `{` pattern-list `}`
|
||||
// pattern alternatives
|
||||
// c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`)
|
||||
// `\` c matches character c
|
||||
//
|
||||
// character-range:
|
||||
// c matches character c (c != `\\`, `-`, `]`)
|
||||
// `\` c matches character c
|
||||
// lo `-` hi matches character c for lo <= c <= hi
|
||||
//
|
||||
// pattern-list:
|
||||
// pattern { `,` pattern }
|
||||
// comma-separated (without spaces) patterns
|
||||
//
|
||||
func Compile(pattern string, separators ...rune) (Glob, error) {
|
||||
ast, err := syntax.Parse(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matcher, err := compiler.Compile(ast, separators)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return matcher, nil
|
||||
}
|
||||
|
||||
// MustCompile is the same as Compile, except that if Compile returns error, this will panic
|
||||
func MustCompile(pattern string, separators ...rune) Glob {
|
||||
g, err := Compile(pattern, separators...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// QuoteMeta returns a string that quotes all glob pattern meta characters
|
||||
// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`.
|
||||
func QuoteMeta(s string) string {
|
||||
b := make([]byte, 2*len(s))
|
||||
|
||||
// a byte loop is correct because all meta characters are ASCII
|
||||
j := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if syntax.Special(s[i]) {
|
||||
b[j] = '\\'
|
||||
j++
|
||||
}
|
||||
b[j] = s[i]
|
||||
j++
|
||||
}
|
||||
|
||||
return string(b[0:j])
|
||||
}
|
45
vendor/github.com/gobwas/glob/match/any.go
generated
vendored
Normal file
45
vendor/github.com/gobwas/glob/match/any.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/strings"
|
||||
)
|
||||
|
||||
type Any struct {
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewAny(s []rune) Any {
|
||||
return Any{s}
|
||||
}
|
||||
|
||||
func (self Any) Match(s string) bool {
|
||||
return strings.IndexAnyRunes(s, self.Separators) == -1
|
||||
}
|
||||
|
||||
func (self Any) Index(s string) (int, []int) {
|
||||
found := strings.IndexAnyRunes(s, self.Separators)
|
||||
switch found {
|
||||
case -1:
|
||||
case 0:
|
||||
return 0, segments0
|
||||
default:
|
||||
s = s[:found]
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(s))
|
||||
for i := range s {
|
||||
segments = append(segments, i)
|
||||
}
|
||||
segments = append(segments, len(s))
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Any) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Any) String() string {
|
||||
return fmt.Sprintf("<any:![%s]>", string(self.Separators))
|
||||
}
|
82
vendor/github.com/gobwas/glob/match/any_of.go
generated
vendored
Normal file
82
vendor/github.com/gobwas/glob/match/any_of.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package match
|
||||
|
||||
import "fmt"
|
||||
|
||||
type AnyOf struct {
|
||||
Matchers Matchers
|
||||
}
|
||||
|
||||
func NewAnyOf(m ...Matcher) AnyOf {
|
||||
return AnyOf{Matchers(m)}
|
||||
}
|
||||
|
||||
func (self *AnyOf) Add(m Matcher) error {
|
||||
self.Matchers = append(self.Matchers, m)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self AnyOf) Match(s string) bool {
|
||||
for _, m := range self.Matchers {
|
||||
if m.Match(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self AnyOf) Index(s string) (int, []int) {
|
||||
index := -1
|
||||
|
||||
segments := acquireSegments(len(s))
|
||||
for _, m := range self.Matchers {
|
||||
idx, seg := m.Index(s)
|
||||
if idx == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if index == -1 || idx < index {
|
||||
index = idx
|
||||
segments = append(segments[:0], seg...)
|
||||
continue
|
||||
}
|
||||
|
||||
if idx > index {
|
||||
continue
|
||||
}
|
||||
|
||||
// here idx == index
|
||||
segments = appendMerge(segments, seg)
|
||||
}
|
||||
|
||||
if index == -1 {
|
||||
releaseSegments(segments)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return index, segments
|
||||
}
|
||||
|
||||
func (self AnyOf) Len() (l int) {
|
||||
l = -1
|
||||
for _, m := range self.Matchers {
|
||||
ml := m.Len()
|
||||
switch {
|
||||
case l == -1:
|
||||
l = ml
|
||||
continue
|
||||
|
||||
case ml == -1:
|
||||
return -1
|
||||
|
||||
case l != ml:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self AnyOf) String() string {
|
||||
return fmt.Sprintf("<any_of:[%s]>", self.Matchers)
|
||||
}
|
185
vendor/github.com/gobwas/glob/match/btree.go
generated
vendored
Normal file
185
vendor/github.com/gobwas/glob/match/btree.go
generated
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type BTree struct {
|
||||
Value Matcher
|
||||
Left Matcher
|
||||
Right Matcher
|
||||
ValueLengthRunes int
|
||||
LeftLengthRunes int
|
||||
RightLengthRunes int
|
||||
LengthRunes int
|
||||
}
|
||||
|
||||
func NewBTree(Value, Left, Right Matcher) (tree BTree) {
|
||||
tree.Value = Value
|
||||
tree.Left = Left
|
||||
tree.Right = Right
|
||||
|
||||
lenOk := true
|
||||
if tree.ValueLengthRunes = Value.Len(); tree.ValueLengthRunes == -1 {
|
||||
lenOk = false
|
||||
}
|
||||
|
||||
if Left != nil {
|
||||
if tree.LeftLengthRunes = Left.Len(); tree.LeftLengthRunes == -1 {
|
||||
lenOk = false
|
||||
}
|
||||
}
|
||||
|
||||
if Right != nil {
|
||||
if tree.RightLengthRunes = Right.Len(); tree.RightLengthRunes == -1 {
|
||||
lenOk = false
|
||||
}
|
||||
}
|
||||
|
||||
if lenOk {
|
||||
tree.LengthRunes = tree.LeftLengthRunes + tree.ValueLengthRunes + tree.RightLengthRunes
|
||||
} else {
|
||||
tree.LengthRunes = -1
|
||||
}
|
||||
|
||||
return tree
|
||||
}
|
||||
|
||||
func (self BTree) Len() int {
|
||||
return self.LengthRunes
|
||||
}
|
||||
|
||||
// todo?
|
||||
func (self BTree) Index(s string) (index int, segments []int) {
|
||||
//inputLen := len(s)
|
||||
//// try to cut unnecessary parts
|
||||
//// by knowledge of length of right and left part
|
||||
//offset, limit := self.offsetLimit(inputLen)
|
||||
//for offset < limit {
|
||||
// // search for matching part in substring
|
||||
// vi, segments := self.Value.Index(s[offset:limit])
|
||||
// if index == -1 {
|
||||
// return -1, nil
|
||||
// }
|
||||
// if self.Left == nil {
|
||||
// if index != offset {
|
||||
// return -1, nil
|
||||
// }
|
||||
// } else {
|
||||
// left := s[:offset+vi]
|
||||
// i := self.Left.IndexSuffix(left)
|
||||
// if i == -1 {
|
||||
// return -1, nil
|
||||
// }
|
||||
// index = i
|
||||
// }
|
||||
// if self.Right != nil {
|
||||
// for _, seg := range segments {
|
||||
// right := s[:offset+vi+seg]
|
||||
// }
|
||||
// }
|
||||
|
||||
// l := s[:offset+index]
|
||||
// var left bool
|
||||
// if self.Left != nil {
|
||||
// left = self.Left.Index(l)
|
||||
// } else {
|
||||
// left = l == ""
|
||||
// }
|
||||
//}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self BTree) Match(s string) bool {
|
||||
inputLen := len(s)
|
||||
// try to cut unnecessary parts
|
||||
// by knowledge of length of right and left part
|
||||
offset, limit := self.offsetLimit(inputLen)
|
||||
|
||||
for offset < limit {
|
||||
// search for matching part in substring
|
||||
index, segments := self.Value.Index(s[offset:limit])
|
||||
if index == -1 {
|
||||
releaseSegments(segments)
|
||||
return false
|
||||
}
|
||||
|
||||
l := s[:offset+index]
|
||||
var left bool
|
||||
if self.Left != nil {
|
||||
left = self.Left.Match(l)
|
||||
} else {
|
||||
left = l == ""
|
||||
}
|
||||
|
||||
if left {
|
||||
for i := len(segments) - 1; i >= 0; i-- {
|
||||
length := segments[i]
|
||||
|
||||
var right bool
|
||||
var r string
|
||||
// if there is no string for the right branch
|
||||
if inputLen <= offset+index+length {
|
||||
r = ""
|
||||
} else {
|
||||
r = s[offset+index+length:]
|
||||
}
|
||||
|
||||
if self.Right != nil {
|
||||
right = self.Right.Match(r)
|
||||
} else {
|
||||
right = r == ""
|
||||
}
|
||||
|
||||
if right {
|
||||
releaseSegments(segments)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, step := utf8.DecodeRuneInString(s[offset+index:])
|
||||
offset += index + step
|
||||
|
||||
releaseSegments(segments)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self BTree) offsetLimit(inputLen int) (offset int, limit int) {
|
||||
// self.Length, self.RLen and self.LLen are values meaning the length of runes for each part
|
||||
// here we manipulating byte length for better optimizations
|
||||
// but these checks still works, cause minLen of 1-rune string is 1 byte.
|
||||
if self.LengthRunes != -1 && self.LengthRunes > inputLen {
|
||||
return 0, 0
|
||||
}
|
||||
if self.LeftLengthRunes >= 0 {
|
||||
offset = self.LeftLengthRunes
|
||||
}
|
||||
if self.RightLengthRunes >= 0 {
|
||||
limit = inputLen - self.RightLengthRunes
|
||||
} else {
|
||||
limit = inputLen
|
||||
}
|
||||
return offset, limit
|
||||
}
|
||||
|
||||
func (self BTree) String() string {
|
||||
const n string = "<nil>"
|
||||
var l, r string
|
||||
if self.Left == nil {
|
||||
l = n
|
||||
} else {
|
||||
l = self.Left.String()
|
||||
}
|
||||
if self.Right == nil {
|
||||
r = n
|
||||
} else {
|
||||
r = self.Right.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<btree:[%s<-%s->%s]>", l, self.Value, r)
|
||||
}
|
58
vendor/github.com/gobwas/glob/match/contains.go
generated
vendored
Normal file
58
vendor/github.com/gobwas/glob/match/contains.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Contains struct {
|
||||
Needle string
|
||||
Not bool
|
||||
}
|
||||
|
||||
func NewContains(needle string, not bool) Contains {
|
||||
return Contains{needle, not}
|
||||
}
|
||||
|
||||
func (self Contains) Match(s string) bool {
|
||||
return strings.Contains(s, self.Needle) != self.Not
|
||||
}
|
||||
|
||||
func (self Contains) Index(s string) (int, []int) {
|
||||
var offset int
|
||||
|
||||
idx := strings.Index(s, self.Needle)
|
||||
|
||||
if !self.Not {
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
offset = idx + len(self.Needle)
|
||||
if len(s) <= offset {
|
||||
return 0, []int{offset}
|
||||
}
|
||||
s = s[offset:]
|
||||
} else if idx != -1 {
|
||||
s = s[:idx]
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(s) + 1)
|
||||
for i := range s {
|
||||
segments = append(segments, offset+i)
|
||||
}
|
||||
|
||||
return 0, append(segments, offset+len(s))
|
||||
}
|
||||
|
||||
func (self Contains) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Contains) String() string {
|
||||
var not string
|
||||
if self.Not {
|
||||
not = "!"
|
||||
}
|
||||
return fmt.Sprintf("<contains:%s[%s]>", not, self.Needle)
|
||||
}
|
99
vendor/github.com/gobwas/glob/match/every_of.go
generated
vendored
Normal file
99
vendor/github.com/gobwas/glob/match/every_of.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type EveryOf struct {
|
||||
Matchers Matchers
|
||||
}
|
||||
|
||||
func NewEveryOf(m ...Matcher) EveryOf {
|
||||
return EveryOf{Matchers(m)}
|
||||
}
|
||||
|
||||
func (self *EveryOf) Add(m Matcher) error {
|
||||
self.Matchers = append(self.Matchers, m)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self EveryOf) Len() (l int) {
|
||||
for _, m := range self.Matchers {
|
||||
if ml := m.Len(); l > 0 {
|
||||
l += ml
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self EveryOf) Index(s string) (int, []int) {
|
||||
var index int
|
||||
var offset int
|
||||
|
||||
// make `in` with cap as len(s),
|
||||
// cause it is the maximum size of output segments values
|
||||
next := acquireSegments(len(s))
|
||||
current := acquireSegments(len(s))
|
||||
|
||||
sub := s
|
||||
for i, m := range self.Matchers {
|
||||
idx, seg := m.Index(sub)
|
||||
if idx == -1 {
|
||||
releaseSegments(next)
|
||||
releaseSegments(current)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
// we use copy here instead of `current = seg`
|
||||
// cause seg is a slice from reusable buffer `in`
|
||||
// and it could be overwritten in next iteration
|
||||
current = append(current, seg...)
|
||||
} else {
|
||||
// clear the next
|
||||
next = next[:0]
|
||||
|
||||
delta := index - (idx + offset)
|
||||
for _, ex := range current {
|
||||
for _, n := range seg {
|
||||
if ex+delta == n {
|
||||
next = append(next, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(next) == 0 {
|
||||
releaseSegments(next)
|
||||
releaseSegments(current)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
current = append(current[:0], next...)
|
||||
}
|
||||
|
||||
index = idx + offset
|
||||
sub = s[index:]
|
||||
offset += idx
|
||||
}
|
||||
|
||||
releaseSegments(next)
|
||||
|
||||
return index, current
|
||||
}
|
||||
|
||||
func (self EveryOf) Match(s string) bool {
|
||||
for _, m := range self.Matchers {
|
||||
if !m.Match(s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self EveryOf) String() string {
|
||||
return fmt.Sprintf("<every_of:[%s]>", self.Matchers)
|
||||
}
|
49
vendor/github.com/gobwas/glob/match/list.go
generated
vendored
Normal file
49
vendor/github.com/gobwas/glob/match/list.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type List struct {
|
||||
List []rune
|
||||
Not bool
|
||||
}
|
||||
|
||||
func NewList(list []rune, not bool) List {
|
||||
return List{list, not}
|
||||
}
|
||||
|
||||
func (self List) Match(s string) bool {
|
||||
r, w := utf8.DecodeRuneInString(s)
|
||||
if len(s) > w {
|
||||
return false
|
||||
}
|
||||
|
||||
inList := runes.IndexRune(self.List, r) != -1
|
||||
return inList == !self.Not
|
||||
}
|
||||
|
||||
func (self List) Len() int {
|
||||
return lenOne
|
||||
}
|
||||
|
||||
func (self List) Index(s string) (int, []int) {
|
||||
for i, r := range s {
|
||||
if self.Not == (runes.IndexRune(self.List, r) == -1) {
|
||||
return i, segmentsByRuneLength[utf8.RuneLen(r)]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self List) String() string {
|
||||
var not string
|
||||
if self.Not {
|
||||
not = "!"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<list:%s[%s]>", not, string(self.List))
|
||||
}
|
81
vendor/github.com/gobwas/glob/match/match.go
generated
vendored
Normal file
81
vendor/github.com/gobwas/glob/match/match.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
package match
|
||||
|
||||
// todo common table of rune's length
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const lenOne = 1
|
||||
const lenZero = 0
|
||||
const lenNo = -1
|
||||
|
||||
type Matcher interface {
|
||||
Match(string) bool
|
||||
Index(string) (int, []int)
|
||||
Len() int
|
||||
String() string
|
||||
}
|
||||
|
||||
type Matchers []Matcher
|
||||
|
||||
func (m Matchers) String() string {
|
||||
var s []string
|
||||
for _, matcher := range m {
|
||||
s = append(s, fmt.Sprint(matcher))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", strings.Join(s, ","))
|
||||
}
|
||||
|
||||
// appendMerge merges and sorts given already SORTED and UNIQUE segments.
|
||||
func appendMerge(target, sub []int) []int {
|
||||
lt, ls := len(target), len(sub)
|
||||
out := make([]int, 0, lt+ls)
|
||||
|
||||
for x, y := 0, 0; x < lt || y < ls; {
|
||||
if x >= lt {
|
||||
out = append(out, sub[y:]...)
|
||||
break
|
||||
}
|
||||
|
||||
if y >= ls {
|
||||
out = append(out, target[x:]...)
|
||||
break
|
||||
}
|
||||
|
||||
xValue := target[x]
|
||||
yValue := sub[y]
|
||||
|
||||
switch {
|
||||
|
||||
case xValue == yValue:
|
||||
out = append(out, xValue)
|
||||
x++
|
||||
y++
|
||||
|
||||
case xValue < yValue:
|
||||
out = append(out, xValue)
|
||||
x++
|
||||
|
||||
case yValue < xValue:
|
||||
out = append(out, yValue)
|
||||
y++
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
target = append(target[:0], out...)
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
func reverseSegments(input []int) {
|
||||
l := len(input)
|
||||
m := l / 2
|
||||
|
||||
for i := 0; i < m; i++ {
|
||||
input[i], input[l-i-1] = input[l-i-1], input[i]
|
||||
}
|
||||
}
|
49
vendor/github.com/gobwas/glob/match/max.go
generated
vendored
Normal file
49
vendor/github.com/gobwas/glob/match/max.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Max struct {
|
||||
Limit int
|
||||
}
|
||||
|
||||
func NewMax(l int) Max {
|
||||
return Max{l}
|
||||
}
|
||||
|
||||
func (self Max) Match(s string) bool {
|
||||
var l int
|
||||
for range s {
|
||||
l += 1
|
||||
if l > self.Limit {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self Max) Index(s string) (int, []int) {
|
||||
segments := acquireSegments(self.Limit + 1)
|
||||
segments = append(segments, 0)
|
||||
var count int
|
||||
for i, r := range s {
|
||||
count++
|
||||
if count > self.Limit {
|
||||
break
|
||||
}
|
||||
segments = append(segments, i+utf8.RuneLen(r))
|
||||
}
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Max) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Max) String() string {
|
||||
return fmt.Sprintf("<max:%d>", self.Limit)
|
||||
}
|
57
vendor/github.com/gobwas/glob/match/min.go
generated
vendored
Normal file
57
vendor/github.com/gobwas/glob/match/min.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Min struct {
|
||||
Limit int
|
||||
}
|
||||
|
||||
func NewMin(l int) Min {
|
||||
return Min{l}
|
||||
}
|
||||
|
||||
func (self Min) Match(s string) bool {
|
||||
var l int
|
||||
for range s {
|
||||
l += 1
|
||||
if l >= self.Limit {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self Min) Index(s string) (int, []int) {
|
||||
var count int
|
||||
|
||||
c := len(s) - self.Limit + 1
|
||||
if c <= 0 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
segments := acquireSegments(c)
|
||||
for i, r := range s {
|
||||
count++
|
||||
if count >= self.Limit {
|
||||
segments = append(segments, i+utf8.RuneLen(r))
|
||||
}
|
||||
}
|
||||
|
||||
if len(segments) == 0 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Min) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Min) String() string {
|
||||
return fmt.Sprintf("<min:%d>", self.Limit)
|
||||
}
|
27
vendor/github.com/gobwas/glob/match/nothing.go
generated
vendored
Normal file
27
vendor/github.com/gobwas/glob/match/nothing.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Nothing struct{}
|
||||
|
||||
func NewNothing() Nothing {
|
||||
return Nothing{}
|
||||
}
|
||||
|
||||
func (self Nothing) Match(s string) bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
func (self Nothing) Index(s string) (int, []int) {
|
||||
return 0, segments0
|
||||
}
|
||||
|
||||
func (self Nothing) Len() int {
|
||||
return lenZero
|
||||
}
|
||||
|
||||
func (self Nothing) String() string {
|
||||
return fmt.Sprintf("<nothing>")
|
||||
}
|
50
vendor/github.com/gobwas/glob/match/prefix.go
generated
vendored
Normal file
50
vendor/github.com/gobwas/glob/match/prefix.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Prefix struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func NewPrefix(p string) Prefix {
|
||||
return Prefix{p}
|
||||
}
|
||||
|
||||
func (self Prefix) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Prefix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
length := len(self.Prefix)
|
||||
var sub string
|
||||
if len(s) > idx+length {
|
||||
sub = s[idx+length:]
|
||||
} else {
|
||||
sub = ""
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(sub) + 1)
|
||||
segments = append(segments, length)
|
||||
for i, r := range sub {
|
||||
segments = append(segments, length+i+utf8.RuneLen(r))
|
||||
}
|
||||
|
||||
return idx, segments
|
||||
}
|
||||
|
||||
func (self Prefix) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Prefix) Match(s string) bool {
|
||||
return strings.HasPrefix(s, self.Prefix)
|
||||
}
|
||||
|
||||
func (self Prefix) String() string {
|
||||
return fmt.Sprintf("<prefix:%s>", self.Prefix)
|
||||
}
|
55
vendor/github.com/gobwas/glob/match/prefix_any.go
generated
vendored
Normal file
55
vendor/github.com/gobwas/glob/match/prefix_any.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
sutil "github.com/gobwas/glob/util/strings"
|
||||
)
|
||||
|
||||
type PrefixAny struct {
|
||||
Prefix string
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewPrefixAny(s string, sep []rune) PrefixAny {
|
||||
return PrefixAny{s, sep}
|
||||
}
|
||||
|
||||
func (self PrefixAny) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Prefix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
n := len(self.Prefix)
|
||||
sub := s[idx+n:]
|
||||
i := sutil.IndexAnyRunes(sub, self.Separators)
|
||||
if i > -1 {
|
||||
sub = sub[:i]
|
||||
}
|
||||
|
||||
seg := acquireSegments(len(sub) + 1)
|
||||
seg = append(seg, n)
|
||||
for i, r := range sub {
|
||||
seg = append(seg, n+i+utf8.RuneLen(r))
|
||||
}
|
||||
|
||||
return idx, seg
|
||||
}
|
||||
|
||||
func (self PrefixAny) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self PrefixAny) Match(s string) bool {
|
||||
if !strings.HasPrefix(s, self.Prefix) {
|
||||
return false
|
||||
}
|
||||
return sutil.IndexAnyRunes(s[len(self.Prefix):], self.Separators) == -1
|
||||
}
|
||||
|
||||
func (self PrefixAny) String() string {
|
||||
return fmt.Sprintf("<prefix_any:%s![%s]>", self.Prefix, string(self.Separators))
|
||||
}
|
62
vendor/github.com/gobwas/glob/match/prefix_suffix.go
generated
vendored
Normal file
62
vendor/github.com/gobwas/glob/match/prefix_suffix.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PrefixSuffix struct {
|
||||
Prefix, Suffix string
|
||||
}
|
||||
|
||||
func NewPrefixSuffix(p, s string) PrefixSuffix {
|
||||
return PrefixSuffix{p, s}
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) Index(s string) (int, []int) {
|
||||
prefixIdx := strings.Index(s, self.Prefix)
|
||||
if prefixIdx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
suffixLen := len(self.Suffix)
|
||||
if suffixLen <= 0 {
|
||||
return prefixIdx, []int{len(s) - prefixIdx}
|
||||
}
|
||||
|
||||
if (len(s) - prefixIdx) <= 0 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
segments := acquireSegments(len(s) - prefixIdx)
|
||||
for sub := s[prefixIdx:]; ; {
|
||||
suffixIdx := strings.LastIndex(sub, self.Suffix)
|
||||
if suffixIdx == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
segments = append(segments, suffixIdx+suffixLen)
|
||||
sub = sub[:suffixIdx]
|
||||
}
|
||||
|
||||
if len(segments) == 0 {
|
||||
releaseSegments(segments)
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
reverseSegments(segments)
|
||||
|
||||
return prefixIdx, segments
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) Match(s string) bool {
|
||||
return strings.HasPrefix(s, self.Prefix) && strings.HasSuffix(s, self.Suffix)
|
||||
}
|
||||
|
||||
func (self PrefixSuffix) String() string {
|
||||
return fmt.Sprintf("<prefix_suffix:[%s,%s]>", self.Prefix, self.Suffix)
|
||||
}
|
48
vendor/github.com/gobwas/glob/match/range.go
generated
vendored
Normal file
48
vendor/github.com/gobwas/glob/match/range.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Range struct {
|
||||
Lo, Hi rune
|
||||
Not bool
|
||||
}
|
||||
|
||||
func NewRange(lo, hi rune, not bool) Range {
|
||||
return Range{lo, hi, not}
|
||||
}
|
||||
|
||||
func (self Range) Len() int {
|
||||
return lenOne
|
||||
}
|
||||
|
||||
func (self Range) Match(s string) bool {
|
||||
r, w := utf8.DecodeRuneInString(s)
|
||||
if len(s) > w {
|
||||
return false
|
||||
}
|
||||
|
||||
inRange := r >= self.Lo && r <= self.Hi
|
||||
|
||||
return inRange == !self.Not
|
||||
}
|
||||
|
||||
func (self Range) Index(s string) (int, []int) {
|
||||
for i, r := range s {
|
||||
if self.Not != (r >= self.Lo && r <= self.Hi) {
|
||||
return i, segmentsByRuneLength[utf8.RuneLen(r)]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self Range) String() string {
|
||||
var not string
|
||||
if self.Not {
|
||||
not = "!"
|
||||
}
|
||||
return fmt.Sprintf("<range:%s[%s,%s]>", not, string(self.Lo), string(self.Hi))
|
||||
}
|
77
vendor/github.com/gobwas/glob/match/row.go
generated
vendored
Normal file
77
vendor/github.com/gobwas/glob/match/row.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Row struct {
|
||||
Matchers Matchers
|
||||
RunesLength int
|
||||
Segments []int
|
||||
}
|
||||
|
||||
func NewRow(len int, m ...Matcher) Row {
|
||||
return Row{
|
||||
Matchers: Matchers(m),
|
||||
RunesLength: len,
|
||||
Segments: []int{len},
|
||||
}
|
||||
}
|
||||
|
||||
func (self Row) matchAll(s string) bool {
|
||||
var idx int
|
||||
for _, m := range self.Matchers {
|
||||
length := m.Len()
|
||||
|
||||
var next, i int
|
||||
for next = range s[idx:] {
|
||||
i++
|
||||
if i == length {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i < length || !m.Match(s[idx:idx+next+1]) {
|
||||
return false
|
||||
}
|
||||
|
||||
idx += next + 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self Row) lenOk(s string) bool {
|
||||
var i int
|
||||
for range s {
|
||||
i++
|
||||
if i > self.RunesLength {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return self.RunesLength == i
|
||||
}
|
||||
|
||||
func (self Row) Match(s string) bool {
|
||||
return self.lenOk(s) && self.matchAll(s)
|
||||
}
|
||||
|
||||
func (self Row) Len() (l int) {
|
||||
return self.RunesLength
|
||||
}
|
||||
|
||||
func (self Row) Index(s string) (int, []int) {
|
||||
for i := range s {
|
||||
if len(s[i:]) < self.RunesLength {
|
||||
break
|
||||
}
|
||||
if self.matchAll(s[i:]) {
|
||||
return i, self.Segments
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self Row) String() string {
|
||||
return fmt.Sprintf("<row_%d:[%s]>", self.RunesLength, self.Matchers)
|
||||
}
|
91
vendor/github.com/gobwas/glob/match/segments.go
generated
vendored
Normal file
91
vendor/github.com/gobwas/glob/match/segments.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type SomePool interface {
|
||||
Get() []int
|
||||
Put([]int)
|
||||
}
|
||||
|
||||
var segmentsPools [1024]sync.Pool
|
||||
|
||||
func toPowerOfTwo(v int) int {
|
||||
v--
|
||||
v |= v >> 1
|
||||
v |= v >> 2
|
||||
v |= v >> 4
|
||||
v |= v >> 8
|
||||
v |= v >> 16
|
||||
v++
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
const (
|
||||
cacheFrom = 16
|
||||
cacheToAndHigher = 1024
|
||||
cacheFromIndex = 15
|
||||
cacheToAndHigherIndex = 1023
|
||||
)
|
||||
|
||||
var (
|
||||
segments0 = []int{0}
|
||||
segments1 = []int{1}
|
||||
segments2 = []int{2}
|
||||
segments3 = []int{3}
|
||||
segments4 = []int{4}
|
||||
)
|
||||
|
||||
var segmentsByRuneLength [5][]int = [5][]int{
|
||||
0: segments0,
|
||||
1: segments1,
|
||||
2: segments2,
|
||||
3: segments3,
|
||||
4: segments4,
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i := cacheToAndHigher; i >= cacheFrom; i >>= 1 {
|
||||
func(i int) {
|
||||
segmentsPools[i-1] = sync.Pool{New: func() interface{} {
|
||||
return make([]int, 0, i)
|
||||
}}
|
||||
}(i)
|
||||
}
|
||||
}
|
||||
|
||||
func getTableIndex(c int) int {
|
||||
p := toPowerOfTwo(c)
|
||||
switch {
|
||||
case p >= cacheToAndHigher:
|
||||
return cacheToAndHigherIndex
|
||||
case p <= cacheFrom:
|
||||
return cacheFromIndex
|
||||
default:
|
||||
return p - 1
|
||||
}
|
||||
}
|
||||
|
||||
func acquireSegments(c int) []int {
|
||||
// make []int with less capacity than cacheFrom
|
||||
// is faster than acquiring it from pool
|
||||
if c < cacheFrom {
|
||||
return make([]int, 0, c)
|
||||
}
|
||||
|
||||
return segmentsPools[getTableIndex(c)].Get().([]int)[:0]
|
||||
}
|
||||
|
||||
func releaseSegments(s []int) {
|
||||
c := cap(s)
|
||||
|
||||
// make []int with less capacity than cacheFrom
|
||||
// is faster than acquiring it from pool
|
||||
if c < cacheFrom {
|
||||
return
|
||||
}
|
||||
|
||||
segmentsPools[getTableIndex(c)].Put(s)
|
||||
}
|
43
vendor/github.com/gobwas/glob/match/single.go
generated
vendored
Normal file
43
vendor/github.com/gobwas/glob/match/single.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// single represents ?
|
||||
type Single struct {
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewSingle(s []rune) Single {
|
||||
return Single{s}
|
||||
}
|
||||
|
||||
func (self Single) Match(s string) bool {
|
||||
r, w := utf8.DecodeRuneInString(s)
|
||||
if len(s) > w {
|
||||
return false
|
||||
}
|
||||
|
||||
return runes.IndexRune(self.Separators, r) == -1
|
||||
}
|
||||
|
||||
func (self Single) Len() int {
|
||||
return lenOne
|
||||
}
|
||||
|
||||
func (self Single) Index(s string) (int, []int) {
|
||||
for i, r := range s {
|
||||
if runes.IndexRune(self.Separators, r) == -1 {
|
||||
return i, segmentsByRuneLength[utf8.RuneLen(r)]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (self Single) String() string {
|
||||
return fmt.Sprintf("<single:![%s]>", string(self.Separators))
|
||||
}
|
35
vendor/github.com/gobwas/glob/match/suffix.go
generated
vendored
Normal file
35
vendor/github.com/gobwas/glob/match/suffix.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Suffix struct {
|
||||
Suffix string
|
||||
}
|
||||
|
||||
func NewSuffix(s string) Suffix {
|
||||
return Suffix{s}
|
||||
}
|
||||
|
||||
func (self Suffix) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Suffix) Match(s string) bool {
|
||||
return strings.HasSuffix(s, self.Suffix)
|
||||
}
|
||||
|
||||
func (self Suffix) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Suffix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return 0, []int{idx + len(self.Suffix)}
|
||||
}
|
||||
|
||||
func (self Suffix) String() string {
|
||||
return fmt.Sprintf("<suffix:%s>", self.Suffix)
|
||||
}
|
43
vendor/github.com/gobwas/glob/match/suffix_any.go
generated
vendored
Normal file
43
vendor/github.com/gobwas/glob/match/suffix_any.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sutil "github.com/gobwas/glob/util/strings"
|
||||
)
|
||||
|
||||
type SuffixAny struct {
|
||||
Suffix string
|
||||
Separators []rune
|
||||
}
|
||||
|
||||
func NewSuffixAny(s string, sep []rune) SuffixAny {
|
||||
return SuffixAny{s, sep}
|
||||
}
|
||||
|
||||
func (self SuffixAny) Index(s string) (int, []int) {
|
||||
idx := strings.Index(s, self.Suffix)
|
||||
if idx == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
i := sutil.LastIndexAnyRunes(s[:idx], self.Separators) + 1
|
||||
|
||||
return i, []int{idx + len(self.Suffix) - i}
|
||||
}
|
||||
|
||||
func (self SuffixAny) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self SuffixAny) Match(s string) bool {
|
||||
if !strings.HasSuffix(s, self.Suffix) {
|
||||
return false
|
||||
}
|
||||
return sutil.IndexAnyRunes(s[:len(s)-len(self.Suffix)], self.Separators) == -1
|
||||
}
|
||||
|
||||
func (self SuffixAny) String() string {
|
||||
return fmt.Sprintf("<suffix_any:![%s]%s>", string(self.Separators), self.Suffix)
|
||||
}
|
33
vendor/github.com/gobwas/glob/match/super.go
generated
vendored
Normal file
33
vendor/github.com/gobwas/glob/match/super.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Super struct{}
|
||||
|
||||
func NewSuper() Super {
|
||||
return Super{}
|
||||
}
|
||||
|
||||
func (self Super) Match(s string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self Super) Len() int {
|
||||
return lenNo
|
||||
}
|
||||
|
||||
func (self Super) Index(s string) (int, []int) {
|
||||
segments := acquireSegments(len(s) + 1)
|
||||
for i := range s {
|
||||
segments = append(segments, i)
|
||||
}
|
||||
segments = append(segments, len(s))
|
||||
|
||||
return 0, segments
|
||||
}
|
||||
|
||||
func (self Super) String() string {
|
||||
return fmt.Sprintf("<super>")
|
||||
}
|
45
vendor/github.com/gobwas/glob/match/text.go
generated
vendored
Normal file
45
vendor/github.com/gobwas/glob/match/text.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package match
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// raw represents raw string to match
|
||||
type Text struct {
|
||||
Str string
|
||||
RunesLength int
|
||||
BytesLength int
|
||||
Segments []int
|
||||
}
|
||||
|
||||
func NewText(s string) Text {
|
||||
return Text{
|
||||
Str: s,
|
||||
RunesLength: utf8.RuneCountInString(s),
|
||||
BytesLength: len(s),
|
||||
Segments: []int{len(s)},
|
||||
}
|
||||
}
|
||||
|
||||
func (self Text) Match(s string) bool {
|
||||
return self.Str == s
|
||||
}
|
||||
|
||||
func (self Text) Len() int {
|
||||
return self.RunesLength
|
||||
}
|
||||
|
||||
func (self Text) Index(s string) (int, []int) {
|
||||
index := strings.Index(s, self.Str)
|
||||
if index == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return index, self.Segments
|
||||
}
|
||||
|
||||
func (self Text) String() string {
|
||||
return fmt.Sprintf("<text:`%v`>", self.Str)
|
||||
}
|
148
vendor/github.com/gobwas/glob/readme.md
generated
vendored
Normal file
148
vendor/github.com/gobwas/glob/readme.md
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
# glob.[go](https://golang.org)
|
||||
|
||||
[![GoDoc][godoc-image]][godoc-url] [![Build Status][travis-image]][travis-url]
|
||||
|
||||
> Go Globbing Library.
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
go get github.com/gobwas/glob
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gobwas/glob"
|
||||
|
||||
func main() {
|
||||
var g glob.Glob
|
||||
|
||||
// create simple glob
|
||||
g = glob.MustCompile("*.github.com")
|
||||
g.Match("api.github.com") // true
|
||||
|
||||
// quote meta characters and then create simple glob
|
||||
g = glob.MustCompile(glob.QuoteMeta("*.github.com"))
|
||||
g.Match("*.github.com") // true
|
||||
|
||||
// create new glob with set of delimiters as ["."]
|
||||
g = glob.MustCompile("api.*.com", '.')
|
||||
g.Match("api.github.com") // true
|
||||
g.Match("api.gi.hub.com") // false
|
||||
|
||||
// create new glob with set of delimiters as ["."]
|
||||
// but now with super wildcard
|
||||
g = glob.MustCompile("api.**.com", '.')
|
||||
g.Match("api.github.com") // true
|
||||
g.Match("api.gi.hub.com") // true
|
||||
|
||||
// create glob with single symbol wildcard
|
||||
g = glob.MustCompile("?at")
|
||||
g.Match("cat") // true
|
||||
g.Match("fat") // true
|
||||
g.Match("at") // false
|
||||
|
||||
// create glob with single symbol wildcard and delimiters ['f']
|
||||
g = glob.MustCompile("?at", 'f')
|
||||
g.Match("cat") // true
|
||||
g.Match("fat") // false
|
||||
g.Match("at") // false
|
||||
|
||||
// create glob with character-list matchers
|
||||
g = glob.MustCompile("[abc]at")
|
||||
g.Match("cat") // true
|
||||
g.Match("bat") // true
|
||||
g.Match("fat") // false
|
||||
g.Match("at") // false
|
||||
|
||||
// create glob with character-list matchers
|
||||
g = glob.MustCompile("[!abc]at")
|
||||
g.Match("cat") // false
|
||||
g.Match("bat") // false
|
||||
g.Match("fat") // true
|
||||
g.Match("at") // false
|
||||
|
||||
// create glob with character-range matchers
|
||||
g = glob.MustCompile("[a-c]at")
|
||||
g.Match("cat") // true
|
||||
g.Match("bat") // true
|
||||
g.Match("fat") // false
|
||||
g.Match("at") // false
|
||||
|
||||
// create glob with character-range matchers
|
||||
g = glob.MustCompile("[!a-c]at")
|
||||
g.Match("cat") // false
|
||||
g.Match("bat") // false
|
||||
g.Match("fat") // true
|
||||
g.Match("at") // false
|
||||
|
||||
// create glob with pattern-alternatives list
|
||||
g = glob.MustCompile("{cat,bat,[fr]at}")
|
||||
g.Match("cat") // true
|
||||
g.Match("bat") // true
|
||||
g.Match("fat") // true
|
||||
g.Match("rat") // true
|
||||
g.Match("at") // false
|
||||
g.Match("zat") // false
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
This library is created for compile-once patterns. This means, that compilation could take time, but
|
||||
strings matching is done faster, than in case when always parsing template.
|
||||
|
||||
If you will not use compiled `glob.Glob` object, and do `g := glob.MustCompile(pattern); g.Match(...)` every time, then your code will be much more slower.
|
||||
|
||||
Run `go test -bench=.` from source root to see the benchmarks:
|
||||
|
||||
Pattern | Fixture | Match | Speed (ns/op)
|
||||
--------|---------|-------|--------------
|
||||
`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my cat has very bright eyes` | `true` | 432
|
||||
`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my dog has very bright eyes` | `false` | 199
|
||||
`https://*.google.*` | `https://account.google.com` | `true` | 96
|
||||
`https://*.google.*` | `https://google.com` | `false` | 66
|
||||
`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://yahoo.com` | `true` | 163
|
||||
`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://google.com` | `false` | 197
|
||||
`{https://*gobwas.com,http://exclude.gobwas.com}` | `https://safe.gobwas.com` | `true` | 22
|
||||
`{https://*gobwas.com,http://exclude.gobwas.com}` | `http://safe.gobwas.com` | `false` | 24
|
||||
`abc*` | `abcdef` | `true` | 8.15
|
||||
`abc*` | `af` | `false` | 5.68
|
||||
`*def` | `abcdef` | `true` | 8.84
|
||||
`*def` | `af` | `false` | 5.74
|
||||
`ab*ef` | `abcdef` | `true` | 15.2
|
||||
`ab*ef` | `af` | `false` | 10.4
|
||||
|
||||
The same things with `regexp` package:
|
||||
|
||||
Pattern | Fixture | Match | Speed (ns/op)
|
||||
--------|---------|-------|--------------
|
||||
`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my cat has very bright eyes` | `true` | 2553
|
||||
`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my dog has very bright eyes` | `false` | 1383
|
||||
`^https:\/\/.*\.google\..*$` | `https://account.google.com` | `true` | 1205
|
||||
`^https:\/\/.*\.google\..*$` | `https://google.com` | `false` | 767
|
||||
`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://yahoo.com` | `true` | 1435
|
||||
`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://google.com` | `false` | 1674
|
||||
`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `https://safe.gobwas.com` | `true` | 1039
|
||||
`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `http://safe.gobwas.com` | `false` | 272
|
||||
`^abc.*$` | `abcdef` | `true` | 237
|
||||
`^abc.*$` | `af` | `false` | 100
|
||||
`^.*def$` | `abcdef` | `true` | 464
|
||||
`^.*def$` | `af` | `false` | 265
|
||||
`^ab.*ef$` | `abcdef` | `true` | 375
|
||||
`^ab.*ef$` | `af` | `false` | 145
|
||||
|
||||
[godoc-image]: https://godoc.org/github.com/gobwas/glob?status.svg
|
||||
[godoc-url]: https://godoc.org/github.com/gobwas/glob
|
||||
[travis-image]: https://travis-ci.org/gobwas/glob.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/gobwas/glob
|
||||
|
||||
## Syntax
|
||||
|
||||
Syntax is inspired by [standard wildcards](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm),
|
||||
except that `**` is aka super-asterisk, that do not sensitive for separators.
|
122
vendor/github.com/gobwas/glob/syntax/ast/ast.go
generated
vendored
Normal file
122
vendor/github.com/gobwas/glob/syntax/ast/ast.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Parent *Node
|
||||
Children []*Node
|
||||
Value interface{}
|
||||
Kind Kind
|
||||
}
|
||||
|
||||
func NewNode(k Kind, v interface{}, ch ...*Node) *Node {
|
||||
n := &Node{
|
||||
Kind: k,
|
||||
Value: v,
|
||||
}
|
||||
for _, c := range ch {
|
||||
Insert(n, c)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (a *Node) Equal(b *Node) bool {
|
||||
if a.Kind != b.Kind {
|
||||
return false
|
||||
}
|
||||
if a.Value != b.Value {
|
||||
return false
|
||||
}
|
||||
if len(a.Children) != len(b.Children) {
|
||||
return false
|
||||
}
|
||||
for i, c := range a.Children {
|
||||
if !c.Equal(b.Children[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *Node) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(a.Kind.String())
|
||||
if a.Value != nil {
|
||||
buf.WriteString(" =")
|
||||
buf.WriteString(fmt.Sprintf("%v", a.Value))
|
||||
}
|
||||
if len(a.Children) > 0 {
|
||||
buf.WriteString(" [")
|
||||
for i, c := range a.Children {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(c.String())
|
||||
}
|
||||
buf.WriteString("]")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func Insert(parent *Node, children ...*Node) {
|
||||
parent.Children = append(parent.Children, children...)
|
||||
for _, ch := range children {
|
||||
ch.Parent = parent
|
||||
}
|
||||
}
|
||||
|
||||
type List struct {
|
||||
Not bool
|
||||
Chars string
|
||||
}
|
||||
|
||||
type Range struct {
|
||||
Not bool
|
||||
Lo, Hi rune
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Text string
|
||||
}
|
||||
|
||||
type Kind int
|
||||
|
||||
const (
|
||||
KindNothing Kind = iota
|
||||
KindPattern
|
||||
KindList
|
||||
KindRange
|
||||
KindText
|
||||
KindAny
|
||||
KindSuper
|
||||
KindSingle
|
||||
KindAnyOf
|
||||
)
|
||||
|
||||
func (k Kind) String() string {
|
||||
switch k {
|
||||
case KindNothing:
|
||||
return "Nothing"
|
||||
case KindPattern:
|
||||
return "Pattern"
|
||||
case KindList:
|
||||
return "List"
|
||||
case KindRange:
|
||||
return "Range"
|
||||
case KindText:
|
||||
return "Text"
|
||||
case KindAny:
|
||||
return "Any"
|
||||
case KindSuper:
|
||||
return "Super"
|
||||
case KindSingle:
|
||||
return "Single"
|
||||
case KindAnyOf:
|
||||
return "AnyOf"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
157
vendor/github.com/gobwas/glob/syntax/ast/parser.go
generated
vendored
Normal file
157
vendor/github.com/gobwas/glob/syntax/ast/parser.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/syntax/lexer"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Lexer interface {
|
||||
Next() lexer.Token
|
||||
}
|
||||
|
||||
type parseFn func(*Node, Lexer) (parseFn, *Node, error)
|
||||
|
||||
func Parse(lexer Lexer) (*Node, error) {
|
||||
var parser parseFn
|
||||
|
||||
root := NewNode(KindPattern, nil)
|
||||
|
||||
var (
|
||||
tree *Node
|
||||
err error
|
||||
)
|
||||
for parser, tree = parserMain, root; parser != nil; {
|
||||
parser, tree, err = parser(tree, lexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func parserMain(tree *Node, lex Lexer) (parseFn, *Node, error) {
|
||||
for {
|
||||
token := lex.Next()
|
||||
switch token.Type {
|
||||
case lexer.EOF:
|
||||
return nil, tree, nil
|
||||
|
||||
case lexer.Error:
|
||||
return nil, tree, errors.New(token.Raw)
|
||||
|
||||
case lexer.Text:
|
||||
Insert(tree, NewNode(KindText, Text{token.Raw}))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.Any:
|
||||
Insert(tree, NewNode(KindAny, nil))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.Super:
|
||||
Insert(tree, NewNode(KindSuper, nil))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.Single:
|
||||
Insert(tree, NewNode(KindSingle, nil))
|
||||
return parserMain, tree, nil
|
||||
|
||||
case lexer.RangeOpen:
|
||||
return parserRange, tree, nil
|
||||
|
||||
case lexer.TermsOpen:
|
||||
a := NewNode(KindAnyOf, nil)
|
||||
Insert(tree, a)
|
||||
|
||||
p := NewNode(KindPattern, nil)
|
||||
Insert(a, p)
|
||||
|
||||
return parserMain, p, nil
|
||||
|
||||
case lexer.Separator:
|
||||
p := NewNode(KindPattern, nil)
|
||||
Insert(tree.Parent, p)
|
||||
|
||||
return parserMain, p, nil
|
||||
|
||||
case lexer.TermsClose:
|
||||
return parserMain, tree.Parent.Parent, nil
|
||||
|
||||
default:
|
||||
return nil, tree, fmt.Errorf("unexpected token: %s", token)
|
||||
}
|
||||
}
|
||||
return nil, tree, fmt.Errorf("unknown error")
|
||||
}
|
||||
|
||||
func parserRange(tree *Node, lex Lexer) (parseFn, *Node, error) {
|
||||
var (
|
||||
not bool
|
||||
lo rune
|
||||
hi rune
|
||||
chars string
|
||||
)
|
||||
for {
|
||||
token := lex.Next()
|
||||
switch token.Type {
|
||||
case lexer.EOF:
|
||||
return nil, tree, errors.New("unexpected end")
|
||||
|
||||
case lexer.Error:
|
||||
return nil, tree, errors.New(token.Raw)
|
||||
|
||||
case lexer.Not:
|
||||
not = true
|
||||
|
||||
case lexer.RangeLo:
|
||||
r, w := utf8.DecodeRuneInString(token.Raw)
|
||||
if len(token.Raw) > w {
|
||||
return nil, tree, fmt.Errorf("unexpected length of lo character")
|
||||
}
|
||||
lo = r
|
||||
|
||||
case lexer.RangeBetween:
|
||||
//
|
||||
|
||||
case lexer.RangeHi:
|
||||
r, w := utf8.DecodeRuneInString(token.Raw)
|
||||
if len(token.Raw) > w {
|
||||
return nil, tree, fmt.Errorf("unexpected length of lo character")
|
||||
}
|
||||
|
||||
hi = r
|
||||
|
||||
if hi < lo {
|
||||
return nil, tree, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo))
|
||||
}
|
||||
|
||||
case lexer.Text:
|
||||
chars = token.Raw
|
||||
|
||||
case lexer.RangeClose:
|
||||
isRange := lo != 0 && hi != 0
|
||||
isChars := chars != ""
|
||||
|
||||
if isChars == isRange {
|
||||
return nil, tree, fmt.Errorf("could not parse range")
|
||||
}
|
||||
|
||||
if isRange {
|
||||
Insert(tree, NewNode(KindRange, Range{
|
||||
Lo: lo,
|
||||
Hi: hi,
|
||||
Not: not,
|
||||
}))
|
||||
} else {
|
||||
Insert(tree, NewNode(KindList, List{
|
||||
Chars: chars,
|
||||
Not: not,
|
||||
}))
|
||||
}
|
||||
|
||||
return parserMain, tree, nil
|
||||
}
|
||||
}
|
||||
}
|
273
vendor/github.com/gobwas/glob/syntax/lexer/lexer.go
generated
vendored
Normal file
273
vendor/github.com/gobwas/glob/syntax/lexer/lexer.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gobwas/glob/util/runes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
char_any = '*'
|
||||
char_comma = ','
|
||||
char_single = '?'
|
||||
char_escape = '\\'
|
||||
char_range_open = '['
|
||||
char_range_close = ']'
|
||||
char_terms_open = '{'
|
||||
char_terms_close = '}'
|
||||
char_range_not = '!'
|
||||
char_range_between = '-'
|
||||
)
|
||||
|
||||
var specials = []byte{
|
||||
char_any,
|
||||
char_single,
|
||||
char_escape,
|
||||
char_range_open,
|
||||
char_range_close,
|
||||
char_terms_open,
|
||||
char_terms_close,
|
||||
}
|
||||
|
||||
func Special(c byte) bool {
|
||||
return bytes.IndexByte(specials, c) != -1
|
||||
}
|
||||
|
||||
type tokens []Token
|
||||
|
||||
func (i *tokens) shift() (ret Token) {
|
||||
ret = (*i)[0]
|
||||
copy(*i, (*i)[1:])
|
||||
*i = (*i)[:len(*i)-1]
|
||||
return
|
||||
}
|
||||
|
||||
func (i *tokens) push(v Token) {
|
||||
*i = append(*i, v)
|
||||
}
|
||||
|
||||
func (i *tokens) empty() bool {
|
||||
return len(*i) == 0
|
||||
}
|
||||
|
||||
var eof rune = 0
|
||||
|
||||
type lexer struct {
|
||||
data string
|
||||
pos int
|
||||
err error
|
||||
|
||||
tokens tokens
|
||||
termsLevel int
|
||||
|
||||
lastRune rune
|
||||
lastRuneSize int
|
||||
hasRune bool
|
||||
}
|
||||
|
||||
func NewLexer(source string) *lexer {
|
||||
l := &lexer{
|
||||
data: source,
|
||||
tokens: tokens(make([]Token, 0, 4)),
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *lexer) Next() Token {
|
||||
if l.err != nil {
|
||||
return Token{Error, l.err.Error()}
|
||||
}
|
||||
if !l.tokens.empty() {
|
||||
return l.tokens.shift()
|
||||
}
|
||||
|
||||
l.fetchItem()
|
||||
return l.Next()
|
||||
}
|
||||
|
||||
func (l *lexer) peek() (r rune, w int) {
|
||||
if l.pos == len(l.data) {
|
||||
return eof, 0
|
||||
}
|
||||
|
||||
r, w = utf8.DecodeRuneInString(l.data[l.pos:])
|
||||
if r == utf8.RuneError {
|
||||
l.errorf("could not read rune")
|
||||
r = eof
|
||||
w = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *lexer) read() rune {
|
||||
if l.hasRune {
|
||||
l.hasRune = false
|
||||
l.seek(l.lastRuneSize)
|
||||
return l.lastRune
|
||||
}
|
||||
|
||||
r, s := l.peek()
|
||||
l.seek(s)
|
||||
|
||||
l.lastRune = r
|
||||
l.lastRuneSize = s
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *lexer) seek(w int) {
|
||||
l.pos += w
|
||||
}
|
||||
|
||||
func (l *lexer) unread() {
|
||||
if l.hasRune {
|
||||
l.errorf("could not unread rune")
|
||||
return
|
||||
}
|
||||
l.seek(-l.lastRuneSize)
|
||||
l.hasRune = true
|
||||
}
|
||||
|
||||
func (l *lexer) errorf(f string, v ...interface{}) {
|
||||
l.err = fmt.Errorf(f, v...)
|
||||
}
|
||||
|
||||
func (l *lexer) inTerms() bool {
|
||||
return l.termsLevel > 0
|
||||
}
|
||||
|
||||
func (l *lexer) termsEnter() {
|
||||
l.termsLevel++
|
||||
}
|
||||
|
||||
func (l *lexer) termsLeave() {
|
||||
l.termsLevel--
|
||||
}
|
||||
|
||||
var inTextBreakers = []rune{char_single, char_any, char_range_open, char_terms_open}
|
||||
var inTermsBreakers = append(inTextBreakers, char_terms_close, char_comma)
|
||||
|
||||
func (l *lexer) fetchItem() {
|
||||
r := l.read()
|
||||
switch {
|
||||
case r == eof:
|
||||
l.tokens.push(Token{EOF, ""})
|
||||
|
||||
case r == char_terms_open:
|
||||
l.termsEnter()
|
||||
l.tokens.push(Token{TermsOpen, string(r)})
|
||||
|
||||
case r == char_comma && l.inTerms():
|
||||
l.tokens.push(Token{Separator, string(r)})
|
||||
|
||||
case r == char_terms_close && l.inTerms():
|
||||
l.tokens.push(Token{TermsClose, string(r)})
|
||||
l.termsLeave()
|
||||
|
||||
case r == char_range_open:
|
||||
l.tokens.push(Token{RangeOpen, string(r)})
|
||||
l.fetchRange()
|
||||
|
||||
case r == char_single:
|
||||
l.tokens.push(Token{Single, string(r)})
|
||||
|
||||
case r == char_any:
|
||||
if l.read() == char_any {
|
||||
l.tokens.push(Token{Super, string(r) + string(r)})
|
||||
} else {
|
||||
l.unread()
|
||||
l.tokens.push(Token{Any, string(r)})
|
||||
}
|
||||
|
||||
default:
|
||||
l.unread()
|
||||
|
||||
var breakers []rune
|
||||
if l.inTerms() {
|
||||
breakers = inTermsBreakers
|
||||
} else {
|
||||
breakers = inTextBreakers
|
||||
}
|
||||
l.fetchText(breakers)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) fetchRange() {
|
||||
var wantHi bool
|
||||
var wantClose bool
|
||||
var seenNot bool
|
||||
for {
|
||||
r := l.read()
|
||||
if r == eof {
|
||||
l.errorf("unexpected end of input")
|
||||
return
|
||||
}
|
||||
|
||||
if wantClose {
|
||||
if r != char_range_close {
|
||||
l.errorf("expected close range character")
|
||||
} else {
|
||||
l.tokens.push(Token{RangeClose, string(r)})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if wantHi {
|
||||
l.tokens.push(Token{RangeHi, string(r)})
|
||||
wantClose = true
|
||||
continue
|
||||
}
|
||||
|
||||
if !seenNot && r == char_range_not {
|
||||
l.tokens.push(Token{Not, string(r)})
|
||||
seenNot = true
|
||||
continue
|
||||
}
|
||||
|
||||
if n, w := l.peek(); n == char_range_between {
|
||||
l.seek(w)
|
||||
l.tokens.push(Token{RangeLo, string(r)})
|
||||
l.tokens.push(Token{RangeBetween, string(n)})
|
||||
wantHi = true
|
||||
continue
|
||||
}
|
||||
|
||||
l.unread() // unread first peek and fetch as text
|
||||
l.fetchText([]rune{char_range_close})
|
||||
wantClose = true
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) fetchText(breakers []rune) {
|
||||
var data []rune
|
||||
var escaped bool
|
||||
|
||||
reading:
|
||||
for {
|
||||
r := l.read()
|
||||
if r == eof {
|
||||
break
|
||||
}
|
||||
|
||||
if !escaped {
|
||||
if r == char_escape {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
|
||||
if runes.IndexRune(breakers, r) != -1 {
|
||||
l.unread()
|
||||
break reading
|
||||
}
|
||||
}
|
||||
|
||||
escaped = false
|
||||
data = append(data, r)
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
l.tokens.push(Token{Text, string(data)})
|
||||
}
|
||||
}
|
88
vendor/github.com/gobwas/glob/syntax/lexer/token.go
generated
vendored
Normal file
88
vendor/github.com/gobwas/glob/syntax/lexer/token.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
package lexer
|
||||
|
||||
import "fmt"
|
||||
|
||||
type TokenType int
|
||||
|
||||
const (
|
||||
EOF TokenType = iota
|
||||
Error
|
||||
Text
|
||||
Char
|
||||
Any
|
||||
Super
|
||||
Single
|
||||
Not
|
||||
Separator
|
||||
RangeOpen
|
||||
RangeClose
|
||||
RangeLo
|
||||
RangeHi
|
||||
RangeBetween
|
||||
TermsOpen
|
||||
TermsClose
|
||||
)
|
||||
|
||||
func (tt TokenType) String() string {
|
||||
switch tt {
|
||||
case EOF:
|
||||
return "eof"
|
||||
|
||||
case Error:
|
||||
return "error"
|
||||
|
||||
case Text:
|
||||
return "text"
|
||||
|
||||
case Char:
|
||||
return "char"
|
||||
|
||||
case Any:
|
||||
return "any"
|
||||
|
||||
case Super:
|
||||
return "super"
|
||||
|
||||
case Single:
|
||||
return "single"
|
||||
|
||||
case Not:
|
||||
return "not"
|
||||
|
||||
case Separator:
|
||||
return "separator"
|
||||
|
||||
case RangeOpen:
|
||||
return "range_open"
|
||||
|
||||
case RangeClose:
|
||||
return "range_close"
|
||||
|
||||
case RangeLo:
|
||||
return "range_lo"
|
||||
|
||||
case RangeHi:
|
||||
return "range_hi"
|
||||
|
||||
case RangeBetween:
|
||||
return "range_between"
|
||||
|
||||
case TermsOpen:
|
||||
return "terms_open"
|
||||
|
||||
case TermsClose:
|
||||
return "terms_close"
|
||||
|
||||
default:
|
||||
return "undef"
|
||||
}
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Type TokenType
|
||||
Raw string
|
||||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
return fmt.Sprintf("%v<%q>", t.Type, t.Raw)
|
||||
}
|
14
vendor/github.com/gobwas/glob/syntax/syntax.go
generated
vendored
Normal file
14
vendor/github.com/gobwas/glob/syntax/syntax.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"github.com/gobwas/glob/syntax/ast"
|
||||
"github.com/gobwas/glob/syntax/lexer"
|
||||
)
|
||||
|
||||
func Parse(s string) (*ast.Node, error) {
|
||||
return ast.Parse(lexer.NewLexer(s))
|
||||
}
|
||||
|
||||
func Special(b byte) bool {
|
||||
return lexer.Special(b)
|
||||
}
|
154
vendor/github.com/gobwas/glob/util/runes/runes.go
generated
vendored
Normal file
154
vendor/github.com/gobwas/glob/util/runes/runes.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
package runes
|
||||
|
||||
func Index(s, needle []rune) int {
|
||||
ls, ln := len(s), len(needle)
|
||||
|
||||
switch {
|
||||
case ln == 0:
|
||||
return 0
|
||||
case ln == 1:
|
||||
return IndexRune(s, needle[0])
|
||||
case ln == ls:
|
||||
if Equal(s, needle) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case ln > ls:
|
||||
return -1
|
||||
}
|
||||
|
||||
head:
|
||||
for i := 0; i < ls && ls-i >= ln; i++ {
|
||||
for y := 0; y < ln; y++ {
|
||||
if s[i+y] != needle[y] {
|
||||
continue head
|
||||
}
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func LastIndex(s, needle []rune) int {
|
||||
ls, ln := len(s), len(needle)
|
||||
|
||||
switch {
|
||||
case ln == 0:
|
||||
if ls == 0 {
|
||||
return 0
|
||||
}
|
||||
return ls
|
||||
case ln == 1:
|
||||
return IndexLastRune(s, needle[0])
|
||||
case ln == ls:
|
||||
if Equal(s, needle) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case ln > ls:
|
||||
return -1
|
||||
}
|
||||
|
||||
head:
|
||||
for i := ls - 1; i >= 0 && i >= ln; i-- {
|
||||
for y := ln - 1; y >= 0; y-- {
|
||||
if s[i-(ln-y-1)] != needle[y] {
|
||||
continue head
|
||||
}
|
||||
}
|
||||
|
||||
return i - ln + 1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// IndexAny returns the index of the first instance of any Unicode code point
|
||||
// from chars in s, or -1 if no Unicode code point from chars is present in s.
|
||||
func IndexAny(s, chars []rune) int {
|
||||
if len(chars) > 0 {
|
||||
for i, c := range s {
|
||||
for _, m := range chars {
|
||||
if c == m {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func Contains(s, needle []rune) bool {
|
||||
return Index(s, needle) >= 0
|
||||
}
|
||||
|
||||
func Max(s []rune) (max rune) {
|
||||
for _, r := range s {
|
||||
if r > max {
|
||||
max = r
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Min(s []rune) rune {
|
||||
min := rune(-1)
|
||||
for _, r := range s {
|
||||
if min == -1 {
|
||||
min = r
|
||||
continue
|
||||
}
|
||||
|
||||
if r < min {
|
||||
min = r
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
}
|
||||
|
||||
func IndexRune(s []rune, r rune) int {
|
||||
for i, c := range s {
|
||||
if c == r {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func IndexLastRune(s []rune, r rune) int {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == r {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func Equal(a, b []rune) bool {
|
||||
if len(a) == len(b) {
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPrefix tests whether the string s begins with prefix.
|
||||
func HasPrefix(s, prefix []rune) bool {
|
||||
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
|
||||
}
|
||||
|
||||
// HasSuffix tests whether the string s ends with suffix.
|
||||
func HasSuffix(s, suffix []rune) bool {
|
||||
return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix)
|
||||
}
|
39
vendor/github.com/gobwas/glob/util/strings/strings.go
generated
vendored
Normal file
39
vendor/github.com/gobwas/glob/util/strings/strings.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package strings
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func IndexAnyRunes(s string, rs []rune) int {
|
||||
for _, r := range rs {
|
||||
if i := strings.IndexRune(s, r); i != -1 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func LastIndexAnyRunes(s string, rs []rune) int {
|
||||
for _, r := range rs {
|
||||
i := -1
|
||||
if 0 <= r && r < utf8.RuneSelf {
|
||||
i = strings.LastIndexByte(s, byte(r))
|
||||
} else {
|
||||
sub := s
|
||||
for len(sub) > 0 {
|
||||
j := strings.IndexRune(s, r)
|
||||
if j == -1 {
|
||||
break
|
||||
}
|
||||
i = j
|
||||
sub = sub[i+1:]
|
||||
}
|
||||
}
|
||||
if i != -1 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
191
vendor/github.com/golang/glog/LICENSE
generated
vendored
191
vendor/github.com/golang/glog/LICENSE
generated
vendored
@ -1,191 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
44
vendor/github.com/golang/glog/README
generated
vendored
44
vendor/github.com/golang/glog/README
generated
vendored
@ -1,44 +0,0 @@
|
||||
glog
|
||||
====
|
||||
|
||||
Leveled execution logs for Go.
|
||||
|
||||
This is an efficient pure Go implementation of leveled logs in the
|
||||
manner of the open source C++ package
|
||||
https://github.com/google/glog
|
||||
|
||||
By binding methods to booleans it is possible to use the log package
|
||||
without paying the expense of evaluating the arguments to the log.
|
||||
Through the -vmodule flag, the package also provides fine-grained
|
||||
control over logging at the file level.
|
||||
|
||||
The comment from glog.go introduces the ideas:
|
||||
|
||||
Package glog implements logging analogous to the Google-internal
|
||||
C++ INFO/ERROR/V setup. It provides functions Info, Warning,
|
||||
Error, Fatal, plus formatting variants such as Infof. It
|
||||
also provides V-style logging controlled by the -v and
|
||||
-vmodule=file=2 flags.
|
||||
|
||||
Basic examples:
|
||||
|
||||
glog.Info("Prepare to repel boarders")
|
||||
|
||||
glog.Fatalf("Initialization failed: %s", err)
|
||||
|
||||
See the documentation for the V function for an explanation
|
||||
of these examples:
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Info("Starting transaction...")
|
||||
}
|
||||
|
||||
glog.V(2).Infoln("Processed", nItems, "elements")
|
||||
|
||||
|
||||
The repository contains an open source version of the log package
|
||||
used inside Google. The master copy of the source lives inside
|
||||
Google, not here. The code in this repo is for export only and is not itself
|
||||
under development. Feature requests will be ignored.
|
||||
|
||||
Send bug reports to golang-nuts@googlegroups.com.
|
1180
vendor/github.com/golang/glog/glog.go
generated
vendored
1180
vendor/github.com/golang/glog/glog.go
generated
vendored
File diff suppressed because it is too large
Load Diff
124
vendor/github.com/golang/glog/glog_file.go
generated
vendored
124
vendor/github.com/golang/glog/glog_file.go
generated
vendored
@ -1,124 +0,0 @@
|
||||
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
|
||||
//
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// File I/O for logs.
|
||||
|
||||
package glog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MaxSize is the maximum size of a log file in bytes.
|
||||
var MaxSize uint64 = 1024 * 1024 * 1800
|
||||
|
||||
// logDirs lists the candidate directories for new log files.
|
||||
var logDirs []string
|
||||
|
||||
// If non-empty, overrides the choice of directory in which to write logs.
|
||||
// See createLogDirs for the full list of possible destinations.
|
||||
var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory")
|
||||
|
||||
func createLogDirs() {
|
||||
if *logDir != "" {
|
||||
logDirs = append(logDirs, *logDir)
|
||||
}
|
||||
logDirs = append(logDirs, os.TempDir())
|
||||
}
|
||||
|
||||
var (
|
||||
pid = os.Getpid()
|
||||
program = filepath.Base(os.Args[0])
|
||||
host = "unknownhost"
|
||||
userName = "unknownuser"
|
||||
)
|
||||
|
||||
func init() {
|
||||
h, err := os.Hostname()
|
||||
if err == nil {
|
||||
host = shortHostname(h)
|
||||
}
|
||||
|
||||
current, err := user.Current()
|
||||
if err == nil {
|
||||
userName = current.Username
|
||||
}
|
||||
|
||||
// Sanitize userName since it may contain filepath separators on Windows.
|
||||
userName = strings.Replace(userName, `\`, "_", -1)
|
||||
}
|
||||
|
||||
// shortHostname returns its argument, truncating at the first period.
|
||||
// For instance, given "www.google.com" it returns "www".
|
||||
func shortHostname(hostname string) string {
|
||||
if i := strings.Index(hostname, "."); i >= 0 {
|
||||
return hostname[:i]
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
|
||||
// logName returns a new log file name containing tag, with start time t, and
|
||||
// the name for the symlink for tag.
|
||||
func logName(tag string, t time.Time) (name, link string) {
|
||||
name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d",
|
||||
program,
|
||||
host,
|
||||
userName,
|
||||
tag,
|
||||
t.Year(),
|
||||
t.Month(),
|
||||
t.Day(),
|
||||
t.Hour(),
|
||||
t.Minute(),
|
||||
t.Second(),
|
||||
pid)
|
||||
return name, program + "." + tag
|
||||
}
|
||||
|
||||
var onceLogDirs sync.Once
|
||||
|
||||
// create creates a new log file and returns the file and its filename, which
|
||||
// contains tag ("INFO", "FATAL", etc.) and t. If the file is created
|
||||
// successfully, create also attempts to update the symlink for that tag, ignoring
|
||||
// errors.
|
||||
func create(tag string, t time.Time) (f *os.File, filename string, err error) {
|
||||
onceLogDirs.Do(createLogDirs)
|
||||
if len(logDirs) == 0 {
|
||||
return nil, "", errors.New("log: no log dirs")
|
||||
}
|
||||
name, link := logName(tag, t)
|
||||
var lastErr error
|
||||
for _, dir := range logDirs {
|
||||
fname := filepath.Join(dir, name)
|
||||
f, err := os.Create(fname)
|
||||
if err == nil {
|
||||
symlink := filepath.Join(dir, link)
|
||||
os.Remove(symlink) // ignore err
|
||||
os.Symlink(name, symlink) // ignore err
|
||||
return f, fname, nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr)
|
||||
}
|
74
vendor/vendor.json
vendored
74
vendor/vendor.json
vendored
@ -62,18 +62,6 @@
|
||||
"revision": "c91e78db502ff629614837aacb7aa4efa61c651a",
|
||||
"revisionTime": "2016-04-30T09:49:23Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "vN3qcK0Nkk4AhOfhTEA8ZUMu2RY=",
|
||||
"path": "github.com/aead/chacha20",
|
||||
"revision": "8d6ce0550041f9d97e7f15ec27ed489f8bbbb0fb",
|
||||
"revisionTime": "2017-06-14T05:10:14Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "zmmUB5W1Oz/czit5mixwbVQq31A=",
|
||||
"path": "github.com/aead/chacha20/chacha",
|
||||
"revision": "8d6ce0550041f9d97e7f15ec27ed489f8bbbb0fb",
|
||||
"revisionTime": "2017-06-14T05:10:14Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "30PBqj9BW03KCVqASvLg3bR+xYc=",
|
||||
"path": "github.com/agl/ed25519/edwards25519",
|
||||
@ -117,16 +105,10 @@
|
||||
"revisionTime": "2017-09-11T08:28:29Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9e9tjPDTESeCEdUMElph250lurs=",
|
||||
"path": "github.com/ginuerzh/pht",
|
||||
"revision": "8acf34a91f436364fadb82a630ab3c7e938e1d0f",
|
||||
"revisionTime": "2017-02-05T06:52:49Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ZefD404me5Nm13S6NTsfJ57UApI=",
|
||||
"checksumSHA1": "oAor5oKUyfFTHUT7ICWfe/aZTrY=",
|
||||
"path": "github.com/ginuerzh/tls-dissector",
|
||||
"revision": "ede94e83b36efefb6d06e5a29d28e2211b8bd6a9",
|
||||
"revisionTime": "2017-10-29T12:10:54Z"
|
||||
"revision": "7037c35ed6947fe9d9c33785fca4ac96eef8e62b",
|
||||
"revisionTime": "2018-11-03T04:46:17Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fBx0fqiyrl26gkGo14J9pJ8zB2Y=",
|
||||
@ -135,10 +117,52 @@
|
||||
"revisionTime": "2017-05-15T17:08:25Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "HmbftipkadrLlCfzzVQ+iFHbl6g=",
|
||||
"path": "github.com/golang/glog",
|
||||
"revision": "23def4e6c14b4da8ac2ed8007337bc5eb5007998",
|
||||
"revisionTime": "2016-01-25T20:49:56Z"
|
||||
"checksumSHA1": "/RE0VzeaXdaBUNeNMUXWqzly7qY=",
|
||||
"path": "github.com/gobwas/glob",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "UIxNdlqa2kcYi5EEe79/WzyPiz4=",
|
||||
"path": "github.com/gobwas/glob/compiler",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "jNdbCxBabqjv9tREXA4Nx45Y4wg=",
|
||||
"path": "github.com/gobwas/glob/match",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "2KhDNUE98XhHnIgg2S9E4gcWoig=",
|
||||
"path": "github.com/gobwas/glob/syntax",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "FPIMdPSazNZGNU+ZmPtIeZpVHFU=",
|
||||
"path": "github.com/gobwas/glob/syntax/ast",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "umyztSrQrQIl9JgwhDDb6wrSLeI=",
|
||||
"path": "github.com/gobwas/glob/syntax/lexer",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "vux29fN5O22F3QPLOnjQ37278vg=",
|
||||
"path": "github.com/gobwas/glob/util/runes",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Sxp2AHWkkLkIq6w9aMcLmzcwD6w=",
|
||||
"path": "github.com/gobwas/glob/util/strings",
|
||||
"revision": "f00a7392b43971b2fdb562418faab1f18da2067a",
|
||||
"revisionTime": "2018-04-02T14:15:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "d9PxF1XQGLMJZRct2R8qVM/eYlE=",
|
||||
|
Loading…
Reference in New Issue
Block a user