github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/loopback.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"bytes"
     8  	"io"
     9  	"net"
    10  	"sync"
    11  	"syscall"
    12  	"time"
    13  )
    14  
    15  // LoopbackAddr is an address class that implement the net.Addr interface for loopback
    16  // devices
    17  type LoopbackAddr struct{}
    18  
    19  // LoopbackListener is a listener that creates new loopback connections.
    20  // It is goroutine safe.
    21  type LoopbackListener struct {
    22  	logCtx LogContext
    23  
    24  	// Protects closing of ch so that we can close ch
    25  	// and set isClosed atomically.
    26  	mutex    sync.Mutex
    27  	ch       chan *LoopbackConn
    28  	isClosed bool
    29  }
    30  
    31  // LoopbackConn implments the net.Conn interface but is used to loopback
    32  // from a process to itself. It is goroutine safe.
    33  type LoopbackConn struct {
    34  
    35  	// wMutex protects isClosed and ch to protect against
    36  	// double-closes, and writes after close. It protects the
    37  	// writer, hence the 'w'.
    38  	wMutex   sync.Mutex
    39  	isClosed bool
    40  	ch       chan<- []byte
    41  
    42  	// rMutex Protects partnerCh and buf, to ensure that only
    43  	// one Go routine is reading at a time. It protects the
    44  	// reader hence the 'r'
    45  	rMutex    sync.Mutex
    46  	partnerCh <-chan []byte
    47  	buf       bytes.Buffer
    48  }
    49  
    50  // NewLoopbackListener creates a new Loopback listener
    51  func NewLoopbackListener(ctx LogContext) *LoopbackListener {
    52  	return &LoopbackListener{
    53  		logCtx:   ctx,
    54  		ch:       make(chan *LoopbackConn),
    55  		isClosed: false,
    56  	}
    57  }
    58  
    59  // NewLoopbackConnPair makes a new loopback connection pair
    60  func NewLoopbackConnPair() (*LoopbackConn, *LoopbackConn) {
    61  	aCh := make(chan []byte)
    62  	bCh := make(chan []byte)
    63  	a := &LoopbackConn{ch: aCh}
    64  	b := &LoopbackConn{ch: bCh}
    65  	a.partnerCh = bCh
    66  	b.partnerCh = aCh
    67  	return a, b
    68  }
    69  
    70  // LoopbackDial dials the given LoopbackListener and yields an new net.Conn
    71  // that's a connection to it.
    72  func (ll *LoopbackListener) Dial() (net.Conn, error) {
    73  	ll.logCtx.GetLog().Debug("+ LoopbackListener.Dial")
    74  	ll.mutex.Lock()
    75  	defer ll.mutex.Unlock()
    76  	if ll.isClosed {
    77  		return nil, syscall.EINVAL
    78  	}
    79  	a, b := NewLoopbackConnPair()
    80  	ll.ch <- a
    81  	return b, nil
    82  }
    83  
    84  // Accept waits for and returns the next connection to the listener.
    85  func (ll *LoopbackListener) Accept() (ret net.Conn, err error) {
    86  	ll.logCtx.GetLog().Debug("+ LoopbackListener.Accept")
    87  	var ok bool
    88  
    89  	// We can't hold the lock (even if we had to) since that would
    90  	// deadlock the process (to have the Accepter and Dialer contending
    91  	// the same lock).
    92  	if ret, ok = <-ll.ch; !ok {
    93  		err = syscall.EINVAL
    94  	}
    95  
    96  	ll.logCtx.GetLog().Debug("- LoopbackListener.Accept -> %v", err)
    97  	return ret, err
    98  }
    99  
   100  // Close closes the listener.
   101  // Any blocked Accept operations will be unblocked and return errors
   102  func (ll *LoopbackListener) Close() (err error) {
   103  	ll.mutex.Lock()
   104  	defer ll.mutex.Unlock()
   105  	if ll.isClosed {
   106  		return syscall.EINVAL
   107  	}
   108  	ll.isClosed = true
   109  	close(ll.ch)
   110  	return
   111  }
   112  
   113  // Addr returns the listener's network address.
   114  func (ll *LoopbackListener) Addr() (addr net.Addr) {
   115  	return LoopbackAddr{}
   116  }
   117  
   118  // Read reads data from the connection.
   119  func (lc *LoopbackConn) Read(b []byte) (n int, err error) {
   120  	lc.rMutex.Lock()
   121  	defer lc.rMutex.Unlock()
   122  
   123  	if lc.buf.Len() > 0 {
   124  		return lc.buf.Read(b)
   125  	}
   126  	msg, ok := <-lc.partnerCh
   127  	if !ok {
   128  		return 0, io.EOF
   129  	}
   130  	lc.buf.Write(msg)
   131  	return lc.buf.Read(b)
   132  }
   133  
   134  // Write writes data to the connection.
   135  func (lc *LoopbackConn) Write(b []byte) (n int, err error) {
   136  	lc.wMutex.Lock()
   137  	defer lc.wMutex.Unlock()
   138  	if lc.isClosed {
   139  		return 0, syscall.EINVAL
   140  	}
   141  	lc.ch <- b
   142  	return len(b), nil
   143  }
   144  
   145  // Close closes the connection.
   146  // Any blocked Read or Write operations will be unblocked and return errors.
   147  func (lc *LoopbackConn) Close() (err error) {
   148  	lc.wMutex.Lock()
   149  	defer lc.wMutex.Unlock()
   150  	if lc.isClosed {
   151  		return syscall.EINVAL
   152  	}
   153  	lc.isClosed = true
   154  	close(lc.ch)
   155  	return nil
   156  }
   157  
   158  // LocalAddr returns the local network address.
   159  func (lc *LoopbackConn) LocalAddr() (addr net.Addr) {
   160  	return
   161  }
   162  
   163  // RemoteAddr returns the remote network address.
   164  func (lc *LoopbackConn) RemoteAddr() (addr net.Addr) {
   165  	return
   166  }
   167  
   168  // SetDeadline sets the read and write deadlines associated
   169  // with the connection. It is equivalent to calling both
   170  // SetReadDeadline and SetWriteDeadline.
   171  //
   172  // A deadline is an absolute time after which I/O operations
   173  // fail with a timeout (see type Error) instead of
   174  // blocking. The deadline applies to all future I/O, not just
   175  // the immediately following call to Read or Write.
   176  //
   177  // An idle timeout can be implemented by repeatedly extending
   178  // the deadline after successful Read or Write calls.
   179  //
   180  // A zero value for t means I/O operations will not time out.
   181  func (lc *LoopbackConn) SetDeadline(t time.Time) (err error) {
   182  	return
   183  }
   184  
   185  // SetReadDeadline sets the deadline for future Read calls.
   186  // A zero value for t means Read will not time out.
   187  func (lc *LoopbackConn) SetReadDeadline(t time.Time) (err error) {
   188  	return
   189  }
   190  
   191  // SetWriteDeadline sets the deadline for future Write calls.
   192  // Even if write times out, it may return n > 0, indicating that
   193  // some of the data was successfully written.
   194  // A zero value for t means Write will not time out.
   195  func (lc *LoopbackConn) SetWriteDeadline(t time.Time) (err error) {
   196  	return
   197  }
   198  
   199  // Network returns the name of the network
   200  func (la LoopbackAddr) Network() (s string) {
   201  	return "loopback"
   202  }
   203  
   204  // String returns the string form of address
   205  func (la LoopbackAddr) String() (s string) {
   206  	return "0"
   207  }