mirage / ocaml-dns

OCaml implementation of the DNS protocol
BSD 2-Clause "Simplified" License
106 stars 43 forks source link

What's the purpose of PROCESSER module? #59

Closed heidihoward closed 9 years ago

heidihoward commented 9 years ago

I'm currently writing a blog post on using the ocaml-dns library including processes. Can someone explain to me why we encapsulate processes into a processer and pass as a first class module, instead of passing processes directly?

here are the definitions from dns_server.ml:

type 'a process = src:ip_endpoint -> 
  dst:ip_endpoint -> 'a -> Dns.Query.answer option Lwt.t

module type PROCESSOR = sig
  include Dns.Protocol.SERVER
  val process : context process
end

type 'a processor = (module PROCESSOR with type context = 'a)

let process_query buf len obuf src dst processor =
  let module Processor = (val processor : PROCESSOR) in
  match Processor.parse (Dns.Buf.sub buf 0 len) with
  |None -> return None
  |Some ctxt -> begin
    lwt answer = Processor.process ~src ~dst ctxt in
    match answer with
    |None -> return None
    |Some answer ->
      let query = Processor.query_of_context ctxt in
      let response = Dns.Query.response_of_answer query answer in
      return (Processor.marshal obuf ctxt response)
 end

let processor_of_process process : Dns.Packet.t processor =
  let module P = struct
    include Dns.Protocol.Server

    let process = process
  end in
  (module P)

Is the purpose to allow the library user the option to provide an alternative implementation of SERVER?

module type SERVER = sig
  type context

  (** Projects a context into its associated query *)
  val query_of_context : context -> Packet.t

  (** DNS wire format parser function.
      @param buf message buffer
      @return parsed packet and context
  *)
  val parse   : Buf.t -> context option

  (** DNS wire format marshal function.
      @param buf output resource
      @param _q context
      @param response answer packet
      @return buffer to write
  *)
  val marshal : Buf.t -> context -> Packet.t -> Buf.t option
end
lcdunstan commented 9 years ago

I was confused too about why some of these modules exist, and then came across this library:

https://github.com/dsheets/ocaml-dnscurve/blob/master/lwt/dnscurve_processor.ml

I didn't delve into the details but at least it is an example.

heidihoward commented 9 years ago

great, that answers my question :)

avsm commented 9 years ago

Reopening :) If two people were confused by this (and indeed, it's a rather opaquely named module), it would be good to add comments in PROCESSOR's interface file to explain its purpose.

dsheets commented 9 years ago

The idea was to separate the IO loop of the server (SERVER) from the specifics of protocol marshaling/demarshaling. I wanted to allow users to still use generic servers by including them into custom processors but have the ability to provide implementations of those same generic functions that may be specialized to an abstract type. The first-class modules are there to carry around the existential context type and provide functions over it.

The initial user was DNSCurve servers which need to behave much like a traditional DNS server but may need to care about the public key of the query channel and must have control over the wire format. I did these horrible things in order to re-use both IO and protocol components from the DNS library while interposing the DNSCurve features. There may be a better way to implement this or name it.

I'll write up some documentation in the mlis soon.