uapi_bsd.go raw

   1  //go:build darwin || freebsd || openbsd
   2  
   3  /* SPDX-License-Identifier: MIT
   4   *
   5   * Copyright (C) 2017-2025 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  }
 133