diff --git a/common/protoext/extensions.proto b/common/protoext/extensions.proto index 94f86c7e7..26dd427bf 100644 --- a/common/protoext/extensions.proto +++ b/common/protoext/extensions.proto @@ -25,4 +25,9 @@ message FieldOpt{ repeated string any_wants = 1; repeated string allowed_values = 2; repeated string allowed_value_types = 3; + + // convert_time_read_file_into read a file into another field, and clear this field during input parsing + string convert_time_read_file_into = 4; + // forbidden marks a boolean to be inaccessible to user + bool forbidden = 5; } \ No newline at end of file diff --git a/infra/conf/protofilter/filter.go b/infra/conf/protofilter/filter.go new file mode 100644 index 000000000..0743c0caf --- /dev/null +++ b/infra/conf/protofilter/filter.go @@ -0,0 +1,90 @@ +package protofilter + +import ( + "github.com/v2fly/v2ray-core/v4/common/platform/filesystem" + "github.com/v2fly/v2ray-core/v4/common/protoext" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +//go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen + +func FilterProtoConfig(config proto.Message) error { + messageProtoReflect := config.ProtoReflect() + return filterMessage(messageProtoReflect) +} + +func filterMessage(message protoreflect.Message) error { + var err error + type fileRead struct { + filename string + field string + } + var fileReadingQueue []fileRead + message.Range(func(descriptor protoreflect.FieldDescriptor, value protoreflect.Value) bool { + v2extension, ferr := protoext.GetFieldOptions(descriptor) + if ferr != nil { + if v2extension.Forbidden { + if value.Bool() { + err = newError("a forbidden value is set ", descriptor.FullName()) + return false + } + } + + if v2extension.ConvertTimeReadFileInto != "" { + fileReadingQueue = append(fileReadingQueue, fileRead{ + filename: value.String(), + field: v2extension.ConvertTimeReadFileInto, + }) + } + } + + switch descriptor.Kind() { + case protoreflect.MessageKind: + if descriptor.IsMap() { + err = filterMap(value.Map()) + break + } + if descriptor.IsList() { + err = filterList(value.List()) + break + } + err = filterMessage(value.Message()) + } + return true + }) + + for _, v := range fileReadingQueue { + file, err := filesystem.ReadFile(v.filename) + if err != nil { + return newError("unable to read file").Base(err) + } + field := message.Descriptor().Fields().ByTextName(v.field) + message.Set(field, protoreflect.ValueOf(file)) + } + return nil +} + +func filterMap(mapValue protoreflect.Map) error { + var err error + mapValue.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool { + err = filterMessage(value.Message()) + if err != nil { + return false + } + return true + }) + return err +} + +func filterList(listValue protoreflect.List) error { + var err error + size := listValue.Len() + for i := 0; i < size; i++ { + err = filterMessage(listValue.Get(i).Message()) + if err != nil { + return err + } + } + return nil +}