124 lines
4.3 KiB
OCaml
124 lines
4.3 KiB
OCaml
|
|
(* Copyright (C) 2023--2024 Petter A. Urkedal <paurkedal@gmail.com>
|
||
|
|
*
|
||
|
|
* This library is free software; you can redistribute it and/or modify it
|
||
|
|
* under the terms of the GNU Lesser General Public License as published by
|
||
|
|
* the Free Software Foundation, either version 3 of the License, or (at your
|
||
|
|
* option) any later version, with the LGPL-3.0 Linking Exception.
|
||
|
|
*
|
||
|
|
* This library is distributed in the hope that it will be useful, but WITHOUT
|
||
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||
|
|
* License for more details.
|
||
|
|
*
|
||
|
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
|
* and the LGPL-3.0 Linking Exception along with this library. If not, see
|
||
|
|
* <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively.
|
||
|
|
*)
|
||
|
|
|
||
|
|
open Caqti_miou_unix
|
||
|
|
|
||
|
|
module TLS_provider = struct
|
||
|
|
type tls_config = Tls.Config.client
|
||
|
|
let tls_config_key = Caqti_tls.Config.client
|
||
|
|
|
||
|
|
module Flow = struct
|
||
|
|
type t =
|
||
|
|
{ fd : Tls_miou_unix.t
|
||
|
|
; queue : (char, Bigarray.int8_unsigned_elt) Ke.Rke.Weighted.t }
|
||
|
|
type bigstring =
|
||
|
|
(char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
|
||
|
|
|
||
|
|
type 'a fiber = 'a
|
||
|
|
|
||
|
|
external get_int32 : bigstring -> int -> int32 = "%caml_bigstring_get32"
|
||
|
|
|
||
|
|
let blit_to_bytes src src_off dst dst_off len =
|
||
|
|
let len0 = len land 3 in
|
||
|
|
let len1 = len asr 2 in
|
||
|
|
|
||
|
|
for i = 0 to len1 - 1 do
|
||
|
|
let i = i * 4 in
|
||
|
|
let v = get_int32 src (src_off + i) in
|
||
|
|
Bytes.set_int32_ne dst (dst_off + i) v
|
||
|
|
done;
|
||
|
|
for i = 0 to len0 - 1 do
|
||
|
|
let i = (len1 * 4) + i in
|
||
|
|
let v = Bigarray.Array1.get src (src_off + i) in
|
||
|
|
Bytes.set dst (dst_off + i) v
|
||
|
|
done
|
||
|
|
|
||
|
|
let flush t =
|
||
|
|
Ke.Rke.Weighted.compress t.queue;
|
||
|
|
match Ke.Rke.Weighted.N.peek t.queue with
|
||
|
|
| [] -> ()
|
||
|
|
| ba :: _ ->
|
||
|
|
let rec split acc ba off len =
|
||
|
|
if len > 0 then begin
|
||
|
|
let max = min len 0x7ff in
|
||
|
|
let buf = Bytes.create max in
|
||
|
|
blit_to_bytes ba off buf 0 max;
|
||
|
|
split (Bytes.unsafe_to_string buf :: acc) ba (off + max) (len - max)
|
||
|
|
end else List.rev acc in
|
||
|
|
let len = Bigarray.Array1.dim ba in
|
||
|
|
let sstr = split [] ba 0 len in
|
||
|
|
List.iter (Tls_miou_unix.write t.fd) sstr;
|
||
|
|
Ke.Rke.Weighted.N.shift_exn t.queue len
|
||
|
|
|
||
|
|
let output_char t chr =
|
||
|
|
match Ke.Rke.Weighted.push t.queue chr with
|
||
|
|
| Some () -> ()
|
||
|
|
| None -> flush t; Ke.Rke.Weighted.push_exn t.queue chr
|
||
|
|
|
||
|
|
external set_int8 : bigstring -> int -> int -> unit = "%caml_ba_set_1"
|
||
|
|
external set_int32 : bigstring -> int -> int32 -> unit
|
||
|
|
= "%caml_bigstring_set32"
|
||
|
|
|
||
|
|
let blit src src_off dst dst_off len =
|
||
|
|
let len0 = len land 3 in
|
||
|
|
let len1 = len asr 2 in
|
||
|
|
|
||
|
|
for i = 0 to len1 - 1 do
|
||
|
|
let i = i * 4 in
|
||
|
|
let v = String.get_int32_ne src (src_off + i) in
|
||
|
|
set_int32 dst (dst_off + i) v
|
||
|
|
done;
|
||
|
|
for i = 0 to len0 - 1 do
|
||
|
|
let i = (len1 * 4) + i in
|
||
|
|
let v = String.get_uint8 src (src_off + i) in
|
||
|
|
set_int8 dst (dst_off + i) v
|
||
|
|
done
|
||
|
|
|
||
|
|
let output_string t str =
|
||
|
|
let rec go str off len =
|
||
|
|
if len > 0 then
|
||
|
|
let max = min (Ke.Rke.Weighted.available t.queue) len in
|
||
|
|
if max > 0 then begin
|
||
|
|
let _ = Ke.Rke.Weighted.N.push t.queue
|
||
|
|
~blit ~length:String.length ~off ~len:max str in
|
||
|
|
if Ke.Rke.Weighted.available t.queue = 0
|
||
|
|
then flush t;
|
||
|
|
go str (off + max) (len - max)
|
||
|
|
end else begin flush t; go str off len end in
|
||
|
|
go str 0 (String.length str)
|
||
|
|
|
||
|
|
let input_char { fd; _ } =
|
||
|
|
let buf = Bytes.make 1 '\000' in
|
||
|
|
let len = Tls_miou_unix.read fd buf in
|
||
|
|
if len = 0 then raise End_of_file else Bytes.get buf 0
|
||
|
|
|
||
|
|
let really_input { fd; _ } buf off len =
|
||
|
|
if len > 0
|
||
|
|
then Tls_miou_unix.really_read fd buf ~off ~len
|
||
|
|
|
||
|
|
let close { fd; _ } = Tls_miou_unix.close fd
|
||
|
|
end
|
||
|
|
|
||
|
|
let start_tls ~config ?host tcp_flow =
|
||
|
|
let fd = Tls_miou_unix.client_of_fd config ?host tcp_flow in
|
||
|
|
let v =
|
||
|
|
{ Flow.fd; queue= fst (Ke.Rke.Weighted.create ~capacity:0x1000 Bigarray.char) } in
|
||
|
|
Ok (System.OCaml ((module Flow), v))
|
||
|
|
end
|
||
|
|
|
||
|
|
let () = System.Net.register_tls_provider (module TLS_provider)
|