Skip to main content

Crate abuse_contact

Crate abuse_contact 

Source
Expand description

Find where to report abuse for an IP address or a domain name.

The contact lives in a different place for each kind of target, and the answers are not interchangeable. The registrar of a domain can suspend the name. The network that holds an IP address can take the host off the air. The operator of the domain runs the service. A report goes to the one that can act on it, so this crate returns every contact it finds with the scope it covers, and leaves the choice to you.

§Sources

SourceTargetWhat it gives
RDAPIP, domainThe contact the registry publishes
AbusixIPThe contact for the network, over DNS
abuse.netdomainThe contact the operator registered, over DNS
RFC 2142domainabuse@ at the domain, a guess

§Reading a result

use abuse_contact::{Contact, EmailAddress, Scope, Source, rank};

let found = vec![
    Contact {
        email: EmailAddress::new("abuse@example.com")?,
        scope: Scope::Domain,
        source: Source::Rfc2142,
    },
    Contact {
        email: EmailAddress::new("registrar-abuse@example.net")?,
        scope: Scope::Registrar,
        source: Source::Rdap { server: "rdap.example.net".to_owned() },
    },
];

// The registry answer comes first. The guess comes last.
let ranked = rank(found);
assert_eq!(ranked[0].scope, Scope::Registrar);

// Pick by what you want to happen, not by what is first.
let takedown = ranked.iter().find(|c| c.scope == Scope::Registrar);
assert!(takedown.is_some());

§Design

Values that go out are checked when you build them. DomainName refuses a name that no registry can hold, so a lookup that is certain to find nothing never reaches the network.

Values that come back are not checked the same way. A change on a registry side must not turn a working lookup into a parse error, so rdap::Response reads the few fields an abuse lookup needs and ignores the rest.

One value that comes back is refused: a registry that hides contact data puts a placeholder such as DATA REDACTED in the email field. EmailAddress rejects it, so the placeholder never reaches a mail queue.

A domain takes one request, not two. The registry record names the registrar and carries its abuse address under the registrar entity, so one lookup is enough. rdap::Response::related_href gives the registrar record for the details the registry leaves out, such as the abuse telephone number.

Registries do not agree on where the abuse entity goes, or on whether to publish one. ARIN puts it under the registrant and again at the top level. RIPE, APNIC and LACNIC put it at the top level. APNIC marks the abuse mailbox with pref and lists a help desk beside it. registro.br marks one entity both technical and abuse and gives it no address. AFRINIC publishes no abuse entity at all. The reader walks the whole tree, sorts by preference, and returns each address one time.

RDAP therefore does not answer everywhere. For AFRINIC space and for registro.br space, DNS is the only source that gives an address. Ask more than one source.

§Fetching a record

Client picks the server from the IANA bootstrap registries and fetches the record. It needs the http feature, which is on by default, and its own documentation shows a lookup.

Turn the default features off with default-features = false to take the readers alone, with no HTTP stack and no resolver. Then fetch with the client you already have and call rdap::Response::abuse_contacts, dns::contacts_from_txt and rank on what comes back.

§Asking the DNS sources

Resolver asks the Abusix and abuse.net zones, and checks that a domain takes mail before it gives abuse@ at the domain. It needs the dns feature, which is on by default. For AFRINIC space, where RDAP publishes no abuse entity, Abusix is the only source that answers.

§Asking every source

Finder asks every source for a target at the same time, and gives the contacts ordered by rank. A source that fails does not fail the lookup: its error is returned beside the contacts from the sources that answered. It needs the http and dns features.

Finder holds the RDAP answers in a Cache, so it does not ask a registry about the same network or the same domain again. The answer for an address is held for the whole range the registry returned. The resolver holds the DNS answers, for the TTL of each record.

Modules§

bootstrap
The IANA bootstrap registries, which say who answers for an address or a name.
dns
The DNS sources: Abusix, abuse.net, and the RFC 2142 fallback.
rdap
The RDAP source: the response types, and how to find the abuse contact in one.

Structs§

Cache
The RDAP answers a finder holds, and for how long.
Client
Fetches RDAP records.
Contact
One address that accepts reports, with what it governs and where it was found.
DomainName
A domain name to look up.
EmailAddress
An email address that accepts abuse reports.
Failure
A source that did not answer.
Finder
Asks every source for a target, at the same time, and merges the answers.
Found
What a lookup found: the contacts, and the sources that did not answer.
Record
A record the client fetched, with the server that answered.
Resolver
Looks up the DNS sources.

Enums§

Destinations
Which addresses the client may connect to.
Error
A lookup that did not finish.
Origin
A source that Finder asks.
Query
The thing you want the abuse contact for.
Scope
What the contact has authority over.
Source
Where a contact came from.
ValidationError
A value that cannot be used as a query or as a result.

Constants§

MAX_BOOTSTRAP_BYTES
The most the client reads of a bootstrap registry, in bytes.
MAX_RECORD_BYTES
The most the client reads of a record, in bytes.

Functions§

is_public
Returns whether the public registries describe this address.
rank
Sorts contacts by source rank, then by address, and drops repeats.