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)