387 lines
10 KiB
Go
Raw Normal View History

2024-07-10 19:49:07 +03:00
package backendpb
import (
"context"
"fmt"
2024-12-05 14:19:25 +03:00
"log/slog"
2024-07-10 19:49:07 +03:00
"net/netip"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
2024-12-05 14:19:25 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
2024-07-10 19:49:07 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
2024-10-14 17:44:24 +03:00
"github.com/c2h5oh/datasize"
2024-07-10 19:49:07 +03:00
)
// toInternal converts the protobuf-encoded data into a profile structure and
// its device structures.
2024-10-14 17:44:24 +03:00
//
2024-12-05 14:19:25 +03:00
// TODO(a.garipov): Refactor into methods of [*ProfileStorage].
2024-07-10 19:49:07 +03:00
func (x *DNSProfile) toInternal(
ctx context.Context,
updTime time.Time,
bindSet netutil.SubnetSet,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
mtrc ProfileDBMetrics,
2024-10-14 17:44:24 +03:00
respSzEst datasize.ByteSize,
2024-07-10 19:49:07 +03:00
) (profile *agd.Profile, devices []*agd.Device, err error) {
if x == nil {
return nil, nil, fmt.Errorf("profile is nil")
}
2024-12-05 14:19:25 +03:00
parental, err := x.Parental.toInternal(ctx, errColl, logger)
2024-07-10 19:49:07 +03:00
if err != nil {
return nil, nil, fmt.Errorf("parental: %w", err)
}
m, err := blockingModeToInternal(x.BlockingMode)
if err != nil {
return nil, nil, fmt.Errorf("blocking mode: %w", err)
}
2024-12-05 14:19:25 +03:00
devices, deviceIds := devicesToInternal(ctx, x.Devices, bindSet, errColl, logger, mtrc)
2024-07-10 19:49:07 +03:00
profID, err := agd.NewProfileID(x.DnsId)
if err != nil {
return nil, nil, fmt.Errorf("id: %w", err)
}
var fltRespTTL time.Duration
if respTTL := x.FilteredResponseTtl; respTTL != nil {
fltRespTTL = respTTL.AsDuration()
}
2024-12-05 14:19:25 +03:00
customRules := rulesToInternal(ctx, x.CustomRules, errColl, logger)
custom := &filter.ConfigCustom{
ID: string(x.DnsId),
UpdateTime: updTime,
Rules: customRules,
// TODO(a.garipov): Consider adding an explicit flag to the protocol.
Enabled: len(customRules) > 0,
}
2024-07-10 19:49:07 +03:00
return &agd.Profile{
2024-12-05 14:19:25 +03:00
FilterConfig: &filter.ConfigClient{
Custom: custom,
Parental: parental,
RuleList: x.RuleLists.toInternal(ctx, errColl, logger),
SafeBrowsing: x.SafeBrowsing.toInternal(),
},
Access: x.Access.toInternal(ctx, errColl, logger),
2024-07-10 19:49:07 +03:00
BlockingMode: m,
2024-12-05 14:19:25 +03:00
Ratelimiter: x.RateLimit.toInternal(ctx, errColl, logger, respSzEst),
2024-07-10 19:49:07 +03:00
ID: profID,
DeviceIDs: deviceIds,
FilteredResponseTTL: fltRespTTL,
2024-12-05 14:19:25 +03:00
AutoDevicesEnabled: x.AutoDevicesEnabled,
BlockChromePrefetch: x.BlockChromePrefetch,
2024-07-10 19:49:07 +03:00
BlockFirefoxCanary: x.BlockFirefoxCanary,
2024-12-05 14:19:25 +03:00
BlockPrivateRelay: x.BlockPrivateRelay,
Deleted: x.Deleted,
FilteringEnabled: x.FilteringEnabled,
2024-07-10 19:49:07 +03:00
IPLogEnabled: x.IpLogEnabled,
2024-12-05 14:19:25 +03:00
QueryLogEnabled: x.QueryLogEnabled,
2024-07-10 19:49:07 +03:00
}, devices, nil
}
2024-12-05 14:19:25 +03:00
// toInternal converts a protobuf parental-protection settings structure to an
// internal one. If x is nil, toInternal returns a disabled configuration.
2024-07-10 19:49:07 +03:00
func (x *ParentalSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
) (c *filter.ConfigParental, err error) {
c = &filter.ConfigParental{}
2024-07-10 19:49:07 +03:00
if x == nil {
2024-12-05 14:19:25 +03:00
return c, nil
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
c.AdultBlockingEnabled = x.BlockAdult
c.BlockedServices = blockedSvcsToInternal(ctx, errColl, logger, x.BlockedServices)
c.Enabled = x.Enabled
c.SafeSearchGeneralEnabled = x.GeneralSafeSearch
c.SafeSearchYouTubeEnabled = x.YoutubeSafeSearch
c.PauseSchedule, err = x.Schedule.toInternal()
2024-07-10 19:49:07 +03:00
if err != nil {
2024-12-05 14:19:25 +03:00
return nil, fmt.Errorf("pause schedule: %w", err)
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
return c, nil
2024-07-10 19:49:07 +03:00
}
2024-10-14 17:44:24 +03:00
// toInternal converts protobuf rate-limiting settings to an internal structure.
// If x is nil, toInternal returns [agd.GlobalRatelimiter].
func (x *RateLimitSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
2024-10-14 17:44:24 +03:00
respSzEst datasize.ByteSize,
) (r agd.Ratelimiter) {
if x == nil || !x.Enabled {
return agd.GlobalRatelimiter{}
}
return agd.NewDefaultRatelimiter(&agd.RatelimitConfig{
2024-12-05 14:19:25 +03:00
ClientSubnets: cidrRangeToInternal(ctx, errColl, logger, x.ClientCidr),
2024-10-14 17:44:24 +03:00
RPS: x.Rps,
Enabled: x.Enabled,
}, respSzEst)
}
2024-12-05 14:19:25 +03:00
// toInternal converts protobuf safe-browsing settings to an internal
// safe-browsing configuration. If x is nil, toInternal returns a disabled
// configuration.
func (x *SafeBrowsingSettings) toInternal() (c *filter.ConfigSafeBrowsing) {
c = &filter.ConfigSafeBrowsing{}
2024-07-10 19:49:07 +03:00
if x == nil {
2024-12-05 14:19:25 +03:00
return c
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
c.Enabled = x.Enabled
c.DangerousDomainsEnabled = x.BlockDangerousDomains
c.NewlyRegisteredDomainsEnabled = x.BlockNrd
return c
2024-07-10 19:49:07 +03:00
}
// toInternal converts protobuf access settings to an internal structure. If x
// is nil, toInternal returns [access.EmptyProfile].
func (x *AccessSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
2024-07-10 19:49:07 +03:00
) (a access.Profile) {
if x == nil || !x.Enabled {
return access.EmptyProfile{}
}
return access.NewDefaultProfile(&access.ProfileConfig{
2024-12-05 14:19:25 +03:00
AllowedNets: cidrRangeToInternal(ctx, errColl, logger, x.AllowlistCidr),
BlockedNets: cidrRangeToInternal(ctx, errColl, logger, x.BlocklistCidr),
2024-07-10 19:49:07 +03:00
AllowedASN: asnToInternal(x.AllowlistAsn),
BlockedASN: asnToInternal(x.BlocklistAsn),
BlocklistDomainRules: x.BlocklistDomainRules,
})
}
// cidrRangeToInternal is a helper that converts a slice of CidrRange to the
// slice of [netip.Prefix].
func cidrRangeToInternal(
ctx context.Context,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
2024-07-10 19:49:07 +03:00
cidrs []*CidrRange,
) (out []netip.Prefix) {
for i, c := range cidrs {
addr, ok := netip.AddrFromSlice(c.Address)
if !ok {
2024-12-05 14:19:25 +03:00
err := fmt.Errorf("bad cidr at index %d: %v", i, c.Address)
errcoll.Collect(ctx, errColl, logger, "converting cidrs", err)
2024-07-10 19:49:07 +03:00
continue
}
out = append(out, netip.PrefixFrom(addr, int(c.Prefix)))
}
return out
}
// asnToInternal is a helper that converts a slice of ASNs to the slice of
// [geoip.ASN].
func asnToInternal(asns []uint32) (out []geoip.ASN) {
for _, asn := range asns {
out = append(out, geoip.ASN(asn))
}
return out
}
// blockedSvcsToInternal is a helper that converts the blocked service IDs from
2024-12-05 14:19:25 +03:00
// the backend response to AdGuard DNS blocked-service IDs.
2024-07-10 19:49:07 +03:00
func blockedSvcsToInternal(
ctx context.Context,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
2024-07-10 19:49:07 +03:00
respSvcs []string,
2024-12-05 14:19:25 +03:00
) (ids []filter.BlockedServiceID) {
2024-07-10 19:49:07 +03:00
l := len(respSvcs)
if l == 0 {
return nil
}
2024-12-05 14:19:25 +03:00
ids = make([]filter.BlockedServiceID, 0, l)
for i, idStr := range respSvcs {
id, err := filter.NewBlockedServiceID(idStr)
2024-07-10 19:49:07 +03:00
if err != nil {
2024-12-05 14:19:25 +03:00
err = fmt.Errorf("at index %d: %w", i, err)
errcoll.Collect(ctx, errColl, logger, "converting blocked services", err)
2024-07-10 19:49:07 +03:00
continue
}
2024-12-05 14:19:25 +03:00
ids = append(ids, id)
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
return ids
2024-07-10 19:49:07 +03:00
}
// toInternal converts a protobuf protection-schedule structure to an internal
// one. If x is nil, toInternal returns nil.
2024-12-05 14:19:25 +03:00
func (x *ScheduleSettings) toInternal() (c *filter.ConfigSchedule, err error) {
2024-07-10 19:49:07 +03:00
if x == nil {
return nil, nil
}
2024-12-05 14:19:25 +03:00
c = &filter.ConfigSchedule{
Week: &filter.WeeklySchedule{},
}
2024-07-10 19:49:07 +03:00
2024-12-05 14:19:25 +03:00
c.TimeZone, err = agdtime.LoadLocation(x.Tmz)
2024-07-10 19:49:07 +03:00
if err != nil {
return nil, fmt.Errorf("loading timezone: %w", err)
}
w := x.WeeklyRange
days := []*DayRange{w.Sun, w.Mon, w.Tue, w.Wed, w.Thu, w.Fri, w.Sat}
for i, d := range days {
if d == nil {
continue
}
2024-12-05 14:19:25 +03:00
ivl := &filter.DayInterval{
2024-07-10 19:49:07 +03:00
Start: uint16(d.Start.AsDuration().Minutes()),
2024-12-05 14:19:25 +03:00
End: uint16(d.End.AsDuration().Minutes() + 1),
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
err = ivl.Validate()
2024-07-10 19:49:07 +03:00
if err != nil {
return nil, fmt.Errorf("weekday %s: %w", time.Weekday(i), err)
}
2024-12-05 14:19:25 +03:00
c.Week[i] = ivl
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
return c, nil
2024-07-10 19:49:07 +03:00
}
// toInternal converts a protobuf custom blocking-mode to an internal one.
// Assumes that at least one IP address is specified in the result blocking-mode
// object.
func (pbm *BlockingModeCustomIP) toInternal() (m dnsmsg.BlockingMode, err error) {
custom := &dnsmsg.BlockingModeCustomIP{}
// TODO(a.garipov): Only one IPv4 address is supported on protobuf side.
var ipv4Addr netip.Addr
err = ipv4Addr.UnmarshalBinary(pbm.Ipv4)
if err != nil {
return nil, fmt.Errorf("bad custom ipv4: %w", err)
} else if ipv4Addr.IsValid() {
custom.IPv4 = []netip.Addr{ipv4Addr}
}
// TODO(a.garipov): Only one IPv6 address is supported on protobuf side.
var ipv6Addr netip.Addr
err = ipv6Addr.UnmarshalBinary(pbm.Ipv6)
if err != nil {
return nil, fmt.Errorf("bad custom ipv6: %w", err)
} else if ipv6Addr.IsValid() {
custom.IPv6 = []netip.Addr{ipv6Addr}
}
if len(custom.IPv4)+len(custom.IPv6) == 0 {
return nil, errors.Error("no valid custom ips found")
}
return custom, nil
}
// blockingModeToInternal converts a protobuf blocking-mode sum-type to an
// internal one. If pbm is nil, blockingModeToInternal returns a null-IP
// blocking mode.
func blockingModeToInternal(pbm isDNSProfile_BlockingMode) (m dnsmsg.BlockingMode, err error) {
switch pbm := pbm.(type) {
case nil:
return &dnsmsg.BlockingModeNullIP{}, nil
case *DNSProfile_BlockingModeCustomIp:
return pbm.BlockingModeCustomIp.toInternal()
case *DNSProfile_BlockingModeNxdomain:
return &dnsmsg.BlockingModeNXDOMAIN{}, nil
case *DNSProfile_BlockingModeNullIp:
return &dnsmsg.BlockingModeNullIP{}, nil
case *DNSProfile_BlockingModeRefused:
return &dnsmsg.BlockingModeREFUSED{}, nil
default:
// Consider unhandled type-switch cases programmer errors.
return nil, fmt.Errorf("bad pb blocking mode %T(%[1]v)", pbm)
}
}
// rulesToInternal is a helper that converts the filter rules from the backend
// response to AdGuard DNS filtering rules.
func rulesToInternal(
ctx context.Context,
respRules []string,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
) (rules []filter.RuleText) {
2024-07-10 19:49:07 +03:00
l := len(respRules)
if l == 0 {
return nil
}
2024-12-05 14:19:25 +03:00
rules = make([]filter.RuleText, 0, l)
2024-07-10 19:49:07 +03:00
for i, r := range respRules {
2024-12-05 14:19:25 +03:00
text, err := filter.NewRuleText(r)
2024-07-10 19:49:07 +03:00
if err != nil {
2024-12-05 14:19:25 +03:00
err = fmt.Errorf("at index %d: %w", i, err)
errcoll.Collect(ctx, errColl, logger, "converting rules", err)
2024-07-10 19:49:07 +03:00
continue
}
rules = append(rules, text)
}
return rules
}
// toInternal is a helper that converts the filter lists from the backend
2024-12-05 14:19:25 +03:00
// response to AdGuard DNS rule-list configuration. If x is nil, toInternal
// returns a disabled configuration.
2024-07-10 19:49:07 +03:00
func (x *RuleListsSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
2024-12-05 14:19:25 +03:00
logger *slog.Logger,
) (c *filter.ConfigRuleList) {
c = &filter.ConfigRuleList{}
2024-07-10 19:49:07 +03:00
if x == nil {
2024-12-05 14:19:25 +03:00
return c
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
c.Enabled = x.Enabled
c.IDs = make([]filter.ID, 0, len(x.Ids))
2024-07-10 19:49:07 +03:00
2024-12-05 14:19:25 +03:00
for i, idStr := range x.Ids {
id, err := filter.NewID(idStr)
2024-07-10 19:49:07 +03:00
if err != nil {
2024-12-05 14:19:25 +03:00
err = fmt.Errorf("at index %d: %w", i, err)
errcoll.Collect(ctx, errColl, logger, "converting filter id", err)
2024-07-10 19:49:07 +03:00
continue
}
2024-12-05 14:19:25 +03:00
c.IDs = append(c.IDs, id)
2024-07-10 19:49:07 +03:00
}
2024-12-05 14:19:25 +03:00
return c
2024-07-10 19:49:07 +03:00
}