github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/gofer/socket.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 gofer 16 17 import ( 18 "github.com/SagerNet/gvisor/pkg/abi/linux" 19 "github.com/SagerNet/gvisor/pkg/context" 20 "github.com/SagerNet/gvisor/pkg/log" 21 "github.com/SagerNet/gvisor/pkg/p9" 22 "github.com/SagerNet/gvisor/pkg/sentry/fs" 23 "github.com/SagerNet/gvisor/pkg/sentry/fs/host" 24 "github.com/SagerNet/gvisor/pkg/sentry/socket/unix/transport" 25 "github.com/SagerNet/gvisor/pkg/syserr" 26 "github.com/SagerNet/gvisor/pkg/waiter" 27 ) 28 29 // BoundEndpoint returns a gofer-backed transport.BoundEndpoint. 30 func (i *inodeOperations) BoundEndpoint(inode *fs.Inode, path string) transport.BoundEndpoint { 31 if !fs.IsSocket(i.fileState.sattr) { 32 return nil 33 } 34 35 s := i.session() 36 if s.overrides != nil { 37 s.overrides.lock() 38 defer s.overrides.unlock() 39 ep := s.overrides.getBoundEndpoint(i.fileState.key) 40 if ep != nil { 41 return ep 42 } 43 44 // Not found in overrides map, it may be a gofer backed unix socket... 45 } 46 47 inode.IncRef() 48 return &endpoint{inode, i.fileState.file.file, path} 49 } 50 51 // LINT.IfChange 52 53 // endpoint is a Gofer-backed transport.BoundEndpoint. 54 // 55 // An endpoint's lifetime is the time between when InodeOperations.BoundEndpoint() 56 // is called and either BoundEndpoint.BidirectionalConnect or 57 // BoundEndpoint.UnidirectionalConnect is called. 58 type endpoint struct { 59 // inode is the filesystem inode which produced this endpoint. 60 inode *fs.Inode 61 62 // file is the p9 file that contains a single unopened fid. 63 file p9.File 64 65 // path is the sentry path where this endpoint is bound. 66 path string 67 } 68 69 func sockTypeToP9(t linux.SockType) (p9.ConnectFlags, bool) { 70 switch t { 71 case linux.SOCK_STREAM: 72 return p9.StreamSocket, true 73 case linux.SOCK_SEQPACKET: 74 return p9.SeqpacketSocket, true 75 case linux.SOCK_DGRAM: 76 return p9.DgramSocket, true 77 } 78 return 0, false 79 } 80 81 // BidirectionalConnect implements ConnectableEndpoint.BidirectionalConnect. 82 func (e *endpoint) BidirectionalConnect(ctx context.Context, ce transport.ConnectingEndpoint, returnConnect func(transport.Receiver, transport.ConnectedEndpoint)) *syserr.Error { 83 cf, ok := sockTypeToP9(ce.Type()) 84 if !ok { 85 return syserr.ErrConnectionRefused 86 } 87 88 // No lock ordering required as only the ConnectingEndpoint has a mutex. 89 ce.Lock() 90 91 // Check connecting state. 92 if ce.Connected() { 93 ce.Unlock() 94 return syserr.ErrAlreadyConnected 95 } 96 if ce.Listening() { 97 ce.Unlock() 98 return syserr.ErrInvalidEndpointState 99 } 100 101 hostFile, err := e.file.Connect(cf) 102 if err != nil { 103 ce.Unlock() 104 return syserr.ErrConnectionRefused 105 } 106 107 c, serr := host.NewConnectedEndpoint(ctx, hostFile, ce.WaiterQueue(), e.path) 108 if serr != nil { 109 ce.Unlock() 110 log.Warningf("Gofer returned invalid host socket for BidirectionalConnect; file %+v flags %+v: %v", e.file, cf, serr) 111 return serr 112 } 113 114 returnConnect(c, c) 115 ce.Unlock() 116 c.Init() 117 118 return nil 119 } 120 121 // UnidirectionalConnect implements 122 // transport.BoundEndpoint.UnidirectionalConnect. 123 func (e *endpoint) UnidirectionalConnect(ctx context.Context) (transport.ConnectedEndpoint, *syserr.Error) { 124 hostFile, err := e.file.Connect(p9.DgramSocket) 125 if err != nil { 126 return nil, syserr.ErrConnectionRefused 127 } 128 129 c, serr := host.NewConnectedEndpoint(ctx, hostFile, &waiter.Queue{}, e.path) 130 if serr != nil { 131 log.Warningf("Gofer returned invalid host socket for UnidirectionalConnect; file %+v: %v", e.file, serr) 132 return nil, serr 133 } 134 c.Init() 135 136 // We don't need the receiver. 137 c.CloseRecv() 138 c.Release(ctx) 139 140 return c, nil 141 } 142 143 // Release implements transport.BoundEndpoint.Release. 144 func (e *endpoint) Release(ctx context.Context) { 145 e.inode.DecRef(ctx) 146 } 147 148 // Passcred implements transport.BoundEndpoint.Passcred. 149 func (e *endpoint) Passcred() bool { 150 return false 151 } 152 153 // LINT.ThenChange(../../fsimpl/gofer/socket.go)