Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file tls_io.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265letsrc=Logs.Src.create"sendmail.tls-io"moduleLog=(valLogs.src_logsrc)[@@@ocamlformat"disable"](* XXX(dinosaure): (c) Hannes Menhert, this code is [tls_mirage.ml]
* with the possibility to define your [+'a io]. *)moduletypeFLOW=sigtype+'aiotypetanderror=private[>`Closed]valread:t->?off:int->?len:int->bytes->([`End|`Lenofint],error)resultiovalwrite:t->?off:int->?len:int->string->(unit,error)resultiovalclose:t->unitiovalbind:'aio->('a->'bio)->'biovalmap:('a->'b)->'aio->'biovalreturn:'a->'aiovalpp_error:errorFmt.tendmoduleMake(Flow:FLOW)=structlet(>>=)=Flow.bindand(>|=)xf=Flow.mapfxtypeerror=[`Tls_alertofTls.Packet.alert_type|`Tls_failureofTls.Engine.failure|`FlowofFlow.error|`Closed]letpp_errorppf=function|`Tls_failuref->Tls.Engine.pp_failureppff|`Tls_alerta->Fmt.stringppf@@Tls.Packet.alert_type_to_stringa|`Flowe->Flow.pp_errorppfetypet={role:[`Server|`Client];flow:Flow.t;mutablestate:[`ActiveofTls.Engine.state|`Read_closedofTls.Engine.state|`Write_closedofTls.Engine.state|`Closed|`Erroroferror];mutablelinger:stringlist;}lethalf_closestatemode=matchstate,modewith|`Activetls,`read->`Read_closedtls|`Activetls,`write->`Write_closedtls|`Active_,`read_write->`Closed|`Read_closedtls,`read->`Read_closedtls|`Read_closed_,(`write|`read_write)->`Closed|`Write_closedtls,`write->`Write_closedtls|`Write_closed_,(`read|`read_write)->`Closed|(`Closed|`Error_)ase,(`read|`write|`read_write)->eletinject_statetls=function|`Active_->`Activetls|`Read_closed_->`Read_closedtls|`Write_closed_->`Write_closedtls|(`Closed|`Error_)ase->elettls_alerta=`Error(`Tls_alerta)lettls_failf=`Error(`Tls_failuref)letwrite_flowflowbuf=Flow.writeflow.flowbuf>>=function|Ok_aso->Flow.returno|Error`Closed->flow.state<-half_closeflow.state`write;Flow.return(Error(`Flow`Closed))|Errore->flow.state<-`Error(`Flowe);Flow.return(Error(`Flowe))letpp_datappf=function|None->Fmt.stringppf"<none>"|Somestr->Fmt.pfppf"@[<hov>%a@]"(Hxd_string.ppHxd.default)strletread_reactflow=lethandletlsbuf=matchTls.Engine.handle_tlstlsbufwith|Ok(state,eof,`Responseresp,`Datadata)->letstate=inject_statestateflow.stateinletstate=Option.(value~default:state(map(fun`Eof->half_closestate`read)eof))inflow.state<-state;(matchrespwith|None->Flow.return(Ok())|Somebuf->write_flowflowbuf)>>=fun_->Log.debug(funm->m"~> @[<hov>%a@]"pp_datadata);Flow.return(`Okdata)|Error(fail,`Responseresp)->letreason=matchfailwith|`Alerta->tls_alerta|f->tls_failfinflow.state<-reason;Flow.writeflow.flowresp>>=fun_->Flow.returnreasoninmatchflow.statewith|`Error_ase->Flow.returne|`Read_closed_|`Closed->Flow.return`Eof|`Active_|`Write_closed_->letbuf=Bytes.create0x800inFlow.readflow.flowbuf>>=function|Errore->flow.state<-`Error(`Flowe);Flow.return(`Error(`Flowe))|Ok`End->flow.state<-half_closeflow.state`read;Flow.return`Eof|Ok`Lenlen->matchflow.statewith|`Activetls|`Write_closedtls->handletls(Bytes.sub_stringbuf0len)|`Read_closed_|`Closed->Flow.return`Eof|`Error_ase->Flow.returneletrecreadflow=matchflow.lingerwith|[]->(read_reactflow>>=function|`OkNone->readflow|`Ok(Somebuf)->Flow.return(Ok(`Databuf))|`Eof->Flow.return(Ok`Eof)|`Errore->Flow.return(Errore))|bufs->flow.linger<-[];letstr=String.concat""(List.revbufs)inFlow.return(Ok(`Datastr))letwritevflowbufs=matchflow.statewith|`Closed|`Write_closed_->Flow.return(Error`Closed)|`Errore->Flow.return(Error(e:>error))|`Activetls|`Read_closedtls->Log.debug(funm->m"<~ @[<hov>%a@]"(Hxd_string.ppHxd.default)(String.concat""bufs));matchTls.Engine.send_application_datatlsbufswith|Some(tls,answer)->flow.state<-`Activetls;write_flowflowanswer|None->(* "Impossible" due to handshake draining. *)assertfalseletwriteflowbuf=writevflow[buf](*
* XXX bad XXX
* This is a point that should particularly be protected from concurrent r/w.
* Doing this before a `t` is returned is safe; redoing it during rekeying is
* not, as the API client already sees the `t` and can mistakenly interleave
* writes while this is in progress.
* *)letrecdrain_handshakeflow=matchflow.statewith|`Activetlswhennot(Tls.Engine.handshake_in_progresstls)->Flow.return(Okflow)|_->(* read_react re-throws *)read_reactflow>>=function|`Okmbuf->flow.linger<-Option.(to_listmbuf)@flow.linger;drain_handshakeflow|`Errore->Flow.return(Error(e:>error))|`Eof->Flow.return(Error`Closed)letunderlyingflow=flow.flow(*
let reneg ?authenticator ?acceptable_cas ?cert ?(drop = true) flow =
match flow.state with
| `Closed | `Write_closed _ | `Read_closed _ -> Lwt.return @@ Error `Closed
| `Error e -> Lwt.return @@ Error (e :> wr_or_msg)
| `Active tls ->
match Tls.Engine.reneg ?authenticator ?acceptable_cas ?cert tls with
| None -> Lwt.return (Error (`Msg "Renegotiation already in progress"))
| Some (tls', buf) ->
if drop then flow.linger <- [] ;
flow.state <- `Active tls' ;
write_flow flow buf >>= fun _ ->
drain_handshake flow >|= function
| Ok _ -> Ok ()
| Error e -> Error (e :> wr_or_msg)
let key_update ?request flow =
match flow.state with
| `Closed | `Write_closed _ -> Lwt.return @@ Error `Closed
| `Error e -> Lwt.return @@ Error (e :> wr_or_msg)
| `Active tls | `Read_closed tls ->
match Tls.Engine.key_update ?request tls with
| Error _ -> Lwt.return (Error (`Msg "Key update failed"))
| Ok (tls', buf) ->
flow.state <- `Active tls' ;
write_flow flow buf >|= function
| Ok _ as o -> o
| Error e -> Error (e :> wr_or_msg)
*)letcloseflow=(matchflow.statewith|`Activetls|`Read_closedtls->lettls,buf=Tls.Engine.send_close_notifytlsinflow.state<-inject_statetlsflow.state;flow.state<-`Closed;write_flowflowbuf>|=fun_->()|`Write_closed_->flow.state<-`Closed;Flow.return()|_->Flow.return())>>=fun()->Flow.closeflow.flowletshutdownflowmode=matchflow.statewith|`Activetls|`Read_closedtls|`Write_closedtls->lettls,buf=matchflow.state,modewith|(`Activetls|`Read_closedtls),(`write|`read_write)->lettls,buf=Tls.Engine.send_close_notifytlsintls,Somebuf|_,_->tls,Noneinflow.state<-inject_statetls(half_closeflow.statemode);(* as outlined above, this may fail since the TCP flow may already be (half-)closed *)Option.fold~none:(Flow.return())~some:(funb->write_flowflowb>|=fun_->())buf>>=fun()->(matchflow.statewith|`Closed->Flow.closeflow.flow|_->Flow.return())|`Error_|`Closed->Flow.closeflow.flowletclient_of_flowconf?hostflow=letconf'=matchhostwith|None->conf|Somehost->Tls.Config.peerconfhostinlet(tls,init)=Tls.Engine.clientconf'inlettls_flow={role=`Client;flow=flow;state=`Activetls;linger=[];}inwrite_flowtls_flowinit>>=fun_->drain_handshaketls_flowletserver_of_flowconfflow=lettls_flow={role=`Server;flow=flow;state=`Active(Tls.Engine.serverconf);linger=[];}indrain_handshaketls_flowletepochflow=matchflow.statewith|`Closed|`Error_->Error()|`Activetls|`Read_closedtls|`Write_closedtls->Tls.Engine.epochtlsend