unixsocket.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !windows && !js
  4. package safesocket
  5. import (
  6. "errors"
  7. "fmt"
  8. "log"
  9. "net"
  10. "os"
  11. "os/exec"
  12. "path/filepath"
  13. "runtime"
  14. )
  15. func connect(s *ConnectionStrategy) (net.Conn, error) {
  16. if runtime.GOOS == "js" {
  17. return nil, errors.New("safesocket.Connect not yet implemented on js/wasm")
  18. }
  19. return net.Dial("unix", s.path)
  20. }
  21. func listen(path string) (net.Listener, error) {
  22. // Unix sockets hang around in the filesystem even after nobody
  23. // is listening on them. (Which is really unfortunate but long-
  24. // entrenched semantics.) Try connecting first; if it works, then
  25. // the socket is still live, so let's not replace it. If it doesn't
  26. // work, then replace it.
  27. //
  28. // Note that there's a race condition between these two steps. A
  29. // "proper" daemon usually uses a dance involving pidfiles to first
  30. // ensure that no other instances of itself are running, but that's
  31. // beyond the scope of our simple socket library.
  32. c, err := net.Dial("unix", path)
  33. if err == nil {
  34. c.Close()
  35. if tailscaledRunningUnderLaunchd() {
  36. return nil, fmt.Errorf("%v: address already in use; tailscaled already running under launchd (to stop, run: $ sudo launchctl stop com.tailscale.tailscaled)", path)
  37. }
  38. return nil, fmt.Errorf("%v: address already in use", path)
  39. }
  40. _ = os.Remove(path)
  41. perm := socketPermissionsForOS()
  42. sockDir := filepath.Dir(path)
  43. if _, err := os.Stat(sockDir); os.IsNotExist(err) {
  44. os.MkdirAll(sockDir, 0755) // best effort
  45. // If we're on a platform where we want the socket
  46. // world-readable, open up the permissions on the
  47. // just-created directory too, in case a umask ate
  48. // it. This primarily affects running tailscaled by
  49. // hand as root in a shell, as there is no umask when
  50. // running under systemd.
  51. if perm == 0666 {
  52. if fi, err := os.Stat(sockDir); err == nil && fi.Mode()&0077 == 0 {
  53. if err := os.Chmod(sockDir, 0755); err != nil {
  54. log.Print(err)
  55. }
  56. }
  57. }
  58. }
  59. pipe, err := net.Listen("unix", path)
  60. if err != nil {
  61. return nil, err
  62. }
  63. os.Chmod(path, perm)
  64. return pipe, err
  65. }
  66. func tailscaledRunningUnderLaunchd() bool {
  67. if runtime.GOOS != "darwin" {
  68. return false
  69. }
  70. plist, err := exec.Command("launchctl", "list", "com.tailscale.tailscaled").Output()
  71. _ = plist // parse it? https://github.com/DHowett/go-plist if we need something.
  72. running := err == nil
  73. return running
  74. }
  75. // socketPermissionsForOS returns the permissions to use for the
  76. // tailscaled.sock.
  77. func socketPermissionsForOS() os.FileMode {
  78. if PlatformUsesPeerCreds() {
  79. return 0666
  80. }
  81. // Otherwise, root only.
  82. return 0600
  83. }