mirror of
https://github.com/fatedier/frp.git
synced 2026-07-31 05:52:54 +08:00
feat: add negotiated binary UDP packets (#5456)
This commit is contained in:
@@ -68,7 +68,8 @@ func NewServerHello(clientHello ClientHello) (ServerHello, error) {
|
||||
return ServerHello{
|
||||
Selected: ServerSelection{
|
||||
Message: MessageSelection{
|
||||
Codec: MessageCodecJSON,
|
||||
Codec: MessageCodecJSON,
|
||||
UDPPacketCodec: selectUDPPacketCodec(clientHello.Capabilities.Message.UDPPacketCodecs),
|
||||
},
|
||||
Crypto: CryptoSelection{
|
||||
Algorithm: algorithm,
|
||||
@@ -92,6 +93,15 @@ func ValidateServerHelloForClient(clientHello ClientHello, serverHello ServerHel
|
||||
if serverHello.Selected.Message.Codec != MessageCodecJSON {
|
||||
return fmt.Errorf("unsupported selected message codec: %s", serverHello.Selected.Message.Codec)
|
||||
}
|
||||
udpPacketCodec := serverHello.Selected.Message.UDPPacketCodec
|
||||
if udpPacketCodec != "" {
|
||||
if udpPacketCodec != UDPPacketCodecBinary {
|
||||
return fmt.Errorf("unsupported selected UDP packet codec: %s", udpPacketCodec)
|
||||
}
|
||||
if !Supports(clientHello.Capabilities.Message.UDPPacketCodecs, udpPacketCodec) {
|
||||
return fmt.Errorf("selected UDP packet codec was not advertised by client: %s", udpPacketCodec)
|
||||
}
|
||||
}
|
||||
cryptoSelection := serverHello.Selected.Crypto
|
||||
if !IsSupportedAEADAlgorithm(cryptoSelection.Algorithm) {
|
||||
return fmt.Errorf("unknown selected crypto algorithm: %s", cryptoSelection.Algorithm)
|
||||
@@ -105,6 +115,13 @@ func ValidateServerHelloForClient(clientHello ClientHello, serverHello ServerHel
|
||||
return nil
|
||||
}
|
||||
|
||||
func selectUDPPacketCodec(codecs []string) string {
|
||||
if Supports(codecs, UDPPacketCodecBinary) {
|
||||
return UDPPacketCodecBinary
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func NewCryptoContext(algorithm string, clientHelloPayload, serverHelloPayload []byte) *CryptoContext {
|
||||
return &CryptoContext{
|
||||
Algorithm: algorithm,
|
||||
|
||||
@@ -36,6 +36,7 @@ const (
|
||||
FrameTypeMessage uint16 = 16
|
||||
|
||||
MessageCodecJSON = "json"
|
||||
UDPPacketCodecBinary = "binary-v1"
|
||||
DefaultMaxFramePayloadSize = 64 * 1024
|
||||
|
||||
MagicV2 = "FRP\x00\x02\r\n"
|
||||
@@ -182,7 +183,8 @@ type ClientCapabilities struct {
|
||||
}
|
||||
|
||||
type MessageCapabilities struct {
|
||||
Codecs []string `json:"codecs,omitempty"`
|
||||
Codecs []string `json:"codecs,omitempty"`
|
||||
UDPPacketCodecs []string `json:"udpPacketCodecs,omitempty"`
|
||||
}
|
||||
|
||||
type CryptoCapabilities struct {
|
||||
@@ -201,7 +203,8 @@ type ServerSelection struct {
|
||||
}
|
||||
|
||||
type MessageSelection struct {
|
||||
Codec string `json:"codec,omitempty"`
|
||||
Codec string `json:"codec,omitempty"`
|
||||
UDPPacketCodec string `json:"udpPacketCodec,omitempty"`
|
||||
}
|
||||
|
||||
type CryptoSelection struct {
|
||||
@@ -214,7 +217,8 @@ func clientHelloWithCryptoRandom(bootstrap BootstrapInfo, clientRandom []byte) C
|
||||
Bootstrap: bootstrap,
|
||||
Capabilities: ClientCapabilities{
|
||||
Message: MessageCapabilities{
|
||||
Codecs: []string{MessageCodecJSON},
|
||||
Codecs: []string{MessageCodecJSON},
|
||||
UDPPacketCodecs: []string{UDPPacketCodecBinary},
|
||||
},
|
||||
Crypto: CryptoCapabilities{
|
||||
Algorithms: PreferredAEADAlgorithms(),
|
||||
|
||||
@@ -148,10 +148,40 @@ func TestNewServerHelloSelectsFirstSupportedAEADAlgorithm(t *testing.T) {
|
||||
serverHello, err := NewServerHello(hello)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, MessageCodecJSON, serverHello.Selected.Message.Codec)
|
||||
require.Equal(t, UDPPacketCodecBinary, serverHello.Selected.Message.UDPPacketCodec)
|
||||
require.Equal(t, AEADAlgorithmXChaCha20Poly1305, serverHello.Selected.Crypto.Algorithm)
|
||||
require.Len(t, serverHello.Selected.Crypto.ServerRandom, CryptoRandomSize)
|
||||
}
|
||||
|
||||
func TestUDPPacketCodecNegotiationFallbackAndValidation(t *testing.T) {
|
||||
hello := mustClientHello(t, BootstrapInfo{})
|
||||
serverHello, err := NewServerHello(hello)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, UDPPacketCodecBinary, serverHello.Selected.Message.UDPPacketCodec)
|
||||
require.NoError(t, ValidateServerHelloForClient(hello, serverHello))
|
||||
|
||||
legacyHello := hello
|
||||
legacyHello.Capabilities.Message.UDPPacketCodecs = nil
|
||||
legacyServerHello, err := NewServerHello(legacyHello)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, legacyServerHello.Selected.Message.UDPPacketCodec)
|
||||
require.NoError(t, ValidateServerHelloForClient(legacyHello, legacyServerHello))
|
||||
|
||||
unknownOffer := hello
|
||||
unknownOffer.Capabilities.Message.UDPPacketCodecs = []string{"unknown"}
|
||||
unknownServerHello, err := NewServerHello(unknownOffer)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, unknownServerHello.Selected.Message.UDPPacketCodec)
|
||||
|
||||
rejected := serverHello
|
||||
rejected.Selected.Message.UDPPacketCodec = "unknown"
|
||||
require.ErrorContains(t, ValidateServerHelloForClient(hello, rejected), "unsupported selected UDP packet codec")
|
||||
|
||||
unadvertised := serverHello
|
||||
unadvertised.Selected.Message.UDPPacketCodec = UDPPacketCodecBinary
|
||||
require.ErrorContains(t, ValidateServerHelloForClient(legacyHello, unadvertised), "was not advertised")
|
||||
}
|
||||
|
||||
func TestNewClientCryptoContextValidatesServerHello(t *testing.T) {
|
||||
hello := mustClientHello(t, BootstrapInfo{})
|
||||
serverHello, err := NewServerHello(hello)
|
||||
|
||||
Reference in New Issue
Block a user