mirror of
https://github.com/amigan/aim-oscar-server.git
synced 2025-01-31 04:52:37 -05:00
IT WORKS
This commit is contained in:
parent
19e26cddbb
commit
099ea1a69b
7 changed files with 140 additions and 17 deletions
|
@ -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[] = [];
|
||||
|
|
|
@ -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),
|
||||
];
|
||||
|
|
|
@ -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()!);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
108
src/util.ts
108
src/util.ts
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue