| Index: third_party/cacheinvalidation/src/google/cacheinvalidation/impl/ticl-message-validator.cc
|
| diff --git a/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/ticl-message-validator.cc b/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/ticl-message-validator.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9a602cb8690bdd05de7a96579dba8bdf2a8fb802
|
| --- /dev/null
|
| +++ b/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/ticl-message-validator.cc
|
| @@ -0,0 +1,369 @@
|
| +// Copyright 2012 Google Inc.
|
| +//
|
| +// Licensed under the Apache License, Version 2.0 (the "License");
|
| +// you may not use this file except in compliance with the License.
|
| +// You may obtain a copy of the License at
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +// distributed under the License is distributed on an "AS IS" BASIS,
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// See the License for the specific language governing permissions and
|
| +// limitations under the License.
|
| +
|
| +// Validator for v2 protocol messages.
|
| +
|
| +#include "google/cacheinvalidation/impl/ticl-message-validator.h"
|
| +
|
| +#include "google/cacheinvalidation/impl/log-macro.h"
|
| +#include "google/cacheinvalidation/impl/proto-helpers.h"
|
| +#include "google/cacheinvalidation/include/system-resources.h"
|
| +
|
| +namespace invalidation {
|
| +
|
| +// High-level design: validation works via the collaboration of a set of macros
|
| +// and template method specializations that obey a specific protocol. A
|
| +// validator for a particular type is defined by a specialization of the method:
|
| +//
|
| +// template<typename T>
|
| +// void TiclMessageValidator::Validate(const T& message, bool* result);
|
| +//
|
| +// A macro, DEFINE_VALIDATOR(type) is defined below to help prevent mistakes in
|
| +// these definitions and to improve code readability. For example, to define
|
| +// the validator for the type ObjectIdP, we'd write:
|
| +//
|
| +// DEFINE_VALIDATOR(ObjectIdP) { /* validation constraints ... */ }
|
| +//
|
| +// The choice of the names |message| and |result| is significant, as many of the
|
| +// macros assume that these refer respectively to the message being validated
|
| +// and the address in which the validation result is to be stored.
|
| +//
|
| +// When a validator is called, |*result| is initially |true|. To reject the
|
| +// message, the validator sets |*result| to |false| and returns. Otherwise, it
|
| +// simply allows control flow to continue; if no reason is found to reject the
|
| +// message, control eventually returns to the caller with |*result| still set to
|
| +// |true|, indicating that the message is acceptable. This protocol keeps the
|
| +// bodies of the validation methods clean--otherwise they would all need need to
|
| +// end with explicit |return| statements.
|
| +//
|
| +// A validator typically consists of a collection of constraints, at least one
|
| +// per field in the message. Several macros are defined for common constraints,
|
| +// including:
|
| +//
|
| +// REQUIRE(field): requires that (optional) |field| be present and valid.
|
| +// ALLOW(field): allows (optional) |field| if valid.
|
| +// ZERO_OR_MORE(field): validates each element of the (repeated) |field|.
|
| +// ONE_OR_MORE(field): like ZERO_OR_MORE, but requires at least one element.
|
| +// NON_EMPTY(field): checks that the string |field| is non-empty (if present).
|
| +// NON_NEGATIVE(field): checks that the integral |field| is >= 0 (if present).
|
| +//
|
| +// For custom constraints, the CONDITION(expr) macro allows an arbitrary boolean
|
| +// expression, which will generally refer to |message|.
|
| +//
|
| +// Note that REQUIRE, ALLOW, ZERO_OR_MORE, and ONE_OR_MORE all perform recursive
|
| +// validation of the mentioned fields. A validation method must therefore be
|
| +// defined for the type of the field, or there will be a link-time error.
|
| +
|
| +
|
| +// Macros:
|
| +
|
| +// Macro to define a specialization of the |Validate| method for the given
|
| +// |type|. This must be followed by a method body in curly braces defining
|
| +// constraints on |message|, which is bound to a value of the given type. If
|
| +// |message| is valid, no action is necessary; if invalid, a diagnostic message
|
| +// should be logged via |logger_|, and |*result| should be set to false.
|
| +#define DEFINE_VALIDATOR(type) \
|
| + template<> \
|
| + void TiclMessageValidator::Validate(const type& message, bool* result)
|
| +
|
| +// Expands into a conditional that checks whether |field| is present in
|
| +// |message| and valid.
|
| +#define REQUIRE(field) \
|
| + if (!message.has_##field()) { \
|
| + TLOG(logger_, SEVERE, "required field " #field " missing from %s", \
|
| + ProtoHelpers::ToString(message).c_str()); \
|
| + *result = false; \
|
| + return; \
|
| + } \
|
| + ALLOW(field);
|
| +
|
| +// Expands into a conditional that checks whether |field| is present in
|
| +// |message|. If so, validates |message.field()|; otherwise, does nothing.
|
| +#define ALLOW(field) \
|
| + if (message.has_##field()) { \
|
| + Validate(message.field(), result); \
|
| + if (!*result) { \
|
| + TLOG(logger_, SEVERE, "field " #field " failed validation in %s", \
|
| + ProtoHelpers::ToString(message).c_str()); \
|
| + return; \
|
| + } \
|
| + }
|
| +
|
| +// Expands into a conditional that checks that, if |field| is present in
|
| +// |message|, then it is greater than or equal to |value|.
|
| +#define GREATER_OR_EQUAL(field, value) \
|
| + if (message.has_##field() && (message.field() < value)) { \
|
| + TLOG(logger_, SEVERE, \
|
| + #field " must be greater than or equal to %d; was %d", \
|
| + value, message.field()); \
|
| + *result = false; \
|
| + return; \
|
| + }
|
| +
|
| +// Expands into a conditional that checks that, if the specified numeric |field|
|
| +// is present, that it is non-negative.
|
| +#define NON_NEGATIVE(field) GREATER_OR_EQUAL(field, 0)
|
| +
|
| +// Expands into a conditional that checks that, if the specified string |field|
|
| +// is present, that it is non-empty.
|
| +#define NON_EMPTY(field) \
|
| + if (message.has_##field() && message.field().empty()) { \
|
| + TLOG(logger_, SEVERE, #field " must be non-empty"); \
|
| + *result = false; \
|
| + return; \
|
| + }
|
| +
|
| +// Expands into a loop that checks that all elements of the repeated |field| are
|
| +// valid.
|
| +#define ZERO_OR_MORE(field) \
|
| + for (int i = 0; i < message.field##_size(); ++i) { \
|
| + Validate(message.field(i), result); \
|
| + if (!*result) { \
|
| + TLOG(logger_, SEVERE, "field " #field " #%d failed validation in %s", \
|
| + i, ProtoHelpers::ToString(message).c_str()); \
|
| + *result = false; \
|
| + return; \
|
| + } \
|
| + }
|
| +
|
| +// Expands into a loop that checks that there is at least one element of the
|
| +// repeated |field|, and that all are valid.
|
| +#define ONE_OR_MORE(field) \
|
| + if (message.field##_size() == 0) { \
|
| + TLOG(logger_, SEVERE, "at least one " #field " required in %s", \
|
| + ProtoHelpers::ToString(message).c_str()); \
|
| + *result = false; \
|
| + return; \
|
| + } \
|
| + ZERO_OR_MORE(field)
|
| +
|
| +// Expands into code that checks that the arbitrary condition |expr| is true.
|
| +#define CONDITION(expr) \
|
| + *result = expr; \
|
| + if (!*result) { \
|
| + TLOG(logger_, SEVERE, #expr " not satisfied by %s", \
|
| + ProtoHelpers::ToString(message).c_str()); \
|
| + return; \
|
| + }
|
| +
|
| +
|
| +// Validators:
|
| +
|
| +// No constraints on primitive types by default.
|
| +DEFINE_VALIDATOR(bool) {}
|
| +DEFINE_VALIDATOR(int) {}
|
| +DEFINE_VALIDATOR(int64) {}
|
| +DEFINE_VALIDATOR(string) {}
|
| +
|
| +// Similarly, for now enum values are always considered valid.
|
| +DEFINE_VALIDATOR(ErrorMessage::Code) {}
|
| +DEFINE_VALIDATOR(InfoRequestMessage::InfoType) {}
|
| +DEFINE_VALIDATOR(InitializeMessage::DigestSerializationType) {}
|
| +DEFINE_VALIDATOR(RegistrationP::OpType) {}
|
| +DEFINE_VALIDATOR(StatusP::Code) {}
|
| +
|
| +DEFINE_VALIDATOR(Version) {
|
| + REQUIRE(major_version);
|
| + NON_NEGATIVE(major_version);
|
| + REQUIRE(minor_version);
|
| + NON_NEGATIVE(minor_version);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ProtocolVersion) {
|
| + REQUIRE(version);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ObjectIdP) {
|
| + REQUIRE(name);
|
| + REQUIRE(source);
|
| + NON_NEGATIVE(source);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(InvalidationP) {
|
| + REQUIRE(object_id);
|
| + REQUIRE(is_known_version);
|
| + REQUIRE(version);
|
| + NON_NEGATIVE(version);
|
| + ALLOW(payload);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationP) {
|
| + REQUIRE(object_id);
|
| + REQUIRE(op_type);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationSummary) {
|
| + REQUIRE(num_registrations);
|
| + NON_NEGATIVE(num_registrations);
|
| + REQUIRE(registration_digest);
|
| + NON_EMPTY(registration_digest);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(InvalidationMessage) {
|
| + ONE_OR_MORE(invalidation);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ClientHeader) {
|
| + REQUIRE(protocol_version);
|
| + ALLOW(client_token);
|
| + NON_EMPTY(client_token);
|
| + ALLOW(registration_summary);
|
| + REQUIRE(client_time_ms);
|
| + REQUIRE(max_known_server_time_ms);
|
| + ALLOW(message_id);
|
| + ALLOW(client_type);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ApplicationClientIdP) {
|
| + REQUIRE(client_type);
|
| + REQUIRE(client_name);
|
| + NON_EMPTY(client_name);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(InitializeMessage) {
|
| + REQUIRE(client_type);
|
| + REQUIRE(nonce);
|
| + NON_EMPTY(nonce);
|
| + REQUIRE(digest_serialization_type);
|
| + REQUIRE(application_client_id);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationMessage) {
|
| + ONE_OR_MORE(registration);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ClientVersion) {
|
| + REQUIRE(version);
|
| + REQUIRE(platform);
|
| + REQUIRE(language);
|
| + REQUIRE(application_info);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(PropertyRecord) {
|
| + REQUIRE(name);
|
| + REQUIRE(value);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RateLimitP) {
|
| + REQUIRE(window_ms);
|
| + GREATER_OR_EQUAL(window_ms, 1000);
|
| + CONDITION(message.window_ms() > message.count());
|
| + REQUIRE(count);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ProtocolHandlerConfigP) {
|
| + ALLOW(batching_delay_ms);
|
| + ZERO_OR_MORE(rate_limit);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ClientConfigP) {
|
| + REQUIRE(version);
|
| + ALLOW(network_timeout_delay_ms);
|
| + ALLOW(write_retry_delay_ms);
|
| + ALLOW(heartbeat_interval_ms);
|
| + ALLOW(perf_counter_delay_ms);
|
| + ALLOW(max_exponential_backoff_factor);
|
| + ALLOW(smear_percent);
|
| + ALLOW(is_transient);
|
| + ALLOW(initial_persistent_heartbeat_delay_ms);
|
| + ALLOW(channel_supports_offline_delivery);
|
| + REQUIRE(protocol_handler_config);
|
| + ALLOW(offline_heartbeat_threshold_ms);
|
| + ALLOW(allow_suppression);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(InfoMessage) {
|
| + REQUIRE(client_version);
|
| + ZERO_OR_MORE(config_parameter);
|
| + ZERO_OR_MORE(performance_counter);
|
| + ALLOW(client_config);
|
| + ALLOW(server_registration_summary_requested);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationSubtree) {
|
| + ZERO_OR_MORE(registered_object);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationSyncMessage) {
|
| + ONE_OR_MORE(subtree);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ClientToServerMessage) {
|
| + REQUIRE(header);
|
| + ALLOW(info_message);
|
| + ALLOW(initialize_message);
|
| + ALLOW(invalidation_ack_message);
|
| + ALLOW(registration_message);
|
| + ALLOW(registration_sync_message);
|
| + CONDITION(message.has_initialize_message() ^
|
| + message.header().has_client_token());
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ServerHeader) {
|
| + REQUIRE(protocol_version);
|
| + REQUIRE(client_token);
|
| + NON_EMPTY(client_token);
|
| + ALLOW(registration_summary);
|
| + REQUIRE(server_time_ms);
|
| + NON_NEGATIVE(server_time_ms);
|
| + ALLOW(message_id);
|
| + NON_EMPTY(message_id);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(StatusP) {
|
| + REQUIRE(code);
|
| + ALLOW(description);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(TokenControlMessage) {
|
| + ALLOW(new_token);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ErrorMessage) {
|
| + REQUIRE(code);
|
| + REQUIRE(description);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationStatus) {
|
| + REQUIRE(registration);
|
| + REQUIRE(status);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationStatusMessage) {
|
| + ONE_OR_MORE(registration_status);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(RegistrationSyncRequestMessage) {}
|
| +
|
| +DEFINE_VALIDATOR(InfoRequestMessage) {
|
| + ONE_OR_MORE(info_type);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ConfigChangeMessage) {
|
| + ALLOW(next_message_delay_ms);
|
| + GREATER_OR_EQUAL(next_message_delay_ms, 1);
|
| +}
|
| +
|
| +DEFINE_VALIDATOR(ServerToClientMessage) {
|
| + REQUIRE(header);
|
| + ALLOW(token_control_message);
|
| + ALLOW(invalidation_message);
|
| + ALLOW(registration_status_message);
|
| + ALLOW(registration_sync_request_message);
|
| + ALLOW(config_change_message);
|
| + ALLOW(info_request_message);
|
| + ALLOW(error_message);
|
| +}
|
| +
|
| +} // namespace invalidation
|
|
|