OPT_FLUSH,
OPT_HASH,
OPT_BINARY,
- OPT_UDP
+ OPT_UDP,
+ OPT_USERNAME,
+ OPT_PASSWD
} memcached_options;
#endif /* CLIENT_OPTIONS */
clients/libutilities.la \
libmemcached/libmemcached.la
+if HAVE_SASL
+CLIENTS_LDADDS+= $(LIBSASL)
+endif
+
bin_PROGRAMS+= \
clients/memcapable \
clients/memcat \
static int opt_displayflag= 0;
static char *opt_servers= NULL;
static char *opt_hash= NULL;
+static char *opt_username;
+static char *opt_passwd;
int main(int argc, char *argv[])
{
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL,
(uint64_t)opt_binary);
- while (optind < argc)
+ if (!initialize_sasl(memc, opt_username, opt_passwd))
+ {
+ memcached_free(memc);
+ return 1;
+ }
+
+ while (optind < argc)
{
string= memcached_get(memc, argv[optind], strlen(argv[optind]),
&string_length, &flags, &rc);
- if (rc == MEMCACHED_SUCCESS)
+ if (rc == MEMCACHED_SUCCESS)
{
if (opt_displayflag)
{
printf("key: %s\nflags: ", argv[optind]);
printf("%x\n", flags);
}
- else
+ else
{
if (opt_verbose)
printf("key: %s\nflags: %x\nlength: %zu\nvalue: ",
}
else if (rc != MEMCACHED_NOTFOUND)
{
- fprintf(stderr, "memcat: %s: memcache error %s",
+ fprintf(stderr, "memcat: %s: memcache error %s",
argv[optind], memcached_strerror(memc, rc));
if (memc->cached_errno)
fprintf(stderr, " system error %s", strerror(memc->cached_errno));
if (opt_hash)
free(opt_hash);
+ shutdown_sasl();
+
return return_code;
}
{(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG},
{(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH},
{(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY},
+ {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME},
+ {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD},
{0, 0, 0, 0},
};
- while (1)
+ while (1)
{
option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
if (option_rv == -1) break;
case OPT_HASH:
opt_hash= strdup(optarg);
break;
+ case OPT_USERNAME:
+ opt_username= optarg;
+ break;
+ case OPT_PASSWD:
+ opt_passwd= optarg;
+ break;
case '?':
/* getopt_long already printed an error message. */
exit(1);
static int opt_method= OPT_SET;
static uint32_t opt_flags= 0;
static time_t opt_expires= 0;
+static char *opt_username;
+static char *opt_passwd;
static long strtol_wrapper(const char *nptr, int base, bool *error)
{
memcached_server_list_free(servers);
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL,
(uint64_t)opt_binary);
+ if (!initialize_sasl(memc, opt_username, opt_passwd))
+ {
+ memcached_free(memc);
+ return 1;
+ }
while (optind < argc)
{
free(opt_servers);
if (opt_hash)
free(opt_hash);
+ shutdown_sasl();
return return_code;
}
{(OPTIONSTRING)"replace", no_argument, NULL, OPT_REPLACE},
{(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH},
{(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY},
+ {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME},
+ {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD},
{0, 0, 0, 0},
};
case OPT_HASH:
opt_hash= strdup(optarg);
break;
- case '?':
+ case OPT_USERNAME:
+ opt_username= optarg;
+ break;
+ case OPT_PASSWD:
+ opt_passwd= optarg;
+ break;
+ case '?':
/* getopt_long already printed an error message. */
exit(1);
default:
static int opt_verbose= 0;
static char *opt_servers= NULL;
static char *opt_hash= NULL;
+static char *opt_username;
+static char *opt_passwd;
/* Print the keys and counter how many were found */
static memcached_return_t key_printer(const memcached_st *ptr __attribute__((unused)),
memcached_server_list_free(servers);
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL,
(uint64_t)opt_binary);
+ if (!initialize_sasl(memc, opt_username, opt_passwd))
+ {
+ memcached_free(memc);
+ return 1;
+ }
rc= memcached_dump(memc, callbacks, NULL, 1);
if (opt_hash)
free(opt_hash);
+ shutdown_sasl();
+
return 0;
}
{(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS},
{(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH},
{(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY},
+ {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME},
+ {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD},
{0, 0, 0, 0}
};
case OPT_HASH:
opt_hash= strdup(optarg);
break;
+ case OPT_USERNAME:
+ opt_username= optarg;
+ break;
+ case OPT_PASSWD:
+ opt_passwd= optarg;
+ break;
case '?':
/* getopt_long already printed an error message. */
exit(1);
static int opt_verbose= 0;
static time_t opt_expire= 0;
static char *opt_servers= NULL;
+static char *opt_username;
+static char *opt_passwd;
#define PROGRAM_NAME "memflush"
#define PROGRAM_DESCRIPTION "Erase all data in a server of memcached servers."
memcached_server_list_free(servers);
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL,
(uint64_t) opt_binary);
-
+
+ if (!initialize_sasl(memc, opt_username, opt_passwd))
+ {
+ memcached_free(memc);
+ return 1;
+ }
+
rc = memcached_flush(memc, opt_expire);
- if (rc != MEMCACHED_SUCCESS)
+ if (rc != MEMCACHED_SUCCESS)
{
- fprintf(stderr, "memflush: memcache error %s",
+ fprintf(stderr, "memflush: memcache error %s",
memcached_strerror(memc, rc));
if (memc->cached_errno)
fprintf(stderr, " system error %s", strerror(memc->cached_errno));
free(opt_servers);
+ shutdown_sasl();
+
return 0;
}
{(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS},
{(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE},
{(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY},
+ {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME},
+ {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD},
{0, 0, 0, 0},
};
int option_index= 0;
int option_rv;
- while (1)
+ while (1)
{
option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
if (option_rv == -1) break;
case OPT_EXPIRE: /* --expire */
opt_expire= (time_t)strtoll(optarg, (char **)NULL, 10);
break;
+ case OPT_USERNAME:
+ opt_username= optarg;
+ break;
+ case OPT_PASSWD:
+ opt_passwd= optarg;
+ break;
case '?':
/* getopt_long already printed an error message. */
exit(1);
static time_t opt_expire= 0;
static char *opt_servers= NULL;
static char *opt_hash= NULL;
+static char *opt_username;
+static char *opt_passwd;
#define PROGRAM_NAME "memrm"
#define PROGRAM_DESCRIPTION "Erase a key or set of keys from a memcached cluster."
memcached_server_list_free(servers);
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL,
(uint64_t) opt_binary);
-
+
+ if (!initialize_sasl(memc, opt_username, opt_passwd))
+ {
+ memcached_free(memc);
+ return 1;
+ }
+
while (optind < argc)
{
if (opt_verbose)
if (opt_hash)
free(opt_hash);
+ shutdown_sasl();
+
return return_code;
}
{(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE},
{(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH},
{(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY},
+ {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME},
+ {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD},
{0, 0, 0, 0},
};
int option_index= 0;
case OPT_HASH:
opt_hash= strdup(optarg);
break;
+ case OPT_USERNAME:
+ opt_username= optarg;
+ break;
+ case OPT_PASSWD:
+ opt_passwd= optarg;
+ break;
case '?':
/* getopt_long already printed an error message. */
exit(1);
case OPT_BINARY: return("Switch to binary protocol.");
case OPT_ANALYZE: return("Analyze the provided servers.");
case OPT_UDP: return("Use UDP protocol when communicating with server.");
+ case OPT_USERNAME: return "Username to use for SASL authentication";
+ case OPT_PASSWD: return "Password to use for SASL authentication";
default: WATCHPOINT_ASSERT(0);
};
printf("\t%s\n\n", description);
printf("Current options. A '=' means the option takes a value.\n\n");
- for (x= 0; long_options[x].name; x++)
+ for (x= 0; long_options[x].name; x++)
{
const char *help_message;
- printf("\t --%s%c\n", long_options[x].name,
- long_options[x].has_arg ? '=' : ' ');
+ printf("\t --%s%c\n", long_options[x].name,
+ long_options[x].has_arg ? '=' : ' ');
if ((help_message= lookup_help(long_options[x].val)))
printf("\t\t%s\n", help_message);
}
}
}
+static char *username;
+static char *passwd;
+
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+static int get_username(void *context, int id, const char **result,
+ unsigned int *len)
+{
+ (void)context;
+ if (!result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME))
+ return SASL_BADPARAM;
+
+ *result= username;
+ if (len)
+ *len= (username == NULL) ? 0 : (unsigned int)strlen(username);
+
+ return SASL_OK;
+}
+
+static int get_password(sasl_conn_t *conn, void *context, int id,
+ sasl_secret_t **psecret)
+{
+ (void)context;
+ static sasl_secret_t* x;
+
+ if (!conn || ! psecret || id != SASL_CB_PASS)
+ return SASL_BADPARAM;
+
+ if (passwd == NULL)
+ {
+ *psecret= NULL;
+ return SASL_OK;
+ }
+
+ size_t len= strlen(passwd);
+ x = realloc(x, sizeof(sasl_secret_t) + len);
+ if (!x)
+ return SASL_NOMEM;
+
+ x->len = len;
+ strcpy((void *)x->data, passwd);
+
+ *psecret = x;
+ return SASL_OK;
+}
+
+/* callbacks we support */
+static sasl_callback_t sasl_callbacks[] = {
+ {
+ SASL_CB_USER, &get_username, NULL
+ }, {
+ SASL_CB_AUTHNAME, &get_username, NULL
+ }, {
+ SASL_CB_PASS, &get_password, NULL
+ }, {
+ SASL_CB_LIST_END, NULL, NULL
+ }
+};
+#endif
+
+bool initialize_sasl(memcached_st *memc, char *user, char *password)
+{
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ if (user != NULL && password != NULL)
+ {
+ username= user;
+ passwd= password;
+
+ if (sasl_client_init(NULL) != SASL_OK)
+ {
+ fprintf(stderr, "Failed to initialize sasl library!\n");
+ return false;
+ }
+ memcached_set_sasl_callbacks(memc, sasl_callbacks);
+ }
+#else
+ (void)memc;
+ (void)user;
+ (void)passwd;
+#endif
+
+ return true;
+}
+
+void shutdown_sasl(void)
+{
+#if LIBMEMCACHED_WITH_SASL_SUPPORT
+ if (username != NULL || passwd != NULL)
+ sasl_done();
+#endif
+}
typedef struct memcached_programs_help_st memcached_programs_help_st;
-struct memcached_programs_help_st
+struct memcached_programs_help_st
{
char *not_used_yet;
};
const struct option *long_options,
memcached_programs_help_st *options);
void process_hash_option(memcached_st *memc, char *opt_hash);
+bool initialize_sasl(memcached_st *memc, char *user, char *password);
+void shutdown_sasl(void);
PANDORA_HAVE_LIBINNODB
PANDORA_PRINT_CALLSTACK
DETECT_BITFIELD
+PANDORA_HAVE_SASL
+
+dnl The sasl functions should only be visible if we build with sasl support
+AS_IF([test "x$ac_cv_sasl" = "xyes"],
+ [LIBMEMCACHED_WITH_SASL_SUPPORT="#define LIBMEMCACHED_WITH_SASL_SUPPORT 1"])
+AC_SUBST(LIBMEMCACHED_WITH_SASL_SUPPORT)
AC_CHECK_HEADERS([atomic.h])
AS_IF([test "x$ac_cv_header_atomic_h" = "xyes"],[
memcached_pool_create.pop \
memcached_pool_destroy.pop \
memcached_pool_pop.pop \
- memcached_pool_push.pop
+ memcached_pool_push.pop
BUILT_SOURCES += ${POOL_PAGES}
RESULT_PAGES= \
memcached_server_list.pop \
memcached_server_add.pop \
memcached_server_add_unix_socket.pop \
- memcached_server_push.pop
+ memcached_server_push.pop
BUILT_SOURCES += ${SERVER_PAGES}
SERVER_ST_PAGES= \
memcached_server_list_free.pop \
memcached_server_list_count.pop \
memcached_server_list_append.pop \
- memcached_servers_parse.pop
+ memcached_servers_parse.pop
BUILT_SOURCES += ${SERVER_ST_PAGES}
SET_PAGES= \
memslap.1 \
memstat.1
+if HAVE_SASL
+POD_FILES+= memcached_sasl.pod
+man_MANS+= \
+ memcached_destroy_sasl_auth_data.3 \
+ memcached_get_sasl_callbacks.3 \
+ memcached_sasl_set_auth_data.3 \
+ memcached_set_sasl_callbacks.3
+
+HTML_FILES+= \
+ memcached_destroy_sasl_auth_data.html \
+ memcached_get_sasl_callbacks.html \
+ memcached_sasl_set_auth_data.html \
+ memcached_set_sasl_callbacks.html
+
+
+SASL_PAGES= \
+ memcached_destroy_sasl_auth_data.pop \
+ memcached_get_sasl_callbacks.pop \
+ memcached_sasl_set_auth_data.pop \
+ memcached_set_sasl_callbacks.pop
+BUILT_SOURCES += ${SASL_PAGES}
+
+${SASL_PAGES}:
+ @rm -f $@
+ ln -s ${top_srcdir}/docs/memcached_sasl.pod ${top_builddir}/docs/$@
+
+endif
+
if BUILD_LIBMEMCACHEDUTIL
man_MANS+= \
libmemcachedutil.3 \
endif
-${CREATE_PAGES}:
+${CREATE_PAGES}:
@rm -f $@
ln -s ${top_srcdir}/docs/memcached_create.pod ${top_builddir}/docs/$@
-${SET_PAGES}:
+${SET_PAGES}:
@rm -f $@
ln -s ${top_srcdir}/docs/memcached_set.pod ${top_builddir}/docs/$@
@rm -f $@
ln -s ${top_srcdir}/docs/memcached_user_data.pod ${top_builddir}/docs/$@
-
${POOL_PAGES}:
@rm -f $@
ln -s ${top_srcdir}/docs/memcached_pool.pod ${top_builddir}/docs/$@
html-local: html-pages html-index
-html-pages: $(HTML_FILES)
+html-pages: $(HTML_FILES)
html-index: html-pages
perl make_index.pl *.html > index.html
--- /dev/null
+=head1 NAME
+
+memcached_set_sasl_callbacks, memcached_get_sasl_callbacks,
+memcached_sasl_set_auth_data, memcached_destroy_sasl_auth_data - SASL support
+
+=head1 LIBRARY
+
+C Client Library for memcached (libmemcached, -lmemcached)
+
+=head1 SYNOPSIS
+
+ #include <memcached.h>
+
+ void memcached_set_sasl_callbacks(memcached_st *ptr,
+ const sasl_callback_t *callbacks)
+
+ const sasl_callback_t *memcached_get_sasl_callbacks(memcached_st *ptr)
+
+ memcached_return memcached_set_sasl_auth_data(memcached_st *ptr,
+ const char *username,
+ const char *password)
+ memcached_return memcached_destroy_sasl_auth_data(memcached_st *ptr)
+
+
+=head1 DESCRIPTION
+
+libmemcached(3) allows you to plug in your own callbacks function used by
+libsasl to perform SASL authentication.
+
+Please note that SASL requires the memcached binary protocol, and you have
+to specify the callbacks before you connect to the server.
+
+memcached_set_sasl_auth_data() is a helper function for you defining
+the basic functionality for you, but it will store the username and password
+in memory. If you choose to use this method you have to call
+memcached_destroy_sasl_auth_data before calling memcached_free to avoid
+a memory leak. You should NOT call memcached_destroy_sasl_auth_data if you
+specify your own callback function with memcached_set_sasl_callbacks().
+
+You as a client user have to initialize libsasl by using sasl_client_init
+before enabling it in libmemcached, and you have to shut down libsasl by
+calling sasl_done() when you are done using SASL from libmemcached.
+
+
+=head1 RETURN
+
+memcached_get_sasl_callbacks() returns the callbacks currently used
+by this memcached handle.
+memcached_get_sasl_set_auth_data() returns MEMCACHED_SUCCESS upon success.
+
+=head1 HOME
+
+To find out more information please check:
+L<http://tangent.org/552/libmemcached.html>
+
+=head1 AUTHOR
+
+Trond Norbye, E<lt>trond.norbye@gmail.comE<gt>
+
+=head1 SEE ALSO
+
+memcached(1) libmemcached(3)
+
+=cut
request.message.body.initial= htonll(initial);
request.message.body.expiration= htonl((uint32_t) expiration);
- if ((memcached_do(instance, request.bytes,
- sizeof(request.bytes), false) != MEMCACHED_SUCCESS) ||
+ memcached_return_t rc;
+ if (((rc= memcached_do(instance, request.bytes,
+ sizeof(request.bytes), false)) != MEMCACHED_SUCCESS) ||
(memcached_io_write(instance, key, key_length, true) == -1))
{
memcached_io_reset(instance);
- return MEMCACHED_WRITE_FAILURE;
+ return (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc;
}
if (no_reply)
@MEMCACHED_BITFIELD@
@DEPRECATED@
+@LIBMEMCACHED_WITH_SASL_SUPPORT@
#define LIBMEMCACHED_VERSION_STRING "@VERSION@"
#define LIBMEMCACHED_VERSION_HEX @PANDORA_HEX_VERSION@
}
}
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ if (ptr->fd != -1 && ptr->root->sasl.callbacks != NULL)
+ {
+ memcached_return rc= memcached_sasl_authenticate_connection(ptr);
+ if (rc != MEMCACHED_SUCCESS)
+ {
+ (void)close(ptr->fd);
+ ptr->fd= -1;
+ return rc;
+ }
+ }
+#endif
+
+
if (ptr->fd != -1)
{
ptr->server_failure_counter= 0;
WATCHPOINT_ASSERT(0);
}
- unlikely ( rc != MEMCACHED_SUCCESS)
+ unlikely ( rc != MEMCACHED_SUCCESS)
{
//@todo create interface around last_discontected_server
memcached_st *root= (memcached_st *)ptr->root;
MEMCACHED_E2BIG,
MEMCACHED_INVALID_ARGUMENTS,
MEMCACHED_KEY_TOO_BIG,
+ MEMCACHED_AUTH_PROBLEM,
+ MEMCACHED_AUTH_FAILURE,
+ MEMCACHED_AUTH_CONTINUE,
MEMCACHED_MAXIMUM_RETURN /* Always add new error code before */
} memcached_return_t;
memcached_return_t rc= MEMCACHED_SUCCESS;
- if ((memcached_do(instance, request.bytes,
- sizeof(request.bytes), false) != MEMCACHED_SUCCESS) ||
+ if (((rc= memcached_do(instance, request.bytes,
+ sizeof(request.bytes), false)) != MEMCACHED_SUCCESS) ||
(memcached_io_write(instance, key,
key_length, flush) == -1))
{
memcached_io_reset(instance);
- rc= MEMCACHED_WRITE_FAILURE;
+ rc= (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc;
}
unlikely (ptr->number_of_replicas > 0)
libmemcached/protocol_handler.h \
libmemcached/quit.h \
libmemcached/result.h \
+ libmemcached/sasl.h \
libmemcached/server.h \
libmemcached/server_list.h \
libmemcached/stats.h \
libmemcached/protocol/pedantic.c \
libmemcached/protocol/protocol_handler.c
-libmemcached_libmemcachedprotocol_la_LDFLAGS= ${AM_LDFLAGS} -version-info 0:0:0
+libmemcached_libmemcachedprotocol_la_LDFLAGS= ${AM_LDFLAGS} -version-info 0:0:0
noinst_LTLIBRARIES+= \
libmemcached/libmemcachedcallbacks.la
libmemcached_libmemcachedprotocol_la_DEPENDENCIES=libmemcached/libbyteorder.la
endif
+if HAVE_SASL
+libmemcached_libmemcached_la_LDFLAGS+= $(LIBSASL)
+libmemcached_libmemcached_la_SOURCES += libmemcached/sasl.c
+endif
+
if HAVE_DTRACE
BUILT_SOURCES+= libmemcached/dtrace_probes.h
libmemcached_libmemcached_la_SOURCES += libmemcached/libmemcached_probes.d
* Use and distribution licensed under the BSD license. See
* the COPYING file in the parent directory for full text.
*
- * Summary:
+ * Summary:
*
*/
self->get_key_failure= NULL;
self->delete_trigger= NULL;
self->callbacks= NULL;
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ self->sasl.callbacks= NULL;
+ self->sasl.is_allocated= false;
+#endif
return true;
}
if (ptr->continuum)
libmemcached_free(ptr, ptr->continuum);
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ if (ptr->sasl.callbacks != NULL)
+ {
+ memcached_destroy_sasl_auth_data(ptr);
+ }
+#endif
+
if (memcached_is_allocated(ptr))
{
libmemcached_free(ptr, ptr);
new_clone->prefix_key_length= source->prefix_key_length;
}
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ if (source->sasl.callbacks)
+ {
+ if (memcached_clone_sasl(new_clone, source) != MEMCACHED_SUCCESS)
+ {
+ memcached_free(new_clone);
+ return NULL;
+ }
+ }
+#endif
+
rc= run_distribution(new_clone);
if (rc != MEMCACHED_SUCCESS)
#include <libmemcached/strerror.h>
#include <libmemcached/verbosity.h>
#include <libmemcached/version.h>
+#include <libmemcached/sasl.h>
struct memcached_st {
/**
memcached_trigger_key_fn get_key_failure;
memcached_trigger_delete_key_fn delete_trigger;
memcached_callback_st *callbacks;
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ struct {
+ const sasl_callback_t *callbacks;
+ /*
+ ** Did we allocate data inside the callbacks, or did the user
+ ** supply that.
+ */
+ bool is_allocated MEMCACHED_BITFIELD;
+ } sasl;
+
+#endif
char prefix_key[MEMCACHED_PREFIX_KEY_MAX_SIZE];
struct {
bool is_allocated MEMCACHED_BITFIELD;
*
* Copy: See Copyright for the status of this software.
*
- * Author: Trond Norbye <trond.norbye@gmail.com>
+ * Author: Trond Norbye <trond.norbye@sun.com>
*/
#ifndef PROTOCOL_BINARY_H
#define PROTOCOL_BINARY_H
-#include <stdint.h>
+/**
+ * \addtogroup Protocol
+ * @{
+ */
/**
* This file contains definitions of the constants and packet formats
PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04,
PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05,
PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06,
+ PROTOCOL_BINARY_RESPONSE_AUTH_ERROR = 0x20,
+ PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE = 0x21,
PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81,
PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82,
PROTOCOL_BINARY_RESPONSE_PAUSE = 0xfe00,
PROTOCOL_BINARY_RESPONSE_EIO = 0xff00
+
} protocol_binary_response_status;
/**
PROTOCOL_BINARY_CMD_APPENDQ = 0x19,
PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a,
+ PROTOCOL_BINARY_CMD_SASL_LIST_MECHS = 0x20,
+ PROTOCOL_BINARY_CMD_SASL_AUTH = 0x21,
+ PROTOCOL_BINARY_CMD_SASL_STEP = 0x22,
+
/* These commands are used for range operations and exist within
* this header for use in other projects. Range operations are
* not expected to be implemented in the memcached server itself.
typedef protocol_binary_request_rangeop protocol_binary_request_rdecr;
typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq;
+ /**
+ * @}
+ */
+
#ifdef __cplusplus
}
#endif
return rc;
}
-memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
+memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
char *buffer, size_t buffer_length,
memcached_result_st *result)
{
/*
* The previous implementation purged all pending requests and just
* returned the last one. Purge all pending messages to ensure backwards
- * compatibility.
+ * compatibility.
*/
if (ptr->root->flags.binary_protocol == false)
while (memcached_server_response_count(ptr) > 1)
unlikely (rc != MEMCACHED_END &&
rc != MEMCACHED_STORED &&
rc != MEMCACHED_SUCCESS &&
- rc != MEMCACHED_STAT &&
+ rc != MEMCACHED_STAT &&
rc != MEMCACHED_DELETED &&
rc != MEMCACHED_NOTFOUND &&
- rc != MEMCACHED_NOTSTORED &&
+ rc != MEMCACHED_NOTSTORED &&
rc != MEMCACHED_DATA_EXISTS)
return rc;
}
}
value_ptr= memcached_string_value_mutable(&result->value);
- /*
- We read the \r\n into the string since not doing so is more
+ /*
+ We read the \r\n into the string since not doing so is more
cycles then the waster of memory to do so.
We are null terminating through, which will most likely make
memcached_server_response_increment(ptr);
return MEMCACHED_STAT;
}
- else if (buffer[1] == 'E') /* SERVER_ERROR */
+ else if (buffer[1] == 'E') /* SERVER_ERROR */
{
char *rel_ptr;
char *startptr= buffer + 13, *endptr= startptr;
while (*endptr != '\r' && *endptr != '\n') endptr++;
- /*
+ /*
Yes, we could make this "efficent" but to do that we would need
to maintain more state for the size of the buffer. Why waste
memory in the struct, which is important, for something that
rarely should happen?
*/
rel_ptr= (char *)libmemcached_realloc(ptr->root,
- ptr->cached_server_error,
+ ptr->cached_server_error,
(size_t) (endptr - startptr + 1));
if (rel_ptr == NULL)
{
protocol_binary_response_header header;
- unlikely (memcached_safe_read(ptr, &header.bytes,
+ unlikely (memcached_safe_read(ptr, &header.bytes,
sizeof(header.bytes)) != MEMCACHED_SUCCESS)
return MEMCACHED_UNKNOWN_READ_FAILURE;
- unlikely (header.response.magic != PROTOCOL_BINARY_RES)
+ unlikely (header.response.magic != PROTOCOL_BINARY_RES)
return MEMCACHED_PROTOCOL_ERROR;
/*
header.response.cas= ntohll(header.response.cas);
uint32_t bodylen= header.response.bodylen;
- if (header.response.status == 0)
+ if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS ||
+ header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
{
switch (header.response.opcode)
{
/*
* We didn't increment the response counter for the GETKQ packet
* (only the final NOOP), so we need to increment the counter again.
- */
- memcached_server_response_increment(ptr);
+ */
+ memcached_server_response_increment(ptr);
/* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_GETK:
{
bodylen -= header.response.extlen;
result->key_length= keylen;
- if (memcached_safe_read(ptr, result->item_key, keylen) != MEMCACHED_SUCCESS)
+ if (memcached_safe_read(ptr, result->item_key, keylen) != MEMCACHED_SUCCESS)
return MEMCACHED_UNKNOWN_READ_FAILURE;
bodylen -= keylen;
if (memcached_string_check(&result->value,
- bodylen) != MEMCACHED_SUCCESS)
+ bodylen) != MEMCACHED_SUCCESS)
return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
char *vptr= memcached_string_value_mutable(&result->value);
- if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
+ if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
return MEMCACHED_UNKNOWN_READ_FAILURE;
- memcached_string_set_length(&result->value, bodylen);
- }
+ memcached_string_set_length(&result->value, bodylen);
+ }
break;
case PROTOCOL_BINARY_CMD_INCREMENT:
case PROTOCOL_BINARY_CMD_DECREMENT:
{
- if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
+ if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
return MEMCACHED_PROTOCOL_ERROR;
WATCHPOINT_ASSERT(bodylen == buffer_length);
uint64_t val;
- if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS)
+ if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS)
return MEMCACHED_UNKNOWN_READ_FAILURE;
val= ntohll(val);
memcpy(buffer, &val, sizeof(val));
- }
+ }
break;
+ case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
case PROTOCOL_BINARY_CMD_VERSION:
{
memset(buffer, 0, buffer_length);
return MEMCACHED_UNKNOWN_READ_FAILURE;
else if (memcached_safe_read(ptr, buffer, bodylen) != MEMCACHED_SUCCESS)
return MEMCACHED_UNKNOWN_READ_FAILURE;
- }
+ }
break;
case PROTOCOL_BINARY_CMD_FLUSH:
case PROTOCOL_BINARY_CMD_QUIT:
{
WATCHPOINT_ASSERT(bodylen == 0);
return MEMCACHED_SUCCESS;
- }
+ }
case PROTOCOL_BINARY_CMD_NOOP:
{
WATCHPOINT_ASSERT(bodylen == 0);
else if (bodylen + 1 > buffer_length)
/* not enough space in buffer.. should not happen... */
return MEMCACHED_UNKNOWN_READ_FAILURE;
- else
+ else
{
- size_t keylen= header.response.keylen;
+ size_t keylen= header.response.keylen;
memset(buffer, 0, buffer_length);
if (memcached_safe_read(ptr, buffer, keylen) != MEMCACHED_SUCCESS ||
- memcached_safe_read(ptr, buffer + keylen + 1,
+ memcached_safe_read(ptr, buffer + keylen + 1,
bodylen - keylen) != MEMCACHED_SUCCESS)
return MEMCACHED_UNKNOWN_READ_FAILURE;
}
- }
+ }
+ break;
+
+ case PROTOCOL_BINARY_CMD_SASL_AUTH:
+ case PROTOCOL_BINARY_CMD_SASL_STEP:
+ {
+ memcached_result_reset(result);
+ result->item_cas= header.response.cas;
+
+ if (memcached_string_check(&result->value,
+ bodylen) != MEMCACHED_SUCCESS)
+ return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+
+ char *vptr= memcached_string_value_mutable(&result->value);
+ if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
+ return MEMCACHED_UNKNOWN_READ_FAILURE;
+
+ memcached_string_set_length(&result->value, bodylen);
+ }
break;
default:
{
/* Command not implemented yet! */
WATCHPOINT_ASSERT(0);
return MEMCACHED_PROTOCOL_ERROR;
- }
+ }
}
- }
- else if (header.response.bodylen)
+ }
+ else if (header.response.bodylen)
{
/* What should I do with the error message??? just discard it for now */
char hole[SMALL_STRING_LEN];
- while (bodylen > 0)
+ while (bodylen > 0)
{
size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
if (memcached_safe_read(ptr, hole, nr) != MEMCACHED_SUCCESS)
}
memcached_return_t rc= MEMCACHED_SUCCESS;
- unlikely(header.response.status != 0)
- switch (header.response.status)
+ unlikely(header.response.status != 0)
+ switch (header.response.status)
{
case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
rc= MEMCACHED_NOTFOUND;
case PROTOCOL_BINARY_RESPONSE_ENOMEM:
rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
break;
+ case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE:
+ rc= MEMCACHED_AUTH_CONTINUE;
+ break;
+ case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
+ rc= MEMCACHED_AUTH_FAILURE;
+ break;
case PROTOCOL_BINARY_RESPONSE_EINVAL:
case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
default:
--- /dev/null
+/* LibMemcached
+ * Copyright (C) 2006-2010 Brian Aker
+ * All rights reserved.
+ *
+ * Use and distribution licensed under the BSD license. See
+ * the COPYING file in the parent directory for full text.
+ *
+ * Summary: interface for memcached server
+ * Description: main include file for libmemcached
+ *
+ */
+#include "common.h"
+
+void memcached_set_sasl_callbacks(memcached_st *ptr,
+ const sasl_callback_t *callbacks)
+{
+ ptr->sasl.callbacks= callbacks;
+ ptr->sasl.is_allocated= false;
+}
+
+const sasl_callback_t *memcached_get_sasl_callbacks(memcached_st *ptr)
+{
+ return ptr->sasl.callbacks;
+}
+
+/**
+ * Resolve the names for both ends of a connection
+ * @param fd socket to check
+ * @param laddr local address (out)
+ * @param raddr remote address (out)
+ * @return true on success false otherwise (errno contains more info)
+ */
+static bool resolve_names(int fd, char *laddr, char *raddr)
+{
+ char host[NI_MAXHOST];
+ char port[NI_MAXSERV];
+ struct sockaddr_storage saddr;
+ socklen_t salen= sizeof(saddr);
+
+ if ((getsockname(fd, (struct sockaddr *)&saddr, &salen) < 0) ||
+ (getnameinfo((struct sockaddr *)&saddr, salen, host, sizeof(host),
+ port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0))
+ {
+ return false;
+ }
+
+ (void)sprintf(laddr, "%s;%s", host, port);
+ salen= sizeof(saddr);
+
+ if ((getpeername(fd, (struct sockaddr *)&saddr, &salen) < 0) ||
+ (getnameinfo((struct sockaddr *)&saddr, salen, host, sizeof(host),
+ port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0))
+ {
+ return false;
+ }
+
+ (void)sprintf(raddr, "%s;%s", host, port);
+
+ return true;
+}
+
+memcached_return_t memcached_sasl_authenticate_connection(memcached_server_st *server)
+{
+ memcached_return_t rc;
+
+ /* SANITY CHECK: SASL can only be used with the binary protocol */
+ unlikely (!server->root->flags.binary_protocol)
+ return MEMCACHED_FAILURE;
+
+ /* Try to get the supported mech from the server. Servers without SASL
+ * support will return UNKNOWN COMMAND, so we can just treat that
+ * as authenticated
+ */
+ protocol_binary_request_no_extras request= {
+ .message.header.request= {
+ .magic= PROTOCOL_BINARY_REQ,
+ .opcode= PROTOCOL_BINARY_CMD_SASL_LIST_MECHS
+ }
+ };
+
+ if (memcached_io_write(server, request.bytes,
+ sizeof(request.bytes), 1) != sizeof(request.bytes))
+ {
+ return MEMCACHED_WRITE_FAILURE;
+ }
+
+ memcached_server_response_increment(server);
+
+ char mech[MEMCACHED_MAX_BUFFER];
+ rc= memcached_response(server, mech, sizeof(mech), NULL);
+ if (rc != MEMCACHED_SUCCESS)
+ {
+ if (rc == MEMCACHED_PROTOCOL_ERROR)
+ {
+ /* If the server doesn't support SASL it will return PROTOCOL_ERROR.
+ * This error may also be returned for other errors, but let's assume
+ * that the server don't support SASL and treat it as success and
+ * let the client fail with the next operation if the error was
+ * caused by another problem....
+ */
+ rc= MEMCACHED_SUCCESS;
+ }
+
+ return rc;
+ }
+
+ /* set ip addresses */
+ char laddr[NI_MAXHOST + NI_MAXSERV];
+ char raddr[NI_MAXHOST + NI_MAXSERV];
+
+ unlikely (!resolve_names(server->fd, laddr, raddr))
+ {
+ server->cached_errno= errno;
+ return MEMCACHED_ERRNO;
+ }
+
+ sasl_conn_t *conn;
+ int ret= sasl_client_new("memcached", server->hostname, laddr, raddr,
+ server->root->sasl.callbacks, 0, &conn);
+ if (ret != SASL_OK)
+ {
+ return MEMCACHED_AUTH_PROBLEM;
+ }
+
+ const char *data;
+ const char *chosenmech;
+ unsigned int len;
+ ret= sasl_client_start(conn, mech, NULL, &data, &len, &chosenmech);
+
+ if (ret != SASL_OK && ret != SASL_CONTINUE)
+ {
+ rc= MEMCACHED_AUTH_PROBLEM;
+ goto end;
+ }
+
+ uint16_t keylen= (uint16_t)strlen(chosenmech);
+ request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SASL_AUTH;
+ request.message.header.request.keylen= htons(keylen);
+ request.message.header.request.bodylen= htonl(len + keylen);
+
+ do {
+ /* send the packet */
+ if (memcached_io_write(server, request.bytes,
+ sizeof(request.bytes), 0) != sizeof(request.bytes) ||
+ memcached_io_write(server, chosenmech, keylen, 0) != keylen ||
+ memcached_io_write(server, data, len, 1) != (ssize_t)len)
+ {
+ rc= MEMCACHED_WRITE_FAILURE;
+ goto end;
+ }
+ memcached_server_response_increment(server);
+
+ /* read the response */
+ rc= memcached_response(server, NULL, 0, NULL);
+ if (rc != MEMCACHED_AUTH_CONTINUE)
+ {
+ goto end;
+ }
+
+ ret= sasl_client_step(conn, memcached_result_value(&server->root->result),
+ (unsigned int)memcached_result_length(&server->root->result),
+ NULL, &data, &len);
+
+ if (ret != SASL_OK && ret != SASL_CONTINUE)
+ {
+ rc= MEMCACHED_AUTH_PROBLEM;
+ goto end;
+ }
+
+ request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SASL_STEP;
+ request.message.header.request.bodylen= htonl(len + keylen);
+ } while (true);
+
+end:
+ /* Release resources */
+ sasl_dispose(&conn);
+
+ return rc;
+}
+
+static int get_username(void *context, int id, const char **result,
+ unsigned int *len)
+{
+ if (!context || !result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME))
+ {
+ return SASL_BADPARAM;
+ }
+
+ *result= context;
+ if (len)
+ {
+ *len= (unsigned int)strlen(*result);
+ }
+
+ return SASL_OK;
+}
+
+static int get_password(sasl_conn_t *conn, void *context, int id,
+ sasl_secret_t **psecret)
+{
+ if (!conn || ! psecret || id != SASL_CB_PASS)
+ {
+ return SASL_BADPARAM;
+ }
+
+ *psecret= context;
+
+ return SASL_OK;
+}
+
+memcached_return_t memcached_set_sasl_auth_data(memcached_st *ptr,
+ const char *username,
+ const char *password)
+{
+ if (ptr == NULL || username == NULL ||
+ password == NULL || ptr->sasl.callbacks != NULL)
+ {
+ return MEMCACHED_FAILURE;
+ }
+
+ sasl_callback_t *cb= libmemcached_calloc(ptr, 4, sizeof(sasl_callback_t));
+ char *name= libmemcached_malloc(ptr, strlen(username) + 1);
+ sasl_secret_t *secret= libmemcached_malloc(ptr, strlen(password) + 1 + sizeof(*secret))
+;
+ if (cb == NULL || name == NULL || secret == NULL)
+ {
+ libmemcached_free(ptr, cb);
+ libmemcached_free(ptr, name);
+ libmemcached_free(ptr, secret);
+ return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+ }
+
+ secret->len= strlen(password);
+ strcpy((void*)secret->data, password);
+
+ cb[0].id= SASL_CB_USER;
+ cb[0].proc= get_username;
+ cb[0].context= strcpy(name, username);
+ cb[1].id= SASL_CB_AUTHNAME;
+ cb[1].proc= get_username;
+ cb[1].context= name;
+ cb[2].id= SASL_CB_PASS;
+ cb[2].proc= get_password;
+ cb[2].context= secret;
+ cb[3].id= SASL_CB_LIST_END;
+
+ ptr->sasl.callbacks= cb;
+ ptr->sasl.is_allocated= true;
+
+ return MEMCACHED_SUCCESS;
+}
+
+memcached_return_t memcached_destroy_sasl_auth_data(memcached_st *ptr)
+{
+ if (ptr == NULL || ptr->sasl.callbacks == NULL)
+ {
+ return MEMCACHED_FAILURE;
+ }
+
+ if (ptr->sasl.is_allocated)
+ {
+ libmemcached_free(ptr, ptr->sasl.callbacks[0].context);
+ libmemcached_free(ptr, ptr->sasl.callbacks[2].context);
+ libmemcached_free(ptr, (void*)ptr->sasl.callbacks);
+ ptr->sasl.is_allocated= false;
+ }
+
+ ptr->sasl.callbacks= NULL;
+
+ return MEMCACHED_SUCCESS;
+}
+
+memcached_return_t memcached_clone_sasl(memcached_st *clone, const memcached_st *source)
+{
+ /* Hopefully we are using our own callback mechanisms.. */
+ if (source->sasl.callbacks[0].id == SASL_CB_USER &&
+ source->sasl.callbacks[0].proc == get_username &&
+ source->sasl.callbacks[1].id == SASL_CB_AUTHNAME &&
+ source->sasl.callbacks[1].proc == get_username &&
+ source->sasl.callbacks[2].id == SASL_CB_PASS &&
+ source->sasl.callbacks[2].proc == get_password &&
+ source->sasl.callbacks[3].id == SASL_CB_LIST_END)
+ {
+ sasl_secret_t *secret= source->sasl.callbacks[2].context;
+ return memcached_set_sasl_auth_data(clone,
+ source->sasl.callbacks[0].context,
+ (const char*)secret->data);
+ }
+
+ /*
+ * But we're not. It may work if we know what the user tries to pass
+ * into the list, but if we don't know the ID we don't know how to handle
+ * the context...
+ */
+ size_t total= 0;
+
+ while (source->sasl.callbacks[total].id != SASL_CB_LIST_END) {
+ switch (source->sasl.callbacks[total].id)
+ {
+ case SASL_CB_USER:
+ case SASL_CB_AUTHNAME:
+ case SASL_CB_PASS:
+ break;
+ default:
+ /* I don't know how to deal with this... */
+ return MEMCACHED_NOT_SUPPORTED;
+ }
+
+ ++total;
+ }
+
+ sasl_callback_t *cb= libmemcached_calloc(clone, total + 1,
+ sizeof(sasl_callback_t));
+ if (cb == NULL)
+ {
+ return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+ }
+ memcpy(cb, source->sasl.callbacks, (total + 1) * sizeof(sasl_callback_t));
+
+ /* Now update the context... */
+ for (size_t x= 0; x < total; ++x)
+ {
+ if (cb[x].id == SASL_CB_USER || cb[x].id == SASL_CB_AUTHNAME)
+ {
+ cb[x].context= libmemcached_malloc(clone, strlen(source->sasl.callbacks[x].context));
+ if (cb[x].context == NULL)
+ {
+ /* Failed to allocate memory, clean up previously allocated memory */
+ for (size_t y= 0; y < x; ++y)
+ {
+ libmemcached_free(clone, clone->sasl.callbacks[y].context);
+ }
+
+ libmemcached_free(clone, cb);
+ return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+ }
+ strcpy(cb[x].context, source->sasl.callbacks[x].context);
+ }
+ else
+ {
+ sasl_secret_t *src = source->sasl.callbacks[x].context;
+ sasl_secret_t *n = libmemcached_malloc(clone, src->len + 1 + sizeof(*n));
+ if (n == NULL)
+ {
+ /* Failed to allocate memory, clean up previously allocated memory */
+ for (size_t y= 0; y < x; ++y)
+ {
+ libmemcached_free(clone, clone->sasl.callbacks[y].context);
+ }
+
+ libmemcached_free(clone, cb);
+ return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+ }
+ memcpy(n, src, src->len + 1 + sizeof(*n));
+ cb[x].context= n;
+ }
+ }
+
+ clone->sasl.callbacks= cb;
+ clone->sasl.is_allocated= true;
+
+ return MEMCACHED_SUCCESS;
+}
--- /dev/null
+/* LibMemcached
+ * Copyright (C) 2006-2010 Brian Aker
+ * All rights reserved.
+ *
+ * Use and distribution licensed under the BSD license. See
+ * the COPYING file in the parent directory for full text.
+ *
+ * Summary: interface for memcached server
+ * Description: main include file for libmemcached
+ *
+ */
+#ifndef LIBMEMCACHED_MEMCACHED_SASL_H
+#define LIBMEMCACHED_MEMCACHED_SASL_H
+
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+#include <sasl/sasl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBMEMCACHED_API
+void memcached_set_sasl_callbacks(memcached_st *ptr,
+ const sasl_callback_t *callbacks);
+
+LIBMEMCACHED_API
+memcached_return_t memcached_set_sasl_auth_data(memcached_st *ptr,
+ const char *username,
+ const char *password);
+
+LIBMEMCACHED_API
+memcached_return_t memcached_destroy_sasl_auth_data(memcached_st *ptr);
+
+
+LIBMEMCACHED_API
+const sasl_callback_t *memcached_get_sasl_callbacks(memcached_st *ptr);
+
+LIBMEMCACHED_LOCAL
+memcached_return_t memcached_clone_sasl(memcached_st *clone, const memcached_st *source);
+
+LIBMEMCACHED_LOCAL
+memcached_return_t memcached_sasl_authenticate_connection(memcached_server_st *server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBMEMCACHED_WITH_SASL_SUPPORT */
+
+#endif /* LIBMEMCACHED_MEMCACHED_SASL_H */
}
/* write the header */
- if ((memcached_do(server, (const char*)request.bytes, send_length, false) != MEMCACHED_SUCCESS) ||
+ memcached_return_t rc;
+ if (((rc= memcached_do(server, (const char*)request.bytes,
+ send_length, false)) != MEMCACHED_SUCCESS) ||
(memcached_io_write(server, key, key_length, false) == -1) ||
(memcached_io_write(server, value, value_length, flush) == -1))
{
memcached_io_reset(server);
- return MEMCACHED_WRITE_FAILURE;
+ return (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc;
}
unlikely (verb == SET_OP && ptr->number_of_replicas > 0)
return "INVALID ARGUMENTS";
case MEMCACHED_KEY_TOO_BIG:
return "KEY RETURNED FROM SERVER WAS TOO LARGE";
+ case MEMCACHED_AUTH_PROBLEM:
+ return "FAILED TO SEND AUTHENTICATION TO SERVER";
+ case MEMCACHED_AUTH_FAILURE:
+ return "AUTHENTICATION FAILURE";
+ case MEMCACHED_AUTH_CONTINUE:
+ return "CONTINUE AUTHENTICATION";
case MEMCACHED_MAXIMUM_RETURN:
return "Gibberish returned!";
default:
--- /dev/null
+dnl Copyright (C) 2009 Sun Microsystems
+dnl This file is free software; Sun Microsystems
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([_PANDORA_SEARCH_SASL],[
+ AC_REQUIRE([AC_LIB_PREFIX])
+
+ dnl --------------------------------------------------------------------
+ dnl Check for sasl
+ dnl --------------------------------------------------------------------
+ AC_ARG_ENABLE([sasl],
+ [AS_HELP_STRING([--disable-sasl],
+ [Build with sasl support @<:@default=on@:>@])],
+ [ac_enable_sasl="$enableval"],
+ [ac_enable_sasl="yes"])
+
+ AS_IF([test "x$ac_enable_sasl" = "xyes"],
+ [
+ AC_LIB_HAVE_LINKFLAGS(sasl,,[
+ #include <stdlib.h>
+ #include <sasl/sasl.h>
+ ],[
+ sasl_server_init(NULL, NULL);
+ ])
+
+ AS_IF([test "x${ac_cv_libsasl}" != "xyes" ],
+ [
+ AC_LIB_HAVE_LINKFLAGS(sasl2,,[
+ #include <stdlib.h>
+ #include <sasl/sasl.h>
+ ],[
+ sasl_server_init(NULL, NULL);
+ ])
+ HAVE_LIBSASL="$HAVE_LIBSASL2"
+ LIBSASL="$LIBSASL2"
+ LIBSASL_PREFIX="$LIBSASL2_PREFIX"
+ LTLIBSASL="$LT_LIBSASL2"
+ ])
+ ])
+
+ AS_IF([test "x${ac_cv_libsasl}" = "xyes" -o "x${ac_cv_libsasl2}" = "xyes"],
+ [ac_cv_sasl=yes],
+ [ac_cv_sasl=no])
+
+ AM_CONDITIONAL(HAVE_LIBSASL, [test "x${ac_cv_libsasl}" = "xyes"])
+ AM_CONDITIONAL(HAVE_LIBSASL2, [test "x${ac_cv_libsasl2}" = "xyes"])
+ AM_CONDITIONAL(HAVE_SASL, [test "x${ac_cv_sasl}" = "xyes"])
+])
+
+AC_DEFUN([PANDORA_HAVE_SASL],[
+ AC_REQUIRE([_PANDORA_SEARCH_SASL])
+])
+
+AC_DEFUN([PANDORA_REQUIRE_SASL],[
+ AC_REQUIRE([_PANDORA_SEARCH_SASL])
+ AS_IF([test "x${ac_cv_sasl}" = "xno"],
+ AC_MSG_ERROR([SASL (libsasl or libsasl2) is required for ${PACKAGE}]))
+])
+
+AC_DEFUN([_PANDORA_SEARCH_LIBSASL],[
+ AC_REQUIRE([AC_LIB_PREFIX])
+
+ dnl --------------------------------------------------------------------
+ dnl Check for libsasl
+ dnl --------------------------------------------------------------------
+
+ AC_ARG_ENABLE([libsasl],
+ [AS_HELP_STRING([--disable-libsasl],
+ [Build with libsasl support @<:@default=on@:>@])],
+ [ac_enable_libsasl="$enableval"],
+ [ac_enable_libsasl="yes"])
+
+ AS_IF([test "x$ac_enable_libsasl" = "xyes"],[
+ AC_LIB_HAVE_LINKFLAGS(sasl,,[
+ #include <stdlib.h>
+ #include <sasl/sasl.h>
+ ],[
+ sasl_server_init(NULL, NULL);
+ ])
+ ],[
+ ac_cv_libsasl="no"
+ ])
+
+ AM_CONDITIONAL(HAVE_LIBSASL, [test "x${ac_cv_libsasl}" = "xyes"])
+])
+
+AC_DEFUN([PANDORA_HAVE_LIBSASL],[
+ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL])
+])
+
+AC_DEFUN([PANDORA_REQUIRE_LIBSASL],[
+ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL])
+ AS_IF([test "x${ac_cv_libsasl}" = "xno"],
+ AC_MSG_ERROR([libsasl is required for ${PACKAGE}]))
+])
+
+AC_DEFUN([_PANDORA_SEARCH_LIBSASL2],[
+ AC_REQUIRE([AC_LIB_PREFIX])
+
+ dnl --------------------------------------------------------------------
+ dnl Check for libsasl2
+ dnl --------------------------------------------------------------------
+
+ AC_ARG_ENABLE([libsasl2],
+ [AS_HELP_STRING([--disable-libsasl2],
+ [Build with libsasl2 support @<:@default=on@:>@])],
+ [ac_enable_libsasl2="$enableval"],
+ [ac_enable_libsasl2="yes"])
+
+ AS_IF([test "x$ac_enable_libsasl2" = "xyes"],[
+ AC_LIB_HAVE_LINKFLAGS(sasl2,,[
+ #include <stdlib.h>
+ #include <sasl2/sasl2.h>
+ ],[
+ sasl2_server_init(NULL, NULL);
+ ])
+ ],[
+ ac_cv_libsasl2="no"
+ ])
+
+ AM_CONDITIONAL(HAVE_LIBSASL2, [test "x${ac_cv_libsasl2}" = "xyes"])
+])
+
+AC_DEFUN([PANDORA_HAVE_LIBSASL2],[
+ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL2])
+])
+
+AC_DEFUN([PANDORA_REQUIRE_LIBSASL2],[
+ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL2])
+ AS_IF([test "x${ac_cv_libsasl2}" = "xno"],
+ AC_MSG_ERROR([libsasl2 is required for ${PACKAGE}]))
+])
[AC_LANG_PUSH([C])
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -I${srcdir}"
- AC_RUN_IFELSE([
+ AC_RUN_IFELSE([
AC_LANG_PROGRAM([[
+#include <inttypes.h>
#include "libmemcached/memcached/protocol_binary.h"
]],[[
protocol_binary_request_set request;
if (sizeof(request) != sizeof(request.bytes)) {
return 1;
}
- ]])],, AC_MSG_ERROR([Unsupported struct padding done by compiler.]))
+ ]])],, AC_MSG_ERROR([Unsupported struct padding done by compiler.]))
CFLAGS="$save_CFLAGS"
AC_LANG_POP
])
tests_testapp_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING)
tests_testapp_SOURCES= tests/mem_functions.c
-tests_testapp_LDADD= \
+tests_testapp_DEPENDENCIES= \
clients/libgenexec.la \
tests/libserver.la \
tests/libtest.la \
libmemcached/libmemcachedinternal.la \
-$(TESTS_LDADDS)
-tests_testapp_DEPENDENCIES= $(tests_testapp_LDADD)
+ $(TESTS_LDADDS)
+tests_testapp_LDADD= $(tests_testapp_DEPENDENCIES) $(LIBSASL)
tests_testplus_SOURCES= tests/plus.cpp
-tests_testplus_LDADD= tests/libtest.la tests/libserver.la $(TESTS_LDADDS)
-tests_testplus_DEPENDENCIES= $(tests_testplus_LDADD)
+tests_testplus_DEPENDENCIES= tests/libtest.la tests/libserver.la $(TESTS_LDADDS)
+tests_testplus_LDADD= $(tests_testplus_DEPENDENCIES) $(LIBSASL)
tests_atomsmasher_SOURCES= tests/atomsmasher.c
-tests_atomsmasher_LDADD= \
+tests_atomsmasher_DEPENDENCIES= \
clients/libgenexec.la \
tests/libserver.la \
tests/libtest.la \
$(TESTS_LDADDS)
-tests_atomsmasher_DEPENDENCIES= $(tests_atomsmasher_LDADD)
+tests_atomsmasher_LDADD= $(tests_atomsmasher_DEPENDENCIES) $(LIBSASL)
tests_testudp_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING)
tests_testudp_SOURCES= tests/mem_udp.c
-tests_testudp_LDADD= \
+tests_testudp_DEPENDENCIES= \
clients/libgenexec.la \
tests/libserver.la \
tests/libtest.la \
$(TESTS_LDADDS)
-tests_testudp_DEPENDENCIES= $(tests_testudp_LDADD)
+tests_testudp_LDADD= $(tests_testudp_DEPENDENCIES) $(LIBSASL)
tests_startservers_SOURCES= tests/start.c
-tests_startservers_LDADD= tests/libserver.la $(TESTS_LDADDS)
-tests_startservers_DEPENDENCIES= $(tests_startservers_LDADD)
+tests_startservers_DEPENDENCIES= tests/libserver.la $(TESTS_LDADDS)
+tests_startservers_LDADD= $(tests_startservers_DEPENDENCIES) $(LIBSASL)
tests_testhashkit_SOURCES = tests/hashkit_functions.c
-tests_testhashkit_LDADD = tests/libtest.la libhashkit/libhashkit.la
-tests_testhashkit_DEPENDENCIES = $(tests_testhashkit_LDADD)
+tests_testhashkit_DEPENDENCIES = tests/libtest.la libhashkit/libhashkit.la
+tests_testhashkit_LDADD = $(tests_testhashkit_DEPENDENCIES) $(LIBSASL)
tests_hashplus_SOURCES = tests/hash_plus.cc
-tests_hashplus_LDADD = $(tests_testhashkit_LDADD)
-tests_hashplus_DEPENDENCIES = $(tests_testhashkit_LDADD)
+tests_hashplus_DEPENDENCIES = $(tests_testhashkit_DEPENDENCIES)
+tests_hashplus_LDADD = $(tests_testhashkit_DEPENDENCIES) $(LIBSASL)
tests_memplus_SOURCES = tests/mem_plus.cc
-tests_memplus_LDADD = tests/libtest.la tests/libserver.la libmemcached/libmemcached.la
-tests_memplus_DEPENDENCIES = $(tests_memplus_LDADD)
+tests_memplus_DEPENDENCIES = tests/libtest.la tests/libserver.la libmemcached/libmemcached.la
+tests_memplus_LDADD = $(tests_memplus_DEPENDENCIES)
test: test-docs test-mem test-hash memcapable
echo "Tests completed"
test_return_t world_pre_run(libmemcached_test_container_st *container)
{
- for (uint32_t loop= 0; loop < memcached_server_list_count(container->construct.servers); loop++)
+ for (uint32_t loop= 0; loop < memcached_server_list_count(container->memc->servers); loop++)
{
memcached_server_instance_st instance=
memcached_server_instance_by_position(container->memc, loop);
4269430871U, 610793021U, 527273862U, 1437122909U,
2300930706U, 2943759320U, 674306647U, 2400528935U,
54481931U, 4186304426U, 1741088401U, 2979625118U,
- 4159057246U, 3425930182U, 2593724503U, 1868899624U};
+ 4159057246U, 3425930182U, 2593724503U, 1868899624U,
+ 1769812374U, 2302537950U, 1110330676U };
// You have updated the memcache_error messages but not updated docs/tests.
- test_true(MEMCACHED_MAXIMUM_RETURN == 40);
+ test_true(MEMCACHED_MAXIMUM_RETURN == 43);
for (rc= MEMCACHED_SUCCESS; rc < MEMCACHED_MAXIMUM_RETURN; rc++)
{
uint32_t hash_val;
memcached_server_instance_st instance=
memcached_server_instance_by_position(memc, 0);
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ if (memcached_get_sasl_callbacks(memc) != NULL)
+ return TEST_SKIPPED;
+#endif
rc= memcached_stat_servername(&memc_stat, NULL,
memcached_server_name(instance),
memcached_server_port(instance));
static test_return_t pre_cork_and_nonblock(memcached_st *memc)
{
test_return_t rc;
-
+
rc= pre_cork(memc);
#ifdef __APPLE__
return rc == MEMCACHED_SUCCESS ? TEST_SUCCESS : TEST_SKIPPED;
}
+static test_return_t pre_sasl(memcached_st *memc)
+{
+ memcached_return_t rc= MEMCACHED_FAILURE;
+
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ const char *server= getenv("LIBMEMCACHED_TEST_SASL_SERVER");
+ const char *user= getenv("LIBMEMCACHED_TEST_SASL_USERNAME");
+ const char *pass= getenv("LIBMEMCACHED_TEST_SASL_PASSWORD");
+
+ if (server != NULL && user != NULL && pass != NULL)
+ {
+ memcached_server_st *servers= memcached_servers_parse(server);
+ test_true(servers != NULL);
+ memcached_servers_reset(memc);
+ test_true(memcached_server_push(memc, servers) == MEMCACHED_SUCCESS);
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1);
+ rc= memcached_set_sasl_auth_data(memc, user, pass);
+ test_true(rc == MEMCACHED_SUCCESS);
+ }
+#else
+ (void)memc;
+#endif
+
+ return rc == MEMCACHED_SUCCESS ? TEST_SUCCESS : TEST_SKIPPED;
+}
static test_return_t pre_replication(memcached_st *memc)
{
rc= memcached_set(mmc[0], key, keylen, "0", 1, 0, 0);
test_true(rc == MEMCACHED_SUCCESS);
- for (size_t x= 0; x < 10; ++x)
+ for (size_t x= 0; x < 10; ++x)
{
uint64_t number_value;
rc= memcached_increment(mmc[x], key, keylen, 1, &number_value);
test_true(md5_values[x] == hash_val);
}
-
+
/*
Now check memcached_st.
*/
return TEST_SUCCESS;
}
+/*
+ * Test that the sasl authentication works. We cannot use the default
+ * pool of servers, because that would require that all servers we want
+ * to test supports SASL authentication, and that they use the default
+ * creds.
+ */
+static test_return_t sasl_auth_test(memcached_st *memc)
+{
+ memcached_return_t rc;
+ rc= memcached_set(memc, "foo", 3, "bar", 3, (time_t)0, (uint32_t)0);
+ test_true(rc == MEMCACHED_SUCCESS);
+ test_true((rc= memcached_delete(memc, "foo", 3, 0)) == MEMCACHED_SUCCESS);
+ test_true((rc= memcached_destroy_sasl_auth_data(memc)) == MEMCACHED_SUCCESS);
+ test_true((rc= memcached_destroy_sasl_auth_data(memc)) == MEMCACHED_FAILURE);
+ test_true((rc= memcached_destroy_sasl_auth_data(NULL)) == MEMCACHED_FAILURE);
+ memcached_quit(memc);
+
+ rc= memcached_set_sasl_auth_data(memc,
+ getenv("LIBMEMCACHED_TEST_SASL_USERNAME"),
+ getenv("LIBMEMCACHED_TEST_SASL_SERVER"));
+ test_true(rc == MEMCACHED_SUCCESS);
+ rc= memcached_set(memc, "foo", 3, "bar", 3, (time_t)0, (uint32_t)0);
+ test_true(rc == MEMCACHED_AUTH_FAILURE);
+ test_true(memcached_destroy_sasl_auth_data(memc) == MEMCACHED_SUCCESS);
+
+ memcached_quit(memc);
+ return TEST_SUCCESS;
+}
/* Clean the server before beginning testing */
test_st tests[] ={
{0, 0, (test_callback_fn)0}
};
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+test_st sasl_auth_tests[]= {
+ {"sasl_auth", 1, (test_callback_fn)sasl_auth_test },
+ {0, 0, (test_callback_fn)0}
+};
+#endif
+
test_st ketama_compatibility[]= {
{"libmemcached", 1, (test_callback_fn)ketama_compatibility_libmemcached },
{"spymemcached", 1, (test_callback_fn)ketama_compatibility_spymemcached },
#endif
{"memory_allocators", (test_callback_fn)set_memory_alloc, 0, tests},
{"prefix", (test_callback_fn)set_prefix, 0, tests},
+ {"sasl_auth", (test_callback_fn)pre_sasl, 0, sasl_auth_tests },
+ {"sasl", (test_callback_fn)pre_sasl, 0, tests },
{"version_1_2_3", (test_callback_fn)check_for_1_2_3, 0, version_1_2_3},
{"string", 0, 0, string_tests},
{"result", 0, 0, result_tests},
Startup, and shutdown the memcached servers.
*/
-#define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10
+#define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10
#include "config.h"
int status;
sprintf(buffer, "/tmp/%umemc.pid", x);
- if (access(buffer, F_OK) == 0)
+ if (access(buffer, F_OK) == 0)
{
FILE *fp= fopen(buffer, "r");
remove(buffer);
if (fp != NULL)
{
if (fgets(buffer, sizeof(buffer), fp) != NULL)
- {
+ {
pid_t pid= (pid_t)atoi(buffer);
- if (pid != 0)
+ if (pid != 0)
kill(pid, SIGTERM);
}
{
sprintf(buffer, "%s -d -P /tmp/%umemc.pid -t 1 -p %u -U %u -m 128",
MEMCACHED_BINARY, x, x + TEST_PORT_BASE, x + TEST_PORT_BASE);
- }
+ }
else
{
sprintf(buffer, "%s -d -P /tmp/%umemc.pid -t 1 -p %u -U %u",
sprintf(buffer, "cat /tmp/%umemc.pid | xargs kill", x);
/* We have to check the return value of this or the compiler will yell */
int sys_ret= system(buffer);
- assert(sys_ret != -1);
+ assert(sys_ret != -1);
sprintf(buffer, "/tmp/%umemc.pid", x);
unlink(buffer);
}
#include <fnmatch.h>
#include <stdint.h>
+#include "libmemcached/memcached.h"
#include "test.h"
static void world_stats_print(world_stats_st *stats)
world_stats_st stats;
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ if (sasl_client_init(NULL) != SASL_OK)
+ {
+ fprintf(stderr, "Failed to initialize sasl library!\n");
+ return 1;
+ }
+#endif
+
memset(&stats, 0, sizeof(stats));
memset(&world, 0, sizeof(world));
get_world(&world);
world_stats_print(&stats);
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+ sasl_done();
+#endif
+
return stats.failed == 0 ? 0 : 1;
}