Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions client/go/outline/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"localhost/client/go/outline/configregistry"
"localhost/client/go/outline/platerrors"
"localhost/client/go/outline/reporting"
"golang.getoutline.org/sdk/network"
"golang.getoutline.org/sdk/network/packetrelay"
"golang.getoutline.org/sdk/transport"
"github.com/goccy/go-yaml"
)
Expand All @@ -37,7 +37,7 @@ import (
// It's used by the connectivity test and the tun2socks handlers.
// TODO(fortuna):
// - Add connectivity test to StartSession()
// - Add NotifyNetworkChange() method. Needs to hold a network.PacketProxy instead of configregistry.PacketListener
// - Add NotifyNetworkChange() method. Needs to hold a packetrelay.PacketRelay instead of configregistry.PacketListener
// to handle that.
// - Refactor so that StartSession returns a Client
type Client struct {
Expand All @@ -52,9 +52,9 @@ func (c *Client) DialStream(ctx context.Context, address string) (transport.Stre
return c.sd.Dial(ctx, address)
}

// NewSession implements PacketProxy.NewSession.
func (c *Client) NewSession(resp network.PacketResponseReceiver) (network.PacketRequestSender, error) {
return c.pp.NewSession(resp)
// NewAssociation implements packetrelay.PacketRelay.NewAssociation.
func (c *Client) NewAssociation() (packetrelay.PacketSender, packetrelay.PacketReceiver, error) {
return c.pp.NewAssociation()
}

func (c *Client) NotifyNetworkChanged() {
Expand Down
10 changes: 5 additions & 5 deletions client/go/outline/configregistry/config_proxyless.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import (
"fmt"
"math/rand"

"localhost/client/go/configyaml"
"golang.getoutline.org/sdk/network"
"golang.getoutline.org/sdk/network/packetrelay"
"golang.getoutline.org/sdk/transport"
"golang.getoutline.org/sdk/transport/tlsfrag"
"localhost/client/go/configyaml"
)

const (
Expand Down Expand Up @@ -63,16 +63,16 @@ func parseProxylessTransportPair(ctx context.Context, configMap map[string]any,
}

pl := &PacketListener{ConnectionProviderInfo{ConnTypeDirect, ""}, &transport.UDPListener{}}
pp, err := network.NewPacketProxyFromPacketListener(pl)
pr, err := packetrelay.NewPacketRelayFromPacketListener(pl.PacketListener)
if err != nil {
return nil, fmt.Errorf("failed to create PacketProxy: %w", err)
return nil, fmt.Errorf("failed to create PacketRelay: %w", err)
}

return &TransportPair{
StreamDialer: &Dialer[transport.StreamConn]{
ConnectionProviderInfo: ConnectionProviderInfo{ConnType: ConnTypeDirect},
Dial: sd.DialStream,
},
PacketProxy: &PacketProxy{ConnectionProviderInfo{ConnTypeDirect, ""}, pp, nil},
PacketProxy: &PacketProxy{ConnectionProviderInfo{ConnTypeDirect, ""}, pr, nil},
}, nil
}
4 changes: 3 additions & 1 deletion client/go/outline/configregistry/config_proxyless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import (
"context"
"testing"

"localhost/client/go/configyaml"
"github.com/stretchr/testify/require"
"golang.getoutline.org/sdk/network/packetrelay"
"localhost/client/go/configyaml"
)

func TestParseProxyless(t *testing.T) {
Expand All @@ -35,4 +36,5 @@ func TestParseProxyless(t *testing.T) {
require.NotNil(t, transportPair.PacketProxy)
require.Equal(t, ConnTypeDirect, transportPair.StreamDialer.ConnType)
require.Equal(t, ConnTypeDirect, transportPair.PacketProxy.ConnType)
require.IsType(t, &packetrelay.PacketListenerRelay{}, transportPair.PacketProxy.PacketRelay)
}
54 changes: 33 additions & 21 deletions client/go/outline/configregistry/outline_dns_intercept.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@
package configregistry

import (
"context"
"fmt"
"log/slog"
"math/rand/v2"
"net/netip"
"time"

"localhost/client/go/outline/connectivity"
"localhost/client/go/outline/dnsintercept"

"golang.getoutline.org/sdk/network"
"golang.getoutline.org/sdk/network/dnsintercept"
"golang.getoutline.org/sdk/network/dnstruncate"
"golang.getoutline.org/sdk/network/packetrelay"
"golang.getoutline.org/sdk/transport"
)

Expand All @@ -50,46 +53,55 @@ func wrapTransportPairWithOutlineDNS(sd *Dialer[transport.StreamConn], pl *Packe
// Randomly selects a DNS resolver for the VPN session
remoteDNS := outlineDNSResolvers[rand.IntN(len(outlineDNSResolvers))]

// Intercept DNS for StreamDialer
sdForward, err := dnsintercept.NewDNSRedirectStreamDialer(transport.FuncStreamDialer(sd.Dial), linkLocalDNS, remoteDNS)
if err != nil {
return nil, fmt.Errorf("failed to create DNS redirect StreamDialer: %w", err)
// Intercept DNS for StreamDialer: remap TCP connections to linkLocalDNS → remoteDNS.
sdForward := func(ctx context.Context, addr string) (transport.StreamConn, error) {
if dst, err := netip.ParseAddrPort(addr); err == nil && dst.Addr().Unmap() == linkLocalDNS.Addr() && dst.Port() == linkLocalDNS.Port() {
addr = remoteDNS.String()
}
return sd.Dial(ctx, addr)
}

// Intercept DNS for PacketProxy
ppBase, err := network.NewPacketProxyFromPacketListener(pl)
baseListener, err := packetrelay.NewPacketRelayFromPacketListener(pl.PacketListener)
if err != nil {
return nil, fmt.Errorf("failed to create PacketProxy: %w", err)
return nil, fmt.Errorf("failed to create base PacketRelay: %w", err)
}
// Forwards everything including DNS. For DNS it translates between the link-local and remote addresses for the DNS resolver.
ppForward, err := dnsintercept.NewDNSRedirectPacketProxy(ppBase, linkLocalDNS, remoteDNS)
// Forward relay: intercept DNS at link-local address, forward to remote resolver.
// DNS gets a shorter 5s timeout on its own independent listener.
dnsListener, err := packetrelay.NewPacketRelayFromPacketListener(pl.PacketListener)
if err != nil {
return nil, fmt.Errorf("failed to create DNS redirect PacketProxy: %w", err)
return nil, fmt.Errorf("failed to create DNS PacketRelay: %w", err)
}
if err := dnsListener.SetWriteIdleTimeout(5 * time.Second); err != nil {
return nil, fmt.Errorf("failed to set DNS relay timeout: %w", err)
}
// Forwards everything except DNS. For DNS it returns a truncated response.
ppTrunc, err := dnsintercept.NewDNSTruncatePacketProxy(ppBase, linkLocalDNS)
relayForward := dnsintercept.NewInterceptDNSPacketRelay(dnsListener, baseListener, linkLocalDNS, remoteDNS)
// Truncate relay: intercept DNS at link-local address, return truncated response (forces TCP retry).
// Non-DNS traffic passes through to baseListener.
dnsTruncRelay, err := dnstruncate.NewPacketRelay()
if err != nil {
return nil, fmt.Errorf("failed to create always-truncate DNS PacketProxy: %w", err)
return nil, fmt.Errorf("failed to create DNS truncate relay: %w", err)
}
ppMain, err := network.NewDelegatePacketProxy(ppTrunc)
relayTrunc := dnsintercept.NewInterceptDNSPacketRelay(dnsTruncRelay, baseListener, linkLocalDNS, remoteDNS)
// Delegate relay starts with truncate (UDP unverified), switches to forward when UDP is healthy.
relayMain, err := packetrelay.NewDelegatePacketRelay(relayTrunc)
if err != nil {
return nil, fmt.Errorf("failed to create indirect PacketProxy: %w", err)
return nil, fmt.Errorf("failed to create delegate PacketRelay: %w", err)
}

onNetworkChanged := func() {
go func() {
if err := connectivity.CheckUDPConnectivity(pl); err == nil {
slog.Info("remote device UDP is healthy")
ppMain.SetProxy(ppForward)
relayMain.SetRelay(relayForward)
} else {
slog.Warn("remote device UDP is not healthy", "err", err)
ppMain.SetProxy(ppTrunc)
relayMain.SetRelay(relayTrunc)
}
Comment thread
fortuna marked this conversation as resolved.
}()
}

return &TransportPair{
&Dialer[transport.StreamConn]{sd.ConnectionProviderInfo, sdForward.DialStream},
&PacketProxy{pl.ConnectionProviderInfo, ppMain, onNetworkChanged},
&Dialer[transport.StreamConn]{sd.ConnectionProviderInfo, sdForward},
&PacketProxy{pl.ConnectionProviderInfo, relayMain, onNetworkChanged},
}, nil
}
6 changes: 3 additions & 3 deletions client/go/outline/configregistry/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"context"
"encoding/json"

"golang.getoutline.org/sdk/network"
"golang.getoutline.org/sdk/network/packetrelay"
"golang.getoutline.org/sdk/transport"
)

Expand Down Expand Up @@ -72,10 +72,10 @@ type PacketListener struct {
transport.PacketListener
}

// PacketProxy is a [network.PacketProxy] with embedded ConnectionProviderInfo.
// PacketProxy is a [packetrelay.PacketRelay] with embedded ConnectionProviderInfo.
type PacketProxy struct {
ConnectionProviderInfo
network.PacketProxy
packetrelay.PacketRelay
NotifyNetworkChanged func()
}

Expand Down
134 changes: 0 additions & 134 deletions client/go/outline/dnsintercept/README.md

This file was deleted.

Loading
Loading