bool
   SpamAssassinTestConnect::TestConnect(const String &hostName, int port, String &message)
   {
     
      String bodyText = 
         "From: [email protected]\r\n"
         "\r\n"
         "XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X.\r\n";

      String tempFile = FileUtilities::GetTempFileName();
      FileUtilities::WriteToFile(tempFile, bodyText, false);

      std::shared_ptr<IOService> pIOService = Application::Instance()->GetIOService();

      bool testCompleted;

      std::shared_ptr<Event> disconnectEvent = std::shared_ptr<Event>(new Event());
      std::shared_ptr<SpamAssassinClient> pSAClient = std::shared_ptr<SpamAssassinClient>(new SpamAssassinClient(tempFile, pIOService->GetIOService(), pIOService->GetClientContext(), disconnectEvent, testCompleted));

      DNSResolver resolver;

      std::vector<String> ip_addresses;
      resolver.GetARecords(hostName, ip_addresses);

      String ip_address;
      if (ip_addresses.size())
      {
         ip_address = *(ip_addresses.begin());
      }
      else
      {
         message = "The IP address for SpamAssassin could not be resolved. Aborting tests.";
         ErrorManager::Instance()->ReportError(ErrorManager::High, 5507, "SpamAssassinTestConnect::TestConnect", message);
         return false;
      }

      // Here we handle of the ownership to the TCPIP-connection layer.
      if (pSAClient->Connect(ip_address, port, IPAddress()))
      {
         // Make sure we keep no references to the TCP connection so that it
         // can be terminated whenever. We're longer own the connection.
         pSAClient.reset();

         disconnectEvent->Wait();
      }

      if (testCompleted)
         message = FileUtilities::ReadCompleteTextFile(tempFile);
      else
      {
         message = "Unable to connect to the specified SpamAssassin server.";
      }

      FileUtilities::DeleteFile(tempFile);

      return testCompleted;

   }
Ejemplo n.º 2
0
   bool
   BLCheck::ClientExistsInDNSBL(const IPAddress &sClientIP, const String &sDNSBLHost, const String &sExpectedResult)
   {
      String sReverted = GetRevertedIP(sClientIP.ToString());
      if (sReverted.GetLength() == 0)
         return false;

      String sCheckHost = sReverted + "." + sDNSBLHost;

      std::vector<String> foundAddresses;
      DNSResolver resolver;
      resolver.GetARecords(sCheckHost, foundAddresses);

      bool isBlocked = false;

      // It's possible to specify that one or many intervals are blocked,
      // using syntax such as 127.0.0.1-5|127.0.0.10-3
      const std::set<String> blockedAddresses = ExpandAddresses(sExpectedResult);

      //
      // The server may return a list of addresses.
      //
      for(const String foundAddress : foundAddresses)
      {
         // Go through all list of blocked addresses and see if host is in it.
         for(const String &blockedAddress : blockedAddresses)
         {
            // We not only support ranges, we support wildcards as well.
            isBlocked = StringParser::WildcardMatch(blockedAddress, foundAddress);

            if (isBlocked)
            {
               // We have a match. No need to continue now.
               break;
            }
         }
         
		 if (isBlocked)
         {
            // We have a match. No need to continue.
            break;
         }
      }

      // If a message is rejected, it's good to mention in the log which DNSBL
      // caused the rejection...
      // http://www.hmailserver.com/forum/viewtopic.php?f=10&t=16221
      //
      String foundAddressesJoined = StringParser::JoinVector(foundAddresses, ", ");
      if (foundAddressesJoined.IsEmpty())
         foundAddressesJoined = "(none)";

      String logMessage = Formatter::Format("DNS lookup: {0}, {1} addresses found: {2}, Match: {3}", sCheckHost, foundAddresses.size(), foundAddressesJoined, isBlocked);
      LOG_SMTP(0,"TCP",logMessage);

      return isBlocked;
   }
