Skip to content

Commit

Permalink
Adding support for wildcard routes. It's now possible to set up a rou…
Browse files Browse the repository at this point in the history
…te such as *.example.com and have it match all example.com sub domains.
  • Loading branch information
martinknafve committed May 1, 2014
1 parent fa6cb42 commit 1066e31
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 222 deletions.
13 changes: 6 additions & 7 deletions hmailserver/source/Server/Common/BO/Routes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,24 @@ namespace HM
_DBLoad(sSQL);
}

vector<shared_ptr<Route> >
Routes::GetItemsByName(const String &sRouteName)
shared_ptr<Route>
Routes::GetItemByNameWithWildcardMatch(const String &domainName)
{
vector<shared_ptr<Route> >::iterator iter = vecObjects.begin();
vector<shared_ptr<Route> >::iterator iterEnd = vecObjects.end();

vector<shared_ptr<Route> > vecResult;

for (; iter != iterEnd; iter++)
{
shared_ptr<Route> pRoute = (*iter);

if (pRoute->DomainName().CompareNoCase(sRouteName) == 0)
if (StringParser::WildcardMatchNoCase(pRoute->DomainName(), domainName))
{
vecResult.push_back(pRoute);
return pRoute;
}
}

return vecResult;
shared_ptr<Route> empty;
return empty;
}

}
3 changes: 1 addition & 2 deletions hmailserver/source/Server/Common/BO/Routes.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ namespace HM
// Refreshes this collection from the database.
void Refresh();

vector<shared_ptr<Route> > GetItemsByName(const String &sRouteName);

shared_ptr<Route> GetItemByNameWithWildcardMatch(const String &domainName);
protected:

virtual String GetCollectionName() const {return "Routes"; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,18 +301,14 @@ namespace HM
{
shared_ptr<Routes> pRoutes = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes();

vector<shared_ptr<Route> > vecRoutes = pRoutes->GetItemsByName(route->GetName());

boost_foreach(shared_ptr<Route> pExistingRoute, vecRoutes)
shared_ptr<Route> existingRoute = pRoutes->GetItemByName(route->GetName());
if (existingRoute && existingRoute->GetID() != route->GetID())
{
if (route->GetID() == 0 || route->GetID() != pExistingRoute->GetID())
{
resultDescription = "Another route with this name already exists.";
return false;
}
resultDescription = "Another route with this name already exists.";
return false;
}

return "";
return true;
}

}
2 changes: 1 addition & 1 deletion hmailserver/source/Server/SMTP/ExternalDelivery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ namespace HM
String sAddress = (*iterAddress).first;
String sDomainName = StringParser::ExtractDomain (sAddress).ToLower();

shared_ptr<Route> pRoute = pRoutes->GetItemByName(sDomainName);
shared_ptr<Route> pRoute = pRoutes->GetItemByNameWithWildcardMatch(sDomainName);

