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

     1  /* SPDX-License-Identifier: MIT
     2   *
     3   * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
     4   */
     5  
     6  package ipc
     7  
     8  import (
     9  	"net"
    10  	"os"
    11  
    12  	"github.com/amnezia-vpn/amneziawg-go/rwcancel"
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  type UAPIListener struct {
    17  	listener        net.Listener // unix socket listener
    18  	connNew         chan net.Conn
    19  	connErr         chan error
    20  	inotifyFd       int
    21  	inotifyRWCancel *rwcancel.RWCancel
    22  }
    23  
    24  func (l *UAPIListener) Accept() (net.Conn, error) {
    25  	for {
    26  		select {
    27  		case conn := <-l.connNew:
    28  			return conn, nil
    29  
    30  		case err := <-l.connErr:
    31  			return nil, err
    32  		}
    33  	}
    34  }
    35  
    36  func (l *UAPIListener) Close() error {
    37  	err1 := unix.Close(l.inotifyFd)
    38  	err2 := l.inotifyRWCancel.Cancel()
    39  	err3 := l.listener.Close()
    40  	if err1 != nil {
    41  		return err1
    42  	}
    43  	if err2 != nil {
    44  		return err2
    45  	}
    46  	return err3
    47  }
    48  
    49  func (l *UAPIListener) Addr() net.Addr {
    50  	return l.listener.Addr()
    51  }
    52  
    53  func UAPIListen(name string, file *os.File) (net.Listener, error) {
    54  	// wrap file in listener
    55  
    56  	listener, err := net.FileListener(file)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	if unixListener, ok := listener.(*net.UnixListener); ok {
    62  		unixListener.SetUnlinkOnClose(true)
    63  	}
    64  
    65  	uapi := &UAPIListener{
    66  		listener: listener,
    67  		connNew:  make(chan net.Conn, 1),
    68  		connErr:  make(chan error, 1),
    69  	}
    70  
    71  	// watch for deletion of socket
    72  
    73  	socketPath := sockPath(name)
    74  
    75  	uapi.inotifyFd, err = unix.InotifyInit()
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	_, err = unix.InotifyAddWatch(
    81  		uapi.inotifyFd,
    82  		socketPath,
    83  		unix.IN_ATTRIB|
    84  			unix.IN_DELETE|
    85  			unix.IN_DELETE_SELF,
    86  	)
    87  
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	uapi.inotifyRWCancel, err = rwcancel.NewRWCancel(uapi.inotifyFd)
    93  	if err != nil {
    94  		unix.Close(uapi.inotifyFd)
    95  		return nil, err
    96  	}
    97  
    98  	go func(l *UAPIListener) {
    99  		var buf [0]byte
   100  		for {
   101  			defer uapi.inotifyRWCancel.Close()
   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  			_, err := uapi.inotifyRWCancel.Read(buf[:])
   108  			if err != nil {
   109  				l.connErr <- err
   110  				return
   111  			}
   112  		}
   113  	}(uapi)
   114  
   115  	// watch for new connections
   116  
   117  	go func(l *UAPIListener) {
   118  		for {
   119  			conn, err := l.listener.Accept()
   120  			if err != nil {
   121  				l.connErr <- err
   122  				break
   123  			}
   124  			l.connNew <- conn
   125  		}
   126  	}(uapi)
   127  
   128  	return uapi, nil
   129  }