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