├── relay └── relay.go ├── protocol ├── service │ ├── http │ │ ├── logger_adapter.go │ │ └── goproxy.go │ ├── socks5 │ │ └── socks5.go │ └── shadowsocks │ │ └── shadowsocks.go ├── listener │ └── tcp │ │ └── tcp.go └── protocol.go ├── .gitignore ├── metadata └── metadata.go ├── endpoint ├── balancer │ ├── roundrobin │ │ └── roundrobin.go │ └── balancer.go └── endpoint.go ├── main.go ├── go.mod ├── log └── logger.go ├── cmd ├── proxy │ └── proxy.go ├── endpoint │ └── endpoint.go ├── init.go └── root.go ├── signal └── signal.go ├── Makefile ├── proxy └── server.go ├── p2p ├── options.go └── libp2p.go ├── README.md ├── config └── config.go ├── LICENSE └── go.sum /relay/relay.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "go.uber.org/multierr" 5 | "io" 6 | ) 7 | 8 | func CloseAfterRelay(dst, src io.ReadWriteCloser) error { 9 | ch := make(chan error) 10 | go relay(dst, src, ch) 11 | go relay(src, dst, ch) 12 | 13 | err := <-ch 14 | return multierr.Combine(err, dst.Close(), src.Close()) 15 | } 16 | 17 | func relay(dst io.Writer, src io.Reader, ch chan<- error) { 18 | _, err := io.Copy(dst, src) 19 | ch <- err 20 | } 21 | -------------------------------------------------------------------------------- /protocol/service/http/logger_adapter.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/diandianl/p2p-proxy/log" 5 | 6 | "github.com/elazarl/goproxy" 7 | ) 8 | 9 | func setLogger(proxy *goproxy.ProxyHttpServer, logger log.Logger) { 10 | logf := logger.Infof 11 | if proxy.Verbose { 12 | logf = logger.Debugf 13 | } 14 | proxy.Logger = &loggerAdapter{logf} 15 | } 16 | 17 | type loggerAdapter struct { 18 | logf func(string, ...interface{}) 19 | } 20 | 21 | func (a *loggerAdapter) Printf(format string, v ...interface{}) { 22 | a.logf(format, v...) 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX leaves these everywhere on SMB shares 2 | ._* 3 | 4 | # OSX trash 5 | .DS_Store 6 | 7 | # Eclipse files 8 | .classpath 9 | .project 10 | .settings/** 11 | 12 | # Files generated by JetBrains IDEs, e.g. IntelliJ IDEA 13 | .idea/ 14 | *.iml 15 | 16 | # Vscode files 17 | .vscode 18 | 19 | # This is where the result of the go build goes 20 | /output*/ 21 | /_output*/ 22 | /_output 23 | 24 | # Vim-related files 25 | [._]*.s[a-w][a-z] 26 | [._]s[a-w][a-z] 27 | *.un~ 28 | Session.vim 29 | .netrwhist 30 | 31 | # cscope-related files 32 | cscope.* 33 | 34 | # Go test binaries 35 | *.test 36 | 37 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 38 | *.o 39 | *.a 40 | *.so 41 | 42 | # Folders 43 | _obj 44 | _test 45 | vendor 46 | 47 | *.exe 48 | *.prof 49 | *.pprof 50 | *.out 51 | *.log 52 | 53 | p2p-proxy* 54 | -------------------------------------------------------------------------------- /metadata/metadata.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import "fmt" 4 | 5 | var Version = "dev-build" 6 | 7 | var CommitSHA = "commit" 8 | 9 | var Banner = ` 10 | 11 | ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗ 12 | ██╔══██╗╚════██╗██╔══██╗ ██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝ 13 | ██████╔╝ █████╔╝██████╔╝█████╗██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝ 14 | ██╔═══╝ ██╔═══╝ ██╔═══╝ ╚════╝██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝ 15 | ██║ ███████╗██║ ██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║ 16 | ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ 17 | 18 | A http(s) proxy based on P2P 19 | 20 | Github: https://github.com/diandianl/p2p-proxy 21 | Version: %s 22 | CommitSHA: %s 23 | 24 | ` 25 | 26 | func PrintBanner() { 27 | if len(Banner) > 0 { 28 | fmt.Printf(Banner, Version, CommitSHA) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /endpoint/balancer/roundrobin/roundrobin.go: -------------------------------------------------------------------------------- 1 | package roundrobin 2 | 3 | import ( 4 | "github.com/diandianl/p2p-proxy/endpoint/balancer" 5 | "github.com/diandianl/p2p-proxy/protocol" 6 | 7 | "go.uber.org/atomic" 8 | ) 9 | 10 | func init() { 11 | err := balancer.RegisterBalancerFactory(balancer.RoundRobin, New) 12 | if err != nil { 13 | panic(err) 14 | } 15 | } 16 | 17 | func New(getter balancer.Getter) (balancer.Balancer, error) { 18 | return &roundrobin{Getter: getter}, nil 19 | } 20 | 21 | type roundrobin struct { 22 | balancer.Getter 23 | counter atomic.Uint32 24 | } 25 | 26 | func (rr *roundrobin) Name() string { 27 | return balancer.RoundRobin 28 | } 29 | 30 | func (rr *roundrobin) Next(p protocol.Protocol) (balancer.Proxy, error) { 31 | proxies := rr.GetProxies(p) 32 | if len(proxies) == 0 { 33 | return balancer.NoProxy, balancer.NewNotEnoughProxiesError(p) 34 | } 35 | return proxies[rr.counter.Inc()%uint32(len(proxies))], nil 36 | } 37 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import ( 19 | "context" 20 | 21 | "github.com/diandianl/p2p-proxy/cmd" 22 | "github.com/diandianl/p2p-proxy/metadata" 23 | "github.com/diandianl/p2p-proxy/signal" 24 | ) 25 | 26 | func main() { 27 | 28 | metadata.PrintBanner() 29 | 30 | ctx := context.Background() 31 | 32 | done, ctx := signal.SetupInterruptHandler(ctx) 33 | 34 | defer done() 35 | 36 | cmd.Execute(ctx) 37 | } 38 | -------------------------------------------------------------------------------- /protocol/listener/tcp/tcp.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "github.com/diandianl/p2p-proxy/log" 5 | "github.com/diandianl/p2p-proxy/protocol" 6 | "net" 7 | ) 8 | 9 | func init() { 10 | protos := []struct { 11 | protocol protocol.Protocol 12 | short string 13 | }{ 14 | {protocol.HTTP, "http"}, 15 | {protocol.Socks5, "socks5"}, 16 | {protocol.Shadowsocks, "shadowsocks"}, 17 | } 18 | for _, proto := range protos { 19 | err := protocol.RegisterListenerFactory(NewFactory(proto.protocol, proto.short)) 20 | if err != nil { 21 | panic(err) 22 | } 23 | } 24 | } 25 | 26 | func NewFactory(p protocol.Protocol, short string) (protocol.Protocol, string, func(logger log.Logger, listen string) (protocol.Listener, error)) { 27 | return p, short, func(logger log.Logger, listen string) (protocol.Listener, error) { 28 | l, err := net.Listen("tcp", listen) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return &listener{protocol: p, Listener: l}, nil 33 | } 34 | } 35 | 36 | type listener struct { 37 | protocol protocol.Protocol 38 | 39 | net.Listener 40 | } 41 | 42 | func (l *listener) Protocol() protocol.Protocol { 43 | return l.protocol 44 | } 45 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/diandianl/p2p-proxy 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 7 | github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 8 | github.com/ipfs/go-cid v0.0.5 9 | github.com/ipfs/go-datastore v0.4.4 10 | github.com/ipfs/go-log v1.0.2 // indirect 11 | github.com/ipfs/go-log/v2 v2.0.2 12 | github.com/libp2p/go-libp2p v0.5.2 13 | github.com/libp2p/go-libp2p-autonat-svc v0.1.0 14 | github.com/libp2p/go-libp2p-core v0.3.1 15 | github.com/libp2p/go-libp2p-discovery v0.2.0 16 | github.com/libp2p/go-libp2p-gostream v0.2.0 17 | github.com/libp2p/go-libp2p-kad-dht v0.5.0 18 | github.com/libp2p/go-libp2p-secio v0.2.1 19 | github.com/mitchellh/go-homedir v1.1.0 20 | github.com/multiformats/go-multiaddr v0.2.0 21 | github.com/multiformats/go-multihash v0.0.13 22 | github.com/onsi/ginkgo v1.11.0 // indirect 23 | github.com/onsi/gomega v1.8.1 // indirect 24 | github.com/shadowsocks/go-shadowsocks2 v0.1.0 25 | github.com/spf13/cobra v0.0.5 26 | github.com/spf13/viper v1.6.2 27 | github.com/urfave/cli/v2 v2.1.1 28 | go.uber.org/atomic v1.4.0 29 | go.uber.org/multierr v1.1.0 30 | gopkg.in/yaml.v2 v2.2.4 31 | ) 32 | -------------------------------------------------------------------------------- /protocol/service/http/goproxy.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | 8 | "github.com/diandianl/p2p-proxy/log" 9 | "github.com/diandianl/p2p-proxy/protocol" 10 | 11 | "github.com/elazarl/goproxy" 12 | ) 13 | 14 | func init() { 15 | err := protocol.RegisterServiceFactory(protocol.HTTP, "http", New) 16 | if err != nil { 17 | panic(err) 18 | } 19 | } 20 | 21 | func New(logger log.Logger, cfg map[string]interface{}) (protocol.Service, error) { 22 | proxy := goproxy.NewProxyHttpServer() 23 | 24 | // TODO process cfg 25 | 26 | setLogger(proxy, logger) 27 | 28 | return &goproxyService{logger: logger, srv: &http.Server{Handler: proxy}, delegate: proxy}, nil 29 | } 30 | 31 | type goproxyService struct { 32 | logger log.Logger 33 | 34 | srv *http.Server 35 | 36 | delegate *goproxy.ProxyHttpServer 37 | } 38 | 39 | func (_ *goproxyService) Protocol() protocol.Protocol { 40 | return protocol.HTTP 41 | } 42 | 43 | func (s *goproxyService) Serve(ctx context.Context, l net.Listener) error { 44 | err := s.srv.Serve(l) 45 | if err == http.ErrServerClosed { 46 | return nil 47 | } 48 | return err 49 | } 50 | 51 | func (s *goproxyService) Shutdown(ctx context.Context) error { 52 | return s.srv.Shutdown(ctx) 53 | } 54 | -------------------------------------------------------------------------------- /log/logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ipfs/go-log/v2" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | const LogSystem = "p2p-proxy" 11 | 12 | type Logger interface { 13 | log.StandardLogger 14 | Sync() error 15 | } 16 | 17 | func NewLogger() Logger { 18 | return log.Logger(LogSystem) 19 | } 20 | 21 | func NewSubLogger(sub string) Logger { 22 | return log.Logger(fmt.Sprintf("%s/%s", LogSystem, sub)) 23 | } 24 | 25 | func SetAllLogLevel(level string) error { 26 | return log.SetLogLevel("*", level) 27 | } 28 | 29 | func SetLogLevel(name, level string) error { 30 | return log.SetLogLevel(name, level) 31 | } 32 | 33 | func SetupLogging(file string, format string, level string) error { 34 | 35 | envs := []struct { 36 | key string 37 | val string 38 | }{ 39 | {"GOLOG_LOG_FMT", format}, 40 | {"GOLOG_FILE", file}, 41 | {"GOLOG_LOG_LEVEL", level}, 42 | } 43 | 44 | setup := false 45 | for _, e := range envs { 46 | if len(e.val) > 0 { 47 | setup = true 48 | err := os.Setenv(e.key, e.val) 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | } 54 | if len(file) > 0 { 55 | err := os.MkdirAll(filepath.Dir(file), 0755) 56 | if err != nil { 57 | return err 58 | } 59 | } 60 | if setup { 61 | log.SetupLogging() 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /protocol/service/socks5/socks5.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "context" 5 | "github.com/diandianl/p2p-proxy/log" 6 | "github.com/diandianl/p2p-proxy/protocol" 7 | "net" 8 | 9 | socks5 "github.com/armon/go-socks5" 10 | ) 11 | 12 | func init() { 13 | err := protocol.RegisterServiceFactory(protocol.Socks5, "socks5", New) 14 | if err != nil { 15 | panic(err) 16 | } 17 | } 18 | 19 | func New(logger log.Logger, cfg map[string]interface{}) (protocol.Service, error) { 20 | 21 | // TODO process cfg 22 | conf := &socks5.Config{} 23 | server, err := socks5.New(conf) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return &socks5Service{logger: logger, delegate: server}, nil 28 | } 29 | 30 | type socks5Service struct { 31 | logger log.Logger 32 | 33 | delegate *socks5.Server 34 | 35 | listener net.Listener 36 | 37 | shuttingDown bool 38 | } 39 | 40 | func (_ *socks5Service) Protocol() protocol.Protocol { 41 | return protocol.Socks5 42 | } 43 | 44 | func (s *socks5Service) Serve(ctx context.Context, l net.Listener) error { 45 | s.listener = l 46 | err := s.delegate.Serve(l) 47 | if s.shuttingDown { 48 | err = nil 49 | } 50 | return err 51 | } 52 | 53 | func (s *socks5Service) Shutdown(ctx context.Context) error { 54 | s.shuttingDown = true 55 | if s.listener != nil { 56 | return s.listener.Close() 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /endpoint/balancer/balancer.go: -------------------------------------------------------------------------------- 1 | package balancer 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/diandianl/p2p-proxy/protocol" 7 | "github.com/libp2p/go-libp2p-core/peer" 8 | ) 9 | 10 | const ( 11 | NoProxy Proxy = "" 12 | 13 | RoundRobin = "round_robin" 14 | ) 15 | 16 | type Proxy = peer.ID 17 | 18 | type Getter interface { 19 | GetProxies(protocol protocol.Protocol) []Proxy 20 | } 21 | 22 | type Balancer interface { 23 | Name() string 24 | Next(protocol protocol.Protocol) (Proxy, error) 25 | } 26 | 27 | type BalancerFactory func(getter Getter) (Balancer, error) 28 | 29 | var registry = map[string]BalancerFactory{} 30 | 31 | func RegisterBalancerFactory(name string, factory BalancerFactory) error { 32 | if _, ok := registry[name]; ok { 33 | return fmt.Errorf("duplicate registration, name [%s] balancer factory registered", name) 34 | } 35 | registry[name] = factory 36 | return nil 37 | } 38 | 39 | func New(name string, getter Getter) (Balancer, error) { 40 | factory, ok := registry[name] 41 | if !ok { 42 | return nil, fmt.Errorf("unsupported balancer [%s]", name) 43 | } 44 | return factory(getter) 45 | } 46 | 47 | type notEnoughProxiesError struct { 48 | p protocol.Protocol 49 | } 50 | 51 | func (e notEnoughProxiesError) Error() string { 52 | return fmt.Sprintf("not enough proxies for protocol [%s]", e.p) 53 | } 54 | func NewNotEnoughProxiesError(p protocol.Protocol) error { 55 | return notEnoughProxiesError{p} 56 | } 57 | func IsNewNotEnoughProxiesError(e error) bool { 58 | _, ok := e.(notEnoughProxiesError) 59 | return ok 60 | } 61 | -------------------------------------------------------------------------------- /cmd/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package proxy 17 | 18 | import ( 19 | "context" 20 | 21 | "github.com/diandianl/p2p-proxy/config" 22 | "github.com/diandianl/p2p-proxy/proxy" 23 | 24 | "github.com/spf13/cobra" 25 | 26 | _ "github.com/diandianl/p2p-proxy/protocol/service/http" 27 | _ "github.com/diandianl/p2p-proxy/protocol/service/shadowsocks" 28 | _ "github.com/diandianl/p2p-proxy/protocol/service/socks5" 29 | ) 30 | 31 | // proxyCmd represents the proxy command 32 | func NewProxyCmd(ctx context.Context, cfgGetter func(proxy bool) (*config.Config, error)) *cobra.Command { 33 | var proxyCmd = &cobra.Command{ 34 | Use: "proxy", 35 | Short: "Start a proxy server peer", 36 | RunE: func(cmd *cobra.Command, args []string) error { 37 | cmd.SilenceUsage = true 38 | 39 | cfg, err := cfgGetter(true) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | proxyService, err := proxy.New(cfg) 45 | if err != nil { 46 | return err 47 | } 48 | return proxyService.Start(ctx) 49 | }, 50 | } 51 | return proxyCmd 52 | } 53 | -------------------------------------------------------------------------------- /signal/signal.go: -------------------------------------------------------------------------------- 1 | package signal 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "sync" 9 | "syscall" 10 | ) 11 | 12 | func SetupInterruptHandler(ctx context.Context) (func(), context.Context) { 13 | h := &interruptHandler{closing: make(chan struct{})} 14 | 15 | ctx, cancelFunc := context.WithCancel(ctx) 16 | 17 | handlerFunc := func(count int, ih *interruptHandler) { 18 | switch count { 19 | case 1: 20 | fmt.Println() // Prevent un-terminated ^C character in terminal 21 | 22 | ih.wg.Add(1) 23 | go func() { 24 | defer ih.wg.Done() 25 | cancelFunc() 26 | }() 27 | 28 | default: 29 | fmt.Println("Received another interrupt before graceful shutdown, terminating...") 30 | os.Exit(-1) 31 | } 32 | } 33 | 34 | h.handle(handlerFunc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) 35 | 36 | return func() { 37 | h.close() 38 | }, ctx 39 | } 40 | 41 | type interruptHandler struct { 42 | closing chan struct{} 43 | wg sync.WaitGroup 44 | } 45 | 46 | func (ih *interruptHandler) close() error { 47 | close(ih.closing) 48 | ih.wg.Wait() 49 | return nil 50 | } 51 | 52 | // Handle starts handling the given signals, and will call the handler 53 | // callback function each time a signal is caught. The function is passed 54 | // the number of times the handler has been triggered in total, as 55 | // well as the handler itself, so that the handling logic can use the 56 | // handler's wait group to ensure clean shutdown when Close() is called. 57 | func (ih *interruptHandler) handle(handler func(count int, ih *interruptHandler), sigs ...os.Signal) { 58 | notify := make(chan os.Signal, 1) 59 | signal.Notify(notify, sigs...) 60 | ih.wg.Add(1) 61 | go func() { 62 | defer ih.wg.Done() 63 | defer signal.Stop(notify) 64 | 65 | count := 0 66 | for { 67 | select { 68 | case <-ih.closing: 69 | return 70 | case <-notify: 71 | count++ 72 | handler(count, ih) 73 | } 74 | } 75 | }() 76 | } 77 | -------------------------------------------------------------------------------- /cmd/endpoint/endpoint.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package endpoint 17 | 18 | import ( 19 | "context" 20 | 21 | "github.com/diandianl/p2p-proxy/config" 22 | "github.com/diandianl/p2p-proxy/endpoint" 23 | 24 | "github.com/spf13/cobra" 25 | "github.com/spf13/viper" 26 | 27 | _ "github.com/diandianl/p2p-proxy/endpoint/balancer/roundrobin" 28 | _ "github.com/diandianl/p2p-proxy/protocol/listener/tcp" 29 | ) 30 | 31 | func NewEndpointCmd(ctx context.Context, cfgGetter func(proxy bool) (*config.Config, error)) *cobra.Command { 32 | 33 | // endpointCmd represents the endpoint command 34 | var endpointCmd = &cobra.Command{ 35 | Use: "endpoint", 36 | Short: "endpoint command run at local for proxy agent", 37 | Long: "endpoint command run at local for proxy agent", 38 | RunE: func(cmd *cobra.Command, args []string) error { 39 | 40 | cmd.SilenceUsage = true 41 | cfg, err := cfgGetter(false) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | ep, err := endpoint.New(cfg) 47 | 48 | if err != nil { 49 | return err 50 | } 51 | 52 | return ep.Start(ctx) 53 | }, 54 | } 55 | 56 | endpointCmd.Flags().StringP("proxy", "p", "", "proxy server address") 57 | viper.BindPFlag("Endpoint.Proxy", endpointCmd.Flags().Lookup("proxy")) 58 | 59 | endpointCmd.Flags().String("http", "", "local http(s) proxy agent listen address") 60 | viper.BindPFlag("Endpoint.HTTP", endpointCmd.Flags().Lookup("http")) 61 | 62 | return endpointCmd 63 | } 64 | -------------------------------------------------------------------------------- /cmd/init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "crypto/rand" 20 | "encoding/base64" 21 | 22 | "github.com/libp2p/go-libp2p-core/crypto" 23 | "github.com/spf13/cobra" 24 | "github.com/spf13/viper" 25 | ) 26 | 27 | // initCmd represents the init command 28 | var initCmd = &cobra.Command{ 29 | Use: "init", 30 | Short: "Generate and write default config", 31 | RunE: func(cmd *cobra.Command, args []string) error { 32 | // parent command PersistentPreRunE hook ensure configFile exist 33 | return initConfig("") 34 | }, 35 | } 36 | 37 | func initConfig(saveAs string) error { 38 | 39 | // log.Debug("Initial run or specified configuration file does not exist or reinit, perform initialization") 40 | 41 | priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rand.Reader) 42 | if err != nil { 43 | return err 44 | } 45 | privKey, err := crypto.MarshalPrivateKey(priv) 46 | 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // log.Debug("Generated Private Key, Using Alg: RSA, Bits: 2048") 52 | 53 | viper.Set("Identity.PrivKey", base64.StdEncoding.EncodeToString(privKey)) 54 | 55 | addrs := map[string]interface{}{ 56 | "Endpoint.HTTP": "127.0.0.1:8010", 57 | "Endpoint.Proxy": "/ip4/149.129.82.89/tcp/8888/ipfs/QmXwj9Uk68XTGZLQrREjQJpTLx6GWokHrGX7xrYPGcRkTn", 58 | "P2P.Addr": []string{"/ip4/0.0.0.0/tcp/8888"}, 59 | "P2P.BootstrapPeers": []string{"/ip4/149.129.82.89/tcp/8888/ipfs/QmXwj9Uk68XTGZLQrREjQJpTLx6GWokHrGX7xrYPGcRkTn"}, 60 | } 61 | 62 | for key, def := range addrs { 63 | if !viper.IsSet(key) { 64 | viper.Set(key, def) 65 | } 66 | } 67 | if saveAs != "" { 68 | // config file not exist 69 | return viper.WriteConfigAs(saveAs) 70 | } 71 | return viper.WriteConfig() 72 | } 73 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # list make targets https://gist.github.com/pvdb/777954 3 | # 4 | # It's necessary to set this because some environments don't link sh -> bash. 5 | SHELL := /bin/bash 6 | 7 | # We don't need make's built-in rules. 8 | .SUFFIXES: 9 | MAKEFLAGS += --no-builtin-rules 10 | 11 | # Constants used throughout. 12 | .EXPORT_ALL_VARIABLES: 13 | OUT_DIR ?= _output 14 | BIN_DIR := $(OUT_DIR)/bin 15 | BINARY ?= p2p-proxy 16 | VERSION ?= v0.0.2 17 | COMMIT_SHA = $(shell git rev-parse --short HEAD) 18 | 19 | MODULE = $(shell go list -m) 20 | GO_SOURCE_FILES := $(shell find . -type f -name '*.go') 21 | GO_TAGS ?= 22 | #GO_LDFLAGS ?= "-s -w" 23 | GO_LDFLAGS ?= 24 | 25 | # defined in metadata/metadata.go 26 | METADATA_VAR = Version=$(VERSION) 27 | METADATA_VAR += CommitSHA=$(COMMIT_SHA) 28 | 29 | RELEASE_PLATFORMS = darwin-amd64 linux-amd64 windows-amd64 30 | 31 | extensions.windows = .exe 32 | 33 | .PHONY: build 34 | build: $(OUT_DIR)/bin/$(BINARY) 35 | 36 | $(OUT_DIR)/bin/$(BINARY): GO_LDFLAGS += $(METADATA_VAR:%=-X $(MODULE)/metadata.%) 37 | $(OUT_DIR)/bin/$(BINARY): $(GO_SOURCE_FILES) 38 | @echo "Building $@" 39 | @mkdir -p $(@D) 40 | GOBIN=$(abspath $(@D)) go install -tags "$(GO_TAGS)" -ldflags "-s -w $(GO_LDFLAGS)" $(MODULE) 41 | @touch $@ 42 | 43 | # builds release packages for all target platforms 44 | .PHONY: release 45 | release: $(RELEASE_PLATFORMS:%=%) 46 | 47 | .PHONY: $(RELEASE_PLATFORMS:%=%) 48 | $(RELEASE_PLATFORMS:%=%): GO_LDFLAGS += $(METADATA_VAR:%=-X $(MODULE)/metadata.%) 49 | $(RELEASE_PLATFORMS:%=%): %: $(OUT_DIR)/%/bin/$(BINARY) README.md 50 | $(eval GOOS = $(word 1,$(subst -, ,$(@F)))) 51 | @mkdir -p $(OUT_DIR)/$(@F)/release 52 | @cp $(OUT_DIR)/$(@F)/bin/* $(OUT_DIR)/$(@F)/release/ 53 | @cp README.md $(OUT_DIR)/$(@F)/release/ 54 | @[ "$(GOOS)" == "windows" ] && cd $(OUT_DIR)/$(@F)/release && zip $(BINARY)-$(@F).$(VERSION).zip * || true 55 | @[ "$(GOOS)" != "windows" ] && cd $(OUT_DIR)/$(@F)/release && tar -czvf $(BINARY)-$(@F).$(VERSION).tar.gz * || true 56 | 57 | 58 | # explicit targets for all platform executables 59 | $(RELEASE_PLATFORMS:%=$(OUT_DIR)/%/bin/$(BINARY)): $(GO_SOURCE_FILES) 60 | $(eval platform = $(patsubst $(OUT_DIR)/%/bin,%,$(@D))) 61 | $(eval GOOS = $(word 1,$(subst -, ,$(platform)))) 62 | $(eval GOARCH = $(word 2,$(subst -, ,$(platform)))) 63 | @echo "Building $@ for $(GOOS)-$(GOARCH)" 64 | mkdir -p $(@D) 65 | GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@$(extensions.$(GOOS)) -tags "$(GO_TAGS)" -ldflags "-s -w $(GO_LDFLAGS)" $(MODULE) 66 | 67 | .PHONY: clean 68 | clean: 69 | @rm -rf $(OUT_DIR) 70 | -------------------------------------------------------------------------------- /proxy/server.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | cfg "github.com/diandianl/p2p-proxy/config" 8 | "github.com/diandianl/p2p-proxy/log" 9 | "github.com/diandianl/p2p-proxy/p2p" 10 | "github.com/diandianl/p2p-proxy/protocol" 11 | 12 | "github.com/libp2p/go-libp2p-core/discovery" 13 | "github.com/libp2p/go-libp2p-core/host" 14 | p2pproto "github.com/libp2p/go-libp2p-core/protocol" 15 | discovery2 "github.com/libp2p/go-libp2p-discovery" 16 | gostream "github.com/libp2p/go-libp2p-gostream" 17 | "go.uber.org/multierr" 18 | ) 19 | 20 | type ProxyServer interface { 21 | Start(ctx context.Context) error 22 | 23 | Stop() error 24 | } 25 | 26 | func New(cfg *cfg.Config) (ProxyServer, error) { 27 | if err := cfg.Validate(true); err != nil { 28 | return nil, err 29 | } 30 | return &proxyServer{logger: log.NewSubLogger("proxy"), cfg: cfg}, nil 31 | } 32 | 33 | type proxyServer struct { 34 | logger log.Logger 35 | 36 | cfg *cfg.Config 37 | 38 | node host.Host 39 | 40 | services []protocol.Service 41 | } 42 | 43 | func (s *proxyServer) Start(ctx context.Context) error { 44 | 45 | logger := s.logger 46 | defer logger.Sync() 47 | 48 | logger.Infof("Starting Proxy Server") 49 | 50 | c := s.cfg 51 | 52 | if len(c.Proxy.Protocols) == 0 { 53 | return errors.New("'Config.Proxy.Protocols' can not be empty") 54 | } 55 | 56 | h, rd, err := p2p.NewHostAndDiscovererAndBootstrap(ctx, c) 57 | if err != nil { 58 | return err 59 | } 60 | s.node = h 61 | 62 | for _, proto := range c.Proxy.Protocols { 63 | svc, err := protocol.NewService(protocol.Protocol(proto.Protocol), proto.Config) 64 | if err != nil { 65 | return err 66 | } 67 | s.services = append(s.services, svc) 68 | logger.Infof("Supporting %s service", svc.Protocol()) 69 | go func() { 70 | err := s.startService(ctx, svc) 71 | if err != nil { 72 | s.logger.Errorf("start proxy service [%s], ", svc.Protocol(), err) 73 | } 74 | }() 75 | } 76 | 77 | discovery2.Advertise(ctx, rd, c.ServiceTag, discovery.TTL(c.Proxy.ServiceAdvertiseInterval)) 78 | 79 | <-ctx.Done() 80 | return s.Stop() 81 | } 82 | 83 | func (s *proxyServer) startService(ctx context.Context, svc protocol.Service) error { 84 | l, err := gostream.Listen(s.node, p2pproto.ID(svc.Protocol())) 85 | if err != nil { 86 | return err 87 | } 88 | return svc.Serve(ctx, l) 89 | } 90 | 91 | func (s *proxyServer) Stop() error { 92 | ctx := context.Background() 93 | errs := make([]error, 0, len(s.services)+1) 94 | for _, svc := range s.services { 95 | errs = append(errs, svc.Shutdown(ctx)) 96 | } 97 | errs = append(errs, s.node.Close()) 98 | return multierr.Combine(errs...) 99 | } 100 | -------------------------------------------------------------------------------- /protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "net" 8 | 9 | "github.com/diandianl/p2p-proxy/log" 10 | ) 11 | 12 | const ( 13 | HTTP Protocol = "/p2p-proxy/http/0.0.1" 14 | 15 | Socks5 Protocol = "/p2p-proxy/socks5/0.0.1" 16 | 17 | Shadowsocks Protocol = "/p2p-proxy/shadowsocks/0.0.1" 18 | ) 19 | 20 | type Protocol string 21 | 22 | type Service interface { 23 | Protocol() Protocol 24 | 25 | Serve(context.Context, net.Listener) error 26 | 27 | Shutdown(context.Context) error 28 | } 29 | 30 | type ServiceFactory func(logger log.Logger, cfg map[string]interface{}) (Service, error) 31 | 32 | type Listener interface { 33 | io.Closer 34 | 35 | Protocol() Protocol 36 | 37 | Accept() (net.Conn, error) 38 | } 39 | 40 | type ListenerFactory func(logger log.Logger, listen string) (Listener, error) 41 | 42 | type metadata struct { 43 | protocol Protocol 44 | short string 45 | svcFactory ServiceFactory 46 | lsrFactory ListenerFactory 47 | } 48 | 49 | var svcRegistry = map[Protocol]*metadata{} 50 | 51 | func RegisterServiceFactory(protocol Protocol, short string, factory ServiceFactory) error { 52 | if _, ok := svcRegistry[protocol]; ok { 53 | return fmt.Errorf("duplicate registration, Protocol [%s] Factory registered", protocol) 54 | } 55 | svcRegistry[protocol] = &metadata{protocol: protocol, short: short, svcFactory: factory} 56 | return nil 57 | } 58 | 59 | func NewService(protocol Protocol, cfg map[string]interface{}) (Service, error) { 60 | m, ok := svcRegistry[protocol] 61 | if !ok { 62 | if len(svcRegistry) == 0 { 63 | return nil, fmt.Errorf("you should first call 'func RegisterServiceFactory' to register") 64 | } 65 | return nil, fmt.Errorf("unsupported Protocol [%s]", protocol) 66 | } 67 | logger := log.NewSubLogger(m.short) 68 | s, err := m.svcFactory(logger, cfg) 69 | if err != nil { 70 | return nil, err 71 | } 72 | if protocol != s.Protocol() { 73 | return nil, fmt.Errorf("mismatched protocol, expect [%s], got [%s]", protocol, s.Protocol()) 74 | } 75 | return s, nil 76 | } 77 | 78 | var lsrRegistry = map[Protocol]*metadata{} 79 | 80 | func RegisterListenerFactory(protocol Protocol, short string, factory ListenerFactory) error { 81 | if _, ok := lsrRegistry[protocol]; ok { 82 | return fmt.Errorf("duplicate registration, Protocol [%s] Factory registered", protocol) 83 | } 84 | lsrRegistry[protocol] = &metadata{protocol: protocol, short: short, lsrFactory: factory} 85 | return nil 86 | } 87 | 88 | func NewListener(protocol Protocol, listen string) (Listener, error) { 89 | m, ok := lsrRegistry[protocol] 90 | if !ok { 91 | if len(svcRegistry) == 0 { 92 | return nil, fmt.Errorf("you should first call 'func RegisterListenerFactory' to register") 93 | } 94 | return nil, fmt.Errorf("unsupported Protocol [%s]", protocol) 95 | } 96 | logger := log.NewSubLogger(m.short) 97 | s, err := m.lsrFactory(logger, listen) 98 | if err != nil { 99 | return nil, err 100 | } 101 | if protocol != s.Protocol() { 102 | return nil, fmt.Errorf("mismatched protocol, expect [%s], got [%s]", protocol, s.Protocol()) 103 | } 104 | return s, nil 105 | } 106 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "context" 20 | "github.com/diandianl/p2p-proxy/metadata" 21 | "os" 22 | 23 | "github.com/diandianl/p2p-proxy/cmd/endpoint" 24 | "github.com/diandianl/p2p-proxy/cmd/proxy" 25 | "github.com/diandianl/p2p-proxy/config" 26 | "github.com/diandianl/p2p-proxy/log" 27 | 28 | "github.com/spf13/cobra" 29 | "github.com/spf13/viper" 30 | ) 31 | 32 | // Execute adds all child commands to the root command and sets flags appropriately. 33 | // This is called by main.main(). It only needs to happen once to the rootCmd. 34 | func Execute(ctx context.Context) { 35 | 36 | viper.SetDefault("Version", metadata.Version) 37 | 38 | cmd := newCommand(ctx) 39 | 40 | if err := cmd.Execute(); err != nil { 41 | log.NewLogger().Error(err) 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | // new root command 47 | func newCommand(ctx context.Context) *cobra.Command { 48 | var cfgFile string 49 | var logLevel string 50 | 51 | var doGetCfg func(proxy bool) (*config.Config, error) 52 | 53 | cfgGetter := func(proxy bool) (*config.Config, error) { 54 | return doGetCfg(proxy) 55 | } 56 | 57 | ep := endpoint.NewEndpointCmd(ctx, cfgGetter) 58 | 59 | cmd := &cobra.Command{ 60 | Use: "p2p-proxy", 61 | Short: "A http(s) proxy based on P2P", 62 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 63 | cfgFile := cmd.Flags().Lookup("config").Value.String() 64 | cfg, cfgFile, err := config.LoadOrInitializeIfNotPresent(cfgFile) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | doGetCfg = func(proxy bool) (c *config.Config, err error) { 70 | err = cfg.Validate(proxy) 71 | if err != nil { 72 | return nil, err 73 | } 74 | err = cfg.SetupLogging(logLevel) 75 | if err != nil { 76 | return nil, err 77 | } 78 | logger := log.NewLogger() 79 | if err = logger.Sync(); err != nil { 80 | logger.Warn("Sync log ", err) 81 | } 82 | logger.Debugf("Using config file: %s", cfgFile) 83 | return cfg, nil 84 | } 85 | return err 86 | }, 87 | RunE: func(cmd *cobra.Command, args []string) error { 88 | cmd.SilenceUsage = true 89 | return ep.RunE(cmd, args) 90 | }, 91 | } 92 | 93 | cmd.PersistentFlags().StringVar(&logLevel, "log-level", "INFO", "set logging level") 94 | 95 | cmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.p2p-proxy.yaml)") 96 | 97 | cmd.PersistentFlags().StringSlice("p2p-addr", []string{}, "peer listen addr(s)") 98 | viper.BindPFlag("P2P.Addr", cmd.PersistentFlags().Lookup("p2p-addr")) 99 | 100 | cmd.Flags().AddFlagSet(ep.Flags()) 101 | 102 | // cmd.AddCommand(initCmd) 103 | 104 | cmd.AddCommand(proxy.NewProxyCmd(ctx, cfgGetter)) 105 | 106 | return cmd 107 | } 108 | -------------------------------------------------------------------------------- /p2p/options.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/diandianl/p2p-proxy/config" 10 | "github.com/diandianl/p2p-proxy/log" 11 | 12 | "github.com/libp2p/go-libp2p" 13 | "github.com/libp2p/go-libp2p-core/crypto" 14 | "github.com/libp2p/go-libp2p-core/metrics" 15 | dhtopts "github.com/libp2p/go-libp2p-kad-dht/opts" 16 | ) 17 | 18 | func hostOptions(ctx context.Context, c *config.Config) (opts []libp2p.Option, err error) { 19 | var opt libp2p.Option 20 | if opt, err = identity(c.P2P.Identity.PrivKey); err != nil { 21 | return 22 | } 23 | opts = append(opts, opt) 24 | 25 | if !(c.Work4Proxy() || c.P2P.AutoNATService || c.P2P.EnableAutoRelay) { 26 | opts = append(opts, libp2p.NoListenAddrs) 27 | } else { 28 | if opt, err = listenAddrs(c.P2P.Addrs...); err != nil { 29 | return 30 | } 31 | opts = append(opts, opt) 32 | } 33 | 34 | if c.P2P.BandWidthReporter.Enable { 35 | if opt, err = BandwidthReporter(ctx, c.P2P.BandWidthReporter.Interval); err != nil { 36 | return 37 | } 38 | opts = append(opts, opt) 39 | } 40 | return 41 | } 42 | 43 | func dhtOptions(c *config.Config) ([]dhtopts.Option, error) { 44 | return []dhtopts.Option{dhtopts.Client(c.P2P.DHT.Client)}, nil 45 | } 46 | 47 | func identity(privKey string) (libp2p.Option, error) { 48 | priv, err := base64.StdEncoding.DecodeString(privKey) 49 | if err != nil { 50 | return nil, err 51 | } 52 | pk, err := crypto.UnmarshalPrivateKey(priv) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return libp2p.Identity(pk), nil 57 | } 58 | 59 | func listenAddrs(addrs ...string) (libp2p.Option, error) { 60 | return libp2p.ListenAddrStrings(addrs...), nil 61 | } 62 | 63 | func BandwidthReporter(ctx context.Context, period time.Duration) (libp2p.Option, error) { 64 | logger := log.NewSubLogger("reporter") 65 | 66 | counter := metrics.NewBandwidthCounter() 67 | ticker := time.NewTicker(period) 68 | 69 | go func() { 70 | for { 71 | select { 72 | case <-ticker.C: 73 | stats := counter.GetBandwidthTotals() 74 | logger.Infof("BW Speed: TIN %s, TOUT %s, RIN %s, ROUT %s\n", 75 | byteCountBinary(stats.TotalIn), 76 | byteCountBinary(stats.TotalOut), 77 | byteRateBinary(stats.RateIn), 78 | byteRateBinary(stats.RateOut)) 79 | case <-ctx.Done(): 80 | ticker.Stop() 81 | return 82 | } 83 | } 84 | }() 85 | 86 | return libp2p.BandwidthReporter(counter), nil 87 | } 88 | 89 | /* 90 | func byteCountDecimal(b int64) string { 91 | const unit = 1000 92 | if b < unit { 93 | return fmt.Sprintf("%d B", b) 94 | } 95 | div, exp := int64(unit), 0 96 | for n := b / unit; n >= unit; n /= unit { 97 | div *= unit 98 | exp++ 99 | } 100 | return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp]) 101 | } 102 | */ 103 | 104 | func byteCountBinary(b int64) string { 105 | const unit = 1024 106 | if b < unit { 107 | return fmt.Sprintf("%d B", b) 108 | } 109 | div, exp := int64(unit), 0 110 | for n := b / unit; n >= unit; n /= unit { 111 | div *= unit 112 | exp++ 113 | } 114 | return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) 115 | } 116 | 117 | func byteRateBinary(b float64) string { 118 | const unit = 1024 119 | if b < unit { 120 | return fmt.Sprintf("%.1f B/s", b) 121 | } 122 | div, exp := int64(unit), 0 123 | for n := b / unit; n >= unit; n /= unit { 124 | div *= unit 125 | exp++ 126 | } 127 | return fmt.Sprintf("%.1f %ciB/s", b/float64(div), "KMGTPE"[exp]) 128 | } 129 | -------------------------------------------------------------------------------- /protocol/service/shadowsocks/shadowsocks.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "context" 5 | "github.com/diandianl/p2p-proxy/log" 6 | "github.com/diandianl/p2p-proxy/protocol" 7 | "github.com/diandianl/p2p-proxy/relay" 8 | "github.com/shadowsocks/go-shadowsocks2/socks" 9 | "io" 10 | "net" 11 | "time" 12 | 13 | sscore "github.com/shadowsocks/go-shadowsocks2/core" 14 | ) 15 | 16 | func init() { 17 | err := protocol.RegisterServiceFactory(protocol.Shadowsocks, "shadowsocks", New) 18 | if err != nil { 19 | panic(err) 20 | } 21 | } 22 | 23 | func New(logger log.Logger, cfg map[string]interface{}) (protocol.Service, error) { 24 | 25 | var ( 26 | ciper = "AES-128-GCM" 27 | password = "123456" 28 | ) 29 | 30 | if c, ok := cfg["Ciper"]; ok { 31 | ciper = c.(string) 32 | } 33 | if p, ok := cfg["Password"]; ok { 34 | password = p.(string) 35 | } 36 | 37 | logger.Infof("New shadowsocks with ciper: %s", ciper) 38 | cip, err := sscore.PickCipher(ciper, nil, password) 39 | 40 | if err != nil { 41 | return nil, err 42 | } 43 | return &shadowsocksService{logger: logger, shadow: cip.StreamConn}, nil 44 | } 45 | 46 | type shadowsocksService struct { 47 | logger log.Logger 48 | 49 | shadow func(net.Conn) net.Conn 50 | 51 | listener net.Listener 52 | 53 | shuttingDown bool 54 | } 55 | 56 | func (_ *shadowsocksService) Protocol() protocol.Protocol { 57 | return protocol.Shadowsocks 58 | } 59 | 60 | func (s *shadowsocksService) Serve(ctx context.Context, l net.Listener) error { 61 | s.listener = l 62 | for { 63 | c, err := l.Accept() 64 | if err != nil { 65 | return s.errorTriggeredByShutdown(err) 66 | } 67 | go s.handleConn(c) 68 | } 69 | } 70 | 71 | func (s *shadowsocksService) handleConn(conn net.Conn) { 72 | 73 | defer conn.Close() 74 | 75 | logger := s.logger 76 | 77 | conn = s.shadow(conn) 78 | tgt, err := socks.ReadAddr(conn) 79 | if err != nil { 80 | if s.errorTriggeredByShutdown(err) != nil { 81 | logger.Warn("Read addr ", err) 82 | } 83 | return 84 | } 85 | 86 | rc, err := net.Dial("tcp", tgt.String()) 87 | if err != nil { 88 | logger.Warnf("dial to target [%s] ", tgt, err) 89 | return 90 | } 91 | 92 | if rc, ok := rc.(*net.TCPConn); ok { 93 | err := rc.SetKeepAlive(true) 94 | if err != nil { 95 | logger.Warn("Set remote connection keepalive ", err) 96 | } 97 | } 98 | 99 | if err := relay.CloseAfterRelay(rc, conn); s.errorTriggeredByShutdown(err) != nil { 100 | logger.Warn("Relay failure ", err) 101 | } 102 | } 103 | 104 | func (s *shadowsocksService) errorTriggeredByShutdown(err error) error { 105 | if s.shuttingDown { 106 | return nil 107 | } 108 | return err 109 | } 110 | 111 | // relay copies between left and right bidirectionally. Returns number of 112 | // bytes copied from right to left, from left to right, and any error occurred. 113 | func relayx(left, right net.Conn) (int64, int64, error) { 114 | type res struct { 115 | N int64 116 | Err error 117 | } 118 | ch := make(chan res) 119 | 120 | go func() { 121 | n, err := io.Copy(right, left) 122 | right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right 123 | left.SetDeadline(time.Now()) // wake up the other goroutine blocking on left 124 | ch <- res{n, err} 125 | }() 126 | 127 | n, err := io.Copy(left, right) 128 | right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right 129 | left.SetDeadline(time.Now()) // wake up the other goroutine blocking on left 130 | rs := <-ch 131 | 132 | if err == nil { 133 | err = rs.Err 134 | } 135 | return n, rs.N, err 136 | } 137 | 138 | func (s *shadowsocksService) Shutdown(ctx context.Context) error { 139 | s.shuttingDown = true 140 | if s.listener != nil { 141 | return s.listener.Close() 142 | } 143 | return nil 144 | } 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # p2p-proxy 2 | 3 | `p2p-proxy` 是一个命令行式的,基于[`libp2p`](https://github.com/libp2p/go-libp2p)和[`goproxy`](github.com/elazarl/goproxy)实现的`http(s)`代理工具。 4 | 5 | ## 实现原理 6 | ``` 7 | +----------------------+ +-----------------------------------+ 8 | | Endpoint | | Proxy | 9 | | +-----+ +-----+ | /proxy-example/0.0.1 | +--------------+ +----------+ | 10 | | | TCP | <--> | p2p | | /secio/1.0.0 | | p2p|gostream + <-> | goproxy | | 11 | | +-----+ +-----+ | <----------------------> | +--------------+ +----------+ | 12 | +----------------------+ +-----------------------------------+ 13 | ``` 14 | 15 | ## 运行方式 16 | `p2p-proxy`为命令行程序,启动分为远端和本地端。 17 | 18 | ### 远端 19 | 远端为实际代理服务器,帮助信息如下: 20 | ``` 21 | ./p2p-proxy proxy -h 22 | Start a proxy server peer 23 | 24 | Usage: 25 | p2p-proxy proxy [flags] 26 | 27 | Flags: 28 | -h, --help help for proxy 29 | 30 | Global Flags: 31 | -c, --config string config file (default is $HOME/.p2p-proxy.yaml) 32 | --log-level string set logging level (default "INFO") 33 | --p2p-addr strings peer listen addr(s) 34 | ``` 35 | 启动示例: 36 | ```shell script 37 | # ./p2p-proxy proxy 38 | Using config file: /Users/someuser/.p2p-proxy.yaml 39 | Proxy server is ready 40 | libp2p-peer addresses: 41 | /ip4/127.0.0.1/tcp/8888/ipfs/QmXktjtfrwwjPYowB9DV7qdViLnjAKHbYgGA4oSjuwYAAY 42 | ``` 43 | 44 | ### 本地端 45 | 本地端为代理服务的入口,目前只支持代理`http(s)`,命令帮助如下: 46 | ``` 47 | # ./p2p-proxy -h 48 | A http(s) proxy based on P2P 49 | 50 | Usage: 51 | p2p-proxy [flags] 52 | p2p-proxy [command] 53 | 54 | Available Commands: 55 | help Help about any command 56 | init Generate and write default config 57 | proxy Start a proxy server peer 58 | 59 | Flags: 60 | -c, --config string config file (default is $HOME/.p2p-proxy.yaml) 61 | -h, --help help for p2p-proxy 62 | --http string local http(s) proxy agent listen address 63 | --log-level string set logging level (default "INFO") 64 | --p2p-addr strings peer listen addr(s) 65 | -p, --proxy string proxy server address 66 | 67 | Use "p2p-proxy [command] --help" for more information about a command. 68 | ``` 69 | 启动实例: 70 | ```shell script 71 | ./p2p-proxy 72 | Using config file: /Users/someuser/.p2p-proxy.yaml 73 | proxy listening on 127.0.0.1:8010 74 | ``` 75 | 76 | ## 配置文件说明 77 | 如果不指定,默认使用`$HOME/.p2p-proxy.yaml`。程序首次启动时是会自动创建配置文件,并生成节点id等信息写入配置文件。 78 | 79 | 配置文件说明: 80 | ```yaml 81 | # 配置版本 82 | Version: v0.0.2 83 | # 配置日志级别 84 | Logging: 85 | File: ~/p2p-proxy.log 86 | Format: console 87 | Level: 88 | all: info 89 | p2p-proxy: debug 90 | # 在dht网络暴露/查找服务的标签 91 | ServiceTag: p2p-proxy/0.0.1 92 | # p2p 网络配置 93 | P2P: 94 | Identity: 95 | # 私钥 96 | PrivKey: CAASpe...k15GFrqg== 97 | # 外部观察到的地址激活阈值,0表示使用libp2p默认(4),即有4个外表节点确认同一外部访问地址则讲该地址添加到本节点地址列表 98 | ObservedAddrActivationThresh: 0 99 | # 本节点监听地址 100 | Addrs: 101 | - /ip4/0.0.0.0/tcp/8010 102 | # boot 节点 103 | BootstrapPeers: 104 | - /ip4/127.0.0.1/tcp/8888/ipfs/Qm...MW 105 | - /dns4/proxy.server.com/tcp/8888/ipfs/QmW...yMW 106 | # 带宽流浪统计 107 | BandWidthReporter: 108 | Enable: false 109 | Interval: 0s 110 | # 启用自动中继 111 | EnableAutoRelay: false 112 | # 启用NAT 服务端 113 | AutoNATService: false 114 | # DHT 配置 115 | DHT: 116 | Client: false 117 | # 代理服务设置 118 | Proxy: 119 | # 支持的协议列表 120 | Protocols: 121 | - Protocol: /p2p-proxy/http/0.0.1 122 | Config: {} 123 | ServiceAdvertiseInterval: 1h0m0s 124 | # 本地端配置 125 | Endpoint: 126 | # 本地端支持(监听)的协议,由远端提供支持 127 | ProxyProtocols: 128 | - Protocol: /p2p-proxy/http/0.0.1 129 | # 协议监听地址 130 | Listen: 127.0.0.1:8010 131 | # 代理服务发现时间间隔 132 | ServiceDiscoveryInterval: 1h0m0s 133 | # 代理服务节点均衡策略 134 | Balancer: round_robin 135 | # 开启交互模式,提供 cli 命令查看内部信息 136 | Interactive: false 137 | ``` -------------------------------------------------------------------------------- /endpoint/endpoint.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "net" 7 | "sync" 8 | "time" 9 | 10 | "github.com/diandianl/p2p-proxy/config" 11 | "github.com/diandianl/p2p-proxy/endpoint/balancer" 12 | "github.com/diandianl/p2p-proxy/log" 13 | "github.com/diandianl/p2p-proxy/p2p" 14 | "github.com/diandianl/p2p-proxy/protocol" 15 | "github.com/diandianl/p2p-proxy/relay" 16 | 17 | "github.com/libp2p/go-libp2p-core/discovery" 18 | "github.com/libp2p/go-libp2p-core/host" 19 | "github.com/libp2p/go-libp2p-core/network" 20 | "github.com/libp2p/go-libp2p-core/peer" 21 | p2pproto "github.com/libp2p/go-libp2p-core/protocol" 22 | discovery2 "github.com/libp2p/go-libp2p-discovery" 23 | "go.uber.org/multierr" 24 | ) 25 | 26 | type Endpoint interface { 27 | Start(ctx context.Context) error 28 | 29 | Stop() error 30 | } 31 | 32 | func New(cfg *config.Config) (Endpoint, error) { 33 | if err := cfg.Validate(false); err != nil { 34 | return nil, err 35 | } 36 | return &endpoint{ 37 | logger: log.NewSubLogger("endpoint"), 38 | cfg: cfg, 39 | proxies: make(map[peer.ID]struct{}), 40 | stopping: make(chan struct{}), 41 | }, nil 42 | } 43 | 44 | type endpoint struct { 45 | logger log.Logger 46 | 47 | cfg *config.Config 48 | 49 | node host.Host 50 | 51 | discoverer discovery.Discoverer 52 | 53 | listeners []protocol.Listener 54 | 55 | balancer balancer.Balancer 56 | 57 | sync.Mutex 58 | proxies map[peer.ID]struct{} 59 | 60 | stopping chan struct{} 61 | } 62 | 63 | func (e *endpoint) Start(ctx context.Context) (err error) { 64 | 65 | logger := e.logger 66 | defer logger.Sync() 67 | 68 | logger.Info("Starting Endpoint") 69 | 70 | c := e.cfg 71 | 72 | if len(c.Endpoint.ProxyProtocols) == 0 { 73 | return errors.New("'Config.Endpoint.ProxyProtocols' can not be empty") 74 | } 75 | 76 | e.balancer, err = balancer.New(c.Endpoint.Balancer, e) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | logger.Debugf("Endpoint using '%s' balancer", e.balancer.Name()) 82 | 83 | e.node, e.discoverer, err = p2p.NewHostAndDiscovererAndBootstrap(ctx, c) 84 | if err != nil { 85 | return err 86 | } 87 | 88 | go e.syncProxies(ctx) 89 | 90 | for _, p := range c.Endpoint.ProxyProtocols { 91 | lsr, err := protocol.NewListener(protocol.Protocol(p.Protocol), p.Listen) 92 | if err != nil { 93 | return err 94 | } 95 | logger.Infof("Enable %s service, listen at: %s", lsr.Protocol(), p.Listen) 96 | e.listeners = append(e.listeners, lsr) 97 | 98 | go func() { 99 | err := e.startListener(ctx, lsr) 100 | if err != nil { 101 | e.logger.Errorf("start proxy listener [%s], ", lsr.Protocol(), err) 102 | } 103 | }() 104 | } 105 | 106 | <-ctx.Done() 107 | return e.Stop() 108 | } 109 | 110 | func (e *endpoint) startListener(ctx context.Context, lsr protocol.Listener) error { 111 | for { 112 | conn, err := lsr.Accept() 113 | if err != nil { 114 | return e.errorTriggeredByStop(err) 115 | } 116 | go e.connHandler(ctx, lsr.Protocol(), conn) 117 | } 118 | } 119 | 120 | func (e *endpoint) connHandler(ctx context.Context, p protocol.Protocol, conn net.Conn) { 121 | stream, err := e.newProxyStream(ctx, p, 3) 122 | // If an error happens, we write an error for response. 123 | if err != nil { 124 | if e.errorTriggeredByStop(err) != nil { 125 | e.logger.Warn("New stream ", err) 126 | } 127 | return 128 | } 129 | if err := relay.CloseAfterRelay(conn, stream); e.errorTriggeredByStop(err) != nil { 130 | e.logger.Warn("Relay failure: ", err) 131 | } 132 | } 133 | 134 | func (e *endpoint) isStopping() bool { 135 | select { 136 | case <-e.stopping: 137 | return true 138 | default: 139 | return false 140 | } 141 | } 142 | 143 | func (e *endpoint) errorTriggeredByStop(err error) error { 144 | select { 145 | case <-e.stopping: 146 | return nil 147 | default: 148 | return err 149 | } 150 | } 151 | 152 | func (e *endpoint) newProxyStream(ctx context.Context, p protocol.Protocol, retry int) (network.Stream, error) { 153 | proxy, err := e.balancer.Next(p) 154 | if err != nil { 155 | if balancer.IsNewNotEnoughProxiesError(err) && retry > 0 { 156 | proxies, err := e.DiscoveryProxies(ctx) 157 | if err != nil { 158 | return nil, err 159 | } 160 | e.UpdateProxies(proxies) 161 | return e.newProxyStream(ctx, p, 0) 162 | } 163 | return nil, err 164 | } 165 | s, err := e.node.NewStream(ctx, proxy, p2pproto.ID(p)) 166 | if err != nil { 167 | e.DeleteProxy(proxy) 168 | retry-- 169 | if retry <= 0 { 170 | return nil, err 171 | } 172 | return e.newProxyStream(ctx, p, retry) 173 | } 174 | return s, nil 175 | } 176 | 177 | func (e *endpoint) syncProxies(ctx context.Context) { 178 | ticker := time.NewTicker(e.cfg.Endpoint.ServiceDiscoveryInterval) 179 | for { 180 | if proxies, err := e.DiscoveryProxies(ctx); err != nil { 181 | e.logger.Error(err) 182 | } else { 183 | e.UpdateProxies(proxies) 184 | } 185 | select { 186 | case <-ticker.C: 187 | case <-ctx.Done(): 188 | ticker.Stop() 189 | return 190 | } 191 | } 192 | } 193 | 194 | func (e *endpoint) UpdateProxies(proxies []peer.ID) { 195 | e.Lock() 196 | defer e.Unlock() 197 | for _, p := range proxies { 198 | e.proxies[p] = struct{}{} 199 | } 200 | } 201 | 202 | func (e *endpoint) GetProxies(p protocol.Protocol) []peer.ID { 203 | if len(e.proxies) == 0 { 204 | return nil 205 | } 206 | e.Lock() 207 | defer e.Unlock() 208 | proxies := make([]peer.ID, 0, len(e.proxies)) 209 | for p, _ := range e.proxies { 210 | proxies = append(proxies, p) 211 | } 212 | return proxies 213 | } 214 | 215 | func (e *endpoint) DeleteProxy(id peer.ID) { 216 | e.Lock() 217 | defer e.Unlock() 218 | delete(e.proxies, id) 219 | } 220 | 221 | func (e *endpoint) DiscoveryProxies(ctx context.Context) ([]peer.ID, error) { 222 | addrs, err := discovery2.FindPeers(ctx, e.discoverer, e.cfg.ServiceTag) 223 | if err != nil { 224 | return nil, err 225 | } 226 | proxies := make([]peer.ID, 0, len(addrs)) 227 | for _, addr := range addrs { 228 | proxies = append(proxies, addr.ID) 229 | } 230 | return proxies, nil 231 | } 232 | 233 | func (e *endpoint) Stop() error { 234 | close(e.stopping) 235 | errs := make([]error, 0, len(e.listeners)+1) 236 | for _, lsr := range e.listeners { 237 | errs = append(errs, lsr.Close()) 238 | } 239 | errs = append(errs, e.node.Close()) 240 | return multierr.Combine(errs...) 241 | } 242 | -------------------------------------------------------------------------------- /p2p/libp2p.go: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | 8 | "github.com/diandianl/p2p-proxy/config" 9 | "github.com/diandianl/p2p-proxy/log" 10 | 11 | "github.com/ipfs/go-cid" 12 | ds "github.com/ipfs/go-datastore" 13 | dssync "github.com/ipfs/go-datastore/sync" 14 | // "github.com/kingwel-xie/xcli" 15 | "github.com/libp2p/go-libp2p" 16 | autonat "github.com/libp2p/go-libp2p-autonat-svc" 17 | "github.com/libp2p/go-libp2p-core/discovery" 18 | "github.com/libp2p/go-libp2p-core/host" 19 | "github.com/libp2p/go-libp2p-core/peer" 20 | "github.com/libp2p/go-libp2p-core/peerstore" 21 | "github.com/libp2p/go-libp2p-core/routing" 22 | discovery2 "github.com/libp2p/go-libp2p-discovery" 23 | dht "github.com/libp2p/go-libp2p-kad-dht" 24 | dhtopts "github.com/libp2p/go-libp2p-kad-dht/opts" 25 | secio "github.com/libp2p/go-libp2p-secio" 26 | "github.com/libp2p/go-libp2p/p2p/protocol/identify" 27 | maddr "github.com/multiformats/go-multiaddr" 28 | mh "github.com/multiformats/go-multihash" 29 | "github.com/urfave/cli/v2" 30 | ) 31 | 32 | func NewHostAndDiscovererAndBootstrap(ctx context.Context, cfg *config.Config) (h host.Host, dis discovery.Discovery, err error) { 33 | logger := log.NewSubLogger("p2p") 34 | 35 | hostOpts, err := hostOptions(ctx, cfg) 36 | if err != nil { 37 | return 38 | } 39 | 40 | dhtOpts, err := dhtOptions(cfg) 41 | if err != nil { 42 | return 43 | } 44 | 45 | var bootPeers []maddr.Multiaddr // dht.DefaultBootstrapPeers 46 | 47 | if len(cfg.P2P.BootstrapPeers) > 0 { 48 | bootPeers, err = convertToMAddr(cfg.P2P.BootstrapPeers) 49 | if err != nil { 50 | return 51 | } 52 | } else if len(config.Default.P2P.BootstrapPeers) > 0 { 53 | bootPeers, err = convertToMAddr(config.Default.P2P.BootstrapPeers) 54 | if err != nil { 55 | return 56 | } 57 | } 58 | 59 | dhtDs := dssync.MutexWrap(ds.NewMapDatastore()) 60 | 61 | dhtOpts = append(dhtOpts, func(options *dhtopts.Options) error { 62 | options.Datastore = dhtDs 63 | return nil 64 | }) 65 | 66 | if cfg.P2P.Identity.ObservedAddrActivationThresh > 0 { 67 | identify.ActivationThresh = cfg.P2P.Identity.ObservedAddrActivationThresh 68 | logger.Debugf("Override LibP2P observed address activation thresh with %d", identify.ActivationThresh) 69 | } 70 | 71 | dhtCreater := newDHTConstructor(ctx, dhtOpts) 72 | 73 | if cfg.P2P.EnableAutoRelay { 74 | logger.Debugf("Enable auto relay") 75 | hostOpts = append(hostOpts, 76 | libp2p.Routing(func(h host.Host) (routing routing.PeerRouting, err error) { 77 | return dhtCreater(h) 78 | }), 79 | libp2p.EnableAutoRelay(), 80 | ) 81 | } 82 | 83 | h, err = libp2p.New(ctx, hostOpts...) 84 | if err != nil { 85 | return nil, nil, err 86 | } 87 | 88 | if len(h.Addrs()) > 0 { 89 | logger.Infof("P2P [/ipfs/%s] working addrs: %s", h.ID().Pretty(), h.Addrs()) 90 | } 91 | 92 | if cfg.P2P.AutoNATService { 93 | logger.Debugf("Enable auto NAT service") 94 | _, err = autonat.NewAutoNATService(ctx, h, 95 | libp2p.Security(secio.ID, secio.New), 96 | libp2p.DefaultTransports, 97 | ) 98 | if err != nil { 99 | h.Close() 100 | return nil, nil, err 101 | } 102 | } 103 | 104 | router, err := dhtCreater(h) 105 | if err != nil { 106 | h.Close() 107 | return nil, nil, err 108 | } 109 | 110 | err = router.Bootstrap(ctx) 111 | if err != nil { 112 | h.Close() 113 | return nil, nil, err 114 | } 115 | 116 | var connected []peer.AddrInfo 117 | 118 | var wg sync.WaitGroup 119 | for _, peerAddr := range bootPeers { 120 | peerinfo, _ := peer.AddrInfoFromP2pAddr(peerAddr) 121 | wg.Add(1) 122 | go func(pi peer.AddrInfo) { 123 | defer wg.Done() 124 | if err := h.Connect(ctx, pi); err != nil { 125 | logger.Warn(err) 126 | } else { 127 | connected = append(connected, pi) 128 | logger.Debug("Connection established with bootstrap node:", pi) 129 | _, err = permanentAddAddrToPeerstore(h, peerAddr) 130 | if err != nil { 131 | logger.Warnf("Permanent add addr [%s] to peerstore: ", peerAddr, err) 132 | } 133 | } 134 | }(*peerinfo) 135 | } 136 | wg.Wait() 137 | 138 | dis = discovery2.NewRoutingDiscovery(router) 139 | 140 | return h, dis, nil 141 | } 142 | 143 | func extCmds() cli.Commands { 144 | 145 | return cli.Commands{ 146 | { 147 | Name: "a2cid", 148 | Category: "p2p", 149 | Usage: "a2cid ", 150 | Description: "calc str cid", 151 | Action: func(c *cli.Context) error { 152 | cid, err := a2cid(c.Args().First()) 153 | if err != nil { 154 | return err 155 | } 156 | fmt.Fprintln(c.App.Writer, cid.Hash().B58String()) 157 | return nil 158 | }, 159 | }, 160 | } 161 | } 162 | 163 | func a2cid(str string) (cid.Cid, error) { 164 | h, err := mh.Sum([]byte(str), mh.SHA2_256, -1) 165 | if err != nil { 166 | return cid.Undef, err 167 | } 168 | return cid.NewCidV1(cid.Raw, h), nil 169 | } 170 | 171 | func permanentAddAddrToPeerstore(h host.Host, ma maddr.Multiaddr) (peer.ID, error) { 172 | pid, err := ma.ValueForProtocol(maddr.P_IPFS) 173 | if err != nil { 174 | return "", err 175 | } 176 | 177 | peerid, err := peer.Decode(pid) 178 | if err != nil { 179 | return "", err 180 | } 181 | 182 | // Decapsulate the /ipfs/ part from the target 183 | // /ip4//ipfs/ becomes /ip4/ 184 | targetPeerAddr, _ := maddr.NewMultiaddr( 185 | fmt.Sprintf("/ipfs/%s", peer.Encode(peerid))) 186 | targetAddr := ma.Decapsulate(targetPeerAddr) 187 | 188 | // We have a peer ID and a targetAddr so we add 189 | // it to the peerstore so LibP2P knows how to contact it 190 | h.Peerstore().AddAddr(peerid, targetAddr, peerstore.PermanentAddrTTL) 191 | return peerid, nil 192 | } 193 | 194 | func newDHTConstructor(ctx context.Context, opts []dhtopts.Option) func(host.Host) (routing.Routing, error) { 195 | var ( 196 | once sync.Once 197 | router routing.Routing 198 | err error 199 | ) 200 | return func(h host.Host) (routing.Routing, error) { 201 | once.Do(func() { 202 | router, err = dht.New(ctx, h, opts...) 203 | }) 204 | return router, err 205 | } 206 | } 207 | 208 | func convertToMAddr(addrs []string) ([]maddr.Multiaddr, error) { 209 | maddrs := make([]maddr.Multiaddr, 0, len(addrs)) 210 | for _, addr := range addrs { 211 | ma, err := maddr.NewMultiaddr(addr) 212 | if err != nil { 213 | return nil, err 214 | } 215 | maddrs = append(maddrs, ma) 216 | } 217 | return maddrs, nil 218 | } 219 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/base64" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | "time" 14 | 15 | "github.com/diandianl/p2p-proxy/log" 16 | "github.com/diandianl/p2p-proxy/metadata" 17 | 18 | "github.com/libp2p/go-libp2p-core/crypto" 19 | "github.com/mitchellh/go-homedir" 20 | "github.com/spf13/viper" 21 | "gopkg.in/yaml.v2" 22 | ) 23 | 24 | const ( 25 | DefaultConfigPath = "~/.p2p-proxy.yaml" 26 | ) 27 | 28 | var InvalidErr = errors.New("config invalid or not checked") 29 | 30 | func init() { 31 | if peers, ok := os.LookupEnv("P2P_PROXY_DEFAULT_BOOT_PEERS"); ok { 32 | Default.P2P.BootstrapPeers = strings.Fields(peers) 33 | } 34 | } 35 | 36 | var Default = &Config{ 37 | P2P: P2P{ 38 | Addrs: []string{ 39 | "/ip4/0.0.0.0/tcp/8888", 40 | }, 41 | BootstrapPeers: []string{}, 42 | }, 43 | Logging: Logging{ 44 | File: "~/p2p-proxy.log", 45 | Format: "console", 46 | Level: map[string]string{ 47 | "all": "info", 48 | }, 49 | }, 50 | Version: metadata.Version, 51 | ServiceTag: "p2p-proxy/0.0.1", 52 | Proxy: Proxy{ 53 | Protocols: []Protocol{ 54 | { 55 | Protocol: "/p2p-proxy/http/0.0.1", 56 | Config: map[string]interface{}{}, 57 | }, 58 | { 59 | Protocol: "/p2p-proxy/shadowsocks/0.0.1", 60 | Config: map[string]interface{}{}, 61 | }, 62 | { 63 | Protocol: "/p2p-proxy/socks5/0.0.1", 64 | Config: map[string]interface{}{}, 65 | }, 66 | }, 67 | ServiceAdvertiseInterval: time.Hour, 68 | }, 69 | Endpoint: Endpoint{ 70 | ProxyProtocols: []ProxyProtocol{ 71 | { 72 | Protocol: "/p2p-proxy/http/0.0.1", 73 | Listen: "127.0.0.1:8010", 74 | }, 75 | { 76 | Protocol: "/p2p-proxy/shadowsocks/0.0.1", 77 | Listen: "127.0.0.1:8020", 78 | }, 79 | { 80 | Protocol: "/p2p-proxy/socks5/0.0.1", 81 | Listen: "127.0.0.1:8030", 82 | }, 83 | }, 84 | 85 | ServiceDiscoveryInterval: time.Hour, 86 | 87 | Balancer: "round_robin", 88 | }, 89 | Interactive: false, 90 | } 91 | 92 | func LoadOrInitializeIfNotPresent(configPath string) (cfg *Config, cfgFile string, err error) { 93 | cfgFile = configPath 94 | if len(cfgFile) == 0 { 95 | cfgFile = DefaultConfigPath 96 | } 97 | 98 | cfgFile, err = homedir.Expand(filepath.Clean(cfgFile)) 99 | if err != nil { 100 | return 101 | } 102 | _, err = os.Stat(cfgFile) 103 | if err != nil { 104 | if os.IsNotExist(err) { 105 | cfg, err = Initialize(cfgFile) 106 | if err != nil { 107 | return nil, cfgFile, err 108 | } 109 | return cfg, cfgFile, err 110 | } 111 | return 112 | } 113 | viper.SetConfigFile(cfgFile) 114 | viper.SetConfigType("yaml") 115 | viper.AutomaticEnv() 116 | 117 | err = viper.ReadInConfig() 118 | 119 | if err != nil { 120 | return 121 | } 122 | cfg = new(Config) 123 | err = viper.Unmarshal(cfg) 124 | if err != nil { 125 | return 126 | } 127 | 128 | if viper.GetString("Version") == "v0.0.1" { 129 | cfg.P2P.Identity.PrivKey = viper.GetString("Identity.PrivKey") 130 | cfg.P2P.Addrs = viper.GetStringSlice("P2P.Addr") 131 | 132 | cfg.Endpoint = Default.Endpoint 133 | cfg.Proxy = Default.Proxy 134 | cfg.ServiceTag = Default.ServiceTag 135 | cfg.Version = metadata.Version 136 | cfg, err = writeConfig(cfgFile, cfg) 137 | if err != nil { 138 | return nil, cfgFile, err 139 | } 140 | } 141 | return cfg, cfgFile, nil 142 | } 143 | 144 | func Initialize(cfgPath string) (*Config, error) { 145 | var cfg Config = *Default 146 | 147 | if runtime.GOOS == "windows" { 148 | // FIXME zap log output file, using url.Parse to parse file path, not work on windows 149 | cfg.Logging.File = "" 150 | } 151 | 152 | priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rand.Reader) 153 | if err != nil { 154 | return nil, err 155 | } 156 | privKey, err := crypto.MarshalPrivateKey(priv) 157 | 158 | if err != nil { 159 | return nil, err 160 | } 161 | cfg.P2P.Identity.PrivKey = base64.StdEncoding.EncodeToString(privKey) 162 | 163 | return writeConfig(cfgPath, &cfg) 164 | } 165 | 166 | func writeConfig(configPath string, cfg *Config) (*Config, error) { 167 | data, err := yaml.Marshal(cfg) 168 | if err != nil { 169 | return nil, err 170 | } 171 | err = ioutil.WriteFile(configPath, data, 0755) 172 | if err != nil { 173 | return nil, err 174 | } 175 | return cfg, nil 176 | } 177 | 178 | type Config struct { 179 | // config version 180 | Version string `yaml:"Version"` 181 | 182 | Logging Logging `yaml:"Logging"` 183 | 184 | ServiceTag string `yaml:"ServiceTag"` 185 | 186 | P2P P2P `yaml:"P2P"` 187 | 188 | Proxy Proxy `yaml:"Proxy"` 189 | 190 | Endpoint Endpoint `yaml:"Endpoint"` 191 | 192 | Interactive bool `yaml:"Interactive"` 193 | 194 | valid bool `yaml:"-"` 195 | work4proxy bool `yaml:"-"` 196 | } 197 | 198 | func (c *Config) Validate(proxy bool) error { 199 | if c.valid { 200 | return nil 201 | } 202 | if len(c.P2P.Identity.PrivKey) == 0 { 203 | return fmt.Errorf("no 'P2P.Identity.PrivKey' config") 204 | } 205 | 206 | if len(c.P2P.Addrs) == 0 { 207 | return fmt.Errorf("no 'P2P.Addrs' config") 208 | } 209 | if proxy { 210 | if len(c.Proxy.Protocols) == 0 { 211 | return fmt.Errorf("no 'Proxy.Protocols' config") 212 | } 213 | } else { 214 | if len(c.Endpoint.ProxyProtocols) == 0 { 215 | return fmt.Errorf("no 'Endpoint.ProxyProtocols' config") 216 | } 217 | if len(c.Endpoint.Balancer) == 0 { 218 | return fmt.Errorf("no 'Endpoint.Balancer' config") 219 | } 220 | } 221 | c.valid = true 222 | c.work4proxy = proxy 223 | return nil 224 | } 225 | 226 | func (c *Config) Work4Proxy() bool { 227 | return c.work4proxy 228 | } 229 | 230 | func (c *Config) SetupLogging(defaultLevel string) (err error) { 231 | if !c.valid { 232 | return InvalidErr 233 | } 234 | 235 | if len(defaultLevel) == 0 { 236 | defaultLevel = c.Logging.Level["all"] 237 | } 238 | delete(c.Logging.Level, "all") 239 | 240 | var logFile string 241 | if len(c.Logging.File) > 0 { 242 | logFile, err = homedir.Expand(filepath.Clean(c.Logging.File)) 243 | if err != nil { 244 | return err 245 | } 246 | } 247 | 248 | err = log.SetupLogging(logFile, c.Logging.Format, defaultLevel) 249 | if err != nil { 250 | return err 251 | } 252 | 253 | if len(c.Logging.Level) > 0 { 254 | for sub, lvl := range c.Logging.Level { 255 | err := log.SetLogLevel(sub, lvl) 256 | if err != nil { 257 | return err 258 | } 259 | } 260 | } 261 | return nil 262 | } 263 | 264 | type P2P struct { 265 | Identity Identity `yaml:"Identity"` 266 | // libp2p multi address 267 | Addrs []string `yaml:"Addrs"` 268 | 269 | BootstrapPeers []string `yaml:"BootstrapPeers"` 270 | 271 | BandWidthReporter BandWidthReporter `yaml:"BandWidthReporter"` 272 | 273 | EnableAutoRelay bool `yaml:"EnableAutoRelay"` 274 | 275 | AutoNATService bool `yaml:"AutoNATService"` 276 | 277 | DHT DHT `yaml:"DHT"` 278 | } 279 | 280 | type Proxy struct { 281 | Protocols []Protocol `yaml:"Protocols"` 282 | 283 | ServiceAdvertiseInterval time.Duration `yaml:"ServiceAdvertiseInterval"` 284 | } 285 | 286 | type Endpoint struct { 287 | ProxyProtocols []ProxyProtocol `yaml:"ProxyProtocols"` 288 | 289 | ServiceDiscoveryInterval time.Duration `yaml:"ServiceDiscoveryInterval"` 290 | 291 | Balancer string `yaml:"Balancer"` 292 | } 293 | 294 | type Identity struct { 295 | PrivKey string `yaml:"PrivKey"` 296 | 297 | ObservedAddrActivationThresh int `yaml:"ObservedAddrActivationThresh"` 298 | } 299 | 300 | type BandWidthReporter struct { 301 | Enable bool `yaml:"Enable"` 302 | 303 | Interval time.Duration `yaml:"Interval"` 304 | } 305 | 306 | type DHT struct { 307 | Client bool `yaml:"Client"` 308 | } 309 | 310 | type ProxyProtocol struct { 311 | Protocol string `yaml:"Protocol"` 312 | Listen string `yaml:"Listen"` 313 | // Config map[string]interface{} `yaml:"Config"` 314 | } 315 | 316 | type Protocol struct { 317 | Protocol string `yaml:"Protocol"` 318 | Config map[string]interface{} `yaml:"Config"` 319 | } 320 | 321 | type Logging struct { 322 | File string `yaml:"File"` 323 | // json console nocolor, default nocolor 324 | Format string `yaml:"Format"` 325 | Level map[string]string `yaml:"Level"` 326 | } 327 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 3 | github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 4 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 5 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 | github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= 7 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 8 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 9 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 10 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 11 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 12 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 13 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 14 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 15 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 16 | github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= 17 | github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= 18 | github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= 19 | github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= 20 | github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= 21 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 22 | github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 23 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 24 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 25 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 26 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 27 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 28 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 29 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 30 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 31 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 32 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 33 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 34 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 35 | github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 36 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 37 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 38 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 39 | github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= 40 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 41 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 42 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 43 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 44 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 45 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 46 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 47 | github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= 48 | github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= 49 | github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= 50 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 51 | github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 52 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 53 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 54 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 55 | github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8= 56 | github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 57 | github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= 58 | github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 59 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 60 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 61 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 62 | github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= 63 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 64 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 65 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 66 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 67 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 68 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 69 | github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 70 | github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= 71 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 72 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 73 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 74 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= 75 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 76 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 77 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 78 | github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= 79 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 80 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 81 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 82 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 83 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 84 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 85 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 86 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 87 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 88 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 89 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 90 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 91 | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 92 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 93 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 94 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 95 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 96 | github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= 97 | github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= 98 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 99 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 100 | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= 101 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 102 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 103 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 104 | github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= 105 | github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 106 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 107 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 108 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 109 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 110 | github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= 111 | github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= 112 | github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= 113 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 114 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 115 | github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= 116 | github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= 117 | github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= 118 | github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= 119 | github.com/ipfs/go-cid v0.0.5 h1:o0Ix8e/ql7Zb5UVUJEUfjsWCIY8t48++9lR8qi6oiJU= 120 | github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= 121 | github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= 122 | github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= 123 | github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= 124 | github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= 125 | github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8= 126 | github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= 127 | github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= 128 | github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= 129 | github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= 130 | github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= 131 | github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= 132 | github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= 133 | github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= 134 | github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= 135 | github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50= 136 | github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= 137 | github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= 138 | github.com/ipfs/go-log v1.0.2 h1:s19ZwJxH8rPWzypjcDpqPLIyV7BnbLqvpli3iZoqYK0= 139 | github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= 140 | github.com/ipfs/go-log/v2 v2.0.2 h1:xguurydRdfKMJjKyxNXNU8lYP0VZH1NUwJRwUorjuEw= 141 | github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= 142 | github.com/ipfs/go-todocounter v0.0.2 h1:9UBngSQhylg2UDcxSAtpkT+rEWFr26hDPXVStE8LFyc= 143 | github.com/ipfs/go-todocounter v0.0.2/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4= 144 | github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= 145 | github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= 146 | github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA= 147 | github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= 148 | github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= 149 | github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= 150 | github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= 151 | github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2 h1:vhC1OXXiT9R2pczegwz6moDvuRpggaroAXhPIseh57A= 152 | github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= 153 | github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= 154 | github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10= 155 | github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= 156 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 157 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 158 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 159 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 160 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 161 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 162 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 163 | github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= 164 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 165 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 166 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 167 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 168 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 169 | github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= 170 | github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ= 171 | github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= 172 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 173 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 174 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 175 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 176 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 177 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 178 | github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88= 179 | github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= 180 | github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= 181 | github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= 182 | github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= 183 | github.com/libp2p/go-conn-security-multistream v0.1.0 h1:aqGmto+ttL/uJgX0JtQI0tD21CIEy5eYd1Hlp0juHY0= 184 | github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= 185 | github.com/libp2p/go-eventbus v0.1.0 h1:mlawomSAjjkk97QnYiEmHsLu7E136+2oCWSHRUvMfzQ= 186 | github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= 187 | github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= 188 | github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= 189 | github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= 190 | github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= 191 | github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= 192 | github.com/libp2p/go-libp2p v0.4.0/go.mod h1:9EsEIf9p2UDuwtPd0DwJsAl0qXVxgAnuDGRvHbfATfI= 193 | github.com/libp2p/go-libp2p v0.5.0/go.mod h1:Os7a5Z3B+ErF4v7zgIJ7nBHNu2LYt8ZMLkTQUB3G/wA= 194 | github.com/libp2p/go-libp2p v0.5.2 h1:fjQUTyB7x/4XgO31OEWkJ5uFeHRgpoExlf0rXz5BO8k= 195 | github.com/libp2p/go-libp2p v0.5.2/go.mod h1:o2r6AcpNl1eNGoiWhRtPji03NYOvZumeQ6u+X6gSxnM= 196 | github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= 197 | github.com/libp2p/go-libp2p-autonat v0.1.1 h1:WLBZcIRsjZlWdAZj9CiBSvU2wQXoUOiS1Zk1tM7DTJI= 198 | github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= 199 | github.com/libp2p/go-libp2p-autonat-svc v0.1.0 h1:28IM7iWMDclZeVkpiFQaWVANwXwE7zLlpbnS7yXxrfs= 200 | github.com/libp2p/go-libp2p-autonat-svc v0.1.0/go.mod h1:fqi8Obl/z3R4PFVLm8xFtZ6PBL9MlV/xumymRFkKq5A= 201 | github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= 202 | github.com/libp2p/go-libp2p-blankhost v0.1.4 h1:I96SWjR4rK9irDHcHq3XHN6hawCRTPUADzkJacgZLvk= 203 | github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= 204 | github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= 205 | github.com/libp2p/go-libp2p-circuit v0.1.3/go.mod h1:Xqh2TjSy8DD5iV2cCOMzdynd6h8OTBGoV1AWbWor3qM= 206 | github.com/libp2p/go-libp2p-circuit v0.1.4 h1:Phzbmrg3BkVzbqd4ZZ149JxCuUWu2wZcXf/Kr6hZJj8= 207 | github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= 208 | github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= 209 | github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= 210 | github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= 211 | github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= 212 | github.com/libp2p/go-libp2p-core v0.2.3/go.mod h1:GqhyQqyIAPsxFYXHMjfXgMv03lxsvM0mFzuYA9Ib42A= 213 | github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= 214 | github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= 215 | github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= 216 | github.com/libp2p/go-libp2p-core v0.3.1 h1:hEnSDjScfjYvPHoTgZhC4F62M8K1x1Oco/BY0RZ1N3s= 217 | github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= 218 | github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= 219 | github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= 220 | github.com/libp2p/go-libp2p-discovery v0.2.0 h1:1p3YSOq7VsgaL+xVHPi8XAmtGyas6D2J6rWBEfz/aiY= 221 | github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= 222 | github.com/libp2p/go-libp2p-gostream v0.2.0 h1:YGLebbo8KfylSkuralCCyas/hVrgWjc+cfnLMCZWvEs= 223 | github.com/libp2p/go-libp2p-gostream v0.2.0/go.mod h1:nN/Aw00orrADXaXgNCeYjCtQrk6eT20PX/G8F12NW/s= 224 | github.com/libp2p/go-libp2p-kad-dht v0.5.0 h1:kDMtCftpQOL2s84/dZmw5z4NmBe6ByeDLKpcn6TcyxU= 225 | github.com/libp2p/go-libp2p-kad-dht v0.5.0/go.mod h1:42YDfiKXzIgaIexiEQ3rKZbVPVPziLOyHpXbOCVd814= 226 | github.com/libp2p/go-libp2p-kbucket v0.2.3 h1:XtNfN4WUy0cfeJoJgWCf1lor4Pp3kBkFJ9vQ+Zs+VUM= 227 | github.com/libp2p/go-libp2p-kbucket v0.2.3/go.mod h1:opWrBZSWnBYPc315q497huxY3sz1t488X6OiXUEYWKA= 228 | github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= 229 | github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= 230 | github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= 231 | github.com/libp2p/go-libp2p-mplex v0.2.1 h1:E1xaJBQnbSiTHGI1gaBKmKhu1TUKkErKJnE8iGvirYI= 232 | github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= 233 | github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= 234 | github.com/libp2p/go-libp2p-nat v0.0.5 h1:/mH8pXFVKleflDL1YwqMg27W9GD8kjEx7NY0P6eGc98= 235 | github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= 236 | github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= 237 | github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= 238 | github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= 239 | github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= 240 | github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= 241 | github.com/libp2p/go-libp2p-peerstore v0.1.4 h1:d23fvq5oYMJ/lkkbO4oTwBp/JP+I/1m5gZJobNXCE/k= 242 | github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= 243 | github.com/libp2p/go-libp2p-record v0.1.2 h1:M50VKzWnmUrk/M5/Dz99qO9Xh4vs8ijsK+7HkJvRP+0= 244 | github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= 245 | github.com/libp2p/go-libp2p-routing v0.1.0 h1:hFnj3WR3E2tOcKaGpyzfP4gvFZ3t8JkQmbapN0Ct+oU= 246 | github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE= 247 | github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= 248 | github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= 249 | github.com/libp2p/go-libp2p-secio v0.2.1 h1:eNWbJTdyPA7NxhP7J3c5lT97DC5d+u+IldkgCYFTPVA= 250 | github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= 251 | github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= 252 | github.com/libp2p/go-libp2p-swarm v0.2.2 h1:T4hUpgEs2r371PweU3DuH7EOmBIdTBCwWs+FLcgx3bQ= 253 | github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= 254 | github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= 255 | github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= 256 | github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= 257 | github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= 258 | github.com/libp2p/go-libp2p-testing v0.1.1 h1:U03z3HnGI7Ni8Xx6ONVZvUFOAzWYmolWf5W5jAOPNmU= 259 | github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= 260 | github.com/libp2p/go-libp2p-transport-upgrader v0.1.1 h1:PZMS9lhjK9VytzMCW3tWHAXtKXmlURSc3ZdvwEcKCzw= 261 | github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= 262 | github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= 263 | github.com/libp2p/go-libp2p-yamux v0.2.1 h1:Q3XYNiKCC2vIxrvUJL+Jg1kiyeEaIDNKLjgEjo3VQdI= 264 | github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= 265 | github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= 266 | github.com/libp2p/go-maddr-filter v0.0.5 h1:CW3AgbMO6vUvT4kf87y4N+0P8KUl2aqLYhrGyDUbLSg= 267 | github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= 268 | github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= 269 | github.com/libp2p/go-mplex v0.1.0 h1:/nBTy5+1yRyY82YaO6HXQRnO5IAGsXTjEJaR3LdTPc0= 270 | github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= 271 | github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= 272 | github.com/libp2p/go-msgio v0.0.4 h1:agEFehY3zWJFUHK6SEMR7UYmk2z6kC3oeCM7ybLhguA= 273 | github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= 274 | github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= 275 | github.com/libp2p/go-nat v0.0.4 h1:KbizNnq8YIf7+Hn7+VFL/xE0eDrkPru2zIO9NMwL8UQ= 276 | github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= 277 | github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= 278 | github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= 279 | github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg= 280 | github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= 281 | github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= 282 | github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= 283 | github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4= 284 | github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= 285 | github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= 286 | github.com/libp2p/go-stream-muxer-multistream v0.2.0 h1:714bRJ4Zy9mdhyTLJ+ZKiROmAFwUHpeRidG+q7LTQOg= 287 | github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= 288 | github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= 289 | github.com/libp2p/go-tcp-transport v0.1.1 h1:yGlqURmqgNA2fvzjSgZNlHcsd/IulAnKM8Ncu+vlqnw= 290 | github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= 291 | github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= 292 | github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y= 293 | github.com/libp2p/go-ws-transport v0.2.0 h1:MJCw2OrPA9+76YNRvdo1wMnSOxb9Bivj6sVFY1Xrj6w= 294 | github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= 295 | github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= 296 | github.com/libp2p/go-yamux v1.2.3 h1:xX8A36vpXb59frIzWFdEgptLMsOANMFq2K7fPRlunYI= 297 | github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= 298 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 299 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 300 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 301 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 302 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 303 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 304 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 305 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 306 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 307 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 308 | github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 309 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= 310 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= 311 | github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= 312 | github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= 313 | github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= 314 | github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= 315 | github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= 316 | github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= 317 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 318 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 319 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 320 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 321 | github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= 322 | github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= 323 | github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 324 | github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= 325 | github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 326 | github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= 327 | github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= 328 | github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 329 | github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 330 | github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 331 | github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 332 | github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= 333 | github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90= 334 | github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= 335 | github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= 336 | github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= 337 | github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY= 338 | github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= 339 | github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= 340 | github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= 341 | github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= 342 | github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= 343 | github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= 344 | github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= 345 | github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= 346 | github.com/multiformats/go-multiaddr-net v0.1.2 h1:P7zcBH9FRETdPkDrylcXVjQLQ2t1JQtNItZULWNWgeg= 347 | github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= 348 | github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA= 349 | github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= 350 | github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= 351 | github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= 352 | github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 353 | github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 354 | github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 355 | github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc= 356 | github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= 357 | github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= 358 | github.com/multiformats/go-multistream v0.1.1 h1:JlAdpIFhBhGRLxe9W6Om0w++Gd6KMWoFPZL/dEnm9nI= 359 | github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= 360 | github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 361 | github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= 362 | github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 363 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 364 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 365 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 366 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 367 | github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 368 | github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= 369 | github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 370 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 371 | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 372 | github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= 373 | github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= 374 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 375 | github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= 376 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 377 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 378 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 379 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 380 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 381 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 382 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 383 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 384 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 385 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 386 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 387 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 388 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 389 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 390 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 391 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 392 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 393 | github.com/riobard/go-bloom v0.0.0-20200213042214-218e1707c495 h1:p7xbxYTzzfXghR1kpsJDeoVVRRWAotKc8u7FP/N48rU= 394 | github.com/riobard/go-bloom v0.0.0-20200213042214-218e1707c495/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= 395 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 396 | github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= 397 | github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= 398 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 399 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 400 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 401 | github.com/shadowsocks/go-shadowsocks2 v0.1.0 h1:3TeNJHk6rnCn0W6MH3F3l+UEoe2xVevMCoEIPwd3yWk= 402 | github.com/shadowsocks/go-shadowsocks2 v0.1.0/go.mod h1:SiGEqep4n+bS5wCZwi6ZgX1MAGRV9erk0zsVOXZ7QvA= 403 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 404 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 405 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 406 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 407 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 408 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 409 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 410 | github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= 411 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 412 | github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= 413 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= 414 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= 415 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 416 | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 417 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 418 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 419 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 420 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 421 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 422 | github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= 423 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 424 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 425 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 426 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 427 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 428 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 429 | github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= 430 | github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= 431 | github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= 432 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 433 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 434 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 435 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 436 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 437 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 438 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 439 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 440 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 441 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 442 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 443 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 444 | github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= 445 | github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= 446 | github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= 447 | github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= 448 | github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= 449 | github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= 450 | github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= 451 | github.com/whyrusleeping/mafmt v1.2.8 h1:TCghSl5kkwEE0j+sU/gudyhVMRlpBin8fMBBHg59EbA= 452 | github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= 453 | github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= 454 | github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= 455 | github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= 456 | github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= 457 | github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= 458 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 459 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 460 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 461 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 462 | go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= 463 | go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= 464 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 465 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= 466 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 467 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= 468 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 469 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= 470 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 471 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 472 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 473 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 474 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 475 | golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 476 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 477 | golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 478 | golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 479 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 480 | golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 481 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 482 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 483 | golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= 484 | golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 485 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 486 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 487 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 488 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 489 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 490 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 491 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 492 | golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 493 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 494 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 495 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 496 | golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 497 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 498 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 499 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 500 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= 501 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 502 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 503 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 504 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 505 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 506 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 507 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 508 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 509 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 510 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 511 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 512 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 513 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 514 | golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 515 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 516 | golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 517 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 518 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 519 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 520 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM= 521 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 522 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 523 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 524 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 525 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 526 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 527 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 528 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 529 | golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 530 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 531 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 532 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 533 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 534 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 535 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= 536 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 537 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 538 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 539 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 540 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 541 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 542 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 543 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 544 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 545 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 546 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 547 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 548 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 549 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 550 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 551 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 552 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 553 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 554 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 555 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 556 | gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= 557 | gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= 558 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 559 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 560 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 561 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 562 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 563 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 564 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 565 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 566 | --------------------------------------------------------------------------------