diff --git a/src/data/txt/http.txtcap b/src/data/txt/http.txtcap index ac18a87..454eff9 100644 --- a/src/data/txt/http.txtcap +++ b/src/data/txt/http.txtcap @@ -12,3 +12,17 @@ 00b0 63 6f 6d 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a com..Connection: 00c0 20 4b 65 65 70 2d 41 6c 69 76 65 0d 0a 0d 0a Keep-Alive.... +0000 00 e0 81 00 b0 28 00 09 6b 88 f5 c9 08 00 45 00 .....(..k.....E. +0010 00 c1 d2 49 40 00 80 06 c8 5b 0a 00 00 05 cf 2e ...I@....[...... +0020 86 5e 0c c3 00 50 a8 00 76 87 7d e0 14 02 50 18 .^...P..v.}...P. +0030 fa f0 ad 62 00 00 48 45 41 44 20 2f 76 34 2f 69 ...b..HEAD /v4/i +0040 75 69 64 65 6e 74 2e 63 61 62 3f 30 33 30 37 30 uident.cab?03070 +0050 31 31 32 30 38 20 48 54 54 50 2f 31 2e 31 0d 0a 11208 HTTP/1.1.. +0060 41 63 63 65 70 74 3a 20 2a 2f 2a 0d 0a 55 73 65 Accept: */*..Use +0070 72 2d 41 67 65 6e 74 3a 20 49 6e 64 75 73 74 72 r-Agent: Industr +0080 79 20 55 70 64 61 74 65 20 43 6f 6e 74 72 6f 6c y Update Control +0090 0d 0a 48 6f 73 74 3a 20 77 69 6e 64 6f 77 73 75 ..Host: windowsu +00a0 70 64 61 74 65 2e 6d 69 63 72 6f 73 6f 66 74 2e pdate.microsoft. +00b0 63 6f 6d 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a com..Connection: +00c0 20 4b 65 65 70 2d 41 6c 69 76 65 0d 0a 0d 0a Keep-Alive.... + diff --git a/src/headerP/utils.ts b/src/headerP/utils.ts index 30f5529..05363ee 100644 --- a/src/headerP/utils.ts +++ b/src/headerP/utils.ts @@ -7,6 +7,19 @@ import { filter_dict, tcp_result, } from "."; +import { + char, + choice, + endOfInput, + everyCharUntil, + many, + many1, + possibly, + regex, + sequence, + tup, + whitespace, +} from "../parser"; export enum tcp_flagE { "ns", @@ -41,6 +54,34 @@ export interface taged_value { // HACK const reHexDigits = /^[0-9a-fA-F]+/; +export type parsed_Frame = [ + string, + string, + string | null, + string, + string | null +]; + +export const InputFrameParser = many1( + sequence( + tup( + regex(reHexDigits), + many1(sequence(tup(whitespace, regex(reHexDigits)))).map((res) => + res.map((e) => e[1]).join("") + ), + possibly(whitespace), + everyCharUntil(choice(tup(char("\n"), endOfInput))), + choice(tup(char("\n"), endOfInput)) + ) + ) +); + +export const inputFramesParser = many( + sequence<[parsed_Frame[], string | null], null>( + tup(InputFrameParser, possibly(char("\n"))) + ) +); + export function filter(o: string[][], data: header_type[]): any { let cond_parm = ["arp", "ipv4", "ipv6", "tcp", "http", "icmp"]; let arg_parm: { [key: string]: (x: header_type, e: string) => boolean } = { @@ -210,12 +251,18 @@ export const convertToBin = (data: string): Uint8Array => { }; // cleaning the offset, the hex-visualisation, and the spaces -export const cleanInput = (data: string): string => { - return data - .split("\n") - .map((s) => s.slice(5, Math.min(s.length, 54))) - .join("") - .replace(/\s/g, ""); +export const cleanInput = (data: string) => { + let result = inputFramesParser.run(data); + + if (result.isError) { + console.log(result); + throw new Error(result.error as string); + } + + let unwraped = result.result.map((x) => x[0].map((frame) => frame[1])); + let out: string[] = []; + for (let a in unwraped) out = [...out, ...unwraped[a]]; + return out.join(""); }; // Use it only if executed by node diff --git a/src/index.ts b/src/index.ts index f637459..55efa4e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ import cli from "./cli"; -cli(); \ No newline at end of file +cli() \ No newline at end of file diff --git a/src/parser/pComb.ts b/src/parser/pComb.ts index 05cec6a..9211d96 100644 --- a/src/parser/pComb.ts +++ b/src/parser/pComb.ts @@ -1,5 +1,6 @@ import Parser, { ParserTuple } from "./parser"; import ParserState from "./pState"; +import { decoder } from "./utils"; /** Takes an array of parsers, and returns a new parser that matches each of them sequentially, collecting up the results into an array. */ export const sequence = (parsers: ParserTuple) => @@ -26,6 +27,7 @@ export const many = function many(parser: Parser): Parser { while (true) { const out = parser.pf(nextState); + console.log(out.index) if (out.isError) { break; @@ -87,3 +89,74 @@ export function coroutine(parserFn: ParserFn): Parser { } }); } + +export const many1 = function many1(parser: Parser): Parser { + return new Parser(function many1$state(state) { + if (state.isError) return state; + + const resState = many(parser).pf(state); + if (resState.result.length) return resState; + + return state.updateError( + `ParseError 'many1' (position ${state.index}): Expecting to match at least one value` + ); + }); +}; + +export function everythingUntil(parser: Parser): Parser { + return new Parser((state) => { + if (state.isError) return state; + + const results = []; + let nextState = state; + + while (true) { + const out = parser.pf(nextState); + + if (out.isError) { + const { index, dataView } = nextState; + + if (dataView.byteLength <= index) { + return nextState.updateError( + `ParseError 'everythingUntil' (position ${nextState.index}): Unexpected end of input.` + ); + } + + const val = dataView.getUint8(index); + if (val) { + results.push(val); + nextState = nextState.updateByteIndex(1).updateResult(val); + } + } else { + break; + } + } + + return nextState.updateResult(results); + }); +} +// TODO type return type +export const choice = (parsers: ParserTuple) => { + if (parsers.length === 0) throw new Error(`List of parsers can't be empty.`); + + return new Parser(function choice$state(state) { + if (state.isError) return state; + + let error = null; + for (const parser of parsers) { + const out = parser.pf(state); + + if (!out.isError) return out; + + if (error === null || (error && out.index > error.index)) { + error = out; + } + } + + return error as ParserState; + }); +}; + +export const everyCharUntil = (parser: Parser) => everythingUntil(parser) + .map(results => decoder.decode(Uint8Array.from(results))); + diff --git a/src/parser/pGen.ts b/src/parser/pGen.ts index c0fb4cf..30534a6 100644 --- a/src/parser/pGen.ts +++ b/src/parser/pGen.ts @@ -1,7 +1,15 @@ import Parser, { ParsingFunction } from "./parser"; +import { many } from "./pComb"; +import ParserState, { InputTypes } from "./pState"; import { encoder, + getCharacterLength, + getNextCharWidth, getString, + getUtf8Char, + reLetter, + reLetters, + reWhitespaces, } from "./utils"; /* doesn't support the bitOffset */ @@ -95,3 +103,76 @@ export const addIndex = (n: number) => export const addByteIndex = (n: number) => new Parser((s) => (s.isError ? s : s.updateBitIndex(n))); + +export const whitespace: Parser = regex(reWhitespaces).errorMap( + ({ index }) => + `ParseError 'whitespace' (position ${index}): Expecting to match at least one space` +); + +export const letters: Parser = regex(reLetters).errorMap( + ({ index }) => + `ParseError 'letters' (position ${index}): expecting to match at least one letter` +); + +export const char = function char(c: string): Parser { + if (!c || getCharacterLength(c) !== 1) { + throw new TypeError( + `char must be called with a single character, but got ${c}` + ); + } + + return new Parser(function char$state(state) { + if (state.isError) return state; + + const { index, dataView } = state; + if (index < dataView.byteLength) { + const charWidth = getNextCharWidth(index, dataView); + if (index + charWidth <= dataView.byteLength) { + const char = getUtf8Char(index, charWidth, dataView); + return char === c + ? state.updateByteIndex(charWidth).updateResult(c) + : state.updateError( + `ParseError (position ${index}): Expecting character '${c}', got '${char}'` + ); + } + } + return state.updateError( + `ParseError (position ${index}): Expecting character '${c}', but got end of input.` + ); + }); +}; + +export const anyChar: Parser = new Parser(function anyChar$state( + state +) { + if (state.isError) return state; + + const { index, dataView } = state; + if (index < dataView.byteLength) { + const charWidth = getNextCharWidth(index, dataView); + if (index + charWidth <= dataView.byteLength) { + const char = getUtf8Char(index, charWidth, dataView); + return state.updateByteIndex(charWidth).updateResult(char); + } + } + return state.updateError( + `ParseError (position ${index}): Expecting a character, but got end of input.` + ); +}); + +export const endOfInput = new Parser(function endOfInput$state(state) { + if (state.isError) return state; + const { dataView, index, inputType } = state; + if (index !== dataView.byteLength) { + const errorByte = + inputType === InputTypes.STRING + ? String.fromCharCode(dataView.getUint8(index)) + : `0x${dataView.getUint8(index).toString(16).padStart(2, "0")}`; + + return state.updateError( + `ParseError 'endOfInput' (position ${index}): Expected end of input but got '${errorByte}'` + ); + } + + return state.updateResult(null); +});