diff --git a/protocol/cloudflare/datagram_v3.go b/protocol/cloudflare/datagram_v3.go index b8c9796d5..c40459117 100644 --- a/protocol/cloudflare/datagram_v3.go +++ b/protocol/cloudflare/datagram_v3.go @@ -142,6 +142,10 @@ func (m *DatagramV3Muxer) handleRegistration(ctx context.Context, data []byte) { if closeAfterIdle == 0 { closeAfterIdle = 210 * time.Second } + if !destination.Addr().IsValid() || destination.Addr().IsUnspecified() || destination.Port() == 0 { + m.sendRegistrationResponse(requestID, v3ResponseDestinationUnreachable, "") + return + } m.sessionAccess.Lock() if existing, exists := m.sessions[requestID]; exists { diff --git a/protocol/cloudflare/datagram_v3_test.go b/protocol/cloudflare/datagram_v3_test.go new file mode 100644 index 000000000..ae41d8ca9 --- /dev/null +++ b/protocol/cloudflare/datagram_v3_test.go @@ -0,0 +1,38 @@ +//go:build with_cloudflare_tunnel + +package cloudflare + +import ( + "context" + "encoding/binary" + "testing" + + "github.com/sagernet/sing-box/adapter/inbound" + C "github.com/sagernet/sing-box/constant" +) + +func TestDatagramV3RegistrationDestinationUnreachable(t *testing.T) { + sender := &captureDatagramSender{} + inboundInstance := &Inbound{ + Adapter: inbound.NewAdapter(C.TypeCloudflareTunnel, "test"), + flowLimiter: &FlowLimiter{}, + } + muxer := NewDatagramV3Muxer(inboundInstance, sender, nil) + + requestID := RequestID{} + requestID[15] = 1 + payload := make([]byte, 1+2+2+16+4) + payload[0] = 0 + binary.BigEndian.PutUint16(payload[1:3], 0) + binary.BigEndian.PutUint16(payload[3:5], 30) + copy(payload[5:21], requestID[:]) + copy(payload[21:25], []byte{0, 0, 0, 0}) + + muxer.handleRegistration(context.Background(), payload) + if len(sender.sent) != 1 { + t.Fatalf("expected one registration response, got %d", len(sender.sent)) + } + if sender.sent[0][0] != byte(DatagramV3TypeRegistrationResponse) || sender.sent[0][1] != v3ResponseDestinationUnreachable { + t.Fatalf("unexpected datagram response: %v", sender.sent[0]) + } +}