9b7d728fc0fd92b1dbc0d5f7e91677d90186521b
[m6w6/libmemcached] / src / libmemcachedprotocol / pedantic.c
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcachedprotocol/common.h"
17
18 #include <sys/types.h>
19
20 #define ensure(a) \
21 if (!(a)) { \
22 return false; \
23 }
24
25 bool memcached_binary_protocol_pedantic_check_request(
26 const protocol_binary_request_header *request) {
27 ensure(request->request.magic == PROTOCOL_BINARY_REQ);
28 ensure(request->request.datatype == PROTOCOL_BINARY_RAW_BYTES);
29
30 ensure(request->bytes[6] == 0);
31 ensure(request->bytes[7] == 0);
32
33 uint8_t opcode = request->request.opcode;
34 uint16_t keylen = ntohs(request->request.keylen);
35 uint8_t extlen = request->request.extlen;
36 uint32_t bodylen = ntohl(request->request.bodylen);
37
38 ensure(bodylen >= (keylen + extlen));
39
40 switch (opcode) {
41 case PROTOCOL_BINARY_CMD_GET:
42 case PROTOCOL_BINARY_CMD_GETK:
43 case PROTOCOL_BINARY_CMD_GETKQ:
44 case PROTOCOL_BINARY_CMD_GETQ:
45 ensure(extlen == 0);
46 ensure(keylen > 0);
47 ensure(keylen == bodylen);
48 ensure(request->request.cas == 0);
49 break;
50
51 case PROTOCOL_BINARY_CMD_ADD:
52 case PROTOCOL_BINARY_CMD_ADDQ:
53 /* it makes no sense to run add with a cas value */
54 ensure(request->request.cas == 0);
55 /* FALLTHROUGH */
56 case PROTOCOL_BINARY_CMD_SET:
57 case PROTOCOL_BINARY_CMD_SETQ:
58 case PROTOCOL_BINARY_CMD_REPLACE:
59 case PROTOCOL_BINARY_CMD_REPLACEQ:
60 ensure(keylen > 0);
61 ensure(extlen == 8);
62 break;
63
64 case PROTOCOL_BINARY_CMD_DELETE:
65 case PROTOCOL_BINARY_CMD_DELETEQ:
66 ensure(extlen == 0);
67 ensure(keylen > 0);
68 ensure(keylen == bodylen);
69 break;
70
71 case PROTOCOL_BINARY_CMD_INCREMENT:
72 case PROTOCOL_BINARY_CMD_INCREMENTQ:
73 case PROTOCOL_BINARY_CMD_DECREMENT:
74 case PROTOCOL_BINARY_CMD_DECREMENTQ:
75 ensure(extlen == 20);
76 ensure(keylen > 0);
77 ensure(keylen + extlen == bodylen);
78 break;
79
80 case PROTOCOL_BINARY_CMD_QUIT:
81 case PROTOCOL_BINARY_CMD_QUITQ:
82 case PROTOCOL_BINARY_CMD_NOOP:
83 case PROTOCOL_BINARY_CMD_VERSION:
84 ensure(extlen == 0);
85 ensure(keylen == 0);
86 ensure(bodylen == 0);
87 break;
88
89 case PROTOCOL_BINARY_CMD_FLUSH:
90 case PROTOCOL_BINARY_CMD_FLUSHQ:
91 ensure(extlen == 0 || extlen == 4);
92 ensure(keylen == 0);
93 ensure(bodylen == extlen);
94 break;
95
96 case PROTOCOL_BINARY_CMD_STAT:
97 ensure(extlen == 0);
98 /* May have key, but not value */
99 ensure(keylen == bodylen);
100 break;
101
102 case PROTOCOL_BINARY_CMD_APPEND:
103 case PROTOCOL_BINARY_CMD_APPENDQ:
104 case PROTOCOL_BINARY_CMD_PREPEND:
105 case PROTOCOL_BINARY_CMD_PREPENDQ:
106 ensure(extlen == 0);
107 ensure(keylen > 0);
108 break;
109 default
110 :
111 /* Unknown command */
112 ;
113 }
114
115 return true;
116 }
117
118 bool memcached_binary_protocol_pedantic_check_response(
119 const protocol_binary_request_header *request,
120 const protocol_binary_response_header *response) {
121 ensure(response->response.magic == PROTOCOL_BINARY_RES);
122 ensure(response->response.datatype == PROTOCOL_BINARY_RAW_BYTES);
123 ensure(response->response.opaque == request->request.opaque);
124
125 uint16_t status = ntohs(response->response.status);
126 uint8_t opcode = response->response.opcode;
127
128 if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
129 switch (opcode) {
130 case PROTOCOL_BINARY_CMD_ADDQ:
131 case PROTOCOL_BINARY_CMD_APPENDQ:
132 case PROTOCOL_BINARY_CMD_DECREMENTQ:
133 case PROTOCOL_BINARY_CMD_DELETEQ:
134 case PROTOCOL_BINARY_CMD_FLUSHQ:
135 case PROTOCOL_BINARY_CMD_INCREMENTQ:
136 case PROTOCOL_BINARY_CMD_PREPENDQ:
137 case PROTOCOL_BINARY_CMD_QUITQ:
138 case PROTOCOL_BINARY_CMD_REPLACEQ:
139 case PROTOCOL_BINARY_CMD_SETQ:
140 /* Quiet command shouldn't return on success */
141 return false;
142 default:
143 break;
144 }
145
146 switch (opcode) {
147 case PROTOCOL_BINARY_CMD_ADD:
148 case PROTOCOL_BINARY_CMD_REPLACE:
149 case PROTOCOL_BINARY_CMD_SET:
150 case PROTOCOL_BINARY_CMD_APPEND:
151 case PROTOCOL_BINARY_CMD_PREPEND:
152 ensure(response->response.keylen == 0);
153 ensure(response->response.extlen == 0);
154 ensure(response->response.bodylen == 0);
155 ensure(response->response.cas);
156 break;
157 case PROTOCOL_BINARY_CMD_FLUSH:
158 case PROTOCOL_BINARY_CMD_NOOP:
159 case PROTOCOL_BINARY_CMD_QUIT:
160 case PROTOCOL_BINARY_CMD_DELETE:
161 ensure(response->response.keylen == 0);
162 ensure(response->response.extlen == 0);
163 ensure(response->response.bodylen == 0);
164 ensure(response->response.cas == 0);
165 break;
166
167 case PROTOCOL_BINARY_CMD_DECREMENT:
168 case PROTOCOL_BINARY_CMD_INCREMENT:
169 ensure(response->response.keylen == 0);
170 ensure(response->response.extlen == 0);
171 ensure(ntohl(response->response.bodylen) == 8);
172 ensure(response->response.cas);
173 break;
174
175 case PROTOCOL_BINARY_CMD_STAT:
176 ensure(response->response.extlen == 0);
177 /* key and value exists in all packets except in the terminating */
178 ensure(response->response.cas == 0);
179 break;
180
181 case PROTOCOL_BINARY_CMD_VERSION:
182 ensure(response->response.keylen == 0);
183 ensure(response->response.extlen == 0);
184 ensure(response->response.bodylen);
185 ensure(response->response.cas == 0);
186 break;
187
188 case PROTOCOL_BINARY_CMD_GET:
189 case PROTOCOL_BINARY_CMD_GETQ:
190 ensure(response->response.keylen == 0);
191 ensure(response->response.extlen == 4);
192 ensure(response->response.cas);
193 break;
194
195 case PROTOCOL_BINARY_CMD_GETK:
196 case PROTOCOL_BINARY_CMD_GETKQ:
197 ensure(response->response.keylen);
198 ensure(response->response.extlen == 4);
199 ensure(response->response.cas);
200 break;
201
202 default:
203 /* Undefined command code */
204 break;
205 }
206 } else {
207 ensure(response->response.cas == 0);
208 ensure(response->response.extlen == 0);
209 if (opcode != PROTOCOL_BINARY_CMD_GETK) {
210 ensure(response->response.keylen == 0);
211 }
212 }
213
214 return true;
215 }