1+ // SPDX-License-Identifier: BSD-3-Clause, CC0-1.0
12// Package tsserver implements the Tailscale coordination protocol for a single
2- // client. Heavy inspiration was taken from https://github.com/juanfont/headscale
3+ // client. Heavy inspiration and code was taken from https://github.com/juanfont/headscale.
4+ // As such, this file is dual licensed under BSD-3-Clause and CC0-1.0.
35package tsserver
46
57import (
@@ -22,9 +24,7 @@ import (
2224 "sync/atomic"
2325 "time"
2426
25- "github.com/coder/wush/overlay"
2627 "github.com/go-chi/chi/v5"
27- "github.com/google/uuid"
2828 "github.com/klauspost/compress/zstd"
2929 "github.com/puzpuzpuz/xsync/v3"
3030 "github.com/valyala/fasthttp/fasthttputil"
@@ -39,6 +39,8 @@ import (
3939 "tailscale.com/types/key"
4040 "tailscale.com/types/opt"
4141 "tailscale.com/types/ptr"
42+
43+ "github.com/coder/wush/overlay"
4244)
4345
4446func DERPMapTailscale (ctx context.Context ) (* tailcfg.DERPMap , error ) {
@@ -242,7 +244,8 @@ func (s *server) NoiseUpgradeHandler(w http.ResponseWriter, r *http.Request) {
242244 logger : s .logger ,
243245 derpMap : s .derpMap ,
244246 challenge : key .NewChallenge (),
245- updates : s .peerMapUpdate ,
247+ peers : xsync .NewMapOf [tailcfg.NodeID , * tailcfg.Node ](),
248+ peerUpdate : s .peerMapUpdate ,
246249 node : & s .node ,
247250 nodeUpdate : s .nodeUpdate ,
248251 getIP : s .overlay .IP ,
@@ -332,7 +335,9 @@ type noiseServer struct {
332335 derpMap * tailcfg.DERPMap
333336 getIP func () netip.Addr
334337
335- updates chan update
338+ peers * xsync.MapOf [tailcfg.NodeID , * tailcfg.Node ]
339+ peerUpdate chan update
340+
336341 node * atomic.Pointer [tailcfg.Node ]
337342 nodeUpdate chan struct {}
338343
@@ -341,37 +346,6 @@ type noiseServer struct {
341346 protocolVersion int
342347}
343348
344- func maskUUID (uid uuid.UUID ) uuid.UUID {
345- // This is Tailscale's ephemeral service prefix. This can be changed easily
346- // later-on, because all of our nodes are ephemeral.
347- // fd7a:115c:a1e0
348- uid [0 ] = 0xfd
349- uid [1 ] = 0x7a
350- uid [2 ] = 0x11
351- uid [3 ] = 0x5c
352- uid [4 ] = 0xa1
353- uid [5 ] = 0xe0
354- return uid
355- }
356-
357- // IP generates a random IP with a static service prefix.
358- func IP () netip.Addr {
359- uid := maskUUID (uuid .New ())
360- return netip .AddrFrom16 (uid )
361- }
362-
363- // func IP4r() netip.Addr {
364- // return netip.AddrFrom4([4]byte{100, 64, 1, 1})
365- // }
366- // func IP4s() netip.Addr {
367- // return netip.AddrFrom4([4]byte{100, 64, 2, 2})
368- // }
369-
370- // IP generates a new IP from a UUID.
371- func IPFromUUID (uid uuid.UUID ) netip.Addr {
372- return netip .AddrFrom16 (maskUUID (uid ))
373- }
374-
375349func (ns * noiseServer ) notifyUpdate () {
376350 ns .nodeUpdate <- struct {}{}
377351}
@@ -389,17 +363,6 @@ func (ns *noiseServer) NoiseRegistrationHandler(w http.ResponseWriter, r *http.R
389363 sp := strings .SplitN (registerRequest .Auth .AuthKey , "-" , 2 )
390364
391365 ip := ns .getIP ()
392- // var ip netip.Addr
393- // switch registerRequest.Auth.AuthKey {
394- // case "receive":
395- // ip = IP4r()
396- // case "send":
397- // ip = IP4s()
398- // default:
399- // http.Error(w, fmt.Sprintf("unknown authkey: %q", registerRequest.Auth.AuthKey), http.StatusBadRequest)
400- // return
401- // }
402- // try insecureskipverify
403366
404367 resp := tailcfg.RegisterResponse {}
405368 resp .MachineAuthorized = true
@@ -506,17 +469,21 @@ func (ns *noiseServer) NoisePollNetMapHandler(
506469func (ns * noiseServer ) peerMap () []* tailcfg.Node {
507470 peers := []* tailcfg.Node {}
508471
509- // TOOD: get peers
472+ ns .peers .Range (func (key tailcfg.NodeID , value * tailcfg.Node ) bool {
473+ peers = append (peers , value .Clone ())
474+ return true
475+ })
476+ xslices .SortFunc (peers , func (a , b * tailcfg.Node ) int {
477+ return cmp .Compare (a .ID , b .ID )
478+ })
510479
511480 return peers
512481}
513482
514483func (ns * noiseServer ) handleStreaming (ctx context.Context , w http.ResponseWriter , req * tailcfg.MapRequest ) {
515- // Upgrade the writer to a ResponseController
516484 rc := http .NewResponseController (w )
517-
518- // Longpolling will break if there is a write timeout,
519- // so it needs to be disabled.
485+ // Longpolling will break if there is a write timeout, so it needs to be
486+ // disabled.
520487 rc .SetWriteDeadline (time.Time {})
521488
522489 node := ns .getSelfNode ()
@@ -552,13 +519,14 @@ func (ns *noiseServer) handleStreaming(ctx context.Context, w http.ResponseWrite
552519 select {
553520 case <- ctx .Done ():
554521 return
555- case upd := <- ns .updates :
522+ case upd := <- ns .peerUpdate :
556523 res := & tailcfg.MapResponse {
557524 KeepAlive : false ,
558525 ControlTime : ptr .To (time .Now ()),
559526 }
560527 if upd .ty == updateTypeNewPeer {
561- res .Peers = []* tailcfg.Node {upd .node }
528+ ns .peers .Store (upd .node .ID , upd .node .Clone ())
529+ res .Peers = ns .peerMap ()
562530 } else if upd .ty == updateTypePeerUpdate {
563531 res .PeersChangedPatch = []* tailcfg.PeerChange {upd .update }
564532 }
@@ -590,7 +558,6 @@ func (ns *noiseServer) handleStreaming(ctx context.Context, w http.ResponseWrite
590558 }
591559 }
592560 }
593-
594561}
595562
596563func writeMapResponse (w http.ResponseWriter , req * tailcfg.MapRequest , res * tailcfg.MapResponse ) error {
@@ -751,7 +718,6 @@ func peerChange(req *tailcfg.MapRequest, node *tailcfg.Node) tailcfg.PeerChange
751718 }
752719 }
753720
754- // TODO(kradalby): Find a good way to compare updates
755721 ret .Endpoints = req .Endpoints
756722
757723 ret .LastSeen = ptr .To (time .Now ())
@@ -831,6 +797,8 @@ const (
831797 earlyPayloadMagic = "\xff \xff \xff TS"
832798)
833799
800+ var _ = (* noiseServer )(nil ).earlyNoise
801+
834802func (ns * noiseServer ) earlyNoise (protocolVersion int , writer io.Writer ) error {
835803 ns .logger .Info ("early noise" )
836804 if protocolVersion < earlyNoiseCapabilityVersion {
0 commit comments