(* (c) 2017 Hannes Mehnert, all rights reserved *) type 'a t (** The type of a domain name, a sequence of labels separated by dots. Each label may contain any bytes. The length of each label may not exceed 63 characters. The total length of a domain name is limited to 253 (its byte representation is 255), but other protocols (such as SMTP) may apply even smaller limits. A domain name label is case preserving, comparison is done in a case insensitive manner. Every [t] is a fully qualified domain name, its last label is the [root] label. The specification of domain names originates from {{:https://tools.ietf.org/html/rfc1035}RFC 1035}. The invariants on the length of domain names are preserved throughout the module - no [t] will exist which violates these. Phantom types are used for further name restrictions, {!host} checks for host names ([`host t]): only letters, digits, and hyphen allowed, hyphen not first or last character of a label, the last label must contain at least one letter. {!service} checks for a service name ([`service t]): its first label is a service name: 1-15 characters, no double-hyphen, hyphen not first or last charactes, only letters, digits and hyphen allowed, and the second label is a protocol ([_tcp] or [_udp] or [_sctp]). When a [t] is constructed (either from a string, etc.), it is a [`raw t]. Subsequent modifications, such as adding or removing labels, appending, of any kind of name also result in a [`raw t], which needs to be checked for [`host t] (using {!host}) or [`service t] (using {!service}) if desired. Constructing a [t] (via {!of_string}, {!of_string_exn}, {!of_strings} etc.) does not require a trailing dot. {e v0.5.0 - {{:https://github.com/hannesm/domain-name }homepage}} *) (** {2 Constructor} *) val root : [ `raw ] t (** [root] is the root domain ("."), the empty label. *) (** {2 String representation} *) val of_string : string -> ([ `raw ] t, [> `Msg of string ]) result (** [of_string name] is either [t], the domain name, or an error if the provided [name] is not a valid domain name. A trailing dot is not requred. *) val of_string_exn : string -> [ `raw ] t (** [of_string_exn name] is [t], the domain name. A trailing dot is not required. @raise Invalid_argument if [name] is not a valid domain name. *) val to_string : ?trailing:bool -> 'a t -> string (** [to_string ~trailing t] is [String.concat ~sep:"." (to_strings t)], a human-readable representation of [t]. If [trailing] is provided and [true] (defaults to [false]), the resulting string will contain a trailing dot. *) (** {2 Predicates and basic operations} *) val canonical : 'a t -> 'a t (** [canonical t] is [t'], the canonical domain name, as specified in RFC 4034 (and 2535): all characters are lowercase. *) val host : 'a t -> ([ `host ] t, [> `Msg of string ]) result (** [host t] is a [`host t] if [t] is a hostname: the contents of the domain name is limited: each label may start with a digit or letter, followed by digits, letters, or hyphens. *) val host_exn : 'a t -> [ `host ] t (** [host_exn t] is a [`host t] if [t] is a hostname: the contents of the domain name is limited: each label may start with a digit or letter, followed by digits, letters, or hyphens. @raise Invalid_argument if [t] is not a hostname. *) val service : 'a t -> ([ `service ] t, [> `Msg of string ]) result (** [service t] is [`service t] if [t] contains a service name, the following conditions have to be met: The first label is a service name (or port number); an underscore preceding 1-15 characters from the set [- a-z A-Z 0-9]. The service name may not contain a hyphen ([-]) following another hyphen; no hyphen at the beginning or end. The second label is the protocol, one of [_tcp], [_udp], or [_sctp]. The remaining labels must form a valid hostname. This function can be used to validate RR's of the types SRV (RFC 2782) and TLSA (RFC 7671). *) val service_exn : 'a t -> [ `service ] t (** [service_exn t] is [`service t] if [t] is a service name (see {!service}). @raise Invalid_argument if [t] is not a service names. *) val raw : 'a t -> [ `raw ] t (** [raw t] is the [`raw t]. *) val count_labels : 'a t -> int (** [count_labels name] returns the amount of labels in [name]. *) val is_subdomain : subdomain:'a t -> domain:'b t -> bool (** [is_subdomain ~subdomain ~domain] is [true] if [subdomain] contains any labels prepended to [domain]: [foo.bar.com] is a subdomain of [bar.com] and of [com], [sub ~subdomain:x ~domain:root] is true for all [x]. *) val get_label : ?rev:bool -> 'a t -> int -> (string, [> `Msg of string ]) result (** [get_label ~rev name idx] retrieves the label at index [idx] from [name]. If [idx] is out of bounds, an Error is returned. If [rev] is provided and [true] (defaults to [false]), [idx] is from the end instead of the beginning. *) val get_label_exn : ?rev:bool -> 'a t -> int -> string (** [get_label_exn ~rev name idx] is the label at index [idx] in [name]. @raise Invalid_argument if [idx] is out of bounds in [name]. *) val find_label : ?rev:bool -> 'a t -> (string -> bool) -> int option (** [find_label ~rev name p] returns the first position where [p lbl] is [true] in [name], if it exists, otherwise [None]. If [rev] is provided and [true] (defaults to [false]), the [name] is traversed from the end instead of the beginning. *) val find_label_exn : ?rev:bool -> 'a t -> (string -> bool) -> int (** [find_label_exn ~rev name p], see {!find_label}. @raise Invalid_argument if [p] does not return [true] in [name]. *) (** {2 Label addition and removal} *) val prepend_label : 'a t -> string -> ([ `raw ] t, [> `Msg of string ]) result (** [prepend_label name pre] is either [t], the new domain name, or an error. *) val prepend_label_exn : 'a t -> string -> [ `raw ] t (** [prepend_label_exn name pre] is [t], the new domain name. @raise Invalid_argument if [pre] is not a valid domain name. *) val drop_label : ?rev:bool -> ?amount:int -> 'a t -> ([ `raw ] t, [> `Msg of string ]) result (** [drop_label ~rev ~amount t] is either [t], a domain name with [amount] (defaults to [1]) labels dropped from the beginning - if [rev] is provided and [true] (defaults to [false]) labels are dropped from the end. [drop_label (of_string_exn "foo.com") = Ok (of_string_exn "com")], [drop_label ~rev:true (of_string_exn "foo.com") = Ok (of_string_exn "foo")]. *) val drop_label_exn : ?rev:bool -> ?amount:int -> 'a t -> [ `raw ] t (** [drop_label_exn ~rev ~amount t], see {!drop_label}. Instead of a [result], the value is returned directly. @raise Invalid_argument if there are not sufficient labels. *) val append : 'a t -> 'b t -> ([ `raw ] t, [> `Msg of string ]) result (** [append pre post] is [pre ^ "." ^ post]. *) val append_exn : 'a t -> 'b t -> [ `raw ] t (** [append_exn pre post] is [pre ^ "." ^ post]. @raise Invalid_argument if the result would violate length restrictions. *) (** {2 Comparison} *) val equal : ?case_sensitive:bool -> 'a t -> 'b t -> bool (** [equal ~case_sensitive t t'] is [true] if all labels of [t] and [t'] are equal. If [case_sensitive] is provided and [true], the cases of the labels are respected (defaults to [false]). *) val compare : 'a t -> 'b t -> int (** [compare t t'] compares the domain names [t] and [t'] using a case insensitive string comparison. This conforms to the canonical DNS name order, as described in RFC 4034, Section 6.1. *) val equal_label : ?case_sensitive:bool -> string -> string -> bool (** [equal_label ~case_sensitive a b] is [true] if [a] and [b] are equal ignoring casing. If [case_sensitive] is provided and [true] (defaults to [false]), the casing is respected. *) val compare_label : string -> string -> int (** [compare_label t t'] compares the labels [t] and [t'] using a case insensitive string comparison. *) (** {2 Collections} *) module Host_map : sig include Map.S with type key = [ `host ] t (** [find key t] is [Some a] where a is the binding of [key] in [t]. [None] if the [key] is not present. *) val find : key -> 'a t -> 'a option end (** The module of a host name map *) module Host_set : Set.S with type elt = [ `host ] t (** The module of a host name set *) module Service_map : sig include Map.S with type key = [ `service ] t (** [find key t] is [Some a] where a is the binding of [key] in [t]. [None] if the [key] is not present. *) val find : key -> 'a t -> 'a option end (** The module of a service name map *) module Service_set : Set.S with type elt = [ `service ] t (** The module of a service name set *) module Map : sig include Map.S with type key = [ `raw ] t (** [find key t] is [Some a] where a is the binding of [key] in [t]. [None] if the [key] is not present. *) val find : key -> 'a t -> 'a option end (** The module of a domain name map *) module Set : Set.S with type elt = [ `raw ] t (** The module of a domain name set *) (** {2 String list representation} *) val of_strings : string list -> ([ `raw ] t, [> `Msg of string ]) result (** [of_strings labels] is either [t], a domain name, or an error if the provided [labels] violate domain name constraints. A trailing empty label is not required. *) val of_strings_exn : string list -> [ `raw ] t (** [of_strings_exn labels] is [t], a domain name. A trailing empty label is not required. @raise Invalid_argument if [labels] are not a valid domain name. *) val to_strings : ?trailing:bool -> 'a t -> string list (** [to_strings ~trailing t] is the list of labels of [t]. If [trailing] is provided and [true] (defaults to [false]), the resulting list will contain a trailing empty label. *) (** {2 Pretty printer} *) val pp : Format.formatter -> 'a t -> unit (** [pp ppf t] pretty prints the domain name [t] on [ppf]. *) (**/**) (* exposing internal structure, used by udns (but could as well use Obj.magic *) val of_array : string array -> [ `raw ] t (** [of_array a] is [t], a domain name from [a], an array containing a reversed domain name. *) val to_array : 'a t -> string array (** [to_array t] is [a], an array containing the reversed domain name of [t]. *)