(* TODO ideally there'd be something like mirage-flow-lwt that didn't depend on lwt and a ton of other things, and still provided [map] and [connect] and so on. leaving this stuff here for now until a better solution presents itself. *) val default_resolver_hostname : [`host] Domain_name.t val default_resolvers : Ipaddr.t list (** [default_resolver] is a list of IPv6 and IPv4 address of the default resolver. Currently it is the IP address of the UncensoredDNS.org anycast service. *) module type S = sig type context (** A context is a network connection initialized by {!connect} *) type +'a io (** [io] is the type of an effect. ['err] is a polymorphic variant. *) type io_addr (** An address for a given context type, usually this will consist of IP address + a TCP/IP or UDP/IP port number, but for some context types it can carry additional information for purposes of cryptographic verification. *) type stack (** A stack with which to connect. *) type t (** The abstract state of a DNS client. *) val create : ?nameservers:(Dns.proto * io_addr list) -> timeout:int64 -> stack -> t (** [create ~nameservers ~timeout stack] creates the state record of the DNS client. We use [timeout] (ns) as a cumulative time budget for connect and request timeouts. *) val nameservers : t -> Dns.proto * io_addr list (** The address of a nameservers that is supposed to work with the underlying context, can be used if the user does not want to bother with configuring their own.*) val rng : int -> string (** [rng t] is a random number generator. *) val clock : unit -> int64 (** [clock t] is the monotonic clock. *) val connect : t -> (Dns.proto * context, [> `Msg of string ]) result io (** [connect t] is a new connection ([context]) to [t], or an error. *) val send_recv : context -> string -> (string, [> `Msg of string ]) result io (** [send_recv context buffer] sends [buffer] to the [context] upstream, and then reads a buffer. *) val close : context -> unit io (** [close context] closes the [context], freeing up resources. *) val bind : 'a io -> ('a -> 'b io) -> 'b io (** a.k.a. [>>=] *) val lift : 'a -> 'a io end module Make : functor (T : S) -> sig type t (** The abstract type of a DNS client. *) val transport : t -> T.t (** [transport t] is the transport of [t]. *) val create : ?cache_size:int -> ?edns:[ `None | `Auto | `Manual of Dns.Edns.t ] -> ?nameservers:(Dns.proto * T.io_addr list) -> ?timeout:int64 -> T.stack -> t (** [create ~cache_size ~edns ~nameservers ~timeout stack] creates the state of the DNS client. We use [timeout] (ns, default 5s) as a time budget for connect and request timeouts. To specify a timeout, use [create ~timeout:(Duration.of_sec 3)]. Whether or not to use {{:https://tools.ietf.org/html/rfc6891}EDNS} in queries is controlled by [~edns] (defaults to [`None]): if [None], no EDNS will be present, [`Auto] adds TCP Keepalive if protocol is TCP, [`Manual edns] adds the EDNS data specified. *) val nameservers : t -> Dns.proto * T.io_addr list (** [nameservers state] returns the list of nameservers to be used. *) val getaddrinfo : t -> 'response Dns.Rr_map.key -> 'a Domain_name.t -> ('response, [> `Msg of string ]) result T.io (** [getaddrinfo state query_type name] is the [query_type]-dependent response regarding [name], or an [Error _] message. See {!Dns_client.query_state} for more information about the result types. *) val gethostbyname : t -> [ `host ] Domain_name.t -> (Ipaddr.V4.t, [> `Msg of string ]) result T.io (** [gethostbyname state hostname] is the IPv4 address of [hostname] resolved via the [state] specified. If the query fails, or if the [domain] does not have any IPv4 addresses, an [Error _] message is returned. Any extraneous IPv4 addresses are ignored. For an example of using this API, see [unix/ohost.ml] in the distribution of this package. *) val gethostbyname6 : t -> [ `host ] Domain_name.t -> (Ipaddr.V6.t, [> `Msg of string ]) result T.io (** [gethostbyname6 state hostname] is the IPv6 address of [hostname] resolved via the [state] specified. It is the IPv6 equivalent of {!gethostbyname}. *) val get_resource_record : t -> 'response Dns.Rr_map.key -> 'a Domain_name.t -> ('response, [> `Msg of string | `No_data of [ `raw ] Domain_name.t * Dns.Soa.t | `No_domain of [ `raw ] Domain_name.t * Dns.Soa.t ]) result T.io (** [get_resource_record state query_type name] resolves [query_type, name] via the [state] specified. The behaviour is equivalent to {!getaddrinfo}, apart from the error return value - [get_resource_record] distinguishes some errors, at the moment [No_data] if the [name] exists, but not the [query_type], and [No_domain] if the [name] does not exist. This allows clients to treat these error conditions explicitly. *) val get_raw_reply : t -> 'response Dns.Rr_map.key -> 'a Domain_name.t -> (Dns.Packet.reply, [> `Partial | `Msg of string ]) result T.io (** [get_raw_reply state query_type name] resolves [query_type, name] via the [state] specified. The complete DNS reply is returned. CNAME records are not followed. This allows DNSSec to process the entire reply. *) end module Pure : sig (** The pure interface to the client part of uDns. Various helper modules to do with side effects are available from {!Dns_client_lwt}, {!Dns_client_unix} and so forth. *) type 'key query_state constraint 'key = 'a Dns.Rr_map.key (** [query_state] is parameterized over the query type, so the type of the representation of the answer depends on what the name server was asked to provide. See {!Dns.Rr_map.k} for a list of response types. The first element (the [int32]) in most of the tuples is the Time-To-Live (TTL) field returned from the server, which you can use to calculate when you should request fresh information in case you are writing a long-running application. *) val make_query : (int -> string) -> Dns.proto -> ?dnssec:bool -> [ `None | `Auto | `Manual of Dns.Edns.t ] -> 'a Domain_name.t -> 'query_type Dns.Rr_map.key -> string * 'query_type Dns.Rr_map.key query_state (** [make_query rng protocol name query_type] is [query, query_state] where [query] is the serialized DNS query to send to the name server, and [query_state] is the information required to validate the response. *) val parse_response : 'query_type Dns.Rr_map.key query_state -> string -> (Dns.Packet.reply, [ `Partial | `Msg of string]) result (** [parse_response query_state response] is the information contained in [response] parsed using [query_state] when the query was successful, or an [`Msg message] if the [response] did not match the [query_state] (or if the query failed). In a TCP usage context the [`Partial] means there are more bytes to be read in order to parse correctly. This can happen due to short reads or if the server (or something along the route) chunks its responses into multiple individual packets. In that case you should concatenate [response] and the next received data and call this function again. In a UDP usage context the [`Partial] means information was lost, due to an incomplete packet. *) val handle_response : 'query_type Dns.Rr_map.key query_state -> string -> ( [ `Data of 'query_type | `Partial | `No_data of [`raw] Domain_name.t * Dns.Soa.t | `No_domain of [`raw] Domain_name.t * Dns.Soa.t ], [`Msg of string]) result (** [handle_response query_state response] is the information contained in [response] parsed using [query_state] when the query was successful, or an [`Msg message] if the [response] did not match the [query_state] (or if the query failed). In a TCP usage context the [`Partial] means there are more bytes to be read in order to parse correctly. This can happen due to short reads or if the server (or something along the route) chunks its responses into multiple individual packets. In that case you should concatenate [response] and the next received data and call this function again. In a UDP usage context the [`Partial] means information was lost, due to an incomplete packet. *) end