LCOV - code coverage report
Current view: top level - source/extensions/tracers/zipkin - span_context_extractor.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 168 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 7 0.0 %

          Line data    Source code
       1             : #include "source/extensions/tracers/zipkin/span_context_extractor.h"
       2             : 
       3             : #include "source/common/common/assert.h"
       4             : #include "source/common/common/utility.h"
       5             : #include "source/extensions/tracers/zipkin/span_context.h"
       6             : #include "source/extensions/tracers/zipkin/zipkin_core_constants.h"
       7             : 
       8             : namespace Envoy {
       9             : namespace Extensions {
      10             : namespace Tracers {
      11             : namespace Zipkin {
      12             : namespace {
      13             : constexpr int FormatMaxLength = 32 + 1 + 16 + 3 + 16; // traceid128-spanid-1-parentid
      14           0 : bool validSamplingFlags(char c) {
      15           0 :   if (c == '1' || c == '0' || c == 'd') {
      16           0 :     return true;
      17           0 :   }
      18           0 :   return false;
      19           0 : }
      20             : 
      21           0 : bool getSamplingFlags(char c, const Tracing::Decision tracing_decision) {
      22           0 :   if (validSamplingFlags(c)) {
      23           0 :     return c == '0' ? false : true;
      24           0 :   } else {
      25           0 :     return tracing_decision.traced;
      26           0 :   }
      27           0 : }
      28             : 
      29             : } // namespace
      30             : 
      31             : SpanContextExtractor::SpanContextExtractor(Tracing::TraceContext& trace_context)
      32           0 :     : trace_context_(trace_context) {}
      33             : 
      34           0 : SpanContextExtractor::~SpanContextExtractor() = default;
      35             : 
      36           0 : bool SpanContextExtractor::extractSampled(const Tracing::Decision tracing_decision) {
      37           0 :   bool sampled(false);
      38           0 :   auto b3_header_entry = ZipkinCoreConstants::get().B3.get(trace_context_);
      39           0 :   if (b3_header_entry.has_value()) {
      40             :     // This is an implicitly untrusted header, so only the first value is used.
      41           0 :     absl::string_view b3 = b3_header_entry.value();
      42           0 :     int sampled_pos = 0;
      43           0 :     switch (b3.length()) {
      44           0 :     case 1:
      45           0 :       break;
      46           0 :     case 35: // 16 + 1 + 16 + 2
      47           0 :       sampled_pos = 34;
      48           0 :       break;
      49           0 :     case 51: // 32 + 1 + 16 + 2
      50           0 :       sampled_pos = 50;
      51           0 :       break;
      52           0 :     case 52: // 16 + 1 + 16 + 2 + 1 + 16
      53           0 :       sampled_pos = 34;
      54           0 :       break;
      55           0 :     case 68: // 32 + 1 + 16 + 2 + 1 + 16
      56           0 :       sampled_pos = 50;
      57           0 :       break;
      58           0 :     default:
      59           0 :       return tracing_decision.traced;
      60           0 :     }
      61           0 :     return getSamplingFlags(b3[sampled_pos], tracing_decision);
      62           0 :   }
      63             : 
      64           0 :   auto x_b3_sampled_entry = ZipkinCoreConstants::get().X_B3_SAMPLED.get(trace_context_);
      65           0 :   if (!x_b3_sampled_entry.has_value()) {
      66           0 :     return tracing_decision.traced;
      67           0 :   }
      68             :   // Checking if sampled flag has been specified. Also checking for 'true' value, as some old
      69             :   // zipkin tracers may still use that value, although should be 0 or 1.
      70             :   // This is an implicitly untrusted header, so only the first value is used.
      71           0 :   absl::string_view xb3_sampled = x_b3_sampled_entry.value();
      72           0 :   sampled = xb3_sampled == SAMPLED || xb3_sampled == "true";
      73           0 :   return sampled;
      74           0 : }
      75             : 
      76           0 : std::pair<SpanContext, bool> SpanContextExtractor::extractSpanContext(bool is_sampled) {
      77           0 :   if (ZipkinCoreConstants::get().B3.get(trace_context_).has_value()) {
      78           0 :     return extractSpanContextFromB3SingleFormat(is_sampled);
      79           0 :   }
      80           0 :   uint64_t trace_id(0);
      81           0 :   uint64_t trace_id_high(0);
      82           0 :   uint64_t span_id(0);
      83           0 :   uint64_t parent_id(0);
      84             : 
      85           0 :   auto b3_trace_id_entry = ZipkinCoreConstants::get().X_B3_TRACE_ID.get(trace_context_);
      86           0 :   auto b3_span_id_entry = ZipkinCoreConstants::get().X_B3_SPAN_ID.get(trace_context_);
      87           0 :   if (b3_span_id_entry.has_value() && b3_trace_id_entry.has_value()) {
      88             :     // Extract trace id - which can either be 128 or 64 bit. For 128 bit,
      89             :     // it needs to be divided into two 64 bit numbers (high and low).
      90             :     // This is an implicitly untrusted header, so only the first value is used.
      91           0 :     const std::string tid(b3_trace_id_entry.value());
      92           0 :     if (b3_trace_id_entry.value().size() == 32) {
      93           0 :       const std::string high_tid = tid.substr(0, 16);
      94           0 :       const std::string low_tid = tid.substr(16, 16);
      95           0 :       if (!StringUtil::atoull(high_tid.c_str(), trace_id_high, 16) ||
      96           0 :           !StringUtil::atoull(low_tid.c_str(), trace_id, 16)) {
      97           0 :         throw ExtractorException(
      98           0 :             fmt::format("Invalid traceid_high {} or tracid {}", high_tid.c_str(), low_tid.c_str()));
      99           0 :       }
     100           0 :     } else if (!StringUtil::atoull(tid.c_str(), trace_id, 16)) {
     101           0 :       throw ExtractorException(absl::StrCat("Invalid trace_id ", tid.c_str()));
     102           0 :     }
     103             : 
     104             :     // This is an implicitly untrusted header, so only the first value is used.
     105           0 :     const std::string spid(b3_span_id_entry.value());
     106           0 :     if (!StringUtil::atoull(spid.c_str(), span_id, 16)) {
     107           0 :       throw ExtractorException(absl::StrCat("Invalid span id ", spid.c_str()));
     108           0 :     }
     109             : 
     110           0 :     auto b3_parent_id_entry = ZipkinCoreConstants::get().X_B3_PARENT_SPAN_ID.get(trace_context_);
     111           0 :     if (b3_parent_id_entry.has_value() && !b3_parent_id_entry.value().empty()) {
     112             :       // This is an implicitly untrusted header, so only the first value is used.
     113           0 :       const std::string pspid(b3_parent_id_entry.value());
     114           0 :       if (!StringUtil::atoull(pspid.c_str(), parent_id, 16)) {
     115           0 :         throw ExtractorException(absl::StrCat("Invalid parent span id ", pspid.c_str()));
     116           0 :       }
     117           0 :     }
     118           0 :   } else {
     119           0 :     return {SpanContext(), false};
     120           0 :   }
     121             : 
     122           0 :   return {SpanContext(trace_id_high, trace_id, span_id, parent_id, is_sampled), true};
     123           0 : }
     124             : 
     125             : std::pair<SpanContext, bool>
     126           0 : SpanContextExtractor::extractSpanContextFromB3SingleFormat(bool is_sampled) {
     127           0 :   auto b3_head_entry = ZipkinCoreConstants::get().B3.get(trace_context_);
     128           0 :   ASSERT(b3_head_entry.has_value());
     129             :   // This is an implicitly untrusted header, so only the first value is used.
     130           0 :   const std::string b3(b3_head_entry.value());
     131           0 :   if (!b3.length()) {
     132           0 :     throw ExtractorException("Invalid input: empty");
     133           0 :   }
     134             : 
     135           0 :   if (b3.length() == 1) { // possibly sampling flags
     136           0 :     if (validSamplingFlags(b3[0])) {
     137           0 :       return {SpanContext(), false};
     138           0 :     }
     139           0 :     throw ExtractorException(fmt::format("Invalid input: invalid sampling flag {}", b3[0]));
     140           0 :   }
     141             : 
     142           0 :   if (b3.length() < 16 + 1 + 16 /* traceid64-spanid */) {
     143           0 :     throw ExtractorException("Invalid input: truncated");
     144           0 :   } else if (b3.length() > FormatMaxLength) {
     145           0 :     throw ExtractorException("Invalid input: too long");
     146           0 :   }
     147             : 
     148           0 :   uint64_t trace_id(0);
     149           0 :   uint64_t trace_id_high(0);
     150           0 :   uint64_t span_id(0);
     151           0 :   uint64_t parent_id(0);
     152             : 
     153           0 :   uint64_t pos = 0;
     154             : 
     155           0 :   const std::string trace_id_str = b3.substr(pos, 16);
     156           0 :   if (b3[pos + 32] == '-') {
     157           0 :     if (!StringUtil::atoull(trace_id_str.c_str(), trace_id_high, 16)) {
     158           0 :       throw ExtractorException(
     159           0 :           fmt::format("Invalid input: invalid trace id high {}", trace_id_str.c_str()));
     160           0 :     }
     161           0 :     pos += 16;
     162           0 :     const std::string trace_id_low_str = b3.substr(pos, 16);
     163           0 :     if (!StringUtil::atoull(trace_id_low_str.c_str(), trace_id, 16)) {
     164           0 :       throw ExtractorException(
     165           0 :           fmt::format("Invalid input: invalid trace id {}", trace_id_low_str.c_str()));
     166           0 :     }
     167           0 :   } else {
     168           0 :     if (!StringUtil::atoull(trace_id_str.c_str(), trace_id, 16)) {
     169           0 :       throw ExtractorException(
     170           0 :           fmt::format("Invalid input: invalid trace id {}", trace_id_str.c_str()));
     171           0 :     }
     172           0 :   }
     173             : 
     174           0 :   pos += 16; // traceId ended
     175           0 :   if (!(b3[pos++] == '-')) {
     176           0 :     throw ExtractorException("Invalid input: not exists span id");
     177           0 :   }
     178             : 
     179           0 :   const std::string span_id_str = b3.substr(pos, 16);
     180           0 :   if (!StringUtil::atoull(span_id_str.c_str(), span_id, 16)) {
     181           0 :     throw ExtractorException(fmt::format("Invalid input: invalid span id {}", span_id_str.c_str()));
     182           0 :   }
     183           0 :   pos += 16; // spanId ended
     184             : 
     185           0 :   if (b3.length() > pos) {
     186             :     // If we are at this point, we have more than just traceId-spanId.
     187             :     // If the sampling field is present, we'll have a delimiter 2 characters from now. Ex "-1"
     188             :     // If it is absent, but a parent ID is (which is strange), we'll have at least 17 characters.
     189             :     // Therefore, if we have less than two characters, the input is truncated.
     190           0 :     if (b3.length() == (pos + 1)) {
     191           0 :       throw ExtractorException("Invalid input: truncated");
     192           0 :     }
     193             : 
     194           0 :     if (!(b3[pos++] == '-')) {
     195           0 :       throw ExtractorException("Invalid input: not exists sampling field");
     196           0 :     }
     197             : 
     198             :     // If our position is at the end of the string, or another delimiter is one character past our
     199             :     // position, try to read sampled status.
     200           0 :     if (b3.length() == pos + 1 || ((b3.length() >= pos + 2) && (b3[pos + 1] == '-'))) {
     201           0 :       if (!validSamplingFlags(b3[pos])) {
     202           0 :         throw ExtractorException(fmt::format("Invalid input: invalid sampling flag {}", b3[pos]));
     203           0 :       }
     204           0 :       pos++; // consume the sampled status
     205           0 :     } else {
     206           0 :       throw ExtractorException("Invalid input: truncated");
     207           0 :     }
     208             : 
     209           0 :     if (b3.length() > pos) {
     210             :       // If we are at this point, we should have a parent ID, encoded as "-[0-9a-f]{16}"
     211           0 :       if (b3.length() != pos + 17) {
     212           0 :         throw ExtractorException("Invalid input: truncated");
     213           0 :       }
     214             : 
     215           0 :       ASSERT(b3[pos] == '-');
     216           0 :       pos++;
     217             : 
     218           0 :       const std::string parent_id_str = b3.substr(pos, b3.length() - pos);
     219           0 :       if (!StringUtil::atoull(parent_id_str.c_str(), parent_id, 16)) {
     220           0 :         throw ExtractorException(
     221           0 :             fmt::format("Invalid input: invalid parent id {}", parent_id_str.c_str()));
     222           0 :       }
     223           0 :     }
     224           0 :   }
     225             : 
     226           0 :   return {SpanContext(trace_id_high, trace_id, span_id, parent_id, is_sampled), true};
     227           0 : }
     228             : 
     229             : } // namespace Zipkin
     230             : } // namespace Tracers
     231             : } // namespace Extensions
     232             : } // namespace Envoy

Generated by: LCOV version 1.15