1.4.x_branch (#1)
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
@@ -1,172 +1,177 @@
|
||||
import Socket from 'net';
|
||||
|
||||
import * as constants from '../utils/constants';
|
||||
|
||||
import packet from './packet';
|
||||
|
||||
export default class connection {
|
||||
constructor(host_or_ip, port, password, socket) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (socket) {
|
||||
this.socket = socket;
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
connected = false;
|
||||
host_or_ip = '';
|
||||
password = '';
|
||||
port = 20000;
|
||||
reject;
|
||||
resolve;
|
||||
socket;
|
||||
|
||||
cleanup_handlers() {
|
||||
this.reject = null;
|
||||
this.resolve = null;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
if (!this.socket) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
this.socket = Socket.createConnection(
|
||||
this.port,
|
||||
this.host_or_ip,
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return reject(err);
|
||||
}
|
||||
return resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'connect()' failed: ${err}`));
|
||||
}
|
||||
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
setup_socket() {
|
||||
let buffer;
|
||||
const cleanup = () => {
|
||||
this.cleanup_handlers();
|
||||
buffer = null;
|
||||
};
|
||||
if (this.socket._socket) {
|
||||
this.socket._socket.setNoDelay(true);
|
||||
this.socket._socket.setKeepAlive(true);
|
||||
} else {
|
||||
this.socket.setNoDelay(true);
|
||||
this.socket.setKeepAlive(true);
|
||||
}
|
||||
|
||||
this.socket.on('data', (chunk) => {
|
||||
buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
|
||||
if (buffer.length > 4) {
|
||||
const size = buffer.readUInt32BE(0);
|
||||
if (buffer.length >= size + 4) {
|
||||
const packet_data = buffer.slice(4, 4 + size);
|
||||
if (this.resolve) {
|
||||
const complete = () => {
|
||||
const reject = this.reject;
|
||||
const resolve = this.resolve;
|
||||
cleanup();
|
||||
return {
|
||||
reject,
|
||||
resolve,
|
||||
};
|
||||
};
|
||||
|
||||
const response = new packet(this.password);
|
||||
response.buffer = new Uint8Array(packet_data);
|
||||
response
|
||||
.decrypt()
|
||||
.then(() => {
|
||||
const { resolve } = complete();
|
||||
if (resolve) {
|
||||
resolve(response);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
const { reject } = complete();
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('error', (e) => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log(e);
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('close', () => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log('socket closed');
|
||||
if (reject) {
|
||||
reject(new Error('socket closed'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
try {
|
||||
if (this.socket) {
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
this.cleanup_handlers();
|
||||
this.connected = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
packet.token = this.password;
|
||||
packet.encode_top_utf8(method_name);
|
||||
packet.encode_top_ui64(optional_thread_id || 1);
|
||||
packet.encode_top_utf8(constants.instance_id);
|
||||
packet.encode_top_ui32(0); // Service flags
|
||||
packet.encode_top_utf8(constants.get_version());
|
||||
await packet.encrypt();
|
||||
packet.encode_top_ui32(packet.buffer.length);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
this.socket.write(Buffer.from(packet.buffer), null, (err) => {
|
||||
if (err) {
|
||||
this.cleanup_handlers();
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
import Socket from 'net';
|
||||
|
||||
import * as constants from '../utils/constants';
|
||||
|
||||
import packet from './packet';
|
||||
|
||||
export default class connection {
|
||||
constructor(host_or_ip, port, password, socket) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (socket) {
|
||||
this.socket = socket;
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
connected = false;
|
||||
host_or_ip = '';
|
||||
nonce = '';
|
||||
password = '';
|
||||
port = 20000;
|
||||
reject;
|
||||
resolve;
|
||||
socket;
|
||||
|
||||
cleanup_handlers() {
|
||||
this.reject = null;
|
||||
this.resolve = null;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
if (!this.socket) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
this.socket = Socket.createConnection(
|
||||
this.port,
|
||||
this.host_or_ip,
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
}
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'connect()' failed: ${err}`));
|
||||
}
|
||||
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
setup_socket() {
|
||||
let buffer;
|
||||
const cleanup = () => {
|
||||
this.cleanup_handlers();
|
||||
buffer = null;
|
||||
};
|
||||
if (this.socket._socket) {
|
||||
this.socket._socket.setNoDelay(true);
|
||||
this.socket._socket.setKeepAlive(true);
|
||||
} else {
|
||||
this.socket.setNoDelay(true);
|
||||
this.socket.setKeepAlive(true);
|
||||
}
|
||||
|
||||
this.socket.on('data', (chunk) => {
|
||||
buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
|
||||
if (buffer.length > 4) {
|
||||
const size = buffer.readUInt32BE(0);
|
||||
if (buffer.length >= size + 4) {
|
||||
const packet_data = buffer.slice(4, 4 + size);
|
||||
if (this.resolve) {
|
||||
const complete = () => {
|
||||
const reject = this.reject;
|
||||
const resolve = this.resolve;
|
||||
cleanup();
|
||||
return {
|
||||
reject,
|
||||
resolve,
|
||||
};
|
||||
};
|
||||
|
||||
const response = new packet(this.password);
|
||||
response.buffer = new Uint8Array(packet_data);
|
||||
response
|
||||
.decrypt()
|
||||
.then(() => {
|
||||
this.nonce = response.decode_utf8();
|
||||
const { resolve } = complete();
|
||||
if (resolve) {
|
||||
resolve(response);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
const { reject } = complete();
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('error', (e) => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log(e);
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('close', () => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log('socket closed');
|
||||
if (reject) {
|
||||
reject(new Error('socket closed'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
try {
|
||||
if (this.socket) {
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
this.cleanup_handlers();
|
||||
this.connected = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
packet.token = this.password;
|
||||
packet.encode_top_utf8(method_name);
|
||||
packet.encode_top_ui64(optional_thread_id || 1);
|
||||
packet.encode_top_utf8(constants.instance_id);
|
||||
packet.encode_top_ui32(0); // Service flags
|
||||
packet.encode_top_utf8(constants.get_version());
|
||||
packet.encode_top_utf8(nonce);
|
||||
await packet.encrypt();
|
||||
packet.encode_top_ui32(packet.buffer.length);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
this.socket.write(Buffer.from(packet.buffer), null, (err) => {
|
||||
if (err) {
|
||||
this.cleanup_handlers();
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
import Pool from 'socket-pool';
|
||||
|
||||
import connection from './connection';
|
||||
|
||||
export default class connection_pool {
|
||||
constructor(pool_size, host_or_ip, port, password) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (pool_size > 1) {
|
||||
this.pool = new Pool({
|
||||
connect: { host: host_or_ip, port: port },
|
||||
connectTimeout: 5000,
|
||||
pool: { max: pool_size, min: 2 },
|
||||
});
|
||||
} else {
|
||||
throw new Error("'pool_size' must be > 1");
|
||||
}
|
||||
}
|
||||
|
||||
host_or_ip = '';
|
||||
next_thread_id = 1;
|
||||
password = '';
|
||||
port = 20000;
|
||||
pool;
|
||||
shutdown = false;
|
||||
|
||||
async disconnect() {
|
||||
await this.pool._pool.drain();
|
||||
await this.pool._pool.clear();
|
||||
this.pool = null;
|
||||
this.shutdown = true;
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
try {
|
||||
const socket = await this.pool.acquire();
|
||||
if (!socket.thread_id) {
|
||||
socket.thread_id = this.next_thread_id++;
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
try {
|
||||
socket.release();
|
||||
} catch (err) {
|
||||
console.log(`'release()' failed: ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await new connection(
|
||||
this.host_or_ip,
|
||||
this.port,
|
||||
this.password,
|
||||
socket
|
||||
).send(method_name, packet, optional_thread_id || socket.thread_id);
|
||||
cleanup();
|
||||
return result;
|
||||
} catch (err) {
|
||||
cleanup();
|
||||
return Promise.reject(
|
||||
new Error(`'send(${method_name})' failed: ${err}`)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'acquire()' socket failed: ${err}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
import Pool from 'socket-pool';
|
||||
|
||||
import connection from './connection';
|
||||
|
||||
export default class connection_pool {
|
||||
constructor(pool_size, host_or_ip, port, password) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (pool_size > 1) {
|
||||
this.pool = new Pool({
|
||||
connect: { host: host_or_ip, port: port },
|
||||
connectTimeout: 5000,
|
||||
pool: { max: pool_size, min: 2 },
|
||||
});
|
||||
} else {
|
||||
throw new Error("'pool_size' must be > 1");
|
||||
}
|
||||
}
|
||||
|
||||
host_or_ip = '';
|
||||
next_thread_id = 1;
|
||||
password = '';
|
||||
port = 20000;
|
||||
pool;
|
||||
shutdown = false;
|
||||
|
||||
async disconnect() {
|
||||
await this.pool._pool.drain();
|
||||
await this.pool._pool.clear();
|
||||
this.pool = null;
|
||||
this.shutdown = true;
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
try {
|
||||
const socket = await this.pool.acquire();
|
||||
if (!socket.thread_id) {
|
||||
socket.thread_id = this.next_thread_id++;
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
try {
|
||||
socket.release();
|
||||
} catch (err) {
|
||||
console.log(`'release()' failed: ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await new connection(
|
||||
this.host_or_ip,
|
||||
this.port,
|
||||
this.password,
|
||||
socket
|
||||
).send(method_name, packet, optional_thread_id || socket.thread_id);
|
||||
cleanup();
|
||||
return result;
|
||||
} catch (err) {
|
||||
cleanup();
|
||||
return Promise.reject(
|
||||
new Error(`'send(${method_name})' failed: ${err}`)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'acquire()' socket failed: ${err}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,330 +1,330 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
import { Int64BE, Uint64BE } from 'int64-buffer';
|
||||
import crypto from 'crypto';
|
||||
import { TextEncoder } from 'text-encoding';
|
||||
import { getCustomEncryption } from '../utils/constants';
|
||||
import {
|
||||
be_ui8_array_to_i16,
|
||||
be_ui8_array_to_i32,
|
||||
be_ui8_array_to_ui16,
|
||||
be_ui8_array_to_ui32,
|
||||
i16_to_be_ui8_array,
|
||||
i32_to_be_ui8_array,
|
||||
i8_to_ui8_array,
|
||||
ui16_to_be_ui8_array,
|
||||
ui32_to_be_ui8_array,
|
||||
ui8_array_to_i8,
|
||||
ui8_array_to_ui8,
|
||||
ui8_to_ui8_array,
|
||||
} from '../utils/byte_order';
|
||||
import { XChaCha20Poly1305 } from '@stablelib/xchacha20poly1305';
|
||||
|
||||
export default class packet {
|
||||
constructor(token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
buffer = new Uint8Array(0);
|
||||
decode_offset = 0;
|
||||
token;
|
||||
|
||||
append_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...this.buffer, ...buffer])
|
||||
: buffer;
|
||||
};
|
||||
|
||||
clear = () => {
|
||||
this.buffer = null;
|
||||
this.decode_offset = 0;
|
||||
};
|
||||
|
||||
decode_buffer = (length) => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const ret = this.buffer.slice(
|
||||
this.decode_offset,
|
||||
this.decode_offset + length
|
||||
);
|
||||
this.decode_offset += length;
|
||||
return Buffer.from(ret);
|
||||
};
|
||||
|
||||
decode_stat = () => {
|
||||
const mode = this.decode_ui16();
|
||||
const nlink = this.decode_ui16();
|
||||
const uid = this.decode_ui32();
|
||||
const gid = this.decode_ui32();
|
||||
const atime = this.decode_ui64();
|
||||
const mtime = this.decode_ui64();
|
||||
const ctime = this.decode_ui64();
|
||||
const birth_time = this.decode_ui64();
|
||||
const size = this.decode_ui64();
|
||||
const blocks = this.decode_ui64();
|
||||
const blksize = this.decode_ui32();
|
||||
const flags = this.decode_ui32();
|
||||
const directory = !!this.decode_ui8();
|
||||
return {
|
||||
mode,
|
||||
nlink,
|
||||
uid,
|
||||
gid,
|
||||
atime,
|
||||
mtime,
|
||||
ctime,
|
||||
birth_time,
|
||||
size,
|
||||
blocks,
|
||||
blksize,
|
||||
flags,
|
||||
directory,
|
||||
};
|
||||
};
|
||||
|
||||
decode_utf8 = () => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const startIndex = this.decode_offset;
|
||||
const endIndex = this.buffer.indexOf(0, startIndex);
|
||||
if (endIndex >= 0) {
|
||||
let ret = '';
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
ret += String.fromCharCode(this.buffer[i]);
|
||||
}
|
||||
this.decode_offset = endIndex + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
throw new Error('String not found in buffer');
|
||||
};
|
||||
|
||||
decode_i8 = () => {
|
||||
return ui8_array_to_i8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_ui8 = () => {
|
||||
return ui8_array_to_ui8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_i16 = () => {
|
||||
const ret = be_ui8_array_to_i16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui16 = () => {
|
||||
const ret = be_ui8_array_to_ui16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i32 = () => {
|
||||
const ret = be_ui8_array_to_i32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui32 = () => {
|
||||
const ret = be_ui8_array_to_ui32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i64 = () => {
|
||||
const ret = new Int64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decode_ui64 = () => {
|
||||
const ret = new Uint64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decrypt = async () => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
const nonce = this.buffer.slice(0, 24);
|
||||
const mac = this.buffer.slice(24, 16 + 24);
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = Buffer.from(
|
||||
await customEncryption.decrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(mac).toString('base64'),
|
||||
Buffer.from(this.buffer.slice(40)).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(nonce.length + mac.length),
|
||||
...this.buffer.slice(nonce.length, nonce.length + mac.length),
|
||||
]);
|
||||
const result = new XChaCha20Poly1305(key).open(nonce, this.buffer, aad);
|
||||
if (!result) {
|
||||
throw new Error('decryption failed');
|
||||
}
|
||||
this.buffer = Buffer.from(result);
|
||||
}
|
||||
|
||||
this.buffer = new Uint8Array(this.buffer);
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
encode_buffer = (buffer) => {
|
||||
this.append_buffer(new Uint8Array(buffer));
|
||||
};
|
||||
|
||||
encode_i8 = (num) => {
|
||||
this.append_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i8 = (num) => {
|
||||
this.push_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_u8 = (num) => {
|
||||
this.append_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_u8 = (num) => {
|
||||
this.push_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i16 = (num) => {
|
||||
this.append_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i16 = (num) => {
|
||||
this.push_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui16 = (num) => {
|
||||
this.append_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui16 = (num) => {
|
||||
this.push_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i32 = (num) => {
|
||||
this.append_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i32 = (num) => {
|
||||
this.push_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui32 = (num) => {
|
||||
this.append_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui32 = (num) => {
|
||||
this.push_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_i64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_ui64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_ui64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.append_buffer(buffer);
|
||||
};
|
||||
|
||||
encode_top_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.push_buffer(buffer);
|
||||
};
|
||||
|
||||
encrypt = async (nonce) => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
if (!nonce) {
|
||||
nonce = Uint8Array.from(randomBytes(24));
|
||||
}
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = new Uint8Array(
|
||||
Buffer.from(
|
||||
await customEncryption.encrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(this.buffer).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length + 40);
|
||||
this.buffer = new XChaCha20Poly1305(key).seal(nonce, this.buffer, aad);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(this.buffer.length - 16),
|
||||
...this.buffer.slice(0, this.buffer.length - 16),
|
||||
]);
|
||||
this.push_buffer(nonce);
|
||||
}
|
||||
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
push_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...buffer, ...this.buffer])
|
||||
: buffer;
|
||||
};
|
||||
}
|
||||
import { randomBytes } from 'crypto';
|
||||
import { Int64BE, Uint64BE } from 'int64-buffer';
|
||||
import crypto from 'crypto';
|
||||
import { TextEncoder } from 'text-encoding';
|
||||
import { getCustomEncryption } from '../utils/constants';
|
||||
import {
|
||||
be_ui8_array_to_i16,
|
||||
be_ui8_array_to_i32,
|
||||
be_ui8_array_to_ui16,
|
||||
be_ui8_array_to_ui32,
|
||||
i16_to_be_ui8_array,
|
||||
i32_to_be_ui8_array,
|
||||
i8_to_ui8_array,
|
||||
ui16_to_be_ui8_array,
|
||||
ui32_to_be_ui8_array,
|
||||
ui8_array_to_i8,
|
||||
ui8_array_to_ui8,
|
||||
ui8_to_ui8_array,
|
||||
} from '../utils/byte_order';
|
||||
import { XChaCha20Poly1305 } from '@stablelib/xchacha20poly1305';
|
||||
|
||||
export default class packet {
|
||||
constructor(token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
buffer = new Uint8Array(0);
|
||||
decode_offset = 0;
|
||||
token;
|
||||
|
||||
append_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...this.buffer, ...buffer])
|
||||
: buffer;
|
||||
};
|
||||
|
||||
clear = () => {
|
||||
this.buffer = null;
|
||||
this.decode_offset = 0;
|
||||
};
|
||||
|
||||
decode_buffer = (length) => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const ret = this.buffer.slice(
|
||||
this.decode_offset,
|
||||
this.decode_offset + length
|
||||
);
|
||||
this.decode_offset += length;
|
||||
return Buffer.from(ret);
|
||||
};
|
||||
|
||||
decode_stat = () => {
|
||||
const mode = this.decode_ui16();
|
||||
const nlink = this.decode_ui16();
|
||||
const uid = this.decode_ui32();
|
||||
const gid = this.decode_ui32();
|
||||
const atime = this.decode_ui64();
|
||||
const mtime = this.decode_ui64();
|
||||
const ctime = this.decode_ui64();
|
||||
const birth_time = this.decode_ui64();
|
||||
const size = this.decode_ui64();
|
||||
const blocks = this.decode_ui64();
|
||||
const blksize = this.decode_ui32();
|
||||
const flags = this.decode_ui32();
|
||||
const directory = !!this.decode_ui8();
|
||||
return {
|
||||
mode,
|
||||
nlink,
|
||||
uid,
|
||||
gid,
|
||||
atime,
|
||||
mtime,
|
||||
ctime,
|
||||
birth_time,
|
||||
size,
|
||||
blocks,
|
||||
blksize,
|
||||
flags,
|
||||
directory,
|
||||
};
|
||||
};
|
||||
|
||||
decode_utf8 = () => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const startIndex = this.decode_offset;
|
||||
const endIndex = this.buffer.indexOf(0, startIndex);
|
||||
if (endIndex >= 0) {
|
||||
let ret = '';
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
ret += String.fromCharCode(this.buffer[i]);
|
||||
}
|
||||
this.decode_offset = endIndex + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
throw new Error('String not found in buffer');
|
||||
};
|
||||
|
||||
decode_i8 = () => {
|
||||
return ui8_array_to_i8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_ui8 = () => {
|
||||
return ui8_array_to_ui8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_i16 = () => {
|
||||
const ret = be_ui8_array_to_i16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui16 = () => {
|
||||
const ret = be_ui8_array_to_ui16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i32 = () => {
|
||||
const ret = be_ui8_array_to_i32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui32 = () => {
|
||||
const ret = be_ui8_array_to_ui32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i64 = () => {
|
||||
const ret = new Int64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decode_ui64 = () => {
|
||||
const ret = new Uint64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decrypt = async () => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
const nonce = this.buffer.slice(0, 24);
|
||||
const mac = this.buffer.slice(24, 16 + 24);
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = Buffer.from(
|
||||
await customEncryption.decrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(mac).toString('base64'),
|
||||
Buffer.from(this.buffer.slice(40)).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(nonce.length + mac.length),
|
||||
...this.buffer.slice(nonce.length, nonce.length + mac.length),
|
||||
]);
|
||||
const result = new XChaCha20Poly1305(key).open(nonce, this.buffer, aad);
|
||||
if (!result) {
|
||||
throw new Error('decryption failed');
|
||||
}
|
||||
this.buffer = Buffer.from(result);
|
||||
}
|
||||
|
||||
this.buffer = new Uint8Array(this.buffer);
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
encode_buffer = (buffer) => {
|
||||
this.append_buffer(new Uint8Array(buffer));
|
||||
};
|
||||
|
||||
encode_i8 = (num) => {
|
||||
this.append_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i8 = (num) => {
|
||||
this.push_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_u8 = (num) => {
|
||||
this.append_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_u8 = (num) => {
|
||||
this.push_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i16 = (num) => {
|
||||
this.append_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i16 = (num) => {
|
||||
this.push_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui16 = (num) => {
|
||||
this.append_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui16 = (num) => {
|
||||
this.push_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i32 = (num) => {
|
||||
this.append_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i32 = (num) => {
|
||||
this.push_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui32 = (num) => {
|
||||
this.append_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui32 = (num) => {
|
||||
this.push_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_i64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_ui64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_ui64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.append_buffer(buffer);
|
||||
};
|
||||
|
||||
encode_top_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.push_buffer(buffer);
|
||||
};
|
||||
|
||||
encrypt = async (nonce) => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
if (!nonce) {
|
||||
nonce = Uint8Array.from(randomBytes(24));
|
||||
}
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = new Uint8Array(
|
||||
Buffer.from(
|
||||
await customEncryption.encrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(this.buffer).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length + 40);
|
||||
this.buffer = new XChaCha20Poly1305(key).seal(nonce, this.buffer, aad);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(this.buffer.length - 16),
|
||||
...this.buffer.slice(0, this.buffer.length - 16),
|
||||
]);
|
||||
this.push_buffer(nonce);
|
||||
}
|
||||
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
push_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...buffer, ...this.buffer])
|
||||
: buffer;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user