mte/unikernel/duniverse/ocaml-dns/server/dns_server.mli
2025-11-11 02:07:51 +01:00

238 lines
10 KiB
OCaml

(* (c) 2017, 2018 Hannes Mehnert, all rights reserved *)
open Dns
(** DNS Server implementation *)
(** Authentication, stored in a Dns_trie with privileges to operations embedded in the name. *)
module Authentication : sig
(** A key is a pair of a [`raw Domain_name.t] and a [Dnskey.t]. In the name,
operation privileges and potentially IP addresses are encoded, e.g.
[foo._transfer.example.com] may do AXFR on [example.com] and any
subdomain, e.g. [foo.example.com]. *)
type operation = [
| `Update
| `Transfer
| `Notify
]
(** The type of operations, sorted by highest ot lowest privileges, an
[`Update] may as well carry out a [`Transfer]. *)
val operation_to_string : operation -> string
(** [operation_to_string op] is the string representation of [op]. *)
val all_ops : operation list
(** [all_ops] is a list of all operations. *)
val access_granted : required:operation -> operation -> bool
(** [access_granted ~required key_operation] is [true] if [key_operation] is
authorised for [required] operation. *)
val zone_and_operation : 'a Domain_name.t -> ([`host] Domain_name.t * operation) option
(** [zone_and_operation key] is [Some (zone, op)], the [zone] of the [key],
and its operation [op]. If the [key] is not in the expected format, [None]
is returned. *)
val access : ?key:'a Domain_name.t -> zone:'b Domain_name.t -> operation -> bool
(** [access op ~key ~zone] checks whether [key] is authorised for [op] on
[zone]. *)
type t
(** Opaque type for storing authentication keys. *)
end
type t = private {
data : Dns_trie.t ;
auth : Authentication.t ;
unauthenticated_zone_transfer : bool ;
rng : int -> string ;
tsig_verify : Tsig_op.verify ;
tsig_sign : Tsig_op.sign ;
}
(** The state of a DNS server. *)
val create : ?unauthenticated_zone_transfer:bool ->
?tsig_verify:Tsig_op.verify ->
?tsig_sign:Tsig_op.sign ->
?auth:Authentication.t ->
Dns_trie.t ->
(int -> string) ->
t
(** [create ~unauthenticated_zone_transfer ~tsig_verify ~tsig_sign ~auth data rng]
constructs a [t]. See {!Primary.create} and {!Secondary.create} for the
logic running a primary or secondary server. *)
val with_data : t -> Dns_trie.t -> t
(** [with_data t data] is [t'] where the [data] field is updated with the
provided value. Be aware that this function breaks the semantics of a
primary server with secondaries, since secondaries won't be notified and
will be out of sync. Use if you know what you do. The data of a secondary
will usually come via zone transfer from the primary name services. *)
val text : 'a Domain_name.t -> Dns_trie.t -> (string, [> `Msg of string ]) result
(** [text name trie] results in a string representation (zonefile) of the trie. *)
val handle_question : t -> Packet.Question.t ->
(Packet.Flags.t * Packet.Answer.t * Name_rr_map.t option,
Rcode.t * Packet.Answer.t option) result
(** [handle_question t question] handles the DNS query [question] by looking
it up in the trie of [t]. The result is either an answer or an error. *)
val update_data : Dns_trie.t -> 'a Domain_name.t ->
Dns.Packet.Update.prereq list Domain_name.Map.t
* Dns.Packet.Update.update list Domain_name.Map.t ->
( Dns_trie.t * (Domain_name.Set.elt * Dns.Soa.t) list,
Dns.Rcode.t )
result
(** [update_data data domain update_content] applies the [update_content] to
the [data] for [domain]. This function breaks the semantics of a primary
server with secondaries, since the secondaries won't be notified of the
update and will be out of sync. Use if you know what you are doing. *)
val handle_update : t -> proto -> [ `raw ] Domain_name.t option ->
Packet.Question.t -> Packet.Update.t ->
(Dns_trie.t * ([`raw] Domain_name.t * Soa.t) list, Rcode.t) result
(** [handle_update t proto keyname question update] authenticates the update
request and processes the update. This function breaks the semantics of a
primary server with secondaries, since the secondaries won't be notified.
Use if you know what you are doing. *)
val handle_axfr_request : t -> proto -> [ `raw ] Domain_name.t option ->
Packet.Question.t -> (Packet.Axfr.t, Rcode.t) result
(** [handle_axfr_request t proto keyname question] authenticates the zone
transfer request and processes it. If the request is valid, and the zone
available, a zone transfer is returned. *)
type trie_cache
val handle_ixfr_request : t -> trie_cache -> proto -> [ `raw ] Domain_name.t option ->
Packet.Question.t -> Soa.t -> (Packet.Ixfr.t, Rcode.t) result
(** [handle_ixfr_request t cache proto keyname question soa] authenticates the
incremental zone transfer request and processes it. If valid, an incremental
zone transfer is returned. *)
val handle_tsig : ?mac:string -> t -> Ptime.t -> Packet.t ->
string -> (([ `raw ] Domain_name.t * Tsig.t * string * Dnskey.t) option,
Tsig_op.e * string option) result
(** [handle_tsig ~mac t now packet buffer] verifies the tsig
signature if present, returning the keyname, tsig, mac, and used key. *)
type packet_callback = Packet.Question.t -> Packet.reply option
(** [packet_callback question] either returns a reply to a DNS question [Some reply] or [None]. *)
module Primary : sig
type s
(** The state of a primary DNS server. *)
val server : s -> t
(** [server s] is the server of the primary. *)
val data : s -> Dns_trie.t
(** [data s] is the data store of [s]. *)
val with_data : s -> Ptime.t -> int64 -> Dns_trie.t ->
s * (Ipaddr.t * string list) list
(** [with_data s now ts trie] replaces the current data with [trie] in [s].
The returned notifications should be send out. *)
val with_keys : s -> Ptime.t -> int64 -> ('a Domain_name.t * Dnskey.t) list ->
s * (Ipaddr.t * string list) list
(** [with_keys s now ts keys] replaces the current keys with [keys] in [s],
and generates notifications. *)
val trie_cache : s -> trie_cache
(** [trie_cache s] is the trie cache of the server. *)
val create : ?trie_cache_entries:int -> ?keys:('a Domain_name.t * Dnskey.t) list ->
?unauthenticated_zone_transfer:bool ->
?tsig_verify:Tsig_op.verify -> ?tsig_sign:Tsig_op.sign ->
rng:(int -> string) -> Dns_trie.t -> s
(** [create ~trie_cache_entries ~keys ~unauthenticated_zone_transfer
~tsig_verify ~tsig_sign ~rng data] creates a primary server. If
[unauthenticated_zone_transfer] is provided and [true] (defaults to
[false]), anyone can transfer the zones. [trie_cache_entries] is the
backlog to keep in memory for incremental zone transfers (IXFR, default is 5). This
affects memory usage. *)
val handle_packet : ?packet_callback:packet_callback -> s -> Ptime.t -> int64
-> proto -> Ipaddr.t -> int -> Packet.t -> 'a Domain_name.t option ->
s * Packet.t option * (Ipaddr.t * string list) list *
[> `Notify of Soa.t option | `Keep ] option
(** [handle_packet ~packet_callback s now ts src src_port proto key packet]
handles the given [packet], returning new state, an answer, and
potentially notify packets to secondary name servers. If [packet_callback]
is specified, it is called for each incoming query. If it returns
[Some reply], this reply is used instead of the usual lookup in the
zone data. It can be used for custom query processing, such as for load
balancing or transporting data. *)
val handle_buf : ?packet_callback:packet_callback -> s -> Ptime.t -> int64
-> proto -> Ipaddr.t -> int -> string ->
s * string list * (Ipaddr.t * string list) list *
[ `Notify of Soa.t option | `Signed_notify of Soa.t option | `Keep ] option *
[ `raw ] Domain_name.t option
(** [handle_buf ~packet_callback s now ts proto src src_port buffer] decodes
the [buffer], processes the DNS frame using {!handle_packet}, and encodes
the reply. The result is a new state, potentially a list of answers to the
requestor, a list of notifications to send out, information whether a
notify (or signed notify) was received, and the hmac key used for
authentication. If [packet_callback] is specified, it is called for each
incoming query. If it returns [Some reply], this reply is used instead of
the usual lookup in the zone data. This can be used for custom query
processing, such as for load balancing or transporting data. *)
val closed : s -> Ipaddr.t -> s
(** [closed s ip] marks the connection to [ip] closed. *)
val timer : s -> Ptime.t -> int64 ->
s * (Ipaddr.t * string list) list
(** [timer s now ts] may encode some notifications to secondary name servers
if previous ones were not acknowledged. *)
val to_be_notified : s -> [ `host ] Domain_name.t ->
(Ipaddr.t * [ `raw ] Domain_name.t option) list
(** [to_be_notified s zone] returns a list of pairs of IP address and optional
tsig key name of the servers to be notified for a zone change. This list
is based on (a) NS entries for the zone, (b) registered TSIG transfer keys,
and (c) active connection (which transmitted a signed SOA). *)
end
module Secondary : sig
type s
(** The state of a secondary DNS server. *)
val data : s -> Dns_trie.t
(** [data s] is the zone data of [s]. *)
val with_data : s -> Dns_trie.t -> s
(** [with_data s trie] is [s] with its data replaced by [trie]. *)
val create : ?primary:Ipaddr.t ->
tsig_verify:Tsig_op.verify -> tsig_sign:Tsig_op.sign ->
rng:(int -> string) -> ('a Domain_name.t * Dnskey.t) list -> s
(** [create ~primary ~tsig_verify ~tsig_sign ~rng keys] creates a secondary
DNS server state. *)
val handle_packet : ?packet_callback:packet_callback -> s -> Ptime.t -> int64 ->
Ipaddr.t -> Packet.t -> 'a Domain_name.t option ->
s * Packet.t option * (Ipaddr.t * string) option
(** [handle_packet s now ts ip proto key t] handles the incoming packet. *)
val handle_buf : ?packet_callback:packet_callback -> s -> Ptime.t -> int64 ->
proto -> Ipaddr.t -> string ->
s * string option * (Ipaddr.t * string) option
(** [handle_buf ~packet_callback s now ts proto src buf] decodes [buf], processes with
{!handle_packet}, and encodes the results. *)
val timer : s -> Ptime.t -> int64 ->
s * (Ipaddr.t * string list) list
(** [timer s now ts] may request SOA or retransmit AXFR. *)
val closed : s -> Ptime.t -> int64 -> Ipaddr.t ->
s * string list
(** [closed s now ts ip] marks [ip] as closed, the returned buffers (SOA
requests) should be sent to [ip]. *)
end