p9y: bsd
[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 #include "p9y/socket.hpp"
18
19 #define ensure(a) \
20 if (!(a)) { \
21 return false; \
22 }
23
24 bool memcached_binary_protocol_pedantic_check_request(
25 const protocol_binary_request_header *request) {
26 ensure(request->request.magic == PROTOCOL_BINARY_REQ);
27 ensure(request->request.datatype == PROTOCOL_BINARY_RAW_BYTES);
28
29 ensure(request->bytes[6] == 0);
30 ensure(request->bytes[7] == 0);
31
32 uint8_t opcode = request->request.opcode;
33 uint16_t keylen = ntohs(request->request.keylen);
34 uint8_t extlen = request->request.extlen;
35 uint32_t bodylen = ntohl(request->request.bodylen);
36
37 ensure(bodylen >= (keylen + extlen));
38
39 switch (opcode) {
40 case PROTOCOL_BINARY_CMD_GET:
41 case PROTOCOL_BINARY_CMD_GETK:
42 case PROTOCOL_BINARY_CMD_GETKQ:
43 case PROTOCOL_BINARY_CMD_GETQ:
44 ensure(extlen == 0);
45 ensure(keylen > 0);
46 ensure(keylen == bodylen);
47 ensure(request->request.cas == 0);
48 break;
49
50 case PROTOCOL_BINARY_CMD_ADD:
51 case PROTOCOL_BINARY_CMD_ADDQ:
52 /* it makes no sense to run add with a cas value */
53 ensure(request->request.cas == 0);
54 /* FALLTHROUGH */
55 case PROTOCOL_BINARY_CMD_SET:
56 case PROTOCOL_BINARY_CMD_SETQ:
57 case PROTOCOL_BINARY_CMD_REPLACE:
58 case PROTOCOL_BINARY_CMD_REPLACEQ:
59 ensure(keylen > 0);
60 ensure(extlen == 8);
61 break;
62
63 case PROTOCOL_BINARY_CMD_DELETE:
64 case PROTOCOL_BINARY_CMD_DELETEQ:
65 ensure(extlen == 0);
66 ensure(keylen > 0);
67 ensure(keylen == bodylen);
68 break;
69
70 case PROTOCOL_BINARY_CMD_INCREMENT:
71 case PROTOCOL_BINARY_CMD_INCREMENTQ:
72 case PROTOCOL_BINARY_CMD_DECREMENT:
73 case PROTOCOL_BINARY_CMD_DECREMENTQ:
74 ensure(extlen == 20);
75 ensure(keylen > 0);
76 ensure(keylen + extlen == bodylen);
77 break;
78
79 case PROTOCOL_BINARY_CMD_QUIT:
80 case PROTOCOL_BINARY_CMD_QUITQ:
81 case PROTOCOL_BINARY_CMD_NOOP:
82 case PROTOCOL_BINARY_CMD_VERSION:
83 ensure(extlen == 0);
84 ensure(keylen == 0);
85 ensure(bodylen == 0);
86 break;
87
88 case PROTOCOL_BINARY_CMD_FLUSH:
89 case PROTOCOL_BINARY_CMD_FLUSHQ:
90 ensure(extlen == 0 || extlen == 4);
91 ensure(keylen == 0);
92 ensure(bodylen == extlen);
93 break;
94
95 case PROTOCOL_BINARY_CMD_STAT:
96 ensure(extlen == 0);
97 /* May have key, but not value */
98 ensure(keylen == bodylen);
99 break;
100
101 case PROTOCOL_BINARY_CMD_APPEND:
102 case PROTOCOL_BINARY_CMD_APPENDQ:
103 case PROTOCOL_BINARY_CMD_PREPEND:
104 case PROTOCOL_BINARY_CMD_PREPENDQ:
105 ensure(extlen == 0);
106 ensure(keylen > 0);
107 break;
108 default
109 :
110 /* Unknown command */
111 ;
112 }
113
114 return true;
115 }
116
117 bool memcached_binary_protocol_pedantic_check_response(
118 const protocol_binary_request_header *request,
119 const protocol_binary_response_header *response) {
120 ensure(response->response.magic == PROTOCOL_BINARY_RES);
121 ensure(response->response.datatype == PROTOCOL_BINARY_RAW_BYTES);
122 ensure(response->response.opaque == request->request.opaque);
123
124 uint16_t status = ntohs(response->response.status);
125 uint8_t opcode = response->response.opcode;
126
127 if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
128 switch (opcode) {
129 case PROTOCOL_BINARY_CMD_ADDQ:
130 case PROTOCOL_BINARY_CMD_APPENDQ:
131 case PROTOCOL_BINARY_CMD_DECREMENTQ:
132 case PROTOCOL_BINARY_CMD_DELETEQ:
133 case PROTOCOL_BINARY_CMD_FLUSHQ:
134 case PROTOCOL_BINARY_CMD_INCREMENTQ:
135 case PROTOCOL_BINARY_CMD_PREPENDQ:
136 case PROTOCOL_BINARY_CMD_QUITQ:
137 case PROTOCOL_BINARY_CMD_REPLACEQ:
138 case PROTOCOL_BINARY_CMD_SETQ:
139 /* Quiet command shouldn't return on success */
140 return false;
141 default:
142 break;
143 }
144
145 switch (opcode) {
146 case PROTOCOL_BINARY_CMD_ADD:
147 case PROTOCOL_BINARY_CMD_REPLACE:
148 case PROTOCOL_BINARY_CMD_SET:
149 case PROTOCOL_BINARY_CMD_APPEND:
150 case PROTOCOL_BINARY_CMD_PREPEND:
151 ensure(response->response.keylen == 0);
152 ensure(response->response.extlen == 0);
153 ensure(response->response.bodylen == 0);
154 ensure(response->response.cas);
155 break;
156 case PROTOCOL_BINARY_CMD_FLUSH:
157 case PROTOCOL_BINARY_CMD_NOOP:
158 case PROTOCOL_BINARY_CMD_QUIT:
159 case PROTOCOL_BINARY_CMD_DELETE:
160 ensure(response->response.keylen == 0);
161 ensure(response->response.extlen == 0);
162 ensure(response->response.bodylen == 0);
163 ensure(response->response.cas == 0);
164 break;
165
166 case PROTOCOL_BINARY_CMD_DECREMENT:
167 case PROTOCOL_BINARY_CMD_INCREMENT:
168 ensure(response->response.keylen == 0);
169 ensure(response->response.extlen == 0);
170 ensure(ntohl(response->response.bodylen) == 8);
171 ensure(response->response.cas);
172 break;
173
174 case PROTOCOL_BINARY_CMD_STAT:
175 ensure(response->response.extlen == 0);
176 /* key and value exists in all packets except in the terminating */
177 ensure(response->response.cas == 0);
178 break;
179
180 case PROTOCOL_BINARY_CMD_VERSION:
181 ensure(response->response.keylen == 0);
182 ensure(response->response.extlen == 0);
183 ensure(response->response.bodylen);
184 ensure(response->response.cas == 0);
185 break;
186
187 case PROTOCOL_BINARY_CMD_GET:
188 case PROTOCOL_BINARY_CMD_GETQ:
189 ensure(response->response.keylen == 0);
190 ensure(response->response.extlen == 4);
191 ensure(response->response.cas);
192 break;
193
194 case PROTOCOL_BINARY_CMD_GETK:
195 case PROTOCOL_BINARY_CMD_GETKQ:
196 ensure(response->response.keylen);
197 ensure(response->response.extlen == 4);
198 ensure(response->response.cas);
199 break;
200
201 default:
202 /* Undefined command code */
203 break;
204 }
205 } else {
206 ensure(response->response.cas == 0);
207 ensure(response->response.extlen == 0);
208 if (opcode != PROTOCOL_BINARY_CMD_GETK) {
209 ensure(response->response.keylen == 0);
210 }
211 }
212
213 return true;
214 }