|
|
@ -156,13 +156,20 @@ func TestTwoNodes(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
|
|
// Create two nodes:
|
|
|
|
// Create two nodes:
|
|
|
|
n1 := newTestNode(t, env)
|
|
|
|
n1 := newTestNode(t, env)
|
|
|
|
|
|
|
|
n1SocksAddrCh := n1.socks5AddrChan()
|
|
|
|
d1 := n1.StartDaemon(t)
|
|
|
|
d1 := n1.StartDaemon(t)
|
|
|
|
defer d1.Kill()
|
|
|
|
defer d1.Kill()
|
|
|
|
|
|
|
|
|
|
|
|
n2 := newTestNode(t, env)
|
|
|
|
n2 := newTestNode(t, env)
|
|
|
|
|
|
|
|
n2SocksAddrCh := n2.socks5AddrChan()
|
|
|
|
d2 := n2.StartDaemon(t)
|
|
|
|
d2 := n2.StartDaemon(t)
|
|
|
|
defer d2.Kill()
|
|
|
|
defer d2.Kill()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
n1Socks := n1.AwaitSocksAddr(t, n1SocksAddrCh)
|
|
|
|
|
|
|
|
n2Socks := n1.AwaitSocksAddr(t, n2SocksAddrCh)
|
|
|
|
|
|
|
|
t.Logf("node1 SOCKS5 addr: %v", n1Socks)
|
|
|
|
|
|
|
|
t.Logf("node2 SOCKS5 addr: %v", n2Socks)
|
|
|
|
|
|
|
|
|
|
|
|
n1.AwaitListening(t)
|
|
|
|
n1.AwaitListening(t)
|
|
|
|
n2.AwaitListening(t)
|
|
|
|
n2.AwaitListening(t)
|
|
|
|
n1.MustUp()
|
|
|
|
n1.MustUp()
|
|
|
@ -358,6 +365,9 @@ type testNode struct {
|
|
|
|
dir string // temp dir for sock & state
|
|
|
|
dir string // temp dir for sock & state
|
|
|
|
sockFile string
|
|
|
|
sockFile string
|
|
|
|
stateFile string
|
|
|
|
stateFile string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
|
|
|
onLogLine []func([]byte)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// newTestNode allocates a temp directory for a new test node.
|
|
|
|
// newTestNode allocates a temp directory for a new test node.
|
|
|
@ -372,6 +382,85 @@ func newTestNode(t *testing.T, env *testEnv) *testNode {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// addLogLineHook registers a hook f to be called on each tailscaled
|
|
|
|
|
|
|
|
// log line output.
|
|
|
|
|
|
|
|
func (n *testNode) addLogLineHook(f func([]byte)) {
|
|
|
|
|
|
|
|
n.mu.Lock()
|
|
|
|
|
|
|
|
defer n.mu.Unlock()
|
|
|
|
|
|
|
|
n.onLogLine = append(n.onLogLine, f)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// socks5AddrChan returns a channel that receives the address (e.g. "localhost:23874")
|
|
|
|
|
|
|
|
// of the node's SOCKS5 listener, once started.
|
|
|
|
|
|
|
|
func (n *testNode) socks5AddrChan() <-chan string {
|
|
|
|
|
|
|
|
ch := make(chan string, 1)
|
|
|
|
|
|
|
|
n.addLogLineHook(func(line []byte) {
|
|
|
|
|
|
|
|
const sub = "SOCKS5 listening on "
|
|
|
|
|
|
|
|
i := mem.Index(mem.B(line), mem.S(sub))
|
|
|
|
|
|
|
|
if i == -1 {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
addr := string(line)[i+len(sub):]
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
|
|
|
case ch <- addr:
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
return ch
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (n *testNode) AwaitSocksAddr(t testing.TB, ch <-chan string) string {
|
|
|
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
timer := time.NewTimer(10 * time.Second)
|
|
|
|
|
|
|
|
defer timer.Stop()
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
|
|
|
case v := <-ch:
|
|
|
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
case <-timer.C:
|
|
|
|
|
|
|
|
t.Fatal("timeout waiting for node to log its SOCK5 listening address")
|
|
|
|
|
|
|
|
panic("unreachable")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// nodeOutputParser parses stderr of tailscaled processes, calling the
|
|
|
|
|
|
|
|
// per-line callbacks previously registered via
|
|
|
|
|
|
|
|
// testNode.addLogLineHook.
|
|
|
|
|
|
|
|
type nodeOutputParser struct {
|
|
|
|
|
|
|
|
buf bytes.Buffer
|
|
|
|
|
|
|
|
n *testNode
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (op *nodeOutputParser) Write(p []byte) (n int, err error) {
|
|
|
|
|
|
|
|
n, err = op.buf.Write(p)
|
|
|
|
|
|
|
|
op.parseLines()
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (op *nodeOutputParser) parseLines() {
|
|
|
|
|
|
|
|
n := op.n
|
|
|
|
|
|
|
|
buf := op.buf.Bytes()
|
|
|
|
|
|
|
|
for len(buf) > 0 {
|
|
|
|
|
|
|
|
nl := bytes.IndexByte(buf, '\n')
|
|
|
|
|
|
|
|
if nl == -1 {
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
line := buf[:nl+1]
|
|
|
|
|
|
|
|
buf = buf[nl+1:]
|
|
|
|
|
|
|
|
lineTrim := bytes.TrimSpace(line)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
n.mu.Lock()
|
|
|
|
|
|
|
|
for _, f := range n.onLogLine {
|
|
|
|
|
|
|
|
f(lineTrim)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
n.mu.Unlock()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(buf) == 0 {
|
|
|
|
|
|
|
|
op.buf.Reset()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
io.CopyN(ioutil.Discard, &op.buf, int64(op.buf.Len()-len(buf)))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Daemon struct {
|
|
|
|
type Daemon struct {
|
|
|
|
Process *os.Process
|
|
|
|
Process *os.Process
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -398,15 +487,17 @@ func (n *testNode) StartDaemon(t testing.TB) *Daemon {
|
|
|
|
"--tun=userspace-networking",
|
|
|
|
"--tun=userspace-networking",
|
|
|
|
"--state="+n.stateFile,
|
|
|
|
"--state="+n.stateFile,
|
|
|
|
"--socket="+n.sockFile,
|
|
|
|
"--socket="+n.sockFile,
|
|
|
|
|
|
|
|
"--socks5-server=localhost:0",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
cmd.Env = append(os.Environ(),
|
|
|
|
cmd.Env = append(os.Environ(),
|
|
|
|
"TS_LOG_TARGET="+n.env.LogCatcherServer.URL,
|
|
|
|
"TS_LOG_TARGET="+n.env.LogCatcherServer.URL,
|
|
|
|
"HTTP_PROXY="+n.env.TrafficTrapServer.URL,
|
|
|
|
"HTTP_PROXY="+n.env.TrafficTrapServer.URL,
|
|
|
|
"HTTPS_PROXY="+n.env.TrafficTrapServer.URL,
|
|
|
|
"HTTPS_PROXY="+n.env.TrafficTrapServer.URL,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
cmd.Stderr = &nodeOutputParser{n: n}
|
|
|
|
if *verboseTailscaled {
|
|
|
|
if *verboseTailscaled {
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stdout
|
|
|
|
cmd.Stderr = io.MultiWriter(cmd.Stderr, os.Stderr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
t.Fatalf("starting tailscaled: %v", err)
|
|
|
|
t.Fatalf("starting tailscaled: %v", err)
|
|
|
|