netbird/relay/messages/message.go

222 lines
6.8 KiB
Go
Raw Normal View History

2024-05-17 17:43:28 +02:00
package messages
import (
"bytes"
2024-07-04 13:42:27 +02:00
"encoding/gob"
2024-05-17 17:43:28 +02:00
"fmt"
log "github.com/sirupsen/logrus"
2024-05-17 17:43:28 +02:00
)
const (
2024-05-23 13:24:02 +02:00
MsgTypeHello MsgType = 0
MsgTypeHelloResponse MsgType = 1
MsgTypeTransport MsgType = 2
2024-06-27 18:40:12 +02:00
MsgTypeClose MsgType = 3
MsgTypeHealthCheck MsgType = 4
2024-07-05 16:12:30 +02:00
sizeOfMsgType = 1
sizeOfMagicBye = 4
headerSizeTransport = sizeOfMsgType + IDSize // 1 byte for msg type, IDSize for peerID
headerSizeHello = sizeOfMsgType + sizeOfMagicBye + IDSize // 1 byte for msg type, 4 byte for magic header, IDSize for peerID
2024-06-26 16:22:26 +02:00
2024-07-10 13:21:50 +02:00
MaxHandshakeSize = 8192
2024-05-17 17:43:28 +02:00
)
var (
ErrInvalidMessageLength = fmt.Errorf("invalid message length")
magicHeader = []byte{0x21, 0x12, 0xA4, 0x42}
2024-06-27 18:40:12 +02:00
healthCheckMsg = []byte{byte(MsgTypeHealthCheck)}
2024-05-17 17:43:28 +02:00
)
type MsgType byte
func (m MsgType) String() string {
switch m {
case MsgTypeHello:
return "hello"
2024-05-23 13:24:02 +02:00
case MsgTypeHelloResponse:
return "hello response"
case MsgTypeTransport:
return "transport"
2024-06-27 18:40:12 +02:00
case MsgTypeClose:
2024-06-05 19:49:30 +02:00
return "close"
2024-07-22 13:04:32 +02:00
case MsgTypeHealthCheck:
return "health check"
default:
return "unknown"
}
}
2024-07-02 11:57:17 +02:00
type HelloResponse struct {
2024-07-05 16:12:30 +02:00
InstanceAddress string
2024-07-02 11:57:17 +02:00
}
2024-07-29 21:53:07 +02:00
// DetermineClientMsgType determines the message type from the first byte of the message
2024-05-17 17:43:28 +02:00
func DetermineClientMsgType(msg []byte) (MsgType, error) {
msgType := MsgType(msg[0])
switch msgType {
case MsgTypeHello:
return msgType, nil
case MsgTypeTransport:
return msgType, nil
2024-06-27 18:40:12 +02:00
case MsgTypeClose:
return msgType, nil
case MsgTypeHealthCheck:
2024-06-05 19:49:30 +02:00
return msgType, nil
2024-05-17 17:43:28 +02:00
default:
2024-06-03 20:14:39 +02:00
return 0, fmt.Errorf("invalid msg type, len: %d", len(msg))
2024-05-17 17:43:28 +02:00
}
}
2024-07-29 21:53:07 +02:00
// DetermineServerMsgType determines the message type from the first byte of the message
2024-05-17 17:43:28 +02:00
func DetermineServerMsgType(msg []byte) (MsgType, error) {
msgType := MsgType(msg[0])
switch msgType {
case MsgTypeHelloResponse:
return msgType, nil
2024-05-17 17:43:28 +02:00
case MsgTypeTransport:
return msgType, nil
2024-06-27 18:40:12 +02:00
case MsgTypeClose:
return msgType, nil
case MsgTypeHealthCheck:
2024-06-05 19:49:30 +02:00
return msgType, nil
2024-05-17 17:43:28 +02:00
default:
return 0, fmt.Errorf("invalid msg type (len: %d)", len(msg))
2024-05-17 17:43:28 +02:00
}
}
// MarshalHelloMsg initial hello message
2024-07-29 21:53:07 +02:00
// The Hello message is the first message sent by a client after establishing a connection with the Relay server. This
// message is used to authenticate the client with the server. The authentication is done using an HMAC method.
// The protocol does not limit to use HMAC, it can be any other method. If the authentication failed the server will
// close the network connection without any response.
2024-07-05 16:12:30 +02:00
func MarshalHelloMsg(peerID []byte, additions []byte) ([]byte, error) {
2024-05-23 13:24:02 +02:00
if len(peerID) != IDSize {
return nil, fmt.Errorf("invalid peerID length: %d", len(peerID))
2024-05-17 17:43:28 +02:00
}
// 5 = 1 byte for msg type, 4 byte for magic header
2024-07-05 16:12:30 +02:00
msg := make([]byte, 5, headerSizeHello+len(additions))
2024-05-17 17:43:28 +02:00
msg[0] = byte(MsgTypeHello)
copy(msg[1:5], magicHeader)
2024-05-23 13:24:02 +02:00
msg = append(msg, peerID...)
2024-07-05 16:12:30 +02:00
msg = append(msg, additions...)
2024-05-17 17:43:28 +02:00
return msg, nil
}
2024-07-29 21:53:07 +02:00
// UnmarshalHelloMsg extracts the peerID and the additional data from the hello message. The Additional data is used to
// authenticate the client with the server.
2024-07-05 16:12:30 +02:00
func UnmarshalHelloMsg(msg []byte) ([]byte, []byte, error) {
if len(msg) < headerSizeHello {
2024-07-05 16:12:30 +02:00
return nil, nil, fmt.Errorf("invalid 'hello' messge")
2024-05-17 17:43:28 +02:00
}
if !bytes.Equal(msg[1:5], magicHeader) {
return nil, nil, fmt.Errorf("invalid magic header")
}
return msg[5 : 5+IDSize], msg[headerSizeHello:], nil
2024-05-17 17:43:28 +02:00
}
2024-07-29 21:53:07 +02:00
// MarshalHelloResponse creates a response message to the hello message.
// In case of success connection the server response with a Hello Response message. This message contains the server's
// instance URL. This URL will be used by choose the common Relay server in case if the peers are in different Relay
// servers.
2024-07-02 11:57:17 +02:00
func MarshalHelloResponse(DomainAddress string) ([]byte, error) {
payload := HelloResponse{
2024-07-05 16:12:30 +02:00
InstanceAddress: DomainAddress,
2024-07-02 11:57:17 +02:00
}
2024-07-04 13:42:27 +02:00
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(payload)
2024-07-02 11:57:17 +02:00
if err != nil {
2024-07-04 13:42:27 +02:00
log.Errorf("failed to gob encode hello response: %s", err)
2024-07-02 11:57:17 +02:00
return nil, err
}
2024-07-04 13:42:27 +02:00
msg := make([]byte, 1, 1+buf.Len())
msg[0] = byte(MsgTypeHelloResponse)
2024-07-04 13:42:27 +02:00
msg = append(msg, buf.Bytes()...)
2024-07-02 11:57:17 +02:00
return msg, nil
}
2024-07-29 21:53:07 +02:00
// UnmarshalHelloResponse extracts the instance address from the hello response message
2024-07-02 11:57:17 +02:00
func UnmarshalHelloResponse(msg []byte) (string, error) {
if len(msg) < 2 {
return "", fmt.Errorf("invalid 'hello response' message")
}
payload := HelloResponse{}
2024-07-04 13:42:27 +02:00
buf := bytes.NewBuffer(msg[1:])
dec := gob.NewDecoder(buf)
err := dec.Decode(&payload)
2024-07-02 11:57:17 +02:00
if err != nil {
2024-07-04 13:42:27 +02:00
log.Errorf("failed to gob decode hello response: %s", err)
2024-07-02 11:57:17 +02:00
return "", err
}
2024-07-05 16:12:30 +02:00
return payload.InstanceAddress, nil
}
2024-07-29 21:53:07 +02:00
// MarshalCloseMsg creates a close message.
// The close message is used to close the connection gracefully between the client and the server. The server and the
// client can send this message. After receiving this message, the server or client will close the connection.
2024-06-05 19:49:30 +02:00
func MarshalCloseMsg() []byte {
msg := make([]byte, 1)
2024-06-27 18:40:12 +02:00
msg[0] = byte(MsgTypeClose)
2024-07-22 13:04:32 +02:00
return msg
2024-06-05 19:49:30 +02:00
}
2024-07-29 21:53:07 +02:00
// MarshalTransportMsg creates a transport message.
// The transport message is used to exchange data between peers. The message contains the data to be exchanged and the
// destination peer hashed ID.
func MarshalTransportMsg(peerID []byte, payload []byte) ([]byte, error) {
2024-05-23 13:24:02 +02:00
if len(peerID) != IDSize {
return nil, fmt.Errorf("invalid peerID length: %d", len(peerID))
2024-05-17 17:43:28 +02:00
}
msg := make([]byte, headerSizeTransport, headerSizeTransport+len(payload))
2024-05-17 17:43:28 +02:00
msg[0] = byte(MsgTypeTransport)
2024-05-23 13:24:02 +02:00
copy(msg[1:], peerID)
2024-05-17 17:43:28 +02:00
msg = append(msg, payload...)
return msg, nil
2024-05-17 17:43:28 +02:00
}
2024-07-29 21:53:07 +02:00
// UnmarshalTransportMsg extracts the peerID and the payload from the transport message.
func UnmarshalTransportMsg(buf []byte) ([]byte, []byte, error) {
if len(buf) < headerSizeTransport {
return nil, nil, ErrInvalidMessageLength
2024-05-17 17:43:28 +02:00
}
return buf[1:headerSizeTransport], buf[headerSizeTransport:], nil
2024-05-17 17:43:28 +02:00
}
2024-07-29 21:53:07 +02:00
// UnmarshalTransportID extracts the peerID from the transport message.
2024-05-23 13:24:02 +02:00
func UnmarshalTransportID(buf []byte) ([]byte, error) {
if len(buf) < headerSizeTransport {
log.Debugf("invalid message length: %d, expected: %d, %x", len(buf), headerSizeTransport, buf)
2024-05-23 13:24:02 +02:00
return nil, ErrInvalidMessageLength
2024-05-17 17:43:28 +02:00
}
return buf[1:headerSizeTransport], nil
2024-05-17 17:43:28 +02:00
}
2024-07-29 21:53:07 +02:00
// UpdateTransportMsg updates the peerID in the transport message.
// With this function the server can reuse the given byte slice to update the peerID in the transport message. So do
// need to allocate a new byte slice.
2024-05-23 13:24:02 +02:00
func UpdateTransportMsg(msg []byte, peerID []byte) error {
if len(msg) < 1+len(peerID) {
2024-05-17 17:43:28 +02:00
return ErrInvalidMessageLength
}
2024-05-23 13:24:02 +02:00
copy(msg[1:], peerID)
2024-05-17 17:43:28 +02:00
return nil
}
2024-06-27 18:40:12 +02:00
2024-07-29 21:53:07 +02:00
// MarshalHealthcheck creates a health check message.
// Health check message is sent by the server periodically. The client will respond with a health check response
// message. If the client does not respond to the health check message, the server will close the connection.
2024-06-27 18:40:12 +02:00
func MarshalHealthcheck() []byte {
return healthCheckMsg
}