github.com/tailscale/wireguard-go@v0.0.20201119-0.20210522003738-46b531feb08a/ipc/uapi_bsd.go (about) 1 // +build darwin freebsd openbsd 2 3 /* SPDX-License-Identifier: MIT 4 * 5 * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. 6 */ 7 8 package ipc 9 10 import ( 11 "errors" 12 "net" 13 "os" 14 "unsafe" 15 16 "golang.org/x/sys/unix" 17 ) 18 19 type UAPIListener struct { 20 listener net.Listener // unix socket listener 21 connNew chan net.Conn 22 connErr chan error 23 kqueueFd int 24 keventFd int 25 } 26 27 func (l *UAPIListener) Accept() (net.Conn, error) { 28 for { 29 select { 30 case conn := <-l.connNew: 31 return conn, nil 32 33 case err := <-l.connErr: 34 return nil, err 35 } 36 } 37 } 38 39 func (l *UAPIListener) Close() error { 40 err1 := unix.Close(l.kqueueFd) 41 err2 := unix.Close(l.keventFd) 42 err3 := l.listener.Close() 43 if err1 != nil { 44 return err1 45 } 46 if err2 != nil { 47 return err2 48 } 49 return err3 50 } 51 52 func (l *UAPIListener) Addr() net.Addr { 53 return l.listener.Addr() 54 } 55 56 func UAPIListen(name string, file *os.File) (net.Listener, error) { 57 58 // wrap file in listener 59 60 listener, err := net.FileListener(file) 61 if err != nil { 62 return nil, err 63 } 64 65 uapi := &UAPIListener{ 66 listener: listener, 67 connNew: make(chan net.Conn, 1), 68 connErr: make(chan error, 1), 69 } 70 71 if unixListener, ok := listener.(*net.UnixListener); ok { 72 unixListener.SetUnlinkOnClose(true) 73 } 74 75 socketPath := sockPath(name) 76 77 // watch for deletion of socket 78 79 uapi.kqueueFd, err = unix.Kqueue() 80 if err != nil { 81 return nil, err 82 } 83 uapi.keventFd, err = unix.Open(socketDirectory, unix.O_RDONLY, 0) 84 if err != nil { 85 unix.Close(uapi.kqueueFd) 86 return nil, err 87 } 88 89 go func(l *UAPIListener) { 90 event := unix.Kevent_t{ 91 Filter: unix.EVFILT_VNODE, 92 Flags: unix.EV_ADD | unix.EV_ENABLE | unix.EV_ONESHOT, 93 Fflags: unix.NOTE_WRITE, 94 } 95 // Allow this assignment to work with both the 32-bit and 64-bit version 96 // of the above struct. If you know another way, please submit a patch. 97 *(*uintptr)(unsafe.Pointer(&event.Ident)) = uintptr(uapi.keventFd) 98 events := make([]unix.Kevent_t, 1) 99 n := 1 100 var kerr error 101 for { 102 // start with lstat to avoid race condition 103 if _, err := os.Lstat(socketPath); os.IsNotExist(err) { 104 l.connErr <- err 105 return 106 } 107 if kerr != nil || n != 1 { 108 if kerr != nil { 109 l.connErr <- kerr 110 } else { 111 l.connErr <- errors.New("kqueue returned empty") 112 } 113 return 114 } 115 n, kerr = unix.Kevent(uapi.kqueueFd, []unix.Kevent_t{event}, events, nil) 116 } 117 }(uapi) 118 119 // watch for new connections 120 121 go func(l *UAPIListener) { 122 for { 123 conn, err := l.listener.Accept() 124 if err != nil { 125 l.connErr <- err 126 break 127 } 128 l.connNew <- conn 129 } 130 }(uapi) 131 132 return uapi, nil 133 }