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