mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 19:45:35 +00:00
f27b2cf569
The design changed during integration and testing, resulting in the earlier implementation growing in the appc package to be intended now only for the sniproxy implementation. That code is moved to it's final location, and the current App Connector code is now renamed. Updates tailscale/corp#15437 Signed-off-by: James Tucker <james@tailscale.com>
105 lines
2.6 KiB
Go
105 lines
2.6 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"net/netip"
|
|
"slices"
|
|
|
|
"inet.af/tcpproxy"
|
|
"tailscale.com/net/netutil"
|
|
)
|
|
|
|
type tcpRoundRobinHandler struct {
|
|
// To is a list of destination addresses to forward to.
|
|
// An entry may be either an IP address or a DNS name.
|
|
To []string
|
|
|
|
// DialContext is used to make the outgoing TCP connection.
|
|
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
|
|
|
// ReachableIPs enumerates the IP addresses this handler is reachable on.
|
|
ReachableIPs []netip.Addr
|
|
}
|
|
|
|
// ReachableOn returns the IP addresses this handler is reachable on.
|
|
func (h *tcpRoundRobinHandler) ReachableOn() []netip.Addr {
|
|
return h.ReachableIPs
|
|
}
|
|
|
|
func (h *tcpRoundRobinHandler) Handle(c net.Conn) {
|
|
addrPortStr := c.LocalAddr().String()
|
|
_, port, err := net.SplitHostPort(addrPortStr)
|
|
if err != nil {
|
|
log.Printf("tcpRoundRobinHandler.Handle: bogus addrPort %q", addrPortStr)
|
|
c.Close()
|
|
return
|
|
}
|
|
|
|
var p tcpproxy.Proxy
|
|
p.ListenFunc = func(net, laddr string) (net.Listener, error) {
|
|
return netutil.NewOneConnListener(c, nil), nil
|
|
}
|
|
|
|
dest := h.To[rand.Intn(len(h.To))]
|
|
dial := &tcpproxy.DialProxy{
|
|
Addr: fmt.Sprintf("%s:%s", dest, port),
|
|
DialContext: h.DialContext,
|
|
}
|
|
|
|
p.AddRoute(addrPortStr, dial)
|
|
p.Start()
|
|
}
|
|
|
|
type tcpSNIHandler struct {
|
|
// Allowlist enumerates the FQDNs which may be proxied via SNI. An
|
|
// empty slice means all domains are permitted.
|
|
Allowlist []string
|
|
|
|
// DialContext is used to make the outgoing TCP connection.
|
|
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
|
|
|
// ReachableIPs enumerates the IP addresses this handler is reachable on.
|
|
ReachableIPs []netip.Addr
|
|
}
|
|
|
|
// ReachableOn returns the IP addresses this handler is reachable on.
|
|
func (h *tcpSNIHandler) ReachableOn() []netip.Addr {
|
|
return h.ReachableIPs
|
|
}
|
|
|
|
func (h *tcpSNIHandler) Handle(c net.Conn) {
|
|
addrPortStr := c.LocalAddr().String()
|
|
_, port, err := net.SplitHostPort(addrPortStr)
|
|
if err != nil {
|
|
log.Printf("tcpSNIHandler.Handle: bogus addrPort %q", addrPortStr)
|
|
c.Close()
|
|
return
|
|
}
|
|
|
|
var p tcpproxy.Proxy
|
|
p.ListenFunc = func(net, laddr string) (net.Listener, error) {
|
|
return netutil.NewOneConnListener(c, nil), nil
|
|
}
|
|
p.AddSNIRouteFunc(addrPortStr, func(ctx context.Context, sniName string) (t tcpproxy.Target, ok bool) {
|
|
if len(h.Allowlist) > 0 {
|
|
// TODO(tom): handle subdomains
|
|
if slices.Index(h.Allowlist, sniName) < 0 {
|
|
return nil, false
|
|
}
|
|
}
|
|
|
|
return &tcpproxy.DialProxy{
|
|
Addr: net.JoinHostPort(sniName, port),
|
|
DialContext: h.DialContext,
|
|
}, true
|
|
})
|
|
p.Start()
|
|
}
|