How to authenticate to Active Directory using gsasl gssapi? - c

I'm trying to authenticate to an Active Directory domain using gsasl. I've already kinit'd as the Administrator. I've tried to follow the test code in gsasl tests/gssapi.c, but the code below is failing with GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR when calling gsasl_step64().
static int callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
{
int ret = GSASL_NO_CALLBACK;
switch (prop) {
case GSASL_AUTHZID:
gsasl_property_set(sctx, GSASL_AUTHZID, "Administrator");
ret = GSASL_OK;
break;
case GSASL_SERVICE:
gsasl_property_set(sctx, prop, "host");
ret = GSASL_OK;
break;
case GSASL_HOSTNAME:
char hostname[HOST_NAME_MAX];
gethostname(hostname, HOST_NAME_MAX);
gsasl_property_set(sctx, prop, hostname);
ret = GSASL_OK;
break;
default:
break;
}
return ret;
}
int main()
{
Gsasl *ctx = NULL;
Gsasl_session *session;
char *s1 = NULL, *s2 = NULL;
int ret;
ret = gsasl_init(&ctx);
if (ret != GSASL_OK) {
cerr << "gsasl_init failed" << endl;
return ret;
}
if (!gsasl_client_support_p(ctx, "GSSAPI")) {
cerr << "No support for GSSAPI." << endl;
return 77;
}
gsasl_callback_set(ctx, callback);
ret = gsasl_client_start(ctx, "GSSAPI", &session);
if (ret != GSASL_OK) {
cerr << "gsasl_client_start failed" << endl;
return ret;
}
do {
ret = gsasl_step64(session, s2, &s1);
gsasl_free(s2);
if (ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
cerr << "gsasl_step64 failed " << ret << endl;
return ret;
}
} while (ret != GSASL_OK);
if (s1) {
gsasl_free(s1);
}
gsasl_finish(session);
}
Does anyone see what I'm doing wrong?

Not really a clear answer because I don't use AD currently, so I can't verify my answer. But to localize the issue:
I would try the examples first. Maybe this test is desired to fail. If it doesn't work with the examples, I would investigate the source of library of the GNU SASL library.
Here is an extract from the source of the library, which throws this error:
maj_stat = gss_init_sec_context (&min_stat,
GSS_C_NO_CREDENTIAL,
&state->context,
state->service,
state->mech_oid,
GSS_C_MUTUAL_FLAG,
0,
&state->cb,
buf,
&actual_mech_type,
&state->token, &ret_flags, NULL);
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
return GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR;
As you can see, the function gss_init_sec_context() is called, In the if clause only GSS_S_COMPLETE and GSS_S_CONTINUE_NEEDED will be tested. But gss_init_sec_context() has many more status codes. So I would change the code of the library and print out a clearer error code, why it doesn't work.
There was also an issue in WINBIND/SAMBA, don't know if you use SAMBA, which throws an unspecified error in this specific function.

Related

I want to ask about the use of the kea dhcp server, I hope someone can help me

