Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add .nvmrc and .node-version files #1

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 5 additions & 103 deletions src/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,66 +70,6 @@ function validateAuthStart({ action, privLvl, authenType, authenService, userLen
}
}

export const STATUSES = {
TAC_PLUS_AUTHEN_STATUS_PASS: 0x01,
TAC_PLUS_AUTHEN_STATUS_FAIL: 0x02,
TAC_PLUS_AUTHEN_STATUS_GETDATA: 0x03,
TAC_PLUS_AUTHEN_STATUS_GETUSER: 0x04,
TAC_PLUS_AUTHEN_STATUS_GETPASS: 0x05,
TAC_PLUS_AUTHEN_STATUS_RESTART: 0x06,
TAC_PLUS_AUTHEN_STATUS_ERROR: 0x07,
TAC_PLUS_AUTHEN_STATUS_FOLLOW: 0x21,
} as const

const ALLOWED_STATUSES = Object.values(STATUSES)

type Status = typeof ALLOWED_STATUSES[number]

function isStatus(maybeStatus: number): maybeStatus is Status {
return (ALLOWED_STATUSES as number[]).includes(maybeStatus)
}

export const AUTH_REPLY_FLAGS = {
TAC_PLUS_REPLY_NO_ACTION: 0x00,
TAC_PLUS_REPLY_FLAG_NOECHO: 0x01,
} as const

const ALLOWED_AUTH_REPLY_FLAGS = Object.values(AUTH_REPLY_FLAGS)

type ReplyFlag = typeof ALLOWED_AUTH_REPLY_FLAGS[number]

function isReplyFlag(maybeReplyFlag: number): maybeReplyFlag is ReplyFlag {
return (ALLOWED_AUTH_REPLY_FLAGS as number[]).includes(maybeReplyFlag)
}

interface AuthReplyRecord {
status: Status
flags: ReplyFlag
messageLength: number
contentLength: number
content: string | null
message: string | null
}

type UnknownAuthReply = Record<'status' | 'flags' | 'messageLength' | 'contentLength', number>

function validateAuthReply({ status, flags, messageLength, contentLength }: UnknownAuthReply) {
if (!isStatus(status)) {
throw new Error('Invalid status')
}

if (!isReplyFlag(flags)) {
throw new Error('Invalid reply flag')
}

return {
status,
flags,
messageLength,
contentLength,
}
}

export const AUTH_CONTINUE_FLAGS = {
TAC_PLUS_CONTINUE_FLAG_ABORT: 0x01,
} as const
Expand Down Expand Up @@ -179,7 +119,7 @@ const TAC_PLUS_VIRTUAL_PORT = ''
const TAC_PLUS_VIRTUAL_REM_ADDR = ''

interface CreateAuthContinueArgs {
password: string
userMsg: string
flags: number
data?: Buffer
}
Expand Down Expand Up @@ -240,43 +180,6 @@ export class Authentication {
}
}

static decodeAuthReply(data: Buffer, length: HeaderRecord['length']): AuthReplyRecord {
if (data.length < Authentication.REPLY_MIN_LENGTH) {
throw new Error('Invalid reply header length')
}

const authReply = validateAuthReply({
status: (data.readUInt8(0) & 0xF),
flags: (data.readUInt8(1) & 0xF),
messageLength: data.readUInt16BE(2),
contentLength: data.readUInt16BE(4),
})

let message = null
let content = null
let pos = 6

if (authReply.messageLength > 0) {
message = data.subarray(pos, pos + authReply.messageLength).toString('ascii')
pos += authReply.messageLength
}

if (authReply.contentLength > 0) {
content = data.subarray(pos, pos + authReply.contentLength).toString('utf8')
pos += authReply.contentLength
}

if (pos !== length) {
throw new Error('Incorrect length in header')
}

return {
...authReply,
message,
content,
}
}

