Skip to content

Commit 9f6ae63

Browse files
committed
feat: added in secure establishment event
[ci skip]
1 parent 1d55f12 commit 9f6ae63

File tree

1 file changed

+121
-31
lines changed

1 file changed

+121
-31
lines changed

src/QUICConnection.ts

Lines changed: 121 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { quiche } from './native';
2323
import * as events from './events';
2424
import * as utils from './utils';
2525
import * as errors from './errors';
26+
import { never } from './utils';
2627

2728
/**
2829
* Think of this as equivalent to `net.Socket`.
@@ -193,6 +194,12 @@ class QUICConnection extends EventTarget {
193194
public readonly lockbox = new LockBox<RWLockWriter>();
194195
public readonly lockCode = 'Lock'; // TODO: more unique code
195196

197+
protected customVerified = false;
198+
protected shortReceived = false;
199+
protected shortSent = false;
200+
protected secured = false;
201+
protected count = 0;
202+
196203
public constructor({
197204
type,
198205
scid,
@@ -353,10 +360,8 @@ class QUICConnection extends EventTarget {
353360
this.socket.connectionMap.set(this.connectionId, this);
354361
// Waits for the first short packet after establishment
355362
// This ensures that TLS has been established and verified on both sides
356-
console.log('sending');
357363
await this.send();
358-
console.log('waiting secured');
359-
// await this.secureEstablishedP;
364+
await this.secureEstablishedP;
360365
this.logger.warn('secured');
361366
// After this is done
362367
// We need to established the keep alive interval time
@@ -447,6 +452,49 @@ class QUICConnection extends EventTarget {
447452
// But during `start` we are just waiting
448453
this.socket.connectionMap.delete(this.connectionId);
449454

455+
// Emit error if peer error
456+
const peerError = this.conn.peerError();
457+
if (peerError != null) {
458+
const message = `Connection errored out with peerError ${Buffer.from(
459+
peerError.reason,
460+
).toString()}(${peerError.errorCode})`;
461+
this.logger.info(message);
462+
this.dispatchEvent(
463+
new events.QUICConnectionErrorEvent({
464+
detail: new errors.ErrorQUICConnectionInternal(
465+
message,
466+
{
467+
data: {
468+
type: 'local',
469+
...peerError,
470+
}
471+
},
472+
),
473+
}),
474+
);
475+
}
476+
477+
const localError = this.conn.localError();
478+
if (localError != null) {
479+
const message = `connection failed with localError ${Buffer.from(
480+
localError.reason,
481+
).toString()}(${localError.errorCode})`;
482+
this.logger.info(message);
483+
this.dispatchEvent(
484+
new events.QUICConnectionErrorEvent({
485+
detail: new errors.ErrorQUICConnectionInternal(
486+
message,
487+
{
488+
data: {
489+
type: 'local',
490+
...localError,
491+
}
492+
}
493+
),
494+
}),
495+
);
496+
}
497+
450498
this.dispatchEvent(new events.QUICConnectionStopEvent());
451499
this.logger.info(`Stopped ${this.constructor.name}`);
452500
}
@@ -518,39 +566,37 @@ class QUICConnection extends EventTarget {
518566
this.lastErrorMessage = e.message;
519567
}
520568

569+
// Checking if the packet was a short frame.
570+
// Short indicates that the peer has completed TLS verification
571+
if (!this.shortReceived) {
572+
const header = quiche.Header.fromSlice(data, quiche.MAX_CONN_ID_LEN);
573+
// if short frame
574+
if (header.ty === 5) {
575+
this.shortReceived = true;
576+
this.conn.sendAckEliciting();
577+
}
578+
}
579+
580+
if (
581+
!this.secured &&
582+
this.shortReceived &&
583+
this.shortReceived &&
584+
!this.conn.isDraining()
585+
) {
586+
if (this.count >= 1) {
587+
this.secured = true;
588+
this.resolveSecureEstablishedP();
589+
// this.dispatchEvent(new events.QUICConnectionRemoteSecureEvent()); TODO
590+
}
591+
this.count += 1;
592+
}
593+
521594
// We don't actually "fail"
522595
// the closedP until we proceed
523596
// But note that if there's an error
524597

525598
if (this.conn.isEstablished()) {
526599
this.resolveEstablishedP();
527-
528-
if (this.type === 'server') {
529-
// For server connections, if we are established
530-
// we are secure established
531-
this.resolveSecureEstablishedP();
532-
} else if (this.type === 'client') {
533-
// We need a hueristic to indicate whether we are securely established
534-
// If we are already established
535-
// AND IF, we are getting a packet after establishment
536-
// And we didn't result in an error
537-
// Neither draining, nor closed, nor timed out
538-
// For server connections
539-
// If we are already established, then we are secure established
540-
// To know if the server is also established
541-
// We need to know the NEXT recv after we are already established
542-
// So we received something, and that allows us to be established
543-
// UPON the next recv
544-
// We need to ensure:
545-
// 1. No errors
546-
// 2. Not draining
547-
// 3. No
548-
// YES the main thing is that there is no errors
549-
// I think that's the KEY
550-
// But we must only switch
551-
// If were "already" established
552-
// That this wasn't the first time we were established
553-
}
554600
}
555601

556602
// We also need to know whether this is our first short frame
@@ -586,7 +632,7 @@ class QUICConnection extends EventTarget {
586632
}
587633
readIds.push(quicStream.streamId);
588634
quicStream.read();
589-
// QuicStream.dispatchEvent(new events.QUICStreamReadableEvent()); // TODO: remove?
635+
// QuicStream.dispatchEvent(new events.QUICStreamReadablaeEvent()); // TODO: remove?
590636
}
591637
if (readIds.length > 0) {
592638
this.logger.info(`processed reads for ${readIds}`);
@@ -691,6 +737,50 @@ class QUICConnection extends EventTarget {
691737
);
692738
this.logger.debug(`sent ${sendLength} bytes`);
693739
}
740+
// Handling custom TLS verification, this must be done after the following conditions.
741+
// 1. Connection established.
742+
// 2. Certs available.
743+
// 3. Sent after connection has established.
744+
if (
745+
!this.customVerified &&
746+
this.conn.isEstablished() &&
747+
this.conn.peerCertChain() != null
748+
) {
749+
this.customVerified = true;
750+
const peerCerts = this.conn.peerCertChain();
751+
if (peerCerts == null) never();
752+
const peerCertsPem = peerCerts.map((c) =>
753+
utils.certificateDERToPEM(c),
754+
);
755+
// Dispatching certs available event
756+
// this.dispatchEvent(new events.QUICConnectionRemoteCertEvent()); TODO
757+
try {
758+
// if (this.verifyCallback != null) this.verifyCallback(peerCertsPem); TODO
759+
this.conn.sendAckEliciting();
760+
} catch (e) {
761+
// Force the connection to end.
762+
// Error 304 indicates cert chain failed verification.
763+
// Error 372 indicates cert chain was missing.
764+
this.conn.close(
765+
false,
766+
304,
767+
Buffer.from(`Custom TLSFail: ${e.message}`),
768+
);
769+
}
770+
}
771+
772+
// Check the header type
773+
if (!this.shortSent) {
774+
const header = quiche.Header.fromSlice(
775+
sendBuffer,
776+
quiche.MAX_CONN_ID_LEN,
777+
);
778+
// If short frame
779+
if (header.ty === 5) {
780+
// Short was sent, locally secured
781+
this.shortSent = true;
782+
}
783+
}
694784
} catch (e) {
695785
// If called `stop` due to an error here
696786
// we MUST not call `this.send` again

0 commit comments

Comments
 (0)