github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/socket/unix/transport/connectioned.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  	"golang.org/x/sys/unix"
    19  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/fdnotifier"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/uniqueid"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/syserr"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/waiter"
    26  )
    27  
    28  type locker interface {
    29  	Lock()
    30  	Unlock()
    31  	NestedLock(endpointlockNameIndex)
    32  	NestedUnlock(endpointlockNameIndex)
    33  }
    34  
    35  // A ConnectingEndpoint is a connectioned unix endpoint that is attempting to
    36  // establish a bidirectional connection with a BoundEndpoint.
    37  type ConnectingEndpoint interface {
    38  	// ID returns the endpoint's globally unique identifier. This identifier
    39  	// must be used to determine locking order if more than one endpoint is
    40  	// to be locked in the same codepath. The endpoint with the smaller
    41  	// identifier must be locked before endpoints with larger identifiers.
    42  	ID() uint64
    43  
    44  	// Passcred implements socket.Credentialer.Passcred.
    45  	Passcred() bool
    46  
    47  	// Type returns the socket type, typically either SockStream or
    48  	// SockSeqpacket. The connection attempt must be aborted if this
    49  	// value doesn't match the BoundEndpoint's type.
    50  	Type() linux.SockType
    51  
    52  	// GetLocalAddress returns the bound path.
    53  	GetLocalAddress() (Address, tcpip.Error)
    54  
    55  	// Locker protects the following methods. While locked, only the holder of
    56  	// the lock can change the return value of the protected methods.
    57  	locker
    58  
    59  	// Connected returns true iff the ConnectingEndpoint is in the connected
    60  	// state. ConnectingEndpoints can only be connected to a single endpoint,
    61  	// so the connection attempt must be aborted if this returns true.
    62  	Connected() bool
    63  
    64  	// ListeningLocked returns true iff the ConnectingEndpoint is in the
    65  	// listening state. ConnectingEndpoints cannot make connections while
    66  	// listening, so the connection attempt must be aborted if this returns
    67  	// true.
    68  	ListeningLocked() bool
    69  
    70  	// WaiterQueue returns a pointer to the endpoint's waiter queue.
    71  	WaiterQueue() *waiter.Queue
    72  }
    73  
    74  // connectionedEndpoint is a Unix-domain connected or connectable endpoint and implements
    75  // ConnectingEndpoint, BoundEndpoint and tcpip.Endpoint.
    76  //
    77  // connectionedEndpoints must be in connected state in order to transfer data.
    78  //
    79  // This implementation includes STREAM and SEQPACKET Unix sockets created with
    80  // socket(2), accept(2) or socketpair(2) and dgram unix sockets created with
    81  // socketpair(2). See unix_connectionless.go for the implementation of DGRAM
    82  // Unix sockets created with socket(2).
    83  //
    84  // The state is much simpler than a TCP endpoint, so it is not encoded
    85  // explicitly. Instead we enforce the following invariants:
    86  //
    87  // receiver != nil, connected != nil => connected.
    88  // path != "" && acceptedChan == nil => bound, not listening.
    89  // path != "" && acceptedChan != nil => bound and listening.
    90  //
    91  // Only one of these will be true at any moment.
    92  //
    93  // +stateify savable
    94  type connectionedEndpoint struct {
    95  	baseEndpoint
    96  
    97  	// id is the unique endpoint identifier. This is used exclusively for
    98  	// lock ordering within connect.
    99  	id uint64
   100  
   101  	// idGenerator is used to generate new unique endpoint identifiers.
   102  	idGenerator uniqueid.Provider
   103  
   104  	// stype is used by connecting sockets to ensure that they are the
   105  	// same type. The value is typically either tcpip.SockSeqpacket or
   106  	// tcpip.SockStream.
   107  	stype linux.SockType
   108  
   109  	// acceptedChan is per the TCP endpoint implementation. Note that the
   110  	// sockets in this channel are _already in the connected state_, and
   111  	// have another associated connectionedEndpoint.
   112  	//
   113  	// If nil, then no listen call has been made.
   114  	acceptedChan chan *connectionedEndpoint `state:".([]*connectionedEndpoint)"`
   115  
   116  	// boundSocketFD corresponds to a bound socket on the host filesystem
   117  	// that may listen and accept incoming connections.
   118  	//
   119  	// boundSocketFD is protected by baseEndpoint.mu.
   120  	boundSocketFD BoundSocketFD
   121  }
   122  
   123  var (
   124  	_ = BoundEndpoint((*connectionedEndpoint)(nil))
   125  	_ = Endpoint((*connectionedEndpoint)(nil))
   126  )
   127  
   128  // NewConnectioned creates a new unbound connectionedEndpoint.
   129  func NewConnectioned(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) Endpoint {
   130  	return newConnectioned(ctx, stype, uid)
   131  }
   132  
   133  func newConnectioned(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) *connectionedEndpoint {
   134  	ep := &connectionedEndpoint{
   135  		baseEndpoint: baseEndpoint{Queue: &waiter.Queue{}},
   136  		id:           uid.UniqueID(),
   137  		idGenerator:  uid,
   138  		stype:        stype,
   139  	}
   140  
   141  	ep.ops.InitHandler(ep, &stackHandler{}, getSendBufferLimits, getReceiveBufferLimits)
   142  	ep.ops.SetSendBufferSize(defaultBufferSize, false /* notify */)
   143  	ep.ops.SetReceiveBufferSize(defaultBufferSize, false /* notify */)
   144  	return ep
   145  }
   146  
   147  // NewPair allocates a new pair of connected unix-domain connectionedEndpoints.
   148  func NewPair(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) (Endpoint, Endpoint) {
   149  	a := newConnectioned(ctx, stype, uid)
   150  	b := newConnectioned(ctx, stype, uid)
   151  
   152  	q1 := &queue{ReaderQueue: a.Queue, WriterQueue: b.Queue, limit: defaultBufferSize}
   153  	q1.InitRefs()
   154  	q2 := &queue{ReaderQueue: b.Queue, WriterQueue: a.Queue, limit: defaultBufferSize}
   155  	q2.InitRefs()
   156  
   157  	if stype == linux.SOCK_STREAM {
   158  		a.receiver = &streamQueueReceiver{queueReceiver: queueReceiver{q1}}
   159  		b.receiver = &streamQueueReceiver{queueReceiver: queueReceiver{q2}}
   160  	} else {
   161  		a.receiver = &queueReceiver{q1}
   162  		b.receiver = &queueReceiver{q2}
   163  	}
   164  
   165  	q2.IncRef()
   166  	a.connected = &connectedEndpoint{
   167  		endpoint:   b,
   168  		writeQueue: q2,
   169  	}
   170  	q1.IncRef()
   171  	b.connected = &connectedEndpoint{
   172  		endpoint:   a,
   173  		writeQueue: q1,
   174  	}
   175  
   176  	return a, b
   177  }
   178  
   179  // NewExternal creates a new externally backed Endpoint. It behaves like a
   180  // socketpair.
   181  func NewExternal(stype linux.SockType, uid uniqueid.Provider, queue *waiter.Queue, receiver Receiver, connected ConnectedEndpoint) Endpoint {
   182  	ep := &connectionedEndpoint{
   183  		baseEndpoint: baseEndpoint{Queue: queue, receiver: receiver, connected: connected},
   184  		id:           uid.UniqueID(),
   185  		idGenerator:  uid,
   186  		stype:        stype,
   187  	}
   188  	ep.ops.InitHandler(ep, &stackHandler{}, getSendBufferLimits, getReceiveBufferLimits)
   189  	ep.ops.SetSendBufferSize(connected.SendMaxQueueSize(), false /* notify */)
   190  	ep.ops.SetReceiveBufferSize(defaultBufferSize, false /* notify */)
   191  	return ep
   192  }
   193  
   194  // ID implements ConnectingEndpoint.ID.
   195  func (e *connectionedEndpoint) ID() uint64 {
   196  	return e.id
   197  }
   198  
   199  // Type implements ConnectingEndpoint.Type and Endpoint.Type.
   200  func (e *connectionedEndpoint) Type() linux.SockType {
   201  	return e.stype
   202  }
   203  
   204  // WaiterQueue implements ConnectingEndpoint.WaiterQueue.
   205  func (e *connectionedEndpoint) WaiterQueue() *waiter.Queue {
   206  	return e.Queue
   207  }
   208  
   209  // isBound returns true iff the connectionedEndpoint is bound (but not
   210  // listening).
   211  func (e *connectionedEndpoint) isBound() bool {
   212  	return e.path != "" && e.acceptedChan == nil
   213  }
   214  
   215  // Listening implements ConnectingEndpoint.Listening.
   216  func (e *connectionedEndpoint) Listening() bool {
   217  	e.Lock()
   218  	defer e.Unlock()
   219  	return e.ListeningLocked()
   220  }
   221  
   222  func (e *connectionedEndpoint) ListeningLocked() bool {
   223  	return e.acceptedChan != nil
   224  }
   225  
   226  // Close puts the connectionedEndpoint in a closed state and frees all
   227  // resources associated with it.
   228  //
   229  // The socket will be a fresh state after a call to close and may be reused.
   230  // That is, close may be used to "unbind" or "disconnect" the socket in error
   231  // paths.
   232  func (e *connectionedEndpoint) Close(ctx context.Context) {
   233  	var acceptedChan chan *connectionedEndpoint
   234  	e.Lock()
   235  	var (
   236  		c ConnectedEndpoint
   237  		r Receiver
   238  	)
   239  	switch {
   240  	case e.Connected():
   241  		e.connected.CloseSend()
   242  		e.receiver.CloseRecv()
   243  		// Still have unread data? If yes, we set this into the write
   244  		// end so that the peer can get ECONNRESET) when it does read.
   245  		if e.receiver.RecvQueuedSize() > 0 {
   246  			e.connected.CloseUnread()
   247  		}
   248  		c = e.connected
   249  		r = e.receiver
   250  		e.connected = nil
   251  		e.receiver = nil
   252  	case e.isBound():
   253  		e.path = ""
   254  	case e.ListeningLocked():
   255  		close(e.acceptedChan)
   256  		acceptedChan = e.acceptedChan
   257  		e.acceptedChan = nil
   258  		e.path = ""
   259  	}
   260  	e.Unlock()
   261  	if acceptedChan != nil {
   262  		for n := range acceptedChan {
   263  			n.Close(ctx)
   264  		}
   265  	}
   266  	if c != nil {
   267  		c.CloseNotify()
   268  		c.Release(ctx)
   269  	}
   270  	e.ResetBoundSocketFD(ctx)
   271  	if r != nil {
   272  		r.CloseNotify()
   273  		r.Release(ctx)
   274  	}
   275  }
   276  
   277  // BidirectionalConnect implements BoundEndpoint.BidirectionalConnect.
   278  func (e *connectionedEndpoint) BidirectionalConnect(ctx context.Context, ce ConnectingEndpoint, returnConnect func(Receiver, ConnectedEndpoint)) *syserr.Error {
   279  	if ce.Type() != e.stype {
   280  		return syserr.ErrWrongProtocolForSocket
   281  	}
   282  
   283  	// Check if ce is e to avoid a deadlock.
   284  	if ce, ok := ce.(*connectionedEndpoint); ok && ce == e {
   285  		return syserr.ErrInvalidEndpointState
   286  	}
   287  
   288  	// Do a dance to safely acquire locks on both endpoints.
   289  	if e.id < ce.ID() {
   290  		e.Lock()
   291  		ce.NestedLock(endpointLockHigherid)
   292  	} else {
   293  		ce.Lock()
   294  		e.NestedLock(endpointLockHigherid)
   295  	}
   296  
   297  	// Check connecting state.
   298  	if ce.Connected() {
   299  		e.NestedUnlock(endpointLockHigherid)
   300  		ce.Unlock()
   301  		return syserr.ErrAlreadyConnected
   302  	}
   303  	if ce.ListeningLocked() {
   304  		e.NestedUnlock(endpointLockHigherid)
   305  		ce.Unlock()
   306  		return syserr.ErrInvalidEndpointState
   307  	}
   308  
   309  	// Check bound state.
   310  	if !e.ListeningLocked() {
   311  		e.NestedUnlock(endpointLockHigherid)
   312  		ce.Unlock()
   313  		return syserr.ErrConnectionRefused
   314  	}
   315  
   316  	// Create a newly bound connectionedEndpoint.
   317  	ne := &connectionedEndpoint{
   318  		baseEndpoint: baseEndpoint{
   319  			path:  e.path,
   320  			Queue: &waiter.Queue{},
   321  		},
   322  		id:          e.idGenerator.UniqueID(),
   323  		idGenerator: e.idGenerator,
   324  		stype:       e.stype,
   325  	}
   326  	ne.ops.InitHandler(ne, &stackHandler{}, getSendBufferLimits, getReceiveBufferLimits)
   327  	ne.ops.SetSendBufferSize(defaultBufferSize, false /* notify */)
   328  	ne.ops.SetReceiveBufferSize(defaultBufferSize, false /* notify */)
   329  	ne.SocketOptions().SetPassCred(e.SocketOptions().GetPassCred())
   330  
   331  	readQueue := &queue{ReaderQueue: ce.WaiterQueue(), WriterQueue: ne.Queue, limit: defaultBufferSize}
   332  	readQueue.InitRefs()
   333  	ne.connected = &connectedEndpoint{
   334  		endpoint:   ce,
   335  		writeQueue: readQueue,
   336  	}
   337  
   338  	// Make sure the accepted endpoint inherits this listening socket's SO_SNDBUF.
   339  	writeQueue := &queue{ReaderQueue: ne.Queue, WriterQueue: ce.WaiterQueue(), limit: e.ops.GetSendBufferSize()}
   340  	writeQueue.InitRefs()
   341  	if e.stype == linux.SOCK_STREAM {
   342  		ne.receiver = &streamQueueReceiver{queueReceiver: queueReceiver{readQueue: writeQueue}}
   343  	} else {
   344  		ne.receiver = &queueReceiver{readQueue: writeQueue}
   345  	}
   346  
   347  	select {
   348  	case e.acceptedChan <- ne:
   349  		// Commit state.
   350  		writeQueue.IncRef()
   351  		connected := &connectedEndpoint{
   352  			endpoint:   ne,
   353  			writeQueue: writeQueue,
   354  		}
   355  		readQueue.IncRef()
   356  		if e.stype == linux.SOCK_STREAM {
   357  			returnConnect(&streamQueueReceiver{queueReceiver: queueReceiver{readQueue: readQueue}}, connected)
   358  		} else {
   359  			returnConnect(&queueReceiver{readQueue: readQueue}, connected)
   360  		}
   361  
   362  		// Notify can deadlock if we are holding these locks.
   363  		e.NestedUnlock(endpointLockHigherid)
   364  		ce.Unlock()
   365  
   366  		// Notify on both ends.
   367  		e.Notify(waiter.ReadableEvents)
   368  		ce.WaiterQueue().Notify(waiter.WritableEvents)
   369  
   370  		return nil
   371  	default:
   372  		// Busy; return EAGAIN per spec.
   373  		e.NestedUnlock(endpointLockHigherid)
   374  		ce.Unlock()
   375  		ne.Close(ctx)
   376  		return syserr.ErrTryAgain
   377  	}
   378  }
   379  
   380  // UnidirectionalConnect implements BoundEndpoint.UnidirectionalConnect.
   381  func (e *connectionedEndpoint) UnidirectionalConnect(ctx context.Context) (ConnectedEndpoint, *syserr.Error) {
   382  	return nil, syserr.ErrConnectionRefused
   383  }
   384  
   385  // Connect attempts to directly connect to another Endpoint.
   386  // Implements Endpoint.Connect.
   387  func (e *connectionedEndpoint) Connect(ctx context.Context, server BoundEndpoint) *syserr.Error {
   388  	returnConnect := func(r Receiver, ce ConnectedEndpoint) {
   389  		e.receiver = r
   390  		e.connected = ce
   391  		// Make sure the newly created connected endpoint's write queue is updated
   392  		// to reflect this endpoint's send buffer size.
   393  		if bufSz := e.connected.SetSendBufferSize(e.ops.GetSendBufferSize()); bufSz != e.ops.GetSendBufferSize() {
   394  			e.ops.SetSendBufferSize(bufSz, false /* notify */)
   395  			e.ops.SetReceiveBufferSize(bufSz, false /* notify */)
   396  		}
   397  	}
   398  
   399  	return server.BidirectionalConnect(ctx, e, returnConnect)
   400  }
   401  
   402  // Listen starts listening on the connection.
   403  func (e *connectionedEndpoint) Listen(ctx context.Context, backlog int) *syserr.Error {
   404  	e.Lock()
   405  	defer e.Unlock()
   406  	if e.ListeningLocked() {
   407  		// Adjust the size of the channel iff we can fix existing
   408  		// pending connections into the new one.
   409  		if len(e.acceptedChan) > backlog {
   410  			return syserr.ErrInvalidEndpointState
   411  		}
   412  		origChan := e.acceptedChan
   413  		e.acceptedChan = make(chan *connectionedEndpoint, backlog)
   414  		close(origChan)
   415  		for ep := range origChan {
   416  			e.acceptedChan <- ep
   417  		}
   418  		if e.boundSocketFD != nil {
   419  			if err := e.boundSocketFD.Listen(ctx, int32(backlog)); err != nil {
   420  				return syserr.FromError(err)
   421  			}
   422  		}
   423  		return nil
   424  	}
   425  	if !e.isBound() {
   426  		return syserr.ErrInvalidEndpointState
   427  	}
   428  
   429  	// Normal case.
   430  	e.acceptedChan = make(chan *connectionedEndpoint, backlog)
   431  	if e.boundSocketFD != nil {
   432  		if err := e.boundSocketFD.Listen(ctx, int32(backlog)); err != nil {
   433  			return syserr.FromError(err)
   434  		}
   435  	}
   436  
   437  	return nil
   438  }
   439  
   440  // Accept accepts a new connection.
   441  func (e *connectionedEndpoint) Accept(ctx context.Context, peerAddr *Address) (Endpoint, *syserr.Error) {
   442  	e.Lock()
   443  
   444  	if !e.ListeningLocked() {
   445  		e.Unlock()
   446  		return nil, syserr.ErrInvalidEndpointState
   447  	}
   448  
   449  	ne, err := e.getAcceptedEndpointLocked(ctx)
   450  	e.Unlock()
   451  	if err != nil {
   452  		return nil, err
   453  	}
   454  
   455  	if peerAddr != nil {
   456  		ne.Lock()
   457  		c := ne.connected
   458  		ne.Unlock()
   459  		if c != nil {
   460  			addr, err := c.GetLocalAddress()
   461  			if err != nil {
   462  				return nil, syserr.TranslateNetstackError(err)
   463  			}
   464  			*peerAddr = addr
   465  		}
   466  	}
   467  	return ne, nil
   468  }
   469  
   470  // Preconditions:
   471  //   - e.Listening()
   472  //   - e is locked.
   473  func (e *connectionedEndpoint) getAcceptedEndpointLocked(ctx context.Context) (*connectionedEndpoint, *syserr.Error) {
   474  	// Accept connections from within the sentry first, since this avoids
   475  	// an RPC to the gofer on the common path.
   476  	select {
   477  	case ne := <-e.acceptedChan:
   478  		return ne, nil
   479  	default:
   480  		// No internal connections.
   481  	}
   482  
   483  	if e.boundSocketFD == nil {
   484  		return nil, syserr.ErrWouldBlock
   485  	}
   486  
   487  	// Check for external connections.
   488  	nfd, err := e.boundSocketFD.Accept(ctx)
   489  	if err == unix.EWOULDBLOCK {
   490  		return nil, syserr.ErrWouldBlock
   491  	}
   492  	if err != nil {
   493  		return nil, syserr.FromError(err)
   494  	}
   495  	q := &waiter.Queue{}
   496  	scme, serr := NewSCMEndpoint(nfd, q, e.path)
   497  	if serr != nil {
   498  		unix.Close(nfd)
   499  		return nil, serr
   500  	}
   501  	scme.Init()
   502  	return NewExternal(e.stype, e.idGenerator, q, scme, scme).(*connectionedEndpoint), nil
   503  
   504  }
   505  
   506  // Bind binds the connection.
   507  //
   508  // For Unix connectionedEndpoints, this _only sets the address associated with
   509  // the socket_. Work associated with sockets in the filesystem or finding those
   510  // sockets must be done by a higher level.
   511  //
   512  // Bind will fail only if the socket is connected, bound or the passed address
   513  // is invalid (the empty string).
   514  func (e *connectionedEndpoint) Bind(addr Address) *syserr.Error {
   515  	e.Lock()
   516  	defer e.Unlock()
   517  	if e.isBound() || e.ListeningLocked() {
   518  		return syserr.ErrAlreadyBound
   519  	}
   520  	if addr.Addr == "" {
   521  		// The empty string is not permitted.
   522  		return syserr.ErrBadLocalAddress
   523  	}
   524  
   525  	// Save the bound address.
   526  	e.path = addr.Addr
   527  	return nil
   528  }
   529  
   530  // SendMsg writes data and a control message to the endpoint's peer.
   531  // This method does not block if the data cannot be written.
   532  func (e *connectionedEndpoint) SendMsg(ctx context.Context, data [][]byte, c ControlMessages, to BoundEndpoint) (int64, func(), *syserr.Error) {
   533  	// Stream sockets do not support specifying the endpoint. Seqpacket
   534  	// sockets ignore the passed endpoint.
   535  	if e.stype == linux.SOCK_STREAM && to != nil {
   536  		return 0, nil, syserr.ErrNotSupported
   537  	}
   538  	return e.baseEndpoint.SendMsg(ctx, data, c, to)
   539  }
   540  
   541  func (e *connectionedEndpoint) isBoundSocketReadable() bool {
   542  	if e.boundSocketFD == nil {
   543  		return false
   544  	}
   545  	return fdnotifier.NonBlockingPoll(e.boundSocketFD.NotificationFD(), waiter.ReadableEvents)&waiter.ReadableEvents != 0
   546  }
   547  
   548  // Readiness returns the current readiness of the connectionedEndpoint. For
   549  // example, if waiter.EventIn is set, the connectionedEndpoint is immediately
   550  // readable.
   551  func (e *connectionedEndpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
   552  	e.Lock()
   553  	defer e.Unlock()
   554  
   555  	ready := waiter.EventMask(0)
   556  	switch {
   557  	case e.Connected():
   558  		if mask&waiter.ReadableEvents != 0 && e.receiver.Readable() {
   559  			ready |= waiter.ReadableEvents
   560  		}
   561  		if mask&waiter.WritableEvents != 0 && e.connected.Writable() {
   562  			ready |= waiter.WritableEvents
   563  		}
   564  	case e.ListeningLocked():
   565  		if mask&waiter.ReadableEvents != 0 && (len(e.acceptedChan) > 0 || e.isBoundSocketReadable()) {
   566  			ready |= waiter.ReadableEvents
   567  		}
   568  	}
   569  
   570  	return ready
   571  }
   572  
   573  // State implements socket.Socket.State.
   574  func (e *connectionedEndpoint) State() uint32 {
   575  	e.Lock()
   576  	defer e.Unlock()
   577  
   578  	if e.Connected() {
   579  		return linux.SS_CONNECTED
   580  	}
   581  	return linux.SS_UNCONNECTED
   582  }
   583  
   584  // OnSetSendBufferSize implements tcpip.SocketOptionsHandler.OnSetSendBufferSize.
   585  func (e *connectionedEndpoint) OnSetSendBufferSize(v int64) (newSz int64) {
   586  	e.Lock()
   587  	defer e.Unlock()
   588  	if e.Connected() {
   589  		return e.baseEndpoint.connected.SetSendBufferSize(v)
   590  	}
   591  	return v
   592  }
   593  
   594  // WakeupWriters implements tcpip.SocketOptionsHandler.WakeupWriters.
   595  func (e *connectionedEndpoint) WakeupWriters() {}
   596  
   597  // SetBoundSocketFD implement HostBountEndpoint.SetBoundSocketFD.
   598  func (e *connectionedEndpoint) SetBoundSocketFD(ctx context.Context, bsFD BoundSocketFD) error {
   599  	e.Lock()
   600  	defer e.Unlock()
   601  	if e.path != "" || e.boundSocketFD != nil {
   602  		bsFD.Close(ctx)
   603  		return syserr.ErrAlreadyBound.ToError()
   604  	}
   605  	e.boundSocketFD = bsFD
   606  	fdnotifier.AddFD(bsFD.NotificationFD(), e.Queue)
   607  	return nil
   608  }
   609  
   610  // SetBoundSocketFD implement HostBountEndpoint.ResetBoundSocketFD.
   611  func (e *connectionedEndpoint) ResetBoundSocketFD(ctx context.Context) {
   612  	e.Lock()
   613  	defer e.Unlock()
   614  	if e.boundSocketFD == nil {
   615  		return
   616  	}
   617  	fdnotifier.RemoveFD(e.boundSocketFD.NotificationFD())
   618  	e.boundSocketFD.Close(ctx)
   619  	e.boundSocketFD = nil
   620  }
   621  
   622  // EventRegister implements waiter.Waitable.EventRegister.
   623  func (e *connectionedEndpoint) EventRegister(we *waiter.Entry) error {
   624  	if err := e.baseEndpoint.EventRegister(we); err != nil {
   625  		return err
   626  	}
   627  
   628  	e.Lock()
   629  	bsFD := e.boundSocketFD
   630  	e.Unlock()
   631  	if bsFD != nil {
   632  		fdnotifier.UpdateFD(bsFD.NotificationFD())
   633  	}
   634  	return nil
   635  }
   636  
   637  // EventUnregister implements waiter.Waitable.EventUnregister.
   638  func (e *connectionedEndpoint) EventUnregister(we *waiter.Entry) {
   639  	e.baseEndpoint.EventUnregister(we)
   640  
   641  	e.Lock()
   642  	bsFD := e.boundSocketFD
   643  	e.Unlock()
   644  	if bsFD != nil {
   645  		fdnotifier.UpdateFD(bsFD.NotificationFD())
   646  	}
   647  }