[go: nahoru, domu]

Skip to content

Commit

Permalink
rename some things based on official oscar protocol docs
Browse files Browse the repository at this point in the history
  • Loading branch information
DrewML committed Dec 27, 2020
1 parent 98f4826 commit d37e115
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 48 deletions.
25 changes: 13 additions & 12 deletions src/AIMAuthServer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from './serverSnacs';
import { parseAuthRequest, parseMD5LoginRequest } from './clientSnacs';
import { LOGIN_ERRORS } from '../constants';
import { FlapType } from '../types';

/**
* @summary The first server an Oscar Protocol client
Expand All @@ -30,12 +31,12 @@ export class AIMAuthServer extends OscarServer {
const { host, port } = oscarSocket.remoteAddress;
console.log(`AIMAuthServer: New connection from ${host}:${port}`);

oscarSocket.onChannel(0x1, (flap) => {
oscarSocket.onFlap(FlapType.SIGNON, (flap) => {
const flapVersion = flap.data.readUInt32BE(0);
assert(flapVersion === 0x1, 'Incorrect client FLAP version');
});

oscarSocket.onChannel(0x2, (flap) => {
oscarSocket.onFlap(FlapType.SIGNOFF, (flap) => {
const state = this.getState(oscarSocket);
const snac = parseSnac(flap.data);

Expand All @@ -48,7 +49,7 @@ export class AIMAuthServer extends OscarServer {

state.setScreenname(authReq.screenname);
const responseFlap = {
channel: 2,
type: FlapType.DATA,
data: authKeyResponseSnac(authKey, snac.requestID),
};

Expand Down Expand Up @@ -83,7 +84,7 @@ export class AIMAuthServer extends OscarServer {
// rather than mapping all to INCORRECT_NICK_OR_PASS
if (!(isValidUsername && isValidPass)) {
const responseFlap = {
channel: 2,
type: FlapType.DATA,
data: loginErrorSnac({
screenname: payload.screenname,
errorCode: LOGIN_ERRORS.INCORRECT_NICK_OR_PASS,
Expand All @@ -97,7 +98,7 @@ export class AIMAuthServer extends OscarServer {
}

const responseFlap = {
channel: 2,
type: FlapType.DATA,
data: loginSuccessSnac({
screenname: payload.screenname,
// TODO: Should be pulled from DB when real
Expand All @@ -117,20 +118,20 @@ export class AIMAuthServer extends OscarServer {
oscarSocket.write(responseFlap);
return;
}
console.log('AIMAuthServer Unhandled Channel 2 Flap: ', flap);
console.log('AIMAuthServer data Flap: ', flap);
});

oscarSocket.onChannel(0x3, (flap) => {
console.log('AIMAuthServer unimplemented channel 3 flap: ', flap);
oscarSocket.onFlap(FlapType.ERROR, (flap) => {
console.log('AIMAuthServer unhandled error flap: ', flap);
});

oscarSocket.onChannel(0x4, (flap) => {
oscarSocket.onFlap(FlapType.SIGNOFF, (flap) => {
// TODO: handle disconnect negotiation
console.log('AIMAuthServer unimplemented channel 4 flap: ', flap);
console.log('AIMAuthServer unhandled signoff flap: ', flap);
});

oscarSocket.onChannel(0x5, (flap) => {
console.log('AIMAuthServer unimplemented channel 5 flap: ', flap);
oscarSocket.onFlap(FlapType.KEEPALIVE, (flap) => {
console.log('AIMAuthServer unhandled keepalive flap: ', flap);
});

// Initialize state needed for auth requests
Expand Down
4 changes: 2 additions & 2 deletions src/AIMAuthServer/serverSnacs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import assert from 'assert';
import { stringTLV, uint16TLV } from '../buildTLV';
import { buildSnac } from '../snacUtils';
import { SNACS, TLVS } from '../constants';
import { uint16BEBuffer } from '../buf';

/**
* @see http://iserverd1.khstu.ru/oscar/snac_17_07.html
Expand All @@ -14,8 +15,7 @@ export function authKeyResponseSnac(authKey: string, reqID: number) {
const authKeyBuf = Buffer.from(authKey, 'utf8');
// Note: The linked docs for this SNAC are wrong.
// The length should be a word (2 byte), not a dword (4 bytes)
const authKeyLen = Buffer.alloc(2);
authKeyLen.writeUInt16BE(authKeyBuf.byteLength, 0);
const authKeyLen = uint16BEBuffer([authKeyBuf.byteLength]);

return buildSnac({
family: SNACS.AUTH.family,
Expand Down
30 changes: 16 additions & 14 deletions src/BossServer/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { timingSafeEqual } from 'crypto';
import { FlapType } from '../types';
import { parseCookieRequest } from './clientSnacs';
import { OscarServer, OscarSocket } from '../OscarServer';
import {
Expand All @@ -16,51 +17,52 @@ export class BossServer extends OscarServer {

oscarSocket.sendStartFlap();

oscarSocket.onChannel(0x1, (flap) => {
oscarSocket.onFlap(FlapType.SIGNON, (flap) => {
const { authCookie } = parseCookieRequest(flap.data);
// TODO: Grab cookie from shared storage with auth service
const expectedCookie = Buffer.from('111111111', 'ascii');
const validCookie = timingSafeEqual(authCookie, expectedCookie);

// TODO: Unsure of what client expects for invalid cookie,
// maybe close via channel 4?
// maybe just a SIGNOFF flap? Let's just crash
// the server for now
assert(validCookie, 'BossServer: Invalid auth cookie');

const snac = supportedFamiliesSnac({ reqID: 1 });
oscarSocket.write({ channel: 2, data: snac });
oscarSocket.write({ type: FlapType.DATA, data: snac });
});

oscarSocket.onChannel(0x2, (flap) => {
oscarSocket.onFlap(FlapType.DATA, (flap) => {
const snac = parseSnac(flap.data);

if (matchSnac(snac, 'GENERAL', 'CLIENT_FAMILY_VERSIONS')) {
return oscarSocket.write({
channel: 2,
type: FlapType.DATA,
data: familyVersionsSnac({ reqID: snac.requestID }),
});
}

if (matchSnac(snac, 'GENERAL', 'RATE_REQUEST')) {
if (matchSnac(snac, 'GENERAL', 'RATE_INFO_REQUEST')) {
assert(false, 'snac 01,07 not implemented yet');
return oscarSocket.write({
channel: 2,
type: FlapType.DATA,
data: rateLimitInfoSnac({ reqID: snac.requestID }),
});
}

console.log('boss unhandled channel 2 flap: ', flap);
console.log('boss unhandled data flap: ', flap);
});

oscarSocket.onChannel(0x3, (flap) => {
console.log('boss channel 3 flap: ', flap);
oscarSocket.onFlap(FlapType.ERROR, (flap) => {
console.log('boss error flap: ', flap);
});

oscarSocket.onChannel(0x4, (flap) => {
console.log('boss channel 4 flap: ', flap);
oscarSocket.onFlap(FlapType.SIGNOFF, (flap) => {
console.log('boss signoff flap: ', flap);
});

oscarSocket.onChannel(0x5, (flap) => {
console.log('boss channel 5 flap: ', flap);
oscarSocket.onFlap(FlapType.KEEPALIVE, (flap) => {
console.log('boss keepalive flap: ', flap);
});
}
}
14 changes: 12 additions & 2 deletions src/BossServer/serverSnacs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,17 @@ export function familyVersionsSnac(opts: { reqID: number }) {
});
}

/**
* @see http://iserverd.khstu.ru/oscar/snac_01_07.html
*/
export function rateLimitInfoSnac(opts: { reqID: number }) {
// TODO: Implement
return Buffer.alloc(0);
// TODO: hardcode a giant buffer here
const data = Buffer.alloc(0);

return buildSnac({
family: SNACS.GENERAL.family,
subtype: SNACS.GENERAL.subtypes.RATE_INFO_RESPONSE,
reqID: opts.reqID,
data,
});
}
20 changes: 10 additions & 10 deletions src/OscarServer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import assert from 'assert';
import { Flap } from './types';
import { Flap, FlapType } from './types';
import { parseFlap, buildFlap } from './flapUtils';
import { createServer, Socket, Server, AddressInfo } from 'net';
import { MultiMap } from './MultiMap';
Expand Down Expand Up @@ -46,13 +46,13 @@ export class OscarServer {
}
}

