mirror of
https://github.com/amigan/aim-oscar-server.git
synced 2024-11-21 20:19:47 -05:00
split auth and chat servers
This commit is contained in:
parent
aad0acfd15
commit
19e26cddbb
15 changed files with 175 additions and 97 deletions
|
@ -5,7 +5,8 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:tsc": "tsc --watch",
|
"dev:tsc": "tsc --watch",
|
||||||
"dev:nodemon": "nodemon --watch ./dist --delay 200ms",
|
"dev:nodemon:auth": "nodemon --watch ./dist --delay 200ms dist/src/main-auth.js",
|
||||||
|
"dev:nodemon:chat": "nodemon --watch ./dist --delay 200ms dist/src/main-chat.js",
|
||||||
"start": "tsc && node ./dist/index.js"
|
"start": "tsc && node ./dist/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,24 +1,6 @@
|
||||||
import net from "net";
|
import net from "net";
|
||||||
import { FLAP, SNAC, TLV, TLVType } from './structures';
|
import { FLAP, SNAC, TLV, TLVType } from './structures';
|
||||||
import { logDataStream } from './util';
|
import { logDataStream } from './util';
|
||||||
import { FLAGS_EMPTY } from './consts';
|
|
||||||
|
|
||||||
import GenericServiceControls from "./services/0x01-GenericServiceControls";
|
|
||||||
import LocationServices from "./services/0x02-LocationSerices";
|
|
||||||
import BuddyListManagement from "./services/0x03-BuddyListManagement";
|
|
||||||
import ICBM from "./services/0x04-ICBM";
|
|
||||||
import Invitation from "./services/0x06-Invitation";
|
|
||||||
import Administration from "./services/0x07-Administration";
|
|
||||||
import Popups from "./services/0x08-Popups";
|
|
||||||
import PrivacyManagement from "./services/0x09-PrivacyManagement";
|
|
||||||
import UserLookup from "./services/0x0a-UserLookup";
|
|
||||||
import UsageStats from "./services/0x0b-UsageStats";
|
|
||||||
import ChatNavigation from "./services/0x0d-ChatNavigation";
|
|
||||||
import Chat from "./services/0x0e-Chat";;
|
|
||||||
import DirectorySearch from "./services/0x0f-DirectorySearch";
|
|
||||||
import ServerStoredBuddyIcons from "./services/0x10-ServerStoredBuddyIcons";
|
|
||||||
import SSI from "./services/0x13-SSI";
|
|
||||||
import AuthorizationRegistrationService from "./services/0x17-AuthorizationRegistration";
|
|
||||||
|
|
||||||
import BaseService from "./services/base";
|
import BaseService from "./services/base";
|
||||||
|
|
||||||
|
@ -30,15 +12,15 @@ export interface User {
|
||||||
|
|
||||||
export default class Communicator {
|
export default class Communicator {
|
||||||
|
|
||||||
|
private keepaliveInterval? : NodeJS.Timer;
|
||||||
private _sequenceNumber = 0;
|
private _sequenceNumber = 0;
|
||||||
private messageBuffer = Buffer.alloc(0);
|
private messageBuffer = Buffer.alloc(0);
|
||||||
public services : {[key: number]: BaseService} = {};
|
public services : {[key: number]: BaseService} = {};
|
||||||
public user? : User;
|
public user? : User;
|
||||||
|
|
||||||
constructor(public socket : net.Socket) {
|
constructor(public socket : net.Socket) {}
|
||||||
// Hold on to the socket
|
|
||||||
this.socket = socket;
|
|
||||||
|
|
||||||
|
startListening() {
|
||||||
this.socket.on('data', (data : Buffer) => {
|
this.socket.on('data', (data : Buffer) => {
|
||||||
// we could get multiple FLAP messages, keep a running buffer of incoming
|
// we could get multiple FLAP messages, keep a running buffer of incoming
|
||||||
// data and shift-off however many successful FLAPs we can make
|
// data and shift-off however many successful FLAPs we can make
|
||||||
|
@ -60,36 +42,23 @@ export default class Communicator {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.registerServices();
|
this.keepaliveInterval = setInterval(() => {
|
||||||
this.start();
|
const keepaliveFlap = new FLAP(0x05, this.nextReqID, Buffer.from(""));
|
||||||
}
|
this.socket.write(keepaliveFlap.toBuffer());
|
||||||
|
}, 4 * 60 * 1000);
|
||||||
|
|
||||||
|
this.socket.on('close', () => {
|
||||||
|
if (this.keepaliveInterval) {
|
||||||
|
clearInterval(this.keepaliveInterval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
start() {
|
|
||||||
// Start negotiating a connection
|
// Start negotiating a connection
|
||||||
const hello = new FLAP(0x01, 0, Buffer.from([0x00, 0x00, 0x00, 0x01]));
|
const hello = new FLAP(0x01, 0, Buffer.from([0x00, 0x00, 0x00, 0x01]));
|
||||||
this.send(hello);
|
this.send(hello);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerServices() {
|
registerServices(services : BaseService[] = []) {
|
||||||
const services = [
|
|
||||||
new GenericServiceControls(this),
|
|
||||||
new LocationServices(this),
|
|
||||||
new BuddyListManagement(this),
|
|
||||||
new ICBM(this),
|
|
||||||
new Invitation(this),
|
|
||||||
new Administration(this),
|
|
||||||
new Popups(this),
|
|
||||||
new PrivacyManagement(this),
|
|
||||||
new UserLookup(this),
|
|
||||||
new UsageStats(this),
|
|
||||||
new ChatNavigation(this),
|
|
||||||
new Chat(this),
|
|
||||||
new DirectorySearch(this),
|
|
||||||
new ServerStoredBuddyIcons(this),
|
|
||||||
// new SSI(this),
|
|
||||||
new AuthorizationRegistrationService(this),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Make a map of the service number to the service handler
|
// Make a map of the service number to the service handler
|
||||||
this.services = {};
|
this.services = {};
|
||||||
services.forEach((service) => {
|
services.forEach((service) => {
|
||||||
|
@ -97,8 +66,8 @@ export default class Communicator {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNewSequenceNumber() {
|
get nextReqID() {
|
||||||
return ++this._sequenceNumber;
|
return ++this._sequenceNumber & 0xFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
send(message : FLAP) {
|
send(message : FLAP) {
|
||||||
|
@ -136,8 +105,8 @@ export default class Communicator {
|
||||||
Object.values(this.services).forEach((subtype) => {
|
Object.values(this.services).forEach((subtype) => {
|
||||||
servicesOffered.push(Buffer.from([0x00, subtype.service]));
|
servicesOffered.push(Buffer.from([0x00, subtype.service]));
|
||||||
});
|
});
|
||||||
const resp = new FLAP(2, this._getNewSequenceNumber(),
|
const resp = new FLAP(2, this.nextReqID,
|
||||||
new SNAC(0x01, 0x03, FLAGS_EMPTY, 0, Buffer.concat(servicesOffered)));
|
new SNAC(0x01, 0x03, Buffer.concat(servicesOffered)));
|
||||||
this.send(resp);
|
this.send(resp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
21
src/haxor-proxy.ts
Normal file
21
src/haxor-proxy.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import net from "net";
|
||||||
|
|
||||||
|
const server = net.createServer((socket) => {
|
||||||
|
socket.setTimeout(5 * 60 * 1000); // 5 minute timeout
|
||||||
|
socket.on('timeout', () => {
|
||||||
|
console.log('socket timeout');
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('end', () => {
|
||||||
|
console.log('client disconnected...');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', (err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(9999, () => {
|
||||||
|
console.log('proxy ready');
|
||||||
|
})
|
|
@ -1,9 +1,11 @@
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import Communicator from './communicator';
|
import Communicator from './communicator';
|
||||||
|
|
||||||
|
import AuthorizationRegistrationService from "./services/0x17-AuthorizationRegistration";
|
||||||
|
|
||||||
const server = net.createServer((socket) => {
|
const server = net.createServer((socket) => {
|
||||||
console.log('client connected...');
|
console.log('client connected...');
|
||||||
socket.setTimeout(30000);
|
socket.setTimeout(5 * 60 * 1000); // 5 minute timeout
|
||||||
|
|
||||||
socket.on('error', (e) => {
|
socket.on('error', (e) => {
|
||||||
console.error('socket encountered an error:', e);
|
console.error('socket encountered an error:', e);
|
||||||
|
@ -19,7 +21,12 @@ const server = net.createServer((socket) => {
|
||||||
console.log('client disconnected...');
|
console.log('client disconnected...');
|
||||||
});
|
});
|
||||||
|
|
||||||
new Communicator(socket);
|
const comm = new Communicator(socket);
|
||||||
|
const services = [
|
||||||
|
new AuthorizationRegistrationService(comm),
|
||||||
|
];
|
||||||
|
comm.registerServices(services);
|
||||||
|
comm.startListening();
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on('error', (err) => {
|
server.on('error', (err) => {
|
||||||
|
@ -27,5 +34,5 @@ server.on('error', (err) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(5190, () => {
|
server.listen(5190, () => {
|
||||||
console.log('OSCAR ready on :5190');
|
console.log('AUTH ready on :5190');
|
||||||
});
|
});
|
66
src/main-chat.ts
Normal file
66
src/main-chat.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import net from 'net';
|
||||||
|
import Communicator from './communicator';
|
||||||
|
|
||||||
|
import GenericServiceControls from "./services/0x01-GenericServiceControls";
|
||||||
|
import LocationServices from "./services/0x02-LocationSerices";
|
||||||
|
import BuddyListManagement from "./services/0x03-BuddyListManagement";
|
||||||
|
import ICBM from "./services/0x04-ICBM";
|
||||||
|
import Invitation from "./services/0x06-Invitation";
|
||||||
|
import Administration from "./services/0x07-Administration";
|
||||||
|
import Popups from "./services/0x08-Popups";
|
||||||
|
import PrivacyManagement from "./services/0x09-PrivacyManagement";
|
||||||
|
import UserLookup from "./services/0x0a-UserLookup";
|
||||||
|
import UsageStats from "./services/0x0b-UsageStats";
|
||||||
|
import ChatNavigation from "./services/0x0d-ChatNavigation";
|
||||||
|
import Chat from "./services/0x0e-Chat";;
|
||||||
|
import DirectorySearch from "./services/0x0f-DirectorySearch";
|
||||||
|
import ServerStoredBuddyIcons from "./services/0x10-ServerStoredBuddyIcons";
|
||||||
|
import SSI from "./services/0x13-SSI";
|
||||||
|
|
||||||
|
const server = net.createServer((socket) => {
|
||||||
|
console.log('client connected...');
|
||||||
|
socket.setTimeout(5 * 60 * 1000); // 5 minute timeout
|
||||||
|
|
||||||
|
socket.on('error', (e) => {
|
||||||
|
console.error('socket encountered an error:', e);
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('timeout', () => {
|
||||||
|
console.log('socket timeout');
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('end', () => {
|
||||||
|
console.log('client disconnected...');
|
||||||
|
});
|
||||||
|
|
||||||
|
const comm = new Communicator(socket);
|
||||||
|
const services = [
|
||||||
|
new GenericServiceControls(comm),
|
||||||
|
new LocationServices(comm),
|
||||||
|
new BuddyListManagement(comm),
|
||||||
|
new ICBM(comm),
|
||||||
|
new Invitation(comm),
|
||||||
|
new Administration(comm),
|
||||||
|
new Popups(comm),
|
||||||
|
new PrivacyManagement(comm),
|
||||||
|
new UserLookup(comm),
|
||||||
|
new UsageStats(comm),
|
||||||
|
new ChatNavigation(comm),
|
||||||
|
new Chat(comm),
|
||||||
|
new DirectorySearch(comm),
|
||||||
|
new ServerStoredBuddyIcons(comm),
|
||||||
|
new SSI(comm),
|
||||||
|
];
|
||||||
|
comm.registerServices(services);
|
||||||
|
comm.startListening();
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', (err) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(5191, () => {
|
||||||
|
console.log('CHAT ready on :5191');
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
import BaseService from './base';
|
import BaseService from './base';
|
||||||
import Communicator from '../communicator';
|
import Communicator from '../communicator';
|
||||||
import { FLAP, Rate, RateClass, RatedServiceGroup, RateGroupPair, SNAC, TLV } from '../structures';
|
import { FLAP, Rate, RateClass, RatedServiceGroup, RateGroupPair, SNAC, TLV } from '../structures';
|
||||||
import { FLAGS_EMPTY, USER_STATUS_VARIOUS, USER_STATUS } from '../consts';
|
import { USER_STATUS_VARIOUS, USER_STATUS } from '../consts';
|
||||||
import { char, word, dword, dot2num } from '../structures/bytes';
|
import { char, word, dword, dot2num } from '../structures/bytes';
|
||||||
|
|
||||||
export default class GenericServiceControls extends BaseService {
|
export default class GenericServiceControls extends BaseService {
|
||||||
|
@ -31,8 +31,8 @@ export default class GenericServiceControls extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.payload.subtype === 0x06) { // Client ask server for rate limits info
|
if (message.payload.subtype === 0x06) { // Client ask server for rate limits info
|
||||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
const resp = new FLAP(0x02, this.nextReqID,
|
||||||
SNAC.forRateClass(0x01, 0x07, FLAGS_EMPTY, 0, [
|
SNAC.forRateClass(0x01, 0x07, [
|
||||||
new Rate(
|
new Rate(
|
||||||
new RateClass(1, 80, 2500, 2000, 1500, 800, 3400 /*fake*/, 6000, 0, 0),
|
new RateClass(1, 80, 2500, 2000, 1500, 800, 3400 /*fake*/, 6000, 0, 0),
|
||||||
new RatedServiceGroup(1, [new RateGroupPair(0x00, 0x00)])
|
new RatedServiceGroup(1, [new RateGroupPair(0x00, 0x00)])
|
||||||
|
@ -40,8 +40,8 @@ export default class GenericServiceControls extends BaseService {
|
||||||
]))
|
]))
|
||||||
this.send(resp);
|
this.send(resp);
|
||||||
|
|
||||||
const motd = new FLAP(0x02, this._getNewSequenceNumber(),
|
const motd = new FLAP(0x02, this.nextReqID,
|
||||||
new SNAC(0x01, 0x13, FLAGS_EMPTY, 0, Buffer.concat([
|
new SNAC(0x01, 0x13, Buffer.concat([
|
||||||
word(0x0004),
|
word(0x0004),
|
||||||
(new TLV(0x0B, Buffer.from("Hello world!"))).toBuffer(),
|
(new TLV(0x0B, Buffer.from("Hello world!"))).toBuffer(),
|
||||||
])))
|
])))
|
||||||
|
@ -85,8 +85,8 @@ export default class GenericServiceControls extends BaseService {
|
||||||
|
|
||||||
const buf = Buffer.concat([payloadHeader, ...tlvs.map((tlv) => tlv.toBuffer())])
|
const buf = Buffer.concat([payloadHeader, ...tlvs.map((tlv) => tlv.toBuffer())])
|
||||||
|
|
||||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
const resp = new FLAP(0x02, this.nextReqID,
|
||||||
new SNAC(0x01, 0x0f, FLAGS_EMPTY, 0, buf));
|
new SNAC(0x01, 0x0f, buf));
|
||||||
|
|
||||||
this.send(resp);
|
this.send(resp);
|
||||||
return;
|
return;
|
||||||
|
@ -97,8 +97,8 @@ export default class GenericServiceControls extends BaseService {
|
||||||
Object.values(this.communicator.services).forEach((subtype) => {
|
Object.values(this.communicator.services).forEach((subtype) => {
|
||||||
serviceVersions.push(Buffer.from([0x00, subtype.service, 0x00, subtype.version]));
|
serviceVersions.push(Buffer.from([0x00, subtype.service, 0x00, subtype.version]));
|
||||||
});
|
});
|
||||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
const resp = new FLAP(0x02, this.nextReqID,
|
||||||
new SNAC(0x01, 0x18, FLAGS_EMPTY, 0, Buffer.concat(serviceVersions)));
|
new SNAC(0x01, 0x18, Buffer.concat(serviceVersions)));
|
||||||
this.send(resp);
|
this.send(resp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Communicator from '../communicator';
|
||||||
|
|
||||||
import { FLAP, SNAC, TLV } from '../structures';
|
import { FLAP, SNAC, TLV } from '../structures';
|
||||||
import { char, word, dword, dot2num } from '../structures/bytes';
|
import { char, word, dword, dot2num } from '../structures/bytes';
|
||||||
import { FLAGS_EMPTY, USER_STATUS, USER_STATUS_VARIOUS } from '../consts';
|
import { USER_STATUS, USER_STATUS_VARIOUS } from '../consts';
|
||||||
|
|
||||||
export default class LocationServices extends BaseService {
|
export default class LocationServices extends BaseService {
|
||||||
constructor(communicator : Communicator) {
|
constructor(communicator : Communicator) {
|
||||||
|
@ -17,8 +17,8 @@ export default class LocationServices extends BaseService {
|
||||||
|
|
||||||
// request location service parameters and limitations
|
// request location service parameters and limitations
|
||||||
if (message.payload.subtype === 0x02) {
|
if (message.payload.subtype === 0x02) {
|
||||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
const resp = new FLAP(0x02, this.nextReqID,
|
||||||
new SNAC(0x02,0x03, FLAGS_EMPTY, 0, [
|
new SNAC(0x02,0x03, [
|
||||||
new TLV(0x01, word(0x400)), // max profile length
|
new TLV(0x01, word(0x400)), // max profile length
|
||||||
new TLV(0x02, word(0x10)), // max capabilities
|
new TLV(0x02, word(0x10)), // max capabilities
|
||||||
new TLV(0x03, word(0xA)), // unknown
|
new TLV(0x03, word(0xA)), // unknown
|
||||||
|
|
|
@ -16,8 +16,8 @@ export default class BuddyListManagement extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.payload.subtype === 0x02) {
|
if (message.payload.subtype === 0x02) {
|
||||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
const resp = new FLAP(0x02, this.nextReqID,
|
||||||
new SNAC(0x03, 0x03, FLAGS_EMPTY, 0, [
|
new SNAC(0x03, 0x03, [
|
||||||
new TLV(0x01, word(600)), // 600 max buddies
|
new TLV(0x01, word(600)), // 600 max buddies
|
||||||
new TLV(0x02, word(750)), // 750 max watchers
|
new TLV(0x02, word(750)), // 750 max watchers
|
||||||
new TLV(0x03, word(512)), // 512 max online notifications ?
|
new TLV(0x03, word(512)), // 512 max online notifications ?
|
||||||
|
|
|
@ -17,7 +17,7 @@ interface ChannelSettings {
|
||||||
|
|
||||||
export default class ICBM extends BaseService {
|
export default class ICBM extends BaseService {
|
||||||
private channel : ChannelSettings = {
|
private channel : ChannelSettings = {
|
||||||
channel: 2,
|
channel: 0,
|
||||||
messageFlags: 3,
|
messageFlags: 3,
|
||||||
maxMessageSnacSize: 512,
|
maxMessageSnacSize: 512,
|
||||||
maxSenderWarningLevel: 999,
|
maxSenderWarningLevel: 999,
|
||||||
|
@ -26,6 +26,8 @@ export default class ICBM extends BaseService {
|
||||||
unknown: 1000,
|
unknown: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private channels : ChannelSettings[] = [];
|
||||||
|
|
||||||
constructor(communicator : Communicator) {
|
constructor(communicator : Communicator) {
|
||||||
super({service: 0x04, version: 0x01}, communicator)
|
super({service: 0x04, version: 0x01}, communicator)
|
||||||
}
|
}
|
||||||
|
@ -52,8 +54,12 @@ export default class ICBM extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = message.payload.payload;
|
const payload = message.payload.payload;
|
||||||
|
const channel = payload.readUInt16BE(0);
|
||||||
|
|
||||||
|
// TODO: set settings based on channel provided
|
||||||
|
|
||||||
this.channel = {
|
this.channel = {
|
||||||
channel: payload.readUInt16BE(0),
|
channel,
|
||||||
messageFlags: payload.readUInt32BE(2),
|
messageFlags: payload.readUInt32BE(2),
|
||||||
maxMessageSnacSize: payload.readUInt16BE(6),
|
maxMessageSnacSize: payload.readUInt16BE(6),
|
||||||
maxSenderWarningLevel: payload.readUInt16BE(8),
|
maxSenderWarningLevel: payload.readUInt16BE(8),
|
||||||
|
@ -79,8 +85,8 @@ export default class ICBM extends BaseService {
|
||||||
// It's identical to the channel set request the client
|
// It's identical to the channel set request the client
|
||||||
// sends earlier. Also the 3.x client sends a channel set request
|
// sends earlier. Also the 3.x client sends a channel set request
|
||||||
// so early
|
// so early
|
||||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
const resp = new FLAP(0x02, this.nextReqID,
|
||||||
new SNAC(0x04, 0x05, FLAGS_EMPTY, 0, payload));
|
new SNAC(0x04, 0x05, payload));
|
||||||
this.send(resp);
|
this.send(resp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ export default class PrivacyManagement extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.payload.subtype === 0x02) {
|
if (message.payload.subtype === 0x02) {
|
||||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
const resp = new FLAP(0x02, this.nextReqID,
|
||||||
new SNAC(0x09, 0x03, FLAGS_EMPTY, 0, [
|
new SNAC(0x09, 0x03, [
|
||||||
new TLV(0x01, word(200)), // max visible list size
|
new TLV(0x01, word(200)), // max visible list size
|
||||||
new TLV(0x02, word(200)) // max invisible list size
|
new TLV(0x02, word(200)) // max invisible list size
|
||||||
]));
|
]));
|
||||||
|
|
|
@ -2,6 +2,7 @@ import crypto from 'crypto';
|
||||||
import BaseService from './base';
|
import BaseService from './base';
|
||||||
import Communicator, { User } from '../communicator';
|
import Communicator, { User } from '../communicator';
|
||||||
import { FLAP, SNAC, TLV, ErrorCode, TLVType } from '../structures';
|
import { FLAP, SNAC, TLV, ErrorCode, TLVType } from '../structures';
|
||||||
|
import { word } from '../structures/bytes';
|
||||||
|
|
||||||
const { AIM_MD5_STRING, FLAGS_EMPTY } = require('../consts');
|
const { AIM_MD5_STRING, FLAGS_EMPTY } = require('../consts');
|
||||||
|
|
||||||
|
@ -44,8 +45,8 @@ export default class AuthorizationRegistrationService extends BaseService {
|
||||||
const username = userTLV.payload.toString('ascii');
|
const username = userTLV.payload.toString('ascii');
|
||||||
|
|
||||||
if (!users[username]) {
|
if (!users[username]) {
|
||||||
const authResp = new FLAP(2, this._getNewSequenceNumber(),
|
const authResp = new FLAP(2, this.nextReqID,
|
||||||
new SNAC(0x17, 0x03, FLAGS_EMPTY, 0, [
|
new SNAC(0x17, 0x03, [
|
||||||
TLV.forUsername(username), // username
|
TLV.forUsername(username), // username
|
||||||
TLV.forError(ErrorCode.IncorrectNick) // incorrect nick/password
|
TLV.forError(ErrorCode.IncorrectNick) // incorrect nick/password
|
||||||
]));
|
]));
|
||||||
|
@ -67,22 +68,25 @@ export default class AuthorizationRegistrationService extends BaseService {
|
||||||
|
|
||||||
if (digest !== (passwordHashTLV as TLV).payload.toString('hex')) {
|
if (digest !== (passwordHashTLV as TLV).payload.toString('hex')) {
|
||||||
console.log('Invalid password for', username);
|
console.log('Invalid password for', username);
|
||||||
const authResp = new FLAP(2, this._getNewSequenceNumber(),
|
const authResp = new FLAP(2, this.nextReqID,
|
||||||
new SNAC(0x17, 0x03, FLAGS_EMPTY, 0, [
|
new SNAC(0x17, 0x03, [
|
||||||
TLV.forUsername(username), // username
|
TLV.forUsername(username), // username
|
||||||
TLV.forError(ErrorCode.IncorrectNick) // incorrect nick/password
|
TLV.forError(ErrorCode.IncorrectNick) // incorrect nick/password
|
||||||
]));
|
]));
|
||||||
this.send(authResp);
|
this.send(authResp);
|
||||||
|
|
||||||
|
// Close this connection
|
||||||
|
const plsLeave = new FLAP(4, this.nextReqID, Buffer.from([]));
|
||||||
|
this.send(plsLeave);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = this.communicator.socket.localAddress.split(':').pop();
|
const chatHost = this.communicator.socket.localAddress.split(':').pop() + ':5191';
|
||||||
const port = this.communicator.socket.localPort;
|
|
||||||
|
const authResp = new FLAP(2, this.nextReqID,
|
||||||
const authResp = new FLAP(2, this._getNewSequenceNumber(),
|
new SNAC(0x17, 0x03, [
|
||||||
new SNAC(0x17, 0x03, FLAGS_EMPTY, 0, [
|
|
||||||
TLV.forUsername(username), // username
|
TLV.forUsername(username), // username
|
||||||
TLV.forBOSAddress(`${host}:${port}`), // BOS address
|
TLV.forBOSAddress(chatHost), // BOS address
|
||||||
TLV.forCookie(JSON.stringify({cookie: 'uwu', user: 'toof'})) // Authorization cookie
|
TLV.forCookie(JSON.stringify({cookie: 'uwu', user: 'toof'})) // Authorization cookie
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
@ -93,8 +97,8 @@ export default class AuthorizationRegistrationService extends BaseService {
|
||||||
case 0x06: // Request md5 authkey
|
case 0x06: // Request md5 authkey
|
||||||
const MD5AuthKeyHeader = Buffer.alloc(2, 0xFF, 'hex');
|
const MD5AuthKeyHeader = Buffer.alloc(2, 0xFF, 'hex');
|
||||||
MD5AuthKeyHeader.writeUInt16BE(this.cipher.length);
|
MD5AuthKeyHeader.writeUInt16BE(this.cipher.length);
|
||||||
const md5ReqResp = new FLAP(2, this._getNewSequenceNumber(),
|
const md5ReqResp = new FLAP(2, this.nextReqID,
|
||||||
new SNAC(0x17, 0x07, FLAGS_EMPTY, 0,
|
new SNAC(0x17, 0x07,
|
||||||
Buffer.concat([MD5AuthKeyHeader, Buffer.from(this.cipher, 'binary')]),
|
Buffer.concat([MD5AuthKeyHeader, Buffer.from(this.cipher, 'binary')]),
|
||||||
));
|
));
|
||||||
this.send(md5ReqResp);
|
this.send(md5ReqResp);
|
||||||
|
|
|
@ -6,7 +6,7 @@ interface ServiceFamilyVersion {
|
||||||
version : number,
|
version : number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class BaseService {
|
export default abstract class BaseService {
|
||||||
public service : number;
|
public service : number;
|
||||||
public version : number;
|
public version : number;
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ export default class BaseService {
|
||||||
this.communicator.send(message);
|
this.communicator.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNewSequenceNumber() {
|
get nextReqID() {
|
||||||
return this.communicator._getNewSequenceNumber();
|
return this.communicator.nextReqID;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessage(message : FLAP) : void {
|
handleMessage(message : FLAP) : void {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
|
import { FLAGS_EMPTY } from "../consts";
|
||||||
import { TLV } from "./TLV";
|
import { TLV } from "./TLV";
|
||||||
|
|
||||||
export class RateClass {
|
export class RateClass {
|
||||||
|
@ -60,13 +61,16 @@ export class Rate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let snacID = 0x2000;
|
||||||
|
|
||||||
export class SNAC {
|
export class SNAC {
|
||||||
constructor(public service : number, public subtype : number, public flags : Buffer, public requestID : number , public payload : (TLV[] | Buffer) = Buffer.alloc(0)) {
|
constructor(public service : number, public subtype : number, public payload : (TLV[] | Buffer) = Buffer.alloc(0), public requestID : number = 0, public flags : Buffer = FLAGS_EMPTY) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.subtype = subtype;
|
this.subtype = subtype;
|
||||||
this.flags = flags;
|
|
||||||
this.requestID = requestID;
|
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
|
|
||||||
|
this.requestID = requestID || (snacID++);
|
||||||
|
this.flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBuffer(buf : Buffer, payloadLength = 0) {
|
static fromBuffer(buf : Buffer, payloadLength = 0) {
|
||||||
|
@ -105,17 +109,17 @@ export class SNAC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SNAC(service, subtype, flags, requestID, payload);
|
return new SNAC(service, subtype, payload, requestID, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static forRateClass(service : number, subtype : number, flags : Buffer, requestID : number, rates : Rate[]) : SNAC {
|
static forRateClass(service : number, subtype : number, rates : Rate[]) : SNAC {
|
||||||
const payloadHeader = Buffer.alloc(2, 0x00);
|
const payloadHeader = Buffer.alloc(2, 0x00);
|
||||||
payloadHeader.writeUInt16BE(rates.length);
|
payloadHeader.writeUInt16BE(rates.length);
|
||||||
|
|
||||||
const payloadBody = rates.map((rateClass) => rateClass.toBuffer());
|
const payloadBody = rates.map((rateClass) => rateClass.toBuffer());
|
||||||
const payload = Buffer.concat([payloadHeader, ...payloadBody]);
|
const payload = Buffer.concat([payloadHeader, ...payloadBody]);
|
||||||
|
|
||||||
return new SNAC(service, subtype, flags, requestID, payload);
|
return new SNAC(service, subtype, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
|
|
|
@ -134,7 +134,7 @@ function bufferFromWebText(webtext : string) : Buffer {
|
||||||
return Buffer.from(webtext.replace(/\s/g, ''), 'hex');
|
return Buffer.from(webtext.replace(/\s/g, ''), 'hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
const FLAPSpec = [
|
const SNAC_01_0F = [
|
||||||
byte("FLAP Header"),
|
byte("FLAP Header"),
|
||||||
byte("Channel"),
|
byte("Channel"),
|
||||||
word("Sequence ID"),
|
word("Sequence ID"),
|
||||||
|
@ -189,5 +189,5 @@ const exampleWebText = ''+
|
||||||
`
|
`
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
printBuffer(bufferFromWebText(exampleWebText), FLAPSpec);
|
printBuffer(bufferFromWebText(exampleWebText), SNAC_01_0F);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {FLAGS_EMPTY} from "../src/consts";
|
||||||
const tests = [
|
const tests = [
|
||||||
() => {
|
() => {
|
||||||
// Construct and test a CLI_AUTH_REQUEST
|
// Construct and test a CLI_AUTH_REQUEST
|
||||||
const md5_auth_req = new FLAP(0x02, 0, new SNAC(0x17, 0x06, FLAGS_EMPTY, 0, [new TLV(0x01, Buffer.from("toof"))]));
|
const md5_auth_req = new FLAP(0x02, 0, new SNAC(0x17, 0x06, [new TLV(0x01, Buffer.from("toof"))]));
|
||||||
assert(md5_auth_req.channel === 2);
|
assert(md5_auth_req.channel === 2);
|
||||||
assert(md5_auth_req.payload instanceof SNAC);
|
assert(md5_auth_req.payload instanceof SNAC);
|
||||||
assert(md5_auth_req.payload.service === 23);
|
assert(md5_auth_req.payload.service === 23);
|
||||||
|
|
Loading…
Reference in a new issue