if (pRoute)
{
Expand Down
6 changes: 2 additions & 4 deletions hmailserver/source/Server/SMTP/MirrorMessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,15 @@ namespace HM
{
// check if a route exists with the same name and account.
bool found = false;
vector<shared_ptr<Route> > vecRoutes = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemsByName(sDomain);
boost_foreach(shared_ptr<Route> route, vecRoutes)
shared_ptr<Route> route = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemByNameWithWildcardMatch(sDomain);
if (route)
{
if (route->ToAllAddresses() || route->GetAddresses()->GetItemByName(sMirrorAddress))
{
found = true;
break;
}
}


if (!found)
{
// The account didn't exist, or it wasn't active. Report a failure.
Expand Down
46 changes: 12 additions & 34 deletions hmailserver/source/Server/SMTP/RecipientParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,26 +157,18 @@ namespace HM

// We have not found the recipient yet. Check if the original address matches a route.
String recipientDomain = StringParser::ExtractDomain(recipientAddress);
vector<shared_ptr<Route> > vecRoutes = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemsByName(recipientDomain);
shared_ptr<Route> pRoute = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemByNameWithWildcardMatch(recipientDomain);

if (vecRoutes.size() > 0)
if (pRoute)
{
vector<shared_ptr<Route> >::iterator iter = vecRoutes.begin();
vector<shared_ptr<Route> >::iterator iterEnd = vecRoutes.end();

for (; iter != iterEnd; iter++)
if (pRoute->ToAllAddresses() || pRoute->GetAddresses()->GetItemByName(recipientAddress))
{
shared_ptr<Route> pRoute = (*iter);
if (iRecursionLevel == 1)
bTreatSecurityAsLocal = pRoute->GetTreatRecipientAsLocalDomain();

if (pRoute->ToAllAddresses() || pRoute->GetAddresses()->GetItemByName(recipientAddress))
{
if (iRecursionLevel == 1)
bTreatSecurityAsLocal = pRoute->GetTreatRecipientAsLocalDomain();

return DP_Possible;
}
return DP_Possible;
}

// We found routes matching the recipients domain, but the recipient
// doesn't exist in any of them.
// Temp fail in case route list is not up-to-date
Expand Down Expand Up @@ -324,28 +316,15 @@ namespace HM
// 2) The domain is not local.
// When we get here, one of these are true.
String recipientDomain = StringParser::ExtractDomain(recipientAddress);
vector<shared_ptr<Route> > vecRoutes = HM::Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemsByName(recipientDomain);
if (vecRoutes.size() > 0)
shared_ptr<Route> pRoute = HM::Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemByNameWithWildcardMatch(recipientDomain);
if (pRoute)
{
vector<shared_ptr<Route> >::iterator iter = vecRoutes.begin();
vector<shared_ptr<Route> >::iterator iterEnd = vecRoutes.end();

bool recipientExists = false;
bool isLocalName = false;
for (; iter != iterEnd; iter++)
{
// Check whether we should route to all, or if we should allow route to this specific address
shared_ptr<Route> pRoute = (*iter);
if (pRoute->ToAllAddresses() || pRoute->GetAddresses()->GetItemByName(recipientAddress))
{
recipientExists = true;
isLocalName = pRoute->GetTreatRecipientAsLocalDomain();
break;
}
}

if (recipientExists)
if (pRoute->ToAllAddresses() || pRoute->GetAddresses()->GetItemByName(recipientAddress))
{
isLocalName = pRoute->GetTreatRecipientAsLocalDomain();

shared_ptr<MessageRecipient> NewRecipient = shared_ptr<MessageRecipient>(new MessageRecipient);
NewRecipient->SetAddress(recipientAddress);
NewRecipient->SetOriginalAddress(sOriginalAddress);
Expand All @@ -357,7 +336,6 @@ namespace HM
}

return;

}

if (bIsLocalDomain)
Expand Down
31 changes: 8 additions & 23 deletions hmailserver/source/Server/SMTP/SMTPConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1783,27 +1783,15 @@ namespace HM
String sLogData;

bool bIsRouteDomain = false;
vector<shared_ptr<Route> > routes = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemsByName(sETRNDomain.ToLower());
shared_ptr<Route> route = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemByNameWithWildcardMatch(sETRNDomain.ToLower());

// See if sender supplied param matches one of our domains
if (routes.size() > 0)
{
boost_foreach(shared_ptr<Route> route, routes)
{
if (route->GetName() == sETRNDomain2)
{
bIsRouteDomain = true;
break;
}
}
}

if (bIsRouteDomain)
if (route && route->GetName() == sETRNDomain2)
{
LOG_SMTP(GetSessionID(), GetIPAddressString(), "SMTPDeliverer - ETRN - Route found, continuing..");

shared_ptr<Routes> pRoutes = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes();
shared_ptr<Route> pRoute = pRoutes->GetItemByName(sETRNDomain.ToLower());
shared_ptr<Route> pRoute = pRoutes->GetItemByNameWithWildcardMatch(sETRNDomain.ToLower());

if (pRoute)
{
Expand Down Expand Up @@ -2032,17 +2020,14 @@ namespace HM
const String senderAddress = m_pCurrentMessage->GetFromAddress();

String senderDomainName = StringParser::ExtractDomain(senderAddress);
vector<shared_ptr<Route> > routes = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemsByName(senderDomainName);
shared_ptr<Route> route = Configuration::Instance()->GetSMTPConfiguration()->GetRoutes()->GetItemByNameWithWildcardMatch(senderDomainName);

if (routes.size() > 0)
if (route)
{
boost_foreach(shared_ptr<Route> route, routes)
if (route->ToAllAddresses() || route->GetAddresses()->GetItemByName(senderAddress))
{
if (route->ToAllAddresses() || route->GetAddresses()->GetItemByName(senderAddress))
{
if (route->GetTreatSenderAsLocalDomain())
return true;
}
if (route->GetTreatSenderAsLocalDomain())
return true;
}
}

Expand Down
2 changes: 1 addition & 1 deletion hmailserver/source/Server/SMTP/ServerTargetResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ namespace HM
shared_ptr<SMTPConfiguration> pSMTPConfig = Configuration::Instance()->GetSMTPConfiguration();

// Check if we have any route for this domain.
shared_ptr<Route> pRoute = pSMTPConfig->GetRoutes()->GetItemByName(sDomain);
shared_ptr<Route> pRoute = pSMTPConfig->GetRoutes()->GetItemByNameWithWildcardMatch(sDomain);

if (pRoute)
{
Expand Down
1 change: 1 addition & 0 deletions hmailserver/test/RegressionTests/RegressionTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
<Compile Include="POP3\Fetching\ServerBehaviors.cs" />
<Compile Include="Shared\SMTPClientSimulator.cs" />
<Compile Include="SMTP\DistributionLists.cs" />
<Compile Include="SMTP\Routes.cs" />
<Compile Include="SMTP\SMTPClientTests.cs" />
<Compile Include="Shared\SMTPServerSimulator.cs" />
<Compile Include="TestResources.Designer.cs">
Expand Down
140 changes: 0 additions & 140 deletions hmailserver/test/RegressionTests/SMTP/Basics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,49 +168,6 @@ public void MailFromShouldNotUpdatedLastLogonTime()
Assert.AreEqual(lastLogonTimeBefore, lastLogonTimeAfter);
}

[Test]
[Description("Issue 284. Sender to Alias to Route not working.")]
public void SendMessageToAliasForwardToRoute()
{
// Set up a server listening on port 250 which accepts email for [email protected]
var deliveryResults = new Dictionary<string, int>();
deliveryResults["[email protected]"] = 250;

int smtpServerPort = TestSetup.GetNextFreePort();
using (var server = new SMTPServerSimulator(1, smtpServerPort))
{
server.AddRecipientResult(deliveryResults);
server.StartListen();

// Add a route pointing at localhost
Route route = _settings.Routes.Add();
route.DomainName = "test.com";
route.TargetSMTPHost = "localhost";
route.TargetSMTPPort = smtpServerPort;
route.NumberOfTries = 1;
route.MinutesBetweenTry = 5;
route.TreatRecipientAsLocalDomain = true;
route.TreatSenderAsLocalDomain = true;
route.AllAddresses = false;
route.Save();

// Make sure only the specific user is valid.
RouteAddress routeAddress = route.Addresses.Add();
routeAddress.Address = "user@" + _domain.Name;
routeAddress.Save();

SingletonProvider<TestSetup>.Instance.AddAlias(_domain, "[email protected]", "[email protected]");

var smtpClient = new SMTPClientSimulator();
Assert.IsTrue(smtpClient.Send("[email protected]", "[email protected]", "Test", "Test message"));
TestSetup.AssertRecipientsInDeliveryQueue(0);

server.WaitForCompletion();

Assert.IsTrue(server.MessageData.Contains("Test message"));
}
}

[Test]
public void TestAntiVirusEnabled()
{
Expand Down Expand Up @@ -989,103 +946,6 @@ public void TestWelcomeMessage()
throw new Exception("ERROR - Wrong welcome message.");
}


[Test]
[Description("If both route and SMTP relay is in use, route should have higher priortiy..")]
public void RoutesShouldHaveHigherPrioThanSMTPRelay()
{
// Set up a server listening on port 250 which accepts email for [email protected]
var deliveryResults = new Dictionary<string, int>();
deliveryResults["[email protected]"] = 250;

// We set the SMTP relayer here, but this should be ignored since the recipient's
// address matches a route set up (test.com).
_application.Settings.SMTPRelayer = "example.com";

int smtpServerPort = TestSetup.GetNextFreePort();
using (var server = new SMTPServerSimulator(1, smtpServerPort))
{
server.AddRecipientResult(deliveryResults);
server.StartListen();

// Add a route pointing at localhost
Route route = _settings.Routes.Add();
route.DomainName = "test.com";
route.TargetSMTPHost = "localhost";
route.TargetSMTPPort = smtpServerPort;
route.NumberOfTries = 1;
route.MinutesBetweenTry = 5;
route.TreatRecipientAsLocalDomain = true;
route.TreatSenderAsLocalDomain = true;
route.AllAddresses = false;
route.Save();

// Make sure only the specific user is valid.
RouteAddress routeAddress = route.Addresses.Add();
routeAddress.Address = "user@" + _domain.Name;
routeAddress.Save();

SingletonProvider<TestSetup>.Instance.AddAlias(_domain, "[email protected]", "[email protected]");

var smtpClient = new SMTPClientSimulator();
Assert.IsTrue(smtpClient.Send("[email protected]", "[email protected]", "Test", "Test message"));
TestSetup.AssertRecipientsInDeliveryQueue(0);

server.WaitForCompletion();

Assert.IsTrue(server.MessageData.Contains("Test message"));
}
}

[Test]
[Description("If a message with 4 recipients on the same domain is is delivered via a route, only one message should be delivered.")]
public void RoutesShouldConsolidateRecipients()
{
// Set up a server listening on port 250 which accepts email for [email protected]
var deliveryResults = new Dictionary<string, int>();
deliveryResults["[email protected]"] = 250;
deliveryResults["[email protected]"] = 250;
deliveryResults["[email protected]"] = 250;
deliveryResults["[email protected]"] = 250;

int smtpServerPort = TestSetup.GetNextFreePort();
using (var server = new SMTPServerSimulator(1, smtpServerPort))
{
server.AddRecipientResult(deliveryResults);
server.StartListen();

// Add a route pointing at localhost
Route route = _settings.Routes.Add();
route.DomainName = "test.com";
route.TargetSMTPHost = "localhost";
route.TargetSMTPPort = smtpServerPort;
route.NumberOfTries = 1;
route.MinutesBetweenTry = 5;
route.TreatRecipientAsLocalDomain = true;
route.TreatSenderAsLocalDomain = true;
route.AllAddresses = true;
route.Save();

var smtpClient = new SMTPClientSimulator();

var recipients = new List<string>()
{
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]"
};

Assert.IsTrue(smtpClient.Send("[email protected]", recipients, "Test", "Test message"));
TestSetup.AssertRecipientsInDeliveryQueue(0);

server.WaitForCompletion();

Assert.IsTrue(server.MessageData.Contains("Test message"));
Assert.AreEqual(deliveryResults.Count, server.RcptTosReceived);
}
}

[Test]
[Description("If a message with 4 recipients on different domains, but all are to be sent through the same SMTP relay, only one message should be sent")]
public void SmtpRelayShouldConsolidateRecipients()
Expand Down
Loading

0 comments on commit 1066e31

Please sign in to comment.