New upstream version 4.0.2
Stephane Glondu
2 years ago
0 | name: Conduit | |
1 | on: [push, pull_request] | |
2 | jobs: | |
3 | tests: | |
4 | name: Tests | |
5 | runs-on: ${{ matrix.operating-system }} | |
6 | strategy: | |
7 | matrix: | |
8 | ocaml-version: [ '4.08.1', '4.09.0', '4.10.0', '4.11.1' ] | |
9 | operating-system: [ ubuntu-latest ] | |
10 | steps: | |
11 | - uses: actions/checkout@v2 | |
12 | - uses: avsm/setup-ocaml@v1 | |
13 | with: | |
14 | ocaml-version: ${{ matrix.ocaml-version }} | |
15 | - name: Deps | |
16 | run: | | |
17 | opam pin add -n conduit.dev . | |
18 | opam pin add -n conduit-lwt.dev . | |
19 | opam pin add -n conduit-lwt-unix.dev . | |
20 | opam pin add -n conduit-async.dev . | |
21 | opam pin add -n conduit-mirage.dev . | |
22 | opam depext -y conduit conduit-lwt conduit-lwt-unix conduit-async conduit-mirage | |
23 | opam install --deps-only -t . | |
24 | - name: Build | |
25 | run: opam exec -- dune build | |
26 | - name: Test | |
27 | run: opam exec -- dune runtest --no-buffer --verbose -j 1 |
0 | version = 0.18.0 | |
1 | profile = conventional | |
2 | break-infix = fit-or-vertical | |
3 | parse-docstrings = true | |
4 | module-item-spacing = compact |
0 | ## v4.0.2 (2021-10-09) | |
1 | ||
2 | * Adapt conduit-mirage to tls 0.15.0 (@hannesm #404) | |
3 | * Remove Conduit_mirage.Endpoint.ok_authenticator (@hannesm #404) | |
4 | * Now Conduit_mirage.Endpoint.server does not use an authenticator - and thus | |
5 | not request a client certificate (@hannesm #404) | |
6 | ||
7 | ## v4.0.1 (2021-08-06) | |
8 | ||
9 | * Add missing `ipaddr-sexp` dependency on conduit-async (#385, @anmonteiro) | |
10 | * Update the link of the documentation (959f57a & #398, reported by @misterfish, @zshipko, @dinosaure) | |
11 | * Gitignore `opam/` even if it is a symlink (#394, @CraigFe, @avsm) | |
12 | * Adapt `conduit-lwt-unix` to `tls.0.14.0` (#396, @hannesm, @dinosaure) | |
13 | ||
14 | ## v4.0.0 (2021-04-15) | |
15 | ||
16 | * conduit-mirage: replace the alias `X509_lwt.priv` by | |
17 | `Tls.Config.certchain` (@dinosaure, @samoht, #381) | |
18 | * conduit-mirage: Upgrade to dns.5.0.0 and use `Mirage_stack.V4V6` | |
19 | instead of `Mirage_stack.V4` - by this way, conduit-mirage supports | |
20 | IPv6 connection | |
21 | * conduit.3.0.0 will be unavailable with this release. The design | |
22 | discussion did not reach consensus and `conduit.3.0.0` is unmaintained. | |
23 | We advise use to use `conduit.2.*` or `conduit.4.*` then and discard | |
24 | the migration process between `conduit.2.*` and `conduit.3.*`. | |
25 | ||
26 | ## v2.3.0 (2020-02-06) | |
27 | ||
28 | * conduit-mirage: simplify the API to not mix functors and first-class | |
29 | modules anymore. We just use functors now and rely on the mirage tool | |
30 | to apply them properly (#376, @samoht) | |
31 | * add client-side TLS certificate validation using OS trust anchors for | |
32 | `conduit-lwt-unix` and Mozilla's NSS for `conduit-mirage` (#375, @samoht) | |
33 | ||
34 | ## v2.2.2 (2020-06-14) | |
35 | ||
36 | * conduit-lwt-unix no longer calls Mirage_crypto_rng_unix.initialize, and is | |
37 | compatible with tls 0.12.1 (#317 @hannesm) | |
38 | ||
39 | ## v2.2.1 (2020-05-20) | |
40 | ||
41 | * conduit-mirage requires mirage-time (fix opam and dune) (#315 @hannesm) | |
42 | ||
43 | ## v2.2.0 (2020-05-12) | |
44 | ||
45 | * conduit-mirage adapt to dns-client 4.5.0 (#314 @hannesm) | |
46 | ||
47 | ## v2.1.0 (2020-03-14) | |
48 | ||
49 | * port to tls.0.11.0 interfaces which also uses mirage-crypto (#309 @hannesm) | |
50 | * do not use deprecated ppx sexplib declarations (#309 @avsm) | |
51 | * replace Appveyor CI with GitHub Actions (#309 @avsm) | |
52 | ||
53 | ## v2.0.2 (2019-11-02) | |
54 | ||
55 | * mirage: do not raise exceptions in DNS lookup (#305 @hannesm) | |
56 | * mirage: adapt to mirage-stack/mirage-time/mirage-flow/mirage-random 2.0.0 interfaces (#306 #hannesm) | |
57 | * mirage: adapt to dns 4.1.0 changes (#306 @hannesm) | |
58 | * mirage: adapt to vchan 5.0.0 changes (#306 @hannesm) | |
59 | ||
60 | ## v2.0.1 (2019-08-19) | |
61 | ||
62 | * lwt-unix: fix compilation with `lwt_ssl` and fix tests to correctly exercise this | |
63 | part of the codepath (#304 @avsm). | |
64 | ||
65 | ## v2.0.0 (2019-08-17) | |
66 | ||
67 | * lwt-unix: obtain client IP correctly when using TLS connections (#277 @victorgomes) | |
68 | * lwt-unix: replace the dune/ocaml file with a `(select)` build form. | |
69 | This avoids invoking `ocamlfind` from the build, and fits in with the | |
70 | rest of dune builds much more naturally (@avsm). | |
71 | * lwt-unix: force callers to give a custom callback `on_exn` in case of exceptions | |
72 | to avoid random crashes (#261 @kit-ty-kate) | |
73 | * mirage: use `dns-client>=4.0.0` which is the `udns` implementation (#290 @hannesm) | |
74 | * mirage: rename `mirage-conduit` to `conduit-mirage` to fit the naming structure | |
75 | of this library suite more. All new users of Mirage should use `conduit-mirage`, | |
76 | and migrating should involve simply swapping the name in your `dune` and `opam` | |
77 | files (#302 @hannesm @avsm) | |
78 | * async: expose `verify_mode` correctly in `Conduit_async` (#298 @brendanlong) | |
79 | ||
80 | ## v1.5.0 | |
81 | ||
82 | * lwt-unix: Do not close file descriptors more than once, which led to a lot of | |
83 | log spam due to EBADF (#294 @hcarty @avsm) | |
84 | * lwt-unix: Always close channels after handling an event (#283 @hcarty) | |
85 | * Allow TCP to be established from existing file descriptors | |
86 | (for example, an inherited systemd socket) (#144 @SGrondin #282 @timbertson) | |
87 | * async: add `Conduit_async.V3` which provides convenience functions for | |
88 | resolving URIs to addresses (#287 @vbmithr) | |
89 | * `Lwt_ssl`: Enable certification validation (#291 @vouillon) | |
90 | * `Async_ssl`: fix exception raised when other side disconnects | |
91 | due to sharing underlying fd (#295 @bogdan2412) | |
92 | ||
93 | ## v1.4.0 | |
94 | ||
95 | * Use Ipaddr 3.0.0+ interfaces (#284 by @avsm). | |
96 | * Update opam metadata files to the opam 2.0 format (#284 by @avsm) | |
97 | * Hook in an introduction ocamldoc page to the `conduit` odoc (#284 by @avsm) | |
98 | ||
99 | ## v1.3.0 (2018-10-19) | |
100 | ||
101 | * Mark `ppx_sexp_conv` as not just a build dependency. | |
102 | * Switch build to dune from jbuilder. | |
103 | * Depend on more precise versions of `mirage-types` instead | |
104 | of just the generic package. | |
105 | * Fix ocamldoc headings to work with latest odoc (level 2 not 1). | |
106 | ||
107 | ## v1.2.0 (2018-08-06) | |
108 | ||
109 | * Correct depopt for conduit-lwt-unix (#260, @dra27) | |
110 | * async: provide all `Async_ssl` options at config (#263, @vbmithr) | |
111 | * async: add a V2 module for a new versioned API (#265, @rgrinberg) | |
112 | * lwt-unix: do not link with tls.lwt on windows (#267, @samoht) | |
113 | ||
114 | ## v1.1.0 (2018-03-22) | |
115 | ||
116 | * Implement SNI (Server Name Indication) for SSL backend (#255 by @vouillon) | |
117 | * Make hostname optional in `Conduit_lwt_unix_ssl.Client.connect` (#255 by @vouillon) | |
118 | * Fix file descriptor leakage on `EADDRINUSE` for the Lwt backend (#257 by @rixed) | |
119 | ||
120 | ## v1.0.3 (2018-01-06) | |
121 | ||
122 | * Favour resolving over IPv4 instead of IPv6, if both are available | |
123 | and one has to be chosen. (#245 via @rixed) | |
124 | * Fix some warnings with the dummy `Lwt_unix_ssl` module. | |
125 | * Add a direct dependency on xenstore for mirage-conduit. | |
126 | * Support latest Async v0.10.0 interfaces (no more `Async.Std`). | |
127 | ||
128 | ## v1.0.2 (2017-09-13) | |
129 | ||
130 | * Fix regression with TLS/SSL backend: there is no need to set `CONDUIT_TLS` | |
131 | manually when using tls (#234, @hcarty) | |
132 | * Update to lwt.3.0.0 (#236, #241, @rgrinberg and @samoht) | |
133 | * Fix regression in linking with the launchd backend (#240, @samoht) | |
134 | ||
135 | ## v1.0.1 (2017-07-25) | |
136 | ||
137 | * Fix linkage of mirage-conduit with apps, as a `tls.mirage` dependency | |
138 | was missing in the mirage-conduit-3.0.0 release (#232 by @samoht) | |
139 | ||
140 | ## v1.0.0 (2017-07-22) | |
141 | ||
142 | Details on changes: https://discuss.ocaml.org/t/ann-major-releases-of-cohttp-conduit-dns-tcpip/571 | |
143 | ||
144 | Port build to jbuilder, and break up OPAM packages into multiple | |
145 | independent packages instead of being optional dependencies against | |
146 | the main `conduit` package. This makes it significantly easier to | |
147 | depend on precisely the libraries you need, but requires porting | |
148 | applications to use the new `ocamlfind` and `opam` scheme. | |
149 | ||
150 | The new package layout is: | |
151 | ||
152 | - `conduit`: the main `Conduit` module | |
153 | - `conduit-lwt`: the portable Lwt implementation | |
154 | - `conduit-lwt-unix`: the Lwt/Unix implementation | |
155 | - `conduit-async` the Jane Street Async implementation | |
156 | - `mirage-conduit`: the MirageOS compatible implementation | |
157 | ||
158 | In each of these packages, the `opam` and `ocamlfind` package | |
159 | names are now _the same_, so you will need to rename the former | |
160 | subpackages such as `conduit.async` to `conduit-async`. The | |
161 | implementation is otherwise the same, so no other code changes | |
162 | should be required. | |
163 | ||
164 | In return for these breaking changes to the packaging, it is | |
165 | now significantly easier to depend on a particular backend, | |
166 | also for us to rev the interfaces towards a stable 1.0 release. | |
167 | Jbuilder also builds the source tree around 4x faster than it | |
168 | did previously. | |
169 | ||
170 | There are still some optional dependencies remaining, most | |
171 | notably the `tls` and `ssl` packages. If they are present, | |
172 | then conduit will be compiled with TLS support. | |
173 | ||
174 | ## 0.15.4 (2017-05-31) | |
175 | * Lwt: Fix meta file and building with lwt_ssl (#222, @dkim) | |
176 | ||
177 | ## 0.15.3 (2017-05-04) | |
178 | * Lwt: lwt 3.0 support for the tls backend too (#219, @@rgrinberg) | |
179 | ||
180 | ## 0.15.2 (2017-05-02) | |
181 | * Move cstruct dependency from conduit to mirage-conduit | |
182 | ||
183 | ## 0.15.1 (2017-04-25) | |
184 | * Lwt: Lwt 3.0 support (#214) | |
185 | * Async: with_connection (#211) | |
186 | ||
187 | ## 0.15.0 (2017-02-23) | |
188 | * support MirageOS 3, and drop support for earlier versions (#203, #202) | |
189 | ||
190 | ## 0.14.5 (2017-01-24) | |
191 | * Fix exception swallowing (#206) | |
192 | ||
193 | ## 0.14.4 (2017-01-03) | |
194 | * Fix tests (#195) | |
195 | ||
196 | ## 0.14.3 (2017-01-03) | |
197 | * Fix mirage-conduit (@samoht, #188) | |
198 | * Use ppx_driver's ocamlbuild plugin (@rgrinberg, #193) | |
199 | ||
200 | ## 0.14.2 (2017-01-01) | |
201 | * Fix discover.ml (#191) | |
202 | ||
203 | ## 0.14.1 (2016-12-29) | |
204 | * Tests: do not link with lwt.ssl if it is not installed (#186) | |
205 | ||
206 | ## 0.14.0 (2016-12-25) | |
207 | * Add listening backlog option and increase it to 128 by deafult (#151) | |
208 | * Add IP based URI support | |
209 | * Fix server stop issues for all Lwt servers | |
210 | * Add Logs based logging to Lwt server errors (#172) | |
211 | * Add on_exn hook to Lwt servers (#181) | |
212 | * Limit maximum number of active connections (#116) | |
213 | ||
214 | ## 0.13.0 (2016-09-18): | |
215 | * Fix build system to stop compiling things twice (#137) | |
216 | * Lwt: pass uncaught exceptions in server callback to async_exception_hook (#143) | |
217 | * Lwt: stop printing stuff to stdout (#143) | |
218 | * Async: fix swallowing of exceptions | |
219 | * Async: add backlog argument to serve | |
220 | ||
221 | ## 0.12.0 (2016-04-30): | |
222 | * Convert build system to use PPX instead of Camlp4. | |
223 | * Call `set_close_on_exec` on `Lwt_unix` listen sockets (#123) | |
224 | ||
225 | ## 0.11.0 (2016-03-25): | |
226 | * Minimum OCaml version is now 4.02.0 | |
227 | * Add multi-distro Travis testing script. | |
228 | * Switch to using `pa_sexp_conv` for latest sexp. | |
229 | * Support Core/Async >=113.24 | |
230 | * Fix `vchan` example code, and use functoria-style mirage | |
231 | for it (#108 via @jonludlam) | |
232 | * [async] Add an `Ssl_unsupported exception` for Async rather than | |
233 | just raising `Failure` | |
234 | * Workaround for infinite loop when failing to accept new connections | |
235 | (Edwin Torok #115) | |
236 | * Support TLSv1.1 and TLSv1.2 with openssl backend (Edwin Torok #115) | |
237 | * Fix FD leak with the openssl backend (Edwin Torok #115) | |
238 | ||
239 | ## 0.10.0 (2015-12-25): | |
240 | * Add support for CA certificates in [Conduit_async.serve] (#98). | |
241 | * Fix file descriptor leak in Lwt backend (#101 from @hannesm). | |
242 | * Server in `Conduit_lwt_tls` waits for a user callback to finish | |
243 | before accepting more connections. Instead, it should only wait | |
244 | until the connection is accepted and detach client callback (#97). | |
245 | * Close socket when `ssl_accept` fails, e.g. when cipher negotiation | |
246 | mismatch (#104). | |
247 | ||
248 | ## 0.9.0 (2015-10-14): | |
249 | * Add a `Launchd` argument for the Conduit_lwt_unix server listener | |
250 | to support the MacOSX service launcher (#96). | |
251 | ||
252 | ## 0.8.8 (2015-09-15): | |
253 | * Expose a new functor `Conduit_mirage.With_tcp` (#92, by @Drup) | |
254 | * Expose a new functor: `Resolver_mirage.Make_with_stack` to build a DNS | |
255 | resolver using an existing network stack (#92, by @Drup) | |
256 | * Expose `Resolver_mirage.S`, the signature for Mirage's conduit resolvers than | |
257 | can perform DNS lookups. These resolvers now expose their `DNS` implmentation | |
258 | as a submodule (#92, by @Drup) | |
259 | * Expose a ?version arg in Conduit_async_ssl.ssl_listen, default being TLS 1.2 | |
260 | (#94, by @vbmithr) | |
261 | ||
262 | ## 0.8.7 (2015-08-18): | |
263 | * Do not ignore custom context when calling `Conduit_lwt_unix_ssl.accept` | |
264 | (reported by @jrb467 in #88) | |
265 | * `Conduit_lwt_unix.Serve` now passes the client `flow` to the server | |
266 | callback instead of the listening server one. This lets servers | |
267 | retrieve the peer endpoint correctly (reported by @fxfactorial in #87) | |
268 | ||
269 | ## 0.8.6 (2015-07-14) | |
270 | * Add a `Conduit_mirage.Context`, a functor for creating HTTP(s) conduit | |
271 | contexts (with a DNS resolver). | |
272 | ||
273 | ## 0.8.5 (2015-07-12) | |
274 | * Fix client-side `https://` resolution for `Conduit_mirage` | |
275 | ||
276 | ## 0.8.4 (2015-05-29): | |
277 | * Full support for `ocaml-tls.0.5.0` | |
278 | * Breaking API change for mirage-conduit. Now all the flows are dynamic, | |
279 | the functors are becoming first-class values so no big functor to build | |
280 | first. | |
281 | ||
282 | ## 0.8.3 (2015-05-04): | |
283 | * Partial support for `ocaml-tls.0.5.0` | |
284 | * setsockopt TCP_NODELAY fails on a Unix domain socket (#63 by @djs55) | |
285 | ||
286 | ## 0.8.2 (2015-04-18): | |
287 | * Make TLS optional in `Conduit_mirage`, and disable it by default | |
288 | so that it is a developer-only option until it is properly released. | |
289 | It can be enabled by setting the `HAVE_MIRAGE_LWT` env variable. | |
290 | ||
291 | ## 0.8.1 (2015-04-17): | |
292 | * Support Async_SSL version 112.24.00 and higher. | |
293 | * Add a TLS echo server in `tests/async/` | |
294 | * [lwt] Do not leak socket fd when a connect or handshake | |
295 | operation fails (#56 via Edwin Torok). | |
296 | * [async] Do not leak pipes in SSL handling (#54 from Trevor Smith). | |
297 | ||
298 | ## 0.8.0 (2015-03-27): | |
299 | * Add TLS client support for Mirage (#50) | |
300 | * Do not overwrite the default name resolver for Mirage (#49) | |
301 | * Add TLS support using the pure OCaml TLS stack (#46). | |
302 | * Replace the Mirage `Make_flow` functor with `Dynamic_flow` that is | |
303 | easier to extend with more flow types. | |
304 | ||
305 | ## 0.7.2 (2015-01-26): | |
306 | * Add an `error_message` function to simplify error display (#38). | |
307 | * Improvements to documentation (#37). | |
308 | ||
309 | ## 0.7.1 (2014-12-05): | |
310 | * Do not emit debug output when the `CONDUIT_DEBUG` variable is not set. | |
311 | * Do not create symlinks in a local build, which helps with OPAM pins. | |
312 | * Improve ocamldoc for `Conduit_lwt_unix`. | |
313 | ||
314 | ## 0.7.0 (2014-12-04): | |
315 | * Add Lwt-unix support for the native OCaml/TLS stack as an alternative | |
316 | to OpenSSL. This can be activated by setting the `CONDUIT_TLS` environment | |
317 | variable to `native`. If this is not set and OpenSSL is available, then | |
318 | OpenSSL is used by in preference to the pure OCaml implementation. | |
319 | * Add sexp convertors for `Conduit_lwt_unix.ctx` and `Conduit_mirage.ctx` | |
320 | and the `Resolver` service types. | |
321 | * Fix the Mirage tests to the Mirage 2.0.1+ Conduit interfaces. | |
322 | * Add more debugging output when the `CONDUIT_DEBUG` variable is set on Unix. | |
323 | * *Interface breaking:* The `client` and `server` types in `Conduit_lwt_unix` | |
324 | now explicitly label the fields of the tuples with a polymorphic variant. | |
325 | This allows them to remain independent of this library but still be | |
326 | more self-descriptive (i.e. `Port of int` instead of just `int`). | |
327 | ||
328 | ## 0.6.1 (2014-11-07): | |
329 | * When terminating conduits, always close the output channel first before | |
330 | the input channel, so that any pending data in the underlying fd is flushed. | |
331 | ||
332 | ## 0.6.0 (2014-11-04): | |
333 | * Add an explicit `ctx` content to track every conduit's runtime state. | |
334 | * Allow the source interface for a conduit to be set. | |
335 | * Support a `password` callback for the SSL layer (#4). | |
336 | * [lwt] Add stop parameters in main-loop of the server (#5). | |
337 | * Add `Conduit_mirage` with Mirage functor suport. | |
338 | * Add ocamldoc of most interfaces. | |
339 | * Add a `CONDUIT_DEBUG` environment variable to the Unix backends for | |
340 | live debugging. | |
341 | * Add a `conn` value to the callback to query more information about the | |
342 | current connection (#2). | |
343 | * Expose the representation of `Conduit_lwt_unix.flow` in the external signature. | |
344 | This lets library users obtain the original `Lwt_unix.file_descr` when using | |
345 | Conduit libraries like Cohttp. | |
346 | ||
347 | ## 0.5.1 (2014-08-07): | |
348 | * Reenable Async SSL by default. | |
349 | ||
350 | ## 0.5.0 (2014-04-13): | |
351 | * First public release. |
0 | ## ISC License | |
1 | ||
2 | Copyright (c) 2014-2018 The ocaml-conduit contributors | |
3 | ||
4 | Permission to use, copy, modify, and/or distribute this software for | |
5 | any purpose with or without fee is hereby granted, provided that the | |
6 | above copyright notice and this permission notice appear in all | |
7 | copies. | |
8 | ||
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | |
10 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED | |
11 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE | |
12 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |
13 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | |
14 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
15 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
16 | PERFORMANCE OF THIS SOFTWARE. |
0 | .PHONY: build clean test doc | |
1 | ||
2 | build: | |
3 | dune build | |
4 | ||
5 | test: | |
6 | dune runtest | |
7 | ||
8 | clean: | |
9 | dune clean | |
10 | ||
11 | doc: | |
12 | dune build @doc |
0 | ## conduit -- an OCaml network connection establishment library | |
1 | ||
2 | [![Build Status](https://travis-ci.org/mirage/ocaml-conduit.svg?branch=master)](https://travis-ci.org/mirage/ocaml-conduit) | |
3 | ||
4 | The `conduit` library takes care of establishing and listening for | |
5 | TCP and SSL/TLS connections for the Lwt and Async libraries. | |
6 | ||
7 | The reason this library exists is to provide a degree of abstraction | |
8 | from the precise SSL library used, since there are a variety of ways | |
9 | to bind to a library (e.g. the C FFI, or the Ctypes library), as well | |
10 | as well as which library is used (just OpenSSL for now). | |
11 | ||
12 | By default, OpenSSL is used as the preferred connection library, but | |
13 | you can force the use of the pure OCaml TLS stack by setting the | |
14 | environment variable `CONDUIT_TLS=native` when starting your program. | |
15 | ||
16 | The opam packages available are: | |
17 | ||
18 | - `conduit`: the main `Conduit` module | |
19 | - `conduit-lwt`: the portable Lwt implementation | |
20 | - `conduit-lwt-unix`: the Lwt/Unix implementation | |
21 | - `conduit-async` the Jane Street Async implementation | |
22 | - `conduit-mirage`: the MirageOS compatible implementation | |
23 | ||
24 | ### Debugging | |
25 | ||
26 | Some of the `Lwt_unix`-based modules use a non-empty `CONDUIT_DEBUG` | |
27 | environment variable to output debugging information to standard error. | |
28 | Just set this variable when running the program to see what URIs | |
29 | are being resolved to. | |
30 | ||
31 | ### Further Informartion | |
32 | ||
33 | * **API Docs:** https://mirage.github.io/ocaml-conduit/ | |
34 | * **WWW:** https://github.com/mirage/ocaml-conduit | |
35 | * **E-mail:** <mirageos-devel@lists.xenproject.org> | |
36 | * **Bugs:** https://github.com/mirage/ocaml-conduit/issues |
0 | version: "4.0.2" | |
1 | opam-version: "2.0" | |
2 | maintainer: "anil@recoil.org" | |
3 | authors: [ | |
4 | "Anil Madhavapeddy" "Thomas Leonard" "Thomas Gazagnaire" "Rudi Grinberg" | |
5 | ] | |
6 | license: "ISC" | |
7 | tags: "org:mirage" | |
8 | homepage: "https://github.com/mirage/ocaml-conduit" | |
9 | bug-reports: "https://github.com/mirage/ocaml-conduit/issues" | |
10 | depends: [ | |
11 | "ocaml" {>= "4.03.0"} | |
12 | "dune" | |
13 | "core" | |
14 | "uri" {>= "4.0.0"} | |
15 | "ppx_here" {>= "v0.9.0"} | |
16 | "ppx_sexp_conv" {>="v0.13.0"} | |
17 | "sexplib" | |
18 | "conduit" {=version} | |
19 | "async" {>= "v0.10.0"} | |
20 | "ipaddr" {>= "3.0.0"} | |
21 | "ipaddr-sexp" {>= "4.0.0"} | |
22 | ] | |
23 | depopts: ["async_ssl"] | |
24 | conflicts: [ | |
25 | "async_ssl" {< "v0.9.0"} | |
26 | ] | |
27 | build: [ | |
28 | ["dune" "subst"] {pinned} | |
29 | ["dune" "build" "-p" name "-j" jobs] | |
30 | ] | |
31 | dev-repo: "git+https://github.com/mirage/ocaml-conduit.git" | |
32 | synopsis: "A network connection establishment library for Async"⏎ |
0 | version: "4.0.2" | |
1 | opam-version: "2.0" | |
2 | maintainer: "anil@recoil.org" | |
3 | authors: [ | |
4 | "Anil Madhavapeddy" "Thomas Leonard" "Thomas Gazagnaire" "Rudi Grinberg" | |
5 | ] | |
6 | license: "ISC" | |
7 | tags: "org:mirage" | |
8 | homepage: "https://github.com/mirage/ocaml-conduit" | |
9 | bug-reports: "https://github.com/mirage/ocaml-conduit/issues" | |
10 | depends: [ | |
11 | "ocaml" {>= "4.07.0"} | |
12 | "dune" | |
13 | "base-unix" | |
14 | "logs" | |
15 | "ppx_sexp_conv" {>="v0.13.0"} | |
16 | "conduit-lwt" {=version} | |
17 | "lwt" {>= "3.0.0"} | |
18 | "uri" {>= "1.9.4"} | |
19 | "ipaddr" {>= "4.0.0"} | |
20 | "ipaddr-sexp" | |
21 | "ca-certs" | |
22 | "lwt_log" {with-test} | |
23 | "ssl" {with-test} | |
24 | "lwt_ssl" {with-test} | |
25 | ] | |
26 | depopts: ["tls" "lwt_ssl" "launchd"] | |
27 | conflicts: [ | |
28 | "tls" {< "0.14.0"} | |
29 | "ssl" {< "0.5.9"} | |
30 | ] | |
31 | build: [ | |
32 | ["dune" "subst"] {pinned} | |
33 | ["dune" "build" "-p" name "-j" jobs] | |
34 | ] | |
35 | dev-repo: "git+https://github.com/mirage/ocaml-conduit.git" | |
36 | synopsis: "A network connection establishment library for Lwt_unix"⏎ |
0 | version: "4.0.2" | |
1 | opam-version: "2.0" | |
2 | maintainer: "anil@recoil.org" | |
3 | authors: [ | |
4 | "Anil Madhavapeddy" "Thomas Leonard" "Thomas Gazagnaire" "Rudi Grinberg" | |
5 | ] | |
6 | license: "ISC" | |
7 | tags: "org:mirage" | |
8 | homepage: "https://github.com/mirage/ocaml-conduit" | |
9 | bug-reports: "https://github.com/mirage/ocaml-conduit/issues" | |
10 | depends: [ | |
11 | "ocaml" {>= "4.03.0"} | |
12 | "dune" | |
13 | "base-unix" | |
14 | "ppx_sexp_conv" {>="v0.13.0"} | |
15 | "sexplib" | |
16 | "conduit" {=version} | |
17 | "lwt" {>= "3.0.0"} | |
18 | ] | |
19 | build: [ | |
20 | ["dune" "subst"] {pinned} | |
21 | ["dune" "build" "-p" name "-j" jobs] | |
22 | ] | |
23 | dev-repo: "git+https://github.com/mirage/ocaml-conduit.git" | |
24 | synopsis: "A portable network connection establishment library using Lwt"⏎ |
0 | version: "4.0.2" | |
1 | opam-version: "2.0" | |
2 | maintainer: "anil@recoil.org" | |
3 | authors: ["Anil Madhavapeddy" "Thomas Leonard" "Thomas Gazagnaire"] | |
4 | license: "ISC" | |
5 | tags: "org:mirage" | |
6 | homepage: "https://github.com/mirage/ocaml-conduit" | |
7 | bug-reports: "https://github.com/mirage/ocaml-conduit/issues" | |
8 | depends: [ | |
9 | "ocaml" {>= "4.07.0"} | |
10 | "dune" | |
11 | "ppx_sexp_conv" {>="v0.13.0"} | |
12 | "sexplib" | |
13 | "uri" {>= "4.0.0"} | |
14 | "cstruct" {>= "3.0.0"} | |
15 | "mirage-stack" {>= "2.2.0"} | |
16 | "mirage-clock" {>= "3.0.0"} | |
17 | "mirage-flow" {>= "2.0.0"} | |
18 | "mirage-flow-combinators" {>= "2.0.0"} | |
19 | "mirage-random" {>= "2.0.0"} | |
20 | "mirage-time" {>= "2.0.0"} | |
21 | "dns-client" {>= "5.0.0"} | |
22 | "conduit-lwt" {=version} | |
23 | "vchan" {>= "5.0.0"} | |
24 | "xenstore" | |
25 | "tls" {>= "0.11.0"} | |
26 | "tls-mirage" {>= "0.11.0"} | |
27 | "ca-certs-nss" | |
28 | "ipaddr" {>= "3.0.0"} | |
29 | "ipaddr-sexp" | |
30 | "tcpip" {with-test} | |
31 | ] | |
32 | conflicts: [ | |
33 | "mirage-conduit" | |
34 | ] | |
35 | ||
36 | build: [ | |
37 | ["dune" "subst"] {pinned} | |
38 | ["dune" "build" "-p" name "-j" jobs] | |
39 | ["dune" "runtest" "-p" name] {with-test} | |
40 | ] | |
41 | dev-repo: "git+https://github.com/mirage/ocaml-conduit.git" | |
42 | synopsis: "A network connection establishment library for MirageOS"⏎ |
0 | version: "4.0.2" | |
1 | opam-version: "2.0" | |
2 | maintainer: "anil@recoil.org" | |
3 | authors: [ | |
4 | "Anil Madhavapeddy" "Thomas Leonard" "Thomas Gazagnaire" "Rudi Grinberg" | |
5 | ] | |
6 | license: "ISC" | |
7 | tags: "org:mirage" | |
8 | homepage: "https://github.com/mirage/ocaml-conduit" | |
9 | doc: "https://mirage.github.io/ocaml-conduit/" | |
10 | bug-reports: "https://github.com/mirage/ocaml-conduit/issues" | |
11 | depends: [ | |
12 | "ocaml" {>= "4.03.0"} | |
13 | "dune" | |
14 | "ppx_sexp_conv" {>="v0.13.0"} | |
15 | "sexplib" | |
16 | "astring" | |
17 | "uri" | |
18 | "logs" {>= "0.5.0"} | |
19 | "ipaddr" {>= "4.0.0"} | |
20 | "ipaddr-sexp" | |
21 | ] | |
22 | build: [ | |
23 | ["dune" "subst"] {pinned} | |
24 | ["dune" "build" "-p" name "-j" jobs] | |
25 | ] | |
26 | dev-repo: "git+https://github.com/mirage/ocaml-conduit.git" | |
27 | synopsis: "A network connection establishment library" | |
28 | description: """ | |
29 | The `conduit` library takes care of establishing and listening for | |
30 | TCP and SSL/TLS connections for the Lwt and Async libraries. | |
31 | ||
32 | The reason this library exists is to provide a degree of abstraction | |
33 | from the precise SSL library used, since there are a variety of ways | |
34 | to bind to a library (e.g. the C FFI, or the Ctypes library), as well | |
35 | as well as which library is used (just OpenSSL for now). | |
36 | ||
37 | By default, OpenSSL is used as the preferred connection library, but | |
38 | you can force the use of the pure OCaml TLS stack by setting the | |
39 | environment variable `CONDUIT_TLS=native` when starting your program. | |
40 | ||
41 | The useful opam packages available that extend this library are: | |
42 | ||
43 | - `conduit`: the main `Conduit` module | |
44 | - `conduit-lwt`: the portable Lwt implementation | |
45 | - `conduit-lwt-unix`: the Lwt/Unix implementation | |
46 | - `conduit-async` the Jane Street Async implementation | |
47 | - `conduit-mirage`: the MirageOS compatible implementation | |
48 | """⏎ |
0 | #!/bin/sh -e | |
1 | ||
2 | echo Setting up a /conduit path in Xenstore | |
3 | xenstore-rm /conduit | |
4 | xenstore-write /conduit "" | |
5 | xenstore-chmod /conduit b0 |
0 | (* | |
1 | * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Sexplib.Std | |
18 | ||
19 | type endp = | |
20 | [ `TCP of Ipaddr_sexp.t * int (** ipaddr and dst port *) | |
21 | | `Unix_domain_socket of string (** unix file path *) | |
22 | | `Vchan_direct of int * string (** domain id, port *) | |
23 | | `Vchan_domain_socket of string * string | |
24 | | `TLS of string * endp (** wrap in a TLS channel, [hostname,endp] *) | |
25 | | `Unknown of string (** failed resolution *) ] | |
26 | [@@deriving sexp] | |
27 | (** The resolver will return an [endp], which the Conduit backend must interpret | |
28 | to make a connection. *) | |
29 | ||
30 | module type IO = sig | |
31 | type +'a t | |
32 | ||
33 | val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t | |
34 | val return : 'a -> 'a t | |
35 | end |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** Interface for establishing reliable stream-oriented connections. | |
18 | ||
19 | This library abstracts the concerns of establishing connections to peers | |
20 | that may be running within the same host (e.g. in another virtual machine) | |
21 | or on a remote host via TCP. It consists of one library that is responsible | |
22 | for {{!transport} establishing individual connections}, and a {{!resolution} | |
23 | name resolver} that maps URIs to endpoints. | |
24 | ||
25 | {2:transport Connection Establishment} | |
26 | ||
27 | Connections are created by identifying remote nodes using an {{!endp} endp} | |
28 | value. To ensure portability, the {!endp} values are translated into | |
29 | concrete connections by separate modules that target [Lwt_unix], [Async] and | |
30 | [Mirage]. This lets those backends use the appropriate local technique for | |
31 | creating the connection (such as using OpenSSL on Unix, or a pure OCaml | |
32 | TLS+TCP implementation on Mirage, or some other combination). | |
33 | ||
34 | The modules dealing with connection establishment are: | |
35 | ||
36 | {!modules:Conduit_lwt_unix Conduit_async Conduit_mirage} | |
37 | ||
38 | {2:resolution Name Resolution} | |
39 | ||
40 | This deals with resolving URIs into a list of {!endp} addresses that can | |
41 | then be connected to by the {{!transport} connection establishment} modules. | |
42 | ||
43 | All of the name resolvers conform to the {!RESOLVER} module type. The | |
44 | OS-specific implementations of this interface are: | |
45 | ||
46 | {!modules:Resolver_lwt Resolver_lwt_unix Resolver_mirage} *) | |
47 | ||
48 | type endp = | |
49 | [ `TCP of Ipaddr.t * int (** IP address and destination port *) | |
50 | | `Unix_domain_socket of string (** Unix domain file path *) | |
51 | | `Vchan_direct of int * string (** domain id, port *) | |
52 | | `Vchan_domain_socket of string * string (** Vchan Xen domain socket *) | |
53 | | `TLS of string * endp (** Wrap in a TLS channel, [hostname,endp] *) | |
54 | | `Unknown of string (** Failed resolution *) ] | |
55 | [@@deriving sexp] | |
56 | (** End points that can potentially be connected to. These are typically | |
57 | returned by a call to a {{!resolution} resolver}. *) | |
58 | ||
59 | (** Module type for cooperative threading that can be satisfied by Lwt or Async *) | |
60 | module type IO = sig | |
61 | type +'a t | |
62 | ||
63 | val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t | |
64 | val return : 'a -> 'a t | |
65 | end |
0 | (* | |
1 | * Copyright (c) 2007-2014 Dave Scott <dave.scott@citrix.com> | |
2 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | * | |
16 | *) | |
17 | ||
18 | open Sexplib.Std | |
19 | ||
20 | type 'a t = Node of string * 'a option * 'a t list [@@deriving sexp] | |
21 | ||
22 | (* Invariant: the only node with an empty string is the root *) | |
23 | let empty = Node ("", None, []) | |
24 | ||
25 | let is_prefix a b = | |
26 | String.length b >= String.length a && String.sub b 0 (String.length a) = a | |
27 | ||
28 | let common_prefix a b = | |
29 | let j = ref 0 in | |
30 | (* length of common prefix *) | |
31 | let skip = ref false in | |
32 | for i = 0 to min (String.length a) (String.length b) - 1 do | |
33 | if not !skip then if a.[i] = b.[i] then incr j else skip := true | |
34 | done; | |
35 | String.sub a 0 !j | |
36 | ||
37 | let sub b a = | |
38 | let length = String.length b - String.length a in | |
39 | String.sub b (String.length b - length) length | |
40 | ||
41 | let string = function Node (s, _, _) -> s | |
42 | ||
43 | (* Relying on the invariant that only the root node has an empty string, it is | |
44 | safe to examine the first characters of the child strings. Moreover since | |
45 | common prefixes are always represented as shared nodes, there can be at most | |
46 | one child with the same initial character as the key we're looking up. *) | |
47 | let choose remaining ns = | |
48 | match List.partition (fun x -> (string x).[0] = remaining.[0]) ns with | |
49 | | [ n ], rest -> Some (n, rest) | |
50 | | [], _ -> None | |
51 | | _ :: _, _ -> assert false | |
52 | ||
53 | let rec insert k v = function | |
54 | (* k could be equal to s *) | |
55 | | Node (s, None, ns) when k = s -> Node (s, Some v, ns) | |
56 | (* k could be a prefix of s *) | |
57 | | Node (s, v', ns) when is_prefix k s -> | |
58 | assert (sub s k <> ""); | |
59 | Node (k, Some v, [ Node (sub s k, v', ns) ]) | |
60 | (* s could be a prefix of k *) | |
61 | | Node (s, v', ns) when is_prefix s k -> ( | |
62 | let remaining = sub k s in | |
63 | assert (remaining <> ""); | |
64 | match choose remaining ns with | |
65 | | Some (n, rest) -> Node (s, v', insert remaining v n :: rest) | |
66 | | None -> Node (s, v', Node (remaining, Some v, []) :: ns)) | |
67 | (* s and k could share a non-empty common prefix *) | |
68 | | Node (s, v', ns) -> | |
69 | let p = common_prefix s k in | |
70 | let s' = sub s p and k' = sub k p in | |
71 | assert (s' <> ""); | |
72 | assert (k' <> ""); | |
73 | Node (p, None, [ Node (s', v', ns); Node (k', Some v, []) ]) | |
74 | ||
75 | let rec fold_over_path f str acc = function | |
76 | | Node (p, v, _) when p = str -> f acc v | |
77 | | Node (p, v, ns) when is_prefix p str -> ( | |
78 | let remaining = sub str p in | |
79 | match choose remaining ns with | |
80 | | Some (n, _) -> fold_over_path f remaining (f acc v) n | |
81 | | None -> f acc v) | |
82 | | _ -> acc | |
83 | ||
84 | let longest_prefix str t = | |
85 | fold_over_path (fun acc b -> if b = None then acc else b) str None t | |
86 | ||
87 | let fold f acc t = | |
88 | let rec inner p acc = function | |
89 | | Node (p', v, ns) -> | |
90 | let pp = p ^ p' in | |
91 | let acc = match v with Some v -> f pp v acc | None -> acc in | |
92 | List.fold_left (fun acc n -> inner pp acc n) acc ns | |
93 | in | |
94 | inner "" acc t |
0 | (* | |
1 | * Copyright (c) 2007-2014 Dave Scott <dave.scott@citrix.com> | |
2 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | * | |
16 | *) | |
17 | ||
18 | (** Radix tree that can do longest-prefix searches on string keys *) | |
19 | ||
20 | type 'a t [@@deriving sexp] | |
21 | (** Radix tree that maps [string] keys to ['a] values *) | |
22 | ||
23 | val empty : 'a t | |
24 | (** An empty tree *) | |
25 | ||
26 | val insert : string -> 'a -> 'a t -> 'a t | |
27 | (** [insert key value tree] returns a new tree with the mapping [key] to [value] *) | |
28 | ||
29 | val longest_prefix : string -> 'a t -> 'a option | |
30 | (** [longest_prefix key tree] finds the key [k] which shares the longest prefix | |
31 | with [key] and returns the associated value. *) | |
32 | ||
33 | val fold : (string -> 'a -> 'b -> 'b) -> 'b -> 'a t -> 'b | |
34 | (** [fold f initial t] folds [f] over all bindings in [t] *) | |
35 | ||
36 | val is_prefix : string -> string -> bool | |
37 | (** [is_prefix a b] returns true if [a] is a prefix of [b] *) |
0 | (library | |
1 | (name conduit) | |
2 | (public_name conduit) | |
3 | (wrapped false) | |
4 | (preprocess | |
5 | (pps ppx_sexp_conv)) | |
6 | (modules conduit conduit_trie resolver) | |
7 | (libraries sexplib ipaddr ipaddr-sexp uri astring)) | |
8 | ||
9 | (documentation | |
10 | (package conduit)) |
0 | {1 Introduction} | |
1 | ||
2 | The {!Conduit} library abstracts the concerns of establishing connections to | |
3 | peers that may be running within the same host (e.g. in another virtual | |
4 | machine) or on a remote host via TCP. It consists of: | |
5 | ||
6 | - The {!Conduit} module with basic type definitions for endpoints | |
7 | - OS-specific modules for {{!transport}establishing individual connections} | |
8 | - The {!Resolver} module for mapping URIs to endpoints | |
9 | - OS-specific {{!resolution}name resolvers} that use available | |
10 | resolution mechanisms | |
11 | ||
12 | {2:transport Connection Establishment} | |
13 | ||
14 | Connections are created by identifying remote nodes using an | |
15 | {{!Conduit.endp}endp} value. To ensure portability, the | |
16 | {{!Conduit.endp}endpoints} are translated into concrete connections by separate | |
17 | modules that target [Lwt_unix], [Async] and [Mirage]. This lets those backends | |
18 | use the appropriate local technique for creating the connection (such as using | |
19 | OpenSSL on Unix, or a pure OCaml TLS+TCP implementation on Mirage, or some | |
20 | other combination). | |
21 | ||
22 | The modules dealing with connection establishment are: | |
23 | {!modules: Conduit_lwt_unix Conduit_async Conduit_mirage} | |
24 | ||
25 | {2:resolution Name Resolution} | |
26 | ||
27 | This deals with resolving URIs into a list of {{!Conduit.endp}endp} | |
28 | addresses that can | |
29 | then be connected to by the {{!transport}connection establishment} modules. | |
30 | ||
31 | All of the name resolvers conform to the {!Resolver.S} module type. | |
32 | The OS-specific implementations of this interface are: | |
33 | {!modules: Resolver_lwt Resolver_lwt_unix Resolver_mirage} | |
34 | ||
35 | {2:resolution Mirage Connections} | |
36 | ||
37 | On Mirage, the networking stack is configured via the application | |
38 | of functors to satisfy various signatures. Some of the available | |
39 | functors are: | |
40 | {!modules: Conduit_xenstore Conduit_localhost} | |
41 | ||
42 | {!indexlist} |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Sexplib.Std | |
18 | open Astring | |
19 | ||
20 | type service = { name : string; port : int; tls : bool } [@@deriving sexp] | |
21 | ||
22 | (** Module type for a {{!resolution} resolver} that can map URIs to concrete | |
23 | {{!Conduit.endp} endpoints} that stream connections can be established with. *) | |
24 | module type S = sig | |
25 | type +'a io | |
26 | (** Abstract type of the cooperative threading library used, normally defined | |
27 | via the {!IO} module type *) | |
28 | ||
29 | type t [@@deriving sexp] | |
30 | (** State handle for a running resolver *) | |
31 | ||
32 | type svc [@@deriving sexp] | |
33 | (** Abstract type for a service entry, which maps a URI scheme into a protocol | |
34 | handler and TCP port *) | |
35 | ||
36 | type rewrite_fn = svc -> Uri.t -> Conduit.endp io | |
37 | (** A rewrite function resolves a {{!svc} service} and a URI into a concrete | |
38 | endpoint. *) | |
39 | ||
40 | type service_fn = string -> svc option io | |
41 | (** A service function maps the string (such as [http] or [ftp]) from a URI | |
42 | scheme into a {{!svc} service} description that includes enough metadata | |
43 | about the service to subsequently {{!rewrite_fn} resolve} it into an | |
44 | {{!Conduit.endp} endpoint}. *) | |
45 | ||
46 | val ( ++ ) : service_fn -> service_fn -> service_fn | |
47 | ||
48 | val init : | |
49 | ?service:service_fn -> ?rewrites:(string * rewrite_fn) list -> unit -> t | |
50 | (** [init ?service ?rewrites] will initialize the resolver and return a state | |
51 | handler. The {{!service_fn} service} argument should contain the | |
52 | system-specific resolution mechanism for URI schemas. | |
53 | ||
54 | The [rewrites] argument can optionally override a subset of the URI domain | |
55 | name with the given {!rewrite_fn} to permit custom resolution rules. For | |
56 | example, a rewrite rule for ".xen" would let the rewrite function resolve | |
57 | hostnames such as "foo.xen" into a shared memory channel for the "foo" | |
58 | virtual machine. *) | |
59 | ||
60 | val add_rewrite : host:string -> f:rewrite_fn -> t -> unit | |
61 | (** [add_rewrite ~host f t] will add to the [t] resolver the [f] rewrite rule | |
62 | for all the domain names that shortest-prefix match [host] *) | |
63 | ||
64 | val set_service : f:service_fn -> t -> unit | |
65 | val service : t -> service_fn | |
66 | ||
67 | val resolve_uri : | |
68 | ?rewrites:(string * rewrite_fn) list -> uri:Uri.t -> t -> Conduit.endp io | |
69 | (** [resolve_uri ?rewrites ~uri t] will use [t] to resolve the [uri] into a | |
70 | concrete endpoint. Any [rewrites] that are passed in will be overlayed on | |
71 | the existing rules within the [t] resolver, but not otherwise modify it. *) | |
72 | end | |
73 | ||
74 | module Make (IO : Conduit.IO) = struct | |
75 | open IO | |
76 | ||
77 | type svc = service [@@deriving sexp] | |
78 | type 'a io = 'a IO.t | |
79 | ||
80 | type rewrite_fn = service -> Uri.t -> Conduit.endp IO.t [@@deriving sexp] | |
81 | (** A rewrite modifies an input URI with more specialization towards a | |
82 | concrete [endp] *) | |
83 | ||
84 | type service_fn = string -> service option IO.t [@@deriving sexp] | |
85 | ||
86 | type t = { | |
87 | default_lookup : rewrite_fn; | |
88 | mutable domains : rewrite_fn Conduit_trie.t; | |
89 | mutable service : service_fn; | |
90 | } | |
91 | [@@deriving sexp] | |
92 | ||
93 | let default_lookup _ uri = | |
94 | (* TODO log *) | |
95 | let host = match Uri.host uri with None -> "" | Some host -> host in | |
96 | return (`Unknown host) | |
97 | ||
98 | let default_service _name = | |
99 | (* TODO log *) | |
100 | return None | |
101 | ||
102 | let host_to_domain_list host = | |
103 | (* TODO: slow, specialise the Trie to be a rev string list instead *) | |
104 | String.concat ~sep:"." (List.rev (String.cuts ~sep:"." host)) | |
105 | ||
106 | let add_rewrite ~host ~f t = | |
107 | t.domains <- Conduit_trie.insert (host_to_domain_list host) f t.domains | |
108 | ||
109 | let set_service ~f t = t.service <- f | |
110 | let service t = t.service | |
111 | let ( ++ ) f g h = f h >>= function None -> g h | x -> return x | |
112 | ||
113 | let init ?(service = default_service) ?(rewrites = []) () = | |
114 | let domains = Conduit_trie.empty in | |
115 | let t = { domains; default_lookup; service } in | |
116 | List.iter (fun (host, f) -> add_rewrite ~host ~f t) rewrites; | |
117 | t | |
118 | ||
119 | let resolve_uri ?rewrites ~uri t = | |
120 | (* Find the service associated with the URI *) | |
121 | match Uri.scheme uri with | |
122 | | None -> return (`Unknown "no scheme") | |
123 | | Some scheme -> ( | |
124 | t.service scheme >>= function | |
125 | | None -> return (`Unknown "unknown scheme") | |
126 | | Some service -> | |
127 | let host = | |
128 | match Uri.host uri with None -> "localhost" | Some host -> host | |
129 | in | |
130 | let trie = | |
131 | (* If there are local rewrites, add them to the trie *) | |
132 | match rewrites with | |
133 | | None -> t.domains | |
134 | | Some rewrites -> | |
135 | List.fold_left | |
136 | (fun acc (host, f) -> | |
137 | Conduit_trie.insert (host_to_domain_list host) f acc) | |
138 | t.domains rewrites | |
139 | in | |
140 | (* Find the longest prefix function that matches this host *) | |
141 | let fn = | |
142 | match | |
143 | Conduit_trie.longest_prefix (host_to_domain_list host) trie | |
144 | with | |
145 | | None -> t.default_lookup | |
146 | | Some fn -> fn | |
147 | in | |
148 | fn service uri >>= fun endp -> | |
149 | if service.tls then return (`TLS (host, endp)) else return endp) | |
150 | end |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** Resolve URIs to endpoints *) | |
18 | ||
19 | type service = { name : string; port : int; tls : bool } [@@deriving sexp] | |
20 | (** Description of a single service. Can be populated from [/etc/services] with | |
21 | the exception of the [tls] field, which indicates if the connection is | |
22 | intended to be TLS/SSL-encrypted or not (e.g. for [https]). *) | |
23 | ||
24 | (** Module type for a {{!resolution} resolver} that can map URIs to concrete | |
25 | {{!endp} endpoints} that stream connections can be established with. *) | |
26 | module type S = sig | |
27 | type +'a io | |
28 | (** Abstract type of the cooperative threading library used, normally defined | |
29 | via the {!IO} module type *) | |
30 | ||
31 | type t [@@deriving sexp] | |
32 | (** State handle for a running resolver *) | |
33 | ||
34 | type svc [@@deriving sexp] | |
35 | (** Abstract type for a service entry, which maps a URI scheme into a protocol | |
36 | handler and TCP port *) | |
37 | ||
38 | type rewrite_fn = svc -> Uri.t -> Conduit.endp io | |
39 | (** A rewrite function resolves a {{!svc} service} and a URI into a concrete | |
40 | endpoint. *) | |
41 | ||
42 | type service_fn = string -> svc option io | |
43 | (** A service function maps the string (such as [http] or [ftp]) from a URI | |
44 | scheme into a {{!svc} service} description that includes enough metadata | |
45 | about the service to subsequently {{!rewrite_fn} resolve} it into an | |
46 | {{!endp} endpoint}. *) | |
47 | ||
48 | val ( ++ ) : service_fn -> service_fn -> service_fn | |
49 | (** [f ++ g] is the composition of the service functions [f] and [g]. *) | |
50 | ||
51 | val init : | |
52 | ?service:service_fn -> ?rewrites:(string * rewrite_fn) list -> unit -> t | |
53 | (** [init ?service ?rewrites] will initialize the resolver and return a state | |
54 | handler. The {{!service_fn} service} argument should contain the | |
55 | system-specific resolution mechanism for URI schemas. | |
56 | ||
57 | The [rewrites] argument can optionally override a subset of the URI domain | |
58 | name with the given {!rewrite_fn} to permit custom resolution rules. For | |
59 | example, a rewrite rule for ".xen" would let the rewrite function resolve | |
60 | hostnames such as "foo.xen" into a shared memory channel for the "foo" | |
61 | virtual machine. *) | |
62 | ||
63 | val add_rewrite : host:string -> f:rewrite_fn -> t -> unit | |
64 | (** [add_rewrite ~host f t] will add to the [t] resolver the [f] rewrite rule | |
65 | for all the domain names that shortest-prefix match [host] *) | |
66 | ||
67 | val set_service : f:service_fn -> t -> unit | |
68 | ||
69 | val service : t -> service_fn | |
70 | (** [service t] is the function which is called when trying to resolve a | |
71 | hostname with [t]. *) | |
72 | ||
73 | val resolve_uri : | |
74 | ?rewrites:(string * rewrite_fn) list -> uri:Uri.t -> t -> Conduit.endp io | |
75 | (** [resolve_uri ?rewrites ~uri t] will use [t] to resolve the [uri] into a | |
76 | concrete endpoint. Any [rewrites] that are passed in will be overlayed on | |
77 | the existing rules within the [t] resolver, but not otherwise modify it. *) | |
78 | end | |
79 | ||
80 | (** Functor to construct a concrete resolver using a {!Conduit.IO} | |
81 | implementation, usually via either Lwt or Async *) | |
82 | module Make (IO : Conduit.IO) : | |
83 | S with type svc = service and type 'a io = 'a IO.t |
0 | (* | |
1 | * Copyright (c) 2012-2017 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | module V1 = V1 | |
18 | module V2 = V2 | |
19 | module V3 = V3 | |
20 | ||
21 | [@@@deprecated "Use Conduit_async.V1"] | |
22 | ||
23 | include V1.Conduit_async |
0 | (library | |
1 | (name conduit_async) | |
2 | (public_name conduit-async) | |
3 | (modules conduit_async private_ssl v1 v2 v3 s) | |
4 | (preprocess | |
5 | (pps ppx_here ppx_sexp_conv)) | |
6 | (libraries | |
7 | conduit | |
8 | async | |
9 | ipaddr-sexp | |
10 | ipaddr.unix | |
11 | uri.services | |
12 | (select | |
13 | private_ssl.ml | |
14 | from | |
15 | (async_ssl -> private_ssl.real.ml) | |
16 | (!async_ssl -> private_ssl.dummy.ml)) | |
17 | (select | |
18 | v1.mli | |
19 | from | |
20 | (async_ssl -> v1.real.mli) | |
21 | (!async_ssl -> v1.dummy.mli)) | |
22 | (select | |
23 | v2.mli | |
24 | from | |
25 | (async_ssl -> v2.real.mli) | |
26 | (!async_ssl -> v2.dummy.mli)) | |
27 | (select | |
28 | v3.mli | |
29 | from | |
30 | (async_ssl -> v3.real.mli) | |
31 | (!async_ssl -> v3.dummy.mli)))) |
0 | open Core | |
1 | ||
2 | module V1 = struct | |
3 | module Ssl = struct | |
4 | module Config = struct | |
5 | type t = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
6 | ||
7 | let verify_certificate _ = | |
8 | failwith "Ssl not available, recompile with Async_ssl" | |
9 | ||
10 | let create ?version:_ ?name:_ ?ca_file:_ ?ca_path:_ ?session:_ ?verify:_ | |
11 | () = | |
12 | failwith "Ssl not available, recompile with Async_ssl" | |
13 | end | |
14 | ||
15 | let connect _cfg _r _w = | |
16 | failwith "Ssl not available, recompile with Async_ssl" | |
17 | ||
18 | let listen ?version:_ ?ca_file:_ ?ca_path:_ ~crt_file:_ ~key_file:_ _ _ = | |
19 | failwith "Ssl not available, recompile with Async_ssl" | |
20 | ||
21 | type session = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
22 | type version = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
23 | type connection = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
24 | end | |
25 | end | |
26 | ||
27 | module V2 = struct | |
28 | module Ssl = struct | |
29 | module Config = struct | |
30 | type t = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
31 | ||
32 | let verify_certificate _ = | |
33 | failwith "Ssl not available, recompile with Async_ssl" | |
34 | ||
35 | let create ?version:_ ?options:_ ?name:_ ?hostname:_ ?allowed_ciphers:_ | |
36 | ?ca_file:_ ?ca_path:_ ?crt_file:_ ?key_file:_ ?session:_ | |
37 | ?verify_modes:_ ?verify:_ () = | |
38 | failwith "Ssl not available, recompile with Async_ssl" | |
39 | end | |
40 | ||
41 | let connect ?cfg:_ _r _w = | |
42 | failwith "Ssl not available, recompile with Async_ssl" | |
43 | ||
44 | let listen _ _r _w = failwith "Ssl not available, recompile with Async_ssl" | |
45 | ||
46 | type version = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
47 | type session = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
48 | type verify = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
49 | type connection = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
50 | type verify_mode = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
51 | type opt = [ `Ssl_not_compiled_in ] [@@deriving sexp] | |
52 | end | |
53 | end |
0 | open Core | |
1 | open Async | |
2 | open Async_ssl | |
3 | ||
4 | let verify_certificate connection = | |
5 | match Ssl.Connection.peer_certificate connection with | |
6 | | None -> return false | |
7 | | Some (Error _) -> return false | |
8 | | Some (Ok _) -> return true | |
9 | ||
10 | let teardown_connection r w = | |
11 | Writer.close ~force_close:(Clock.after (sec 30.)) w >>= fun () -> | |
12 | Reader.close r | |
13 | ||
14 | (* One needs to be careful around Async Readers and Writers that share the same underyling | |
15 | file descriptor, which is something that happens when they're used for sockets. | |
16 | ||
17 | Closing the Reader before the Writer will cause the Writer to throw and complain about | |
18 | its underlying file descriptor being closed. This is why instead of using Reader.pipe | |
19 | directly below, we write out an equivalent version which will first close the Writer | |
20 | before closing the Reader once the input pipe is fully consumed. | |
21 | ||
22 | Additionally, [Writer.pipe] will not close the writer if the pipe is closed, so in | |
23 | order to avoid leaking file descriptors, we allow the pipe 30 seconds to flush before | |
24 | closing the writer. *) | |
25 | let reader_writer_pipes r w = | |
26 | let reader_pipe_r, reader_pipe_w = Pipe.create () in | |
27 | let writer_pipe = Writer.pipe w in | |
28 | upon (Reader.transfer r reader_pipe_w) (fun () -> | |
29 | teardown_connection r w >>> fun () -> Pipe.close reader_pipe_w); | |
30 | upon (Pipe.closed writer_pipe) (fun () -> | |
31 | Deferred.choose | |
32 | [ | |
33 | Deferred.choice (Clock.after (sec 30.)) (fun () -> ()); | |
34 | Deferred.choice (Pipe.downstream_flushed writer_pipe) | |
35 | (fun (_ : Pipe.Flushed_result.t) -> ()); | |
36 | ] | |
37 | >>> fun () -> don't_wait_for (teardown_connection r w)); | |
38 | (reader_pipe_r, writer_pipe) | |
39 | ||
40 | (* [Reader.of_pipe] will not close the pipe when the returned [Reader] is closed, so we | |
41 | manually do that ourselves. | |
42 | ||
43 | [Writer.of_pipe] will create a writer that will raise once the pipe is closed, so we | |
44 | set [raise_when_consumer_leaves] to false. *) | |
45 | let reader_writer_of_pipes app_rd app_wr = | |
46 | Reader.of_pipe (Info.of_string "async_conduit_ssl_reader") app_rd | |
47 | >>= fun app_reader -> | |
48 | upon (Reader.close_finished app_reader) (fun () -> Pipe.close_read app_rd); | |
49 | Writer.of_pipe (Info.of_string "async_conduit_ssl_writer") app_wr | |
50 | >>| fun (app_writer, _) -> | |
51 | Writer.set_raise_when_consumer_leaves app_writer false; | |
52 | (app_reader, app_writer) | |
53 | ||
54 | module V1 = struct | |
55 | module Ssl = struct | |
56 | module Config = struct | |
57 | type t = { | |
58 | version : Ssl.Version.t option; | |
59 | name : string option; | |
60 | ca_file : string option; | |
61 | ca_path : string option; | |
62 | session : (Ssl.Session.t[@sexp.opaque]) option; | |
63 | verify : (Ssl.Connection.t -> bool Deferred.t) option; | |
64 | } | |
65 | [@@deriving sexp] | |
66 | ||
67 | let verify_certificate = verify_certificate | |
68 | ||
69 | let create ?version ?name ?ca_file ?ca_path ?session ?verify () = | |
70 | { version; name; ca_file; ca_path; session; verify } | |
71 | end | |
72 | ||
73 | let connect cfg r w = | |
74 | let { Config.version; name; ca_file; ca_path; session; verify } = cfg in | |
75 | let net_to_ssl, ssl_to_net = reader_writer_pipes r w in | |
76 | let app_to_ssl, app_wr = Pipe.create () in | |
77 | let app_rd, ssl_to_app = Pipe.create () in | |
78 | let verify_connection = | |
79 | match verify with None -> Fn.const (return true) | Some f -> f | |
80 | in | |
81 | Ssl.client ?version ?name ?ca_file ?ca_path ?session ~app_to_ssl | |
82 | ~ssl_to_app ~net_to_ssl ~ssl_to_net () | |
83 | >>= function | |
84 | | Error error -> teardown_connection r w >>= fun () -> Error.raise error | |
85 | | Ok conn -> ( | |
86 | verify_connection conn >>= function | |
87 | | false -> | |
88 | teardown_connection r w >>= fun () -> | |
89 | failwith "Connection verification failed." | |
90 | | true -> | |
91 | reader_writer_of_pipes app_rd app_wr | |
92 | >>| fun (app_reader, app_writer) -> (app_reader, app_writer)) | |
93 | ||
94 | let listen ?(version = Ssl.Version.Tlsv1_2) ?ca_file ?ca_path ~crt_file | |
95 | ~key_file r w = | |
96 | let net_to_ssl, ssl_to_net = reader_writer_pipes r w in | |
97 | let app_to_ssl, app_wr = Pipe.create () in | |
98 | let app_rd, ssl_to_app = Pipe.create () in | |
99 | Ssl.server ?ca_file ?ca_path ~version ~crt_file ~key_file ~app_to_ssl | |
100 | ~ssl_to_app ~net_to_ssl ~ssl_to_net () | |
101 | >>= function | |
102 | | Error error -> teardown_connection r w >>= fun () -> Error.raise error | |
103 | | Ok _ -> | |
104 | reader_writer_of_pipes app_rd app_wr | |
105 | >>| fun (app_reader, app_writer) -> (app_reader, app_writer) | |
106 | ||
107 | type session = (Ssl.Session.t[@sexp.opaque]) [@@deriving sexp] | |
108 | type version = Ssl.Version.t [@@deriving sexp] | |
109 | type connection = (Ssl.Connection.t[@sexp.opaque]) [@@deriving sexp] | |
110 | end | |
111 | end | |
112 | ||
113 | module V2 = struct | |
114 | module Ssl = struct | |
115 | type allowed_ciphers = [ `Only of string list | `Openssl_default | `Secure ] | |
116 | [@@deriving sexp] | |
117 | ||
118 | module Config = struct | |
119 | type t = { | |
120 | version : Ssl.Version.t option; | |
121 | options : Ssl.Opt.t list option; | |
122 | name : string option; | |
123 | hostname : string option; | |
124 | allowed_ciphers : allowed_ciphers option; | |
125 | ca_file : string option; | |
126 | ca_path : string option; | |
127 | crt_file : string option; | |
128 | key_file : string option; | |
129 | session : (Ssl.Session.t[@sexp.opaque]) option; | |
130 | verify_modes : (Verify_mode.t[@sexp.opaque]) list option; | |
131 | verify : (Ssl.Connection.t -> bool Deferred.t) option; | |
132 | } | |
133 | [@@deriving sexp_of] | |
134 | ||
135 | let verify_certificate = verify_certificate | |
136 | ||
137 | let create ?version ?options ?name ?hostname ?allowed_ciphers ?ca_file | |
138 | ?ca_path ?crt_file ?key_file ?session ?verify_modes ?verify () = | |
139 | { | |
140 | version; | |
141 | options; | |
142 | name; | |
143 | hostname; | |
144 | allowed_ciphers; | |
145 | ca_file; | |
146 | ca_path; | |
147 | crt_file; | |
148 | key_file; | |
149 | session; | |
150 | verify_modes; | |
151 | verify; | |
152 | } | |
153 | end | |
154 | ||
155 | let connect ?(cfg = Config.create ()) r w = | |
156 | let { | |
157 | Config.version; | |
158 | options; | |
159 | name; | |
160 | hostname; | |
161 | allowed_ciphers; | |
162 | ca_file; | |
163 | ca_path; | |
164 | crt_file; | |
165 | key_file; | |
166 | session; | |
167 | verify_modes; | |
168 | verify; | |
169 | } = | |
170 | cfg | |
171 | in | |
172 | let net_to_ssl, ssl_to_net = reader_writer_pipes r w in | |
173 | let app_to_ssl, app_wr = Pipe.create () in | |
174 | let app_rd, ssl_to_app = Pipe.create () in | |
175 | let verify_connection = | |
176 | match verify with None -> Fn.const (return true) | Some f -> f | |
177 | in | |
178 | Ssl.client ?version ?options ?name ?hostname ?allowed_ciphers ?ca_file | |
179 | ?ca_path ?crt_file ?key_file ?session ?verify_modes ~app_to_ssl | |
180 | ~ssl_to_app ~net_to_ssl ~ssl_to_net () | |
181 | >>= function | |
182 | | Error error -> teardown_connection r w >>= fun () -> Error.raise error | |
183 | | Ok conn -> ( | |
184 | verify_connection conn >>= function | |
185 | | false -> | |
186 | teardown_connection r w >>= fun () -> | |
187 | failwith "Connection verification failed." | |
188 | | true -> | |
189 | reader_writer_of_pipes app_rd app_wr | |
190 | >>| fun (app_reader, app_writer) -> (app_reader, app_writer)) | |
191 | ||
192 | let listen | |
193 | { | |
194 | Config.version; | |
195 | options; | |
196 | name; | |
197 | allowed_ciphers; | |
198 | ca_file; | |
199 | ca_path; | |
200 | crt_file; | |
201 | key_file; | |
202 | verify_modes; | |
203 | _; | |
204 | } r w = | |
205 | let crt_file, key_file = | |
206 | match (crt_file, key_file) with | |
207 | | Some crt_file, Some key_file -> (crt_file, key_file) | |
208 | | _ -> | |
209 | invalid_arg | |
210 | "Conduit_async_ssl.ssl_listen: crt_file and key_file must be \ | |
211 | specified in cfg." | |
212 | in | |
213 | let net_to_ssl, ssl_to_net = reader_writer_pipes r w in | |
214 | let app_to_ssl, app_wr = Pipe.create () in | |
215 | let app_rd, ssl_to_app = Pipe.create () in | |
216 | Ssl.server ?version ?options ?name ?allowed_ciphers ?ca_file ?ca_path | |
217 | ~crt_file ~key_file ?verify_modes ~app_to_ssl ~ssl_to_app ~net_to_ssl | |
218 | ~ssl_to_net () | |
219 | >>= function | |
220 | | Error error -> teardown_connection r w >>= fun () -> Error.raise error | |
221 | | Ok _ -> | |
222 | reader_writer_of_pipes app_rd app_wr | |
223 | >>| fun (app_reader, app_writer) -> (app_reader, app_writer) | |
224 | ||
225 | type verify_mode = Ssl.Verify_mode.t [@@deriving sexp_of] | |
226 | type session = (Ssl.Session.t[@sexp.opaque]) [@@deriving sexp_of] | |
227 | type version = Ssl.Version.t [@@deriving sexp] | |
228 | type connection = Ssl.Connection.t [@@deriving sexp_of] | |
229 | type opt = Ssl.Opt.t [@@deriving sexp] | |
230 | end | |
231 | end |
0 | open Async | |
1 | ||
2 | module type V1 = sig | |
3 | type session [@@deriving sexp_of] | |
4 | type ssl_conn [@@deriving sexp_of] | |
5 | type ssl_version [@@deriving sexp] | |
6 | ||
7 | module Conduit_async : sig | |
8 | module Ssl : sig | |
9 | type config [@@deriving sexp] | |
10 | ||
11 | val verify_certificate : ssl_conn -> bool Deferred.t | |
12 | ||
13 | val configure : | |
14 | ?version:ssl_version -> | |
15 | ?name:string -> | |
16 | ?ca_file:string -> | |
17 | ?ca_path:string -> | |
18 | ?session:session -> | |
19 | ?verify:(ssl_conn -> bool Deferred.t) -> | |
20 | unit -> | |
21 | config | |
22 | end | |
23 | ||
24 | type +'a io = 'a Deferred.t | |
25 | type ic = Reader.t | |
26 | type oc = Writer.t | |
27 | ||
28 | type addr = | |
29 | [ `OpenSSL of string * Ipaddr.t * int | |
30 | | `OpenSSL_with_config of string * Ipaddr.t * int * Ssl.config | |
31 | | `TCP of Ipaddr.t * int | |
32 | | `Unix_domain_socket of string ] | |
33 | [@@deriving sexp] | |
34 | ||
35 | val connect : ?interrupt:unit io -> addr -> (ic * oc) io | |
36 | ||
37 | val with_connection : | |
38 | ?interrupt:unit io -> addr -> (ic -> oc -> unit io) -> unit io | |
39 | ||
40 | type trust_chain = | |
41 | [ `Ca_file of string | |
42 | | `Ca_path of string | |
43 | | `Search_file_first_then_path of [ `File of string ] * [ `Path of string ] | |
44 | ] | |
45 | [@@deriving sexp] | |
46 | ||
47 | type openssl = | |
48 | [ `OpenSSL of [ `Crt_file_path of string ] * [ `Key_file_path of string ] ] | |
49 | [@@deriving sexp] | |
50 | ||
51 | type server = | |
52 | [ openssl | `TCP | `OpenSSL_with_trust_chain of openssl * trust_chain ] | |
53 | [@@deriving sexp] | |
54 | ||
55 | val serve : | |
56 | ?max_connections:int -> | |
57 | ?backlog:int -> | |
58 | ?buffer_age_limit:Writer.buffer_age_limit -> | |
59 | on_handler_error: | |
60 | [ `Call of ([< Socket.Address.t ] as 'a) -> exn -> unit | |
61 | | `Ignore | |
62 | | `Raise ] -> | |
63 | server -> | |
64 | ('a, 'b) Tcp.Where_to_listen.t -> | |
65 | ('a -> ic -> oc -> unit io) -> | |
66 | ('a, 'b) Tcp.Server.t io | |
67 | end | |
68 | ||
69 | module Conduit_async_ssl : sig | |
70 | module Ssl_config = Conduit_async.Ssl | |
71 | ||
72 | val ssl_connect : | |
73 | Conduit_async.Ssl.config -> | |
74 | Reader.t -> | |
75 | Writer.t -> | |
76 | (Reader.t * Writer.t) Deferred.t | |
77 | ||
78 | val ssl_listen : | |
79 | ?version:ssl_version -> | |
80 | ?ca_file:string -> | |
81 | ?ca_path:string -> | |
82 | crt_file:string -> | |
83 | key_file:string -> | |
84 | Reader.t -> | |
85 | Writer.t -> | |
86 | (Reader.t * Writer.t) Deferred.t | |
87 | end | |
88 | end | |
89 | ||
90 | module type V2 = sig | |
91 | type allowed_ciphers = [ `Only of string list | `Openssl_default | `Secure ] | |
92 | [@@deriving sexp] | |
93 | ||
94 | type ssl_version [@@deriving sexp] | |
95 | type session [@@deriving sexp_of] | |
96 | type verify_mode [@@deriving sexp_of] | |
97 | type ssl_opt [@@deriving sexp] | |
98 | type ssl_conn [@@deriving sexp_of] | |
99 | ||
100 | module Ssl : sig | |
101 | module Config : sig | |
102 | type t [@@deriving sexp_of] | |
103 | ||
104 | val create : | |
105 | ?version:ssl_version -> | |
106 | ?options:ssl_opt list -> | |
107 | ?name:string -> | |
108 | ?hostname:string -> | |
109 | ?allowed_ciphers:allowed_ciphers -> | |
110 | ?ca_file:string -> | |
111 | ?ca_path:string -> | |
112 | ?crt_file:string -> | |
113 | ?key_file:string -> | |
114 | ?session:session -> | |
115 | ?verify_modes:verify_mode list -> | |
116 | ?verify:(ssl_conn -> bool Deferred.t) -> | |
117 | unit -> | |
118 | t | |
119 | end | |
120 | end | |
121 | ||
122 | type addr = | |
123 | [ `OpenSSL of Ipaddr.t * int * Ssl.Config.t | |
124 | | `TCP of Ipaddr.t * int | |
125 | | `Unix_domain_socket of string ] | |
126 | [@@deriving sexp_of] | |
127 | ||
128 | val connect : | |
129 | ?interrupt:unit Deferred.t -> addr -> (Reader.t * Writer.t) Deferred.t | |
130 | ||
131 | val with_connection : | |
132 | ?interrupt:unit Deferred.t -> | |
133 | addr -> | |
134 | (Reader.t -> Writer.t -> unit Deferred.t) -> | |
135 | unit Deferred.t | |
136 | ||
137 | type trust_chain = | |
138 | [ `Ca_file of string | |
139 | | `Ca_path of string | |
140 | | `Search_file_first_then_path of [ `File of string ] * [ `Path of string ] | |
141 | ] | |
142 | [@@deriving sexp] | |
143 | ||
144 | type openssl = | |
145 | [ `OpenSSL of [ `Crt_file_path of string ] * [ `Key_file_path of string ] ] | |
146 | [@@deriving sexp] | |
147 | ||
148 | type server = | |
149 | [ openssl | `TCP | `OpenSSL_with_trust_chain of openssl * trust_chain ] | |
150 | [@@deriving sexp] | |
151 | ||
152 | val serve : | |
153 | ?max_connections:int -> | |
154 | ?backlog:int -> | |
155 | ?buffer_age_limit:Writer.buffer_age_limit -> | |
156 | on_handler_error: | |
157 | [ `Call of ([< Socket.Address.t ] as 'a) -> exn -> unit | |
158 | | `Ignore | |
159 | | `Raise ] -> | |
160 | server -> | |
161 | ('a, 'b) Tcp.Where_to_listen.t -> | |
162 | ('a -> Reader.t -> Writer.t -> unit Deferred.t) -> | |
163 | ('a, 'b) Tcp.Server.t Deferred.t | |
164 | end | |
165 | ||
166 | module type V3 = sig | |
167 | type allowed_ciphers = [ `Only of string list | `Openssl_default | `Secure ] | |
168 | [@@deriving sexp] | |
169 | ||
170 | type ssl_version [@@deriving sexp] | |
171 | type session [@@deriving sexp_of] | |
172 | type verify_mode [@@deriving sexp_of] | |
173 | type ssl_opt [@@deriving sexp] | |
174 | type ssl_conn [@@deriving sexp_of] | |
175 | ||
176 | module Ssl : sig | |
177 | module Config : sig | |
178 | type t [@@deriving sexp_of] | |
179 | ||
180 | val create : | |
181 | ?version:ssl_version -> | |
182 | ?options:ssl_opt list -> | |
183 | ?name:string -> | |
184 | ?hostname:string -> | |
185 | ?allowed_ciphers:allowed_ciphers -> | |
186 | ?ca_file:string -> | |
187 | ?ca_path:string -> | |
188 | ?crt_file:string -> | |
189 | ?key_file:string -> | |
190 | ?session:session -> | |
191 | ?verify_modes:verify_mode list -> | |
192 | ?verify:(ssl_conn -> bool Deferred.t) -> | |
193 | unit -> | |
194 | t | |
195 | end | |
196 | end | |
197 | ||
198 | type _ addr = | |
199 | | OpenSSL : | |
200 | Socket.Address.Inet.t * Ssl.Config.t | |
201 | -> Socket.Address.Inet.t addr | |
202 | | Inet : Socket.Address.Inet.t -> Socket.Address.Inet.t addr | |
203 | | Unix : Socket.Address.Unix.t -> Socket.Address.Unix.t addr | |
204 | [@@deriving sexp_of] | |
205 | ||
206 | type _ tcp_sock = | |
207 | | Inet_sock : | |
208 | ([ `Active ], Socket.Address.Inet.t) Socket.t | |
209 | -> Socket.Address.Inet.t tcp_sock | |
210 | | Unix_sock : | |
211 | ([ `Active ], Socket.Address.Unix.t) Socket.t | |
212 | -> Socket.Address.Unix.t tcp_sock | |
213 | ||
214 | val resolve_uri : | |
215 | ?options:Unix.Addr_info.getaddrinfo_option list -> | |
216 | Uri.t -> | |
217 | Socket.Address.Inet.t addr Deferred.t | |
218 | ||
219 | val connect : | |
220 | ?interrupt:unit Deferred.t -> | |
221 | 'a addr -> | |
222 | ('a tcp_sock * Reader.t * Writer.t) Deferred.t | |
223 | ||
224 | val with_connection : | |
225 | ?interrupt:unit Deferred.t -> | |
226 | 'a addr -> | |
227 | ('a tcp_sock -> Reader.t -> Writer.t -> 'b Deferred.t) -> | |
228 | 'b Deferred.t | |
229 | ||
230 | val connect_uri : | |
231 | ?options:Unix.Addr_info.getaddrinfo_option list -> | |
232 | ?interrupt:unit Deferred.t -> | |
233 | Uri.t -> | |
234 | (Socket.Address.Inet.t tcp_sock * Reader.t * Writer.t) Deferred.t | |
235 | ||
236 | val with_connection_uri : | |
237 | ?options:Unix.Addr_info.getaddrinfo_option list -> | |
238 | ?interrupt:unit Deferred.t -> | |
239 | Uri.t -> | |
240 | (Socket.Address.Inet.t tcp_sock -> Reader.t -> Writer.t -> 'a Deferred.t) -> | |
241 | 'a Deferred.t | |
242 | ||
243 | type trust_chain = | |
244 | [ `Ca_file of string | |
245 | | `Ca_path of string | |
246 | | `Search_file_first_then_path of [ `File of string ] * [ `Path of string ] | |
247 | ] | |
248 | [@@deriving sexp] | |
249 | ||
250 | type openssl = | |
251 | [ `OpenSSL of [ `Crt_file_path of string ] * [ `Key_file_path of string ] ] | |
252 | [@@deriving sexp] | |
253 | ||
254 | type server = | |
255 | [ openssl | `TCP | `OpenSSL_with_trust_chain of openssl * trust_chain ] | |
256 | [@@deriving sexp] | |
257 | ||
258 | val serve : | |
259 | ?max_connections:int -> | |
260 | ?backlog:int -> | |
261 | ?buffer_age_limit:Writer.buffer_age_limit -> | |
262 | on_handler_error: | |
263 | [ `Call of ([< Socket.Address.t ] as 'a) -> exn -> unit | |
264 | | `Ignore | |
265 | | `Raise ] -> | |
266 | server -> | |
267 | ('a, 'b) Tcp.Where_to_listen.t -> | |
268 | ('a -> Reader.t -> Writer.t -> unit Deferred.t) -> | |
269 | ('a, 'b) Tcp.Server.t Deferred.t | |
270 | end |
0 | include | |
1 | S.V1 | |
2 | with type session = [ `Ssl_not_compiled_in ] | |
3 | and type ssl_version = [ `Ssl_not_compiled_in ] | |
4 | and type ssl_conn = [ `Ssl_not_compiled_in ] |
0 | open Core | |
1 | open Async | |
2 | open Private_ssl.V1 | |
3 | ||
4 | type session = Ssl.session [@@deriving sexp] | |
5 | type ssl_version = Ssl.version [@@deriving sexp] | |
6 | type ssl_conn = Ssl.connection [@@deriving sexp] | |
7 | ||
8 | module Conduit_async = struct | |
9 | module Ssl = struct | |
10 | include Ssl | |
11 | ||
12 | type nonrec config = Config.t [@@deriving sexp] | |
13 | ||
14 | let configure = Config.create | |
15 | let verify_certificate = Config.verify_certificate | |
16 | end | |
17 | ||
18 | type oc = Writer.t | |
19 | type ic = Reader.t | |
20 | type 'a io = 'a Deferred.t | |
21 | ||
22 | type addr = | |
23 | [ `OpenSSL of string * Ipaddr_sexp.t * int | |
24 | | `OpenSSL_with_config of string * Ipaddr_sexp.t * int * Ssl.config | |
25 | | `TCP of Ipaddr_sexp.t * int | |
26 | | `Unix_domain_socket of string ] | |
27 | [@@deriving sexp] | |
28 | ||
29 | let connect ?interrupt dst = | |
30 | match dst with | |
31 | | `TCP (ip, port) -> | |
32 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
33 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_host_and_port endp) | |
34 | >>= fun (_, rd, wr) -> return (rd, wr) | |
35 | | `OpenSSL (_, ip, port) -> | |
36 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
37 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_host_and_port endp) | |
38 | >>= fun (_, rd, wr) -> | |
39 | let config = Ssl.configure () in | |
40 | Ssl.connect config rd wr | |
41 | | `OpenSSL_with_config (_, ip, port, config) -> | |
42 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
43 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_host_and_port endp) | |
44 | >>= fun (_, rd, wr) -> Ssl.connect config rd wr | |
45 | | `Unix_domain_socket file -> | |
46 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_file file) | |
47 | >>= fun (_, rd, wr) -> return (rd, wr) | |
48 | ||
49 | let with_connection ?interrupt dst f = | |
50 | match dst with | |
51 | | `TCP (ip, port) -> | |
52 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
53 | Tcp.with_connection ?interrupt | |
54 | (Tcp.Where_to_connect.of_host_and_port endp) (fun _ rd wr -> f rd wr) | |
55 | | `OpenSSL (_, ip, port) -> | |
56 | let config = Ssl.configure () in | |
57 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
58 | Tcp.with_connection ?interrupt | |
59 | (Tcp.Where_to_connect.of_host_and_port endp) (fun _ rd wr -> | |
60 | Ssl.connect config rd wr >>= fun (rd, wr) -> | |
61 | Monitor.protect | |
62 | (fun () -> f rd wr) | |
63 | ~finally:(fun () -> | |
64 | Deferred.all_unit [ Reader.close rd; Writer.close wr ])) | |
65 | | `OpenSSL_with_config (_, ip, port, config) -> | |
66 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
67 | Tcp.with_connection ?interrupt | |
68 | (Tcp.Where_to_connect.of_host_and_port endp) (fun _ rd wr -> | |
69 | Ssl.connect config rd wr >>= fun (rd, wr) -> | |
70 | Monitor.protect | |
71 | (fun () -> f rd wr) | |
72 | ~finally:(fun () -> | |
73 | Deferred.all_unit [ Reader.close rd; Writer.close wr ])) | |
74 | | `Unix_domain_socket file -> | |
75 | Tcp.with_connection ?interrupt (Tcp.Where_to_connect.of_file file) | |
76 | (fun _ rd wr -> f rd wr) | |
77 | ||
78 | type trust_chain = | |
79 | [ `Ca_file of string | |
80 | | `Ca_path of string | |
81 | | `Search_file_first_then_path of [ `File of string ] * [ `Path of string ] | |
82 | ] | |
83 | [@@deriving sexp] | |
84 | ||
85 | type openssl = | |
86 | [ `OpenSSL of [ `Crt_file_path of string ] * [ `Key_file_path of string ] ] | |
87 | [@@deriving sexp] | |
88 | ||
89 | type requires_async_ssl = | |
90 | [ openssl | `OpenSSL_with_trust_chain of openssl * trust_chain ] | |
91 | [@@deriving sexp] | |
92 | ||
93 | type server = [ `TCP | requires_async_ssl ] [@@deriving sexp] | |
94 | ||
95 | let serve ?max_connections ?backlog ?buffer_age_limit ~on_handler_error mode | |
96 | where_to_listen handle_request = | |
97 | let handle_client handle_request sock rd wr = | |
98 | match mode with | |
99 | | `TCP -> handle_request sock rd wr | |
100 | | #requires_async_ssl as async_ssl -> | |
101 | let crt_file, key_file, ca_file, ca_path = | |
102 | match async_ssl with | |
103 | | `OpenSSL (`Crt_file_path crt_file, `Key_file_path key_file) -> | |
104 | (crt_file, key_file, None, None) | |
105 | | `OpenSSL_with_trust_chain | |
106 | (`OpenSSL (`Crt_file_path crt, `Key_file_path key), trust_chain) | |
107 | -> | |
108 | let ca_file, ca_path = | |
109 | match trust_chain with | |
110 | | `Ca_file ca_file -> (Some ca_file, None) | |
111 | | `Ca_path ca_path -> (None, Some ca_path) | |
112 | | `Search_file_first_then_path (`File ca_file, `Path ca_path) | |
113 | -> | |
114 | (Some ca_file, Some ca_path) | |
115 | in | |
116 | (crt, key, ca_file, ca_path) | |
117 | in | |
118 | Ssl.listen ?ca_file ?ca_path ~crt_file ~key_file rd wr | |
119 | >>= fun (rd, wr) -> | |
120 | Monitor.protect | |
121 | (fun () -> handle_request sock rd wr) | |
122 | ~finally:(fun () -> | |
123 | Deferred.all_unit [ Reader.close rd; Writer.close wr ]) | |
124 | in | |
125 | Tcp.Server.create ?max_connections ?backlog ?buffer_age_limit | |
126 | ~on_handler_error where_to_listen | |
127 | (handle_client handle_request) | |
128 | end | |
129 | ||
130 | module Conduit_async_ssl = struct | |
131 | module Ssl_config = Conduit_async.Ssl | |
132 | ||
133 | let ssl_connect = Ssl.connect | |
134 | let ssl_listen = Ssl.listen | |
135 | end |
0 | open Async_ssl | |
1 | ||
2 | include | |
3 | S.V1 | |
4 | with type session = Ssl.Session.t | |
5 | and type ssl_version = Ssl.Version.t | |
6 | and type ssl_conn = Ssl.Connection.t |
0 | include | |
1 | S.V2 | |
2 | with type session = [ `Ssl_not_compiled_in ] | |
3 | and type ssl_version = [ `Ssl_not_compiled_in ] | |
4 | and type ssl_conn = [ `Ssl_not_compiled_in ] | |
5 | and type ssl_opt = [ `Ssl_not_compiled_in ] | |
6 | and type allowed_ciphers = | |
7 | [ `Only of string list | `Openssl_default | `Secure ] |
0 | open Core | |
1 | open Async | |
2 | open Private_ssl.V2 | |
3 | ||
4 | type addr = | |
5 | [ `OpenSSL of Ipaddr_sexp.t * int * Ssl.Config.t | |
6 | | `TCP of Ipaddr_sexp.t * int | |
7 | | `Unix_domain_socket of string ] | |
8 | [@@deriving sexp_of] | |
9 | ||
10 | let connect ?interrupt dst = | |
11 | match dst with | |
12 | | `TCP (ip, port) -> | |
13 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
14 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_host_and_port endp) | |
15 | >>= fun (_, rd, wr) -> return (rd, wr) | |
16 | | `OpenSSL (ip, port, cfg) -> | |
17 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
18 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_host_and_port endp) | |
19 | >>= fun (_, rd, wr) -> Ssl.connect ~cfg rd wr | |
20 | | `Unix_domain_socket file -> | |
21 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_file file) | |
22 | >>= fun (_, rd, wr) -> return (rd, wr) | |
23 | ||
24 | let with_connection ?interrupt dst f = | |
25 | match dst with | |
26 | | `TCP (ip, port) -> | |
27 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
28 | Tcp.with_connection ?interrupt | |
29 | (Tcp.Where_to_connect.of_host_and_port endp) (fun _ rd wr -> f rd wr) | |
30 | | `OpenSSL (ip, port, cfg) -> | |
31 | let endp = Host_and_port.create ~host:(Ipaddr.to_string ip) ~port in | |
32 | Tcp.with_connection ?interrupt | |
33 | (Tcp.Where_to_connect.of_host_and_port endp) (fun _ rd wr -> | |
34 | Ssl.connect ~cfg rd wr >>= fun (rd, wr) -> | |
35 | Monitor.protect | |
36 | (fun () -> f rd wr) | |
37 | ~finally:(fun () -> | |
38 | Deferred.all_unit [ Reader.close rd; Writer.close wr ])) | |
39 | | `Unix_domain_socket file -> | |
40 | Tcp.with_connection ?interrupt (Tcp.Where_to_connect.of_file file) | |
41 | (fun _ rd wr -> f rd wr) | |
42 | ||
43 | type trust_chain = | |
44 | [ `Ca_file of string | |
45 | | `Ca_path of string | |
46 | | `Search_file_first_then_path of [ `File of string ] * [ `Path of string ] | |
47 | ] | |
48 | [@@deriving sexp] | |
49 | ||
50 | type openssl = | |
51 | [ `OpenSSL of [ `Crt_file_path of string ] * [ `Key_file_path of string ] ] | |
52 | [@@deriving sexp] | |
53 | ||
54 | type requires_async_ssl = | |
55 | [ openssl | `OpenSSL_with_trust_chain of openssl * trust_chain ] | |
56 | [@@deriving sexp] | |
57 | ||
58 | type server = [ `TCP | requires_async_ssl ] [@@deriving sexp] | |
59 | ||
60 | let serve ?max_connections ?backlog ?buffer_age_limit ~on_handler_error mode | |
61 | where_to_listen handle_request = | |
62 | let handle_client handle_request sock rd wr = | |
63 | match mode with | |
64 | | `TCP -> handle_request sock rd wr | |
65 | | #requires_async_ssl as async_ssl -> | |
66 | let crt_file, key_file, ca_file, ca_path = | |
67 | match async_ssl with | |
68 | | `OpenSSL (`Crt_file_path crt_file, `Key_file_path key_file) -> | |
69 | (crt_file, key_file, None, None) | |
70 | | `OpenSSL_with_trust_chain | |
71 | (`OpenSSL (`Crt_file_path crt, `Key_file_path key), trust_chain) | |
72 | -> | |
73 | let ca_file, ca_path = | |
74 | match trust_chain with | |
75 | | `Ca_file ca_file -> (Some ca_file, None) | |
76 | | `Ca_path ca_path -> (None, Some ca_path) | |
77 | | `Search_file_first_then_path (`File ca_file, `Path ca_path) -> | |
78 | (Some ca_file, Some ca_path) | |
79 | in | |
80 | (crt, key, ca_file, ca_path) | |
81 | in | |
82 | let cfg = Ssl.Config.create ?ca_file ?ca_path ~crt_file ~key_file () in | |
83 | Ssl.listen cfg rd wr >>= fun (rd, wr) -> | |
84 | Monitor.protect | |
85 | (fun () -> handle_request sock rd wr) | |
86 | ~finally:(fun () -> | |
87 | Deferred.all_unit [ Reader.close rd; Writer.close wr ]) | |
88 | in | |
89 | Tcp.Server.create ?max_connections ?backlog ?buffer_age_limit | |
90 | ~on_handler_error where_to_listen | |
91 | (handle_client handle_request) | |
92 | ||
93 | type ssl_version = Ssl.version [@@deriving sexp] | |
94 | type ssl_opt = Ssl.opt [@@deriving sexp] | |
95 | type ssl_conn = Ssl.connection [@@deriving sexp_of] | |
96 | ||
97 | type allowed_ciphers = [ `Only of string list | `Openssl_default | `Secure ] | |
98 | [@@deriving sexp] | |
99 | ||
100 | type verify_mode = Ssl.verify_mode [@@deriving sexp_of] | |
101 | type session = Ssl.session [@@deriving sexp_of] | |
102 | ||
103 | module Ssl = struct | |
104 | module Config = Ssl.Config | |
105 | end |
0 | open Async_ssl | |
1 | ||
2 | include | |
3 | S.V2 | |
4 | with type session = Ssl.Session.t | |
5 | and type ssl_version = Ssl.Version.t | |
6 | and type ssl_conn = Ssl.Connection.t | |
7 | and type ssl_opt = Ssl.Opt.t | |
8 | and type verify_mode = Ssl.Verify_mode.t | |
9 | and type allowed_ciphers = | |
10 | [ `Only of string list | `Openssl_default | `Secure ] |
0 | include | |
1 | S.V3 | |
2 | with type session = [ `Ssl_not_compiled_in ] | |
3 | and type ssl_version = [ `Ssl_not_compiled_in ] | |
4 | and type ssl_conn = [ `Ssl_not_compiled_in ] | |
5 | and type ssl_opt = [ `Ssl_not_compiled_in ] | |
6 | and type allowed_ciphers = | |
7 | [ `Only of string list | `Openssl_default | `Secure ] |
0 | open Core | |
1 | open Async | |
2 | open Private_ssl.V2 | |
3 | ||
4 | type _ addr = | |
5 | | OpenSSL : Socket.Address.Inet.t * Ssl.Config.t -> Socket.Address.Inet.t addr | |
6 | | Inet : Socket.Address.Inet.t -> Socket.Address.Inet.t addr | |
7 | | Unix : Socket.Address.Unix.t -> Socket.Address.Unix.t addr | |
8 | [@@deriving sexp_of] | |
9 | ||
10 | type _ tcp_sock = | |
11 | | Inet_sock : | |
12 | ([ `Active ], Socket.Address.Inet.t) Socket.t | |
13 | -> Socket.Address.Inet.t tcp_sock | |
14 | | Unix_sock : | |
15 | ([ `Active ], Socket.Address.Unix.t) Socket.t | |
16 | -> Socket.Address.Unix.t tcp_sock | |
17 | ||
18 | let ssl_schemes = [ "https"; "wss" ] | |
19 | let mem_scheme s = List.mem ssl_schemes ~equal:String.equal s | |
20 | ||
21 | let resolve_uri ?(options = []) uri = | |
22 | let host = | |
23 | Option.value_exn ~here:[%here] ~message:"no host in URL" (Uri.host uri) | |
24 | in | |
25 | let service = | |
26 | match (Uri.port uri, Uri_services.tcp_port_of_uri uri) with | |
27 | | Some p, _ -> Some (string_of_int p) | |
28 | | None, Some p -> Some (string_of_int p) | |
29 | | _ -> None | |
30 | in | |
31 | (* Async_extra does not yet support IPv6 *) | |
32 | let options = Unix.Addr_info.AI_FAMILY PF_INET :: options in | |
33 | Unix.Addr_info.get ~host ?service options >>= function | |
34 | | [] -> failwithf "unable to resolve %s" (Uri.to_string uri) () | |
35 | | { ai_addr; _ } :: _ -> ( | |
36 | match (Uri.scheme uri, ai_addr) with | |
37 | | _, ADDR_UNIX _ -> invalid_arg "uri must resolve to inet address" | |
38 | | Some s, ADDR_INET (h, p) when mem_scheme s -> | |
39 | return (OpenSSL (`Inet (h, p), Ssl.Config.create ())) | |
40 | | _, ADDR_INET (h, p) -> return (Inet (`Inet (h, p)))) | |
41 | ||
42 | let connect (type a) ?interrupt (addr : a addr) : | |
43 | (a tcp_sock * Reader.t * Writer.t) Deferred.t = | |
44 | match addr with | |
45 | | Inet addr -> | |
46 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_inet_address addr) | |
47 | >>| fun (s, r, w) -> (Inet_sock s, r, w) | |
48 | | OpenSSL (addr, cfg) -> | |
49 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_inet_address addr) | |
50 | >>= fun (s, rd, wr) -> | |
51 | Ssl.connect ~cfg rd wr >>| fun (rd, wr) -> (Inet_sock s, rd, wr) | |
52 | | Unix addr -> | |
53 | Tcp.connect ?interrupt (Tcp.Where_to_connect.of_unix_address addr) | |
54 | >>| fun (s, r, w) -> (Unix_sock s, r, w) | |
55 | ||
56 | let with_connection (type a) ?interrupt (addr : a addr) | |
57 | (f : a tcp_sock -> Reader.t -> Writer.t -> 'a Deferred.t) = | |
58 | match addr with | |
59 | | Inet addr -> | |
60 | Tcp.with_connection ?interrupt (Tcp.Where_to_connect.of_inet_address addr) | |
61 | (fun s rd wr -> f (Inet_sock s) rd wr) | |
62 | | OpenSSL (addr, cfg) -> | |
63 | Tcp.with_connection ?interrupt (Tcp.Where_to_connect.of_inet_address addr) | |
64 | (fun s rd wr -> | |
65 | Ssl.connect ~cfg rd wr >>= fun (rd, wr) -> | |
66 | Monitor.protect | |
67 | (fun () -> f (Inet_sock s) rd wr) | |
68 | ~finally:(fun () -> | |
69 | Deferred.all_unit [ Reader.close rd; Writer.close wr ])) | |
70 | | Unix addr -> | |
71 | Tcp.with_connection ?interrupt (Tcp.Where_to_connect.of_unix_address addr) | |
72 | (fun s rd wr -> f (Unix_sock s) rd wr) | |
73 | ||
74 | let connect_uri ?options ?interrupt uri = | |
75 | resolve_uri ?options uri >>= fun addr -> connect ?interrupt addr | |
76 | ||
77 | let with_connection_uri ?options ?interrupt uri f = | |
78 | resolve_uri ?options uri >>= fun addr -> with_connection ?interrupt addr f | |
79 | ||
80 | type trust_chain = | |
81 | [ `Ca_file of string | |
82 | | `Ca_path of string | |
83 | | `Search_file_first_then_path of [ `File of string ] * [ `Path of string ] | |
84 | ] | |
85 | [@@deriving sexp] | |
86 | ||
87 | type openssl = | |
88 | [ `OpenSSL of [ `Crt_file_path of string ] * [ `Key_file_path of string ] ] | |
89 | [@@deriving sexp] | |
90 | ||
91 | type requires_async_ssl = | |
92 | [ openssl | `OpenSSL_with_trust_chain of openssl * trust_chain ] | |
93 | [@@deriving sexp] | |
94 | ||
95 | type server = [ `TCP | requires_async_ssl ] [@@deriving sexp] | |
96 | ||
97 | let serve ?max_connections ?backlog ?buffer_age_limit ~on_handler_error mode | |
98 | where_to_listen handle_request = | |
99 | let handle_client handle_request sock rd wr = | |
100 | match mode with | |
101 | | `TCP -> handle_request sock rd wr | |
102 | | #requires_async_ssl as async_ssl -> | |
103 | let crt_file, key_file, ca_file, ca_path = | |
104 | match async_ssl with | |
105 | | `OpenSSL (`Crt_file_path crt_file, `Key_file_path key_file) -> | |
106 | (crt_file, key_file, None, None) | |
107 | | `OpenSSL_with_trust_chain | |
108 | (`OpenSSL (`Crt_file_path crt, `Key_file_path key), trust_chain) | |
109 | -> | |
110 | let ca_file, ca_path = | |
111 | match trust_chain with | |
112 | | `Ca_file ca_file -> (Some ca_file, None) | |
113 | | `Ca_path ca_path -> (None, Some ca_path) | |
114 | | `Search_file_first_then_path (`File ca_file, `Path ca_path) -> | |
115 | (Some ca_file, Some ca_path) | |
116 | in | |
117 | (crt, key, ca_file, ca_path) | |
118 | in | |
119 | let cfg = Ssl.Config.create ?ca_file ?ca_path ~crt_file ~key_file () in | |
120 | Ssl.listen cfg rd wr >>= fun (rd, wr) -> | |
121 | Monitor.protect | |
122 | (fun () -> handle_request sock rd wr) | |
123 | ~finally:(fun () -> | |
124 | Deferred.all_unit [ Reader.close rd; Writer.close wr ]) | |
125 | in | |
126 | Tcp.Server.create ?max_connections ?backlog ?buffer_age_limit | |
127 | ~on_handler_error where_to_listen | |
128 | (handle_client handle_request) | |
129 | ||
130 | type ssl_version = Ssl.version [@@deriving sexp] | |
131 | type ssl_opt = Ssl.opt [@@deriving sexp] | |
132 | type ssl_conn = Ssl.connection [@@deriving sexp_of] | |
133 | ||
134 | type allowed_ciphers = [ `Only of string list | `Openssl_default | `Secure ] | |
135 | [@@deriving sexp] | |
136 | ||
137 | type verify_mode = Ssl.verify_mode [@@deriving sexp_of] | |
138 | type session = Ssl.session [@@deriving sexp_of] | |
139 | ||
140 | module Ssl = struct | |
141 | module Config = Ssl.Config | |
142 | end |
0 | open Async_ssl | |
1 | ||
2 | include | |
3 | S.V3 | |
4 | with type session = Ssl.Session.t | |
5 | and type ssl_version = Ssl.Version.t | |
6 | and type ssl_conn = Ssl.Connection.t | |
7 | and type ssl_opt = Ssl.Opt.t | |
8 | and type verify_mode = Ssl.Verify_mode.t | |
9 | and type allowed_ciphers = | |
10 | [ `Only of string list | `Openssl_default | `Secure ] |
0 | (library | |
1 | (name conduit_lwt) | |
2 | (public_name conduit-lwt) | |
3 | (preprocess | |
4 | (pps ppx_sexp_conv)) | |
5 | (wrapped false) | |
6 | (modules resolver_lwt) | |
7 | (libraries conduit lwt)) |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | module IO = struct | |
18 | type 'a t = 'a Lwt.t | |
19 | ||
20 | let ( >>= ) = Lwt.bind | |
21 | let return = Lwt.return | |
22 | end | |
23 | ||
24 | module type S = | |
25 | Resolver.S with type svc = Resolver.service and type 'a io = 'a Lwt.t | |
26 | ||
27 | include Resolver.Make (IO) |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** Resolve URIs to endpoints using the {{:http://ocsigen.org/lwt} Lwt} library *) | |
18 | ||
19 | module IO : Conduit.IO with type 'a t = 'a Lwt.t | |
20 | (** IO module compatible with {!Conduit.IO} that uses Lwt *) | |
21 | ||
22 | (** Module type that specialises {!Conduit.RESOLVER} to use Lwt threads *) | |
23 | module type S = | |
24 | Resolver.S with type svc = Resolver.service and type 'a io = 'a Lwt.t | |
25 | ||
26 | include S |
0 | (* | |
1 | * Copyright (c) 2015-2017 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | let activate _fn _name = Lwt.fail_with "No Launchd support" |
0 | (* | |
1 | * Copyright (c) 2015-2017 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Lwt.Infix | |
18 | ||
19 | let activate fn name = | |
20 | Lwt_launchd.activate_socket name >>= fun sockets -> | |
21 | match Launchd.error_to_msg sockets with | |
22 | | Ok sockets -> Lwt_list.iter_p fn sockets | |
23 | | Error (`Msg m) -> Lwt.fail_with m |
0 | open Lwt.Infix | |
1 | ||
2 | let src = Logs.Src.create "conduit_lwt_server" ~doc:"Conduit Lwt transport" | |
3 | ||
4 | module Log = (val Logs.src_log src : Logs.LOG) | |
5 | ||
6 | let safe_close t = | |
7 | Lwt.catch (fun () -> Lwt_io.close t) (fun _ -> Lwt.return_unit) | |
8 | ||
9 | let close (ic, oc) = safe_close oc >>= fun () -> safe_close ic | |
10 | ||
11 | let with_socket sockaddr f = | |
12 | let fd = | |
13 | Lwt_unix.socket (Unix.domain_of_sockaddr sockaddr) Unix.SOCK_STREAM 0 | |
14 | in | |
15 | Lwt.catch | |
16 | (fun () -> f fd) | |
17 | (fun e -> | |
18 | Lwt.catch (fun () -> Lwt_unix.close fd) (fun _ -> Lwt.return_unit) | |
19 | >>= fun () -> Lwt.fail e) | |
20 | ||
21 | let listen ?(backlog = 128) sa = | |
22 | with_socket sa (fun fd -> | |
23 | Lwt_unix.(setsockopt fd SO_REUSEADDR true); | |
24 | Lwt_unix.bind fd sa >|= fun () -> | |
25 | Lwt_unix.listen fd backlog; | |
26 | Lwt_unix.set_close_on_exec fd; | |
27 | fd) | |
28 | ||
29 | let process_accept ?timeout callback (sa, ic, oc) = | |
30 | let c = callback sa ic oc in | |
31 | let events = | |
32 | match timeout with | |
33 | | None -> [ c ] | |
34 | | Some t -> [ c; Lwt_unix.sleep (float_of_int t) ] | |
35 | in | |
36 | Lwt.finalize (fun () -> Lwt.pick events) (fun () -> close (ic, oc)) | |
37 | ||
38 | (* File descriptors are a global resource so this has to be a global limit too *) | |
39 | let maxactive = ref None | |
40 | let active = ref 0 | |
41 | let cond = Lwt_condition.create () | |
42 | let connected () = incr active | |
43 | ||
44 | let disconnected () = | |
45 | decr active; | |
46 | Lwt_condition.broadcast cond () | |
47 | ||
48 | let rec throttle () = | |
49 | match !maxactive with | |
50 | | Some limit when !active > limit -> Lwt_condition.wait cond >>= throttle | |
51 | | _ -> Lwt.return_unit | |
52 | ||
53 | let set_max_active max_active = | |
54 | maxactive := Some max_active; | |
55 | Lwt_condition.broadcast cond () | |
56 | ||
57 | let run_handler handler v = | |
58 | Lwt.async (fun () -> | |
59 | Lwt.try_bind | |
60 | (fun () -> handler v) | |
61 | (fun () -> | |
62 | disconnected (); | |
63 | Lwt.return_unit) | |
64 | (fun x -> | |
65 | disconnected (); | |
66 | (match x with | |
67 | | Lwt.Canceled -> () | |
68 | | ex -> | |
69 | Log.warn (fun f -> | |
70 | f "Uncaught exception in handler: %s" (Printexc.to_string ex))); | |
71 | Lwt.return_unit)) | |
72 | ||
73 | let init ?(stop = fst (Lwt.wait ())) handler fd = | |
74 | let stop = Lwt.map (fun () -> `Stop) stop in | |
75 | let rec loop () = | |
76 | Lwt.try_bind | |
77 | (fun () -> | |
78 | connected (); | |
79 | throttle () >>= fun () -> | |
80 | let accept = Lwt.map (fun v -> `Accept v) (Lwt_unix.accept fd) in | |
81 | Lwt.choose [ accept; stop ] >|= function | |
82 | | `Stop -> | |
83 | Lwt.cancel accept; | |
84 | `Stop | |
85 | | `Accept _ as x -> x) | |
86 | (function | |
87 | | `Stop -> | |
88 | disconnected (); | |
89 | Lwt.return_unit | |
90 | | `Accept v -> | |
91 | run_handler handler v; | |
92 | loop ()) | |
93 | (fun exn -> | |
94 | disconnected (); | |
95 | match exn with | |
96 | | Lwt.Canceled -> Lwt.return_unit | |
97 | | ex -> | |
98 | Log.warn (fun f -> | |
99 | f "Uncaught exception accepting connection: %s" | |
100 | (Printexc.to_string ex)); | |
101 | Lwt_unix.yield () >>= loop) | |
102 | in | |
103 | Lwt.finalize loop (fun () -> Lwt_unix.close fd) |
0 | val close : 'a Lwt_io.channel * 'b Lwt_io.channel -> unit Lwt.t | |
1 | val set_max_active : int -> unit | |
2 | val listen : ?backlog:int -> Unix.sockaddr -> Lwt_unix.file_descr Lwt.t | |
3 | val with_socket : Unix.sockaddr -> (Lwt_unix.file_descr -> 'a Lwt.t) -> 'a Lwt.t | |
4 | ||
5 | val process_accept : | |
6 | ?timeout:int -> | |
7 | ('a -> 'b Lwt_io.channel -> 'c Lwt_io.channel -> unit Lwt.t) -> | |
8 | 'a * 'b Lwt_io.channel * 'c Lwt_io.channel -> | |
9 | unit Lwt.t | |
10 | ||
11 | val init : | |
12 | ?stop:unit Lwt.t -> | |
13 | (Lwt_unix.file_descr * Lwt_unix.sockaddr -> unit Lwt.t) -> | |
14 | Lwt_unix.file_descr -> | |
15 | unit Lwt.t |
0 | module X509 = struct | |
1 | let private_of_pems ~cert:_ ~priv_key:_ = Lwt.fail_with "Tls not available" | |
2 | ||
3 | type authenticator = unit | |
4 | ||
5 | let default_authenticator = () | |
6 | end | |
7 | ||
8 | module Client = struct | |
9 | let connect ?src:_ ?certificates:_ ~authenticator:_ _host _sa = | |
10 | Lwt.fail_with "Tls not available" | |
11 | end | |
12 | ||
13 | module Server = struct | |
14 | let init' ?backlog:_ ?stop:_ ?timeout:_ _tls _sa _callback = | |
15 | Lwt.fail_with "Tls not available" | |
16 | ||
17 | let init ?backlog:_ ~certfile:_ ~keyfile:_ ?stop:_ ?timeout:_ _sa _callback = | |
18 | Lwt.fail_with "Tls not available" | |
19 | end | |
20 | ||
21 | let available = false |
0 | (* | |
1 | * Copyright (c) 2014 Hannes Mehnert <hannes@mehnert.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** TLS/SSL connections via OCaml-TLS *) | |
18 | ||
19 | module X509 : sig | |
20 | val private_of_pems : cert:string -> priv_key:string -> 'a Lwt.t | |
21 | ||
22 | type authenticator = unit | |
23 | ||
24 | val default_authenticator : authenticator | |
25 | end | |
26 | ||
27 | module Client : sig | |
28 | val connect : | |
29 | ?src:Lwt_unix.sockaddr -> | |
30 | ?certificates:'a -> | |
31 | authenticator:X509.authenticator -> | |
32 | [ `host ] Domain_name.t -> | |
33 | Lwt_unix.sockaddr -> | |
34 | (Lwt_unix.file_descr * Lwt_io.input_channel * Lwt_io.output_channel) Lwt.t | |
35 | end | |
36 | ||
37 | module Server : sig | |
38 | val init : | |
39 | ?backlog:int -> | |
40 | certfile:string -> | |
41 | keyfile:string -> | |
42 | ?stop:unit Lwt.t -> | |
43 | ?timeout:int -> | |
44 | Lwt_unix.sockaddr -> | |
45 | (Lwt_unix.sockaddr -> | |
46 | Lwt_unix.file_descr -> | |
47 | Lwt_io.input_channel -> | |
48 | Lwt_io.output_channel -> | |
49 | unit Lwt.t) -> | |
50 | unit Lwt.t | |
51 | ||
52 | val init' : | |
53 | ?backlog:int -> | |
54 | ?stop:unit Lwt.t -> | |
55 | ?timeout:int -> | |
56 | 'config -> | |
57 | Lwt_unix.sockaddr -> | |
58 | (Lwt_unix.sockaddr -> | |
59 | Lwt_unix.file_descr -> | |
60 | Lwt_io.input_channel -> | |
61 | Lwt_io.output_channel -> | |
62 | unit Lwt.t) -> | |
63 | unit Lwt.t | |
64 | end | |
65 | ||
66 | (**/**) | |
67 | ||
68 | val available : bool |
0 | (* | |
1 | * Copyright (c) 2014 Hannes Mehnert <hannes@mehnert.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Lwt.Infix | |
18 | ||
19 | module X509 = struct | |
20 | let private_of_pems ~cert ~priv_key = X509_lwt.private_of_pems ~cert ~priv_key | |
21 | ||
22 | type authenticator = X509.Authenticator.t | |
23 | ||
24 | let default_authenticator = | |
25 | match Ca_certs.authenticator () with | |
26 | | Ok a -> a | |
27 | | Error (`Msg msg) -> failwith msg | |
28 | end | |
29 | ||
30 | module Client = struct | |
31 | let connect ?src ?certificates ~authenticator host sa = | |
32 | Conduit_lwt_server.with_socket sa (fun fd -> | |
33 | (match src with | |
34 | | None -> Lwt.return_unit | |
35 | | Some src_sa -> Lwt_unix.bind fd src_sa) | |
36 | >>= fun () -> | |
37 | let config = Tls.Config.client ~authenticator ?certificates () in | |
38 | Lwt_unix.connect fd sa >>= fun () -> | |
39 | Tls_lwt.Unix.client_of_fd config ~host fd >|= fun t -> | |
40 | let ic, oc = Tls_lwt.of_t t in | |
41 | (fd, ic, oc)) | |
42 | end | |
43 | ||
44 | module Server = struct | |
45 | let init' ?backlog ?stop ?timeout tls sa callback = | |
46 | sa | |
47 | |> Conduit_lwt_server.listen ?backlog | |
48 | >>= Conduit_lwt_server.init ?stop (fun (fd, addr) -> | |
49 | Lwt.try_bind | |
50 | (fun () -> Tls_lwt.Unix.server_of_fd tls fd) | |
51 | (fun t -> | |
52 | let ic, oc = Tls_lwt.of_t t in | |
53 | Lwt.return (fd, ic, oc)) | |
54 | (fun exn -> Lwt_unix.close fd >>= fun () -> Lwt.fail exn) | |
55 | >>= Conduit_lwt_server.process_accept ?timeout (callback addr)) | |
56 | ||
57 | let init ?backlog ~certfile ~keyfile ?stop ?timeout sa callback = | |
58 | X509_lwt.private_of_pems ~cert:certfile ~priv_key:keyfile | |
59 | >>= fun certificate -> | |
60 | let config = Tls.Config.server ~certificates:(`Single certificate) () in | |
61 | init' ?backlog ?stop ?timeout config sa callback | |
62 | end | |
63 | ||
64 | let available = true |
0 | (* | |
1 | * Copyright (c) 2014 Hannes Mehnert <hannes@mehnert.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** TLS/SSL connections via OCaml-TLS *) | |
18 | ||
19 | module X509 : sig | |
20 | val private_of_pems : | |
21 | cert:string -> priv_key:string -> Tls.Config.certchain Lwt.t | |
22 | ||
23 | type authenticator = X509.Authenticator.t | |
24 | ||
25 | val default_authenticator : authenticator | |
26 | end | |
27 | ||
28 | module Client : sig | |
29 | val connect : | |
30 | ?src:Lwt_unix.sockaddr -> | |
31 | ?certificates:Tls.Config.own_cert -> | |
32 | authenticator:X509.authenticator -> | |
33 | [ `host ] Domain_name.t -> | |
34 | Lwt_unix.sockaddr -> | |
35 | (Lwt_unix.file_descr * Lwt_io.input_channel * Lwt_io.output_channel) Lwt.t | |
36 | end | |
37 | ||
38 | module Server : sig | |
39 | val init : | |
40 | ?backlog:int -> | |
41 | certfile:string -> | |
42 | keyfile:string -> | |
43 | ?stop:unit Lwt.t -> | |
44 | ?timeout:int -> | |
45 | Lwt_unix.sockaddr -> | |
46 | (Lwt_unix.sockaddr -> | |
47 | Lwt_unix.file_descr -> | |
48 | Lwt_io.input_channel -> | |
49 | Lwt_io.output_channel -> | |
50 | unit Lwt.t) -> | |
51 | unit Lwt.t | |
52 | ||
53 | val init' : | |
54 | ?backlog:int -> | |
55 | ?stop:unit Lwt.t -> | |
56 | ?timeout:int -> | |
57 | Tls.Config.server -> | |
58 | Lwt_unix.sockaddr -> | |
59 | (Lwt_unix.sockaddr -> | |
60 | Lwt_unix.file_descr -> | |
61 | Lwt_io.input_channel -> | |
62 | Lwt_io.output_channel -> | |
63 | unit Lwt.t) -> | |
64 | unit Lwt.t | |
65 | end | |
66 | ||
67 | (**/**) | |
68 | ||
69 | val available : bool |
0 | (* | |
1 | * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * Copyright (c) 2014 Hannes Mehnert <hannes@mehnert.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | * | |
16 | *) | |
17 | ||
18 | open Lwt.Infix | |
19 | open Sexplib.Conv | |
20 | ||
21 | let debug = ref false | |
22 | let debug_print = ref Printf.eprintf | |
23 | ||
24 | let () = | |
25 | try | |
26 | ignore (Sys.getenv "CONDUIT_DEBUG"); | |
27 | debug := true | |
28 | with Not_found -> () | |
29 | ||
30 | type tls_lib = OpenSSL | Native | No_tls [@@deriving sexp] | |
31 | ||
32 | let default_tls_library = | |
33 | (* TODO build time selection *) | |
34 | let default = | |
35 | if Conduit_lwt_tls.available then Native | |
36 | else if Conduit_lwt_unix_ssl.available then OpenSSL | |
37 | else No_tls | |
38 | in | |
39 | match String.lowercase_ascii (Sys.getenv "CONDUIT_TLS") with | |
40 | | "native" -> Native | |
41 | | "openssl" | "libressl" -> OpenSSL | |
42 | | "none" | "notls" -> No_tls | |
43 | | _ -> default | |
44 | | exception Not_found -> default | |
45 | ||
46 | let tls_library = ref default_tls_library | |
47 | ||
48 | let () = | |
49 | if !debug then | |
50 | !debug_print "Selected TLS library: %s\n" | |
51 | (Sexplib.Sexp.to_string (sexp_of_tls_lib !tls_library)) | |
52 | ||
53 | type +'a io = 'a Lwt.t | |
54 | type ic = Lwt_io.input_channel | |
55 | type oc = Lwt_io.output_channel | |
56 | ||
57 | type client_tls_config = | |
58 | [ `Hostname of string ] * [ `IP of Ipaddr_sexp.t ] * [ `Port of int ] | |
59 | [@@deriving sexp] | |
60 | ||
61 | type client = | |
62 | [ `TLS of client_tls_config | |
63 | | `TLS_native of client_tls_config | |
64 | | `OpenSSL of client_tls_config | |
65 | | `TCP of [ `IP of Ipaddr_sexp.t ] * [ `Port of int ] | |
66 | | `Unix_domain_socket of [ `File of string ] | |
67 | | `Vchan_direct of [ `Domid of int ] * [ `Port of string ] | |
68 | | `Vchan_domain_socket of [ `Domain_name of string ] * [ `Port of string ] ] | |
69 | [@@deriving sexp] | |
70 | ||
71 | type server_tls_config = | |
72 | [ `Crt_file_path of string ] | |
73 | * [ `Key_file_path of string ] | |
74 | * [ `Password of bool -> string | `No_password ] | |
75 | * [ `Port of int ] | |
76 | [@@deriving sexp] | |
77 | (** Configuration fragment for a listening TLS server *) | |
78 | ||
79 | type tcp_config = | |
80 | [ `Port of int | `Socket of (Lwt_unix.file_descr[@sexp.opaque]) ] | |
81 | [@@deriving sexp] | |
82 | (** Set of ways to create TCP servers *) | |
83 | ||
84 | type server = | |
85 | [ `TLS of server_tls_config | |
86 | | `OpenSSL of server_tls_config | |
87 | | `TLS_native of server_tls_config | |
88 | | `TCP of tcp_config | |
89 | | `Unix_domain_socket of [ `File of string ] | |
90 | | `Vchan_direct of int * string | |
91 | | `Vchan_domain_socket of string * string | |
92 | | `Launchd of string ] | |
93 | [@@deriving sexp] | |
94 | (** Set of supported listening mechanisms that are supported by this module. *) | |
95 | ||
96 | type tls_own_key = | |
97 | [ `None | |
98 | | `TLS of | |
99 | [ `Crt_file_path of string ] | |
100 | * [ `Key_file_path of string ] | |
101 | * [ `Password of bool -> string | `No_password ] ] | |
102 | [@@deriving sexp] | |
103 | ||
104 | type tls_server_key = tls_own_key [@@deriving sexp] | |
105 | ||
106 | type ctx = { | |
107 | src : Unix.sockaddr option; | |
108 | tls_own_key : tls_own_key; | |
109 | tls_authenticator : Conduit_lwt_tls.X509.authenticator; | |
110 | } | |
111 | ||
112 | let string_of_unix_sockaddr sa = | |
113 | let open Unix in | |
114 | match sa with | |
115 | | ADDR_UNIX s -> Printf.sprintf "ADDR_UNIX(%s)" s | |
116 | | ADDR_INET (ia, port) -> | |
117 | Printf.sprintf "ADDR_INET(%s,%d)" (string_of_inet_addr ia) port | |
118 | ||
119 | let sexp_of_ctx ctx = | |
120 | [%sexp_of: string option * tls_own_key] | |
121 | ( (match ctx.src with | |
122 | | None -> None | |
123 | | Some sa -> Some (string_of_unix_sockaddr sa)), | |
124 | ctx.tls_own_key ) | |
125 | ||
126 | type tcp_flow = { | |
127 | fd : (Lwt_unix.file_descr[@sexp.opaque]); | |
128 | ip : Ipaddr_sexp.t; | |
129 | port : int; | |
130 | } | |
131 | [@@deriving sexp] | |
132 | ||
133 | type domain_flow = { fd : (Lwt_unix.file_descr[@sexp.opaque]); path : string } | |
134 | [@@deriving sexp] | |
135 | ||
136 | type vchan_flow = { domid : int; port : string } [@@deriving sexp] | |
137 | ||
138 | type flow = | |
139 | | TCP of tcp_flow | |
140 | | Domain_socket of domain_flow | |
141 | | Vchan of vchan_flow | |
142 | [@@deriving sexp] | |
143 | ||
144 | let flow_of_fd fd sa = | |
145 | match sa with | |
146 | | Unix.ADDR_UNIX path -> Domain_socket { fd; path } | |
147 | | Unix.ADDR_INET (ip, port) -> | |
148 | TCP { fd; ip = Ipaddr_unix.of_inet_addr ip; port } | |
149 | ||
150 | let default_ctx = | |
151 | { | |
152 | src = None; | |
153 | tls_own_key = `None; | |
154 | tls_authenticator = Conduit_lwt_tls.X509.default_authenticator; | |
155 | } | |
156 | ||
157 | let init ?src ?(tls_own_key = `None) | |
158 | ?(tls_authenticator = Conduit_lwt_tls.X509.default_authenticator) () = | |
159 | match src with | |
160 | | None -> Lwt.return { src = None; tls_own_key; tls_authenticator } | |
161 | | Some host -> ( | |
162 | let open Unix in | |
163 | Lwt_unix.getaddrinfo host "0" [ AI_PASSIVE; AI_SOCKTYPE SOCK_STREAM ] | |
164 | >>= function | |
165 | | { ai_addr; _ } :: _ -> | |
166 | Lwt.return { src = Some ai_addr; tls_own_key; tls_authenticator } | |
167 | | [] -> Lwt.fail_with "Invalid conduit source address specified") | |
168 | ||
169 | module Sockaddr_io = struct | |
170 | let shutdown_no_exn fd mode = | |
171 | try Lwt_unix.shutdown fd mode | |
172 | with Unix.Unix_error (Unix.ENOTCONN, _, _) -> () | |
173 | ||
174 | let make_fd_state () = ref `Open | |
175 | ||
176 | let make fd = | |
177 | let fd_state = make_fd_state () in | |
178 | let close_in () = | |
179 | match !fd_state with | |
180 | | `Open -> | |
181 | fd_state := `In_closed; | |
182 | shutdown_no_exn fd Unix.SHUTDOWN_RECEIVE; | |
183 | Lwt.return_unit | |
184 | | `Out_closed -> | |
185 | fd_state := `Closed; | |
186 | Lwt_unix.close fd | |
187 | | `In_closed (* repeating on a closed channel is a noop in Lwt_io *) | |
188 | | `Closed -> | |
189 | Lwt.return_unit | |
190 | in | |
191 | let close_out () = | |
192 | match !fd_state with | |
193 | | `Open -> | |
194 | fd_state := `Out_closed; | |
195 | shutdown_no_exn fd Unix.SHUTDOWN_SEND; | |
196 | Lwt.return_unit | |
197 | | `In_closed -> | |
198 | fd_state := `Closed; | |
199 | Lwt_unix.close fd | |
200 | | `Out_closed (* repeating on a closed channel is a noop in Lwt_io *) | |
201 | | `Closed -> | |
202 | Lwt.return_unit | |
203 | in | |
204 | let ic = Lwt_io.of_fd ~close:close_in ~mode:Lwt_io.input fd in | |
205 | let oc = Lwt_io.of_fd ~close:close_out ~mode:Lwt_io.output fd in | |
206 | (ic, oc) | |
207 | end | |
208 | ||
209 | (* Vanilla sockaddr connection *) | |
210 | module Sockaddr_client = struct | |
211 | let connect ?src sa = | |
212 | Conduit_lwt_server.with_socket sa (fun fd -> | |
213 | (match src with | |
214 | | None -> Lwt.return_unit | |
215 | | Some src_sa -> Lwt_unix.bind fd src_sa) | |
216 | >>= fun () -> | |
217 | Lwt_unix.connect fd sa >>= fun () -> | |
218 | let ic, oc = Sockaddr_io.make fd in | |
219 | Lwt.return (fd, ic, oc)) | |
220 | end | |
221 | ||
222 | module Sockaddr_server = struct | |
223 | let set_sockopts_no_exn fd = | |
224 | try Lwt_unix.setsockopt fd Lwt_unix.TCP_NODELAY true | |
225 | with | |
226 | (* This is expected for Unix domain sockets *) | |
227 | | Unix.Unix_error (Unix.EOPNOTSUPP, _, _) -> | |
228 | () | |
229 | ||
230 | let process_accept ?timeout callback (client, peeraddr) = | |
231 | set_sockopts_no_exn client; | |
232 | let ic, oc = Sockaddr_io.make client in | |
233 | let c = callback (flow_of_fd client peeraddr) ic oc in | |
234 | let events = | |
235 | match timeout with | |
236 | | None -> [ c ] | |
237 | | Some t -> [ c; Lwt_unix.sleep (float_of_int t) ] | |
238 | in | |
239 | Lwt.finalize | |
240 | (fun () -> Lwt.pick events) | |
241 | (fun () -> Conduit_lwt_server.close (ic, oc)) | |
242 | ||
243 | let init ~on ?stop ?backlog ?timeout callback = | |
244 | (match on with | |
245 | | `Socket s -> Lwt.return s | |
246 | | `Sockaddr sockaddr -> Conduit_lwt_server.listen ?backlog sockaddr) | |
247 | >>= Conduit_lwt_server.init ?stop (process_accept ?timeout callback) | |
248 | end | |
249 | ||
250 | let set_max_active maxactive = Conduit_lwt_server.set_max_active maxactive | |
251 | ||
252 | (** TLS client connection functions *) | |
253 | ||
254 | let connect_with_tls_native ~ctx (`Hostname hostname, `IP ip, `Port port) = | |
255 | let sa = Unix.ADDR_INET (Ipaddr_unix.to_inet_addr ip, port) in | |
256 | (match ctx.tls_own_key with | |
257 | | `None -> Lwt.return_none | |
258 | | `TLS (_, _, `Password _) -> | |
259 | Lwt.fail_with "OCaml-TLS cannot handle encrypted pem files" | |
260 | | `TLS (`Crt_file_path cert, `Key_file_path priv_key, `No_password) -> | |
261 | Conduit_lwt_tls.X509.private_of_pems ~cert ~priv_key | |
262 | >|= fun certificate -> Some (`Single certificate)) | |
263 | >>= fun certificates -> | |
264 | let hostname = | |
265 | try Domain_name.(host_exn (of_string_exn hostname)) | |
266 | with Invalid_argument msg -> | |
267 | let s = | |
268 | Printf.sprintf "couldn't convert %s to a [`host] Domain_name.t: %s" | |
269 | hostname msg | |
270 | in | |
271 | invalid_arg s | |
272 | in | |
273 | Conduit_lwt_tls.Client.connect ?src:ctx.src ?certificates | |
274 | ~authenticator:ctx.tls_authenticator hostname sa | |
275 | >|= fun (fd, ic, oc) -> | |
276 | let flow = TCP { fd; ip; port } in | |
277 | (flow, ic, oc) | |
278 | ||
279 | let connect_with_openssl ~ctx (`Hostname hostname, `IP ip, `Port port) = | |
280 | let sa = Unix.ADDR_INET (Ipaddr_unix.to_inet_addr ip, port) in | |
281 | let ctx_ssl = | |
282 | match ctx.tls_own_key with | |
283 | | `None -> None | |
284 | | `TLS (`Crt_file_path certfile, `Key_file_path keyfile, password) -> | |
285 | let password = | |
286 | match password with `No_password -> None | `Password fn -> Some fn | |
287 | in | |
288 | let ctx_ssl = | |
289 | Conduit_lwt_unix_ssl.Client.create_ctx ~certfile ~keyfile ?password () | |
290 | in | |
291 | Some ctx_ssl | |
292 | in | |
293 | Conduit_lwt_unix_ssl.Client.connect ?ctx:ctx_ssl ?src:ctx.src ~hostname sa | |
294 | >>= fun (fd, ic, oc) -> | |
295 | let flow = TCP { fd; ip; port } in | |
296 | Lwt.return (flow, ic, oc) | |
297 | ||
298 | let connect_with_default_tls ~ctx tls_client_config = | |
299 | match !tls_library with | |
300 | | OpenSSL -> connect_with_openssl ~ctx tls_client_config | |
301 | | Native -> connect_with_tls_native ~ctx tls_client_config | |
302 | | No_tls -> Lwt.fail_with "No SSL or TLS support compiled into Conduit" | |
303 | ||
304 | (** Main connection function *) | |
305 | ||
306 | let connect ~ctx (mode : client) = | |
307 | match mode with | |
308 | | `TCP (`IP ip, `Port port) -> | |
309 | let sa = Unix.ADDR_INET (Ipaddr_unix.to_inet_addr ip, port) in | |
310 | Sockaddr_client.connect ?src:ctx.src sa >>= fun (fd, ic, oc) -> | |
311 | let flow = TCP { fd; ip; port } in | |
312 | Lwt.return (flow, ic, oc) | |
313 | | `Unix_domain_socket (`File path) -> | |
314 | Sockaddr_client.connect (Unix.ADDR_UNIX path) >>= fun (fd, ic, oc) -> | |
315 | let flow = Domain_socket { fd; path } in | |
316 | Lwt.return (flow, ic, oc) | |
317 | | `TLS c -> connect_with_default_tls ~ctx c | |
318 | | `OpenSSL c -> connect_with_openssl ~ctx c | |
319 | | `TLS_native c -> connect_with_tls_native ~ctx c | |
320 | | `Vchan_direct _ -> Lwt.fail_with "Vchan_direct not available on unix" | |
321 | | `Vchan_domain_socket _uuid -> | |
322 | Lwt.fail_with "Vchan_domain_socket not implemented" | |
323 | ||
324 | let sockaddr_on_tcp_port ctx port = | |
325 | let open Unix in | |
326 | match ctx.src with | |
327 | | Some (ADDR_UNIX _) -> failwith "Cant listen to TCP on a domain socket" | |
328 | | Some (ADDR_INET (a, _)) -> (ADDR_INET (a, port), Ipaddr_unix.of_inet_addr a) | |
329 | | None -> (ADDR_INET (inet_addr_any, port), Ipaddr.(V4 V4.any)) | |
330 | ||
331 | let serve_with_openssl ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
332 | callback = | |
333 | let sockaddr, _ = sockaddr_on_tcp_port ctx port in | |
334 | let password = | |
335 | match pass with `No_password -> None | `Password fn -> Some fn | |
336 | in | |
337 | Conduit_lwt_unix_ssl.Server.init ?password ~certfile ~keyfile ?timeout ?stop | |
338 | sockaddr (fun addr fd ic oc -> callback (flow_of_fd fd addr) ic oc) | |
339 | ||
340 | let serve_with_tls_native ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
341 | callback = | |
342 | let sockaddr, _ = sockaddr_on_tcp_port ctx port in | |
343 | (match pass with | |
344 | | `No_password -> Lwt.return () | |
345 | | `Password _ -> Lwt.fail_with "OCaml-TLS cannot handle encrypted pem files") | |
346 | >>= fun () -> | |
347 | Conduit_lwt_tls.Server.init ~certfile ~keyfile ?timeout ?stop sockaddr | |
348 | (fun addr fd ic oc -> callback (flow_of_fd fd addr) ic oc) | |
349 | ||
350 | let serve_with_default_tls ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
351 | callback = | |
352 | match !tls_library with | |
353 | | OpenSSL -> | |
354 | serve_with_openssl ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
355 | callback | |
356 | | Native -> | |
357 | serve_with_tls_native ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
358 | callback | |
359 | | No_tls -> failwith "No SSL or TLS support compiled into Conduit" | |
360 | ||
361 | let serve ?backlog ?timeout ?stop ~on_exn ~(ctx : ctx) ~(mode : server) callback | |
362 | = | |
363 | let callback flow ic oc = | |
364 | Lwt.catch | |
365 | (fun () -> callback flow ic oc) | |
366 | (fun exn -> | |
367 | on_exn exn; | |
368 | Lwt.return_unit) | |
369 | in | |
370 | match mode with | |
371 | | `TCP (`Port port) -> | |
372 | let sockaddr, _ = sockaddr_on_tcp_port ctx port in | |
373 | Sockaddr_server.init ~on:(`Sockaddr sockaddr) ?backlog ?timeout ?stop | |
374 | callback | |
375 | | `TCP (`Socket s) -> | |
376 | Sockaddr_server.init ~on:(`Socket s) ?backlog ?timeout ?stop callback | |
377 | | `Unix_domain_socket (`File path) -> | |
378 | let sockaddr = Unix.ADDR_UNIX path in | |
379 | Sockaddr_server.init ~on:(`Sockaddr sockaddr) ?backlog ?timeout ?stop | |
380 | callback | |
381 | | `TLS (`Crt_file_path certfile, `Key_file_path keyfile, pass, `Port port) -> | |
382 | serve_with_default_tls ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
383 | callback | |
384 | | `OpenSSL (`Crt_file_path certfile, `Key_file_path keyfile, pass, `Port port) | |
385 | -> | |
386 | serve_with_openssl ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
387 | callback | |
388 | | `TLS_native | |
389 | (`Crt_file_path certfile, `Key_file_path keyfile, pass, `Port port) -> | |
390 | serve_with_tls_native ?timeout ?stop ~ctx ~certfile ~keyfile ~pass ~port | |
391 | callback | |
392 | | `Vchan_direct _ -> Lwt.fail_with "Vchan_direct not implemented" | |
393 | | `Vchan_domain_socket _uuid -> | |
394 | Lwt.fail_with "Vchan_domain_socket not implemented" | |
395 | | `Launchd name -> | |
396 | let fn s = Sockaddr_server.init ~on:(`Socket s) ?timeout ?stop callback in | |
397 | Conduit_lwt_launchd.activate fn name | |
398 | ||
399 | let endp_of_flow = function | |
400 | | TCP { ip; port; _ } -> `TCP (ip, port) | |
401 | | Domain_socket { path; _ } -> `Unix_domain_socket path | |
402 | | Vchan { domid; port } -> `Vchan_direct (domid, port) | |
403 | ||
404 | (** Use the configuration of the server to interpret how to handle a particular | |
405 | endpoint from the resolver into a concrete implementation of type [client] *) | |
406 | let endp_to_client ~ctx:_ (endp : Conduit.endp) : client Lwt.t = | |
407 | match endp with | |
408 | | `TCP (ip, port) -> Lwt.return (`TCP (`IP ip, `Port port)) | |
409 | | `Unix_domain_socket file -> Lwt.return (`Unix_domain_socket (`File file)) | |
410 | | `Vchan_direct (domid, port) -> | |
411 | Lwt.return (`Vchan_direct (`Domid domid, `Port port)) | |
412 | | `Vchan_domain_socket (name, port) -> | |
413 | Lwt.return (`Vchan_domain_socket (`Domain_name name, `Port port)) | |
414 | | `TLS (host, `TCP (ip, port)) -> | |
415 | Lwt.return (`TLS (`Hostname host, `IP ip, `Port port)) | |
416 | | `TLS (host, endp) -> | |
417 | Lwt.fail_with | |
418 | (Printf.sprintf "TLS to non-TCP currently unsupported: host=%s endp=%s" | |
419 | host | |
420 | (Sexplib.Sexp.to_string_hum (Conduit.sexp_of_endp endp))) | |
421 | | `Unknown err -> Lwt.fail_with ("resolution failed: " ^ err) | |
422 | ||
423 | let endp_to_server ~ctx (endp : Conduit.endp) = | |
424 | match endp with | |
425 | | `Unix_domain_socket path -> Lwt.return (`Unix_domain_socket (`File path)) | |
426 | | `TLS (_host, `TCP (_ip, port)) -> ( | |
427 | match ctx.tls_own_key with | |
428 | | `None -> Lwt.fail_with "No TLS server key configured" | |
429 | | `TLS (`Crt_file_path crt, `Key_file_path key, pass) -> | |
430 | Lwt.return | |
431 | (`TLS (`Crt_file_path crt, `Key_file_path key, pass, `Port port))) | |
432 | | `TCP (_ip, port) -> Lwt.return (`TCP (`Port port)) | |
433 | | `Vchan_direct _ as mode -> Lwt.return mode | |
434 | | `Vchan_domain_socket _ as mode -> Lwt.return mode | |
435 | | `TLS (_host, _) -> Lwt.fail_with "TLS to non-TCP currently unsupported" | |
436 | | `Unknown err -> Lwt.fail_with ("resolution failed: " ^ err) |
0 | (* | |
1 | * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * Copyright (c) 2014 Hannes Mehnert <hannes@mehnert.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | * | |
16 | *) | |
17 | ||
18 | (** Connection establishment using the {{:http://ocsigen.org/lwt/api/Lwt_unix} | |
19 | Lwt_unix} library *) | |
20 | ||
21 | (** {2 Core types} *) | |
22 | ||
23 | type client_tls_config = | |
24 | [ `Hostname of string ] * [ `IP of Ipaddr.t ] * [ `Port of int ] | |
25 | [@@deriving sexp] | |
26 | (** Configuration fragment for a TLS client connecting to a remote endpoint *) | |
27 | ||
28 | type client = | |
29 | [ `TLS of client_tls_config | |
30 | | `TLS_native of client_tls_config | |
31 | (** Force use of native OCaml TLS stack to connect.*) | |
32 | | `OpenSSL of client_tls_config | |
33 | (** Force use of Lwt OpenSSL bindings to connect. *) | |
34 | | `TCP of [ `IP of Ipaddr.t ] * [ `Port of int ] | |
35 | (** Use TCP to connect to the given [ip], [port] tuple. *) | |
36 | | `Unix_domain_socket of [ `File of string ] | |
37 | (** Use UNIX domain sockets to connect to a socket on the [path]. *) | |
38 | | `Vchan_direct of [ `Domid of int ] * [ `Port of string ] | |
39 | (** Connect to the remote VM on the [domid], [port] tuple. *) | |
40 | | `Vchan_domain_socket of [ `Domain_name of string ] * [ `Port of string ] | |
41 | (** Use the Vchan name resolution to connect *) ] | |
42 | [@@deriving sexp] | |
43 | (** Set of supported client connections that are supported by this module: | |
44 | ||
45 | - [`TLS (`Hostname host, `IP ip, `Port port)]: Use OCaml-TLS or OpenSSL | |
46 | (depending on CONDUIT_TLS) to connect to the given [host], [ip], [port] | |
47 | tuple via TCP. | |
48 | - [`TLS_native _]: Force use of native OCaml TLS stack to connect. | |
49 | - [`OpenSSL _]: Force use of Lwt OpenSSL bindings to connect. | |
50 | - [`TCP (`IP ip, `Port port)]: Use TCP to connect to the given [ip], [port] | |
51 | tuple. | |
52 | - [`Unix_domain_socket (`File path)]: Use UNIX domain sockets to connect to | |
53 | a socket on the [path]. | |
54 | - [`Vchan_direct (`Domid domid, `Port port)]: Connect to the remote VM on | |
55 | the [domid], [port] tuple. | |
56 | - [`Vchan_domain_socket (`Domain_name domain, `Port port_name)]: Use the | |
57 | Vchan name resolution to connect. *) | |
58 | ||
59 | type server_tls_config = | |
60 | [ `Crt_file_path of string ] | |
61 | * [ `Key_file_path of string ] | |
62 | * [ `Password of bool -> string | `No_password ] | |
63 | * [ `Port of int ] | |
64 | [@@deriving sexp] | |
65 | (** Configuration fragment for a listening TLS server *) | |
66 | ||
67 | type tcp_config = | |
68 | [ `Port of int | `Socket of Lwt_unix.file_descr [@sexp.opaque] ] | |
69 | [@@deriving sexp] | |
70 | (** Set of ways to create TCP servers | |
71 | ||
72 | - [`Port port]: Create a socket listening to provided port. | |
73 | - [`Socket file_descr]: Use the provided file descriptor to create a server. *) | |
74 | ||
75 | type server = | |
76 | [ `TLS of server_tls_config | |
77 | | `OpenSSL of server_tls_config | |
78 | | `TLS_native of server_tls_config | |
79 | | `TCP of tcp_config | |
80 | | `Unix_domain_socket of [ `File of string ] | |
81 | | `Vchan_direct of int * string | |
82 | | `Vchan_domain_socket of string * string | |
83 | | `Launchd of string ] | |
84 | [@@deriving sexp] | |
85 | (** Set of supported listening mechanisms that are supported by this module. | |
86 | ||
87 | - [`TLS server_tls_config]: Use OCaml-TLS or OpenSSL (depending on | |
88 | CONDUIT_TLS) to connect to the given [host], [ip], [port] tuple via TCP. | |
89 | - [`TLS_native _]: Force use of native OCaml TLS stack to connect. | |
90 | - [`OpenSSL _]: Force use of Lwt OpenSSL bindings to connect. | |
91 | - [`TCP (`Port port)]: Listen on the specified TCPv4 port. | |
92 | - [`Unix_domain_socket (`File path)]: Use UNIX domain sockets to listen on | |
93 | the path. | |
94 | - [`Vchan_direct (domid, port)]: Listen for the remote VM on the [domid], | |
95 | [port] tuple. | |
96 | - [`Vchan_domain_socket (domain, port_name)]: Use the Vchan name resolution | |
97 | to listen | |
98 | - [`Listening_socket fd]: Use the socket given, useful for inherited systemd | |
99 | sockets. | |
100 | - [`Launchd name]: uses MacOS X launchd to start the service, via the name | |
101 | of the [Sockets] element within the service description plist file. See | |
102 | the {{:http://mirage.github.io/ocaml-launchd/launchd/} ocaml-launchd} | |
103 | documentation for more. *) | |
104 | ||
105 | type 'a io = 'a Lwt.t | |
106 | type ic = Lwt_io.input_channel | |
107 | type oc = Lwt_io.output_channel | |
108 | ||
109 | type tcp_flow = private { | |
110 | fd : Lwt_unix.file_descr; [@sexp.opaque] | |
111 | ip : Ipaddr.t; | |
112 | port : int; | |
113 | } | |
114 | [@@deriving sexp_of] | |
115 | (** [tcp_flow] contains the state of a single TCP connection. *) | |
116 | ||
117 | type domain_flow = private { | |
118 | fd : Lwt_unix.file_descr; [@sexp.opaque] | |
119 | path : string; | |
120 | } | |
121 | [@@deriving sexp_of] | |
122 | (** [domain_flow] contains the state of a single Unix domain socket connection. *) | |
123 | ||
124 | type vchan_flow = private { domid : int; port : string } [@@deriving sexp_of] | |
125 | (** [vchan_flow] contains the state of a single Vchan shared memory connection. *) | |
126 | ||
127 | (** A [flow] contains the state of a single connection, over a specific | |
128 | transport method. *) | |
129 | type flow = private | |
130 | | TCP of tcp_flow | |
131 | | Domain_socket of domain_flow | |
132 | | Vchan of vchan_flow | |
133 | [@@deriving sexp_of] | |
134 | ||
135 | type tls_own_key = | |
136 | [ `None | |
137 | | `TLS of | |
138 | [ `Crt_file_path of string ] | |
139 | * [ `Key_file_path of string ] | |
140 | * [ `Password of bool -> string | `No_password ] ] | |
141 | [@@deriving sexp] | |
142 | (** Type describing where to locate a PEM key in the filesystem *) | |
143 | ||
144 | (**/**) | |
145 | ||
146 | type tls_server_key = tls_own_key [@@deriving sexp] | |
147 | ||
148 | (**/**) | |
149 | ||
150 | type ctx [@@deriving sexp_of] | |
151 | (** State handler for an active conduit *) | |
152 | ||
153 | (** {2 Connection and listening} *) | |
154 | ||
155 | val default_ctx : ctx | |
156 | (** Default context that listens on all source addresses with no TLS certificate | |
157 | associated with the Conduit *) | |
158 | ||
159 | val init : | |
160 | ?src:string -> | |
161 | ?tls_own_key:tls_own_key -> | |
162 | ?tls_authenticator:Conduit_lwt_tls.X509.authenticator -> | |
163 | unit -> | |
164 | ctx io | |
165 | (** [init ?src ?tls_own_key ()] will initialize a Unix conduit that binds to the | |
166 | [src] interface if specified. If TLS server connections are used, then | |
167 | [tls_server_key] must contain a valid certificate to be used to advertise a | |
168 | TLS connection. | |
169 | ||
170 | The certificate is validated using [tls_authenticator]. By default, the | |
171 | validation is using the {{:https://github.com/mirage/ca-certs} OS trust | |
172 | anchors}. *) | |
173 | ||
174 | val connect : ctx:ctx -> client -> (flow * ic * oc) io | |
175 | (** [connect ~ctx client] establishes an outgoing connection via the [ctx] | |
176 | context to the endpoint described by [client] *) | |
177 | ||
178 | val serve : | |
179 | ?backlog:int -> | |
180 | ?timeout:int -> | |
181 | ?stop:unit io -> | |
182 | on_exn:(exn -> unit) -> | |
183 | ctx:ctx -> | |
184 | mode:server -> | |
185 | (flow -> ic -> oc -> unit io) -> | |
186 | unit io | |
187 | (** [serve ?backlog ?timeout ?stop ~on_exn ~ctx ~mode fn] establishes a | |
188 | listening connection of type [mode], using the [ctx] context. The [stop] | |
189 | thread will terminate the server if it ever becomes determined. Every | |
190 | connection will be served in a new lightweight thread that is invoked via | |
191 | the [fn] callback. The [fn] callback is passed the {!flow} representing the | |
192 | client connection and the associated input {!ic} and output {!oc} channels. | |
193 | If the callback raises an exception, it is passed to [on_exn]. *) | |
194 | ||
195 | val set_max_active : int -> unit | |
196 | (** [set_max_active nconn] sets the maximum number of active connections | |
197 | accepted. When the limit is hit accept blocks until another server | |
198 | connection is closed. *) | |
199 | ||
200 | val endp_of_flow : flow -> Conduit.endp | |
201 | (** [endp_of_flow flow] retrieves the original {!Conduit.endp} from the | |
202 | established [flow] *) | |
203 | ||
204 | val endp_to_client : ctx:ctx -> Conduit.endp -> client io | |
205 | (** [endp_to_client ~ctx endp] converts an [endp] into a a concrete connection | |
206 | mechanism of type [client] *) | |
207 | ||
208 | val endp_to_server : ctx:ctx -> Conduit.endp -> server io | |
209 | (** [endp_to_server ~ctx endp] converts an [endp] into a a concrete connection | |
210 | mechanism of type [server] *) | |
211 | ||
212 | (** {2 TLS library selection} *) | |
213 | ||
214 | (** Currently selected method of using TLS for client and servers *) | |
215 | type tls_lib = | |
216 | | OpenSSL (** The [Lwt_ssl] bindings to the C OpenSSL library *) | |
217 | | Native (** A pure OCaml TLS implementation *) | |
218 | | No_tls (** No TLS implementation available, so any connections will fail *) | |
219 | ||
220 | val tls_library : tls_lib ref | |
221 | (** The default selection is to select {!OpenSSL}, {!Native} and {!No_tls} in | |
222 | decreasing order of priority. The native OCaml stack can be forced by | |
223 | setting the [CONDUIT_TLS] Unix environment variable to [native]. *) |
0 | (* | |
1 | * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | module Client = struct | |
18 | let default_ctx = `Ssl_not_available | |
19 | let create_ctx ?certfile:_ ?keyfile:_ ?password:_ () = default_ctx | |
20 | ||
21 | let connect ?(ctx = default_ctx) ?src:_ ?hostname:_ _sa = | |
22 | ignore ctx; | |
23 | Lwt.fail_with "Ssl not available" | |
24 | end | |
25 | ||
26 | module Server = struct | |
27 | let default_ctx = `Ssl_not_available | |
28 | ||
29 | let init ?(ctx = default_ctx) ?backlog:_ ?password:_ ~certfile:_ ~keyfile:_ | |
30 | ?stop:_ ?timeout:_ _sa _cb = | |
31 | ignore ctx; | |
32 | Lwt.fail_with "Ssl not available" | |
33 | end | |
34 | ||
35 | let available = false |
0 | (* | |
1 | * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** TLS/SSL connections via {{:http://www.openssl.org} OpenSSL} C bindings *) | |
18 | ||
19 | module Client : sig | |
20 | val default_ctx : [ `Ssl_not_available ] | |
21 | ||
22 | val create_ctx : | |
23 | ?certfile:string -> | |
24 | ?keyfile:string -> | |
25 | ?password:(bool -> string) -> | |
26 | unit -> | |
27 | [ `Ssl_not_available ] | |
28 | ||
29 | val connect : | |
30 | ?ctx:[ `Ssl_not_available ] -> | |
31 | ?src:Lwt_unix.sockaddr -> | |
32 | ?hostname:string -> | |
33 | Lwt_unix.sockaddr -> | |
34 | (Lwt_unix.file_descr * Lwt_io.input_channel * Lwt_io.output_channel) Lwt.t | |
35 | end | |
36 | ||
37 | module Server : sig | |
38 | val default_ctx : [ `Ssl_not_available ] | |
39 | ||
40 | val init : | |
41 | ?ctx:[ `Ssl_not_available ] -> | |
42 | ?backlog:int -> | |
43 | ?password:(bool -> string) -> | |
44 | certfile:string -> | |
45 | keyfile:string -> | |
46 | ?stop:unit Lwt.t -> | |
47 | ?timeout:int -> | |
48 | Lwt_unix.sockaddr -> | |
49 | (Lwt_unix.sockaddr -> | |
50 | Lwt_unix.file_descr -> | |
51 | Lwt_io.input_channel -> | |
52 | Lwt_io.output_channel -> | |
53 | unit Lwt.t) -> | |
54 | unit Lwt.t | |
55 | end | |
56 | ||
57 | (**/**) | |
58 | ||
59 | val available : bool |
0 | (* | |
1 | * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Lwt.Infix | |
18 | ||
19 | let () = Ssl.init () | |
20 | ||
21 | let chans_of_fd sock = | |
22 | let is_open = ref true in | |
23 | let shutdown () = | |
24 | if !is_open then Lwt_ssl.ssl_shutdown sock else Lwt.return_unit | |
25 | in | |
26 | let close () = | |
27 | is_open := false; | |
28 | Lwt_ssl.close sock | |
29 | in | |
30 | let oc = | |
31 | Lwt_io.make ~mode:Lwt_io.output ~close:shutdown (Lwt_ssl.write_bytes sock) | |
32 | in | |
33 | let ic = Lwt_io.make ~mode:Lwt_io.input ~close (Lwt_ssl.read_bytes sock) in | |
34 | (Lwt_ssl.get_fd sock, ic, oc) | |
35 | ||
36 | module Client = struct | |
37 | let create_ctx ?certfile ?keyfile ?password () = | |
38 | let ctx = Ssl.create_context Ssl.SSLv23 Ssl.Client_context in | |
39 | Ssl.disable_protocols ctx [ Ssl.SSLv23 ]; | |
40 | (* Use default CA certificates *) | |
41 | ignore (Ssl.set_default_verify_paths ctx); | |
42 | (* Enable peer verification *) | |
43 | Ssl.set_verify ctx [ Ssl.Verify_peer ] None; | |
44 | (match (certfile, keyfile) with | |
45 | | Some certfile, Some keyfile -> Ssl.use_certificate ctx certfile keyfile | |
46 | | None, _ | _, None -> ()); | |
47 | (match password with | |
48 | | Some password -> Ssl.set_password_callback ctx password | |
49 | | None -> ()); | |
50 | ctx | |
51 | ||
52 | let default_ctx = create_ctx () | |
53 | ||
54 | let connect ?(ctx = default_ctx) ?src ?hostname sa = | |
55 | Conduit_lwt_server.with_socket sa (fun fd -> | |
56 | (match src with | |
57 | | None -> Lwt.return_unit | |
58 | | Some src_sa -> Lwt_unix.bind fd src_sa) | |
59 | >>= fun () -> | |
60 | Lwt_unix.connect fd sa >>= fun () -> | |
61 | (match hostname with | |
62 | | Some host -> | |
63 | let s = Lwt_ssl.embed_uninitialized_socket fd ctx in | |
64 | let ssl = Lwt_ssl.ssl_socket_of_uninitialized_socket s in | |
65 | Ssl.set_client_SNI_hostname ssl host; | |
66 | (* Enable hostname verification *) | |
67 | Ssl.set_hostflags ssl [ Ssl.No_partial_wildcards ]; | |
68 | Ssl.set_host ssl host; | |
69 | Lwt_ssl.ssl_perform_handshake s | |
70 | | None -> Lwt_ssl.ssl_connect fd ctx) | |
71 | >>= fun sock -> Lwt.return (chans_of_fd sock)) | |
72 | end | |
73 | ||
74 | module Server = struct | |
75 | let default_ctx = Ssl.create_context Ssl.SSLv23 Ssl.Server_context | |
76 | let () = Ssl.disable_protocols default_ctx [ Ssl.SSLv23 ] | |
77 | ||
78 | let listen ?(ctx = default_ctx) ?backlog ?password ~certfile ~keyfile sa = | |
79 | let fd = Conduit_lwt_server.listen ?backlog sa in | |
80 | (match password with | |
81 | | None -> () | |
82 | | Some fn -> Ssl.set_password_callback ctx fn); | |
83 | Ssl.use_certificate ctx certfile keyfile; | |
84 | fd | |
85 | ||
86 | let init ?(ctx = default_ctx) ?backlog ?password ~certfile ~keyfile ?stop | |
87 | ?timeout sa cb = | |
88 | sa | |
89 | |> listen ~ctx ?backlog ?password ~certfile ~keyfile | |
90 | >>= Conduit_lwt_server.init ?stop (fun (fd, addr) -> | |
91 | Lwt.try_bind | |
92 | (fun () -> Lwt_ssl.ssl_accept fd ctx) | |
93 | (fun sock -> Lwt.return (chans_of_fd sock)) | |
94 | (fun exn -> Lwt_unix.close fd >>= fun () -> Lwt.fail exn) | |
95 | >>= Conduit_lwt_server.process_accept ?timeout (cb addr)) | |
96 | end | |
97 | ||
98 | let available = true |
0 | (* | |
1 | * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** TLS/SSL connections via {{:http://www.openssl.org} OpenSSL} C bindings *) | |
18 | ||
19 | module Client : sig | |
20 | val default_ctx : Ssl.context | |
21 | ||
22 | val create_ctx : | |
23 | ?certfile:string -> | |
24 | ?keyfile:string -> | |
25 | ?password:(bool -> string) -> | |
26 | unit -> | |
27 | Ssl.context | |
28 | ||
29 | val connect : | |
30 | ?ctx:Ssl.context -> | |
31 | ?src:Lwt_unix.sockaddr -> | |
32 | ?hostname:string -> | |
33 | Lwt_unix.sockaddr -> | |
34 | (Lwt_unix.file_descr * Lwt_io.input_channel * Lwt_io.output_channel) Lwt.t | |
35 | end | |
36 | ||
37 | module Server : sig | |
38 | val default_ctx : Ssl.context | |
39 | ||
40 | val init : | |
41 | ?ctx:Ssl.context -> | |
42 | ?backlog:int -> | |
43 | ?password:(bool -> string) -> | |
44 | certfile:string -> | |
45 | keyfile:string -> | |
46 | ?stop:unit Lwt.t -> | |
47 | ?timeout:int -> | |
48 | Lwt_unix.sockaddr -> | |
49 | (Lwt_unix.sockaddr -> | |
50 | Lwt_unix.file_descr -> | |
51 | Lwt_io.input_channel -> | |
52 | Lwt_io.output_channel -> | |
53 | unit Lwt.t) -> | |
54 | unit Lwt.t | |
55 | end | |
56 | ||
57 | (**/**) | |
58 | ||
59 | val available : bool |
0 | (library | |
1 | (name conduit_lwt_unix) | |
2 | (public_name conduit-lwt-unix) | |
3 | (preprocess | |
4 | (pps ppx_sexp_conv)) | |
5 | (wrapped false) | |
6 | (modules resolver_lwt_unix conduit_lwt_unix conduit_lwt_server | |
7 | conduit_lwt_tls conduit_lwt_unix_ssl conduit_lwt_launchd) | |
8 | (libraries | |
9 | conduit-lwt | |
10 | lwt.unix | |
11 | uri.services | |
12 | ipaddr-sexp | |
13 | ipaddr.unix | |
14 | logs | |
15 | (select | |
16 | conduit_lwt_launchd.ml | |
17 | from | |
18 | (launchd.lwt -> conduit_lwt_launchd.real.ml) | |
19 | (-> conduit_lwt_launchd.dummy.ml)) | |
20 | (select | |
21 | conduit_lwt_unix_ssl.ml | |
22 | from | |
23 | (lwt_ssl -> conduit_lwt_unix_ssl.real.ml) | |
24 | (-> conduit_lwt_unix_ssl.dummy.ml)) | |
25 | (select | |
26 | conduit_lwt_unix_ssl.mli | |
27 | from | |
28 | (lwt_ssl -> conduit_lwt_unix_ssl.real.mli) | |
29 | (-> conduit_lwt_unix_ssl.dummy.mli)) | |
30 | (select | |
31 | conduit_lwt_tls.ml | |
32 | from | |
33 | (tls.lwt ca-certs -> conduit_lwt_tls.real.ml) | |
34 | (-> conduit_lwt_tls.dummy.ml)) | |
35 | (select | |
36 | conduit_lwt_tls.mli | |
37 | from | |
38 | (tls.lwt ca-certs -> conduit_lwt_tls.real.mli) | |
39 | (-> conduit_lwt_tls.dummy.mli)))) |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Lwt.Infix | |
18 | ||
19 | let debug = ref false | |
20 | let debug_print = ref Printf.eprintf | |
21 | ||
22 | let () = | |
23 | try | |
24 | ignore (Sys.getenv "CONDUIT_DEBUG"); | |
25 | debug := true | |
26 | with Not_found -> () | |
27 | ||
28 | let return_endp name svc uri endp = | |
29 | if !debug then | |
30 | !debug_print "Resolver %s: %s %s -> %s\n%!" name (Uri.to_string uri) | |
31 | (Sexplib.Sexp.to_string_hum (Resolver.sexp_of_service svc)) | |
32 | (Sexplib.Sexp.to_string_hum (Conduit.sexp_of_endp endp)); | |
33 | Lwt.return endp | |
34 | ||
35 | let is_tls_service = | |
36 | (* TODO fill in the blanks. nowhere else to get this information *) | |
37 | function | |
38 | | "https" | "imaps" -> true | |
39 | | _ -> false | |
40 | ||
41 | let system_service name = | |
42 | (* TODO memoize *) | |
43 | Lwt.catch | |
44 | (fun () -> | |
45 | Lwt_unix.getservbyname name "tcp" >>= fun s -> | |
46 | let tls = is_tls_service name in | |
47 | let svc = { Resolver.name; port = s.Lwt_unix.s_port; tls } in | |
48 | Lwt.return (Some svc)) | |
49 | (function Not_found -> Lwt.return_none | e -> Lwt.fail e) | |
50 | ||
51 | let static_service name = | |
52 | match Uri_services.tcp_port_of_service name with | |
53 | | [] -> Lwt.return_none | |
54 | | port :: _ -> | |
55 | let tls = is_tls_service name in | |
56 | let svc = { Resolver.name; port; tls } in | |
57 | Lwt.return (Some svc) | |
58 | ||
59 | let get_host uri = | |
60 | match Uri.host uri with | |
61 | | None -> "localhost" | |
62 | | Some host -> ( | |
63 | match Ipaddr.of_string host with | |
64 | | Ok ip -> Ipaddr.to_string ip | |
65 | | Error _ -> host) | |
66 | ||
67 | let get_port service uri = | |
68 | match Uri.port uri with None -> service.Resolver.port | Some port -> port | |
69 | ||
70 | (* Build a default resolver that uses the system gethostbyname and | |
71 | the /etc/services file *) | |
72 | let system_resolver service uri = | |
73 | let open Lwt_unix in | |
74 | let host = get_host uri in | |
75 | let port = get_port service uri in | |
76 | getaddrinfo host (string_of_int port) [ AI_SOCKTYPE SOCK_STREAM ] | |
77 | >>= fun addrinfos -> | |
78 | (* In case both IPv4 and IPv6 addresses exist, favor IPv4: *) | |
79 | let v4, rest = List.partition (fun i -> i.ai_family = PF_INET) addrinfos in | |
80 | match List.rev_append v4 rest with | |
81 | | [] -> return_endp "system" service uri (`Unknown "name resolution failed") | |
82 | | { ai_addr = ADDR_INET (addr, port); _ } :: _ -> | |
83 | return_endp "system" service uri | |
84 | (`TCP (Ipaddr_unix.of_inet_addr addr, port)) | |
85 | | { ai_addr = ADDR_UNIX file; _ } :: _ -> | |
86 | return_endp "system" service uri (`Unix_domain_socket file) | |
87 | ||
88 | let static_resolver hosts service uri = | |
89 | try return_endp "static" service uri (Hashtbl.find hosts (get_host uri)) | |
90 | with Not_found -> | |
91 | return_endp "static" service uri (`Unknown "name resolution failed") | |
92 | ||
93 | let system = | |
94 | let service = system_service in | |
95 | let rewrites = [ ("", system_resolver) ] in | |
96 | Resolver_lwt.init ~service ~rewrites () | |
97 | ||
98 | (* Build a default resolver from a static set of lookup rules *) | |
99 | let static hosts = | |
100 | let service = static_service in | |
101 | let rewrites = [ ("", static_resolver hosts) ] in | |
102 | Resolver_lwt.init ~service ~rewrites () |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** Resolve URIs to endpoints using Unix system calls *) | |
18 | ||
19 | (** {2 Prebuilt resolvers} *) | |
20 | ||
21 | val system : Resolver_lwt.t | |
22 | (** Use the Unix system name resolver via [getaddrinfo] and [getservbyname] *) | |
23 | ||
24 | val static : (string, Conduit.endp) Hashtbl.t -> Resolver_lwt.t | |
25 | (** [static hosts] constructs a resolver that looks up any resolution requests | |
26 | from the static [hosts] hashtable instead of using the system resolver. *) | |
27 | ||
28 | (** {2 Rewrite and service functions} | |
29 | ||
30 | These can be used to assemble your own resolvers if the prebuilt ones are | |
31 | not quite what you need. *) | |
32 | ||
33 | val system_service : string -> Resolver_lwt.svc option Lwt.t | |
34 | (** Perform service lookup using [getservbyname] *) | |
35 | ||
36 | val static_service : string -> Resolver_lwt.svc option Lwt.t | |
37 | (** Perform service lookup using the builtin {!Uri_services} module *) | |
38 | ||
39 | val system_resolver : Resolver_lwt.rewrite_fn | |
40 | (** Rewrite function that uses the {!system_service} and {!static_service} to | |
41 | resolve hosts *) | |
42 | ||
43 | (** {2 Debugging Hooks} *) | |
44 | ||
45 | val debug : bool ref | |
46 | (** If [debug] is true, the builtin resolvers will output their resolution | |
47 | responses via the {!debug_print} function. The default value of [debug] is | |
48 | true if the [CONDUIT_DEBUG] environment variable is set, and false | |
49 | otherwise. *) | |
50 | ||
51 | val debug_print : | |
52 | ((string -> string -> string -> string -> unit, out_channel, unit) format -> | |
53 | string -> | |
54 | string -> | |
55 | string -> | |
56 | string -> | |
57 | unit) | |
58 | ref | |
59 | (** [debug_print] is called by the {!debug} functions to output the results of | |
60 | resolution. Defaults to {!Printf.eprintf} to go to the standard error. *) |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * Copyright (c) 2015 Thomas Gazagnaire <thomas@gazagnaire.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | * | |
16 | *) | |
17 | ||
18 | open Sexplib.Conv | |
19 | ||
20 | let ( >>= ) = Lwt.( >>= ) | |
21 | let ( >|= ) = Lwt.( >|= ) | |
22 | let fail fmt = Fmt.kstrf (fun s -> Lwt.fail (Failure s)) fmt | |
23 | let err_tcp_not_supported = fail "%s: TCP is not supported" | |
24 | let err_tls_not_supported = fail "%s: TLS is not supported" | |
25 | ||
26 | let err_domain_sockets_not_supported = | |
27 | fail "%s: Unix domain sockets are not supported inside Unikernels" | |
28 | ||
29 | let err_vchan_not_supported = fail "%s: VCHAN is not supported" | |
30 | let err_unknown = fail "%s: unknown endpoint type" | |
31 | ||
32 | let err_not_supported = function | |
33 | | `TLS _ -> err_tls_not_supported | |
34 | | `TCP _ -> err_tcp_not_supported | |
35 | | `Vchan _ -> err_vchan_not_supported | |
36 | ||
37 | type client = | |
38 | [ `TCP of Ipaddr_sexp.t * int | |
39 | | `TLS of Tls.Config.client * client | |
40 | | `Vchan of | |
41 | [ `Direct of int * Vchan.Port.t | `Domain_socket of string * Vchan.Port.t ] | |
42 | ] | |
43 | [@@deriving sexp] | |
44 | ||
45 | type server = | |
46 | [ `TCP of int | |
47 | | `TLS of Tls.Config.server * server | |
48 | | `Vchan of [ `Direct of int * Vchan.Port.t | `Domain_socket ] ] | |
49 | [@@deriving sexp] | |
50 | ||
51 | module type S = sig | |
52 | type t | |
53 | type flow | |
54 | ||
55 | module Flow : Mirage_flow.S with type flow = flow | |
56 | ||
57 | val connect : t -> client -> flow Lwt.t | |
58 | val listen : t -> server -> (flow -> unit Lwt.t) -> unit Lwt.t | |
59 | end | |
60 | ||
61 | (* TCP *) | |
62 | let tcp_client i p = Lwt.return (`TCP (i, p)) | |
63 | let tcp_server _ p = Lwt.return (`TCP p) | |
64 | ||
65 | module TCP (S : Mirage_stack.V4V6) = struct | |
66 | module Flow = S.TCP | |
67 | ||
68 | type flow = Flow.flow | |
69 | type t = S.t | |
70 | ||
71 | let err_tcp e = | |
72 | Lwt.fail | |
73 | @@ Failure (Format.asprintf "TCP connection failed: %a" S.TCP.pp_error e) | |
74 | ||
75 | let connect (t : t) (c : client) = | |
76 | match c with | |
77 | | `TCP (ip, port) -> ( | |
78 | S.TCP.create_connection (S.tcp t) (ip, port) >>= function | |
79 | | Error e -> err_tcp e | |
80 | | Ok flow -> Lwt.return flow) | |
81 | | _ -> err_not_supported c "connect" | |
82 | ||
83 | let listen (t : t) (s : server) fn = | |
84 | match s with | |
85 | | `TCP port -> | |
86 | let s, _u = Lwt.task () in | |
87 | S.listen_tcp t ~port (fun flow -> fn flow); | |
88 | s | |
89 | | _ -> err_not_supported s "listen" | |
90 | end | |
91 | ||
92 | (* VCHAN *) | |
93 | ||
94 | let err_vchan_port = fail "%s: invalid Vchan port" | |
95 | ||
96 | let port p = | |
97 | match Vchan.Port.of_string p with | |
98 | | Error (`Msg s) -> err_vchan_port s | |
99 | | Ok p -> Lwt.return p | |
100 | ||
101 | let vchan_client = function | |
102 | | `Vchan_direct (i, p) -> port p >|= fun p -> `Vchan (`Direct (i, p)) | |
103 | | `Vchan_domain_socket (i, p) -> | |
104 | port p >|= fun p -> `Vchan (`Domain_socket (i, p)) | |
105 | ||
106 | let vchan_server = function | |
107 | | `Vchan_direct (i, p) -> port p >|= fun p -> `Vchan (`Direct (i, p)) | |
108 | | `Vchan_domain_socket _ -> Lwt.return (`Vchan `Domain_socket) | |
109 | ||
110 | module Vchan | |
111 | (Xs : Xs_client_lwt.S) | |
112 | (V : Vchan.S.ENDPOINT with type port = Vchan.Port.t) = | |
113 | struct | |
114 | module Flow = V | |
115 | module XS = Conduit_xenstore.Make (Xs) | |
116 | ||
117 | type flow = Flow.flow | |
118 | type t = XS.t | |
119 | ||
120 | let register = XS.register | |
121 | ||
122 | let rec connect (t : t) (c : client) = | |
123 | match c with | |
124 | | `Vchan (`Domain_socket (uid, port)) -> | |
125 | XS.connect t ~remote_name:uid ~port >>= fun endp -> | |
126 | connect t (`Vchan endp :> client) | |
127 | | `Vchan (`Direct (domid, port)) -> V.client ~domid ~port () | |
128 | | _ -> err_not_supported c "connect" | |
129 | ||
130 | let listen (t : t) (s : server) fn = | |
131 | match s with | |
132 | | `Vchan (`Direct (domid, port)) -> V.server ~domid ~port () >>= fn | |
133 | | `Vchan `Domain_socket -> | |
134 | XS.listen t >>= fun conns -> | |
135 | Lwt_stream.iter_p | |
136 | (function `Direct (domid, port) -> V.server ~domid ~port () >>= fn) | |
137 | conns | |
138 | | _ -> err_not_supported s "listen" | |
139 | end | |
140 | ||
141 | (* TLS *) | |
142 | ||
143 | let tls_client ~authenticator x = `TLS (Tls.Config.client ~authenticator (), x) | |
144 | let tls_server ?authenticator x = `TLS (Tls.Config.server ?authenticator (), x) | |
145 | ||
146 | module TLS (S : S) = struct | |
147 | module TLS = Tls_mirage.Make (S.Flow) | |
148 | ||
149 | type flow = TLS of TLS.flow | Clear of S.flow | |
150 | type t = S.t | |
151 | ||
152 | module Flow = struct | |
153 | type nonrec flow = flow | |
154 | type error = [ `Flow of S.Flow.error | `TLS of TLS.error ] | |
155 | ||
156 | type write_error = | |
157 | [ Mirage_flow.write_error | |
158 | | `Flow of S.Flow.write_error | |
159 | | `TLS of TLS.write_error ] | |
160 | ||
161 | let pp_error ppf = function | |
162 | | `Flow e -> S.Flow.pp_error ppf e | |
163 | | `TLS e -> TLS.pp_error ppf e | |
164 | ||
165 | let pp_write_error ppf = function | |
166 | | #Mirage_flow.write_error as e -> Mirage_flow.pp_write_error ppf e | |
167 | | `Flow e -> S.Flow.pp_write_error ppf e | |
168 | | `TLS e -> TLS.pp_write_error ppf e | |
169 | ||
170 | let tls_err = function Ok _ as x -> x | Error e -> Error (`TLS e) | |
171 | let flow_err = function Ok _ as x -> x | Error e -> Error (`Flow e) | |
172 | ||
173 | let tls_write_err = function | |
174 | | Ok _ as x -> x | |
175 | | Error `Closed as x -> x | |
176 | | Error e -> Error (`TLS e) | |
177 | ||
178 | let flow_write_err = function | |
179 | | Ok _ as x -> x | |
180 | | Error `Closed as x -> x | |
181 | | Error e -> Error (`Flow e) | |
182 | ||
183 | let read = function | |
184 | | TLS f -> TLS.read f >|= tls_err | |
185 | | Clear f -> S.Flow.read f >|= flow_err | |
186 | ||
187 | let write t x = | |
188 | match t with | |
189 | | TLS f -> TLS.write f x >|= tls_write_err | |
190 | | Clear f -> S.Flow.write f x >|= flow_write_err | |
191 | ||
192 | let writev t x = | |
193 | match t with | |
194 | | TLS f -> TLS.writev f x >|= tls_err | |
195 | | Clear f -> S.Flow.writev f x >|= flow_err | |
196 | ||
197 | let close = function TLS f -> TLS.close f | Clear f -> S.Flow.close f | |
198 | end | |
199 | ||
200 | let err_flow_write m e = fail "%s: %a" m TLS.pp_write_error e | |
201 | ||
202 | let connect (t : t) (c : client) = | |
203 | match c with | |
204 | | `TLS (c, x) -> ( | |
205 | S.connect t x >>= fun flow -> | |
206 | TLS.client_of_flow c flow >>= function | |
207 | | Error e -> err_flow_write "connect" e | |
208 | | Ok flow -> Lwt.return (TLS flow)) | |
209 | | _ -> S.connect t c >|= fun t -> Clear t | |
210 | ||
211 | let listen (t : t) (s : server) fn = | |
212 | match s with | |
213 | | `TLS (c, x) -> | |
214 | S.listen t x (fun flow -> | |
215 | TLS.server_of_flow c flow >>= function | |
216 | | Error e -> err_flow_write "listen" e | |
217 | | Ok flow -> fn (TLS flow)) | |
218 | | _ -> S.listen t s (fun f -> fn (Clear f)) | |
219 | end | |
220 | ||
221 | module Endpoint (P : Mirage_clock.PCLOCK) = struct | |
222 | module Ca_certs = Ca_certs_nss.Make (P) | |
223 | ||
224 | let nss_authenticator = | |
225 | match Ca_certs.authenticator () with | |
226 | | Ok a -> a | |
227 | | Error (`Msg msg) -> failwith msg | |
228 | ||
229 | let rec client ?(tls_authenticator = nss_authenticator) e = | |
230 | match e with | |
231 | | `TCP (x, y) -> tcp_client x y | |
232 | | `Unix_domain_socket _ -> err_domain_sockets_not_supported "client" | |
233 | | (`Vchan_direct _ | `Vchan_domain_socket _) as x -> vchan_client x | |
234 | | `TLS (_host, y) -> | |
235 | client ~tls_authenticator y | |
236 | >|= tls_client ~authenticator:tls_authenticator | |
237 | | `Unknown s -> err_unknown s | |
238 | ||
239 | let rec server ?tls_authenticator e = | |
240 | match e with | |
241 | | `TCP (x, y) -> tcp_server x y | |
242 | | `Unix_domain_socket _ -> err_domain_sockets_not_supported "server" | |
243 | | (`Vchan_direct _ | `Vchan_domain_socket _) as x -> vchan_server x | |
244 | | `TLS (_host, y) -> | |
245 | server y >|= tls_server ?authenticator:tls_authenticator | |
246 | | `Unknown s -> err_unknown s | |
247 | end |
0 | (* | |
1 | * Copyright (c) 2012-2015 Anil Madhavapeddy <anil@recoil.org> | |
2 | * Copyright (c) 2015 Thomas Gazagnaire <thomas@gazagnaire.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | * | |
16 | *) | |
17 | ||
18 | (** Functorial connection establishment interface that is compatible with the | |
19 | Mirage libraries. *) | |
20 | ||
21 | type client = | |
22 | [ `TCP of Ipaddr.t * int (** address and destination port *) | |
23 | | `TLS of Tls.Config.client * client | |
24 | | `Vchan of | |
25 | [ `Direct of int * Vchan.Port.t (** domain id, port *) | |
26 | | `Domain_socket of string * Vchan.Port.t (** Vchan Xen domain socket *) | |
27 | ] ] | |
28 | [@@deriving sexp] | |
29 | (** The type for client configuration values. *) | |
30 | ||
31 | type server = | |
32 | [ `TCP of int (** listening port *) | |
33 | | `TLS of Tls.Config.server * server | |
34 | | `Vchan of | |
35 | [ `Direct of int * Vchan.Port.t (** domain id, port *) | |
36 | | `Domain_socket (** Vchan Xen domain socket *) ] ] | |
37 | [@@deriving sexp] | |
38 | (** The type for server configuration values. *) | |
39 | ||
40 | module Endpoint (P : Mirage_clock.PCLOCK) : sig | |
41 | val nss_authenticator : X509.Authenticator.t | |
42 | (** [nss_authenticator] is the validator using the | |
43 | {{:https://github.com/mirage/ca-certs-nss} trust anchors extracted from | |
44 | Mozilla's NSS}. *) | |
45 | ||
46 | val client : | |
47 | ?tls_authenticator:X509.Authenticator.t -> Conduit.endp -> client Lwt.t | |
48 | (** [client] resolves a conduit endpoint into a client configuration. | |
49 | ||
50 | The certificate is validated using [tls_authenticator]. By default, it is | |
51 | [nss_authenticator] *) | |
52 | ||
53 | val server : | |
54 | ?tls_authenticator:X509.Authenticator.t -> Conduit.endp -> server Lwt.t | |
55 | (** [server] resolves a confuit endpoint into a server configuration. | |
56 | ||
57 | Clent certificates are validated using [tls_authenticator]. *) | |
58 | end | |
59 | ||
60 | module type S = sig | |
61 | (** The signature for conduits *) | |
62 | ||
63 | type flow | |
64 | (** The type for networking flows. *) | |
65 | ||
66 | type t | |
67 | (** The type for handlers. *) | |
68 | ||
69 | module Flow : Mirage_flow.S with type flow = flow | |
70 | (** The type for flows. *) | |
71 | ||
72 | val connect : t -> client -> flow Lwt.t | |
73 | (** Connect a conduit using client configuration. *) | |
74 | ||
75 | val listen : t -> server -> (flow -> unit Lwt.t) -> unit Lwt.t | |
76 | (** Listen to a conduit using a server configuration. *) | |
77 | end | |
78 | ||
79 | (** {2 TCP} *) | |
80 | ||
81 | module TCP (S : Mirage_stack.V4V6) : | |
82 | S with type t = S.t and type flow = S.TCP.flow | |
83 | ||
84 | (** {2 VCHAN} *) | |
85 | ||
86 | module Vchan | |
87 | (X : Xs_client_lwt.S) | |
88 | (V : Vchan.S.ENDPOINT with type port = Vchan.Port.t) : sig | |
89 | include S | |
90 | ||
91 | val register : string -> t Lwt.t | |
92 | end | |
93 | ||
94 | (** {2 TLS} *) | |
95 | ||
96 | module TLS (S : S) : sig | |
97 | type flow = TLS of Tls_mirage.Make(S.Flow).flow | Clear of S.flow | |
98 | ||
99 | include S with type t = S.t and type flow := flow | |
100 | end |
0 | (* | |
1 | * Copyright (c) 2014-2015 Anil Madhavapeddy <anil@recoil.org> | |
2 | * Copyright (c) 2015 Thomas Gazagnaire <thomas@gazagnaire.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | * | |
16 | *) | |
17 | ||
18 | open Sexplib.Conv | |
19 | ||
20 | type direct = [ `Direct of int * Vchan.Port.t ] | |
21 | ||
22 | let ( >>= ) = Lwt.( >>= ) | |
23 | let ( / ) = Filename.concat | |
24 | let fail fmt = Printf.ksprintf (fun m -> Lwt.fail (Failure m)) fmt | |
25 | let err_peer_not_found = fail "Conduit_xenstore: %s peer not found" | |
26 | ||
27 | let err_no_entry_found () = | |
28 | fail "No /conduit Xenstore entry found. Run `xenstore-conduit-init`" | |
29 | ||
30 | let err_port = fail "%s: invalid port" | |
31 | ||
32 | module Make (Xs : Xs_client_lwt.S) = struct | |
33 | type t = { xs : (Xs.client[@sexp.opaque]); name : string } | |
34 | [@@deriving sexp_of] | |
35 | ||
36 | let get_my_id xs = Xs.(immediate xs (fun h -> read h "domid")) | |
37 | ||
38 | let xenstore_register xs myname = | |
39 | get_my_id xs >>= fun domid -> | |
40 | Xs.(immediate xs (fun h -> write h ("/conduit" / myname) domid)) | |
41 | ||
42 | let get_peer_id xs name = | |
43 | Lwt.catch | |
44 | (fun () -> Xs.(immediate xs (fun h -> read h ("/conduit" / name)))) | |
45 | (fun _ -> err_peer_not_found name) | |
46 | ||
47 | let readdir h d = | |
48 | Xs.(directory h d) >>= fun dirs -> | |
49 | let dirs = List.filter (fun p -> p <> "") dirs in | |
50 | match dirs with | |
51 | | [] -> Lwt.fail Xs_protocol.Eagain | |
52 | | hd :: _ -> Lwt.return hd | |
53 | ||
54 | let register name = | |
55 | Xs.make () >>= fun xs -> | |
56 | (* Check that a /conduit directory exists *) | |
57 | Lwt.catch | |
58 | (fun () -> | |
59 | Xs.(immediate xs (fun h -> read h "/conduit")) >>= fun _ -> | |
60 | Lwt.return_unit) | |
61 | (fun _ -> err_no_entry_found ()) | |
62 | >>= fun () -> | |
63 | xenstore_register xs name >>= fun () -> Lwt.return { xs; name } | |
64 | ||
65 | let accept { xs; name } = | |
66 | let waitfn h = | |
67 | readdir h ("/conduit" / name) >>= fun remote_name -> | |
68 | readdir h ("/conduit" / name / remote_name) >>= fun port -> | |
69 | Xs.read h ("/conduit" / remote_name) >>= fun remote_domid -> | |
70 | let remote_domid = int_of_string remote_domid in | |
71 | Xs.rm h ("/conduit" / name / remote_name) >>= fun () -> | |
72 | match Vchan.Port.of_string port with | |
73 | | Error (`Msg e) -> err_port e | |
74 | | Ok port -> Lwt.return (`Direct (remote_domid, port)) | |
75 | in | |
76 | Xs.wait xs waitfn | |
77 | ||
78 | let listen ({ name; _ } as v) = | |
79 | (* TODO cancellation *) | |
80 | let conn, push_conn = Lwt_stream.create () in | |
81 | Printf.printf "Conduit_xenstore: listen on %s\n%!" name; | |
82 | let rec loop () = | |
83 | accept v >>= fun c -> | |
84 | push_conn (Some c); | |
85 | loop () | |
86 | in | |
87 | Lwt.ignore_result (loop ()); | |
88 | Lwt.return conn | |
89 | ||
90 | let connect { xs; name } ~remote_name ~port = | |
91 | let port_str = Vchan.Port.to_string port in | |
92 | get_peer_id xs remote_name >>= fun remote_domid -> | |
93 | let remote_domid = int_of_string remote_domid in | |
94 | let path = "/conduit" / remote_name / name / port_str in | |
95 | Xs.(immediate xs (fun h -> write h path port_str)) >>= fun () -> | |
96 | Lwt.return (`Direct (remote_domid, port)) | |
97 | end |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** Establish Vchans via named endpoints in XenStore *) | |
18 | ||
19 | type direct = [ `Direct of int * Vchan.Port.t ] | |
20 | ||
21 | module Make (Xs : Xs_client_lwt.S) : sig | |
22 | type t | |
23 | ||
24 | val register : string -> t Lwt.t | |
25 | val listen : t -> direct Lwt_stream.t Lwt.t | |
26 | val connect : t -> remote_name:string -> port:Vchan.Port.t -> direct Lwt.t | |
27 | end |
0 | (library | |
1 | (name conduit_mirage) | |
2 | (public_name conduit-mirage) | |
3 | (preprocess | |
4 | (pps ppx_sexp_conv)) | |
5 | (modules conduit_mirage resolver_mirage conduit_xenstore) | |
6 | (wrapped false) | |
7 | (libraries conduit conduit-lwt mirage-stack mirage-clock mirage-random | |
8 | mirage-time mirage-flow mirage-flow-combinators dns-client.mirage | |
9 | ipaddr-sexp vchan tls tls-mirage xenstore.client uri.services ca-certs-nss)) |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Lwt.Infix | |
18 | ||
19 | module type S = sig | |
20 | include Resolver_lwt.S | |
21 | ||
22 | val static : (string, port:int -> Conduit.endp) Hashtbl.t -> t | |
23 | val localhost : t | |
24 | end | |
25 | ||
26 | module Make | |
27 | (R : Mirage_random.S) | |
28 | (T : Mirage_time.S) | |
29 | (C : Mirage_clock.MCLOCK) | |
30 | (S : Mirage_stack.V4V6) = | |
31 | struct | |
32 | include Resolver_lwt | |
33 | ||
34 | let is_tls_service = | |
35 | (* TODO fill in the blanks. nowhere else to get this information *) | |
36 | function | |
37 | | "https" | "imaps" -> true | |
38 | | _ -> false | |
39 | ||
40 | let get_host uri = | |
41 | match Uri.host uri with | |
42 | | None -> "localhost" | |
43 | | Some host -> ( | |
44 | match Ipaddr.of_string host with | |
45 | | Ok ip -> Ipaddr.to_string ip | |
46 | | Error _ -> host) | |
47 | ||
48 | let get_port service uri = | |
49 | match Uri.port uri with None -> service.Resolver.port | Some port -> port | |
50 | ||
51 | let static_resolver hosts service uri = | |
52 | let port = get_port service uri in | |
53 | try | |
54 | let fn = Hashtbl.find hosts (get_host uri) in | |
55 | Lwt.return (fn ~port) | |
56 | with Not_found -> Lwt.return (`Unknown "name resolution failed") | |
57 | ||
58 | let static_service name = | |
59 | match Uri_services.tcp_port_of_service name with | |
60 | | [] -> Lwt.return_none | |
61 | | port :: _ -> | |
62 | let tls = is_tls_service name in | |
63 | let svc = { Resolver.name; port; tls } in | |
64 | Lwt.return (Some svc) | |
65 | ||
66 | let static hosts = | |
67 | let service = static_service in | |
68 | let rewrites = [ ("", static_resolver hosts) ] in | |
69 | Resolver_lwt.init ~service ~rewrites () | |
70 | ||
71 | let localhost = | |
72 | let hosts = Hashtbl.create 3 in | |
73 | Hashtbl.add hosts "localhost" (fun ~port -> | |
74 | `TCP (Ipaddr.(V4 V4.localhost), port)); | |
75 | static hosts | |
76 | ||
77 | let vchan_resolver ~tld = | |
78 | let tld_len = String.length tld in | |
79 | let get_short_host uri = | |
80 | let n = get_host uri in | |
81 | let len = String.length n in | |
82 | if len > tld_len && String.sub n (len - tld_len) tld_len = tld then | |
83 | String.sub n 0 (len - tld_len) | |
84 | else n | |
85 | in | |
86 | fun service uri -> | |
87 | (* Strip the tld from the hostname *) | |
88 | let remote_name = get_short_host uri in | |
89 | Printf.printf "vchan_lookup: %s %s -> normalizes to %s\n%!" | |
90 | (Sexplib.Sexp.to_string_hum (Resolver.sexp_of_service service)) | |
91 | (Uri.to_string uri) remote_name; | |
92 | Lwt.return (`Vchan_domain_socket (remote_name, service.Resolver.name)) | |
93 | ||
94 | module DNS = Dns_client_mirage.Make (R) (T) (C) (S) | |
95 | ||
96 | let dns_stub_resolver dns service uri : Conduit.endp Lwt.t = | |
97 | let hostn = get_host uri in | |
98 | let port = get_port service uri in | |
99 | (match Ipaddr.V4.of_string hostn with | |
100 | | Ok addr -> Lwt.return (Ok addr) | |
101 | | Error _ -> ( | |
102 | match Domain_name.of_string hostn with | |
103 | | Error (`Msg msg) -> Lwt.return (Error (`Msg msg)) | |
104 | | Ok domain -> ( | |
105 | match Domain_name.host domain with | |
106 | | Error (`Msg msg) -> Lwt.return (Error (`Msg msg)) | |
107 | | Ok host -> DNS.gethostbyname dns host))) | |
108 | >|= function | |
109 | | Error (`Msg err) -> `Unknown ("name resolution failed: " ^ err) | |
110 | | Ok addr -> `TCP (Ipaddr.V4 addr, port) | |
111 | ||
112 | let register ?ns ?(ns_port = 53) s res = | |
113 | (* DNS stub resolver *) | |
114 | let nameserver = | |
115 | match ns with None -> None | Some ip -> Some (`TCP, (ip, ns_port)) | |
116 | in | |
117 | let dns = DNS.create ?nameserver s in | |
118 | let f = dns_stub_resolver dns in | |
119 | Resolver_lwt.add_rewrite ~host:"" ~f res; | |
120 | let service = Resolver_lwt.(service res ++ static_service) in | |
121 | Resolver_lwt.set_service ~f:service res; | |
122 | let vchan_tld = ".xen" in | |
123 | let vchan_res = vchan_resolver ~tld:vchan_tld in | |
124 | Resolver_lwt.add_rewrite ~host:vchan_tld ~f:vchan_res res | |
125 | ||
126 | let v ?ns ?ns_port stack = | |
127 | let res = Resolver_lwt.init () in | |
128 | register ?ns ?ns_port stack res; | |
129 | res | |
130 | ||
131 | type t = Resolver_lwt.t | |
132 | end |
0 | (* | |
1 | * Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | (** Functorial interface for resolving URIs to endpoints. *) | |
18 | ||
19 | module type S = sig | |
20 | include Resolver_lwt.S | |
21 | ||
22 | val static : (string, port:int -> Conduit.endp) Hashtbl.t -> t | |
23 | (** [static hosts] constructs a resolver that looks up any resolution requests | |
24 | from the static [hosts] hashtable instead of using the system resolver. *) | |
25 | ||
26 | val localhost : t | |
27 | (** [localhost] is a static resolver that has a single entry that maps | |
28 | [localhost] to [127.0.0.1], and fails on all other hostnames. *) | |
29 | end | |
30 | ||
31 | (** Provides a DNS-enabled {!Resolver_lwt} given a network stack. *) | |
32 | module Make | |
33 | (R : Mirage_random.S) | |
34 | (T : Mirage_time.S) | |
35 | (C : Mirage_clock.MCLOCK) | |
36 | (S : Mirage_stack.V4V6) : sig | |
37 | include S | |
38 | ||
39 | val v : ?ns:Ipaddr.t -> ?ns_port:int -> S.t -> t | |
40 | (** [v ?ns ?ns_port ?stack ()] TODO *) | |
41 | end |
0 | (executable | |
1 | (libraries async conduit-async) | |
2 | (name ssl_echo)) | |
3 | ||
4 | (rule | |
5 | (alias runtest) | |
6 | (deps | |
7 | (:echo ./ssl_echo.exe)) | |
8 | (package conduit-async) | |
9 | (action | |
10 | (run echo skip))) |
0 | (* | |
1 | * Copyright (c) 2015 Anil Madhavapeddy <anil@recoil.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | * | |
15 | *) | |
16 | ||
17 | open Async | |
18 | ||
19 | let handler _sock ic oc = | |
20 | Reader.pipe ic |> fun rd -> | |
21 | Writer.pipe oc |> fun wr -> Pipe.transfer_id rd wr | |
22 | ||
23 | let determine_mode cert_file_path key_file_path = | |
24 | (* Determines if the server runs in http or https *) | |
25 | match (cert_file_path, key_file_path) with | |
26 | | Some c, Some k -> `OpenSSL (`Crt_file_path c, `Key_file_path k) | |
27 | | None, None -> `TCP | |
28 | | _ -> failwith "Error: must specify both certificate and key for TLS" | |
29 | ||
30 | let start_server port host cert_file key_file () = | |
31 | let mode = determine_mode cert_file key_file in | |
32 | let mode_str = match mode with `OpenSSL _ -> "OpenSSL" | `TCP -> "TCP" in | |
33 | printf "Listening for %s requests on: %s %d\n%!" mode_str host port; | |
34 | Unix.Inet_addr.of_string_or_getbyname host >>= fun host -> | |
35 | let listen_on = | |
36 | Tcp.Where_to_listen.create ~socket_type:Socket.Type.tcp | |
37 | ~address:(`Inet (host, port)) | |
38 | ~listening_on:(fun _ -> port) | |
39 | in | |
40 | Conduit_async.serve ~on_handler_error:`Raise mode listen_on handler | |
41 | >>= fun _ -> never () | |
42 | ||
43 | let () = | |
44 | Command.async_spec ~summary:"Echo server over SSL" | |
45 | Command.Spec.( | |
46 | empty | |
47 | +> flag "-p" | |
48 | (optional_with_default 8080 int) | |
49 | ~doc:"port TCP port to listen on" | |
50 | +> flag "-s" | |
51 | (optional_with_default "0.0.0.0" string) | |
52 | ~doc:"address IP address to listen on" | |
53 | +> flag "-cert-file" (optional string) ~doc:"file Certificate file" | |
54 | +> flag "-key-file" (optional string) ~doc:"File Private key file") | |
55 | start_server | |
56 | |> Command.run |
0 | (* | |
1 | * Copyright (c) 2016 Skylable Ltd. <info-copyright@skylable.com> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | *) | |
15 | ||
16 | open Lwt.Infix | |
17 | ||
18 | let port = | |
19 | Random.self_init (); | |
20 | 16_384 + Random.int 10_000 | |
21 | ||
22 | let config = | |
23 | ( `Crt_file_path "server.pem", | |
24 | `Key_file_path "server.key", | |
25 | `No_password, | |
26 | `Port port ) | |
27 | ||
28 | let rec repeat n f = | |
29 | if n = 0 then Lwt.return_unit else f () >>= fun () -> repeat (n - 1) f | |
30 | ||
31 | let skip _ = () | |
32 | ||
33 | let perform () = | |
34 | let stop, do_stop = Lwt.wait () in | |
35 | Conduit_lwt_unix.init ~src:"127.0.0.1" () >>= fun ctx -> | |
36 | let _ = | |
37 | Conduit_lwt_unix.serve ~stop ~ctx ~mode:(`TLS config) ~on_exn:skip | |
38 | (fun _ ic oc -> | |
39 | Lwt_io.read ic >>= fun _ -> | |
40 | Lwt_io.write oc "foo" >>= fun () -> Lwt_io.flush oc) | |
41 | in | |
42 | let sa = Unix.ADDR_INET (Unix.inet_addr_loopback, port) in | |
43 | let wait, wake = Lwt.task () in | |
44 | let active = ref 0 in | |
45 | let cond = Lwt_condition.create () in | |
46 | let client_test_wait timeout wait = | |
47 | (* connect using low-level operations to check what happens if client closes connection | |
48 | without calling ssl_shutdown (e.g. TCP connection is lost) *) | |
49 | let s = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in | |
50 | let ctx = Ssl.create_context Ssl.TLSv1_2 Ssl.Client_context in | |
51 | Lwt_unix.with_timeout timeout (fun () -> | |
52 | Lwt.finalize | |
53 | (fun () -> | |
54 | Lwt_unix.connect s sa >>= fun () -> | |
55 | Lwt_ssl.ssl_connect s ctx >>= fun _ss -> | |
56 | incr active; | |
57 | Lwt_condition.signal cond (); | |
58 | wait) | |
59 | (fun () -> Lwt_unix.close s)) | |
60 | in | |
61 | let client_test _ = client_test_wait 1. Lwt.return_unit in | |
62 | let limit = 5 in | |
63 | ||
64 | Conduit_lwt_unix.set_max_active limit; | |
65 | (* when clients = max_active no more clients are allowed and some get errors, | |
66 | * use a higher timeout here so that all these connections are still active | |
67 | * when doing the 2nd test below *) | |
68 | let t = | |
69 | Array.init limit (fun _ -> client_test_wait 10. wait) | |
70 | |> Array.to_list | |
71 | |> Lwt.join | |
72 | in | |
73 | Lwt.catch | |
74 | (fun () -> | |
75 | (* wait for all 5 threads to connect *) | |
76 | let rec wait_all_conn () = | |
77 | Lwt_condition.wait cond >>= fun () -> | |
78 | if !active < limit then wait_all_conn () else Lwt.return_unit | |
79 | in | |
80 | wait_all_conn () >>= fun () -> | |
81 | print_endline "Waiting for error"; | |
82 | (* use a lower timeout here, these should fail immediately *) | |
83 | Array.init (2 * limit) client_test |> Array.to_list |> Lwt.pick | |
84 | >>= fun () -> | |
85 | prerr_endline "Expected errors, but got none"; | |
86 | exit 2) | |
87 | (fun _exn -> | |
88 | print_endline "Waking up connections"; | |
89 | Lwt.wakeup wake (); | |
90 | Lwt.catch (fun () -> t) (fun _ -> Lwt.return_unit) >>= fun () -> | |
91 | print_endline "Opening more connections"; | |
92 | (* clients can connect again, handled in batches of 5 *) | |
93 | Array.init 10 client_test |> Array.to_list |> Lwt.join) | |
94 | >>= fun () -> | |
95 | print_endline "Running single connection leak test"; | |
96 | repeat 1024 client_test >>= fun () -> | |
97 | Lwt.wakeup do_stop (); | |
98 | Lwt.return_unit | |
99 | ||
100 | let () = | |
101 | Lwt.async_exception_hook := ignore; | |
102 | Sys.(set_signal sigpipe Signal_ignore); | |
103 | Lwt_main.run | |
104 | (Lwt_unix.with_timeout 60. (fun () -> Lwt_unix.handle_unix_error perform ())); | |
105 | print_endline "OK" |
0 | (* | |
1 | * Copyright (c) 2016 Skylable Ltd. <info-copyright@skylable.com> | |
2 | * Copyright (c) 2016 Vincent Bernardoff <vb@luminar.eu.org> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | *) | |
16 | ||
17 | open Lwt.Infix | |
18 | ||
19 | let port = | |
20 | Random.self_init (); | |
21 | 16_384 + Random.int 10_000 | |
22 | ||
23 | let config = | |
24 | ( `Crt_file_path "server.pem", | |
25 | `Key_file_path "server.key", | |
26 | `No_password, | |
27 | `Port port ) | |
28 | ||
29 | let rec repeat n f = | |
30 | if n = 0 then Lwt.return_unit else f () >>= fun () -> repeat (n - 1) f | |
31 | ||
32 | let skip _ = () | |
33 | ||
34 | let perform () = | |
35 | let stop, do_stop = Lwt.wait () in | |
36 | Conduit_lwt_unix.init ~src:"::1" () >>= fun ctx -> | |
37 | let serve () = | |
38 | Conduit_lwt_unix.serve ~stop ~ctx ~mode:(`TLS config) ~on_exn:skip | |
39 | (fun _flow ic oc -> | |
40 | Lwt_log.notice "Server: Callback started." >>= fun () -> | |
41 | Lwt_io.read ~count:5 ic >>= fun msg -> | |
42 | Lwt_log.notice_f "Server: read %s." msg >>= fun () -> | |
43 | Lwt_io.write oc "foo") | |
44 | in | |
45 | let client_test () = | |
46 | (* connect using low-level operations to check what happens if client closes connection | |
47 | without calling ssl_shutdown (e.g. TCP connection is lost) *) | |
48 | let client = | |
49 | `TLS (`Hostname "", `IP Ipaddr.(V6 V6.localhost), `Port port) | |
50 | in | |
51 | Conduit_lwt_unix.(connect ~ctx:default_ctx client) | |
52 | >>= fun (_flow, ic, oc) -> | |
53 | Lwt_log.notice "Connected!" >>= fun () -> | |
54 | Lwt_io.write oc "hello" >>= fun () -> | |
55 | Lwt_log.notice "Written hello." >>= fun () -> | |
56 | Lwt_io.read ic ~count:3 >>= fun msg -> | |
57 | Lwt_log.notice_f "Got correct msg (%s), disconnecting." msg >>= fun () -> | |
58 | Lwt_io.close ic | |
59 | in | |
60 | Lwt.async serve; | |
61 | Lwt_unix.sleep 1. >>= fun () -> | |
62 | Lwt_log.notice_f "Server running on port %d" port >>= fun () -> | |
63 | repeat 10 client_test >>= fun () -> | |
64 | Lwt.wakeup do_stop (); | |
65 | Lwt.return_unit | |
66 | ||
67 | let () = | |
68 | Lwt.async_exception_hook := ignore; | |
69 | Sys.(set_signal sigpipe Signal_ignore); | |
70 | Lwt_main.run (Lwt_unix.handle_unix_error perform ()); | |
71 | print_endline "OK" |
0 | (executables | |
1 | (libraries lwt_ssl ssl conduit-lwt-unix lwt_log) | |
2 | (names cdtest cdtest_tls exit_test)) | |
3 | ||
4 | (rule | |
5 | (alias runtest) | |
6 | (deps ./cdtest.exe ./cdtest_tls.exe ./exit_test.exe) | |
7 | (package conduit-lwt-unix) | |
8 | (action | |
9 | (run echo skip))) |
0 | (* | |
1 | * Copyright (c) 2016 Vincent Bernardoff <vb@luminar.eu.org> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | *) | |
15 | ||
16 | open Lwt.Infix | |
17 | ||
18 | let skip _ = () | |
19 | ||
20 | let perform () = | |
21 | let stop, do_stop = Lwt.wait () in | |
22 | Conduit_lwt_unix.init ~src:"::1" () >>= fun ctx -> | |
23 | let serve () = | |
24 | let callback _flow ic oc = | |
25 | Lwt_io.read ~count:5 ic >>= fun _msg -> Lwt_io.write oc "foo" | |
26 | in | |
27 | Conduit_lwt_unix.serve ~stop ~ctx | |
28 | ~mode:(`TCP (`Port 8080)) | |
29 | ~on_exn:skip callback | |
30 | in | |
31 | let handle = serve () in | |
32 | Lwt.async (fun () -> Lwt_unix.sleep 0.2 >|= Lwt.wakeup do_stop); | |
33 | handle | |
34 | ||
35 | let () = | |
36 | Lwt.async_exception_hook := ignore; | |
37 | let t_start = Unix.gettimeofday () in | |
38 | Lwt_main.run (Lwt_unix.handle_unix_error perform ()); | |
39 | let t_end = Unix.gettimeofday () in | |
40 | if t_end -. t_start > 0.15 then Printf.printf "OK %.3f\n" (t_end -. t_start) | |
41 | else Printf.printf "FAILED %.3f (must be > 0.2)" (t_end -. t_start) |
0 | #!/bin/sh | |
1 | set -e | |
2 | set -o nounset | |
3 | cat >server.conf <<EOF | |
4 | [ req ] | |
5 | default_bits = 2048 | |
6 | distinguished_name = req_distinguished_name | |
7 | prompt = no | |
8 | encrypt_key = no | |
9 | x509_extensions = v3_ca | |
10 | ||
11 | [ req_distinguished_name ] | |
12 | CN = localhost | |
13 | ||
14 | [ CA_default ] | |
15 | copy_extensions = copy | |
16 | ||
17 | [ alternate_names ] | |
18 | DNS.2=localhost | |
19 | ||
20 | [ v3_ca ] | |
21 | subjectAltName=@alternate_names | |
22 | subjectKeyIdentifier=hash | |
23 | authorityKeyIdentifier=keyid:always,issuer:always | |
24 | basicConstraints = critical,CA:true | |
25 | keyUsage=keyCertSign,cRLSign,digitalSignature,keyEncipherment,nonRepudiation | |
26 | EOF | |
27 | ||
28 | openssl req -days 1 -x509 -config server.conf -new -keyout server.key -out server.pem |
0 | [ req ] | |
1 | default_bits = 2048 | |
2 | distinguished_name = req_distinguished_name | |
3 | prompt = no | |
4 | encrypt_key = no | |
5 | x509_extensions = v3_ca | |
6 | ||
7 | [ req_distinguished_name ] | |
8 | CN = localhost | |
9 | ||
10 | [ CA_default ] | |
11 | copy_extensions = copy | |
12 | ||
13 | [ alternate_names ] | |
14 | DNS.2=localhost | |
15 | ||
16 | [ v3_ca ] | |
17 | subjectAltName=@alternate_names | |
18 | subjectKeyIdentifier=hash | |
19 | authorityKeyIdentifier=keyid:always,issuer:always | |
20 | basicConstraints = critical,CA:true | |
21 | keyUsage=keyCertSign,cRLSign,digitalSignature,keyEncipherment,nonRepudiation |
0 | all: http-server-unix http-fetch-unix vchan-client-xen vchan-server-xen | |
1 | @ | |
2 | ||
3 | http-server-unix: | |
4 | cd http-server && mirage configure -t unix && make | |
5 | ||
6 | http-fetch-unix: | |
7 | cd http-fetch && mirage configure -t unix && make | |
8 | ||
9 | vchan-client-xen: | |
10 | cd vchan && mirage configure -f config_client.ml -t xen && make | |
11 | ||
12 | vchan-server-xen: | |
13 | cd vchan && mirage configure -f config_server.ml -t xen && make |
0 | open Mirage | |
1 | ||
2 | let client = | |
3 | foreign ~deps:[ abstract nocrypto ] "Unikernel.Client" | |
4 | @@ console | |
5 | @-> stackv4 | |
6 | @-> job | |
7 | ||
8 | let () = | |
9 | register | |
10 | ~libraries:[ "conduit.lwt"; "conduit.mirage"; "dns.mirage" ] | |
11 | ~packages:[ "mirage-dns"; "conduit" ] | |
12 | "conduit-client" | |
13 | [ client $ default_console $ generic_stackv4 default_console tap0 ] |
0 | open Lwt.Infix | |
1 | open Mirage_types_lwt | |
2 | open Printf | |
3 | ||
4 | let red fmt = sprintf ("\027[31m" ^^ fmt ^^ "\027[m") | |
5 | let green fmt = sprintf ("\027[32m" ^^ fmt ^^ "\027[m") | |
6 | let yellow fmt = sprintf ("\027[33m" ^^ fmt ^^ "\027[m") | |
7 | let blue fmt = sprintf ("\027[36m" ^^ fmt ^^ "\027[m") | |
8 | let domain = "anil.recoil.org" | |
9 | let uri = Uri.of_string "http://anil.recoil.org" | |
10 | let ns = "8.8.8.8" | |
11 | ||
12 | module Client (C : CONSOLE) (S : STACKV4) = struct | |
13 | module DNS = Dns_resolver_mirage.Make (OS.Time) (S) | |
14 | module RES = Resolver_mirage.Make (DNS) | |
15 | ||
16 | let start c stack _ = | |
17 | C.log_s c (sprintf "Resolving in 3s using DNS server %s" ns) >>= fun () -> | |
18 | OS.Time.sleep 3.0 >>= fun () -> | |
19 | let res = Resolver_lwt.init () in | |
20 | RES.register ~ns:(Ipaddr.V4.of_string_exn ns) ~stack res; | |
21 | Resolver_lwt.resolve_uri ~uri res >>= fun endp -> | |
22 | mk_conduit stack >>= fun conduit -> | |
23 | Conduit_mirage.client endp >>= fun client -> | |
24 | let endp = Sexplib.Sexp.to_string_hum (Conduit.sexp_of_endp endp) in | |
25 | C.log_s c endp >>= fun () -> | |
26 | Conduit_mirage.connect conduit client >>= fun flow -> | |
27 | let page = Io_page.(to_cstruct (get 1)) in | |
28 | let http_get = "GET / HTTP/1.1\nHost: anil.recoil.org\n\n" in | |
29 | Cstruct.blit_from_string http_get 0 page 0 (String.length http_get); | |
30 | let buf = Cstruct.sub page 0 (String.length http_get) in | |
31 | Conduit_mirage.Flow.write flow buf >>= function | |
32 | | `Eof -> C.log_s c "EOF on write" | |
33 | | `Error _ -> C.log_s c "ERR on write" | |
34 | | `Ok buf -> ( | |
35 | Conduit_mirage.Flow.read flow >>= function | |
36 | | `Eof -> C.log_s c "EOF" | |
37 | | `Error _ -> C.log_s c "ERR" | |
38 | | `Ok buf -> C.log_s c (sprintf "OK\n%s\n" (Cstruct.to_string buf))) | |
39 | end |
0 | open Mirage | |
1 | ||
2 | let client = | |
3 | foreign ~deps:[ abstract nocrypto ] "Unikernel.Client" | |
4 | @@ console | |
5 | @-> stackv4 | |
6 | @-> job | |
7 | ||
8 | let () = | |
9 | register | |
10 | ~libraries:[ "conduit.lwt"; "conduit.mirage"; "vchan" ] | |
11 | "http-server" | |
12 | [ client $ default_console $ generic_stackv4 default_console tap0 ] |
0 | open Lwt.Infix | |
1 | open Mirage_types_lwt | |
2 | open Printf | |
3 | ||
4 | let red fmt = sprintf ("\027[31m" ^^ fmt ^^ "\027[m") | |
5 | let green fmt = sprintf ("\027[32m" ^^ fmt ^^ "\027[m") | |
6 | let yellow fmt = sprintf ("\027[33m" ^^ fmt ^^ "\027[m") | |
7 | let blue fmt = sprintf ("\027[36m" ^^ fmt ^^ "\027[m") | |
8 | let uri = Uri.of_string "http://localhost" | |
9 | ||
10 | module Client (C : CONSOLE) (S : STACKV4) = struct | |
11 | let mk_conduit s = | |
12 | let stackv4 = Conduit_mirage.stackv4 (module S) in | |
13 | Conduit_mirage.with_tcp Conduit_mirage.empty stackv4 s | |
14 | ||
15 | let callback c _flow = C.log_s c "Connection!" | |
16 | ||
17 | let start c stack _ = | |
18 | let r = Resolver_mirage.localhost in | |
19 | mk_conduit stack >>= fun conduit -> | |
20 | Resolver_lwt.resolve_uri ~uri r >>= fun endp -> | |
21 | Conduit_mirage.server endp >>= fun mode -> | |
22 | let endp = Sexplib.Sexp.to_string_hum (Conduit.sexp_of_endp endp) in | |
23 | C.log_s c endp >>= fun () -> Conduit_mirage.listen conduit mode (callback c) | |
24 | end |
0 | open Lwt.Infix | |
1 | ||
2 | let client : Conduit_mirage.client = | |
3 | `TCP (Ipaddr.of_string_exn "127.0.0.1", 12345) | |
4 | ||
5 | let server : Conduit_mirage.server = `TCP 12345 | |
6 | ||
7 | module TCP = Conduit_mirage.TCP (Tcpip_stack_socket.V4V6) | |
8 | ||
9 | let tcp () = | |
10 | let ipv4_only = false and ipv6_only = false in | |
11 | Udpv4v6_socket.connect ~ipv4_only ~ipv6_only Ipaddr.V4.Prefix.global None | |
12 | >>= fun udp -> | |
13 | Tcpv4v6_socket.connect ~ipv4_only ~ipv6_only Ipaddr.V4.Prefix.global None | |
14 | >>= fun tcp -> Tcpip_stack_socket.V4V6.connect udp tcp | |
15 | ||
16 | let _client () = tcp () >>= fun t -> TCP.connect t client | |
17 | ||
18 | let _server () = | |
19 | tcp () >>= fun t -> TCP.listen t server (fun _flow -> Lwt.return ()) |
0 | open Mirage | |
1 | ||
2 | let main = foreign "Unikernel.Client" (time @-> job) | |
3 | ||
4 | let () = | |
5 | register | |
6 | ~libraries:[ "conduit.mirage"; "vchan.xen" ] | |
7 | ~packages:[ "conduit"; "vchan" ] "vchan_client" [ main $ default_time ] |
0 | open Mirage | |
1 | ||
2 | let main = foreign "Unikernel.Server" (time @-> job) | |
3 | ||
4 | let () = | |
5 | register | |
6 | ~libraries:[ "conduit.mirage"; "vchan.xen" ] | |
7 | ~packages:[ "conduit"; "vchan" ] "vchan_server" [ main $ default_time ] |
0 | #!/bin/sh -e | |
1 | ||
2 | echo Setting up a /conduit path in xenstore | |
3 | xenstore-rm /conduit | |
4 | xenstore-write /conduit "" | |
5 | xenstore-chmod /conduit b0 |
0 | #!/bin/sh | |
1 | ||
2 | sudo xl destroy vchan_server || true | |
3 | sudo xl destroy vchan_client || true | |
4 | sudo ./init-xenstore.sh | |
5 | ./build.sh |
0 | open Lwt.Infix | |
1 | open Printf | |
2 | ||
3 | let conduit = Conduit_mirage.empty | |
4 | let vchan = Conduit_mirage.vchan (module Vchan_xen) | |
5 | let xs = Conduit_mirage.xs (module OS.Xs) | |
6 | ||
7 | module Server (Time : Mirage_types_lwt.TIME) = struct | |
8 | let server_src = Logs.Src.create "server" ~doc:"vchan server" | |
9 | ||
10 | module Log = (val Logs.src_log server_src : Logs.LOG) | |
11 | ||
12 | let start _ = | |
13 | Conduit_mirage.with_vchan conduit xs vchan "foo_server" >>= fun t -> | |
14 | Log.info (fun f -> f "Server initialising"); | |
15 | let callback flow = | |
16 | Log.info (fun f -> f "Got a new flow!"); | |
17 | let rec loop () = | |
18 | Conduit_mirage.Flow.read flow >>= fun res -> | |
19 | match res with | |
20 | | `Ok buf -> | |
21 | Log.info (fun f -> f "Received: %s" @@ Cstruct.to_string buf); | |
22 | loop () | |
23 | | `Eof -> | |
24 | Log.info (fun f -> f "End of transmission!"); | |
25 | Lwt.return_unit | |
26 | | `Error e -> | |
27 | Log.warn (fun f -> f "Error reading the vchan flow!"); | |
28 | Lwt.return_unit | |
29 | in | |
30 | loop () | |
31 | in | |
32 | Conduit_mirage.listen t (`Vchan `Domain_socket) callback | |
33 | end | |
34 | ||
35 | module Client (Time : Mirage_types_lwt.TIME) = struct | |
36 | let client_src = Logs.Src.create "client" ~doc:"vchan client" | |
37 | ||
38 | module Log = (val Logs.src_log client_src : Logs.LOG) | |
39 | ||
40 | let conduit = Conduit_mirage.empty | |
41 | ||
42 | let start _t = | |
43 | Time.sleep 2.0 >>= fun () -> | |
44 | Conduit_mirage.with_vchan conduit xs vchan "foo_client" >>= fun t -> | |
45 | Log.info (fun f -> f "Connecting..."); | |
46 | let client = | |
47 | match Vchan.Port.of_string "flibble" with | |
48 | | `Ok port -> `Vchan (`Domain_socket ("foo_server", port)) | |
49 | | `Error e -> failwith e | |
50 | in | |
51 | Conduit_mirage.connect t client >>= fun flow -> | |
52 | ( Conduit_mirage.sexp_of_client client | |
53 | |> Sexplib.Sexp.to_string_hum | |
54 | |> sprintf "Endpoint: %s" | |
55 | |> fun s -> Log.info (fun f -> f "%s" s) ); | |
56 | ||
57 | Log.info (fun f -> f "Client connected"); | |
58 | let rec write num = | |
59 | let buf = Io_page.(to_cstruct (get 1)) in | |
60 | let s = sprintf "num is %d" num in | |
61 | let len = String.length s in | |
62 | Cstruct.blit_from_string s 0 buf 0 len; | |
63 | let buf = Cstruct.sub buf 0 len in | |
64 | Conduit_mirage.Flow.write flow buf >>= function | |
65 | | `Eof -> | |
66 | Log.info (fun f -> f "EOF"); | |
67 | Time.sleep 5. | |
68 | | `Error _ -> | |
69 | Log.warn (fun f -> f "ERR"); | |
70 | Time.sleep 5. | |
71 | | `Ok () -> Time.sleep 0.1 >>= fun () -> write (num + 1) | |
72 | in | |
73 | write 0 | |
74 | end |