mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Makes the naming consistent with the public configuration. There is no behavior change. Updates #4805 PiperOrigin-RevId: 462448505
246 lines
6.2 KiB
Go
246 lines
6.2 KiB
Go
// Copyright 2022 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package server provides a common server implementation that can connect with
|
|
// remote.Remote.
|
|
package server
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"google.golang.org/protobuf/proto"
|
|
"gvisor.dev/gvisor/pkg/cleanup"
|
|
"gvisor.dev/gvisor/pkg/log"
|
|
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
|
|
"gvisor.dev/gvisor/pkg/sentry/seccheck/sinks/remote/wire"
|
|
"gvisor.dev/gvisor/pkg/sync"
|
|
"gvisor.dev/gvisor/pkg/unet"
|
|
)
|
|
|
|
// ClientHandler is used to interface with client that connect to the server.
|
|
type ClientHandler interface {
|
|
// NewClient is called when a new client connects to the server. It returns
|
|
// a handler that will be bound to the client.
|
|
NewClient() (MessageHandler, error)
|
|
}
|
|
|
|
// MessageHandler is used to process messages from a client.
|
|
type MessageHandler interface {
|
|
// Message processes a single message. raw contains the entire unparsed
|
|
// message. hdr is the parser message header and payload is the unparsed
|
|
// message data.
|
|
Message(raw []byte, hdr wire.Header, payload []byte) error
|
|
|
|
// Version returns what wire version of the protocol is supported.
|
|
Version() uint32
|
|
|
|
// Close closes the handler.
|
|
Close()
|
|
}
|
|
|
|
type client struct {
|
|
socket *unet.Socket
|
|
handler MessageHandler
|
|
}
|
|
|
|
func (c client) close() {
|
|
_ = c.socket.Close()
|
|
c.handler.Close()
|
|
}
|
|
|
|
// CommonServer provides common functionality to connect and process messages
|
|
// from different clients. Implementors decide how clients and messages are
|
|
// handled, e.g. counting messages for testing.
|
|
type CommonServer struct {
|
|
// Endpoint is the path to the socket that the server listens to.
|
|
Endpoint string
|
|
|
|
socket *unet.ServerSocket
|
|
|
|
handler ClientHandler
|
|
|
|
cond sync.Cond
|
|
|
|
// +checklocks:cond.L
|
|
clients []client
|
|
}
|
|
|
|
// Init initializes the server. It must be called before it is used.
|
|
func (s *CommonServer) Init(path string, handler ClientHandler) {
|
|
s.Endpoint = path
|
|
s.handler = handler
|
|
s.cond = sync.Cond{L: &sync.Mutex{}}
|
|
}
|
|
|
|
// Start creates the socket file and listens for new connections.
|
|
func (s *CommonServer) Start() error {
|
|
socket, err := unix.Socket(unix.AF_UNIX, unix.SOCK_SEQPACKET, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("socket(AF_UNIX, SOCK_SEQPACKET, 0): %w", err)
|
|
}
|
|
cu := cleanup.Make(func() {
|
|
_ = unix.Close(socket)
|
|
})
|
|
defer cu.Clean()
|
|
|
|
sa := &unix.SockaddrUnix{Name: s.Endpoint}
|
|
if err := unix.Bind(socket, sa); err != nil {
|
|
return fmt.Errorf("bind(%q): %w", s.Endpoint, err)
|
|
}
|
|
|
|
s.socket, err = unet.NewServerSocket(socket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cu.Add(func() { s.socket.Close() })
|
|
|
|
if err := s.socket.Listen(); err != nil {
|
|
return err
|
|
}
|
|
|
|
go s.run()
|
|
cu.Release()
|
|
return nil
|
|
}
|
|
|
|
func (s *CommonServer) run() {
|
|
for {
|
|
socket, err := s.socket.Accept()
|
|
if err != nil {
|
|
// EBADF returns when the socket closes.
|
|
if !errors.Is(err, unix.EBADF) {
|
|
log.Warningf("socket.Accept(): %v", err)
|
|
}
|
|
return
|
|
}
|
|
msgHandler, err := s.handler.NewClient()
|
|
if err != nil {
|
|
log.Warningf("handler.NewClient: %v", err)
|
|
return
|
|
}
|
|
client := client{
|
|
socket: socket,
|
|
handler: msgHandler,
|
|
}
|
|
s.cond.L.Lock()
|
|
s.clients = append(s.clients, client)
|
|
s.cond.Broadcast()
|
|
s.cond.L.Unlock()
|
|
|
|
if err := s.handshake(client); err != nil {
|
|
log.Warningf(err.Error())
|
|
s.closeClient(client)
|
|
continue
|
|
}
|
|
go s.handleClient(client)
|
|
}
|
|
}
|
|
|
|
// handshake performs version exchange with client. See common.proto for details
|
|
// about the protocol.
|
|
func (s *CommonServer) handshake(client client) error {
|
|
var in [1024]byte
|
|
read, err := client.socket.Read(in[:])
|
|
if err != nil {
|
|
return fmt.Errorf("reading handshake message: %w", err)
|
|
}
|
|
hsIn := pb.Handshake{}
|
|
if err := proto.Unmarshal(in[:read], &hsIn); err != nil {
|
|
return fmt.Errorf("unmarshalling handshake message: %w", err)
|
|
}
|
|
if hsIn.Version != wire.CurrentVersion {
|
|
return fmt.Errorf("wrong version number, want: %d, got, %d", wire.CurrentVersion, hsIn.Version)
|
|
}
|
|
|
|
hsOut := pb.Handshake{Version: client.handler.Version()}
|
|
out, err := proto.Marshal(&hsOut)
|
|
if err != nil {
|
|
return fmt.Errorf("marshalling handshake message: %w", err)
|
|
}
|
|
if _, err := client.socket.Write(out); err != nil {
|
|
return fmt.Errorf("sending handshake message: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *CommonServer) handleClient(client client) {
|
|
defer s.closeClient(client)
|
|
|
|
var buf = make([]byte, 1024*1024)
|
|
for {
|
|
read, err := client.socket.Read(buf)
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) || errors.Is(err, unix.EBADF) {
|
|
// Both errors indicate that the socket has been closed.
|
|
return
|
|
}
|
|
panic(err)
|
|
}
|
|
if read < wire.HeaderStructSize {
|
|
panic("message too small")
|
|
}
|
|
hdr := wire.Header{}
|
|
hdr.UnmarshalUnsafe(buf[0:wire.HeaderStructSize])
|
|
if read < int(hdr.HeaderSize) {
|
|
panic(fmt.Sprintf("message truncated, header size: %d, read: %d", hdr.HeaderSize, read))
|
|
}
|
|
if err := client.handler.Message(buf[:read], hdr, buf[hdr.HeaderSize:read]); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *CommonServer) closeClient(client client) {
|
|
client.close()
|
|
|
|
// Stop tracking this client.
|
|
s.cond.L.Lock()
|
|
for i, c := range s.clients {
|
|
if c == client {
|
|
s.clients = append(s.clients[:i], s.clients[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
s.cond.Broadcast()
|
|
s.cond.L.Unlock()
|
|
}
|
|
|
|
// Close stops listening and closes all connections.
|
|
func (s *CommonServer) Close() {
|
|
if s.socket != nil {
|
|
_ = s.socket.Close()
|
|
}
|
|
s.cond.L.Lock()
|
|
for _, client := range s.clients {
|
|
client.close()
|
|
}
|
|
s.clients = nil
|
|
s.cond.Broadcast()
|
|
s.cond.L.Unlock()
|
|
_ = os.Remove(s.Endpoint)
|
|
}
|
|
|
|
// WaitForNoClients waits until the number of clients connected reaches 0.
|
|
func (s *CommonServer) WaitForNoClients() {
|
|
s.cond.L.Lock()
|
|
defer s.cond.L.Unlock()
|
|
for len(s.clients) > 0 {
|
|
s.cond.Wait()
|
|
}
|
|
}
|