package uspf

  1. Overview
  2. Docs
SPF implementation in OCaml

Install

Dune Dependency

Authors

Maintainers

Sources

uspf-0.0.3.tbz
sha256=9e7903a149b1a9708bf33d0b34ea243172feebe60737e81522f487711e910568
sha512=d95c6b61ded3b5fff91a13ab2ea9df43ccec26f0f5fd52fe4f6561575313898947a24b591fb85ea697fd703bd402af494491de5d5e9e0b18c6954c33f92f439f

doc/uspf/Uspf/index.html

Module UspfSource

(Un)Sender Policy Framework.

uSPF is a framework to check the identity of the email's sender. When an email passes through an SMTP server, some informations are available such as the source of the email, the IP address (because the sender must initiate a TCP/IP connexion).

From these informations and via uSPF (and DNS records), we are able to authorize the given email or not. Indeed, the email submission process requires an identity with the SMTP MAILFROM command. At this stage, we are able to check if the domain name given by MAILFROM and the current IP address of the sender match!

The domain-name used by MAILFROM should have some DNS records which describe which IP address is allowed to send an email via the MAILFROM's identity. uSPF will check that and it will try to find a match. In any results - if uSPF fails or not - the SMTP server will put the result of such check into the given email.

Finally, it permits to check, at any step of the submission, the identity of the sender. However, it does not ensure a high level of securities when uSPF should be use with DKIM/DMARC to ensure some others aspects such as the integrity of the given email.

How to use uSPF.

uSPF requires some meta informations such as the MAILFROM identity and the IP address of the sender. The user can create a ctx and fill it with these information:

  let ctx =
    Uspf.empty |> Uspf.with_sender (`MAILFROM path) |> Uspf.with_ip ipaddr

From this ctx, then the user is able to check the identity of the sender via a DNS implementation. The user must get the SPF DNS record, analyze it and use it then with the ctx:

  let res =
    Uspf.get ctx sched dns (module DNS)
    >>= Uspf.check ctx sched dns (module DNS)

From the result, the user is able to generate an header field. It optional to give your identity (your domain) to be exhaustive about meta information on the field value:

  let field_name, value = Uspf.to_field ~ctx ?receiver res

The value is well-formed for the incoming email. You just need to prepend the field before the email.

Reproductibility.

The API provides a possibility to extract SPF results from an incoming email and regenerate the ctx from them. By this way, locally, you can reproduce the process above. By this way, you are able to reproduce the written result and check if it still is valid.

Indeed, due to the DNS record requirement to check the identity of the sender, it possible that meta informations from the given email are obsoletes (for any reasons).

As a server.

uSPF allows the end-user to craft its own record and publish it then into its primary/secondary DNS server. Multiple values exists such as:

They permits to describe via OCaml the SPF record. It can be serialized to a simple string then and the user can save it into its own primary/secondary DNS server.

Sourcemodule Sigs : sig ... end
Sourcetype ctx

The type for contexts. It's a heterogeneous map of values to help uSPF to validate the sender. It requires the MAILFROM parameter given by the SMTP protocol (which can be filled via with_sender) and/or the IP address of the sender (which can be filled via with_ip).

Sourceval empty : ctx

empty is an empty context.

Sourceval with_sender : [ `HELO of [ `raw ] Domain_name.t | `MAILFROM of Colombe.Path.t ] -> ctx -> ctx

with_sender v ctx adds into the given ctx the sender of the incoming email (its simple domain name or the complete email address).

Sourceval with_ip : Ipaddr.t -> ctx -> ctx

with_ip v ctx adds into the given ctx the IP address of the sender.

Sourceval domain : ctx -> [ `raw ] Domain_name.t option

domain ctx returns the domain-name of the sender if it exists.

Sourcemodule Macro : sig ... end
Sourcemodule Term : sig ... end
Sourcetype record

The type of SPF records.

Sourcetype mechanism

The type of mechanisms.

A mechanism permits to design and identify a set of IP addresses as being permitted or not permitted to use the domain for sending mail.

Sourceval a : ?cidr_v4:int -> ?cidr_v6:int -> [ `raw ] Domain_name.t -> mechanism

This mechanism matches if the sender's IP address is one of the domain-name's IP addresses. For clarity, this means the a mechanism also matches AAAA records.

An address lookup is done on the domain-name using the type of lookup (A or AAAA) appropriate for the connection type. The IP is compared to the returned address(es). If any address matches, the mechanism matches.

A Classless Inter-Domain Routing can be applied to returned address(es) (IPv4 or IPv6) to compare with the sender's IP address. For instance, a=10.0.0.42/32 matches only 10.0.0.42 as the sender's IP address but a=10.0.0.42/24 matches any 10.0.0.* addresses. By default, cidr_v4 = 32 and cidr_v6 = 128.

Sourceval all : mechanism

The all mechanism is a test that always matches. It is used as the rightmost mechanism (the last mechanism) in a record to provide an explicit default. For example v=spf1 a mx -all.

Mechanisms after all will never be tested.

Sourceval exists : [ `raw ] Domain_name.t -> mechanism

This mechanism is used to construct an arbitrary domain name that is used for a DNS A record query. It allows for complicated schemes involving arbitrary parts on the mail envelope to determine what is permitted.

Sourceval inc : [ `raw ] Domain_name.t -> mechanism

The include mechanism triggers a recursive evaluation of check:

  1. The macro is expanded according to the given ctx
  2. We re-execute check with the produced domain-name (IP and sender arguments remain the same)
  3. The recursive evaluation returns match, not-match or an error.
  4. If it returns match, then the appropriate result for the include mechanism is used (see qualifier)
  5. It it returns not-match or an error, the check process tests the next mechanism.

Note: for instance, if the domain-name has -all, include does not strictly terminates the processus. It fails and let check to process the next mechanism.

Sourceval mx : ?cidr_v4:int -> ?cidr_v6:int -> [ `raw ] Domain_name.t -> mechanism

This mechanims matches if the sender's IP is one of the MX hosts for a domain-name. A domain-name should have a MX record which is an IP address. If this IP address is the same as the given IP address into the given ctx, we consider that the sender matches.

Note: if the domain-name has no MX record, check does not apply the implicit MX rules by querying for an A or AAAA record for the same name.

This mechanism test whether the given IP from the given ctx is contained within a given IPv4 network.

This mechanism test whether the given IP from the given ctx is contained within a given IPv: network.

Sourcetype modifier

The type of modifiers.

They are not available because they mostly provide additional information which are not needed for check. By this way, it's not needed to let the user to define some when they are not effective on the user's sender policy.

Sourcetype qualifier =
  1. | Pass
  2. | Fail
  3. | Softfail
  4. | Neutral
    (*

    The type of qualifiers.

    A qualifier specifies what the mechanism returns when it matches or not:

    • + returns pass if the mechanism matches
    • - returns fail if the mechanism matches
    • ~ returns softfail if the mechanism matches
    • ? returns neutral if the mechanism matches
    *)

pass m specifies the qualifier of the given mechanism m. If the mechanism matches from the given ctx, check returns `Pass. Otherwise, check tries the next mechanism.

fail m specifies the qualifier of the given mechanism m. If the mechanism matches from the given ctx, check tries the next mechanism (as it considers the current one as a failure). If the mechanism is the last one, check returns `Fail so.

Sourceval softfail : mechanism -> qualifier * mechanism

softfail m specifies the qualifier of the given mechanism m. If the mechanism matches from the given ctx, check tries the next mechanism (as it considers the current one as a soft failure). If the mechanism is the last one, check returns `Softfail so.

neutral m specifies the qualifier of the given mechanism m. Regardless the result of the mechanism (if it matches or not), check tries the next mechanism. If the mechanism is the last one, check returns `Neutral so.

Sourceval record : (qualifier * mechanism) list -> modifier list -> record

record ms [] returns a record which can be serialized into the zone file of a specific domain-name as the sender policy.

Sourceval record_to_string : record -> string

record_to_string v returns the serialized version of the record to be able to save it into the zone file of a domain-name as the sender policy.

Sourceval record_of_string : ctx:ctx -> string -> (record, [> `Msg of string ]) result

