split auth and chat servers

This commit is contained in:
Artem Titoulenko 2021-09-24 00:47:41 -04:00
parent aad0acfd15
commit 19e26cddbb
15 changed files with 175 additions and 97 deletions

View file

@ -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": {

View file

@ -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
View 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');
})

View file

@ -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
View 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');
});

View file

@ -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;
} }

View file

@ -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

View file

@ -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 ?

View file

@ -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;
} }

View file

@ -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
])); ]));

View file

@ -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);

View file

@ -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 {

View file

@ -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() {

View file

@ -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);
} }

View file

@ -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);