Fix for possible memory overflow when fetching error messages.
[m6w6/libmemcached] / libmemcached / response.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2009 Brian Aker All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38 #include <libmemcached/common.h>
39 #include <libmemcached/string.hpp>
40
41 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
42 char *buffer, size_t buffer_length,
43 memcached_result_st *result);
44 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
45 char *buffer, size_t buffer_length,
46 memcached_result_st *result);
47
48 memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
49 char *buffer, size_t buffer_length,
50 memcached_result_st *result)
51 {
52 memcached_server_response_decrement(ptr);
53
54 if (result == NULL)
55 {
56 memcached_st *root= (memcached_st *)ptr->root;
57 result = &root->result;
58 }
59
60 memcached_return_t rc;
61 if (ptr->root->flags.binary_protocol)
62 {
63 rc= binary_read_one_response(ptr, buffer, buffer_length, result);
64 }
65 else
66 {
67 rc= textual_read_one_response(ptr, buffer, buffer_length, result);
68 }
69
70 unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE or
71 rc == MEMCACHED_PROTOCOL_ERROR or
72 rc == MEMCACHED_CLIENT_ERROR or
73 rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
74 memcached_io_reset(ptr);
75
76 return rc;
77 }
78
79 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
80 char *buffer, size_t buffer_length,
81 memcached_result_st *result)
82 {
83 /* We may have old commands in the buffer not set, first purge */
84 if ((ptr->root->flags.no_block) && (memcached_is_processing_input(ptr->root) == false))
85 {
86 (void)memcached_io_write(ptr, NULL, 0, true);
87 }
88
89 /*
90 * The previous implementation purged all pending requests and just
91 * returned the last one. Purge all pending messages to ensure backwards
92 * compatibility.
93 */
94 if (ptr->root->flags.binary_protocol == false)
95 {
96 while (memcached_server_response_count(ptr) > 1)
97 {
98 memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result);
99
100 unlikely (rc != MEMCACHED_END &&
101 rc != MEMCACHED_STORED &&
102 rc != MEMCACHED_SUCCESS &&
103 rc != MEMCACHED_STAT &&
104 rc != MEMCACHED_DELETED &&
105 rc != MEMCACHED_NOTFOUND &&
106 rc != MEMCACHED_NOTSTORED &&
107 rc != MEMCACHED_DATA_EXISTS)
108 return rc;
109 }
110 }
111
112 return memcached_read_one_response(ptr, buffer, buffer_length, result);
113 }
114
115 static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr,
116 char *buffer,
117 memcached_result_st *result)
118 {
119 char *string_ptr;
120 char *end_ptr;
121 char *next_ptr;
122 size_t value_length;
123 size_t to_read;
124 ssize_t read_length= 0;
125
126 if (ptr->root->flags.use_udp)
127 return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
128
129 WATCHPOINT_ASSERT(ptr->root);
130 end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
131
132 memcached_result_reset(result);
133
134 string_ptr= buffer;
135 string_ptr+= 6; /* "VALUE " */
136
137
138 /* We load the key */
139 {
140 char *key;
141 size_t prefix_length;
142
143 key= result->item_key;
144 result->key_length= 0;
145
146 for (prefix_length= memcached_array_size(ptr->root->_namespace); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
147 {
148 if (prefix_length == 0)
149 {
150 *key= *string_ptr;
151 key++;
152 result->key_length++;
153 }
154 else
155 prefix_length--;
156 }
157 result->item_key[result->key_length]= 0;
158 }
159
160 if (end_ptr == string_ptr)
161 goto read_error;
162
163 /* Flags fetch move past space */
164 string_ptr++;
165 if (end_ptr == string_ptr)
166 goto read_error;
167
168 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
169 result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10);
170
171 if (end_ptr == string_ptr)
172 goto read_error;
173
174 /* Length fetch move past space*/
175 string_ptr++;
176 if (end_ptr == string_ptr)
177 goto read_error;
178
179 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
180 value_length= (size_t)strtoull(next_ptr, &string_ptr, 10);
181
182 if (end_ptr == string_ptr)
183 goto read_error;
184
185 /* Skip spaces */
186 if (*string_ptr == '\r')
187 {
188 /* Skip past the \r\n */
189 string_ptr+= 2;
190 }
191 else
192 {
193 string_ptr++;
194 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
195 result->item_cas= strtoull(next_ptr, &string_ptr, 10);
196 }
197
198 if (end_ptr < string_ptr)
199 goto read_error;
200
201 /* We add two bytes so that we can walk the \r\n */
202 if (memcached_failed(memcached_string_check(&result->value, value_length +2)))
203 {
204 return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
205 }
206
207 {
208 char *value_ptr= memcached_string_value_mutable(&result->value);
209 /*
210 We read the \r\n into the string since not doing so is more
211 cycles then the waster of memory to do so.
212
213 We are null terminating through, which will most likely make
214 some people lazy about using the return length.
215 */
216 to_read= (value_length) + 2;
217 memcached_return_t rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length);
218 if (memcached_failed(rrc) and rrc == MEMCACHED_IN_PROGRESS)
219 {
220 memcached_quit_server(ptr, true);
221 return memcached_set_error(*ptr, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
222 }
223 else if (memcached_failed(rrc))
224 {
225 return rrc;
226 }
227 }
228
229 if (read_length != (ssize_t)(value_length + 2))
230 {
231 goto read_error;
232 }
233
234 /* This next bit blows the API, but this is internal....*/
235 {
236 char *char_ptr;
237 char_ptr= memcached_string_value_mutable(&result->value);;
238 char_ptr[value_length]= 0;
239 char_ptr[value_length +1]= 0;
240 memcached_string_set_length(&result->value, value_length);
241 }
242
243 return MEMCACHED_SUCCESS;
244
245 read_error:
246 memcached_io_reset(ptr);
247
248 return MEMCACHED_PARTIAL_READ;
249 }
250
251 static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
252 char *buffer, size_t buffer_length,
253 memcached_result_st *result)
254 {
255 size_t total_read;
256 memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
257
258 if (memcached_failed(rc))
259 {
260 return rc;
261 }
262
263 switch(buffer[0])
264 {
265 case 'V': /* VALUE || VERSION */
266 if (buffer[1] == 'A') /* VALUE */
267 {
268 /* We add back in one because we will need to search for END */
269 memcached_server_response_increment(ptr);
270 return textual_value_fetch(ptr, buffer, result);
271 }
272 else if (buffer[1] == 'E') /* VERSION */
273 {
274 return MEMCACHED_SUCCESS;
275 }
276 else
277 {
278 WATCHPOINT_STRING(buffer);
279 return MEMCACHED_UNKNOWN_READ_FAILURE;
280 }
281 case 'O': /* OK */
282 return MEMCACHED_SUCCESS;
283 case 'S': /* STORED STATS SERVER_ERROR */
284 {
285 if (buffer[2] == 'A') /* STORED STATS */
286 {
287 memcached_server_response_increment(ptr);
288 return MEMCACHED_STAT;
289 }
290 else if (buffer[1] == 'E') /* SERVER_ERROR */
291 {
292 if (total_read == memcached_literal_param_size("SERVER_ERROR"))
293 {
294 return MEMCACHED_SERVER_ERROR;
295 }
296
297 if (total_read > memcached_literal_param_size("SERVER_ERROR object too large for cache") and
298 (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache")) == 0))
299 {
300 return MEMCACHED_E2BIG;
301 }
302
303 // Move past the basic error message and whitespace
304 char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
305 if (startptr[0] == ' ')
306 {
307 startptr++;
308 }
309
310 char *endptr= startptr;
311 while (*endptr != '\r' && *endptr != '\n') endptr++;
312
313 return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
314 }
315 else if (buffer[1] == 'T')
316 {
317 return MEMCACHED_STORED;
318 }
319 else
320 {
321 WATCHPOINT_STRING(buffer);
322 return MEMCACHED_UNKNOWN_READ_FAILURE;
323 }
324 }
325 case 'D': /* DELETED */
326 return MEMCACHED_DELETED;
327
328 case 'N': /* NOT_FOUND */
329 {
330 if (buffer[4] == 'F')
331 return MEMCACHED_NOTFOUND;
332 else if (buffer[4] == 'S')
333 return MEMCACHED_NOTSTORED;
334 else
335 {
336 WATCHPOINT_STRING(buffer);
337 return MEMCACHED_UNKNOWN_READ_FAILURE;
338 }
339 }
340 case 'E': /* PROTOCOL ERROR or END */
341 {
342 if (buffer[1] == 'N')
343 return MEMCACHED_END;
344 else if (buffer[1] == 'R')
345 return MEMCACHED_PROTOCOL_ERROR;
346 else if (buffer[1] == 'X')
347 return MEMCACHED_DATA_EXISTS;
348 else
349 {
350 WATCHPOINT_STRING(buffer);
351 return MEMCACHED_UNKNOWN_READ_FAILURE;
352 }
353
354 }
355 case 'I': /* CLIENT ERROR */
356 /* We add back in one because we will need to search for END */
357 memcached_server_response_increment(ptr);
358 return MEMCACHED_ITEM;
359 case 'C': /* CLIENT ERROR */
360 return MEMCACHED_CLIENT_ERROR;
361 default:
362 {
363 unsigned long long auto_return_value;
364
365 if (sscanf(buffer, "%llu", &auto_return_value) == 1)
366 return MEMCACHED_SUCCESS;
367
368 WATCHPOINT_STRING(buffer);
369 return MEMCACHED_UNKNOWN_READ_FAILURE;
370 }
371 }
372
373 /* NOTREACHED */
374 }
375
376 static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr,
377 char *buffer, size_t buffer_length,
378 memcached_result_st *result)
379 {
380 memcached_return_t rc;
381 protocol_binary_response_header header;
382
383 if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS)
384 {
385 WATCHPOINT_ERROR(rc);
386 return rc;
387 }
388
389 if (header.response.magic != PROTOCOL_BINARY_RES)
390 {
391 return MEMCACHED_PROTOCOL_ERROR;
392 }
393
394 /*
395 ** Convert the header to host local endian!
396 */
397 header.response.keylen= ntohs(header.response.keylen);
398 header.response.status= ntohs(header.response.status);
399 header.response.bodylen= ntohl(header.response.bodylen);
400 header.response.cas= memcached_ntohll(header.response.cas);
401 uint32_t bodylen= header.response.bodylen;
402
403 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS or
404 header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
405 {
406 switch (header.response.opcode)
407 {
408 case PROTOCOL_BINARY_CMD_GETKQ:
409 /*
410 * We didn't increment the response counter for the GETKQ packet
411 * (only the final NOOP), so we need to increment the counter again.
412 */
413 memcached_server_response_increment(ptr);
414 /* FALLTHROUGH */
415 case PROTOCOL_BINARY_CMD_GETK:
416 {
417 uint16_t keylen= header.response.keylen;
418 memcached_result_reset(result);
419 result->item_cas= header.response.cas;
420
421 if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS)
422 {
423 WATCHPOINT_ERROR(rc);
424 return MEMCACHED_UNKNOWN_READ_FAILURE;
425 }
426
427 result->item_flags= ntohl(result->item_flags);
428 bodylen -= header.response.extlen;
429
430 result->key_length= keylen;
431 if (memcached_failed(rc= memcached_safe_read(ptr, result->item_key, keylen)))
432 {
433 WATCHPOINT_ERROR(rc);
434 return MEMCACHED_UNKNOWN_READ_FAILURE;
435 }
436
437 // Only bother with doing this if key_length > 0
438 if (result->key_length)
439 {
440 if (memcached_array_size(ptr->root->_namespace) and memcached_array_size(ptr->root->_namespace) >= result->key_length)
441 {
442 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
443 }
444
445 if (memcached_array_size(ptr->root->_namespace))
446 {
447 result->key_length-= memcached_array_size(ptr->root->_namespace);
448 memmove(result->item_key, result->item_key +memcached_array_size(ptr->root->_namespace), result->key_length);
449 }
450 }
451
452 bodylen -= keylen;
453 if (memcached_failed(memcached_string_check(&result->value, bodylen)))
454 {
455 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
456 }
457
458 char *vptr= memcached_string_value_mutable(&result->value);
459 if (memcached_failed(rc= memcached_safe_read(ptr, vptr, bodylen)))
460 {
461 WATCHPOINT_ERROR(rc);
462 return MEMCACHED_UNKNOWN_READ_FAILURE;
463 }
464
465 memcached_string_set_length(&result->value, bodylen);
466 }
467 break;
468
469 case PROTOCOL_BINARY_CMD_INCREMENT:
470 case PROTOCOL_BINARY_CMD_DECREMENT:
471 {
472 if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
473 {
474 return MEMCACHED_PROTOCOL_ERROR;
475 }
476
477 WATCHPOINT_ASSERT(bodylen == buffer_length);
478 uint64_t val;
479 if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
480 {
481 WATCHPOINT_ERROR(rc);
482 return MEMCACHED_UNKNOWN_READ_FAILURE;
483 }
484
485 val= memcached_ntohll(val);
486 memcpy(buffer, &val, sizeof(val));
487 }
488 break;
489
490 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
491 case PROTOCOL_BINARY_CMD_VERSION:
492 {
493 memset(buffer, 0, buffer_length);
494 if (bodylen >= buffer_length)
495 {
496 /* not enough space in buffer.. should not happen... */
497 return MEMCACHED_UNKNOWN_READ_FAILURE;
498 }
499 else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS)
500 {
501 WATCHPOINT_ERROR(rc);
502 return MEMCACHED_UNKNOWN_READ_FAILURE;
503 }
504 }
505 break;
506 case PROTOCOL_BINARY_CMD_FLUSH:
507 case PROTOCOL_BINARY_CMD_QUIT:
508 case PROTOCOL_BINARY_CMD_SET:
509 case PROTOCOL_BINARY_CMD_ADD:
510 case PROTOCOL_BINARY_CMD_REPLACE:
511 case PROTOCOL_BINARY_CMD_APPEND:
512 case PROTOCOL_BINARY_CMD_PREPEND:
513 case PROTOCOL_BINARY_CMD_DELETE:
514 {
515 WATCHPOINT_ASSERT(bodylen == 0);
516 return MEMCACHED_SUCCESS;
517 }
518 case PROTOCOL_BINARY_CMD_NOOP:
519 {
520 WATCHPOINT_ASSERT(bodylen == 0);
521 return MEMCACHED_END;
522 }
523 case PROTOCOL_BINARY_CMD_STAT:
524 {
525 if (bodylen == 0)
526 {
527 return MEMCACHED_END;
528 }
529 else if (bodylen + 1 > buffer_length)
530 {
531 /* not enough space in buffer.. should not happen... */
532 return MEMCACHED_UNKNOWN_READ_FAILURE;
533 }
534 else
535 {
536 size_t keylen= header.response.keylen;
537 memset(buffer, 0, buffer_length);
538 if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS ||
539 (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS)
540 {
541 WATCHPOINT_ERROR(rc);
542 return MEMCACHED_UNKNOWN_READ_FAILURE;
543 }
544 }
545 }
546 break;
547
548 case PROTOCOL_BINARY_CMD_SASL_AUTH:
549 case PROTOCOL_BINARY_CMD_SASL_STEP:
550 {
551 memcached_result_reset(result);
552 result->item_cas= header.response.cas;
553
554 if (memcached_string_check(&result->value,
555 bodylen) != MEMCACHED_SUCCESS)
556 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
557
558 char *vptr= memcached_string_value_mutable(&result->value);
559 if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS)
560 {
561 WATCHPOINT_ERROR(rc);
562 return MEMCACHED_UNKNOWN_READ_FAILURE;
563 }
564
565 memcached_string_set_length(&result->value, bodylen);
566 }
567 break;
568 default:
569 {
570 /* Command not implemented yet! */
571 WATCHPOINT_ASSERT(0);
572 return MEMCACHED_PROTOCOL_ERROR;
573 }
574 }
575 }
576 else if (header.response.bodylen)
577 {
578 /* What should I do with the error message??? just discard it for now */
579 char hole[SMALL_STRING_LEN];
580 while (bodylen > 0)
581 {
582 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
583 if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS)
584 {
585 WATCHPOINT_ERROR(rc);
586 return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
587 }
588 bodylen-= (uint32_t) nr;
589 }
590
591 /* This might be an error from one of the quiet commands.. if
592 * so, just throw it away and get the next one. What about creating
593 * a callback to the user with the error information?
594 */
595 switch (header.response.opcode)
596 {
597 case PROTOCOL_BINARY_CMD_SETQ:
598 case PROTOCOL_BINARY_CMD_ADDQ:
599 case PROTOCOL_BINARY_CMD_REPLACEQ:
600 case PROTOCOL_BINARY_CMD_APPENDQ:
601 case PROTOCOL_BINARY_CMD_PREPENDQ:
602 return binary_read_one_response(ptr, buffer, buffer_length, result);
603
604 default:
605 break;
606 }
607 }
608
609 rc= MEMCACHED_SUCCESS;
610 if (header.response.status != 0)
611 {
612 switch (header.response.status)
613 {
614 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
615 rc= MEMCACHED_NOTFOUND;
616 break;
617
618 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
619 rc= MEMCACHED_DATA_EXISTS;
620 break;
621
622 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
623 rc= MEMCACHED_NOTSTORED;
624 break;
625
626 case PROTOCOL_BINARY_RESPONSE_E2BIG:
627 rc= MEMCACHED_E2BIG;
628 break;
629
630 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
631 rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
632 break;
633
634 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
635 rc= MEMCACHED_AUTH_CONTINUE;
636 break;
637
638 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
639 rc= MEMCACHED_AUTH_FAILURE;
640 break;
641
642 case PROTOCOL_BINARY_RESPONSE_EINVAL:
643 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
644 default:
645 /* @todo fix the error mappings */
646 rc= MEMCACHED_PROTOCOL_ERROR;
647 break;
648 }
649 }
650
651 return rc;
652 }