github.com/liloew/wireguard-go@v0.0.0-20220224014633-9cd745e6f114/ipc/uapi_bsd.go (about) 1 //go: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 // wrap file in listener 58 59 listener, err := net.FileListener(file) 60 if err != nil { 61 return nil, err 62 } 63 64 uapi := &UAPIListener{ 65 listener: listener, 66 connNew: make(chan net.Conn, 1), 67 connErr: make(chan error, 1), 68 } 69 70 if unixListener, ok := listener.(*net.UnixListener); ok { 71 unixListener.SetUnlinkOnClose(true) 72 } 73 74 socketPath := sockPath(name) 75 76 // watch for deletion of socket 77 78 uapi.kqueueFd, err = unix.Kqueue() 79 if err != nil { 80 return nil, err 81 } 82 uapi.keventFd, err = unix.Open(socketDirectory, unix.O_RDONLY, 0) 83 if err != nil { 84 unix.Close(uapi.kqueueFd) 85 return nil, err 86 } 87 88 go func(l *UAPIListener) { 89 event := unix.Kevent_t{ 90 Filter: unix.EVFILT_VNODE, 91 Flags: unix.EV_ADD | unix.EV_ENABLE | unix.EV_ONESHOT, 92 Fflags: unix.NOTE_WRITE, 93 } 94 // Allow this assignment to work with both the 32-bit and 64-bit version 95 // of the above struct. If you know another way, please submit a patch. 96 *(*uintptr)(unsafe.Pointer(&event.Ident)) = uintptr(uapi.keventFd) 97 events := make([]unix.Kevent_t, 1) 98 n := 1 99 var kerr error 100 for { 101 // start with lstat to avoid race condition 102 if _, err := os.Lstat(socketPath); os.IsNotExist(err) { 103 l.connErr <- err 104 return 105 } 106 if (kerr != nil || n != 1) && kerr != unix.EINTR { 107 if kerr != nil { 108 l.connErr <- kerr 109 } else { 110 l.connErr <- errors.New("kqueue returned empty") 111 } 112 return 113 } 114 n, kerr = unix.Kevent(uapi.kqueueFd, []unix.Kevent_t{event}, events, nil) 115 } 116 }(uapi) 117 118 // watch for new connections 119 120 go func(l *UAPIListener) { 121 for { 122 conn, err := l.listener.Accept() 123 if err != nil { 124 l.connErr <- err 125 break 126 } 127 l.connNew <- conn 128 } 129 }(uapi) 130 131 return uapi, nil 132 }