mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-02 00:11:56 +03:00
Make network as a separae interface
This commit is contained in:
@@ -8,8 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type cli struct {
|
type cli struct {
|
||||||
|
network network.Network
|
||||||
conf *config
|
conf *config
|
||||||
network *network.Network
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cli) ReadConfig(path string) error {
|
func (c *cli) ReadConfig(path string) error {
|
||||||
|
|||||||
+2
-4
@@ -73,13 +73,11 @@ func (c *cliCommandAccess) Run(cli *CLI) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *cliCommandAccess) getIP(protocol string) net.IP {
|
func (c *cliCommandAccess) getIP(protocol string) net.IP {
|
||||||
client := &http.Client{
|
client := c.network.MakeHTTPClient(0)
|
||||||
Timeout: c.network.HTTP.Timeout,
|
client.Transport = &http.Transport{
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
return c.network.DialContext(ctx, protocol, address)
|
return c.network.DialContext(ctx, protocol, address)
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Get("https://ifconfig.co") // nolint: bodyclose, noctx
|
resp, err := client.Get("https://ifconfig.co") // nolint: bodyclose, noctx
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ type config struct {
|
|||||||
} `json:"public-ip"`
|
} `json:"public-ip"`
|
||||||
Timeout struct {
|
Timeout struct {
|
||||||
TCP configTypeDuration `json:"tcp"`
|
TCP configTypeDuration `json:"tcp"`
|
||||||
HTTP configTypeDuration `json:"http"`
|
Idle configTypeDuration `json:"idle"`
|
||||||
} `json:"timeout"`
|
} `json:"timeout"`
|
||||||
DOHIP configTypeIP `json:"doh-ip"`
|
DOHIP configTypeIP `json:"doh-ip"`
|
||||||
Proxies []configTypeURL `json:"proxies"`
|
Proxies []configTypeURL `json:"proxies"`
|
||||||
@@ -478,7 +478,7 @@ type configRaw struct {
|
|||||||
} `toml:"public-ip" json:"public-ip"`
|
} `toml:"public-ip" json:"public-ip"`
|
||||||
Timeout struct {
|
Timeout struct {
|
||||||
TCP string `toml:"tcp" json:"tcp"`
|
TCP string `toml:"tcp" json:"tcp"`
|
||||||
HTTP string `toml:"http" json:"http"`
|
Idle string `toml:"idle" json:"idle"`
|
||||||
} `toml:"timeout" json:"timeout"`
|
} `toml:"timeout" json:"timeout"`
|
||||||
DOHIP string `toml:"doh-ip" json:"doh-ip"`
|
DOHIP string `toml:"doh-ip" json:"doh-ip"`
|
||||||
Proxies []string `toml:"proxies" json:"proxies"`
|
Proxies []string `toml:"proxies" json:"proxies"`
|
||||||
|
|||||||
+9
-3
@@ -101,11 +101,17 @@ proxies = [
|
|||||||
ipv4 = ""
|
ipv4 = ""
|
||||||
ipv6 = ""
|
ipv6 = ""
|
||||||
|
|
||||||
# network timeouts define different settings for timeouts. HTTP timeout
|
# network timeouts define different settings for timeouts. tcp timeout
|
||||||
# is required only for DOH.
|
# define a global timeout on establishing of network connections. idle
|
||||||
|
# means a timeout on pumping data between sockset when nothing is
|
||||||
|
# happening.
|
||||||
|
#
|
||||||
|
# please be noticed that handshakes have no timeouts intentionally. You can
|
||||||
|
# find a reasoning here:
|
||||||
|
# https://www.ndss-symposium.org/wp-content/uploads/2020/02/23087-paper.pdf
|
||||||
[network.timeout]
|
[network.timeout]
|
||||||
tcp = "5s"
|
tcp = "5s"
|
||||||
http = "10s"
|
idle = "1m"
|
||||||
|
|
||||||
# FakeTLS can compare timestamps to prevent probes. Each message has
|
# FakeTLS can compare timestamps to prevent probes. Each message has
|
||||||
# encrypted timestamp. So, mtg can compare this timestamp and decide if
|
# encrypted timestamp. So, mtg can compare this timestamp and decide if
|
||||||
|
|||||||
+13
-2
@@ -4,13 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultTimeout = 10 * time.Second
|
DefaultTimeout = 10 * time.Second
|
||||||
DefaultDNSTimeout = time.Second
|
DefaultIdleTimeout = time.Minute
|
||||||
DefaultHTTPTimeout = DefaultTimeout
|
DefaultHTTPTimeout = 5 * time.Second
|
||||||
DefaultBufferSize = 4096
|
DefaultBufferSize = 4096
|
||||||
|
|
||||||
ProxyDialerOpenThreshold = 5
|
ProxyDialerOpenThreshold = 5
|
||||||
@@ -18,6 +19,8 @@ const (
|
|||||||
ProxyDialerResetFailuresTimeout = 10 * time.Second
|
ProxyDialerResetFailuresTimeout = 10 * time.Second
|
||||||
|
|
||||||
DefaultDOHHostname = "9.9.9.9"
|
DefaultDOHHostname = "9.9.9.9"
|
||||||
|
|
||||||
|
DNSTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -29,3 +32,11 @@ type Dialer interface {
|
|||||||
Dial(network, address string) (net.Conn, error)
|
Dial(network, address string) (net.Conn, error)
|
||||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Network interface {
|
||||||
|
Dialer
|
||||||
|
|
||||||
|
DNSResolve(network, hostname string) (ips []string, err error)
|
||||||
|
MakeHTTPClient(timeout time.Duration) *http.Client
|
||||||
|
IdleTimeout() time.Duration
|
||||||
|
}
|
||||||
|
|||||||
+50
-38
@@ -12,21 +12,20 @@ import (
|
|||||||
doh "github.com/babolivier/go-doh-client"
|
doh "github.com/babolivier/go-doh-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Network struct {
|
type network struct {
|
||||||
HTTP http.Client
|
idleTimeout time.Duration
|
||||||
DNS doh.Resolver
|
|
||||||
|
|
||||||
dialer Dialer
|
dialer Dialer
|
||||||
|
dns doh.Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Network) Dial(network, address string) (net.Conn, error) {
|
func (n *network) Dial(protocol, address string) (net.Conn, error) {
|
||||||
return d.DialContext(context.Background(), network, address)
|
return n.DialContext(context.Background(), protocol, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Network) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func (n *network) DialContext(ctx context.Context, protocol, address string) (net.Conn, error) {
|
||||||
host, port, _ := net.SplitHostPort(address)
|
host, port, _ := net.SplitHostPort(address)
|
||||||
|
|
||||||
ips, err := d.resolveIPs(network, host)
|
ips, err := n.DNSResolve(protocol, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot resolve dns names: %w", err)
|
return nil, fmt.Errorf("cannot resolve dns names: %w", err)
|
||||||
}
|
}
|
||||||
@@ -37,16 +36,20 @@ func (d *Network) DialContext(ctx context.Context, network, address string) (net
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var conn net.Conn
|
||||||
|
|
||||||
for _, v := range ips {
|
for _, v := range ips {
|
||||||
if conn, err := d.dialer.DialContext(ctx, network, net.JoinHostPort(v, port)); err == nil {
|
conn, err = n.dialer.DialContext(ctx, protocol, net.JoinHostPort(v, port))
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("cannot dial to %s:%s", network, address)
|
return nil, fmt.Errorf("cannot dial to %s:%s: %w", protocol, address, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Network) resolveIPs(network, address string) ([]string, error) {
|
func (n *network) DNSResolve(protocol, address string) ([]string, error) {
|
||||||
if net.ParseIP(address) != nil {
|
if net.ParseIP(address) != nil {
|
||||||
return []string{address}, nil
|
return []string{address}, nil
|
||||||
}
|
}
|
||||||
@@ -55,14 +58,14 @@ func (d *Network) resolveIPs(network, address string) ([]string, error) {
|
|||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
mutex := &sync.Mutex{}
|
mutex := &sync.Mutex{}
|
||||||
|
|
||||||
switch network {
|
switch protocol {
|
||||||
case "tcp", "tcp4":
|
case "tcp", "tcp4":
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
if recs, _, err := d.DNS.LookupA(address); err == nil {
|
if recs, _, err := n.dns.LookupA(address); err == nil {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
||||||
@@ -73,14 +76,14 @@ func (d *Network) resolveIPs(network, address string) ([]string, error) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch network {
|
switch protocol {
|
||||||
case "tcp", "tcp6":
|
case "tcp", "tcp6":
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
if recs, _, err := d.DNS.LookupAAAA(address); err == nil {
|
if recs, _, err := n.dns.LookupAAAA(address); err == nil {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
||||||
@@ -94,44 +97,53 @@ func (d *Network) resolveIPs(network, address string) ([]string, error) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
if len(ips) == 0 {
|
if len(ips) == 0 {
|
||||||
return nil, fmt.Errorf("cannot find any ips for %s:%s", network, address)
|
return nil, fmt.Errorf("cannot find any ips for %s:%s", protocol, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNetwork(dialer Dialer, dohHostname string, httpTimeout time.Duration) (*Network, error) {
|
func (n *network) IdleTimeout() time.Duration {
|
||||||
|
return n.idleTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *network) MakeHTTPClient(timeout time.Duration) *http.Client {
|
||||||
|
if timeout <= 0 {
|
||||||
|
timeout = DefaultHTTPTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Client{
|
||||||
|
Timeout: timeout,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: n.DialContext,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNetwork(dialer Dialer, dohHostname string, idleTimeout time.Duration) (Network, error) {
|
||||||
switch {
|
switch {
|
||||||
case httpTimeout < 0:
|
case idleTimeout < 0:
|
||||||
return nil, fmt.Errorf("timeout should be positive number %v", httpTimeout)
|
return nil, fmt.Errorf("timeout should be positive number %s", idleTimeout)
|
||||||
case httpTimeout == 0:
|
case idleTimeout == 0:
|
||||||
httpTimeout = DefaultHTTPTimeout
|
idleTimeout = DefaultIdleTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
if net.ParseIP(dohHostname) == nil {
|
if net.ParseIP(dohHostname) == nil {
|
||||||
return nil, fmt.Errorf("hostname %s should be IP address", dohHostname)
|
return nil, fmt.Errorf("hostname %s should be IP address", dohHostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
dohHTTPClient := &http.Client{
|
return &network{
|
||||||
Timeout: DefaultDNSTimeout,
|
dialer: dialer,
|
||||||
|
idleTimeout: idleTimeout,
|
||||||
|
dns: doh.Resolver{
|
||||||
|
Host: dohHostname,
|
||||||
|
Class: doh.IN,
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: DNSTimeout,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
DialContext: dialer.DialContext,
|
DialContext: dialer.DialContext,
|
||||||
},
|
},
|
||||||
}
|
|
||||||
network := &Network{
|
|
||||||
dialer: dialer,
|
|
||||||
DNS: doh.Resolver{
|
|
||||||
Host: dohHostname,
|
|
||||||
Class: doh.IN,
|
|
||||||
HTTPClient: dohHTTPClient,
|
|
||||||
},
|
},
|
||||||
}
|
|
||||||
network.HTTP = http.Client{
|
|
||||||
Timeout: httpTimeout,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: network.DialContext,
|
|
||||||
},
|
},
|
||||||
}
|
}, nil
|
||||||
|
|
||||||
return network, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
"github.com/9seconds/mtg/v2/mtglib/network"
|
"github.com/9seconds/mtg/v2/mtglib/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeNetwork(conf *config) (*network.Network, error) {
|
func makeNetwork(conf *config) (network.Network, error) {
|
||||||
tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout)
|
tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout)
|
||||||
httpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultHTTPTimeout)
|
idleTimeout := conf.Network.Timeout.Idle.Value(network.DefaultIdleTimeout)
|
||||||
dohIP := conf.Network.DOHIP.Value(net.ParseIP(network.DefaultDOHHostname)).String()
|
dohIP := conf.Network.DOHIP.Value(net.ParseIP(network.DefaultDOHHostname)).String()
|
||||||
bufferSize := conf.TCPBuffer.Value(network.DefaultBufferSize)
|
bufferSize := conf.TCPBuffer.Value(network.DefaultBufferSize)
|
||||||
|
|
||||||
@@ -32,14 +32,14 @@ func makeNetwork(conf *config) (*network.Network, error) {
|
|||||||
|
|
||||||
switch len(proxyURLs) {
|
switch len(proxyURLs) {
|
||||||
case 0:
|
case 0:
|
||||||
return network.NewNetwork(baseDialer, dohIP, httpTimeout)
|
return network.NewNetwork(baseDialer, dohIP, idleTimeout)
|
||||||
case 1:
|
case 1:
|
||||||
socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0])
|
socksDialer, err := network.NewSocks5Dialer(baseDialer, proxyURLs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
|
return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return network.NewNetwork(socksDialer, dohIP, httpTimeout)
|
return network.NewNetwork(socksDialer, dohIP, idleTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs)
|
socksDialer, err := network.NewLoadBalancedSocks5Dialer(baseDialer, proxyURLs)
|
||||||
@@ -47,7 +47,7 @@ func makeNetwork(conf *config) (*network.Network, error) {
|
|||||||
return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
|
return nil, fmt.Errorf("cannot build socks5 dialer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return network.NewNetwork(socksDialer, dohIP, httpTimeout)
|
return network.NewNetwork(socksDialer, dohIP, idleTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exhaustResponse(response *http.Response) {
|
func exhaustResponse(response *http.Response) {
|
||||||
|
|||||||
Reference in New Issue
Block a user