github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/socket/unix/transport/connectionless.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package transport 16 17 import ( 18 "github.com/SagerNet/gvisor/pkg/abi/linux" 19 "github.com/SagerNet/gvisor/pkg/context" 20 "github.com/SagerNet/gvisor/pkg/syserr" 21 "github.com/SagerNet/gvisor/pkg/tcpip" 22 "github.com/SagerNet/gvisor/pkg/waiter" 23 ) 24 25 // connectionlessEndpoint is a unix endpoint for unix sockets that support operating in 26 // a connectionless fashon. 27 // 28 // Specifically, this means datagram unix sockets not created with 29 // socketpair(2). 30 // 31 // +stateify savable 32 type connectionlessEndpoint struct { 33 baseEndpoint 34 } 35 36 var ( 37 _ = BoundEndpoint((*connectionlessEndpoint)(nil)) 38 _ = Endpoint((*connectionlessEndpoint)(nil)) 39 ) 40 41 // NewConnectionless creates a new unbound dgram endpoint. 42 func NewConnectionless(ctx context.Context) Endpoint { 43 ep := &connectionlessEndpoint{baseEndpoint{Queue: &waiter.Queue{}}} 44 q := queue{ReaderQueue: ep.Queue, WriterQueue: &waiter.Queue{}, limit: defaultBufferSize} 45 q.InitRefs() 46 ep.receiver = &queueReceiver{readQueue: &q} 47 ep.ops.SetSendBufferSize(defaultBufferSize, false /* notify */) 48 ep.ops.SetReceiveBufferSize(defaultBufferSize, false /* notify */) 49 ep.ops.InitHandler(ep, &stackHandler{}, getSendBufferLimits, getReceiveBufferLimits) 50 return ep 51 } 52 53 // isBound returns true iff the endpoint is bound. 54 func (e *connectionlessEndpoint) isBound() bool { 55 return e.path != "" 56 } 57 58 // Close puts the endpoint in a closed state and frees all resources associated 59 // with it. 60 func (e *connectionlessEndpoint) Close(ctx context.Context) { 61 e.Lock() 62 if e.connected != nil { 63 e.connected.Release(ctx) 64 e.connected = nil 65 } 66 67 if e.isBound() { 68 e.path = "" 69 } 70 71 e.receiver.CloseRecv() 72 r := e.receiver 73 e.receiver = nil 74 e.Unlock() 75 76 r.CloseNotify() 77 r.Release(ctx) 78 } 79 80 // BidirectionalConnect implements BoundEndpoint.BidirectionalConnect. 81 func (e *connectionlessEndpoint) BidirectionalConnect(ctx context.Context, ce ConnectingEndpoint, returnConnect func(Receiver, ConnectedEndpoint)) *syserr.Error { 82 return syserr.ErrConnectionRefused 83 } 84 85 // UnidirectionalConnect implements BoundEndpoint.UnidirectionalConnect. 86 func (e *connectionlessEndpoint) UnidirectionalConnect(ctx context.Context) (ConnectedEndpoint, *syserr.Error) { 87 e.Lock() 88 r := e.receiver 89 e.Unlock() 90 if r == nil { 91 return nil, syserr.ErrConnectionRefused 92 } 93 q := r.(*queueReceiver).readQueue 94 if !q.TryIncRef() { 95 return nil, syserr.ErrConnectionRefused 96 } 97 return &connectedEndpoint{ 98 endpoint: e, 99 writeQueue: q, 100 }, nil 101 } 102 103 // SendMsg writes data and a control message to the specified endpoint. 104 // This method does not block if the data cannot be written. 105 func (e *connectionlessEndpoint) SendMsg(ctx context.Context, data [][]byte, c ControlMessages, to BoundEndpoint) (int64, *syserr.Error) { 106 if to == nil { 107 return e.baseEndpoint.SendMsg(ctx, data, c, nil) 108 } 109 110 connected, err := to.UnidirectionalConnect(ctx) 111 if err != nil { 112 return 0, syserr.ErrInvalidEndpointState 113 } 114 defer connected.Release(ctx) 115 116 e.Lock() 117 n, notify, err := connected.Send(ctx, data, c, tcpip.FullAddress{Addr: tcpip.Address(e.path)}) 118 e.Unlock() 119 120 if notify { 121 connected.SendNotify() 122 } 123 124 return n, err 125 } 126 127 // Type implements Endpoint.Type. 128 func (e *connectionlessEndpoint) Type() linux.SockType { 129 return linux.SOCK_DGRAM 130 } 131 132 // Connect attempts to connect directly to server. 133 func (e *connectionlessEndpoint) Connect(ctx context.Context, server BoundEndpoint) *syserr.Error { 134 connected, err := server.UnidirectionalConnect(ctx) 135 if err != nil { 136 return err 137 } 138 139 e.Lock() 140 if e.connected != nil { 141 e.connected.Release(ctx) 142 } 143 e.connected = connected 144 e.Unlock() 145 146 return nil 147 } 148 149 // Listen starts listening on the connection. 150 func (*connectionlessEndpoint) Listen(int) *syserr.Error { 151 return syserr.ErrNotSupported 152 } 153 154 // Accept accepts a new connection. 155 func (*connectionlessEndpoint) Accept(*tcpip.FullAddress) (Endpoint, *syserr.Error) { 156 return nil, syserr.ErrNotSupported 157 } 158 159 // Bind binds the connection. 160 // 161 // For Unix endpoints, this _only sets the address associated with the socket_. 162 // Work associated with sockets in the filesystem or finding those sockets must 163 // be done by a higher level. 164 // 165 // Bind will fail only if the socket is connected, bound or the passed address 166 // is invalid (the empty string). 167 func (e *connectionlessEndpoint) Bind(addr tcpip.FullAddress, commit func() *syserr.Error) *syserr.Error { 168 e.Lock() 169 defer e.Unlock() 170 if e.isBound() { 171 return syserr.ErrAlreadyBound 172 } 173 if addr.Addr == "" { 174 // The empty string is not permitted. 175 return syserr.ErrBadLocalAddress 176 } 177 if commit != nil { 178 if err := commit(); err != nil { 179 return err 180 } 181 } 182 183 // Save the bound address. 184 e.path = string(addr.Addr) 185 return nil 186 } 187 188 // Readiness returns the current readiness of the endpoint. For example, if 189 // waiter.EventIn is set, the endpoint is immediately readable. 190 func (e *connectionlessEndpoint) Readiness(mask waiter.EventMask) waiter.EventMask { 191 e.Lock() 192 defer e.Unlock() 193 194 ready := waiter.EventMask(0) 195 if mask&waiter.ReadableEvents != 0 && e.receiver.Readable() { 196 ready |= waiter.ReadableEvents 197 } 198 199 if e.Connected() { 200 if mask&waiter.WritableEvents != 0 && e.connected.Writable() { 201 ready |= waiter.WritableEvents 202 } 203 } 204 205 return ready 206 } 207 208 // State implements socket.Socket.State. 209 func (e *connectionlessEndpoint) State() uint32 { 210 e.Lock() 211 defer e.Unlock() 212 213 switch { 214 case e.isBound(): 215 return linux.SS_UNCONNECTED 216 case e.Connected(): 217 return linux.SS_CONNECTING 218 default: 219 return linux.SS_DISCONNECTING 220 } 221 } 222 223 // OnSetSendBufferSize implements tcpip.SocketOptionsHandler.OnSetSendBufferSize. 224 func (e *connectionlessEndpoint) OnSetSendBufferSize(v int64) (newSz int64) { 225 if e.Connected() { 226 return e.baseEndpoint.connected.SetSendBufferSize(v) 227 } 228 return v 229 }