Add support for SASL
authorTrond Norbye <trond.norbye@gmail.com>
Wed, 3 Mar 2010 10:50:13 +0000 (11:50 +0100)
committerTrond Norbye <trond.norbye@gmail.com>
Wed, 3 Mar 2010 10:50:13 +0000 (11:50 +0100)
33 files changed:
clients/client_options.h
clients/include.am
clients/memcat.c
clients/memcp.c
clients/memdump.c
clients/memflush.c
clients/memrm.c
clients/utilities.c
clients/utilities.h
configure.ac
docs/Makefile.am
docs/memcached_sasl.pod [new file with mode: 0644]
libmemcached/auto.c
libmemcached/configure.h.in
libmemcached/connect.c
libmemcached/constants.h
libmemcached/delete.c
libmemcached/include.am
libmemcached/memcached.c
libmemcached/memcached.h
libmemcached/memcached/protocol_binary.h
libmemcached/response.c
libmemcached/sasl.c [new file with mode: 0644]
libmemcached/sasl.h [new file with mode: 0644]
libmemcached/storage.c
libmemcached/strerror.c
m4/pandora_have_sasl.m4 [new file with mode: 0644]
m4/protocol_binary.m4
tests/include.am
tests/libmemcached_world.h
tests/mem_functions.c
tests/server.c
tests/test.c

index 955bcd18ff8bb959e70d1a4b8bd32a0bca2d44b0..9293b9cacafba3b252d41f9de1a2e914b9b4e622 100644 (file)
@@ -35,7 +35,9 @@ typedef enum {
   OPT_FLUSH,
   OPT_HASH,
   OPT_BINARY,
-  OPT_UDP
+  OPT_UDP,
+  OPT_USERNAME,
+  OPT_PASSWD
 } memcached_options;
 
 #endif /* CLIENT_OPTIONS */
index cc734ca7137dbe7fe49b4564672d1521dced8222..7b7cbc125d0b3276c00f75d0eb6b70f5216d7dd2 100644 (file)
@@ -7,6 +7,10 @@ CLIENTS_LDADDS = \
        clients/libutilities.la \
        libmemcached/libmemcached.la
 
+if HAVE_SASL
+CLIENTS_LDADDS+= $(LIBSASL)
+endif
+
 bin_PROGRAMS+= \
        clients/memcapable \
        clients/memcat \
index f5f7f8f911d28b2bd5cf4dfeca5c4690e34af021..3ae82e460188ef0ef76a20369db04af2aa408f83 100644 (file)
@@ -31,6 +31,8 @@ static int opt_verbose= 0;
 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[])
 {
@@ -68,11 +70,17 @@ 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)
       {
@@ -80,7 +88,7 @@ int main(int argc, char *argv[])
           printf("key: %s\nflags: ", argv[optind]);
         printf("%x\n", flags);
       }
-      else 
+      else
       {
         if (opt_verbose)
           printf("key: %s\nflags: %x\nlength: %zu\nvalue: ",
@@ -91,7 +99,7 @@ int main(int argc, char *argv[])
     }
     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));
@@ -115,6 +123,8 @@ int main(int argc, char *argv[])
   if (opt_hash)
     free(opt_hash);
 
+  shutdown_sasl();
+
   return return_code;
 }
 
@@ -139,10 +149,12 @@ void options_parse(int argc, char *argv[])
       {(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;
@@ -171,6 +183,12 @@ void options_parse(int argc, char *argv[])
     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);
index dd8aba9ac907e9001d6e823374e7cf498520af8f..1dc30631900eed71032fa4424c6201eceed8b9b0 100644 (file)
@@ -41,6 +41,8 @@ static char *opt_hash= NULL;
 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)
 {
@@ -106,6 +108,11 @@ int main(int argc, char *argv[])
   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)
   {
@@ -193,6 +200,7 @@ int main(int argc, char *argv[])
     free(opt_servers);
   if (opt_hash)
     free(opt_hash);
+  shutdown_sasl();
 
   return return_code;
 }
@@ -221,6 +229,8 @@ static void options_parse(int argc, char *argv[])
       {(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},
     };
 
@@ -285,7 +295,13 @@ static void options_parse(int argc, char *argv[])
     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:
index e185453890e3abf21b934673d467f91a40305e6d..40ab430a4ceca1a0b339569068c02b1b2173217e 100644 (file)
@@ -38,6 +38,8 @@ static int opt_binary=0;
 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)),
@@ -85,6 +87,11 @@ int main(int argc, char *argv[])
   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);
 
@@ -103,6 +110,8 @@ int main(int argc, char *argv[])
   if (opt_hash)
     free(opt_hash);
 
+  shutdown_sasl();
+
   return 0;
 }
 
