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  }