Ejemplo n.º 1
0
LazyAddrIterator::LazyAddrIterator(DnsResult& dns_result,
                                   BaseResolver* resolver,
                                   int port,
                                   int transport,
                                   SAS::TrailId trail) :
  _resolver(resolver),
  _trail(trail),
  _first_call(true)
{
  _hostname = dns_result.domain();

  AddrInfo ai;
  ai.port = port;
  ai.transport = transport;

  // Reserve memory for the unused results vector to avoid reallocation.
  _unused_results.reserve(dns_result.records().size());

  // Create an AddrInfo object for each returned DnsRRecord, and add them to the
  // query results vector.
  for (std::vector<DnsRRecord*>::const_iterator result_it = dns_result.records().begin();
       result_it != dns_result.records().end();
       ++result_it)
  {
    ai.address = _resolver->to_ip46(*result_it);
    _unused_results.push_back(ai);
  }

  // Shuffle the results for load balancing purposes
  std::random_shuffle(_unused_results.begin(), _unused_results.end());
}
Ejemplo n.º 2
0
BaseAddrIterator* BaseResolver::a_resolve_iter(const std::string& hostname,
                                               int af,
                                               int port,
                                               int transport,
                                               int& ttl,
                                               SAS::TrailId trail)
{
  DnsResult result = _dns_client->dns_query(hostname, (af == AF_INET) ? ns_t_a : ns_t_aaaa, trail);
  ttl = result.ttl();

  TRC_DEBUG("Found %ld A/AAAA records, creating iterator", result.records().size());

  return new LazyAddrIterator(result, this, port, transport, trail);
}
Ejemplo n.º 3
0
BaseResolver::SRVPriorityList* BaseResolver::SRVCacheFactory::get(std::string key, int& ttl, SAS::TrailId trail)
{
  TRC_DEBUG("SRV cache factory called for %s", key.c_str());
  SRVPriorityList* srv_list = NULL;

  DnsResult result = _dns_client->dns_query(key, ns_t_srv, trail);

  if (!result.records().empty())
  {
    // We have a result.
    TRC_DEBUG("SRV query returned %d records", result.records().size());
    srv_list = new SRVPriorityList;
    ttl = result.ttl();

    // Sort the records on priority.
    std::sort(result.records().begin(), result.records().end(), compare_srv_priority);

    // Now rearrange the results in to an SRV priority list (a map of vectors
    // for each priority level).
    for (std::vector<DnsRRecord*>::const_iterator i = result.records().begin();
         i != result.records().end();
         ++i)
    {
      DnsSrvRecord* srv_record = (DnsSrvRecord*)(*i);

      // Get the appropriate priority list of SRVs.
      std::vector<SRV>& plist = (*srv_list)[srv_record->priority()];

      // Add a new entry for this SRV.
      plist.push_back(SRV());
      SRV& srv = plist.back();
      srv.target = srv_record->target();
      srv.port = srv_record->port();
      srv.priority = srv_record->priority();
      srv.weight = srv_record->weight();

      // Adjust the weight.  Any items which have weight 0 are increase to
      // weight of one, and non-zero weights are multiplied by 100.  This gives
      // the right behaviour as per RFC2782 - when all weights are zero we
      // round-robin (but still have the ability to blacklist) and when there
      // are non-zero weights the zero weighted items have a small (but not
      // specified in RFC2782) chance of selection.
      srv.weight = (srv.weight == 0) ? 1 : srv.weight * 100;
    }
  }
  else
  {
    // No results from SRV query, so return no entry with the default TTL
    ttl = _default_ttl;
  }

  return srv_list;
}
Ejemplo n.º 4
0
void SIPResolver::resolve(const std::string& name,
                          int af,
                          int port,
                          int transport,
                          int retries,
                          std::vector<AddrInfo>& targets,
                          SAS::TrailId trail)
{
  int dummy_ttl = 0;
  targets.clear();

  // First determine the transport following the process in RFC3263 section
  // 4.1.
  AddrInfo ai;

  TRC_DEBUG("SIPResolver::resolve for name %s, port %d, transport %d, family %d",
            name.c_str(), port, transport, af);

  if (trail != 0)
  {
    SAS::Event event(trail, SASEvent::SIPRESOLVE_START, 0);
    event.add_var_param(name);
    std::string port_str = std::to_string(port);
    std::string transport_str = get_transport_str(transport);
    event.add_var_param(port_str);
    event.add_var_param(transport_str);
    SAS::report_event(event);
  }

  if (parse_ip_target(name, ai.address))
  {
    // The name is already an IP address, so no DNS resolution is possible.
    // Use specified transport and port or defaults if not specified.
    TRC_DEBUG("Target is an IP address - default port/transport if required");
    ai.transport = (transport != -1) ? transport : IPPROTO_UDP;
    ai.port = (port != 0) ? port : 5060;
    targets.push_back(ai);

    if (trail != 0)
    {
      SAS::Event event(trail, SASEvent::SIPRESOLVE_IP_ADDRESS, 0);
      event.add_var_param(name);
      std::string port_str = std::to_string(ai.port);
      std::string transport_str = get_transport_str(ai.transport);
      event.add_var_param(transport_str);
      event.add_var_param(port_str);
      SAS::report_event(event);
    }
  }
  else
  {
    std::string srv_name;
    std::string a_name = name;

    if (port != 0)
    {
      // Port is specified, so don't do NAPTR or SRV look-ups.  Default transport
      // if required and move straight to A record look-up.
      TRC_DEBUG("Port is specified");
      transport = (transport != -1) ? transport : IPPROTO_UDP;

      if (trail != 0)
      {
        SAS::Event event(trail, SASEvent::SIPRESOLVE_PORT_A_LOOKUP, 0);
        event.add_var_param(name);
        std::string port_str = std::to_string(port);
        std::string transport_str = get_transport_str(transport);
        event.add_var_param(transport_str);
        event.add_var_param(port_str);
        SAS::report_event(event);
      }
    }
    else if (transport == -1)
    {
      // Transport protocol isn't specified, so do a NAPTR lookup for the target.
      TRC_DEBUG("Do NAPTR look-up for %s", name.c_str());

      if (trail != 0)
      {
        SAS::Event event(trail, SASEvent::SIPRESOLVE_NAPTR_LOOKUP, 0);
        event.add_var_param(name);
        SAS::report_event(event);
      }

      NAPTRReplacement* naptr = _naptr_cache->get(name, dummy_ttl, trail);

      if (naptr != NULL)
      {
        // NAPTR resolved to a supported service
        TRC_DEBUG("NAPTR resolved to transport %d", naptr->transport);
        transport = naptr->transport;
        if (strcasecmp(naptr->flags.c_str(), "S") == 0)
        {
          // Do an SRV lookup with the replacement domain from the NAPTR lookup.
          srv_name = naptr->replacement;

          if (trail != 0)
          {
            SAS::Event event(trail, SASEvent::SIPRESOLVE_NAPTR_SUCCESS_SRV, 0);
            event.add_var_param(name);
            event.add_var_param(srv_name);
            std::string transport_str = get_transport_str(naptr->transport);
            event.add_var_param(transport_str);
            SAS::report_event(event);
          }
        }
        else
        {
          // Move straight to A/AAAA lookup of the domain returned by NAPTR.
          a_name = naptr->replacement;

          if (trail != 0)
          {
            SAS::Event event(trail, SASEvent::SIPRESOLVE_NAPTR_SUCCESS_A, 0);
            event.add_var_param(name);
            event.add_var_param(a_name);
            SAS::report_event(event);
          }
        }
      }
      else
      {
        // NAPTR resolution failed, so do SRV lookups for both UDP and TCP to
        // see which transports are supported.
        TRC_DEBUG("NAPTR lookup failed, so do SRV lookups for UDP and TCP");

        if (trail != 0)
        {
          SAS::Event event(trail, SASEvent::SIPRESOLVE_NAPTR_FAILURE, 0);
          event.add_var_param(name);
          SAS::report_event(event);
        }

        std::vector<std::string> domains;
        domains.push_back("_sip._udp." + name);
        domains.push_back("_sip._tcp." + name);
        std::vector<DnsResult> results;
        _dns_client->dns_query(domains, ns_t_srv, results, trail);
        DnsResult& udp_result = results[0];
        TRC_DEBUG("UDP SRV record %s returned %d records",
                  udp_result.domain().c_str(), udp_result.records().size());
        DnsResult& tcp_result = results[1];
        TRC_DEBUG("TCP SRV record %s returned %d records",
                  tcp_result.domain().c_str(), tcp_result.records().size());

        if (!udp_result.records().empty())
        {
          // UDP SRV lookup returned some records, so use UDP transport.
          TRC_DEBUG("UDP SRV lookup successful, select UDP transport");
          transport = IPPROTO_UDP;
          srv_name = udp_result.domain();
        }
        else if (!tcp_result.records().empty())
        {
          // TCP SRV lookup returned some records, so use TCP transport.
          TRC_DEBUG("TCP SRV lookup successful, select TCP transport");
          transport = IPPROTO_TCP;
          srv_name = tcp_result.domain();
        }
        else
        {
          // Neither UDP nor TCP SRV lookup returned any results, so default to
          // UDP transport and move straight to A/AAAA record lookups.
          TRC_DEBUG("UDP and TCP SRV queries unsuccessful, default to UDP");
          transport = IPPROTO_UDP;
        }
      }

      _naptr_cache->dec_ref(name);
    }
    else if (transport == IPPROTO_UDP)
    {
      // Use specified transport and try an SRV lookup.
      if (trail != 0)
      {
        SAS::Event event(trail, SASEvent::SIPRESOLVE_TRANSPORT_SRV_LOOKUP, 0);
        event.add_var_param(name);
        std::string transport_str = get_transport_str(transport);
        event.add_var_param(transport_str);
        SAS::report_event(event);
      }

      DnsResult result = _dns_client->dns_query("_sip._udp." + name, ns_t_srv, trail);

      if (!result.records().empty())
      {
        srv_name = result.domain();
      }
    }
    else if (transport == IPPROTO_TCP)
    {
      // Use specified transport and try an SRV lookup.
      if (trail != 0)
      {
        SAS::Event event(trail, SASEvent::SIPRESOLVE_TRANSPORT_SRV_LOOKUP, 0);
        event.add_var_param(name);
        std::string transport_str = get_transport_str(transport);
        event.add_var_param(transport_str);
        SAS::report_event(event);
      }

      DnsResult result = _dns_client->dns_query("_sip._tcp." + name, ns_t_srv, trail);

      if (!result.records().empty())
      {
        srv_name = result.domain();
      }
    }

    if (srv_name != "")
    {
      TRC_DEBUG("Do SRV lookup for %s", srv_name.c_str());

      if (trail != 0)
      {
        SAS::Event event(trail, SASEvent::SIPRESOLVE_SRV_LOOKUP, 0);
        event.add_var_param(srv_name);
        std::string transport_str = get_transport_str(transport);
        event.add_var_param(transport_str);
        SAS::report_event(event);
      }

      srv_resolve(srv_name, af, transport, retries, targets, dummy_ttl, trail);
    }
    else
    {
      TRC_DEBUG("Perform A/AAAA record lookup only, name = %s", a_name.c_str());
      port = (port != 0) ? port : 5060;

      if (trail != 0)
      {
        SAS::Event event(trail, SASEvent::SIPRESOLVE_A_LOOKUP, 0);
        event.add_var_param(a_name);
        std::string transport_str = get_transport_str(transport);
        std::string port_str = std::to_string(port);
        event.add_var_param(transport_str);
        event.add_var_param(port_str);
        SAS::report_event(event);
      }

      a_resolve(a_name, af, port, transport, retries, targets, dummy_ttl, trail);
    }
  }
}
Ejemplo n.º 5
0
BaseResolver::NAPTRReplacement* BaseResolver::NAPTRCacheFactory::get(std::string key, int& ttl, SAS::TrailId trail)
{
  // Iterate NAPTR lookups starting with querying the target domain until
  // we get a terminal result.
  TRC_DEBUG("NAPTR cache factory called for %s", key.c_str());
  NAPTRReplacement* repl = NULL;
  std::string query_key = key;
  int expires = 0;
  bool loop_again = true;

  while (loop_again)
  {
    // Assume this is our last loop - we'll correct this if we find we should go round again.
    loop_again = false;

    // Issue the NAPTR query.
    TRC_DEBUG("Sending DNS NAPTR query for %s", query_key.c_str());
    DnsResult result = _dns_client->dns_query(query_key, ns_t_naptr, trail);

    if (!result.records().empty())
    {
      // Process the NAPTR records as per RFC2915 section 4.  First step is to
      // collect the records that match one of the requested services and have
      // acceptable flags.
      std::vector<DnsNaptrRecord*> filtered;

      for (std::vector<DnsRRecord*>::const_iterator i = result.records().begin();
           i != result.records().end();
           ++i)
      {
        DnsNaptrRecord* naptr = (DnsNaptrRecord*)(*i);
        if ((_services.find(naptr->service()) != _services.end()) &&
            ((strcasecmp(naptr->flags().c_str(), "S") == 0) ||
             (strcasecmp(naptr->flags().c_str(), "A") == 0) ||
             (strcasecmp(naptr->flags().c_str(), "") == 0)))
        {
          // This record has passed the filter.
          filtered.push_back(naptr);
        }
      }

      // Now sort the resulting filtered list on order and preference.
      std::sort(filtered.begin(),
                filtered.end(),
                BaseResolver::NAPTRCacheFactory::compare_naptr_order_preference);

      // Now loop through the records looking for the first match - where a
      // match is either a record with a replacement string, or a regular
      // expression which matches the input.  Note that we must always use
      // the originally specified domain in the regex - using the result of
      // a previous regex application is not allowed.
      for (size_t ii = 0; ii < filtered.size(); ++ii)
      {
        DnsNaptrRecord* naptr = filtered[ii];
        std::string replacement = naptr->replacement();

        if ((replacement == "") &&
            (naptr->regexp() != ""))
        {
          // Record has no replacement value, but does have a regular expression.
          boost::regex regex;
          std::string replace;
          if (parse_regex_replace(naptr->regexp(), regex, replace))
          {
            // We have a valid regex so try to match it to the original
            // queried domain to generate a new key.  Note that going straight
            // to replace is okay.  We haven't set the format_no_copy flag so
            // parts of the string that do not match the regex will not be
            // copied, so no match means we end up with an empty string.
            replacement = boost::regex_replace(key,
                                               regex,
                                               replace,
                                               boost::regex_constants::format_first_only);
          }
        }

        // Update ttl with the expiry of this record, so if we do get
        // a positive result we only cache it while all of the NAPTR records
        // we followed are still valid.
        if ((expires == 0) ||
            (expires > naptr->expires()))
        {
          expires = naptr->expires();
        }

        if (replacement != "")
        {
          // We have a match, so we can terminate processing here and look
          // at the flags to decide what to do next.
          if (strcasecmp(naptr->flags().c_str(), "") == 0)
          {
            // We need to iterate the NAPTR query.
            query_key = replacement;
            loop_again = true;
          }
          else
          {
            // This is a terminal record, so set up the result.
            repl = new NAPTRReplacement;
            repl->replacement = replacement;
            repl->flags = naptr->flags();
            repl->transport = _services[naptr->service()];
            ttl = expires - time(NULL);
          }
          break;
        }
      }
    }
    else
    {
      // No NAPTR record found, so return no entry with the default TTL.
      ttl = _default_ttl;
    }
  }
  return repl;
}