interface ChannelListener {
interface FlapListener {
(flap: Flap): void;
}

export class OscarSocket {
private sequenceID = 0;
private channelListeners = new MultiMap<number, ChannelListener>();
private flapListeners = new MultiMap<number, FlapListener>();

constructor(private socket: Socket) {
socket.on('data', this.onData.bind(this));
Expand All @@ -73,32 +73,32 @@ export class OscarSocket {
*/
sendStartFlap() {
this.write({
channel: 1,
type: 1,
data: Buffer.from([0x0, 0x0, 0x0, 0x1]),
});
}

write(flap: { channel: number; data: Buffer }) {
write(flap: { type: FlapType; data: Buffer }) {
const fullFlap = {
...flap,
sequence: this.sequenceID++,
};
this.socket.write(buildFlap(fullFlap));
}

onChannel(channel: number, listener: ChannelListener) {
this.channelListeners.set(channel, listener);
onFlap(type: FlapType, listener: FlapListener) {
this.flapListeners.set(type, listener);
return this;
}

private onData(data: Buffer) {
const flap = parseFlap(data);
assert(
this.channelListeners.has(flap.channel),
`Channel ${flap.channel} has no handler in OscarSocket}`,
this.flapListeners.has(flap.type),
`No handler for Flap type ${flap.type}`,
);

const listeners = this.channelListeners.get(flap.channel);
const listeners = this.flapListeners.get(flap.type);
for (const listener of listeners) {
listener(flap);
}
Expand Down
14 changes: 13 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,41 +46,53 @@ export const SNACS = {
SUPPORTED_FAMILIES: 0x3,
CLIENT_FAMILY_VERSIONS: 0x17,
SERVER_FAMILY_VERSIONS: 0x18,
RATE_REQUEST: 0x6,
RATE_INFO_REQUEST: 0x6,
RATE_INFO_RESPONSE: 0x7,
},
},
LOCATION: {
family: 0x2,
subtypes: {},
},
BUDDYLIST: {
family: 0x3,
subtypes: {},
},
ICBM: {
family: 0x4,
subtypes: {},
},
INVITATION: {
family: 0x6,
subtypes: {},
},
ADMINISTRATIVE: {
family: 0x7,
subtypes: {},
},
POPUP_NOTICE: {
family: 0x8,
subtypes: {},
},
PRIVACY_MGMT: {
family: 0x9,
subtypes: {},
},
USER_LOOKUP: {
family: 0x0a,
subtypes: {},
},
USAGE_STATS: {
family: 0x0b,
subtypes: {},
},
SSI: {
family: 0x13,
subtypes: {},
},
OFFLINE: {
family: 0x15,
subtypes: {},
},
AUTH: {
family: 0x17,
Expand Down
12 changes: 6 additions & 6 deletions src/flapUtils.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import assert from 'assert';
import { Flap } from './types';
import { Flap, FlapType } from './types';

interface BuildFlapOpts {
channel: number;
type: FlapType;
sequence: number;
data: Buffer;
}

export function buildFlap({ channel, sequence, data }: BuildFlapOpts) {
assert(channel < 6 && channel > 0, `Unexpected Channel: ${channel}`);
export function buildFlap({ type, sequence, data }: BuildFlapOpts) {
assert(type < 6 && type > 0, `Unexpected Flap Frame Type: ${type}`);

const buf = Buffer.alloc(6);
buf.writeUInt8(0x2a, 0); // Flap start signal
buf.writeUInt8(channel, 1);
buf.writeUInt8(type, 1);
buf.writeUInt16BE(sequence, 2);
buf.writeUInt16BE(data.byteLength, 4);

Expand All @@ -27,7 +27,7 @@ export function parseFlap(rawFlap: Buffer): Flap {
assert(id === 0x2a, 'Unexpected Flap ID');

const flap: Flap = {
channel: rawFlap.readUInt8(1),
type: rawFlap.readUInt8(1),
sequence: rawFlap.readUInt16BE(2),
byteLength: rawFlap.readUInt16BE(4),
data: rawFlap.subarray(6),
Expand Down
13 changes: 12 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
/**
* @see http://web.archive.org/web/20080308233204/http://dev.aol.com/aim/oscar/#FLAP__FRAME_TYPE
*/
export const enum FlapType {
SIGNON = 1,
DATA = 2,
ERROR = 3,
SIGNOFF = 4,
KEEPALIVE = 5,
}

/**
* @see http://web.archive.org/web/20080308233204/http://dev.aol.com/aim/oscar/#FLAP
*/
export interface Flap {
channel: number;
type: FlapType;
sequence: number;
byteLength: number;
data: Buffer;
Expand Down

0 comments on commit d37e115

Please sign in to comment.