1
// NOLINT(namespace-envoy)
2
#pragma once
3

            
4
// Note that this file is included in emscripten and NullVM environments and thus depends on
5
// the context in which it is included, hence we need to disable clang-tidy warnings.
6

            
7
extern "C" WasmResult envoy_resolve_dns(const char* dns_address, size_t dns_address_size,
8
                                        uint32_t* token);
9

            
10
class EnvoyContextBase {
11
public:
12
19
  virtual ~EnvoyContextBase() = default;
13
};
14

            
15
class EnvoyRootContext : public RootContext, public EnvoyContextBase {
16
public:
17
18
  EnvoyRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {}
18
18
  ~EnvoyRootContext() override = default;
19

            
20
1
  virtual void onResolveDns(uint32_t /* token */, uint32_t /* result_size */) {}
21
1
  virtual void onStatsUpdate(uint32_t /* result_size */) {}
22
};
23

            
24
class EnvoyContext : public Context, public EnvoyContextBase {
25
public:
26
1
  EnvoyContext(uint32_t id, RootContext* root) : Context(id, root) {}
27
1
  ~EnvoyContext() override = default;
28
};
29

            
30
struct DnsResult {
31
  uint32_t ttl_seconds;
32
  std::string address;
33
};
34

            
35
struct CounterResult {
36
  uint64_t delta;
37
  std::string_view name;
38
  uint64_t value;
39
};
40

            
41
struct GaugeResult {
42
  uint64_t value;
43
  std::string_view name;
44
};
45

            
46
struct StatResult {
47
  std::vector<CounterResult> counters;
48
  std::vector<GaugeResult> gauges;
49
};
50

            
51
enum class StatType : uint32_t {
52
  Counter = 1,
53
  Gauge = 2,
54
};
55

            
56
2
inline std::vector<DnsResult> parseDnsResults(std::string_view data) {
57
2
  if (data.size() < 4) {
58
1
    return {};
59
1
  }
60
1
  const uint32_t* pn = reinterpret_cast<const uint32_t*>(data.data());
61
1
  uint32_t n = *pn++;
62
1
  std::vector<DnsResult> results;
63
1
  results.resize(n);
64
1
  const char* pa = data.data() + (1 + n) * sizeof(uint32_t); // skip n + n TTLs
65
3
  for (uint32_t i = 0; i < n; i++) {
66
2
    auto& e = results[i];
67
2
    e.ttl_seconds = *pn++;
68
2
    auto alen = strlen(pa);
69
2
    e.address.assign(pa, alen);
70
2
    pa += alen + 1;
71
2
  }
72
1
  return results;
73
2
}
74

            
75
4
template <typename I> inline uint32_t align(uint32_t i) {
76
4
  return (i + sizeof(I) - 1) & ~(sizeof(I) - 1);
77
4
}
78

            
79
// Used attribute is used here to work around a coverage issue in clang/llvm:
80
// https://github.com/llvm/llvm-project/issues/32849.
81
//
82
// It's a temporary hack and is not supposed to be a permanent solution.
83
// If we address the issue upstream in clang or migrate to static linking for coverage tests
84
// we can drop this.
85
//
86
// Alternative to using used attribute here is to move the function definition to
87
// the cc file. However, I didn't go for that, given that it looks that the function
88
// was put in the header file intentionally.
89
2
__attribute__((used)) inline StatResult parseStatResults(std::string_view data) {
90
2
  StatResult results;
91
2
  uint32_t data_len = 0;
92
6
  while (data_len < data.length()) {
93
4
    const uint32_t* n = reinterpret_cast<const uint32_t*>(data.data() + data_len);
94
4
    uint32_t block_size = *n++;
95
4
    uint32_t block_type = *n++;
96
4
    uint32_t num_stats = *n++;
97
4
    if (static_cast<StatType>(block_type) == StatType::Counter) { // counter
98
2
      std::vector<CounterResult> counters(num_stats);
99
2
      uint32_t stat_index = data_len + 3 * sizeof(uint32_t);
100
4
      for (uint32_t i = 0; i < num_stats; i++) {
101
2
        const uint32_t* stat_name = reinterpret_cast<const uint32_t*>(data.data() + stat_index);
102
2
        uint32_t name_len = *stat_name;
103
2
        stat_index += sizeof(uint32_t);
104

            
105
2
        auto& e = counters[i];
106
2
        e.name = {data.data() + stat_index, name_len};
107
2
        stat_index = align<uint64_t>(stat_index + name_len);
108

            
109
2
        const uint64_t* stat_vals = reinterpret_cast<const uint64_t*>(data.data() + stat_index);
110
2
        e.value = *stat_vals++;
111
2
        e.delta = *stat_vals++;
112

            
113
2
        stat_index += 2 * sizeof(uint64_t);
114
2
      }
115
2
      results.counters = counters;
116
2
    } else if (static_cast<StatType>(block_type) == StatType::Gauge) { // gauge
117
2
      std::vector<GaugeResult> gauges(num_stats);
118
2
      uint32_t stat_index = data_len + 3 * sizeof(uint32_t);
119
4
      for (uint32_t i = 0; i < num_stats; i++) {
120
2
        const uint32_t* stat_name = reinterpret_cast<const uint32_t*>(data.data() + stat_index);
121
2
        uint32_t name_len = *stat_name;
122
2
        stat_index += sizeof(uint32_t);
123

            
124
2
        auto& e = gauges[i];
125
2
        e.name = {data.data() + stat_index, name_len};
126
2
        stat_index = align<uint64_t>(stat_index + name_len);
127

            
128
2
        const uint64_t* stat_vals = reinterpret_cast<const uint64_t*>(data.data() + stat_index);
129
2
        e.value = *stat_vals++;
130

            
131
2
        stat_index += sizeof(uint64_t);
132
2
      }
133
2
      results.gauges = gauges;
134
2
    }
135
4
    data_len += block_size;
136
4
  }
137

            
138
2
  return results;
139
2
}
140

            
141
extern "C" WasmResult envoy_resolve_dns(const char* address, size_t address_size, uint32_t* token);