@ -56,16 +56,17 @@ type serveHTTPContext struct {
DestPort uint16
}
// serve Listener is the state of host-level net.Listen for a specific (Tailscale IP, serve port)
// local Listener is the state of host-level net.Listen for a specific (Tailscale IP, port)
// combination. If there are two TailscaleIPs (v4 and v6) and three ports being served,
// then there will be six of these active and looping in their Run method.
//
// This is not used in userspace-networking mode.
//
// Most serve traffic is intercepted by netstack. This exists purely for connections
// from the machine itself, as that goes via the kernel, so we need to be in the
// kernel's listening/routing tables.
type serveListener struct {
// localListener is used by tailscale serve (TCP only) as well as the built-in web client.
// Most serve traffic and peer traffic for the web client are intercepted by netstack.
// This listener exists purely for connections from the machine itself, as that goes via the kernel,
// so we need to be in the kernel's listening/routing tables.
type localListener struct {
b * LocalBackend
ap netip . AddrPort
ctx context . Context // valid while listener is desired
@ -73,24 +74,35 @@ type serveListener struct {
logf logger . Logf
bo * backoff . Backoff // for retrying failed Listen calls
handler func ( net . Conn ) error // handler for inbound connections
closeListener syncs . AtomicValue [ func ( ) error ] // Listener's Close method, if any
}
func ( b * LocalBackend ) newServeListener ( ctx context . Context , ap netip . AddrPort , logf logger . Logf ) * serve Listener {
func ( b * LocalBackend ) newServeListener ( ctx context . Context , ap netip . AddrPort , logf logger . Logf ) * local Listener {
ctx , cancel := context . WithCancel ( ctx )
return & serve Listener{
return & local Listener{
b : b ,
ap : ap ,
ctx : ctx ,
cancel : cancel ,
logf : logf ,
handler : func ( conn net . Conn ) error {
srcAddr := conn . RemoteAddr ( ) . ( * net . TCPAddr ) . AddrPort ( )
handler := b . tcpHandlerForServe ( ap . Port ( ) , srcAddr )
if handler == nil {
b . logf ( "[unexpected] local-serve: no handler for %v to port %v" , srcAddr , ap . Port ( ) )
conn . Close ( )
}
return nil
} ,
bo : backoff . NewBackoff ( "serve-listener" , logf , 30 * time . Second ) ,
}
}
// Close cancels the context and closes the listener, if any.
func ( s * serveListener ) Close ( ) error {
func ( s * local Listener) Close ( ) error {
s . cancel ( )
if close , ok := s . closeListener . LoadOk ( ) ; ok {
s . closeListener . Store ( nil )
@ -99,10 +111,10 @@ func (s *serveListener) Close() error {
return nil
}
// Run starts a net.Listen for the serve Listener's address and port.
// Run starts a net.Listen for the local Listener's address and port.
// If unable to listen, it retries with exponential backoff.
// Listen is retried until the context is canceled.
func ( s * serve Listener) Run ( ) {
func ( s * local Listener) Run ( ) {
for {
ip := s . ap . Addr ( )
ipStr := ip . String ( )
@ -115,7 +127,7 @@ func (s *serveListener) Run() {
// a specific interface. Without this hook, the system
// chooses a default interface to bind to.
if err := initListenConfig ( & lc , ip , s . b . prevIfState , s . b . dialer . TUNName ( ) ) ; err != nil {
s . logf ( " serve failed to init listen config %v, backing off: %v", s . ap , err )
s . logf ( " localListener failed to init listen config %v, backing off: %v", s . ap , err )
s . bo . BackOff ( s . ctx , err )
continue
}
@ -138,26 +150,26 @@ func (s *serveListener) Run() {
ln , err := lc . Listen ( s . ctx , tcp4or6 , net . JoinHostPort ( ipStr , fmt . Sprint ( s . ap . Port ( ) ) ) )
if err != nil {
if s . shouldWarnAboutListenError ( err ) {
s . logf ( " serve failed to listen on %v, backing off: %v", s . ap , err )
s . logf ( " localListener failed to listen on %v, backing off: %v", s . ap , err )
}
s . bo . BackOff ( s . ctx , err )
continue
}
s . closeListener . Store ( ln . Close )
s . logf ( " serve listening on %v", s . ap )
err = s . handle Serve ListenersAccept( ln )
s . logf ( " listening on %v", s . ap )
err = s . handle ListenersAccept( ln )
if s . ctx . Err ( ) != nil {
// context canceled, we're done
return
}
if err != nil {
s . logf ( " serve l istener accept error, retrying: %v", err )
s . logf ( " localL istener accept error, retrying: %v", err )
}
}
}
func ( s * serve Listener) shouldWarnAboutListenError ( err error ) bool {
func ( s * local Listener) shouldWarnAboutListenError ( err error ) bool {
if ! s . b . sys . NetMon . Get ( ) . InterfaceState ( ) . HasIP ( s . ap . Addr ( ) ) {
// Machine likely doesn't have IPv6 enabled (or the IP is still being
// assigned). No need to warn. Notably, WSL2 (Issue 6303).
@ -168,23 +180,17 @@ func (s *serveListener) shouldWarnAboutListenError(err error) bool {
return true
}
// handle Serve ListenersAccept accepts connections for the Listener. It calls the
// handle ListenersAccept accepts connections for the Listener. It calls the
// handler in a new goroutine for each accepted connection. This is used to
// handle local "tailscale serve" traffic originating from the machine itself.
func ( s * serveListener ) handleServeListenersAccept ( ln net . Listener ) error {
// handle local "tailscale serve" and web client traffic originating from the
// machine itself.
func ( s * localListener ) handleListenersAccept ( ln net . Listener ) error {
for {
conn , err := ln . Accept ( )
if err != nil {
return err
}
srcAddr := conn . RemoteAddr ( ) . ( * net . TCPAddr ) . AddrPort ( )
handler := s . b . tcpHandlerForServe ( s . ap . Port ( ) , srcAddr )
if handler == nil {
s . b . logf ( "[unexpected] local-serve: no handler for %v to port %v" , srcAddr , s . ap . Port ( ) )
conn . Close ( )
continue
}
go handler ( conn )
go s . handler ( conn )
}
}