Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd9c9f6844 |
@@ -361,6 +361,13 @@ func newIPN(jsConfig js.Value) map[string]any {
|
||||
"suggestExitNode": js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
return jsIPN.suggestExitNode()
|
||||
}),
|
||||
"setServices": js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
if len(args) != 1 {
|
||||
log.Printf("Usage: setServices(services)")
|
||||
return nil
|
||||
}
|
||||
return jsIPN.setServices(args[0])
|
||||
}),
|
||||
"localAPI": js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
if len(args) < 2 {
|
||||
log.Printf("Usage: localAPI(method, path[, body])")
|
||||
@@ -467,6 +474,7 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
|
||||
NodeKey: nm.NodeKey.String(),
|
||||
MachineKey: nm.MachineKey.String(),
|
||||
PeerAPIURL: selfPeerAPIURL,
|
||||
Services: userServicesFromView(nm.SelfNode.Hostinfo().Services()),
|
||||
},
|
||||
MachineStatus: jsMachineStatus[nm.GetMachineStatus()],
|
||||
},
|
||||
@@ -516,6 +524,7 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
|
||||
MachineKey: p.Machine().String(),
|
||||
NodeKey: p.Key().String(),
|
||||
PeerAPIURL: peerURL,
|
||||
Services: userServicesFromView(p.Hostinfo().Services()),
|
||||
},
|
||||
Online: p.Online().Clone(),
|
||||
TailscaleSSHEnabled: p.Hostinfo().TailscaleSSHEnabled(),
|
||||
@@ -1328,6 +1337,39 @@ func (i *jsIPN) suggestExitNode() js.Value {
|
||||
})
|
||||
}
|
||||
|
||||
func (i *jsIPN) setServices(jsServices js.Value) js.Value {
|
||||
return makePromise(func() (any, error) {
|
||||
n := jsServices.Length()
|
||||
svcs := make([]tailcfg.Service, 0, n)
|
||||
for idx := range n {
|
||||
s := jsServices.Index(idx)
|
||||
proto := tailcfg.ServiceProto(s.Get("proto").String())
|
||||
port := uint16(s.Get("port").Int())
|
||||
var desc string
|
||||
if d := s.Get("description"); d.Type() == js.TypeString {
|
||||
desc = d.String()
|
||||
}
|
||||
svcs = append(svcs, tailcfg.Service{Proto: proto, Port: port, Description: desc})
|
||||
}
|
||||
i.lb.SetExplicitServices(svcs)
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
|
||||
// userServicesFromView converts a hostinfo services slice to jsService entries,
|
||||
// filtering out internal peerapi protocol entries (already reflected in peerAPIURL).
|
||||
func userServicesFromView(svcs views.Slice[tailcfg.Service]) []jsService {
|
||||
var out []jsService
|
||||
for _, s := range svcs.All() {
|
||||
switch s.Proto {
|
||||
case tailcfg.PeerAPI4, tailcfg.PeerAPI6, tailcfg.PeerAPIDNS:
|
||||
continue
|
||||
}
|
||||
out = append(out, jsService{Proto: string(s.Proto), Port: s.Port, Description: s.Description})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (i *jsIPN) localAPI(method, path, body string) js.Value {
|
||||
return makePromise(func() (any, error) {
|
||||
h := localapi.NewHandler(localapi.HandlerConfig{
|
||||
@@ -1564,12 +1606,19 @@ type jsNetMap struct {
|
||||
LockedOut bool `json:"lockedOut"`
|
||||
}
|
||||
|
||||
type jsService struct {
|
||||
Proto string `json:"proto"`
|
||||
Port uint16 `json:"port"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
type jsNetMapNode struct {
|
||||
Name string `json:"name"`
|
||||
Addresses []string `json:"addresses"`
|
||||
MachineKey string `json:"machineKey"`
|
||||
NodeKey string `json:"nodeKey"`
|
||||
PeerAPIURL string `json:"peerAPIURL,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Addresses []string `json:"addresses"`
|
||||
MachineKey string `json:"machineKey"`
|
||||
NodeKey string `json:"nodeKey"`
|
||||
PeerAPIURL string `json:"peerAPIURL,omitempty"`
|
||||
Services []jsService `json:"services,omitempty"`
|
||||
}
|
||||
|
||||
type jsNetMapSelfNode struct {
|
||||
|
||||
+21
-1
@@ -294,6 +294,7 @@ type LocalBackend struct {
|
||||
capTailnetLock bool // whether netMap contains the tailnet lock capability
|
||||
// hostinfo is mutated in-place while mu is held.
|
||||
hostinfo *tailcfg.Hostinfo // TODO(nickkhyl): move to nodeBackend
|
||||
explicitServices []tailcfg.Service // services set explicitly via SetExplicitServices; always uploaded
|
||||
nmExpiryTimer tstime.TimerController // for updating netMap on node expiry; can be nil; TODO(nickkhyl): move to nodeBackend
|
||||
activeLogin string // last logged LoginName from netMap; TODO(nickkhyl): move to nodeBackend (or remove? it's in [ipn.LoginProfile]).
|
||||
engineStatus ipn.EngineStatus
|
||||
@@ -4967,6 +4968,23 @@ func (b *LocalBackend) setPortlistServices(sl []tailcfg.Service) {
|
||||
b.doSetHostinfoFilterServices()
|
||||
}
|
||||
|
||||
// SetExplicitServices sets the services this node advertises on the netmap.
|
||||
// Unlike the OS port-scan path (setPortlistServices), services set here are
|
||||
// always uploaded to the control server regardless of the ShouldUploadServices
|
||||
// hook — suitable for environments like browser WASM where OS port scanning is
|
||||
// unavailable and services are declared programmatically.
|
||||
func (b *LocalBackend) SetExplicitServices(sl []tailcfg.Service) {
|
||||
b.mu.Lock()
|
||||
if b.hostinfo == nil {
|
||||
b.hostinfo = new(tailcfg.Hostinfo)
|
||||
}
|
||||
b.hostinfo.Services = sl
|
||||
b.explicitServices = sl
|
||||
b.mu.Unlock()
|
||||
|
||||
b.doSetHostinfoFilterServices()
|
||||
}
|
||||
|
||||
// doSetHostinfoFilterServices calls SetHostinfo on the controlclient,
|
||||
// possibly after mangling the given hostinfo.
|
||||
//
|
||||
@@ -5011,7 +5029,9 @@ func (b *LocalBackend) hostInfoWithServicesLocked() *tailcfg.Hostinfo {
|
||||
// Make a shallow copy of hostinfo so we can mutate
|
||||
// at the Service field.
|
||||
if f, ok := b.extHost.Hooks().ShouldUploadServices.GetOk(); !ok || !f() {
|
||||
hi.Services = []tailcfg.Service{}
|
||||
if len(b.explicitServices) == 0 {
|
||||
hi.Services = []tailcfg.Service{}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't mutate hi.Service's underlying array. Append to
|
||||
|
||||
Reference in New Issue
Block a user