// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause package appc import ( "bytes" "context" "encoding/hex" "io" "net" "net/netip" "strings" "testing" "tailscale.com/net/memnet" ) func echoConnOnce(conn net.Conn) { defer conn.Close() b := make([]byte, 256) n, err := conn.Read(b) if err != nil { return } if _, err := conn.Write(b[:n]); err != nil { return } } func TestTCPRoundRobinHandler(t *testing.T) { h := tcpRoundRobinHandler{ To: []string{"yeet.com"}, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { if network != "tcp" { t.Errorf("network = %s, want %s", network, "tcp") } if addr != "yeet.com:22" { t.Errorf("addr = %s, want %s", addr, "yeet.com:22") } c, s := memnet.NewConn("outbound", 1024) go echoConnOnce(s) return c, nil }, } cSock, sSock := memnet.NewTCPConn(netip.MustParseAddrPort("10.64.1.2:22"), netip.MustParseAddrPort("10.64.1.2:22"), 1024) h.Handle(sSock) // Test data write and read, the other end will echo back // a single stanza want := "hello" if _, err := io.WriteString(cSock, want); err != nil { t.Fatal(err) } got := make([]byte, len(want)) if _, err := io.ReadAtLeast(cSock, got, len(got)); err != nil { t.Fatal(err) } if string(got) != want { t.Errorf("got %q, want %q", got, want) } // The other end closed the socket after the first echo, so // any following read should error. io.WriteString(cSock, "deadass heres some data on god fr") if _, err := io.ReadAtLeast(cSock, got, len(got)); err == nil { t.Error("read succeeded on closed socket") } } // Capture of first TCP data segment for a connection to https://pkgs.tailscale.com const tlsStart = `45000239ff1840004006f9f5c0a801f2 c726b5efcf9e01bbe803b21394e3b752 801801f641dc00000101080ade3474f2 2fb93ee71603010200010001fc030303 c3acbd19d2624765bb19af4bce03365e 1d197f5bb939cdadeff26b0f8e7a0620 295b04127b82bae46aac4ff58cffef25 eba75a4b7a6de729532c411bd9dd0d2c 00203a3a130113021303c02bc02fc02c c030cca9cca8c013c014009c009d002f 003501000193caca0000000a000a0008 1a1a001d001700180010000e000c0268 3208687474702f312e31002b0007062a 2a03040303ff01000100000d00120010 04030804040105030805050108060601 000b00020100002300000033002b0029 1a1a000100001d0020d3c76bef062979 a812ce935cfb4dbe6b3a84dc5ba9226f 23b0f34af9d1d03b4a001b0003020002 00120000446900050003026832000000 170015000012706b67732e7461696c73 63616c652e636f6d002d000201010005 00050100000000001700003a3a000100 0015002d000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0000290094006f0069e76f2016f963ad 38c8632d1f240cd75e00e25fdef295d4 7042b26f3a9a543b1c7dc74939d77803 20527d423ff996997bda2c6383a14f49 219eeef8a053e90a32228df37ddbe126 eccf6b085c93890d08341d819aea6111 0d909f4cd6b071d9ea40618e74588a33 90d494bbb5c3002120d5a164a16c9724 c9ef5e540d8d6f007789a7acf9f5f16f bf6a1907a6782ed02b` func fakeSNIHeader() []byte { b, err := hex.DecodeString(strings.Replace(tlsStart, "\n", "", -1)) if err != nil { panic(err) } return b[0x34:] // trim IP + TCP header } func TestTCPSNIHandler(t *testing.T) { h := tcpSNIHandler{ Allowlist: []string{"pkgs.tailscale.com"}, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { if network != "tcp" { t.Errorf("network = %s, want %s", network, "tcp") } if addr != "pkgs.tailscale.com:443" { t.Errorf("addr = %s, want %s", addr, "pkgs.tailscale.com:443") } c, s := memnet.NewConn("outbound", 1024) go echoConnOnce(s) return c, nil }, } cSock, sSock := memnet.NewTCPConn(netip.MustParseAddrPort("10.64.1.2:22"), netip.MustParseAddrPort("10.64.1.2:443"), 1024) h.Handle(sSock) // Fake a TLS handshake record with an SNI in it. if _, err := cSock.Write(fakeSNIHeader()); err != nil { t.Fatal(err) } // Test read, the other end will echo back // a single stanza, which is at least the beginning of the SNI header. want := fakeSNIHeader()[:5] if _, err := cSock.Write(want); err != nil { t.Fatal(err) } got := make([]byte, len(want)) if _, err := io.ReadAtLeast(cSock, got, len(got)); err != nil { t.Fatal(err) } if !bytes.Equal(got, want) { t.Errorf("got %q, want %q", got, want) } }