Skip to content

Commit 1890801

Browse files
authored
Merge pull request #491 from MatrixAI/feature-dns-multi
Multi-Host DNS and Multi NodeID resolution - for network entry and general usage
2 parents 854ef56 + ec5d32f commit 1890801

27 files changed

+904
-374
lines changed

src/PolykeyAgent.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ class PolykeyAgent {
573573
PolykeyAgent.eventSymbols.Proxy,
574574
async (data: ConnectionData) => {
575575
if (data.type === 'reverse') {
576+
if (this.keyManager.getNodeId().equals(data.remoteNodeId)) return;
576577
const address = networkUtils.buildAddress(
577578
data.remoteHost,
578579
data.remotePort,

src/agent/GRPCClientAgent.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ class GRPCClientAgent extends GRPCClient<AgentServiceClient> {
7272
return grpcClientAgent;
7373
}
7474

75-
public async destroy() {
76-
await super.destroy();
75+
public async destroy({
76+
timeout,
77+
}: {
78+
timeout?: number;
79+
} = {}) {
80+
await super.destroy({ timeout });
7781
}
7882

7983
@ready(new agentErrors.ErrorAgentClientDestroyed())

src/agent/service/nodesHolePunchMessageSend.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function nodesHolePunchMessageSend({
7171
call.request.getProxyAddress(),
7272
);
7373
logger.debug(
74-
`Received signalling message to target ${call.request.getSrcId()}@${host}:${port}`,
74+
`Received signaling message to target ${call.request.getSrcId()}@${host}:${port}`,
7575
);
7676
// Ignore failure
7777
try {
@@ -81,7 +81,7 @@ function nodesHolePunchMessageSend({
8181
}
8282
} else {
8383
logger.error(
84-
'Received signalling message, target information was missing, skipping reverse hole punch',
84+
'Received signaling message, target information was missing, skipping reverse hole punch',
8585
);
8686
}
8787
} else if (await nodeManager.knowsNode(sourceId, tran)) {
@@ -92,15 +92,22 @@ function nodesHolePunchMessageSend({
9292
connectionInfo!.remoteHost,
9393
connectionInfo!.remotePort,
9494
);
95+
// Checking if the source and destination are the same
96+
if (sourceId?.equals(targetId)) {
97+
// Logging and silently dropping operation
98+
logger.warn('Signaling relay message requested signal to itself');
99+
callback(null, response);
100+
return;
101+
}
95102
call.request.setProxyAddress(proxyAddress);
96103
logger.debug(
97-
`Relaying signalling message from ${srcNodeId}@${
104+
`Relaying signaling message from ${srcNodeId}@${
98105
connectionInfo!.remoteHost
99106
}:${
100107
connectionInfo!.remotePort
101108
} to ${targetNodeId} with information ${proxyAddress}`,
102109
);
103-
await nodeConnectionManager.relaySignallingMessage(call.request, {
110+
await nodeConnectionManager.relaySignalingMessage(call.request, {
104111
host: connectionInfo!.remoteHost,
105112
port: connectionInfo!.remotePort,
106113
});

src/bin/CommandPolykey.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import type { FileSystem } from '../types';
22
import commander from 'commander';
3-
import Logger, { StreamHandler, formatting } from '@matrixai/logger';
3+
import Logger, {
4+
StreamHandler,
5+
formatting,
6+
levelToString,
7+
evalLogDataValue,
8+
} from '@matrixai/logger';
49
import * as binUtils from './utils';
510
import * as binOptions from './utils/options';
611
import * as binErrors from './errors';
@@ -68,8 +73,21 @@ class CommandPolykey extends commander.Command {
6873
// Set the logger formatter according to the format
6974
if (opts.format === 'json') {
7075
this.logger.handlers.forEach((handler) =>
71-
handler.setFormatter(formatting.jsonFormatter),
76+
handler.setFormatter((record) => {
77+
return JSON.stringify(
78+
{
79+
level: levelToString(record.level),
80+
keys: record.keys,
81+
msg: record.msg,
82+
...record.data,
83+
},
84+
evalLogDataValue,
85+
);
86+
}),
7287
);
88+
} else {
89+
const format = formatting.format`${formatting.level}:${formatting.keys}:${formatting.msg}`;
90+
this.logger.handlers.forEach((handler) => handler.setFormatter(format));
7391
}
7492
// Set the global upstream GRPC logger
7593
grpcSetLogger(this.logger.getChild('grpc'));

src/config.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
11
import type { Host, Port } from './network/types';
2+
import type { NodeAddress } from 'nodes/types';
23
import { getDefaultNodePath } from './utils';
34
// @ts-ignore package.json is outside rootDir
45
import { version } from '../package.json';
56

7+
/**
8+
* Configuration for testnet node addresses.
9+
* Extracted here to enforce types properly.
10+
*/
11+
const testnet: Record<string, NodeAddress> = {
12+
vg9a9e957878s2qgtbdmu2atvli8ms7muukb1dk4dpbm4llkki3h0: {
13+
host: 'testnet.polykey.io' as Host,
14+
port: 1314 as Port,
15+
},
16+
vh9oqtvct10eaiv3cl4ebm0ko33sl0qqpvb59vud8cngfvqs4p4ng: {
17+
host: 'testnet.polykey.io' as Host,
18+
port: 1314 as Port,
19+
},
20+
};
21+
22+
/**
23+
* Configuration for main net node addresses.
24+
* Extracted here to enforce types properly.
25+
*/
26+
const mainnet: Record<string, NodeAddress> = {};
27+
628
/**
729
* Polykey static configuration
830
* This is intended only for static properties
@@ -96,8 +118,8 @@ const config = {
96118
},
97119
// This is not used by the `PolykeyAgent` which defaults to `{}`
98120
network: {
99-
mainnet: {},
100-
testnet: {},
121+
mainnet: mainnet,
122+
testnet: testnet,
101123
},
102124
},
103125
};

src/contexts/functions/timedCancellable.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ function setupTimedCancellable<C extends ContextTimed, P extends Array<any>, R>(
104104
);
105105
ctx.signal = abortController.signal;
106106
teardownContext = () => {
107+
// The timer is not cancelled here because
108+
// it was not created in this scope
107109
finished = true;
108110
};
109111
} else {

src/grpc/GRPCClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ abstract class GRPCClient<T extends Client = Client> {
152152
const socket = session.socket as TLSSocket;
153153
serverCertChain = networkUtils.getCertificateChain(socket);
154154
try {
155-
networkUtils.verifyServerCertificateChain(nodeId, serverCertChain);
155+
networkUtils.verifyServerCertificateChain([nodeId], serverCertChain);
156156
} catch (e) {
157157
const e_ = e;
158158
if (e instanceof networkErrors.ErrorCertChain) {

src/network/ConnectionForward.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ type ConnectionsForward = {
2525
interface ConnectionForward extends StartStop {}
2626
@StartStop()
2727
class ConnectionForward extends Connection {
28-
public readonly nodeId: NodeId;
29-
28+
protected nodeId_: NodeId;
29+
protected nodeIds: Array<NodeId>;
3030
protected connections: ConnectionsForward;
3131
protected pingInterval: ReturnType<typeof setInterval>;
3232
protected utpConn: UTPConnection;
@@ -98,15 +98,15 @@ class ConnectionForward extends Connection {
9898
};
9999

100100
public constructor({
101-
nodeId,
101+
nodeIds,
102102
connections,
103103
...rest
104104
}: {
105-
nodeId: NodeId;
105+
nodeIds: Array<NodeId>;
106106
connections: ConnectionsForward;
107107
} & AbstractConstructorParameters<typeof Connection>[0]) {
108108
super(rest);
109-
this.nodeId = nodeId;
109+
this.nodeIds = nodeIds;
110110
this.connections = connections;
111111
}
112112

@@ -137,6 +137,10 @@ class ConnectionForward extends Connection {
137137
} else {
138138
ctx.signal.addEventListener('abort', () => resolveAbortedP());
139139
}
140+
void ctx.timer.then(
141+
() => resolveAbortedP(),
142+
() => {},
143+
);
140144
this.resolveReadyP = resolveReadyP;
141145
this.utpSocket.on('message', this.handleMessage);
142146
const handleStartError = (e) => {
@@ -211,7 +215,10 @@ class ConnectionForward extends Connection {
211215
}
212216
const serverCertChain = networkUtils.getCertificateChain(this.tlsSocket);
213217
try {
214-
networkUtils.verifyServerCertificateChain(this.nodeId, serverCertChain);
218+
this.nodeId_ = networkUtils.verifyServerCertificateChain(
219+
this.nodeIds,
220+
serverCertChain,
221+
);
215222
} catch (e) {
216223
this.logger.debug(
217224
`Failed to start Connection Forward: verification failed`,
@@ -259,6 +266,11 @@ class ConnectionForward extends Connection {
259266
this.logger.info('Stopped Connection Forward');
260267
}
261268

269+
@ready(new networkErrors.ErrorConnectionNotRunning())
270+
get nodeId() {
271+
return this.nodeId_;
272+
}
273+
262274
@ready(new networkErrors.ErrorConnectionNotRunning())
263275
public compose(clientSocket: Socket): void {
264276
try {

src/network/Proxy.ts

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import * as nodesUtils from '../nodes/utils';
2626
import { promisify } from '../utils';
2727
import { timedCancellable, context } from '../contexts';
2828

29+
const clientConnectionClosedReason = Symbol('clientConnectionClosedReason');
30+
2931
interface Proxy extends StartStop {}
3032
@StartStop()
3133
class Proxy {
@@ -316,23 +318,32 @@ class Proxy {
316318
* Set timer to `null` explicitly to wait forever
317319
*/
318320
public openConnectionForward(
319-
nodeId: NodeId,
321+
nodeIds: Array<NodeId>,
320322
proxyHost: Host,
321323
proxyPort: Port,
322324
ctx?: Partial<ContextTimed>,
323-
): PromiseCancellable<void>;
325+
): PromiseCancellable<NodeId>;
324326
@ready(new networkErrors.ErrorProxyNotRunning(), true)
325327
@timedCancellable(true, (proxy: Proxy) => proxy.connConnectTime)
326328
public async openConnectionForward(
327-
nodeId: NodeId,
329+
nodeId: Array<NodeId>,
328330
proxyHost: Host,
329331
proxyPort: Port,
330332
@context ctx: ContextTimed,
331-
): Promise<void> {
333+
): Promise<NodeId> {
332334
const proxyAddress = networkUtils.buildAddress(proxyHost, proxyPort);
333-
await this.connectionLocksForward.withF([proxyAddress, Lock], async () => {
334-
await this.establishConnectionForward(nodeId, proxyHost, proxyPort, ctx);
335-
});
335+
return await this.connectionLocksForward.withF(
336+
[proxyAddress, Lock],
337+
async () => {
338+
const connectionForward = await this.establishConnectionForward(
339+
nodeId,
340+
proxyHost,
341+
proxyPort,
342+
ctx,
343+
);
344+
return connectionForward.nodeId;
345+
},
346+
);
336347
}
337348

338349
@ready(new networkErrors.ErrorProxyNotRunning(), true)
@@ -409,10 +420,22 @@ class Proxy {
409420
}
410421
await this.connectionLocksForward.withF([proxyAddress, Lock], async () => {
411422
const timer = new Timer({ delay: this.connConnectTime });
423+
let cleanUpConnectionListener = () => {};
412424
try {
413-
await this.connectForward(nodeId, proxyHost, proxyPort, clientSocket, {
414-
timer,
415-
});
425+
const connectForwardProm = this.connectForward(
426+
[nodeId],
427+
proxyHost,
428+
proxyPort,
429+
clientSocket,
430+
{
431+
timer,
432+
},
433+
);
434+
cleanUpConnectionListener = () => {
435+
connectForwardProm.cancel(clientConnectionClosedReason);
436+
};
437+
clientSocket.addListener('close', cleanUpConnectionListener);
438+
await connectForwardProm;
416439
} catch (e) {
417440
if (e instanceof networkErrors.ErrorProxyConnectInvalidUrl) {
418441
if (!clientSocket.destroyed) {
@@ -471,6 +494,7 @@ class Proxy {
471494
return;
472495
} finally {
473496
timer.cancel();
497+
clientSocket.removeListener('close', cleanUpConnectionListener);
474498
}
475499
// After composing, switch off this error handler
476500
clientSocket.off('error', handleConnectError);
@@ -482,22 +506,22 @@ class Proxy {
482506
};
483507

484508
protected connectForward(
485-
nodeId: NodeId,
509+
nodeIds: Array<NodeId>,
486510
proxyHost: Host,
487511
proxyPort: Port,
488512
clientSocket: Socket,
489513
ctx?: Partial<ContextTimed>,
490-
): PromiseCancellable<void>;
514+
): PromiseCancellable<NodeId>;
491515
@timedCancellable(true, (proxy: Proxy) => proxy.connConnectTime)
492516
protected async connectForward(
493-
nodeId: NodeId,
517+
nodeIds: Array<NodeId>,
494518
proxyHost: Host,
495519
proxyPort: Port,
496520
clientSocket: Socket,
497521
@context ctx: ContextTimed,
498-
): Promise<void> {
522+
): Promise<NodeId> {
499523
const conn = await this.establishConnectionForward(
500-
nodeId,
524+
nodeIds,
501525
proxyHost,
502526
proxyPort,
503527
ctx,
@@ -511,10 +535,11 @@ class Proxy {
511535
remotePort: conn.port,
512536
type: 'forward',
513537
});
538+
return conn.nodeId;
514539
}
515540

516541
protected async establishConnectionForward(
517-
nodeId: NodeId,
542+
nodeIds: Array<NodeId>,
518543
proxyHost: Host,
519544
proxyPort: Port,
520545
ctx: ContextTimed,
@@ -530,7 +555,7 @@ class Proxy {
530555
return conn;
531556
}
532557
conn = new ConnectionForward({
533-
nodeId,
558+
nodeIds,
534559
connections: this.connectionsForward,
535560
utpSocket: this.utpSocket,
536561
host: proxyHost,

src/network/errors.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class ErrorConnectionEndTimeout<T> extends ErrorConnection<T> {
5757
exitCode = sysexits.UNAVAILABLE;
5858
}
5959

60+
class ErrorConnectionNodesEmpty<T> extends ErrorConnection<T> {
61+
static description = 'Nodes list to verify against was empty';
62+
exitCode = sysexits.USAGE;
63+
}
64+
6065
/**
6166
* Used by ConnectionForward and ConnectionReverse
6267
*/
@@ -129,9 +134,9 @@ class ErrorCertChainSignatureInvalid<T> extends ErrorCertChain<T> {
129134
exitCode = sysexits.PROTOCOL;
130135
}
131136

132-
class ErrorHostnameResolutionFailed<T> extends ErrorNetwork<T> {
133-
static description = 'Unable to resolve hostname';
134-
exitCode = sysexits.USAGE;
137+
class ErrorDNSResolver<T> extends ErrorNetwork<T> {
138+
static description = 'DNS resolution failed';
139+
exitCode = sysexits.SOFTWARE;
135140
}
136141

137142
export {
@@ -148,6 +153,7 @@ export {
148153
ErrorConnectionMessageParse,
149154
ErrorConnectionTimeout,
150155
ErrorConnectionEndTimeout,
156+
ErrorConnectionNodesEmpty,
151157
ErrorConnectionStart,
152158
ErrorConnectionStartTimeout,
153159
ErrorConnectionStartTimeoutMax,
@@ -161,5 +167,5 @@ export {
161167
ErrorCertChainNameInvalid,
162168
ErrorCertChainKeyInvalid,
163169
ErrorCertChainSignatureInvalid,
164-
ErrorHostnameResolutionFailed,
170+
ErrorDNSResolver,
165171
};

0 commit comments

Comments
 (0)