pairnet 0.1.0 → 0.2.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/eslint.config.js +3 -0
- package/index.js +206 -109
- package/lib/create-query-error.js +8 -0
- package/lib/network/connect-multiple.js +74 -0
- package/lib/pairing-token/generate.js +7 -0
- package/lib/pairing-token/reduce-addresses.js +21 -0
- package/lib/pairing-token/wrapped.js +77 -0
- package/{promisified → lib/promisified}/server-close.js +2 -2
- package/{promisified → lib/promisified}/server-listen.js +2 -2
- package/lib/protocol/evaluate-query.js +19 -0
- package/lib/protocol/index.js +161 -0
- package/lib/protocol/message-types.js +37 -0
- package/lib/reply-tracker.js +25 -10
- package/lib/sender.js +1 -1
- package/{storages → lib/storages}/memory.js +7 -0
- package/lib/store.js +12 -6
- package/notes.txt +3 -0
- package/package.json +32 -27
- package/test/index.js +54 -21
- package/test/wrapped-token.js +30 -0
- package/.eslintrc +0 -3
- package/lib/maybe-call.js +0 -7
- package/lib/protocol.js +0 -191
- package/promisified/disconnect-socket.js +0 -11
- package/streams/infinite-queue.js +0 -37
- /package/{storages → lib/storages}/file.js +0 -0
- /package/{storages → lib/storages}/symbols.js +0 -0
- /package/lib/{generate-keys.js → tls/generate-keys.js} +0 -0
- /package/lib/{get-fingerprint.js → tls/get-fingerprint.js} +0 -0
package/lib/protocol.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const encodeCBOR = require("@promistream/encode-cbor");
|
|
4
|
-
const decodeCBOR = require("@promistream/decode-cbor");
|
|
5
|
-
const pipe = require("@promistream/pipe");
|
|
6
|
-
const fromNodeStream = require("@promistream/from-node-stream");
|
|
7
|
-
const map = require("@promistream/map");
|
|
8
|
-
const infiniteQueue = require("../streams/infinite-queue");
|
|
9
|
-
const simpleSink = require("@promistream/simple-sink");
|
|
10
|
-
const createDebugStream = require("@promistream/debug");
|
|
11
|
-
const objid = require("@joepie91/objid");
|
|
12
|
-
|
|
13
|
-
const debug = require("debug")("pairnet:protocol");
|
|
14
|
-
|
|
15
|
-
const createSender = require("./sender");
|
|
16
|
-
const createReplyTracker = require("./reply-tracker");
|
|
17
|
-
const disconnectSocket = require("../promisified/disconnect-socket");
|
|
18
|
-
const filter = require("@promistream/filter");
|
|
19
|
-
|
|
20
|
-
let NORMAL_QUERY_TYPE = "q";
|
|
21
|
-
let NEGOTIATION_QUERY_TYPE = "nq";
|
|
22
|
-
let EVENT_TYPE = "e";
|
|
23
|
-
// negotiation query, negotiation reply, negotiation reply error
|
|
24
|
-
let NEGOTIATION_TYPES = new Set([ "nq", "n", "N" ]);
|
|
25
|
-
// reply, reply error, negotiation reply, negotiation reply error
|
|
26
|
-
let REPLY_TYPES = new Set([ "r", "R", "n", "N" ]);
|
|
27
|
-
let SUCCESS_REPLIES = new Set([ "r", "n" ]);
|
|
28
|
-
let ERROR_REPLIES = new Set([ "R", "N" ]);
|
|
29
|
-
|
|
30
|
-
async function evaluateQuery(query, handler) {
|
|
31
|
-
try {
|
|
32
|
-
return {
|
|
33
|
-
success: true,
|
|
34
|
-
message: await handler(query)
|
|
35
|
-
};
|
|
36
|
-
} catch (error) {
|
|
37
|
-
if (error.code === "QUERY_ERROR_RESPONSE") {
|
|
38
|
-
return {
|
|
39
|
-
success: false,
|
|
40
|
-
message: error.messageData
|
|
41
|
-
};
|
|
42
|
-
} else {
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
module.exports = async function handleProtocol(socket, { onQuery, protocolConfiguration } = {}) {
|
|
49
|
-
debug("Attaching semantic protocol handler");
|
|
50
|
-
|
|
51
|
-
let writeQueue = infiniteQueue([]);
|
|
52
|
-
let replyTracker = createReplyTracker();
|
|
53
|
-
let negotiationCompleted = false;
|
|
54
|
-
|
|
55
|
-
let sender = createSender(replyTracker, (message) => {
|
|
56
|
-
writeQueue.push(message);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Send pipeline
|
|
60
|
-
pipe([
|
|
61
|
-
writeQueue.stream,
|
|
62
|
-
map((message) => {
|
|
63
|
-
debug("Sending message:", message);
|
|
64
|
-
|
|
65
|
-
let isNegotiationMessage = NEGOTIATION_TYPES.has(message.t);
|
|
66
|
-
|
|
67
|
-
if (negotiationCompleted !== true && !isNegotiationMessage) {
|
|
68
|
-
debug(`[handleProtocol] Prevented message send; not allowed type '${message.t}' during negotiation`);
|
|
69
|
-
throw new Error(`Cannot send messages in negotiation phase`);
|
|
70
|
-
} else {
|
|
71
|
-
return message;
|
|
72
|
-
}
|
|
73
|
-
}),
|
|
74
|
-
// createDebugStream(`pairnet:protocol:send-pipeline ${objid(socket)}`),
|
|
75
|
-
encodeCBOR(),
|
|
76
|
-
fromNodeStream.fromWritable(socket)
|
|
77
|
-
]).read().catch((error) => {
|
|
78
|
-
// These errors are also mirrored on the read side, so we don't need to handle them here
|
|
79
|
-
debug(`Error occurred on writable side of socket`, error);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
let incomingMessageStream = pipe([
|
|
83
|
-
fromNodeStream.fromReadable(socket),
|
|
84
|
-
decodeCBOR(),
|
|
85
|
-
// createDebugStream(`pairnet:protocol:receive-pipeline ${objid(socket)}`),
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
let clientHelloQuery = (protocolConfiguration.isClient)
|
|
89
|
-
? sender.sendNegotiationQuery(await protocolConfiguration.generateClientHello())._queryID.toString("hex") // FIXME: Hacky
|
|
90
|
-
: null;
|
|
91
|
-
|
|
92
|
-
// We read messages for as long as negotiation has not been completed yet
|
|
93
|
-
await pipe([
|
|
94
|
-
incomingMessageStream,
|
|
95
|
-
simpleSink(async (message) => {
|
|
96
|
-
debug("Received message:", message);
|
|
97
|
-
|
|
98
|
-
let isNegotiationMessage = NEGOTIATION_TYPES.has(message.t);
|
|
99
|
-
let isReplyMessage = REPLY_TYPES.has(message.t);
|
|
100
|
-
|
|
101
|
-
if (!isNegotiationMessage) {
|
|
102
|
-
debug(`[readLoop] Ignored received message; not allowed type '${message.t}' during negotiation`);
|
|
103
|
-
} else if (isReplyMessage) {
|
|
104
|
-
if (message.i.toString("hex") === clientHelloQuery) {
|
|
105
|
-
let helloResponse = message.m;
|
|
106
|
-
let helloDecision = await protocolConfiguration.onServerHello(helloResponse);
|
|
107
|
-
|
|
108
|
-
if (helloDecision === false) {
|
|
109
|
-
// Connection rejected
|
|
110
|
-
return disconnectSocket(socket);
|
|
111
|
-
} else {
|
|
112
|
-
debug(`[readLoop] Negotiation completed on client side; detaching sink...`);
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
throw new Error(`Unrecognized negotiation query reply, id: ${message.i}`);
|
|
117
|
-
}
|
|
118
|
-
} else if (message.t === NEGOTIATION_QUERY_TYPE) {
|
|
119
|
-
let result = await evaluateQuery(message.m, protocolConfiguration.onClientHello);
|
|
120
|
-
|
|
121
|
-
if (result.success) {
|
|
122
|
-
await sender.sendNegotiationReply(message.i, result.message);
|
|
123
|
-
|
|
124
|
-
debug(`[readLoop] Negotiation completed on server side; detaching sink...`);
|
|
125
|
-
return null;
|
|
126
|
-
} else {
|
|
127
|
-
await sender.sendNegotiationReplyError(message.i, result.message);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
debug(`[readLoop] Processed negotiation query ID '${message.i.toString("hex")}'`);
|
|
131
|
-
} else {
|
|
132
|
-
debug(`[readLoop] Received message with unknown type '${message.t}'`);
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
]).read();
|
|
136
|
-
|
|
137
|
-
// Unlock the sender
|
|
138
|
-
negotiationCompleted = true;
|
|
139
|
-
|
|
140
|
-
let eventStream = pipe([
|
|
141
|
-
incomingMessageStream,
|
|
142
|
-
filter(async (message) => {
|
|
143
|
-
let isReplyMessage = REPLY_TYPES.has(message.t);
|
|
144
|
-
|
|
145
|
-
if (message.t === NORMAL_QUERY_TYPE) {
|
|
146
|
-
let result = await evaluateQuery(message.m, onQuery);
|
|
147
|
-
|
|
148
|
-
if (result.success) {
|
|
149
|
-
await sender.sendReply(message.i, result.message);
|
|
150
|
-
} else {
|
|
151
|
-
await sender.sendReplyError(message.i, result.message);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
debug(`[readLoop] Processed normal query ID '${message.i.toString("hex")}'`);
|
|
155
|
-
return false;
|
|
156
|
-
} else if (message.t === EVENT_TYPE) {
|
|
157
|
-
// This is the only type of message that we will pass through as-is
|
|
158
|
-
debug(`[readLoop] Passed through event message as-is`);
|
|
159
|
-
return true;
|
|
160
|
-
} else if (isReplyMessage) {
|
|
161
|
-
try {
|
|
162
|
-
if (SUCCESS_REPLIES.has(message.t)) {
|
|
163
|
-
replyTracker.registerReply(message.i).resolve(message.m);
|
|
164
|
-
} else if (ERROR_REPLIES.has(message.t)) {
|
|
165
|
-
let error = new Error(`Received error response from peer`);
|
|
166
|
-
error.code = "PEER_ERROR";
|
|
167
|
-
error.message = message.m;
|
|
168
|
-
|
|
169
|
-
replyTracker.registerReply(message.i).reject(error);
|
|
170
|
-
}
|
|
171
|
-
} catch (error) {
|
|
172
|
-
if (error.code === "UNKNOWN_QUERY_ID") {
|
|
173
|
-
debug(`[readLoop] Received reply to unknown query ID '${message.i.toString("hex")}'`);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
} else {
|
|
177
|
-
debug(`[readLoop] Received message with unknown type '${message.t}'`);
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
|
-
}),
|
|
181
|
-
map(async (message) => {
|
|
182
|
-
return message.m; // Unpack actual message content from event message
|
|
183
|
-
})
|
|
184
|
-
]);
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
eventStream: eventStream,
|
|
188
|
-
sender: sender,
|
|
189
|
-
disconnect: () => disconnectSocket(socket)
|
|
190
|
-
};
|
|
191
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const debug = require("debug")("pairnet");
|
|
4
|
-
const pEvent = require("p-event");
|
|
5
|
-
|
|
6
|
-
module.exports = async function disconnect(socket) {
|
|
7
|
-
debug(`[disconnectSocket] Disconnecting...`);
|
|
8
|
-
socket.destroySoon();
|
|
9
|
-
await pEvent(socket, "close");
|
|
10
|
-
debug(`[disconnectSocket] Disconnected!`);
|
|
11
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const simpleSource = require("@promistream/simple-source");
|
|
4
|
-
const Aborted = require("@promistream/aborted");
|
|
5
|
-
const pDefer = require("p-defer");
|
|
6
|
-
|
|
7
|
-
module.exports = function createInfiniteQueue(initialItems = []) {
|
|
8
|
-
let queue = initialItems.slice();
|
|
9
|
-
let defers = [];
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
push: function (value) {
|
|
13
|
-
if (defers.length > 0) {
|
|
14
|
-
defers.shift().resolve(value);
|
|
15
|
-
} else {
|
|
16
|
-
queue.push(value);
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
stream: simpleSource({
|
|
20
|
-
onRequest: () => {
|
|
21
|
-
if (queue.length > 0) {
|
|
22
|
-
return queue.shift();
|
|
23
|
-
} else {
|
|
24
|
-
let defer = pDefer();
|
|
25
|
-
defers.push(defer);
|
|
26
|
-
return defer.promise;
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
onAbort: (reason) => {
|
|
30
|
-
for (let defer of defers) {
|
|
31
|
-
// TODO: Test properly
|
|
32
|
-
defer.reject(new Aborted("Stream was aborted"));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
};
|
|
37
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|