@@ -120,6 +129,8 @@ static void options_parse(int argc, char *argv[])
       {(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}
     };
 
@@ -154,6 +165,12 @@ static void options_parse(int argc, char *argv[])
     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);
index 885aa6c85089708dcabd6058c58ee20f90e1c9da..4cce8cb0b0fb49d150a61f1018fe238f5395de8a 100644 (file)
@@ -22,6 +22,8 @@ static int opt_binary= 0;
 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."
@@ -57,11 +59,17 @@ int main(int argc, char *argv[])
   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));
@@ -72,6 +80,8 @@ int main(int argc, char *argv[])
 
   free(opt_servers);
 
+  shutdown_sasl();
+
   return 0;
 }
 
@@ -92,12 +102,14 @@ void options_parse(int argc, char *argv[])
     {(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;
@@ -126,6 +138,12 @@ void options_parse(int argc, char *argv[])
     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);
index 81c5f852bd21a992e718b86acb44e7c6203694c6..dfb2c2fbe00a074b01086ee65ca41f91e03c84df 100644 (file)
@@ -23,6 +23,8 @@ static int opt_verbose= 0;
 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."
@@ -61,7 +63,13 @@ int main(int argc, char *argv[])
   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)
@@ -89,6 +97,8 @@ int main(int argc, char *argv[])
   if (opt_hash)
     free(opt_hash);
 
+  shutdown_sasl();
+
   return return_code;
 }
 
@@ -110,6 +120,8 @@ static void options_parse(int argc, char *argv[])
     {(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;
@@ -147,6 +159,12 @@ static void options_parse(int argc, char *argv[])
     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);
index 5d761e3c94729c03dca3931b005ec381f359abb0..ef1e60f37c131b157feccd1ac61f4fd5fa94d0ac 100644 (file)
@@ -58,6 +58,8 @@ static const char *lookup_help(memcached_options option)
   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);
   };
 
@@ -75,12 +77,12 @@ void help_command(const char *command_name, const char *description,
   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);
   }
@@ -122,3 +124,93 @@ void process_hash_option(memcached_st *memc, char *opt_hash)
   }
 }
 
+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
+}
index f55997a5852eb754605693100ae2482aa4cc422c..162366ab82d48403cbb11dc1b92c9254b56d0719 100644 (file)
@@ -35,7 +35,7 @@
 
 typedef struct memcached_programs_help_st memcached_programs_help_st;
 
-struct memcached_programs_help_st 
+struct memcached_programs_help_st
 {
   char *not_used_yet;
 };
@@ -48,3 +48,5 @@ void help_command(const char *command_name, const char *description,
                   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);
index 6f4b0ba882cdfe093a80fbd512b1a0a593948db7..be8e625f0884f608d02739d47f026ba96933158b 100644 (file)
@@ -55,6 +55,12 @@ ENABLE_DEPRECATED
 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"],[
index b1d3e6ee37b787769b8e698443398972d1faf7e7..348e9ca63096189870242e5acc53b7c722a1f682 100644 (file)
@@ -89,7 +89,7 @@ POOL_PAGES= \
            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= \
@@ -111,14 +111,14 @@ SERVER_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= \
@@ -416,6 +416,34 @@ man_MANS = \
           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 \
@@ -428,11 +456,11 @@ man_MANS+= \
 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/$@
 
@@ -488,7 +516,6 @@ ${USER_DATA_PAGES}:
        @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/$@
@@ -511,7 +538,7 @@ test-docs: $(POD_FILES)
 
 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
diff --git a/docs/memcached_sasl.pod b/docs/memcached_sasl.pod
new file mode 100644 (file)
index 0000000..0d182c9
--- /dev/null
@@ -0,0 +1,64 @@
+=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
index c36cd92f4c969ccf0508f89b19d09a75093a1596..fde81d47e3da67ceb19265a2d42cb1f203fee8e0 100644 (file)
@@ -111,12 +111,13 @@ static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd,
   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)
index 064f3b73c2c96dbf4d96d70c13dd0cf693fd1c6b..c030d3054c6ed7ed21f6dc0ad016c79129cd6d3b 100644 (file)
@@ -18,6 +18,7 @@ extern "C" {
 
 @MEMCACHED_BITFIELD@
 @DEPRECATED@
+@LIBMEMCACHED_WITH_SASL_SUPPORT@
 
 #define LIBMEMCACHED_VERSION_STRING "@VERSION@"
 #define LIBMEMCACHED_VERSION_HEX @PANDORA_HEX_VERSION@
index 4b8939c11d4942802ea511134df2ad962acb3672..f3832faed079d8b771ee18e61b1237b7a8283c5e 100644 (file)
@@ -282,6 +282,20 @@ static memcached_return_t network_connect(memcached_server_st *ptr)
         }
       }
 
