123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348(*
* Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org>
* Copyright (c) 2015 Thomas Gazagnaire <thomas@gazagnaire.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*)openSexplib.Convlet(>>=)=Lwt.(>>=)let(>|=)=Lwt.(>|=)letfailfmt=Fmt.kstrf(funs->Lwt.fail(Failures))fmtleterr_tcp_not_supported=fail"%s: TCP is not supported"leterr_tls_not_supported=fail"%s: TLS is not supported"leterr_domain_sockets_not_supported=fail"%s: Unix domain sockets are not supported inside Unikernels"leterr_vchan_not_supported=fail"%s: VCHAN is not supported"leterr_unknown=fail"%s: unknown endpoint type"leterr_ipv6=fail"%s: IPv6 is not supported"moduleFlow=structtypeerror=[`Msgofstring]typewrite_error=[Mirage_flow.write_error|error]letpp_errorppf(`Msgs)=Fmt.stringppfsletpp_write_errorppf=function|#Mirage_flow.write_errorase->Mirage_flow.pp_write_errorppfe|#errorase->pp_errorppfeopenMirage_flow_combinatorstypeflow=Flow:(moduleCONCRETEwithtypeflow='a)*'a->flowletcreate(typea)(moduleM:Mirage_flow.Swithtypeflow=a)t=letm=(moduleConcrete(M):CONCRETEwithtypeflow=a)inFlow(m,t)letread(Flow((moduleF),flow))=F.readflowletwrite(Flow((moduleF),flow))b=F.writeflowbletwritev(Flow((moduleF),flow))b=F.writevflowbletclose(Flow((moduleF),flow))=F.closeflowendtypecallback=Flow.flow->unitLwt.tmoduletypeHandler=sig(** Runtime handler *)typettypeclient[@@derivingsexp]typeserver[@@derivingsexp]valconnect:t->client->Flow.flowLwt.tvallisten:t->server->callback->unitLwt.tendtypetcp_client=[`TCPofIpaddr_sexp.t*int][@@derivingsexp]typetcp_server=[`TCPofint][@@derivingsexp]type'astackv4=(moduleMirage_stack.V4withtypet='a)letstackv4x=xmoduletypeVCHAN=Vchan.S.ENDPOINTwithtypeport=Vchan.Port.tmoduletypeXS=Xs_client_lwt.Stypevchan_client=[|`Vchanof[|`Directofint*Vchan.Port.t(** domain id, port *)|`Domain_socketofstring*Vchan.Port.t(** Vchan Xen domain socket *)]][@@derivingsexp]typevchan_server=[|`Vchanof[|`Directofint*Vchan.Port.t(** domain id, port *)|`Domain_socket(** Vchan Xen domain socket *)]][@@derivingsexp]typevchan=(moduleVCHAN)typexs=(moduleXS)letvchanx=xletxsx=xtype'atls_client=[`TLSofTls.Config.client*'a][@@derivingsexp]type'atls_server=[`TLSofTls.Config.server*'a][@@derivingsexp]typeclient=[tcp_client|vchan_client|clienttls_client][@@derivingsexp]typeserver=[tcp_server|vchan_server|servertls_server][@@derivingsexp]typetls_client'=clienttls_client[@@derivingsexp]typetls_server'=servertls_server[@@derivingsexp]type('c,'s)handler=S:(moduleHandlerwithtypet='aandtypeclient='candtypeserver='s)*'a->('c,'s)handlerlettcp_clientip=Lwt.return(`TCP(i,p))lettcp_server_p=Lwt.return(`TCPp)typet={tcp:(tcp_client,tcp_server)handleroption;tls:(tls_client',tls_server')handleroption;vchan:(vchan_client,vchan_server)handleroption;}letempty={tcp=None;tls=None;vchan=None}letconnectt(c:client)=matchcwith|`TCP_asx->beginmatcht.tcpwith|None->err_tcp_not_supported"connect"|Some(S((moduleS),t))->S.connecttxend|`Vchan_asx->beginmatcht.vchanwith|None->err_vchan_not_supported"connect"|Some(S((moduleS),t))->S.connecttxend|`TLS_asx->beginmatcht.tlswith|None->err_tls_not_supported"connect"|Some(S((moduleS),t))->S.connecttxendletlistent(s:server)f=matchswith|`TCP_asx->beginmatcht.tcpwith|None->err_tcp_not_supported"listen"|Some(S((moduleS),t))->S.listentxfend|`Vchan_asx->beginmatcht.vchanwith|None->err_vchan_not_supported"listen";|Some(S((moduleS),t))->S.listentxfend|`TLS_asx->beginmatcht.tlswith|None->err_tls_not_supported"listen"|Some(S((moduleS),t))->S.listentxfend(******************************************************************************)(* Implementation of handlers *)(******************************************************************************)(* TCP *)moduleTCP(S:Mirage_stack.V4)=structtypet=S.ttypeclient=tcp_client[@@derivingsexp]typeserver=tcp_server[@@derivingsexp]leterr_tcpe=Lwt.fail@@Failure(Format.asprintf"TCP connection failed: %a"S.TCPV4.pp_errore)letconnectt(`TCP(ip,port):client)=matchIpaddr.to_v4ipwith|None->err_ipv6"connect"|Someip->S.TCPV4.create_connection(S.tcpv4t)(ip,port)>>=function|Errore->err_tcpe|Okflow->letflow=Flow.create(moduleS.TCPV4)flowinLwt.returnflowletlistent(`TCPport:server)fn=lets,_u=Lwt.task()inS.listen_tcpv4t~port(funflow->letf=Flow.create(moduleS.TCPV4)flowinfnf);sendmoduleWith_tcp(S:Mirage_stack.V4)=structmoduleM=TCP(S)lethandlerstack=Lwt.return(S((moduleM),stack))letconnectstackt=handlerstack>|=funx->{twithtcp=Somex}endletwith_tcp(typet)t(moduleS:Mirage_stack.V4withtypet=t)stack=letmoduleM=With_tcp(S)inM.connectstackt(* VCHAN *)leterr_vchan_port=fail"%s: invalid Vchan port"letportp=matchVchan.Port.of_stringpwith|Error(`Msgs)->err_vchan_ports|Okp->Lwt.returnpletvchan_client=function|`Vchan_direct(i,p)->portp>|=funp->`Vchan(`Direct(i,p))|`Vchan_domain_socket(i,p)->portp>|=funp->`Vchan(`Domain_socket(i,p))letvchan_server=function|`Vchan_direct(i,p)->portp>|=funp->`Vchan(`Direct(i,p))|`Vchan_domain_socket_->Lwt.return(`Vchan`Domain_socket)moduleVchan(Xs:Xs_client_lwt.S)(V:VCHAN)=structmoduleXS=Conduit_xenstore.Make(Xs)typet=XS.ttypeclient=vchan_client[@@derivingsexp]typeserver=vchan_server[@@derivingsexp]letregister=XS.registerletrecconnectt(c:vchan_client)=matchcwith|`Vchan(`Domain_socket(uid,port))->XS.connectt~remote_name:uid~port>>=funendp->connectt(`Vchanendp:>vchan_client)|`Vchan(`Direct(domid,port))->V.client~domid~port()>>=funflow->Lwt.return(Flow.create(moduleV)flow)letlisten(t:t)(server:vchan_server)fn=matchserverwith|`Vchan(`Direct(domid,port))->V.server~domid~port()>>=funt->fn(Flow.create(moduleV)t)|`Vchan`Domain_socket->XS.listent>>=funconns->Lwt_stream.iter_p(function|`Direct(domid,port)->V.server~domid~port()>>=funt->fn(Flow.create(moduleV)t))connsendletmk_vchan(moduleX:XS)(moduleV:VCHAN)t=letmoduleV=Vchan(X)(V)inV.registert>|=funt->S((moduleV),t)letwith_vchantxyz=mk_vchanxyz>|=funx->{twithvchan=Somex}(* TLS *)letclient_of_bytes_=(* an https:// request doesn't need client-side authentication *)letauthenticator~host:__=OkNoneinTls.Config.client~authenticator()letserver_of_bytesstr=Tls.Config.server_of_sexp(Sexplib.Sexp.of_stringstr)lettls_clientcx=Lwt.return(`TLS(client_of_bytesc,x))lettls_serversx=Lwt.return(`TLS(server_of_bytess,x))moduleTLS=structmoduleTLS=Tls_mirage.Make(Flow)leterr_flow_writeme=fail"%s: %a"mTLS.pp_write_erroretypex=ttypet=xtypeclient=tls_client'[@@derivingsexp]typeserver=tls_server'[@@derivingsexp]letconnect(t:t)(`TLS(c,x):client)=connecttx>>=funflow->TLS.client_of_flowcflow>>=function|Errore->err_flow_write"connect"e|Okflow->Lwt.return(Flow.create(moduleTLS)flow)letlisten(t:t)(`TLS(c,x):server)fn=listentx(funflow->TLS.server_of_flowcflow>>=function|Errore->err_flow_write"listen"e|Okflow->fn(Flow.create(moduleTLS)flow))endlettlst=Lwt.return(S((moduleTLS),t))letwith_tlst=tlst>|=funx->{twithtls=Somex}typeconduit=tmoduletypeS=sigtypet=conduitvalempty:tmoduleWith_tcp(S:Mirage_stack.V4):sigvalconnect:S.t->t->tLwt.tendvalwith_tcp:t->'astackv4->'a->tLwt.tvalwith_tls:t->tLwt.tvalwith_vchan:t->xs->vchan->string->tLwt.tvalconnect:t->client->Flow.flowLwt.tvallisten:t->server->callback->unitLwt.tendletrecclient(e:Conduit.endp):clientLwt.t=matchewith|`TCP(x,y)->tcp_clientxy|`Unix_domain_socket_->err_domain_sockets_not_supported"client"|`Vchan_direct_|`Vchan_domain_socket_asx->vchan_clientx|`TLS(x,y)->clienty>>=func->tls_clientxc|`Unknowns->err_unknownsletrecserver(e:Conduit.endp):serverLwt.t=matchewith|`TCP(x,y)->tcp_serverxy|`Unix_domain_socket_->err_domain_sockets_not_supported"server"|`Vchan_direct_|`Vchan_domain_socket_asx->vchan_serverx|`TLS(x,y)->servery>>=funs->tls_serverxs|`Unknowns->err_unknownsmoduleContext(R:Mirage_random.S)(C:Mirage_clock.MCLOCK)(S:Mirage_stack.V4)=structtypet=Resolver_lwt.t*conduitmoduleRES=Resolver_mirage.Make_with_stack(R)(C)(S)letconduit=emptyletstackv4=stackv4(moduleS:Mirage_stack.V4withtypet=S.t)letcreate?(tls=false)stack=letres=Resolver_lwt.init()inRES.R.register~stackres;with_tcpconduitstackv4stack>>=funconduit->iftlsthenwith_tlsconduit>|=funconduit->res,conduitelseLwt.return(res,conduit)end