mirror of
https://github.com/coredns/coredns.git
synced 2026-10-16 23:35:43 -04:00
The proxyproto plugin parsed PROXY protocol v2 headers only to recover the real client address; the TLV vector was discarded, so downstream plugins had no way to read values such as the AWS VPC endpoint ID, SSL client CN, or vendor-specific custom TLVs. Add an opt-in `tlv_metadata` option. When set, the connection's raw PPv2 TLV vector is captured and the plugin, acting as a metadata.Provider, publishes it under the `proxyproto/tlv` label as a hex-encoded string. Consumers decode and split it with proxyproto.SplitTLVs. The option is off by default, so existing deployments skip the extra per-connection processing and behave exactly as before. PROXY protocol v1 connections, which carry no TLVs, are unaffected. Capture spans all transports: dns:// (UDP and TCP), tls:// (DoT), https:// (DoH), grpc://, quic:// (DoQ), and https3:// (DoH3). QUIC-based transports require udp_session_tracking, since the PPv2 header arrives as a standalone first datagram ahead of the QUIC handshake. Includes unit tests for capture, the metadata provider (direct and through the real metadata chain), v1 compatibility, and option parsing, plus README and man page updates. Fixes #8633 Signed-off-by: Mekias Yohannes <mekias@amazon.com> Co-authored-by: Mekias Yohannes <mekias@amazon.com>
519 lines
16 KiB
Go
519 lines
16 KiB
Go
package proxyproto
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
proxyproto "github.com/pires/go-proxyproto"
|
|
)
|
|
|
|
// TestV1HeaderNoTLVs verifies PROXY protocol v1 (which has no TLVs) is handled
|
|
// cleanly: capture is a no-op, with no error or panic.
|
|
func TestV1HeaderNoTLVs(t *testing.T) {
|
|
h := &proxyproto.Header{
|
|
Version: 1,
|
|
Command: proxyproto.PROXY,
|
|
TransportProtocol: proxyproto.TCPv4,
|
|
SourceAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000},
|
|
DestinationAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53},
|
|
}
|
|
if got := rawTLVs(h); got != nil {
|
|
t.Fatalf("rawTLVs(v1) = %v, want nil", got)
|
|
}
|
|
pc := &PacketConn{CaptureTLVs: true}
|
|
if got := pc.tlvsFor(h); got != nil {
|
|
t.Fatalf("tlvsFor(v1) = %v, want nil", got)
|
|
}
|
|
if tlvs, err := h.TLVs(); err != nil || len(tlvs) != 0 {
|
|
t.Fatalf("v1 TLVs() = %v, %v; want empty, nil", tlvs, err)
|
|
}
|
|
}
|
|
|
|
// TestAddrFromConnCommand covers both PROXY and LOCAL commands: PROXY publishes
|
|
// the TLVs, LOCAL must not (the PROXY protocol spec requires discarding the
|
|
// protocol block for LOCAL, even though go-proxyproto retains the parsed TLVs).
|
|
func TestAddrFromConnCommand(t *testing.T) {
|
|
tlvs := []proxyproto.TLV{{Type: 0xEA, Value: []byte{0x01, 0x02}}}
|
|
tests := []struct {
|
|
name string
|
|
command proxyproto.ProtocolVersionAndCommand
|
|
wantTLV bool
|
|
}{
|
|
{"PROXY", proxyproto.PROXY, true},
|
|
{"LOCAL", proxyproto.LOCAL, false},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
h := &proxyproto.Header{
|
|
Version: 2,
|
|
Command: tc.command,
|
|
TransportProtocol: proxyproto.TCPv4,
|
|
SourceAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000},
|
|
DestinationAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53},
|
|
}
|
|
if err := h.SetTLVs(tlvs); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
raw, _ := h.Format()
|
|
pc := proxyproto.NewConn(&preloadedConn{buf: raw})
|
|
defer pc.Close()
|
|
|
|
a := AddrFromConn(pc)
|
|
if tc.wantTLV {
|
|
if a == nil || len(a.RawTLVs()) == 0 {
|
|
t.Fatalf("AddrFromConn(%s) did not return TLVs", tc.name)
|
|
}
|
|
} else if a != nil {
|
|
t.Fatalf("AddrFromConn(%s) = %v, want nil (TLVs must not be published)", tc.name, a)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// fakeListener returns a preset sequence of conns from Accept.
|
|
type fakeListener struct {
|
|
conns []net.Conn
|
|
i int
|
|
addr net.Addr
|
|
closed bool
|
|
}
|
|
|
|
func (l *fakeListener) Accept() (net.Conn, error) {
|
|
if l.i >= len(l.conns) {
|
|
return nil, net.ErrClosed
|
|
}
|
|
c := l.conns[l.i]
|
|
l.i++
|
|
return c, nil
|
|
}
|
|
func (l *fakeListener) Close() error { l.closed = true; return nil }
|
|
func (l *fakeListener) Addr() net.Addr {
|
|
if l.addr != nil {
|
|
return l.addr
|
|
}
|
|
return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 53}
|
|
}
|
|
|
|
func TestNewTLVListenerAcceptWrapsProxyConn(t *testing.T) {
|
|
src := &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000}
|
|
dst := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53}
|
|
h := &proxyproto.Header{
|
|
Version: 2, Command: proxyproto.PROXY, TransportProtocol: proxyproto.TCPv4,
|
|
SourceAddr: src, DestinationAddr: dst,
|
|
}
|
|
if err := h.SetTLVs([]proxyproto.TLV{{Type: 0xEA, Value: []byte{0x01, 0x02}}}); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
raw, _ := h.Format()
|
|
pc := proxyproto.NewConn(&preloadedConn{buf: raw})
|
|
|
|
// Inject a fake inner listener that hands back the *proxyproto.Conn, so we
|
|
// test TLVListener.Accept's wrapping without driving the real proxyproto
|
|
// Listener's own header/policy handling.
|
|
l := &TLVListener{inner: &fakeListener{conns: []net.Conn{pc}}}
|
|
defer l.Close()
|
|
_ = l.Addr()
|
|
|
|
c, err := l.Accept()
|
|
if err != nil {
|
|
t.Fatalf("Accept: %v", err)
|
|
}
|
|
a, ok := c.RemoteAddr().(*Addr)
|
|
if !ok {
|
|
t.Fatalf("Accept conn RemoteAddr type = %T, want *Addr", c.RemoteAddr())
|
|
}
|
|
got, err := proxyproto.SplitTLVs(a.RawTLVs())
|
|
if err != nil || len(got) != 1 || !bytes.Equal(got[0].Value, []byte{0x01, 0x02}) {
|
|
t.Fatalf("TLV mismatch: %+v err=%v", got, err)
|
|
}
|
|
c.Close()
|
|
}
|
|
|
|
func TestTLVListenerAcceptPassesThroughNonProxyConn(t *testing.T) {
|
|
// A non-*proxyproto.Conn is returned unchanged.
|
|
plain := plainRemoteConn{src: &net.TCPAddr{IP: net.ParseIP("198.51.100.1"), Port: 7000}}
|
|
l := &TLVListener{inner: &fakeListener{conns: []net.Conn{plain}}}
|
|
c, err := l.Accept()
|
|
if err != nil {
|
|
t.Fatalf("Accept: %v", err)
|
|
}
|
|
if _, ok := c.RemoteAddr().(*Addr); ok {
|
|
t.Fatal("non-proxy conn should pass through without *Addr")
|
|
}
|
|
}
|
|
|
|
func TestNewTLVListenerConstructor(t *testing.T) {
|
|
// NewTLVListener returns a *TLVListener wrapping a proxyproto.Listener.
|
|
base := &fakeListener{addr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9953}}
|
|
l := NewTLVListener(base, nil)
|
|
if l.Addr().String() != base.Addr().String() {
|
|
t.Fatalf("Addr = %s, want %s", l.Addr(), base.Addr())
|
|
}
|
|
if err := l.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
if !base.closed {
|
|
t.Fatal("expected inner listener to be closed")
|
|
}
|
|
}
|
|
|
|
func TestNewAddr(t *testing.T) {
|
|
u := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53}
|
|
r := &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000}
|
|
a := NewAddr(u, r, []byte{0xaa})
|
|
if a.Network() != u.Network() {
|
|
t.Errorf("Network = %q, want %q", a.Network(), u.Network())
|
|
}
|
|
if a.String() != r.String() {
|
|
t.Errorf("String = %q, want %q", a.String(), r.String())
|
|
}
|
|
if !bytes.Equal(a.RawTLVs(), []byte{0xaa}) {
|
|
t.Errorf("RawTLVs = %v, want [aa]", a.RawTLVs())
|
|
}
|
|
}
|
|
|
|
// tlsLikeConn wraps a net.Conn and exposes NetConn, mimicking *tls.Conn so the
|
|
// netConner branch of underlyingProxyConn is exercised.
|
|
type tlsLikeConn struct {
|
|
net.Conn
|
|
inner net.Conn
|
|
}
|
|
|
|
func (c *tlsLikeConn) NetConn() net.Conn { return c.inner }
|
|
|
|
func TestAddrFromConnUnwrapsTLSLayer(t *testing.T) {
|
|
src := &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000}
|
|
dst := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53}
|
|
h := &proxyproto.Header{
|
|
Version: 2, Command: proxyproto.PROXY, TransportProtocol: proxyproto.TCPv4,
|
|
SourceAddr: src, DestinationAddr: dst,
|
|
}
|
|
if err := h.SetTLVs([]proxyproto.TLV{{Type: 0xEA, Value: []byte{0x07}}}); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
raw, _ := h.Format()
|
|
pc := proxyproto.NewConn(&preloadedConn{buf: raw})
|
|
|
|
// tlsLike -> *proxyproto.Conn. RemoteAddr on the plain tls-like wrapper does
|
|
// NOT promote the proxy header, so AddrFromConn must fall through to the
|
|
// NetConn-unwrap path to reach the *proxyproto.Conn.
|
|
wrapped := &tlsLikeConn{Conn: plainRemoteConn{src: dst}, inner: pc}
|
|
|
|
a := AddrFromConn(wrapped)
|
|
if a == nil {
|
|
t.Fatal("AddrFromConn did not unwrap tls-like layer")
|
|
}
|
|
got, err := proxyproto.SplitTLVs(a.RawTLVs())
|
|
if err != nil || len(got) != 1 || !bytes.Equal(got[0].Value, []byte{0x07}) {
|
|
t.Fatalf("TLV mismatch after unwrap: %+v err=%v", got, err)
|
|
}
|
|
pc.Close()
|
|
}
|
|
|
|
func TestAddrFromConnUnknownConn(t *testing.T) {
|
|
// A conn that is neither *proxyproto.Conn, *tlvConn, nor a netConner, and
|
|
// whose RemoteAddr is a plain addr: AddrFromConn returns nil.
|
|
if a := AddrFromConn(plainRemoteConn{src: &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5}}); a != nil {
|
|
t.Fatalf("AddrFromConn(unknown) = %v, want nil", a)
|
|
}
|
|
}
|
|
|
|
// plainRemoteConn is a net.Conn whose RemoteAddr is a plain *net.TCPAddr.
|
|
type plainRemoteConn struct {
|
|
net.Conn
|
|
src net.Addr
|
|
}
|
|
|
|
func (c plainRemoteConn) RemoteAddr() net.Addr { return c.src }
|
|
|
|
// preloadedConn is a net.Conn whose Read drains a fixed byte buffer (the PPv2
|
|
// header), avoiding net.Pipe synchronization in unit tests.
|
|
type preloadedConn struct {
|
|
net.Conn
|
|
buf []byte
|
|
off int
|
|
remote net.Addr
|
|
}
|
|
|
|
func (c *preloadedConn) Read(p []byte) (int, error) {
|
|
if c.off >= len(c.buf) {
|
|
return 0, io.EOF
|
|
}
|
|
n := copy(p, c.buf[c.off:])
|
|
c.off += n
|
|
return n, nil
|
|
}
|
|
func (c *preloadedConn) Write(p []byte) (int, error) { return len(p), nil }
|
|
func (c *preloadedConn) Close() error { return nil }
|
|
func (c *preloadedConn) RemoteAddr() net.Addr {
|
|
if c.remote != nil {
|
|
return c.remote
|
|
}
|
|
return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1}
|
|
}
|
|
func (c *preloadedConn) LocalAddr() net.Addr {
|
|
return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 53}
|
|
}
|
|
func (c *preloadedConn) SetDeadline(time.Time) error { return nil }
|
|
func (c *preloadedConn) SetReadDeadline(time.Time) error { return nil }
|
|
func (c *preloadedConn) SetWriteDeadline(time.Time) error { return nil }
|
|
|
|
// ppv2UDPPacketWithTLVs builds a valid PPv2 UDP datagram carrying the given
|
|
// TLVs followed by the DNS payload, using the library's own formatter.
|
|
func ppv2UDPPacketWithTLVs(t *testing.T, src, dst *net.UDPAddr, tlvs []proxyproto.TLV, payload []byte) []byte {
|
|
t.Helper()
|
|
h := &proxyproto.Header{
|
|
Version: 2,
|
|
Command: proxyproto.PROXY,
|
|
TransportProtocol: proxyproto.UDPv4,
|
|
SourceAddr: src,
|
|
DestinationAddr: dst,
|
|
}
|
|
if len(tlvs) > 0 {
|
|
if err := h.SetTLVs(tlvs); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
}
|
|
raw, err := h.Format()
|
|
if err != nil {
|
|
t.Fatalf("Format: %v", err)
|
|
}
|
|
return append(raw, payload...)
|
|
}
|
|
|
|
func usePolicy(proxyproto.ConnPolicyOptions) (proxyproto.Policy, error) {
|
|
return proxyproto.USE, nil
|
|
}
|
|
|
|
func TestReadFromCapturesTLVsWhenEnabled(t *testing.T) {
|
|
src := udpAddr("192.168.1.10", 40000)
|
|
dst := udpAddr("10.0.0.1", 53)
|
|
payload := []byte("dns-payload")
|
|
tlvs := []proxyproto.TLV{
|
|
{Type: proxyproto.PP2_TYPE_AUTHORITY, Value: []byte("example.org")},
|
|
{Type: 0xEA, Value: []byte{0x01, 0x02, 0x03}},
|
|
}
|
|
packet := ppv2UDPPacketWithTLVs(t, src, dst, tlvs, payload)
|
|
|
|
pc := &PacketConn{PacketConn: &singlePacketConn{}, ConnPolicy: usePolicy, CaptureTLVs: true}
|
|
n, addr, err := pc.readFrom(packet, udpAddr("203.0.113.9", 5000))
|
|
if err != nil {
|
|
t.Fatalf("readFrom: %v", err)
|
|
}
|
|
if !bytes.Equal(packet[:n], payload) {
|
|
t.Fatalf("payload = %q, want %q", packet[:n], payload)
|
|
}
|
|
a, ok := addr.(*Addr)
|
|
if !ok {
|
|
t.Fatalf("addr type = %T, want *Addr", addr)
|
|
}
|
|
raw := a.RawTLVs()
|
|
if len(raw) == 0 {
|
|
t.Fatal("expected raw TLVs to be captured")
|
|
}
|
|
// Round-trip: the captured bytes must re-split to the TLVs we sent.
|
|
got, err := proxyproto.SplitTLVs(raw)
|
|
if err != nil {
|
|
t.Fatalf("SplitTLVs: %v", err)
|
|
}
|
|
if len(got) != len(tlvs) {
|
|
t.Fatalf("got %d TLVs, want %d", len(got), len(tlvs))
|
|
}
|
|
for i, want := range tlvs {
|
|
if got[i].Type != want.Type || !bytes.Equal(got[i].Value, want.Value) {
|
|
t.Errorf("TLV %d = {%#x,%v}, want {%#x,%v}", i, got[i].Type, got[i].Value, want.Type, want.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadFromSkipsTLVsWhenDisabled(t *testing.T) {
|
|
src := udpAddr("192.168.1.10", 40000)
|
|
dst := udpAddr("10.0.0.1", 53)
|
|
tlvs := []proxyproto.TLV{{Type: 0xEA, Value: []byte{0x09}}}
|
|
packet := ppv2UDPPacketWithTLVs(t, src, dst, tlvs, []byte("x"))
|
|
|
|
pc := &PacketConn{PacketConn: &singlePacketConn{}, ConnPolicy: usePolicy} // CaptureTLVs defaults to false
|
|
_, addr, err := pc.readFrom(packet, udpAddr("203.0.113.9", 5000))
|
|
if err != nil {
|
|
t.Fatalf("readFrom: %v", err)
|
|
}
|
|
a, ok := addr.(*Addr)
|
|
if !ok {
|
|
t.Fatalf("addr type = %T, want *Addr", addr)
|
|
}
|
|
if a.RawTLVs() != nil {
|
|
t.Fatalf("expected no TLVs captured when disabled, got %v", a.RawTLVs())
|
|
}
|
|
}
|
|
|
|
func TestReadFromNoTLVsPresent(t *testing.T) {
|
|
src := udpAddr("192.168.1.10", 40000)
|
|
dst := udpAddr("10.0.0.1", 53)
|
|
packet := ppv2UDPPacketWithTLVs(t, src, dst, nil, []byte("x"))
|
|
|
|
pc := &PacketConn{PacketConn: &singlePacketConn{}, ConnPolicy: usePolicy, CaptureTLVs: true}
|
|
_, addr, err := pc.readFrom(packet, udpAddr("203.0.113.9", 5000))
|
|
if err != nil {
|
|
t.Fatalf("readFrom: %v", err)
|
|
}
|
|
a, ok := addr.(*Addr)
|
|
if !ok {
|
|
t.Fatalf("addr type = %T, want *Addr", addr)
|
|
}
|
|
if a.RawTLVs() != nil {
|
|
t.Fatalf("expected nil TLVs when header has none, got %v", a.RawTLVs())
|
|
}
|
|
}
|
|
|
|
func TestRawTLVsHelper(t *testing.T) {
|
|
// nil header -> nil
|
|
if got := rawTLVs(nil); got != nil {
|
|
t.Fatalf("rawTLVs(nil) = %v, want nil", got)
|
|
}
|
|
// header with no TLVs -> nil
|
|
h := &proxyproto.Header{Version: 2}
|
|
if got := rawTLVs(h); got != nil {
|
|
t.Fatalf("rawTLVs(no tlvs) = %v, want nil", got)
|
|
}
|
|
// header with TLVs -> re-splittable bytes
|
|
tlvs := []proxyproto.TLV{{Type: proxyproto.PP2_TYPE_UNIQUE_ID, Value: []byte("abc")}}
|
|
if err := h.SetTLVs(tlvs); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
raw := rawTLVs(h)
|
|
got, err := proxyproto.SplitTLVs(raw)
|
|
if err != nil {
|
|
t.Fatalf("SplitTLVs: %v", err)
|
|
}
|
|
if len(got) != 1 || got[0].Type != proxyproto.PP2_TYPE_UNIQUE_ID || !bytes.Equal(got[0].Value, []byte("abc")) {
|
|
t.Fatalf("round-trip mismatch: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestTLVConnRemoteAddrCarriesTLVs(t *testing.T) {
|
|
src := &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000}
|
|
dst := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53}
|
|
tlvs := []proxyproto.TLV{{Type: 0xEA, Value: []byte{0xaa, 0xbb}}}
|
|
h := &proxyproto.Header{
|
|
Version: 2,
|
|
Command: proxyproto.PROXY,
|
|
TransportProtocol: proxyproto.TCPv4,
|
|
SourceAddr: src,
|
|
DestinationAddr: dst,
|
|
}
|
|
if err := h.SetTLVs(tlvs); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
raw, err := h.Format()
|
|
if err != nil {
|
|
t.Fatalf("Format: %v", err)
|
|
}
|
|
|
|
client, server := net.Pipe()
|
|
go func() {
|
|
client.Write(raw)
|
|
// Keep the pipe open; ProxyHeader only needs the header bytes.
|
|
}()
|
|
|
|
pc := proxyproto.NewConn(server)
|
|
tc := &tlvConn{Conn: pc, pc: pc}
|
|
|
|
addr := tc.RemoteAddr()
|
|
a, ok := addr.(*Addr)
|
|
if !ok {
|
|
t.Fatalf("RemoteAddr type = %T, want *Addr", addr)
|
|
}
|
|
if a.String() != src.String() {
|
|
t.Errorf("RemoteAddr = %s, want %s", a.String(), src)
|
|
}
|
|
got, err := proxyproto.SplitTLVs(a.RawTLVs())
|
|
if err != nil {
|
|
t.Fatalf("SplitTLVs: %v", err)
|
|
}
|
|
if len(got) != 1 || got[0].Type != 0xEA || !bytes.Equal(got[0].Value, []byte{0xaa, 0xbb}) {
|
|
t.Fatalf("TLV round-trip mismatch: %+v", got)
|
|
}
|
|
tc.Close()
|
|
}
|
|
|
|
func TestTLVConnRemoteAddrNoHeader(t *testing.T) {
|
|
// A connection with no PROXY header: RemoteAddr falls back to the plain
|
|
// address, not an *Addr.
|
|
client, server := net.Pipe()
|
|
go func() {
|
|
client.Write([]byte("not a proxy header"))
|
|
}()
|
|
pc := proxyproto.NewConn(server)
|
|
tc := &tlvConn{Conn: pc, pc: pc}
|
|
if _, ok := tc.RemoteAddr().(*Addr); ok {
|
|
t.Fatal("expected plain addr when no PROXY header present")
|
|
}
|
|
tc.Close()
|
|
}
|
|
|
|
// delegatingConn embeds a net.Conn and promotes its RemoteAddr, mimicking the
|
|
// wrapper chain (tls.Conn, connection limiter) the DoH path sees.
|
|
type delegatingConn struct {
|
|
net.Conn
|
|
}
|
|
|
|
func TestAddrFromConnDirect(t *testing.T) {
|
|
src := &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000}
|
|
dst := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53}
|
|
h := &proxyproto.Header{
|
|
Version: 2, Command: proxyproto.PROXY, TransportProtocol: proxyproto.TCPv4,
|
|
SourceAddr: src, DestinationAddr: dst,
|
|
}
|
|
if err := h.SetTLVs([]proxyproto.TLV{{Type: 0xEA, Value: []byte{0x01}}}); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
raw, _ := h.Format()
|
|
client, server := net.Pipe()
|
|
go func() { client.Write(raw) }()
|
|
pc := proxyproto.NewConn(server)
|
|
|
|
// Direct *proxyproto.Conn.
|
|
if a := AddrFromConn(pc); a == nil || len(a.RawTLVs()) == 0 {
|
|
t.Fatal("AddrFromConn(*proxyproto.Conn) did not recover TLVs")
|
|
}
|
|
pc.Close()
|
|
}
|
|
|
|
func TestAddrFromConnThroughWrapper(t *testing.T) {
|
|
src := &net.TCPAddr{IP: net.ParseIP("192.168.1.10"), Port: 40000}
|
|
dst := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 53}
|
|
h := &proxyproto.Header{
|
|
Version: 2, Command: proxyproto.PROXY, TransportProtocol: proxyproto.TCPv4,
|
|
SourceAddr: src, DestinationAddr: dst,
|
|
}
|
|
if err := h.SetTLVs([]proxyproto.TLV{{Type: 0xEA, Value: []byte{0x02, 0x03}}}); err != nil {
|
|
t.Fatalf("SetTLVs: %v", err)
|
|
}
|
|
raw, _ := h.Format()
|
|
client, server := net.Pipe()
|
|
go func() { client.Write(raw) }()
|
|
pc := proxyproto.NewConn(server)
|
|
tc := &tlvConn{Conn: pc, pc: pc}
|
|
// Wrap in a conn that only promotes RemoteAddr (like tls.Conn / limiter).
|
|
wrapped := &delegatingConn{Conn: tc}
|
|
|
|
a := AddrFromConn(wrapped)
|
|
if a == nil {
|
|
t.Fatal("AddrFromConn through wrapper returned nil")
|
|
}
|
|
got, err := proxyproto.SplitTLVs(a.RawTLVs())
|
|
if err != nil || len(got) != 1 || !bytes.Equal(got[0].Value, []byte{0x02, 0x03}) {
|
|
t.Fatalf("TLV mismatch through wrapper: %+v err=%v", got, err)
|
|
}
|
|
tc.Close()
|
|
}
|
|
|
|
func TestAddrFromConnNil(t *testing.T) {
|
|
if a := AddrFromConn(nil); a != nil {
|
|
t.Fatalf("AddrFromConn(nil) = %v, want nil", a)
|
|
}
|
|
}
|