14c86fa59STrond Norbye/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
258df6f95STrond Norbye#include "config.h"
37bb82191STrond Norbye#include <sys/types.h>
44c86fa59STrond Norbye#include <stdio.h>
54c86fa59STrond Norbye#include <stdlib.h>
64c86fa59STrond Norbye#include <errno.h>
74c86fa59STrond Norbye#include <string.h>
80731dc82STrond Norbye#include <fcntl.h>
92bd6278aSTrond Norbye#include <ctype.h>
10f603fdb6STrond Norbye#include <time.h>
11f603fdb6STrond Norbye#include <evutil.h>
1293d8ef54STrond Norbye#include <snappy-c.h>
13595bef70STrond Norbye#include <cJSON.h>
14595bef70STrond Norbye
154c86fa59STrond Norbye
16f603fdb6STrond Norbye#include "daemon/cache.h"
175011fc53SSean Lynch#include <memcached/util.h>
185011fc53SSean Lynch#include <memcached/protocol_binary.h>
195011fc53SSean Lynch#include <memcached/config_parser.h>
207e2c95cbSTrond Norbye#include "extensions/protocol/fragment_rw.h"
21e73832dcSjim#include "extensions/protocol/testapp_extension.h"
22f4b802c8Sjim#include "platform/platform.h"
23f4b802c8Sjim#include "memcached/openssl.h"
24f4b802c8Sjim#include "programs/utilities.h"
257e2c95cbSTrond Norbye
267e2c95cbSTrond Norbye/* Set the read/write commands differently than the default values
277e2c95cbSTrond Norbye * so that we can verify that the override works
287e2c95cbSTrond Norbye */
297e2c95cbSTrond Norbyestatic uint8_t read_command = 0xe1;
307e2c95cbSTrond Norbyestatic uint8_t write_command = 0xe2;
314c86fa59STrond Norbye
32595bef70STrond Norbyeconst char config_file[] = "memcached_testapp.json";
33595bef70STrond Norbye
34891082f3SDustin Sallings#define TMP_TEMPLATE "/tmp/test_file.XXXXXXX"
35891082f3SDustin Sallings
364c86fa59STrond Norbyeenum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL };
374c86fa59STrond Norbye
38f4b802c8Sjim/* test phases */
39f4b802c8Sjim#define phase_plain 1
40f4b802c8Sjim#define phase_ssl 2
41f4b802c8Sjim#define phase_cleanup 4
42f4b802c8Sjim#define phase_max 3
43f4b802c8Sjimstatic int current_phase = 0;
456ae66c84STrond Norbyestatic pid_t server_pid;
46f4b802c8Sjimstatic in_port_t port = -1;
47f4b802c8Sjimstatic in_port_t ssl_port = -1;
48f603fdb6STrond Norbyestatic SOCKET sock;
496410c8f7Sjimstatic SOCKET sock_ssl;
506ae66c84STrond Norbyestatic bool allow_closed_read = false;
51e73832dcSjimstatic time_t server_start_time = 0;
52f4b802c8Sjimstatic SSL_CTX *ssl_ctx = NULL;
536410c8f7Sjimstatic SSL *ssl = NULL;
546410c8f7Sjimstatic BIO *ssl_bio_r = NULL;
556410c8f7Sjimstatic BIO *ssl_bio_w = NULL;
566ae66c84STrond Norbye
574c86fa59STrond Norbyestatic enum test_return cache_create_test(void)
584c86fa59STrond Norbye{
594c86fa59STrond Norbye    cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
604c86fa59STrond Norbye                                  NULL, NULL);
61464d6f0bSTrond Norbye    cb_assert(cache != NULL);
624c86fa59STrond Norbye    cache_destroy(cache);
634c86fa59STrond Norbye    return TEST_PASS;
644c86fa59STrond Norbye}
654c86fa59STrond Norbye
664c86fa59STrond Norbyeconst uint64_t constructor_pattern = 0xdeadcafebabebeef;
674c86fa59STrond Norbye
684c86fa59STrond Norbyestatic int cache_constructor(void *buffer, void *notused1, int notused2) {
694c86fa59STrond Norbye    uint64_t *ptr = buffer;
704c86fa59STrond Norbye    *ptr = constructor_pattern;
714c86fa59STrond Norbye    return 0;
724c86fa59STrond Norbye}
734c86fa59STrond Norbye
744c86fa59STrond Norbyestatic enum test_return cache_constructor_test(void)
754c86fa59STrond Norbye{
76f603fdb6STrond Norbye    uint64_t *ptr;
77f603fdb6STrond Norbye    uint64_t pattern;
784c86fa59STrond Norbye    cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
794c86fa59STrond Norbye                                  cache_constructor, NULL);
80f603fdb6STrond Norbye
81f603fdb6STrond Norbye
82464d6f0bSTrond Norbye    cb_assert(cache != NULL);
83f603fdb6STrond Norbye    ptr = cache_alloc(cache);
84f603fdb6STrond Norbye    pattern = *ptr;
854c86fa59STrond Norbye    cache_free(cache, ptr);
864c86fa59STrond Norbye    cache_destroy(cache);
874c86fa59STrond Norbye    return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL;
884c86fa59STrond Norbye}
894c86fa59STrond Norbye
904c86fa59STrond Norbyestatic int cache_fail_constructor(void *buffer, void *notused1, int notused2) {
914c86fa59STrond Norbye    return 1;
924c86fa59STrond Norbye}
934c86fa59STrond Norbye
944c86fa59STrond Norbyestatic enum test_return cache_fail_constructor_test(void)
954c86fa59STrond Norbye{
964c86fa59STrond Norbye    enum test_return ret = TEST_PASS;
97f603fdb6STrond Norbye    uint64_t *ptr;
984c86fa59STrond Norbye    cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
994c86fa59STrond Norbye                                  cache_fail_constructor, NULL);
100464d6f0bSTrond Norbye    cb_assert(cache != NULL);
101f603fdb6STrond Norbye    ptr = cache_alloc(cache);
1024c86fa59STrond Norbye    if (ptr != NULL) {
1034c86fa59STrond Norbye        ret = TEST_FAIL;
1044c86fa59STrond Norbye    }
1054c86fa59STrond Norbye    cache_destroy(cache);
1064c86fa59STrond Norbye    return ret;
1074c86fa59STrond Norbye}
1084c86fa59STrond Norbye
1094c86fa59STrond Norbyestatic void *destruct_data = 0;
1104c86fa59STrond Norbye
1114c86fa59STrond Norbyestatic void cache_destructor(void *buffer, void *notused) {
1124c86fa59STrond Norbye    destruct_data = buffer;
1134c86fa59STrond Norbye}
1144c86fa59STrond Norbye
1154c86fa59STrond Norbyestatic enum test_return cache_destructor_test(void)
1164c86fa59STrond Norbye{
117f603fdb6STrond Norbye    char *ptr;
1184c86fa59STrond Norbye    cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
1194c86fa59STrond Norbye                                  NULL, cache_destructor);
120464d6f0bSTrond Norbye    cb_assert(cache != NULL);
121f603fdb6STrond Norbye    ptr = cache_alloc(cache);
1224c86fa59STrond Norbye    cache_free(cache, ptr);
1234c86fa59STrond Norbye    cache_destroy(cache);
1244c86fa59STrond Norbye
1254c86fa59STrond Norbye    return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL;
1264c86fa59STrond Norbye}
1274c86fa59STrond Norbye
1284c86fa59STrond Norbyestatic enum test_return cache_reuse_test(void)
1294c86fa59STrond Norbye{
1304c86fa59STrond Norbye    int ii;
1314c86fa59STrond Norbye    cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
1324c86fa59STrond Norbye                                  NULL, NULL);
1334c86fa59STrond Norbye    char *ptr = cache_alloc(cache);
1344c86fa59STrond Norbye    cache_free(cache, ptr);
1354c86fa59STrond Norbye    for (ii = 0; ii < 100; ++ii) {
1364c86fa59STrond Norbye        char *p = cache_alloc(cache);
137464d6f0bSTrond Norbye        cb_assert(p == ptr);
1384c86fa59STrond Norbye        cache_free(cache, ptr);
1394c86fa59STrond Norbye    }
1404c86fa59STrond Norbye    cache_destroy(cache);
1414c86fa59STrond Norbye    return TEST_PASS;
1424c86fa59STrond Norbye}
1434c86fa59STrond Norbye
1446e74e748STrond Norbye
1456e74e748STrond Norbyestatic enum test_return cache_bulkalloc(size_t datasize)
1466e74e748STrond Norbye{
1476e74e748STrond Norbye    cache_t *cache = cache_create("test", datasize, sizeof(char*),
1486e74e748STrond Norbye                                  NULL, NULL);
1496e74e748STrond Norbye#define ITERATIONS 1024
1506e74e748STrond Norbye    void *ptr[ITERATIONS];
151f603fdb6STrond Norbye    int ii;
152f603fdb6STrond Norbye    for (ii = 0; ii < ITERATIONS; ++ii) {
1536e74e748STrond Norbye        ptr[ii] = cache_alloc(cache);
154464d6f0bSTrond Norbye        cb_assert(ptr[ii] != 0);
1556e74e748STrond Norbye        memset(ptr[ii], 0xff, datasize);
1566e74e748STrond Norbye    }
1576e74e748STrond Norbye
158f603fdb6STrond Norbye    for (ii = 0; ii < ITERATIONS; ++ii) {
1596e74e748STrond Norbye        cache_free(cache, ptr[ii]);
1606e74e748STrond Norbye    }
1616e74e748STrond Norbye
1626e74e748STrond Norbye#undef ITERATIONS
1636e74e748STrond Norbye    cache_destroy(cache);
1646e74e748STrond Norbye    return TEST_PASS;
1656e74e748STrond Norbye}
1666e74e748STrond Norbye
1676e74e748STrond Norbyestatic enum test_return test_issue_161(void)
1686e74e748STrond Norbye{
1696e74e748STrond Norbye    enum test_return ret = cache_bulkalloc(1);
1706e74e748STrond Norbye    if (ret == TEST_PASS) {
1716e74e748STrond Norbye        ret = cache_bulkalloc(512);
1726e74e748STrond Norbye    }
1736e74e748STrond Norbye
1746e74e748STrond Norbye    return ret;
1756e74e748STrond Norbye}
1766e74e748STrond Norbye
1774c86fa59STrond Norbyestatic enum test_return cache_redzone_test(void)
1784c86fa59STrond Norbye{
179f603fdb6STrond Norbye#if !defined(HAVE_UMEM_H) && !defined(NDEBUG) && !defined(WIN32)
1804c86fa59STrond Norbye    cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
1814c86fa59STrond Norbye                                  NULL, NULL);
1824c86fa59STrond Norbye
1834c86fa59STrond Norbye    /* Ignore SIGABORT */
1844c86fa59STrond Norbye    struct sigaction old_action;
185f603fdb6STrond Norbye    struct sigaction action;
186f603fdb6STrond Norbye    char *p;
187f603fdb6STrond Norbye    char old;
188f603fdb6STrond Norbye
189f603fdb6STrond Norbye    memset(&action, 0, sizeof(action));
190f603fdb6STrond Norbye    action.sa_handler = SIG_IGN;
1914c86fa59STrond Norbye    sigemptyset(&action.sa_mask);
1924c86fa59STrond Norbye    sigaction(SIGABRT, &action, &old_action);
1934c86fa59STrond Norbye
1944c86fa59STrond Norbye    /* check memory debug.. */
195f603fdb6STrond Norbye    p = cache_alloc(cache);
196f603fdb6STrond Norbye    old = *(p - 1);
1974c86fa59STrond Norbye    *(p - 1) = 0;
1984c86fa59STrond Norbye    cache_free(cache, p);
199464d6f0bSTrond Norbye    cb_assert(cache_error == -1);
2004c86fa59STrond Norbye    *(p - 1) = old;
2014c86fa59STrond Norbye
2024c86fa59STrond Norbye    p[sizeof(uint32_t)] = 0;
2034c86fa59STrond Norbye    cache_free(cache, p);
204464d6f0bSTrond Norbye    cb_assert(cache_error == 1);
2054c86fa59STrond Norbye
2064c86fa59STrond Norbye    /* restore signal handler */
2074c86fa59STrond Norbye    sigaction(SIGABRT, &old_action, NULL);
2084c86fa59STrond Norbye
2094c86fa59STrond Norbye    cache_destroy(cache);
2104c86fa59STrond Norbye
2114c86fa59STrond Norbye    return TEST_PASS;
2124c86fa59STrond Norbye#else
2134c86fa59STrond Norbye    return TEST_SKIP;
2144c86fa59STrond Norbye#endif
2154c86fa59STrond Norbye}
2164c86fa59STrond Norbye
2174c86fa59STrond Norbyestatic enum test_return test_safe_strtoul(void) {
2184c86fa59STrond Norbye    uint32_t val;
219464d6f0bSTrond Norbye    cb_assert(safe_strtoul("123", &val));
220464d6f0bSTrond Norbye    cb_assert(val == 123);
221464d6f0bSTrond Norbye    cb_assert(safe_strtoul("+123", &val));
222464d6f0bSTrond Norbye    cb_assert(val == 123);
223464d6f0bSTrond Norbye    cb_assert(!safe_strtoul("", &val));  /* empty */
224464d6f0bSTrond Norbye    cb_assert(!safe_strtoul("123BOGUS", &val));  /* non-numeric */
2254c86fa59STrond Norbye    /* Not sure what it does, but this works with ICC :/
226464d6f0bSTrond Norbye       cb_assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
2274c86fa59STrond Norbye    */
2284c86fa59STrond Norbye
229f603fdb6STrond Norbye    /* extremes: */
230464d6f0bSTrond Norbye    cb_assert(safe_strtoul("4294967295", &val)); /* 2**32 - 1 */
231464d6f0bSTrond Norbye    cb_assert(val == 4294967295L);
2324c86fa59STrond Norbye    /* This actually works on 64-bit ubuntu
233464d6f0bSTrond Norbye       cb_assert(!safe_strtoul("4294967296", &val)); 2**32
2344c86fa59STrond Norbye    */
235464d6f0bSTrond Norbye    cb_assert(!safe_strtoul("-1", &val));  /* negative */
2364c86fa59STrond Norbye    return TEST_PASS;
2374c86fa59STrond Norbye}
2384c86fa59STrond Norbye
2394c86fa59STrond Norbye
2404c86fa59STrond Norbyestatic enum test_return test_safe_strtoull(void) {
2414c86fa59STrond Norbye    uint64_t val;
242f603fdb6STrond Norbye    uint64_t exp = -1;
243464d6f0bSTrond Norbye    cb_assert(safe_strtoull("123", &val));
244464d6f0bSTrond Norbye    cb_assert(val == 123);
245464d6f0bSTrond Norbye    cb_assert(safe_strtoull("+123", &val));
246464d6f0bSTrond Norbye    cb_assert(val == 123);
247464d6f0bSTrond Norbye    cb_assert(!safe_strtoull("", &val));  /* empty */
248464d6f0bSTrond Norbye    cb_assert(!safe_strtoull("123BOGUS", &val));  /* non-numeric */
249464d6f0bSTrond Norbye    cb_assert(!safe_strtoull("92837498237498237498029383", &val)); /* out of range */
250f603fdb6STrond Norbye
251f603fdb6STrond Norbye    /* extremes: */
252464d6f0bSTrond Norbye    cb_assert(safe_strtoull("18446744073709551615", &val)); /* 2**64 - 1 */
253464d6f0bSTrond Norbye    cb_assert(val == exp);
254464d6f0bSTrond Norbye    cb_assert(!safe_strtoull("18446744073709551616", &val)); /* 2**64 */
255464d6f0bSTrond Norbye    cb_assert(!safe_strtoull("-1", &val));  /* negative */
2564c86fa59STrond Norbye    return TEST_PASS;
2574c86fa59STrond Norbye}
2584c86fa59STrond Norbye
2594c86fa59STrond Norbyestatic enum test_return test_safe_strtoll(void) {
2604c86fa59STrond Norbye    int64_t val;
261f603fdb6STrond Norbye    int64_t exp = 1;
262f603fdb6STrond Norbye    exp <<= 63;
263f603fdb6STrond Norbye    exp -= 1;
264464d6f0bSTrond Norbye    cb_assert(safe_strtoll("123", &val));
265464d6f0bSTrond Norbye    cb_assert(val == 123);
266464d6f0bSTrond Norbye    cb_assert(safe_strtoll("+123", &val));
267464d6f0bSTrond Norbye    cb_assert(val == 123);
268464d6f0bSTrond Norbye    cb_assert(safe_strtoll("-123", &val));
269464d6f0bSTrond Norbye    cb_assert(val == -123);
270464d6f0bSTrond Norbye    cb_assert(!safe_strtoll("", &val));  /* empty */
271464d6f0bSTrond Norbye    cb_assert(!safe_strtoll("123BOGUS", &val));  /* non-numeric */
272464d6f0bSTrond Norbye    cb_assert(!safe_strtoll("92837498237498237498029383", &val)); /* out of range */
273f603fdb6STrond Norbye
274f603fdb6STrond Norbye    /* extremes: */
275464d6f0bSTrond Norbye    cb_assert(!safe_strtoll("18446744073709551615", &val)); /* 2**64 - 1 */
276464d6f0bSTrond Norbye    cb_assert(safe_strtoll("9223372036854775807", &val)); /* 2**63 - 1 */
277f603fdb6STrond Norbye
278464d6f0bSTrond Norbye    cb_assert(val == exp); /* 9223372036854775807LL); */
2794c86fa59STrond Norbye    /*
280464d6f0bSTrond Norbye      cb_assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
281464d6f0bSTrond Norbye      cb_assert(val == -9223372036854775808LL);
2824c86fa59STrond Norbye    */
283464d6f0bSTrond Norbye    cb_assert(!safe_strtoll("-9223372036854775809", &val)); /* -2**63 - 1 */
2844c86fa59STrond Norbye
285f603fdb6STrond Norbye    /* We'll allow space to terminate the string.  And leading space. */
286464d6f0bSTrond Norbye    cb_assert(safe_strtoll(" 123 foo", &val));
287464d6f0bSTrond Norbye    cb_assert(val == 123);
2884c86fa59STrond Norbye    return TEST_PASS;
2894c86fa59STrond Norbye}
2904c86fa59STrond Norbye
2914c86fa59STrond Norbyestatic enum test_return test_safe_strtol(void) {
2924c86fa59STrond Norbye    int32_t val;
293464d6f0bSTrond Norbye    cb_assert(safe_strtol("123", &val));
294464d6f0bSTrond Norbye    cb_assert(val == 123);
295464d6f0bSTrond Norbye    cb_assert(safe_strtol("+123", &val));
296464d6f0bSTrond Norbye    cb_assert(val == 123);
297464d6f0bSTrond Norbye    cb_assert(safe_strtol("-123", &val));
298464d6f0bSTrond Norbye    cb_assert(val == -123);
299464d6f0bSTrond Norbye    cb_assert(!safe_strtol("", &val));  /* empty */
300464d6f0bSTrond Norbye    cb_assert(!safe_strtol("123BOGUS", &val));  /* non-numeric */
301464d6f0bSTrond Norbye    cb_assert(!safe_strtol("92837498237498237498029383", &val)); /* out of range */
3024c86fa59STrond Norbye
303f603fdb6STrond Norbye    /* extremes: */
3044c86fa59STrond Norbye    /* This actually works on 64-bit ubuntu
305464d6f0bSTrond Norbye       cb_assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
3064c86fa59STrond Norbye    */
307464d6f0bSTrond Norbye    cb_assert(safe_strtol("2147483647", &val)); /* (- (expt 2.0 31) 1) */
308464d6f0bSTrond Norbye    cb_assert(val == 2147483647L);
3094c86fa59STrond Norbye    /* This actually works on 64-bit ubuntu
310464d6f0bSTrond Norbye       cb_assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
3114c86fa59STrond Norbye    */
3124c86fa59STrond Norbye
313f603fdb6STrond Norbye    /* We'll allow space to terminate the string.  And leading space. */
314464d6f0bSTrond Norbye    cb_assert(safe_strtol(" 123 foo", &val));
315464d6f0bSTrond Norbye    cb_assert(val == 123);
3164c86fa59STrond Norbye    return TEST_PASS;
3174c86fa59STrond Norbye}
3184c86fa59STrond Norbye
3192bd6278aSTrond Norbyestatic enum test_return test_safe_strtof(void) {
3202bd6278aSTrond Norbye    float val;
321464d6f0bSTrond Norbye    cb_assert(safe_strtof("123", &val));
322464d6f0bSTrond Norbye    cb_assert(val == 123.00f);
323464d6f0bSTrond Norbye    cb_assert(safe_strtof("+123", &val));
324464d6f0bSTrond Norbye    cb_assert(val == 123.00f);
325464d6f0bSTrond Norbye    cb_assert(safe_strtof("-123", &val));
326464d6f0bSTrond Norbye    cb_assert(val == -123.00f);
327464d6f0bSTrond Norbye    cb_assert(!safe_strtof("", &val));  /* empty */
328464d6f0bSTrond Norbye    cb_assert(!safe_strtof("123BOGUS", &val));  /* non-numeric */
3292bd6278aSTrond Norbye
330f603fdb6STrond Norbye    /* We'll allow space to terminate the string.  And leading space. */
331464d6f0bSTrond Norbye    cb_assert(safe_strtof(" 123 foo", &val));
332464d6f0bSTrond Norbye    cb_assert(val == 123.00f);
3332bd6278aSTrond Norbye
334464d6f0bSTrond Norbye    cb_assert(safe_strtof("123.23", &val));
335464d6f0bSTrond Norbye    cb_assert(val == 123.23f);
3362bd6278aSTrond Norbye
337464d6f0bSTrond Norbye    cb_assert(safe_strtof("123.00", &val));
338464d6f0bSTrond Norbye    cb_assert(val == 123.00f);
3392bd6278aSTrond Norbye
3402bd6278aSTrond Norbye    return TEST_PASS;
3412bd6278aSTrond Norbye}
3422bd6278aSTrond Norbye
343f603fdb6STrond Norbye#ifdef WIN32
344f603fdb6STrond Norbyestatic void log_network_error(const char* prefix) {
345f603fdb6STrond Norbye    LPVOID error_msg;
346f603fdb6STrond Norbye    DWORD err = WSAGetLastError();
3477e2c95cbSTrond Norbye
348f603fdb6STrond Norbye    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
349f603fdb6STrond Norbye                      FORMAT_MESSAGE_FROM_SYSTEM |
350f603fdb6STrond Norbye                      FORMAT_MESSAGE_IGNORE_INSERTS,
351f603fdb6STrond Norbye                      NULL, err, 0,
352f603fdb6STrond Norbye                      (LPTSTR)&error_msg, 0, NULL) != 0) {
353f603fdb6STrond Norbye        fprintf(stderr, prefix, error_msg);
354f603fdb6STrond Norbye        LocalFree(error_msg);
355f603fdb6STrond Norbye    } else {
356f603fdb6STrond Norbye        fprintf(stderr, prefix, "unknown error");
357f603fdb6STrond Norbye    }
358f603fdb6STrond Norbye}
359f603fdb6STrond Norbye#else
360f603fdb6STrond Norbyestatic void log_network_error(const char* prefix) {
361f603fdb6STrond Norbye    fprintf(stderr, prefix, strerror(errno));
3627e2c95cbSTrond Norbye}
363f603fdb6STrond Norbye#endif
3647e2c95cbSTrond Norbye
365f4b802c8Sjim#ifdef WIN32
366f4b802c8Sjim#define CERTIFICATE_PATH(file) ("\\tests\\cert\\"#file)
368f4b802c8Sjim#define CERTIFICATE_PATH(file) ("/tests/cert/"#file)
371f4b802c8Sjimstatic void get_working_current_directory(char* out_buf, int out_buf_len) {
372f4b802c8Sjim    bool ok = false;
373f4b802c8Sjim#ifdef WIN32
374f4b802c8Sjim    ok = GetCurrentDirectory(out_buf_len, out_buf) != 0;
376f4b802c8Sjim    ok = getcwd(out_buf, out_buf_len) != NULL;
378f4b802c8Sjim    /* memcached may throw a warning, but let's push through */
379f4b802c8Sjim    if (!ok) {
380f4b802c8Sjim        fprintf(stderr, "Failed to determine current working directory");
381f4b802c8Sjim        strncpy(out_buf, ".", out_buf_len);
382f4b802c8Sjim    }
384595bef70STrond Norbye
385595bef70STrond Norbyestatic int generate_config(const char *fname)
386595bef70STrond Norbye{
387595bef70STrond Norbye    FILE *fp;
388595bef70STrond Norbye    cJSON *root = cJSON_CreateObject();
389595bef70STrond Norbye    cJSON *array = cJSON_CreateArray();
390595bef70STrond Norbye    cJSON *obj = cJSON_CreateObject();
391f4b802c8Sjim    cJSON *obj_ssl = NULL;
392f4b802c8Sjim    char pem_path[256];
393f4b802c8Sjim    char cert_path[256];
395f4b802c8Sjim    get_working_current_directory(pem_path, 256);
396f4b802c8Sjim    strncpy(cert_path, pem_path, 256);
397f4b802c8Sjim    strncat(pem_path, CERTIFICATE_PATH(testapp.pem), 256);
398f4b802c8Sjim    strncat(cert_path, CERTIFICATE_PATH(testapp.cert), 256);
399595bef70STrond Norbye
400595bef70STrond Norbye    cJSON_AddStringToObject(obj, "module", "default_engine.so");
401595bef70STrond Norbye    cJSON_AddItemReferenceToObject(root, "engine", obj);
402595bef70STrond Norbye
403595bef70STrond Norbye    obj = cJSON_CreateObject();
404595bef70STrond Norbye    cJSON_AddStringToObject(obj, "module", "blackhole_logger.so");
405595bef70STrond Norbye    cJSON_AddItemToArray(array, obj);
406595bef70STrond Norbye    obj = cJSON_CreateObject();
407595bef70STrond Norbye    cJSON_AddStringToObject(obj, "module", "fragment_rw_ops.so");
408595bef70STrond Norbye    cJSON_AddStringToObject(obj, "config", "r=225;w=226");
409595bef70STrond Norbye    cJSON_AddItemToArray(array, obj);
410e73832dcSjim    obj = cJSON_CreateObject();
411e73832dcSjim    cJSON_AddStringToObject(obj, "module", "testapp_extension.so");
412e73832dcSjim    cJSON_AddItemToArray(array, obj);
413595bef70STrond Norbye
414595bef70STrond Norbye    cJSON_AddItemReferenceToObject(root, "extensions", array);
415595bef70STrond Norbye
416595bef70STrond Norbye    array = cJSON_CreateArray();
417595bef70STrond Norbye    obj = cJSON_CreateObject();
418f4b802c8Sjim    obj_ssl = cJSON_CreateObject();
420595bef70STrond Norbye#ifdef WIN32
421595bef70STrond Norbye    cJSON_AddNumberToObject(obj, "port", 11211);
422595bef70STrond Norbye#else
423595bef70STrond Norbye    cJSON_AddNumberToObject(obj, "port", 0);
424595bef70STrond Norbye#endif
425595bef70STrond Norbye    cJSON_AddNumberToObject(obj, "maxconn", 1000);
426595bef70STrond Norbye    cJSON_AddNumberToObject(obj, "backlog", 1024);
427595bef70STrond Norbye    cJSON_AddStringToObject(obj, "host", "*");
428595bef70STrond Norbye    cJSON_AddItemToArray(array, obj);
429595bef70STrond Norbye
430f4b802c8Sjim    obj = cJSON_CreateObject();
431f4b802c8Sjim    cJSON_AddNumberToObject(obj, "port", 11996);
432f4b802c8Sjim    cJSON_AddNumberToObject(obj, "maxconn", 1000);
433f4b802c8Sjim    cJSON_AddNumberToObject(obj, "backlog", 1024);
434f4b802c8Sjim    cJSON_AddStringToObject(obj, "host", "*");
435f4b802c8Sjim    cJSON_AddItemToObject(obj, "ssl", obj_ssl = cJSON_CreateObject());
436f4b802c8Sjim    cJSON_AddStringToObject(obj_ssl, "key", pem_path);
437f4b802c8Sjim    cJSON_AddStringToObject(obj_ssl, "cert", cert_path);
438f4b802c8Sjim    cJSON_AddItemToArray(array, obj);
439595bef70STrond Norbye    cJSON_AddItemReferenceToObject(root, "interfaces", array);
440595bef70STrond Norbye
4418667210cSTrond Norbye    cJSON_AddStringToObject(root, "admin", "");
44271c3fd66STrond Norbye    cJSON_AddTrueToObject(root, "datatype_support");
4438667210cSTrond Norbye
444595bef70STrond Norbye    if ((fp = fopen(fname, "w")) == NULL) {
445595bef70STrond Norbye        return -1;
446595bef70STrond Norbye    } else {
447595bef70STrond Norbye        fprintf(fp, "%s", cJSON_Print(root));
448595bef70STrond Norbye        fclose(fp);
449595bef70STrond Norbye    }
450595bef70STrond Norbye
451595bef70STrond Norbye    return 0;
452595bef70STrond Norbye}
453595bef70STrond Norbye
454f603fdb6STrond Norbye#ifdef WIN32
455f4b802c8Sjimstatic HANDLE start_server(in_port_t *port_out, in_port_t *ssl_port_out, bool daemon, int timeout) {
456f603fdb6STrond Norbye    STARTUPINFO sinfo;
457f603fdb6STrond Norbye    PROCESS_INFORMATION pinfo;
458521feb91STrond Norbye    char *commandline = malloc(1024);
459f603fdb6STrond Norbye    char env[80];
460f603fdb6STrond Norbye    sprintf_s(env, sizeof(env), "MEMCACHED_PARENT_MONITOR=%u", GetCurrentProcessId());
461f603fdb6STrond Norbye    putenv(env);
462f603fdb6STrond Norbye
463521feb91STrond Norbye    memset(&sinfo, 0, sizeof(sinfo));
464f603fdb6STrond Norbye    memset(&pinfo, 0, sizeof(pinfo));
465f603fdb6STrond Norbye    sinfo.cb = sizeof(sinfo);
466f603fdb6STrond Norbye
467595bef70STrond Norbye    sprintf(commandline, "memcached.exe -C %s", config_file);
468f603fdb6STrond Norbye
469f603fdb6STrond Norbye    if (!CreateProcess("memcached.exe",
470f603fdb6STrond Norbye                       commandline,
471f603fdb6STrond Norbye                       NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo)) {
472f603fdb6STrond Norbye        LPVOID error_msg;
473f603fdb6STrond Norbye        DWORD err = GetLastError();
474f603fdb6STrond Norbye
475f603fdb6STrond Norbye        if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
476f603fdb6STrond Norbye                          FORMAT_MESSAGE_FROM_SYSTEM |
477f603fdb6STrond Norbye                          FORMAT_MESSAGE_IGNORE_INSERTS,
478f603fdb6STrond Norbye                          NULL, err, 0,
479f603fdb6STrond Norbye                          (LPTSTR)&error_msg, 0, NULL) != 0) {
480f603fdb6STrond Norbye            fprintf(stderr, "Failed to start process: %s\n", error_msg);
481f603fdb6STrond Norbye            LocalFree(error_msg);
482f603fdb6STrond Norbye        } else {
483f603fdb6STrond Norbye            fprintf(stderr, "Failed to start process: unknown error\n");
484f603fdb6STrond Norbye        }
485f603fdb6STrond Norbye        exit(EXIT_FAILURE);
486f603fdb6STrond Norbye    }
487521feb91STrond Norbye    /* Do a short sleep to let the other process to start */
488f603fdb6STrond Norbye    Sleep(1);
489f603fdb6STrond Norbye    CloseHandle(pinfo.hThread);
490f603fdb6STrond Norbye
491f603fdb6STrond Norbye    *port_out = 11211;
492f4b802c8Sjim    *ssl_port_out = 11996;
493f603fdb6STrond Norbye    return pinfo.hProcess;
494f603fdb6STrond Norbye}
495f603fdb6STrond Norbye#else
496e337faf6STrond Norbye/**
497e337faf6STrond Norbye * Function to start the server and let it listen on a random port
498e337faf6STrond Norbye *
499e337faf6STrond Norbye * @param port_out where to store the TCP port number the server is
500e337faf6STrond Norbye *                 listening on
501e337faf6STrond Norbye * @param daemon set to true if you want to run the memcached server
502e337faf6STrond Norbye *               as a daemon process
503e337faf6STrond Norbye * @return the pid of the memcached server
504e337faf6STrond Norbye */
506f4b802c8Sjimstatic pid_t start_server(in_port_t *port_out, in_port_t *ssl_port_out, bool daemon, int timeout) {
507e337faf6STrond Norbye    char environment[80];
508e337faf6STrond Norbye    char *filename= environment + strlen("MEMCACHED_PORT_FILENAME=");
509f603fdb6STrond Norbye#ifdef __sun
510f603fdb6STrond Norbye    char coreadm[128];
511f603fdb6STrond Norbye#endif
512f603fdb6STrond Norbye    pid_t pid;
513f603fdb6STrond Norbye    FILE *fp;
514f603fdb6STrond Norbye    char buffer[80];
515e337faf6STrond Norbye
5163819ce23STrond Norbye    char env[80];
5173819ce23STrond Norbye    snprintf(env, sizeof(env), "MEMCACHED_PARENT_MONITOR=%lu", (unsigned long)getpid());
5183819ce23STrond Norbye    putenv(env);
5193819ce23STrond Norbye
520f603fdb6STrond Norbye    snprintf(environment, sizeof(environment),
521f603fdb6STrond Norbye             "MEMCACHED_PORT_FILENAME=/tmp/ports.%lu", (long)getpid());
522e337faf6STrond Norbye    remove(filename);
5230e1d1167STrond Norbye
5246ae66c84STrond Norbye#ifdef __sun
5256ae66c84STrond Norbye    /* I want to name the corefiles differently so that they don't
5266ae66c84STrond Norbye       overwrite each other
5276ae66c84STrond Norbye    */
528cb10b041STrond Norbye    snprintf(coreadm, sizeof(coreadm),
529cb10b041STrond Norbye             "coreadm -p core.%%f.%%p %lu", (unsigned long)getpid());
5306ae66c84STrond Norbye    system(coreadm);
5316ae66c84STrond Norbye#endif
5326ae66c84STrond Norbye
533f603fdb6STrond Norbye    pid = fork();
534464d6f0bSTrond Norbye    cb_assert(pid != -1);
535e337faf6STrond Norbye
536e337faf6STrond Norbye    if (pid == 0) {
537e337faf6STrond Norbye        /* Child */
538fba0a891SDustin Sallings        char *argv[20];
539e337faf6STrond Norbye        int arg = 0;
5406ae66c84STrond Norbye        char tmo[24];
5416ae66c84STrond Norbye
542f603fdb6STrond Norbye        snprintf(tmo, sizeof(tmo), "%u", timeout);
543e337faf6STrond Norbye        putenv(environment);
54489d5126bSTrond Norbye
545e19b3127STrond Norbye        argv[arg++] = "./memcached";
546595bef70STrond Norbye        argv[arg++] = "-C";
547595bef70STrond Norbye        argv[arg++] = (char*)config_file;
548595bef70STrond Norbye
549e337faf6STrond Norbye        argv[arg++] = NULL;
550464d6f0bSTrond Norbye        cb_assert(execv(argv[0], argv) != -1);
551e337faf6STrond Norbye    }
552e337faf6STrond Norbye
553e337faf6STrond Norbye    /* Yeah just let us "busy-wait" for the file to be created ;-) */
554e337faf6STrond Norbye    while (access(filename, F_OK) == -1) {
555e337faf6STrond Norbye        usleep(10);
556e337faf6STrond Norbye    }
557e337faf6STrond Norbye
558f603fdb6STrond Norbye    fp = fopen(filename, "r");
559e337faf6STrond Norbye    if (fp == NULL) {
560e337faf6STrond Norbye        fprintf(stderr, "Failed to open the file containing port numbers: %s\n",
561e337faf6STrond Norbye                strerror(errno));
562464d6f0bSTrond Norbye        cb_assert(false);
563e337faf6STrond Norbye    }
564e337faf6STrond Norbye
565e337faf6STrond Norbye    *port_out = (in_port_t)-1;
566f4b802c8Sjim    *ssl_port_out = (in_port_t)-1;
568e337faf6STrond Norbye    while ((fgets(buffer, sizeof(buffer), fp)) != NULL) {
569e337faf6STrond Norbye        if (strncmp(buffer, "TCP INET: ", 10) == 0) {
570e337faf6STrond Norbye            int32_t val;
571464d6f0bSTrond Norbye            cb_assert(safe_strtol(buffer + 10, &val));
572f4b802c8Sjim            if (*port_out == (in_port_t)-1) {
573f4b802c8Sjim                *port_out = (in_port_t)val;
574f4b802c8Sjim            } else {
575f4b802c8Sjim                *ssl_port_out = (in_port_t)val;
576f4b802c8Sjim            }
577e337faf6STrond Norbye        }
578e337faf6STrond Norbye    }
5794c86fa59STrond Norbye    fclose(fp);
580464d6f0bSTrond Norbye    cb_assert(remove(filename) == 0);
581e337faf6STrond Norbye
582e337faf6STrond Norbye    return pid;
583e337faf6STrond Norbye}
584f603fdb6STrond Norbye#endif
585e337faf6STrond Norbye
5867bb82191STrond Norbyestatic struct addrinfo *lookuphost(const char *hostname, in_port_t port)
5877bb82191STrond Norbye{
5887bb82191STrond Norbye    struct addrinfo *ai = 0;
589f603fdb6STrond Norbye    struct addrinfo hints;
5907bb82191STrond Norbye    char service[NI_MAXSERV];
5917bb82191STrond Norbye    int error;
5927bb82191STrond Norbye
593f603fdb6STrond Norbye    memset(&hints, 0, sizeof(hints));
594f603fdb6STrond Norbye    hints.ai_family = AF_UNSPEC;
595f603fdb6STrond Norbye    hints.ai_protocol = IPPROTO_TCP;
596f603fdb6STrond Norbye    hints.ai_socktype = SOCK_STREAM;
597f603fdb6STrond Norbye
5987bb82191STrond Norbye    (void)snprintf(service, NI_MAXSERV, "%d", port);
5997bb82191STrond Norbye    if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
600f603fdb6STrond Norbye#ifdef WIN32
601f603fdb6STrond Norbye        log_network_error("getaddrinfo(): %s\r\n");
602f603fdb6STrond Norbye#else
6037bb82191STrond Norbye       if (error != EAI_SYSTEM) {
6047bb82191STrond Norbye          fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
6057bb82191STrond Norbye       } else {
6067bb82191STrond Norbye          perror("getaddrinfo()");
6077bb82191STrond Norbye       }
608f603fdb6STrond Norbye#endif
6097bb82191STrond Norbye    }
6107bb82191STrond Norbye
6117bb82191STrond Norbye    return ai;
6127bb82191STrond Norbye}
6137bb82191STrond Norbye
6166410c8f7Sjimstatic SOCKET create_connect_plain_socket(const char *hostname, in_port_t port, bool nonblock)
6177bb82191STrond Norbye{
6187bb82191STrond Norbye    struct addrinfo *ai = lookuphost(hostname, port);
619f603fdb6STrond Norbye    SOCKET sock = INVALID_SOCKET;
6207bb82191STrond Norbye    if (ai != NULL) {
6217bb82191STrond Norbye       if ((sock = socket(ai->ai_family, ai->ai_socktype,
622f603fdb6STrond Norbye                          ai->ai_protocol)) != INVALID_SOCKET) {
623ce9a18d0STrond Norbye          if (connect(sock, ai->ai_addr, (socklen_t)ai->ai_addrlen) == SOCKET_ERROR) {
624f603fdb6STrond Norbye             log_network_error("Failed to connect socket: %s\n");
625f603fdb6STrond Norbye             closesocket(sock);
626f603fdb6STrond Norbye             sock = INVALID_SOCKET;
6276410c8f7Sjim          }
6287bb82191STrond Norbye       } else {
6297bb82191STrond Norbye          fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
6307bb82191STrond Norbye       }
6317bb82191STrond Norbye
6327bb82191STrond Norbye       freeaddrinfo(ai);
6337bb82191STrond Norbye    }
6347bb82191STrond Norbye    return sock;
6357bb82191STrond Norbye}
6367bb82191STrond Norbye
6376410c8f7Sjimstatic SOCKET create_connect_ssl_socket(const char *hostname, in_port_t port, bool nonblocking) {
638f4b802c8Sjim    char port_str[32];
6396410c8f7Sjim    int sfd = 0;
6406410c8f7Sjim    BIO* temp_bio = NULL;