package thoth import ( "context" "errors" "fmt" "log/slog" "net/http" "strings" "time" "github.com/TecharoHQ/anubis/internal" "github.com/TecharoHQ/anubis/lib/policy/checker" iptoasnv1 "github.com/TecharoHQ/thoth-proto/gen/techaro/thoth/iptoasn/v1" ) func (c *Client) ASNCheckerFor(asns []uint32) checker.Impl { asnMap := map[uint32]struct{}{} var sb strings.Builder fmt.Fprintln(&sb, "ASNChecker") for _, asn := range asns { asnMap[asn] = struct{}{} fmt.Fprintln(&sb, "AS", asn) } return &ASNChecker{ iptoasn: c.IPToASN, asns: asnMap, hash: internal.FastHash(sb.String()), } } type ASNChecker struct { iptoasn iptoasnv1.IpToASNServiceClient asns map[uint32]struct{} hash string } func (asnc *ASNChecker) Check(r *http.Request) (bool, error) { ctx, cancel := context.WithTimeout(r.Context(), 500*time.Millisecond) defer cancel() ipInfo, err := asnc.iptoasn.Lookup(ctx, &iptoasnv1.LookupRequest{ IpAddress: r.Header.Get("X-Real-Ip"), }) if err != nil { switch { case errors.Is(err, context.DeadlineExceeded): slog.Debug("error contacting thoth", "err", err, "actionable", false) return false, nil default: slog.Error("error contacting thoth, please contact support", "err", err, "actionable", true) return false, nil } } // If IP is not publicly announced, return false if !ipInfo.GetAnnounced() { return false, nil } _, ok := asnc.asns[uint32(ipInfo.GetAsNumber())] return ok, nil } func (asnc *ASNChecker) Hash() string { return asnc.hash }