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