1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2019 Couchbase, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 #include "mcbp_test.h"
18
19 #include <mcbp/protocol/framebuilder.h>
20
21 using cb::mcbp::ClientOpcode;
22 using cb::mcbp::Status;
23 using cb::mcbp::request::FrameInfoId;
24
25 class FrameExtrasValidatorTests : public mcbp::test::ValidatorTest {
26 public:
FrameExtrasValidatorTests()27 FrameExtrasValidatorTests()
28 : ValidatorTest(false), builder({blob, sizeof(blob)}) {
29 }
30
31 void SetUp() override {
32 ValidatorTest::SetUp();
33 builder.setMagic(cb::mcbp::Magic::AltClientRequest);
34 builder.setOpcode(ClientOpcode::Set);
35 cb::mcbp::request::MutationPayload ext;
36 builder.setExtras(ext.getBuffer());
37 builder.setKey("foo");
38 }
39
40 protected:
encodeFrameInfo(FrameInfoId id, cb::const_byte_buffer payload)41 std::vector<uint8_t> encodeFrameInfo(FrameInfoId id,
42 cb::const_byte_buffer payload) {
43 std::vector<uint8_t> result;
44
45 auto idbits = static_cast<uint16_t>(id);
46 if (idbits < 0x0f) {
47 result.emplace_back(uint8_t(idbits << 4u));
48 } else {
49 result.emplace_back(0xf0);
50 result.emplace_back(uint8_t(idbits - 0x0f));
51 }
52
53 if (payload.size() < 0x0f) {
54 result[0] |= uint8_t(payload.size());
55 } else {
56 result[0] |= 0x0fu;
57 result.emplace_back(uint8_t(payload.size() - 0x0f));
58 }
59
60 std::copy(payload.begin(), payload.end(), std::back_inserter(result));
61 return result;
62 }
63
64 protected:
65 cb::mcbp::RequestBuilder builder;
66 };
67
TEST_F(FrameExtrasValidatorTests, Barrier)68 TEST_F(FrameExtrasValidatorTests, Barrier) {
69 auto fe = encodeFrameInfo(FrameInfoId::Barrier, {});
70 builder.setFramingExtras({fe.data(), fe.size()});
71 EXPECT_EQ(Status::Success, validate(ClientOpcode::Set, blob));
72 }
73
TEST_F(FrameExtrasValidatorTests, BarrierInvalidSize)74 TEST_F(FrameExtrasValidatorTests, BarrierInvalidSize) {
75 auto fe = encodeFrameInfo(FrameInfoId::Barrier, {blob, 1});
76 builder.setFramingExtras({fe.data(), fe.size()});
77 EXPECT_EQ("Barrier should not contain value",
78 validate_error_context(ClientOpcode::Set, blob, Status::Einval));
79 }
80
TEST_F(FrameExtrasValidatorTests, DurabilityRequirement)81 TEST_F(FrameExtrasValidatorTests, DurabilityRequirement) {
82 uint8_t level = 1;
83 auto fe = encodeFrameInfo(FrameInfoId::DurabilityRequirement, {&level, 1});
84 builder.setFramingExtras({fe.data(), fe.size()});
85 EXPECT_EQ(Status::Success, validate(ClientOpcode::Set, blob));
86 }
87
TEST_F(FrameExtrasValidatorTests, DurabilityRequirementInvalidLevel)88 TEST_F(FrameExtrasValidatorTests, DurabilityRequirementInvalidLevel) {
89 uint8_t level = 0;
90 auto fe = encodeFrameInfo(FrameInfoId::DurabilityRequirement, {&level, 1});
91 builder.setFramingExtras({fe.data(), fe.size()});
92 EXPECT_EQ(Status::DurabilityInvalidLevel,
93 validate(ClientOpcode::Set, blob));
94 }
95
TEST_F(FrameExtrasValidatorTests, DurabilityRequirementInvalidCommand)96 TEST_F(FrameExtrasValidatorTests, DurabilityRequirementInvalidCommand) {
97 uint8_t level = 1;
98 auto fe = encodeFrameInfo(FrameInfoId::DurabilityRequirement, {&level, 1});
99 builder.setFramingExtras({fe.data(), fe.size()});
100 builder.setOpcode(ClientOpcode::Get);
101 builder.setExtras({});
102 EXPECT_EQ("The requested command does not support durability requirements",
103 validate_error_context(ClientOpcode::Get, blob, Status::Einval));
104 }
105
TEST_F(FrameExtrasValidatorTests, DurabilityRequirementInvalidSize)106 TEST_F(FrameExtrasValidatorTests, DurabilityRequirementInvalidSize) {
107 uint8_t level[10] = {1, 0, 1};
108 auto fe = encodeFrameInfo(FrameInfoId::DurabilityRequirement, {level, 2});
109 builder.setFramingExtras({fe.data(), fe.size()});
110 EXPECT_EQ("Invalid sized buffer provided: 2",
111 validate_error_context(ClientOpcode::Set, blob, Status::Einval));
112
113 // size 3 == level + timeout
114 fe = encodeFrameInfo(FrameInfoId::DurabilityRequirement, {level, 3});
115 builder.setFramingExtras({fe.data(), fe.size()});
116 EXPECT_EQ(Status::Success, validate(ClientOpcode::Set, blob));
117
118 // size 4 invalid
119 fe = encodeFrameInfo(FrameInfoId::DurabilityRequirement, {level, 4});
120 builder.setFramingExtras({fe.data(), fe.size()});
121 EXPECT_EQ("Invalid sized buffer provided: 4",
122 validate_error_context(ClientOpcode::Set, blob, Status::Einval));
123 }
124
TEST_F(FrameExtrasValidatorTests, DcpStreamId)125 TEST_F(FrameExtrasValidatorTests, DcpStreamId) {
126 uint16_t id = 0;
127
128 auto fe = encodeFrameInfo(
129 FrameInfoId::DcpStreamId,
130 {reinterpret_cast<const uint8_t*>(&id), sizeof(id)});
131 builder.setFramingExtras({fe.data(), fe.size()});
132 EXPECT_EQ(Status::Success, validate(ClientOpcode::Set, blob));
133 }
134
TEST_F(FrameExtrasValidatorTests, DcpStreamIdInvalidSize)135 TEST_F(FrameExtrasValidatorTests, DcpStreamIdInvalidSize) {
136 uint32_t id = 0;
137 auto fe = encodeFrameInfo(
138 FrameInfoId::DcpStreamId,
139 {reinterpret_cast<const uint8_t*>(&id), sizeof(id)});
140 builder.setFramingExtras({fe.data(), fe.size()});
141 EXPECT_EQ("DcpStreamId invalid size:4",
142 validate_error_context(ClientOpcode::Set, blob, Status::Einval));
143 }
144
TEST_F(FrameExtrasValidatorTests, OpenTracingContext)145 TEST_F(FrameExtrasValidatorTests, OpenTracingContext) {
146 const std::string context{"context"};
147
148 auto fe = encodeFrameInfo(
149 FrameInfoId::OpenTracingContext,
150 {reinterpret_cast<const uint8_t*>(&context), context.size()});
151 builder.setFramingExtras({fe.data(), fe.size()});
152 EXPECT_EQ(Status::Success, validate(ClientOpcode::Set, blob));
153 }
154
TEST_F(FrameExtrasValidatorTests, OpenTracingContextInvalidSize)155 TEST_F(FrameExtrasValidatorTests, OpenTracingContextInvalidSize) {
156 auto fe = encodeFrameInfo(FrameInfoId::OpenTracingContext, {});
157 builder.setFramingExtras({fe.data(), fe.size()});
158 EXPECT_EQ("OpenTracingContext cannot be empty",
159 validate_error_context(ClientOpcode::Set, blob, Status::Einval));
160 }
161
TEST_F(FrameExtrasValidatorTests, UnknownFrameId)162 TEST_F(FrameExtrasValidatorTests, UnknownFrameId) {
163 auto fe = encodeFrameInfo(FrameInfoId(0xff), {});
164 builder.setFramingExtras({fe.data(), fe.size()});
165 EXPECT_EQ(Status::UnknownFrameInfo, validate(ClientOpcode::Set, blob));
166 }
167
TEST_F(FrameExtrasValidatorTests, BufferOverflow)168 TEST_F(FrameExtrasValidatorTests, BufferOverflow) {
169 std::vector<uint8_t> fe;
170 fe.push_back(0x11); // Id 1, size 1 (but not present)
171 builder.setFramingExtras({fe.data(), fe.size()});
172 EXPECT_EQ("Invalid encoding in FrameExtras",
173 validate_error_context(ClientOpcode::Set, blob, Status::Einval));
174 }
175