+#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;
@@ -368,7 +382,7 @@ memcached_return_t memcached_connect(memcached_server_write_instance_st ptr)
     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;
index f05b03ebb0316fe0dce49002ce284fc87bb409a8..ef8229af04a8765007fa0ba42b3b83a88b375517 100644 (file)
@@ -69,6 +69,9 @@ typedef enum {
   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;
 
index 7fd1f2aa307c1837521fbb6b8fa41ad0b54fca42..da91f7036edabf4c67e175db19861318108a62fd 100644 (file)
@@ -173,13 +173,13 @@ static inline memcached_return_t binary_delete(memcached_st *ptr,
 
   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)
index 737c5e12e5c312d4c7b17c84e0fbf5dc96c09d84..dc125bd70680aa35f06b3321e292f1d24bcee5d9 100644 (file)
@@ -44,6 +44,7 @@ nobase_include_HEADERS+= \
                         libmemcached/protocol_handler.h \
                         libmemcached/quit.h \
                         libmemcached/result.h \
+                         libmemcached/sasl.h \
                         libmemcached/server.h \
                         libmemcached/server_list.h \
                         libmemcached/stats.h \
@@ -64,7 +65,7 @@ libmemcached_libmemcachedprotocol_la_SOURCES =  \
                                                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
@@ -138,6 +139,11 @@ libmemcached_libmemcachedprotocol_la_LIBADD=libmemcached/libbyteorder.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
index 4e0308a043df72b29bdc43800a475d04942c73d7..2d28830fa9fe3a24e7cfbbdd70cc31fed77ba814 100644 (file)
@@ -5,7 +5,7 @@
  * Use and distribution licensed under the BSD license.  See
  * the COPYING file in the parent directory for full text.
  *
- * Summary: 
+ * Summary:
  *
  */
 
@@ -88,6 +88,10 @@ static inline bool _memcached_init(memcached_st *self)
   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;
 }
@@ -155,6 +159,13 @@ void memcached_free(memcached_st *ptr)
   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);
@@ -243,6 +254,17 @@ memcached_st *memcached_clone(memcached_st *clone, const memcached_st *source)
     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)
index 30823a8733d3d96cc12cfc666c74d2c61af14ef4..5aca6db122074ed962cd7b53362f8c7d7489bd7a 100644 (file)
@@ -53,6 +53,7 @@
 #include <libmemcached/strerror.h>
 #include <libmemcached/verbosity.h>
 #include <libmemcached/version.h>
+#include <libmemcached/sasl.h>
 
 struct memcached_st {
   /**
@@ -121,6 +122,17 @@ 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;
index e5226b5bf467f0821e2ad874b6acfe49b588dcf1..ecdc52e333b1ce19eeb7d2d0f85c23527e8b492c 100644 (file)
  *
  * 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
@@ -69,11 +72,14 @@ extern "C"
         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;
 
     /**
@@ -109,6 +115,10 @@ extern "C"
         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.
@@ -415,6 +425,10 @@ extern "C"
     typedef protocol_binary_request_rangeop protocol_binary_request_rdecr;
     typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq;
 
+    /**
+     * @}
+     */
+
 #ifdef __cplusplus
 }
 #endif
