Skip to content

Commit da4fa79

Browse files
committed
INTERNAL: Refactor textual_value_fetch logic
1 parent 1f8f120 commit da4fa79

File tree

1 file changed

+58
-81
lines changed

1 file changed

+58
-81
lines changed

libmemcached/response.cc

Lines changed: 58 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -54,94 +54,79 @@
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+
5788
static 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

192169
static 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

Comments
 (0)