1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-11-14 00:07:09 -05:00
v2fly/common/protofilter/filter.go
2023-11-26 10:55:27 +00:00

140 lines
3.4 KiB
Go

package protofilter
import (
"context"
"io"
"net"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/environment/filesystemcap"
"github.com/v2fly/v2ray-core/v5/common/protoext"
)
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
func FilterProtoConfig(ctx context.Context, config proto.Message) error {
messageProtoReflect := config.ProtoReflect()
return filterMessage(ctx, messageProtoReflect)
}
func filterMessage(ctx context.Context, message protoreflect.Message) error {
var err error
type fileRead struct {
filename string
field string
}
var fileReadingQueue []fileRead
type pendingWrite struct {
field protoreflect.FieldDescriptor
value protoreflect.Value
}
var pendingWriteQueue []pendingWrite
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,
})
}
if v2extension.ConvertTimeParseIp != "" {
ipValue := net.ParseIP(value.String())
target := message.Descriptor().Fields().ByTextName(v2extension.ConvertTimeParseIp)
if ipValue.To4() != nil {
ipValue = ipValue.To4()
}
pendingWriteQueue = append(pendingWriteQueue, pendingWrite{
field: target,
value: protoreflect.ValueOf([]byte(ipValue)),
})
}
}
switch descriptor.Kind() {
case protoreflect.MessageKind:
if descriptor.IsMap() {
err = filterMap(ctx, value.Map())
break
}
if descriptor.IsList() {
err = filterList(ctx, value.List())
break
}
err = filterMessage(ctx, value.Message())
}
return true
})
if err != nil {
return err
}
fsenvironment := envctx.EnvironmentFromContext(ctx)
fsifce, fsifceOk := fsenvironment.(filesystemcap.FileSystemCapabilitySet)
for _, v := range fileReadingQueue {
if !fsifceOk {
return newError("unable to read file as filesystem capability is not given")
}
field := message.Descriptor().Fields().ByTextName(v.field)
if v.filename == "" {
continue
}
if len(message.Get(field).Bytes()) > 0 {
continue
}
file, err := fsifce.OpenFileForRead()(v.filename)
if err != nil {
return newError("unable to open file").Base(err)
}
fileContent, err := io.ReadAll(file)
if err != nil {
return newError("unable to read file").Base(err)
}
file.Close()
message.Set(field, protoreflect.ValueOf(fileContent))
}
for _, v := range pendingWriteQueue {
message.Set(v.field, v.value)
}
return nil
}
func filterMap(ctx context.Context, mapValue protoreflect.Map) error {
var err error
mapValue.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
err = filterMessage(ctx, value.Message())
return err == nil
})
return err
}
func filterList(ctx context.Context, listValue protoreflect.List) error {
var err error
size := listValue.Len()
for i := 0; i < size; i++ {
err = filterMessage(ctx, listValue.Get(i).Message())
if err != nil {
return err
}
}
return nil
}