Ejemplo n.º 3
0
   String 
   Utilities::GenerateReceivedHeader(const String &remote_ip, String remote_hostname, bool authenticated, bool start_tls_used)
   {
      String sComputerName = Utilities::ComputerName(); 
      std::vector<String> results;
      // do a PTR lookup, solves an issue with some spam filerting programs such as SA
      // not having a PTR in the Received header.
      String ptrRecord;
      DNSResolver resolver;
      if (!resolver.GetPTRRecords(remote_ip, results))
      {
         LOG_DEBUG("Could not get PTR record for IP (false)! " + remote_ip);
         ptrRecord = "Unknown";
      }
      else
      {

         if (results.size() == 0)
         {
            LOG_DEBUG("Could not get PTR record for IP (empty)! " + remote_ip);
            ptrRecord = "Unknown";
         }
         else ptrRecord = results[0];
      }

      if (remote_hostname.IsEmpty())
         remote_hostname = remote_ip;

      String esmtp_additions;

      if (start_tls_used) 
         esmtp_additions += "S";

      if (authenticated) 
         esmtp_additions += "A";

      String sResult;
      sResult.Format(_T("from %s (%s [%s])\r\n")
                     _T("\tby %s with ESMTP%s\r\n")
                     _T("\t; %s"), 
                     remote_hostname.c_str(),
                     ptrRecord.c_str(),
                     remote_ip.c_str(),
                     sComputerName.c_str(),
                     esmtp_additions.c_str(),
                     Time::GetCurrentMimeDate().c_str());

      return sResult;

   }
   bool 
   SpamTestMXRecords::HasAnyMXRecords_(const String &sSenderEMail)
   {
      const String sHostName = StringParser::ExtractDomain(sSenderEMail);

      // Check if full host name has any MX records.
      std::vector<String> vecFoundNames;

      DNSResolver resolver;
      if (!resolver.GetMXRecords(sHostName, vecFoundNames))
      {
         // DNS query failed. Let's pretend he has MX records.
         return true;
      }

      // DNS query succeeded. Check if MX records exists.
      if (vecFoundNames.size() > 0)
         return true;

      // No MX records were found.

      // Extract the domain name from the host name. For example, replace
      // cgi.example.com with example.com.
      bool bIsIPAddress = false;
      String sDomainName = sHostName;
      if (!TLD::Instance()->GetDomainNameFromHost(sDomainName, bIsIPAddress))
         return true;

      if (bIsIPAddress)
      {
         // Sender address contains an IP address. Skip MX checks.
         return true;
      }

      if (sDomainName == sHostName)
      {
         // Host name is same as domain name. We have already queried the host name
         // so there's no need to re-run this query.
         return false;
      }

      if (!resolver.GetMXRecords(sDomainName, vecFoundNames))
      {
         // DNS query failed. Let's pretend he has MX records.
         return true;
      }

      return (vecFoundNames.size() > 0);
   }
