@amritk/nish-x86_64-linux 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +43 -34
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +117 -8
- package/runtime/nish.h +30 -7
- package/runtime/nish.mjs +71 -7
- package/runtime/runtime-host.c +43 -24
- package/runtime/runtime-net.c +57 -10
- package/runtime/runtime-os.c +62 -20
- package/runtime/runtime-wasm.c +1 -0
- package/runtime/runtime.c +46 -8
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +1 -3
- package/std/README.md +166 -35
- package/std/crypto/aes.ts +51 -8
- package/std/crypto/base64url.ts +25 -12
- package/std/crypto/chacha20poly1305.ts +43 -29
- package/std/crypto/ct.ts +6 -1
- package/std/crypto/hkdf.ts +364 -3
- package/std/crypto/hmac.ts +307 -7
- package/std/crypto/p256.ts +148 -27
- package/std/crypto/sha1.ts +147 -0
- package/std/crypto/sha256.ts +3 -2
- package/std/crypto/x25519.ts +88 -36
- package/std/crypto/x509.ts +130 -47
- package/std/net/hpack.ts +1094 -0
- package/std/net/http-fields.ts +604 -0
- package/std/net/http1-server.ts +1389 -0
- package/std/net/http1.ts +1492 -0
- package/std/net/http2-frame.ts +745 -0
- package/std/net/http2-tls.ts +216 -0
- package/std/net/http2.ts +1433 -0
- package/std/net/http3-frame.ts +457 -0
- package/std/net/http3-server.ts +905 -0
- package/std/net/http3.ts +2145 -0
- package/std/net/qpack.ts +1163 -0
- package/std/net/quic-conn-ack.ts +206 -0
- package/std/net/quic-conn-cid.ts +425 -0
- package/std/net/quic-conn-params.ts +392 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +653 -0
- package/std/net/quic-packet.ts +1474 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1603 -0
- package/std/net/quic.ts +3214 -0
- package/std/net/tls/codec.ts +1089 -0
- package/std/net/tls/record-server.ts +1173 -0
- package/std/net/tls/record.ts +621 -0
- package/std/net/tls/schedule.ts +429 -0
- package/std/net/tls-tcp.ts +410 -0
- package/std/net/tls.ts +1368 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- package/std/secret.ts +62 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nish/net/tls-tcp` — TLS 1.3 over TCP (WP34 T2): a pool of server
|
|
3
|
+
* connections on `nish:net`, each a `TlsRecordServer` driving a `TlsServer`,
|
|
4
|
+
* for a program that owns its readiness loop.
|
|
5
|
+
*
|
|
6
|
+
* const listener = tcpListen("0.0.0.0", 8443, 128);
|
|
7
|
+
* const tls = new TlsTcpServer(config, listener, 64);
|
|
8
|
+
* pollAdd(loop, listener, 1, LISTENER);
|
|
9
|
+
* // when the listener is readable:
|
|
10
|
+
* const slot: i32 = tls.accept(random32(), random32());
|
|
11
|
+
* if (slot >= 0) { pollAdd(loop, tls.fd(slot), 1, slot); }
|
|
12
|
+
* // when a connection's token comes back:
|
|
13
|
+
* let wants: i32 = (events & 5) !== 0 ? tls.readable(slot) : tls.writable(slot);
|
|
14
|
+
* if ((wants & TLS_RECORD_SIGN) !== 0) { wants = tls.signP256(slot, leafKey); }
|
|
15
|
+
* if ((wants & TLS_RECORD_DATA) !== 0) { tls.read(slot, buf, 0, buf.length); ... }
|
|
16
|
+
* if ((wants & TLS_RECORD_DONE) !== 0) { tls.close(slot); }
|
|
17
|
+
* else { pollModify(loop, tls.fd(slot), wants & 3, slot); }
|
|
18
|
+
*
|
|
19
|
+
* **The program is the loop.** A function is not a value, so there are no
|
|
20
|
+
* callbacks: every call answers what the connection wants next as the
|
|
21
|
+
* `TLS_RECORD_*` bits of `nish/net/tls/record-server`, and the low two bits
|
|
22
|
+
* are exactly the events `pollModify` takes — readable, writable — so the
|
|
23
|
+
* program re-arms the descriptor with them after each call. The loop stays
|
|
24
|
+
* level-triggered and never spins: a connection whose application data the
|
|
25
|
+
* program has not read stops asking to be read, and one with nothing to send
|
|
26
|
+
* stops asking to be written. An event with the hang-up bit (4) goes to
|
|
27
|
+
* `readable`, which is where a closed or failed socket is noticed.
|
|
28
|
+
*
|
|
29
|
+
* **Slots, sized at start-up.** The constructor allocates every connection's
|
|
30
|
+
* buffers — about 136 KB of resident memory each, the largest record twice
|
|
31
|
+
* over in each direction and the handshake's own state (TLS-3) — and `accept` hands the next connection a free slot and
|
|
32
|
+
* reuses it once the program `close`s it (WP34 N9). When every slot is busy
|
|
33
|
+
* a new connection is accepted and closed at once, so the listener does not
|
|
34
|
+
* stay readable; the program sees `TLS_TCP_POOL_FULL`. Each slot keeps its
|
|
35
|
+
* own `TlsServer`, which `accept` restarts for the connection, so neither the
|
|
36
|
+
* handshake nor application data allocates anything that outlives a call:
|
|
37
|
+
* a thousand connections through one slot leave `Arena.mark()` where the
|
|
38
|
+
* first left it, under every suite (TLS-3 in `docs/security/tls.md`,
|
|
39
|
+
* `tests/link/net_tls_memory`).
|
|
40
|
+
*
|
|
41
|
+
* **Randomness is the caller's.** `accept` takes the 32-byte server random and
|
|
42
|
+
* the 32-byte x25519 private key the handshake uses, as `TlsServer` does, so a
|
|
43
|
+
* test replays RFC 8448's trace and a server draws them with
|
|
44
|
+
* `crypto.getRandomValues`. `accept` copies both into the slot and wipes the
|
|
45
|
+
* caller's key array, so one buffer of each refilled for every `accept` is
|
|
46
|
+
* safe, and refuses either when it is not 32 bytes before taking a
|
|
47
|
+
* connection; the slot's copy of the key is wiped once the ServerHello is
|
|
48
|
+
* written, when the handshake fails, or when the slot is closed. **Signing is the caller's**, as `TlsServer`'s is: `signP256` signs
|
|
49
|
+
* the CertificateVerify with a P-256 key the caller holds as a `Secret` and
|
|
50
|
+
* borrows for the call, and `signatureInput` / `sign` hand the input out and
|
|
51
|
+
* take any signature back.
|
|
52
|
+
*
|
|
53
|
+
* `read`, `write` and `close` answer as `nish:net`'s calls do — a count, `0`
|
|
54
|
+
* at the end of the stream, `-11` to wait — so a program written against a
|
|
55
|
+
* plain socket moves over with the `slot` in place of the descriptor.
|
|
56
|
+
* `close` frees the slot at once; `shutdown` sends the `close_notify` as the
|
|
57
|
+
* socket takes it, and the slot is done when its interest says so.
|
|
58
|
+
*
|
|
59
|
+
* Written from RFC 8446 and docs/LANGUAGE.md's `nish:net`, not ported from
|
|
60
|
+
* another implementation.
|
|
61
|
+
*/
|
|
62
|
+
import { Secret } from "nish:secret"
|
|
63
|
+
import { netClose, netRead, netWrite, tcpAccept } from "nish:net"
|
|
64
|
+
import { TlsServer, TlsServerConfig, tlsSignEcdsaP256 } from "nish/net/tls"
|
|
65
|
+
import { TLS_ALERT_INTERNAL_ERROR } from "nish/net/tls/codec"
|
|
66
|
+
import { TLS_MAX_RECORD } from "nish/net/tls/record"
|
|
67
|
+
import {
|
|
68
|
+
TLS_RECORD_AGAIN,
|
|
69
|
+
TLS_RECORD_DONE,
|
|
70
|
+
TLS_RECORD_INVALID,
|
|
71
|
+
TLS_RECORD_STATE_FAILED,
|
|
72
|
+
TlsRecordServer,
|
|
73
|
+
} from "nish/net/tls/record-server"
|
|
74
|
+
|
|
75
|
+
/** What `accept` answers when every slot is busy: the connection was accepted and closed. Linux's ENOBUFS. */
|
|
76
|
+
export const TLS_TCP_POOL_FULL: i32 = -105
|
|
77
|
+
|
|
78
|
+
/** A free slot's descriptor. */
|
|
79
|
+
const TLS_TCP_FREE: i32 = -1
|
|
80
|
+
|
|
81
|
+
/** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
|
|
82
|
+
const TLS_TCP_FROM: i32 = 0
|
|
83
|
+
|
|
84
|
+
/** The length of an x25519 private key and of a server random: what `TlsServer` takes for each. */
|
|
85
|
+
const TLS_TCP_KEY_SIZE: i32 = 32
|
|
86
|
+
|
|
87
|
+
/** The address form's length, from `nish:net`. */
|
|
88
|
+
const TLS_TCP_ADDRESS_SIZE: i32 = 18
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A pool of TLS 1.3 server connections on one listening socket. `slot`s are
|
|
92
|
+
* the indices `accept` answers, from 0 to the pool's size; a slot is the
|
|
93
|
+
* program's from `accept` until it calls `close`, and its descriptor is
|
|
94
|
+
* `fd(slot)`.
|
|
95
|
+
*/
|
|
96
|
+
export class TlsTcpServer {
|
|
97
|
+
config: TlsServerConfig
|
|
98
|
+
/** The connections, one per slot, made once. */
|
|
99
|
+
connections: TlsRecordServer[]
|
|
100
|
+
/** Each slot's descriptor, or -1 while the slot is free. */
|
|
101
|
+
fds: i32[]
|
|
102
|
+
/** Where the socket is read into before a connection takes the bytes: the largest record. */
|
|
103
|
+
scratch: u8[]
|
|
104
|
+
/** The last accepted peer's address, in `nish:net`'s 18-byte form. */
|
|
105
|
+
peer: u8[]
|
|
106
|
+
/**
|
|
107
|
+
* Each slot's own copy of its connection's ephemeral key, so that wiping it
|
|
108
|
+
* can never reach an array the program has refilled for the next one.
|
|
109
|
+
*/
|
|
110
|
+
keys: u8[][]
|
|
111
|
+
/**
|
|
112
|
+
* Each slot's own copy of its connection's server random, for the same
|
|
113
|
+
* reason: `TlsServer` keeps the array it is given and writes it into the
|
|
114
|
+
* ServerHello only once the ClientHello arrives.
|
|
115
|
+
*/
|
|
116
|
+
randoms: u8[][]
|
|
117
|
+
/** The listening socket, which the program made with `tcpListen` and still owns. */
|
|
118
|
+
listener: i32 = -1
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A pool of `size` slots, at least one, serving `config` on `listener`, a
|
|
122
|
+
* socket `tcpListen` made. Every slot's buffers are allocated here.
|
|
123
|
+
*/
|
|
124
|
+
constructor(config: TlsServerConfig, listener: i32, size: i32) {
|
|
125
|
+
this.config = config
|
|
126
|
+
this.listener = listener
|
|
127
|
+
this.connections = []
|
|
128
|
+
this.fds = []
|
|
129
|
+
this.keys = []
|
|
130
|
+
this.randoms = []
|
|
131
|
+
const none: u8[] = []
|
|
132
|
+
const slots: i32 = size < 1 ? 1 : size
|
|
133
|
+
for (let k: i32 = 0; k < slots; k++) {
|
|
134
|
+
// Each slot's own handshake, refused by `TlsServer` for its empty
|
|
135
|
+
// randomness, so the slot starts out failed until `accept` restarts it
|
|
136
|
+
// for a connection; every later connection reuses its buffers.
|
|
137
|
+
this.connections.push(new TlsRecordServer(new TlsServer(config, none, none)))
|
|
138
|
+
this.fds.push(TLS_TCP_FREE)
|
|
139
|
+
this.keys.push(new Array<u8>(TLS_TCP_KEY_SIZE))
|
|
140
|
+
this.randoms.push(new Array<u8>(TLS_TCP_KEY_SIZE))
|
|
141
|
+
}
|
|
142
|
+
this.scratch = new Array<u8>(TLS_MAX_RECORD)
|
|
143
|
+
this.peer = new Array<u8>(TLS_TCP_ADDRESS_SIZE)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** How many slots the pool has. */
|
|
147
|
+
size(): i32 {
|
|
148
|
+
return toI32(this.fds.length)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** How many slots hold a connection. */
|
|
152
|
+
busy(): i32 {
|
|
153
|
+
let n: i32 = 0
|
|
154
|
+
for (const fd of this.fds) {
|
|
155
|
+
if (fd >= 0) {
|
|
156
|
+
n = n + 1
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return n
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Whether `slot` is a slot of this pool that holds a connection. */
|
|
163
|
+
holds(slot: i32): boolean {
|
|
164
|
+
return slot >= 0 && slot < toI32(this.fds.length) && this.fds[slot] >= 0
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** The descriptor of `slot`, for `pollAdd`; -1 for a free or unknown slot. */
|
|
168
|
+
fd(slot: i32): i32 {
|
|
169
|
+
return this.holds(slot) ? this.fds[slot] : -1
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The connection in `slot`: its state, its handshake (`tls`: suite, ALPN,
|
|
174
|
+
* server name) and the alerts sent and received. A free slot answers the
|
|
175
|
+
* last connection it held.
|
|
176
|
+
*/
|
|
177
|
+
connection(slot: i32): TlsRecordServer {
|
|
178
|
+
const at: i32 = slot >= 0 && slot < toI32(this.connections.length) ? slot : 0
|
|
179
|
+
return this.connections[at]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Accepts the next waiting connection into a free slot and starts its
|
|
184
|
+
* handshake with `serverRandom` and `ephemeralPrivate` (32 bytes each),
|
|
185
|
+
* and answers the slot. The peer's address is in `peer`. Both arrays are
|
|
186
|
+
* copied into the slot, which reads them only when the ClientHello
|
|
187
|
+
* arrives, and the key's array is then wiped, so the program may refill
|
|
188
|
+
* one buffer of each for every call. Answers -11 when no connection is
|
|
189
|
+
* waiting, `TLS_TCP_POOL_FULL` when one was but every slot is busy (it is
|
|
190
|
+
* closed), or `tcpAccept`'s own failure; and `TLS_RECORD_INVALID` (-22),
|
|
191
|
+
* before any connection is taken, for a random or key that is not 32
|
|
192
|
+
* bytes. On any answer but a slot both arrays are left as they were.
|
|
193
|
+
*/
|
|
194
|
+
accept(serverRandom: u8[], ephemeralPrivate: u8[]): i32 {
|
|
195
|
+
if (
|
|
196
|
+
toI32(serverRandom.length) !== TLS_TCP_KEY_SIZE ||
|
|
197
|
+
toI32(ephemeralPrivate.length) !== TLS_TCP_KEY_SIZE
|
|
198
|
+
) {
|
|
199
|
+
return TLS_RECORD_INVALID
|
|
200
|
+
}
|
|
201
|
+
const fd: i32 = tcpAccept(this.listener, this.peer)
|
|
202
|
+
if (fd < 0) {
|
|
203
|
+
return fd
|
|
204
|
+
}
|
|
205
|
+
let slot: i32 = -1
|
|
206
|
+
for (let k: i32 = 0; k < toI32(this.fds.length); k++) {
|
|
207
|
+
if (this.fds[k] < 0) {
|
|
208
|
+
slot = k
|
|
209
|
+
break
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (slot < 0) {
|
|
213
|
+
netClose(fd)
|
|
214
|
+
return TLS_TCP_POOL_FULL
|
|
215
|
+
}
|
|
216
|
+
this.fds[slot] = fd
|
|
217
|
+
const random: u8[] = this.randoms[slot]
|
|
218
|
+
const key: u8[] = this.keys[slot]
|
|
219
|
+
for (
|
|
220
|
+
let k: i32 = 0;
|
|
221
|
+
k < TLS_TCP_KEY_SIZE && k < toI32(random.length) && k < toI32(serverRandom.length);
|
|
222
|
+
k++
|
|
223
|
+
) {
|
|
224
|
+
random[k] = serverRandom[k]
|
|
225
|
+
}
|
|
226
|
+
for (
|
|
227
|
+
let k: i32 = 0;
|
|
228
|
+
k < TLS_TCP_KEY_SIZE && k < toI32(key.length) && k < toI32(ephemeralPrivate.length);
|
|
229
|
+
k++
|
|
230
|
+
) {
|
|
231
|
+
key[k] = ephemeralPrivate[k]
|
|
232
|
+
}
|
|
233
|
+
secureZero(ephemeralPrivate)
|
|
234
|
+
const conn: TlsRecordServer = this.connections[slot]
|
|
235
|
+
conn.tls.restart(random, key)
|
|
236
|
+
conn.start(conn.tls)
|
|
237
|
+
return slot
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* The socket of `slot` is readable: reads it until it would block or the
|
|
242
|
+
* connection has no room, handing each read to the connection, then sends
|
|
243
|
+
* what that produced. Answers the connection's `interest`; a free slot
|
|
244
|
+
* answers `TLS_RECORD_DONE`.
|
|
245
|
+
*/
|
|
246
|
+
readable(slot: i32): i32 {
|
|
247
|
+
if (!this.holds(slot)) {
|
|
248
|
+
return TLS_RECORD_DONE
|
|
249
|
+
}
|
|
250
|
+
const fd: i32 = this.fds[slot]
|
|
251
|
+
const conn: TlsRecordServer = this.connections[slot]
|
|
252
|
+
while (true) {
|
|
253
|
+
const room: i32 = conn.space()
|
|
254
|
+
const want: i32 = room < toI32(this.scratch.length) ? room : toI32(this.scratch.length)
|
|
255
|
+
if (want <= 0 || conn.state === TLS_RECORD_STATE_FAILED || conn.peerClosed) {
|
|
256
|
+
break
|
|
257
|
+
}
|
|
258
|
+
const n: i32 = netRead(fd, this.scratch, TLS_TCP_FROM, want)
|
|
259
|
+
if (n <= 0) {
|
|
260
|
+
if (n === 0) {
|
|
261
|
+
conn.end()
|
|
262
|
+
} else if (n !== TLS_RECORD_AGAIN) {
|
|
263
|
+
conn.abort()
|
|
264
|
+
}
|
|
265
|
+
break
|
|
266
|
+
}
|
|
267
|
+
conn.receive(this.scratch, TLS_TCP_FROM, n)
|
|
268
|
+
}
|
|
269
|
+
return this.flush(slot)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** The socket of `slot` is writable: sends what the connection holds. Answers its `interest`. */
|
|
273
|
+
writable(slot: i32): i32 {
|
|
274
|
+
if (!this.holds(slot)) {
|
|
275
|
+
return TLS_RECORD_DONE
|
|
276
|
+
}
|
|
277
|
+
return this.flush(slot)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Writes the connection's output until the socket would block or it is all sent, and answers its `interest`. */
|
|
281
|
+
flush(slot: i32): i32 {
|
|
282
|
+
const fd: i32 = this.fds[slot]
|
|
283
|
+
const conn: TlsRecordServer = this.connections[slot]
|
|
284
|
+
while (conn.outputEnd > conn.outputStart) {
|
|
285
|
+
const n: i32 = netWrite(fd, conn.output, conn.outputStart, conn.outputEnd - conn.outputStart)
|
|
286
|
+
if (n <= 0) {
|
|
287
|
+
if (n !== TLS_RECORD_AGAIN) {
|
|
288
|
+
conn.abort()
|
|
289
|
+
}
|
|
290
|
+
break
|
|
291
|
+
}
|
|
292
|
+
conn.consume(n)
|
|
293
|
+
}
|
|
294
|
+
return conn.interest()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** What `slot`'s connection wants now, without touching the socket; `TLS_RECORD_DONE` for a free slot. */
|
|
298
|
+
interest(slot: i32): i32 {
|
|
299
|
+
return this.holds(slot) ? this.connections[slot].interest() : TLS_RECORD_DONE
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** What `slot`'s CertificateVerify signs, while its `interest` says `TLS_RECORD_SIGN`; `null` otherwise. */
|
|
303
|
+
signatureInput(slot: i32): u8[] | null {
|
|
304
|
+
return this.holds(slot) ? this.connections[slot].signatureInput() : null
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Hands `slot` the signature over `signatureInput(slot)`, seals the rest of its flight and sends it. Answers its `interest`. */
|
|
308
|
+
sign(slot: i32, signature: u8[]): i32 {
|
|
309
|
+
if (!this.holds(slot)) {
|
|
310
|
+
return TLS_RECORD_DONE
|
|
311
|
+
}
|
|
312
|
+
this.connections[slot].sign(signature)
|
|
313
|
+
return this.flush(slot)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Signs `slot`'s CertificateVerify with the P-256 private key `key` and
|
|
318
|
+
* hands it over, as `sign` does. The key is borrowed: the caller made the
|
|
319
|
+
* `Secret`, and wipes it. A malformed key fails the connection with
|
|
320
|
+
* `internal_error`.
|
|
321
|
+
*/
|
|
322
|
+
signP256(slot: i32, key: Secret<u8[]>): i32 {
|
|
323
|
+
const input: u8[] | null = this.signatureInput(slot)
|
|
324
|
+
if (input === null) {
|
|
325
|
+
return this.interest(slot)
|
|
326
|
+
}
|
|
327
|
+
const signature: u8[] | null = tlsSignEcdsaP256(key, input)
|
|
328
|
+
if (signature === null) {
|
|
329
|
+
this.connections[slot].fail(TLS_ALERT_INTERNAL_ERROR)
|
|
330
|
+
return this.flush(slot)
|
|
331
|
+
}
|
|
332
|
+
return this.sign(slot, signature)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Application data from `slot`'s client into `buf[off .. off + len)`: the
|
|
337
|
+
* count, 0 at the end of its stream, -11 when nothing is waiting, -104 once
|
|
338
|
+
* the connection has failed, as `TlsRecordServer.read` says. Reading makes
|
|
339
|
+
* room, and the records that were waiting for it may be handled and
|
|
340
|
+
* answered (a KeyUpdate), which is sent here; the connection may then want
|
|
341
|
+
* the socket read again, so re-arm it with `interest(slot)`.
|
|
342
|
+
*/
|
|
343
|
+
read(slot: i32, buf: u8[], off: i32, len: i32): i32 {
|
|
344
|
+
if (!this.holds(slot)) {
|
|
345
|
+
return TLS_RECORD_INVALID
|
|
346
|
+
}
|
|
347
|
+
const n: i32 = this.connections[slot].read(buf, off, len)
|
|
348
|
+
this.flush(slot)
|
|
349
|
+
return n
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Application data for `slot`'s client, `data[off .. off + len)`: seals as
|
|
354
|
+
* much as fits and sends what the socket takes, and answers how much was
|
|
355
|
+
* taken, -11 when nothing fits yet or the handshake is under way, -32 once
|
|
356
|
+
* the server has closed, as `TlsRecordServer.write` says.
|
|
357
|
+
*/
|
|
358
|
+
write(slot: i32, data: u8[], off: i32, len: i32): i32 {
|
|
359
|
+
if (!this.holds(slot)) {
|
|
360
|
+
return TLS_RECORD_INVALID
|
|
361
|
+
}
|
|
362
|
+
const n: i32 = this.connections[slot].write(data, off, len)
|
|
363
|
+
this.flush(slot)
|
|
364
|
+
return n
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Sends a KeyUpdate on `slot`, asking the client for one too when `requestPeer`; answers as `TlsRecordServer.keyUpdate`. */
|
|
368
|
+
keyUpdate(slot: i32, requestPeer: boolean): i32 {
|
|
369
|
+
if (!this.holds(slot)) {
|
|
370
|
+
return TLS_RECORD_INVALID
|
|
371
|
+
}
|
|
372
|
+
const r: i32 = this.connections[slot].keyUpdate(requestPeer)
|
|
373
|
+
this.flush(slot)
|
|
374
|
+
return r
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Closes the server's side of `slot`'s connection (§6.1): a `close_notify`
|
|
379
|
+
* after whatever is already written, sent as the socket takes it. The slot
|
|
380
|
+
* stays the program's until its interest says `TLS_RECORD_DONE` — every
|
|
381
|
+
* byte is sent — and the program calls `close`. Answers the interest.
|
|
382
|
+
*/
|
|
383
|
+
shutdown(slot: i32): i32 {
|
|
384
|
+
if (!this.holds(slot)) {
|
|
385
|
+
return TLS_RECORD_DONE
|
|
386
|
+
}
|
|
387
|
+
this.connections[slot].close()
|
|
388
|
+
return this.flush(slot)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Ends `slot`'s connection and frees the slot at once: a `close_notify`
|
|
393
|
+
* unless the connection already failed or closed, one attempt to send what
|
|
394
|
+
* is left, every key wiped, and the descriptor closed, which also takes it
|
|
395
|
+
* out of every loop. A program that must know every byte and the
|
|
396
|
+
* `close_notify` arrived calls `shutdown` instead, and `close` once the
|
|
397
|
+
* slot's interest says `TLS_RECORD_DONE`.
|
|
398
|
+
*/
|
|
399
|
+
close(slot: i32): void {
|
|
400
|
+
if (!this.holds(slot)) {
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
const conn: TlsRecordServer = this.connections[slot]
|
|
404
|
+
conn.close()
|
|
405
|
+
this.flush(slot)
|
|
406
|
+
netClose(this.fds[slot])
|
|
407
|
+
this.fds[slot] = TLS_TCP_FREE
|
|
408
|
+
conn.wipeKeys()
|
|
409
|
+
}
|
|
410
|
+
}
|