5454#include < libmemcached/common.h>
5555#include < libmemcached/string.hpp>
5656
57+ static size_t tokenize (char *buffer, size_t length, char **tokens, size_t max_tokens)
58+ {
59+ if (buffer[length - 2 ] != ' \r ' || buffer[length - 1 ] != ' \n ' )
60+ return 0 ;
61+ memset (buffer + length - 2 , 0 , 2 );
62+
63+ char *saveptr;
64+ char *token= strtok_r (buffer, " " , &saveptr);
65+ size_t ntoken= 0 ;
66+ while (token != NULL )
67+ {
68+ if (ntoken >= max_tokens) return max_tokens + 1 ;
69+ tokens[ntoken++]= token;
70+ token= strtok_r (NULL , " " , &saveptr);
71+ }
72+ return ntoken;
73+ }
74+
75+ static bool safe_strtoui (char *str, void *out, uintmax_t max_num)
76+ {
77+ /* string to unsinged integer */
78+ errno= 0 ;
79+ char *endptr;
80+ *(uintmax_t *)out= 0 ;
81+ uintmax_t num= strtoull (str, &endptr, 10 );
82+ if (errno == ERANGE || *endptr != ' \0 ' || num > max_num)
83+ return false ;
84+ *(uintmax_t *)out= num;
85+ return true ;
86+ }
87+
5788static memcached_return_t textual_value_fetch (memcached_server_write_instance_st ptr,
5889 char *buffer, size_t buffer_length,
5990 memcached_result_st *result)
6091{
6192 char *string_ptr;
62- char *end_ptr;
63- char *next_ptr;
64- size_t value_length;
65- size_t to_read;
66- ssize_t read_length= 0 ;
93+ ssize_t value_length;
94+ ssize_t read_length;
6795
6896 if (ptr->root ->flags .use_udp )
6997 return memcached_set_error (*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
7098
7199 WATCHPOINT_ASSERT (ptr->root );
72- end_ptr= buffer + buffer_length;
73100
74101 memcached_result_reset (result);
75102
76- string_ptr= buffer;
77- string_ptr+= 6 ; /* "VALUE " */
103+ string_ptr= buffer + 6 ; /* "VALUE " */
104+
105+ char *tokens[4 ]= {NULL , };
106+ size_t ntoken= tokenize (string_ptr, buffer_length - 6 , tokens, 4 );
107+ if (ntoken < 3 || ntoken > 4 )
108+ return MEMCACHED_PARTIAL_READ;
78109
79110 /* We load the key */
80111 {
81- char *key= result->item_key ;
82- size_t key_length= 0 ;
83-
84- string_ptr += memcached_array_size (ptr->root ->_namespace ); /* prefix length */
85-
86- while (!iscntrl (*string_ptr) && !isspace (*string_ptr)) {
87- if (key_length < MEMCACHED_MAX_KEY) {
88- key[key_length]= *string_ptr;
89- }
90- key_length++;
91- string_ptr++;
92- }
93-
94- if (key_length < MEMCACHED_MAX_KEY) {
95- key[key_length]= 0 ;
96- result->key_length = key_length;
97- } else {
98- snprintf (key + MEMCACHED_MAX_KEY - 4 , 4 , " ..." );
99- result->key_length = MEMCACHED_MAX_KEY - 1 ;
112+ char *key= tokens[0 ] + memcached_array_size (ptr->root ->_namespace );
113+ int key_length= snprintf (result->item_key , MEMCACHED_MAX_KEY, " %s" , key);
114+ if (key_length >= MEMCACHED_MAX_KEY)
115+ {
116+ snprintf (result->item_key + MEMCACHED_MAX_KEY - 4 , 4 , " ..." );
117+ key_length= MEMCACHED_MAX_KEY - 1 ;
100118 memcached_set_error (*ptr, MEMCACHED_KEY_TOO_BIG, MEMCACHED_AT);
101119 }
120+ result->key_length = (size_t )key_length;
102121 }
103122
104- if (end_ptr == string_ptr)
105- goto read_error;
106-
107- /* Flags fetch move past space */
108- string_ptr++;
109- if (end_ptr == string_ptr)
110- goto read_error;
111-
112- for (next_ptr= string_ptr; isdigit (*string_ptr); string_ptr++) {};
113- result->item_flags = (uint32_t ) strtoul (next_ptr, &string_ptr, 10 );
114-
115- if (end_ptr == string_ptr)
116- goto read_error;
117-
118- /* Length fetch move past space*/
119- string_ptr++;
120- if (end_ptr == string_ptr)
121- goto read_error;
122-
123- for (next_ptr= string_ptr; isdigit (*string_ptr); string_ptr++) {};
124- value_length= (size_t )strtoull (next_ptr, &string_ptr, 10 );
125-
126- if (end_ptr == string_ptr)
127- goto read_error;
128-
129- /* Skip spaces */
130- if (*string_ptr == ' \r ' )
123+ if (!safe_strtoui (tokens[1 ], (void *)&result->item_flags , UINT32_MAX) ||
124+ !safe_strtoui (tokens[2 ], (void *)&value_length, SSIZE_MAX) ||
125+ (ntoken == 4 && !safe_strtoui (tokens[3 ], (void *)&result->item_cas , ULLONG_MAX)))
131126 {
132- /* Skip past the \r\n */
133- string_ptr+= 2 ;
134- }
135- else
136- {
137- string_ptr++;
138- for (next_ptr= string_ptr; isdigit (*string_ptr); string_ptr++) {};
139- result->item_cas = strtoull (next_ptr, &string_ptr, 10 );
127+ return MEMCACHED_PROTOCOL_ERROR;
140128 }
141129
142- if (end_ptr < string_ptr)
143- goto read_error;
144-
145130 /* We add two bytes so that we can walk the \r\n */
146131 if (memcached_failed (memcached_string_check (&result->value , value_length +2 )))
147132 {
@@ -157,7 +142,7 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
157142 We are null terminating through, which will most likely make
158143 some people lazy about using the return length.
159144 */
160- to_read= (value_length) + 2 ;
145+ size_t to_read= (value_length) + 2 ;
161146 memcached_return_t rrc= memcached_io_read (ptr, value_ptr, to_read, &read_length);
162147 if (memcached_failed (rrc))
163148 {
@@ -167,26 +152,18 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
167152 }
168153 return rrc;
169154 }
170- }
171155
172- if (read_length != ( ssize_t )( value_length + 2 ) )
173- {
174- goto read_error ;
175- }
156+ if (read_length != value_length + 2 )
157+ {
158+ return MEMCACHED_PARTIAL_READ ;
159+ }
176160
177- /* This next bit blows the API, but this is internal....*/
178- {
179- char *char_ptr;
180- char_ptr= memcached_string_value_mutable (&result->value );;
181- char_ptr[value_length]= 0 ;
182- char_ptr[value_length +1 ]= 0 ;
161+ /* Replace the last "\r\n" characters with '\0' */
162+ memset (value_ptr + value_length, 0 , 2 );
183163 memcached_string_set_length (&result->value , value_length);
184164 }
185165
186166 return MEMCACHED_SUCCESS;
187-
188- read_error:
189- return MEMCACHED_PARTIAL_READ;
190167}
191168
192169static memcached_return_t textual_version_fetch (memcached_server_write_instance_st instance, char *buffer)
@@ -359,7 +336,7 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
359336 {
360337 /* We add back in one because we will need to search for END */
361338 memcached_server_response_increment (ptr);
362- return textual_value_fetch (ptr, buffer, buffer_length , result);
339+ return textual_value_fetch (ptr, buffer, total_read , result);
363340 }
364341 else if (memcmp (buffer, " VERSION" , 7 ) == 0 )
365342 {
0 commit comments