forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathforeign.cc
328 lines (300 loc) · 13.2 KB
/
foreign.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#include "source/common/common/logger.h"
#include "source/extensions/common/wasm/ext/declare_property.pb.h"
#include "source/extensions/common/wasm/ext/set_envoy_filter_state.pb.h"
#include "source/extensions/common/wasm/ext/verify_signature.pb.h"
#include "source/extensions/common/wasm/wasm.h"
#if defined(WASM_USE_CEL_PARSER)
#include "eval/public/builtin_func_registrar.h"
#include "eval/public/cel_expr_builder_factory.h"
#include "parser/parser.h"
#endif
#include "zlib.h"
#include "source/common/crypto/crypto_impl.h"
#include "source/common/crypto/utility.h"
#include "source/common/common/logger.h"
using proxy_wasm::RegisterForeignFunction;
using proxy_wasm::WasmForeignFunction;
namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
using CelStateType = Filters::Common::Expr::CelStateType;
template <typename T> WasmForeignFunction createFromClass() {
auto c = std::make_shared<T>();
return c->create(c);
}
inline StreamInfo::FilterState::LifeSpan
toFilterStateLifeSpan(envoy::source::extensions::common::wasm::LifeSpan span) {
switch (span) {
case envoy::source::extensions::common::wasm::LifeSpan::FilterChain:
return StreamInfo::FilterState::LifeSpan::FilterChain;
case envoy::source::extensions::common::wasm::LifeSpan::DownstreamRequest:
return StreamInfo::FilterState::LifeSpan::Request;
case envoy::source::extensions::common::wasm::LifeSpan::DownstreamConnection:
return StreamInfo::FilterState::LifeSpan::Connection;
default:
return StreamInfo::FilterState::LifeSpan::FilterChain;
}
}
RegisterForeignFunction registerVerifySignatureForeignFunction(
"verify_signature",
[](WasmBase&, std::string_view arguments,
const std::function<void*(size_t size)>& alloc_result) -> WasmResult {
envoy::source::extensions::common::wasm::VerifySignatureArguments args;
if (args.ParseFromArray(arguments.data(), arguments.size())) {
const auto& hash = args.hash_function();
auto key_str = args.public_key();
auto signature_str = args.signature();
auto text_str = args.text();
std::vector<uint8_t> key(key_str.begin(), key_str.end());
std::vector<uint8_t> signature(signature_str.begin(), signature_str.end());
std::vector<uint8_t> text(text_str.begin(), text_str.end());
auto& crypto_util = Envoy::Common::Crypto::UtilitySingleton::get();
Envoy::Common::Crypto::CryptoObjectPtr crypto_ptr = crypto_util.importPublicKey(key);
auto output = crypto_util.verifySignature(hash, *crypto_ptr, signature, text);
envoy::source::extensions::common::wasm::VerifySignatureResult verification_result;
verification_result.set_result(output.result_);
verification_result.set_error(output.error_message_);
auto size = verification_result.ByteSizeLong();
auto result = alloc_result(size);
verification_result.SerializeToArray(result, static_cast<int>(size));
return WasmResult::Ok;
}
return WasmResult::BadArgument;
});
RegisterForeignFunction registerCompressForeignFunction(
"compress",
[](WasmBase&, std::string_view arguments,
const std::function<void*(size_t size)>& alloc_result) -> WasmResult {
unsigned long dest_len = compressBound(arguments.size());
std::unique_ptr<unsigned char[]> b(new unsigned char[dest_len]);
if (compress(b.get(), &dest_len, reinterpret_cast<const unsigned char*>(arguments.data()),
arguments.size()) != Z_OK) {
return WasmResult::SerializationFailure;
}
auto result = alloc_result(dest_len);
memcpy(result, b.get(), dest_len); // NOLINT(safe-memcpy)
return WasmResult::Ok;
});
RegisterForeignFunction registerUncompressForeignFunction(
"uncompress",
[](WasmBase&, std::string_view arguments,
const std::function<void*(size_t size)>& alloc_result) -> WasmResult {
unsigned long dest_len = arguments.size() * 2 + 2; // output estimate.
while (true) {
std::unique_ptr<unsigned char[]> b(new unsigned char[dest_len]);
auto r =
uncompress(b.get(), &dest_len, reinterpret_cast<const unsigned char*>(arguments.data()),
arguments.size());
if (r == Z_OK) {
auto result = alloc_result(dest_len);
memcpy(result, b.get(), dest_len); // NOLINT(safe-memcpy)
return WasmResult::Ok;
}
if (r != Z_BUF_ERROR) {
return WasmResult::SerializationFailure;
}
dest_len = dest_len * 2;
}
});
RegisterForeignFunction registerSetEnvoyFilterStateForeignFunction(
"set_envoy_filter_state",
[](WasmBase&, std::string_view arguments,
const std::function<void*(size_t size)>&) -> WasmResult {
envoy::source::extensions::common::wasm::SetEnvoyFilterStateArguments args;
if (args.ParseFromArray(arguments.data(), arguments.size())) {
auto context = static_cast<Context*>(proxy_wasm::current_context_);
return context->setEnvoyFilterState(args.path(), args.value(),
toFilterStateLifeSpan(args.span()));
}
return WasmResult::BadArgument;
});
#if defined(WASM_USE_CEL_PARSER)
class ExpressionFactory : public Logger::Loggable<Logger::Id::wasm> {
protected:
struct ExpressionData {
google::api::expr::v1alpha1::ParsedExpr parsed_expr_;
Filters::Common::Expr::ExpressionPtr compiled_expr_;
};
class ExpressionContext : public StorageObject {
public:
friend class ExpressionFactory;
ExpressionContext(Filters::Common::Expr::BuilderPtr builder) : builder_(std::move(builder)) {}
uint32_t createToken() {
uint32_t token = next_expr_token_++;
for (;;) {
if (!expr_.count(token)) {
break;
}
token = next_expr_token_++;
}
return token;
}
bool hasExpression(uint32_t token) { return expr_.contains(token); }
ExpressionData& getExpression(uint32_t token) { return expr_[token]; }
void deleteExpression(uint32_t token) { expr_.erase(token); }
Filters::Common::Expr::Builder* builder() { return builder_.get(); }
private:
Filters::Common::Expr::BuilderPtr builder_{};
uint32_t next_expr_token_ = 0;
absl::flat_hash_map<uint32_t, ExpressionData> expr_;
};
static ExpressionContext& getOrCreateContext(ContextBase* context_base) {
auto context = static_cast<Context*>(context_base);
std::string data_name = "cel";
auto expr_context = context->getForeignData<ExpressionContext>(data_name);
if (!expr_context) {
google::api::expr::runtime::InterpreterOptions options;
auto builder = google::api::expr::runtime::CreateCelExpressionBuilder(options);
auto status =
google::api::expr::runtime::RegisterBuiltinFunctions(builder->GetRegistry(), options);
if (!status.ok()) {
ENVOY_LOG(warn, "failed to register built-in functions: {}", status.message());
}
auto new_context = std::make_unique<ExpressionContext>(std::move(builder));
expr_context = new_context.get();
context->setForeignData(data_name, std::move(new_context));
}
return *expr_context;
}
};
class CreateExpressionFactory : public ExpressionFactory {
public:
WasmForeignFunction create(std::shared_ptr<CreateExpressionFactory> self) const {
WasmForeignFunction f =
[self](WasmBase&, std::string_view expr,
const std::function<void*(size_t size)>& alloc_result) -> WasmResult {
auto parse_status = google::api::expr::parser::Parse(std::string(expr));
if (!parse_status.ok()) {
ENVOY_LOG(info, "expr_create parse error: {}", parse_status.status().message());
return WasmResult::BadArgument;
}
auto& expr_context = getOrCreateContext(proxy_wasm::current_context_->root_context());
auto token = expr_context.createToken();
auto& handler = expr_context.getExpression(token);
handler.parsed_expr_ = parse_status.value();
auto cel_expression_status = expr_context.builder()->CreateExpression(
&handler.parsed_expr_.expr(), &handler.parsed_expr_.source_info());
if (!cel_expression_status.ok()) {
ENVOY_LOG(info, "expr_create compile error: {}", cel_expression_status.status().message());
expr_context.deleteExpression(token);
return WasmResult::BadArgument;
}
handler.compiled_expr_ = std::move(cel_expression_status.value());
auto result = reinterpret_cast<uint32_t*>(alloc_result(sizeof(uint32_t)));
*result = token;
return WasmResult::Ok;
};
return f;
}
};
RegisterForeignFunction
registerCreateExpressionForeignFunction("expr_create",
createFromClass<CreateExpressionFactory>());
class EvaluateExpressionFactory : public ExpressionFactory {
public:
WasmForeignFunction create(std::shared_ptr<EvaluateExpressionFactory> self) const {
WasmForeignFunction f =
[self](WasmBase&, std::string_view argument,
const std::function<void*(size_t size)>& alloc_result) -> WasmResult {
auto& expr_context = getOrCreateContext(proxy_wasm::current_context_->root_context());
if (argument.size() != sizeof(uint32_t)) {
return WasmResult::BadArgument;
}
uint32_t token = *reinterpret_cast<const uint32_t*>(argument.data());
if (!expr_context.hasExpression(token)) {
return WasmResult::NotFound;
}
Protobuf::Arena arena;
auto& handler = expr_context.getExpression(token);
auto context = static_cast<Context*>(proxy_wasm::current_context_);
auto eval_status = handler.compiled_expr_->Evaluate(*context, &arena);
if (!eval_status.ok()) {
ENVOY_LOG(debug, "expr_evaluate error: {}", eval_status.status().message());
return WasmResult::InternalFailure;
}
auto value = eval_status.value();
if (value.IsError()) {
ENVOY_LOG(debug, "expr_evaluate value error: {}", value.ErrorOrDie()->message());
return WasmResult::InternalFailure;
}
std::string result;
auto serialize_status = serializeValue(value, &result);
if (serialize_status != WasmResult::Ok) {
return serialize_status;
}
auto output = alloc_result(result.size());
memcpy(output, result.data(), result.size()); // NOLINT(safe-memcpy)
return WasmResult::Ok;
};
return f;
}
};
RegisterForeignFunction
registerEvaluateExpressionForeignFunction("expr_evaluate",
createFromClass<EvaluateExpressionFactory>());
class DeleteExpressionFactory : public ExpressionFactory {
public:
WasmForeignFunction create(std::shared_ptr<DeleteExpressionFactory> self) const {
WasmForeignFunction f = [self](WasmBase&, std::string_view argument,
const std::function<void*(size_t size)>&) -> WasmResult {
auto& expr_context = getOrCreateContext(proxy_wasm::current_context_->root_context());
if (argument.size() != sizeof(uint32_t)) {
return WasmResult::BadArgument;
}
uint32_t token = *reinterpret_cast<const uint32_t*>(argument.data());
expr_context.deleteExpression(token);
return WasmResult::Ok;
};
return f;
}
};
RegisterForeignFunction
registerDeleteExpressionForeignFunction("expr_delete",
createFromClass<DeleteExpressionFactory>());
#endif
// TODO(kyessenov) The factories should be separated into individual compilation units.
// TODO(kyessenov) Leverage the host argument marshaller instead of the protobuf argument list.
class DeclarePropertyFactory {
public:
WasmForeignFunction create(std::shared_ptr<DeclarePropertyFactory> self) const {
WasmForeignFunction f = [self](WasmBase&, std::string_view arguments,
const std::function<void*(size_t size)>&) -> WasmResult {
envoy::source::extensions::common::wasm::DeclarePropertyArguments args;
if (args.ParseFromArray(arguments.data(), arguments.size())) {
CelStateType type = CelStateType::Bytes;
switch (args.type()) {
case envoy::source::extensions::common::wasm::WasmType::Bytes:
type = CelStateType::Bytes;
break;
case envoy::source::extensions::common::wasm::WasmType::Protobuf:
type = CelStateType::Protobuf;
break;
case envoy::source::extensions::common::wasm::WasmType::String:
type = CelStateType::String;
break;
case envoy::source::extensions::common::wasm::WasmType::FlatBuffers:
type = CelStateType::FlatBuffers;
break;
default:
// do nothing
break;
}
StreamInfo::FilterState::LifeSpan span = toFilterStateLifeSpan(args.span());
auto context = static_cast<Context*>(proxy_wasm::current_context_);
return context->declareProperty(
args.name(), std::make_unique<const Filters::Common::Expr::CelStatePrototype>(
args.readonly(), type, args.schema(), span));
}
return WasmResult::BadArgument;
};
return f;
}
};
RegisterForeignFunction
registerDeclarePropertyForeignFunction("declare_property",
createFromClass<DeclarePropertyFactory>());
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy