mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 10:11:18 +01:00 
			
		
		
		
	The goal is to move more network state accessors to netmon.Monitor where they can be cheaper/cached. But first (this change and others) we need to make sure the one netmon.Monitor is plumbed everywhere. Some notable bits: * tsdial.NewDialer is added, taking a now-required netmon * because a tsdial.Dialer always has a netmon, anything taking both a Dialer and a NetMon is now redundant; take only the Dialer and get the NetMon from that if/when needed. * netmon.NewStatic is added, primarily for tests Updates tailscale/corp#10910 Updates tailscale/corp#18960 Updates #7967 Updates #3299 Change-Id: I877f9cb87618c4eb037cee098241d18da9c01691 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			189 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
package controlclient
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/ed25519"
 | 
						|
	"encoding/json"
 | 
						|
	"net/http"
 | 
						|
	"net/http/httptest"
 | 
						|
	"net/netip"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"tailscale.com/hostinfo"
 | 
						|
	"tailscale.com/ipn/ipnstate"
 | 
						|
	"tailscale.com/net/netmon"
 | 
						|
	"tailscale.com/net/tsdial"
 | 
						|
	"tailscale.com/tailcfg"
 | 
						|
	"tailscale.com/types/key"
 | 
						|
)
 | 
						|
 | 
						|
