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 }