I want to modify the dhcp4 source code to notify the ddns server to set the dynamic domain name when assigning the lease, but I added the nanomsg statement in dhcp4_srv.cc. When my nanomsg performs shutdown or close, the dhcp4 service will automatically close. This is why there is no other way to implement dynamic domain names (my dynamic domain name mainly sends the field set by the foreground and the mac address and IP address to the ddns server, or it may be the login account of the foreground).
Someone can help me? thank you very much.
if (lease) {
// We have a lease! Let's set it in the packet and send it back to
// the client.
// 我们有租约! 让我们在数据包中进行设置,然后将其发送回客户端。
if (fake_allocation) {
//租约建议
LOG_INFO(lease4_logger, DHCP4_LEASE_ADVERT)
.arg(query->getLabel())
.arg(lease->addr_.toText());
} else {
//成功授予租约
LOG_INFO(lease4_logger, DHCP4_LEASE_ALLOC)
.arg(query->getLabel())
.arg(lease->addr_.toText())
.arg(lease->valid_lft_);
int rc = 0;
int pair_socket = 0;
int str_len = 0;
char buf[256] = { 0 };
char buf1[256] = { 0 };
int timeo = 5000;
//计算长度
str_len = strlen(HELLOWORLD);
//初始化socket
pair_socket = nn_socket(1, NN_PAIR);
if (pair_socket == -1) {
printf("nn_socket failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
//设置超时
rc = nn_setsockopt(pair_socket, 0, NN_SNDTIMEO, &timeo, sizeof(timeo));
rc = nn_setsockopt(pair_socket, 0, NN_RCVTIMEO, &timeo, sizeof(timeo));
//连接服务端
rc = nn_connect(pair_socket, SOCKET_ADDRESS2);
if (rc < 0) {
printf("bind failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
//将hello world复制到buf中
memcpy(buf, HELLOWORLD, str_len);
//发送数据
rc = nn_send(pair_socket, buf, str_len, 0);
if (rc < 0) {
printf("nn_send failed! error: %s.rc = %d.\n", nn_err_strerror(errno), rc);
}
//打印
printf("send:%s\n", buf);
//这里主要是测试使用,平时项目不要使用标签
//接收数据
rc = nn_recv(pair_socket, buf1, 256, 0);
if (rc < 0) {
printf("nn_recv failed! error: %s.rc = %d.\n", nn_err_strerror(errno), rc);
}
//打印
printf("recv:%s\n", buf1);
memset(buf1, 0, 256);
//Sleep(1000);
//关闭套接字
rc = nn_shutdown(pair_socket, 1);
if (rc != 1) {
printf("nn_close failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
std::cout << "testtttttttttttttttttttttttttttttttttt "
<< "hostnameeeeeeeeeeeeeeeeeeeeeeeeeeeeee:" << lease->hostname_ << std::endl;
std::cout << "testtttttttttttttttttttttttttttttttttt "
<< "ipppppppppppppppppppppppppppppppppppp:" << lease->addr_.toText() << std::endl;
std::cout << "lease ALLOC " << __LINE__ << " file name " << __FILE__
<< " HOST:" << lease->hostname_ << std::endl;
std::cout << "lease ALLOC " << __LINE__ << " file name " << __FILE__
<< " IP:" << lease->addr_.toText() << std::endl;
}
It sounds like you need some general information on the KEA software developed by Internet Systems Consortium aka: https://kea.isc.org/ I would read through the Kea docs, mailing lists and the general development wiki to try to hone in on the specific issue you are having with your code. Once you are able explain the issue you are having with your code you can edit your question with your details, and there is a much better chance that you will get more meaningful answers from this site.

avformat_write_header fails with error "invalid argument

I'm trying to write a chunk of a mp4 file to a .ts file, which does not yet exist.
So first I create AVFormatContext this way:
AVOutputFormat *oformat = av_guess_format( NULL,
"mpegts",
"video/x-mpegts" );
AVFormatContext * outFormatContext = NULL;
avformat_alloc_output_context2( &outFormatContext, oformat, NULL, dst_file)
outFormatContext is created and outFormatContext->oformat is set to oformat. Then I open I/O context on outFormatContext:
avio_open(&outFormatContext->pb, dst_file, AVIO_FLAG_WRITE)
avformat_write_header( outFormatContext, NULL );
according to this this is enough for avformat_write_header to work but it fails with error "invalid argument".
What else needs to be done to successfully write the header?
Although it's a little bit late, I hope it would be helpful.
Here is my way to call avformat_write_header, you should have a stream provided to write the header.
AVFormatContext* pOutFormatContext;
AVOutputFormat* avOutputFormat;
if ((avOutputFormat = av_guess_format(NULL, "mp4", "video/mp4")) == NULL) {
cerr << "Could not guess output format" << endl;
return -1;
}
avformat_alloc_output_context2(&pOutFormatContext,
av_guess_format("mp4", NULL, "video/mp4"), NULL, NULL);
if (pOutFormatContext == NULL) {
cerr << "Could not allocate output context" << endl;
return -1;
}
for (int i = 0; i < avFormatContext->nb_streams; i++) {
AVStream* inAVStream = avFormatContext->streams[i];
AVStream* outAVStream = avformat_new_stream(pOutFormatContext, inAVStream->codec->codec);
if (avcodec_copy_context(outAVStream->codec, inAVStream->codec) < 0) {
cerr << "Failed to copy codec context" << endl;
return -1;
}
outAVStream->codec->codec_tag = 0;
if (pOutFormatContext->oformat->flags & AVFMT_GLOBALHEADER) {
outAVStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
avio_open(&pOutFormatContext->pb, "test.mp4", AVIO_FLAG_READ_WRITE);
if (pOutFormatContext->pb == NULL) {
cerr << "Could not open for writing" << endl;
return -1;
}
if (avformat_write_header(pOutFormatContext, NULL) != 0) {
cerr << "Could not write header" << endl;
return -1;
}

iTunes COM for Windows SDK IITUserPlaylist::AddFile access violation

I am just try to write simple program for some practice. But iTunes COM for Windows SDK threw me a little surprise.
I am want to create playlist and add file to it but when i call IITUserPlaylist::AddFile program crash with access violation. I can all other methods like Delete etc.
Project (MSVC10): http://www76.zippyshare.com/v/34795637/file.html
#include <iostream>
#include <windows.h>
#include <comutil.h>
#include "iTunesCOMInterface.h"
int main(int argc, char* argv[])
{
CoInitialize(0);
HRESULT hRes;
IiTunes* itunes = 0;
// note - CLSID_iTunesApp and IID_IiTunes are defined in iTunesCOMInterface_i.c
hRes = ::CoCreateInstance(CLSID_iTunesApp, NULL, CLSCTX_LOCAL_SERVER, IID_IiTunes, (PVOID *)&itunes);
if(hRes != S_OK && itunes == nullptr)
{
std::cout << "Can not create iTunesApp instance" << std::endl;
CoUninitialize();
return 1;
}
IITLibraryPlaylist* mainLibrary = 0;
hRes = itunes->get_LibraryPlaylist(&mainLibrary);
if(hRes != S_OK || mainLibrary == 0)
{
std::cout << "Can not get main library" << std::endl;
itunes->Release();
CoUninitialize();
return 1;
}
IITSource* mainLibrarySource = 0;
hRes = itunes->get_LibrarySource(&mainLibrarySource);
if(hRes != S_OK || mainLibrarySource == 0)
{
std::cout << "Can not get library source" << std::endl;
mainLibrary->Release();
itunes->Release();
CoUninitialize();
return 1;
}
IITPlaylistCollection* playlistCollection = 0;
hRes = mainLibrarySource->get_Playlists(&playlistCollection);
if(hRes != S_OK || playlistCollection == 0)
{
std::cout << "Can not get source playlists" << std::endl;
mainLibrarySource->Release();
mainLibrary->Release();
itunes->Release();
CoUninitialize();
return 1;
}
BSTR playlistName = SysAllocString(L"SoundFrost playlist");
IITPlaylist* ownPlaylist = 0;
//TODO: But there can be additional playlists with the same name, since playlist names are not unique
hRes = playlistCollection->get_ItemByName(playlistName, &ownPlaylist);
if(hRes != S_OK || ownPlaylist == 0)
{
//just create new
hRes = itunes->CreatePlaylist(playlistName, &ownPlaylist);
if(hRes != S_OK || ownPlaylist == 0)
{
std::cout << "Can not create playlist";
SysFreeString(playlistName);
playlistCollection->Release();
mainLibrarySource->Release();
mainLibrary->Release();
itunes->Release();
CoUninitialize();
return 1;
}
}
SysFreeString(playlistName);
ITPlaylistKind playlistKind;
hRes = ownPlaylist->get_Kind(&playlistKind);
if(hRes == S_OK && playlistKind == ITPlaylistKindUser)
{
IITUserPlaylist* ownUserPlaylist = static_cast<IITUserPlaylist*>(ownPlaylist);
if(ownUserPlaylist != 0)
{
BSTR filePath = SysAllocString(L"C:\\test.mp3");
IITOperationStatus* status;
//hRes = mainLibrary->AddFile(filePath, &status);
//hRes = ownUserPlaylist->AddFile(filePath, &status);
SysFreeString(filePath);
}
}
playlistCollection->Release();
ownPlaylist->Release();
mainLibrarySource->Release();
mainLibrary->Release();
itunes->Release();
CoUninitialize();
return 0;
}
If i call IITLibraryPlaylist::AddFile all was right.
Just uncomment 95 or 96 line for test.
Simple C# code works fine:
iTunesApp itunes = new iTunesLib.iTunesApp();
IITUserPlaylist playlist = itunes.CreatePlaylist("C# playlist") as IITUserPlaylist;
IITOperationStatus status = playlist.AddFile("C:\\test.mp3");
I can not understand why this is happening.
You can't really do this with COM:
IITUserPlaylist* ownUserPlaylist = static_cast<IITUserPlaylist*>(ownPlaylist);
You're just casting an interface pointer into another interface pointer. You must do a QueryInterface instead:
HRESULT hr = ownPlaylist->QueryInterface(IID_IITUserPlaylist, (void**)&ownUserPlaylist);

C: Code style for returning errors?

All my functions look like this:
short Function()
{
short ret = 0;
ret = FunctionA();
if(ret != 0) return ret;
ret = FunctionB();
if(ret != 0) return ret;
ret = FunctionC();
if(ret != 0) return ret;
return 0;
}
Is there a nicer way to write this? Without having to repeat
if(ret != 0) return ret;
all the time?
If using a short-circuited || like suggested in the other answer is not an option, you could define a macro for that:
#define TRY(var, x) if ((var = (x))) return var
Then in your code:
short Function()
{
short ret;
TRY(ret, FunctionA());
TRY(ret, FunctionB());
TRY(ret, FunctionC());
return 0;
}
NOTE: You should be very careful when deciding to use macros, but in this case I think it can be a clean way to solve the problem.
It must be mentioned, though, that these statements conceil the fact that the function could return early at every one of them. If you have open resource handles (file descriptors, pointers to malloced data, ...), they will leak. You and everyone working with the code should be aware of this and use appropriate error handling and cleanup routines for more complex cases than this one.
short Function()
{
short ret = 0;
if(
(ret = FunctionA()) != 0 ||
(ret = FunctionB()) != 0 ||
(ret = FunctionC()) != 0
)
{
return ret;
}
return 0;
}
I'm going the other way, and showing you how I actually do this in the programs I write:
short Function() {
short ret = 0;
ret = FunctionA();
if(ret != 0) {
SomeUsefulMessageOrAssertionHere();
return ret;
}
...
It actually reduces upstream error handling code because well written diagnostics are best handled at the callsite. Of course, it does not alter the action that must be taken in the event of an error.
There are many ways to rewrite this, but I can't think of anything more straightforward and easier on the eyes than yours.
Here's another variant, especially designed to minimise the number of if ... return statements.
int i;
for (i = 0; i<3; +i) {
switch (i) {
case 0: ret = FunctionA(); break;
case 1: ret = FunctionB(); break;
case 2: ret = FunctionC(); break;
}
if (ret != 0) return ret;
}
return 0;
This should be equivalent if I made no mistake
short Function()
{
short ret = FunctionA();
if(ret == 0)
{
ret = FunctionB();
if(ret == 0)
{
ret = FunctionC();
}
}
return ret;
}
short Function() {
short temp;
return (temp = FunctionA()) ? temp : (temp = FunctionB()) ? temp : FunctionC();
}
You could write:
short ret;
if (ret = FunctionA()) return ret;
if (ret = FunctionB()) return ret;
if (ret = FunctionC()) return ret;
You'll possibly surprise a few of your co-workers like that, though!
An alternative, using short-circuiting, inspired by #Tim's post:
short ret;
return (ret = FunctionA()) || (ret = FunctionB()) || (ret = FunctionC()) ? ret : 0;
In C++ you can say:
if (short ret = FunctionA()) return ret;
I've always liked function pointers, myself.
int main()
{
typedef ret-type (*Fptr)( ... args-types ... );
const int N_FUNC = 3;
Fptr functions[] = { FunctionA, FunctionB, FunctionC };
short ret = 0;
for( int i=0; ! ret && i < N_FUNCS; i++ )
ret = functions[i];
return ret;
}

Example Code for MemCached in C

I am looking for some sample C code for using memcache to set a value
Connecting to Server/Port
using multiple memcache_set
Close
I have the app running in PHP in 5 lines of code but can not find any good memcache samples in C which I need to port to.
This is a great memcached sample in C
#include <libmemcached/memcached.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
//memcached_servers_parse (char *server_strings);
memcached_server_st *servers = NULL;
memcached_st *memc;
memcached_return rc;
char *key = "keystring";
char *value = "keyvalue";
char *retrieved_value;
size_t value_length;
uint32_t flags;
memc = memcached_create(NULL);
servers = memcached_server_list_append(servers, "localhost", 11211, &rc);
rc = memcached_server_push(memc, servers);
if (rc == MEMCACHED_SUCCESS)
fprintf(stderr, "Added server successfully\n");
else
fprintf(stderr, "Couldn't add server: %s\n", memcached_strerror(memc, rc));
rc = memcached_set(memc, key, strlen(key), value, strlen(value), (time_t)0, (uint32_t)0);
if (rc == MEMCACHED_SUCCESS)
fprintf(stderr, "Key stored successfully\n");
else
fprintf(stderr, "Couldn't store key: %s\n", memcached_strerror(memc, rc));
retrieved_value = memcached_get(memc, key, strlen(key), &value_length, &flags, &rc);
printf("Yay!\n");
if (rc == MEMCACHED_SUCCESS) {
fprintf(stderr, "Key retrieved successfully\n");
printf("The key '%s' returned value '%s'.\n", key, retrieved_value);
free(retrieved_value);
}
else
fprintf(stderr, "Couldn't retrieve key: %s\n", memcached_strerror(memc, rc));
return 0;
}
Here is a slightly more complete answer, which also shows how to get and delete from a memcached server. The code is technically in C++, but doesn't use any features that would be unfamiliar to most C programmers.
To compile: g++ sample.cc -o sample -ggdb -O3 -lmemcached
To run: ./sample -s localhost -p 11211
The functions whose names begin with mcd_ show the basics of inserting, deleting, and getting, as well as connecting and disconnecting from memcached. The remaining code makes use of those functions to insert 50 key/value pairs, verify they all are in the server, remove half the pairs, and then verify that the right keys remain / are gone.
#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <libmemcached/memcached.h>
// NB: I know that `using namespace std` at global scope is bad form, and that
// global variables are bad form. However, they help keep this answer under
// 200 lines.
using namespace std;
/// The connection to memcached
memcached_st *mcd;
/// A global set of key/value pairs that we manufacture for the sake of this
/// example
vector<pair<string, string>> kv_pairs;
/// Put a key/value pair into memcached with expiration 0, no special flags
bool mcd_set(const string &key, const string &val) {
auto rc = memcached_set(mcd, key.c_str(), key.length(), val.c_str(),
val.length(), (time_t)0, (uint32_t)0);
if (rc == MEMCACHED_SUCCESS)
return true;
cout << "Error in mcd_set(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Delete a key/value pair from memcached. return true on success
/// NB: `(time_t)0` is an expiration of `0`, i.e., immediately
bool mcd_delete(const string &key) {
auto rc = memcached_delete(mcd, key.c_str(), key.length(), (time_t)0);
if (rc == MEMCACHED_SUCCESS)
return true;
cout << "Error in mcd_delete(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Get a value from the kv store, using its key to do the lookup. return
/// true on success. On success, the by-ref out parameter `val` will be set.
bool mcd_get(const string &key, string &val) {
memcached_return rc;
size_t len;
uint32_t flags = 0;
char *res = memcached_get(mcd, key.c_str(), key.length(), &len, &flags, &rc);
if (rc == MEMCACHED_SUCCESS) {
val = string(res, len);
free(res);
return true;
}
// NB: skip next line, because we don't want error messages on a failed get:
//
// cout << "Error in mcd_get(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Connect to a single memcached server on the provided port
bool mcd_connect(const string &servername, const int port) {
mcd = memcached_create(nullptr);
memcached_return rc;
memcached_server_st *servers = nullptr;
servers =
memcached_server_list_append(servers, servername.c_str(), port, &rc);
rc = memcached_server_push(mcd, servers);
if (rc == MEMCACHED_SUCCESS) {
cout << " Successfully connected to " << servername << ":" << port << endl;
return true;
}
cout << "Error in mcd_connect(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Close the connection to memcached
void mcd_shutdown() {
memcached_free(mcd);
cout << " Successfully disconnected\n";
}
/// Create a bunch of key/value pairs
void build_kv_pairs(int howmany) {
for (int i = 0; i < howmany; ++i) {
string key = "key" + to_string(i) + "_______";
string val = "val" + to_string(i);
for (int i = 0; i < 100; ++i)
val += ("_" + to_string(i));
kv_pairs.push_back({key, val});
}
}
/// insert a bunch of k/v pairs into memcached
bool put_kv_pairs(int howmany) {
for (int i = 0; i < howmany; ++i) {
if (!mcd_set(kv_pairs[i].first, kv_pairs[i].second)) {
cout << "Error inserting key `" << kv_pairs[i].first << "`\n";
return false;
}
}
cout << " put_kv_pairs(" << howmany << ") completed successfully\n";
return true;
}
/// Remove a sequence of keys from memcached
///
/// NB: Here and below, we use first/last/stride so that we can vary wich
/// key/value pairs are operated on
bool delete_kv_pairs(int first, int last, int stride) {
for (int i = first; i <= last; i += stride) {
if (!mcd_delete(kv_pairs[i].first)) {
cout << "Error removing key `" << kv_pairs[i].first << "`\n";
return false;
}
}
cout << " delete_kv_pairs(" << first << ", " << last << ", " << stride
<< ") completed successfully\n";
return true;
}
/// Verify that a sequence of keys is in memcached, with the right expected
/// values
bool check_present_pairs(int first, int last, int stride) {
for (int i = first; i <= last; i += stride) {
string value;
if (!mcd_get(kv_pairs[i].first, value)) {
cout << "Error getting key `" << kv_pairs[i].first
<< "`: key not found\n";
return false;
}
if (value != kv_pairs[i].second) {
cout << "Value error while getting key `" << kv_pairs[i].first << "`\n";
cout << " Expected: `" << kv_pairs[i].second << "`\n";
cout << " Found: `" << value << "`\n";
return false;
}
}
cout << " check_present_pairs(" << first << ", " << last << ", " << stride
<< ") completed successfully\n";
return true;
}
/// Verify that a sequence of keys is *not* in memcached
bool check_missing_pairs(int first, int last, int stride) {
for (int i = first; i <= last; i += stride) {
string value;
if (mcd_get(kv_pairs[i].first, value)) {
cout << "Error getting key `" << kv_pairs[i].first
<< "`: key unexpectedly found\n";
return false;
}
}
cout << " check_missing_pairs(" << first << ", " << last << ", " << stride
<< ") completed successfully\n";
return true;
}
int main(int argc, char **argv) {
// Parse args to get server name (-s) and port (-p)
string servername = "";
int port;
long opt;
while ((opt = getopt(argc, argv, "s:p:")) != -1) {
switch (opt) {
case 's':
servername = string(optarg);
break;
case 'p':
port = atoi(optarg);
break;
}
}
// Create a bunch of key/value pairs to use for the experiments
int howmany = 50;
build_kv_pairs(howmany);
// Connect to memcached
mcd_connect(servername, port);
// insert all the pairs, make sure they are all present
put_kv_pairs(howmany);
check_present_pairs(0, howmany - 1, 1);
// Delete the even pairs
delete_kv_pairs(0, howmany - 2, 2);
// ensure the odd pairs are present
check_present_pairs(1, howmany - 1, 2);
// ensure the even pairs are not present
check_missing_pairs(0, howmany, 2);
mcd_shutdown();
}
The code should produce output like the following:
Successfully connected to localhost:11211
put_kv_pairs(50) completed successfully
check_present_pairs(0, 49, 1) completed successfully
delete_kv_pairs(0, 48, 2) completed successfully
check_present_pairs(1, 49, 2) completed successfully
check_missing_pairs(0, 50, 2) completed successfully
Successfully disconnected

Resources