/proc/self/cwd/source/extensions/filters/common/lua/wrappers.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/extensions/filters/common/lua/wrappers.h" |
2 | | |
3 | | #include <lua.h> |
4 | | |
5 | | #include <cstdint> |
6 | | |
7 | | #include "source/common/common/assert.h" |
8 | | #include "source/common/common/hex.h" |
9 | | |
10 | | #include "absl/time/time.h" |
11 | | |
12 | | namespace Envoy { |
13 | | namespace Extensions { |
14 | | namespace Filters { |
15 | | namespace Common { |
16 | | namespace Lua { |
17 | | |
18 | | namespace { |
19 | | |
20 | | // Builds a Lua table from a list of strings. |
21 | | template <typename StringList> |
22 | 0 | void createLuaTableFromStringList(lua_State* state, const StringList& list) { |
23 | 0 | lua_createtable(state, list.size(), 0); |
24 | 0 | for (size_t i = 0; i < list.size(); i++) { |
25 | 0 | lua_pushlstring(state, list[i].data(), list[i].size()); |
26 | | // After the list[i].data() is pushed to the stack, we need to set the "current element" with |
27 | | // that value. The lua_rawseti(state, t, i) helps us to set the value of table t with key i. |
28 | | // Given the index of the current element/table in the stack is below the pushed value i.e. -2 |
29 | | // and the key (refers to where the element is in the table) is i + 1 (note that in Lua index |
30 | | // starts from 1), hence we have: |
31 | 0 | lua_rawseti(state, -2, i + 1); |
32 | 0 | } |
33 | 0 | } |
34 | | |
35 | | // By default, LUA_INTEGER is https://en.cppreference.com/w/cpp/types/ptrdiff_t |
36 | | // (https://github.com/LuaJIT/LuaJIT/blob/8271c643c21d1b2f344e339f559f2de6f3663191/src/luaconf.h#L104), |
37 | | // which is large enough to hold timestamp-since-epoch in seconds. Note: In Lua, we usually use |
38 | | // os.time(os.date("!*t")) to get current timestamp-since-epoch in seconds. |
39 | 0 | int64_t timestampInSeconds(const absl::optional<SystemTime>& system_time) { |
40 | 0 | return system_time.has_value() ? std::chrono::duration_cast<std::chrono::seconds>( |
41 | 0 | system_time.value().time_since_epoch()) |
42 | 0 | .count() |
43 | 0 | : 0; |
44 | 0 | } |
45 | | } // namespace |
46 | | |
47 | 0 | int BufferWrapper::luaLength(lua_State* state) { |
48 | 0 | lua_pushnumber(state, data_.length()); |
49 | 0 | return 1; |
50 | 0 | } |
51 | | |
52 | 0 | int BufferWrapper::luaGetBytes(lua_State* state) { |
53 | 0 | const int index = luaL_checkint(state, 2); |
54 | 0 | const int length = luaL_checkint(state, 3); |
55 | 0 | if (index < 0 || length < 0 || |
56 | 0 | static_cast<uint64_t>(index) + static_cast<uint64_t>(length) > data_.length()) { |
57 | 0 | luaL_error(state, "index/length must be >= 0 and (index + length) must be <= buffer size"); |
58 | 0 | } |
59 | | |
60 | | // TODO(mattklein123): Reduce copies here by using Lua direct buffer builds. |
61 | 0 | std::unique_ptr<char[]> data(new char[length]); |
62 | 0 | data_.copyOut(index, length, data.get()); |
63 | 0 | lua_pushlstring(state, data.get(), length); |
64 | 0 | return 1; |
65 | 0 | } |
66 | | |
67 | 0 | int BufferWrapper::luaSetBytes(lua_State* state) { |
68 | 0 | data_.drain(data_.length()); |
69 | 0 | absl::string_view bytes = getStringViewFromLuaString(state, 2); |
70 | 0 | data_.add(bytes); |
71 | 0 | headers_.setContentLength(data_.length()); |
72 | 0 | lua_pushnumber(state, data_.length()); |
73 | 0 | return 1; |
74 | 0 | } |
75 | | |
76 | 0 | void MetadataMapHelper::setValue(lua_State* state, const ProtobufWkt::Value& value) { |
77 | 0 | ProtobufWkt::Value::KindCase kind = value.kind_case(); |
78 | |
|
79 | 0 | switch (kind) { |
80 | 0 | case ProtobufWkt::Value::kNullValue: |
81 | 0 | return lua_pushnil(state); |
82 | 0 | case ProtobufWkt::Value::kNumberValue: |
83 | 0 | return lua_pushnumber(state, value.number_value()); |
84 | 0 | case ProtobufWkt::Value::kBoolValue: |
85 | 0 | return lua_pushboolean(state, value.bool_value()); |
86 | 0 | case ProtobufWkt::Value::kStructValue: |
87 | 0 | return createTable(state, value.struct_value().fields()); |
88 | 0 | case ProtobufWkt::Value::kStringValue: { |
89 | 0 | const auto& string_value = value.string_value(); |
90 | 0 | return lua_pushlstring(state, string_value.data(), string_value.size()); |
91 | 0 | } |
92 | 0 | case ProtobufWkt::Value::kListValue: { |
93 | 0 | const auto& list = value.list_value(); |
94 | 0 | const int values_size = list.values_size(); |
95 | |
|
96 | 0 | lua_createtable(state, values_size, 0); |
97 | 0 | for (int i = 0; i < values_size; i++) { |
98 | | // Here we want to build an array (or a list). Array in lua is just a name for table used in a |
99 | | // specific way. Basically we want to have: 'elements' table. Where elements[i] is an entry |
100 | | // in that table, where key = i and value = list.values[i]. |
101 | | // |
102 | | // Firstly, we need to push the value to the stack. |
103 | 0 | setValue(state, list.values(i)); |
104 | | |
105 | | // Secondly, after the list.value(i) is pushed to the stack, we need to set the 'current |
106 | | // element' with that value. The lua_rawseti(state, t, i) helps us to set the value of table t |
107 | | // with key i. Given the index of the current element/table in the stack is below the pushed |
108 | | // value i.e. -2 and the key (refers to where the element is in the table) is i + 1 (note that |
109 | | // in lua index starts from 1), hence we have: |
110 | 0 | lua_rawseti(state, -2, i + 1); |
111 | 0 | } |
112 | 0 | return; |
113 | 0 | } |
114 | 0 | case ProtobufWkt::Value::KIND_NOT_SET: |
115 | 0 | PANIC("not implemented"); |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | | void MetadataMapHelper::createTable(lua_State* state, |
120 | 0 | const Protobuf::Map<std::string, ProtobufWkt::Value>& fields) { |
121 | 0 | lua_createtable(state, 0, fields.size()); |
122 | 0 | for (const auto& field : fields) { |
123 | 0 | int top = lua_gettop(state); |
124 | 0 | lua_pushlstring(state, field.first.data(), field.first.size()); |
125 | 0 | setValue(state, field.second); |
126 | 0 | lua_settable(state, top); |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | /** |
131 | | * Converts the value on top of the Lua stack into a ProtobufWkt::Value. |
132 | | * Any Lua types that cannot be directly mapped to Value types will |
133 | | * yield an error. |
134 | | */ |
135 | 0 | ProtobufWkt::Value MetadataMapHelper::loadValue(lua_State* state) { |
136 | 0 | ProtobufWkt::Value value; |
137 | 0 | int type = lua_type(state, -1); |
138 | |
|
139 | 0 | switch (type) { |
140 | 0 | case LUA_TNIL: |
141 | 0 | value.set_null_value(ProtobufWkt::NullValue()); |
142 | 0 | break; |
143 | 0 | case LUA_TNUMBER: |
144 | 0 | value.set_number_value(static_cast<double>(lua_tonumber(state, -1))); |
145 | 0 | break; |
146 | 0 | case LUA_TBOOLEAN: |
147 | 0 | value.set_bool_value(lua_toboolean(state, -1) != 0); |
148 | 0 | break; |
149 | 0 | case LUA_TTABLE: { |
150 | 0 | int length = MetadataMapHelper::tableLength(state); |
151 | 0 | if (length > 0) { |
152 | 0 | *value.mutable_list_value() = MetadataMapHelper::loadList(state, length); |
153 | 0 | } else { |
154 | 0 | *value.mutable_struct_value() = MetadataMapHelper::loadStruct(state); |
155 | 0 | } |
156 | 0 | break; |
157 | 0 | } |
158 | 0 | case LUA_TSTRING: |
159 | 0 | value.set_string_value(lua_tostring(state, -1)); |
160 | 0 | break; |
161 | 0 | default: |
162 | 0 | luaL_error(state, "unexpected type '%s' in dynamicMetadata", lua_typename(state, type)); |
163 | 0 | } |
164 | | |
165 | 0 | return value; |
166 | 0 | } |
167 | | |
168 | | /** |
169 | | * Returns the length of a Lua table if it's actually shaped like a List, |
170 | | * i.e. if all the keys are consecutive number values. Otherwise, returns -1. |
171 | | */ |
172 | 0 | int MetadataMapHelper::tableLength(lua_State* state) { |
173 | 0 | double max = 0; |
174 | |
|
175 | 0 | lua_pushnil(state); |
176 | 0 | while (lua_next(state, -2) != 0) { |
177 | 0 | if (lua_type(state, -2) == LUA_TNUMBER) { |
178 | 0 | double k = lua_tonumber(state, -2); |
179 | 0 | if (floor(k) == k && k >= 1) { |
180 | 0 | if (k > max) { |
181 | 0 | max = k; |
182 | 0 | } |
183 | 0 | lua_pop(state, 1); |
184 | 0 | continue; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | lua_pop(state, 2); |
188 | 0 | return -1; |
189 | 0 | } |
190 | 0 | return static_cast<int>(max); |
191 | 0 | } |
192 | | |
193 | 0 | ProtobufWkt::ListValue MetadataMapHelper::loadList(lua_State* state, int length) { |
194 | 0 | ProtobufWkt::ListValue list; |
195 | |
|
196 | 0 | for (int i = 1; i <= length; i++) { |
197 | 0 | lua_rawgeti(state, -1, i); |
198 | 0 | *list.add_values() = MetadataMapHelper::loadValue(state); |
199 | 0 | lua_pop(state, 1); |
200 | 0 | } |
201 | |
|
202 | 0 | return list; |
203 | 0 | } |
204 | | |
205 | 0 | ProtobufWkt::Struct MetadataMapHelper::loadStruct(lua_State* state) { |
206 | 0 | ProtobufWkt::Struct struct_obj; |
207 | |
|
208 | 0 | lua_pushnil(state); |
209 | 0 | while (lua_next(state, -2) != 0) { |
210 | 0 | int key_type = lua_type(state, -2); |
211 | 0 | if (key_type != LUA_TSTRING) { |
212 | 0 | luaL_error(state, "unexpected type %s in table key (only string keys are supported)", |
213 | 0 | lua_typename(state, key_type)); |
214 | 0 | } |
215 | 0 | const char* key = lua_tostring(state, -2); |
216 | 0 | (*struct_obj.mutable_fields())[key] = MetadataMapHelper::loadValue(state); |
217 | 0 | lua_pop(state, 1); |
218 | 0 | } |
219 | |
|
220 | 0 | return struct_obj; |
221 | 0 | } |
222 | | |
223 | | MetadataMapIterator::MetadataMapIterator(MetadataMapWrapper& parent) |
224 | 0 | : parent_{parent}, current_{parent.metadata_.fields().begin()} {} |
225 | | |
226 | 0 | int MetadataMapIterator::luaPairsIterator(lua_State* state) { |
227 | 0 | if (current_ == parent_.metadata_.fields().end()) { |
228 | 0 | parent_.iterator_.reset(); |
229 | 0 | return 0; |
230 | 0 | } |
231 | | |
232 | 0 | lua_pushlstring(state, current_->first.data(), current_->first.size()); |
233 | 0 | MetadataMapHelper::setValue(state, current_->second); |
234 | |
|
235 | 0 | current_++; |
236 | 0 | return 2; |
237 | 0 | } |
238 | | |
239 | 0 | int MetadataMapWrapper::luaGet(lua_State* state) { |
240 | 0 | const char* key = luaL_checkstring(state, 2); |
241 | 0 | const auto filter_it = metadata_.fields().find(key); |
242 | 0 | if (filter_it == metadata_.fields().end()) { |
243 | 0 | return 0; |
244 | 0 | } |
245 | | |
246 | 0 | MetadataMapHelper::setValue(state, filter_it->second); |
247 | 0 | return 1; |
248 | 0 | } |
249 | | |
250 | 0 | int MetadataMapWrapper::luaPairs(lua_State* state) { |
251 | 0 | if (iterator_.get() != nullptr) { |
252 | 0 | luaL_error(state, "cannot create a second iterator before completing the first"); |
253 | 0 | } |
254 | |
|
255 | 0 | iterator_.reset(MetadataMapIterator::create(state, *this), true); |
256 | 0 | lua_pushcclosure(state, MetadataMapIterator::static_luaPairsIterator, 1); |
257 | 0 | return 1; |
258 | 0 | } |
259 | | |
260 | 0 | int SslConnectionWrapper::luaPeerCertificatePresented(lua_State* state) { |
261 | 0 | lua_pushboolean(state, connection_info_.peerCertificatePresented()); |
262 | 0 | return 1; |
263 | 0 | } |
264 | | |
265 | 0 | int SslConnectionWrapper::luaPeerCertificateValidated(lua_State* state) { |
266 | 0 | lua_pushboolean(state, connection_info_.peerCertificateValidated()); |
267 | 0 | return 1; |
268 | 0 | } |
269 | | |
270 | 0 | int SslConnectionWrapper::luaUriSanLocalCertificate(lua_State* state) { |
271 | 0 | createLuaTableFromStringList(state, connection_info_.uriSanLocalCertificate()); |
272 | 0 | return 1; |
273 | 0 | } |
274 | | |
275 | 0 | int SslConnectionWrapper::luaSha256PeerCertificateDigest(lua_State* state) { |
276 | 0 | const std::string& cert_digest = connection_info_.sha256PeerCertificateDigest(); |
277 | 0 | lua_pushlstring(state, cert_digest.data(), cert_digest.size()); |
278 | 0 | return 1; |
279 | 0 | } |
280 | | |
281 | 0 | int SslConnectionWrapper::luaSerialNumberPeerCertificate(lua_State* state) { |
282 | 0 | const std::string& peer_cert = connection_info_.serialNumberPeerCertificate(); |
283 | 0 | lua_pushlstring(state, peer_cert.data(), peer_cert.size()); |
284 | 0 | return 1; |
285 | 0 | } |
286 | | |
287 | 0 | int SslConnectionWrapper::luaIssuerPeerCertificate(lua_State* state) { |
288 | 0 | const std::string& peer_cert_serial = connection_info_.issuerPeerCertificate(); |
289 | 0 | lua_pushlstring(state, peer_cert_serial.data(), peer_cert_serial.size()); |
290 | 0 | return 1; |
291 | 0 | } |
292 | | |
293 | 0 | int SslConnectionWrapper::luaSubjectPeerCertificate(lua_State* state) { |
294 | 0 | const std::string& peer_cert_subject = connection_info_.subjectPeerCertificate(); |
295 | 0 | lua_pushlstring(state, peer_cert_subject.data(), peer_cert_subject.size()); |
296 | 0 | return 1; |
297 | 0 | } |
298 | | |
299 | 0 | int SslConnectionWrapper::luaUriSanPeerCertificate(lua_State* state) { |
300 | 0 | createLuaTableFromStringList(state, connection_info_.uriSanPeerCertificate()); |
301 | 0 | return 1; |
302 | 0 | } |
303 | | |
304 | 0 | int SslConnectionWrapper::luaSubjectLocalCertificate(lua_State* state) { |
305 | 0 | const std::string& subject_local_cert = connection_info_.subjectLocalCertificate(); |
306 | 0 | lua_pushlstring(state, subject_local_cert.data(), subject_local_cert.size()); |
307 | 0 | return 1; |
308 | 0 | } |
309 | | |
310 | 0 | int SslConnectionWrapper::luaDnsSansPeerCertificate(lua_State* state) { |
311 | 0 | createLuaTableFromStringList(state, connection_info_.dnsSansPeerCertificate()); |
312 | 0 | return 1; |
313 | 0 | } |
314 | | |
315 | 0 | int SslConnectionWrapper::luaDnsSansLocalCertificate(lua_State* state) { |
316 | 0 | createLuaTableFromStringList(state, connection_info_.dnsSansLocalCertificate()); |
317 | 0 | return 1; |
318 | 0 | } |
319 | | |
320 | 0 | int SslConnectionWrapper::luaValidFromPeerCertificate(lua_State* state) { |
321 | 0 | lua_pushinteger(state, timestampInSeconds(connection_info_.validFromPeerCertificate())); |
322 | 0 | return 1; |
323 | 0 | } |
324 | | |
325 | 0 | int SslConnectionWrapper::luaExpirationPeerCertificate(lua_State* state) { |
326 | 0 | lua_pushinteger(state, timestampInSeconds(connection_info_.expirationPeerCertificate())); |
327 | 0 | return 1; |
328 | 0 | } |
329 | | |
330 | 0 | int SslConnectionWrapper::luaSessionId(lua_State* state) { |
331 | 0 | const std::string& session_id = connection_info_.sessionId(); |
332 | 0 | lua_pushlstring(state, session_id.data(), session_id.size()); |
333 | 0 | return 1; |
334 | 0 | } |
335 | | |
336 | 0 | int SslConnectionWrapper::luaCiphersuiteId(lua_State* state) { |
337 | 0 | const std::string& cipher_suite_id = |
338 | 0 | absl::StrCat("0x", Hex::uint16ToHex(connection_info_.ciphersuiteId())); |
339 | 0 | lua_pushlstring(state, cipher_suite_id.data(), cipher_suite_id.size()); |
340 | 0 | return 1; |
341 | 0 | } |
342 | | |
343 | 0 | int SslConnectionWrapper::luaCiphersuiteString(lua_State* state) { |
344 | 0 | const std::string& cipher_suite = connection_info_.ciphersuiteString(); |
345 | 0 | lua_pushlstring(state, cipher_suite.data(), cipher_suite.size()); |
346 | 0 | return 1; |
347 | 0 | } |
348 | | |
349 | 0 | int SslConnectionWrapper::luaUrlEncodedPemEncodedPeerCertificate(lua_State* state) { |
350 | 0 | const std::string& peer_cert_pem = connection_info_.urlEncodedPemEncodedPeerCertificate(); |
351 | 0 | lua_pushlstring(state, peer_cert_pem.data(), peer_cert_pem.size()); |
352 | 0 | return 1; |
353 | 0 | } |
354 | | |
355 | 0 | int SslConnectionWrapper::luaUrlEncodedPemEncodedPeerCertificateChain(lua_State* state) { |
356 | 0 | const std::string& peer_cert_chain_pem = |
357 | 0 | connection_info_.urlEncodedPemEncodedPeerCertificateChain(); |
358 | 0 | lua_pushlstring(state, peer_cert_chain_pem.data(), peer_cert_chain_pem.size()); |
359 | 0 | return 1; |
360 | 0 | } |
361 | | |
362 | 0 | int SslConnectionWrapper::luaTlsVersion(lua_State* state) { |
363 | 0 | const std::string& tls_version = connection_info_.tlsVersion(); |
364 | 0 | lua_pushlstring(state, tls_version.data(), tls_version.size()); |
365 | 0 | return 1; |
366 | 0 | } |
367 | | |
368 | 0 | int ConnectionWrapper::luaSsl(lua_State* state) { |
369 | 0 | const auto& ssl = connection_->ssl(); |
370 | 0 | if (ssl != nullptr) { |
371 | 0 | if (ssl_connection_wrapper_.get() != nullptr) { |
372 | 0 | ssl_connection_wrapper_.pushStack(); |
373 | 0 | } else { |
374 | 0 | ssl_connection_wrapper_.reset(SslConnectionWrapper::create(state, *ssl), true); |
375 | 0 | } |
376 | 0 | } else { |
377 | 0 | lua_pushnil(state); |
378 | 0 | } |
379 | 0 | return 1; |
380 | 0 | } |
381 | | |
382 | | } // namespace Lua |
383 | | } // namespace Common |
384 | | } // namespace Filters |
385 | | } // namespace Extensions |
386 | | } // namespace Envoy |