github.com/amnezia-vpn/amneziawg-go@v0.2.8/ipc/uapi_bsd.go (about)

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