Add support for SASL
[m6w6/libmemcached] / libmemcached / response.c
1 /* LibMemcached
2 * Copyright (C) 2006-2009 Brian Aker
3 * All rights reserved.
4 *
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
7 *
8 * Summary: memcached_response() is used to determine the return result from an issued command.
9 *
10 */
11
12 #include "common.h"
13
14 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
15 char *buffer, size_t buffer_length,
16 memcached_result_st *result);
17 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
18 char *buffer, size_t buffer_length,
19 memcached_result_st *result);
20
21 memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
22 char *buffer, size_t buffer_length,
23 memcached_result_st *result)
24 {
25 memcached_server_response_decrement(ptr);
26
27 if (result == NULL)
28 {
29 memcached_st *root= (memcached_st *)ptr->root;
30 result = &root->result;
31 }
32
33 memcached_return_t rc;
34 if (ptr->root->flags.binary_protocol)
35 rc= binary_read_one_response(ptr, buffer, buffer_length, result);
36 else
37 rc= textual_read_one_response(ptr, buffer, buffer_length, result);
38
39 unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE ||
40 rc == MEMCACHED_PROTOCOL_ERROR ||
41 rc == MEMCACHED_CLIENT_ERROR ||
42 rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
43 memcached_io_reset(ptr);
44
45 return rc;
46 }
47
48 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
49 char *buffer, size_t buffer_length,
50 memcached_result_st *result)
51 {
52 /* We may have old commands in the buffer not set, first purge */
53 if ((ptr->root->flags.no_block) && (memcached_is_processing_input(ptr->root) == false))
54 {
55 (void)memcached_io_write(ptr, NULL, 0, true);
56 }
57
58 /*
59 * The previous implementation purged all pending requests and just
60 * returned the last one. Purge all pending messages to ensure backwards
61 * compatibility.
62 */
63 if (ptr->root->flags.binary_protocol == false)
64 while (memcached_server_response_count(ptr) > 1)
65 {
66 memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result);
67
68 unlikely (rc != MEMCACHED_END &&
69 rc != MEMCACHED_STORED &&
70 rc != MEMCACHED_SUCCESS &&
71 rc != MEMCACHED_STAT &&
72 rc != MEMCACHED_DELETED &&
73 rc != MEMCACHED_NOTFOUND &&
74 rc != MEMCACHED_NOTSTORED &&
75 rc != MEMCACHED_DATA_EXISTS)
76 return rc;
77 }
78
79 return memcached_read_one_response(ptr, buffer, buffer_length, result);
80 }
81
82 static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr,
83 char *buffer,
84 memcached_result_st *result)
85 {
86 memcached_return_t rc= MEMCACHED_SUCCESS;
87 char *string_ptr;
88 char *end_ptr;
89 char *next_ptr;
90 size_t value_length;
91 size_t to_read;
92 char *value_ptr;
93 ssize_t read_length= 0;
94 memcached_return_t rrc;
95
96 if (ptr->root->flags.use_udp)
97 return MEMCACHED_NOT_SUPPORTED;
98
99 WATCHPOINT_ASSERT(ptr->root);
100 end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
101
102 memcached_result_reset(result);
103
104 string_ptr= buffer;
105 string_ptr+= 6; /* "VALUE " */
106
107
108 /* We load the key */
109 {
110 char *key;
111 size_t prefix_length;
112
113 key= result->item_key;
114 result->key_length= 0;
115
116 for (prefix_length= ptr->root->prefix_key_length; !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
117 {
118 if (prefix_length == 0)
119 {
120 *key= *string_ptr;
121 key++;
122 result->key_length++;
123 }
124 else
125 prefix_length--;
126 }
127 result->item_key[result->key_length]= 0;
128 }
129
130 if (end_ptr == string_ptr)
131 goto read_error;
132
133 /* Flags fetch move past space */
134 string_ptr++;
135 if (end_ptr == string_ptr)
136 goto read_error;
137 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++);
138 result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10);
139
140 if (end_ptr == string_ptr)
141 goto read_error;
142
143 /* Length fetch move past space*/
144 string_ptr++;
145 if (end_ptr == string_ptr)
146 goto read_error;
147
148 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++);
149 value_length= (size_t)strtoull(next_ptr, &string_ptr, 10);
150
151 if (end_ptr == string_ptr)
152 goto read_error;
153
154 /* Skip spaces */
155 if (*string_ptr == '\r')
156 {
157 /* Skip past the \r\n */
158 string_ptr+= 2;
159 }
160 else
161 {
162 string_ptr++;
163 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++);
164 result->item_cas= strtoull(next_ptr, &string_ptr, 10);
165 }
166
167 if (end_ptr < string_ptr)
168 goto read_error;
169
170 /* We add two bytes so that we can walk the \r\n */
171 rc= memcached_string_check(&result->value, value_length+2);
172 if (rc != MEMCACHED_SUCCESS)
173 {
174 value_length= 0;
175 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
176 }
177
178 value_ptr= memcached_string_value_mutable(&result->value);
179 /*
180 We read the \r\n into the string since not doing so is more
181 cycles then the waster of memory to do so.
182
183 We are null terminating through, which will most likely make
184 some people lazy about using the return length.
185 */
186 to_read= (value_length) + 2;
187 rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length);
188 if (rrc != MEMCACHED_SUCCESS)
189 return rrc;
190
191 if (read_length != (ssize_t)(value_length + 2))
192 {
193 goto read_error;
194 }
195
196 /* This next bit blows the API, but this is internal....*/
197 {
198 char *char_ptr;
199 char_ptr= memcached_string_value_mutable(&result->value);;
200 char_ptr[value_length]= 0;
201 char_ptr[value_length + 1]= 0;
202 memcached_string_set_length(&result->value, value_length);
203 }
204
205 return MEMCACHED_SUCCESS;
206
207 read_error:
208 memcached_io_reset(ptr);
209
210 return MEMCACHED_PARTIAL_READ;
211 }
212
213 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
214 char *buffer, size_t buffer_length,
215 memcached_result_st *result)
216 {
217 memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length);
218 if (rc != MEMCACHED_SUCCESS)
219 return rc;
220
221 switch(buffer[0])
222 {
223 case 'V': /* VALUE || VERSION */
224 if (buffer[1] == 'A') /* VALUE */
225 {
226 /* We add back in one because we will need to search for END */
227 memcached_server_response_increment(ptr);
228 return textual_value_fetch(ptr, buffer, result);
229 }
230 else if (buffer[1] == 'E') /* VERSION */
231 {
232 return MEMCACHED_SUCCESS;
233 }
234 else
235 {
236 WATCHPOINT_STRING(buffer);
237 WATCHPOINT_ASSERT(0);
238 return MEMCACHED_UNKNOWN_READ_FAILURE;
239 }
240 case 'O': /* OK */
241 return MEMCACHED_SUCCESS;
242 case 'S': /* STORED STATS SERVER_ERROR */
243 {
244 if (buffer[2] == 'A') /* STORED STATS */
245 {
246 memcached_server_response_increment(ptr);
247 return MEMCACHED_STAT;
248 }
249 else if (buffer[1] == 'E') /* SERVER_ERROR */
250 {
251 char *rel_ptr;
252 char *startptr= buffer + 13, *endptr= startptr;
253
254 while (*endptr != '\r' && *endptr != '\n') endptr++;
255
256 /*
257 Yes, we could make this "efficent" but to do that we would need
258 to maintain more state for the size of the buffer. Why waste
259 memory in the struct, which is important, for something that
260 rarely should happen?
261 */
262 rel_ptr= (char *)libmemcached_realloc(ptr->root,
263 ptr->cached_server_error,
264 (size_t) (endptr - startptr + 1));
265
266 if (rel_ptr == NULL)
267 {
268 /* If we happened to have some memory, we just null it since we don't know the size */
269 if (ptr->cached_server_error)
270 ptr->cached_server_error[0]= 0;
271 return MEMCACHED_SERVER_ERROR;
272 }
273 ptr->cached_server_error= rel_ptr;
274
275 memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr));
276 ptr->cached_server_error[endptr - startptr]= 0;
277 return MEMCACHED_SERVER_ERROR;
278 }
279 else if (buffer[1] == 'T')
280 return MEMCACHED_STORED;
281 else
282 {
283 WATCHPOINT_STRING(buffer);
284 WATCHPOINT_ASSERT(0);
285 return MEMCACHED_UNKNOWN_READ_FAILURE;
286 }
287 }
288 case 'D': /* DELETED */
289 return MEMCACHED_DELETED;
290 case 'N': /* NOT_FOUND */
291 {
292 if (buffer[4] == 'F')
293 return MEMCACHED_NOTFOUND;
294 else if (buffer[4] == 'S')
295 return MEMCACHED_NOTSTORED;
296 else
297 return MEMCACHED_UNKNOWN_READ_FAILURE;
298 }
299 case 'E': /* PROTOCOL ERROR or END */
300 {
301 if (buffer[1] == 'N')
302 return MEMCACHED_END;
303 else if (buffer[1] == 'R')
304 return MEMCACHED_PROTOCOL_ERROR;
305 else if (buffer[1] == 'X')
306 return MEMCACHED_DATA_EXISTS;
307 else
308 return MEMCACHED_UNKNOWN_READ_FAILURE;
309 }
310 case 'I': /* CLIENT ERROR */
311 /* We add back in one because we will need to search for END */
312 memcached_server_response_increment(ptr);
313 return MEMCACHED_ITEM;
314 case 'C': /* CLIENT ERROR */
315 return MEMCACHED_CLIENT_ERROR;
316 default:
317 {
318 unsigned long long auto_return_value;
319
320 if (sscanf(buffer, "%llu", &auto_return_value) == 1)
321 return MEMCACHED_SUCCESS;
322
323 return MEMCACHED_UNKNOWN_READ_FAILURE;
324 }
325 }
326
327 /* NOTREACHED */
328 }
329
330 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
331 char *buffer, size_t buffer_length,
332 memcached_result_st *result)
333 {
334 protocol_binary_response_header header;
335
336 unlikely (memcached_safe_read(ptr, &header.bytes,
337 sizeof(header.bytes)) != MEMCACHED_SUCCESS)
338 return MEMCACHED_UNKNOWN_READ_FAILURE;
339
340 unlikely (header.response.magic != PROTOCOL_BINARY_RES)
341 return MEMCACHED_PROTOCOL_ERROR;
342
343 /*
344 ** Convert the header to host local endian!
345 */
346 header.response.keylen= ntohs(header.response.keylen);
347 header.response.status= ntohs(header.response.status);
348 header.response.bodylen= ntohl(header.response.bodylen);
349 header.response.cas= ntohll(header.response.cas);
350 uint32_t bodylen= header.response.bodylen;
351
352 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS ||
353 header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
354 {
355 switch (header.response.opcode)
356 {
357 case PROTOCOL_BINARY_CMD_GETKQ:
358 /*
359 * We didn't increment the response counter for the GETKQ packet
360 * (only the final NOOP), so we need to increment the counter again.
361 */
362 memcached_server_response_increment(ptr);
363 /* FALLTHROUGH */
364 case PROTOCOL_BINARY_CMD_GETK:
365 {
366 uint16_t keylen= header.response.keylen;
367 memcached_result_reset(result);
368 result->item_cas= header.response.cas;
369
370 if (memcached_safe_read(ptr, &result->item_flags,
371 sizeof (result->item_flags)) != MEMCACHED_SUCCESS)
372 return MEMCACHED_UNKNOWN_READ_FAILURE;
373
374 result->item_flags= ntohl(result->item_flags);
375 bodylen -= header.response.extlen;
376
377 result->key_length= keylen;
378 if (memcached_safe_read(ptr, result->item_key, keylen) != MEMCACHED_SUCCESS)
379 return MEMCACHED_UNKNOWN_READ_FAILURE;
380
381 bodylen -= keylen;
382 if (memcached_string_check(&result->value,
383 bodylen) != MEMCACHED_SUCCESS)
384 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
385
386 char *vptr= memcached_string_value_mutable(&result->value);
387 if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
388 return MEMCACHED_UNKNOWN_READ_FAILURE;
389
390 memcached_string_set_length(&result->value, bodylen);
391 }
392 break;
393 case PROTOCOL_BINARY_CMD_INCREMENT:
394 case PROTOCOL_BINARY_CMD_DECREMENT:
395 {
396 if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
397 return MEMCACHED_PROTOCOL_ERROR;
398
399 WATCHPOINT_ASSERT(bodylen == buffer_length);
400 uint64_t val;
401 if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS)
402 return MEMCACHED_UNKNOWN_READ_FAILURE;
403
404 val= ntohll(val);
405 memcpy(buffer, &val, sizeof(val));
406 }
407 break;
408 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
409 case PROTOCOL_BINARY_CMD_VERSION:
410 {
411 memset(buffer, 0, buffer_length);
412 if (bodylen >= buffer_length)
413 /* not enough space in buffer.. should not happen... */
414 return MEMCACHED_UNKNOWN_READ_FAILURE;
415 else if (memcached_safe_read(ptr, buffer, bodylen) != MEMCACHED_SUCCESS)
416 return MEMCACHED_UNKNOWN_READ_FAILURE;
417 }
418 break;
419 case PROTOCOL_BINARY_CMD_FLUSH:
420 case PROTOCOL_BINARY_CMD_QUIT:
421 case PROTOCOL_BINARY_CMD_SET:
422 case PROTOCOL_BINARY_CMD_ADD:
423 case PROTOCOL_BINARY_CMD_REPLACE:
424 case PROTOCOL_BINARY_CMD_APPEND:
425 case PROTOCOL_BINARY_CMD_PREPEND:
426 case PROTOCOL_BINARY_CMD_DELETE:
427 {
428 WATCHPOINT_ASSERT(bodylen == 0);
429 return MEMCACHED_SUCCESS;
430 }
431 case PROTOCOL_BINARY_CMD_NOOP:
432 {
433 WATCHPOINT_ASSERT(bodylen == 0);
434 return MEMCACHED_END;
435 }
436 case PROTOCOL_BINARY_CMD_STAT:
437 {
438 if (bodylen == 0)
439 return MEMCACHED_END;
440 else if (bodylen + 1 > buffer_length)
441 /* not enough space in buffer.. should not happen... */
442 return MEMCACHED_UNKNOWN_READ_FAILURE;
443 else
444 {
445 size_t keylen= header.response.keylen;
446 memset(buffer, 0, buffer_length);
447 if (memcached_safe_read(ptr, buffer, keylen) != MEMCACHED_SUCCESS ||
448 memcached_safe_read(ptr, buffer + keylen + 1,
449 bodylen - keylen) != MEMCACHED_SUCCESS)
450 return MEMCACHED_UNKNOWN_READ_FAILURE;
451 }
452 }
453 break;
454
455 case PROTOCOL_BINARY_CMD_SASL_AUTH:
456 case PROTOCOL_BINARY_CMD_SASL_STEP:
457 {
458 memcached_result_reset(result);
459 result->item_cas= header.response.cas;
460
461 if (memcached_string_check(&result->value,
462 bodylen) != MEMCACHED_SUCCESS)
463 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
464
465 char *vptr= memcached_string_value_mutable(&result->value);
466 if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
467 return MEMCACHED_UNKNOWN_READ_FAILURE;
468
469 memcached_string_set_length(&result->value, bodylen);
470 }
471 break;
472 default:
473 {
474 /* Command not implemented yet! */
475 WATCHPOINT_ASSERT(0);
476 return MEMCACHED_PROTOCOL_ERROR;
477 }
478 }
479 }
480 else if (header.response.bodylen)
481 {
482 /* What should I do with the error message??? just discard it for now */
483 char hole[SMALL_STRING_LEN];
484 while (bodylen > 0)
485 {
486 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
487 if (memcached_safe_read(ptr, hole, nr) != MEMCACHED_SUCCESS)
488 return MEMCACHED_UNKNOWN_READ_FAILURE;
489 bodylen-= (uint32_t) nr;
490 }
491
492 /* This might be an error from one of the quiet commands.. if
493 * so, just throw it away and get the next one. What about creating
494 * a callback to the user with the error information?
495 */
496 switch (header.response.opcode)
497 {
498 case PROTOCOL_BINARY_CMD_SETQ:
499 case PROTOCOL_BINARY_CMD_ADDQ:
500 case PROTOCOL_BINARY_CMD_REPLACEQ:
501 case PROTOCOL_BINARY_CMD_APPENDQ:
502 case PROTOCOL_BINARY_CMD_PREPENDQ:
503 return binary_read_one_response(ptr, buffer, buffer_length, result);
504 default:
505 break;
506 }
507 }
508
509 memcached_return_t rc= MEMCACHED_SUCCESS;
510 unlikely(header.response.status != 0)
511 switch (header.response.status)
512 {
513 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
514 rc= MEMCACHED_NOTFOUND;
515 break;
516 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
517 rc= MEMCACHED_DATA_EXISTS;
518 break;
519 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
520 rc= MEMCACHED_NOTSTORED;
521 break;
522 case PROTOCOL_BINARY_RESPONSE_E2BIG:
523 rc= MEMCACHED_E2BIG;
524 break;
525 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
526 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
527 break;
528 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
529 rc= MEMCACHED_AUTH_CONTINUE;
530 break;
531 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
532 rc= MEMCACHED_AUTH_FAILURE;
533 break;
534 case PROTOCOL_BINARY_RESPONSE_EINVAL:
535 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
536 default:
537 /* @todo fix the error mappings */
538 rc= MEMCACHED_PROTOCOL_ERROR;
539 break;
540 }
541
542 return rc;
543 }