ce7cb702218693d08ea6d83464d2457c9250027c
This commit is contained in:
commit
34c5f8963c
1 changed files with 145336 additions and 0 deletions
245
services/oci-l4-relay/proxy_test.go
Normal file
245
services/oci-l4-relay/proxy_test.go
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type proxyRecord struct {
|
||||
header []byte
|
||||
payload []byte
|
||||
peer net.IP
|
||||
}
|
||||
|
||||
func TestMailTarget(t *testing.T) {
|
||||
tunnel, err := parseCIDR("10.153.238.225/30")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
target, upstream, err := mailProxyTarget("stalwart-relay.stalwart.svc.cluster.local", tunnel)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !target.address.Equal(tunnel.IP) || upstream != "stalwart-relay.stalwart.svc.cluster.local" {
|
||||
t.Fatalf("unexpected proxy target: %#v %q", target, upstream)
|
||||
}
|
||||
if len(target.ports) != 3 || target.ports[0] != 25 || target.ports[1] != 465 || target.ports[2] != 993 {
|
||||
t.Fatalf("unexpected proxy ports: %v", target.ports)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMailProxyTCP(t *testing.T) {
|
||||
upstream := listenTCP(t)
|
||||
listener := listenTCP(t)
|
||||
failures := make(chan error, 1)
|
||||
proxy := newMailProxy(listener, upstream.Addr().String(), net.ParseIP("127.0.0.2"), failures, make(chan struct{}, mailConnectionLimit))
|
||||
go proxy.serve()
|
||||
defer proxy.close()
|
||||
records := make(chan proxyRecord, 1)
|
||||
go func() {
|
||||
connection, err := upstream.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer connection.Close()
|
||||
header := make([]byte, 28)
|
||||
if _, err := io.ReadFull(connection, header); err != nil {
|
||||
return
|
||||
}
|
||||
payload := make([]byte, 4)
|
||||
if _, err := io.ReadFull(connection, payload); err != nil {
|
||||
return
|
||||
}
|
||||
records <- proxyRecord{header: header, payload: payload, peer: connection.RemoteAddr().(*net.TCPAddr).IP}
|
||||
_, _ = connection.Write([]byte("pong"))
|
||||
}()
|
||||
client, err := net.DialTCP("tcp4", nil, listener.Addr().(*net.TCPAddr))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
if _, err := client.Write([]byte("ping")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reply := make([]byte, 4)
|
||||
if _, err := io.ReadFull(client, reply); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(reply) != "pong" {
|
||||
t.Fatalf("reply = %q", reply)
|
||||
}
|
||||
select {
|
||||
case record := <-records:
|
||||
assertProxyRecord(t, record, client.LocalAddr().(*net.TCPAddr), listener.Addr().(*net.TCPAddr))
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("upstream did not receive the proxied stream")
|
||||
}
|
||||
select {
|
||||
case err := <-failures:
|
||||
t.Fatalf("unexpected proxy failure: %v", err)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func TestMailDialFailure(t *testing.T) {
|
||||
unavailable := listenTCP(t)
|
||||
upstream := unavailable.Addr().String()
|
||||
_ = unavailable.Close()
|
||||
listener := listenTCP(t)
|
||||
failures := make(chan error, 1)
|
||||
proxy := newMailProxy(listener, upstream, nil, failures, make(chan struct{}, mailConnectionLimit))
|
||||
go proxy.serve()
|
||||
defer proxy.close()
|
||||
client, err := net.Dial("tcp4", listener.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
if err := client.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := client.Read(make([]byte, 1)); err == nil {
|
||||
t.Fatal("client remained open after the upstream refusal")
|
||||
}
|
||||
select {
|
||||
case err := <-failures:
|
||||
t.Fatalf("connection-level dial failure was reported as relay-fatal: %v", err)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func TestMailProxyClose(t *testing.T) {
|
||||
upstream := listenTCP(t)
|
||||
listener := listenTCP(t)
|
||||
proxy := newMailProxy(listener, upstream.Addr().String(), nil, make(chan error, 1), make(chan struct{}, mailConnectionLimit))
|
||||
go proxy.serve()
|
||||
accepted := make(chan net.Conn, 1)
|
||||
go func() {
|
||||
connection, err := upstream.Accept()
|
||||
if err == nil {
|
||||
accepted <- connection
|
||||
}
|
||||
}()
|
||||
client, err := net.Dial("tcp4", listener.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
var server net.Conn
|
||||
select {
|
||||
case server = <-accepted:
|
||||
defer server.Close()
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("proxy did not open the upstream stream")
|
||||
}
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
proxy.close()
|
||||
close(closed)
|
||||
}()
|
||||
select {
|
||||
case <-closed:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("proxy shutdown did not close active streams")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMailProxyConnectionLimit(t *testing.T) {
|
||||
upstream := listenTCP(t)
|
||||
listener := listenTCP(t)
|
||||
slots := make(chan struct{}, 1)
|
||||
proxy := newMailProxy(listener, upstream.Addr().String(), nil, make(chan error, 1), slots)
|
||||
go proxy.serve()
|
||||
defer proxy.close()
|
||||
accepted := make(chan net.Conn, 2)
|
||||
go func() {
|
||||
for {
|
||||
connection, err := upstream.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
accepted <- connection
|
||||
}
|
||||
}()
|
||||
first, err := net.Dial("tcp4", listener.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer first.Close()
|
||||
firstUpstream := waitForAccepted(t, accepted)
|
||||
defer firstUpstream.Close()
|
||||
second, err := net.Dial("tcp4", listener.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer second.Close()
|
||||
if err := second.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := second.Read(make([]byte, 1)); err == nil {
|
||||
t.Fatal("over-limit connection remained open")
|
||||
}
|
||||
select {
|
||||
case connection := <-accepted:
|
||||
_ = connection.Close()
|
||||
t.Fatal("over-limit connection reached the upstream")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
}
|
||||
_ = first.Close()
|
||||
_ = firstUpstream.Close()
|
||||
deadline := time.Now().Add(time.Second)
|
||||
for len(slots) != 0 && time.Now().Before(deadline) {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if len(slots) != 0 {
|
||||
t.Fatal("connection slot was not released")
|
||||
}
|
||||
third, err := net.Dial("tcp4", listener.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer third.Close()
|
||||
thirdUpstream := waitForAccepted(t, accepted)
|
||||
_ = thirdUpstream.Close()
|
||||
}
|
||||
|
||||
func listenTCP(t *testing.T) *net.TCPListener {
|
||||
t.Helper()
|
||||
listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.ParseIP("127.0.0.1")})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = listener.Close() })
|
||||
return listener
|
||||
}
|
||||
|
||||
func waitForAccepted(t *testing.T, connections <-chan net.Conn) net.Conn {
|
||||
t.Helper()
|
||||
select {
|
||||
case connection := <-connections:
|
||||
return connection
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("proxy did not open the upstream stream")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func assertProxyRecord(t *testing.T, record proxyRecord, source, destination *net.TCPAddr) {
|
||||
t.Helper()
|
||||
if len(record.header) != 28 || !bytes.Equal(record.header[:12], proxySignature) || record.header[12] != 0x21 || record.header[13] != 0x11 || binary.BigEndian.Uint16(record.header[14:16]) != 12 {
|
||||
t.Fatalf("invalid PROXY header: %x", record.header)
|
||||
}
|
||||
if !bytes.Equal(record.header[16:20], source.IP.To4()) || !bytes.Equal(record.header[20:24], destination.IP.To4()) || binary.BigEndian.Uint16(record.header[24:26]) != uint16(source.Port) || binary.BigEndian.Uint16(record.header[26:28]) != uint16(destination.Port) {
|
||||
t.Fatalf("unexpected PROXY addresses: %x", record.header)
|
||||
}
|
||||
if string(record.payload) != "ping" {
|
||||
t.Fatalf("payload = %q", record.payload)
|
||||
}
|
||||
if !record.peer.Equal(net.ParseIP("127.0.0.2")) {
|
||||
t.Fatalf("upstream peer = %s", record.peer)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue