1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2007 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.0 of the PHP license,       |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_0.txt.                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Antony Dovgal <tony2001@phpclub.net>                        |
16   |          Mikael Johansson <mikael AT synd DOT info>                  |
17   +----------------------------------------------------------------------+
18 */
19 
20 /* $Id$ */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #define MMC_DEBUG 0
27 
28 #ifdef PHP_WIN32
29 #include <win32/php_stdint.h>
30 #include <winsock2.h>
31 #else
32 #include <stdint.h>
33 #include <arpa/inet.h>
34 #include <netinet/in.h>
35 #endif
36 #include "memcache_pool.h"
37 #include "ext/standard/php_smart_string.h"
38 
39 #ifdef __DragonFly__
40 #include <sys/endian.h>
41 #ifndef __BYTE_ORDER
42 #define __BYTE_ORDER   _BYTE_ORDER
43 #define __LITTLE_ENDIAN        _LITTLE_ENDIAN
44 #define __BIG_ENDIAN   _BIG_ENDIAN
45 #endif
46 #endif
47 
48 #ifdef htonll
49 #undef htonll
50 #endif
51 
52 #ifdef ntohll
53 #undef ntohll
54 #endif
55 
56 #ifndef PHP_WIN32
57 #if __BYTE_ORDER == __BIG_ENDIAN
58 # define ntohll(x) (x)
59 # define htonll(x) (x)
60 #elif __BYTE_ORDER == __LITTLE_ENDIAN
61 #ifdef __DragonFly__
62 # define ntohll(x) bswap64(x)
63 # define htonll(x) bswap64(x)
64 #else
65 # include <byteswap.h>
66 # define ntohll(x) bswap_64(x)
67 # define htonll(x) bswap_64(x)
68 #endif
69 #else
70 # error "Could not determine byte order: __BYTE_ORDER uncorrectly defined"
71 #endif
72 #else
73 uint64_t mmc_htonll(uint64_t value);
74 # define ntohll mmc_htonll
75 # define htonll mmc_htonll
76 #endif
77 
78 #ifdef __GNUC__
79 # define MMC_ATTR_PACKED __attribute__((packed))
80 #else
81 # define MMC_ATTR_PACKED
82 #endif /* UD_ATTR_PACKED */
83 
84 #define MMC_REQUEST_MAGIC	0x80
85 #define	MMC_RESPONSE_MAGIC	0x81
86 
87 #define MMC_OP_GET			0x00
88 #define MMC_OP_SET			0x01
89 #define MMC_OP_ADD			0x02
90 #define MMC_OP_REPLACE		0x03
91 #define MMC_OP_DELETE		0x04
92 #define MMC_OP_INCR			0x05
93 #define MMC_OP_DECR			0x06
94 #define MMC_OP_QUIT			0x07
95 #define MMC_OP_FLUSH		0x08
96 #define MMC_OP_GETQ			0x09
97 #define MMC_OP_NOOP			0x0a
98 #define MMC_OP_VERSION		0x0b
99 #define MMC_BIN_OP_APPEND		0x0e
100 #define MMC_BIN_OP_PREPEND		0x0f
101 
102 #define MMC_BINARY_STATUS_OK				0x00
103 #define MMC_BINARY_STATUS_KEY_NOT_FOUND		0x01
104 #define MMC_BINARY_STATUS_KEY_EXISTS		0x02
105 #define MMC_BINARY_STATUS_VALUE_TOO_LARGE	0x03
106 #define MMC_BINARY_STATUS_INVALID_ARGS		0x04
107 #define MMC_BINARY_STATUS_ITEM_NOT_STORED	0x05
108 #define MMC_BINARY_STATUS_INCR_DECR_ERROR	0x06 /* Incr/Decr on non-numeric value */
109 #define MMC_BINARY_STATUS_UNKNOWN_COMMAND	0x81
110 #define MMC_BINARY_STATUS_OUT_OF_MEMORY		0x82
111 
112 #define MMC_OP_SASL_LIST		0x20
113 #define MMC_OP_SASL_AUTH		0x21
114 #define MMC_OP_SASL_AUTH_STEP	0x21
115 
116 typedef struct mmc_binary_request {
117 	mmc_request_t		base;					/* enable cast to mmc_request_t */
118 	mmc_request_parser	next_parse_handler;		/* next payload parser state */
119 	mmc_queue_t			keys;					/* mmc_queue_t<zval *>, reqid -> key mappings */
120 	struct {
121 		uint8_t			opcode;
122 		uint16_t		error;					/* error received in current request */
123 		uint32_t		reqid;					/* current reqid being processed */
124 	} command;
125 	struct {									/* stores value info while the body is being read */
126 		unsigned int	flags;
127 		unsigned long	length;
128 		uint64_t		cas;					/* CAS counter */
129 	} value;
130 } mmc_binary_request_t;
131 
132 typedef struct mmc_request_header {
133 	uint8_t		magic;
134 	uint8_t		opcode;
135 	uint16_t	key_len;
136 	uint8_t		extras_len;
137 	uint8_t		datatype;
138 	uint16_t	_reserved;
139 	uint32_t	length;							/* trailing body total_body_length (not including this header) */
140 	uint32_t	reqid;							/* opaque request id */
141 	uint64_t	cas;
142 } MMC_ATTR_PACKED mmc_request_header_t;
143 
144 typedef struct mmc_get_request_header {
145 	mmc_request_header_t	base;
146 } MMC_ATTR_PACKED mmc_get_request_header_t;
147 
148 typedef struct mmc_version_request_header {
149 	mmc_request_header_t	base;
150 } MMC_ATTR_PACKED mmc_version_request_header_t;
151 
152 typedef struct mmc_store_request_header {
153 	mmc_request_header_t	base;
154 	uint32_t				flags;
155 	uint32_t				exptime;
156 } MMC_ATTR_PACKED mmc_store_request_header_t;
157 
158 typedef struct mmc_store_append_header {
159 	mmc_request_header_t	base;
160 } MMC_ATTR_PACKED mmc_store_append_header_t;
161 
162 typedef struct mmc_delete_request_header {
163 	mmc_request_header_t	base;
164 } MMC_ATTR_PACKED mmc_delete_request_header_t;
165 
166 typedef struct mmc_mutate_request_header {
167 	mmc_request_header_t	base;
168 	uint64_t				delta;
169 	uint64_t				initial;
170 	uint32_t				expiration;
171 } MMC_ATTR_PACKED mmc_mutate_request_header_t;
172 
173 typedef struct mmc_sasl_request_header {
174 	mmc_request_header_t	base;
175 } MMC_ATTR_PACKED mmc_sasl_request_header;
176 
177 typedef struct mmc_response_header {
178 	uint8_t		magic;
179 	uint8_t		opcode;
180 	uint16_t	key_len;
181 	uint8_t		extras_len;
182 	uint8_t		datatype;
183 	uint16_t	status;
184 	uint32_t	total_body_length;	/* trailing body total_body_length (not including this header) */
185 	uint32_t	reqid;				/* echo'ed from request */
186 	uint64_t	cas;
187 } MMC_ATTR_PACKED mmc_response_header_t;
188 
189 typedef struct mmc_get_response_header {
190 	uint32_t				flags;
191 } MMC_ATTR_PACKED mmc_get_response_header_t;
192 
193 typedef struct mmc_mutate_response_header {
194 	uint64_t				value;
195 } MMC_ATTR_PACKED mmc_mutate_response_header_t;
196 
197 static int mmc_request_read_response(mmc_t *, mmc_request_t *);
198 static int mmc_request_parse_value(mmc_t *, mmc_request_t *);
199 static int mmc_request_read_value(mmc_t *, mmc_request_t *);
200 
mmc_binary_hexdump(void * mem,unsigned int len)201 void mmc_binary_hexdump(void *mem, unsigned int len)
202 {
203 #	define HEXDUMP_COLS 4
204 	unsigned int i, j;
205 	for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) {
206 		if(i % HEXDUMP_COLS == 0) {
207 			printf("%06i: ", i);
208 		}
209 
210 		if(i < len) {
211 			printf("%02x ", 0xFF & ((char*)mem)[i]);
212 		} else {
213 			printf("   ");
214 		}
215 
216 		if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
217 			for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
218 				if(j >= len) {
219 					putchar(' ');
220 				} else if(isprint(((char*)mem)[j])) {
221 					putchar(0xFF & ((char*)mem)[j]);
222 				} else {
223 					putchar('.');
224 				}
225 			}
226 			putchar('\n');
227 		}
228 	}
229 }
230 #ifdef PHP_WIN32
mmc_htonll(uint64_t value)231 uint64_t mmc_htonll(uint64_t value)
232 {
233 	if (value == 0) {
234 		return 0x0LL;
235 	} else {
236 		static const int num = 42;
237 
238 		// Check the endianness
239 		if (*(const char *)(&num) == num) {
240 			const uint32_t high_part = htonl((uint32_t)(value >> 32));
241 			const uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL));
242 
243 			return ((uint64_t)(low_part) << 32) | high_part;
244 		} else {
245 			return value;
246 		}
247 	}
248 }
249 #endif
250 
mmc_stream_get(mmc_stream_t * io,size_t bytes)251 static inline char *mmc_stream_get(mmc_stream_t *io, size_t bytes) /*
252 	attempts to read a number of bytes from server, returns the a pointer to the buffer on success, NULL if the complete number of bytes could not be read {{{ */
253 {
254 	io->input.idx += io->read(io, io->input.value + io->input.idx, bytes - io->input.idx);
255 
256 	if (io->input.idx >= bytes) {
257 		io->input.idx = 0;
258 		return io->input.value;
259 	}
260 
261 	return NULL;
262 }
263 /* }}} */
264 
mmc_request_parse_response(mmc_t * mmc,mmc_request_t * request)265 static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request) /*
266 	reads a generic response header and reads the response body {{{ */
267 {
268 	mmc_response_header_t *header;
269 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
270 
271 	header = (mmc_response_header_t *)mmc_stream_get(request->io, sizeof(mmc_response_header_t));
272 
273 	if (header != NULL) {
274 		if (header->magic != MMC_RESPONSE_MAGIC) {
275 			return mmc_server_failure(mmc, request->io, "Malformed server response (invalid magic byte)", 0);
276 		}
277 
278 		if (header->opcode == MMC_OP_NOOP) {
279 			return MMC_REQUEST_DONE;
280 		}
281 
282 		req->command.opcode = header->opcode;
283 
284 		switch (ntohs(header->status)) {
285 			case MMC_BINARY_STATUS_OK:
286 				req->command.error = MMC_OK;
287 				break;
288 			case MMC_BINARY_STATUS_KEY_NOT_FOUND:
289 				req->command.error = MMC_RESPONSE_NOT_FOUND;
290 				break;
291 			case MMC_BINARY_STATUS_KEY_EXISTS:
292 				req->command.error = MMC_RESPONSE_EXISTS;
293 				break;
294 			case MMC_BINARY_STATUS_VALUE_TOO_LARGE:
295 				req->command.error = MMC_RESPONSE_TOO_LARGE;
296 				break;
297 			case MMC_BINARY_STATUS_INVALID_ARGS:
298 			case MMC_BINARY_STATUS_INCR_DECR_ERROR:
299 				req->command.error = MMC_RESPONSE_CLIENT_ERROR;
300 				break;
301 			case MMC_BINARY_STATUS_ITEM_NOT_STORED:
302 				req->command.error = MMC_RESPONSE_NOT_STORED;
303 				break;
304 			case MMC_BINARY_STATUS_UNKNOWN_COMMAND:
305 				req->command.error = MMC_RESPONSE_UNKNOWN_CMD;
306 				break;
307 			case MMC_BINARY_STATUS_OUT_OF_MEMORY:
308 				req->command.error = MMC_RESPONSE_OUT_OF_MEMORY;
309 				break;
310 			default:
311 				req->command.error = MMC_RESPONSE_UNKNOWN;
312 				break;
313 		}
314 
315 		req->command.reqid = ntohl(header->reqid);
316 		req->value.length = ntohl(header->total_body_length);
317 		req->value.cas = ntohll(header->cas);
318 
319 		if (req->value.length == 0) {
320 			return request->response_handler(mmc, request, req->command.error, "", 0, request->response_handler_param);
321 		}
322 
323 		/* allow read_response handler to read the response body */
324 		if (req->command.error) {
325 			request->parse = mmc_request_read_response;
326 			mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
327 		}
328 		else {
329 			request->parse = req->next_parse_handler;
330 
331 			if (req->value.length >= header->extras_len) {
332 				req->value.length -= header->extras_len;
333 			}
334 
335 			mmc_buffer_alloc(&(request->readbuf), req->value.length + 1);
336 		}
337 
338 		/* read more, php streams buffer input which must be read if available */
339 		return MMC_REQUEST_AGAIN;
340 	}
341 
342 	return MMC_REQUEST_MORE;
343 }
344 /* }}}*/
345 
mmc_request_parse_null(mmc_t * mmc,mmc_request_t * request)346 static int mmc_request_parse_null(mmc_t *mmc, mmc_request_t *request) /*
347 	always returns MMC_REQUEST_DONE {{{ */
348 {
349 	return MMC_REQUEST_DONE;
350 }
351 
mmc_request_read_response(mmc_t * mmc,mmc_request_t * request)352 static int mmc_request_read_response(mmc_t *mmc, mmc_request_t *request) /*
353 	read the response body into the buffer and delegates to response_handler {{{ */
354 {
355 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
356 
357 	request->readbuf.idx +=
358 		request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length - request->readbuf.idx);
359 
360 	/* done reading? */
361 	if (request->readbuf.idx >= req->value.length) {
362 		request->readbuf.value.c[req->value.length] = '\0';
363 		return request->response_handler(mmc, request, req->command.error, request->readbuf.value.c, req->value.length, request->response_handler_param);
364 	}
365 
366 	return MMC_REQUEST_MORE;
367 }
368 /* }}}*/
369 
mmc_request_read_mutate(mmc_t * mmc,mmc_request_t * request)370 static int mmc_request_read_mutate(mmc_t *mmc, mmc_request_t *request) /*
371 	reads and parses the mutate response header {{{ */
372 {
373 	mmc_mutate_response_header_t *header;
374 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
375 
376 	header = (mmc_mutate_response_header_t *)mmc_stream_get(request->io, sizeof(*header));
377 	if (header != NULL) {
378 		int result;
379 		zval *key, value;
380 
381 		/* convert remembered key to string and unpack value */
382 		key = (zval *)mmc_queue_item(&(req->keys), req->command.reqid);
383 
384 		ZVAL_LONG(&value, ntohll(header->value));
385 
386 		if (Z_TYPE_P(key) != IS_STRING) {
387 			zval keytmp = *key;
388 
389 			zval_copy_ctor(&keytmp);
390 			convert_to_string(&keytmp);
391 
392 			result = request->value_handler(
393 				Z_STRVAL(keytmp), Z_STRLEN(keytmp), &value,
394 				req->value.flags, req->value.cas, request->value_handler_param);
395 
396 			zval_dtor(&keytmp);
397 		}
398 		else {
399 			result = request->value_handler(
400 				Z_STRVAL_P(key), Z_STRLEN_P(key), &value,
401 				req->value.flags, req->value.cas, request->value_handler_param);
402 		}
403 
404 		return result;
405 	}
406 
407 	return MMC_REQUEST_MORE;
408 }
409 /* }}}*/
410 
mmc_request_parse_value(mmc_t * mmc,mmc_request_t * request)411 static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request) /*
412 	reads and parses the value response header and then reads the value body {{{ */
413 {
414 	mmc_get_response_header_t *header;
415 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
416 
417 	header = (mmc_get_response_header_t *)mmc_stream_get(request->io, sizeof(mmc_get_response_header_t));
418 	if (header != NULL) {
419 		//req->value.cas = ntohll(header->cas);
420 		req->value.flags = ntohl(header->flags);
421 
422 		/* allow read_value handler to read the value body */
423 		request->parse = mmc_request_read_value;
424 
425 		/* read more, php streams buffer input which must be read if available */
426 		return MMC_REQUEST_AGAIN;
427 	}
428 
429 	return MMC_REQUEST_MORE;
430 }
431 /* }}}*/
432 
mmc_request_read_value(mmc_t * mmc,mmc_request_t * request)433 static int mmc_request_read_value(mmc_t *mmc, mmc_request_t *request) /*
434 	read the value body into the buffer {{{ */
435 {
436 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
437 
438 	request->readbuf.idx +=
439 		request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length - request->readbuf.idx);
440 
441 	/* done reading? */
442 	if (request->readbuf.idx >= req->value.length) {
443 		zval *key;
444 		int result;
445 
446 		/* allow parse_value to read next VALUE or NOOP, done here to ensure reentrancy */
447 		if (req->command.opcode == MMC_OP_GET) {
448 			request->parse = mmc_request_parse_null;
449 		}
450 		else {
451 			request->parse = mmc_request_parse_response;
452 		}
453 		mmc_buffer_reset(&(request->readbuf));
454 
455 		/* convert remembered key to string and unpack value */
456 		key = (zval *)mmc_queue_item(&(req->keys), req->command.reqid);
457 		if (Z_TYPE_P(key) != IS_STRING) {
458 			zval keytmp = *key;
459 
460 			zval_copy_ctor(&keytmp);
461 			convert_to_string(&keytmp);
462 
463 			result = mmc_unpack_value(
464 				mmc, request, &(request->readbuf), Z_STRVAL(keytmp), Z_STRLEN(keytmp),
465 				req->value.flags, req->value.cas, req->value.length);
466 
467 			zval_dtor(&keytmp);
468 		}
469 		else {
470 			result = mmc_unpack_value(
471 				mmc, request, &(request->readbuf), Z_STRVAL_P(key), Z_STRLEN_P(key),
472 				req->value.flags, req->value.cas, req->value.length);
473 		}
474 
475 		if (result == MMC_REQUEST_DONE && (req->command.opcode == MMC_OP_GET || req->command.reqid >= req->keys.len)) {
476 			return MMC_REQUEST_DONE;
477 		}
478 
479 		return MMC_REQUEST_AGAIN;
480 	}
481 
482 	return MMC_REQUEST_MORE;
483 }
484 /* }}}*/
485 
mmc_pack_header(mmc_request_header_t * header,uint8_t opcode,unsigned int reqid,unsigned int key_len,unsigned int extras_len,unsigned int length)486 static inline void mmc_pack_header(mmc_request_header_t *header, uint8_t opcode, unsigned int reqid, unsigned int key_len, unsigned int extras_len, unsigned int length) /* {{{ */
487 {
488 	header->magic = MMC_REQUEST_MAGIC;
489 	header->opcode = opcode;
490 	header->key_len = htons(key_len);
491 	header->extras_len = extras_len;
492 	header->datatype = 0;
493 	header->_reserved = 0;
494 	header->length = htonl(key_len + extras_len + length);
495 	header->reqid = htonl(reqid);
496 	header->cas = 0;
497 }
498 /* }}} */
499 
mmc_binary_create_request()500 static mmc_request_t *mmc_binary_create_request() /* {{{ */
501 {
502 	mmc_binary_request_t *request = emalloc(sizeof(mmc_binary_request_t));
503 	ZEND_SECURE_ZERO(request, sizeof(mmc_binary_request_t));
504 	return (mmc_request_t *)request;
505 }
506 /* }}} */
507 
mmc_binary_clone_request(mmc_request_t * clone,mmc_request_t * request)508 static void mmc_binary_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */
509 {
510 	mmc_binary_request_t *rcl = (mmc_binary_request_t *)clone;
511 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
512 
513 	rcl->next_parse_handler = req->next_parse_handler;
514 	mmc_queue_copy(&(rcl->keys), &(req->keys));
515 }
516 /* }}} */
517 
mmc_binary_reset_request(mmc_request_t * request)518 static void mmc_binary_reset_request(mmc_request_t *request) /* {{{ */
519 {
520 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
521 	mmc_queue_reset(&(req->keys));
522 	req->value.cas = 0;
523 	mmc_request_reset(request);
524 }
525 /* }}} */
526 
mmc_binary_free_request(mmc_request_t * request)527 static void mmc_binary_free_request(mmc_request_t *request) /* {{{ */
528 {
529 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
530 	mmc_queue_free(&(req->keys));
531 	mmc_request_free(request);
532 }
533 /* }}} */
534 
mmc_binary_begin_get(mmc_request_t * request,int op)535 static void mmc_binary_begin_get(mmc_request_t *request, int op) /* {{{ */
536 {
537 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
538 	request->parse = mmc_request_parse_response;
539 	req->next_parse_handler = mmc_request_parse_value;
540 }
541 /* }}} */
542 
mmc_binary_append_get(mmc_request_t * request,zval * zkey,const char * key,unsigned int key_len)543 static void mmc_binary_append_get(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len) /* {{{ */
544 {
545 	mmc_request_header_t header;
546 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
547 
548 	/* reqid/opaque is the index into the collection of key pointers */
549 	mmc_pack_header(&header, MMC_OP_GETQ, req->keys.len, key_len, 0, 0);
550 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(mmc_request_header_t));
551 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
552 
553 	/* store key to be used by the response handler */
554 	mmc_queue_push(&(req->keys), zkey);
555 }
556 /* }}} */
557 
mmc_binary_end_get(mmc_request_t * request)558 static void mmc_binary_end_get(mmc_request_t *request) /* {{{ */
559 {
560 	mmc_request_header_t header;
561 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
562 	mmc_pack_header(&header, MMC_OP_NOOP, req->keys.len, 0, 0, 0);
563 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
564 }
565 /* }}} */
566 
mmc_binary_get(mmc_request_t * request,int op,zval * zkey,const char * key,unsigned int key_len)567 static void mmc_binary_get(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len) /* {{{ */
568 {
569 	mmc_get_request_header_t header;
570 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
571 
572 	request->parse = mmc_request_parse_response;
573 	req->next_parse_handler = mmc_request_parse_value;
574 
575 	/* reqid/opaque is the index into the collection of key pointers */
576 	mmc_pack_header(&(header.base), MMC_OP_GET, req->keys.len, key_len, 0, 0);
577 	header.base.cas = 0x0;
578 
579 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(mmc_get_request_header_t));
580 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
581 #if MMC_DEBUG
582 	mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
583 #endif
584 	/* store key to be used by the response handler */
585 	mmc_queue_push(&(req->keys), zkey);
586 }
587 /* }}} */
588 
mmc_binary_store(mmc_pool_t * pool,mmc_request_t * request,int op,const char * key,unsigned int key_len,unsigned int flags,unsigned int exptime,unsigned long cas,zval * value)589 static int mmc_binary_store(
590 	mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len,
591 	unsigned int flags, unsigned int exptime, unsigned long cas, zval *value) /* {{{ */
592 {
593 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
594 	int status, prevlen, valuelen;
595 
596 	request->parse = mmc_request_parse_response;
597 	req->next_parse_handler = mmc_request_read_response;
598 
599 	prevlen = request->sendbuf.value.len;
600 
601 /*  placeholder for upcoming append/prepend support */
602 #if 1
603 	if (op == MMC_OP_APPEND || op == MMC_OP_PREPEND) {
604 		mmc_store_append_header_t *header;
605 
606 		if (op == MMC_OP_APPEND) {
607 			op = MMC_BIN_OP_APPEND;
608 		} else {
609 			op = MMC_BIN_OP_PREPEND;
610 		}
611 
612 		/* allocate space for header */
613 		mmc_buffer_alloc(&(request->sendbuf), sizeof(mmc_store_append_header_t));
614 		request->sendbuf.value.len += sizeof(mmc_store_append_header_t);
615 
616 		/* append key and data */
617 		smart_string_appendl(&(request->sendbuf.value), key, key_len);
618 
619 		valuelen = request->sendbuf.value.len;
620 		status = mmc_pack_value(pool, &(request->sendbuf), value, &flags);
621 
622 		if (status != MMC_OK) {
623 			return status;
624 		}
625 
626 		header = (mmc_store_append_header_t *)(request->sendbuf.value.c + prevlen);
627 
628 		mmc_pack_header(&(header->base), op, 0, key_len, sizeof(mmc_store_append_header_t) - sizeof(mmc_request_header_t), request->sendbuf.value.len - valuelen);
629 		header->base.cas = htonll(cas);
630 
631 #if MMC_DEBUG
632 		mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
633 #endif
634 		/* todo */
635 		return MMC_OK;
636 	} else
637 #endif
638 	{
639 		mmc_store_request_header_t *header;
640 
641 		/* allocate space for header */
642 		mmc_buffer_alloc(&(request->sendbuf), sizeof(mmc_store_request_header_t));
643 		request->sendbuf.value.len += sizeof(mmc_store_request_header_t);
644 
645 		/* append key and data */
646 		smart_string_appendl(&(request->sendbuf.value), key, key_len);
647 
648 		valuelen = request->sendbuf.value.len;
649 		status = mmc_pack_value(pool, &(request->sendbuf), value, &flags);
650 
651 		if (status != MMC_OK) {
652 			return status;
653 		}
654 
655 		/* initialize header */
656 		header = (mmc_store_request_header_t *)(request->sendbuf.value.c + prevlen);
657 
658 		if (op == MMC_OP_CAS) {
659 			op = MMC_OP_SET;
660 		}
661 
662 		mmc_pack_header(&(header->base), op, 0, key_len, sizeof(mmc_store_request_header_t) - sizeof(mmc_request_header_t), request->sendbuf.value.len - valuelen);
663 
664 		header->base.cas = htonll(cas);
665 		header->flags = htonl(flags);
666 		header->exptime = htonl(exptime);
667 
668 		return MMC_OK;
669 	}
670 }
671 /* }}} */
672 
mmc_binary_delete(mmc_request_t * request,const char * key,unsigned int key_len,unsigned int exptime)673 static void mmc_binary_delete(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime) /* {{{ */
674 {
675 	mmc_delete_request_header_t header;
676 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
677 
678 	request->parse = mmc_request_parse_response;
679 	req->next_parse_handler = mmc_request_read_response;
680 
681 	mmc_pack_header(&(header.base), MMC_OP_DELETE, 0, key_len, 0, 0);
682 
683 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
684 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
685 }
686 /* }}} */
687 
mmc_binary_mutate(mmc_request_t * request,zval * zkey,const char * key,unsigned int key_len,long value,long defval,int defval_used,unsigned int exptime)688 static void mmc_binary_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime) /* {{{ */
689 {
690 	mmc_mutate_request_header_t header;
691 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
692 	uint8_t op;
693 
694 	request->parse = mmc_request_parse_response;
695 	req->next_parse_handler = mmc_request_read_mutate;
696 
697 	if (value >= 0) {
698 		op = MMC_OP_INCR;
699 		header.delta = htonll((uint64_t)value);
700 		//header.delta = (uint64_t)value;
701 	} else {
702 		op = MMC_OP_DECR;
703 		header.delta = htonll((uint64_t)-value);
704 	}
705 
706 	/* extra is always 20 bytes
707 	https://code.google.com/p/memcached/wiki/BinaryProtocolRevamped#Increment,_Decrement
708 	TODO: add flags to do not force alignments so we can rely on sizeof instead of
709 	fixed sizes, safer&cleaner */
710 	mmc_pack_header(&(header.base), op, req->keys.len, key_len,  20, 0);
711 	header.base.cas = 0x0;
712 	header.initial = htonll((int64_t)defval);
713 
714 	if (defval_used) {
715 		/* server inserts defval if key doesn't exist */
716 		header.expiration = htonl(exptime);
717 	}
718 	else {
719 		/* server replies with NOT_FOUND if exptime ~0 and key doesn't exist */
720 		header.expiration = ~(uint32_t)0;
721 	}
722 
723 	/* mutate request is 43 bytes */
724 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, 44);
725 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
726 
727 #if MMC_DEBUG
728 	mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
729 #endif
730 
731 	/* store key to be used by the response handler */
732 	mmc_queue_push(&(req->keys), zkey);
733 }
734 /* }}} */
735 
mmc_binary_flush(mmc_request_t * request,unsigned int exptime)736 static void mmc_binary_flush(mmc_request_t *request, unsigned int exptime) /* {{{ */
737 {
738 	mmc_request_header_t header;
739 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
740 
741 	request->parse = mmc_request_parse_response;
742 	req->next_parse_handler = mmc_request_read_response;
743 
744 	mmc_pack_header(&header, MMC_OP_FLUSH, 0, 0, 0, 0);
745 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
746 }
747 /* }}} */
748 
mmc_binary_version(mmc_request_t * request)749 static void mmc_binary_version(mmc_request_t *request) /* {{{ */
750 {
751 	mmc_version_request_header_t header;
752 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
753 
754 	request->parse = mmc_request_parse_response;
755 	req->next_parse_handler = mmc_request_read_response;
756 
757 	mmc_pack_header(&(header.base), MMC_OP_VERSION, 0, 0, 0, 0);
758 	header.base.cas = 0x0;
759 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
760 }
761 /* }}} */
762 
mmc_binary_stats(mmc_request_t * request,const char * type,long slabid,long limit)763 static void mmc_binary_stats(mmc_request_t *request, const char *type, long slabid, long limit) /* {{{ */
764 {
765 	/* stats not supported */
766 	mmc_request_header_t header;
767 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
768 
769 	request->parse = mmc_request_parse_response;
770 	req->next_parse_handler = mmc_request_read_response;
771 
772 	mmc_pack_header(&header, MMC_OP_NOOP, 0, 0, 0, 0);
773 	smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
774 }
775 /* }}} */
776 
mmc_set_sasl_auth_data(mmc_pool_t * pool,mmc_request_t * request,const char * user,const char * password)777 static void mmc_set_sasl_auth_data(mmc_pool_t *pool, mmc_request_t *request, const char *user,  const char *password) /* {{{ */
778 {
779 	const unsigned int key_len = 5;
780 	int prevlen;
781 	mmc_sasl_request_header *header;
782 	mmc_binary_request_t *req = (mmc_binary_request_t *)request;
783 
784 	request->parse = mmc_request_parse_response;
785 	req->next_parse_handler = mmc_request_read_response;
786 
787 	memcpy(request->key, "PLAIN", 5 + 1);
788 
789 	prevlen = request->sendbuf.value.len;
790 
791 	/* allocate space for header */
792 	mmc_buffer_alloc(&(request->sendbuf), sizeof(*header));
793 	request->sendbuf.value.len += sizeof(*header);
794 
795 	/* append key and data */
796 	smart_string_appendl(&(request->sendbuf.value), "PLAIN", 5);
797 
798 	/* initialize header */
799 	header = (mmc_sasl_request_header *)(request->sendbuf.value.c + prevlen);
800 
801 	(header->base).magic = MMC_REQUEST_MAGIC;
802 	(header->base).opcode = MMC_OP_SASL_AUTH;
803 	(header->base).key_len = htons(key_len);
804 	(header->base).extras_len = 0x0;
805 	(header->base).datatype = 0x0;
806 	(header->base)._reserved = 0x0;
807 
808 	(header->base).length = htonl(strlen(user) + strlen(password) + key_len + 2);
809 	(header->base).reqid = htonl(0);
810 	header->base.cas = 0x0;
811 
812 	smart_string_appendl(&(request->sendbuf.value), "\0", 1);
813 	smart_string_appendl(&(request->sendbuf.value), user, strlen(user));
814 	smart_string_appendl(&(request->sendbuf.value), "\0", 1);
815 	smart_string_appendl(&(request->sendbuf.value), password, strlen(password));
816 
817 #if MMC_DEBUG
818 	mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
819 #endif
820 	return;
821 }
822 /* }}} */
823 
824 mmc_protocol_t mmc_binary_protocol = {
825 	mmc_binary_create_request,
826 	mmc_binary_clone_request,
827 	mmc_binary_reset_request,
828 	mmc_binary_free_request,
829 	mmc_binary_get,
830 	mmc_binary_begin_get,
831 	mmc_binary_append_get,
832 	mmc_binary_end_get,
833 	mmc_binary_store,
834 	mmc_binary_delete,
835 	mmc_binary_mutate,
836 	mmc_binary_flush,
837 	mmc_binary_version,
838 	mmc_binary_stats,
839 	mmc_set_sasl_auth_data
840 };
841 
842 /*
843  * Local variables:
844  * tab-width: 4
845  * c-basic-offset: 4
846  * End:
847  * vim600: noet sw=4 ts=4 fdm=marker
848  * vim<600: noet sw=4 ts=4
849  */
850