// 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() } }