func TestNewDirect(t *testing.T) {
 | 
						|
	hi := hostinfo.New()
 | 
						|
	ni := tailcfg.NetInfo{LinkType: "wired"}
 | 
						|
	hi.NetInfo = &ni
 | 
						|
 | 
						|
	k := key.NewMachine()
 | 
						|
	opts := Options{
 | 
						|
		ServerURL: "https://example.com",
 | 
						|
		Hostinfo:  hi,
 | 
						|
		GetMachinePrivateKey: func() (key.MachinePrivate, error) {
 | 
						|
			return k, nil
 | 
						|
		},
 | 
						|
		Dialer: tsdial.NewDialer(netmon.NewStatic()),
 | 
						|
	}
 | 
						|
	c, err := NewDirect(opts)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if c.serverURL != opts.ServerURL {
 | 
						|
		t.Errorf("c.serverURL got %v want %v", c.serverURL, opts.ServerURL)
 | 
						|
	}
 | 
						|
 | 
						|
	// hi is stored without its NetInfo field.
 | 
						|
	hiWithoutNi := *hi
 | 
						|
	hiWithoutNi.NetInfo = nil
 | 
						|
	if !hiWithoutNi.Equal(c.hostinfo) {
 | 
						|
		t.Errorf("c.hostinfo got %v want %v", c.hostinfo, hi)
 | 
						|
	}
 | 
						|
 | 
						|
	changed := c.SetNetInfo(&ni)
 | 
						|
	if changed {
 | 
						|
		t.Errorf("c.SetNetInfo(ni) want false got %v", changed)
 | 
						|
	}
 | 
						|
	ni = tailcfg.NetInfo{LinkType: "wifi"}
 | 
						|
	changed = c.SetNetInfo(&ni)
 | 
						|
	if !changed {
 | 
						|
		t.Errorf("c.SetNetInfo(ni) want true got %v", changed)
 | 
						|
	}
 | 
						|
 | 
						|
	changed = c.SetHostinfo(hi)
 | 
						|
	if changed {
 | 
						|
		t.Errorf("c.SetHostinfo(hi) want false got %v", changed)
 | 
						|
	}
 | 
						|
	hi = hostinfo.New()
 | 
						|
	hi.Hostname = "different host name"
 | 
						|
	changed = c.SetHostinfo(hi)
 | 
						|
	if !changed {
 | 
						|
		t.Errorf("c.SetHostinfo(hi) want true got %v", changed)
 | 
						|
	}
 | 
						|
 | 
						|
	endpoints := fakeEndpoints(1, 2, 3)
 | 
						|
	changed = c.newEndpoints(endpoints)
 | 
						|
	if !changed {
 | 
						|
		t.Errorf("c.newEndpoints want true got %v", changed)
 | 
						|
	}
 | 
						|
	changed = c.newEndpoints(endpoints)
 | 
						|
	if changed {
 | 
						|
		t.Errorf("c.newEndpoints want false got %v", changed)
 | 
						|
	}
 | 
						|
	endpoints = fakeEndpoints(4, 5, 6)
 | 
						|
	changed = c.newEndpoints(endpoints)
 | 
						|
	if !changed {
 | 
						|
		t.Errorf("c.newEndpoints want true got %v", changed)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func fakeEndpoints(ports ...uint16) (ret []tailcfg.Endpoint) {
 | 
						|
	for _, port := range ports {
 | 
						|
		ret = append(ret, tailcfg.Endpoint{
 | 
						|
			Addr: netip.AddrPortFrom(netip.Addr{}, port),
 | 
						|
		})
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func TestTsmpPing(t *testing.T) {
 | 
						|
	hi := hostinfo.New()
 | 
						|
	ni := tailcfg.NetInfo{LinkType: "wired"}
 | 
						|
	hi.NetInfo = &ni
 | 
						|
 | 
						|
	k := key.NewMachine()
 | 
						|
	opts := Options{
 | 
						|
		ServerURL: "https://example.com",
 | 
						|
		Hostinfo:  hi,
 | 
						|
		GetMachinePrivateKey: func() (key.MachinePrivate, error) {
 | 
						|
			return k, nil
 | 
						|
		},
 | 
						|
		Dialer: tsdial.NewDialer(netmon.NewStatic()),
 | 
						|
	}
 | 
						|
 | 
						|
	c, err := NewDirect(opts)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	pingRes := &tailcfg.PingResponse{
 | 
						|
		Type:     "TSMP",
 | 
						|
		IP:       "123.456.7890",
 | 
						|
		Err:      "",
 | 
						|
		NodeName: "testnode",
 | 
						|
	}
 | 
						|
 | 
						|
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		defer r.Body.Close()
 | 
						|
		body := new(ipnstate.PingResult)
 | 
						|
		if err := json.NewDecoder(r.Body).Decode(body); err != nil {
 | 
						|
			t.Fatal(err)
 | 
						|
		}
 | 
						|
		if pingRes.IP != body.IP {
 | 
						|
			t.Fatalf("PingResult did not have the correct IP : got %v, expected : %v", body.IP, pingRes.IP)
 | 
						|
		}
 | 
						|
		w.WriteHeader(200)
 | 
						|
	}))
 | 
						|
	defer ts.Close()
 | 
						|
 | 
						|
	now := time.Now()
 | 
						|
 | 
						|
	pr := &tailcfg.PingRequest{
 | 
						|
		URL: ts.URL,
 | 
						|
	}
 | 
						|
 | 
						|
	err = postPingResult(now, t.Logf, c.httpc, pr, pingRes)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestDecodeWrappedAuthkey(t *testing.T) {
 | 
						|
	k, isWrapped, sig, priv := decodeWrappedAuthkey("tskey-32mjsdkdsffds9o87dsfkjlh", nil)
 | 
						|
	if want := "tskey-32mjsdkdsffds9o87dsfkjlh"; k != want {
 | 
						|
		t.Errorf("decodeWrappedAuthkey(<unwrapped-key>).key = %q, want %q", k, want)
 | 
						|
	}
 | 
						|
	if isWrapped {
 | 
						|
		t.Error("decodeWrappedAuthkey(<unwrapped-key>).isWrapped = true, want false")
 | 
						|
	}
 | 
						|
	if sig != nil {
 | 
						|
		t.Errorf("decodeWrappedAuthkey(<unwrapped-key>).sig = %v, want nil", sig)
 | 
						|
	}
 | 
						|
	if priv != nil {
 | 
						|
		t.Errorf("decodeWrappedAuthkey(<unwrapped-key>).priv = %v, want nil", priv)
 | 
						|
	}
 | 
						|
 | 
						|
	k, isWrapped, sig, priv = decodeWrappedAuthkey("tskey-auth-k7UagY1CNTRL-ZZZZZ--TLpAEDA1ggnXuw4/fWnNWUwcoOjLemhOvml1juMl5lhLmY5sBUsj8EWEAfL2gdeD9g8VDw5tgcxCiHGlEb67BgU2DlFzZApi4LheLJraA+pYjTGChVhpZz1iyiBPD+U2qxDQAbM3+WFY0EBlggxmVqG53Hu0Rg+KmHJFMlUhfgzo+AQP6+Kk9GzvJJOs4-k36RdoSFqaoARfQo0UncHAV0t3YTqrkD5r/z2jTrE43GZWobnce7RGD4qYckUyVSF+DOj4BA/r4qT0bO8kk6zg", nil)
 | 
						|
	if want := "tskey-auth-k7UagY1CNTRL-ZZZZZ"; k != want {
 | 
						|
		t.Errorf("decodeWrappedAuthkey(<wrapped-key>).key = %q, want %q", k, want)
 | 
						|
	}
 | 
						|
	if !isWrapped {
 | 
						|
		t.Error("decodeWrappedAuthkey(<wrapped-key>).isWrapped = false, want true")
 | 
						|
	}
 | 
						|
 | 
						|
	if sig == nil {
 | 
						|
		t.Fatal("decodeWrappedAuthkey(<wrapped-key>).sig = nil, want non-nil signature")
 | 
						|
	}
 | 
						|
	sigHash := sig.SigHash()
 | 
						|
	if !ed25519.Verify(sig.KeyID, sigHash[:], sig.Signature) {
 | 
						|
		t.Error("signature failed to verify")
 | 
						|
	}
 | 
						|
 | 
						|
	// Make sure the private is correct by using it.
 | 
						|
	someSig := ed25519.Sign(priv, []byte{1, 2, 3, 4})
 | 
						|
	if !ed25519.Verify(sig.WrappingPubkey, []byte{1, 2, 3, 4}, someSig) {
 | 
						|
		t.Error("failed to use priv")
 | 
						|
	}
 | 
						|
 | 
						|
}
 |