Add a basic server

parent aea9d8b8
......@@ -355,7 +355,7 @@ class CFLBinaryPListParser {
throw new Error('Expected count to be a packed int object');
}
return this.unpackInt(count_object_type, data);
return this.unpackInt(count_object_info, data);
}
return [object_info, data];
......
#!/usr/bin/env node
import Client from './client';
import Server from './server';
import Property from './property';
import yargs from 'yargs';
......@@ -26,13 +27,17 @@ yargs.command('version', 'Shows the node-acp version', () => {}, argv => {
console.log('node-acp v' + require('../package').version);
});
async function getClient(argv) {
const client = new Client(argv.host, argv.port, argv.password);
yargs.command('server', 'Start the ACP server', yargs => {}, async argv => {
const server = new Server(argv.host, argv.port, argv.password);
await client.connect();
try {
await server.listen();
} catch (err) {
console.error(err);
}
return client;
}
// Leave the server to run
});
const commandHandler = handler => async argv => {
const client = new Client(argv.host, argv.port, argv.password);
......
......@@ -70,7 +70,7 @@ export default class Message {
const magic = buffer.slice(0, 4).toString();
const version = buffer.readInt32BE(4);
const header_checksum = buffer.readUInt32BE(8);
const body_checksum = buffer.readInt32BE(12);
const body_checksum = buffer.readUInt32BE(12);
const body_size = buffer.readInt32BE(16);
const flags = buffer.readInt32BE(20);
const unused = buffer.readInt32BE(24);
......@@ -90,7 +90,7 @@ export default class Message {
buffer.write(magic, 0, 4);
buffer.writeInt32BE(version, 4);
buffer.writeUInt32BE(header_checksum, 8);
buffer.writeInt32BE(body_checksum, 12);
buffer.writeUInt32BE(body_checksum, 12);
buffer.writeInt32BE(body_size, 16);
buffer.writeInt32BE(flags, 20);
buffer.writeInt32BE(unused, 24);
......@@ -204,8 +204,8 @@ export default class Message {
return message.composeRawPacket();
}
static composeAuthCommand(flags, payload) {
const message = new Message(0x00030001, flags, 0, 0x1a, 0, generateACPHeaderKey(''), payload);
static composeAuthCommand(flags, password, payload) {
const message = new Message(0x00030001, flags, 0, 0x1a, 0, generateACPHeaderKey(password), payload);
return message.composeRawPacket();
}
......
import Session from './session';
import Message, {HEADER_SIZE as MESSAGE_HEADER_SIZE} from './message';
import Property, {HEADER_SIZE as ELEMENT_HEADER_SIZE} from './property';
import CFLBinaryPList from './cflbinary';
import net from 'net';
export default class Server {
constructor(host, port, password) {
this.host = host;
this.port = port;
this.password = password;
this.socket = undefined;
}
listen(_timeout) {
return new Promise((resolve, reject) => {
this.socket = new net.Server();
setTimeout(() => {
this.reading -= 1;
reject('Timeout');
}, _timeout);
this.socket.listen(this.port, this.host, err => {
console.log('Connected', err);
if (err) reject(err);
else resolve();
});
this.socket.on('close', had_error => {
this.socket = undefined;
});
this.socket.on('connection', connection => {
this.handleConnection(connection);
});
});
}
close() {
if (!this.socket) return;
this.socket.end();
return new Promise((resolve, reject) => {
this.socket.on('close', resolve);
});
}
handleConnection(socket) {
const session = new Session(socket.remoteAddress, socket.remotePort, this.password);
session.socket = socket;
socket.on('data', data => {
console.debug(0, 'Receiving data', data, 'on connection', session.host + ' port ' + session.port);
if (session.reading) return;
session.buffer += data.toString('binary');
// Try decoding the data as a message
if (session.buffer.length >= MESSAGE_HEADER_SIZE) {
this.tryHandleMessage(session);
}
});
}
async tryHandleMessage(session, buffer) {
try {
const message = await Message.parseRaw(session.buffer);
if (message.body.length !== message.body_size) {
// Haven't received the message body yet
return;
}
// session.buffer = session.buffer.substr(MESSAGE_HEADER_SIZE);
this.handleMessage(session, message);
} catch (err) {
console.error(session, err);
}
}
async handleMessage(session, message) {
console.log('Received message', message);
switch (message.command) {
// Get prop
case 0x14: {
let data = message.body;
const props = [];
// Read the requested props into an array of Propertys
while (data.length) {
const prop_header = data.substr(0, ELEMENT_HEADER_SIZE);
const prop_data = await Property.parseRawElementHeader(prop_header);
console.debug(prop_data);
const {name, flags, size} = prop_data;
const value = data.substr(0, ELEMENT_HEADER_SIZE + size);
data = data.substr(ELEMENT_HEADER_SIZE + size);
const prop = new Property(name, value);
if (typeof prop.name === 'undefined' && typeof prop.value === 'undefined') {
break;
}
props.push(prop);
}
// Send back an array of Propertys
const ret = this.getProperties(props);
let payload = '', i = 0;
for (let prop of ret) {
payload += Property.composeRawElement(0, prop instanceof Property ? prop : new Property(props[i], prop));
i++;
}
const response = Message.composeGetPropCommand(4, this.password, payload);
return;
}
}
}
getProperties(props) {
return props.map(prop => this.getProperty(prop));
}
getProperty(prop) {
if (prop.name === 'dbug') return new Property('dbug', 0x3000);
}
}
import Message, {HEADER_SIZE as MESSAGE_HEADER_SIZE} from './message';
import Property, {HEADER_SIZE as ELEMENT_HEADER_SIZE} from './property';
// import {ClientEncryption, ServerEncryption} from './encryption';
import net from 'net';
......@@ -53,6 +55,25 @@ export default class Session {
});
}
async receiveMessage(timeout) {
const raw_header = await this.receiveMessageHeader(timeout);
const message = await Message.parseRaw(raw_header);
const data = await this.receive(message.body_size);
message.body = data;
return message;
}
receiveMessageHeader(timeout) {
return this.receive(MESSAGE_HEADER_SIZE, timeout);
}
receivePropertyElementHeader(timeout) {
return this.receive(ELEMENT_HEADER_SIZE, timeout);
}
async sendAndReceive(data, size, timeout = 10000) {
await this.send(data);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment