소스 검색

Make network as a separae interface

tags/v2.0.0-rc1
9seconds 5 년 전
부모
커밋
24add0dce4
8개의 변경된 파일89개의 추가작업 그리고 62개의 파일을 삭제
  1. 1
    1
      cli.go
  2. 4
    6
      cli_access.go
  3. 1
    1
      cli_generate_secret.go
  4. 2
    2
      config.go
  5. 9
    3
      example.config.toml
  6. 13
    2
      mtglib/network/init.go
  7. 54
    42
      mtglib/network/network.go
  8. 5
    5
      utils.go

+ 1
- 1
cli.go 파일 보기

8
 )
8
 )
9
 
9
 
10
 type cli struct {
10
 type cli struct {
11
+	network network.Network
11
 	conf    *config
12
 	conf    *config
12
-	network *network.Network
13
 }
13
 }
14
 
14
 
15
 func (c *cli) ReadConfig(path string) error {
15
 func (c *cli) ReadConfig(path string) error {

+ 4
- 6
cli_access.go 파일 보기

73
 }
73
 }
74
 
74
 
75
 func (c *cliCommandAccess) getIP(protocol string) net.IP {
75
 func (c *cliCommandAccess) getIP(protocol string) net.IP {
76
-	client := &http.Client{
77
-		Timeout: c.network.HTTP.Timeout,
78
-		Transport: &http.Transport{
79
-			DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
80
-				return c.network.DialContext(ctx, protocol, address)
81
-			},
76
+	client := c.network.MakeHTTPClient(0)
77
+	client.Transport = &http.Transport{
78
+		DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
79
+			return c.network.DialContext(ctx, protocol, address)
82
 		},
80
 		},
83
 	}
81
 	}
84
 
82
 

+ 1
- 1
cli_generate_secret.go 파일 보기

7
 )
7
 )
8
 
8
 
9
 type cliCommandGenerateSecret struct {
9
 type cliCommandGenerateSecret struct {
10
-    cli
10
+	cli
11
 
11
 
12
 	HostName string `arg optional help:"Hostname to use for domain fronting. Default is '${domain_front}'." name:"hostname" default:"${domain_front}"` // nolint: lll, govet
12
 	HostName string `arg optional help:"Hostname to use for domain fronting. Default is '${domain_front}'." name:"hostname" default:"${domain_front}"` // nolint: lll, govet
13
 	Hex      bool   `help:"Print secret in hex encoding."`
13
 	Hex      bool   `help:"Print secret in hex encoding."`

+ 2
- 2
config.go 파일 보기

412
 		} `json:"public-ip"`
412
 		} `json:"public-ip"`
413
 		Timeout struct {
413
 		Timeout struct {
414
 			TCP  configTypeDuration `json:"tcp"`
414
 			TCP  configTypeDuration `json:"tcp"`
415
-			HTTP configTypeDuration `json:"http"`
415
+			Idle configTypeDuration `json:"idle"`
416
 		} `json:"timeout"`
416
 		} `json:"timeout"`
417
 		DOHIP   configTypeIP    `json:"doh-ip"`
417
 		DOHIP   configTypeIP    `json:"doh-ip"`
418
 		Proxies []configTypeURL `json:"proxies"`
418
 		Proxies []configTypeURL `json:"proxies"`
478
 		} `toml:"public-ip" json:"public-ip"`
478
 		} `toml:"public-ip" json:"public-ip"`
479
 		Timeout struct {
479
 		Timeout struct {
480
 			TCP  string `toml:"tcp" json:"tcp"`
480
 			TCP  string `toml:"tcp" json:"tcp"`
481
-			HTTP string `toml:"http" json:"http"`
481
+			Idle string `toml:"idle" json:"idle"`
482
 		} `toml:"timeout" json:"timeout"`
482
 		} `toml:"timeout" json:"timeout"`
483
 		DOHIP   string   `toml:"doh-ip" json:"doh-ip"`
483
 		DOHIP   string   `toml:"doh-ip" json:"doh-ip"`
484
 		Proxies []string `toml:"proxies" json:"proxies"`
484
 		Proxies []string `toml:"proxies" json:"proxies"`

+ 9
- 3
example.config.toml 파일 보기

101
 ipv4 = ""
101
 ipv4 = ""
102
 ipv6 = ""
102
 ipv6 = ""
