Add a basic server

parent aea9d8b8
...@@ -355,7 +355,7 @@ class CFLBinaryPListParser { ...@@ -355,7 +355,7 @@ class CFLBinaryPListParser {
throw new Error('Expected count to be a packed int object'); 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]; return [object_info, data];
......
#!/usr/bin/env node #!/usr/bin/env node
import Client from './client'; import Client from './client';
import Server from './server';
import Property from './property'; import Property from './property';
import yargs from 'yargs'; import yargs from 'yargs';
...@@ -26,13 +27,17 @@ yargs.command('version', 'Shows the node-acp version', () => {}, argv => { ...@@ -26,13 +27,17 @@ yargs.command('version', 'Shows the node-acp version', () => {}, argv => {
console.log('node-acp v' + require('../package').version); console.log('node-acp v' + require('../package').version);
}); });
async function getClient(argv) { yargs.command('server', 'Start the ACP server', yargs => {}, async argv => {
const client = new Client(argv.host, argv.port, argv.password); 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 commandHandler = handler => async argv => {
const client = new Client(argv.host, argv.port, argv.password); const client = new Client(argv.host, argv.port, argv.password);
......
...@@ -70,7 +70,7 @@ export default class Message { ...@@ -70,7 +70,7 @@ export default class Message {
const magic = buffer.slice(0, 4).toString(); const magic = buffer.slice(0, 4).toString();
const version = buffer.readInt32BE(4); const version = buffer.readInt32BE(4);
const header_checksum = buffer.readUInt32BE(8); 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 body_size = buffer.readInt32BE(16);
const flags = buffer.readInt32BE(20); const flags = buffer.readInt32BE(20);
const unused = buffer.readInt32BE(24); const unused = buffer.readInt32BE(24);
...@@ -90,7 +90,7 @@ export default class Message { ...@@ -90,7 +90,7 @@ export default class Message {
buffer.write(magic, 0, 4); buffer.write(magic, 0, 4);
buffer.writeInt32BE(version, 4); buffer.writeInt32BE(version, 4);
buffer.writeUInt32BE(header_checksum, 8); buffer.writeUInt32BE(header_checksum, 8);
buffer.writeInt32BE(body_checksum, 12); buffer.writeUInt32BE(body_checksum, 12);
buffer.writeInt32BE(body_size, 16); buffer.writeInt32BE(body_size, 16);
buffer.writeInt32BE(flags, 20); buffer.writeInt32BE(flags, 20);
buffer.writeInt32BE(unused, 24); buffer.writeInt32BE(unused, 24);
...@@ -204,8 +204,8 @@ export default class Message { ...@@ -204,8 +204,8 @@ export default class Message {
return message.composeRawPacket(); return message.composeRawPacket();
} }
static composeAuthCommand(flags, payload) { static composeAuthCommand(flags, password, payload) {
const message = new Message(0x00030001, flags, 0, 0x1a, 0, generateACPHeaderKey(''), payload); const message = new Message(0x00030001, flags, 0, 0x1a, 0, generateACPHeaderKey(password), payload);
return message.composeRawPacket(); 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 {ClientEncryption, ServerEncryption} from './encryption';
import net from 'net'; import net from 'net';
...@@ -53,6 +55,25 @@ export default class Session { ...@@ -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) { async sendAndReceive(data, size, timeout = 10000) {
await this.send(data); 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