mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-24 09:51:41 +00:00

This adds netx.DialFunc, unifying a type we have a bazillion other places, giving it now a nice short name that's clickable in editors, etc. That highlighted that my earlier move (03b47a55c7956) of stuff from nettest into netx moved too much: it also dragged along the memnet impl, meaning all users of netx.DialFunc who just wanted netx for the type definition were instead also pulling in all of memnet. So move the memnet implementation netx.Network into memnet, a package we already had. Then use netx.DialFunc in a bunch of places. I'm sure I missed some. And plenty remain in other repos, to be updated later. Updates tailscale/corp#27636 Change-Id: I7296cd4591218e8624e214f8c70dab05fb884e95 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
106 lines
2.6 KiB
Go
106 lines
2.6 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"math/rand/v2"
|
|
"net"
|
|
"net/netip"
|
|
"slices"
|
|
|
|
"github.com/inetaf/tcpproxy"
|
|
"tailscale.com/net/netutil"
|
|
"tailscale.com/net/netx"
|
|
)
|
|
|
|
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 netx.DialFunc
|
|
|
|
// 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()
|
|
}
|