package tls
Transport Layer Security purely in OCaml
Install
dune-project
Dependency
Authors
Maintainers
Sources
tls-2.0.3.tbz
sha256=d7159ba745f3da2e73844353f020fdbc767393882b47565f8b61b941c351c3d7
sha512=fc136c13bd4c8ff1e69250809c63495299d6e00a58252ed2dd76bd704f7b95f8baa45bde3c5f0f27152767f9986fa3ba183f28d68d336dbf25a25482bd8b44b7
doc/src/tls.unix/tls_unix.ml.html
Source file tls_unix.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
(* NOTE: mostly copied from miou/tls_miou_unix.ml, so any change should be synchronized. *) let src = Logs.Src.create "tls-unix" module Log = (val Logs.src_log src : Logs.LOG) external reraise : exn -> 'a = "%reraise" let ( $ ) f x = f x exception Tls_alert of Tls.Packet.alert_type exception Tls_failure of Tls.Engine.failure exception Closed_by_peer let () = Printexc.register_printer @@ function | Closed_by_peer -> Some "Connection closed by peer" | Tls_alert alert -> Some (Tls.Packet.alert_type_to_string alert) | Tls_failure failure -> Some (Tls.Engine.string_of_failure failure) | _ -> None type state = [ `Active of Tls.Engine.state | `Read_closed of Tls.Engine.state | `Write_closed of Tls.Engine.state | `Closed | `Error of exn ] type t = { role : [ `Server | `Client ]; fd : Unix.file_descr; mutable state : state; mutable linger : string option; read_buffer_size : int; buf : bytes; mutable rd_closed : bool; } let file_descr { fd; _ } = fd let half_close state mode = match (state, mode) with | `Active tls, `read -> `Read_closed tls | `Active tls, `write -> `Write_closed tls | `Active _, `read_write -> `Closed | `Read_closed tls, `read -> `Read_closed tls | `Read_closed _, (`write | `read_write) -> `Closed | `Write_closed tls, `write -> `Write_closed tls | `Write_closed _, (`read | `read_write) -> `Closed | ((`Closed | `Error _) as e), (`read | `write | `read_write) -> e let inject_state tls = function | `Active _ -> `Active tls | `Read_closed _ -> `Read_closed tls | `Write_closed _ -> `Write_closed tls | (`Closed | `Error _) as e -> e let tls_alert a = Tls_alert a let tls_fail f = Tls_failure f let inhibit fn v = try fn v with _ -> () let rec unix_write fd str off len = let written = Unix.write_substring fd str off len in if not (Int.equal written len) then unix_write fd str (off + written) (len - written) let write flow str = Log.debug (fun m -> m "try to write %d byte(s)" (String.length str)); try unix_write flow.fd str 0 (String.length str) with | Unix.Unix_error ((Unix.EPIPE | Unix.ECONNRESET), _, _) -> flow.state <- half_close flow.state `write; raise Closed_by_peer | Unix.Unix_error (_, _, _) as exn -> flow.state <- `Error exn; reraise exn let handle flow tls str = match Tls.Engine.handle_tls tls str with | Ok (state, eof, `Response resp, `Data data) -> Log.debug (fun m -> m "We handled %d byte(s)" (String.length str)); let state = inject_state state flow.state in let state = Option.(value ~default:state (map (fun `Eof -> half_close state `read) eof)) in flow.state <- state; let to_close = flow.state = `Closed in Option.iter (inhibit $ write flow) resp; (* NOTE(dinosaure): [write flow] can set [flow.state]. So we must check if the actual [flow.state] or the [flow.state] after [write flow] want to close the underlying file-descriptor. *) if to_close || flow.state = `Closed then Unix.close flow.fd; data | Error (fail, `Response resp) -> let exn = match fail with | `Alert a -> tls_alert a | f -> tls_fail f in flow.state <- `Error exn; let _ = inhibit (write flow) resp in raise exn let read flow = match Unix.read flow.fd flow.buf 0 (Bytes.length flow.buf) with | 0 -> Ok String.empty | len -> Ok (Bytes.sub_string flow.buf 0 len) | exception Unix.Unix_error (Unix.ECONNRESET, _, _) -> Ok String.empty | exception exn -> Error exn let not_errored = function `Error _ -> false | _ -> true let garbage flow = match flow.linger with | Some "" | None -> false | _ -> true let read_react flow = match flow.state with | `Error exn -> raise exn | `Read_closed _ | `Closed when garbage flow -> (* XXX(dinosaure): [`Closed] can appear "at the same time" than some application-data. In that case, we stored them into [t.linger]. Depending on who closed the connection, [read_react] gives this /garbage/ in any situation (even if the user closed the connection). An extra layer with [read] below check if [`Read_closed]/[`Close] comes from the network (the peer closed the connection) or the user. In the first case, we must give pending application-data. In the second case, we must return [0] (or raise [End_of_file]). *) let mbuf = flow.linger in flow.linger <- None; mbuf | `Read_closed _ | `Closed -> (* XXX(dinosaure): the goal of [read_react] is to read some encrypted bytes and try to decrypt them with [handle]. If the linger is empty, this means that we're trying to get more data (to decrypt) when we can't get any more. From this point of view, it's an error that needs to be notified. However, this error can be interpreted in 2 ways: - we want to have more data decrypted. In this case, this error is expected and may result in the user being told that there is nothing left to read (for example, returning 0). - we attempt a handshake. In this case, we are dealing with an unexpected error. *) raise End_of_file | `Active _ | `Write_closed _ -> Log.debug (fun m -> m "read something from the TLS session"); match read flow with | Error exn -> if not_errored flow.state then flow.state <- `Error exn; raise exn | Ok "" -> (* XXX(dinosaure): see [`Read_closed _ | `Closed] case. *) raise End_of_file | Ok str -> Log.debug (fun m -> m "got %d byte(s)" (String.length str)); match flow.state with | `Active tls | `Read_closed tls | `Write_closed tls -> handle flow tls str | `Closed -> raise End_of_file | `Error exn -> raise exn [@@ocamlformat "disable"] let rec read_in flow ?(off= 0) ?len buf = let len = Option.value ~default:(Bytes.length buf - off) len in let write_in res = let rlen = String.length res in let mlen = min len rlen in Bytes.blit_string res 0 buf off mlen; let linger = if mlen < rlen then Some (String.sub res mlen (rlen - mlen)) else None in flow.linger <- linger; mlen in match flow.linger with | Some res -> write_in res | None -> ( match read_react flow with | None -> read_in ~off ~len flow buf | Some res -> write_in res) let writev flow bufs = match flow.state with | `Closed | `Write_closed _ -> raise Closed_by_peer | `Error exn -> reraise exn | `Active tls | `Read_closed tls -> ( match Tls.Engine.send_application_data tls bufs with | Some (tls, answer) -> flow.state <- inject_state tls flow.state; write flow answer | None -> assert false) let rec drain_handshake flow = let push_linger flow mcs = match (mcs, flow.linger) with | None, _ -> () | scs, None -> flow.linger <- scs | Some cs, Some l -> flow.linger <- Some (l ^ cs) in match flow.state with | `Active tls when not (Tls.Engine.handshake_in_progress tls) -> flow | (`Read_closed _ | `Closed) when garbage flow -> flow | _ -> Log.debug (fun m -> m "start to read something from the TLS session"); let mcs = read_react flow in push_linger flow mcs; drain_handshake flow let close flow = match flow.state with | `Active tls | `Read_closed tls -> let tls, str = Tls.Engine.send_close_notify tls in flow.rd_closed <- true; flow.state <- inject_state tls flow.state; flow.state <- `Closed; inhibit (write flow) str; Unix.close flow.fd | `Write_closed _ -> flow.rd_closed <- true; flow.state <- `Closed; Unix.close flow.fd | `Closed -> flow.rd_closed <- true | `Error _ -> flow.rd_closed <- true; Unix.close flow.fd let closed_by_user flow = function | `read | `read_write -> flow.rd_closed <- true | `write -> () let shutdown flow mode = closed_by_user flow mode; match (flow.state, mode) with | `Active tls, `read -> Log.debug (fun m -> m "shutdown `read"); flow.state <- inject_state tls (half_close flow.state mode) | (`Active tls | `Read_closed tls), (`write | `read_write) -> let tls, str = Tls.Engine.send_close_notify tls in flow.state <- inject_state tls (half_close flow.state mode); (* NOTE(dinosaure): [write flow] can set [flow.state]. So we must check if the actual [flow.state] or the [flow.state] after [write flow] want to close the underlying file-descriptor. *) let to_close = flow.state = `Closed in inhibit (write flow) str; if to_close || flow.state = `Closed then Unix.close flow.fd | `Write_closed tls, (`read | `read_write) -> flow.state <- inject_state tls (half_close flow.state mode); if flow.state = `Closed then Unix.close flow.fd | `Error _, _ -> Unix.close flow.fd | `Read_closed _, `read -> () | `Write_closed _, `write -> () | `Closed, _ -> () let client_of_fd conf ?(read_buffer_size = 0x1000) ?host fd = let conf' = match host with None -> conf | Some host -> Tls.Config.peer conf host in let tls, init = Tls.Engine.client conf' in let tls_flow = { role = `Client; fd; state = `Active tls; linger = None; read_buffer_size; buf = Bytes.make read_buffer_size '\000'; rd_closed = false; } in write tls_flow init; drain_handshake tls_flow let server_of_fd conf ?(read_buffer_size = 0x1000) fd = let tls = Tls.Engine.server conf in let tls_flow = { role = `Server; fd; state = `Active tls; linger = None; read_buffer_size; buf = Bytes.make read_buffer_size '\000'; rd_closed = false; } in drain_handshake tls_flow let write flow ?(off = 0) ?len str = let len = Option.value ~default:(String.length str - off) len in if off < 0 || len < 0 || off > String.length str - len then invalid_arg "Tls_unix.write"; if len > 0 then writev flow [ String.sub str off len ] let read t ?(off= 0) ?len buf = let len = Option.value ~default:(Bytes.length buf - off) len in if off < 0 || len < 0 || off > Bytes.length buf - len then invalid_arg "Tls_unix.read"; if t.rd_closed then 0 else try read_in t ~off ~len buf with End_of_file -> 0 let rec really_read_go t off len buf = let len' = read t buf ~off ~len in if len' == 0 then raise End_of_file else if len - len' > 0 then really_read_go t (off + len') (len - len') buf let really_read t ?(off= 0) ?len buf = let len = Option.value ~default:(Bytes.length buf - off) len in if off < 0 || len < 0 || off > Bytes.length buf - len then invalid_arg "Tls_unix.really_read"; if len > 0 then really_read_go t off len buf let resolve host service = let tcp = Unix.getprotobyname "tcp" in match Unix.getaddrinfo host service [ AI_PROTOCOL tcp.p_proto ] with | [] -> Fmt.invalid_arg "No address for %s:%s" host service | ai :: _ -> ai.ai_addr let connect authenticator (v, port) = let conf = match Tls.Config.client ~authenticator () with | Ok config -> config | Error `Msg msg -> Fmt.invalid_arg "Configuration failure: %s" msg in let addr = resolve v (string_of_int port) in let fd = match addr with | Unix.ADDR_UNIX _ -> invalid_arg "Tls_unix.connect: Invalid UNIX socket" | Unix.ADDR_INET (inet_addr, _) -> if Unix.is_inet6_addr inet_addr then Unix.socket Unix.PF_INET6 Unix.SOCK_STREAM 0 else Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in let host = Result.to_option Domain_name.(Result.bind (of_string v) host) in match Unix.connect fd addr with | () -> client_of_fd conf ?host fd | exception exn -> Unix.close fd; raise exn let epoch flow = match flow.state with | `Active tls | `Read_closed tls | `Write_closed tls -> ( match Tls.Engine.epoch tls with | Error () -> assert false | Ok data -> Some data ) | _ -> None
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>