/* Pi-hole: A black hole for Internet advertisements * (c) 2019 Pi-hole, LLC (https://pi-hole.net) * Network-wide ad blocking via your own hardware. * * Web Interface * Create fake API data * * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ const faker = require("faker"); const fs = require("fs-extra"); function unique(generator, size) { const data = []; while (data.length !== size) { const item = generator(); if (!data.includes(item)) data.push(item); } return data; } function list() { return unique(faker.internet.domainName, 10); } function status() { return { status: "enabled" }; } function auth() { return { status: "success" }; } function pastDate() { return Math.floor(faker.date.past().getTime() / 1000); } function history(length) { const startDate = pastDate(); return { cursor: null, history: new Array(length).fill(null).map((_, i) => { const isIPv4 = faker.random.boolean(); const isHostname = faker.random.boolean(); return { timestamp: startDate + i, type: isIPv4 ? 1 : 2, status: Math.floor(Math.random() * 6.9), domain: faker.internet.domainName(), client: isHostname ? faker.internet.domainWord() + ".local" : isIPv4 ? faker.internet.ip() : faker.internet.ipv6(), dnssec: Math.floor(Math.random() * 5.9), reply: Math.floor(Math.random() * 7.9), response_time: Math.floor(Math.random() * 100.9) }; }) }; } function summary() { const queryTypeTotals = [ faker.random.number(), // A faker.random.number(), // AAAA faker.random.number(), // ANY faker.random.number(), // SRV faker.random.number(), // SOA faker.random.number(), // PTR faker.random.number() // TXT ]; const total = queryTypeTotals.reduce( (previous, current) => previous + current, 0 ); const blocked = faker.random.number({ max: total }); const forwarded = faker.random.number({ max: total }); const clients = faker.random.number(); return { gravity_size: faker.random.number(), sum_queries: total, total_queries: { A: queryTypeTotals[0], AAAA: queryTypeTotals[1], ANY: queryTypeTotals[2], SRV: queryTypeTotals[3], SOA: queryTypeTotals[4], PTR: queryTypeTotals[5], TXT: queryTypeTotals[6] }, blocked_queries: blocked, percent_blocked: (blocked * 100) / total, unique_domains: faker.random.number({ max: total }), forwarded_queries: forwarded, cached_queries: total - forwarded, reply_types: { IP: total / 5, CNAME: total / 5, DOMAIN: total / 5, NODATA: total / 5, NXDOMAIN: total / 5 }, total_clients: clients, active_clients: clients - faker.random.number({ max: clients }), ...status() }; } function queryTypes() { const data = []; const total = faker.random.number(); const names = ["A", "AAAA", "ANY", "SRV", "SOA", "PTR", "TXT"]; const count = total / names.length; for (const name of names) { data.push({ name, count }); } return data; } function upstreams(length) { const upstreams = []; const numbers = []; const names = unique(faker.internet.domainName, length); const ipAddrs = unique(faker.internet.ip, length); let forwardedQueries = 0; for (let i = 0; i < length; i++) { const number = faker.random.number(); forwardedQueries += number; numbers.push(number); } for (let i = 0; i < length; i++) { upstreams.push({ name: names[i], ip: ipAddrs[i], count: numbers[i] }); } return { upstreams, forwarded_queries: forwardedQueries, total_queries: forwardedQueries + faker.random.number() }; } function topDomainList(length, max) { const result = []; const numbers = []; const domains = unique(faker.internet.domainName, length); for (let i = 0; i < length; i++) numbers.push(faker.random.number({ max: max })); numbers.sort((a, b) => (parseInt(a, 10) > parseInt(b, 10) ? -1 : 1)); for (let i = 0; i < length; i++) { result.push({ domain: domains[i], count: numbers[i] }); } return result; } function topBlockedDomains(length) { const totalQueries = faker.random.number(); return { top_domains: topDomainList(length, totalQueries), blocked_queries: totalQueries }; } function topDomains(length) { const totalQueries = faker.random.number(); return { top_domains: topDomainList(length, totalQueries), total_queries: totalQueries }; } function topClientList(length, totalQueries) { const top_clients = []; const numbers = []; const clients = unique(faker.internet.ip, length); for (let i = 0; i < length; i++) numbers.push(faker.random.number({ max: totalQueries })); numbers.sort((a, b) => (parseInt(a, 10) > parseInt(b, 10) ? -1 : 1)); for (let i = 0; i < length; i++) { top_clients.push({ name: "", ip: clients[i], count: numbers[i] }); } return top_clients; } function topClients(length) { const totalQueries = faker.random.number(); return { top_clients: topClientList(length, totalQueries), total_queries: totalQueries }; } function topBlockedClients(length) { const totalQueries = faker.random.number(); return { top_clients: topClientList(length, totalQueries), blocked_queries: totalQueries }; } function clientsOverTime(range, size) { const startDate = pastDate(); const clients = unique(faker.internet.ip, size).map(ip => ({ name: faker.internet.domainWord(), ip })); const graph = []; for (let i = 0; i < range; i++) { graph.push({ timestamp: startDate + 600 * i, data: new Array(size).fill(null).map(() => faker.random.number()) }); } return { over_time: graph, clients: clients }; } function historyOverTime(range) { const startDate = pastDate(); const history = []; for (let i = 0; i < range; i++) { const totalQueries = faker.random.number(); const blockedQueries = faker.random.number({ max: totalQueries }); const timestamp = startDate + 600 * i; history.push({ timestamp, total_queries: totalQueries + blockedQueries, blocked_queries: blockedQueries }); } return history; } function write(filePath, data) { fs.outputFileSync(filePath, JSON.stringify(data)); } function getNetworkInfo() { return { interface: faker.random.arrayElement(["eth0", "eth1", "wlan0", "wlan1"]), ipv4_address: faker.internet.ip(), ipv6_address: faker.internet.ipv6(), hostname: faker.random.word().toLowerCase().split(" ", 2)[0] }; } function getFTLdb() { return { queries: faker.random.number({ min: 50000, max: 1000000 }), filesize: faker.random.number({ min: 1000, max: 100000 }), sqlite_version: "3.0.1" }; } function getVersionInfo() { return { core: { branch: faker.random.arrayElement([ "master", "development", "FTL", "beta", "test" ]), hash: faker.internet.color().substring(1) + faker.random.number(9), tag: "vDev" }, ftl: { branch: faker.random.arrayElement([ "master", "development", "FTL", "beta", "test" ]), hash: faker.internet.color().substring(1) + faker.random.number(9), tag: "vDev" }, web: { branch: faker.random.arrayElement([ "master", "development", "FTL", "beta", "test" ]), hash: faker.internet.color().substring(1) + faker.random.number(9), tag: "vDev" } }; } function getDHCPInfo() { return { active: faker.random.boolean(), ip_start: faker.internet.ip(), ip_end: faker.internet.ip(), router_ip: faker.internet.ip(), lease_time: faker.random.number({ min: 1, max: 99 }), domain: faker.random.word().toLowerCase().split(" ", 2)[0], ipv6_support: faker.random.boolean(), rapid_commit: faker.random.boolean() }; } function getDNSInfo() { return { upstream_dns: [ faker.internet.ip(), faker.internet.ip(), faker.internet.ip() ], options: { fqdn_required: faker.random.boolean(), bogus_priv: faker.random.boolean(), dnssec: faker.random.boolean(), listening_type: faker.random.arrayElement(["single", "local", "all"]) }, conditional_forwarding: { enabled: faker.random.boolean(), ip: faker.internet.ip(), domain: faker.random.word().toLowerCase().split(" ", 2)[0], cidr: faker.random.arrayElement([8, 16, 24, 32]) } }; } function getPreferences() { return { layout: "boxed", language: "en" }; } function cacheinfo() { return { cache_size: faker.random.number(), cache_evicted: faker.random.number(), cache_inserted: faker.random.number() }; } console.log("Deleting old fake API data..."); fs.emptyDirSync("public/fakeAPI/dns"); fs.emptyDirSync("public/fakeAPI/stats"); fs.emptyDirSync("public/fakeAPI/settings"); fs.removeSync("public/fakeAPI/auth"); fs.removeSync("public/fakeAPI/version"); console.log("Generating new fake API data..."); write("public/fakeAPI/dns/whitelist", list()); write("public/fakeAPI/dns/blacklist", list()); write("public/fakeAPI/dns/regexlist", list()); write("public/fakeAPI/dns/status", status()); write("public/fakeAPI/dns/cacheinfo", cacheinfo()); write("public/fakeAPI/settings/dhcp", getDHCPInfo()); write("public/fakeAPI/settings/dns", getDNSInfo()); write("public/fakeAPI/settings/network", getNetworkInfo()); write("public/fakeAPI/settings/ftldb", getFTLdb()); write("public/fakeAPI/settings/web", getPreferences()); write("public/fakeAPI/stats/overTime/history", historyOverTime(144)); write("public/fakeAPI/stats/overTime/clients", clientsOverTime(144, 5)); write("public/fakeAPI/stats/summary", summary()); write("public/fakeAPI/stats/history", history(5000)); write("public/fakeAPI/stats/query_types", queryTypes()); write("public/fakeAPI/stats/upstreams", upstreams(3)); write("public/fakeAPI/stats/top_blocked", topBlockedDomains(10)); write("public/fakeAPI/stats/top_domains", topDomains(10)); write("public/fakeAPI/stats/top_clients", topClients(10)); write("public/fakeAPI/stats/top_blocked_clients", topBlockedClients(10)); write("public/fakeAPI/stats/database/overTime/history", historyOverTime(144)); write( "public/fakeAPI/stats/database/overTime/clients", clientsOverTime(144, 5) ); write("public/fakeAPI/stats/database/summary", summary()); write("public/fakeAPI/stats/database/query_types", queryTypes()); write("public/fakeAPI/stats/database/upstreams", upstreams(3)); write("public/fakeAPI/stats/database/top_blocked", topBlockedDomains(10)); write("public/fakeAPI/stats/database/top_domains", topDomains(10)); write("public/fakeAPI/stats/database/top_clients", topClients(10)); write( "public/fakeAPI/stats/database/top_blocked_clients", topBlockedClients(10) ); write("public/fakeAPI/auth", auth()); write("public/fakeAPI/version", getVersionInfo()); console.log("Done!");