diff --git a/fed/deliver.go b/fed/deliver.go index a3114659..4fdbbc0e 100644 --- a/fed/deliver.go +++ b/fed/deliver.go @@ -174,10 +174,14 @@ func deliver(ctx context.Context, log *slog.Logger, db *sql.DB, activity *ap.Act if to, err := resolver.Resolve(ctx, log, db, actor, actorID, false); err != nil { log.Warn("Failed to resolve a recipient", "to", actorID, "activity", activity.ID, "error", err) - anyFailed = true + if !errors.Is(err, ErrActorGone) && !errors.Is(err, ErrBlockedDomain) { + anyFailed = true + } } else if err := Send(ctx, log, db, actor, resolver, to, buf); err != nil { log.Warn("Failed to send a post", "to", actorID, "activity", activity.ID, "error", err) - anyFailed = true + if !errors.Is(err, ErrBlockedDomain) { + anyFailed = true + } } return true diff --git a/fed/resolve.go b/fed/resolve.go index 7cf9c351..1a1af7a2 100644 --- a/fed/resolve.go +++ b/fed/resolve.go @@ -30,6 +30,7 @@ import ( "io" "io/ioutil" "log/slog" + "net" "net/http" "net/url" "path" @@ -40,6 +41,7 @@ import ( const ( resolverCacheTTL = time.Hour * 24 * 3 resolverMaxIdleConns = 128 + maxInstanceRecoveryTime = time.Hour * 24 * 30 resolverIdleConnTimeout = time.Minute ) @@ -123,10 +125,12 @@ func (r *Resolver) resolve(ctx context.Context, log *slog.Logger, db *sql.DB, fr var actorString string var updated int64 + var sinceLastUpdate time.Duration if err := db.QueryRowContext(ctx, `select actor, updated from persons where id = ?`, to).Scan(&actorString, &updated); err != nil && !errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("Failed to fetch %s cache: %w", to, err) } else if err == nil { - if !isLocal && !offline && time.Now().Sub(time.Unix(updated, 0)) > resolverCacheTTL { + sinceLastUpdate = time.Now().Sub(time.Unix(updated, 0)) + if !isLocal && !offline && sinceLastUpdate > resolverCacheTTL { log.Info("Updating old cache entry for actor", "to", to) update = true } else { @@ -163,6 +167,17 @@ func (r *Resolver) resolve(ctx context.Context, log *slog.Logger, db *sql.DB, fr return nil, fmt.Errorf("Failed to fetch %s: %w", finger, ErrActorGone) } + var ( + urlError *url.Error + opError *net.OpError + dnsError *net.DNSError + ) + // if it's been a while since the last update and the server's domain is expired (NXDOMAIN), actor is gone + if sinceLastUpdate > maxInstanceRecoveryTime && errors.As(err, &urlError) && errors.As(urlError.Err, &opError) && errors.As(opError.Err, &dnsError) && dnsError.IsNotFound { + log.Warn("Server is probably gone, deleting associated objects", "to", to) + deleteActor(ctx, log, db, to) + } + return nil, fmt.Errorf("Failed to fetch %s: %w", finger, err) } defer resp.Body.Close()