This commit is contained in:
Artem Titoulenko 2021-11-09 11:42:15 -05:00
parent 19e26cddbb
commit 099ea1a69b
7 changed files with 140 additions and 17 deletions

View file

@ -6,6 +6,7 @@ import BaseService from "./services/base";
export interface User {
uin: string,
username: string,
password: string,
memberSince: Date,
}
@ -97,8 +98,16 @@ export default class Communicator {
}
const tlv = TLV.fromBuffer(message.payload.slice(4));
console.log('thing sent to channel 1:');
console.log(tlv.toString());
if (tlv.type === 0x06) {
// client sent us a cookie
const {cookie, user} = JSON.parse(tlv.payload.toString());
console.log('cookie:', cookie);
this.user = user;
}
if (tlv.type === TLVType.GetServices) { // Requesting available services
// this is just a dword list of subtype families
const servicesOffered : Buffer[] = [];

View file

@ -47,9 +47,9 @@ const server = net.createServer((socket) => {
new PrivacyManagement(comm),
new UserLookup(comm),
new UsageStats(comm),
new ChatNavigation(comm),
new Chat(comm),
new DirectorySearch(comm),
// new ChatNavigation(comm),
// new Chat(comm),
// new DirectorySearch(comm),
new ServerStoredBuddyIcons(comm),
new SSI(comm),
];

View file

@ -31,11 +31,21 @@ export default class GenericServiceControls extends BaseService {
}
if (message.payload.subtype === 0x06) { // Client ask server for rate limits info
// HACK: set rate limits for all services. I can't tell which message subtypes they support so
// make it set rate limits for everything under 0x21.
const pairs : RateGroupPair[] = [];
Object.values(this.communicator.services).forEach((service) => {
for (let i = 0; i < 0x21; i++) {
pairs.push(new RateGroupPair(service.service, i));
}
});
const resp = new FLAP(0x02, this.nextReqID,
SNAC.forRateClass(0x01, 0x07, [
new Rate(
new RateClass(1, 80, 2500, 2000, 1500, 800, 3400 /*fake*/, 6000, 0, 0),
new RatedServiceGroup(1, [new RateGroupPair(0x00, 0x00)])
new RatedServiceGroup(1, pairs),
)
]))
this.send(resp);
@ -50,7 +60,7 @@ export default class GenericServiceControls extends BaseService {
}
if (message.payload.subtype === 0x0e) { // Client requests own online information
const uin = '400'; // this.communicator.user.uin;
const uin = this.communicator.user?.username || 'user';
const warning = 0;
const since = +(new Date('December 17, 1998 03:24:00'));
const externalIP = dot2num(this.communicator.socket.remoteAddress!.split(':').pop()!);

View file

@ -57,7 +57,6 @@ export default class ICBM extends BaseService {
const channel = payload.readUInt16BE(0);
// TODO: set settings based on channel provided
this.channel = {
channel,
messageFlags: payload.readUInt32BE(2),
@ -65,7 +64,7 @@ export default class ICBM extends BaseService {
maxSenderWarningLevel: payload.readUInt16BE(8),
maxReceiverWarningLevel: payload.readUInt16BE(10),
minimumMessageInterval: payload.readUInt16BE(12),
unknown: payload.readUInt16BE(14),
unknown: 1000, //payload.readUInt16BE(14),
}
console.log("ICBM set channel", this.channel);
return;
@ -81,10 +80,6 @@ export default class ICBM extends BaseService {
payload.writeInt16BE(this.channel.minimumMessageInterval, 12);
payload.writeInt16BE(this.channel.unknown, 14);
// For some reason this response crashes the client?
// It's identical to the channel set request the client
// sends earlier. Also the 3.x client sends a channel set request
// so early
const resp = new FLAP(0x02, this.nextReqID,
new SNAC(0x04, 0x05, payload));
this.send(resp);

View file

@ -4,6 +4,6 @@ import Communicator from '../communicator';
// SSI is Server Stored Information
export default class SSI extends BaseService {
constructor(communicator : Communicator) {
super({service: 0x10, version: 0x01}, communicator)
super({service: 0x13, version: 0x01}, communicator)
}
}

View file

@ -9,6 +9,7 @@ const { AIM_MD5_STRING, FLAGS_EMPTY } = require('../consts');
const users : {[key: string]: User} = {
'toof': {
uin: '156089',
username: 'toof',
password: 'foo',
memberSince: new Date('December 17, 1998 03:24:00'),
}
@ -87,12 +88,18 @@ export default class AuthorizationRegistrationService extends BaseService {
new SNAC(0x17, 0x03, [
TLV.forUsername(username), // username
TLV.forBOSAddress(chatHost), // BOS address
TLV.forCookie(JSON.stringify({cookie: 'uwu', user: 'toof'})) // Authorization cookie
TLV.forCookie(JSON.stringify({cookie: 'uwu', user: users[username]})) // Authorization cookie
]));
this.communicator.user = Object.assign({}, users[username], {username});
this.communicator.user = Object.assign({username}, users[username]);
console.log(this.communicator.user);
this.send(authResp);
// tell them to leave
const disconnectResp = new FLAP(4, this.nextReqID, Buffer.alloc(0));
this.send(disconnectResp);
return;
case 0x06: // Request md5 authkey
const MD5AuthKeyHeader = Buffer.alloc(2, 0xFF, 'hex');

View file

@ -29,6 +29,7 @@ interface Spec {
isRepeat? : boolean,
isParam? : boolean,
isTLV? : boolean,
dump? : boolean,
repeatSpecs?: Spec[],
}
@ -56,6 +57,10 @@ function tlv(description : string) : Spec {
return {size : -1, description, isTLV: true};
}
function dump() : Spec {
return {size: -1, description: '', dump: true};
}
function parseBuffer(buf : Buffer, spec : Spec[], repeatTimes = 0) {
let offset = 0;
let rows = [];
@ -64,6 +69,12 @@ function parseBuffer(buf : Buffer, spec : Spec[], repeatTimes = 0) {
for (let section of spec) {
let value : any = 0;
let bufStr : string = '';
if (section.dump) {
rows.push({raw: logDataStream(buf.slice(offset))});
break;
}
if (section.size === 8) {
bufStr = buf.slice(offset, offset + 1).toString('hex');
value = buf.readInt8(offset);
@ -127,7 +138,15 @@ function parseBuffer(buf : Buffer, spec : Spec[], repeatTimes = 0) {
function printBuffer(buf : Buffer, spec : Spec[]) {
const rows = parseBuffer(buf, spec);
console.log((new Table(rows)).toString());
const lastRow = rows[rows.length - 1];
if (!!lastRow.raw) {
console.log((new Table(rows.slice(0, -1))).toString());
console.log(lastRow.raw);
} else {
console.log((new Table(rows)).toString());
}
}
function bufferFromWebText(webtext : string) : Buffer {
@ -169,7 +188,7 @@ const SNAC_01_0F = [
dword("Last EXT status update time"),
];
const exampleWebText = ''+
const exSNAC_01_0F = ''+
`
2a 02 00 05 00 71 00 01
00 0f 00 00 00 00 00 00
@ -186,8 +205,91 @@ const exampleWebText = ''+
00 00 00 00 00 00 00 00
00 03 00 00 00 00 00 00
00 00 00 00 00 00 00
`;
const SNAC_01_07 = [
byte("FLAP Header"),
byte("Channel"),
word("Sequence ID"),
word("Payload Length"),
word("SNAC Family"),
word("SNAC Subtype"),
word("SNAC Flags"),
dword("SNAC Request-ID"),
repeat(16, "Number of Rate Classes", [
word('Rate class ID'),
dword('Window size'),
dword('Clear level'),
dword('Alert level'),
dword('Limit level'),
dword('Disconnect level'),
dword('Current level'),
dword('Max level'),
dword('Last time'),
byte('Current State'),
]),
dump(),
];
const exSNAC_01_07 = ''+
`
2a 02 00 05 03 3b 00 01 00 07 00 00 00 00
00 00 00 05 00 01 00 00 00 50 00 00 09 c4 00 00
07 d0 00 00 05 dc 00 00 03 20 00 00 16 dc 00 00
17 70 00 00 00 00 00 00 02 00 00 00 50 00 00 0b
b8 00 00 07 d0 00 00 05 dc 00 00 03 e8 00 00 17
70 00 00 17 70 00 00 00 7b 00 00 03 00 00 00 1e
00 00 0e 74 00 00 0f a0 00 00 05 dc 00 00 03 e8
00 00 17 70 00 00 17 70 00 00 00 00 00 00 04 00
00 00 14 00 00 15 7c 00 00 14 b4 00 00 10 68 00
00 0b b8 00 00 17 70 00 00 1f 40 00 00 00 7b 00
00 05 00 00 00 0a 00 00 15 7c 00 00 14 b4 00 00
10 68 00 00 0b b8 00 00 17 70 00 00 1f 40 00 00
00 7b 00 00 01 00 91 00 01 00 01 00 01 00 02 00
01 00 03 00 01 00 04 00 01 00 05 00 01 00 06 00
01 00 07 00 01 00 08 00 01 00 09 00 01 00 0a 00
01 00 0b 00 01 00 0c 00 01 00 0d 00 01 00 0e 00
01 00 0f 00 01 00 10 00 01 00 11 00 01 00 12 00
01 00 13 00 01 00 14 00 01 00 15 00 01 00 16 00
01 00 17 00 01 00 18 00 01 00 19 00 01 00 1a 00
01 00 1b 00 01 00 1c 00 01 00 1d 00 01 00 1e 00
01 00 1f 00 01 00 20 00 01 00 21 00 02 00 01 00
02 00 02 00 02 00 03 00 02 00 04 00 02 00 06 00
02 00 07 00 02 00 08 00 02 00 0a 00 02 00 0c 00
02 00 0d 00 02 00 0e 00 02 00 0f 00 02 00 10 00
02 00 11 00 02 00 12 00 02 00 13 00 02 00 14 00
02 00 15 00 03 00 01 00 03 00 02 00 03 00 03 00
03 00 06 00 03 00 07 00 03 00 08 00 03 00 09 00
03 00 0a 00 03 00 0b 00 03 00 0c 00 04 00 01 00
04 00 02 00 04 00 03 00 04 00 04 00 04 00 05 00
04 00 07 00 04 00 08 00 04 00 09 00 04 00 0a 00
04 00 0b 00 04 00 0c 00 04 00 0d 00 04 00 0e 00
04 00 0f 00 04 00 10 00 04 00 11 00 04 00 12 00
04 00 13 00 04 00 14 00 06 00 01 00 06 00 02 00
06 00 03 00 08 00 01 00 08 00 02 00 09 00 01 00
09 00 02 00 09 00 03 00 09 00 04 00 09 00 09 00
09 00 0a 00 09 00 0b 00 0a 00 01 00 0a 00 02 00
0a 00 03 00 0b 00 01 00 0b 00 02 00 0b 00 03 00
0b 00 04 00 0c 00 01 00 0c 00 02 00 0c 00 03 00
13 00 01 00 13 00 02 00 13 00 03 00 13 00 04 00
13 00 05 00 13 00 06 00 13 00 07 00 13 00 08 00
13 00 09 00 13 00 0a 00 13 00 0b 00 13 00 0c 00
13 00 0d 00 13 00 0e 00 13 00 0f 00 13 00 10 00
13 00 11 00 13 00 12 00 13 00 13 00 13 00 14 00
13 00 15 00 13 00 16 00 13 00 17 00 13 00 18 00
13 00 19 00 13 00 1a 00 13 00 1b 00 13 00 1c 00
13 00 1d 00 13 00 1e 00 13 00 1f 00 13 00 20 00
13 00 21 00 13 00 22 00 13 00 23 00 13 00 24 00
13 00 25 00 13 00 26 00 13 00 27 00 13 00 28 00
15 00 01 00 15 00 02 00 15 00 03 00 02 00 06 00
03 00 04 00 03 00 05 00 09 00 05 00 09 00 06 00
09 00 07 00 09 00 08 00 03 00 02 00 02 00 05 00
04 00 06 00 04 00 02 00 02 00 09 00 02 00 0b 00
05 00 00
`;
if (require.main === module) {
printBuffer(bufferFromWebText(exampleWebText), SNAC_01_0F);
printBuffer(bufferFromWebText(exSNAC_01_07), SNAC_01_07);
}