index c14dd1ff6688c6678922c5d4356c77e02bbe6e17..0b8ebf6f4a2aa9db0069522457566c5f0e2996c1 100644 (file)
@@ -45,7 +45,7 @@ memcached_return_t memcached_read_one_response(memcached_server_write_instance_s
   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)
 {
@@ -58,7 +58,7 @@ memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
   /*
    * 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)
@@ -68,10 +68,10 @@ memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
       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;
     }
@@ -176,8 +176,8 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
   }
 
   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
@@ -246,21 +246,21 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
         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)
@@ -333,11 +333,11 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
 {
   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;
 
   /*
@@ -349,7 +349,8 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
   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)
     {
@@ -357,8 +358,8 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
       /*
        * 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:
       {
@@ -374,36 +375,37 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
         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);
@@ -412,7 +414,7 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
           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:
@@ -425,7 +427,7 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
       {
         WATCHPOINT_ASSERT(bodylen == 0);
         return MEMCACHED_SUCCESS;
-      } 
+      }
     case PROTOCOL_BINARY_CMD_NOOP:
       {
         WATCHPOINT_ASSERT(bodylen == 0);
@@ -438,30 +440,48 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
         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)
@@ -487,8 +507,8 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
   }
 
   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;
@@ -505,6 +525,12 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
     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:
diff --git a/libmemcached/sasl.c b/libmemcached/sasl.c
new file mode 100644 (file)
index 0000000..9b4533b
--- /dev/null
@@ -0,0 +1,363 @@
+/* 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;
+}
diff --git a/libmemcached/sasl.h b/libmemcached/sasl.h
new file mode 100644 (file)
index 0000000..6867f07
--- /dev/null
@@ -0,0 +1,50 @@
+/* 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 */
index 6225af3732b15934db4d94df5e8b02be3dd95f63..abc5b7ca21af675f352984bc89ec8b93047745e0 100644 (file)
@@ -488,12 +488,14 @@ static memcached_return_t memcached_send_binary(memcached_st *ptr,
   }
 
   /* 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)
index 6698ff18f88ce4cf3c52c475823ffd2a16b14e82..4ed2a24c8a3b0414bc31b2482c62bf30d35503df 100644 (file)
@@ -84,6 +84,12 @@ const char *memcached_strerror(memcached_st *ptr __attribute__((unused)), memcac
      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:
diff --git a/m4/pandora_have_sasl.m4 b/m4/pandora_have_sasl.m4
new file mode 100644 (file)
index 0000000..75513d1
--- /dev/null
@@ -0,0 +1,133 @@
+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}]))
+])
index d31bcb04a2a06c1ca2ab9af82e3b76f090ac9b39..ba7acaf5f52d01ceaf2a113ba54dbd2860ba3956 100644 (file)
@@ -5,15 +5,16 @@ AC_DEFUN([PROTOCOL_BINARY_TEST],
   [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
 ])
index b34f1f165ae8c9e9c65bc505716b9d6782162bad..2577e2f46f92f65ee24cdccc9a3d6998f0db80cd 100644 (file)
@@ -52,50 +52,50 @@ tests_libtest_la_SOURCES= tests/test.c
 
 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"
index b608d9ae953294824ef558d06c13d48b5e15f3bc..5a5e15b5c4fb5ff622e08e96d08357025be29fab 100644 (file)
@@ -96,7 +96,7 @@ test_return_t world_flush(libmemcached_test_container_st *container)
 
 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);
index 84ac0dc14abbd5677b22afde097b8871a13ce4d4..263119d6194887ead789a9b7db5c2b102629f20f 100644 (file)
@@ -376,10 +376,11 @@ static test_return_t error_test(memcached_st *memc)
                         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;
@@ -1215,6 +1216,10 @@ static test_return_t stats_servername_test(memcached_st *memc)
   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));
@@ -3467,7 +3472,7 @@ static test_return_t pre_cork(memcached_st *memc)
 static test_return_t pre_cork_and_nonblock(memcached_st *memc)
 {
   test_return_t rc;
-  
+
   rc= pre_cork(memc);
 
 #ifdef __APPLE__
@@ -3642,6 +3647,31 @@ static test_return_t pre_binary(memcached_st *memc)
   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)
 {
@@ -4252,7 +4282,7 @@ static test_return_t connection_pool_test(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);
@@ -4788,7 +4818,7 @@ static test_return_t memcached_get_hashkit_test (memcached_st *memc)
     test_true(md5_values[x] == hash_val);
   }
 
-  
+
   /*
     Now check memcached_st.
   */
@@ -5499,8 +5529,36 @@ static test_return_t regression_bug_490486(memcached_st *memc)
   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[] ={
@@ -5661,6 +5719,13 @@ test_st regression_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 },
@@ -5766,6 +5831,8 @@ collection_st collection[] ={
 #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},
index 383469bd5b846307c8812396bfdb995a57331fcb..c96f5972dbda3a5c2b2e32df8ea43bed999e0fd9 100644 (file)
@@ -13,7 +13,7 @@
   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"
 
@@ -52,7 +52,7 @@ void server_startup(server_startup_st *construct)
         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);
@@ -60,9 +60,9 @@ void server_startup(server_startup_st *construct)
           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);
             }
 
@@ -74,7 +74,7 @@ void server_startup(server_startup_st *construct)
         {
           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",
@@ -119,7 +119,7 @@ void server_shutdown(server_startup_st *construct)
       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);
     }
index b177f64fdffdbb8a7d84f401ab5dcbd30691f10f..dd64be6933ef287c7bd1c03024791dd7eb903198 100644 (file)
@@ -21,6 +21,7 @@
 #include <fnmatch.h>
 #include <stdint.h>
 
+#include "libmemcached/memcached.h"
 #include "test.h"
 
 static void world_stats_print(world_stats_st *stats)
@@ -147,6 +148,14 @@ int main(int argc, char *argv[])
 
   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);
@@ -350,5 +359,9 @@ cleanup:
 
   world_stats_print(&stats);
 
+#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
+  sasl_done();
+#endif
+
   return stats.failed == 0 ? 0 : 1;
 }