FS-3588 --resolve thanks drk
This commit is contained in:
parent
5e0b3fa077
commit
aa7800690d
|
@ -604,6 +604,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_opus", "src\mod\codecs\
|
|||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "make_t43_gray_code_tables", "libs\spandsp\src\msvc\make_t43_gray_code_tables.2012.vcxproj", "{EDDB8AB9-C53E-44C0-A620-0E86C2CBD5D5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winFailToBan", "src\mod\languages\mod_managed\managed\examples\winFailToBan\winFailToBan.csproj", "{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
All|Win32 = All|Win32
|
||||
|
@ -4056,6 +4058,18 @@ Global
|
|||
{EDDB8AB9-C53E-44C0-A620-0E86C2CBD5D5}.Release|x64.Build.0 = All|Win32
|
||||
{EDDB8AB9-C53E-44C0-A620-0E86C2CBD5D5}.Release|x64 Setup.ActiveCfg = All|Win32
|
||||
{EDDB8AB9-C53E-44C0-A620-0E86C2CBD5D5}.Release|x86 Setup.ActiveCfg = All|Win32
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.All|Win32.ActiveCfg = Release|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.All|x64.ActiveCfg = Release|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.All|x64 Setup.ActiveCfg = Release|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.All|x86 Setup.ActiveCfg = Release|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Debug|x64 Setup.ActiveCfg = Debug|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Debug|x86 Setup.ActiveCfg = Debug|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Release|x64 Setup.ActiveCfg = Release|Any CPU
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}.Release|x86 Setup.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -4188,6 +4202,7 @@ Global
|
|||
{7B077E7F-1BE7-4291-AB86-55E527B25CAC} = {0C808854-54D1-4230-BFF5-77B5FD905000}
|
||||
{7B42BDA1-72C0-4378-A9B6-5C530F8CD61E} = {0C808854-54D1-4230-BFF5-77B5FD905000}
|
||||
{834E2B2F-5483-4B80-8FE3-FE48FF76E5C0} = {0C808854-54D1-4230-BFF5-77B5FD905000}
|
||||
{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0} = {0C808854-54D1-4230-BFF5-77B5FD905000}
|
||||
{692F6330-4D87-4C82-81DF-40DB5892636E} = {4CF6A6AC-07DE-4B9E-ABE1-7F98B64E0BB0}
|
||||
{2286DA73-9FC5-45BC-A508-85994C3317AB} = {4CF6A6AC-07DE-4B9E-ABE1-7F98B64E0BB0}
|
||||
{66444AEE-554C-11DD-A9F0-8C5D56D89593} = {4CF6A6AC-07DE-4B9E-ABE1-7F98B64E0BB0}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FreeSWITCH;
|
||||
using FreeSWITCH.Native;
|
||||
|
||||
namespace winFailToBan
|
||||
{
|
||||
public static class BanTracker
|
||||
{
|
||||
public static int MaxFails = 3;
|
||||
public static int FailMinutes = 1;
|
||||
public static int BanMinutes = 1;
|
||||
public static String BanApi = @"system netsh adv fire add rule name={0} dir=in action=block remoteip={1}";
|
||||
public static String UnBanApi = @"system netsh adv fire delete rule name={0}";
|
||||
|
||||
// Tracker object
|
||||
public static Dictionary<String, List<DateTime>> MainTracker =
|
||||
new Dictionary<string, List<DateTime>>();
|
||||
|
||||
// Active Ban list Key=IP val=baninfo
|
||||
public static Dictionary<String, BanInfo> ActiveBans =
|
||||
new Dictionary<string, BanInfo>();
|
||||
|
||||
|
||||
public static void Startup()
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
private static void LoadSettings()
|
||||
{
|
||||
using (var a = new Api(null))
|
||||
{
|
||||
var setting = a.ExecuteString("global_getvar ban_maxfails");
|
||||
if (!String.IsNullOrEmpty(setting))
|
||||
MaxFails = int.Parse(setting);
|
||||
|
||||
setting = a.ExecuteString("global_getvar ban_failminutes");
|
||||
if (!String.IsNullOrEmpty(setting))
|
||||
FailMinutes = int.Parse(setting);
|
||||
|
||||
setting = a.ExecuteString("global_getvar ban_banminutes");
|
||||
if (!String.IsNullOrEmpty(setting))
|
||||
BanMinutes = int.Parse(setting);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanOld(List<DateTime> l)
|
||||
{
|
||||
var expiretime = DateTime.Now.Subtract(new TimeSpan(0, FailMinutes, 0));
|
||||
var expired = l.Where(x => x < expiretime).ToList();
|
||||
expired.ForEach(x => l.Remove(x));
|
||||
}
|
||||
|
||||
public static void CleanUp()
|
||||
{
|
||||
var templist = new List<String>();
|
||||
foreach (var kvp in MainTracker)
|
||||
{
|
||||
CleanOld(kvp.Value);
|
||||
if (kvp.Value.Count == 0)
|
||||
templist.Add(kvp.Key);
|
||||
}
|
||||
|
||||
templist.ForEach(i =>
|
||||
{
|
||||
MainTracker.Remove(i);
|
||||
Log.WriteLine(LogLevel.Critical, "FTB: Removed tracker entry for {0}", i);
|
||||
}); // remove all the dictinoary entries that are old
|
||||
|
||||
templist.Clear();
|
||||
|
||||
// now unban the expired bans
|
||||
templist.AddRange(from kvp in ActiveBans where kvp.Value.Expires < DateTime.Now select kvp.Key);
|
||||
templist.ForEach(Unban);
|
||||
}
|
||||
|
||||
public static void TrackFailure(String ipAddress)
|
||||
{
|
||||
LoadSettings(); // just in case they've changed...
|
||||
if (ActiveBans.ContainsKey(ipAddress))
|
||||
return; // don't process again, some delay may happen between the ban, and it taking effect by external system
|
||||
|
||||
if (!MainTracker.ContainsKey(ipAddress))
|
||||
MainTracker.Add(ipAddress, new List<DateTime>());
|
||||
var l = MainTracker[ipAddress];
|
||||
CleanOld(l); // clean out the old ones
|
||||
l.Add(DateTime.Now); // add the failure to the list
|
||||
Log.WriteLine(LogLevel.Critical, "Fail to ban logging attempt from {0} count is {1}", ipAddress, l.Count);
|
||||
if (l.Count > MaxFails)
|
||||
{
|
||||
// do the ban here
|
||||
l.Clear();
|
||||
MainTracker.Remove(ipAddress);
|
||||
Ban(ipAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ban(String ipAddress)
|
||||
{
|
||||
Log.WriteLine(LogLevel.Critical, "FTP Banning IP Address {0}", ipAddress);
|
||||
if (ActiveBans.ContainsKey(ipAddress))
|
||||
return; // it's already banned so f-it
|
||||
|
||||
var bi = new BanInfo();
|
||||
ActiveBans.Add(ipAddress, bi);
|
||||
// Execute the ban API callback here
|
||||
var acmd = String.Format(BanApi, bi.FirewallRuleName, ipAddress);
|
||||
Log.WriteLine(LogLevel.Critical, "FTB: api command: {0}", acmd);
|
||||
using (var a = new Api(null))
|
||||
a.ExecuteString(acmd);
|
||||
}
|
||||
|
||||
public static void Unban(String ipAddress)
|
||||
{
|
||||
Log.WriteLine(LogLevel.Critical, "FTB: Unbanning ip address {0}", ipAddress);
|
||||
if (!ActiveBans.ContainsKey(ipAddress))
|
||||
return; // nothing to do, it's not banned
|
||||
|
||||
var bi = ActiveBans[ipAddress]; // get the ban info
|
||||
// Execute the unban API
|
||||
var acmd = String.Format(UnBanApi, bi.FirewallRuleName);
|
||||
Log.WriteLine(LogLevel.Critical, "FTB: api command: {0}", acmd);
|
||||
using (var a = new Api(null))
|
||||
a.ExecuteString(acmd);
|
||||
|
||||
ActiveBans.Remove(ipAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public class BanInfo
|
||||
{
|
||||
public String FirewallRuleName { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
public BanInfo()
|
||||
{
|
||||
FirewallRuleName = "ftb-" + Guid.NewGuid().ToString("N");
|
||||
Expires = DateTime.Now.AddMinutes(BanTracker.BanMinutes);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using FreeSWITCH.Native;
|
||||
using winFailToBan.Internal;
|
||||
using FreeSWITCH;
|
||||
|
||||
namespace winFailToBan
|
||||
{
|
||||
public static class EventLoop
|
||||
{
|
||||
public static Boolean Running = true;
|
||||
private static Thread _eventThread;
|
||||
|
||||
public static void StartEvents()
|
||||
{
|
||||
if (_eventThread != null)
|
||||
return;
|
||||
_eventThread = new Thread(EventMainLoop);
|
||||
Running = true;
|
||||
_eventThread.Start();
|
||||
}
|
||||
|
||||
public static void StopEvents()
|
||||
{
|
||||
Running = false;
|
||||
}
|
||||
|
||||
public static void EventMainLoop()
|
||||
{
|
||||
EventConsumer ec = null;
|
||||
try
|
||||
{
|
||||
|
||||
ec = new EventConsumer("CUSTOM", "sofia::register_attempt", 100);
|
||||
ec.bind("SHUTDOWN", String.Empty);
|
||||
ec.bind("HEARTBEAT", String.Empty);
|
||||
while (Running)
|
||||
{
|
||||
var evt = ec.pop(0, 0);
|
||||
if (evt == null)
|
||||
continue;
|
||||
|
||||
var en = evt.InternalEvent.GetValueOfHeader("Event-Name");
|
||||
if (en == "CUSTOM")
|
||||
en = evt.InternalEvent.GetValueOfHeader("Event-SubClass");
|
||||
switch (en)
|
||||
{
|
||||
case @"sofia::register_attempt":
|
||||
{
|
||||
var iev = evt.InternalEvent;
|
||||
var ar = iev.GetValueOfHeader("auth-result"); // get the value of the result to see if it's the case we want
|
||||
var ip = iev.GetValueOfHeader("network-ip"); // and the ip address the register came from
|
||||
|
||||
if (ar == "FORBIDDEN")
|
||||
{
|
||||
BanTracker.TrackFailure(ip);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "SHUTDOWN":
|
||||
Log.WriteLine(LogLevel.Critical,"FTB: Processing Shutdown event");
|
||||
Running = false;
|
||||
break;
|
||||
|
||||
case "HEARTBEAT":
|
||||
BanTracker.CleanUp();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exx)
|
||||
{
|
||||
Log.WriteLine(LogLevel.Critical, "FailToBan -- Exception in event loop {0}", exx.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(ec != null)
|
||||
ec.Dispose();
|
||||
_eventThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using FreeSWITCH;
|
||||
|
||||
namespace winFailToBan
|
||||
{
|
||||
public class Fail2Ban : IApiPlugin , ILoadNotificationPlugin
|
||||
{
|
||||
public void Execute(ApiContext context)
|
||||
{
|
||||
var cmds = context.Arguments.Split(" ".ToCharArray());
|
||||
var cmd = cmds[0].ToLower();
|
||||
switch (cmd)
|
||||
{
|
||||
case "shutdown":
|
||||
Shutdown();
|
||||
break;
|
||||
|
||||
default:
|
||||
context.Stream.Write("\n\nInvalid Command\n\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteBackground(ApiBackgroundContext context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public static void Startup()
|
||||
{
|
||||
BanTracker.Startup();
|
||||
EventLoop.StartEvents();
|
||||
}
|
||||
|
||||
public static void Shutdown()
|
||||
{
|
||||
EventLoop.StopEvents();
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
Startup();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using FreeSWITCH;
|
||||
using FreeSWITCH.Native;
|
||||
|
||||
namespace winFailToBan.Internal
|
||||
{
|
||||
|
||||
public class ConfigurationEventArgs : EventArgs
|
||||
{
|
||||
public SwitchXmlSearchBinding.XmlBindingArgs FsArgs { get; private set; }
|
||||
public fsConfigDocument Result { get; set; }
|
||||
public Boolean DontProcess { get; set; }
|
||||
|
||||
public ConfigurationEventArgs(SwitchXmlSearchBinding.XmlBindingArgs args)
|
||||
{
|
||||
DontProcess = false;
|
||||
FsArgs = args;
|
||||
Result = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Bind XML search function turned into CLR events for ease use
|
||||
public class ConfigHandler : IDisposable
|
||||
{
|
||||
public event EventHandler<ConfigurationEventArgs> DirectoryRequest;
|
||||
public event EventHandler<ConfigurationEventArgs> DialPlanRequest;
|
||||
|
||||
private IDisposable _binder; // object to bind to
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(_binder != null)
|
||||
_binder.Dispose();
|
||||
_binder = null;
|
||||
}
|
||||
|
||||
public String XmlCallback(SwitchXmlSearchBinding.XmlBindingArgs args)
|
||||
{
|
||||
String rv = null; // return value
|
||||
switch (args.Section.ToLower())
|
||||
{
|
||||
case "directory":
|
||||
var dargs = new ConfigurationEventArgs(args);
|
||||
if (DirectoryRequest != null)
|
||||
{
|
||||
var temp = DirectoryRequest;
|
||||
temp(this, dargs);
|
||||
if (dargs.DontProcess)
|
||||
return null;
|
||||
if (dargs.Result != null)
|
||||
rv = dargs.Result.ToXMLString();
|
||||
}
|
||||
break;
|
||||
|
||||
case "dialplan":
|
||||
var dialargs = new ConfigurationEventArgs(args);
|
||||
if(DialPlanRequest != null)
|
||||
{
|
||||
var temp = DialPlanRequest;
|
||||
temp(this, dialargs);
|
||||
if (dialargs.Result != null)
|
||||
rv = dialargs.Result.ToXMLString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rv ?? new fsNotFoundDocument().ToXMLString();
|
||||
}
|
||||
|
||||
~ConfigHandler()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public ConfigHandler()
|
||||
{
|
||||
_binder = SwitchXmlSearchBinding.Bind(XmlCallback,
|
||||
switch_xml_section_enum_t.SWITCH_XML_SECTION_DIRECTORY |
|
||||
switch_xml_section_enum_t.SWITCH_XML_SECTION_DIALPLAN);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,327 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using FreeSWITCH.Native;
|
||||
using Stream = System.IO.Stream;
|
||||
|
||||
namespace winFailToBan.Internal
|
||||
{
|
||||
public abstract class fsConfigDocument
|
||||
{
|
||||
public XElement xmldoc;
|
||||
public XElement main;
|
||||
|
||||
protected fsConfigDocument(String SectionName)
|
||||
{
|
||||
main = new XElement("wtf",
|
||||
new XElement("document",
|
||||
new XAttribute("type", "freeswitch/xml"),
|
||||
new XElement("section",
|
||||
new XAttribute("name", SectionName),
|
||||
new XAttribute("description", "Auto generated")
|
||||
)));
|
||||
xmldoc = main.Descendants("document").Single();
|
||||
}
|
||||
|
||||
public Int64 Length()
|
||||
{
|
||||
return xmldoc.ToString().Length + 2;
|
||||
}
|
||||
|
||||
public String ToXMLString()
|
||||
{
|
||||
return xmldoc.ToString();
|
||||
}
|
||||
|
||||
public Int64 WriteXML(Stream outStream)
|
||||
{
|
||||
var wr = new StreamWriter(outStream);
|
||||
wr.WriteLine(xmldoc.ToString());
|
||||
wr.Flush();
|
||||
return xmldoc.ToString().Length + 2;
|
||||
}
|
||||
|
||||
public void WriteXML(TextWriter outText)
|
||||
{
|
||||
outText.Write(xmldoc.ToString());
|
||||
}
|
||||
|
||||
|
||||
protected void SectionChild(XElement Node)
|
||||
{
|
||||
// ReSharper disable PossibleNullReferenceException
|
||||
if (xmldoc != null) xmldoc.Element("section").Add(Node);
|
||||
// ReSharper restore PossibleNullReferenceException
|
||||
}
|
||||
}
|
||||
|
||||
public class fsNotFoundDocument : fsConfigDocument
|
||||
{
|
||||
public fsNotFoundDocument()
|
||||
: base("result")
|
||||
{
|
||||
SectionChild(new XElement("result",
|
||||
new XAttribute("status", "not found")));
|
||||
}
|
||||
}
|
||||
|
||||
public class fsDomainGatewayDirectoryDocument : fsConfigDocument
|
||||
{
|
||||
public fsDomainGatewayDirectoryDocument(Dictionary<String, Dictionary<String, Dictionary<String, String>>> domainGatewayList)
|
||||
: base("directory")
|
||||
{
|
||||
if (xmldoc == null)
|
||||
return;
|
||||
foreach (var v in
|
||||
(from d in domainGatewayList
|
||||
select new XElement("domain",
|
||||
new XAttribute("name", d.Key),
|
||||
new XElement("user",
|
||||
new XAttribute("id", "gatewaydummyser"),
|
||||
new XElement("gateways",
|
||||
from g in d.Value
|
||||
select new XElement("gateway",
|
||||
new XAttribute("name", g.Key),
|
||||
from p in g.Value
|
||||
select new XElement("param",
|
||||
new XAttribute(
|
||||
"name", p.Key),
|
||||
new XAttribute(
|
||||
"value", p.Value))))))))
|
||||
xmldoc.Element("section").Add(v);
|
||||
}
|
||||
}
|
||||
|
||||
public class fsDirectoryDocument : fsConfigDocument
|
||||
{
|
||||
public fsDirectoryDocument(String Domain, String User, String Password)
|
||||
: base("directory")
|
||||
{
|
||||
SectionChild(new XElement("domain",
|
||||
new XAttribute("name", Domain),
|
||||
new XElement("user",
|
||||
new XAttribute("id", User),
|
||||
new XElement("params",
|
||||
new XElement("param",
|
||||
new XAttribute("name", "password"),
|
||||
new XAttribute("value", Password)
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
public fsDirectoryDocument(String Domain, String User, String Password, Dictionary<String, String> Params)
|
||||
: this(Domain, User, Password)
|
||||
{
|
||||
if (Params == null)
|
||||
return;
|
||||
// ReSharper disable PossibleNullReferenceException
|
||||
xmldoc.Element("section").Element("domain").Element("user").Element("params").Add(
|
||||
// ReSharper restore PossibleNullReferenceException
|
||||
from par in Params
|
||||
select new XElement("param",
|
||||
new XAttribute("name", par.Key),
|
||||
new XAttribute("value", par.Value)));
|
||||
}
|
||||
|
||||
public fsDirectoryDocument(String Domain, String User, String Password, Dictionary<String, String> Params, Dictionary<String, String> Variables)
|
||||
: this(Domain, User, Password, Params)
|
||||
{
|
||||
if (Variables == null)
|
||||
return;
|
||||
// ReSharper disable PossibleNullReferenceException
|
||||
xmldoc.Element("section").Element("domain").Element("user").Add(
|
||||
// ReSharper restore PossibleNullReferenceException
|
||||
new XElement("variables", from v in Variables
|
||||
select new XElement("variable",
|
||||
new XAttribute("name", v.Key),
|
||||
new XAttribute("value", v.Value))));
|
||||
}
|
||||
}
|
||||
|
||||
public class fsDialPlanDocument : fsConfigDocument
|
||||
{
|
||||
|
||||
private static XElement MakeActionNode(String ActionString)
|
||||
{
|
||||
var p = ActionString.Split(",".ToCharArray(), 2);
|
||||
var rv = new XElement("action",
|
||||
new XAttribute("application", p[0]));
|
||||
if (p.Length > 1)
|
||||
rv.Add(new XAttribute("data", p[1]));
|
||||
return rv;
|
||||
}
|
||||
|
||||
public fsDialPlanDocument(String Context, IEnumerable<String> Actions)
|
||||
: base("dialplan")
|
||||
{
|
||||
SectionChild(new XElement("context",
|
||||
new XAttribute("name", Context),
|
||||
new XElement("extension",
|
||||
new XAttribute("name", "extension"),
|
||||
new XElement("condition",
|
||||
from act in Actions
|
||||
select MakeActionNode(act)))));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConfigExtensions
|
||||
{
|
||||
public static switch_event_header GetHeader(this switch_event e, String HeaderName)
|
||||
{
|
||||
for (var x = e.headers; x != null; x = x.next)
|
||||
{
|
||||
if (HeaderName.ToLower() == x.name.ToLower())
|
||||
return x;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String GetValueOfHeader(this switch_event e, String headerName, String defaultValue = "")
|
||||
{
|
||||
var head = e.GetHeader(headerName);
|
||||
return (head == null ? defaultValue : head.value);
|
||||
}
|
||||
|
||||
public static Boolean GetBooleanHeader(this switch_event e, String headerName)
|
||||
{
|
||||
var hv = e.GetValueOfHeader(headerName);
|
||||
hv = (String.IsNullOrEmpty(hv) ? "false" : hv);
|
||||
Boolean rv = false;
|
||||
Boolean.TryParse(hv, out rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static Guid GetUUID(this Event e)
|
||||
{
|
||||
var idfld = e.GetHeader("Unique-ID");
|
||||
return String.IsNullOrEmpty(idfld) ? Guid.Empty : new Guid(idfld);
|
||||
}
|
||||
|
||||
public static void ForEach(this switch_event e, Action<String, String> action)
|
||||
{
|
||||
for (var x = e.headers; x != null; x = x.next)
|
||||
{
|
||||
action(x.name, x.value);
|
||||
}
|
||||
}
|
||||
|
||||
public static Guid GetGuid(this switch_event e)
|
||||
{
|
||||
return e.GetHeader("Unique-ID") == null ? Guid.Empty : Guid.Parse(e.GetHeader("Unique-ID").value);
|
||||
}
|
||||
|
||||
public static void Dump(this switch_event e)
|
||||
{
|
||||
e.ForEach((n, v) => Console.WriteLine("{0}={1}", n, v));
|
||||
}
|
||||
|
||||
public static Dictionary<String, String> ExtractVars(this switch_event e)
|
||||
{
|
||||
var r = new Dictionary<String, String>();
|
||||
for (var x = e.headers; x != null; x = x.next)
|
||||
{
|
||||
if (x.name.StartsWith("variable_"))
|
||||
{
|
||||
r.Add(x.name.ToLower().Replace("variable_", String.Empty),
|
||||
x.value);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public static String ModDigits(this String Orig, int qdel, String Prefix)
|
||||
{
|
||||
var rv = Orig;
|
||||
rv = qdel > Orig.Length ? String.Empty : rv.Substring(qdel);
|
||||
if (!String.IsNullOrEmpty(Prefix))
|
||||
rv = Prefix + rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
//public static String RoutingReplace(this String Orig, String Orignal, String Modified)
|
||||
//{
|
||||
// var rv = Orig;
|
||||
// rv = rv.Replace("%d", Modified);
|
||||
// rv = rv.Replace("%o", Orignal);
|
||||
// return rv;
|
||||
//}
|
||||
|
||||
public static void MergeFrom<TKey, TValue>(
|
||||
this Dictionary<TKey, TValue> dict,
|
||||
Dictionary<TKey, TValue> src
|
||||
)
|
||||
{
|
||||
foreach (var entry in src)
|
||||
{
|
||||
if (!dict.ContainsKey(entry.Key))
|
||||
dict.Add(entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveKeysIn<TKey, TValue>(
|
||||
this Dictionary<TKey, TValue> dict,
|
||||
IEnumerable<TKey> keys
|
||||
)
|
||||
{
|
||||
foreach (var k in keys)
|
||||
if (dict.ContainsKey(k))
|
||||
dict.Remove(k);
|
||||
}
|
||||
|
||||
public static void AddVariableTextList(this Dictionary<String, String> d, String varslist)
|
||||
{
|
||||
if (d == null || String.IsNullOrEmpty(varslist))
|
||||
return;
|
||||
foreach (var vardef in varslist.Split(",".ToCharArray()))
|
||||
{
|
||||
var args = vardef.Split("=".ToCharArray(), 2);
|
||||
if (!d.ContainsKey(args[0]))
|
||||
{
|
||||
d.Add(args[0], args[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
d[args[0]] = args[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String ToParamString(this Dictionary<String, String> d)
|
||||
{
|
||||
var firstPart = new List<String>();
|
||||
foreach (var e in d)
|
||||
{
|
||||
firstPart.Add(String.Format("{0}={1}", e.Key, e.Value));
|
||||
}
|
||||
var rv = String.Join(",", firstPart.ToArray());
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static String ToChannelVars(this Dictionary<String, String> d, Boolean bLocal)
|
||||
{
|
||||
if (d == null || d.Count == 0)
|
||||
return String.Empty;
|
||||
String fmt;
|
||||
if (bLocal)
|
||||
fmt = "[{0}]";
|
||||
else
|
||||
fmt = "{{{0}}}";
|
||||
return String.Format(fmt, d.ToParamString());
|
||||
}
|
||||
|
||||
public static void AddActions(this List<String> l, params String[] strings)
|
||||
{
|
||||
String seperator = "^";
|
||||
foreach (var s in strings)
|
||||
{
|
||||
if (String.IsNullOrEmpty(s))
|
||||
continue;
|
||||
l.AddRange(s.Split(seperator.ToCharArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("winFailToBan")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("winFailToBan")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("a171cab9-d773-4070-9993-26a581c6f243")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FreeSWITCHManaged.LibCS" version="1.0.1.10" targetFramework="net40" />
|
||||
</packages>
|
|
@ -0,0 +1,166 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FreeSWITCH;
|
||||
using winFailToBan.Internal;
|
||||
|
||||
namespace winFailToBan
|
||||
{
|
||||
public class SampleApp : IAppPlugin
|
||||
{
|
||||
// example class for a dialplan APP just implment the run method
|
||||
public void Run(AppContext context)
|
||||
{
|
||||
var s = context.Session;
|
||||
var args = context.Arguments;
|
||||
// Do something with them here
|
||||
}
|
||||
}
|
||||
|
||||
// Example class to implment an API command
|
||||
public class SampleApi : IApiPlugin
|
||||
{
|
||||
public void Execute(ApiContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ExecuteBackground(ApiBackgroundContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
// This examle class can be used to handle XML config lookups for dialplan and directory
|
||||
|
||||
public class SampleConfigHandler : ILoadNotificationPlugin
|
||||
{
|
||||
private static ConfigHandler MyConfigHandler;
|
||||
|
||||
static void HandleDirectoryLookups(Object sender, ConfigurationEventArgs e)
|
||||
{
|
||||
e.Result = null; // not found example just return after this
|
||||
// return; // uncomment to just return not-fouond
|
||||
|
||||
// return a directory object that will work
|
||||
var evt = e.FsArgs.Parameters; // Get the raw event that generated the userDir lookup
|
||||
var eventName = evt.GetHeader("Event-Name").value; // Find the event name
|
||||
|
||||
// If your module handles voicemail authorization then implment the following
|
||||
// to update the voicemail password, when they change their voicemail password using TUI
|
||||
if (eventName == "CUSTOM")
|
||||
{
|
||||
var subClass = evt.GetValueOfHeader("Event-Subclass");
|
||||
if (subClass == "vm::maintenance")
|
||||
{
|
||||
var vmaction = evt.GetValueOfHeader("VM-Actoun");
|
||||
var username = evt.GetValueOfHeader("VM-User");
|
||||
var newPassword = evt.GetValueOfHeader("VM-User-Password");
|
||||
if (vmaction == "change-password" && !string.IsNullOrEmpty(username) &&
|
||||
!String.IsNullOrEmpty(newPassword))
|
||||
{
|
||||
// implment your code to update the users vm password in your database
|
||||
return; // No more processing we don't actually do an auth just a notification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to make sure we don't have some future events messing us up...
|
||||
if (eventName != "REQUEST_PARAMS" && eventName != "GENERAL")
|
||||
return;
|
||||
|
||||
// implment the following if you want to handle gateway lookup from directory when a profile loads
|
||||
if (evt.GetValueOfHeader("purpose") == "gateways")
|
||||
{
|
||||
var profileName = evt.GetValueOfHeader("profile");
|
||||
// implment your gateway lookup
|
||||
//e.Result = new fsDomainGatewayDirectoryDocument(myGwStructure);
|
||||
return;
|
||||
}
|
||||
|
||||
var action = evt.GetValueOfHeader("action", "none"); // get the action
|
||||
|
||||
// If you want to handle ESL Logins implment the following
|
||||
if (action == "event_socket_auth")
|
||||
{
|
||||
// preform your stuff here
|
||||
// e.result = ...
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal lookup processing
|
||||
if (evt.GetHeader("user") == null || evt.GetHeader("domain") == null)
|
||||
return; // does't have required fields
|
||||
var method = evt.GetValueOfHeader("sip_auth_method", "unknown");
|
||||
var user = evt.GetValueOfHeader("user");
|
||||
var domain = evt.GetValueOfHeader("domain");
|
||||
|
||||
// Some variables to return the params and variables section of the user record
|
||||
var variables = new Dictionary<String, String>();
|
||||
var uparams = new Dictionary<String, String>();
|
||||
|
||||
|
||||
// if you're implmenting reverse-auth of devices
|
||||
if (action == "reverse-auth-lookup")
|
||||
{
|
||||
// lookup stuff in your db
|
||||
uparams.Add("reverse-auth-user", "device uername");
|
||||
uparams.Add("reverse-auth-pass", "device password");
|
||||
}
|
||||
|
||||
// if you handle voicemail passwords ...
|
||||
if (true /*check for voicemail box */)
|
||||
{
|
||||
uparams.Add("vm-password", "theirvmpassword");
|
||||
// the following is optional
|
||||
uparams.Add("MWI-Account", "registrationstring");
|
||||
}
|
||||
// add more parameters here
|
||||
uparams.Add("anyotherparameters", "value");
|
||||
|
||||
// add variables here for example
|
||||
variables.Add("user_context", "theuserscontext");
|
||||
|
||||
e.Result = new fsDirectoryDocument(
|
||||
domain,
|
||||
user,
|
||||
"theirpassword",
|
||||
uparams,
|
||||
variables);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Example dialplan handler
|
||||
static void HandleDialPlanRequest(object sender, ConfigurationEventArgs e)
|
||||
{
|
||||
var evt = e.FsArgs.Parameters; // get the native event that caused this dialplan lookup
|
||||
|
||||
// extract the minimum variables you will need
|
||||
var context = evt.GetValueOfHeader("Hunt-Context"); // the context
|
||||
var destination = evt.GetValueOfHeader("Hunt-Destination-Number"); // the dialed number or "DID"
|
||||
var ani = evt.GetValueOfHeader("Hunt-ANI"); // The ANI/CallerID number
|
||||
|
||||
// A place to return the dialplan actions you want
|
||||
var actions = new List<String>(); // format is "app,data"
|
||||
|
||||
// add the actions for your code they shouldn't be static this is just an example
|
||||
actions.Add("set,continue_on_fail=true");
|
||||
actions.Add("brige,sofia/mygateway/" + destination);
|
||||
actions.Add("transfer,fialedDest XML failedcontext");
|
||||
e.Result = new fsDialPlanDocument(context, actions);
|
||||
return; // Isn't this easy?
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
// Start any threads doing event consumer loops
|
||||
MyConfigHandler = new ConfigHandler(); // init a config handler
|
||||
MyConfigHandler.DirectoryRequest += HandleDirectoryLookups;
|
||||
MyConfigHandler.DialPlanRequest += HandleDialPlanRequest;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{5BA0D5BD-330D-4EE2-B959-CAFEA04E50E0}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>winFailToBan</RootNamespace>
|
||||
<AssemblyName>winFailToBan</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BanTracker.cs" />
|
||||
<Compile Include="EventLoop.cs" />
|
||||
<Compile Include="Fail2Ban.cs" />
|
||||
<Compile Include="Internal\ConfigHandler.cs" />
|
||||
<Compile Include="Internal\ConfigHelper.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<None Include="skel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSWITCH.Managed.2012.csproj">
|
||||
<Project>{834e2b2f-5483-4b80-8fe3-fe48ff76e5c0}</Project>
|
||||
<Name>FreeSWITCH.Managed.2012</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
Loading…
Reference in New Issue