github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/gofer/socket.go (about) 1 // Copyright 2020 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 "golang.org/x/sys/unix" 19 "github.com/SagerNet/gvisor/pkg/abi/linux" 20 "github.com/SagerNet/gvisor/pkg/context" 21 "github.com/SagerNet/gvisor/pkg/log" 22 "github.com/SagerNet/gvisor/pkg/p9" 23 "github.com/SagerNet/gvisor/pkg/sentry/fsimpl/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 func (d *dentry) isSocket() bool { 30 return d.fileType() == linux.S_IFSOCK 31 } 32 33 // endpoint is a Gofer-backed transport.BoundEndpoint. 34 // 35 // An endpoint's lifetime is the time between when filesystem.BoundEndpointAt() 36 // is called and either BoundEndpoint.BidirectionalConnect or 37 // BoundEndpoint.UnidirectionalConnect is called. 38 // 39 // +stateify savable 40 type endpoint struct { 41 // dentry is the filesystem dentry which produced this endpoint. 42 dentry *dentry 43 44 // path is the sentry path where this endpoint is bound. 45 path string 46 } 47 48 func sockTypeToP9(t linux.SockType) (p9.ConnectFlags, bool) { 49 switch t { 50 case linux.SOCK_STREAM: 51 return p9.StreamSocket, true 52 case linux.SOCK_SEQPACKET: 53 return p9.SeqpacketSocket, true 54 case linux.SOCK_DGRAM: 55 return p9.DgramSocket, true 56 } 57 return 0, false 58 } 59 60 // BidirectionalConnect implements ConnectableEndpoint.BidirectionalConnect. 61 func (e *endpoint) BidirectionalConnect(ctx context.Context, ce transport.ConnectingEndpoint, returnConnect func(transport.Receiver, transport.ConnectedEndpoint)) *syserr.Error { 62 cf, ok := sockTypeToP9(ce.Type()) 63 if !ok { 64 return syserr.ErrConnectionRefused 65 } 66 67 // No lock ordering required as only the ConnectingEndpoint has a mutex. 68 ce.Lock() 69 70 // Check connecting state. 71 if ce.Connected() { 72 ce.Unlock() 73 return syserr.ErrAlreadyConnected 74 } 75 if ce.Listening() { 76 ce.Unlock() 77 return syserr.ErrInvalidEndpointState 78 } 79 80 c, err := e.newConnectedEndpoint(ctx, cf, ce.WaiterQueue()) 81 if err != nil { 82 ce.Unlock() 83 return err 84 } 85 86 returnConnect(c, c) 87 ce.Unlock() 88 if err := c.Init(); err != nil { 89 return syserr.FromError(err) 90 } 91 92 return nil 93 } 94 95 // UnidirectionalConnect implements 96 // transport.BoundEndpoint.UnidirectionalConnect. 97 func (e *endpoint) UnidirectionalConnect(ctx context.Context) (transport.ConnectedEndpoint, *syserr.Error) { 98 c, err := e.newConnectedEndpoint(ctx, p9.DgramSocket, &waiter.Queue{}) 99 if err != nil { 100 return nil, err 101 } 102 103 if err := c.Init(); err != nil { 104 return nil, syserr.FromError(err) 105 } 106 107 // We don't need the receiver. 108 c.CloseRecv() 109 c.Release(ctx) 110 111 return c, nil 112 } 113 114 func (e *endpoint) newConnectedEndpoint(ctx context.Context, flags p9.ConnectFlags, queue *waiter.Queue) (*host.SCMConnectedEndpoint, *syserr.Error) { 115 hostFile, err := e.dentry.file.connect(ctx, flags) 116 if err != nil { 117 return nil, syserr.ErrConnectionRefused 118 } 119 // Dup the fd so that the new endpoint can manage its lifetime. 120 hostFD, err := unix.Dup(hostFile.FD()) 121 if err != nil { 122 log.Warningf("Could not dup host socket fd %d: %v", hostFile.FD(), err) 123 return nil, syserr.FromError(err) 124 } 125 // After duplicating, we no longer need hostFile. 126 hostFile.Close() 127 128 c, serr := host.NewSCMEndpoint(ctx, hostFD, queue, e.path) 129 if serr != nil { 130 log.Warningf("Gofer returned invalid host socket for BidirectionalConnect; file %+v flags %+v: %v", e.dentry.file, flags, serr) 131 return nil, serr 132 } 133 return c, nil 134 } 135 136 // Release implements transport.BoundEndpoint.Release. 137 func (e *endpoint) Release(ctx context.Context) { 138 e.dentry.DecRef(ctx) 139 } 140 141 // Passcred implements transport.BoundEndpoint.Passcred. 142 func (e *endpoint) Passcred() bool { 143 return false 144 }