record_of_string ~ctx str tries to parse and expand macro of the given string which should come from the TXT record of a domain-name as the sender policy of this domain-name.

Sourceval record_equal : record -> record -> bool
Sourcetype res = [
  1. | `None
  2. | `Neutral
  3. | `Pass of mechanism
  4. | `Fail
  5. | `Softfail
  6. | `Temperror
  7. | `Permerror
]
Sourceval pp : record Fmt.t
Sourceval pp_res : res Fmt.t
Sourceval get : ctx:ctx -> 't Sigs.state -> 'dns -> (module Sigs.DNS with type backend = 't and type t = 'dns) -> (([ res | `Record of record ], [> `Msg of string ]) result, 't) Sigs.io

get ~ctx scheduler dns (module DNS) tries to get the sender policy of the domain-name given by ctx. It requires a scheduler which allows the high-kind polymorphism over a monadic scheduler and a DNS implementation to request the sender policy.

Sourceval check : ctx:ctx -> 't Sigs.state -> 'dns -> (module Sigs.DNS with type backend = 't and type t = 'dns) -> [ res | `Record of record ] -> (res, 't) Sigs.io

check ~ctx scheduler dns (module DNS) tries to check the sender policy with the given ctx. It returns the result of this check.

Sourcetype newline =
  1. | LF
  2. | CRLF
Sourceval to_field : ctx:ctx -> ?receiver:Emile.domain -> res -> Mrmime.Field_name.t * Unstrctrd.t

to_field ~ctx ?received v serializes as an email field the result of the sender policy check according to the given ctx. The user is able to prepend then its email with this field.

Sourcetype extracted = spf list
Sourceand spf = {
  1. result : [ `None | `Neutral | `Pass | `Fail | `Softfail | `Temperror | `Permerror ];
  2. receiver : Emile.domain option;
  3. sender : Emile.mailbox option;
  4. ip : Ipaddr.t option;
  5. ctx : ctx;
}
Sourceval pp_spf : spf Fmt.t
Sourceval extract_received_spf : ?newline:newline -> 'flow -> 't Sigs.state -> (module Sigs.FLOW with type backend = 't and type flow = 'flow) -> ((extracted, [> `Msg of string ]) result, 't) Sigs.io

extract_received_spf ?newline flow scheduler (module Flow) tries to recognized SPF fields values from the given email represented as a flow.

/

Sourceval select_spf1 : string list -> (string, [> `None ]) result
Sourceval field_received_spf : Mrmime.Field_name.t
Sourceval parse_received_spf_field_value : Unstrctrd.t -> (spf, [> `Msg of string ]) result
OCaml

Innovation. Community. Security.