github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/agent/handshake.go (about) 1 package agent 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 ) 8 9 // magicNumberBytes is a type capable of holding a Mutagen magic byte sequence. 10 type magicNumberBytes [3]byte 11 12 // serverMagicNumber is a byte sequence that is sent by an endpoint server to 13 // identify the start of a Mutagen protocol stream. It is intentionally composed 14 // of bytes that are not (all) printable ASCII characters. The purpose of adding 15 // this magic number to the beginning of streams is to work around agent-style 16 // transports where the underlying transport executable may write error output 17 // to standard output, which would otherwise be interpreted as version 18 // information. By identifying this magic number, we can be sure that we're 19 // talking to a Mutagen stream before we start exchanging version information. 20 var serverMagicNumber = magicNumberBytes{0x05, 0x27, 0x87} 21 22 // clientMagicNumber serves the same purpose as serverMagicNumber, but it is 23 // send by the endpoint client to the endpoint server. It is not as necessary, 24 // since whatever connects to the server should already know what it's doing, 25 // but it serves as an extra sanity check in the world of agent-style 26 // transports. 27 var clientMagicNumber = magicNumberBytes{0x87, 0x27, 0x05} 28 29 // sendMagicNumber sends the Mutagen magic byte sequence to the specified 30 // writer. 31 func sendMagicNumber(writer io.Writer, magicNumber magicNumberBytes) error { 32 _, err := writer.Write(magicNumber[:]) 33 return err 34 } 35 36 // receiveAndCompareMagicNumber reads a Mutagen magic byte sequence from the 37 // specified reader and verifies that it matches what's expected. 38 func receiveAndCompareMagicNumber(reader io.Reader, expected magicNumberBytes) (bool, error) { 39 // Read the bytes. 40 var received magicNumberBytes 41 if _, err := io.ReadFull(reader, received[:]); err != nil { 42 return false, err 43 } 44 45 // Compare the bytes. 46 return received == expected, nil 47 } 48 49 // ClientHandshake performs a client-side handshake on the stream. 50 func ClientHandshake(stream io.ReadWriter) error { 51 // Receive the server's magic number. 52 if magicOk, err := receiveAndCompareMagicNumber(stream, serverMagicNumber); err != nil { 53 return fmt.Errorf("unable to receive server magic number: %w", err) 54 } else if !magicOk { 55 return errors.New("server magic number incorrect") 56 } 57 58 // Send our magic number to the server. 59 if err := sendMagicNumber(stream, clientMagicNumber); err != nil { 60 return fmt.Errorf("unable to send client magic number: %w", err) 61 } 62 63 // Success. 64 return nil 65 } 66 67 // ServerHandshake performs a server-side handshake on the stream. 68 func ServerHandshake(stream io.ReadWriter) error { 69 // Send our magic number to the client. 70 if err := sendMagicNumber(stream, serverMagicNumber); err != nil { 71 return fmt.Errorf("unable to send server magic number: %w", err) 72 } 73 74 // Receive the client's magic number. We treat a mismatch of the magic 75 // number as a transport error as well, because it indicates that we're not 76 // actually talking to a Mutagen client. 77 if magicOk, err := receiveAndCompareMagicNumber(stream, clientMagicNumber); err != nil { 78 return fmt.Errorf("unable to receive client magic number: %w", err) 79 } else if !magicOk { 80 return errors.New("client magic number incorrect") 81 } 82 83 // Success. 84 return nil 85 }