diff --git a/Protest/Front/hexviewer.css b/Protest/Front/hexviewer.css index 2839b2e2..eb623edb 100644 --- a/Protest/Front/hexviewer.css +++ b/Protest/Front/hexviewer.css @@ -51,9 +51,9 @@ height: 20px; font-size: large; font-family: monospace; - line-height: 16px; text-align: center; border-radius: 2px; + margin: 1px 0px; } .hexviewer-hexbox > div > div:nth-child(16n + 8), diff --git a/Protest/Front/hexviewer.js b/Protest/Front/hexviewer.js index 456209d4..d008a542 100644 --- a/Protest/Front/hexviewer.js +++ b/Protest/Front/hexviewer.js @@ -9,6 +9,7 @@ class HexViewer extends Window { 16: "TXT", 28: "AAAA", 33: "SRV", + 47: "NSEC", 255: "ANY", }; @@ -375,7 +376,7 @@ class HexViewer extends Window { } } - PopulateLabel(label, indentation, hexContainer, charContainer, offset, length) { + PopulateLabel(label, indentation, hexContainer, charContainer, offset, length, isDnsPointers=false) { const element = document.createElement("div"); element.style.paddingLeft = `${8 + indentation * 20}px`; this.list.appendChild(element); @@ -438,7 +439,7 @@ class HexViewer extends Window { this.lastIndentationValue = indentation; this.lastIndentationElement = element; - element.onclick = event=> { + element.onclick = ()=> { const hexElements = hexContainer.childNodes; const charElements = charContainer.childNodes; const listElements = this.list.childNodes; @@ -471,13 +472,25 @@ class HexViewer extends Window { charElements[i].scrollIntoView({ block:"center", inline:"center" }); } + if (isDnsPointers) { + if (length === 2) { + const byteA = parseInt("0x"+hexElements[offset].textContent, 16); + const byteB = parseInt("0x"+hexElements[offset+1].textContent, 16); + if ((byteA & 0xC0) === 0xC0) { + let pointer = ((byteA & 0x3F) << 8) | byteB; + hexElements[pointer].style.color = "#000"; + hexElements[pointer].style.backgroundColor = "var(--clr-warning)"; + } + } + } + element.style.color = "#000"; element.style.backgroundColor = "var(--clr-select)"; }; return element; } - + PopulateDnsLabels(hexContainer, charContainer, stream) { const transactionId = stream[0].toString(16).padStart(2,"0") + stream[1].toString(16).padStart(2,"0"); this.PopulateLabel(`Transaction ID: 0x${transactionId}`, 0, hexContainer, charContainer, 0, 2); @@ -519,97 +532,104 @@ class HexViewer extends Window { const adCount = stream[10] << 8 | stream[11]; this.PopulateLabel(`Additional RRs: ${adCount}`, 0, hexContainer, charContainer, 10, 2); - let offset = 12; + let index = 12; let count = 0; - while (offset < stream.length && count < qCount) { //questions - const start = offset; + while (index < stream.length && count < qCount) { //questions + const start = index; let end = start; - if (stream[offset] === 0xC0) { //pointer + + if ((stream[index] & 0xC0) === 0xC0) { //pointer end += 2; } else { - while (end < stream.length && stream[end] !== 0 && stream[end] !== 0xC0) { + while (end < stream.length && stream[end] !== 0 && (stream[end] & 0xC0) !== 0xC0) { end++; } if (stream[end] === 0) { //null termination end++; } - else if (stream[end] === 0xC0) { //pointer + else if ((stream[end] & 0xC0) === 0xC0) { //pointer end += 2; } } - const first = this.PopulateLabel("Name", 1, hexContainer, charContainer, start, end - start - 1); - offset = end; + const first = this.PopulateLabel("Name", 1, hexContainer, charContainer, start, end - start - 1, true); + index = end; + + let type = (stream[index] << 8) | stream[index+1]; + this.PopulateLabel(`Type: ${type} ${HexViewer.DNS_RECORD_TYPES[type] ? `(${HexViewer.DNS_RECORD_TYPES[type]})` : ""}`, 1, hexContainer, charContainer, index, 2); + index += 2; - let type = (stream[offset] << 8) | stream[offset+1]; - this.PopulateLabel(`Type: ${type} ${HexViewer.DNS_RECORD_TYPES[type] ? `(${HexViewer.DNS_RECORD_TYPES[type]})` : ""}`, 1, hexContainer, charContainer, offset, 2); - offset += 2; + const cacheFlashFlag = stream[index] & 0x80; + if (cacheFlashFlag > 0) { + this.PopulateLabel(`Cache-flush: true`, 1, hexContainer, charContainer, index, 2); + } - let class_ = (stream[offset] << 8) | stream[offset+1]; - this.PopulateLabel(`Class: ${class_} ${HexViewer.DNS_CLASSES[class_] ? `(${HexViewer.DNS_CLASSES[class_]})` : ""}`, 1, hexContainer, charContainer, offset, 2); + let class_ = stream[index+1]; + this.PopulateLabel(`Class: ${class_} ${HexViewer.DNS_CLASSES[class_] ? `(${HexViewer.DNS_CLASSES[class_]})` : ""}`, 1, hexContainer, charContainer, index, 2); - offset += 2; + index += 2; - const element = this.PopulateLabel("Question:", 0, hexContainer, charContainer, start, offset - start); + const element = this.PopulateLabel("Question:", 0, hexContainer, charContainer, start, index - start, true); this.list.insertBefore(element, first); count++; } const totalRecords = qCount + anCount + auCount + adCount; - while (offset < stream.length && count < totalRecords) { //records - let start = offset; - let end = offset; + while (index < stream.length && count < totalRecords) { //records + let start = index; + let end = index; - if (stream[offset] === 0xC0) { //pointer - console.log(this.hexBox.childNodes[offset]); + if ((stream[index] & 0xC0) === 0xC0) { //pointer end += 2; } else { - while (end < stream.length && stream[end] !== 0 && stream[end] !== 0xC0) { + while (end < stream.length && stream[end] !== 0 && (stream[end] & 0xC0) !== 0xC0) { end++; } if (stream[end] === 0) { //null termination end++; } - else if (stream[end] === 0xC0) { //pointer + else if ((stream[end] & 0xC0) === 0xC0) { //pointer end += 2; } } - console.log(offset.toString(16).padStart(2, "0"), end.toString(16).padStart(2, "0")); + const first = this.PopulateLabel("Name", 1, hexContainer, charContainer, start, end - start, true); + index = end; - const first = this.PopulateLabel("Name", 1, hexContainer, charContainer, start, end - start); + const type = (stream[index] << 8) | stream[index+1]; + this.PopulateLabel(`Type: ${type} ${HexViewer.DNS_RECORD_TYPES[type] ? `(${HexViewer.DNS_RECORD_TYPES[type]})` : ""}`, 1, hexContainer, charContainer, index, 2); + index += 2; - offset = end; - - let type = (stream[offset] << 8) | stream[offset+1]; - this.PopulateLabel(`Type: ${type} ${HexViewer.DNS_RECORD_TYPES[type] ? `(${HexViewer.DNS_RECORD_TYPES[type]})` : ""}`, 1, hexContainer, charContainer, offset, 2); - offset += 2; + const cacheFlashFlag = stream[index] & 0x80; + if (cacheFlashFlag > 0) { + this.PopulateLabel(`Cache-flush: true`, 1, hexContainer, charContainer, index, 2); + } - let class_ = (stream[offset] << 8) | stream[offset+1]; - this.PopulateLabel(`Class: ${class_} ${HexViewer.DNS_CLASSES[class_] ? `(${HexViewer.DNS_CLASSES[class_]})` : ""}`, 1, hexContainer, charContainer, offset, 2); - offset += 2; + const class_ = stream[index+1]; + this.PopulateLabel(`Class: ${class_} ${HexViewer.DNS_CLASSES[class_] ? `(${HexViewer.DNS_CLASSES[class_]})` : ""}`, 1, hexContainer, charContainer, index, 2); + index += 2; - let ttl = (stream[offset] << 24) | (stream[offset+1] << 16) | (stream[offset+2] << 8) | stream[offset+3]; - this.PopulateLabel(`TTL: ${ttl}`, 1, hexContainer, charContainer, offset, 4); - offset += 4; + const ttl = (stream[index] << 24) | (stream[index+1] << 16) | (stream[index+2] << 8) | stream[index+3]; + this.PopulateLabel(`TTL: ${ttl}`, 1, hexContainer, charContainer, index, 4); + index += 4; - let len = (stream[offset] << 8) | stream[offset+1]; - this.PopulateLabel(`Length: ${len}`, 1, hexContainer, charContainer, offset, 2); - offset += 2; + const len = (stream[index] << 8) | stream[index+1]; + this.PopulateLabel(`Length: ${len}`, 1, hexContainer, charContainer, index, 2); + index += 2; let data; switch (type) { case 1: //A if (len === 4) { - data = `${stream[offset]}.${stream[offset+1]}.${stream[offset+2]}.${stream[offset+3]}`; - this.PopulateLabel(data, 1, hexContainer, charContainer, offset, len); + data = `${stream[index]}.${stream[index+1]}.${stream[index+2]}.${stream[index+3]}`; + this.PopulateLabel(data, 1, hexContainer, charContainer, index, len); } break; @@ -618,22 +638,49 @@ class HexViewer extends Window { data = ""; for (let j = 0; j < 16; j+=2) { if (j > 0) data += ":"; - data += stream[offset + j].toString(16).padStart(2, "0"); - data += stream[offset + j + 1].toString(16).padStart(2, "0"); + data += stream[index + j].toString(16).padStart(2, "0"); + data += stream[index + j + 1].toString(16).padStart(2, "0"); } - this.PopulateLabel(data, 1, hexContainer, charContainer, offset, len); + this.PopulateLabel(data, 1, hexContainer, charContainer, index, len); } break; + case 33: //SRV + const priority = (stream[index] << 8) | stream[index + 1]; + const weight = (stream[index + 2] << 8) | stream[index + 3]; + const port = (stream[index + 4] << 8) | stream[index + 5]; + + let targetOffset = index + 6; + if ((stream[targetOffset] & 0xC0) === 0xC0) { //pointer + targetOffset += 2; + } + else { + while (targetOffset < stream.length && stream[targetOffset] !== 0 && (stream[targetOffset] & 0xC0) !== 0xC0) { + targetOffset++; + } + + if (stream[targetOffset] === 0) { //null termination + targetOffset++; + } + else if ((stream[targetOffset] & 0xC0) === 0xC0) { //pointer + targetOffset += 2; + } + } + + this.PopulateLabel(`Priority: ${priority}`, 1, hexContainer, charContainer, index, 2); + this.PopulateLabel(`Weight: ${weight}`, 1, hexContainer, charContainer, index + 2, 2); + this.PopulateLabel(`Port: ${port}`, 1, hexContainer, charContainer, index + 4, 2); + this.PopulateLabel("Target", 1, hexContainer, charContainer, index + 6, targetOffset - (index + 6), true); + break; + default: - this.PopulateLabel("Answer", 1, hexContainer, charContainer, offset, len); + this.PopulateLabel("Answer", 1, hexContainer, charContainer, index, len); break; } -//console.log("offset = " + offset + " + " + len); - offset += len; + index += len; - end = offset; + end = index; let element; if (count < qCount + anCount) { diff --git a/Protest/Protocols/Dns.cs b/Protest/Protocols/Dns.cs index 4a59dae1..bcbb6f0a 100644 --- a/Protest/Protocols/Dns.cs +++ b/Protest/Protocols/Dns.cs @@ -31,6 +31,7 @@ public enum RecordType : byte { TXT = 16, AAAA = 28, SRV = 33, + NSEC = 47, ANY = 255 } diff --git a/Protest/Protocols/Mdns.cs b/Protest/Protocols/Mdns.cs index bdb141ca..5698d924 100644 --- a/Protest/Protocols/Mdns.cs +++ b/Protest/Protocols/Mdns.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using System; using System.Collections.Generic; using System.IO; @@ -248,17 +249,16 @@ private static Answer[] DeconstructResponse(byte[] response, RecordType queryTyp } List result = new List(); - int totalRecords = answerCount + authorityCount + additionalCount; - for (int i = 0; i < totalRecords; i++) { - Console.WriteLine($"Starting at: {index.ToString("x2")}"); + for (int i = 0; i < totalRecords; i++) { - if (index + 4 >= response.Length) { break; } + if (index + 10 >= response.Length) { break; } int nameStartIndex; - if ((response[index] & 0xFF) == 0xC0) { //pointer - nameStartIndex = response[index + 1]; + if ((response[index] & 0xC0) == 0xC0) { //pointer + if (index + 1 >= response.Length) { break; } + nameStartIndex = ((response[index] & 0x3F) << 8) | response[index + 1]; index += 2; } else { @@ -278,13 +278,9 @@ private static Answer[] DeconstructResponse(byte[] response, RecordType queryTyp } } - Answer ans = new Answer(); - ans.remote = remoteEndPoint; - - if (index + 10 > response.Length) { - ans.error = 254; - break; - } + Answer ans = new Answer() { + remote = remoteEndPoint + }; ans.questionString = ExtractName(response, nameStartIndex); @@ -293,6 +289,10 @@ private static Answer[] DeconstructResponse(byte[] response, RecordType queryTyp index += 2; //skip class + if (index + 4 > response.Length) { + ans.error = 254; + break; + } ans.ttl = (response[index] << 24) | (response[index + 1] << 16) | (response[index + 2] << 8) | response[index + 3]; index += 4; @@ -336,9 +336,19 @@ private static Answer[] DeconstructResponse(byte[] response, RecordType queryTyp index += ans.length; break; + case RecordType.SRV: + if (index + 6 <= response.Length) { + //ans.priority = (ushort)((response[index] << 8) | response[index + 1]); + //ans.weight = (ushort)((response[index+2] << 8) | response[index + 3]); + //ans.port = (ushort)((response[index+4] << 8) | response[index + 5]); + //index += 6; + ans.answerString = ExtractName(response, index); + } + index += ans.length; + break; + default: ans.answerString = String.Empty; //BitConverter.ToString(response, index, ans.length); - index += ans.length; break; } @@ -359,15 +369,15 @@ private static string ExtractName(byte[] response, int startIndex) { StringBuilder name = new StringBuilder(); int index = startIndex; - while (response[index] != 0) { - if ((response[index] & 0xFF) == 0xC0) { //compressed name + while (index < response.Length && response[index] != 0) { + if ((response[index] & 0xC0) == 0xC0) { //is pointer int pointer = ((response[index] & 0x3F) << 8) | response[index + 1]; name.Append(ExtractName(response, pointer)); break; } else { int length = response[index++]; - for (int i = 0; i < length; i++) { + for (int i = 0; i < length && index < response.Length; i++) { name.Append((char)response[index++]); } name.Append('.'); @@ -476,6 +486,11 @@ private static byte[] Serialize(byte[] query, List data, List an builder.Append($"\"name\":\"{answers[i].answerString}\","); break; + case RecordType.NSEC: + builder.Append("\"type\":\"NSEC\","); + builder.Append($"\"name\":\"{answers[i].answerString}\","); + break; + case RecordType.SRV: builder.Append("\"type\":\"SRV\","); builder.Append($"\"name\":\"{answers[i].answerString}\",");