Ejemplo n.º 5
0
int main(int argc, char** argv) {
	char* host = 0;
	if (argc > 1) host = argv[1];
	if (!host) {
		printf("Usage: %s address\n", argv[0]);
		printf("A simple dns lookup utility parser\n");
		exit(-1);
	}
	
	SocketMonitor* monitor = SocketMonitor::getInstance();
	DNSResolver resolver;
	
	resolver.lookup(host);
	while (running) {
		monitor->wait(25);
	}
}
Ejemplo n.º 6
0
dns_srv_record_list OSS_API dns_lookup_srv(const std::string& query)
{
  dns_srv_record_list rrlist;
  
  if (OSS::string_ends_with(query, ".invalid"))
  {
    return rrlist;
  }
  
  DNSSRVRecord rr = gResolver.resolveSRV(query, 0);
  
  for (DNSSRVRecordList::iterator iter = rr.getRecords().begin(); iter != rr.getRecords().end(); iter++)
  {
    dns_srv_record record;
    record.get<0>() = query;
    record.get<1>() = iter->name;
    record.get<2>() = iter->port;
    record.get<3>() = iter->priority;
    record.get<4>() = iter->weight;

    //
    // Check if name is an IP address
    //
    
    if (!OSS::Net::IPAddress::isIPAddress(iter->name))
    {
      //
      // Resolve it further
      //
      DNSARecord arec = gResolver.resolveA4(iter->name, 0);
      if (!arec.getRecords().empty())
      {
        record.get<1>() = arec.getRecords().front();
      }
    }
    
    rrlist.insert(record);
  }
  
  return rrlist;
}
Ejemplo n.º 7
0
dns_host_record_list OSS_API dns_lookup_host(const std::string& query)
{
  dns_host_record_list rrlist;
  
  if (OSS::string_ends_with(query, ".invalid"))
  {
    return rrlist;
  }
  
  if (OSS::Net::IPAddress::isIPAddress(query))
  {
    rrlist.push_back(query);
    return rrlist;
  }
  
  DNSARecord rr = gResolver.resolveA4(query, 0);
  
  for (DNSAddressList::iterator iter = rr.getRecords().begin(); iter != rr.getRecords().end(); iter++)
  {
    rrlist.push_back(*iter);
  }
  
  return rrlist;
}
Ejemplo n.º 8
0
std::string DNSEnumService::lookup_uri_from_user(const std::string& user, SAS::TrailId trail) const
{
  if (user.empty())
  {
    TRC_INFO("No dial string supplied, so don't do ENUM lookup");
    return std::string();
  }

  // Log starting ENUM processing.
  SAS::Event event(trail, SASEvent::ENUM_START, 0);
  event.add_var_param(user);
  SAS::report_event(event);

  // Determine the Application Unique String (AUS) from the user.  This is
  // used to form the first key, and also as the input into the regular
  // expressions.
  std::string aus = user_to_aus(user);
  std::string string = aus;
  // Get the resolver to use.  This comes from thread-local data.
  DNSResolver* resolver = get_resolver();
  // Spin round until we've finished (successfully or otherwise) or we've done
  // the maximum number of queries.
  bool complete = false;
  bool failed = false;
  bool server_failed = false;
  int dns_queries = 0;
  while ((!complete) &&
         (!failed) &&
         (dns_queries < MAX_DNS_QUERIES))
  {
    // Translate the key into a domain and issue a query for it.
    std::string domain = key_to_domain(string);
    struct ares_naptr_reply* naptr_reply = NULL;
    int status = resolver->perform_naptr_query(domain, naptr_reply, trail);
    if (status == ARES_SUCCESS)
    {
      // Parse the reply into a sorted list of rules.
      std::vector<Rule> rules;
      parse_naptr_reply(naptr_reply, rules);
      // Now spin through the rules, looking for the first match.
      std::vector<DNSEnumService::Rule>::const_iterator rule;
      for (rule = rules.begin();
           rule != rules.end();
           ++rule)
      {
        if (rule->matches(string))
        {
          // We found a match, so apply the regular expression to the AUS (not
          // the previous string - this is what ENUM mandates).  If this was a
          // terminal rule, we now have a SIP URI and we're finished.
          // Otherwise, the output of the regular expression is used as the
          // next key.
          try
          {
            string = rule->replace(aus, trail);
            complete = rule->is_terminal();
          }
          catch(...) // LCOV_EXCL_START Only throws if expression too complex or similar hard-to-hit conditions
          {
            TRC_ERROR("Failed to translate number with regex");
            failed = true;
            // LCOV_EXCL_STOP
          }
          break;
        }
      }
      // If we didn't find a match (and so hit the end of the list), consider
      // this a failure.
      failed = failed || (rule == rules.end());
    }
    else if (status == ARES_ENOTFOUND)
    {
      // Our DNS query failed, so give up, but this is not an ENUM server issue -
      // we just tried to look up an unknown name.
      failed = true;
    }
    else
    {
      // Our DNS query failed. Give up, and track an ENUM server failure.
      failed = true;
      server_failed = true;
    }


    // Free off the NAPTR reply if we have one.
    if (naptr_reply != NULL)
    {
      resolver->free_naptr_reply(naptr_reply);
      naptr_reply = NULL;
    }

    dns_queries++;
  }

  // Log that we've finished processing (and whether it was successful or not).
  if (complete)
  {
    TRC_DEBUG("Enum lookup completes: %s", string.c_str());
    SAS::Event event(trail, SASEvent::ENUM_COMPLETE, 0);
    event.add_var_param(user);
    event.add_var_param(string);
    SAS::report_event(event);
  }
  else
  {
    TRC_WARNING("Enum lookup did not complete for user %s", user.c_str());
    SAS::Event event(trail, SASEvent::ENUM_INCOMPLETE, 0);
    event.add_var_param(user);
    SAS::report_event(event);
    // On failure, we must return an empty (rather than incomplete) string.
    string = std::string("");
  }

  // Report state of last communication attempt (which may potentially set/clear
  // an associated alarm). 
  if (_comm_monitor)
  {
    if (server_failed)
    {
      _comm_monitor->inform_failure();
    }
    else
    {
      _comm_monitor->inform_success();
    }
  }

  return string;
}
Ejemplo n.º 9
0
   bool
   SpamProtection::PerformGreyListing(std::shared_ptr<Message> message, const std::set<std::shared_ptr<SpamTestResult> > &spamTestResults, const String &toAddress, const IPAddress &ipaddress)
   {
      if (!Configuration::Instance()->GetAntiSpamConfiguration().GetUseGreyListing())
      {
         // The sender is either local, or grey listing is not enabled. We should
         // not do grey list checks in these cases.
         return true;
      }

      // Check if we should use grey listing for the recipient domain.
      String sRecipientDomain = StringParser::ExtractDomain(toAddress);
      std::shared_ptr<const Domain> pDomain = CacheContainer::Instance()->GetDomain(sRecipientDomain);         

      if (pDomain && !pDomain->GetASUseGreyListing())
      {
         // Grey listing not enabled in domain. Skip it.
         return true;
      }


      // Check if the SPF test has succeeded. If so, maybe we should not do
      if (Configuration::Instance()->GetAntiSpamConfiguration().GetBypassGreyListingOnSPFSuccess())
      {
         for(std::shared_ptr<SpamTestResult> testResult : spamTestResults)
         {
            if (testResult->GetTestName() == SpamTestSPF::GetTestName())
            {
               if (testResult->GetResult() == SpamTestResult::Pass)
               {
                  // We should not run grey listing since the SPF test has passed
                  LOG_DEBUG("SPF passed, skipping greylisting.");
                  return true;
               }
            }
         }
      }

      // Check if the mail is coming from the domain A record or MX record. If so, bypass grey listing.
      if (Configuration::Instance()->GetAntiSpamConfiguration().GetBypassGreyListingOnMailFromMX())
      {
         String senderDomain = StringParser::ExtractDomain(message->GetFromAddress());
         
         if (senderDomain.GetLength() > 0)
         {
            std::vector<String> found_ip_addresses;
               
            DNSResolver resolver;
            resolver.GetARecords(senderDomain, found_ip_addresses);

            std::vector<HostNameAndIpAddress> host_name_with_addresses;
            resolver.GetEmailServers(senderDomain, host_name_with_addresses);

            for(HostNameAndIpAddress host_and_ip : host_name_with_addresses)
               found_ip_addresses.push_back(host_and_ip.GetIpAddress());

            String actualFromAddress = ipaddress.ToString();
            for(String found_ip_address : found_ip_addresses)
            {
               if (found_ip_address.CompareNoCase(actualFromAddress) == 0)
               {
                  // The message is coming from either an A record or a MX record. Skip greylisting.
                  LOG_DEBUG("Mail coming from A or MX record. Skipping grey listing.");
                  return true;
               }
            }
         }
      }

      // Grey listing is enabled, and there's no reason to skip it. Go ahead!
      if (!GreyListingAllowSend(message->GetFromAddress(), toAddress, ipaddress))
         return false;

      return true; 
   }
