EdgexAgent/device-gps-go/vendor/github.com/openziti/sdk-golang/ziti/edge/messages.go
2025-07-10 20:30:06 +08:00

437 lines
16 KiB
Go

/*
Copyright 2019 NetFoundry Inc.
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
https://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 edge
import (
"encoding/binary"
"github.com/openziti/channel/v3"
"github.com/openziti/foundation/v2/uuidz"
"github.com/openziti/sdk-golang/pb/edge_client_pb"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
ContentTypeConnect = int32(edge_client_pb.ContentType_ConnectType)
ContentTypeStateConnected = int32(edge_client_pb.ContentType_StateConnectedType)
ContentTypeStateClosed = int32(edge_client_pb.ContentType_StateClosedType)
ContentTypeData = int32(edge_client_pb.ContentType_DataType)
ContentTypeDial = int32(edge_client_pb.ContentType_DialType)
ContentTypeDialSuccess = int32(edge_client_pb.ContentType_DialSuccessType)
ContentTypeDialFailed = int32(edge_client_pb.ContentType_DialFailedType)
ContentTypeBind = int32(edge_client_pb.ContentType_BindType)
ContentTypeUnbind = int32(edge_client_pb.ContentType_UnbindType)
ContentTypeStateSessionEnded = int32(edge_client_pb.ContentType_StateSessionEndedType)
ContentTypeProbe = int32(edge_client_pb.ContentType_ProbeType)
ContentTypeUpdateBind = int32(edge_client_pb.ContentType_UpdateBindType)
ContentTypeHealthEvent = int32(edge_client_pb.ContentType_HealthEventType)
ContentTypeTraceRoute = int32(edge_client_pb.ContentType_TraceRouteType)
ContentTypeTraceRouteResponse = int32(edge_client_pb.ContentType_TraceRouteResponseType)
ContentTypeConnInspectRequest = int32(edge_client_pb.ContentType_ConnInspectRequest)
ContentTypeConnInspectResponse = int32(edge_client_pb.ContentType_ConnInspectResponse)
ContentTypeBindSuccess = int32(edge_client_pb.ContentType_BindSuccess)
ContentTypeUpdateToken = int32(edge_client_pb.ContentType_UpdateTokenType)
ContentTypeUpdateTokenSuccess = int32(edge_client_pb.ContentType_UpdateTokenSuccessType)
ContentTypeUpdateTokenFailure = int32(edge_client_pb.ContentType_UpdateTokenFailureType)
ContentTypePostureResponse = int32(edge_client_pb.ContentType_PostureResponseType)
)
const (
// UUIDHeader is put in the reflected range so replies will share the same UUID
UUIDHeader = int32(edge_client_pb.HeaderId_UUID)
ConnIdHeader = int32(edge_client_pb.HeaderId_ConnId)
SeqHeader = int32(edge_client_pb.HeaderId_Seq)
SessionTokenHeader = int32(edge_client_pb.HeaderId_SessionToken)
PublicKeyHeader = int32(edge_client_pb.HeaderId_PublicKey)
CostHeader = int32(edge_client_pb.HeaderId_Cost)
PrecedenceHeader = int32(edge_client_pb.HeaderId_Precedence)
TerminatorIdentityHeader = int32(edge_client_pb.HeaderId_TerminatorIdentity)
TerminatorIdentitySecretHeader = int32(edge_client_pb.HeaderId_TerminatorIdentitySecret)
CallerIdHeader = int32(edge_client_pb.HeaderId_CallerId)
CryptoMethodHeader = int32(edge_client_pb.HeaderId_CryptoMethod)
FlagsHeader = int32(edge_client_pb.HeaderId_Flags)
AppDataHeader = int32(edge_client_pb.HeaderId_AppData)
RouterProvidedConnId = int32(edge_client_pb.HeaderId_RouterProvidedConnId)
HealthStatusHeader = int32(edge_client_pb.HeaderId_HealthStatus)
ErrorCodeHeader = int32(edge_client_pb.HeaderId_ErrorCode)
TimestampHeader = int32(edge_client_pb.HeaderId_Timestamp)
TraceHopCountHeader = int32(edge_client_pb.HeaderId_TraceHopCount)
TraceHopTypeHeader = int32(edge_client_pb.HeaderId_TraceHopType)
TraceHopIdHeader = int32(edge_client_pb.HeaderId_TraceHopId)
TraceSourceRequestIdHeader = int32(edge_client_pb.HeaderId_TraceSourceRequestId)
TraceError = int32(edge_client_pb.HeaderId_TraceError)
ListenerId = int32(edge_client_pb.HeaderId_ListenerId)
ConnTypeHeader = int32(edge_client_pb.HeaderId_ConnType)
SupportsInspectHeader = int32(edge_client_pb.HeaderId_SupportsInspect)
SupportsBindSuccessHeader = int32(edge_client_pb.HeaderId_SupportsBindSuccess)
ConnectionMarkerHeader = int32(edge_client_pb.HeaderId_ConnectionMarker)
CircuitIdHeader = int32(edge_client_pb.HeaderId_CircuitId)
StickinessTokenHeader = int32(edge_client_pb.HeaderId_StickinessToken)
)
const (
ErrorCodeInternal = int32(edge_client_pb.Error_Internal)
ErrorCodeInvalidApiSession = int32(edge_client_pb.Error_InvalidApiSession)
ErrorCodeInvalidSession = int32(edge_client_pb.Error_InvalidSession)
ErrorCodeWrongSessionType = int32(edge_client_pb.Error_WrongSessionType)
ErrorCodeInvalidEdgeRouterForSession = int32(edge_client_pb.Error_InvalidEdgeRouterForSession)
ErrorCodeInvalidService = int32(edge_client_pb.Error_InvalidService)
ErrorCodeTunnelingNotEnabled = int32(edge_client_pb.Error_TunnelingNotEnabled)
ErrorCodeInvalidTerminator = int32(edge_client_pb.Error_InvalidTerminator)
ErrorCodeInvalidPrecedence = int32(edge_client_pb.Error_InvalidPrecedence)
ErrorCodeInvalidCost = int32(edge_client_pb.Error_InvalidCost)
ErrorCodeEncryptionDataMissing = int32(edge_client_pb.Error_EncryptionDataMissing)
)
const (
PrecedenceDefault = Precedence(edge_client_pb.PrecedenceValue_Default)
PrecedenceRequired = Precedence(edge_client_pb.PrecedenceValue_Required)
PrecedenceFailed = Precedence(edge_client_pb.PrecedenceValue_Failed)
)
const (
// CryptoMethodLibsodium are used to indicate the crypto engine in use
CryptoMethodLibsodium CryptoMethod = 0 // default: crypto_kx_*, crypto_secretstream_*
CryptoMethodSSL CryptoMethod = 1 // OpenSSL(possibly with FIPS): ECDH, AES256-GCM
)
const (
// FIN is an edge payload flag used to signal communication ends
FIN = uint32(edge_client_pb.Flag_FIN)
// TRACE_UUID indicates that peer will send data messages with specially constructed UUID headers
TRACE_UUID = uint32(edge_client_pb.Flag_TRACE_UUID)
// MULTIPART indicates that peer can accept multipart data messages
MULTIPART = uint32(edge_client_pb.Flag_MULTIPART)
// STREAM indicates connection with stream semantics
// this allows consolidation of payloads to lower overhead
STREAM = uint32(edge_client_pb.Flag_STREAM)
// MULTIPART_MSG set on data message with multiple payloads
MULTIPART_MSG = uint32(edge_client_pb.Flag_MULTIPART_MSG)
)
type CryptoMethod byte
type Precedence byte
var ContentTypeValue = map[string]int32{
"EdgeConnectType": ContentTypeConnect,
"EdgeStateConnectedType": ContentTypeStateConnected,
"EdgeStateClosedType": ContentTypeStateClosed,
"EdgeDataType": ContentTypeData,
"EdgeDialType": ContentTypeDial,
"EdgeDialSuccessType": ContentTypeDialSuccess,
"EdgeDialFailedType": ContentTypeDialFailed,
"EdgeBindType": ContentTypeBind,
"EdgeUnbindType": ContentTypeUnbind,
"EdgeProbeType": ContentTypeProbe,
"EdgeUpdateTokenType": ContentTypeUpdateToken,
"EdgeUpdateTokenSuccessType": ContentTypeUpdateTokenSuccess,
"EdgeUpdateTokenFailureType": ContentTypeUpdateTokenFailure,
}
var ContentTypeNames = map[int32]string{
ContentTypeConnect: "EdgeConnectType",
ContentTypeStateConnected: "EdgeStateConnectedType",
ContentTypeStateClosed: "EdgeStateClosedType",
ContentTypeData: "EdgeDataType",
ContentTypeDial: "EdgeDialType",
ContentTypeDialSuccess: "EdgeDialSuccessType",
ContentTypeDialFailed: "EdgeDialFailedType",
ContentTypeBind: "EdgeBindType",
ContentTypeUnbind: "EdgeUnbindType",
ContentTypeProbe: "EdgeProbeType",
ContentTypeUpdateToken: "EdgeUpdateTokenType",
ContentTypeUpdateTokenSuccess: "EdgeUpdateTokenSuccessType",
ContentTypeUpdateTokenFailure: "EdgeUpdateTokenFailureType",
}
type MsgEvent struct {
ConnId uint32
Seq uint32
MsgUUID []byte
Msg *channel.Message
}
func newMsg(contentType int32, connId uint32, seq uint32, data []byte) *channel.Message {
msg := channel.NewMessage(contentType, data)
msg.PutUint32Header(ConnIdHeader, connId)
msg.PutUint32Header(SeqHeader, seq)
return msg
}
func NewDataMsg(connId uint32, seq uint32, data []byte) *channel.Message {
return newMsg(ContentTypeData, connId, seq, data)
}
func NewProbeMsg() *channel.Message {
return channel.NewMessage(ContentTypeProbe, nil)
}
func NewTraceRouteMsg(connId uint32, hops uint32, timestamp uint64) *channel.Message {
msg := channel.NewMessage(ContentTypeTraceRoute, nil)
msg.PutUint32Header(ConnIdHeader, connId)
msg.PutUint32Header(TraceHopCountHeader, hops)
msg.PutUint64Header(TimestampHeader, timestamp)
return msg
}
func NewTraceRouteResponseMsg(connId uint32, hops uint32, timestamp uint64, hopType, hopId string) *channel.Message {
msg := channel.NewMessage(ContentTypeTraceRouteResponse, nil)
msg.PutUint32Header(ConnIdHeader, connId)
msg.PutUint32Header(TraceHopCountHeader, hops)
msg.PutUint64Header(TimestampHeader, timestamp)
msg.Headers[TraceHopTypeHeader] = []byte(hopType)
msg.Headers[TraceHopIdHeader] = []byte(hopId)
return msg
}
func NewConnInspectResponse(connId uint32, connType ConnType, state string) *channel.Message {
msg := channel.NewMessage(ContentTypeConnInspectResponse, []byte(state))
msg.PutUint32Header(ConnIdHeader, connId)
msg.PutByteHeader(ConnTypeHeader, byte(connType))
return msg
}
func NewConnectMsg(connId uint32, token string, pubKey []byte, options *DialOptions) *channel.Message {
msg := newMsg(ContentTypeConnect, connId, 0, []byte(token))
if pubKey != nil {
msg.Headers[PublicKeyHeader] = pubKey
msg.PutByteHeader(CryptoMethodHeader, byte(CryptoMethodLibsodium))
}
if options.Identity != "" {
msg.Headers[TerminatorIdentityHeader] = []byte(options.Identity)
}
if options.CallerId != "" {
msg.Headers[CallerIdHeader] = []byte(options.CallerId)
}
if options.AppData != nil {
msg.Headers[AppDataHeader] = options.AppData
}
if options.StickinessToken != nil {
msg.Headers[StickinessTokenHeader] = options.StickinessToken
}
return msg
}
func NewStateConnectedMsg(connId uint32) *channel.Message {
return newMsg(ContentTypeStateConnected, connId, 0, nil)
}
func NewStateClosedMsg(connId uint32, message string) *channel.Message {
return newMsg(ContentTypeStateClosed, connId, 0, []byte(message))
}
func NewDialMsg(connId uint32, token string, callerId string) *channel.Message {
msg := newMsg(ContentTypeDial, connId, 0, []byte(token))
msg.Headers[CallerIdHeader] = []byte(callerId)
return msg
}
func NewBindMsg(connId uint32, token string, pubKey []byte, options *ListenOptions) *channel.Message {
msg := newMsg(ContentTypeBind, connId, 0, []byte(token))
msg.PutBoolHeader(SupportsInspectHeader, true)
msg.PutBoolHeader(SupportsBindSuccessHeader, true)
if pubKey != nil {
msg.Headers[PublicKeyHeader] = pubKey
msg.PutByteHeader(CryptoMethodHeader, byte(CryptoMethodLibsodium))
}
if options.Cost > 0 {
costBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(costBytes, options.Cost)
msg.Headers[CostHeader] = costBytes
}
if options.Precedence != PrecedenceDefault {
msg.PutByteHeader(PrecedenceHeader, byte(options.Precedence))
}
if options.Identity != "" {
msg.PutStringHeader(TerminatorIdentityHeader, options.Identity)
if options.IdentitySecret != "" {
msg.PutStringHeader(TerminatorIdentitySecretHeader, options.IdentitySecret)
}
}
if options.ListenerId != "" {
msg.PutStringHeader(ListenerId, options.ListenerId)
}
msg.PutBoolHeader(RouterProvidedConnId, true)
return msg
}
func NewUnbindMsg(connId uint32, token string) *channel.Message {
return newMsg(ContentTypeUnbind, connId, 0, []byte(token))
}
func NewUpdateBindMsg(connId uint32, token string, cost *uint16, precedence *Precedence) *channel.Message {
msg := newMsg(ContentTypeUpdateBind, connId, 0, []byte(token))
if cost != nil {
msg.PutUint16Header(CostHeader, *cost)
}
if precedence != nil {
msg.Headers[PrecedenceHeader] = []byte{byte(*precedence)}
}
return msg
}
func NewHealthEventMsg(connId uint32, token string, pass bool) *channel.Message {
msg := newMsg(ContentTypeHealthEvent, connId, 0, []byte(token))
msg.PutBoolHeader(HealthStatusHeader, pass)
return msg
}
func NewDialSuccessMsg(connId uint32, newConnId uint32) *channel.Message {
newConnIdBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(newConnIdBytes, newConnId)
msg := newMsg(ContentTypeDialSuccess, connId, 0, newConnIdBytes)
return msg
}
func NewDialFailedMsg(connId uint32, message string) *channel.Message {
return newMsg(ContentTypeDialFailed, connId, 0, []byte(message))
}
func NewStateSessionEndedMsg(reason string) *channel.Message {
return newMsg(ContentTypeStateSessionEnded, 0, 0, []byte(reason))
}
// NewUpdateTokenMsg creates a message sent to edge routers to update the token that
// allows the client to stay connection. If the token is not update before the current
// one expires, the connection and all service connections through it will be terminated.
func NewUpdateTokenMsg(token []byte) *channel.Message {
msg := channel.NewMessage(ContentTypeUpdateToken, token)
return msg
}
// NewUpdateTokenFailedMsg is returned in response to a token update where the token failed
// validation.
func NewUpdateTokenFailedMsg(err error) *channel.Message {
msg := channel.NewMessage(ContentTypeUpdateTokenFailure, []byte(err.Error()))
return msg
}
// NewUpdateTokenSuccessMsg is returned in response to a toke update where the token
// was accepted.
func NewUpdateTokenSuccessMsg() *channel.Message {
msg := channel.NewMessage(ContentTypeUpdateTokenSuccess, nil)
return msg
}
type DialResult struct {
ConnId uint32
NewConnId uint32
Success bool
Message string
}
func UnmarshalDialResult(msg *channel.Message) (*DialResult, error) {
connId, found := msg.GetUint32Header(ConnIdHeader)
if !found {
return nil, errors.Errorf("received edge message with no connection id header")
}
if msg.ContentType == ContentTypeDialSuccess {
if len(msg.Body) != 4 {
return nil, errors.Errorf("dial success msg improperly formatted. body len: %v", len(msg.Body))
}
newConnId := binary.LittleEndian.Uint32(msg.Body)
return &DialResult{
ConnId: connId,
NewConnId: newConnId,
Success: true,
}, nil
}
if msg.ContentType == ContentTypeDialFailed {
return &DialResult{
ConnId: connId,
Success: false,
Message: string(msg.Body),
}, nil
}
return nil, errors.Errorf("unexpected response. received %v instead of dial result message", msg.ContentType)
}
func GetLoggerFields(msg *channel.Message) logrus.Fields {
var msgUUID string
if id, found := msg.Headers[UUIDHeader]; found {
msgUUID = uuidz.ToString(id)
}
connId, _ := msg.GetUint32Header(ConnIdHeader)
seq, _ := msg.GetUint32Header(SeqHeader)
fields := logrus.Fields{
"connId": connId,
"type": ContentTypeNames[msg.ContentType],
"chSeq": msg.Sequence(),
"edgeSeq": seq,
}
if msgUUID != "" {
fields["uuid"] = msgUUID
}
if circuitId, found := msg.GetStringHeader(CircuitIdHeader); found {
fields["circuitId"] = circuitId
}
return fields
}
type ConnType byte
const (
ConnTypeInvalid ConnType = 0
ConnTypeDial ConnType = 1
ConnTypeBind ConnType = 2
ConnTypeUnknown ConnType = 3
)
type InspectResult struct {
ConnId uint32
Type ConnType
Detail string
}
func UnmarshalInspectResult(msg *channel.Message) (*InspectResult, error) {
if msg.ContentType == ContentTypeConnInspectResponse {
connId, _ := msg.GetUint32Header(ConnIdHeader)
connType, found := msg.GetByteHeader(ConnTypeHeader)
if !found {
connType = byte(ConnTypeUnknown)
}
return &InspectResult{
ConnId: connId,
Type: ConnType(connType),
Detail: string(msg.Body),
}, nil
}
return nil, errors.Errorf("unexpected response. received %v instead of inspect result message", msg.ContentType)
}