feat(filtering): implement the divert policy (#569)
This is the policy we need to provoke certificate errors. We'll divert from, say, `8.8.8.8:443/udp` to, say, `1.1.1.1:443/udp`. We'll do something similar for `443/tcp`. This will cause certificate validation errors. With this change, we have now implemented the simple design described by https://github.com/ooni/probe/issues/1803#issuecomment-957323297.
This commit is contained in:
		
							parent
							
								
									675e3a5ba5
								
							
						
					
					
						commit
						851b9913fa
					
				@ -7,6 +7,8 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/miekg/dns"
 | 
						"github.com/miekg/dns"
 | 
				
			||||||
@ -39,6 +41,10 @@ const (
 | 
				
			|||||||
	// TProxyPolicyHijackHTTP causes the dialer to replace the target
 | 
						// TProxyPolicyHijackHTTP causes the dialer to replace the target
 | 
				
			||||||
	// address with the address of the local censored HTTP server.
 | 
						// address with the address of the local censored HTTP server.
 | 
				
			||||||
	TProxyPolicyHijackHTTP = TProxyPolicy("hijack-http")
 | 
						TProxyPolicyHijackHTTP = TProxyPolicy("hijack-http")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TProxyPolicyDivert causes the dialer, or WriteTo, to look into the
 | 
				
			||||||
 | 
						// divert table to map the endpoint to another endpoint.
 | 
				
			||||||
 | 
						TProxyPolicyDivert = TProxyPolicy("divert")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TProxyConfig contains configuration for TProxy.
 | 
					// TProxyConfig contains configuration for TProxy.
 | 
				
			||||||
@ -52,6 +58,10 @@ type TProxyConfig struct {
 | 
				
			|||||||
	// method _before_ using the TProxy.
 | 
						// method _before_ using the TProxy.
 | 
				
			||||||
	DNSCache map[string][]string
 | 
						DNSCache map[string][]string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Divert is a table that maps an endpoint to another endpoint. This
 | 
				
			||||||
 | 
						// table is only cheched when using the "divert" policy in the Endpoints table.
 | 
				
			||||||
 | 
						Divert map[string]string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Domains contains rules for filtering the lookup of domains. Note
 | 
						// Domains contains rules for filtering the lookup of domains. Note
 | 
				
			||||||
	// that the map MUST contain FQDNs. That is, you need to append
 | 
						// that the map MUST contain FQDNs. That is, you need to append
 | 
				
			||||||
	// a final dot to the domain name (e.g., `example.com.`).  If you
 | 
						// a final dot to the domain name (e.g., `example.com.`).  If you
 | 
				
			||||||
@ -224,11 +234,53 @@ func (c *tProxyUDPLikeConn) WriteTo(pkt []byte, addr net.Addr) (int, error) {
 | 
				
			|||||||
	case TProxyPolicyDropData:
 | 
						case TProxyPolicyDropData:
 | 
				
			||||||
		c.proxy.logger.Infof("tproxy: WriteTo: %s => %s", endpoint, policy)
 | 
							c.proxy.logger.Infof("tproxy: WriteTo: %s => %s", endpoint, policy)
 | 
				
			||||||
		return len(pkt), nil
 | 
							return len(pkt), nil
 | 
				
			||||||
 | 
						case TProxyPolicyDivert:
 | 
				
			||||||
 | 
							c.proxy.logger.Infof("tproxy: WriteTo: %s => %s", endpoint, policy)
 | 
				
			||||||
 | 
							return c.writeToWithDivert(pkt, endpoint)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return c.UDPLikeConn.WriteTo(pkt, addr)
 | 
							return c.UDPLikeConn.WriteTo(pkt, addr)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						errMissingDivertEntry    = errors.New("tproxy: missing divert entry")
 | 
				
			||||||
 | 
						errInvalidDivertProtocol = errors.New("tproxy: invalid divert protocol")
 | 
				
			||||||
 | 
						errInvalidDivertIP       = errors.New("tproxy: invalid divert IP")
 | 
				
			||||||
 | 
						errInvalidDivertPort     = errors.New("tproxy: invalid divert port")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *tProxyUDPLikeConn) writeToWithDivert(pkt []byte, endpoint string) (int, error) {
 | 
				
			||||||
 | 
						divert := c.proxy.config.Divert[endpoint]
 | 
				
			||||||
 | 
						if divert == "" {
 | 
				
			||||||
 | 
							return 0, errMissingDivertEntry
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						idx := strings.LastIndex(divert, "/udp")
 | 
				
			||||||
 | 
						if idx < 0 {
 | 
				
			||||||
 | 
							return 0, errInvalidDivertProtocol
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						divert = divert[:idx]
 | 
				
			||||||
 | 
						addr, port, err := net.SplitHostPort(divert)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ipAddr := net.ParseIP(addr)
 | 
				
			||||||
 | 
						if ipAddr == nil {
 | 
				
			||||||
 | 
							return 0, errInvalidDivertIP
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						portnum, err := strconv.Atoi(port)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if portnum <= 0 || portnum > 65535 {
 | 
				
			||||||
 | 
							return 0, errInvalidDivertPort
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						udpAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
							IP:   ipAddr,
 | 
				
			||||||
 | 
							Port: portnum,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return c.UDPLikeConn.WriteTo(pkt, udpAddr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// System resolver
 | 
					// System resolver
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
@ -276,6 +328,9 @@ func (d *tProxyDialer) DialContext(ctx context.Context, network, address string)
 | 
				
			|||||||
	case TProxyPolicyTCPRejectSYN:
 | 
						case TProxyPolicyTCPRejectSYN:
 | 
				
			||||||
		d.proxy.logger.Infof("tproxy: DialContext: %s/%s => %s", address, network, policy)
 | 
							d.proxy.logger.Infof("tproxy: DialContext: %s/%s => %s", address, network, policy)
 | 
				
			||||||
		return nil, netxlite.ECONNREFUSED
 | 
							return nil, netxlite.ECONNREFUSED
 | 
				
			||||||
 | 
						case TProxyPolicyDivert:
 | 
				
			||||||
 | 
							d.proxy.logger.Infof("tproxy: DialContext: %s/%s => %s", address, network, policy)
 | 
				
			||||||
 | 
							return d.dialContextWithDivert(ctx, network, endpoint)
 | 
				
			||||||
	case TProxyPolicyHijackDNS:
 | 
						case TProxyPolicyHijackDNS:
 | 
				
			||||||
		d.proxy.logger.Infof("tproxy: DialContext: %s/%s => %s", address, network, policy)
 | 
							d.proxy.logger.Infof("tproxy: DialContext: %s/%s => %s", address, network, policy)
 | 
				
			||||||
		address = d.proxy.dnsListener.LocalAddr().String()
 | 
							address = d.proxy.dnsListener.LocalAddr().String()
 | 
				
			||||||
@ -288,6 +343,29 @@ func (d *tProxyDialer) DialContext(ctx context.Context, network, address string)
 | 
				
			|||||||
	default:
 | 
						default:
 | 
				
			||||||
		// nothing
 | 
							// nothing
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return d.doDialContext(ctx, network, address)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *tProxyDialer) dialContextWithDivert(
 | 
				
			||||||
 | 
						ctx context.Context, network, endpoint string) (net.Conn, error) {
 | 
				
			||||||
 | 
						divert := d.proxy.config.Divert[endpoint]
 | 
				
			||||||
 | 
						if divert == "" {
 | 
				
			||||||
 | 
							return nil, errMissingDivertEntry
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						idx := strings.LastIndex(divert, "/")
 | 
				
			||||||
 | 
						if idx < 0 {
 | 
				
			||||||
 | 
							return nil, errInvalidDivertProtocol
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						address := divert[:idx]
 | 
				
			||||||
 | 
						protocol := divert[idx+1:]
 | 
				
			||||||
 | 
						if protocol != "tcp" && protocol != "udp" {
 | 
				
			||||||
 | 
							return nil, errInvalidDivertProtocol
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d.doDialContext(ctx, network, address)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *tProxyDialer) doDialContext(
 | 
				
			||||||
 | 
						ctx context.Context, network, address string) (net.Conn, error) {
 | 
				
			||||||
	conn, err := d.dialer.DialContext(ctx, network, address)
 | 
						conn, err := d.dialer.DialContext(ctx, network, address)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 | 
				
			|||||||
@ -224,6 +224,320 @@ func TestTProxyQUIC(t *testing.T) {
 | 
				
			|||||||
				t.Fatal("called")
 | 
									t.Fatal("called")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("with divert policy", func(t *testing.T) {
 | 
				
			||||||
 | 
								t.Run("no divert entry", func(t *testing.T) {
 | 
				
			||||||
 | 
									config := &TProxyConfig{
 | 
				
			||||||
 | 
										Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer proxy.Close()
 | 
				
			||||||
 | 
									var called bool
 | 
				
			||||||
 | 
									proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
				
			||||||
 | 
										return &mocks.QUICUDPLikeConn{
 | 
				
			||||||
 | 
											MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
 | 
												called = true
 | 
				
			||||||
 | 
												return len(p), nil
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pconn, err := proxy.ListenUDP("udp", &net.UDPAddr{})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := make([]byte, 128)
 | 
				
			||||||
 | 
									destAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
										IP:   net.IPv4(127, 0, 0, 1),
 | 
				
			||||||
 | 
										Port: 1234,
 | 
				
			||||||
 | 
										Zone: "",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count, err := pconn.WriteTo(data, destAddr)
 | 
				
			||||||
 | 
									if !errors.Is(err, errMissingDivertEntry) {
 | 
				
			||||||
 | 
										t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if count != 0 {
 | 
				
			||||||
 | 
										t.Fatal("unexpected number of bytes written")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if called {
 | 
				
			||||||
 | 
										t.Fatal("called")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								t.Run("invalid protocol", func(t *testing.T) {
 | 
				
			||||||
 | 
									config := &TProxyConfig{
 | 
				
			||||||
 | 
										Divert: map[string]string{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": "127.0.0.1:1235",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer proxy.Close()
 | 
				
			||||||
 | 
									var called bool
 | 
				
			||||||
 | 
									proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
				
			||||||
 | 
										return &mocks.QUICUDPLikeConn{
 | 
				
			||||||
 | 
											MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
 | 
												called = true
 | 
				
			||||||
 | 
												return len(p), nil
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pconn, err := proxy.ListenUDP("udp", &net.UDPAddr{})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := make([]byte, 128)
 | 
				
			||||||
 | 
									destAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
										IP:   net.IPv4(127, 0, 0, 1),
 | 
				
			||||||
 | 
										Port: 1234,
 | 
				
			||||||
 | 
										Zone: "",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count, err := pconn.WriteTo(data, destAddr)
 | 
				
			||||||
 | 
									if !errors.Is(err, errInvalidDivertProtocol) {
 | 
				
			||||||
 | 
										t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if count != 0 {
 | 
				
			||||||
 | 
										t.Fatal("unexpected number of bytes written")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if called {
 | 
				
			||||||
 | 
										t.Fatal("called")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								t.Run("invalid addrport", func(t *testing.T) {
 | 
				
			||||||
 | 
									config := &TProxyConfig{
 | 
				
			||||||
 | 
										Divert: map[string]string{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": "127.0.0.1/udp",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer proxy.Close()
 | 
				
			||||||
 | 
									var called bool
 | 
				
			||||||
 | 
									proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
				
			||||||
 | 
										return &mocks.QUICUDPLikeConn{
 | 
				
			||||||
 | 
											MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
 | 
												called = true
 | 
				
			||||||
 | 
												return len(p), nil
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pconn, err := proxy.ListenUDP("udp", &net.UDPAddr{})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := make([]byte, 128)
 | 
				
			||||||
 | 
									destAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
										IP:   net.IPv4(127, 0, 0, 1),
 | 
				
			||||||
 | 
										Port: 1234,
 | 
				
			||||||
 | 
										Zone: "",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count, err := pconn.WriteTo(data, destAddr)
 | 
				
			||||||
 | 
									if err == nil || !strings.HasSuffix(err.Error(), "missing port in address") {
 | 
				
			||||||
 | 
										t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if count != 0 {
 | 
				
			||||||
 | 
										t.Fatal("unexpected number of bytes written")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if called {
 | 
				
			||||||
 | 
										t.Fatal("called")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								t.Run("invalid address", func(t *testing.T) {
 | 
				
			||||||
 | 
									config := &TProxyConfig{
 | 
				
			||||||
 | 
										Divert: map[string]string{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": "localhost:1235/udp",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer proxy.Close()
 | 
				
			||||||
 | 
									var called bool
 | 
				
			||||||
 | 
									proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
				
			||||||
 | 
										return &mocks.QUICUDPLikeConn{
 | 
				
			||||||
 | 
											MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
 | 
												called = true
 | 
				
			||||||
 | 
												return len(p), nil
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pconn, err := proxy.ListenUDP("udp", &net.UDPAddr{})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := make([]byte, 128)
 | 
				
			||||||
 | 
									destAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
										IP:   net.IPv4(127, 0, 0, 1),
 | 
				
			||||||
 | 
										Port: 1234,
 | 
				
			||||||
 | 
										Zone: "",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count, err := pconn.WriteTo(data, destAddr)
 | 
				
			||||||
 | 
									if !errors.Is(err, errInvalidDivertIP) {
 | 
				
			||||||
 | 
										t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if count != 0 {
 | 
				
			||||||
 | 
										t.Fatal("unexpected number of bytes written")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if called {
 | 
				
			||||||
 | 
										t.Fatal("called")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								t.Run("invalid port syntax", func(t *testing.T) {
 | 
				
			||||||
 | 
									config := &TProxyConfig{
 | 
				
			||||||
 | 
										Divert: map[string]string{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": "127.0.0.1:xo/udp",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer proxy.Close()
 | 
				
			||||||
 | 
									var called bool
 | 
				
			||||||
 | 
									proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
				
			||||||
 | 
										return &mocks.QUICUDPLikeConn{
 | 
				
			||||||
 | 
											MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
 | 
												called = true
 | 
				
			||||||
 | 
												return len(p), nil
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pconn, err := proxy.ListenUDP("udp", &net.UDPAddr{})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := make([]byte, 128)
 | 
				
			||||||
 | 
									destAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
										IP:   net.IPv4(127, 0, 0, 1),
 | 
				
			||||||
 | 
										Port: 1234,
 | 
				
			||||||
 | 
										Zone: "",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count, err := pconn.WriteTo(data, destAddr)
 | 
				
			||||||
 | 
									if err == nil || !strings.HasSuffix(err.Error(), "invalid syntax") {
 | 
				
			||||||
 | 
										t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if count != 0 {
 | 
				
			||||||
 | 
										t.Fatal("unexpected number of bytes written")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if called {
 | 
				
			||||||
 | 
										t.Fatal("called")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								t.Run("invalid port value", func(t *testing.T) {
 | 
				
			||||||
 | 
									config := &TProxyConfig{
 | 
				
			||||||
 | 
										Divert: map[string]string{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": "127.0.0.1:65536/udp",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer proxy.Close()
 | 
				
			||||||
 | 
									var called bool
 | 
				
			||||||
 | 
									proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
				
			||||||
 | 
										return &mocks.QUICUDPLikeConn{
 | 
				
			||||||
 | 
											MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
 | 
												called = true
 | 
				
			||||||
 | 
												return len(p), nil
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pconn, err := proxy.ListenUDP("udp", &net.UDPAddr{})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := make([]byte, 128)
 | 
				
			||||||
 | 
									destAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
										IP:   net.IPv4(127, 0, 0, 1),
 | 
				
			||||||
 | 
										Port: 1234,
 | 
				
			||||||
 | 
										Zone: "",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count, err := pconn.WriteTo(data, destAddr)
 | 
				
			||||||
 | 
									if !errors.Is(err, errInvalidDivertPort) {
 | 
				
			||||||
 | 
										t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if count != 0 {
 | 
				
			||||||
 | 
										t.Fatal("unexpected number of bytes written")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if called {
 | 
				
			||||||
 | 
										t.Fatal("called")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								t.Run("correct settings", func(t *testing.T) {
 | 
				
			||||||
 | 
									config := &TProxyConfig{
 | 
				
			||||||
 | 
										Divert: map[string]string{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": "127.0.0.1:1235/udp",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
											"127.0.0.1:1234/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer proxy.Close()
 | 
				
			||||||
 | 
									var realAddr *net.UDPAddr
 | 
				
			||||||
 | 
									proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
				
			||||||
 | 
										return &mocks.QUICUDPLikeConn{
 | 
				
			||||||
 | 
											MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
 | 
												realAddr = addr.(*net.UDPAddr)
 | 
				
			||||||
 | 
												return len(p), nil
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pconn, err := proxy.ListenUDP("udp", &net.UDPAddr{})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									data := make([]byte, 128)
 | 
				
			||||||
 | 
									destAddr := &net.UDPAddr{
 | 
				
			||||||
 | 
										IP:   net.IPv4(127, 0, 0, 1),
 | 
				
			||||||
 | 
										Port: 1234,
 | 
				
			||||||
 | 
										Zone: "",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count, err := pconn.WriteTo(data, destAddr)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if count != len(data) {
 | 
				
			||||||
 | 
										t.Fatal("unexpected number of bytes written")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if realAddr == nil || (*realAddr).Port != 1235 {
 | 
				
			||||||
 | 
										t.Fatal("invalid realAddr or invalid port value")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -521,6 +835,111 @@ func TestTProxyDial(t *testing.T) {
 | 
				
			|||||||
			t.Fatal("expected nil conn here")
 | 
								t.Fatal("expected nil conn here")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("with divert", func(t *testing.T) {
 | 
				
			||||||
 | 
							t.Run("with missing entry", func(t *testing.T) {
 | 
				
			||||||
 | 
								config := &TProxyConfig{
 | 
				
			||||||
 | 
									Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
										"8.8.8.8:53/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatal(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer proxy.Close()
 | 
				
			||||||
 | 
								dialer := proxy.NewTProxyDialer(10 * time.Second)
 | 
				
			||||||
 | 
								resolver := netxlite.NewResolverUDP(
 | 
				
			||||||
 | 
									log.Log, &tProxyDialerAdapter{dialer}, "8.8.8.8:53")
 | 
				
			||||||
 | 
								addrs, err := resolver.LookupHost(context.Background(), "example.com")
 | 
				
			||||||
 | 
								if !errors.Is(err, errMissingDivertEntry) {
 | 
				
			||||||
 | 
									t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(addrs) != 0 {
 | 
				
			||||||
 | 
									t.Fatal("expected no addrs here")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("with no divert protocol", func(t *testing.T) {
 | 
				
			||||||
 | 
								config := &TProxyConfig{
 | 
				
			||||||
 | 
									Divert: map[string]string{
 | 
				
			||||||
 | 
										"8.8.8.8:53/udp": "8.8.8.8:54",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
										"8.8.8.8:53/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatal(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer proxy.Close()
 | 
				
			||||||
 | 
								dialer := proxy.NewTProxyDialer(10 * time.Second)
 | 
				
			||||||
 | 
								resolver := netxlite.NewResolverUDP(
 | 
				
			||||||
 | 
									log.Log, &tProxyDialerAdapter{dialer}, "8.8.8.8:53")
 | 
				
			||||||
 | 
								addrs, err := resolver.LookupHost(context.Background(), "example.com")
 | 
				
			||||||
 | 
								if !errors.Is(err, errInvalidDivertProtocol) {
 | 
				
			||||||
 | 
									t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(addrs) != 0 {
 | 
				
			||||||
 | 
									t.Fatal("expected no addrs here")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("with invalid divert protocol", func(t *testing.T) {
 | 
				
			||||||
 | 
								config := &TProxyConfig{
 | 
				
			||||||
 | 
									Divert: map[string]string{
 | 
				
			||||||
 | 
										"8.8.8.8:53/udp": "8.8.8.8:54/antani",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
										"8.8.8.8:53/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatal(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer proxy.Close()
 | 
				
			||||||
 | 
								dialer := proxy.NewTProxyDialer(10 * time.Second)
 | 
				
			||||||
 | 
								resolver := netxlite.NewResolverUDP(
 | 
				
			||||||
 | 
									log.Log, &tProxyDialerAdapter{dialer}, "8.8.8.8:53")
 | 
				
			||||||
 | 
								addrs, err := resolver.LookupHost(context.Background(), "example.com")
 | 
				
			||||||
 | 
								if !errors.Is(err, errInvalidDivertProtocol) {
 | 
				
			||||||
 | 
									t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(addrs) != 0 {
 | 
				
			||||||
 | 
									t.Fatal("expected no addrs here")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("with all good", func(t *testing.T) {
 | 
				
			||||||
 | 
								config := &TProxyConfig{
 | 
				
			||||||
 | 
									Divert: map[string]string{
 | 
				
			||||||
 | 
										"8.8.8.8:53/udp": "8.8.8.8:54/udp",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Endpoints: map[string]TProxyPolicy{
 | 
				
			||||||
 | 
										"8.8.8.8:53/udp": TProxyPolicyDivert,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								proxy, err := NewTProxy(config, log.Log)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatal(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer proxy.Close()
 | 
				
			||||||
 | 
								dialer := proxy.NewTProxyDialer(10 * time.Second)
 | 
				
			||||||
 | 
								resolver := netxlite.NewResolverUDP(
 | 
				
			||||||
 | 
									log.Log, &tProxyDialerAdapter{dialer}, "8.8.8.8:53")
 | 
				
			||||||
 | 
								ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
 | 
				
			||||||
 | 
								defer cancel()
 | 
				
			||||||
 | 
								addrs, err := resolver.LookupHost(ctx, "example.com")
 | 
				
			||||||
 | 
								if err == nil || err.Error() != netxlite.FailureGenericTimeoutError {
 | 
				
			||||||
 | 
									t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(addrs) != 0 {
 | 
				
			||||||
 | 
									t.Fatal("expected no addrs here")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestTProxyDNSCache(t *testing.T) {
 | 
					func TestTProxyDNSCache(t *testing.T) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user