static decodeAuthContinue(data: Buffer, length: HeaderRecord['length']): AuthContinueRecord {
if (data.length < Authentication.CONTINUE_MIN_LENGTH) {
throw new Error('Invalid continue header length')
Expand Down Expand Up @@ -372,20 +275,19 @@ export class Authentication {
}

static createAuthContinue2(args: CreateAuthContinueArgs) {
const password = args.password
const userMsg = args.userMsg
const data = args.data ?? Buffer.alloc(0)
const flags = args.flags ?? 0 // TODO(lwvemike): see why this is needed 0

const passwordBuffer = Buffer.from(password)
const userMsgBuffer = Buffer.from(userMsg)
const header = Buffer.alloc(5)
header.writeUInt16BE(passwordBuffer.length, 0)
header.writeUInt16BE(userMsgBuffer.length, 0)
header.writeUInt16BE(data.length, 2)
header.writeUInt8(flags, 4)

return Buffer.concat([header, passwordBuffer, data])
return Buffer.concat([header, userMsgBuffer, data])
}

static readonly START_MIN_LENGTH = 8
static readonly REPLY_MIN_LENGTH = 6
static readonly CONTINUE_MIN_LENGTH = 5
}
131 changes: 131 additions & 0 deletions src/authentication/AuthenticationReply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import type { Buffer } from 'node:buffer'
import type { ToHumanReadable } from '../types'
import { getNameFromCollectionValue } from '../utils'
import type { Header } from '../header'

function validateAuthReply({ status, flags, serverMsgLen, dataLen }: UnknownAuthReply) {
if (!isStatus(status)) {
throw new Error('Invalid status')
}

if (!isReplyFlag(flags)) {
throw new Error('Invalid reply flag')
}

return {
status,
flags,
serverMsgLen,
dataLen,
}
}

export interface AuthReplyRecord {
status: Status
flags: ReplyFlag
serverMsgLen: number
dataLen: number
data: string | null
serverMsg: string | null
}

export type UnknownAuthReply = Record<'status' | 'flags' | 'serverMsgLen' | 'dataLen', number>

/**
* @throws Error
*/
export class AuthenticationReply implements ToHumanReadable {
readonly #status: AuthReplyRecord['status']
readonly #serverMsg: AuthReplyRecord['serverMsg']
readonly #flags: AuthReplyRecord['flags']
readonly #serverMsgLen: AuthReplyRecord['serverMsgLen']
readonly #dataLen: AuthReplyRecord['dataLen']
readonly #data: AuthReplyRecord['data']

constructor(body: Buffer, length: Header['length']) {
if (body.length < AuthenticationReply.MIN_LENGTH) {
throw new Error('Invalid reply header length')
}

const reply = validateAuthReply({
status: (body.readUInt8(0) & 0xF),
flags: (body.readUInt8(1) & 0xF),
serverMsgLen: body.readUInt16BE(2),
dataLen: body.readUInt16BE(4),
})

let message = null
let content = null
let pos = 6

if (reply.serverMsgLen > 0) {
message = body.subarray(pos, pos + reply.serverMsgLen).toString('ascii')
pos += reply.serverMsgLen
}

if (reply.dataLen > 0) {
content = body.subarray(pos, pos + reply.dataLen).toString('utf8')
pos += reply.dataLen
}

if (pos !== length) {
throw new Error('Incorrect length in header')
}

this.#status = reply.status
this.#serverMsg = message
this.#flags = reply.flags
this.#serverMsgLen = reply.serverMsgLen
this.#dataLen = reply.dataLen
this.#data = content
}

get status() {
return this.#status
}

toHumanReadable() {
return (
`status: ${this.#status} | ${getNameFromCollectionValue(this.#status, AuthenticationReply.STATUSES)}
flags: ${this.#flags} | ${getNameFromCollectionValue(this.#flags, AuthenticationReply.FLAGS)}
serverMsgLen: ${this.#serverMsgLen}
dataLen: ${this.#dataLen}
serverMsg: ${this.#serverMsg}
data: ${this.#data}`
)
}

static readonly MIN_LENGTH = 6

static STATUSES = {
TAC_PLUS_AUTHEN_STATUS_PASS: 0x01,
TAC_PLUS_AUTHEN_STATUS_FAIL: 0x02,
TAC_PLUS_AUTHEN_STATUS_GETDATA: 0x03,
TAC_PLUS_AUTHEN_STATUS_GETUSER: 0x04,
TAC_PLUS_AUTHEN_STATUS_GETPASS: 0x05,
TAC_PLUS_AUTHEN_STATUS_RESTART: 0x06,
TAC_PLUS_AUTHEN_STATUS_ERROR: 0x07,
TAC_PLUS_AUTHEN_STATUS_FOLLOW: 0x21,
} as const

static FLAGS = {
TAC_PLUS_REPLY_NO_ACTION: 0x00,
TAC_PLUS_REPLY_FLAG_NOECHO: 0x01,
} as const
}

const ALLOWED_STATUSES = Object.values(AuthenticationReply.STATUSES)

type Status = typeof ALLOWED_STATUSES[number]

function isStatus(maybeStatus: number): maybeStatus is Status {
return (ALLOWED_STATUSES as number[]).includes(maybeStatus)
}

const ALLOWED_AUTH_REPLY_FLAGS = Object.values(AuthenticationReply.FLAGS)

type ReplyFlag = typeof ALLOWED_AUTH_REPLY_FLAGS[number]

function isReplyFlag(maybeReplyFlag: number): maybeReplyFlag is ReplyFlag {
return (ALLOWED_AUTH_REPLY_FLAGS as number[]).includes(maybeReplyFlag)
}
Loading
Loading