This repository has been archived by the owner on Mar 31, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Program.cs
314 lines (294 loc) · 13.8 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace LocalDns
{
class Program
{
const uint ENABLE_QUICK_EDIT = 0x0040;
const int STD_INPUT_HANDLE = -10;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
static bool MONO = false;
static string RulesUrl = @"https://raw.githubusercontent.com/exelix11/LocalDns/master/Rules.txt";
static void Main(string[] args)
{
MONO = Type.GetType("Mono.Runtime") != null;
if (!MONO) //Is running on windows ?
{
//this disables select in the cmd window
IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
uint consoleMode;
GetConsoleMode(consoleHandle, out consoleMode);
consoleMode &= ~ENABLE_QUICK_EDIT;
SetConsoleMode(consoleHandle, consoleMode);
}
Console.Title = "LocalDNS";
Console.WriteLine("LocalDNS 1.2 By Exelix11");
Console.WriteLine("https://github.com/exelix11/LocalDns");
Console.WriteLine("");
Dictionary<string, DnsSettings> dicRules = null;
List<KeyValuePair<string, DnsSettings>> regRules = null;
DnsCore Dns = new LocalDns.DnsCore();
#region parseArgs
bool DownloadRules = false; //Uhhh bad code :(
if (args.Length != 0)
{
try
{
for (int i = 0; i < args.Length; i++)
{
if ((args[i].StartsWith("-") || args[i].StartsWith(@"\") || args[i].StartsWith("/")) && args[i].Length > 1) args[i] = args[i].Remove(0, 1);
switch (args[i].ToLower())
{
case "?":
case "h":
case "help":
PrintHelp();
return;
case "downloadrules":
DownloadRules = true;
break;
case "rules":
if (DownloadRules) break;
if (File.Exists(args[i + 1]))
{
ParseRules(args[i + 1], out dicRules, out regRules);
}
else
{
Console.WriteLine("_____________________________________________\r\n" + args[i + 1] + " not found !");
PrintHelp();
return;
}
break;
case "blocknotinrules":
Dns.DenyNotInRules = args[i + 1].ToLower() == "true";
break;
case "localhost":
{
string ipStr = args[i + 1].Trim();
IPAddress ipAddr;
if (IPAddress.TryParse(ipStr, out ipAddr))
{
Dns.LocalHostIp = ipAddr;
}
else Console.WriteLine("Warning: Couldn't parse '{ipStr}' as an IP address");
}
break;
}
}
}
catch
{
Console.WriteLine("Can't parse args !");
PrintHelp();
return;
}
}
#endregion
if (DownloadRules)
{
Console.WriteLine("Downloading rules.....");
if (MONO) ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
Dns.DenyNotInRules = false;
HttpWebRequest http = (HttpWebRequest)WebRequest.Create(RulesUrl);
WebResponse response = http.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
string content = sr.ReadToEnd();
ParseRules(content, out dicRules, out regRules, false);
}
else if (dicRules == null)
{
if (File.Exists("Rules.txt"))
{
ParseRules("Rules.txt", out dicRules, out regRules);
}
else
{
Console.WriteLine("_____________________________________________\r\nRules.txt not found !");
PrintHelp();
return;
}
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("BlockNotInRules: " + Dns.DenyNotInRules.ToString());
Console.WriteLine("localhost: " + Dns.LocalHostIp.ToString());
}
Dns.dicRules = dicRules;
Dns.regRules = regRules;
Dns.FireEvents = true;
Dns.ResolvedIp += ResolvedIp;
Dns.ConnectionRequest += ConnectionRequest;
Dns.ServerReady += ServerReady;
Dns.socketException += BindError;
Dns.RunDns();
}
private static void BindError(SocketException ex)
{
Console.WriteLine("_____________________________________________");
Console.WriteLine("Couldn't bind to port 53.");
Console.WriteLine("It may be already in use, on windows check with \"netstat -ano\"\r\nIf you're on linux make sure to run with sudo");
Console.WriteLine("Error message: " + ex.Message);
Console.ReadLine();
}
private static void ServerReady(Dictionary<string, string> e)
{
Console.WriteLine("Starting DNS... (Press CTRL + C to close)");
Console.Title = "LocalDNS, press CTRL + C to exit";
Console.WriteLine("_____________________________________________");
switch (e.Keys.Count)
{
case 0:
Console.WriteLine("Socket ready");
Console.WriteLine("WARNING: no local ip address found");
break;
case 1:
Console.WriteLine("Socket ready, running on " + e.Keys.ToArray()[0]);
break;
default:
Console.WriteLine("Socket ready, running on: (an ip for every network interface)");
foreach (string k in e.Keys.ToArray()) Console.WriteLine(k + " on " + e[k]);
break;
}
Console.WriteLine("_____________________________________________");
}
static void ParseRules(string Filename, out Dictionary<string, DnsSettings> DicRules, out List<KeyValuePair<string, DnsSettings>> StarRules, bool IsFilename = true)
{
DicRules = new Dictionary<string,DnsSettings>();
StarRules = new List<KeyValuePair<string, DnsSettings>>();
string[] rules = IsFilename ? File.ReadAllLines(Filename) : Filename.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
foreach (string s in rules)
{
if (s.StartsWith(";") || s.Trim() == "") continue;
string[] split = s.Split(',');
DnsSettings dns = new DnsSettings();
switch (split[1].Trim().ToLower())
{
case "deny":
dns.Mode = HandleMode.Deny;
break;
case "allow":
dns.Mode = HandleMode.Allow;
break;
case "redirect":
dns.Mode = HandleMode.Redirect;
dns.Address = split[2].Trim();
break;
default:
throw new Exception("Can't parse rules !");
}
string domain = split[0].Trim();
if (domain.Contains("*"))
{
// Escape all possible URI characters conflicting with Regex
domain = domain.Replace(".", "\\.");
domain = domain.Replace("$", "\\$");
domain = domain.Replace("[", "\\[");
domain = domain.Replace("]", "\\]");
domain = domain.Replace("(", "\\(");
domain = domain.Replace(")", "\\)");
domain = domain.Replace("+", "\\+");
domain = domain.Replace("?", "\\?");
// Replace "*" characters with ".*" which means any number of any character for Regexp
domain = domain.Replace("*", ".*");
StarRules.Add(new KeyValuePair<string, DnsSettings>(domain, dns));
}
else
{
DicRules.Add(domain, dns);
DicRules.Add("www." + domain, dns);
}
}
Console.WriteLine(DicRules.Count.ToString() + " dictionary rules and " + StarRules.Count.ToString() + " star rules loaded");
if (System.Diagnostics.Debugger.IsAttached)
{
List<string[]> ToPad = new List<string[]>();
foreach (string s in DicRules.Keys.ToArray()) ToPad.Add(new string[] { s, DicRules[s].Mode.ToString(), DicRules[s].Address == null ? "" : DicRules[s].Address });
foreach (KeyValuePair<string, DnsSettings> rule in StarRules) ToPad.Add(new string[] { rule.Key, rule.Value.Mode.ToString(), rule.Value.Address == null ? "" : rule.Value.Address });
Console.WriteLine(ConsoleUtility.PadElementsInLines(ToPad, 5));
}
}
static void PrintHelp()
{
Console.WriteLine("_____________________________________________");
Console.WriteLine("Usage:");
Console.WriteLine("LocalDns.exe [-Rules Rules.txt] [-BlockNotInRules false] [-LocalHost NXDOMAIN]");
Console.WriteLine("-Localhost: the ip to redirect every blocked url (like 127.0.0.1),by default is set to NXDOMAIN, by doing so the domain not found error will be sent instead of an ip");
Console.WriteLine("-DownloadRules: Uses the latest rules file from the github repo, doesn't overwrite Rules.txt, if set, -Rules and -BlockNotInRule will be ignored");
Console.WriteLine("-Rules : Specifies a rules file, if not set Rules.txt is loaded by default. Don't use spaces in the path !");
Console.WriteLine("-BlockNotInRules: only true or false, if set to true will redirect to Localhost urls not in the rules file, else will return the real address, by default is set to false");
Console.ReadKey();
}
private static void ResolvedIp(DnsEventArgs e)
{
Console.WriteLine("Resolved: " + e.Url + " to: " + ((e.Host == IPAddress.None) ? "NXDOMAIN" : e.Host.ToString()));
}
private static void ConnectionRequest(DnsConnectionRequestEventArgs e)
{
Console.WriteLine("Got request from: " + e.Host);
}
static public bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true; //This isn't a good thing to do, but to keep the code simple i prefer doing this, it will be used only on mono
}
}
}
// Source: http://dev.flauschig.ch/wordpress/?p=387
public static class ConsoleUtility
{
/// <summary>
/// Converts a List of string arrays to a string where each element in each line is correctly padded.
/// Make sure that each array contains the same amount of elements!
/// - Example without:
/// Title Name Street
/// Mr. Roman Sesamstreet
/// Mrs. Claudia Abbey Road
/// - Example with:
/// Title Name Street
/// Mr. Roman Sesamstreet
/// Mrs. Claudia Abbey Road
/// <param name="lines">List lines, where each line is an array of elements for that line.</param>
/// <param name="padding">Additional padding between each element (default = 1)</param>
/// </summary>
public static string PadElementsInLines(List<string[]> lines, int padding = 1)
{
// Calculate maximum numbers for each element accross all lines
var numElements = lines[0].Length;
var maxValues = new int[numElements];
for (int i = 0; i < numElements; i++)
{
maxValues[i] = lines.Max(x => x[i].Length) + padding;
}
var sb = new StringBuilder();
// Build the output
bool isFirst = true;
foreach (var line in lines)
{
if (!isFirst)
{
sb.AppendLine();
}
isFirst = false;
for (int i = 0; i < line.Length; i++)
{
var value = line[i];
// Append the value with padding of the maximum length of any value for this element
sb.Append(value.PadRight(maxValues[i]));
}
}
return sb.ToString();
}
}