@ -45,6 +45,7 @@ import (
"tailscale.com/logtail"
"tailscale.com/net/bakedroots"
"tailscale.com/net/netns"
"tailscale.com/net/tsaddr"
"tailscale.com/net/tsdial"
"tailscale.com/safesocket"
"tailscale.com/tailcfg"
@ -252,6 +253,20 @@ func newIPN(jsConfig js.Value) map[string]any {
}
return jsIPN . dialTLS ( args [ 0 ] . String ( ) , opts )
} ) ,
"setExitNode" : js . FuncOf ( func ( this js . Value , args [ ] js . Value ) any {
if len ( args ) != 1 {
log . Printf ( "Usage: setExitNode(stableNodeID)" )
return nil
}
return jsIPN . setExitNode ( args [ 0 ] . String ( ) )
} ) ,
"setExitNodeEnabled" : js . FuncOf ( func ( this js . Value , args [ ] js . Value ) any {
if len ( args ) != 1 {
log . Printf ( "Usage: setExitNodeEnabled(enabled)" )
return nil
}
return jsIPN . setExitNodeEnabled ( args [ 0 ] . Bool ( ) )
} ) ,
}
}
@ -333,6 +348,8 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
} ,
Online : p . Online ( ) . Clone ( ) ,
TailscaleSSHEnabled : p . Hostinfo ( ) . TailscaleSSHEnabled ( ) ,
ExitNodeOption : tsaddr . ContainsExitRoutes ( p . AllowedIPs ( ) ) ,
StableNodeID : string ( p . StableID ( ) ) ,
}
} ) ,
LockedOut : nm . TKAEnabled && nm . SelfNode . KeySignature ( ) . Len ( ) == 0 ,
@ -343,6 +360,9 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
log . Printf ( "Could not generate JSON netmap: %v" , err )
}
}
if n . Prefs . Valid ( ) {
jsCallbacks . Call ( "notifyExitNode" , string ( n . Prefs . ExitNodeID ( ) ) )
}
if n . BrowseToURL != nil {
jsCallbacks . Call ( "notifyBrowseToURL" , * n . BrowseToURL )
}
@ -576,6 +596,24 @@ func (i *jsIPN) fetch(url string) js.Value {
} )
}
func ( i * jsIPN ) setExitNode ( stableNodeID string ) js . Value {
return makePromise ( func ( ) ( any , error ) {
mp := & ipn . MaskedPrefs {
ExitNodeIDSet : true ,
Prefs : ipn . Prefs { ExitNodeID : tailcfg . StableNodeID ( stableNodeID ) } ,
}
_ , err := i . lb . EditPrefs ( mp )
return nil , err
} )
}
func ( i * jsIPN ) setExitNodeEnabled ( enabled bool ) js . Value {
return makePromise ( func ( ) ( any , error ) {
_ , err := i . lb . SetUseExitNodeEnabled ( ipnauth . Self , enabled )
return nil , err
} )
}
func ( i * jsIPN ) dial ( network , addr string ) js . Value {
return makePromise ( func ( ) ( any , error ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 30 * time . Second )
@ -875,8 +913,10 @@ type jsNetMapSelfNode struct {
type jsNetMapPeerNode struct {
jsNetMapNode
Online * bool ` json:"online,omitempty" `
TailscaleSSHEnabled bool ` json:"tailscaleSSHEnabled" `
Online * bool ` json:"online,omitempty" `
TailscaleSSHEnabled bool ` json:"tailscaleSSHEnabled" `
ExitNodeOption bool ` json:"exitNodeOption" `
StableNodeID string ` json:"stableNodeID" `
}
type jsStateStore struct {