bind9/bin/tests/omapi_test.c
David Lawrence 1b6d529cb5 Megacommit of many files.
Mostly, several functions that take pointers as arguments, almost
always char * pointers, had those pointers qualified with "const".
Those that returned pointers to previously const-qualified arguments
had their return values qualified as const.  Some structure members
were qualified as const to retain that attribute from the variables
from which they were assigned.

Several unused functions removed from dispatch_tcp_test.c (left over
from when it was copied from dispatch_test.c).

Minor other ISC style cleanups.
2000-06-01 19:11:07 +00:00

719 lines
19 KiB
C

/*
* Copyright (C) 1996, 1997, 1998, 1999, 2000 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* Test code for OMAPI.
*/
#include <config.h>
#include <stdlib.h>
#include <isc/commandline.h>
#include <isc/condition.h>
#include <isc/mem.h>
#include <isc/socket.h>
#include <isc/string.h>
#include <isc/task.h>
#include <isc/util.h>
#include <dst/result.h>
#include <omapi/omapi.h>
char *progname;
isc_mem_t *mctx;
isc_boolean_t error_noobject = ISC_FALSE;
isc_boolean_t error_nosig = ISC_FALSE;
isc_boolean_t error_badsig = ISC_FALSE;
isc_boolean_t error_unknownsig = ISC_FALSE;
isc_boolean_t error_denyall = ISC_FALSE;
/*
* Two different structures are used in this program to store the
* value of interest on both the client and the server, but the
* same structure can be used on each if desired (with the other variables
* that are not in common between them being stored elsewhere).
*/
typedef struct server_object {
OMAPI_OBJECT_PREAMBLE;
unsigned long value;
isc_boolean_t target_reached;
} server_object_t;
typedef struct client_object {
OMAPI_OBJECT_PREAMBLE;
unsigned long value;
} client_object_t;
static server_object_t master_data;
static omapi_objecttype_t *server_type;
static omapi_objecttype_t *client_type;
static isc_condition_t waiter;
static isc_mutex_t mutex;
/*
* This is a string that names the registry of objects of type server_object_t.
*/
#define SERVER_OBJECT_TYPE "test-data"
/*
* This is the name of the variable that is being manipulated in the server.
* Note that it necessarily has no direct relevance to the *real* name of
* the variable (but of course, making them the same would make for clearer
* programming).
*/
#define MASTER_VALUE "amount"
#define KEY1_NAME "test-key"
#define KEY2_NAME "another-key"
/*
* Create an OMAPI message on the client that requests an object on the
* server be opened. If the boolean 'update' is given, then the value
* of the client's object will set the value of the server's object,
* otherwise the server just refreshes the values in the client's object
* with the master data.
*/
static isc_result_t
open_object(omapi_object_t *handle, omapi_object_t *manager,
isc_boolean_t update)
{
omapi_object_t *message = NULL;
REQUIRE(handle->type == client_type);
/*
* Create a new message object to store the information that will
* be sent to the server.
*/
RUNTIME_CHECK(omapi_message_create(&message) == ISC_R_SUCCESS);
/*
* Specify the OPEN operation, and the UPDATE option if requested.
*/
RUNTIME_CHECK(omapi_object_setinteger(message, "op", OMAPI_OP_OPEN)
== ISC_R_SUCCESS);
if (update)
RUNTIME_CHECK(omapi_object_setboolean(message, "update",
ISC_TRUE)
== ISC_R_SUCCESS);
/*
* Tell the server the type of the object being opened; it needs
* to know this so that it can apply the proper object methods
* for lookup/setvalue.
*/
RUNTIME_CHECK(omapi_object_setstring(message, "type",
SERVER_OBJECT_TYPE)
== ISC_R_SUCCESS);
/*
* Associate the client object with the message, so that it
* will have its values stuffed in the message. Without it,
* the OPEN operation will fail because there is no name/value
* pair to use as a key for looking up the desired object at
* the server.
*/
if (! error_noobject)
RUNTIME_CHECK(omapi_object_setobject(message, "object", handle)
== ISC_R_SUCCESS);
/*
* Set up an object that will receive the "status" signal in its
* signal handler when the response is received from the server.
* This is not needed as a general rule, because normally the
* the object associated with the name "object" in the message
* is what gets that message. However, in this case the
* particular error that is being tested when error_noobject is true
* is one where no item named "object" has been set, so not only
* can it not be used as a key when the server gets the message,
* it can't be used to get the "status" signal on the client
* when the server's "no key" error message comes back.
*
* If both "object" and "notify-object" are set, only the latter
* will receive the signal (as currently written; it was originally
* the other way 'round). In this particular case it hardly matters,
* as both "object" and "notify-object" are the same object.
*/
RUNTIME_CHECK(omapi_object_setobject(message, "notify-object", handle)
== ISC_R_SUCCESS);
/*
* Add the new message to the list of known messages. When the
* server's response comes back, the client will verify that
* the response was for a message it really sent.
*/
omapi_message_register(message);
/*
* Deliver the message to the server. The manager's outer object
* is the connection object to the server.
*/
RUNTIME_CHECK(omapi_message_send(message, manager) == ISC_R_SUCCESS);
/*
* Free the message.
*/
omapi_message_unregister(message);
omapi_object_dereference(&message);
return (ISC_R_SUCCESS);
}
/*
* client_setvalue() is called on the client by the library's internal
* message_process() function when the server replies to the OPEN operation
* with its own REFRESH message for the client. It is how the client learns
* what data is on the server.
*/
static isc_result_t
client_setvalue(omapi_object_t *handle, omapi_string_t *name,
omapi_data_t *value)
{
isc_region_t region;
client_object_t *client;
REQUIRE(handle->type == client_type);
client = (client_object_t *)handle;
/*
* Only the MASTER_VALUE value has meaning in this program.
*/
if (omapi_string_strcmp(name, MASTER_VALUE) == 0) {
client->value = omapi_data_getint(value);
return (ISC_R_SUCCESS);
} else if (omapi_string_strcmp(name, "remote-handle") == 0) {
/*
* The server will also set "remote-handle" to let the client
* have an identifier for the object on the server that could
* be used with the other OMAPI operations, such as
* OMAPI_OP_DELETE. The value of remote-handle is an integer,
* fetched with:
* omapi_data_getint(&remote_handle, value).
*
* It is not used by this test program.
*/
return (ISC_R_SUCCESS);
}
omapi_string_totext(name, &region);
fprintf(stderr, "client_setvalue: unknown name: '%.*s'\n",
(int)region.length, region.base);
return (ISC_R_NOTFOUND);
}
/*
* This function is used by omapi_message_send to publish the values of
* the data in a client object.
*/
static isc_result_t
client_stuffvalues(omapi_object_t *connection, omapi_object_t *handle) {
client_object_t *client;
REQUIRE(handle->type == client_type);
client = (client_object_t *)handle;
/*
* Write the MASTER_VALUE name, followed by the value length,
* follwed by its value.
*/
RUNTIME_CHECK(omapi_connection_putname(connection, MASTER_VALUE)
== ISC_R_SUCCESS);
RUNTIME_CHECK(omapi_connection_putuint32(connection,
sizeof(isc_uint32_t))
== ISC_R_SUCCESS);
RUNTIME_CHECK(omapi_connection_putuint32(connection, client->value)
== ISC_R_SUCCESS);
return (ISC_R_SUCCESS);
}
static isc_result_t
client_signalhandler(omapi_object_t *handle, const char *name, va_list ap) {
client_object_t *client;
omapi_value_t *tv;
isc_region_t region;
isc_boolean_t expected;
REQUIRE(handle->type == client_type);
client = (client_object_t *)handle;
if (strcmp(name, "updated") == 0) {
client->waitresult = ISC_R_SUCCESS;
} else if (strcmp(name, "status") == 0) {
/*
* "status" is signalled with the result of the message's
* operation.
*/
client->waitresult = va_arg(ap, isc_result_t);
expected = ISC_TF((error_noobject &&
client->waitresult == ISC_R_NOTFOUND) ||
(error_nosig &&
client->waitresult == ISC_R_NOPERM) ||
(error_badsig &&
client->waitresult ==
DST_R_VERIFYFINALFAILURE));
if (client->waitresult != ISC_R_SUCCESS)
fprintf(stderr, "%s: message status: %s (%s)\n",
progname,
isc_result_totext(client->waitresult),
expected ? "expected" : "UNEXPECTED");
tv = va_arg(ap, omapi_value_t *);
if (tv != NULL) {
omapi_value_getregion(tv, &region);
fprintf(stderr, "%s: additional text provided: %.*s\n",
progname, (int)region.length, region.base);
}
} else {
/*
* Pass any unknown signal any internal object.
* (This normally does not happen; there is no
* inner object, nor anything else being signalled.)
*/
fprintf(stderr, "%s: client_signalhandler: unknown signal: %s",
progname, name);
return (omapi_object_passsignal(handle, name, ap));
}
return (ISC_R_SUCCESS);
}
/*
* This is the function that is called when an incoming OMAPI_OP_OPEN
* message is received with either the create or update option set.
* It is called once for each name/value pair in the message's object
* value list.
*
* (Primary caller: message_process())
*/
static isc_result_t
server_setvalue(omapi_object_t *handle, omapi_string_t *name,
omapi_data_t *value)
{
isc_region_t region;
RUNTIME_CHECK(handle == (omapi_object_t *)&master_data);
/*
* Only one name is supported for this object, MASTER_VALUE.
*/
if (omapi_string_strcmp(name, MASTER_VALUE) == 0) {
fprintf(stderr, "existing value: %lu\n", master_data.value);
master_data.value = omapi_data_getint(value);
fprintf(stderr, "new value: %lu\n", master_data.value);
/*
* 32 is an arbitrary disconnect point.
*/
if (master_data.value >= 32) {
master_data.target_reached = ISC_TRUE;
SIGNAL(&waiter);
}
return (ISC_R_SUCCESS);
}
omapi_string_totext(name, &region);
fprintf(stderr, "server_setvalue: unknown name: '%.*s'\n",
(int)region.length, region.base);
return (ISC_R_NOTFOUND);
}
/*
* This is the function that is called by the library's internal
* message_process() function when an incoming OMAPI_OP_OPEN
* message is received. It is normally supposed to look up the object
* in the server that corresponds to the key data (name/value pair(s))
* in 'ref'.
*/
static isc_result_t
server_lookup(omapi_object_t **server_object, omapi_object_t *key) {
/*
* For this test program, there is only one static structure
* which is being used, so key is not needed.
*/
UNUSED(key);
omapi_object_reference(server_object, (omapi_object_t *)&master_data);
return (ISC_R_SUCCESS);
}
/*
* This function is called when the server is sending a reply to a client
* that opened an object of its type. It needs to output all published
* name/value pairs for the object, and will typically also put the data
* for any inner objects (but in this program, there will be no inner
* objects).
*/
static isc_result_t
server_stuffvalues(omapi_object_t *connection, omapi_object_t *handle) {
server_object_t *master = (server_object_t *)handle;
/*
* Write the MASTER_VALUE name, followed by the value length,
* follwed by its value.
*/
RUNTIME_CHECK(omapi_connection_putname(connection, MASTER_VALUE)
== ISC_R_SUCCESS);
RUNTIME_CHECK(omapi_connection_putuint32(connection,
sizeof(isc_uint32_t))
== ISC_R_SUCCESS);
RUNTIME_CHECK(omapi_connection_putuint32(connection, master->value)
== ISC_R_SUCCESS);
return (ISC_R_SUCCESS);
}
static void
do_connect(const char *host, int port) {
omapi_object_t *manager;
omapi_object_t *omapi_client = NULL;
client_object_t *client = NULL;
isc_result_t result;
const char *key;
RUNTIME_CHECK(omapi_object_register(&client_type, "client",
client_setvalue,
NULL, /* getvalue */
NULL, /* destroy */
client_signalhandler,
client_stuffvalues,
NULL, /* lookup */
NULL, /* create */
NULL) /* remove */
== ISC_R_SUCCESS);
/*
* Create the top level object which will manage the
* connection to the server.
*/
manager = NULL;
RUNTIME_CHECK(omapi_object_create(&manager, NULL, 0)
== ISC_R_SUCCESS);
result = omapi_protocol_connect(manager, host, port, NULL);
if (result != ISC_R_SUCCESS) {
fprintf(stderr, "%s: omapi_protocol_connect: %s\n",
progname, isc_result_totext(result));
omapi_object_dereference(&manager);
return;
}
/*
* Authenticate to the server.
*/
key = KEY1_NAME;
if (error_badsig) {
omapi_auth_deregister(KEY1_NAME);
RUNTIME_CHECK(omapi_auth_register(KEY1_NAME,
"this secret is wrong",
OMAPI_AUTH_HMACMD5)
== ISC_R_SUCCESS);
} else if (error_unknownsig) {
RUNTIME_CHECK(omapi_auth_register(KEY2_NAME,
"Yet Another Secret",
OMAPI_AUTH_HMACMD5)
== ISC_R_SUCCESS);
key = KEY2_NAME;
}
if (! error_nosig) {
result = omapi_auth_use(manager, key, OMAPI_AUTH_HMACMD5);
if (result != ISC_R_SUCCESS)
fprintf(stderr, "%s: omapi_auth_use: %s (%s)\n",
progname, isc_result_totext(result),
(error_unknownsig && result == ISC_R_NOTFOUND)
? "expected" : "UNEXPECTED");
}
if (result == ISC_R_SUCCESS) {
/*
* Create the client's object.
*/
omapi_object_create((omapi_object_t **)&client, client_type,
sizeof(client_object_t));
omapi_client = (omapi_object_t *)client;
/*
* The object needs to have a name/value pair created for it
* even before it contacts the server so the server will know
* that there is an object that needs values filled in. This
* name/value is created with the value of 0, but any interger
* value would work.
*/
RUNTIME_CHECK(omapi_object_setinteger(omapi_client,
MASTER_VALUE, 0)
== ISC_R_SUCCESS);
RUNTIME_CHECK(open_object(omapi_client, manager, ISC_FALSE)
== ISC_R_SUCCESS);
if (client->waitresult == ISC_R_SUCCESS) {
/*
* Set the new value to be stored at the server and
* reopen the server object with an UPDATE operation.
*/
fprintf(stderr, "existing value: %lu\n",
client->value);
client->value *= 2;
if (client->value == 0) /* Check overflow. */
client->value = 1;
fprintf(stderr, "new value: %lu\n", client->value);
RUNTIME_CHECK(open_object(omapi_client, manager,
ISC_TRUE)
== ISC_R_SUCCESS);
RUNTIME_CHECK(client->waitresult == ISC_R_SUCCESS);
}
}
/*
* Close the connection and wait to be disconnected.
*/
omapi_protocol_disconnect(manager, OMAPI_CLEAN_DISCONNECT);
/*
* Free the protocol manager and client object.
*/
omapi_object_dereference(&manager);
if (omapi_client != NULL)
omapi_object_dereference(&omapi_client);
}
static void
listen_done(isc_task_t *task, isc_event_t *event) {
omapi_object_t *listener = event->ev_arg;
UNUSED(task);
fprintf(stderr, "SERVER STOPPED\n");
isc_event_free(&event);
omapi_object_dereference(&listener);
omapi_lib_destroy();
}
static void
do_listen(int port) {
omapi_object_t *listener = NULL;
isc_sockaddr_t sockaddr;
struct in_addr inaddr;
dns_acl_t *acl;
/*
* Create the manager for handling incoming server connections.
*/
RUNTIME_CHECK(omapi_object_create(&listener, NULL, 0)
== ISC_R_SUCCESS);
/*
* Register the server_object. The SERVER_OBJECT_TYPE is what
* a client would need to specify as a value for the name "type"
* when contacting the server in order to be able to find objects
* server_type.
*/
RUNTIME_CHECK(omapi_object_register(&server_type, SERVER_OBJECT_TYPE,
server_setvalue,
NULL, /* getvalue */
NULL, /* destroy */
NULL, /* signalhandler */
server_stuffvalues,
server_lookup,
NULL, /* create */
NULL) /* remove */
== ISC_R_SUCCESS);
/*
* Initialize the server_object data.
*/
master_data.type = server_type;
master_data.refcnt = 1;
master_data.value = 2;
master_data.target_reached = ISC_FALSE;
LOCK(&mutex);
/*
* Set the access control list for valid connections.
*/
if (error_denyall)
RUNTIME_CHECK(dns_acl_none(mctx, &acl) == ISC_R_SUCCESS);
else
RUNTIME_CHECK(dns_acl_any(mctx, &acl) == ISC_R_SUCCESS);
/*
* Start listening for connections.
*/
inaddr.s_addr = INADDR_ANY;
isc_sockaddr_fromin(&sockaddr, &inaddr, port);
RUNTIME_CHECK(omapi_protocol_listen(listener, &sockaddr, acl, 1,
listen_done, listener)
== ISC_R_SUCCESS);
fprintf(stderr, "SERVER STARTED\n");
/*
* Lose one reference to the acl; the omapi library holds another
* reference and should free it when it is done.
*/
dns_acl_detach(&acl);
/*
* Block until done. "Done" is when server_setvalue has reached
* its trigger value.
*/
do {
WAIT(&waiter, &mutex);
} while (! master_data.target_reached);
omapi_listener_shutdown(listener);
}
#undef ARG_IS
#define ARG_IS(s) (strcmp(isc_commandline_argument, (s)) == 0)
int
main(int argc, char **argv) {
isc_boolean_t show_final_mem = ISC_FALSE;
isc_socketmgr_t *socketmgr = NULL;
isc_taskmgr_t *taskmgr = NULL;
int ch;
progname = strrchr(*argv, '/');
if (progname != NULL)
progname++;
else
progname = *argv;
while ((ch = isc_commandline_parse(argc, argv, "e:m")) != -1) {
switch (ch) {
case 'e':
if (ARG_IS("noobject"))
error_noobject = ISC_TRUE;
else if (ARG_IS("nosig"))
error_nosig = ISC_TRUE;
else if (ARG_IS("badsig"))
error_badsig = ISC_TRUE;
else if (ARG_IS("unknownsig"))
error_unknownsig = ISC_TRUE;
else if (ARG_IS("denyall"))
error_denyall = ISC_TRUE;
else {
fprintf(stderr, "Unknown forced error: %s\n",
isc_commandline_argument);
fprintf(stderr, "Valid forced errors: "
"noobject nosig badsig unknownsig\n");
exit(1);
}
break;
case 'm':
show_final_mem = ISC_TRUE;
break;
}
}
argc -= isc_commandline_index;
argv += isc_commandline_index;
RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
RUNTIME_CHECK(isc_taskmgr_create(mctx, 1, 0, &taskmgr)
== ISC_R_SUCCESS);
RUNTIME_CHECK(isc_socketmgr_create(mctx, &socketmgr)
== ISC_R_SUCCESS);
RUNTIME_CHECK(omapi_lib_init(mctx, taskmgr, socketmgr)
== ISC_R_SUCCESS);
RUNTIME_CHECK(isc_mutex_init(&mutex) == ISC_R_SUCCESS);
RUNTIME_CHECK(isc_condition_init(&waiter) == ISC_R_SUCCESS);
/*
* The secret key is shared on both the client and server side.
*/
RUNTIME_CHECK(omapi_auth_register(KEY1_NAME, "shhh, this is a secret",
OMAPI_AUTH_HMACMD5)
== ISC_R_SUCCESS);
if (argc > 1 && strcmp(argv[0], "listen") == 0) {
if (argc != 2) {
fprintf(stderr, "Usage: %s listen port\n", progname);
exit (1);
}
do_listen(atoi(argv[1]));
} else if (argc > 1 && !strcmp (argv[0], "connect")) {
if (argc != 3) {
fprintf(stderr, "Usage: %s connect address port\n",
progname);
exit (1);
}
do_connect(argv[1], atoi(argv[2]));
omapi_lib_destroy();
} else {
fprintf(stderr, "Usage: %s [-m] [listen | connect] ...\n",
progname);
exit (1);
}
isc_socketmgr_destroy(&socketmgr);
isc_taskmgr_destroy(&taskmgr);
if (show_final_mem)
isc_mem_stats(mctx, stderr);
isc_mem_destroy(&mctx);
return (0);
}