gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/tracereplay/replay.go (about) 1 // Copyright 2022 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 tracereplay 16 17 import ( 18 "encoding/json" 19 "errors" 20 "fmt" 21 "io" 22 "os" 23 24 "golang.org/x/sys/unix" 25 "google.golang.org/protobuf/proto" 26 "gvisor.dev/gvisor/pkg/log" 27 pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto" 28 ) 29 30 // Replay implements the functionality required for the "replay" command. 31 type Replay struct { 32 Endpoint string 33 In string 34 } 35 36 // Execute connects to the remote endpoint and replays all messages stored in 37 // the `In` file. 38 func (r *Replay) Execute() error { 39 socket, err := connect(r.Endpoint) 40 if err != nil { 41 return err 42 } 43 defer socket.Close() 44 45 f, err := os.Open(r.In) 46 if err != nil { 47 return err 48 } 49 defer f.Close() 50 51 hdr := make([]byte, len(signature)) 52 if err := readFull(f, hdr); err != nil { 53 return err 54 } 55 if string(hdr) != signature { 56 return fmt.Errorf("%q is not a replay file", r.In) 57 } 58 59 cfgJSON, err := readWithSize(f) 60 if err != nil { 61 return err 62 } 63 cfg := Config{} 64 if err := json.Unmarshal(cfgJSON, &cfg); err != nil { 65 return err 66 } 67 if err := handshake(socket, cfg.Version); err != nil { 68 return err 69 } 70 fmt.Printf("Handshake completed\n") 71 72 for count := 1; ; count++ { 73 bytes, err := readWithSize(f) 74 if err != nil { 75 if errors.Is(err, io.EOF) { 76 break 77 } 78 return err 79 } 80 fmt.Printf("\rReplaying message: %d", count) 81 if _, err := socket.Write(bytes); err != nil { 82 return err 83 } 84 } 85 fmt.Printf("\nDone\n") 86 return nil 87 } 88 89 func connect(endpoint string) (*os.File, error) { 90 log.Debugf("Connecting to %q", endpoint) 91 socket, err := unix.Socket(unix.AF_UNIX, unix.SOCK_SEQPACKET, 0) 92 if err != nil { 93 return nil, fmt.Errorf("socket(AF_UNIX, SOCK_SEQPACKET, 0): %w", err) 94 } 95 f := os.NewFile(uintptr(socket), endpoint) 96 97 addr := unix.SockaddrUnix{Name: endpoint} 98 if err := unix.Connect(int(f.Fd()), &addr); err != nil { 99 _ = f.Close() 100 return nil, fmt.Errorf("connect(%q): %w", endpoint, err) 101 } 102 return f, nil 103 } 104 105 // See common.proto for details about the handshake protocol. 106 func handshake(socket *os.File, version uint32) error { 107 hsOut := pb.Handshake{Version: version} 108 out, err := proto.Marshal(&hsOut) 109 if err != nil { 110 return err 111 } 112 if _, err := socket.Write(out); err != nil { 113 return fmt.Errorf("sending handshake message: %w", err) 114 } 115 116 in := make([]byte, 10240) 117 read, err := socket.Read(in) 118 if err != nil && !errors.Is(err, io.EOF) { 119 return fmt.Errorf("reading handshake message: %w", err) 120 } 121 // Protect against the handshake becoming larger than the buffer. 122 if read == len(in) { 123 return fmt.Errorf("handshake message too big") 124 } 125 hsIn := pb.Handshake{} 126 if err := proto.Unmarshal(in[:read], &hsIn); err != nil { 127 return fmt.Errorf("unmarshalling handshake message: %w", err) 128 } 129 130 // Just validate that the message can unmarshall and accept any version from 131 // the server. Will try to replay and see what happens... 132 return nil 133 }