END_TEST START_TEST (test_safe_strtoull) { uint64_t val; fail_unless(safe_strtoull("123", &val), "Failed parsing 123"); fail_unless(val == 123, "Didn't parse 123 to 123"); fail_unless(safe_strtoull("+123", &val), "Failed parsing +123"); fail_unless(val == 123, "Didn't parse +123 to 123"); fail_if(safe_strtoull("", &val), "Unexpectedly parsed an empty string."); fail_if(safe_strtoull("123BOGUS", &val), "Parsed 123BOGUS"); fail_if(safe_strtoull("92837498237498237498029383", &val), "Parsed out of range value."); /* extremes: */ fail_unless(safe_strtoull("18446744073709551615", &val), "Failed parsing 18446744073709551615"); /* 2**64 - 1 */ fail_unless(val == 18446744073709551615ULL, "18446744073709551615 != 18446744073709551615ULL"); fail_if(safe_strtoull("18446744073709551616", &val), "Parsed 18446744073709551616"); /* 2**64 */ fail_if(safe_strtoull("-1", &val), "Parsed a negative number as unsigned."); }
/* * adds a delta value to a numeric item. * * c connection requesting the operation * it item to adjust * incr true to increment value, false to decrement * delta amount to adjust value by * @param ritem The resulting item after adding the delta. Only valid if * ENGINE_SUCCESS is returned. Caller is responsible for calling * do_item_release() on this when finished with it. * * returns a response code to send back to the client. */ static ENGINE_ERROR_CODE do_add_delta(struct default_engine *engine, hash_item *it, const bool incr, const int64_t delta, item** ritem, uint64_t *result, const void *cookie) { const char *ptr; uint64_t value; char buf[80]; int res; if (it->nbytes >= (sizeof(buf) - 1)) { return ENGINE_EINVAL; } ptr = item_get_data(it); memcpy(buf, ptr, it->nbytes); buf[it->nbytes] = '\0'; if (!safe_strtoull(buf, &value)) { return ENGINE_EINVAL; } if (incr) { value += delta; } else { if ((uint64_t)delta > value) { value = 0; } else { value -= delta; } } *result = value; res = snprintf(buf, sizeof(buf), "%" PRIu64, value); if (res < 0 || res >= sizeof(buf)) { return ENGINE_EINVAL; } if (it->refcount == 1 && res <= (int)it->nbytes) { /* we can do inline replacement */ memcpy(item_get_data(it), buf, res); memset(item_get_data(it) + res, ' ', it->nbytes - res); item_set_cas(NULL, NULL, it, get_cas_id()); *ritem = it; } else { hash_item *new_it = do_item_alloc(engine, item_get_key(it), it->flags, it->exptime, res, cookie, it->datatype); if (new_it == NULL) { do_item_unlink(engine, it); return ENGINE_ENOMEM; } memcpy(item_get_data(new_it), buf, res); do_item_replace(engine, it, new_it); *ritem = new_it; } return ENGINE_SUCCESS; }
bool protocol_stats_merge_smallest(char *v1, int v1len, char *v2, int v2len, char *out, int outlen) { int dot = count_dot_pair(v1, v1len, v2, v2len); if (dot > 0) { float v1f = strtof(v1, NULL); float v2f = strtof(v2, NULL); sprintf(out, "%f", (v1f > v2f ? v1f : v2f)); return true; } else { uint64_t v1i = 0; uint64_t v2i = 0; if (safe_strtoull(v1, &v1i) && safe_strtoull(v2, &v2i)) { sprintf(out, "%"PRIu64"", (v1i > v2i ? v1i : v2i)); return true; } } (void)outlen; return false; }
static enum test_return test_safe_strtoull(void) { uint64_t val; assert(safe_strtoull("123", &val)); assert(val == 123); assert(safe_strtoull("+123", &val)); assert(val == 123); assert(!safe_strtoull("", &val)); /* empty */ assert(!safe_strtoull("123BOGUS", &val)); /* non-numeric */ assert(!safe_strtoull("92837498237498237498029383", &val)); /* out of range */ /* extremes: */ assert(safe_strtoull("18446744073709551615", &val)); /* 2**64 - 1 */ assert(val == 18446744073709551615ULL); assert(!safe_strtoull("18446744073709551616", &val)); /* 2**64 */ assert(!safe_strtoull("-1", &val)); /* negative */ return TEST_PASS; }
void cproxy_process_a2a_downstream(conn *c, char *line) { assert(c != NULL); assert(c->next == NULL); assert(c->extra != NULL); assert(c->cmd == -1); assert(c->item == NULL); assert(line != NULL); assert(line == c->rcurr); assert(IS_ASCII(c->protocol)); assert(IS_PROXY(c->protocol)); if (settings.verbose > 1) fprintf(stderr, "<%d cproxy_process_a2a_downstream %s\n", c->sfd, line); downstream *d = c->extra; assert(d != NULL); assert(d->ptd != NULL); assert(d->ptd->proxy != NULL); if (strncmp(line, "VALUE ", 6) == 0) { token_t tokens[MAX_TOKENS]; size_t ntokens; unsigned int flags; int clen = 0; int vlen; uint64_t cas = CPROXY_NOT_CAS; ntokens = scan_tokens(line, tokens, MAX_TOKENS, &clen); if (ntokens >= 5 && // Accounts for extra termimation token. ntokens <= 6 && tokens[KEY_TOKEN].length <= KEY_MAX_LENGTH && safe_strtoul(tokens[2].value, (uint32_t *) &flags) && safe_strtoul(tokens[3].value, (uint32_t *) &vlen)) { char *key = tokens[KEY_TOKEN].value; size_t nkey = tokens[KEY_TOKEN].length; item *it = item_alloc(key, nkey, flags, 0, vlen + 2); if (it != NULL) { if (ntokens == 5 || safe_strtoull(tokens[4].value, &cas)) { ITEM_set_cas(it, cas); c->item = it; c->ritem = ITEM_data(it); c->rlbytes = it->nbytes; c->cmd = -1; conn_set_state(c, conn_nread); return; // Success. } else { if (settings.verbose > 1) fprintf(stderr, "cproxy could not parse cas\n"); } } else { if (settings.verbose > 1) fprintf(stderr, "cproxy could not item_alloc size %u\n", vlen + 2); } if (it != NULL) item_remove(it); it = NULL; c->sbytes = vlen + 2; // Number of bytes to swallow. conn_set_state(c, conn_swallow); // Note, eventually, we'll see an END later. } else { // We don't know how much to swallow, so close the downstream. // The conn_closing should release the downstream, // which should write a suffix/error to the upstream. // conn_set_state(c, conn_closing); } } else if (strncmp(line, "END", 3) == 0) { conn_set_state(c, conn_pause); } else if (strncmp(line, "OK", 2) == 0) { conn_set_state(c, conn_pause); // TODO: Handle flush_all's expiration parameter against // the front_cache. // // TODO: We flush the front_cache too often, inefficiently // on every downstream flush_all OK response, rather than // on just the last flush_all OK response. // conn *uc = d->upstream_conn; if (uc != NULL && uc->cmd_curr == PROTOCOL_BINARY_CMD_FLUSH) { mcache_flush_all(&d->ptd->proxy->front_cache, 0); } } else if (strncmp(line, "STAT ", 5) == 0 || strncmp(line, "ITEM ", 5) == 0 || strncmp(line, "PREFIX ", 7) == 0) { assert(d->merger != NULL); conn *uc = d->upstream_conn; if (uc != NULL) { assert(uc->next == NULL); if (protocol_stats_merge_line(d->merger, line) == false) { // Forward the line as-is if we couldn't merge it. // int nline = strlen(line); item *it = item_alloc("s", 1, 0, 0, nline + 2); if (it != NULL) { strncpy(ITEM_data(it), line, nline); strncpy(ITEM_data(it) + nline, "\r\n", 2); if (add_conn_item(uc, it)) { add_iov(uc, ITEM_data(it), nline + 2); it = NULL; } if (it != NULL) item_remove(it); } } } conn_set_state(c, conn_new_cmd); } else { conn_set_state(c, conn_pause); // The upstream conn might be NULL when closed already // or while handling a noreply. // conn *uc = d->upstream_conn; if (uc != NULL) { assert(uc->next == NULL); out_string(uc, line); if (!update_event(uc, EV_WRITE | EV_PERSIST)) { if (settings.verbose > 1) fprintf(stderr, "Can't update upstream write event\n"); d->ptd->stats.stats.err_oom++; cproxy_close_conn(uc); } cproxy_del_front_cache_key_ascii_response(d, line, uc->cmd_start); } } }
static int parseFirstLineRequest(fallocator_t fallocator, requestParserImpl_t* pParser, char** tokens, int ntokens) { int returnValue = 0; if (ntokens >= 2 && (((strcmp(tokens[0], "get") == 0) && (pParser->pCommand->command = COMMAND_GET)) || ((strcmp(tokens[0], "bget") == 0) && (pParser->pCommand->command = COMMAND_BGET)))) { if (ntokens > 2) { //copy the keys ... pParser->pCommand->multiGetKeys = fallocatorMalloc(fallocator, (ntokens-1) * sizeof(char*)); IfTrue(pParser->pCommand->multiGetKeys, WARN, "Error allocating memory"); pParser->pCommand->multiGetKeysCount = ntokens -1 ; for (int i = 1; i < ntokens; i++) { pParser->pCommand->multiGetKeys[i-1] = tokens[i]; tokens[i] = 0; } }else { pParser->pCommand->key = tokens[1]; pParser->pCommand->keySize = strlen(tokens[1]); tokens[1] = 0; } } else if ((ntokens == 5 || ntokens == 6) && ((strcmp(tokens[0], "add") == 0 && (pParser->pCommand->command = COMMAND_ADD)) || (strcmp(tokens[0], "set") == 0 && (pParser->pCommand->command = COMMAND_SET)) || (strcmp( tokens[0], "replace") == 0 && (pParser->pCommand->command = COMMAND_REPLACE)) || (strcmp(tokens[0], "prepend") == 0 && (pParser->pCommand->command = COMMAND_PREPEND)) || (strcmp(tokens[0], "append") == 0 && (pParser->pCommand->command = COMMAND_APPEND)))) { pParser->pCommand->key = tokens[1]; pParser->pCommand->keySize = strlen(tokens[1]); tokens[1] = 0; IfTrue(safe_strtoul(tokens[2], &pParser->pCommand->flags), INFO, "Error parsing flags"); IfTrue(safe_strtoul(tokens[3], &pParser->pCommand->expiryTime), INFO, "Error parsing expiry time "); IfTrue(safe_strtoul(tokens[4], &pParser->pCommand->dataLength), INFO, "Error parsing data length"); if (tokens[5] != NULL) { if (strcmp(tokens[5], "noreply") == 0) { pParser->pCommand->noreply = 1; } } } else if ((ntokens == 6 || ntokens == 7) && (strcmp(tokens[0], "cas") == 0)) { pParser->pCommand->command = COMMAND_CAS; pParser->pCommand->key = tokens[1]; pParser->pCommand->keySize = strlen(tokens[1]); tokens[1] = 0; IfTrue(safe_strtoul(tokens[2], &pParser->pCommand->flags), INFO, "Error parsing flags"); IfTrue(safe_strtoul(tokens[3], &pParser->pCommand->expiryTime), INFO, "Error parsing expiry time "); IfTrue(safe_strtoul(tokens[4], &pParser->pCommand->dataLength), INFO, "Error parsing data length"); IfTrue(safe_strtoull(tokens[5], &pParser->pCommand->cas), INFO, "Error parsing cas id"); if (tokens[6] != NULL) { if (strcmp(tokens[6], "noreply") == 0) { pParser->pCommand->noreply = 1; } } } else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[0], "incr") == 0)) { pParser->pCommand->command = COMMAND_INCR; pParser->pCommand->key = tokens[1]; pParser->pCommand->keySize = strlen(tokens[1]); tokens[1] = 0; IfTrue(safe_strtoull(tokens[2], &pParser->pCommand->delta), INFO, "Error parsing delta"); if (tokens[3] != NULL) { if (strcmp(tokens[3], "noreply") == 0) { pParser->pCommand->noreply = 1; } } } else if (ntokens >= 2 && (strcmp(tokens[0], "gets") == 0)) { pParser->pCommand->command = COMMAND_GETS; pParser->pCommand->key = tokens[1]; pParser->pCommand->keySize = strlen(tokens[1]); tokens[1] = 0; //TODO - only suport one key per get for now } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[0], "decr") == 0)) { pParser->pCommand->command = COMMAND_DECR; pParser->pCommand->key = tokens[1]; pParser->pCommand->keySize = strlen(tokens[1]); tokens[1] = 0; IfTrue(safe_strtoull(tokens[2], &pParser->pCommand->delta), INFO, "Error parsing delta"); if (tokens[3] != NULL) { if (strcmp(tokens[3], "noreply") == 0) { pParser->pCommand->noreply = 1; } } } else if (ntokens >= 2 && ntokens <= 4 && (strcmp(tokens[0], "delete") == 0)) { pParser->pCommand->command = COMMAND_DELETE; pParser->pCommand->key = tokens[1]; pParser->pCommand->keySize = strlen(tokens[1]); tokens[1] = 0; if (tokens[2] != NULL) { if (strcmp(tokens[2], "noreply") == 0) { pParser->pCommand->noreply = 1; } } } else if (ntokens >= 2 && (strcmp(tokens[0], "stats") == 0)) { pParser->pCommand->command = COMMAND_STATS; //TODO - later } else if (ntokens >= 1 && ntokens <= 2 && (strcmp(tokens[0], "flush_all") == 0)) { pParser->pCommand->command = COMMAND_FLUSH_ALL; //TODO - later } else if (ntokens == 1 && (strcmp(tokens[0], "version") == 0)) { pParser->pCommand->command = COMMAND_VERSION; //TODO - later } else if (ntokens == 1 && (strcmp(tokens[0], "quit") == 0)) { pParser->pCommand->command = COMMAND_QUIT; //TODO - later } else if ((ntokens == 2 || ntokens == 3) && (strcmp(tokens[0], "verbosity") == 0)) { pParser->pCommand->command = COMMAND_VERBOSITY; IfTrue(safe_strtoul(tokens[2], &pParser->pCommand->flags), INFO, "Error parsing verosity level"); } else { //this is error } goto OnSuccess; OnError: returnValue = -1; OnSuccess: return returnValue; }