"Fossies" - the Fresh Open Source Software Archive

Member "memcached-1.6.9/proto_bin.c" (21 Nov 2020, 43438 Bytes) of package /linux/www/memcached-1.6.9.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "proto_bin.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.6.8_vs_1.6.9.

    1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
    2 /*
    3  * Functions for handling the binary protocol.
    4  * NOTE: The binary protocol is deprecated as of 1.6.0.
    5  */
    6 
    7 #include "memcached.h"
    8 #include "proto_bin.h"
    9 #include "storage.h"
   10 #ifdef TLS
   11 #include "tls.h"
   12 #endif
   13 #include <string.h>
   14 #include <stdlib.h>
   15 
   16 /** binprot handlers **/
   17 static void process_bin_flush(conn *c, char *extbuf);
   18 static void process_bin_append_prepend(conn *c);
   19 static void process_bin_update(conn *c, char *extbuf);
   20 static void process_bin_get_or_touch(conn *c, char *extbuf);
   21 static void process_bin_delete(conn *c);
   22 static void complete_incr_bin(conn *c, char *extbuf);
   23 static void process_bin_stat(conn *c);
   24 static void process_bin_sasl_auth(conn *c);
   25 static void dispatch_bin_command(conn *c, char *extbuf);
   26 static void complete_update_bin(conn *c);
   27 static void process_bin_complete_sasl_auth(conn *c);
   28 
   29 static void write_bin_miss_response(conn *c, char *key, size_t nkey);
   30 
   31 void complete_nread_binary(conn *c) {
   32     assert(c != NULL);
   33     assert(c->cmd >= 0);
   34 
   35     switch(c->substate) {
   36     case bin_read_set_value:
   37         complete_update_bin(c);
   38         break;
   39     case bin_reading_sasl_auth_data:
   40         process_bin_complete_sasl_auth(c);
   41         if (c->item) {
   42             do_item_remove(c->item);
   43             c->item = NULL;
   44         }
   45         break;
   46     default:
   47         fprintf(stderr, "Not handling substate %d\n", c->substate);
   48         assert(0);
   49     }
   50 }
   51 
   52 int try_read_command_binary(conn *c) {
   53     /* Do we have the complete packet header? */
   54     if (c->rbytes < sizeof(c->binary_header)) {
   55         /* need more data! */
   56         return 0;
   57     } else {
   58         memcpy(&c->binary_header, c->rcurr, sizeof(c->binary_header));
   59         protocol_binary_request_header* req;
   60         req = &c->binary_header;
   61 
   62         if (settings.verbose > 1) {
   63             /* Dump the packet before we convert it to host order */
   64             int ii;
   65             fprintf(stderr, "<%d Read binary protocol data:", c->sfd);
   66             for (ii = 0; ii < sizeof(req->bytes); ++ii) {
   67                 if (ii % 4 == 0) {
   68                     fprintf(stderr, "\n<%d   ", c->sfd);
   69                 }
   70                 fprintf(stderr, " 0x%02x", req->bytes[ii]);
   71             }
   72             fprintf(stderr, "\n");
   73         }
   74 
   75         c->binary_header = *req;
   76         c->binary_header.request.keylen = ntohs(req->request.keylen);
   77         c->binary_header.request.bodylen = ntohl(req->request.bodylen);
   78         c->binary_header.request.cas = ntohll(req->request.cas);
   79 
   80         if (c->binary_header.request.magic != PROTOCOL_BINARY_REQ) {
   81             if (settings.verbose) {
   82                 fprintf(stderr, "Invalid magic:  %x\n",
   83                         c->binary_header.request.magic);
   84             }
   85             conn_set_state(c, conn_closing);
   86             return -1;
   87         }
   88 
   89         uint8_t extlen = c->binary_header.request.extlen;
   90         uint16_t keylen = c->binary_header.request.keylen;
   91         if (c->rbytes < keylen + extlen + sizeof(c->binary_header)) {
   92             // Still need more bytes. Let try_read_network() realign the
   93             // read-buffer and fetch more data as necessary.
   94             return 0;
   95         }
   96 
   97         if (!resp_start(c)) {
   98             conn_set_state(c, conn_closing);
   99             return -1;
  100         }
  101 
  102         c->cmd = c->binary_header.request.opcode;
  103         c->keylen = c->binary_header.request.keylen;
  104         c->opaque = c->binary_header.request.opaque;
  105         /* clear the returned cas value */
  106         c->cas = 0;
  107 
  108         c->last_cmd_time = current_time;
  109         // sigh. binprot has no "largest possible extlen" define, and I don't
  110         // want to refactor a ton of code either. Header is only ever used out
  111         // of c->binary_header, but the extlen stuff is used for the latter
  112         // bytes. Just wastes 24 bytes on the stack this way.
  113         char extbuf[sizeof(c->binary_header) + BIN_MAX_EXTLEN+1];
  114         memcpy(extbuf + sizeof(c->binary_header), c->rcurr + sizeof(c->binary_header),
  115                 extlen > BIN_MAX_EXTLEN ? BIN_MAX_EXTLEN : extlen);
  116         c->rbytes -= sizeof(c->binary_header) + extlen + keylen;
  117         c->rcurr += sizeof(c->binary_header) + extlen + keylen;
  118 
  119         dispatch_bin_command(c, extbuf);
  120     }
  121 
  122     return 1;
  123 }
  124 
  125 /**
  126  * get a pointer to the key in this request
  127  */
  128 static char* binary_get_key(conn *c) {
  129     return c->rcurr - (c->binary_header.request.keylen);
  130 }
  131 
  132 static void add_bin_header(conn *c, uint16_t err, uint8_t hdr_len, uint16_t key_len, uint32_t body_len) {
  133     protocol_binary_response_header* header;
  134     mc_resp *resp = c->resp;
  135 
  136     assert(c);
  137 
  138     resp_reset(resp);
  139 
  140     header = (protocol_binary_response_header *)resp->wbuf;
  141 
  142     header->response.magic = (uint8_t)PROTOCOL_BINARY_RES;
  143     header->response.opcode = c->binary_header.request.opcode;
  144     header->response.keylen = (uint16_t)htons(key_len);
  145 
  146     header->response.extlen = (uint8_t)hdr_len;
  147     header->response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES;
  148     header->response.status = (uint16_t)htons(err);
  149 
  150     header->response.bodylen = htonl(body_len);
  151     header->response.opaque = c->opaque;
  152     header->response.cas = htonll(c->cas);
  153 
  154     if (settings.verbose > 1) {
  155         int ii;
  156         fprintf(stderr, ">%d Writing bin response:", c->sfd);
  157         for (ii = 0; ii < sizeof(header->bytes); ++ii) {
  158             if (ii % 4 == 0) {
  159                 fprintf(stderr, "\n>%d  ", c->sfd);
  160             }
  161             fprintf(stderr, " 0x%02x", header->bytes[ii]);
  162         }
  163         fprintf(stderr, "\n");
  164     }
  165 
  166     resp->wbytes = sizeof(header->response);
  167     resp_add_iov(resp, resp->wbuf, resp->wbytes);
  168 }
  169 
  170 
  171 /**
  172  * Writes a binary error response. If errstr is supplied, it is used as the
  173  * error text; otherwise a generic description of the error status code is
  174  * included.
  175  */
  176 void write_bin_error(conn *c, protocol_binary_response_status err,
  177                             const char *errstr, int swallow) {
  178     size_t len;
  179 
  180     if (!errstr) {
  181         switch (err) {
  182         case PROTOCOL_BINARY_RESPONSE_ENOMEM:
  183             errstr = "Out of memory";
  184             break;
  185         case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
  186             errstr = "Unknown command";
  187             break;
  188         case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
  189             errstr = "Not found";
  190             break;
  191         case PROTOCOL_BINARY_RESPONSE_EINVAL:
  192             errstr = "Invalid arguments";
  193             break;
  194         case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
  195             errstr = "Data exists for key.";
  196             break;
  197         case PROTOCOL_BINARY_RESPONSE_E2BIG:
  198             errstr = "Too large.";
  199             break;
  200         case PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL:
  201             errstr = "Non-numeric server-side value for incr or decr";
  202             break;
  203         case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
  204             errstr = "Not stored.";
  205             break;
  206         case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
  207             errstr = "Auth failure.";
  208             break;
  209         default:
  210             assert(false);
  211             errstr = "UNHANDLED ERROR";
  212             fprintf(stderr, ">%d UNHANDLED ERROR: %d\n", c->sfd, err);
  213         }
  214     }
  215 
  216     if (settings.verbose > 1) {
  217         fprintf(stderr, ">%d Writing an error: %s\n", c->sfd, errstr);
  218     }
  219 
  220     len = strlen(errstr);
  221     add_bin_header(c, err, 0, 0, len);
  222     if (len > 0) {
  223         resp_add_iov(c->resp, errstr, len);
  224     }
  225     if (swallow > 0) {
  226         c->sbytes = swallow;
  227         conn_set_state(c, conn_swallow);
  228     } else {
  229         conn_set_state(c, conn_mwrite);
  230     }
  231 }
  232 
  233 /* Just write an error message and disconnect the client */
  234 static void handle_binary_protocol_error(conn *c) {
  235     write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, 0);
  236     if (settings.verbose) {
  237         fprintf(stderr, "Protocol error (opcode %02x), close connection %d\n",
  238                 c->binary_header.request.opcode, c->sfd);
  239     }
  240     c->close_after_write = true;
  241 }
  242 
  243 /* Form and send a response to a command over the binary protocol */
  244 static void write_bin_response(conn *c, void *d, int hlen, int keylen, int dlen) {
  245     if (!c->noreply || c->cmd == PROTOCOL_BINARY_CMD_GET ||
  246         c->cmd == PROTOCOL_BINARY_CMD_GETK) {
  247         add_bin_header(c, 0, hlen, keylen, dlen);
  248         mc_resp *resp = c->resp;
  249         if (dlen > 0) {
  250             resp_add_iov(resp, d, dlen);
  251         }
  252     }
  253 
  254     conn_set_state(c, conn_new_cmd);
  255 }
  256 
  257 static void complete_incr_bin(conn *c, char *extbuf) {
  258     item *it;
  259     char *key;
  260     size_t nkey;
  261     /* Weird magic in add_delta forces me to pad here */
  262     char tmpbuf[INCR_MAX_STORAGE_LEN];
  263     uint64_t cas = 0;
  264 
  265     protocol_binary_response_incr* rsp = (protocol_binary_response_incr*)c->resp->wbuf;
  266     protocol_binary_request_incr* req = (void *)extbuf;
  267 
  268     assert(c != NULL);
  269     //assert(c->wsize >= sizeof(*rsp));
  270 
  271     /* fix byteorder in the request */
  272     req->message.body.delta = ntohll(req->message.body.delta);
  273     req->message.body.initial = ntohll(req->message.body.initial);
  274     req->message.body.expiration = ntohl(req->message.body.expiration);
  275     key = binary_get_key(c);
  276     nkey = c->binary_header.request.keylen;
  277 
  278     if (settings.verbose > 1) {
  279         int i;
  280         fprintf(stderr, "incr ");
  281 
  282         for (i = 0; i < nkey; i++) {
  283             fprintf(stderr, "%c", key[i]);
  284         }
  285         fprintf(stderr, " %lld, %llu, %d\n",
  286                 (long long)req->message.body.delta,
  287                 (long long)req->message.body.initial,
  288                 req->message.body.expiration);
  289     }
  290 
  291     if (c->binary_header.request.cas != 0) {
  292         cas = c->binary_header.request.cas;
  293     }
  294     switch(add_delta(c, key, nkey, c->cmd == PROTOCOL_BINARY_CMD_INCREMENT,
  295                      req->message.body.delta, tmpbuf,
  296                      &cas)) {
  297     case OK:
  298         rsp->message.body.value = htonll(strtoull(tmpbuf, NULL, 10));
  299         if (cas) {
  300             c->cas = cas;
  301         }
  302         write_bin_response(c, &rsp->message.body, 0, 0,
  303                            sizeof(rsp->message.body.value));
  304         break;
  305     case NON_NUMERIC:
  306         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL, NULL, 0);
  307         break;
  308     case EOM:
  309         out_of_memory(c, "SERVER_ERROR Out of memory incrementing value");
  310         break;
  311     case DELTA_ITEM_NOT_FOUND:
  312         if (req->message.body.expiration != 0xffffffff) {
  313             /* Save some room for the response */
  314             rsp->message.body.value = htonll(req->message.body.initial);
  315 
  316             snprintf(tmpbuf, INCR_MAX_STORAGE_LEN, "%llu",
  317                 (unsigned long long)req->message.body.initial);
  318             int res = strlen(tmpbuf);
  319             it = item_alloc(key, nkey, 0, realtime(req->message.body.expiration),
  320                             res + 2);
  321 
  322             if (it != NULL) {
  323                 memcpy(ITEM_data(it), tmpbuf, res);
  324                 memcpy(ITEM_data(it) + res, "\r\n", 2);
  325 
  326                 if (store_item(it, NREAD_ADD, c)) {
  327                     c->cas = ITEM_get_cas(it);
  328                     write_bin_response(c, &rsp->message.body, 0, 0, sizeof(rsp->message.body.value));
  329                 } else {
  330                     write_bin_error(c, PROTOCOL_BINARY_RESPONSE_NOT_STORED,
  331                                     NULL, 0);
  332                 }
  333                 item_remove(it);         /* release our reference */
  334             } else {
  335                 out_of_memory(c,
  336                         "SERVER_ERROR Out of memory allocating new item");
  337             }
  338         } else {
  339             pthread_mutex_lock(&c->thread->stats.mutex);
  340             if (c->cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
  341                 c->thread->stats.incr_misses++;
  342             } else {
  343                 c->thread->stats.decr_misses++;
  344             }
  345             pthread_mutex_unlock(&c->thread->stats.mutex);
  346 
  347             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
  348         }
  349         break;
  350     case DELTA_ITEM_CAS_MISMATCH:
  351         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
  352         break;
  353     }
  354 }
  355 
  356 static void complete_update_bin(conn *c) {
  357     protocol_binary_response_status eno = PROTOCOL_BINARY_RESPONSE_EINVAL;
  358     enum store_item_type ret = NOT_STORED;
  359     assert(c != NULL);
  360 
  361     item *it = c->item;
  362     pthread_mutex_lock(&c->thread->stats.mutex);
  363     c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++;
  364     pthread_mutex_unlock(&c->thread->stats.mutex);
  365 
  366     /* We don't actually receive the trailing two characters in the bin
  367      * protocol, so we're going to just set them here */
  368     if ((it->it_flags & ITEM_CHUNKED) == 0) {
  369         *(ITEM_data(it) + it->nbytes - 2) = '\r';
  370         *(ITEM_data(it) + it->nbytes - 1) = '\n';
  371     } else {
  372         assert(c->ritem);
  373         item_chunk *ch = (item_chunk *) c->ritem;
  374         if (ch->size == ch->used)
  375             ch = ch->next;
  376         assert(ch->size - ch->used >= 2);
  377         ch->data[ch->used] = '\r';
  378         ch->data[ch->used + 1] = '\n';
  379         ch->used += 2;
  380     }
  381 
  382     ret = store_item(it, c->cmd, c);
  383 
  384 #ifdef ENABLE_DTRACE
  385     uint64_t cas = ITEM_get_cas(it);
  386     switch (c->cmd) {
  387     case NREAD_ADD:
  388         MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
  389                               (ret == STORED) ? it->nbytes : -1, cas);
  390         break;
  391     case NREAD_REPLACE:
  392         MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
  393                                   (ret == STORED) ? it->nbytes : -1, cas);
  394         break;
  395     case NREAD_APPEND:
  396         MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
  397                                  (ret == STORED) ? it->nbytes : -1, cas);
  398         break;
  399     case NREAD_PREPEND:
  400         MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
  401                                  (ret == STORED) ? it->nbytes : -1, cas);
  402         break;
  403     case NREAD_SET:
  404         MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
  405                               (ret == STORED) ? it->nbytes : -1, cas);
  406         break;
  407     }
  408 #endif
  409 
  410     switch (ret) {
  411     case STORED:
  412         /* Stored */
  413         write_bin_response(c, NULL, 0, 0, 0);
  414         break;
  415     case EXISTS:
  416         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
  417         break;
  418     case NOT_FOUND:
  419         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
  420         break;
  421     case NOT_STORED:
  422     case TOO_LARGE:
  423     case NO_MEMORY:
  424         if (c->cmd == NREAD_ADD) {
  425             eno = PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
  426         } else if(c->cmd == NREAD_REPLACE) {
  427             eno = PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
  428         } else {
  429             eno = PROTOCOL_BINARY_RESPONSE_NOT_STORED;
  430         }
  431         write_bin_error(c, eno, NULL, 0);
  432     }
  433 
  434     item_remove(c->item);       /* release the c->item reference */
  435     c->item = 0;
  436 }
  437 
  438 static void write_bin_miss_response(conn *c, char *key, size_t nkey) {
  439     if (nkey) {
  440         add_bin_header(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
  441                 0, nkey, nkey);
  442         char *ofs = c->resp->wbuf + sizeof(protocol_binary_response_header);
  443         memcpy(ofs, key, nkey);
  444         resp_add_iov(c->resp, ofs, nkey);
  445         conn_set_state(c, conn_new_cmd);
  446     } else {
  447         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
  448                         NULL, 0);
  449     }
  450 }
  451 
  452 static void process_bin_get_or_touch(conn *c, char *extbuf) {
  453     item *it;
  454 
  455     protocol_binary_response_get* rsp = (protocol_binary_response_get*)c->resp->wbuf;
  456     char* key = binary_get_key(c);
  457     size_t nkey = c->binary_header.request.keylen;
  458     int should_touch = (c->cmd == PROTOCOL_BINARY_CMD_TOUCH ||
  459                         c->cmd == PROTOCOL_BINARY_CMD_GAT ||
  460                         c->cmd == PROTOCOL_BINARY_CMD_GATK);
  461     int should_return_key = (c->cmd == PROTOCOL_BINARY_CMD_GETK ||
  462                              c->cmd == PROTOCOL_BINARY_CMD_GATK);
  463     int should_return_value = (c->cmd != PROTOCOL_BINARY_CMD_TOUCH);
  464     bool failed = false;
  465 
  466     if (settings.verbose > 1) {
  467         fprintf(stderr, "<%d %s ", c->sfd, should_touch ? "TOUCH" : "GET");
  468         if (fwrite(key, 1, nkey, stderr)) {}
  469         fputc('\n', stderr);
  470     }
  471 
  472     if (should_touch) {
  473         protocol_binary_request_touch *t = (void *)extbuf;
  474         time_t exptime = ntohl(t->message.body.expiration);
  475 
  476         it = item_touch(key, nkey, realtime(exptime), c);
  477     } else {
  478         it = item_get(key, nkey, c, DO_UPDATE);
  479     }
  480 
  481     if (it) {
  482         /* the length has two unnecessary bytes ("\r\n") */
  483         uint16_t keylen = 0;
  484         uint32_t bodylen = sizeof(rsp->message.body) + (it->nbytes - 2);
  485 
  486         pthread_mutex_lock(&c->thread->stats.mutex);
  487         if (should_touch) {
  488             c->thread->stats.touch_cmds++;
  489             c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
  490         } else {
  491             c->thread->stats.get_cmds++;
  492             c->thread->stats.lru_hits[it->slabs_clsid]++;
  493         }
  494         pthread_mutex_unlock(&c->thread->stats.mutex);
  495 
  496         if (should_touch) {
  497             MEMCACHED_COMMAND_TOUCH(c->sfd, ITEM_key(it), it->nkey,
  498                                     it->nbytes, ITEM_get_cas(it));
  499         } else {
  500             MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
  501                                   it->nbytes, ITEM_get_cas(it));
  502         }
  503 
  504         if (c->cmd == PROTOCOL_BINARY_CMD_TOUCH) {
  505             bodylen -= it->nbytes - 2;
  506         } else if (should_return_key) {
  507             bodylen += nkey;
  508             keylen = nkey;
  509         }
  510 
  511         add_bin_header(c, 0, sizeof(rsp->message.body), keylen, bodylen);
  512         rsp->message.header.response.cas = htonll(ITEM_get_cas(it));
  513 
  514         // add the flags
  515         FLAGS_CONV(it, rsp->message.body.flags);
  516         rsp->message.body.flags = htonl(rsp->message.body.flags);
  517         resp_add_iov(c->resp, &rsp->message.body, sizeof(rsp->message.body));
  518 
  519         if (should_return_key) {
  520             resp_add_iov(c->resp, ITEM_key(it), nkey);
  521         }
  522 
  523         if (should_return_value) {
  524             /* Add the data minus the CRLF */
  525 #ifdef EXTSTORE
  526             if (it->it_flags & ITEM_HDR) {
  527                 if (storage_get_item(c, it, c->resp) != 0) {
  528                     pthread_mutex_lock(&c->thread->stats.mutex);
  529                     c->thread->stats.get_oom_extstore++;
  530                     pthread_mutex_unlock(&c->thread->stats.mutex);
  531 
  532                     failed = true;
  533                 }
  534             } else if ((it->it_flags & ITEM_CHUNKED) == 0) {
  535                 resp_add_iov(c->resp, ITEM_data(it), it->nbytes - 2);
  536             } else {
  537                 // Allow transmit handler to find the item and expand iov's
  538                 resp_add_chunked_iov(c->resp, it, it->nbytes - 2);
  539             }
  540 #else
  541             if ((it->it_flags & ITEM_CHUNKED) == 0) {
  542                 resp_add_iov(c->resp, ITEM_data(it), it->nbytes - 2);
  543             } else {
  544                 resp_add_chunked_iov(c->resp, it, it->nbytes - 2);
  545             }
  546 #endif
  547         }
  548 
  549         if (!failed) {
  550             conn_set_state(c, conn_new_cmd);
  551             /* Remember this command so we can garbage collect it later */
  552 #ifdef EXTSTORE
  553             if ((it->it_flags & ITEM_HDR) != 0 && should_return_value) {
  554                 // Only have extstore clean if header and returning value.
  555                 c->resp->item = NULL;
  556             } else {
  557                 c->resp->item = it;
  558             }
  559 #else
  560             c->resp->item = it;
  561 #endif
  562         } else {
  563             item_remove(it);
  564         }
  565     } else {
  566         failed = true;
  567     }
  568 
  569     if (failed) {
  570         pthread_mutex_lock(&c->thread->stats.mutex);
  571         if (should_touch) {
  572             c->thread->stats.touch_cmds++;
  573             c->thread->stats.touch_misses++;
  574         } else {
  575             c->thread->stats.get_cmds++;
  576             c->thread->stats.get_misses++;
  577         }
  578         pthread_mutex_unlock(&c->thread->stats.mutex);
  579 
  580         if (should_touch) {
  581             MEMCACHED_COMMAND_TOUCH(c->sfd, key, nkey, -1, 0);
  582         } else {
  583             MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
  584         }
  585 
  586         if (c->noreply) {
  587             conn_set_state(c, conn_new_cmd);
  588         } else {
  589             if (should_return_key) {
  590                 write_bin_miss_response(c, key, nkey);
  591             } else {
  592                 write_bin_miss_response(c, NULL, 0);
  593             }
  594         }
  595     }
  596 
  597     if (settings.detail_enabled) {
  598         stats_prefix_record_get(key, nkey, NULL != it);
  599     }
  600 }
  601 
  602 static void process_bin_stat(conn *c) {
  603     char *subcommand = binary_get_key(c);
  604     size_t nkey = c->binary_header.request.keylen;
  605 
  606     if (settings.verbose > 1) {
  607         int ii;
  608         fprintf(stderr, "<%d STATS ", c->sfd);
  609         for (ii = 0; ii < nkey; ++ii) {
  610             fprintf(stderr, "%c", subcommand[ii]);
  611         }
  612         fprintf(stderr, "\n");
  613     }
  614 
  615     if (nkey == 0) {
  616         /* request all statistics */
  617         server_stats(&append_stats, c);
  618         (void)get_stats(NULL, 0, &append_stats, c);
  619     } else if (strncmp(subcommand, "reset", 5) == 0) {
  620         stats_reset();
  621     } else if (strncmp(subcommand, "settings", 8) == 0) {
  622         process_stat_settings(&append_stats, c);
  623     } else if (strncmp(subcommand, "detail", 6) == 0) {
  624         char *subcmd_pos = subcommand + 6;
  625         if (strncmp(subcmd_pos, " dump", 5) == 0) {
  626             int len;
  627             char *dump_buf = stats_prefix_dump(&len);
  628             if (dump_buf == NULL || len <= 0) {
  629                 out_of_memory(c, "SERVER_ERROR Out of memory generating stats");
  630                 if (dump_buf != NULL)
  631                     free(dump_buf);
  632                 return;
  633             } else {
  634                 append_stats("detailed", strlen("detailed"), dump_buf, len, c);
  635                 free(dump_buf);
  636             }
  637         } else if (strncmp(subcmd_pos, " on", 3) == 0) {
  638             settings.detail_enabled = 1;
  639         } else if (strncmp(subcmd_pos, " off", 4) == 0) {
  640             settings.detail_enabled = 0;
  641         } else {
  642             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
  643             return;
  644         }
  645     } else {
  646         if (get_stats(subcommand, nkey, &append_stats, c)) {
  647             if (c->stats.buffer == NULL) {
  648                 out_of_memory(c, "SERVER_ERROR Out of memory generating stats");
  649             } else {
  650                 write_and_free(c, c->stats.buffer, c->stats.offset);
  651                 c->stats.buffer = NULL;
  652             }
  653         } else {
  654             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
  655         }
  656 
  657         return;
  658     }
  659 
  660     /* Append termination package and start the transfer */
  661     append_stats(NULL, 0, NULL, 0, c);
  662     if (c->stats.buffer == NULL) {
  663         out_of_memory(c, "SERVER_ERROR Out of memory preparing to send stats");
  664     } else {
  665         write_and_free(c, c->stats.buffer, c->stats.offset);
  666         c->stats.buffer = NULL;
  667     }
  668 }
  669 
  670 static void init_sasl_conn(conn *c) {
  671     assert(c);
  672     /* should something else be returned? */
  673     if (!settings.sasl)
  674         return;
  675 
  676     c->authenticated = false;
  677 
  678     if (!c->sasl_conn) {
  679         int result=sasl_server_new("memcached",
  680                                    NULL,
  681                                    my_sasl_hostname[0] ? my_sasl_hostname : NULL,
  682                                    NULL, NULL,
  683                                    NULL, 0, &c->sasl_conn);
  684         if (result != SASL_OK) {
  685             if (settings.verbose) {
  686                 fprintf(stderr, "Failed to initialize SASL conn.\n");
  687             }
  688             c->sasl_conn = NULL;
  689         }
  690     }
  691 }
  692 
  693 static void bin_list_sasl_mechs(conn *c) {
  694     // Guard against a disabled SASL.
  695     if (!settings.sasl) {
  696         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
  697                         c->binary_header.request.bodylen
  698                         - c->binary_header.request.keylen);
  699         return;
  700     }
  701 
  702     init_sasl_conn(c);
  703     const char *result_string = NULL;
  704     unsigned int string_length = 0;
  705     int result=sasl_listmech(c->sasl_conn, NULL,
  706                              "",   /* What to prepend the string with */
  707                              " ",  /* What to separate mechanisms with */
  708                              "",   /* What to append to the string */
  709                              &result_string, &string_length,
  710                              NULL);
  711     if (result != SASL_OK) {
  712         /* Perhaps there's a better error for this... */
  713         if (settings.verbose) {
  714             fprintf(stderr, "Failed to list SASL mechanisms.\n");
  715         }
  716         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
  717         return;
  718     }
  719     write_bin_response(c, (char*)result_string, 0, 0, string_length);
  720 }
  721 
  722 static void process_bin_sasl_auth(conn *c) {
  723     // Guard for handling disabled SASL on the server.
  724     if (!settings.sasl) {
  725         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
  726                         c->binary_header.request.bodylen
  727                         - c->binary_header.request.keylen);
  728         return;
  729     }
  730 
  731     assert(c->binary_header.request.extlen == 0);
  732 
  733     int nkey = c->binary_header.request.keylen;
  734     int vlen = c->binary_header.request.bodylen - nkey;
  735 
  736     if (nkey > MAX_SASL_MECH_LEN) {
  737         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen);
  738         conn_set_state(c, conn_swallow);
  739         return;
  740     }
  741 
  742     char *key = binary_get_key(c);
  743     assert(key);
  744 
  745     item *it = item_alloc(key, nkey, 0, 0, vlen+2);
  746 
  747     /* Can't use a chunked item for SASL authentication. */
  748     if (it == 0 || (it->it_flags & ITEM_CHUNKED)) {
  749         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, NULL, vlen);
  750         conn_set_state(c, conn_swallow);
  751         if (it) {
  752             do_item_remove(it);
  753         }
  754         return;
  755     }
  756 
  757     c->item = it;
  758     c->ritem = ITEM_data(it);
  759     c->rlbytes = vlen;
  760     conn_set_state(c, conn_nread);
  761     c->substate = bin_reading_sasl_auth_data;
  762 }
  763 
  764 static void process_bin_complete_sasl_auth(conn *c) {
  765     assert(settings.sasl);
  766     const char *out = NULL;
  767     unsigned int outlen = 0;
  768 
  769     assert(c->item);
  770     init_sasl_conn(c);
  771 
  772     int nkey = c->binary_header.request.keylen;
  773     int vlen = c->binary_header.request.bodylen - nkey;
  774 
  775     if (nkey > ((item*) c->item)->nkey) {
  776         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen);
  777         conn_set_state(c, conn_swallow);
  778         return;
  779     }
  780 
  781     char mech[nkey+1];
  782     memcpy(mech, ITEM_key((item*)c->item), nkey);
  783     mech[nkey] = 0x00;
  784 
  785     if (settings.verbose)
  786         fprintf(stderr, "mech:  ``%s'' with %d bytes of data\n", mech, vlen);
  787 
  788     const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c->item);
  789 
  790     if (vlen > ((item*) c->item)->nbytes) {
  791         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen);
  792         conn_set_state(c, conn_swallow);
  793         return;
  794     }
  795 
  796     int result=-1;
  797 
  798     switch (c->cmd) {
  799     case PROTOCOL_BINARY_CMD_SASL_AUTH:
  800         result = sasl_server_start(c->sasl_conn, mech,
  801                                    challenge, vlen,
  802                                    &out, &outlen);
  803         c->sasl_started = (result == SASL_OK || result == SASL_CONTINUE);
  804         break;
  805     case PROTOCOL_BINARY_CMD_SASL_STEP:
  806         if (!c->sasl_started) {
  807             if (settings.verbose) {
  808                 fprintf(stderr, "%d: SASL_STEP called but sasl_server_start "
  809                         "not called for this connection!\n", c->sfd);
  810             }
  811             break;
  812         }
  813         result = sasl_server_step(c->sasl_conn,
  814                                   challenge, vlen,
  815                                   &out, &outlen);
  816         break;
  817     default:
  818         assert(false); /* CMD should be one of the above */
  819         /* This code is pretty much impossible, but makes the compiler
  820            happier */
  821         if (settings.verbose) {
  822             fprintf(stderr, "Unhandled command %d with challenge %s\n",
  823                     c->cmd, challenge);
  824         }
  825         break;
  826     }
  827 
  828     if (settings.verbose) {
  829         fprintf(stderr, "sasl result code:  %d\n", result);
  830     }
  831 
  832     switch(result) {
  833     case SASL_OK:
  834         c->authenticated = true;
  835         write_bin_response(c, "Authenticated", 0, 0, strlen("Authenticated"));
  836         pthread_mutex_lock(&c->thread->stats.mutex);
  837         c->thread->stats.auth_cmds++;
  838         pthread_mutex_unlock(&c->thread->stats.mutex);
  839         break;
  840     case SASL_CONTINUE:
  841         add_bin_header(c, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE, 0, 0, outlen);
  842         if (outlen > 0) {
  843             resp_add_iov(c->resp, out, outlen);
  844         }
  845         // Immediately flush our write.
  846         conn_set_state(c, conn_mwrite);
  847         break;
  848     default:
  849         if (settings.verbose)
  850             fprintf(stderr, "Unknown sasl response:  %d\n", result);
  851         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
  852         pthread_mutex_lock(&c->thread->stats.mutex);
  853         c->thread->stats.auth_cmds++;
  854         c->thread->stats.auth_errors++;
  855         pthread_mutex_unlock(&c->thread->stats.mutex);
  856     }
  857 }
  858 
  859 static bool authenticated(conn *c) {
  860     assert(settings.sasl);
  861     bool rv = false;
  862 
  863     switch (c->cmd) {
  864     case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: /* FALLTHROUGH */
  865     case PROTOCOL_BINARY_CMD_SASL_AUTH:       /* FALLTHROUGH */
  866     case PROTOCOL_BINARY_CMD_SASL_STEP:       /* FALLTHROUGH */
  867     case PROTOCOL_BINARY_CMD_VERSION:         /* FALLTHROUGH */
  868         rv = true;
  869         break;
  870     default:
  871         rv = c->authenticated;
  872     }
  873 
  874     if (settings.verbose > 1) {
  875         fprintf(stderr, "authenticated() in cmd 0x%02x is %s\n",
  876                 c->cmd, rv ? "true" : "false");
  877     }
  878 
  879     return rv;
  880 }
  881 
  882 static void dispatch_bin_command(conn *c, char *extbuf) {
  883     int protocol_error = 0;
  884 
  885     uint8_t extlen = c->binary_header.request.extlen;
  886     uint16_t keylen = c->binary_header.request.keylen;
  887     uint32_t bodylen = c->binary_header.request.bodylen;
  888 
  889     if (keylen > bodylen || keylen + extlen > bodylen) {
  890         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL, 0);
  891         c->close_after_write = true;
  892         return;
  893     }
  894 
  895     if (settings.sasl && !authenticated(c)) {
  896         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
  897         c->close_after_write = true;
  898         return;
  899     }
  900 
  901     MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
  902     c->noreply = true;
  903 
  904     /* binprot supports 16bit keys, but internals are still 8bit */
  905     if (keylen > KEY_MAX_LENGTH) {
  906         handle_binary_protocol_error(c);
  907         return;
  908     }
  909 
  910     switch (c->cmd) {
  911     case PROTOCOL_BINARY_CMD_SETQ:
  912         c->cmd = PROTOCOL_BINARY_CMD_SET;
  913         break;
  914     case PROTOCOL_BINARY_CMD_ADDQ:
  915         c->cmd = PROTOCOL_BINARY_CMD_ADD;
  916         break;
  917     case PROTOCOL_BINARY_CMD_REPLACEQ:
  918         c->cmd = PROTOCOL_BINARY_CMD_REPLACE;
  919         break;
  920     case PROTOCOL_BINARY_CMD_DELETEQ:
  921         c->cmd = PROTOCOL_BINARY_CMD_DELETE;
  922         break;
  923     case PROTOCOL_BINARY_CMD_INCREMENTQ:
  924         c->cmd = PROTOCOL_BINARY_CMD_INCREMENT;
  925         break;
  926     case PROTOCOL_BINARY_CMD_DECREMENTQ:
  927         c->cmd = PROTOCOL_BINARY_CMD_DECREMENT;
  928         break;
  929     case PROTOCOL_BINARY_CMD_QUITQ:
  930         c->cmd = PROTOCOL_BINARY_CMD_QUIT;
  931         break;
  932     case PROTOCOL_BINARY_CMD_FLUSHQ:
  933         c->cmd = PROTOCOL_BINARY_CMD_FLUSH;
  934         break;
  935     case PROTOCOL_BINARY_CMD_APPENDQ:
  936         c->cmd = PROTOCOL_BINARY_CMD_APPEND;
  937         break;
  938     case PROTOCOL_BINARY_CMD_PREPENDQ:
  939         c->cmd = PROTOCOL_BINARY_CMD_PREPEND;
  940         break;
  941     case PROTOCOL_BINARY_CMD_GETQ:
  942         c->cmd = PROTOCOL_BINARY_CMD_GET;
  943         break;
  944     case PROTOCOL_BINARY_CMD_GETKQ:
  945         c->cmd = PROTOCOL_BINARY_CMD_GETK;
  946         break;
  947     case PROTOCOL_BINARY_CMD_GATQ:
  948         c->cmd = PROTOCOL_BINARY_CMD_GAT;
  949         break;
  950     case PROTOCOL_BINARY_CMD_GATKQ:
  951         c->cmd = PROTOCOL_BINARY_CMD_GATK;
  952         break;
  953     default:
  954         c->noreply = false;
  955     }
  956 
  957     switch (c->cmd) {
  958         case PROTOCOL_BINARY_CMD_VERSION:
  959             if (extlen == 0 && keylen == 0 && bodylen == 0) {
  960                 write_bin_response(c, VERSION, 0, 0, strlen(VERSION));
  961             } else {
  962                 protocol_error = 1;
  963             }
  964             break;
  965         case PROTOCOL_BINARY_CMD_FLUSH:
  966             if (keylen == 0 && bodylen == extlen && (extlen == 0 || extlen == 4)) {
  967                 process_bin_flush(c, extbuf);
  968             } else {
  969                 protocol_error = 1;
  970             }
  971             break;
  972         case PROTOCOL_BINARY_CMD_NOOP:
  973             if (extlen == 0 && keylen == 0 && bodylen == 0) {
  974                 write_bin_response(c, NULL, 0, 0, 0);
  975                 // NOOP forces pipeline flush.
  976                 conn_set_state(c, conn_mwrite);
  977             } else {
  978                 protocol_error = 1;
  979             }
  980             break;
  981         case PROTOCOL_BINARY_CMD_SET: /* FALLTHROUGH */
  982         case PROTOCOL_BINARY_CMD_ADD: /* FALLTHROUGH */
  983         case PROTOCOL_BINARY_CMD_REPLACE:
  984             if (extlen == 8 && keylen != 0 && bodylen >= (keylen + 8)) {
  985                 process_bin_update(c, extbuf);
  986             } else {
  987                 protocol_error = 1;
  988             }
  989             break;
  990         case PROTOCOL_BINARY_CMD_GETQ:  /* FALLTHROUGH */
  991         case PROTOCOL_BINARY_CMD_GET:   /* FALLTHROUGH */
  992         case PROTOCOL_BINARY_CMD_GETKQ: /* FALLTHROUGH */
  993         case PROTOCOL_BINARY_CMD_GETK:
  994             if (extlen == 0 && bodylen == keylen && keylen > 0) {
  995                 process_bin_get_or_touch(c, extbuf);
  996             } else {
  997                 protocol_error = 1;
  998             }
  999             break;
 1000         case PROTOCOL_BINARY_CMD_DELETE:
 1001             if (keylen > 0 && extlen == 0 && bodylen == keylen) {
 1002                 process_bin_delete(c);
 1003             } else {
 1004                 protocol_error = 1;
 1005             }
 1006             break;
 1007         case PROTOCOL_BINARY_CMD_INCREMENT:
 1008         case PROTOCOL_BINARY_CMD_DECREMENT:
 1009             if (keylen > 0 && extlen == 20 && bodylen == (keylen + extlen)) {
 1010                 complete_incr_bin(c, extbuf);
 1011             } else {
 1012                 protocol_error = 1;
 1013             }
 1014             break;
 1015         case PROTOCOL_BINARY_CMD_APPEND:
 1016         case PROTOCOL_BINARY_CMD_PREPEND:
 1017             if (keylen > 0 && extlen == 0) {
 1018                 process_bin_append_prepend(c);
 1019             } else {
 1020                 protocol_error = 1;
 1021             }
 1022             break;
 1023         case PROTOCOL_BINARY_CMD_STAT:
 1024             if (extlen == 0) {
 1025                 process_bin_stat(c);
 1026             } else {
 1027                 protocol_error = 1;
 1028             }
 1029             break;
 1030         case PROTOCOL_BINARY_CMD_QUIT:
 1031             if (keylen == 0 && extlen == 0 && bodylen == 0) {
 1032                 write_bin_response(c, NULL, 0, 0, 0);
 1033                 conn_set_state(c, conn_mwrite);
 1034                 c->close_after_write = true;
 1035             } else {
 1036                 protocol_error = 1;
 1037             }
 1038             break;
 1039         case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
 1040             if (extlen == 0 && keylen == 0 && bodylen == 0) {
 1041                 bin_list_sasl_mechs(c);
 1042             } else {
 1043                 protocol_error = 1;
 1044             }
 1045             break;
 1046         case PROTOCOL_BINARY_CMD_SASL_AUTH:
 1047         case PROTOCOL_BINARY_CMD_SASL_STEP:
 1048             if (extlen == 0 && keylen != 0) {
 1049                 process_bin_sasl_auth(c);
 1050             } else {
 1051                 protocol_error = 1;
 1052             }
 1053             break;
 1054         case PROTOCOL_BINARY_CMD_TOUCH:
 1055         case PROTOCOL_BINARY_CMD_GAT:
 1056         case PROTOCOL_BINARY_CMD_GATQ:
 1057         case PROTOCOL_BINARY_CMD_GATK:
 1058         case PROTOCOL_BINARY_CMD_GATKQ:
 1059             if (extlen == 4 && keylen != 0) {
 1060                 process_bin_get_or_touch(c, extbuf);
 1061             } else {
 1062                 protocol_error = 1;
 1063             }
 1064             break;
 1065         default:
 1066             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
 1067                             bodylen);
 1068     }
 1069 
 1070     if (protocol_error)
 1071         handle_binary_protocol_error(c);
 1072 }
 1073 
 1074 static void process_bin_update(conn *c, char *extbuf) {
 1075     char *key;
 1076     int nkey;
 1077     int vlen;
 1078     item *it;
 1079     protocol_binary_request_set* req = (void *)extbuf;
 1080 
 1081     assert(c != NULL);
 1082 
 1083     key = binary_get_key(c);
 1084     nkey = c->binary_header.request.keylen;
 1085 
 1086     /* fix byteorder in the request */
 1087     req->message.body.flags = ntohl(req->message.body.flags);
 1088     req->message.body.expiration = ntohl(req->message.body.expiration);
 1089 
 1090     vlen = c->binary_header.request.bodylen - (nkey + c->binary_header.request.extlen);
 1091 
 1092     if (settings.verbose > 1) {
 1093         int ii;
 1094         if (c->cmd == PROTOCOL_BINARY_CMD_ADD) {
 1095             fprintf(stderr, "<%d ADD ", c->sfd);
 1096         } else if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
 1097             fprintf(stderr, "<%d SET ", c->sfd);
 1098         } else {
 1099             fprintf(stderr, "<%d REPLACE ", c->sfd);
 1100         }
 1101         for (ii = 0; ii < nkey; ++ii) {
 1102             fprintf(stderr, "%c", key[ii]);
 1103         }
 1104 
 1105         fprintf(stderr, " Value len is %d", vlen);
 1106         fprintf(stderr, "\n");
 1107     }
 1108 
 1109     if (settings.detail_enabled) {
 1110         stats_prefix_record_set(key, nkey);
 1111     }
 1112 
 1113     it = item_alloc(key, nkey, req->message.body.flags,
 1114             realtime(req->message.body.expiration), vlen+2);
 1115 
 1116     if (it == 0) {
 1117         enum store_item_type status;
 1118         if (! item_size_ok(nkey, req->message.body.flags, vlen + 2)) {
 1119             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
 1120             status = TOO_LARGE;
 1121         } else {
 1122             out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
 1123             /* This error generating method eats the swallow value. Add here. */
 1124             c->sbytes = vlen;
 1125             status = NO_MEMORY;
 1126         }
 1127         /* FIXME: losing c->cmd since it's translated below. refactor? */
 1128         LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE,
 1129                 NULL, status, 0, key, nkey, req->message.body.expiration,
 1130                 ITEM_clsid(it), c->sfd);
 1131 
 1132         /* Avoid stale data persisting in cache because we failed alloc.
 1133          * Unacceptable for SET. Anywhere else too? */
 1134         if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
 1135             it = item_get(key, nkey, c, DONT_UPDATE);
 1136             if (it) {
 1137                 item_unlink(it);
 1138                 STORAGE_delete(c->thread->storage, it);
 1139                 item_remove(it);
 1140             }
 1141         }
 1142 
 1143         /* swallow the data line */
 1144         conn_set_state(c, conn_swallow);
 1145         return;
 1146     }
 1147 
 1148     ITEM_set_cas(it, c->binary_header.request.cas);
 1149 
 1150     switch (c->cmd) {
 1151         case PROTOCOL_BINARY_CMD_ADD:
 1152             c->cmd = NREAD_ADD;
 1153             break;
 1154         case PROTOCOL_BINARY_CMD_SET:
 1155             c->cmd = NREAD_SET;
 1156             break;
 1157         case PROTOCOL_BINARY_CMD_REPLACE:
 1158             c->cmd = NREAD_REPLACE;
 1159             break;
 1160         default:
 1161             assert(0);
 1162     }
 1163 
 1164     if (ITEM_get_cas(it) != 0) {
 1165         c->cmd = NREAD_CAS;
 1166     }
 1167 
 1168     c->item = it;
 1169 #ifdef NEED_ALIGN
 1170     if (it->it_flags & ITEM_CHUNKED) {
 1171         c->ritem = ITEM_schunk(it);
 1172     } else {
 1173         c->ritem = ITEM_data(it);
 1174     }
 1175 #else
 1176     c->ritem = ITEM_data(it);
 1177 #endif
 1178     c->rlbytes = vlen;
 1179     conn_set_state(c, conn_nread);
 1180     c->substate = bin_read_set_value;
 1181 }
 1182 
 1183 static void process_bin_append_prepend(conn *c) {
 1184     char *key;
 1185     int nkey;
 1186     int vlen;
 1187     item *it;
 1188 
 1189     assert(c != NULL);
 1190 
 1191     key = binary_get_key(c);
 1192     nkey = c->binary_header.request.keylen;
 1193     vlen = c->binary_header.request.bodylen - nkey;
 1194 
 1195     if (settings.verbose > 1) {
 1196         fprintf(stderr, "Value len is %d\n", vlen);
 1197     }
 1198 
 1199     if (settings.detail_enabled) {
 1200         stats_prefix_record_set(key, nkey);
 1201     }
 1202 
 1203     it = item_alloc(key, nkey, 0, 0, vlen+2);
 1204 
 1205     if (it == 0) {
 1206         if (! item_size_ok(nkey, 0, vlen + 2)) {
 1207             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
 1208         } else {
 1209             out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
 1210             /* OOM calls eat the swallow value. Add here. */
 1211             c->sbytes = vlen;
 1212         }
 1213         /* swallow the data line */
 1214         conn_set_state(c, conn_swallow);
 1215         return;
 1216     }
 1217 
 1218     ITEM_set_cas(it, c->binary_header.request.cas);
 1219 
 1220     switch (c->cmd) {
 1221         case PROTOCOL_BINARY_CMD_APPEND:
 1222             c->cmd = NREAD_APPEND;
 1223             break;
 1224         case PROTOCOL_BINARY_CMD_PREPEND:
 1225             c->cmd = NREAD_PREPEND;
 1226             break;
 1227         default:
 1228             assert(0);
 1229     }
 1230 
 1231     c->item = it;
 1232 #ifdef NEED_ALIGN
 1233     if (it->it_flags & ITEM_CHUNKED) {
 1234         c->ritem = ITEM_schunk(it);
 1235     } else {
 1236         c->ritem = ITEM_data(it);
 1237     }
 1238 #else
 1239     c->ritem = ITEM_data(it);
 1240 #endif
 1241     c->rlbytes = vlen;
 1242     conn_set_state(c, conn_nread);
 1243     c->substate = bin_read_set_value;
 1244 }
 1245 
 1246 static void process_bin_flush(conn *c, char *extbuf) {
 1247     time_t exptime = 0;
 1248     protocol_binary_request_flush* req = (void *)extbuf;
 1249     rel_time_t new_oldest = 0;
 1250 
 1251     if (!settings.flush_enabled) {
 1252       // flush_all is not allowed but we log it on stats
 1253       write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
 1254       return;
 1255     }
 1256 
 1257     if (c->binary_header.request.extlen == sizeof(req->message.body)) {
 1258         exptime = ntohl(req->message.body.expiration);
 1259     }
 1260 
 1261     if (exptime > 0) {
 1262         new_oldest = realtime(exptime);
 1263     } else {
 1264         new_oldest = current_time;
 1265     }
 1266     if (settings.use_cas) {
 1267         settings.oldest_live = new_oldest - 1;
 1268         if (settings.oldest_live <= current_time)
 1269             settings.oldest_cas = get_cas_id();
 1270     } else {
 1271         settings.oldest_live = new_oldest;
 1272     }
 1273 
 1274     pthread_mutex_lock(&c->thread->stats.mutex);
 1275     c->thread->stats.flush_cmds++;
 1276     pthread_mutex_unlock(&c->thread->stats.mutex);
 1277 
 1278     write_bin_response(c, NULL, 0, 0, 0);
 1279 }
 1280 
 1281 static void process_bin_delete(conn *c) {
 1282     item *it;
 1283     uint32_t hv;
 1284 
 1285     char* key = binary_get_key(c);
 1286     size_t nkey = c->binary_header.request.keylen;
 1287 
 1288     assert(c != NULL);
 1289 
 1290     if (settings.verbose > 1) {
 1291         int ii;
 1292         fprintf(stderr, "Deleting ");
 1293         for (ii = 0; ii < nkey; ++ii) {
 1294             fprintf(stderr, "%c", key[ii]);
 1295         }
 1296         fprintf(stderr, "\n");
 1297     }
 1298 
 1299     if (settings.detail_enabled) {
 1300         stats_prefix_record_delete(key, nkey);
 1301     }
 1302 
 1303     it = item_get_locked(key, nkey, c, DONT_UPDATE, &hv);
 1304     if (it) {
 1305         uint64_t cas = c->binary_header.request.cas;
 1306         if (cas == 0 || cas == ITEM_get_cas(it)) {
 1307             MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
 1308             pthread_mutex_lock(&c->thread->stats.mutex);
 1309             c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
 1310             pthread_mutex_unlock(&c->thread->stats.mutex);
 1311             do_item_unlink(it, hv);
 1312             STORAGE_delete(c->thread->storage, it);
 1313             write_bin_response(c, NULL, 0, 0, 0);
 1314         } else {
 1315             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
 1316         }
 1317         do_item_remove(it);      /* release our reference */
 1318     } else {
 1319         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
 1320         pthread_mutex_lock(&c->thread->stats.mutex);
 1321         c->thread->stats.delete_misses++;
 1322         pthread_mutex_unlock(&c->thread->stats.mutex);
 1323     }
 1324     item_unlock(hv);
 1325 }
 1326 
 1327