Ejemplo n.º 10
0
   String 
   Utilities::GenerateReceivedHeader(const String &sRemoteIP, String sHostName)
   {
      String sComputerName = Utilities::ComputerName(); 
      vector<String> results;
      // do a PTR lookup, solves an issue with some spam filerting programs such as SA
      // not having a PTR in the Received header.
      String ptrRecord;
      DNSResolver resolver;
      if (!resolver.GetPTRRecords(sRemoteIP, results))
      {
         LOG_DEBUG("Could not get PTR record for IP (false)! " + sRemoteIP);
         ptrRecord = "Unknown";
      }
      else
      {
         if (results.size() == 0)
         {
            LOG_DEBUG("Could not get PTR record for IP (empty)! " + sRemoteIP);
            ptrRecord = "Unknown";
         }
         else ptrRecord = results[0];
      }

      if (sHostName.IsEmpty())
         sHostName = sRemoteIP;

      // Time-stamp-line = "Received:" FWS Stamp <CRLF>
      // Stamp = From-domain By-domain Opt-info ";"  FWS date-time
      // From-domain = "FROM" FWS Extended-Domain CFWS
      // By-domain = "BY" FWS Extended-Domain CFWS
      // Extended-Domain = Domain /
      //                   ( Domain FWS "(" TCP-info ")" ) /           
      //                   ( Address-literal FWS "(" TCP-info ")" )
      // TCP-info        = Address-literal / 
      //                   ( Domain FWS Address-literal )          
      //                   ; Information derived by server from TCP connection
      //                   ; not client EHLO.
      //Opt-info = [Via] [With] [ID] [For]
      //
      // The header produced by hMailServer used to look like this:
      //
      // Received: from <hostinhelo> ([ip-address])
      //              by <thiscomputername> 
      //              with hMailServer; timestamp
      //
      // The header produced by hMailServer now looks like this:
      //
      // Received: from <hostinhelo> ([ip-address])
      //              by <thiscomputername> 
      //              ; timestamp

      // JDR: insert the PTR result here. If none was found Unknown is used.
      String sResult;
      sResult.Format(_T("from %s (%s [%s])\r\n")
                     _T("\tby %s\r\n")
                     _T("\t; %s"), 
                        sHostName,
                        ptrRecord,
                        sRemoteIP,
                        sComputerName, 
                        Time::GetCurrentMimeDate());

      return sResult;

   }