This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,383 @@
module type S = sig
type stack
type ipaddr
module TCP : sig
include Mirage_flow.S
val dst : flow -> ipaddr * int
val no_close : flow -> unit
val to_close : flow -> unit
end
module TLS : sig
type error =
[ `Tls_alert of Tls.Packet.alert_type
| `Tls_failure of Tls.Engine.failure
| `Read of TCP.error
| `Write of TCP.write_error ]
type write_error = [ `Closed | error ]
include
Mirage_flow.S with type error := error and type write_error := write_error
val no_close : flow -> unit
val to_close : flow -> unit
val epoch : flow -> (Tls.Core.epoch_data, unit) result
val reneg :
?authenticator:X509.Authenticator.t ->
?acceptable_cas:X509.Distinguished_name.t list ->
?cert:Tls.Config.own_cert ->
?drop:bool ->
flow ->
(unit, [ write_error | `Msg of string ]) result Lwt.t
val key_update :
?request:bool ->
flow ->
(unit, [ write_error | `Msg of string ]) result Lwt.t
val server_of_flow :
Tls.Config.server -> TCP.flow -> (flow, write_error) result Lwt.t
val client_of_flow :
Tls.Config.client ->
?host:[ `host ] Domain_name.t ->
TCP.flow ->
(flow, write_error) result Lwt.t
end
val tcp_protocol : (stack * ipaddr * int, TCP.flow) Mimic.protocol
val tcp_edn : (stack * ipaddr * int) Mimic.value
val tls_edn :
([ `host ] Domain_name.t option * Tls.Config.client * stack * ipaddr * int)
Mimic.value
val tls_protocol :
( [ `host ] Domain_name.t option * Tls.Config.client * stack * ipaddr * int,
TLS.flow )
Mimic.protocol
type t
type dst = ipaddr * int
val init : port:int -> stack -> t Lwt.t
val accept : t -> (TCP.flow, [> `Closed ]) result Lwt.t
val close : t -> unit Lwt.t
val http_service :
?config:H1.Config.t ->
error_handler:(dst -> H1.Server_connection.error_handler) ->
(TCP.flow -> dst -> H1.Server_connection.request_handler) ->
t Paf.service
val https_service :
tls:Tls.Config.server ->
?config:H1.Config.t ->
error_handler:(dst -> H1.Server_connection.error_handler) ->
(TLS.flow -> dst -> H1.Server_connection.request_handler) ->
t Paf.service
val alpn_service :
tls:Tls.Config.server ->
?config:H1.Config.t * H2.Config.t ->
(TLS.flow, dst) Alpn.server_handler ->
t Paf.service
val serve :
?stop:Lwt_switch.t -> 't Paf.service -> 't -> [ `Initialized of unit Lwt.t ]
end
module Make (Stack : Tcpip.Tcp.S) :
S with type stack = Stack.t and type ipaddr = Stack.ipaddr = struct
open Lwt.Infix
type ipaddr = Stack.ipaddr
type dst = ipaddr * int
module TCP = struct
let src = Logs.Src.create "paf-tcp"
module Log = (val Logs.src_log src : Logs.LOG)
include Stack
type nonrec flow = { flow : flow; mutable no_close : bool }
type endpoint = Stack.t * Stack.ipaddr * int
type nonrec write_error =
[ `Write of write_error | `Connect of error | `Closed ]
let pp_write_error ppf = function
| `Write err | (`Closed as err) -> pp_write_error ppf err
| `Connect err -> pp_error ppf err
let read flow = read flow.flow
let dst flow = dst flow.flow
let write flow cs =
write flow.flow cs >>= function
| Ok _ as v -> Lwt.return v
| Error err -> Lwt.return_error (`Write err)
let writev flow css =
writev flow.flow css >>= function
| Ok _ as v -> Lwt.return v
| Error err -> Lwt.return_error (`Write err)
let connect (stack, ipaddr, port) =
create_connection stack (ipaddr, port) >>= function
| Ok flow -> Lwt.return_ok { flow; no_close = false }
| Error err -> Lwt.return_error (`Connect err)
let no_close flow = flow.no_close <- true
let to_close flow = flow.no_close <- false
let close flow =
match flow.no_close with
| true ->
Log.debug (fun m -> m "Fakely close the connection.") ;
Lwt.return_unit
| false ->
Log.debug (fun m -> m "Really close the connection.") ;
close flow.flow
let shutdown flow = shutdown flow.flow
end
module TLS = struct
let src = Logs.Src.create "paf-tls"
module Log = (val Logs.src_log src : Logs.LOG)
include Tls_mirage.Make (TCP)
type endpoint =
[ `host ] Domain_name.t option
* Tls.Config.client
* Stack.t
* Stack.ipaddr
* int
type nonrec flow = TCP.flow * flow
let connect (host, cfg, stack, ipaddr, port) =
Stack.create_connection stack (ipaddr, port) >>= function
| Error err -> Lwt.return_error (`Read err)
| Ok flow ->
let open Lwt_result.Infix in
let tcp_flow = { TCP.flow; TCP.no_close = false } in
client_of_flow cfg ?host tcp_flow >>= fun tls_flow ->
Lwt.return_ok (tcp_flow, tls_flow)
let no_close (tcp_flow, _) = TCP.no_close tcp_flow
let to_close (tcp_flow, _) = TCP.to_close tcp_flow
let read (_, tls_flow) = read tls_flow
let write (_, tls_flow) = write tls_flow
let writev (_, tls_flow) = writev tls_flow
let shutdown (_, tls_flow) = shutdown tls_flow
let epoch (_, tls_flow) = epoch tls_flow
let reneg ?authenticator ?acceptable_cas ?cert ?drop (_, tls_flow) =
reneg ?authenticator ?acceptable_cas ?cert ?drop tls_flow
let key_update ?request (_, tls_flow) = key_update ?request tls_flow
let server_of_flow config tcp_flow =
Lwt_result.Infix.(
server_of_flow config tcp_flow >>= fun tls_flow ->
Lwt.return_ok (tcp_flow, tls_flow))
let client_of_flow config ?host tcp_flow =
Lwt_result.Infix.(
client_of_flow config ?host tcp_flow >>= fun tls_flow ->
Lwt.return_ok (tcp_flow, tls_flow))
let close (tcp_flow, tls_flow) =
match tcp_flow.TCP.no_close with
| true -> Lwt.return_unit
| false -> close tls_flow
end
let src = Logs.Src.create "paf-layer"
module Log = (val Logs.src_log src : Logs.LOG)
type stack = Stack.t
let tcp_edn, tcp_protocol = Mimic.register ~name:"tcp" (module TCP)
let tls_edn, tls_protocol =
Mimic.register ~priority:10 ~name:"tls" (module TLS)
type t = {
stack : Stack.t;
queue : Stack.flow Queue.t;
condition : unit Lwt_condition.t;
mutex : Lwt_mutex.t;
mutable closed : bool;
}
let init ~port stack =
let queue = Queue.create () in
let condition = Lwt_condition.create () in
let mutex = Lwt_mutex.create () in
let listener flow =
Lwt_mutex.lock mutex >>= fun () ->
Queue.push flow queue ;
Lwt_condition.signal condition () ;
Lwt_mutex.unlock mutex ;
Lwt.return () in
Stack.listen ~port stack listener ;
Lwt.return { stack; queue; condition; mutex; closed = false }
let rec accept ({ queue; condition; mutex; _ } as t) =
Lwt_mutex.lock mutex >>= fun () ->
let rec await () =
if Queue.is_empty queue && not t.closed
then Lwt_condition.wait condition ~mutex >>= await
else Lwt.return_unit in
await () >>= fun () ->
match Queue.pop queue with
| flow ->
Lwt_mutex.unlock mutex ;
Lwt.return_ok { TCP.flow; TCP.no_close = false }
| exception Queue.Empty ->
if t.closed
then (
Lwt_mutex.unlock mutex ;
Lwt.return_error `Closed)
else (
Lwt_mutex.unlock mutex ;
accept t)
let close ({ condition; _ } as t) =
t.closed <- true ;
(* Stack.disconnect stack >>= fun () -> *)
Lwt_condition.signal condition () ;
Lwt.return_unit
let http_service ?config ~error_handler request_handler =
let module R = (val Mimic.repr tcp_protocol) in
let connection flow =
let dst = TCP.dst flow in
let error_handler = error_handler dst in
let request_handler' reqd = request_handler flow dst reqd in
let conn =
H1.Server_connection.create ?config ~error_handler request_handler'
in
Lwt.return_ok (R.T flow, Paf.Runtime ((module H1.Server_connection), conn))
in
Paf.service connection Lwt.return_ok accept close
let https_service ~tls ?config ~error_handler request_handler =
let module R = (val Mimic.repr tls_protocol) in
let handshake tcp_flow =
let dst = TCP.dst tcp_flow in
TLS.server_of_flow tls tcp_flow >>= function
| Ok flow -> Lwt.return_ok (dst, flow)
| Error `Closed ->
(* XXX(dinosaure): be care! [`Closed] at this stage does not mean
* that the bound socket is closed but the socket with the peer is
* closed. *)
Log.err (fun m -> m "The connection was closed by peer.") ;
TCP.close tcp_flow >>= fun () -> Lwt.return_error `Closed
| Error err ->
Log.err (fun m -> m "Got a TLS error: %a." TLS.pp_write_error err) ;
TCP.close tcp_flow >>= fun () -> Lwt.return_error err in
let connection (dst, flow) =
let error_handler = error_handler dst in
let request_handler' reqd = request_handler flow dst reqd in
let conn =
H1.Server_connection.create ?config ~error_handler request_handler'
in
Lwt.return_ok (R.T flow, Paf.Runtime ((module H1.Server_connection), conn))
in
Paf.service connection handshake accept close
let alpn =
let module R = (val Mimic.repr tls_protocol) in
let alpn_of_tls_connection (_edn, flow) =
match TLS.epoch flow with
| Ok { Tls.Core.alpn_protocol; _ } -> alpn_protocol
| Error _ -> None in
let peer_of_tls_connection (edn, _flow) = edn in
(* XXX(dinosaure): [TLS]/[ocaml-tls] should let us to project the underlying
* [flow] and apply [TCP.dst] on it.
* Actually, we did it with the [TLS] module. *)
let injection (_edn, flow) = R.T flow in
{
Alpn.alpn = alpn_of_tls_connection;
Alpn.peer = peer_of_tls_connection;
Alpn.injection;
}
let alpn_service ~tls ?config:(_ = (H1.Config.default, H2.Config.default))
handler =
let handshake tcp_flow =
let dst = TCP.dst tcp_flow in
TLS.server_of_flow tls tcp_flow >>= function
| Ok flow -> Lwt.return_ok (dst, flow)
| Error `Closed ->
(* XXX(dinosaure): be care! [`Closed] at this stage does not mean
* that the bound socket is closed but the socket with the peer is
* closed. *)
Log.err (fun m -> m "The connection was closed by peer.") ;
Lwt.return_error (`Write `Closed)
| Error err ->
Log.err (fun m -> m "Got a TLS error: %a." TLS.pp_write_error err) ;
TCP.close tcp_flow >>= fun () ->
Lwt.return_error (err :> [ TLS.write_error | `Msg of string ]) in
let module R = (val Mimic.repr tls_protocol) in
let request flow edn reqd protocol =
match flow with
| R.T flow -> handler.Alpn.request flow edn reqd protocol
| _ -> assert false
(* XXX(dinosaure): this case should never occur. Indeed, the [injection]
given to [Alpn.service] only create a [tls_protocol] flow. We just
destruct it and give it to [request_handler]. *)
in
Alpn.service alpn { handler with request } handshake accept close
let serve ?stop service t = Paf.serve ?stop service t
end
type transmission = [ `Clear | `TLS of string option ]
let paf_transmission : transmission Mimic.value =
Mimic.make ~name:"paf-transmission"
let paf_endpoint : (Ipaddr.t * int) Mimic.value =
Mimic.make ~name:"paf-endpoint"
open Lwt.Infix
let rec kind_of_flow : Mimic.edn list -> transmission option = function
| Mimic.Edn (k, v) :: r -> (
match Mimic.equal k paf_transmission with
| Some Mimic.Refl -> Some v
| None -> kind_of_flow r)
| [] -> None
let rec endpoint_of_flow : Mimic.edn list -> (Ipaddr.t * int) option = function
| Mimic.Edn (k, v) :: r -> (
match Mimic.equal k paf_endpoint with
| Some Mimic.Refl -> Some v
| None -> endpoint_of_flow r)
| [] -> None
let ( >>? ) = Lwt_result.bind
let run ~ctx handler request =
Mimic.unfold ctx >>? fun ress ->
Mimic.connect ress >>= fun res ->
match (res, kind_of_flow ress) with
| (Error _ as err), _ -> Lwt.return err
| Ok flow, (Some `Clear | None) ->
let edn = endpoint_of_flow ress in
let alpn = match request with `V1 _ -> "http/1.1" | `V2 _ -> "h2c" in
Alpn.run ~alpn handler edn request flow
| Ok flow, Some (`TLS alpn) ->
let edn = endpoint_of_flow ress in
Alpn.run ?alpn handler edn request flow