103
 
103
 
104
-# network timeouts define different settings for timeouts. HTTP timeout
105
-# is required only for DOH.
104
+# network timeouts define different settings for timeouts. tcp timeout
105
+# define a global timeout on establishing of network connections. idle
106
+# means a timeout on pumping data between sockset when nothing is
107
+# happening.
108
+#
109
+# please be noticed that handshakes have no timeouts intentionally. You can
110
+# find a reasoning here:
111
+# https://www.ndss-symposium.org/wp-content/uploads/2020/02/23087-paper.pdf
106
 [network.timeout]
112
 [network.timeout]
107
 tcp = "5s"
113
 tcp = "5s"
108
-http = "10s"
114
+idle = "1m"
109
 
115
 
110
 # FakeTLS can compare timestamps to prevent probes. Each message has
116
 # FakeTLS can compare timestamps to prevent probes. Each message has
111
 # encrypted timestamp. So, mtg can compare this timestamp and decide if
117
 # encrypted timestamp. So, mtg can compare this timestamp and decide if

+ 13
- 2
mtglib/network/init.go 파일 보기

4
 	"context"
4
 	"context"
5
 	"errors"
5
 	"errors"
6
 	"net"
6
 	"net"
7
+	"net/http"
7
 	"time"
8
 	"time"
8
 )
9
 )
9
 
10
 
10
 const (
11
 const (
11
 	DefaultTimeout     = 10 * time.Second
12
 	DefaultTimeout     = 10 * time.Second
12
-	DefaultDNSTimeout  = time.Second
13
-	DefaultHTTPTimeout = DefaultTimeout
13
+	DefaultIdleTimeout = time.Minute
14
+	DefaultHTTPTimeout = 5 * time.Second
14
 	DefaultBufferSize  = 4096
15
 	DefaultBufferSize  = 4096
15
 
16
 
16
 	ProxyDialerOpenThreshold        = 5
17
 	ProxyDialerOpenThreshold        = 5
18
 	ProxyDialerResetFailuresTimeout = 10 * time.Second
19
 	ProxyDialerResetFailuresTimeout = 10 * time.Second
19
 
20
 
20
 	DefaultDOHHostname = "9.9.9.9"
21
 	DefaultDOHHostname = "9.9.9.9"
22
+
23
+	DNSTimeout = 5 * time.Second
21
 )
24
 )
22
 
25
 
23
 var (
26
 var (
29
 	Dial(network, address string) (net.Conn, error)
32
 	Dial(network, address string) (net.Conn, error)
30
 	DialContext(ctx context.Context, network, address string) (net.Conn, error)
33
 	DialContext(ctx context.Context, network, address string) (net.Conn, error)
31
 }
34
 }
35
+
36
+type Network interface {
37
+	Dialer
38
+
39
+	DNSResolve(network, hostname string) (ips []string, err error)
40
+	MakeHTTPClient(timeout time.Duration) *http.Client
41
+	IdleTimeout() time.Duration
42
+}

+ 54
- 42
mtglib/network/network.go 파일 보기

12
 	doh "github.com/babolivier/go-doh-client"
12
 	doh "github.com/babolivier/go-doh-client"
13
 )
13
 )
14
 
14
 
15
-type Network struct {
16
-	HTTP http.Client
17
-	DNS  doh.Resolver
18
-
19
-	dialer Dialer
15
+type network struct {
16
+	idleTimeout time.Duration
17
+	dialer      Dialer
18
+	dns         doh.Resolver
20
 }
19
 }
21
 
20
 
22
-func (d *Network) Dial(network, address string) (net.Conn, error) {
23
-	return d.DialContext(context.Background(), network, address)
21
+func (n *network) Dial(protocol, address string) (net.Conn, error) {
22
+	return n.DialContext(context.Background(), protocol, address)
24
 }
23
 }
25
 
24
 
26
-func (d *Network) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
25
+func (n *network) DialContext(ctx context.Context, protocol, address string) (net.Conn, error) {
27
 	host, port, _ := net.SplitHostPort(address)
26
 	host, port, _ := net.SplitHostPort(address)
28
 
27
 
29
-	ips, err := d.resolveIPs(network, host)
28
+	ips, err := n.DNSResolve(protocol, host)
30
 	if err != nil {
29
 	if err != nil {
31
 		return nil, fmt.Errorf("cannot resolve dns names: %w", err)
30
 		return nil, fmt.Errorf("cannot resolve dns names: %w", err)
32
 	}
31
 	}
37
 		})
