2023-05-10 07:24:05 +00:00
|
|
|
package hscontrol
|
2022-08-14 19:15:58 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/json"
|
2023-01-02 06:48:30 +00:00
|
|
|
"sync"
|
2022-08-14 19:15:58 +00:00
|
|
|
|
2023-05-11 07:09:18 +00:00
|
|
|
"github.com/juanfont/headscale/hscontrol/util"
|
2022-08-14 19:15:58 +00:00
|
|
|
"github.com/klauspost/compress/zstd"
|
|
|
|
"github.com/rs/zerolog/log"
|
2023-01-02 06:48:30 +00:00
|
|
|
"tailscale.com/smallzstd"
|
2022-08-14 19:15:58 +00:00
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
"tailscale.com/types/key"
|
|
|
|
)
|
|
|
|
|
2022-08-14 20:50:39 +00:00
|
|
|
func (h *Headscale) getMapResponseData(
|
|
|
|
mapRequest tailcfg.MapRequest,
|
|
|
|
machine *Machine,
|
|
|
|
isNoise bool,
|
|
|
|
) ([]byte, error) {
|
|
|
|
mapResponse, err := h.generateMapResponse(mapRequest, machine)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if isNoise {
|
2022-12-09 16:56:43 +00:00
|
|
|
return h.marshalMapResponse(mapResponse, key.MachinePublic{}, mapRequest.Compress, isNoise)
|
2022-08-14 20:50:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var machineKey key.MachinePublic
|
2023-05-11 07:09:18 +00:00
|
|
|
err = machineKey.UnmarshalText([]byte(util.MachinePublicKeyEnsurePrefix(machine.MachineKey)))
|
2022-08-14 20:50:39 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Cannot parse client key")
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-12-09 16:56:43 +00:00
|
|
|
return h.marshalMapResponse(mapResponse, machineKey, mapRequest.Compress, isNoise)
|
2022-08-14 20:50:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Headscale) getMapKeepAliveResponseData(
|
2022-08-14 19:15:58 +00:00
|
|
|
mapRequest tailcfg.MapRequest,
|
2022-08-14 20:50:39 +00:00
|
|
|
machine *Machine,
|
|
|
|
isNoise bool,
|
2022-08-14 19:15:58 +00:00
|
|
|
) ([]byte, error) {
|
2022-08-14 20:50:39 +00:00
|
|
|
keepAliveResponse := tailcfg.MapResponse{
|
2022-08-14 19:15:58 +00:00
|
|
|
KeepAlive: true,
|
|
|
|
}
|
2022-08-14 20:50:39 +00:00
|
|
|
|
|
|
|
if isNoise {
|
2023-05-11 07:09:18 +00:00
|
|
|
return h.marshalMapResponse(
|
|
|
|
keepAliveResponse,
|
|
|
|
key.MachinePublic{},
|
|
|
|
mapRequest.Compress,
|
|
|
|
isNoise,
|
|
|
|
)
|
2022-08-14 20:50:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var machineKey key.MachinePublic
|
2023-05-11 07:09:18 +00:00
|
|
|
err := machineKey.UnmarshalText([]byte(util.MachinePublicKeyEnsurePrefix(machine.MachineKey)))
|
2022-08-14 20:50:39 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Cannot parse client key")
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-12-09 16:56:43 +00:00
|
|
|
return h.marshalMapResponse(keepAliveResponse, machineKey, mapRequest.Compress, isNoise)
|
2022-08-14 20:50:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Headscale) marshalResponse(
|
|
|
|
resp interface{},
|
|
|
|
machineKey key.MachinePublic,
|
2022-12-09 16:56:43 +00:00
|
|
|
isNoise bool,
|
2022-08-19 12:19:29 +00:00
|
|
|
) ([]byte, error) {
|
|
|
|
jsonBody, err := json.Marshal(resp)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Cannot marshal response")
|
2022-09-04 09:36:03 +00:00
|
|
|
|
|
|
|
return nil, err
|
2022-08-19 12:19:29 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 16:56:43 +00:00
|
|
|
if isNoise {
|
2022-08-19 12:19:29 +00:00
|
|
|
return jsonBody, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return h.privateKey.SealTo(machineKey, jsonBody), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Headscale) marshalMapResponse(
|
|
|
|
resp interface{},
|
|
|
|
machineKey key.MachinePublic,
|
|
|
|
compression string,
|
2022-12-09 16:56:43 +00:00
|
|
|
isNoise bool,
|
2022-08-14 20:50:39 +00:00
|
|
|
) ([]byte, error) {
|
|
|
|
jsonBody, err := json.Marshal(resp)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Cannot marshal map response")
|
|
|
|
}
|
|
|
|
|
2022-08-14 19:15:58 +00:00
|
|
|
var respBody []byte
|
2023-05-11 07:09:18 +00:00
|
|
|
if compression == util.ZstdCompression {
|
2023-01-02 06:48:30 +00:00
|
|
|
respBody = zstdEncode(jsonBody)
|
2022-12-09 16:56:43 +00:00
|
|
|
if !isNoise { // if legacy protocol
|
2022-08-14 20:50:39 +00:00
|
|
|
respBody = h.privateKey.SealTo(machineKey, respBody)
|
|
|
|
}
|
2022-08-14 19:15:58 +00:00
|
|
|
} else {
|
2022-12-09 16:56:43 +00:00
|
|
|
if !isNoise { // if legacy protocol
|
2022-08-14 20:50:39 +00:00
|
|
|
respBody = h.privateKey.SealTo(machineKey, jsonBody)
|
2022-08-14 21:15:41 +00:00
|
|
|
} else {
|
|
|
|
respBody = jsonBody
|
2022-08-14 19:15:58 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-14 20:50:39 +00:00
|
|
|
|
2022-08-14 19:15:58 +00:00
|
|
|
data := make([]byte, reservedResponseHeaderSize)
|
|
|
|
binary.LittleEndian.PutUint32(data, uint32(len(respBody)))
|
|
|
|
data = append(data, respBody...)
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|
2023-01-02 06:48:30 +00:00
|
|
|
|
|
|
|
func zstdEncode(in []byte) []byte {
|
|
|
|
encoder, ok := zstdEncoderPool.Get().(*zstd.Encoder)
|
|
|
|
if !ok {
|
|
|
|
panic("invalid type in sync pool")
|
|
|
|
}
|
|
|
|
out := encoder.EncodeAll(in, nil)
|
|
|
|
_ = encoder.Close()
|
|
|
|
zstdEncoderPool.Put(encoder)
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
var zstdEncoderPool = &sync.Pool{
|
|
|
|
New: func() any {
|
|
|
|
encoder, err := smallzstd.NewEncoder(
|
|
|
|
nil,
|
|
|
|
zstd.WithEncoderLevel(zstd.SpeedFastest))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return encoder
|
|
|
|
},
|
|
|
|
}
|