|
|
|
|
@ -22,7 +22,9 @@ import ( |
|
|
|
|
"strings" |
|
|
|
|
|
|
|
|
|
"inet.af/netaddr" |
|
|
|
|
"tailscale.com/ipn" |
|
|
|
|
"tailscale.com/net/interfaces" |
|
|
|
|
"tailscale.com/syncs" |
|
|
|
|
"tailscale.com/tailcfg" |
|
|
|
|
"tailscale.com/wgengine" |
|
|
|
|
) |
|
|
|
|
@ -30,10 +32,52 @@ import ( |
|
|
|
|
var initListenConfig func(*net.ListenConfig, netaddr.IP, *interfaces.State, string) error |
|
|
|
|
|
|
|
|
|
type peerAPIServer struct { |
|
|
|
|
b *LocalBackend |
|
|
|
|
rootDir string |
|
|
|
|
tunName string |
|
|
|
|
selfNode *tailcfg.Node |
|
|
|
|
b *LocalBackend |
|
|
|
|
rootDir string |
|
|
|
|
tunName string |
|
|
|
|
selfNode *tailcfg.Node |
|
|
|
|
knownEmpty syncs.AtomicBool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const partialSuffix = ".tspartial" |
|
|
|
|
|
|
|
|
|
// hasFilesWaiting reports whether any files are buffered in the
|
|
|
|
|
// tailscaled daemon storage.
|
|
|
|
|
func (s *peerAPIServer) hasFilesWaiting() bool { |
|
|
|
|
if s.rootDir == "" { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
if s.knownEmpty.Get() { |
|
|
|
|
// Optimization: this is usually empty, so avoid opening
|
|
|
|
|
// the directory and checking. We can't cache the actual
|
|
|
|
|
// has-files-or-not values as the macOS/iOS client might
|
|
|
|
|
// in the future use+delete the files directly. So only
|
|
|
|
|
// keep this negative cache.
|
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
f, err := os.Open(s.rootDir) |
|
|
|
|
if err != nil { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
defer f.Close() |
|
|
|
|
for { |
|
|
|
|
des, err := f.ReadDir(10) |
|
|
|
|
for _, de := range des { |
|
|
|
|
if strings.HasSuffix(de.Name(), partialSuffix) { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
if de.Type().IsRegular() { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if err == io.EOF { |
|
|
|
|
s.knownEmpty.Set(true) |
|
|
|
|
} |
|
|
|
|
if err != nil { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *peerAPIServer) listen(ip netaddr.IP, ifState *interfaces.State) (ln net.Listener, err error) { |
|
|
|
|
@ -221,7 +265,7 @@ func (h *peerAPIHandler) put(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
name := path.Base(r.URL.Path) |
|
|
|
|
if name == "." || name == "/" { |
|
|
|
|
if name == "." || name == "/" || strings.HasSuffix(name, partialSuffix) { |
|
|
|
|
http.Error(w, "bad filename", http.StatusForbidden) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
@ -258,4 +302,6 @@ func (h *peerAPIHandler) put(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
// TODO: some real response
|
|
|
|
|
success = true |
|
|
|
|
io.WriteString(w, "{}\n") |
|
|
|
|
h.ps.knownEmpty.Set(false) |
|
|
|
|
h.ps.b.send(ipn.Notify{}) // it will set FilesWaiting
|
|
|
|
|
} |
|
|
|
|
|