some logging bullshit here, some refactoring there
This commit is contained in:
parent
ad0e31bf55
commit
c22cc400d5
|
@ -27,6 +27,50 @@ import type { AimeDBHandlerFn } from "../types/handlers";
|
|||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
async function createAndInsertNewAccessCode(
|
||||
table: typeof felicaCardLookup | typeof felicaMobileLookup,
|
||||
cardType: "FeliCa Card" | "FeliCa Mobile",
|
||||
idm: bigint,
|
||||
prefix: string,
|
||||
key: string
|
||||
) {
|
||||
const mostRecentRow = await db
|
||||
.select()
|
||||
.from(table)
|
||||
.orderBy(desc(table.id))
|
||||
.limit(1)
|
||||
.then((r) => r[0]);
|
||||
|
||||
const nextId = mostRecentRow ? mostRecentRow.id + 1 : 1;
|
||||
|
||||
const accessCode = CalculateAccessCode(nextId, prefix, key);
|
||||
|
||||
logger.debug(`Created ${cardType} access code for serial ${nextId}.`, {
|
||||
nextId,
|
||||
accessCode,
|
||||
});
|
||||
|
||||
const value = { idm: idm.toString(16), accessCode };
|
||||
|
||||
const result = await db
|
||||
.insert(felicaCardLookup)
|
||||
.values(value)
|
||||
.returning()
|
||||
.then((r) => r[0]);
|
||||
|
||||
if (!result) {
|
||||
logger.crit("Failed to insert new lookup entry into the database.", value);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// This is supposed to be just a lookup handler, but my guess is that most games assume that there
|
||||
// is already a valid card in the database if the response is successful and don't bother to try
|
||||
// registering a new card, if the card is not FeliCa Mobile, since the game will happily display
|
||||
// access code 00000000000000000000.
|
||||
export const FelicaLookupHandler: AimeDBHandlerFn<"FelicaLookupResponse"> = async (
|
||||
header,
|
||||
data
|
||||
|
@ -46,22 +90,56 @@ export const FelicaLookupHandler: AimeDBHandlerFn<"FelicaLookupResponse"> = asyn
|
|||
|
||||
const table = IsSupportedFelicaMobile(req.osVer) ? felicaMobileLookup : felicaCardLookup;
|
||||
|
||||
const result = await db
|
||||
let result = await db
|
||||
.select()
|
||||
.from(table)
|
||||
.where(eq(table.idm, req.idm.toString(16)))
|
||||
.then((r) => r[0]);
|
||||
|
||||
if (!result) {
|
||||
resp.felicaIndex = -1;
|
||||
} else {
|
||||
resp.felicaIndex = result.id;
|
||||
resp.accessCode.set(Buffer.from(result.accessCode, "hex"));
|
||||
// Exit early if card is FeliCa mobile, so the game can use the register endpoint
|
||||
if (IsSupportedFelicaMobile(req.osVer)) {
|
||||
resp.felicaIndex = -1;
|
||||
return resp;
|
||||
}
|
||||
|
||||
if (!Config.AIMEDB_CONFIG.RESERVED_CARD_KEY || !Config.AIMEDB_CONFIG.RESERVED_CARD_PREFIX) {
|
||||
logger.error(
|
||||
"AIMEDB_CONFIG.RESERVED_CARD_KEY or AIMEDB_CONFIG.RESERVED_CARD_PREFIX is not set in config file. Cannot generate a new access code.",
|
||||
{ req }
|
||||
);
|
||||
|
||||
header.result = ResultCodes.INVALID_AIME_ID;
|
||||
return resp;
|
||||
}
|
||||
|
||||
const tmp = await createAndInsertNewAccessCode(
|
||||
felicaCardLookup,
|
||||
"FeliCa Card",
|
||||
req.idm,
|
||||
Config.AIMEDB_CONFIG.RESERVED_CARD_PREFIX,
|
||||
Config.AIMEDB_CONFIG.RESERVED_CARD_KEY
|
||||
);
|
||||
|
||||
if (!tmp) {
|
||||
header.result = ResultCodes.UNKNOWN_ERROR;
|
||||
return resp;
|
||||
}
|
||||
|
||||
result = tmp;
|
||||
}
|
||||
|
||||
header.result = ResultCodes.SUCCESS;
|
||||
resp.felicaIndex = result.id;
|
||||
resp.accessCode.set(Buffer.from(result.accessCode, "hex"));
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
// This is supposed to be just a lookup handler, but my guess is that most games assume that there
|
||||
// is already a valid card in the database if the response is successful and don't bother to try
|
||||
// registering a new card, if the card is not FeliCa Mobile, since the game will happily display
|
||||
// access code 00000000000000000000.
|
||||
export const FelicaExtendedLookupHandler: AimeDBHandlerFn<"FelicaExtendedLookupResponse"> = async (
|
||||
header,
|
||||
data
|
||||
|
@ -112,16 +190,15 @@ export const FelicaExtendedLookupHandler: AimeDBHandlerFn<"FelicaExtendedLookupR
|
|||
: result.aimedb_felica_card_lookup;
|
||||
const cardResult = result.aimedb_card;
|
||||
|
||||
// HACK: Since we cannot possibly know who made it (even AICC cards have
|
||||
// the same manufacturer code `01:2e`!), we're just going to treat everything
|
||||
// as a SEGA card.
|
||||
resp.companyCode = CompanyCodes.SEGA;
|
||||
resp.accessCode.set(Buffer.from(lookupResult.accessCode, "hex"));
|
||||
|
||||
if (cardResult) {
|
||||
resp.accountId = cardResult.id;
|
||||
resp.portalRegistered = PortalRegistration.UNREGISTERED;
|
||||
|
||||
// HACK: Since we cannot possibly know who made it (even AICC cards have
|
||||
// the same manufacturer code `01:2e`!), we're just going to treat everything
|
||||
// as a SEGA card.
|
||||
resp.companyCode = CompanyCodes.SEGA;
|
||||
}
|
||||
|
||||
return resp;
|
||||
|
@ -143,43 +220,36 @@ export const FelicaExtendedLookupHandler: AimeDBHandlerFn<"FelicaExtendedLookupR
|
|||
return resp;
|
||||
}
|
||||
|
||||
const mostRecentRow = await db
|
||||
.select()
|
||||
.from(felicaCardLookup)
|
||||
.orderBy(desc(felicaCardLookup.id))
|
||||
.limit(1)
|
||||
.then((r) => r[0]);
|
||||
|
||||
const nextId = mostRecentRow ? mostRecentRow.id + 1 : 1;
|
||||
|
||||
const accessCode = CalculateAccessCode(
|
||||
nextId,
|
||||
const row = await createAndInsertNewAccessCode(
|
||||
felicaCardLookup,
|
||||
"FeliCa Card",
|
||||
req.idm,
|
||||
Config.AIMEDB_CONFIG.RESERVED_CARD_PREFIX,
|
||||
Config.AIMEDB_CONFIG.RESERVED_CARD_KEY
|
||||
);
|
||||
|
||||
logger.verbose(`Created FeliCa Card access code for serial ${nextId}.`, {
|
||||
nextId,
|
||||
accessCode,
|
||||
});
|
||||
|
||||
const value = { idm: req.idm.toString(16), accessCode };
|
||||
const row = await db
|
||||
.insert(felicaCardLookup)
|
||||
.values(value)
|
||||
.returning()
|
||||
.then((r) => r[0]);
|
||||
|
||||
if (!row) {
|
||||
logger.crit("Failed to insert new lookup entry into the database.", value);
|
||||
|
||||
header.result = ResultCodes.UNKNOWN_ERROR;
|
||||
return resp;
|
||||
}
|
||||
|
||||
header.result = ResultCodes.SUCCESS;
|
||||
// HACK: Since we cannot possibly know who made it (even AICC cards have
|
||||
// the same manufacturer code `01:2e`!), we're just going to treat everything
|
||||
// as a SEGA card.
|
||||
resp.companyCode = CompanyCodes.SEGA;
|
||||
resp.accessCode.set(Buffer.from(row.accessCode, "hex"));
|
||||
|
||||
const cardResult = await db
|
||||
.select()
|
||||
.from(card)
|
||||
.where(eq(card.accessCode, row.accessCode))
|
||||
.then((r) => r[0]);
|
||||
|
||||
if (cardResult) {
|
||||
resp.accountId = cardResult.id;
|
||||
resp.portalRegistered = PortalRegistration.UNREGISTERED;
|
||||
}
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
|
@ -193,9 +263,12 @@ export const FelicaRegisterHandler: AimeDBHandlerFn<"FelicaLookupResponse"> = as
|
|||
const req = new FelicaLookupRequestStruct(data);
|
||||
const resp = new FelicaLookupResponseStruct();
|
||||
|
||||
if (!IsSupportedFelica(req.osVer)) {
|
||||
logger.verbose("Rejecting card of unknown OS version.", {
|
||||
req,
|
||||
if (!IsSupportedFelicaMobile(req.osVer)) {
|
||||
logger.error("Rejecting card because it is not FeliCa Mobile.", {
|
||||
idm: req.idm,
|
||||
chipCode: req.chipCode,
|
||||
osVer: req.osVer,
|
||||
timing: req.timing,
|
||||
});
|
||||
|
||||
header.result = ResultCodes.INVALID_AIME_ID;
|
||||
|
@ -229,35 +302,15 @@ export const FelicaRegisterHandler: AimeDBHandlerFn<"FelicaLookupResponse"> = as
|
|||
return resp;
|
||||
}
|
||||
|
||||
const mostRecentRow = await db
|
||||
.select()
|
||||
.from(felicaMobileLookup)
|
||||
.orderBy(desc(felicaMobileLookup.id))
|
||||
.limit(1)
|
||||
.then((r) => r[0]);
|
||||
|
||||
const nextId = mostRecentRow ? mostRecentRow.id + 1 : 1;
|
||||
|
||||
const accessCode = CalculateAccessCode(
|
||||
nextId,
|
||||
const row = await createAndInsertNewAccessCode(
|
||||
felicaMobileLookup,
|
||||
"FeliCa Mobile",
|
||||
req.idm,
|
||||
"01035",
|
||||
Config.AIMEDB_CONFIG.AIME_MOBILE_CARD_KEY
|
||||
);
|
||||
|
||||
logger.verbose(`Created FeliCa Mobile access code for serial ${nextId}.`, {
|
||||
nextId,
|
||||
accessCode,
|
||||
});
|
||||
|
||||
const value = { idm: req.idm.toString(16), accessCode };
|
||||
const row = await db
|
||||
.insert(felicaMobileLookup)
|
||||
.values(value)
|
||||
.returning()
|
||||
.then((r) => r[0]);
|
||||
|
||||
if (!row) {
|
||||
logger.crit("Failed to insert new lookup entry into the database.", value);
|
||||
header.result = ResultCodes.UNKNOWN_ERROR;
|
||||
resp.felicaIndex = -1;
|
||||
return resp;
|
||||
|
|
|
@ -35,14 +35,20 @@ const AimeDBServerFactory = () => {
|
|||
|
||||
const AIMEDB_KEY = Buffer.from(Config.AIMEDB_CONFIG.KEY, "utf-8");
|
||||
|
||||
const logResponse = (header: InstanceType<typeof PacketHeaderStruct>, body: unknown) => {
|
||||
const logResponse = (
|
||||
socket: Socket,
|
||||
header: InstanceType<typeof PacketHeaderStruct>,
|
||||
body: unknown
|
||||
) => {
|
||||
if (header.result !== 1) {
|
||||
logger.info(`Returned result code ${header.result}.`, {
|
||||
fromIp: socket.remoteAddress,
|
||||
header,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
logger.verbose(`Returned result code ${header.result}.`, {
|
||||
fromIp: socket.remoteAddress,
|
||||
header,
|
||||
body,
|
||||
});
|
||||
|
@ -84,16 +90,16 @@ const AimeDBServerFactory = () => {
|
|||
});
|
||||
|
||||
socket.on("data", async (data) => {
|
||||
logger.verbose(`Received packet: ${data.toString("hex")}.`);
|
||||
logger.debug(`Received packet: ${data.toString("hex")}.`, {
|
||||
ip: socket.remoteAddress,
|
||||
});
|
||||
|
||||
let packet: Buffer;
|
||||
|
||||
try {
|
||||
packet = decryptPacket(data, AIMEDB_KEY);
|
||||
} catch (err) {
|
||||
logger.error("Could not decrypt AimeDB packet.", {
|
||||
err,
|
||||
});
|
||||
logger.error("Could not decrypt AimeDB packet.", { err });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -104,29 +110,35 @@ const AimeDBServerFactory = () => {
|
|||
} catch (err) {
|
||||
logger.error("Decoding AimeDB header failed.", {
|
||||
err,
|
||||
data: packet.toString("hex"),
|
||||
header: packet.slice(0, 32).toString("hex"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Received AimeDB request.", {
|
||||
header,
|
||||
data: packet.slice(32).toString("hex"),
|
||||
});
|
||||
|
||||
if (header.magic !== 0xa13e) {
|
||||
logger.error("Request's magic bytes did not match expected value 0xA13E.", {
|
||||
ip: socket.remoteAddress,
|
||||
header,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.keychipId === "ABCD1234567" || header.storeId === 0xfff0) {
|
||||
logger.warning("Received request from uninitialized AMLib.", { header });
|
||||
logger.warning("Received request from uninitialized AMLib.", {
|
||||
ip: socket.remoteAddress,
|
||||
header,
|
||||
body: packet.slice(0, 32).toString("hex"),
|
||||
});
|
||||
} else {
|
||||
logger.debug("Received AimeDB request.", {
|
||||
ip: socket.remoteAddress,
|
||||
header,
|
||||
body: packet.slice(0, 32).toString("hex"),
|
||||
});
|
||||
}
|
||||
|
||||
if (header.commandId === CommandId.CLIENT_END_REQUEST) {
|
||||
logger.debug("Client ended the session.", { header });
|
||||
logger.debug("Client ended the session.", { ip: socket.remoteAddress });
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
@ -142,6 +154,8 @@ const AimeDBServerFactory = () => {
|
|||
);
|
||||
} else if (expectedLength !== header.length || packet.length !== header.length) {
|
||||
logger.error("Packet does not have expected size.", {
|
||||
ip: socket.remoteAddress,
|
||||
header,
|
||||
expectedLength,
|
||||
actualLength: packet.length,
|
||||
declaredLength: header.length,
|
||||
|
@ -150,7 +164,7 @@ const AimeDBServerFactory = () => {
|
|||
header.result = ResultCodes.UNKNOWN_ERROR;
|
||||
header.length = 32;
|
||||
|
||||
logResponse(header, null);
|
||||
logResponse(socket, header, null);
|
||||
writeResponse(socket, header, null);
|
||||
|
||||
return;
|
||||
|
@ -159,13 +173,16 @@ const AimeDBServerFactory = () => {
|
|||
const handler = AIMEDB_HANDLERS[header.commandId];
|
||||
|
||||
if (!handler) {
|
||||
logger.error("No handlers available for the requested command ID.", { header });
|
||||
logger.error("No handlers available for the requested command ID.", {
|
||||
ip: socket.remoteAddress,
|
||||
header,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const body = await handler(header, packet.slice(32));
|
||||
|
||||
logResponse(header, body);
|
||||
logResponse(socket, header, body);
|
||||
writeResponse(socket, header, body);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue