fe01e3886c33c804f426b10669fc697caa093b63
[awesomized/libmemcached] / src / libmemcached / response.cc
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached-awesome - 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-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcached/common.h"
17 #include "libmemcached/string.hpp"
18
19 static inline memcached_return_t ascii_read_inc(memcached_instance_st *instance, const char *buffer,
20 memcached_result_st *result) {
21 {
22 errno = 0;
23 unsigned long long int auto_return_value = strtoull(buffer, (char **) NULL, 10);
24
25 if (errno) {
26 result->numeric_value = UINT64_MAX;
27 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
28 memcached_literal_param("Numeric response was out of range"));
29 }
30
31 result->numeric_value = uint64_t(auto_return_value);
32 return MEMCACHED_SUCCESS;
33 }
34 }
35
36 static inline memcached_return_t ascii_read_ull(size_t &value, char **string_ptr, char *end_ptr) {
37 char *next_ptr = *string_ptr;
38
39 errno = 0;
40 value = strtoull(next_ptr, string_ptr, 10);
41
42 if (errno) {
43 return MEMCACHED_ERRNO;
44 }
45 if (*string_ptr >= end_ptr) {
46 return MEMCACHED_PARTIAL_READ;
47 }
48 return MEMCACHED_SUCCESS;
49 }
50
51 static inline memcached_return_t ascii_read_key(char *buf, size_t len, char **str_ptr, char *end_ptr) {
52 char *tmp_ptr = buf;
53 while (**str_ptr != ' ' && **str_ptr != '\r') {
54 *(tmp_ptr++) = *((*str_ptr)++);
55 if (*str_ptr == end_ptr || size_t(tmp_ptr - buf) >= len) {
56 return MEMCACHED_PARTIAL_READ;
57 }
58 }
59 *tmp_ptr = 0;
60 return MEMCACHED_SUCCESS;
61 }
62
63 static inline memcached_return_t ascii_read_val(memcached_instance_st *instance, memcached_result_st *result,
64 ssize_t &to_read) {
65 /* We add two bytes so that we can walk the \r\n */
66 if (memcached_failed(memcached_string_check(&result->value, to_read))) {
67 return memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
68 }
69
70 {
71 char *value_ptr = memcached_string_value_mutable(&result->value);
72 /*
73 We read the \r\n into the string since not doing so is more
74 cycles then the waster of memory to do so.
75
76 We are null terminating through, which will most likely make
77 some people lazy about using the return length.
78 */
79 memcached_return_t read_rc = memcached_io_read(instance, value_ptr, to_read, to_read);
80 if (memcached_failed(read_rc)) {
81 if (read_rc == MEMCACHED_IN_PROGRESS) {
82 memcached_quit_server(instance, true);
83 return memcached_set_error(*instance, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
84 }
85 return read_rc;
86 }
87
88 /* This next bit blows the API, but this is internal....*/
89 {
90 char *char_ptr = memcached_string_value_mutable(&result->value);
91 char_ptr[--to_read] = 0;
92 char_ptr[--to_read] = 0;
93 memcached_string_set_length(&result->value, to_read);
94 }
95
96 return MEMCACHED_SUCCESS;
97 }
98 }
99
100 static memcached_return_t result_decrypt(memcached_instance_st *instance, memcached_result_st *result) {
101 memcached_return_t rc = MEMCACHED_SUCCESS;
102
103 if (memcached_result_length(result)) {
104 hashkit_string_st *destination = hashkit_decrypt(&instance->root->hashkit, memcached_result_value(result),
105 memcached_result_length(result));
106 if(!destination) {
107 return memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
108 memcached_literal_param("hashkit_decrypt() failed"));
109 }
110
111 memcached_result_reset_value(result);
112
113 rc = memcached_result_set_value(result, hashkit_string_c_str(destination), hashkit_string_length(destination));
114 if (memcached_failed(rc)) {
115 rc = memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
116 memcached_literal_param("hashkit_decrypt() failed"));
117 }
118
119 hashkit_string_free(destination);
120 }
121
122 return rc;
123 }
124
125 static memcached_return_t meta_fetch_flags(memcached_instance_st *instance, char *str_ptr, char *end_ptr,
126 memcached_result_st *result, char opaque[MEMCACHED_MAX_KEY]) {
127 size_t ull_val;
128 char *tmp_ptr;
129
130 while (str_ptr < end_ptr && *str_ptr != '\r') {
131 switch (*str_ptr++) {
132 case ' ':
133 break;
134
135 case 'c': // CAS
136 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
137 goto read_error;
138 }
139 result->item_cas = ull_val;
140 break;
141
142 case 'f':
143 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
144 goto read_error;
145 }
146 result->item_flags = ull_val;
147 break;
148
149 case 'k':
150 str_ptr += memcached_array_size(instance->root->_namespace);
151 if (str_ptr >= end_ptr) {
152 goto read_error;
153 }
154 tmp_ptr = str_ptr;
155 if (MEMCACHED_SUCCESS != ascii_read_key(result->item_key, sizeof(result->item_key), &str_ptr, end_ptr)) {
156 goto read_error;
157 }
158 result->key_length = str_ptr - tmp_ptr;
159 break;
160
161 case 'l':
162 if (ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
163 goto read_error;
164 }
165 /* legacy result does not support last_accessed */
166 break;
167
168 case 'O':
169 tmp_ptr = str_ptr;
170 if (MEMCACHED_SUCCESS != ascii_read_key(opaque, MEMCACHED_MAX_KEY, &str_ptr, end_ptr)) {
171 goto read_error;
172 }
173 /* legacy result does not support opaque */
174 break;
175
176 case 's': // size
177 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
178 goto read_error;
179 }
180 /* legacy result does not support size */
181 break;
182
183 case 't':
184 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
185 goto read_error;
186 }
187 result->item_expiration = time_t(ull_val);
188 break;
189
190 case 'W':
191 case 'X':
192 case 'Z':
193 /* legacy result does not support re-cache semantics */
194 break;
195 }
196 }
197
198 return MEMCACHED_SUCCESS;
199 read_error:
200 memcached_io_reset(instance);
201 return MEMCACHED_PARTIAL_READ;
202 }
203
204 static memcached_return_t meta_va_fetch(memcached_instance_st *instance, char *buffer,
205 memcached_result_st *result) {
206 char opaque[MEMCACHED_MAX_KEY] = "";
207 char *str_ptr = buffer + sizeof("VA");
208 char *end_ptr = buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
209 size_t ull_val, val_len;
210 memcached_return_t rc;
211
212 while (isspace(*str_ptr) && str_ptr < end_ptr) {
213 ++str_ptr;
214 }
215 if (str_ptr == end_ptr) {
216 goto read_error;
217 }
218
219 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
220 goto read_error;
221 }
222 val_len = ull_val;
223
224 rc = meta_fetch_flags(instance, str_ptr, end_ptr, result, opaque);
225 if (memcached_success(rc)) {
226 auto read_len = ssize_t(val_len + 2);
227 rc = ascii_read_val(instance, result, read_len);
228 if (memcached_success(rc)) {
229 if (read_len != ssize_t(val_len)) {
230 goto read_error;
231 }
232
233 /* meta INC/DEC response */
234 if ('+' == *opaque) {
235 rc = ascii_read_inc(instance, result->value.string, result);
236 } else if (memcached_is_encrypted(instance->root)) {
237 rc = result_decrypt(instance, result);
238 if (memcached_failed(rc)) {
239 memcached_result_reset(result);
240 }
241 }
242 }
243 }
244
245 return rc;
246
247 read_error:
248 memcached_io_reset(instance);
249 return MEMCACHED_PARTIAL_READ;
250 }
251
252 static memcached_return_t textual_value_fetch(memcached_instance_st *instance, char *buffer,
253 memcached_result_st *result) {
254 ssize_t read_length = 0;
255 size_t value_length, ull_val;
256
257 WATCHPOINT_ASSERT(instance->root);
258 char *end_ptr = buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
259
260 memcached_result_reset(result);
261
262 char *string_ptr = buffer;
263 string_ptr += 6; /* "VALUE " */
264
265 // Just used for cases of AES decrypt currently
266 memcached_return_t rc = MEMCACHED_SUCCESS;
267
268 /* We load the key */
269 {
270 char *key = result->item_key;
271 result->key_length = 0;
272
273 for (ptrdiff_t prefix_length = memcached_array_size(instance->root->_namespace);
274 !(iscntrl(*string_ptr) || isspace(*string_ptr)); string_ptr++)
275 {
276 if (prefix_length == 0) {
277 *key = *string_ptr;
278 key++;
279 result->key_length++;
280 } else
281 prefix_length--;
282 }
283 result->item_key[result->key_length] = 0;
284 }
285
286 if (end_ptr == string_ptr) {
287 goto read_error;
288 }
289
290 /* Flags fetch move past space */
291 string_ptr++;
292 if (end_ptr == string_ptr) {
293 goto read_error;
294 }
295
296 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &string_ptr, end_ptr)) {
297 goto read_error;
298 }
299 result->item_flags = ull_val;
300
301 /* Length fetch move past space*/
302 string_ptr++;
303 if (end_ptr == string_ptr) {
304 goto read_error;
305 }
306
307 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &string_ptr, end_ptr)) {
308 goto read_error;
309 }
310 value_length = ull_val;
311
312 /* Skip spaces */
313 if (*string_ptr == '\r') {
314 /* Skip past the \r\n */
315 string_ptr += 2;
316 } else {
317 string_ptr++;
318 if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &string_ptr, end_ptr)) {
319 goto read_error;
320 }
321 result->item_cas = ull_val;
322 }
323
324 if (end_ptr < string_ptr) {
325 goto read_error;
326 }
327
328 read_length = ssize_t(value_length + 2);
329 rc = ascii_read_val(instance, result, read_length);
330 if (MEMCACHED_SUCCESS != rc) {
331 return rc;
332 }
333
334 if (read_length != (ssize_t)(value_length)) {
335 goto read_error;
336 }
337
338 if (memcached_is_encrypted(instance->root)) {
339 rc = result_decrypt(instance, result);
340 if (memcached_failed(rc)) {
341 memcached_result_reset(result);
342 }
343 }
344
345 return rc;
346
347 read_error:
348 memcached_io_reset(instance);
349 return MEMCACHED_PARTIAL_READ;
350 }
351
352 static memcached_return_t textual_read_one_response(memcached_instance_st *instance, char *buffer,
353 const size_t buffer_length,
354 memcached_result_st *result) {
355 size_t total_read;
356 memcached_return_t rc = memcached_io_readline(instance, buffer, buffer_length, total_read);
357
358 if (memcached_failed(rc)) {
359 return rc;
360 }
361 assert(total_read);
362
363 switch (buffer[0]) {
364 case 'V': {
365 // VALUE
366 if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
367 {
368 /* We add back in one because we will need to search for END */
369 memcached_server_response_increment(instance);
370 return textual_value_fetch(instance, buffer, result);
371 }
372 // VA
373 else if (buffer[1] == 'A' && buffer[2] == ' ') {
374 return meta_va_fetch(instance, buffer, result);
375 }
376 // VERSION
377 else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'S' and buffer[4] == 'I'
378 and buffer[5] == 'O' and buffer[6] == 'N') /* VERSION */
379 {
380 /* Find the space, and then move one past it to copy version */
381 char *response_ptr = strchr(buffer, ' ');
382
383 char *endptr;
384 errno = 0;
385 long int version = strtol(response_ptr, &endptr, 10);
386 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX
387 or version == 0) {
388 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
389 return memcached_set_error(
390 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
391 memcached_literal_param("strtol() failed to parse major version"));
392 }
393 instance->major_version = uint8_t(version);
394
395 endptr++;
396 errno = 0;
397 version = strtol(endptr, &endptr, 10);
398 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
399 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
400 return memcached_set_error(
401 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
402 memcached_literal_param("strtol() failed to parse minor version"));
403 }
404 instance->minor_version = uint8_t(version);
405
406 endptr++;
407 errno = 0;
408 version = strtol(endptr, &endptr, 10);
409 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
410 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
411 return memcached_set_error(
412 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
413 memcached_literal_param("strtol() failed to parse micro version"));
414 }
415 instance->micro_version = uint8_t(version);
416
417 return MEMCACHED_SUCCESS;
418 }
419 } break;
420
421 case 'O': {
422 // OK
423 if (buffer[1] == 'K') {
424 return MEMCACHED_SUCCESS;
425 }
426 } break;
427
428 case 'S': {
429 // STAT
430 if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */ {
431 memcached_server_response_increment(instance);
432 return MEMCACHED_STAT;
433 }
434 // SERVER_ERROR
435 else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'V' and buffer[4] == 'E'
436 and buffer[5] == 'R' and buffer[6] == '_' and buffer[7] == 'E' and buffer[8] == 'R'
437 and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
438 {
439 if (total_read == memcached_literal_param_size("SERVER_ERROR")) {
440 return MEMCACHED_SERVER_ERROR;
441 }
442
443 if (total_read >= memcached_literal_param_size("SERVER_ERROR object too large for cache")
444 and (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache"))
445 == 0))
446 {
447 return MEMCACHED_E2BIG;
448 }
449
450 if (total_read >= memcached_literal_param_size("SERVER_ERROR out of memory")
451 and ((memcmp(buffer, memcached_literal_param("SERVER_ERROR out of memory")) == 0)
452 or (memcmp(buffer, memcached_literal_param("SERVER_ERROR Out of memory")) == 0)))
453 {
454 return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE;
455 }
456
457 // Move past the basic error message and whitespace
458 char *startptr = buffer + memcached_literal_param_size("SERVER_ERROR");
459 if (startptr[0] == ' ') {
460 startptr++;
461 }
462
463 char *endptr = startptr;
464 while (*endptr != '\r' && *endptr != '\n') endptr++;
465
466 return memcached_set_error(*instance, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr,
467 size_t(endptr - startptr));
468 }
469 // STORED
470 else if (buffer[1] == 'T' and buffer[2] == 'O'
471 and buffer[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
472 {
473 return MEMCACHED_STORED;
474 }
475 } break;
476
477 case 'D': {
478 // DELETED
479 if (buffer[1] == 'E' and buffer[2] == 'L' and buffer[3] == 'E' and buffer[4] == 'T'
480 and buffer[5] == 'E' and buffer[6] == 'D')
481 {
482 return MEMCACHED_DELETED;
483 }
484 } break;
485
486 case 'N': {
487 // NOT_FOUND
488 if (buffer[1] == 'F') {
489 return MEMCACHED_NOTFOUND;
490 }
491 else if (buffer[1] == 'O' and buffer[2] == 'T' and buffer[3] == '_' and buffer[4] == 'F'
492 and buffer[5] == 'O' and buffer[6] == 'U' and buffer[7] == 'N' and buffer[8] == 'D')
493 {
494 return MEMCACHED_NOTFOUND;
495 }
496 // NOT_STORED
497 else if (buffer[1] == 'S') {
498 return MEMCACHED_NOTSTORED;
499 }
500 else if (buffer[1] == 'O' and buffer[2] == 'T' and buffer[3] == '_' and buffer[4] == 'S'
501 and buffer[5] == 'T' and buffer[6] == 'O' and buffer[7] == 'R' and buffer[8] == 'E'
502 and buffer[9] == 'D')
503 {
504 return MEMCACHED_NOTSTORED;
505 }
506 } break;
507
508 case 'M': /* META NOOP */
509 if (buffer[1] == 'N') {
510 return MEMCACHED_END;
511 }
512 break;
513
514 case 'E': /* PROTOCOL ERROR or END */
515 {
516 // END
517 if (buffer[1] == 'N') {
518 if (buffer[2] == 'D') {
519 return MEMCACHED_END;
520 }
521 return MEMCACHED_NOTFOUND;
522 }
523 // ERROR
524 else if (buffer[1] == 'R' and buffer[2] == 'R' and buffer[3] == 'O' and buffer[4] == 'R')
525 {
526 return MEMCACHED_ERROR;
527 }
528 // EXISTS
529 else if (buffer[1] == 'X')
530 {
531 return MEMCACHED_DATA_EXISTS;
532 }
533 } break;
534
535 case 'T': /* TOUCHED */
536 {
537 // TOUCHED
538 if (buffer[1] == 'O' and buffer[2] == 'U' and buffer[3] == 'C' and buffer[4] == 'H'
539 and buffer[5] == 'E' and buffer[6] == 'D')
540 {
541 return MEMCACHED_SUCCESS;
542 }
543 } break;
544
545 case 'I': /* ITEM */
546 {
547 // ITEM
548 if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M') {
549 /* We add back in one because we will need to search for END */
550 memcached_server_response_increment(instance);
551 return MEMCACHED_ITEM;
552 }
553 } break;
554
555 case 'C': /* CLIENT ERROR */
556 {
557 // CLIENT_ERROR
558 if (buffer[1] == 'L' and buffer[2] == 'I' and buffer[3] == 'E' and buffer[4] == 'N'
559 and buffer[5] == 'T' and buffer[6] == '_' and buffer[7] == 'E' and buffer[8] == 'R'
560 and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
561 {
562 // Move past the basic error message and whitespace
563 char *startptr = buffer + memcached_literal_param_size("CLIENT_ERROR");
564 if (startptr[0] == ' ') {
565 startptr++;
566 }
567
568 char *endptr = startptr;
569 while (*endptr != '\r' && *endptr != '\n') endptr++;
570
571 return memcached_set_error(*instance, MEMCACHED_CLIENT_ERROR, MEMCACHED_AT, startptr,
572 size_t(endptr - startptr));
573 }
574 } break;
575
576 case '0': /* INCR/DECR response */
577 case '1': /* INCR/DECR response */
578 case '2': /* INCR/DECR response */
579 case '3': /* INCR/DECR response */
580 case '4': /* INCR/DECR response */
581 case '5': /* INCR/DECR response */
582 case '6': /* INCR/DECR response */
583 case '7': /* INCR/DECR response */
584 case '8': /* INCR/DECR response */
585 case '9': /* INCR/DECR response */
586 return ascii_read_inc(instance, buffer, result);
587
588 default:
589 break;
590 }
591
592 buffer[total_read] = 0;
593 #if 0
594 if (total_read >= sizeof("STORSTORED") -1)
595 {
596 fprintf(stderr, "%s:%d '%s', %.*s\n", __FILE__, __LINE__,
597 buffer, MEMCACHED_MAX_BUFFER, instance->read_buffer);
598 assert(memcmp(buffer,"STORSTORED", sizeof("STORSTORED") -1));
599 }
600 #endif
601 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, buffer,
602 total_read);
603 }
604
605 static memcached_return_t binary_read_one_response(memcached_instance_st *instance, char *buffer,
606 const size_t buffer_length,
607 memcached_result_st *result) {
608 memcached_return_t rc;
609 protocol_binary_response_header header;
610
611 assert(memcached_is_binary(instance->root));
612
613 if ((rc = memcached_safe_read(instance, &header.bytes, sizeof(header.bytes)))
614 != MEMCACHED_SUCCESS) {
615 WATCHPOINT_ERROR(rc);
616 return rc;
617 }
618
619 if (header.response.magic != PROTOCOL_BINARY_RES) {
620 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
621 }
622
623 /*
624 ** Convert the header to host local endian!
625 */
626 header.response.keylen = ntohs(header.response.keylen);
627 header.response.status = ntohs(header.response.status);
628 header.response.bodylen = ntohl(header.response.bodylen);
629 header.response.cas = memcached_ntohll(header.response.cas);
630 uint32_t bodylen = header.response.bodylen;
631
632 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS
633 or header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
634 {
635 switch (header.response.opcode) {
636 case PROTOCOL_BINARY_CMD_GETKQ:
637 /*
638 * We didn't increment the response counter for the GETKQ packet
639 * (only the final NOOP), so we need to increment the counter again.
640 */
641 memcached_server_response_increment(instance);
642 /* fall through */
643 case PROTOCOL_BINARY_CMD_GETK: {
644 uint16_t keylen = header.response.keylen;
645 memcached_result_reset(result);
646 result->item_cas = header.response.cas;
647
648 if ((rc = memcached_safe_read(instance, &result->item_flags, sizeof(result->item_flags)))
649 != MEMCACHED_SUCCESS)
650 {
651 WATCHPOINT_ERROR(rc);
652 return MEMCACHED_UNKNOWN_READ_FAILURE;
653 }
654
655 result->item_flags = ntohl(result->item_flags);
656 bodylen -= header.response.extlen;
657
658 result->key_length = keylen;
659 if (memcached_failed(rc = memcached_safe_read(instance, result->item_key, keylen))) {
660 WATCHPOINT_ERROR(rc);
661 return MEMCACHED_UNKNOWN_READ_FAILURE;
662 }
663
664 // Only bother with doing this if key_length > 0
665 if (result->key_length) {
666 if (memcached_array_size(instance->root->_namespace)
667 and memcached_array_size(instance->root->_namespace) >= result->key_length)
668 {
669 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
670 }
671
672 if (memcached_array_size(instance->root->_namespace)) {
673 result->key_length -= memcached_array_size(instance->root->_namespace);
674 memmove(result->item_key,
675 result->item_key + memcached_array_size(instance->root->_namespace),
676 result->key_length);
677 }
678 }
679
680 bodylen -= keylen;
681 if (memcached_failed(memcached_string_check(&result->value, bodylen))) {
682 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
683 }
684
685 char *vptr = memcached_string_value_mutable(&result->value);
686 if (memcached_failed(rc = memcached_safe_read(instance, vptr, bodylen))) {
687 WATCHPOINT_ERROR(rc);
688 return MEMCACHED_UNKNOWN_READ_FAILURE;
689 }
690
691 memcached_string_set_length(&result->value, bodylen);
692 } break;
693
694 case PROTOCOL_BINARY_CMD_INCREMENT:
695 case PROTOCOL_BINARY_CMD_DECREMENT: {
696 if (bodylen != sizeof(uint64_t)) {
697 result->numeric_value = UINT64_MAX;
698 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
699 }
700
701 uint64_t val;
702 if ((rc = memcached_safe_read(instance, &val, sizeof(val))) != MEMCACHED_SUCCESS) {
703 result->numeric_value = UINT64_MAX;
704 return MEMCACHED_UNKNOWN_READ_FAILURE;
705 }
706
707 result->numeric_value = memcached_ntohll(val);
708 } break;
709
710 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: {
711 if (header.response.keylen || bodylen + 1 > buffer_length) {
712 return MEMCACHED_UNKNOWN_READ_FAILURE;
713 } else {
714 if ((rc = memcached_safe_read(instance, buffer, bodylen)) != MEMCACHED_SUCCESS) {
715 return MEMCACHED_UNKNOWN_READ_FAILURE;
716 }
717 }
718 } break;
719
720 case PROTOCOL_BINARY_CMD_VERSION: {
721 char version_buffer[32]; // @todo document this number
722 memset(version_buffer, 0, sizeof(version_buffer));
723
724 if (memcached_safe_read(instance, version_buffer, bodylen) != MEMCACHED_SUCCESS) {
725 return MEMCACHED_UNKNOWN_READ_FAILURE;
726 }
727
728 char *endptr;
729 errno = 0;
730 long int version = strtol(version_buffer, &endptr, 10);
731 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX
732 or version == 0) {
733 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
734 return memcached_set_error(
735 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
736 memcached_literal_param("strtol() failed to parse major version"));
737 }
738 instance->major_version = uint8_t(version);
739
740 endptr++;
741 errno = 0;
742 version = strtol(endptr, &endptr, 10);
743 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
744 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
745 return memcached_set_error(
746 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
747 memcached_literal_param("strtol() failed to parse minor version"));
748 }
749 instance->minor_version = uint8_t(version);
750
751 endptr++;
752 errno = 0;
753 version = strtol(endptr, &endptr, 10);
754 if (errno or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
755 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
756 return memcached_set_error(
757 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
758 memcached_literal_param("strtol() failed to parse micro version"));
759 }
760 instance->micro_version = uint8_t(version);
761 } break;
762
763 case PROTOCOL_BINARY_CMD_TOUCH: {
764 rc = MEMCACHED_SUCCESS;
765 if (bodylen == 4) // The four byte read is a bug?
766 {
767 char touch_buffer[4]; // @todo document this number
768 rc = memcached_safe_read(instance, touch_buffer, sizeof(touch_buffer));
769 #if 0
770 fprintf(stderr, "%s:%d %d %d %d %d %.*s(%d)\n", __FILE__, __LINE__,
771 int(touch_buffer[0]),
772 int(touch_buffer[1]),
773 int(touch_buffer[2]),
774 int(touch_buffer[3]),
775 int(bodylen), touch_buffer, int(bodylen));
776 #endif
777 }
778 return memcached_set_error(*instance, rc, MEMCACHED_AT);
779 }
780
781 case PROTOCOL_BINARY_CMD_FLUSH:
782 case PROTOCOL_BINARY_CMD_QUIT:
783 case PROTOCOL_BINARY_CMD_SET:
784 case PROTOCOL_BINARY_CMD_ADD:
785 case PROTOCOL_BINARY_CMD_REPLACE:
786 case PROTOCOL_BINARY_CMD_APPEND:
787 case PROTOCOL_BINARY_CMD_PREPEND:
788 case PROTOCOL_BINARY_CMD_DELETE: {
789 WATCHPOINT_ASSERT(bodylen == 0);
790 return MEMCACHED_SUCCESS;
791 }
792
793 case PROTOCOL_BINARY_CMD_NOOP: {
794 WATCHPOINT_ASSERT(bodylen == 0);
795 return MEMCACHED_END;
796 }
797
798 case PROTOCOL_BINARY_CMD_STAT: {
799 if (bodylen == 0) {
800 return MEMCACHED_END;
801 } else if (bodylen + 1 > buffer_length) {
802 /* not enough space in buffer.. should not happen... */
803 return MEMCACHED_UNKNOWN_READ_FAILURE;
804 } else {
805 size_t keylen = header.response.keylen;
806 memset(buffer, 0, buffer_length);
807 if ((rc = memcached_safe_read(instance, buffer, keylen)) != MEMCACHED_SUCCESS
808 || (rc = memcached_safe_read(instance, buffer + keylen + 1, bodylen - keylen))
809 != MEMCACHED_SUCCESS)
810 {
811 WATCHPOINT_ERROR(rc);
812 return MEMCACHED_UNKNOWN_READ_FAILURE;
813 }
814 }
815 } break;
816
817 case PROTOCOL_BINARY_CMD_SASL_AUTH:
818 case PROTOCOL_BINARY_CMD_SASL_STEP: {
819 memcached_result_reset(result);
820 result->item_cas = header.response.cas;
821
822 if (memcached_string_check(&result->value, bodylen) != MEMCACHED_SUCCESS)
823 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
824
825 char *vptr = memcached_string_value_mutable(&result->value);
826 if ((rc = memcached_safe_read(instance, vptr, bodylen)) != MEMCACHED_SUCCESS) {
827 WATCHPOINT_ERROR(rc);
828 return MEMCACHED_UNKNOWN_READ_FAILURE;
829 }
830
831 memcached_string_set_length(&result->value, bodylen);
832 } break;
833 default: {
834 /* Command not implemented yet! */
835 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
836 }
837 }
838 } else if (header.response.bodylen) {
839 /* What should I do with the error message??? just discard it for now */
840 char hole[SMALL_STRING_LEN];
841 while (bodylen > 0) {
842 size_t nr = (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
843 if ((rc = memcached_safe_read(instance, hole, nr)) != MEMCACHED_SUCCESS) {
844 WATCHPOINT_ERROR(rc);
845 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
846 }
847 bodylen -= (uint32_t) nr;
848 }
849
850 /* This might be an error from one of the quiet commands.. if
851 * so, just throw it away and get the next one. What about creating
852 * a callback to the user with the error information?
853 */
854 switch (header.response.opcode) {
855 case PROTOCOL_BINARY_CMD_SETQ:
856 case PROTOCOL_BINARY_CMD_ADDQ:
857 case PROTOCOL_BINARY_CMD_REPLACEQ:
858 case PROTOCOL_BINARY_CMD_APPENDQ:
859 case PROTOCOL_BINARY_CMD_PREPENDQ:
860 return MEMCACHED_FETCH_NOTFINISHED;
861
862 default:
863 break;
864 }
865 }
866
867 rc = MEMCACHED_SUCCESS;
868 if (header.response.status) {
869 switch (header.response.status) {
870 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
871 rc = MEMCACHED_NOTFOUND;
872 break;
873
874 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
875 rc = MEMCACHED_DATA_EXISTS;
876 break;
877
878 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
879 rc = MEMCACHED_NOTSTORED;
880 break;
881
882 case PROTOCOL_BINARY_RESPONSE_E2BIG:
883 rc = MEMCACHED_E2BIG;
884 break;
885
886 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
887 rc = MEMCACHED_MEMORY_ALLOCATION_FAILURE;
888 break;
889
890 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
891 rc = MEMCACHED_AUTH_CONTINUE;
892 break;
893
894 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
895 rc = MEMCACHED_AUTH_FAILURE;
896 break;
897
898 case PROTOCOL_BINARY_RESPONSE_EINVAL:
899 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
900 default:
901 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
902 break;
903 }
904 }
905
906 return rc;
907 }
908
909 static memcached_return_t _read_one_response(memcached_instance_st *instance, char *buffer,
910 const size_t buffer_length,
911 memcached_result_st *result) {
912 memcached_server_response_decrement(instance);
913
914 if (result == NULL) {
915 Memcached *root = (Memcached *) instance->root;
916 result = &root->result;
917 }
918
919 memcached_return_t rc;
920 if (memcached_is_binary(instance->root)) {
921 do {
922 rc = binary_read_one_response(instance, buffer, buffer_length, result);
923 } while (rc == MEMCACHED_FETCH_NOTFINISHED);
924 } else {
925 rc = textual_read_one_response(instance, buffer, buffer_length, result);
926 }
927
928 if (memcached_fatal(rc) && rc != MEMCACHED_TIMEOUT) {
929 memcached_io_reset(instance);
930 }
931
932 return rc;
933 }
934
935 memcached_return_t memcached_read_one_response(memcached_instance_st *instance,
936 memcached_result_st *result) {
937 char buffer[SMALL_STRING_LEN];
938
939 if (memcached_is_udp(instance->root)) {
940 return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
941 }
942
943 return _read_one_response(instance, buffer, sizeof(buffer), result);
944 }
945
946 memcached_return_t memcached_response(memcached_instance_st *instance,
947 memcached_result_st *result) {
948 char buffer[1024];
949
950 return memcached_response(instance, buffer, sizeof(buffer), result);
951 }
952
953 memcached_return_t memcached_response(memcached_instance_st *instance, char *buffer,
954 size_t buffer_length, memcached_result_st *result) {
955 if (memcached_is_udp(instance->root)) {
956 return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
957 }
958
959 /* We may have old commands in the buffer not sent, first purge */
960 if ((instance->root->flags.no_block) and (memcached_is_processing_input(instance->root) == false))
961 {
962 (void) memcached_io_write(instance);
963 }
964
965 /* Before going into loop wait to see if we have any IO waiting for us */
966 if (0) {
967 memcached_return_t read_rc = memcached_io_wait_for_read(instance);
968 fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, memcached_strerror(NULL, read_rc));
969 }
970
971 /*
972 * The previous implementation purged all pending requests and just
973 * returned the last one. Purge all pending messages to ensure backwards
974 * compatibility.
975 */
976 if (memcached_is_binary(instance->root) == false
977 and memcached_server_response_count(instance) > 1) {
978 memcached_result_st junked_result;
979 memcached_result_st *junked_result_ptr =
980 memcached_result_create(instance->root, &junked_result);
981
982 assert(junked_result_ptr);
983
984 while (memcached_server_response_count(instance) > 1) {
985 memcached_return_t rc =
986 _read_one_response(instance, buffer, buffer_length, junked_result_ptr);
987
988 // @TODO should we return an error on another but a bad read case?
989 if (memcached_fatal(rc)) {
990 memcached_result_free(junked_result_ptr);
991 return rc;
992 }
993 }
994 memcached_result_free(junked_result_ptr);
995 }
996
997 return _read_one_response(instance, buffer, buffer_length, result);
998 }