36
 		})
38
 	}
37
 	}
39
 
38
 
39
+	var conn net.Conn
40
+
40
 	for _, v := range ips {
41
 	for _, v := range ips {
41
-		if conn, err := d.dialer.DialContext(ctx, network, net.JoinHostPort(v, port)); err == nil {
42
+		conn, err = n.dialer.DialContext(ctx, protocol, net.JoinHostPort(v, port))
43
+
44
+		if err == nil {
42
 			return conn, nil
45
 			return conn, nil
43
 		}
46
 		}
44
 	}
47
 	}
45
 
48
 
46
-	return nil, fmt.Errorf("cannot dial to %s:%s", network, address)
49
+	return nil, fmt.Errorf("cannot dial to %s:%s: %w", protocol, address, err)
47
 }
50
 }
48
 
51
 
49
-func (d *Network) resolveIPs(network, address string) ([]string, error) {
52
+func (n *network) DNSResolve(protocol, address string) ([]string, error) {
50
 	if net.ParseIP(address) != nil {
53
 	if net.ParseIP(address) != nil {
51
 		return []string{address}, nil
54
 		return []string{address}, nil
52
 	}
55
 	}
55
 	wg := &sync.WaitGroup{}
58
 	wg := &sync.WaitGroup{}
56
 	mutex := &sync.Mutex{}
59
 	mutex := &sync.Mutex{}
57
 
60
 
58
-	switch network {
61
+	switch protocol {
59
 	case "tcp", "tcp4":
62
 	case "tcp", "tcp4":
60
 		wg.Add(1)
63
 		wg.Add(1)
61
 
64
 
62
 		go func() {
65
 		go func() {
63
 			defer wg.Done()
66
 			defer wg.Done()
64
 
67
 
65
-			if recs, _, err := d.DNS.LookupA(address); err == nil {
68
+			if recs, _, err := n.dns.LookupA(address); err == nil {
66
 				mutex.Lock()
69
 				mutex.Lock()
67
 				defer mutex.Unlock()
70
 				defer mutex.Unlock()
68
 
71
 
73
 		}()
76
 		}()
74
 	}
77
 	}
75
 
78
 
76
-	switch network {
79
+	switch protocol {
77
 	case "tcp", "tcp6":
80
 	case "tcp", "tcp6":
78
 		wg.Add(1)
81
 		wg.Add(1)
79
 
82
 
80
 		go func() {
83
 		go func() {
81
 			defer wg.Done()
84
 			defer wg.Done()
82
 
85
 
83
-			if recs, _, err := d.DNS.LookupAAAA(address); err == nil {
86
+			if recs, _, err := n.dns.LookupAAAA(address); err == nil {
84
 				mutex.Lock()
87
 				mutex.Lock()
85
 				defer mutex.Unlock()
88
 				defer mutex.Unlock()
86
 
89
 
94
 	wg.Wait()
97
 	wg.Wait()
95
 
98
 
96
 	if len(ips) == 0 {
99
 	if len(ips) == 0 {
97
-		return nil, fmt.Errorf("cannot find any ips for %s:%s", network, address)
100
+		return nil, fmt.Errorf("cannot find any ips for %s:%s", protocol, address)
98
 	}
101
 	}
99
 
102
 
100
 	return ips, nil
103
 	return ips, nil
101
 }
104
 }
102
 
105
 
103
-func NewNetwork(dialer Dialer, dohHostname string, httpTimeout time.Duration) (*Network, error) {
104
-	switch {
105
-	case httpTimeout < 0:
106
-		return nil, fmt.Errorf("timeout should be positive number %v", httpTimeout)
107
-	case httpTimeout == 0:
108
-		httpTimeout = DefaultHTTPTimeout
109
-	}
106
+func (n *network) IdleTimeout() time.Duration {
107
+	return n.idleTimeout
108
+}
110
 
109
 
111
-	if net.ParseIP(dohHostname) == nil {
112
-		return nil, fmt.Errorf("hostname %s should be IP address", dohHostname)
110
+func (n *network) MakeHTTPClient(timeout time.Duration) *http.Client {
111
+	if timeout <= 0 {
112
+		timeout = DefaultHTTPTimeout
113
 	}
113
 	}
114
 
114
 
115
-	dohHTTPClient := &http.Client{
116
-		Timeout: DefaultDNSTimeout,
115
+	return &http.Client{
116
+		Timeout: timeout,
117
 		Transport: &http.Transport{
117
 		Transport: &http.Transport{
118
-			DialContext: dialer.DialContext,
118
+			DialContext: n.DialContext,
119
 		},
119
 		},
120
 	}
120
 	}
121
-	network := &Network{
122
-		dialer: dialer,
123
-		DNS: doh.Resolver{
124
-			Host:       dohHostname,
125
-			Class:      doh.IN,
126
-			HTTPClient: dohHTTPClient,
127
-		},
121
+}
122
+
123
+func NewNetwork(dialer Dialer, dohHostname string, idleTimeout time.Duration) (Network, error) {
124
+	switch {
125
+	case idleTimeout < 0:
126
+		return nil, fmt.Errorf("timeout should be positive number %s", idleTimeout)
127
+	case idleTimeout == 0:
128
+		idleTimeout = DefaultIdleTimeout
128
 	}
129
 	}
129
-	network.HTTP = http.Client{
130
-		Timeout: httpTimeout,
131
-		Transport: &http.Transport{
132
-			DialContext: network.DialContext,
133
-		},
130
+
131
+	if net.ParseIP(dohHostname) == nil {
132
+		return nil, fmt.Errorf("hostname %s should be IP address", dohHostname)
134
 	}
133
 	}
135
 
134
 
136
-	return network, nil
135
+	return &network{
136
+		dialer:      dialer,
137
+		idleTimeout: idleTimeout,
138
+		dns: doh.Resolver{
139
+			Host:  dohHostname,
140
+			Class: doh.IN,
141
+			HTTPClient: &http.Client{
142
+				Timeout: DNSTimeout,
143
+				Transport: &http.Transport{
144
+					DialContext: dialer.DialContext,
145
+				},
146
+			},
147
+		},
148
+	}, nil
137
 }
149
 }

+ 5
- 5
utils.go 파일 보기

11
 	"github.com/9seconds/mtg/v2/mtglib/network"
11
 	"github.com/9seconds/mtg/v2/mtglib/network"
12
 )
12
 )
13
 
13
 
14
-func makeNetwork(conf *config) (*network.Network, error) {
14
+func makeNetwork(conf *config) (network.Network, error) {
15
 	tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout)
15
 	tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout)
16
-	httpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultHTTPTimeout)
16
+	idleTimeout := conf.Network.Timeout.Idle.Value(network.DefaultIdleTimeout)
17
 	dohIP := conf.Network.DOHIP.Value(net.ParseIP(network.DefaultDOHHostname)).String()
17
 	dohIP := conf.Network.DOHIP.Value(net.ParseIP(network.DefaultDOHHostname)).String()
18
 	bufferSize := conf.TCPBuffer.Value(network.DefaultBufferSize)
18
 	bufferSize := conf.TCPBuffer.Value(network.DefaultBufferSize)
19
 
19
 
32
 
32
 
33
 	switch len(proxyURLs) {
33
 	switch len(proxyURLs) {
34
 	case 0:
34
 	case 0:
35
-		return network.NewNetwork(baseDialer, dohIP, httpTimeout)
35
+		return network.NewNetwork(baseDialer, dohIP, idleTimeout)
36
 	case 1:
36
 	case 1:
37
 		socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0])
37
 		socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0])
38
 		if err != nil {
38
 		if err != nil {
39
 			return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
39
 			return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
40
 		}
40
 		}
41
 
41
 
42
-		return network.NewNetwork(socksDialer, dohIP, httpTimeout)
42
+		return network.NewNetwork(socksDialer, dohIP, idleTimeout)
43
 	}
43
 	}
44
 
44
 
45
 	socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs)
45
 	socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs)
47
 		return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
47
 		return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
48
 	}
48
 	}
49
 
49
 
50
-	return network.NewNetwork(socksDialer, dohIP, httpTimeout)
50
+	return network.NewNetwork(socksDialer, dohIP, idleTimeout)
51
 }
51
 }
52
 
52
 
53
 func exhaustResponse(response *http.Response) {
53
 func exhaustResponse(response *http.Response) {

Loading…
취소
저장