1 import Base 2 import ./proxy.bend as P 3 4 # Net 5 # === 6 # 7 # A TLS connection is a Socket handle wrapped in Tls, so code typed against 8 # Tls can never hand it to the raw TCP effects. The effects (effs/tls.c on 9 # native builds, effs/tls.js on the JavaScript lane) only dial, handshake 10 # and move bytes; every decision about proxies is proxy.bend's, here. 11 12 type Tls is Type: 13 Tls{sock: Socket} 14 15 type Bytes is Data: 16 Bytes{list: List<&2, U32>} 17 18 def Res(-A: Type) -> Type: 19 Result<&1, &1, U32 & String, A> 20 21 # Effects 22 # ------- 23 24 # A plain TCP connection: host may be a name (resolved, IPv4 or IPv6) or an 25 # IP literal; ms bounds the connect (0 for 30s). Until Tls.secure, Tls.send 26 # and Tls.recv carry its bytes as they are. 27 def Tls.dial(host: String, port: U32, ms: U32) -> IO(Res(Socket)): 28 import "./effs/tls.c" 29 import "./effs/tls.js" 30 31 # Handshakes on a connection from Tls.dial and verifies the server as host 32 # (a name, or an IP literal), always: ca is a PEM bundle of trusted roots 33 # ("" for the system's), cert and key a PEM client certificate and key ("" 34 # for none), ms the handshake's deadline (0 for 30s). TLS 1.2 or later. A 35 # failure closes the connection. 36 def Tls.secure(sock: Socket, host: String, ca: String, cert: String, key: String, ms: U32) -> 37 IO(Res(Socket)): 38 import "./effs/tls.c" 39 import "./effs/tls.js" 40 41 # Sends text as UTF-8; ms bounds the whole send (0: no deadline). 42 def Tls.send(sock: Socket, text: String, ms: U32) -> IO(Socket & Res(Unit)): 43 import "./effs/tls.c" 44 import "./effs/tls.js" 45 46 # Sends raw bytes (each 0..255); ms bounds the whole send (0: no deadline). 47 def Tls.send_bytes(sock: Socket, bytes: List<&2, U32>, ms: U32) -> IO(Socket & Res(Unit)): 48 import "./effs/tls.c" 49 import "./effs/tls.js" 50 51 # Up to max bytes (0: 16KiB); an empty list is the end of the stream. ms 52 # bounds the wait (0: none), past which it fails with ETIMEDOUT (60). 53 def Tls.recv(sock: Socket, max: U32, ms: U32) -> IO(Socket & Res(List<&2, U32>)): 54 import "./effs/tls.c" 55 import "./effs/tls.js" 56 57 def Tls.close(sock: Socket) -> IO(Unit): 58 import "./effs/tls.c" 59 import "./effs/tls.js" 60 61 # Opening 62 # ------- 63 64 def Tls.fail(s: Socket, e: U32 & String) -> IO(Res(Socket)): 65 IO.bind(Unit, Res(Socket), Tls.close(s), u => IO.pure(Res(Socket), Fail{e})) 66 67 # What the CONNECT exchange just saw. 68 type TunEv is Type: 69 TGot{sock: Socket, r: Res(List<&2, U32>), st: P.Tunnel} 70 TStep{sock: Socket, st: P.Tunnel} 71 72 def TunEv.got(p: Socket & Res(List<&2, U32>), st: P.Tunnel) -> TunEv: 73 match p: 74 case (s, r): 75 TGot{s, r, st} 76 77 # Reads the proxy's answer to CONNECT; once it opens the tunnel, secures 78 # the connection end to end with the API server. 79 def Tls.tunnel(fuel: Nat, ev: TunEv, +host: String, ca: String, cert: String, key: String, 80 +ms: U32) -> IO(Res(Socket)): 81 match fuel ev: 82 case 0n TGot{s, r, st}: 83 Tls.fail(s, (0, "proxy: answer too long")) 84 case 0n TStep{s, st}: 85 Tls.fail(s, (0, "proxy: answer too long")) 86 case 1n+f TGot{s, Done{bs}, st}: 87 Tls.tunnel(f, TStep{s, P.Tunnel.feed(st, bs)}, host, ca, cert, key, ms) 88 case 1n+f TGot{s, Fail{(c, m)}, st}: 89 Tls.fail(s, (c, "proxy: " ++ m)) 90 case 1n+f TStep{s, P.TMore{h}}: 91 IO.bind(Socket & Res(List<&2, U32>), Res(Socket), Tls.recv(s, 4096, ms), 92 p => Tls.tunnel(f, TunEv.got(p, P.TMore{h}), host, ca, cert, key, ms)) 93 case 1n+f TStep{s, P.TOpen{}}: 94 Tls.secure(s, host, ca, cert, key, ms) 95 case 1n+f TStep{s, P.TFail{why}}: 96 Tls.fail(s, (0, why)) 97 98 def Tls.open.sent(p: Socket & Res(Unit), +host: String, ca: String, cert: String, key: String, 99 +ms: U32) -> IO(Res(Socket)): 100 match p: 101 case (s, Done{u}): 102 Tls.tunnel(4294967295n, TStep{s, P.Tunnel.init()}, host, ca, cert, key, ms) 103 case (s, Fail{(c, m)}): 104 Tls.fail(s, (c, "proxy: " ++ m)) 105 106 def Tls.open.proxied(d: Res(Socket), req: String, +host: String, ca: String, cert: String, 107 key: String, +ms: U32) -> IO(Res(Socket)): 108 match d: 109 case Done{s}: 110 IO.bind(Socket & Res(Unit), Res(Socket), Tls.send(s, req, ms), 111 p => Tls.open.sent(p, host, ca, cert, key, ms)) 112 case Fail{(c, m)}: 113 IO.pure(Res(Socket), Fail{(c, "proxy: " ++ m)}) 114 115 def Tls.open.direct(d: Res(Socket), +host: String, ca: String, cert: String, key: String, 116 +ms: U32) -> IO(Res(Socket)): 117 match d: 118 case Done{s}: 119 Tls.secure(s, host, ca, cert, key, ms) 120 case Fail{e}: 121 IO.pure(Res(Socket), Fail{e}) 122 123 def Tls.open.route(r: P.Route, +host: String, +port: U32, ca: String, cert: String, 124 key: String, +ms: U32) -> IO(Res(Socket)): 125 match r: 126 case P.Direct{}: 127 IO.bind(Res(Socket), Res(Socket), Tls.dial(host, port, ms), 128 d => Tls.open.direct(d, host, ca, cert, key, ms)) 129 case P.Via{P.Proxy{ph, pp, auth}}: 130 IO.bind(Res(Socket), Res(Socket), Tls.dial(ph, pp, ms), 131 d => Tls.open.proxied(d, P.Proxy.connect(host, port, auth), host, ca, cert, key, ms)) 132 case P.NoRoute{why}: 133 IO.pure(Res(Socket), Fail{(22, why)}) 134 135 def Tls.env.or(r: Res(String), alt: Res(String)) -> String: 136 match r alt: 137 case Done{SCon{c, t}} a: 138 SCon{c, t} 139 case _ Done{v}: 140 v 141 case _ _: 142 "" 143 144 # The first of two environment variables that is set and not empty, as Go 145 # reads HTTPS_PROXY then https_proxy. 146 def Tls.env(name: String, alt: String) -> IO(String): 147 IO.bind(Res(String), String, IO.get_env(name), 148 r => IO.bind(Res(String), String, IO.get_env(alt), a => IO.pure(String, Tls.env.or(r, a)))) 149 150 # Opens a verified TLS connection to host:port (see Tls.secure), through 151 # the CONNECT tunnel of HTTPS_PROXY unless NO_PROXY or a loopback host 152 # says otherwise (proxy.bend). 153 def Tls.open(+host: String, +port: U32, ca: String, cert: String, key: String, +ms: U32) -> 154 IO(Res(Socket)): 155 IO.bind(String, Res(Socket), Tls.env("HTTPS_PROXY", "https_proxy"), 156 url => IO.bind(String, Res(Socket), Tls.env("NO_PROXY", "no_proxy"), 157 no => Tls.open.route(P.Proxy.route(url, no, host, port), host, port, ca, cert, key, ms)))