From 1f9d93ab771c12aa7fc25edb27eaa9bce01656e2 Mon Sep 17 00:00:00 2001 From: Kingloo Date: Sat, 18 Aug 2018 15:52:46 +0200 Subject: [PATCH] Initial commit --- DataTypes.fs | 88 ++++++++++++++++++++++++++++++++++++++++++ Program.fs | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ test.fsproj | 13 +++++++ 3 files changed, 207 insertions(+) create mode 100644 DataTypes.fs create mode 100644 Program.fs create mode 100644 test.fsproj diff --git a/DataTypes.fs b/DataTypes.fs new file mode 100644 index 0000000..189632a --- /dev/null +++ b/DataTypes.fs @@ -0,0 +1,88 @@ +namespace Hosts + +module DataTypes = + + open System + open System.IO + + type ExitCodes = + | Success = 0 + | ErrorServerTypeSwitchMissing = -1 + | ErrorServerTypeUnknown = -2 + + let blackHole = "0.0.0.0" + //let directory = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + let directory = Environment.CurrentDirectory + let addedHostsFilePath = Path.Combine (directory, "addedHosts.txt") + let excludedHostsFilePath = Path.Combine (directory, "excludedHosts.txt") + let customExtrasFilePath = Path.Combine (directory, "customExtras.txt") + + type DnsServerType = + | Bind + | Unbound + | Windows + | Missing + | Unknown + + type DnsServer = { + Name: DnsServerType + Format: string -> string + } + + let dnsServerTypes : List = [ + { + Name = Bind + Format = (fun x -> "zone \"" + x + "\" { type master; file \"/etc/bind/zones/db.poison\"; };") + } + { + Name = Unbound + Format = (fun x -> "local-zone: \"" + x + "\" inform_deny.") + } + { + Name = Windows + Format = (fun x -> blackHole + " " + x) + } + { + Name = Unknown + Format = (fun x -> "# domain name server type unknown") + } + ] + + type DomainSourceType = + | AbuseCH + | MVPS + | SANS + + type DomainSource = { + Name: DomainSourceType + Url: Uri + Format: string -> string + } + + let domainSources : List = [ + { + Name = AbuseCH; + Url = new Uri("https://ransomwaretracker.abuse.ch/downloads/RW_DOMBL.txt"); + Format = (fun raw -> if raw.StartsWith("#") then "" else raw) + } + { + Name = SANS; + Url = new Uri("https://isc.sans.edu/feeds/suspiciousdomains_Low.txt"); + Format = (fun raw -> if raw.StartsWith("#") || raw.StartsWith("site") then "" else raw) + } + { + Name = SANS; + Url = new Uri("https://isc.sans.edu/feeds/suspiciousdomains_Medium.txt"); + Format = (fun raw -> if raw.StartsWith("#") || raw.StartsWith("site") then "" else raw) + } + { + Name = SANS; + Url = new Uri("https://isc.sans.edu/feeds/suspiciousdomains_High.txt"); + Format = (fun raw -> if raw.StartsWith("#") || raw.StartsWith("site") then "" else raw) + } + { + Name = MVPS; + Url = new Uri("http://winhelp2002.mvps.org/hosts.txt"); + Format = (fun (raw: string) -> if raw.StartsWith("#") || String.IsNullOrWhiteSpace raw then "" else raw.Split(" ", StringSplitOptions.RemoveEmptyEntries).[1]) + } + ] \ No newline at end of file diff --git a/Program.fs b/Program.fs new file mode 100644 index 0000000..8eae39e --- /dev/null +++ b/Program.fs @@ -0,0 +1,106 @@ +namespace Hosts + +module Program = + + open System + open System.IO + open System.Net.Http + open DataTypes + + let writeMessage (message: string) = + use writer = Console.Error + writer.WriteLine(message) + + let determineServerType args : DnsServerType = + let typeIdx = Array.tryFindIndex (fun elem -> elem = "-type") args + match typeIdx with + | Some idx -> + try + match args.[idx + 1] with // the domain type will be in the array position after "-type" + | "bind" -> Bind + | "unbound" -> Unbound + | "windows" -> Windows + | _ -> Unknown + with + | :? IndexOutOfRangeException as ex -> Unknown + | None -> Missing + + let getLinesFromWebString (webString: string) (domainSource: DomainSource): Async> = + async { + let lines = new System.Collections.Generic.List() + use reader = new StringReader(webString) + let mutable hasMoreLines = true + while hasMoreLines do + let! line = reader.ReadLineAsync() |> Async.AwaitTask + if line <> null then + let formattedLine = domainSource.Format line + match Uri.TryCreate("http://" + formattedLine, UriKind.Absolute) with + | true, _ -> lines.Add formattedLine + | false, _ -> () + else + hasMoreLines <- false + writeMessage ("loaded " + lines.Count.ToString() + " lines from " + domainSource.Name.ToString() + " (" + domainSource.Url.AbsoluteUri + ")") + return lines :> seq + } + + let downloadDomainSource (domainSource: DomainSource) : Async> = + async { + try + use client = new HttpClient() + let! result = client.GetStringAsync(domainSource.Url) |> Async.AwaitTask + return! getLinesFromWebString result domainSource + with + | ex -> + writeMessage ("downloading (" + domainSource.Url.AbsoluteUri + ") failed: " + ex.GetType().Name + " (" + ex.Message + ")") + return Seq.empty + } + + let getRemoteDomains domainSources : seq = + domainSources + |> List.map downloadDomainSource + |> Async.Parallel + |> Async.RunSynchronously + |> Array.reduce (fun acc item -> Seq.append acc item) // turns an array of seqs into one seq of everything + + let printLines (lines: seq) = + use writer = Console.Out + lines + |> Seq.iter (fun line -> writer.WriteLine(line)) + + let printDomains (serverType: DnsServerType) (domains: seq) = + domains + |> Seq.map (fun item -> + let dns = List.find (fun (y: DnsServer) -> y.Name = serverType) dnsServerTypes + dns.Format item) + |> printLines + + let loadLinesFromFile filePath = + try + let lines = File.ReadAllLines filePath + writeMessage ("loaded " + lines.Length.ToString() + " lines from " + filePath) + lines + with _ -> + writeMessage (filePath + " was not found") + Array.empty + + [] + let main args = + match determineServerType args with + | Unknown -> + writeMessage "! server type unknown !" + int ExitCodes.ErrorServerTypeUnknown + | Missing -> + writeMessage "! no domain type switch !" + int ExitCodes.ErrorServerTypeSwitchMissing + | serverType -> + let customExtras = loadLinesFromFile customExtrasFilePath + let addedHosts = loadLinesFromFile addedHostsFilePath + let excludedHosts = loadLinesFromFile excludedHostsFilePath + printLines customExtras + domainSources + |> getRemoteDomains + |> Seq.append addedHosts + |> Seq.except excludedHosts + |> Seq.distinct + |> printDomains serverType + int ExitCodes.Success \ No newline at end of file diff --git a/test.fsproj b/test.fsproj new file mode 100644 index 0000000..15ed608 --- /dev/null +++ b/test.fsproj @@ -0,0 +1,13 @@ + + + + Exe + netcoreapp2.1 + + + + + + + +